1use 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#[derive(JSTraceable, MallocSizeOf, PartialEq)]
63pub(crate) enum IsUserAgentWidget {
64 No,
65 Yes,
66}
67
68#[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 #[custom_trace]
77 author_styles: DomRefCell<AuthorStyles<ServoStylesheetInDocument>>,
78 stylesheet_list: MutNullableDom<StyleSheetList>,
79 window: Dom<Window>,
80
81 mode: ShadowRootMode,
83
84 slot_assignment_mode: SlotAssignmentMode,
86
87 clonable: bool,
89
90 available_to_element_internals: Cell<bool>,
92
93 slots: DomRefCell<HashMap<DOMString, Vec<Dom<HTMLSlotElement>>>>,
94
95 is_user_agent_widget: bool,
96
97 declarative: Cell<bool>,
99
100 serializable: Cell<bool>,
102
103 delegates_focus: Cell<bool>,
105
106 adopted_stylesheets: DomRefCell<Vec<Dom<CSSStyleSheet>>>,
109
110 #[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 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 #[cfg_attr(crown, allow(crown::unrooted_must_root))] 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 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 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 #[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 #[cfg_attr(crown, allow(crown::unrooted_must_root))] 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 self.host.upcast::<Node>().dirty(NodeDamage::Style);
269
270 let mut restyle = self.document.ensure_pending_restyle(&self.host);
273 restyle.hint.insert(RestyleHint::restyle_subtree());
274 }
275
276 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 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 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 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 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 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 fn ElementFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Option<DomRoot<Element>> {
385 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 = self
395 .upcast::<EventTarget>()
396 .retarget(e.upcast::<EventTarget>());
397 retargeted_node.downcast::<Element>().map(DomRoot::from_ref)
398 },
399 None => None,
400 }
401 }
402
403 fn ElementsFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Vec<DomRoot<Element>> {
405 let mut elements = Vec::new();
408 for e in self
409 .document_or_shadow_root
410 .elements_from_point(x, y, None, self.document.has_browsing_context())
411 .iter()
412 {
413 let retargeted_node = self
414 .upcast::<EventTarget>()
415 .retarget(e.upcast::<EventTarget>());
416 if let Some(element) = retargeted_node.downcast::<Element>().map(DomRoot::from_ref) {
417 elements.push(element);
418 }
419 }
420 elements
421 }
422
423 fn Mode(&self) -> ShadowRootMode {
425 self.mode
426 }
427
428 fn DelegatesFocus(&self) -> bool {
430 self.delegates_focus.get()
431 }
432
433 fn Clonable(&self) -> bool {
435 self.clonable
436 }
437
438 fn Serializable(&self) -> bool {
440 self.serializable.get()
441 }
442
443 fn Host(&self) -> DomRoot<Element> {
445 self.host.as_rooted()
446 }
447
448 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 fn GetHTML(&self, options: &GetHTMLOptions, can_gc: CanGc) -> DOMString {
461 self.upcast::<Node>().html_serialize(
464 TraversalScope::ChildrenOnly(None),
465 options.serializableShadowRoots,
466 options.shadowRoots.clone(),
467 can_gc,
468 )
469 }
470
471 fn GetInnerHTML(&self, can_gc: CanGc) -> Fallible<TrustedHTMLOrNullIsEmptyString> {
473 self.upcast::<Node>()
476 .fragment_serialization_algorithm(true, can_gc)
477 .map(TrustedHTMLOrNullIsEmptyString::NullIsEmptyString)
478 }
479
480 fn SetInnerHTML(&self, value: TrustedHTMLOrNullIsEmptyString, can_gc: CanGc) -> ErrorResult {
482 let value = TrustedHTML::get_trusted_script_compliant_string(
485 &self.owner_global(),
486 value.convert(),
487 "ShadowRoot innerHTML",
488 can_gc,
489 )?;
490
491 let context = self.Host();
493
494 let frag = context.parse_fragment(value, can_gc)?;
500
501 Node::replace_all(Some(frag.upcast()), self.upcast(), can_gc);
503 Ok(())
504 }
505
506 fn SlotAssignment(&self) -> SlotAssignmentMode {
508 self.slot_assignment_mode
509 }
510
511 fn SetHTMLUnsafe(&self, value: TrustedHTMLOrString, can_gc: CanGc) -> ErrorResult {
513 let value = TrustedHTML::get_trusted_script_compliant_string(
517 &self.owner_global(),
518 value,
519 "ShadowRoot setHTMLUnsafe",
520 can_gc,
521 )?;
522 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 event_handler!(onslotchange, GetOnslotchange, SetOnslotchange);
532
533 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 fn SetAdoptedStyleSheets(
552 &self,
553 context: JSContext,
554 val: HandleValue,
555 can_gc: CanGc,
556 ) -> ErrorResult {
557 let result = DocumentOrShadowRoot::set_adopted_stylesheet_from_jsval(
558 context,
559 self.adopted_stylesheets.borrow_mut().as_mut(),
560 val,
561 &StyleSheetListOwner::ShadowRoot(Dom::from_ref(self)),
562 can_gc,
563 );
564
565 if result.is_ok() {
567 self.adopted_stylesheets_frozen_types.clear();
568 }
569
570 result
571 }
572}
573
574impl VirtualMethods for ShadowRoot {
575 fn super_type(&self) -> Option<&dyn VirtualMethods> {
576 Some(self.upcast::<DocumentFragment>() as &dyn VirtualMethods)
577 }
578
579 fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
580 if let Some(s) = self.super_type() {
581 s.bind_to_tree(context, can_gc);
582 }
583
584 if context.tree_connected {
587 let document = self.owner_document();
588 document.register_shadow_root(self);
589 }
590
591 let shadow_root = self.upcast::<Node>();
592
593 shadow_root.set_flag(NodeFlags::IS_CONNECTED, context.tree_connected);
594
595 let context = BindContext::new(shadow_root, IsShadowTree::Yes);
596
597 for node in shadow_root.traverse_preorder(ShadowIncluding::Yes).skip(1) {
599 node.set_flag(NodeFlags::IS_CONNECTED, context.tree_connected);
600
601 debug_assert!(!node.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS));
603 vtable_for(&node).bind_to_tree(&context, can_gc);
604 }
605 }
606
607 fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
608 if let Some(s) = self.super_type() {
609 s.unbind_from_tree(context, can_gc);
610 }
611
612 if context.tree_connected {
613 let document = self.owner_document();
614 document.unregister_shadow_root(self);
615 }
616 }
617}
618
619#[expect(unsafe_code)]
620pub(crate) trait LayoutShadowRootHelpers<'dom> {
621 fn get_host_for_layout(self) -> LayoutDom<'dom, Element>;
622 fn get_style_data_for_layout(self) -> &'dom CascadeData;
623 unsafe fn flush_stylesheets<E: TElement>(
624 self,
625 stylist: &mut Stylist,
626 guard: &SharedRwLockReadGuard,
627 );
628}
629
630impl<'dom> LayoutShadowRootHelpers<'dom> for LayoutDom<'dom, ShadowRoot> {
631 #[inline]
632 #[expect(unsafe_code)]
633 fn get_host_for_layout(self) -> LayoutDom<'dom, Element> {
634 unsafe { self.unsafe_get().host.to_layout() }
635 }
636
637 #[inline]
638 #[expect(unsafe_code)]
639 fn get_style_data_for_layout(self) -> &'dom CascadeData {
640 fn is_sync<T: Sync>() {}
641 let _ = is_sync::<CascadeData>;
642 unsafe { &self.unsafe_get().author_styles.borrow_for_layout().data }
643 }
644
645 #[inline]
648 #[expect(unsafe_code)]
649 unsafe fn flush_stylesheets<E: TElement>(
650 self,
651 stylist: &mut Stylist,
652 guard: &SharedRwLockReadGuard,
653 ) {
654 let author_styles = unsafe { self.unsafe_get().author_styles.borrow_mut_for_layout() };
655 if author_styles.stylesheets.dirty() {
656 author_styles.flush::<E>(stylist, guard);
657 }
658 }
659}
660
661impl Convert<devtools_traits::ShadowRootMode> for ShadowRootMode {
662 fn convert(self) -> devtools_traits::ShadowRootMode {
663 match self {
664 ShadowRootMode::Open => devtools_traits::ShadowRootMode::Open,
665 ShadowRootMode::Closed => devtools_traits::ShadowRootMode::Closed,
666 }
667 }
668}