Bug 1286182 - Implement the layout for <input type=date>. r=mconley,smaug

--HG--
rename : dom/html/test/forms/test_input_time_focus_blur_events.html => dom/html/test/forms/test_input_datetime_focus_blur_events.html
This commit is contained in:
Jessica Jong 2016-12-19 00:50:00 +08:00
parent 764b557d1e
commit 556aae19f0
15 changed files with 846 additions and 141 deletions

View File

@ -124,14 +124,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=753984
"Peaches", {"string": "linkAbbr"}, "Plums", {"string": "linkAbbr"},
{"string": "listAbbr"},
{"string": "cellInfoAbbr", "args": [ 1, 1]}]]
}, {
accOrElmOrID: "date",
expectedUtterance: [[{"string": "textInputType_date"},
{"string": "entry"}, "2011-09-29"], ["2011-09-29",
{"string": "textInputType_date"}, {"string": "entry"}]],
expectedBraille: [[{"string": "textInputType_date"},
{"string": "entryAbbr"}, "2011-09-29"], ["2011-09-29",
{"string": "textInputType_date"}, {"string": "entryAbbr"}]]
}, {
accOrElmOrID: "email",
expectedUtterance: [[{"string": "textInputType_email"},
@ -619,7 +611,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=753984
<label for="password">Secret Password</label><input id="password" type="password"></input>
<label for="radio_unselected">any old radio button</label><input id="radio_unselected" type="radio"></input>
<label for="radio_selected">a unique radio button</label><input id="radio_selected" type="radio" checked></input>
<input id="date" type="date" value="2011-09-29" />
<input id="email" type="email" value="test@example.com" />
<input id="search" type="search" value="This is a search" />
<input id="tel" type="tel" value="555-5555" />

View File

@ -2763,7 +2763,8 @@ HTMLInputElement::GetOwnerDateTimeControl()
HTMLInputElement::FromContentOrNull(
GetParent()->GetParent()->GetParent()->GetParent());
if (ownerDateTimeControl &&
ownerDateTimeControl->mType == NS_FORM_INPUT_TIME) {
(ownerDateTimeControl->mType == NS_FORM_INPUT_TIME ||
ownerDateTimeControl->mType == NS_FORM_INPUT_DATE)) {
return ownerDateTimeControl;
}
}
@ -3215,7 +3216,8 @@ HTMLInputElement::SetValueInternal(const nsAString& aValue, uint32_t aFlags)
if (frame) {
frame->UpdateForValueChange();
}
} else if (mType == NS_FORM_INPUT_TIME &&
} else if ((mType == NS_FORM_INPUT_TIME ||
mType == NS_FORM_INPUT_DATE) &&
!IsExperimentalMobileType(mType)) {
nsDateTimeControlFrame* frame = do_QueryFrame(GetPrimaryFrame());
if (frame) {
@ -3524,7 +3526,8 @@ HTMLInputElement::Blur(ErrorResult& aError)
}
}
if (mType == NS_FORM_INPUT_TIME && !IsExperimentalMobileType(mType)) {
if ((mType == NS_FORM_INPUT_TIME || mType == NS_FORM_INPUT_DATE) &&
!IsExperimentalMobileType(mType)) {
nsDateTimeControlFrame* frame = do_QueryFrame(GetPrimaryFrame());
if (frame) {
frame->HandleBlurEvent();
@ -3551,7 +3554,8 @@ HTMLInputElement::Focus(ErrorResult& aError)
}
}
if (mType == NS_FORM_INPUT_TIME && !IsExperimentalMobileType(mType)) {
if ((mType == NS_FORM_INPUT_TIME || mType == NS_FORM_INPUT_DATE) &&
!IsExperimentalMobileType(mType)) {
nsDateTimeControlFrame* frame = do_QueryFrame(GetPrimaryFrame());
if (frame) {
frame->HandleFocusEvent();
@ -3882,7 +3886,7 @@ HTMLInputElement::GetEventTargetParent(EventChainPreVisitor& aVisitor)
}
}
if (mType == NS_FORM_INPUT_TIME &&
if ((mType == NS_FORM_INPUT_TIME || mType == NS_FORM_INPUT_DATE) &&
!IsExperimentalMobileType(mType) &&
aVisitor.mEvent->mMessage == eFocus &&
aVisitor.mEvent->mOriginalTarget == this) {
@ -4002,7 +4006,8 @@ HTMLInputElement::GetEventTargetParent(EventChainPreVisitor& aVisitor)
// Stop the event if the related target's first non-native ancestor is the
// same as the original target's first non-native ancestor (we are moving
// inside of the same element).
if (mType == NS_FORM_INPUT_TIME && !IsExperimentalMobileType(mType) &&
if ((mType == NS_FORM_INPUT_TIME || mType == NS_FORM_INPUT_DATE) &&
!IsExperimentalMobileType(mType) &&
(aVisitor.mEvent->mMessage == eFocus ||
aVisitor.mEvent->mMessage == eFocusIn ||
aVisitor.mEvent->mMessage == eFocusOut ||
@ -7112,13 +7117,15 @@ HTMLInputElement::IsHTMLFocusable(bool aWithMouse, bool* aIsFocusable, int32_t*
if (mType == NS_FORM_INPUT_FILE ||
mType == NS_FORM_INPUT_NUMBER ||
mType == NS_FORM_INPUT_TIME) {
mType == NS_FORM_INPUT_TIME ||
mType == NS_FORM_INPUT_DATE) {
if (aTabIndex) {
// We only want our native anonymous child to be tabable to, not ourself.
*aTabIndex = -1;
}
if (mType == NS_FORM_INPUT_NUMBER ||
mType == NS_FORM_INPUT_TIME) {
mType == NS_FORM_INPUT_TIME ||
mType == NS_FORM_INPUT_DATE) {
*aIsFocusable = true;
} else {
*aIsFocusable = defaultFocusable;

View File

@ -270,8 +270,8 @@ nsIFormControl::IsSingleLineTextControl(bool aExcludePassword, uint32_t aType)
#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
// On Android/B2G, date/time input appears as a normal text box.
aType == NS_FORM_INPUT_TIME ||
#endif
aType == NS_FORM_INPUT_DATE ||
#endif
aType == NS_FORM_INPUT_MONTH ||
aType == NS_FORM_INPUT_WEEK ||
aType == NS_FORM_INPUT_DATETIME_LOCAL ||

View File

@ -30,8 +30,12 @@ skip-if = os == "android" # up/down arrow keys not supported on android
skip-if = android_version == '18' # Android, bug 1147974
[test_input_color_picker_update.html]
skip-if = android_version == '18' # Android, bug 1147974
[test_input_date_key_events.html]
skip-if = os == "android"
[test_input_datetime_focus_blur.html]
skip-if = os == "android"
[test_input_datetime_focus_blur_events.html]
skip-if = os == "android"
[test_input_datetime_tabindex.html]
skip-if = os == "android"
[test_input_defaultValue.html]
@ -61,8 +65,6 @@ skip-if = os == "android"
[test_input_textarea_set_value_no_scroll.html]
[test_input_time_key_events.html]
skip-if = os == "android"
[test_input_time_focus_blur_events.html]
skip-if = os == "android"
[test_input_types_pref.html]
[test_input_typing_sanitization.html]
[test_input_untrusted_key_events.html]

View File

@ -0,0 +1,228 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1286182
-->
<head>
<title>Test key events for time control</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<meta charset="UTF-8">
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1286182">Mozilla Bug 1286182</a>
<p id="display"></p>
<div id="content">
<input id="input" type="date">
</div>
<pre id="test">
<script type="application/javascript">
SimpleTest.waitForExplicitFinish();
// Turn off Spatial Navigation because it hijacks arrow keydown events:
SimpleTest.waitForFocus(function() {
SpecialPowers.pushPrefEnv({"set":[["snav.enabled", false]]}, function() {
test();
SimpleTest.finish();
});
});
var testData = [
/**
* keys: keys to send to the input element.
* initialVal: initial value set to the input element.
* expectedVal: expected value of the input element after sending the keys.
*/
{
// Type 11222016, default order is month, day, year.
keys: ["11222016"],
initialVal: "",
expectedVal: "2016-11-22"
},
{
// Type 3 in the month field will automatically advance to the day field,
// then type 5 in the day field will automatically advance to the year
// field.
keys: ["352016"],
initialVal: "",
expectedVal: "2016-03-05"
},
{
// Type 13 in the month field will set it to the maximum month, which is
// 12.
keys: ["13012016"],
initialVal: "",
expectedVal: "2016-12-01"
},
{
// Type 00 in the month field will set it to the minimum month, which is 1.
keys: ["00012016"],
initialVal: "",
expectedVal: "2016-01-01"
},
{
// Type 33 in the day field will set it to the maximum day, which is 31.
keys: ["12332016"],
initialVal: "",
expectedVal: "2016-12-31"
},
{
// Type 00 in the day field will set it to the minimum day, which is 1.
keys: ["12002016"],
initialVal: "",
expectedVal: "2016-12-01"
},
{
// Type 275769 in the year field will set it to the maximum year, which is
// 275760.
keys: ["0101275769"],
initialVal: "",
expectedVal: "275760-01-01"
},
{
// Type 000000 in the year field will set it to the minimum year, which is
// 0001.
keys: ["0101000000"],
initialVal: "",
expectedVal: "0001-01-01"
},
{
// Advance to year field and decrement.
keys: ["VK_TAB", "VK_TAB", "VK_DOWN"],
initialVal: "2016-11-25",
expectedVal: "2015-11-25"
},
{
// Right key should do the same thing as TAB key.
keys: ["VK_RIGHT", "VK_RIGHT", "VK_DOWN"],
initialVal: "2016-11-25",
expectedVal: "2015-11-25"
},
{
// Advance to day field then back to month field and decrement.
keys: ["VK_RIGHT", "VK_LEFT", "VK_DOWN"],
initialVal: "2000-05-01",
expectedVal: "2000-04-01"
},
{
// Focus starts on the first field, month in this case, and increment.
keys: ["VK_UP"],
initialVal: "2000-03-01",
expectedVal: "2000-04-01"
},
{
// Advance to day field and decrement.
keys: ["VK_TAB", "VK_DOWN"],
initialVal: "1234-01-01",
expectedVal: "1234-01-31"
},
{
// Advance to day field and increment.
keys: ["VK_TAB", "VK_UP"],
initialVal: "1234-01-01",
expectedVal: "1234-01-02"
},
{
// PageUp on month field increments month by 3.
keys: ["VK_PAGE_UP"],
initialVal: "1999-01-01",
expectedVal: "1999-04-01"
},
{
// PageDown on month field decrements month by 3.
keys: ["VK_PAGE_DOWN"],
initialVal: "1999-01-01",
expectedVal: "1999-10-01"
},
{
// PageUp on day field increments day by 7.
keys: ["VK_TAB", "VK_PAGE_UP"],
initialVal: "1999-01-01",
expectedVal: "1999-01-08"
},
{
// PageDown on day field decrements day by 7.
keys: ["VK_TAB", "VK_PAGE_DOWN"],
initialVal: "1999-01-01",
expectedVal: "1999-01-25"
},
{
// PageUp on year field increments year by 10.
keys: ["VK_TAB", "VK_TAB", "VK_PAGE_UP"],
initialVal: "1999-01-01",
expectedVal: "2009-01-01"
},
{
// PageDown on year field decrements year by 10.
keys: ["VK_TAB", "VK_TAB", "VK_PAGE_DOWN"],
initialVal: "1999-01-01",
expectedVal: "1989-01-01"
},
{
// Home key on month field sets it to the minimum month, which is 01.
keys: ["VK_HOME"],
initialVal: "2016-06-01",
expectedVal: "2016-01-01"
},
{
// End key on month field sets it to the maximum month, which is 12.
keys: ["VK_END"],
initialVal: "2016-06-01",
expectedVal: "2016-12-01"
},
{
// Home key on day field sets it to the minimum day, which is 01.
keys: ["VK_TAB", "VK_HOME"],
initialVal: "2016-01-10",
expectedVal: "2016-01-01"
},
{
// End key on day field sets it to the maximum day, which is 31.
keys: ["VK_TAB", "VK_END"],
initialVal: "2016-01-10",
expectedVal: "2016-01-31"
},
{
// Home key on year field sets it to the minimum year, which is 0001.
keys: ["VK_TAB", "VK_TAB", "VK_HOME"],
initialVal: "2016-01-01",
expectedVal: "0001-01-01"
},
{
// End key on year field sets it to the maximum year, which is 275760.
keys: ["VK_TAB", "VK_TAB", "VK_END"],
initialVal: "2016-01-01",
expectedVal: "275760-01-01"
},
];
function sendKeys(aKeys) {
for (let i = 0; i < aKeys.length; i++) {
let key = aKeys[i];
if (key.startsWith("VK")) {
synthesizeKey(key, {});
} else {
sendString(key);
}
}
}
function test() {
var elem = document.getElementById("input");
for (let { keys, initialVal, expectedVal } of testData) {
elem.focus();
elem.value = initialVal;
sendKeys(keys);
elem.blur();
is(elem.value, expectedVal,
"Test with " + keys + ", result should be " + expectedVal);
elem.value = "";
}
}
</script>
</pre>
</body>
</html>

View File

@ -4,7 +4,7 @@
https://bugzilla.mozilla.org/show_bug.cgi?id=1288591
-->
<head>
<title>Test focus/blur behaviour for &lt;input type='time'&gt;</title>
<title>Test focus/blur behaviour for date/time input types</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
@ -12,7 +12,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1288591
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1288591">Mozilla Bug 1288591</a>
<p id="display"></p>
<div id="content">
<input id="input" type="time">
<input id="input_time" type="time">
<input id="input_date" type="date">
</div>
<pre id="test">
<script type="application/javascript">
@ -30,15 +31,15 @@ SimpleTest.waitForFocus(function() {
SimpleTest.finish();
});
function test() {
let time = document.getElementById("input");
time.focus();
function testFocusBlur(type) {
let input = document.getElementById("input_" + type);
input.focus();
// The active element returns the input type=time.
// The active element returns the date/time input element.
let activeElement = document.activeElement;
is(activeElement, time, "activeElement should be the time element");
is(activeElement, input, "activeElement should be the date/time input element");
is(activeElement.localName, "input", "activeElement should be an input element");
is(activeElement.type, "time", "activeElement should be of type time");
is(activeElement.type, type, "activeElement should be of type " + type);
// Use FocusManager to check that the actual focus is on the anonymous
// text control.
@ -48,10 +49,17 @@ function test() {
is(focusedElement.localName, "input", "focusedElement should be an input element");
is(focusedElement.type, "text", "focusedElement should be of type text");
time.blur();
isnot(document.activeElement, time, "activeElement should no longer be the time element");
input.blur();
isnot(document.activeElement, input, "activeElement should no longer be the datetime input element");
}
function test() {
let inputTypes = ["time", "date"];
for (let i = 0; i < inputTypes.length; i++) {
testFocusBlur(inputTypes[i]);
}
}
</script>
</pre>
</body>

View File

@ -0,0 +1,90 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1301306
-->
<head>
<title>Test for Bug 1301306</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1301306">Mozilla Bug 722599</a>
<p id="display"></p>
<div id="content">
<input type="time" id="input_time" onfocus="++focusEvents[0]"
onblur="++blurEvents[0]" onfocusin="++focusInEvents[0]"
onfocusout="++focusOutEvents[0]">
<input type="date" id="input_date" onfocus="++focusEvents[1]"
onblur="++blurEvents[1]" onfocusin="++focusInEvents[1]"
onfocusout="++focusOutEvents[1]">
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
/**
* Test for Bug 1301306.
* This test checks that when moving inside the time input element, e.g. jumping
* through the inner text boxes, does not fire extra focus/blur events.
**/
var inputTypes = ["time", "date"];
var focusEvents = [0, 0];
var focusInEvents = [0, 0];
var focusOutEvents = [0, 0];
var blurEvents = [0, 0];
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(function() {
test();
SimpleTest.finish();
});
function test() {
for (var i = 0; i < inputTypes.length; i++) {
var input = document.getElementById("input_" + inputTypes[i]);
input.focus();
is(focusEvents[i], 1, inputTypes[i] + " input element should have dispatched focus event.");
is(focusInEvents[i], 1, inputTypes[i] + " input element should have dispatched focusin event.");
is(focusOutEvents[i], 0, inputTypes[i] + " input element should not have dispatched focusout event.");
is(blurEvents[i], 0, inputTypes[i] + " input element should not have dispatched blur event.");
// Move around inside the input element's input box.
synthesizeKey("VK_TAB", {});
is(focusEvents[i], 1, inputTypes[i] + " input element should not have dispatched focus event.");
is(focusInEvents[i], 1, inputTypes[i] + " input element should not have dispatched focusin event.");
is(focusOutEvents[i], 0, inputTypes[i] + " input element should not have dispatched focusout event.");
is(blurEvents[i], 0, inputTypes[i] + " time input element should not have dispatched blur event.");
synthesizeKey("VK_RIGHT", {});
is(focusEvents[i], 1, inputTypes[i] + " input element should not have dispatched focus event.");
is(focusInEvents[i], 1, inputTypes[i] + " input element should not have dispatched focusin event.");
is(focusOutEvents[i], 0, inputTypes[i] + " input element should not have dispatched focusout event.");
is(blurEvents[i], 0, inputTypes[i] + " input element should not have dispatched blur event.");
synthesizeKey("VK_LEFT", {});
is(focusEvents[i], 1,inputTypes[i] + " input element should not have dispatched focus event.");
is(focusInEvents[i], 1, inputTypes[i] + " input element should not have dispatched focusin event.");
is(focusOutEvents[i], 0, inputTypes[i] + " input element should not have dispatched focusout event.");
is(blurEvents[i], 0, inputTypes[i] + " input element should not have dispatched blur event.");
synthesizeKey("VK_RIGHT", {});
is(focusEvents[i], 1, inputTypes[i] + " input element should not have dispatched focus event.");
is(focusInEvents[i], 1, inputTypes[i] + " input element should not have dispatched focusin event.");
is(focusOutEvents[i], 0, inputTypes[i] + " input element should not have dispatched focusout event.");
is(blurEvents[i], 0, inputTypes[i] + " input element should not have dispatched blur event.");
input.blur();
is(focusEvents[i], 1, inputTypes[i] + " input element should not have dispatched focus event.");
is(focusInEvents[i], 1, inputTypes[i] + " input element should not have dispatched focusin event.");
is(focusOutEvents[i], 1, inputTypes[i] + " input element should have dispatched focusout event.");
is(blurEvents[i], 1, inputTypes[i] + " input element should have dispatched blur event.");
}
}
</script>
</pre>
</body>
</html>

View File

@ -4,7 +4,7 @@
https://bugzilla.mozilla.org/show_bug.cgi?id=1288591
-->
<head>
<title>Test tabindex attribute for &lt;input type='time'&gt;</title>
<title>Test tabindex attribute for date/time input types</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
@ -16,13 +16,16 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1288591
<input id="time1" type="time" tabindex="0">
<input id="time2" type="time" tabindex="-1">
<input id="time3" type="time" tabindex="0">
<input id="date1" type="date" tabindex="0">
<input id="date2" type="date" tabindex="-1">
<input id="date3" type="date" tabindex="0">
</div>
<pre id="test">
<script type="application/javascript">
/**
* Test for Bug 1288591.
* This test checks whether date/time input types' tabindex attribute works
* This test checks whether date/time input types tabindex attribute works
* correctly.
**/
SimpleTest.waitForExplicitFinish();
@ -31,41 +34,49 @@ SimpleTest.waitForFocus(function() {
SimpleTest.finish();
});
function test() {
let time1 = document.getElementById("time1");
let time2 = document.getElementById("time2");
let time3 = document.getElementById("time3");
function testTabindex(type) {
let input1 = document.getElementById(type + "1");
let input2 = document.getElementById(type + "2");
let input3 = document.getElementById(type + "3");
time1.focus();
is(document.activeElement, time1,
input1.focus();
is(document.activeElement, input1,
"input element with tabindex=0 is focusable");
// Advance to time1 minute field
// Advance to next inner field
synthesizeKey("VK_TAB", {});
is(document.activeElement, time1,
is(document.activeElement, input1,
"input element with tabindex=0 is tabbable");
// Advance to time1 AM/PM field
// Advance to next inner field
synthesizeKey("VK_TAB", {});
is(document.activeElement, time1,
is(document.activeElement, input1,
"input element with tabindex=0 is tabbable");
// Advance to next element
synthesizeKey("VK_TAB", {});
is(document.activeElement, time3,
is(document.activeElement, input3,
"input element with tabindex=-1 is not tabbable");
time2.focus();
is(document.activeElement, time2,
input2.focus();
is(document.activeElement, input2,
"input element with tabindex=-1 is still focusable");
// Changing the tabindex attribute dynamically.
time3.setAttribute("tabindex", "-1");
synthesizeKey("VK_TAB", {}); // need only one TAB since time2 is not tabbable
isnot(document.activeElement, time3,
input3.setAttribute("tabindex", "-1");
synthesizeKey("VK_TAB", {}); // need only one TAB since input2 is not tabbable
isnot(document.activeElement, input3,
"element with tabindex changed to -1 should not be tabbable");
}
function test() {
let inputTypes = ["time", "date"];
for (let i = 0; i < inputTypes.length; i++) {
testTabindex(inputTypes[i]);
}
}
</script>
</pre>
</body>

View File

@ -1,82 +0,0 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1301306
-->
<head>
<title>Test for Bug 1301306</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1301306">Mozilla Bug 722599</a>
<p id="display"></p>
<div id="content">
<input type="time" id="input_time" onfocus="++focusEvent" onblur="++blurEvent"
onfocusin="++focusInEvent" onfocusout="++focusOutEvent">
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
/**
* Test for Bug 1301306.
* This test checks that when moving inside the time input element, e.g. jumping
* through the inner text boxes, does not fire extra focus/blur events.
**/
var focusEvent = 0;
var focusInEvent = 0;
var focusOutEvent = 0;
var blurEvent = 0;
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(function() {
test();
SimpleTest.finish();
});
function test() {
var time = document.getElementById("input_time");
time.focus();
is(focusEvent, 1, "time input element should have dispatched focus event.");
is(focusInEvent, 1, "time input element should have dispatched focusin event.");
is(focusOutEvent, 0, "time input element should not have dispatched focusout event.");
is(blurEvent, 0, "time input element should not have dispatched blur event.");
// Move around inside the input element's input box.
synthesizeKey("VK_TAB", {});
is(focusEvent, 1, "time input element should not have dispatched focus event.");
is(focusInEvent, 1, "time input element should have dispatched focusin event.");
is(focusOutEvent, 0, "time input element should not have dispatched focusout event.");
is(blurEvent, 0, "time input element should not have dispatched blur event.");
synthesizeKey("VK_RIGHT", {});
is(focusEvent, 1, "time input element should not have dispatched focus event.");
is(focusInEvent, 1, "time input element should have dispatched focusin event.");
is(focusOutEvent, 0, "time input element should not have dispatched focusout event.");
is(blurEvent, 0, "time input element should not have dispatched blur event.");
synthesizeKey("VK_LEFT", {});
is(focusEvent, 1, "time input element should not have dispatched focus event.");
is(focusInEvent, 1, "time input element should have dispatched focusin event.");
is(focusOutEvent, 0, "time input element should not have dispatched focusout event.");
is(blurEvent, 0, "time input element should not have dispatched blur event.");
synthesizeKey("VK_RIGHT", {});
is(focusEvent, 1, "time input element should not have dispatched focus event.");
is(focusInEvent, 1, "time input element should have dispatched focusin event.");
is(focusOutEvent, 0, "time input element should not have dispatched focusout event.");
is(blurEvent, 0, "time input element should not have dispatched blur event.");
time.blur();
is(focusEvent, 1, "time input element should not have dispatched focus event.");
is(focusInEvent, 1, "time input element should have dispatched focusin event.");
is(focusOutEvent, 1, "time input element should not have dispatched focusout event.");
is(blurEvent, 1, "time input element should have dispatched blur event.");
}
</script>
</pre>
</body>
</html>

View File

@ -26,6 +26,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=765772
* This test checks that when a user types in some input types, it will not be
* in a state where the value will be un-sanitized and usable (by a script).
*/
const isDesktop = !/Mobile|Tablet/.test(navigator.userAgent);
var input = document.getElementById('i');
var form = document.getElementById('f');
@ -138,6 +139,7 @@ function* runTest()
]
},
{
mobileOnly: true,
type: 'date',
validData: [
'0001-01-01',
@ -155,6 +157,28 @@ function* runTest()
'1000-12-99',
]
},
{
mobileOnly: true,
type: 'time',
validData: [
'00:00',
'09:09:00',
'08:23:23.1',
'21:43:56.12',
'23:12:45.100',
],
invalidData: [
'00:',
'00:00:',
'25:00',
'-00:00',
'00:00:00.',
'00:60',
'10:58:99',
':19:10',
'23:08:09.1012',
]
},
{
type: 'month',
validData: [
@ -213,6 +237,10 @@ function* runTest()
for (test of data) {
gCurrentTest = test;
if (gCurrentTest.mobileOnly && isDesktop) {
continue;
}
input.type = test.type;
gValidData = test.validData;
gInvalidData = test.invalidData;

View File

@ -3664,13 +3664,13 @@ nsCSSFrameConstructor::FindInputData(Element* aElement,
nsCSSAnonBoxes::buttonContent) },
// TODO: this is temporary until a frame is written: bug 635240.
SIMPLE_INT_CREATE(NS_FORM_INPUT_NUMBER, NS_NewNumberControlFrame),
// TODO: this is temporary until a frame is written: bug 888320.
SIMPLE_INT_CREATE(NS_FORM_INPUT_DATE, NS_NewTextControlFrame),
#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
// On Android/B2G, date/time input appears as a normal text box.
SIMPLE_INT_CREATE(NS_FORM_INPUT_TIME, NS_NewTextControlFrame),
SIMPLE_INT_CREATE(NS_FORM_INPUT_DATE, NS_NewTextControlFrame),
#else
SIMPLE_INT_CREATE(NS_FORM_INPUT_TIME, NS_NewDateTimeControlFrame),
SIMPLE_INT_CREATE(NS_FORM_INPUT_DATE, NS_NewDateTimeControlFrame),
#endif
// TODO: this is temporary until a frame is written: bug 888320
SIMPLE_INT_CREATE(NS_FORM_INPUT_MONTH, NS_NewTextControlFrame),

View File

@ -372,7 +372,8 @@ nsDateTimeControlFrame::AttributeChanged(int32_t aNameSpaceID,
auto contentAsInputElem = static_cast<dom::HTMLInputElement*>(mContent);
// If script changed the <input>'s type before setting these attributes
// then we don't need to do anything since we are going to be reframed.
if (contentAsInputElem->GetType() == NS_FORM_INPUT_TIME) {
if (contentAsInputElem->GetType() == NS_FORM_INPUT_TIME ||
contentAsInputElem->GetType() == NS_FORM_INPUT_DATE) {
if (aAttribute == nsGkAtoms::value) {
nsCOMPtr<nsIDateTimeInputArea> inputAreaContent =
do_QueryInterface(mInputAreaContent);

View File

@ -776,6 +776,11 @@ input[type="time"] > xul|datetimebox {
-moz-binding: url("chrome://global/content/bindings/datetimebox.xml#time-input");
}
input[type="date"] > xul|datetimebox {
display: flex;
-moz-binding: url("chrome://global/content/bindings/datetimebox.xml#date-input");
}
/* details & summary */
details > summary:first-of-type,
details > summary:-moz-native-anonymous {

View File

@ -172,7 +172,7 @@ function setupFormHistory(aCallback) {
{ op : "add", fieldname : "field8", value : "value" },
{ op : "add", fieldname : "field9", value : "value" },
{ op : "add", fieldname : "field10", value : "42" },
{ op : "add", fieldname : "field11", value : "2010-10-10" },
{ op : "add", fieldname : "field11", value : "2010-10-10" }, // not used, since type=date doesn't have autocomplete currently
{ op : "add", fieldname : "field12", value : "21:21" }, // not used, since type=time doesn't have autocomplete currently
{ op : "add", fieldname : "field13", value : "32" }, // not used, since type=range doesn't have a drop down menu
{ op : "add", fieldname : "field14", value : "#ffffff" }, // not used, since type=color doesn't have autocomplete currently
@ -899,15 +899,13 @@ function runTest() {
input = $_(14, "field11");
restoreForm();
expectPopup();
doKey("down");
waitForMenuChange(0);
break;
case 405:
checkMenuEntries(["2010-10-10"]);
doKey("down");
doKey("return");
checkForm("2010-10-10");
checkMenuEntries([]); // type=date with it's own control frame does not
// have a drop down menu for now
checkForm("");
input = $_(15, "field12");
restoreForm();

View File

@ -10,6 +10,405 @@
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:xbl="http://www.mozilla.org/xbl">
<binding id="date-input"
extends="chrome://global/content/bindings/datetimebox.xml#datetime-input-base">
<resources>
<stylesheet src="chrome://global/content/textbox.css"/>
<stylesheet src="chrome://global/skin/textbox.css"/>
<stylesheet src="chrome://global/content/bindings/datetimebox.css"/>
</resources>
<implementation>
<constructor>
<![CDATA[
// TODO: Bug 1320227 - [DateTimeInput] localization for
// <input type=date> input box
this.mMonthPlaceHolder = "mm";
this.mDayPlaceHolder = "dd";
this.mYearPlaceHolder = "yyyy";
this.mSeparatorText = "/";
this.mMinMonth = 1;
this.mMaxMonth = 12;
this.mMinDay = 1;
this.mMaxDay = 31;
this.mMinYear = 1;
// Maximum year limited by ECMAScript date object range, year <= 275760.
this.mMaxYear = 275760;
this.mMonthDayLength = 2;
this.mYearLength = 4;
this.mMonthPageUpDownInterval = 3;
this.mDayPageUpDownInterval = 7;
this.mYearPageUpDownInterval = 10;
// Default to en-US, month-day-year order.
this.mMonthField =
document.getAnonymousElementByAttribute(this, "anonid", "input-one");
this.mDayField =
document.getAnonymousElementByAttribute(this, "anonid", "input-two");
this.mYearField =
document.getAnonymousElementByAttribute(this, "anonid", "input-three");
this.mYearField.size = this.mYearLength;
this.mYearField.maxLength = this.mMaxYear.toString().length;
this.mMonthField.placeholder = this.mMonthPlaceHolder;
this.mDayField.placeholder = this.mDayPlaceHolder;
this.mYearField.placeholder = this.mYearPlaceHolder;
this.mMonthField.setAttribute("min", this.mMinMonth);
this.mMonthField.setAttribute("max", this.mMaxMonth);
this.mMonthField.setAttribute("pginterval",
this.mMonthPageUpDownInterval);
this.mDayField.setAttribute("min", this.mMinDay);
this.mDayField.setAttribute("max", this.mMaxDay);
this.mDayField.setAttribute("pginterval", this.mDayPageUpDownInterval);
this.mYearField.setAttribute("min", this.mMinYear);
this.mYearField.setAttribute("max", this.mMaxYear);
this.mYearField.setAttribute("pginterval",
this.mYearPageUpDownInterval);
this.mDaySeparator =
document.getAnonymousElementByAttribute(this, "anonid", "sep-first");
this.mDaySeparator.textContent = this.mSeparatorText;
this.mYearSeparator =
document.getAnonymousElementByAttribute(this, "anonid", "sep-second");
this.mYearSeparator.textContent = this.mSeparatorText;
if (this.mInputElement.value) {
this.setFieldsFromInputValue();
}
]]>
</constructor>
<method name="clearInputFields">
<parameter name="aFromInputElement"/>
<body>
<![CDATA[
this.log("clearInputFields");
if (this.isDisabled() || this.isReadonly()) {
return;
}
if (this.mMonthField && !this.mMonthField.disabled &&
!this.mMonthField.readOnly) {
this.mMonthField.value = "";
this.mMonthField.setAttribute("typeBuffer", "");
}
if (this.mDayField && !this.mDayField.disabled &&
!this.mDayField.readOnly) {
this.mDayField.value = "";
this.mDayField.setAttribute("typeBuffer", "");
}
if (this.mYearField && !this.mYearField.disabled &&
!this.mYearField.readOnly) {
this.mYearField.value = "";
this.mYearField.setAttribute("typeBuffer", "");
}
if (!aFromInputElement) {
this.mInputElement.setUserInput("");
}
]]>
</body>
</method>
<method name="setFieldsFromInputValue">
<body>
<![CDATA[
let value = this.mInputElement.value;
if (!value) {
this.clearInputFields(true);
return;
}
this.log("setFieldsFromInputValue: " + value);
let [year, month, day] = value.split("-");
this.setFieldValue(this.mYearField, year);
this.setFieldValue(this.mMonthField, month);
this.setFieldValue(this.mDayField, day);
this.notifyPicker();
]]>
</body>
</method>
<method name="getDaysInMonth">
<parameter name="aMonth"/>
<parameter name="aYear"/>
<body>
<![CDATA[
// Javascript's month is 0-based, so this means last day of the
// previous month.
return new Date(aYear, aMonth, 0).getDate();
]]>
</body>
</method>
<method name="isFieldInvalid">
<parameter name="aField"/>
<body>
<![CDATA[
if (this.isEmpty(aField.value)) {
return true;
}
let min = Number(aField.getAttribute("min"));
let max = Number(aField.getAttribute("max"));
if (Number(aField.value) < min || Number(aField.value) > max) {
return true;
}
return false;
]]>
</body>
</method>
<method name="setInputValueFromFields">
<body>
<![CDATA[
if (this.isFieldInvalid(this.mYearField) ||
this.isFieldInvalid(this.mMonthField) ||
this.isFieldInvalid(this.mDayField)) {
// We still need to notify picker in case any of the field has
// changed. If we can set input element value, then notifyPicker
// will be called in setFieldsFromInputValue().
this.notifyPicker();
return;
}
let year = this.mYearField.value;
let month = this.mMonthField.value;
let day = this.mDayField.value;
if (day > this.getDaysInMonth(month, year)) {
// Don't set invalid date, otherwise input element's value will be
// set to empty.
return;
}
let date = [year, month, day].join("-");
this.log("setInputValueFromFields: " + date);
this.mInputElement.setUserInput(date);
]]>
</body>
</method>
<method name="setFieldsFromPicker">
<body>
<![CDATA[
// TODO: Bug 1320225 - [DateTimeInput] Integration of input type=date
// input box with picker.
]]>
</body>
</method>
<method name="handleKeypress">
<parameter name="aEvent"/>
<body>
<![CDATA[
if (this.isDisabled() || this.isReadonly()) {
return;
}
let targetField = aEvent.originalTarget;
let key = aEvent.key;
if (targetField.classList.contains("numeric") && key.match(/[0-9]/)) {
let buffer = targetField.getAttribute("typeBuffer") || "";
buffer = buffer.concat(key);
this.setFieldValue(targetField, buffer);
targetField.select();
let n = Number(buffer);
let max = targetField.getAttribute("max");
if (buffer.length >= targetField.maxLength || n * 10 > max) {
buffer = "";
this.advanceToNextField();
}
targetField.setAttribute("typeBuffer", buffer);
}
]]>
</body>
</method>
<method name="incrementFieldValue">
<parameter name="aTargetField"/>
<parameter name="aTimes"/>
<body>
<![CDATA[
let value;
// Use current date if field is empty.
if (this.isEmpty(aTargetField.value)) {
let now = new Date();
if (aTargetField == this.mYearField) {
value = now.getFullYear();
} else if (aTargetField == this.mMonthField) {
value = now.getMonth() + 1;
} else if (aTargetField == this.mDayField) {
value = now.getDate();
} else {
this.log("Field not supported in incrementFieldValue.");
return;
}
} else {
value = Number(aTargetField.value);
}
let min = Number(aTargetField.getAttribute("min"));
let max = Number(aTargetField.getAttribute("max"));
value += Number(aTimes);
if (value > max) {
value -= (max - min + 1);
} else if (value < min) {
value += (max - min + 1);
}
this.setFieldValue(aTargetField, value);
aTargetField.select();
]]>
</body>
</method>
<method name="handleKeyboardNav">
<parameter name="aEvent"/>
<body>
<![CDATA[
if (this.isDisabled() || this.isReadonly()) {
return;
}
let targetField = aEvent.originalTarget;
let key = aEvent.key;
switch (key) {
case "ArrowUp":
this.incrementFieldValue(targetField, 1);
break;
case "ArrowDown":
this.incrementFieldValue(targetField, -1);
break;
case "PageUp": {
let interval = targetField.getAttribute("pginterval");
this.incrementFieldValue(targetField, interval);
break;
}
case "PageDown": {
let interval = targetField.getAttribute("pginterval");
this.incrementFieldValue(targetField, 0 - interval);
break;
}
case "Home":
let min = targetField.getAttribute("min");
this.setFieldValue(targetField, min);
targetField.select();
break;
case "End":
let max = targetField.getAttribute("max");
this.setFieldValue(targetField, max);
targetField.select();
break;
}
this.setInputValueFromFields();
]]>
</body>
</method>
<method name="getCurrentValue">
<body>
<![CDATA[
let year;
if (!this.isEmpty(this.mYearField.value)) {
year = Number(this.mYearField.value);
}
let month;
if (!this.isEmpty(this.mMonthField.value)) {
month = Number(this.mMonthField.value);
}
let day;
if (!this.isEmpty(this.mDayField.value)) {
day = Number(this.mDayField.value);
}
let date = { year, month, day };
this.log("getCurrentValue: " + JSON.stringify(date));
return date;
]]>
</body>
</method>
<method name="setFieldValue">
<parameter name="aField"/>
<parameter name="aValue"/>
<body>
<![CDATA[
let value = Number(aValue);
if (isNaN(value)) {
this.log("NaN on setFieldValue!");
return;
}
if (aValue.length == aField.maxLength) {
let min = Number(aField.getAttribute("min"));
let max = Number(aField.getAttribute("max"));
if (aValue < min) {
value = min;
} else if (aValue > max) {
value = max;
}
}
if (aField == this.mMonthField ||
aField == this.mDayField) {
// prepend zero
if (value < 10) {
value = "0" + value;
}
} else {
// prepend zeroes
if (value < 10) {
value = "000" + value;
} else if (value < 100) {
value = "00" + value;
} else if (value < 1000) {
value = "0" + value;
}
if (value.toString().length > this.mYearLength &&
value.toString().length <= this.mMaxYear.toString().length) {
this.mYearField.size = value.toString().length;
}
}
aField.value = value;
]]>
</body>
</method>
<method name="isValueAvailable">
<body>
<![CDATA[
return !this.isEmpty(this.mMonthField.value) ||
!this.isEmpty(this.mDayField.value) ||
!this.isEmpty(this.mYearField.value);
]]>
</body>
</method>
</implementation>
</binding>
<binding id="time-input"
extends="chrome://global/content/bindings/datetimebox.xml#datetime-input-base">
<resources>
@ -745,6 +1144,12 @@
</body>
</method>
<method name="getCurrentValue">
<body>
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
</body>
</method>
<method name="notifyPicker">
<body>
<![CDATA[
@ -791,7 +1196,7 @@
break;
}
case "blur": {
this.setInputValueFromFields();
this.onBlur(aEvent);
break;
}
case "copy":
@ -822,6 +1227,19 @@
</body>
</method>
<method name="onBlur">
<parameter name="aEvent"/>
<body>
<![CDATA[
this.log("onBlur originalTarget: " + aEvent.originalTarget);
let target = aEvent.originalTarget;
target.setAttribute("typeBuffer", "");
this.setInputValueFromFields();
]]>
</body>
</method>
<method name="onKeyPress">
<parameter name="aEvent"/>
<body>