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, NoGC};
12use js::rust::{HandleValue, MutableHandleValue};
13use script_bindings::cell::{DomRefCell, RefMut};
14use script_bindings::dom::UnrootedDom;
15use script_bindings::error::{ErrorResult, Fallible};
16use script_bindings::reflector::reflect_dom_object;
17use servo_arc::Arc;
18use style::author_styles::AuthorStyles;
19use style::invalidation::element::restyle_hints::RestyleHint;
20use style::shared_lock::SharedRwLockReadGuard;
21use style::stylesheets::Stylesheet;
22use style::stylist::{CascadeData, Stylist};
23use stylo_atoms::Atom;
24
25use crate::conversions::Convert;
26use crate::css::stylesheet_set::StylesheetSetRef;
27use crate::dom::bindings::codegen::Bindings::ElementBinding::GetHTMLOptions;
28use crate::dom::bindings::codegen::Bindings::HTMLSlotElementBinding::HTMLSlotElement_Binding::HTMLSlotElementMethods;
29use crate::dom::bindings::codegen::Bindings::SanitizerBinding::{
30 SetHTMLOptions, SetHTMLUnsafeOptions,
31};
32use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::ShadowRoot_Binding::ShadowRootMethods;
33use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{
34 ShadowRootMode, SlotAssignmentMode,
35};
36use crate::dom::bindings::codegen::UnionTypes::{
37 TrustedHTMLOrNullIsEmptyString, TrustedHTMLOrString,
38};
39use crate::dom::bindings::frozenarray::CachedFrozenArray;
40use crate::dom::bindings::inheritance::Castable;
41use crate::dom::bindings::num::Finite;
42use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom};
43use crate::dom::bindings::str::DOMString;
44use crate::dom::css::cssstylesheet::CSSStyleSheet;
45use crate::dom::css::stylesheetlist::{StyleSheetList, StyleSheetListOwner};
46use crate::dom::customelementregistry::CustomElementRegistry;
47use crate::dom::document::Document;
48use crate::dom::documentfragment::DocumentFragment;
49use crate::dom::documentorshadowroot::{
50 DocumentOrShadowRoot, ServoStylesheetInDocument, StylesheetSource,
51};
52use crate::dom::element::Element;
53use crate::dom::html::htmlslotelement::HTMLSlotElement;
54use crate::dom::htmldetailselement::DetailsNameGroups;
55use crate::dom::iterators::ShadowIncluding;
56use crate::dom::node::virtualmethods::{VirtualMethods, vtable_for};
57use crate::dom::node::{
58 BindContext, IsShadowTree, Node, NodeDamage, NodeFlags, NodeTraits, UnbindContext,
59 VecPreOrderInsertionHelper,
60};
61use crate::dom::sanitizer::Sanitizer;
62use crate::dom::trustedtypes::trustedhtml::TrustedHTML;
63use crate::dom::types::EventTarget;
64use crate::dom::window::Window;
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 cx: &mut JSContext,
164 host: &Element,
165 document: &Document,
166 mode: ShadowRootMode,
167 slot_assignment_mode: SlotAssignmentMode,
168 clonable: bool,
169 is_user_agent_widget: IsUserAgentWidget,
170 ) -> DomRoot<ShadowRoot> {
171 reflect_dom_object(
172 cx,
173 Box::new(ShadowRoot::new_inherited(
174 host,
175 document,
176 mode,
177 slot_assignment_mode,
178 clonable,
179 is_user_agent_widget,
180 )),
181 document.window(),
182 )
183 }
184
185 pub(crate) fn host_unrooted<'a>(&self, no_gc: &'a NoGC) -> UnrootedDom<'a, Element> {
186 self.upcast::<DocumentFragment>()
187 .host_unrooted(no_gc)
188 .expect("ShadowRoot always has an element as host")
189 }
190
191 pub(crate) fn owner_doc(&self) -> &Document {
192 &self.document
193 }
194
195 pub(crate) fn stylesheet_count(&self) -> usize {
196 self.author_styles.borrow().stylesheets.len()
197 }
198
199 pub(crate) fn stylesheet_at(
200 &self,
201 cx: &mut JSContext,
202 index: usize,
203 ) -> Option<DomRoot<CSSStyleSheet>> {
204 let stylesheets = &self.author_styles.borrow().stylesheets;
205
206 stylesheets
207 .get(index)
208 .and_then(|s| s.owner.get_cssom_object(cx))
209 }
210
211 #[cfg_attr(crown, expect(crown::unrooted_must_root))] pub(crate) fn add_owned_stylesheet(&self, owner_node: &Element, sheet: Arc<Stylesheet>) {
218 let stylesheets = &mut self.author_styles.borrow_mut().stylesheets;
219
220 let insertion_point = stylesheets
222 .iter()
223 .find(|sheet_in_shadow| {
224 match &sheet_in_shadow.owner {
225 StylesheetSource::Element(other_node) => {
226 owner_node.upcast::<Node>().is_before(other_node.upcast())
227 },
228 StylesheetSource::Constructed(_) => true,
231 }
232 })
233 .cloned();
234
235 DocumentOrShadowRoot::add_stylesheet(
236 StylesheetSource::Element(Dom::from_ref(owner_node)),
237 StylesheetSetRef::Author(stylesheets),
238 sheet,
239 insertion_point,
240 self.document.style_shared_author_lock(),
241 );
242 }
243
244 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
246 pub(crate) fn append_constructed_stylesheet(&self, cssom_stylesheet: &CSSStyleSheet) {
247 debug_assert!(cssom_stylesheet.is_constructed());
248
249 let stylesheets = &mut self.author_styles.borrow_mut().stylesheets;
250 let sheet = cssom_stylesheet.style_stylesheet().clone();
251
252 let insertion_point = stylesheets.iter().last().cloned();
253
254 DocumentOrShadowRoot::add_stylesheet(
255 StylesheetSource::Constructed(Dom::from_ref(cssom_stylesheet)),
256 StylesheetSetRef::Author(stylesheets),
257 sheet,
258 insertion_point,
259 self.document.style_shared_author_lock(),
260 );
261 }
262
263 #[cfg_attr(crown, expect(crown::unrooted_must_root))] pub(crate) fn remove_stylesheet(&self, owner: StylesheetSource, s: &Arc<Stylesheet>) {
266 DocumentOrShadowRoot::remove_stylesheet(
267 owner,
268 s,
269 StylesheetSetRef::Author(&mut self.author_styles.borrow_mut().stylesheets),
270 )
271 }
272
273 pub(crate) fn invalidate_stylesheets(&self, no_gc: &NoGC) {
274 self.document.invalidate_shadow_roots_stylesheets();
275 self.author_styles.borrow_mut().stylesheets.force_dirty();
276 self.Host().upcast::<Node>().dirty(no_gc, NodeDamage::Style);
278
279 let mut restyle = self.document.ensure_pending_restyle(&self.Host());
282 restyle.hint.insert(RestyleHint::restyle_subtree());
283 }
284
285 pub(crate) fn unregister_element_id(&self, id: &Atom) {
288 self.document_fragment.id_map().remove(id);
289 }
290
291 pub(crate) fn register_element_id(&self, element: &Element, id: &Atom) {
293 self.document_fragment.id_map().add(id, element)
294 }
295
296 pub(crate) fn register_slot(&self, slot: &HTMLSlotElement) {
297 debug!("Registering slot with name={:?}", slot.Name().str());
298
299 let mut slots = self.slots.borrow_mut();
300
301 let slots_with_the_same_name = slots.entry(slot.Name()).or_default();
302
303 slots_with_the_same_name.insert_pre_order(slot, self.upcast::<Node>());
305 }
306
307 pub(crate) fn unregister_slot(&self, name: DOMString, slot: &HTMLSlotElement) {
308 debug!("Unregistering slot with name={:?}", name.str());
309
310 let mut slots = self.slots.borrow_mut();
311 let Entry::Occupied(mut entry) = slots.entry(name) else {
312 panic!("slot is not registered");
313 };
314 entry.get_mut().retain(|s| slot != &**s);
315 }
316
317 pub(crate) fn slot_for_name(&self, name: &DOMString) -> Option<DomRoot<HTMLSlotElement>> {
319 self.slots
320 .borrow()
321 .get(name)
322 .and_then(|slots| slots.first())
323 .map(|slot| slot.as_rooted())
324 }
325
326 pub(crate) fn has_slot_descendants(&self) -> bool {
327 !self.slots.borrow().is_empty()
328 }
329
330 pub(crate) fn set_available_to_element_internals(&self, value: bool) {
331 self.available_to_element_internals.set(value);
332 }
333
334 pub(crate) fn is_available_to_element_internals(&self) -> bool {
336 self.available_to_element_internals.get()
337 }
338
339 pub(crate) fn is_user_agent_widget(&self) -> bool {
340 self.is_user_agent_widget
341 }
342
343 pub(crate) fn set_declarative(&self, declarative: bool) {
344 self.declarative.set(declarative);
345 }
346
347 pub(crate) fn is_declarative(&self) -> bool {
348 self.declarative.get()
349 }
350
351 pub(crate) fn shadow_root_mode(&self) -> ShadowRootMode {
352 self.mode
353 }
354
355 pub(crate) fn set_serializable(&self, serializable: bool) {
356 self.serializable.set(serializable);
357 }
358
359 pub(crate) fn set_delegates_focus(&self, delegates_focus: bool) {
360 self.delegates_focus.set(delegates_focus);
361 }
362
363 pub(crate) fn details_name_groups<'a: 'b, 'b>(
364 &'a self,
365 no_gc: &'b NoGC,
366 ) -> RefMut<'b, DetailsNameGroups> {
367 RefMut::map(
368 self.details_name_groups.safe_borrow_mut(no_gc),
369 |details_name_groups| details_name_groups.get_or_insert_default(),
370 )
371 }
372
373 pub(crate) fn custom_element_registry(&self) -> Option<DomRoot<CustomElementRegistry>> {
374 self.document_or_shadow_root.custom_element_registry()
375 }
376
377 pub(crate) fn set_custom_element_registry(&self, registry: Option<&CustomElementRegistry>) {
378 self.document_or_shadow_root
379 .set_custom_element_registry(registry);
380 }
381}
382
383impl ShadowRootMethods<crate::DomTypeHolder> for ShadowRoot {
384 fn GetActiveElement(&self) -> Option<DomRoot<Element>> {
386 self.document_or_shadow_root.active_element(self.upcast())
387 }
388
389 fn GetCustomElementRegistry(&self) -> Option<DomRoot<CustomElementRegistry>> {
391 self.custom_element_registry()
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 self.upcast(),
400 x,
401 y,
402 None,
403 self.document.has_browsing_context(),
404 ) {
405 Some(e) => {
406 let retargeted_node = e.upcast::<EventTarget>().retarget(self.upcast());
407 retargeted_node.downcast::<Element>().map(DomRoot::from_ref)
408 },
409 None => None,
410 }
411 }
412
413 fn ElementsFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Vec<DomRoot<Element>> {
415 let mut elements = Vec::new();
418 for e in self
419 .document_or_shadow_root
420 .elements_from_point(
421 self.upcast(),
422 x,
423 y,
424 None,
425 self.document.has_browsing_context(),
426 )
427 .iter()
428 {
429 let retargeted_node = e.upcast::<EventTarget>().retarget(self.upcast());
430 if let Some(element) = retargeted_node.downcast::<Element>().map(DomRoot::from_ref) {
431 elements.push(element);
432 }
433 }
434 elements
435 }
436
437 fn Mode(&self) -> ShadowRootMode {
439 self.mode
440 }
441
442 fn DelegatesFocus(&self) -> bool {
444 self.delegates_focus.get()
445 }
446
447 fn Clonable(&self) -> bool {
449 self.clonable
450 }
451
452 fn Serializable(&self) -> bool {
454 self.serializable.get()
455 }
456
457 fn Host(&self) -> DomRoot<Element> {
459 self.upcast::<DocumentFragment>()
460 .host()
461 .expect("ShadowRoot always has an element as host")
462 }
463
464 fn StyleSheets(&self, cx: &mut JSContext) -> DomRoot<StyleSheetList> {
466 self.stylesheet_list.or_init(|| {
467 StyleSheetList::new(
468 cx,
469 &self.window,
470 StyleSheetListOwner::ShadowRoot(Dom::from_ref(self)),
471 )
472 })
473 }
474
475 fn GetHTML(&self, cx: &mut JSContext, options: &GetHTMLOptions) -> DOMString {
477 self.upcast::<Node>().html_serialize(
480 cx,
481 TraversalScope::ChildrenOnly(None),
482 options.serializableShadowRoots,
483 options.shadowRoots.clone(),
484 )
485 }
486
487 fn GetInnerHTML(&self, cx: &mut JSContext) -> Fallible<TrustedHTMLOrNullIsEmptyString> {
489 self.upcast::<Node>()
492 .fragment_serialization_algorithm(cx, true)
493 .map(TrustedHTMLOrNullIsEmptyString::NullIsEmptyString)
494 }
495
496 fn SetInnerHTML(
498 &self,
499 cx: &mut JSContext,
500 value: TrustedHTMLOrNullIsEmptyString,
501 ) -> ErrorResult {
502 let value = TrustedHTML::get_trusted_type_compliant_string(
505 cx,
506 &self.owner_global(),
507 value.convert(),
508 "ShadowRoot innerHTML",
509 )?;
510
511 let context = self.Host();
513
514 let frag = context.parse_fragment(value, cx)?;
520
521 Node::replace_all(cx, Some(frag.upcast()), self.upcast());
523 Ok(())
524 }
525
526 fn SlotAssignment(&self) -> SlotAssignmentMode {
528 self.slot_assignment_mode
529 }
530
531 fn SetHTMLUnsafe(
533 &self,
534 cx: &mut JSContext,
535 value: TrustedHTMLOrString,
536 options: &SetHTMLUnsafeOptions,
537 ) -> ErrorResult {
538 let compliant_html = TrustedHTML::get_trusted_type_compliant_string(
542 cx,
543 &self.owner_global(),
544 value,
545 "ShadowRoot setHTMLUnsafe",
546 )?;
547
548 Sanitizer::set_and_filter_html(
551 cx,
552 self.upcast(),
553 &self.Host(),
554 compliant_html,
555 options,
556 false,
557 )?;
558
559 Ok(())
560 }
561
562 fn SetHTML(
564 &self,
565 cx: &mut JSContext,
566 html: DOMString,
567 options: &SetHTMLOptions,
568 ) -> ErrorResult {
569 let target = self.upcast::<Node>();
574 let context_element = self.Host();
575 Sanitizer::set_and_filter_html(cx, target, &context_element, html, options, true)
576 }
577
578 event_handler!(slotchange, GetOnslotchange, SetOnslotchange);
580
581 fn AdoptedStyleSheets(&self, cx: &mut JSContext, retval: MutableHandleValue) {
583 self.adopted_stylesheets_frozen_types.get_or_init(
584 cx,
585 || {
586 self.adopted_stylesheets
587 .borrow()
588 .clone()
589 .iter()
590 .map(|sheet| sheet.as_rooted())
591 .collect()
592 },
593 retval,
594 );
595 }
596
597 fn SetAdoptedStyleSheets(&self, cx: &mut JSContext, val: HandleValue) -> ErrorResult {
599 let result = DocumentOrShadowRoot::set_adopted_stylesheet_from_jsval(
600 cx,
601 &self.adopted_stylesheets,
602 val,
603 &StyleSheetListOwner::ShadowRoot(Dom::from_ref(self)),
604 );
605
606 if result.is_ok() {
607 if self.author_styles.borrow().stylesheets.dirty() {
608 self.invalidate_stylesheets(cx.no_gc());
609 }
610
611 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}