Source/WebCore/ChangeLog

 12012-08-01 Keishi Hattori <keishi@webkit.org>
 2
 3 Slider should snap to datalist tick marks
 4 https://bugs.webkit.org/show_bug.cgi?id=92640
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 Input type=range slider snaps to datalist tick marks.
 9
 10 Test: fast/forms/datalist/range-snap-to-datalist.html
 11
 12 * html/HTMLInputElement.cpp:
 13 (WebCore::HTMLInputElement::findClosestTickMarkValue): Just calls InputType::findClosestTickMarkValue.
 14 (WebCore):
 15 * html/HTMLInputElement.h:
 16 (HTMLInputElement):
 17 * html/InputType.cpp:
 18 (WebCore):
 19 (WebCore::InputType::findClosestTickMarkValue): Overridden by RangeInputType.
 20 * html/InputType.h:
 21 (InputType):
 22 * html/RangeInputType.cpp:
 23 (WebCore::RangeInputType::RangeInputType):
 24 (WebCore):
 25 (WebCore::RangeInputType::listAttributeTargetChanged): Mark m_tickMarkValues as dirty.
 26 (WebCore::decimalCompare):
 27 (WebCore::RangeInputType::updateTickMarkValues): Sets m_tickMarkValues to a list of sorted valid datalist values.
 28 (WebCore::RangeInputType::findClosestTickMarkValue): Finds closest tick mark value to a given value.
 29 * html/RangeInputType.h:
 30 (RangeInputType):
 31 * html/shadow/SliderThumbElement.cpp:
 32 (WebCore):
 33 (WebCore::SliderThumbElement::setPositionFromPoint): Snaps to the closest tick mark value if it is within a certain distance.
 34
1352012-08-01 Mikhail Pozdnyakov <mikhail.pozdnyakov@intel.com>
236
337 [EFL] Crash at WebCore::toRenderSlider

Source/WebCore/html/HTMLInputElement.cpp

@@StepRange HTMLInputElement::createStepRange(AnyStepHandling anyStepHandling) con
313313 return m_inputType->createStepRange(anyStepHandling);
314314}
315315
 316#if ENABLE(DATALIST_ELEMENT)
 317Decimal HTMLInputElement::findClosestTickMarkValue(const Decimal& value)
 318{
 319 return m_inputType->findClosestTickMarkValue(value);
 320}
 321#endif
 322
316323void HTMLInputElement::stepUp(int n, ExceptionCode& ec)
317324{
318325 m_inputType->stepUp(n, ec);

Source/WebCore/html/HTMLInputElement.h

@@public:
7272 bool getAllowedValueStep(Decimal*) const;
7373 StepRange createStepRange(AnyStepHandling) const;
7474
 75#if ENABLE(DATALIST_ELEMENT)
 76 Decimal findClosestTickMarkValue(const Decimal&);
 77#endif
 78
7579 // Implementations of HTMLInputElement::stepUp() and stepDown().
7680 void stepUp(int, ExceptionCode&);
7781 void stepDown(int, ExceptionCode&);

Source/WebCore/html/InputType.cpp

@@String InputType::defaultToolTip() const
908908void InputType::listAttributeTargetChanged()
909909{
910910}
 911
 912Decimal InputType::findClosestTickMarkValue(const Decimal&)
 913{
 914 ASSERT_NOT_REACHED();
 915 return Decimal::nan();
 916}
911917#endif
912918
913919bool InputType::supportsIndeterminateAppearance() const

Source/WebCore/html/InputType.h

@@public:
281281 virtual String defaultToolTip() const;
282282#if ENABLE(DATALIST_ELEMENT)
283283 virtual void listAttributeTargetChanged();
 284 virtual Decimal findClosestTickMarkValue(const Decimal&);
284285#endif
285286
286287 // Parses the specified string for the type, and return

Source/WebCore/html/RangeInputType.cpp

5555#include "TouchList.h"
5656#endif
5757
 58#if ENABLE(DATALIST_ELEMENT)
 59#include "HTMLDataListElement.h"
 60#include "HTMLOptionElement.h"
 61#include <wtf/NonCopyingSort.h>
 62#endif
 63
5864namespace WebCore {
5965
6066using namespace HTMLNames;

@@PassOwnPtr<InputType> RangeInputType::create(HTMLInputElement* element)
7682 return adoptPtr(new RangeInputType(element));
7783}
7884
 85RangeInputType::RangeInputType(HTMLInputElement* element)
 86 : InputType(element)
 87#if ENABLE(DATALIST_ELEMENT)
 88 , m_tickMarkValuesDirty(true)
 89#endif
 90{
 91}
 92
7993bool RangeInputType::isRangeControl() const
8094{
8195 return true;

@@HTMLElement* RangeInputType::sliderThumbElement() const
319333#if ENABLE(DATALIST_ELEMENT)
320334void RangeInputType::listAttributeTargetChanged()
321335{
 336 m_tickMarkValuesDirty = true;
322337 element()->setNeedsStyleRecalc();
323338}
 339
 340static bool decimalCompare(const Decimal& a, const Decimal& b)
 341{
 342 return a < b;
 343}
 344
 345void RangeInputType::updateTickMarkValues()
 346{
 347 if (!m_tickMarkValuesDirty)
 348 return;
 349 m_tickMarkValues.clear();
 350 m_tickMarkValuesDirty = false;
 351 HTMLDataListElement* dataList = element()->dataList();
 352 if (!dataList)
 353 return;
 354 RefPtr<HTMLCollection> options = dataList->options();
 355 m_tickMarkValues.reserveCapacity(options->length());
 356 StepRange stepRange(createStepRange(RejectAny));
 357 for (unsigned i = 0; i < options->length(); ++i) {
 358 Node* node = options->item(i);
 359 HTMLOptionElement* optionElement = toHTMLOptionElement(node);
 360 String optionValue = optionElement->value();
 361 if (!element()->isValidValue(optionValue))
 362 continue;
 363 m_tickMarkValues.append(parseToNumber(optionValue, stepRange.defaultValue()));
 364 }
 365 m_tickMarkValues.shrinkToFit();
 366 nonCopyingSort(m_tickMarkValues.begin(), m_tickMarkValues.end(), decimalCompare);
 367}
 368
 369Decimal RangeInputType::findClosestTickMarkValue(const Decimal& value)
 370{
 371 updateTickMarkValues();
 372 if (!m_tickMarkValues.size())
 373 return Decimal::nan();
 374
 375 size_t left = 0;
 376 size_t right = m_tickMarkValues.size();
 377 size_t middle;
 378 while (true) {
 379 ASSERT(left <= right);
 380 middle = left + (right - left) / 2;
 381 if (!middle)
 382 break;
 383 if (middle == m_tickMarkValues.size() - 1 && m_tickMarkValues[middle] < value) {
 384 middle++;
 385 break;
 386 }
 387 if (m_tickMarkValues[middle - 1] <= value && m_tickMarkValues[middle] >= value)
 388 break;
 389
 390 if (m_tickMarkValues[middle] < value)
 391 left = middle;
 392 else
 393 right = middle;
 394 }
 395 const Decimal closestLeft = middle ? m_tickMarkValues[middle - 1] : Decimal::infinity(Decimal::Negative);
 396 const Decimal closestRight = middle != m_tickMarkValues.size() ? m_tickMarkValues[middle] : Decimal::infinity(Decimal::Positive);
 397 if (closestRight - value < value - closestLeft)
 398 return closestRight;
 399 return closestLeft;
 400}
324401#endif
325402
326403} // namespace WebCore

Source/WebCore/html/RangeInputType.h

@@public:
4242 static PassOwnPtr<InputType> create(HTMLInputElement*);
4343
4444private:
45  RangeInputType(HTMLInputElement* element) : InputType(element) { }
 45 RangeInputType(HTMLInputElement*);
4646 virtual bool isRangeControl() const OVERRIDE;
4747 virtual const AtomicString& formControlType() const OVERRIDE;
4848 virtual double valueAsDouble() const OVERRIDE;

@@private:
7272 virtual HTMLElement* sliderThumbElement() const OVERRIDE;
7373#if ENABLE(DATALIST_ELEMENT)
7474 virtual void listAttributeTargetChanged() OVERRIDE;
 75 void updateTickMarkValues();
 76 virtual Decimal findClosestTickMarkValue(const Decimal&) OVERRIDE;
 77
 78 bool m_tickMarkValuesDirty;
 79 Vector<Decimal> m_tickMarkValues;
7580#endif
7681};
7782

Source/WebCore/html/shadow/SliderThumbElement.cpp

@@using namespace std;
5151
5252namespace WebCore {
5353
 54using namespace HTMLNames;
 55
5456inline static Decimal sliderPosition(HTMLInputElement* element)
5557{
5658 const StepRange stepRange(element->createStepRange(RejectAny));

@@void SliderThumbElement::setPositionFromPoint(const LayoutPoint& point)
270272 const Decimal ratio = Decimal::fromDouble(static_cast<double>(position) / trackSize);
271273 const Decimal fraction = isVertical || !renderBox()->style()->isLeftToRightDirection() ? Decimal(1) - ratio : ratio;
272274 StepRange stepRange(input->createStepRange(RejectAny));
273  const Decimal value = stepRange.clampValue(stepRange.valueFromProportion(fraction));
 275 Decimal value = stepRange.clampValue(stepRange.valueFromProportion(fraction));
 276
 277#if ENABLE(DATALIST_ELEMENT)
 278 Decimal closest = input->findClosestTickMarkValue(value);
 279 if (closest.isFinite()) {
 280 double closestFraction = stepRange.proportionFromValue(closest).toDouble();
 281 double closestRatio = isVertical || !renderBox()->style()->isLeftToRightDirection() ? 1.0 - closestFraction : closestFraction;
 282 double closestPosition = trackSize * closestRatio;
 283 const int snapThreshold = 3;
 284 if (abs(closestPosition - static_cast<double>(position)) < snapThreshold)
 285 value = closest;
 286 }
 287#endif
274288
275289 // FIXME: This is no longer being set from renderer. Consider updating the method name.
276290 input->setValueFromRenderer(serializeForNumberType(value));

LayoutTests/ChangeLog

 12012-08-01 Keishi Hattori <keishi@webkit.org>
 2
 3 Slider should snap to datalist tick marks
 4 https://bugs.webkit.org/show_bug.cgi?id=92640
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 * fast/forms/datalist/range-snap-to-datalist-expected.txt: Added.
 9 * fast/forms/datalist/range-snap-to-datalist.html: Added.
 10
1112012-08-01 Raymond Toy <rtoy@google.com>
212
313 Enable codec tests in TestExpectations and update expected mp3 files

LayoutTests/fast/forms/datalist/range-snap-to-datalist-expected.txt

 1Tests if clicking close to a tick mark snaps to it.
 2
 3PASS input.value is "0"
 4PASS parseInt(input.value, 10) < 500 is true
 5PASS input.value is "0"
 6PASS parseInt(input.value, 10) < 500 is true
 7PASS input.value is "0"
 8PASS parseInt(input.value, 10) < 500 is true
 9PASS input.value is "0"
 10PASS input.value is '500'
 11PASS input.value is "0"
 12PASS input.value is '500'
 13PASS input.value is "0"
 14PASS input.value is '500'
 15PASS input.value is "0"
 16PASS input.value is '500'
 17PASS input.value is "0"
 18PASS input.value is '500'
 19PASS input.value is "0"
 20PASS parseInt(input.value, 10) > 500 is true
 21PASS input.value is "0"
 22PASS parseInt(input.value, 10) > 500 is true
 23PASS input.value is "0"
 24PASS parseInt(input.value, 10) > 500 is true
 25PASS successfullyParsed is true
 26
 27TEST COMPLETE
 28

LayoutTests/fast/forms/datalist/range-snap-to-datalist.html

 1<!DOCTYPE html>
 2<html>
 3<head>
 4<script src="../../js/resources/js-test-pre.js"></script>
 5</head>
 6<body>
 7<p id="description">Tests if clicking close to a tick mark snaps to it.</p>
 8<div id="console"></div>
 9<input id="input" type=range min=0 max=1000 step=1 list=target style="width: 100px">
 10<datalist id="target">
 11 <option>500</option>
 12</datalist>
 13<script>
 14var input = document.getElementById("input");
 15function clickSlider(offsetLeft) {
 16 var centerY = input.offsetTop + input.offsetHeight / 2;
 17 eventSender.mouseMoveTo(input.offsetLeft + offsetLeft, centerY);
 18 eventSender.mouseDown();
 19 eventSender.mouseUp();
 20}
 21function resetSliderPosition() {
 22 clickSlider(0);
 23 shouldBe('input.value', '"0"');
 24}
 25resetSliderPosition();
 26clickSlider(45);
 27shouldBeTrue('parseInt(input.value, 10) < 500');
 28resetSliderPosition();
 29clickSlider(46);
 30shouldBeTrue('parseInt(input.value, 10) < 500');
 31resetSliderPosition();
 32clickSlider(47);
 33shouldBeTrue('parseInt(input.value, 10) < 500');
 34resetSliderPosition();
 35clickSlider(48);
 36shouldBe('input.value', "'500'");
 37resetSliderPosition();
 38clickSlider(49);
 39shouldBe('input.value', "'500'");
 40resetSliderPosition();
 41clickSlider(50);
 42shouldBe('input.value', "'500'");
 43resetSliderPosition();
 44clickSlider(51);
 45shouldBe('input.value', "'500'");
 46resetSliderPosition();
 47clickSlider(52);
 48shouldBe('input.value', "'500'");
 49resetSliderPosition();
 50clickSlider(53);
 51shouldBeTrue('parseInt(input.value, 10) > 500');
 52resetSliderPosition();
 53clickSlider(54);
 54shouldBeTrue('parseInt(input.value, 10) > 500');
 55resetSliderPosition();
 56clickSlider(55);
 57shouldBeTrue('parseInt(input.value, 10) > 500');
 58</script>
 59
 60<script src="../../js/resources/js-test-post.js"></script>
 61</body>
 62</html>