diff --git a/toolkit/components/passwordmgr/src/nsLoginManager.js b/toolkit/components/passwordmgr/src/nsLoginManager.js index 43cfd819b45..9d82f85405c 100644 --- a/toolkit/components/passwordmgr/src/nsLoginManager.js +++ b/toolkit/components/passwordmgr/src/nsLoginManager.js @@ -715,6 +715,20 @@ LoginManager.prototype = { }, + /* + * _isAutoCompleteDisabled + * + * Returns true if the page requests autocomplete be disabled for the + * specified form input. + */ + _isAutocompleteDisabled : function (element) { + if (element && element.hasAttribute("autocomplete") && + element.getAttribute("autocomplete").toLowerCase() == "off") + return true; + + return false; + }, + /* * _onFormSubmit * @@ -725,15 +739,6 @@ LoginManager.prototype = { */ _onFormSubmit : function (form) { - // local helper function - function autocompleteDisabled(element) { - if (element && element.hasAttribute("autocomplete") && - element.getAttribute("autocomplete").toLowerCase() == "off") - return true; - - return false; - }; - // local helper function function getPrompter(aWindow) { var prompterSvc = Cc["@mozilla.org/login-manager/prompter;1"]. @@ -769,10 +774,10 @@ LoginManager.prototype = { // Check for autocomplete=off attribute. We don't use it to prevent // autofilling (for existing logins), but won't save logins when it's // present. - if (autocompleteDisabled(form) || - autocompleteDisabled(usernameField) || - autocompleteDisabled(newPasswordField) || - autocompleteDisabled(oldPasswordField)) { + if (this._isAutocompleteDisabled(form) || + this._isAutocompleteDisabled(usernameField) || + this._isAutocompleteDisabled(newPasswordField) || + this._isAutocompleteDisabled(oldPasswordField)) { this.log("(form submission ignored -- autocomplete=off found)"); return; } @@ -1014,7 +1019,20 @@ LoginManager.prototype = { if (usernameField) this._attachToInput(usernameField); - if (autofillForm) { + // If the form has an autocomplete=off attribute in play, don't + // fill in the login automatically. We check this after attaching + // the autocomplete stuff to the username field, so the user can + // still manually select a login to be filled in. + var isFormDisabled = false; + if (this._isAutocompleteDisabled(form) || + this._isAutocompleteDisabled(usernameField) || + this._isAutocompleteDisabled(passwordField)) { + + isFormDisabled = true; + this.log("form[" + i + "]: not filled, has autocomplete=off"); + } + + if (autofillForm && !isFormDisabled) { if (usernameField && usernameField.value) { // If username was specified in the form, only fill in the diff --git a/toolkit/components/passwordmgr/test/test_basic_form_autocomplete.html b/toolkit/components/passwordmgr/test/test_basic_form_autocomplete.html index 2fe365aae8e..61202bd9419 100644 --- a/toolkit/components/passwordmgr/test/test_basic_form_autocomplete.html +++ b/toolkit/components/passwordmgr/test/test_basic_form_autocomplete.html @@ -15,14 +15,44 @@ Login Manager test: multiple login autocomplete
@@ -68,12 +98,18 @@ var login4 = new nsLoginInfo( "http://localhost:8888", "http://autocomplete:8888", null, "zzzuser4", "zzzpass4", "uname", "pword"); +// login 5 only used in the single-user forms +var login5 = new nsLoginInfo( + "http://localhost:8888", "http://autocomplete2", null, + "singleuser5", "singlepass5", "uname", "pword"); + // try/catch in case someone runs the tests manually, twice. try { pwmgr.addLogin(login1); pwmgr.addLogin(login2); pwmgr.addLogin(login3); pwmgr.addLogin(login4); + pwmgr.addLogin(login5); } catch (e) { ok(false, "addLogin threw: " + e); } @@ -89,8 +125,9 @@ function restoreForm() { // Check for expected username/password in form. function checkACForm(expectedUsername, expectedPassword) { - is(uname.value, expectedUsername, "Checking form username"); - is(pword.value, expectedPassword, "Checking form password"); + var formID = uname.parentNode.id; + is(uname.value, expectedUsername, "Checking " + formID + " username"); + is(pword.value, expectedPassword, "Checking " + formID + " password"); } @@ -407,8 +444,100 @@ function runTest(testNum) { checkACForm("", ""); numLogins = pwmgr.countLogins("http://localhost:8888", "http://autocomplete:8888", null); is(numLogins, 0, "Correct number of logins after deleting one"); + testNum = 99; + break; + + + /* Tests for single-user forms with autocomplete=off */ + + case 100: + // Turn our attention to form2 + uname = $_(2, "uname"); + pword = $_(2, "pword"); + checkACForm("", ""); + + // Trigger autocomplete popup + restoreForm(); + doKey("down"); + break; + + case 101: + // Check first entry + doKey("down"); + checkACForm("", ""); // value shouldn't update + doKey("return"); // not "enter"! + checkACForm("singleuser5", "singlepass5"); + break; + + case 102: + // Turn our attention to form3 + uname = $_(3, "uname"); + pword = $_(3, "pword"); + checkACForm("", ""); + + // Trigger autocomplete popup + restoreForm(); + doKey("down"); + break; + + case 103: + // Check first entry + doKey("down"); + checkACForm("", ""); // value shouldn't update + doKey("return"); // not "enter"! + checkACForm("singleuser5", "singlepass5"); + break; + + case 104: + // Turn our attention to form4 + uname = $_(4, "uname"); + pword = $_(4, "pword"); + checkACForm("", ""); + + // Trigger autocomplete popup + restoreForm(); + doKey("down"); + break; + + case 105: + // Check first entry + doKey("down"); + checkACForm("", ""); // value shouldn't update + doKey("return"); // not "enter"! + checkACForm("singleuser5", "singlepass5"); + break; + + case 106: + // Turn our attention to form5 + uname = $_(5, "uname"); + pword = $_(5, "pword"); + checkACForm("", ""); + + // Trigger autocomplete popup + restoreForm(); + doKey("down"); + break; + + case 107: + // Check first entry + doKey("down"); + checkACForm("", ""); // value shouldn't update + doKey("return"); // not "enter"! + checkACForm("singleuser5", "singlepass5"); + break; + + case 108: + // Turn our attention to form6 + // (this is a control, w/o autocomplete=off, to ensure the login + // that was being suppressed would have been filled in otherwise) + uname = $_(6, "uname"); + pword = $_(6, "pword"); + checkACForm("singleuser5", "singlepass5"); + + /* FALLTHRU */ default: + pwmgr.removeLogin(login5); SimpleTest.finish(); return; } diff --git a/toolkit/components/passwordmgr/test/test_bug_227640.html b/toolkit/components/passwordmgr/test/test_bug_227640.html index 07afdced49d..f67e33aaaf8 100644 --- a/toolkit/components/passwordmgr/test/test_bug_227640.html +++ b/toolkit/components/passwordmgr/test/test_bug_227640.html @@ -161,10 +161,13 @@ function startTest() { numStartingLogins = countLogins(); ok(numStartingLogins > 0, "counting logins at start"); - // Check first set of forms, which should be filled by pwmgr. + // Check first set of forms, which should not be filled by pwmgr. for (var i = 1; i <= 7; i++) { - is($_(i, "uname").value, "testuser", "Checking for filled username " + i); - is($_(i, "pword").value, "testpass", "Checking for filled password " + i); + is($_(i, "uname").value, "", "Checking for unfilled username " + i); + is($_(i, "pword").value, "", "Checking for unfilled password " + i); + // Set the field values to that of an existing login + $_(i, "uname").value = "testuser"; + $_(i, "pword").value = "testpass"; } // Check second set of forms, which should have preset values (and are unknown to pwmgr). diff --git a/toolkit/components/satchel/src/nsFormFillController.cpp b/toolkit/components/satchel/src/nsFormFillController.cpp index 4f0e8eb8caa..642674f0d31 100644 --- a/toolkit/components/satchel/src/nsFormFillController.cpp +++ b/toolkit/components/satchel/src/nsFormFillController.cpp @@ -608,15 +608,21 @@ nsFormFillController::Focus(nsIDOMEvent* aEvent) nsAutoString autocomplete; input->GetAttribute(NS_LITERAL_STRING("autocomplete"), autocomplete); + + PRInt32 dummy; + PRBool isPwmgrInput = PR_FALSE; + if (mPwmgrInputs.Get(input, &dummy)) + isPwmgrInput = PR_TRUE; + if (type.LowerCaseEqualsLiteral("text") && !isReadOnly && - !autocomplete.LowerCaseEqualsLiteral("off")) { + (!autocomplete.LowerCaseEqualsLiteral("off") || isPwmgrInput)) { nsCOMPtrform; input->GetForm(getter_AddRefs(form)); if (form) form->GetAttribute(NS_LITERAL_STRING("autocomplete"), autocomplete); - if (!form || !autocomplete.LowerCaseEqualsLiteral("off")) + if (!form || !autocomplete.LowerCaseEqualsLiteral("off") || isPwmgrInput) StartControllingInput(input); }