1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "WebBrowserPersistLocalDocument.h"
7 #include "WebBrowserPersistDocumentParent.h"
9 #include "mozilla/dom/Attr.h"
10 #include "mozilla/dom/BrowsingContext.h"
11 #include "mozilla/dom/Comment.h"
12 #include "mozilla/dom/Element.h"
13 #include "mozilla/dom/HTMLAnchorElement.h"
14 #include "mozilla/dom/HTMLAreaElement.h"
15 #include "mozilla/dom/HTMLImageElement.h"
16 #include "mozilla/dom/HTMLInputElement.h"
17 #include "mozilla/dom/HTMLLinkElement.h"
18 #include "mozilla/dom/HTMLObjectElement.h"
19 #include "mozilla/dom/HTMLOptionElement.h"
20 #include "mozilla/dom/HTMLSharedElement.h"
21 #include "mozilla/dom/HTMLTextAreaElement.h"
22 #include "mozilla/dom/NodeFilterBinding.h"
23 #include "mozilla/dom/ProcessingInstruction.h"
24 #include "mozilla/dom/ResponsiveImageSelector.h"
25 #include "mozilla/dom/BrowserParent.h"
26 #include "mozilla/dom/TreeWalker.h"
27 #include "mozilla/Encoding.h"
28 #include "mozilla/Try.h"
29 #include "mozilla/Unused.h"
30 #include "nsComponentManagerUtils.h"
31 #include "nsContentUtils.h"
32 #include "nsContentCID.h"
33 #include "nsCycleCollectionParticipant.h"
34 #include "nsDOMAttributeMap.h"
35 #include "nsFrameLoader.h"
36 #include "nsGlobalWindowOuter.h"
37 #include "nsIContent.h"
38 #include "nsICookieJarSettings.h"
39 #include "nsIDOMWindowUtils.h"
40 #include "mozilla/dom/Document.h"
41 #include "nsIDocumentEncoder.h"
42 #include "nsILoadContext.h"
43 #include "nsIProtocolHandler.h"
44 #include "nsISHEntry.h"
45 #include "nsIURIMutator.h"
46 #include "nsIWebBrowserPersist.h"
47 #include "nsIWebNavigation.h"
48 #include "nsIWebPageDescriptor.h"
49 #include "nsNetUtil.h"
50 #include "nsQueryObject.h"
54 NS_IMPL_CYCLE_COLLECTING_ADDREF(WebBrowserPersistLocalDocument
)
55 NS_IMPL_CYCLE_COLLECTING_RELEASE(WebBrowserPersistLocalDocument
)
57 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebBrowserPersistLocalDocument
)
58 NS_INTERFACE_MAP_ENTRY(nsIWebBrowserPersistDocument
)
59 NS_INTERFACE_MAP_ENTRY(nsISupports
)
62 NS_IMPL_CYCLE_COLLECTION(WebBrowserPersistLocalDocument
, mDocument
)
64 WebBrowserPersistLocalDocument::WebBrowserPersistLocalDocument(
65 dom::Document
* aDocument
)
66 : mDocument(aDocument
), mPersistFlags(0) {
67 MOZ_ASSERT(mDocument
);
70 WebBrowserPersistLocalDocument::~WebBrowserPersistLocalDocument() = default;
73 WebBrowserPersistLocalDocument::SetPersistFlags(uint32_t aFlags
) {
74 mPersistFlags
= aFlags
;
79 WebBrowserPersistLocalDocument::GetPersistFlags(uint32_t* aFlags
) {
80 *aFlags
= mPersistFlags
;
85 WebBrowserPersistLocalDocument::GetIsClosed(bool* aIsClosed
) {
91 WebBrowserPersistLocalDocument::GetIsPrivate(bool* aIsPrivate
) {
92 nsCOMPtr
<nsILoadContext
> privacyContext
= mDocument
->GetLoadContext();
93 *aIsPrivate
= privacyContext
&& privacyContext
->UsePrivateBrowsing();
98 WebBrowserPersistLocalDocument::GetDocumentURI(nsACString
& aURISpec
) {
99 nsCOMPtr
<nsIURI
> uri
= mDocument
->GetDocumentURI();
101 return NS_ERROR_UNEXPECTED
;
103 return uri
->GetSpec(aURISpec
);
107 WebBrowserPersistLocalDocument::GetBaseURI(nsACString
& aURISpec
) {
108 nsCOMPtr
<nsIURI
> uri
= GetBaseURI();
110 return NS_ERROR_UNEXPECTED
;
112 return uri
->GetSpec(aURISpec
);
116 WebBrowserPersistLocalDocument::GetContentType(nsACString
& aContentType
) {
117 nsAutoString utf16Type
;
118 mDocument
->GetContentType(utf16Type
);
119 CopyUTF16toUTF8(utf16Type
, aContentType
);
124 WebBrowserPersistLocalDocument::GetCharacterSet(nsACString
& aCharSet
) {
125 GetCharacterSet()->Name(aCharSet
);
130 WebBrowserPersistLocalDocument::GetTitle(nsAString
& aTitle
) {
131 nsAutoString titleBuffer
;
132 mDocument
->GetTitle(titleBuffer
);
133 aTitle
= titleBuffer
;
138 WebBrowserPersistLocalDocument::GetReferrerInfo(
139 nsIReferrerInfo
** aReferrerInfo
) {
140 *aReferrerInfo
= mDocument
->GetReferrerInfo();
141 NS_IF_ADDREF(*aReferrerInfo
);
146 WebBrowserPersistLocalDocument::GetCookieJarSettings(
147 nsICookieJarSettings
** aCookieJarSettings
) {
148 *aCookieJarSettings
= mDocument
->CookieJarSettings();
149 NS_ADDREF(*aCookieJarSettings
);
154 WebBrowserPersistLocalDocument::GetContentDisposition(nsAString
& aCD
) {
155 nsCOMPtr
<nsPIDOMWindowOuter
> window
= mDocument
->GetWindow();
156 if (NS_WARN_IF(!window
)) {
160 nsCOMPtr
<nsIDOMWindowUtils
> utils
=
161 nsGlobalWindowOuter::Cast(window
)->WindowUtils();
162 nsresult rv
= utils
->GetDocumentMetadata(u
"content-disposition"_ns
, aCD
);
163 if (NS_WARN_IF(NS_FAILED(rv
))) {
170 WebBrowserPersistLocalDocument::GetCacheKey(uint32_t* aKey
) {
171 Maybe
<uint32_t> cacheKey
;
173 if (nsDocShell
* docShell
= nsDocShell::Cast(mDocument
->GetDocShell())) {
174 cacheKey
= docShell
->GetCacheKeyFromCurrentEntry();
176 *aKey
= cacheKey
.valueOr(0);
182 WebBrowserPersistLocalDocument::GetPostData(nsIInputStream
** aStream
) {
183 nsCOMPtr
<nsIInputStream
> postData
;
184 if (nsDocShell
* docShell
= nsDocShell::Cast(mDocument
->GetDocShell())) {
185 postData
= docShell
->GetPostDataFromCurrentEntry();
188 postData
.forget(aStream
);
193 WebBrowserPersistLocalDocument::GetPrincipal(nsIPrincipal
** aPrincipal
) {
194 nsCOMPtr
<nsIPrincipal
> nodePrincipal
= mDocument
->NodePrincipal();
195 nodePrincipal
.forget(aPrincipal
);
199 already_AddRefed
<nsISHEntry
> WebBrowserPersistLocalDocument::GetHistory() {
200 nsCOMPtr
<nsPIDOMWindowOuter
> window
= mDocument
->GetWindow();
201 if (NS_WARN_IF(!window
)) {
204 nsCOMPtr
<nsIWebNavigation
> webNav
= do_GetInterface(window
);
205 if (NS_WARN_IF(!webNav
)) {
208 nsCOMPtr
<nsIWebPageDescriptor
> desc
= do_QueryInterface(webNav
);
209 if (NS_WARN_IF(!desc
)) {
212 nsCOMPtr
<nsISupports
> curDesc
;
213 nsresult rv
= desc
->GetCurrentDescriptor(getter_AddRefs(curDesc
));
214 // This can fail if, e.g., the document is a Print Preview.
215 if (NS_FAILED(rv
) || NS_WARN_IF(!curDesc
)) {
218 nsCOMPtr
<nsISHEntry
> history
= do_QueryInterface(curDesc
);
219 return history
.forget();
222 NotNull
<const Encoding
*> WebBrowserPersistLocalDocument::GetCharacterSet()
224 return mDocument
->GetDocumentCharacterSet();
227 uint32_t WebBrowserPersistLocalDocument::GetPersistFlags() const {
228 return mPersistFlags
;
231 nsIURI
* WebBrowserPersistLocalDocument::GetBaseURI() const {
232 return mDocument
->GetBaseURI();
237 // Helper class for ReadResources().
238 class ResourceReader final
: public nsIWebBrowserPersistDocumentReceiver
{
240 ResourceReader(WebBrowserPersistLocalDocument
* aParent
,
241 nsIWebBrowserPersistResourceVisitor
* aVisitor
);
242 nsresult
OnWalkDOMNode(nsINode
* aNode
);
244 // This is called both to indicate the end of the document walk
245 // and when a subdocument is (maybe asynchronously) sent to the
246 // visitor. The call to EndVisit needs to happen after both of
247 // those have finished.
248 void DocumentDone(nsresult aStatus
);
250 NS_DECL_NSIWEBBROWSERPERSISTDOCUMENTRECEIVER
254 RefPtr
<WebBrowserPersistLocalDocument
> mParent
;
255 nsCOMPtr
<nsIWebBrowserPersistResourceVisitor
> mVisitor
;
256 nsCOMPtr
<nsIURI
> mCurrentBaseURI
;
257 uint32_t mPersistFlags
;
259 // The number of DocumentDone calls after which EndVisit will be
260 // called on the visitor. Counts the main document if it's still
261 // being walked and any outstanding asynchronous subdocument
262 // StartPersistence calls.
263 size_t mOutstandingDocuments
;
264 // Collects the status parameters to DocumentDone calls.
267 nsresult
OnWalkURI(const nsACString
& aURISpec
,
268 nsContentPolicyType aContentPolicyType
);
269 nsresult
OnWalkURI(nsIURI
* aURI
, nsContentPolicyType aContentPolicyType
);
270 nsresult
OnWalkAttribute(dom::Element
* aElement
,
271 nsContentPolicyType aContentPolicyType
,
272 const char* aAttribute
,
273 const char* aNamespaceURI
= "");
274 nsresult
OnWalkSubframe(nsINode
* aNode
);
275 nsresult
OnWalkSrcSet(dom::Element
* aElement
);
279 using IWBP
= nsIWebBrowserPersist
;
282 NS_IMPL_ISUPPORTS(ResourceReader
, nsIWebBrowserPersistDocumentReceiver
)
284 ResourceReader::ResourceReader(WebBrowserPersistLocalDocument
* aParent
,
285 nsIWebBrowserPersistResourceVisitor
* aVisitor
)
288 mCurrentBaseURI(aParent
->GetBaseURI()),
289 mPersistFlags(aParent
->GetPersistFlags()),
290 mOutstandingDocuments(1),
292 MOZ_ASSERT(mCurrentBaseURI
);
295 ResourceReader::~ResourceReader() { MOZ_ASSERT(mOutstandingDocuments
== 0); }
297 void ResourceReader::DocumentDone(nsresult aStatus
) {
298 MOZ_ASSERT(mOutstandingDocuments
> 0);
299 if (NS_SUCCEEDED(mEndStatus
)) {
300 mEndStatus
= aStatus
;
302 if (--mOutstandingDocuments
== 0) {
303 mVisitor
->EndVisit(mParent
, mEndStatus
);
307 nsresult
ResourceReader::OnWalkSubframe(nsINode
* aNode
) {
308 RefPtr
<nsFrameLoaderOwner
> loaderOwner
= do_QueryObject(aNode
);
309 NS_ENSURE_STATE(loaderOwner
);
310 RefPtr
<nsFrameLoader
> loader
= loaderOwner
->GetFrameLoader();
311 NS_ENSURE_STATE(loader
);
313 RefPtr
<dom::BrowsingContext
> context
= loader
->GetBrowsingContext();
314 NS_ENSURE_STATE(context
);
316 if (loader
->IsRemoteFrame()) {
317 mVisitor
->VisitBrowsingContext(mParent
, context
);
321 ++mOutstandingDocuments
;
323 loader
->StartPersistence(context
, this, err
);
324 nsresult rv
= err
.StealNSResult();
326 if (rv
== NS_ERROR_NO_CONTENT
) {
327 // Just ignore frames with no content document.
330 // StartPersistence won't eventually call this if it failed,
331 // so this does so (to keep mOutstandingDocuments correct).
338 ResourceReader::OnDocumentReady(nsIWebBrowserPersistDocument
* aDocument
) {
339 mVisitor
->VisitDocument(mParent
, aDocument
);
345 ResourceReader::OnError(nsresult aFailure
) {
346 DocumentDone(aFailure
);
350 nsresult
ResourceReader::OnWalkURI(nsIURI
* aURI
,
351 nsContentPolicyType aContentPolicyType
) {
352 // Test if this URI should be persisted. By default
353 // we should assume the URI is persistable.
354 bool doNotPersistURI
;
355 nsresult rv
= NS_URIChainHasFlags(
356 aURI
, nsIProtocolHandler::URI_NON_PERSISTABLE
, &doNotPersistURI
);
357 if (NS_SUCCEEDED(rv
) && doNotPersistURI
) {
361 nsAutoCString stringURI
;
362 rv
= aURI
->GetSpec(stringURI
);
363 NS_ENSURE_SUCCESS(rv
, rv
);
364 return mVisitor
->VisitResource(mParent
, stringURI
, aContentPolicyType
);
367 nsresult
ResourceReader::OnWalkURI(const nsACString
& aURISpec
,
368 nsContentPolicyType aContentPolicyType
) {
370 nsCOMPtr
<nsIURI
> uri
;
372 rv
= NS_NewURI(getter_AddRefs(uri
), aURISpec
, mParent
->GetCharacterSet(),
375 // We don't want to break saving a page in case of a malformed URI.
378 return OnWalkURI(uri
, aContentPolicyType
);
381 static void ExtractAttribute(dom::Element
* aElement
, const char* aAttribute
,
382 const char* aNamespaceURI
, nsCString
& aValue
) {
383 // Find the named URI attribute on the (element) node and store
384 // a reference to the URI that maps onto a local file name
386 RefPtr
<nsDOMAttributeMap
> attrMap
= aElement
->Attributes();
388 NS_ConvertASCIItoUTF16
namespaceURI(aNamespaceURI
);
389 NS_ConvertASCIItoUTF16
attribute(aAttribute
);
390 RefPtr
<dom::Attr
> attr
= attrMap
->GetNamedItemNS(namespaceURI
, attribute
);
393 attr
->GetValue(value
);
394 CopyUTF16toUTF8(value
, aValue
);
400 nsresult
ResourceReader::OnWalkAttribute(dom::Element
* aElement
,
401 nsContentPolicyType aContentPolicyType
,
402 const char* aAttribute
,
403 const char* aNamespaceURI
) {
404 nsAutoCString uriSpec
;
405 ExtractAttribute(aElement
, aAttribute
, aNamespaceURI
, uriSpec
);
406 if (uriSpec
.IsEmpty()) {
409 return OnWalkURI(uriSpec
, aContentPolicyType
);
412 nsresult
ResourceReader::OnWalkSrcSet(dom::Element
* aElement
) {
414 if (!aElement
->GetAttr(nsGkAtoms::srcset
, srcSet
)) {
419 auto eachCandidate
= [&](dom::ResponsiveImageCandidate
&& aCandidate
) {
420 if (!aCandidate
.IsValid() || NS_FAILED(rv
)) {
423 rv
= OnWalkURI(NS_ConvertUTF16toUTF8(aCandidate
.URLString()),
424 nsIContentPolicy::TYPE_IMAGE
);
426 dom::ResponsiveImageSelector::ParseSourceSet(srcSet
, eachCandidate
);
430 static nsresult
GetXMLStyleSheetLink(dom::ProcessingInstruction
* aPI
,
435 nsContentUtils::GetPseudoAttributeValue(data
, nsGkAtoms::href
, aHref
);
439 nsresult
ResourceReader::OnWalkDOMNode(nsINode
* aNode
) {
440 // Fixup xml-stylesheet processing instructions
441 if (auto nodeAsPI
= dom::ProcessingInstruction::FromNode(aNode
)) {
443 nodeAsPI
->GetTarget(target
);
444 if (target
.EqualsLiteral("xml-stylesheet")) {
446 GetXMLStyleSheetLink(nodeAsPI
, href
);
447 if (!href
.IsEmpty()) {
448 return OnWalkURI(NS_ConvertUTF16toUTF8(href
),
449 nsIContentPolicy::TYPE_STYLESHEET
);
455 // Test the node to see if it's an image, frame, iframe, css, js
456 if (auto* img
= dom::HTMLImageElement::FromNode(*aNode
)) {
457 MOZ_TRY(OnWalkAttribute(img
, nsIContentPolicy::TYPE_IMAGE
, "src"));
458 MOZ_TRY(OnWalkSrcSet(img
));
462 if (aNode
->IsSVGElement(nsGkAtoms::image
)) {
463 MOZ_TRY(OnWalkAttribute(aNode
->AsElement(), nsIContentPolicy::TYPE_IMAGE
,
465 return OnWalkAttribute(aNode
->AsElement(), nsIContentPolicy::TYPE_IMAGE
,
466 "href", "http://www.w3.org/1999/xlink");
469 if (aNode
->IsAnyOfHTMLElements(nsGkAtoms::audio
, nsGkAtoms::video
)) {
470 return OnWalkAttribute(aNode
->AsElement(), nsIContentPolicy::TYPE_MEDIA
,
474 if (aNode
->IsHTMLElement(nsGkAtoms::source
)) {
475 MOZ_TRY(OnWalkSrcSet(aNode
->AsElement()));
476 return OnWalkAttribute(aNode
->AsElement(), nsIContentPolicy::TYPE_MEDIA
,
480 if (aNode
->IsHTMLElement(nsGkAtoms::body
)) {
481 return OnWalkAttribute(aNode
->AsElement(), nsIContentPolicy::TYPE_IMAGE
,
485 if (aNode
->IsHTMLElement(nsGkAtoms::table
)) {
486 return OnWalkAttribute(aNode
->AsElement(), nsIContentPolicy::TYPE_IMAGE
,
490 if (aNode
->IsHTMLElement(nsGkAtoms::tr
)) {
491 return OnWalkAttribute(aNode
->AsElement(), nsIContentPolicy::TYPE_IMAGE
,
495 if (aNode
->IsAnyOfHTMLElements(nsGkAtoms::td
, nsGkAtoms::th
)) {
496 return OnWalkAttribute(aNode
->AsElement(), nsIContentPolicy::TYPE_IMAGE
,
500 if (aNode
->IsHTMLElement(nsGkAtoms::script
)) {
501 return OnWalkAttribute(aNode
->AsElement(), nsIContentPolicy::TYPE_SCRIPT
,
505 if (aNode
->IsSVGElement(nsGkAtoms::script
)) {
506 MOZ_TRY(OnWalkAttribute(aNode
->AsElement(), nsIContentPolicy::TYPE_SCRIPT
,
508 return OnWalkAttribute(aNode
->AsElement(), nsIContentPolicy::TYPE_SCRIPT
,
509 "href", "http://www.w3.org/1999/xlink");
512 if (aNode
->IsHTMLElement(nsGkAtoms::embed
)) {
513 return OnWalkAttribute(aNode
->AsElement(), nsIContentPolicy::TYPE_OBJECT
,
517 if (aNode
->IsHTMLElement(nsGkAtoms::object
)) {
518 return OnWalkAttribute(aNode
->AsElement(), nsIContentPolicy::TYPE_OBJECT
,
522 if (auto nodeAsLink
= dom::HTMLLinkElement::FromNode(aNode
)) {
523 // Test if the link has a rel value indicating it to be a stylesheet
524 nsAutoString linkRel
;
525 nodeAsLink
->GetRel(linkRel
);
526 if (!linkRel
.IsEmpty()) {
527 nsReadingIterator
<char16_t
> start
;
528 nsReadingIterator
<char16_t
> end
;
529 nsReadingIterator
<char16_t
> current
;
531 linkRel
.BeginReading(start
);
532 linkRel
.EndReading(end
);
534 // Walk through space delimited string looking for "stylesheet"
535 for (current
= start
; current
!= end
; ++current
) {
537 if (nsCRT::IsAsciiSpace(*current
)) {
541 // Grab the next space delimited word
542 nsReadingIterator
<char16_t
> startWord
= current
;
545 } while (current
!= end
&& !nsCRT::IsAsciiSpace(*current
));
547 // Store the link for fix up if it says "stylesheet"
548 if (Substring(startWord
, current
)
549 .LowerCaseEqualsLiteral("stylesheet")) {
550 OnWalkAttribute(aNode
->AsElement(), nsIContentPolicy::TYPE_STYLESHEET
,
554 if (current
== end
) {
562 if (aNode
->IsHTMLElement(nsGkAtoms::frame
)) {
563 return OnWalkSubframe(aNode
);
566 if (aNode
->IsHTMLElement(nsGkAtoms::iframe
) &&
567 !(mPersistFlags
& IWBP::PERSIST_FLAGS_IGNORE_IFRAMES
)) {
568 return OnWalkSubframe(aNode
);
571 auto nodeAsInput
= dom::HTMLInputElement::FromNode(aNode
);
573 return OnWalkAttribute(aNode
->AsElement(), nsIContentPolicy::TYPE_IMAGE
,
580 // Helper class for node rewriting in writeContent().
581 class PersistNodeFixup final
: public nsIDocumentEncoderNodeFixup
{
583 PersistNodeFixup(WebBrowserPersistLocalDocument
* aParent
,
584 nsIWebBrowserPersistURIMap
* aMap
, nsIURI
* aTargetURI
);
587 NS_DECL_NSIDOCUMENTENCODERNODEFIXUP
589 virtual ~PersistNodeFixup() = default;
590 RefPtr
<WebBrowserPersistLocalDocument
> mParent
;
591 nsClassHashtable
<nsCStringHashKey
, nsCString
> mMap
;
592 nsCOMPtr
<nsIURI
> mCurrentBaseURI
;
593 nsCOMPtr
<nsIURI
> mTargetBaseURI
;
595 bool IsFlagSet(uint32_t aFlag
) const {
596 return mParent
->GetPersistFlags() & aFlag
;
599 nsresult
GetNodeToFixup(nsINode
* aNodeIn
, nsINode
** aNodeOut
);
600 nsresult
FixupURI(nsAString
& aURI
);
601 nsresult
FixupAttribute(nsINode
* aNode
, const char* aAttribute
,
602 const char* aNamespaceURI
= "");
603 nsresult
FixupAnchor(nsINode
* aNode
);
604 nsresult
FixupXMLStyleSheetLink(dom::ProcessingInstruction
* aPI
,
605 const nsAString
& aHref
);
607 nsresult
FixupSrcSet(nsINode
*);
609 using IWBP
= nsIWebBrowserPersist
;
612 NS_IMPL_ISUPPORTS(PersistNodeFixup
, nsIDocumentEncoderNodeFixup
)
614 PersistNodeFixup::PersistNodeFixup(WebBrowserPersistLocalDocument
* aParent
,
615 nsIWebBrowserPersistURIMap
* aMap
,
618 mCurrentBaseURI(aParent
->GetBaseURI()),
619 mTargetBaseURI(aTargetURI
) {
622 nsresult rv
= aMap
->GetNumMappedURIs(&mapSize
);
623 MOZ_ASSERT(NS_SUCCEEDED(rv
));
624 NS_ENSURE_SUCCESS_VOID(rv
);
625 for (uint32_t i
= 0; i
< mapSize
; ++i
) {
626 nsAutoCString urlFrom
;
627 auto urlTo
= MakeUnique
<nsCString
>();
629 rv
= aMap
->GetURIMapping(i
, urlFrom
, *urlTo
);
630 MOZ_ASSERT(NS_SUCCEEDED(rv
));
631 if (NS_SUCCEEDED(rv
)) {
632 mMap
.InsertOrUpdate(urlFrom
, std::move(urlTo
));
638 nsresult
PersistNodeFixup::GetNodeToFixup(nsINode
* aNodeIn
,
639 nsINode
** aNodeOut
) {
640 // Avoid mixups in FixupNode that could leak objects; this goes
641 // against the usual out parameter convention, but it's a private
642 // method so shouldn't be a problem.
643 MOZ_ASSERT(!*aNodeOut
);
645 if (!IsFlagSet(IWBP::PERSIST_FLAGS_FIXUP_ORIGINAL_DOM
)) {
647 *aNodeOut
= aNodeIn
->CloneNode(false, rv
).take();
648 return rv
.StealNSResult();
651 NS_ADDREF(*aNodeOut
= aNodeIn
);
655 nsresult
PersistNodeFixup::FixupURI(nsAString
& aURI
) {
656 // get the current location of the file (absolutized)
657 nsCOMPtr
<nsIURI
> uri
;
658 nsresult rv
= NS_NewURI(getter_AddRefs(uri
), aURI
, mParent
->GetCharacterSet(),
660 NS_ENSURE_SUCCESS(rv
, rv
);
662 rv
= uri
->GetSpec(spec
);
663 NS_ENSURE_SUCCESS(rv
, rv
);
665 const nsCString
* replacement
= mMap
.Get(spec
);
667 // Note that most callers ignore this "failure".
668 return NS_ERROR_FAILURE
;
670 if (!replacement
->IsEmpty()) {
671 CopyUTF8toUTF16(*replacement
, aURI
);
676 nsresult
PersistNodeFixup::FixupSrcSet(nsINode
* aNode
) {
677 dom::Element
* element
= aNode
->AsElement();
678 nsAutoString originalSrcSet
;
679 if (!element
->GetAttr(nsGkAtoms::srcset
, originalSrcSet
)) {
682 nsAutoString newSrcSet
;
684 auto eachCandidate
= [&](dom::ResponsiveImageCandidate
&& aCandidate
) {
685 if (!aCandidate
.IsValid()) {
689 newSrcSet
.AppendLiteral(", ");
692 nsAutoString
uri(aCandidate
.URLString());
694 newSrcSet
.Append(uri
);
695 aCandidate
.AppendDescriptors(newSrcSet
);
697 dom::ResponsiveImageSelector::ParseSourceSet(originalSrcSet
, eachCandidate
);
698 element
->SetAttr(nsGkAtoms::srcset
, newSrcSet
, IgnoreErrors());
702 nsresult
PersistNodeFixup::FixupAttribute(nsINode
* aNode
,
703 const char* aAttribute
,
704 const char* aNamespaceURI
) {
705 MOZ_ASSERT(aNode
->IsElement());
706 dom::Element
* element
= aNode
->AsElement();
708 RefPtr
<nsDOMAttributeMap
> attrMap
= element
->Attributes();
710 NS_ConvertASCIItoUTF16
attribute(aAttribute
);
711 NS_ConvertASCIItoUTF16
namespaceURI(aNamespaceURI
);
712 RefPtr
<dom::Attr
> attr
= attrMap
->GetNamedItemNS(namespaceURI
, attribute
);
718 if (NS_SUCCEEDED(rv
)) {
719 attr
->SetValue(uri
, IgnoreErrors());
726 nsresult
PersistNodeFixup::FixupAnchor(nsINode
* aNode
) {
727 if (IsFlagSet(IWBP::PERSIST_FLAGS_DONT_FIXUP_LINKS
)) {
731 MOZ_ASSERT(aNode
->IsElement());
732 dom::Element
* element
= aNode
->AsElement();
734 RefPtr
<nsDOMAttributeMap
> attrMap
= element
->Attributes();
736 // Make all anchor links absolute so they point off onto the Internet
737 nsString
attribute(u
"href"_ns
);
738 RefPtr
<dom::Attr
> attr
= attrMap
->GetNamedItem(attribute
);
741 attr
->GetValue(oldValue
);
742 NS_ConvertUTF16toUTF8
oldCValue(oldValue
);
744 // Skip empty values and self-referencing bookmarks
745 if (oldCValue
.IsEmpty() || oldCValue
.CharAt(0) == '#') {
749 // if saving file to same location, we don't need to do any fixup
751 if (mTargetBaseURI
&&
752 NS_SUCCEEDED(mCurrentBaseURI
->Equals(mTargetBaseURI
, &isEqual
)) &&
757 nsCOMPtr
<nsIURI
> relativeURI
;
758 relativeURI
= IsFlagSet(IWBP::PERSIST_FLAGS_FIXUP_LINKS_TO_DESTINATION
)
761 // Make a new URI to replace the current one
762 nsCOMPtr
<nsIURI
> newURI
;
763 nsresult rv
= NS_NewURI(getter_AddRefs(newURI
), oldCValue
,
764 mParent
->GetCharacterSet(), relativeURI
);
765 if (NS_SUCCEEDED(rv
) && newURI
) {
766 Unused
<< NS_MutateURI(newURI
).SetUserPass(""_ns
).Finalize(newURI
);
767 nsAutoCString uriSpec
;
768 rv
= newURI
->GetSpec(uriSpec
);
769 NS_ENSURE_SUCCESS(rv
, rv
);
770 attr
->SetValue(NS_ConvertUTF8toUTF16(uriSpec
), IgnoreErrors());
777 static void AppendXMLAttr(const nsAString
& key
, const nsAString
& aValue
,
778 nsAString
& aBuffer
) {
779 if (!aBuffer
.IsEmpty()) {
783 aBuffer
.AppendLiteral(R
"(=")");
784 for (size_t i = 0; i < aValue.Length(); ++i) {
787 aBuffer.AppendLiteral("&
;");
790 aBuffer.AppendLiteral("<
;");
793 aBuffer.AppendLiteral(">
;");
796 aBuffer.AppendLiteral(""");
799 aBuffer.Append(aValue[i]);
806 nsresult PersistNodeFixup::FixupXMLStyleSheetLink(
807 dom::ProcessingInstruction* aPI, const nsAString& aHref) {
808 NS_ENSURE_ARG_POINTER(aPI);
814 nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::href, href);
816 // Construct and set a new data value for the xml-stylesheet
817 if (!aHref.IsEmpty() && !href.IsEmpty()) {
818 nsAutoString alternate;
819 nsAutoString charset;
824 nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::alternate,
826 nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::charset, charset);
827 nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::title, title);
828 nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::type, type);
829 nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::media, media);
831 nsAutoString newData;
832 AppendXMLAttr(u"href
"_ns, aHref, newData);
833 if (!title.IsEmpty()) {
834 AppendXMLAttr(u"title
"_ns, title, newData);
836 if (!media.IsEmpty()) {
837 AppendXMLAttr(u"media
"_ns, media, newData);
839 if (!type.IsEmpty()) {
840 AppendXMLAttr(u"type
"_ns, type, newData);
842 if (!charset.IsEmpty()) {
843 AppendXMLAttr(u"charset
"_ns, charset, newData);
845 if (!alternate.IsEmpty()) {
846 AppendXMLAttr(u"alternate
"_ns, alternate, newData);
848 aPI->SetData(newData, IgnoreErrors());
855 PersistNodeFixup::FixupNode(nsINode* aNodeIn, bool* aSerializeCloneKids,
856 nsINode** aNodeOut) {
858 *aSerializeCloneKids = false;
860 uint16_t type = aNodeIn->NodeType();
861 if (type != nsINode::ELEMENT_NODE &&
862 type != nsINode::PROCESSING_INSTRUCTION_NODE) {
866 MOZ_ASSERT(aNodeIn->IsContent());
868 // Fixup xml-stylesheet processing instructions
869 if (auto nodeAsPI = dom::ProcessingInstruction::FromNode(aNodeIn)) {
871 nodeAsPI->GetTarget(target);
872 if (target.EqualsLiteral("xml
-stylesheet
")) {
873 nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut);
874 if (NS_SUCCEEDED(rv) && *aNodeOut) {
875 MOZ_ASSERT((*aNodeOut)->IsProcessingInstruction());
876 auto nodeAsPI = static_cast<dom::ProcessingInstruction*>(*aNodeOut);
878 GetXMLStyleSheetLink(nodeAsPI, href);
879 if (!href.IsEmpty()) {
881 FixupXMLStyleSheetLink(nodeAsPI, href);
888 nsCOMPtr<nsIContent> content = do_QueryInterface(aNodeIn);
893 // BASE elements are replaced by a comment so relative links are not hosed.
894 if (!IsFlagSet(IWBP::PERSIST_FLAGS_NO_BASE_TAG_MODIFICATIONS) &&
895 content->IsHTMLElement(nsGkAtoms::base)) {
896 // Base uses HTMLSharedElement, which would be awkward to implement
897 // FromContent on, since it represents multiple elements. Since we've
898 // already checked IsHTMLElement here, just cast as we were doing.
899 auto* base = static_cast<dom::HTMLSharedElement*>(content.get());
900 dom::Document* ownerDoc = base->OwnerDoc();
903 base->GetHref(href); // Doesn't matter if this fails
904 nsAutoString commentText;
905 commentText.AssignLiteral(" base
");
906 if (!href.IsEmpty()) {
907 commentText += u"href
=\""_ns + href + u"\" "_ns;
909 *aNodeOut = ownerDoc->CreateComment(commentText).take();
913 // Fix up href and file links in the elements
914 RefPtr<dom::HTMLAnchorElement> nodeAsAnchor =
915 dom::HTMLAnchorElement::FromNode(content);
917 nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut);
918 if (NS_SUCCEEDED(rv) && *aNodeOut) {
919 FixupAnchor(*aNodeOut);
924 RefPtr<dom::HTMLAreaElement> nodeAsArea =
925 dom::HTMLAreaElement::FromNode(content);
927 nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut);
928 if (NS_SUCCEEDED(rv) && *aNodeOut) {
929 FixupAnchor(*aNodeOut);
934 if (content->IsHTMLElement(nsGkAtoms::body)) {
935 nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut);
936 if (NS_SUCCEEDED(rv) && *aNodeOut) {
937 FixupAttribute(*aNodeOut, "background
");
942 if (content->IsHTMLElement(nsGkAtoms::table)) {
943 nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut);
944 if (NS_SUCCEEDED(rv) && *aNodeOut) {
945 FixupAttribute(*aNodeOut, "background
");
950 if (content->IsHTMLElement(nsGkAtoms::tr)) {
951 nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut);
952 if (NS_SUCCEEDED(rv) && *aNodeOut) {
953 FixupAttribute(*aNodeOut, "background
");
958 if (content->IsAnyOfHTMLElements(nsGkAtoms::td, nsGkAtoms::th)) {
959 nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut);
960 if (NS_SUCCEEDED(rv) && *aNodeOut) {
961 FixupAttribute(*aNodeOut, "background
");
966 if (content->IsHTMLElement(nsGkAtoms::img)) {
967 MOZ_TRY(GetNodeToFixup(aNodeIn, aNodeOut));
972 // Disable image loads
973 nsCOMPtr<nsIImageLoadingContent> imgCon = do_QueryInterface(*aNodeOut);
975 imgCon->SetLoadingEnabled(false);
977 // FIXME(emilio): Why fixing up <img href>? Looks bogus
978 FixupAnchor(*aNodeOut);
979 FixupAttribute(*aNodeOut, "src
");
980 FixupSrcSet(*aNodeOut);
984 if (content->IsAnyOfHTMLElements(nsGkAtoms::audio, nsGkAtoms::video)) {
985 nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut);
986 if (NS_SUCCEEDED(rv) && *aNodeOut) {
987 FixupAttribute(*aNodeOut, "src
");
992 if (content->IsHTMLElement(nsGkAtoms::source)) {
993 nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut);
994 if (NS_SUCCEEDED(rv) && *aNodeOut) {
995 FixupAttribute(*aNodeOut, "src
");
996 FixupSrcSet(*aNodeOut);
1001 if (content->IsSVGElement(nsGkAtoms::image)) {
1002 nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut);
1003 if (NS_SUCCEEDED(rv) && *aNodeOut) {
1004 // Disable image loads
1005 nsCOMPtr<nsIImageLoadingContent> imgCon = do_QueryInterface(*aNodeOut);
1006 if (imgCon) imgCon->SetLoadingEnabled(false);
1008 // FixupAnchor(*aNodeOut); // XXXjwatt: is this line needed?
1009 FixupAttribute(*aNodeOut, "href
");
1010 FixupAttribute(*aNodeOut, "href
", "http
://www.w3.org/1999/xlink");
1015 if (content
->IsHTMLElement(nsGkAtoms::script
)) {
1016 nsresult rv
= GetNodeToFixup(aNodeIn
, aNodeOut
);
1017 if (NS_SUCCEEDED(rv
) && *aNodeOut
) {
1018 FixupAttribute(*aNodeOut
, "src");
1023 if (content
->IsSVGElement(nsGkAtoms::script
)) {
1024 nsresult rv
= GetNodeToFixup(aNodeIn
, aNodeOut
);
1025 if (NS_SUCCEEDED(rv
) && *aNodeOut
) {
1026 FixupAttribute(*aNodeOut
, "href");
1027 FixupAttribute(*aNodeOut
, "href", "http://www.w3.org/1999/xlink");
1032 if (content
->IsHTMLElement(nsGkAtoms::embed
)) {
1033 nsresult rv
= GetNodeToFixup(aNodeIn
, aNodeOut
);
1034 if (NS_SUCCEEDED(rv
) && *aNodeOut
) {
1035 FixupAttribute(*aNodeOut
, "src");
1040 if (content
->IsHTMLElement(nsGkAtoms::object
)) {
1041 nsresult rv
= GetNodeToFixup(aNodeIn
, aNodeOut
);
1042 if (NS_SUCCEEDED(rv
) && *aNodeOut
) {
1043 FixupAttribute(*aNodeOut
, "data");
1048 if (content
->IsHTMLElement(nsGkAtoms::link
)) {
1049 nsresult rv
= GetNodeToFixup(aNodeIn
, aNodeOut
);
1050 if (NS_SUCCEEDED(rv
) && *aNodeOut
) {
1051 // First see if the link represents linked content
1052 rv
= FixupAttribute(*aNodeOut
, "href");
1053 if (NS_FAILED(rv
)) {
1054 // Perhaps this link is actually an anchor to related content
1055 FixupAnchor(*aNodeOut
);
1057 // TODO if "type" attribute == "text/css"
1063 if (content
->IsHTMLElement(nsGkAtoms::frame
)) {
1064 nsresult rv
= GetNodeToFixup(aNodeIn
, aNodeOut
);
1065 if (NS_SUCCEEDED(rv
) && *aNodeOut
) {
1066 FixupAttribute(*aNodeOut
, "src");
1071 if (content
->IsHTMLElement(nsGkAtoms::iframe
)) {
1072 nsresult rv
= GetNodeToFixup(aNodeIn
, aNodeOut
);
1073 if (NS_SUCCEEDED(rv
) && *aNodeOut
) {
1074 FixupAttribute(*aNodeOut
, "src");
1079 RefPtr
<dom::HTMLInputElement
> nodeAsInput
=
1080 dom::HTMLInputElement::FromNodeOrNull(content
);
1082 nsresult rv
= GetNodeToFixup(aNodeIn
, aNodeOut
);
1083 if (NS_SUCCEEDED(rv
) && *aNodeOut
) {
1084 // Disable image loads
1085 nsCOMPtr
<nsIImageLoadingContent
> imgCon
= do_QueryInterface(*aNodeOut
);
1087 imgCon
->SetLoadingEnabled(false);
1090 FixupAttribute(*aNodeOut
, "src");
1092 nsAutoString valueStr
;
1093 constexpr auto valueAttr
= u
"value"_ns
;
1094 // Update element node attributes with user-entered form state
1095 RefPtr
<dom::HTMLInputElement
> outElt
=
1096 dom::HTMLInputElement::FromNode((*aNodeOut
)->AsContent());
1097 nsCOMPtr
<nsIFormControl
> formControl
= do_QueryInterface(*aNodeOut
);
1098 switch (formControl
->ControlType()) {
1099 case FormControlType::InputEmail
:
1100 case FormControlType::InputSearch
:
1101 case FormControlType::InputText
:
1102 case FormControlType::InputTel
:
1103 case FormControlType::InputUrl
:
1104 case FormControlType::InputNumber
:
1105 case FormControlType::InputRange
:
1106 case FormControlType::InputDate
:
1107 case FormControlType::InputTime
:
1108 case FormControlType::InputColor
:
1109 nodeAsInput
->GetValue(valueStr
, dom::CallerType::System
);
1110 // Avoid superfluous value="" serialization
1111 if (valueStr
.IsEmpty()) {
1112 outElt
->RemoveAttribute(valueAttr
, IgnoreErrors());
1114 outElt
->SetAttribute(valueAttr
, valueStr
, IgnoreErrors());
1117 case FormControlType::InputCheckbox
:
1118 case FormControlType::InputRadio
:
1119 outElt
->SetDefaultChecked(nodeAsInput
->Checked(), IgnoreErrors());
1128 dom::HTMLTextAreaElement
* nodeAsTextArea
=
1129 dom::HTMLTextAreaElement::FromNode(content
);
1130 if (nodeAsTextArea
) {
1131 nsresult rv
= GetNodeToFixup(aNodeIn
, aNodeOut
);
1132 if (NS_SUCCEEDED(rv
) && *aNodeOut
) {
1133 // Tell the document encoder to serialize the text child we create below
1134 *aSerializeCloneKids
= true;
1136 nsAutoString valueStr
;
1137 nodeAsTextArea
->GetValue(valueStr
);
1139 (*aNodeOut
)->SetTextContent(valueStr
, IgnoreErrors());
1144 dom::HTMLOptionElement
* nodeAsOption
=
1145 dom::HTMLOptionElement::FromNode(content
);
1147 nsresult rv
= GetNodeToFixup(aNodeIn
, aNodeOut
);
1148 if (NS_SUCCEEDED(rv
) && *aNodeOut
) {
1149 dom::HTMLOptionElement
* outElt
=
1150 dom::HTMLOptionElement::FromNode((*aNodeOut
)->AsContent());
1151 bool selected
= nodeAsOption
->Selected();
1152 outElt
->SetDefaultSelected(selected
, IgnoreErrors());
1160 } // unnamed namespace
1163 WebBrowserPersistLocalDocument::ReadResources(
1164 nsIWebBrowserPersistResourceVisitor
* aVisitor
) {
1165 nsresult rv
= NS_OK
;
1166 nsCOMPtr
<nsIWebBrowserPersistResourceVisitor
> visitor
= aVisitor
;
1168 NS_ENSURE_TRUE(mDocument
, NS_ERROR_FAILURE
);
1171 RefPtr
<dom::TreeWalker
> walker
= mDocument
->CreateTreeWalker(
1173 dom::NodeFilter_Binding::SHOW_ELEMENT
|
1174 dom::NodeFilter_Binding::SHOW_DOCUMENT
|
1175 dom::NodeFilter_Binding::SHOW_PROCESSING_INSTRUCTION
,
1178 if (NS_WARN_IF(err
.Failed())) {
1179 return err
.StealNSResult();
1183 RefPtr
<ResourceReader
> reader
= new ResourceReader(this, aVisitor
);
1184 nsCOMPtr
<nsINode
> currentNode
= walker
->CurrentNode();
1186 rv
= reader
->OnWalkDOMNode(currentNode
);
1187 if (NS_WARN_IF(NS_FAILED(rv
))) {
1192 currentNode
= walker
->NextNode(err
);
1193 if (NS_WARN_IF(err
.Failed())) {
1194 err
.SuppressException();
1197 } while (currentNode
);
1198 reader
->DocumentDone(rv
);
1199 // If NS_FAILED(rv), it was / will be reported by an EndVisit call
1200 // via DocumentDone. This method must return a failure if and
1201 // only if visitor won't be invoked.
1205 static uint32_t ConvertEncoderFlags(uint32_t aEncoderFlags
) {
1206 uint32_t encoderFlags
= 0;
1208 if (aEncoderFlags
& nsIWebBrowserPersist::ENCODE_FLAGS_SELECTION_ONLY
)
1209 encoderFlags
|= nsIDocumentEncoder::OutputSelectionOnly
;
1210 if (aEncoderFlags
& nsIWebBrowserPersist::ENCODE_FLAGS_FORMATTED
)
1211 encoderFlags
|= nsIDocumentEncoder::OutputFormatted
;
1212 if (aEncoderFlags
& nsIWebBrowserPersist::ENCODE_FLAGS_RAW
)
1213 encoderFlags
|= nsIDocumentEncoder::OutputRaw
;
1214 if (aEncoderFlags
& nsIWebBrowserPersist::ENCODE_FLAGS_BODY_ONLY
)
1215 encoderFlags
|= nsIDocumentEncoder::OutputBodyOnly
;
1216 if (aEncoderFlags
& nsIWebBrowserPersist::ENCODE_FLAGS_PREFORMATTED
)
1217 encoderFlags
|= nsIDocumentEncoder::OutputPreformatted
;
1218 if (aEncoderFlags
& nsIWebBrowserPersist::ENCODE_FLAGS_WRAP
)
1219 encoderFlags
|= nsIDocumentEncoder::OutputWrap
;
1220 if (aEncoderFlags
& nsIWebBrowserPersist::ENCODE_FLAGS_FORMAT_FLOWED
)
1221 encoderFlags
|= nsIDocumentEncoder::OutputFormatFlowed
;
1222 if (aEncoderFlags
& nsIWebBrowserPersist::ENCODE_FLAGS_ABSOLUTE_LINKS
)
1223 encoderFlags
|= nsIDocumentEncoder::OutputAbsoluteLinks
;
1224 if (aEncoderFlags
& nsIWebBrowserPersist::ENCODE_FLAGS_ENCODE_BASIC_ENTITIES
)
1225 encoderFlags
|= nsIDocumentEncoder::OutputEncodeBasicEntities
;
1226 if (aEncoderFlags
& nsIWebBrowserPersist::ENCODE_FLAGS_CR_LINEBREAKS
)
1227 encoderFlags
|= nsIDocumentEncoder::OutputCRLineBreak
;
1228 if (aEncoderFlags
& nsIWebBrowserPersist::ENCODE_FLAGS_LF_LINEBREAKS
)
1229 encoderFlags
|= nsIDocumentEncoder::OutputLFLineBreak
;
1230 if (aEncoderFlags
& nsIWebBrowserPersist::ENCODE_FLAGS_NOSCRIPT_CONTENT
)
1231 encoderFlags
|= nsIDocumentEncoder::OutputNoScriptContent
;
1232 if (aEncoderFlags
& nsIWebBrowserPersist::ENCODE_FLAGS_NOFRAMES_CONTENT
)
1233 encoderFlags
|= nsIDocumentEncoder::OutputNoFramesContent
;
1235 return encoderFlags
;
1238 static bool ContentTypeEncoderExists(const nsACString
& aType
) {
1239 return do_getDocumentTypeSupportedForEncoding(
1240 PromiseFlatCString(aType
).get());
1243 void WebBrowserPersistLocalDocument::DecideContentType(
1244 nsACString
& aContentType
) {
1245 if (aContentType
.IsEmpty()) {
1246 if (NS_WARN_IF(NS_FAILED(GetContentType(aContentType
)))) {
1247 aContentType
.Truncate();
1250 if (!aContentType
.IsEmpty() && !ContentTypeEncoderExists(aContentType
)) {
1251 aContentType
.Truncate();
1253 if (aContentType
.IsEmpty()) {
1254 aContentType
.AssignLiteral("text/html");
1258 nsresult
WebBrowserPersistLocalDocument::GetDocEncoder(
1259 const nsACString
& aContentType
, uint32_t aEncoderFlags
,
1260 nsIDocumentEncoder
** aEncoder
) {
1261 nsCOMPtr
<nsIDocumentEncoder
> encoder
=
1262 do_createDocumentEncoder(PromiseFlatCString(aContentType
).get());
1263 NS_ENSURE_TRUE(encoder
, NS_ERROR_FAILURE
);
1266 encoder
->NativeInit(mDocument
, NS_ConvertASCIItoUTF16(aContentType
),
1267 ConvertEncoderFlags(aEncoderFlags
));
1268 NS_ENSURE_SUCCESS(rv
, NS_ERROR_FAILURE
);
1270 nsAutoCString charSet
;
1271 rv
= GetCharacterSet(charSet
);
1272 NS_ENSURE_SUCCESS(rv
, NS_ERROR_FAILURE
);
1273 rv
= encoder
->SetCharset(charSet
);
1274 NS_ENSURE_SUCCESS(rv
, NS_ERROR_FAILURE
);
1276 encoder
.forget(aEncoder
);
1281 WebBrowserPersistLocalDocument::WriteContent(
1282 nsIOutputStream
* aStream
, nsIWebBrowserPersistURIMap
* aMap
,
1283 const nsACString
& aRequestedContentType
, uint32_t aEncoderFlags
,
1284 uint32_t aWrapColumn
, nsIWebBrowserPersistWriteCompletion
* aCompletion
) {
1285 NS_ENSURE_ARG_POINTER(aStream
);
1286 NS_ENSURE_ARG_POINTER(aCompletion
);
1287 nsAutoCString
contentType(aRequestedContentType
);
1288 DecideContentType(contentType
);
1290 nsCOMPtr
<nsIDocumentEncoder
> encoder
;
1292 GetDocEncoder(contentType
, aEncoderFlags
, getter_AddRefs(encoder
));
1293 NS_ENSURE_SUCCESS(rv
, rv
);
1295 if (aWrapColumn
!= 0 &&
1296 (aEncoderFlags
& nsIWebBrowserPersist::ENCODE_FLAGS_WRAP
)) {
1297 encoder
->SetWrapColumn(aWrapColumn
);
1300 nsCOMPtr
<nsIURI
> targetURI
;
1302 nsAutoCString targetURISpec
;
1303 rv
= aMap
->GetTargetBaseURI(targetURISpec
);
1304 if (NS_SUCCEEDED(rv
) && !targetURISpec
.IsEmpty()) {
1305 rv
= NS_NewURI(getter_AddRefs(targetURI
), targetURISpec
);
1306 NS_ENSURE_SUCCESS(rv
, NS_ERROR_UNEXPECTED
);
1307 } else if (mPersistFlags
&
1308 nsIWebBrowserPersist::PERSIST_FLAGS_FIXUP_LINKS_TO_DESTINATION
) {
1309 return NS_ERROR_UNEXPECTED
;
1312 rv
= encoder
->SetNodeFixup(new PersistNodeFixup(this, aMap
, targetURI
));
1313 NS_ENSURE_SUCCESS(rv
, NS_ERROR_FAILURE
);
1315 rv
= encoder
->EncodeToStream(aStream
);
1316 aCompletion
->OnFinish(this, aStream
, contentType
, rv
);
1320 } // namespace mozilla