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_with_cx;
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::stylesheet_set::StylesheetSetRef;
64
65#[derive(JSTraceable, MallocSizeOf, PartialEq)]
67pub(crate) enum IsUserAgentWidget {
68 No,
69 Yes,
70}
71
72#[dom_struct]
74pub(crate) struct ShadowRoot {
75 document_fragment: DocumentFragment,
77 document_or_shadow_root: DocumentOrShadowRoot,
78 document: Dom<Document>,
79 #[custom_trace]
81 author_styles: DomRefCell<AuthorStyles<ServoStylesheetInDocument>>,
82 stylesheet_list: MutNullableDom<StyleSheetList>,
83 window: Dom<Window>,
84
85 mode: ShadowRootMode,
87
88 slot_assignment_mode: SlotAssignmentMode,
90
91 clonable: bool,
93
94 available_to_element_internals: Cell<bool>,
96
97 slots: DomRefCell<HashMap<DOMString, Vec<Dom<HTMLSlotElement>>>>,
98
99 is_user_agent_widget: bool,
100
101 declarative: Cell<bool>,
103
104 serializable: Cell<bool>,
106
107 delegates_focus: Cell<bool>,
109
110 adopted_stylesheets: DomRefCell<Vec<Dom<CSSStyleSheet>>>,
113
114 #[ignore_malloc_size_of = "mozjs"]
116 adopted_stylesheets_frozen_types: CachedFrozenArray,
117
118 details_name_groups: DomRefCell<Option<DetailsNameGroups>>,
119}
120
121impl ShadowRoot {
122 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
123 fn new_inherited(
124 host: &Element,
125 document: &Document,
126 mode: ShadowRootMode,
127 slot_assignment_mode: SlotAssignmentMode,
128 clonable: bool,
129 is_user_agent_widget: IsUserAgentWidget,
130 ) -> ShadowRoot {
131 let document_fragment = DocumentFragment::new_inherited(document, Some(host));
132 let node = document_fragment.upcast::<Node>();
133 node.set_flag(NodeFlags::IS_IN_SHADOW_TREE, true);
134 node.set_flag(
135 NodeFlags::IS_CONNECTED,
136 host.upcast::<Node>().is_connected(),
137 );
138
139 ShadowRoot {
140 document_fragment,
141 document_or_shadow_root: DocumentOrShadowRoot::new(document.window()),
142 document: Dom::from_ref(document),
143 author_styles: DomRefCell::new(AuthorStyles::new()),
144 stylesheet_list: MutNullableDom::new(None),
145 window: Dom::from_ref(document.window()),
146 mode,
147 slot_assignment_mode,
148 clonable,
149 available_to_element_internals: Cell::new(false),
150 slots: Default::default(),
151 is_user_agent_widget: is_user_agent_widget == IsUserAgentWidget::Yes,
152 declarative: Cell::new(false),
153 serializable: Cell::new(false),
154 delegates_focus: Cell::new(false),
155 adopted_stylesheets: Default::default(),
156 adopted_stylesheets_frozen_types: CachedFrozenArray::new(),
157 details_name_groups: Default::default(),
158 }
159 }
160
161 pub(crate) fn new(
162 cx: &mut JSContext,
163 host: &Element,
164 document: &Document,
165 mode: ShadowRootMode,
166 slot_assignment_mode: SlotAssignmentMode,
167 clonable: bool,
168 is_user_agent_widget: IsUserAgentWidget,
169 ) -> DomRoot<ShadowRoot> {
170 reflect_dom_object_with_cx(
171 Box::new(ShadowRoot::new_inherited(
172 host,
173 document,
174 mode,
175 slot_assignment_mode,
176 clonable,
177 is_user_agent_widget,
178 )),
179 document.window(),
180 cx,
181 )
182 }
183
184 pub(crate) fn owner_doc(&self) -> &Document {
185 &self.document
186 }
187
188 pub(crate) fn stylesheet_count(&self) -> usize {
189 self.author_styles.borrow().stylesheets.len()
190 }
191
192 pub(crate) fn stylesheet_at(
193 &self,
194 cx: &mut JSContext,
195 index: usize,
196 ) -> Option<DomRoot<CSSStyleSheet>> {
197 let stylesheets = &self.author_styles.borrow().stylesheets;
198
199 stylesheets
200 .get(index)
201 .and_then(|s| s.owner.get_cssom_object(cx))
202 }
203
204 #[cfg_attr(crown, expect(crown::unrooted_must_root))] pub(crate) fn add_owned_stylesheet(&self, owner_node: &Element, sheet: Arc<Stylesheet>) {
211 let stylesheets = &mut self.author_styles.borrow_mut().stylesheets;
212
213 let insertion_point = stylesheets
215 .iter()
216 .find(|sheet_in_shadow| {
217 match &sheet_in_shadow.owner {
218 StylesheetSource::Element(other_node) => {
219 owner_node.upcast::<Node>().is_before(other_node.upcast())
220 },
221 StylesheetSource::Constructed(_) => true,
224 }
225 })
226 .cloned();
227
228 DocumentOrShadowRoot::add_stylesheet(
229 StylesheetSource::Element(Dom::from_ref(owner_node)),
230 StylesheetSetRef::Author(stylesheets),
231 sheet,
232 insertion_point,
233 self.document.style_shared_author_lock(),
234 );
235 }
236
237 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
239 pub(crate) fn append_constructed_stylesheet(&self, cssom_stylesheet: &CSSStyleSheet) {
240 debug_assert!(cssom_stylesheet.is_constructed());
241
242 let stylesheets = &mut self.author_styles.borrow_mut().stylesheets;
243 let sheet = cssom_stylesheet.style_stylesheet().clone();
244
245 let insertion_point = stylesheets.iter().last().cloned();
246
247 DocumentOrShadowRoot::add_stylesheet(
248 StylesheetSource::Constructed(Dom::from_ref(cssom_stylesheet)),
249 StylesheetSetRef::Author(stylesheets),
250 sheet,
251 insertion_point,
252 self.document.style_shared_author_lock(),
253 );
254 }
255
256 #[cfg_attr(crown, expect(crown::unrooted_must_root))] pub(crate) fn remove_stylesheet(&self, owner: StylesheetSource, s: &Arc<Stylesheet>) {
259 DocumentOrShadowRoot::remove_stylesheet(
260 owner,
261 s,
262 StylesheetSetRef::Author(&mut self.author_styles.borrow_mut().stylesheets),
263 )
264 }
265
266 pub(crate) fn invalidate_stylesheets(&self) {
267 self.document.invalidate_shadow_roots_stylesheets();
268 self.author_styles.borrow_mut().stylesheets.force_dirty();
269 self.Host().upcast::<Node>().dirty(NodeDamage::Style);
271
272 let mut restyle = self.document.ensure_pending_restyle(&self.Host());
275 restyle.hint.insert(RestyleHint::restyle_subtree());
276 }
277
278 pub(crate) fn unregister_element_id(&self, id: &Atom) {
281 self.document_fragment.id_map().remove(id);
282 }
283
284 pub(crate) fn register_element_id(&self, element: &Element, id: &Atom) {
286 self.document_fragment.id_map().add(id, element)
287 }
288
289 pub(crate) fn register_slot(&self, slot: &HTMLSlotElement) {
290 debug!("Registering slot with name={:?}", slot.Name().str());
291
292 let mut slots = self.slots.borrow_mut();
293
294 let slots_with_the_same_name = slots.entry(slot.Name()).or_default();
295
296 slots_with_the_same_name.insert_pre_order(slot, self.upcast::<Node>());
298 }
299
300 pub(crate) fn unregister_slot(&self, name: DOMString, slot: &HTMLSlotElement) {
301 debug!("Unregistering slot with name={:?}", name.str());
302
303 let mut slots = self.slots.borrow_mut();
304 let Entry::Occupied(mut entry) = slots.entry(name) else {
305 panic!("slot is not registered");
306 };
307 entry.get_mut().retain(|s| slot != &**s);
308 }
309
310 pub(crate) fn slot_for_name(&self, name: &DOMString) -> Option<DomRoot<HTMLSlotElement>> {
312 self.slots
313 .borrow()
314 .get(name)
315 .and_then(|slots| slots.first())
316 .map(|slot| slot.as_rooted())
317 }
318
319 pub(crate) fn has_slot_descendants(&self) -> bool {
320 !self.slots.borrow().is_empty()
321 }
322
323 pub(crate) fn set_available_to_element_internals(&self, value: bool) {
324 self.available_to_element_internals.set(value);
325 }
326
327 pub(crate) fn is_available_to_element_internals(&self) -> bool {
329 self.available_to_element_internals.get()
330 }
331
332 pub(crate) fn is_user_agent_widget(&self) -> bool {
333 self.is_user_agent_widget
334 }
335
336 pub(crate) fn set_declarative(&self, declarative: bool) {
337 self.declarative.set(declarative);
338 }
339
340 pub(crate) fn is_declarative(&self) -> bool {
341 self.declarative.get()
342 }
343
344 pub(crate) fn shadow_root_mode(&self) -> ShadowRootMode {
345 self.mode
346 }
347
348 pub(crate) fn set_serializable(&self, serializable: bool) {
349 self.serializable.set(serializable);
350 }
351
352 pub(crate) fn set_delegates_focus(&self, delegates_focus: bool) {
353 self.delegates_focus.set(delegates_focus);
354 }
355
356 pub(crate) fn details_name_groups(&self) -> RefMut<'_, DetailsNameGroups> {
357 RefMut::map(
358 self.details_name_groups.borrow_mut(),
359 |details_name_groups| details_name_groups.get_or_insert_default(),
360 )
361 }
362
363 pub(crate) fn custom_element_registry(&self) -> Option<DomRoot<CustomElementRegistry>> {
364 self.document_or_shadow_root.custom_element_registry()
365 }
366
367 pub(crate) fn set_custom_element_registry(&self, registry: &CustomElementRegistry) {
368 self.document_or_shadow_root
369 .set_custom_element_registry(Some(registry));
370 }
371}
372
373impl ShadowRootMethods<crate::DomTypeHolder> for ShadowRoot {
374 fn GetActiveElement(&self) -> Option<DomRoot<Element>> {
376 self.document_or_shadow_root.active_element(self.upcast())
377 }
378
379 fn GetCustomElementRegistry(&self) -> Option<DomRoot<CustomElementRegistry>> {
381 self.custom_element_registry()
382 }
383
384 fn ElementFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Option<DomRoot<Element>> {
386 match self.document_or_shadow_root.element_from_point(
389 self.upcast(),
390 x,
391 y,
392 None,
393 self.document.has_browsing_context(),
394 ) {
395 Some(e) => {
396 let retargeted_node = e.upcast::<EventTarget>().retarget(self.upcast());
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(
411 self.upcast(),
412 x,
413 y,
414 None,
415 self.document.has_browsing_context(),
416 )
417 .iter()
418 {
419 let retargeted_node = e.upcast::<EventTarget>().retarget(self.upcast());
420 if let Some(element) = retargeted_node.downcast::<Element>().map(DomRoot::from_ref) {
421 elements.push(element);
422 }
423 }
424 elements
425 }
426
427 fn Mode(&self) -> ShadowRootMode {
429 self.mode
430 }
431
432 fn DelegatesFocus(&self) -> bool {
434 self.delegates_focus.get()
435 }
436
437 fn Clonable(&self) -> bool {
439 self.clonable
440 }
441
442 fn Serializable(&self) -> bool {
444 self.serializable.get()
445 }
446
447 fn Host(&self) -> DomRoot<Element> {
449 self.upcast::<DocumentFragment>()
450 .host()
451 .expect("ShadowRoot always has an element as host")
452 }
453
454 fn StyleSheets(&self, cx: &mut JSContext) -> DomRoot<StyleSheetList> {
456 self.stylesheet_list.or_init(|| {
457 StyleSheetList::new(
458 cx,
459 &self.window,
460 StyleSheetListOwner::ShadowRoot(Dom::from_ref(self)),
461 )
462 })
463 }
464
465 fn GetHTML(&self, cx: &mut JSContext, options: &GetHTMLOptions) -> DOMString {
467 self.upcast::<Node>().html_serialize(
470 cx,
471 TraversalScope::ChildrenOnly(None),
472 options.serializableShadowRoots,
473 options.shadowRoots.clone(),
474 )
475 }
476
477 fn GetInnerHTML(&self, cx: &mut JSContext) -> Fallible<TrustedHTMLOrNullIsEmptyString> {
479 self.upcast::<Node>()
482 .fragment_serialization_algorithm(cx, true)
483 .map(TrustedHTMLOrNullIsEmptyString::NullIsEmptyString)
484 }
485
486 fn SetInnerHTML(
488 &self,
489 cx: &mut JSContext,
490 value: TrustedHTMLOrNullIsEmptyString,
491 ) -> ErrorResult {
492 let value = TrustedHTML::get_trusted_type_compliant_string(
495 cx,
496 &self.owner_global(),
497 value.convert(),
498 "ShadowRoot innerHTML",
499 )?;
500
501 let context = self.Host();
503
504 let frag = context.parse_fragment(value, cx)?;
510
511 Node::replace_all(cx, Some(frag.upcast()), self.upcast());
513 Ok(())
514 }
515
516 fn SlotAssignment(&self) -> SlotAssignmentMode {
518 self.slot_assignment_mode
519 }
520
521 fn SetHTMLUnsafe(
523 &self,
524 cx: &mut JSContext,
525 value: TrustedHTMLOrString,
526 options: &SetHTMLUnsafeOptions,
527 ) -> ErrorResult {
528 let compliant_html = TrustedHTML::get_trusted_type_compliant_string(
532 cx,
533 &self.owner_global(),
534 value,
535 "ShadowRoot setHTMLUnsafe",
536 )?;
537
538 Sanitizer::set_and_filter_html(
541 cx,
542 self.upcast(),
543 &self.Host(),
544 compliant_html,
545 options,
546 false,
547 )?;
548
549 Ok(())
550 }
551
552 fn SetHTML(
554 &self,
555 cx: &mut JSContext,
556 html: DOMString,
557 options: &SetHTMLOptions,
558 ) -> ErrorResult {
559 let target = self.upcast::<Node>();
564 let context_element = self.Host();
565 Sanitizer::set_and_filter_html(cx, target, &context_element, html, options, true)
566 }
567
568 event_handler!(slotchange, GetOnslotchange, SetOnslotchange);
570
571 fn AdoptedStyleSheets(&self, cx: &mut JSContext, retval: MutableHandleValue) {
573 self.adopted_stylesheets_frozen_types.get_or_init(
574 cx,
575 || {
576 self.adopted_stylesheets
577 .borrow()
578 .clone()
579 .iter()
580 .map(|sheet| sheet.as_rooted())
581 .collect()
582 },
583 retval,
584 );
585 }
586
587 fn SetAdoptedStyleSheets(&self, cx: &mut JSContext, val: HandleValue) -> ErrorResult {
589 let result = DocumentOrShadowRoot::set_adopted_stylesheet_from_jsval(
590 cx,
591 self.adopted_stylesheets.borrow_mut().as_mut(),
592 val,
593 &StyleSheetListOwner::ShadowRoot(Dom::from_ref(self)),
594 );
595
596 if result.is_ok() {
598 self.adopted_stylesheets_frozen_types.clear();
599 }
600
601 result
602 }
603
604 fn GetFullscreenElement(&self) -> Option<DomRoot<Element>> {
606 DocumentOrShadowRoot::get_fullscreen_element(
607 self.upcast::<Node>(),
608 self.document.fullscreen_element(),
609 )
610 }
611}
612
613impl VirtualMethods for ShadowRoot {
614 fn super_type(&self) -> Option<&dyn VirtualMethods> {
615 Some(self.upcast::<DocumentFragment>() as &dyn VirtualMethods)
616 }
617
618 fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
619 if let Some(s) = self.super_type() {
620 s.bind_to_tree(cx, context);
621 }
622
623 if context.tree_connected {
626 let document = self.owner_document();
627 document.register_shadow_root(self);
628 }
629
630 let shadow_root = self.upcast::<Node>();
631
632 shadow_root.set_flag(NodeFlags::IS_CONNECTED, context.tree_connected);
633
634 let inner_context = BindContext::new(shadow_root, IsShadowTree::Yes);
635
636 for node in shadow_root.traverse_preorder(ShadowIncluding::No).skip(1) {
638 node.set_flag(NodeFlags::IS_CONNECTED, inner_context.tree_connected);
639
640 debug_assert!(!node.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS));
642 vtable_for(&node).bind_to_tree(cx, &inner_context);
643 }
644 }
645
646 fn unbind_from_tree(&self, cx: &mut JSContext, context: &UnbindContext) {
647 if let Some(s) = self.super_type() {
648 s.unbind_from_tree(cx, context);
649 }
650
651 if context.tree_connected {
652 let document = self.owner_document();
653 document.unregister_shadow_root(self);
654 }
655 }
656}
657
658impl<'dom> LayoutDom<'dom, ShadowRoot> {
659 #[inline]
660 pub(crate) fn get_host_for_layout(self) -> LayoutDom<'dom, Element> {
661 self.upcast::<DocumentFragment>()
662 .shadowroot_host_for_layout()
663 }
664
665 #[inline]
666 #[expect(unsafe_code)]
667 pub(crate) fn get_style_data_for_layout(self) -> &'dom CascadeData {
668 fn is_sync<T: Sync>() {}
669 let _ = is_sync::<CascadeData>;
670 unsafe { &self.unsafe_get().author_styles.borrow_for_layout().data }
671 }
672
673 #[inline]
674 pub(crate) fn is_user_agent_widget(&self) -> bool {
675 self.unsafe_get().is_user_agent_widget()
676 }
677
678 #[inline]
681 #[expect(unsafe_code)]
682 pub(crate) unsafe fn flush_stylesheets_for_layout(
683 self,
684 stylist: &mut Stylist,
685 guard: &SharedRwLockReadGuard,
686 ) {
687 unsafe {
688 debug_assert!(self.upcast::<Node>().get_flag(NodeFlags::IS_CONNECTED));
689 };
690 let author_styles = unsafe { self.unsafe_get().author_styles.borrow_mut_for_layout() };
691 if author_styles.stylesheets.dirty() {
692 author_styles.flush(stylist, guard);
693 }
694 }
695}
696
697impl Convert<devtools_traits::ShadowRootMode> for ShadowRootMode {
698 fn convert(self) -> devtools_traits::ShadowRootMode {
699 match self {
700 ShadowRootMode::Open => devtools_traits::ShadowRootMode::Open,
701 ShadowRootMode::Closed => devtools_traits::ShadowRootMode::Closed,
702 }
703 }
704}