Commit dc351adc authored by Friedrich Horschig's avatar Friedrich Horschig Committed by Commit Bot

[Passwords] Change password check title based on status

With this CL, the title and subtitle will change based on the progress
and the status of the password check.

Screenshots at the linked https://crbug.com/1047726#c31

Translation screenshots:
IDS_SETTINGS_CHECK_PASSWORDS_ERROR_GENERIC.png:
IDS_SETTINGS_CHECK_PASSWORDS_ERROR_NO_PASSWORDS.png:
IDS_SETTINGS_CHECK_PASSWORDS_ERROR_OFFLINE.png:
IDS_SETTINGS_CHECK_PASSWORDS_ERROR_QUOTA_LIMIT.png:
IDS_SETTINGS_CHECK_PASSWORDS_ERROR_SIGNED_OUT.png:
IDS_SETTINGS_CHECK_PASSWORDS_ERROR_TOO_MANY_PASSWORDS.png:
IDS_SETTINGS_CHECK_PASSWORDS_PROGRESS.png:
IDS_SETTINGS_CHECK_PASSWORDS_CANCELED.png:

https: //storage.cloud.google.com/chromium-translation-screenshots/4de6e19b4772e8e130b87952ad8fcc10b79054e7
https: //storage.cloud.google.com/chromium-translation-screenshots/ce276f9b31384860c1369b11dd2844ed01d988d6
https: //storage.cloud.google.com/chromium-translation-screenshots/014f118ca023ad004523e7860746aae0a96f981d
https: //storage.cloud.google.com/chromium-translation-screenshots/7685361448310ce06f9b704483d1ddd16721ee62
https: //storage.cloud.google.com/chromium-translation-screenshots/25b0c6ed74bd2313c5b7f954a7d34a0ec46cf2d7
https: //storage.cloud.google.com/chromium-translation-screenshots/31e9ccf14e6882a56ffffac5d6ee583e756b7136
https: //storage.cloud.google.com/chromium-translation-screenshots/d5a0c340e5f407e3347eb3323bd9c08b85c09f95
https: //storage.cloud.google.com/chromium-translation-screenshots/1b9d821e5ff2b0fcbf93c120f7e604f61eb6cb63
Bug: 1047726
Change-Id: I265376b6879427c3cbbc318d1d50bc1a0b89d969
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2087679
Commit-Queue: Friedrich [CET] <fhorschig@chromium.org>
Reviewed-by: default avatarVasilii Sukhanov <vasilii@chromium.org>
Reviewed-by: default avatarJan Wilken Dörrie <jdoerrie@chromium.org>
Cr-Commit-Position: refs/heads/master@{#748728}
parent 6a84d0e9
......@@ -320,21 +320,48 @@
<message name="IDS_SETTINGS_CHECK_PASSWORDS" desc="Name for the check passwords subsection and settings entry used to perform a password bulk check.">
Check passwords
</message>
<message name="IDS_SETTINGS_CHECK_PASSWORDS_CANCELED" desc="Message for when the password check was canceled by the user.">
Canceled
</message>
<message name="IDS_SETTINGS_CHECKED_PASSWORDS" desc="Title above amount of found compromised passwords after password bulk check.">
Checked passwords
</message>
<message name="IDS_SETTINGS_CHECK_PASSWORDS_DESCRIPTION" desc="Explanation of the passwords bulk check feature found within the password settings.">
Keep your passwords safe from data breaches and other security issues
</message>
<message name="IDS_SETTINGS_CHECK_PASSWORDS_ERROR_OFFLINE" desc="Error message when the password check can't be completed because the user is offline.">
Chrome can't check your passwords. Try checking your internet connection.
</message>
<message name="IDS_SETTINGS_CHECK_PASSWORDS_ERROR_SIGNED_OUT" desc="Error message when the password check can't be completed because the user is not signed in.">
Chrome can check your passwords when you sign in with your Google Account.
</message>
<message name="IDS_SETTINGS_CHECK_PASSWORDS_ERROR_NO_PASSWORDS" desc="Error message when the password check can't be completed since no passwords could be checked.">
No saved passwords. Chrome can check your passwords when you save them.
</message>
<message name="IDS_SETTINGS_CHECK_PASSWORDS_ERROR_TOO_MANY_PASSWORDS" desc="Error message when the password check can't be completed since the user saved too many passwords.">
Chrome can't check your passwords because there are too many.
</message>
<message name="IDS_SETTINGS_CHECK_PASSWORDS_ERROR_QUOTA_LIMIT" desc="Error message when the password check can't be completed since the user hit the quota limit.">
Chrome can't check your passwords. Try again tomorrow.
</message>
<message name="IDS_SETTINGS_CHECK_PASSWORDS_ERROR_GENERIC" desc="Error message when the password check can't be completed for an unspecified reason.">
Chrome can't check your passwords.
</message>
<message name="IDS_SETTINGS_COMPROMISED_PASSWORDS_COUNT" desc="Number of compromised passwords present in the database">
{COUNT, plural,
=0 {No compromised passwords found}
=1 {{COUNT} compromised password}
other {{COUNT} compromised passwords}}
</message>
<message name="IDS_SETTINGS_LEAKED_PASSWORDS_COUNT" desc="Number of compromised passwords found during bulk check.">
<ph name="COUNT">$1<ex>5</ex></ph> compromised passwords
</message>
<message name="IDS_SETTINGS_CHECK_PASSWORDS_AGAIN" desc="Button to start bulk password check manually in passwords check section.">
Check again
</message>
<message name="IDS_SETTINGS_CHECK_PASSWORDS_PROGRESS" desc="Text for a label showing how many passwords were checked so far and how many passwords need to be checked in total.">
Checking passwords (<ph name="CHECKED_PASSWORDS">$1<ex>6</ex></ph> of <ph name="TOTAL_PASSWORDS">$2<ex>31</ex></ph>)…
</message>
<message name="IDS_SETTINGS_PASSWORDS_JUST_NOW" desc="Label shown when a compromised credential was found less than a minute ago">
Just now
</message>
......
1b9d821e5ff2b0fcbf93c120f7e604f61eb6cb63
\ No newline at end of file
4de6e19b4772e8e130b87952ad8fcc10b79054e7
\ No newline at end of file
ce276f9b31384860c1369b11dd2844ed01d988d6
\ No newline at end of file
014f118ca023ad004523e7860746aae0a96f981d
\ No newline at end of file
7685361448310ce06f9b704483d1ddd16721ee62
\ No newline at end of file
25b0c6ed74bd2313c5b7f954a7d34a0ec46cf2d7
\ No newline at end of file
31e9ccf14e6882a56ffffac5d6ee583e756b7136
\ No newline at end of file
d5a0c340e5f407e3347eb3323bd9c08b85c09f95
\ No newline at end of file
......@@ -45,14 +45,15 @@
</template>
<div class="start settings-box-text">
<div>
$i18n{checkedPasswords}
<div id="title">
[[getTitle_(status_)]]
<span class="secondary inline" id="lastCompletedCheck"
hidden$="[[!lastCompletedCheck_]]">
hidden$="[[!showsTimestamp_(status_)]]">
&bull; [[lastCompletedCheck_]]
</span>
</div>
<div class="secondary" id="passwordLeakCount">
<div class="secondary" id="subtitle"
hidden$="[[!showsPasswordsCount_(status_, leakedPasswords)]]">
[[compromisedPasswordsCount_]]
</div>
</div>
......
......@@ -201,6 +201,38 @@ Polymer({
return '';
},
/**
* Returns the title message indicating the state of the last/ongoing check.
* @param {!PasswordManagerProxy.PasswordCheckStatus} status
* @return {string}
* @private
*/
getTitle_(status) {
switch (status.state) {
case CheckState.IDLE:
return this.i18n('checkPasswords');
case CheckState.CANCELED:
return this.i18n('checkPasswordsCanceled');
case CheckState.RUNNING:
return this.i18n(
'checkPasswordsProgress', status.alreadyProcessed || 0,
status.remainingInQueue + status.alreadyProcessed);
case CheckState.OFFLINE:
return this.i18n('checkPasswordsErrorOffline');
case CheckState.SIGNED_OUT:
return this.i18n('checkPasswordsErrorSignedOut');
case CheckState.NO_PASSWORDS:
return this.i18n('checkPasswordsErrorNoPasswords');
case CheckState.TOO_MANY_PASSWORDS:
return this.i18n('checkPasswordsErrorTooManyPasswords');
case CheckState.QUOTA_LIMIT:
return this.i18n('checkPasswordsErrorQuota');
case CheckState.OTHER_ERROR:
return this.i18n('checkPasswordsErrorGeneric');
}
throw 'Can\'t find a title for state: ' + status.state;
},
/**
* Returns true iff a check is running right according to the given |status|.
* @param {!PasswordManagerProxy.PasswordCheckStatus} status
......@@ -211,6 +243,17 @@ Polymer({
return status.state == CheckState.RUNNING;
},
/**
* Returns true to show the timestamp when a check was completed successfully.
* @param {!PasswordManagerProxy.PasswordCheckStatus} status
* @return {boolean}
* @private
*/
showsTimestamp_(status) {
return status.state == CheckState.IDLE;
},
/**
* Returns true if there are leaked credentials or the status is unexpected
* for a regular password check.
......@@ -239,5 +282,32 @@ Polymer({
}
throw 'Not specified whether to state is an error: ' + status.state;
},
/**
* Returns true if there are leaked credentials or the status is unexpected
* for a regular password check.
* @param {!PasswordManagerProxy.PasswordCheckStatus} status
* @param {!Array<PasswordManagerProxy.CompromisedCredential>}
* leakedPasswords
* @return {boolean}
* @private
*/
showsPasswordsCount_(status, leakedPasswords) {
switch (status.state) {
case CheckState.IDLE:
return true;
case CheckState.CANCELED:
case CheckState.RUNNING:
return this.hasLeakedCredentials_(leakedPasswords);
case CheckState.OFFLINE:
case CheckState.SIGNED_OUT:
case CheckState.NO_PASSWORDS:
case CheckState.TOO_MANY_PASSWORDS:
case CheckState.QUOTA_LIMIT:
case CheckState.OTHER_ERROR:
return false;
}
throw 'Not specified whether to show passwords for state: ' + status.state;
},
});
})();
......@@ -729,9 +729,23 @@ void AddAutofillStrings(content::WebUIDataSource* html_source,
{"autofillPageTitle", IDS_SETTINGS_AUTOFILL},
{"passwords", IDS_SETTINGS_PASSWORDS},
{"checkPasswords", IDS_SETTINGS_CHECK_PASSWORDS},
{"checkPasswordsCanceled", IDS_SETTINGS_CHECK_PASSWORDS_CANCELED},
{"checkedPasswords", IDS_SETTINGS_CHECKED_PASSWORDS},
{"checkPasswordsDescription", IDS_SETTINGS_CHECK_PASSWORDS_DESCRIPTION},
{"checkPasswordsErrorOffline",
IDS_SETTINGS_CHECK_PASSWORDS_ERROR_OFFLINE},
{"checkPasswordsErrorSignedOut",
IDS_SETTINGS_CHECK_PASSWORDS_ERROR_SIGNED_OUT},
{"checkPasswordsErrorNoPasswords",
IDS_SETTINGS_CHECK_PASSWORDS_ERROR_NO_PASSWORDS},
{"checkPasswordsErrorTooManyPasswords",
IDS_SETTINGS_CHECK_PASSWORDS_ERROR_TOO_MANY_PASSWORDS},
{"checkPasswordsErrorQuota",
IDS_SETTINGS_CHECK_PASSWORDS_ERROR_QUOTA_LIMIT},
{"checkPasswordsErrorGeneric",
IDS_SETTINGS_CHECK_PASSWORDS_ERROR_GENERIC},
{"checkPasswordsAgain", IDS_SETTINGS_CHECK_PASSWORDS_AGAIN},
{"checkPasswordsProgress", IDS_SETTINGS_CHECK_PASSWORDS_PROGRESS},
{"compromisedPasswords", IDS_SETTINGS_COMPROMISED_PASSWORDS},
{"compromisedPasswordsDescription",
IDS_SETTINGS_COMPROMISED_PASSWORDS_ADVICE},
......
......@@ -161,24 +161,28 @@ cr.define('settings_passwords_check', function() {
/*state=*/ PasswordCheckState.RUNNING,
/*checked=*/ 1,
/*remaining=*/ 1);
passwordManager.data.leakedCredentials =
autofill_test_util.makeCompromisedCredentialsInfo([], 'just now');
const section = createCheckPasswordSection();
return passwordManager.whenCalled('getPasswordCheckStatus').then(() => {
Polymer.dom.flush();
expectEquals(PasswordCheckState.RUNNING, section.status_.state);
assert(isElementVisible(section.$.title));
expectEquals(
section.i18n('checkPasswordsProgress', 1, 2),
section.$.title.innerText);
// Change status from running to IDLE.
assert(!!passwordManager.lastCallback.addPasswordCheckStatusListener);
passwordManager.lastCallback.addPasswordCheckStatusListener(
autofill_test_util.makePasswordCheckStatus(
/*state=*/ PasswordCheckState.IDLE,
/*state=*/ PasswordCheckState.RUNNING,
/*checked=*/ 2,
/*remaining=*/ 0));
Polymer.dom.flush();
expectEquals(PasswordCheckState.IDLE, section.status_.state);
assert(isElementVisible(section.$.title));
expectEquals(
section.i18n('checkPasswordsProgress', 2, 2),
section.$.title.innerText);
});
});
......@@ -275,5 +279,210 @@ cr.define('settings_passwords_check', function() {
expectFalse(isElementVisible(icon));
});
});
// While running, the check should show the processed and total passwords.
test('testShowOnlyProgressWhileRunningWithoutLeaks', function() {
passwordManager.data.checkStatus =
autofill_test_util.makePasswordCheckStatus(
/*state=*/ PasswordCheckState.RUNNING,
/*checked=*/ 1,
/*remaining=*/ 3);
passwordManager.data.leakedCredentials =
autofill_test_util.makeCompromisedCredentialsInfo([], 'just now');
section = createCheckPasswordSection();
return passwordManager.whenCalled('getPasswordCheckStatus').then(() => {
Polymer.dom.flush();
const title = section.$.title;
assert(isElementVisible(title));
expectEquals(
section.i18n('checkPasswordsProgress', 1, 4), title.innerText);
expectFalse(isElementVisible(section.$.subtitle));
});
});
// While running, show progress and already found leak count.
test('testShowProgressAndLeaksWhileRunning', function() {
const data = passwordManager.data;
data.checkStatus = autofill_test_util.makePasswordCheckStatus(
/*state=*/ PasswordCheckState.RUNNING,
/*checked=*/ 2,
/*remaining=*/ 3);
data.leakedCredentials =
autofill_test_util.makeCompromisedCredentialsInfo(
[
autofill_test_util.makeCompromisedCredentials(
'one.com', 'test4', 'LEAKED'),
],
'just now');
const section = createCheckPasswordSection();
return passwordManager.whenCalled('getPasswordCheckStatus').then(() => {
Polymer.dom.flush();
const title = section.$.title;
const subtitle = section.$.subtitle;
assert(isElementVisible(title));
assert(isElementVisible(subtitle));
expectEquals(
section.i18n('checkPasswordsProgress', 2, 5), title.innerText);
});
});
// When canceled, show string explaining that and already found leak
// count.
test('testShowProgressAndLeaksAfterCanceled', function() {
const data = passwordManager.data;
data.checkStatus = autofill_test_util.makePasswordCheckStatus(
/*state=*/ PasswordCheckState.CANCELED,
/*checked=*/ 2,
/*remaining=*/ 3);
data.leakedCredentials =
autofill_test_util.makeCompromisedCredentialsInfo(
[
autofill_test_util.makeCompromisedCredentials(
'one.com', 'test4', 'LEAKED'),
],
'just now');
const section = createCheckPasswordSection();
return passwordManager.whenCalled('getPasswordCheckStatus').then(() => {
Polymer.dom.flush();
const title = section.$.title;
const subtitle = section.$.subtitle;
assert(isElementVisible(title));
assert(isElementVisible(subtitle));
expectEquals(section.i18n('checkPasswordsCanceled'), title.innerText);
});
});
// After running, show confirmation, timestamp and number of leaks.
test('testShowLeakCountWhenIdle', function() {
const data = passwordManager.data;
data.checkStatus = autofill_test_util.makePasswordCheckStatus(
/*state=*/ PasswordCheckState.IDLE,
/*checked=*/ 4,
/*remaining=*/ 0);
data.leakedCredentials =
autofill_test_util.makeCompromisedCredentialsInfo(
[
autofill_test_util.makeCompromisedCredentials(
'one.com', 'test4', 'LEAKED'),
],
'just now');
const section = createCheckPasswordSection();
return passwordManager.whenCalled('getPasswordCheckStatus').then(() => {
Polymer.dom.flush();
const title = section.$.title;
const subtitle = section.$.subtitle;
assert(isElementVisible(title));
assert(isElementVisible(subtitle));
expectEquals(
section.i18n('checkPasswords') + ' • just now', title.innerText);
});
});
// When offline, only show an error.
test('testShowOnlyErrorWhenOffline', function() {
passwordManager.data.checkStatus =
autofill_test_util.makePasswordCheckStatus(
PasswordCheckState.OFFLINE);
const section = createCheckPasswordSection();
return passwordManager.whenCalled('getPasswordCheckStatus').then(() => {
Polymer.dom.flush();
const title = section.$.title;
assert(isElementVisible(title));
expectEquals(
section.i18n('checkPasswordsErrorOffline'), title.innerText);
expectFalse(isElementVisible(section.$.subtitle));
});
});
// When signed out, only show an error.
test('testShowOnlyErrorWhenSignedOut', function() {
passwordManager.data.checkStatus =
autofill_test_util.makePasswordCheckStatus(
PasswordCheckState.SIGNED_OUT);
const section = createCheckPasswordSection();
return passwordManager.whenCalled('getPasswordCheckStatus').then(() => {
Polymer.dom.flush();
const title = section.$.title;
assert(isElementVisible(title));
expectEquals(
section.i18n('checkPasswordsErrorSignedOut'), title.innerText);
expectFalse(isElementVisible(section.$.subtitle));
});
});
// When no passwords are saved, only show an error.
test('testShowOnlyErrorWithoutPasswords', function() {
passwordManager.data.checkStatus =
autofill_test_util.makePasswordCheckStatus(
PasswordCheckState.NO_PASSWORDS);
const section = createCheckPasswordSection();
return passwordManager.whenCalled('getPasswordCheckStatus').then(() => {
Polymer.dom.flush();
const title = section.$.title;
assert(isElementVisible(title));
expectEquals(
section.i18n('checkPasswordsErrorNoPasswords'), title.innerText);
expectFalse(isElementVisible(section.$.subtitle));
});
});
// When too many passwords were saved to check them, only show an error.
test('testShowOnlyErrorWhenTooManyPasswords', function() {
passwordManager.data.checkStatus =
autofill_test_util.makePasswordCheckStatus(
PasswordCheckState.TOO_MANY_PASSWORDS);
const section = createCheckPasswordSection();
return passwordManager.whenCalled('getPasswordCheckStatus').then(() => {
Polymer.dom.flush();
const title = section.$.title;
assert(isElementVisible(title));
// TODO(crbug.com/1047726): Check for account redirection.
expectEquals(
section.i18n('checkPasswordsErrorTooManyPasswords'),
title.innerText);
expectFalse(isElementVisible(section.$.subtitle));
});
});
// When users run out of quota, only show an error.
test('testShowOnlyErrorWhenQuotaIsHit', function() {
passwordManager.data.checkStatus =
autofill_test_util.makePasswordCheckStatus(
PasswordCheckState.QUOTA_LIMIT);
const section = createCheckPasswordSection();
return passwordManager.whenCalled('getPasswordCheckStatus').then(() => {
Polymer.dom.flush();
const title = section.$.title;
assert(isElementVisible(title));
expectEquals(section.i18n('checkPasswordsErrorQuota'), title.innerText);
expectFalse(isElementVisible(section.$.subtitle));
});
});
// When a general error occurs, only show the message.
test('testShowOnlyGenericError', function() {
passwordManager.data.checkStatus =
autofill_test_util.makePasswordCheckStatus(
PasswordCheckState.OTHER_ERROR);
const section = createCheckPasswordSection();
return passwordManager.whenCalled('getPasswordCheckStatus').then(() => {
Polymer.dom.flush();
const title = section.$.title;
assert(isElementVisible(title));
expectEquals(
section.i18n('checkPasswordsErrorGeneric'), title.innerText);
expectFalse(isElementVisible(section.$.subtitle));
});
});
});
});
......@@ -148,15 +148,15 @@ cr.define('autofill_test_util', function() {
/**
* Creates a new password check status.
* @param {!chrome.passwordsPrivate.PasswordCheckState} state
* @param {number} checked
* @param {number} remaining
* @param {!number|undefined} checked
* @param {!number|undefined} remaining
* @return {!chrome.passwordsPrivate.PasswordCheckStatus}
*/
function makePasswordCheckStatus(state, checked, remaining) {
return {
state: state || chrome.passwordsPrivate.PasswordCheckState.IDLE,
alreadyProcessed: checked || 0,
remainingInQueue: remaining || 0
alreadyProcessed: checked,
remainingInQueue: remaining
};
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment