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