☠☠ backed out by a902fdf8f03c ☠ ☠ | |
author | Sam Foster <sfoster@mozilla.com> |
Wed, 06 Mar 2019 01:27:30 +0000 | |
changeset 462535 | 1ba42a5ba5d672533f395b15dc1b87e44b19b743 |
parent 462534 | 494abd5b60b8cab73684b24f29541f0d736d36a9 |
child 462536 | 96c2336e72c01dfb89146026708b89060003a19d |
push id | 79703 |
push user | sfoster@mozilla.com |
push date | Wed, 06 Mar 2019 04:37:49 +0000 |
treeherder | autoland@1ba42a5ba5d6 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | MattN |
bugs | 1531135 |
milestone | 67.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
|
--- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -4668,16 +4668,17 @@ pref("font.name-list.monospace.x-unicode # AIX #endif // Login Manager prefs pref("signon.rememberSignons", true); pref("signon.rememberSignons.visibilityToggle", true); pref("signon.autofillForms", true); +pref("signon.autofillForms.autocompleteOff", true); pref("signon.autofillForms.http", false); pref("signon.autologin.proxy", false); pref("signon.formlessCapture.enabled", true); pref("signon.privateBrowsingCapture.enabled", false); pref("signon.storeWhenAutocompleteOff", true); pref("signon.debug", false); pref("signon.recipes.path", "chrome://passwordmgr/content/recipes.json"); pref("signon.schemeUpgrades", false);
--- a/toolkit/components/passwordmgr/LoginHelper.jsm +++ b/toolkit/components/passwordmgr/LoginHelper.jsm @@ -35,16 +35,17 @@ var LoginHelper = { 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.autocompleteOff = Services.prefs.getBoolPref("signon.autofillForms.autocompleteOff"); 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");
--- a/toolkit/components/passwordmgr/LoginManagerContent.jsm +++ b/toolkit/components/passwordmgr/LoginManagerContent.jsm @@ -1114,17 +1114,16 @@ var LoginManagerContent = { clobberPassword = false, userTriggered = false, } = {}) { if (ChromeUtils.getClassName(form) === "HTMLFormElement") { throw new Error("_fillForm should only be called with FormLike objects"); } log("_fillForm", form.elements); - let ignoreAutocomplete = true; // Will be set to one of AUTOFILL_RESULT in the `try` block. let autofillResult = -1; const AUTOFILL_RESULT = { FILLED: 0, NO_PASSWORD_FIELD: 1, PASSWORD_DISABLED_READONLY: 2, NO_LOGINS_FIT: 3, NO_SAVED_LOGINS: 4, @@ -1206,23 +1205,16 @@ var LoginManagerContent = { // Prevent autofilling insecure forms. if (!userTriggered && !LoginHelper.insecureAutofill && !InsecurePasswordUtils.isFormSecure(form)) { log("not filling form since it's insecure"); autofillResult = AUTOFILL_RESULT.INSECURE; return; } - var isAutocompleteOff = false; - if (this._isAutocompleteDisabled(form) || - this._isAutocompleteDisabled(usernameField) || - this._isAutocompleteDisabled(passwordField)) { - isAutocompleteOff = true; - } - // Discard logins which have username/password values that don't // fit into the fields (as specified by the maxlength attribute). // The user couldn't enter these values anyway, and it helps // with sites that have an extra PIN to be entered (bug 391514) var maxUsernameLen = Number.MAX_VALUE; var maxPasswordLen = Number.MAX_VALUE; // If attribute wasn't set, default is -1. @@ -1244,19 +1236,21 @@ var LoginManagerContent = { }, this); if (logins.length == 0) { log("form not filled, none of the logins fit in the field"); autofillResult = AUTOFILL_RESULT.NO_LOGINS_FIT; return; } + const passwordACFieldName = passwordField.getAutocompleteInfo().fieldName; + // If the password field has the autocomplete value of "new-password" // and we're autofilling without user interaction, there's nothing to do. - if (!userTriggered && passwordField.getAutocompleteInfo().fieldName == "new-password") { + if (!userTriggered && passwordACFieldName == "new-password") { log("not filling form, password field has the autocomplete new-password value"); autofillResult = AUTOFILL_RESULT.PASSWORD_AUTOCOMPLETE_NEW_PASSWORD; return; } // Don't clobber an existing password. if (passwordField.value && !clobberPassword) { log("form not filled, the password field was already filled"); @@ -1317,26 +1311,28 @@ var LoginManagerContent = { // We will always have a selectedLogin at this point. if (!autofillForm) { log("autofillForms=false but form can be filled"); autofillResult = AUTOFILL_RESULT.NO_AUTOFILL_FORMS; return; } - if (isAutocompleteOff && !ignoreAutocomplete) { - log("Not filling the login because we're respecting autocomplete=off"); + if (!userTriggered && + passwordACFieldName == "off" && + !LoginHelper.autocompleteOff) { + log("Not autofilling the login because we're respecting autocomplete=off"); autofillResult = AUTOFILL_RESULT.AUTOCOMPLETE_OFF; return; } // Fill the form if (usernameField) { - // Don't modify the username field if it's disabled or readOnly so we preserve its case. + // Don't modify the username field if it's disabled or readOnly so we preserve its case. let disabledOrReadOnly = usernameField.disabled || usernameField.readOnly; let userNameDiffers = selectedLogin.username != usernameField.value; // Don't replace the username if it differs only in case, and the user triggered // this autocomplete. We assume that if it was user-triggered the entered text // is desired. let userEnteredDifferentCase = userTriggered && userNameDiffers && usernameField.value.toLowerCase() == selectedLogin.username.toLowerCase();
--- a/toolkit/components/passwordmgr/test/mochitest/mochitest.ini +++ b/toolkit/components/passwordmgr/test/mochitest/mochitest.ini @@ -46,16 +46,19 @@ skip-if = toolkit == 'android' # autocom [test_basic_form_0pw.html] [test_basic_form_1pw.html] [test_basic_form_1pw_2.html] [test_basic_form_2pw_1.html] [test_basic_form_2pw_2.html] [test_basic_form_3pw_1.html] [test_basic_form_autocomplete.html] skip-if = toolkit == 'android' # android:autocomplete. +[test_basic_form_honor_autocomplete_off.html] +scheme = https +skip-if = toolkit == 'android' # android:autocomplete. [test_insecure_form_field_autocomplete.html] skip-if = toolkit == 'android' || os == 'linux' # android:autocomplete., linux: bug 1325778 [test_password_field_autocomplete.html] skip-if = toolkit == 'android' # android:autocomplete. [test_insecure_form_field_no_saved_login.html] skip-if = toolkit == 'android' # android:autocomplete. [test_basic_form_html5.html] [test_basic_form_pwevent.html]
new file mode 100644 --- /dev/null +++ b/toolkit/components/passwordmgr/test/mochitest/test_basic_form_honor_autocomplete_off.html @@ -0,0 +1,167 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test login autofill autocomplete when signon.autofillForms.autocompleteOff is false</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script> + <script type="text/javascript" src="/tests/SimpleTest/AddTask.js"></script> + <script type="text/javascript" src="../../../satchel/test/satchel_common.js"></script> + <script type="text/javascript" src="pwmgr_common.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +Login Manager test: autofilling when autocomplete=off + +<script> +const chromeScript = runChecksAfterCommonInit(); + +const setupScript = runInParent(function setup() { + const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"); + + // Create some logins just for this form, since we'll be deleting them. + const nsLoginInfo = Components.Constructor("@mozilla.org/login-manager/loginInfo;1", + Ci.nsILoginInfo, "init"); + assert.ok(nsLoginInfo != null, "nsLoginInfo constructor"); + + const login = new nsLoginInfo("https://example.com", "https://autocomplete2", null, + "singleuser", "singlepass", "uname", "pword"); + Services.logins.addLogin(login); +}); +</script> +<p id="display"></p> + +<!-- we presumably can't hide the content for this test. --> +<div id="content"> + <!-- test single logins, with autocomplete=off set --> + <form id="form1" action="https://autocomplete2" onsubmit="return false;"> + <input type="text" name="uname"> + <input type="password" name="pword" autocomplete="off"> + <button type="submit">Submit</button> + </form> + + <form id="form2" action="https://autocomplete2" onsubmit="return false;"> + <input type="text" name="uname" autocomplete="off"> + <input type="password" name="pword"> + <button type="submit">Submit</button> + </form> + + <form id="form3" action="https://autocomplete2" onsubmit="return false;" autocomplete="off"> + <input type="text" name="uname"> + <input type="password" name="pword"> + <button type="submit">Submit</button> + </form> + + <form id="form4" action="https://autocomplete2" onsubmit="return false;"> + <input type="text" name="uname" autocomplete="off"> + <input type="password" name="pword" autocomplete="off"> + <button type="submit">Submit</button> + </form> + + <!-- control --> + <form id="form5" action="https://autocomplete2" onsubmit="return false;"> + <input type="text" name="uname"> + <input type="password" name="pword"> + <button type="submit">Submit</button> + </form> + +<pre id="test"> +<script class="testbody" type="text/javascript"> +/** Test for Login Manager: multiple login autocomplete. **/ +let {ContentTaskUtils} = SpecialPowers.Cu.import("resource://testing-common/ContentTaskUtils.jsm", {}); + +// Set the pref before the document loads. +SpecialPowers.setBoolPref("signon.autofillForms.autocompleteOff", false); + +SimpleTest.registerCleanupFunction(() => { + SpecialPowers.clearUserPref("signon.autofillForms.autocompleteOff"); +}); + +// Check for expected username/password in form. +function checkFormValues(form, expectedUsername, expectedPassword) { + let uname = form.querySelector("[name='uname']"); + let pword = form.querySelector("[name='pword']"); + is(uname.value, expectedUsername, `Checking ${form.id} username is: ${expectedUsername}`); + is(pword.value, expectedPassword, `Checking ${form.id} password is: ${expectedPassword}`); +} + +async function autoCompleteFieldsFromFirstMatch(form) { + // trigger autocomplete from the username field + await SimpleTest.promiseFocus(form.ownerGlobal); + let uname = form.querySelector("[name='uname']"); + let shownPromise; + shownPromise = promiseACShown(); + uname.focus(); + await ContentTaskUtils.waitForCondition(() => { + return document.activeElement == uname; + }, "The textbox should have been focused"); + await shownPromise; + + let formFilled = promiseFormsProcessed(); + await synthesizeKey("KEY_ArrowDown"); // open + await synthesizeKey("KEY_Enter"); + await formFilled; + await Promise.resolve(); +} + +add_task(async function setup() { + listenForUnexpectedPopupShown(); +}); + +/* Tests for autofill of single-user forms for when we honor autocomplete=off on password fields */ +add_task(async function test_form1_honor_password_autocomplete_off() { + await SimpleTest.promiseFocus(window); + // With the pref toggled off, and with autocomplete=off on the password field, + // we expect not to have autofilled this form + let form = document.getElementById("form1"); + ok(form, "found form under test"); + checkFormValues(form, "", ""); + + // ..but it should autocomplete just fine + await autoCompleteFieldsFromFirstMatch(form); + checkFormValues(form, "singleuser", "singlepass"); +}); + +add_task(async function test_form2_honor_password_autocomplete_off() { + await SimpleTest.promiseFocus(window); + // With the pref toggled off, and with autocomplete=off on the username field, + // we expect to have autofilled this form + let form = document.getElementById("form2"); + ok(form, "found form under test"); + checkFormValues(form, "singleuser", "singlepass"); +}); + +add_task(async function test_form3_honor_password_autocomplete_off() { + await SimpleTest.promiseFocus(window); + // With the pref toggled off, and with autocomplete=off on the form, + // we expect to have autofilled this form + let form = document.getElementById("form3"); + ok(form, "found form under test"); + checkFormValues(form, "singleuser", "singlepass"); +}); + +add_task(async function test_form4_honor_password_autocomplete_off() { + await SimpleTest.promiseFocus(window); + // With the pref toggled off, and autocomplete=off on the username and password field, + // we expect not to have autofilled this form + let form = document.getElementById("form4"); + ok(form, "found form under test"); + checkFormValues(form, "", ""); + + // ..but it should autocomplete just fine + await autoCompleteFieldsFromFirstMatch(form); + checkFormValues(form, "singleuser", "singlepass"); +}); + +add_task(async function test_form5() { + await SimpleTest.promiseFocus(window); + // (this is a control, w/o autocomplete=off, to ensure the login + // that was being suppressed would have been filled in otherwise) + let form = document.getElementById("form5"); + ok(form, "found form under test"); + checkFormValues(form, "singleuser", "singlepass"); +}); +</script> +</pre> +</body> +</html>