From 0ad400f8e10dc000e92775e55488cb7933ff81e6 Mon Sep 17 00:00:00 2001 From: Henri Sivonen Date: Wed, 5 Mar 2014 21:38:50 +0200 Subject: [PATCH] Bug 959150 part 4 - Avoid using tree ops when parsing with nsHtml5StringParser. r=smaug. --- parser/html/javasrc/TreeBuilder.java | 17 +- parser/html/moz.build | 2 + parser/html/nsHtml5DocumentBuilder.cpp | 133 ++++++- parser/html/nsHtml5DocumentBuilder.h | 72 +++- parser/html/nsHtml5OplessBuilder.cpp | 49 +++ parser/html/nsHtml5OplessBuilder.h | 26 ++ parser/html/nsHtml5StringParser.cpp | 46 +-- parser/html/nsHtml5StringParser.h | 10 +- parser/html/nsHtml5TreeBuilder.cpp | 12 +- parser/html/nsHtml5TreeBuilder.h | 1 + parser/html/nsHtml5TreeBuilderCppSupplement.h | 330 ++++++++++++++++-- parser/html/nsHtml5TreeBuilderHSupplement.h | 14 +- parser/html/nsHtml5TreeOpExecutor.cpp | 129 +------ parser/html/nsHtml5TreeOpExecutor.h | 75 +--- parser/html/nsHtml5TreeOperation.cpp | 18 +- parser/html/nsHtml5TreeOperation.h | 3 +- 16 files changed, 636 insertions(+), 301 deletions(-) create mode 100644 parser/html/nsHtml5OplessBuilder.cpp create mode 100644 parser/html/nsHtml5OplessBuilder.h diff --git a/parser/html/javasrc/TreeBuilder.java b/parser/html/javasrc/TreeBuilder.java index 05e5a393b430..6e85ef47280f 100644 --- a/parser/html/javasrc/TreeBuilder.java +++ b/parser/html/javasrc/TreeBuilder.java @@ -5137,6 +5137,9 @@ public abstract class TreeBuilder implements TokenHandler, checkAttributes(attributes, "http://www.w3.org/1999/xhtml"); // ]NOCPP] // This method can't be called for custom elements + HtmlAttributes clone = attributes.cloneAttributes(null); + // Attributes must not be read after calling createElement, because + // createElement may delete attributes in C++. T elt = createElement("http://www.w3.org/1999/xhtml", elementName.name, attributes); StackNode current = stack[currentPtr]; if (current.isFosterParenting()) { @@ -5145,7 +5148,7 @@ public abstract class TreeBuilder implements TokenHandler, } else { appendElement(elt, current.node); } - StackNode node = new StackNode(elementName, elt, attributes.cloneAttributes(null) + StackNode node = new StackNode(elementName, elt, clone // [NOCPP[ , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer) // ]NOCPP] @@ -5211,6 +5214,13 @@ public abstract class TreeBuilder implements TokenHandler, popName = checkPopName(popName); } // ]NOCPP] + boolean markAsHtmlIntegrationPoint = false; + if (ElementName.ANNOTATION_XML == elementName + && annotationXmlEncodingPermitsHtml(attributes)) { + markAsHtmlIntegrationPoint = true; + } + // Attributes must not be read after calling createElement(), since + // createElement may delete the object in C++. T elt = createElement("http://www.w3.org/1998/Math/MathML", popName, attributes); StackNode current = stack[currentPtr]; @@ -5220,11 +5230,6 @@ public abstract class TreeBuilder implements TokenHandler, } else { appendElement(elt, current.node); } - boolean markAsHtmlIntegrationPoint = false; - if (ElementName.ANNOTATION_XML == elementName - && annotationXmlEncodingPermitsHtml(attributes)) { - markAsHtmlIntegrationPoint = true; - } StackNode node = new StackNode(elementName, elt, popName, markAsHtmlIntegrationPoint // [NOCPP[ diff --git a/parser/html/moz.build b/parser/html/moz.build index 9b034358f0d1..df6dcf0bf3dc 100644 --- a/parser/html/moz.build +++ b/parser/html/moz.build @@ -30,6 +30,7 @@ EXPORTS += [ 'nsHtml5Module.h', 'nsHtml5NamedCharacters.h', 'nsHtml5NamedCharactersAccel.h', + 'nsHtml5OplessBuilder.h', 'nsHtml5OwningUTF16Buffer.h', 'nsHtml5Parser.h', 'nsHtml5PendingNotification.h', @@ -63,6 +64,7 @@ UNIFIED_SOURCES += [ 'nsHtml5Module.cpp', 'nsHtml5NamedCharacters.cpp', 'nsHtml5NamedCharactersAccel.cpp', + 'nsHtml5OplessBuilder.cpp', 'nsHtml5OwningUTF16Buffer.cpp', 'nsHtml5Parser.cpp', 'nsHtml5PlainTextUtils.cpp', diff --git a/parser/html/nsHtml5DocumentBuilder.cpp b/parser/html/nsHtml5DocumentBuilder.cpp index 5175085f82a5..e1639089e4b0 100644 --- a/parser/html/nsHtml5DocumentBuilder.cpp +++ b/parser/html/nsHtml5DocumentBuilder.cpp @@ -6,9 +6,10 @@ #include "nsHtml5DocumentBuilder.h" +#include "nsIStyleSheetLinkingElement.h" +#include "nsStyleLinkElement.h" #include "nsScriptLoader.h" -#include "mozilla/css/Loader.h" -#include "nsIDocShell.h" +#include "nsIHTMLDocument.h" NS_IMPL_CYCLE_COLLECTION_INHERITED_1(nsHtml5DocumentBuilder, nsContentSink, mOwnedElements) @@ -19,15 +20,125 @@ NS_INTERFACE_MAP_END_INHERITING(nsContentSink) NS_IMPL_ADDREF_INHERITED(nsHtml5DocumentBuilder, nsContentSink) NS_IMPL_RELEASE_INHERITED(nsHtml5DocumentBuilder, nsContentSink) -void -nsHtml5DocumentBuilder::DropHeldElements() +nsHtml5DocumentBuilder::nsHtml5DocumentBuilder(bool aRunsToCompletion) { - mScriptLoader = nullptr; - mDocument = nullptr; - mNodeInfoManager = nullptr; - mCSSLoader = nullptr; - mDocumentURI = nullptr; - mDocShell = nullptr; - mOwnedElements.Clear(); + mRunsToCompletion = aRunsToCompletion; } +nsresult +nsHtml5DocumentBuilder::Init(nsIDocument* aDoc, + nsIURI* aURI, + nsISupports* aContainer, + nsIChannel* aChannel) +{ + return nsContentSink::Init(aDoc, aURI, aContainer, aChannel); +} + +nsHtml5DocumentBuilder::~nsHtml5DocumentBuilder() +{ +} + +nsresult +nsHtml5DocumentBuilder::MarkAsBroken(nsresult aReason) +{ + mBroken = aReason; + return aReason; +} + +void +nsHtml5DocumentBuilder::SetDocumentCharsetAndSource(nsACString& aCharset, int32_t aCharsetSource) +{ + if (mDocument) { + mDocument->SetDocumentCharacterSetSource(aCharsetSource); + mDocument->SetDocumentCharacterSet(aCharset); + } +} + +void +nsHtml5DocumentBuilder::UpdateStyleSheet(nsIContent* aElement) +{ + // Break out of the doc update created by Flush() to zap a runnable + // waiting to call UpdateStyleSheet without the right observer + EndDocUpdate(); + + if (MOZ_UNLIKELY(!mParser)) { + // EndDocUpdate ran stuff that called nsIParser::Terminate() + return; + } + + nsCOMPtr ssle(do_QueryInterface(aElement)); + NS_ASSERTION(ssle, "Node didn't QI to style."); + + ssle->SetEnableUpdates(true); + + bool willNotify; + bool isAlternate; + nsresult rv = ssle->UpdateStyleSheet(mRunsToCompletion ? nullptr : this, + &willNotify, + &isAlternate); + if (NS_SUCCEEDED(rv) && willNotify && !isAlternate && !mRunsToCompletion) { + ++mPendingSheetCount; + mScriptLoader->AddExecuteBlocker(); + } + + if (aElement->IsHTML(nsGkAtoms::link)) { + // look for + nsAutoString relVal; + aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::rel, relVal); + if (!relVal.IsEmpty()) { + uint32_t linkTypes = nsStyleLinkElement::ParseLinkTypes(relVal); + bool hasPrefetch = linkTypes & nsStyleLinkElement::ePREFETCH; + if (hasPrefetch || (linkTypes & nsStyleLinkElement::eNEXT)) { + nsAutoString hrefVal; + aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::href, hrefVal); + if (!hrefVal.IsEmpty()) { + PrefetchHref(hrefVal, aElement, hasPrefetch); + } + } + if (linkTypes & nsStyleLinkElement::eDNS_PREFETCH) { + nsAutoString hrefVal; + aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::href, hrefVal); + if (!hrefVal.IsEmpty()) { + PrefetchDNS(hrefVal); + } + } + } + } + + // Re-open update + BeginDocUpdate(); +} + +void +nsHtml5DocumentBuilder::SetDocumentMode(nsHtml5DocumentMode m) +{ + nsCompatibility mode = eCompatibility_NavQuirks; + switch (m) { + case STANDARDS_MODE: + mode = eCompatibility_FullStandards; + break; + case ALMOST_STANDARDS_MODE: + mode = eCompatibility_AlmostStandards; + break; + case QUIRKS_MODE: + mode = eCompatibility_NavQuirks; + break; + } + nsCOMPtr htmlDocument = do_QueryInterface(mDocument); + NS_ASSERTION(htmlDocument, "Document didn't QI into HTML document."); + htmlDocument->SetCompatibilityMode(mode); +} + +// nsContentSink overrides + +void +nsHtml5DocumentBuilder::UpdateChildCounts() +{ + // No-op +} + +nsresult +nsHtml5DocumentBuilder::FlushTags() +{ + return NS_OK; +} diff --git a/parser/html/nsHtml5DocumentBuilder.h b/parser/html/nsHtml5DocumentBuilder.h index f16f0dbeaf6e..99c11c575201 100644 --- a/parser/html/nsHtml5DocumentBuilder.h +++ b/parser/html/nsHtml5DocumentBuilder.h @@ -8,6 +8,9 @@ #define nsHtml5DocumentBuilder_h #include "nsHtml5PendingNotification.h" +#include "nsContentSink.h" +#include "nsHtml5DocumentMode.h" +#include "nsIDocument.h" typedef nsIContent* nsIContentPtr; @@ -92,7 +95,8 @@ public: mFlushState = eInDocUpdate; } - void DropHeldElements(); + nsresult Init(nsIDocument* aDoc, nsIURI* aURI, + nsISupports* aContainer, nsIChannel* aChannel); // Getters and setters for fields from nsContentSink nsIDocument* GetDocument() { @@ -102,7 +106,54 @@ public: return mNodeInfoManager; } - virtual bool BelongsToStringParser() = 0; + /** + * Marks this parser as broken and tells the stream parser (if any) to + * terminate. + * + * @return aReason for convenience + */ + virtual nsresult MarkAsBroken(nsresult aReason); + + /** + * Checks if this parser is broken. Returns a non-NS_OK (i.e. non-0) + * value if broken. + */ + inline nsresult IsBroken() { + return mBroken; + } + + inline void BeginDocUpdate() { + NS_PRECONDITION(mFlushState == eInFlush, "Tried to double-open update."); + NS_PRECONDITION(mParser, "Started update without parser."); + mFlushState = eInDocUpdate; + mDocument->BeginUpdate(UPDATE_CONTENT_MODEL); + } + + inline void EndDocUpdate() { + NS_PRECONDITION(mFlushState != eNotifying, "mFlushState out of sync"); + if (mFlushState == eInDocUpdate) { + FlushPendingAppendNotifications(); + mFlushState = eInFlush; + mDocument->EndUpdate(UPDATE_CONTENT_MODEL); + } + } + + void SetDocumentCharsetAndSource(nsACString& aCharset, int32_t aCharsetSource); + + /** + * Sets up style sheet load / parse + */ + void UpdateStyleSheet(nsIContent* aElement); + + void SetDocumentMode(nsHtml5DocumentMode m); + + void SetNodeInfoManager(nsNodeInfoManager* aManager) { + mNodeInfoManager = aManager; + } + + // nsContentSink methods + virtual void UpdateChildCounts(); + virtual nsresult FlushTags(); protected: inline void SetAppendBatchCapacity(uint32_t aCapacity) @@ -110,11 +161,26 @@ protected: mElementsSeenInThisAppendBatch.SetCapacity(aCapacity); } + nsHtml5DocumentBuilder(bool aRunsToCompletion); + virtual ~nsHtml5DocumentBuilder(); + private: nsTArray mPendingNotifications; - nsTArray > mOwnedElements; nsTArray mElementsSeenInThisAppendBatch; protected: + nsTArray > mOwnedElements; + /** + * Non-NS_OK if this parser should refuse to process any more input. + * For example, the parser needs to be marked as broken if it drops some + * input due to a memory allocation failure. In such a case, the whole + * parser needs to be marked as broken, because some input has been lost + * and parsing more input could lead to a DOM where pieces of HTML source + * that weren't supposed to become scripts become scripts. + * + * Since NS_OK is actually 0, zeroing operator new takes care of + * initializing this. + */ + nsresult mBroken; eHtml5FlushState mFlushState; }; diff --git a/parser/html/nsHtml5OplessBuilder.cpp b/parser/html/nsHtml5OplessBuilder.cpp new file mode 100644 index 000000000000..ac1c03f10682 --- /dev/null +++ b/parser/html/nsHtml5OplessBuilder.cpp @@ -0,0 +1,49 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 sw=2 et tw=78: */ +/* 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 "nsHtml5OplessBuilder.h" + +#include "nsScriptLoader.h" +#include "mozilla/css/Loader.h" +#include "nsIDocShell.h" +#include "nsIHTMLDocument.h" + +nsHtml5OplessBuilder::nsHtml5OplessBuilder() + : nsHtml5DocumentBuilder(true) +{ +} + +nsHtml5OplessBuilder::~nsHtml5OplessBuilder() +{ +} + +void +nsHtml5OplessBuilder::Start() +{ + mFlushState = eInFlush; + BeginDocUpdate(); +} + +void +nsHtml5OplessBuilder::Finish() +{ + EndDocUpdate(); + DropParserAndPerfHint(); + mScriptLoader = nullptr; + mDocument = nullptr; + mNodeInfoManager = nullptr; + mCSSLoader = nullptr; + mDocumentURI = nullptr; + mDocShell = nullptr; + mOwnedElements.Clear(); + mFlushState = eNotFlushing; +} + +void +nsHtml5OplessBuilder::SetParser(nsParserBase* aParser) +{ + mParser = aParser; +} diff --git a/parser/html/nsHtml5OplessBuilder.h b/parser/html/nsHtml5OplessBuilder.h new file mode 100644 index 000000000000..c1d913e175c7 --- /dev/null +++ b/parser/html/nsHtml5OplessBuilder.h @@ -0,0 +1,26 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 sw=2 et tw=78: */ +/* 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/. */ + +#ifndef nsHtml5OplessBuilder_h +#define nsHtml5OplessBuilder_h + +#include "nsHtml5DocumentBuilder.h" + +class nsParserBase; + +class nsHtml5OplessBuilder : public nsHtml5DocumentBuilder +{ +public: + NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW + + nsHtml5OplessBuilder(); + ~nsHtml5OplessBuilder(); + void Start(); + void Finish(); + void SetParser(nsParserBase* aParser); +}; + +#endif // nsHtml5OplessBuilder_h diff --git a/parser/html/nsHtml5StringParser.cpp b/parser/html/nsHtml5StringParser.cpp index 35032ae7b62a..70f63571af59 100644 --- a/parser/html/nsHtml5StringParser.cpp +++ b/parser/html/nsHtml5StringParser.cpp @@ -14,8 +14,8 @@ NS_IMPL_ISUPPORTS0(nsHtml5StringParser) nsHtml5StringParser::nsHtml5StringParser() - : mExecutor(new nsHtml5TreeOpExecutor(true)) - , mTreeBuilder(new nsHtml5TreeBuilder(mExecutor, nullptr)) + : mBuilder(new nsHtml5OplessBuilder()) + , mTreeBuilder(new nsHtml5TreeBuilder(mBuilder)) , mTokenizer(new nsHtml5Tokenizer(mTreeBuilder, false)) { MOZ_COUNT_CTOR(nsHtml5StringParser); @@ -42,10 +42,9 @@ nsHtml5StringParser::ParseFragment(const nsAString& aSourceBuffer, nsIURI* uri = doc->GetDocumentURI(); NS_ENSURE_TRUE(uri, NS_ERROR_NOT_AVAILABLE); - nsIContent* target = aTargetNode; mTreeBuilder->setFragmentContext(aContextLocalName, aContextNamespace, - &target, + aTargetNode, aQuirks); #ifdef DEBUG @@ -61,8 +60,7 @@ nsHtml5StringParser::ParseFragment(const nsAString& aSourceBuffer, mTreeBuilder->SetPreventScriptExecution(aPreventScriptExecution); - Tokenize(aSourceBuffer, doc, true); - return NS_OK; + return Tokenize(aSourceBuffer, doc, true); } nsresult @@ -82,28 +80,28 @@ nsHtml5StringParser::ParseDocument(const nsAString& aSourceBuffer, mTreeBuilder->SetPreventScriptExecution(true); - Tokenize(aSourceBuffer, aTargetDoc, aScriptingEnabledForNoscriptParsing); - return NS_OK; + return Tokenize(aSourceBuffer, aTargetDoc, aScriptingEnabledForNoscriptParsing); } -void +nsresult nsHtml5StringParser::Tokenize(const nsAString& aSourceBuffer, nsIDocument* aDocument, bool aScriptingEnabledForNoscriptParsing) { nsIURI* uri = aDocument->GetDocumentURI(); - mExecutor->Init(aDocument, uri, nullptr, nullptr); + mBuilder->Init(aDocument, uri, nullptr, nullptr); - mExecutor->SetParser(this); - mExecutor->SetNodeInfoManager(aDocument->NodeInfoManager()); + mBuilder->SetParser(this); + mBuilder->SetNodeInfoManager(aDocument->NodeInfoManager()); + + // Mark the parser as *not* broken by passing NS_OK + nsresult rv = mBuilder->MarkAsBroken(NS_OK); - NS_PRECONDITION(!mExecutor->HasStarted(), - "Tried to start parse without initializing the parser."); mTreeBuilder->setScriptingEnabled(aScriptingEnabledForNoscriptParsing); mTreeBuilder->setIsSrcdocDocument(aDocument->IsSrcdocDocument()); + mBuilder->Start(); mTokenizer->start(); - mExecutor->Start(); // Don't call WillBuildModel in fragment case if (!aSourceBuffer.IsEmpty()) { bool lastWasCR = false; nsHtml5DependentUTF16Buffer buffer(aSourceBuffer); @@ -112,25 +110,15 @@ nsHtml5StringParser::Tokenize(const nsAString& aSourceBuffer, lastWasCR = false; if (buffer.hasMore()) { lastWasCR = mTokenizer->tokenizeBuffer(&buffer); - if (mTreeBuilder->HasScript()) { - // If we come here, we are in createContextualFragment() or in the - // upcoming document.parse(). It's unclear if it's really necessary - // to flush here, but let's do so for consistency with other flushes - // to avoid different code paths on the executor side. - mTreeBuilder->Flush(); // Move ops to the executor - mExecutor->FlushDocumentWrite(); // run the ops + if (NS_FAILED(rv = mBuilder->IsBroken())) { + break; } } } } mTokenizer->eof(); - mTreeBuilder->StreamEnded(); - mTreeBuilder->Flush(); - mExecutor->FlushDocumentWrite(); mTokenizer->end(); - mExecutor->DropParserAndPerfHint(); - mExecutor->DropHeldElements(); - mTreeBuilder->DropHandles(); + mBuilder->Finish(); mAtomTable.Clear(); - mExecutor->Reset(); + return rv; } diff --git a/parser/html/nsHtml5StringParser.h b/parser/html/nsHtml5StringParser.h index 6048193a872b..00210cbaad42 100644 --- a/parser/html/nsHtml5StringParser.h +++ b/parser/html/nsHtml5StringParser.h @@ -8,7 +8,7 @@ #include "nsHtml5AtomTable.h" #include "nsParserBase.h" -class nsHtml5TreeOpExecutor; +class nsHtml5OplessBuilder; class nsHtml5TreeBuilder; class nsHtml5Tokenizer; class nsIContent; @@ -58,14 +58,14 @@ class nsHtml5StringParser : public nsParserBase private: - void Tokenize(const nsAString& aSourceBuffer, - nsIDocument* aDocument, - bool aScriptingEnabledForNoscriptParsing); + nsresult Tokenize(const nsAString& aSourceBuffer, + nsIDocument* aDocument, + bool aScriptingEnabledForNoscriptParsing); /** * The tree operation executor */ - nsRefPtr mExecutor; + nsRefPtr mBuilder; /** * The HTML5 tree builder diff --git a/parser/html/nsHtml5TreeBuilder.cpp b/parser/html/nsHtml5TreeBuilder.cpp index 200457d32e34..86e3e473c94e 100644 --- a/parser/html/nsHtml5TreeBuilder.cpp +++ b/parser/html/nsHtml5TreeBuilder.cpp @@ -54,6 +54,7 @@ #include "nsHtml5ViewSourceUtils.h" #include "mozilla/Likely.h" #include "nsIContentHandle.h" +#include "nsHtml5OplessBuilder.h" #include "nsHtml5Tokenizer.h" #include "nsHtml5MetaScanner.h" @@ -3899,6 +3900,7 @@ nsHtml5TreeBuilder::appendToCurrentNodeAndPushFormElementMayFoster(nsHtml5HtmlAt void nsHtml5TreeBuilder::appendToCurrentNodeAndPushFormattingElementMayFoster(nsHtml5ElementName* elementName, nsHtml5HtmlAttributes* attributes) { + nsHtml5HtmlAttributes* clone = attributes->cloneAttributes(nullptr); nsIContentHandle* elt = createElement(kNameSpaceID_XHTML, elementName->name, attributes); nsHtml5StackNode* current = stack[currentPtr]; if (current->isFosterParenting()) { @@ -3907,7 +3909,7 @@ nsHtml5TreeBuilder::appendToCurrentNodeAndPushFormattingElementMayFoster(nsHtml5 } else { appendElement(elt, current->node); } - nsHtml5StackNode* node = new nsHtml5StackNode(elementName, elt, attributes->cloneAttributes(nullptr)); + nsHtml5StackNode* node = new nsHtml5StackNode(elementName, elt, clone); push(node); append(node); node->retain(); @@ -3945,6 +3947,10 @@ void nsHtml5TreeBuilder::appendToCurrentNodeAndPushElementMayFosterMathML(nsHtml5ElementName* elementName, nsHtml5HtmlAttributes* attributes) { nsIAtom* popName = elementName->name; + bool markAsHtmlIntegrationPoint = false; + if (nsHtml5ElementName::ELT_ANNOTATION_XML == elementName && annotationXmlEncodingPermitsHtml(attributes)) { + markAsHtmlIntegrationPoint = true; + } nsIContentHandle* elt = createElement(kNameSpaceID_MathML, popName, attributes); nsHtml5StackNode* current = stack[currentPtr]; if (current->isFosterParenting()) { @@ -3953,10 +3959,6 @@ nsHtml5TreeBuilder::appendToCurrentNodeAndPushElementMayFosterMathML(nsHtml5Elem } else { appendElement(elt, current->node); } - bool markAsHtmlIntegrationPoint = false; - if (nsHtml5ElementName::ELT_ANNOTATION_XML == elementName && annotationXmlEncodingPermitsHtml(attributes)) { - markAsHtmlIntegrationPoint = true; - } nsHtml5StackNode* node = new nsHtml5StackNode(elementName, elt, popName, markAsHtmlIntegrationPoint); push(node); } diff --git a/parser/html/nsHtml5TreeBuilder.h b/parser/html/nsHtml5TreeBuilder.h index 174d2cb10b7b..621a207766b1 100644 --- a/parser/html/nsHtml5TreeBuilder.h +++ b/parser/html/nsHtml5TreeBuilder.h @@ -55,6 +55,7 @@ #include "nsHtml5ViewSourceUtils.h" #include "mozilla/Likely.h" #include "nsIContentHandle.h" +#include "nsHtml5OplessBuilder.h" class nsHtml5StreamParser; diff --git a/parser/html/nsHtml5TreeBuilderCppSupplement.h b/parser/html/nsHtml5TreeBuilderCppSupplement.h index f192c1ab282b..b23313a46fe6 100644 --- a/parser/html/nsHtml5TreeBuilderCppSupplement.h +++ b/parser/html/nsHtml5TreeBuilderCppSupplement.h @@ -13,6 +13,27 @@ class nsPresContext; +nsHtml5TreeBuilder::nsHtml5TreeBuilder(nsHtml5OplessBuilder* aBuilder) + : scriptingEnabled(false) + , fragment(false) + , contextNode(nullptr) + , formPointer(nullptr) + , headPointer(nullptr) + , mBuilder(aBuilder) + , mViewSource(nullptr) + , mOpSink(nullptr) + , mHandles(nullptr) + , mHandlesUsed(0) + , mSpeculativeLoadStage(nullptr) + , mCurrentHtmlScriptIsAsyncOrDefer(false) + , mPreventScriptExecution(false) +#ifdef DEBUG + , mActive(false) +#endif +{ + MOZ_COUNT_CTOR(nsHtml5TreeBuilder); +} + nsHtml5TreeBuilder::nsHtml5TreeBuilder(nsAHtml5TreeOpSink* aOpSink, nsHtml5TreeOpStage* aStage) : scriptingEnabled(false) @@ -20,6 +41,7 @@ nsHtml5TreeBuilder::nsHtml5TreeBuilder(nsAHtml5TreeOpSink* aOpSink, , contextNode(nullptr) , formPointer(nullptr) , headPointer(nullptr) + , mBuilder(nullptr) , mViewSource(nullptr) , mOpSink(aOpSink) , mHandles(new nsIContent*[NS_HTML5_TREE_BUILDER_HANDLE_ARRAY_LENGTH]) @@ -51,6 +73,20 @@ nsHtml5TreeBuilder::createElement(int32_t aNamespace, nsIAtom* aName, nsHtml5Htm aNamespace == kNameSpaceID_MathML, "Bogus namespace."); + if (mBuilder) { + nsCOMPtr name = nsHtml5TreeOperation::Reget(aName); + nsIContent* elem = + nsHtml5TreeOperation::CreateElement(aNamespace, + name, + aAttributes, + mozilla::dom::FROM_PARSER_FRAGMENT, + mBuilder); + if (aAttributes != nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES) { + delete aAttributes; + } + return elem; + } + nsIContentHandle* content = AllocateContentHandle(); nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); NS_ASSERTION(treeOp, "Tree op allocation failed."); @@ -214,9 +250,14 @@ nsHtml5TreeBuilder::createElement(int32_t aNamespace, nsIAtom* aName, nsHtml5Htm { nsIContentHandle* content = createElement(aNamespace, aName, aAttributes); if (aFormElement) { - nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); - NS_ASSERTION(treeOp, "Tree op allocation failed."); - treeOp->Init(eTreeOpSetFormElement, content, aFormElement); + if (mBuilder) { + nsHtml5TreeOperation::SetFormElement(static_cast(content), + static_cast(aFormElement)); + } else { + nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); + NS_ASSERTION(treeOp, "Tree op allocation failed."); + treeOp->Init(eTreeOpSetFormElement, content, aFormElement); + } } return content; } @@ -225,9 +266,17 @@ nsIContentHandle* nsHtml5TreeBuilder::createHtmlElementSetAsRoot(nsHtml5HtmlAttributes* aAttributes) { nsIContentHandle* content = createElement(kNameSpaceID_XHTML, nsHtml5Atoms::html, aAttributes); - nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); - NS_ASSERTION(treeOp, "Tree op allocation failed."); - treeOp->Init(eTreeOpAppendToDocument, content); + if (mBuilder) { + nsresult rv = nsHtml5TreeOperation::AppendToDocument(static_cast(content), + mBuilder); + if (NS_FAILED(rv)) { + MarkAsBrokenAndRequestSuspension(rv); + } + } else { + nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); + NS_ASSERTION(treeOp, "Tree op allocation failed."); + treeOp->Init(eTreeOpAppendToDocument, content); + } return content; } @@ -236,6 +285,12 @@ nsHtml5TreeBuilder::detachFromParent(nsIContentHandle* aElement) { NS_PRECONDITION(aElement, "Null element"); + if (mBuilder) { + nsHtml5TreeOperation::Detach(static_cast(aElement), + mBuilder); + return; + } + nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); NS_ASSERTION(treeOp, "Tree op allocation failed."); treeOp->Init(eTreeOpDetach, aElement); @@ -249,6 +304,17 @@ nsHtml5TreeBuilder::appendElement(nsIContentHandle* aChild, nsIContentHandle* aP if (deepTreeSurrogateParent) { return; } + + if (mBuilder) { + nsresult rv = nsHtml5TreeOperation::Append(static_cast(aChild), + static_cast(aParent), + mBuilder); + if (NS_FAILED(rv)) { + MarkAsBrokenAndRequestSuspension(rv); + } + return; + } + nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); NS_ASSERTION(treeOp, "Tree op allocation failed."); treeOp->Init(eTreeOpAppend, aChild, aParent); @@ -260,6 +326,17 @@ nsHtml5TreeBuilder::appendChildrenToNewParent(nsIContentHandle* aOldParent, nsIC NS_PRECONDITION(aOldParent, "Null old parent"); NS_PRECONDITION(aNewParent, "Null new parent"); + if (mBuilder) { + nsresult rv = nsHtml5TreeOperation::AppendChildrenToNewParent( + static_cast(aOldParent), + static_cast(aNewParent), + mBuilder); + if (NS_FAILED(rv)) { + MarkAsBrokenAndRequestSuspension(rv); + } + return; + } + nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); NS_ASSERTION(treeOp, "Tree op allocation failed."); treeOp->Init(eTreeOpAppendChildrenToNewParent, aOldParent, aNewParent); @@ -271,6 +348,20 @@ nsHtml5TreeBuilder::insertFosterParentedCharacters(char16_t* aBuffer, int32_t aS NS_PRECONDITION(aBuffer, "Null buffer"); NS_PRECONDITION(aTable, "Null table"); NS_PRECONDITION(aStackParent, "Null stack parent"); + MOZ_ASSERT(!aStart, "aStart must always be zero."); + + if (mBuilder) { + nsresult rv = nsHtml5TreeOperation::FosterParentText( + static_cast(aStackParent), + aBuffer, // XXX aStart always ignored??? + aLength, + static_cast(aTable), + mBuilder); + if (NS_FAILED(rv)) { + MarkAsBrokenAndRequestSuspension(rv); + } + return; + } char16_t* bufferCopy = new char16_t[aLength]; memcpy(bufferCopy, aBuffer, aLength * sizeof(char16_t)); @@ -287,6 +378,18 @@ nsHtml5TreeBuilder::insertFosterParentedChild(nsIContentHandle* aChild, nsIConte NS_PRECONDITION(aTable, "Null table"); NS_PRECONDITION(aStackParent, "Null stack parent"); + if (mBuilder) { + nsresult rv = nsHtml5TreeOperation::FosterParent( + static_cast(aChild), + static_cast(aStackParent), + static_cast(aTable), + mBuilder); + if (NS_FAILED(rv)) { + MarkAsBrokenAndRequestSuspension(rv); + } + return; + } + nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); NS_ASSERTION(treeOp, "Tree op allocation failed."); treeOp->Init(eTreeOpFosterParent, aChild, aStackParent, aTable); @@ -297,6 +400,20 @@ nsHtml5TreeBuilder::appendCharacters(nsIContentHandle* aParent, char16_t* aBuffe { NS_PRECONDITION(aBuffer, "Null buffer"); NS_PRECONDITION(aParent, "Null parent"); + MOZ_ASSERT(!aStart, "aStart must always be zero."); + + if (mBuilder) { + nsresult rv = nsHtml5TreeOperation::AppendText( + aBuffer, // XXX aStart always ignored??? + aLength, + static_cast(deepTreeSurrogateParent ? + deepTreeSurrogateParent : aParent), + mBuilder); + if (NS_FAILED(rv)) { + MarkAsBrokenAndRequestSuspension(rv); + } + return; + } char16_t* bufferCopy = new char16_t[aLength]; memcpy(bufferCopy, aBuffer, aLength * sizeof(char16_t)); @@ -312,6 +429,16 @@ nsHtml5TreeBuilder::appendIsindexPrompt(nsIContentHandle* aParent) { NS_PRECONDITION(aParent, "Null parent"); + if (mBuilder) { + nsresult rv = nsHtml5TreeOperation::AppendIsindexPrompt( + static_cast(aParent), + mBuilder); + if (NS_FAILED(rv)) { + MarkAsBrokenAndRequestSuspension(rv); + } + return; + } + nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); NS_ASSERTION(treeOp, "Tree op allocation failed."); treeOp->Init(eTreeOpAppendIsindexPrompt, aParent); @@ -322,10 +449,24 @@ nsHtml5TreeBuilder::appendComment(nsIContentHandle* aParent, char16_t* aBuffer, { NS_PRECONDITION(aBuffer, "Null buffer"); NS_PRECONDITION(aParent, "Null parent"); + MOZ_ASSERT(!aStart, "aStart must always be zero."); + if (deepTreeSurrogateParent) { return; } + if (mBuilder) { + nsresult rv = nsHtml5TreeOperation::AppendComment( + static_cast(aParent), + aBuffer, // XXX aStart always ignored??? + aLength, + mBuilder); + if (NS_FAILED(rv)) { + MarkAsBrokenAndRequestSuspension(rv); + } + return; + } + char16_t* bufferCopy = new char16_t[aLength]; memcpy(bufferCopy, aBuffer, aLength * sizeof(char16_t)); @@ -338,6 +479,18 @@ void nsHtml5TreeBuilder::appendCommentToDocument(char16_t* aBuffer, int32_t aStart, int32_t aLength) { NS_PRECONDITION(aBuffer, "Null buffer"); + MOZ_ASSERT(!aStart, "aStart must always be zero."); + + if (mBuilder) { + nsresult rv = nsHtml5TreeOperation::AppendCommentToDocument( + aBuffer, // XXX aStart always ignored??? + aLength, + mBuilder); + if (NS_FAILED(rv)) { + MarkAsBrokenAndRequestSuspension(rv); + } + return; + } char16_t* bufferCopy = new char16_t[aLength]; memcpy(bufferCopy, aBuffer, aLength * sizeof(char16_t)); @@ -356,6 +509,18 @@ nsHtml5TreeBuilder::addAttributesToElement(nsIContentHandle* aElement, nsHtml5Ht if (aAttributes == nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES) { return; } + + if (mBuilder) { + nsresult rv = nsHtml5TreeOperation::AddAttributes( + static_cast(aElement), + aAttributes, + mBuilder); + if (NS_FAILED(rv)) { + MarkAsBrokenAndRequestSuspension(rv); + } + return; + } + nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); NS_ASSERTION(treeOp, "Tree op allocation failed."); treeOp->Init(aElement, aAttributes); @@ -366,6 +531,11 @@ nsHtml5TreeBuilder::markMalformedIfScript(nsIContentHandle* aElement) { NS_PRECONDITION(aElement, "Null element"); + if (mBuilder) { + // XXX innerHTML + return; + } + nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); NS_ASSERTION(treeOp, "Tree op allocation failed."); treeOp->Init(eTreeOpMarkMalformedIfScript, aElement); @@ -395,6 +565,19 @@ nsHtml5TreeBuilder::appendDoctypeToDocument(nsIAtom* aName, nsString* aPublicId, { NS_PRECONDITION(aName, "Null name"); + if (mBuilder) { + nsCOMPtr name = nsHtml5TreeOperation::Reget(aName); + nsresult rv = + nsHtml5TreeOperation::AppendDoctypeToDocument(name, + *aPublicId, + *aSystemId, + mBuilder); + if (NS_FAILED(rv)) { + MarkAsBrokenAndRequestSuspension(rv); + } + return; + } + nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); NS_ASSERTION(treeOp, "Tree op allocation failed."); treeOp->Init(aName, *aPublicId, *aSystemId); @@ -442,6 +625,10 @@ nsHtml5TreeBuilder::elementPushed(int32_t aNamespace, nsIAtom* aName, nsIContent return; } if (aName == nsHtml5Atoms::body || aName == nsHtml5Atoms::frameset) { + if (mBuilder) { + // InnerHTML and DOMParser shouldn't start layout anyway + return; + } nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); NS_ASSERTION(treeOp, "Tree op allocation failed."); treeOp->Init(eTreeOpStartLayout); @@ -453,15 +640,27 @@ nsHtml5TreeBuilder::elementPushed(int32_t aNamespace, nsIAtom* aName, nsIContent // If form inputs don't belong to a form, their state preservation // won't work right without an append notification flush at this // point. See bug 497861. - mOpQueue.AppendElement()->Init(eTreeOpFlushPendingAppendNotifications); + if (mBuilder) { + mBuilder->FlushPendingAppendNotifications(); + } else { + mOpQueue.AppendElement()->Init(eTreeOpFlushPendingAppendNotifications); + } + } + if (mBuilder) { + nsHtml5TreeOperation::DoneCreatingElement(static_cast(aElement)); + } else { + mOpQueue.AppendElement()->Init(eTreeOpDoneCreatingElement, aElement); } - mOpQueue.AppendElement()->Init(eTreeOpDoneCreatingElement, aElement); return; } if (aName == nsHtml5Atoms::audio || aName == nsHtml5Atoms::video || aName == nsHtml5Atoms::menuitem) { - mOpQueue.AppendElement()->Init(eTreeOpDoneCreatingElement, aElement); + if (mBuilder) { + nsHtml5TreeOperation::DoneCreatingElement(static_cast(aElement)); + } else { + mOpQueue.AppendElement()->Init(eTreeOpDoneCreatingElement, aElement); + } return; } } @@ -481,9 +680,16 @@ nsHtml5TreeBuilder::elementPopped(int32_t aNamespace, nsIAtom* aName, nsIContent // we now have only SVG and HTML if (aName == nsHtml5Atoms::script) { if (mPreventScriptExecution) { + if (mBuilder) { + nsHtml5TreeOperation::PreventScriptExecution(static_cast(aElement)); + return; + } mOpQueue.AppendElement()->Init(eTreeOpPreventScriptExecution, aElement); return; } + if (mBuilder) { + return; + } if (mCurrentHtmlScriptIsAsyncOrDefer) { NS_ASSERTION(aNamespace == kNameSpaceID_XHTML, "Only HTML scripts may be async/defer."); @@ -500,18 +706,34 @@ nsHtml5TreeBuilder::elementPopped(int32_t aNamespace, nsIAtom* aName, nsIContent return; } if (aName == nsHtml5Atoms::title) { + if (mBuilder) { + nsHtml5TreeOperation::DoneAddingChildren(static_cast(aElement), mBuilder); + return; + } nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); NS_ASSERTION(treeOp, "Tree op allocation failed."); treeOp->Init(eTreeOpDoneAddingChildren, aElement); return; } if (aName == nsHtml5Atoms::style || (aNamespace == kNameSpaceID_XHTML && aName == nsHtml5Atoms::link)) { + if (mBuilder) { + MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript(), + "Scripts must be blocked."); + mBuilder->FlushPendingAppendNotifications(); + mBuilder->UpdateStyleSheet(static_cast(aElement)); + return; + } nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); NS_ASSERTION(treeOp, "Tree op allocation failed."); treeOp->Init(eTreeOpUpdateStyleSheet, aElement); return; } if (aNamespace == kNameSpaceID_SVG) { + if (mBuilder) { + // XXX innerHTML + // is this ever needed for the on-the-main-thread case + return; + } if (aName == nsHtml5Atoms::svg) { nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); NS_ASSERTION(treeOp, "Tree op allocation failed."); @@ -525,6 +747,10 @@ nsHtml5TreeBuilder::elementPopped(int32_t aNamespace, nsIAtom* aName, nsIContent // XXX expose ElementName group here and do switch if (aName == nsHtml5Atoms::object || aName == nsHtml5Atoms::applet) { + if (mBuilder) { + nsHtml5TreeOperation::DoneAddingChildren(static_cast(aElement), mBuilder); + return; + } nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); NS_ASSERTION(treeOp, "Tree op allocation failed."); treeOp->Init(eTreeOpDoneAddingChildren, aElement); @@ -536,16 +762,24 @@ nsHtml5TreeBuilder::elementPopped(int32_t aNamespace, nsIAtom* aName, nsIContent // If form inputs don't belong to a form, their state preservation // won't work right without an append notification flush at this // point. See bug 497861 and bug 539895. - nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); - NS_ASSERTION(treeOp, "Tree op allocation failed."); - treeOp->Init(eTreeOpFlushPendingAppendNotifications); + if (mBuilder) { + mBuilder->FlushPendingAppendNotifications(); + } else { + nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); + NS_ASSERTION(treeOp, "Tree op allocation failed."); + treeOp->Init(eTreeOpFlushPendingAppendNotifications); + } + } + if (mBuilder) { + nsHtml5TreeOperation::DoneAddingChildren(static_cast(aElement), mBuilder); + return; } nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); NS_ASSERTION(treeOp, "Tree op allocation failed."); treeOp->Init(eTreeOpDoneAddingChildren, aElement); return; } - if (aName == nsHtml5Atoms::meta && !fragment) { + if (aName == nsHtml5Atoms::meta && !fragment && !mBuilder) { nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); NS_ASSERTION(treeOp, "Tree op allocation failed."); treeOp->Init(eTreeOpProcessMeta, aElement); @@ -571,6 +805,10 @@ nsHtml5TreeBuilder::accumulateCharacters(const char16_t* aBuf, int32_t aStart, i nsIContentHandle* nsHtml5TreeBuilder::AllocateContentHandle() { + if (MOZ_UNLIKELY(mBuilder)) { + MOZ_ASSUME_UNREACHABLE("Must never allocate a handle with builder."); + return nullptr; + } if (mHandlesUsed == NS_HTML5_TREE_BUILDER_HANDLE_ARRAY_LENGTH) { mOldHandles.AppendElement(mHandles.forget()); mHandles = new nsIContent*[NS_HTML5_TREE_BUILDER_HANDLE_ARRAY_LENGTH]; @@ -595,6 +833,10 @@ nsHtml5TreeBuilder::HasScript() bool nsHtml5TreeBuilder::Flush(bool aDiscretionary) { + if (MOZ_UNLIKELY(mBuilder)) { + MOZ_ASSUME_UNREACHABLE("Must never flush with builder."); + return false; + } if (!aDiscretionary || !(charBufferLen && currentPtr >= 0 && @@ -621,6 +863,10 @@ nsHtml5TreeBuilder::Flush(bool aDiscretionary) void nsHtml5TreeBuilder::FlushLoads() { + if (MOZ_UNLIKELY(mBuilder)) { + MOZ_ASSUME_UNREACHABLE("Must never flush loads with builder."); + return; + } if (!mSpeculativeLoadQueue.IsEmpty()) { mSpeculativeLoadStage->MoveSpeculativeLoadsFrom(mSpeculativeLoadQueue); } @@ -630,7 +876,9 @@ void nsHtml5TreeBuilder::SetDocumentCharset(nsACString& aCharset, int32_t aCharsetSource) { - if (mSpeculativeLoadStage) { + if (mBuilder) { + mBuilder->SetDocumentCharsetAndSource(aCharset, aCharsetSource); + } else if (mSpeculativeLoadStage) { mSpeculativeLoadQueue.AppendElement()->InitSetDocumentCharset( aCharset, aCharsetSource); } else { @@ -642,16 +890,11 @@ nsHtml5TreeBuilder::SetDocumentCharset(nsACString& aCharset, void nsHtml5TreeBuilder::StreamEnded() { - // The fragment mode calls DidBuildModel from nsHtml5Parser. - // Letting DidBuildModel be called from the executor in the fragment case - // confuses the EndLoad logic of nsHTMLDocument, since nsHTMLDocument - // thinks it is dealing with document.written content as opposed to - // innerHTML content. - if (!fragment) { - nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); - NS_ASSERTION(treeOp, "Tree op allocation failed."); - treeOp->Init(eTreeOpStreamEnded); - } + MOZ_ASSERT(!mBuilder, "Must not call StreamEnded with builder."); + MOZ_ASSERT(!fragment, "Must not parse fragments off the main thread."); + nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); + NS_ASSERTION(treeOp, "Tree op allocation failed."); + treeOp->Init(eTreeOpStreamEnded); } void @@ -659,6 +902,10 @@ nsHtml5TreeBuilder::NeedsCharsetSwitchTo(const nsACString& aCharset, int32_t aCharsetSource, int32_t aLineNumber) { + if (MOZ_UNLIKELY(mBuilder)) { + MOZ_ASSUME_UNREACHABLE("Must never switch charset with builder."); + return; + } nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); NS_ASSERTION(treeOp, "Tree op allocation failed."); treeOp->Init(eTreeOpNeedsCharsetSwitchTo, @@ -672,12 +919,20 @@ nsHtml5TreeBuilder::MaybeComplainAboutCharset(const char* aMsgId, bool aError, int32_t aLineNumber) { + if (MOZ_UNLIKELY(mBuilder)) { + MOZ_ASSUME_UNREACHABLE("Must never complain about charset with builder."); + return; + } mOpQueue.AppendElement()->Init(aMsgId, aError, aLineNumber); } void nsHtml5TreeBuilder::AddSnapshotToScript(nsAHtml5TreeBuilderState* aSnapshot, int32_t aLine) { + if (MOZ_UNLIKELY(mBuilder)) { + MOZ_ASSUME_UNREACHABLE("Must never use snapshots with builder."); + return; + } NS_PRECONDITION(HasScript(), "No script to add a snapshot to!"); NS_PRECONDITION(aSnapshot, "Got null snapshot."); mOpQueue.ElementAt(mOpQueue.Length() - 1).SetSnapshot(aSnapshot, aLine); @@ -686,6 +941,7 @@ nsHtml5TreeBuilder::AddSnapshotToScript(nsAHtml5TreeBuilderState* aSnapshot, int void nsHtml5TreeBuilder::DropHandles() { + MOZ_ASSERT(!mBuilder, "Must not drop handles with builder."); mOldHandles.Clear(); mHandlesUsed = 0; } @@ -693,6 +949,10 @@ nsHtml5TreeBuilder::DropHandles() void nsHtml5TreeBuilder::MarkAsBroken() { + if (MOZ_UNLIKELY(mBuilder)) { + MOZ_ASSUME_UNREACHABLE("Must not call this with builder."); + return; + } mOpQueue.Clear(); // Previous ops don't matter anymore mOpQueue.AppendElement()->Init(eTreeOpMarkAsBroken); } @@ -700,6 +960,7 @@ nsHtml5TreeBuilder::MarkAsBroken() void nsHtml5TreeBuilder::StartPlainTextViewSource(const nsAutoString& aTitle) { + MOZ_ASSERT(!mBuilder, "Must not view source with builder."); startTag(nsHtml5ElementName::ELT_TITLE, nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES, false); @@ -726,6 +987,7 @@ nsHtml5TreeBuilder::StartPlainTextViewSource(const nsAutoString& aTitle) void nsHtml5TreeBuilder::StartPlainText() { + MOZ_ASSERT(!mBuilder, "Must not view source with builder."); startTag(nsHtml5ElementName::ELT_LINK, nsHtml5PlainTextUtils::NewLinkAttributes(), false); @@ -736,6 +998,7 @@ nsHtml5TreeBuilder::StartPlainText() void nsHtml5TreeBuilder::StartPlainTextBody() { + MOZ_ASSERT(!mBuilder, "Must not view source with builder."); startTag(nsHtml5ElementName::ELT_PRE, nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES, false); @@ -746,6 +1009,10 @@ nsHtml5TreeBuilder::StartPlainTextBody() void nsHtml5TreeBuilder::documentMode(nsHtml5DocumentMode m) { + if (mBuilder) { + mBuilder->SetDocumentMode(m); + return; + } nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); NS_ASSERTION(treeOp, "Tree op allocation failed."); treeOp->Init(m); @@ -754,6 +1021,9 @@ nsHtml5TreeBuilder::documentMode(nsHtml5DocumentMode m) nsIContentHandle* nsHtml5TreeBuilder::getDocumentFragmentForTemplate(nsIContentHandle* aTemplate) { + if (mBuilder) { + return nsHtml5TreeOperation::GetDocumentFragmentForTemplate(static_cast(aTemplate)); + } nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); NS_ASSERTION(treeOp, "Tree op allocation failed."); nsIContentHandle* fragHandle = AllocateContentHandle(); @@ -764,15 +1034,16 @@ nsHtml5TreeBuilder::getDocumentFragmentForTemplate(nsIContentHandle* aTemplate) nsIContentHandle* nsHtml5TreeBuilder::getFormPointerForContext(nsIContentHandle* aContext) { + MOZ_ASSERT(mBuilder, "Must have builder."); if (!aContext) { return nullptr; } MOZ_ASSERT(NS_IsMainThread()); - // aContext must always be a handle to an element that already exists - // in the document. It must never be an empty handle. - nsIContent* contextNode = *(static_cast(aContext)); + // aContext must always be an element that already exists + // in the document. + nsIContent* contextNode = static_cast(aContext); nsIContent* currentAncestor = contextNode; // We traverse the ancestors of the context node to find the nearest @@ -790,9 +1061,7 @@ nsHtml5TreeBuilder::getFormPointerForContext(nsIContentHandle* aContext) return nullptr; } - nsIContentHandle* formPointer = AllocateContentHandle(); - *(static_cast(formPointer)) = nearestForm; - return formPointer; + return nearestForm; } // Error reporting @@ -800,6 +1069,7 @@ nsHtml5TreeBuilder::getFormPointerForContext(nsIContentHandle* aContext) void nsHtml5TreeBuilder::EnableViewSource(nsHtml5Highlighter* aHighlighter) { + MOZ_ASSERT(!mBuilder, "Must not view source with builder."); mViewSource = aHighlighter; } diff --git a/parser/html/nsHtml5TreeBuilderHSupplement.h b/parser/html/nsHtml5TreeBuilderHSupplement.h index 1ed93b51336e..546b4b871b1b 100644 --- a/parser/html/nsHtml5TreeBuilderHSupplement.h +++ b/parser/html/nsHtml5TreeBuilderHSupplement.h @@ -5,6 +5,11 @@ #define NS_HTML5_TREE_BUILDER_HANDLE_ARRAY_LENGTH 512 private: + nsHtml5OplessBuilder* mBuilder; + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // If mBuilder is not null, the tree op machinery is not in use and + // the fields below aren't in use, either. + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! nsHtml5Highlighter* mViewSource; nsTArray mOpQueue; nsTArray mSpeculativeLoadQueue; @@ -13,7 +18,6 @@ int32_t mHandlesUsed; nsTArray > mOldHandles; nsHtml5TreeOpStage* mSpeculativeLoadStage; - nsIContent** mDeepTreeSurrogateParent; bool mCurrentHtmlScriptIsAsyncOrDefer; bool mPreventScriptExecution; #ifdef DEBUG @@ -60,8 +64,16 @@ accumulateCharacters(aBuf, aStart, aLength); } + void MarkAsBrokenAndRequestSuspension(nsresult aRv) + { + mBuilder->MarkAsBroken(aRv); + requestSuspension(); + } + public: + nsHtml5TreeBuilder(nsHtml5OplessBuilder* aBuilder); + nsHtml5TreeBuilder(nsAHtml5TreeOpSink* aOpSink, nsHtml5TreeOpStage* aStage); diff --git a/parser/html/nsHtml5TreeOpExecutor.cpp b/parser/html/nsHtml5TreeOpExecutor.cpp index b8056dcb3b20..703d1929529a 100644 --- a/parser/html/nsHtml5TreeOpExecutor.cpp +++ b/parser/html/nsHtml5TreeOpExecutor.cpp @@ -13,8 +13,6 @@ #include "nsIMarkupDocumentViewer.h" #include "nsIContentViewer.h" #include "nsIDocShellTreeItem.h" -#include "nsIStyleSheetLinkingElement.h" -#include "nsStyleLinkElement.h" #include "nsIDocShell.h" #include "nsIScriptGlobalObject.h" #include "nsIScriptSecurityManager.h" @@ -62,10 +60,10 @@ class nsHtml5ExecutorReflusher : public nsRunnable static mozilla::LinkedList* gBackgroundFlushList = nullptr; static nsITimer* gFlushTimer = nullptr; -nsHtml5TreeOpExecutor::nsHtml5TreeOpExecutor(bool aRunsToCompletion) - : mPreloadedURLs(23) // Mean # of preloadable resources per page on dmoz +nsHtml5TreeOpExecutor::nsHtml5TreeOpExecutor() + : nsHtml5DocumentBuilder(false) + , mPreloadedURLs(23) // Mean # of preloadable resources per page on dmoz { - mRunsToCompletion = aRunsToCompletion; // zeroing operator new for everything else } @@ -210,34 +208,16 @@ nsHtml5TreeOpExecutor::FlushPendingNotifications(mozFlushType aType) } } -void -nsHtml5TreeOpExecutor::SetDocumentCharsetAndSource(nsACString& aCharset, int32_t aCharsetSource) -{ - if (mDocument) { - mDocument->SetDocumentCharacterSetSource(aCharsetSource); - mDocument->SetDocumentCharacterSet(aCharset); - } -} - nsISupports* nsHtml5TreeOpExecutor::GetTarget() { return mDocument; } -// nsContentSink overrides - -void -nsHtml5TreeOpExecutor::UpdateChildCounts() -{ - // No-op -} - nsresult nsHtml5TreeOpExecutor::MarkAsBroken(nsresult aReason) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(!mRunsToCompletion, "Fragment parsers can't be broken!"); mBroken = aReason; if (mStreamParser) { mStreamParser->Terminate(); @@ -255,12 +235,6 @@ nsHtml5TreeOpExecutor::MarkAsBroken(nsresult aReason) return aReason; } -nsresult -nsHtml5TreeOpExecutor::FlushTags() -{ - return NS_OK; -} - void FlushTimerCallback(nsITimer* aTimer, void* aClosure) { @@ -303,61 +277,6 @@ nsHtml5TreeOpExecutor::ContinueInterruptedParsingAsync() } } -void -nsHtml5TreeOpExecutor::UpdateStyleSheet(nsIContent* aElement) -{ - // Break out of the doc update created by Flush() to zap a runnable - // waiting to call UpdateStyleSheet without the right observer - EndDocUpdate(); - - if (MOZ_UNLIKELY(!mParser)) { - // EndDocUpdate ran stuff that called nsIParser::Terminate() - return; - } - - nsCOMPtr ssle(do_QueryInterface(aElement)); - NS_ASSERTION(ssle, "Node didn't QI to style."); - - ssle->SetEnableUpdates(true); - - bool willNotify; - bool isAlternate; - nsresult rv = ssle->UpdateStyleSheet(mRunsToCompletion ? nullptr : this, - &willNotify, - &isAlternate); - if (NS_SUCCEEDED(rv) && willNotify && !isAlternate && !mRunsToCompletion) { - ++mPendingSheetCount; - mScriptLoader->AddExecuteBlocker(); - } - - if (aElement->IsHTML(nsGkAtoms::link)) { - // look for - nsAutoString relVal; - aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::rel, relVal); - if (!relVal.IsEmpty()) { - uint32_t linkTypes = nsStyleLinkElement::ParseLinkTypes(relVal); - bool hasPrefetch = linkTypes & nsStyleLinkElement::ePREFETCH; - if (hasPrefetch || (linkTypes & nsStyleLinkElement::eNEXT)) { - nsAutoString hrefVal; - aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::href, hrefVal); - if (!hrefVal.IsEmpty()) { - PrefetchHref(hrefVal, aElement, hasPrefetch); - } - } - if (linkTypes & nsStyleLinkElement::eDNS_PREFETCH) { - nsAutoString hrefVal; - aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::href, hrefVal); - if (!hrefVal.IsEmpty()) { - PrefetchDNS(hrefVal); - } - } - } - } - - // Re-open update - BeginDocUpdate(); -} - void nsHtml5TreeOpExecutor::FlushSpeculativeLoads() { @@ -665,26 +584,6 @@ nsHtml5TreeOpExecutor::IsScriptEnabled() ScriptAllowed(globalObject->GetGlobalJSObject()); } -void -nsHtml5TreeOpExecutor::SetDocumentMode(nsHtml5DocumentMode m) -{ - nsCompatibility mode = eCompatibility_NavQuirks; - switch (m) { - case STANDARDS_MODE: - mode = eCompatibility_FullStandards; - break; - case ALMOST_STANDARDS_MODE: - mode = eCompatibility_AlmostStandards; - break; - case QUIRKS_MODE: - mode = eCompatibility_NavQuirks; - break; - } - nsCOMPtr htmlDocument = do_QueryInterface(mDocument); - NS_ASSERTION(htmlDocument, "Document didn't QI into HTML document."); - htmlDocument->SetCompatibilityMode(mode); -} - void nsHtml5TreeOpExecutor::StartLayout() { if (mLayoutStarted || !mDocument) { @@ -767,15 +666,6 @@ nsHtml5TreeOpExecutor::RunScript(nsIContent* aScriptElement) } } -nsresult -nsHtml5TreeOpExecutor::Init(nsIDocument* aDoc, - nsIURI* aURI, - nsISupports* aContainer, - nsIChannel* aChannel) -{ - return nsContentSink::Init(aDoc, aURI, aContainer, aChannel); -} - void nsHtml5TreeOpExecutor::Start() { @@ -882,19 +772,6 @@ nsHtml5TreeOpExecutor::GetParser() return static_cast(mParser.get()); } -void -nsHtml5TreeOpExecutor::Reset() -{ - MOZ_ASSERT(mRunsToCompletion); - DropHeldElements(); - mOpQueue.Clear(); - mStarted = false; - mFlushState = eNotFlushing; - mRunFlushLoopOnStack = false; - MOZ_ASSERT(!mReadingFromStage); - MOZ_ASSERT(NS_SUCCEEDED(mBroken)); -} - void nsHtml5TreeOpExecutor::MoveOpsFrom(nsTArray& aOpQueue) { diff --git a/parser/html/nsHtml5TreeOpExecutor.h b/parser/html/nsHtml5TreeOpExecutor.h index c6771383dfe5..14a67532a38b 100644 --- a/parser/html/nsHtml5TreeOpExecutor.h +++ b/parser/html/nsHtml5TreeOpExecutor.h @@ -80,19 +80,6 @@ class nsHtml5TreeOpExecutor : public nsHtml5DocumentBuilder, bool mCallContinueInterruptedParsingIfEnabled; - /** - * Non-NS_OK if this parser should refuse to process any more input. - * For example, the parser needs to be marked as broken if it drops some - * input due to a memory allocation failure. In such a case, the whole - * parser needs to be marked as broken, because some input has been lost - * and parsing more input could lead to a DOM where pieces of HTML source - * that weren't supposed to become scripts become scripts. - * - * Since NS_OK is actually 0, zeroing operator new takes care of - * initializing this. - */ - nsresult mBroken; - /** * Whether this executor has already complained about matters related * to character encoding declarations. @@ -101,7 +88,7 @@ class nsHtml5TreeOpExecutor : public nsHtml5DocumentBuilder, public: - nsHtml5TreeOpExecutor(bool aRunsToCompletion = false); + nsHtml5TreeOpExecutor(); virtual ~nsHtml5TreeOpExecutor(); // nsIContentSink @@ -154,16 +141,8 @@ class nsHtml5TreeOpExecutor : public nsHtml5DocumentBuilder, */ virtual nsISupports *GetTarget(); - // nsContentSink methods - virtual void UpdateChildCounts(); - virtual nsresult FlushTags(); virtual void ContinueInterruptedParsingAsync(); - /** - * Sets up style sheet load / parse - */ - void UpdateStyleSheet(nsIContent* aElement); - // XXX Does anyone need this? nsIDocShell* GetDocShell() { return mDocShell; @@ -172,14 +151,8 @@ class nsHtml5TreeOpExecutor : public nsHtml5DocumentBuilder, bool IsScriptExecuting() { return IsScriptExecutingImpl(); } - - void SetNodeInfoManager(nsNodeInfoManager* aManager) { - mNodeInfoManager = aManager; - } - - // Not from interface - void SetDocumentCharsetAndSource(nsACString& aCharset, int32_t aCharsetSource); + // Not from interface void SetStreamParser(nsHtml5StreamParser* aStreamParser) { mStreamParser = aStreamParser; @@ -189,50 +162,10 @@ class nsHtml5TreeOpExecutor : public nsHtml5DocumentBuilder, bool IsScriptEnabled(); - bool BelongsToStringParser() { - return mRunsToCompletion; - } + virtual nsresult MarkAsBroken(nsresult aReason); - /** - * Marks this parser as broken and tells the stream parser (if any) to - * terminate. - * - * @return aReason for convenience - */ - nsresult MarkAsBroken(nsresult aReason); - - /** - * Checks if this parser is broken. Returns a non-NS_OK (i.e. non-0) - * value if broken. - */ - inline nsresult IsBroken() { - return mBroken; - } - - inline void BeginDocUpdate() { - NS_PRECONDITION(mFlushState == eInFlush, "Tried to double-open update."); - NS_PRECONDITION(mParser, "Started update without parser."); - mFlushState = eInDocUpdate; - mDocument->BeginUpdate(UPDATE_CONTENT_MODEL); - } - - inline void EndDocUpdate() { - NS_PRECONDITION(mFlushState != eNotifying, "mFlushState out of sync"); - if (mFlushState == eInDocUpdate) { - FlushPendingAppendNotifications(); - mFlushState = eInFlush; - mDocument->EndUpdate(UPDATE_CONTENT_MODEL); - } - } - - void StartLayout(); - void SetDocumentMode(nsHtml5DocumentMode m); - - nsresult Init(nsIDocument* aDoc, nsIURI* aURI, - nsISupports* aContainer, nsIChannel* aChannel); - void FlushSpeculativeLoads(); void RunFlushLoop(); @@ -273,8 +206,6 @@ class nsHtml5TreeOpExecutor : public nsHtml5DocumentBuilder, void RunScript(nsIContent* aScriptElement); - void Reset(); - /** * Flush the operations from the tree operations from the argument * queue unconditionally. (This is for the main thread case.) diff --git a/parser/html/nsHtml5TreeOperation.cpp b/parser/html/nsHtml5TreeOperation.cpp index 8f61a2a969a1..5346b362f83e 100644 --- a/parser/html/nsHtml5TreeOperation.cpp +++ b/parser/html/nsHtml5TreeOperation.cpp @@ -344,7 +344,7 @@ nsIContent* nsHtml5TreeOperation::CreateElement(int32_t aNs, nsIAtom* aName, nsHtml5HtmlAttributes* aAttributes, - bool aFromNetwork, + mozilla::dom::FromParser aFromParser, nsHtml5DocumentBuilder* aBuilder) { bool isKeygen = (aName == nsHtml5Atoms::keygen && aNs == kNameSpaceID_XHTML); @@ -358,11 +358,7 @@ nsHtml5TreeOperation::CreateElement(int32_t aNs, NS_ASSERTION(nodeInfo, "Got null nodeinfo."); NS_NewElement(getter_AddRefs(newContent), nodeInfo.forget(), - (aFromNetwork ? - dom::FROM_PARSER_NETWORK - : (aBuilder->BelongsToStringParser() ? - dom::FROM_PARSER_FRAGMENT : - dom::FROM_PARSER_DOCUMENT_WRITE))); + aFromParser); NS_ASSERTION(newContent, "Element creation created null pointer."); aBuilder->HoldElement(newContent); @@ -406,11 +402,7 @@ nsHtml5TreeOperation::CreateElement(int32_t aNs, nsCOMPtr ni = optionNodeInfo; NS_NewElement(getter_AddRefs(optionElt), ni.forget(), - (aFromNetwork ? - dom::FROM_PARSER_NETWORK - : (aBuilder->BelongsToStringParser() ? - dom::FROM_PARSER_FRAGMENT : - dom::FROM_PARSER_DOCUMENT_WRITE))); + aFromParser); nsRefPtr optionText = new nsTextNode(aBuilder->GetNodeInfoManager()); (void) optionText->SetText(theContent[i], false); @@ -674,7 +666,9 @@ nsHtml5TreeOperation::Perform(nsHtml5TreeOpExecutor* aBuilder, *target = CreateElement(ns, name, attributes, - mOpCode == eTreeOpCreateElementNetwork, + mOpCode == eTreeOpCreateElementNetwork ? + dom::FROM_PARSER_NETWORK : + dom::FROM_PARSER_DOCUMENT_WRITE, aBuilder); return NS_OK; } diff --git a/parser/html/nsHtml5TreeOperation.h b/parser/html/nsHtml5TreeOperation.h index 6b3a386f9a81..8cff6d49d4f0 100644 --- a/parser/html/nsHtml5TreeOperation.h +++ b/parser/html/nsHtml5TreeOperation.h @@ -8,6 +8,7 @@ #include "nsHtml5DocumentMode.h" #include "nsHtml5HtmlAttributes.h" #include "nsXPCOMStrings.h" +#include "mozilla/dom/FromParser.h" class nsIContent; class nsHtml5TreeOpExecutor; @@ -146,7 +147,7 @@ class nsHtml5TreeOperation { static nsIContent* CreateElement(int32_t aNs, nsIAtom* aName, nsHtml5HtmlAttributes* aAttributes, - bool aFromNetwork, + mozilla::dom::FromParser aFromParser, nsHtml5DocumentBuilder* aBuilder); static void SetFormElement(nsIContent* aNode, nsIContent* aParent);