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_with_cx;
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::dom::bindings::codegen::Bindings::ElementBinding::GetHTMLOptions;
27use crate::dom::bindings::codegen::Bindings::HTMLSlotElementBinding::HTMLSlotElement_Binding::HTMLSlotElementMethods;
28use crate::dom::bindings::codegen::Bindings::SanitizerBinding::{
29 SetHTMLOptions, SetHTMLUnsafeOptions,
30};
31use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::ShadowRoot_Binding::ShadowRootMethods;
32use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{
33 ShadowRootMode, SlotAssignmentMode,
34};
35use crate::dom::bindings::codegen::UnionTypes::{
36 TrustedHTMLOrNullIsEmptyString, TrustedHTMLOrString,
37};
38use crate::dom::bindings::frozenarray::CachedFrozenArray;
39use crate::dom::bindings::inheritance::Castable;
40use crate::dom::bindings::num::Finite;
41use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom};
42use crate::dom::bindings::str::DOMString;
43use crate::dom::css::cssstylesheet::CSSStyleSheet;
44use crate::dom::css::stylesheetlist::{StyleSheetList, StyleSheetListOwner};
45use crate::dom::customelementregistry::CustomElementRegistry;
46use crate::dom::document::Document;
47use crate::dom::documentfragment::DocumentFragment;
48use crate::dom::documentorshadowroot::{
49 DocumentOrShadowRoot, ServoStylesheetInDocument, StylesheetSource,
50};
51use crate::dom::element::Element;
52use crate::dom::html::htmlslotelement::HTMLSlotElement;
53use crate::dom::htmldetailselement::DetailsNameGroups;
54use crate::dom::iterators::ShadowIncluding;
55use crate::dom::node::virtualmethods::{VirtualMethods, vtable_for};
56use crate::dom::node::{
57 BindContext, IsShadowTree, Node, NodeDamage, NodeFlags, NodeTraits, UnbindContext,
58 VecPreOrderInsertionHelper,
59};
60use crate::dom::sanitizer::Sanitizer;
61use crate::dom::trustedtypes::trustedhtml::TrustedHTML;
62use crate::dom::types::EventTarget;
63use crate::dom::window::Window;
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 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_with_cx(
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 cx,
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(&self) -> RefMut<'_, DetailsNameGroups> {
364 RefMut::map(
365 self.details_name_groups.borrow_mut(),
366 |details_name_groups| details_name_groups.get_or_insert_default(),
367 )
368 }
369
370 pub(crate) fn custom_element_registry(&self) -> Option<DomRoot<CustomElementRegistry>> {
371 self.document_or_shadow_root.custom_element_registry()
372 }
373
374 pub(crate) fn set_custom_element_registry(&self, registry: &CustomElementRegistry) {
375 self.document_or_shadow_root
376 .set_custom_element_registry(Some(registry));
377 }
378}
379
380impl ShadowRootMethods<crate::DomTypeHolder> for ShadowRoot {
381 fn GetActiveElement(&self) -> Option<DomRoot<Element>> {
383 self.document_or_shadow_root.active_element(self.upcast())
384 }
385
386 fn GetCustomElementRegistry(&self) -> Option<DomRoot<CustomElementRegistry>> {
388 self.custom_element_registry()
389 }
390
391 fn ElementFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Option<DomRoot<Element>> {
393 match self.document_or_shadow_root.element_from_point(
396 self.upcast(),
397 x,
398 y,
399 None,
400 self.document.has_browsing_context(),
401 ) {
402 Some(e) => {
403 let retargeted_node = e.upcast::<EventTarget>().retarget(self.upcast());
404 retargeted_node.downcast::<Element>().map(DomRoot::from_ref)
405 },
406 None => None,
407 }
408 }
409
410 fn ElementsFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Vec<DomRoot<Element>> {
412 let mut elements = Vec::new();
415 for e in self
416 .document_or_shadow_root
417 .elements_from_point(
418 self.upcast(),
419 x,
420 y,
421 None,
422 self.document.has_browsing_context(),
423 )
424 .iter()
425 {
426 let retargeted_node = e.upcast::<EventTarget>().retarget(self.upcast());
427 if let Some(element) = retargeted_node.downcast::<Element>().map(DomRoot::from_ref) {
428 elements.push(element);
429 }
430 }
431 elements
432 }
433
434 fn Mode(&self) -> ShadowRootMode {
436 self.mode
437 }
438
439 fn DelegatesFocus(&self) -> bool {
441 self.delegates_focus.get()
442 }
443
444 fn Clonable(&self) -> bool {
446 self.clonable
447 }
448
449 fn Serializable(&self) -> bool {
451 self.serializable.get()
452 }
453
454 fn Host(&self) -> DomRoot<Element> {
456 self.upcast::<DocumentFragment>()
457 .host()
458 .expect("ShadowRoot always has an element as host")
459 }
460
461 fn StyleSheets(&self, cx: &mut JSContext) -> DomRoot<StyleSheetList> {
463 self.stylesheet_list.or_init(|| {
464 StyleSheetList::new(
465 cx,
466 &self.window,
467 StyleSheetListOwner::ShadowRoot(Dom::from_ref(self)),
468 )
469 })
470 }
471
472 fn GetHTML(&self, cx: &mut JSContext, options: &GetHTMLOptions) -> DOMString {
474 self.upcast::<Node>().html_serialize(
477 cx,
478 TraversalScope::ChildrenOnly(None),
479 options.serializableShadowRoots,
480 options.shadowRoots.clone(),
481 )
482 }
483
484 fn GetInnerHTML(&self, cx: &mut JSContext) -> Fallible<TrustedHTMLOrNullIsEmptyString> {
486 self.upcast::<Node>()
489 .fragment_serialization_algorithm(cx, true)
490 .map(TrustedHTMLOrNullIsEmptyString::NullIsEmptyString)
491 }
492
493 fn SetInnerHTML(
495 &self,
496 cx: &mut JSContext,
497 value: TrustedHTMLOrNullIsEmptyString,
498 ) -> ErrorResult {
499 let value = TrustedHTML::get_trusted_type_compliant_string(
502 cx,
503 &self.owner_global(),
504 value.convert(),
505 "ShadowRoot innerHTML",
506 )?;
507
508 let context = self.Host();
510
511 let frag = context.parse_fragment(value, cx)?;
517
518 Node::replace_all(cx, Some(frag.upcast()), self.upcast());
520 Ok(())
521 }
522
523 fn SlotAssignment(&self) -> SlotAssignmentMode {
525 self.slot_assignment_mode
526 }
527
528 fn SetHTMLUnsafe(
530 &self,
531 cx: &mut JSContext,
532 value: TrustedHTMLOrString,
533 options: &SetHTMLUnsafeOptions,
534 ) -> ErrorResult {
535 let compliant_html = TrustedHTML::get_trusted_type_compliant_string(
539 cx,
540 &self.owner_global(),
541 value,
542 "ShadowRoot setHTMLUnsafe",
543 )?;
544
545 Sanitizer::set_and_filter_html(
548 cx,
549 self.upcast(),
550 &self.Host(),
551 compliant_html,
552 options,
553 false,
554 )?;
555
556 Ok(())
557 }
558
559 fn SetHTML(
561 &self,
562 cx: &mut JSContext,
563 html: DOMString,
564 options: &SetHTMLOptions,
565 ) -> ErrorResult {
566 let target = self.upcast::<Node>();
571 let context_element = self.Host();
572 Sanitizer::set_and_filter_html(cx, target, &context_element, html, options, true)
573 }
574
575 event_handler!(slotchange, GetOnslotchange, SetOnslotchange);
577
578 fn AdoptedStyleSheets(&self, cx: &mut JSContext, retval: MutableHandleValue) {
580 self.adopted_stylesheets_frozen_types.get_or_init(
581 cx,
582 || {
583 self.adopted_stylesheets
584 .borrow()
585 .clone()
586 .iter()
587 .map(|sheet| sheet.as_rooted())
588 .collect()
589 },
590 retval,
591 );
592 }
593
594 fn SetAdoptedStyleSheets(&self, cx: &mut JSContext, val: HandleValue) -> ErrorResult {
596 let result = DocumentOrShadowRoot::set_adopted_stylesheet_from_jsval(
597 cx,
598 &self.adopted_stylesheets,
599 val,
600 &StyleSheetListOwner::ShadowRoot(Dom::from_ref(self)),
601 );
602
603 if result.is_ok() {
604 if self.author_styles.borrow().stylesheets.dirty() {
605 self.invalidate_stylesheets(cx.no_gc());
606 }
607
608 self.adopted_stylesheets_frozen_types.clear();
610 }
611
612 result
613 }
614
615 fn GetFullscreenElement(&self) -> Option<DomRoot<Element>> {
617 DocumentOrShadowRoot::get_fullscreen_element(
618 self.upcast::<Node>(),
619 self.document.fullscreen_element(),
620 )
621 }
622}
623
624impl VirtualMethods for ShadowRoot {
625 fn super_type(&self) -> Option<&dyn VirtualMethods> {
626 Some(self.upcast::<DocumentFragment>() as &dyn VirtualMethods)
627 }
628
629 fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
630 if let Some(s) = self.super_type() {
631 s.bind_to_tree(cx, context);
632 }
633
634 if context.tree_connected {
637 let document = self.owner_document();
638 document.register_shadow_root(self);
639 }
640
641 let shadow_root = self.upcast::<Node>();
642
643 shadow_root.set_flag(NodeFlags::IS_CONNECTED, context.tree_connected);
644
645 let inner_context = BindContext::new(shadow_root, IsShadowTree::Yes);
646
647 for node in shadow_root.traverse_preorder(ShadowIncluding::No).skip(1) {
649 node.set_flag(NodeFlags::IS_CONNECTED, inner_context.tree_connected);
650
651 debug_assert!(!node.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS));
653 vtable_for(&node).bind_to_tree(cx, &inner_context);
654 }
655 }
656
657 fn unbind_from_tree(&self, cx: &mut JSContext, context: &UnbindContext) {
658 if let Some(s) = self.super_type() {
659 s.unbind_from_tree(cx, context);
660 }
661
662 if context.tree_connected {
663 let document = self.owner_document();
664 document.unregister_shadow_root(self);
665 }
666 }
667}
668
669impl<'dom> LayoutDom<'dom, ShadowRoot> {
670 #[inline]
671 pub(crate) fn get_host_for_layout(self) -> LayoutDom<'dom, Element> {
672 self.upcast::<DocumentFragment>()
673 .shadowroot_host_for_layout()
674 }
675
676 #[inline]
677 #[expect(unsafe_code)]
678 pub(crate) fn get_style_data_for_layout(self) -> &'dom CascadeData {
679 fn is_sync<T: Sync>() {}
680 let _ = is_sync::<CascadeData>;
681 unsafe { &self.unsafe_get().author_styles.borrow_for_layout().data }
682 }
683
684 #[inline]
685 pub(crate) fn is_user_agent_widget(&self) -> bool {
686 self.unsafe_get().is_user_agent_widget()
687 }
688
689 #[inline]
692 #[expect(unsafe_code)]
693 pub(crate) unsafe fn flush_stylesheets_for_layout(
694 self,
695 stylist: &mut Stylist,
696 guard: &SharedRwLockReadGuard,
697 ) {
698 unsafe {
699 debug_assert!(self.upcast::<Node>().get_flag(NodeFlags::IS_CONNECTED));
700 };
701 let author_styles = unsafe { self.unsafe_get().author_styles.borrow_mut_for_layout() };
702 if author_styles.stylesheets.dirty() {
703 author_styles.flush(stylist, guard);
704 }
705 }
706}
707
708impl Convert<devtools_traits::ShadowRootMode> for ShadowRootMode {
709 fn convert(self) -> devtools_traits::ShadowRootMode {
710 match self {
711 ShadowRootMode::Open => devtools_traits::ShadowRootMode::Open,
712 ShadowRootMode::Closed => devtools_traits::ShadowRootMode::Closed,
713 }
714 }
715}