Bug 1373130 - Send message after identifyAutofillFields being invoked to indicate that formautofill is ready to open popup. r=lchang
MozReview-Commit-ID: 4dZzgYNahEq
--- a/browser/extensions/formautofill/content/FormAutofillFrameScript.js
+++ b/browser/extensions/formautofill/content/FormAutofillFrameScript.js
@@ -31,16 +31,19 @@ var FormAutofillFrameScript = {
return;
}
this._hasPendingTask = true;
setTimeout(() => {
FormAutofillContent.identifyAutofillFields(this._nextHandleElement);
this._hasPendingTask = false;
this._nextHandleElement = null;
+ // This is for testing purpose only which sends a message to indicate that the
+ // form has been identified, and ready to open popup.
+ sendAsyncMessage("FormAutofill:FieldsIdentified");
});
},
init() {
addEventListener("focusin", this);
addMessageListener("FormAutofill:PreviewProfile", this);
addMessageListener("FormAutoComplete:PopupClosed", this);
addMessageListener("FormAutoComplete:PopupOpened", this);
--- a/browser/extensions/formautofill/test/browser/head.js
+++ b/browser/extensions/formautofill/test/browser/head.js
@@ -109,34 +109,46 @@ async function expectPopupOpen(browser)
return (item.getAttribute("originaltype") == "autofill-profile" ||
item.getAttribute("originaltype") == "insecureWarning" ||
item.getAttribute("originaltype") == "autofill-footer") &&
item.hasAttribute("formautofillattached");
});
}, "The popup should be a form autofill one");
}
+async function waitForFieldsIdentified() {
+ return new Promise(resolve => {
+ Services.mm.addMessageListener("FormAutofill:FieldsIdentified", function onIdentified() {
+ Services.mm.removeMessageListener("FormAutofill:FieldsIdentified", onIdentified);
+ resolve();
+ });
+ });
+}
+
async function openPopupOn(browser, selector) {
await SimpleTest.promiseFocus(browser);
- /* eslint no-shadow: ["error", { "allow": ["selector"] }] */
- const identified = await ContentTask.spawn(browser, {selector}, async function({selector}) {
+ /* eslint no-shadow: ["error", { "allow": ["selector", "focused", "identified"] }] */
+ const {focused, identified} = await ContentTask.spawn(browser, {selector}, async function({selector}) {
const input = content.document.querySelector(selector);
const forms = content.document.getElementsByTagName("form");
const rootElement = [...forms].find(form => form.contains(input)) || content.document.body;
input.focus();
- if (rootElement.hasAttribute("test-formautofill-identified")) {
- return true;
- }
+ const focused = content.document.activeElement == input;
+ const identified = rootElement.hasAttribute("test-formautofill-identified");
+
rootElement.setAttribute("test-formautofill-identified", "true");
- return false;
+ return {focused, identified};
});
- // Wait 2 seconds for identifyAutofillFields if the form hasn't been identified yet.
+ // Wait 200ms for identifyAutofillFields if the form hasn't been identified yet.
if (!identified) {
- await sleep(2000);
+ if (!focused) {
+ await waitForFieldsIdentified();
+ }
+ await sleep(200);
}
await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, browser);
await expectPopupOpen(browser);
}
async function expectPopupClose(browser) {
await BrowserTestUtils.waitForCondition(() => !browser.autoCompletePopup.popupOpen,
"popup should have closed");
--- a/browser/extensions/formautofill/test/mochitest/formautofill_common.js
+++ b/browser/extensions/formautofill/test/mochitest/formautofill_common.js
@@ -9,28 +9,40 @@ let expectingPopup = null;
const {FormAutofillUtils} = SpecialPowers.Cu.import("resource://formautofill/FormAutofillUtils.jsm");
async function sleep(ms = 500, reason = "Intentionally wait for UI ready") {
SimpleTest.requestFlakyTimeout(reason);
await new Promise(resolve => setTimeout(resolve, ms));
}
+async function waitForFieldsIdentified() {
+ return new Promise(resolve => {
+ formFillChromeScript.addMessageListener("FormAutofillTest:FieldsIdentified", function onIdentified() {
+ formFillChromeScript.removeMessageListener("FormAutofillTest:FieldsIdentified", onIdentified);
+ resolve();
+ });
+ });
+}
+
async function setInput(selector, value) {
let input = document.querySelector("input" + selector);
input.value = value;
input.focus();
+ if (document.activeElement != input) {
+ await waitForFieldsIdentified();
+ }
// "identifyAutofillFields" is invoked asynchronously in "focusin" event. We
// should make sure fields are ready for popup before doing tests.
//
// TODO: "sleep" 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.
- await sleep(500, "Guarantee asynchronous identifyAutofillFields is invoked");
+ await sleep(200, "Guarantee asynchronous identifyAutofillFields is invoked");
return input;
}
function clickOnElement(selector) {
let element = document.querySelector(selector);
if (!element) {
throw new Error("Can not find the element");
--- a/browser/extensions/formautofill/test/mochitest/formautofill_parent_utils.js
+++ b/browser/extensions/formautofill/test/mochitest/formautofill_parent_utils.js
@@ -164,16 +164,20 @@ var ParentUtils = {
observe(subject, topic, data) {
assert.ok(topic === "formautofill-storage-changed");
sendAsyncMessage("formautofill-storage-changed", {subject: null, topic, data});
},
};
Services.obs.addObserver(ParentUtils, "formautofill-storage-changed");
+Services.mm.addMessageListener("FormAutofill:FieldsIdentified", () => {
+ sendAsyncMessage("FormAutofillTest:FieldsIdentified");
+});
+
addMessageListener("FormAutofillTest:AddAddress", (msg) => {
ParentUtils.operateAddress("add", msg, "FormAutofillTest:AddressAdded");
});
addMessageListener("FormAutofillTest:RemoveAddress", (msg) => {
ParentUtils.operateAddress("remove", msg, "FormAutofillTest:AddressRemoved");
});
--- a/browser/extensions/formautofill/test/mochitest/test_on_address_submission.html
+++ b/browser/extensions/formautofill/test/mochitest/test_on_address_submission.html
@@ -26,44 +26,46 @@ let TEST_ADDRESSES = [{
}, {
organization: "Mozilla",
"street-address": "331 E. Evelyn Avenue",
tel: "+16509030800",
}];
initPopupListener();
+async function fillMockProfilesSequentially(profile) {
+ return Object.entries(profile)
+ .map(([key, value]) => setInput("#" + key, value))
+ .reduce((p, c) => p.then(c), Promise.resolve());
+}
+
// Submit first address for saving.
add_task(async function check_storage_after_form_submitted() {
// We already verified the first time use case in browser test
await SpecialPowers.pushPrefEnv({
set: [["extensions.formautofill.firstTimeUse", false]],
});
- for (let key in TEST_ADDRESSES[0]) {
- await setInput("#" + key, TEST_ADDRESSES[0][key]);
- }
+ await fillMockProfilesSequentially(TEST_ADDRESSES[0]);
clickOnElement("input[type=submit]");
let expectedAddresses = TEST_ADDRESSES.slice(0, 1);
await onStorageChanged("add");
// Check if timesUsed is set correctly
expectedAddresses[0].timesUsed = 1;
let matching = await checkAddresses(expectedAddresses);
ok(matching, "Address saved as expected");
delete expectedAddresses[0].timesUsed;
});
// Submit another new address.
add_task(async function check_storage_after_another_address_submitted() {
document.querySelector("form").reset();
- for (let key in TEST_ADDRESSES[1]) {
- await setInput("#" + key, TEST_ADDRESSES[1][key]);
- }
+ await fillMockProfilesSequentially(TEST_ADDRESSES[1]);
clickOnElement("input[type=submit]");
// The 2nd test address should be on the top since it's the last used one.
let addressesInMenu = TEST_ADDRESSES.slice(1);
addressesInMenu.push(TEST_ADDRESSES[0]);
// let expectedAddresses = TEST_ADDRESSES.slice(0);
@@ -77,19 +79,17 @@ add_task(async function check_storage_af
checkMenuEntries(addressesInMenu.map(address =>
JSON.stringify({primary: address.organization, secondary: address["street-address"]})
));
});
// Submit another new address that is mergeable.
add_task(async function new_address_submitted_and_merged() {
document.querySelector("form").reset();
- for (let key in TEST_ADDRESSES[0]) {
- await setInput("#" + key, TEST_ADDRESSES[0][key]);
- }
+ await fillMockProfilesSequentially(TEST_ADDRESSES[0]);
// Add country to first address in storage
await setInput("#country", "US");
TEST_ADDRESSES[0].country = "US";
clickOnElement("input[type=submit]");
let expectedAddresses = TEST_ADDRESSES.slice(0);
// Check if timesUsed is set correctly
expectedAddresses[0].timesUsed = 2;