Bug 1192800 - Get rid of CPOWs in browser_context_menu.js. r=MattN, a=test-only
authorJohann Hofmann <jhofmann@mozilla.com>
Thu, 16 Mar 2017 09:09:44 +0100
changeset 379305 0dd7d76f4461fb300134d66cfd5c01d398868677
parent 379304 bb1a8fb289193d5117d99100c35aea2d3f23ae4e
child 379306 6f8b65d518c4061bceefa58c480cff4db0661a91
push id1419
push userjlund@mozilla.com
push dateMon, 10 Apr 2017 20:44:07 +0000
treeherdermozilla-release@5e6801b73ef6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersMattN, test-only
bugs1192800
milestone53.0
Bug 1192800 - Get rid of CPOWs in browser_context_menu.js. r=MattN, a=test-only MozReview-Commit-ID: 8lkjFGvwS8d
toolkit/components/passwordmgr/test/browser/browser.ini
toolkit/components/passwordmgr/test/browser/browser_context_menu.js
--- a/toolkit/components/passwordmgr/test/browser/browser.ini
+++ b/toolkit/components/passwordmgr/test/browser/browser.ini
@@ -56,17 +56,16 @@ support-files =
 [browser_notifications.js]
 [browser_notifications_username.js]
 [browser_notifications_password.js]
 [browser_notifications_2.js]
 skip-if = os == "linux" # Bug 1272849 Main action button disabled state intermittent
 [browser_passwordmgr_editing.js]
 skip-if = os == "linux"
 [browser_context_menu.js]
-skip-if = e10s
 [browser_context_menu_iframe.js]
 [browser_passwordmgr_contextmenu.js]
 subsuite = clipboard
 [browser_passwordmgr_fields.js]
 [browser_passwordmgr_observers.js]
 [browser_passwordmgr_sort.js]
 [browser_passwordmgr_switchtab.js]
 [browser_passwordmgrdlg.js]
--- a/toolkit/components/passwordmgr/test/browser/browser_context_menu.js
+++ b/toolkit/components/passwordmgr/test/browser/browser_context_menu.js
@@ -1,18 +1,23 @@
 /*
  * Test the password manager context menu.
  */
 
+/* eslint no-shadow:"off" */
+
 "use strict";
 
 // The hostname for the test URIs.
 const TEST_HOSTNAME = "https://example.com";
 const MULTIPLE_FORMS_PAGE_PATH = "/browser/toolkit/components/passwordmgr/test/browser/multiple_forms.html";
 
+const CONTEXT_MENU = document.getElementById("contentAreaContextMenu");
+const POPUP_HEADER = document.getElementById("fill-login");
+
 /**
  * Initialize logins needed for the tests and disable autofill
  * for login forms for easier testing of manual fill.
  */
 add_task(function* test_initialize() {
   Services.prefs.setBoolPref("signon.autofillForms", false);
   registerCleanupFunction(() => {
     Services.prefs.clearUserPref("signon.autofillForms");
@@ -28,297 +33,353 @@ add_task(function* test_initialize() {
  * menuitems for the target password input field.
  */
 add_task(function* test_context_menu_populate_password_noSchemeUpgrades() {
   Services.prefs.setBoolPref("signon.schemeUpgrades", false);
   yield BrowserTestUtils.withNewTab({
     gBrowser,
     url: TEST_HOSTNAME + MULTIPLE_FORMS_PAGE_PATH,
   }, function* (browser) {
-    let passwordInput = browser.contentWindow.document.getElementById("test-password-1");
-
-    yield openPasswordContextMenu(browser, passwordInput);
+    yield openPasswordContextMenu(browser, "#test-password-1");
 
     // Check the content of the password manager popup
     let popupMenu = document.getElementById("fill-login-popup");
     checkMenu(popupMenu, 2);
 
-    let contextMenu = document.getElementById("contentAreaContextMenu");
-    contextMenu.hidePopup();
+    CONTEXT_MENU.hidePopup();
   });
 });
 
 /**
  * Check if the context menu is populated with the right
  * menuitems for the target password input field.
  */
 add_task(function* test_context_menu_populate_password_schemeUpgrades() {
   Services.prefs.setBoolPref("signon.schemeUpgrades", true);
   yield BrowserTestUtils.withNewTab({
     gBrowser,
     url: TEST_HOSTNAME + MULTIPLE_FORMS_PAGE_PATH,
   }, function* (browser) {
-    let passwordInput = browser.contentWindow.document.getElementById("test-password-1");
-
-    yield openPasswordContextMenu(browser, passwordInput);
+    yield openPasswordContextMenu(browser, "#test-password-1");
 
     // Check the content of the password manager popup
     let popupMenu = document.getElementById("fill-login-popup");
     checkMenu(popupMenu, 3);
 
-    let contextMenu = document.getElementById("contentAreaContextMenu");
-    contextMenu.hidePopup();
+    CONTEXT_MENU.hidePopup();
   });
 });
 
 /**
  * Check if the context menu is populated with the right menuitems
  * for the target username field with a password field present.
  */
 add_task(function* test_context_menu_populate_username_with_password_noSchemeUpgrades() {
   Services.prefs.setBoolPref("signon.schemeUpgrades", false);
   yield BrowserTestUtils.withNewTab({
     gBrowser,
     url: TEST_HOSTNAME + "/browser/toolkit/components/" +
          "passwordmgr/test/browser/multiple_forms.html",
   }, function* (browser) {
-    let passwordInput = browser.contentWindow.document.getElementById("test-username-2");
-
-    yield openPasswordContextMenu(browser, passwordInput);
+    yield openPasswordContextMenu(browser, "#test-username-2");
 
     // Check the content of the password manager popup
     let popupMenu = document.getElementById("fill-login-popup");
     checkMenu(popupMenu, 2);
 
-    let contextMenu = document.getElementById("contentAreaContextMenu");
-    contextMenu.hidePopup();
+    CONTEXT_MENU.hidePopup();
   });
 });
 /**
  * Check if the context menu is populated with the right menuitems
  * for the target username field with a password field present.
  */
 add_task(function* test_context_menu_populate_username_with_password_schemeUpgrades() {
   Services.prefs.setBoolPref("signon.schemeUpgrades", true);
   yield BrowserTestUtils.withNewTab({
     gBrowser,
     url: TEST_HOSTNAME + "/browser/toolkit/components/" +
          "passwordmgr/test/browser/multiple_forms.html",
   }, function* (browser) {
-    let passwordInput = browser.contentWindow.document.getElementById("test-username-2");
-
-    yield openPasswordContextMenu(browser, passwordInput);
+    yield openPasswordContextMenu(browser, "#test-username-2");
 
     // Check the content of the password manager popup
     let popupMenu = document.getElementById("fill-login-popup");
     checkMenu(popupMenu, 3);
 
-    let contextMenu = document.getElementById("contentAreaContextMenu");
-    contextMenu.hidePopup();
+    CONTEXT_MENU.hidePopup();
   });
 });
 
 /**
  * Check if the password field is correctly filled when one
  * login menuitem is clicked.
  */
 add_task(function* test_context_menu_password_fill() {
   Services.prefs.setBoolPref("signon.schemeUpgrades", true);
   yield BrowserTestUtils.withNewTab({
     gBrowser,
     url: TEST_HOSTNAME + MULTIPLE_FORMS_PAGE_PATH,
   }, function* (browser) {
+    let formDescriptions = yield ContentTask.spawn(browser, {}, function*() {
+      let forms = Array.from(content.document.getElementsByClassName("test-form"));
+      return forms.map((f) => f.getAttribute("description"));
+    });
 
-    let testForms = browser.contentWindow.document.getElementsByClassName("test-form");
-    for (let form of testForms) {
-      let usernameInputList = form.querySelectorAll("input[type='password']");
-      info("Testing form: " + form.getAttribute("description"));
+    for (let description of formDescriptions) {
+      info("Testing form: " + description);
 
-      for (let passwordField of usernameInputList) {
-        info("Testing password field: " + passwordField.id);
+      let passwordInputIds = yield ContentTask.spawn(browser, {description}, function*({description}) {
+        let formElement = content.document.querySelector(`[description="${description}"]`);
+        let passwords = Array.from(formElement.querySelectorAll("input[type='password']"));
+        return passwords.map((p) => p.id);
+      });
 
-        let contextMenu = document.getElementById("contentAreaContextMenu");
-        let menuItemStatus = form.getAttribute("menuitemStatus");
+      for (let inputId of passwordInputIds) {
+        info("Testing password field: " + inputId);
 
         // Synthesize a right mouse click over the username input element.
-        yield openPasswordContextMenu(browser, passwordField, () => {
-          let popupHeader = document.getElementById("fill-login");
+        yield openPasswordContextMenu(browser, "#" + inputId, function*() {
+          let inputDisabled = yield ContentTask
+            .spawn(browser, {inputId}, function*({inputId}) {
+              let input = content.document.getElementById(inputId);
+              return input.disabled || input.readOnly;
+          });
 
           // If the password field is disabled or read-only, we want to see
           // the disabled Fill Password popup header.
-          if (passwordField.disabled || passwordField.readOnly) {
-            Assert.ok(!popupHeader.hidden, "Popup menu is not hidden.");
-            Assert.ok(popupHeader.disabled, "Popup menu is disabled.");
-            contextMenu.hidePopup();
-            return false;
+          if (inputDisabled) {
+            Assert.ok(!POPUP_HEADER.hidden, "Popup menu is not hidden.");
+            Assert.ok(POPUP_HEADER.disabled, "Popup menu is disabled.");
+            CONTEXT_MENU.hidePopup();
           }
-          return true;
+
+          return !inputDisabled;
         });
 
-        if (contextMenu.state != "open") {
+        if (CONTEXT_MENU.state != "open") {
           continue;
         }
 
         // The only field affected by the password fill
         // should be the target password field itself.
-        let unchangedFields = form.querySelectorAll("input:not(#" + passwordField.id + ")");
-        yield assertContextMenuFill(form, null, passwordField, unchangedFields, 1);
-        Assert.equal(passwordField.value, "password1", "Check upgraded login was actually used");
-        contextMenu.hidePopup();
+        yield assertContextMenuFill(browser, description, null, inputId, 1);
+        yield ContentTask.spawn(browser, {inputId}, function*({inputId}) {
+          let passwordField = content.document.getElementById(inputId);
+          Assert.equal(passwordField.value, "password1", "Check upgraded login was actually used");
+        });
+
+        CONTEXT_MENU.hidePopup();
       }
     }
   });
 });
 
 /**
  * Check if the form is correctly filled when one
  * username context menu login menuitem is clicked.
  */
 add_task(function* test_context_menu_username_login_fill() {
   Services.prefs.setBoolPref("signon.schemeUpgrades", true);
   yield BrowserTestUtils.withNewTab({
     gBrowser,
     url: TEST_HOSTNAME + MULTIPLE_FORMS_PAGE_PATH,
   }, function* (browser) {
 
-    let testForms = browser.contentWindow.document.getElementsByClassName("test-form");
-    for (let form of testForms) {
-      let usernameInputList = form.querySelectorAll("input[type='text']");
-      info("Testing form: " + form.getAttribute("description"));
+    let formDescriptions = yield ContentTask.spawn(browser, {}, function*() {
+      let forms = Array.from(content.document.getElementsByClassName("test-form"));
+      return forms.map((f) => f.getAttribute("description"));
+    });
 
-      for (let usernameField of usernameInputList) {
-        info("Testing username field: " + usernameField.id);
+    for (let description of formDescriptions) {
+      info("Testing form: " + description);
+      let usernameInputIds = yield ContentTask
+        .spawn(browser, {description}, function*({description}) {
+          let formElement = content.document.querySelector(`[description="${description}"]`);
+          let inputs = Array.from(formElement.querySelectorAll("input[type='text']"));
+          return inputs.map((p) => p.id);
+      });
 
-        // We always want to check if the first password field is filled,
-        // since this is the current behavior from the _fillForm function.
-        let passwordField = form.querySelector("input[type='password']");
-
-        let contextMenu = document.getElementById("contentAreaContextMenu");
-        let menuItemStatus = form.getAttribute("menuitemStatus");
+      for (let inputId of usernameInputIds) {
+        info("Testing username field: " + inputId);
 
         // Synthesize a right mouse click over the username input element.
-        yield openPasswordContextMenu(browser, usernameField, () => {
-          let popupHeader = document.getElementById("fill-login");
+        yield openPasswordContextMenu(browser, "#" + inputId, function*() {
+          let headerHidden = POPUP_HEADER.hidden;
+          let headerDisabled = POPUP_HEADER.disabled;
+
+          let data = {description, inputId, headerHidden, headerDisabled};
+          let shouldContinue = yield ContentTask.spawn(browser, data, function*(data) {
+            let {description, inputId, headerHidden, headerDisabled} = data;
+            let formElement = content.document.querySelector(`[description="${description}"]`);
+            let usernameField = content.document.getElementById(inputId);
+            // We always want to check if the first password field is filled,
+            // since this is the current behavior from the _fillForm function.
+            let passwordField = formElement.querySelector("input[type='password']");
 
-          // If we don't want to see the actual popup menu,
-          // check if the popup is hidden or disabled.
-          if (!passwordField || usernameField.disabled || usernameField.readOnly ||
-              passwordField.disabled || passwordField.readOnly) {
-            if (!passwordField) {
-              Assert.ok(popupHeader.hidden, "Popup menu is hidden.");
-            } else {
-              Assert.ok(!popupHeader.hidden, "Popup menu is not hidden.");
-              Assert.ok(popupHeader.disabled, "Popup menu is disabled.");
+            // If we don't want to see the actual popup menu,
+            // check if the popup is hidden or disabled.
+            if (!passwordField || usernameField.disabled || usernameField.readOnly ||
+                passwordField.disabled || passwordField.readOnly) {
+              if (!passwordField) {
+                Assert.ok(headerHidden, "Popup menu is hidden.");
+              } else {
+                Assert.ok(!headerHidden, "Popup menu is not hidden.");
+                Assert.ok(headerDisabled, "Popup menu is disabled.");
+              }
+              return false;
             }
-            contextMenu.hidePopup();
-            return false;
+            return true;
+          });
+
+          if (!shouldContinue) {
+            CONTEXT_MENU.hidePopup();
           }
-          return true;
+
+          return shouldContinue;
         });
 
-        if (contextMenu.state != "open") {
+        if (CONTEXT_MENU.state != "open") {
           continue;
         }
+
+        let passwordFieldId = yield ContentTask
+          .spawn(browser, {description}, function*({description}) {
+            let formElement = content.document.querySelector(`[description="${description}"]`);
+            return formElement.querySelector("input[type='password']").id;
+        });
+
         // We shouldn't change any field that's not the target username field or the first password field
-        let unchangedFields = form.querySelectorAll("input:not(#" + usernameField.id + "):not(#" + passwordField.id + ")");
-        yield assertContextMenuFill(form, usernameField, passwordField, unchangedFields, 1);
-        if (!passwordField.hasAttribute("expectedFail")) {
-          Assert.equal(passwordField.value, "password1", "Check upgraded login was actually used");
-        }
-        contextMenu.hidePopup();
+        yield assertContextMenuFill(browser, description, inputId, passwordFieldId, 1);
+
+        yield ContentTask.spawn(browser, {passwordFieldId}, function*({passwordFieldId}) {
+          let passwordField = content.document.getElementById(passwordFieldId);
+          if (!passwordField.hasAttribute("expectedFail")) {
+            Assert.equal(passwordField.value, "password1", "Check upgraded login was actually used");
+          }
+        });
+
+        CONTEXT_MENU.hidePopup();
       }
     }
   });
 });
 
 /**
  * Synthesize mouse clicks to open the password manager context menu popup
  * for a target password input element.
  *
  * assertCallback should return true if we should continue or else false.
  */
 function* openPasswordContextMenu(browser, passwordInput, assertCallback = null) {
   // Synthesize a right mouse click over the password input element.
-  let contextMenuShownPromise = BrowserTestUtils.waitForEvent(window, "popupshown");
+  let contextMenuShownPromise = BrowserTestUtils.waitForEvent(CONTEXT_MENU, "popupshown");
   let eventDetails = {type: "contextmenu", button: 2};
   BrowserTestUtils.synthesizeMouseAtCenter(passwordInput, eventDetails, browser);
   yield contextMenuShownPromise;
 
   if (assertCallback) {
-    if (!assertCallback.call()) {
+    let shouldContinue = yield assertCallback();
+    if (!shouldContinue) {
       return;
     }
   }
 
   // Synthesize a mouse click over the fill login menu header.
-  let popupHeader = document.getElementById("fill-login");
-  let popupShownPromise = BrowserTestUtils.waitForEvent(popupHeader, "popupshown");
-  EventUtils.synthesizeMouseAtCenter(popupHeader, {});
+  let popupShownPromise = BrowserTestUtils.waitForEvent(POPUP_HEADER, "popupshown");
+  EventUtils.synthesizeMouseAtCenter(POPUP_HEADER, {});
   yield popupShownPromise;
 }
 
 /**
  * Verify that only the expected form fields are filled.
  */
-function* assertContextMenuFill(form, usernameField, passwordField, unchangedFields, loginIndex) {
+function* assertContextMenuFill(browser, formId, usernameFieldId, passwordFieldId, loginIndex) {
   let popupMenu = document.getElementById("fill-login-popup");
+  let unchangedSelector = `[description="${formId}"] input:not(#${passwordFieldId})`;
+
+  if (usernameFieldId) {
+    unchangedSelector += `:not(#${usernameFieldId})`;
+  }
 
-  // Store the value of fields that should remain unchanged.
-  if (unchangedFields.length) {
-    for (let field of unchangedFields) {
-      field.setAttribute("original-value", field.value);
+  yield ContentTask.spawn(browser, {unchangedSelector}, function*({unchangedSelector}) {
+    let unchangedFields = content.document.querySelectorAll(unchangedSelector);
+
+    // Store the value of fields that should remain unchanged.
+    if (unchangedFields.length) {
+      for (let field of unchangedFields) {
+        field.setAttribute("original-value", field.value);
+      }
     }
-  }
+  });
 
   // Execute the default command of the specified login menuitem found in the context menu.
   let loginItem = popupMenu.getElementsByClassName("context-login-item")[loginIndex];
-  loginItem.doCommand();
-
-  yield BrowserTestUtils.waitForEvent(form, "input", "Username input value changed");
 
   // Find the used login by it's username (Use only unique usernames in this test).
-  let login = getLoginFromUsername(loginItem.label);
+  let {username, password} = getLoginFromUsername(loginItem.label);
+
+  let data = {username, password, usernameFieldId, passwordFieldId, formId, unchangedSelector};
+  let continuePromise = ContentTask.spawn(browser, data, function*(data) {
+    let {username, password, usernameFieldId, passwordFieldId, formId, unchangedSelector} = data;
+    let form = content.document.querySelector(`[description="${formId}"]`);
+    yield ContentTaskUtils.waitForEvent(form, "input", "Username input value changed");
 
-  // If we have an username field, check if it's correctly filled
-  if (usernameField && usernameField.getAttribute("expectedFail") == null) {
-    Assert.equal(login.username, usernameField.value, "Username filled and correct.");
-  }
+    if (usernameFieldId) {
+      let usernameField = content.document.getElementById(usernameFieldId);
 
-  // If we have a password field, check if it's correctly filled
-  if (passwordField && passwordField.getAttribute("expectedFail") == null) {
-    Assert.equal(passwordField.value, login.password, "Password filled and correct.");
-  }
+      // If we have an username field, check if it's correctly filled
+      if (usernameField.getAttribute("expectedFail") == null) {
+        Assert.equal(username, usernameField.value, "Username filled and correct.");
+      }
+    }
+
+    if (passwordFieldId) {
+      let passwordField = content.document.getElementById(passwordFieldId);
 
-  // Check that all fields that should not change have the same value as before.
-  if (unchangedFields.length) {
-    Assert.ok(() => {
-      for (let field of unchangedFields) {
-        if (field.value != field.getAttribute("original-value")) {
-          return false;
+      // If we have a password field, check if it's correctly filled
+      if (passwordField && passwordField.getAttribute("expectedFail") == null) {
+        Assert.equal(password, passwordField.value, "Password filled and correct.");
+      }
+    }
+
+    let unchangedFields = content.document.querySelectorAll(unchangedSelector);
+
+    // Check that all fields that should not change have the same value as before.
+    if (unchangedFields.length) {
+      Assert.ok(() => {
+        for (let field of unchangedFields) {
+          if (field.value != field.getAttribute("original-value")) {
+            return false;
+          }
         }
-      }
-      return true;
-    }, "Other fields were not changed.");
-  }
+        return true;
+      }, "Other fields were not changed.");
+    }
+  });
+
+  loginItem.doCommand();
+
+  return continuePromise;
 }
 
 /**
  * Check if every login that matches the page hostname are available at the context menu.
  * @param {Element} contextMenu
  * @param {Number} expectedCount - Number of logins expected in the context menu. Used to ensure
 *                                  we continue testing something useful.
  */
 function checkMenu(contextMenu, expectedCount) {
   let logins = loginList().filter(login => {
     return LoginHelper.isOriginMatching(login.hostname, TEST_HOSTNAME, {
       schemeUpgrades: Services.prefs.getBoolPref("signon.schemeUpgrades"),
     });
   });
   // Make an array of menuitems for easier comparison.
-  let menuitems = [...contextMenu.getElementsByClassName("context-login-item")];
+  let menuitems = [...CONTEXT_MENU.getElementsByClassName("context-login-item")];
   Assert.equal(menuitems.length, expectedCount, "Expected number of menu items");
   Assert.ok(logins.every(l => menuitems.some(m => l.username == m.label)), "Every login have an item at the menu.");
 }
 
 /**
  * Search for a login by it's username.
  *
  * Only unique login/hostname combinations should be used at this test.