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