in parser/html/javasrc/TreeBuilder.java [3201:3981]
public final void endTag(ElementName elementName) throws SAXException {
flushCharacters();
needToDropLF = false;
int eltPos;
int group = elementName.getGroup();
@Local String name = elementName.getName();
endtagloop: for (;;) {
if (isInForeign()) {
if (stack[currentPtr].name != name) {
if (currentPtr == 0) {
errStrayEndTag(name);
} else {
errEndTagDidNotMatchCurrentOpenElement(name, stack[currentPtr].popName);
}
}
eltPos = currentPtr;
int origPos = currentPtr;
for (;;) {
if (eltPos == 0) {
assert fragment: "We can get this close to the root of the stack in foreign content only in the fragment case.";
break endtagloop;
}
if (stack[eltPos].name == name) {
while (currentPtr >= eltPos) {
popForeign(origPos, eltPos);
}
break endtagloop;
}
if (stack[--eltPos].ns == "http://www.w3.org/1999/xhtml") {
break;
}
}
}
switch (mode) {
case IN_TEMPLATE:
switch (group) {
case TEMPLATE:
// fall through to IN_HEAD
break;
default:
errStrayEndTag(name);
break endtagloop;
}
// CPPONLY: MOZ_FALLTHROUGH;
case IN_ROW:
switch (group) {
case TR:
eltPos = findLastOrRoot(TreeBuilder.TR);
if (eltPos == 0) {
assert fragment || isTemplateContents();
errNoTableRowToClose();
break endtagloop;
}
clearStackBackTo(eltPos);
pop();
mode = IN_TABLE_BODY;
break endtagloop;
case TABLE:
eltPos = findLastOrRoot(TreeBuilder.TR);
if (eltPos == 0) {
assert fragment || isTemplateContents();
errNoTableRowToClose();
break endtagloop;
}
clearStackBackTo(eltPos);
pop();
mode = IN_TABLE_BODY;
continue;
case TBODY_OR_THEAD_OR_TFOOT:
if (findLastInTableScope(name) == TreeBuilder.NOT_FOUND_ON_STACK) {
errStrayEndTag(name);
break endtagloop;
}
eltPos = findLastOrRoot(TreeBuilder.TR);
if (eltPos == 0) {
assert fragment || isTemplateContents();
errNoTableRowToClose();
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);
break endtagloop;
default:
// fall through to IN_TABLE
}
// CPPONLY: MOZ_FALLTHROUGH;
case IN_TABLE_BODY:
switch (group) {
case TBODY_OR_THEAD_OR_TFOOT:
eltPos = findLastOrRoot(name);
if (eltPos == 0) {
errStrayEndTag(name);
break endtagloop;
}
clearStackBackTo(eltPos);
pop();
mode = IN_TABLE;
break endtagloop;
case TABLE:
eltPos = findLastInTableScopeOrRootTemplateTbodyTheadTfoot();
if (eltPos == 0 || stack[eltPos].getGroup() == TEMPLATE) {
assert fragment || isTemplateContents();
errStrayEndTag(name);
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);
break endtagloop;
default:
// fall through to IN_TABLE
}
// CPPONLY: MOZ_FALLTHROUGH;
case IN_TABLE:
switch (group) {
case TABLE:
eltPos = findLast("table");
if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
assert fragment || isTemplateContents();
errStrayEndTag(name);
break endtagloop;
}
while (currentPtr >= eltPos) {
pop();
}
resetTheInsertionMode();
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);
break endtagloop;
case TEMPLATE:
// fall through to IN_HEAD
break;
default:
errStrayEndTag(name);
// fall through to IN_BODY
}
// CPPONLY: MOZ_FALLTHROUGH;
case IN_CAPTION:
switch (group) {
case CAPTION:
eltPos = findLastInTableScope("caption");
if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
break endtagloop;
}
generateImpliedEndTags();
if (errorHandler != null && currentPtr != eltPos) {
errUnclosedElements(eltPos, name);
}
while (currentPtr >= eltPos) {
pop();
}
clearTheListOfActiveFormattingElementsUpToTheLastMarker();
mode = IN_TABLE;
break endtagloop;
case TABLE:
eltPos = findLastInTableScope("caption");
if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
assert fragment || isTemplateContents();
errStrayEndTag(name);
break endtagloop;
}
generateImpliedEndTags();
if (errorHandler != null && 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);
break endtagloop;
default:
// fall through to IN_BODY
}
// CPPONLY: MOZ_FALLTHROUGH;
case IN_CELL:
switch (group) {
case TD_OR_TH:
eltPos = findLastInTableScope(name);
if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
errStrayEndTag(name);
break endtagloop;
}
generateImpliedEndTags();
if (errorHandler != null && !isCurrent(name)) {
errUnclosedElements(eltPos, name);
}
while (currentPtr >= eltPos) {
pop();
}
clearTheListOfActiveFormattingElementsUpToTheLastMarker();
mode = IN_ROW;
break endtagloop;
case TABLE:
case TBODY_OR_THEAD_OR_TFOOT:
case TR:
if (findLastInTableScope(name) == TreeBuilder.NOT_FOUND_ON_STACK) {
assert name == "tbody" || name == "tfoot" || name == "thead" || fragment || isTemplateContents();
errStrayEndTag(name);
break endtagloop;
}
closeTheCell(findLastInTableScopeTdTh());
continue;
case BODY:
case CAPTION:
case COL:
case COLGROUP:
case HTML:
errStrayEndTag(name);
break endtagloop;
default:
// fall through to IN_BODY
}
// CPPONLY: MOZ_FALLTHROUGH;
case FRAMESET_OK:
case IN_BODY:
switch (group) {
case BODY:
if (!isSecondOnStackBody()) {
assert fragment || isTemplateContents();
errStrayEndTag(name);
break endtagloop;
}
assert currentPtr >= 1;
if (errorHandler != null) {
uncloseloop1: for (int i = 2; i <= currentPtr; i++) {
switch (stack[i].getGroup()) {
case DD_OR_DT:
case LI:
case OPTGROUP:
case OPTION: // is this possible?
case P:
case RB_OR_RTC:
case RT_OR_RP:
case TD_OR_TH:
case TBODY_OR_THEAD_OR_TFOOT:
break;
default:
errEndWithUnclosedElements(name);
break uncloseloop1;
}
}
}
mode = AFTER_BODY;
break endtagloop;
case HTML:
if (!isSecondOnStackBody()) {
assert fragment || isTemplateContents();
errStrayEndTag(name);
break endtagloop;
}
if (errorHandler != null) {
uncloseloop2: for (int 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);
break uncloseloop2;
}
}
}
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 == TreeBuilder.NOT_FOUND_ON_STACK) {
errStrayEndTag(name);
} else {
generateImpliedEndTags();
if (errorHandler != null && !isCurrent(name)) {
errUnclosedElements(eltPos, name);
}
while (currentPtr >= eltPos) {
pop();
}
}
break endtagloop;
case FORM:
if (!isTemplateContents()) {
if (formPointer == null) {
errStrayEndTag(name);
break endtagloop;
}
formPointer = null;
eltPos = findLastInScope(name);
if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
errStrayEndTag(name);
break endtagloop;
}
generateImpliedEndTags();
if (errorHandler != null && !isCurrent(name)) {
errUnclosedElements(eltPos, name);
}
removeFromStack(eltPos);
break endtagloop;
} else {
eltPos = findLastInScope(name);
if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
errStrayEndTag(name);
break endtagloop;
}
generateImpliedEndTags();
if (errorHandler != null && !isCurrent(name)) {
errUnclosedElements(eltPos, name);
}
while (currentPtr >= eltPos) {
pop();
}
break endtagloop;
}
case P:
eltPos = findLastInButtonScope("p");
if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
errNoElementToCloseButEndTagSeen("p");
// XXX Can the 'in foreign' case happen anymore?
if (isInForeign()) {
errHtmlStartTagInForeignContext(name);
// Check for currentPtr for the fragment
// case.
while (currentPtr >= 0 && stack[currentPtr].ns != "http://www.w3.org/1999/xhtml") {
pop();
}
}
appendVoidElementToCurrentMayFoster(
elementName,
HtmlAttributes.EMPTY_ATTRIBUTES);
break endtagloop;
}
generateImpliedEndTagsExceptFor("p");
assert eltPos != TreeBuilder.NOT_FOUND_ON_STACK;
if (errorHandler != null && eltPos != currentPtr) {
errUnclosedElements(eltPos, name);
}
while (currentPtr >= eltPos) {
pop();
}
break endtagloop;
case LI:
eltPos = findLastInListScope(name);
if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
errNoElementToCloseButEndTagSeen(name);
} else {
generateImpliedEndTagsExceptFor(name);
if (errorHandler != null
&& eltPos != currentPtr) {
errUnclosedElements(eltPos, name);
}
while (currentPtr >= eltPos) {
pop();
}
}
break endtagloop;
case DD_OR_DT:
eltPos = findLastInScope(name);
if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
errNoElementToCloseButEndTagSeen(name);
} else {
generateImpliedEndTagsExceptFor(name);
if (errorHandler != null
&& eltPos != currentPtr) {
errUnclosedElements(eltPos, name);
}
while (currentPtr >= eltPos) {
pop();
}
}
break endtagloop;
case H1_OR_H2_OR_H3_OR_H4_OR_H5_OR_H6:
eltPos = findLastInScopeHn();
if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
errStrayEndTag(name);
} else {
generateImpliedEndTags();
if (errorHandler != null && !isCurrent(name)) {
errUnclosedElements(eltPos, name);
}
while (currentPtr >= eltPos) {
pop();
}
}
break endtagloop;
case OBJECT:
case MARQUEE_OR_APPLET:
eltPos = findLastInScope(name);
if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
errStrayEndTag(name);
} else {
generateImpliedEndTags();
if (errorHandler != null && !isCurrent(name)) {
errUnclosedElements(eltPos, name);
}
while (currentPtr >= eltPos) {
pop();
}
clearTheListOfActiveFormattingElementsUpToTheLastMarker();
}
break endtagloop;
case BR:
errEndTagBr();
if (isInForeign()) {
// XXX can this happen anymore?
errHtmlStartTagInForeignContext(name);
// Check for currentPtr for the fragment
// case.
while (currentPtr >= 0 && stack[currentPtr].ns != "http://www.w3.org/1999/xhtml") {
pop();
}
}
reconstructTheActiveFormattingElements();
appendVoidElementToCurrentMayFoster(
elementName,
HtmlAttributes.EMPTY_ATTRIBUTES);
break endtagloop;
case TEMPLATE:
// fall through to IN_HEAD;
break;
case AREA_OR_WBR:
case KEYGEN: // XXX??
case PARAM_OR_SOURCE_OR_TRACK:
case EMBED:
case IMG:
case IMAGE:
case INPUT:
case HR:
case IFRAME:
case NOEMBED: // XXX???
case NOFRAMES: // XXX??
case SELECT:
case TABLE:
case TEXTAREA: // XXX??
errStrayEndTag(name);
break endtagloop;
case NOSCRIPT:
if (scriptingEnabled) {
errStrayEndTag(name);
break endtagloop;
}
// CPPONLY: MOZ_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)) {
break endtagloop;
}
// else handle like any other tag
// CPPONLY: MOZ_FALLTHROUGH;
default:
if (isCurrent(name)) {
pop();
break endtagloop;
}
eltPos = currentPtr;
for (;;) {
StackNode<T> node = stack[eltPos];
if (node.ns == "http://www.w3.org/1999/xhtml" && node.name == name) {
generateImpliedEndTags();
if (errorHandler != null
&& !isCurrent(name)) {
errUnclosedElements(eltPos, name);
}
while (currentPtr >= eltPos) {
pop();
}
break endtagloop;
} else if (eltPos == 0 || node.isSpecial()) {
errStrayEndTag(name);
break endtagloop;
}
eltPos--;
}
}
// CPPONLY: MOZ_FALLTHROUGH;
case IN_HEAD:
switch (group) {
case HEAD:
pop();
mode = AFTER_HEAD;
break endtagloop;
case BR:
case HTML:
case BODY:
pop();
mode = AFTER_HEAD;
continue;
case TEMPLATE:
endTagTemplateInHead();
break endtagloop;
default:
errStrayEndTag(name);
break endtagloop;
}
case IN_HEAD_NOSCRIPT:
switch (group) {
case NOSCRIPT:
pop();
mode = IN_HEAD;
break endtagloop;
case BR:
errStrayEndTag(name);
pop();
mode = IN_HEAD;
continue;
default:
errStrayEndTag(name);
break endtagloop;
}
case IN_COLUMN_GROUP:
switch (group) {
case COLGROUP:
if (currentPtr == 0 || stack[currentPtr].getGroup() ==
TreeBuilder.TEMPLATE) {
assert fragment || isTemplateContents();
errGarbageInColgroup();
break endtagloop;
}
pop();
mode = IN_TABLE;
break endtagloop;
case COL:
errStrayEndTag(name);
break endtagloop;
case TEMPLATE:
endTagTemplateInHead();
break endtagloop;
default:
if (currentPtr == 0 || stack[currentPtr].getGroup() ==
TreeBuilder.TEMPLATE) {
assert fragment || isTemplateContents();
errGarbageInColgroup();
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) != TreeBuilder.NOT_FOUND_ON_STACK) {
eltPos = findLastInTableScope("select");
if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
assert fragment;
break endtagloop; // http://www.w3.org/Bugs/Public/show_bug.cgi?id=8375
}
while (currentPtr >= eltPos) {
pop();
}
resetTheInsertionMode();
continue;
} else {
break endtagloop;
}
default:
// fall through to IN_SELECT
}
// CPPONLY: MOZ_FALLTHROUGH;
case IN_SELECT:
switch (group) {
case OPTION:
if (isCurrent("option")) {
pop();
break endtagloop;
} else {
errStrayEndTag(name);
break endtagloop;
}
case OPTGROUP:
if (isCurrent("option")
&& "optgroup" == stack[currentPtr - 1].name) {
pop();
}
if (isCurrent("optgroup")) {
pop();
} else {
errStrayEndTag(name);
}
break endtagloop;
case SELECT:
eltPos = findLastInTableScope("select");
if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
assert fragment;
errStrayEndTag(name);
break endtagloop;
}
while (currentPtr >= eltPos) {
pop();
}
resetTheInsertionMode();
break endtagloop;
case TEMPLATE:
endTagTemplateInHead();
break endtagloop;
default:
errStrayEndTag(name);
break endtagloop;
}
case AFTER_BODY:
switch (group) {
case HTML:
if (fragment) {
errStrayEndTag(name);
break endtagloop;
} else {
mode = AFTER_AFTER_BODY;
break endtagloop;
}
default:
errEndTagAfterBody();
mode = framesetOk ? FRAMESET_OK : IN_BODY;
continue;
}
case IN_FRAMESET:
switch (group) {
case FRAMESET:
if (currentPtr == 0) {
assert fragment;
errStrayEndTag(name);
break endtagloop;
}
pop();
if ((!fragment) && !isCurrent("frameset")) {
mode = AFTER_FRAMESET;
}
break endtagloop;
default:
errStrayEndTag(name);
break endtagloop;
}
case AFTER_FRAMESET:
switch (group) {
case HTML:
mode = AFTER_AFTER_FRAMESET;
break endtagloop;
default:
errStrayEndTag(name);
break endtagloop;
}
case INITIAL:
/*
* Parse error.
*/
errEndTagSeenWithoutDoctype();
/*
*
* Set the document to quirks mode.
*/
documentModeInternal(DocumentMode.QUIRKS_MODE, null, null);
/*
* Then, switch to the root element mode of the tree
* construction stage
*/
mode = BEFORE_HTML;
/*
* and reprocess the current token.
*/
continue;
case BEFORE_HTML:
switch (group) {
case HEAD:
case BR:
case HTML:
case BODY:
/*
* Create an HTMLElement node with the tag name
* html, in the HTML namespace. Append it to the
* Document object.
*/
appendHtmlElementToDocumentAndPush();
/* Switch to the main mode */
mode = BEFORE_HEAD;
/*
* reprocess the current token.
*/
continue;
default:
errStrayEndTag(name);
break endtagloop;
}
case BEFORE_HEAD:
switch (group) {
case HEAD:
case BR:
case HTML:
case BODY:
appendToCurrentNodeAndPushHeadElement(HtmlAttributes.EMPTY_ATTRIBUTES);
mode = IN_HEAD;
continue;
default:
errStrayEndTag(name);
break endtagloop;
}
case AFTER_HEAD:
switch (group) {
case TEMPLATE:
endTagTemplateInHead();
break endtagloop;
case HTML:
case BODY:
case BR:
appendToCurrentNodeAndPushBodyElement();
mode = FRAMESET_OK;
continue;
default:
errStrayEndTag(name);
break endtagloop;
}
case AFTER_AFTER_BODY:
errStrayEndTag(name);
mode = framesetOk ? FRAMESET_OK : IN_BODY;
continue;
case AFTER_AFTER_FRAMESET:
errStrayEndTag(name);
break endtagloop;
case TEXT:
// XXX need to manage insertion point here
pop();
if (originalMode == AFTER_HEAD) {
silentPop();
}
mode = originalMode;
break endtagloop;
}
} // endtagloop
}