script/dom/html/input_element/
text_value_widget.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 js::context::JSContext;
8use script_bindings::codegen::GenericBindings::CharacterDataBinding::CharacterDataMethods;
9use script_bindings::root::Dom;
10use script_bindings::script_runtime::CanGc;
11
12use crate::dom::bindings::cell::DomRefCell;
13use crate::dom::bindings::inheritance::Castable;
14use crate::dom::characterdata::CharacterData;
15use crate::dom::element::Element;
16use crate::dom::htmlinputelement::HTMLInputElement;
17use crate::dom::node::{Node, NodeTraits};
18use crate::dom::text::Text;
19
20#[derive(Default, JSTraceable, MallocSizeOf, PartialEq)]
21#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
22pub(crate) struct TextValueWidget {
23    shadow_tree: DomRefCell<Option<TextValueShadowTree>>,
24}
25
26impl TextValueWidget {
27    /// Get the shadow tree for this [`HTMLInputElement`], if it is created and valid, otherwise
28    /// recreate the shadow tree and return it.
29    fn get_or_create_shadow_tree(
30        &self,
31        cx: &mut JSContext,
32        input: &HTMLInputElement,
33    ) -> Ref<'_, TextValueShadowTree> {
34        {
35            if let Ok(shadow_tree) = Ref::filter_map(self.shadow_tree.borrow(), |shadow_tree| {
36                shadow_tree.as_ref()
37            }) {
38                return shadow_tree;
39            }
40        }
41
42        let element = input.upcast::<Element>();
43        let shadow_root = element
44            .shadow_root()
45            .unwrap_or_else(|| element.attach_ua_shadow_root(cx, true));
46        let shadow_root = shadow_root.upcast();
47        *self.shadow_tree.borrow_mut() = Some(TextValueShadowTree::new(cx, shadow_root));
48        self.get_or_create_shadow_tree(cx, input)
49    }
50
51    pub(crate) fn update_shadow_tree(&self, cx: &mut JSContext, input: &HTMLInputElement) {
52        self.get_or_create_shadow_tree(cx, input).update(input)
53    }
54}
55
56#[derive(Clone, JSTraceable, MallocSizeOf, PartialEq)]
57#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
58struct TextValueShadowTree {
59    value: Dom<Text>,
60}
61
62impl TextValueShadowTree {
63    fn new(cx: &mut JSContext, shadow_root: &Node) -> Self {
64        let value = Text::new(
65            Default::default(),
66            &shadow_root.owner_document(),
67            CanGc::from_cx(cx),
68        );
69        Node::replace_all(cx, Some(value.upcast()), shadow_root);
70        Self {
71            value: value.as_traced(),
72        }
73    }
74
75    fn update(&self, input_element: &HTMLInputElement) {
76        let character_data = self.value.upcast::<CharacterData>();
77        let value = input_element.value_for_shadow_dom();
78        if character_data.Data() != value {
79            character_data.SetData(value);
80        }
81    }
82}