2011-01-13 Dimitri Glazkov <dglazkov@chromium.org>
[WebKit.git] / Source / WebCore / html / RangeInputType.cpp
1 /*
2  * Copyright (C) 2010 Google Inc. All rights reserved.
3  * Copyright (C) 2011 Apple Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following disclaimer
13  * in the documentation and/or other materials provided with the
14  * distribution.
15  *     * Neither the name of Google Inc. nor the names of its
16  * contributors may be used to endorse or promote products derived from
17  * this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 #include "config.h"
33 #include "RangeInputType.h"
34
35 #include "HTMLInputElement.h"
36 #include "HTMLNames.h"
37 #include "HTMLParserIdioms.h"
38 #include "KeyboardEvent.h"
39 #include "RenderSlider.h"
40 #include "SliderThumbElement.h"
41 #include "StepRange.h"
42 #include <limits>
43 #include <wtf/MathExtras.h>
44 #include <wtf/PassOwnPtr.h>
45
46 namespace WebCore {
47
48 using namespace HTMLNames;
49 using namespace std;
50
51 static const double rangeDefaultMinimum = 0.0;
52 static const double rangeDefaultMaximum = 100.0;
53 static const double rangeDefaultStep = 1.0;
54 static const double rangeStepScaleFactor = 1.0;
55
56 PassOwnPtr<InputType> RangeInputType::create(HTMLInputElement* element)
57 {
58     return adoptPtr(new RangeInputType(element));
59 }
60
61 bool RangeInputType::isRangeControl() const
62 {
63     return true;
64 }
65
66 const AtomicString& RangeInputType::formControlType() const
67 {
68     return InputTypeNames::range();
69 }
70
71 double RangeInputType::valueAsNumber() const
72 {
73     return parseToDouble(element()->value(), numeric_limits<double>::quiet_NaN());
74 }
75
76 void RangeInputType::setValueAsNumber(double newValue, ExceptionCode&) const
77 {
78     element()->setValue(serialize(newValue));
79 }
80
81 bool RangeInputType::supportsRequired() const
82 {
83     return false;
84 }
85
86 bool RangeInputType::rangeUnderflow(const String& value) const
87 {
88     // Guaranteed by sanitization.
89     ASSERT_UNUSED(value, parseToDouble(value, numeric_limits<double>::quiet_NaN()) >= minimum());
90     return false;
91 }
92
93 bool RangeInputType::rangeOverflow(const String& value) const
94 {
95     // Guaranteed by sanitization.
96     ASSERT_UNUSED(value, parseToDouble(value, numeric_limits<double>::quiet_NaN()) <= maximum());
97     return false;
98 }
99
100 bool RangeInputType::supportsRangeLimitation() const
101 {
102     return true;
103 }
104
105 double RangeInputType::minimum() const
106 {
107     return parseToDouble(element()->fastGetAttribute(minAttr), rangeDefaultMinimum);
108 }
109
110 double RangeInputType::maximum() const
111 {
112     double max = parseToDouble(element()->fastGetAttribute(maxAttr), rangeDefaultMaximum);
113     // A remedy for the inconsistent min/max values.
114     // Sets the maximum to the default or the minimum value.
115     double min = minimum();
116     if (max < min)
117         max = std::max(min, rangeDefaultMaximum);
118     return max;
119 }
120
121 bool RangeInputType::stepMismatch(const String&, double) const
122 {
123     // stepMismatch doesn't occur for type=range. RenderSlider guarantees the
124     // value matches to step on user input, and sanitization takes care
125     // of the general case.
126     return false;
127 }
128
129 double RangeInputType::stepBase() const
130 {
131     return minimum();
132 }
133
134 double RangeInputType::defaultStep() const
135 {
136     return rangeDefaultStep;
137 }
138
139 double RangeInputType::stepScaleFactor() const
140 {
141     return rangeStepScaleFactor;
142 }
143
144 void RangeInputType::handleKeydownEvent(KeyboardEvent* event)
145 {
146     const String& key = event->keyIdentifier();
147     if (key != "Up" && key != "Right" && key != "Down" && key != "Left")
148         return;
149
150     ExceptionCode ec;
151     if (equalIgnoringCase(element()->fastGetAttribute(stepAttr), "any")) {
152         double min = minimum();
153         double max = maximum();
154         // FIXME: We can't use stepUp() for the step value "any". So, we increase
155         // or decrease the value by 1/100 of the value range. Is it reasonable?
156         double step = (max - min) / 100;
157         double current = parseToDouble(element()->value(), numeric_limits<double>::quiet_NaN());
158         ASSERT(isfinite(current));
159         // Stepping-up and -down for step="any" are special cases for type="range" from renderer for convenient.
160         // No stepping normally for step="any". They cannot be handled by stepUp()/stepDown()/stepUpFromRenderer().
161         // So calculating values stepped-up or -down here.
162         double newValue;
163         if (key == "Up" || key == "Right") {
164             newValue = current + step;
165             if (newValue > max)
166                 newValue = max;
167         } else {
168             newValue = current - step;
169             if (newValue < min)
170                 newValue = min;
171         }
172         if (newValue != current) {
173             setValueAsNumber(newValue, ec);
174             element()->dispatchFormControlChangeEvent();
175         }
176     } else {
177         int stepMagnification = (key == "Up" || key == "Right") ? 1 : -1;
178         // Reasonable stepping-up/-down by stepUpFromRenderer() unless step="any"
179         element()->stepUpFromRenderer(stepMagnification);
180     }
181     event->setDefaultHandled();
182 }
183
184 void RangeInputType::forwardEvent(Event* event)
185 {
186     if (element()->renderer() && (event->isMouseEvent() || event->isDragEvent() || event->isWheelEvent()))
187         toRenderSlider(element()->renderer())->forwardEvent(event);
188 }
189
190 void RangeInputType::createShadowSubtree()
191 {
192     element()->setShadowRoot(SliderThumbElement::create(element()->document()));
193 }
194
195 RenderObject* RangeInputType::createRenderer(RenderArena* arena, RenderStyle*) const
196 {
197     return new (arena) RenderSlider(element());
198 }
199
200 double RangeInputType::parseToDouble(const String& src, double defaultValue) const
201 {
202     double numberValue;
203     if (!parseToDoubleForNumberType(src, &numberValue))
204         return defaultValue;
205     ASSERT(isfinite(numberValue));
206     return numberValue;
207 }
208
209 String RangeInputType::serialize(double value) const
210 {
211     if (!isfinite(value))
212         return String();
213     return serializeForNumberType(value);
214 }
215
216 // FIXME: Could share this with BaseButtonInputType and BaseCheckableInputType if we had a common base class.
217 void RangeInputType::accessKeyAction(bool sendToAnyElement)
218 {
219     InputType::accessKeyAction(sendToAnyElement);
220
221     // Send mouse button events if the caller specified sendToAnyElement.
222     // FIXME: The comment above is no good. It says what we do, but not why.
223     element()->dispatchSimulatedClick(0, sendToAnyElement);
224 }
225
226 void RangeInputType::minOrMaxAttributeChanged()
227 {
228     InputType::minOrMaxAttributeChanged();
229
230     // Sanitize the value.
231     element()->setValue(element()->value());
232     element()->setNeedsStyleRecalc();
233 }
234
235 String RangeInputType::fallbackValue()
236 {
237     return serializeForNumberType(StepRange(element()).defaultValue());
238 }
239
240 String RangeInputType::sanitizeValue(const String& proposedValue)
241 {
242     // If the proposedValue is null than this is a reset scenario and we
243     // want the range input's value attribute to take priority over the
244     // calculated default (middle) value.
245     if (proposedValue.isNull())
246         return proposedValue;
247
248     return serializeForNumberType(StepRange(element()).clampValue(proposedValue));
249 }
250
251 bool RangeInputType::shouldRespectListAttribute()
252 {
253     return true;
254 }
255
256 } // namespace WebCore