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#if ENABLE(METER_ELEMENT)
23#include "HTMLMeterElement.h"
24
25#include "Attribute.h"
26#include "ElementIterator.h"
27#include "EventNames.h"
28#include "ExceptionCode.h"
29#include "FormDataList.h"
30#include "HTMLFormElement.h"
31#include "HTMLNames.h"
32#include "HTMLParserIdioms.h"
33#include "MeterShadowElement.h"
34#include "Page.h"
35#include "RenderMeter.h"
36#include "RenderTheme.h"
37#include "ShadowRoot.h"
38
39namespace WebCore {
40
41using namespace HTMLNames;
42
43HTMLMeterElement::HTMLMeterElement(const QualifiedName& tagName, Document& document)
44    : LabelableElement(tagName, document)
45{
46    ASSERT(hasTagName(meterTag));
47}
48
49HTMLMeterElement::~HTMLMeterElement()
50{
51}
52
53PassRefPtr<HTMLMeterElement> HTMLMeterElement::create(const QualifiedName& tagName, Document& document)
54{
55    RefPtr<HTMLMeterElement> meter = adoptRef(new HTMLMeterElement(tagName, document));
56    meter->ensureUserAgentShadowRoot();
57    return meter;
58}
59
60RenderPtr<RenderElement> HTMLMeterElement::createElementRenderer(PassRef<RenderStyle> style)
61{
62    if (!document().page()->theme().supportsMeter(style.get().appearance()))
63        return RenderElement::createFor(*this, WTF::move(style));
64
65    return createRenderer<RenderMeter>(*this, WTF::move(style));
66}
67
68bool HTMLMeterElement::childShouldCreateRenderer(const Node& child) const
69{
70    return hasShadowRootParent(child) && HTMLElement::childShouldCreateRenderer(child);
71}
72
73void HTMLMeterElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
74{
75    if (name == valueAttr || name == minAttr || name == maxAttr || name == lowAttr || name == highAttr || name == optimumAttr)
76        didElementStateChange();
77    else
78        LabelableElement::parseAttribute(name, value);
79}
80
81double HTMLMeterElement::min() const
82{
83    return parseToDoubleForNumberType(getAttribute(minAttr), 0);
84}
85
86void HTMLMeterElement::setMin(double min, ExceptionCode& ec)
87{
88    if (!std::isfinite(min)) {
89        ec = NOT_SUPPORTED_ERR;
90        return;
91    }
92    setAttribute(minAttr, AtomicString::number(min));
93}
94
95double HTMLMeterElement::max() const
96{
97    return std::max(parseToDoubleForNumberType(getAttribute(maxAttr), std::max(1.0, min())), min());
98}
99
100void HTMLMeterElement::setMax(double max, ExceptionCode& ec)
101{
102    if (!std::isfinite(max)) {
103        ec = NOT_SUPPORTED_ERR;
104        return;
105    }
106    setAttribute(maxAttr, AtomicString::number(max));
107}
108
109double HTMLMeterElement::value() const
110{
111    double value = parseToDoubleForNumberType(getAttribute(valueAttr), 0);
112    return std::min(std::max(value, min()), max());
113}
114
115void HTMLMeterElement::setValue(double value, ExceptionCode& ec)
116{
117    if (!std::isfinite(value)) {
118        ec = NOT_SUPPORTED_ERR;
119        return;
120    }
121    setAttribute(valueAttr, AtomicString::number(value));
122}
123
124double HTMLMeterElement::low() const
125{
126    double low = parseToDoubleForNumberType(getAttribute(lowAttr), min());
127    return std::min(std::max(low, min()), max());
128}
129
130void HTMLMeterElement::setLow(double low, ExceptionCode& ec)
131{
132    if (!std::isfinite(low)) {
133        ec = NOT_SUPPORTED_ERR;
134        return;
135    }
136    setAttribute(lowAttr, AtomicString::number(low));
137}
138
139double HTMLMeterElement::high() const
140{
141    double high = parseToDoubleForNumberType(getAttribute(highAttr), max());
142    return std::min(std::max(high, low()), max());
143}
144
145void HTMLMeterElement::setHigh(double high, ExceptionCode& ec)
146{
147    if (!std::isfinite(high)) {
148        ec = NOT_SUPPORTED_ERR;
149        return;
150    }
151    setAttribute(highAttr, AtomicString::number(high));
152}
153
154double HTMLMeterElement::optimum() const
155{
156    double optimum = parseToDoubleForNumberType(getAttribute(optimumAttr), (max() + min()) / 2);
157    return std::min(std::max(optimum, min()), max());
158}
159
160void HTMLMeterElement::setOptimum(double optimum, ExceptionCode& ec)
161{
162    if (!std::isfinite(optimum)) {
163        ec = NOT_SUPPORTED_ERR;
164        return;
165    }
166    setAttribute(optimumAttr, AtomicString::number(optimum));
167}
168
169HTMLMeterElement::GaugeRegion HTMLMeterElement::gaugeRegion() const
170{
171    double lowValue = low();
172    double highValue = high();
173    double theValue = value();
174    double optimumValue = optimum();
175
176    if (optimumValue < lowValue) {
177        // The optimum range stays under low
178        if (theValue <= lowValue)
179            return GaugeRegionOptimum;
180        if (theValue <= highValue)
181            return GaugeRegionSuboptimal;
182        return GaugeRegionEvenLessGood;
183    }
184
185    if (highValue < optimumValue) {
186        // The optimum range stays over high
187        if (highValue <= theValue)
188            return GaugeRegionOptimum;
189        if (lowValue <= theValue)
190            return GaugeRegionSuboptimal;
191        return GaugeRegionEvenLessGood;
192    }
193
194    // The optimum range stays between high and low.
195    // According to the standard, <meter> never show GaugeRegionEvenLessGood in this case
196    // because the value is never less or greater than min or max.
197    if (lowValue <= theValue && theValue <= highValue)
198        return GaugeRegionOptimum;
199    return GaugeRegionSuboptimal;
200}
201
202double HTMLMeterElement::valueRatio() const
203{
204    double min = this->min();
205    double max = this->max();
206    double value = this->value();
207
208    if (max <= min)
209        return 0;
210    return (value - min) / (max - min);
211}
212
213void HTMLMeterElement::didElementStateChange()
214{
215    m_value->setWidthPercentage(valueRatio()*100);
216    m_value->updatePseudo();
217    if (RenderMeter* render = renderMeter())
218        render->updateFromElement();
219}
220
221RenderMeter* HTMLMeterElement::renderMeter() const
222{
223    if (renderer() && renderer()->isMeter())
224        return toRenderMeter(renderer());
225    return toRenderMeter(descendantsOfType<Element>(*userAgentShadowRoot()).first()->renderer());
226}
227
228void HTMLMeterElement::didAddUserAgentShadowRoot(ShadowRoot* root)
229{
230    ASSERT(!m_value);
231
232    RefPtr<MeterInnerElement> inner = MeterInnerElement::create(document());
233    root->appendChild(inner);
234
235    RefPtr<MeterBarElement> bar = MeterBarElement::create(document());
236    m_value = MeterValueElement::create(document());
237    m_value->setWidthPercentage(0);
238    m_value->updatePseudo();
239    bar->appendChild(m_value, ASSERT_NO_EXCEPTION);
240
241    inner->appendChild(bar, ASSERT_NO_EXCEPTION);
242}
243
244} // namespace
245#endif
246