mirror of
https://github.com/servo/servo.git
synced 2025-08-05 13:40:08 +01:00
Implement declarative shadow root support for async HTML parser (#37443)
Implements the [`TreeSink::attach_declarative_shadow`](https://docs.rs/html5ever/latest/html5ever/interface/trait.TreeSink.html#method.attach_declarative_shadow) method for the async html parser. Try run with the async html parser: https://github.com/simonwuelker/servo/actions/runs/15634240606/job/44046871826. (There are far fewer failures than in the initial try run from https://github.com/servo/servo/issues/37418) Testing: We don't run tests with the async html parser Part of https://github.com/servo/servo/issues/37418 Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
This commit is contained in:
parent
9487f66eaa
commit
2dc62c504f
2 changed files with 151 additions and 76 deletions
|
@ -37,7 +37,9 @@ use crate::dom::htmlscriptelement::HTMLScriptElement;
|
||||||
use crate::dom::htmltemplateelement::HTMLTemplateElement;
|
use crate::dom::htmltemplateelement::HTMLTemplateElement;
|
||||||
use crate::dom::node::Node;
|
use crate::dom::node::Node;
|
||||||
use crate::dom::processinginstruction::ProcessingInstruction;
|
use crate::dom::processinginstruction::ProcessingInstruction;
|
||||||
use crate::dom::servoparser::{ElementAttribute, ParsingAlgorithm, create_element_for_token};
|
use crate::dom::servoparser::{
|
||||||
|
ElementAttribute, ParsingAlgorithm, attach_declarative_shadow_inner, create_element_for_token,
|
||||||
|
};
|
||||||
use crate::dom::virtualmethods::vtable_for;
|
use crate::dom::virtualmethods::vtable_for;
|
||||||
use crate::script_runtime::CanGc;
|
use crate::script_runtime::CanGc;
|
||||||
|
|
||||||
|
@ -139,6 +141,15 @@ enum ParseOperation {
|
||||||
#[no_trace]
|
#[no_trace]
|
||||||
mode: ServoQuirksMode,
|
mode: ServoQuirksMode,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
AttachDeclarativeShadowRoot {
|
||||||
|
location: ParseNodeId,
|
||||||
|
template: ParseNodeId,
|
||||||
|
attributes: Vec<Attribute>,
|
||||||
|
/// Used to notify the parser thread whether or not attaching the shadow root succeeded
|
||||||
|
#[no_trace]
|
||||||
|
sender: Sender<bool>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(MallocSizeOf)]
|
#[derive(MallocSizeOf)]
|
||||||
|
@ -562,6 +573,26 @@ impl Tokenizer {
|
||||||
ParseOperation::SetQuirksMode { mode } => {
|
ParseOperation::SetQuirksMode { mode } => {
|
||||||
document.set_quirks_mode(mode);
|
document.set_quirks_mode(mode);
|
||||||
},
|
},
|
||||||
|
ParseOperation::AttachDeclarativeShadowRoot {
|
||||||
|
location,
|
||||||
|
template,
|
||||||
|
attributes,
|
||||||
|
sender,
|
||||||
|
} => {
|
||||||
|
let location = self.get_node(&location);
|
||||||
|
let template = self.get_node(&template);
|
||||||
|
let attributes = attributes
|
||||||
|
.into_iter()
|
||||||
|
.map(|attribute| HtmlAttribute {
|
||||||
|
name: attribute.name,
|
||||||
|
value: StrTendril::from(attribute.value),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let did_succeed =
|
||||||
|
attach_declarative_shadow_inner(&location, &template, attributes).is_ok();
|
||||||
|
sender.send(did_succeed).unwrap();
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -920,4 +951,40 @@ impl TreeSink for Sink {
|
||||||
fn pop(&self, node: &Self::Handle) {
|
fn pop(&self, node: &Self::Handle) {
|
||||||
self.send_op(ParseOperation::Pop { node: node.id });
|
self.send_op(ParseOperation::Pop { node: node.id });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attach declarative shadow
|
||||||
|
fn attach_declarative_shadow(
|
||||||
|
&self,
|
||||||
|
location: &Self::Handle,
|
||||||
|
template: &Self::Handle,
|
||||||
|
attributes: Vec<HtmlAttribute>,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
let attributes = attributes
|
||||||
|
.into_iter()
|
||||||
|
.map(|attribute| Attribute {
|
||||||
|
name: attribute.name,
|
||||||
|
value: String::from(attribute.value),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Unfortunately the parser can only proceed after it knows whether attaching the shadow root
|
||||||
|
// succeeded or failed. Attaching a shadow root can fail for many different reasons,
|
||||||
|
// and so we need to block until the script thread has processed this operation.
|
||||||
|
let (sender, receiver) = unbounded();
|
||||||
|
self.send_op(ParseOperation::AttachDeclarativeShadowRoot {
|
||||||
|
location: location.id,
|
||||||
|
template: template.id,
|
||||||
|
attributes,
|
||||||
|
sender,
|
||||||
|
});
|
||||||
|
|
||||||
|
let did_succeed = receiver.recv().unwrap();
|
||||||
|
|
||||||
|
// TODO: This api is silly, we shouldn't have to return a string here
|
||||||
|
if did_succeed {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err("Attaching declarative shadow root failed".to_owned())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1441,81 +1441,7 @@ impl TreeSink for Sink {
|
||||||
template: &Dom<Node>,
|
template: &Dom<Node>,
|
||||||
attrs: Vec<Attribute>,
|
attrs: Vec<Attribute>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let host_element = host.downcast::<Element>().unwrap();
|
attach_declarative_shadow_inner(host, template, attrs)
|
||||||
|
|
||||||
if host_element.shadow_root().is_some() {
|
|
||||||
return Err(String::from("Already in a shadow host"));
|
|
||||||
}
|
|
||||||
|
|
||||||
let template_element = template.downcast::<HTMLTemplateElement>().unwrap();
|
|
||||||
|
|
||||||
// Step 3. Let mode be template start tag's shadowrootmode attribute's value.
|
|
||||||
// Step 4. Let clonable be true if template start tag has a shadowrootclonable attribute; otherwise false.
|
|
||||||
// Step 5. Let delegatesfocus be true if template start tag
|
|
||||||
// has a shadowrootdelegatesfocus attribute; otherwise false.
|
|
||||||
// Step 6. Let serializable be true if template start tag
|
|
||||||
// has a shadowrootserializable attribute; otherwise false.
|
|
||||||
let mut shadow_root_mode = ShadowRootMode::Open;
|
|
||||||
let mut clonable = false;
|
|
||||||
let mut delegatesfocus = false;
|
|
||||||
let mut serializable = false;
|
|
||||||
|
|
||||||
let attrs: Vec<ElementAttribute> = attrs
|
|
||||||
.clone()
|
|
||||||
.into_iter()
|
|
||||||
.map(|attr| ElementAttribute::new(attr.name, DOMString::from(String::from(attr.value))))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
attrs
|
|
||||||
.iter()
|
|
||||||
.for_each(|attr: &ElementAttribute| match attr.name.local {
|
|
||||||
local_name!("shadowrootmode") => {
|
|
||||||
if attr.value.str().eq_ignore_ascii_case("open") {
|
|
||||||
shadow_root_mode = ShadowRootMode::Open;
|
|
||||||
} else if attr.value.str().eq_ignore_ascii_case("closed") {
|
|
||||||
shadow_root_mode = ShadowRootMode::Closed;
|
|
||||||
} else {
|
|
||||||
unreachable!("shadowrootmode value is not open nor closed");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
local_name!("shadowrootclonable") => {
|
|
||||||
clonable = true;
|
|
||||||
},
|
|
||||||
local_name!("shadowrootdelegatesfocus") => {
|
|
||||||
delegatesfocus = true;
|
|
||||||
},
|
|
||||||
local_name!("shadowrootserializable") => {
|
|
||||||
serializable = true;
|
|
||||||
},
|
|
||||||
_ => {},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Step 8.1. Attach a shadow root with declarative shadow host element,
|
|
||||||
// mode, clonable, serializable, delegatesFocus, and "named".
|
|
||||||
match host_element.attach_shadow(
|
|
||||||
IsUserAgentWidget::No,
|
|
||||||
shadow_root_mode,
|
|
||||||
clonable,
|
|
||||||
serializable,
|
|
||||||
delegatesfocus,
|
|
||||||
SlotAssignmentMode::Named,
|
|
||||||
CanGc::note(),
|
|
||||||
) {
|
|
||||||
Ok(shadow_root) => {
|
|
||||||
// Step 8.3. Set shadow's declarative to true.
|
|
||||||
shadow_root.set_declarative(true);
|
|
||||||
|
|
||||||
// Set 8.4. Set template's template contents property to shadow.
|
|
||||||
let shadow = shadow_root.upcast::<DocumentFragment>();
|
|
||||||
template_element.set_contents(Some(shadow));
|
|
||||||
|
|
||||||
// Step 8.5. Set shadow’s available to element internals to true.
|
|
||||||
shadow_root.set_available_to_element_internals(true);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
Err(_) => Err(String::from("Attaching shadow fails")),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1652,3 +1578,85 @@ impl TendrilSink<UTF8> for NetworkSink {
|
||||||
self.output
|
self.output
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn attach_declarative_shadow_inner(
|
||||||
|
host: &Node,
|
||||||
|
template: &Node,
|
||||||
|
attrs: Vec<Attribute>,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
let host_element = host.downcast::<Element>().unwrap();
|
||||||
|
|
||||||
|
if host_element.shadow_root().is_some() {
|
||||||
|
return Err(String::from("Already in a shadow host"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let template_element = template.downcast::<HTMLTemplateElement>().unwrap();
|
||||||
|
|
||||||
|
// Step 3. Let mode be template start tag's shadowrootmode attribute's value.
|
||||||
|
// Step 4. Let clonable be true if template start tag has a shadowrootclonable attribute; otherwise false.
|
||||||
|
// Step 5. Let delegatesfocus be true if template start tag
|
||||||
|
// has a shadowrootdelegatesfocus attribute; otherwise false.
|
||||||
|
// Step 6. Let serializable be true if template start tag
|
||||||
|
// has a shadowrootserializable attribute; otherwise false.
|
||||||
|
let mut shadow_root_mode = ShadowRootMode::Open;
|
||||||
|
let mut clonable = false;
|
||||||
|
let mut delegatesfocus = false;
|
||||||
|
let mut serializable = false;
|
||||||
|
|
||||||
|
let attrs: Vec<ElementAttribute> = attrs
|
||||||
|
.clone()
|
||||||
|
.into_iter()
|
||||||
|
.map(|attr| ElementAttribute::new(attr.name, DOMString::from(String::from(attr.value))))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
attrs
|
||||||
|
.iter()
|
||||||
|
.for_each(|attr: &ElementAttribute| match attr.name.local {
|
||||||
|
local_name!("shadowrootmode") => {
|
||||||
|
if attr.value.str().eq_ignore_ascii_case("open") {
|
||||||
|
shadow_root_mode = ShadowRootMode::Open;
|
||||||
|
} else if attr.value.str().eq_ignore_ascii_case("closed") {
|
||||||
|
shadow_root_mode = ShadowRootMode::Closed;
|
||||||
|
} else {
|
||||||
|
unreachable!("shadowrootmode value is not open nor closed");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
local_name!("shadowrootclonable") => {
|
||||||
|
clonable = true;
|
||||||
|
},
|
||||||
|
local_name!("shadowrootdelegatesfocus") => {
|
||||||
|
delegatesfocus = true;
|
||||||
|
},
|
||||||
|
local_name!("shadowrootserializable") => {
|
||||||
|
serializable = true;
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Step 8.1. Attach a shadow root with declarative shadow host element,
|
||||||
|
// mode, clonable, serializable, delegatesFocus, and "named".
|
||||||
|
match host_element.attach_shadow(
|
||||||
|
IsUserAgentWidget::No,
|
||||||
|
shadow_root_mode,
|
||||||
|
clonable,
|
||||||
|
serializable,
|
||||||
|
delegatesfocus,
|
||||||
|
SlotAssignmentMode::Named,
|
||||||
|
CanGc::note(),
|
||||||
|
) {
|
||||||
|
Ok(shadow_root) => {
|
||||||
|
// Step 8.3. Set shadow's declarative to true.
|
||||||
|
shadow_root.set_declarative(true);
|
||||||
|
|
||||||
|
// Set 8.4. Set template's template contents property to shadow.
|
||||||
|
let shadow = shadow_root.upcast::<DocumentFragment>();
|
||||||
|
template_element.set_contents(Some(shadow));
|
||||||
|
|
||||||
|
// Step 8.5. Set shadow’s available to element internals to true.
|
||||||
|
shadow_root.set_available_to_element_internals(true);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
Err(_) => Err(String::from("Attaching shadow fails")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue