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::invalidation::element::restyle_hints::RestyleHint;
17use style::shared_lock::SharedRwLockReadGuard;
18use style::stylesheets::Stylesheet;
19use style::stylist::{CascadeData, Stylist};
20use stylo_atoms::Atom;
21
22use crate::conversions::Convert;
23use crate::dom::bindings::cell::{DomRefCell, RefMut};
24use crate::dom::bindings::codegen::Bindings::ElementBinding::GetHTMLOptions;
25use crate::dom::bindings::codegen::Bindings::HTMLSlotElementBinding::HTMLSlotElement_Binding::HTMLSlotElementMethods;
26use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::ShadowRoot_Binding::ShadowRootMethods;
27use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{
28 ShadowRootMode, SlotAssignmentMode,
29};
30use crate::dom::bindings::codegen::UnionTypes::{
31 TrustedHTMLOrNullIsEmptyString, TrustedHTMLOrString,
32};
33use crate::dom::bindings::frozenarray::CachedFrozenArray;
34use crate::dom::bindings::inheritance::Castable;
35use crate::dom::bindings::num::Finite;
36use crate::dom::bindings::reflector::reflect_dom_object;
37use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom};
38use crate::dom::bindings::str::DOMString;
39use crate::dom::css::cssstylesheet::CSSStyleSheet;
40use crate::dom::css::stylesheetlist::{StyleSheetList, StyleSheetListOwner};
41use crate::dom::document::Document;
42use crate::dom::documentfragment::{DocumentFragment, LayoutDocumentFragmentHelpers};
43use crate::dom::documentorshadowroot::{
44 DocumentOrShadowRoot, ServoStylesheetInDocument, StylesheetSource,
45};
46use crate::dom::element::Element;
47use crate::dom::html::htmlslotelement::HTMLSlotElement;
48use crate::dom::htmldetailselement::DetailsNameGroups;
49use crate::dom::node::{
50 BindContext, IsShadowTree, Node, NodeDamage, NodeFlags, NodeTraits, ShadowIncluding,
51 UnbindContext, VecPreOrderInsertionHelper,
52};
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#[derive(JSTraceable, MallocSizeOf, PartialEq)]
62pub(crate) enum IsUserAgentWidget {
63 No,
64 Yes,
65}
66
67#[dom_struct]
69pub(crate) struct ShadowRoot {
70 document_fragment: DocumentFragment,
71 document_or_shadow_root: DocumentOrShadowRoot,
72 document: Dom<Document>,
73 #[custom_trace]
75 author_styles: DomRefCell<AuthorStyles<ServoStylesheetInDocument>>,
76 stylesheet_list: MutNullableDom<StyleSheetList>,
77 window: Dom<Window>,
78
79 mode: ShadowRootMode,
81
82 slot_assignment_mode: SlotAssignmentMode,
84
85 clonable: bool,
87
88 available_to_element_internals: Cell<bool>,
90
91 slots: DomRefCell<HashMap<DOMString, Vec<Dom<HTMLSlotElement>>>>,
92
93 is_user_agent_widget: bool,
94
95 declarative: Cell<bool>,
97
98 serializable: Cell<bool>,
100
101 delegates_focus: Cell<bool>,
103
104 adopted_stylesheets: DomRefCell<Vec<Dom<CSSStyleSheet>>>,
107
108 #[ignore_malloc_size_of = "mozjs"]
110 adopted_stylesheets_frozen_types: CachedFrozenArray,
111
112 details_name_groups: DomRefCell<Option<DetailsNameGroups>>,
113}
114
115impl ShadowRoot {
116 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
117 fn new_inherited(
118 host: &Element,
119 document: &Document,
120 mode: ShadowRootMode,
121 slot_assignment_mode: SlotAssignmentMode,
122 clonable: bool,
123 is_user_agent_widget: IsUserAgentWidget,
124 ) -> ShadowRoot {
125 let document_fragment = DocumentFragment::new_inherited(document, Some(host));
126 let node = document_fragment.upcast::<Node>();
127 node.set_flag(NodeFlags::IS_IN_SHADOW_TREE, true);
128 node.set_flag(
129 NodeFlags::IS_CONNECTED,
130 host.upcast::<Node>().is_connected(),
131 );
132
133 ShadowRoot {
134 document_fragment,
135 document_or_shadow_root: DocumentOrShadowRoot::new(document.window()),
136 document: Dom::from_ref(document),
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 details_name_groups: Default::default(),
152 }
153 }
154
155 pub(crate) fn new(
156 host: &Element,
157 document: &Document,
158 mode: ShadowRootMode,
159 slot_assignment_mode: SlotAssignmentMode,
160 clonable: bool,
161 is_user_agent_widget: IsUserAgentWidget,
162 can_gc: CanGc,
163 ) -> DomRoot<ShadowRoot> {
164 reflect_dom_object(
165 Box::new(ShadowRoot::new_inherited(
166 host,
167 document,
168 mode,
169 slot_assignment_mode,
170 clonable,
171 is_user_agent_widget,
172 )),
173 document.window(),
174 can_gc,
175 )
176 }
177
178 pub(crate) fn owner_doc(&self) -> &Document {
179 &self.document
180 }
181
182 pub(crate) fn get_focused_element(&self) -> Option<DomRoot<Element>> {
183 None
185 }
186
187 pub(crate) fn stylesheet_count(&self) -> usize {
188 self.author_styles.borrow().stylesheets.len()
189 }
190
191 pub(crate) fn stylesheet_at(&self, index: usize) -> Option<DomRoot<CSSStyleSheet>> {
192 let stylesheets = &self.author_styles.borrow().stylesheets;
193
194 stylesheets
195 .get(index)
196 .and_then(|s| s.owner.get_cssom_object())
197 }
198
199 #[cfg_attr(crown, expect(crown::unrooted_must_root))] pub(crate) fn add_owned_stylesheet(&self, owner_node: &Element, sheet: Arc<Stylesheet>) {
206 let stylesheets = &mut self.author_styles.borrow_mut().stylesheets;
207
208 let insertion_point = stylesheets
210 .iter()
211 .find(|sheet_in_shadow| {
212 match &sheet_in_shadow.owner {
213 StylesheetSource::Element(other_node) => {
214 owner_node.upcast::<Node>().is_before(other_node.upcast())
215 },
216 StylesheetSource::Constructed(_) => true,
219 }
220 })
221 .cloned();
222
223 if self.document.has_browsing_context() {
224 let document_context = self.window.web_font_context();
225 self.window
226 .layout_mut()
227 .load_web_fonts_from_stylesheet(&sheet, &document_context);
228 }
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 #[cfg_attr(crown, expect(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 if self.document.has_browsing_context() {
250 let document_context = self.window.web_font_context();
251 self.window
252 .layout_mut()
253 .load_web_fonts_from_stylesheet(&sheet, &document_context);
254 }
255
256 DocumentOrShadowRoot::add_stylesheet(
257 StylesheetSource::Constructed(Dom::from_ref(cssom_stylesheet)),
258 StylesheetSetRef::Author(stylesheets),
259 sheet,
260 insertion_point,
261 self.document.style_shared_lock(),
262 );
263 }
264
265 #[cfg_attr(crown, expect(crown::unrooted_must_root))] pub(crate) fn remove_stylesheet(&self, owner: StylesheetSource, s: &Arc<Stylesheet>) {
268 DocumentOrShadowRoot::remove_stylesheet(
269 owner,
270 s,
271 StylesheetSetRef::Author(&mut self.author_styles.borrow_mut().stylesheets),
272 )
273 }
274
275 pub(crate) fn invalidate_stylesheets(&self) {
276 self.document.invalidate_shadow_roots_stylesheets();
277 self.author_styles.borrow_mut().stylesheets.force_dirty();
278 self.Host().upcast::<Node>().dirty(NodeDamage::Style);
280
281 let mut restyle = self.document.ensure_pending_restyle(&self.Host());
284 restyle.hint.insert(RestyleHint::restyle_subtree());
285 }
286
287 pub(crate) fn unregister_element_id(&self, to_unregister: &Element, id: Atom, _can_gc: CanGc) {
290 self.document_or_shadow_root.unregister_named_element(
291 self.document_fragment.id_map(),
292 to_unregister,
293 &id,
294 );
295 }
296
297 pub(crate) fn register_element_id(&self, element: &Element, id: Atom, _can_gc: CanGc) {
299 let root = self
300 .upcast::<Node>()
301 .inclusive_ancestors(ShadowIncluding::No)
302 .last()
303 .unwrap();
304 self.document_or_shadow_root.register_named_element(
305 self.document_fragment.id_map(),
306 element,
307 &id,
308 root,
309 );
310 }
311
312 pub(crate) fn register_slot(&self, slot: &HTMLSlotElement) {
313 debug!("Registering slot with name={:?}", slot.Name().str());
314
315 let mut slots = self.slots.borrow_mut();
316
317 let slots_with_the_same_name = slots.entry(slot.Name()).or_default();
318
319 slots_with_the_same_name.insert_pre_order(slot, self.upcast::<Node>());
321 }
322
323 pub(crate) fn unregister_slot(&self, name: DOMString, slot: &HTMLSlotElement) {
324 debug!("Unregistering slot with name={:?}", name.str());
325
326 let mut slots = self.slots.borrow_mut();
327 let Entry::Occupied(mut entry) = slots.entry(name) else {
328 panic!("slot is not registered");
329 };
330 entry.get_mut().retain(|s| slot != &**s);
331 }
332
333 pub(crate) fn slot_for_name(&self, name: &DOMString) -> Option<DomRoot<HTMLSlotElement>> {
335 self.slots
336 .borrow()
337 .get(name)
338 .and_then(|slots| slots.first())
339 .map(|slot| slot.as_rooted())
340 }
341
342 pub(crate) fn has_slot_descendants(&self) -> bool {
343 !self.slots.borrow().is_empty()
344 }
345
346 pub(crate) fn set_available_to_element_internals(&self, value: bool) {
347 self.available_to_element_internals.set(value);
348 }
349
350 pub(crate) fn is_available_to_element_internals(&self) -> bool {
352 self.available_to_element_internals.get()
353 }
354
355 pub(crate) fn is_user_agent_widget(&self) -> bool {
356 self.is_user_agent_widget
357 }
358
359 pub(crate) fn set_declarative(&self, declarative: bool) {
360 self.declarative.set(declarative);
361 }
362
363 pub(crate) fn is_declarative(&self) -> bool {
364 self.declarative.get()
365 }
366
367 pub(crate) fn shadow_root_mode(&self) -> ShadowRootMode {
368 self.mode
369 }
370
371 pub(crate) fn set_serializable(&self, serializable: bool) {
372 self.serializable.set(serializable);
373 }
374
375 pub(crate) fn set_delegates_focus(&self, delegates_focus: bool) {
376 self.delegates_focus.set(delegates_focus);
377 }
378
379 pub(crate) fn details_name_groups(&self) -> RefMut<'_, DetailsNameGroups> {
380 RefMut::map(
381 self.details_name_groups.borrow_mut(),
382 |details_name_groups| details_name_groups.get_or_insert_default(),
383 )
384 }
385}
386
387impl ShadowRootMethods<crate::DomTypeHolder> for ShadowRoot {
388 fn GetActiveElement(&self) -> Option<DomRoot<Element>> {
390 self.document_or_shadow_root
391 .get_active_element(self.get_focused_element(), None, None)
392 }
393
394 fn ElementFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Option<DomRoot<Element>> {
396 match self.document_or_shadow_root.element_from_point(
399 x,
400 y,
401 None,
402 self.document.has_browsing_context(),
403 ) {
404 Some(e) => {
405 let retargeted_node = e.upcast::<EventTarget>().retarget(self.upcast());
406 retargeted_node.downcast::<Element>().map(DomRoot::from_ref)
407 },
408 None => None,
409 }
410 }
411
412 fn ElementsFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Vec<DomRoot<Element>> {
414 let mut elements = Vec::new();
417 for e in self
418 .document_or_shadow_root
419 .elements_from_point(x, y, None, self.document.has_browsing_context())
420 .iter()
421 {
422 let retargeted_node = e.upcast::<EventTarget>().retarget(self.upcast());
423 if let Some(element) = retargeted_node.downcast::<Element>().map(DomRoot::from_ref) {
424 elements.push(element);
425 }
426 }
427 elements
428 }
429
430 fn Mode(&self) -> ShadowRootMode {
432 self.mode
433 }
434
435 fn DelegatesFocus(&self) -> bool {
437 self.delegates_focus.get()
438 }
439
440 fn Clonable(&self) -> bool {
442 self.clonable
443 }
444
445 fn Serializable(&self) -> bool {
447 self.serializable.get()
448 }
449
450 fn Host(&self) -> DomRoot<Element> {
452 self.upcast::<DocumentFragment>()
453 .host()
454 .expect("ShadowRoot always has an element as host")
455 }
456
457 fn StyleSheets(&self) -> DomRoot<StyleSheetList> {
459 self.stylesheet_list.or_init(|| {
460 StyleSheetList::new(
461 &self.window,
462 StyleSheetListOwner::ShadowRoot(Dom::from_ref(self)),
463 CanGc::note(),
464 )
465 })
466 }
467
468 fn GetHTML(&self, options: &GetHTMLOptions, can_gc: CanGc) -> DOMString {
470 self.upcast::<Node>().html_serialize(
473 TraversalScope::ChildrenOnly(None),
474 options.serializableShadowRoots,
475 options.shadowRoots.clone(),
476 can_gc,
477 )
478 }
479
480 fn GetInnerHTML(&self, can_gc: CanGc) -> Fallible<TrustedHTMLOrNullIsEmptyString> {
482 self.upcast::<Node>()
485 .fragment_serialization_algorithm(true, can_gc)
486 .map(TrustedHTMLOrNullIsEmptyString::NullIsEmptyString)
487 }
488
489 fn SetInnerHTML(&self, value: TrustedHTMLOrNullIsEmptyString, can_gc: CanGc) -> ErrorResult {
491 let value = TrustedHTML::get_trusted_script_compliant_string(
494 &self.owner_global(),
495 value.convert(),
496 "ShadowRoot innerHTML",
497 can_gc,
498 )?;
499
500 let context = self.Host();
502
503 let frag = context.parse_fragment(value, can_gc)?;
509
510 Node::replace_all(Some(frag.upcast()), self.upcast(), can_gc);
512 Ok(())
513 }
514
515 fn SlotAssignment(&self) -> SlotAssignmentMode {
517 self.slot_assignment_mode
518 }
519
520 fn SetHTMLUnsafe(&self, value: TrustedHTMLOrString, can_gc: CanGc) -> ErrorResult {
522 let value = TrustedHTML::get_trusted_script_compliant_string(
526 &self.owner_global(),
527 value,
528 "ShadowRoot setHTMLUnsafe",
529 can_gc,
530 )?;
531 let target = self.upcast::<Node>();
533 let context_element = self.Host();
534
535 Node::unsafely_set_html(target, &context_element, value, can_gc);
536 Ok(())
537 }
538
539 event_handler!(onslotchange, GetOnslotchange, SetOnslotchange);
541
542 fn AdoptedStyleSheets(&self, context: JSContext, can_gc: CanGc, retval: MutableHandleValue) {
544 self.adopted_stylesheets_frozen_types.get_or_init(
545 || {
546 self.adopted_stylesheets
547 .borrow()
548 .clone()
549 .iter()
550 .map(|sheet| sheet.as_rooted())
551 .collect()
552 },
553 context,
554 retval,
555 can_gc,
556 );
557 }
558
559 fn SetAdoptedStyleSheets(
561 &self,
562 context: JSContext,
563 val: HandleValue,
564 can_gc: CanGc,
565 ) -> ErrorResult {
566 let result = DocumentOrShadowRoot::set_adopted_stylesheet_from_jsval(
567 context,
568 self.adopted_stylesheets.borrow_mut().as_mut(),
569 val,
570 &StyleSheetListOwner::ShadowRoot(Dom::from_ref(self)),
571 can_gc,
572 );
573
574 if result.is_ok() {
576 self.adopted_stylesheets_frozen_types.clear();
577 }
578
579 result
580 }
581
582 fn GetFullscreenElement(&self) -> Option<DomRoot<Element>> {
584 DocumentOrShadowRoot::get_fullscreen_element(
585 self.upcast::<Node>(),
586 self.document.fullscreen_element(),
587 )
588 }
589}
590
591impl VirtualMethods for ShadowRoot {
592 fn super_type(&self) -> Option<&dyn VirtualMethods> {
593 Some(self.upcast::<DocumentFragment>() as &dyn VirtualMethods)
594 }
595
596 fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
597 if let Some(s) = self.super_type() {
598 s.bind_to_tree(context, can_gc);
599 }
600
601 if context.tree_connected {
604 let document = self.owner_document();
605 document.register_shadow_root(self);
606 }
607
608 let shadow_root = self.upcast::<Node>();
609
610 shadow_root.set_flag(NodeFlags::IS_CONNECTED, context.tree_connected);
611
612 let context = BindContext::new(shadow_root, IsShadowTree::Yes);
613
614 for node in shadow_root.traverse_preorder(ShadowIncluding::Yes).skip(1) {
616 node.set_flag(NodeFlags::IS_CONNECTED, context.tree_connected);
617
618 debug_assert!(!node.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS));
620 vtable_for(&node).bind_to_tree(&context, can_gc);
621 }
622 }
623
624 fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
625 if let Some(s) = self.super_type() {
626 s.unbind_from_tree(context, can_gc);
627 }
628
629 if context.tree_connected {
630 let document = self.owner_document();
631 document.unregister_shadow_root(self);
632 }
633 }
634}
635
636#[expect(unsafe_code)]
637pub(crate) trait LayoutShadowRootHelpers<'dom> {
638 fn get_host_for_layout(self) -> LayoutDom<'dom, Element>;
639 fn get_style_data_for_layout(self) -> &'dom CascadeData;
640 fn is_ua_widget(&self) -> bool;
641 unsafe fn flush_stylesheets(self, stylist: &mut Stylist, guard: &SharedRwLockReadGuard);
642}
643
644impl<'dom> LayoutShadowRootHelpers<'dom> for LayoutDom<'dom, ShadowRoot> {
645 #[inline]
646 fn get_host_for_layout(self) -> LayoutDom<'dom, Element> {
647 self.upcast::<DocumentFragment>()
648 .shadowroot_host_for_layout()
649 }
650
651 #[inline]
652 #[expect(unsafe_code)]
653 fn get_style_data_for_layout(self) -> &'dom CascadeData {
654 fn is_sync<T: Sync>() {}
655 let _ = is_sync::<CascadeData>;
656 unsafe { &self.unsafe_get().author_styles.borrow_for_layout().data }
657 }
658
659 #[inline]
660 fn is_ua_widget(&self) -> bool {
661 self.unsafe_get().is_user_agent_widget()
662 }
663
664 #[inline]
667 #[expect(unsafe_code)]
668 unsafe fn flush_stylesheets(self, stylist: &mut Stylist, guard: &SharedRwLockReadGuard) {
669 let author_styles = unsafe { self.unsafe_get().author_styles.borrow_mut_for_layout() };
670 if author_styles.stylesheets.dirty() {
671 author_styles.flush(stylist, guard);
672 }
673 }
674}
675
676impl Convert<devtools_traits::ShadowRootMode> for ShadowRootMode {
677 fn convert(self) -> devtools_traits::ShadowRootMode {
678 match self {
679 ShadowRootMode::Open => devtools_traits::ShadowRootMode::Open,
680 ShadowRootMode::Closed => devtools_traits::ShadowRootMode::Closed,
681 }
682 }
683}