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