in parser/html/nsHtml5TreeBuilder.cpp [2362:3199]
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_SEARCH_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:
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:;
}