зеркало из https://github.com/mozilla/gecko-dev.git
1366 строки
43 KiB
C++
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("&");
|
|
break;
|
|
case '<':
|
|
aBuffer.AppendLiteral("<");
|
|
break;
|
|
case '>':
|
|
aBuffer.AppendLiteral(">");
|
|
break;
|
|
case '"':
|
|
aBuffer.AppendLiteral(""");
|
|
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
|