mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-29 11:57:32 +09:00
Issue #3057 - Implement new <select> HTML parser rules
This commit is contained in:
parent
2415b3eaaf
commit
952767296d
5 changed files with 131 additions and 36 deletions
|
|
@ -2162,7 +2162,7 @@ public final class ElementName
|
|||
public static final ElementName SELECT = new ElementName("select", "select",
|
||||
// CPPONLY: NS_NewHTMLSelectElement,
|
||||
// CPPONLY: NS_NewSVGUnknownElement,
|
||||
TreeBuilder.SELECT | SPECIAL);
|
||||
TreeBuilder.SELECT | SPECIAL | SCOPING);
|
||||
public static final ElementName SCALARPRODUCT = new ElementName("scalarproduct", "scalarproduct",
|
||||
// CPPONLY: NS_NewHTMLUnknownElement,
|
||||
// CPPONLY: NS_NewSVGUnknownElement,
|
||||
|
|
|
|||
|
|
@ -866,7 +866,7 @@ nsHtml5ElementName::initializeStatics()
|
|||
ELT_RECT = new nsHtml5ElementName(nsHtml5Atoms::rect, nsHtml5Atoms::rect, NS_NewHTMLUnknownElement, NS_NewSVGRectElement, nsHtml5TreeBuilder::OTHER);
|
||||
ELT_RADIALGRADIENT = new nsHtml5ElementName(nsHtml5Atoms::radialgradient, nsHtml5Atoms::radialGradient, NS_NewHTMLUnknownElement, NS_NewSVGRadialGradientElement, nsHtml5TreeBuilder::OTHER);
|
||||
ELT_ROOT = new nsHtml5ElementName(nsHtml5Atoms::root, nsHtml5Atoms::root, NS_NewHTMLUnknownElement, NS_NewSVGUnknownElement, nsHtml5TreeBuilder::OTHER);
|
||||
ELT_SELECT = new nsHtml5ElementName(nsHtml5Atoms::select, nsHtml5Atoms::select, NS_NewHTMLSelectElement, NS_NewSVGUnknownElement, nsHtml5TreeBuilder::SELECT | SPECIAL);
|
||||
ELT_SELECT = new nsHtml5ElementName(nsHtml5Atoms::select, nsHtml5Atoms::select, NS_NewHTMLSelectElement, NS_NewSVGUnknownElement, nsHtml5TreeBuilder::SELECT | SPECIAL | SCOPING);
|
||||
ELT_SCALARPRODUCT = new nsHtml5ElementName(nsHtml5Atoms::scalarproduct, nsHtml5Atoms::scalarproduct, NS_NewHTMLUnknownElement, NS_NewSVGUnknownElement, nsHtml5TreeBuilder::OTHER);
|
||||
ELT_SUBSET = new nsHtml5ElementName(nsHtml5Atoms::subset, nsHtml5Atoms::subset, NS_NewHTMLUnknownElement, NS_NewSVGUnknownElement, nsHtml5TreeBuilder::OTHER);
|
||||
ELT_SLOT = new nsHtml5ElementName(nsHtml5Atoms::slot, nsHtml5Atoms::slot, NS_NewHTMLSlotElement, NS_NewSVGUnknownElement, nsHtml5TreeBuilder::OTHER);
|
||||
|
|
|
|||
|
|
@ -1216,6 +1216,14 @@ nsHtml5TreeBuilder::startTag(nsHtml5ElementName* elementName, nsHtml5HtmlAttribu
|
|||
}
|
||||
case HR: {
|
||||
implicitlyCloseP();
|
||||
if (findLastInScope(nsHtml5Atoms::select) != nsHtml5TreeBuilder::NOT_FOUND_ON_STACK) {
|
||||
generateImpliedEndTags();
|
||||
if (!!MOZ_UNLIKELY(mViewSource) &&
|
||||
(findLastInScope(nsHtml5Atoms::option) != nsHtml5TreeBuilder::NOT_FOUND_ON_STACK ||
|
||||
findLastInScope(nsHtml5Atoms::optgroup) != nsHtml5TreeBuilder::NOT_FOUND_ON_STACK)) {
|
||||
errUnclosedElements(currentPtr, name);
|
||||
}
|
||||
}
|
||||
appendVoidElementToCurrentMayFoster(elementName, attributes);
|
||||
selfClosing = false;
|
||||
attributes = nullptr;
|
||||
|
|
@ -1229,6 +1237,19 @@ nsHtml5TreeBuilder::startTag(nsHtml5ElementName* elementName, nsHtml5HtmlAttribu
|
|||
case IMG:
|
||||
case KEYGEN:
|
||||
case INPUT: {
|
||||
if (fragment && nsHtml5Atoms::select == contextName) {
|
||||
errStartTagWithSelectOpen(name);
|
||||
NS_HTML5_BREAK(starttagloop);
|
||||
}
|
||||
eltPos = findLastInScope(nsHtml5Atoms::select);
|
||||
if (eltPos != nsHtml5TreeBuilder::NOT_FOUND_ON_STACK) {
|
||||
errStartTagWithSelectOpen(name);
|
||||
while (currentPtr >= eltPos) {
|
||||
pop();
|
||||
}
|
||||
resetTheInsertionMode();
|
||||
NS_HTML5_CONTINUE(starttagloop);
|
||||
}
|
||||
reconstructTheActiveFormattingElements();
|
||||
appendVoidElementToCurrentMayFoster(elementName, attributes, formPointer);
|
||||
selfClosing = false;
|
||||
|
|
@ -1313,33 +1334,54 @@ nsHtml5TreeBuilder::startTag(nsHtml5ElementName* elementName, nsHtml5HtmlAttribu
|
|||
NS_HTML5_BREAK(starttagloop);
|
||||
}
|
||||
case SELECT: {
|
||||
if (fragment && nsHtml5Atoms::select == contextName) {
|
||||
errStartSelectWhereEndSelectExpected();
|
||||
NS_HTML5_BREAK(starttagloop);
|
||||
}
|
||||
eltPos = findLastInScope(nsHtml5Atoms::select);
|
||||
if (eltPos != nsHtml5TreeBuilder::NOT_FOUND_ON_STACK) {
|
||||
errStartSelectWhereEndSelectExpected();
|
||||
while (currentPtr >= eltPos) {
|
||||
pop();
|
||||
}
|
||||
NS_HTML5_BREAK(starttagloop);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
framesetOk = false;
|
||||
attributes = nullptr;
|
||||
NS_HTML5_BREAK(starttagloop);
|
||||
}
|
||||
case OPTGROUP:
|
||||
case OPTION: {
|
||||
if (isCurrent(nsHtml5Atoms::option)) {
|
||||
if (findLastInScope(nsHtml5Atoms::select) != nsHtml5TreeBuilder::NOT_FOUND_ON_STACK) {
|
||||
generateImpliedEndTagsExceptFor(nsHtml5Atoms::optgroup);
|
||||
eltPos = findLastInScope(nsHtml5Atoms::option);
|
||||
if (!!MOZ_UNLIKELY(mViewSource) && eltPos != nsHtml5TreeBuilder::NOT_FOUND_ON_STACK) {
|
||||
errUnclosedElements(eltPos, name);
|
||||
}
|
||||
} else {
|
||||
if (isCurrent(nsHtml5Atoms::option)) {
|
||||
pop();
|
||||
}
|
||||
}
|
||||
reconstructTheActiveFormattingElements();
|
||||
appendToCurrentNodeAndPushElement(elementName, attributes);
|
||||
attributes = nullptr;
|
||||
NS_HTML5_BREAK(starttagloop);
|
||||
}
|
||||
case OPTGROUP: {
|
||||
if (findLastInScope(nsHtml5Atoms::select) != nsHtml5TreeBuilder::NOT_FOUND_ON_STACK) {
|
||||
generateImpliedEndTags();
|
||||
if (!!MOZ_UNLIKELY(mViewSource) &&
|
||||
(findLastInScope(nsHtml5Atoms::option) != nsHtml5TreeBuilder::NOT_FOUND_ON_STACK ||
|
||||
findLastInScope(nsHtml5Atoms::optgroup) != nsHtml5TreeBuilder::NOT_FOUND_ON_STACK)) {
|
||||
errUnclosedElements(currentPtr, name);
|
||||
}
|
||||
} else if (isCurrent(nsHtml5Atoms::option)) {
|
||||
pop();
|
||||
}
|
||||
reconstructTheActiveFormattingElements();
|
||||
appendToCurrentNodeAndPushElementMayFoster(elementName, attributes);
|
||||
appendToCurrentNodeAndPushElement(elementName, attributes);
|
||||
attributes = nullptr;
|
||||
NS_HTML5_BREAK(starttagloop);
|
||||
}
|
||||
|
|
@ -2697,6 +2739,21 @@ nsHtml5TreeBuilder::endTag(nsHtml5ElementName* elementName)
|
|||
case TEMPLATE: {
|
||||
break;
|
||||
}
|
||||
case SELECT: {
|
||||
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 AREA_OR_WBR:
|
||||
#ifdef ENABLE_VOID_MENUITEM
|
||||
case MENUITEM:
|
||||
|
|
@ -2712,7 +2769,6 @@ nsHtml5TreeBuilder::endTag(nsHtml5ElementName* elementName)
|
|||
case IFRAME:
|
||||
case NOEMBED:
|
||||
case NOFRAMES:
|
||||
case SELECT:
|
||||
case TABLE:
|
||||
case TEXTAREA: {
|
||||
errStrayEndTag(name);
|
||||
|
|
@ -3335,23 +3391,7 @@ nsHtml5TreeBuilder::resetTheInsertionMode()
|
|||
return;
|
||||
}
|
||||
}
|
||||
if (nsHtml5Atoms::select == name) {
|
||||
int32_t ancestorIndex = i;
|
||||
while (ancestorIndex > 0) {
|
||||
nsHtml5StackNode* ancestor = stack[ancestorIndex--];
|
||||
if (kNameSpaceID_XHTML == ancestor->ns) {
|
||||
if (nsHtml5Atoms::template_ == ancestor->name) {
|
||||
break;
|
||||
}
|
||||
if (nsHtml5Atoms::table == ancestor->name) {
|
||||
mode = IN_SELECT_IN_TABLE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
mode = IN_SELECT;
|
||||
return;
|
||||
} else if (nsHtml5Atoms::td == name || nsHtml5Atoms::th == name) {
|
||||
if (nsHtml5Atoms::td == name || nsHtml5Atoms::th == name) {
|
||||
mode = IN_CELL;
|
||||
return;
|
||||
} else if (nsHtml5Atoms::tr == name) {
|
||||
|
|
|
|||
|
|
@ -147,3 +147,4 @@ skip-if = toolkit == 'android' #TIMED_OUT
|
|||
# Disable test due to frequent orange on Mac
|
||||
# test_bug534293.html
|
||||
[test_bug1209658.html]
|
||||
[test_select_parser_relaxed.html]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1933591
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for relaxed select parser rules</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function parse(markup) {
|
||||
const host = document.createElement("div");
|
||||
host.innerHTML = markup;
|
||||
return host;
|
||||
}
|
||||
|
||||
(function testDivAllowedInSelect() {
|
||||
const host = parse("<select><div><option>One</option></div></select>");
|
||||
const select = host.querySelector("select");
|
||||
ok(!!select, "select should parse");
|
||||
is(select.firstElementChild && select.firstElementChild.tagName, "DIV",
|
||||
"div should remain inside select");
|
||||
is(select.querySelector("option").textContent, "One",
|
||||
"option should still be found under nested content");
|
||||
})();
|
||||
|
||||
(function testTextareaDoesNotCloseSelect() {
|
||||
const host = parse("<select><textarea>abc</textarea><option>One</option></select>");
|
||||
const select = host.querySelector("select");
|
||||
ok(!!select, "select should parse");
|
||||
ok(!!select.querySelector("textarea"), "textarea should remain inside select");
|
||||
ok(!!select.querySelector("option"), "option should remain inside select");
|
||||
})();
|
||||
|
||||
(function testInputStillClosesSelect() {
|
||||
const host = parse("<select><div><input></div><option>One</option></select>");
|
||||
const select = host.querySelector("select");
|
||||
ok(!!select, "select should parse");
|
||||
ok(!select.querySelector("input"), "input should not remain inside select");
|
||||
ok(!select.querySelector("option"), "option should no longer be inside the closed select");
|
||||
ok(!!host.querySelector("input"), "input should still be inserted in the tree");
|
||||
})();
|
||||
|
||||
SimpleTest.finish();
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Add table
Add a link
Reference in a new issue