From 7c565dc045bfa123dc86306c6ece8234b075aed2 Mon Sep 17 00:00:00 2001 From: Noemi Erli Date: Thu, 21 Feb 2019 04:04:47 +0200 Subject: [PATCH] Backed out 6 changesets (bug 1304001) for mochitest failures in test_bug430351.html CLOSED TREE Backed out changeset 5fafa838de11 (bug 1304001) Backed out changeset d75340a9a264 (bug 1304001) Backed out changeset ed7ae6b877df (bug 1304001) Backed out changeset 93bd4d634b14 (bug 1304001) Backed out changeset e41015881591 (bug 1304001) Backed out changeset 6346331d934d (bug 1304001) --- browser/base/content/content.js | 4 +- browser/base/content/nsContextMenu.js | 3 +- mobile/android/components/BrowserCLH.js | 6 +- .../components/passwordmgr/LoginHelper.jsm | 87 +++-------- .../passwordmgr/LoginManagerContent.jsm | 136 ++++++++++++------ .../test/unit/test_getPasswordOrigin.js | 7 +- .../unit/test_getUserNameAndPasswordFields.js | 6 +- 7 files changed, 124 insertions(+), 125 deletions(-) diff --git a/browser/base/content/content.js b/browser/base/content/content.js index b8646f20a63c..8fa4bd8123c8 100644 --- a/browser/base/content/content.js +++ b/browser/base/content/content.js @@ -45,7 +45,7 @@ addEventListener("DOMFormHasPassword", function(event) { if (shouldIgnoreLoginManagerEvent(event)) { return; } - LoginManagerContent.onDOMFormHasPassword(event); + LoginManagerContent.onDOMFormHasPassword(event, content); let formLike = LoginFormFactory.createFromForm(event.originalTarget); InsecurePasswordUtils.reportInsecurePasswords(formLike); }); @@ -53,7 +53,7 @@ addEventListener("DOMInputPasswordAdded", function(event) { if (shouldIgnoreLoginManagerEvent(event)) { return; } - LoginManagerContent.onDOMInputPasswordAdded(event); + LoginManagerContent.onDOMInputPasswordAdded(event, content); let formLike = LoginFormFactory.createFromField(event.originalTarget); InsecurePasswordUtils.reportInsecurePasswords(formLike); }); diff --git a/browser/base/content/nsContextMenu.js b/browser/base/content/nsContextMenu.js index 17dcd8e95e14..35b58ed34854 100644 --- a/browser/base/content/nsContextMenu.js +++ b/browser/base/content/nsContextMenu.js @@ -718,9 +718,8 @@ nsContextMenu.prototype = { // don't want to show the form fill option. let showFill = loginFillInfo && loginFillInfo.passwordField.found; - // Disable the fill option if the user hasn't unlocked with their master password + // Disable the fill option if the user has set a master password // or if the password field or target field are disabled. - // XXX: Bug 1529025 to respect signon.rememberSignons let disableFill = !loginFillInfo || !Services.logins || !Services.logins.isLoggedIn || diff --git a/mobile/android/components/BrowserCLH.js b/mobile/android/components/BrowserCLH.js index fd8b02819291..be67d3a4718f 100644 --- a/mobile/android/components/BrowserCLH.js +++ b/mobile/android/components/BrowserCLH.js @@ -230,14 +230,14 @@ BrowserCLH.prototype = { if (shouldIgnoreLoginManagerEvent(event)) { return; } - this.LoginManagerContent.onDOMFormHasPassword(event); + this.LoginManagerContent.onDOMFormHasPassword(event, event.target.ownerGlobal.top); }, options); aWindow.addEventListener("DOMInputPasswordAdded", event => { if (shouldIgnoreLoginManagerEvent(event)) { return; } - this.LoginManagerContent.onDOMInputPasswordAdded(event); + this.LoginManagerContent.onDOMInputPasswordAdded(event, event.target.ownerGlobal.top); }, options); aWindow.addEventListener("DOMAutoComplete", event => { @@ -250,7 +250,7 @@ BrowserCLH.prototype = { aWindow.addEventListener("pageshow", event => { // XXXbz what about non-HTML documents?? if (ChromeUtils.getClassName(event.target) == "HTMLDocument") { - this.LoginManagerContent.onPageShow(event); + this.LoginManagerContent.onPageShow(event, event.target.defaultView.top); } }, options); }, diff --git a/toolkit/components/passwordmgr/LoginHelper.jsm b/toolkit/components/passwordmgr/LoginHelper.jsm index 280d58d53e97..d84f522cbf95 100644 --- a/toolkit/components/passwordmgr/LoginHelper.jsm +++ b/toolkit/components/passwordmgr/LoginHelper.jsm @@ -21,57 +21,49 @@ var EXPORTED_SYMBOLS = [ const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"); const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm"); +// LoginHelper + /** * Contains functions shared by different Login Manager components. */ var LoginHelper = { - debug: null, - enabled: null, - formlessCaptureEnabled: null, - schemeUpgrades: null, - insecureAutofill: null, - privateBrowsingCaptureEnabled: null, - - init() { - // Watch for pref changes to update cached pref values. - Services.prefs.addObserver("signon.", () => this.updateSignonPrefs()); - this.updateSignonPrefs(); - }, - - updateSignonPrefs() { - this.autofillForms = Services.prefs.getBoolPref("signon.autofillForms"); - this.debug = Services.prefs.getBoolPref("signon.debug"); - this.enabled = Services.prefs.getBoolPref("signon.rememberSignons"); - this.formlessCaptureEnabled = Services.prefs.getBoolPref("signon.formlessCapture.enabled"); - this.insecureAutofill = Services.prefs.getBoolPref("signon.autofillForms.http"); - this.privateBrowsingCaptureEnabled = - Services.prefs.getBoolPref("signon.privateBrowsingCapture.enabled"); - - this.schemeUpgrades = Services.prefs.getBoolPref("signon.schemeUpgrades"); - this.storeWhenAutocompleteOff = Services.prefs.getBoolPref("signon.storeWhenAutocompleteOff"); - }, + /** + * Warning: these only update if a logger was created. + */ + debug: Services.prefs.getBoolPref("signon.debug"), + formlessCaptureEnabled: Services.prefs.getBoolPref("signon.formlessCapture.enabled"), + schemeUpgrades: Services.prefs.getBoolPref("signon.schemeUpgrades"), + insecureAutofill: Services.prefs.getBoolPref("signon.autofillForms.http"), + privateBrowsingCaptureEnabled: + Services.prefs.getBoolPref("signon.privateBrowsingCapture.enabled"), createLogger(aLogPrefix) { let getMaxLogLevel = () => { - return this.debug ? "Debug" : "Warn"; + return this.debug ? "debug" : "warn"; }; let logger; function getConsole() { if (!logger) { // Create a new instance of the ConsoleAPI so we can control the maxLogLevel with a pref. + let ConsoleAPI = ChromeUtils.import("resource://gre/modules/Console.jsm", {}).ConsoleAPI; let consoleOptions = { maxLogLevel: getMaxLogLevel(), prefix: aLogPrefix, }; - logger = console.createInstance(consoleOptions); + logger = new ConsoleAPI(consoleOptions); } return logger; } // Watch for pref changes and update this.debug and the maxLogLevel for created loggers - Services.prefs.addObserver("signon.debug", () => { + Services.prefs.addObserver("signon.", () => { this.debug = Services.prefs.getBoolPref("signon.debug"); + this.formlessCaptureEnabled = Services.prefs.getBoolPref("signon.formlessCapture.enabled"); + this.schemeUpgrades = Services.prefs.getBoolPref("signon.schemeUpgrades"); + this.insecureAutofill = Services.prefs.getBoolPref("signon.autofillForms.http"); + this.privateBrowsingCaptureEnabled = + Services.prefs.getBoolPref("signon.privateBrowsingCapture.enabled"); if (logger) { logger.maxLogLevel = getMaxLogLevel(); } @@ -218,43 +210,6 @@ var LoginHelper = { return aURL; }, - /** - * Get the parts of the URL we want for identification. - * Strip out things like the userPass portion and handle javascript:. - */ - getLoginOrigin(uriString, allowJS) { - let realm = ""; - try { - let uri = Services.io.newURI(uriString); - - if (allowJS && uri.scheme == "javascript") { - return "javascript:"; - } - - // Build this manually instead of using prePath to avoid including the userPass portion. - realm = uri.scheme + "://" + uri.displayHostPort; - } catch (e) { - // bug 159484 - disallow url types that don't support a hostPort. - // (although we handle "javascript:..." as a special case above.) - log.warn("Couldn't parse origin for", uriString, e); - realm = null; - } - - return realm; - }, - - getFormActionOrigin(form) { - let uriString = form.action; - - // A blank or missing action submits to where it came from. - if (uriString == "") { - // ala bug 297761 - uriString = form.baseURI; - } - - return this.getLoginOrigin(uriString, true); - }, - /** * @param {String} aLoginOrigin - An origin value from a stored login's * hostname or formSubmitURL properties. @@ -831,8 +786,6 @@ var LoginHelper = { }, }; -LoginHelper.init(); - XPCOMUtils.defineLazyPreferenceGetter(LoginHelper, "showInsecureFieldWarning", "security.insecure_field_warning.contextual.enabled"); diff --git a/toolkit/components/passwordmgr/LoginManagerContent.jsm b/toolkit/components/passwordmgr/LoginManagerContent.jsm index f6033b243231..b161e443f33c 100644 --- a/toolkit/components/passwordmgr/LoginManagerContent.jsm +++ b/toolkit/components/passwordmgr/LoginManagerContent.jsm @@ -2,13 +2,6 @@ * 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/. */ -/** - * Module doing most of the content process work for the password manager. - */ - -// Disable use-ownerGlobal since FormLike don't have it. -/* eslint-disable mozilla/use-ownerGlobal */ - "use strict"; var EXPORTED_SYMBOLS = [ "LoginManagerContent", @@ -47,6 +40,8 @@ Services.cpmm.addMessageListener("clearRecipeCache", () => { LoginRecipesContent._clearRecipeCache(); }); +// These mirror signon.* prefs. +var gEnabled, gAutofillForms, gStoreWhenAutocompleteOff; var gLastRightClickTimeStamp = Number.NEGATIVE_INFINITY; var observer = { @@ -73,6 +68,12 @@ var observer = { return true; // Always return true, or form submit will be canceled. }, + onPrefChange() { + gEnabled = Services.prefs.getBoolPref("signon.rememberSignons"); + gAutofillForms = Services.prefs.getBoolPref("signon.autofillForms"); + gStoreWhenAutocompleteOff = Services.prefs.getBoolPref("signon.storeWhenAutocompleteOff"); + }, + // nsIWebProgressListener onLocationChange(aWebProgress, aRequest, aLocation, aFlags) { // Only handle pushState/replaceState here. @@ -117,7 +118,7 @@ var observer = { return; } - if (!LoginHelper.enabled) { + if (!gEnabled) { return; } @@ -146,6 +147,11 @@ var observer = { }; Services.obs.addObserver(observer, "earlyformsubmit"); +var prefBranch = Services.prefs.getBranch("signon."); +prefBranch.addObserver("", observer.onPrefChange); + +observer.onPrefChange(); // read initial values + // This object maps to the "child" process (even in the single-process case). var LoginManagerContent = { @@ -164,10 +170,8 @@ var LoginManagerContent = { .getService(Ci.nsIUUIDGenerator).generateUUID().toString(); }, - _messages: [ - "RemoteLogins:loginsFound", - "RemoteLogins:loginsAutoCompleted", - ], + _messages: [ "RemoteLogins:loginsFound", + "RemoteLogins:loginsAutoCompleted" ], /** * WeakMap of the root element of a FormLike to the FormLike representing its fields. @@ -243,10 +247,10 @@ var LoginManagerContent = { return deferred.promise; }, - receiveMessage(msg, topWindow) { + receiveMessage(msg, window) { if (msg.name == "RemoteLogins:fillForm") { this.fillForm({ - topDocument: topWindow.document, + topDocument: window.document, loginFormOrigin: msg.data.loginFormOrigin, loginsFound: LoginHelper.vanillaObjectsToLogins(msg.data.logins), recipes: msg.data.recipes, @@ -268,7 +272,8 @@ var LoginManagerContent = { } case "RemoteLogins:loginsAutoCompleted": { - let loginsFound = LoginHelper.vanillaObjectsToLogins(msg.data.logins); + let loginsFound = + LoginHelper.vanillaObjectsToLogins(msg.data.logins); let messageManager = msg.target; request.promise.resolve({ logins: loginsFound, messageManager }); break; @@ -287,11 +292,11 @@ var LoginManagerContent = { let doc = form.ownerDocument; let win = doc.defaultView; - let formOrigin = LoginHelper.getLoginOrigin(doc.documentURI); + let formOrigin = LoginUtils._getPasswordOrigin(doc.documentURI); if (!formOrigin) { return Promise.reject("_getLoginDataFromParent: A form origin is required"); } - let actionOrigin = LoginHelper.getFormActionOrigin(form); + let actionOrigin = LoginUtils._getActionOrigin(form); let messageManager = win.docShell.messageManager; @@ -312,8 +317,8 @@ var LoginManagerContent = { let form = LoginFormFactory.createFromField(aElement); let win = doc.defaultView; - let formOrigin = LoginHelper.getLoginOrigin(doc.documentURI); - let actionOrigin = LoginHelper.getFormActionOrigin(form); + let formOrigin = LoginUtils._getPasswordOrigin(doc.documentURI); + let actionOrigin = LoginUtils._getActionOrigin(form); let messageManager = win.docShell.messageManager; @@ -339,7 +344,10 @@ var LoginManagerContent = { setupEventListeners(global) { global.addEventListener("pageshow", (event) => { - this.onPageShow(event); + this.onPageShow(event, global.content); + }); + global.addEventListener("blur", (event) => { + this.onUsernameInput(event); }); }, @@ -360,7 +368,7 @@ var LoginManagerContent = { } }, - onDOMFormHasPassword(event) { + onDOMFormHasPassword(event, window) { if (!event.isTrusted) { return; } @@ -368,10 +376,10 @@ var LoginManagerContent = { let form = event.target; let formLike = LoginFormFactory.createFromForm(form); log("onDOMFormHasPassword:", form, formLike); - this._fetchLoginsFromParentAndFillForm(formLike); + this._fetchLoginsFromParentAndFillForm(formLike, window); }, - onDOMInputPasswordAdded(event) { + onDOMInputPasswordAdded(event, window) { if (!event.isTrusted) { return; } @@ -382,7 +390,6 @@ var LoginManagerContent = { return; } - let window = pwField.ownerGlobal; // Only setup the listener for formless inputs. // Capture within a
but without a submit event is bug 1287202. this.setupProgressListener(window); @@ -401,7 +408,7 @@ var LoginManagerContent = { let formLike2 = this._formLikeByRootElement.get(formLike.rootElement); log("Running deferred processing of onDOMInputPasswordAdded", formLike2); this._deferredPasswordAddedTasksByRootElement.delete(formLike2.rootElement); - this._fetchLoginsFromParentAndFillForm(formLike2); + this._fetchLoginsFromParentAndFillForm(formLike2, window); }, PASSWORD_INPUT_ADDED_COALESCING_THRESHOLD_MS, 0); this._deferredPasswordAddedTasksByRootElement.set(formLike.rootElement, deferredTask); @@ -427,15 +434,15 @@ var LoginManagerContent = { * Fetch logins from the parent for a given form and then attempt to fill it. * * @param {FormLike} form to fetch the logins for then try autofill. + * @param {Window} window */ - _fetchLoginsFromParentAndFillForm(form) { - let window = form.ownerDocument.defaultView; - this._detectInsecureFormLikes(window.top); + _fetchLoginsFromParentAndFillForm(form, window) { + this._detectInsecureFormLikes(window); let messageManager = window.docShell.messageManager; messageManager.sendAsyncMessage("LoginStats:LoginEncountered"); - if (!LoginHelper.enabled) { + if (!gEnabled) { return; } @@ -444,8 +451,7 @@ var LoginManagerContent = { .catch(Cu.reportError); }, - onPageShow(event) { - let window = event.target.ownerGlobal; + onPageShow(event, window) { this._detectInsecureFormLikes(window); }, @@ -527,10 +533,10 @@ var LoginManagerContent = { log("fillForm: No input element specified"); return; } - if (LoginHelper.getLoginOrigin(topDocument.documentURI) != loginFormOrigin) { + if (LoginUtils._getPasswordOrigin(topDocument.documentURI) != loginFormOrigin) { if (!inputElement || - LoginHelper.getLoginOrigin(inputElement.ownerDocument.documentURI) != loginFormOrigin) { - log("fillForm: The requested origin doesn't match the one from the", + LoginUtils._getPasswordOrigin(inputElement.ownerDocument.documentURI) != loginFormOrigin) { + log("fillForm: The requested origin doesn't match the one form the", "document. This may mean we navigated to a document from a different", "site before we had a chance to indicate this change in the user", "interface."); @@ -555,9 +561,9 @@ var LoginManagerContent = { loginsFound({ form, loginsFound, recipes }) { let doc = form.ownerDocument; - let autofillForm = LoginHelper.autofillForms && !PrivateBrowsingUtils.isContentWindowPrivate(doc.defaultView); + let autofillForm = gAutofillForms && !PrivateBrowsingUtils.isContentWindowPrivate(doc.defaultView); - let formOrigin = LoginHelper.getLoginOrigin(doc.documentURI); + let formOrigin = LoginUtils._getPasswordOrigin(doc.documentURI); LoginRecipesContent.cacheRecipes(formOrigin, doc.defaultView, recipes); this._fillForm(form, loginsFound, recipes, {autofillForm}); @@ -607,7 +613,7 @@ var LoginManagerContent = { return; } - if (!LoginHelper.enabled) { + if (!gEnabled) { return; } @@ -644,7 +650,7 @@ var LoginManagerContent = { let acForm = LoginFormFactory.createFromField(acInputField); let doc = acForm.ownerDocument; - let formOrigin = LoginHelper.getLoginOrigin(doc.documentURI); + let formOrigin = LoginUtils._getPasswordOrigin(doc.documentURI); let recipes = LoginRecipesContent.getRecipes(formOrigin, doc.defaultView); // Make sure the username field fillForm will use is the @@ -935,18 +941,18 @@ var LoginManagerContent = { return; } - // If password saving is disabled globally, bail out now. - if (!LoginHelper.enabled) { + // If password saving is disabled (globally or for host), bail out now. + if (!gEnabled) { return; } - var hostname = LoginHelper.getLoginOrigin(doc.documentURI); + var hostname = LoginUtils._getPasswordOrigin(doc.documentURI); if (!hostname) { log("(form submission ignored -- invalid hostname)"); return; } - let formSubmitURL = LoginHelper.getFormActionOrigin(form); + let formSubmitURL = LoginUtils._getActionOrigin(form); let messageManager = win.docShell.messageManager; let recipes = LoginRecipesContent.getRecipes(hostname, win); @@ -968,7 +974,7 @@ var LoginManagerContent = { this._isAutocompleteDisabled(usernameField) || this._isAutocompleteDisabled(newPasswordField) || this._isAutocompleteDisabled(oldPasswordField)) && - !LoginHelper.storeWhenAutocompleteOff) { + !gStoreWhenAutocompleteOff) { log("(form submission ignored -- autocomplete=off found)"); return; } @@ -1401,7 +1407,7 @@ var LoginManagerContent = { let form = LoginFormFactory.createFromField(aField); let doc = aField.ownerDocument; - let formOrigin = LoginHelper.getLoginOrigin(doc.documentURI); + let formOrigin = LoginUtils._getPasswordOrigin(doc.documentURI); let recipes = LoginRecipesContent.getRecipes(formOrigin, doc.defaultView); return this._getFormFields(form, false, recipes); @@ -1449,6 +1455,44 @@ var LoginManagerContent = { }, }; +var LoginUtils = { + /** + * Get the parts of the URL we want for identification. + * Strip out things like the userPass portion + */ + _getPasswordOrigin(uriString, allowJS) { + var realm = ""; + try { + var uri = Services.io.newURI(uriString); + + if (allowJS && uri.scheme == "javascript") { + return "javascript:"; + } + + // Build this manually instead of using prePath to avoid including the userPass portion. + realm = uri.scheme + "://" + uri.displayHostPort; + } catch (e) { + // bug 159484 - disallow url types that don't support a hostPort. + // (although we handle "javascript:..." as a special case above.) + log("Couldn't parse origin for", uriString, e); + realm = null; + } + + return realm; + }, + + _getActionOrigin(form) { + var uriString = form.action; + + // A blank or missing action submits to where it came from. + if (uriString == "") { + uriString = form.baseURI; + } // ala bug 297761 + + return this._getPasswordOrigin(uriString, true); + }, +}; + // nsIAutoCompleteResult implementation function UserAutoCompleteResult(aSearchString, matchingLogins, {isSecure, messageManager, isPasswordField}) { function loginSort(a, b) { @@ -1628,7 +1672,7 @@ var LoginFormFactory = { */ createFromForm(aForm) { let formLike = FormLikeFactory.createFromForm(aForm); - formLike.action = LoginHelper.getFormActionOrigin(aForm); + formLike.action = LoginUtils._getActionOrigin(aForm); let state = LoginManagerContent.stateForDocument(formLike.ownerDocument); state.loginFormRootElements.add(formLike.rootElement); @@ -1666,7 +1710,7 @@ var LoginFormFactory = { } let formLike = FormLikeFactory.createFromField(aField); - formLike.action = LoginHelper.getLoginOrigin(aField.ownerDocument.baseURI); + formLike.action = LoginUtils._getPasswordOrigin(aField.ownerDocument.baseURI); log("Created non-form FormLike for rootElement:", aField.ownerDocument.documentElement); let state = LoginManagerContent.stateForDocument(formLike.ownerDocument); diff --git a/toolkit/components/passwordmgr/test/unit/test_getPasswordOrigin.js b/toolkit/components/passwordmgr/test/unit/test_getPasswordOrigin.js index d3bc3ee64160..e2aa6cb8cb38 100644 --- a/toolkit/components/passwordmgr/test/unit/test_getPasswordOrigin.js +++ b/toolkit/components/passwordmgr/test/unit/test_getPasswordOrigin.js @@ -1,9 +1,10 @@ -/** - * Test for LoginHelper.getLoginOrigin +/* + * Test for LoginUtils._getPasswordOrigin */ "use strict"; +const LMCBackstagePass = ChromeUtils.import("resource://gre/modules/LoginManagerContent.jsm", null); const TESTCASES = [ ["javascript:void(0);", null], ["javascript:void(0);", "javascript:", true], @@ -22,6 +23,6 @@ const TESTCASES = [ ]; for (let [input, expected, allowJS] of TESTCASES) { - let actual = LoginHelper.getLoginOrigin(input, allowJS); + let actual = LMCBackstagePass.LoginUtils._getPasswordOrigin(input, allowJS); Assert.strictEqual(actual, expected, "Checking: " + input); } diff --git a/toolkit/components/passwordmgr/test/unit/test_getUserNameAndPasswordFields.js b/toolkit/components/passwordmgr/test/unit/test_getUserNameAndPasswordFields.js index eddb53866ab2..8ff2a9a8d0c6 100644 --- a/toolkit/components/passwordmgr/test/unit/test_getUserNameAndPasswordFields.js +++ b/toolkit/components/passwordmgr/test/unit/test_getUserNameAndPasswordFields.js @@ -1,9 +1,11 @@ -/** +/* * Test for LoginManagerContent.getUserNameAndPasswordFields */ "use strict"; +// Services.prefs.setBoolPref("signon.debug", true); + XPCOMUtils.defineLazyGlobalGetters(this, ["URL"]); const LMCBackstagePass = ChromeUtils.import("resource://gre/modules/LoginManagerContent.jsm", null); @@ -93,7 +95,7 @@ for (let tc of TESTCASES) { Object.defineProperty(document, "defaultView", { value: win, }); - let formOrigin = LoginHelper.getLoginOrigin(document.documentURI); + let formOrigin = LMCBackstagePass.LoginUtils._getPasswordOrigin(document.documentURI); LoginRecipesContent.cacheRecipes(formOrigin, win, new Set()); let actual = LoginManagerContent.getUserNameAndPasswordFields(input);