Bug 1492950 - Get login prompter off of getBrowserForContentWindow. r=MattN
authorMike Conley <mconley@mozilla.com>
Tue, 02 Oct 2018 17:52:54 +0000
changeset 494953 bd55b04144dccce7c1875c165e3306ab86a743b6
parent 494952 9bb5657e614c806a361bafd35d94eced2611cd66
child 494954 680d1feab4fed4384ba94eae0205da1da8786579
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersMattN
bugs1492950
milestone64.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 1492950 - Get login prompter off of getBrowserForContentWindow. r=MattN Depends on D6711 Differential Revision: https://phabricator.services.mozilla.com/D6712
toolkit/components/passwordmgr/LoginManagerContent.jsm
toolkit/components/passwordmgr/LoginManagerParent.jsm
toolkit/components/passwordmgr/nsILoginManagerPrompter.idl
toolkit/components/passwordmgr/nsLoginManagerPrompter.js
--- a/toolkit/components/passwordmgr/LoginManagerContent.jsm
+++ b/toolkit/components/passwordmgr/LoginManagerContent.jsm
@@ -965,26 +965,30 @@ var LoginManagerContent = {
                          null;
     let mockPassword = { name: newPasswordField.name,
                          value: newPasswordField.value };
     let mockOldPassword = oldPasswordField ?
                             { name: oldPasswordField.name,
                               value: oldPasswordField.value } :
                             null;
 
-    // Make sure to pass the opener's top in case it was in a frame.
-    let openerTopWindow = win.opener ? win.opener.top : null;
+    // Make sure to pass the opener's top ID in case it was in a frame.
+    let openerTopWindowID = null;
+    if (win.opener) {
+      openerTopWindowID = win.opener.top.windowUtils.outerWindowID;
+    }
 
     messageManager.sendAsyncMessage("RemoteLogins:onFormSubmit",
                                     { hostname,
                                       formSubmitURL,
                                       usernameField: mockUsername,
                                       newPasswordField: mockPassword,
-                                      oldPasswordField: mockOldPassword },
-                                    { openerTopWindow });
+                                      oldPasswordField: mockOldPassword,
+                                      openerTopWindowID,
+                                    });
   },
 
   /**
    * Attempt to find the username and password fields in a form, and fill them
    * in using the provided logins and recipes.
    *
    * @param {LoginForm} form
    * @param {nsILoginInfo[]} foundLogins an array of nsILoginInfo that could be
--- a/toolkit/components/passwordmgr/LoginManagerParent.jsm
+++ b/toolkit/components/passwordmgr/LoginManagerParent.jsm
@@ -87,17 +87,17 @@ var LoginManagerParent = {
 
       case "RemoteLogins:onFormSubmit": {
         // TODO Verify msg.target's principals against the formOrigin?
         this.onFormSubmit(data.hostname,
                           data.formSubmitURL,
                           data.usernameField,
                           data.newPasswordField,
                           data.oldPasswordField,
-                          msg.objects.openerTopWindow,
+                          data.openerTopWindowID,
                           msg.target);
         break;
       }
 
       case "RemoteLogins:insecureLoginFormPresent": {
         this.setHasInsecureLoginForms(msg.target, data.hasInsecureLoginForms);
         break;
       }
@@ -283,24 +283,39 @@ var LoginManagerParent = {
     target.messageManager.sendAsyncMessage("RemoteLogins:loginsAutoCompleted", {
       requestId,
       logins: jsLogins,
     });
   },
 
   onFormSubmit(hostname, formSubmitURL,
                          usernameField, newPasswordField,
-                         oldPasswordField, openerTopWindow,
+                         oldPasswordField, openerTopWindowID,
                          target) {
     function getPrompter() {
       var prompterSvc = Cc["@mozilla.org/login-manager/prompter;1"].
                         createInstance(Ci.nsILoginManagerPrompter);
       prompterSvc.init(target.ownerGlobal);
       prompterSvc.browser = target;
-      prompterSvc.opener = openerTopWindow;
+
+      for (let win of Services.wm.getEnumerator(null)) {
+        if (!win.gBrowser && !win.getBrowser) {
+          continue;
+        }
+
+        let tabbrowser = win.gBrowser || win.getBrowser();
+        if (tabbrowser) {
+          let browser = tabbrowser.getBrowserForOuterWindowID(openerTopWindowID);
+          if (browser) {
+            prompterSvc.openerBrowser = browser;
+            break;
+          }
+        }
+      }
+
       return prompterSvc;
     }
 
     function recordLoginUse(login) {
       // Update the lastUsed timestamp and increment the use count.
       let propBag = Cc["@mozilla.org/hash-property-bag;1"].
                     createInstance(Ci.nsIWritablePropertyBag);
       propBag.setProperty("timeLastUsed", Date.now());
--- a/toolkit/components/passwordmgr/nsILoginManagerPrompter.idl
+++ b/toolkit/components/passwordmgr/nsILoginManagerPrompter.idl
@@ -33,22 +33,21 @@ interface nsILoginManagerPrompter : nsIS
 
   /**
    * The browser this prompter is being created for.
    * This is required if the init function received a chrome window as argument.
    */
   attribute Element browser;
 
   /**
-   * The opener that was used to open the window passed to init.
+   * The opener browser that was used to open the window passed to init.
    * The opener can be used to determine in which window the prompt
-   * should be shown. Must be a content window that is not a frame window,
-   * make sure to pass the top window using e.g. window.top.
+   * should be shown.
    */
-  attribute nsIDOMWindow opener;
+  attribute Element openerBrowser;
 
   /**
    * Ask the user if they want to save a login (Yes, Never, Not Now)
    *
    * @param aLogin
    *        The login to be saved.
    */
   void promptToSavePassword(in nsILoginInfo aLogin);
--- a/toolkit/components/passwordmgr/nsLoginManagerPrompter.js
+++ b/toolkit/components/passwordmgr/nsLoginManagerPrompter.js
@@ -242,17 +242,17 @@ LoginManagerPrompter.prototype = {
   classID: Components.ID("{8aa66d77-1bbb-45a6-991e-b8f47751c291}"),
   QueryInterface: ChromeUtils.generateQI([Ci.nsIAuthPrompt,
                                            Ci.nsIAuthPrompt2,
                                            Ci.nsILoginManagerPrompter]),
 
   _factory: null,
   _chromeWindow: null,
   _browser: null,
-  _opener: null,
+  _openerBrowser: null,
 
   __strBundle: null, // String bundle for L10N
   get _strBundle() {
     if (!this.__strBundle) {
       this.__strBundle = Services.strings.createBundle(
                   "chrome://passwordmgr/locale/passwordmgr.properties");
       if (!this.__strBundle)
         throw new Error("String bundle for Login Manager not present!");
@@ -718,37 +718,38 @@ LoginManagerPrompter.prototype = {
       this._chromeWindow = aWindow;
       // needs to be set explicitly using setBrowser
       this._browser = null;
     } else {
       let {win, browser} = this._getChromeWindow(aWindow);
       this._chromeWindow = win;
       this._browser = browser;
     }
-    this._opener = null;
+    this._openerBrowser = null;
     this._factory = aFactory || null;
 
     this.log("===== initialized =====");
   },
 
   set browser(aBrowser) {
     this._browser = aBrowser;
   },
 
-  set opener(aOpener) {
-    this._opener = aOpener;
+  set openerBrowser(aOpenerBrowser) {
+    this._openerBrowser = aOpenerBrowser;
   },
 
   promptToSavePassword(aLogin) {
     this.log("promptToSavePassword");
     var notifyObj = this._getPopupNote() || this._getNotifyBox();
-    if (notifyObj)
+    if (notifyObj) {
       this._showSaveLoginNotification(notifyObj, aLogin);
-    else
+    } else {
       this._showSaveLoginDialog(aLogin);
+    }
   },
 
   /**
    * Displays a notification bar.
    */
   _showLoginNotification(aNotifyBox, aName, aText, aButtons) {
     var oldBar = aNotifyBox.getNotificationWithValue(aName);
     const priority = aNotifyBox.PRIORITY_INFO_MEDIUM;
@@ -1392,62 +1393,53 @@ LoginManagerPrompter.prototype = {
   },
 
   /**
    * Given a content DOM window, returns the chrome window and browser it's in.
    */
   _getChromeWindow(aWindow) {
     // Handle non-e10s toolkit consumers.
     if (!Cu.isCrossProcessWrapper(aWindow)) {
-      let chromeWin = aWindow.docShell.chromeEventHandler.ownerGlobal;
+      let browser = aWindow.docShell.chromeEventHandler;
+      if (!browser) {
+        return null;
+      }
+
+      let chromeWin = browser.ownerGlobal;
       if (!chromeWin) {
         return null;
       }
 
-      // gBrowser only exists on some apps, like Firefox.
-      let tabbrowser = chromeWin.gBrowser ||
-        (typeof chromeWin.getBrowser == "function" ? chromeWin.getBrowser() : null);
-      // At least serve the chrome window if getBrowser()
-      // or getBrowserForContentWindow() are not supported.
-      if (!tabbrowser || typeof tabbrowser.getBrowserForContentWindow != "function") {
-        return { win: chromeWin };
-      }
-
-      let browser = tabbrowser.getBrowserForContentWindow(aWindow);
       return { win: chromeWin, browser };
     }
 
-    for (let win of Services.wm.getEnumerator(null)) {
-      let tabbrowser = win.gBrowser || win.getBrowser();
-      let browser = tabbrowser.getBrowserForContentWindow(aWindow);
-      if (browser) {
-        return { win, browser };
-      }
-    }
     return null;
   },
 
   _getNotifyWindow() {
     // Some sites pop up a temporary login window, which disappears
     // upon submission of credentials. We want to put the notification
     // bar in the opener window if this seems to be happening.
-    if (this._opener) {
+    if (this._openerBrowser) {
       let chromeDoc = this._chromeWindow.document.documentElement;
 
       // Check to see if the current window was opened with chrome
       // disabled, and if so use the opener window. But if the window
       // has been used to visit other pages (ie, has a history),
       // assume it'll stick around and *don't* use the opener.
       if (chromeDoc.getAttribute("chromehidden") && !this._browser.canGoBack) {
         this.log("Using opener window for notification bar.");
-        return this._getChromeWindow(this._opener);
+        return { win: this._openerBrowser.ownerGlobal, browser: this._openerBrowser };
       }
     }
 
-    return { win: this._chromeWindow, browser: this._browser };
+    return {
+      win: this._chromeWindow,
+      browser: this._browser,
+    };
   },
 
   /**
    * Returns the popup notification to this prompter,
    * or null if there isn't one available.
    */
   _getPopupNote() {
     let popupNote = null;