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