Bug 1446722: set default value for 'mFocusedValue' for the html input elements 'date' and 'time'. r=smaug

In order to trigger the 'onchange' event when resetting the 'date' or
'time' html input elements.

Differential Revision: https://phabricator.services.mozilla.com/D24206

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Mirko Brodesser 2019-03-22 10:52:06 +00:00
Родитель 033681c09c
Коммит 7558681ee9
5 изменённых файлов: 206 добавлений и 5 удалений

Просмотреть файл

@ -5662,6 +5662,7 @@ nsresult HTMLInputElement::SetDefaultValueAsValue() {
GetDefaultValue(resetVal);
// SetValueInternal is going to sanitize the value.
// TODO(mbrodesser): sanitizing will only happen if `mDoneCreating` is true.
return SetValueInternal(resetVal, nsTextEditorState::eSetValue_Internal);
}
@ -5673,6 +5674,14 @@ void HTMLInputElement::SetDirectionFromValue(bool aNotify) {
}
}
namespace {
bool IsDateOrTime(uint8_t aType) {
return (aType == NS_FORM_INPUT_DATE) || (aType == NS_FORM_INPUT_TIME);
}
} // namespace
NS_IMETHODIMP
HTMLInputElement::Reset() {
// We should be able to reset all dirty flags regardless of the type.
@ -5681,8 +5690,15 @@ HTMLInputElement::Reset() {
mLastValueChangeWasInteractive = false;
switch (GetValueMode()) {
case VALUE_MODE_VALUE:
return SetDefaultValueAsValue();
case VALUE_MODE_VALUE: {
nsresult result = SetDefaultValueAsValue();
if (IsDateOrTime(mType)) {
// mFocusedValue has to be set here, so that `FireChangeEventIfNeeded`
// can fire a change event if necessary.
GetValue(mFocusedValue, CallerType::System);
}
return result;
}
case VALUE_MODE_DEFAULT_ON:
DoSetChecked(DefaultChecked(), true, false);
return NS_OK;
@ -5917,7 +5933,7 @@ void HTMLInputElement::DoneCreatingElement() {
DoSetChecked(DefaultChecked(), false, false);
}
// Sanitize the value.
// Sanitize the value and potentially set mFocusedValue.
if (GetValueMode() == VALUE_MODE_VALUE) {
nsAutoString aValue;
GetValue(aValue, CallerType::System);
@ -5925,6 +5941,12 @@ void HTMLInputElement::DoneCreatingElement() {
// may potentially be big, but most likely we've failed to allocate
// before the type change.)
SetValueInternal(aValue, nsTextEditorState::eSetValue_Internal);
if (IsDateOrTime(mType)) {
// mFocusedValue has to be set here, so that `FireChangeEventIfNeeded` can
// fire a change event if necessary.
mFocusedValue = aValue;
}
}
mShouldInitChecked = false;

Просмотреть файл

@ -412,7 +412,9 @@ class HTMLInputElement final : public nsGenericHTMLFormElementWithState,
void UpdateValidityUIBits(bool aIsFocused);
/**
* Fires change event if mFocusedValue and current value held are unequal.
* Fires change event if mFocusedValue and current value held are unequal and
* if a change event may be fired on bluring.
* Sets mFocusedValue to value, if a change event is fired.
*/
void FireChangeEventIfNeeded();
@ -1465,7 +1467,8 @@ class HTMLInputElement final : public nsGenericHTMLFormElementWithState,
* when the element is either changed through a script, focused or dispatches
* a change event. This is to ensure correct future change event firing.
* NB: This is ONLY applicable where the element is a text control. ie,
* where type= "text", "email", "search", "tel", "url" or "password".
* where type= "date", "time", "text", "email", "search", "tel", "url" or
* "password".
*/
nsString mFocusedValue;

Просмотреть файл

@ -2,6 +2,7 @@
support-files =
save_restore_radio_groups.sjs
test_input_number_data.js
utils.js
!/dom/html/test/reflect.js
FAIL.html
PASS.html
@ -39,6 +40,7 @@ skip-if = android_version == '18' # Android, bug 1147974
[test_input_datetime_focus_blur_events.html]
[test_input_datetime_focus_state.html]
[test_input_datetime_hidden.html]
[test_input_datetime_reset_default_value_input_change_event.html]
[test_input_datetime_tabindex.html]
[test_input_defaultValue.html]
[test_input_email.html]

Просмотреть файл

@ -0,0 +1,153 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1446722
-->
<head>
<title>Test for bug 1446722</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script type="application/javascript" src="utils.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=1446722">Mozilla bug 1446722</a>
<p id="display"></p>
<div id="content">
<form>
<input type="time" id="input_time" value="10:30" onchange="++numberChangeEvents"
oninput="++numberInputEvents">
<input type="date" id="input_date" value="2012-05-06" onchange="++numberChangeEvents"
oninput="++numberInputEvents">
<input type="time" id="input_time2" value="11:30" onchange="++numberChangeEvents"
oninput="++numberInputEvents">
<input type="date" id="input_date2" value="2014-07-08"
onchange="++numberChangeEvents"
oninput="++numberInputEvents">
<input type="time" id="input_time3" value="12:30" onchange="++numberChangeEvents"
oninput="++numberInputEvents">
<input type="date" id="input_date3" value="2014-08-09"
onchange="++numberChangeEvents"
oninput="++numberInputEvents">
<input type="reset" id="input_reset">
</form>
</div>
<pre id="test">
<script class="testbody" type="application/javascript">
/**
* Test for bug 1446722.
*
* Test change and input events are fired for date and time inputs when the
* default value is reset from the date UI and the time UI.
* Test they are not fired when the value is changed via a script.
* Test clicking the reset button of a form does not fire these events.
**/
const INPUT_FIELD_ID_PREFIX = "input_";
var numberChangeEvents = 0;
var numberInputEvents = 0;
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(function() {
if (isDesktopUserAgent(navigator)) {
test_reset_in_ui_triggers_change_and_input_event(
"time", numberChangeEvents, numberInputEvents);
test_reset_in_ui_triggers_change_and_input_event(
"date", numberChangeEvents, numberInputEvents);
}
test_reset_in_script_does_not_trigger_change_and_input_event(
"time2", numberChangeEvents, numberInputEvents);
test_reset_in_script_does_not_trigger_change_and_input_event(
"date2", numberChangeEvents, numberInputEvents);
test_reset_form_does_not_trigger_change_and_input_events("time3", "14:00",
numberChangeEvents, numberInputEvents);
test_reset_form_does_not_trigger_change_and_input_events("date3", "2016-01-01",
numberChangeEvents, numberInputEvents);
SimpleTest.finish();
});
function test_reset_in_ui_triggers_change_and_input_event(
inputFieldIdSuffix, oldNumberChangeEvents, oldNumberInputEvents) {
const inputFieldName = INPUT_FIELD_ID_PREFIX + inputFieldIdSuffix;
var input = document.getElementById(inputFieldName);
is(input.value, input.defaultValue,
"Check " + inputFieldName + "'s default value is initialized correctly.");
is(numberChangeEvents, oldNumberChangeEvents,
"Check numberChangeEvents is initialized correctly for " + inputFieldName +
".");
is(numberInputEvents, oldNumberInputEvents,
"Check numberInputEvents is initialized correctly for " + inputFieldName +
".");
simulateUserClicksResetButton(input);
is(input.value, "",
"Check " + inputFieldName + "'s value was set correctly.");
is(numberChangeEvents, oldNumberChangeEvents + 1,
"Change event should be dispatched for " + inputFieldName + ".");
is(numberInputEvents, oldNumberInputEvents + 1,
"Input event should be dispatched for " + inputFieldName + ".");
}
function test_reset_in_script_does_not_trigger_change_and_input_event(
inputFieldIdSuffix, oldNumberChangeEvents, oldNumberInputEvents) {
const inputFieldName = INPUT_FIELD_ID_PREFIX + inputFieldIdSuffix;
var input = document.getElementById(inputFieldName);
is(input.value, input.defaultValue,
"Check " + inputFieldName + "'s default value is initialized correctly.");
is(numberChangeEvents, oldNumberChangeEvents,
"Check numberChangeEvents is initialized correctly for " + inputFieldName +
".");
is(numberInputEvents, oldNumberInputEvents,
"Check numberInputEvents is initialized correctly for " + inputFieldName +
".");
input.value = "";
is(numberChangeEvents, oldNumberChangeEvents,
"Change event should not be dispatched for " + inputFieldName + ".");
is(numberInputEvents, oldNumberInputEvents,
"Input event should not be dispatched for " + inputFieldName + ".");
}
function test_reset_form_does_not_trigger_change_and_input_events(
inputFieldIdSuffix, newValue, oldNumberChangeEvents, oldNumberInputEvents) {
const inputFieldName = INPUT_FIELD_ID_PREFIX + inputFieldIdSuffix;
const inputFieldResetButtonName = "input_reset";
var input = document.getElementById(inputFieldName);
is(input.value, input.defaultValue,
"Check " + inputFieldName + "'s default value is initialized correctly.");
isnot(input.defaultValue, newValue, "Check default value differs from newValue for " +
inputFieldName + ".");
is(numberChangeEvents, oldNumberChangeEvents,
"Check numberChangeEvents is initialized correctly for " + inputFieldName +
".");
is(numberInputEvents, oldNumberInputEvents,
"Check numberInputEvents is initialized correctly for " + inputFieldName +
".");
input.value = newValue;
var resetButton = document.getElementById(inputFieldResetButtonName);
synthesizeMouseAtCenter(resetButton, {});
is(input.value, input.defaultValue, "Check value is reset to default for " +
inputFieldName + ".");
is(numberChangeEvents, oldNumberChangeEvents,
"Change event should not be dispatched for " + inputFieldResetButtonName + ".");
is(numberInputEvents, oldNumberInputEvents,
"Input event should not be dispatched for " + inputFieldResetButtonName + ".");
}
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -0,0 +1,21 @@
/**
* Simulate the user clicks the reset button of the given date or time element.
*
* @param inputElement A date or time input element of default size.
*/
function simulateUserClicksResetButton(inputElement) {
var inputRectangle = inputElement.getBoundingClientRect();
const offsetX = inputRectangle.width - 15;
const offsetY = inputRectangle.height / 2;
synthesizeMouse(inputElement, offsetX, offsetY, {});
}
/**
* @param navigator https://www.w3schools.com/jsref/obj_navigator.asp.
* @return true, iff it's a desktop user agent.
*/
function isDesktopUserAgent(navigator) {
return !/Mobile|Tablet/.test(navigator.userAgent);
}