1 | /* |
2 | * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). |
3 | * |
4 | * This library is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Library General Public |
6 | * License as published by the Free Software Foundation; either |
7 | * version 2 of the License, or (at your option) any later version. |
8 | * |
9 | * This library is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | * Library General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU Library General Public License |
15 | * along with this library; see the file COPYING.LIB. If not, write to |
16 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
17 | * Boston, MA 02110-1301, USA. |
18 | * |
19 | */ |
20 | |
21 | #include "config.h" |
22 | #include "HTMLMeterElement.h" |
23 | |
24 | #if ENABLE(METER_ELEMENT) |
25 | |
26 | #include "Attribute.h" |
27 | #include "ElementIterator.h" |
28 | #include "HTMLDivElement.h" |
29 | #include "HTMLFormElement.h" |
30 | #include "HTMLNames.h" |
31 | #include "HTMLParserIdioms.h" |
32 | #include "HTMLStyleElement.h" |
33 | #include "Page.h" |
34 | #include "RenderMeter.h" |
35 | #include "RenderTheme.h" |
36 | #include "ShadowRoot.h" |
37 | #include "UserAgentStyleSheets.h" |
38 | #include <wtf/IsoMallocInlines.h> |
39 | |
40 | namespace WebCore { |
41 | |
42 | WTF_MAKE_ISO_ALLOCATED_IMPL(HTMLMeterElement); |
43 | |
44 | using namespace HTMLNames; |
45 | |
46 | HTMLMeterElement::HTMLMeterElement(const QualifiedName& tagName, Document& document) |
47 | : LabelableElement(tagName, document) |
48 | { |
49 | ASSERT(hasTagName(meterTag)); |
50 | } |
51 | |
52 | HTMLMeterElement::~HTMLMeterElement() = default; |
53 | |
54 | Ref<HTMLMeterElement> HTMLMeterElement::create(const QualifiedName& tagName, Document& document) |
55 | { |
56 | Ref<HTMLMeterElement> meter = adoptRef(*new HTMLMeterElement(tagName, document)); |
57 | meter->ensureUserAgentShadowRoot(); |
58 | return meter; |
59 | } |
60 | |
61 | RenderPtr<RenderElement> HTMLMeterElement::createElementRenderer(RenderStyle&& style, const RenderTreePosition&) |
62 | { |
63 | if (!RenderTheme::singleton().supportsMeter(style.appearance())) |
64 | return RenderElement::createFor(*this, WTFMove(style)); |
65 | |
66 | return createRenderer<RenderMeter>(*this, WTFMove(style)); |
67 | } |
68 | |
69 | bool HTMLMeterElement::childShouldCreateRenderer(const Node& child) const |
70 | { |
71 | return !is<RenderMeter>(renderer()) && HTMLElement::childShouldCreateRenderer(child); |
72 | } |
73 | |
74 | void HTMLMeterElement::parseAttribute(const QualifiedName& name, const AtomicString& value) |
75 | { |
76 | if (name == valueAttr || name == minAttr || name == maxAttr || name == lowAttr || name == highAttr || name == optimumAttr) |
77 | didElementStateChange(); |
78 | else |
79 | LabelableElement::parseAttribute(name, value); |
80 | } |
81 | |
82 | double HTMLMeterElement::min() const |
83 | { |
84 | return parseToDoubleForNumberType(attributeWithoutSynchronization(minAttr), 0); |
85 | } |
86 | |
87 | void HTMLMeterElement::setMin(double min) |
88 | { |
89 | setAttributeWithoutSynchronization(minAttr, AtomicString::number(min)); |
90 | } |
91 | |
92 | double HTMLMeterElement::max() const |
93 | { |
94 | return std::max(parseToDoubleForNumberType(attributeWithoutSynchronization(maxAttr), std::max(1.0, min())), min()); |
95 | } |
96 | |
97 | void HTMLMeterElement::setMax(double max) |
98 | { |
99 | setAttributeWithoutSynchronization(maxAttr, AtomicString::number(max)); |
100 | } |
101 | |
102 | double HTMLMeterElement::value() const |
103 | { |
104 | double value = parseToDoubleForNumberType(attributeWithoutSynchronization(valueAttr), 0); |
105 | return std::min(std::max(value, min()), max()); |
106 | } |
107 | |
108 | void HTMLMeterElement::setValue(double value) |
109 | { |
110 | setAttributeWithoutSynchronization(valueAttr, AtomicString::number(value)); |
111 | } |
112 | |
113 | double HTMLMeterElement::low() const |
114 | { |
115 | double low = parseToDoubleForNumberType(attributeWithoutSynchronization(lowAttr), min()); |
116 | return std::min(std::max(low, min()), max()); |
117 | } |
118 | |
119 | void HTMLMeterElement::setLow(double low) |
120 | { |
121 | setAttributeWithoutSynchronization(lowAttr, AtomicString::number(low)); |
122 | } |
123 | |
124 | double HTMLMeterElement::high() const |
125 | { |
126 | double high = parseToDoubleForNumberType(attributeWithoutSynchronization(highAttr), max()); |
127 | return std::min(std::max(high, low()), max()); |
128 | } |
129 | |
130 | void HTMLMeterElement::setHigh(double high) |
131 | { |
132 | setAttributeWithoutSynchronization(highAttr, AtomicString::number(high)); |
133 | } |
134 | |
135 | double HTMLMeterElement::optimum() const |
136 | { |
137 | double optimum = parseToDoubleForNumberType(attributeWithoutSynchronization(optimumAttr), (max() + min()) / 2); |
138 | return std::min(std::max(optimum, min()), max()); |
139 | } |
140 | |
141 | void HTMLMeterElement::setOptimum(double optimum) |
142 | { |
143 | setAttributeWithoutSynchronization(optimumAttr, AtomicString::number(optimum)); |
144 | } |
145 | |
146 | HTMLMeterElement::GaugeRegion HTMLMeterElement::gaugeRegion() const |
147 | { |
148 | double lowValue = low(); |
149 | double highValue = high(); |
150 | double theValue = value(); |
151 | double optimumValue = optimum(); |
152 | |
153 | if (optimumValue < lowValue) { |
154 | // The optimum range stays under low |
155 | if (theValue <= lowValue) |
156 | return GaugeRegionOptimum; |
157 | if (theValue <= highValue) |
158 | return GaugeRegionSuboptimal; |
159 | return GaugeRegionEvenLessGood; |
160 | } |
161 | |
162 | if (highValue < optimumValue) { |
163 | // The optimum range stays over high |
164 | if (highValue <= theValue) |
165 | return GaugeRegionOptimum; |
166 | if (lowValue <= theValue) |
167 | return GaugeRegionSuboptimal; |
168 | return GaugeRegionEvenLessGood; |
169 | } |
170 | |
171 | // The optimum range stays between high and low. |
172 | // According to the standard, <meter> never show GaugeRegionEvenLessGood in this case |
173 | // because the value is never less or greater than min or max. |
174 | if (lowValue <= theValue && theValue <= highValue) |
175 | return GaugeRegionOptimum; |
176 | return GaugeRegionSuboptimal; |
177 | } |
178 | |
179 | double HTMLMeterElement::valueRatio() const |
180 | { |
181 | double min = this->min(); |
182 | double max = this->max(); |
183 | double value = this->value(); |
184 | |
185 | if (max <= min) |
186 | return 0; |
187 | return (value - min) / (max - min); |
188 | } |
189 | |
190 | static void setValueClass(HTMLElement& element, HTMLMeterElement::GaugeRegion gaugeRegion) |
191 | { |
192 | switch (gaugeRegion) { |
193 | case HTMLMeterElement::GaugeRegionOptimum: |
194 | element.setAttribute(HTMLNames::classAttr, "optimum" ); |
195 | element.setPseudo("-webkit-meter-optimum-value" ); |
196 | return; |
197 | case HTMLMeterElement::GaugeRegionSuboptimal: |
198 | element.setAttribute(HTMLNames::classAttr, "suboptimum" ); |
199 | element.setPseudo("-webkit-meter-suboptimum-value" ); |
200 | return; |
201 | case HTMLMeterElement::GaugeRegionEvenLessGood: |
202 | element.setAttribute(HTMLNames::classAttr, "even-less-good" ); |
203 | element.setPseudo("-webkit-meter-even-less-good-value" ); |
204 | return; |
205 | default: |
206 | ASSERT_NOT_REACHED(); |
207 | } |
208 | } |
209 | |
210 | void HTMLMeterElement::didElementStateChange() |
211 | { |
212 | m_value->setInlineStyleProperty(CSSPropertyWidth, valueRatio()*100, CSSPrimitiveValue::CSS_PERCENTAGE); |
213 | setValueClass(*m_value, gaugeRegion()); |
214 | |
215 | if (RenderMeter* render = renderMeter()) |
216 | render->updateFromElement(); |
217 | } |
218 | |
219 | RenderMeter* HTMLMeterElement::renderMeter() const |
220 | { |
221 | if (is<RenderMeter>(renderer())) |
222 | return downcast<RenderMeter>(renderer()); |
223 | return nullptr; |
224 | } |
225 | |
226 | void HTMLMeterElement::didAddUserAgentShadowRoot(ShadowRoot& root) |
227 | { |
228 | ASSERT(!m_value); |
229 | |
230 | static NeverDestroyed<String> shadowStyle(meterElementShadowUserAgentStyleSheet, String::ConstructFromLiteral); |
231 | |
232 | auto style = HTMLStyleElement::create(HTMLNames::styleTag, document(), false); |
233 | style->setTextContent(shadowStyle); |
234 | root.appendChild(style); |
235 | |
236 | // Pseudos are set to allow author styling. |
237 | auto inner = HTMLDivElement::create(document()); |
238 | inner->setIdAttribute("inner" ); |
239 | inner->setPseudo("-webkit-meter-inner-element" ); |
240 | root.appendChild(inner); |
241 | |
242 | auto bar = HTMLDivElement::create(document()); |
243 | bar->setIdAttribute("bar" ); |
244 | bar->setPseudo("-webkit-meter-bar" ); |
245 | inner->appendChild(bar); |
246 | |
247 | m_value = HTMLDivElement::create(document()); |
248 | m_value->setIdAttribute("value" ); |
249 | bar->appendChild(*m_value); |
250 | |
251 | didElementStateChange(); |
252 | } |
253 | |
254 | } // namespace |
255 | #endif |
256 | |