diff --git a/content/html/content/src/HTMLInputElement.cpp b/content/html/content/src/HTMLInputElement.cpp index 303202edcef5..2c36dc04cc03 100644 --- a/content/html/content/src/HTMLInputElement.cpp +++ b/content/html/content/src/HTMLInputElement.cpp @@ -2603,7 +2603,7 @@ HTMLInputElement::HandleNumberControlSpin(void* aData) // anything else. input->StopNumberControlSpinnerSpin(); } else { - input->ApplyStep(input->mNumberControlSpinnerSpinsUp ? 1 : -1); + input->StepNumberControlForUserEvent(input->mNumberControlSpinnerSpinsUp ? 1 : -1); } } @@ -3498,9 +3498,21 @@ HTMLInputElement::StopNumberControlSpinnerSpin() nsRepeatService::GetInstance()->Stop(HandleNumberControlSpin, this); mNumberControlSpinnerIsSpinning = false; + + FireChangeEventIfNeeded(); } } +void +HTMLInputElement::StepNumberControlForUserEvent(int32_t aDirection) +{ + ApplyStep(aDirection); + nsContentUtils::DispatchTrustedEvent(OwnerDoc(), + static_cast(this), + NS_LITERAL_STRING("input"), true, + false); +} + static bool SelectTextFieldOnFocus() { @@ -3731,7 +3743,7 @@ HTMLInputElement::PostHandleEvent(nsEventChainPostVisitor& aVisitor) // 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 // 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; } else if (nsEventStatus_eIgnore == aVisitor.mEventStatus) { switch (aVisitor.mEvent->message) { @@ -3959,13 +3971,13 @@ HTMLInputElement::PostHandleEvent(nsEventChainPostVisitor& aVisitor) switch (numberControlFrame->GetSpinButtonForPointerEvent( aVisitor.mEvent->AsMouseEvent())) { case nsNumberControlFrame::eSpinButtonUp: - ApplyStep(1); + StepNumberControlForUserEvent(1); mNumberControlSpinnerSpinsUp = true; StartNumberControlSpinnerSpin(); aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault; break; case nsNumberControlFrame::eSpinButtonDown: - ApplyStep(-1); + StepNumberControlForUserEvent(-1); mNumberControlSpinnerSpinsUp = false; StartNumberControlSpinnerSpin(); aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault; diff --git a/content/html/content/src/HTMLInputElement.h b/content/html/content/src/HTMLInputElement.h index 5b6e76cb4d0b..ade169fdecb2 100644 --- a/content/html/content/src/HTMLInputElement.h +++ b/content/html/content/src/HTMLInputElement.h @@ -680,6 +680,7 @@ public: void StartNumberControlSpinnerSpin(); void StopNumberControlSpinnerSpin(); + void StepNumberControlForUserEvent(int32_t aDirection); /** * The callback function used by the nsRepeatService that we use to spin the diff --git a/content/html/content/test/forms/test_input_event.html b/content/html/content/test/forms/test_input_event.html index 3febe5b3ee24..c81f94d9049c 100644 --- a/content/html/content/test/forms/test_input_event.html +++ b/content/html/content/test/forms/test_input_event.html @@ -30,6 +30,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=851780 +
@@ -48,6 +49,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=851780
   var NonTextInput = [0, 0, 0, 0, 0, 0];
 
   var rangeInput = 0;
+  var numberInput = 0;
 
   SimpleTest.waitForExplicitFinish();
   var MockFilePicker = SpecialPowers.MockFilePicker;
@@ -177,6 +179,28 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=851780
     synthesizeMouse(range, centerOfRangeX, centerOfRangeY, { type: "mouseup" });
     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");
+
+    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();
     SimpleTest.finish();
   }
diff --git a/content/html/content/test/forms/test_input_number_mouse_events.html b/content/html/content/test/forms/test_input_number_mouse_events.html
index d2a08f9683da..b4c5a2670616 100644
--- a/content/html/content/test/forms/test_input_number_mouse_events.html
+++ b/content/html/content/test/forms/test_input_number_mouse_events.html
@@ -35,19 +35,20 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=935501
 SimpleTest.waitForExplicitFinish();
 SimpleTest.waitForFocus(function() {
   test();
-  SimpleTest.finish();
 });
 
-function test() {
-  var input = document.getElementById("input");
-  var inputRect = input.getBoundingClientRect();
+var input = document.getElementById("input");
+var inputRect = input.getBoundingClientRect();
 
-  // Points over the input's spin-up and spin-down buttons (as offsets from the
-  // top-left of the input's bounding client rect):
-  const SPIN_UP_X = inputRect.width - 3;
-  const SPIN_UP_Y = 3;
-  const SPIN_DOWN_X = inputRect.width - 3;
-  const SPIN_DOWN_Y = inputRect.height - 3;
+// Points over the input's spin-up and spin-down buttons (as offsets from the
+// top-left of the input's bounding client rect):
+const SPIN_UP_X = inputRect.width - 3;
+const SPIN_UP_Y = 3;
+const SPIN_DOWN_X = inputRect.width - 3;
+const SPIN_DOWN_Y = inputRect.height - 3;
+
+function test() {
+  input.value = 0;
 
   // Test click on spin-up button:
   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");
   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
-  // 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
-  // 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
   // 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:
-
-  // XXX TODO
-  // Check that we do not spin when a mousedown occurs outside the spin
-  // buttons and then the mouse is moved over the buttons:
-}
+  function() {
+    var inputEventCount = 0;
+    input.value = 0;
+    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" });
+  }
+]);