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
38pub(crate) trait CreateUAShadowRootForSelf<ShadowTree>:
39    NodeTraits + DerivedFrom<Node>
40{
41    fn create_element_in_ua_shadowroot(
42        &self,
43        cx: &mut JSContext,
44        local_name: LocalName,
45    ) -> DomRoot<Element> {
46        let document = self.owner_document();
47        Element::create(
48            cx,
49            QualName::new(None, ns!(html), local_name),
50            None,
51            &document,
52            ElementCreator::ScriptCreated,
53            CustomElementCreationMode::Asynchronous,
54            None,
55        )
56    }
57
58    fn shadow_tree(&self, cx: &mut JSContext) -> Ref<'_, ShadowTree>;
59}
60
61impl<ShadowTree, E: UAShadowRoot<ShadowTree> + Castable + DerivedFrom<Element> + DerivedFrom<Node>>
62    CreateUAShadowRootForSelf<ShadowTree> for E
63{
64    fn shadow_tree(&self, cx: &mut JSContext) -> Ref<'_, ShadowTree> {
65        self.ensure_shadow_tree(cx, self.upcast())
66    }
67}
68
69pub(crate) trait SpecificShadowTree<ElementType, ShadowTreeHolder> {
70    fn update(&self, cx: &mut JSContext, holder: &ShadowTreeHolder, element: &ElementType);
71
72    fn create_element_in_ua_shadowroot(
73        cx: &mut JSContext,
74        document: &Document,
75        local_name: LocalName,
76    ) -> DomRoot<Element> {
77        Element::create(
78            cx,
79            QualName::new(None, ns!(html), local_name),
80            None,
81            document,
82            ElementCreator::ScriptCreated,
83            CustomElementCreationMode::Asynchronous,
84            None,
85        )
86    }
87}
88
89pub(crate) trait UpdateUAShadowRootForOther<ElementType, ShadowTree>:
90    UAShadowRoot<ShadowTree>
91{
92    fn update_shadow_tree(&self, cx: &mut JSContext, element: &ElementType);
93}
94
95impl<
96    T: UAShadowRoot<ShadowTree>,
97    ShadowTree: SpecificShadowTree<ElementType, T>,
98    ElementType: Castable + DerivedFrom<Element>,
99> UpdateUAShadowRootForOther<ElementType, ShadowTree> for T
100{
101    fn update_shadow_tree(&self, cx: &mut JSContext, element: &ElementType) {
102        self.ensure_shadow_tree(cx, element.upcast())
103            .update(cx, self, element)
104    }
105}
106
107trait UAShadowRootHelpers<ShadowTree>: UAShadowRoot<ShadowTree> {
108    fn attach_ua_shadow_root(cx: &mut JSContext, element: &Element) -> DomRoot<ShadowRoot>;
109    fn create_shadow_tree(&self, cx: &mut JSContext, element: &Element);
110}
111
112impl<T: ?Sized + UAShadowRoot<ShadowTree>, ShadowTree> UAShadowRootHelpers<ShadowTree> for T {
113    /// Attach a UA widget shadow root with its default parameters.
114    /// Additionally mark ShadowRoot to use styling configuration for a UA widget.
115    ///
116    /// The general trait of these elements is that it would hide the implementation.
117    /// Thus, we would make it inaccessible (i.e., closed mode, not cloneable, and
118    /// not serializable).
119    ///
120    /// With UA shadow root element being assumed as one element, any focus should
121    /// be delegated to its host.
122    ///
123    // TODO: Ideally, all of the UA shadow root should use UA widget styling, but
124    //       some of the UA widget implemented prior to the implementation of Gecko's
125    //       UA widget matching might need some tweaking.
126    // FIXME: We are yet to implement more complex focusing with that is necessary
127    //        for delegate focus, and we are using workarounds for that right now.
128    fn attach_ua_shadow_root(cx: &mut JSContext, element: &Element) -> DomRoot<ShadowRoot> {
129        let root = element
130            .attach_shadow(
131                cx,
132                IsUserAgentWidget::Yes,
133                ShadowRootMode::Closed,
134                false,
135                false,
136                false,
137                SlotAssignmentMode::Manual,
138            )
139            .expect("Attaching UA shadow root failed");
140
141        root.upcast::<Node>().set_in_ua_widget(true);
142        root
143    }
144
145    fn create_shadow_tree(&self, cx: &mut JSContext, element: &Element) {
146        let root = element
147            .shadow_root()
148            .unwrap_or_else(|| Self::attach_ua_shadow_root(cx, element));
149
150        self.store_for_shadow_tree(cx, root);
151
152        element
153            .upcast::<Node>()
154            .dirty(cx.no_gc(), crate::dom::node::NodeDamage::Other);
155    }
156}