зеркало из https://github.com/mozilla/gecko-dev.git
4718 строки
153 KiB
C++
4718 строки
153 KiB
C++
/*
|
|
* 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<const char*, int32_t> nsHtml5TreeBuilder::QUIRKY_PUBLIC_IDS = {
|
|
QUIRKY_PUBLIC_IDS_DATA, MOZ_ARRAY_LENGTH(QUIRKY_PUBLIC_IDS_DATA)};
|
|
void nsHtml5TreeBuilder::startTokenization(nsHtml5Tokenizer* self) {
|
|
tokenizer = self;
|
|
stackNodes = jArray<nsHtml5StackNode*, int32_t>::newJArray(64);
|
|
stack = jArray<nsHtml5StackNode*, int32_t>::newJArray(64);
|
|
templateModeStack = jArray<int32_t, int32_t>::newJArray(64);
|
|
listOfActiveFormattingElements =
|
|
jArray<nsHtml5StackNode*, int32_t>::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<char16_t, int32_t> 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<int32_t, int32_t> newStack =
|
|
jArray<int32_t, int32_t>::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<nsHtml5StackNode*, int32_t> newStack =
|
|
jArray<nsHtml5StackNode*, int32_t>::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<nsHtml5StackNode*, int32_t> newStack =
|
|
jArray<nsHtml5StackNode*, int32_t>::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<nsHtml5StackNode*, int32_t> newList =
|
|
jArray<nsHtml5StackNode*, int32_t>::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<nsHtml5StackNode*, int32_t> newStack =
|
|
jArray<nsHtml5StackNode*, int32_t>::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<nsHtml5StackNode*, int32_t> listCopy =
|
|
jArray<nsHtml5StackNode*, int32_t>::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<nsHtml5StackNode*, int32_t> stackCopy =
|
|
jArray<nsHtml5StackNode*, int32_t>::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<int32_t, int32_t> templateModeStackCopy =
|
|
jArray<int32_t, int32_t>::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<nsHtml5StackNode*, int32_t> stackCopy = snapshot->getStack();
|
|
int32_t stackLen = snapshot->getStackLength();
|
|
jArray<nsHtml5StackNode*, int32_t> listCopy =
|
|
snapshot->getListOfActiveFormattingElements();
|
|
int32_t listLen = snapshot->getListOfActiveFormattingElementsLength();
|
|
jArray<int32_t, int32_t> 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<nsHtml5StackNode*, int32_t> stackCopy = snapshot->getStack();
|
|
int32_t stackLen = snapshot->getStackLength();
|
|
jArray<nsHtml5StackNode*, int32_t> listCopy =
|
|
snapshot->getListOfActiveFormattingElements();
|
|
int32_t listLen = snapshot->getListOfActiveFormattingElementsLength();
|
|
jArray<int32_t, int32_t> 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<nsHtml5StackNode*, int32_t>::newJArray(listLen);
|
|
}
|
|
listPtr = listLen - 1;
|
|
for (int32_t i = 0; i <= currentPtr; i++) {
|
|
stack[i]->release(this);
|
|
}
|
|
if (stack.length < stackLen) {
|
|
stack = jArray<nsHtml5StackNode*, int32_t>::newJArray(stackLen);
|
|
}
|
|
currentPtr = stackLen - 1;
|
|
if (templateModeStack.length < templateModeStackLen) {
|
|
templateModeStack =
|
|
jArray<int32_t, int32_t>::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<nsHtml5StackNode*, int32_t> 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<nsHtml5StackNode*, int32_t>
|
|
nsHtml5TreeBuilder::getListOfActiveFormattingElements() {
|
|
return listOfActiveFormattingElements;
|
|
}
|
|
|
|
jArray<nsHtml5StackNode*, int32_t> nsHtml5TreeBuilder::getStack() {
|
|
return stack;
|
|
}
|
|
|
|
jArray<int32_t, int32_t> 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"
|