initial import
[vuplus_webkit] / 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 "AXObjectCache.h"
36 #include "HTMLDivElement.h"
37 #include "HTMLInputElement.h"
38 #include "HTMLNames.h"
39 #include "HTMLParserIdioms.h"
40 #include "KeyboardEvent.h"
41 #include "MouseEvent.h"
42 #include "PlatformMouseEvent.h"
43 #include "RenderSlider.h"
44 #include "ShadowRoot.h"
45 #include "SliderThumbElement.h"
46 #include "StepRange.h"
47 #include <limits>
48 #include <wtf/MathExtras.h>
49 #include <wtf/PassOwnPtr.h>
50
51 namespace WebCore {
52
53 using namespace HTMLNames;
54 using namespace std;
55
56 static const double rangeDefaultMinimum = 0.0;
57 static const double rangeDefaultMaximum = 100.0;
58 static const double rangeDefaultStep = 1.0;
59 static const double rangeStepScaleFactor = 1.0;
60
61 PassOwnPtr<InputType> RangeInputType::create(HTMLInputElement* element)
62 {
63     return adoptPtr(new RangeInputType(element));
64 }
65
66 bool RangeInputType::isRangeControl() const
67 {
68     return true;
69 }
70
71 const AtomicString& RangeInputType::formControlType() const
72 {
73     return InputTypeNames::range();
74 }
75
76 double RangeInputType::valueAsNumber() const
77 {
78     return parseToDouble(element()->value(), numeric_limits<double>::quiet_NaN());
79 }
80
81 void RangeInputType::setValueAsNumber(double newValue, bool sendChangeEvent, ExceptionCode&) const
82 {
83     element()->setValue(serialize(newValue), sendChangeEvent);
84 }
85
86 bool RangeInputType::supportsRequired() const
87 {
88     return false;
89 }
90
91 bool RangeInputType::rangeUnderflow(const String& value) const
92 {
93     // Guaranteed by sanitization.
94     ASSERT_UNUSED(value, parseToDouble(value, numeric_limits<double>::quiet_NaN()) >= minimum());
95     return false;
96 }
97
98 bool RangeInputType::rangeOverflow(const String& value) const
99 {
100     // Guaranteed by sanitization.
101     ASSERT_UNUSED(value, parseToDouble(value, numeric_limits<double>::quiet_NaN()) <= maximum());
102     return false;
103 }
104
105 bool RangeInputType::supportsRangeLimitation() const
106 {
107     return true;
108 }
109
110 double RangeInputType::minimum() const
111 {
112     return parseToDouble(element()->fastGetAttribute(minAttr), rangeDefaultMinimum);
113 }
114
115 double RangeInputType::maximum() const
116 {
117     double max = parseToDouble(element()->fastGetAttribute(maxAttr), rangeDefaultMaximum);
118     // A remedy for the inconsistent min/max values.
119     // Sets the maximum to the default or the minimum value.
120     double min = minimum();
121     if (max < min)
122         max = std::max(min, rangeDefaultMaximum);
123     return max;
124 }
125
126 bool RangeInputType::isSteppable() const
127 {
128     return true;
129 }
130
131 bool RangeInputType::stepMismatch(const String&, double) const
132 {
133     // stepMismatch doesn't occur for type=range. RenderSlider guarantees the
134     // value matches to step on user input, and sanitization takes care
135     // of the general case.
136     return false;
137 }
138
139 double RangeInputType::stepBase() const
140 {
141     return minimum();
142 }
143
144 double RangeInputType::defaultStep() const
145 {
146     return rangeDefaultStep;
147 }
148
149 double RangeInputType::stepScaleFactor() const
150 {
151     return rangeStepScaleFactor;
152 }
153
154 void RangeInputType::handleMouseDownEvent(MouseEvent* event)
155 {
156     if (element()->disabled() || element()->readOnly())
157         return;
158
159     Node* targetNode = event->target()->toNode();
160     if (event->button() != LeftButton || !targetNode || (targetNode != element() && !targetNode->isDescendantOf(element()->shadowRoot())))
161         return;
162     SliderThumbElement* thumb = sliderThumbElementOf(element());
163     if (targetNode == thumb)
164         return;
165     thumb->dragFrom(event->absoluteLocation());
166 }
167
168 void RangeInputType::handleKeydownEvent(KeyboardEvent* event)
169 {
170     if (element()->disabled() || element()->readOnly())
171         return;
172
173     const String& key = event->keyIdentifier();
174
175     double current = parseToDouble(element()->value(), numeric_limits<double>::quiet_NaN());
176     ASSERT(isfinite(current));
177
178     double step, bigStep;
179     if (equalIgnoringCase(element()->fastGetAttribute(stepAttr), "any")) {
180         // FIXME: We can't use stepUp() for the step value "any". So, we increase
181         // or decrease the value by 1/100 of the value range. Is it reasonable?
182         step = (maximum() - minimum()) / 100;
183         bigStep = step * 10;
184     } else {
185         if (!element()->getAllowedValueStep(&step))
186             ASSERT_NOT_REACHED();
187
188         bigStep = (maximum() - minimum()) / 10;
189         if (bigStep < step)
190             bigStep = step;
191     }
192
193     bool isVertical = false;
194     if (element()->renderer()) {
195         ControlPart part = element()->renderer()->style()->appearance();
196         isVertical = part == SliderVerticalPart || part == MediaVolumeSliderPart;
197     }
198
199     double newValue;
200     if (key == "Up")
201         newValue = current + step;
202     else if (key == "Down")
203         newValue = current - step;
204     else if (key == "Left")
205         newValue = isVertical ? current + step : current - step;
206     else if (key == "Right")
207         newValue = isVertical ? current - step : current + step;
208     else if (key == "PageUp")
209         newValue = current + bigStep;
210     else if (key == "PageDown")
211         newValue = current - bigStep;
212     else if (key == "Home")
213         newValue = isVertical ? maximum() : minimum();
214     else if (key == "End")
215         newValue = isVertical ? minimum() : maximum();
216     else
217         return; // Did not match any key binding.
218
219     newValue = StepRange(element()).clampValue(newValue);
220
221     if (newValue != current) {
222         ExceptionCode ec;
223         bool sendChangeEvent = true;
224         setValueAsNumber(newValue, sendChangeEvent, ec);
225
226         if (AXObjectCache::accessibilityEnabled())
227             element()->document()->axObjectCache()->postNotification(element()->renderer(), AXObjectCache::AXValueChanged, true);
228         element()->dispatchFormControlChangeEvent();
229     }
230
231     event->setDefaultHandled();
232 }
233
234 void RangeInputType::createShadowSubtree()
235 {
236     Document* document = element()->document();
237     RefPtr<HTMLDivElement> track = HTMLDivElement::create(document);
238     track->setShadowPseudoId("-webkit-slider-runnable-track");
239     ExceptionCode ec = 0;
240     track->appendChild(SliderThumbElement::create(document), ec);
241     RefPtr<HTMLElement> container = SliderContainerElement::create(document);
242     container->appendChild(track.release(), ec);
243     container->appendChild(TrackLimiterElement::create(document), ec);
244     element()->ensureShadowRoot()->appendChild(container.release(), ec);
245 }
246
247 RenderObject* RangeInputType::createRenderer(RenderArena* arena, RenderStyle*) const
248 {
249     return new (arena) RenderSlider(element());
250 }
251
252 double RangeInputType::parseToDouble(const String& src, double defaultValue) const
253 {
254     double numberValue;
255     if (!parseToDoubleForNumberType(src, &numberValue))
256         return defaultValue;
257     ASSERT(isfinite(numberValue));
258     return numberValue;
259 }
260
261 String RangeInputType::serialize(double value) const
262 {
263     if (!isfinite(value))
264         return String();
265     return serializeForNumberType(value);
266 }
267
268 // FIXME: Could share this with BaseButtonInputType and BaseCheckableInputType if we had a common base class.
269 void RangeInputType::accessKeyAction(bool sendToAnyElement)
270 {
271     InputType::accessKeyAction(sendToAnyElement);
272
273     // Send mouse button events if the caller specified sendToAnyElement.
274     // FIXME: The comment above is no good. It says what we do, but not why.
275     element()->dispatchSimulatedClick(0, sendToAnyElement);
276 }
277
278 void RangeInputType::minOrMaxAttributeChanged()
279 {
280     InputType::minOrMaxAttributeChanged();
281
282     // Sanitize the value.
283     if (element()->hasDirtyValue())
284         element()->setValue(element()->value());
285     element()->setNeedsStyleRecalc();
286 }
287
288 void RangeInputType::setValue(const String& value, bool valueChanged, bool sendChangeEvent)
289 {
290     InputType::setValue(value, valueChanged, sendChangeEvent);
291
292     if (!valueChanged)
293         return;
294
295     sliderThumbElementOf(element())->setPositionFromValue();
296 }
297
298 String RangeInputType::fallbackValue()
299 {
300     return serializeForNumberType(StepRange(element()).defaultValue());
301 }
302
303 String RangeInputType::sanitizeValue(const String& proposedValue)
304 {
305     // If the proposedValue is null than this is a reset scenario and we
306     // want the range input's value attribute to take priority over the
307     // calculated default (middle) value.
308     if (proposedValue.isNull())
309         return proposedValue;
310
311     return serializeForNumberType(StepRange(element()).clampValue(proposedValue));
312 }
313
314 bool RangeInputType::shouldRespectListAttribute()
315 {
316     return true;
317 }
318
319 } // namespace WebCore