Files
tubestation/embedding/components/webbrowserpersist/WebBrowserPersistLocalDocument.cpp
Jed Davis cdea937a6f Bug 1193903 - Fix nsWebBrowserPersist for documents with no page descriptor. r=mconley
This restores the behavior from before bug 1101100, when various
indirect properties of the document were obtained by JS that ignored
exceptions by using null instead.  This is currently breaking for the
documents created by Print Preview, because they have no page descriptor.
This patch also makes similar changes to the contentDescriptor getter.
2015-08-13 18:08:09 -07:00

1470 lines
47 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/HTMLInputElement.h"
#include "mozilla/dom/HTMLSharedElement.h"
#include "mozilla/dom/HTMLSharedObjectElement.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 "nsIDOMHTMLAnchorElement.h"
#include "nsIDOMHTMLAppletElement.h"
#include "nsIDOMHTMLAreaElement.h"
#include "nsIDOMHTMLBaseElement.h"
#include "nsIDOMHTMLCollection.h"
#include "nsIDOMHTMLDocument.h"
#include "nsIDOMHTMLEmbedElement.h"
#include "nsIDOMHTMLFrameElement.h"
#include "nsIDOMHTMLIFrameElement.h"
#include "nsIDOMHTMLImageElement.h"
#include "nsIDOMHTMLInputElement.h"
#include "nsIDOMHTMLLinkElement.h"
#include "nsIDOMHTMLMediaElement.h"
#include "nsIDOMHTMLObjectElement.h"
#include "nsIDOMHTMLOptionElement.h"
#include "nsIDOMHTMLScriptElement.h"
#include "nsIDOMHTMLSourceElement.h"
#include "nsIDOMHTMLTextAreaElement.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()
{
}
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)
{
aCharSet = GetCharacterSet();
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<nsIDOMWindow> 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<nsIDOMWindow> 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();
}
const nsCString&
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:
nsRefPtr<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);
bool IsFlagSet(uint32_t aFlag) const {
return mParent->GetPersistFlags() & aFlag;
}
~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);
nsRefPtr<nsFrameLoader> loader = loaderOwner->GetFrameLoader();
NS_ENSURE_STATE(loader);
++mOutstandingDocuments;
nsresult rv = loader->StartPersistence(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().get(),
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
nsCOMPtr<nsIDOMHTMLImageElement> nodeAsImage = do_QueryInterface(aNode);
if (nodeAsImage) {
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");
}
nsCOMPtr<nsIDOMHTMLSourceElement> nodeAsSource = do_QueryInterface(aNode);
if (nodeAsSource) {
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");
}
nsCOMPtr<nsIDOMHTMLScriptElement> nodeAsScript = do_QueryInterface(aNode);
if (nodeAsScript) {
return OnWalkAttribute(aNode, "src");
}
if (content->IsSVGElement(nsGkAtoms::script)) {
return OnWalkAttribute(aNode, "href", "http://www.w3.org/1999/xlink");
}
nsCOMPtr<nsIDOMHTMLEmbedElement> nodeAsEmbed = do_QueryInterface(aNode);
if (nodeAsEmbed) {
return OnWalkAttribute(aNode, "src");
}
nsCOMPtr<nsIDOMHTMLObjectElement> nodeAsObject = do_QueryInterface(aNode);
if (nodeAsObject) {
return OnWalkAttribute(aNode, "data");
}
nsCOMPtr<nsIDOMHTMLAppletElement> nodeAsApplet = do_QueryInterface(aNode);
if (nodeAsApplet) {
// For an applet, relative URIs are resolved relative to the
// codebase (which is resolved relative to the base URI).
nsCOMPtr<nsIURI> oldBase = mCurrentBaseURI;
nsAutoString codebase;
rv = nodeAsApplet->GetCodeBase(codebase);
NS_ENSURE_SUCCESS(rv, rv);
if (!codebase.IsEmpty()) {
nsCOMPtr<nsIURI> baseURI;
rv = NS_NewURI(getter_AddRefs(baseURI), codebase,
mParent->GetCharacterSet().get(), mCurrentBaseURI);
NS_ENSURE_SUCCESS(rv, rv);
if (baseURI) {
mCurrentBaseURI = baseURI;
// Must restore this before returning (or ENSURE'ing).
}
}
// We only store 'code' locally if there is no 'archive',
// otherwise we assume the archive file(s) contains it (bug 430283).
nsAutoCString archiveAttr;
rv = ExtractAttribute(aNode, "archive", "", archiveAttr);
if (NS_SUCCEEDED(rv)) {
if (!archiveAttr.IsEmpty()) {
rv = OnWalkURI(archiveAttr);
} else {
rv = OnWalkAttribute(aNode, "core");
}
}
// restore the base URI we really want to have
mCurrentBaseURI = oldBase;
return rv;
}
nsCOMPtr<nsIDOMHTMLLinkElement> nodeAsLink = do_QueryInterface(aNode);
if (nodeAsLink) {
// Test if the link has a rel value indicating it to be a stylesheet
nsAutoString linkRel;
if (NS_SUCCEEDED(nodeAsLink->GetRel(linkRel)) && !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;
}
nsCOMPtr<nsIDOMHTMLFrameElement> nodeAsFrame = do_QueryInterface(aNode);
if (nodeAsFrame) {
return OnWalkSubframe(aNode);
}
nsCOMPtr<nsIDOMHTMLIFrameElement> nodeAsIFrame = do_QueryInterface(aNode);
if (nodeAsIFrame && !(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() { }
nsRefPtr<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;
nsCString* 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);
}
nsCOMPtr<nsIDOMHTMLElement> element(do_QueryInterface(*aNodeOut));
if (element) {
// Make sure this is not XHTML
nsAutoString namespaceURI;
element->GetNamespaceURI(namespaceURI);
if (namespaceURI.IsEmpty()) {
// This is a tag-soup node. It may have a _base_href attribute
// stuck on it by the parser, but since we're fixing up all URIs
// relative to the overall document base that will screw us up.
// Just remove the _base_href.
element->RemoveAttribute(NS_LITERAL_STRING("_base_href"));
}
}
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().get(), 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().get(), relativeURI);
if (NS_SUCCEEDED(rv) && newURI)
{
newURI->SetUserPass(EmptyCString());
nsAutoCString uriSpec;
newURI->GetSpec(uriSpec);
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("=\"");
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;
}
// BASE elements are replaced by a comment so relative links are not hosed.
if (!IsFlagSet(IWBP::PERSIST_FLAGS_NO_BASE_TAG_MODIFICATIONS)) {
nsCOMPtr<nsIDOMHTMLBaseElement> nodeAsBase = do_QueryInterface(aNodeIn);
if (nodeAsBase) {
nsCOMPtr<nsIDOMDocument> ownerDocument;
auto* base = static_cast<dom::HTMLSharedElement*>(nodeAsBase.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;
}
}
nsCOMPtr<nsIContent> content = do_QueryInterface(aNodeIn);
if (!content) {
return NS_OK;
}
// Fix up href and file links in the elements
nsCOMPtr<nsIDOMHTMLAnchorElement> nodeAsAnchor = do_QueryInterface(aNodeIn);
if (nodeAsAnchor) {
rv = GetNodeToFixup(aNodeIn, aNodeOut);
if (NS_SUCCEEDED(rv) && *aNodeOut) {
FixupAnchor(*aNodeOut);
}
return rv;
}
nsCOMPtr<nsIDOMHTMLAreaElement> nodeAsArea = do_QueryInterface(aNodeIn);
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;
}
nsCOMPtr<nsIDOMHTMLImageElement> nodeAsImage = do_QueryInterface(aNodeIn);
if (nodeAsImage) {
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;
}
nsCOMPtr<nsIDOMHTMLSourceElement> nodeAsSource = do_QueryInterface(aNodeIn);
if (nodeAsSource) {
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;
}
nsCOMPtr<nsIDOMHTMLScriptElement> nodeAsScript = do_QueryInterface(aNodeIn);
if (nodeAsScript) {
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;
}
nsCOMPtr<nsIDOMHTMLEmbedElement> nodeAsEmbed = do_QueryInterface(aNodeIn);
if (nodeAsEmbed) {
rv = GetNodeToFixup(aNodeIn, aNodeOut);
if (NS_SUCCEEDED(rv) && *aNodeOut) {
FixupAttribute(*aNodeOut, "src");
}
return rv;
}
nsCOMPtr<nsIDOMHTMLObjectElement> nodeAsObject = do_QueryInterface(aNodeIn);
if (nodeAsObject) {
rv = GetNodeToFixup(aNodeIn, aNodeOut);
if (NS_SUCCEEDED(rv) && *aNodeOut) {
FixupAttribute(*aNodeOut, "data");
}
return rv;
}
nsCOMPtr<nsIDOMHTMLAppletElement> nodeAsApplet = do_QueryInterface(aNodeIn);
if (nodeAsApplet) {
rv = GetNodeToFixup(aNodeIn, aNodeOut);
if (NS_SUCCEEDED(rv) && *aNodeOut) {
nsCOMPtr<nsIDOMHTMLAppletElement> newApplet =
do_QueryInterface(*aNodeOut);
// For an applet, relative URIs are resolved relative to the
// codebase (which is resolved relative to the base URI).
nsCOMPtr<nsIURI> oldBase = mCurrentBaseURI;
nsAutoString codebase;
nodeAsApplet->GetCodeBase(codebase);
if (!codebase.IsEmpty()) {
nsCOMPtr<nsIURI> baseURI;
NS_NewURI(getter_AddRefs(baseURI), codebase,
mParent->GetCharacterSet().get(), mCurrentBaseURI);
if (baseURI) {
mCurrentBaseURI = baseURI;
}
}
// Unset the codebase too, since we'll correctly relativize the
// code and archive paths.
static_cast<dom::HTMLSharedObjectElement*>(newApplet.get())->
RemoveAttribute(NS_LITERAL_STRING("codebase"));
FixupAttribute(*aNodeOut, "code");
FixupAttribute(*aNodeOut, "archive");
// restore the base URI we really want to have
mCurrentBaseURI = oldBase;
}
return rv;
}
nsCOMPtr<nsIDOMHTMLLinkElement> nodeAsLink = do_QueryInterface(aNodeIn);
if (nodeAsLink) {
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;
}
nsCOMPtr<nsIDOMHTMLFrameElement> nodeAsFrame = do_QueryInterface(aNodeIn);
if (nodeAsFrame) {
rv = GetNodeToFixup(aNodeIn, aNodeOut);
if (NS_SUCCEEDED(rv) && *aNodeOut) {
FixupAttribute(*aNodeOut, "src");
}
return rv;
}
nsCOMPtr<nsIDOMHTMLIFrameElement> nodeAsIFrame = do_QueryInterface(aNodeIn);
if (nodeAsIFrame) {
rv = GetNodeToFixup(aNodeIn, aNodeOut);
if (NS_SUCCEEDED(rv) && *aNodeOut) {
FixupAttribute(*aNodeOut, "src");
}
return rv;
}
nsCOMPtr<nsIDOMHTMLInputElement> nodeAsInput = do_QueryInterface(aNodeIn);
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);
nsRefPtr<dom::HTMLInputElement> outElt =
dom::HTMLInputElement::FromContentOrNull(content);
nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(*aNodeOut);
switch (formControl->GetType()) {
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);
// Avoid superfluous value="" serialization
if (valueStr.IsEmpty())
outElt->RemoveAttribute(valueAttr);
else
outElt->SetAttribute(valueAttr, valueStr);
break;
case NS_FORM_INPUT_CHECKBOX:
case NS_FORM_INPUT_RADIO:
bool checked;
nodeAsInput->GetChecked(&checked);
outElt->SetDefaultChecked(checked);
break;
default:
break;
}
}
return rv;
}
nsCOMPtr<nsIDOMHTMLTextAreaElement> nodeAsTextArea = do_QueryInterface(aNodeIn);
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;
}
nsCOMPtr<nsIDOMHTMLOptionElement> nodeAsOption = do_QueryInterface(aNodeIn);
if (nodeAsOption) {
rv = GetNodeToFixup(aNodeIn, aNodeOut);
if (NS_SUCCEEDED(rv) && *aNodeOut) {
nsCOMPtr<nsIDOMHTMLOptionElement> outElt = do_QueryInterface(*aNodeOut);
bool selected;
nodeAsOption->GetSelected(&selected);
outElt->SetDefaultSelected(selected);
}
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);
nsRefPtr<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);
return rv;
}
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_ENCODE_LATIN1_ENTITIES)
encoderFlags |= nsIDocumentEncoder::OutputEncodeLatin1Entities;
if (aEncoderFlags & nsIWebBrowserPersist::ENCODE_FLAGS_ENCODE_HTML_ENTITIES)
encoderFlags |= nsIDocumentEncoder::OutputEncodeHTMLEntities;
if (aEncoderFlags & nsIWebBrowserPersist::ENCODE_FLAGS_ENCODE_W3C_ENTITIES)
encoderFlags |= nsIDocumentEncoder::OutputEncodeW3CEntities;
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