498cb50ee30df663acef404b312ed253fab2e8be
[WebKit.git] / Source / WebCore / html / HTMLMeterElement.cpp
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
39 namespace WebCore {
40
41 using namespace HTMLNames;
42
43 HTMLMeterElement::HTMLMeterElement(const QualifiedName& tagName, Document& document)
44     : LabelableElement(tagName, document)
45 {
46     ASSERT(hasTagName(meterTag));
47 }
48
49 HTMLMeterElement::~HTMLMeterElement() = default;
50
51 Ref<HTMLMeterElement> HTMLMeterElement::create(const QualifiedName& tagName, Document& document)
52 {
53     Ref<HTMLMeterElement> meter = adoptRef(*new HTMLMeterElement(tagName, document));
54     meter->ensureUserAgentShadowRoot();
55     return meter;
56 }
57
58 RenderPtr<RenderElement> HTMLMeterElement::createElementRenderer(RenderStyle&& style, const RenderTreePosition&)
59 {
60     if (!RenderTheme::singleton().supportsMeter(style.appearance()))
61         return RenderElement::createFor(*this, WTFMove(style));
62
63     return createRenderer<RenderMeter>(*this, WTFMove(style));
64 }
65
66 bool HTMLMeterElement::childShouldCreateRenderer(const Node& child) const
67 {
68     return !is<RenderMeter>(renderer()) && HTMLElement::childShouldCreateRenderer(child);
69 }
70
71 void HTMLMeterElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
72 {
73     if (name == valueAttr || name == minAttr || name == maxAttr || name == lowAttr || name == highAttr || name == optimumAttr)
74         didElementStateChange();
75     else
76         LabelableElement::parseAttribute(name, value);
77 }
78
79 double HTMLMeterElement::min() const
80 {
81     return parseToDoubleForNumberType(attributeWithoutSynchronization(minAttr), 0);
82 }
83
84 void HTMLMeterElement::setMin(double min)
85 {
86     setAttributeWithoutSynchronization(minAttr, AtomicString::number(min));
87 }
88
89 double HTMLMeterElement::max() const
90 {
91     return std::max(parseToDoubleForNumberType(attributeWithoutSynchronization(maxAttr), std::max(1.0, min())), min());
92 }
93
94 void HTMLMeterElement::setMax(double max)
95 {
96     setAttributeWithoutSynchronization(maxAttr, AtomicString::number(max));
97 }
98
99 double HTMLMeterElement::value() const
100 {
101     double value = parseToDoubleForNumberType(attributeWithoutSynchronization(valueAttr), 0);
102     return std::min(std::max(value, min()), max());
103 }
104
105 void HTMLMeterElement::setValue(double value)
106 {
107     setAttributeWithoutSynchronization(valueAttr, AtomicString::number(value));
108 }
109
110 double HTMLMeterElement::low() const
111 {
112     double low = parseToDoubleForNumberType(attributeWithoutSynchronization(lowAttr), min());
113     return std::min(std::max(low, min()), max());
114 }
115
116 void HTMLMeterElement::setLow(double low)
117 {
118     setAttributeWithoutSynchronization(lowAttr, AtomicString::number(low));
119 }
120
121 double HTMLMeterElement::high() const
122 {
123     double high = parseToDoubleForNumberType(attributeWithoutSynchronization(highAttr), max());
124     return std::min(std::max(high, low()), max());
125 }
126
127 void HTMLMeterElement::setHigh(double high)
128 {
129     setAttributeWithoutSynchronization(highAttr, AtomicString::number(high));
130 }
131
132 double HTMLMeterElement::optimum() const
133 {
134     double optimum = parseToDoubleForNumberType(attributeWithoutSynchronization(optimumAttr), (max() + min()) / 2);
135     return std::min(std::max(optimum, min()), max());
136 }
137
138 void HTMLMeterElement::setOptimum(double optimum)
139 {
140     setAttributeWithoutSynchronization(optimumAttr, AtomicString::number(optimum));
141 }
142
143 HTMLMeterElement::GaugeRegion HTMLMeterElement::gaugeRegion() const
144 {
145     double lowValue = low();
146     double highValue = high();
147     double theValue = value();
148     double optimumValue = optimum();
149
150     if (optimumValue < lowValue) {
151         // The optimum range stays under low
152         if (theValue <= lowValue)
153             return GaugeRegionOptimum;
154         if (theValue <= highValue)
155             return GaugeRegionSuboptimal;
156         return GaugeRegionEvenLessGood;
157     }
158     
159     if (highValue < optimumValue) {
160         // The optimum range stays over high
161         if (highValue <= theValue)
162             return GaugeRegionOptimum;
163         if (lowValue <= theValue)
164             return GaugeRegionSuboptimal;
165         return GaugeRegionEvenLessGood;
166     }
167
168     // The optimum range stays between high and low.
169     // According to the standard, <meter> never show GaugeRegionEvenLessGood in this case
170     // because the value is never less or greater than min or max.
171     if (lowValue <= theValue && theValue <= highValue)
172         return GaugeRegionOptimum;
173     return GaugeRegionSuboptimal;
174 }
175
176 double HTMLMeterElement::valueRatio() const
177 {
178     double min = this->min();
179     double max = this->max();
180     double value = this->value();
181
182     if (max <= min)
183         return 0;
184     return (value - min) / (max - min);
185 }
186
187 static void setValueClass(HTMLElement& element, HTMLMeterElement::GaugeRegion gaugeRegion)
188 {
189     switch (gaugeRegion) {
190     case HTMLMeterElement::GaugeRegionOptimum:
191         element.setAttribute(HTMLNames::classAttr, "optimum");
192         element.setPseudo("-webkit-meter-optimum-value");
193         return;
194     case HTMLMeterElement::GaugeRegionSuboptimal:
195         element.setAttribute(HTMLNames::classAttr, "suboptimum");
196         element.setPseudo("-webkit-meter-suboptimum-value");
197         return;
198     case HTMLMeterElement::GaugeRegionEvenLessGood:
199         element.setAttribute(HTMLNames::classAttr, "even-less-good");
200         element.setPseudo("-webkit-meter-even-less-good-value");
201         return;
202     default:
203         ASSERT_NOT_REACHED();
204     }
205 }
206
207 void HTMLMeterElement::didElementStateChange()
208 {
209     m_value->setInlineStyleProperty(CSSPropertyWidth, valueRatio()*100, CSSPrimitiveValue::CSS_PERCENTAGE);
210     setValueClass(*m_value, gaugeRegion());
211
212     if (RenderMeter* render = renderMeter())
213         render->updateFromElement();
214 }
215
216 RenderMeter* HTMLMeterElement::renderMeter() const
217 {
218     if (is<RenderMeter>(renderer()))
219         return downcast<RenderMeter>(renderer());
220     return nullptr;
221 }
222
223 void HTMLMeterElement::didAddUserAgentShadowRoot(ShadowRoot* root)
224 {
225     ASSERT(!m_value);
226
227     static NeverDestroyed<String> shadowStyle(meterElementShadowUserAgentStyleSheet, String::ConstructFromLiteral);
228
229     auto style = HTMLStyleElement::create(HTMLNames::styleTag, document(), false);
230     style->setTextContent(shadowStyle);
231     root->appendChild(style);
232
233     // Pseudos are set to allow author styling.
234     auto inner = HTMLDivElement::create(document());
235     inner->setIdAttribute("inner");
236     inner->setPseudo("-webkit-meter-inner-element");
237     root->appendChild(inner);
238
239     auto bar = HTMLDivElement::create(document());
240     bar->setIdAttribute("bar");
241     bar->setPseudo("-webkit-meter-bar");
242     inner->appendChild(bar);
243
244     m_value = HTMLDivElement::create(document());
245     m_value->setIdAttribute("value");
246     bar->appendChild(*m_value);
247
248     didElementStateChange();
249 }
250
251 } // namespace
252 #endif