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::context::JSContext;
12use js::rust::{HandleValue, MutableHandleValue};
13use script_bindings::cell::{DomRefCell, RefMut};
14use script_bindings::error::{ErrorResult, Fallible};
15use script_bindings::reflector::reflect_dom_object;
16use servo_arc::Arc;
17use style::author_styles::AuthorStyles;
18use style::invalidation::element::restyle_hints::RestyleHint;
19use style::shared_lock::SharedRwLockReadGuard;
20use style::stylesheets::Stylesheet;
21use style::stylist::{CascadeData, Stylist};
22use stylo_atoms::Atom;
23
24use crate::conversions::Convert;
25use crate::dom::bindings::codegen::Bindings::ElementBinding::GetHTMLOptions;
26use crate::dom::bindings::codegen::Bindings::HTMLSlotElementBinding::HTMLSlotElement_Binding::HTMLSlotElementMethods;
27use crate::dom::bindings::codegen::Bindings::SanitizerBinding::{
28 SetHTMLOptions, SetHTMLUnsafeOptions,
29};
30use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::ShadowRoot_Binding::ShadowRootMethods;
31use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{
32 ShadowRootMode, SlotAssignmentMode,
33};
34use crate::dom::bindings::codegen::UnionTypes::{
35 TrustedHTMLOrNullIsEmptyString, TrustedHTMLOrString,
36};
37use crate::dom::bindings::frozenarray::CachedFrozenArray;
38use crate::dom::bindings::inheritance::Castable;
39use crate::dom::bindings::num::Finite;
40use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom};
41use crate::dom::bindings::str::DOMString;
42use crate::dom::css::cssstylesheet::CSSStyleSheet;
43use crate::dom::css::stylesheetlist::{StyleSheetList, StyleSheetListOwner};
44use crate::dom::customelementregistry::CustomElementRegistry;
45use crate::dom::document::Document;
46use crate::dom::documentfragment::DocumentFragment;
47use crate::dom::documentorshadowroot::{
48 DocumentOrShadowRoot, ServoStylesheetInDocument, StylesheetSource,
49};
50use crate::dom::element::Element;
51use crate::dom::html::htmlslotelement::HTMLSlotElement;
52use crate::dom::htmldetailselement::DetailsNameGroups;
53use crate::dom::iterators::ShadowIncluding;
54use crate::dom::node::virtualmethods::{VirtualMethods, vtable_for};
55use crate::dom::node::{
56 BindContext, IsShadowTree, Node, NodeDamage, NodeFlags, NodeTraits, UnbindContext,
57 VecPreOrderInsertionHelper,
58};
59use crate::dom::sanitizer::Sanitizer;
60use crate::dom::trustedtypes::trustedhtml::TrustedHTML;
61use crate::dom::types::EventTarget;
62use crate::dom::window::Window;
63use crate::script_runtime::CanGc;
64use crate::stylesheet_set::StylesheetSetRef;
65
66#[derive(JSTraceable, MallocSizeOf, PartialEq)]
68pub(crate) enum IsUserAgentWidget {
69 No,
70 Yes,
71}
72
73#[dom_struct]
75pub(crate) struct ShadowRoot {
76 document_fragment: DocumentFragment,
78 document_or_shadow_root: DocumentOrShadowRoot,
79 document: Dom<Document>,
80 #[custom_trace]
82 author_styles: DomRefCell<AuthorStyles<ServoStylesheetInDocument>>,
83 stylesheet_list: MutNullableDom<StyleSheetList>,
84 window: Dom<Window>,
85
86 mode: ShadowRootMode,
88
89 slot_assignment_mode: SlotAssignmentMode,
91
92 clonable: bool,
94
95 available_to_element_internals: Cell<bool>,
97
98 slots: DomRefCell<HashMap<DOMString, Vec<Dom<HTMLSlotElement>>>>,
99
100 is_user_agent_widget: bool,
101
102 declarative: Cell<bool>,
104
105 serializable: Cell<bool>,
107
108 delegates_focus: Cell<bool>,
110
111 adopted_stylesheets: DomRefCell<Vec<Dom<CSSStyleSheet>>>,
114
115 #[ignore_malloc_size_of = "mozjs"]
117 adopted_stylesheets_frozen_types: CachedFrozenArray,
118
119 details_name_groups: DomRefCell<Option<DetailsNameGroups>>,
120}
121
122impl ShadowRoot {
123 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
124 fn new_inherited(
125 host: &Element,
126 document: &Document,
127 mode: ShadowRootMode,
128 slot_assignment_mode: SlotAssignmentMode,
129 clonable: bool,
130 is_user_agent_widget: IsUserAgentWidget,
131 ) -> ShadowRoot {
132 let document_fragment = DocumentFragment::new_inherited(document, Some(host));
133 let node = document_fragment.upcast::<Node>();
134 node.set_flag(NodeFlags::IS_IN_SHADOW_TREE, true);
135 node.set_flag(
136 NodeFlags::IS_CONNECTED,
137 host.upcast::<Node>().is_connected(),
138 );
139
140 ShadowRoot {
141 document_fragment,
142 document_or_shadow_root: DocumentOrShadowRoot::new(document.window()),
143 document: Dom::from_ref(document),
144 author_styles: DomRefCell::new(AuthorStyles::new()),
145 stylesheet_list: MutNullableDom::new(None),
146 window: Dom::from_ref(document.window()),
147 mode,
148 slot_assignment_mode,
149 clonable,
150 available_to_element_internals: Cell::new(false),
151 slots: Default::default(),
152 is_user_agent_widget: is_user_agent_widget == IsUserAgentWidget::Yes,
153 declarative: Cell::new(false),
154 serializable: Cell::new(false),
155 delegates_focus: Cell::new(false),
156 adopted_stylesheets: Default::default(),
157 adopted_stylesheets_frozen_types: CachedFrozenArray::new(),
158 details_name_groups: Default::default(),
159 }
160 }
161
162 pub(crate) fn new(
163 host: &Element,
164 document: &Document,
165 mode: ShadowRootMode,
166 slot_assignment_mode: SlotAssignmentMode,
167 clonable: bool,
168 is_user_agent_widget: IsUserAgentWidget,
169 can_gc: CanGc,
170 ) -> DomRoot<ShadowRoot> {
171 reflect_dom_object(
172 Box::new(ShadowRoot::new_inherited(
173 host,
174 document,
175 mode,
176 slot_assignment_mode,
177 clonable,
178 is_user_agent_widget,
179 )),
180 document.window(),
181 can_gc,
182 )
183 }
184
185 pub(crate) fn owner_doc(&self) -> &Document {
186 &self.document
187 }
188
189 pub(crate) fn stylesheet_count(&self) -> usize {
190 self.author_styles.borrow().stylesheets.len()
191 }
192
193 pub(crate) fn stylesheet_at(&self, index: usize) -> Option<DomRoot<CSSStyleSheet>> {
194 let stylesheets = &self.author_styles.borrow().stylesheets;
195
196 stylesheets
197 .get(index)
198 .and_then(|s| s.owner.get_cssom_object())
199 }
200
201 #[cfg_attr(crown, expect(crown::unrooted_must_root))] pub(crate) fn add_owned_stylesheet(
208 &self,
209 cx: &mut JSContext,
210 owner_node: &Element,
211 sheet: Arc<Stylesheet>,
212 ) {
213 let stylesheets = &mut self.author_styles.borrow_mut().stylesheets;
214
215 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 StylesheetSource::Constructed(_) => true,
226 }
227 })
228 .cloned();
229
230 if self.document.has_browsing_context() {
231 self.document.load_web_fonts_from_stylesheet(cx, &sheet);
232 }
233
234 DocumentOrShadowRoot::add_stylesheet(
235 StylesheetSource::Element(Dom::from_ref(owner_node)),
236 StylesheetSetRef::Author(stylesheets),
237 sheet,
238 insertion_point,
239 self.document.style_shared_author_lock(),
240 );
241 }
242
243 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
245 pub(crate) fn append_constructed_stylesheet(
246 &self,
247 cx: &mut JSContext,
248 cssom_stylesheet: &CSSStyleSheet,
249 ) {
250 debug_assert!(cssom_stylesheet.is_constructed());
251
252 let stylesheets = &mut self.author_styles.borrow_mut().stylesheets;
253 let sheet = cssom_stylesheet.style_stylesheet().clone();
254
255 let insertion_point = stylesheets.iter().last().cloned();
256
257 if self.document.has_browsing_context() {
258 self.document.load_web_fonts_from_stylesheet(cx, &sheet);
259 }
260
261 DocumentOrShadowRoot::add_stylesheet(
262 StylesheetSource::Constructed(Dom::from_ref(cssom_stylesheet)),
263 StylesheetSetRef::Author(stylesheets),
264 sheet,
265 insertion_point,
266 self.document.style_shared_author_lock(),
267 );
268 }
269
270 #[cfg_attr(crown, expect(crown::unrooted_must_root))] pub(crate) fn remove_stylesheet(&self, owner: StylesheetSource, s: &Arc<Stylesheet>) {
273 DocumentOrShadowRoot::remove_stylesheet(
274 owner,
275 s,
276 StylesheetSetRef::Author(&mut self.author_styles.borrow_mut().stylesheets),
277 )
278 }
279
280 pub(crate) fn invalidate_stylesheets(&self) {
281 self.document.invalidate_shadow_roots_stylesheets();
282 self.author_styles.borrow_mut().stylesheets.force_dirty();
283 self.Host().upcast::<Node>().dirty(NodeDamage::Style);
285
286 let mut restyle = self.document.ensure_pending_restyle(&self.Host());
289 restyle.hint.insert(RestyleHint::restyle_subtree());
290 }
291
292 pub(crate) fn unregister_element_id(&self, id: &Atom) {
295 self.document_fragment.id_map().remove(id);
296 }
297
298 pub(crate) fn register_element_id(&self, element: &Element, id: &Atom, _can_gc: CanGc) {
300 self.document_fragment.id_map().add(id, element)
301 }
302
303 pub(crate) fn register_slot(&self, slot: &HTMLSlotElement) {
304 debug!("Registering slot with name={:?}", slot.Name().str());
305
306 let mut slots = self.slots.borrow_mut();
307
308 let slots_with_the_same_name = slots.entry(slot.Name()).or_default();
309
310 slots_with_the_same_name.insert_pre_order(slot, self.upcast::<Node>());
312 }
313
314 pub(crate) fn unregister_slot(&self, name: DOMString, slot: &HTMLSlotElement) {
315 debug!("Unregistering slot with name={:?}", name.str());
316
317 let mut slots = self.slots.borrow_mut();
318 let Entry::Occupied(mut entry) = slots.entry(name) else {
319 panic!("slot is not registered");
320 };
321 entry.get_mut().retain(|s| slot != &**s);
322 }
323
324 pub(crate) fn slot_for_name(&self, name: &DOMString) -> Option<DomRoot<HTMLSlotElement>> {
326 self.slots
327 .borrow()
328 .get(name)
329 .and_then(|slots| slots.first())
330 .map(|slot| slot.as_rooted())
331 }
332
333 pub(crate) fn has_slot_descendants(&self) -> bool {
334 !self.slots.borrow().is_empty()
335 }
336
337 pub(crate) fn set_available_to_element_internals(&self, value: bool) {
338 self.available_to_element_internals.set(value);
339 }
340
341 pub(crate) fn is_available_to_element_internals(&self) -> bool {
343 self.available_to_element_internals.get()
344 }
345
346 pub(crate) fn is_user_agent_widget(&self) -> bool {
347 self.is_user_agent_widget
348 }
349
350 pub(crate) fn set_declarative(&self, declarative: bool) {
351 self.declarative.set(declarative);
352 }
353
354 pub(crate) fn is_declarative(&self) -> bool {
355 self.declarative.get()
356 }
357
358 pub(crate) fn shadow_root_mode(&self) -> ShadowRootMode {
359 self.mode
360 }
361
362 pub(crate) fn set_serializable(&self, serializable: bool) {
363 self.serializable.set(serializable);
364 }
365
366 pub(crate) fn set_delegates_focus(&self, delegates_focus: bool) {
367 self.delegates_focus.set(delegates_focus);
368 }
369
370 pub(crate) fn details_name_groups(&self) -> RefMut<'_, DetailsNameGroups> {
371 RefMut::map(
372 self.details_name_groups.borrow_mut(),
373 |details_name_groups| details_name_groups.get_or_insert_default(),
374 )
375 }
376
377 pub(crate) fn custom_element_registry(&self) -> Option<DomRoot<CustomElementRegistry>> {
378 self.document_or_shadow_root.custom_element_registry()
379 }
380
381 pub(crate) fn set_custom_element_registry(&self, registry: &CustomElementRegistry) {
382 self.document_or_shadow_root
383 .set_custom_element_registry(Some(registry));
384 }
385}
386
387impl ShadowRootMethods<crate::DomTypeHolder> for ShadowRoot {
388 fn GetActiveElement(&self) -> Option<DomRoot<Element>> {
390 self.document_or_shadow_root.active_element(self.upcast())
391 }
392
393 fn GetCustomElementRegistry(&self) -> Option<DomRoot<CustomElementRegistry>> {
395 self.custom_element_registry()
396 }
397
398 fn ElementFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Option<DomRoot<Element>> {
400 match self.document_or_shadow_root.element_from_point(
403 self.upcast(),
404 x,
405 y,
406 None,
407 self.document.has_browsing_context(),
408 ) {
409 Some(e) => {
410 let retargeted_node = e.upcast::<EventTarget>().retarget(self.upcast());
411 retargeted_node.downcast::<Element>().map(DomRoot::from_ref)
412 },
413 None => None,
414 }
415 }
416
417 fn ElementsFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Vec<DomRoot<Element>> {
419 let mut elements = Vec::new();
422 for e in self
423 .document_or_shadow_root
424 .elements_from_point(
425 self.upcast(),
426 x,
427 y,
428 None,
429 self.document.has_browsing_context(),
430 )
431 .iter()
432 {
433 let retargeted_node = e.upcast::<EventTarget>().retarget(self.upcast());
434 if let Some(element) = retargeted_node.downcast::<Element>().map(DomRoot::from_ref) {
435 elements.push(element);
436 }
437 }
438 elements
439 }
440
441 fn Mode(&self) -> ShadowRootMode {
443 self.mode
444 }
445
446 fn DelegatesFocus(&self) -> bool {
448 self.delegates_focus.get()
449 }
450
451 fn Clonable(&self) -> bool {
453 self.clonable
454 }
455
456 fn Serializable(&self) -> bool {
458 self.serializable.get()
459 }
460
461 fn Host(&self) -> DomRoot<Element> {
463 self.upcast::<DocumentFragment>()
464 .host()
465 .expect("ShadowRoot always has an element as host")
466 }
467
468 fn StyleSheets(&self) -> DomRoot<StyleSheetList> {
470 self.stylesheet_list.or_init(|| {
471 StyleSheetList::new(
472 &self.window,
473 StyleSheetListOwner::ShadowRoot(Dom::from_ref(self)),
474 CanGc::deprecated_note(),
475 )
476 })
477 }
478
479 fn GetHTML(&self, cx: &mut JSContext, options: &GetHTMLOptions) -> DOMString {
481 self.upcast::<Node>().html_serialize(
484 cx,
485 TraversalScope::ChildrenOnly(None),
486 options.serializableShadowRoots,
487 options.shadowRoots.clone(),
488 )
489 }
490
491 fn GetInnerHTML(&self, cx: &mut JSContext) -> Fallible<TrustedHTMLOrNullIsEmptyString> {
493 self.upcast::<Node>()
496 .fragment_serialization_algorithm(cx, true)
497 .map(TrustedHTMLOrNullIsEmptyString::NullIsEmptyString)
498 }
499
500 fn SetInnerHTML(
502 &self,
503 cx: &mut JSContext,
504 value: TrustedHTMLOrNullIsEmptyString,
505 ) -> ErrorResult {
506 let value = TrustedHTML::get_trusted_type_compliant_string(
509 cx,
510 &self.owner_global(),
511 value.convert(),
512 "ShadowRoot innerHTML",
513 )?;
514
515 let context = self.Host();
517
518 let frag = context.parse_fragment(value, cx)?;
524
525 Node::replace_all(cx, Some(frag.upcast()), self.upcast());
527 Ok(())
528 }
529
530 fn SlotAssignment(&self) -> SlotAssignmentMode {
532 self.slot_assignment_mode
533 }
534
535 fn SetHTMLUnsafe(
537 &self,
538 cx: &mut JSContext,
539 value: TrustedHTMLOrString,
540 options: &SetHTMLUnsafeOptions,
541 ) -> ErrorResult {
542 let compliant_html = TrustedHTML::get_trusted_type_compliant_string(
546 cx,
547 &self.owner_global(),
548 value,
549 "ShadowRoot setHTMLUnsafe",
550 )?;
551
552 Sanitizer::set_and_filter_html(
555 cx,
556 self.upcast(),
557 &self.Host(),
558 compliant_html,
559 options,
560 false,
561 )?;
562
563 Ok(())
564 }
565
566 fn SetHTML(
568 &self,
569 cx: &mut JSContext,
570 html: DOMString,
571 options: &SetHTMLOptions,
572 ) -> ErrorResult {
573 let target = self.upcast::<Node>();
578 let context_element = self.Host();
579 Sanitizer::set_and_filter_html(cx, target, &context_element, html, options, true)
580 }
581
582 event_handler!(slotchange, GetOnslotchange, SetOnslotchange);
584
585 fn AdoptedStyleSheets(&self, cx: &mut JSContext, retval: MutableHandleValue) {
587 self.adopted_stylesheets_frozen_types.get_or_init(
588 cx,
589 || {
590 self.adopted_stylesheets
591 .borrow()
592 .clone()
593 .iter()
594 .map(|sheet| sheet.as_rooted())
595 .collect()
596 },
597 retval,
598 );
599 }
600
601 fn SetAdoptedStyleSheets(&self, cx: &mut JSContext, val: HandleValue) -> ErrorResult {
603 let result = DocumentOrShadowRoot::set_adopted_stylesheet_from_jsval(
604 cx,
605 self.adopted_stylesheets.borrow_mut().as_mut(),
606 val,
607 &StyleSheetListOwner::ShadowRoot(Dom::from_ref(self)),
608 );
609
610 if result.is_ok() {
612 self.adopted_stylesheets_frozen_types.clear();
613 }
614
615 result
616 }
617
618 fn GetFullscreenElement(&self) -> Option<DomRoot<Element>> {
620 DocumentOrShadowRoot::get_fullscreen_element(
621 self.upcast::<Node>(),
622 self.document.fullscreen_element(),
623 )
624 }
625}
626
627impl VirtualMethods for ShadowRoot {
628 fn super_type(&self) -> Option<&dyn VirtualMethods> {
629 Some(self.upcast::<DocumentFragment>() as &dyn VirtualMethods)
630 }
631
632 fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
633 if let Some(s) = self.super_type() {
634 s.bind_to_tree(cx, context);
635 }
636
637 if context.tree_connected {
640 let document = self.owner_document();
641 document.register_shadow_root(self);
642 }
643
644 let shadow_root = self.upcast::<Node>();
645
646 shadow_root.set_flag(NodeFlags::IS_CONNECTED, context.tree_connected);
647
648 let inner_context = BindContext::new(shadow_root, IsShadowTree::Yes);
649
650 for node in shadow_root.traverse_preorder(ShadowIncluding::No).skip(1) {
652 node.set_flag(NodeFlags::IS_CONNECTED, inner_context.tree_connected);
653
654 debug_assert!(!node.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS));
656 vtable_for(&node).bind_to_tree(cx, &inner_context);
657 }
658 }
659
660 fn unbind_from_tree(&self, cx: &mut JSContext, context: &UnbindContext) {
661 if let Some(s) = self.super_type() {
662 s.unbind_from_tree(cx, context);
663 }
664
665 if context.tree_connected {
666 let document = self.owner_document();
667 document.unregister_shadow_root(self);
668 }
669 }
670}
671
672impl<'dom> LayoutDom<'dom, ShadowRoot> {
673 #[inline]
674 pub(crate) fn get_host_for_layout(self) -> LayoutDom<'dom, Element> {
675 self.upcast::<DocumentFragment>()
676 .shadowroot_host_for_layout()
677 }
678
679 #[inline]
680 #[expect(unsafe_code)]
681 pub(crate) fn get_style_data_for_layout(self) -> &'dom CascadeData {
682 fn is_sync<T: Sync>() {}
683 let _ = is_sync::<CascadeData>;
684 unsafe { &self.unsafe_get().author_styles.borrow_for_layout().data }
685 }
686
687 #[inline]
688 pub(crate) fn is_user_agent_widget(&self) -> bool {
689 self.unsafe_get().is_user_agent_widget()
690 }
691
692 #[inline]
695 #[expect(unsafe_code)]
696 pub(crate) unsafe fn flush_stylesheets_for_layout(
697 self,
698 stylist: &mut Stylist,
699 guard: &SharedRwLockReadGuard,
700 ) {
701 unsafe {
702 debug_assert!(self.upcast::<Node>().get_flag(NodeFlags::IS_CONNECTED));
703 };
704 let author_styles = unsafe { self.unsafe_get().author_styles.borrow_mut_for_layout() };
705 if author_styles.stylesheets.dirty() {
706 author_styles.flush(stylist, guard);
707 }
708 }
709}
710
711impl Convert<devtools_traits::ShadowRootMode> for ShadowRootMode {
712 fn convert(self) -> devtools_traits::ShadowRootMode {
713 match self {
714 ShadowRootMode::Open => devtools_traits::ShadowRootMode::Open,
715 ShadowRootMode::Closed => devtools_traits::ShadowRootMode::Closed,
716 }
717 }
718}