Bug 1217134 - Replace show password placeholder with conventional show password checkbox
WIP patch
MozReview-Commit-ID: JkkO2wjwNB6
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -1301,32 +1301,8 @@ toolbarpaletteitem[place="palette"][hidd
.popup-notification-invalid-input {
box-shadow: 0 0 1.5px 1px red;
}
.popup-notification-invalid-input[focused] {
box-shadow: 0 0 2px 2px rgba(255,0,0,0.4);
}
-
-#password-notification-password::after {
- color: hsl(0,0%,60%);
- content: attr(show-content);
- pointer-events: none;
- position: absolute;
- right: 0;
- transition: color 250ms;
-}
-
-#password-notification-password:hover::after {
- color: hsl(210,100%,50%);
-}
-
-#password-notification-password[focused]::after {
- content: none;
-}
-
-/* Bug 1175941: Disable the transition on 10.10 due to flickering, possibly due to an OS X bug. */
-@media (-moz-mac-yosemite-theme) {
- #password-notification-password::after {
- transition: none;
- }
-}
--- a/browser/base/content/popup-notifications.inc
+++ b/browser/base/content/popup-notifications.inc
@@ -50,16 +50,17 @@
<label id="pointerLock-cancel">&pointerLock.notification.message;</label>
</popupnotificationcontent>
</popupnotification>
<popupnotification id="password-notification" hidden="true">
<popupnotificationcontent orient="vertical">
<textbox id="password-notification-username"/>
<textbox id="password-notification-password" type="password" show-content=""/>
+ <checkbox id="password-notification-visibilityToggle" hidden="true"/>
</popupnotificationcontent>
</popupnotification>
<stack id="login-fill-doorhanger" hidden="true">
<vbox id="login-fill-mainview">
<description id="login-fill-testing"
value="Thanks for testing the login fill doorhanger!"/>
<textbox id="login-fill-filter"/>
--- a/toolkit/components/passwordmgr/nsLoginManagerPrompter.js
+++ b/toolkit/components/passwordmgr/nsLoginManagerPrompter.js
@@ -850,77 +850,51 @@ LoginManagerPrompter.prototype = {
let writeDataToUI = () => {
// setAttribute is used since the <textbox> binding may not be attached yet.
chromeDoc.getElementById("password-notification-username")
.setAttribute("placeholder", usernamePlaceholder);
chromeDoc.getElementById("password-notification-username")
.setAttribute("value", login.username);
+ let showCheckbox = chromeDoc.getElementById("password-notification-visibilityToggle");
+ showCheckbox.removeAttribute("checked");
let passwordField = chromeDoc.getElementById("password-notification-password");
// Ensure the type is reset so the field is masked.
passwordField.setAttribute("type", "password");
passwordField.setAttribute("value", login.password);
- if (Services.prefs.getBoolPref("signon.rememberSignons.visibilityToggle")) {
- passwordField.setAttribute("show-content", showPasswordPlaceholder);
- } else {
- passwordField.setAttribute("show-content", "");
- }
updateButtonLabel();
};
let readDataFromUI = () => {
login.username =
chromeDoc.getElementById("password-notification-username").value;
login.password =
chromeDoc.getElementById("password-notification-password").value;
};
let onInput = () => {
readDataFromUI();
updateButtonLabel();
};
- let onPasswordFocus = (focusEvent) => {
+ let onVisibilityToggle = (commandEvent) => {
let passwordField = chromeDoc.getElementById("password-notification-password");
// Gets the caret position before changing the type of the textbox
let selectionStart = passwordField.selectionStart;
let selectionEnd = passwordField.selectionEnd;
- if (focusEvent.rangeParent != null) {
- // Check for a click over the SHOW placeholder
- selectionStart = passwordField.value.length;
- selectionEnd = passwordField.value.length;
+ // Use setAttribute in case the <textbox> binding isn't applied.
+ passwordField.setAttribute("type", commandEvent.target.checked ? "" : "password");
+ if (!passwordField.hasAttribute("focused")) {
+ return;
}
- passwordField.setAttribute("type", "");
passwordField.selectionStart = selectionStart;
passwordField.selectionEnd = selectionEnd;
};
- let onPasswordBlur = () => {
- // Use setAttribute in case the <textbox> binding isn't applied.
- chromeDoc.getElementById("password-notification-password").setAttribute("type", "password");
- };
-
- let onNotificationClick = (clickEvent) => {
- // Removes focus from textboxes when we click elsewhere on the doorhanger.
- let focusedElement = Services.focus.focusedElement;
- if (!focusedElement || focusedElement.nodeName != "html:input") {
- // No input is focused so we don't need to blur
- return;
- }
-
- let focusedBindingParent = chromeDoc.getBindingParent(focusedElement);
- if (!focusedBindingParent || focusedBindingParent.nodeName != "textbox" ||
- clickEvent.explicitOriginalTarget == focusedBindingParent) {
- // The focus wasn't in a textbox or the click was in the focused textbox.
- return;
- }
- focusedBindingParent.blur();
- };
-
let persistData = () => {
let foundLogins = Services.logins.findLogins({}, login.hostname,
login.formSubmitURL,
login.httpRealm);
let logins = this._filterUpdatableLogins(login, foundLogins);
if (logins.length == 0) {
// The original login we have been provided with might have its own
@@ -968,17 +942,18 @@ LoginManagerPrompter.prototype = {
callback: () => {
histogram.add(PROMPT_NEVER);
Services.logins.setLoginSavingEnabled(login.hostname, false);
browser.focus();
}
}] : null;
let usernamePlaceholder = this._getLocalizedString("noUsernamePlaceholder");
- let showPasswordPlaceholder = this._getLocalizedString("showPasswordPlaceholder");
+ let showPasswordLabel = this._getLocalizedString("showPasswordLabel");
+ let showPasswordAccessKey = this._getLocalizedString("showPasswordAccessKey");
let displayHost = this._getShortDisplayHost(login.hostname);
this._getPopupNote().show(
browser,
"password",
promptMsg,
"password-notification-icon",
mainAction,
@@ -992,42 +967,40 @@ LoginManagerPrompter.prototype = {
switch (topic) {
case "showing":
currentNotification = this;
chromeDoc.getElementById("password-notification-username")
.addEventListener("input", onInput);
chromeDoc.getElementById("password-notification-password")
.addEventListener("input", onInput);
if (Services.prefs.getBoolPref("signon.rememberSignons.visibilityToggle")) {
- chromeDoc.getElementById("password-notification-password")
- .addEventListener("focus", onPasswordFocus);
+ chromeDoc.getElementById("password-notification-visibilityToggle")
+ .addEventListener("command", onVisibilityToggle);
+ chromeDoc.getElementById("password-notification-visibilityToggle")
+ .setAttribute("label", showPasswordLabel);
+ chromeDoc.getElementById("password-notification-visibilityToggle")
+ .setAttribute("accesskey", showPasswordAccessKey);
+ chromeDoc.getElementById("password-notification-visibilityToggle")
+ .removeAttribute("hidden");
}
- chromeDoc.getElementById("password-notification-password")
- .addEventListener("blur", onPasswordBlur);
break;
case "shown":
- chromeDoc.getElementById("notification-popup")
- .addEventListener("click", onNotificationClick);
writeDataToUI();
break;
case "dismissed":
readDataFromUI();
// Fall through.
case "removed":
currentNotification = null;
- chromeDoc.getElementById("notification-popup")
- .removeEventListener("click", onNotificationClick);
chromeDoc.getElementById("password-notification-username")
.removeEventListener("input", onInput);
chromeDoc.getElementById("password-notification-password")
.removeEventListener("input", onInput);
- chromeDoc.getElementById("password-notification-password")
- .removeEventListener("focus", onPasswordFocus);
- chromeDoc.getElementById("password-notification-password")
- .removeEventListener("blur", onPasswordBlur);
+ chromeDoc.getElementById("password-notification-visibilityToggle")
+ .removeEventListener("command", onVisibilityToggle);
break;
}
return false;
},
}
);
},
--- a/toolkit/components/passwordmgr/test/browser/browser_notifications.js
+++ b/toolkit/components/passwordmgr/test/browser/browser_notifications.js
@@ -370,24 +370,27 @@ add_task(function* test_empty_password()
doc.getElementById("form-basic-password").value = "p";
doc.getElementById("form-basic").submit();
});
yield promiseShown;
let notificationElement = PopupNotifications.panel.childNodes[0];
let passwordTextbox = notificationElement.querySelector("#password-notification-password");
+ let checkbox = notificationElement.querySelector("#password-notification-visibilityToggle");
+ yield EventUtils.synthesizeMouseAtCenter(checkbox, {});
+
+ // Wait for the textbox type to change
+ yield ContentTaskUtils.waitForCondition(() => passwordTextbox.type == "", "Password textbox changed type");
+
// Focus the password textbox
let focusPassword = BrowserTestUtils.waitForEvent(passwordTextbox, "focus");
passwordTextbox.focus();
yield focusPassword;
- // Wait for the textbox type to change
- yield ContentTaskUtils.waitForCondition(() => passwordTextbox.type == "", "Password textbox changed type");
-
// Synthesize input to empty the field
EventUtils.synthesizeKey("VK_RIGHT", {});
yield EventUtils.synthesizeKey("VK_BACK_SPACE", {});
let mainActionButton = document.getAnonymousElementByAttribute(notificationElement.button, "anonid", "button");
// Wait for main button to get disabled
yield ContentTaskUtils.waitForCondition(() => mainActionButton.disabled, "Main action button is disabled");
@@ -428,28 +431,27 @@ add_task(function* test_unfocus_click()
doc.getElementById("form-basic-password").value = "password";
doc.getElementById("form-basic").submit();
});
yield promiseShown;
let notificationElement = PopupNotifications.panel.childNodes[0];
let passwordTextbox = notificationElement.querySelector("#password-notification-password");
+ let checkbox = notificationElement.querySelector("#password-notification-visibilityToggle");
+ yield EventUtils.synthesizeMouseAtCenter(checkbox, {});
+
+ // Wait for the textbox type to change
+ yield ContentTaskUtils.waitForCondition(() => passwordTextbox.type == "",
+ "Password textbox changed type");
+
// Focus the password textbox
let focusPassword = BrowserTestUtils.waitForEvent(passwordTextbox, "focus");
passwordTextbox.focus();
yield focusPassword;
- // Wait for the textbox type to change
- yield ContentTaskUtils.waitForCondition(() => passwordTextbox.type == "",
- "Password textbox changed type");
-
let notificationIcon = document.getAnonymousElementByAttribute(notificationElement,
"class",
"popup-notification-icon");
yield EventUtils.synthesizeMouseAtCenter(notificationIcon, {});
-
- // Wait for the textbox type to change back
- yield ContentTaskUtils.waitForCondition(() => passwordTextbox.type == "password",
- "Password textbox changed type back to password");
});
});
--- a/toolkit/locales/en-US/chrome/passwordmgr/passwordmgr.properties
+++ b/toolkit/locales/en-US/chrome/passwordmgr/passwordmgr.properties
@@ -19,20 +19,18 @@ updateLoginButtonAccessKey = U
# Note that long usernames may be truncated.
rememberPasswordMsg = Would you like to remember the password for "%1$S" on %2$S?
# LOCALIZATION NOTE (rememberPasswordMsgNoUsername):
# String is the login's hostname.
rememberPasswordMsgNoUsername = Would you like to remember the password on %S?
# LOCALIZATION NOTE (noUsernamePlaceholder):
# This is displayed in place of the username when it is missing.
noUsernamePlaceholder=No username
-# LOCALIZATION NOTE (showPasswordPlaceholder):
-# This is displayed in the password field to indicate that the password will be
-# shown if focused.
-showPasswordPlaceholder=SHOW
+showPasswordLabel=Show password
+showPasswordAccessKey=S
notNowButtonText = &Not Now
notifyBarNotNowButtonText = Not Now
notifyBarNotNowButtonAccessKey = N
neverForSiteButtonText = Ne&ver for This Site
notifyBarNeverRememberButtonText = Never Remember Password for This Site
notifyBarNeverRememberButtonAccessKey = e
rememberButtonText = &Remember
notifyBarRememberPasswordButtonText = Remember Password