DateTimeYearFieldElement should respect min/max values specified by page authors
authortkent@chromium.org <tkent@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 4 Oct 2012 04:32:42 +0000 (04:32 +0000)
committertkent@chromium.org <tkent@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 4 Oct 2012 04:32:42 +0000 (04:32 +0000)
https://bugs.webkit.org/show_bug.cgi?id=98227

Reviewed by Hajime Morita.

Source/WebCore:

Before this patch, we always set 1 to the minimum limit and 275760 to
the maximum limit for a year field, and a user can specify any year
regardless of min/max attributes. Such wide range is unnecessary for
normal applications and we should provide a way to limit the range.

Test: fast/forms/month-multiple-fields/month-multiple-fields-keyboard-events.html

* html/shadow/DateTimeFieldElements.h:
To add four constructor arguments, introduce Parameters struct.
Actually, we add the followings;
 - minimum year in UI
 - maximum year in UI
 - min attribute is specified
 - max attribute is specified
(Parameters):
(WebCore::DateTimeYearFieldElement::Parameters::Parameters):
(DateTimeYearFieldElement):
* html/shadow/DateTimeFieldElements.cpp:
(WebCore::DateTimeYearFieldElement::DateTimeYearFieldElement): ditto.
(WebCore::DateTimeYearFieldElement::create): ditto.
(WebCore::DateTimeYearFieldElement::clampValueForHardLimits):
Override DateTimeNumericFieldElement::clampValueForHardLimits.
By this, we allow to set out-of-range year values.
(WebCore::currentFullYear): A helper to get the current year.
(WebCore::DateTimeYearFieldElement::defaultValueForStepDown):
If the field has no value and step down operation occurs,
 - the field has the current year if the max attribute is not specified.
 - the field has the maximum value otherwise.
(WebCore::DateTimeYearFieldElement::defaultValueForStepUp): Similar change.

* html/shadow/DateTimeNumericFieldElement.h:
(DateTimeNumericFieldElement): Declare clampValueForHardLimits.
* html/shadow/DateTimeNumericFieldElement.cpp:
(WebCore::DateTimeNumericFieldElement::clampValueForHardLimits):
(WebCore::DateTimeNumericFieldElement::setValueAsInteger):
Call clampValueForHardLimits instead of clampValue in order to
distinguish limits for UI and limits for internal value update.

* html/shadow/DateTimeEditElement.h:
(LayoutParameters): Add minimumYear and maximumYear members.
(WebCore::DateTimeEditElement::LayoutParameters::LayoutParameters):
Initialize minimumYear and maximumYear.
(WebCore::DateTimeEditElement::LayoutParameters::undefinedYear):
Represents 'undefined' value for minimumYear and maximumYear.
* html/shadow/DateTimeEditElement.cpp:
(WebCore::DateTimeEditBuilder::visitField):
Preparas DateTimeYearField::Parameters and pass it to the DateTimeYearField factory.

* html/BaseMultipleFieldsDateAndTimeInputType.cpp:
(WebCore::BaseMultipleFieldsDateAndTimeInputType::fullYear):
A helper to get a year value from an attribute value string.
* html/BaseMultipleFieldsDateAndTimeInputType.h:
(BaseMultipleFieldsDateAndTimeInputType): Add fullYear().

* html/DateTimeInputType.cpp:
(WebCore::DateTimeInputType::setupLayoutParameters):
Set LayoutParameters::minimumYear and maximumYear.
* html/DateTimeLocalInputType.cpp:
(WebCore::DateTimeLocalInputType::setupLayoutParameters): ditto.
* html/MonthInputType.cpp:
(WebCore::MonthInputType::setupLayoutParameters): ditto.
* html/WeekInputType.cpp:
(WebCore::WeekInputType::setupLayoutParameters): ditto.

LayoutTests:

* fast/forms/month-multiple-fields/month-multiple-fields-keyboard-events-expected.txt: Added.
* fast/forms/month-multiple-fields/month-multiple-fields-keyboard-events.html: Added.

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

16 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/forms/month-multiple-fields/month-multiple-fields-keyboard-events-expected.txt [new file with mode: 0644]
LayoutTests/fast/forms/month-multiple-fields/month-multiple-fields-keyboard-events.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/html/BaseMultipleFieldsDateAndTimeInputType.cpp
Source/WebCore/html/BaseMultipleFieldsDateAndTimeInputType.h
Source/WebCore/html/DateTimeInputType.cpp
Source/WebCore/html/DateTimeLocalInputType.cpp
Source/WebCore/html/MonthInputType.cpp
Source/WebCore/html/WeekInputType.cpp
Source/WebCore/html/shadow/DateTimeEditElement.cpp
Source/WebCore/html/shadow/DateTimeEditElement.h
Source/WebCore/html/shadow/DateTimeFieldElements.cpp
Source/WebCore/html/shadow/DateTimeFieldElements.h
Source/WebCore/html/shadow/DateTimeNumericFieldElement.cpp
Source/WebCore/html/shadow/DateTimeNumericFieldElement.h

index 2d49700..3ef1ad4 100644 (file)
@@ -1,3 +1,13 @@
+2012-10-03  Kent Tamura  <tkent@chromium.org>
+
+        DateTimeYearFieldElement should respect min/max values specified by page authors
+        https://bugs.webkit.org/show_bug.cgi?id=98227
+
+        Reviewed by Hajime Morita.
+
+        * fast/forms/month-multiple-fields/month-multiple-fields-keyboard-events-expected.txt: Added.
+        * fast/forms/month-multiple-fields/month-multiple-fields-keyboard-events.html: Added.
+
 2012-10-03  Douglas Stockwell  <dstockwell@chromium.org>
 
         Chromium needs support for border radius clipping
diff --git a/LayoutTests/fast/forms/month-multiple-fields/month-multiple-fields-keyboard-events-expected.txt b/LayoutTests/fast/forms/month-multiple-fields/month-multiple-fields-keyboard-events-expected.txt
new file mode 100644 (file)
index 0000000..9edf883
--- /dev/null
@@ -0,0 +1,59 @@
+Multiple fields UI of month input type with keyboard events
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Please run this with DumpRenderTree.
+
+Test following keys:
+Digits
+Left/Right - Move focus field inside element
+Up/Down - Increment/decrement value of focus field
+Tab - Move focus field
+Backspace - Make value empty
+  
+== Digit keys ==
+PASS input.value is "2012-09"
+== Left/Right keys ==
+PASS input.value is "0005-06"
+PASS document.activeElement.id is "input"
+== Up/Down keys ==
+PASS input.value is "2012-10"
+PASS input.value is "2012-08"
+== Up/Down keys on empty value ==
+PASS input.value is "2012-11"
+== Up/Down keys on empty value 2 ==
+PASS input.value is "2012-02"
+== Tab key ==
+PASS input.value is "0005-09"
+PASS input.value is "0005-07"
+PASS document.activeElement.id is "another"
+== Shfit+Tab key ==
+PASS input.value is "0003-09"
+PASS document.activeElement.id is "before"
+== Up key on maximum value ==
+PASS input.value is "0001-10"
+== Up key with a maximum attribute ==
+PASS input.value is "0001-01"
+== Down key on minimum value ==
+PASS input.value is "275760-12"
+== Down key with a minimum attribute ==
+PASS input.value is "275760-12"
+== Inconsistent min-max attributes ==
+PASS input.value is "1000-12"
+PASS input.value is "1999-12"
+== Backspace key ==
+PASS input.value is ""
+== Delete key ==
+PASS input.value is ""
+== Typeahead ==
+PASS input.value is "0001-12"
+PASS input.value is "0002-12"
+== RTL Left/Right keys ==
+PASS input.value is "2012-01"
+PASS input.value is "0002-01"
+PASS input.value is "0002-03"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/forms/month-multiple-fields/month-multiple-fields-keyboard-events.html b/LayoutTests/fast/forms/month-multiple-fields/month-multiple-fields-keyboard-events.html
new file mode 100644 (file)
index 0000000..dc42e4c
--- /dev/null
@@ -0,0 +1,184 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../js/resources/js-test-pre.js"></script>
+</head>
+<body>
+<p id="description"></p>
+<p>
+Please run this with DumpRenderTree.
+</p>
+Test following keys:
+<ul>
+    <li>Digits</li>
+    <li>Left/Right - Move focus field inside element</li>
+    <li>Up/Down - Increment/decrement value of focus field</li>
+    <li>Tab - Move focus field</li>
+    <li>Backspace - Make value empty</li>
+</ul>
+<input id="before">
+<input id="input" type="month">
+<input id="another">
+<div id="console"></div>
+<script>
+description("Multiple fields UI of month input type with keyboard events");
+var input = document.getElementById("input");
+
+function keyDown(key, modifiers)
+{
+    if (!window.eventSender)
+        return;
+    eventSender.keyDown(key, modifiers);
+}
+
+function beginTest(title, value, opt_min, opt_max)
+{
+    debug("== " + title + " ==");
+    input.value = value;
+    input.min = opt_min ? opt_min : "";
+    input.max = opt_max ? opt_max : "";
+    input.blur();
+    input.focus();
+}
+
+// We assume the year-month format is MM/yyyy.
+
+beginTest('Digit keys');
+keyDown('9');
+keyDown('2');
+keyDown('0');
+keyDown('1');
+keyDown('2');
+keyDown('A');
+shouldBeEqualToString('input.value', '2012-09');
+
+// FIXME: We should test type ahead time out. When event.leapForward() affects
+// keyboard event time stamp, we can uncomment this fragment.
+/*
+beginTest('Digit keys with type ahead timeout');
+keyDown('1');
+leapForward(1100);
+keyDown('1');
+keyDown('5');
+keyDown('6');
+keyDown('A');
+shouldBeEqualToString('input.value', '0056-01');
+*/
+
+beginTest('Left/Right keys', '2012-09');
+keyDown('rightArrow');
+keyDown('5');
+keyDown('leftArrow');
+keyDown('6');
+shouldBeEqualToString('input.value', '0005-06');
+keyDown('leftArrow');
+keyDown('leftArrow');
+keyDown('leftArrow');
+shouldBeEqualToString('document.activeElement.id', 'input');
+
+beginTest('Up/Down keys', '2012-09');
+keyDown('upArrow');
+shouldBeEqualToString('input.value', '2012-10');
+keyDown('downArrow');
+keyDown('downArrow');
+shouldBeEqualToString('input.value', '2012-08');
+
+// Note that empty value for the year field without min/max attributes is not
+// testable because its default value depends on the current date.
+beginTest('Up/Down keys on empty value', '', '2012-01');
+keyDown('downArrow');
+keyDown('downArrow');
+keyDown('rightArrow');
+keyDown('upArrow');
+keyDown('upArrow');
+keyDown('downArrow');
+shouldBeEqualToString('input.value', '2012-11');
+
+beginTest('Up/Down keys on empty value 2', '', undefined, '2012-01');
+keyDown('upArrow');
+keyDown('upArrow');
+keyDown('rightArrow');
+keyDown('downArrow');
+keyDown('downArrow');
+keyDown('upArrow');
+shouldBeEqualToString('input.value', '2012-02');
+
+beginTest('Tab key', '2012-09');
+keyDown('\t');
+keyDown('5');
+shouldBeEqualToString('input.value', '0005-09');
+keyDown('\t', ['shiftKey']);
+keyDown('7');
+shouldBeEqualToString('input.value', '0005-07');
+keyDown('\t');
+shouldBeEqualToString('document.activeElement.id', 'another');
+
+beginTest('Shfit+Tab key', '2012-09');
+another.focus();
+keyDown('\t', ['shiftKey']);
+keyDown('3');
+shouldBeEqualToString('input.value', '0003-09');
+keyDown('\t', ['shiftKey']);
+keyDown('\t', ['shiftKey']);
+shouldBeEqualToString('document.activeElement.id', 'before');
+
+beginTest('Up key on maximum value', '275760-09');
+keyDown('upArrow');
+keyDown('\t');
+keyDown('upArrow');
+shouldBeEqualToString('input.value', '0001-10');
+beginTest('Up key with a maximum attribute', '1999-12', undefined, '1999-12');
+keyDown('upArrow');
+keyDown('\t');
+keyDown('upArrow');
+shouldBeEqualToString('input.value', '0001-01');
+
+beginTest('Down key on minimum value', '0001-01', 'bad min', 'wrong max');
+keyDown('downArrow');
+keyDown('\t');
+keyDown('downArrow');
+shouldBeEqualToString('input.value', '275760-12');
+beginTest('Down key with a minimum attribute', '0001-01', '0001-01');
+keyDown('downArrow');
+keyDown('\t');
+keyDown('downArrow');
+shouldBeEqualToString('input.value', '275760-12');
+
+beginTest('Inconsistent min-max attributes', '1999-12', '1999-12', '1000-01');
+keyDown('\t');
+keyDown('upArrow');
+shouldBeEqualToString('input.value', '1000-12');
+keyDown('downArrow');
+shouldBeEqualToString('input.value', '1999-12');
+
+beginTest('Backspace key', '2012-09');
+keyDown("\b");
+shouldBeEqualToString('input.value', '');
+
+beginTest('Delete key', '2012-09');
+keyDown("delete");
+shouldBeEqualToString('input.value', '');
+
+beginTest('Typeahead', '2012-12');
+keyDown('rightArrow');
+keyDown('1');
+shouldBeEqualToString('input.value', '0001-12');
+keyDown('leftArrow');
+keyDown('rightArrow');
+keyDown('2');
+shouldBeEqualToString('input.value', '0002-12');
+
+beginTest('RTL Left/Right keys', '2012-09');
+input.setAttribute("dir", "rtl");
+keyDown('1');
+shouldBeEqualToString('input.value', '2012-01');
+keyDown('leftArrow');
+keyDown('2');
+shouldBeEqualToString('input.value', '0002-01');
+keyDown('rightArrow');
+keyDown('3');
+shouldBeEqualToString('input.value', '0002-03');
+</script>
+<script src="../../js/resources/js-test-post.js"></script>
+</body>
+</html>
index ac9c460..4a989f1 100644 (file)
@@ -1,3 +1,74 @@
+2012-10-04  Kent Tamura  <tkent@chromium.org>
+
+        DateTimeYearFieldElement should respect min/max values specified by page authors
+        https://bugs.webkit.org/show_bug.cgi?id=98227
+
+        Reviewed by Hajime Morita.
+
+        Before this patch, we always set 1 to the minimum limit and 275760 to
+        the maximum limit for a year field, and a user can specify any year
+        regardless of min/max attributes. Such wide range is unnecessary for
+        normal applications and we should provide a way to limit the range.
+
+        Test: fast/forms/month-multiple-fields/month-multiple-fields-keyboard-events.html
+
+        * html/shadow/DateTimeFieldElements.h:
+        To add four constructor arguments, introduce Parameters struct.
+        Actually, we add the followings;
+         - minimum year in UI
+         - maximum year in UI
+         - min attribute is specified
+         - max attribute is specified
+        (Parameters):
+        (WebCore::DateTimeYearFieldElement::Parameters::Parameters):
+        (DateTimeYearFieldElement):
+        * html/shadow/DateTimeFieldElements.cpp:
+        (WebCore::DateTimeYearFieldElement::DateTimeYearFieldElement): ditto.
+        (WebCore::DateTimeYearFieldElement::create): ditto.
+        (WebCore::DateTimeYearFieldElement::clampValueForHardLimits):
+        Override DateTimeNumericFieldElement::clampValueForHardLimits.
+        By this, we allow to set out-of-range year values.
+        (WebCore::currentFullYear): A helper to get the current year.
+        (WebCore::DateTimeYearFieldElement::defaultValueForStepDown):
+        If the field has no value and step down operation occurs,
+         - the field has the current year if the max attribute is not specified.
+         - the field has the maximum value otherwise.
+        (WebCore::DateTimeYearFieldElement::defaultValueForStepUp): Similar change.
+
+        * html/shadow/DateTimeNumericFieldElement.h:
+        (DateTimeNumericFieldElement): Declare clampValueForHardLimits.
+        * html/shadow/DateTimeNumericFieldElement.cpp:
+        (WebCore::DateTimeNumericFieldElement::clampValueForHardLimits):
+        (WebCore::DateTimeNumericFieldElement::setValueAsInteger):
+        Call clampValueForHardLimits instead of clampValue in order to
+        distinguish limits for UI and limits for internal value update.
+
+        * html/shadow/DateTimeEditElement.h:
+        (LayoutParameters): Add minimumYear and maximumYear members.
+        (WebCore::DateTimeEditElement::LayoutParameters::LayoutParameters):
+        Initialize minimumYear and maximumYear.
+        (WebCore::DateTimeEditElement::LayoutParameters::undefinedYear):
+        Represents 'undefined' value for minimumYear and maximumYear.
+        * html/shadow/DateTimeEditElement.cpp:
+        (WebCore::DateTimeEditBuilder::visitField):
+        Preparas DateTimeYearField::Parameters and pass it to the DateTimeYearField factory.
+
+        * html/BaseMultipleFieldsDateAndTimeInputType.cpp:
+        (WebCore::BaseMultipleFieldsDateAndTimeInputType::fullYear):
+        A helper to get a year value from an attribute value string.
+        * html/BaseMultipleFieldsDateAndTimeInputType.h:
+        (BaseMultipleFieldsDateAndTimeInputType): Add fullYear().
+
+        * html/DateTimeInputType.cpp:
+        (WebCore::DateTimeInputType::setupLayoutParameters):
+        Set LayoutParameters::minimumYear and maximumYear.
+        * html/DateTimeLocalInputType.cpp:
+        (WebCore::DateTimeLocalInputType::setupLayoutParameters): ditto.
+        * html/MonthInputType.cpp:
+        (WebCore::MonthInputType::setupLayoutParameters): ditto.
+        * html/WeekInputType.cpp:
+        (WebCore::WeekInputType::setupLayoutParameters): ditto.
+
 2012-10-03  Adam Barth  <abarth@webkit.org>
 
         Unreviewed. Fix parse error in vcproj file.
index 5951314..bd0cafa 100644 (file)
@@ -304,6 +304,14 @@ void BaseMultipleFieldsDateAndTimeInputType::showPickerIndicator()
 }
 #endif
 
+int BaseMultipleFieldsDateAndTimeInputType::fullYear(const String& source) const
+{
+    DateComponents date;
+    if (!parseToDateComponents(source, &date))
+        return DateTimeEditElement::LayoutParameters::undefinedYear();
+    return date.fullYear();
+}
+
 bool BaseMultipleFieldsDateAndTimeInputType::shouldHaveSecondField(const DateComponents& date) const
 {
     StepRange stepRange = createStepRange(AnyIsDefaultStep);
index e78c556..5f30837 100644 (file)
@@ -44,6 +44,8 @@ class BaseMultipleFieldsDateAndTimeInputType : public BaseDateAndTimeInputType,
 protected:
     BaseMultipleFieldsDateAndTimeInputType(HTMLInputElement*);
     virtual ~BaseMultipleFieldsDateAndTimeInputType();
+
+    int fullYear(const String&) const;
     virtual void setupLayoutParameters(DateTimeEditElement::LayoutParameters&, const DateComponents&) const = 0;
     bool shouldHaveSecondField(const DateComponents&) const;
 
index dd2f20a..feb620b 100644 (file)
@@ -152,6 +152,8 @@ void DateTimeInputType::setupLayoutParameters(DateTimeEditElement::LayoutParamet
         layoutParameters.dateTimeFormat = layoutParameters.localizer.dateTimeFormatWithoutSecond();
         layoutParameters.fallbackDateTimeFormat = "dd/MM/yyyy HH:mm";
     }
+    layoutParameters.minimumYear = fullYear(element()->fastGetAttribute(minAttr));
+    layoutParameters.maximumYear = fullYear(element()->fastGetAttribute(maxAttr));
     layoutParameters.placeholderForDay = placeholderForDayOfMonthField();
     layoutParameters.placeholderForMonth = placeholderForMonthField();
     layoutParameters.placeholderForYear = placeholderForYearField();
index ffc452b..d3dd2f4 100644 (file)
@@ -158,6 +158,8 @@ void DateTimeLocalInputType::setupLayoutParameters(DateTimeEditElement::LayoutPa
         layoutParameters.dateTimeFormat = layoutParameters.localizer.dateTimeFormatWithoutSecond();
         layoutParameters.fallbackDateTimeFormat = "dd/MM/yyyy HH:mm";
     }
+    layoutParameters.minimumYear = fullYear(element()->fastGetAttribute(minAttr));
+    layoutParameters.maximumYear = fullYear(element()->fastGetAttribute(maxAttr));
     layoutParameters.placeholderForDay = placeholderForDayOfMonthField();
     layoutParameters.placeholderForMonth = placeholderForMonthField();
     layoutParameters.placeholderForYear = placeholderForYearField();
index ef07620..b587438 100644 (file)
@@ -156,6 +156,8 @@ void MonthInputType::setupLayoutParameters(DateTimeEditElement::LayoutParameters
 {
     layoutParameters.dateTimeFormat = monthFormatInLDML();
     layoutParameters.fallbackDateTimeFormat = "MM/yyyy";
+    layoutParameters.minimumYear = fullYear(element()->fastGetAttribute(minAttr));
+    layoutParameters.maximumYear = fullYear(element()->fastGetAttribute(maxAttr));
     layoutParameters.placeholderForMonth = "--";
     layoutParameters.placeholderForYear = "----";
 }
index 6cae87f..b16ac28 100644 (file)
@@ -110,6 +110,8 @@ void WeekInputType::setupLayoutParameters(DateTimeEditElement::LayoutParameters&
 {
     layoutParameters.dateTimeFormat = weekFormatInLDML();
     layoutParameters.fallbackDateTimeFormat = "'Week' ww-yyyy";
+    layoutParameters.minimumYear = fullYear(element()->fastGetAttribute(minAttr));
+    layoutParameters.maximumYear = fullYear(element()->fastGetAttribute(maxAttr));
     layoutParameters.placeholderForYear = "----";
 }
 #endif
index 20c51f4..c20080a 100644 (file)
@@ -157,9 +157,30 @@ void DateTimeEditBuilder::visitField(DateTimeFormat::FieldType fieldType, int)
         m_editElement.addField(DateTimeWeekFieldElement::create(document, m_editElement));
         return;
 
-    case DateTimeFormat::FieldTypeYear:
-        m_editElement.addField(DateTimeYearFieldElement::create(document, m_editElement, m_parameters.placeholderForYear));
+    case DateTimeFormat::FieldTypeYear: {
+        DateTimeYearFieldElement::Parameters yearParams;
+        if (m_parameters.minimumYear == m_parameters.undefinedYear()) {
+            yearParams.minimumYear = DateComponents::minimumYear();
+            yearParams.minIsSpecified = false;
+        } else {
+            yearParams.minimumYear = m_parameters.minimumYear;
+            yearParams.minIsSpecified = true;
+        }
+        if (m_parameters.maximumYear == m_parameters.undefinedYear()) {
+            yearParams.maximumYear = DateComponents::maximumYear();
+            yearParams.maxIsSpecified = false;
+        } else {
+            yearParams.maximumYear = m_parameters.maximumYear;
+            yearParams.maxIsSpecified = true;
+        }
+        if (yearParams.minimumYear > yearParams.maximumYear) {
+            std::swap(yearParams.minimumYear, yearParams.maximumYear);
+            std::swap(yearParams.minIsSpecified, yearParams.maxIsSpecified);
+        }
+        yearParams.placeholder = m_parameters.placeholderForYear;
+        m_editElement.addField(DateTimeYearFieldElement::create(document, m_editElement, yearParams));
         return;
+    }
 
     default:
         return;
index ecbeaad..46fc852 100644 (file)
@@ -66,6 +66,8 @@ public:
         String fallbackDateTimeFormat;
         Localizer& localizer;
         const StepRange stepRange;
+        int minimumYear;
+        int maximumYear;
         String placeholderForDay;
         String placeholderForMonth;
         String placeholderForYear;
@@ -73,8 +75,12 @@ public:
         LayoutParameters(Localizer& localizer, const StepRange& stepRange)
             : localizer(localizer)
             , stepRange(stepRange)
+            , minimumYear(undefinedYear())
+            , maximumYear(undefinedYear())
         {
         }
+
+        static inline int undefinedYear() { return -1; }
     };
 
     static PassRefPtr<DateTimeEditElement> create(Document*, EditControlOwner&);
index bbcede7..adcc722 100644 (file)
@@ -422,20 +422,29 @@ void DateTimeWeekFieldElement::setValueAsDateTimeFieldsState(const DateTimeField
 
 // ----------------------------
 
-DateTimeYearFieldElement::DateTimeYearFieldElement(Document* document, FieldOwner& fieldOwner, const String& placeholder)
-    : DateTimeNumericFieldElement(document, fieldOwner, DateComponents::minimumYear(), DateComponents::maximumYear(), placeholder)
+DateTimeYearFieldElement::DateTimeYearFieldElement(Document* document, FieldOwner& fieldOwner, const DateTimeYearFieldElement::Parameters& parameters)
+    : DateTimeNumericFieldElement(document, fieldOwner, parameters.minimumYear, parameters.maximumYear, parameters.placeholder.isEmpty() ? ASCIILiteral("----") : parameters.placeholder)
+    , m_minIsSpecified(parameters.minIsSpecified)
+    , m_maxIsSpecified(parameters.maxIsSpecified)
 {
+    ASSERT(parameters.minimumYear >= DateComponents::minimumYear());
+    ASSERT(parameters.maximumYear <= DateComponents::maximumYear());
 }
 
-PassRefPtr<DateTimeYearFieldElement> DateTimeYearFieldElement::create(Document* document, FieldOwner& fieldOwner, const String& placeholder)
+PassRefPtr<DateTimeYearFieldElement> DateTimeYearFieldElement::create(Document* document, FieldOwner& fieldOwner, const DateTimeYearFieldElement::Parameters& parameters)
 {
     DEFINE_STATIC_LOCAL(AtomicString, yearPsuedoId, ("-webkit-datetime-edit-year-field"));
-    RefPtr<DateTimeYearFieldElement> field = adoptRef(new DateTimeYearFieldElement(document, fieldOwner, placeholder.isEmpty() ? ASCIILiteral("----") : placeholder));
+    RefPtr<DateTimeYearFieldElement> field = adoptRef(new DateTimeYearFieldElement(document, fieldOwner, parameters));
     field->initialize(yearPsuedoId, AXYearFieldText());
     return field.release();
 }
 
-int DateTimeYearFieldElement::defaultValueForStepDown() const
+int DateTimeYearFieldElement::clampValueForHardLimits(int value) const
+{
+    return Range(DateComponents::minimumYear(), DateComponents::maximumYear()).clampValue(value);
+}
+
+static int currentFullYear()
 {
     double current = currentTimeMS();
     double utcOffset = calculateUTCOffset();
@@ -448,9 +457,14 @@ int DateTimeYearFieldElement::defaultValueForStepDown() const
     return date.fullYear();
 }
 
+int DateTimeYearFieldElement::defaultValueForStepDown() const
+{
+    return m_maxIsSpecified ? DateTimeNumericFieldElement::defaultValueForStepDown() : currentFullYear();
+}
+
 int DateTimeYearFieldElement::defaultValueForStepUp() const
 {
-    return defaultValueForStepDown();
+    return m_minIsSpecified ? DateTimeNumericFieldElement::defaultValueForStepUp() : currentFullYear();
 }
 
 void DateTimeYearFieldElement::populateDateTimeFieldsState(DateTimeFieldsState& dateTimeFieldsState)
index 1984efb..46d51a7 100644 (file)
@@ -166,10 +166,26 @@ class DateTimeYearFieldElement : public DateTimeNumericFieldElement {
     WTF_MAKE_NONCOPYABLE(DateTimeYearFieldElement);
 
 public:
-    static PassRefPtr<DateTimeYearFieldElement> create(Document*, FieldOwner&, const String& placeholder);
+    struct Parameters {
+        int minimumYear;
+        int maximumYear;
+        bool minIsSpecified;
+        bool maxIsSpecified;
+        String placeholder;
+
+        Parameters()
+            : minimumYear(-1)
+            , maximumYear(-1)
+            , minIsSpecified(false)
+            , maxIsSpecified(false)
+        {
+        }
+    };
+
+    static PassRefPtr<DateTimeYearFieldElement> create(Document*, FieldOwner&, const Parameters&);
 
 private:
-    DateTimeYearFieldElement(Document*, FieldOwner&, const String& placeholder);
+    DateTimeYearFieldElement(Document*, FieldOwner&, const Parameters&);
 
     // DateTimeFieldElement functions.
     virtual void populateDateTimeFieldsState(DateTimeFieldsState&) OVERRIDE FINAL;
@@ -177,8 +193,12 @@ private:
     virtual void setValueAsDateTimeFieldsState(const DateTimeFieldsState&, const DateComponents& dateForReadOnlyField) OVERRIDE FINAL;
 
     // DateTimeNumericFieldElement functions.
+    virtual int clampValueForHardLimits(int) const OVERRIDE FINAL;
     virtual int defaultValueForStepDown() const OVERRIDE FINAL;
     virtual int defaultValueForStepUp() const OVERRIDE FINAL;
+
+    bool m_minIsSpecified;
+    bool m_maxIsSpecified;
 };
 
 } // namespace WebCore
index a631cc5..2165f2f 100644 (file)
@@ -64,6 +64,11 @@ DateTimeNumericFieldElement::DateTimeNumericFieldElement(Document* document, Fie
 {
 }
 
+int DateTimeNumericFieldElement::clampValueForHardLimits(int value) const
+{
+    return clampValue(value);
+}
+
 int DateTimeNumericFieldElement::defaultValueForStepDown() const
 {
     return m_range.maximum;
@@ -144,7 +149,7 @@ void DateTimeNumericFieldElement::setEmptyValue(const DateComponents& dateForRea
 
 void DateTimeNumericFieldElement::setValueAsInteger(int value, EventBehavior eventBehavior)
 {
-    m_value = clampValue(value);
+    m_value = clampValueForHardLimits(value);
     m_hasValue = true;
     updateVisibleValue(eventBehavior);
     m_lastDigitCharTime = 0;
index 8d06cfb..78a0f46 100644 (file)
@@ -54,6 +54,7 @@ protected:
     DateTimeNumericFieldElement(Document*, FieldOwner&, int minimum, int maximum, const String& placeholder);
 
     int clampValue(int value) const { return m_range.clampValue(value); }
+    virtual int clampValueForHardLimits(int) const;
     virtual int defaultValueForStepDown() const;
     virtual int defaultValueForStepUp() const;
     const Range& range() const { return m_range; }