Skip to content

Commit 9120820

Browse files
committed
Relax selector parsing
Neither the proposal nor the test changes are merged yet. Also, the tests are still failing. html5lib/html5lib-tests#178 whatwg/html#10557
1 parent 8ca1bc6 commit 9120820

File tree

6 files changed

+47
-150
lines changed

6 files changed

+47
-150
lines changed

html5ever/src/tree_builder/mod.rs

+1-10
Original file line numberDiff line numberDiff line change
@@ -1146,6 +1146,7 @@ where
11461146
n
11471147
}
11481148

1149+
/// Pop element until an element with the given name has been popped.
11491150
fn pop_until_named(&self, name: LocalName) -> usize {
11501151
self.pop_until(|p| *p.ns == ns!(html) && *p.local == name)
11511152
}
@@ -1231,16 +1232,6 @@ where
12311232
_ => continue,
12321233
};
12331234
match *name {
1234-
local_name!("select") => {
1235-
for ancestor in self.open_elems.borrow()[0..i].iter().rev() {
1236-
if self.html_elem_named(ancestor, local_name!("template")) {
1237-
return InSelect;
1238-
} else if self.html_elem_named(ancestor, local_name!("table")) {
1239-
return InSelectInTable;
1240-
}
1241-
}
1242-
return InSelect;
1243-
},
12441235
local_name!("td") | local_name!("th") => {
12451236
if !last {
12461237
return InCell;

html5ever/src/tree_builder/rules.rs

+40-126
Original file line numberDiff line numberDiff line change
@@ -624,6 +624,13 @@ where
624624

625625
tag @ <hr> => {
626626
self.close_p_element_in_button_scope();
627+
if self.in_scope_named(default_scope, local_name!("select")) {
628+
self.generate_implied_end_except(local_name!("optgroup"));
629+
if self.in_scope_named(default_scope, local_name!("option")) {
630+
self.sink.parse_error(Borrowed("hr in option"));
631+
}
632+
}
633+
627634
self.insert_and_pop_element_for(tag);
628635
self.frameset_ok.set(false);
629636
DoneAckSelfClosing
@@ -662,28 +669,50 @@ where
662669
// <noscript> handled in wildcard case below
663670

664671
tag @ <select> => {
672+
if self.in_scope_named(default_scope, local_name!("select")) {
673+
self.sink.parse_error(Borrowed("nested select"));
674+
self.pop_until_named(local_name!("select"));
675+
}
676+
665677
self.reconstruct_formatting();
666678
self.insert_element_for(tag);
667679
self.frameset_ok.set(false);
668-
// NB: mode == InBody but possibly self.mode != mode, if
669-
// we're processing "as in the rules for InBody".
670-
self.mode.set(match self.mode.get() {
671-
InTable | InCaption | InTableBody
672-
| InRow | InCell => InSelectInTable,
673-
_ => InSelect,
674-
});
675680
Done
676681
}
677682

678-
tag @ <optgroup> <option> => {
679-
if self.current_node_named(local_name!("option")) {
680-
self.pop();
683+
tag @ <option> => {
684+
if self.in_scope_named(default_scope, local_name!("select")) {
685+
self.generate_implied_end_except(local_name!("optgroup"));
686+
if self.in_scope_named(default_scope, local_name!("option")) {
687+
self.sink.parse_error(Borrowed("nested options"));
688+
}
689+
} else {
690+
if self.current_node_named(local_name!("option")) {
691+
self.pop();
692+
}
693+
self.reconstruct_formatting();
681694
}
682-
self.reconstruct_formatting();
695+
683696
self.insert_element_for(tag);
684697
Done
685698
}
686699

700+
tag @ <optgroup> => {
701+
if self.in_scope_named(default_scope, local_name!("select")) {
702+
self.generate_implied_end(cursory_implied_end);
703+
// XXX: perf
704+
if self.in_scope_named(default_scope, local_name!("option")) || self.in_scope_named(default_scope, local_name!("optgroup")) {
705+
self.sink.parse_error(Borrowed("nested options"));
706+
}
707+
} else {
708+
if self.current_node_named(local_name!("option")) {
709+
self.pop();
710+
}
711+
self.reconstruct_formatting();
712+
}
713+
Done
714+
}
715+
687716
tag @ <rb> <rtc> => {
688717
if self.in_scope_named(default_scope, local_name!("ruby")) {
689718
self.generate_implied_end(cursory_implied_end);
@@ -1100,121 +1129,6 @@ where
11001129
token => self.step(InBody, token),
11011130
}),
11021131

1103-
//§ parsing-main-inselect
1104-
InSelect => match_token!(token {
1105-
NullCharacterToken => self.unexpected(&token),
1106-
CharacterTokens(_, text) => self.append_text(text),
1107-
CommentToken(text) => self.append_comment(text),
1108-
1109-
<html> => self.step(InBody, token),
1110-
1111-
tag @ <option> => {
1112-
if self.current_node_named(local_name!("option")) {
1113-
self.pop();
1114-
}
1115-
self.insert_element_for(tag);
1116-
Done
1117-
}
1118-
1119-
tag @ <optgroup> => {
1120-
if self.current_node_named(local_name!("option")) {
1121-
self.pop();
1122-
}
1123-
if self.current_node_named(local_name!("optgroup")) {
1124-
self.pop();
1125-
}
1126-
self.insert_element_for(tag);
1127-
Done
1128-
}
1129-
1130-
tag @ <hr> => {
1131-
if self.current_node_named(local_name!("option")) {
1132-
self.pop();
1133-
}
1134-
if self.current_node_named(local_name!("optgroup")) {
1135-
self.pop();
1136-
}
1137-
self.insert_element_for(tag);
1138-
self.pop();
1139-
DoneAckSelfClosing
1140-
}
1141-
1142-
</optgroup> => {
1143-
if self.open_elems.borrow().len() >= 2
1144-
&& self.current_node_named(local_name!("option"))
1145-
&& self.html_elem_named(&self.open_elems.borrow()[self.open_elems.borrow().len() - 2],
1146-
local_name!("optgroup")) {
1147-
self.pop();
1148-
}
1149-
if self.current_node_named(local_name!("optgroup")) {
1150-
self.pop();
1151-
} else {
1152-
self.unexpected(&token);
1153-
}
1154-
Done
1155-
}
1156-
1157-
</option> => {
1158-
if self.current_node_named(local_name!("option")) {
1159-
self.pop();
1160-
} else {
1161-
self.unexpected(&token);
1162-
}
1163-
Done
1164-
}
1165-
1166-
tag @ <select> </select> => {
1167-
let in_scope = self.in_scope_named(select_scope, local_name!("select"));
1168-
1169-
if !in_scope || tag.kind == StartTag {
1170-
self.unexpected(&tag);
1171-
}
1172-
1173-
if in_scope {
1174-
self.pop_until_named(local_name!("select"));
1175-
self.mode.set(self.reset_insertion_mode());
1176-
}
1177-
Done
1178-
}
1179-
1180-
<input> <keygen> <textarea> => {
1181-
self.unexpected(&token);
1182-
if self.in_scope_named(select_scope, local_name!("select")) {
1183-
self.pop_until_named(local_name!("select"));
1184-
Reprocess(self.reset_insertion_mode(), token)
1185-
} else {
1186-
Done
1187-
}
1188-
}
1189-
1190-
<script> <template> </template> => self.step(InHead, token),
1191-
1192-
EOFToken => self.step(InBody, token),
1193-
1194-
token => self.unexpected(&token),
1195-
}),
1196-
1197-
//§ parsing-main-inselectintable
1198-
InSelectInTable => match_token!(token {
1199-
<caption> <table> <tbody> <tfoot> <thead> <tr> <td> <th> => {
1200-
self.unexpected(&token);
1201-
self.pop_until_named(local_name!("select"));
1202-
Reprocess(self.reset_insertion_mode(), token)
1203-
}
1204-
1205-
tag @ </caption> </table> </tbody> </tfoot> </thead> </tr> </td> </th> => {
1206-
self.unexpected(&tag);
1207-
if self.in_scope_named(table_scope, tag.name.clone()) {
1208-
self.pop_until_named(local_name!("select"));
1209-
Reprocess(self.reset_insertion_mode(), TagToken(tag))
1210-
} else {
1211-
Done
1212-
}
1213-
}
1214-
1215-
token => self.step(InSelect, token),
1216-
}),
1217-
12181132
//§ parsing-main-intemplate
12191133
InTemplate => match_token!(token {
12201134
CharacterTokens(_, _) => self.step(InBody, token),

html5ever/src/tree_builder/tag_sets.rs

+1-6
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,9 @@ macro_rules! declare_tag_set (
4848
pub(crate) fn empty_set(_: ExpandedName) -> bool {
4949
false
5050
}
51-
#[inline(always)]
52-
pub(crate) fn full_set(_: ExpandedName) -> bool {
53-
true
54-
}
5551

5652
declare_tag_set!(pub html_default_scope =
57-
"applet" "caption" "html" "table" "td" "th" "marquee" "object" "template");
53+
"applet" "caption" "html" "table" "td" "th" "marquee" "object" "select" "template");
5854

5955
#[inline(always)]
6056
pub(crate) fn default_scope(name: ExpandedName) -> bool {
@@ -66,7 +62,6 @@ pub(crate) fn default_scope(name: ExpandedName) -> bool {
6662
declare_tag_set!(pub list_item_scope = [default_scope] + "ol" "ul");
6763
declare_tag_set!(pub button_scope = [default_scope] + "button");
6864
declare_tag_set!(pub table_scope = "html" "table" "template");
69-
declare_tag_set!(pub select_scope = [full_set] - "optgroup" "option");
7065

7166
declare_tag_set!(pub table_body_context = "tbody" "tfoot" "thead" "template" "html");
7267
declare_tag_set!(pub table_row_context = "tr" "template" "html");

html5ever/src/tree_builder/types.rs

-2
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,6 @@ pub(crate) enum InsertionMode {
3838
InTableBody,
3939
InRow,
4040
InCell,
41-
InSelect,
42-
InSelectInTable,
4341
InTemplate,
4442
AfterBody,
4543
InFrameset,

rcdom/tests/html-tree-builder.rs

+4-5
Original file line numberDiff line numberDiff line change
@@ -189,14 +189,13 @@ fn make_test_desc_with_scripting_flag(
189189
fields: &HashMap<String, String>,
190190
scripting_enabled: bool,
191191
) -> Test {
192-
let get_field = |key| {
193-
let field = fields.get(key).expect("missing field");
194-
field.trim_end_matches('\n').to_string()
192+
let expect_field = |key| {
193+
fields.get(key).unwrap_or_else(|| panic!("missing field {}, testcase: {:?}", key, fields)).to_string()
195194
};
196195

197-
let mut data = fields.get("data").expect("missing data").to_string();
196+
let mut data = expect_field("data");
198197
data.pop();
199-
let expected = get_field("document");
198+
let expected = expect_field("document").trim_end_matches('\n').to_string();
200199
let context = fields
201200
.get("document-fragment")
202201
.map(|field| context_name(field.trim_end_matches('\n')));

0 commit comments

Comments
 (0)