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