Bug 1548381 - LoginManagerParent.doAutocompleteSearch/getGeneratedPassword tests. r=sfoster
☠☠ backed out by 2690e619a493 ☠ ☠
authorMatthew Noorenberghe <mozilla@noorenberghe.ca>
Mon, 20 May 2019 19:55:15 +0000
changeset 474620 e0cf735bdcf5235d4e2e7416d5a07956e7e74671
parent 474619 fde90ccfb57027a25127edd92eade0bc3e8c853f
child 474621 1e2300b95a59dfa70594e01e6b34716141e22105
push id36042
push userdvarga@mozilla.com
push dateTue, 21 May 2019 04:19:40 +0000
treeherdermozilla-central@ca560ff55451 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfoster
bugs1548381
milestone69.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 1548381 - LoginManagerParent.doAutocompleteSearch/getGeneratedPassword tests. r=sfoster Differential Revision: https://phabricator.services.mozilla.com/D31207
toolkit/components/passwordmgr/LoginManagerParent.jsm
toolkit/components/passwordmgr/test/unit/test_LoginManagerParent_doAutocompleteSearch.js
toolkit/components/passwordmgr/test/unit/test_LoginManagerParent_getGeneratedPassword.js
toolkit/components/passwordmgr/test/unit/xpcshell.ini
--- a/toolkit/components/passwordmgr/LoginManagerParent.jsm
+++ b/toolkit/components/passwordmgr/LoginManagerParent.jsm
@@ -323,16 +323,23 @@ var LoginManagerParent = {
     var jsLogins = LoginHelper.loginsToVanillaObjects(matchingLogins);
     target.messageManager.sendAsyncMessage("PasswordManager:loginsAutoCompleted", {
       requestId,
       generatedPassword,
       logins: jsLogins,
     });
   },
 
+  /**
+   * Expose `BrowsingContext` so we can stub it in tests.
+   */
+  get _browsingContextGlobal() {
+    return BrowsingContext;
+  },
+
   getGeneratedPassword(browsingContextId) {
     if (!LoginHelper.enabled || !LoginHelper.generationAvailable || !LoginHelper.generationEnabled) {
       return null;
     }
 
     let browsingContext = BrowsingContext.get(browsingContextId);
     if (!browsingContext) {
       return null;
new file mode 100644
--- /dev/null
+++ b/toolkit/components/passwordmgr/test/unit/test_LoginManagerParent_doAutocompleteSearch.js
@@ -0,0 +1,92 @@
+/**
+ * Test LoginManagerParent.doAutocompleteSearch()
+ */
+
+"use strict";
+
+const {sinon} = ChromeUtils.import("resource://testing-common/Sinon.jsm");
+const {LoginManagerParent: LMP} = ChromeUtils.import("resource://gre/modules/LoginManagerParent.jsm");
+
+add_task(async function test_doAutocompleteSearch_generated_noLogins() {
+  Services.prefs.setBoolPref("signon.generation.available", true); // TODO: test both with false
+  Services.prefs.setBoolPref("signon.generation.enabled", true);
+
+  ok(LMP.doAutocompleteSearch, "doAutocompleteSearch exists");
+
+  // Default to the happy path
+  let arg1 = {
+    autocompleteInfo: {
+      section: "",
+      addressType: "",
+      contactType: "",
+      fieldName: "new-password",
+      canAutomaticallyPersist: false,
+    },
+    browsingContextId: 123,
+    formOrigin: "https://example.com",
+    actionOrigin: "https://mozilla.org",
+    searchString: "",
+    previousResult: null,
+    requestId: "foo",
+    isSecure: true,
+    isPasswordField: true,
+  };
+
+  let sendMessageStub = sinon.stub();
+  let fakeBrowser = {
+    messageManager: {
+      sendAsyncMessage: sendMessageStub,
+    },
+  };
+
+  sinon.stub(LMP._browsingContextGlobal, "get").withArgs(123).callsFake(() => {
+    return {
+      currentWindowGlobal: {
+        documentPrincipal: Services.scriptSecurityManager.createCodebasePrincipalFromOrigin("https://www.example.com^userContextId=1"),
+      },
+    };
+  });
+
+  LMP.doAutocompleteSearch(arg1, fakeBrowser);
+  ok(sendMessageStub.calledOnce, "sendAsyncMessage was called");
+  let msg1 = sendMessageStub.firstCall.args[1];
+  equal(msg1.requestId, arg1.requestId, "requestId matches");
+  equal(msg1.logins.length, 0, "no logins");
+  ok(msg1.generatedPassword, "has a generated password");
+  equal(msg1.generatedPassword.length, 15, "generated password length");
+  sendMessageStub.resetHistory();
+
+  info("repeat the search and ensure the same password was used");
+  LMP.doAutocompleteSearch(arg1, fakeBrowser);
+  ok(sendMessageStub.calledOnce, "sendAsyncMessage was called");
+  let msg2 = sendMessageStub.firstCall.args[1];
+  equal(msg2.requestId, arg1.requestId, "requestId matches");
+  equal(msg2.logins.length, 0, "no logins");
+  equal(msg2.generatedPassword, msg1.generatedPassword, "same generated password");
+  sendMessageStub.resetHistory();
+
+  info("Check cases where a password shouldn't be generated");
+
+  LMP.doAutocompleteSearch({...arg1, ...{isPasswordField: false}}, fakeBrowser);
+  ok(sendMessageStub.calledOnce, "sendAsyncMessage was called");
+  let msg = sendMessageStub.firstCall.args[1];
+  equal(msg.requestId, arg1.requestId, "requestId matches");
+  equal(msg.generatedPassword, null, "no generated password when not a pw. field");
+  sendMessageStub.resetHistory();
+
+  let arg1_2 = {...arg1};
+  arg1_2.autocompleteInfo.fieldName = "";
+  LMP.doAutocompleteSearch(arg1_2, fakeBrowser);
+  ok(sendMessageStub.calledOnce, "sendAsyncMessage was called");
+  msg = sendMessageStub.firstCall.args[1];
+  equal(msg.requestId, arg1.requestId, "requestId matches");
+  equal(msg.generatedPassword, null, "no generated password when not autocomplete=new-password");
+  sendMessageStub.resetHistory();
+
+  LMP.doAutocompleteSearch({...arg1, ...{browsingContextId: 999}}, fakeBrowser);
+  ok(sendMessageStub.calledOnce, "sendAsyncMessage was called");
+  msg = sendMessageStub.firstCall.args[1];
+  equal(msg.requestId, arg1.requestId, "requestId matches");
+  equal(msg.generatedPassword, null, "no generated password with a missing browsingContextId");
+  sendMessageStub.resetHistory();
+});
new file mode 100644
--- /dev/null
+++ b/toolkit/components/passwordmgr/test/unit/test_LoginManagerParent_getGeneratedPassword.js
@@ -0,0 +1,69 @@
+/**
+ * Test LoginManagerParent.getGeneratedPassword()
+ */
+
+"use strict";
+
+const {sinon} = ChromeUtils.import("resource://testing-common/Sinon.jsm");
+const {LoginManagerParent: LMP} = ChromeUtils.import("resource://gre/modules/LoginManagerParent.jsm");
+
+add_task(async function test_getGeneratedPassword() {
+  // Force the feature to be enabled.
+  Services.prefs.setBoolPref("signon.generation.available", true);
+  Services.prefs.setBoolPref("signon.generation.enabled", true);
+
+  ok(LMP.getGeneratedPassword, "LMP.getGeneratedPassword exists");
+  equal(LMP._generatedPasswordsByPrincipalOrigin.size, 0, "Empty cache to start");
+
+  equal(LMP.getGeneratedPassword(99), null, "Null with no BrowsingContext");
+
+  ok(LMP._browsingContextGlobal, "Check _browsingContextGlobal exists");
+  ok(!LMP._browsingContextGlobal.get(99), "BrowsingContext 99 shouldn't exist yet");
+  info("Stubbing BrowsingContext.get(99)");
+  sinon.stub(LMP._browsingContextGlobal, "get").withArgs(99).callsFake(() => {
+    return {
+      currentWindowGlobal: {
+        documentPrincipal: Services.scriptSecurityManager.createCodebasePrincipalFromOrigin("https://www.example.com^userContextId=6"),
+      },
+    };
+  });
+  ok(LMP._browsingContextGlobal.get(99), "Checking BrowsingContext.get(99) stub");
+
+  let password1 = LMP.getGeneratedPassword(99);
+  notEqual(password1, null, "Check password was returned");
+  equal(password1.length, 15, "Check password length");
+  equal(LMP._generatedPasswordsByPrincipalOrigin.size, 1, "1 added to cache");
+  equal(LMP._generatedPasswordsByPrincipalOrigin.get("https://www.example.com^userContextId=6"),
+        password1, "Cache key and value");
+  let password2 = LMP.getGeneratedPassword(99);
+  equal(password1, password2, "Same password should be returned for the same origin");
+
+  info("Changing the documentPrincipal to simulate a navigation in the frame");
+  LMP._browsingContextGlobal.get.restore();
+  sinon.stub(LMP._browsingContextGlobal, "get").withArgs(99).callsFake(() => {
+    return {
+      currentWindowGlobal: {
+        documentPrincipal: Services.scriptSecurityManager.createCodebasePrincipalFromOrigin("https://www.mozilla.org^userContextId=2"),
+      },
+    };
+  });
+  let password3 = LMP.getGeneratedPassword(99);
+  notEqual(password2, password3, "Different password for a different origin for the same BC");
+  equal(password3.length, 15, "Check password3 length");
+
+  info("Now checks cases where null should be returned");
+
+  Services.prefs.setBoolPref("signon.rememberSignons", false);
+  equal(LMP.getGeneratedPassword(99), null, "Prevented when pwmgr disabled");
+  Services.prefs.setBoolPref("signon.rememberSignons", true);
+
+  Services.prefs.setBoolPref("signon.generation.available", false);
+  equal(LMP.getGeneratedPassword(99), null, "Prevented when unavailable");
+  Services.prefs.setBoolPref("signon.generation.available", true);
+
+  Services.prefs.setBoolPref("signon.generation.enabled", false);
+  equal(LMP.getGeneratedPassword(99), null, "Prevented when disabled");
+  Services.prefs.setBoolPref("signon.generation.enabled", true);
+
+  equal(LMP.getGeneratedPassword(123), null, "Prevented when browsingContext is missing");
+});
--- a/toolkit/components/passwordmgr/test/unit/xpcshell.ini
+++ b/toolkit/components/passwordmgr/test/unit/xpcshell.ini
@@ -22,16 +22,18 @@ run-if = buildapp == "browser"
 [test_doLoginsMatch.js]
 [test_getFormFields.js]
 [test_getPasswordFields.js]
 [test_getPasswordOrigin.js]
 [test_getUserNameAndPasswordFields.js]
 [test_isOriginMatching.js]
 [test_isUsernameFieldType.js]
 [test_legacy_empty_formSubmitURL.js]
+[test_LoginManagerParent_doAutocompleteSearch.js]
+[test_LoginManagerParent_getGeneratedPassword.js]
 [test_legacy_validation.js]
 [test_login_autocomplete_result.js]
 skip-if = os == "android"
 [test_logins_change.js]
 [test_logins_decrypt_failure.js]
 skip-if = os == "android" # Bug 1171687: Needs fixing on Android
 [test_logins_metainfo.js]
 [test_logins_search.js]