Skip to main content

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