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