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