INPUT_MULTIPLE_FIELDS_UI: Step-up/-down of hour field should respect min/max attributes
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 28 Feb 2013 06:44:41 +0000 (06:44 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 28 Feb 2013 06:44:41 +0000 (06:44 +0000)
https://bugs.webkit.org/show_bug.cgi?id=109555

Patch by Kunihiko Sakamoto <ksakamoto@chromium.org> on 2013-02-27
Reviewed by Kent Tamura.

Source/WebCore:

Make step-up/-down of the hour field respect the min/max attributes of the element.
Note that it still accepts any keyboard inputs (the element
becomes 'invalid' state when out-of-range values entered).
Also, disable the hour field and/or the AMPM field when there is only single possible value.

Test: fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-readonly-subfield.html
      fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-stepup-stepdown-from-renderer.html
      fast/forms/time-multiple-fields/time-multiple-fields-readonly-subfield.html
      fast/forms/time-multiple-fields/time-multiple-fields-stepup-stepdown-from-renderer.html

* html/TimeInputType.cpp:
(WebCore::TimeInputType::setupLayoutParameters): Populates layoutParameters.{minimum,maximum}.
* html/shadow/DateTimeEditElement.cpp:
(DateTimeEditBuilder): Add data fields for min/max of day and hour fields.
(WebCore::DateTimeEditBuilder::DateTimeEditBuilder): Set newly added data members.
(WebCore::DateTimeEditBuilder::visitField): Pass minimum/maximum value to the month field constructors.
(WebCore::DateTimeEditBuilder::shouldAMPMFieldDisabled): Added.
(WebCore::DateTimeEditBuilder::shouldDayOfMonthFieldDisabled):
(WebCore::DateTimeEditBuilder::shouldHourFieldDisabled):
Disables the hour field when min, max, and value have the same hour, except when the minute
field is disabled (by step attribute), because we need to leave at least one field editable.
* html/shadow/DateTimeFieldElements.cpp:
(WebCore::DateTimeHourFieldElementBase::DateTimeHourFieldElementBase):
(WebCore::DateTimeHourFieldElementBase::initialize):
(WebCore::DateTimeHourFieldElementBase::setValueAsDate):
(WebCore::DateTimeHourFieldElementBase::setValueAsDateTimeFieldsState):
(WebCore::DateTimeHour11FieldElement::DateTimeHour11FieldElement):
(WebCore::DateTimeHour11FieldElement::create):
(WebCore::DateTimeHour11FieldElement::populateDateTimeFieldsState):
(WebCore::DateTimeHour11FieldElement::setValueAsInteger):
(WebCore::DateTimeHour11FieldElement::clampValueForHardLimits):
(WebCore::DateTimeHour12FieldElement::DateTimeHour12FieldElement):
(WebCore::DateTimeHour12FieldElement::create):
(WebCore::DateTimeHour12FieldElement::populateDateTimeFieldsState):
(WebCore::DateTimeHour12FieldElement::setValueAsInteger):
(WebCore::DateTimeHour12FieldElement::clampValueForHardLimits):
(WebCore::DateTimeHour23FieldElement::DateTimeHour23FieldElement):
(WebCore::DateTimeHour23FieldElement::create):
(WebCore::DateTimeHour23FieldElement::populateDateTimeFieldsState):
(WebCore::DateTimeHour23FieldElement::setValueAsInteger):
(WebCore::DateTimeHour23FieldElement::clampValueForHardLimits):
(WebCore::DateTimeHour24FieldElement::DateTimeHour24FieldElement):
(WebCore::DateTimeHour24FieldElement::create):
(WebCore::DateTimeHour24FieldElement::populateDateTimeFieldsState):
(WebCore::DateTimeHour24FieldElement::setValueAsInteger):
(WebCore::DateTimeHour24FieldElement::clampValueForHardLimits):
* html/shadow/DateTimeFieldElements.h: Splitted DateTimeHourFieldElement into a base class and four derived classes that represents different hour formats.
(DateTimeHourFieldElementBase): Added.
(DateTimeHour11FieldElement): Added. Represents 0-11 hour format.
(DateTimeHour12FieldElement): Added. Represents 1-12 hour format.
(DateTimeHour23FieldElement): Added. Represents 0-23 hour format.
(DateTimeHour24FieldElement): Added. Represents 1-24 hour format.

LayoutTests:

Added test cases with min/max attributes.

* fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-readonly-subfield-expected.txt:
* fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-readonly-subfield.html:
* fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-stepup-stepdown-from-renderer-expected.txt: Added.
* fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-stepup-stepdown-from-renderer.html: Added.
* fast/forms/time-multiple-fields/time-multiple-fields-readonly-subfield-expected.txt:
* fast/forms/time-multiple-fields/time-multiple-fields-readonly-subfield.html:
* fast/forms/time-multiple-fields/time-multiple-fields-stepup-stepdown-from-renderer-expected.txt:
* fast/forms/time-multiple-fields/time-multiple-fields-stepup-stepdown-from-renderer.html:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@144263 268f45cc-cd09-0410-ab3c-d52691b4dbfc

14 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-readonly-subfield-expected.txt
LayoutTests/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-readonly-subfield.html
LayoutTests/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-stepup-stepdown-from-renderer-expected.txt [new file with mode: 0644]
LayoutTests/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-stepup-stepdown-from-renderer.html [new file with mode: 0644]
LayoutTests/fast/forms/time-multiple-fields/time-multiple-fields-readonly-subfield-expected.txt
LayoutTests/fast/forms/time-multiple-fields/time-multiple-fields-readonly-subfield.html
LayoutTests/fast/forms/time-multiple-fields/time-multiple-fields-stepup-stepdown-from-renderer-expected.txt
LayoutTests/fast/forms/time-multiple-fields/time-multiple-fields-stepup-stepdown-from-renderer.html
Source/WebCore/ChangeLog
Source/WebCore/html/TimeInputType.cpp
Source/WebCore/html/shadow/DateTimeEditElement.cpp
Source/WebCore/html/shadow/DateTimeFieldElements.cpp
Source/WebCore/html/shadow/DateTimeFieldElements.h

index 425f8c2..e5b0c20 100644 (file)
@@ -1,3 +1,21 @@
+2013-02-27  Kunihiko Sakamoto  <ksakamoto@chromium.org>
+
+        INPUT_MULTIPLE_FIELDS_UI: Step-up/-down of hour field should respect min/max attributes
+        https://bugs.webkit.org/show_bug.cgi?id=109555
+
+        Reviewed by Kent Tamura.
+
+        Added test cases with min/max attributes.
+
+        * fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-readonly-subfield-expected.txt:
+        * fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-readonly-subfield.html:
+        * fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-stepup-stepdown-from-renderer-expected.txt: Added.
+        * fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-stepup-stepdown-from-renderer.html: Added.
+        * fast/forms/time-multiple-fields/time-multiple-fields-readonly-subfield-expected.txt:
+        * fast/forms/time-multiple-fields/time-multiple-fields-readonly-subfield.html:
+        * fast/forms/time-multiple-fields/time-multiple-fields-stepup-stepdown-from-renderer-expected.txt:
+        * fast/forms/time-multiple-fields/time-multiple-fields-stepup-stepdown-from-renderer.html:
+
 2013-02-27  Adam Barth  <abarth@webkit.org>
 
         [Chromium] Enable threaded HTML parser by default in DumpRenderTree
index 2bd1683..239579d 100644 (file)
@@ -25,8 +25,14 @@ PASS isReadOnlyField(createInput("2013-01-16T11:30", "2013-01-16T23:00", step1da
 PASS isReadOnlyField(createInput("2013-01-16T11:30", "2013-01-16T23:00", step1day, "2013-01-16T11:30"), pseudoAMPM) is false
 PASS isReadOnlyField(createInput("2013-01-16T11:30", "2013-01-16T23:00", step1day, "2013-01-16T11:30"), pseudoMinute) is true
 PASS isReadOnlyField(createInput("2013-01-16T11:30", "2013-01-16T11:59", step1day, "2013-01-16T11:30"), pseudoHour) is false
-PASS isReadOnlyField(createInput("2013-01-16T11:30", "2013-01-16T11:59", step1day, "2013-01-16T11:30"), pseudoAMPM) is false
+PASS isReadOnlyField(createInput("2013-01-16T11:30", "2013-01-16T11:59", step1day, "2013-01-16T11:30"), pseudoAMPM) is true
 PASS isReadOnlyField(createInput("2013-01-16T11:30", "2013-01-16T11:59", step1day, "2013-01-16T11:30"), pseudoMinute) is true
+PASS isReadOnlyField(createInput("2013-01-16T11:30", "2013-01-16T11:59", step1min, ""), pseudoHour) is true
+PASS isReadOnlyField(createInput("2013-01-16T11:30", "2013-01-16T11:59", step1min, ""), pseudoAMPM) is true
+PASS isReadOnlyField(createInput("2013-01-16T11:30", "2013-01-16T11:59", step1min, ""), pseudoMinute) is false
+AM/PM field:
+PASS isReadOnlyField(createInput("2013-01-16T08:00", "2013-01-16T11:00", step1min, ""), pseudoAMPM) is true
+PASS isReadOnlyField(createInput("2013-01-16T08:00", "2013-01-17T11:00", step1min, ""), pseudoAMPM) is false
 PASS successfullyParsed is true
 
 TEST COMPLETE
index f9bcf57..0df2471 100644 (file)
@@ -28,6 +28,7 @@ var pseudoMinute = '-webkit-datetime-edit-minute-field';
 var pseudoHour = '-webkit-datetime-edit-hour-field';
 var pseudoAMPM = '-webkit-datetime-edit-ampm-field';
 var step1day = '86400';
+var step1min = '60';
 
 description('Sub-fields in input[type=datetime-local] should be read-only in some cases. This requires window.internals.');
 debug('createInput argument order: min, max, step, value');
@@ -59,9 +60,17 @@ shouldBeFalse('isReadOnlyField(createInput("2013-01-16T11:30", "2013-01-16T23:00
 shouldBeTrue('isReadOnlyField(createInput("2013-01-16T11:30", "2013-01-16T23:00", step1day, "2013-01-16T11:30"), pseudoMinute)');
 
 shouldBeFalse('isReadOnlyField(createInput("2013-01-16T11:30", "2013-01-16T11:59", step1day, "2013-01-16T11:30"), pseudoHour)');
-shouldBeFalse('isReadOnlyField(createInput("2013-01-16T11:30", "2013-01-16T11:59", step1day, "2013-01-16T11:30"), pseudoAMPM)');
+shouldBeTrue('isReadOnlyField(createInput("2013-01-16T11:30", "2013-01-16T11:59", step1day, "2013-01-16T11:30"), pseudoAMPM)');
 shouldBeTrue('isReadOnlyField(createInput("2013-01-16T11:30", "2013-01-16T11:59", step1day, "2013-01-16T11:30"), pseudoMinute)');
 
+shouldBeTrue('isReadOnlyField(createInput("2013-01-16T11:30", "2013-01-16T11:59", step1min, ""), pseudoHour)');
+shouldBeTrue('isReadOnlyField(createInput("2013-01-16T11:30", "2013-01-16T11:59", step1min, ""), pseudoAMPM)');
+shouldBeFalse('isReadOnlyField(createInput("2013-01-16T11:30", "2013-01-16T11:59", step1min, ""), pseudoMinute)');
+
+debug('AM/PM field:');
+shouldBeTrue('isReadOnlyField(createInput("2013-01-16T08:00", "2013-01-16T11:00", step1min, ""), pseudoAMPM)');
+shouldBeFalse('isReadOnlyField(createInput("2013-01-16T08:00", "2013-01-17T11:00", step1min, ""), pseudoAMPM)');
+
 </script>
 <script src="../../js/resources/js-test-post.js"></script>
 </body>
diff --git a/LayoutTests/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-stepup-stepdown-from-renderer-expected.txt b/LayoutTests/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-stepup-stepdown-from-renderer-expected.txt
new file mode 100644 (file)
index 0000000..af0e030
--- /dev/null
@@ -0,0 +1,16 @@
+Check stepping-up and -down for datetime-local input fields from renderer.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Function arguments are (value, step, min, max, [keySequence]).
+The hour field respects min/max attributes when they have the same date.
+PASS stepUp("2013-01-16T17:00", 1, "2013-01-16T15:00", "2013-01-16T17:00") is "2013-01-16T15:00"
+PASS stepDown("2013-01-16T15:00", 1, "2013-01-16T15:00", "2013-01-16T17:00") is "2013-01-16T17:00"
+PASS stepUp("2013-01-16T17:00", 1, "2013-01-16T15:00", "2013-01-17T17:00") is "2013-01-16T18:00"
+PASS stepDown("2013-01-16T17:00", 1, "2013-01-16T17:00", "2013-01-17T20:00") is "2013-01-16T16:00"
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-stepup-stepdown-from-renderer.html b/LayoutTests/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-stepup-stepdown-from-renderer.html
new file mode 100644 (file)
index 0000000..137fcf7
--- /dev/null
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../js/resources/js-test-pre.js"></script>
+<script src="../resources/common.js"></script>
+</head>
+<body>
+<script>
+description('Check stepping-up and -down for datetime-local input fields from renderer.');
+
+var input = document.createElement('input');
+
+document.body.appendChild(input);
+
+function keyDown(key, modifiers)
+{
+    if (!window.eventSender)
+        return;
+    eventSender.keyDown(key, modifiers);
+}
+
+function setInputAttributes(value, min, max, step) {
+    input.value = value;
+    input.min = min;
+    input.max = max;
+    input.step = step;
+}
+
+function test(value, step, min, max, keySequence) {
+    setInputAttributes(value, min, max, step);
+    for (var i = 0; i < keySequence.length; i++)
+        keyDown(keySequence[i]);
+    return input.value;
+}
+
+function stepUp(value, step, min, max) {
+    return test(value, step, min, max, ['upArrow']);
+}
+
+function stepDown(value, step, min, max) {
+    return test(value, step, min, max, ['downArrow']);
+}
+
+input.type = 'datetime-local';
+input.focus();
+debug('Function arguments are (value, step, min, max, [keySequence]).');
+debug('The hour field respects min/max attributes when they have the same date.');
+keyDown('rightArrow');
+keyDown('rightArrow');
+keyDown('rightArrow');
+shouldBeEqualToString('stepUp("2013-01-16T17:00", 1, "2013-01-16T15:00", "2013-01-16T17:00")', '2013-01-16T15:00');
+shouldBeEqualToString('stepDown("2013-01-16T15:00", 1, "2013-01-16T15:00", "2013-01-16T17:00")', '2013-01-16T17:00');
+shouldBeEqualToString('stepUp("2013-01-16T17:00", 1, "2013-01-16T15:00", "2013-01-17T17:00")', '2013-01-16T18:00');
+shouldBeEqualToString('stepDown("2013-01-16T17:00", 1, "2013-01-16T17:00", "2013-01-17T20:00")', '2013-01-16T16:00');
+
+debug('');
+document.body.removeChild(input);
+</script>
+<script src="../../js/resources/js-test-post.js"></script>
+</body>
+</html>
index f99e40b..efe7f10 100644 (file)
@@ -3,37 +3,52 @@ Sub-fields in input[type=time] should be read-only in some cases. This requires
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 
-createTimeInput argument order: min, step, value
+createTimeInput argument order: min, max, step, value
 
 Milliseconds field:
-PASS isReadOnlyField(createTimeInput("00:00:00.500", step1sec, ""), pseudoMillisecond) is true
-PASS isReadOnlyField(createTimeInput("00:00:00.500", step1sec, "00:00:00.500"), pseudoMillisecond) is true
-PASS isReadOnlyField(createTimeInput("00:00:00.500", step1sec, "00:00:00.600"), pseudoMillisecond) is false
-PASS isReadOnlyField(createTimeInput("00:00:00.000", step1sec, "00:00:00.600"), pseudoMillisecond) is false
+PASS isReadOnlyField(createTimeInput("00:00:00.500", null, step1sec, ""), pseudoMillisecond) is true
+PASS isReadOnlyField(createTimeInput("00:00:00.500", null, step1sec, "00:00:00.500"), pseudoMillisecond) is true
+PASS isReadOnlyField(createTimeInput("00:00:00.500", null, step1sec, "00:00:00.600"), pseudoMillisecond) is false
+PASS isReadOnlyField(createTimeInput("00:00:00.000", null, step1sec, "00:00:00.600"), pseudoMillisecond) is false
 Seconds field:
-PASS isReadOnlyField(createTimeInput("00:00:30", step1min, ""), pseudoSecond) is true
-PASS isReadOnlyField(createTimeInput("00:00:30", step1min, "00:00:30"), pseudoSecond) is true
-PASS isReadOnlyField(createTimeInput("00:00:30.500", step1min, "00:00:30.600"), pseudoSecond) is true
-PASS isReadOnlyField(createTimeInput("00:00:30", step1min, "00:00:35"), pseudoSecond) is false
-PASS isReadOnlyField(createTimeInput("00:00:00", step1min, "00:00:35"), pseudoSecond) is false
+PASS isReadOnlyField(createTimeInput("00:00:30", null, step1min, ""), pseudoSecond) is true
+PASS isReadOnlyField(createTimeInput("00:00:30", null, step1min, "00:00:30"), pseudoSecond) is true
+PASS isReadOnlyField(createTimeInput("00:00:30.500", null, step1min, "00:00:30.600"), pseudoSecond) is true
+PASS isReadOnlyField(createTimeInput("00:00:30", null, step1min, "00:00:35"), pseudoSecond) is false
+PASS isReadOnlyField(createTimeInput("00:00:00", null, step1min, "00:00:35"), pseudoSecond) is false
 Minutes field:
-PASS isReadOnlyField(createTimeInput("00:30", step1hour, ""), pseudoMinute) is true
-PASS isReadOnlyField(createTimeInput("00:30", step1hour, "00:30"), pseudoMinute) is true
-PASS isReadOnlyField(createTimeInput("00:30", step1hour, "00:35"), pseudoMinute) is false
-PASS isReadOnlyField(createTimeInput("00:00", step1hour, "00:35"), pseudoMinute) is false
+PASS isReadOnlyField(createTimeInput("00:30", null, step1hour, ""), pseudoMinute) is true
+PASS isReadOnlyField(createTimeInput("00:30", null, step1hour, "00:30"), pseudoMinute) is true
+PASS isReadOnlyField(createTimeInput("00:30", null, step1hour, "00:35"), pseudoMinute) is false
+PASS isReadOnlyField(createTimeInput("00:00", null, step1hour, "00:35"), pseudoMinute) is false
 Hour field:
-PASS isReadOnlyField(createTimeInput("11:00", step1day, ""), pseudoHour) is false
-PASS isReadOnlyField(createTimeInput("11:00", step1day, ""), pseudoAMPM) is false
-PASS isReadOnlyField(createTimeInput("11:00", step1day, ""), pseudoMinute) is true
-PASS isReadOnlyField(createTimeInput("11:30", step1day, "11:30"), pseudoHour) is false
-PASS isReadOnlyField(createTimeInput("11:30", step1day, "11:30"), pseudoAMPM) is false
-PASS isReadOnlyField(createTimeInput("11:30", step1day, "11:30"), pseudoMinute) is true
-PASS isReadOnlyField(createTimeInput("11:30", step1day, "12:30"), pseudoHour) is false
-PASS isReadOnlyField(createTimeInput("11:30", step1day, "12:30"), pseudoAMPM) is false
-PASS isReadOnlyField(createTimeInput("11:30", step1day, "12:30"), pseudoMinute) is true
-PASS isReadOnlyField(createTimeInput("00:00", step1day, "12:30"), pseudoHour) is false
-PASS isReadOnlyField(createTimeInput("00:00", step1day, "12:30"), pseudoAMPM) is false
-PASS isReadOnlyField(createTimeInput("00:00", step1day, "12:30"), pseudoMinute) is false
+PASS isReadOnlyField(createTimeInput("11:00", null, step1day, ""), pseudoHour) is false
+PASS isReadOnlyField(createTimeInput("11:00", null, step1day, ""), pseudoAMPM) is false
+PASS isReadOnlyField(createTimeInput("11:00", null, step1day, ""), pseudoMinute) is true
+PASS isReadOnlyField(createTimeInput("11:30", null, step1day, "11:30"), pseudoHour) is false
+PASS isReadOnlyField(createTimeInput("11:30", null, step1day, "11:30"), pseudoAMPM) is false
+PASS isReadOnlyField(createTimeInput("11:30", null, step1day, "11:30"), pseudoMinute) is true
+PASS isReadOnlyField(createTimeInput("11:30", null, step1day, "12:30"), pseudoHour) is false
+PASS isReadOnlyField(createTimeInput("11:30", null, step1day, "12:30"), pseudoAMPM) is false
+PASS isReadOnlyField(createTimeInput("11:30", null, step1day, "12:30"), pseudoMinute) is true
+PASS isReadOnlyField(createTimeInput("00:00", null, step1day, "12:30"), pseudoHour) is false
+PASS isReadOnlyField(createTimeInput("00:00", null, step1day, "12:30"), pseudoAMPM) is false
+PASS isReadOnlyField(createTimeInput("00:00", null, step1day, "12:30"), pseudoMinute) is false
+PASS isReadOnlyField(createTimeInput("00:00", "00:30", step1min, ""), pseudoHour) is true
+PASS isReadOnlyField(createTimeInput("00:00", "00:30", step1min, ""), pseudoAMPM) is true
+PASS isReadOnlyField(createTimeInput("00:00", "00:30", step1min, ""), pseudoMinute) is false
+PASS isReadOnlyField(createTimeInput("00:00", "00:00", step1hour, ""), pseudoHour) is false
+PASS isReadOnlyField(createTimeInput("00:00", "00:00", step1hour, ""), pseudoAMPM) is true
+PASS isReadOnlyField(createTimeInput("00:00", "00:00", step1hour, ""), pseudoMinute) is true
+AM/PM field:
+PASS isReadOnlyField(createTimeInput("00:00", null, step1min, ""), pseudoAMPM) is false
+PASS isReadOnlyField(createTimeInput("23:00", null, step1min, ""), pseudoAMPM) is false
+PASS isReadOnlyField(createTimeInput(null, "01:00", step1min, ""), pseudoAMPM) is false
+PASS isReadOnlyField(createTimeInput(null, "23:00", step1min, ""), pseudoAMPM) is false
+PASS isReadOnlyField(createTimeInput("00:00", "11:59", step1min, ""), pseudoAMPM) is true
+PASS isReadOnlyField(createTimeInput("00:00", "12:00", step1min, ""), pseudoAMPM) is false
+PASS isReadOnlyField(createTimeInput("12:00", "12:00", step1min, ""), pseudoAMPM) is true
+PASS isReadOnlyField(createTimeInput("12:00", "23:59", step1min, ""), pseudoAMPM) is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
index c876493..1faf1be 100644 (file)
@@ -2,10 +2,11 @@
 <body>
 <script src="../../js/resources/js-test-pre.js"></script>
 <script>
-function createTimeInput(min, step, value) {
+function createTimeInput(min, max, step, value) {
     var input = document.createElement('input');
     input.type = 'time';
     input.min = min;
+    input.max = max;
     input.step = step;
     if (value)
         input.value = value;
@@ -31,41 +32,58 @@ var step1hour = '3600';
 var step1day = '86400';
 
 description('Sub-fields in input[type=time] should be read-only in some cases. This requires window.internals.');
-debug('createTimeInput argument order: min, step, value');
+debug('createTimeInput argument order: min, max, step, value');
 debug('');
 
 debug('Milliseconds field:');
-shouldBeTrue('isReadOnlyField(createTimeInput("00:00:00.500", step1sec, ""), pseudoMillisecond)');
-shouldBeTrue('isReadOnlyField(createTimeInput("00:00:00.500", step1sec, "00:00:00.500"), pseudoMillisecond)');
-shouldBeFalse('isReadOnlyField(createTimeInput("00:00:00.500", step1sec, "00:00:00.600"), pseudoMillisecond)');
-shouldBeFalse('isReadOnlyField(createTimeInput("00:00:00.000", step1sec, "00:00:00.600"), pseudoMillisecond)');
+shouldBeTrue('isReadOnlyField(createTimeInput("00:00:00.500", null, step1sec, ""), pseudoMillisecond)');
+shouldBeTrue('isReadOnlyField(createTimeInput("00:00:00.500", null, step1sec, "00:00:00.500"), pseudoMillisecond)');
+shouldBeFalse('isReadOnlyField(createTimeInput("00:00:00.500", null, step1sec, "00:00:00.600"), pseudoMillisecond)');
+shouldBeFalse('isReadOnlyField(createTimeInput("00:00:00.000", null, step1sec, "00:00:00.600"), pseudoMillisecond)');
 
 debug('Seconds field:');
-shouldBeTrue('isReadOnlyField(createTimeInput("00:00:30", step1min, ""), pseudoSecond)');
-shouldBeTrue('isReadOnlyField(createTimeInput("00:00:30", step1min, "00:00:30"), pseudoSecond)');
-shouldBeTrue('isReadOnlyField(createTimeInput("00:00:30.500", step1min, "00:00:30.600"), pseudoSecond)');
-shouldBeFalse('isReadOnlyField(createTimeInput("00:00:30", step1min, "00:00:35"), pseudoSecond)');
-shouldBeFalse('isReadOnlyField(createTimeInput("00:00:00", step1min, "00:00:35"), pseudoSecond)');
+shouldBeTrue('isReadOnlyField(createTimeInput("00:00:30", null, step1min, ""), pseudoSecond)');
+shouldBeTrue('isReadOnlyField(createTimeInput("00:00:30", null, step1min, "00:00:30"), pseudoSecond)');
+shouldBeTrue('isReadOnlyField(createTimeInput("00:00:30.500", null, step1min, "00:00:30.600"), pseudoSecond)');
+shouldBeFalse('isReadOnlyField(createTimeInput("00:00:30", null, step1min, "00:00:35"), pseudoSecond)');
+shouldBeFalse('isReadOnlyField(createTimeInput("00:00:00", null, step1min, "00:00:35"), pseudoSecond)');
 
 debug('Minutes field:');
-shouldBeTrue('isReadOnlyField(createTimeInput("00:30", step1hour, ""), pseudoMinute)');
-shouldBeTrue('isReadOnlyField(createTimeInput("00:30", step1hour, "00:30"), pseudoMinute)');
-shouldBeFalse('isReadOnlyField(createTimeInput("00:30", step1hour, "00:35"), pseudoMinute)');
-shouldBeFalse('isReadOnlyField(createTimeInput("00:00", step1hour, "00:35"), pseudoMinute)');
+shouldBeTrue('isReadOnlyField(createTimeInput("00:30", null, step1hour, ""), pseudoMinute)');
+shouldBeTrue('isReadOnlyField(createTimeInput("00:30", null, step1hour, "00:30"), pseudoMinute)');
+shouldBeFalse('isReadOnlyField(createTimeInput("00:30", null, step1hour, "00:35"), pseudoMinute)');
+shouldBeFalse('isReadOnlyField(createTimeInput("00:00", null, step1hour, "00:35"), pseudoMinute)');
 
 debug('Hour field:');
-shouldBeFalse('isReadOnlyField(createTimeInput("11:00", step1day, ""), pseudoHour)');
-shouldBeFalse('isReadOnlyField(createTimeInput("11:00", step1day, ""), pseudoAMPM)');
-shouldBeTrue('isReadOnlyField(createTimeInput("11:00", step1day, ""), pseudoMinute)');
-shouldBeFalse('isReadOnlyField(createTimeInput("11:30", step1day, "11:30"), pseudoHour)');
-shouldBeFalse('isReadOnlyField(createTimeInput("11:30", step1day, "11:30"), pseudoAMPM)');
-shouldBeTrue('isReadOnlyField(createTimeInput("11:30", step1day, "11:30"), pseudoMinute)');
-shouldBeFalse('isReadOnlyField(createTimeInput("11:30", step1day, "12:30"), pseudoHour)');
-shouldBeFalse('isReadOnlyField(createTimeInput("11:30", step1day, "12:30"), pseudoAMPM)');
-shouldBeTrue('isReadOnlyField(createTimeInput("11:30", step1day, "12:30"), pseudoMinute)');
-shouldBeFalse('isReadOnlyField(createTimeInput("00:00", step1day, "12:30"), pseudoHour)');
-shouldBeFalse('isReadOnlyField(createTimeInput("00:00", step1day, "12:30"), pseudoAMPM)');
-shouldBeFalse('isReadOnlyField(createTimeInput("00:00", step1day, "12:30"), pseudoMinute)');
+shouldBeFalse('isReadOnlyField(createTimeInput("11:00", null, step1day, ""), pseudoHour)');
+shouldBeFalse('isReadOnlyField(createTimeInput("11:00", null, step1day, ""), pseudoAMPM)');
+shouldBeTrue('isReadOnlyField(createTimeInput("11:00", null, step1day, ""), pseudoMinute)');
+shouldBeFalse('isReadOnlyField(createTimeInput("11:30", null, step1day, "11:30"), pseudoHour)');
+shouldBeFalse('isReadOnlyField(createTimeInput("11:30", null, step1day, "11:30"), pseudoAMPM)');
+shouldBeTrue('isReadOnlyField(createTimeInput("11:30", null, step1day, "11:30"), pseudoMinute)');
+shouldBeFalse('isReadOnlyField(createTimeInput("11:30", null, step1day, "12:30"), pseudoHour)');
+shouldBeFalse('isReadOnlyField(createTimeInput("11:30", null, step1day, "12:30"), pseudoAMPM)');
+shouldBeTrue('isReadOnlyField(createTimeInput("11:30", null, step1day, "12:30"), pseudoMinute)');
+shouldBeFalse('isReadOnlyField(createTimeInput("00:00", null, step1day, "12:30"), pseudoHour)');
+shouldBeFalse('isReadOnlyField(createTimeInput("00:00", null, step1day, "12:30"), pseudoAMPM)');
+shouldBeFalse('isReadOnlyField(createTimeInput("00:00", null, step1day, "12:30"), pseudoMinute)');
+shouldBeTrue('isReadOnlyField(createTimeInput("00:00", "00:30", step1min, ""), pseudoHour)');
+shouldBeTrue('isReadOnlyField(createTimeInput("00:00", "00:30", step1min, ""), pseudoAMPM)');
+shouldBeFalse('isReadOnlyField(createTimeInput("00:00", "00:30", step1min, ""), pseudoMinute)');
+shouldBeFalse('isReadOnlyField(createTimeInput("00:00", "00:00", step1hour, ""), pseudoHour)');
+shouldBeTrue('isReadOnlyField(createTimeInput("00:00", "00:00", step1hour, ""), pseudoAMPM)');
+shouldBeTrue('isReadOnlyField(createTimeInput("00:00", "00:00", step1hour, ""), pseudoMinute)');
+
+debug('AM/PM field:');
+shouldBeFalse('isReadOnlyField(createTimeInput("00:00", null, step1min, ""), pseudoAMPM)');
+shouldBeFalse('isReadOnlyField(createTimeInput("23:00", null, step1min, ""), pseudoAMPM)');
+shouldBeFalse('isReadOnlyField(createTimeInput(null, "01:00", step1min, ""), pseudoAMPM)');
+shouldBeFalse('isReadOnlyField(createTimeInput(null, "23:00", step1min, ""), pseudoAMPM)');
+shouldBeTrue('isReadOnlyField(createTimeInput("00:00", "11:59", step1min, ""), pseudoAMPM)');
+shouldBeFalse('isReadOnlyField(createTimeInput("00:00", "12:00", step1min, ""), pseudoAMPM)');
+shouldBeTrue('isReadOnlyField(createTimeInput("12:00", "12:00", step1min, ""), pseudoAMPM)');
+shouldBeTrue('isReadOnlyField(createTimeInput("12:00", "23:59", step1min, ""), pseudoAMPM)');
+
 
 </script>
 <script src="../../js/resources/js-test-post.js"></script>
index 6bdb506..d2e2333 100644 (file)
@@ -108,6 +108,28 @@ PASS test("06:00", 7200, null, null, ["delete", "downArrow"]) is "00:00"
 PASS getUserAgentShadowTextContent(input) is "12:00 AM"
 PASS test("06:00", 7200, "01:00", null, ["delete", "upArrow"]) is "01:00"
 PASS test("06:00", 7200, "01:00", null, ["delete", "downArrow"]) is "11:00"
+PASS stepUp("17:00", 1, "17:00", "20:00") is "18:00"
+PASS stepDown("17:00", 1, "17:00", "20:00") is "20:00"
+PASS stepUp("17:00", 1, "15:00", "17:00") is "15:00"
+PASS stepDown("17:00", 1, "15:00", "17:00") is "16:00"
+PASS stepUp("15:00", 1, "17:00", "20:00") is "17:00"
+PASS stepDown("15:00", 1, "17:00", "20:00") is "20:00"
+PASS stepUp("15:00", 1, "13:00", "13:00") is "13:00"
+PASS stepDown("15:00", 1, "13:00", "13:00") is "13:00"
+PASS stepUp("12:00", 1, "12:00", "15:00") is "13:00"
+PASS stepDown("12:00", 1, "12:00", "15:00") is "23:00"
+PASS stepUp("15:00", 1, "12:00", "15:00") is "16:00"
+PASS stepDown("15:00", 1, "12:00", "15:00") is "14:00"
+PASS stepUp("12:00", 1, "10:00", "12:00") is "13:00"
+PASS stepDown("12:00", 1, "10:00", "12:00") is "23:00"
+PASS stepUp("00:00", 1, "00:00", "03:00") is "01:00"
+PASS stepDown("00:00", 1, "00:00", "03:00") is "11:00"
+PASS stepUp("15:00", 1, "10:00", "15:00") is "16:00"
+PASS stepDown("10:00", 1, "10:00", "15:00") is "09:00"
+PASS stepUp("17:00", 7200, "17:00", "20:00") is "19:00"
+PASS stepDown("17:00", 7200, "17:00", "20:00") is "19:00"
+PASS stepUp("17:00", 7200, "17:00", "18:00") is "17:00"
+PASS stepDown("17:00", 7200, "17:00", "18:00") is "17:00"
 Hours, 0-11
 PASS stepUp("11:00", 1, null, null) is "00:00"
 PASS getUserAgentShadowTextContent(input) is "00:00 AM"
@@ -121,6 +143,26 @@ PASS test("06:00", 7200, null, null, ["delete", "upArrow"]) is "00:00"
 PASS test("06:00", 7200, null, null, ["delete", "downArrow"]) is "10:00"
 PASS test("06:00", 7200, "01:00", null, ["delete", "upArrow"]) is "01:00"
 PASS test("06:00", 7200, "01:00", null, ["delete", "downArrow"]) is "11:00"
+PASS stepUp("17:00", 1, "17:00", "20:00") is "18:00"
+PASS stepDown("17:00", 1, "17:00", "20:00") is "20:00"
+PASS stepUp("17:00", 1, "15:00", "17:00") is "15:00"
+PASS stepDown("17:00", 1, "15:00", "17:00") is "16:00"
+PASS stepUp("15:00", 1, "17:00", "20:00") is "17:00"
+PASS stepDown("15:00", 1, "17:00", "20:00") is "20:00"
+PASS stepUp("15:00", 1, "13:00", "13:00") is "13:00"
+PASS stepDown("15:00", 1, "13:00", "13:00") is "13:00"
+PASS stepUp("12:00", 1, "12:00", "15:00") is "13:00"
+PASS stepDown("12:00", 1, "12:00", "15:00") is "15:00"
+PASS stepUp("15:00", 1, "12:00", "15:00") is "12:00"
+PASS stepDown("15:00", 1, "12:00", "15:00") is "14:00"
+PASS stepUp("12:00", 1, "10:00", "12:00") is "13:00"
+PASS stepDown("12:00", 1, "10:00", "12:00") is "23:00"
+PASS stepUp("00:00", 1, "00:00", "03:00") is "01:00"
+PASS stepDown("00:00", 1, "00:00", "03:00") is "03:00"
+PASS stepUp("15:00", 1, "10:00", "15:00") is "16:00"
+PASS stepDown("10:00", 1, "10:00", "15:00") is "09:00"
+PASS stepUp("20:00", 7200, "17:00", "20:00") is "17:00"
+PASS stepDown("20:00", 7200, "17:00", "20:00") is "19:00"
 Hours, 0-23
 PASS stepUp("07:00", 1, null, null) is "08:00"
 PASS stepDown("07:00", 1, null, null) is "06:00"
@@ -147,6 +189,26 @@ PASS getUserAgentShadowTextContent(input) is "00:00"
 PASS test("06:00", 7200, null, null, ["delete", "downArrow"]) is "22:00"
 PASS test("06:00", 7200, "01:00", null, ["delete", "upArrow"]) is "01:00"
 PASS test("06:00", 7200, "01:00", null, ["delete", "downArrow"]) is "23:00"
+PASS stepUp("17:00", 1, "17:00", "20:00") is "18:00"
+PASS stepDown("17:00", 1, "17:00", "20:00") is "20:00"
+PASS stepUp("17:00", 1, "15:00", "17:00") is "15:00"
+PASS stepDown("17:00", 1, "15:00", "17:00") is "16:00"
+PASS stepUp("15:00", 1, "17:00", "20:00") is "17:00"
+PASS stepDown("15:00", 1, "17:00", "20:00") is "20:00"
+PASS stepUp("15:00", 1, "13:00", "13:00") is "13:00"
+PASS stepDown("15:00", 1, "13:00", "13:00") is "13:00"
+PASS stepUp("00:00", 1, "00:00", "03:00") is "01:00"
+PASS stepDown("00:00", 1, "00:00", "03:00") is "03:00"
+PASS stepUp("03:00", 1, "00:00", "03:00") is "00:00"
+PASS stepDown("03:00", 1, "00:00", "03:00") is "02:00"
+PASS stepUp("12:00", 1, "10:00", "12:00") is "10:00"
+PASS stepDown("12:00", 1, "10:00", "12:00") is "11:00"
+PASS stepUp("00:00", 1, "00:00", "03:00") is "01:00"
+PASS stepDown("00:00", 1, "00:00", "03:00") is "03:00"
+PASS stepUp("15:00", 1, "10:00", "15:00") is "10:00"
+PASS stepDown("10:00", 1, "10:00", "15:00") is "15:00"
+PASS stepUp("20:00", 7200, "17:00", "20:00") is "17:00"
+PASS stepDown("20:00", 7200, "17:00", "20:00") is "19:00"
 Hours, 1-24
 PASS stepUp("11:00", 1, null, null) is "12:00"
 PASS getUserAgentShadowTextContent(input) is "12:00"
@@ -160,6 +222,28 @@ PASS test("06:00", 7200, null, null, ["delete", "upArrow"]) is "02:00"
 PASS test("06:00", 7200, null, null, ["delete", "downArrow"]) is "00:00"
 PASS test("06:00", 7200, "01:00", null, ["delete", "upArrow"]) is "01:00"
 PASS test("06:00", 7200, "01:00", null, ["delete", "downArrow"]) is "23:00"
+PASS stepUp("17:00", 1, "17:00", "20:00") is "18:00"
+PASS stepDown("17:00", 1, "17:00", "20:00") is "20:00"
+PASS stepUp("17:00", 1, "15:00", "17:00") is "15:00"
+PASS stepDown("17:00", 1, "15:00", "17:00") is "16:00"
+PASS stepUp("15:00", 1, "17:00", "20:00") is "17:00"
+PASS stepDown("15:00", 1, "17:00", "20:00") is "20:00"
+PASS stepUp("15:00", 1, "13:00", "13:00") is "13:00"
+PASS stepDown("15:00", 1, "13:00", "13:00") is "13:00"
+PASS stepUp("00:00", 1, "00:00", "03:00") is "01:00"
+PASS stepDown("00:00", 1, "00:00", "03:00") is "23:00"
+PASS stepUp("03:00", 1, "00:00", "03:00") is "04:00"
+PASS stepDown("03:00", 1, "00:00", "03:00") is "02:00"
+PASS stepUp("12:00", 1, "10:00", "12:00") is "10:00"
+PASS stepDown("12:00", 1, "10:00", "12:00") is "11:00"
+PASS stepUp("00:00", 1, "00:00", "03:00") is "01:00"
+PASS stepDown("00:00", 1, "00:00", "03:00") is "23:00"
+PASS stepUp("15:00", 1, "10:00", "15:00") is "10:00"
+PASS stepDown("10:00", 1, "10:00", "15:00") is "15:00"
+PASS stepUp("17:00", 7200, "17:00", "20:00") is "19:00"
+PASS stepDown("17:00", 7200, "17:00", "20:00") is "19:00"
+PASS stepUp("17:00", 7200, "17:00", "18:00") is "17:00"
+PASS stepDown("17:00", 7200, "17:00", "18:00") is "17:00"
 
 PASS successfullyParsed is true
 
index e03a6eb..1b369b2 100644 (file)
@@ -167,6 +167,28 @@ shouldBeEqualToString('test("06:00", 7200, null, null, ["delete", "downArrow"])'
 shouldBeEqualToString('getUserAgentShadowTextContent(input)', '12:00 AM');
 shouldBeEqualToString('test("06:00", 7200, "01:00", null, ["delete", "upArrow"])', '01:00');
 shouldBeEqualToString('test("06:00", 7200, "01:00", null, ["delete", "downArrow"])', '11:00');
+shouldBeEqualToString('stepUp("17:00", 1, "17:00", "20:00")', '18:00');
+shouldBeEqualToString('stepDown("17:00", 1, "17:00", "20:00")', '20:00');
+shouldBeEqualToString('stepUp("17:00", 1, "15:00", "17:00")', '15:00');
+shouldBeEqualToString('stepDown("17:00", 1, "15:00", "17:00")', '16:00');
+shouldBeEqualToString('stepUp("15:00", 1, "17:00", "20:00")', '17:00');
+shouldBeEqualToString('stepDown("15:00", 1, "17:00", "20:00")', '20:00');
+shouldBeEqualToString('stepUp("15:00", 1, "13:00", "13:00")', '13:00');
+shouldBeEqualToString('stepDown("15:00", 1, "13:00", "13:00")', '13:00');
+shouldBeEqualToString('stepUp("12:00", 1, "12:00", "15:00")', '13:00');
+shouldBeEqualToString('stepDown("12:00", 1, "12:00", "15:00")', '23:00');
+shouldBeEqualToString('stepUp("15:00", 1, "12:00", "15:00")', '16:00');
+shouldBeEqualToString('stepDown("15:00", 1, "12:00", "15:00")', '14:00');
+shouldBeEqualToString('stepUp("12:00", 1, "10:00", "12:00")', '13:00');
+shouldBeEqualToString('stepDown("12:00", 1, "10:00", "12:00")', '23:00');
+shouldBeEqualToString('stepUp("00:00", 1, "00:00", "03:00")', '01:00');
+shouldBeEqualToString('stepDown("00:00", 1, "00:00", "03:00")', '11:00');
+shouldBeEqualToString('stepUp("15:00", 1, "10:00", "15:00")', '16:00');
+shouldBeEqualToString('stepDown("10:00", 1, "10:00", "15:00")', '09:00');
+shouldBeEqualToString('stepUp("17:00", 7200, "17:00", "20:00")', '19:00');
+shouldBeEqualToString('stepDown("17:00", 7200, "17:00", "20:00")', '19:00');
+shouldBeEqualToString('stepUp("17:00", 7200, "17:00", "18:00")', '17:00');
+shouldBeEqualToString('stepDown("17:00", 7200, "17:00", "18:00")', '17:00');
 
 debug('Hours, 0-11');
 setDateTimeFormat('KK:mm a');
@@ -182,6 +204,26 @@ shouldBeEqualToString('test("06:00", 7200, null, null, ["delete", "upArrow"])',
 shouldBeEqualToString('test("06:00", 7200, null, null, ["delete", "downArrow"])', '10:00');
 shouldBeEqualToString('test("06:00", 7200, "01:00", null, ["delete", "upArrow"])', '01:00');
 shouldBeEqualToString('test("06:00", 7200, "01:00", null, ["delete", "downArrow"])', '11:00');
+shouldBeEqualToString('stepUp("17:00", 1, "17:00", "20:00")', '18:00');
+shouldBeEqualToString('stepDown("17:00", 1, "17:00", "20:00")', '20:00');
+shouldBeEqualToString('stepUp("17:00", 1, "15:00", "17:00")', '15:00');
+shouldBeEqualToString('stepDown("17:00", 1, "15:00", "17:00")', '16:00');
+shouldBeEqualToString('stepUp("15:00", 1, "17:00", "20:00")', '17:00');
+shouldBeEqualToString('stepDown("15:00", 1, "17:00", "20:00")', '20:00');
+shouldBeEqualToString('stepUp("15:00", 1, "13:00", "13:00")', '13:00');
+shouldBeEqualToString('stepDown("15:00", 1, "13:00", "13:00")', '13:00');
+shouldBeEqualToString('stepUp("12:00", 1, "12:00", "15:00")', '13:00');
+shouldBeEqualToString('stepDown("12:00", 1, "12:00", "15:00")', '15:00');
+shouldBeEqualToString('stepUp("15:00", 1, "12:00", "15:00")', '12:00');
+shouldBeEqualToString('stepDown("15:00", 1, "12:00", "15:00")', '14:00');
+shouldBeEqualToString('stepUp("12:00", 1, "10:00", "12:00")', '13:00');
+shouldBeEqualToString('stepDown("12:00", 1, "10:00", "12:00")', '23:00');
+shouldBeEqualToString('stepUp("00:00", 1, "00:00", "03:00")', '01:00');
+shouldBeEqualToString('stepDown("00:00", 1, "00:00", "03:00")', '03:00');
+shouldBeEqualToString('stepUp("15:00", 1, "10:00", "15:00")', '16:00');
+shouldBeEqualToString('stepDown("10:00", 1, "10:00", "15:00")', '09:00');
+shouldBeEqualToString('stepUp("20:00", 7200, "17:00", "20:00")', '17:00');
+shouldBeEqualToString('stepDown("20:00", 7200, "17:00", "20:00")', '19:00');
 
 debug('Hours, 0-23');
 setDateTimeFormat('HH:mm');
@@ -210,6 +252,26 @@ shouldBeEqualToString('getUserAgentShadowTextContent(input)', '00:00');
 shouldBeEqualToString('test("06:00", 7200, null, null, ["delete", "downArrow"])', '22:00');
 shouldBeEqualToString('test("06:00", 7200, "01:00", null, ["delete", "upArrow"])', '01:00');
 shouldBeEqualToString('test("06:00", 7200, "01:00", null, ["delete", "downArrow"])', '23:00');
+shouldBeEqualToString('stepUp("17:00", 1, "17:00", "20:00")', '18:00');
+shouldBeEqualToString('stepDown("17:00", 1, "17:00", "20:00")', '20:00');
+shouldBeEqualToString('stepUp("17:00", 1, "15:00", "17:00")', '15:00');
+shouldBeEqualToString('stepDown("17:00", 1, "15:00", "17:00")', '16:00');
+shouldBeEqualToString('stepUp("15:00", 1, "17:00", "20:00")', '17:00');
+shouldBeEqualToString('stepDown("15:00", 1, "17:00", "20:00")', '20:00');
+shouldBeEqualToString('stepUp("15:00", 1, "13:00", "13:00")', '13:00');
+shouldBeEqualToString('stepDown("15:00", 1, "13:00", "13:00")', '13:00');
+shouldBeEqualToString('stepUp("00:00", 1, "00:00", "03:00")', '01:00');
+shouldBeEqualToString('stepDown("00:00", 1, "00:00", "03:00")', '03:00');
+shouldBeEqualToString('stepUp("03:00", 1, "00:00", "03:00")', '00:00');
+shouldBeEqualToString('stepDown("03:00", 1, "00:00", "03:00")', '02:00');
+shouldBeEqualToString('stepUp("12:00", 1, "10:00", "12:00")', '10:00');
+shouldBeEqualToString('stepDown("12:00", 1, "10:00", "12:00")', '11:00');
+shouldBeEqualToString('stepUp("00:00", 1, "00:00", "03:00")', '01:00');
+shouldBeEqualToString('stepDown("00:00", 1, "00:00", "03:00")', '03:00');
+shouldBeEqualToString('stepUp("15:00", 1, "10:00", "15:00")', '10:00');
+shouldBeEqualToString('stepDown("10:00", 1, "10:00", "15:00")', '15:00');
+shouldBeEqualToString('stepUp("20:00", 7200, "17:00", "20:00")', '17:00');
+shouldBeEqualToString('stepDown("20:00", 7200, "17:00", "20:00")', '19:00');
 
 debug('Hours, 1-24');
 setDateTimeFormat('kk:mm');
@@ -225,6 +287,28 @@ shouldBeEqualToString('test("06:00", 7200, null, null, ["delete", "upArrow"])',
 shouldBeEqualToString('test("06:00", 7200, null, null, ["delete", "downArrow"])', '00:00');
 shouldBeEqualToString('test("06:00", 7200, "01:00", null, ["delete", "upArrow"])', '01:00');
 shouldBeEqualToString('test("06:00", 7200, "01:00", null, ["delete", "downArrow"])', '23:00');
+shouldBeEqualToString('stepUp("17:00", 1, "17:00", "20:00")', '18:00');
+shouldBeEqualToString('stepDown("17:00", 1, "17:00", "20:00")', '20:00');
+shouldBeEqualToString('stepUp("17:00", 1, "15:00", "17:00")', '15:00');
+shouldBeEqualToString('stepDown("17:00", 1, "15:00", "17:00")', '16:00');
+shouldBeEqualToString('stepUp("15:00", 1, "17:00", "20:00")', '17:00');
+shouldBeEqualToString('stepDown("15:00", 1, "17:00", "20:00")', '20:00');
+shouldBeEqualToString('stepUp("15:00", 1, "13:00", "13:00")', '13:00');
+shouldBeEqualToString('stepDown("15:00", 1, "13:00", "13:00")', '13:00');
+shouldBeEqualToString('stepUp("00:00", 1, "00:00", "03:00")', '01:00');
+shouldBeEqualToString('stepDown("00:00", 1, "00:00", "03:00")', '23:00');
+shouldBeEqualToString('stepUp("03:00", 1, "00:00", "03:00")', '04:00');
+shouldBeEqualToString('stepDown("03:00", 1, "00:00", "03:00")', '02:00');
+shouldBeEqualToString('stepUp("12:00", 1, "10:00", "12:00")', '10:00');
+shouldBeEqualToString('stepDown("12:00", 1, "10:00", "12:00")', '11:00');
+shouldBeEqualToString('stepUp("00:00", 1, "00:00", "03:00")', '01:00');
+shouldBeEqualToString('stepDown("00:00", 1, "00:00", "03:00")', '23:00');
+shouldBeEqualToString('stepUp("15:00", 1, "10:00", "15:00")', '10:00');
+shouldBeEqualToString('stepDown("10:00", 1, "10:00", "15:00")', '15:00');
+shouldBeEqualToString('stepUp("17:00", 7200, "17:00", "20:00")', '19:00');
+shouldBeEqualToString('stepDown("17:00", 7200, "17:00", "20:00")', '19:00');
+shouldBeEqualToString('stepUp("17:00", 7200, "17:00", "18:00")', '17:00');
+shouldBeEqualToString('stepDown("17:00", 7200, "17:00", "18:00")', '17:00');
 
 setDateTimeFormat('');
 debug('');
index 9725521..9a3c2d1 100644 (file)
@@ -1,3 +1,63 @@
+2013-02-27  Kunihiko Sakamoto  <ksakamoto@chromium.org>
+
+        INPUT_MULTIPLE_FIELDS_UI: Step-up/-down of hour field should respect min/max attributes
+        https://bugs.webkit.org/show_bug.cgi?id=109555
+
+        Reviewed by Kent Tamura.
+
+        Make step-up/-down of the hour field respect the min/max attributes of the element.
+        Note that it still accepts any keyboard inputs (the element
+        becomes 'invalid' state when out-of-range values entered).
+        Also, disable the hour field and/or the AMPM field when there is only single possible value.
+
+        Test: fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-readonly-subfield.html
+              fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-stepup-stepdown-from-renderer.html
+              fast/forms/time-multiple-fields/time-multiple-fields-readonly-subfield.html
+              fast/forms/time-multiple-fields/time-multiple-fields-stepup-stepdown-from-renderer.html
+
+        * html/TimeInputType.cpp:
+        (WebCore::TimeInputType::setupLayoutParameters): Populates layoutParameters.{minimum,maximum}.
+        * html/shadow/DateTimeEditElement.cpp:
+        (DateTimeEditBuilder): Add data fields for min/max of day and hour fields.
+        (WebCore::DateTimeEditBuilder::DateTimeEditBuilder): Set newly added data members.
+        (WebCore::DateTimeEditBuilder::visitField): Pass minimum/maximum value to the month field constructors.
+        (WebCore::DateTimeEditBuilder::shouldAMPMFieldDisabled): Added.
+        (WebCore::DateTimeEditBuilder::shouldDayOfMonthFieldDisabled):
+        (WebCore::DateTimeEditBuilder::shouldHourFieldDisabled):
+        Disables the hour field when min, max, and value have the same hour, except when the minute
+        field is disabled (by step attribute), because we need to leave at least one field editable.
+        * html/shadow/DateTimeFieldElements.cpp:
+        (WebCore::DateTimeHourFieldElementBase::DateTimeHourFieldElementBase):
+        (WebCore::DateTimeHourFieldElementBase::initialize):
+        (WebCore::DateTimeHourFieldElementBase::setValueAsDate):
+        (WebCore::DateTimeHourFieldElementBase::setValueAsDateTimeFieldsState):
+        (WebCore::DateTimeHour11FieldElement::DateTimeHour11FieldElement):
+        (WebCore::DateTimeHour11FieldElement::create):
+        (WebCore::DateTimeHour11FieldElement::populateDateTimeFieldsState):
+        (WebCore::DateTimeHour11FieldElement::setValueAsInteger):
+        (WebCore::DateTimeHour11FieldElement::clampValueForHardLimits):
+        (WebCore::DateTimeHour12FieldElement::DateTimeHour12FieldElement):
+        (WebCore::DateTimeHour12FieldElement::create):
+        (WebCore::DateTimeHour12FieldElement::populateDateTimeFieldsState):
+        (WebCore::DateTimeHour12FieldElement::setValueAsInteger):
+        (WebCore::DateTimeHour12FieldElement::clampValueForHardLimits):
+        (WebCore::DateTimeHour23FieldElement::DateTimeHour23FieldElement):
+        (WebCore::DateTimeHour23FieldElement::create):
+        (WebCore::DateTimeHour23FieldElement::populateDateTimeFieldsState):
+        (WebCore::DateTimeHour23FieldElement::setValueAsInteger):
+        (WebCore::DateTimeHour23FieldElement::clampValueForHardLimits):
+        (WebCore::DateTimeHour24FieldElement::DateTimeHour24FieldElement):
+        (WebCore::DateTimeHour24FieldElement::create):
+        (WebCore::DateTimeHour24FieldElement::populateDateTimeFieldsState):
+        (WebCore::DateTimeHour24FieldElement::setValueAsInteger):
+        (WebCore::DateTimeHour24FieldElement::clampValueForHardLimits):
+        * html/shadow/DateTimeFieldElements.h: Splitted DateTimeHourFieldElement into a base class and four derived classes that represents different hour formats.
+        (DateTimeHourFieldElementBase): Added.
+        (DateTimeHour11FieldElement): Added. Represents 0-11 hour format.
+        (DateTimeHour12FieldElement): Added. Represents 1-12 hour format.
+        (DateTimeHour23FieldElement): Added. Represents 0-23 hour format.
+        (DateTimeHour24FieldElement): Added. Represents 1-24 hour format.
+
 2013-02-27  Tien-Ren Chen  <trchen@chromium.org>
 
         Need to re-layout fixed position elements after scale when using settings()->fixedElementsLayoutRelativeToFrame()
index 9f080d2..d5b4a7a 100644 (file)
@@ -165,6 +165,10 @@ void TimeInputType::setupLayoutParameters(DateTimeEditElement::LayoutParameters&
         layoutParameters.dateTimeFormat = layoutParameters.locale.shortTimeFormat();
         layoutParameters.fallbackDateTimeFormat = "HH:mm";
     }
+    if (!parseToDateComponents(element()->fastGetAttribute(minAttr), &layoutParameters.minimum))
+        layoutParameters.minimum = DateComponents();
+    if (!parseToDateComponents(element()->fastGetAttribute(maxAttr), &layoutParameters.maximum))
+        layoutParameters.maximum = DateComponents();
 }
 #endif
 
index 4921eb0..91dd1b3 100644 (file)
@@ -59,9 +59,9 @@ public:
     bool build(const String&);
 
 private:
-    void getRangeOfDayOfMonthField(int& minDay, int& maxDay) const;
     bool needMillisecondField() const;
-    bool shouldDayOfMonthFieldDisabled(int minDay, int maxDay) const;
+    bool shouldAMPMFieldDisabled() const;
+    bool shouldDayOfMonthFieldDisabled() const;
     bool shouldHourFieldDisabled() const;
     bool shouldMillisecondFieldDisabled() const;
     bool shouldMinuteFieldDisabled() const;
@@ -77,13 +77,42 @@ private:
     DateTimeEditElement& m_editElement;
     const DateComponents m_dateValue;
     const DateTimeEditElement::LayoutParameters& m_parameters;
+    int m_minDay;
+    int m_maxDay;
+    int m_minHour23;
+    int m_maxHour23;
 };
 
 DateTimeEditBuilder::DateTimeEditBuilder(DateTimeEditElement& elemnt, const DateTimeEditElement::LayoutParameters& layoutParameters, const DateComponents& dateValue)
     : m_editElement(elemnt)
     , m_dateValue(dateValue)
     , m_parameters(layoutParameters)
-{
+    , m_minDay(1)
+    , m_maxDay(31)
+    , m_minHour23(0)
+    , m_maxHour23(23)
+{
+    if (m_dateValue.type() == DateComponents::Date
+        || m_dateValue.type() == DateComponents::DateTimeLocal
+        || m_dateValue.type() == DateComponents::DateTime) {
+        if (m_parameters.minimum.type() != DateComponents::Invalid
+            && m_parameters.maximum.type() != DateComponents::Invalid
+            && m_parameters.minimum.fullYear() == m_parameters.maximum.fullYear()
+            && m_parameters.minimum.month() == m_parameters.maximum.month()
+            && m_parameters.minimum.monthDay() <= m_parameters.maximum.monthDay()) {
+            m_minDay = m_parameters.minimum.monthDay();
+            m_maxDay = m_parameters.maximum.monthDay();
+        }
+    }
+
+    if (m_dateValue.type() == DateComponents::Time || m_minDay == m_maxDay) {
+        if (m_parameters.minimum.type() != DateComponents::Invalid
+            && m_parameters.maximum.type() != DateComponents::Invalid
+            && m_parameters.minimum.hour() <= m_parameters.maximum.hour()) {
+            m_minHour23 = m_parameters.minimum.hour();
+            m_maxHour23 = m_parameters.maximum.hour();
+        }
+    }
 }
 
 bool DateTimeEditBuilder::build(const String& formatString)
@@ -92,20 +121,6 @@ bool DateTimeEditBuilder::build(const String& formatString)
     return DateTimeFormat::parse(formatString, *this);
 }
 
-void DateTimeEditBuilder::getRangeOfDayOfMonthField(int& minDay, int& maxDay) const
-{
-    minDay = 1;
-    maxDay = 31;
-    if (m_parameters.minimum.type() != DateComponents::Invalid
-        && m_parameters.maximum.type() != DateComponents::Invalid
-        && m_parameters.minimum.fullYear() == m_parameters.maximum.fullYear()
-        && m_parameters.minimum.month() == m_parameters.maximum.month()
-        && m_parameters.minimum.monthDay() <= m_parameters.maximum.monthDay()) {
-        minDay = m_parameters.minimum.monthDay();
-        maxDay = m_parameters.maximum.monthDay();
-    }
-}
-
 bool DateTimeEditBuilder::needMillisecondField() const
 {
     return m_dateValue.millisecond()
@@ -122,11 +137,9 @@ void DateTimeEditBuilder::visitField(DateTimeFormat::FieldType fieldType, int co
 
     switch (fieldType) {
     case DateTimeFormat::FieldTypeDayOfMonth: {
-        int minDay, maxDay;
-        getRangeOfDayOfMonthField(minDay, maxDay);
-        RefPtr<DateTimeFieldElement> field = DateTimeDayFieldElement::create(document, m_editElement, m_parameters.placeholderForDay, minDay, maxDay);
+        RefPtr<DateTimeFieldElement> field = DateTimeDayFieldElement::create(document, m_editElement, m_parameters.placeholderForDay, m_minDay, m_maxDay);
         m_editElement.addField(field);
-        if (shouldDayOfMonthFieldDisabled(minDay, maxDay)) {
+        if (shouldDayOfMonthFieldDisabled()) {
             field->setValueAsDate(m_dateValue);
             field->setDisabled();
         }
@@ -135,7 +148,7 @@ void DateTimeEditBuilder::visitField(DateTimeFormat::FieldType fieldType, int co
 
     case DateTimeFormat::FieldTypeHour11: {
         DateTimeNumericFieldElement::Parameters parameters = createNumericFieldParameters(static_cast<int>(msPerHour), static_cast<int>(msPerHour * 12));
-        RefPtr<DateTimeFieldElement> field = DateTimeHourFieldElement::create(document, m_editElement, 0, 11, parameters);
+        RefPtr<DateTimeFieldElement> field = DateTimeHour11FieldElement::create(document, m_editElement, m_minHour23, m_maxHour23, parameters);
         m_editElement.addField(field);
         if (shouldHourFieldDisabled()) {
             field->setValueAsDate(m_dateValue);
@@ -146,7 +159,7 @@ void DateTimeEditBuilder::visitField(DateTimeFormat::FieldType fieldType, int co
 
     case DateTimeFormat::FieldTypeHour12: {
         DateTimeNumericFieldElement::Parameters parameters = createNumericFieldParameters(static_cast<int>(msPerHour), static_cast<int>(msPerHour * 12));
-        RefPtr<DateTimeFieldElement> field = DateTimeHourFieldElement::create(document, m_editElement, 1, 12, parameters);
+        RefPtr<DateTimeFieldElement> field = DateTimeHour12FieldElement::create(document, m_editElement, m_minHour23, m_maxHour23, parameters);
         m_editElement.addField(field);
         if (shouldHourFieldDisabled()) {
             field->setValueAsDate(m_dateValue);
@@ -157,7 +170,7 @@ void DateTimeEditBuilder::visitField(DateTimeFormat::FieldType fieldType, int co
 
     case DateTimeFormat::FieldTypeHour23: {
         DateTimeNumericFieldElement::Parameters parameters = createNumericFieldParameters(static_cast<int>(msPerHour), static_cast<int>(msPerDay));
-        RefPtr<DateTimeFieldElement> field = DateTimeHourFieldElement::create(document, m_editElement, 0, 23, parameters);
+        RefPtr<DateTimeFieldElement> field = DateTimeHour23FieldElement::create(document, m_editElement, m_minHour23, m_maxHour23, parameters);
         m_editElement.addField(field);
         if (shouldHourFieldDisabled()) {
             field->setValueAsDate(m_dateValue);
@@ -168,7 +181,7 @@ void DateTimeEditBuilder::visitField(DateTimeFormat::FieldType fieldType, int co
 
     case DateTimeFormat::FieldTypeHour24: {
         DateTimeNumericFieldElement::Parameters parameters = createNumericFieldParameters(static_cast<int>(msPerHour), static_cast<int>(msPerDay));
-        RefPtr<DateTimeFieldElement> field = DateTimeHourFieldElement::create(document, m_editElement, 1, 24, parameters);
+        RefPtr<DateTimeFieldElement> field = DateTimeHour24FieldElement::create(document, m_editElement, m_minHour23, m_maxHour23, parameters);
         m_editElement.addField(field);
         if (shouldHourFieldDisabled()) {
             field->setValueAsDate(m_dateValue);
@@ -222,7 +235,7 @@ void DateTimeEditBuilder::visitField(DateTimeFormat::FieldType fieldType, int co
     case DateTimeFormat::FieldTypePeriod: {
         RefPtr<DateTimeFieldElement> field = DateTimeAMPMFieldElement::create(document, m_editElement, m_parameters.locale.timeAMPMLabels());
         m_editElement.addField(field);
-        if (shouldHourFieldDisabled()) {
+        if (shouldAMPMFieldDisabled()) {
             field->setValueAsDate(m_dateValue);
             field->setDisabled();
         }
@@ -305,19 +318,28 @@ void DateTimeEditBuilder::visitField(DateTimeFormat::FieldType fieldType, int co
     }
 }
 
-bool DateTimeEditBuilder::shouldDayOfMonthFieldDisabled(int minDay, int maxDay) const
+bool DateTimeEditBuilder::shouldAMPMFieldDisabled() const
 {
-    return minDay == maxDay && minDay == m_dateValue.monthDay() && m_dateValue.type() != DateComponents::Date;
+    return shouldHourFieldDisabled()
+        || (m_minHour23 < 12 && m_maxHour23 < 12 && m_dateValue.hour() < 12)
+        || (m_minHour23 >= 12 && m_maxHour23 >= 12 && m_dateValue.hour() >= 12);
+}
+
+bool DateTimeEditBuilder::shouldDayOfMonthFieldDisabled() const
+{
+    return m_minDay == m_maxDay && m_minDay == m_dateValue.monthDay() && m_dateValue.type() != DateComponents::Date;
 }
 
 bool DateTimeEditBuilder::shouldHourFieldDisabled() const
 {
+    if (m_minHour23 == m_maxHour23 && m_minHour23 == m_dateValue.hour() && !shouldMinuteFieldDisabled())
+        return true;
+
     if (m_dateValue.type() == DateComponents::Time)
         return false;
     ASSERT(m_dateValue.type() == DateComponents::DateTimeLocal || m_dateValue.type() == DateComponents::DateTime);
-    int minDay, maxDay;
-    getRangeOfDayOfMonthField(minDay, maxDay);
-    if (shouldDayOfMonthFieldDisabled(minDay, maxDay)) {
+
+    if (shouldDayOfMonthFieldDisabled()) {
         ASSERT(m_parameters.minimum.fullYear() == m_parameters.maximum.fullYear());
         ASSERT(m_parameters.minimum.month() == m_parameters.maximum.month());
         return false;
index ebfee33..d7b0c4d 100644 (file)
@@ -117,102 +117,228 @@ int DateTimeDayFieldElement::clampValueForHardLimits(int value) const
 
 // ----------------------------
 
-DateTimeHourFieldElement::DateTimeHourFieldElement(Document* document, FieldOwner& fieldOwner, int minimum, int maximum, const DateTimeNumericFieldElement::Parameters& parameters)
+DateTimeHourFieldElementBase::DateTimeHourFieldElementBase(Document* document, FieldOwner& fieldOwner, int minimum, int maximum, const DateTimeNumericFieldElement::Parameters& parameters)
     : DateTimeNumericFieldElement(document, fieldOwner, minimum, maximum, "--", parameters)
-    , m_alignment(maximum + maximum % 2)
 {
-    ASSERT((!minimum && (maximum == 11 || maximum == 23)) || (minimum == 1 && (maximum == 12 || maximum == 24)));
 }
 
-PassRefPtr<DateTimeHourFieldElement> DateTimeHourFieldElement::create(Document* document, FieldOwner& fieldOwner, int minimum, int maximum, const DateTimeNumericFieldElement::Parameters& parameters)
+void DateTimeHourFieldElementBase::initialize()
 {
     DEFINE_STATIC_LOCAL(AtomicString, hourPsuedoId, ("-webkit-datetime-edit-hour-field", AtomicString::ConstructFromLiteral));
-    RefPtr<DateTimeHourFieldElement> field = adoptRef(new DateTimeHourFieldElement(document, fieldOwner, minimum, maximum, parameters));
-    field->initialize(hourPsuedoId, AXHourFieldText());
+    DateTimeNumericFieldElement::initialize(hourPsuedoId, AXHourFieldText());
+}
+
+void DateTimeHourFieldElementBase::setValueAsDate(const DateComponents& date)
+{
+    setValueAsInteger(date.hour());
+}
+
+void DateTimeHourFieldElementBase::setValueAsDateTimeFieldsState(const DateTimeFieldsState& dateTimeFieldsState)
+{
+    if (!dateTimeFieldsState.hasHour()) {
+        setEmptyValue();
+        return;
+    }
+
+    const int hour12 = dateTimeFieldsState.hour();
+    if (hour12 < 1 || hour12 > 12) {
+        setEmptyValue();
+        return;
+    }
+
+    if (dateTimeFieldsState.ampm() == DateTimeFieldsState::AMPMValuePM)
+        setValueAsInteger((hour12 + 12) % 24);
+    else
+        setValueAsInteger(hour12 % 12);
+}
+// ----------------------------
+
+DateTimeHour11FieldElement::DateTimeHour11FieldElement(Document* document, FieldOwner& fieldOwner, int minimum, int maximum, const DateTimeNumericFieldElement::Parameters& parameters)
+    : DateTimeHourFieldElementBase(document, fieldOwner, minimum, maximum, parameters)
+{
+}
+
+PassRefPtr<DateTimeHour11FieldElement> DateTimeHour11FieldElement::create(Document* document, FieldOwner& fieldOwner, int minimumHour23, int maximumHour23, const DateTimeNumericFieldElement::Parameters& parameters)
+{
+    ASSERT(minimumHour23 >= 0);
+    ASSERT(maximumHour23 <= 23);
+    ASSERT(minimumHour23 <= maximumHour23);
+    int minimum = 0, maximum = 11;
+    if (maximumHour23 < 12) {
+        minimum = minimumHour23;
+        maximum = maximumHour23;
+    } else if (minimumHour23 >= 12) {
+        minimum = minimumHour23 - 12;
+        maximum = maximumHour23 - 12;
+    }
+
+    RefPtr<DateTimeHour11FieldElement> field = adoptRef(new DateTimeHour11FieldElement(document, fieldOwner, minimum, maximum, parameters));
+    field->initialize();
     return field.release();
 }
 
-void DateTimeHourFieldElement::populateDateTimeFieldsState(DateTimeFieldsState& dateTimeFieldsState)
+void DateTimeHour11FieldElement::populateDateTimeFieldsState(DateTimeFieldsState& dateTimeFieldsState)
 {
     if (!hasValue()) {
         dateTimeFieldsState.setHour(DateTimeFieldsState::emptyValue);
         return;
     }
-
     const int value = valueAsInteger();
+    dateTimeFieldsState.setHour(value ? value : 12);
+}
 
-    switch (maximum()) {
-    case 11:
-        dateTimeFieldsState.setHour(value ? value : 12);
-        return;
-    case 12:
-        dateTimeFieldsState.setHour(value);
-        return;
-    case 23:
-        dateTimeFieldsState.setHour(value ? value % 12 : 12);
-        dateTimeFieldsState.setAMPM(value >= 12 ? DateTimeFieldsState::AMPMValuePM : DateTimeFieldsState::AMPMValueAM);
-        return;
-    case 24:
-        if (value == 24) {
-            dateTimeFieldsState.setHour(12);
-            dateTimeFieldsState.setAMPM(DateTimeFieldsState::AMPMValueAM);
-            return;
-        }
-        dateTimeFieldsState.setHour(value == 12 ? 12 : value % 12);
-        dateTimeFieldsState.setAMPM(value >= 12 ? DateTimeFieldsState::AMPMValuePM : DateTimeFieldsState::AMPMValueAM);
-        return;
-    default:
-        ASSERT_NOT_REACHED();
+void DateTimeHour11FieldElement::setValueAsInteger(int value, EventBehavior eventBehavior)
+{
+    value = Range(0, 23).clampValue(value) % 12;
+    DateTimeNumericFieldElement::setValueAsInteger(value, eventBehavior);
+}
+
+int DateTimeHour11FieldElement::clampValueForHardLimits(int value) const
+{
+    return Range(0, 11).clampValue(value);
+}
+
+// ----------------------------
+
+DateTimeHour12FieldElement::DateTimeHour12FieldElement(Document* document, FieldOwner& fieldOwner, int minimum, int maximum, const DateTimeNumericFieldElement::Parameters& parameters)
+    : DateTimeHourFieldElementBase(document, fieldOwner, minimum, maximum, parameters)
+{
+}
+
+PassRefPtr<DateTimeHour12FieldElement> DateTimeHour12FieldElement::create(Document* document, FieldOwner& fieldOwner, int minimumHour23, int maximumHour23, const DateTimeNumericFieldElement::Parameters& parameters)
+{
+    ASSERT(minimumHour23 >= 0);
+    ASSERT(maximumHour23 <= 23);
+    ASSERT(minimumHour23 <= maximumHour23);
+    int minimum = 1, maximum = 12;
+    if (maximumHour23 < 12) {
+        minimum = minimumHour23;
+        maximum = maximumHour23;
+    } else if (minimumHour23 >= 12) {
+        minimum = minimumHour23 - 12;
+        maximum = maximumHour23 - 12;
     }
+    if (!minimum)
+        minimum = 12;
+    if (!maximum)
+        maximum = 12;
+    if (minimum > maximum) {
+        minimum = 1;
+        maximum = 12;
+    }
+    RefPtr<DateTimeHour12FieldElement> field = adoptRef(new DateTimeHour12FieldElement(document, fieldOwner, minimum, maximum, parameters));
+    field->initialize();
+    return field.release();
 }
 
-void DateTimeHourFieldElement::setValueAsDate(const DateComponents& date)
+void DateTimeHour12FieldElement::populateDateTimeFieldsState(DateTimeFieldsState& dateTimeFieldsState)
 {
-    setValueAsInteger(date.hour());
+    dateTimeFieldsState.setHour(hasValue() ? valueAsInteger() : DateTimeFieldsState::emptyValue);
 }
 
-void DateTimeHourFieldElement::setValueAsDateTimeFieldsState(const DateTimeFieldsState& dateTimeFieldsState)
+void DateTimeHour12FieldElement::setValueAsInteger(int value, EventBehavior eventBehavior)
 {
-    if (!dateTimeFieldsState.hasHour()) {
-        setEmptyValue();
+    value = Range(0, 24).clampValue(value) % 12;
+    DateTimeNumericFieldElement::setValueAsInteger(value ? value : 12, eventBehavior);
+}
+
+int DateTimeHour12FieldElement::clampValueForHardLimits(int value) const
+{
+    return Range(1, 12).clampValue(value);
+}
+
+// ----------------------------
+
+DateTimeHour23FieldElement::DateTimeHour23FieldElement(Document* document, FieldOwner& fieldOwner, int minimum, int maximum, const DateTimeNumericFieldElement::Parameters& parameters)
+    : DateTimeHourFieldElementBase(document, fieldOwner, minimum, maximum, parameters)
+{
+}
+
+PassRefPtr<DateTimeHour23FieldElement> DateTimeHour23FieldElement::create(Document* document, FieldOwner& fieldOwner, int minimumHour23, int maximumHour23, const DateTimeNumericFieldElement::Parameters& parameters)
+{
+    ASSERT(minimumHour23 >= 0);
+    ASSERT(maximumHour23 <= 23);
+    ASSERT(minimumHour23 <= maximumHour23);
+    RefPtr<DateTimeHour23FieldElement> field = adoptRef(new DateTimeHour23FieldElement(document, fieldOwner, minimumHour23, maximumHour23, parameters));
+    field->initialize();
+    return field.release();
+}
+
+void DateTimeHour23FieldElement::populateDateTimeFieldsState(DateTimeFieldsState& dateTimeFieldsState)
+{
+    if (!hasValue()) {
+        dateTimeFieldsState.setHour(DateTimeFieldsState::emptyValue);
         return;
     }
 
-    const int hour12 = dateTimeFieldsState.hour();
+    const int value = valueAsInteger();
 
-    if (hour12 < 1 || hour12 > 12) {
-        setEmptyValue();
-        return;
+    dateTimeFieldsState.setHour(value ? value % 12 : 12);
+    dateTimeFieldsState.setAMPM(value >= 12 ? DateTimeFieldsState::AMPMValuePM : DateTimeFieldsState::AMPMValueAM);
+}
+
+void DateTimeHour23FieldElement::setValueAsInteger(int value, EventBehavior eventBehavior)
+{
+    value = Range(0, 23).clampValue(value);
+    DateTimeNumericFieldElement::setValueAsInteger(value, eventBehavior);
+}
+
+int DateTimeHour23FieldElement::clampValueForHardLimits(int value) const
+{
+    return Range(0, 23).clampValue(value);
+}
+
+// ----------------------------
+
+DateTimeHour24FieldElement::DateTimeHour24FieldElement(Document* document, FieldOwner& fieldOwner, int minimum, int maximum, const DateTimeNumericFieldElement::Parameters& parameters)
+    : DateTimeHourFieldElementBase(document, fieldOwner, minimum, maximum, parameters)
+{
+}
+
+PassRefPtr<DateTimeHour24FieldElement> DateTimeHour24FieldElement::create(Document* document, FieldOwner& fieldOwner, int minimumHour23, int maximumHour23, const DateTimeNumericFieldElement::Parameters& parameters)
+{
+    ASSERT(minimumHour23 >= 0);
+    ASSERT(maximumHour23 <= 23);
+    ASSERT(minimumHour23 <= maximumHour23);
+    int minimum = minimumHour23 ? minimumHour23 : 24;
+    int maximum = maximumHour23 ? maximumHour23 : 24;
+    if (minimum > maximum) {
+        minimum = 1;
+        maximum = 24;
     }
 
-    switch (maximum()) {
-    case 11:
-        DateTimeNumericFieldElement::setValueAsInteger(hour12 % 12);
-        return;
-    case 12:
-        DateTimeNumericFieldElement::setValueAsInteger(hour12);
-        return;
-    case 23:
-        if (dateTimeFieldsState.ampm() == DateTimeFieldsState::AMPMValuePM)
-            DateTimeNumericFieldElement::setValueAsInteger((hour12 + 12) % 24);
-        else
-            DateTimeNumericFieldElement::setValueAsInteger(hour12 % 12);
-        return;
-    case 24:
-        if (dateTimeFieldsState.ampm() == DateTimeFieldsState::AMPMValuePM)
-            DateTimeNumericFieldElement::setValueAsInteger(hour12 == 12 ? 12 : hour12 + 12);
-        else
-            DateTimeNumericFieldElement::setValueAsInteger(hour12);
+    RefPtr<DateTimeHour24FieldElement> field = adoptRef(new DateTimeHour24FieldElement(document, fieldOwner, minimum, maximum, parameters));
+    field->initialize();
+    return field.release();
+}
+
+void DateTimeHour24FieldElement::populateDateTimeFieldsState(DateTimeFieldsState& dateTimeFieldsState)
+{
+    if (!hasValue()) {
+        dateTimeFieldsState.setHour(DateTimeFieldsState::emptyValue);
         return;
-    default:
-        ASSERT_NOT_REACHED();
     }
+
+    const int value = valueAsInteger();
+
+    if (value == 24) {
+        dateTimeFieldsState.setHour(12);
+        dateTimeFieldsState.setAMPM(DateTimeFieldsState::AMPMValueAM);
+    } else {
+        dateTimeFieldsState.setHour(value == 12 ? 12 : value % 12);
+        dateTimeFieldsState.setAMPM(value >= 12 ? DateTimeFieldsState::AMPMValuePM : DateTimeFieldsState::AMPMValueAM);
+    }
+}
+
+void DateTimeHour24FieldElement::setValueAsInteger(int value, EventBehavior eventBehavior)
+{
+    value = Range(0, 24).clampValue(value);
+    DateTimeNumericFieldElement::setValueAsInteger(value ? value : 24, eventBehavior);
 }
 
-void DateTimeHourFieldElement::setValueAsInteger(int value, EventBehavior eventBehavior)
+int DateTimeHour24FieldElement::clampValueForHardLimits(int value) const
 {
-    value = Range(0, 24 - range().maximum % 2).clampValue(value) % m_alignment;
-    DateTimeNumericFieldElement::setValueAsInteger(range().minimum && !value ? m_alignment : value, eventBehavior);
+    return Range(1, 24).clampValue(value);
 }
 
 // ----------------------------
index 69281ef..0a59ac2 100644 (file)
@@ -65,28 +65,85 @@ private:
     virtual int clampValueForHardLimits(int) const OVERRIDE FINAL;
 };
 
-// DateTimeHourFieldElement is used for hour field of date time format
-// supporting following patterns:
-//  - 0 to 11
-//  - 1 to 12
-//  - 0 to 23
-//  - 1 to 24
-class DateTimeHourFieldElement : public DateTimeNumericFieldElement {
-    WTF_MAKE_NONCOPYABLE(DateTimeHourFieldElement);
+class DateTimeHourFieldElementBase : public DateTimeNumericFieldElement {
+    WTF_MAKE_NONCOPYABLE(DateTimeHourFieldElementBase);
+
+protected:
+    DateTimeHourFieldElementBase(Document*, FieldOwner&, int minimum, int maximum, const DateTimeNumericFieldElement::Parameters&);
+    void initialize();
+
+private:
+    // DateTimeFieldElement functions.
+    virtual void setValueAsDate(const DateComponents&) OVERRIDE FINAL;
+    virtual void setValueAsDateTimeFieldsState(const DateTimeFieldsState&) OVERRIDE FINAL;
+};
+
+class DateTimeHour11FieldElement : public DateTimeHourFieldElementBase {
+    WTF_MAKE_NONCOPYABLE(DateTimeHour11FieldElement);
 
 public:
-    static PassRefPtr<DateTimeHourFieldElement> create(Document*, FieldOwner&, int minimum, int maximum, const DateTimeNumericFieldElement::Parameters&);
+    static PassRefPtr<DateTimeHour11FieldElement> create(Document*, FieldOwner&, int minimumHour23, int maximumHour23, const DateTimeNumericFieldElement::Parameters&);
 
 private:
-    DateTimeHourFieldElement(Document*, FieldOwner&, int minimum, int maximum, const DateTimeNumericFieldElement::Parameters&);
+    DateTimeHour11FieldElement(Document*, FieldOwner&, int minimum, int maximum, const DateTimeNumericFieldElement::Parameters&);
+
+    // DateTimeFieldElement functions.
+    virtual void populateDateTimeFieldsState(DateTimeFieldsState&) OVERRIDE FINAL;
+    virtual void setValueAsInteger(int, EventBehavior = DispatchNoEvent) OVERRIDE FINAL;
+
+    // DateTimeNumericFieldElement function.
+    virtual int clampValueForHardLimits(int) const OVERRIDE FINAL;
+};
+
+class DateTimeHour12FieldElement : public DateTimeHourFieldElementBase {
+    WTF_MAKE_NONCOPYABLE(DateTimeHour12FieldElement);
+
+public:
+    static PassRefPtr<DateTimeHour12FieldElement> create(Document*, FieldOwner&, int minimumHour23, int maximumHour23, const DateTimeNumericFieldElement::Parameters&);
+
+private:
+    DateTimeHour12FieldElement(Document*, FieldOwner&, int minimum, int maximum, const DateTimeNumericFieldElement::Parameters&);
 
     // DateTimeFieldElement functions.
     virtual void populateDateTimeFieldsState(DateTimeFieldsState&) OVERRIDE FINAL;
-    virtual void setValueAsDate(const DateComponents&) OVERRIDE FINAL;
-    virtual void setValueAsDateTimeFieldsState(const DateTimeFieldsState&) OVERRIDE FINAL;
     virtual void setValueAsInteger(int, EventBehavior = DispatchNoEvent) OVERRIDE FINAL;
 
-    const int m_alignment;
+    // DateTimeNumericFieldElement function.
+    virtual int clampValueForHardLimits(int) const OVERRIDE FINAL;
+};
+
+class DateTimeHour23FieldElement : public DateTimeHourFieldElementBase {
+    WTF_MAKE_NONCOPYABLE(DateTimeHour23FieldElement);
+
+public:
+    static PassRefPtr<DateTimeHour23FieldElement> create(Document*, FieldOwner&, int minimumHour23, int maximumHour23, const DateTimeNumericFieldElement::Parameters&);
+
+private:
+    DateTimeHour23FieldElement(Document*, FieldOwner&, int minimum, int maximum, const DateTimeNumericFieldElement::Parameters&);
+
+    // DateTimeFieldElement functions.
+    virtual void populateDateTimeFieldsState(DateTimeFieldsState&) OVERRIDE FINAL;
+    virtual void setValueAsInteger(int, EventBehavior = DispatchNoEvent) OVERRIDE FINAL;
+
+    // DateTimeNumericFieldElement function.
+    virtual int clampValueForHardLimits(int) const OVERRIDE FINAL;
+};
+
+class DateTimeHour24FieldElement : public DateTimeHourFieldElementBase {
+    WTF_MAKE_NONCOPYABLE(DateTimeHour24FieldElement);
+
+public:
+    static PassRefPtr<DateTimeHour24FieldElement> create(Document*, FieldOwner&, int minimumHour23, int maximumHour23, const DateTimeNumericFieldElement::Parameters&);
+
+private:
+    DateTimeHour24FieldElement(Document*, FieldOwner&, int minimum, int maximum, const DateTimeNumericFieldElement::Parameters&);
+
+    // DateTimeFieldElement functions.
+    virtual void populateDateTimeFieldsState(DateTimeFieldsState&) OVERRIDE FINAL;
+    virtual void setValueAsInteger(int, EventBehavior = DispatchNoEvent) OVERRIDE FINAL;
+
+    // DateTimeNumericFieldElement function.
+    virtual int clampValueForHardLimits(int) const OVERRIDE FINAL;
 };
 
 class DateTimeMillisecondFieldElement : public DateTimeNumericFieldElement {