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::error::{ErrorResult, Fallible};
13use script_bindings::script_runtime::JSContext;
14use servo_arc::Arc;
15use style::author_styles::AuthorStyles;
16use style::dom::TElement;
17use style::invalidation::element::restyle_hints::RestyleHint;
18use style::shared_lock::SharedRwLockReadGuard;
19use style::stylesheets::Stylesheet;
20use style::stylist::{CascadeData, Stylist};
21use stylo_atoms::Atom;
22
23use crate::conversions::Convert;
24use crate::dom::bindings::cell::{DomRefCell, RefMut};
25use crate::dom::bindings::codegen::Bindings::ElementBinding::GetHTMLOptions;
26use crate::dom::bindings::codegen::Bindings::HTMLSlotElementBinding::HTMLSlotElement_Binding::HTMLSlotElementMethods;
27use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::ShadowRoot_Binding::ShadowRootMethods;
28use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{
29    ShadowRootMode, SlotAssignmentMode,
30};
31use crate::dom::bindings::codegen::UnionTypes::{
32    TrustedHTMLOrNullIsEmptyString, TrustedHTMLOrString,
33};
34use crate::dom::bindings::frozenarray::CachedFrozenArray;
35use crate::dom::bindings::inheritance::Castable;
36use crate::dom::bindings::num::Finite;
37use crate::dom::bindings::reflector::reflect_dom_object;
38use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom, ToLayout};
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::trustedhtml::TrustedHTML;
55use crate::dom::types::EventTarget;
56use crate::dom::virtualmethods::{VirtualMethods, vtable_for};
57use crate::dom::window::Window;
58use crate::script_runtime::CanGc;
59use crate::stylesheet_set::StylesheetSetRef;
60
61/// Whether a shadow root hosts an User Agent widget.
62#[derive(JSTraceable, MallocSizeOf, PartialEq)]
63pub(crate) enum IsUserAgentWidget {
64    No,
65    Yes,
66}
67
68/// <https://dom.spec.whatwg.org/#interface-shadowroot>
69#[dom_struct]
70pub(crate) struct ShadowRoot {
71    document_fragment: DocumentFragment,
72    document_or_shadow_root: DocumentOrShadowRoot,
73    document: Dom<Document>,
74    host: Dom<Element>,
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, allow(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);
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            host: Dom::from_ref(host),
140            author_styles: DomRefCell::new(AuthorStyles::new()),
141            stylesheet_list: MutNullableDom::new(None),
142            window: Dom::from_ref(document.window()),
143            mode,
144            slot_assignment_mode,
145            clonable,
146            available_to_element_internals: Cell::new(false),
147            slots: Default::default(),
148            is_user_agent_widget: is_user_agent_widget == IsUserAgentWidget::Yes,
149            declarative: Cell::new(false),
150            serializable: Cell::new(false),
151            delegates_focus: Cell::new(false),
152            adopted_stylesheets: Default::default(),
153            adopted_stylesheets_frozen_types: CachedFrozenArray::new(),
154            details_name_groups: Default::default(),
155        }
156    }
157
158    pub(crate) fn new(
159        host: &Element,
160        document: &Document,
161        mode: ShadowRootMode,
162        slot_assignment_mode: SlotAssignmentMode,
163        clonable: bool,
164        is_user_agent_widget: IsUserAgentWidget,
165        can_gc: CanGc,
166    ) -> DomRoot<ShadowRoot> {
167        reflect_dom_object(
168            Box::new(ShadowRoot::new_inherited(
169                host,
170                document,
171                mode,
172                slot_assignment_mode,
173                clonable,
174                is_user_agent_widget,
175            )),
176            document.window(),
177            can_gc,
178        )
179    }
180
181    pub(crate) fn owner_doc(&self) -> &Document {
182        &self.document
183    }
184
185    pub(crate) fn get_focused_element(&self) -> Option<DomRoot<Element>> {
186        // XXX get retargeted focused element
187        None
188    }
189
190    pub(crate) fn stylesheet_count(&self) -> usize {
191        self.author_styles.borrow().stylesheets.len()
192    }
193
194    pub(crate) fn stylesheet_at(&self, index: usize) -> Option<DomRoot<CSSStyleSheet>> {
195        let stylesheets = &self.author_styles.borrow().stylesheets;
196
197        stylesheets
198            .get(index)
199            .and_then(|s| s.owner.get_cssom_object())
200    }
201
202    /// Add a stylesheet owned by `owner_node` to the list of shadow root sheets, in the
203    /// correct tree position. Additionally, ensure that owned stylesheet is inserted before
204    /// any constructed stylesheet.
205    ///
206    /// <https://drafts.csswg.org/cssom/#documentorshadowroot-final-css-style-sheets>
207    #[cfg_attr(crown, allow(crown::unrooted_must_root))] // Owner needs to be rooted already necessarily.
208    pub(crate) fn add_owned_stylesheet(&self, owner_node: &Element, sheet: Arc<Stylesheet>) {
209        let stylesheets = &mut self.author_styles.borrow_mut().stylesheets;
210
211        // FIXME(stevennovaryo): This is almost identical with the one in Document::add_stylesheet.
212        let insertion_point = stylesheets
213            .iter()
214            .find(|sheet_in_shadow| {
215                match &sheet_in_shadow.owner {
216                    StylesheetSource::Element(other_node) => {
217                        owner_node.upcast::<Node>().is_before(other_node.upcast())
218                    },
219                    // Non-constructed stylesheet should be ordered before the
220                    // constructed ones.
221                    StylesheetSource::Constructed(_) => true,
222                }
223            })
224            .cloned();
225
226        DocumentOrShadowRoot::add_stylesheet(
227            StylesheetSource::Element(Dom::from_ref(owner_node)),
228            StylesheetSetRef::Author(stylesheets),
229            sheet,
230            insertion_point,
231            self.document.style_shared_lock(),
232        );
233    }
234
235    /// Append a constructed stylesheet to the back of shadow root stylesheet set.
236    #[cfg_attr(crown, allow(crown::unrooted_must_root))]
237    pub(crate) fn append_constructed_stylesheet(&self, cssom_stylesheet: &CSSStyleSheet) {
238        debug_assert!(cssom_stylesheet.is_constructed());
239
240        let stylesheets = &mut self.author_styles.borrow_mut().stylesheets;
241        let sheet = cssom_stylesheet.style_stylesheet().clone();
242
243        let insertion_point = stylesheets.iter().last().cloned();
244
245        DocumentOrShadowRoot::add_stylesheet(
246            StylesheetSource::Constructed(Dom::from_ref(cssom_stylesheet)),
247            StylesheetSetRef::Author(stylesheets),
248            sheet,
249            insertion_point,
250            self.document.style_shared_lock(),
251        );
252    }
253
254    /// Remove a stylesheet owned by `owner` from the list of shadow root sheets.
255    #[cfg_attr(crown, allow(crown::unrooted_must_root))] // Owner needs to be rooted already necessarily.
256    pub(crate) fn remove_stylesheet(&self, owner: StylesheetSource, s: &Arc<Stylesheet>) {
257        DocumentOrShadowRoot::remove_stylesheet(
258            owner,
259            s,
260            StylesheetSetRef::Author(&mut self.author_styles.borrow_mut().stylesheets),
261        )
262    }
263
264    pub(crate) fn invalidate_stylesheets(&self) {
265        self.document.invalidate_shadow_roots_stylesheets();
266        self.author_styles.borrow_mut().stylesheets.force_dirty();
267        // Mark the host element dirty so a reflow will be performed.
268        self.host.upcast::<Node>().dirty(NodeDamage::Style);
269
270        // Also mark the host element with `RestyleHint::restyle_subtree` so a reflow
271        // can traverse into the shadow tree.
272        let mut restyle = self.document.ensure_pending_restyle(&self.host);
273        restyle.hint.insert(RestyleHint::restyle_subtree());
274    }
275
276    /// Remove any existing association between the provided id and any elements
277    /// in this shadow tree.
278    pub(crate) fn unregister_element_id(&self, to_unregister: &Element, id: Atom, _can_gc: CanGc) {
279        self.document_or_shadow_root.unregister_named_element(
280            self.document_fragment.id_map(),
281            to_unregister,
282            &id,
283        );
284    }
285
286    /// Associate an element present in this shadow tree with the provided id.
287    pub(crate) fn register_element_id(&self, element: &Element, id: Atom, _can_gc: CanGc) {
288        let root = self
289            .upcast::<Node>()
290            .inclusive_ancestors(ShadowIncluding::No)
291            .last()
292            .unwrap();
293        self.document_or_shadow_root.register_named_element(
294            self.document_fragment.id_map(),
295            element,
296            &id,
297            root,
298        );
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(&self) -> RefMut<'_, DetailsNameGroups> {
369        RefMut::map(
370            self.details_name_groups.borrow_mut(),
371            |details_name_groups| details_name_groups.get_or_insert_default(),
372        )
373    }
374}
375
376impl ShadowRootMethods<crate::DomTypeHolder> for ShadowRoot {
377    /// <https://html.spec.whatwg.org/multipage/#dom-document-activeelement>
378    fn GetActiveElement(&self) -> Option<DomRoot<Element>> {
379        self.document_or_shadow_root
380            .get_active_element(self.get_focused_element(), None, None)
381    }
382
383    /// <https://drafts.csswg.org/cssom-view/#dom-document-elementfrompoint>
384    fn ElementFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Option<DomRoot<Element>> {
385        // Return the result of running the retargeting algorithm with context object
386        // and the original result as input.
387        match self.document_or_shadow_root.element_from_point(
388            x,
389            y,
390            None,
391            self.document.has_browsing_context(),
392        ) {
393            Some(e) => {
394                let retargeted_node = e.upcast::<EventTarget>().retarget(self.upcast());
395                retargeted_node.downcast::<Element>().map(DomRoot::from_ref)
396            },
397            None => None,
398        }
399    }
400
401    /// <https://drafts.csswg.org/cssom-view/#dom-document-elementsfrompoint>
402    fn ElementsFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Vec<DomRoot<Element>> {
403        // Return the result of running the retargeting algorithm with context object
404        // and the original result as input
405        let mut elements = Vec::new();
406        for e in self
407            .document_or_shadow_root
408            .elements_from_point(x, y, None, self.document.has_browsing_context())
409            .iter()
410        {
411            let retargeted_node = e.upcast::<EventTarget>().retarget(self.upcast());
412            if let Some(element) = retargeted_node.downcast::<Element>().map(DomRoot::from_ref) {
413                elements.push(element);
414            }
415        }
416        elements
417    }
418
419    /// <https://dom.spec.whatwg.org/#dom-shadowroot-mode>
420    fn Mode(&self) -> ShadowRootMode {
421        self.mode
422    }
423
424    /// <https://dom.spec.whatwg.org/#dom-delegates-focus>
425    fn DelegatesFocus(&self) -> bool {
426        self.delegates_focus.get()
427    }
428
429    /// <https://dom.spec.whatwg.org/#dom-shadowroot-clonable>
430    fn Clonable(&self) -> bool {
431        self.clonable
432    }
433
434    /// <https://dom.spec.whatwg.org/#dom-serializable>
435    fn Serializable(&self) -> bool {
436        self.serializable.get()
437    }
438
439    /// <https://dom.spec.whatwg.org/#dom-shadowroot-host>
440    fn Host(&self) -> DomRoot<Element> {
441        self.host.as_rooted()
442    }
443
444    /// <https://drafts.csswg.org/cssom/#dom-document-stylesheets>
445    fn StyleSheets(&self) -> DomRoot<StyleSheetList> {
446        self.stylesheet_list.or_init(|| {
447            StyleSheetList::new(
448                &self.window,
449                StyleSheetListOwner::ShadowRoot(Dom::from_ref(self)),
450                CanGc::note(),
451            )
452        })
453    }
454
455    /// <https://html.spec.whatwg.org/multipage/#dom-shadowroot-gethtml>
456    fn GetHTML(&self, options: &GetHTMLOptions, can_gc: CanGc) -> DOMString {
457        // > ShadowRoot's getHTML(options) method steps are to return the result of HTML fragment serialization
458        // >  algorithm with this, options["serializableShadowRoots"], and options["shadowRoots"].
459        self.upcast::<Node>().html_serialize(
460            TraversalScope::ChildrenOnly(None),
461            options.serializableShadowRoots,
462            options.shadowRoots.clone(),
463            can_gc,
464        )
465    }
466
467    /// <https://html.spec.whatwg.org/multipage/#dom-shadowroot-innerhtml>
468    fn GetInnerHTML(&self, can_gc: CanGc) -> Fallible<TrustedHTMLOrNullIsEmptyString> {
469        // ShadowRoot's innerHTML getter steps are to return the result of running fragment serializing
470        // algorithm steps with this and true.
471        self.upcast::<Node>()
472            .fragment_serialization_algorithm(true, can_gc)
473            .map(TrustedHTMLOrNullIsEmptyString::NullIsEmptyString)
474    }
475
476    /// <https://html.spec.whatwg.org/multipage/#dom-shadowroot-innerhtml>
477    fn SetInnerHTML(&self, value: TrustedHTMLOrNullIsEmptyString, can_gc: CanGc) -> ErrorResult {
478        // Step 1. Let compliantString be the result of invoking the Get Trusted Type compliant string algorithm
479        // with TrustedHTML, this's relevant global object, the given value, "ShadowRoot innerHTML", and "script".
480        let value = TrustedHTML::get_trusted_script_compliant_string(
481            &self.owner_global(),
482            value.convert(),
483            "ShadowRoot innerHTML",
484            can_gc,
485        )?;
486
487        // Step 2. Let context be this's host.
488        let context = self.Host();
489
490        // Step 3. Let fragment be the result of invoking the fragment parsing algorithm steps with context and
491        // compliantString.
492        //
493        // NOTE: The spec doesn't strictly tell us to bail out here, but
494        // we can't continue if parsing failed
495        let frag = context.parse_fragment(value, can_gc)?;
496
497        // Step 4. Replace all with fragment within this.
498        Node::replace_all(Some(frag.upcast()), self.upcast(), can_gc);
499        Ok(())
500    }
501
502    /// <https://dom.spec.whatwg.org/#dom-shadowroot-slotassignment>
503    fn SlotAssignment(&self) -> SlotAssignmentMode {
504        self.slot_assignment_mode
505    }
506
507    /// <https://html.spec.whatwg.org/multipage/#dom-shadowroot-sethtmlunsafe>
508    fn SetHTMLUnsafe(&self, value: TrustedHTMLOrString, can_gc: CanGc) -> ErrorResult {
509        // Step 1. Let compliantHTML be the result of invoking the
510        // Get Trusted Type compliant string algorithm with TrustedHTML,
511        // this's relevant global object, html, "ShadowRoot setHTMLUnsafe", and "script".
512        let value = TrustedHTML::get_trusted_script_compliant_string(
513            &self.owner_global(),
514            value,
515            "ShadowRoot setHTMLUnsafe",
516            can_gc,
517        )?;
518        // Step 2. Unsafely set HTMl given this, this's shadow host, and complaintHTML
519        let target = self.upcast::<Node>();
520        let context_element = self.Host();
521
522        Node::unsafely_set_html(target, &context_element, value, can_gc);
523        Ok(())
524    }
525
526    // https://dom.spec.whatwg.org/#dom-shadowroot-onslotchange
527    event_handler!(onslotchange, GetOnslotchange, SetOnslotchange);
528
529    /// <https://drafts.csswg.org/cssom/#dom-documentorshadowroot-adoptedstylesheets>
530    fn AdoptedStyleSheets(&self, context: JSContext, can_gc: CanGc, retval: MutableHandleValue) {
531        self.adopted_stylesheets_frozen_types.get_or_init(
532            || {
533                self.adopted_stylesheets
534                    .borrow()
535                    .clone()
536                    .iter()
537                    .map(|sheet| sheet.as_rooted())
538                    .collect()
539            },
540            context,
541            retval,
542            can_gc,
543        );
544    }
545
546    /// <https://drafts.csswg.org/cssom/#dom-documentorshadowroot-adoptedstylesheets>
547    fn SetAdoptedStyleSheets(
548        &self,
549        context: JSContext,
550        val: HandleValue,
551        can_gc: CanGc,
552    ) -> ErrorResult {
553        let result = DocumentOrShadowRoot::set_adopted_stylesheet_from_jsval(
554            context,
555            self.adopted_stylesheets.borrow_mut().as_mut(),
556            val,
557            &StyleSheetListOwner::ShadowRoot(Dom::from_ref(self)),
558            can_gc,
559        );
560
561        // If update is successful, clear the FrozenArray cache.
562        if result.is_ok() {
563            self.adopted_stylesheets_frozen_types.clear();
564        }
565
566        result
567    }
568}
569
570impl VirtualMethods for ShadowRoot {
571    fn super_type(&self) -> Option<&dyn VirtualMethods> {
572        Some(self.upcast::<DocumentFragment>() as &dyn VirtualMethods)
573    }
574
575    fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
576        if let Some(s) = self.super_type() {
577            s.bind_to_tree(context, can_gc);
578        }
579
580        // TODO(stevennovaryo): Handle adoptedStylesheet to deal with different
581        //                      constructor document.
582        if context.tree_connected {
583            let document = self.owner_document();
584            document.register_shadow_root(self);
585        }
586
587        let shadow_root = self.upcast::<Node>();
588
589        shadow_root.set_flag(NodeFlags::IS_CONNECTED, context.tree_connected);
590
591        let context = BindContext::new(shadow_root, IsShadowTree::Yes);
592
593        // avoid iterate over the shadow root itself
594        for node in shadow_root.traverse_preorder(ShadowIncluding::Yes).skip(1) {
595            node.set_flag(NodeFlags::IS_CONNECTED, context.tree_connected);
596
597            // Out-of-document elements never have the descendants flag set
598            debug_assert!(!node.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS));
599            vtable_for(&node).bind_to_tree(&context, can_gc);
600        }
601    }
602
603    fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
604        if let Some(s) = self.super_type() {
605            s.unbind_from_tree(context, can_gc);
606        }
607
608        if context.tree_connected {
609            let document = self.owner_document();
610            document.unregister_shadow_root(self);
611        }
612    }
613}
614
615#[expect(unsafe_code)]
616pub(crate) trait LayoutShadowRootHelpers<'dom> {
617    fn get_host_for_layout(self) -> LayoutDom<'dom, Element>;
618    fn get_style_data_for_layout(self) -> &'dom CascadeData;
619    fn is_ua_widget(&self) -> bool;
620    unsafe fn flush_stylesheets<E: TElement>(
621        self,
622        stylist: &mut Stylist,
623        guard: &SharedRwLockReadGuard,
624    );
625}
626
627impl<'dom> LayoutShadowRootHelpers<'dom> for LayoutDom<'dom, ShadowRoot> {
628    #[inline]
629    #[expect(unsafe_code)]
630    fn get_host_for_layout(self) -> LayoutDom<'dom, Element> {
631        unsafe { self.unsafe_get().host.to_layout() }
632    }
633
634    #[inline]
635    #[expect(unsafe_code)]
636    fn get_style_data_for_layout(self) -> &'dom CascadeData {
637        fn is_sync<T: Sync>() {}
638        let _ = is_sync::<CascadeData>;
639        unsafe { &self.unsafe_get().author_styles.borrow_for_layout().data }
640    }
641
642    #[inline]
643    fn is_ua_widget(&self) -> bool {
644        self.unsafe_get().is_user_agent_widget()
645    }
646
647    // FIXME(nox): This uses the dreaded borrow_mut_for_layout so this should
648    // probably be revisited.
649    #[inline]
650    #[expect(unsafe_code)]
651    unsafe fn flush_stylesheets<E: TElement>(
652        self,
653        stylist: &mut Stylist,
654        guard: &SharedRwLockReadGuard,
655    ) {
656        let author_styles = unsafe { self.unsafe_get().author_styles.borrow_mut_for_layout() };
657        if author_styles.stylesheets.dirty() {
658            author_styles.flush::<E>(stylist, guard);
659        }
660    }
661}
662
663impl Convert<devtools_traits::ShadowRootMode> for ShadowRootMode {
664    fn convert(self) -> devtools_traits::ShadowRootMode {
665        match self {
666            ShadowRootMode::Open => devtools_traits::ShadowRootMode::Open,
667            ShadowRootMode::Closed => devtools_traits::ShadowRootMode::Closed,
668        }
669    }
670}