Bug 1077308 - If a field is auto-filled while not in focus, fire a change event immediately. r=smaug,MattN
authorMichael Best <mbest@dasya.com>
Mon, 13 Oct 2014 15:10:37 -0700
changeset 274907 0d79dd9a6338686d6e7243aa580503d8d6bdbed6
parent 274862 9bfd8ab6b440c12adb946ec9606f299353141b07
child 274908 a831e8f494d7874c71b0a69da4dc3a32fb7efbf7
push id36540
push usermozilla@noorenberghe.ca
push dateMon, 13 Oct 2014 22:12:13 +0000
treeherdertry@a831e8f494d7 [default view] [failures only]
reviewerssmaug, MattN
bugs1077308
milestone36.0a1
Bug 1077308 - If a field is auto-filled while not in focus, fire a change event immediately. r=smaug,MattN
content/html/content/src/HTMLInputElement.cpp
content/html/content/test/test_bug388558.html
toolkit/components/passwordmgr/test/test_input_events.html
--- a/content/html/content/src/HTMLInputElement.cpp
+++ b/content/html/content/src/HTMLInputElement.cpp
@@ -2410,20 +2410,28 @@ HTMLInputElement::SetUserInput(const nsA
     Sequence<nsString> list;
     list.AppendElement(aValue);
     MozSetFileNameArray(list);
     return NS_OK;
   } else {
     SetValueInternal(aValue, true, true);
   }
 
-  return nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
-                                              static_cast<nsIDOMHTMLInputElement*>(this),
-                                              NS_LITERAL_STRING("input"), true,
-                                              true);
+  nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
+                                       static_cast<nsIDOMHTMLInputElement*>(this),
+                                       NS_LITERAL_STRING("input"), true,
+                                       true);
+
+  // If this element is not currently focused, it won't receive a change event for this
+  // update through the normal channels. So fire a change event immediately, instead.
+  if (!ShouldBlur(this)) {
+    FireChangeEventIfNeeded();
+  }
+
+  return NS_OK;
 }
 
 nsIEditor*
 HTMLInputElement::GetEditor()
 {
   nsTextEditorState* state = GetEditorState();
   if (state) {
     return state->GetEditor();
--- a/content/html/content/test/test_bug388558.html
+++ b/content/html/content/test/test_bug388558.html
@@ -37,17 +37,17 @@ function testUserInput() {
   is(inputChange, 1,
      "Change event dispatched when setting the value of the input element");
 
   input.value = "";
   is(inputChange, 1, 
      "Change event dispatched when setting the value of the input element (2).");
 
   SpecialPowers.wrap(input).setUserInput("foo");
-  is(inputChange, 1,
+  is(inputChange, 2,
      "Change event dispatched when input element doesn't have focus.");
 
   textarea.focus();
   textarea.setUserInput("foo");
   textarea.blur();
   is(textareaChange, 1, "Textarea element should have got one change event.");
 
   textarea.focus();
--- a/toolkit/components/passwordmgr/test/test_input_events.html
+++ b/toolkit/components/passwordmgr/test/test_input_events.html
@@ -12,62 +12,82 @@ Login Manager test: input events should 
 <script>
 commonInit();
 SimpleTest.waitForExplicitFinish();
 
 /** Test for Login Manager: form fill, should get input events. **/
 
 var usernameInputFired = false;
 var passwordInputFired = false;
+var usernameChangeFired = false;
+var passwordChangeFired = false;
 var onloadFired = false;
 
 function onNewEvent(e) {
   info("Got " + e.type + " event.");
   if (e.type == "load") {
     onloadFired = true;
-  } else if (e.target.name == "uname") {
-    ise(e.target.value, "testuser", "Should get 'testuser' as username");
-    ok(!usernameInputFired, "Should not have gotten an input event for the username field yet.");
-    usernameInputFired = true;
-  } else if (e.target.name == "pword") {
-    ise(e.target.value, "testpass", "Should get 'testpass' as password");
-    ok(!passwordInputFired, "Should not have gotten an input event for the password field yet.");
-    passwordInputFired = true;
+  } else if (e.type == "input") {
+    if (e.target.name == "uname") {
+      ise(e.target.value, "testuser", "Should get 'testuser' as username");
+      ok(!usernameInputFired, "Should not have gotten an input event for the username field yet.");
+      usernameInputFired = true;
+    } else if (e.target.name == "pword") {
+      ise(e.target.value, "testpass", "Should get 'testpass' as password");
+      ok(!passwordInputFired, "Should not have gotten an input event for the password field yet.");
+      passwordInputFired = true;
+    }
+  } else if (e.type == "change") {
+    if (e.target.name == "uname") {
+      ise(e.target.value, "testuser", "Should get 'testuser' as username");
+      ok(usernameInputFired, "Should get input event before change event for username field.");
+      ok(!usernameChangeFired, "Should not have gotten a change event for the username field yet.");
+      usernameChangeFired = true;
+    } else if (e.target.name == "pword") {
+      ise(e.target.value, "testpass", "Should get 'testpass' as password");
+      ok(passwordInputFired, "Should get input event before change event for password field.");
+      ok(!passwordChangeFired, "Should not have gotten a change event for the password field yet.");
+      passwordChangeFired = true;
+    }
   }
-  if (onloadFired && usernameInputFired && passwordInputFired) {
+  if (onloadFired && usernameInputFired && passwordInputFired && usernameChangeFired && passwordChangeFired) {
     ok(true, "All events fired as expected, we're done.");
     SimpleTest.finish();
   }
 }
 
 SimpleTest.registerCleanupFunction(function cleanup() {
   clearTimeout(timeout);
   $_(1, "uname").removeAttribute("oninput");
   $_(1, "pword").removeAttribute("oninput");
+  $_(1, "uname").removeAttribute("onchange");
+  $_(1, "pword").removeAttribute("onchange");
   document.body.removeAttribute("onload");
 });
 
 var timeout = setTimeout(function() {
   ok(usernameInputFired, "Username input event should have fired by now.");
   ok(passwordInputFired, "Password input event should have fired by now.");
+  ok(usernameChangeFired, "Username change event should have fired by now.");
+  ok(passwordChangeFired, "Password change event should have fired by now.");
   ok(onloadFired, "Window load event should have fired by now.");
   ok(false, "Not all events fired yet.");
   SimpleTest.finish();
 }, 10000);
 
 </script>
 
 <p id="display"></p>
 
 <div id="content" style="display: none">
 
   <form id="form1" action="formtest.js">
     <p>This is form 1.</p>
-    <input  type="text"       name="uname" oninput="onNewEvent(event)">
-    <input  type="password"   name="pword" oninput="onNewEvent(event)">
+    <input  type="text"       name="uname" oninput="onNewEvent(event)" onchange="onNewEvent(event)">
+    <input  type="password"   name="pword" oninput="onNewEvent(event)" onchange="onNewEvent(event)">
 
     <button type="submit">Submit</button>
     <button type="reset"> Reset </button>
   </form>
 
 </div>
 <pre id="test"></pre>
 </body>