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::error::{ErrorResult, Fallible};
13use script_bindings::script_runtime::JSContext;
14use servo_arc::Arc;
15use style::author_styles::AuthorStyles;
16use style::dom::TElement;
17use style::invalidation::element::restyle_hints::RestyleHint;
18use style::shared_lock::SharedRwLockReadGuard;
19use style::stylesheets::Stylesheet;
20use style::stylist::{CascadeData, Stylist};
21use stylo_atoms::Atom;
22
23use crate::conversions::Convert;
24use crate::dom::bindings::cell::DomRefCell;
25use crate::dom::bindings::codegen::Bindings::ElementBinding::GetHTMLOptions;
26use crate::dom::bindings::codegen::Bindings::HTMLSlotElementBinding::HTMLSlotElement_Binding::HTMLSlotElementMethods;
27use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::ShadowRoot_Binding::ShadowRootMethods;
28use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{
29 ShadowRootMode, SlotAssignmentMode,
30};
31use crate::dom::bindings::codegen::UnionTypes::{
32 TrustedHTMLOrNullIsEmptyString, TrustedHTMLOrString,
33};
34use crate::dom::bindings::frozenarray::CachedFrozenArray;
35use crate::dom::bindings::inheritance::Castable;
36use crate::dom::bindings::num::Finite;
37use crate::dom::bindings::reflector::reflect_dom_object;
38use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom};
39use crate::dom::bindings::str::DOMString;
40use crate::dom::cssstylesheet::CSSStyleSheet;
41use crate::dom::document::Document;
42use crate::dom::documentfragment::DocumentFragment;
43use crate::dom::documentorshadowroot::{
44 DocumentOrShadowRoot, ServoStylesheetInDocument, StylesheetSource,
45};
46use crate::dom::element::Element;
47use crate::dom::html::htmlslotelement::HTMLSlotElement;
48use crate::dom::node::{
49 BindContext, Node, NodeDamage, NodeFlags, NodeTraits, ShadowIncluding, UnbindContext,
50 VecPreOrderInsertionHelper,
51};
52use crate::dom::stylesheetlist::{StyleSheetList, StyleSheetListOwner};
53use crate::dom::trustedhtml::TrustedHTML;
54use crate::dom::types::EventTarget;
55use crate::dom::virtualmethods::{VirtualMethods, vtable_for};
56use crate::dom::window::Window;
57use crate::script_runtime::CanGc;
58use crate::stylesheet_set::StylesheetSetRef;
59
60#[derive(JSTraceable, MallocSizeOf, PartialEq)]
62pub(crate) enum IsUserAgentWidget {
63 No,
64 Yes,
65}
66
67#[dom_struct]
69pub(crate) struct ShadowRoot {
70 document_fragment: DocumentFragment,
71 document_or_shadow_root: DocumentOrShadowRoot,
72 document: Dom<Document>,
73 host: MutNullableDom<Element>,
74 #[custom_trace]
76 author_styles: DomRefCell<AuthorStyles<ServoStylesheetInDocument>>,
77 stylesheet_list: MutNullableDom<StyleSheetList>,
78 window: Dom<Window>,
79
80 mode: ShadowRootMode,
82
83 slot_assignment_mode: SlotAssignmentMode,
85
86 clonable: bool,
88
89 available_to_element_internals: Cell<bool>,
91
92 slots: DomRefCell<HashMap<DOMString, Vec<Dom<HTMLSlotElement>>>>,
93
94 is_user_agent_widget: bool,
95
96 declarative: Cell<bool>,
98
99 serializable: Cell<bool>,
101
102 delegates_focus: Cell<bool>,
104
105 adopted_stylesheets: DomRefCell<Vec<Dom<CSSStyleSheet>>>,
108
109 #[ignore_malloc_size_of = "mozjs"]
111 adopted_stylesheets_frozen_types: CachedFrozenArray,
112}
113
114impl ShadowRoot {
115 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
116 fn new_inherited(
117 host: &Element,
118 document: &Document,
119 mode: ShadowRootMode,
120 slot_assignment_mode: SlotAssignmentMode,
121 clonable: bool,
122 is_user_agent_widget: IsUserAgentWidget,
123 ) -> ShadowRoot {
124 let document_fragment = DocumentFragment::new_inherited(document);
125 let node = document_fragment.upcast::<Node>();
126 node.set_flag(NodeFlags::IS_IN_SHADOW_TREE, true);
127 node.set_flag(
128 NodeFlags::IS_CONNECTED,
129 host.upcast::<Node>().is_connected(),
130 );
131
132 ShadowRoot {
133 document_fragment,
134 document_or_shadow_root: DocumentOrShadowRoot::new(document.window()),
135 document: Dom::from_ref(document),
136 host: MutNullableDom::new(Some(host)),
137 author_styles: DomRefCell::new(AuthorStyles::new()),
138 stylesheet_list: MutNullableDom::new(None),
139 window: Dom::from_ref(document.window()),
140 mode,
141 slot_assignment_mode,
142 clonable,
143 available_to_element_internals: Cell::new(false),
144 slots: Default::default(),
145 is_user_agent_widget: is_user_agent_widget == IsUserAgentWidget::Yes,
146 declarative: Cell::new(false),
147 serializable: Cell::new(false),
148 delegates_focus: Cell::new(false),
149 adopted_stylesheets: Default::default(),
150 adopted_stylesheets_frozen_types: CachedFrozenArray::new(),
151 }
152 }
153
154 pub(crate) fn new(
155 host: &Element,
156 document: &Document,
157 mode: ShadowRootMode,
158 slot_assignment_mode: SlotAssignmentMode,
159 clonable: bool,
160 is_user_agent_widget: IsUserAgentWidget,
161 can_gc: CanGc,
162 ) -> DomRoot<ShadowRoot> {
163 reflect_dom_object(
164 Box::new(ShadowRoot::new_inherited(
165 host,
166 document,
167 mode,
168 slot_assignment_mode,
169 clonable,
170 is_user_agent_widget,
171 )),
172 document.window(),
173 can_gc,
174 )
175 }
176
177 pub(crate) fn detach(&self, can_gc: CanGc) {
178 self.document.unregister_shadow_root(self);
179 let node = self.upcast::<Node>();
180 node.set_containing_shadow_root(None);
181 Node::complete_remove_subtree(node, &UnbindContext::new(node, None, None, None), can_gc);
182 self.host.set(None);
183 }
184
185 pub(crate) fn owner_doc(&self) -> &Document {
186 &self.document
187 }
188
189 pub(crate) fn get_focused_element(&self) -> Option<DomRoot<Element>> {
190 None
192 }
193
194 pub(crate) fn stylesheet_count(&self) -> usize {
195 self.author_styles.borrow().stylesheets.len()
196 }
197
198 pub(crate) fn stylesheet_at(&self, index: usize) -> Option<DomRoot<CSSStyleSheet>> {
199 let stylesheets = &self.author_styles.borrow().stylesheets;
200
201 stylesheets
202 .get(index)
203 .and_then(|s| s.owner.get_cssom_object())
204 }
205
206 #[cfg_attr(crown, allow(crown::unrooted_must_root))] pub(crate) fn add_owned_stylesheet(&self, owner_node: &Element, sheet: Arc<Stylesheet>) {
213 let stylesheets = &mut self.author_styles.borrow_mut().stylesheets;
214
215 let insertion_point = stylesheets
217 .iter()
218 .find(|sheet_in_shadow| {
219 match &sheet_in_shadow.owner {
220 StylesheetSource::Element(other_node) => {
221 owner_node.upcast::<Node>().is_before(other_node.upcast())
222 },
223 StylesheetSource::Constructed(_) => true,
226 }
227 })
228 .cloned();
229
230 DocumentOrShadowRoot::add_stylesheet(
231 StylesheetSource::Element(Dom::from_ref(owner_node)),
232 StylesheetSetRef::Author(stylesheets),
233 sheet,
234 insertion_point,
235 self.document.style_shared_lock(),
236 );
237 }
238
239 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
241 pub(crate) fn append_constructed_stylesheet(&self, cssom_stylesheet: &CSSStyleSheet) {
242 debug_assert!(cssom_stylesheet.is_constructed());
243
244 let stylesheets = &mut self.author_styles.borrow_mut().stylesheets;
245 let sheet = cssom_stylesheet.style_stylesheet().clone();
246
247 let insertion_point = stylesheets.iter().last().cloned();
248
249 DocumentOrShadowRoot::add_stylesheet(
250 StylesheetSource::Constructed(Dom::from_ref(cssom_stylesheet)),
251 StylesheetSetRef::Author(stylesheets),
252 sheet,
253 insertion_point,
254 self.document.style_shared_lock(),
255 );
256 }
257
258 #[cfg_attr(crown, allow(crown::unrooted_must_root))] pub(crate) fn remove_stylesheet(&self, owner: StylesheetSource, s: &Arc<Stylesheet>) {
261 DocumentOrShadowRoot::remove_stylesheet(
262 owner,
263 s,
264 StylesheetSetRef::Author(&mut self.author_styles.borrow_mut().stylesheets),
265 )
266 }
267
268 pub(crate) fn invalidate_stylesheets(&self) {
269 self.document.invalidate_shadow_roots_stylesheets();
270 self.author_styles.borrow_mut().stylesheets.force_dirty();
271 if let Some(host) = self.host.get() {
273 host.upcast::<Node>().dirty(NodeDamage::Style);
274
275 let mut restyle = self.document.ensure_pending_restyle(&host);
278 restyle.hint.insert(RestyleHint::restyle_subtree());
279 }
280 }
281
282 pub(crate) fn unregister_element_id(&self, to_unregister: &Element, id: Atom, _can_gc: CanGc) {
285 self.document_or_shadow_root.unregister_named_element(
286 self.document_fragment.id_map(),
287 to_unregister,
288 &id,
289 );
290 }
291
292 pub(crate) fn register_element_id(&self, element: &Element, id: Atom, _can_gc: CanGc) {
294 let root = self
295 .upcast::<Node>()
296 .inclusive_ancestors(ShadowIncluding::No)
297 .last()
298 .unwrap();
299 self.document_or_shadow_root.register_named_element(
300 self.document_fragment.id_map(),
301 element,
302 &id,
303 root,
304 );
305 }
306
307 pub(crate) fn register_slot(&self, slot: &HTMLSlotElement) {
308 debug!("Registering slot with name={:?}", slot.Name().str());
309
310 let mut slots = self.slots.borrow_mut();
311
312 let slots_with_the_same_name = slots.entry(slot.Name()).or_default();
313
314 slots_with_the_same_name.insert_pre_order(slot, self.upcast::<Node>());
316 }
317
318 pub(crate) fn unregister_slot(&self, name: DOMString, slot: &HTMLSlotElement) {
319 debug!("Unregistering slot with name={:?}", name.str());
320
321 let mut slots = self.slots.borrow_mut();
322 let Entry::Occupied(mut entry) = slots.entry(name) else {
323 panic!("slot is not registered");
324 };
325 entry.get_mut().retain(|s| slot != &**s);
326 }
327
328 pub(crate) fn slot_for_name(&self, name: &DOMString) -> Option<DomRoot<HTMLSlotElement>> {
330 self.slots
331 .borrow()
332 .get(name)
333 .and_then(|slots| slots.first())
334 .map(|slot| slot.as_rooted())
335 }
336
337 pub(crate) fn has_slot_descendants(&self) -> bool {
338 !self.slots.borrow().is_empty()
339 }
340
341 pub(crate) fn set_available_to_element_internals(&self, value: bool) {
342 self.available_to_element_internals.set(value);
343 }
344
345 pub(crate) fn is_available_to_element_internals(&self) -> bool {
347 self.available_to_element_internals.get()
348 }
349
350 pub(crate) fn is_user_agent_widget(&self) -> bool {
351 self.is_user_agent_widget
352 }
353
354 pub(crate) fn set_declarative(&self, declarative: bool) {
355 self.declarative.set(declarative);
356 }
357
358 pub(crate) fn is_declarative(&self) -> bool {
359 self.declarative.get()
360 }
361
362 pub(crate) fn shadow_root_mode(&self) -> ShadowRootMode {
363 self.mode
364 }
365
366 pub(crate) fn set_serializable(&self, serializable: bool) {
367 self.serializable.set(serializable);
368 }
369
370 pub(crate) fn set_delegates_focus(&self, delegates_focus: bool) {
371 self.delegates_focus.set(delegates_focus);
372 }
373}
374
375impl ShadowRootMethods<crate::DomTypeHolder> for ShadowRoot {
376 fn GetActiveElement(&self) -> Option<DomRoot<Element>> {
378 self.document_or_shadow_root
379 .get_active_element(self.get_focused_element(), None, None)
380 }
381
382 fn ElementFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Option<DomRoot<Element>> {
384 match self.document_or_shadow_root.element_from_point(
387 x,
388 y,
389 None,
390 self.document.has_browsing_context(),
391 ) {
392 Some(e) => {
393 let retargeted_node = self
394 .upcast::<EventTarget>()
395 .retarget(e.upcast::<EventTarget>());
396 retargeted_node.downcast::<Element>().map(DomRoot::from_ref)
397 },
398 None => None,
399 }
400 }
401
402 fn ElementsFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Vec<DomRoot<Element>> {
404 let mut elements = Vec::new();
407 for e in self
408 .document_or_shadow_root
409 .elements_from_point(x, y, None, self.document.has_browsing_context())
410 .iter()
411 {
412 let retargeted_node = self
413 .upcast::<EventTarget>()
414 .retarget(e.upcast::<EventTarget>());
415 if let Some(element) = retargeted_node.downcast::<Element>().map(DomRoot::from_ref) {
416 elements.push(element);
417 }
418 }
419 elements
420 }
421
422 fn Mode(&self) -> ShadowRootMode {
424 self.mode
425 }
426
427 fn DelegatesFocus(&self) -> bool {
429 self.delegates_focus.get()
430 }
431
432 fn Clonable(&self) -> bool {
434 self.clonable
435 }
436
437 fn Serializable(&self) -> bool {
439 self.serializable.get()
440 }
441
442 fn Host(&self) -> DomRoot<Element> {
444 let host = self.host.get();
445 host.expect("Trying to get host from a detached shadow root")
446 }
447
448 fn StyleSheets(&self) -> DomRoot<StyleSheetList> {
450 self.stylesheet_list.or_init(|| {
451 StyleSheetList::new(
452 &self.window,
453 StyleSheetListOwner::ShadowRoot(Dom::from_ref(self)),
454 CanGc::note(),
455 )
456 })
457 }
458
459 fn GetHTML(&self, options: &GetHTMLOptions, can_gc: CanGc) -> DOMString {
461 self.upcast::<Node>().html_serialize(
464 TraversalScope::ChildrenOnly(None),
465 options.serializableShadowRoots,
466 options.shadowRoots.clone(),
467 can_gc,
468 )
469 }
470
471 fn GetInnerHTML(&self, can_gc: CanGc) -> Fallible<TrustedHTMLOrNullIsEmptyString> {
473 self.upcast::<Node>()
476 .fragment_serialization_algorithm(true, can_gc)
477 .map(TrustedHTMLOrNullIsEmptyString::NullIsEmptyString)
478 }
479
480 fn SetInnerHTML(&self, value: TrustedHTMLOrNullIsEmptyString, can_gc: CanGc) -> ErrorResult {
482 let value = TrustedHTML::get_trusted_script_compliant_string(
485 &self.owner_global(),
486 value.convert(),
487 "ShadowRoot innerHTML",
488 can_gc,
489 )?;
490
491 let context = self.Host();
493
494 let frag = context.parse_fragment(value, can_gc)?;
500
501 Node::replace_all(Some(frag.upcast()), self.upcast(), can_gc);
503 Ok(())
504 }
505
506 fn SlotAssignment(&self) -> SlotAssignmentMode {
508 self.slot_assignment_mode
509 }
510
511 fn SetHTMLUnsafe(&self, value: TrustedHTMLOrString, can_gc: CanGc) -> ErrorResult {
513 let value = TrustedHTML::get_trusted_script_compliant_string(
517 &self.owner_global(),
518 value,
519 "ShadowRoot setHTMLUnsafe",
520 can_gc,
521 )?;
522 let target = self.upcast::<Node>();
524 let context_element = self.Host();
525
526 Node::unsafely_set_html(target, &context_element, value, can_gc);
527 Ok(())
528 }
529
530 event_handler!(onslotchange, GetOnslotchange, SetOnslotchange);
532
533 fn AdoptedStyleSheets(&self, context: JSContext, can_gc: CanGc, retval: MutableHandleValue) {
535 self.adopted_stylesheets_frozen_types.get_or_init(
536 || {
537 self.adopted_stylesheets
538 .borrow()
539 .clone()
540 .iter()
541 .map(|sheet| sheet.as_rooted())
542 .collect()
543 },
544 context,
545 retval,
546 can_gc,
547 );
548 }
549
550 fn SetAdoptedStyleSheets(&self, context: JSContext, val: HandleValue) -> ErrorResult {
552 let result = DocumentOrShadowRoot::set_adopted_stylesheet_from_jsval(
553 context,
554 self.adopted_stylesheets.borrow_mut().as_mut(),
555 val,
556 &StyleSheetListOwner::ShadowRoot(Dom::from_ref(self)),
557 );
558
559 if result.is_ok() {
561 self.adopted_stylesheets_frozen_types.clear();
562 }
563
564 result
565 }
566}
567
568impl VirtualMethods for ShadowRoot {
569 fn super_type(&self) -> Option<&dyn VirtualMethods> {
570 Some(self.upcast::<DocumentFragment>() as &dyn VirtualMethods)
571 }
572
573 fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
574 if let Some(s) = self.super_type() {
575 s.bind_to_tree(context, can_gc);
576 }
577
578 if context.tree_connected {
581 let document = self.owner_document();
582 document.register_shadow_root(self);
583 }
584
585 let shadow_root = self.upcast::<Node>();
586
587 shadow_root.set_flag(NodeFlags::IS_CONNECTED, context.tree_connected);
588
589 for node in shadow_root.traverse_preorder(ShadowIncluding::Yes).skip(1) {
591 node.set_flag(NodeFlags::IS_CONNECTED, context.tree_connected);
592
593 debug_assert!(!node.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS));
595 vtable_for(&node).bind_to_tree(
596 &BindContext {
597 tree_connected: context.tree_connected,
598 tree_is_in_a_document_tree: false,
599 tree_is_in_a_shadow_tree: true,
600 },
601 can_gc,
602 );
603 }
604 }
605
606 fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
607 if let Some(s) = self.super_type() {
608 s.unbind_from_tree(context, can_gc);
609 }
610
611 if context.tree_connected {
612 let document = self.owner_document();
613 document.unregister_shadow_root(self);
614 }
615 }
616}
617
618#[allow(unsafe_code)]
619pub(crate) trait LayoutShadowRootHelpers<'dom> {
620 fn get_host_for_layout(self) -> LayoutDom<'dom, Element>;
621 fn get_style_data_for_layout(self) -> &'dom CascadeData;
622 unsafe fn flush_stylesheets<E: TElement>(
623 self,
624 stylist: &mut Stylist,
625 guard: &SharedRwLockReadGuard,
626 );
627}
628
629impl<'dom> LayoutShadowRootHelpers<'dom> for LayoutDom<'dom, ShadowRoot> {
630 #[inline]
631 #[allow(unsafe_code)]
632 fn get_host_for_layout(self) -> LayoutDom<'dom, Element> {
633 unsafe {
634 self.unsafe_get()
635 .host
636 .get_inner_as_layout()
637 .expect("We should never do layout on a detached shadow root")
638 }
639 }
640
641 #[inline]
642 #[allow(unsafe_code)]
643 fn get_style_data_for_layout(self) -> &'dom CascadeData {
644 fn is_sync<T: Sync>() {}
645 let _ = is_sync::<CascadeData>;
646 unsafe { &self.unsafe_get().author_styles.borrow_for_layout().data }
647 }
648
649 #[inline]
652 #[allow(unsafe_code)]
653 unsafe fn flush_stylesheets<E: TElement>(
654 self,
655 stylist: &mut Stylist,
656 guard: &SharedRwLockReadGuard,
657 ) {
658 let author_styles = self.unsafe_get().author_styles.borrow_mut_for_layout();
659 if author_styles.stylesheets.dirty() {
660 author_styles.flush::<E>(stylist, guard);
661 }
662 }
663}
664
665impl Convert<devtools_traits::ShadowRootMode> for ShadowRootMode {
666 fn convert(self) -> devtools_traits::ShadowRootMode {
667 match self {
668 ShadowRootMode::Open => devtools_traits::ShadowRootMode::Open,
669 ShadowRootMode::Closed => devtools_traits::ShadowRootMode::Closed,
670 }
671 }
672}