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 "nsCycleCollectionParticipant.h"
33 #include "nsDOMAttributeMap.h"
34 #include "nsFrameLoader.h"
35 #include "nsGlobalWindowOuter.h"
36 #include "nsIContent.h"
37 #include "nsICookieJarSettings.h"
38 #include "nsIDOMWindowUtils.h"
39 #include "mozilla/dom/Document.h"
40 #include "nsIDocumentEncoder.h"
41 #include "nsILoadContext.h"
42 #include "nsIProtocolHandler.h"
43 #include "nsISHEntry.h"
44 #include "nsIURIMutator.h"
45 #include "nsIWebBrowserPersist.h"
46 #include "nsIWebNavigation.h"
47 #include "nsIWebPageDescriptor.h"
48 #include "nsNetUtil.h"
49 #include "nsQueryObject.h"
53 NS_IMPL_CYCLE_COLLECTING_ADDREF(WebBrowserPersistLocalDocument
)
54 NS_IMPL_CYCLE_COLLECTING_RELEASE(WebBrowserPersistLocalDocument
)
56 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebBrowserPersistLocalDocument
)
57 NS_INTERFACE_MAP_ENTRY(nsIWebBrowserPersistDocument
)
58 NS_INTERFACE_MAP_ENTRY(nsISupports
)
61 NS_IMPL_CYCLE_COLLECTION(WebBrowserPersistLocalDocument
, mDocument
)
63 WebBrowserPersistLocalDocument::WebBrowserPersistLocalDocument(
64 dom::Document
* aDocument
)
65 : mDocument(aDocument
), mPersistFlags(0) {
66 MOZ_ASSERT(mDocument
);
69 WebBrowserPersistLocalDocument::~WebBrowserPersistLocalDocument() = default;
72 WebBrowserPersistLocalDocument::SetPersistFlags(uint32_t aFlags
) {
73 mPersistFlags
= aFlags
;
78 WebBrowserPersistLocalDocument::GetPersistFlags(uint32_t* aFlags
) {
79 *aFlags
= mPersistFlags
;
84 WebBrowserPersistLocalDocument::GetIsClosed(bool* aIsClosed
) {
90 WebBrowserPersistLocalDocument::GetIsPrivate(bool* aIsPrivate
) {
91 nsCOMPtr
<nsILoadContext
> privacyContext
= mDocument
->GetLoadContext();
92 *aIsPrivate
= privacyContext
&& privacyContext
->UsePrivateBrowsing();
97 WebBrowserPersistLocalDocument::GetDocumentURI(nsACString
& aURISpec
) {
98 nsCOMPtr
<nsIURI
> uri
= mDocument
->GetDocumentURI();
100 return NS_ERROR_UNEXPECTED
;
102 return uri
->GetSpec(aURISpec
);
106 WebBrowserPersistLocalDocument::GetBaseURI(nsACString
& aURISpec
) {
107 nsCOMPtr
<nsIURI
> uri
= GetBaseURI();
109 return NS_ERROR_UNEXPECTED
;
111 return uri
->GetSpec(aURISpec
);
115 WebBrowserPersistLocalDocument::GetContentType(nsACString
& aContentType
) {
116 nsAutoString utf16Type
;
117 mDocument
->GetContentType(utf16Type
);
118 CopyUTF16toUTF8(utf16Type
, aContentType
);
123 WebBrowserPersistLocalDocument::GetCharacterSet(nsACString
& aCharSet
) {
124 GetCharacterSet()->Name(aCharSet
);
129 WebBrowserPersistLocalDocument::GetTitle(nsAString
& aTitle
) {
130 nsAutoString titleBuffer
;
131 mDocument
->GetTitle(titleBuffer
);
132 aTitle
= titleBuffer
;
137 WebBrowserPersistLocalDocument::GetReferrerInfo(
138 nsIReferrerInfo
** aReferrerInfo
) {
139 *aReferrerInfo
= mDocument
->GetReferrerInfo();
140 NS_IF_ADDREF(*aReferrerInfo
);
145 WebBrowserPersistLocalDocument::GetCookieJarSettings(
146 nsICookieJarSettings
** aCookieJarSettings
) {
147 *aCookieJarSettings
= mDocument
->CookieJarSettings();
148 NS_ADDREF(*aCookieJarSettings
);
153 WebBrowserPersistLocalDocument::GetContentDisposition(nsAString
& aCD
) {
154 nsCOMPtr
<nsPIDOMWindowOuter
> window
= mDocument
->GetWindow();
155 if (NS_WARN_IF(!window
)) {
159 nsCOMPtr
<nsIDOMWindowUtils
> utils
=
160 nsGlobalWindowOuter::Cast(window
)->WindowUtils();
161 nsresult rv
= utils
->GetDocumentMetadata(u
"content-disposition"_ns
, aCD
);
162 if (NS_WARN_IF(NS_FAILED(rv
))) {
169 WebBrowserPersistLocalDocument::GetCacheKey(uint32_t* aKey
) {
170 Maybe
<uint32_t> cacheKey
;
172 if (nsDocShell
* docShell
= nsDocShell::Cast(mDocument
->GetDocShell())) {
173 cacheKey
= docShell
->GetCacheKeyFromCurrentEntry();
175 *aKey
= cacheKey
.valueOr(0);
181 WebBrowserPersistLocalDocument::GetPostData(nsIInputStream
** aStream
) {
182 nsCOMPtr
<nsIInputStream
> postData
;
183 if (nsDocShell
* docShell
= nsDocShell::Cast(mDocument
->GetDocShell())) {
184 postData
= docShell
->GetPostDataFromCurrentEntry();
187 postData
.forget(aStream
);
192 WebBrowserPersistLocalDocument::GetPrincipal(nsIPrincipal
** aPrincipal
) {
193 nsCOMPtr
<nsIPrincipal
> nodePrincipal
= mDocument
->NodePrincipal();
194 nodePrincipal
.forget(aPrincipal
);
198 already_AddRefed
<nsISHEntry
> WebBrowserPersistLocalDocument::GetHistory() {
199 nsCOMPtr
<nsPIDOMWindowOuter
> window
= mDocument
->GetWindow();
200 if (NS_WARN_IF(!window
)) {
203 nsCOMPtr
<nsIWebNavigation
> webNav
= do_GetInterface(window
);
204 if (NS_WARN_IF(!webNav
)) {
207 nsCOMPtr
<nsIWebPageDescriptor
> desc
= do_QueryInterface(webNav
);
208 if (NS_WARN_IF(!desc
)) {
211 nsCOMPtr
<nsISupports
> curDesc
;
212 nsresult rv
= desc
->GetCurrentDescriptor(getter_AddRefs(curDesc
));
213 // This can fail if, e.g., the document is a Print Preview.
214 if (NS_FAILED(rv
) || NS_WARN_IF(!curDesc
)) {
217 nsCOMPtr
<nsISHEntry
> history
= do_QueryInterface(curDesc
);
218 return history
.forget();
221 NotNull
<const Encoding
*> WebBrowserPersistLocalDocument::GetCharacterSet()
223 return mDocument
->GetDocumentCharacterSet();
226 uint32_t WebBrowserPersistLocalDocument::GetPersistFlags() const {
227 return mPersistFlags
;
230 nsIURI
* WebBrowserPersistLocalDocument::GetBaseURI() const {
231 return mDocument
->GetBaseURI();
236 // Helper class for ReadResources().
237 class ResourceReader final
: public nsIWebBrowserPersistDocumentReceiver
{
239 ResourceReader(WebBrowserPersistLocalDocument
* aParent
,
240 nsIWebBrowserPersistResourceVisitor
* aVisitor
);
241 nsresult
OnWalkDOMNode(nsINode
* aNode
);
243 // This is called both to indicate the end of the document walk
244 // and when a subdocument is (maybe asynchronously) sent to the
245 // visitor. The call to EndVisit needs to happen after both of
246 // those have finished.
247 void DocumentDone(nsresult aStatus
);
249 NS_DECL_NSIWEBBROWSERPERSISTDOCUMENTRECEIVER
253 RefPtr
<WebBrowserPersistLocalDocument
> mParent
;
254 nsCOMPtr
<nsIWebBrowserPersistResourceVisitor
> mVisitor
;
255 nsCOMPtr
<nsIURI
> mCurrentBaseURI
;
256 uint32_t mPersistFlags
;
258 // The number of DocumentDone calls after which EndVisit will be
259 // called on the visitor. Counts the main document if it's still
260 // being walked and any outstanding asynchronous subdocument
261 // StartPersistence calls.
262 size_t mOutstandingDocuments
;
263 // Collects the status parameters to DocumentDone calls.
266 nsresult
OnWalkURI(const nsACString
& aURISpec
,
267 nsContentPolicyType aContentPolicyType
);
268 nsresult
OnWalkURI(nsIURI
* aURI
, nsContentPolicyType aContentPolicyType
);
269 nsresult
OnWalkAttribute(dom::Element
* aElement
,
270 nsContentPolicyType aContentPolicyType
,
271 const char* aAttribute
,
272 const char* aNamespaceURI
= "");
273 nsresult
OnWalkSubframe(nsINode
* aNode
);
274 nsresult
OnWalkSrcSet(dom::Element
* aElement
);
278 using IWBP
= nsIWebBrowserPersist
;
281 NS_IMPL_ISUPPORTS(ResourceReader
, nsIWebBrowserPersistDocumentReceiver
)
283 ResourceReader::ResourceReader(WebBrowserPersistLocalDocument
* aParent
,
284 nsIWebBrowserPersistResourceVisitor
* aVisitor
)
287 mCurrentBaseURI(aParent
->GetBaseURI()),
288 mPersistFlags(aParent
->GetPersistFlags()),
289 mOutstandingDocuments(1),
291 MOZ_ASSERT(mCurrentBaseURI
);
294 ResourceReader::~ResourceReader() { MOZ_ASSERT(mOutstandingDocuments
== 0); }
296 void ResourceReader::DocumentDone(nsresult aStatus
) {
297 MOZ_ASSERT(mOutstandingDocuments
> 0);
298 if (NS_SUCCEEDED(mEndStatus
)) {
299 mEndStatus
= aStatus
;
301 if (--mOutstandingDocuments
== 0) {
302 mVisitor
->EndVisit(mParent
, mEndStatus
);
306 nsresult
ResourceReader::OnWalkSubframe(nsINode
* aNode
) {
307 RefPtr
<nsFrameLoaderOwner
> loaderOwner
= do_QueryObject(aNode
);
308 NS_ENSURE_STATE(loaderOwner
);
309 RefPtr
<nsFrameLoader
> loader
= loaderOwner
->GetFrameLoader();
310 NS_ENSURE_STATE(loader
);
312 RefPtr
<dom::BrowsingContext
> context
= loader
->GetBrowsingContext();
313 NS_ENSURE_STATE(context
);
315 if (loader
->IsRemoteFrame()) {
316 mVisitor
->VisitBrowsingContext(mParent
, context
);
320 ++mOutstandingDocuments
;
322 loader
->StartPersistence(context
, this, err
);
323 nsresult rv
= err
.StealNSResult();
325 if (rv
== NS_ERROR_NO_CONTENT
) {
326 // Just ignore frames with no content document.
329 // StartPersistence won't eventually call this if it failed,
330 // so this does so (to keep mOutstandingDocuments correct).
337 ResourceReader::OnDocumentReady(nsIWebBrowserPersistDocument
* aDocument
) {
338 mVisitor
->VisitDocument(mParent
, aDocument
);
344 ResourceReader::OnError(nsresult aFailure
) {
345 DocumentDone(aFailure
);
349 nsresult
ResourceReader::OnWalkURI(nsIURI
* aURI
,
350 nsContentPolicyType aContentPolicyType
) {
351 // Test if this URI should be persisted. By default
352 // we should assume the URI is persistable.
353 bool doNotPersistURI
;
354 nsresult rv
= NS_URIChainHasFlags(
355 aURI
, nsIProtocolHandler::URI_NON_PERSISTABLE
, &doNotPersistURI
);
356 if (NS_SUCCEEDED(rv
) && doNotPersistURI
) {
360 nsAutoCString stringURI
;
361 rv
= aURI
->GetSpec(stringURI
);
362 NS_ENSURE_SUCCESS(rv
, rv
);
363 return mVisitor
->VisitResource(mParent
, stringURI
, aContentPolicyType
);
366 nsresult
ResourceReader::OnWalkURI(const nsACString
& aURISpec
,
367 nsContentPolicyType aContentPolicyType
) {
369 nsCOMPtr
<nsIURI
> uri
;
371 rv
= NS_NewURI(getter_AddRefs(uri
), aURISpec
, mParent
->GetCharacterSet(),
374 // We don't want to break saving a page in case of a malformed URI.
377 return OnWalkURI(uri
, aContentPolicyType
);
380 static void ExtractAttribute(dom::Element
* aElement
, const char* aAttribute
,
381 const char* aNamespaceURI
, nsCString
& aValue
) {
382 // Find the named URI attribute on the (element) node and store
383 // a reference to the URI that maps onto a local file name
385 RefPtr
<nsDOMAttributeMap
> attrMap
= aElement
->Attributes();
387 NS_ConvertASCIItoUTF16
namespaceURI(aNamespaceURI
);
388 NS_ConvertASCIItoUTF16
attribute(aAttribute
);
389 RefPtr
<dom::Attr
> attr
= attrMap
->GetNamedItemNS(namespaceURI
, attribute
);
392 attr
->GetValue(value
);
393 CopyUTF16toUTF8(value
, aValue
);
399 nsresult
ResourceReader::OnWalkAttribute(dom::Element
* aElement
,
400 nsContentPolicyType aContentPolicyType
,
401 const char* aAttribute
,
402 const char* aNamespaceURI
) {
403 nsAutoCString uriSpec
;
404 ExtractAttribute(aElement
, aAttribute
, aNamespaceURI
, uriSpec
);
405 if (uriSpec
.IsEmpty()) {
408 return OnWalkURI(uriSpec
, aContentPolicyType
);
411 nsresult
ResourceReader::OnWalkSrcSet(dom::Element
* aElement
) {
413 if (!aElement
->GetAttr(nsGkAtoms::srcset
, srcSet
)) {
418 auto eachCandidate
= [&](dom::ResponsiveImageCandidate
&& aCandidate
) {
419 if (!aCandidate
.IsValid() || NS_FAILED(rv
)) {
422 rv
= OnWalkURI(NS_ConvertUTF16toUTF8(aCandidate
.URLString()),
423 nsIContentPolicy::TYPE_IMAGE
);
425 dom::ResponsiveImageSelector::ParseSourceSet(srcSet
, eachCandidate
);
429 static nsresult
GetXMLStyleSheetLink(dom::ProcessingInstruction
* aPI
,
434 nsContentUtils::GetPseudoAttributeValue(data
, nsGkAtoms::href
, aHref
);
438 nsresult
ResourceReader::OnWalkDOMNode(nsINode
* aNode
) {
439 // Fixup xml-stylesheet processing instructions
440 if (auto nodeAsPI
= dom::ProcessingInstruction::FromNode(aNode
)) {
442 nodeAsPI
->GetTarget(target
);
443 if (target
.EqualsLiteral("xml-stylesheet")) {
445 GetXMLStyleSheetLink(nodeAsPI
, href
);
446 if (!href
.IsEmpty()) {
447 return OnWalkURI(NS_ConvertUTF16toUTF8(href
),
448 nsIContentPolicy::TYPE_STYLESHEET
);
454 // Test the node to see if it's an image, frame, iframe, css, js
455 if (auto* img
= dom::HTMLImageElement::FromNode(*aNode
)) {
456 MOZ_TRY(OnWalkAttribute(img
, nsIContentPolicy::TYPE_IMAGE
, "src"));
457 MOZ_TRY(OnWalkSrcSet(img
));
461 if (aNode
->IsSVGElement(nsGkAtoms::image
)) {
462 MOZ_TRY(OnWalkAttribute(aNode
->AsElement(), nsIContentPolicy::TYPE_IMAGE
,
464 return OnWalkAttribute(aNode
->AsElement(), nsIContentPolicy::TYPE_IMAGE
,
465 "href", "http://www.w3.org/1999/xlink");
468 if (aNode
->IsAnyOfHTMLElements(nsGkAtoms::audio
, nsGkAtoms::video
)) {
469 return OnWalkAttribute(aNode
->AsElement(), nsIContentPolicy::TYPE_MEDIA
,
473 if (aNode
->IsHTMLElement(nsGkAtoms::source
)) {
474 MOZ_TRY(OnWalkSrcSet(aNode
->AsElement()));
475 return OnWalkAttribute(aNode
->AsElement(), nsIContentPolicy::TYPE_MEDIA
,
479 if (aNode
->IsHTMLElement(nsGkAtoms::body
)) {
480 return OnWalkAttribute(aNode
->AsElement(), nsIContentPolicy::TYPE_IMAGE
,
484 if (aNode
->IsHTMLElement(nsGkAtoms::table
)) {
485 return OnWalkAttribute(aNode
->AsElement(), nsIContentPolicy::TYPE_IMAGE
,
489 if (aNode
->IsHTMLElement(nsGkAtoms::tr
)) {
490 return OnWalkAttribute(aNode
->AsElement(), nsIContentPolicy::TYPE_IMAGE
,
494 if (aNode
->IsAnyOfHTMLElements(nsGkAtoms::td
, nsGkAtoms::th
)) {
495 return OnWalkAttribute(aNode
->AsElement(), nsIContentPolicy::TYPE_IMAGE
,
499 if (aNode
->IsHTMLElement(nsGkAtoms::script
)) {
500 return OnWalkAttribute(aNode
->AsElement(), nsIContentPolicy::TYPE_SCRIPT
,
504 if (aNode
->IsSVGElement(nsGkAtoms::script
)) {
505 MOZ_TRY(OnWalkAttribute(aNode
->AsElement(), nsIContentPolicy::TYPE_SCRIPT
,
507 return OnWalkAttribute(aNode
->AsElement(), nsIContentPolicy::TYPE_SCRIPT
,
508 "href", "http://www.w3.org/1999/xlink");
511 if (aNode
->IsHTMLElement(nsGkAtoms::embed
)) {
512 return OnWalkAttribute(aNode
->AsElement(), nsIContentPolicy::TYPE_OBJECT
,
516 if (aNode
->IsHTMLElement(nsGkAtoms::object
)) {
517 return OnWalkAttribute(aNode
->AsElement(), nsIContentPolicy::TYPE_OBJECT
,
521 if (auto nodeAsLink
= dom::HTMLLinkElement::FromNode(aNode
)) {
522 // Test if the link has a rel value indicating it to be a stylesheet
523 nsAutoString linkRel
;
524 nodeAsLink
->GetRel(linkRel
);
525 if (!linkRel
.IsEmpty()) {
526 nsReadingIterator
<char16_t
> start
;
527 nsReadingIterator
<char16_t
> end
;
528 nsReadingIterator
<char16_t
> current
;
530 linkRel
.BeginReading(start
);
531 linkRel
.EndReading(end
);
533 // Walk through space delimited string looking for "stylesheet"
534 for (current
= start
; current
!= end
; ++current
) {
536 if (nsCRT::IsAsciiSpace(*current
)) {
540 // Grab the next space delimited word
541 nsReadingIterator
<char16_t
> startWord
= current
;
544 } while (current
!= end
&& !nsCRT::IsAsciiSpace(*current
));
546 // Store the link for fix up if it says "stylesheet"
547 if (Substring(startWord
, current
)
548 .LowerCaseEqualsLiteral("stylesheet")) {
549 OnWalkAttribute(aNode
->AsElement(), nsIContentPolicy::TYPE_STYLESHEET
,
553 if (current
== end
) {
561 if (aNode
->IsHTMLElement(nsGkAtoms::frame
)) {
562 return OnWalkSubframe(aNode
);
565 if (aNode
->IsHTMLElement(nsGkAtoms::iframe
) &&
566 !(mPersistFlags
& IWBP::PERSIST_FLAGS_IGNORE_IFRAMES
)) {
567 return OnWalkSubframe(aNode
);
570 auto nodeAsInput
= dom::HTMLInputElement::FromNode(aNode
);
572 return OnWalkAttribute(aNode
->AsElement(), nsIContentPolicy::TYPE_IMAGE
,
579 // Helper class for node rewriting in writeContent().
580 class PersistNodeFixup final
: public nsIDocumentEncoderNodeFixup
{
582 PersistNodeFixup(WebBrowserPersistLocalDocument
* aParent
,
583 nsIWebBrowserPersistURIMap
* aMap
, nsIURI
* aTargetURI
);
586 NS_DECL_NSIDOCUMENTENCODERNODEFIXUP
588 virtual ~PersistNodeFixup() = default;
589 RefPtr
<WebBrowserPersistLocalDocument
> mParent
;
590 nsClassHashtable
<nsCStringHashKey
, nsCString
> mMap
;
591 nsCOMPtr
<nsIURI
> mCurrentBaseURI
;
592 nsCOMPtr
<nsIURI
> mTargetBaseURI
;
594 bool IsFlagSet(uint32_t aFlag
) const {
595 return mParent
->GetPersistFlags() & aFlag
;
598 nsresult
GetNodeToFixup(nsINode
* aNodeIn
, nsINode
** aNodeOut
);
599 nsresult
FixupURI(nsAString
& aURI
);
600 nsresult
FixupAttribute(nsINode
* aNode
, const char* aAttribute
,
601 const char* aNamespaceURI
= "");
602 nsresult
FixupAnchor(nsINode
* aNode
);
603 nsresult
FixupXMLStyleSheetLink(dom::ProcessingInstruction
* aPI
,
604 const nsAString
& aHref
);
606 nsresult
FixupSrcSet(nsINode
*);
608 using IWBP
= nsIWebBrowserPersist
;
611 NS_IMPL_ISUPPORTS(PersistNodeFixup
, nsIDocumentEncoderNodeFixup
)
613 PersistNodeFixup::PersistNodeFixup(WebBrowserPersistLocalDocument
* aParent
,
614 nsIWebBrowserPersistURIMap
* aMap
,
617 mCurrentBaseURI(aParent
->GetBaseURI()),
618 mTargetBaseURI(aTargetURI
) {
621 nsresult rv
= aMap
->GetNumMappedURIs(&mapSize
);
622 MOZ_ASSERT(NS_SUCCEEDED(rv
));
623 NS_ENSURE_SUCCESS_VOID(rv
);
624 for (uint32_t i
= 0; i
< mapSize
; ++i
) {
625 nsAutoCString urlFrom
;
626 auto urlTo
= MakeUnique
<nsCString
>();
628 rv
= aMap
->GetURIMapping(i
, urlFrom
, *urlTo
);
629 MOZ_ASSERT(NS_SUCCEEDED(rv
));
630 if (NS_SUCCEEDED(rv
)) {
631 mMap
.InsertOrUpdate(urlFrom
, std::move(urlTo
));
637 nsresult
PersistNodeFixup::GetNodeToFixup(nsINode
* aNodeIn
,
638 nsINode
** aNodeOut
) {
639 // Avoid mixups in FixupNode that could leak objects; this goes
640 // against the usual out parameter convention, but it's a private
641 // method so shouldn't be a problem.
642 MOZ_ASSERT(!*aNodeOut
);
644 if (!IsFlagSet(IWBP::PERSIST_FLAGS_FIXUP_ORIGINAL_DOM
)) {
646 *aNodeOut
= aNodeIn
->CloneNode(false, rv
).take();
647 return rv
.StealNSResult();
650 NS_ADDREF(*aNodeOut
= aNodeIn
);
654 nsresult
PersistNodeFixup::FixupURI(nsAString
& aURI
) {
655 // get the current location of the file (absolutized)
656 nsCOMPtr
<nsIURI
> uri
;
657 nsresult rv
= NS_NewURI(getter_AddRefs(uri
), aURI
, mParent
->GetCharacterSet(),
659 NS_ENSURE_SUCCESS(rv
, rv
);
661 rv
= uri
->GetSpec(spec
);
662 NS_ENSURE_SUCCESS(rv
, rv
);
664 const nsCString
* replacement
= mMap
.Get(spec
);
666 // Note that most callers ignore this "failure".
667 return NS_ERROR_FAILURE
;
669 if (!replacement
->IsEmpty()) {
670 CopyUTF8toUTF16(*replacement
, aURI
);
675 nsresult
PersistNodeFixup::FixupSrcSet(nsINode
* aNode
) {
676 dom::Element
* element
= aNode
->AsElement();
677 nsAutoString originalSrcSet
;
678 if (!element
->GetAttr(nsGkAtoms::srcset
, originalSrcSet
)) {
681 nsAutoString newSrcSet
;
683 auto eachCandidate
= [&](dom::ResponsiveImageCandidate
&& aCandidate
) {
684 if (!aCandidate
.IsValid()) {
688 newSrcSet
.AppendLiteral(", ");
691 nsAutoString
uri(aCandidate
.URLString());
693 newSrcSet
.Append(uri
);
694 aCandidate
.AppendDescriptors(newSrcSet
);
696 dom::ResponsiveImageSelector::ParseSourceSet(originalSrcSet
, eachCandidate
);
697 element
->SetAttr(nsGkAtoms::srcset
, newSrcSet
, IgnoreErrors());
701 nsresult
PersistNodeFixup::FixupAttribute(nsINode
* aNode
,
702 const char* aAttribute
,
703 const char* aNamespaceURI
) {
704 MOZ_ASSERT(aNode
->IsElement());
705 dom::Element
* element
= aNode
->AsElement();
707 RefPtr
<nsDOMAttributeMap
> attrMap
= element
->Attributes();
709 NS_ConvertASCIItoUTF16
attribute(aAttribute
);
710 NS_ConvertASCIItoUTF16
namespaceURI(aNamespaceURI
);
711 RefPtr
<dom::Attr
> attr
= attrMap
->GetNamedItemNS(namespaceURI
, attribute
);
717 if (NS_SUCCEEDED(rv
)) {
718 attr
->SetValue(uri
, IgnoreErrors());
725 nsresult
PersistNodeFixup::FixupAnchor(nsINode
* aNode
) {
726 if (IsFlagSet(IWBP::PERSIST_FLAGS_DONT_FIXUP_LINKS
)) {
730 MOZ_ASSERT(aNode
->IsElement());
731 dom::Element
* element
= aNode
->AsElement();
733 RefPtr
<nsDOMAttributeMap
> attrMap
= element
->Attributes();
735 // Make all anchor links absolute so they point off onto the Internet
736 nsString
attribute(u
"href"_ns
);
737 RefPtr
<dom::Attr
> attr
= attrMap
->GetNamedItem(attribute
);
740 attr
->GetValue(oldValue
);
741 NS_ConvertUTF16toUTF8
oldCValue(oldValue
);
743 // Skip empty values and self-referencing bookmarks
744 if (oldCValue
.IsEmpty() || oldCValue
.CharAt(0) == '#') {
748 // if saving file to same location, we don't need to do any fixup
750 if (mTargetBaseURI
&&
751 NS_SUCCEEDED(mCurrentBaseURI
->Equals(mTargetBaseURI
, &isEqual
)) &&
756 nsCOMPtr
<nsIURI
> relativeURI
;
757 relativeURI
= IsFlagSet(IWBP::PERSIST_FLAGS_FIXUP_LINKS_TO_DESTINATION
)
760 // Make a new URI to replace the current one
761 nsCOMPtr
<nsIURI
> newURI
;
762 nsresult rv
= NS_NewURI(getter_AddRefs(newURI
), oldCValue
,
763 mParent
->GetCharacterSet(), relativeURI
);
764 if (NS_SUCCEEDED(rv
) && newURI
) {
765 Unused
<< NS_MutateURI(newURI
).SetUserPass(""_ns
).Finalize(newURI
);
766 nsAutoCString uriSpec
;
767 rv
= newURI
->GetSpec(uriSpec
);
768 NS_ENSURE_SUCCESS(rv
, rv
);
769 attr
->SetValue(NS_ConvertUTF8toUTF16(uriSpec
), IgnoreErrors());
776 static void AppendXMLAttr(const nsAString
& key
, const nsAString
& aValue
,
777 nsAString
& aBuffer
) {
778 if (!aBuffer
.IsEmpty()) {
782 aBuffer
.AppendLiteral(R
"(=")");
783 for (size_t i = 0; i < aValue.Length(); ++i) {
786 aBuffer.AppendLiteral("&
;");
789 aBuffer.AppendLiteral("<
;");
792 aBuffer.AppendLiteral(">
;");
795 aBuffer.AppendLiteral(""");
798 aBuffer.Append(aValue[i]);
805 nsresult PersistNodeFixup::FixupXMLStyleSheetLink(
806 dom::ProcessingInstruction* aPI, const nsAString& aHref) {
807 NS_ENSURE_ARG_POINTER(aPI);
813 nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::href, href);
815 // Construct and set a new data value for the xml-stylesheet
816 if (!aHref.IsEmpty() && !href.IsEmpty()) {
817 nsAutoString alternate;
818 nsAutoString charset;
823 nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::alternate,
825 nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::charset, charset);
826 nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::title, title);
827 nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::type, type);
828 nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::media, media);
830 nsAutoString newData;
831 AppendXMLAttr(u"href
"_ns, aHref, newData);
832 if (!title.IsEmpty()) {
833 AppendXMLAttr(u"title
"_ns, title, newData);
835 if (!media.IsEmpty()) {
836 AppendXMLAttr(u"media
"_ns, media, newData);
838 if (!type.IsEmpty()) {
839 AppendXMLAttr(u"type
"_ns, type, newData);
841 if (!charset.IsEmpty()) {
842 AppendXMLAttr(u"charset
"_ns, charset, newData);
844 if (!alternate.IsEmpty()) {
845 AppendXMLAttr(u"alternate
"_ns, alternate, newData);
847 aPI->SetData(newData, IgnoreErrors());
854 PersistNodeFixup::FixupNode(nsINode* aNodeIn, bool* aSerializeCloneKids,
855 nsINode** aNodeOut) {
857 *aSerializeCloneKids = false;
859 uint16_t type = aNodeIn->NodeType();
860 if (type != nsINode::ELEMENT_NODE &&
861 type != nsINode::PROCESSING_INSTRUCTION_NODE) {
865 MOZ_ASSERT(aNodeIn->IsContent());
867 // Fixup xml-stylesheet processing instructions
868 if (auto nodeAsPI = dom::ProcessingInstruction::FromNode(aNodeIn)) {
870 nodeAsPI->GetTarget(target);
871 if (target.EqualsLiteral("xml
-stylesheet
")) {
872 nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut);
873 if (NS_SUCCEEDED(rv) && *aNodeOut) {
874 MOZ_ASSERT((*aNodeOut)->IsProcessingInstruction());
875 auto nodeAsPI = static_cast<dom::ProcessingInstruction*>(*aNodeOut);
877 GetXMLStyleSheetLink(nodeAsPI, href);
878 if (!href.IsEmpty()) {
880 FixupXMLStyleSheetLink(nodeAsPI, href);
887 nsCOMPtr<nsIContent> content = do_QueryInterface(aNodeIn);
892 // BASE elements are replaced by a comment so relative links are not hosed.
893 if (!IsFlagSet(IWBP::PERSIST_FLAGS_NO_BASE_TAG_MODIFICATIONS) &&
894 content->IsHTMLElement(nsGkAtoms::base)) {
895 // Base uses HTMLSharedElement, which would be awkward to implement
896 // FromContent on, since it represents multiple elements. Since we've
897 // already checked IsHTMLElement here, just cast as we were doing.
898 auto* base = static_cast<dom::HTMLSharedElement*>(content.get());
899 dom::Document* ownerDoc = base->OwnerDoc();
902 base->GetHref(href); // Doesn't matter if this fails
903 nsAutoString commentText;
904 commentText.AssignLiteral(" base
");
905 if (!href.IsEmpty()) {
906 commentText += u"href
=\""_ns + href + u"\" "_ns;
908 *aNodeOut = ownerDoc->CreateComment(commentText).take();
912 // Fix up href and file links in the elements
913 RefPtr<dom::HTMLAnchorElement> nodeAsAnchor =
914 dom::HTMLAnchorElement::FromNode(content);
916 nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut);
917 if (NS_SUCCEEDED(rv) && *aNodeOut) {
918 FixupAnchor(*aNodeOut);
923 RefPtr<dom::HTMLAreaElement> nodeAsArea =
924 dom::HTMLAreaElement::FromNode(content);
926 nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut);
927 if (NS_SUCCEEDED(rv) && *aNodeOut) {
928 FixupAnchor(*aNodeOut);
933 if (content->IsHTMLElement(nsGkAtoms::body)) {
934 nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut);
935 if (NS_SUCCEEDED(rv) && *aNodeOut) {
936 FixupAttribute(*aNodeOut, "background
");
941 if (content->IsHTMLElement(nsGkAtoms::table)) {
942 nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut);
943 if (NS_SUCCEEDED(rv) && *aNodeOut) {
944 FixupAttribute(*aNodeOut, "background
");
949 if (content->IsHTMLElement(nsGkAtoms::tr)) {
950 nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut);
951 if (NS_SUCCEEDED(rv) && *aNodeOut) {
952 FixupAttribute(*aNodeOut, "background
");
957 if (content->IsAnyOfHTMLElements(nsGkAtoms::td, nsGkAtoms::th)) {
958 nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut);
959 if (NS_SUCCEEDED(rv) && *aNodeOut) {
960 FixupAttribute(*aNodeOut, "background
");
965 if (content->IsHTMLElement(nsGkAtoms::img)) {
966 MOZ_TRY(GetNodeToFixup(aNodeIn, aNodeOut));
971 // Disable image loads
972 nsCOMPtr<nsIImageLoadingContent> imgCon = do_QueryInterface(*aNodeOut);
974 imgCon->SetLoadingEnabled(false);
976 // FIXME(emilio): Why fixing up <img href>? Looks bogus
977 FixupAnchor(*aNodeOut);
978 FixupAttribute(*aNodeOut, "src
");
979 FixupSrcSet(*aNodeOut);
983 if (content->IsAnyOfHTMLElements(nsGkAtoms::audio, nsGkAtoms::video)) {
984 nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut);
985 if (NS_SUCCEEDED(rv) && *aNodeOut) {
986 FixupAttribute(*aNodeOut, "src
");
991 if (content->IsHTMLElement(nsGkAtoms::source)) {
992 nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut);
993 if (NS_SUCCEEDED(rv) && *aNodeOut) {
994 FixupAttribute(*aNodeOut, "src
");
995 FixupSrcSet(*aNodeOut);
1000 if (content->IsSVGElement(nsGkAtoms::image)) {
1001 nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut);
1002 if (NS_SUCCEEDED(rv) && *aNodeOut) {
1003 // Disable image loads
1004 nsCOMPtr<nsIImageLoadingContent> imgCon = do_QueryInterface(*aNodeOut);
1005 if (imgCon) imgCon->SetLoadingEnabled(false);
1007 // FixupAnchor(*aNodeOut); // XXXjwatt: is this line needed?
1008 FixupAttribute(*aNodeOut, "href
");
1009 FixupAttribute(*aNodeOut, "href
", "http
://www.w3.org/1999/xlink");
1014 if (content
->IsHTMLElement(nsGkAtoms::script
)) {
1015 nsresult rv
= GetNodeToFixup(aNodeIn
, aNodeOut
);
1016 if (NS_SUCCEEDED(rv
) && *aNodeOut
) {
1017 FixupAttribute(*aNodeOut
, "src");
1022 if (content
->IsSVGElement(nsGkAtoms::script
)) {
1023 nsresult rv
= GetNodeToFixup(aNodeIn
, aNodeOut
);
1024 if (NS_SUCCEEDED(rv
) && *aNodeOut
) {
1025 FixupAttribute(*aNodeOut
, "href");
1026 FixupAttribute(*aNodeOut
, "href", "http://www.w3.org/1999/xlink");
1031 if (content
->IsHTMLElement(nsGkAtoms::embed
)) {
1032 nsresult rv
= GetNodeToFixup(aNodeIn
, aNodeOut
);
1033 if (NS_SUCCEEDED(rv
) && *aNodeOut
) {
1034 FixupAttribute(*aNodeOut
, "src");
1039 if (content
->IsHTMLElement(nsGkAtoms::object
)) {
1040 nsresult rv
= GetNodeToFixup(aNodeIn
, aNodeOut
);
1041 if (NS_SUCCEEDED(rv
) && *aNodeOut
) {
1042 FixupAttribute(*aNodeOut
, "data");
1047 if (content
->IsHTMLElement(nsGkAtoms::link
)) {
1048 nsresult rv
= GetNodeToFixup(aNodeIn
, aNodeOut
);
1049 if (NS_SUCCEEDED(rv
) && *aNodeOut
) {
1050 // First see if the link represents linked content
1051 rv
= FixupAttribute(*aNodeOut
, "href");
1052 if (NS_FAILED(rv
)) {
1053 // Perhaps this link is actually an anchor to related content
1054 FixupAnchor(*aNodeOut
);
1056 // TODO if "type" attribute == "text/css"
1062 if (content
->IsHTMLElement(nsGkAtoms::frame
)) {
1063 nsresult rv
= GetNodeToFixup(aNodeIn
, aNodeOut
);
1064 if (NS_SUCCEEDED(rv
) && *aNodeOut
) {
1065 FixupAttribute(*aNodeOut
, "src");
1070 if (content
->IsHTMLElement(nsGkAtoms::iframe
)) {
1071 nsresult rv
= GetNodeToFixup(aNodeIn
, aNodeOut
);
1072 if (NS_SUCCEEDED(rv
) && *aNodeOut
) {
1073 FixupAttribute(*aNodeOut
, "src");
1078 RefPtr
<dom::HTMLInputElement
> nodeAsInput
=
1079 dom::HTMLInputElement::FromNodeOrNull(content
);
1081 nsresult rv
= GetNodeToFixup(aNodeIn
, aNodeOut
);
1082 if (NS_SUCCEEDED(rv
) && *aNodeOut
) {
1083 // Disable image loads
1084 nsCOMPtr
<nsIImageLoadingContent
> imgCon
= do_QueryInterface(*aNodeOut
);
1086 imgCon
->SetLoadingEnabled(false);
1089 FixupAttribute(*aNodeOut
, "src");
1091 nsAutoString valueStr
;
1092 constexpr auto valueAttr
= u
"value"_ns
;
1093 // Update element node attributes with user-entered form state
1094 RefPtr
<dom::HTMLInputElement
> outElt
=
1095 dom::HTMLInputElement::FromNode((*aNodeOut
)->AsContent());
1096 nsCOMPtr
<nsIFormControl
> formControl
= do_QueryInterface(*aNodeOut
);
1097 switch (formControl
->ControlType()) {
1098 case FormControlType::InputEmail
:
1099 case FormControlType::InputSearch
:
1100 case FormControlType::InputText
:
1101 case FormControlType::InputTel
:
1102 case FormControlType::InputUrl
:
1103 case FormControlType::InputNumber
:
1104 case FormControlType::InputRange
:
1105 case FormControlType::InputDate
:
1106 case FormControlType::InputTime
:
1107 case FormControlType::InputColor
:
1108 nodeAsInput
->GetValue(valueStr
, dom::CallerType::System
);
1109 // Avoid superfluous value="" serialization
1110 if (valueStr
.IsEmpty()) {
1111 outElt
->RemoveAttribute(valueAttr
, IgnoreErrors());
1113 outElt
->SetAttribute(valueAttr
, valueStr
, IgnoreErrors());
1116 case FormControlType::InputCheckbox
:
1117 case FormControlType::InputRadio
:
1118 outElt
->SetDefaultChecked(nodeAsInput
->Checked(), IgnoreErrors());
1127 dom::HTMLTextAreaElement
* nodeAsTextArea
=
1128 dom::HTMLTextAreaElement::FromNode(content
);
1129 if (nodeAsTextArea
) {
1130 nsresult rv
= GetNodeToFixup(aNodeIn
, aNodeOut
);
1131 if (NS_SUCCEEDED(rv
) && *aNodeOut
) {
1132 // Tell the document encoder to serialize the text child we create below
1133 *aSerializeCloneKids
= true;
1135 nsAutoString valueStr
;
1136 nodeAsTextArea
->GetValue(valueStr
);
1138 (*aNodeOut
)->SetTextContent(valueStr
, IgnoreErrors());
1143 dom::HTMLOptionElement
* nodeAsOption
=
1144 dom::HTMLOptionElement::FromNode(content
);
1146 nsresult rv
= GetNodeToFixup(aNodeIn
, aNodeOut
);
1147 if (NS_SUCCEEDED(rv
) && *aNodeOut
) {
1148 dom::HTMLOptionElement
* outElt
=
1149 dom::HTMLOptionElement::FromNode((*aNodeOut
)->AsContent());
1150 bool selected
= nodeAsOption
->Selected();
1151 outElt
->SetDefaultSelected(selected
, IgnoreErrors());
1159 } // unnamed namespace
1162 WebBrowserPersistLocalDocument::ReadResources(
1163 nsIWebBrowserPersistResourceVisitor
* aVisitor
) {
1164 nsresult rv
= NS_OK
;
1165 nsCOMPtr
<nsIWebBrowserPersistResourceVisitor
> visitor
= aVisitor
;
1167 NS_ENSURE_TRUE(mDocument
, NS_ERROR_FAILURE
);
1170 RefPtr
<dom::TreeWalker
> walker
= mDocument
->CreateTreeWalker(
1172 dom::NodeFilter_Binding::SHOW_ELEMENT
|
1173 dom::NodeFilter_Binding::SHOW_DOCUMENT
|
1174 dom::NodeFilter_Binding::SHOW_PROCESSING_INSTRUCTION
,
1177 if (NS_WARN_IF(err
.Failed())) {
1178 return err
.StealNSResult();
1182 RefPtr
<ResourceReader
> reader
= new ResourceReader(this, aVisitor
);
1183 nsCOMPtr
<nsINode
> currentNode
= walker
->CurrentNode();
1185 rv
= reader
->OnWalkDOMNode(currentNode
);
1186 if (NS_WARN_IF(NS_FAILED(rv
))) {
1191 currentNode
= walker
->NextNode(err
);
1192 if (NS_WARN_IF(err
.Failed())) {
1193 err
.SuppressException();
1196 } while (currentNode
);
1197 reader
->DocumentDone(rv
);
1198 // If NS_FAILED(rv), it was / will be reported by an EndVisit call
1199 // via DocumentDone. This method must return a failure if and
1200 // only if visitor won't be invoked.
1204 static uint32_t ConvertEncoderFlags(uint32_t aEncoderFlags
) {
1205 uint32_t encoderFlags
= 0;
1207 if (aEncoderFlags
& nsIWebBrowserPersist::ENCODE_FLAGS_SELECTION_ONLY
)
1208 encoderFlags
|= nsIDocumentEncoder::OutputSelectionOnly
;
1209 if (aEncoderFlags
& nsIWebBrowserPersist::ENCODE_FLAGS_FORMATTED
)
1210 encoderFlags
|= nsIDocumentEncoder::OutputFormatted
;
1211 if (aEncoderFlags
& nsIWebBrowserPersist::ENCODE_FLAGS_RAW
)
1212 encoderFlags
|= nsIDocumentEncoder::OutputRaw
;
1213 if (aEncoderFlags
& nsIWebBrowserPersist::ENCODE_FLAGS_BODY_ONLY
)
1214 encoderFlags
|= nsIDocumentEncoder::OutputBodyOnly
;
1215 if (aEncoderFlags
& nsIWebBrowserPersist::ENCODE_FLAGS_PREFORMATTED
)
1216 encoderFlags
|= nsIDocumentEncoder::OutputPreformatted
;
1217 if (aEncoderFlags
& nsIWebBrowserPersist::ENCODE_FLAGS_WRAP
)
1218 encoderFlags
|= nsIDocumentEncoder::OutputWrap
;
1219 if (aEncoderFlags
& nsIWebBrowserPersist::ENCODE_FLAGS_FORMAT_FLOWED
)
1220 encoderFlags
|= nsIDocumentEncoder::OutputFormatFlowed
;
1221 if (aEncoderFlags
& nsIWebBrowserPersist::ENCODE_FLAGS_ABSOLUTE_LINKS
)
1222 encoderFlags
|= nsIDocumentEncoder::OutputAbsoluteLinks
;
1223 if (aEncoderFlags
& nsIWebBrowserPersist::ENCODE_FLAGS_ENCODE_BASIC_ENTITIES
)
1224 encoderFlags
|= nsIDocumentEncoder::OutputEncodeBasicEntities
;
1225 if (aEncoderFlags
& nsIWebBrowserPersist::ENCODE_FLAGS_CR_LINEBREAKS
)
1226 encoderFlags
|= nsIDocumentEncoder::OutputCRLineBreak
;
1227 if (aEncoderFlags
& nsIWebBrowserPersist::ENCODE_FLAGS_LF_LINEBREAKS
)
1228 encoderFlags
|= nsIDocumentEncoder::OutputLFLineBreak
;
1229 if (aEncoderFlags
& nsIWebBrowserPersist::ENCODE_FLAGS_NOSCRIPT_CONTENT
)
1230 encoderFlags
|= nsIDocumentEncoder::OutputNoScriptContent
;
1231 if (aEncoderFlags
& nsIWebBrowserPersist::ENCODE_FLAGS_NOFRAMES_CONTENT
)
1232 encoderFlags
|= nsIDocumentEncoder::OutputNoFramesContent
;
1234 return encoderFlags
;
1237 static bool ContentTypeEncoderExists(const nsACString
& aType
) {
1238 return do_getDocumentTypeSupportedForEncoding(
1239 PromiseFlatCString(aType
).get());
1242 void WebBrowserPersistLocalDocument::DecideContentType(
1243 nsACString
& aContentType
) {
1244 if (aContentType
.IsEmpty()) {
1245 if (NS_WARN_IF(NS_FAILED(GetContentType(aContentType
)))) {
1246 aContentType
.Truncate();
1249 if (!aContentType
.IsEmpty() && !ContentTypeEncoderExists(aContentType
)) {
1250 aContentType
.Truncate();
1252 if (aContentType
.IsEmpty()) {
1253 aContentType
.AssignLiteral("text/html");
1257 nsresult
WebBrowserPersistLocalDocument::GetDocEncoder(
1258 const nsACString
& aContentType
, uint32_t aEncoderFlags
,
1259 nsIDocumentEncoder
** aEncoder
) {
1260 nsCOMPtr
<nsIDocumentEncoder
> encoder
=
1261 do_createDocumentEncoder(PromiseFlatCString(aContentType
).get());
1262 NS_ENSURE_TRUE(encoder
, NS_ERROR_FAILURE
);
1265 encoder
->NativeInit(mDocument
, NS_ConvertASCIItoUTF16(aContentType
),
1266 ConvertEncoderFlags(aEncoderFlags
));
1267 NS_ENSURE_SUCCESS(rv
, NS_ERROR_FAILURE
);
1269 nsAutoCString charSet
;
1270 rv
= GetCharacterSet(charSet
);
1271 NS_ENSURE_SUCCESS(rv
, NS_ERROR_FAILURE
);
1272 rv
= encoder
->SetCharset(charSet
);
1273 NS_ENSURE_SUCCESS(rv
, NS_ERROR_FAILURE
);
1275 encoder
.forget(aEncoder
);
1280 WebBrowserPersistLocalDocument::WriteContent(
1281 nsIOutputStream
* aStream
, nsIWebBrowserPersistURIMap
* aMap
,
1282 const nsACString
& aRequestedContentType
, uint32_t aEncoderFlags
,
1283 uint32_t aWrapColumn
, nsIWebBrowserPersistWriteCompletion
* aCompletion
) {
1284 NS_ENSURE_ARG_POINTER(aStream
);
1285 NS_ENSURE_ARG_POINTER(aCompletion
);
1286 nsAutoCString
contentType(aRequestedContentType
);
1287 DecideContentType(contentType
);
1289 nsCOMPtr
<nsIDocumentEncoder
> encoder
;
1291 GetDocEncoder(contentType
, aEncoderFlags
, getter_AddRefs(encoder
));
1292 NS_ENSURE_SUCCESS(rv
, rv
);
1294 if (aWrapColumn
!= 0 &&
1295 (aEncoderFlags
& nsIWebBrowserPersist::ENCODE_FLAGS_WRAP
)) {
1296 encoder
->SetWrapColumn(aWrapColumn
);
1299 nsCOMPtr
<nsIURI
> targetURI
;
1301 nsAutoCString targetURISpec
;
1302 rv
= aMap
->GetTargetBaseURI(targetURISpec
);
1303 if (NS_SUCCEEDED(rv
) && !targetURISpec
.IsEmpty()) {
1304 rv
= NS_NewURI(getter_AddRefs(targetURI
), targetURISpec
);
1305 NS_ENSURE_SUCCESS(rv
, NS_ERROR_UNEXPECTED
);
1306 } else if (mPersistFlags
&
1307 nsIWebBrowserPersist::PERSIST_FLAGS_FIXUP_LINKS_TO_DESTINATION
) {
1308 return NS_ERROR_UNEXPECTED
;
1311 rv
= encoder
->SetNodeFixup(new PersistNodeFixup(this, aMap
, targetURI
));
1312 NS_ENSURE_SUCCESS(rv
, NS_ERROR_FAILURE
);
1314 rv
= encoder
->EncodeToStream(aStream
);
1315 aCompletion
->OnFinish(this, aStream
, contentType
, rv
);
1319 } // namespace mozilla