Bug 945784, part 1 - Fire 'input' events for <input type=number> more frequently, per the new HTML5 rules. r=smaug

This commit is contained in:
Jonathan Watt 2013-12-05 16:20:33 +00:00
Родитель 50ab3e3db1
Коммит a3390898c6
4 изменённых файлов: 152 добавлений и 37 удалений

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

@ -2603,7 +2603,7 @@ HTMLInputElement::HandleNumberControlSpin(void* aData)
// anything else. // anything else.
input->StopNumberControlSpinnerSpin(); input->StopNumberControlSpinnerSpin();
} else { } else {
input->ApplyStep(input->mNumberControlSpinnerSpinsUp ? 1 : -1); input->StepNumberControlForUserEvent(input->mNumberControlSpinnerSpinsUp ? 1 : -1);
} }
} }
@ -3498,9 +3498,21 @@ HTMLInputElement::StopNumberControlSpinnerSpin()
nsRepeatService::GetInstance()->Stop(HandleNumberControlSpin, this); nsRepeatService::GetInstance()->Stop(HandleNumberControlSpin, this);
mNumberControlSpinnerIsSpinning = false; mNumberControlSpinnerIsSpinning = false;
FireChangeEventIfNeeded();
} }
} }
void
HTMLInputElement::StepNumberControlForUserEvent(int32_t aDirection)
{
ApplyStep(aDirection);
nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
static_cast<nsIDOMHTMLInputElement*>(this),
NS_LITERAL_STRING("input"), true,
false);
}
static bool static bool
SelectTextFieldOnFocus() SelectTextFieldOnFocus()
{ {
@ -3731,7 +3743,7 @@ HTMLInputElement::PostHandleEvent(nsEventChainPostVisitor& aVisitor)
// XXX we still need to allow script to call preventDefault() on the // XXX we still need to allow script to call preventDefault() on the
// event, but right now we can't tell the difference between the editor // event, but right now we can't tell the difference between the editor
// on script doing that (bug 930374). // on script doing that (bug 930374).
ApplyStep(keyEvent->keyCode == NS_VK_UP ? 1 : -1); StepNumberControlForUserEvent(keyEvent->keyCode == NS_VK_UP ? 1 : -1);
aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault; aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
} else if (nsEventStatus_eIgnore == aVisitor.mEventStatus) { } else if (nsEventStatus_eIgnore == aVisitor.mEventStatus) {
switch (aVisitor.mEvent->message) { switch (aVisitor.mEvent->message) {
@ -3959,13 +3971,13 @@ HTMLInputElement::PostHandleEvent(nsEventChainPostVisitor& aVisitor)
switch (numberControlFrame->GetSpinButtonForPointerEvent( switch (numberControlFrame->GetSpinButtonForPointerEvent(
aVisitor.mEvent->AsMouseEvent())) { aVisitor.mEvent->AsMouseEvent())) {
case nsNumberControlFrame::eSpinButtonUp: case nsNumberControlFrame::eSpinButtonUp:
ApplyStep(1); StepNumberControlForUserEvent(1);
mNumberControlSpinnerSpinsUp = true; mNumberControlSpinnerSpinsUp = true;
StartNumberControlSpinnerSpin(); StartNumberControlSpinnerSpin();
aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault; aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
break; break;
case nsNumberControlFrame::eSpinButtonDown: case nsNumberControlFrame::eSpinButtonDown:
ApplyStep(-1); StepNumberControlForUserEvent(-1);
mNumberControlSpinnerSpinsUp = false; mNumberControlSpinnerSpinsUp = false;
StartNumberControlSpinnerSpin(); StartNumberControlSpinnerSpin();
aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault; aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;

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

@ -680,6 +680,7 @@ public:
void StartNumberControlSpinnerSpin(); void StartNumberControlSpinnerSpin();
void StopNumberControlSpinnerSpin(); void StopNumberControlSpinnerSpin();
void StepNumberControlForUserEvent(int32_t aDirection);
/** /**
* The callback function used by the nsRepeatService that we use to spin the * The callback function used by the nsRepeatService that we use to spin the

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

@ -30,6 +30,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=851780
<input type="radio" id="input_radio" oninput="++NonTextInput[4];"></input> <input type="radio" id="input_radio" oninput="++NonTextInput[4];"></input>
<input type="checkbox" id="input_checkbox" oninput="++NonTextInput[5];"></input> <input type="checkbox" id="input_checkbox" oninput="++NonTextInput[5];"></input>
<input type="range" id="input_range" oninput="++rangeInput;"></input> <input type="range" id="input_range" oninput="++rangeInput;"></input>
<input type="number" id="input_number" oninput="++numberInput;"></input>
</div> </div>
<pre id="test"> <pre id="test">
@ -37,6 +38,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=851780
/** Test for input event. This is highly based on test_change_event.html **/ /** Test for input event. This is highly based on test_change_event.html **/
const isDesktop = !/Mobile|Tablet/.test(navigator.userAgent);
var textareaInput = 0; var textareaInput = 0;
// Those are types were the input event apply. // Those are types were the input event apply.
@ -48,6 +51,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=851780
var NonTextInput = [0, 0, 0, 0, 0, 0]; var NonTextInput = [0, 0, 0, 0, 0, 0];
var rangeInput = 0; var rangeInput = 0;
var numberInput = 0;
SimpleTest.waitForExplicitFinish(); SimpleTest.waitForExplicitFinish();
var MockFilePicker = SpecialPowers.MockFilePicker; var MockFilePicker = SpecialPowers.MockFilePicker;
@ -177,6 +181,30 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=851780
synthesizeMouse(range, centerOfRangeX, centerOfRangeY, { type: "mouseup" }); synthesizeMouse(range, centerOfRangeX, centerOfRangeY, { type: "mouseup" });
is(rangeInput, 4, "Input event should be dispatched at the end of a drag"); is(rangeInput, 4, "Input event should be dispatched at the end of a drag");
// Tests for type='number'.
// We only test key events here since input events for mouse event changes
// are tested in test_input_number_mouse_events.html
var number = document.getElementById("input_number");
if (isDesktop) { // up/down arrow keys not supported on android/b2g
number.value = "";
number.focus();
synthesizeKey("VK_UP", {});
is(numberInput, 1, "input event should be dispatched for up/down arrow key keypress");
is(number.value, 1, "sanity check value of number control after keypress");
synthesizeKey("VK_DOWN", { type: "keydown" });
synthesizeKey("VK_DOWN", { type: "keypress" });
synthesizeKey("VK_DOWN", { type: "keypress" });
synthesizeKey("VK_DOWN", { type: "keypress" });
synthesizeKey("VK_DOWN", { type: "keyup" });
is(numberInput, 4, "input event should be dispatched for each up/down arrow key keypress event, even when rapidly repeated");
is(number.value, -2, "sanity check value of number control after multiple keydown events");
number.blur();
is(numberInput, 4, "input event shouldn't be dispatched on blur");
}
MockFilePicker.cleanup(); MockFilePicker.cleanup();
SimpleTest.finish(); SimpleTest.finish();
} }

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

@ -35,19 +35,20 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=935501
SimpleTest.waitForExplicitFinish(); SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(function() { SimpleTest.waitForFocus(function() {
test(); test();
SimpleTest.finish();
}); });
function test() { var input = document.getElementById("input");
var input = document.getElementById("input"); var inputRect = input.getBoundingClientRect();
var inputRect = input.getBoundingClientRect();
// Points over the input's spin-up and spin-down buttons (as offsets from the // Points over the input's spin-up and spin-down buttons (as offsets from the
// top-left of the input's bounding client rect): // top-left of the input's bounding client rect):
const SPIN_UP_X = inputRect.width - 3; const SPIN_UP_X = inputRect.width - 3;
const SPIN_UP_Y = 3; const SPIN_UP_Y = 3;
const SPIN_DOWN_X = inputRect.width - 3; const SPIN_DOWN_X = inputRect.width - 3;
const SPIN_DOWN_Y = inputRect.height - 3; const SPIN_DOWN_Y = inputRect.height - 3;
function test() {
input.value = 0;
// Test click on spin-up button: // Test click on spin-up button:
synthesizeMouse(input, SPIN_UP_X, SPIN_UP_Y, { type: "mousedown" }); synthesizeMouse(input, SPIN_UP_X, SPIN_UP_Y, { type: "mousedown" });
@ -73,37 +74,110 @@ function test() {
is(input.value, 1, "Test that preventDefault() works for click on spin-down button"); is(input.value, 1, "Test that preventDefault() works for click on spin-down button");
input.removeEventListener("mousedown", preventDefault, false); input.removeEventListener("mousedown", preventDefault, false);
// XXX TODO // Run the spin tests:
runNextSpinTest();
}
function runNextSpinTest() {
try {
var [index, test] = spinTests.next();
test();
} catch(e) {
if (e == StopIteration) {
SimpleTest.finish();
return; // We're all done
}
throw e;
}
}
const SETTIMEOUT_DELAY = 500;
var spinTests = Iterator([
// Test spining when the mouse button is kept depressed on the spin-up // Test spining when the mouse button is kept depressed on the spin-up
// button: // button, then moved over the spin-down button:
function() {
var inputEventCount = 0;
input.value = 0;
input.addEventListener("input", function(evt) {
++inputEventCount;
if (inputEventCount == 3) {
ok(input.value, 3, "Testing spin-up button");
synthesizeMouse(input, SPIN_DOWN_X, SPIN_DOWN_Y, { type: "mousemove" });
} else if (inputEventCount == 6) {
ok(input.value, 0, "Testing spin direction is reversed after mouse moves from spin-up button to spin-down button");
input.removeEventListener("input", arguments.callee, false);
synthesizeMouse(input, SPIN_DOWN_X, SPIN_DOWN_Y, { type: "mouseup" });
runNextSpinTest();
}
}, false);
synthesizeMouse(input, SPIN_UP_X, SPIN_UP_Y, { type: "mousedown" });
},
// XXX TODO
// Test spining when the mouse button is kept depressed on the spin-down // Test spining when the mouse button is kept depressed on the spin-down
// button: // button, then moved over the spin-up button:
function() {
var inputEventCount = 0;
input.value = 0;
input.addEventListener("input", function(evt) {
++inputEventCount;
if (inputEventCount == 3) {
ok(input.value, -3, "Testing spin-down button");
synthesizeMouse(input, SPIN_UP_X, SPIN_UP_Y, { type: "mousemove" });
} else if (inputEventCount == 6) {
ok(input.value, 0, "Testing spin direction is reversed after mouse moves from spin-down button to spin-up button");
input.removeEventListener("input", arguments.callee, false);
synthesizeMouse(input, SPIN_UP_X, SPIN_UP_Y, { type: "mouseup" });
runNextSpinTest();
}
}, false);
synthesizeMouse(input, SPIN_DOWN_X, SPIN_DOWN_Y, { type: "mousedown" });
},
// XXX TODO
// Test spin direction reverses when the mouse button is depressod on the
// spin-up button, then moved over the spin-down button once the spin begins:
// XXX TODO
// Test spin direction reverses when the mouse button is depressod on the
// spin-down button, then moved over the spin-up button once the spin begins:
// XXX TODO
// Test that the spin is stopped when the mouse button is depressod on the
// spin-down button, then moved outside both buttons once the spin starts:
// XXX TODO
// Test that the spin is stopped when the mouse button is depressod on the // Test that the spin is stopped when the mouse button is depressod on the
// spin-up button, then moved outside both buttons once the spin starts: // spin-up button, then moved outside both buttons once the spin starts:
function() {
var inputEventCount = 0;
input.value = 0;
input.addEventListener("input", function(evt) {
++inputEventCount;
if (inputEventCount == 3) {
synthesizeMouse(input, -1, -1, { type: "mousemove" });
var eventHandler = arguments.callee;
setTimeout(function() {
ok(input.value, 3, "Testing moving the mouse outside the spin buttons stops the spin");
ok(inputEventCount, 3, "Testing moving the mouse outside the spin buttons stops the spin input events");
input.removeEventListener("input", eventHandler, false);
synthesizeMouse(input, -1, -1, { type: "mouseup" });
runNextSpinTest();
}, SETTIMEOUT_DELAY);
}
}, false);
synthesizeMouse(input, SPIN_UP_X, SPIN_UP_Y, { type: "mousedown" });
},
// XXX TODO
// Test that changing the input type in the middle of a spin cancels the spin: // Test that changing the input type in the middle of a spin cancels the spin:
function() {
// XXX TODO var inputEventCount = 0;
// Check that we do not spin when a mousedown occurs outside the spin input.value = 0;
// buttons and then the mouse is moved over the buttons: input.addEventListener("input", function(evt) {
} ++inputEventCount;
if (inputEventCount == 3) {
input.type = "text"
var eventHandler = arguments.callee;
setTimeout(function() {
ok(input.value, 3, "Testing changing input type during a spin stops the spin");
ok(inputEventCount, 3, "Testing changing input type during a spin stops the spin input events");
input.removeEventListener("input", eventHandler, false);
synthesizeMouse(input, SPIN_DOWN_X, SPIN_DOWN_Y, { type: "mouseup" });
input.type = "number"; // restore
runNextSpinTest();
}, SETTIMEOUT_DELAY);
}
}, false);
synthesizeMouse(input, SPIN_DOWN_X, SPIN_DOWN_Y, { type: "mousedown" });
}
]);
</script> </script>
</pre> </pre>