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(
218 &self,
219 no_gc: &NoGC,
220 owner_node: &Element,
221 sheet: Arc<Stylesheet>,
222 ) {
223 let stylesheets = &mut self.author_styles.borrow_mut().stylesheets;
224
225 let insertion_point = stylesheets
227 .iter()
228 .find(|sheet_in_shadow| {
229 match &sheet_in_shadow.owner {
230 StylesheetSource::Element(other_node) => owner_node
231 .upcast::<Node>()
232 .is_before(no_gc, other_node.upcast()),
233 StylesheetSource::Constructed(_) => true,
236 }
237 })
238 .cloned();
239
240 DocumentOrShadowRoot::add_stylesheet(
241 StylesheetSource::Element(Dom::from_ref(owner_node)),
242 StylesheetSetRef::Author(stylesheets),
243 sheet,
244 insertion_point,
245 self.document.style_shared_author_lock(),
246 );
247 }
248
249 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
251 pub(crate) fn append_constructed_stylesheet(&self, cssom_stylesheet: &CSSStyleSheet) {
252 debug_assert!(cssom_stylesheet.is_constructed());
253
254 let stylesheets = &mut self.author_styles.borrow_mut().stylesheets;
255 let sheet = cssom_stylesheet.style_stylesheet().clone();
256
257 let insertion_point = stylesheets.iter().last().cloned();
258
259 DocumentOrShadowRoot::add_stylesheet(
260 StylesheetSource::Constructed(Dom::from_ref(cssom_stylesheet)),
261 StylesheetSetRef::Author(stylesheets),
262 sheet,
263 insertion_point,
264 self.document.style_shared_author_lock(),
265 );
266 }
267
268 #[cfg_attr(crown, expect(crown::unrooted_must_root))] pub(crate) fn remove_stylesheet(&self, owner: StylesheetSource, s: &Arc<Stylesheet>) {
271 DocumentOrShadowRoot::remove_stylesheet(
272 owner,
273 s,
274 StylesheetSetRef::Author(&mut self.author_styles.borrow_mut().stylesheets),
275 )
276 }
277
278 pub(crate) fn invalidate_stylesheets(&self, no_gc: &NoGC) {
279 self.document.invalidate_shadow_roots_stylesheets();
280 self.author_styles.borrow_mut().stylesheets.force_dirty();
281 self.Host().upcast::<Node>().dirty(no_gc, NodeDamage::Style);
283
284 let mut restyle = self.document.ensure_pending_restyle(&self.Host());
287 restyle.hint.insert(RestyleHint::restyle_subtree());
288 }
289
290 pub(crate) fn unregister_element_id(&self, id: &Atom) {
293 self.document_fragment.id_map().remove(id);
294 }
295
296 pub(crate) fn register_element_id(&self, element: &Element, id: &Atom) {
298 self.document_fragment.id_map().add(id, element)
299 }
300
301 pub(crate) fn register_slot(&self, slot: &HTMLSlotElement) {
302 debug!("Registering slot with name={:?}", slot.Name().str());
303
304 let mut slots = self.slots.borrow_mut();
305
306 let slots_with_the_same_name = slots.entry(slot.Name()).or_default();
307
308 slots_with_the_same_name.insert_pre_order(slot, self.upcast::<Node>());
310 }
311
312 pub(crate) fn unregister_slot(&self, name: DOMString, slot: &HTMLSlotElement) {
313 debug!("Unregistering slot with name={:?}", name.str());
314
315 let mut slots = self.slots.borrow_mut();
316 let Entry::Occupied(mut entry) = slots.entry(name) else {
317 panic!("slot is not registered");
318 };
319 entry.get_mut().retain(|s| slot != &**s);
320 }
321
322 pub(crate) fn slot_for_name(&self, name: &DOMString) -> Option<DomRoot<HTMLSlotElement>> {
324 self.slots
325 .borrow()
326 .get(name)
327 .and_then(|slots| slots.first())
328 .map(|slot| slot.as_rooted())
329 }
330
331 pub(crate) fn has_slot_descendants(&self) -> bool {
332 !self.slots.borrow().is_empty()
333 }
334
335 pub(crate) fn set_available_to_element_internals(&self, value: bool) {
336 self.available_to_element_internals.set(value);
337 }
338
339 pub(crate) fn is_available_to_element_internals(&self) -> bool {
341 self.available_to_element_internals.get()
342 }
343
344 pub(crate) fn is_user_agent_widget(&self) -> bool {
345 self.is_user_agent_widget
346 }
347
348 pub(crate) fn set_declarative(&self, declarative: bool) {
349 self.declarative.set(declarative);
350 }
351
352 pub(crate) fn is_declarative(&self) -> bool {
353 self.declarative.get()
354 }
355
356 pub(crate) fn shadow_root_mode(&self) -> ShadowRootMode {
357 self.mode
358 }
359
360 pub(crate) fn set_serializable(&self, serializable: bool) {
361 self.serializable.set(serializable);
362 }
363
364 pub(crate) fn set_delegates_focus(&self, delegates_focus: bool) {
365 self.delegates_focus.set(delegates_focus);
366 }
367
368 pub(crate) fn details_name_groups<'a: 'b, 'b>(
369 &'a self,
370 no_gc: &'b NoGC,
371 ) -> RefMut<'b, DetailsNameGroups> {
372 RefMut::map(
373 self.details_name_groups.safe_borrow_mut(no_gc),
374 |details_name_groups| details_name_groups.get_or_insert_default(),
375 )
376 }
377
378 pub(crate) fn custom_element_registry(&self) -> Option<DomRoot<CustomElementRegistry>> {
379 self.document_or_shadow_root.custom_element_registry()
380 }
381
382 pub(crate) fn set_custom_element_registry(&self, registry: Option<&CustomElementRegistry>) {
383 self.document_or_shadow_root
384 .set_custom_element_registry(registry);
385 }
386}
387
388impl ShadowRootMethods<crate::DomTypeHolder> for ShadowRoot {
389 fn GetActiveElement(&self) -> Option<DomRoot<Element>> {
391 self.document_or_shadow_root.active_element(self.upcast())
392 }
393
394 fn GetCustomElementRegistry(&self) -> Option<DomRoot<CustomElementRegistry>> {
396 self.custom_element_registry()
397 }
398
399 fn ElementFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Option<DomRoot<Element>> {
401 match self.document_or_shadow_root.element_from_point(
404 self.upcast(),
405 x,
406 y,
407 None,
408 self.document.has_browsing_context(),
409 ) {
410 Some(e) => {
411 let retargeted_node = e.upcast::<EventTarget>().retarget(self.upcast());
412 retargeted_node.downcast::<Element>().map(DomRoot::from_ref)
413 },
414 None => None,
415 }
416 }
417
418 fn ElementsFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Vec<DomRoot<Element>> {
420 let mut elements = Vec::new();
423 for e in self
424 .document_or_shadow_root
425 .elements_from_point(
426 self.upcast(),
427 x,
428 y,
429 None,
430 self.document.has_browsing_context(),
431 )
432 .iter()
433 {
434 let retargeted_node = e.upcast::<EventTarget>().retarget(self.upcast());
435 if let Some(element) = retargeted_node.downcast::<Element>().map(DomRoot::from_ref) {
436 elements.push(element);
437 }
438 }
439 elements
440 }
441
442 fn Mode(&self) -> ShadowRootMode {
444 self.mode
445 }
446
447 fn DelegatesFocus(&self) -> bool {
449 self.delegates_focus.get()
450 }
451
452 fn Clonable(&self) -> bool {
454 self.clonable
455 }
456
457 fn Serializable(&self) -> bool {
459 self.serializable.get()
460 }
461
462 fn Host(&self) -> DomRoot<Element> {
464 self.upcast::<DocumentFragment>()
465 .host()
466 .expect("ShadowRoot always has an element as host")
467 }
468
469 fn StyleSheets(&self, cx: &mut JSContext) -> DomRoot<StyleSheetList> {
471 self.stylesheet_list.or_init(|| {
472 StyleSheetList::new(
473 cx,
474 &self.window,
475 StyleSheetListOwner::ShadowRoot(Dom::from_ref(self)),
476 )
477 })
478 }
479
480 fn GetHTML(&self, cx: &mut JSContext, options: &GetHTMLOptions) -> DOMString {
482 self.upcast::<Node>().html_serialize(
485 cx,
486 TraversalScope::ChildrenOnly(None),
487 options.serializableShadowRoots,
488 options.shadowRoots.clone(),
489 )
490 }
491
492 fn GetInnerHTML(&self, cx: &mut JSContext) -> Fallible<TrustedHTMLOrNullIsEmptyString> {
494 self.upcast::<Node>()
497 .fragment_serialization_algorithm(cx, true)
498 .map(TrustedHTMLOrNullIsEmptyString::NullIsEmptyString)
499 }
500
501 fn SetInnerHTML(
503 &self,
504 cx: &mut JSContext,
505 value: TrustedHTMLOrNullIsEmptyString,
506 ) -> ErrorResult {
507 let value = TrustedHTML::get_trusted_type_compliant_string(
510 cx,
511 &self.owner_global(),
512 value.convert(),
513 "ShadowRoot innerHTML",
514 )?;
515
516 let context = self.Host();
518
519 let frag = context.parse_fragment(value, cx)?;
525
526 Node::replace_all(cx, Some(frag.upcast()), self.upcast());
528 Ok(())
529 }
530
531 fn SlotAssignment(&self) -> SlotAssignmentMode {
533 self.slot_assignment_mode
534 }
535
536 fn SetHTMLUnsafe(
538 &self,
539 cx: &mut JSContext,
540 value: TrustedHTMLOrString,
541 options: &SetHTMLUnsafeOptions,
542 ) -> ErrorResult {
543 let compliant_html = TrustedHTML::get_trusted_type_compliant_string(
547 cx,
548 &self.owner_global(),
549 value,
550 "ShadowRoot setHTMLUnsafe",
551 )?;
552
553 Sanitizer::set_and_filter_html(
556 cx,
557 self.upcast(),
558 &self.Host(),
559 compliant_html,
560 options,
561 false,
562 )?;
563
564 Ok(())
565 }
566
567 fn SetHTML(
569 &self,
570 cx: &mut JSContext,
571 html: DOMString,
572 options: &SetHTMLOptions,
573 ) -> ErrorResult {
574 let target = self.upcast::<Node>();
579 let context_element = self.Host();
580 Sanitizer::set_and_filter_html(cx, target, &context_element, html, options, true)
581 }
582
583 event_handler!(slotchange, GetOnslotchange, SetOnslotchange);
585
586 fn AdoptedStyleSheets(&self, cx: &mut JSContext, retval: MutableHandleValue) {
588 self.adopted_stylesheets_frozen_types.get_or_init(
589 cx,
590 || {
591 self.adopted_stylesheets
592 .borrow()
593 .clone()
594 .iter()
595 .map(|sheet| sheet.as_rooted())
596 .collect()
597 },
598 retval,
599 );
600 }
601
602 fn SetAdoptedStyleSheets(&self, cx: &mut JSContext, val: HandleValue) -> ErrorResult {
604 let result = DocumentOrShadowRoot::set_adopted_stylesheet_from_jsval(
605 cx,
606 &self.adopted_stylesheets,
607 val,
608 &StyleSheetListOwner::ShadowRoot(Dom::from_ref(self)),
609 );
610
611 if result.is_ok() {
612 if self.author_styles.borrow().stylesheets.dirty() {
613 self.invalidate_stylesheets(cx.no_gc());
614 }
615
616 self.adopted_stylesheets_frozen_types.clear();
618 }
619
620 result
621 }
622
623 fn GetFullscreenElement(&self) -> Option<DomRoot<Element>> {
625 DocumentOrShadowRoot::get_fullscreen_element(
626 self.upcast::<Node>(),
627 self.document.fullscreen_element(),
628 )
629 }
630}
631
632impl VirtualMethods for ShadowRoot {
633 fn super_type(&self) -> Option<&dyn VirtualMethods> {
634 Some(self.upcast::<DocumentFragment>() as &dyn VirtualMethods)
635 }
636
637 fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
638 if let Some(s) = self.super_type() {
639 s.bind_to_tree(cx, context);
640 }
641
642 if context.tree_connected {
645 let document = self.owner_document();
646 document.register_shadow_root(self);
647 }
648
649 let shadow_root = self.upcast::<Node>();
650
651 shadow_root.set_flag(NodeFlags::IS_CONNECTED, context.tree_connected);
652
653 let inner_context = BindContext::new(shadow_root, IsShadowTree::Yes);
654
655 for node in shadow_root.traverse_preorder(ShadowIncluding::No).skip(1) {
657 node.set_flag(NodeFlags::IS_CONNECTED, inner_context.tree_connected);
658
659 debug_assert!(!node.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS));
661 vtable_for(&node).bind_to_tree(cx, &inner_context);
662 }
663 }
664
665 fn unbind_from_tree(&self, cx: &mut JSContext, context: &UnbindContext) {
666 if let Some(s) = self.super_type() {
667 s.unbind_from_tree(cx, context);
668 }
669
670 if context.tree_connected {
671 let document = self.owner_document();
672 document.unregister_shadow_root(self);
673 }
674 }
675}
676
677impl<'dom> LayoutDom<'dom, ShadowRoot> {
678 #[inline]
679 pub(crate) fn get_host_for_layout(self) -> LayoutDom<'dom, Element> {
680 self.upcast::<DocumentFragment>()
681 .shadowroot_host_for_layout()
682 }
683
684 #[inline]
685 #[expect(unsafe_code)]
686 pub(crate) fn get_style_data_for_layout(self) -> &'dom CascadeData {
687 fn is_sync<T: Sync>() {}
688 let _ = is_sync::<CascadeData>;
689 unsafe { &self.unsafe_get().author_styles.borrow_for_layout().data }
690 }
691
692 #[inline]
693 pub(crate) fn is_user_agent_widget(&self) -> bool {
694 self.unsafe_get().is_user_agent_widget()
695 }
696
697 #[inline]
700 #[expect(unsafe_code)]
701 pub(crate) unsafe fn flush_stylesheets_for_layout(
702 self,
703 stylist: &mut Stylist,
704 guard: &SharedRwLockReadGuard,
705 ) {
706 unsafe {
707 debug_assert!(self.upcast::<Node>().get_flag(NodeFlags::IS_CONNECTED));
708 };
709 let author_styles = unsafe { self.unsafe_get().author_styles.borrow_mut_for_layout() };
710 if author_styles.stylesheets.dirty() {
711 author_styles.flush(stylist, guard);
712 }
713 }
714}
715
716impl Convert<devtools_traits::ShadowRootMode> for ShadowRootMode {
717 fn convert(self) -> devtools_traits::ShadowRootMode {
718 match self {
719 ShadowRootMode::Open => devtools_traits::ShadowRootMode::Open,
720 ShadowRootMode::Closed => devtools_traits::ShadowRootMode::Closed,
721 }
722 }
723}