1use std::cell::Ref;
6use std::ops::{Add, Div};
7
8use dom_struct::dom_struct;
9use html5ever::{LocalName, Prefix, local_name};
10use js::rust::HandleObject;
11use stylo_dom::ElementState;
12
13use crate::dom::attr::Attr;
14use crate::dom::bindings::cell::DomRefCell;
15use crate::dom::bindings::codegen::Bindings::HTMLMeterElementBinding::HTMLMeterElementMethods;
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::{AttributeMutation, Element};
23use crate::dom::html::htmldivelement::HTMLDivElement;
24use crate::dom::html::htmlelement::HTMLElement;
25use crate::dom::node::{BindContext, ChildrenMutation, Node, NodeTraits};
26use crate::dom::nodelist::NodeList;
27use crate::dom::virtualmethods::VirtualMethods;
28use crate::script_runtime::CanGc;
29
30#[dom_struct]
31pub(crate) struct HTMLMeterElement {
32 htmlelement: HTMLElement,
33 labels_node_list: MutNullableDom<NodeList>,
34 shadow_tree: DomRefCell<Option<ShadowTree>>,
35}
36
37#[derive(Clone, JSTraceable, MallocSizeOf)]
39#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
40struct ShadowTree {
41 meter_value: Dom<HTMLDivElement>,
42}
43
44impl HTMLMeterElement {
46 fn new_inherited(
47 local_name: LocalName,
48 prefix: Option<Prefix>,
49 document: &Document,
50 ) -> HTMLMeterElement {
51 HTMLMeterElement {
52 htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
53 labels_node_list: MutNullableDom::new(None),
54 shadow_tree: Default::default(),
55 }
56 }
57
58 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
59 pub(crate) fn new(
60 local_name: LocalName,
61 prefix: Option<Prefix>,
62 document: &Document,
63 proto: Option<HandleObject>,
64 can_gc: CanGc,
65 ) -> DomRoot<HTMLMeterElement> {
66 Node::reflect_node_with_proto(
67 Box::new(HTMLMeterElement::new_inherited(
68 local_name, prefix, document,
69 )),
70 document,
71 proto,
72 can_gc,
73 )
74 }
75
76 fn create_shadow_tree(&self, can_gc: CanGc) {
77 let document = self.owner_document();
78 let root = self.upcast::<Element>().attach_ua_shadow_root(true, can_gc);
79
80 let meter_value = HTMLDivElement::new(local_name!("div"), None, &document, None, can_gc);
81 root.upcast::<Node>()
82 .AppendChild(meter_value.upcast::<Node>(), can_gc)
83 .unwrap();
84
85 let _ = self.shadow_tree.borrow_mut().insert(ShadowTree {
86 meter_value: meter_value.as_traced(),
87 });
88 self.upcast::<Node>()
89 .dirty(crate::dom::node::NodeDamage::Other);
90 }
91
92 fn shadow_tree(&self, can_gc: CanGc) -> Ref<'_, ShadowTree> {
93 if !self.upcast::<Element>().is_shadow_host() {
94 self.create_shadow_tree(can_gc);
95 }
96
97 Ref::filter_map(self.shadow_tree.borrow(), Option::as_ref)
98 .ok()
99 .expect("UA shadow tree was not created")
100 }
101
102 fn update_state(&self, can_gc: CanGc) {
103 let value = *self.Value();
104 let low = *self.Low();
105 let high = *self.High();
106 let min = *self.Min();
107 let max = *self.Max();
108 let optimum = *self.Optimum();
109
110 let element_state = if optimum < low {
115 if value < low {
116 ElementState::OPTIMUM
117 } else if value <= high {
118 ElementState::SUB_OPTIMUM
119 } else {
120 ElementState::SUB_SUB_OPTIMUM
121 }
122 }
123 else if optimum > high {
128 if value > high {
129 ElementState::OPTIMUM
130 } else if value >= low {
131 ElementState::SUB_OPTIMUM
132 } else {
133 ElementState::SUB_SUB_OPTIMUM
134 }
135 }
136 else if (low..=high).contains(&value) {
140 ElementState::OPTIMUM
141 } else {
142 ElementState::SUB_OPTIMUM
143 };
144
145 self.upcast::<Element>()
147 .set_state(ElementState::METER_OPTIMUM_STATES, false);
148 self.upcast::<Element>().set_state(element_state, true);
149
150 let shadow_tree = self.shadow_tree(can_gc);
152 let position = (value - min) / (max - min) * 100.0;
153 let style = format!("width: {position}%");
154 shadow_tree
155 .meter_value
156 .upcast::<Element>()
157 .set_string_attribute(&local_name!("style"), style.into(), can_gc);
158 }
159}
160
161impl HTMLMeterElementMethods<crate::DomTypeHolder> for HTMLMeterElement {
162 make_labels_getter!(Labels, labels_node_list);
164
165 fn Value(&self) -> Finite<f64> {
167 let min = *self.Min();
168 let max = *self.Max();
169
170 Finite::wrap(
171 self.upcast::<Element>()
172 .get_string_attribute(&local_name!("value"))
173 .parse_floating_point_number()
174 .map_or(0.0, |candidate_actual_value| {
175 candidate_actual_value.clamp(min, max)
176 }),
177 )
178 }
179
180 fn SetValue(&self, value: Finite<f64>, can_gc: CanGc) {
182 let mut string_value = DOMString::from_string((*value).to_string());
183
184 string_value.set_best_representation_of_the_floating_point_number();
185
186 self.upcast::<Element>()
187 .set_string_attribute(&local_name!("value"), string_value, can_gc);
188 }
189
190 fn Min(&self) -> Finite<f64> {
192 Finite::wrap(
193 self.upcast::<Element>()
194 .get_string_attribute(&local_name!("min"))
195 .parse_floating_point_number()
196 .unwrap_or(0.0),
197 )
198 }
199
200 fn SetMin(&self, value: Finite<f64>, can_gc: CanGc) {
202 let mut string_value = DOMString::from_string((*value).to_string());
203
204 string_value.set_best_representation_of_the_floating_point_number();
205
206 self.upcast::<Element>()
207 .set_string_attribute(&local_name!("min"), string_value, can_gc);
208 }
209
210 fn Max(&self) -> Finite<f64> {
212 Finite::wrap(
213 self.upcast::<Element>()
214 .get_string_attribute(&local_name!("max"))
215 .parse_floating_point_number()
216 .unwrap_or(1.0)
217 .max(*self.Min()),
218 )
219 }
220
221 fn SetMax(&self, value: Finite<f64>, can_gc: CanGc) {
223 let mut string_value = DOMString::from_string((*value).to_string());
224
225 string_value.set_best_representation_of_the_floating_point_number();
226
227 self.upcast::<Element>()
228 .set_string_attribute(&local_name!("max"), string_value, can_gc);
229 }
230
231 fn Low(&self) -> Finite<f64> {
233 let min = *self.Min();
234 let max = *self.Max();
235
236 Finite::wrap(
237 self.upcast::<Element>()
238 .get_string_attribute(&local_name!("low"))
239 .parse_floating_point_number()
240 .map_or(min, |candidate_low_boundary| {
241 candidate_low_boundary.clamp(min, max)
242 }),
243 )
244 }
245
246 fn SetLow(&self, value: Finite<f64>, can_gc: CanGc) {
248 let mut string_value = DOMString::from_string((*value).to_string());
249
250 string_value.set_best_representation_of_the_floating_point_number();
251
252 self.upcast::<Element>()
253 .set_string_attribute(&local_name!("low"), string_value, can_gc);
254 }
255
256 fn High(&self) -> Finite<f64> {
258 let max: f64 = *self.Max();
259 let low: f64 = *self.Low();
260
261 Finite::wrap(
262 self.upcast::<Element>()
263 .get_string_attribute(&local_name!("high"))
264 .parse_floating_point_number()
265 .map_or(max, |candidate_high_boundary| {
266 if candidate_high_boundary < low {
267 return low;
268 }
269
270 candidate_high_boundary.clamp(*self.Min(), max)
271 }),
272 )
273 }
274
275 fn SetHigh(&self, value: Finite<f64>, can_gc: CanGc) {
277 let mut string_value = DOMString::from_string((*value).to_string());
278
279 string_value.set_best_representation_of_the_floating_point_number();
280
281 self.upcast::<Element>()
282 .set_string_attribute(&local_name!("high"), string_value, can_gc);
283 }
284
285 fn Optimum(&self) -> Finite<f64> {
287 let max = *self.Max();
288 let min = *self.Min();
289
290 Finite::wrap(
291 self.upcast::<Element>()
292 .get_string_attribute(&local_name!("optimum"))
293 .parse_floating_point_number()
294 .map_or(max.add(min).div(2.0), |candidate_optimum_point| {
295 candidate_optimum_point.clamp(min, max)
296 }),
297 )
298 }
299
300 fn SetOptimum(&self, value: Finite<f64>, can_gc: CanGc) {
302 let mut string_value = DOMString::from_string((*value).to_string());
303
304 string_value.set_best_representation_of_the_floating_point_number();
305
306 self.upcast::<Element>().set_string_attribute(
307 &local_name!("optimum"),
308 string_value,
309 can_gc,
310 );
311 }
312}
313
314impl VirtualMethods for HTMLMeterElement {
315 fn super_type(&self) -> Option<&dyn VirtualMethods> {
316 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
317 }
318
319 fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
320 self.super_type()
321 .unwrap()
322 .attribute_mutated(attr, mutation, can_gc);
323
324 let is_important_attribute = matches!(
325 attr.local_name(),
326 &local_name!("high") |
327 &local_name!("low") |
328 &local_name!("min") |
329 &local_name!("max") |
330 &local_name!("optimum") |
331 &local_name!("value")
332 );
333 if is_important_attribute {
334 self.update_state(CanGc::note());
335 }
336 }
337
338 fn children_changed(&self, mutation: &ChildrenMutation) {
339 self.super_type().unwrap().children_changed(mutation);
340
341 self.update_state(CanGc::note());
342 }
343
344 fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
345 self.super_type().unwrap().bind_to_tree(context, can_gc);
346
347 self.update_state(CanGc::note());
348 }
349}