From cd2c5b48a8e23176434acf6ccf38c9bf8ee0cab7 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Thu, 4 Sep 2014 17:01:32 -0700 Subject: [PATCH] Bug 1045987 - Fix doorhanger notifications for the login manager. r=billm/dolske --- browser/base/content/tabbrowser.xml | 14 +++- .../metro/components/LoginManagerPrompter.js | 6 ++ .../components/LoginManagerPrompter.js | 3 + .../passwordmgr/LoginManagerContent.jsm | 6 +- .../passwordmgr/LoginManagerParent.jsm | 6 +- .../passwordmgr/nsILoginManagerPrompter.idl | 12 ++- .../passwordmgr/nsLoginManagerPrompter.js | 73 ++++++++++++++----- 7 files changed, 96 insertions(+), 24 deletions(-) diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index 2c1af22e9b1..741baa911d1 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -372,6 +372,16 @@ + + + + + + + @@ -388,7 +398,9 @@ } for (let i = 0; i < this.browsers.length; i++) { - if (this.browsers[i].contentWindow == aWindow) + // NB: We use contentWindowAsCPOW so that this code works both + // for remote browsers as well. aWindow may be a CPOW. + if (this.browsers[i].contentWindowAsCPOW == aWindow) return this.tabs[i]; } return null; diff --git a/browser/metro/components/LoginManagerPrompter.js b/browser/metro/components/LoginManagerPrompter.js index 0689a1702a4..f21538714fe 100644 --- a/browser/metro/components/LoginManagerPrompter.js +++ b/browser/metro/components/LoginManagerPrompter.js @@ -123,6 +123,12 @@ LoginManagerPrompter.prototype = { }, + setE10sData : function (aBrowser) { + // XXX Implement me! + throw new Error("Not Yet Implemented"); + }, + + /* * promptToSavePassword * diff --git a/mobile/android/components/LoginManagerPrompter.js b/mobile/android/components/LoginManagerPrompter.js index 26623493ad1..b49c8bbab1b 100644 --- a/mobile/android/components/LoginManagerPrompter.js +++ b/mobile/android/components/LoginManagerPrompter.js @@ -108,6 +108,9 @@ LoginManagerPrompter.prototype = { this.log("===== initialized ====="); }, + setE10sData : function (aBrowser) { + throw new Error("This should be filled in when Android is multiprocess"); + }, /* * promptToSavePassword diff --git a/toolkit/components/passwordmgr/LoginManagerContent.jsm b/toolkit/components/passwordmgr/LoginManagerContent.jsm index a2db10dfab6..c06731cb50e 100644 --- a/toolkit/components/passwordmgr/LoginManagerContent.jsm +++ b/toolkit/components/passwordmgr/LoginManagerContent.jsm @@ -541,13 +541,17 @@ var LoginManagerContent = { value: oldPasswordField.value } : null; + // Make sure to pass the opener's top in case it was in a frame. + let opener = win.opener ? win.opener.top : null; + let messageManager = messageManagerFromWindow(win); messageManager.sendAsyncMessage("RemoteLogins:onFormSubmit", { hostname: hostname, formSubmitURL: formSubmitURL, usernameField: mockUsername, newPasswordField: mockPassword, - oldPasswordField: mockOldPassword }); + oldPasswordField: mockOldPassword }, + { openerWin: opener }); }, /* diff --git a/toolkit/components/passwordmgr/LoginManagerParent.jsm b/toolkit/components/passwordmgr/LoginManagerParent.jsm index 5aeb3020fea..4721c25450c 100644 --- a/toolkit/components/passwordmgr/LoginManagerParent.jsm +++ b/toolkit/components/passwordmgr/LoginManagerParent.jsm @@ -69,7 +69,6 @@ var LoginManagerParent = { receiveMessage: function (msg) { let data = msg.data; - switch (msg.name) { case "RemoteLogins:findLogins": { // TODO Verify msg.target's principals against the formOrigin? @@ -88,6 +87,7 @@ var LoginManagerParent = { data.usernameField, data.newPasswordField, data.oldPasswordField, + msg.objects.openerWin, msg.target); break; } @@ -203,7 +203,7 @@ var LoginManagerParent = { onFormSubmit: function(hostname, formSubmitURL, usernameField, newPasswordField, - oldPasswordField, + oldPasswordField, opener, target) { function getPrompter() { var prompterSvc = Cc["@mozilla.org/login-manager/prompter;1"]. @@ -216,6 +216,8 @@ var LoginManagerParent = { prompterSvc.init(target.isRemoteBrowser ? target.ownerDocument.defaultView : target.contentWindow); + if (target.isRemoteBrowser) + prompterSvc.setE10sData({ browser: target, opener: opener }); return prompterSvc; } diff --git a/toolkit/components/passwordmgr/nsILoginManagerPrompter.idl b/toolkit/components/passwordmgr/nsILoginManagerPrompter.idl index 1410fbbf62e..261791fec59 100644 --- a/toolkit/components/passwordmgr/nsILoginManagerPrompter.idl +++ b/toolkit/components/passwordmgr/nsILoginManagerPrompter.idl @@ -8,8 +8,7 @@ interface nsILoginInfo; interface nsIDOMWindow; -[scriptable, uuid(68b3cb59-51b8-4c57-bd7f-b2ce955a593d)] - +[scriptable, uuid(88b75787-a78c-43aa-bfe8-52c3248b8dfd)] interface nsILoginManagerPrompter : nsISupports { /** * Initialize the prompter. Must be called before using other interfaces. @@ -22,6 +21,15 @@ interface nsILoginManagerPrompter : nsISupports { */ void init(in nsIDOMWindow aWindow); + /** + * If the caller knows which browser this prompter is being created for, + * they can call this function to avoid having to calculate it from the + * window passed to init. + * + * @param aBrowser the to use for this prompter. + */ + void setE10sData(in jsval aData); + /** * Ask the user if they want to save a login (Yes, Never, Not Now) * diff --git a/toolkit/components/passwordmgr/nsLoginManagerPrompter.js b/toolkit/components/passwordmgr/nsLoginManagerPrompter.js index 42b8c823a16..fff29fcaa70 100644 --- a/toolkit/components/passwordmgr/nsLoginManagerPrompter.js +++ b/toolkit/components/passwordmgr/nsLoginManagerPrompter.js @@ -1,3 +1,4 @@ +/* vim: set ts=4 sts=4 sw=4 et tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ @@ -199,6 +200,8 @@ LoginManagerPrompter.prototype = { _factory : null, _window : null, + _browser : null, + _opener : null, _debug : false, // mirrors signon.debug __pwmgr : null, // Password Manager service @@ -702,12 +705,21 @@ LoginManagerPrompter.prototype = { init : function (aWindow, aFactory) { this._window = aWindow; this._factory = aFactory || null; + this._browser = null; + this._opener = null; var prefBranch = Services.prefs.getBranch("signon."); this._debug = prefBranch.getBoolPref("debug"); this.log("===== initialized ====="); }, + setE10sData : function (aData) { + if (!(this._window instanceof Ci.nsIDOMChromeWindow)) + throw new Error("Unexpected call"); + this._browser = aData.browser; + this._opener = aData.opener; + }, + /* * promptToSavePassword @@ -823,10 +835,7 @@ LoginManagerPrompter.prototype = { } ]; - var notifyWin = this._getNotifyWindow(); - var chromeWin = this._getChromeWindow(notifyWin).wrappedJSObject; - var browser = chromeWin.gBrowser. - getBrowserForDocument(notifyWin.top.document); + var { browser } = this._getNotifyWindow(); aNotifyObj.show(browser, "password-save", notificationText, "password-notification-icon", mainAction, @@ -1021,10 +1030,7 @@ LoginManagerPrompter.prototype = { } }; - var notifyWin = this._getNotifyWindow(); - var chromeWin = this._getChromeWindow(notifyWin).wrappedJSObject; - var browser = chromeWin.gBrowser. - getBrowserForDocument(notifyWin.top.document); + var { browser } = this._getNotifyWindow(); aNotifyObj.show(browser, "password-change", notificationText, "password-notification-icon", mainAction, @@ -1165,6 +1171,9 @@ LoginManagerPrompter.prototype = { * Given a content DOM window, returns the chrome window it's in. */ _getChromeWindow: function (aWindow) { + // In e10s, aWindow may already be a chrome window. + if (aWindow instanceof Ci.nsIDOMChromeWindow) + return aWindow; var chromeWin = aWindow.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIWebNavigation) .QueryInterface(Ci.nsIDocShell) @@ -1181,33 +1190,61 @@ LoginManagerPrompter.prototype = { try { // Get topmost window, in case we're in a frame. var notifyWin = this._window.top; + var isE10s = (notifyWin instanceof Ci.nsIDOMChromeWindow); + var useOpener = false; - // Some sites pop up a temporary login window, when disappears + // 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 (notifyWin.opener) { var chromeDoc = this._getChromeWindow(notifyWin). document.documentElement; - var webnav = notifyWin. - QueryInterface(Ci.nsIInterfaceRequestor). - getInterface(Ci.nsIWebNavigation); + + var hasHistory; + if (isE10s) { + if (!this._browser) + throw new Error("Expected a browser in e10s"); + hasHistory = this._browser.canGoBack; + } else { + var webnav = notifyWin. + QueryInterface(Ci.nsIInterfaceRequestor). + getInterface(Ci.nsIWebNavigation); + hasHistory = webnav.sessionHistory.count > 1; + } // 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") && - webnav.sessionHistory.count == 1) { + if (chromeDoc.getAttribute("chromehidden") && !hasHistory) { this.log("Using opener window for notification bar."); notifyWin = notifyWin.opener; + useOpener = true; } } - return notifyWin; + let browser; + if (useOpener && isE10s) { + // In e10s, we have to reconstruct the opener browser from + // the CPOW passed in the message (and then passed to us in + // setE10sData). + // NB: notifyWin is now the chrome window for the opening + // window. + + browser = notifyWin.gBrowser.getBrowserForContentWindow(this._opener); + } else if (isE10s) { + browser = this._browser; + } else { + var chromeWin = this._getChromeWindow(notifyWin).wrappedJSObject; + browser = chromeWin.gBrowser + .getBrowserForDocument(notifyWin.top.document); + } + + return { notifyWin: notifyWin, browser: browser }; } catch (e) { // If any errors happen, just assume no notification box. - this.log("Unable to get notify window"); + this.log("Unable to get notify window: " + e.fileName + ":" + e.lineNumber + ": " + e.message); return null; } }, @@ -1223,7 +1260,7 @@ LoginManagerPrompter.prototype = { let popupNote = null; try { - let notifyWin = this._getNotifyWindow(); + let { notifyWin } = this._getNotifyWindow(); // Get the chrome window for the content window we're using. // .wrappedJSObject needed here -- see bug 422974 comment 5. @@ -1248,7 +1285,7 @@ LoginManagerPrompter.prototype = { let notifyBox = null; try { - let notifyWin = this._getNotifyWindow(); + let { notifyWin } = this._getNotifyWindow(); // Get the chrome window for the content window we're using. // .wrappedJSObject needed here -- see bug 422974 comment 5.