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