Files
servo/components/script/dom/shadowroot.rs
Tim van der Lippe e8aa7d51ba script: Set host for template content (#42276)
A template element should set the host of a DocumentFragment. However,
it didn't have a host yet. That's because ShadowRoot declares a host
attribute which returns Element, but its property is declared on
DocumentFragment.

Therefore, define the host on DocumentFragment instead and use it in all
relevant points for shadow roots. Then, update the pre-insert validity
check to use the correct host-including variant of inclusive ancestors.
Lastly, set the host of the template to wire it all up.

---------

Signed-off-by: Tim van der Lippe <tvanderlippe@gmail.com>
2026-02-02 08:25:54 +00:00

685 lines
25 KiB
Rust

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::cell::Cell;
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use dom_struct::dom_struct;
use html5ever::serialize::TraversalScope;
use js::rust::{HandleValue, MutableHandleValue};
use script_bindings::error::{ErrorResult, Fallible};
use script_bindings::script_runtime::JSContext;
use servo_arc::Arc;
use style::author_styles::AuthorStyles;
use style::dom::TElement;
use style::invalidation::element::restyle_hints::RestyleHint;
use style::shared_lock::SharedRwLockReadGuard;
use style::stylesheets::Stylesheet;
use style::stylist::{CascadeData, Stylist};
use stylo_atoms::Atom;
use crate::conversions::Convert;
use crate::dom::bindings::cell::{DomRefCell, RefMut};
use crate::dom::bindings::codegen::Bindings::ElementBinding::GetHTMLOptions;
use crate::dom::bindings::codegen::Bindings::HTMLSlotElementBinding::HTMLSlotElement_Binding::HTMLSlotElementMethods;
use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::ShadowRoot_Binding::ShadowRootMethods;
use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{
ShadowRootMode, SlotAssignmentMode,
};
use crate::dom::bindings::codegen::UnionTypes::{
TrustedHTMLOrNullIsEmptyString, TrustedHTMLOrString,
};
use crate::dom::bindings::frozenarray::CachedFrozenArray;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::num::Finite;
use crate::dom::bindings::reflector::reflect_dom_object;
use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom};
use crate::dom::bindings::str::DOMString;
use crate::dom::css::cssstylesheet::CSSStyleSheet;
use crate::dom::css::stylesheetlist::{StyleSheetList, StyleSheetListOwner};
use crate::dom::document::Document;
use crate::dom::documentfragment::{DocumentFragment, LayoutDocumentFragmentHelpers};
use crate::dom::documentorshadowroot::{
DocumentOrShadowRoot, ServoStylesheetInDocument, StylesheetSource,
};
use crate::dom::element::Element;
use crate::dom::html::htmlslotelement::HTMLSlotElement;
use crate::dom::htmldetailselement::DetailsNameGroups;
use crate::dom::node::{
BindContext, IsShadowTree, Node, NodeDamage, NodeFlags, NodeTraits, ShadowIncluding,
UnbindContext, VecPreOrderInsertionHelper,
};
use crate::dom::trustedhtml::TrustedHTML;
use crate::dom::types::EventTarget;
use crate::dom::virtualmethods::{VirtualMethods, vtable_for};
use crate::dom::window::Window;
use crate::script_runtime::CanGc;
use crate::stylesheet_set::StylesheetSetRef;
/// Whether a shadow root hosts an User Agent widget.
#[derive(JSTraceable, MallocSizeOf, PartialEq)]
pub(crate) enum IsUserAgentWidget {
No,
Yes,
}
/// <https://dom.spec.whatwg.org/#interface-shadowroot>
#[dom_struct]
pub(crate) struct ShadowRoot {
document_fragment: DocumentFragment,
document_or_shadow_root: DocumentOrShadowRoot,
document: Dom<Document>,
/// List of author styles associated with nodes in this shadow tree.
#[custom_trace]
author_styles: DomRefCell<AuthorStyles<ServoStylesheetInDocument>>,
stylesheet_list: MutNullableDom<StyleSheetList>,
window: Dom<Window>,
/// <https://dom.spec.whatwg.org/#dom-shadowroot-mode>
mode: ShadowRootMode,
/// <https://dom.spec.whatwg.org/#dom-shadowroot-slotassignment>
slot_assignment_mode: SlotAssignmentMode,
/// <https://dom.spec.whatwg.org/#dom-shadowroot-clonable>
clonable: bool,
/// <https://dom.spec.whatwg.org/#shadowroot-available-to-element-internals>
available_to_element_internals: Cell<bool>,
slots: DomRefCell<HashMap<DOMString, Vec<Dom<HTMLSlotElement>>>>,
is_user_agent_widget: bool,
/// <https://dom.spec.whatwg.org/#shadowroot-declarative>
declarative: Cell<bool>,
/// <https://dom.spec.whatwg.org/#shadowroot-serializable>
serializable: Cell<bool>,
/// <https://dom.spec.whatwg.org/#shadowroot-delegates-focus>
delegates_focus: Cell<bool>,
/// The constructed stylesheet that is adopted by this [ShadowRoot].
/// <https://drafts.csswg.org/cssom/#dom-documentorshadowroot-adoptedstylesheets>
adopted_stylesheets: DomRefCell<Vec<Dom<CSSStyleSheet>>>,
/// Cached frozen array of [`Self::adopted_stylesheets`]
#[ignore_malloc_size_of = "mozjs"]
adopted_stylesheets_frozen_types: CachedFrozenArray,
details_name_groups: DomRefCell<Option<DetailsNameGroups>>,
}
impl ShadowRoot {
#[cfg_attr(crown, expect(crown::unrooted_must_root))]
fn new_inherited(
host: &Element,
document: &Document,
mode: ShadowRootMode,
slot_assignment_mode: SlotAssignmentMode,
clonable: bool,
is_user_agent_widget: IsUserAgentWidget,
) -> ShadowRoot {
let document_fragment = DocumentFragment::new_inherited(document, Some(host));
let node = document_fragment.upcast::<Node>();
node.set_flag(NodeFlags::IS_IN_SHADOW_TREE, true);
node.set_flag(
NodeFlags::IS_CONNECTED,
host.upcast::<Node>().is_connected(),
);
ShadowRoot {
document_fragment,
document_or_shadow_root: DocumentOrShadowRoot::new(document.window()),
document: Dom::from_ref(document),
author_styles: DomRefCell::new(AuthorStyles::new()),
stylesheet_list: MutNullableDom::new(None),
window: Dom::from_ref(document.window()),
mode,
slot_assignment_mode,
clonable,
available_to_element_internals: Cell::new(false),
slots: Default::default(),
is_user_agent_widget: is_user_agent_widget == IsUserAgentWidget::Yes,
declarative: Cell::new(false),
serializable: Cell::new(false),
delegates_focus: Cell::new(false),
adopted_stylesheets: Default::default(),
adopted_stylesheets_frozen_types: CachedFrozenArray::new(),
details_name_groups: Default::default(),
}
}
pub(crate) fn new(
host: &Element,
document: &Document,
mode: ShadowRootMode,
slot_assignment_mode: SlotAssignmentMode,
clonable: bool,
is_user_agent_widget: IsUserAgentWidget,
can_gc: CanGc,
) -> DomRoot<ShadowRoot> {
reflect_dom_object(
Box::new(ShadowRoot::new_inherited(
host,
document,
mode,
slot_assignment_mode,
clonable,
is_user_agent_widget,
)),
document.window(),
can_gc,
)
}
pub(crate) fn owner_doc(&self) -> &Document {
&self.document
}
pub(crate) fn get_focused_element(&self) -> Option<DomRoot<Element>> {
// XXX get retargeted focused element
None
}
pub(crate) fn stylesheet_count(&self) -> usize {
self.author_styles.borrow().stylesheets.len()
}
pub(crate) fn stylesheet_at(&self, index: usize) -> Option<DomRoot<CSSStyleSheet>> {
let stylesheets = &self.author_styles.borrow().stylesheets;
stylesheets
.get(index)
.and_then(|s| s.owner.get_cssom_object())
}
/// Add a stylesheet owned by `owner_node` to the list of shadow root sheets, in the
/// correct tree position. Additionally, ensure that owned stylesheet is inserted before
/// any constructed stylesheet.
///
/// <https://drafts.csswg.org/cssom/#documentorshadowroot-final-css-style-sheets>
#[cfg_attr(crown, expect(crown::unrooted_must_root))] // Owner needs to be rooted already necessarily.
pub(crate) fn add_owned_stylesheet(&self, owner_node: &Element, sheet: Arc<Stylesheet>) {
let stylesheets = &mut self.author_styles.borrow_mut().stylesheets;
// FIXME(stevennovaryo): This is almost identical with the one in Document::add_stylesheet.
let insertion_point = stylesheets
.iter()
.find(|sheet_in_shadow| {
match &sheet_in_shadow.owner {
StylesheetSource::Element(other_node) => {
owner_node.upcast::<Node>().is_before(other_node.upcast())
},
// Non-constructed stylesheet should be ordered before the
// constructed ones.
StylesheetSource::Constructed(_) => true,
}
})
.cloned();
if self.document.has_browsing_context() {
let document_context = self.window.web_font_context();
self.window
.layout_mut()
.load_web_fonts_from_stylesheet(&sheet, &document_context);
}
DocumentOrShadowRoot::add_stylesheet(
StylesheetSource::Element(Dom::from_ref(owner_node)),
StylesheetSetRef::Author(stylesheets),
sheet,
insertion_point,
self.document.style_shared_lock(),
);
}
/// Append a constructed stylesheet to the back of shadow root stylesheet set.
#[cfg_attr(crown, expect(crown::unrooted_must_root))]
pub(crate) fn append_constructed_stylesheet(&self, cssom_stylesheet: &CSSStyleSheet) {
debug_assert!(cssom_stylesheet.is_constructed());
let stylesheets = &mut self.author_styles.borrow_mut().stylesheets;
let sheet = cssom_stylesheet.style_stylesheet().clone();
let insertion_point = stylesheets.iter().last().cloned();
if self.document.has_browsing_context() {
let document_context = self.window.web_font_context();
self.window
.layout_mut()
.load_web_fonts_from_stylesheet(&sheet, &document_context);
}
DocumentOrShadowRoot::add_stylesheet(
StylesheetSource::Constructed(Dom::from_ref(cssom_stylesheet)),
StylesheetSetRef::Author(stylesheets),
sheet,
insertion_point,
self.document.style_shared_lock(),
);
}
/// Remove a stylesheet owned by `owner` from the list of shadow root sheets.
#[cfg_attr(crown, expect(crown::unrooted_must_root))] // Owner needs to be rooted already necessarily.
pub(crate) fn remove_stylesheet(&self, owner: StylesheetSource, s: &Arc<Stylesheet>) {
DocumentOrShadowRoot::remove_stylesheet(
owner,
s,
StylesheetSetRef::Author(&mut self.author_styles.borrow_mut().stylesheets),
)
}
pub(crate) fn invalidate_stylesheets(&self) {
self.document.invalidate_shadow_roots_stylesheets();
self.author_styles.borrow_mut().stylesheets.force_dirty();
// Mark the host element dirty so a reflow will be performed.
self.Host().upcast::<Node>().dirty(NodeDamage::Style);
// Also mark the host element with `RestyleHint::restyle_subtree` so a reflow
// can traverse into the shadow tree.
let mut restyle = self.document.ensure_pending_restyle(&self.Host());
restyle.hint.insert(RestyleHint::restyle_subtree());
}
/// Remove any existing association between the provided id and any elements
/// in this shadow tree.
pub(crate) fn unregister_element_id(&self, to_unregister: &Element, id: Atom, _can_gc: CanGc) {
self.document_or_shadow_root.unregister_named_element(
self.document_fragment.id_map(),
to_unregister,
&id,
);
}
/// Associate an element present in this shadow tree with the provided id.
pub(crate) fn register_element_id(&self, element: &Element, id: Atom, _can_gc: CanGc) {
let root = self
.upcast::<Node>()
.inclusive_ancestors(ShadowIncluding::No)
.last()
.unwrap();
self.document_or_shadow_root.register_named_element(
self.document_fragment.id_map(),
element,
&id,
root,
);
}
pub(crate) fn register_slot(&self, slot: &HTMLSlotElement) {
debug!("Registering slot with name={:?}", slot.Name().str());
let mut slots = self.slots.borrow_mut();
let slots_with_the_same_name = slots.entry(slot.Name()).or_default();
// Insert the slot before the first element that comes after it in tree order
slots_with_the_same_name.insert_pre_order(slot, self.upcast::<Node>());
}
pub(crate) fn unregister_slot(&self, name: DOMString, slot: &HTMLSlotElement) {
debug!("Unregistering slot with name={:?}", name.str());
let mut slots = self.slots.borrow_mut();
let Entry::Occupied(mut entry) = slots.entry(name) else {
panic!("slot is not registered");
};
entry.get_mut().retain(|s| slot != &**s);
}
/// Find the first slot with the given name among this root's descendants in tree order
pub(crate) fn slot_for_name(&self, name: &DOMString) -> Option<DomRoot<HTMLSlotElement>> {
self.slots
.borrow()
.get(name)
.and_then(|slots| slots.first())
.map(|slot| slot.as_rooted())
}
pub(crate) fn has_slot_descendants(&self) -> bool {
!self.slots.borrow().is_empty()
}
pub(crate) fn set_available_to_element_internals(&self, value: bool) {
self.available_to_element_internals.set(value);
}
/// <https://dom.spec.whatwg.org/#shadowroot-available-to-element-internals>
pub(crate) fn is_available_to_element_internals(&self) -> bool {
self.available_to_element_internals.get()
}
pub(crate) fn is_user_agent_widget(&self) -> bool {
self.is_user_agent_widget
}
pub(crate) fn set_declarative(&self, declarative: bool) {
self.declarative.set(declarative);
}
pub(crate) fn is_declarative(&self) -> bool {
self.declarative.get()
}
pub(crate) fn shadow_root_mode(&self) -> ShadowRootMode {
self.mode
}
pub(crate) fn set_serializable(&self, serializable: bool) {
self.serializable.set(serializable);
}
pub(crate) fn set_delegates_focus(&self, delegates_focus: bool) {
self.delegates_focus.set(delegates_focus);
}
pub(crate) fn details_name_groups(&self) -> RefMut<'_, DetailsNameGroups> {
RefMut::map(
self.details_name_groups.borrow_mut(),
|details_name_groups| details_name_groups.get_or_insert_default(),
)
}
}
impl ShadowRootMethods<crate::DomTypeHolder> for ShadowRoot {
/// <https://html.spec.whatwg.org/multipage/#dom-document-activeelement>
fn GetActiveElement(&self) -> Option<DomRoot<Element>> {
self.document_or_shadow_root
.get_active_element(self.get_focused_element(), None, None)
}
/// <https://drafts.csswg.org/cssom-view/#dom-document-elementfrompoint>
fn ElementFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Option<DomRoot<Element>> {
// Return the result of running the retargeting algorithm with context object
// and the original result as input.
match self.document_or_shadow_root.element_from_point(
x,
y,
None,
self.document.has_browsing_context(),
) {
Some(e) => {
let retargeted_node = e.upcast::<EventTarget>().retarget(self.upcast());
retargeted_node.downcast::<Element>().map(DomRoot::from_ref)
},
None => None,
}
}
/// <https://drafts.csswg.org/cssom-view/#dom-document-elementsfrompoint>
fn ElementsFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Vec<DomRoot<Element>> {
// Return the result of running the retargeting algorithm with context object
// and the original result as input
let mut elements = Vec::new();
for e in self
.document_or_shadow_root
.elements_from_point(x, y, None, self.document.has_browsing_context())
.iter()
{
let retargeted_node = e.upcast::<EventTarget>().retarget(self.upcast());
if let Some(element) = retargeted_node.downcast::<Element>().map(DomRoot::from_ref) {
elements.push(element);
}
}
elements
}
/// <https://dom.spec.whatwg.org/#dom-shadowroot-mode>
fn Mode(&self) -> ShadowRootMode {
self.mode
}
/// <https://dom.spec.whatwg.org/#dom-delegates-focus>
fn DelegatesFocus(&self) -> bool {
self.delegates_focus.get()
}
/// <https://dom.spec.whatwg.org/#dom-shadowroot-clonable>
fn Clonable(&self) -> bool {
self.clonable
}
/// <https://dom.spec.whatwg.org/#dom-serializable>
fn Serializable(&self) -> bool {
self.serializable.get()
}
/// <https://dom.spec.whatwg.org/#dom-shadowroot-host>
fn Host(&self) -> DomRoot<Element> {
self.upcast::<DocumentFragment>()
.host()
.expect("ShadowRoot always has an element as host")
}
/// <https://drafts.csswg.org/cssom/#dom-document-stylesheets>
fn StyleSheets(&self) -> DomRoot<StyleSheetList> {
self.stylesheet_list.or_init(|| {
StyleSheetList::new(
&self.window,
StyleSheetListOwner::ShadowRoot(Dom::from_ref(self)),
CanGc::note(),
)
})
}
/// <https://html.spec.whatwg.org/multipage/#dom-shadowroot-gethtml>
fn GetHTML(&self, options: &GetHTMLOptions, can_gc: CanGc) -> DOMString {
// > ShadowRoot's getHTML(options) method steps are to return the result of HTML fragment serialization
// > algorithm with this, options["serializableShadowRoots"], and options["shadowRoots"].
self.upcast::<Node>().html_serialize(
TraversalScope::ChildrenOnly(None),
options.serializableShadowRoots,
options.shadowRoots.clone(),
can_gc,
)
}
/// <https://html.spec.whatwg.org/multipage/#dom-shadowroot-innerhtml>
fn GetInnerHTML(&self, can_gc: CanGc) -> Fallible<TrustedHTMLOrNullIsEmptyString> {
// ShadowRoot's innerHTML getter steps are to return the result of running fragment serializing
// algorithm steps with this and true.
self.upcast::<Node>()
.fragment_serialization_algorithm(true, can_gc)
.map(TrustedHTMLOrNullIsEmptyString::NullIsEmptyString)
}
/// <https://html.spec.whatwg.org/multipage/#dom-shadowroot-innerhtml>
fn SetInnerHTML(&self, value: TrustedHTMLOrNullIsEmptyString, can_gc: CanGc) -> ErrorResult {
// Step 1. Let compliantString be the result of invoking the Get Trusted Type compliant string algorithm
// with TrustedHTML, this's relevant global object, the given value, "ShadowRoot innerHTML", and "script".
let value = TrustedHTML::get_trusted_script_compliant_string(
&self.owner_global(),
value.convert(),
"ShadowRoot innerHTML",
can_gc,
)?;
// Step 2. Let context be this's host.
let context = self.Host();
// Step 3. Let fragment be the result of invoking the fragment parsing algorithm steps with context and
// compliantString.
//
// NOTE: The spec doesn't strictly tell us to bail out here, but
// we can't continue if parsing failed
let frag = context.parse_fragment(value, can_gc)?;
// Step 4. Replace all with fragment within this.
Node::replace_all(Some(frag.upcast()), self.upcast(), can_gc);
Ok(())
}
/// <https://dom.spec.whatwg.org/#dom-shadowroot-slotassignment>
fn SlotAssignment(&self) -> SlotAssignmentMode {
self.slot_assignment_mode
}
/// <https://html.spec.whatwg.org/multipage/#dom-shadowroot-sethtmlunsafe>
fn SetHTMLUnsafe(&self, value: TrustedHTMLOrString, can_gc: CanGc) -> ErrorResult {
// Step 1. Let compliantHTML be the result of invoking the
// Get Trusted Type compliant string algorithm with TrustedHTML,
// this's relevant global object, html, "ShadowRoot setHTMLUnsafe", and "script".
let value = TrustedHTML::get_trusted_script_compliant_string(
&self.owner_global(),
value,
"ShadowRoot setHTMLUnsafe",
can_gc,
)?;
// Step 2. Unsafely set HTMl given this, this's shadow host, and complaintHTML
let target = self.upcast::<Node>();
let context_element = self.Host();
Node::unsafely_set_html(target, &context_element, value, can_gc);
Ok(())
}
// https://dom.spec.whatwg.org/#dom-shadowroot-onslotchange
event_handler!(onslotchange, GetOnslotchange, SetOnslotchange);
/// <https://drafts.csswg.org/cssom/#dom-documentorshadowroot-adoptedstylesheets>
fn AdoptedStyleSheets(&self, context: JSContext, can_gc: CanGc, retval: MutableHandleValue) {
self.adopted_stylesheets_frozen_types.get_or_init(
|| {
self.adopted_stylesheets
.borrow()
.clone()
.iter()
.map(|sheet| sheet.as_rooted())
.collect()
},
context,
retval,
can_gc,
);
}
/// <https://drafts.csswg.org/cssom/#dom-documentorshadowroot-adoptedstylesheets>
fn SetAdoptedStyleSheets(
&self,
context: JSContext,
val: HandleValue,
can_gc: CanGc,
) -> ErrorResult {
let result = DocumentOrShadowRoot::set_adopted_stylesheet_from_jsval(
context,
self.adopted_stylesheets.borrow_mut().as_mut(),
val,
&StyleSheetListOwner::ShadowRoot(Dom::from_ref(self)),
can_gc,
);
// If update is successful, clear the FrozenArray cache.
if result.is_ok() {
self.adopted_stylesheets_frozen_types.clear();
}
result
}
}
impl VirtualMethods for ShadowRoot {
fn super_type(&self) -> Option<&dyn VirtualMethods> {
Some(self.upcast::<DocumentFragment>() as &dyn VirtualMethods)
}
fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
if let Some(s) = self.super_type() {
s.bind_to_tree(context, can_gc);
}
// TODO(stevennovaryo): Handle adoptedStylesheet to deal with different
// constructor document.
if context.tree_connected {
let document = self.owner_document();
document.register_shadow_root(self);
}
let shadow_root = self.upcast::<Node>();
shadow_root.set_flag(NodeFlags::IS_CONNECTED, context.tree_connected);
let context = BindContext::new(shadow_root, IsShadowTree::Yes);
// avoid iterate over the shadow root itself
for node in shadow_root.traverse_preorder(ShadowIncluding::Yes).skip(1) {
node.set_flag(NodeFlags::IS_CONNECTED, context.tree_connected);
// Out-of-document elements never have the descendants flag set
debug_assert!(!node.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS));
vtable_for(&node).bind_to_tree(&context, can_gc);
}
}
fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
if let Some(s) = self.super_type() {
s.unbind_from_tree(context, can_gc);
}
if context.tree_connected {
let document = self.owner_document();
document.unregister_shadow_root(self);
}
}
}
#[expect(unsafe_code)]
pub(crate) trait LayoutShadowRootHelpers<'dom> {
fn get_host_for_layout(self) -> LayoutDom<'dom, Element>;
fn get_style_data_for_layout(self) -> &'dom CascadeData;
fn is_ua_widget(&self) -> bool;
unsafe fn flush_stylesheets<E: TElement>(
self,
stylist: &mut Stylist,
guard: &SharedRwLockReadGuard,
);
}
impl<'dom> LayoutShadowRootHelpers<'dom> for LayoutDom<'dom, ShadowRoot> {
#[inline]
fn get_host_for_layout(self) -> LayoutDom<'dom, Element> {
self.upcast::<DocumentFragment>()
.shadowroot_host_for_layout()
}
#[inline]
#[expect(unsafe_code)]
fn get_style_data_for_layout(self) -> &'dom CascadeData {
fn is_sync<T: Sync>() {}
let _ = is_sync::<CascadeData>;
unsafe { &self.unsafe_get().author_styles.borrow_for_layout().data }
}
#[inline]
fn is_ua_widget(&self) -> bool {
self.unsafe_get().is_user_agent_widget()
}
// FIXME(nox): This uses the dreaded borrow_mut_for_layout so this should
// probably be revisited.
#[inline]
#[expect(unsafe_code)]
unsafe fn flush_stylesheets<E: TElement>(
self,
stylist: &mut Stylist,
guard: &SharedRwLockReadGuard,
) {
let author_styles = unsafe { self.unsafe_get().author_styles.borrow_mut_for_layout() };
if author_styles.stylesheets.dirty() {
author_styles.flush::<E>(stylist, guard);
}
}
}
impl Convert<devtools_traits::ShadowRootMode> for ShadowRootMode {
fn convert(self) -> devtools_traits::ShadowRootMode {
match self {
ShadowRootMode::Open => devtools_traits::ShadowRootMode::Open,
ShadowRootMode::Closed => devtools_traits::ShadowRootMode::Closed,
}
}
}