Bug 1621853 - Clear password generation cache upon last-pb-context-exited. r=sfoster
authorMatthew Noorenberghe <mozilla@noorenberghe.ca>
Thu, 12 Mar 2020 18:55:19 +0000
changeset 518446 a1771d7b1b8f9aada3a762b8bd81f48f2affccb5
parent 518445 77c28cff842546022ed25e9858164e964c78b95d
child 518447 cad9fd303a77f52836d7b754754d1deff3888ff5
push id37210
push userdvarga@mozilla.com
push dateFri, 13 Mar 2020 04:24:24 +0000
treeherdermozilla-central@7fd4c0e31fde [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfoster
bugs1621853
milestone76.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 1621853 - Clear password generation cache upon last-pb-context-exited. r=sfoster Differential Revision: https://phabricator.services.mozilla.com/D66532
toolkit/components/passwordmgr/LoginManagerParent.jsm
toolkit/components/passwordmgr/test/browser/browser_autocomplete_generated_password_private_window.js
--- a/toolkit/components/passwordmgr/LoginManagerParent.jsm
+++ b/toolkit/components/passwordmgr/LoginManagerParent.jsm
@@ -77,16 +77,32 @@ let gRecipeManager = null;
  * TODO: Bug XXX - Should be `Number.NEGATIVE_INFINITY`.
  */
 let gLastMPLoginCancelled = Math.NEGATIVE_INFINITY;
 
 let gGeneratedPasswordObserver = {
   addedObserver: false,
 
   observe(subject, topic, data) {
+    if (topic == "last-pb-context-exited") {
+      // The last private browsing context closed so clear all cached generated
+      // passwords for private window origins.
+      for (let principalOrigin of gGeneratedPasswordsByPrincipalOrigin.keys()) {
+        let principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+          principalOrigin
+        );
+        if (!principal.privateBrowsingId) {
+          // The origin isn't for a private context so leave it alone.
+          continue;
+        }
+        gGeneratedPasswordsByPrincipalOrigin.delete(principalOrigin);
+      }
+      return;
+    }
+
     if (
       topic == "passwordmgr-autosaved-login-merged" ||
       (topic == "passwordmgr-storage-changed" && data == "removeLogin")
     ) {
       let { origin, guid } = subject;
       let generatedPW = gGeneratedPasswordsByPrincipalOrigin.get(origin);
 
       // in the case where an autosaved login removed or merged into an existing login,
@@ -553,16 +569,20 @@ class LoginManagerParent extends JSWindo
       Services.obs.addObserver(
         gGeneratedPasswordObserver,
         "passwordmgr-autosaved-login-merged"
       );
       Services.obs.addObserver(
         gGeneratedPasswordObserver,
         "passwordmgr-storage-changed"
       );
+      Services.obs.addObserver(
+        gGeneratedPasswordObserver,
+        "last-pb-context-exited"
+      );
       gGeneratedPasswordObserver.addedObserver = true;
     }
 
     gGeneratedPasswordsByPrincipalOrigin.set(framePrincipalOrigin, generatedPW);
     return generatedPW.value;
   }
 
   /**
--- a/toolkit/components/passwordmgr/test/browser/browser_autocomplete_generated_password_private_window.js
+++ b/toolkit/components/passwordmgr/test/browser/browser_autocomplete_generated_password_private_window.js
@@ -1,15 +1,34 @@
 // The origin for the test URIs.
 const TEST_ORIGIN = "https://example.com";
 const FORM_PAGE_PATH =
   "/browser/toolkit/components/passwordmgr/test/browser/form_basic.html";
 const passwordInputSelector = "#form-basic-password";
 
 add_task(async function test_autocomplete_new_password_popup_item_visible() {
+  await BrowserTestUtils.withNewTab(
+    {
+      gBrowser,
+      url: TEST_ORIGIN,
+    },
+    async function(browser) {
+      info("Generate and cache a password for a non-private context");
+      let lmp = browser.browsingContext.currentWindowGlobal.getActor(
+        "LoginManager"
+      );
+      lmp.getGeneratedPassword();
+      is(
+        LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin().size,
+        1,
+        "Non-Private password should be cached"
+      );
+    }
+  );
+
   await LoginTestUtils.addLogin({ username: "username", password: "pass1" });
   const win = await BrowserTestUtils.openNewBrowserWindow({ private: true });
   const doc = win.document;
   await BrowserTestUtils.withNewTab(
     {
       gBrowser: win.gBrowser,
       url: TEST_ORIGIN + FORM_PAGE_PATH,
     },
@@ -35,17 +54,27 @@ add_task(async function test_autocomplet
         "Popup should get closed"
       );
 
       await TestUtils.waitForTick();
       await closePopup(popup);
       await onPopupClosed;
     }
   );
+
+  let lastPBContextExitedPromise = TestUtils.topicObserved(
+    "last-pb-context-exited"
+  ).then(() => TestUtils.waitForTick());
   await BrowserTestUtils.closeWindow(win);
+  await lastPBContextExitedPromise;
+  is(
+    LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin().size,
+    1,
+    "Only private-context passwords should be cleared"
+  );
 });
 
 add_task(async function test_autocomplete_menu_item_enabled() {
   const win = await BrowserTestUtils.openNewBrowserWindow({ private: true });
   const doc = win.document;
   await BrowserTestUtils.withNewTab(
     {
       gBrowser: win.gBrowser,