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