Title: [266779] trunk
Revision
266779
Author
[email protected]
Date
2020-09-09 07:48:26 -0700 (Wed, 09 Sep 2020)

Log Message

[macOS] Add editability to input type=time
https://bugs.webkit.org/show_bug.cgi?id=216188

Reviewed by Devin Rousso.

Source/WebCore:

This patch adds editability to input type=time by leveraging existing
logic to add editable components to date/time inputs.

DateTime{Hour|Minute|Second|Millisecond|Meridiem}FieldElements were
created to represent the new editable fields. By default, only the
hour and minute fields are displayed. However, the millisecond
and second fields may be added depending on the initial value of
the element, or the value of the step attribute.

Tests: fast/forms/time/time-editable-components/time-editable-components-focus-and-blur-events.html
       fast/forms/time/time-editable-components/time-editable-components-keyboard-events.html
       fast/forms/time/time-editable-components/time-editable-components-mouse-events.html
       fast/forms/time/time-editable-components/time-editable-components-second-and-millisecond-field.html

* css/html.css:

Update stylesheet to handle hour, minute, second, millisecond and meridiem fields.

(input::-webkit-datetime-edit-fields-wrapper):
(input::-webkit-datetime-edit-year-field,):
(input::-webkit-datetime-edit-year-field:focus,):
(input[disabled]::-webkit-datetime-edit-year-field,):
* html/BaseChooserOnlyDateAndTimeInputType.cpp:
(WebCore::DateTimeFormatValidator::visitField):
(WebCore::BaseChooserOnlyDateAndTimeInputType::updateInnerTextValue):
(WebCore::BaseChooserOnlyDateAndTimeInputType::attributeChanged):

The step attribute can determine whether the second and/or millisecond
fields are displayed. Consequently, we should update the fields in
m_dateTimeEditElement when the step attribute is changed.

* html/BaseChooserOnlyDateAndTimeInputType.h:

setupLayoutParameters() now takes an additional DateComponents argument.
This argument is needed to determine whether the second and/or
millisecond field is displayed.

* html/BaseDateAndTimeInputType.h:
* html/DateInputType.cpp:
(WebCore::DateInputType::setupLayoutParameters const):
* html/DateInputType.h:
* html/DateTimeFieldsState.h:
* html/DateTimeLocalInputType.cpp:
(WebCore::DateTimeLocalInputType::setupLayoutParameters const):
* html/DateTimeLocalInputType.h:
* html/MonthInputType.cpp:
(WebCore::MonthInputType::setupLayoutParameters const):
* html/MonthInputType.h:
* html/TimeInputType.cpp:
(WebCore::TimeInputType::isValidFormat const):
(WebCore::TimeInputType::formatDateTimeFieldsState const):
(WebCore::TimeInputType::setupLayoutParameters const):

The millisecond field is displayed if the date has a non-zero value for
milliseconds, or if the step attribute has sub-second precision. The
second field is displayed if the millisecond field is displayed, if the
date has a non-zero value for seconds, or if the step attribute has
sub-minute precision.

* html/TimeInputType.h:
* html/WeekInputType.cpp:
(WebCore::WeekInputType::setupLayoutParameters const):
* html/WeekInputType.h:
* html/shadow/DateTimeEditElement.cpp:
(WebCore::DateTimeEditBuilder::visitField): Updated to add new field types to the element.
* html/shadow/DateTimeEditElement.h:
* html/shadow/DateTimeFieldElements.cpp:
(WebCore::DateTimeHourFieldElement::DateTimeHourFieldElement):
(WebCore::DateTimeHourFieldElement::create):
(WebCore::DateTimeHourFieldElement::populateDateTimeFieldsState):
(WebCore::DateTimeHourFieldElement::setValueAsDate):
(WebCore::DateTimeMeridiemFieldElement::DateTimeMeridiemFieldElement):
(WebCore::DateTimeMeridiemFieldElement::create):
(WebCore::DateTimeMeridiemFieldElement::populateDateTimeFieldsState):
(WebCore::DateTimeMeridiemFieldElement::setValueAsDate):
(WebCore::DateTimeMillisecondFieldElement::DateTimeMillisecondFieldElement):
(WebCore::DateTimeMillisecondFieldElement::create):
(WebCore::DateTimeMillisecondFieldElement::populateDateTimeFieldsState):
(WebCore::DateTimeMillisecondFieldElement::setValueAsDate):
(WebCore::DateTimeMinuteFieldElement::DateTimeMinuteFieldElement):
(WebCore::DateTimeMinuteFieldElement::create):
(WebCore::DateTimeMinuteFieldElement::populateDateTimeFieldsState):
(WebCore::DateTimeMinuteFieldElement::setValueAsDate):
(WebCore::DateTimeSecondFieldElement::DateTimeSecondFieldElement):
(WebCore::DateTimeSecondFieldElement::create):
(WebCore::DateTimeSecondFieldElement::populateDateTimeFieldsState):
(WebCore::DateTimeSecondFieldElement::setValueAsDate):
* html/shadow/DateTimeFieldElements.h:
* html/shadow/DateTimeNumericFieldElement.cpp:
(WebCore::DateTimeNumericFieldElement::maximum const):
* html/shadow/DateTimeNumericFieldElement.h:
* html/shadow/DateTimeSymbolicFieldElement.cpp:
(WebCore::DateTimeSymbolicFieldElement::DateTimeSymbolicFieldElement):
(WebCore::DateTimeSymbolicFieldElement::handleKeyboardEvent): Implement editing using the same typeahead behavior as <select> elements.
(WebCore::DateTimeSymbolicFieldElement::indexOfSelectedOption const):
(WebCore::DateTimeSymbolicFieldElement::optionCount const):
(WebCore::DateTimeSymbolicFieldElement::optionAtIndex const):
* html/shadow/DateTimeSymbolicFieldElement.h:
* platform/text/PlatformLocale.cpp:
(WebCore::Locale::localizedDecimalSeparator):

Added method to ensure the correct decimal separator is displayed
depending on the user's locale. This separator is used when
the millisecond field is present.

* platform/text/PlatformLocale.h:

LayoutTests:

* TestExpectations:
* fast/forms/time/time-editable-components/time-editable-components-focus-and-blur-events-expected.txt: Added.
* fast/forms/time/time-editable-components/time-editable-components-focus-and-blur-events.html: Added.
* fast/forms/time/time-editable-components/time-editable-components-keyboard-events-expected.txt: Added.
* fast/forms/time/time-editable-components/time-editable-components-keyboard-events.html: Added.
* fast/forms/time/time-editable-components/time-editable-components-mouse-events-expected.txt: Added.
* fast/forms/time/time-editable-components/time-editable-components-mouse-events.html: Added.
* fast/forms/time/time-editable-components/time-editable-components-second-and-millisecond-field-expected.txt: Added.
* fast/forms/time/time-editable-components/time-editable-components-second-and-millisecond-field.html: Added.
* platform/mac-wk2/TestExpectations:
* platform/mac-wk2/fast/forms/time/time-appearance-basic-expected.txt: Rebaselined for new appearance.
* platform/mac-wk2/fast/forms/time/time-input-rendering-basic-expected.txt: Rebaselined for new appearance.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (266778 => 266779)


--- trunk/LayoutTests/ChangeLog	2020-09-09 13:37:16 UTC (rev 266778)
+++ trunk/LayoutTests/ChangeLog	2020-09-09 14:48:26 UTC (rev 266779)
@@ -1,3 +1,23 @@
+2020-09-09  Aditya Keerthi  <[email protected]>
+
+        [macOS] Add editability to input type=time
+        https://bugs.webkit.org/show_bug.cgi?id=216188
+
+        Reviewed by Devin Rousso.
+
+        * TestExpectations:
+        * fast/forms/time/time-editable-components/time-editable-components-focus-and-blur-events-expected.txt: Added.
+        * fast/forms/time/time-editable-components/time-editable-components-focus-and-blur-events.html: Added.
+        * fast/forms/time/time-editable-components/time-editable-components-keyboard-events-expected.txt: Added.
+        * fast/forms/time/time-editable-components/time-editable-components-keyboard-events.html: Added.
+        * fast/forms/time/time-editable-components/time-editable-components-mouse-events-expected.txt: Added.
+        * fast/forms/time/time-editable-components/time-editable-components-mouse-events.html: Added.
+        * fast/forms/time/time-editable-components/time-editable-components-second-and-millisecond-field-expected.txt: Added.
+        * fast/forms/time/time-editable-components/time-editable-components-second-and-millisecond-field.html: Added.
+        * platform/mac-wk2/TestExpectations:
+        * platform/mac-wk2/fast/forms/time/time-appearance-basic-expected.txt: Rebaselined for new appearance.
+        * platform/mac-wk2/fast/forms/time/time-input-rendering-basic-expected.txt: Rebaselined for new appearance.
+
 2020-09-08  Ryan Haddad  <[email protected]>
 
         Unreviewed test gardening after r266761.

Modified: trunk/LayoutTests/TestExpectations (266778 => 266779)


--- trunk/LayoutTests/TestExpectations	2020-09-09 13:37:16 UTC (rev 266778)
+++ trunk/LayoutTests/TestExpectations	2020-09-09 14:48:26 UTC (rev 266779)
@@ -31,6 +31,7 @@
 fast/css/ios [ Skip ]
 fast/css/watchos [ Skip ]
 fast/forms/date/date-editable-components [ Skip ]
+fast/forms/time/time-editable-components [ Skip ]
 fast/dom/Window/watchos [ Skip ]
 fast/forms/select/mac-wk2 [ Skip ]
 fast/forms/textarea/ios [ Skip ]

Added: trunk/LayoutTests/fast/forms/time/time-editable-components/time-editable-components-focus-and-blur-events-expected.txt (0 => 266779)


--- trunk/LayoutTests/fast/forms/time/time-editable-components/time-editable-components-focus-and-blur-events-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/forms/time/time-editable-components/time-editable-components-focus-and-blur-events-expected.txt	2020-09-09 14:48:26 UTC (rev 266779)
@@ -0,0 +1,61 @@
+Test for focus and blur events for <input type=time>
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Focus/blur using mouse
+
+PASS focusEventsFired is 1
+PASS blurEventsFired is 0
+PASS focusEventsFired is 1
+PASS blurEventsFired is 0
+PASS focusEventsFired is 1
+PASS blurEventsFired is 0
+PASS focusEventsFired is 1
+PASS blurEventsFired is 0
+PASS focusEventsFired is 1
+PASS blurEventsFired is 1
+
+Focus/blur using keyboard
+
+PASS focusEventsFired is 1
+PASS blurEventsFired is 0
+PASS focusEventsFired is 1
+PASS blurEventsFired is 0
+PASS focusEventsFired is 1
+PASS blurEventsFired is 0
+PASS focusEventsFired is 1
+PASS blurEventsFired is 1
+PASS focusEventsFired is 2
+PASS blurEventsFired is 1
+PASS focusEventsFired is 2
+PASS blurEventsFired is 1
+PASS focusEventsFired is 2
+PASS blurEventsFired is 1
+PASS focusEventsFired is 2
+PASS blurEventsFired is 2
+
+Focus/blur on disabled input
+
+PASS focusEventsFired is 0
+PASS blurEventsFired is 0
+PASS document.activeElement.id is "after"
+PASS focusEventsFired is 0
+PASS blurEventsFired is 0
+PASS document.activeElement.id is "before"
+PASS focusEventsFired is 0
+PASS blurEventsFired is 0
+
+Focus/blur on readonly input
+
+PASS focusEventsFired is 1
+PASS blurEventsFired is 0
+PASS document.activeElement.id is "after"
+PASS focusEventsFired is 1
+PASS blurEventsFired is 1
+PASS focusEventsFired is 1
+PASS blurEventsFired is 1
+PASS successfullyParsed is true
+
+TEST COMPLETE
+  

Added: trunk/LayoutTests/fast/forms/time/time-editable-components/time-editable-components-focus-and-blur-events.html (0 => 266779)


--- trunk/LayoutTests/fast/forms/time/time-editable-components/time-editable-components-focus-and-blur-events.html	                        (rev 0)
+++ trunk/LayoutTests/fast/forms/time/time-editable-components/time-editable-components-focus-and-blur-events.html	2020-09-09 14:48:26 UTC (rev 266779)
@@ -0,0 +1,168 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src=""
+<script src=""
+<style>
+input {
+    width: 300px;
+}
+
+input::-webkit-datetime-edit-text {
+    font-size: 30px;
+}
+
+input::-webkit-datetime-edit-hour-field {
+    font-size: 30px;
+}
+
+input::-webkit-datetime-edit-minute-field {
+    font-size: 30px;
+}
+
+input::-webkit-datetime-edit-meridiem-field {
+    font-size: 30px;
+}
+</style>
+</head>
+<body>
+
+<input id="before" type="text">
+<input id="input" type="time" value="18:30">
+<input id="after" type="text">
+
+<script>
+
+description("Test for focus and blur events for &lt;input type=time&gt;");
+
+blurEventsFired = 0;
+function onBlurEvent() {
+    blurEventsFired++;
+}
+
+focusEventsFired = 0;
+function onFocusEvent() {
+    focusEventsFired++;
+}
+
+function assertFocusAndBlurCount(numFocusEvents, numBlurEvents) {
+    shouldBe("focusEventsFired", numFocusEvents.toString());
+    shouldBe("blurEventsFired", numBlurEvents.toString());
+}
+
+function resetFocusAndBlurCount() {
+    blurEventsFired = 0;
+    focusEventsFired = 0;
+}
+
+function mouseClickOn(x, y) {
+    if (!window.eventSender)
+        return;
+    eventSender.mouseMoveTo(x + input.offsetLeft, y + input.offsetTop);
+    eventSender.mouseDown();
+    eventSender.mouseUp();
+}
+
+input.addEventListener("blur", onBlurEvent);
+input.addEventListener("focus", onFocusEvent);
+
+const center = input.offsetHeight / 2;
+
+debug("Focus/blur using mouse\n");
+
+// Click on hour field.
+mouseClickOn(20, center);
+assertFocusAndBlurCount(1, 0);
+// Click on minute field.
+mouseClickOn(60, center);
+assertFocusAndBlurCount(1, 0);
+// Click on AM/PM field.
+mouseClickOn(120, center);
+assertFocusAndBlurCount(1, 0);
+// Click on control, but not a specific field.
+mouseClickOn(250, center);
+assertFocusAndBlurCount(1, 0);
+// Click outside control.
+mouseClickOn(input.offsetWidth + 5, input.offsetHeight + 5);
+assertFocusAndBlurCount(1, 1);
+resetFocusAndBlurCount();
+
+debug("\nFocus/blur using keyboard\n");
+
+UIHelper.activateElement(before);
+// Focus on hour field.
+UIHelper.keyDown("\t");
+assertFocusAndBlurCount(1, 0);
+// Focus on minute field.
+UIHelper.keyDown("\t");
+assertFocusAndBlurCount(1, 0);
+// Focus on AM/PM field.
+UIHelper.keyDown("\t");
+assertFocusAndBlurCount(1, 0);
+// Focus out.
+UIHelper.keyDown("\t");
+assertFocusAndBlurCount(1, 1);
+// Focus on hour field.
+UIHelper.keyDown("\t", ["shiftKey"]);
+assertFocusAndBlurCount(2, 1);
+// Focus on minute field.
+UIHelper.keyDown("\t", ["shiftKey"]);
+assertFocusAndBlurCount(2, 1);
+// Focus on AM/PM field.
+UIHelper.keyDown("\t", ["shiftKey"]);
+assertFocusAndBlurCount(2, 1);
+// Focus out.
+UIHelper.keyDown("\t", ["shiftKey"]);
+assertFocusAndBlurCount(2, 2);
+resetFocusAndBlurCount();
+
+debug("\nFocus/blur on disabled input\n")
+
+input.disabled = true;
+
+UIHelper.activateElement(before);
+// Tab to focus should skip disabled input.
+UIHelper.keyDown("\t");
+assertFocusAndBlurCount(0, 0);
+shouldBeEqualToString("document.activeElement.id", "after");
+// Shift+Tab should skip disabled input.
+UIHelper.keyDown("\t", ["shiftKey"]);
+assertFocusAndBlurCount(0, 0);
+shouldBeEqualToString("document.activeElement.id", "before");
+// Clicking on any part of the control should not focus/blur events.
+mouseClickOn(20, center);
+mouseClickOn(60, center);
+mouseClickOn(120, center);
+mouseClickOn(250, center);
+mouseClickOn(input.offsetWidth + 5, input.offsetHeight + 5);
+assertFocusAndBlurCount(0, 0);
+resetFocusAndBlurCount();
+
+debug("\nFocus/blur on readonly input\n")
+
+input.disabled = false;
+input.readOnly = true;
+
+UIHelper.activateElement(before);
+// Tab to focus should not skip readonly input.
+UIHelper.keyDown("\t");
+assertFocusAndBlurCount(1, 0);
+UIHelper.keyDown("\t");
+UIHelper.keyDown("\t");
+UIHelper.keyDown("\t");
+shouldBeEqualToString("document.activeElement.id", "after");
+assertFocusAndBlurCount(1, 1);
+// Clicking on any part of the control should fire the appropriate events.
+mouseClickOn(20, center);
+mouseClickOn(60, center);
+mouseClickOn(120, center);
+mouseClickOn(250, center);
+mouseClickOn(input.offsetWidth + 5, input.offsetHeight + 5);
+assertFocusAndBlurCount(1, 1);
+resetFocusAndBlurCount();
+
+</script>
+
+<script src=""
+</body>
+</html>

Added: trunk/LayoutTests/fast/forms/time/time-editable-components/time-editable-components-keyboard-events-expected.txt (0 => 266779)


--- trunk/LayoutTests/fast/forms/time/time-editable-components/time-editable-components-keyboard-events-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/forms/time/time-editable-components/time-editable-components-keyboard-events-expected.txt	2020-09-09 14:48:26 UTC (rev 266779)
@@ -0,0 +1,72 @@
+Test for keyboard operations for <input type=time>
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+
+Digit keys
+PASS input.value is "00:02"
+PASS changeEventsFired is 1
+PASS inputEventsFired is 1
+
+Digit keys with timeout
+PASS input.value is "14:05"
+PASS changeEventsFired is 1
+PASS inputEventsFired is 1
+
+Digit keys clamp value
+PASS input.value is "13:22"
+PASS input.value is "12:22"
+PASS input.value is "13:22"
+PASS input.value is "13:06"
+PASS input.value is "13:59"
+PASS changeEventsFired is 5
+PASS inputEventsFired is 5
+
+Left/Right arrow keys
+PASS input.value is "02:02"
+PASS input.value is "03:03"
+PASS changeEventsFired is 5
+PASS inputEventsFired is 5
+
+Up/Down arrow keys
+PASS input.value is "12:59"
+PASS input.value is "13:59"
+PASS input.value is "12:59"
+PASS input.value is "23:59"
+PASS input.value is "11:59"
+PASS input.value is "23:59"
+PASS input.value is "11:59"
+PASS changeEventsFired is 7
+PASS inputEventsFired is 7
+
+Tab key
+PASS input.value is "02:02"
+PASS document.activeElement.id is "after"
+PASS input.value is "03:03"
+PASS document.activeElement.id is "before"
+PASS changeEventsFired is 3
+PASS inputEventsFired is 3
+
+Backspace key
+PASS input.value is ""
+PASS input.value is "19:30"
+PASS changeEventsFired is 2
+PASS inputEventsFired is 2
+
+Delete key
+PASS input.value is ""
+PASS changeEventsFired is 1
+PASS inputEventsFired is 1
+
+Disabled/readonly
+PASS input.value is "09:01"
+PASS input.value is "01:01"
+PASS input.value is "01:01"
+PASS input.value is "01:02"
+PASS changeEventsFired is 2
+PASS inputEventsFired is 2
+PASS successfullyParsed is true
+
+TEST COMPLETE
+  

Added: trunk/LayoutTests/fast/forms/time/time-editable-components/time-editable-components-keyboard-events.html (0 => 266779)


--- trunk/LayoutTests/fast/forms/time/time-editable-components/time-editable-components-keyboard-events.html	                        (rev 0)
+++ trunk/LayoutTests/fast/forms/time/time-editable-components/time-editable-components-keyboard-events.html	2020-09-09 14:48:26 UTC (rev 266779)
@@ -0,0 +1,175 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src=""
+<script src=""
+</head>
+<body>
+
+<input id="before" type="text">
+<input id="input" type="time">
+<input id="after" type="text">
+
+<script>
+
+jsTestIsAsync = true;
+
+changeEventsFired = 0;
+function onChangeEvent() {
+    changeEventsFired++;
+}
+
+inputEventsFired = 0;
+function onInputEvent() {
+    inputEventsFired++;
+}
+
+function beginTest(title, value) {
+    debug("\n" + title);
+    input.value = value || "";
+    input.blur();
+    input.focus();
+
+    changeEventsFired = 0;
+    inputEventsFired = 0;
+}
+
+input.addEventListener("change", onChangeEvent);
+input.addEventListener("input", onInputEvent);
+
+addEventListener("load", async () => {
+    description("Test for keyboard operations for &lt;input type=time&gt;");
+
+    beginTest("Digit keys");                               // [hh]:mm tt
+    UIHelper.keyDown("1");                                 // -> [01]:mm tt
+    UIHelper.keyDown("2");                                 // -> [12]:mm tt
+    UIHelper.keyDown("rightArrow");                        // -> 12:[mm] tt
+    UIHelper.keyDown("2");                                 // -> 12:[02] tt
+    UIHelper.keyDown("rightArrow");                        // -> 12:02 [tt]
+    UIHelper.keyDown("A");                                 // -> 12:02 [AM]
+    shouldBeEqualToString("input.value", "00:02");
+    shouldBe("changeEventsFired", "1");
+    shouldBe("inputEventsFired", "1");
+
+    beginTest("Digit keys with timeout");                  // [hh]:mm tt
+    UIHelper.keyDown("2");                                 // [02]:mm tt
+    UIHelper.keyDown("rightArrow");                        // -> 02:[mm] tt
+    UIHelper.keyDown("4");                                 // -> 02:[04] tt
+    await UIHelper.delayFor(1500);                         // Wait.
+    UIHelper.keyDown("5");                                 // -> 02:[05] tt
+    UIHelper.keyDown("rightArrow");                        // -> 02:05 [tt]
+    UIHelper.keyDown("P");                                 // -> 02:05 [PM]
+    shouldBeEqualToString("input.value", "14:05");
+    shouldBe("changeEventsFired", "1");
+    shouldBe("inputEventsFired", "1");
+
+    beginTest("Digit keys clamp value", "12:22");          // [12]:22 PM
+    UIHelper.keyDown("1");                                 // -> [01]:22 PM
+    shouldBeEqualToString("input.value", "13:22");
+    UIHelper.keyDown("3");                                 // -> [12]:22 PM
+    shouldBeEqualToString("input.value", "12:22");
+    UIHelper.keyDown("0");                                 // -> [01]:22 PM
+    shouldBeEqualToString("input.value", "13:22");
+    UIHelper.keyDown("rightArrow");                        // -> 13:[22] PM
+    UIHelper.keyDown("6");                                 // -> 13:[06] PM
+    shouldBeEqualToString("input.value", "13:06");
+    UIHelper.keyDown("1");                                 // -> 13:[59] PM
+    shouldBeEqualToString("input.value", "13:59");
+    shouldBe("changeEventsFired", "5");
+    shouldBe("inputEventsFired", "5");
+
+    beginTest("Left/Right arrow keys", "15:27");           // [03]:27 PM
+    UIHelper.keyDown("2");                                 // -> [02]:27 PM
+    UIHelper.keyDown("rightArrow");                        // -> 02:[27] PM
+    UIHelper.keyDown("2");                                 // -> 02:[02] PM
+    UIHelper.keyDown("rightArrow");                        // -> 02:02 [PM]
+    UIHelper.keyDown("A");                                 // -> 02:02 [AM]
+    shouldBeEqualToString("input.value", "02:02");
+    UIHelper.keyDown("leftArrow");                         // -> 02:[02] AM
+    UIHelper.keyDown("3");                                 // -> 02:[03] AM
+    UIHelper.keyDown("leftArrow");                         // -> [02]:03 AM
+    UIHelper.keyDown("3");                                 // -> [03]:03 AM
+    shouldBeEqualToString("input.value", "03:03");
+    shouldBe("changeEventsFired", "5");
+    shouldBe("inputEventsFired", "5");
+
+    beginTest("Up/Down arrow keys", "23:59");              // [11]:59 PM
+    UIHelper.keyDown("upArrow");                           // -> [12]:59 PM
+    shouldBeEqualToString("input.value", "12:59");
+    UIHelper.keyDown("upArrow");                           // -> [01]:59 PM
+    shouldBeEqualToString("input.value", "13:59");
+    UIHelper.keyDown("downArrow");                         // -> [12]:59 PM
+    shouldBeEqualToString("input.value", "12:59");
+    UIHelper.keyDown("downArrow");                         // -> [11]:59 PM
+    shouldBeEqualToString("input.value", "23:59");
+    UIHelper.keyDown("rightArrow");                        // -> 11:[59] PM
+    UIHelper.keyDown("rightArrow");                        // -> 11:59 [PM]
+    UIHelper.keyDown("upArrow");                           // -> 11:59 [AM]
+    shouldBeEqualToString("input.value", "11:59");
+    UIHelper.keyDown("downArrow");                         // -> 11:59 [PM]
+    shouldBeEqualToString("input.value", "23:59");
+    UIHelper.keyDown("downArrow");                         // -> 11:59 [AM]
+    shouldBeEqualToString("input.value", "11:59");
+    shouldBe("changeEventsFired", "7");
+    shouldBe("inputEventsFired", "7");
+
+    beginTest("Tab key");                                  // [hh]:mm tt
+    UIHelper.keyDown("2");                                 // -> [02]:mm tt
+    UIHelper.keyDown("\t");                                // -> 02:[mm] tt
+    UIHelper.keyDown("2");                                 // -> 02:[02] tt
+    UIHelper.keyDown("\t");                                // -> 02:02 [tt]
+    UIHelper.keyDown("A");                                 // -> 02:02 [AM]
+    shouldBeEqualToString("input.value", "02:02");
+    UIHelper.keyDown("\t");                                // Focus out.
+    shouldBeEqualToString("document.activeElement.id", "after");
+    UIHelper.keyDown("\t", ["shiftKey"]);                  // -> 02:02 [AM]
+    UIHelper.keyDown("\t", ["shiftKey"]);                  // -> 02:[02] AM
+    UIHelper.keyDown("3");                                 // -> 02:[03] AM
+    UIHelper.keyDown("\t", ["shiftKey"]);                  // -> [02]:03 AM
+    UIHelper.keyDown("3");                                 // -> [03]:03 AM
+    shouldBeEqualToString("input.value", "03:03");
+    UIHelper.keyDown("\t", ["shiftKey"]);                  // Focus out.
+    shouldBeEqualToString("document.activeElement.id", "before");
+    shouldBe("changeEventsFired", "3");
+    shouldBe("inputEventsFired", "3");
+
+    beginTest("Backspace key", "16:30");                   // [04]:30 PM
+    UIHelper.keyDown("\b");                                // -> [hh]:30 PM
+    shouldBeEqualToString("input.value", "");
+    UIHelper.keyDown("7");                                 // -> [07]:30 PM
+    shouldBeEqualToString("input.value", "19:30");
+    shouldBe("changeEventsFired", "2");
+    shouldBe("inputEventsFired", "2");
+
+    beginTest("Delete key", "18:20");                      // [06]:20 PM
+    UIHelper.keyDown("delete");                            // -> [hh]:20 PM
+    shouldBeEqualToString("input.value", "");
+    shouldBe("changeEventsFired", "1");
+    shouldBe("inputEventsFired", "1");
+
+    beginTest("Disabled/readonly", "09:01");
+    input.disabled = true;
+    UIHelper.keyDown("1");
+    shouldBeEqualToString("input.value", "09:01");
+    input.disabled = false;
+    input.focus();
+    UIHelper.keyDown("1");
+    shouldBeEqualToString("input.value", "01:01");
+    input.readOnly = true;
+    UIHelper.keyDown("rightArrow");
+    UIHelper.keyDown("2");
+    shouldBeEqualToString("input.value", "01:01");
+    input.readOnly = false;
+    UIHelper.keyDown("2");
+    shouldBeEqualToString("input.value", "01:02");
+    shouldBe("changeEventsFired", "2");
+    shouldBe("inputEventsFired", "2");
+
+    finishJSTest();
+});
+
+</script>
+
+<script src=""
+</body>
+</html>

Added: trunk/LayoutTests/fast/forms/time/time-editable-components/time-editable-components-mouse-events-expected.txt (0 => 266779)


--- trunk/LayoutTests/fast/forms/time/time-editable-components/time-editable-components-mouse-events-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/forms/time/time-editable-components/time-editable-components-mouse-events-expected.txt	2020-09-09 14:48:26 UTC (rev 266779)
@@ -0,0 +1,25 @@
+Test for mouse events for <input type=time>
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Enabled Input
+
+PASS input.value is "09:30"
+PASS input.value is "09:12"
+PASS input.value is "21:12"
+PASS input.value is "18:12"
+PASS input.value is "18:12"
+PASS clickEventsFired is 4
+
+Disabled Input
+
+PASS clickEventsFired is 0
+
+Readonly Input
+
+PASS clickEventsFired is 4
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/forms/time/time-editable-components/time-editable-components-mouse-events.html (0 => 266779)


--- trunk/LayoutTests/fast/forms/time/time-editable-components/time-editable-components-mouse-events.html	                        (rev 0)
+++ trunk/LayoutTests/fast/forms/time/time-editable-components/time-editable-components-mouse-events.html	2020-09-09 14:48:26 UTC (rev 266779)
@@ -0,0 +1,122 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src=""
+<script src=""
+<style>
+input {
+    width: 300px;
+}
+
+input::-webkit-datetime-edit-text {
+    font-size: 30px;
+}
+
+input::-webkit-datetime-edit-hour-field {
+    font-size: 30px;
+}
+
+input::-webkit-datetime-edit-minute-field {
+    font-size: 30px;
+}
+
+input::-webkit-datetime-edit-meridiem-field {
+    font-size: 30px;
+}
+</style>
+</head>
+<body>
+
+<input id="input" type="time" value="05:30">
+
+<script>
+
+description("Test for mouse events for &lt;input type=time&gt;");
+
+clickEventsFired = 0;
+function onClickEvent() {
+    clickEventsFired++;
+}
+
+function mouseClickOn(x, y) {
+    if (!window.eventSender)
+        return;
+    eventSender.mouseMoveTo(x + input.offsetLeft, y + input.offsetTop);
+    eventSender.mouseDown();
+    eventSender.mouseUp();
+}
+
+input.addEventListener("click", onClickEvent);
+const center = input.offsetHeight / 2;
+
+debug("Enabled Input\n");
+
+// Click on hour field.
+mouseClickOn(20, center);
+UIHelper.keyDown("9");
+shouldBeEqualToString("input.value", "09:30");
+
+// Click on minute field.
+mouseClickOn(60, center);
+UIHelper.keyDown("1");
+UIHelper.keyDown("2");
+shouldBeEqualToString("input.value", "09:12");
+
+// Click on AM/PM field.
+mouseClickOn(120, center);
+UIHelper.keyDown("upArrow");
+shouldBeEqualToString("input.value", "21:12");
+
+// Click on control, but not a specific field, defaults to first field.
+mouseClickOn(250, center);
+UIHelper.keyDown("6");
+shouldBeEqualToString("input.value", "18:12");
+
+// Click outside control.
+mouseClickOn(input.offsetWidth + 5, input.offsetHeight + 5);
+UIHelper.keyDown("5");
+shouldBeEqualToString("input.value", "18:12");
+
+shouldBe("clickEventsFired", "4");
+
+debug("\nDisabled Input\n");
+clickEventsFired = 0;
+input.disabled = true;
+input.readOnly = false;
+
+// Click on hour field.
+mouseClickOn(20, center);
+// Click on minute field.
+mouseClickOn(60, center);
+// Click on AM/PM field.
+mouseClickOn(120, center);
+// Click on control, but not a specific field, defaults to first field.
+mouseClickOn(250, center);
+// Click outside control.
+mouseClickOn(input.offsetWidth + 5, input.offsetHeight + 5);
+
+shouldBe("clickEventsFired", "0");
+
+debug("\nReadonly Input\n");
+clickEventsFired = 0;
+input.disabled = false;
+input.readOnly = true;
+
+// Click on hour field.
+mouseClickOn(20, center);
+// Click on minute field.
+mouseClickOn(60, center);
+// Click on AM/PM field.
+mouseClickOn(120, center);
+// Click on control, but not a specific field, defaults to first field.
+mouseClickOn(250, center);
+// Click outside control.
+mouseClickOn(input.offsetWidth + 5, input.offsetHeight + 5);
+
+shouldBe("clickEventsFired", "4");
+
+</script>
+
+<script src=""
+</body>
+</html>

Added: trunk/LayoutTests/fast/forms/time/time-editable-components/time-editable-components-second-and-millisecond-field-expected.txt (0 => 266779)


--- trunk/LayoutTests/fast/forms/time/time-editable-components/time-editable-components-second-and-millisecond-field-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/forms/time/time-editable-components/time-editable-components-second-and-millisecond-field-expected.txt	2020-09-09 14:48:26 UTC (rev 266779)
@@ -0,0 +1,40 @@
+Test for presence of second and millisecond fields in <input type=time>
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+
+Default
+PASS numberOfFields is 3
+
+Zero seconds
+PASS numberOfFields is 3
+
+Zero seconds and zero milliseconds
+PASS numberOfFields is 3
+
+Zero seconds and non-zero milliseconds
+PASS numberOfFields is 5
+
+Non-zero seconds
+PASS numberOfFields is 4
+
+Non-zero seconds and zero milliseconds
+PASS numberOfFields is 4
+
+Non-zero seconds and non-zero milliseconds
+PASS numberOfFields is 5
+
+Step attribute minute precision
+PASS numberOfFields is 3
+
+Step attribute second precision
+PASS numberOfFields is 4
+
+Step attribute millisecond precision
+PASS numberOfFields is 5
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+  

Added: trunk/LayoutTests/fast/forms/time/time-editable-components/time-editable-components-second-and-millisecond-field.html (0 => 266779)


--- trunk/LayoutTests/fast/forms/time/time-editable-components/time-editable-components-second-and-millisecond-field.html	                        (rev 0)
+++ trunk/LayoutTests/fast/forms/time/time-editable-components/time-editable-components-second-and-millisecond-field.html	2020-09-09 14:48:26 UTC (rev 266779)
@@ -0,0 +1,89 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src=""
+<script src=""
+</head>
+<body>
+
+<input id="before" type="text">
+<input id="input" type="time">
+<input id="after" type="text">
+
+<script>
+
+jsTestIsAsync = true;
+
+function beginTest(title, value, step) {
+    debug("\n" + title);
+    input.value = value || "";
+    input.step = step || "";
+    input.blur();
+    input.focus();
+}
+
+function verifyNumberOfFields(expected) {
+    numberOfFields = 0;
+
+    // Focus first field.
+    input.blur();
+    input.focus();
+
+    while (document.activeElement !== after) {
+        numberOfFields++;
+        UIHelper.keyDown("\t");
+    }
+
+    // Return focus to first field.
+    input.blur();
+    input.focus();
+
+    shouldBe("numberOfFields", expected.toString());
+}
+
+addEventListener("load", async () => {
+    description("Test for presence of second and millisecond fields in &lt;input type=time&gt;");
+
+    defaultNumberOfFields = 3; // Hour, minute, and AM/PM fields
+    numberOfFieldsWithSeconds = defaultNumberOfFields + 1;
+    numberOfFieldsWithMilliseconds = numberOfFieldsWithSeconds + 1;
+
+    beginTest("Default", "10:20");
+    verifyNumberOfFields(defaultNumberOfFields);
+
+    beginTest("Zero seconds", "10:20:00");
+    verifyNumberOfFields(defaultNumberOfFields);
+
+    beginTest("Zero seconds and zero milliseconds", "10:20:00.000");
+    verifyNumberOfFields(defaultNumberOfFields);
+
+    beginTest("Zero seconds and non-zero milliseconds", "10:20:00.567");
+    verifyNumberOfFields(numberOfFieldsWithMilliseconds);
+
+    beginTest("Non-zero seconds", "10:20:45");
+    verifyNumberOfFields(numberOfFieldsWithSeconds);
+
+    beginTest("Non-zero seconds and zero milliseconds", "10:20:45.000");
+    verifyNumberOfFields(numberOfFieldsWithSeconds);
+
+    beginTest("Non-zero seconds and non-zero milliseconds", "10:20:45.567");
+    verifyNumberOfFields(numberOfFieldsWithMilliseconds);
+
+    beginTest("Step attribute minute precision", "10:20", "60");
+    verifyNumberOfFields(defaultNumberOfFields);
+
+    beginTest("Step attribute second precision", "10:20", "2");
+    verifyNumberOfFields(numberOfFieldsWithSeconds);
+
+    beginTest("Step attribute millisecond precision", "10:20", "0.5");
+    verifyNumberOfFields(numberOfFieldsWithMilliseconds);
+
+    debug("");
+    finishJSTest();
+});
+
+</script>
+
+<script src=""
+</body>
+</html>

Modified: trunk/LayoutTests/platform/mac-wk2/TestExpectations (266778 => 266779)


--- trunk/LayoutTests/platform/mac-wk2/TestExpectations	2020-09-09 13:37:16 UTC (rev 266778)
+++ trunk/LayoutTests/platform/mac-wk2/TestExpectations	2020-09-09 14:48:26 UTC (rev 266779)
@@ -13,6 +13,7 @@
 editing/pasteboard/dom-paste [ Pass ]
 fast/events/cursors [ Pass ]
 fast/forms/date/date-editable-components [ Pass ]
+fast/forms/time/time-editable-components [ Pass ]
 fast/forms/select/mac-wk2 [ Pass ]
 fast/sandbox/mac [ Pass ]
 fast/scrolling/mac [ Pass ]

Modified: trunk/LayoutTests/platform/mac-wk2/fast/forms/time/time-appearance-basic-expected.txt (266778 => 266779)


--- trunk/LayoutTests/platform/mac-wk2/fast/forms/time/time-appearance-basic-expected.txt	2020-09-09 13:37:16 UTC (rev 266778)
+++ trunk/LayoutTests/platform/mac-wk2/fast/forms/time/time-appearance-basic-expected.txt	2020-09-09 14:48:26 UTC (rev 266779)
@@ -4,7 +4,7 @@
 step=60  
 step=3600  
 step=86400  
-step mismatched 
+step mismatched  
 RTL 
 Disabled, step=3600  
 Readonly, step=3600  

Modified: trunk/LayoutTests/platform/mac-wk2/fast/forms/time/time-input-rendering-basic-expected.txt (266778 => 266779)


--- trunk/LayoutTests/platform/mac-wk2/fast/forms/time/time-input-rendering-basic-expected.txt	2020-09-09 13:37:16 UTC (rev 266778)
+++ trunk/LayoutTests/platform/mac-wk2/fast/forms/time/time-input-rendering-basic-expected.txt	2020-09-09 14:48:26 UTC (rev 266779)
@@ -3,10 +3,46 @@
 layer at (0,0) size 800x600
   RenderBlock {HTML} at (0,0) size 800x600
     RenderBody {BODY} at (8,8) size 784x584
-      RenderText {#text} at (110,0) size 4x18
-        text run at (110,0) width 4: " "
+      RenderText {#text} at (110,2) size 4x18
+        text run at (110,2) width 4: " "
       RenderText {#text} at (0,0) size 0x0
-layer at (8,19) size 110x6 clip at (10,21) size 106x2
-  RenderFlexibleBox {INPUT} at (0,11) size 110x6 [bgcolor=#FFFFFF] [border: (2px inset #000000)]
-layer at (122,19) size 110x6 clip at (124,21) size 106x2
-  RenderFlexibleBox {INPUT} at (114,11) size 110x6 [bgcolor=#FFFFFF] [border: (2px inset #000000)]
+layer at (8,10) size 110x19 clip at (10,12) size 106x15
+  RenderFlexibleBox {INPUT} at (0,2) size 110x19 [bgcolor=#FFFFFF] [border: (2px inset #000000)]
+layer at (11,13) size 56x13 scrollHeight 14
+  RenderBlock {DIV} at (3,3) size 56x13
+    RenderBlock {DIV} at (0,0) size 56x13
+      RenderInline {DIV} at (0,0) size 17x15
+        RenderText {#text} at (1,0) size 15x13
+          text run at (1,0) width 15: "09"
+      RenderInline {DIV} at (0,0) size 4x13
+        RenderText {#text} at (16,0) size 4x13
+          text run at (16,0) width 4: ":"
+      RenderInline {DIV} at (0,0) size 15x15
+        RenderText {#text} at (20,0) size 13x13
+          text run at (20,0) width 13: "41"
+      RenderInline {DIV} at (0,0) size 4x13
+        RenderText {#text} at (33,0) size 4x13
+          text run at (33,0) width 4: " "
+      RenderInline {DIV} at (0,0) size 20x15
+        RenderText {#text} at (37,0) size 18x13
+          text run at (37,0) width 18: "AM"
+layer at (122,10) size 110x19 clip at (124,12) size 106x15
+  RenderFlexibleBox {INPUT} at (114,2) size 110x19 [bgcolor=#FFFFFF] [border: (2px inset #000000)]
+layer at (173,13) size 56x13 scrollHeight 14
+  RenderBlock {DIV} at (51,3) size 56x13
+    RenderBlock {DIV} at (0,0) size 56x13
+      RenderInline {DIV} at (0,0) size 17x15
+        RenderText {#text} at (23,0) size 15x13
+          text run at (23,0) width 15: "09"
+      RenderInline {DIV} at (0,0) size 4x13
+        RenderText {#text} at (38,0) size 4x13
+          text run at (38,0) width 4: ":"
+      RenderInline {DIV} at (0,0) size 15x15
+        RenderText {#text} at (42,0) size 13x13
+          text run at (42,0) width 13: "41"
+      RenderInline {DIV} at (0,0) size 4x13
+        RenderText {#text} at (19,0) size 4x13
+          text run at (19,0) width 4 RTL: " "
+      RenderInline {DIV} at (0,0) size 20x15
+        RenderText {#text} at (1,0) size 18x13
+          text run at (1,0) width 18: "AM"

Modified: trunk/Source/WebCore/ChangeLog (266778 => 266779)


--- trunk/Source/WebCore/ChangeLog	2020-09-09 13:37:16 UTC (rev 266778)
+++ trunk/Source/WebCore/ChangeLog	2020-09-09 14:48:26 UTC (rev 266779)
@@ -1,3 +1,117 @@
+2020-09-09  Aditya Keerthi  <[email protected]>
+
+        [macOS] Add editability to input type=time
+        https://bugs.webkit.org/show_bug.cgi?id=216188
+
+        Reviewed by Devin Rousso.
+
+        This patch adds editability to input type=time by leveraging existing
+        logic to add editable components to date/time inputs.
+
+        DateTime{Hour|Minute|Second|Millisecond|Meridiem}FieldElements were
+        created to represent the new editable fields. By default, only the
+        hour and minute fields are displayed. However, the millisecond
+        and second fields may be added depending on the initial value of
+        the element, or the value of the step attribute.
+
+        Tests: fast/forms/time/time-editable-components/time-editable-components-focus-and-blur-events.html
+               fast/forms/time/time-editable-components/time-editable-components-keyboard-events.html
+               fast/forms/time/time-editable-components/time-editable-components-mouse-events.html
+               fast/forms/time/time-editable-components/time-editable-components-second-and-millisecond-field.html
+
+        * css/html.css:
+
+        Update stylesheet to handle hour, minute, second, millisecond and meridiem fields.
+
+        (input::-webkit-datetime-edit-fields-wrapper):
+        (input::-webkit-datetime-edit-year-field,):
+        (input::-webkit-datetime-edit-year-field:focus,):
+        (input[disabled]::-webkit-datetime-edit-year-field,):
+        * html/BaseChooserOnlyDateAndTimeInputType.cpp:
+        (WebCore::DateTimeFormatValidator::visitField):
+        (WebCore::BaseChooserOnlyDateAndTimeInputType::updateInnerTextValue):
+        (WebCore::BaseChooserOnlyDateAndTimeInputType::attributeChanged):
+
+        The step attribute can determine whether the second and/or millisecond
+        fields are displayed. Consequently, we should update the fields in
+        m_dateTimeEditElement when the step attribute is changed.
+
+        * html/BaseChooserOnlyDateAndTimeInputType.h:
+
+        setupLayoutParameters() now takes an additional DateComponents argument.
+        This argument is needed to determine whether the second and/or
+        millisecond field is displayed.
+
+        * html/BaseDateAndTimeInputType.h:
+        * html/DateInputType.cpp:
+        (WebCore::DateInputType::setupLayoutParameters const):
+        * html/DateInputType.h:
+        * html/DateTimeFieldsState.h:
+        * html/DateTimeLocalInputType.cpp:
+        (WebCore::DateTimeLocalInputType::setupLayoutParameters const):
+        * html/DateTimeLocalInputType.h:
+        * html/MonthInputType.cpp:
+        (WebCore::MonthInputType::setupLayoutParameters const):
+        * html/MonthInputType.h:
+        * html/TimeInputType.cpp:
+        (WebCore::TimeInputType::isValidFormat const):
+        (WebCore::TimeInputType::formatDateTimeFieldsState const):
+        (WebCore::TimeInputType::setupLayoutParameters const):
+
+        The millisecond field is displayed if the date has a non-zero value for
+        milliseconds, or if the step attribute has sub-second precision. The
+        second field is displayed if the millisecond field is displayed, if the
+        date has a non-zero value for seconds, or if the step attribute has
+        sub-minute precision.
+
+        * html/TimeInputType.h:
+        * html/WeekInputType.cpp:
+        (WebCore::WeekInputType::setupLayoutParameters const):
+        * html/WeekInputType.h:
+        * html/shadow/DateTimeEditElement.cpp:
+        (WebCore::DateTimeEditBuilder::visitField): Updated to add new field types to the element.
+        * html/shadow/DateTimeEditElement.h:
+        * html/shadow/DateTimeFieldElements.cpp:
+        (WebCore::DateTimeHourFieldElement::DateTimeHourFieldElement):
+        (WebCore::DateTimeHourFieldElement::create):
+        (WebCore::DateTimeHourFieldElement::populateDateTimeFieldsState):
+        (WebCore::DateTimeHourFieldElement::setValueAsDate):
+        (WebCore::DateTimeMeridiemFieldElement::DateTimeMeridiemFieldElement):
+        (WebCore::DateTimeMeridiemFieldElement::create):
+        (WebCore::DateTimeMeridiemFieldElement::populateDateTimeFieldsState):
+        (WebCore::DateTimeMeridiemFieldElement::setValueAsDate):
+        (WebCore::DateTimeMillisecondFieldElement::DateTimeMillisecondFieldElement):
+        (WebCore::DateTimeMillisecondFieldElement::create):
+        (WebCore::DateTimeMillisecondFieldElement::populateDateTimeFieldsState):
+        (WebCore::DateTimeMillisecondFieldElement::setValueAsDate):
+        (WebCore::DateTimeMinuteFieldElement::DateTimeMinuteFieldElement):
+        (WebCore::DateTimeMinuteFieldElement::create):
+        (WebCore::DateTimeMinuteFieldElement::populateDateTimeFieldsState):
+        (WebCore::DateTimeMinuteFieldElement::setValueAsDate):
+        (WebCore::DateTimeSecondFieldElement::DateTimeSecondFieldElement):
+        (WebCore::DateTimeSecondFieldElement::create):
+        (WebCore::DateTimeSecondFieldElement::populateDateTimeFieldsState):
+        (WebCore::DateTimeSecondFieldElement::setValueAsDate):
+        * html/shadow/DateTimeFieldElements.h:
+        * html/shadow/DateTimeNumericFieldElement.cpp:
+        (WebCore::DateTimeNumericFieldElement::maximum const):
+        * html/shadow/DateTimeNumericFieldElement.h:
+        * html/shadow/DateTimeSymbolicFieldElement.cpp:
+        (WebCore::DateTimeSymbolicFieldElement::DateTimeSymbolicFieldElement):
+        (WebCore::DateTimeSymbolicFieldElement::handleKeyboardEvent): Implement editing using the same typeahead behavior as <select> elements.
+        (WebCore::DateTimeSymbolicFieldElement::indexOfSelectedOption const):
+        (WebCore::DateTimeSymbolicFieldElement::optionCount const):
+        (WebCore::DateTimeSymbolicFieldElement::optionAtIndex const):
+        * html/shadow/DateTimeSymbolicFieldElement.h:
+        * platform/text/PlatformLocale.cpp:
+        (WebCore::Locale::localizedDecimalSeparator):
+
+        Added method to ensure the correct decimal separator is displayed
+        depending on the user's locale. This separator is used when
+        the millisecond field is present.
+
+        * platform/text/PlatformLocale.h:
+
 2020-09-08  Ryosuke Niwa  <[email protected]>
 
         Node flags should be an OptionSet

Modified: trunk/Source/WebCore/css/html.css (266778 => 266779)


--- trunk/Source/WebCore/css/html.css	2020-09-09 13:37:16 UTC (rev 266778)
+++ trunk/Source/WebCore/css/html.css	2020-09-09 14:48:26 UTC (rev 266779)
@@ -497,11 +497,17 @@
 
 input::-webkit-datetime-edit-fields-wrapper {
     display: inline-block;
+    white-space: pre;
 }
 
 input::-webkit-datetime-edit-year-field,
 input::-webkit-datetime-edit-month-field,
-input::-webkit-datetime-edit-day-field {
+input::-webkit-datetime-edit-day-field,
+input::-webkit-datetime-edit-hour-field,
+input::-webkit-datetime-edit-minute-field,
+input::-webkit-datetime-edit-second-field,
+input::-webkit-datetime-edit-millisecond-field,
+input::-webkit-datetime-edit-meridiem-field {
     display: inline;
     padding: 1px;
 }
@@ -508,7 +514,12 @@
 
 input::-webkit-datetime-edit-year-field:focus,
 input::-webkit-datetime-edit-month-field:focus,
-input::-webkit-datetime-edit-day-field:focus {
+input::-webkit-datetime-edit-day-field:focus,
+input::-webkit-datetime-edit-hour-field:focus,
+input::-webkit-datetime-edit-minute-field:focus,
+input::-webkit-datetime-edit-second-field:focus,
+input::-webkit-datetime-edit-millisecond-field:focus,
+input::-webkit-datetime-edit-meridiem-field:focus {
 #if defined(WTF_PLATFORM_COCOA) && WTF_PLATFORM_COCOA
     background-color: -apple-system-control-accent;
     color: white;
@@ -522,6 +533,11 @@
 input[disabled]::-webkit-datetime-edit-year-field,
 input[disabled]::-webkit-datetime-edit-month-field,
 input[disabled]::-webkit-datetime-edit-day-field,
+input[disabled]::-webkit-datetime-edit-hour-field,
+input[disabled]::-webkit-datetime-edit-minute-field,
+input[disabled]::-webkit-datetime-edit-second-field,
+input[disabled]::-webkit-datetime-edit-millisecond-field,
+input[disabled]::-webkit-datetime-edit-meridiem-field,
 input[disabled]::-webkit-datetime-edit-text {
     color: GrayText;
 }

Modified: trunk/Source/WebCore/html/BaseChooserOnlyDateAndTimeInputType.cpp (266778 => 266779)


--- trunk/Source/WebCore/html/BaseChooserOnlyDateAndTimeInputType.cpp	2020-09-09 13:37:16 UTC (rev 266778)
+++ trunk/Source/WebCore/html/BaseChooserOnlyDateAndTimeInputType.cpp	2020-09-09 14:48:26 UTC (rev 266779)
@@ -40,6 +40,7 @@
 #include "RenderElement.h"
 #include "Settings.h"
 #include "ShadowRoot.h"
+#include "StepRange.h"
 #include "Text.h"
 #include "UserGestureIndicator.h"
 #include <wtf/NeverDestroyed.h>
@@ -80,7 +81,7 @@
         break;
 
     case DateTimeFormat::FieldTypePeriod:
-        m_results.add(DateTimeFormatValidationResults::HasAMPM);
+        m_results.add(DateTimeFormatValidationResults::HasMeridiem);
         break;
 
     case DateTimeFormat::FieldTypeHour11:
@@ -91,7 +92,7 @@
     case DateTimeFormat::FieldTypeHour23:
     case DateTimeFormat::FieldTypeHour24:
         m_results.add(DateTimeFormatValidationResults::HasHour);
-        m_results.add(DateTimeFormatValidationResults::HasAMPM);
+        m_results.add(DateTimeFormatValidationResults::HasMeridiem);
         break;
 
     case DateTimeFormat::FieldTypeMinute:
@@ -192,12 +193,21 @@
     }
 
     DateTimeEditElement::LayoutParameters layoutParameters(element()->locale());
-    setupLayoutParameters(layoutParameters);
 
+    auto date = parseToDateComponents(element()->value());
+    if (date)
+        setupLayoutParameters(layoutParameters, *date);
+    else {
+        if (auto dateForLayout = setMillisecondToDateComponents(createStepRange(AnyStepHandling::Default).minimum().toDouble()))
+            setupLayoutParameters(layoutParameters, *dateForLayout);
+        else
+            setupLayoutParameters(layoutParameters, DateComponents());
+    }
+
     if (!DateTimeFormatValidator().validateFormat(layoutParameters.dateTimeFormat, *this))
         layoutParameters.dateTimeFormat = layoutParameters.fallbackDateTimeFormat;
 
-    if (auto date = parseToDateComponents(element()->value()))
+    if (date)
         m_dateTimeEditElement->setValueAsDate(layoutParameters, *date);
     else
         m_dateTimeEditElement->setEmptyValue(layoutParameters);
@@ -298,7 +308,9 @@
             if (!element->hasDirtyValue())
                 updateInnerTextValue();
         }
-    }
+    } else if (name == stepAttr && m_dateTimeEditElement)
+        updateInnerTextValue();
+
     BaseDateAndTimeInputType::attributeChanged(name);
 }
 

Modified: trunk/Source/WebCore/html/BaseChooserOnlyDateAndTimeInputType.h (266778 => 266779)


--- trunk/Source/WebCore/html/BaseChooserOnlyDateAndTimeInputType.h	2020-09-09 13:37:16 UTC (rev 266778)
+++ trunk/Source/WebCore/html/BaseChooserOnlyDateAndTimeInputType.h	2020-09-09 14:48:26 UTC (rev 266779)
@@ -42,7 +42,7 @@
     HasMonth = 1 << 1,
     HasWeek = 1 << 2,
     HasDay = 1 << 3,
-    HasAMPM = 1 << 4,
+    HasMeridiem = 1 << 4,
     HasHour = 1 << 5,
     HasMinute = 1 << 6,
     HasSecond = 1 << 7,
@@ -56,7 +56,7 @@
     explicit BaseChooserOnlyDateAndTimeInputType(HTMLInputElement& element) : BaseDateAndTimeInputType(element) { }
     ~BaseChooserOnlyDateAndTimeInputType();
 
-    virtual void setupLayoutParameters(DateTimeEditElement::LayoutParameters&) const = 0;
+    virtual void setupLayoutParameters(DateTimeEditElement::LayoutParameters&, const DateComponents&) const = 0;
 
 private:
     void updateInnerTextValue() override;

Modified: trunk/Source/WebCore/html/BaseDateAndTimeInputType.h (266778 => 266779)


--- trunk/Source/WebCore/html/BaseDateAndTimeInputType.h	2020-09-09 13:37:16 UTC (rev 266778)
+++ trunk/Source/WebCore/html/BaseDateAndTimeInputType.h	2020-09-09 14:48:26 UTC (rev 266779)
@@ -53,10 +53,9 @@
     bool isKeyboardFocusable(KeyboardEvent*) const override;
 
     virtual Optional<DateComponents> parseToDateComponents(const StringView&) const = 0;
+    virtual Optional<DateComponents> setMillisecondToDateComponents(double) const = 0;
 
 private:
-    virtual Optional<DateComponents> setMillisecondToDateComponents(double) const = 0;
-
     double valueAsDate() const override;
     ExceptionOr<void> setValueAsDate(double) const override;
     double valueAsDouble() const override;

Modified: trunk/Source/WebCore/html/DateInputType.cpp (266778 => 266779)


--- trunk/Source/WebCore/html/DateInputType.cpp	2020-09-09 13:37:16 UTC (rev 266778)
+++ trunk/Source/WebCore/html/DateInputType.cpp	2020-09-09 14:48:26 UTC (rev 266779)
@@ -102,7 +102,7 @@
     return makeString(pad('0', 4, *state.year), '-', pad('0', 2, *state.month), '-', pad('0', 2, *state.dayOfMonth));
 }
 
-void DateInputType::setupLayoutParameters(DateTimeEditElement::LayoutParameters& layoutParameters) const
+void DateInputType::setupLayoutParameters(DateTimeEditElement::LayoutParameters& layoutParameters, const DateComponents&) const
 {
     layoutParameters.dateTimeFormat = layoutParameters.locale.dateFormat();
     layoutParameters.fallbackDateTimeFormat = "yyyy-MM-dd"_s;

Modified: trunk/Source/WebCore/html/DateInputType.h (266778 => 266779)


--- trunk/Source/WebCore/html/DateInputType.h	2020-09-09 13:37:16 UTC (rev 266778)
+++ trunk/Source/WebCore/html/DateInputType.h	2020-09-09 14:48:26 UTC (rev 266779)
@@ -50,7 +50,7 @@
 
     bool isValidFormat(OptionSet<DateTimeFormatValidationResults>) const final;
     String formatDateTimeFieldsState(const DateTimeFieldsState&) const final;
-    void setupLayoutParameters(DateTimeEditElement::LayoutParameters&) const final;
+    void setupLayoutParameters(DateTimeEditElement::LayoutParameters&, const DateComponents&) const final;
 };
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/html/DateTimeFieldsState.h (266778 => 266779)


--- trunk/Source/WebCore/html/DateTimeFieldsState.h	2020-09-09 13:37:16 UTC (rev 266778)
+++ trunk/Source/WebCore/html/DateTimeFieldsState.h	2020-09-09 14:48:26 UTC (rev 266779)
@@ -33,9 +33,19 @@
 namespace WebCore {
 
 struct DateTimeFieldsState {
+    enum class Meridiem : bool {
+        AM,
+        PM,
+    };
+
     Optional<unsigned> year;
     Optional<unsigned> month;
     Optional<unsigned> dayOfMonth;
+    Optional<unsigned> hour;
+    Optional<unsigned> minute;
+    Optional<unsigned> second;
+    Optional<unsigned> millisecond;
+    Optional<Meridiem> meridiem;
 };
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/html/DateTimeLocalInputType.cpp (266778 => 266779)


--- trunk/Source/WebCore/html/DateTimeLocalInputType.cpp	2020-09-09 13:37:16 UTC (rev 266778)
+++ trunk/Source/WebCore/html/DateTimeLocalInputType.cpp	2020-09-09 14:48:26 UTC (rev 266779)
@@ -106,7 +106,7 @@
     return emptyString();
 }
 
-void DateTimeLocalInputType::setupLayoutParameters(DateTimeEditElement::LayoutParameters&) const
+void DateTimeLocalInputType::setupLayoutParameters(DateTimeEditElement::LayoutParameters&, const DateComponents&) const
 {
 }
 

Modified: trunk/Source/WebCore/html/DateTimeLocalInputType.h (266778 => 266779)


--- trunk/Source/WebCore/html/DateTimeLocalInputType.h	2020-09-09 13:37:16 UTC (rev 266778)
+++ trunk/Source/WebCore/html/DateTimeLocalInputType.h	2020-09-09 14:48:26 UTC (rev 266779)
@@ -52,7 +52,7 @@
 
     bool isValidFormat(OptionSet<DateTimeFormatValidationResults>) const final;
     String formatDateTimeFieldsState(const DateTimeFieldsState&) const final;
-    void setupLayoutParameters(DateTimeEditElement::LayoutParameters&) const final;
+    void setupLayoutParameters(DateTimeEditElement::LayoutParameters&, const DateComponents&) const final;
 };
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/html/MonthInputType.cpp (266778 => 266779)


--- trunk/Source/WebCore/html/MonthInputType.cpp	2020-09-09 13:37:16 UTC (rev 266778)
+++ trunk/Source/WebCore/html/MonthInputType.cpp	2020-09-09 14:48:26 UTC (rev 266779)
@@ -144,7 +144,7 @@
     return emptyString();
 }
 
-void MonthInputType::setupLayoutParameters(DateTimeEditElement::LayoutParameters&) const
+void MonthInputType::setupLayoutParameters(DateTimeEditElement::LayoutParameters&, const DateComponents&) const
 {
 }
 

Modified: trunk/Source/WebCore/html/MonthInputType.h (266778 => 266779)


--- trunk/Source/WebCore/html/MonthInputType.h	2020-09-09 13:37:16 UTC (rev 266778)
+++ trunk/Source/WebCore/html/MonthInputType.h	2020-09-09 14:48:26 UTC (rev 266779)
@@ -55,7 +55,7 @@
 
     bool isValidFormat(OptionSet<DateTimeFormatValidationResults>) const final;
     String formatDateTimeFieldsState(const DateTimeFieldsState&) const final;
-    void setupLayoutParameters(DateTimeEditElement::LayoutParameters&) const final;
+    void setupLayoutParameters(DateTimeEditElement::LayoutParameters&, const DateComponents&) const final;
 };
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/html/TimeInputType.cpp (266778 => 266779)


--- trunk/Source/WebCore/html/TimeInputType.cpp	2020-09-09 13:37:16 UTC (rev 266778)
+++ trunk/Source/WebCore/html/TimeInputType.cpp	2020-09-09 14:48:26 UTC (rev 266779)
@@ -33,6 +33,7 @@
 #include "TimeInputType.h"
 
 #include "DateComponents.h"
+#include "DateTimeFieldsState.h"
 #include "Decimal.h"
 #include "HTMLInputElement.h"
 #include "HTMLNames.h"
@@ -112,16 +113,48 @@
 
 bool TimeInputType::isValidFormat(OptionSet<DateTimeFormatValidationResults> results) const
 {
-    return results.containsAll({ DateTimeFormatValidationResults::HasHour, DateTimeFormatValidationResults::HasMinute });
+    return results.containsAll({ DateTimeFormatValidationResults::HasHour, DateTimeFormatValidationResults::HasMinute, DateTimeFormatValidationResults::HasMeridiem });
 }
 
-String TimeInputType::formatDateTimeFieldsState(const DateTimeFieldsState&) const
+String TimeInputType::formatDateTimeFieldsState(const DateTimeFieldsState& state) const
 {
-    return emptyString();
+    if (!state.hour || !state.minute || !state.meridiem)
+        return emptyString();
+
+    unsigned hour23 = (*state.hour % 12) + (*state.meridiem == DateTimeFieldsState::Meridiem::PM ? 12 : 0);
+    auto hourMinuteString = makeString(pad('0', 2, hour23), ':', pad('0', 2, *state.minute));
+
+    if (state.millisecond)
+        return makeString(hourMinuteString, ':', pad('0', 2, state.second ? *state.second : 0), '.', pad('0', 3, *state.millisecond));
+
+    if (state.second)
+        return makeString(hourMinuteString, ':', pad('0', 2, *state.second));
+
+    return hourMinuteString;
 }
 
-void TimeInputType::setupLayoutParameters(DateTimeEditElement::LayoutParameters&) const
+void TimeInputType::setupLayoutParameters(DateTimeEditElement::LayoutParameters& layoutParameters, const DateComponents& date) const
 {
+    auto stepRange = createStepRange(AnyStepHandling::Default);
+    auto millisecondsPerSecond = Decimal::fromDouble(msPerSecond);
+    auto millisecondsPerMinute = Decimal::fromDouble(msPerMinute);
+
+    layoutParameters.shouldHaveMillisecondField = date.millisecond()
+        || !stepRange.minimum().remainder(millisecondsPerSecond).isZero()
+        || !stepRange.step().remainder(millisecondsPerSecond).isZero();
+
+    bool shouldHaveSecondField = layoutParameters.shouldHaveMillisecondField
+        || date.second()
+        || !stepRange.minimum().remainder(millisecondsPerMinute).isZero()
+        || !stepRange.step().remainder(millisecondsPerMinute).isZero();
+
+    if (shouldHaveSecondField) {
+        layoutParameters.dateTimeFormat = layoutParameters.locale.timeFormat();
+        layoutParameters.fallbackDateTimeFormat = "HH:mm:ss"_s;
+    } else {
+        layoutParameters.dateTimeFormat = layoutParameters.locale.shortTimeFormat();
+        layoutParameters.fallbackDateTimeFormat = "HH:mm"_s;
+    }
 }
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/html/TimeInputType.h (266778 => 266779)


--- trunk/Source/WebCore/html/TimeInputType.h	2020-09-09 13:37:16 UTC (rev 266778)
+++ trunk/Source/WebCore/html/TimeInputType.h	2020-09-09 14:48:26 UTC (rev 266779)
@@ -52,7 +52,7 @@
 
     bool isValidFormat(OptionSet<DateTimeFormatValidationResults>) const final;
     String formatDateTimeFieldsState(const DateTimeFieldsState&) const final;
-    void setupLayoutParameters(DateTimeEditElement::LayoutParameters&) const final;
+    void setupLayoutParameters(DateTimeEditElement::LayoutParameters&, const DateComponents&) const final;
 };
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/html/WeekInputType.cpp (266778 => 266779)


--- trunk/Source/WebCore/html/WeekInputType.cpp	2020-09-09 13:37:16 UTC (rev 266778)
+++ trunk/Source/WebCore/html/WeekInputType.cpp	2020-09-09 14:48:26 UTC (rev 266779)
@@ -97,7 +97,7 @@
     return emptyString();
 }
 
-void WeekInputType::setupLayoutParameters(DateTimeEditElement::LayoutParameters&) const
+void WeekInputType::setupLayoutParameters(DateTimeEditElement::LayoutParameters&, const DateComponents&) const
 {
 }
 

Modified: trunk/Source/WebCore/html/WeekInputType.h (266778 => 266779)


--- trunk/Source/WebCore/html/WeekInputType.h	2020-09-09 13:37:16 UTC (rev 266778)
+++ trunk/Source/WebCore/html/WeekInputType.h	2020-09-09 14:48:26 UTC (rev 266779)
@@ -51,7 +51,7 @@
 
     bool isValidFormat(OptionSet<DateTimeFormatValidationResults>) const final;
     String formatDateTimeFieldsState(const DateTimeFieldsState&) const final;
-    void setupLayoutParameters(DateTimeEditElement::LayoutParameters&) const final;
+    void setupLayoutParameters(DateTimeEditElement::LayoutParameters&, const DateComponents&) const final;
 };
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/html/shadow/DateTimeEditElement.cpp (266778 => 266779)


--- trunk/Source/WebCore/html/shadow/DateTimeEditElement.cpp	2020-09-09 13:37:16 UTC (rev 266778)
+++ trunk/Source/WebCore/html/shadow/DateTimeEditElement.cpp	2020-09-09 14:48:26 UTC (rev 266779)
@@ -85,6 +85,36 @@
         return;
     }
 
+    case DateTimeFormat::FieldTypeFractionalSecond: {
+        m_editElement.addField(DateTimeMillisecondFieldElement::create(document, m_editElement));
+        return;
+    }
+
+    case DateTimeFormat::FieldTypeHour11: {
+        m_editElement.addField(DateTimeHourFieldElement::create(document, m_editElement, 0, 11));
+        return;
+    }
+
+    case DateTimeFormat::FieldTypeHour12: {
+        m_editElement.addField(DateTimeHourFieldElement::create(document, m_editElement, 1, 12));
+        return;
+    }
+
+    case DateTimeFormat::FieldTypeHour23: {
+        m_editElement.addField(DateTimeHourFieldElement::create(document, m_editElement, 0, 23));
+        return;
+    }
+
+    case DateTimeFormat::FieldTypeHour24: {
+        m_editElement.addField(DateTimeHourFieldElement::create(document, m_editElement, 1, 24));
+        return;
+    }
+
+    case DateTimeFormat::FieldTypeMinute: {
+        m_editElement.addField(DateTimeMinuteFieldElement::create(document, m_editElement));
+        return;
+    }
+
     case DateTimeFormat::FieldTypeMonth:
     case DateTimeFormat::FieldTypeMonthStandAlone: {
         constexpr int countForAbbreviatedMonth = 3;
@@ -109,6 +139,21 @@
         }
     }
 
+    case DateTimeFormat::FieldTypePeriod: {
+        m_editElement.addField(DateTimeMeridiemFieldElement::create(document, m_editElement, m_parameters.locale.timeAMPMLabels()));
+        return;
+    }
+
+    case DateTimeFormat::FieldTypeSecond: {
+        m_editElement.addField(DateTimeSecondFieldElement::create(document, m_editElement));
+
+        if (m_parameters.shouldHaveMillisecondField) {
+            visitLiteral(m_parameters.locale.localizedDecimalSeparator());
+            visitField(DateTimeFormat::FieldTypeFractionalSecond, 3);
+        }
+        return;
+    }
+
     case DateTimeFormat::FieldTypeYear: {
         m_editElement.addField(DateTimeYearFieldElement::create(document, m_editElement));
         return;

Modified: trunk/Source/WebCore/html/shadow/DateTimeEditElement.h (266778 => 266779)


--- trunk/Source/WebCore/html/shadow/DateTimeEditElement.h	2020-09-09 13:37:16 UTC (rev 266778)
+++ trunk/Source/WebCore/html/shadow/DateTimeEditElement.h	2020-09-09 14:48:26 UTC (rev 266779)
@@ -55,6 +55,7 @@
         String dateTimeFormat;
         String fallbackDateTimeFormat;
         Locale& locale;
+        bool shouldHaveMillisecondField { false };
 
         LayoutParameters(Locale& locale)
             : locale(locale)

Modified: trunk/Source/WebCore/html/shadow/DateTimeFieldElements.cpp (266778 => 266779)


--- trunk/Source/WebCore/html/shadow/DateTimeFieldElements.cpp	2020-09-09 13:37:16 UTC (rev 266778)
+++ trunk/Source/WebCore/html/shadow/DateTimeFieldElements.cpp	2020-09-09 14:48:26 UTC (rev 266779)
@@ -60,6 +60,145 @@
     setValueAsInteger(date.monthDay());
 }
 
+WTF_MAKE_ISO_ALLOCATED_IMPL(DateTimeHourFieldElement);
+
+DateTimeHourFieldElement::DateTimeHourFieldElement(Document& document, FieldOwner& fieldOwner, int minimum, int maximum)
+    : DateTimeNumericFieldElement(document, fieldOwner, Range(minimum, maximum), "--"_s)
+{
+    static MainThreadNeverDestroyed<const AtomString> hourPseudoId("-webkit-datetime-edit-hour-field", AtomString::ConstructFromLiteral);
+    initialize(hourPseudoId);
+}
+
+Ref<DateTimeHourFieldElement> DateTimeHourFieldElement::create(Document& document, FieldOwner& fieldOwner, int minimum, int maximum)
+{
+    return adoptRef(*new DateTimeHourFieldElement(document, fieldOwner, minimum, maximum));
+}
+
+void DateTimeHourFieldElement::populateDateTimeFieldsState(DateTimeFieldsState& state)
+{
+    if (!hasValue())
+        return;
+
+    int value = valueAsInteger();
+
+    switch (maximum()) {
+    case 11:
+        state.hour = value ?: 12;
+        return;
+    case 12:
+        state.hour = value;
+        return;
+    case 23:
+        state.hour = (value % 12) ?: 12;
+        state.meridiem = value >= 12 ? DateTimeFieldsState::Meridiem::PM : DateTimeFieldsState::Meridiem::AM;
+        return;
+    case 24:
+        if (value == 24) {
+            state.hour = 12;
+            state.meridiem = DateTimeFieldsState::Meridiem::AM;
+            return;
+        }
+        state.hour = (value % 12) ?: 12;
+        state.meridiem = value >= 12 ? DateTimeFieldsState::Meridiem::PM : DateTimeFieldsState::Meridiem::AM;
+        return;
+    }
+}
+
+void DateTimeHourFieldElement::setValueAsDate(const DateComponents& date)
+{
+    int hour = date.hour();
+
+    switch (maximum()) {
+    case 11:
+        setValueAsInteger(hour % 12);
+        return;
+    case 12:
+        setValueAsInteger((hour % 12) ?: 12);
+        return;
+    case 23:
+        setValueAsInteger(hour);
+        return;
+    case 24:
+        setValueAsInteger(hour + 1);
+        return;
+    }
+}
+
+WTF_MAKE_ISO_ALLOCATED_IMPL(DateTimeMeridiemFieldElement);
+
+DateTimeMeridiemFieldElement::DateTimeMeridiemFieldElement(Document& document, FieldOwner& fieldOwner, const Vector<String>& labels)
+    : DateTimeSymbolicFieldElement(document, fieldOwner, labels)
+{
+    static MainThreadNeverDestroyed<const AtomString> meridiemPseudoId("-webkit-datetime-edit-meridiem-field", AtomString::ConstructFromLiteral);
+    initialize(meridiemPseudoId);
+}
+
+Ref<DateTimeMeridiemFieldElement> DateTimeMeridiemFieldElement::create(Document& document, FieldOwner& fieldOwner, const Vector<String>& labels)
+{
+    return adoptRef(*new DateTimeMeridiemFieldElement(document, fieldOwner, labels));
+}
+
+void DateTimeMeridiemFieldElement::populateDateTimeFieldsState(DateTimeFieldsState& state)
+{
+    if (hasValue())
+        state.meridiem = valueAsInteger() ? DateTimeFieldsState::Meridiem::PM : DateTimeFieldsState::Meridiem::AM;
+}
+
+void DateTimeMeridiemFieldElement::setValueAsDate(const DateComponents& date)
+{
+    setValueAsInteger(date.hour() >= 12 ? 1 : 0);
+}
+
+WTF_MAKE_ISO_ALLOCATED_IMPL(DateTimeMillisecondFieldElement);
+
+DateTimeMillisecondFieldElement::DateTimeMillisecondFieldElement(Document& document, FieldOwner& fieldOwner)
+    : DateTimeNumericFieldElement(document, fieldOwner, Range(0, 999), "--"_s)
+{
+    static MainThreadNeverDestroyed<const AtomString> millisecondPseudoId("-webkit-datetime-edit-millisecond-field", AtomString::ConstructFromLiteral);
+    initialize(millisecondPseudoId);
+}
+
+Ref<DateTimeMillisecondFieldElement> DateTimeMillisecondFieldElement::create(Document& document, FieldOwner& fieldOwner)
+{
+    return adoptRef(*new DateTimeMillisecondFieldElement(document, fieldOwner));
+}
+
+void DateTimeMillisecondFieldElement::populateDateTimeFieldsState(DateTimeFieldsState& state)
+{
+    if (hasValue())
+        state.millisecond = valueAsInteger();
+}
+
+void DateTimeMillisecondFieldElement::setValueAsDate(const DateComponents& date)
+{
+    setValueAsInteger(date.millisecond());
+}
+
+WTF_MAKE_ISO_ALLOCATED_IMPL(DateTimeMinuteFieldElement);
+
+DateTimeMinuteFieldElement::DateTimeMinuteFieldElement(Document& document, FieldOwner& fieldOwner)
+    : DateTimeNumericFieldElement(document, fieldOwner, Range(0, 59), "--"_s)
+{
+    static MainThreadNeverDestroyed<const AtomString> minutePseudoId("-webkit-datetime-edit-minute-field", AtomString::ConstructFromLiteral);
+    initialize(minutePseudoId);
+}
+
+Ref<DateTimeMinuteFieldElement> DateTimeMinuteFieldElement::create(Document& document, FieldOwner& fieldOwner)
+{
+    return adoptRef(*new DateTimeMinuteFieldElement(document, fieldOwner));
+}
+
+void DateTimeMinuteFieldElement::populateDateTimeFieldsState(DateTimeFieldsState& state)
+{
+    if (hasValue())
+        state.minute = valueAsInteger();
+}
+
+void DateTimeMinuteFieldElement::setValueAsDate(const DateComponents& date)
+{
+    setValueAsInteger(date.minute());
+}
+
 WTF_MAKE_ISO_ALLOCATED_IMPL(DateTimeMonthFieldElement);
 
 DateTimeMonthFieldElement::DateTimeMonthFieldElement(Document& document, FieldOwner& fieldOwner)
@@ -86,6 +225,31 @@
     setValueAsInteger(date.month() + 1);
 }
 
+WTF_MAKE_ISO_ALLOCATED_IMPL(DateTimeSecondFieldElement);
+
+DateTimeSecondFieldElement::DateTimeSecondFieldElement(Document& document, FieldOwner& fieldOwner)
+    : DateTimeNumericFieldElement(document, fieldOwner, Range(0, 59), "--"_s)
+{
+    static MainThreadNeverDestroyed<const AtomString> secondPseudoId("-webkit-datetime-edit-second-field", AtomString::ConstructFromLiteral);
+    initialize(secondPseudoId);
+}
+
+Ref<DateTimeSecondFieldElement> DateTimeSecondFieldElement::create(Document& document, FieldOwner& fieldOwner)
+{
+    return adoptRef(*new DateTimeSecondFieldElement(document, fieldOwner));
+}
+
+void DateTimeSecondFieldElement::populateDateTimeFieldsState(DateTimeFieldsState& state)
+{
+    if (hasValue())
+        state.second = valueAsInteger();
+}
+
+void DateTimeSecondFieldElement::setValueAsDate(const DateComponents& date)
+{
+    setValueAsInteger(date.second());
+}
+
 WTF_MAKE_ISO_ALLOCATED_IMPL(DateTimeSymbolicMonthFieldElement);
 
 DateTimeSymbolicMonthFieldElement::DateTimeSymbolicMonthFieldElement(Document& document, FieldOwner& fieldOwner, const Vector<String>& labels)

Modified: trunk/Source/WebCore/html/shadow/DateTimeFieldElements.h (266778 => 266779)


--- trunk/Source/WebCore/html/shadow/DateTimeFieldElements.h	2020-09-09 13:37:16 UTC (rev 266778)
+++ trunk/Source/WebCore/html/shadow/DateTimeFieldElements.h	2020-09-09 14:48:26 UTC (rev 266779)
@@ -47,6 +47,62 @@
     void populateDateTimeFieldsState(DateTimeFieldsState&);
 };
 
+class DateTimeHourFieldElement final : public DateTimeNumericFieldElement {
+    WTF_MAKE_ISO_ALLOCATED(DateTimeHourFieldElement);
+
+public:
+    static Ref<DateTimeHourFieldElement> create(Document&, FieldOwner&, int minimum, int maximum);
+
+private:
+    DateTimeHourFieldElement(Document&, FieldOwner&, int minimum, int maximum);
+
+    // DateTimeFieldElement functions:
+    virtual void populateDateTimeFieldsState(DateTimeFieldsState&);
+    virtual void setValueAsDate(const DateComponents&);
+};
+
+class DateTimeMeridiemFieldElement final : public DateTimeSymbolicFieldElement {
+    WTF_MAKE_ISO_ALLOCATED(DateTimeMeridiemFieldElement);
+
+public:
+    static Ref<DateTimeMeridiemFieldElement> create(Document&, FieldOwner&, const Vector<String>&);
+
+private:
+    DateTimeMeridiemFieldElement(Document&, FieldOwner&, const Vector<String>&);
+
+    // DateTimeFieldElement functions:
+    void populateDateTimeFieldsState(DateTimeFieldsState&);
+    void setValueAsDate(const DateComponents&);
+};
+
+class DateTimeMillisecondFieldElement final : public DateTimeNumericFieldElement {
+    WTF_MAKE_ISO_ALLOCATED(DateTimeMillisecondFieldElement);
+
+public:
+    static Ref<DateTimeMillisecondFieldElement> create(Document&, FieldOwner&);
+
+private:
+    DateTimeMillisecondFieldElement(Document&, FieldOwner&);
+
+    // DateTimeFieldElement functions:
+    virtual void populateDateTimeFieldsState(DateTimeFieldsState&);
+    virtual void setValueAsDate(const DateComponents&);
+};
+
+class DateTimeMinuteFieldElement final : public DateTimeNumericFieldElement {
+    WTF_MAKE_ISO_ALLOCATED(DateTimeMinuteFieldElement);
+
+public:
+    static Ref<DateTimeMinuteFieldElement> create(Document&, FieldOwner&);
+
+private:
+    DateTimeMinuteFieldElement(Document&, FieldOwner&);
+
+    // DateTimeFieldElement functions:
+    virtual void populateDateTimeFieldsState(DateTimeFieldsState&);
+    virtual void setValueAsDate(const DateComponents&);
+};
+
 class DateTimeMonthFieldElement final : public DateTimeNumericFieldElement {
     WTF_MAKE_ISO_ALLOCATED(DateTimeMonthFieldElement);
 
@@ -61,6 +117,20 @@
     void populateDateTimeFieldsState(DateTimeFieldsState&);
 };
 
+class DateTimeSecondFieldElement final : public DateTimeNumericFieldElement {
+    WTF_MAKE_ISO_ALLOCATED(DateTimeSecondFieldElement);
+
+public:
+    static Ref<DateTimeSecondFieldElement> create(Document&, FieldOwner&);
+
+private:
+    DateTimeSecondFieldElement(Document&, FieldOwner&);
+
+    // DateTimeFieldElement functions:
+    virtual void populateDateTimeFieldsState(DateTimeFieldsState&);
+    virtual void setValueAsDate(const DateComponents&);
+};
+
 class DateTimeSymbolicMonthFieldElement final : public DateTimeSymbolicFieldElement {
     WTF_MAKE_ISO_ALLOCATED(DateTimeSymbolicMonthFieldElement);
 

Modified: trunk/Source/WebCore/html/shadow/DateTimeNumericFieldElement.cpp (266778 => 266779)


--- trunk/Source/WebCore/html/shadow/DateTimeNumericFieldElement.cpp	2020-09-09 13:37:16 UTC (rev 266778)
+++ trunk/Source/WebCore/html/shadow/DateTimeNumericFieldElement.cpp	2020-09-09 14:48:26 UTC (rev 266779)
@@ -57,6 +57,11 @@
 {
 }
 
+int DateTimeNumericFieldElement::maximum() const
+{
+    return m_range.maximum;
+}
+
 String DateTimeNumericFieldElement::formatValue(int value) const
 {
     Locale& locale = localeForOwner();

Modified: trunk/Source/WebCore/html/shadow/DateTimeNumericFieldElement.h (266778 => 266779)


--- trunk/Source/WebCore/html/shadow/DateTimeNumericFieldElement.h	2020-09-09 13:37:16 UTC (rev 266778)
+++ trunk/Source/WebCore/html/shadow/DateTimeNumericFieldElement.h	2020-09-09 14:48:26 UTC (rev 266779)
@@ -50,6 +50,8 @@
 protected:
     DateTimeNumericFieldElement(Document&, FieldOwner&, const Range&, const String& placeholder);
 
+    int maximum() const;
+
     // DateTimeFieldElement functions:
     bool hasValue() const final;
     void initialize(const AtomString&);

Modified: trunk/Source/WebCore/html/shadow/DateTimeSymbolicFieldElement.cpp (266778 => 266779)


--- trunk/Source/WebCore/html/shadow/DateTimeSymbolicFieldElement.cpp	2020-09-09 13:37:16 UTC (rev 266778)
+++ trunk/Source/WebCore/html/shadow/DateTimeSymbolicFieldElement.cpp	2020-09-09 14:48:26 UTC (rev 266779)
@@ -49,6 +49,7 @@
     : DateTimeFieldElement(document, fieldOwner)
     , m_symbols(symbols)
     , m_visibleEmptyValue(makeVisibleEmptyValue(symbols))
+    , m_typeAhead(this)
 {
     ASSERT(!m_symbols.isEmpty());
 }
@@ -111,11 +112,34 @@
     return hasValue() ? m_symbols[m_selectedIndex] : visibleEmptyValue();
 }
 
-void DateTimeSymbolicFieldElement::handleKeyboardEvent(KeyboardEvent&)
+void DateTimeSymbolicFieldElement::handleKeyboardEvent(KeyboardEvent& keyboardEvent)
 {
-    // FIXME: Implement after adding layout for <input type=time>.
+    if (keyboardEvent.type() != eventNames().keypressEvent)
+        return;
+
+    keyboardEvent.setDefaultHandled();
+
+    int index = m_typeAhead.handleEvent(&keyboardEvent, TypeAhead::MatchPrefix | TypeAhead::CycleFirstChar | TypeAhead::MatchIndex);
+    if (index < 0)
+        return;
+    setValueAsInteger(index, DispatchInputAndChangeEvents);
 }
 
+int DateTimeSymbolicFieldElement::indexOfSelectedOption() const
+{
+    return m_selectedIndex;
+}
+
+int DateTimeSymbolicFieldElement::optionCount() const
+{
+    return m_symbols.size();
+}
+
+String DateTimeSymbolicFieldElement::optionAtIndex(int index) const
+{
+    return m_symbols[index];
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(DATE_AND_TIME_INPUT_TYPES)

Modified: trunk/Source/WebCore/html/shadow/DateTimeSymbolicFieldElement.h (266778 => 266779)


--- trunk/Source/WebCore/html/shadow/DateTimeSymbolicFieldElement.h	2020-09-09 13:37:16 UTC (rev 266778)
+++ trunk/Source/WebCore/html/shadow/DateTimeSymbolicFieldElement.h	2020-09-09 14:48:26 UTC (rev 266779)
@@ -29,10 +29,11 @@
 #if ENABLE(DATE_AND_TIME_INPUT_TYPES)
 
 #include "DateTimeFieldElement.h"
+#include "TypeAhead.h"
 
 namespace WebCore {
 
-class DateTimeSymbolicFieldElement : public DateTimeFieldElement {
+class DateTimeSymbolicFieldElement : public DateTimeFieldElement, public TypeAheadDataSource {
     WTF_MAKE_ISO_ALLOCATED(DateTimeSymbolicFieldElement);
 protected:
     DateTimeSymbolicFieldElement(Document&, FieldOwner&, const Vector<String>&);
@@ -55,9 +56,15 @@
     String visibleValue() const final;
     void handleKeyboardEvent(KeyboardEvent&) final;
 
+    // TypeAheadDataSource functions:
+    int indexOfSelectedOption() const final;
+    int optionCount() const final;
+    String optionAtIndex(int index) const final;
+
     const Vector<String> m_symbols;
 
     const AtomString m_visibleEmptyValue;
+    TypeAhead m_typeAhead;
     int m_selectedIndex { invalidIndex };
 };
 

Modified: trunk/Source/WebCore/platform/text/PlatformLocale.cpp (266778 => 266779)


--- trunk/Source/WebCore/platform/text/PlatformLocale.cpp	2020-09-09 13:37:16 UTC (rev 266778)
+++ trunk/Source/WebCore/platform/text/PlatformLocale.cpp	2020-09-09 14:48:26 UTC (rev 266779)
@@ -354,6 +354,12 @@
     }
     return builder.toString();
 }
+
+String Locale::localizedDecimalSeparator()
+{
+    initializeLocaleData();
+    return m_decimalSymbols[DecimalSeparatorIndex];
+}
 #endif
 
 }

Modified: trunk/Source/WebCore/platform/text/PlatformLocale.h (266778 => 266779)


--- trunk/Source/WebCore/platform/text/PlatformLocale.h	2020-09-09 13:37:16 UTC (rev 266778)
+++ trunk/Source/WebCore/platform/text/PlatformLocale.h	2020-09-09 14:48:26 UTC (rev 266779)
@@ -108,6 +108,8 @@
     // localized string of January, and the last item is a localized string of
     // December. These strings should not be abbreviations.
     virtual const Vector<String>& monthLabels() = 0;
+
+    String localizedDecimalSeparator();
 #endif
 
 #if ENABLE(DATE_AND_TIME_INPUT_TYPES)
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to