Skip to main content

script/dom/shadowroot/
ua_shadowroot.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::cell::Ref;
6
7use html5ever::{LocalName, QualName, ns};
8use js::context::JSContext;
9
10use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{
11    ShadowRootMode, SlotAssignmentMode,
12};
13use crate::dom::bindings::conversions::DerivedFrom;
14use crate::dom::bindings::inheritance::Castable;
15use crate::dom::bindings::root::DomRoot;
16use crate::dom::document::Document;
17use crate::dom::element::element::Element;
18use crate::dom::element::{CustomElementCreationMode, ElementCreator};
19use crate::dom::node::{Node, NodeTraits};
20use crate::dom::shadowroot::shadowroot::{IsUserAgentWidget, ShadowRoot};
21
22pub(crate) trait UAShadowRoot<ShadowTree> {
23    fn store_for_shadow_tree(&self, cx: &mut JSContext, shadow_root: DomRoot<ShadowRoot>);
24
25    fn borrow_for_shadow_tree(&self) -> Ref<'_, Option<ShadowTree>>;
26
27    fn ensure_shadow_tree(&self, cx: &mut JSContext, element: &Element) -> Ref<'_, ShadowTree> {
28        if self.borrow_for_shadow_tree().is_none() {
29            self.create_shadow_tree(cx, element);
30        }
31
32        Ref::filter_map(self.borrow_for_shadow_tree(), Option::as_ref)
33            .ok()
34            .expect("UA shadow tree was not created")
35    }
36
37    /// TODO: Remove this method and fix the media element controls to properly be clickable
38    fn should_mark_as_ua_widget(&self) -> bool {
39        true
40    }
41}
42
43pub(crate) trait CreateUAShadowRootForSelf<ShadowTree>:
44    NodeTraits + DerivedFrom<Node>
45{
46    fn create_element_in_ua_shadowroot(
47        &self,
48        cx: &mut JSContext,
49        local_name: LocalName,
50    ) -> DomRoot<Element> {
51        let document = self.owner_document();
52        Element::create(
53            cx,
54            QualName::new(None, ns!(html), local_name),
55            None,
56            &document,
57            ElementCreator::ScriptCreated,
58            CustomElementCreationMode::Asynchronous,
59            None,
60        )
61    }
62
63    fn shadow_tree(&self, cx: &mut JSContext) -> Ref<'_, ShadowTree>;
64}
65
66impl<ShadowTree, E: UAShadowRoot<ShadowTree> + Castable + DerivedFrom<Element> + DerivedFrom<Node>>
67    CreateUAShadowRootForSelf<ShadowTree> for E
68{
69    fn shadow_tree(&self, cx: &mut JSContext) -> Ref<'_, ShadowTree> {
70        self.ensure_shadow_tree(cx, self.upcast())
71    }
72}
73
74pub(crate) trait SpecificShadowTree<ElementType, ShadowTreeHolder> {
75    fn update(&self, cx: &mut JSContext, holder: &ShadowTreeHolder, element: &ElementType);
76
77    fn create_element_in_ua_shadowroot(
78        cx: &mut JSContext,
79        document: &Document,
80        local_name: LocalName,
81    ) -> DomRoot<Element> {
82        Element::create(
83            cx,
84            QualName::new(None, ns!(html), local_name),
85            None,
86            document,
87            ElementCreator::ScriptCreated,
88            CustomElementCreationMode::Asynchronous,
89            None,
90        )
91    }
92}
93
94trait UAShadowRootHelpers<ShadowTree>: UAShadowRoot<ShadowTree> {
95    fn attach_ua_shadow_root(&self, cx: &mut JSContext, element: &Element) -> DomRoot<ShadowRoot>;
96    fn create_shadow_tree(&self, cx: &mut JSContext, element: &Element);
97}
98
99impl<T: ?Sized + UAShadowRoot<ShadowTree>, ShadowTree> UAShadowRootHelpers<ShadowTree> for T {
100    /// Attach a UA widget shadow root with its default parameters.
101    /// Additionally mark ShadowRoot to use styling configuration for a UA widget.
102    ///
103    /// The general trait of these elements is that it would hide the implementation.
104    /// Thus, we would make it inaccessible (i.e., closed mode, not cloneable, and
105    /// not serializable).
106    ///
107    /// With UA shadow root element being assumed as one element, any focus should
108    /// be delegated to its host.
109    ///
110    // TODO: Ideally, all of the UA shadow root should use UA widget styling, but
111    //       some of the UA widget implemented prior to the implementation of Gecko's
112    //       UA widget matching might need some tweaking.
113    // FIXME: We are yet to implement more complex focusing with that is necessary
114    //        for delegate focus, and we are using workarounds for that right now.
115    fn attach_ua_shadow_root(&self, cx: &mut JSContext, element: &Element) -> DomRoot<ShadowRoot> {
116        let root = element
117            .attach_shadow(
118                cx,
119                IsUserAgentWidget::Yes,
120                ShadowRootMode::Closed,
121                false,
122                false,
123                false,
124                SlotAssignmentMode::Manual,
125            )
126            .expect("Attaching UA shadow root failed");
127
128        if self.should_mark_as_ua_widget() {
129            root.upcast::<Node>().mark_in_ua_widget();
130        }
131        root
132    }
133
134    fn create_shadow_tree(&self, cx: &mut JSContext, element: &Element) {
135        let root = element
136            .shadow_root()
137            .unwrap_or_else(|| self.attach_ua_shadow_root(cx, element));
138
139        self.store_for_shadow_tree(cx, root);
140
141        element
142            .upcast::<Node>()
143            .dirty(cx.no_gc(), crate::dom::node::NodeDamage::Other);
144    }
145}