summaryrefslogtreecommitdiffstats
path: root/dom/xml/nsXMLContentSink.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/xml/nsXMLContentSink.cpp')
-rw-r--r--dom/xml/nsXMLContentSink.cpp1536
1 files changed, 1536 insertions, 0 deletions
diff --git a/dom/xml/nsXMLContentSink.cpp b/dom/xml/nsXMLContentSink.cpp
new file mode 100644
index 0000000000..4080a10b93
--- /dev/null
+++ b/dom/xml/nsXMLContentSink.cpp
@@ -0,0 +1,1536 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "nsCOMPtr.h"
+#include "nsXMLContentSink.h"
+#include "nsIParser.h"
+#include "mozilla/dom/Document.h"
+#include "nsIContent.h"
+#include "nsIURI.h"
+#include "nsNetUtil.h"
+#include "nsHTMLParts.h"
+#include "nsCRT.h"
+#include "mozilla/StyleSheetInlines.h"
+#include "mozilla/css/Loader.h"
+#include "nsGkAtoms.h"
+#include "nsContentUtils.h"
+#include "nsDocElementCreatedNotificationRunner.h"
+#include "nsIDocShell.h"
+#include "nsIScriptContext.h"
+#include "nsNameSpaceManager.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsIContentViewer.h"
+#include "prtime.h"
+#include "mozilla/Logging.h"
+#include "nsRect.h"
+#include "nsIScriptElement.h"
+#include "nsReadableUtils.h"
+#include "nsUnicharUtils.h"
+#include "nsIChannel.h"
+#include "nsXMLPrettyPrinter.h"
+#include "nsNodeInfoManager.h"
+#include "nsContentCreatorFunctions.h"
+#include "nsIContentPolicy.h"
+#include "nsContentPolicyUtils.h"
+#include "nsError.h"
+#include "nsIScriptGlobalObject.h"
+#include "mozAutoDocUpdate.h"
+#include "nsMimeTypes.h"
+#include "nsHtml5SVGLoadDispatcher.h"
+#include "nsTextNode.h"
+#include "mozilla/dom/CDATASection.h"
+#include "mozilla/dom/Comment.h"
+#include "mozilla/dom/DocumentType.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/HTMLTemplateElement.h"
+#include "mozilla/dom/MutationObservers.h"
+#include "mozilla/dom/ProcessingInstruction.h"
+#include "mozilla/dom/ScriptLoader.h"
+#include "mozilla/dom/txMozillaXSLTProcessor.h"
+#include "mozilla/CycleCollectedJSContext.h"
+#include "mozilla/LoadInfo.h"
+#include "mozilla/UseCounter.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+// XXX Open Issues:
+// 1) what's not allowed - We need to figure out which HTML tags
+// (prefixed with a HTML namespace qualifier) are explicitly not
+// allowed (if any).
+// 2) factoring code with nsHTMLContentSink - There's some amount of
+// common code between this and the HTML content sink. This will
+// increase as we support more and more HTML elements. How can code
+// from the code be factored?
+
+nsresult NS_NewXMLContentSink(nsIXMLContentSink** aResult, Document* aDoc,
+ nsIURI* aURI, nsISupports* aContainer,
+ nsIChannel* aChannel) {
+ MOZ_ASSERT(nullptr != aResult, "null ptr");
+ if (nullptr == aResult) {
+ return NS_ERROR_NULL_POINTER;
+ }
+ RefPtr<nsXMLContentSink> it = new nsXMLContentSink();
+
+ nsresult rv = it->Init(aDoc, aURI, aContainer, aChannel);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ it.forget(aResult);
+ return NS_OK;
+}
+
+nsXMLContentSink::nsXMLContentSink()
+ : mState(eXMLContentSinkState_InProlog),
+ mTextLength(0),
+ mNotifyLevel(0),
+ mPrettyPrintXML(true),
+ mPrettyPrintHasSpecialRoot(0),
+ mPrettyPrintHasFactoredElements(0),
+ mPrettyPrinting(0),
+ mPreventScriptExecution(0) {
+ PodArrayZero(mText);
+}
+
+nsXMLContentSink::~nsXMLContentSink() = default;
+
+nsresult nsXMLContentSink::Init(Document* aDoc, nsIURI* aURI,
+ nsISupports* aContainer, nsIChannel* aChannel) {
+ nsresult rv = nsContentSink::Init(aDoc, aURI, aContainer, aChannel);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aDoc->AddObserver(this);
+ mIsDocumentObserver = true;
+
+ if (!mDocShell) {
+ mPrettyPrintXML = false;
+ }
+
+ mState = eXMLContentSinkState_InProlog;
+ mDocElement = nullptr;
+
+ return NS_OK;
+}
+
+inline void ImplCycleCollectionTraverse(
+ nsCycleCollectionTraversalCallback& aCallback,
+ nsXMLContentSink::StackNode& aField, const char* aName,
+ uint32_t aFlags = 0) {
+ ImplCycleCollectionTraverse(aCallback, aField.mContent, aName, aFlags);
+}
+
+inline void ImplCycleCollectionUnlink(nsXMLContentSink::StackNode& aField) {
+ ImplCycleCollectionUnlink(aField.mContent);
+}
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXMLContentSink)
+ NS_INTERFACE_MAP_ENTRY(nsIContentSink)
+ NS_INTERFACE_MAP_ENTRY(nsIXMLContentSink)
+ NS_INTERFACE_MAP_ENTRY(nsIExpatSink)
+ NS_INTERFACE_MAP_ENTRY(nsITransformObserver)
+NS_INTERFACE_MAP_END_INHERITING(nsContentSink)
+
+NS_IMPL_ADDREF_INHERITED(nsXMLContentSink, nsContentSink)
+NS_IMPL_RELEASE_INHERITED(nsXMLContentSink, nsContentSink)
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED(nsXMLContentSink, nsContentSink,
+ mCurrentHead, mDocElement, mLastTextNode,
+ mContentStack, mDocumentChildren)
+
+// nsIContentSink
+NS_IMETHODIMP
+nsXMLContentSink::WillParse(void) { return WillParseImpl(); }
+
+NS_IMETHODIMP
+nsXMLContentSink::WillBuildModel(nsDTDMode aDTDMode) {
+ WillBuildModelImpl();
+
+ // Notify document that the load is beginning
+ mDocument->BeginLoad();
+
+ // Check for correct load-command for maybe prettyprinting
+ if (mPrettyPrintXML) {
+ nsAutoCString command;
+ GetParser()->GetCommand(command);
+ if (!command.EqualsLiteral("view")) {
+ mPrettyPrintXML = false;
+ }
+ }
+
+ return NS_OK;
+}
+
+bool nsXMLContentSink::CanStillPrettyPrint() {
+ return mPrettyPrintXML &&
+ (!mPrettyPrintHasFactoredElements || mPrettyPrintHasSpecialRoot);
+}
+
+nsresult nsXMLContentSink::MaybePrettyPrint() {
+ if (!CanStillPrettyPrint()) {
+ mPrettyPrintXML = false;
+
+ return NS_OK;
+ }
+
+ {
+ // Try to perform a microtask checkpoint; this avoids always breaking
+ // pretty-printing if webextensions insert new content right after the
+ // document loads.
+ nsAutoMicroTask mt;
+ }
+
+ // stop observing in order to avoid crashing when replacing content
+ mDocument->RemoveObserver(this);
+ mIsDocumentObserver = false;
+
+ // Reenable the CSSLoader so that the prettyprinting stylesheets can load
+ if (mCSSLoader) {
+ mCSSLoader->SetEnabled(true);
+ }
+
+ RefPtr<nsXMLPrettyPrinter> printer;
+ nsresult rv = NS_NewXMLPrettyPrinter(getter_AddRefs(printer));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool isPrettyPrinting;
+ rv = printer->PrettyPrint(mDocument, &isPrettyPrinting);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mPrettyPrinting = isPrettyPrinting;
+ return NS_OK;
+}
+
+static void CheckXSLTParamPI(ProcessingInstruction* aPi,
+ nsIDocumentTransformer* aProcessor,
+ nsINode* aSource) {
+ nsAutoString target, data;
+ aPi->GetTarget(target);
+
+ // Check for namespace declarations
+ if (target.EqualsLiteral("xslt-param-namespace")) {
+ aPi->GetData(data);
+ nsAutoString prefix, namespaceAttr;
+ nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::prefix, prefix);
+ if (!prefix.IsEmpty() && nsContentUtils::GetPseudoAttributeValue(
+ data, nsGkAtoms::_namespace, namespaceAttr)) {
+ aProcessor->AddXSLTParamNamespace(prefix, namespaceAttr);
+ }
+ }
+
+ // Check for actual parameters
+ else if (target.EqualsLiteral("xslt-param")) {
+ aPi->GetData(data);
+ nsAutoString name, namespaceAttr, select, value;
+ nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::name, name);
+ nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::_namespace,
+ namespaceAttr);
+ if (!nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::select,
+ select)) {
+ select.SetIsVoid(true);
+ }
+ if (!nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::value,
+ value)) {
+ value.SetIsVoid(true);
+ }
+ if (!name.IsEmpty()) {
+ aProcessor->AddXSLTParam(name, namespaceAttr, select, value, aSource);
+ }
+ }
+}
+
+NS_IMETHODIMP
+nsXMLContentSink::DidBuildModel(bool aTerminated) {
+ if (!mParser) {
+ // If mParser is null, this parse has already been terminated and must
+ // not been terminated again. However, Document may still think that
+ // the parse has not been terminated and call back into here in the case
+ // where the XML parser has finished but the XSLT transform associated
+ // with the document has not.
+ return NS_OK;
+ }
+
+ FlushTags();
+
+ DidBuildModelImpl(aTerminated);
+
+ if (mXSLTProcessor) {
+ // stop observing in order to avoid crashing when replacing content
+ mDocument->RemoveObserver(this);
+ mIsDocumentObserver = false;
+
+ ErrorResult rv;
+ RefPtr<DocumentFragment> source = mDocument->CreateDocumentFragment();
+ for (nsIContent* child : mDocumentChildren) {
+ // XPath data model doesn't have DocumentType nodes.
+ if (child->NodeType() != nsINode::DOCUMENT_TYPE_NODE) {
+ source->AppendChild(*child, rv);
+ if (rv.Failed()) {
+ return rv.StealNSResult();
+ }
+ }
+ }
+
+ // Check for xslt-param and xslt-param-namespace PIs
+ for (nsIContent* child : mDocumentChildren) {
+ if (auto pi = ProcessingInstruction::FromNode(child)) {
+ CheckXSLTParamPI(pi, mXSLTProcessor, source);
+ } else if (child->IsElement()) {
+ // Only honor PIs in the prolog
+ break;
+ }
+ }
+
+ mXSLTProcessor->SetSourceContentModel(source);
+ // Since the processor now holds a reference to us we drop our reference
+ // to it to avoid owning cycles
+ mXSLTProcessor = nullptr;
+ } else {
+ // Kick off layout for non-XSLT transformed documents.
+
+ // Check if we want to prettyprint
+ MaybePrettyPrint();
+
+ bool startLayout = true;
+
+ if (mPrettyPrinting) {
+ NS_ASSERTION(!mPendingSheetCount, "Shouldn't have pending sheets here!");
+
+ // We're pretty-printing now. See whether we should wait up on
+ // stylesheet loads
+ if (mDocument->CSSLoader()->HasPendingLoads()) {
+ mDocument->CSSLoader()->AddObserver(this);
+ // wait for those sheets to load
+ startLayout = false;
+ }
+ }
+
+ if (startLayout) {
+ StartLayout(false);
+
+ ScrollToRef();
+ }
+
+ mDocument->RemoveObserver(this);
+ mIsDocumentObserver = false;
+
+ mDocument->EndLoad();
+
+ DropParserAndPerfHint();
+ }
+
+ return NS_OK;
+}
+
+nsresult nsXMLContentSink::OnDocumentCreated(Document* aSourceDocument,
+ Document* aResultDocument) {
+ aResultDocument->SetDocWriteDisabled(true);
+
+ nsCOMPtr<nsIContentViewer> contentViewer;
+ mDocShell->GetContentViewer(getter_AddRefs(contentViewer));
+ // Make sure that we haven't loaded a new document into the contentviewer
+ // after starting the XSLT transform.
+ if (contentViewer && contentViewer->GetDocument() == aSourceDocument) {
+ return contentViewer->SetDocumentInternal(aResultDocument, true);
+ }
+ return NS_OK;
+}
+
+nsresult nsXMLContentSink::OnTransformDone(Document* aSourceDocument,
+ nsresult aResult,
+ Document* aResultDocument) {
+ MOZ_ASSERT(aResultDocument,
+ "Don't notify about transform end without a document.");
+
+ mDocumentChildren.Clear();
+
+ nsCOMPtr<nsIContentViewer> contentViewer;
+ mDocShell->GetContentViewer(getter_AddRefs(contentViewer));
+
+ RefPtr<Document> originalDocument = mDocument;
+ bool blockingOnload = mIsBlockingOnload;
+
+ // Make sure that we haven't loaded a new document into the contentviewer
+ // after starting the XSLT transform.
+ if (contentViewer && (contentViewer->GetDocument() == aSourceDocument ||
+ contentViewer->GetDocument() == aResultDocument)) {
+ if (NS_FAILED(aResult)) {
+ // Transform failed.
+ aResultDocument->SetMayStartLayout(false);
+ // We have an error document.
+ contentViewer->SetDocument(aResultDocument);
+ }
+
+ if (!mRunsToCompletion) {
+ // This BlockOnload call corresponds to the UnblockOnload call in
+ // nsContentSink::DropParserAndPerfHint.
+ aResultDocument->BlockOnload();
+ mIsBlockingOnload = true;
+ }
+ // Transform succeeded, or it failed and we have an error document to
+ // display.
+ mDocument = aResultDocument;
+ aResultDocument->SetDocWriteDisabled(false);
+
+ // Notify document observers that all the content has been stuck
+ // into the document.
+ // XXX do we need to notify for things like PIs? Or just the
+ // documentElement?
+ nsIContent* rootElement = mDocument->GetRootElement();
+ if (rootElement) {
+ NS_ASSERTION(mDocument->ComputeIndexOf(rootElement).isSome(),
+ "rootElement not in doc?");
+ mDocument->BeginUpdate();
+ MutationObservers::NotifyContentInserted(mDocument, rootElement);
+ mDocument->EndUpdate();
+ }
+
+ // Start the layout process
+ StartLayout(false);
+
+ ScrollToRef();
+ }
+
+ originalDocument->EndLoad();
+ if (blockingOnload) {
+ // This UnblockOnload call corresponds to the BlockOnload call in
+ // nsContentSink::WillBuildModelImpl.
+ originalDocument->UnblockOnload(true);
+ }
+
+ DropParserAndPerfHint();
+
+ // By this point, the result document has been set in the content viewer. But
+ // the content viewer does not call Destroy on the original document, so we
+ // won't end up reporting document use counters. It's possible we should be
+ // detaching the document from the window, but for now, we call
+ // ReportDocumentUseCounters on the original document here, to avoid
+ // assertions in ~Document about not having reported them.
+ originalDocument->ReportDocumentUseCounters();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXMLContentSink::StyleSheetLoaded(StyleSheet* aSheet, bool aWasDeferred,
+ nsresult aStatus) {
+ if (!mPrettyPrinting) {
+ return nsContentSink::StyleSheetLoaded(aSheet, aWasDeferred, aStatus);
+ }
+
+ if (!mDocument->CSSLoader()->HasPendingLoads()) {
+ mDocument->CSSLoader()->RemoveObserver(this);
+ StartLayout(false);
+ ScrollToRef();
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXMLContentSink::WillInterrupt(void) { return WillInterruptImpl(); }
+
+void nsXMLContentSink::WillResume() { WillResumeImpl(); }
+
+NS_IMETHODIMP
+nsXMLContentSink::SetParser(nsParserBase* aParser) {
+ MOZ_ASSERT(aParser, "Should have a parser here!");
+ mParser = aParser;
+ return NS_OK;
+}
+
+static bool FindIsAttrValue(const char16_t** aAtts, const char16_t** aResult) {
+ RefPtr<nsAtom> prefix, localName;
+ for (; *aAtts; aAtts += 2) {
+ int32_t nameSpaceID;
+ nsContentUtils::SplitExpatName(aAtts[0], getter_AddRefs(prefix),
+ getter_AddRefs(localName), &nameSpaceID);
+ if (nameSpaceID == kNameSpaceID_None && localName == nsGkAtoms::is) {
+ *aResult = aAtts[1];
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+nsresult nsXMLContentSink::CreateElement(
+ const char16_t** aAtts, uint32_t aAttsCount,
+ mozilla::dom::NodeInfo* aNodeInfo, uint32_t aLineNumber,
+ uint32_t aColumnNumber, nsIContent** aResult, bool* aAppendContent,
+ FromParser aFromParser) {
+ NS_ASSERTION(aNodeInfo, "can't create element without nodeinfo");
+
+ *aResult = nullptr;
+ *aAppendContent = true;
+ nsresult rv = NS_OK;
+
+ RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
+ RefPtr<Element> content;
+
+ const char16_t* is = nullptr;
+ if ((aNodeInfo->NamespaceEquals(kNameSpaceID_XHTML) ||
+ aNodeInfo->NamespaceEquals(kNameSpaceID_XUL)) &&
+ FindIsAttrValue(aAtts, &is)) {
+ const nsDependentString isStr(is);
+ rv = NS_NewElement(getter_AddRefs(content), ni.forget(), aFromParser,
+ &isStr);
+ } else {
+ rv = NS_NewElement(getter_AddRefs(content), ni.forget(), aFromParser);
+ }
+
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XHTML) ||
+ aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_SVG)) {
+ nsCOMPtr<nsIScriptElement> sele = do_QueryInterface(content);
+ if (sele) {
+ sele->SetScriptLineNumber(aLineNumber);
+ sele->SetScriptColumnNumber(aColumnNumber);
+ sele->SetCreatorParser(GetParser());
+ } else {
+ MOZ_ASSERT(nsNameSpaceManager::GetInstance()->mSVGDisabled,
+ "Node didn't QI to script, but SVG wasn't disabled.");
+ }
+ }
+
+ // XHTML needs some special attention
+ if (aNodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
+ mPrettyPrintHasFactoredElements = true;
+ } else {
+ // If we care, find out if we just used a special factory.
+ if (!mPrettyPrintHasFactoredElements && !mPrettyPrintHasSpecialRoot &&
+ mPrettyPrintXML) {
+ mPrettyPrintHasFactoredElements =
+ nsNameSpaceManager::GetInstance()->HasElementCreator(
+ aNodeInfo->NamespaceID());
+ }
+
+ if (!aNodeInfo->NamespaceEquals(kNameSpaceID_SVG)) {
+ content.forget(aResult);
+
+ return NS_OK;
+ }
+ }
+
+ if (aNodeInfo->Equals(nsGkAtoms::link, kNameSpaceID_XHTML) ||
+ aNodeInfo->Equals(nsGkAtoms::style, kNameSpaceID_XHTML) ||
+ aNodeInfo->Equals(nsGkAtoms::style, kNameSpaceID_SVG)) {
+ if (auto* linkStyle = LinkStyle::FromNode(*content)) {
+ if (aFromParser) {
+ linkStyle->SetEnableUpdates(false);
+ }
+ if (!aNodeInfo->Equals(nsGkAtoms::link, kNameSpaceID_XHTML)) {
+ linkStyle->SetLineNumber(aFromParser ? aLineNumber : 0);
+ linkStyle->SetColumnNumber(aFromParser ? aColumnNumber : 0);
+ }
+ }
+ }
+
+ content.forget(aResult);
+
+ return NS_OK;
+}
+
+nsresult nsXMLContentSink::CloseElement(nsIContent* aContent) {
+ NS_ASSERTION(aContent, "missing element to close");
+
+ mozilla::dom::NodeInfo* nodeInfo = aContent->NodeInfo();
+
+ // Some HTML nodes need DoneAddingChildren() called to initialize
+ // properly (eg form state restoration).
+ if (nsIContent::RequiresDoneAddingChildren(nodeInfo->NamespaceID(),
+ nodeInfo->NameAtom())) {
+ aContent->DoneAddingChildren(HaveNotifiedForCurrentContent());
+ }
+
+ if (IsMonolithicContainer(nodeInfo)) {
+ mInMonolithicContainer--;
+ }
+
+ if (!nodeInfo->NamespaceEquals(kNameSpaceID_XHTML) &&
+ !nodeInfo->NamespaceEquals(kNameSpaceID_SVG)) {
+ return NS_OK;
+ }
+
+ if (nodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XHTML) ||
+ nodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_SVG)) {
+ nsCOMPtr<nsIScriptElement> sele = do_QueryInterface(aContent);
+ if (!sele) {
+ MOZ_ASSERT(nsNameSpaceManager::GetInstance()->mSVGDisabled,
+ "Node didn't QI to script, but SVG wasn't disabled.");
+ return NS_OK;
+ }
+
+ if (mPreventScriptExecution) {
+ sele->PreventExecution();
+ return NS_OK;
+ }
+
+ // Always check the clock in nsContentSink right after a script
+ StopDeflecting();
+
+ // Now tell the script that it's ready to go. This may execute the script
+ // or return true, or neither if the script doesn't need executing.
+ bool block = sele->AttemptToExecute();
+ if (mParser) {
+ if (block) {
+ GetParser()->BlockParser();
+ }
+
+ // If the parser got blocked, make sure to return the appropriate rv.
+ // I'm not sure if this is actually needed or not.
+ if (!mParser->IsParserEnabled()) {
+ block = true;
+ }
+ }
+
+ return block ? NS_ERROR_HTMLPARSER_BLOCK : NS_OK;
+ }
+
+ nsresult rv = NS_OK;
+ if (nodeInfo->Equals(nsGkAtoms::link, kNameSpaceID_XHTML) ||
+ nodeInfo->Equals(nsGkAtoms::style, kNameSpaceID_XHTML) ||
+ nodeInfo->Equals(nsGkAtoms::style, kNameSpaceID_SVG)) {
+ if (auto* linkStyle = LinkStyle::FromNode(*aContent)) {
+ linkStyle->SetEnableUpdates(true);
+ auto updateOrError =
+ linkStyle->UpdateStyleSheet(mRunsToCompletion ? nullptr : this);
+ if (updateOrError.isErr()) {
+ rv = updateOrError.unwrapErr();
+ } else if (updateOrError.unwrap().ShouldBlock() && !mRunsToCompletion) {
+ ++mPendingSheetCount;
+ mScriptLoader->AddParserBlockingScriptExecutionBlocker();
+ }
+ }
+ }
+
+ return rv;
+}
+
+nsresult nsXMLContentSink::AddContentAsLeaf(nsIContent* aContent) {
+ nsresult result = NS_OK;
+
+ if (mState == eXMLContentSinkState_InProlog) {
+ NS_ASSERTION(mDocument, "Fragments have no prolog");
+ mDocumentChildren.AppendElement(aContent);
+ } else if (mState == eXMLContentSinkState_InEpilog) {
+ NS_ASSERTION(mDocument, "Fragments have no epilog");
+ if (mXSLTProcessor) {
+ mDocumentChildren.AppendElement(aContent);
+ } else {
+ mDocument->AppendChildTo(aContent, false, IgnoreErrors());
+ }
+ } else {
+ nsCOMPtr<nsIContent> parent = GetCurrentContent();
+
+ if (parent) {
+ ErrorResult rv;
+ parent->AppendChildTo(aContent, false, rv);
+ result = rv.StealNSResult();
+ }
+ }
+ return result;
+}
+
+// Create an XML parser and an XSL content sink and start parsing
+// the XSL stylesheet located at the given URI.
+nsresult nsXMLContentSink::LoadXSLStyleSheet(nsIURI* aUrl) {
+ nsCOMPtr<nsIDocumentTransformer> processor = new txMozillaXSLTProcessor();
+ mDocument->SetUseCounter(eUseCounter_custom_XSLStylesheet);
+
+ processor->SetTransformObserver(this);
+
+ if (NS_SUCCEEDED(processor->LoadStyleSheet(aUrl, mDocument))) {
+ mXSLTProcessor.swap(processor);
+ }
+
+ // Intentionally ignore errors here, we should continue loading the
+ // XML document whether we're able to load the XSLT stylesheet or
+ // not.
+
+ return NS_OK;
+}
+
+nsresult nsXMLContentSink::ProcessStyleLinkFromHeader(
+ const nsAString& aHref, bool aAlternate, const nsAString& aTitle,
+ const nsAString& aIntegrity, const nsAString& aType,
+ const nsAString& aMedia, const nsAString& aReferrerPolicy) {
+ mPrettyPrintXML = false;
+
+ nsAutoCString cmd;
+ if (mParser) GetParser()->GetCommand(cmd);
+ if (cmd.EqualsASCII(kLoadAsData))
+ return NS_OK; // Do not load stylesheets when loading as data
+
+ bool wasXSLT;
+ nsresult rv = MaybeProcessXSLTLink(nullptr, aHref, aAlternate, aType, aType,
+ aMedia, aReferrerPolicy, &wasXSLT);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (wasXSLT) {
+ // We're done here.
+ return NS_OK;
+ }
+
+ // Otherwise fall through to nsContentSink to handle CSS Link headers.
+ return nsContentSink::ProcessStyleLinkFromHeader(
+ aHref, aAlternate, aTitle, aIntegrity, aType, aMedia, aReferrerPolicy);
+}
+
+nsresult nsXMLContentSink::MaybeProcessXSLTLink(
+ ProcessingInstruction* aProcessingInstruction, const nsAString& aHref,
+ bool aAlternate, const nsAString& aTitle, const nsAString& aType,
+ const nsAString& aMedia, const nsAString& aReferrerPolicy, bool* aWasXSLT) {
+ bool wasXSLT = aType.LowerCaseEqualsLiteral(TEXT_XSL) ||
+ aType.LowerCaseEqualsLiteral(APPLICATION_XSLT_XML) ||
+ aType.LowerCaseEqualsLiteral(TEXT_XML) ||
+ aType.LowerCaseEqualsLiteral(APPLICATION_XML);
+
+ if (aWasXSLT) {
+ *aWasXSLT = wasXSLT;
+ }
+
+ if (!wasXSLT) {
+ return NS_OK;
+ }
+
+ if (aAlternate) {
+ // don't load alternate XSLT
+ return NS_OK;
+ }
+ // LoadXSLStyleSheet needs a mDocShell.
+ if (!mDocShell) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIURI> url;
+ nsresult rv = NS_NewURI(getter_AddRefs(url), aHref, nullptr,
+ mDocument->GetDocBaseURI());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Do security check
+ nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
+ rv = secMan->CheckLoadURIWithPrincipal(mDocument->NodePrincipal(), url,
+ nsIScriptSecurityManager::ALLOW_CHROME,
+ mDocument->InnerWindowID());
+ NS_ENSURE_SUCCESS(rv, NS_OK);
+
+ nsCOMPtr<nsILoadInfo> secCheckLoadInfo =
+ new net::LoadInfo(mDocument->NodePrincipal(), // loading principal
+ mDocument->NodePrincipal(), // triggering principal
+ aProcessingInstruction,
+ nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
+ nsIContentPolicy::TYPE_XSLT);
+
+ // Do content policy check
+ int16_t decision = nsIContentPolicy::ACCEPT;
+ rv = NS_CheckContentLoadPolicy(url, secCheckLoadInfo,
+ NS_ConvertUTF16toUTF8(aType), &decision,
+ nsContentUtils::GetContentPolicy());
+
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (NS_CP_REJECTED(decision)) {
+ return NS_OK;
+ }
+
+ return LoadXSLStyleSheet(url);
+}
+
+void nsXMLContentSink::SetDocumentCharset(NotNull<const Encoding*> aEncoding) {
+ if (mDocument) {
+ mDocument->SetDocumentCharacterSet(aEncoding);
+ }
+}
+
+nsISupports* nsXMLContentSink::GetTarget() { return ToSupports(mDocument); }
+
+bool nsXMLContentSink::IsScriptExecuting() { return IsScriptExecutingImpl(); }
+
+nsresult nsXMLContentSink::FlushText(bool aReleaseTextNode) {
+ nsresult rv = NS_OK;
+
+ if (mTextLength != 0) {
+ if (mLastTextNode) {
+ bool notify = HaveNotifiedForCurrentContent();
+ // We could probably always increase mInNotification here since
+ // if AppendText doesn't notify it shouldn't trigger evil code.
+ // But just in case it does, we don't want to mask any notifications.
+ if (notify) {
+ ++mInNotification;
+ }
+ rv = mLastTextNode->AppendText(mText, mTextLength, notify);
+ if (notify) {
+ --mInNotification;
+ }
+
+ mTextLength = 0;
+ } else {
+ RefPtr<nsTextNode> textContent =
+ new (mNodeInfoManager) nsTextNode(mNodeInfoManager);
+
+ mLastTextNode = textContent;
+
+ // Set the text in the text node
+ textContent->SetText(mText, mTextLength, false);
+ mTextLength = 0;
+
+ // Add text to its parent
+ rv = AddContentAsLeaf(textContent);
+ }
+ }
+
+ if (aReleaseTextNode) {
+ mLastTextNode = nullptr;
+ }
+
+ return rv;
+}
+
+nsIContent* nsXMLContentSink::GetCurrentContent() {
+ if (mContentStack.Length() == 0) {
+ return nullptr;
+ }
+ return GetCurrentStackNode()->mContent;
+}
+
+nsXMLContentSink::StackNode* nsXMLContentSink::GetCurrentStackNode() {
+ int32_t count = mContentStack.Length();
+ return count != 0 ? &mContentStack[count - 1] : nullptr;
+}
+
+nsresult nsXMLContentSink::PushContent(nsIContent* aContent) {
+ MOZ_ASSERT(aContent, "Null content being pushed!");
+ StackNode* sn = mContentStack.AppendElement();
+ NS_ENSURE_TRUE(sn, NS_ERROR_OUT_OF_MEMORY);
+
+ nsIContent* contentToPush = aContent;
+
+ // When an XML parser would append a node to a template element, it
+ // must instead append it to the template element's template contents.
+ if (contentToPush->IsHTMLElement(nsGkAtoms::_template)) {
+ HTMLTemplateElement* templateElement =
+ static_cast<HTMLTemplateElement*>(contentToPush);
+ contentToPush = templateElement->Content();
+ }
+
+ sn->mContent = contentToPush;
+ sn->mNumFlushed = 0;
+ return NS_OK;
+}
+
+void nsXMLContentSink::PopContent() {
+ if (mContentStack.IsEmpty()) {
+ NS_WARNING("Popping empty stack");
+ return;
+ }
+
+ mContentStack.RemoveLastElement();
+}
+
+bool nsXMLContentSink::HaveNotifiedForCurrentContent() const {
+ uint32_t stackLength = mContentStack.Length();
+ if (stackLength) {
+ const StackNode& stackNode = mContentStack[stackLength - 1];
+ nsIContent* parent = stackNode.mContent;
+ return stackNode.mNumFlushed == parent->GetChildCount();
+ }
+ return true;
+}
+
+void nsXMLContentSink::MaybeStartLayout(bool aIgnorePendingSheets) {
+ // XXXbz if aIgnorePendingSheets is true, what should we do when
+ // mXSLTProcessor or CanStillPrettyPrint()?
+ if (mLayoutStarted || mXSLTProcessor || CanStillPrettyPrint()) {
+ return;
+ }
+ StartLayout(aIgnorePendingSheets);
+}
+
+////////////////////////////////////////////////////////////////////////
+
+bool nsXMLContentSink::SetDocElement(int32_t aNameSpaceID, nsAtom* aTagName,
+ nsIContent* aContent) {
+ if (mDocElement) return false;
+
+ mDocElement = aContent;
+
+ if (mXSLTProcessor) {
+ mDocumentChildren.AppendElement(aContent);
+ return true;
+ }
+
+ if (!mDocumentChildren.IsEmpty()) {
+ for (nsIContent* child : mDocumentChildren) {
+ mDocument->AppendChildTo(child, false, IgnoreErrors());
+ }
+ mDocumentChildren.Clear();
+ }
+
+ // check for root elements that needs special handling for
+ // prettyprinting
+ if (aNameSpaceID == kNameSpaceID_XSLT &&
+ (aTagName == nsGkAtoms::stylesheet || aTagName == nsGkAtoms::transform)) {
+ mPrettyPrintHasSpecialRoot = true;
+ if (mPrettyPrintXML) {
+ // In this case, disable script execution, stylesheet
+ // loading, and auto XLinks since we plan to prettyprint.
+ mDocument->ScriptLoader()->SetEnabled(false);
+ if (mCSSLoader) {
+ mCSSLoader->SetEnabled(false);
+ }
+ }
+ }
+
+ IgnoredErrorResult rv;
+ mDocument->AppendChildTo(mDocElement, NotifyForDocElement(), rv);
+ if (rv.Failed()) {
+ // If we return false here, the caller will bail out because it won't
+ // find a parent content node to append to, which is fine.
+ return false;
+ }
+
+ return true;
+}
+
+NS_IMETHODIMP
+nsXMLContentSink::HandleStartElement(const char16_t* aName,
+ const char16_t** aAtts,
+ uint32_t aAttsCount, uint32_t aLineNumber,
+ uint32_t aColumnNumber) {
+ return HandleStartElement(aName, aAtts, aAttsCount, aLineNumber,
+ aColumnNumber, true);
+}
+
+nsresult nsXMLContentSink::HandleStartElement(
+ const char16_t* aName, const char16_t** aAtts, uint32_t aAttsCount,
+ uint32_t aLineNumber, uint32_t aColumnNumber, bool aInterruptable) {
+ MOZ_ASSERT(aAttsCount % 2 == 0, "incorrect aAttsCount");
+ // Adjust aAttsCount so it's the actual number of attributes
+ aAttsCount /= 2;
+
+ nsresult result = NS_OK;
+ bool appendContent = true;
+ nsCOMPtr<nsIContent> content;
+
+ // XXX Hopefully the parser will flag this before we get
+ // here. If we're in the epilog, there should be no
+ // new elements
+ MOZ_ASSERT(eXMLContentSinkState_InEpilog != mState);
+
+ FlushText();
+ DidAddContent();
+
+ mState = eXMLContentSinkState_InDocumentElement;
+
+ int32_t nameSpaceID;
+ RefPtr<nsAtom> prefix, localName;
+ nsContentUtils::SplitExpatName(aName, getter_AddRefs(prefix),
+ getter_AddRefs(localName), &nameSpaceID);
+
+ if (!OnOpenContainer(aAtts, aAttsCount, nameSpaceID, localName,
+ aLineNumber)) {
+ return NS_OK;
+ }
+
+ RefPtr<mozilla::dom::NodeInfo> nodeInfo;
+ nodeInfo = mNodeInfoManager->GetNodeInfo(localName, prefix, nameSpaceID,
+ nsINode::ELEMENT_NODE);
+
+ result = CreateElement(aAtts, aAttsCount, nodeInfo, aLineNumber,
+ aColumnNumber, getter_AddRefs(content), &appendContent,
+ FROM_PARSER_NETWORK);
+ NS_ENSURE_SUCCESS(result, result);
+
+ // Have to do this before we push the new content on the stack... and have to
+ // do that before we set attributes, call BindToTree, etc. Ideally we'd push
+ // on the stack inside CreateElement (which is effectively what the HTML sink
+ // does), but that's hard with all the subclass overrides going on.
+ nsCOMPtr<nsIContent> parent = GetCurrentContent();
+
+ result = PushContent(content);
+ NS_ENSURE_SUCCESS(result, result);
+
+ // Set the attributes on the new content element
+ result = AddAttributes(aAtts, content->AsElement());
+
+ if (NS_OK == result) {
+ // Store the element
+ if (!SetDocElement(nameSpaceID, localName, content) && appendContent) {
+ NS_ENSURE_TRUE(parent, NS_ERROR_UNEXPECTED);
+
+ parent->AppendChildTo(content, false, IgnoreErrors());
+ }
+ }
+
+ // Some HTML nodes need DoneCreatingElement() called to initialize
+ // properly (eg form state restoration).
+ if (nsIContent::RequiresDoneCreatingElement(nodeInfo->NamespaceID(),
+ nodeInfo->NameAtom())) {
+ content->DoneCreatingElement();
+ }
+
+ if (nodeInfo->NamespaceID() == kNameSpaceID_XHTML &&
+ nodeInfo->NameAtom() == nsGkAtoms::head && !mCurrentHead) {
+ mCurrentHead = content;
+ }
+
+ if (IsMonolithicContainer(nodeInfo)) {
+ mInMonolithicContainer++;
+ }
+
+ if (!mXSLTProcessor) {
+ if (content == mDocElement) {
+ nsContentUtils::AddScriptRunner(
+ new nsDocElementCreatedNotificationRunner(mDocument));
+
+ if (aInterruptable && NS_SUCCEEDED(result) && mParser &&
+ !mParser->IsParserEnabled()) {
+ return NS_ERROR_HTMLPARSER_BLOCK;
+ }
+ } else if (!mCurrentHead) {
+ // This isn't the root and we're not inside an XHTML <head>.
+ // Might need to start layout
+ MaybeStartLayout(false);
+ }
+ }
+
+ return aInterruptable && NS_SUCCEEDED(result) ? DidProcessATokenImpl()
+ : result;
+}
+
+NS_IMETHODIMP
+nsXMLContentSink::HandleEndElement(const char16_t* aName) {
+ return HandleEndElement(aName, true);
+}
+
+nsresult nsXMLContentSink::HandleEndElement(const char16_t* aName,
+ bool aInterruptable) {
+ nsresult result = NS_OK;
+
+ // XXX Hopefully the parser will flag this before we get
+ // here. If we're in the prolog or epilog, there should be
+ // no close tags for elements.
+ MOZ_ASSERT(eXMLContentSinkState_InDocumentElement == mState);
+
+ FlushText();
+
+ StackNode* sn = GetCurrentStackNode();
+ if (!sn) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ nsCOMPtr<nsIContent> content;
+ sn->mContent.swap(content);
+ uint32_t numFlushed = sn->mNumFlushed;
+
+ PopContent();
+ NS_ASSERTION(content, "failed to pop content");
+#ifdef DEBUG
+ // Check that we're closing the right thing
+ RefPtr<nsAtom> debugNameSpacePrefix, debugTagAtom;
+ int32_t debugNameSpaceID;
+ nsContentUtils::SplitExpatName(aName, getter_AddRefs(debugNameSpacePrefix),
+ getter_AddRefs(debugTagAtom),
+ &debugNameSpaceID);
+ // Check if we are closing a template element because template
+ // elements do not get pushed on the stack, the template
+ // element content is pushed instead.
+ bool isTemplateElement = debugTagAtom == nsGkAtoms::_template &&
+ debugNameSpaceID == kNameSpaceID_XHTML;
+ NS_ASSERTION(
+ content->NodeInfo()->Equals(debugTagAtom, debugNameSpaceID) ||
+ (debugNameSpaceID == kNameSpaceID_MathML &&
+ content->NodeInfo()->NamespaceID() == kNameSpaceID_disabled_MathML &&
+ content->NodeInfo()->Equals(debugTagAtom)) ||
+ (debugNameSpaceID == kNameSpaceID_SVG &&
+ content->NodeInfo()->NamespaceID() == kNameSpaceID_disabled_SVG &&
+ content->NodeInfo()->Equals(debugTagAtom)) ||
+ isTemplateElement,
+ "Wrong element being closed");
+#endif
+
+ // Make sure to notify on our kids before we call out to any other code that
+ // might reenter us and call FlushTags, in a state in which we've already
+ // popped "content" from the stack but haven't notified on its kids yet.
+ int32_t stackLen = mContentStack.Length();
+ if (mNotifyLevel >= stackLen) {
+ if (numFlushed < content->GetChildCount()) {
+ NotifyAppend(content, numFlushed);
+ }
+ mNotifyLevel = stackLen - 1;
+ }
+
+ result = CloseElement(content);
+
+ if (mCurrentHead == content) {
+ mCurrentHead = nullptr;
+ }
+
+ if (mDocElement == content) {
+ // XXXbz for roots that don't want to be appended on open, we
+ // probably need to deal here.... (and stop appending them on open).
+ mState = eXMLContentSinkState_InEpilog;
+
+ mDocument->OnParsingCompleted();
+
+ // We might have had no occasion to start layout yet. Do so now.
+ MaybeStartLayout(false);
+ }
+
+ DidAddContent();
+
+ if (content->IsSVGElement(nsGkAtoms::svg)) {
+ FlushTags();
+ nsCOMPtr<nsIRunnable> event = new nsHtml5SVGLoadDispatcher(content);
+ if (NS_FAILED(content->OwnerDoc()->Dispatch(TaskCategory::Other,
+ event.forget()))) {
+ NS_WARNING("failed to dispatch svg load dispatcher");
+ }
+ }
+
+ return aInterruptable && NS_SUCCEEDED(result) ? DidProcessATokenImpl()
+ : result;
+}
+
+NS_IMETHODIMP
+nsXMLContentSink::HandleComment(const char16_t* aName) {
+ FlushText();
+
+ RefPtr<Comment> comment = new (mNodeInfoManager) Comment(mNodeInfoManager);
+ comment->SetText(nsDependentString(aName), false);
+ nsresult rv = AddContentAsLeaf(comment);
+ DidAddContent();
+
+ return NS_SUCCEEDED(rv) ? DidProcessATokenImpl() : rv;
+}
+
+NS_IMETHODIMP
+nsXMLContentSink::HandleCDataSection(const char16_t* aData, uint32_t aLength) {
+ // XSLT doesn't differentiate between text and cdata and wants adjacent
+ // textnodes merged, so add as text.
+ if (mXSLTProcessor) {
+ return AddText(aData, aLength);
+ }
+
+ FlushText();
+
+ RefPtr<CDATASection> cdata =
+ new (mNodeInfoManager) CDATASection(mNodeInfoManager);
+ cdata->SetText(aData, aLength, false);
+ nsresult rv = AddContentAsLeaf(cdata);
+ DidAddContent();
+
+ return NS_SUCCEEDED(rv) ? DidProcessATokenImpl() : rv;
+}
+
+NS_IMETHODIMP
+nsXMLContentSink::HandleDoctypeDecl(const nsAString& aSubset,
+ const nsAString& aName,
+ const nsAString& aSystemId,
+ const nsAString& aPublicId,
+ nsISupports* aCatalogData) {
+ FlushText();
+
+ NS_ASSERTION(mDocument, "Shouldn't get here from a document fragment");
+
+ RefPtr<nsAtom> name = NS_Atomize(aName);
+ NS_ENSURE_TRUE(name, NS_ERROR_OUT_OF_MEMORY);
+
+ // Create a new doctype node
+ RefPtr<DocumentType> docType = NS_NewDOMDocumentType(
+ mNodeInfoManager, name, aPublicId, aSystemId, aSubset);
+
+ MOZ_ASSERT(!aCatalogData,
+ "Need to add back support for catalog style "
+ "sheets");
+
+ mDocumentChildren.AppendElement(docType);
+ DidAddContent();
+ return DidProcessATokenImpl();
+}
+
+NS_IMETHODIMP
+nsXMLContentSink::HandleCharacterData(const char16_t* aData, uint32_t aLength) {
+ return HandleCharacterData(aData, aLength, true);
+}
+
+nsresult nsXMLContentSink::HandleCharacterData(const char16_t* aData,
+ uint32_t aLength,
+ bool aInterruptable) {
+ nsresult rv = NS_OK;
+ if (aData && mState != eXMLContentSinkState_InProlog &&
+ mState != eXMLContentSinkState_InEpilog) {
+ rv = AddText(aData, aLength);
+ }
+ return aInterruptable && NS_SUCCEEDED(rv) ? DidProcessATokenImpl() : rv;
+}
+
+NS_IMETHODIMP
+nsXMLContentSink::HandleProcessingInstruction(const char16_t* aTarget,
+ const char16_t* aData) {
+ FlushText();
+
+ const nsDependentString target(aTarget);
+ const nsDependentString data(aData);
+
+ RefPtr<ProcessingInstruction> node =
+ NS_NewXMLProcessingInstruction(mNodeInfoManager, target, data);
+
+ auto* linkStyle = LinkStyle::FromNode(*node);
+ if (linkStyle) {
+ linkStyle->SetEnableUpdates(false);
+ mPrettyPrintXML = false;
+ }
+
+ nsresult rv = AddContentAsLeaf(node);
+ NS_ENSURE_SUCCESS(rv, rv);
+ DidAddContent();
+
+ if (linkStyle) {
+ // This is an xml-stylesheet processing instruction... but it might not be
+ // a CSS one if the type is set to something else.
+ linkStyle->SetEnableUpdates(true);
+ auto updateOrError =
+ linkStyle->UpdateStyleSheet(mRunsToCompletion ? nullptr : this);
+ if (updateOrError.isErr()) {
+ return updateOrError.unwrapErr();
+ }
+
+ auto update = updateOrError.unwrap();
+ if (update.WillNotify()) {
+ // Successfully started a stylesheet load
+ if (update.ShouldBlock() && !mRunsToCompletion) {
+ ++mPendingSheetCount;
+ mScriptLoader->AddParserBlockingScriptExecutionBlocker();
+ }
+ return NS_OK;
+ }
+ }
+
+ // Check whether this is a CSS stylesheet PI. Make sure the type
+ // handling here matches
+ // XMLStylesheetProcessingInstruction::GetStyleSheetInfo.
+ nsAutoString type;
+ nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::type, type);
+ nsAutoString mimeType, notUsed;
+ nsContentUtils::SplitMimeType(type, mimeType, notUsed);
+
+ if (mState != eXMLContentSinkState_InProlog ||
+ !target.EqualsLiteral("xml-stylesheet") || mimeType.IsEmpty() ||
+ mimeType.LowerCaseEqualsLiteral("text/css")) {
+ // Either not a useful stylesheet PI, or a CSS stylesheet PI that
+ // got handled above by the "ssle" bits. We're done here.
+ return DidProcessATokenImpl();
+ }
+
+ // If it's not a CSS stylesheet PI...
+ nsAutoString href, title, media;
+ bool isAlternate = false;
+
+ // If there was no href, we can't do anything with this PI
+ if (!ParsePIData(data, href, title, media, isAlternate)) {
+ return DidProcessATokenImpl();
+ }
+
+ // <?xml-stylesheet?> processing instructions don't have a referrerpolicy
+ // pseudo-attribute, so we pass in an empty string
+ rv =
+ MaybeProcessXSLTLink(node, href, isAlternate, title, type, media, u""_ns);
+ return NS_SUCCEEDED(rv) ? DidProcessATokenImpl() : rv;
+}
+
+/* static */
+bool nsXMLContentSink::ParsePIData(const nsString& aData, nsString& aHref,
+ nsString& aTitle, nsString& aMedia,
+ bool& aIsAlternate) {
+ // If there was no href, we can't do anything with this PI
+ if (!nsContentUtils::GetPseudoAttributeValue(aData, nsGkAtoms::href, aHref)) {
+ return false;
+ }
+
+ nsContentUtils::GetPseudoAttributeValue(aData, nsGkAtoms::title, aTitle);
+
+ nsContentUtils::GetPseudoAttributeValue(aData, nsGkAtoms::media, aMedia);
+
+ nsAutoString alternate;
+ nsContentUtils::GetPseudoAttributeValue(aData, nsGkAtoms::alternate,
+ alternate);
+
+ aIsAlternate = alternate.EqualsLiteral("yes");
+
+ return true;
+}
+
+NS_IMETHODIMP
+nsXMLContentSink::HandleXMLDeclaration(const char16_t* aVersion,
+ const char16_t* aEncoding,
+ int32_t aStandalone) {
+ mDocument->SetXMLDeclaration(aVersion, aEncoding, aStandalone);
+
+ return DidProcessATokenImpl();
+}
+
+NS_IMETHODIMP
+nsXMLContentSink::ReportError(const char16_t* aErrorText,
+ const char16_t* aSourceText,
+ nsIScriptError* aError, bool* _retval) {
+ MOZ_ASSERT(aError && aSourceText && aErrorText, "Check arguments!!!");
+ nsresult rv = NS_OK;
+
+ // The expat driver should report the error. We're just cleaning up the mess.
+ *_retval = true;
+
+ mPrettyPrintXML = false;
+
+ mState = eXMLContentSinkState_InProlog;
+
+ // XXX need to stop scripts here -- hsivonen
+
+ // stop observing in order to avoid crashing when removing content
+ mDocument->RemoveObserver(this);
+ mIsDocumentObserver = false;
+
+ // Clear the current content
+ mDocumentChildren.Clear();
+ while (mDocument->GetLastChild()) {
+ mDocument->GetLastChild()->Remove();
+ }
+ mDocElement = nullptr;
+
+ // Clear any buffered-up text we have. It's enough to set the length to 0.
+ // The buffer itself is allocated when we're created and deleted in our
+ // destructor, so don't mess with it.
+ mTextLength = 0;
+
+ if (mXSLTProcessor) {
+ // Get rid of the XSLT processor.
+ mXSLTProcessor->CancelLoads();
+ mXSLTProcessor = nullptr;
+ }
+
+ // release the nodes on stack
+ mContentStack.Clear();
+ mNotifyLevel = 0;
+
+ // return leaving the document empty if we're asked to not add a <parsererror>
+ // root node
+ if (mDocument->SuppressParserErrorElement()) {
+ return NS_OK;
+ }
+
+ // prepare to set <parsererror> as the document root
+ rv = HandleProcessingInstruction(
+ u"xml-stylesheet",
+ u"href=\"chrome://global/locale/intl.css\" type=\"text/css\"");
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ const char16_t* noAtts[] = {0, 0};
+
+ constexpr auto errorNs =
+ u"http://www.mozilla.org/newlayout/xml/parsererror.xml"_ns;
+
+ nsAutoString parsererror(errorNs);
+ parsererror.Append((char16_t)0xFFFF);
+ parsererror.AppendLiteral("parsererror");
+
+ rv = HandleStartElement(parsererror.get(), noAtts, 0, (uint32_t)-1, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = HandleCharacterData(aErrorText, NS_strlen(aErrorText), false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString sourcetext(errorNs);
+ sourcetext.Append((char16_t)0xFFFF);
+ sourcetext.AppendLiteral("sourcetext");
+
+ rv = HandleStartElement(sourcetext.get(), noAtts, 0, (uint32_t)-1, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = HandleCharacterData(aSourceText, NS_strlen(aSourceText), false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = HandleEndElement(sourcetext.get(), false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = HandleEndElement(parsererror.get(), false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ FlushTags();
+
+ return NS_OK;
+}
+
+nsresult nsXMLContentSink::AddAttributes(const char16_t** aAtts,
+ Element* aContent) {
+ // Add tag attributes to the content attributes
+ RefPtr<nsAtom> prefix, localName;
+ while (*aAtts) {
+ int32_t nameSpaceID;
+ nsContentUtils::SplitExpatName(aAtts[0], getter_AddRefs(prefix),
+ getter_AddRefs(localName), &nameSpaceID);
+
+ // Add attribute to content
+ aContent->SetAttr(nameSpaceID, localName, prefix,
+ nsDependentString(aAtts[1]), false);
+ aAtts += 2;
+ }
+
+ return NS_OK;
+}
+
+#define NS_ACCUMULATION_BUFFER_SIZE 4096
+
+nsresult nsXMLContentSink::AddText(const char16_t* aText, int32_t aLength) {
+ // Copy data from string into our buffer; flush buffer when it fills up.
+ int32_t offset = 0;
+ while (0 != aLength) {
+ int32_t amount = NS_ACCUMULATION_BUFFER_SIZE - mTextLength;
+ if (0 == amount) {
+ nsresult rv = FlushText(false);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ MOZ_ASSERT(mTextLength == 0);
+ amount = NS_ACCUMULATION_BUFFER_SIZE;
+ }
+
+ if (amount > aLength) {
+ amount = aLength;
+ }
+ memcpy(&mText[mTextLength], &aText[offset], sizeof(char16_t) * amount);
+ mTextLength += amount;
+ offset += amount;
+ aLength -= amount;
+ }
+
+ return NS_OK;
+}
+
+void nsXMLContentSink::InitialTranslationCompleted() { StartLayout(false); }
+
+void nsXMLContentSink::FlushPendingNotifications(FlushType aType) {
+ // Only flush tags if we're not doing the notification ourselves
+ // (since we aren't reentrant)
+ if (!mInNotification) {
+ if (mIsDocumentObserver) {
+ // Only flush if we're still a document observer (so that our child
+ // counts should be correct).
+ if (aType >= FlushType::ContentAndNotify) {
+ FlushTags();
+ } else {
+ FlushText(false);
+ }
+ }
+ if (aType >= FlushType::EnsurePresShellInitAndFrames) {
+ // Make sure that layout has started so that the reflow flush
+ // will actually happen.
+ MaybeStartLayout(true);
+ }
+ }
+}
+
+/**
+ * NOTE!! Forked from SinkContext. Please keep in sync.
+ *
+ * Flush all elements that have been seen so far such that
+ * they are visible in the tree. Specifically, make sure
+ * that they are all added to their respective parents.
+ * Also, do notification at the top for all content that
+ * has been newly added so that the frame tree is complete.
+ */
+nsresult nsXMLContentSink::FlushTags() {
+ mDeferredFlushTags = false;
+ uint32_t oldUpdates = mUpdatesInNotification;
+
+ mUpdatesInNotification = 0;
+ ++mInNotification;
+ {
+ // Scope so we call EndUpdate before we decrease mInNotification
+ mozAutoDocUpdate updateBatch(mDocument, true);
+
+ // Don't release last text node in case we need to add to it again
+ FlushText(false);
+
+ // Start from the base of the stack (growing downward) and do
+ // a notification from the node that is closest to the root of
+ // tree for any content that has been added.
+
+ int32_t stackPos;
+ int32_t stackLen = mContentStack.Length();
+ bool flushed = false;
+ uint32_t childCount;
+ nsIContent* content;
+
+ for (stackPos = 0; stackPos < stackLen; ++stackPos) {
+ content = mContentStack[stackPos].mContent;
+ childCount = content->GetChildCount();
+
+ if (!flushed && (mContentStack[stackPos].mNumFlushed < childCount)) {
+ NotifyAppend(content, mContentStack[stackPos].mNumFlushed);
+ flushed = true;
+ }
+
+ mContentStack[stackPos].mNumFlushed = childCount;
+ }
+ mNotifyLevel = stackLen - 1;
+ }
+ --mInNotification;
+
+ if (mUpdatesInNotification > 1) {
+ UpdateChildCounts();
+ }
+
+ mUpdatesInNotification = oldUpdates;
+ return NS_OK;
+}
+
+/**
+ * NOTE!! Forked from SinkContext. Please keep in sync.
+ */
+void nsXMLContentSink::UpdateChildCounts() {
+ // Start from the top of the stack (growing upwards) and see if any
+ // new content has been appended. If so, we recognize that reflows
+ // have been generated for it and we should make sure that no
+ // further reflows occur. Note that we have to include stackPos == 0
+ // to properly notify on kids of <html>.
+ int32_t stackLen = mContentStack.Length();
+ int32_t stackPos = stackLen - 1;
+ while (stackPos >= 0) {
+ StackNode& node = mContentStack[stackPos];
+ node.mNumFlushed = node.mContent->GetChildCount();
+
+ stackPos--;
+ }
+ mNotifyLevel = stackLen - 1;
+}
+
+bool nsXMLContentSink::IsMonolithicContainer(
+ mozilla::dom::NodeInfo* aNodeInfo) {
+ return ((aNodeInfo->NamespaceID() == kNameSpaceID_XHTML &&
+ (aNodeInfo->NameAtom() == nsGkAtoms::tr ||
+ aNodeInfo->NameAtom() == nsGkAtoms::select ||
+ aNodeInfo->NameAtom() == nsGkAtoms::object)) ||
+ (aNodeInfo->NamespaceID() == kNameSpaceID_MathML &&
+ (aNodeInfo->NameAtom() == nsGkAtoms::math)));
+}
+
+void nsXMLContentSink::ContinueInterruptedParsingIfEnabled() {
+ if (mParser && mParser->IsParserEnabled()) {
+ GetParser()->ContinueInterruptedParsing();
+ }
+}
+
+void nsXMLContentSink::ContinueInterruptedParsingAsync() {
+ nsCOMPtr<nsIRunnable> ev = NewRunnableMethod(
+ "nsXMLContentSink::ContinueInterruptedParsingIfEnabled", this,
+ &nsXMLContentSink::ContinueInterruptedParsingIfEnabled);
+
+ mDocument->Dispatch(mozilla::TaskCategory::Other, ev.forget());
+}
+
+nsIParser* nsXMLContentSink::GetParser() {
+ return static_cast<nsIParser*>(mParser.get());
+}