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