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