Bug 1364818 - [Form Autofill] popup won't apply to an auto-focused input until it's refocused. r=MattN
authorLuke Chang <lchang@mozilla.com>
Mon, 15 May 2017 13:26:25 +0800
changeset 410614 e39dfa768be99729f407caa406cfdcc961ce417c
parent 410613 78dcfa118bddbd18b987ae124552c00826a3826f
child 410615 93a9ea3ab784f2b1aba8a33af5d4e2c2ed731185
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersMattN
bugs1364818
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1364818 - [Form Autofill] popup won't apply to an auto-focused input until it's refocused. r=MattN MozReview-Commit-ID: H3CZEFzAJm6
browser/extensions/formautofill/content/FormAutofillFrameScript.js
browser/extensions/formautofill/test/mochitest/formautofill_common.js
browser/extensions/formautofill/test/mochitest/formautofill_parent_utils.js
browser/extensions/formautofill/test/mochitest/mochitest.ini
browser/extensions/formautofill/test/mochitest/test_autofocus_form.html
browser/extensions/formautofill/test/mochitest/test_basic_autocomplete_form.html
--- a/browser/extensions/formautofill/content/FormAutofillFrameScript.js
+++ b/browser/extensions/formautofill/content/FormAutofillFrameScript.js
@@ -36,20 +36,30 @@ var FormAutofillFrameScript = {
 
     if (!Services.prefs.getBoolPref("extensions.formautofill.addresses.enabled")) {
       return;
     }
 
     switch (evt.type) {
       case "focusin": {
         let element = evt.target;
+        let doc = element.ownerDocument;
+
         if (!FormAutofillUtils.isFieldEligibleForAutofill(element)) {
           return;
         }
-        FormAutofillContent.identifyAutofillFields(element.ownerDocument);
+
+        let doIdentifyAutofillFields =
+          () => setTimeout(() => FormAutofillContent.identifyAutofillFields(doc));
+
+        if (doc.readyState === "loading") {
+          doc.addEventListener("DOMContentLoaded", doIdentifyAutofillFields, {once: true});
+        } else {
+          doIdentifyAutofillFields();
+        }
         break;
       }
     }
   },
 
   receiveMessage(message) {
     if (!Services.prefs.getBoolPref("extensions.formautofill.addresses.enabled")) {
       return;
--- a/browser/extensions/formautofill/test/mochitest/formautofill_common.js
+++ b/browser/extensions/formautofill/test/mochitest/formautofill_common.js
@@ -5,16 +5,24 @@
 "use strict";
 
 let formFillChromeScript;
 
 function setInput(selector, value) {
   let input = document.querySelector("input" + selector);
   input.value = value;
   input.focus();
+
+  // "identifyAutofillFields" is invoked asynchronously in "focusin" event. We
+  // should make sure fields are ready for popup before doing tests.
+  //
+  // TODO: "setTimeout" is used here temporarily because there's no event to
+  //       notify us of the state of "identifyAutofillFields" for now. We should
+  //       figure out a better way after the heuristics land.
+  return new Promise(resolve => setTimeout(resolve));
 }
 
 function checkMenuEntries(expectedValues) {
   let actualValues = getMenuEntries();
 
   is(actualValues.length, expectedValues.length, " Checking length of expected menu");
   for (let i = 0; i < expectedValues.length; i++) {
     is(actualValues[i], expectedValues[i], " Checking menu entry #" + i);
--- a/browser/extensions/formautofill/test/mochitest/formautofill_parent_utils.js
+++ b/browser/extensions/formautofill/test/mochitest/formautofill_parent_utils.js
@@ -38,17 +38,16 @@ var ParentUtils = {
   },
 
   cleanup() {
     Services.obs.removeObserver(this, "formautofill-storage-changed");
     this.cleanUpAddress();
   },
 };
 
-ParentUtils.cleanUpAddress();
 Services.obs.addObserver(ParentUtils, "formautofill-storage-changed");
 
 addMessageListener("FormAutofillTest:AddAddress", (msg) => {
   ParentUtils.updateAddress("add", "FormAutofill:SaveAddress", msg, "FormAutofillTest:AddressAdded");
 });
 
 addMessageListener("FormAutofillTest:RemoveAddress", (msg) => {
   ParentUtils.updateAddress("remove", "FormAutofill:RemoveAddress", msg, "FormAutofillTest:AddressRemoved");
--- a/browser/extensions/formautofill/test/mochitest/mochitest.ini
+++ b/browser/extensions/formautofill/test/mochitest/mochitest.ini
@@ -1,9 +1,9 @@
 [DEFAULT]
 support-files =
   ../../../../../toolkit/components/satchel/test/satchel_common.js
   ../../../../../toolkit/components/satchel/test/parent_utils.js
   formautofill_common.js
   formautofill_parent_utils.js
 
+[test_autofocus_form.html]
 [test_basic_autocomplete_form.html]
-
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/test/mochitest/test_autofocus_form.html
@@ -0,0 +1,86 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test basic autofill</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+  <script type="text/javascript" src="formautofill_common.js"></script>
+  <script type="text/javascript" src="satchel_common.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+Form autofill test: autocomplete on an autofocus form
+
+<script>
+/* import-globals-from ../../../../../testing/mochitest/tests/SimpleTest/SpawnTask.js */
+/* import-globals-from ../../../../../toolkit/components/satchel/test/satchel_common.js */
+/* import-globals-from formautofill_common.js */
+
+"use strict";
+
+let expectingPopup = null;
+let MOCK_STORAGE = [{
+  organization: "Sesame Street",
+  "street-address": "123 Sesame Street.",
+  tel: "1-345-345-3456",
+}, {
+  organization: "Mozilla",
+  "street-address": "331 E. Evelyn Avenue",
+  tel: "1-650-903-0800",
+}];
+
+function expectPopup() {
+  info("expecting a popup");
+  return new Promise(resolve => {
+    expectingPopup = resolve;
+  });
+}
+
+function popupShownListener() {
+  info("popup shown for test ");
+  if (expectingPopup) {
+    expectingPopup();
+    expectingPopup = null;
+  }
+}
+
+async function setupAddressStorage() {
+  await addAddress(MOCK_STORAGE[0]);
+  await addAddress(MOCK_STORAGE[1]);
+}
+
+add_task(async function check_autocomplete_on_autofocus_field() {
+  await setupAddressStorage();
+  doKey("down");
+  await expectPopup();
+  checkMenuEntries(MOCK_STORAGE.map(address =>
+    JSON.stringify({primary: address.organization, secondary: address["street-address"]})
+  ));
+});
+
+registerPopupShownListener(popupShownListener);
+
+</script>
+
+<p id="display"></p>
+
+<div id="content">
+
+  <form id="form1">
+    <p>This is a basic form.</p>
+    <p><label>organization: <input id="organization" name="organization" autocomplete="organization" type="text"></label></p>
+    <script>
+      // Focuses the input before DOMContentLoaded
+      document.getElementById("organization").focus();
+    </script>
+    <p><label>streetAddress: <input id="street-address" name="street-address" autocomplete="street-address" type="text"></label></p>
+    <p><label>tel: <input id="tel" name="tel" autocomplete="tel" type="text"></label></p>
+    <p><label>country: <input id="country" name="country" autocomplete="country" type="text"></label></p>
+  </form>
+
+</div>
+
+<pre id="test"></pre>
+</body>
+</html>
--- a/browser/extensions/formautofill/test/mochitest/test_basic_autocomplete_form.html
+++ b/browser/extensions/formautofill/test/mochitest/test_basic_autocomplete_form.html
@@ -90,71 +90,71 @@ async function setupFormHistory() {
     {op: "add", fieldname: "email", value: "foo@mozilla.com"},
   ]);
 }
 
 // Form with history only.
 add_task(async function history_only_menu_checking() {
   await setupFormHistory();
 
-  setInput("#tel", "");
+  await setInput("#tel", "");
   doKey("down");
   await expectPopup();
   checkMenuEntries(["1-234-567-890"]);
 });
 
 // Form with both history and address storage.
 add_task(async function check_menu_when_both_existed() {
   await setupAddressStorage();
 
-  setInput("#organization", "");
+  await setInput("#organization", "");
   doKey("down");
   await expectPopup();
   checkMenuEntries(MOCK_STORAGE.map(address =>
     JSON.stringify({primary: address.organization, secondary: address["street-address"]})
   ));
 
-  setInput("#street-address", "");
+  await setInput("#street-address", "");
   doKey("down");
   await expectPopup();
   checkMenuEntries(MOCK_STORAGE.map(address =>
     JSON.stringify({primary: address["street-address"], secondary: address.organization})
   ));
 
-  setInput("#tel", "");
+  await setInput("#tel", "");
   doKey("down");
   await expectPopup();
   checkMenuEntries(MOCK_STORAGE.map(address =>
     JSON.stringify({primary: address.tel, secondary: address["street-address"]})
   ));
 });
 
 // Display history search result if no matched data in addresses.
 add_task(async function check_fallback_for_mismatched_field() {
-  setInput("#email", "");
+  await setInput("#email", "");
   doKey("down");
   await expectPopup();
   checkMenuEntries(["foo@mozilla.com"]);
 });
 
 // Autofill the address from dropdown menu.
 add_task(async function check_fields_after_form_autofill() {
-  setInput("#organization", "Moz");
+  await setInput("#organization", "Moz");
   doKey("down");
   await expectPopup();
   checkMenuEntries(MOCK_STORAGE.map(address =>
     JSON.stringify({primary: address.organization, secondary: address["street-address"]})
   ).slice(1));
   doKey("down");
   await checkFormFilled(MOCK_STORAGE[1]);
 });
 
 // Fallback to history search after autofill address.
 add_task(async function check_fallback_after_form_autofill() {
-  setInput("#tel", "");
+  await setInput("#tel", "");
   doKey("down");
   await expectPopup();
   checkMenuEntries(["1-234-567-890"]);
 });
 
 registerPopupShownListener(popupShownListener);
 
 </script>