Skip to content
46 changes: 45 additions & 1 deletion html5ever/examples/arena.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ pub struct Node<'arena> {
}

/// HTML node data which can be an element, a comment, a string, a DOCTYPE, etc...
#[derive(Clone)]
pub enum NodeData<'arena> {
Document,
Doctype {
Expand Down Expand Up @@ -338,6 +339,21 @@ impl<'arena> TreeSink for Sink<'arena> {
new_parent.append(child)
}
}

fn clone_subtree(&self, node: &Self::Handle) -> Self::Handle {
// Allocate the new node in the arena using Clone
let cloned_node = self.arena.alloc(Node::new(node.data.clone()));

// Clone all children and append them
let mut child = node.first_child.get();
while let Some(current_child) = child {
let cloned_child = self.clone_subtree(&current_child);
cloned_node.append(cloned_child);
child = current_child.next_sibling.get();
}

cloned_node
}
}

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

let arena = typed_arena::Arena::new();
html5ever_parse_slice_into_arena(&bytes, &arena);
let dom = html5ever_parse_slice_into_arena(&bytes, &arena);

// Print the DOM structure
print_node(dom, 0);
}

fn print_node<'arena>(node: &Node<'arena>, depth: usize) {
let indent = " ".repeat(depth);

match &node.data {
NodeData::Document => println!("{}Document", indent),
NodeData::Doctype { name, .. } => println!("{}<!DOCTYPE {}>", indent, name),
NodeData::Text { contents } => {
let text = contents.borrow();
if !text.trim().is_empty() {
println!("{}\"{}\"", indent, text.trim());
}
},
NodeData::Comment { contents } => println!("{}<!-- {} -->", indent, contents),
NodeData::Element { name, .. } => println!("{}<{}>", indent, name.local),
NodeData::ProcessingInstruction { target, .. } => println!("{}<?{}>", indent, target),
}

// Print all children
let mut child = node.first_child.get();
while let Some(current_child) = child {
print_node(current_child, depth + 1);
child = current_child.next_sibling.get();
}
}
5 changes: 5 additions & 0 deletions html5ever/examples/noop-tree-builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,11 @@ impl TreeSink for Sink {
fn remove_from_parent(&self, _target: &usize) {}
fn reparent_children(&self, _node: &usize, _new_parent: &usize) {}
fn mark_script_already_started(&self, _node: &usize) {}

fn clone_subtree(&self, _node: &Self::Handle) -> Self::Handle {
// For this noop example, just return a new placeholder ID
self.get_id()
}
}

/// In this example we implement the TreeSink trait which takes each parsed elements and insert
Expand Down
6 changes: 6 additions & 0 deletions html5ever/examples/print-tree-actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,12 @@ impl TreeSink for Sink {
fn pop(&self, elem: &usize) {
println!("Popped element {elem}");
}

fn clone_subtree(&self, node: &Self::Handle) -> Self::Handle {
println!("Clone subtree for node {node}");
// For this example, just return a new placeholder ID
self.get_id()
}
}

/// Same example as the "noop-tree-builder", but this time every function implemented in our
Expand Down
40 changes: 30 additions & 10 deletions html5ever/src/tree_builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ pub struct TreeBuilder<Handle, Sink> {

/// Form element pointer.
form_elem: RefCell<Option<Handle>>,

/// selectedcontent element pointer.
selectedcontent_elem: RefCell<Option<Handle>>,
//§ END
/// Frameset-ok flag.
frameset_ok: Cell<bool>,
Expand Down Expand Up @@ -163,6 +166,7 @@ where
active_formatting: Default::default(),
head_elem: Default::default(),
form_elem: Default::default(),
selectedcontent_elem: Default::default(),
frameset_ok: Cell::new(true),
ignore_lf: Default::default(),
foster_parenting: Default::default(),
Expand Down Expand Up @@ -203,6 +207,7 @@ where
active_formatting: Default::default(),
head_elem: Default::default(),
form_elem: RefCell::new(form_elem),
selectedcontent_elem: Default::default(),
frameset_ok: Cell::new(true),
ignore_lf: Default::default(),
foster_parenting: Default::default(),
Expand Down Expand Up @@ -285,6 +290,10 @@ where
tracer.trace_handle(form_elem);
}

if let Some(selectedcontent_elem) = self.selectedcontent_elem.borrow().as_ref() {
tracer.trace_handle(selectedcontent_elem);
}

if let Some(context_elem) = self.context_elem.borrow().as_ref() {
tracer.trace_handle(context_elem);
}
Expand Down Expand Up @@ -923,6 +932,7 @@ where
.borrow_mut()
.pop()
.expect("no current element");

self.sink.pop(&elem);
elem
}
Expand Down Expand Up @@ -1183,6 +1193,7 @@ where
n
}

/// Pop element until an element with the given name has been popped.
fn pop_until_named(&self, name: LocalName) -> usize {
self.pop_until(|p| *p.ns == ns!(html) && *p.local == name)
}
Expand Down Expand Up @@ -1269,16 +1280,6 @@ where
_ => continue,
};
match *name {
local_name!("select") => {
for ancestor in self.open_elems.borrow()[0..i].iter().rev() {
if self.html_elem_named(ancestor, local_name!("template")) {
return InsertionMode::InSelect;
} else if self.html_elem_named(ancestor, local_name!("table")) {
return InsertionMode::InSelectInTable;
}
}
return InsertionMode::InSelect;
},
local_name!("td") | local_name!("th") => {
if !last {
return InsertionMode::InCell;
Expand Down Expand Up @@ -1401,6 +1402,12 @@ where

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

if qname.local == local_name!("selectedcontent")
&& self.selectedcontent_elem.borrow().is_none()
{
*self.selectedcontent_elem.borrow_mut() = Some(elem.clone());
}

match push {
PushFlag::Push => self.push(&elem),
PushFlag::NoPush => (),
Expand Down Expand Up @@ -1585,6 +1592,19 @@ where
self.remove_from_stack(&node);
}

fn maybe_clone_option_into_selectedcontent(&self, option: &Handle) {
if let Some(selectedcontent) = self.selectedcontent_elem.borrow().as_ref().cloned() {
self.clone_option_into_selectedcontent(option, &selectedcontent);
}
}

fn clone_option_into_selectedcontent(&self, option: &Handle, selectedcontent: &Handle) {
self.sink
.reparent_children(selectedcontent, &self.sink.get_document());
let cloned_option = self.sink.clone_subtree(option);
self.sink.reparent_children(&cloned_option, selectedcontent);
}

//§ tree-construction
fn is_foreign(&self, token: &Token) -> bool {
if let Token::Eof = *token {
Expand Down
Loading
Loading