gecko-dev/dom/webbrowserpersist/WebBrowserPersistLocalDocum...

1366 строки
43 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "WebBrowserPersistLocalDocument.h"
#include "WebBrowserPersistDocumentParent.h"
#include "mozilla/dom/HTMLAnchorElement.h"
#include "mozilla/dom/HTMLAreaElement.h"
#include "mozilla/dom/HTMLInputElement.h"
#include "mozilla/dom/HTMLLinkElement.h"
#include "mozilla/dom/HTMLObjectElement.h"
#include "mozilla/dom/HTMLOptionElement.h"
#include "mozilla/dom/HTMLSharedElement.h"
#include "mozilla/dom/HTMLTextAreaElement.h"
#include "mozilla/dom/TabParent.h"
#include "nsComponentManagerUtils.h"
#include "nsContentUtils.h"
#include "nsContentCID.h"
#include "nsCycleCollectionParticipant.h"
#include "nsFrameLoader.h"
#include "nsIComponentRegistrar.h"
#include "nsIContent.h"
#include "nsIDOMAttr.h"
#include "nsIDOMComment.h"
#include "nsIDOMDocument.h"
#include "nsIDOMHTMLDocument.h"
#include "nsIDOMHTMLInputElement.h"
#include "nsIDOMHTMLMediaElement.h"
#include "nsIDOMMozNamedAttrMap.h"
#include "nsIDOMNode.h"
#include "nsIDOMNodeFilter.h"
#include "nsIDOMNodeList.h"
#include "nsIDOMProcessingInstruction.h"
#include "nsIDOMTreeWalker.h"
#include "nsIDOMWindowUtils.h"
#include "nsIDocShell.h"
#include "nsIDocument.h"
#include "nsIDocumentEncoder.h"
#include "nsILoadContext.h"
#include "nsIProtocolHandler.h"
#include "nsISHEntry.h"
#include "nsISupportsPrimitives.h"
#include "nsITabParent.h"
#include "nsIWebBrowserPersist.h"
#include "nsIWebNavigation.h"
#include "nsIWebPageDescriptor.h"
#include "nsNetUtil.h"
namespace mozilla {
NS_IMPL_CYCLE_COLLECTING_ADDREF(WebBrowserPersistLocalDocument)
NS_IMPL_CYCLE_COLLECTING_RELEASE(WebBrowserPersistLocalDocument)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebBrowserPersistLocalDocument)
NS_INTERFACE_MAP_ENTRY(nsIWebBrowserPersistDocument)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTION(WebBrowserPersistLocalDocument, mDocument)
WebBrowserPersistLocalDocument::WebBrowserPersistLocalDocument(nsIDocument* aDocument)
: mDocument(aDocument)
, mPersistFlags(0)
{
MOZ_ASSERT(mDocument);
}
WebBrowserPersistLocalDocument::~WebBrowserPersistLocalDocument() = default;
NS_IMETHODIMP
WebBrowserPersistLocalDocument::SetPersistFlags(uint32_t aFlags)
{
mPersistFlags = aFlags;
return NS_OK;
}
NS_IMETHODIMP
WebBrowserPersistLocalDocument::GetPersistFlags(uint32_t* aFlags)
{
*aFlags = mPersistFlags;
return NS_OK;
}
NS_IMETHODIMP
WebBrowserPersistLocalDocument::GetIsPrivate(bool* aIsPrivate)
{
nsCOMPtr<nsILoadContext> privacyContext = mDocument->GetLoadContext();
*aIsPrivate = privacyContext && privacyContext->UsePrivateBrowsing();
return NS_OK;
}
NS_IMETHODIMP
WebBrowserPersistLocalDocument::GetDocumentURI(nsACString& aURISpec)
{
nsCOMPtr<nsIURI> uri = mDocument->GetDocumentURI();
if (!uri) {
return NS_ERROR_UNEXPECTED;
}
return uri->GetSpec(aURISpec);
}
NS_IMETHODIMP
WebBrowserPersistLocalDocument::GetBaseURI(nsACString& aURISpec)
{
nsCOMPtr<nsIURI> uri = GetBaseURI();
if (!uri) {
return NS_ERROR_UNEXPECTED;
}
return uri->GetSpec(aURISpec);
}
NS_IMETHODIMP
WebBrowserPersistLocalDocument::GetContentType(nsACString& aContentType)
{
nsAutoString utf16Type;
nsresult rv;
rv = mDocument->GetContentType(utf16Type);
NS_ENSURE_SUCCESS(rv, rv);
aContentType = NS_ConvertUTF16toUTF8(utf16Type);
return NS_OK;
}
NS_IMETHODIMP
WebBrowserPersistLocalDocument::GetCharacterSet(nsACString& aCharSet)
{
GetCharacterSet()->Name(aCharSet);
return NS_OK;
}
NS_IMETHODIMP
WebBrowserPersistLocalDocument::GetTitle(nsAString& aTitle)
{
nsAutoString titleBuffer;
mDocument->GetTitle(titleBuffer);
aTitle = titleBuffer;
return NS_OK;
}
NS_IMETHODIMP
WebBrowserPersistLocalDocument::GetReferrer(nsAString& aReferrer)
{
mDocument->GetReferrer(aReferrer);
return NS_OK;
}
NS_IMETHODIMP
WebBrowserPersistLocalDocument::GetContentDisposition(nsAString& aCD)
{
nsCOMPtr<nsPIDOMWindowOuter> window = mDocument->GetDefaultView();
if (NS_WARN_IF(!window)) {
aCD.SetIsVoid(true);
return NS_OK;
}
nsCOMPtr<nsIDOMWindowUtils> utils = do_GetInterface(window);
if (NS_WARN_IF(!utils)) {
aCD.SetIsVoid(true);
return NS_OK;
}
nsresult rv = utils->GetDocumentMetadata(
NS_LITERAL_STRING("content-disposition"), aCD);
if (NS_WARN_IF(NS_FAILED(rv))) {
aCD.SetIsVoid(true);
}
return NS_OK;
}
NS_IMETHODIMP
WebBrowserPersistLocalDocument::GetCacheKey(uint32_t* aKey)
{
nsCOMPtr<nsISHEntry> history = GetHistory();
if (!history) {
*aKey = 0;
return NS_OK;
}
nsCOMPtr<nsISupports> abstractKey;
nsresult rv = history->GetCacheKey(getter_AddRefs(abstractKey));
if (NS_WARN_IF(NS_FAILED(rv)) || !abstractKey) {
*aKey = 0;
return NS_OK;
}
nsCOMPtr<nsISupportsPRUint32> u32 = do_QueryInterface(abstractKey);
if (NS_WARN_IF(!u32)) {
*aKey = 0;
return NS_OK;
}
return u32->GetData(aKey);
}
NS_IMETHODIMP
WebBrowserPersistLocalDocument::GetPostData(nsIInputStream** aStream)
{
nsCOMPtr<nsISHEntry> history = GetHistory();
if (!history) {
*aStream = nullptr;
return NS_OK;
}
return history->GetPostData(aStream);
}
already_AddRefed<nsISHEntry>
WebBrowserPersistLocalDocument::GetHistory()
{
nsCOMPtr<nsPIDOMWindowOuter> window = mDocument->GetDefaultView();
if (NS_WARN_IF(!window)) {
return nullptr;
}
nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(window);
if (NS_WARN_IF(!webNav)) {
return nullptr;
}
nsCOMPtr<nsIWebPageDescriptor> desc = do_QueryInterface(webNav);
if (NS_WARN_IF(!desc)) {
return nullptr;
}
nsCOMPtr<nsISupports> curDesc;
nsresult rv = desc->GetCurrentDescriptor(getter_AddRefs(curDesc));
// This can fail if, e.g., the document is a Print Preview.
if (NS_FAILED(rv) || NS_WARN_IF(!curDesc)) {
return nullptr;
}
nsCOMPtr<nsISHEntry> history = do_QueryInterface(curDesc);
return history.forget();
}
NotNull<const Encoding*>
WebBrowserPersistLocalDocument::GetCharacterSet() const
{
return mDocument->GetDocumentCharacterSet();
}
uint32_t
WebBrowserPersistLocalDocument::GetPersistFlags() const
{
return mPersistFlags;
}
already_AddRefed<nsIURI>
WebBrowserPersistLocalDocument::GetBaseURI() const
{
return mDocument->GetBaseURI();
}
namespace {
// Helper class for ReadResources().
class ResourceReader final : public nsIWebBrowserPersistDocumentReceiver {
public:
ResourceReader(WebBrowserPersistLocalDocument* aParent,
nsIWebBrowserPersistResourceVisitor* aVisitor);
nsresult OnWalkDOMNode(nsIDOMNode* aNode);
// This is called both to indicate the end of the document walk
// and when a subdocument is (maybe asynchronously) sent to the
// visitor. The call to EndVisit needs to happen after both of
// those have finished.
void DocumentDone(nsresult aStatus);
NS_DECL_NSIWEBBROWSERPERSISTDOCUMENTRECEIVER
NS_DECL_ISUPPORTS
private:
RefPtr<WebBrowserPersistLocalDocument> mParent;
nsCOMPtr<nsIWebBrowserPersistResourceVisitor> mVisitor;
nsCOMPtr<nsIURI> mCurrentBaseURI;
uint32_t mPersistFlags;
// The number of DocumentDone calls after which EndVisit will be
// called on the visitor. Counts the main document if it's still
// being walked and any outstanding asynchronous subdocument
// StartPersistence calls.
size_t mOutstandingDocuments;
// Collects the status parameters to DocumentDone calls.
nsresult mEndStatus;
nsresult OnWalkURI(const nsACString& aURISpec);
nsresult OnWalkURI(nsIURI* aURI);
nsresult OnWalkAttribute(nsIDOMNode* aNode,
const char* aAttribute,
const char* aNamespaceURI = "");
nsresult OnWalkSubframe(nsIDOMNode* aNode);
~ResourceReader();
using IWBP = nsIWebBrowserPersist;
};
NS_IMPL_ISUPPORTS(ResourceReader, nsIWebBrowserPersistDocumentReceiver)
ResourceReader::ResourceReader(WebBrowserPersistLocalDocument* aParent,
nsIWebBrowserPersistResourceVisitor* aVisitor)
: mParent(aParent)
, mVisitor(aVisitor)
, mCurrentBaseURI(aParent->GetBaseURI())
, mPersistFlags(aParent->GetPersistFlags())
, mOutstandingDocuments(1)
, mEndStatus(NS_OK)
{
MOZ_ASSERT(mCurrentBaseURI);
}
ResourceReader::~ResourceReader()
{
MOZ_ASSERT(mOutstandingDocuments == 0);
}
void
ResourceReader::DocumentDone(nsresult aStatus)
{
MOZ_ASSERT(mOutstandingDocuments > 0);
if (NS_SUCCEEDED(mEndStatus)) {
mEndStatus = aStatus;
}
if (--mOutstandingDocuments == 0) {
mVisitor->EndVisit(mParent, mEndStatus);
}
}
nsresult
ResourceReader::OnWalkSubframe(nsIDOMNode* aNode)
{
nsCOMPtr<nsIFrameLoaderOwner> loaderOwner = do_QueryInterface(aNode);
NS_ENSURE_STATE(loaderOwner);
RefPtr<nsFrameLoader> loader = loaderOwner->GetFrameLoader();
NS_ENSURE_STATE(loader);
++mOutstandingDocuments;
// Pass in 0 as the outer window ID so that we start
// persisting the root of this subframe, and not some other
// subframe child of this subframe.
nsresult rv = loader->StartPersistence(0, this);
if (NS_FAILED(rv)) {
if (rv == NS_ERROR_NO_CONTENT) {
// Just ignore frames with no content document.
rv = NS_OK;
}
// StartPersistence won't eventually call this if it failed,
// so this does so (to keep mOutstandingDocuments correct).
DocumentDone(rv);
}
return rv;
}
NS_IMETHODIMP
ResourceReader::OnDocumentReady(nsIWebBrowserPersistDocument* aDocument)
{
mVisitor->VisitDocument(mParent, aDocument);
DocumentDone(NS_OK);
return NS_OK;
}
NS_IMETHODIMP
ResourceReader::OnError(nsresult aFailure)
{
DocumentDone(aFailure);
return NS_OK;
}
nsresult
ResourceReader::OnWalkURI(nsIURI* aURI)
{
// Test if this URI should be persisted. By default
// we should assume the URI is persistable.
bool doNotPersistURI;
nsresult rv = NS_URIChainHasFlags(aURI,
nsIProtocolHandler::URI_NON_PERSISTABLE,
&doNotPersistURI);
if (NS_SUCCEEDED(rv) && doNotPersistURI) {
return NS_OK;
}
nsAutoCString stringURI;
rv = aURI->GetSpec(stringURI);
NS_ENSURE_SUCCESS(rv, rv);
return mVisitor->VisitResource(mParent, stringURI);
}
nsresult
ResourceReader::OnWalkURI(const nsACString& aURISpec)
{
nsresult rv;
nsCOMPtr<nsIURI> uri;
rv = NS_NewURI(getter_AddRefs(uri),
aURISpec,
mParent->GetCharacterSet(),
mCurrentBaseURI);
NS_ENSURE_SUCCESS(rv, rv);
return OnWalkURI(uri);
}
static nsresult
ExtractAttribute(nsIDOMNode* aNode,
const char* aAttribute,
const char* aNamespaceURI,
nsCString& aValue)
{
nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aNode);
MOZ_ASSERT(element);
// Find the named URI attribute on the (element) node and store
// a reference to the URI that maps onto a local file name
nsCOMPtr<nsIDOMMozNamedAttrMap> attrMap;
nsresult rv = element->GetAttributes(getter_AddRefs(attrMap));
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
NS_ConvertASCIItoUTF16 namespaceURI(aNamespaceURI);
NS_ConvertASCIItoUTF16 attribute(aAttribute);
nsCOMPtr<nsIDOMAttr> attr;
rv = attrMap->GetNamedItemNS(namespaceURI, attribute, getter_AddRefs(attr));
NS_ENSURE_SUCCESS(rv, rv);
if (attr) {
nsAutoString value;
rv = attr->GetValue(value);
NS_ENSURE_SUCCESS(rv, rv);
aValue = NS_ConvertUTF16toUTF8(value);
} else {
aValue.Truncate();
}
return NS_OK;
}
nsresult
ResourceReader::OnWalkAttribute(nsIDOMNode* aNode,
const char* aAttribute,
const char* aNamespaceURI)
{
nsAutoCString uriSpec;
nsresult rv = ExtractAttribute(aNode, aAttribute, aNamespaceURI, uriSpec);
NS_ENSURE_SUCCESS(rv, rv);
if (uriSpec.IsEmpty()) {
return NS_OK;
}
return OnWalkURI(uriSpec);
}
static nsresult
GetXMLStyleSheetLink(nsIDOMProcessingInstruction *aPI, nsAString &aHref)
{
nsresult rv;
nsAutoString data;
rv = aPI->GetData(data);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::href, aHref);
return NS_OK;
}
nsresult
ResourceReader::OnWalkDOMNode(nsIDOMNode* aNode)
{
nsresult rv;
// Fixup xml-stylesheet processing instructions
nsCOMPtr<nsIDOMProcessingInstruction> nodeAsPI = do_QueryInterface(aNode);
if (nodeAsPI) {
nsAutoString target;
rv = nodeAsPI->GetTarget(target);
NS_ENSURE_SUCCESS(rv, rv);
if (target.EqualsLiteral("xml-stylesheet")) {
nsAutoString href;
GetXMLStyleSheetLink(nodeAsPI, href);
if (!href.IsEmpty()) {
return OnWalkURI(NS_ConvertUTF16toUTF8(href));
}
}
return NS_OK;
}
nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
if (!content) {
return NS_OK;
}
// Test the node to see if it's an image, frame, iframe, css, js
if (content->IsHTMLElement(nsGkAtoms::img)) {
return OnWalkAttribute(aNode, "src");
}
if (content->IsSVGElement(nsGkAtoms::img)) {
return OnWalkAttribute(aNode, "href", "http://www.w3.org/1999/xlink");
}
nsCOMPtr<nsIDOMHTMLMediaElement> nodeAsMedia = do_QueryInterface(aNode);
if (nodeAsMedia) {
return OnWalkAttribute(aNode, "src");
}
if (content->IsHTMLElement(nsGkAtoms::source)) {
return OnWalkAttribute(aNode, "src");
}
if (content->IsHTMLElement(nsGkAtoms::body)) {
return OnWalkAttribute(aNode, "background");
}
if (content->IsHTMLElement(nsGkAtoms::table)) {
return OnWalkAttribute(aNode, "background");
}
if (content->IsHTMLElement(nsGkAtoms::tr)) {
return OnWalkAttribute(aNode, "background");
}
if (content->IsAnyOfHTMLElements(nsGkAtoms::td, nsGkAtoms::th)) {
return OnWalkAttribute(aNode, "background");
}
if (content->IsHTMLElement(nsGkAtoms::script)) {
return OnWalkAttribute(aNode, "src");
}
if (content->IsSVGElement(nsGkAtoms::script)) {
return OnWalkAttribute(aNode, "href", "http://www.w3.org/1999/xlink");
}
if (content->IsHTMLElement(nsGkAtoms::embed)) {
return OnWalkAttribute(aNode, "src");
}
if (content->IsHTMLElement(nsGkAtoms::object)) {
return OnWalkAttribute(aNode, "data");
}
if (auto nodeAsLink = dom::HTMLLinkElement::FromContent(content)) {
// Test if the link has a rel value indicating it to be a stylesheet
nsAutoString linkRel;
nodeAsLink->GetRel(linkRel);
if (!linkRel.IsEmpty()) {
nsReadingIterator<char16_t> start;
nsReadingIterator<char16_t> end;
nsReadingIterator<char16_t> current;
linkRel.BeginReading(start);
linkRel.EndReading(end);
// Walk through space delimited string looking for "stylesheet"
for (current = start; current != end; ++current) {
// Ignore whitespace
if (nsCRT::IsAsciiSpace(*current)) {
continue;
}
// Grab the next space delimited word
nsReadingIterator<char16_t> startWord = current;
do {
++current;
} while (current != end && !nsCRT::IsAsciiSpace(*current));
// Store the link for fix up if it says "stylesheet"
if (Substring(startWord, current)
.LowerCaseEqualsLiteral("stylesheet")) {
OnWalkAttribute(aNode, "href");
return NS_OK;
}
if (current == end) {
break;
}
}
}
return NS_OK;
}
if (content->IsHTMLElement(nsGkAtoms::frame)) {
return OnWalkSubframe(aNode);
}
if (content->IsHTMLElement(nsGkAtoms::iframe) &&
!(mPersistFlags & IWBP::PERSIST_FLAGS_IGNORE_IFRAMES)) {
return OnWalkSubframe(aNode);
}
nsCOMPtr<nsIDOMHTMLInputElement> nodeAsInput = do_QueryInterface(aNode);
if (nodeAsInput) {
return OnWalkAttribute(aNode, "src");
}
return NS_OK;
}
// Helper class for node rewriting in writeContent().
class PersistNodeFixup final : public nsIDocumentEncoderNodeFixup {
public:
PersistNodeFixup(WebBrowserPersistLocalDocument* aParent,
nsIWebBrowserPersistURIMap* aMap,
nsIURI* aTargetURI);
NS_DECL_ISUPPORTS
NS_DECL_NSIDOCUMENTENCODERNODEFIXUP
private:
virtual ~PersistNodeFixup() = default;
RefPtr<WebBrowserPersistLocalDocument> mParent;
nsClassHashtable<nsCStringHashKey, nsCString> mMap;
nsCOMPtr<nsIURI> mCurrentBaseURI;
nsCOMPtr<nsIURI> mTargetBaseURI;
bool IsFlagSet(uint32_t aFlag) const {
return mParent->GetPersistFlags() & aFlag;
}
nsresult GetNodeToFixup(nsIDOMNode* aNodeIn, nsIDOMNode** aNodeOut);
nsresult FixupURI(nsAString& aURI);
nsresult FixupAttribute(nsIDOMNode* aNode,
const char* aAttribute,
const char* aNamespaceURI = "");
nsresult FixupAnchor(nsIDOMNode* aNode);
nsresult FixupXMLStyleSheetLink(nsIDOMProcessingInstruction* aPI,
const nsAString& aHref);
using IWBP = nsIWebBrowserPersist;
};
NS_IMPL_ISUPPORTS(PersistNodeFixup, nsIDocumentEncoderNodeFixup)
PersistNodeFixup::PersistNodeFixup(WebBrowserPersistLocalDocument* aParent,
nsIWebBrowserPersistURIMap* aMap,
nsIURI* aTargetURI)
: mParent(aParent)
, mCurrentBaseURI(aParent->GetBaseURI())
, mTargetBaseURI(aTargetURI)
{
if (aMap) {
uint32_t mapSize;
nsresult rv = aMap->GetNumMappedURIs(&mapSize);
MOZ_ASSERT(NS_SUCCEEDED(rv));
NS_ENSURE_SUCCESS_VOID(rv);
for (uint32_t i = 0; i < mapSize; ++i) {
nsAutoCString urlFrom;
auto* urlTo = new nsCString();
rv = aMap->GetURIMapping(i, urlFrom, *urlTo);
MOZ_ASSERT(NS_SUCCEEDED(rv));
if (NS_SUCCEEDED(rv)) {
mMap.Put(urlFrom, urlTo);
}
}
}
}
nsresult
PersistNodeFixup::GetNodeToFixup(nsIDOMNode *aNodeIn, nsIDOMNode **aNodeOut)
{
// Avoid mixups in FixupNode that could leak objects; this goes
// against the usual out parameter convention, but it's a private
// method so shouldn't be a problem.
MOZ_ASSERT(!*aNodeOut);
if (!IsFlagSet(IWBP::PERSIST_FLAGS_FIXUP_ORIGINAL_DOM)) {
nsresult rv = aNodeIn->CloneNode(false, 1, aNodeOut);
NS_ENSURE_SUCCESS(rv, rv);
} else {
NS_ADDREF(*aNodeOut = aNodeIn);
}
return NS_OK;
}
nsresult
PersistNodeFixup::FixupURI(nsAString &aURI)
{
// get the current location of the file (absolutized)
nsCOMPtr<nsIURI> uri;
nsresult rv = NS_NewURI(getter_AddRefs(uri), aURI,
mParent->GetCharacterSet(), mCurrentBaseURI);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoCString spec;
rv = uri->GetSpec(spec);
NS_ENSURE_SUCCESS(rv, rv);
const nsCString* replacement = mMap.Get(spec);
if (!replacement) {
// Note that most callers ignore this "failure".
return NS_ERROR_FAILURE;
}
if (!replacement->IsEmpty()) {
aURI = NS_ConvertUTF8toUTF16(*replacement);
}
return NS_OK;
}
nsresult
PersistNodeFixup::FixupAttribute(nsIDOMNode* aNode,
const char* aAttribute,
const char* aNamespaceURI)
{
nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aNode);
MOZ_ASSERT(element);
nsCOMPtr<nsIDOMMozNamedAttrMap> attrMap;
nsresult rv = element->GetAttributes(getter_AddRefs(attrMap));
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
NS_ConvertASCIItoUTF16 attribute(aAttribute);
NS_ConvertASCIItoUTF16 namespaceURI(aNamespaceURI);
nsCOMPtr<nsIDOMAttr> attr;
rv = attrMap->GetNamedItemNS(namespaceURI, attribute, getter_AddRefs(attr));
if (attr) {
nsString uri;
attr->GetValue(uri);
rv = FixupURI(uri);
if (NS_SUCCEEDED(rv)) {
attr->SetValue(uri);
}
}
return rv;
}
nsresult
PersistNodeFixup::FixupAnchor(nsIDOMNode *aNode)
{
if (IsFlagSet(IWBP::PERSIST_FLAGS_DONT_FIXUP_LINKS)) {
return NS_OK;
}
nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aNode);
MOZ_ASSERT(element);
nsCOMPtr<nsIDOMMozNamedAttrMap> attrMap;
nsresult rv = element->GetAttributes(getter_AddRefs(attrMap));
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
// Make all anchor links absolute so they point off onto the Internet
nsString attribute(NS_LITERAL_STRING("href"));
nsCOMPtr<nsIDOMAttr> attr;
rv = attrMap->GetNamedItem(attribute, getter_AddRefs(attr));
if (attr) {
nsString oldValue;
attr->GetValue(oldValue);
NS_ConvertUTF16toUTF8 oldCValue(oldValue);
// Skip empty values and self-referencing bookmarks
if (oldCValue.IsEmpty() || oldCValue.CharAt(0) == '#') {
return NS_OK;
}
// if saving file to same location, we don't need to do any fixup
bool isEqual;
if (mTargetBaseURI &&
NS_SUCCEEDED(mCurrentBaseURI->Equals(mTargetBaseURI, &isEqual)) &&
isEqual) {
return NS_OK;
}
nsCOMPtr<nsIURI> relativeURI;
relativeURI = IsFlagSet(IWBP::PERSIST_FLAGS_FIXUP_LINKS_TO_DESTINATION)
? mTargetBaseURI : mCurrentBaseURI;
// Make a new URI to replace the current one
nsCOMPtr<nsIURI> newURI;
rv = NS_NewURI(getter_AddRefs(newURI), oldCValue,
mParent->GetCharacterSet(), relativeURI);
if (NS_SUCCEEDED(rv) && newURI) {
newURI->SetUserPass(EmptyCString());
nsAutoCString uriSpec;
rv = newURI->GetSpec(uriSpec);
NS_ENSURE_SUCCESS(rv, rv);
attr->SetValue(NS_ConvertUTF8toUTF16(uriSpec));
}
}
return NS_OK;
}
static void
AppendXMLAttr(const nsAString& key, const nsAString& aValue, nsAString& aBuffer)
{
if (!aBuffer.IsEmpty()) {
aBuffer.Append(' ');
}
aBuffer.Append(key);
aBuffer.AppendLiteral(R"(=")");
for (size_t i = 0; i < aValue.Length(); ++i) {
switch (aValue[i]) {
case '&':
aBuffer.AppendLiteral("&amp;");
break;
case '<':
aBuffer.AppendLiteral("&lt;");
break;
case '>':
aBuffer.AppendLiteral("&gt;");
break;
case '"':
aBuffer.AppendLiteral("&quot;");
break;
default:
aBuffer.Append(aValue[i]);
break;
}
}
aBuffer.Append('"');
}
nsresult
PersistNodeFixup::FixupXMLStyleSheetLink(nsIDOMProcessingInstruction* aPI,
const nsAString& aHref)
{
NS_ENSURE_ARG_POINTER(aPI);
nsresult rv = NS_OK;
nsAutoString data;
rv = aPI->GetData(data);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
nsAutoString href;
nsContentUtils::GetPseudoAttributeValue(data,
nsGkAtoms::href,
href);
// Construct and set a new data value for the xml-stylesheet
if (!aHref.IsEmpty() && !href.IsEmpty())
{
nsAutoString alternate;
nsAutoString charset;
nsAutoString title;
nsAutoString type;
nsAutoString media;
nsContentUtils::GetPseudoAttributeValue(data,
nsGkAtoms::alternate,
alternate);
nsContentUtils::GetPseudoAttributeValue(data,
nsGkAtoms::charset,
charset);
nsContentUtils::GetPseudoAttributeValue(data,
nsGkAtoms::title,
title);
nsContentUtils::GetPseudoAttributeValue(data,
nsGkAtoms::type,
type);
nsContentUtils::GetPseudoAttributeValue(data,
nsGkAtoms::media,
media);
nsAutoString newData;
AppendXMLAttr(NS_LITERAL_STRING("href"), aHref, newData);
if (!title.IsEmpty()) {
AppendXMLAttr(NS_LITERAL_STRING("title"), title, newData);
}
if (!media.IsEmpty()) {
AppendXMLAttr(NS_LITERAL_STRING("media"), media, newData);
}
if (!type.IsEmpty()) {
AppendXMLAttr(NS_LITERAL_STRING("type"), type, newData);
}
if (!charset.IsEmpty()) {
AppendXMLAttr(NS_LITERAL_STRING("charset"), charset, newData);
}
if (!alternate.IsEmpty()) {
AppendXMLAttr(NS_LITERAL_STRING("alternate"), alternate, newData);
}
aPI->SetData(newData);
}
return rv;
}
NS_IMETHODIMP
PersistNodeFixup::FixupNode(nsIDOMNode *aNodeIn,
bool *aSerializeCloneKids,
nsIDOMNode **aNodeOut)
{
*aNodeOut = nullptr;
*aSerializeCloneKids = false;
uint16_t type;
nsresult rv = aNodeIn->GetNodeType(&type);
NS_ENSURE_SUCCESS(rv, rv);
if (type != nsIDOMNode::ELEMENT_NODE &&
type != nsIDOMNode::PROCESSING_INSTRUCTION_NODE) {
return NS_OK;
}
// Fixup xml-stylesheet processing instructions
nsCOMPtr<nsIDOMProcessingInstruction> nodeAsPI = do_QueryInterface(aNodeIn);
if (nodeAsPI) {
nsAutoString target;
nodeAsPI->GetTarget(target);
if (target.EqualsLiteral("xml-stylesheet"))
{
rv = GetNodeToFixup(aNodeIn, aNodeOut);
if (NS_SUCCEEDED(rv) && *aNodeOut) {
nsCOMPtr<nsIDOMProcessingInstruction> outNode =
do_QueryInterface(*aNodeOut);
nsAutoString href;
GetXMLStyleSheetLink(nodeAsPI, href);
if (!href.IsEmpty()) {
FixupURI(href);
FixupXMLStyleSheetLink(outNode, href);
}
}
}
return NS_OK;
}
nsCOMPtr<nsIContent> content = do_QueryInterface(aNodeIn);
if (!content) {
return NS_OK;
}
// BASE elements are replaced by a comment so relative links are not hosed.
if (!IsFlagSet(IWBP::PERSIST_FLAGS_NO_BASE_TAG_MODIFICATIONS) &&
content->IsHTMLElement(nsGkAtoms::base)) {
nsCOMPtr<nsIDOMDocument> ownerDocument;
// Base uses HTMLSharedElement, which would be awkward to implement
// FromContent on, since it represents multiple elements. Since we've
// already checked IsHTMLElement here, just cast as we were doing.
auto* base = static_cast<dom::HTMLSharedElement*>(content.get());
base->GetOwnerDocument(getter_AddRefs(ownerDocument));
if (ownerDocument) {
nsAutoString href;
base->GetHref(href); // Doesn't matter if this fails
nsCOMPtr<nsIDOMComment> comment;
nsAutoString commentText;
commentText.AssignLiteral(" base ");
if (!href.IsEmpty()) {
commentText += NS_LITERAL_STRING("href=\"") + href
+ NS_LITERAL_STRING("\" ");
}
rv = ownerDocument->CreateComment(commentText,
getter_AddRefs(comment));
if (comment) {
return CallQueryInterface(comment, aNodeOut);
}
}
return NS_OK;
}
// Fix up href and file links in the elements
RefPtr<dom::HTMLAnchorElement> nodeAsAnchor = dom::HTMLAnchorElement::FromContent(content);
if (nodeAsAnchor) {
rv = GetNodeToFixup(aNodeIn, aNodeOut);
if (NS_SUCCEEDED(rv) && *aNodeOut) {
FixupAnchor(*aNodeOut);
}
return rv;
}
RefPtr<dom::HTMLAreaElement> nodeAsArea = dom::HTMLAreaElement::FromContent(content);
if (nodeAsArea) {
rv = GetNodeToFixup(aNodeIn, aNodeOut);
if (NS_SUCCEEDED(rv) && *aNodeOut) {
FixupAnchor(*aNodeOut);
}
return rv;
}
if (content->IsHTMLElement(nsGkAtoms::body)) {
rv = GetNodeToFixup(aNodeIn, aNodeOut);
if (NS_SUCCEEDED(rv) && *aNodeOut) {
FixupAttribute(*aNodeOut, "background");
}
return rv;
}
if (content->IsHTMLElement(nsGkAtoms::table)) {
rv = GetNodeToFixup(aNodeIn, aNodeOut);
if (NS_SUCCEEDED(rv) && *aNodeOut) {
FixupAttribute(*aNodeOut, "background");
}
return rv;
}
if (content->IsHTMLElement(nsGkAtoms::tr)) {
rv = GetNodeToFixup(aNodeIn, aNodeOut);
if (NS_SUCCEEDED(rv) && *aNodeOut) {
FixupAttribute(*aNodeOut, "background");
}
return rv;
}
if (content->IsAnyOfHTMLElements(nsGkAtoms::td, nsGkAtoms::th)) {
rv = GetNodeToFixup(aNodeIn, aNodeOut);
if (NS_SUCCEEDED(rv) && *aNodeOut) {
FixupAttribute(*aNodeOut, "background");
}
return rv;
}
if (content->IsHTMLElement(nsGkAtoms::img)) {
rv = GetNodeToFixup(aNodeIn, aNodeOut);
if (NS_SUCCEEDED(rv) && *aNodeOut) {
// Disable image loads
nsCOMPtr<nsIImageLoadingContent> imgCon =
do_QueryInterface(*aNodeOut);
if (imgCon) {
imgCon->SetLoadingEnabled(false);
}
FixupAnchor(*aNodeOut);
FixupAttribute(*aNodeOut, "src");
}
return rv;
}
nsCOMPtr<nsIDOMHTMLMediaElement> nodeAsMedia = do_QueryInterface(aNodeIn);
if (nodeAsMedia) {
rv = GetNodeToFixup(aNodeIn, aNodeOut);
if (NS_SUCCEEDED(rv) && *aNodeOut) {
FixupAttribute(*aNodeOut, "src");
}
return rv;
}
if (content->IsHTMLElement(nsGkAtoms::source)) {
rv = GetNodeToFixup(aNodeIn, aNodeOut);
if (NS_SUCCEEDED(rv) && *aNodeOut) {
FixupAttribute(*aNodeOut, "src");
}
return rv;
}
if (content->IsSVGElement(nsGkAtoms::img)) {
rv = GetNodeToFixup(aNodeIn, aNodeOut);
if (NS_SUCCEEDED(rv) && *aNodeOut) {
// Disable image loads
nsCOMPtr<nsIImageLoadingContent> imgCon =
do_QueryInterface(*aNodeOut);
if (imgCon)
imgCon->SetLoadingEnabled(false);
// FixupAnchor(*aNodeOut); // XXXjwatt: is this line needed?
FixupAttribute(*aNodeOut, "href", "http://www.w3.org/1999/xlink");
}
return rv;
}
if (content->IsHTMLElement(nsGkAtoms::script)) {
rv = GetNodeToFixup(aNodeIn, aNodeOut);
if (NS_SUCCEEDED(rv) && *aNodeOut) {
FixupAttribute(*aNodeOut, "src");
}
return rv;
}
if (content->IsSVGElement(nsGkAtoms::script)) {
rv = GetNodeToFixup(aNodeIn, aNodeOut);
if (NS_SUCCEEDED(rv) && *aNodeOut) {
FixupAttribute(*aNodeOut, "href", "http://www.w3.org/1999/xlink");
}
return rv;
}
if (content->IsHTMLElement(nsGkAtoms::embed)) {
rv = GetNodeToFixup(aNodeIn, aNodeOut);
if (NS_SUCCEEDED(rv) && *aNodeOut) {
FixupAttribute(*aNodeOut, "src");
}
return rv;
}
if (content->IsHTMLElement(nsGkAtoms::object)) {
rv = GetNodeToFixup(aNodeIn, aNodeOut);
if (NS_SUCCEEDED(rv) && *aNodeOut) {
FixupAttribute(*aNodeOut, "data");
}
return rv;
}
if (content->IsHTMLElement(nsGkAtoms::link)) {
rv = GetNodeToFixup(aNodeIn, aNodeOut);
if (NS_SUCCEEDED(rv) && *aNodeOut) {
// First see if the link represents linked content
rv = FixupAttribute(*aNodeOut, "href");
if (NS_FAILED(rv)) {
// Perhaps this link is actually an anchor to related content
FixupAnchor(*aNodeOut);
}
// TODO if "type" attribute == "text/css"
// fixup stylesheet
}
return rv;
}
if (content->IsHTMLElement(nsGkAtoms::frame)) {
rv = GetNodeToFixup(aNodeIn, aNodeOut);
if (NS_SUCCEEDED(rv) && *aNodeOut) {
FixupAttribute(*aNodeOut, "src");
}
return rv;
}
if (content->IsHTMLElement(nsGkAtoms::iframe)) {
rv = GetNodeToFixup(aNodeIn, aNodeOut);
if (NS_SUCCEEDED(rv) && *aNodeOut) {
FixupAttribute(*aNodeOut, "src");
}
return rv;
}
RefPtr<dom::HTMLInputElement> nodeAsInput =
dom::HTMLInputElement::FromContentOrNull(content);
if (nodeAsInput) {
rv = GetNodeToFixup(aNodeIn, aNodeOut);
if (NS_SUCCEEDED(rv) && *aNodeOut) {
// Disable image loads
nsCOMPtr<nsIImageLoadingContent> imgCon =
do_QueryInterface(*aNodeOut);
if (imgCon) {
imgCon->SetLoadingEnabled(false);
}
FixupAttribute(*aNodeOut, "src");
nsAutoString valueStr;
NS_NAMED_LITERAL_STRING(valueAttr, "value");
// Update element node attributes with user-entered form state
nsCOMPtr<nsIContent> content = do_QueryInterface(*aNodeOut);
RefPtr<dom::HTMLInputElement> outElt =
dom::HTMLInputElement::FromContentOrNull(content);
nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(*aNodeOut);
switch (formControl->ControlType()) {
case NS_FORM_INPUT_EMAIL:
case NS_FORM_INPUT_SEARCH:
case NS_FORM_INPUT_TEXT:
case NS_FORM_INPUT_TEL:
case NS_FORM_INPUT_URL:
case NS_FORM_INPUT_NUMBER:
case NS_FORM_INPUT_RANGE:
case NS_FORM_INPUT_DATE:
case NS_FORM_INPUT_TIME:
case NS_FORM_INPUT_COLOR:
nodeAsInput->GetValue(valueStr, dom::CallerType::System);
// Avoid superfluous value="" serialization
if (valueStr.IsEmpty()) {
IgnoredErrorResult ignored;
outElt->RemoveAttribute(valueAttr, ignored);
} else {
outElt->SetAttribute(valueAttr, valueStr);
}
break;
case NS_FORM_INPUT_CHECKBOX:
case NS_FORM_INPUT_RADIO:
{
bool checked = nodeAsInput->Checked();
IgnoredErrorResult ignored;
outElt->SetDefaultChecked(checked, ignored);
}
break;
default:
break;
}
}
return rv;
}
dom::HTMLTextAreaElement* nodeAsTextArea = dom::HTMLTextAreaElement::FromContent(content);
if (nodeAsTextArea) {
rv = GetNodeToFixup(aNodeIn, aNodeOut);
if (NS_SUCCEEDED(rv) && *aNodeOut) {
// Tell the document encoder to serialize the text child we create below
*aSerializeCloneKids = true;
nsAutoString valueStr;
nodeAsTextArea->GetValue(valueStr);
(*aNodeOut)->SetTextContent(valueStr);
}
return rv;
}
dom::HTMLOptionElement* nodeAsOption = dom::HTMLOptionElement::FromContent(content);
if (nodeAsOption) {
rv = GetNodeToFixup(aNodeIn, aNodeOut);
if (NS_SUCCEEDED(rv) && *aNodeOut) {
nsCOMPtr<nsIContent> outContent = do_QueryInterface(*aNodeOut);
dom::HTMLOptionElement* outElt = dom::HTMLOptionElement::FromContent(outContent);
bool selected = nodeAsOption->Selected();
IgnoredErrorResult ignored;
outElt->SetDefaultSelected(selected, ignored);
}
return rv;
}
return NS_OK;
}
} // unnamed namespace
NS_IMETHODIMP
WebBrowserPersistLocalDocument::ReadResources(nsIWebBrowserPersistResourceVisitor* aVisitor)
{
nsresult rv = NS_OK;
nsCOMPtr<nsIWebBrowserPersistResourceVisitor> visitor = aVisitor;
nsCOMPtr<nsIDOMNode> docAsNode = do_QueryInterface(mDocument);
NS_ENSURE_TRUE(docAsNode, NS_ERROR_FAILURE);
nsCOMPtr<nsIDOMTreeWalker> walker;
nsCOMPtr<nsIDOMDocument> oldStyleDoc = do_QueryInterface(mDocument);
MOZ_ASSERT(oldStyleDoc);
rv = oldStyleDoc->CreateTreeWalker(docAsNode,
nsIDOMNodeFilter::SHOW_ELEMENT |
nsIDOMNodeFilter::SHOW_DOCUMENT |
nsIDOMNodeFilter::SHOW_PROCESSING_INSTRUCTION,
nullptr, 1, getter_AddRefs(walker));
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
MOZ_ASSERT(walker);
RefPtr<ResourceReader> reader = new ResourceReader(this, aVisitor);
nsCOMPtr<nsIDOMNode> currentNode;
walker->GetCurrentNode(getter_AddRefs(currentNode));
while (currentNode) {
rv = reader->OnWalkDOMNode(currentNode);
if (NS_WARN_IF(NS_FAILED(rv))) {
break;
}
rv = walker->NextNode(getter_AddRefs(currentNode));
if (NS_WARN_IF(NS_FAILED(rv))) {
break;
}
}
reader->DocumentDone(rv);
// If NS_FAILED(rv), it was / will be reported by an EndVisit call
// via DocumentDone. This method must return a failure if and
// only if visitor won't be invoked.
return NS_OK;
}
static uint32_t
ConvertEncoderFlags(uint32_t aEncoderFlags)
{
uint32_t encoderFlags = 0;
if (aEncoderFlags & nsIWebBrowserPersist::ENCODE_FLAGS_SELECTION_ONLY)
encoderFlags |= nsIDocumentEncoder::OutputSelectionOnly;
if (aEncoderFlags & nsIWebBrowserPersist::ENCODE_FLAGS_FORMATTED)
encoderFlags |= nsIDocumentEncoder::OutputFormatted;
if (aEncoderFlags & nsIWebBrowserPersist::ENCODE_FLAGS_RAW)
encoderFlags |= nsIDocumentEncoder::OutputRaw;
if (aEncoderFlags & nsIWebBrowserPersist::ENCODE_FLAGS_BODY_ONLY)
encoderFlags |= nsIDocumentEncoder::OutputBodyOnly;
if (aEncoderFlags & nsIWebBrowserPersist::ENCODE_FLAGS_PREFORMATTED)
encoderFlags |= nsIDocumentEncoder::OutputPreformatted;
if (aEncoderFlags & nsIWebBrowserPersist::ENCODE_FLAGS_WRAP)
encoderFlags |= nsIDocumentEncoder::OutputWrap;
if (aEncoderFlags & nsIWebBrowserPersist::ENCODE_FLAGS_FORMAT_FLOWED)
encoderFlags |= nsIDocumentEncoder::OutputFormatFlowed;
if (aEncoderFlags & nsIWebBrowserPersist::ENCODE_FLAGS_ABSOLUTE_LINKS)
encoderFlags |= nsIDocumentEncoder::OutputAbsoluteLinks;
if (aEncoderFlags & nsIWebBrowserPersist::ENCODE_FLAGS_ENCODE_BASIC_ENTITIES)
encoderFlags |= nsIDocumentEncoder::OutputEncodeBasicEntities;
if (aEncoderFlags & nsIWebBrowserPersist::ENCODE_FLAGS_CR_LINEBREAKS)
encoderFlags |= nsIDocumentEncoder::OutputCRLineBreak;
if (aEncoderFlags & nsIWebBrowserPersist::ENCODE_FLAGS_LF_LINEBREAKS)
encoderFlags |= nsIDocumentEncoder::OutputLFLineBreak;
if (aEncoderFlags & nsIWebBrowserPersist::ENCODE_FLAGS_NOSCRIPT_CONTENT)
encoderFlags |= nsIDocumentEncoder::OutputNoScriptContent;
if (aEncoderFlags & nsIWebBrowserPersist::ENCODE_FLAGS_NOFRAMES_CONTENT)
encoderFlags |= nsIDocumentEncoder::OutputNoFramesContent;
return encoderFlags;
}
static bool
ContentTypeEncoderExists(const nsACString& aType)
{
nsAutoCString contractID(NS_DOC_ENCODER_CONTRACTID_BASE);
contractID.Append(aType);
nsCOMPtr<nsIComponentRegistrar> registrar;
nsresult rv = NS_GetComponentRegistrar(getter_AddRefs(registrar));
MOZ_ASSERT(NS_SUCCEEDED(rv));
if (NS_SUCCEEDED(rv) && registrar) {
bool result;
rv = registrar->IsContractIDRegistered(contractID.get(), &result);
MOZ_ASSERT(NS_SUCCEEDED(rv));
return NS_SUCCEEDED(rv) && result;
}
return false;
}
void
WebBrowserPersistLocalDocument::DecideContentType(nsACString& aContentType)
{
if (aContentType.IsEmpty()) {
if (NS_WARN_IF(NS_FAILED(GetContentType(aContentType)))) {
aContentType.Truncate();
}
}
if (!aContentType.IsEmpty() &&
!ContentTypeEncoderExists(aContentType)) {
aContentType.Truncate();
}
if (aContentType.IsEmpty()) {
aContentType.AssignLiteral("text/html");
}
}
nsresult
WebBrowserPersistLocalDocument::GetDocEncoder(const nsACString& aContentType,
uint32_t aEncoderFlags,
nsIDocumentEncoder** aEncoder)
{
nsresult rv;
nsAutoCString contractID(NS_DOC_ENCODER_CONTRACTID_BASE);
contractID.Append(aContentType);
nsCOMPtr<nsIDocumentEncoder> encoder =
do_CreateInstance(contractID.get(), &rv);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
rv = encoder->NativeInit(mDocument,
NS_ConvertASCIItoUTF16(aContentType),
ConvertEncoderFlags(aEncoderFlags));
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
nsAutoCString charSet;
rv = GetCharacterSet(charSet);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
rv = encoder->SetCharset(charSet);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
encoder.forget(aEncoder);
return NS_OK;
}
NS_IMETHODIMP
WebBrowserPersistLocalDocument::WriteContent(
nsIOutputStream* aStream,
nsIWebBrowserPersistURIMap* aMap,
const nsACString& aRequestedContentType,
uint32_t aEncoderFlags,
uint32_t aWrapColumn,
nsIWebBrowserPersistWriteCompletion* aCompletion)
{
NS_ENSURE_ARG_POINTER(aStream);
NS_ENSURE_ARG_POINTER(aCompletion);
nsAutoCString contentType(aRequestedContentType);
DecideContentType(contentType);
nsCOMPtr<nsIDocumentEncoder> encoder;
nsresult rv = GetDocEncoder(contentType, aEncoderFlags,
getter_AddRefs(encoder));
NS_ENSURE_SUCCESS(rv, rv);
if (aWrapColumn != 0 && (aEncoderFlags
& nsIWebBrowserPersist::ENCODE_FLAGS_WRAP)) {
encoder->SetWrapColumn(aWrapColumn);
}
nsCOMPtr<nsIURI> targetURI;
if (aMap) {
nsAutoCString targetURISpec;
rv = aMap->GetTargetBaseURI(targetURISpec);
if (NS_SUCCEEDED(rv) && !targetURISpec.IsEmpty()) {
rv = NS_NewURI(getter_AddRefs(targetURI), targetURISpec,
/* charset: */ nullptr, /* base: */ nullptr);
NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
} else if (mPersistFlags & nsIWebBrowserPersist::PERSIST_FLAGS_FIXUP_LINKS_TO_DESTINATION) {
return NS_ERROR_UNEXPECTED;
}
}
rv = encoder->SetNodeFixup(new PersistNodeFixup(this, aMap, targetURI));
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
rv = encoder->EncodeToStream(aStream);
aCompletion->OnFinish(this, aStream, contentType, rv);
return NS_OK;
}
} // namespace mozilla