script/dom/html/form_controls/
htmlprogresselement.rs1use std::cell::Ref;
6
7use dom_struct::dom_struct;
8use html5ever::{LocalName, Prefix, local_name};
9use js::context::JSContext;
10use js::rust::HandleObject;
11use script_bindings::cell::DomRefCell;
12use style::selector_parser::PseudoElement;
13use stylo_dom::ElementState;
14
15use crate::dom::bindings::codegen::Bindings::HTMLProgressElementBinding::HTMLProgressElementMethods;
16use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods;
17use crate::dom::bindings::inheritance::Castable;
18use crate::dom::bindings::num::Finite;
19use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
20use crate::dom::bindings::str::DOMString;
21use crate::dom::document::Document;
22use crate::dom::element::attributes::storage::AttrRef;
23use crate::dom::element::{AttributeMutation, Element};
24use crate::dom::html::htmlelement::HTMLElement;
25use crate::dom::node::virtualmethods::VirtualMethods;
26use crate::dom::node::{BindContext, Node};
27use crate::dom::nodelist::NodeList;
28use crate::dom::shadowroot::ShadowRoot;
29use crate::dom::shadowroot::ua_shadowroot::{CreateUAShadowRootForSelf, UAShadowRoot};
30
31#[dom_struct]
32pub(crate) struct HTMLProgressElement {
33 htmlelement: HTMLElement,
34 labels_node_list: MutNullableDom<NodeList>,
35 shadow_tree: DomRefCell<Option<ShadowTree>>,
36}
37
38#[derive(Clone, JSTraceable, MallocSizeOf)]
40#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
41struct ShadowTree {
42 progress_bar: Dom<Element>,
43}
44
45impl UAShadowRoot<ShadowTree> for HTMLProgressElement {
46 fn store_for_shadow_tree(&self, cx: &mut JSContext, shadow_root: DomRoot<ShadowRoot>) {
47 let progress_bar = self.create_element_in_ua_shadowroot(cx, local_name!("div"));
48 shadow_root
49 .upcast::<Node>()
50 .AppendChild(cx, progress_bar.upcast::<Node>())
51 .unwrap();
52 progress_bar
53 .upcast::<Node>()
54 .set_implemented_pseudo_element(PseudoElement::MozProgressBar);
55
56 let _ = self
57 .shadow_tree
58 .safe_borrow_mut(cx.no_gc())
59 .insert(ShadowTree {
60 progress_bar: progress_bar.as_traced(),
61 });
62 }
63
64 fn borrow_for_shadow_tree(&self) -> Ref<'_, Option<ShadowTree>> {
65 self.shadow_tree.borrow()
66 }
67}
68
69impl HTMLProgressElement {
70 fn new_inherited(
71 local_name: LocalName,
72 prefix: Option<Prefix>,
73 document: &Document,
74 ) -> HTMLProgressElement {
75 HTMLProgressElement {
76 htmlelement: HTMLElement::new_inherited_with_state(
77 ElementState::INDETERMINATE,
78 local_name,
79 prefix,
80 document,
81 ),
82 labels_node_list: MutNullableDom::new(None),
83 shadow_tree: Default::default(),
84 }
85 }
86
87 pub(crate) fn new(
88 cx: &mut js::context::JSContext,
89 local_name: LocalName,
90 prefix: Option<Prefix>,
91 document: &Document,
92 proto: Option<HandleObject>,
93 ) -> DomRoot<HTMLProgressElement> {
94 Node::reflect_node_with_proto(
95 cx,
96 Box::new(HTMLProgressElement::new_inherited(
97 local_name, prefix, document,
98 )),
99 document,
100 proto,
101 )
102 }
103
104 fn update_state(&self, cx: &mut JSContext) {
106 let shadow_tree = self.shadow_tree(cx);
107 let position = (*self.Value() / *self.Max()) * 100.0;
108 let style = format!("width: {}%", position);
109
110 shadow_tree
111 .progress_bar
112 .set_string_attribute(cx, &local_name!("style"), style.into());
113 }
114}
115
116impl HTMLProgressElementMethods<crate::DomTypeHolder> for HTMLProgressElement {
117 make_labels_getter!(Labels, labels_node_list);
119
120 fn Value(&self) -> Finite<f64> {
122 self.upcast::<Element>()
129 .get_string_attribute(&local_name!("value"))
130 .parse_floating_point_number()
131 .map_or(Finite::wrap(0.0), |v| {
132 if v < 0.0 {
133 Finite::wrap(0.0)
134 } else {
135 Finite::wrap(v.min(*self.Max()))
136 }
137 })
138 }
139
140 fn SetValue(&self, cx: &mut JSContext, new_val: Finite<f64>) {
142 if *new_val >= 0.0 {
143 let mut string_value = DOMString::from((*new_val).to_string());
144 string_value.set_best_representation_of_the_floating_point_number();
145 self.upcast::<Element>()
146 .set_string_attribute(cx, &local_name!("value"), string_value);
147 }
148 }
149
150 fn Max(&self) -> Finite<f64> {
152 self.upcast::<Element>()
156 .get_string_attribute(&local_name!("max"))
157 .parse_floating_point_number()
158 .map_or(Finite::wrap(1.0), |m| {
159 if m <= 0.0 {
160 Finite::wrap(1.0)
161 } else {
162 Finite::wrap(m)
163 }
164 })
165 }
166
167 fn SetMax(&self, cx: &mut JSContext, new_val: Finite<f64>) {
169 if *new_val > 0.0 {
170 let mut string_value = DOMString::from((*new_val).to_string());
171 string_value.set_best_representation_of_the_floating_point_number();
172 self.upcast::<Element>()
173 .set_string_attribute(cx, &local_name!("max"), string_value);
174 }
175 }
176
177 fn Position(&self) -> Finite<f64> {
179 let value = self
180 .upcast::<Element>()
181 .get_string_attribute(&local_name!("value"));
182 if value.is_empty() {
183 Finite::wrap(-1.0)
184 } else {
185 let value = self.Value();
186 let max = self.Max();
187 Finite::wrap(*value / *max)
195 }
196 }
197}
198
199impl VirtualMethods for HTMLProgressElement {
200 fn super_type(&self) -> Option<&dyn VirtualMethods> {
201 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
202 }
203
204 fn attribute_mutated(
205 &self,
206 cx: &mut js::context::JSContext,
207 attr: AttrRef<'_>,
208 mutation: AttributeMutation,
209 ) {
210 self.super_type()
211 .unwrap()
212 .attribute_mutated(cx, attr, mutation);
213
214 let is_important_attribute = matches!(
215 attr.local_name(),
216 &local_name!("value") | &local_name!("max")
217 );
218 if is_important_attribute {
219 self.update_state(cx);
220 }
221
222 if matches!(attr.local_name(), &local_name!("value")) {
223 self.upcast::<Element>().set_state(
224 cx.no_gc(),
225 ElementState::INDETERMINATE,
226 matches!(mutation, AttributeMutation::Removed),
227 );
228 }
229 }
230
231 fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
232 self.super_type().unwrap().bind_to_tree(cx, context);
233
234 self.update_state(cx);
235 }
236}