Skip to content

Commit

Permalink
Add support for declarative shadow DOM (#578)
Browse files Browse the repository at this point in the history
* Update declarative shadow dom

Signed-off-by: batu_hoang <longvatrong111@gmail.com>

* Fix shadowhost to adjusted current node

Signed-off-by: batu_hoang <longvatrong111@gmail.com>

* Update comments according to the spec

Signed-off-by: batu_hoang <longvatrong111@gmail.com>

---------

Signed-off-by: batu_hoang <longvatrong111@gmail.com>
  • Loading branch information
longvatrong111 authored Mar 7, 2025
1 parent f952757 commit 6b3601a
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 7 deletions.
37 changes: 34 additions & 3 deletions html5ever/src/tree_builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1376,8 +1376,31 @@ where
fn insert_phantom(&self, name: LocalName) -> Handle {
self.insert_element(Push, ns!(html), name, vec![])
}

/// <https://html.spec.whatwg.org/multipage/parsing.html#insert-an-element-at-the-adjusted-insertion-location>
fn insert_foreign_element(
&self,
tag: Tag,
ns: Namespace,
only_add_to_element_stack: bool,
) -> Handle {
let adjusted_insertion_location = self.appropriate_place_for_insertion(None);
let qname = QualName::new(None, ns, tag.name);
let elem = create_element(&self.sink, qname.clone(), tag.attrs.clone());

if !only_add_to_element_stack {
self.insert_at(adjusted_insertion_location, AppendNode(elem.clone()));
}

self.push(&elem);

elem
}
//§ END

/// <https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-inhead>
///
/// A start tag whose tag name is "template"
fn should_attach_declarative_shadow(&self, tag: &Tag) -> bool {
let adjusted_insertion_location = self.appropriate_place_for_insertion(None);

Expand All @@ -1392,7 +1415,7 @@ where
// template start tag's shadowrootmode is not in the none state
let is_shadow_root_mode = tag.attrs.iter().any(|attr| {
attr.name.local == local_name!("shadowrootmode")
&& (attr.value.to_string() == *"open" || attr.value.to_string() == *"close")
&& (attr.value.as_ref() == "open" || attr.value.as_ref() == "closed")
});

// Check if intended_parent's document allows declarative shadow roots
Expand All @@ -1416,9 +1439,17 @@ where
is_shadow_root_mode && allow_declarative_shadow_roots && adjusted_current_node_not_topmost
}

fn attach_declarative_shadow(&self, tag: &Tag) -> Result<(), String> {
/// <https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-inhead>
///
/// A start tag whose tag name is "template"
fn attach_declarative_shadow(
&self,
tag: &Tag,
shadow_host: &Handle,
template: &Handle,
) -> Result<(), String> {
self.sink
.attach_declarative_shadow(self.open_elems.borrow().last().unwrap(), tag.attrs.clone())
.attach_declarative_shadow(shadow_host, template, tag.attrs.clone())
}

fn create_formatting_element_for(&self, tag: Tag) -> Handle {
Expand Down
22 changes: 18 additions & 4 deletions html5ever/src/tree_builder/rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,10 +160,24 @@ where
self.template_modes.borrow_mut().push(InTemplate);

if (self.should_attach_declarative_shadow(&tag)) {
if self.attach_declarative_shadow(&tag).is_err() {
// TODO:
// insert at the adjusted insertion location
// with the result of insert a foreign element for template tag
// Attach shadow path

// Step 1. Let declarative shadow host element be adjusted current node.
let mut shadow_host = self.open_elems.borrow().last().unwrap().clone();
if self.is_fragment() && self.open_elems.borrow().len() == 1 {
shadow_host = self.context_elem.borrow().clone().unwrap();
}

// Step 2. Let template be the result of insert a foreign element for template start tag, with HTML namespace and true.
let template = self.insert_foreign_element(tag.clone(), ns!(html), true);

// Step 3 - 8.
// Attach a shadow root with declarative shadow host element, mode, clonable, serializable, delegatesFocus, and "named".
if self.attach_declarative_shadow(&tag, &shadow_host, &template).is_err() {
// Step 8.1.1. Insert an element at the adjusted insertion location with template.
// Pop the current template element created in step 2 first.
self.pop();
self.insert_element_for(tag);
}
} else {
self.insert_element_for(tag);
Expand Down
1 change: 1 addition & 0 deletions markup5ever/interface/tree_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ pub trait TreeSink {
fn attach_declarative_shadow(
&self,
_location: &Self::Handle,
_template: &Self::Handle,
_attrs: Vec<Attribute>,
) -> Result<(), String> {
Err(String::from(
Expand Down

0 comments on commit 6b3601a

Please sign in to comment.