/* * Copyright (c) 2007 Henri Sivonen * Copyright (c) 2007-2017 Mozilla Foundation * Portions of comments Copyright 2004-2008 Apple Computer, Inc., Mozilla * Foundation, and Opera Software ASA. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /* * THIS IS A GENERATED FILE. PLEASE DO NOT EDIT. * Please edit TreeBuilder.java instead and regenerate. */ #define nsHtml5TreeBuilder_cpp__ #include "nsContentUtils.h" #include "nsAtom.h" #include "nsHtml5AtomTable.h" #include "nsHtml5String.h" #include "nsNameSpaceManager.h" #include "nsIContent.h" #include "nsTraceRefcnt.h" #include "jArray.h" #include "nsHtml5DocumentMode.h" #include "nsHtml5ArrayCopy.h" #include "nsHtml5Parser.h" #include "nsGkAtoms.h" #include "nsHtml5TreeOperation.h" #include "nsHtml5StateSnapshot.h" #include "nsHtml5StackNode.h" #include "nsHtml5TreeOpExecutor.h" #include "nsHtml5StreamParser.h" #include "nsAHtml5TreeBuilderState.h" #include "nsHtml5Highlighter.h" #include "nsHtml5PlainTextUtils.h" #include "nsHtml5ViewSourceUtils.h" #include "mozilla/ImportScanner.h" #include "mozilla/Likely.h" #include "nsIContentHandle.h" #include "nsHtml5OplessBuilder.h" #include "nsHtml5AttributeName.h" #include "nsHtml5ElementName.h" #include "nsHtml5Tokenizer.h" #include "nsHtml5MetaScanner.h" #include "nsHtml5StackNode.h" #include "nsHtml5UTF16Buffer.h" #include "nsHtml5StateSnapshot.h" #include "nsHtml5Portability.h" #include "nsHtml5TreeBuilder.h" char16_t nsHtml5TreeBuilder::REPLACEMENT_CHARACTER[] = {0xfffd}; static const char* const QUIRKY_PUBLIC_IDS_DATA[] = { "+//silmaril//dtd html pro v0r11 19970101//", "-//advasoft ltd//dtd html 3.0 aswedit + extensions//", "-//as//dtd html 3.0 aswedit + extensions//", "-//ietf//dtd html 2.0 level 1//", "-//ietf//dtd html 2.0 level 2//", "-//ietf//dtd html 2.0 strict level 1//", "-//ietf//dtd html 2.0 strict level 2//", "-//ietf//dtd html 2.0 strict//", "-//ietf//dtd html 2.0//", "-//ietf//dtd html 2.1e//", "-//ietf//dtd html 3.0//", "-//ietf//dtd html 3.2 final//", "-//ietf//dtd html 3.2//", "-//ietf//dtd html 3//", "-//ietf//dtd html level 0//", "-//ietf//dtd html level 1//", "-//ietf//dtd html level 2//", "-//ietf//dtd html level 3//", "-//ietf//dtd html strict level 0//", "-//ietf//dtd html strict level 1//", "-//ietf//dtd html strict level 2//", "-//ietf//dtd html strict level 3//", "-//ietf//dtd html strict//", "-//ietf//dtd html//", "-//metrius//dtd metrius presentational//", "-//microsoft//dtd internet explorer 2.0 html strict//", "-//microsoft//dtd internet explorer 2.0 html//", "-//microsoft//dtd internet explorer 2.0 tables//", "-//microsoft//dtd internet explorer 3.0 html strict//", "-//microsoft//dtd internet explorer 3.0 html//", "-//microsoft//dtd internet explorer 3.0 tables//", "-//netscape comm. corp.//dtd html//", "-//netscape comm. corp.//dtd strict html//", "-//o'reilly and associates//dtd html 2.0//", "-//o'reilly and associates//dtd html extended 1.0//", "-//o'reilly and associates//dtd html extended relaxed 1.0//", "-//softquad software//dtd hotmetal pro 6.0::19990601::extensions to html " "4.0//", "-//softquad//dtd hotmetal pro 4.0::19971010::extensions to html 4.0//", "-//spyglass//dtd html 2.0 extended//", "-//sq//dtd html 2.0 hotmetal + extensions//", "-//sun microsystems corp.//dtd hotjava html//", "-//sun microsystems corp.//dtd hotjava strict html//", "-//w3c//dtd html 3 1995-03-24//", "-//w3c//dtd html 3.2 draft//", "-//w3c//dtd html 3.2 final//", "-//w3c//dtd html 3.2//", "-//w3c//dtd html 3.2s draft//", "-//w3c//dtd html 4.0 frameset//", "-//w3c//dtd html 4.0 transitional//", "-//w3c//dtd html experimental 19960712//", "-//w3c//dtd html experimental 970421//", "-//w3c//dtd w3 html//", "-//w3o//dtd w3 html 3.0//", "-//webtechs//dtd mozilla html 2.0//", "-//webtechs//dtd mozilla html//"}; staticJArray nsHtml5TreeBuilder::QUIRKY_PUBLIC_IDS = { QUIRKY_PUBLIC_IDS_DATA, MOZ_ARRAY_LENGTH(QUIRKY_PUBLIC_IDS_DATA)}; void nsHtml5TreeBuilder::startTokenization(nsHtml5Tokenizer* self) { tokenizer = self; stackNodes = jArray::newJArray(64); stack = jArray::newJArray(64); templateModeStack = jArray::newJArray(64); listOfActiveFormattingElements = jArray::newJArray(64); needToDropLF = false; originalMode = INITIAL; templateModePtr = -1; stackNodesIdx = 0; numStackNodes = 0; currentPtr = -1; listPtr = -1; formPointer = nullptr; headPointer = nullptr; start(fragment); charBufferLen = 0; charBuffer = nullptr; framesetOk = true; if (fragment) { nsIContentHandle* elt; if (contextNode) { elt = contextNode; } else { elt = createHtmlElementSetAsRoot(tokenizer->emptyAttributes()); } if (contextNamespace == kNameSpaceID_SVG) { nsHtml5ElementName* elementName = nsHtml5ElementName::ELT_SVG; if (nsGkAtoms::title == contextName || nsGkAtoms::desc == contextName || nsGkAtoms::foreignObject == contextName) { elementName = nsHtml5ElementName::ELT_FOREIGNOBJECT; } nsHtml5StackNode* node = createStackNode(elementName, elementName->getCamelCaseName(), elt); currentPtr++; stack[currentPtr] = node; tokenizer->setState(nsHtml5Tokenizer::DATA); mode = FRAMESET_OK; } else if (contextNamespace == kNameSpaceID_MathML) { nsHtml5ElementName* elementName = nsHtml5ElementName::ELT_MATH; if (nsGkAtoms::mi_ == contextName || nsGkAtoms::mo_ == contextName || nsGkAtoms::mn_ == contextName || nsGkAtoms::ms_ == contextName || nsGkAtoms::mtext_ == contextName) { elementName = nsHtml5ElementName::ELT_MTEXT; } else if (nsGkAtoms::annotation_xml_ == contextName) { elementName = nsHtml5ElementName::ELT_ANNOTATION_XML; } nsHtml5StackNode* node = createStackNode(elementName, elt, elementName->getName(), false); currentPtr++; stack[currentPtr] = node; tokenizer->setState(nsHtml5Tokenizer::DATA); mode = FRAMESET_OK; } else { nsHtml5StackNode* node = createStackNode(nsHtml5ElementName::ELT_HTML, elt); currentPtr++; stack[currentPtr] = node; if (nsGkAtoms::_template == contextName) { pushTemplateMode(IN_TEMPLATE); } resetTheInsertionMode(); formPointer = getFormPointerForContext(contextNode); if (nsGkAtoms::title == contextName || nsGkAtoms::textarea == contextName) { tokenizer->setState(nsHtml5Tokenizer::RCDATA); } else if (nsGkAtoms::style == contextName || nsGkAtoms::xmp == contextName || nsGkAtoms::iframe == contextName || nsGkAtoms::noembed == contextName || nsGkAtoms::noframes == contextName || (scriptingEnabled && nsGkAtoms::noscript == contextName)) { tokenizer->setState(nsHtml5Tokenizer::RAWTEXT); } else if (nsGkAtoms::plaintext == contextName) { tokenizer->setState(nsHtml5Tokenizer::PLAINTEXT); } else if (nsGkAtoms::script == contextName) { tokenizer->setState(nsHtml5Tokenizer::SCRIPT_DATA); } else { tokenizer->setState(nsHtml5Tokenizer::DATA); } } } else { mode = INITIAL; if (tokenizer->isViewingXmlSource()) { nsIContentHandle* elt = createElement( kNameSpaceID_SVG, nsGkAtoms::svg, tokenizer->emptyAttributes(), nullptr, svgCreator(NS_NewSVGSVGElement)); nsHtml5StackNode* node = createStackNode(nsHtml5ElementName::ELT_SVG, nsGkAtoms::svg, elt); currentPtr++; stack[currentPtr] = node; } } } void nsHtml5TreeBuilder::doctype(nsAtom* name, nsHtml5String publicIdentifier, nsHtml5String systemIdentifier, bool forceQuirks) { needToDropLF = false; if (!isInForeign() && mode == INITIAL) { nsHtml5String emptyString = nsHtml5Portability::newEmptyString(); appendDoctypeToDocument(!name ? nsGkAtoms::_empty : name, !publicIdentifier ? emptyString : publicIdentifier, !systemIdentifier ? emptyString : systemIdentifier); emptyString.Release(); if (isQuirky(name, publicIdentifier, systemIdentifier, forceQuirks)) { errQuirkyDoctype(); documentModeInternal(QUIRKS_MODE, publicIdentifier, systemIdentifier); } else if (isAlmostStandards(publicIdentifier, systemIdentifier)) { errAlmostStandardsDoctype(); documentModeInternal(ALMOST_STANDARDS_MODE, publicIdentifier, systemIdentifier); } else { documentModeInternal(STANDARDS_MODE, publicIdentifier, systemIdentifier); } mode = BEFORE_HTML; return; } errStrayDoctype(); return; } void nsHtml5TreeBuilder::comment(char16_t* buf, int32_t start, int32_t length) { needToDropLF = false; if (!isInForeign()) { switch (mode) { case INITIAL: case BEFORE_HTML: case AFTER_AFTER_BODY: case AFTER_AFTER_FRAMESET: { appendCommentToDocument(buf, start, length); return; } case AFTER_BODY: { flushCharacters(); appendComment(stack[0]->node, buf, start, length); return; } default: { break; } } } flushCharacters(); appendComment(stack[currentPtr]->node, buf, start, length); return; } void nsHtml5TreeBuilder::characters(const char16_t* buf, int32_t start, int32_t length) { if (tokenizer->isViewingXmlSource()) { return; } if (needToDropLF) { needToDropLF = false; if (buf[start] == '\n') { start++; length--; if (!length) { return; } } } switch (mode) { case IN_BODY: case IN_CELL: case IN_CAPTION: { if (!isInForeignButNotHtmlOrMathTextIntegrationPoint()) { reconstructTheActiveFormattingElements(); } [[fallthrough]]; } case TEXT: { accumulateCharacters(buf, start, length); return; } case IN_TABLE: case IN_TABLE_BODY: case IN_ROW: { accumulateCharactersForced(buf, start, length); return; } default: { int32_t end = start + length; for (int32_t i = start; i < end; i++) { switch (buf[i]) { case ' ': case '\t': case '\n': case '\r': case '\f': { switch (mode) { case INITIAL: case BEFORE_HTML: case BEFORE_HEAD: { start = i + 1; continue; } case IN_HEAD: case IN_HEAD_NOSCRIPT: case AFTER_HEAD: case IN_COLUMN_GROUP: case IN_FRAMESET: case AFTER_FRAMESET: { continue; } case FRAMESET_OK: case IN_TEMPLATE: case IN_BODY: case IN_CELL: case IN_CAPTION: { if (start < i) { accumulateCharacters(buf, start, i - start); start = i; } if (!isInForeignButNotHtmlOrMathTextIntegrationPoint()) { flushCharacters(); reconstructTheActiveFormattingElements(); } NS_HTML5_BREAK(charactersloop); } case IN_SELECT: case IN_SELECT_IN_TABLE: { NS_HTML5_BREAK(charactersloop); } case IN_TABLE: case IN_TABLE_BODY: case IN_ROW: { accumulateCharactersForced(buf, i, 1); start = i + 1; continue; } case AFTER_BODY: case AFTER_AFTER_BODY: case AFTER_AFTER_FRAMESET: { if (start < i) { accumulateCharacters(buf, start, i - start); start = i; } flushCharacters(); reconstructTheActiveFormattingElements(); continue; } } MOZ_FALLTHROUGH_ASSERT(); } default: { switch (mode) { case INITIAL: { documentModeInternal(QUIRKS_MODE, nullptr, nullptr); mode = BEFORE_HTML; i--; continue; } case BEFORE_HTML: { appendHtmlElementToDocumentAndPush(); mode = BEFORE_HEAD; i--; continue; } case BEFORE_HEAD: { if (start < i) { accumulateCharacters(buf, start, i - start); start = i; } flushCharacters(); appendToCurrentNodeAndPushHeadElement( nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES); mode = IN_HEAD; i--; continue; } case IN_HEAD: { if (start < i) { accumulateCharacters(buf, start, i - start); start = i; } flushCharacters(); pop(); mode = AFTER_HEAD; i--; continue; } case IN_HEAD_NOSCRIPT: { if (start < i) { accumulateCharacters(buf, start, i - start); start = i; } errNonSpaceInNoscriptInHead(); flushCharacters(); pop(); mode = IN_HEAD; i--; continue; } case AFTER_HEAD: { if (start < i) { accumulateCharacters(buf, start, i - start); start = i; } flushCharacters(); appendToCurrentNodeAndPushBodyElement(); mode = FRAMESET_OK; i--; continue; } case FRAMESET_OK: { framesetOk = false; mode = IN_BODY; i--; continue; } case IN_TEMPLATE: case IN_BODY: case IN_CELL: case IN_CAPTION: { if (start < i) { accumulateCharacters(buf, start, i - start); start = i; } if (!isInForeignButNotHtmlOrMathTextIntegrationPoint()) { flushCharacters(); reconstructTheActiveFormattingElements(); } NS_HTML5_BREAK(charactersloop); } case IN_TABLE: case IN_TABLE_BODY: case IN_ROW: { accumulateCharactersForced(buf, i, 1); start = i + 1; continue; } case IN_COLUMN_GROUP: { if (start < i) { accumulateCharacters(buf, start, i - start); start = i; } if (!currentPtr || stack[currentPtr]->getGroup() == nsHtml5TreeBuilder::TEMPLATE) { errNonSpaceInColgroupInFragment(); start = i + 1; continue; } flushCharacters(); pop(); mode = IN_TABLE; i--; continue; } case IN_SELECT: case IN_SELECT_IN_TABLE: { NS_HTML5_BREAK(charactersloop); } case AFTER_BODY: { errNonSpaceAfterBody(); mode = framesetOk ? FRAMESET_OK : IN_BODY; i--; continue; } case IN_FRAMESET: { if (start < i) { accumulateCharacters(buf, start, i - start); } errNonSpaceInFrameset(); start = i + 1; continue; } case AFTER_FRAMESET: { if (start < i) { accumulateCharacters(buf, start, i - start); } errNonSpaceAfterFrameset(); start = i + 1; continue; } case AFTER_AFTER_BODY: { errNonSpaceInTrailer(); mode = framesetOk ? FRAMESET_OK : IN_BODY; i--; continue; } case AFTER_AFTER_FRAMESET: { if (start < i) { accumulateCharacters(buf, start, i - start); } errNonSpaceInTrailer(); start = i + 1; continue; } } } } } charactersloop_end:; if (start < end) { accumulateCharacters(buf, start, end - start); } } } } void nsHtml5TreeBuilder::zeroOriginatingReplacementCharacter() { if (mode == TEXT) { accumulateCharacters(REPLACEMENT_CHARACTER, 0, 1); return; } if (currentPtr >= 0) { if (isSpecialParentInForeign(stack[currentPtr])) { return; } accumulateCharacters(REPLACEMENT_CHARACTER, 0, 1); } } void nsHtml5TreeBuilder::eof() { flushCharacters(); for (;;) { switch (mode) { case INITIAL: { documentModeInternal(QUIRKS_MODE, nullptr, nullptr); mode = BEFORE_HTML; continue; } case BEFORE_HTML: { appendHtmlElementToDocumentAndPush(); mode = BEFORE_HEAD; continue; } case BEFORE_HEAD: { appendToCurrentNodeAndPushHeadElement( nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES); mode = IN_HEAD; continue; } case IN_HEAD: { while (currentPtr > 0) { popOnEof(); } mode = AFTER_HEAD; continue; } case IN_HEAD_NOSCRIPT: { while (currentPtr > 1) { popOnEof(); } mode = IN_HEAD; continue; } case AFTER_HEAD: { appendToCurrentNodeAndPushBodyElement(); mode = IN_BODY; continue; } case IN_TABLE_BODY: case IN_ROW: case IN_TABLE: case IN_SELECT_IN_TABLE: case IN_SELECT: case IN_COLUMN_GROUP: case FRAMESET_OK: case IN_CAPTION: case IN_CELL: case IN_BODY: { if (isTemplateModeStackEmpty()) { NS_HTML5_BREAK(eofloop); } [[fallthrough]]; } case IN_TEMPLATE: { int32_t eltPos = findLast(nsGkAtoms::_template); if (eltPos == nsHtml5TreeBuilder::NOT_FOUND_ON_STACK) { MOZ_ASSERT(fragment); NS_HTML5_BREAK(eofloop); } if (MOZ_UNLIKELY(mViewSource)) { errUnclosedElements(eltPos, nsGkAtoms::_template); } while (currentPtr >= eltPos) { pop(); } clearTheListOfActiveFormattingElementsUpToTheLastMarker(); popTemplateMode(); resetTheInsertionMode(); continue; } case TEXT: { if (originalMode == AFTER_HEAD) { popOnEof(); } popOnEof(); mode = originalMode; continue; } case IN_FRAMESET: { NS_HTML5_BREAK(eofloop); } case AFTER_BODY: case AFTER_FRAMESET: case AFTER_AFTER_BODY: case AFTER_AFTER_FRAMESET: default: { NS_HTML5_BREAK(eofloop); } } } eofloop_end:; while (currentPtr > 0) { popOnEof(); } if (!fragment) { popOnEof(); } } void nsHtml5TreeBuilder::endTokenization() { formPointer = nullptr; headPointer = nullptr; contextName = nullptr; contextNode = nullptr; templateModeStack = nullptr; if (stack) { while (currentPtr > -1) { stack[currentPtr]->release(this); currentPtr--; } stack = nullptr; } if (listOfActiveFormattingElements) { while (listPtr > -1) { if (listOfActiveFormattingElements[listPtr]) { listOfActiveFormattingElements[listPtr]->release(this); } listPtr--; } listOfActiveFormattingElements = nullptr; } if (stackNodes) { for (int32_t i = 0; i < numStackNodes; i++) { MOZ_ASSERT(stackNodes[i]->isUnused()); delete stackNodes[i]; } numStackNodes = 0; stackNodesIdx = 0; stackNodes = nullptr; } charBuffer = nullptr; end(); } void nsHtml5TreeBuilder::startTag(nsHtml5ElementName* elementName, nsHtml5HtmlAttributes* attributes, bool selfClosing) { flushCharacters(); int32_t eltPos; needToDropLF = false; starttagloop: for (;;) { int32_t group = elementName->getGroup(); nsAtom* name = elementName->getName(); if (isInForeign()) { nsHtml5StackNode* currentNode = stack[currentPtr]; int32_t currNs = currentNode->ns; if (!(currentNode->isHtmlIntegrationPoint() || (currNs == kNameSpaceID_MathML && ((currentNode->getGroup() == MI_MO_MN_MS_MTEXT && group != MGLYPH_OR_MALIGNMARK) || (currentNode->getGroup() == ANNOTATION_XML && group == SVG))))) { switch (group) { case B_OR_BIG_OR_CODE_OR_EM_OR_I_OR_S_OR_SMALL_OR_STRIKE_OR_STRONG_OR_TT_OR_U: case DIV_OR_BLOCKQUOTE_OR_CENTER_OR_MENU: case BODY: case BR: case RUBY_OR_SPAN_OR_SUB_OR_SUP_OR_VAR: case DD_OR_DT: case UL_OR_OL_OR_DL: case EMBED: case IMG: case H1_OR_H2_OR_H3_OR_H4_OR_H5_OR_H6: case HEAD: case HR: case LI: case META: case NOBR: case P: case PRE_OR_LISTING: case TABLE: case FONT: { if (!(group == FONT && !(attributes->contains(nsHtml5AttributeName::ATTR_COLOR) || attributes->contains(nsHtml5AttributeName::ATTR_FACE) || attributes->contains(nsHtml5AttributeName::ATTR_SIZE)))) { errHtmlStartTagInForeignContext(name); if (!fragment) { while (!isSpecialParentInForeign(stack[currentPtr])) { popForeign(-1, -1); } NS_HTML5_CONTINUE(starttagloop); } } [[fallthrough]]; } default: { if (kNameSpaceID_SVG == currNs) { attributes->adjustForSvg(); if (selfClosing) { appendVoidElementToCurrentMayFosterSVG(elementName, attributes); selfClosing = false; } else { appendToCurrentNodeAndPushElementMayFosterSVG(elementName, attributes); } attributes = nullptr; NS_HTML5_BREAK(starttagloop); } else { attributes->adjustForMath(); if (selfClosing) { appendVoidElementToCurrentMayFosterMathML(elementName, attributes); selfClosing = false; } else { appendToCurrentNodeAndPushElementMayFosterMathML(elementName, attributes); } attributes = nullptr; NS_HTML5_BREAK(starttagloop); } } } } } switch (mode) { case IN_TEMPLATE: { switch (group) { case COL: { popTemplateMode(); pushTemplateMode(IN_COLUMN_GROUP); mode = IN_COLUMN_GROUP; continue; } case CAPTION: case COLGROUP: case TBODY_OR_THEAD_OR_TFOOT: { popTemplateMode(); pushTemplateMode(IN_TABLE); mode = IN_TABLE; continue; } case TR: { popTemplateMode(); pushTemplateMode(IN_TABLE_BODY); mode = IN_TABLE_BODY; continue; } case TD_OR_TH: { popTemplateMode(); pushTemplateMode(IN_ROW); mode = IN_ROW; continue; } case META: { checkMetaCharset(attributes); appendVoidElementToCurrentMayFoster(elementName, attributes); selfClosing = false; attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case TITLE: { startTagTitleInHead(elementName, attributes); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case BASE: case LINK_OR_BASEFONT_OR_BGSOUND: { appendVoidElementToCurrentMayFoster(elementName, attributes); selfClosing = false; attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case SCRIPT: { startTagScriptInHead(elementName, attributes); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case NOFRAMES: case STYLE: { startTagGenericRawText(elementName, attributes); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case TEMPLATE: { startTagTemplateInHead(elementName, attributes); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } default: { popTemplateMode(); pushTemplateMode(IN_BODY); mode = IN_BODY; continue; } } } case IN_ROW: { switch (group) { case TD_OR_TH: { clearStackBackTo(findLastOrRoot(nsHtml5TreeBuilder::TR)); appendToCurrentNodeAndPushElement(elementName, attributes); mode = IN_CELL; insertMarker(); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case CAPTION: case COL: case COLGROUP: case TBODY_OR_THEAD_OR_TFOOT: case TR: { eltPos = findLastOrRoot(nsHtml5TreeBuilder::TR); if (!eltPos) { MOZ_ASSERT(fragment || isTemplateContents()); errNoTableRowToClose(); NS_HTML5_BREAK(starttagloop); } clearStackBackTo(eltPos); pop(); mode = IN_TABLE_BODY; continue; } default:; // fall through } [[fallthrough]]; } case IN_TABLE_BODY: { switch (group) { case TR: { clearStackBackTo( findLastInTableScopeOrRootTemplateTbodyTheadTfoot()); appendToCurrentNodeAndPushElement(elementName, attributes); mode = IN_ROW; attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case TD_OR_TH: { errStartTagInTableBody(name); clearStackBackTo( findLastInTableScopeOrRootTemplateTbodyTheadTfoot()); appendToCurrentNodeAndPushElement( nsHtml5ElementName::ELT_TR, nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES); mode = IN_ROW; continue; } case CAPTION: case COL: case COLGROUP: case TBODY_OR_THEAD_OR_TFOOT: { eltPos = findLastInTableScopeOrRootTemplateTbodyTheadTfoot(); if (!eltPos || stack[eltPos]->getGroup() == TEMPLATE) { MOZ_ASSERT(fragment || isTemplateContents()); errStrayStartTag(name); NS_HTML5_BREAK(starttagloop); } else { clearStackBackTo(eltPos); pop(); mode = IN_TABLE; continue; } } default:; // fall through } [[fallthrough]]; } case IN_TABLE: { for (;;) { switch (group) { case CAPTION: { clearStackBackTo(findLastOrRoot(nsHtml5TreeBuilder::TABLE)); insertMarker(); appendToCurrentNodeAndPushElement(elementName, attributes); mode = IN_CAPTION; attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case COLGROUP: { clearStackBackTo(findLastOrRoot(nsHtml5TreeBuilder::TABLE)); appendToCurrentNodeAndPushElement(elementName, attributes); mode = IN_COLUMN_GROUP; attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case COL: { clearStackBackTo(findLastOrRoot(nsHtml5TreeBuilder::TABLE)); appendToCurrentNodeAndPushElement( nsHtml5ElementName::ELT_COLGROUP, nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES); mode = IN_COLUMN_GROUP; NS_HTML5_CONTINUE(starttagloop); } case TBODY_OR_THEAD_OR_TFOOT: { clearStackBackTo(findLastOrRoot(nsHtml5TreeBuilder::TABLE)); appendToCurrentNodeAndPushElement(elementName, attributes); mode = IN_TABLE_BODY; attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case TR: case TD_OR_TH: { clearStackBackTo(findLastOrRoot(nsHtml5TreeBuilder::TABLE)); appendToCurrentNodeAndPushElement( nsHtml5ElementName::ELT_TBODY, nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES); mode = IN_TABLE_BODY; NS_HTML5_CONTINUE(starttagloop); } case TEMPLATE: { NS_HTML5_BREAK(intableloop); } case TABLE: { errTableSeenWhileTableOpen(); eltPos = findLastInTableScope(name); if (eltPos == nsHtml5TreeBuilder::NOT_FOUND_ON_STACK) { MOZ_ASSERT(fragment || isTemplateContents()); NS_HTML5_BREAK(starttagloop); } generateImpliedEndTags(); if (!!MOZ_UNLIKELY(mViewSource) && !isCurrent(nsGkAtoms::table)) { errNoCheckUnclosedElementsOnStack(); } while (currentPtr >= eltPos) { pop(); } resetTheInsertionMode(); NS_HTML5_CONTINUE(starttagloop); } case SCRIPT: { appendToCurrentNodeAndPushElement(elementName, attributes); originalMode = mode; mode = TEXT; tokenizer->setStateAndEndTagExpectation( nsHtml5Tokenizer::SCRIPT_DATA, elementName); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case STYLE: { appendToCurrentNodeAndPushElement(elementName, attributes); originalMode = mode; mode = TEXT; tokenizer->setStateAndEndTagExpectation(nsHtml5Tokenizer::RAWTEXT, elementName); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case INPUT: { errStartTagInTable(name); if (!nsHtml5Portability:: lowerCaseLiteralEqualsIgnoreAsciiCaseString( "hidden", attributes->getValue( nsHtml5AttributeName::ATTR_TYPE))) { NS_HTML5_BREAK(intableloop); } appendVoidInputToCurrent(attributes, formPointer); selfClosing = false; attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case FORM: { if (!!formPointer || isTemplateContents()) { errFormWhenFormOpen(); NS_HTML5_BREAK(starttagloop); } else { errStartTagInTable(name); appendVoidFormToCurrent(attributes); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } } default: { errStartTagInTable(name); NS_HTML5_BREAK(intableloop); } } } intableloop_end:; [[fallthrough]]; } case IN_CAPTION: { switch (group) { case CAPTION: case COL: case COLGROUP: case TBODY_OR_THEAD_OR_TFOOT: case TR: case TD_OR_TH: { eltPos = findLastInTableScope(nsGkAtoms::caption); if (eltPos == nsHtml5TreeBuilder::NOT_FOUND_ON_STACK) { MOZ_ASSERT(fragment || isTemplateContents()); errStrayStartTag(name); NS_HTML5_BREAK(starttagloop); } generateImpliedEndTags(); if (!!MOZ_UNLIKELY(mViewSource) && currentPtr != eltPos) { errNoCheckUnclosedElementsOnStack(); } while (currentPtr >= eltPos) { pop(); } clearTheListOfActiveFormattingElementsUpToTheLastMarker(); mode = IN_TABLE; continue; } default:; // fall through } [[fallthrough]]; } case IN_CELL: { switch (group) { case CAPTION: case COL: case COLGROUP: case TBODY_OR_THEAD_OR_TFOOT: case TR: case TD_OR_TH: { eltPos = findLastInTableScopeTdTh(); if (eltPos == nsHtml5TreeBuilder::NOT_FOUND_ON_STACK) { errNoCellToClose(); NS_HTML5_BREAK(starttagloop); } else { closeTheCell(eltPos); continue; } } default:; // fall through } [[fallthrough]]; } case FRAMESET_OK: { switch (group) { case FRAMESET: { if (mode == FRAMESET_OK) { if (!currentPtr || stack[1]->getGroup() != BODY) { MOZ_ASSERT(fragment || isTemplateContents()); errStrayStartTag(name); NS_HTML5_BREAK(starttagloop); } else { errFramesetStart(); detachFromParent(stack[1]->node); while (currentPtr > 0) { pop(); } appendToCurrentNodeAndPushElement(elementName, attributes); mode = IN_FRAMESET; attributes = nullptr; NS_HTML5_BREAK(starttagloop); } } else { errStrayStartTag(name); NS_HTML5_BREAK(starttagloop); } } case PRE_OR_LISTING: case LI: case DD_OR_DT: case BUTTON: case MARQUEE_OR_APPLET: case OBJECT: case TABLE: case AREA_OR_WBR: case KEYGEN: case BR: case EMBED: case IMG: case INPUT: case HR: case TEXTAREA: case XMP: case IFRAME: case SELECT: { if (mode == FRAMESET_OK && !(group == INPUT && nsHtml5Portability:: lowerCaseLiteralEqualsIgnoreAsciiCaseString( "hidden", attributes->getValue( nsHtml5AttributeName::ATTR_TYPE)))) { framesetOk = false; mode = IN_BODY; } [[fallthrough]]; } default:; // fall through } [[fallthrough]]; } case IN_BODY: { for (;;) { switch (group) { case HTML: { errStrayStartTag(name); if (!fragment && !isTemplateContents()) { addAttributesToHtml(attributes); attributes = nullptr; } NS_HTML5_BREAK(starttagloop); } case BASE: case LINK_OR_BASEFONT_OR_BGSOUND: case META: case STYLE: case SCRIPT: case TITLE: case TEMPLATE: { NS_HTML5_BREAK(inbodyloop); } case BODY: { if (!currentPtr || stack[1]->getGroup() != BODY || isTemplateContents()) { MOZ_ASSERT(fragment || isTemplateContents()); errStrayStartTag(name); NS_HTML5_BREAK(starttagloop); } errFooSeenWhenFooOpen(name); framesetOk = false; if (mode == FRAMESET_OK) { mode = IN_BODY; } if (addAttributesToBody(attributes)) { attributes = nullptr; } NS_HTML5_BREAK(starttagloop); } case P: case DIV_OR_BLOCKQUOTE_OR_CENTER_OR_MENU: case UL_OR_OL_OR_DL: case ADDRESS_OR_ARTICLE_OR_ASIDE_OR_DETAILS_OR_DIALOG_OR_DIR_OR_FIGCAPTION_OR_FIGURE_OR_FOOTER_OR_HEADER_OR_HGROUP_OR_MAIN_OR_NAV_OR_SECTION_OR_SUMMARY: { implicitlyCloseP(); appendToCurrentNodeAndPushElementMayFoster(elementName, attributes); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case H1_OR_H2_OR_H3_OR_H4_OR_H5_OR_H6: { implicitlyCloseP(); if (stack[currentPtr]->getGroup() == H1_OR_H2_OR_H3_OR_H4_OR_H5_OR_H6) { errHeadingWhenHeadingOpen(); pop(); } appendToCurrentNodeAndPushElementMayFoster(elementName, attributes); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case FIELDSET: { implicitlyCloseP(); appendToCurrentNodeAndPushElementMayFoster( elementName, attributes, formPointer); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case PRE_OR_LISTING: { implicitlyCloseP(); appendToCurrentNodeAndPushElementMayFoster(elementName, attributes); needToDropLF = true; attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case FORM: { if (!!formPointer && !isTemplateContents()) { errFormWhenFormOpen(); NS_HTML5_BREAK(starttagloop); } else { implicitlyCloseP(); appendToCurrentNodeAndPushFormElementMayFoster(attributes); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } } case LI: case DD_OR_DT: { eltPos = currentPtr; for (;;) { nsHtml5StackNode* node = stack[eltPos]; if (node->getGroup() == group) { generateImpliedEndTagsExceptFor(node->name); if (!!MOZ_UNLIKELY(mViewSource) && eltPos != currentPtr) { errUnclosedElementsImplied(eltPos, name); } while (currentPtr >= eltPos) { pop(); } break; } else if (!eltPos || (node->isSpecial() && (node->ns != kNameSpaceID_XHTML || (node->name != nsGkAtoms::p && node->name != nsGkAtoms::address && node->name != nsGkAtoms::div)))) { break; } eltPos--; } implicitlyCloseP(); appendToCurrentNodeAndPushElementMayFoster(elementName, attributes); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case PLAINTEXT: { implicitlyCloseP(); appendToCurrentNodeAndPushElementMayFoster(elementName, attributes); tokenizer->setStateAndEndTagExpectation( nsHtml5Tokenizer::PLAINTEXT, elementName); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case A: { int32_t activeAPos = findInListOfActiveFormattingElementsContainsBetweenEndAndLastMarker( nsGkAtoms::a); if (activeAPos != -1) { errFooSeenWhenFooOpen(name); nsHtml5StackNode* activeA = listOfActiveFormattingElements[activeAPos]; activeA->retain(); adoptionAgencyEndTag(nsGkAtoms::a); removeFromStack(activeA); activeAPos = findInListOfActiveFormattingElements(activeA); if (activeAPos != -1) { removeFromListOfActiveFormattingElements(activeAPos); } activeA->release(this); } reconstructTheActiveFormattingElements(); appendToCurrentNodeAndPushFormattingElementMayFoster(elementName, attributes); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case B_OR_BIG_OR_CODE_OR_EM_OR_I_OR_S_OR_SMALL_OR_STRIKE_OR_STRONG_OR_TT_OR_U: case FONT: { reconstructTheActiveFormattingElements(); maybeForgetEarlierDuplicateFormattingElement( elementName->getName(), attributes); appendToCurrentNodeAndPushFormattingElementMayFoster(elementName, attributes); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case NOBR: { reconstructTheActiveFormattingElements(); if (nsHtml5TreeBuilder::NOT_FOUND_ON_STACK != findLastInScope(nsGkAtoms::nobr)) { errFooSeenWhenFooOpen(name); adoptionAgencyEndTag(nsGkAtoms::nobr); reconstructTheActiveFormattingElements(); } appendToCurrentNodeAndPushFormattingElementMayFoster(elementName, attributes); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case BUTTON: { eltPos = findLastInScope(name); if (eltPos != nsHtml5TreeBuilder::NOT_FOUND_ON_STACK) { errFooSeenWhenFooOpen(name); generateImpliedEndTags(); if (!!MOZ_UNLIKELY(mViewSource) && !isCurrent(name)) { errUnclosedElementsImplied(eltPos, name); } while (currentPtr >= eltPos) { pop(); } NS_HTML5_CONTINUE(starttagloop); } else { reconstructTheActiveFormattingElements(); appendToCurrentNodeAndPushElementMayFoster( elementName, attributes, formPointer); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } } case OBJECT: { reconstructTheActiveFormattingElements(); appendToCurrentNodeAndPushElementMayFoster( elementName, attributes, formPointer); insertMarker(); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case MARQUEE_OR_APPLET: { reconstructTheActiveFormattingElements(); appendToCurrentNodeAndPushElementMayFoster(elementName, attributes); insertMarker(); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case TABLE: { if (!quirks) { implicitlyCloseP(); } appendToCurrentNodeAndPushElementMayFoster(elementName, attributes); mode = IN_TABLE; attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case BR: case EMBED: case AREA_OR_WBR: case KEYGEN: { reconstructTheActiveFormattingElements(); [[fallthrough]]; } #ifdef ENABLE_VOID_MENUITEM case MENUITEM: #endif case PARAM_OR_SOURCE_OR_TRACK: { appendVoidElementToCurrentMayFoster(elementName, attributes); selfClosing = false; attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case HR: { implicitlyCloseP(); appendVoidElementToCurrentMayFoster(elementName, attributes); selfClosing = false; attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case IMAGE: { errImage(); elementName = nsHtml5ElementName::ELT_IMG; NS_HTML5_CONTINUE(starttagloop); } case IMG: case INPUT: { reconstructTheActiveFormattingElements(); appendVoidElementToCurrentMayFoster(elementName, attributes, formPointer); selfClosing = false; attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case TEXTAREA: { appendToCurrentNodeAndPushElementMayFoster( elementName, attributes, formPointer); tokenizer->setStateAndEndTagExpectation(nsHtml5Tokenizer::RCDATA, elementName); originalMode = mode; mode = TEXT; needToDropLF = true; attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case XMP: { implicitlyCloseP(); reconstructTheActiveFormattingElements(); appendToCurrentNodeAndPushElementMayFoster(elementName, attributes); originalMode = mode; mode = TEXT; tokenizer->setStateAndEndTagExpectation(nsHtml5Tokenizer::RAWTEXT, elementName); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case NOSCRIPT: { if (!scriptingEnabled) { reconstructTheActiveFormattingElements(); appendToCurrentNodeAndPushElementMayFoster(elementName, attributes); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } [[fallthrough]]; } case NOFRAMES: case IFRAME: case NOEMBED: { startTagGenericRawText(elementName, attributes); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case SELECT: { reconstructTheActiveFormattingElements(); appendToCurrentNodeAndPushElementMayFoster( elementName, attributes, formPointer); switch (mode) { case IN_TABLE: case IN_CAPTION: case IN_COLUMN_GROUP: case IN_TABLE_BODY: case IN_ROW: case IN_CELL: { mode = IN_SELECT_IN_TABLE; break; } default: { mode = IN_SELECT; break; } } attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case OPTGROUP: case OPTION: { if (isCurrent(nsGkAtoms::option)) { pop(); } reconstructTheActiveFormattingElements(); appendToCurrentNodeAndPushElementMayFoster(elementName, attributes); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case RB_OR_RTC: { eltPos = findLastInScope(nsGkAtoms::ruby); if (eltPos != NOT_FOUND_ON_STACK) { generateImpliedEndTags(); } if (eltPos != currentPtr) { if (eltPos == NOT_FOUND_ON_STACK) { errStartTagSeenWithoutRuby(name); } else { errUnclosedChildrenInRuby(); } } appendToCurrentNodeAndPushElementMayFoster(elementName, attributes); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case RT_OR_RP: { eltPos = findLastInScope(nsGkAtoms::ruby); if (eltPos != NOT_FOUND_ON_STACK) { generateImpliedEndTagsExceptFor(nsGkAtoms::rtc); } if (eltPos != currentPtr) { if (!isCurrent(nsGkAtoms::rtc)) { if (eltPos == NOT_FOUND_ON_STACK) { errStartTagSeenWithoutRuby(name); } else { errUnclosedChildrenInRuby(); } } } appendToCurrentNodeAndPushElementMayFoster(elementName, attributes); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case MATH: { reconstructTheActiveFormattingElements(); attributes->adjustForMath(); if (selfClosing) { appendVoidElementToCurrentMayFosterMathML(elementName, attributes); selfClosing = false; } else { appendToCurrentNodeAndPushElementMayFosterMathML(elementName, attributes); } attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case SVG: { reconstructTheActiveFormattingElements(); attributes->adjustForSvg(); if (selfClosing) { appendVoidElementToCurrentMayFosterSVG(elementName, attributes); selfClosing = false; } else { appendToCurrentNodeAndPushElementMayFosterSVG(elementName, attributes); } attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case CAPTION: case COL: case COLGROUP: case TBODY_OR_THEAD_OR_TFOOT: case TR: case TD_OR_TH: case FRAME: case FRAMESET: case HEAD: { errStrayStartTag(name); NS_HTML5_BREAK(starttagloop); } case OUTPUT: { reconstructTheActiveFormattingElements(); appendToCurrentNodeAndPushElementMayFoster( elementName, attributes, formPointer); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } default: { reconstructTheActiveFormattingElements(); appendToCurrentNodeAndPushElementMayFoster(elementName, attributes); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } } } inbodyloop_end:; [[fallthrough]]; } case IN_HEAD: { for (;;) { switch (group) { case HTML: { errStrayStartTag(name); if (!fragment && !isTemplateContents()) { addAttributesToHtml(attributes); attributes = nullptr; } NS_HTML5_BREAK(starttagloop); } case BASE: case LINK_OR_BASEFONT_OR_BGSOUND: { appendVoidElementToCurrentMayFoster(elementName, attributes); selfClosing = false; attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case META: { NS_HTML5_BREAK(inheadloop); } case TITLE: { startTagTitleInHead(elementName, attributes); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case NOSCRIPT: { if (scriptingEnabled) { appendToCurrentNodeAndPushElement(elementName, attributes); originalMode = mode; mode = TEXT; tokenizer->setStateAndEndTagExpectation( nsHtml5Tokenizer::RAWTEXT, elementName); } else { appendToCurrentNodeAndPushElementMayFoster(elementName, attributes); mode = IN_HEAD_NOSCRIPT; } attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case SCRIPT: { startTagScriptInHead(elementName, attributes); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case STYLE: case NOFRAMES: { startTagGenericRawText(elementName, attributes); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case HEAD: { errFooSeenWhenFooOpen(name); NS_HTML5_BREAK(starttagloop); } case TEMPLATE: { startTagTemplateInHead(elementName, attributes); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } default: { pop(); mode = AFTER_HEAD; NS_HTML5_CONTINUE(starttagloop); } } } inheadloop_end:; [[fallthrough]]; } case IN_HEAD_NOSCRIPT: { switch (group) { case HTML: { errStrayStartTag(name); if (!fragment && !isTemplateContents()) { addAttributesToHtml(attributes); attributes = nullptr; } NS_HTML5_BREAK(starttagloop); } case LINK_OR_BASEFONT_OR_BGSOUND: { appendVoidElementToCurrentMayFoster(elementName, attributes); selfClosing = false; attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case META: { checkMetaCharset(attributes); appendVoidElementToCurrentMayFoster(elementName, attributes); selfClosing = false; attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case STYLE: case NOFRAMES: { appendToCurrentNodeAndPushElement(elementName, attributes); originalMode = mode; mode = TEXT; tokenizer->setStateAndEndTagExpectation(nsHtml5Tokenizer::RAWTEXT, elementName); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case HEAD: { errFooSeenWhenFooOpen(name); NS_HTML5_BREAK(starttagloop); } case NOSCRIPT: { errFooSeenWhenFooOpen(name); NS_HTML5_BREAK(starttagloop); } default: { errBadStartTagInNoscriptInHead(name); pop(); mode = IN_HEAD; continue; } } } case IN_COLUMN_GROUP: { switch (group) { case HTML: { errStrayStartTag(name); if (!fragment && !isTemplateContents()) { addAttributesToHtml(attributes); attributes = nullptr; } NS_HTML5_BREAK(starttagloop); } case COL: { appendVoidElementToCurrentMayFoster(elementName, attributes); selfClosing = false; attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case TEMPLATE: { startTagTemplateInHead(elementName, attributes); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } default: { if (!currentPtr || stack[currentPtr]->getGroup() == TEMPLATE) { MOZ_ASSERT(fragment || isTemplateContents()); errGarbageInColgroup(); NS_HTML5_BREAK(starttagloop); } pop(); mode = IN_TABLE; continue; } } } case IN_SELECT_IN_TABLE: { switch (group) { case CAPTION: case TBODY_OR_THEAD_OR_TFOOT: case TR: case TD_OR_TH: case TABLE: { errStartTagWithSelectOpen(name); eltPos = findLastInTableScope(nsGkAtoms::select); if (eltPos == nsHtml5TreeBuilder::NOT_FOUND_ON_STACK) { MOZ_ASSERT(fragment); NS_HTML5_BREAK(starttagloop); } while (currentPtr >= eltPos) { pop(); } resetTheInsertionMode(); continue; } default:; // fall through } [[fallthrough]]; } case IN_SELECT: { switch (group) { case HTML: { errStrayStartTag(name); if (!fragment) { addAttributesToHtml(attributes); attributes = nullptr; } NS_HTML5_BREAK(starttagloop); } case OPTION: { if (isCurrent(nsGkAtoms::option)) { pop(); } appendToCurrentNodeAndPushElement(elementName, attributes); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case OPTGROUP: { if (isCurrent(nsGkAtoms::option)) { pop(); } if (isCurrent(nsGkAtoms::optgroup)) { pop(); } appendToCurrentNodeAndPushElement(elementName, attributes); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case SELECT: { errStartSelectWhereEndSelectExpected(); eltPos = findLastInTableScope(name); if (eltPos == nsHtml5TreeBuilder::NOT_FOUND_ON_STACK) { MOZ_ASSERT(fragment); errNoSelectInTableScope(); NS_HTML5_BREAK(starttagloop); } else { while (currentPtr >= eltPos) { pop(); } resetTheInsertionMode(); NS_HTML5_BREAK(starttagloop); } } case INPUT: case TEXTAREA: { errStartTagWithSelectOpen(name); eltPos = findLastInTableScope(nsGkAtoms::select); if (eltPos == nsHtml5TreeBuilder::NOT_FOUND_ON_STACK) { MOZ_ASSERT(fragment); NS_HTML5_BREAK(starttagloop); } while (currentPtr >= eltPos) { pop(); } resetTheInsertionMode(); continue; } case SCRIPT: { startTagScriptInHead(elementName, attributes); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case TEMPLATE: { startTagTemplateInHead(elementName, attributes); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } default: { errStrayStartTag(name); NS_HTML5_BREAK(starttagloop); } } } case AFTER_BODY: { switch (group) { case HTML: { errStrayStartTag(name); if (!fragment && !isTemplateContents()) { addAttributesToHtml(attributes); attributes = nullptr; } NS_HTML5_BREAK(starttagloop); } default: { errStrayStartTag(name); mode = framesetOk ? FRAMESET_OK : IN_BODY; continue; } } } case IN_FRAMESET: { switch (group) { case FRAMESET: { appendToCurrentNodeAndPushElement(elementName, attributes); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case FRAME: { appendVoidElementToCurrentMayFoster(elementName, attributes); selfClosing = false; attributes = nullptr; NS_HTML5_BREAK(starttagloop); } default:; // fall through } [[fallthrough]]; } case AFTER_FRAMESET: { switch (group) { case HTML: { errStrayStartTag(name); if (!fragment && !isTemplateContents()) { addAttributesToHtml(attributes); attributes = nullptr; } NS_HTML5_BREAK(starttagloop); } case NOFRAMES: { appendToCurrentNodeAndPushElement(elementName, attributes); originalMode = mode; mode = TEXT; tokenizer->setStateAndEndTagExpectation(nsHtml5Tokenizer::RAWTEXT, elementName); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } default: { errStrayStartTag(name); NS_HTML5_BREAK(starttagloop); } } } case INITIAL: { errStartTagWithoutDoctype(); documentModeInternal(QUIRKS_MODE, nullptr, nullptr); mode = BEFORE_HTML; continue; } case BEFORE_HTML: { switch (group) { case HTML: { if (attributes == nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES) { appendHtmlElementToDocumentAndPush(); } else { appendHtmlElementToDocumentAndPush(attributes); } mode = BEFORE_HEAD; attributes = nullptr; NS_HTML5_BREAK(starttagloop); } default: { appendHtmlElementToDocumentAndPush(); mode = BEFORE_HEAD; continue; } } } case BEFORE_HEAD: { switch (group) { case HTML: { errStrayStartTag(name); if (!fragment && !isTemplateContents()) { addAttributesToHtml(attributes); attributes = nullptr; } NS_HTML5_BREAK(starttagloop); } case HEAD: { appendToCurrentNodeAndPushHeadElement(attributes); mode = IN_HEAD; attributes = nullptr; NS_HTML5_BREAK(starttagloop); } default: { appendToCurrentNodeAndPushHeadElement( nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES); mode = IN_HEAD; continue; } } } case AFTER_HEAD: { switch (group) { case HTML: { errStrayStartTag(name); if (!fragment && !isTemplateContents()) { addAttributesToHtml(attributes); attributes = nullptr; } NS_HTML5_BREAK(starttagloop); } case BODY: { if (!attributes->getLength()) { appendToCurrentNodeAndPushBodyElement(); } else { appendToCurrentNodeAndPushBodyElement(attributes); } framesetOk = false; mode = IN_BODY; attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case FRAMESET: { appendToCurrentNodeAndPushElement(elementName, attributes); mode = IN_FRAMESET; attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case TEMPLATE: { errFooBetweenHeadAndBody(name); pushHeadPointerOntoStack(); nsHtml5StackNode* headOnStack = stack[currentPtr]; startTagTemplateInHead(elementName, attributes); removeFromStack(headOnStack); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case BASE: case LINK_OR_BASEFONT_OR_BGSOUND: { errFooBetweenHeadAndBody(name); pushHeadPointerOntoStack(); appendVoidElementToCurrentMayFoster(elementName, attributes); selfClosing = false; pop(); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case META: { errFooBetweenHeadAndBody(name); checkMetaCharset(attributes); pushHeadPointerOntoStack(); appendVoidElementToCurrentMayFoster(elementName, attributes); selfClosing = false; pop(); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case SCRIPT: { errFooBetweenHeadAndBody(name); pushHeadPointerOntoStack(); appendToCurrentNodeAndPushElement(elementName, attributes); originalMode = mode; mode = TEXT; tokenizer->setStateAndEndTagExpectation( nsHtml5Tokenizer::SCRIPT_DATA, elementName); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case STYLE: case NOFRAMES: { errFooBetweenHeadAndBody(name); pushHeadPointerOntoStack(); appendToCurrentNodeAndPushElement(elementName, attributes); originalMode = mode; mode = TEXT; tokenizer->setStateAndEndTagExpectation(nsHtml5Tokenizer::RAWTEXT, elementName); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case TITLE: { errFooBetweenHeadAndBody(name); pushHeadPointerOntoStack(); appendToCurrentNodeAndPushElement(elementName, attributes); originalMode = mode; mode = TEXT; tokenizer->setStateAndEndTagExpectation(nsHtml5Tokenizer::RCDATA, elementName); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } case HEAD: { errStrayStartTag(name); NS_HTML5_BREAK(starttagloop); } default: { appendToCurrentNodeAndPushBodyElement(); mode = FRAMESET_OK; continue; } } } case AFTER_AFTER_BODY: { switch (group) { case HTML: { errStrayStartTag(name); if (!fragment && !isTemplateContents()) { addAttributesToHtml(attributes); attributes = nullptr; } NS_HTML5_BREAK(starttagloop); } default: { errStrayStartTag(name); mode = framesetOk ? FRAMESET_OK : IN_BODY; continue; } } } case AFTER_AFTER_FRAMESET: { switch (group) { case HTML: { errStrayStartTag(name); if (!fragment && !isTemplateContents()) { addAttributesToHtml(attributes); attributes = nullptr; } NS_HTML5_BREAK(starttagloop); } case NOFRAMES: { startTagGenericRawText(elementName, attributes); attributes = nullptr; NS_HTML5_BREAK(starttagloop); } default: { errStrayStartTag(name); NS_HTML5_BREAK(starttagloop); } } } case TEXT: { MOZ_ASSERT(false); NS_HTML5_BREAK(starttagloop); } } } starttagloop_end:; if (selfClosing) { errSelfClosing(); } if (!mBuilder && attributes != nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES) { delete attributes; } } void nsHtml5TreeBuilder::startTagTitleInHead( nsHtml5ElementName* elementName, nsHtml5HtmlAttributes* attributes) { appendToCurrentNodeAndPushElementMayFoster(elementName, attributes); originalMode = mode; mode = TEXT; tokenizer->setStateAndEndTagExpectation(nsHtml5Tokenizer::RCDATA, elementName); } void nsHtml5TreeBuilder::startTagGenericRawText( nsHtml5ElementName* elementName, nsHtml5HtmlAttributes* attributes) { appendToCurrentNodeAndPushElementMayFoster(elementName, attributes); originalMode = mode; mode = TEXT; tokenizer->setStateAndEndTagExpectation(nsHtml5Tokenizer::RAWTEXT, elementName); } void nsHtml5TreeBuilder::startTagScriptInHead( nsHtml5ElementName* elementName, nsHtml5HtmlAttributes* attributes) { appendToCurrentNodeAndPushElementMayFoster(elementName, attributes); originalMode = mode; mode = TEXT; tokenizer->setStateAndEndTagExpectation(nsHtml5Tokenizer::SCRIPT_DATA, elementName); } void nsHtml5TreeBuilder::startTagTemplateInHead( nsHtml5ElementName* elementName, nsHtml5HtmlAttributes* attributes) { appendToCurrentNodeAndPushElement(elementName, attributes); insertMarker(); framesetOk = false; originalMode = mode; mode = IN_TEMPLATE; pushTemplateMode(IN_TEMPLATE); } bool nsHtml5TreeBuilder::isTemplateContents() { return nsHtml5TreeBuilder::NOT_FOUND_ON_STACK != findLast(nsGkAtoms::_template); } bool nsHtml5TreeBuilder::isTemplateModeStackEmpty() { return templateModePtr == -1; } bool nsHtml5TreeBuilder::isSpecialParentInForeign(nsHtml5StackNode* stackNode) { int32_t ns = stackNode->ns; return (kNameSpaceID_XHTML == ns) || (stackNode->isHtmlIntegrationPoint()) || ((kNameSpaceID_MathML == ns) && (stackNode->getGroup() == MI_MO_MN_MS_MTEXT)); } nsHtml5String nsHtml5TreeBuilder::extractCharsetFromContent( nsHtml5String attributeValue, nsHtml5TreeBuilder* tb) { int32_t charsetState = CHARSET_INITIAL; int32_t start = -1; int32_t end = -1; autoJArray buffer = nsHtml5Portability::newCharArrayFromString(attributeValue); for (int32_t i = 0; i < buffer.length; i++) { char16_t c = buffer[i]; switch (charsetState) { case CHARSET_INITIAL: { switch (c) { case 'c': case 'C': { charsetState = CHARSET_C; continue; } default: { continue; } } } case CHARSET_C: { switch (c) { case 'h': case 'H': { charsetState = CHARSET_H; continue; } default: { charsetState = CHARSET_INITIAL; continue; } } } case CHARSET_H: { switch (c) { case 'a': case 'A': { charsetState = CHARSET_A; continue; } default: { charsetState = CHARSET_INITIAL; continue; } } } case CHARSET_A: { switch (c) { case 'r': case 'R': { charsetState = CHARSET_R; continue; } default: { charsetState = CHARSET_INITIAL; continue; } } } case CHARSET_R: { switch (c) { case 's': case 'S': { charsetState = CHARSET_S; continue; } default: { charsetState = CHARSET_INITIAL; continue; } } } case CHARSET_S: { switch (c) { case 'e': case 'E': { charsetState = CHARSET_E; continue; } default: { charsetState = CHARSET_INITIAL; continue; } } } case CHARSET_E: { switch (c) { case 't': case 'T': { charsetState = CHARSET_T; continue; } default: { charsetState = CHARSET_INITIAL; continue; } } } case CHARSET_T: { switch (c) { case '\t': case '\n': case '\f': case '\r': case ' ': { continue; } case '=': { charsetState = CHARSET_EQUALS; continue; } default: { return nullptr; } } } case CHARSET_EQUALS: { switch (c) { case '\t': case '\n': case '\f': case '\r': case ' ': { continue; } case '\'': { start = i + 1; charsetState = CHARSET_SINGLE_QUOTED; continue; } case '\"': { start = i + 1; charsetState = CHARSET_DOUBLE_QUOTED; continue; } default: { start = i; charsetState = CHARSET_UNQUOTED; continue; } } } case CHARSET_SINGLE_QUOTED: { switch (c) { case '\'': { end = i; NS_HTML5_BREAK(charsetloop); } default: { continue; } } } case CHARSET_DOUBLE_QUOTED: { switch (c) { case '\"': { end = i; NS_HTML5_BREAK(charsetloop); } default: { continue; } } } case CHARSET_UNQUOTED: { switch (c) { case '\t': case '\n': case '\f': case '\r': case ' ': case ';': { end = i; NS_HTML5_BREAK(charsetloop); } default: { continue; } } } } } charsetloop_end:; if (start != -1) { if (end == -1) { if (charsetState == CHARSET_UNQUOTED) { end = buffer.length; } else { return nullptr; } } return nsHtml5Portability::newStringFromBuffer(buffer, start, end - start, tb, false); } return nullptr; } void nsHtml5TreeBuilder::checkMetaCharset(nsHtml5HtmlAttributes* attributes) { nsHtml5String charset = attributes->getValue(nsHtml5AttributeName::ATTR_CHARSET); if (charset) { if (tokenizer->internalEncodingDeclaration(charset)) { requestSuspension(); return; } return; } if (!nsHtml5Portability::lowerCaseLiteralEqualsIgnoreAsciiCaseString( "content-type", attributes->getValue(nsHtml5AttributeName::ATTR_HTTP_EQUIV))) { return; } nsHtml5String content = attributes->getValue(nsHtml5AttributeName::ATTR_CONTENT); if (content) { nsHtml5String extract = nsHtml5TreeBuilder::extractCharsetFromContent(content, this); if (extract) { if (tokenizer->internalEncodingDeclaration(extract)) { requestSuspension(); } } extract.Release(); } } void nsHtml5TreeBuilder::endTag(nsHtml5ElementName* elementName) { flushCharacters(); needToDropLF = false; int32_t eltPos; int32_t group = elementName->getGroup(); nsAtom* name = elementName->getName(); for (;;) { if (isInForeign()) { if (stack[currentPtr]->name != name) { if (!currentPtr) { errStrayEndTag(name); } else { errEndTagDidNotMatchCurrentOpenElement(name, stack[currentPtr]->popName); } } eltPos = currentPtr; int32_t origPos = currentPtr; for (;;) { if (!eltPos) { MOZ_ASSERT(fragment, "We can get this close to the root of the stack in " "foreign content only in the fragment case."); NS_HTML5_BREAK(endtagloop); } if (stack[eltPos]->name == name) { while (currentPtr >= eltPos) { popForeign(origPos, eltPos); } NS_HTML5_BREAK(endtagloop); } if (stack[--eltPos]->ns == kNameSpaceID_XHTML) { break; } } } switch (mode) { case IN_TEMPLATE: { switch (group) { case TEMPLATE: { break; } default: { errStrayEndTag(name); NS_HTML5_BREAK(endtagloop); } } [[fallthrough]]; } case IN_ROW: { switch (group) { case TR: { eltPos = findLastOrRoot(nsHtml5TreeBuilder::TR); if (!eltPos) { MOZ_ASSERT(fragment || isTemplateContents()); errNoTableRowToClose(); NS_HTML5_BREAK(endtagloop); } clearStackBackTo(eltPos); pop(); mode = IN_TABLE_BODY; NS_HTML5_BREAK(endtagloop); } case TABLE: { eltPos = findLastOrRoot(nsHtml5TreeBuilder::TR); if (!eltPos) { MOZ_ASSERT(fragment || isTemplateContents()); errNoTableRowToClose(); NS_HTML5_BREAK(endtagloop); } clearStackBackTo(eltPos); pop(); mode = IN_TABLE_BODY; continue; } case TBODY_OR_THEAD_OR_TFOOT: { if (findLastInTableScope(name) == nsHtml5TreeBuilder::NOT_FOUND_ON_STACK) { errStrayEndTag(name); NS_HTML5_BREAK(endtagloop); } eltPos = findLastOrRoot(nsHtml5TreeBuilder::TR); if (!eltPos) { MOZ_ASSERT(fragment || isTemplateContents()); errNoTableRowToClose(); NS_HTML5_BREAK(endtagloop); } clearStackBackTo(eltPos); pop(); mode = IN_TABLE_BODY; continue; } case BODY: case CAPTION: case COL: case COLGROUP: case HTML: case TD_OR_TH: { errStrayEndTag(name); NS_HTML5_BREAK(endtagloop); } default:; // fall through } [[fallthrough]]; } case IN_TABLE_BODY: { switch (group) { case TBODY_OR_THEAD_OR_TFOOT: { eltPos = findLastOrRoot(name); if (!eltPos) { errStrayEndTag(name); NS_HTML5_BREAK(endtagloop); } clearStackBackTo(eltPos); pop(); mode = IN_TABLE; NS_HTML5_BREAK(endtagloop); } case TABLE: { eltPos = findLastInTableScopeOrRootTemplateTbodyTheadTfoot(); if (!eltPos || stack[eltPos]->getGroup() == TEMPLATE) { MOZ_ASSERT(fragment || isTemplateContents()); errStrayEndTag(name); NS_HTML5_BREAK(endtagloop); } clearStackBackTo(eltPos); pop(); mode = IN_TABLE; continue; } case BODY: case CAPTION: case COL: case COLGROUP: case HTML: case TD_OR_TH: case TR: { errStrayEndTag(name); NS_HTML5_BREAK(endtagloop); } default:; // fall through } [[fallthrough]]; } case IN_TABLE: { switch (group) { case TABLE: { eltPos = findLast(nsGkAtoms::table); if (eltPos == nsHtml5TreeBuilder::NOT_FOUND_ON_STACK) { MOZ_ASSERT(fragment || isTemplateContents()); errStrayEndTag(name); NS_HTML5_BREAK(endtagloop); } while (currentPtr >= eltPos) { pop(); } resetTheInsertionMode(); NS_HTML5_BREAK(endtagloop); } case BODY: case CAPTION: case COL: case COLGROUP: case HTML: case TBODY_OR_THEAD_OR_TFOOT: case TD_OR_TH: case TR: { errStrayEndTag(name); NS_HTML5_BREAK(endtagloop); } case TEMPLATE: { break; } default: { errStrayEndTag(name); } } [[fallthrough]]; } case IN_CAPTION: { switch (group) { case CAPTION: { eltPos = findLastInTableScope(nsGkAtoms::caption); if (eltPos == nsHtml5TreeBuilder::NOT_FOUND_ON_STACK) { NS_HTML5_BREAK(endtagloop); } generateImpliedEndTags(); if (!!MOZ_UNLIKELY(mViewSource) && currentPtr != eltPos) { errUnclosedElements(eltPos, name); } while (currentPtr >= eltPos) { pop(); } clearTheListOfActiveFormattingElementsUpToTheLastMarker(); mode = IN_TABLE; NS_HTML5_BREAK(endtagloop); } case TABLE: { eltPos = findLastInTableScope(nsGkAtoms::caption); if (eltPos == nsHtml5TreeBuilder::NOT_FOUND_ON_STACK) { MOZ_ASSERT(fragment || isTemplateContents()); errStrayEndTag(name); NS_HTML5_BREAK(endtagloop); } generateImpliedEndTags(); if (!!MOZ_UNLIKELY(mViewSource) && currentPtr != eltPos) { errUnclosedElements(eltPos, name); } while (currentPtr >= eltPos) { pop(); } clearTheListOfActiveFormattingElementsUpToTheLastMarker(); mode = IN_TABLE; continue; } case BODY: case COL: case COLGROUP: case HTML: case TBODY_OR_THEAD_OR_TFOOT: case TD_OR_TH: case TR: { errStrayEndTag(name); NS_HTML5_BREAK(endtagloop); } default:; // fall through } [[fallthrough]]; } case IN_CELL: { switch (group) { case TD_OR_TH: { eltPos = findLastInTableScope(name); if (eltPos == nsHtml5TreeBuilder::NOT_FOUND_ON_STACK) { errStrayEndTag(name); NS_HTML5_BREAK(endtagloop); } generateImpliedEndTags(); if (!!MOZ_UNLIKELY(mViewSource) && !isCurrent(name)) { errUnclosedElements(eltPos, name); } while (currentPtr >= eltPos) { pop(); } clearTheListOfActiveFormattingElementsUpToTheLastMarker(); mode = IN_ROW; NS_HTML5_BREAK(endtagloop); } case TABLE: case TBODY_OR_THEAD_OR_TFOOT: case TR: { if (findLastInTableScope(name) == nsHtml5TreeBuilder::NOT_FOUND_ON_STACK) { MOZ_ASSERT(name == nsGkAtoms::tbody || name == nsGkAtoms::tfoot || name == nsGkAtoms::thead || fragment || isTemplateContents()); errStrayEndTag(name); NS_HTML5_BREAK(endtagloop); } closeTheCell(findLastInTableScopeTdTh()); continue; } case BODY: case CAPTION: case COL: case COLGROUP: case HTML: { errStrayEndTag(name); NS_HTML5_BREAK(endtagloop); } default:; // fall through } [[fallthrough]]; } case FRAMESET_OK: case IN_BODY: { switch (group) { case BODY: { if (!isSecondOnStackBody()) { MOZ_ASSERT(fragment || isTemplateContents()); errStrayEndTag(name); NS_HTML5_BREAK(endtagloop); } MOZ_ASSERT(currentPtr >= 1); if (MOZ_UNLIKELY(mViewSource)) { for (int32_t i = 2; i <= currentPtr; i++) { switch (stack[i]->getGroup()) { case DD_OR_DT: case LI: case OPTGROUP: case OPTION: case P: case RB_OR_RTC: case RT_OR_RP: case TD_OR_TH: case TBODY_OR_THEAD_OR_TFOOT: { break; } default: { errEndWithUnclosedElements(name); NS_HTML5_BREAK(uncloseloop1); } } } uncloseloop1_end:; } mode = AFTER_BODY; NS_HTML5_BREAK(endtagloop); } case HTML: { if (!isSecondOnStackBody()) { MOZ_ASSERT(fragment || isTemplateContents()); errStrayEndTag(name); NS_HTML5_BREAK(endtagloop); } if (MOZ_UNLIKELY(mViewSource)) { for (int32_t i = 0; i <= currentPtr; i++) { switch (stack[i]->getGroup()) { case DD_OR_DT: case LI: case P: case RB_OR_RTC: case RT_OR_RP: case TBODY_OR_THEAD_OR_TFOOT: case TD_OR_TH: case BODY: case HTML: { break; } default: { errEndWithUnclosedElements(name); NS_HTML5_BREAK(uncloseloop2); } } } uncloseloop2_end:; } mode = AFTER_BODY; continue; } case DIV_OR_BLOCKQUOTE_OR_CENTER_OR_MENU: case UL_OR_OL_OR_DL: case PRE_OR_LISTING: case FIELDSET: case BUTTON: case ADDRESS_OR_ARTICLE_OR_ASIDE_OR_DETAILS_OR_DIALOG_OR_DIR_OR_FIGCAPTION_OR_FIGURE_OR_FOOTER_OR_HEADER_OR_HGROUP_OR_MAIN_OR_NAV_OR_SECTION_OR_SUMMARY: { eltPos = findLastInScope(name); if (eltPos == nsHtml5TreeBuilder::NOT_FOUND_ON_STACK) { errStrayEndTag(name); } else { generateImpliedEndTags(); if (!!MOZ_UNLIKELY(mViewSource) && !isCurrent(name)) { errUnclosedElements(eltPos, name); } while (currentPtr >= eltPos) { pop(); } } NS_HTML5_BREAK(endtagloop); } case FORM: { if (!isTemplateContents()) { if (!formPointer) { errStrayEndTag(name); NS_HTML5_BREAK(endtagloop); } formPointer = nullptr; eltPos = findLastInScope(name); if (eltPos == nsHtml5TreeBuilder::NOT_FOUND_ON_STACK) { errStrayEndTag(name); NS_HTML5_BREAK(endtagloop); } generateImpliedEndTags(); if (!!MOZ_UNLIKELY(mViewSource) && !isCurrent(name)) { errUnclosedElements(eltPos, name); } removeFromStack(eltPos); NS_HTML5_BREAK(endtagloop); } else { eltPos = findLastInScope(name); if (eltPos == nsHtml5TreeBuilder::NOT_FOUND_ON_STACK) { errStrayEndTag(name); NS_HTML5_BREAK(endtagloop); } generateImpliedEndTags(); if (!!MOZ_UNLIKELY(mViewSource) && !isCurrent(name)) { errUnclosedElements(eltPos, name); } while (currentPtr >= eltPos) { pop(); } NS_HTML5_BREAK(endtagloop); } } case P: { eltPos = findLastInButtonScope(nsGkAtoms::p); if (eltPos == nsHtml5TreeBuilder::NOT_FOUND_ON_STACK) { errNoElementToCloseButEndTagSeen(nsGkAtoms::p); if (isInForeign()) { errHtmlStartTagInForeignContext(name); while (currentPtr >= 0 && stack[currentPtr]->ns != kNameSpaceID_XHTML) { pop(); } } appendVoidElementToCurrentMayFoster( elementName, nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES); NS_HTML5_BREAK(endtagloop); } generateImpliedEndTagsExceptFor(nsGkAtoms::p); MOZ_ASSERT(eltPos != nsHtml5TreeBuilder::NOT_FOUND_ON_STACK); if (!!MOZ_UNLIKELY(mViewSource) && eltPos != currentPtr) { errUnclosedElements(eltPos, name); } while (currentPtr >= eltPos) { pop(); } NS_HTML5_BREAK(endtagloop); } case LI: { eltPos = findLastInListScope(name); if (eltPos == nsHtml5TreeBuilder::NOT_FOUND_ON_STACK) { errNoElementToCloseButEndTagSeen(name); } else { generateImpliedEndTagsExceptFor(name); if (!!MOZ_UNLIKELY(mViewSource) && eltPos != currentPtr) { errUnclosedElements(eltPos, name); } while (currentPtr >= eltPos) { pop(); } } NS_HTML5_BREAK(endtagloop); } case DD_OR_DT: { eltPos = findLastInScope(name); if (eltPos == nsHtml5TreeBuilder::NOT_FOUND_ON_STACK) { errNoElementToCloseButEndTagSeen(name); } else { generateImpliedEndTagsExceptFor(name); if (!!MOZ_UNLIKELY(mViewSource) && eltPos != currentPtr) { errUnclosedElements(eltPos, name); } while (currentPtr >= eltPos) { pop(); } } NS_HTML5_BREAK(endtagloop); } case H1_OR_H2_OR_H3_OR_H4_OR_H5_OR_H6: { eltPos = findLastInScopeHn(); if (eltPos == nsHtml5TreeBuilder::NOT_FOUND_ON_STACK) { errStrayEndTag(name); } else { generateImpliedEndTags(); if (!!MOZ_UNLIKELY(mViewSource) && !isCurrent(name)) { errUnclosedElements(eltPos, name); } while (currentPtr >= eltPos) { pop(); } } NS_HTML5_BREAK(endtagloop); } case OBJECT: case MARQUEE_OR_APPLET: { eltPos = findLastInScope(name); if (eltPos == nsHtml5TreeBuilder::NOT_FOUND_ON_STACK) { errStrayEndTag(name); } else { generateImpliedEndTags(); if (!!MOZ_UNLIKELY(mViewSource) && !isCurrent(name)) { errUnclosedElements(eltPos, name); } while (currentPtr >= eltPos) { pop(); } clearTheListOfActiveFormattingElementsUpToTheLastMarker(); } NS_HTML5_BREAK(endtagloop); } case BR: { errEndTagBr(); if (isInForeign()) { errHtmlStartTagInForeignContext(name); while (currentPtr >= 0 && stack[currentPtr]->ns != kNameSpaceID_XHTML) { pop(); } } reconstructTheActiveFormattingElements(); appendVoidElementToCurrentMayFoster( elementName, nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES); NS_HTML5_BREAK(endtagloop); } case TEMPLATE: { break; } case AREA_OR_WBR: case KEYGEN: #ifdef ENABLE_VOID_MENUITEM case MENUITEM: #endif case PARAM_OR_SOURCE_OR_TRACK: case EMBED: case IMG: case IMAGE: case INPUT: case HR: case IFRAME: case NOEMBED: case NOFRAMES: case SELECT: case TABLE: case TEXTAREA: { errStrayEndTag(name); NS_HTML5_BREAK(endtagloop); } case NOSCRIPT: { if (scriptingEnabled) { errStrayEndTag(name); NS_HTML5_BREAK(endtagloop); } [[fallthrough]]; } case A: case B_OR_BIG_OR_CODE_OR_EM_OR_I_OR_S_OR_SMALL_OR_STRIKE_OR_STRONG_OR_TT_OR_U: case FONT: case NOBR: { if (adoptionAgencyEndTag(name)) { NS_HTML5_BREAK(endtagloop); } [[fallthrough]]; } default: { if (isCurrent(name)) { pop(); NS_HTML5_BREAK(endtagloop); } eltPos = currentPtr; for (;;) { nsHtml5StackNode* node = stack[eltPos]; if (node->ns == kNameSpaceID_XHTML && node->name == name) { generateImpliedEndTags(); if (!!MOZ_UNLIKELY(mViewSource) && !isCurrent(name)) { errUnclosedElements(eltPos, name); } while (currentPtr >= eltPos) { pop(); } NS_HTML5_BREAK(endtagloop); } else if (!eltPos || node->isSpecial()) { errStrayEndTag(name); NS_HTML5_BREAK(endtagloop); } eltPos--; } } } [[fallthrough]]; } case IN_HEAD: { switch (group) { case HEAD: { pop(); mode = AFTER_HEAD; NS_HTML5_BREAK(endtagloop); } case BR: case HTML: case BODY: { pop(); mode = AFTER_HEAD; continue; } case TEMPLATE: { endTagTemplateInHead(); NS_HTML5_BREAK(endtagloop); } default: { errStrayEndTag(name); NS_HTML5_BREAK(endtagloop); } } } case IN_HEAD_NOSCRIPT: { switch (group) { case NOSCRIPT: { pop(); mode = IN_HEAD; NS_HTML5_BREAK(endtagloop); } case BR: { errStrayEndTag(name); pop(); mode = IN_HEAD; continue; } default: { errStrayEndTag(name); NS_HTML5_BREAK(endtagloop); } } } case IN_COLUMN_GROUP: { switch (group) { case COLGROUP: { if (!currentPtr || stack[currentPtr]->getGroup() == nsHtml5TreeBuilder::TEMPLATE) { MOZ_ASSERT(fragment || isTemplateContents()); errGarbageInColgroup(); NS_HTML5_BREAK(endtagloop); } pop(); mode = IN_TABLE; NS_HTML5_BREAK(endtagloop); } case COL: { errStrayEndTag(name); NS_HTML5_BREAK(endtagloop); } case TEMPLATE: { endTagTemplateInHead(); NS_HTML5_BREAK(endtagloop); } default: { if (!currentPtr || stack[currentPtr]->getGroup() == nsHtml5TreeBuilder::TEMPLATE) { MOZ_ASSERT(fragment || isTemplateContents()); errGarbageInColgroup(); NS_HTML5_BREAK(endtagloop); } pop(); mode = IN_TABLE; continue; } } } case IN_SELECT_IN_TABLE: { switch (group) { case CAPTION: case TABLE: case TBODY_OR_THEAD_OR_TFOOT: case TR: case TD_OR_TH: { errEndTagSeenWithSelectOpen(name); if (findLastInTableScope(name) != nsHtml5TreeBuilder::NOT_FOUND_ON_STACK) { eltPos = findLastInTableScope(nsGkAtoms::select); if (eltPos == nsHtml5TreeBuilder::NOT_FOUND_ON_STACK) { MOZ_ASSERT(fragment); NS_HTML5_BREAK(endtagloop); } while (currentPtr >= eltPos) { pop(); } resetTheInsertionMode(); continue; } else { NS_HTML5_BREAK(endtagloop); } } default:; // fall through } [[fallthrough]]; } case IN_SELECT: { switch (group) { case OPTION: { if (isCurrent(nsGkAtoms::option)) { pop(); NS_HTML5_BREAK(endtagloop); } else { errStrayEndTag(name); NS_HTML5_BREAK(endtagloop); } } case OPTGROUP: { if (isCurrent(nsGkAtoms::option) && nsGkAtoms::optgroup == stack[currentPtr - 1]->name) { pop(); } if (isCurrent(nsGkAtoms::optgroup)) { pop(); } else { errStrayEndTag(name); } NS_HTML5_BREAK(endtagloop); } case SELECT: { eltPos = findLastInTableScope(nsGkAtoms::select); if (eltPos == nsHtml5TreeBuilder::NOT_FOUND_ON_STACK) { MOZ_ASSERT(fragment); errStrayEndTag(name); NS_HTML5_BREAK(endtagloop); } while (currentPtr >= eltPos) { pop(); } resetTheInsertionMode(); NS_HTML5_BREAK(endtagloop); } case TEMPLATE: { endTagTemplateInHead(); NS_HTML5_BREAK(endtagloop); } default: { errStrayEndTag(name); NS_HTML5_BREAK(endtagloop); } } } case AFTER_BODY: { switch (group) { case HTML: { if (fragment) { errStrayEndTag(name); NS_HTML5_BREAK(endtagloop); } else { mode = AFTER_AFTER_BODY; NS_HTML5_BREAK(endtagloop); } } default: { errEndTagAfterBody(); mode = framesetOk ? FRAMESET_OK : IN_BODY; continue; } } } case IN_FRAMESET: { switch (group) { case FRAMESET: { if (!currentPtr) { MOZ_ASSERT(fragment); errStrayEndTag(name); NS_HTML5_BREAK(endtagloop); } pop(); if ((!fragment) && !isCurrent(nsGkAtoms::frameset)) { mode = AFTER_FRAMESET; } NS_HTML5_BREAK(endtagloop); } default: { errStrayEndTag(name); NS_HTML5_BREAK(endtagloop); } } } case AFTER_FRAMESET: { switch (group) { case HTML: { mode = AFTER_AFTER_FRAMESET; NS_HTML5_BREAK(endtagloop); } default: { errStrayEndTag(name); NS_HTML5_BREAK(endtagloop); } } } case INITIAL: { errEndTagSeenWithoutDoctype(); documentModeInternal(QUIRKS_MODE, nullptr, nullptr); mode = BEFORE_HTML; continue; } case BEFORE_HTML: { switch (group) { case HEAD: case BR: case HTML: case BODY: { appendHtmlElementToDocumentAndPush(); mode = BEFORE_HEAD; continue; } default: { errStrayEndTag(name); NS_HTML5_BREAK(endtagloop); } } } case BEFORE_HEAD: { switch (group) { case HEAD: case BR: case HTML: case BODY: { appendToCurrentNodeAndPushHeadElement( nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES); mode = IN_HEAD; continue; } default: { errStrayEndTag(name); NS_HTML5_BREAK(endtagloop); } } } case AFTER_HEAD: { switch (group) { case TEMPLATE: { endTagTemplateInHead(); NS_HTML5_BREAK(endtagloop); } case HTML: case BODY: case BR: { appendToCurrentNodeAndPushBodyElement(); mode = FRAMESET_OK; continue; } default: { errStrayEndTag(name); NS_HTML5_BREAK(endtagloop); } } } case AFTER_AFTER_BODY: { errStrayEndTag(name); mode = framesetOk ? FRAMESET_OK : IN_BODY; continue; } case AFTER_AFTER_FRAMESET: { errStrayEndTag(name); NS_HTML5_BREAK(endtagloop); } case TEXT: { pop(); if (originalMode == AFTER_HEAD) { silentPop(); } mode = originalMode; NS_HTML5_BREAK(endtagloop); } } } endtagloop_end:; } void nsHtml5TreeBuilder::endTagTemplateInHead() { int32_t eltPos = findLast(nsGkAtoms::_template); if (eltPos == nsHtml5TreeBuilder::NOT_FOUND_ON_STACK) { errStrayEndTag(nsGkAtoms::_template); return; } generateImpliedEndTags(); if (!!MOZ_UNLIKELY(mViewSource) && !isCurrent(nsGkAtoms::_template)) { errUnclosedElements(eltPos, nsGkAtoms::_template); } while (currentPtr >= eltPos) { pop(); } clearTheListOfActiveFormattingElementsUpToTheLastMarker(); popTemplateMode(); resetTheInsertionMode(); } int32_t nsHtml5TreeBuilder::findLastInTableScopeOrRootTemplateTbodyTheadTfoot() { for (int32_t i = currentPtr; i > 0; i--) { if (stack[i]->getGroup() == nsHtml5TreeBuilder::TBODY_OR_THEAD_OR_TFOOT || stack[i]->getGroup() == nsHtml5TreeBuilder::TEMPLATE) { return i; } } return 0; } int32_t nsHtml5TreeBuilder::findLast(nsAtom* name) { for (int32_t i = currentPtr; i > 0; i--) { if (stack[i]->ns == kNameSpaceID_XHTML && stack[i]->name == name) { return i; } } return nsHtml5TreeBuilder::NOT_FOUND_ON_STACK; } int32_t nsHtml5TreeBuilder::findLastInTableScope(nsAtom* name) { for (int32_t i = currentPtr; i > 0; i--) { if (stack[i]->ns == kNameSpaceID_XHTML) { if (stack[i]->name == name) { return i; } else if (stack[i]->name == nsGkAtoms::table || stack[i]->name == nsGkAtoms::_template) { return nsHtml5TreeBuilder::NOT_FOUND_ON_STACK; } } } return nsHtml5TreeBuilder::NOT_FOUND_ON_STACK; } int32_t nsHtml5TreeBuilder::findLastInButtonScope(nsAtom* name) { for (int32_t i = currentPtr; i > 0; i--) { if (stack[i]->ns == kNameSpaceID_XHTML) { if (stack[i]->name == name) { return i; } else if (stack[i]->name == nsGkAtoms::button) { return nsHtml5TreeBuilder::NOT_FOUND_ON_STACK; } } if (stack[i]->isScoping()) { return nsHtml5TreeBuilder::NOT_FOUND_ON_STACK; } } return nsHtml5TreeBuilder::NOT_FOUND_ON_STACK; } int32_t nsHtml5TreeBuilder::findLastInScope(nsAtom* name) { for (int32_t i = currentPtr; i > 0; i--) { if (stack[i]->ns == kNameSpaceID_XHTML && stack[i]->name == name) { return i; } else if (stack[i]->isScoping()) { return nsHtml5TreeBuilder::NOT_FOUND_ON_STACK; } } return nsHtml5TreeBuilder::NOT_FOUND_ON_STACK; } int32_t nsHtml5TreeBuilder::findLastInListScope(nsAtom* name) { for (int32_t i = currentPtr; i > 0; i--) { if (stack[i]->ns == kNameSpaceID_XHTML) { if (stack[i]->name == name) { return i; } else if (stack[i]->name == nsGkAtoms::ul || stack[i]->name == nsGkAtoms::ol) { return nsHtml5TreeBuilder::NOT_FOUND_ON_STACK; } } if (stack[i]->isScoping()) { return nsHtml5TreeBuilder::NOT_FOUND_ON_STACK; } } return nsHtml5TreeBuilder::NOT_FOUND_ON_STACK; } int32_t nsHtml5TreeBuilder::findLastInScopeHn() { for (int32_t i = currentPtr; i > 0; i--) { if (stack[i]->getGroup() == nsHtml5TreeBuilder::H1_OR_H2_OR_H3_OR_H4_OR_H5_OR_H6) { return i; } else if (stack[i]->isScoping()) { return nsHtml5TreeBuilder::NOT_FOUND_ON_STACK; } } return nsHtml5TreeBuilder::NOT_FOUND_ON_STACK; } void nsHtml5TreeBuilder::generateImpliedEndTagsExceptFor(nsAtom* name) { for (;;) { nsHtml5StackNode* node = stack[currentPtr]; switch (node->getGroup()) { case P: case LI: case DD_OR_DT: case OPTION: case OPTGROUP: case RB_OR_RTC: case RT_OR_RP: { if (node->ns == kNameSpaceID_XHTML && node->name == name) { return; } pop(); continue; } default: { return; } } } } void nsHtml5TreeBuilder::generateImpliedEndTags() { for (;;) { switch (stack[currentPtr]->getGroup()) { case P: case LI: case DD_OR_DT: case OPTION: case OPTGROUP: case RB_OR_RTC: case RT_OR_RP: { pop(); continue; } default: { return; } } } } bool nsHtml5TreeBuilder::isSecondOnStackBody() { return currentPtr >= 1 && stack[1]->getGroup() == nsHtml5TreeBuilder::BODY; } void nsHtml5TreeBuilder::documentModeInternal(nsHtml5DocumentMode m, nsHtml5String publicIdentifier, nsHtml5String systemIdentifier) { if (isSrcdocDocument) { quirks = false; this->documentMode(STANDARDS_MODE); return; } quirks = (m == QUIRKS_MODE); this->documentMode(m); } bool nsHtml5TreeBuilder::isAlmostStandards(nsHtml5String publicIdentifier, nsHtml5String systemIdentifier) { if (nsHtml5Portability::lowerCaseLiteralIsPrefixOfIgnoreAsciiCaseString( "-//w3c//dtd xhtml 1.0 transitional//", publicIdentifier)) { return true; } if (nsHtml5Portability::lowerCaseLiteralIsPrefixOfIgnoreAsciiCaseString( "-//w3c//dtd xhtml 1.0 frameset//", publicIdentifier)) { return true; } if (systemIdentifier) { if (nsHtml5Portability::lowerCaseLiteralIsPrefixOfIgnoreAsciiCaseString( "-//w3c//dtd html 4.01 transitional//", publicIdentifier)) { return true; } if (nsHtml5Portability::lowerCaseLiteralIsPrefixOfIgnoreAsciiCaseString( "-//w3c//dtd html 4.01 frameset//", publicIdentifier)) { return true; } } return false; } bool nsHtml5TreeBuilder::isQuirky(nsAtom* name, nsHtml5String publicIdentifier, nsHtml5String systemIdentifier, bool forceQuirks) { if (forceQuirks) { return true; } if (name != nsGkAtoms::html) { return true; } if (publicIdentifier) { for (int32_t i = 0; i < nsHtml5TreeBuilder::QUIRKY_PUBLIC_IDS.length; i++) { if (nsHtml5Portability::lowerCaseLiteralIsPrefixOfIgnoreAsciiCaseString( nsHtml5TreeBuilder::QUIRKY_PUBLIC_IDS[i], publicIdentifier)) { return true; } } if (nsHtml5Portability::lowerCaseLiteralEqualsIgnoreAsciiCaseString( "-//w3o//dtd w3 html strict 3.0//en//", publicIdentifier) || nsHtml5Portability::lowerCaseLiteralEqualsIgnoreAsciiCaseString( "-/w3c/dtd html 4.0 transitional/en", publicIdentifier) || nsHtml5Portability::lowerCaseLiteralEqualsIgnoreAsciiCaseString( "html", publicIdentifier)) { return true; } } if (!systemIdentifier) { if (nsHtml5Portability::lowerCaseLiteralIsPrefixOfIgnoreAsciiCaseString( "-//w3c//dtd html 4.01 transitional//", publicIdentifier)) { return true; } else if (nsHtml5Portability:: lowerCaseLiteralIsPrefixOfIgnoreAsciiCaseString( "-//w3c//dtd html 4.01 frameset//", publicIdentifier)) { return true; } } else if (nsHtml5Portability::lowerCaseLiteralEqualsIgnoreAsciiCaseString( "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd", systemIdentifier)) { return true; } return false; } void nsHtml5TreeBuilder::closeTheCell(int32_t eltPos) { generateImpliedEndTags(); if (!!MOZ_UNLIKELY(mViewSource) && eltPos != currentPtr) { errUnclosedElementsCell(eltPos); } while (currentPtr >= eltPos) { pop(); } clearTheListOfActiveFormattingElementsUpToTheLastMarker(); mode = IN_ROW; return; } int32_t nsHtml5TreeBuilder::findLastInTableScopeTdTh() { for (int32_t i = currentPtr; i > 0; i--) { nsAtom* name = stack[i]->name; if (stack[i]->ns == kNameSpaceID_XHTML) { if (nsGkAtoms::td == name || nsGkAtoms::th == name) { return i; } else if (name == nsGkAtoms::table || name == nsGkAtoms::_template) { return nsHtml5TreeBuilder::NOT_FOUND_ON_STACK; } } } return nsHtml5TreeBuilder::NOT_FOUND_ON_STACK; } void nsHtml5TreeBuilder::clearStackBackTo(int32_t eltPos) { int32_t eltGroup = stack[eltPos]->getGroup(); while (currentPtr > eltPos) { if (stack[currentPtr]->ns == kNameSpaceID_XHTML && stack[currentPtr]->getGroup() == TEMPLATE && (eltGroup == TABLE || eltGroup == TBODY_OR_THEAD_OR_TFOOT || eltGroup == TR || !eltPos)) { return; } pop(); } } void nsHtml5TreeBuilder::resetTheInsertionMode() { nsHtml5StackNode* node; nsAtom* name; int32_t ns; for (int32_t i = currentPtr; i >= 0; i--) { node = stack[i]; name = node->name; ns = node->ns; if (!i) { if (!(contextNamespace == kNameSpaceID_XHTML && (contextName == nsGkAtoms::td || contextName == nsGkAtoms::th))) { if (fragment) { name = contextName; ns = contextNamespace; } } else { mode = framesetOk ? FRAMESET_OK : IN_BODY; return; } } if (nsGkAtoms::select == name) { int32_t ancestorIndex = i; while (ancestorIndex > 0) { nsHtml5StackNode* ancestor = stack[ancestorIndex--]; if (kNameSpaceID_XHTML == ancestor->ns) { if (nsGkAtoms::_template == ancestor->name) { break; } if (nsGkAtoms::table == ancestor->name) { mode = IN_SELECT_IN_TABLE; return; } } } mode = IN_SELECT; return; } else if (nsGkAtoms::td == name || nsGkAtoms::th == name) { mode = IN_CELL; return; } else if (nsGkAtoms::tr == name) { mode = IN_ROW; return; } else if (nsGkAtoms::tbody == name || nsGkAtoms::thead == name || nsGkAtoms::tfoot == name) { mode = IN_TABLE_BODY; return; } else if (nsGkAtoms::caption == name) { mode = IN_CAPTION; return; } else if (nsGkAtoms::colgroup == name) { mode = IN_COLUMN_GROUP; return; } else if (nsGkAtoms::table == name) { mode = IN_TABLE; return; } else if (kNameSpaceID_XHTML != ns) { mode = framesetOk ? FRAMESET_OK : IN_BODY; return; } else if (nsGkAtoms::_template == name) { MOZ_ASSERT(templateModePtr >= 0); mode = templateModeStack[templateModePtr]; return; } else if (nsGkAtoms::head == name) { if (name == contextName) { mode = framesetOk ? FRAMESET_OK : IN_BODY; } else { mode = IN_HEAD; } return; } else if (nsGkAtoms::body == name) { mode = framesetOk ? FRAMESET_OK : IN_BODY; return; } else if (nsGkAtoms::frameset == name) { mode = IN_FRAMESET; return; } else if (nsGkAtoms::html == name) { if (!headPointer) { mode = BEFORE_HEAD; } else { mode = AFTER_HEAD; } return; } else if (!i) { mode = framesetOk ? FRAMESET_OK : IN_BODY; return; } } } void nsHtml5TreeBuilder::implicitlyCloseP() { int32_t eltPos = findLastInButtonScope(nsGkAtoms::p); if (eltPos == nsHtml5TreeBuilder::NOT_FOUND_ON_STACK) { return; } generateImpliedEndTagsExceptFor(nsGkAtoms::p); if (!!MOZ_UNLIKELY(mViewSource) && eltPos != currentPtr) { errUnclosedElementsImplied(eltPos, nsGkAtoms::p); } while (currentPtr >= eltPos) { pop(); } } bool nsHtml5TreeBuilder::debugOnlyClearLastStackSlot() { stack[currentPtr] = nullptr; return true; } bool nsHtml5TreeBuilder::debugOnlyClearLastListSlot() { listOfActiveFormattingElements[listPtr] = nullptr; return true; } void nsHtml5TreeBuilder::pushTemplateMode(int32_t mode) { templateModePtr++; if (templateModePtr == templateModeStack.length) { jArray newStack = jArray::newJArray(templateModeStack.length + 64); nsHtml5ArrayCopy::arraycopy(templateModeStack, newStack, templateModeStack.length); templateModeStack = newStack; } templateModeStack[templateModePtr] = mode; } void nsHtml5TreeBuilder::push(nsHtml5StackNode* node) { currentPtr++; if (currentPtr == stack.length) { jArray newStack = jArray::newJArray(stack.length + 64); nsHtml5ArrayCopy::arraycopy(stack, newStack, stack.length); stack = newStack; } stack[currentPtr] = node; elementPushed(node->ns, node->popName, node->node); } void nsHtml5TreeBuilder::silentPush(nsHtml5StackNode* node) { currentPtr++; if (currentPtr == stack.length) { jArray newStack = jArray::newJArray(stack.length + 64); nsHtml5ArrayCopy::arraycopy(stack, newStack, stack.length); stack = newStack; } stack[currentPtr] = node; } void nsHtml5TreeBuilder::append(nsHtml5StackNode* node) { listPtr++; if (listPtr == listOfActiveFormattingElements.length) { jArray newList = jArray::newJArray( listOfActiveFormattingElements.length + 64); nsHtml5ArrayCopy::arraycopy(listOfActiveFormattingElements, newList, listOfActiveFormattingElements.length); listOfActiveFormattingElements = newList; } listOfActiveFormattingElements[listPtr] = node; } void nsHtml5TreeBuilder:: clearTheListOfActiveFormattingElementsUpToTheLastMarker() { while (listPtr > -1) { if (!listOfActiveFormattingElements[listPtr]) { --listPtr; return; } listOfActiveFormattingElements[listPtr]->release(this); --listPtr; } } void nsHtml5TreeBuilder::removeFromStack(int32_t pos) { if (currentPtr == pos) { pop(); } else { stack[pos]->release(this); nsHtml5ArrayCopy::arraycopy(stack, pos + 1, pos, currentPtr - pos); MOZ_ASSERT(debugOnlyClearLastStackSlot()); currentPtr--; } } void nsHtml5TreeBuilder::removeFromStack(nsHtml5StackNode* node) { if (stack[currentPtr] == node) { pop(); } else { int32_t pos = currentPtr - 1; while (pos >= 0 && stack[pos] != node) { pos--; } if (pos == -1) { return; } node->release(this); nsHtml5ArrayCopy::arraycopy(stack, pos + 1, pos, currentPtr - pos); currentPtr--; } } void nsHtml5TreeBuilder::removeFromListOfActiveFormattingElements(int32_t pos) { MOZ_ASSERT(!!listOfActiveFormattingElements[pos]); listOfActiveFormattingElements[pos]->release(this); if (pos == listPtr) { MOZ_ASSERT(debugOnlyClearLastListSlot()); listPtr--; return; } MOZ_ASSERT(pos < listPtr); nsHtml5ArrayCopy::arraycopy(listOfActiveFormattingElements, pos + 1, pos, listPtr - pos); MOZ_ASSERT(debugOnlyClearLastListSlot()); listPtr--; } bool nsHtml5TreeBuilder::adoptionAgencyEndTag(nsAtom* name) { if (stack[currentPtr]->ns == kNameSpaceID_XHTML && stack[currentPtr]->name == name && findInListOfActiveFormattingElements(stack[currentPtr]) == -1) { pop(); return true; } for (int32_t i = 0; i < 8; ++i) { int32_t formattingEltListPos = listPtr; while (formattingEltListPos > -1) { nsHtml5StackNode* listNode = listOfActiveFormattingElements[formattingEltListPos]; if (!listNode) { formattingEltListPos = -1; break; } else if (listNode->name == name) { break; } formattingEltListPos--; } if (formattingEltListPos == -1) { return false; } nsHtml5StackNode* formattingElt = listOfActiveFormattingElements[formattingEltListPos]; int32_t formattingEltStackPos = currentPtr; bool inScope = true; while (formattingEltStackPos > -1) { nsHtml5StackNode* node = stack[formattingEltStackPos]; if (node == formattingElt) { break; } else if (node->isScoping()) { inScope = false; } formattingEltStackPos--; } if (formattingEltStackPos == -1) { errNoElementToCloseButEndTagSeen(name); removeFromListOfActiveFormattingElements(formattingEltListPos); return true; } if (!inScope) { errNoElementToCloseButEndTagSeen(name); return true; } if (formattingEltStackPos != currentPtr) { errEndTagViolatesNestingRules(name); } int32_t furthestBlockPos = formattingEltStackPos + 1; while (furthestBlockPos <= currentPtr) { nsHtml5StackNode* node = stack[furthestBlockPos]; MOZ_ASSERT(furthestBlockPos > 0, "How is formattingEltStackPos + 1 not > 0?"); if (node->isSpecial()) { break; } furthestBlockPos++; } if (furthestBlockPos > currentPtr) { while (currentPtr >= formattingEltStackPos) { pop(); } removeFromListOfActiveFormattingElements(formattingEltListPos); return true; } nsHtml5StackNode* commonAncestor = stack[formattingEltStackPos - 1]; nsIContentHandle* insertionCommonAncestor = nodeFromStackWithBlinkCompat(formattingEltStackPos - 1); nsHtml5StackNode* furthestBlock = stack[furthestBlockPos]; int32_t bookmark = formattingEltListPos; int32_t nodePos = furthestBlockPos; nsHtml5StackNode* lastNode = furthestBlock; int32_t j = 0; for (;;) { ++j; nodePos--; if (nodePos == formattingEltStackPos) { break; } nsHtml5StackNode* node = stack[nodePos]; int32_t nodeListPos = findInListOfActiveFormattingElements(node); if (j > 3 && nodeListPos != -1) { removeFromListOfActiveFormattingElements(nodeListPos); if (nodeListPos <= formattingEltListPos) { formattingEltListPos--; } if (nodeListPos <= bookmark) { bookmark--; } nodeListPos = -1; } if (nodeListPos == -1) { MOZ_ASSERT(formattingEltStackPos < nodePos); MOZ_ASSERT(bookmark < nodePos); MOZ_ASSERT(furthestBlockPos > nodePos); removeFromStack(nodePos); furthestBlockPos--; continue; } if (nodePos == furthestBlockPos) { bookmark = nodeListPos + 1; } MOZ_ASSERT(node == listOfActiveFormattingElements[nodeListPos]); MOZ_ASSERT(node == stack[nodePos]); nsIContentHandle* clone = createElement( kNameSpaceID_XHTML, node->name, node->attributes->cloneAttributes(), insertionCommonAncestor, htmlCreator(node->getHtmlCreator())); nsHtml5StackNode* newNode = createStackNode( node->getFlags(), node->ns, node->name, clone, node->popName, node->attributes, node->getHtmlCreator()); node->dropAttributes(); stack[nodePos] = newNode; newNode->retain(); listOfActiveFormattingElements[nodeListPos] = newNode; node->release(this); node->release(this); node = newNode; detachFromParent(lastNode->node); appendElement(lastNode->node, nodeFromStackWithBlinkCompat(nodePos)); lastNode = node; } if (commonAncestor->isFosterParenting()) { detachFromParent(lastNode->node); insertIntoFosterParent(lastNode->node); } else { detachFromParent(lastNode->node); appendElement(lastNode->node, insertionCommonAncestor); } nsIContentHandle* clone = createElement( kNameSpaceID_XHTML, formattingElt->name, formattingElt->attributes->cloneAttributes(), furthestBlock->node, htmlCreator(formattingElt->getHtmlCreator())); nsHtml5StackNode* formattingClone = createStackNode( formattingElt->getFlags(), formattingElt->ns, formattingElt->name, clone, formattingElt->popName, formattingElt->attributes, formattingElt->getHtmlCreator()); formattingElt->dropAttributes(); appendChildrenToNewParent(furthestBlock->node, clone); appendElement(clone, furthestBlock->node); removeFromListOfActiveFormattingElements(formattingEltListPos); insertIntoListOfActiveFormattingElements(formattingClone, bookmark); MOZ_ASSERT(formattingEltStackPos < furthestBlockPos); removeFromStack(formattingEltStackPos); insertIntoStack(formattingClone, furthestBlockPos); } return true; } void nsHtml5TreeBuilder::insertIntoStack(nsHtml5StackNode* node, int32_t position) { MOZ_ASSERT(currentPtr + 1 < stack.length); MOZ_ASSERT(position <= currentPtr + 1); if (position == currentPtr + 1) { push(node); } else { nsHtml5ArrayCopy::arraycopy(stack, position, position + 1, (currentPtr - position) + 1); currentPtr++; stack[position] = node; } } void nsHtml5TreeBuilder::insertIntoListOfActiveFormattingElements( nsHtml5StackNode* formattingClone, int32_t bookmark) { formattingClone->retain(); MOZ_ASSERT(listPtr + 1 < listOfActiveFormattingElements.length); if (bookmark <= listPtr) { nsHtml5ArrayCopy::arraycopy(listOfActiveFormattingElements, bookmark, bookmark + 1, (listPtr - bookmark) + 1); } listPtr++; listOfActiveFormattingElements[bookmark] = formattingClone; } int32_t nsHtml5TreeBuilder::findInListOfActiveFormattingElements( nsHtml5StackNode* node) { for (int32_t i = listPtr; i >= 0; i--) { if (node == listOfActiveFormattingElements[i]) { return i; } } return -1; } int32_t nsHtml5TreeBuilder:: findInListOfActiveFormattingElementsContainsBetweenEndAndLastMarker( nsAtom* name) { for (int32_t i = listPtr; i >= 0; i--) { nsHtml5StackNode* node = listOfActiveFormattingElements[i]; if (!node) { return -1; } else if (node->name == name) { return i; } } return -1; } void nsHtml5TreeBuilder::maybeForgetEarlierDuplicateFormattingElement( nsAtom* name, nsHtml5HtmlAttributes* attributes) { int32_t candidate = -1; int32_t count = 0; for (int32_t i = listPtr; i >= 0; i--) { nsHtml5StackNode* node = listOfActiveFormattingElements[i]; if (!node) { break; } if (node->name == name && node->attributes->equalsAnother(attributes)) { candidate = i; ++count; } } if (count >= 3) { removeFromListOfActiveFormattingElements(candidate); } } int32_t nsHtml5TreeBuilder::findLastOrRoot(nsAtom* name) { for (int32_t i = currentPtr; i > 0; i--) { if (stack[i]->ns == kNameSpaceID_XHTML && stack[i]->name == name) { return i; } } return 0; } int32_t nsHtml5TreeBuilder::findLastOrRoot(int32_t group) { for (int32_t i = currentPtr; i > 0; i--) { if (stack[i]->getGroup() == group) { return i; } } return 0; } bool nsHtml5TreeBuilder::addAttributesToBody( nsHtml5HtmlAttributes* attributes) { if (currentPtr >= 1) { nsHtml5StackNode* body = stack[1]; if (body->getGroup() == nsHtml5TreeBuilder::BODY) { addAttributesToElement(body->node, attributes); return true; } } return false; } void nsHtml5TreeBuilder::addAttributesToHtml( nsHtml5HtmlAttributes* attributes) { addAttributesToElement(stack[0]->node, attributes); } void nsHtml5TreeBuilder::pushHeadPointerOntoStack() { MOZ_ASSERT(!!headPointer); MOZ_ASSERT(mode == AFTER_HEAD); silentPush(createStackNode(nsHtml5ElementName::ELT_HEAD, headPointer)); } void nsHtml5TreeBuilder::reconstructTheActiveFormattingElements() { if (listPtr == -1) { return; } nsHtml5StackNode* mostRecent = listOfActiveFormattingElements[listPtr]; if (!mostRecent || isInStack(mostRecent)) { return; } int32_t entryPos = listPtr; for (;;) { entryPos--; if (entryPos == -1) { break; } if (!listOfActiveFormattingElements[entryPos]) { break; } if (isInStack(listOfActiveFormattingElements[entryPos])) { break; } } while (entryPos < listPtr) { entryPos++; nsHtml5StackNode* entry = listOfActiveFormattingElements[entryPos]; nsHtml5StackNode* current = stack[currentPtr]; nsIContentHandle* clone; if (current->isFosterParenting()) { clone = createAndInsertFosterParentedElement( kNameSpaceID_XHTML, entry->name, entry->attributes->cloneAttributes(), htmlCreator(entry->getHtmlCreator())); } else { nsIContentHandle* currentNode = nodeFromStackWithBlinkCompat(currentPtr); clone = createElement(kNameSpaceID_XHTML, entry->name, entry->attributes->cloneAttributes(), currentNode, htmlCreator(entry->getHtmlCreator())); appendElement(clone, currentNode); } nsHtml5StackNode* entryClone = createStackNode( entry->getFlags(), entry->ns, entry->name, clone, entry->popName, entry->attributes, entry->getHtmlCreator()); entry->dropAttributes(); push(entryClone); listOfActiveFormattingElements[entryPos] = entryClone; entry->release(this); entryClone->retain(); } } void nsHtml5TreeBuilder::notifyUnusedStackNode(int32_t idxInStackNodes) { if (idxInStackNodes < stackNodesIdx) { stackNodesIdx = idxInStackNodes; } } nsHtml5StackNode* nsHtml5TreeBuilder::getUnusedStackNode() { while (stackNodesIdx < numStackNodes) { if (stackNodes[stackNodesIdx]->isUnused()) { return stackNodes[stackNodesIdx++]; } stackNodesIdx++; } if (stackNodesIdx < stackNodes.length) { stackNodes[stackNodesIdx] = new nsHtml5StackNode(stackNodesIdx); numStackNodes++; return stackNodes[stackNodesIdx++]; } jArray newStack = jArray::newJArray(stackNodes.length + 64); nsHtml5ArrayCopy::arraycopy(stackNodes, newStack, stackNodes.length); stackNodes = newStack; stackNodes[stackNodesIdx] = new nsHtml5StackNode(stackNodesIdx); numStackNodes++; return stackNodes[stackNodesIdx++]; } nsHtml5StackNode* nsHtml5TreeBuilder::createStackNode( int32_t flags, int32_t ns, nsAtom* name, nsIContentHandle* node, nsAtom* popName, nsHtml5HtmlAttributes* attributes, mozilla::dom::HTMLContentCreatorFunction htmlCreator) { nsHtml5StackNode* instance = getUnusedStackNode(); instance->setValues(flags, ns, name, node, popName, attributes, htmlCreator); return instance; } nsHtml5StackNode* nsHtml5TreeBuilder::createStackNode( nsHtml5ElementName* elementName, nsIContentHandle* node) { nsHtml5StackNode* instance = getUnusedStackNode(); instance->setValues(elementName, node); return instance; } nsHtml5StackNode* nsHtml5TreeBuilder::createStackNode( nsHtml5ElementName* elementName, nsIContentHandle* node, nsHtml5HtmlAttributes* attributes) { nsHtml5StackNode* instance = getUnusedStackNode(); instance->setValues(elementName, node, attributes); return instance; } nsHtml5StackNode* nsHtml5TreeBuilder::createStackNode( nsHtml5ElementName* elementName, nsIContentHandle* node, nsAtom* popName) { nsHtml5StackNode* instance = getUnusedStackNode(); instance->setValues(elementName, node, popName); return instance; } nsHtml5StackNode* nsHtml5TreeBuilder::createStackNode( nsHtml5ElementName* elementName, nsAtom* popName, nsIContentHandle* node) { nsHtml5StackNode* instance = getUnusedStackNode(); instance->setValues(elementName, popName, node); return instance; } nsHtml5StackNode* nsHtml5TreeBuilder::createStackNode( nsHtml5ElementName* elementName, nsIContentHandle* node, nsAtom* popName, bool markAsIntegrationPoint) { nsHtml5StackNode* instance = getUnusedStackNode(); instance->setValues(elementName, node, popName, markAsIntegrationPoint); return instance; } void nsHtml5TreeBuilder::insertIntoFosterParent(nsIContentHandle* child) { int32_t tablePos = findLastOrRoot(nsHtml5TreeBuilder::TABLE); int32_t templatePos = findLastOrRoot(nsHtml5TreeBuilder::TEMPLATE); if (templatePos >= tablePos) { appendElement(child, stack[templatePos]->node); return; } nsHtml5StackNode* node = stack[tablePos]; insertFosterParentedChild(child, node->node, stack[tablePos - 1]->node); } nsIContentHandle* nsHtml5TreeBuilder::createAndInsertFosterParentedElement( int32_t ns, nsAtom* name, nsHtml5HtmlAttributes* attributes, nsHtml5ContentCreatorFunction creator) { return createAndInsertFosterParentedElement(ns, name, attributes, nullptr, creator); } nsIContentHandle* nsHtml5TreeBuilder::createAndInsertFosterParentedElement( int32_t ns, nsAtom* name, nsHtml5HtmlAttributes* attributes, nsIContentHandle* form, nsHtml5ContentCreatorFunction creator) { int32_t tablePos = findLastOrRoot(nsHtml5TreeBuilder::TABLE); int32_t templatePos = findLastOrRoot(nsHtml5TreeBuilder::TEMPLATE); if (templatePos >= tablePos) { nsIContentHandle* child = createElement(ns, name, attributes, form, stack[templatePos]->node, creator); appendElement(child, stack[templatePos]->node); return child; } nsHtml5StackNode* node = stack[tablePos]; return createAndInsertFosterParentedElement( ns, name, attributes, form, node->node, stack[tablePos - 1]->node, creator); } bool nsHtml5TreeBuilder::isInStack(nsHtml5StackNode* node) { for (int32_t i = currentPtr; i >= 0; i--) { if (stack[i] == node) { return true; } } return false; } void nsHtml5TreeBuilder::popTemplateMode() { templateModePtr--; } void nsHtml5TreeBuilder::pop() { nsHtml5StackNode* node = stack[currentPtr]; MOZ_ASSERT(debugOnlyClearLastStackSlot()); currentPtr--; elementPopped(node->ns, node->popName, node->node); node->release(this); } void nsHtml5TreeBuilder::popForeign(int32_t origPos, int32_t eltPos) { nsHtml5StackNode* node = stack[currentPtr]; if (origPos != currentPtr || eltPos != currentPtr) { markMalformedIfScript(node->node); } MOZ_ASSERT(debugOnlyClearLastStackSlot()); currentPtr--; elementPopped(node->ns, node->popName, node->node); node->release(this); } void nsHtml5TreeBuilder::silentPop() { nsHtml5StackNode* node = stack[currentPtr]; MOZ_ASSERT(debugOnlyClearLastStackSlot()); currentPtr--; node->release(this); } void nsHtml5TreeBuilder::popOnEof() { nsHtml5StackNode* node = stack[currentPtr]; MOZ_ASSERT(debugOnlyClearLastStackSlot()); currentPtr--; markMalformedIfScript(node->node); elementPopped(node->ns, node->popName, node->node); node->release(this); } void nsHtml5TreeBuilder::appendHtmlElementToDocumentAndPush( nsHtml5HtmlAttributes* attributes) { nsIContentHandle* elt = createHtmlElementSetAsRoot(attributes); nsHtml5StackNode* node = createStackNode(nsHtml5ElementName::ELT_HTML, elt); push(node); } void nsHtml5TreeBuilder::appendHtmlElementToDocumentAndPush() { appendHtmlElementToDocumentAndPush(tokenizer->emptyAttributes()); } void nsHtml5TreeBuilder::appendToCurrentNodeAndPushHeadElement( nsHtml5HtmlAttributes* attributes) { nsIContentHandle* currentNode = nodeFromStackWithBlinkCompat(currentPtr); nsIContentHandle* elt = createElement(kNameSpaceID_XHTML, nsGkAtoms::head, attributes, currentNode, htmlCreator(NS_NewHTMLSharedElement)); appendElement(elt, currentNode); headPointer = elt; nsHtml5StackNode* node = createStackNode(nsHtml5ElementName::ELT_HEAD, elt); push(node); } void nsHtml5TreeBuilder::appendToCurrentNodeAndPushBodyElement( nsHtml5HtmlAttributes* attributes) { appendToCurrentNodeAndPushElement(nsHtml5ElementName::ELT_BODY, attributes); } void nsHtml5TreeBuilder::appendToCurrentNodeAndPushBodyElement() { appendToCurrentNodeAndPushBodyElement(tokenizer->emptyAttributes()); } void nsHtml5TreeBuilder::appendToCurrentNodeAndPushFormElementMayFoster( nsHtml5HtmlAttributes* attributes) { nsIContentHandle* elt; nsHtml5StackNode* current = stack[currentPtr]; if (current->isFosterParenting()) { elt = createAndInsertFosterParentedElement( kNameSpaceID_XHTML, nsGkAtoms::form, attributes, htmlCreator(NS_NewHTMLFormElement)); } else { nsIContentHandle* currentNode = nodeFromStackWithBlinkCompat(currentPtr); elt = createElement(kNameSpaceID_XHTML, nsGkAtoms::form, attributes, currentNode, htmlCreator(NS_NewHTMLFormElement)); appendElement(elt, currentNode); } if (!isTemplateContents()) { formPointer = elt; } nsHtml5StackNode* node = createStackNode(nsHtml5ElementName::ELT_FORM, elt); push(node); } void nsHtml5TreeBuilder::appendToCurrentNodeAndPushFormattingElementMayFoster( nsHtml5ElementName* elementName, nsHtml5HtmlAttributes* attributes) { nsHtml5HtmlAttributes* clone = attributes->cloneAttributes(); nsIContentHandle* elt; nsHtml5StackNode* current = stack[currentPtr]; if (current->isFosterParenting()) { elt = createAndInsertFosterParentedElement( kNameSpaceID_XHTML, elementName->getName(), attributes, htmlCreator(elementName->getHtmlCreator())); } else { nsIContentHandle* currentNode = nodeFromStackWithBlinkCompat(currentPtr); elt = createElement(kNameSpaceID_XHTML, elementName->getName(), attributes, currentNode, htmlCreator(elementName->getHtmlCreator())); appendElement(elt, currentNode); } nsHtml5StackNode* node = createStackNode(elementName, elt, clone); push(node); append(node); node->retain(); } void nsHtml5TreeBuilder::appendToCurrentNodeAndPushElement( nsHtml5ElementName* elementName, nsHtml5HtmlAttributes* attributes) { nsIContentHandle* currentNode = nodeFromStackWithBlinkCompat(currentPtr); nsIContentHandle* elt = createElement(kNameSpaceID_XHTML, elementName->getName(), attributes, currentNode, htmlCreator(elementName->getHtmlCreator())); appendElement(elt, currentNode); if (nsHtml5ElementName::ELT_TEMPLATE == elementName) { elt = getDocumentFragmentForTemplate(elt); } nsHtml5StackNode* node = createStackNode(elementName, elt); push(node); } void nsHtml5TreeBuilder::appendToCurrentNodeAndPushElementMayFoster( nsHtml5ElementName* elementName, nsHtml5HtmlAttributes* attributes) { nsAtom* popName = elementName->getName(); nsIContentHandle* elt; nsHtml5StackNode* current = stack[currentPtr]; if (current->isFosterParenting()) { elt = createAndInsertFosterParentedElement( kNameSpaceID_XHTML, popName, attributes, htmlCreator(elementName->getHtmlCreator())); } else { nsIContentHandle* currentNode = nodeFromStackWithBlinkCompat(currentPtr); elt = createElement(kNameSpaceID_XHTML, popName, attributes, currentNode, htmlCreator(elementName->getHtmlCreator())); appendElement(elt, currentNode); } nsHtml5StackNode* node = createStackNode(elementName, elt, popName); push(node); } void nsHtml5TreeBuilder::appendToCurrentNodeAndPushElementMayFosterMathML( nsHtml5ElementName* elementName, nsHtml5HtmlAttributes* attributes) { nsAtom* popName = elementName->getName(); bool markAsHtmlIntegrationPoint = false; if (nsHtml5ElementName::ELT_ANNOTATION_XML == elementName && annotationXmlEncodingPermitsHtml(attributes)) { markAsHtmlIntegrationPoint = true; } nsIContentHandle* elt; nsHtml5StackNode* current = stack[currentPtr]; if (current->isFosterParenting()) { elt = createAndInsertFosterParentedElement( kNameSpaceID_MathML, popName, attributes, htmlCreator(nullptr)); } else { nsIContentHandle* currentNode = nodeFromStackWithBlinkCompat(currentPtr); elt = createElement(kNameSpaceID_MathML, popName, attributes, currentNode, htmlCreator(nullptr)); appendElement(elt, currentNode); } nsHtml5StackNode* node = createStackNode(elementName, elt, popName, markAsHtmlIntegrationPoint); push(node); } bool nsHtml5TreeBuilder::annotationXmlEncodingPermitsHtml( nsHtml5HtmlAttributes* attributes) { nsHtml5String encoding = attributes->getValue(nsHtml5AttributeName::ATTR_ENCODING); if (!encoding) { return false; } return nsHtml5Portability::lowerCaseLiteralEqualsIgnoreAsciiCaseString( "application/xhtml+xml", encoding) || nsHtml5Portability::lowerCaseLiteralEqualsIgnoreAsciiCaseString( "text/html", encoding); } void nsHtml5TreeBuilder::appendToCurrentNodeAndPushElementMayFosterSVG( nsHtml5ElementName* elementName, nsHtml5HtmlAttributes* attributes) { nsAtom* popName = elementName->getCamelCaseName(); nsIContentHandle* elt; nsHtml5StackNode* current = stack[currentPtr]; if (current->isFosterParenting()) { elt = createAndInsertFosterParentedElement( kNameSpaceID_SVG, popName, attributes, svgCreator(elementName->getSvgCreator())); } else { nsIContentHandle* currentNode = nodeFromStackWithBlinkCompat(currentPtr); elt = createElement(kNameSpaceID_SVG, popName, attributes, currentNode, svgCreator(elementName->getSvgCreator())); appendElement(elt, currentNode); } nsHtml5StackNode* node = createStackNode(elementName, popName, elt); push(node); } void nsHtml5TreeBuilder::appendToCurrentNodeAndPushElementMayFoster( nsHtml5ElementName* elementName, nsHtml5HtmlAttributes* attributes, nsIContentHandle* form) { nsIContentHandle* elt; nsIContentHandle* formOwner = !form || fragment || isTemplateContents() ? nullptr : form; nsHtml5StackNode* current = stack[currentPtr]; if (current->isFosterParenting()) { elt = createAndInsertFosterParentedElement( kNameSpaceID_XHTML, elementName->getName(), attributes, formOwner, htmlCreator(elementName->getHtmlCreator())); } else { nsIContentHandle* currentNode = nodeFromStackWithBlinkCompat(currentPtr); elt = createElement(kNameSpaceID_XHTML, elementName->getName(), attributes, formOwner, currentNode, htmlCreator(elementName->getHtmlCreator())); appendElement(elt, currentNode); } nsHtml5StackNode* node = createStackNode(elementName, elt); push(node); } void nsHtml5TreeBuilder::appendVoidElementToCurrentMayFoster( nsHtml5ElementName* elementName, nsHtml5HtmlAttributes* attributes, nsIContentHandle* form) { nsAtom* name = elementName->getName(); nsIContentHandle* elt; nsIContentHandle* formOwner = !form || fragment || isTemplateContents() ? nullptr : form; nsHtml5StackNode* current = stack[currentPtr]; if (current->isFosterParenting()) { elt = createAndInsertFosterParentedElement( kNameSpaceID_XHTML, name, attributes, formOwner, htmlCreator(elementName->getHtmlCreator())); } else { nsIContentHandle* currentNode = nodeFromStackWithBlinkCompat(currentPtr); elt = createElement(kNameSpaceID_XHTML, name, attributes, formOwner, currentNode, htmlCreator(elementName->getHtmlCreator())); appendElement(elt, currentNode); } elementPushed(kNameSpaceID_XHTML, name, elt); elementPopped(kNameSpaceID_XHTML, name, elt); } void nsHtml5TreeBuilder::appendVoidElementToCurrentMayFoster( nsHtml5ElementName* elementName, nsHtml5HtmlAttributes* attributes) { nsAtom* popName = elementName->getName(); nsIContentHandle* elt; nsHtml5StackNode* current = stack[currentPtr]; if (current->isFosterParenting()) { elt = createAndInsertFosterParentedElement( kNameSpaceID_XHTML, popName, attributes, htmlCreator(elementName->getHtmlCreator())); } else { nsIContentHandle* currentNode = nodeFromStackWithBlinkCompat(currentPtr); elt = createElement(kNameSpaceID_XHTML, popName, attributes, currentNode, htmlCreator(elementName->getHtmlCreator())); appendElement(elt, currentNode); } elementPushed(kNameSpaceID_XHTML, popName, elt); elementPopped(kNameSpaceID_XHTML, popName, elt); } void nsHtml5TreeBuilder::appendVoidElementToCurrentMayFosterSVG( nsHtml5ElementName* elementName, nsHtml5HtmlAttributes* attributes) { nsAtom* popName = elementName->getCamelCaseName(); nsIContentHandle* elt; nsHtml5StackNode* current = stack[currentPtr]; if (current->isFosterParenting()) { elt = createAndInsertFosterParentedElement( kNameSpaceID_SVG, popName, attributes, svgCreator(elementName->getSvgCreator())); } else { nsIContentHandle* currentNode = nodeFromStackWithBlinkCompat(currentPtr); elt = createElement(kNameSpaceID_SVG, popName, attributes, currentNode, svgCreator(elementName->getSvgCreator())); appendElement(elt, currentNode); } elementPushed(kNameSpaceID_SVG, popName, elt); elementPopped(kNameSpaceID_SVG, popName, elt); } void nsHtml5TreeBuilder::appendVoidElementToCurrentMayFosterMathML( nsHtml5ElementName* elementName, nsHtml5HtmlAttributes* attributes) { nsAtom* popName = elementName->getName(); nsIContentHandle* elt; nsHtml5StackNode* current = stack[currentPtr]; if (current->isFosterParenting()) { elt = createAndInsertFosterParentedElement( kNameSpaceID_MathML, popName, attributes, htmlCreator(nullptr)); } else { nsIContentHandle* currentNode = nodeFromStackWithBlinkCompat(currentPtr); elt = createElement(kNameSpaceID_MathML, popName, attributes, currentNode, htmlCreator(nullptr)); appendElement(elt, currentNode); } elementPushed(kNameSpaceID_MathML, popName, elt); elementPopped(kNameSpaceID_MathML, popName, elt); } void nsHtml5TreeBuilder::appendVoidInputToCurrent( nsHtml5HtmlAttributes* attributes, nsIContentHandle* form) { nsIContentHandle* currentNode = nodeFromStackWithBlinkCompat(currentPtr); nsIContentHandle* elt = createElement(kNameSpaceID_XHTML, nsGkAtoms::input, attributes, !form || fragment || isTemplateContents() ? nullptr : form, currentNode, htmlCreator(NS_NewHTMLInputElement)); appendElement(elt, currentNode); elementPushed(kNameSpaceID_XHTML, nsGkAtoms::input, elt); elementPopped(kNameSpaceID_XHTML, nsGkAtoms::input, elt); } void nsHtml5TreeBuilder::appendVoidFormToCurrent( nsHtml5HtmlAttributes* attributes) { nsIContentHandle* currentNode = nodeFromStackWithBlinkCompat(currentPtr); nsIContentHandle* elt = createElement(kNameSpaceID_XHTML, nsGkAtoms::form, attributes, currentNode, htmlCreator(NS_NewHTMLFormElement)); formPointer = elt; appendElement(elt, currentNode); elementPushed(kNameSpaceID_XHTML, nsGkAtoms::form, elt); elementPopped(kNameSpaceID_XHTML, nsGkAtoms::form, elt); } void nsHtml5TreeBuilder::requestSuspension() { tokenizer->requestSuspension(); } ; bool nsHtml5TreeBuilder::isInForeign() { return currentPtr >= 0 && stack[currentPtr]->ns != kNameSpaceID_XHTML; } bool nsHtml5TreeBuilder::isInForeignButNotHtmlOrMathTextIntegrationPoint() { if (currentPtr < 0) { return false; } return !isSpecialParentInForeign(stack[currentPtr]); } void nsHtml5TreeBuilder::setFragmentContext(nsAtom* context, int32_t ns, nsIContentHandle* node, bool quirks) { this->contextName = context; this->contextNamespace = ns; this->contextNode = node; this->fragment = (!!contextName); this->quirks = quirks; } nsIContentHandle* nsHtml5TreeBuilder::currentNode() { return stack[currentPtr]->node; } bool nsHtml5TreeBuilder::isScriptingEnabled() { return scriptingEnabled; } void nsHtml5TreeBuilder::setScriptingEnabled(bool scriptingEnabled) { this->scriptingEnabled = scriptingEnabled; } void nsHtml5TreeBuilder::setIsSrcdocDocument(bool isSrcdocDocument) { this->isSrcdocDocument = isSrcdocDocument; } void nsHtml5TreeBuilder::flushCharacters() { if (charBufferLen > 0) { if ((mode == IN_TABLE || mode == IN_TABLE_BODY || mode == IN_ROW) && charBufferContainsNonWhitespace()) { errNonSpaceInTable(); reconstructTheActiveFormattingElements(); if (!stack[currentPtr]->isFosterParenting()) { appendCharacters(currentNode(), charBuffer, 0, charBufferLen); charBufferLen = 0; return; } int32_t tablePos = findLastOrRoot(nsHtml5TreeBuilder::TABLE); int32_t templatePos = findLastOrRoot(nsHtml5TreeBuilder::TEMPLATE); if (templatePos >= tablePos) { appendCharacters(stack[templatePos]->node, charBuffer, 0, charBufferLen); charBufferLen = 0; return; } nsHtml5StackNode* tableElt = stack[tablePos]; insertFosterParentedCharacters(charBuffer, 0, charBufferLen, tableElt->node, stack[tablePos - 1]->node); charBufferLen = 0; return; } appendCharacters(currentNode(), charBuffer, 0, charBufferLen); charBufferLen = 0; } } bool nsHtml5TreeBuilder::charBufferContainsNonWhitespace() { for (int32_t i = 0; i < charBufferLen; i++) { switch (charBuffer[i]) { case ' ': case '\t': case '\n': case '\r': case '\f': { continue; } default: { return true; } } } return false; } nsAHtml5TreeBuilderState* nsHtml5TreeBuilder::newSnapshot() { jArray listCopy = jArray::newJArray(listPtr + 1); for (int32_t i = 0; i < listCopy.length; i++) { nsHtml5StackNode* node = listOfActiveFormattingElements[i]; if (node) { nsHtml5StackNode* newNode = new nsHtml5StackNode(-1); newNode->setValues(node->getFlags(), node->ns, node->name, node->node, node->popName, node->attributes->cloneAttributes(), node->getHtmlCreator()); listCopy[i] = newNode; } else { listCopy[i] = nullptr; } } jArray stackCopy = jArray::newJArray(currentPtr + 1); for (int32_t i = 0; i < stackCopy.length; i++) { nsHtml5StackNode* node = stack[i]; int32_t listIndex = findInListOfActiveFormattingElements(node); if (listIndex == -1) { nsHtml5StackNode* newNode = new nsHtml5StackNode(-1); newNode->setValues(node->getFlags(), node->ns, node->name, node->node, node->popName, nullptr, node->getHtmlCreator()); stackCopy[i] = newNode; } else { stackCopy[i] = listCopy[listIndex]; stackCopy[i]->retain(); } } jArray templateModeStackCopy = jArray::newJArray(templateModePtr + 1); nsHtml5ArrayCopy::arraycopy(templateModeStack, templateModeStackCopy, templateModeStackCopy.length); return new nsHtml5StateSnapshot(stackCopy, listCopy, templateModeStackCopy, formPointer, headPointer, mode, originalMode, framesetOk, needToDropLF, quirks); } bool nsHtml5TreeBuilder::snapshotMatches(nsAHtml5TreeBuilderState* snapshot) { jArray stackCopy = snapshot->getStack(); int32_t stackLen = snapshot->getStackLength(); jArray listCopy = snapshot->getListOfActiveFormattingElements(); int32_t listLen = snapshot->getListOfActiveFormattingElementsLength(); jArray templateModeStackCopy = snapshot->getTemplateModeStack(); int32_t templateModeStackLen = snapshot->getTemplateModeStackLength(); if (stackLen != currentPtr + 1 || listLen != listPtr + 1 || templateModeStackLen != templateModePtr + 1 || formPointer != snapshot->getFormPointer() || headPointer != snapshot->getHeadPointer() || mode != snapshot->getMode() || originalMode != snapshot->getOriginalMode() || framesetOk != snapshot->isFramesetOk() || needToDropLF != snapshot->isNeedToDropLF() || quirks != snapshot->isQuirks()) { return false; } for (int32_t i = listLen - 1; i >= 0; i--) { if (!listCopy[i] && !listOfActiveFormattingElements[i]) { continue; } else if (!listCopy[i] || !listOfActiveFormattingElements[i]) { return false; } if (listCopy[i]->node != listOfActiveFormattingElements[i]->node) { return false; } } for (int32_t i = stackLen - 1; i >= 0; i--) { if (stackCopy[i]->node != stack[i]->node) { return false; } } for (int32_t i = templateModeStackLen - 1; i >= 0; i--) { if (templateModeStackCopy[i] != templateModeStack[i]) { return false; } } return true; } void nsHtml5TreeBuilder::loadState(nsAHtml5TreeBuilderState* snapshot) { mCurrentHtmlScriptIsAsyncOrDefer = false; jArray stackCopy = snapshot->getStack(); int32_t stackLen = snapshot->getStackLength(); jArray listCopy = snapshot->getListOfActiveFormattingElements(); int32_t listLen = snapshot->getListOfActiveFormattingElementsLength(); jArray templateModeStackCopy = snapshot->getTemplateModeStack(); int32_t templateModeStackLen = snapshot->getTemplateModeStackLength(); for (int32_t i = 0; i <= listPtr; i++) { if (listOfActiveFormattingElements[i]) { listOfActiveFormattingElements[i]->release(this); } } if (listOfActiveFormattingElements.length < listLen) { listOfActiveFormattingElements = jArray::newJArray(listLen); } listPtr = listLen - 1; for (int32_t i = 0; i <= currentPtr; i++) { stack[i]->release(this); } if (stack.length < stackLen) { stack = jArray::newJArray(stackLen); } currentPtr = stackLen - 1; if (templateModeStack.length < templateModeStackLen) { templateModeStack = jArray::newJArray(templateModeStackLen); } templateModePtr = templateModeStackLen - 1; for (int32_t i = 0; i < listLen; i++) { nsHtml5StackNode* node = listCopy[i]; if (node) { nsHtml5StackNode* newNode = createStackNode( node->getFlags(), node->ns, node->name, node->node, node->popName, node->attributes->cloneAttributes(), node->getHtmlCreator()); listOfActiveFormattingElements[i] = newNode; } else { listOfActiveFormattingElements[i] = nullptr; } } for (int32_t i = 0; i < stackLen; i++) { nsHtml5StackNode* node = stackCopy[i]; int32_t listIndex = findInArray(node, listCopy); if (listIndex == -1) { nsHtml5StackNode* newNode = createStackNode(node->getFlags(), node->ns, node->name, node->node, node->popName, nullptr, node->getHtmlCreator()); stack[i] = newNode; } else { stack[i] = listOfActiveFormattingElements[listIndex]; stack[i]->retain(); } } nsHtml5ArrayCopy::arraycopy(templateModeStackCopy, templateModeStack, templateModeStackLen); formPointer = snapshot->getFormPointer(); headPointer = snapshot->getHeadPointer(); mode = snapshot->getMode(); originalMode = snapshot->getOriginalMode(); framesetOk = snapshot->isFramesetOk(); needToDropLF = snapshot->isNeedToDropLF(); quirks = snapshot->isQuirks(); } int32_t nsHtml5TreeBuilder::findInArray( nsHtml5StackNode* node, jArray arr) { for (int32_t i = listPtr; i >= 0; i--) { if (node == arr[i]) { return i; } } return -1; } nsIContentHandle* nsHtml5TreeBuilder::nodeFromStackWithBlinkCompat( int32_t stackPos) { if (stackPos > 511) { errDeepTree(); return stack[511]->node; } return stack[stackPos]->node; } nsIContentHandle* nsHtml5TreeBuilder::getFormPointer() { return formPointer; } nsIContentHandle* nsHtml5TreeBuilder::getHeadPointer() { return headPointer; } jArray nsHtml5TreeBuilder::getListOfActiveFormattingElements() { return listOfActiveFormattingElements; } jArray nsHtml5TreeBuilder::getStack() { return stack; } jArray nsHtml5TreeBuilder::getTemplateModeStack() { return templateModeStack; } int32_t nsHtml5TreeBuilder::getMode() { return mode; } int32_t nsHtml5TreeBuilder::getOriginalMode() { return originalMode; } bool nsHtml5TreeBuilder::isFramesetOk() { return framesetOk; } bool nsHtml5TreeBuilder::isNeedToDropLF() { return needToDropLF; } bool nsHtml5TreeBuilder::isQuirks() { return quirks; } int32_t nsHtml5TreeBuilder::getListOfActiveFormattingElementsLength() { return listPtr + 1; } int32_t nsHtml5TreeBuilder::getStackLength() { return currentPtr + 1; } int32_t nsHtml5TreeBuilder::getTemplateModeStackLength() { return templateModePtr + 1; } void nsHtml5TreeBuilder::initializeStatics() {} void nsHtml5TreeBuilder::releaseStatics() {} #include "nsHtml5TreeBuilderCppSupplement.h"