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 "nsError.h" michael@0: #include "nsIPresShell.h" michael@0: #include "nsNodeUtils.h" michael@0: #include "nsIFrame.h" michael@0: #include "mozilla/Likely.h" michael@0: michael@0: class nsPresContext; michael@0: michael@0: nsHtml5TreeBuilder::nsHtml5TreeBuilder(nsHtml5OplessBuilder* aBuilder) michael@0: : scriptingEnabled(false) michael@0: , fragment(false) michael@0: , contextNode(nullptr) michael@0: , formPointer(nullptr) michael@0: , headPointer(nullptr) michael@0: , mBuilder(aBuilder) michael@0: , mViewSource(nullptr) michael@0: , mOpSink(nullptr) michael@0: , mHandles(nullptr) michael@0: , mHandlesUsed(0) michael@0: , mSpeculativeLoadStage(nullptr) michael@0: , mCurrentHtmlScriptIsAsyncOrDefer(false) michael@0: , mPreventScriptExecution(false) michael@0: #ifdef DEBUG michael@0: , mActive(false) michael@0: #endif michael@0: { michael@0: MOZ_COUNT_CTOR(nsHtml5TreeBuilder); michael@0: } michael@0: michael@0: nsHtml5TreeBuilder::nsHtml5TreeBuilder(nsAHtml5TreeOpSink* aOpSink, michael@0: nsHtml5TreeOpStage* aStage) michael@0: : scriptingEnabled(false) michael@0: , fragment(false) michael@0: , contextNode(nullptr) michael@0: , formPointer(nullptr) michael@0: , headPointer(nullptr) michael@0: , mBuilder(nullptr) michael@0: , mViewSource(nullptr) michael@0: , mOpSink(aOpSink) michael@0: , mHandles(new nsIContent*[NS_HTML5_TREE_BUILDER_HANDLE_ARRAY_LENGTH]) michael@0: , mHandlesUsed(0) michael@0: , mSpeculativeLoadStage(aStage) michael@0: , mCurrentHtmlScriptIsAsyncOrDefer(false) michael@0: , mPreventScriptExecution(false) michael@0: #ifdef DEBUG michael@0: , mActive(false) michael@0: #endif michael@0: { michael@0: MOZ_COUNT_CTOR(nsHtml5TreeBuilder); michael@0: } michael@0: michael@0: nsHtml5TreeBuilder::~nsHtml5TreeBuilder() michael@0: { michael@0: MOZ_COUNT_DTOR(nsHtml5TreeBuilder); michael@0: NS_ASSERTION(!mActive, "nsHtml5TreeBuilder deleted without ever calling end() on it!"); michael@0: mOpQueue.Clear(); michael@0: } michael@0: michael@0: nsIContentHandle* michael@0: nsHtml5TreeBuilder::createElement(int32_t aNamespace, nsIAtom* aName, nsHtml5HtmlAttributes* aAttributes) michael@0: { michael@0: NS_PRECONDITION(aAttributes, "Got null attributes."); michael@0: NS_PRECONDITION(aName, "Got null name."); michael@0: NS_PRECONDITION(aNamespace == kNameSpaceID_XHTML || michael@0: aNamespace == kNameSpaceID_SVG || michael@0: aNamespace == kNameSpaceID_MathML, michael@0: "Bogus namespace."); michael@0: michael@0: if (mBuilder) { michael@0: nsCOMPtr name = nsHtml5TreeOperation::Reget(aName); michael@0: nsIContent* elem = michael@0: nsHtml5TreeOperation::CreateElement(aNamespace, michael@0: name, michael@0: aAttributes, michael@0: mozilla::dom::FROM_PARSER_FRAGMENT, michael@0: mBuilder); michael@0: if (MOZ_UNLIKELY(aAttributes != tokenizer->GetAttributes() && michael@0: aAttributes != nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES)) { michael@0: delete aAttributes; michael@0: } michael@0: return elem; michael@0: } michael@0: michael@0: nsIContentHandle* content = AllocateContentHandle(); michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: treeOp->Init(aNamespace, michael@0: aName, michael@0: aAttributes, michael@0: content, michael@0: !!mSpeculativeLoadStage); michael@0: // mSpeculativeLoadStage is non-null only in the off-the-main-thread michael@0: // tree builder, which handles the network stream michael@0: michael@0: // Start wall of code for speculative loading and line numbers michael@0: michael@0: if (mSpeculativeLoadStage) { michael@0: switch (aNamespace) { michael@0: case kNameSpaceID_XHTML: michael@0: if (nsHtml5Atoms::img == aName) { michael@0: nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_SRC); michael@0: if (url) { michael@0: nsString* crossOrigin = michael@0: aAttributes->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN); michael@0: mSpeculativeLoadQueue.AppendElement()-> michael@0: InitImage(*url, michael@0: crossOrigin ? *crossOrigin : NullString()); michael@0: } michael@0: } else if (nsHtml5Atoms::script == aName) { michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: treeOp->Init(eTreeOpSetScriptLineNumberAndFreeze, content, tokenizer->getLineNumber()); michael@0: michael@0: nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_SRC); michael@0: if (url) { michael@0: nsString* charset = aAttributes->getValue(nsHtml5AttributeName::ATTR_CHARSET); michael@0: nsString* type = aAttributes->getValue(nsHtml5AttributeName::ATTR_TYPE); michael@0: nsString* crossOrigin = michael@0: aAttributes->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN); michael@0: mSpeculativeLoadQueue.AppendElement()-> michael@0: InitScript(*url, michael@0: (charset) ? *charset : EmptyString(), michael@0: (type) ? *type : EmptyString(), michael@0: (crossOrigin) ? *crossOrigin : NullString(), michael@0: mode == NS_HTML5TREE_BUILDER_IN_HEAD); michael@0: mCurrentHtmlScriptIsAsyncOrDefer = michael@0: aAttributes->contains(nsHtml5AttributeName::ATTR_ASYNC) || michael@0: aAttributes->contains(nsHtml5AttributeName::ATTR_DEFER); michael@0: } michael@0: } else if (nsHtml5Atoms::link == aName) { michael@0: nsString* rel = aAttributes->getValue(nsHtml5AttributeName::ATTR_REL); michael@0: // Not splitting on space here is bogus but the old parser didn't even michael@0: // do a case-insensitive check. michael@0: if (rel && rel->LowerCaseEqualsASCII("stylesheet")) { michael@0: nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_HREF); michael@0: if (url) { michael@0: nsString* charset = aAttributes->getValue(nsHtml5AttributeName::ATTR_CHARSET); michael@0: nsString* crossOrigin = michael@0: aAttributes->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN); michael@0: mSpeculativeLoadQueue.AppendElement()-> michael@0: InitStyle(*url, michael@0: (charset) ? *charset : EmptyString(), michael@0: (crossOrigin) ? *crossOrigin : NullString()); michael@0: } michael@0: } michael@0: } else if (nsHtml5Atoms::video == aName) { michael@0: nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_POSTER); michael@0: if (url) { michael@0: mSpeculativeLoadQueue.AppendElement()->InitImage(*url, NullString()); michael@0: } michael@0: } else if (nsHtml5Atoms::style == aName) { michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: treeOp->Init(eTreeOpSetStyleLineNumber, content, tokenizer->getLineNumber()); michael@0: } else if (nsHtml5Atoms::html == aName) { michael@0: nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_MANIFEST); michael@0: if (url) { michael@0: mSpeculativeLoadQueue.AppendElement()->InitManifest(*url); michael@0: } else { michael@0: mSpeculativeLoadQueue.AppendElement()->InitManifest(EmptyString()); michael@0: } michael@0: } else if (nsHtml5Atoms::base == aName) { michael@0: nsString* url = michael@0: aAttributes->getValue(nsHtml5AttributeName::ATTR_HREF); michael@0: if (url) { michael@0: mSpeculativeLoadQueue.AppendElement()->InitBase(*url); michael@0: } michael@0: } michael@0: break; michael@0: case kNameSpaceID_SVG: michael@0: if (nsHtml5Atoms::image == aName) { michael@0: nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_XLINK_HREF); michael@0: if (url) { michael@0: mSpeculativeLoadQueue.AppendElement()->InitImage(*url, NullString()); michael@0: } michael@0: } else if (nsHtml5Atoms::script == aName) { michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: treeOp->Init(eTreeOpSetScriptLineNumberAndFreeze, content, tokenizer->getLineNumber()); michael@0: michael@0: nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_XLINK_HREF); michael@0: if (url) { michael@0: nsString* type = aAttributes->getValue(nsHtml5AttributeName::ATTR_TYPE); michael@0: nsString* crossOrigin = michael@0: aAttributes->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN); michael@0: mSpeculativeLoadQueue.AppendElement()-> michael@0: InitScript(*url, michael@0: EmptyString(), michael@0: (type) ? *type : EmptyString(), michael@0: (crossOrigin) ? *crossOrigin : NullString(), michael@0: mode == NS_HTML5TREE_BUILDER_IN_HEAD); michael@0: } michael@0: } else if (nsHtml5Atoms::style == aName) { michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: treeOp->Init(eTreeOpSetStyleLineNumber, content, tokenizer->getLineNumber()); michael@0: michael@0: nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_XLINK_HREF); michael@0: if (url) { michael@0: nsString* crossOrigin = michael@0: aAttributes->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN); michael@0: mSpeculativeLoadQueue.AppendElement()-> michael@0: InitStyle(*url, EmptyString(), michael@0: (crossOrigin) ? *crossOrigin : NullString()); michael@0: } michael@0: } michael@0: break; michael@0: } michael@0: } else if (aNamespace != kNameSpaceID_MathML) { michael@0: // No speculative loader--just line numbers and defer/async check michael@0: if (nsHtml5Atoms::style == aName) { michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: treeOp->Init(eTreeOpSetStyleLineNumber, content, tokenizer->getLineNumber()); michael@0: } else if (nsHtml5Atoms::script == aName) { michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: treeOp->Init(eTreeOpSetScriptLineNumberAndFreeze, content, tokenizer->getLineNumber()); michael@0: if (aNamespace == kNameSpaceID_XHTML) { michael@0: mCurrentHtmlScriptIsAsyncOrDefer = michael@0: aAttributes->contains(nsHtml5AttributeName::ATTR_SRC) && michael@0: (aAttributes->contains(nsHtml5AttributeName::ATTR_ASYNC) || michael@0: aAttributes->contains(nsHtml5AttributeName::ATTR_DEFER)); michael@0: } michael@0: } else if (aNamespace == kNameSpaceID_XHTML && nsHtml5Atoms::html == aName) { michael@0: nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_MANIFEST); michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: if (url) { michael@0: treeOp->Init(eTreeOpProcessOfflineManifest, *url); michael@0: } else { michael@0: treeOp->Init(eTreeOpProcessOfflineManifest, EmptyString()); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // End wall of code for speculative loading michael@0: michael@0: return content; michael@0: } michael@0: michael@0: nsIContentHandle* michael@0: nsHtml5TreeBuilder::createElement(int32_t aNamespace, nsIAtom* aName, nsHtml5HtmlAttributes* aAttributes, nsIContentHandle* aFormElement) michael@0: { michael@0: nsIContentHandle* content = createElement(aNamespace, aName, aAttributes); michael@0: if (aFormElement) { michael@0: if (mBuilder) { michael@0: nsHtml5TreeOperation::SetFormElement(static_cast(content), michael@0: static_cast(aFormElement)); michael@0: } else { michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: treeOp->Init(eTreeOpSetFormElement, content, aFormElement); michael@0: } michael@0: } michael@0: return content; michael@0: } michael@0: michael@0: nsIContentHandle* michael@0: nsHtml5TreeBuilder::createHtmlElementSetAsRoot(nsHtml5HtmlAttributes* aAttributes) michael@0: { michael@0: nsIContentHandle* content = createElement(kNameSpaceID_XHTML, nsHtml5Atoms::html, aAttributes); michael@0: if (mBuilder) { michael@0: nsresult rv = nsHtml5TreeOperation::AppendToDocument(static_cast(content), michael@0: mBuilder); michael@0: if (NS_FAILED(rv)) { michael@0: MarkAsBrokenAndRequestSuspension(rv); michael@0: } michael@0: } else { michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: treeOp->Init(eTreeOpAppendToDocument, content); michael@0: } michael@0: return content; michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::detachFromParent(nsIContentHandle* aElement) michael@0: { michael@0: NS_PRECONDITION(aElement, "Null element"); michael@0: michael@0: if (mBuilder) { michael@0: nsHtml5TreeOperation::Detach(static_cast(aElement), michael@0: mBuilder); michael@0: return; michael@0: } michael@0: michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: treeOp->Init(eTreeOpDetach, aElement); michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::appendElement(nsIContentHandle* aChild, nsIContentHandle* aParent) michael@0: { michael@0: NS_PRECONDITION(aChild, "Null child"); michael@0: NS_PRECONDITION(aParent, "Null parent"); michael@0: if (deepTreeSurrogateParent) { michael@0: return; michael@0: } michael@0: michael@0: if (mBuilder) { michael@0: nsresult rv = nsHtml5TreeOperation::Append(static_cast(aChild), michael@0: static_cast(aParent), michael@0: mBuilder); michael@0: if (NS_FAILED(rv)) { michael@0: MarkAsBrokenAndRequestSuspension(rv); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: treeOp->Init(eTreeOpAppend, aChild, aParent); michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::appendChildrenToNewParent(nsIContentHandle* aOldParent, nsIContentHandle* aNewParent) michael@0: { michael@0: NS_PRECONDITION(aOldParent, "Null old parent"); michael@0: NS_PRECONDITION(aNewParent, "Null new parent"); michael@0: michael@0: if (mBuilder) { michael@0: nsresult rv = nsHtml5TreeOperation::AppendChildrenToNewParent( michael@0: static_cast(aOldParent), michael@0: static_cast(aNewParent), michael@0: mBuilder); michael@0: if (NS_FAILED(rv)) { michael@0: MarkAsBrokenAndRequestSuspension(rv); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: treeOp->Init(eTreeOpAppendChildrenToNewParent, aOldParent, aNewParent); michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::insertFosterParentedCharacters(char16_t* aBuffer, int32_t aStart, int32_t aLength, nsIContentHandle* aTable, nsIContentHandle* aStackParent) michael@0: { michael@0: NS_PRECONDITION(aBuffer, "Null buffer"); michael@0: NS_PRECONDITION(aTable, "Null table"); michael@0: NS_PRECONDITION(aStackParent, "Null stack parent"); michael@0: MOZ_ASSERT(!aStart, "aStart must always be zero."); michael@0: michael@0: if (mBuilder) { michael@0: nsresult rv = nsHtml5TreeOperation::FosterParentText( michael@0: static_cast(aStackParent), michael@0: aBuffer, // XXX aStart always ignored??? michael@0: aLength, michael@0: static_cast(aTable), michael@0: mBuilder); michael@0: if (NS_FAILED(rv)) { michael@0: MarkAsBrokenAndRequestSuspension(rv); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: char16_t* bufferCopy = new char16_t[aLength]; michael@0: memcpy(bufferCopy, aBuffer, aLength * sizeof(char16_t)); michael@0: michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: treeOp->Init(eTreeOpFosterParentText, bufferCopy, aLength, aStackParent, aTable); michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::insertFosterParentedChild(nsIContentHandle* aChild, nsIContentHandle* aTable, nsIContentHandle* aStackParent) michael@0: { michael@0: NS_PRECONDITION(aChild, "Null child"); michael@0: NS_PRECONDITION(aTable, "Null table"); michael@0: NS_PRECONDITION(aStackParent, "Null stack parent"); michael@0: michael@0: if (mBuilder) { michael@0: nsresult rv = nsHtml5TreeOperation::FosterParent( michael@0: static_cast(aChild), michael@0: static_cast(aStackParent), michael@0: static_cast(aTable), michael@0: mBuilder); michael@0: if (NS_FAILED(rv)) { michael@0: MarkAsBrokenAndRequestSuspension(rv); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: treeOp->Init(eTreeOpFosterParent, aChild, aStackParent, aTable); michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::appendCharacters(nsIContentHandle* aParent, char16_t* aBuffer, int32_t aStart, int32_t aLength) michael@0: { michael@0: NS_PRECONDITION(aBuffer, "Null buffer"); michael@0: NS_PRECONDITION(aParent, "Null parent"); michael@0: MOZ_ASSERT(!aStart, "aStart must always be zero."); michael@0: michael@0: if (mBuilder) { michael@0: nsresult rv = nsHtml5TreeOperation::AppendText( michael@0: aBuffer, // XXX aStart always ignored??? michael@0: aLength, michael@0: static_cast(deepTreeSurrogateParent ? michael@0: deepTreeSurrogateParent : aParent), michael@0: mBuilder); michael@0: if (NS_FAILED(rv)) { michael@0: MarkAsBrokenAndRequestSuspension(rv); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: char16_t* bufferCopy = new char16_t[aLength]; michael@0: memcpy(bufferCopy, aBuffer, aLength * sizeof(char16_t)); michael@0: michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: treeOp->Init(eTreeOpAppendText, bufferCopy, aLength, michael@0: deepTreeSurrogateParent ? deepTreeSurrogateParent : aParent); michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::appendIsindexPrompt(nsIContentHandle* aParent) michael@0: { michael@0: NS_PRECONDITION(aParent, "Null parent"); michael@0: michael@0: if (mBuilder) { michael@0: nsresult rv = nsHtml5TreeOperation::AppendIsindexPrompt( michael@0: static_cast(aParent), michael@0: mBuilder); michael@0: if (NS_FAILED(rv)) { michael@0: MarkAsBrokenAndRequestSuspension(rv); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: treeOp->Init(eTreeOpAppendIsindexPrompt, aParent); michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::appendComment(nsIContentHandle* aParent, char16_t* aBuffer, int32_t aStart, int32_t aLength) michael@0: { michael@0: NS_PRECONDITION(aBuffer, "Null buffer"); michael@0: NS_PRECONDITION(aParent, "Null parent"); michael@0: MOZ_ASSERT(!aStart, "aStart must always be zero."); michael@0: michael@0: if (deepTreeSurrogateParent) { michael@0: return; michael@0: } michael@0: michael@0: if (mBuilder) { michael@0: nsresult rv = nsHtml5TreeOperation::AppendComment( michael@0: static_cast(aParent), michael@0: aBuffer, // XXX aStart always ignored??? michael@0: aLength, michael@0: mBuilder); michael@0: if (NS_FAILED(rv)) { michael@0: MarkAsBrokenAndRequestSuspension(rv); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: char16_t* bufferCopy = new char16_t[aLength]; michael@0: memcpy(bufferCopy, aBuffer, aLength * sizeof(char16_t)); michael@0: michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: treeOp->Init(eTreeOpAppendComment, bufferCopy, aLength, aParent); michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::appendCommentToDocument(char16_t* aBuffer, int32_t aStart, int32_t aLength) michael@0: { michael@0: NS_PRECONDITION(aBuffer, "Null buffer"); michael@0: MOZ_ASSERT(!aStart, "aStart must always be zero."); michael@0: michael@0: if (mBuilder) { michael@0: nsresult rv = nsHtml5TreeOperation::AppendCommentToDocument( michael@0: aBuffer, // XXX aStart always ignored??? michael@0: aLength, michael@0: mBuilder); michael@0: if (NS_FAILED(rv)) { michael@0: MarkAsBrokenAndRequestSuspension(rv); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: char16_t* bufferCopy = new char16_t[aLength]; michael@0: memcpy(bufferCopy, aBuffer, aLength * sizeof(char16_t)); michael@0: michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: treeOp->Init(eTreeOpAppendCommentToDocument, bufferCopy, aLength); michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::addAttributesToElement(nsIContentHandle* aElement, nsHtml5HtmlAttributes* aAttributes) michael@0: { michael@0: NS_PRECONDITION(aElement, "Null element"); michael@0: NS_PRECONDITION(aAttributes, "Null attributes"); michael@0: michael@0: if (aAttributes == nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES) { michael@0: return; michael@0: } michael@0: michael@0: if (mBuilder) { michael@0: MOZ_ASSERT(aAttributes == tokenizer->GetAttributes(), michael@0: "Using attribute other than the tokenizer's to add to body or html."); michael@0: nsresult rv = nsHtml5TreeOperation::AddAttributes( michael@0: static_cast(aElement), michael@0: aAttributes, michael@0: mBuilder); michael@0: if (NS_FAILED(rv)) { michael@0: MarkAsBrokenAndRequestSuspension(rv); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: treeOp->Init(aElement, aAttributes); michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::markMalformedIfScript(nsIContentHandle* aElement) michael@0: { michael@0: NS_PRECONDITION(aElement, "Null element"); michael@0: michael@0: if (mBuilder) { michael@0: nsHtml5TreeOperation::MarkMalformedIfScript( michael@0: static_cast(aElement)); michael@0: return; michael@0: } michael@0: michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: treeOp->Init(eTreeOpMarkMalformedIfScript, aElement); michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::start(bool fragment) michael@0: { michael@0: mCurrentHtmlScriptIsAsyncOrDefer = false; michael@0: deepTreeSurrogateParent = nullptr; michael@0: #ifdef DEBUG michael@0: mActive = true; michael@0: #endif michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::end() michael@0: { michael@0: mOpQueue.Clear(); michael@0: #ifdef DEBUG michael@0: mActive = false; michael@0: #endif michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::appendDoctypeToDocument(nsIAtom* aName, nsString* aPublicId, nsString* aSystemId) michael@0: { michael@0: NS_PRECONDITION(aName, "Null name"); michael@0: michael@0: if (mBuilder) { michael@0: nsCOMPtr name = nsHtml5TreeOperation::Reget(aName); michael@0: nsresult rv = michael@0: nsHtml5TreeOperation::AppendDoctypeToDocument(name, michael@0: *aPublicId, michael@0: *aSystemId, michael@0: mBuilder); michael@0: if (NS_FAILED(rv)) { michael@0: MarkAsBrokenAndRequestSuspension(rv); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: treeOp->Init(aName, *aPublicId, *aSystemId); michael@0: // nsXMLContentSink can flush here, but what's the point? michael@0: // It can also interrupt here, but we can't. michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::elementPushed(int32_t aNamespace, nsIAtom* aName, nsIContentHandle* aElement) michael@0: { michael@0: NS_ASSERTION(aNamespace == kNameSpaceID_XHTML || aNamespace == kNameSpaceID_SVG || aNamespace == kNameSpaceID_MathML, "Element isn't HTML, SVG or MathML!"); michael@0: NS_ASSERTION(aName, "Element doesn't have local name!"); michael@0: NS_ASSERTION(aElement, "No element!"); michael@0: /* michael@0: * The frame constructor uses recursive algorithms, so it can't deal with michael@0: * arbitrarily deep trees. This is especially a problem on Windows where michael@0: * the permitted depth of the runtime stack is rather small. michael@0: * michael@0: * The following is a protection against author incompetence--not against michael@0: * malice. There are other ways to make the DOM deep anyway. michael@0: * michael@0: * The basic idea is that when the tree builder stack gets too deep, michael@0: * append operations no longer append to the node that the HTML parsing michael@0: * algorithm says they should but instead text nodes are append to the last michael@0: * element that was seen before a magic tree builder stack threshold was michael@0: * reached and element and comment nodes aren't appended to the DOM at all. michael@0: * michael@0: * However, for security reasons, non-child descendant text nodes inside an michael@0: * SVG script or style element should not become children. Also, non-cell michael@0: * table elements shouldn't be used as surrogate parents for user experience michael@0: * reasons. michael@0: */ michael@0: if (!deepTreeSurrogateParent && currentPtr >= MAX_REFLOW_DEPTH && michael@0: !(aName == nsHtml5Atoms::script || michael@0: aName == nsHtml5Atoms::table || michael@0: aName == nsHtml5Atoms::thead || michael@0: aName == nsHtml5Atoms::tfoot || michael@0: aName == nsHtml5Atoms::tbody || michael@0: aName == nsHtml5Atoms::tr || michael@0: aName == nsHtml5Atoms::colgroup || michael@0: aName == nsHtml5Atoms::style)) { michael@0: deepTreeSurrogateParent = aElement; michael@0: } michael@0: if (aNamespace != kNameSpaceID_XHTML) { michael@0: return; michael@0: } michael@0: if (aName == nsHtml5Atoms::body || aName == nsHtml5Atoms::frameset) { michael@0: if (mBuilder) { michael@0: // InnerHTML and DOMParser shouldn't start layout anyway michael@0: return; michael@0: } michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: treeOp->Init(eTreeOpStartLayout); michael@0: return; michael@0: } michael@0: if (aName == nsHtml5Atoms::input || michael@0: aName == nsHtml5Atoms::button) { michael@0: if (!formPointer) { michael@0: // If form inputs don't belong to a form, their state preservation michael@0: // won't work right without an append notification flush at this michael@0: // point. See bug 497861. michael@0: if (mBuilder) { michael@0: mBuilder->FlushPendingAppendNotifications(); michael@0: } else { michael@0: mOpQueue.AppendElement()->Init(eTreeOpFlushPendingAppendNotifications); michael@0: } michael@0: } michael@0: if (mBuilder) { michael@0: nsHtml5TreeOperation::DoneCreatingElement(static_cast(aElement)); michael@0: } else { michael@0: mOpQueue.AppendElement()->Init(eTreeOpDoneCreatingElement, aElement); michael@0: } michael@0: return; michael@0: } michael@0: if (aName == nsHtml5Atoms::audio || michael@0: aName == nsHtml5Atoms::video || michael@0: aName == nsHtml5Atoms::menuitem) { michael@0: if (mBuilder) { michael@0: nsHtml5TreeOperation::DoneCreatingElement(static_cast(aElement)); michael@0: } else { michael@0: mOpQueue.AppendElement()->Init(eTreeOpDoneCreatingElement, aElement); michael@0: } michael@0: return; michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::elementPopped(int32_t aNamespace, nsIAtom* aName, nsIContentHandle* aElement) michael@0: { michael@0: NS_ASSERTION(aNamespace == kNameSpaceID_XHTML || aNamespace == kNameSpaceID_SVG || aNamespace == kNameSpaceID_MathML, "Element isn't HTML, SVG or MathML!"); michael@0: NS_ASSERTION(aName, "Element doesn't have local name!"); michael@0: NS_ASSERTION(aElement, "No element!"); michael@0: if (deepTreeSurrogateParent && currentPtr <= MAX_REFLOW_DEPTH) { michael@0: deepTreeSurrogateParent = nullptr; michael@0: } michael@0: if (aNamespace == kNameSpaceID_MathML) { michael@0: return; michael@0: } michael@0: // we now have only SVG and HTML michael@0: if (aName == nsHtml5Atoms::script) { michael@0: if (mPreventScriptExecution) { michael@0: if (mBuilder) { michael@0: nsHtml5TreeOperation::PreventScriptExecution(static_cast(aElement)); michael@0: return; michael@0: } michael@0: mOpQueue.AppendElement()->Init(eTreeOpPreventScriptExecution, aElement); michael@0: return; michael@0: } michael@0: if (mBuilder) { michael@0: return; michael@0: } michael@0: if (mCurrentHtmlScriptIsAsyncOrDefer) { michael@0: NS_ASSERTION(aNamespace == kNameSpaceID_XHTML, michael@0: "Only HTML scripts may be async/defer."); michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: treeOp->Init(eTreeOpRunScriptAsyncDefer, aElement); michael@0: mCurrentHtmlScriptIsAsyncOrDefer = false; michael@0: return; michael@0: } michael@0: requestSuspension(); michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: treeOp->InitScript(aElement); michael@0: return; michael@0: } michael@0: if (aName == nsHtml5Atoms::title) { michael@0: if (mBuilder) { michael@0: nsHtml5TreeOperation::DoneAddingChildren(static_cast(aElement), mBuilder); michael@0: return; michael@0: } michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: treeOp->Init(eTreeOpDoneAddingChildren, aElement); michael@0: return; michael@0: } michael@0: if (aName == nsHtml5Atoms::style || (aNamespace == kNameSpaceID_XHTML && aName == nsHtml5Atoms::link)) { michael@0: if (mBuilder) { michael@0: MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript(), michael@0: "Scripts must be blocked."); michael@0: mBuilder->FlushPendingAppendNotifications(); michael@0: mBuilder->UpdateStyleSheet(static_cast(aElement)); michael@0: return; michael@0: } michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: treeOp->Init(eTreeOpUpdateStyleSheet, aElement); michael@0: return; michael@0: } michael@0: if (aNamespace == kNameSpaceID_SVG) { michael@0: if (aName == nsHtml5Atoms::svg) { michael@0: if (mBuilder) { michael@0: nsHtml5TreeOperation::SvgLoad(static_cast(aElement)); michael@0: return; michael@0: } michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: treeOp->Init(eTreeOpSvgLoad, aElement); michael@0: } michael@0: return; michael@0: } michael@0: // we now have only HTML michael@0: // Some HTML nodes need DoneAddingChildren() called to initialize michael@0: // properly (e.g. form state restoration). michael@0: // XXX expose ElementName group here and do switch michael@0: if (aName == nsHtml5Atoms::object || michael@0: aName == nsHtml5Atoms::applet) { michael@0: if (mBuilder) { michael@0: nsHtml5TreeOperation::DoneAddingChildren(static_cast(aElement), mBuilder); michael@0: return; michael@0: } michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: treeOp->Init(eTreeOpDoneAddingChildren, aElement); michael@0: return; michael@0: } michael@0: if (aName == nsHtml5Atoms::select || michael@0: aName == nsHtml5Atoms::textarea) { michael@0: if (!formPointer) { michael@0: // If form inputs don't belong to a form, their state preservation michael@0: // won't work right without an append notification flush at this michael@0: // point. See bug 497861 and bug 539895. michael@0: if (mBuilder) { michael@0: mBuilder->FlushPendingAppendNotifications(); michael@0: } else { michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: treeOp->Init(eTreeOpFlushPendingAppendNotifications); michael@0: } michael@0: } michael@0: if (mBuilder) { michael@0: nsHtml5TreeOperation::DoneAddingChildren(static_cast(aElement), mBuilder); michael@0: return; michael@0: } michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: treeOp->Init(eTreeOpDoneAddingChildren, aElement); michael@0: return; michael@0: } michael@0: if (aName == nsHtml5Atoms::meta && !fragment && !mBuilder) { michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: treeOp->Init(eTreeOpProcessMeta, aElement); michael@0: return; michael@0: } michael@0: return; michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::accumulateCharacters(const char16_t* aBuf, int32_t aStart, int32_t aLength) michael@0: { michael@0: int32_t newFillLen = charBufferLen + aLength; michael@0: if (newFillLen > charBuffer.length) { michael@0: int32_t newAllocLength = newFillLen + (newFillLen >> 1); michael@0: jArray newBuf = jArray::newJArray(newAllocLength); michael@0: memcpy(newBuf, charBuffer, sizeof(char16_t) * charBufferLen); michael@0: charBuffer = newBuf; michael@0: } michael@0: memcpy(charBuffer + charBufferLen, aBuf + aStart, sizeof(char16_t) * aLength); michael@0: charBufferLen = newFillLen; michael@0: } michael@0: michael@0: nsIContentHandle* michael@0: nsHtml5TreeBuilder::AllocateContentHandle() michael@0: { michael@0: if (MOZ_UNLIKELY(mBuilder)) { michael@0: MOZ_ASSUME_UNREACHABLE("Must never allocate a handle with builder."); michael@0: return nullptr; michael@0: } michael@0: if (mHandlesUsed == NS_HTML5_TREE_BUILDER_HANDLE_ARRAY_LENGTH) { michael@0: mOldHandles.AppendElement(mHandles.forget()); michael@0: mHandles = new nsIContent*[NS_HTML5_TREE_BUILDER_HANDLE_ARRAY_LENGTH]; michael@0: mHandlesUsed = 0; michael@0: } michael@0: #ifdef DEBUG michael@0: mHandles[mHandlesUsed] = (nsIContent*)0xC0DEDBAD; michael@0: #endif michael@0: return &mHandles[mHandlesUsed++]; michael@0: } michael@0: michael@0: bool michael@0: nsHtml5TreeBuilder::HasScript() michael@0: { michael@0: uint32_t len = mOpQueue.Length(); michael@0: if (!len) { michael@0: return false; michael@0: } michael@0: return mOpQueue.ElementAt(len - 1).IsRunScript(); michael@0: } michael@0: michael@0: bool michael@0: nsHtml5TreeBuilder::Flush(bool aDiscretionary) michael@0: { michael@0: if (MOZ_UNLIKELY(mBuilder)) { michael@0: MOZ_ASSUME_UNREACHABLE("Must never flush with builder."); michael@0: return false; michael@0: } michael@0: if (!aDiscretionary || michael@0: !(charBufferLen && michael@0: currentPtr >= 0 && michael@0: stack[currentPtr]->isFosterParenting())) { michael@0: // Don't flush text on discretionary flushes if the current element on michael@0: // the stack is a foster-parenting element and there's pending text, michael@0: // because flushing in that case would make the tree shape dependent on michael@0: // where the flush points fall. michael@0: flushCharacters(); michael@0: } michael@0: FlushLoads(); michael@0: if (mOpSink) { michael@0: bool hasOps = !mOpQueue.IsEmpty(); michael@0: if (hasOps) { michael@0: mOpSink->MoveOpsFrom(mOpQueue); michael@0: } michael@0: return hasOps; michael@0: } michael@0: // no op sink: throw away ops michael@0: mOpQueue.Clear(); michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::FlushLoads() michael@0: { michael@0: if (MOZ_UNLIKELY(mBuilder)) { michael@0: MOZ_ASSUME_UNREACHABLE("Must never flush loads with builder."); michael@0: return; michael@0: } michael@0: if (!mSpeculativeLoadQueue.IsEmpty()) { michael@0: mSpeculativeLoadStage->MoveSpeculativeLoadsFrom(mSpeculativeLoadQueue); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::SetDocumentCharset(nsACString& aCharset, michael@0: int32_t aCharsetSource) michael@0: { michael@0: if (mBuilder) { michael@0: mBuilder->SetDocumentCharsetAndSource(aCharset, aCharsetSource); michael@0: } else if (mSpeculativeLoadStage) { michael@0: mSpeculativeLoadQueue.AppendElement()->InitSetDocumentCharset( michael@0: aCharset, aCharsetSource); michael@0: } else { michael@0: mOpQueue.AppendElement()->Init( michael@0: eTreeOpSetDocumentCharset, aCharset, aCharsetSource); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::StreamEnded() michael@0: { michael@0: MOZ_ASSERT(!mBuilder, "Must not call StreamEnded with builder."); michael@0: MOZ_ASSERT(!fragment, "Must not parse fragments off the main thread."); michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: treeOp->Init(eTreeOpStreamEnded); michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::NeedsCharsetSwitchTo(const nsACString& aCharset, michael@0: int32_t aCharsetSource, michael@0: int32_t aLineNumber) michael@0: { michael@0: if (MOZ_UNLIKELY(mBuilder)) { michael@0: MOZ_ASSUME_UNREACHABLE("Must never switch charset with builder."); michael@0: return; michael@0: } michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: treeOp->Init(eTreeOpNeedsCharsetSwitchTo, michael@0: aCharset, michael@0: aCharsetSource, michael@0: aLineNumber); michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::MaybeComplainAboutCharset(const char* aMsgId, michael@0: bool aError, michael@0: int32_t aLineNumber) michael@0: { michael@0: if (MOZ_UNLIKELY(mBuilder)) { michael@0: MOZ_ASSUME_UNREACHABLE("Must never complain about charset with builder."); michael@0: return; michael@0: } michael@0: mOpQueue.AppendElement()->Init(aMsgId, aError, aLineNumber); michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::AddSnapshotToScript(nsAHtml5TreeBuilderState* aSnapshot, int32_t aLine) michael@0: { michael@0: if (MOZ_UNLIKELY(mBuilder)) { michael@0: MOZ_ASSUME_UNREACHABLE("Must never use snapshots with builder."); michael@0: return; michael@0: } michael@0: NS_PRECONDITION(HasScript(), "No script to add a snapshot to!"); michael@0: NS_PRECONDITION(aSnapshot, "Got null snapshot."); michael@0: mOpQueue.ElementAt(mOpQueue.Length() - 1).SetSnapshot(aSnapshot, aLine); michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::DropHandles() michael@0: { michael@0: MOZ_ASSERT(!mBuilder, "Must not drop handles with builder."); michael@0: mOldHandles.Clear(); michael@0: mHandlesUsed = 0; michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::MarkAsBroken(nsresult aRv) michael@0: { michael@0: if (MOZ_UNLIKELY(mBuilder)) { michael@0: MOZ_ASSUME_UNREACHABLE("Must not call this with builder."); michael@0: return; michael@0: } michael@0: mOpQueue.Clear(); // Previous ops don't matter anymore michael@0: mOpQueue.AppendElement()->Init(aRv); michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::StartPlainTextViewSource(const nsAutoString& aTitle) michael@0: { michael@0: MOZ_ASSERT(!mBuilder, "Must not view source with builder."); michael@0: startTag(nsHtml5ElementName::ELT_TITLE, michael@0: nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES, michael@0: false); michael@0: michael@0: // XUL will add the "Source of: " prefix. michael@0: uint32_t length = aTitle.Length(); michael@0: if (length > INT32_MAX) { michael@0: length = INT32_MAX; michael@0: } michael@0: characters(aTitle.get(), 0, (int32_t)length); michael@0: endTag(nsHtml5ElementName::ELT_TITLE); michael@0: michael@0: startTag(nsHtml5ElementName::ELT_LINK, michael@0: nsHtml5ViewSourceUtils::NewLinkAttributes(), michael@0: false); michael@0: michael@0: startTag(nsHtml5ElementName::ELT_BODY, michael@0: nsHtml5ViewSourceUtils::NewBodyAttributes(), michael@0: false); michael@0: michael@0: StartPlainTextBody(); michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::StartPlainText() michael@0: { michael@0: MOZ_ASSERT(!mBuilder, "Must not view source with builder."); michael@0: startTag(nsHtml5ElementName::ELT_LINK, michael@0: nsHtml5PlainTextUtils::NewLinkAttributes(), michael@0: false); michael@0: michael@0: StartPlainTextBody(); michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::StartPlainTextBody() michael@0: { michael@0: MOZ_ASSERT(!mBuilder, "Must not view source with builder."); michael@0: startTag(nsHtml5ElementName::ELT_PRE, michael@0: nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES, michael@0: false); michael@0: needToDropLF = false; michael@0: } michael@0: michael@0: // DocumentModeHandler michael@0: void michael@0: nsHtml5TreeBuilder::documentMode(nsHtml5DocumentMode m) michael@0: { michael@0: if (mBuilder) { michael@0: mBuilder->SetDocumentMode(m); michael@0: return; michael@0: } michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: treeOp->Init(m); michael@0: } michael@0: michael@0: nsIContentHandle* michael@0: nsHtml5TreeBuilder::getDocumentFragmentForTemplate(nsIContentHandle* aTemplate) michael@0: { michael@0: if (mBuilder) { michael@0: return nsHtml5TreeOperation::GetDocumentFragmentForTemplate(static_cast(aTemplate)); michael@0: } michael@0: nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); michael@0: NS_ASSERTION(treeOp, "Tree op allocation failed."); michael@0: nsIContentHandle* fragHandle = AllocateContentHandle(); michael@0: treeOp->Init(eTreeOpGetDocumentFragmentForTemplate, aTemplate, fragHandle); michael@0: return fragHandle; michael@0: } michael@0: michael@0: nsIContentHandle* michael@0: nsHtml5TreeBuilder::getFormPointerForContext(nsIContentHandle* aContext) michael@0: { michael@0: MOZ_ASSERT(mBuilder, "Must have builder."); michael@0: if (!aContext) { michael@0: return nullptr; michael@0: } michael@0: michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: // aContext must always be an element that already exists michael@0: // in the document. michael@0: nsIContent* contextNode = static_cast(aContext); michael@0: nsIContent* currentAncestor = contextNode; michael@0: michael@0: // We traverse the ancestors of the context node to find the nearest michael@0: // form pointer. This traversal is why aContext must not be an emtpy handle. michael@0: nsIContent* nearestForm = nullptr; michael@0: while (currentAncestor) { michael@0: if (currentAncestor->IsHTML(nsGkAtoms::form)) { michael@0: nearestForm = currentAncestor; michael@0: break; michael@0: } michael@0: currentAncestor = currentAncestor->GetParent(); michael@0: } michael@0: michael@0: if (!nearestForm) { michael@0: return nullptr; michael@0: } michael@0: michael@0: return nearestForm; michael@0: } michael@0: michael@0: // Error reporting michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::EnableViewSource(nsHtml5Highlighter* aHighlighter) michael@0: { michael@0: MOZ_ASSERT(!mBuilder, "Must not view source with builder."); michael@0: mViewSource = aHighlighter; michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errStrayStartTag(nsIAtom* aName) michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource)) { michael@0: mViewSource->AddErrorToCurrentRun("errStrayStartTag2", aName); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errStrayEndTag(nsIAtom* aName) michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource)) { michael@0: mViewSource->AddErrorToCurrentRun("errStrayEndTag", aName); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errUnclosedElements(int32_t aIndex, nsIAtom* aName) michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource)) { michael@0: mViewSource->AddErrorToCurrentRun("errUnclosedElements", aName); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errUnclosedElementsImplied(int32_t aIndex, nsIAtom* aName) michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource)) { michael@0: mViewSource->AddErrorToCurrentRun("errUnclosedElementsImplied", michael@0: aName); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errUnclosedElementsCell(int32_t aIndex) michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource)) { michael@0: mViewSource->AddErrorToCurrentRun("errUnclosedElementsCell"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errStrayDoctype() michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource)) { michael@0: mViewSource->AddErrorToCurrentRun("errStrayDoctype"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errAlmostStandardsDoctype() michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource) && !isSrcdocDocument) { michael@0: mViewSource->AddErrorToCurrentRun("errAlmostStandardsDoctype"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errQuirkyDoctype() michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource) && !isSrcdocDocument) { michael@0: mViewSource->AddErrorToCurrentRun("errQuirkyDoctype"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errNonSpaceInTrailer() michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource)) { michael@0: mViewSource->AddErrorToCurrentRun("errNonSpaceInTrailer"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errNonSpaceAfterFrameset() michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource)) { michael@0: mViewSource->AddErrorToCurrentRun("errNonSpaceAfterFrameset"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errNonSpaceInFrameset() michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource)) { michael@0: mViewSource->AddErrorToCurrentRun("errNonSpaceInFrameset"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errNonSpaceAfterBody() michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource)) { michael@0: mViewSource->AddErrorToCurrentRun("errNonSpaceAfterBody"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errNonSpaceInColgroupInFragment() michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource)) { michael@0: mViewSource->AddErrorToCurrentRun("errNonSpaceInColgroupInFragment"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errNonSpaceInNoscriptInHead() michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource)) { michael@0: mViewSource->AddErrorToCurrentRun("errNonSpaceInNoscriptInHead"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errFooBetweenHeadAndBody(nsIAtom* aName) michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource)) { michael@0: mViewSource->AddErrorToCurrentRun("errFooBetweenHeadAndBody", aName); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errStartTagWithoutDoctype() michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource) && !isSrcdocDocument) { michael@0: mViewSource->AddErrorToCurrentRun("errStartTagWithoutDoctype"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errNoSelectInTableScope() michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource)) { michael@0: mViewSource->AddErrorToCurrentRun("errNoSelectInTableScope"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errStartSelectWhereEndSelectExpected() michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource)) { michael@0: mViewSource->AddErrorToCurrentRun( michael@0: "errStartSelectWhereEndSelectExpected"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errStartTagWithSelectOpen(nsIAtom* aName) michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource)) { michael@0: mViewSource->AddErrorToCurrentRun("errStartTagWithSelectOpen", aName); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errBadStartTagInHead(nsIAtom* aName) michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource)) { michael@0: mViewSource->AddErrorToCurrentRun("errBadStartTagInHead2", aName); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errImage() michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource)) { michael@0: mViewSource->AddErrorToCurrentRun("errImage"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errIsindex() michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource)) { michael@0: mViewSource->AddErrorToCurrentRun("errIsindex"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errFooSeenWhenFooOpen(nsIAtom* aName) michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource)) { michael@0: mViewSource->AddErrorToCurrentRun("errFooSeenWhenFooOpen", aName); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errHeadingWhenHeadingOpen() michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource)) { michael@0: mViewSource->AddErrorToCurrentRun("errHeadingWhenHeadingOpen"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errFramesetStart() michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource)) { michael@0: mViewSource->AddErrorToCurrentRun("errFramesetStart"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errNoCellToClose() michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource)) { michael@0: mViewSource->AddErrorToCurrentRun("errNoCellToClose"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errStartTagInTable(nsIAtom* aName) michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource)) { michael@0: mViewSource->AddErrorToCurrentRun("errStartTagInTable", aName); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errFormWhenFormOpen() michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource)) { michael@0: mViewSource->AddErrorToCurrentRun("errFormWhenFormOpen"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errTableSeenWhileTableOpen() michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource)) { michael@0: mViewSource->AddErrorToCurrentRun("errTableSeenWhileTableOpen"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errStartTagInTableBody(nsIAtom* aName) michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource)) { michael@0: mViewSource->AddErrorToCurrentRun("errStartTagInTableBody", aName); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errEndTagSeenWithoutDoctype() michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource) && !isSrcdocDocument) { michael@0: mViewSource->AddErrorToCurrentRun("errEndTagSeenWithoutDoctype"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errEndTagAfterBody() michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource)) { michael@0: mViewSource->AddErrorToCurrentRun("errEndTagAfterBody"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errEndTagSeenWithSelectOpen(nsIAtom* aName) michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource)) { michael@0: mViewSource->AddErrorToCurrentRun("errEndTagSeenWithSelectOpen", michael@0: aName); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errGarbageInColgroup() michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource)) { michael@0: mViewSource->AddErrorToCurrentRun("errGarbageInColgroup"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errEndTagBr() michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource)) { michael@0: mViewSource->AddErrorToCurrentRun("errEndTagBr"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errNoElementToCloseButEndTagSeen(nsIAtom* aName) michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource)) { michael@0: mViewSource->AddErrorToCurrentRun( michael@0: "errNoElementToCloseButEndTagSeen", aName); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errHtmlStartTagInForeignContext(nsIAtom* aName) michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource)) { michael@0: mViewSource->AddErrorToCurrentRun("errHtmlStartTagInForeignContext", michael@0: aName); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errTableClosedWhileCaptionOpen() michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource)) { michael@0: mViewSource->AddErrorToCurrentRun("errTableClosedWhileCaptionOpen"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errNoTableRowToClose() michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource)) { michael@0: mViewSource->AddErrorToCurrentRun("errNoTableRowToClose"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errNonSpaceInTable() michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource)) { michael@0: mViewSource->AddErrorToCurrentRun("errNonSpaceInTable"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errUnclosedChildrenInRuby() michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource)) { michael@0: mViewSource->AddErrorToCurrentRun("errUnclosedChildrenInRuby"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errStartTagSeenWithoutRuby(nsIAtom* aName) michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource)) { michael@0: mViewSource->AddErrorToCurrentRun("errStartTagSeenWithoutRuby", michael@0: aName); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errSelfClosing() michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource)) { michael@0: mViewSource->AddErrorToCurrentSlash("errSelfClosing"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errNoCheckUnclosedElementsOnStack() michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource)) { michael@0: mViewSource->AddErrorToCurrentRun( michael@0: "errNoCheckUnclosedElementsOnStack"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errEndTagDidNotMatchCurrentOpenElement(nsIAtom* aName, michael@0: nsIAtom* aOther) michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource)) { michael@0: mViewSource->AddErrorToCurrentRun( michael@0: "errEndTagDidNotMatchCurrentOpenElement", aName, aOther); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errEndTagViolatesNestingRules(nsIAtom* aName) michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource)) { michael@0: mViewSource->AddErrorToCurrentRun("errEndTagViolatesNestingRules", aName); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHtml5TreeBuilder::errEndWithUnclosedElements(nsIAtom* aName) michael@0: { michael@0: if (MOZ_UNLIKELY(mViewSource)) { michael@0: mViewSource->AddErrorToCurrentRun("errEndWithUnclosedElements", aName); michael@0: } michael@0: }