--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -1287,32 +1287,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
@@ -332,17 +332,17 @@ LoginManagerPrompter.prototype = {
this._pwmgr.getLoginSavingEnabled(hostname);
// if checkBoxLabel is null, the checkbox won't be shown at all.
if (canRememberLogin)
checkBoxLabel = this._getLocalizedString("rememberPassword");
// Look for existing logins.
var foundLogins = this._pwmgr.findLogins({}, hostname, null,
- realm);
+ realm);
// XXX Like the original code, we can't deal with multiple
// account selection. (bug 227632)
if (foundLogins.length > 0) {
selectedLogin = foundLogins[0];
// If the caller provided a username, try to use it. If they
// provided only a password, this will try to find a password-only
@@ -433,17 +433,17 @@ LoginManagerPrompter.prototype = {
// if checkBoxLabel is null, the checkbox won't be shown at all.
if (canRememberLogin)
checkBoxLabel = this._getLocalizedString("rememberPassword");
if (!aPassword.value) {
// Look for existing logins.
var foundLogins = this._pwmgr.findLogins({}, hostname, null,
- realm);
+ realm);
// XXX Like the original code, we can't deal with multiple
// account selection (bug 227632). We can deal with finding the
// account based on the supplied username - but in this case we'll
// just return the first match.
for (var i = 0; i < foundLogins.length; ++i) {
if (foundLogins[i].username == username) {
aPassword.value = foundLogins[i].password;
@@ -520,27 +520,25 @@ LoginManagerPrompter.prototype = {
promptAuth : function (aChannel, aLevel, aAuthInfo) {
var selectedLogin = null;
var checkbox = { value : false };
var checkboxLabel = null;
var epicfail = false;
var canAutologin = false;
try {
-
this.log("===== promptAuth called =====");
// If the user submits a login but it fails, we need to remove the
// notification bar that was displayed. Conveniently, the user will
// be prompted for authentication again, which brings us here.
this._removeLoginNotifications();
var [hostname, httpRealm] = this._getAuthTarget(aChannel, aAuthInfo);
-
// Looks for existing logins to prefill the prompt with.
var foundLogins = this._pwmgr.findLogins({},
hostname, null, httpRealm);
this.log("found " + foundLogins.length + " matching logins.");
// XXX Can't select from multiple accounts yet. (bug 227632)
if (foundLogins.length > 0) {
selectedLogin = foundLogins[0];
@@ -615,19 +613,17 @@ LoginManagerPrompter.prototype = {
this.log("New login seen for " + username +
" @ " + hostname + " (" + httpRealm + ")");
let notifyObj = this._getPopupNote() || notifyBox;
if (notifyObj)
this._showSaveLoginNotification(notifyObj, newLogin);
else
this._pwmgr.addLogin(newLogin);
-
} else if (password != selectedLogin.password) {
-
this.log("Updating password for " + username +
" @ " + hostname + " (" + httpRealm + ")");
let notifyObj = this._getPopupNote() || notifyBox;
if (notifyObj)
this._showChangeLoginNotification(notifyObj,
selectedLogin, newLogin);
else
this._updateLogin(selectedLogin, newLogin);
@@ -850,77 +846,50 @@ 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 toggleCheckbox = chromeDoc.getElementById("password-notification-visibilityToggle");
+ toggleCheckbox.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;
+ 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 +937,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 togglePasswordLabel = this._getLocalizedString("togglePasswordLabel");
+ let togglePasswordAccessKey = this._getLocalizedString("togglePasswordAccessKey");
let displayHost = this._getShortDisplayHost(login.hostname);
this._getPopupNote().show(
browser,
"password",
promptMsg,
"password-notification-icon",
mainAction,
@@ -992,42 +962,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", togglePasswordLabel);
+ chromeDoc.getElementById("password-notification-visibilityToggle")
+ .setAttribute("accesskey", togglePasswordAccessKey);
+ 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.ini
+++ b/toolkit/components/passwordmgr/test/browser/browser.ini
@@ -34,17 +34,17 @@ support-files =
subtst_notifications_change_p.html
[browser_DOMFormHasPassword.js]
[browser_DOMInputPasswordAdded.js]
[browser_filldoorhanger.js]
[browser_hasInsecureLoginForms.js]
[browser_hasInsecureLoginForms_streamConverter.js]
[browser_insecurePasswordWarning.js]
[browser_notifications.js]
-skip-if = true # Intermittent failures: Bug 1182296, bug 1148771
+[browser_notifications_2.js]
[browser_passwordmgr_editing.js]
skip-if = os == "linux"
[browser_context_menu.js]
skip-if = e10s
[browser_passwordmgr_contextmenu.js]
[browser_passwordmgr_fields.js]
[browser_passwordmgr_observers.js]
[browser_passwordmgr_sort.js]
--- a/toolkit/components/passwordmgr/test/browser/browser_notifications.js
+++ b/toolkit/components/passwordmgr/test/browser/browser_notifications.js
@@ -44,29 +44,29 @@ add_task(function* test_save_change() {
function* ({ username, password }) {
let doc = content.document;
doc.getElementById("form-basic-username").value = username;
doc.getElementById("form-basic-password").value = password;
doc.getElementById("form-basic").submit();
});
yield promiseShown;
+ let notificationElement = PopupNotifications.panel.childNodes[0];
// Check the actual content of the popup notification.
- Assert.equal(document.getElementById("password-notification-username")
+ Assert.equal(notificationElement.querySelector("#password-notification-username")
.getAttribute("value"), username);
- Assert.equal(document.getElementById("password-notification-password")
+ Assert.equal(notificationElement.querySelector("#password-notification-password")
.getAttribute("value"), password);
// Simulate the action on the notification to request the login to be
// saved, and wait for the data to be updated or saved based on the type
// of operation we expect.
let expectedNotification = oldPassword ? "modifyLogin" : "addLogin";
let promiseLogin = TestUtils.topicObserved("passwordmgr-storage-changed",
(_, data) => data == expectedNotification);
- let notificationElement = PopupNotifications.panel.childNodes[0];
notificationElement.button.doCommand();
let [result] = yield promiseLogin;
// Check that the values in the database match the expected values.
let login = oldPassword ? result.QueryInterface(Ci.nsIArray)
.queryElementAt(1, Ci.nsILoginInfo)
: result.QueryInterface(Ci.nsILoginInfo);
Assert.equal(login.username, username);
@@ -126,16 +126,17 @@ add_task(function* test_edit_username()
if (testCase.usernameInPageExists) {
Services.logins.addLogin(LoginTestUtils.testData.formLogin({
hostname: "https://example.com",
formSubmitURL: "https://example.com",
username: testCase.usernameInPage,
password: "old password",
}));
}
+
if (testCase.usernameChangedToExists) {
Services.logins.addLogin(LoginTestUtils.testData.formLogin({
hostname: "https://example.com",
formSubmitURL: "https://example.com",
username: testCase.usernameChangedTo,
password: "old password",
}));
}
@@ -153,35 +154,35 @@ add_task(function* test_edit_username()
function* (usernameInPage) {
let doc = content.document;
doc.getElementById("form-basic-username").value = usernameInPage;
doc.getElementById("form-basic-password").value = "password";
doc.getElementById("form-basic").submit();
});
yield promiseShown;
+ let notificationElement = PopupNotifications.panel.childNodes[0];
// Modify the username in the dialog if requested.
if (testCase.usernameChangedTo) {
- document.getElementById("password-notification-username")
+ notificationElement.querySelector("#password-notification-username")
.setAttribute("value", testCase.usernameChangedTo);
}
// We expect a modifyLogin notification if the final username used by the
// dialog exists in the logins database, otherwise an addLogin one.
let expectModifyLogin = testCase.usernameChangedTo
? testCase.usernameChangedToExists
: testCase.usernameInPageExists;
// Simulate the action on the notification to request the login to be
// saved, and wait for the data to be updated or saved based on the type
// of operation we expect.
let expectedNotification = expectModifyLogin ? "modifyLogin" : "addLogin";
let promiseLogin = TestUtils.topicObserved("passwordmgr-storage-changed",
(_, data) => data == expectedNotification);
- let notificationElement = PopupNotifications.panel.childNodes[0];
notificationElement.button.doCommand();
let [result] = yield promiseLogin;
// Check that the values in the database match the expected values.
let login = expectModifyLogin ? result.QueryInterface(Ci.nsIArray)
.queryElementAt(1, Ci.nsILoginInfo)
: result.QueryInterface(Ci.nsILoginInfo);
Assert.equal(login.username, testCase.usernameChangedTo ||
@@ -195,18 +196,18 @@ add_task(function* test_edit_username()
});
/**
* Test changing the password inside the doorhanger notification for passwords.
*
* We check the following cases:
* - Editing the password of a new login.
* - Editing the password of an existing login.
- * - Changing the username and password to an existing login with different password.
- * - Changing the username and password to an existing login with the same password;
+ * - Changing both username and password to an existing login.
+ * - Changing the username to an existing login.
* - Editing username to an empty one and a new password.
*
* If both the username and password matches an already existing login, we should not
* update it's password, but only it's usage timestamp and count.
*/
add_task(function* test_edit_password() {
let testCases = [{
usernameInPage: "username",
@@ -236,31 +237,32 @@ add_task(function* test_edit_password()
timesUsed: 2,
checkPasswordNotUpdated: true,
}, {
usernameInPage: "newUsername",
usernameChangedTo: "",
usernameChangedToExists: true,
passwordInPage: "password",
passwordChangedTo: "newPassword",
- timesUsed: 1,
+ timesUsed: 2,
}];
for (let testCase of testCases) {
info("Test case: " + JSON.stringify(testCase));
// Create the pre-existing logins when needed.
if (testCase.usernameInPageExists) {
Services.logins.addLogin(LoginTestUtils.testData.formLogin({
hostname: "https://example.com",
formSubmitURL: "https://example.com",
username: testCase.usernameInPage,
password: testCase.passwordInStorage,
}));
}
+
if (testCase.usernameChangedToExists) {
Services.logins.addLogin(LoginTestUtils.testData.formLogin({
hostname: "https://example.com",
formSubmitURL: "https://example.com",
username: testCase.usernameChangedTo,
password: testCase.passwordChangedTo,
}));
}
@@ -278,41 +280,41 @@ add_task(function* test_edit_password()
function* (testCase) {
let doc = content.document;
doc.getElementById("form-basic-username").value = testCase.usernameInPage;
doc.getElementById("form-basic-password").value = testCase.passwordInPage;
doc.getElementById("form-basic").submit();
});
yield promiseShown;
+ let notificationElement = PopupNotifications.panel.childNodes[0];
// Modify the username in the dialog if requested.
if (testCase.usernameChangedTo) {
- document.getElementById("password-notification-username")
+ notificationElement.querySelector("#password-notification-username")
.setAttribute("value", testCase.usernameChangedTo);
}
// Modify the password in the dialog if requested.
if (testCase.passwordChangedTo) {
- document.getElementById("password-notification-password")
+ notificationElement.querySelector("#password-notification-password")
.setAttribute("value", testCase.passwordChangedTo);
}
// We expect a modifyLogin notification if the final username used by the
// dialog exists in the logins database, otherwise an addLogin one.
- let expectModifyLogin = testCase.usernameChangedTo
+ let expectModifyLogin = typeof testCase.usernameChangedTo !== "undefined"
? testCase.usernameChangedToExists
: testCase.usernameInPageExists;
// Simulate the action on the notification to request the login to be
// saved, and wait for the data to be updated or saved based on the type
// of operation we expect.
let expectedNotification = expectModifyLogin ? "modifyLogin" : "addLogin";
let promiseLogin = TestUtils.topicObserved("passwordmgr-storage-changed",
(_, data) => data == expectedNotification);
- let notificationElement = PopupNotifications.panel.childNodes[0];
notificationElement.button.doCommand();
let [result] = yield promiseLogin;
// Check that the values in the database match the expected values.
let login = expectModifyLogin ? result.QueryInterface(Ci.nsIArray)
.queryElementAt(1, Ci.nsILoginInfo)
: result.QueryInterface(Ci.nsILoginInfo);
@@ -330,122 +332,8 @@ add_task(function* test_edit_password()
Assert.ok(meta.timeCreated == meta.timePasswordChanged);
}
});
// Clean up the database before the next test case is executed.
Services.logins.removeAllLogins();
}
});
-
-/**
- * Test that the doorhanger main action button is disabled
- * when the password field is empty.
- *
- * Also checks that submiting an empty password throws an error.
- *
- * We skip Linux for now because focusing elements on the doorhanger
- * doesn't work as expected (Bug 1182296)
- */
-add_task(function* test_empty_password() {
- if (Services.appinfo.OS == "Linux") {
- Assert.ok(true, "Skipping test on Linux.");
- return;
- }
- yield BrowserTestUtils.withNewTab({
- gBrowser,
- url: "https://example.com/browser/toolkit/components/" +
- "passwordmgr/test/browser/form_basic.html",
- }, function* (browser) {
- // Submit the form in the content page with the credentials from the test
- // case. This will cause the doorhanger notification to be displayed.
- let promiseShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel,
- "popupshown");
- yield ContentTask.spawn(browser, null,
- function* () {
- let doc = content.document;
- doc.getElementById("form-basic-username").value = "username";
- 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");
-
- // 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");
-
- // Makes sure submiting an empty password throws an error
- Assert.throws(notificationElement.button.doCommand(),
- "Can't add a login with a null or empty password.",
- "Should fail for an empty password");
- });
-});
-
-/**
- * Checks that clicking at the doorhanger pane takes the focus
- * out of the password field.
- *
- * We skip Linux for now because focusing elements on the doorhanger
- * doesn't work as expected (Bug 1182296)
- */
-add_task(function* test_unfocus_click() {
- if (Services.appinfo.OS == "Linux") {
- Assert.ok(true, "Skipping test on Linux.");
- return;
- }
- yield BrowserTestUtils.withNewTab({
- gBrowser,
- url: "https://example.com/browser/toolkit/components/" +
- "passwordmgr/test/browser/form_basic.html",
- }, function* (browser) {
- // Submit the form in the content page with the credentials from the test
- // case. This will cause the doorhanger notification to be displayed.
-
- let promiseShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel,
- "popupshown");
- yield ContentTask.spawn(browser, null,
- function* () {
- let doc = content.document;
- doc.getElementById("form-basic-username").value = "username";
- 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");
-
- // 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");
- });
-});
new file mode 100644
--- /dev/null
+++ b/toolkit/components/passwordmgr/test/browser/browser_notifications_2.js
@@ -0,0 +1,83 @@
+Cu.import("resource://testing-common/ContentTaskUtils.jsm", this);
+
+/**
+ * Test that the doorhanger main action button is disabled
+ * when the password field is empty.
+ *
+ * Also checks that submiting an empty password throws an error.
+ */
+add_task(function* test_empty_password() {
+ yield BrowserTestUtils.withNewTab({
+ gBrowser,
+ url: "https://example.com/browser/toolkit/components/" +
+ "passwordmgr/test/browser/form_basic.html",
+ }, function* (browser) {
+ // Submit the form in the content page with the credentials from the test
+ // case. This will cause the doorhanger notification to be displayed.
+ let promiseShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel,
+ "popupshown");
+ yield ContentTask.spawn(browser, null,
+ function* () {
+ let doc = content.document;
+ doc.getElementById("form-basic-username").value = "username";
+ 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 toggleCheckbox = notificationElement.querySelector("#password-notification-visibilityToggle");
+
+ // Synthesize input to empty the field
+ passwordTextbox.focus();
+ yield EventUtils.synthesizeKey("VK_RIGHT", {});
+ yield EventUtils.synthesizeKey("VK_BACK_SPACE", {});
+
+ let mainActionButton = document.getAnonymousElementByAttribute(notificationElement.button, "anonid", "button");
+ Assert.ok(mainActionButton.disabled, "Main action button is disabled");
+
+ // Makes sure submiting an empty password throws an error
+ Assert.throws(notificationElement.button.doCommand(),
+ "Can't add a login with a null or empty password.",
+ "Should fail for an empty password");
+ });
+});
+
+/**
+ * Test that the doorhanger password field shows plain or * text
+ * when the checkbox is checked.
+ *
+ */
+add_task(function* test_toggle_password() {
+ yield BrowserTestUtils.withNewTab({
+ gBrowser,
+ url: "https://example.com/browser/toolkit/components/" +
+ "passwordmgr/test/browser/form_basic.html",
+ }, function* (browser) {
+ // Submit the form in the content page with the credentials from the test
+ // case. This will cause the doorhanger notification to be displayed.
+ let promiseShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel,
+ "popupshown");
+ yield ContentTask.spawn(browser, null,
+ function* () {
+ let doc = content.document;
+ doc.getElementById("form-basic-username").value = "username";
+ 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 toggleCheckbox = notificationElement.querySelector("#password-notification-visibilityToggle");
+
+ yield EventUtils.synthesizeMouseAtCenter(toggleCheckbox, {});
+ Assert.ok(toggleCheckbox.checked);
+ Assert.equal(passwordTextbox.type, "", "Password textbox changed to plain text");
+
+ yield EventUtils.synthesizeMouseAtCenter(toggleCheckbox, {});
+ Assert.ok(!toggleCheckbox.checked);
+ Assert.equal(passwordTextbox.type, "password", "Password textbox changed to * text");
+ });
+});
--- a/toolkit/locales/en-US/chrome/passwordmgr/passwordmgr.properties
+++ b/toolkit/locales/en-US/chrome/passwordmgr/passwordmgr.properties
@@ -10,29 +10,27 @@ rememberLoginMsg = Would you like %S to
rememberLoginMsgNoUser = Would you like %S to remember this password?
rememberLoginButtonText = Remember
rememberLoginButtonAccessKey = R
updateLoginMsg = Would you like to update this login?
updateLoginMsgNoUser = Would you like to update this password?
updateLoginButtonText = Update
updateLoginButtonAccessKey = U
# LOCALIZATION NOTE (rememberPasswordMsg):
-# 1st string is the username for the login, 2nd is the login's hostname.
+# 1st string is the username for the login, 2nd is the login's hostname.
# 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
+togglePasswordLabel=Show password
+togglePasswordAccessKey=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