зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1569723 - Show a 'Discard' dialog when canceling or leaving the edit/create mode in about:logins. r=fluent-reviewers,jaws,flod
Differential Revision: https://phabricator.services.mozilla.com/D39984 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
b09e5ab2d6
Коммит
a1ea346540
|
@ -126,7 +126,23 @@ export default class LoginItem extends HTMLElement {
|
|||
break;
|
||||
}
|
||||
case "AboutLoginsLoginSelected": {
|
||||
this.setLogin(event.detail);
|
||||
let login = event.detail;
|
||||
if (this.hasPendingChanges()) {
|
||||
event.preventDefault();
|
||||
this.showConfirmationDialog("discard-changes", () => {
|
||||
// Clear any pending changes
|
||||
this.setLogin(login);
|
||||
|
||||
window.dispatchEvent(
|
||||
new CustomEvent("AboutLoginsLoginSelected", {
|
||||
detail: login,
|
||||
cancelable: true,
|
||||
})
|
||||
);
|
||||
});
|
||||
} else {
|
||||
this.setLogin(login);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "blur": {
|
||||
|
@ -153,7 +169,13 @@ export default class LoginItem extends HTMLElement {
|
|||
if (classList.contains("cancel-button")) {
|
||||
let wasExistingLogin = !!this._login.guid;
|
||||
if (wasExistingLogin) {
|
||||
this.setLogin(this._login);
|
||||
if (this.hasPendingChanges()) {
|
||||
this.showConfirmationDialog("discard-changes", () => {
|
||||
this.setLogin(this._login);
|
||||
});
|
||||
} else {
|
||||
this.setLogin(this._login);
|
||||
}
|
||||
} else {
|
||||
window.dispatchEvent(new CustomEvent("AboutLoginsClearSelection"));
|
||||
}
|
||||
|
@ -274,6 +296,15 @@ export default class LoginItem extends HTMLElement {
|
|||
message: "confirm-delete-dialog-message",
|
||||
confirmButtonLabel: "confirm-delete-dialog-confirm-button",
|
||||
};
|
||||
break;
|
||||
}
|
||||
case "discard-changes": {
|
||||
options = {
|
||||
title: "confirm-discard-changes-dialog-title",
|
||||
message: "confirm-discard-changes-dialog-message",
|
||||
confirmButtonLabel: "confirm-discard-changes-dialog-confirm-button",
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
let dialogPromise = dialog.show(options);
|
||||
|
@ -297,6 +328,17 @@ export default class LoginItem extends HTMLElement {
|
|||
});
|
||||
}
|
||||
|
||||
hasPendingChanges() {
|
||||
let { origin = "", username = "", password = "" } = this._login || {};
|
||||
|
||||
let valuesChanged = !window.AboutLoginsUtils.doLoginsMatch(
|
||||
{ origin, username, password },
|
||||
this._loginFromForm()
|
||||
);
|
||||
|
||||
return this.dataset.editing && valuesChanged;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {login} login The login that should be displayed. The login object is
|
||||
* a plain JS object representation of nsILoginInfo/nsILoginMetaInfo.
|
||||
|
|
|
@ -118,6 +118,7 @@ export default class LoginList extends HTMLElement {
|
|||
new CustomEvent("AboutLoginsLoginSelected", {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
cancelable: true, // allow calling preventDefault() on event
|
||||
detail: listItem._login,
|
||||
})
|
||||
);
|
||||
|
@ -137,6 +138,7 @@ export default class LoginList extends HTMLElement {
|
|||
window.dispatchEvent(
|
||||
new CustomEvent("AboutLoginsLoginSelected", {
|
||||
detail: this._logins[0],
|
||||
cancelable: true,
|
||||
})
|
||||
);
|
||||
break;
|
||||
|
@ -152,7 +154,7 @@ export default class LoginList extends HTMLElement {
|
|||
break;
|
||||
}
|
||||
case "AboutLoginsLoginSelected": {
|
||||
if (this._selectedGuid == event.detail.guid) {
|
||||
if (event.defaultPrevented || this._selectedGuid == event.detail.guid) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -64,14 +64,38 @@ add_task(async function test_login_item() {
|
|||
usernameInput.value += "-undome";
|
||||
passwordInput.value += "-undome";
|
||||
|
||||
let dialog = content.document.querySelector("confirmation-dialog");
|
||||
ok(dialog.hidden, "Confirm dialog should initially be hidden");
|
||||
|
||||
let cancelButton = loginItem.shadowRoot.querySelector(".cancel-button");
|
||||
cancelButton.click();
|
||||
|
||||
ok(!dialog.hidden, "Confirm dialog should be visible");
|
||||
|
||||
let confirmDiscardButton = dialog.shadowRoot.querySelector(
|
||||
".confirm-button"
|
||||
);
|
||||
await content.document.l10n.translateElements([
|
||||
dialog.shadowRoot.querySelector(".title"),
|
||||
dialog.shadowRoot.querySelector(".message"),
|
||||
confirmDiscardButton,
|
||||
]);
|
||||
|
||||
confirmDiscardButton.click();
|
||||
|
||||
ok(dialog.hidden, "Confirm dialog should be hidden after confirming");
|
||||
|
||||
usernameInput = loginItem.shadowRoot.querySelector(
|
||||
"input[name='username']"
|
||||
);
|
||||
passwordInput = loginItem.shadowRoot.querySelector(
|
||||
"input[name='password']"
|
||||
);
|
||||
|
||||
await ContentTaskUtils.waitForCondition(
|
||||
() => usernameInput.value == login.username
|
||||
);
|
||||
|
||||
is(
|
||||
usernameInput.value,
|
||||
login.username,
|
||||
|
|
|
@ -59,5 +59,12 @@ Object.defineProperty(window, "AboutLoginsUtils", {
|
|||
promptForMasterPassword(resolve) {
|
||||
resolve(true);
|
||||
},
|
||||
doLoginsMatch(login1, login2) {
|
||||
return (
|
||||
login1.origin == login2.origin &&
|
||||
login1.username == login2.username &&
|
||||
login1.password == login2.password
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -9,6 +9,7 @@ Test the login-item component
|
|||
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="module" src="chrome://browser/content/aboutlogins/components/login-item.js"></script>
|
||||
<script type="module" src="chrome://browser/content/aboutlogins/components/confirmation-dialog.js"></script>
|
||||
<script src="aboutlogins_common.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
|
||||
|
@ -25,7 +26,7 @@ Test the login-item component
|
|||
<script>
|
||||
/** Test the login-item component **/
|
||||
|
||||
let gLoginItem;
|
||||
let gLoginItem, gConfirmationDialog;
|
||||
const TEST_LOGIN_1 = {
|
||||
guid: "123456789",
|
||||
origin: "https://example.com",
|
||||
|
@ -61,6 +62,10 @@ add_task(async function setup() {
|
|||
|
||||
gLoginItem = document.createElement("login-item");
|
||||
displayEl.appendChild(gLoginItem);
|
||||
|
||||
gConfirmationDialog = document.createElement("confirmation-dialog");
|
||||
gConfirmationDialog.hidden = true;
|
||||
displayEl.appendChild(gConfirmationDialog);
|
||||
});
|
||||
|
||||
add_task(async function test_empty_item() {
|
||||
|
@ -152,6 +157,13 @@ add_task(async function test_edit_login_cancel() {
|
|||
"loginItem should not be in 'isNewLogin' mode");
|
||||
|
||||
gLoginItem.shadowRoot.querySelector(".cancel-button").click();
|
||||
gConfirmationDialog.shadowRoot.querySelector(".confirm-button").click();
|
||||
|
||||
await SimpleTest.promiseWaitForCondition(
|
||||
() => gConfirmationDialog.hidden,
|
||||
"waiting for confirmation dialog to hide"
|
||||
);
|
||||
|
||||
ok(!gLoginItem.dataset.editing, "loginItem should not be in 'edit' mode");
|
||||
ok(!gLoginItem.dataset.isNewLogin, "loginItem should not be in 'isNewLogin' mode");
|
||||
});
|
||||
|
|
|
@ -103,6 +103,10 @@ confirm-delete-dialog-title = Delete this login?
|
|||
confirm-delete-dialog-message = This action cannot be undone.
|
||||
confirm-delete-dialog-confirm-button = Delete
|
||||
|
||||
confirm-discard-changes-dialog-title = Discard unsaved changes?
|
||||
confirm-discard-changes-dialog-message = All unsaved changes will be lost.
|
||||
confirm-discard-changes-dialog-confirm-button = Discard
|
||||
|
||||
## Breach Alert notification
|
||||
|
||||
breach-alert-text = Passwords were leaked or stolen from this website since you last updated your login details. Change your password to protect your account.
|
||||
|
|
Загрузка…
Ссылка в новой задаче