Skip to main content

script/dom/shadowroot/
shadowroot.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::cell::Cell;
6use std::collections::HashMap;
7use std::collections::hash_map::Entry;
8
9use dom_struct::dom_struct;
10use html5ever::serialize::TraversalScope;
11use js::context::{JSContext, NoGC};
12use script_bindings::cell::{DomRefCell, RefMut};
13use script_bindings::dom::UnrootedDom;
14use script_bindings::domstring::TracedDOMString;
15use script_bindings::error::{ErrorResult, Fallible};
16use script_bindings::reflector::reflect_dom_object;
17use servo_arc::Arc;
18use style::author_styles::AuthorStyles;
19use style::invalidation::element::restyle_hints::RestyleHint;
20use style::shared_lock::SharedRwLockReadGuard;
21use style::stylesheets::Stylesheet;
22use style::stylist::{CascadeData, Stylist};
23use stylo_atoms::Atom;
24
25use crate::conversions::Convert;
26use crate::css::stylesheet_set::StylesheetSetRef;
27use crate::dom::bindings::codegen::Bindings::ElementBinding::GetHTMLOptions;
28use crate::dom::bindings::codegen::Bindings::HTMLSlotElementBinding::HTMLSlotElement_Binding::HTMLSlotElementMethods;
29use crate::dom::bindings::codegen::Bindings::SanitizerBinding::{
30    SetHTMLOptions, SetHTMLUnsafeOptions,
31};
32use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::ShadowRoot_Binding::ShadowRootMethods;
33use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{
34    ShadowRootMode, SlotAssignmentMode,
35};
36use crate::dom::bindings::codegen::UnionTypes::{
37    TrustedHTMLOrNullIsEmptyString, TrustedHTMLOrString,
38};
39use crate::dom::bindings::inheritance::Castable;
40use crate::dom::bindings::num::Finite;
41use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom};
42use crate::dom::bindings::str::DOMString;
43use crate::dom::css::cssstylesheet::CSSStyleSheet;
44use crate::dom::css::stylesheetlist::{StyleSheetList, StyleSheetListOwner};
45use crate::dom::customelementregistry::CustomElementRegistry;
46use crate::dom::document::Document;
47use crate::dom::documentfragment::DocumentFragment;
48use crate::dom::documentorshadowroot::{
49    DocumentOrShadowRoot, ServoStylesheetInDocument, StylesheetSource,
50};
51use crate::dom::element::Element;
52use crate::dom::html::htmlslotelement::HTMLSlotElement;
53use crate::dom::htmldetailselement::DetailsNameGroups;
54use crate::dom::iterators::ShadowIncluding;
55use crate::dom::node::virtualmethods::{VirtualMethods, vtable_for};
56use crate::dom::node::{
57    BindContext, IsShadowTree, Node, NodeDamage, NodeFlags, NodeTraits, UnbindContext,
58    VecPreOrderInsertionHelper,
59};
60use crate::dom::sanitizer::Sanitizer;
61use crate::dom::trustedtypes::trustedhtml::TrustedHTML;
62use crate::dom::types::EventTarget;
63use crate::dom::window::Window;
64
65/// Whether a shadow root hosts an User Agent widget.
66#[derive(JSTraceable, MallocSizeOf, PartialEq)]
67pub(crate) enum IsUserAgentWidget {
68    No,
69    Yes,
70}
71
72/// <https://dom.spec.whatwg.org/#interface-shadowroot>
73#[dom_struct]
74pub(crate) struct ShadowRoot {
75    /// The [`DocumentFragment`] that this [`ShadowRoot`] inherits from.
76    document_fragment: DocumentFragment,
77    document_or_shadow_root: DocumentOrShadowRoot,
78    document: Dom<Document>,
79    /// List of author styles associated with nodes in this shadow tree.
80    #[custom_trace]
81    author_styles: DomRefCell<AuthorStyles<ServoStylesheetInDocument>>,
82    stylesheet_list: MutNullableDom<StyleSheetList>,
83    window: Dom<Window>,
84
85    /// <https://dom.spec.whatwg.org/#dom-shadowroot-mode>
86    mode: ShadowRootMode,
87
88    /// <https://dom.spec.whatwg.org/#dom-shadowroot-slotassignment>
89    slot_assignment_mode: SlotAssignmentMode,
90
91    /// <https://dom.spec.whatwg.org/#dom-shadowroot-clonable>
92    clonable: bool,
93
94    /// <https://dom.spec.whatwg.org/#shadowroot-available-to-element-internals>
95    available_to_element_internals: Cell<bool>,
96
97    slots: DomRefCell<HashMap<TracedDOMString, Vec<Dom<HTMLSlotElement>>>>,
98
99    is_user_agent_widget: bool,
100
101    /// <https://dom.spec.whatwg.org/#shadowroot-declarative>
102    declarative: Cell<bool>,
103
104    /// <https://dom.spec.whatwg.org/#shadowroot-serializable>
105    serializable: Cell<bool>,
106
107    /// <https://dom.spec.whatwg.org/#shadowroot-delegates-focus>
108    delegates_focus: Cell<bool>,
109
110    /// The DOM-side adopted stylesheet list for this [ShadowRoot], including duplicates.
111    /// <https://drafts.csswg.org/cssom/#dom-documentorshadowroot-adoptedstylesheets>
112    adopted_stylesheets: DomRefCell<Vec<Dom<CSSStyleSheet>>>,
113
114    details_name_groups: DomRefCell<Option<DetailsNameGroups>>,
115}
116
117impl ShadowRoot {
118    #[cfg_attr(crown, expect(crown::unrooted_must_root))]
119    fn new_inherited(
120        host: &Element,
121        document: &Document,
122        mode: ShadowRootMode,
123        slot_assignment_mode: SlotAssignmentMode,
124        clonable: bool,
125        is_user_agent_widget: IsUserAgentWidget,
126    ) -> ShadowRoot {
127        let document_fragment = DocumentFragment::new_inherited(document, Some(host));
128        let node = document_fragment.upcast::<Node>();
129        node.set_flag(NodeFlags::IS_IN_SHADOW_TREE, true);
130        node.set_flag(
131            NodeFlags::IS_CONNECTED,
132            host.upcast::<Node>().is_connected(),
133        );
134
135        ShadowRoot {
136            document_fragment,
137            document_or_shadow_root: DocumentOrShadowRoot::new(document.window()),
138            document: Dom::from_ref(document),
139            author_styles: DomRefCell::new(AuthorStyles::new()),
140            stylesheet_list: MutNullableDom::new(None),
141            window: Dom::from_ref(document.window()),
142            mode,
143            slot_assignment_mode,
144            clonable,
145            available_to_element_internals: Cell::new(false),
146            slots: Default::default(),
147            is_user_agent_widget: is_user_agent_widget == IsUserAgentWidget::Yes,
148            declarative: Cell::new(false),
149            serializable: Cell::new(false),
150            delegates_focus: Cell::new(false),
151            adopted_stylesheets: Default::default(),
152            details_name_groups: Default::default(),
153        }
154    }
155
156    pub(crate) fn new(
157        cx: &mut JSContext,
158        host: &Element,
159        document: &Document,
160        mode: ShadowRootMode,
161        slot_assignment_mode: SlotAssignmentMode,
162        clonable: bool,
163        is_user_agent_widget: IsUserAgentWidget,
164    ) -> DomRoot<ShadowRoot> {
165        reflect_dom_object(
166            cx,
167            Box::new(ShadowRoot::new_inherited(
168                host,
169                document,
170                mode,
171                slot_assignment_mode,
172                clonable,
173                is_user_agent_widget,
174            )),
175            document.window(),
176        )
177    }
178
179    pub(crate) fn host_unrooted<'a>(&self, no_gc: &'a NoGC) -> UnrootedDom<'a, Element> {
180        self.upcast::<DocumentFragment>()
181            .host_unrooted(no_gc)
182            .expect("ShadowRoot always has an element as host")
183    }
184
185    pub(crate) fn owner_doc(&self) -> &Document {
186        &self.document
187    }
188
189    pub(crate) fn stylesheet_count(&self) -> usize {
190        self.author_styles.borrow().stylesheets.len()
191    }
192
193    pub(crate) fn stylesheet_at(
194        &self,
195        cx: &mut JSContext,
196        index: usize,
197    ) -> Option<DomRoot<CSSStyleSheet>> {
198        let stylesheets = &self.author_styles.borrow().stylesheets;
199
200        stylesheets
201            .get(index)
202            .and_then(|s| s.owner.get_cssom_object(cx))
203    }
204
205    /// Add a stylesheet owned by `owner_node` to the list of shadow root sheets, in the
206    /// correct tree position. Additionally, ensure that owned stylesheet is inserted before
207    /// any constructed stylesheet.
208    ///
209    /// <https://drafts.csswg.org/cssom/#documentorshadowroot-final-css-style-sheets>
210    #[cfg_attr(crown, expect(crown::unrooted_must_root))] // Owner needs to be rooted already necessarily.
211    pub(crate) fn add_owned_stylesheet(
212        &self,
213        no_gc: &NoGC,
214        owner_node: &Element,
215        sheet: Arc<Stylesheet>,
216    ) {
217        let stylesheets = &mut self.author_styles.borrow_mut().stylesheets;
218
219        // FIXME(stevennovaryo): This is almost identical with the one in Document::add_stylesheet.
220        let insertion_point = stylesheets
221            .iter()
222            .find(|sheet_in_shadow| {
223                match &sheet_in_shadow.owner {
224                    StylesheetSource::Element(other_node) => owner_node
225                        .upcast::<Node>()
226                        .is_before(no_gc, other_node.upcast()),
227                    // Non-constructed stylesheet should be ordered before the
228                    // constructed ones.
229                    StylesheetSource::Constructed(_) => true,
230                }
231            })
232            .cloned();
233
234        DocumentOrShadowRoot::add_stylesheet(
235            StylesheetSource::Element(Dom::from_ref(owner_node)),
236            StylesheetSetRef::Author(stylesheets),
237            sheet,
238            insertion_point,
239            self.document.style_shared_author_lock(),
240        );
241    }
242
243    /// Append a constructed stylesheet to the back of shadow root stylesheet set.
244    #[cfg_attr(crown, expect(crown::unrooted_must_root))]
245    pub(crate) fn append_constructed_stylesheet(&self, cssom_stylesheet: &CSSStyleSheet) {
246        debug_assert!(cssom_stylesheet.is_constructed());
247
248        let stylesheets = &mut self.author_styles.borrow_mut().stylesheets;
249        let sheet = cssom_stylesheet.style_stylesheet().clone();
250
251        let insertion_point = stylesheets.iter().last().cloned();
252
253        DocumentOrShadowRoot::add_stylesheet(
254            StylesheetSource::Constructed(Dom::from_ref(cssom_stylesheet)),
255            StylesheetSetRef::Author(stylesheets),
256            sheet,
257            insertion_point,
258            self.document.style_shared_author_lock(),
259        );
260    }
261
262    /// Remove a stylesheet owned by `owner` from the list of shadow root sheets.
263    #[cfg_attr(crown, expect(crown::unrooted_must_root))] // Owner needs to be rooted already necessarily.
264    pub(crate) fn remove_stylesheet(&self, owner: StylesheetSource, s: &Arc<Stylesheet>) {
265        DocumentOrShadowRoot::remove_stylesheet(
266            owner,
267            s,
268            StylesheetSetRef::Author(&mut self.author_styles.borrow_mut().stylesheets),
269        )
270    }
271
272    pub(crate) fn invalidate_stylesheets(&self, no_gc: &NoGC) {
273        self.document.invalidate_shadow_roots_stylesheets();
274        self.author_styles.borrow_mut().stylesheets.force_dirty();
275        // Mark the host element dirty so a reflow will be performed.
276        self.Host().upcast::<Node>().dirty(no_gc, NodeDamage::Style);
277
278        // Also mark the host element with `RestyleHint::restyle_subtree` so a reflow
279        // can traverse into the shadow tree.
280        let mut restyle = self.document.ensure_pending_restyle(&self.Host());
281        restyle.hint.insert(RestyleHint::restyle_subtree());
282    }
283
284    /// Remove any existing association between the provided id and any elements
285    /// in this shadow tree.
286    pub(crate) fn unregister_element_id(&self, id: &Atom) {
287        self.document_fragment.id_map().remove(id);
288    }
289
290    /// Associate an element present in this shadow tree with the provided id.
291    pub(crate) fn register_element_id(&self, element: &Element, id: &Atom) {
292        self.document_fragment.id_map().add(id, element)
293    }
294
295    pub(crate) fn register_slot(&self, slot: &HTMLSlotElement) {
296        debug!("Registering slot with name={:?}", slot.Name().str());
297
298        let mut slots = self.slots.borrow_mut();
299
300        let slots_with_the_same_name = slots.entry(slot.Name().traced()).or_default();
301
302        // Insert the slot before the first element that comes after it in tree order
303        slots_with_the_same_name.insert_pre_order(slot, self.upcast::<Node>());
304    }
305
306    pub(crate) fn unregister_slot(&self, name: DOMString, slot: &HTMLSlotElement) {
307        debug!("Unregistering slot with name={:?}", name.str());
308
309        let mut slots = self.slots.borrow_mut();
310        let Entry::Occupied(mut entry) = slots.entry(name.traced()) else {
311            panic!("slot is not registered");
312        };
313        entry.get_mut().retain(|s| slot != &**s);
314    }
315
316    /// Find the first slot with the given name among this root's descendants in tree order
317    pub(crate) fn slot_for_name(&self, name: &DOMString) -> Option<DomRoot<HTMLSlotElement>> {
318        self.slots
319            .borrow()
320            .get(name)
321            .and_then(|slots| slots.first())
322            .map(|slot| slot.as_rooted())
323    }
324
325    pub(crate) fn has_slot_descendants(&self) -> bool {
326        !self.slots.borrow().is_empty()
327    }
328
329    pub(crate) fn set_available_to_element_internals(&self, value: bool) {
330        self.available_to_element_internals.set(value);
331    }
332
333    /// <https://dom.spec.whatwg.org/#shadowroot-available-to-element-internals>
334    pub(crate) fn is_available_to_element_internals(&self) -> bool {
335        self.available_to_element_internals.get()
336    }
337
338    pub(crate) fn is_user_agent_widget(&self) -> bool {
339        self.is_user_agent_widget
340    }
341
342    pub(crate) fn set_declarative(&self, declarative: bool) {
343        self.declarative.set(declarative);
344    }
345
346    pub(crate) fn is_declarative(&self) -> bool {
347        self.declarative.get()
348    }
349
350    pub(crate) fn shadow_root_mode(&self) -> ShadowRootMode {
351        self.mode
352    }
353
354    pub(crate) fn set_serializable(&self, serializable: bool) {
355        self.serializable.set(serializable);
356    }
357
358    pub(crate) fn set_delegates_focus(&self, delegates_focus: bool) {
359        self.delegates_focus.set(delegates_focus);
360    }
361
362    pub(crate) fn details_name_groups<'a: 'b, 'b>(
363        &'a self,
364        no_gc: &'b NoGC,
365    ) -> RefMut<'b, DetailsNameGroups> {
366        RefMut::map(
367            self.details_name_groups.safe_borrow_mut(no_gc),
368            |details_name_groups| details_name_groups.get_or_insert_default(),
369        )
370    }
371
372    pub(crate) fn custom_element_registry(&self) -> Option<DomRoot<CustomElementRegistry>> {
373        self.document_or_shadow_root.custom_element_registry()
374    }
375
376    pub(crate) fn set_custom_element_registry(&self, registry: Option<&CustomElementRegistry>) {
377        self.document_or_shadow_root
378            .set_custom_element_registry(registry);
379    }
380}
381
382impl ShadowRootMethods<crate::DomTypeHolder> for ShadowRoot {
383    /// <https://html.spec.whatwg.org/multipage/#dom-document-activeelement>
384    fn GetActiveElement(&self) -> Option<DomRoot<Element>> {
385        self.document_or_shadow_root.active_element(self.upcast())
386    }
387
388    /// <https://dom.spec.whatwg.org/#dom-documentorshadowroot-customelementregistry>
389    fn GetCustomElementRegistry(&self) -> Option<DomRoot<CustomElementRegistry>> {
390        self.custom_element_registry()
391    }
392
393    /// <https://drafts.csswg.org/cssom-view/#dom-document-elementfrompoint>
394    fn ElementFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Option<DomRoot<Element>> {
395        // Return the result of running the retargeting algorithm with context object
396        // and the original result as input.
397        match self.document_or_shadow_root.element_from_point(
398            self.upcast(),
399            x,
400            y,
401            None,
402            self.document.has_browsing_context(),
403        ) {
404            Some(e) => {
405                let retargeted_node = e.upcast::<EventTarget>().retarget(self.upcast());
406                retargeted_node.downcast::<Element>().map(DomRoot::from_ref)
407            },
408            None => None,
409        }
410    }
411
412    /// <https://drafts.csswg.org/cssom-view/#dom-document-elementsfrompoint>
413    fn ElementsFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Vec<DomRoot<Element>> {
414        // Return the result of running the retargeting algorithm with context object
415        // and the original result as input
416        let mut elements = Vec::new();
417        for e in self
418            .document_or_shadow_root
419            .elements_from_point(
420                self.upcast(),
421                x,
422                y,
423                None,
424                self.document.has_browsing_context(),
425            )
426            .iter()
427        {
428            let retargeted_node = e.upcast::<EventTarget>().retarget(self.upcast());
429            if let Some(element) = retargeted_node.downcast::<Element>().map(DomRoot::from_ref) {
430                elements.push(element);
431            }
432        }
433        elements
434    }
435
436    /// <https://dom.spec.whatwg.org/#dom-shadowroot-mode>
437    fn Mode(&self) -> ShadowRootMode {
438        self.mode
439    }
440
441    /// <https://dom.spec.whatwg.org/#dom-delegates-focus>
442    fn DelegatesFocus(&self) -> bool {
443        self.delegates_focus.get()
444    }
445
446    /// <https://dom.spec.whatwg.org/#dom-shadowroot-clonable>
447    fn Clonable(&self) -> bool {
448        self.clonable
449    }
450
451    /// <https://dom.spec.whatwg.org/#dom-serializable>
452    fn Serializable(&self) -> bool {
453        self.serializable.get()
454    }
455
456    /// <https://dom.spec.whatwg.org/#dom-shadowroot-host>
457    fn Host(&self) -> DomRoot<Element> {
458        self.upcast::<DocumentFragment>()
459            .host()
460            .expect("ShadowRoot always has an element as host")
461    }
462
463    /// <https://drafts.csswg.org/cssom/#dom-document-stylesheets>
464    fn StyleSheets(&self, cx: &mut JSContext) -> DomRoot<StyleSheetList> {
465        self.stylesheet_list.or_init(|| {
466            StyleSheetList::new(
467                cx,
468                &self.window,
469                StyleSheetListOwner::ShadowRoot(Dom::from_ref(self)),
470            )
471        })
472    }
473
474    /// <https://html.spec.whatwg.org/multipage/#dom-shadowroot-gethtml>
475    fn GetHTML(&self, cx: &mut JSContext, options: &GetHTMLOptions) -> DOMString {
476        // > ShadowRoot's getHTML(options) method steps are to return the result of HTML fragment serialization
477        // >  algorithm with this, options["serializableShadowRoots"], and options["shadowRoots"].
478        self.upcast::<Node>().html_serialize(
479            cx,
480            TraversalScope::ChildrenOnly(None),
481            options.serializableShadowRoots,
482            options.shadowRoots.clone(),
483        )
484    }
485
486    /// <https://html.spec.whatwg.org/multipage/#dom-shadowroot-innerhtml>
487    fn GetInnerHTML(&self, cx: &mut JSContext) -> Fallible<TrustedHTMLOrNullIsEmptyString> {
488        // ShadowRoot's innerHTML getter steps are to return the result of running fragment serializing
489        // algorithm steps with this and true.
490        self.upcast::<Node>()
491            .fragment_serialization_algorithm(cx, true)
492            .map(TrustedHTMLOrNullIsEmptyString::NullIsEmptyString)
493    }
494
495    /// <https://html.spec.whatwg.org/multipage/#dom-shadowroot-innerhtml>
496    fn SetInnerHTML(
497        &self,
498        cx: &mut JSContext,
499        value: TrustedHTMLOrNullIsEmptyString,
500    ) -> ErrorResult {
501        // Step 1. Let compliantString be the result of invoking the Get Trusted Type compliant string algorithm
502        // with TrustedHTML, this's relevant global object, the given value, "ShadowRoot innerHTML", and "script".
503        let value = TrustedHTML::get_trusted_type_compliant_string(
504            cx,
505            &self.owner_global(),
506            value.convert(),
507            "ShadowRoot innerHTML",
508        )?;
509
510        // Step 2. Let context be this's host.
511        let context = self.Host();
512
513        // Step 3. Let fragment be the result of invoking the fragment parsing algorithm steps with context and
514        // compliantString.
515        //
516        // NOTE: The spec doesn't strictly tell us to bail out here, but
517        // we can't continue if parsing failed
518        let frag = context.parse_fragment(value, cx)?;
519
520        // Step 4. Replace all with fragment within this.
521        Node::replace_all(cx, Some(frag.upcast()), self.upcast());
522        Ok(())
523    }
524
525    /// <https://dom.spec.whatwg.org/#dom-shadowroot-slotassignment>
526    fn SlotAssignment(&self) -> SlotAssignmentMode {
527        self.slot_assignment_mode
528    }
529
530    /// <https://html.spec.whatwg.org/multipage/#dom-shadowroot-sethtmlunsafe>
531    fn SetHTMLUnsafe(
532        &self,
533        cx: &mut JSContext,
534        value: TrustedHTMLOrString,
535        options: &SetHTMLUnsafeOptions,
536    ) -> ErrorResult {
537        // Step 1. Let compliantHTML be the result of invoking the
538        // Get Trusted Type compliant string algorithm with TrustedHTML,
539        // this's relevant global object, html, "ShadowRoot setHTMLUnsafe", and "script".
540        let compliant_html = TrustedHTML::get_trusted_type_compliant_string(
541            cx,
542            &self.owner_global(),
543            value,
544            "ShadowRoot setHTMLUnsafe",
545        )?;
546
547        // Step 2. Set and filter HTML given this, this's shadow host, compliantHTML, options, and
548        // false.
549        Sanitizer::set_and_filter_html(
550            cx,
551            self.upcast(),
552            &self.Host(),
553            compliant_html,
554            options,
555            false,
556        )?;
557
558        Ok(())
559    }
560
561    /// <https://wicg.github.io/sanitizer-api/#dom-shadowroot-sethtml>
562    fn SetHTML(
563        &self,
564        cx: &mut JSContext,
565        html: DOMString,
566        options: &SetHTMLOptions,
567    ) -> ErrorResult {
568        // Step 1. Set and filter HTML using this (as target), this (as context element), html,
569        // options, and true.
570        // NOTE: The specification text is incorrect. We should use this's shadow host as context
571        // element.
572        let target = self.upcast::<Node>();
573        let context_element = self.Host();
574        Sanitizer::set_and_filter_html(cx, target, &context_element, html, options, true)
575    }
576
577    // https://dom.spec.whatwg.org/#dom-shadowroot-onslotchange
578    event_handler!(slotchange, GetOnslotchange, SetOnslotchange);
579
580    /// <https://drafts.csswg.org/cssom/#dom-documentorshadowroot-adoptedstylesheets>
581    fn OnSetAdoptedStyleSheets(
582        &self,
583        cx: &mut JSContext,
584        value: DomRoot<CSSStyleSheet>,
585        index: u32,
586    ) -> ErrorResult {
587        let result = DocumentOrShadowRoot::on_set_adopted_stylesheets(
588            self.adopted_stylesheets.safe_borrow_mut(cx).as_mut(),
589            &value,
590            index,
591            &StyleSheetListOwner::ShadowRoot(Dom::from_ref(self)),
592        );
593
594        if result.is_ok() && self.author_styles.borrow().stylesheets.dirty() {
595            self.invalidate_stylesheets(cx.no_gc());
596        }
597
598        result
599    }
600
601    /// <https://drafts.csswg.org/cssom/#dom-documentorshadowroot-adoptedstylesheets>
602    fn OnDeleteAdoptedStyleSheets(
603        &self,
604        cx: &mut JSContext,
605        _value: DomRoot<CSSStyleSheet>,
606        index: u32,
607    ) -> ErrorResult {
608        let result = DocumentOrShadowRoot::on_delete_adopted_stylesheets(
609            self.adopted_stylesheets.safe_borrow_mut(cx).as_mut(),
610            index,
611            &StyleSheetListOwner::ShadowRoot(Dom::from_ref(self)),
612        );
613
614        if result.is_ok() && self.author_styles.borrow().stylesheets.dirty() {
615            self.invalidate_stylesheets(cx.no_gc());
616        }
617
618        result
619    }
620
621    /// <https://fullscreen.spec.whatwg.org/#dom-document-fullscreenelement>
622    fn GetFullscreenElement(&self) -> Option<DomRoot<Element>> {
623        DocumentOrShadowRoot::get_fullscreen_element(
624            self.upcast::<Node>(),
625            self.document.fullscreen_element(),
626        )
627    }
628}
629
630impl VirtualMethods for ShadowRoot {
631    fn super_type(&self) -> Option<&dyn VirtualMethods> {
632        Some(self.upcast::<DocumentFragment>() as &dyn VirtualMethods)
633    }
634
635    fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
636        if let Some(s) = self.super_type() {
637            s.bind_to_tree(cx, context);
638        }
639
640        // TODO(stevennovaryo): Handle adoptedStylesheet to deal with different
641        //                      constructor document.
642        if context.tree_connected {
643            self.owner_document_unrooted(cx.no_gc())
644                .register_shadow_root(self);
645        }
646
647        let shadow_root = self.upcast::<Node>();
648
649        shadow_root.set_flag(NodeFlags::IS_CONNECTED, context.tree_connected);
650
651        let inner_context = BindContext::new(shadow_root, IsShadowTree::Yes);
652
653        // avoid iterate over the shadow root itself
654        for node in shadow_root.traverse_preorder(ShadowIncluding::No).skip(1) {
655            node.set_flag(NodeFlags::IS_CONNECTED, inner_context.tree_connected);
656
657            // Out-of-document elements never have the descendants flag set
658            debug_assert!(!node.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS));
659            vtable_for(&node).bind_to_tree(cx, &inner_context);
660        }
661    }
662
663    fn unbind_from_tree(&self, cx: &mut JSContext, context: &UnbindContext) {
664        if let Some(s) = self.super_type() {
665            s.unbind_from_tree(cx, context);
666        }
667
668        if context.tree_connected {
669            self.owner_document_unrooted(cx.no_gc())
670                .unregister_shadow_root(self);
671        }
672    }
673}
674
675impl<'dom> LayoutDom<'dom, ShadowRoot> {
676    #[inline]
677    pub(crate) fn get_host_for_layout(self) -> LayoutDom<'dom, Element> {
678        self.upcast::<DocumentFragment>()
679            .shadowroot_host_for_layout()
680    }
681
682    #[inline]
683    #[expect(unsafe_code)]
684    pub(crate) fn get_style_data_for_layout(self) -> &'dom CascadeData {
685        fn is_sync<T: Sync>() {}
686        let _ = is_sync::<CascadeData>;
687        unsafe { &self.unsafe_get().author_styles.borrow_for_layout().data }
688    }
689
690    #[inline]
691    pub(crate) fn is_user_agent_widget(&self) -> bool {
692        self.unsafe_get().is_user_agent_widget()
693    }
694
695    // FIXME(nox): This uses the dreaded borrow_mut_for_layout so this should
696    // probably be revisited.
697    #[inline]
698    #[expect(unsafe_code)]
699    pub(crate) unsafe fn flush_stylesheets_for_layout(
700        self,
701        stylist: &mut Stylist,
702        guard: &SharedRwLockReadGuard,
703    ) {
704        unsafe {
705            debug_assert!(self.upcast::<Node>().get_flag(NodeFlags::IS_CONNECTED));
706        };
707        let author_styles = unsafe { self.unsafe_get().author_styles.borrow_mut_for_layout() };
708        if author_styles.stylesheets.dirty() {
709            author_styles.flush(stylist, guard);
710        }
711    }
712}
713
714#[cfg(feature = "devtools")]
715impl Convert<devtools_traits::ShadowRootMode> for ShadowRootMode {
716    fn convert(self) -> devtools_traits::ShadowRootMode {
717        match self {
718            ShadowRootMode::Open => devtools_traits::ShadowRootMode::Open,
719            ShadowRootMode::Closed => devtools_traits::ShadowRootMode::Closed,
720        }
721    }
722}