Implement element.shadowRoot attribute (#34306)

* Implement Element.shadowRoot attribute

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>

* Remove comments about shadowdom not being exposed for web content

This is obviously not the case anymore.

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>

* Update WPT expectations

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>

---------

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
This commit is contained in:
Simon Wülker 2024-11-21 01:22:42 +01:00 committed by GitHub
parent c5cf2621b6
commit 527e2d426d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 209 additions and 65 deletions

View file

@ -73,11 +73,13 @@ use crate::dom::attr::{Attr, AttrHelpersForLayout};
use crate::dom::bindings::cell::{ref_filter_map, DomRefCell, Ref, RefMut};
use crate::dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
use crate::dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
use crate::dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
use crate::dom::bindings::codegen::Bindings::ElementBinding::{ElementMethods, ShadowRootInit};
use crate::dom::bindings::codegen::Bindings::FunctionBinding::Function;
use crate::dom::bindings::codegen::Bindings::HTMLTemplateElementBinding::HTMLTemplateElementMethods;
use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::ShadowRoot_Binding::ShadowRootMethods;
use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{
ShadowRootMethods, ShadowRootMode,
};
use crate::dom::bindings::codegen::Bindings::WindowBinding::{
ScrollBehavior, ScrollToOptions, WindowMethods,
};
@ -507,9 +509,11 @@ impl Element {
}
/// <https://dom.spec.whatwg.org/#dom-element-attachshadow>
/// XXX This is not exposed to web content yet. It is meant to be used
/// for UA widgets only.
pub fn attach_shadow(&self, is_ua_widget: IsUserAgentWidget) -> Fallible<DomRoot<ShadowRoot>> {
pub fn attach_shadow(
&self,
is_ua_widget: IsUserAgentWidget,
mode: ShadowRootMode,
) -> Fallible<DomRoot<ShadowRoot>> {
// Step 1.
if self.namespace != ns!(html) {
return Err(Error::NotSupported);
@ -546,7 +550,7 @@ impl Element {
}
// Steps 4, 5 and 6.
let shadow_root = ShadowRoot::new(self, &self.node.owner_doc());
let shadow_root = ShadowRoot::new(self, &self.node.owner_doc(), mode);
self.ensure_rare_data().shadow_root = Some(Dom::from_ref(&*shadow_root));
shadow_root
.upcast::<Node>()
@ -3050,11 +3054,29 @@ impl ElementMethods for Element {
doc.enter_fullscreen(self, can_gc)
}
// XXX Hidden under dom.shadowdom.enabled pref. Only exposed to be able
// to test partial Shadow DOM support for UA widgets.
// https://dom.spec.whatwg.org/#dom-element-attachshadow
fn AttachShadow(&self) -> Fallible<DomRoot<ShadowRoot>> {
self.attach_shadow(IsUserAgentWidget::No)
fn AttachShadow(&self, init: &ShadowRootInit) -> Fallible<DomRoot<ShadowRoot>> {
// Step 1. Run attach a shadow root with this, init["mode"], init["clonable"], init["serializable"],
// init["delegatesFocus"], and init["slotAssignment"].
let shadow_root = self.attach_shadow(IsUserAgentWidget::No, init.mode)?;
// Step 2. Return thiss shadow root.
Ok(shadow_root)
}
/// <https://dom.spec.whatwg.org/#dom-element-shadowroot>
fn GetShadowRoot(&self) -> Option<DomRoot<ShadowRoot>> {
// Step 1. Let shadow be thiss shadow root.
let shadow_or_none = self.shadow_root();
// Step 2. If shadow is null or its mode is "closed", then return null.
let shadow = shadow_or_none?;
if shadow.Mode() == ShadowRootMode::Closed {
return None;
}
// Step 3. Return shadow.
Some(shadow)
}
fn GetRole(&self) -> Option<DOMString> {

View file

@ -54,6 +54,7 @@ use crate::dom::bindings::codegen::Bindings::MediaErrorBinding::MediaErrorConsta
use crate::dom::bindings::codegen::Bindings::MediaErrorBinding::MediaErrorMethods;
use crate::dom::bindings::codegen::Bindings::NavigatorBinding::Navigator_Binding::NavigatorMethods;
use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods;
use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::ShadowRootMode;
use crate::dom::bindings::codegen::Bindings::TextTrackBinding::{TextTrackKind, TextTrackMode};
use crate::dom::bindings::codegen::Bindings::URLBinding::URLMethods;
use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::WindowMethods;
@ -1908,7 +1909,9 @@ impl HTMLMediaElement {
// if we are already showing the controls.
return;
}
let shadow_root = element.attach_shadow(IsUserAgentWidget::Yes).unwrap();
let shadow_root = element
.attach_shadow(IsUserAgentWidget::Yes, ShadowRootMode::Closed)
.unwrap();
let document = document_from_node(self);
let script = HTMLScriptElement::new(
local_name!("script"),

View file

@ -36,7 +36,7 @@ pub enum IsUserAgentWidget {
Yes,
}
// https://dom.spec.whatwg.org/#interface-shadowroot
/// <https://dom.spec.whatwg.org/#interface-shadowroot>
#[dom_struct]
pub struct ShadowRoot {
document_fragment: DocumentFragment,
@ -48,11 +48,14 @@ pub struct ShadowRoot {
author_styles: DomRefCell<AuthorStyles<StyleSheetInDocument>>,
stylesheet_list: MutNullableDom<StyleSheetList>,
window: Dom<Window>,
/// <https://dom.spec.whatwg.org/#dom-shadowroot-mode>
mode: ShadowRootMode,
}
impl ShadowRoot {
#[allow(crown::unrooted_must_root)]
fn new_inherited(host: &Element, document: &Document) -> ShadowRoot {
fn new_inherited(host: &Element, document: &Document, mode: ShadowRootMode) -> ShadowRoot {
let document_fragment = DocumentFragment::new_inherited(document);
let node = document_fragment.upcast::<Node>();
node.set_flag(NodeFlags::IS_IN_SHADOW_TREE, true);
@ -60,6 +63,7 @@ impl ShadowRoot {
NodeFlags::IS_CONNECTED,
host.upcast::<Node>().is_connected(),
);
ShadowRoot {
document_fragment,
document_or_shadow_root: DocumentOrShadowRoot::new(document.window()),
@ -68,12 +72,13 @@ impl ShadowRoot {
author_styles: DomRefCell::new(AuthorStyles::new()),
stylesheet_list: MutNullableDom::new(None),
window: Dom::from_ref(document.window()),
mode,
}
}
pub fn new(host: &Element, document: &Document) -> DomRoot<ShadowRoot> {
pub fn new(host: &Element, document: &Document, mode: ShadowRootMode) -> DomRoot<ShadowRoot> {
reflect_dom_object(
Box::new(ShadowRoot::new_inherited(host, document)),
Box::new(ShadowRoot::new_inherited(host, document, mode)),
document.window(),
)
}
@ -226,7 +231,7 @@ impl ShadowRootMethods for ShadowRoot {
/// <https://dom.spec.whatwg.org/#dom-shadowroot-mode>
fn Mode(&self) -> ShadowRootMode {
ShadowRootMode::Closed
self.mode
}
/// <https://dom.spec.whatwg.org/#dom-shadowroot-host>

View file

@ -83,7 +83,16 @@ interface Element : Node {
[CEReactions, Throws]
undefined insertAdjacentHTML(DOMString position, DOMString html);
[Throws, Pref="dom.shadowdom.enabled"] ShadowRoot attachShadow();
[Throws, Pref="dom.shadowdom.enabled"] ShadowRoot attachShadow(ShadowRootInit init);
readonly attribute ShadowRoot? shadowRoot;
};
dictionary ShadowRootInit {
required ShadowRootMode mode;
// boolean delegatesFocus = false;
// SlotAssignmentMode slotAssignment = "named";
// boolean clonable = false;
// boolean serializable = false;
};
// http://dev.w3.org/csswg/cssom-view/#extensions-to-the-element-interface

View file

@ -13,5 +13,6 @@ interface ShadowRoot : DocumentFragment {
};
enum ShadowRootMode { "open", "closed"};
// enum SlotAssignmentMode { "manual", "named" };
ShadowRoot includes DocumentOrShadowRoot;

View file

@ -1,2 +1,3 @@
[transform-origin-in-shadow.html]
expected: ERROR
['transform-origin' on <svg> being direct descendant of shadow root]
expected: FAIL

View file

@ -64,9 +64,6 @@
[XPathResult interface: constant STRING_TYPE on interface prototype object]
expected: FAIL
[Element interface: attribute shadowRoot]
expected: FAIL
[XPathResult interface: document.evaluate("//*", document.body) must inherit property "ORDERED_NODE_ITERATOR_TYPE" with the proper type]
expected: FAIL
@ -91,9 +88,6 @@
[EventTarget interface: new AbortController().signal must inherit property "removeEventListener(DOMString, EventListener?, optional (EventListenerOptions or boolean))" with the proper type]
expected: FAIL
[Element interface: operation attachShadow(ShadowRootInit)]
expected: FAIL
[Document interface: xmlDoc must inherit property "createNSResolver(Node)" with the proper type]
expected: FAIL
@ -124,9 +118,6 @@
[XPathResult interface: constant UNORDERED_NODE_SNAPSHOT_TYPE on interface prototype object]
expected: FAIL
[Element interface: calling attachShadow(ShadowRootInit) on element with too few arguments must throw TypeError]
expected: FAIL
[XPathResult interface: constant ORDERED_NODE_SNAPSHOT_TYPE on interface object]
expected: FAIL
@ -511,9 +502,6 @@
[Element interface: operation append((Node or DOMString)...)]
expected: FAIL
[Element interface: element must inherit property "shadowRoot" with the proper type]
expected: FAIL
[XPathEvaluator interface: existence and properties of interface prototype object]
expected: FAIL

View file

@ -1,2 +1,2 @@
[dialog-focus-shadow-double-nested.html]
expected: ERROR
expected: CRASH

View file

@ -1,2 +1,150 @@
[dialog-focus-shadow.html]
expected: ERROR
[show: No autofocus, no delegatesFocus, no siblings]
expected: FAIL
[showModal: No autofocus, no delegatesFocus, no siblings]
expected: FAIL
[show: No autofocus, no delegatesFocus, sibling before]
expected: FAIL
[showModal: No autofocus, no delegatesFocus, sibling before]
expected: FAIL
[show: No autofocus, no delegatesFocus, sibling after]
expected: FAIL
[showModal: No autofocus, no delegatesFocus, sibling after]
expected: FAIL
[show: No autofocus, yes delegatesFocus, no siblings]
expected: FAIL
[showModal: No autofocus, yes delegatesFocus, no siblings]
expected: FAIL
[show: No autofocus, yes delegatesFocus, sibling before]
expected: FAIL
[showModal: No autofocus, yes delegatesFocus, sibling before]
expected: FAIL
[show: No autofocus, yes delegatesFocus, sibling after]
expected: FAIL
[showModal: No autofocus, yes delegatesFocus, sibling after]
expected: FAIL
[show: Autofocus before, no delegatesFocus]
expected: FAIL
[showModal: Autofocus before, no delegatesFocus]
expected: FAIL
[show: Autofocus before, yes delegatesFocus]
expected: FAIL
[showModal: Autofocus before, yes delegatesFocus]
expected: FAIL
[show: Autofocus after, no delegatesFocus]
expected: FAIL
[showModal: Autofocus after, no delegatesFocus]
expected: FAIL
[show: Autofocus after, yes delegatesFocus]
expected: FAIL
[showModal: Autofocus after, yes delegatesFocus]
expected: FAIL
[show: Autofocus on shadow host, yes delegatesFocus, no siblings]
expected: FAIL
[showModal: Autofocus on shadow host, yes delegatesFocus, no siblings]
expected: FAIL
[show: Autofocus on shadow host, yes delegatesFocus, sibling before]
expected: FAIL
[showModal: Autofocus on shadow host, yes delegatesFocus, sibling before]
expected: FAIL
[show: Autofocus on shadow host, yes delegatesFocus, sibling after]
expected: FAIL
[showModal: Autofocus on shadow host, yes delegatesFocus, sibling after]
expected: FAIL
[show: Autofocus on shadow host, no delegatesFocus, no siblings]
expected: FAIL
[showModal: Autofocus on shadow host, no delegatesFocus, no siblings]
expected: FAIL
[show: Autofocus on shadow host, no delegatesFocus, sibling before]
expected: FAIL
[showModal: Autofocus on shadow host, no delegatesFocus, sibling before]
expected: FAIL
[show: Autofocus on shadow host, no delegatesFocus, sibling after]
expected: FAIL
[showModal: Autofocus on shadow host, no delegatesFocus, sibling after]
expected: FAIL
[show: Autofocus inside shadow tree, yes delegatesFocus, no siblings]
expected: FAIL
[showModal: Autofocus inside shadow tree, yes delegatesFocus, no siblings]
expected: FAIL
[show: Autofocus inside shadow tree, yes delegatesFocus, sibling before]
expected: FAIL
[showModal: Autofocus inside shadow tree, yes delegatesFocus, sibling before]
expected: FAIL
[show: Autofocus inside shadow tree, yes delegatesFocus, sibling after]
expected: FAIL
[showModal: Autofocus inside shadow tree, yes delegatesFocus, sibling after]
expected: FAIL
[show: Autofocus inside shadow tree, no delegatesFocus, no siblings]
expected: FAIL
[showModal: Autofocus inside shadow tree, no delegatesFocus, no siblings]
expected: FAIL
[show: Autofocus inside shadow tree, no delegatesFocus, sibling before]
expected: FAIL
[showModal: Autofocus inside shadow tree, no delegatesFocus, sibling before]
expected: FAIL
[show: Autofocus inside shadow tree, no delegatesFocus, sibling after]
expected: FAIL
[showModal: Autofocus inside shadow tree, no delegatesFocus, sibling after]
expected: FAIL
[show: Two shadow trees, both delegatesFocus, first tree doesn't have autofocus element, second does]
expected: FAIL
[showModal: Two shadow trees, both delegatesFocus, first tree doesn't have autofocus element, second does]
expected: FAIL
[show: No autofocus, no delegatesFocus, slotted target]
expected: FAIL
[showModal: No autofocus, no delegatesFocus, slotted target]
expected: FAIL
[show: Shadowroot on child, no autofocus, no delegatesFocus]
expected: FAIL
[showModal: Shadowroot on child, no autofocus, no delegatesFocus]
expected: FAIL

View file

@ -1,6 +1,3 @@
[Element-interface-attachShadow.html]
[Element.attachShadow must throw a TypeError if mode is not "open" or "closed"]
expected: FAIL
[Element.attachShadow must throw a NotSupportedError if the context object already hosts a shadow tree]
expected: FAIL

View file

@ -1,9 +0,0 @@
[Element-interface-shadowRoot-attribute.html]
[shadowRoot must be defined on Element prototype]
expected: FAIL
[shadowRoot attribute must return the open shadow root associated with the element]
expected: FAIL
[shadowRoot attribute must return null if the shadow root attached to the element is closed]
expected: FAIL

View file

@ -4,6 +4,3 @@
[cloneNode on a shadow root in closed mode must throw a NotSupportedError]
expected: FAIL
[cloneNode on an element with an open shadow root should not clone its shadow root]
expected: FAIL

View file

@ -56,8 +56,5 @@
[Declarative Shadow DOM: template containing declarative shadow root and UA shadow root]
expected: FAIL
[Declarative Shadow DOM: declarative shadow roots are not supported by the template element]
expected: FAIL
[Declarative Shadow DOM: explicit test that exceptions are not thrown]
expected: FAIL

View file

@ -8,15 +8,9 @@
[:focus applies to host with delegatesFocus=true when an element in a nested shadow tree with delegatesFocus=true is focused]
expected: FAIL
[:focus should be removed from hosts with delegatesFocus=true when none of the elements in a nested shadow tree with delegatesFocus=true is focused]
expected: FAIL
[:focus applies to host with delegatesFocus=true when an element in a nested shadow tree with delegatesFocus=false is focused]
expected: FAIL
[:focus should be removed from hosts with delegatesFocus=true when none of the elements in a nested shadow tree with delegatesFocus=false is focused]
expected: FAIL
[:focus applies to host with delegatesFocus=false when the shadow root's descendant has focus]
expected: FAIL
@ -26,11 +20,5 @@
[:focus applies to host with delegatesFocus=false when an element in a nested shadow tree with delegatesFocus=true is focused]
expected: FAIL
[:focus should be removed from hosts with delegatesFocus=false when none of the elements in a nested shadow tree with delegatesFocus=true is focused]
expected: FAIL
[:focus applies to host with delegatesFocus=false when an element in a nested shadow tree with delegatesFocus=false is focused]
expected: FAIL
[:focus should be removed from hosts with delegatesFocus=false when none of the elements in a nested shadow tree with delegatesFocus=false is focused]
expected: FAIL

View file

@ -1,3 +0,0 @@
[test-006.html]
[A_10_02_01_06_T01]
expected: FAIL