gecko-dev/dom/xml/nsXMLContentSink.cpp

1676 строки
49 KiB
C++

/* -*- 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 "nsIDocument.h"
#include "nsIContent.h"
#include "nsIURI.h"
#include "nsNetUtil.h"
#include "nsIDocShell.h"
#include "nsIStyleSheetLinkingElement.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 "nsIScriptContext.h"
#include "nsNameSpaceManager.h"
#include "nsIServiceManager.h"
#include "nsIScriptSecurityManager.h"
#include "nsIContentViewer.h"
#include "prtime.h"
#include "mozilla/Logging.h"
#include "nsRect.h"
#include "nsIWebNavigation.h"
#include "nsIScriptElement.h"
#include "nsStyleLinkElement.h"
#include "nsReadableUtils.h"
#include "nsUnicharUtils.h"
#include "nsICookieService.h"
#include "nsIPrompt.h"
#include "nsIChannel.h"
#include "nsIPrincipal.h"
#include "nsXMLPrettyPrinter.h"
#include "nsNodeInfoManager.h"
#include "nsContentCreatorFunctions.h"
#include "nsIContentPolicy.h"
#include "nsContentPolicyUtils.h"
#include "nsError.h"
#include "nsNodeUtils.h"
#include "nsIScriptGlobalObject.h"
#include "nsIHTMLDocument.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/ProcessingInstruction.h"
#include "mozilla/dom/ScriptLoader.h"
#include "mozilla/dom/txMozillaXSLTProcessor.h"
#include "mozilla/LoadInfo.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,
nsIDocument* 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()
{
}
nsresult
nsXMLContentSink::Init(nsIDocument* 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;
}
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_CLASS(nsXMLContentSink)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsXMLContentSink,
nsContentSink)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCurrentHead)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocElement)
for (uint32_t i = 0, count = tmp->mContentStack.Length(); i < count; i++) {
const StackNode& node = tmp->mContentStack.ElementAt(i);
cb.NoteXPCOMChild(node.mContent);
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentChildren)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
// 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;
}
// 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, nsDocument 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;
}
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;
}
NS_IMETHODIMP
nsXMLContentSink::OnDocumentCreated(nsIDocument* aResultDocument)
{
NS_ENSURE_ARG(aResultDocument);
nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(aResultDocument);
if (htmlDoc) {
htmlDoc->SetDocWriteDisabled(true);
}
nsCOMPtr<nsIContentViewer> contentViewer;
mDocShell->GetContentViewer(getter_AddRefs(contentViewer));
if (contentViewer) {
return contentViewer->SetDocumentInternal(aResultDocument, true);
}
return NS_OK;
}
NS_IMETHODIMP
nsXMLContentSink::OnTransformDone(nsresult aResult,
nsIDocument* aResultDocument)
{
MOZ_ASSERT(aResultDocument, "Don't notify about transform end without a document.");
mDocumentChildren.Clear();
nsCOMPtr<nsIContentViewer> contentViewer;
mDocShell->GetContentViewer(getter_AddRefs(contentViewer));
if (NS_FAILED(aResult) && contentViewer) {
// Transform failed.
aResultDocument->SetMayStartLayout(false);
// We have an error document.
contentViewer->SetDocument(aResultDocument);
}
nsCOMPtr<nsIDocument> originalDocument = mDocument;
bool blockingOnload = mIsBlockingOnload;
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;
nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(mDocument);
if (htmlDoc) {
htmlDoc->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) != -1,
"rootElement not in doc?");
mDocument->BeginUpdate();
nsNodeUtils::ContentInserted(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();
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();
}
NS_IMETHODIMP
nsXMLContentSink::WillResume(void)
{
return 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 =
nsContentUtils::NameSpaceManager()->
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)) {
nsCOMPtr<nsIStyleSheetLinkingElement> ssle(do_QueryInterface(content));
if (ssle) {
ssle->InitStyleLinkElement(false);
if (aFromParser) {
ssle->SetEnableUpdates(false);
}
if (!aNodeInfo->Equals(nsGkAtoms::link, kNameSpaceID_XHTML)) {
ssle->SetLineNumber(aFromParser ? aLineNumber : 0);
ssle->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 ((nodeInfo->NamespaceID() == kNameSpaceID_XHTML &&
(nodeInfo->NameAtom() == nsGkAtoms::select ||
nodeInfo->NameAtom() == nsGkAtoms::textarea ||
nodeInfo->NameAtom() == nsGkAtoms::video ||
nodeInfo->NameAtom() == nsGkAtoms::audio ||
nodeInfo->NameAtom() == nsGkAtoms::head ||
nodeInfo->NameAtom() == nsGkAtoms::object))
|| nodeInfo->NameAtom() == nsGkAtoms::title
) {
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 the parser got blocked, make sure to return the appropriate rv.
// I'm not sure if this is actually needed or not.
if (mParser && !mParser->IsParserEnabled()) {
block = true;
}
return block ? NS_ERROR_HTMLPARSER_BLOCK : NS_OK;
}
nsresult rv = NS_OK;
if (nodeInfo->Equals(nsGkAtoms::meta, kNameSpaceID_XHTML) &&
// Need to check here to make sure this meta tag does not set
// mPrettyPrintXML to false when we have a special root!
(!mPrettyPrintXML || !mPrettyPrintHasSpecialRoot)) {
rv = ProcessMETATag(aContent);
}
else if (nodeInfo->Equals(nsGkAtoms::link, kNameSpaceID_XHTML) ||
nodeInfo->Equals(nsGkAtoms::style, kNameSpaceID_XHTML) ||
nodeInfo->Equals(nsGkAtoms::style, kNameSpaceID_SVG)) {
nsCOMPtr<nsIStyleSheetLinkingElement> ssle(do_QueryInterface(aContent));
if (ssle) {
ssle->SetEnableUpdates(true);
auto updateOrError =
ssle->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);
}
} else {
nsCOMPtr<nsIContent> parent = GetCurrentContent();
if (parent) {
result = parent->AppendChildTo(aContent, false);
}
}
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();
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& 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, 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);
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 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 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;
}
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()
{
int32_t count = mContentStack.Length();
if (count == 0) {
NS_WARNING("Popping empty stack");
return;
}
mContentStack.RemoveElementAt(count - 1);
}
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);
}
mDocumentChildren.Clear();
}
// check for root elements that needs special handling for
// prettyprinting
if ((aNameSpaceID == kNameSpaceID_XBL &&
aTagName == nsGkAtoms::bindings) ||
(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);
}
}
}
nsresult rv = mDocument->AppendChildTo(mDocElement, NotifyForDocElement());
if (NS_FAILED(rv)) {
// 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;
}
if (aTagName == nsGkAtoms::html &&
aNameSpaceID == kNameSpaceID_XHTML) {
ProcessOfflineManifest(aContent);
}
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);
}
}
// Some HTML nodes need DoneCreatingElement() called to initialize
// properly (eg form state restoration).
if (nodeInfo->NamespaceID() == kNameSpaceID_XHTML) {
if (nodeInfo->NameAtom() == nsGkAtoms::input ||
nodeInfo->NameAtom() == nsGkAtoms::button ||
nodeInfo->NameAtom() == nsGkAtoms::menuitem ||
nodeInfo->NameAtom() == nsGkAtoms::audio ||
nodeInfo->NameAtom() == nsGkAtoms::video) {
content->DoneCreatingElement();
} else if (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;
// 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 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 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);
nsCOMPtr<nsIStyleSheetLinkingElement> ssle =
do_QueryInterface(ToSupports(node));
if (ssle) {
ssle->InitStyleLinkElement(false);
ssle->SetEnableUpdates(false);
mPrettyPrintXML = false;
}
nsresult rv = AddContentAsLeaf(node);
NS_ENSURE_SUCCESS(rv, rv);
DidAddContent();
if (ssle) {
// This is an xml-stylesheet processing instruction... but it might not be
// a CSS one if the type is set to something else.
ssle->SetEnableUpdates(true);
auto updateOrError =
ssle->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,
EmptyString());
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 };
NS_NAMED_LITERAL_STRING(errorNs,
"http://www.mozilla.org/newlayout/xml/parsererror.xml");
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::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;
bool oldBeganUpdate = mBeganUpdate;
uint32_t oldUpdates = mUpdatesInNotification;
mUpdatesInNotification = 0;
++mInNotification;
{
// Scope so we call EndUpdate before we decrease mInNotification
mozAutoDocUpdate updateBatch(mDocument, true);
mBeganUpdate = 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;
mBeganUpdate = oldBeganUpdate;
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());
}