michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=2 sw=2 et tw=78: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsCOMPtr.h" michael@0: #include "nsXMLContentSink.h" michael@0: #include "nsIParser.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsIDOMDocument.h" michael@0: #include "nsIDOMDocumentType.h" michael@0: #include "nsIContent.h" michael@0: #include "nsIURI.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsIDocShell.h" michael@0: #include "nsIStyleSheetLinkingElement.h" michael@0: #include "nsIDOMComment.h" michael@0: #include "nsIDOMCDATASection.h" michael@0: #include "DocumentType.h" michael@0: #include "nsHTMLParts.h" michael@0: #include "nsCRT.h" michael@0: #include "nsCSSStyleSheet.h" michael@0: #include "mozilla/css/Loader.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsIScriptContext.h" michael@0: #include "nsNameSpaceManager.h" michael@0: #include "nsIServiceManager.h" michael@0: #include "nsIScriptSecurityManager.h" michael@0: #include "nsIContentViewer.h" michael@0: #include "prtime.h" michael@0: #include "prlog.h" michael@0: #include "prmem.h" michael@0: #include "nsRect.h" michael@0: #include "nsIWebNavigation.h" michael@0: #include "nsIScriptElement.h" michael@0: #include "nsScriptLoader.h" michael@0: #include "nsStyleLinkElement.h" michael@0: #include "nsReadableUtils.h" michael@0: #include "nsUnicharUtils.h" michael@0: #include "nsICookieService.h" michael@0: #include "nsIPrompt.h" michael@0: #include "nsIChannel.h" michael@0: #include "nsIPrincipal.h" michael@0: #include "nsXMLPrettyPrinter.h" michael@0: #include "nsNodeInfoManager.h" michael@0: #include "nsContentCreatorFunctions.h" michael@0: #include "nsIContentPolicy.h" michael@0: #include "nsContentPolicyUtils.h" michael@0: #include "nsError.h" michael@0: #include "nsIDOMProcessingInstruction.h" michael@0: #include "nsNodeUtils.h" michael@0: #include "nsIScriptGlobalObject.h" michael@0: #include "nsIHTMLDocument.h" michael@0: #include "mozAutoDocUpdate.h" michael@0: #include "nsMimeTypes.h" michael@0: #include "nsHtml5SVGLoadDispatcher.h" michael@0: #include "nsTextNode.h" michael@0: #include "mozilla/dom/CDATASection.h" michael@0: #include "mozilla/dom/Comment.h" michael@0: #include "mozilla/dom/Element.h" michael@0: #include "mozilla/dom/ProcessingInstruction.h" michael@0: michael@0: using namespace mozilla::dom; michael@0: michael@0: // XXX Open Issues: michael@0: // 1) what's not allowed - We need to figure out which HTML tags michael@0: // (prefixed with a HTML namespace qualifier) are explicitly not michael@0: // allowed (if any). michael@0: // 2) factoring code with nsHTMLContentSink - There's some amount of michael@0: // common code between this and the HTML content sink. This will michael@0: // increase as we support more and more HTML elements. How can code michael@0: // from the code be factored? michael@0: michael@0: nsresult michael@0: NS_NewXMLContentSink(nsIXMLContentSink** aResult, michael@0: nsIDocument* aDoc, michael@0: nsIURI* aURI, michael@0: nsISupports* aContainer, michael@0: nsIChannel* aChannel) michael@0: { michael@0: NS_PRECONDITION(nullptr != aResult, "null ptr"); michael@0: if (nullptr == aResult) { michael@0: return NS_ERROR_NULL_POINTER; michael@0: } michael@0: nsXMLContentSink* it = new nsXMLContentSink(); michael@0: michael@0: nsCOMPtr kungFuDeathGrip = it; michael@0: nsresult rv = it->Init(aDoc, aURI, aContainer, aChannel); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return CallQueryInterface(it, aResult); michael@0: } michael@0: michael@0: nsXMLContentSink::nsXMLContentSink() michael@0: : mConstrainSize(true), michael@0: mPrettyPrintXML(true) michael@0: { michael@0: } michael@0: michael@0: nsXMLContentSink::~nsXMLContentSink() michael@0: { michael@0: if (mText) { michael@0: PR_Free(mText); // Doesn't null out, unlike PR_FREEIF michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsXMLContentSink::Init(nsIDocument* aDoc, michael@0: nsIURI* aURI, michael@0: nsISupports* aContainer, michael@0: nsIChannel* aChannel) michael@0: { michael@0: nsresult rv = nsContentSink::Init(aDoc, aURI, aContainer, aChannel); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: aDoc->AddObserver(this); michael@0: mIsDocumentObserver = true; michael@0: michael@0: if (!mDocShell) { michael@0: mPrettyPrintXML = false; michael@0: } michael@0: michael@0: mState = eXMLContentSinkState_InProlog; michael@0: mDocElement = nullptr; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsXMLContentSink) michael@0: NS_INTERFACE_MAP_ENTRY(nsIContentSink) michael@0: NS_INTERFACE_MAP_ENTRY(nsIXMLContentSink) michael@0: NS_INTERFACE_MAP_ENTRY(nsIExpatSink) michael@0: NS_INTERFACE_MAP_ENTRY(nsITransformObserver) michael@0: NS_INTERFACE_MAP_END_INHERITING(nsContentSink) michael@0: michael@0: NS_IMPL_ADDREF_INHERITED(nsXMLContentSink, nsContentSink) michael@0: NS_IMPL_RELEASE_INHERITED(nsXMLContentSink, nsContentSink) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CLASS(nsXMLContentSink) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsXMLContentSink, michael@0: nsContentSink) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCurrentHead) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocElement) michael@0: for (uint32_t i = 0, count = tmp->mContentStack.Length(); i < count; i++) { michael@0: const StackNode& node = tmp->mContentStack.ElementAt(i); michael@0: cb.NoteXPCOMChild(node.mContent); michael@0: } michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END michael@0: michael@0: // nsIContentSink michael@0: NS_IMETHODIMP michael@0: nsXMLContentSink::WillParse(void) michael@0: { michael@0: return WillParseImpl(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXMLContentSink::WillBuildModel(nsDTDMode aDTDMode) michael@0: { michael@0: WillBuildModelImpl(); michael@0: michael@0: // Notify document that the load is beginning michael@0: mDocument->BeginLoad(); michael@0: michael@0: // Check for correct load-command for maybe prettyprinting michael@0: if (mPrettyPrintXML) { michael@0: nsAutoCString command; michael@0: GetParser()->GetCommand(command); michael@0: if (!command.EqualsLiteral("view")) { michael@0: mPrettyPrintXML = false; michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: nsXMLContentSink::CanStillPrettyPrint() michael@0: { michael@0: return mPrettyPrintXML && michael@0: (!mPrettyPrintHasFactoredElements || mPrettyPrintHasSpecialRoot); michael@0: } michael@0: michael@0: nsresult michael@0: nsXMLContentSink::MaybePrettyPrint() michael@0: { michael@0: if (!CanStillPrettyPrint()) { michael@0: mPrettyPrintXML = false; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // stop observing in order to avoid crashing when replacing content michael@0: mDocument->RemoveObserver(this); michael@0: mIsDocumentObserver = false; michael@0: michael@0: // Reenable the CSSLoader so that the prettyprinting stylesheets can load michael@0: if (mCSSLoader) { michael@0: mCSSLoader->SetEnabled(true); michael@0: } michael@0: michael@0: nsRefPtr printer; michael@0: nsresult rv = NS_NewXMLPrettyPrinter(getter_AddRefs(printer)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: bool isPrettyPrinting; michael@0: rv = printer->PrettyPrint(mDocument, &isPrettyPrinting); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: mPrettyPrinting = isPrettyPrinting; michael@0: return NS_OK; michael@0: } michael@0: michael@0: static void michael@0: CheckXSLTParamPI(nsIDOMProcessingInstruction* aPi, michael@0: nsIDocumentTransformer* aProcessor, michael@0: nsIDocument* aDocument) michael@0: { michael@0: nsAutoString target, data; michael@0: aPi->GetTarget(target); michael@0: michael@0: // Check for namespace declarations michael@0: if (target.EqualsLiteral("xslt-param-namespace")) { michael@0: aPi->GetData(data); michael@0: nsAutoString prefix, namespaceAttr; michael@0: nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::prefix, michael@0: prefix); michael@0: if (!prefix.IsEmpty() && michael@0: nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::_namespace, michael@0: namespaceAttr)) { michael@0: aProcessor->AddXSLTParamNamespace(prefix, namespaceAttr); michael@0: } michael@0: } michael@0: michael@0: // Check for actual parameters michael@0: else if (target.EqualsLiteral("xslt-param")) { michael@0: aPi->GetData(data); michael@0: nsAutoString name, namespaceAttr, select, value; michael@0: nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::name, michael@0: name); michael@0: nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::_namespace, michael@0: namespaceAttr); michael@0: if (!nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::select, select)) { michael@0: select.SetIsVoid(true); michael@0: } michael@0: if (!nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::value, value)) { michael@0: value.SetIsVoid(true); michael@0: } michael@0: if (!name.IsEmpty()) { michael@0: nsCOMPtr doc = do_QueryInterface(aDocument); michael@0: aProcessor->AddXSLTParam(name, namespaceAttr, select, value, doc); michael@0: } michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXMLContentSink::DidBuildModel(bool aTerminated) michael@0: { michael@0: if (!mParser) { michael@0: // If mParser is null, this parse has already been terminated and must michael@0: // not been terminated again. However, nsDocument may still think that michael@0: // the parse has not been terminated and call back into here in the case michael@0: // where the XML parser has finished but the XSLT transform associated michael@0: // with the document has not. michael@0: return NS_OK; michael@0: } michael@0: michael@0: DidBuildModelImpl(aTerminated); michael@0: michael@0: if (mXSLTProcessor) { michael@0: // stop observing in order to avoid crashing when replacing content michael@0: mDocument->RemoveObserver(this); michael@0: mIsDocumentObserver = false; michael@0: michael@0: // Check for xslt-param and xslt-param-namespace PIs michael@0: for (nsIContent* child = mDocument->GetFirstChild(); michael@0: child; michael@0: child = child->GetNextSibling()) { michael@0: if (child->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION)) { michael@0: nsCOMPtr pi = do_QueryInterface(child); michael@0: CheckXSLTParamPI(pi, mXSLTProcessor, mDocument); michael@0: } michael@0: else if (child->IsElement()) { michael@0: // Only honor PIs in the prolog michael@0: break; michael@0: } michael@0: } michael@0: michael@0: nsCOMPtr currentDOMDoc(do_QueryInterface(mDocument)); michael@0: mXSLTProcessor->SetSourceContentModel(currentDOMDoc); michael@0: // Since the processor now holds a reference to us we drop our reference michael@0: // to it to avoid owning cycles michael@0: mXSLTProcessor = nullptr; michael@0: } michael@0: else { michael@0: // Kick off layout for non-XSLT transformed documents. michael@0: michael@0: // Check if we want to prettyprint michael@0: MaybePrettyPrint(); michael@0: michael@0: bool startLayout = true; michael@0: michael@0: if (mPrettyPrinting) { michael@0: NS_ASSERTION(!mPendingSheetCount, "Shouldn't have pending sheets here!"); michael@0: michael@0: // We're pretty-printing now. See whether we should wait up on michael@0: // stylesheet loads michael@0: if (mDocument->CSSLoader()->HasPendingLoads() && michael@0: NS_SUCCEEDED(mDocument->CSSLoader()->AddObserver(this))) { michael@0: // wait for those sheets to load michael@0: startLayout = false; michael@0: } michael@0: } michael@0: michael@0: if (startLayout) { michael@0: StartLayout(false); michael@0: michael@0: ScrollToRef(); michael@0: } michael@0: michael@0: mDocument->RemoveObserver(this); michael@0: mIsDocumentObserver = false; michael@0: michael@0: mDocument->EndLoad(); michael@0: } michael@0: michael@0: DropParserAndPerfHint(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXMLContentSink::OnDocumentCreated(nsIDocument* aResultDocument) michael@0: { michael@0: NS_ENSURE_ARG(aResultDocument); michael@0: michael@0: nsCOMPtr htmlDoc = do_QueryInterface(aResultDocument); michael@0: if (htmlDoc) { michael@0: htmlDoc->SetDocWriteDisabled(true); michael@0: } michael@0: michael@0: nsCOMPtr contentViewer; michael@0: mDocShell->GetContentViewer(getter_AddRefs(contentViewer)); michael@0: if (contentViewer) { michael@0: return contentViewer->SetDocumentInternal(aResultDocument, true); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXMLContentSink::OnTransformDone(nsresult aResult, michael@0: nsIDocument* aResultDocument) michael@0: { michael@0: NS_ASSERTION(NS_FAILED(aResult) || aResultDocument, michael@0: "Don't notify about transform success without a document."); michael@0: michael@0: nsCOMPtr domDoc = do_QueryInterface(aResultDocument); michael@0: michael@0: nsCOMPtr contentViewer; michael@0: mDocShell->GetContentViewer(getter_AddRefs(contentViewer)); michael@0: michael@0: if (NS_FAILED(aResult) && contentViewer) { michael@0: // Transform failed. michael@0: if (domDoc) { michael@0: aResultDocument->SetMayStartLayout(false); michael@0: // We have an error document. michael@0: contentViewer->SetDOMDocument(domDoc); michael@0: } michael@0: else { michael@0: // We don't have an error document, display the michael@0: // untransformed source document. michael@0: nsCOMPtr document = do_QueryInterface(mDocument); michael@0: contentViewer->SetDOMDocument(document); michael@0: } michael@0: } michael@0: michael@0: nsCOMPtr originalDocument = mDocument; michael@0: if (NS_SUCCEEDED(aResult) || aResultDocument) { michael@0: // Transform succeeded or it failed and we have an error michael@0: // document to display. michael@0: mDocument = aResultDocument; michael@0: nsCOMPtr htmlDoc = do_QueryInterface(mDocument); michael@0: if (htmlDoc) { michael@0: htmlDoc->SetDocWriteDisabled(false); michael@0: } michael@0: } michael@0: michael@0: // Notify document observers that all the content has been stuck michael@0: // into the document. michael@0: // XXX do we need to notify for things like PIs? Or just the michael@0: // documentElement? michael@0: nsIContent *rootElement = mDocument->GetRootElement(); michael@0: if (rootElement) { michael@0: NS_ASSERTION(mDocument->IndexOf(rootElement) != -1, michael@0: "rootElement not in doc?"); michael@0: mDocument->BeginUpdate(UPDATE_CONTENT_MODEL); michael@0: nsNodeUtils::ContentInserted(mDocument, rootElement, michael@0: mDocument->IndexOf(rootElement)); michael@0: mDocument->EndUpdate(UPDATE_CONTENT_MODEL); michael@0: } michael@0: michael@0: // Start the layout process michael@0: StartLayout(false); michael@0: michael@0: ScrollToRef(); michael@0: michael@0: originalDocument->EndLoad(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXMLContentSink::StyleSheetLoaded(nsCSSStyleSheet* aSheet, michael@0: bool aWasAlternate, michael@0: nsresult aStatus) michael@0: { michael@0: if (!mPrettyPrinting) { michael@0: return nsContentSink::StyleSheetLoaded(aSheet, aWasAlternate, aStatus); michael@0: } michael@0: michael@0: if (!mDocument->CSSLoader()->HasPendingLoads()) { michael@0: mDocument->CSSLoader()->RemoveObserver(this); michael@0: StartLayout(false); michael@0: ScrollToRef(); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXMLContentSink::WillInterrupt(void) michael@0: { michael@0: return WillInterruptImpl(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXMLContentSink::WillResume(void) michael@0: { michael@0: return WillResumeImpl(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXMLContentSink::SetParser(nsParserBase* aParser) michael@0: { michael@0: NS_PRECONDITION(aParser, "Should have a parser here!"); michael@0: mParser = aParser; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsXMLContentSink::CreateElement(const char16_t** aAtts, uint32_t aAttsCount, michael@0: nsINodeInfo* aNodeInfo, uint32_t aLineNumber, michael@0: nsIContent** aResult, bool* aAppendContent, michael@0: FromParser aFromParser) michael@0: { michael@0: NS_ASSERTION(aNodeInfo, "can't create element without nodeinfo"); michael@0: michael@0: *aResult = nullptr; michael@0: *aAppendContent = true; michael@0: nsresult rv = NS_OK; michael@0: michael@0: nsCOMPtr ni = aNodeInfo; michael@0: nsCOMPtr content; michael@0: rv = NS_NewElement(getter_AddRefs(content), ni.forget(), aFromParser); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XHTML) michael@0: || aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_SVG) michael@0: ) { michael@0: nsCOMPtr sele = do_QueryInterface(content); michael@0: sele->SetScriptLineNumber(aLineNumber); michael@0: sele->SetCreatorParser(GetParser()); michael@0: mConstrainSize = false; michael@0: } michael@0: michael@0: // XHTML needs some special attention michael@0: if (aNodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) { michael@0: mPrettyPrintHasFactoredElements = true; michael@0: } michael@0: else { michael@0: // If we care, find out if we just used a special factory. michael@0: if (!mPrettyPrintHasFactoredElements && !mPrettyPrintHasSpecialRoot && michael@0: mPrettyPrintXML) { michael@0: mPrettyPrintHasFactoredElements = michael@0: nsContentUtils::NameSpaceManager()-> michael@0: HasElementCreator(aNodeInfo->NamespaceID()); michael@0: } michael@0: michael@0: if (!aNodeInfo->NamespaceEquals(kNameSpaceID_SVG)) { michael@0: content.forget(aResult); michael@0: michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: if (aNodeInfo->Equals(nsGkAtoms::link, kNameSpaceID_XHTML) || michael@0: aNodeInfo->Equals(nsGkAtoms::style, kNameSpaceID_XHTML) || michael@0: aNodeInfo->Equals(nsGkAtoms::style, kNameSpaceID_SVG)) { michael@0: nsCOMPtr ssle(do_QueryInterface(content)); michael@0: if (ssle) { michael@0: ssle->InitStyleLinkElement(false); michael@0: if (aFromParser) { michael@0: ssle->SetEnableUpdates(false); michael@0: } michael@0: if (!aNodeInfo->Equals(nsGkAtoms::link, kNameSpaceID_XHTML)) { michael@0: ssle->SetLineNumber(aFromParser ? aLineNumber : 0); michael@0: } michael@0: } michael@0: } michael@0: michael@0: content.forget(aResult); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: nsXMLContentSink::CloseElement(nsIContent* aContent) michael@0: { michael@0: NS_ASSERTION(aContent, "missing element to close"); michael@0: michael@0: nsINodeInfo *nodeInfo = aContent->NodeInfo(); michael@0: michael@0: // Some HTML nodes need DoneAddingChildren() called to initialize michael@0: // properly (eg form state restoration). michael@0: if ((nodeInfo->NamespaceID() == kNameSpaceID_XHTML && michael@0: (nodeInfo->NameAtom() == nsGkAtoms::select || michael@0: nodeInfo->NameAtom() == nsGkAtoms::textarea || michael@0: nodeInfo->NameAtom() == nsGkAtoms::video || michael@0: nodeInfo->NameAtom() == nsGkAtoms::audio || michael@0: nodeInfo->NameAtom() == nsGkAtoms::object || michael@0: nodeInfo->NameAtom() == nsGkAtoms::applet)) michael@0: || nodeInfo->NameAtom() == nsGkAtoms::title michael@0: ) { michael@0: aContent->DoneAddingChildren(HaveNotifiedForCurrentContent()); michael@0: } michael@0: michael@0: if (IsMonolithicContainer(nodeInfo)) { michael@0: mInMonolithicContainer--; michael@0: } michael@0: michael@0: if (!nodeInfo->NamespaceEquals(kNameSpaceID_XHTML) && michael@0: !nodeInfo->NamespaceEquals(kNameSpaceID_SVG)) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (nodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XHTML) michael@0: || nodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_SVG) michael@0: ) { michael@0: mConstrainSize = true; michael@0: nsCOMPtr sele = do_QueryInterface(aContent); michael@0: michael@0: if (mPreventScriptExecution) { michael@0: sele->PreventExecution(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Always check the clock in nsContentSink right after a script michael@0: StopDeflecting(); michael@0: michael@0: // Now tell the script that it's ready to go. This may execute the script michael@0: // or return true, or neither if the script doesn't need executing. michael@0: bool block = sele->AttemptToExecute(); michael@0: michael@0: // If the parser got blocked, make sure to return the appropriate rv. michael@0: // I'm not sure if this is actually needed or not. michael@0: if (mParser && !mParser->IsParserEnabled()) { michael@0: // XXX The HTML sink doesn't call BlockParser here, why do we? michael@0: GetParser()->BlockParser(); michael@0: block = true; michael@0: } michael@0: michael@0: return block ? NS_ERROR_HTMLPARSER_BLOCK : NS_OK; michael@0: } michael@0: michael@0: nsresult rv = NS_OK; michael@0: if (nodeInfo->Equals(nsGkAtoms::meta, kNameSpaceID_XHTML) && michael@0: // Need to check here to make sure this meta tag does not set michael@0: // mPrettyPrintXML to false when we have a special root! michael@0: (!mPrettyPrintXML || !mPrettyPrintHasSpecialRoot)) { michael@0: rv = ProcessMETATag(aContent); michael@0: } michael@0: else if (nodeInfo->Equals(nsGkAtoms::link, kNameSpaceID_XHTML) || michael@0: nodeInfo->Equals(nsGkAtoms::style, kNameSpaceID_XHTML) || michael@0: nodeInfo->Equals(nsGkAtoms::style, kNameSpaceID_SVG)) { michael@0: nsCOMPtr ssle(do_QueryInterface(aContent)); michael@0: if (ssle) { michael@0: ssle->SetEnableUpdates(true); michael@0: bool willNotify; michael@0: bool isAlternate; michael@0: rv = ssle->UpdateStyleSheet(mRunsToCompletion ? nullptr : this, michael@0: &willNotify, michael@0: &isAlternate); michael@0: if (NS_SUCCEEDED(rv) && willNotify && !isAlternate && !mRunsToCompletion) { michael@0: ++mPendingSheetCount; michael@0: mScriptLoader->AddExecuteBlocker(); michael@0: } michael@0: } michael@0: // Look for michael@0: // and look for like in HTML sink michael@0: if (nodeInfo->Equals(nsGkAtoms::link, kNameSpaceID_XHTML)) { michael@0: nsAutoString relVal; michael@0: aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::rel, relVal); michael@0: if (!relVal.IsEmpty()) { michael@0: uint32_t linkTypes = nsStyleLinkElement::ParseLinkTypes(relVal); michael@0: bool hasPrefetch = linkTypes & nsStyleLinkElement::ePREFETCH; michael@0: if (hasPrefetch || (linkTypes & nsStyleLinkElement::eNEXT)) { michael@0: nsAutoString hrefVal; michael@0: aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::href, hrefVal); michael@0: if (!hrefVal.IsEmpty()) { michael@0: PrefetchHref(hrefVal, aContent, hasPrefetch); michael@0: } michael@0: } michael@0: if (linkTypes & nsStyleLinkElement::eDNS_PREFETCH) { michael@0: nsAutoString hrefVal; michael@0: aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::href, hrefVal); michael@0: if (!hrefVal.IsEmpty()) { michael@0: PrefetchDNS(hrefVal); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsXMLContentSink::AddContentAsLeaf(nsIContent *aContent) michael@0: { michael@0: nsresult result = NS_OK; michael@0: michael@0: if ((eXMLContentSinkState_InProlog == mState) || michael@0: (eXMLContentSinkState_InEpilog == mState)) { michael@0: NS_ASSERTION(mDocument, "Fragments have no prolog or epilog"); michael@0: mDocument->AppendChildTo(aContent, false); michael@0: } michael@0: else { michael@0: nsCOMPtr parent = GetCurrentContent(); michael@0: michael@0: if (parent) { michael@0: result = parent->AppendChildTo(aContent, false); michael@0: } michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: // Create an XML parser and an XSL content sink and start parsing michael@0: // the XSL stylesheet located at the given URI. michael@0: nsresult michael@0: nsXMLContentSink::LoadXSLStyleSheet(nsIURI* aUrl) michael@0: { michael@0: nsCOMPtr processor = michael@0: do_CreateInstance("@mozilla.org/document-transformer;1?type=xslt"); michael@0: if (!processor) { michael@0: // No XSLT processor available, continue normal document loading michael@0: return NS_OK; michael@0: } michael@0: michael@0: processor->Init(mDocument->NodePrincipal()); michael@0: processor->SetTransformObserver(this); michael@0: michael@0: nsCOMPtr loadGroup = mDocument->GetDocumentLoadGroup(); michael@0: if (!loadGroup) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: if (NS_SUCCEEDED(processor->LoadStyleSheet(aUrl, loadGroup))) { michael@0: mXSLTProcessor.swap(processor); michael@0: } michael@0: michael@0: // Intentionally ignore errors here, we should continue loading the michael@0: // XML document whether we're able to load the XSLT stylesheet or michael@0: // not. michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsXMLContentSink::ProcessStyleLink(nsIContent* aElement, michael@0: const nsSubstring& aHref, michael@0: bool aAlternate, michael@0: const nsSubstring& aTitle, michael@0: const nsSubstring& aType, michael@0: const nsSubstring& aMedia) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: mPrettyPrintXML = false; michael@0: michael@0: nsAutoCString cmd; michael@0: if (mParser) michael@0: GetParser()->GetCommand(cmd); michael@0: if (cmd.EqualsASCII(kLoadAsData)) michael@0: return NS_OK; // Do not load stylesheets when loading as data michael@0: michael@0: NS_ConvertUTF16toUTF8 type(aType); michael@0: if (type.EqualsIgnoreCase(TEXT_XSL) || michael@0: type.EqualsIgnoreCase(APPLICATION_XSLT_XML) || michael@0: type.EqualsIgnoreCase(TEXT_XML) || michael@0: type.EqualsIgnoreCase(APPLICATION_XML)) { michael@0: if (aAlternate) { michael@0: // don't load alternate XSLT michael@0: return NS_OK; michael@0: } michael@0: // LoadXSLStyleSheet needs a mDocShell. michael@0: if (!mDocShell) michael@0: return NS_OK; michael@0: michael@0: nsCOMPtr url; michael@0: rv = NS_NewURI(getter_AddRefs(url), aHref, nullptr, michael@0: mDocument->GetDocBaseURI()); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Do security check michael@0: nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager(); michael@0: rv = secMan-> michael@0: CheckLoadURIWithPrincipal(mDocument->NodePrincipal(), url, michael@0: nsIScriptSecurityManager::ALLOW_CHROME); michael@0: NS_ENSURE_SUCCESS(rv, NS_OK); michael@0: michael@0: // Do content policy check michael@0: int16_t decision = nsIContentPolicy::ACCEPT; michael@0: rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_XSLT, michael@0: url, michael@0: mDocument->NodePrincipal(), michael@0: aElement, michael@0: type, michael@0: nullptr, michael@0: &decision, michael@0: nsContentUtils::GetContentPolicy(), michael@0: nsContentUtils::GetSecurityManager()); michael@0: michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (NS_CP_REJECTED(decision)) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: return LoadXSLStyleSheet(url); michael@0: } michael@0: michael@0: // Let nsContentSink deal with css. michael@0: rv = nsContentSink::ProcessStyleLink(aElement, aHref, aAlternate, michael@0: aTitle, aType, aMedia); michael@0: michael@0: // nsContentSink::ProcessStyleLink handles the bookkeeping here wrt michael@0: // pending sheets. michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXMLContentSink::SetDocumentCharset(nsACString& aCharset) michael@0: { michael@0: if (mDocument) { michael@0: mDocument->SetDocumentCharacterSet(aCharset); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsISupports * michael@0: nsXMLContentSink::GetTarget() michael@0: { michael@0: return mDocument; michael@0: } michael@0: michael@0: bool michael@0: nsXMLContentSink::IsScriptExecuting() michael@0: { michael@0: return IsScriptExecutingImpl(); michael@0: } michael@0: michael@0: nsresult michael@0: nsXMLContentSink::FlushText(bool aReleaseTextNode) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: michael@0: if (mTextLength != 0) { michael@0: if (mLastTextNode) { michael@0: if ((mLastTextNodeSize + mTextLength) > mTextSize && !mXSLTProcessor) { michael@0: mLastTextNodeSize = 0; michael@0: mLastTextNode = nullptr; michael@0: FlushText(aReleaseTextNode); michael@0: } else { michael@0: bool notify = HaveNotifiedForCurrentContent(); michael@0: // We could probably always increase mInNotification here since michael@0: // if AppendText doesn't notify it shouldn't trigger evil code. michael@0: // But just in case it does, we don't want to mask any notifications. michael@0: if (notify) { michael@0: ++mInNotification; michael@0: } michael@0: rv = mLastTextNode->AppendText(mText, mTextLength, notify); michael@0: if (notify) { michael@0: --mInNotification; michael@0: } michael@0: michael@0: mLastTextNodeSize += mTextLength; michael@0: mTextLength = 0; michael@0: } michael@0: } else { michael@0: nsRefPtr textContent = new nsTextNode(mNodeInfoManager); michael@0: michael@0: mLastTextNode = textContent; michael@0: michael@0: // Set the text in the text node michael@0: textContent->SetText(mText, mTextLength, false); michael@0: mLastTextNodeSize += mTextLength; michael@0: mTextLength = 0; michael@0: michael@0: // Add text to its parent michael@0: rv = AddContentAsLeaf(textContent); michael@0: } michael@0: } michael@0: michael@0: if (aReleaseTextNode) { michael@0: mLastTextNodeSize = 0; michael@0: mLastTextNode = nullptr; michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: nsIContent* michael@0: nsXMLContentSink::GetCurrentContent() michael@0: { michael@0: if (mContentStack.Length() == 0) { michael@0: return nullptr; michael@0: } michael@0: return GetCurrentStackNode()->mContent; michael@0: } michael@0: michael@0: StackNode* michael@0: nsXMLContentSink::GetCurrentStackNode() michael@0: { michael@0: int32_t count = mContentStack.Length(); michael@0: return count != 0 ? &mContentStack[count-1] : nullptr; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: nsXMLContentSink::PushContent(nsIContent *aContent) michael@0: { michael@0: NS_PRECONDITION(aContent, "Null content being pushed!"); michael@0: StackNode *sn = mContentStack.AppendElement(); michael@0: NS_ENSURE_TRUE(sn, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: sn->mContent = aContent; michael@0: sn->mNumFlushed = 0; michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsXMLContentSink::PopContent() michael@0: { michael@0: int32_t count = mContentStack.Length(); michael@0: michael@0: if (count == 0) { michael@0: NS_WARNING("Popping empty stack"); michael@0: return; michael@0: } michael@0: michael@0: mContentStack.RemoveElementAt(count - 1); michael@0: } michael@0: michael@0: bool michael@0: nsXMLContentSink::HaveNotifiedForCurrentContent() const michael@0: { michael@0: uint32_t stackLength = mContentStack.Length(); michael@0: if (stackLength) { michael@0: const StackNode& stackNode = mContentStack[stackLength - 1]; michael@0: nsIContent* parent = stackNode.mContent; michael@0: return stackNode.mNumFlushed == parent->GetChildCount(); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: nsXMLContentSink::MaybeStartLayout(bool aIgnorePendingSheets) michael@0: { michael@0: // XXXbz if aIgnorePendingSheets is true, what should we do when michael@0: // mXSLTProcessor or CanStillPrettyPrint()? michael@0: if (mLayoutStarted || mXSLTProcessor || CanStillPrettyPrint()) { michael@0: return; michael@0: } michael@0: StartLayout(aIgnorePendingSheets); michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////// michael@0: michael@0: bool michael@0: nsXMLContentSink::SetDocElement(int32_t aNameSpaceID, michael@0: nsIAtom* aTagName, michael@0: nsIContent *aContent) michael@0: { michael@0: if (mDocElement) michael@0: return false; michael@0: michael@0: // check for root elements that needs special handling for michael@0: // prettyprinting michael@0: if ((aNameSpaceID == kNameSpaceID_XBL && michael@0: aTagName == nsGkAtoms::bindings) || michael@0: (aNameSpaceID == kNameSpaceID_XSLT && michael@0: (aTagName == nsGkAtoms::stylesheet || michael@0: aTagName == nsGkAtoms::transform))) { michael@0: mPrettyPrintHasSpecialRoot = true; michael@0: if (mPrettyPrintXML) { michael@0: // In this case, disable script execution, stylesheet michael@0: // loading, and auto XLinks since we plan to prettyprint. michael@0: mDocument->ScriptLoader()->SetEnabled(false); michael@0: if (mCSSLoader) { michael@0: mCSSLoader->SetEnabled(false); michael@0: } michael@0: } michael@0: } michael@0: michael@0: mDocElement = aContent; michael@0: nsresult rv = mDocument->AppendChildTo(mDocElement, NotifyForDocElement()); michael@0: if (NS_FAILED(rv)) { michael@0: // If we return false here, the caller will bail out because it won't michael@0: // find a parent content node to append to, which is fine. michael@0: return false; michael@0: } michael@0: michael@0: if (aTagName == nsGkAtoms::html && michael@0: aNameSpaceID == kNameSpaceID_XHTML) { michael@0: ProcessOfflineManifest(aContent); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXMLContentSink::HandleStartElement(const char16_t *aName, michael@0: const char16_t **aAtts, michael@0: uint32_t aAttsCount, michael@0: int32_t aIndex, michael@0: uint32_t aLineNumber) michael@0: { michael@0: return HandleStartElement(aName, aAtts, aAttsCount, aIndex, aLineNumber, michael@0: true); michael@0: } michael@0: michael@0: nsresult michael@0: nsXMLContentSink::HandleStartElement(const char16_t *aName, michael@0: const char16_t **aAtts, michael@0: uint32_t aAttsCount, michael@0: int32_t aIndex, michael@0: uint32_t aLineNumber, michael@0: bool aInterruptable) michael@0: { michael@0: NS_PRECONDITION(aIndex >= -1, "Bogus aIndex"); michael@0: NS_PRECONDITION(aAttsCount % 2 == 0, "incorrect aAttsCount"); michael@0: // Adjust aAttsCount so it's the actual number of attributes michael@0: aAttsCount /= 2; michael@0: michael@0: nsresult result = NS_OK; michael@0: bool appendContent = true; michael@0: nsCOMPtr content; michael@0: michael@0: // XXX Hopefully the parser will flag this before we get michael@0: // here. If we're in the epilog, there should be no michael@0: // new elements michael@0: PR_ASSERT(eXMLContentSinkState_InEpilog != mState); michael@0: michael@0: FlushText(); michael@0: DidAddContent(); michael@0: michael@0: mState = eXMLContentSinkState_InDocumentElement; michael@0: michael@0: int32_t nameSpaceID; michael@0: nsCOMPtr prefix, localName; michael@0: nsContentUtils::SplitExpatName(aName, getter_AddRefs(prefix), michael@0: getter_AddRefs(localName), &nameSpaceID); michael@0: michael@0: if (!OnOpenContainer(aAtts, aAttsCount, nameSpaceID, localName, aLineNumber)) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsCOMPtr nodeInfo; michael@0: nodeInfo = mNodeInfoManager->GetNodeInfo(localName, prefix, nameSpaceID, michael@0: nsIDOMNode::ELEMENT_NODE); michael@0: michael@0: result = CreateElement(aAtts, aAttsCount, nodeInfo, aLineNumber, michael@0: getter_AddRefs(content), &appendContent, michael@0: FROM_PARSER_NETWORK); michael@0: NS_ENSURE_SUCCESS(result, result); michael@0: michael@0: // Have to do this before we push the new content on the stack... and have to michael@0: // do that before we set attributes, call BindToTree, etc. Ideally we'd push michael@0: // on the stack inside CreateElement (which is effectively what the HTML sink michael@0: // does), but that's hard with all the subclass overrides going on. michael@0: nsCOMPtr parent = GetCurrentContent(); michael@0: michael@0: result = PushContent(content); michael@0: NS_ENSURE_SUCCESS(result, result); michael@0: michael@0: // Set the ID attribute atom on the node info object for this node michael@0: // This must occur before the attributes are added so the name michael@0: // of the id attribute is known. michael@0: if (aIndex != -1 && NS_SUCCEEDED(result)) { michael@0: nsCOMPtr IDAttr = do_GetAtom(aAtts[aIndex]); michael@0: michael@0: if (IDAttr) { michael@0: nodeInfo->SetIDAttributeAtom(IDAttr); michael@0: } michael@0: } michael@0: michael@0: // Set the attributes on the new content element michael@0: result = AddAttributes(aAtts, content); michael@0: michael@0: if (NS_OK == result) { michael@0: // Store the element michael@0: if (!SetDocElement(nameSpaceID, localName, content) && appendContent) { michael@0: NS_ENSURE_TRUE(parent, NS_ERROR_UNEXPECTED); michael@0: michael@0: parent->AppendChildTo(content, false); michael@0: } michael@0: } michael@0: michael@0: // Some HTML nodes need DoneCreatingElement() called to initialize michael@0: // properly (eg form state restoration). michael@0: if (nodeInfo->NamespaceID() == kNameSpaceID_XHTML) { michael@0: if (nodeInfo->NameAtom() == nsGkAtoms::input || michael@0: nodeInfo->NameAtom() == nsGkAtoms::button || michael@0: nodeInfo->NameAtom() == nsGkAtoms::menuitem || michael@0: nodeInfo->NameAtom() == nsGkAtoms::audio || michael@0: nodeInfo->NameAtom() == nsGkAtoms::video) { michael@0: content->DoneCreatingElement(); michael@0: } else if (nodeInfo->NameAtom() == nsGkAtoms::head && !mCurrentHead) { michael@0: mCurrentHead = content; michael@0: } michael@0: } michael@0: michael@0: if (IsMonolithicContainer(nodeInfo)) { michael@0: mInMonolithicContainer++; michael@0: } michael@0: michael@0: if (content != mDocElement && !mCurrentHead) { michael@0: // This isn't the root and we're not inside an XHTML . michael@0: // Might need to start layout michael@0: MaybeStartLayout(false); michael@0: } michael@0: michael@0: if (content == mDocElement) { michael@0: NotifyDocElementCreated(mDocument); michael@0: } michael@0: michael@0: return aInterruptable && NS_SUCCEEDED(result) ? DidProcessATokenImpl() : michael@0: result; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXMLContentSink::HandleEndElement(const char16_t *aName) michael@0: { michael@0: return HandleEndElement(aName, true); michael@0: } michael@0: michael@0: nsresult michael@0: nsXMLContentSink::HandleEndElement(const char16_t *aName, michael@0: bool aInterruptable) michael@0: { michael@0: nsresult result = NS_OK; michael@0: michael@0: // XXX Hopefully the parser will flag this before we get michael@0: // here. If we're in the prolog or epilog, there should be michael@0: // no close tags for elements. michael@0: PR_ASSERT(eXMLContentSinkState_InDocumentElement == mState); michael@0: michael@0: FlushText(); michael@0: michael@0: StackNode* sn = GetCurrentStackNode(); michael@0: if (!sn) { michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: nsCOMPtr content; michael@0: sn->mContent.swap(content); michael@0: uint32_t numFlushed = sn->mNumFlushed; michael@0: michael@0: PopContent(); michael@0: NS_ASSERTION(content, "failed to pop content"); michael@0: #ifdef DEBUG michael@0: // Check that we're closing the right thing michael@0: nsCOMPtr debugNameSpacePrefix, debugTagAtom; michael@0: int32_t debugNameSpaceID; michael@0: nsContentUtils::SplitExpatName(aName, getter_AddRefs(debugNameSpacePrefix), michael@0: getter_AddRefs(debugTagAtom), michael@0: &debugNameSpaceID); michael@0: NS_ASSERTION(content->NodeInfo()->Equals(debugTagAtom, debugNameSpaceID), michael@0: "Wrong element being closed"); michael@0: #endif michael@0: michael@0: result = CloseElement(content); michael@0: michael@0: if (mCurrentHead == content) { michael@0: mCurrentHead = nullptr; michael@0: } michael@0: michael@0: if (mDocElement == content) { michael@0: // XXXbz for roots that don't want to be appended on open, we michael@0: // probably need to deal here.... (and stop appending them on open). michael@0: mState = eXMLContentSinkState_InEpilog; michael@0: michael@0: // We might have had no occasion to start layout yet. Do so now. michael@0: MaybeStartLayout(false); michael@0: } michael@0: michael@0: int32_t stackLen = mContentStack.Length(); michael@0: if (mNotifyLevel >= stackLen) { michael@0: if (numFlushed < content->GetChildCount()) { michael@0: NotifyAppend(content, numFlushed); michael@0: } michael@0: mNotifyLevel = stackLen - 1; michael@0: } michael@0: DidAddContent(); michael@0: michael@0: if (content->IsSVG(nsGkAtoms::svg)) { michael@0: FlushTags(); michael@0: nsCOMPtr event = new nsHtml5SVGLoadDispatcher(content); michael@0: if (NS_FAILED(NS_DispatchToMainThread(event))) { michael@0: NS_WARNING("failed to dispatch svg load dispatcher"); michael@0: } michael@0: } michael@0: michael@0: return aInterruptable && NS_SUCCEEDED(result) ? DidProcessATokenImpl() : michael@0: result; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXMLContentSink::HandleComment(const char16_t *aName) michael@0: { michael@0: FlushText(); michael@0: michael@0: nsRefPtr comment = new Comment(mNodeInfoManager); michael@0: comment->SetText(nsDependentString(aName), false); michael@0: nsresult rv = AddContentAsLeaf(comment); michael@0: DidAddContent(); michael@0: michael@0: return NS_SUCCEEDED(rv) ? DidProcessATokenImpl() : rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXMLContentSink::HandleCDataSection(const char16_t *aData, michael@0: uint32_t aLength) michael@0: { michael@0: // XSLT doesn't differentiate between text and cdata and wants adjacent michael@0: // textnodes merged, so add as text. michael@0: if (mXSLTProcessor) { michael@0: return AddText(aData, aLength); michael@0: } michael@0: michael@0: FlushText(); michael@0: michael@0: nsRefPtr cdata = new CDATASection(mNodeInfoManager); michael@0: cdata->SetText(aData, aLength, false); michael@0: nsresult rv = AddContentAsLeaf(cdata); michael@0: DidAddContent(); michael@0: michael@0: return NS_SUCCEEDED(rv) ? DidProcessATokenImpl() : rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXMLContentSink::HandleDoctypeDecl(const nsAString & aSubset, michael@0: const nsAString & aName, michael@0: const nsAString & aSystemId, michael@0: const nsAString & aPublicId, michael@0: nsISupports* aCatalogData) michael@0: { michael@0: FlushText(); michael@0: michael@0: nsresult rv = NS_OK; michael@0: michael@0: NS_ASSERTION(mDocument, "Shouldn't get here from a document fragment"); michael@0: michael@0: nsCOMPtr name = do_GetAtom(aName); michael@0: NS_ENSURE_TRUE(name, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: // Create a new doctype node michael@0: nsCOMPtr docType; michael@0: rv = NS_NewDOMDocumentType(getter_AddRefs(docType), mNodeInfoManager, michael@0: name, aPublicId, aSystemId, aSubset); michael@0: if (NS_FAILED(rv) || !docType) { michael@0: return rv; michael@0: } michael@0: michael@0: if (aCatalogData && mCSSLoader && mDocument) { michael@0: // bug 124570 - we only expect additional agent sheets for now -- ignore michael@0: // exit codes, error are not fatal here, just that the stylesheet won't apply michael@0: nsCOMPtr uri(do_QueryInterface(aCatalogData)); michael@0: if (uri) { michael@0: nsRefPtr sheet; michael@0: mCSSLoader->LoadSheetSync(uri, true, true, getter_AddRefs(sheet)); michael@0: michael@0: #ifdef DEBUG michael@0: nsAutoCString uriStr; michael@0: uri->GetSpec(uriStr); michael@0: printf("Loading catalog stylesheet: %s ... %s\n", uriStr.get(), sheet.get() ? "Done" : "Failed"); michael@0: #endif michael@0: if (sheet) { michael@0: mDocument->BeginUpdate(UPDATE_STYLE); michael@0: mDocument->AddCatalogStyleSheet(sheet); michael@0: mDocument->EndUpdate(UPDATE_STYLE); michael@0: } michael@0: } michael@0: } michael@0: michael@0: nsCOMPtr content = do_QueryInterface(docType); michael@0: NS_ASSERTION(content, "doctype isn't content?"); michael@0: michael@0: rv = mDocument->AppendChildTo(content, false); michael@0: DidAddContent(); michael@0: return NS_SUCCEEDED(rv) ? DidProcessATokenImpl() : rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXMLContentSink::HandleCharacterData(const char16_t *aData, michael@0: uint32_t aLength) michael@0: { michael@0: return HandleCharacterData(aData, aLength, true); michael@0: } michael@0: michael@0: nsresult michael@0: nsXMLContentSink::HandleCharacterData(const char16_t *aData, uint32_t aLength, michael@0: bool aInterruptable) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: if (aData && mState != eXMLContentSinkState_InProlog && michael@0: mState != eXMLContentSinkState_InEpilog) { michael@0: rv = AddText(aData, aLength); michael@0: } michael@0: return aInterruptable && NS_SUCCEEDED(rv) ? DidProcessATokenImpl() : rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXMLContentSink::HandleProcessingInstruction(const char16_t *aTarget, michael@0: const char16_t *aData) michael@0: { michael@0: FlushText(); michael@0: michael@0: const nsDependentString target(aTarget); michael@0: const nsDependentString data(aData); michael@0: michael@0: nsCOMPtr node = michael@0: NS_NewXMLProcessingInstruction(mNodeInfoManager, target, data); michael@0: michael@0: nsCOMPtr ssle(do_QueryInterface(node)); michael@0: if (ssle) { michael@0: ssle->InitStyleLinkElement(false); michael@0: ssle->SetEnableUpdates(false); michael@0: mPrettyPrintXML = false; michael@0: } michael@0: michael@0: nsresult rv = AddContentAsLeaf(node); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: DidAddContent(); michael@0: michael@0: if (ssle) { michael@0: // This is an xml-stylesheet processing instruction... but it might not be michael@0: // a CSS one if the type is set to something else. michael@0: ssle->SetEnableUpdates(true); michael@0: bool willNotify; michael@0: bool isAlternate; michael@0: rv = ssle->UpdateStyleSheet(mRunsToCompletion ? nullptr : this, michael@0: &willNotify, michael@0: &isAlternate); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (willNotify) { michael@0: // Successfully started a stylesheet load michael@0: if (!isAlternate && !mRunsToCompletion) { michael@0: ++mPendingSheetCount; michael@0: mScriptLoader->AddExecuteBlocker(); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: // If it's not a CSS stylesheet PI... michael@0: nsAutoString type; michael@0: nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::type, type); michael@0: michael@0: if (mState != eXMLContentSinkState_InProlog || michael@0: !target.EqualsLiteral("xml-stylesheet") || michael@0: type.IsEmpty() || michael@0: type.LowerCaseEqualsLiteral("text/css")) { michael@0: return DidProcessATokenImpl(); michael@0: } michael@0: michael@0: nsAutoString href, title, media; michael@0: bool isAlternate = false; michael@0: michael@0: // If there was no href, we can't do anything with this PI michael@0: if (!ParsePIData(data, href, title, media, isAlternate)) { michael@0: return DidProcessATokenImpl(); michael@0: } michael@0: michael@0: rv = ProcessStyleLink(node, href, isAlternate, title, type, media); michael@0: return NS_SUCCEEDED(rv) ? DidProcessATokenImpl() : rv; michael@0: } michael@0: michael@0: /* static */ michael@0: bool michael@0: nsXMLContentSink::ParsePIData(const nsString &aData, nsString &aHref, michael@0: nsString &aTitle, nsString &aMedia, michael@0: bool &aIsAlternate) michael@0: { michael@0: // If there was no href, we can't do anything with this PI michael@0: if (!nsContentUtils::GetPseudoAttributeValue(aData, nsGkAtoms::href, aHref)) { michael@0: return false; michael@0: } michael@0: michael@0: nsContentUtils::GetPseudoAttributeValue(aData, nsGkAtoms::title, aTitle); michael@0: michael@0: nsContentUtils::GetPseudoAttributeValue(aData, nsGkAtoms::media, aMedia); michael@0: michael@0: nsAutoString alternate; michael@0: nsContentUtils::GetPseudoAttributeValue(aData, michael@0: nsGkAtoms::alternate, michael@0: alternate); michael@0: michael@0: aIsAlternate = alternate.EqualsLiteral("yes"); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXMLContentSink::HandleXMLDeclaration(const char16_t *aVersion, michael@0: const char16_t *aEncoding, michael@0: int32_t aStandalone) michael@0: { michael@0: mDocument->SetXMLDeclaration(aVersion, aEncoding, aStandalone); michael@0: michael@0: return DidProcessATokenImpl(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXMLContentSink::ReportError(const char16_t* aErrorText, michael@0: const char16_t* aSourceText, michael@0: nsIScriptError *aError, michael@0: bool *_retval) michael@0: { michael@0: NS_PRECONDITION(aError && aSourceText && aErrorText, "Check arguments!!!"); michael@0: nsresult rv = NS_OK; michael@0: michael@0: // The expat driver should report the error. We're just cleaning up the mess. michael@0: *_retval = true; michael@0: michael@0: mPrettyPrintXML = false; michael@0: michael@0: mState = eXMLContentSinkState_InProlog; michael@0: michael@0: // XXX need to stop scripts here -- hsivonen michael@0: michael@0: // stop observing in order to avoid crashing when removing content michael@0: mDocument->RemoveObserver(this); michael@0: mIsDocumentObserver = false; michael@0: michael@0: // Clear the current content and michael@0: // prepare to set as the document root michael@0: nsCOMPtr node(do_QueryInterface(mDocument)); michael@0: if (node) { michael@0: for (;;) { michael@0: nsCOMPtr child, dummy; michael@0: node->GetLastChild(getter_AddRefs(child)); michael@0: if (!child) michael@0: break; michael@0: node->RemoveChild(child, getter_AddRefs(dummy)); michael@0: } michael@0: } michael@0: mDocElement = nullptr; michael@0: michael@0: // Clear any buffered-up text we have. It's enough to set the length to 0. michael@0: // The buffer itself is allocated when we're created and deleted in our michael@0: // destructor, so don't mess with it. michael@0: mTextLength = 0; michael@0: michael@0: if (mXSLTProcessor) { michael@0: // Get rid of the XSLT processor. michael@0: mXSLTProcessor->CancelLoads(); michael@0: mXSLTProcessor = nullptr; michael@0: } michael@0: michael@0: // release the nodes on stack michael@0: mContentStack.Clear(); michael@0: mNotifyLevel = 0; michael@0: michael@0: rv = HandleProcessingInstruction(MOZ_UTF16("xml-stylesheet"), michael@0: MOZ_UTF16("href=\"chrome://global/locale/intl.css\" type=\"text/css\"")); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: const char16_t* noAtts[] = { 0, 0 }; michael@0: michael@0: NS_NAMED_LITERAL_STRING(errorNs, michael@0: "http://www.mozilla.org/newlayout/xml/parsererror.xml"); michael@0: michael@0: nsAutoString parsererror(errorNs); michael@0: parsererror.Append((char16_t)0xFFFF); michael@0: parsererror.AppendLiteral("parsererror"); michael@0: michael@0: rv = HandleStartElement(parsererror.get(), noAtts, 0, -1, (uint32_t)-1, michael@0: false); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = HandleCharacterData(aErrorText, NS_strlen(aErrorText), false); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsAutoString sourcetext(errorNs); michael@0: sourcetext.Append((char16_t)0xFFFF); michael@0: sourcetext.AppendLiteral("sourcetext"); michael@0: michael@0: rv = HandleStartElement(sourcetext.get(), noAtts, 0, -1, (uint32_t)-1, michael@0: false); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = HandleCharacterData(aSourceText, NS_strlen(aSourceText), false); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = HandleEndElement(sourcetext.get(), false); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = HandleEndElement(parsererror.get(), false); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: FlushTags(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsXMLContentSink::AddAttributes(const char16_t** aAtts, michael@0: nsIContent* aContent) michael@0: { michael@0: // Add tag attributes to the content attributes michael@0: nsCOMPtr prefix, localName; michael@0: while (*aAtts) { michael@0: int32_t nameSpaceID; michael@0: nsContentUtils::SplitExpatName(aAtts[0], getter_AddRefs(prefix), michael@0: getter_AddRefs(localName), &nameSpaceID); michael@0: michael@0: // Add attribute to content michael@0: aContent->SetAttr(nameSpaceID, localName, prefix, michael@0: nsDependentString(aAtts[1]), false); michael@0: aAtts += 2; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: #define NS_ACCUMULATION_BUFFER_SIZE 4096 michael@0: michael@0: nsresult michael@0: nsXMLContentSink::AddText(const char16_t* aText, michael@0: int32_t aLength) michael@0: { michael@0: // Create buffer when we first need it michael@0: if (0 == mTextSize) { michael@0: mText = (char16_t *) PR_MALLOC(sizeof(char16_t) * NS_ACCUMULATION_BUFFER_SIZE); michael@0: if (nullptr == mText) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: mTextSize = NS_ACCUMULATION_BUFFER_SIZE; michael@0: } michael@0: michael@0: // Copy data from string into our buffer; flush buffer when it fills up michael@0: int32_t offset = 0; michael@0: while (0 != aLength) { michael@0: int32_t amount = mTextSize - mTextLength; michael@0: if (0 == amount) { michael@0: // XSLT wants adjacent textnodes merged. michael@0: if (mConstrainSize && !mXSLTProcessor) { michael@0: nsresult rv = FlushText(); michael@0: if (NS_OK != rv) { michael@0: return rv; michael@0: } michael@0: michael@0: amount = mTextSize - mTextLength; michael@0: } michael@0: else { michael@0: mTextSize += aLength; michael@0: mText = (char16_t *) PR_REALLOC(mText, sizeof(char16_t) * mTextSize); michael@0: if (nullptr == mText) { michael@0: mTextSize = 0; michael@0: michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: amount = aLength; michael@0: } michael@0: } michael@0: if (amount > aLength) { michael@0: amount = aLength; michael@0: } michael@0: memcpy(&mText[mTextLength], &aText[offset], sizeof(char16_t) * amount); michael@0: mTextLength += amount; michael@0: offset += amount; michael@0: aLength -= amount; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsXMLContentSink::FlushPendingNotifications(mozFlushType aType) michael@0: { michael@0: // Only flush tags if we're not doing the notification ourselves michael@0: // (since we aren't reentrant) michael@0: if (!mInNotification) { michael@0: if (mIsDocumentObserver) { michael@0: // Only flush if we're still a document observer (so that our child michael@0: // counts should be correct). michael@0: if (aType >= Flush_ContentAndNotify) { michael@0: FlushTags(); michael@0: } michael@0: else { michael@0: FlushText(false); michael@0: } michael@0: } michael@0: if (aType >= Flush_InterruptibleLayout) { michael@0: // Make sure that layout has started so that the reflow flush michael@0: // will actually happen. michael@0: MaybeStartLayout(true); michael@0: } michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * NOTE!! Forked from SinkContext. Please keep in sync. michael@0: * michael@0: * Flush all elements that have been seen so far such that michael@0: * they are visible in the tree. Specifically, make sure michael@0: * that they are all added to their respective parents. michael@0: * Also, do notification at the top for all content that michael@0: * has been newly added so that the frame tree is complete. michael@0: */ michael@0: nsresult michael@0: nsXMLContentSink::FlushTags() michael@0: { michael@0: mDeferredFlushTags = false; michael@0: bool oldBeganUpdate = mBeganUpdate; michael@0: uint32_t oldUpdates = mUpdatesInNotification; michael@0: michael@0: mUpdatesInNotification = 0; michael@0: ++mInNotification; michael@0: { michael@0: // Scope so we call EndUpdate before we decrease mInNotification michael@0: mozAutoDocUpdate updateBatch(mDocument, UPDATE_CONTENT_MODEL, true); michael@0: mBeganUpdate = true; michael@0: michael@0: // Don't release last text node in case we need to add to it again michael@0: FlushText(false); michael@0: michael@0: // Start from the base of the stack (growing downward) and do michael@0: // a notification from the node that is closest to the root of michael@0: // tree for any content that has been added. michael@0: michael@0: int32_t stackPos; michael@0: int32_t stackLen = mContentStack.Length(); michael@0: bool flushed = false; michael@0: uint32_t childCount; michael@0: nsIContent* content; michael@0: michael@0: for (stackPos = 0; stackPos < stackLen; ++stackPos) { michael@0: content = mContentStack[stackPos].mContent; michael@0: childCount = content->GetChildCount(); michael@0: michael@0: if (!flushed && (mContentStack[stackPos].mNumFlushed < childCount)) { michael@0: NotifyAppend(content, mContentStack[stackPos].mNumFlushed); michael@0: flushed = true; michael@0: } michael@0: michael@0: mContentStack[stackPos].mNumFlushed = childCount; michael@0: } michael@0: mNotifyLevel = stackLen - 1; michael@0: } michael@0: --mInNotification; michael@0: michael@0: if (mUpdatesInNotification > 1) { michael@0: UpdateChildCounts(); michael@0: } michael@0: michael@0: mUpdatesInNotification = oldUpdates; michael@0: mBeganUpdate = oldBeganUpdate; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /** michael@0: * NOTE!! Forked from SinkContext. Please keep in sync. michael@0: */ michael@0: void michael@0: nsXMLContentSink::UpdateChildCounts() michael@0: { michael@0: // Start from the top of the stack (growing upwards) and see if any michael@0: // new content has been appended. If so, we recognize that reflows michael@0: // have been generated for it and we should make sure that no michael@0: // further reflows occur. Note that we have to include stackPos == 0 michael@0: // to properly notify on kids of . michael@0: int32_t stackLen = mContentStack.Length(); michael@0: int32_t stackPos = stackLen - 1; michael@0: while (stackPos >= 0) { michael@0: StackNode & node = mContentStack[stackPos]; michael@0: node.mNumFlushed = node.mContent->GetChildCount(); michael@0: michael@0: stackPos--; michael@0: } michael@0: mNotifyLevel = stackLen - 1; michael@0: } michael@0: michael@0: bool michael@0: nsXMLContentSink::IsMonolithicContainer(nsINodeInfo* aNodeInfo) michael@0: { michael@0: return ((aNodeInfo->NamespaceID() == kNameSpaceID_XHTML && michael@0: (aNodeInfo->NameAtom() == nsGkAtoms::tr || michael@0: aNodeInfo->NameAtom() == nsGkAtoms::select || michael@0: aNodeInfo->NameAtom() == nsGkAtoms::object || michael@0: aNodeInfo->NameAtom() == nsGkAtoms::applet)) || michael@0: (aNodeInfo->NamespaceID() == kNameSpaceID_MathML && michael@0: (aNodeInfo->NameAtom() == nsGkAtoms::math)) michael@0: ); michael@0: } michael@0: michael@0: void michael@0: nsXMLContentSink::ContinueInterruptedParsingIfEnabled() michael@0: { michael@0: if (mParser && mParser->IsParserEnabled()) { michael@0: GetParser()->ContinueInterruptedParsing(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsXMLContentSink::ContinueInterruptedParsingAsync() michael@0: { michael@0: nsCOMPtr ev = NS_NewRunnableMethod(this, michael@0: &nsXMLContentSink::ContinueInterruptedParsingIfEnabled); michael@0: michael@0: NS_DispatchToCurrentThread(ev); michael@0: } michael@0: michael@0: nsIParser* michael@0: nsXMLContentSink::GetParser() michael@0: { michael@0: return static_cast(mParser.get()); michael@0: }