1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "HTMLMeterElement.h"
8 #include "mozilla/EventStates.h"
9 #include "mozilla/dom/HTMLMeterElementBinding.h"
10 
11 NS_IMPL_NS_NEW_HTML_ELEMENT(Meter)
12 
13 namespace mozilla::dom {
14 
15 const double HTMLMeterElement::kDefaultValue = 0.0;
16 const double HTMLMeterElement::kDefaultMin = 0.0;
17 const double HTMLMeterElement::kDefaultMax = 1.0;
18 
HTMLMeterElement(already_AddRefed<mozilla::dom::NodeInfo> && aNodeInfo)19 HTMLMeterElement::HTMLMeterElement(
20     already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
21     : nsGenericHTMLElement(std::move(aNodeInfo)) {}
22 
23 HTMLMeterElement::~HTMLMeterElement() = default;
24 
NS_IMPL_ELEMENT_CLONE(HTMLMeterElement)25 NS_IMPL_ELEMENT_CLONE(HTMLMeterElement)
26 
27 EventStates HTMLMeterElement::IntrinsicState() const {
28   EventStates state = nsGenericHTMLElement::IntrinsicState();
29 
30   state |= GetOptimumState();
31 
32   return state;
33 }
34 
ParseAttribute(int32_t aNamespaceID,nsAtom * aAttribute,const nsAString & aValue,nsIPrincipal * aMaybeScriptedPrincipal,nsAttrValue & aResult)35 bool HTMLMeterElement::ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute,
36                                       const nsAString& aValue,
37                                       nsIPrincipal* aMaybeScriptedPrincipal,
38                                       nsAttrValue& aResult) {
39   if (aNamespaceID == kNameSpaceID_None) {
40     if (aAttribute == nsGkAtoms::value || aAttribute == nsGkAtoms::max ||
41         aAttribute == nsGkAtoms::min || aAttribute == nsGkAtoms::low ||
42         aAttribute == nsGkAtoms::high || aAttribute == nsGkAtoms::optimum) {
43       return aResult.ParseDoubleValue(aValue);
44     }
45   }
46 
47   return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
48                                               aMaybeScriptedPrincipal, aResult);
49 }
50 
51 /*
52  * Value getters :
53  * const getters used by XPCOM methods and by IntrinsicState
54  */
55 
Min() const56 double HTMLMeterElement::Min() const {
57   /**
58    * If the attribute min is defined, the minimum is this value.
59    * Otherwise, the minimum is the default value.
60    */
61   const nsAttrValue* attrMin = mAttrs.GetAttr(nsGkAtoms::min);
62   if (attrMin && attrMin->Type() == nsAttrValue::eDoubleValue) {
63     return attrMin->GetDoubleValue();
64   }
65   return kDefaultMin;
66 }
67 
Max() const68 double HTMLMeterElement::Max() const {
69   /**
70    * If the attribute max is defined, the maximum is this value.
71    * Otherwise, the maximum is the default value.
72    * If the maximum value is less than the minimum value,
73    * the maximum value is the same as the minimum value.
74    */
75   double max;
76 
77   const nsAttrValue* attrMax = mAttrs.GetAttr(nsGkAtoms::max);
78   if (attrMax && attrMax->Type() == nsAttrValue::eDoubleValue) {
79     max = attrMax->GetDoubleValue();
80   } else {
81     max = kDefaultMax;
82   }
83 
84   return std::max(max, Min());
85 }
86 
Value() const87 double HTMLMeterElement::Value() const {
88   /**
89    * If the attribute value is defined, the actual value is this value.
90    * Otherwise, the actual value is the default value.
91    * If the actual value is less than the minimum value,
92    * the actual value is the same as the minimum value.
93    * If the actual value is greater than the maximum value,
94    * the actual value is the same as the maximum value.
95    */
96   double value;
97 
98   const nsAttrValue* attrValue = mAttrs.GetAttr(nsGkAtoms::value);
99   if (attrValue && attrValue->Type() == nsAttrValue::eDoubleValue) {
100     value = attrValue->GetDoubleValue();
101   } else {
102     value = kDefaultValue;
103   }
104 
105   double min = Min();
106 
107   if (value <= min) {
108     return min;
109   }
110 
111   return std::min(value, Max());
112 }
113 
Position() const114 double HTMLMeterElement::Position() const {
115   const double max = Max();
116   const double min = Min();
117   const double value = Value();
118 
119   double range = max - min;
120   return range != 0.0 ? (value - min) / range : 1.0;
121 }
122 
Low() const123 double HTMLMeterElement::Low() const {
124   /**
125    * If the low value is defined, the low value is this value.
126    * Otherwise, the low value is the minimum value.
127    * If the low value is less than the minimum value,
128    * the low value is the same as the minimum value.
129    * If the low value is greater than the maximum value,
130    * the low value is the same as the maximum value.
131    */
132 
133   double min = Min();
134 
135   const nsAttrValue* attrLow = mAttrs.GetAttr(nsGkAtoms::low);
136   if (!attrLow || attrLow->Type() != nsAttrValue::eDoubleValue) {
137     return min;
138   }
139 
140   double low = attrLow->GetDoubleValue();
141 
142   if (low <= min) {
143     return min;
144   }
145 
146   return std::min(low, Max());
147 }
148 
High() const149 double HTMLMeterElement::High() const {
150   /**
151    * If the high value is defined, the high value is this value.
152    * Otherwise, the high value is the maximum value.
153    * If the high value is less than the low value,
154    * the high value is the same as the low value.
155    * If the high value is greater than the maximum value,
156    * the high value is the same as the maximum value.
157    */
158 
159   double max = Max();
160 
161   const nsAttrValue* attrHigh = mAttrs.GetAttr(nsGkAtoms::high);
162   if (!attrHigh || attrHigh->Type() != nsAttrValue::eDoubleValue) {
163     return max;
164   }
165 
166   double high = attrHigh->GetDoubleValue();
167 
168   if (high >= max) {
169     return max;
170   }
171 
172   return std::max(high, Low());
173 }
174 
Optimum() const175 double HTMLMeterElement::Optimum() const {
176   /**
177    * If the optimum value is defined, the optimum value is this value.
178    * Otherwise, the optimum value is the midpoint between
179    * the minimum value and the maximum value :
180    * min + (max - min)/2 = (min + max)/2
181    * If the optimum value is less than the minimum value,
182    * the optimum value is the same as the minimum value.
183    * If the optimum value is greater than the maximum value,
184    * the optimum value is the same as the maximum value.
185    */
186 
187   double max = Max();
188 
189   double min = Min();
190 
191   const nsAttrValue* attrOptimum = mAttrs.GetAttr(nsGkAtoms::optimum);
192   if (!attrOptimum || attrOptimum->Type() != nsAttrValue::eDoubleValue) {
193     return (min + max) / 2.0;
194   }
195 
196   double optimum = attrOptimum->GetDoubleValue();
197 
198   if (optimum <= min) {
199     return min;
200   }
201 
202   return std::min(optimum, max);
203 }
204 
GetOptimumState() const205 EventStates HTMLMeterElement::GetOptimumState() const {
206   /*
207    * If the optimum value is in [minimum, low[,
208    *     return if the value is in optimal, suboptimal or sub-suboptimal region
209    *
210    * If the optimum value is in [low, high],
211    *     return if the value is in optimal or suboptimal region
212    *
213    * If the optimum value is in ]high, maximum],
214    *     return if the value is in optimal, suboptimal or sub-suboptimal region
215    */
216   double value = Value();
217   double low = Low();
218   double high = High();
219   double optimum = Optimum();
220 
221   if (optimum < low) {
222     if (value < low) {
223       return NS_EVENT_STATE_OPTIMUM;
224     }
225     if (value <= high) {
226       return NS_EVENT_STATE_SUB_OPTIMUM;
227     }
228     return NS_EVENT_STATE_SUB_SUB_OPTIMUM;
229   }
230   if (optimum > high) {
231     if (value > high) {
232       return NS_EVENT_STATE_OPTIMUM;
233     }
234     if (value >= low) {
235       return NS_EVENT_STATE_SUB_OPTIMUM;
236     }
237     return NS_EVENT_STATE_SUB_SUB_OPTIMUM;
238   }
239   // optimum in [low, high]
240   if (value >= low && value <= high) {
241     return NS_EVENT_STATE_OPTIMUM;
242   }
243   return NS_EVENT_STATE_SUB_OPTIMUM;
244 }
245 
WrapNode(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)246 JSObject* HTMLMeterElement::WrapNode(JSContext* aCx,
247                                      JS::Handle<JSObject*> aGivenProto) {
248   return HTMLMeterElement_Binding::Wrap(aCx, this, aGivenProto);
249 }
250 
251 }  // namespace mozilla::dom
252