Skip to content

Commit d0240a8

Browse files
authored
Relax <select> parser (WHATWG proposal) (#560)
* 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 * fmt * whoops * bump html5lib-tests again * renamed methods * fixup * fix select state * fmt * remove xxx * fix input case * wip * select * select * fmt * clippy * ignore failing selectedcontent tests * lint fix
1 parent 795caf4 commit d0240a8

File tree

14 files changed

+221
-176
lines changed

14 files changed

+221
-176
lines changed

html5ever/examples/arena.rs

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ pub struct Node<'arena> {
5555
}
5656

5757
/// HTML node data which can be an element, a comment, a string, a DOCTYPE, etc...
58+
#[derive(Clone)]
5859
pub enum NodeData<'arena> {
5960
Document,
6061
Doctype {
@@ -338,6 +339,21 @@ impl<'arena> TreeSink for Sink<'arena> {
338339
new_parent.append(child)
339340
}
340341
}
342+
343+
fn clone_subtree(&self, node: &Self::Handle) -> Self::Handle {
344+
// Allocate the new node in the arena using Clone
345+
let cloned_node = self.arena.alloc(Node::new(node.data.clone()));
346+
347+
// Clone all children and append them
348+
let mut child = node.first_child.get();
349+
while let Some(current_child) = child {
350+
let cloned_child = self.clone_subtree(&current_child);
351+
cloned_node.append(cloned_child);
352+
child = current_child.next_sibling.get();
353+
}
354+
355+
cloned_node
356+
}
341357
}
342358

343359
/// In this example an "arena" is created and filled with the DOM nodes.
@@ -352,5 +368,33 @@ fn main() {
352368
io::stdin().read_to_end(&mut bytes).unwrap();
353369

354370
let arena = typed_arena::Arena::new();
355-
html5ever_parse_slice_into_arena(&bytes, &arena);
371+
let dom = html5ever_parse_slice_into_arena(&bytes, &arena);
372+
373+
// Print the DOM structure
374+
print_node(dom, 0);
375+
}
376+
377+
fn print_node<'arena>(node: &Node<'arena>, depth: usize) {
378+
let indent = " ".repeat(depth);
379+
380+
match &node.data {
381+
NodeData::Document => println!("{}Document", indent),
382+
NodeData::Doctype { name, .. } => println!("{}<!DOCTYPE {}>", indent, name),
383+
NodeData::Text { contents } => {
384+
let text = contents.borrow();
385+
if !text.trim().is_empty() {
386+
println!("{}\"{}\"", indent, text.trim());
387+
}
388+
},
389+
NodeData::Comment { contents } => println!("{}<!-- {} -->", indent, contents),
390+
NodeData::Element { name, .. } => println!("{}<{}>", indent, name.local),
391+
NodeData::ProcessingInstruction { target, .. } => println!("{}<?{}>", indent, target),
392+
}
393+
394+
// Print all children
395+
let mut child = node.first_child.get();
396+
while let Some(current_child) = child {
397+
print_node(current_child, depth + 1);
398+
child = current_child.next_sibling.get();
399+
}
356400
}

html5ever/examples/noop-tree-builder.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,11 @@ impl TreeSink for Sink {
113113
fn remove_from_parent(&self, _target: &usize) {}
114114
fn reparent_children(&self, _node: &usize, _new_parent: &usize) {}
115115
fn mark_script_already_started(&self, _node: &usize) {}
116+
117+
fn clone_subtree(&self, _node: &Self::Handle) -> Self::Handle {
118+
// For this noop example, just return a new placeholder ID
119+
self.get_id()
120+
}
116121
}
117122

118123
/// In this example we implement the TreeSink trait which takes each parsed elements and insert

html5ever/examples/print-tree-actions.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,12 @@ impl TreeSink for Sink {
167167
fn pop(&self, elem: &usize) {
168168
println!("Popped element {elem}");
169169
}
170+
171+
fn clone_subtree(&self, node: &Self::Handle) -> Self::Handle {
172+
println!("Clone subtree for node {node}");
173+
// For this example, just return a new placeholder ID
174+
self.get_id()
175+
}
170176
}
171177

172178
/// Same example as the "noop-tree-builder", but this time every function implemented in our

html5ever/src/tree_builder/mod.rs

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,9 @@ pub struct TreeBuilder<Handle, Sink> {
118118

119119
/// Form element pointer.
120120
form_elem: RefCell<Option<Handle>>,
121+
122+
/// selectedcontent element pointer.
123+
selectedcontent_elem: RefCell<Option<Handle>>,
121124
//§ END
122125
/// Frameset-ok flag.
123126
frameset_ok: Cell<bool>,
@@ -163,6 +166,7 @@ where
163166
active_formatting: Default::default(),
164167
head_elem: Default::default(),
165168
form_elem: Default::default(),
169+
selectedcontent_elem: Default::default(),
166170
frameset_ok: Cell::new(true),
167171
ignore_lf: Default::default(),
168172
foster_parenting: Default::default(),
@@ -203,6 +207,7 @@ where
203207
active_formatting: Default::default(),
204208
head_elem: Default::default(),
205209
form_elem: RefCell::new(form_elem),
210+
selectedcontent_elem: Default::default(),
206211
frameset_ok: Cell::new(true),
207212
ignore_lf: Default::default(),
208213
foster_parenting: Default::default(),
@@ -285,6 +290,10 @@ where
285290
tracer.trace_handle(form_elem);
286291
}
287292

293+
if let Some(selectedcontent_elem) = self.selectedcontent_elem.borrow().as_ref() {
294+
tracer.trace_handle(selectedcontent_elem);
295+
}
296+
288297
if let Some(context_elem) = self.context_elem.borrow().as_ref() {
289298
tracer.trace_handle(context_elem);
290299
}
@@ -923,6 +932,7 @@ where
923932
.borrow_mut()
924933
.pop()
925934
.expect("no current element");
935+
926936
self.sink.pop(&elem);
927937
elem
928938
}
@@ -1183,6 +1193,7 @@ where
11831193
n
11841194
}
11851195

1196+
/// Pop element until an element with the given name has been popped.
11861197
fn pop_until_named(&self, name: LocalName) -> usize {
11871198
self.pop_until(|p| *p.ns == ns!(html) && *p.local == name)
11881199
}
@@ -1269,16 +1280,6 @@ where
12691280
_ => continue,
12701281
};
12711282
match *name {
1272-
local_name!("select") => {
1273-
for ancestor in self.open_elems.borrow()[0..i].iter().rev() {
1274-
if self.html_elem_named(ancestor, local_name!("template")) {
1275-
return InsertionMode::InSelect;
1276-
} else if self.html_elem_named(ancestor, local_name!("table")) {
1277-
return InsertionMode::InSelectInTable;
1278-
}
1279-
}
1280-
return InsertionMode::InSelect;
1281-
},
12821283
local_name!("td") | local_name!("th") => {
12831284
if !last {
12841285
return InsertionMode::InCell;
@@ -1401,6 +1402,12 @@ where
14011402

14021403
self.insert_at(insertion_point, AppendNode(elem.clone()));
14031404

1405+
if qname.local == local_name!("selectedcontent")
1406+
&& self.selectedcontent_elem.borrow().is_none()
1407+
{
1408+
*self.selectedcontent_elem.borrow_mut() = Some(elem.clone());
1409+
}
1410+
14041411
match push {
14051412
PushFlag::Push => self.push(&elem),
14061413
PushFlag::NoPush => (),
@@ -1585,6 +1592,19 @@ where
15851592
self.remove_from_stack(&node);
15861593
}
15871594

1595+
fn maybe_clone_option_into_selectedcontent(&self, option: &Handle) {
1596+
if let Some(selectedcontent) = self.selectedcontent_elem.borrow().as_ref().cloned() {
1597+
self.clone_option_into_selectedcontent(option, &selectedcontent);
1598+
}
1599+
}
1600+
1601+
fn clone_option_into_selectedcontent(&self, option: &Handle, selectedcontent: &Handle) {
1602+
self.sink
1603+
.reparent_children(selectedcontent, &self.sink.get_document());
1604+
let cloned_option = self.sink.clone_subtree(option);
1605+
self.sink.reparent_children(&cloned_option, selectedcontent);
1606+
}
1607+
15881608
//§ tree-construction
15891609
fn is_foreign(&self, token: &Token) -> bool {
15901610
if let Token::Eof = *token {

0 commit comments

Comments
 (0)