mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 05:11:16 +00:00
Bug 1189524 - Provide visual feedback to the user when login fields are autofilled and autocompleted. r=MattN
Differential Revision: https://phabricator.services.mozilla.com/D18153 --HG-- extra : transplant_source : %B6%06%3A%9B%99%FBhi%06_%93%EC%1C%89%EE%D8%C8%C6%D0%96
This commit is contained in:
parent
dba455e2b4
commit
763eb39bf4
@ -61,7 +61,7 @@ addEventListener("DOMAutoComplete", function(event) {
|
||||
if (shouldIgnoreLoginManagerEvent(event)) {
|
||||
return;
|
||||
}
|
||||
LoginManagerContent.onUsernameInput(event);
|
||||
LoginManagerContent.onDOMAutoComplete(event);
|
||||
});
|
||||
|
||||
ContentMetaHandler.init(this);
|
||||
|
@ -214,15 +214,7 @@ BrowserCLH.prototype = {
|
||||
if (shouldIgnoreLoginManagerEvent(event)) {
|
||||
return;
|
||||
}
|
||||
this.LoginManagerContent.onUsernameInput(event);
|
||||
}, options);
|
||||
|
||||
aWindow.addEventListener("blur", event => {
|
||||
if (ChromeUtils.getClassName(event.target) !== "HTMLInputElement" ||
|
||||
shouldIgnoreLoginManagerEvent(event)) {
|
||||
return;
|
||||
}
|
||||
this.LoginManagerContent.onUsernameInput(event);
|
||||
this.LoginManagerContent.onDOMAutoComplete(event);
|
||||
}, options);
|
||||
|
||||
aWindow.addEventListener("pageshow", event => {
|
||||
|
@ -10,6 +10,7 @@ var EXPORTED_SYMBOLS = [ "LoginManagerContent",
|
||||
|
||||
const PASSWORD_INPUT_ADDED_COALESCING_THRESHOLD_MS = 1;
|
||||
const AUTOCOMPLETE_AFTER_RIGHT_CLICK_THRESHOLD_MS = 400;
|
||||
const AUTOFILL_STATE = "-moz-autofill";
|
||||
|
||||
const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
@ -605,9 +606,9 @@ var LoginManagerContent = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Listens for DOMAutoComplete and blur events on an input field.
|
||||
* Listens for DOMAutoComplete event on login form.
|
||||
*/
|
||||
onUsernameInput(event) {
|
||||
onDOMAutoComplete(event) {
|
||||
if (!event.isTrusted) {
|
||||
return;
|
||||
}
|
||||
@ -616,21 +617,27 @@ var LoginManagerContent = {
|
||||
return;
|
||||
}
|
||||
|
||||
var acInputField = event.target;
|
||||
let acInputField = event.target;
|
||||
|
||||
// This is probably a bit over-conservatative.
|
||||
if (ChromeUtils.getClassName(acInputField.ownerDocument) != "HTMLDocument") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!LoginHelper.isUsernameFieldType(acInputField)) {
|
||||
if (!LoginFormFactory.createFromField(acInputField)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var acForm = LoginFormFactory.createFromField(acInputField);
|
||||
if (!acForm) {
|
||||
return;
|
||||
if (LoginHelper.isUsernameFieldType(acInputField)) {
|
||||
this.onUsernameInput(event);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Calls fill form on the username field.
|
||||
*/
|
||||
onUsernameInput(event) {
|
||||
let acInputField = event.target;
|
||||
|
||||
// If the username is blank, bail out now -- we don't want
|
||||
// fillForm() to try filling in a login without a username
|
||||
@ -641,6 +648,7 @@ var LoginManagerContent = {
|
||||
|
||||
log("onUsernameInput from", event.type);
|
||||
|
||||
let acForm = LoginFormFactory.createFromField(acInputField);
|
||||
let doc = acForm.ownerDocument;
|
||||
let formOrigin = LoginUtils._getPasswordOrigin(doc.documentURI);
|
||||
let recipes = LoginRecipesContent.getRecipes(formOrigin, doc.defaultView);
|
||||
@ -998,6 +1006,28 @@ var LoginManagerContent = {
|
||||
});
|
||||
},
|
||||
|
||||
/** Remove login field highlight when its value is cleared or overwritten.
|
||||
*/
|
||||
_removeFillFieldHighlight(event) {
|
||||
let winUtils = event.target.ownerGlobal.windowUtils;
|
||||
winUtils.removeManuallyManagedState(event.target, AUTOFILL_STATE);
|
||||
},
|
||||
|
||||
/**
|
||||
* Highlight login fields on autocomplete or autofill on page load.
|
||||
* @param {Node} element that needs highlighting.
|
||||
*/
|
||||
_highlightFilledField(element) {
|
||||
let winUtils = element.ownerGlobal.windowUtils;
|
||||
|
||||
winUtils.addManuallyManagedState(element, AUTOFILL_STATE);
|
||||
// Remove highlighting when the field is changed.
|
||||
element.addEventListener("input", this._removeFillFieldHighlight, {
|
||||
mozSystemGroup: true,
|
||||
once: true,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Attempt to find the username and password fields in a form, and fill them
|
||||
* in using the provided logins and recipes.
|
||||
@ -1248,8 +1278,12 @@ var LoginManagerContent = {
|
||||
let userEnteredDifferentCase = userTriggered && userNameDiffers &&
|
||||
usernameField.value.toLowerCase() == selectedLogin.username.toLowerCase();
|
||||
|
||||
if (!disabledOrReadOnly && !userEnteredDifferentCase && userNameDiffers) {
|
||||
usernameField.setUserInput(selectedLogin.username);
|
||||
if (!disabledOrReadOnly) {
|
||||
if (!userEnteredDifferentCase && userNameDiffers) {
|
||||
usernameField.setUserInput(selectedLogin.username);
|
||||
}
|
||||
|
||||
this._highlightFilledField(usernameField);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1267,6 +1301,8 @@ var LoginManagerContent = {
|
||||
this.stateForDocument(doc).fillsByRootElement.set(form.rootElement, autoFilledLogin);
|
||||
}
|
||||
|
||||
this._highlightFilledField(passwordField);
|
||||
|
||||
log("_fillForm succeeded");
|
||||
autofillResult = AUTOFILL_RESULT.FILLED;
|
||||
|
||||
|
@ -21,11 +21,16 @@ support-files =
|
||||
../authenticate.sjs
|
||||
skip-if = toolkit == 'android' && !isFennec # Don't run on GeckoView
|
||||
|
||||
[test_autocomplete_highlight.html]
|
||||
scheme = https
|
||||
skip-if = toolkit == 'android' # autocomplete
|
||||
[test_autocomplete_https_upgrade.html]
|
||||
skip-if = toolkit == 'android' # autocomplete
|
||||
[test_autocomplete_sandboxed.html]
|
||||
scheme = https
|
||||
skip-if = toolkit == 'android' # autocomplete
|
||||
[test_autofill_highlight.html]
|
||||
scheme = https
|
||||
[test_autofill_https_upgrade.html]
|
||||
skip-if = toolkit == 'android' # Bug 1259768
|
||||
[test_autofill_sandboxed.html]
|
||||
|
@ -0,0 +1,81 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test form field autofill highlight</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>
|
||||
<script>
|
||||
runInParent(function initLogins() {
|
||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
let login1 = Cc["@mozilla.org/login-manager/loginInfo;1"]
|
||||
.createInstance(Ci.nsILoginInfo);
|
||||
login1.init("https://example.com", "https://autocomplete", null,
|
||||
"user1", "pass1", "", "");
|
||||
|
||||
let login2 = Cc["@mozilla.org/login-manager/loginInfo;1"]
|
||||
.createInstance(Ci.nsILoginInfo);
|
||||
login2.init("https://example.com", "https://autocomplete", null,
|
||||
"user2", "pass2", "", "");
|
||||
|
||||
Services.logins.addLogin(login1);
|
||||
Services.logins.addLogin(login2);
|
||||
});
|
||||
|
||||
</script>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content">
|
||||
<form id="form1" action="https://autocomplete" onsubmit="return false;">
|
||||
<input type="text" id="uname">
|
||||
<input type="password" id="pword">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
<pre id="test">
|
||||
<script>
|
||||
let {ContentTaskUtils} = SpecialPowers.Cu.import("resource://testing-common/ContentTaskUtils.jsm", {});
|
||||
|
||||
add_task(async function test_field_highlight_on_autocomplete() {
|
||||
// Test username autocomplete.
|
||||
let username = document.getElementById("uname");
|
||||
let password = document.getElementById("pword");
|
||||
|
||||
username.focus();
|
||||
|
||||
let shownPromise = promiseACShown();
|
||||
synthesizeKey("KEY_ArrowDown");
|
||||
await shownPromise;
|
||||
synthesizeKey("KEY_ArrowDown");
|
||||
await synthesizeKey("KEY_Enter");
|
||||
|
||||
await ContentTaskUtils.waitForCondition(() => {
|
||||
return document.defaultView.getComputedStyle(username).getPropertyValue("filter") !== "none";
|
||||
}, "Highlight was successfully applied to the username field on username autocomplete");
|
||||
|
||||
isnot(document.defaultView.getComputedStyle(password).getPropertyValue("filter"), "none",
|
||||
"Highlight was successfully applied to the password field on username autocomplete");
|
||||
|
||||
// Clear existing highlight on login fields. We check by pressing the tab key after backspace
|
||||
// (by shifting focus to the next element) because the tab key is known to cause a bug where the
|
||||
// highlight is applied once again.
|
||||
username.focus();
|
||||
synthesizeKey("KEY_Backspace");
|
||||
synthesizeKey("KEY_Tab");
|
||||
is(document.defaultView.getComputedStyle(username).getPropertyValue("filter"), "none",
|
||||
"Highlight was successfully removed on the username field");
|
||||
|
||||
synthesizeKey("KEY_Backspace");
|
||||
synthesizeKey("KEY_Tab");
|
||||
is(document.defaultView.getComputedStyle(password).getPropertyValue("filter"), "none",
|
||||
"Highlight was successfully removed on the password field");
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -0,0 +1,67 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test form field autofill highlight</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="pwmgr_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<script>
|
||||
runInParent(function initLogins() {
|
||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
let login1 = Cc["@mozilla.org/login-manager/loginInfo;1"]
|
||||
.createInstance(Ci.nsILoginInfo);
|
||||
login1.init("https://example.com", "https://autofill", null,
|
||||
"user1", "pass1", "", "");
|
||||
|
||||
Services.logins.addLogin(login1);
|
||||
});
|
||||
</script>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
|
||||
<div id="content">
|
||||
<form id="form1" action="https://autofill" onsubmit="return false;">
|
||||
<input type="text" id="uname">
|
||||
<input type="password" id="pword">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
<pre id="test">
|
||||
<script>
|
||||
let {ContentTaskUtils} = SpecialPowers.Cu.import("resource://testing-common/ContentTaskUtils.jsm", {});
|
||||
|
||||
add_task(async function test_field_highlight_on_autofill() {
|
||||
let username = document.getElementById("uname");
|
||||
let password = document.getElementById("pword");
|
||||
|
||||
await ContentTaskUtils.waitForCondition(() => {
|
||||
return document.defaultView.getComputedStyle(username).getPropertyValue("filter") !== "none";
|
||||
}, "Highlight was successfully applied to the username field on page load autofill");
|
||||
|
||||
isnot(document.defaultView.getComputedStyle(password).getPropertyValue("filter"), "none",
|
||||
"Highlight was successfully applied to the password field on page load autofill");
|
||||
|
||||
// Test that initiating a change on the input value will remove the highlight. We check by pressing
|
||||
// the tab key after backspace(by shifting focus to the next element) because the tab key is known to
|
||||
// cause a bug where the highlight is applied once again.
|
||||
username.focus();
|
||||
synthesizeKey("KEY_Backspace");
|
||||
synthesizeKey("KEY_Tab");
|
||||
|
||||
let computedStyle = document.defaultView.getComputedStyle(username);
|
||||
is(computedStyle.getPropertyValue("filter"), "none", "Highlight was successfully removed on change in value of username input element");
|
||||
|
||||
synthesizeKey("KEY_Backspace");
|
||||
synthesizeKey("KEY_Tab");
|
||||
computedStyle = document.defaultView.getComputedStyle(password);
|
||||
is(computedStyle.getPropertyValue("filter"), "none", "Highlight was successfully removed on change in value of password input element");
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -6,7 +6,6 @@
|
||||
<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>
|
||||
|
Loading…
Reference in New Issue
Block a user