diff options
Diffstat (limited to 'browser/components/aboutlogins')
22 files changed, 443 insertions, 145 deletions
diff --git a/browser/components/aboutlogins/AboutLoginsChild.sys.mjs b/browser/components/aboutlogins/AboutLoginsChild.sys.mjs index 3fcdf77923..c7059d8f40 100644 --- a/browser/components/aboutlogins/AboutLoginsChild.sys.mjs +++ b/browser/components/aboutlogins/AboutLoginsChild.sys.mjs @@ -160,7 +160,11 @@ export class AboutLoginsChild extends JSWindowActorChild { } #aboutLoginsCopyLoginDetail(detail) { - lazy.ClipboardHelper.copyString(detail, lazy.ClipboardHelper.Sensitive); + lazy.ClipboardHelper.copyString( + detail, + this.windowContext, + lazy.ClipboardHelper.Sensitive + ); } #aboutLoginsCreateLogin(login) { diff --git a/browser/components/aboutlogins/AboutLoginsParent.sys.mjs b/browser/components/aboutlogins/AboutLoginsParent.sys.mjs index 28f56c2172..4342e60296 100644 --- a/browser/components/aboutlogins/AboutLoginsParent.sys.mjs +++ b/browser/components/aboutlogins/AboutLoginsParent.sys.mjs @@ -17,7 +17,6 @@ ChromeUtils.defineESModuleGetters(lazy, { LoginExport: "resource://gre/modules/LoginExport.sys.mjs", LoginHelper: "resource://gre/modules/LoginHelper.sys.mjs", MigrationUtils: "resource:///modules/MigrationUtils.sys.mjs", - OSKeyStore: "resource://gre/modules/OSKeyStore.sys.mjs", UIState: "resource://services-sync/UIState.sys.mjs", }); @@ -38,12 +37,6 @@ XPCOMUtils.defineLazyPreferenceGetter( ); XPCOMUtils.defineLazyPreferenceGetter( lazy, - "OS_AUTH_ENABLED", - "signon.management.page.os-auth.enabled", - true -); -XPCOMUtils.defineLazyPreferenceGetter( - lazy, "VULNERABLE_PASSWORDS_ENABLED", "signon.management.page.vulnerable-passwords.enabled", false @@ -266,11 +259,15 @@ export class AboutLoginsParent extends JSWindowActorParent { let messageText = { value: "NOT SUPPORTED" }; let captionText = { value: "" }; + const isOSAuthEnabled = lazy.LoginHelper.getOSAuthEnabled( + lazy.LoginHelper.OS_AUTH_FOR_PASSWORDS_PREF + ); + // This feature is only supported on Windows and macOS // but we still call in to OSKeyStore on Linux to get // the proper auth_details for Telemetry. // See bug 1614874 for Linux support. - if (lazy.OS_AUTH_ENABLED && lazy.OSKeyStore.canReauth()) { + if (isOSAuthEnabled) { messageId += "-" + AppConstants.platform; [messageText, captionText] = await lazy.AboutLoginsL10n.formatMessages([ { @@ -284,7 +281,7 @@ export class AboutLoginsParent extends JSWindowActorParent { let { isAuthorized, telemetryEvent } = await lazy.LoginHelper.requestReauth( this.browsingContext.embedderElement, - lazy.OS_AUTH_ENABLED, + isOSAuthEnabled, AboutLogins._authExpirationTime, messageText.value, captionText.value @@ -378,11 +375,15 @@ export class AboutLoginsParent extends JSWindowActorParent { let messageText = { value: "NOT SUPPORTED" }; let captionText = { value: "" }; + const isOSAuthEnabled = lazy.LoginHelper.getOSAuthEnabled( + lazy.LoginHelper.OS_AUTH_FOR_PASSWORDS_PREF + ); + // This feature is only supported on Windows and macOS // but we still call in to OSKeyStore on Linux to get // the proper auth_details for Telemetry. // See bug 1614874 for Linux support. - if (lazy.OSKeyStore.canReauth()) { + if (isOSAuthEnabled) { const messageId = EXPORT_PASSWORD_OS_AUTH_DIALOG_MESSAGE_IDS[AppConstants.platform]; if (!messageId) { diff --git a/browser/components/aboutlogins/LoginBreaches.sys.mjs b/browser/components/aboutlogins/LoginBreaches.sys.mjs index b2a0af5e39..496e5de575 100644 --- a/browser/components/aboutlogins/LoginBreaches.sys.mjs +++ b/browser/components/aboutlogins/LoginBreaches.sys.mjs @@ -2,6 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; + /** * Manages breach alerts for saved logins using data from Firefox Monitor via * RemoteSettings. @@ -16,6 +18,13 @@ ChromeUtils.defineESModuleGetters(lazy, { "resource://services-settings/RemoteSettingsClient.sys.mjs", }); +XPCOMUtils.defineLazyPreferenceGetter( + lazy, + "VULNERABLE_PASSWORDS_ENABLED", + "signon.management.page.vulnerable-passwords.enabled", + false +); + export const LoginBreaches = { REMOTE_SETTINGS_COLLECTION: "fxmonitor-breaches", @@ -138,6 +147,20 @@ export const LoginBreaches = { return vulnerablePasswordsByLoginGUID; }, + recordBreachAlertDismissal(loginGuid) { + const storageJSON = Services.logins.wrappedJSObject._storage; + return storageJSON.recordBreachAlertDismissal(loginGuid); + }, + + isVulnerablePassword(login) { + if (!lazy.VULNERABLE_PASSWORDS_ENABLED) { + return false; + } + + const storageJSON = Services.logins.wrappedJSObject._storage; + return storageJSON.isPotentiallyVulnerablePassword(login); + }, + async clearAllPotentiallyVulnerablePasswords() { await Services.logins.initializationPromise; const storageJSON = Services.logins.wrappedJSObject._storage; diff --git a/browser/components/aboutlogins/content/aboutLogins.html b/browser/components/aboutlogins/content/aboutLogins.html index 67712c8f29..a0c04149e7 100644 --- a/browser/components/aboutlogins/content/aboutLogins.html +++ b/browser/components/aboutlogins/content/aboutLogins.html @@ -11,7 +11,6 @@ <title data-l10n-id="about-logins-page-title-name"></title> <link rel="localization" href="branding/brand.ftl"> <link rel="localization" href="browser/aboutLogins.ftl"> - <link rel="localization" href="toolkit/branding/accounts.ftl"> <link rel="localization" href="toolkit/branding/brandings.ftl"> <script type="module" src="chrome://browser/content/aboutlogins/components/confirmation-dialog.mjs"></script> <script type="module" src="chrome://browser/content/aboutlogins/components/remove-logins-dialog.mjs"></script> diff --git a/browser/components/aboutlogins/content/aboutLoginsImportReport.html b/browser/components/aboutlogins/content/aboutLoginsImportReport.html index 9ab2641ca2..c9956e2cca 100644 --- a/browser/components/aboutlogins/content/aboutLoginsImportReport.html +++ b/browser/components/aboutlogins/content/aboutLoginsImportReport.html @@ -14,7 +14,6 @@ <title data-l10n-id="about-logins-import-report-page-title"></title> <link rel="localization" href="branding/brand.ftl" /> <link rel="localization" href="browser/aboutLogins.ftl" /> - <link rel="localization" href="toolkit/branding/accounts.ftl" /> <link rel="localization" href="toolkit/branding/brandings.ftl" /> <script type="module" diff --git a/browser/components/aboutlogins/content/components/login-item.css b/browser/components/aboutlogins/content/components/login-item.css index 4a3d85d859..e9e91f78ed 100644 --- a/browser/components/aboutlogins/content/components/login-item.css +++ b/browser/components/aboutlogins/content/components/login-item.css @@ -64,11 +64,6 @@ form { display: none; } -input[type="password"], -input[type="text"], -input[type="url"] { - text-align: match-parent !important; /* override `all: unset` in the rule below */ -} :host(:not([data-editing])) input[type="password"]:read-only, input[type="text"]:read-only, @@ -82,6 +77,17 @@ input[type="url"]:read-only { width: 100%; } +input:is([type="password"], [type="text"], [type="url"]) { + /* Override all: unset above */ + appearance: textfield !important; + text-align: match-parent !important; +} + +input.password-display, +input[name="password"] { + font-family: monospace !important; /* Override all: unset above */ +} + /* We can't use `margin-inline-start` here because we force * the input to have dir="ltr", so we set the margin manually * using the parent element's directionality. */ @@ -197,11 +203,6 @@ moz-button-group, box-shadow: none; } -input.password-display, -input[name="password"] { - font-family: monospace !important; /* override `all: unset` in the rule above */ -} - .reveal-password-checkbox { appearance: none; background-image: url("resource://gre-resources/password.svg"); diff --git a/browser/components/aboutlogins/tests/browser/browser.toml b/browser/components/aboutlogins/tests/browser/browser.toml index 0b38e0dda1..07c49c4c88 100644 --- a/browser/components/aboutlogins/tests/browser/browser.toml +++ b/browser/components/aboutlogins/tests/browser/browser.toml @@ -2,7 +2,6 @@ support-files = ["head.js"] prefs = [ "signon.management.page.vulnerable-passwords.enabled=true", - "signon.management.page.os-auth.enabled=true", "toolkit.telemetry.ipcBatchTimeout=10", # lower the interval for event telemetry in the content process to update the parent process ] # Run first so content events from previous tests won't trickle in. diff --git a/browser/components/aboutlogins/tests/browser/browser_aaa_eventTelemetry_run_first.js b/browser/components/aboutlogins/tests/browser/browser_aaa_eventTelemetry_run_first.js index 52b2eb02a2..4f3cdbe48e 100644 --- a/browser/components/aboutlogins/tests/browser/browser_aaa_eventTelemetry_run_first.js +++ b/browser/components/aboutlogins/tests/browser/browser_aaa_eventTelemetry_run_first.js @@ -72,7 +72,10 @@ add_task(async function test_telemetry_events() { await LoginTestUtils.telemetry.waitForEventCount(3); if (OSKeyStoreTestUtils.canTestOSKeyStoreLogin()) { - let reauthObserved = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(true); + let reauthObserved = Promise.resolve(); + if (OSKeyStore.canReauth()) { + reauthObserved = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(true); + } await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () { let loginItem = content.document.querySelector("login-item"); let copyButton = loginItem.shadowRoot.querySelector( @@ -106,9 +109,12 @@ add_task(async function test_telemetry_events() { // Show the password if (OSKeyStoreTestUtils.canTestOSKeyStoreLogin()) { - let reauthObserved = forceAuthTimeoutAndWaitForOSKeyStoreLogin({ - loginResult: true, - }); + let reauthObserved = Promise.resolve(); + if (OSKeyStore.canReauth()) { + reauthObserved = forceAuthTimeoutAndWaitForOSKeyStoreLogin({ + loginResult: true, + }); + } nextTelemetryEventCount++; // An extra event is observed for the reauth event. await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () { let loginItem = content.document.querySelector("login-item"); diff --git a/browser/components/aboutlogins/tests/browser/browser_alertDismissedAfterChangingPassword.js b/browser/components/aboutlogins/tests/browser/browser_alertDismissedAfterChangingPassword.js index b2b036121a..b28dcf25ee 100644 --- a/browser/components/aboutlogins/tests/browser/browser_alertDismissedAfterChangingPassword.js +++ b/browser/components/aboutlogins/tests/browser/browser_alertDismissedAfterChangingPassword.js @@ -32,6 +32,7 @@ add_setup(async function () { gBrowser, url: "about:logins", }); + registerCleanupFunction(() => { BrowserTestUtils.removeTab(gBrowser.selectedTab); Services.logins.removeAllUserFacingLogins(); @@ -104,9 +105,13 @@ add_task(async function test_added_login_shows_breach_warning() { return; } - let reauthObserved = forceAuthTimeoutAndWaitForOSKeyStoreLogin({ - loginResult: true, - }); + let reauthObserved = Promise.resolve(); + if (OSKeyStore.canReauth()) { + reauthObserved = forceAuthTimeoutAndWaitForOSKeyStoreLogin({ + loginResult: true, + }); + } + // Change the password on the breached login and check that the // login is no longer marked as breached. The vulnerable login // should still be marked as vulnerable afterwards. diff --git a/browser/components/aboutlogins/tests/browser/browser_contextmenuFillLogins.js b/browser/components/aboutlogins/tests/browser/browser_contextmenuFillLogins.js index ee1527d792..55180435c7 100644 --- a/browser/components/aboutlogins/tests/browser/browser_contextmenuFillLogins.js +++ b/browser/components/aboutlogins/tests/browser/browser_contextmenuFillLogins.js @@ -32,8 +32,10 @@ if (OSKeyStoreTestUtils.canTestOSKeyStoreLogin()) { gTests[gTests.length] = { name: "test contextmenu on password field in edit login view", async setup(browser) { - let osAuthDialogShown = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(true); - + let osAuthDialogShown = Promise.resolve(); + if (OSKeyStore.canReauth()) { + osAuthDialogShown = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(true); + } // load up the edit login view await SpecialPowers.spawn( browser, diff --git a/browser/components/aboutlogins/tests/browser/browser_copyToClipboardButton.js b/browser/components/aboutlogins/tests/browser/browser_copyToClipboardButton.js index 8b0d3b7bf6..1f6eff8806 100644 --- a/browser/components/aboutlogins/tests/browser/browser_copyToClipboardButton.js +++ b/browser/components/aboutlogins/tests/browser/browser_copyToClipboardButton.js @@ -49,9 +49,11 @@ add_task(async function test() { info( "waiting for " + testObj.expectedValue + " to be placed on clipboard" ); - let reauthObserved = true; + let reauthObserved = Promise.resolve(); if (testObj.copyButtonSelector.includes("password")) { - reauthObserved = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(true); + if (OSKeyStore.canReauth()) { + reauthObserved = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(true); + } } await SimpleTest.promiseClipboardChange( diff --git a/browser/components/aboutlogins/tests/browser/browser_createLogin.js b/browser/components/aboutlogins/tests/browser/browser_createLogin.js index 4aac8cece1..46c4487ba3 100644 --- a/browser/components/aboutlogins/tests/browser/browser_createLogin.js +++ b/browser/components/aboutlogins/tests/browser/browser_createLogin.js @@ -232,9 +232,12 @@ add_task(async function test_create_login() { continue; } - let reauthObserved = forceAuthTimeoutAndWaitForOSKeyStoreLogin({ - loginResult: true, - }); + let reauthObserved = Promise.resolve(); + if (OSKeyStore.canReauth()) { + reauthObserved = forceAuthTimeoutAndWaitForOSKeyStoreLogin({ + loginResult: true, + }); + } await SpecialPowers.spawn(browser, [], async () => { let loginItem = Cu.waiveXrays( content.document.querySelector("login-item") diff --git a/browser/components/aboutlogins/tests/browser/browser_deleteLogin.js b/browser/components/aboutlogins/tests/browser/browser_deleteLogin.js index 90195d0e0a..4fc250523c 100644 --- a/browser/components/aboutlogins/tests/browser/browser_deleteLogin.js +++ b/browser/components/aboutlogins/tests/browser/browser_deleteLogin.js @@ -71,7 +71,10 @@ add_task(async function test_login_item() { }, "Waiting for login item to get populated"); Assert.ok(loginItemPopulated, "The login item should get populated"); }); - let reauthObserved = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(true); + let reauthObserved = Promise.resolve(); + if (OSKeyStore.canReauth()) { + reauthObserved = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(true); + } await SpecialPowers.spawn(browser, [], async () => { let loginItem = Cu.waiveXrays( content.document.querySelector("login-item") diff --git a/browser/components/aboutlogins/tests/browser/browser_loginItemErrors.js b/browser/components/aboutlogins/tests/browser/browser_loginItemErrors.js index a78f49d3c9..1789d47b8e 100644 --- a/browser/components/aboutlogins/tests/browser/browser_loginItemErrors.js +++ b/browser/components/aboutlogins/tests/browser/browser_loginItemErrors.js @@ -107,7 +107,10 @@ add_task(async function test_showLoginItemErrors() { // The rest of the test uses Edit mode which causes an OS prompt in official builds. return; } - let reauthObserved = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(true); + let reauthObserved = Promise.resolve(); + if (OSKeyStore.canReauth()) { + reauthObserved = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(true); + } await SpecialPowers.spawn( browser, [[LoginHelper.loginToVanillaObject(LOGIN_TO_UPDATE), LOGIN_UPDATES]], diff --git a/browser/components/aboutlogins/tests/browser/browser_openExport.js b/browser/components/aboutlogins/tests/browser/browser_openExport.js index 1a61510862..f4f7761259 100644 --- a/browser/components/aboutlogins/tests/browser/browser_openExport.js +++ b/browser/components/aboutlogins/tests/browser/browser_openExport.js @@ -8,9 +8,6 @@ * Test the export logins file picker appears. */ -let { OSKeyStore } = ChromeUtils.importESModule( - "resource://gre/modules/OSKeyStore.sys.mjs" -); let { TelemetryTestUtils } = ChromeUtils.importESModule( "resource://testing-common/TelemetryTestUtils.sys.mjs" ); diff --git a/browser/components/aboutlogins/tests/browser/browser_openSite.js b/browser/components/aboutlogins/tests/browser/browser_openSite.js index f33d57a8e4..c3fc8d5cd1 100644 --- a/browser/components/aboutlogins/tests/browser/browser_openSite.js +++ b/browser/components/aboutlogins/tests/browser/browser_openSite.js @@ -44,7 +44,10 @@ add_task(async function test_launch_login_item() { gBrowser, TEST_LOGIN1.origin + "/" ); - let reauthObserved = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(true); + let reauthObserved = Promise.resolve(); + if (OSKeyStore.canReauth()) { + reauthObserved = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(true); + } await SpecialPowers.spawn(browser, [], async () => { let loginItem = Cu.waiveXrays(content.document.querySelector("login-item")); loginItem._editButton.click(); diff --git a/browser/components/aboutlogins/tests/browser/browser_osAuthDialog.js b/browser/components/aboutlogins/tests/browser/browser_osAuthDialog.js index 9c2688cc77..21a5eeda12 100644 --- a/browser/components/aboutlogins/tests/browser/browser_osAuthDialog.js +++ b/browser/components/aboutlogins/tests/browser/browser_osAuthDialog.js @@ -1,12 +1,105 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -add_task(async function test() { - info( - `updatechannel: ${UpdateUtils.getUpdateChannel(false)}; platform: ${ - AppConstants.platform - }` +"use strict"; + +// On mac, this test times out in chaos mode +requestLongerTimeout(2); + +const PAGE_PREFS = "about:preferences"; +const PAGE_PRIVACY = PAGE_PREFS + "#privacy"; +const SELECTORS = { + reauthCheckbox: "#osReauthCheckbox", +}; + +add_setup(async function () { + TEST_LOGIN1 = await addLogin(TEST_LOGIN1); + TEST_LOGIN2 = await addLogin(TEST_LOGIN2); + // Undo mocking from head.js + sinon.restore(); +}); + +add_task(async function test_os_auth_enabled_with_checkbox() { + let finalPrefPaneLoaded = TestUtils.topicObserved("sync-pane-loaded"); + await BrowserTestUtils.withNewTab( + { gBrowser, url: PAGE_PRIVACY }, + async function (browser) { + await finalPrefPaneLoaded; + + await SpecialPowers.spawn( + browser, + [SELECTORS, AppConstants.NIGHTLY_BUILD], + async (selectors, isNightly) => { + is( + content.document.querySelector(selectors.reauthCheckbox).checked, + isNightly, + "OSReauth for Passwords should be checked" + ); + } + ); + is( + LoginHelper.getOSAuthEnabled(PASSWORDS_OS_REAUTH_PREF), + AppConstants.NIGHTLY_BUILD, + "OSAuth should be enabled." + ); + } ); +}); + +add_task(async function test_os_auth_disabled_with_checkbox() { + let finalPrefPaneLoaded = TestUtils.topicObserved("sync-pane-loaded"); + LoginHelper.setOSAuthEnabled(PASSWORDS_OS_REAUTH_PREF, false); + await BrowserTestUtils.withNewTab( + { gBrowser, url: PAGE_PRIVACY }, + async function (browser) { + await finalPrefPaneLoaded; + + await SpecialPowers.spawn(browser, [SELECTORS], async selectors => { + is( + content.document.querySelector(selectors.reauthCheckbox).checked, + false, + "OSReauth for passwords should be unchecked" + ); + }); + is( + LoginHelper.getOSAuthEnabled(PASSWORDS_OS_REAUTH_PREF), + false, + "OSAuth should be disabled" + ); + } + ); + LoginHelper.setOSAuthEnabled(PASSWORDS_OS_REAUTH_PREF, true); +}); + +add_task(async function test_OSAuth_enabled_with_random_value_in_pref() { + let finalPrefPaneLoaded = TestUtils.topicObserved("sync-pane-loaded"); + await SpecialPowers.pushPrefEnv({ + set: [[PASSWORDS_OS_REAUTH_PREF, "poutine-gravy"]], + }); + await BrowserTestUtils.withNewTab( + { gBrowser, url: PAGE_PRIVACY }, + async function (browser) { + await finalPrefPaneLoaded; + await SpecialPowers.spawn(browser, [SELECTORS], async selectors => { + let reauthCheckbox = content.document.querySelector( + selectors.reauthCheckbox + ); + is( + reauthCheckbox.checked, + true, + "OSReauth for passwords should be checked" + ); + }); + is( + LoginHelper.getOSAuthEnabled(PASSWORDS_OS_REAUTH_PREF), + true, + "OSAuth should be enabled since the pref does not decrypt to 'opt out'." + ); + } + ); +}); + +add_task(async function test_osAuth_shown_on_edit_login() { if (!OSKeyStoreTestUtils.canTestOSKeyStoreLogin()) { Assert.ok( true, @@ -14,41 +107,51 @@ add_task(async function test() { ); return; } - - TEST_LOGIN1 = await addLogin(TEST_LOGIN1); - await BrowserTestUtils.openNewForegroundTab({ gBrowser, url: "about:logins", }); - - registerCleanupFunction(function () { - Services.logins.removeAllUserFacingLogins(); - BrowserTestUtils.removeTab(gBrowser.selectedTab); - }); - - // Show OS auth dialog when Reveal Password checkbox is checked if not on a new login - let osAuthDialogShown = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(false); + let osAuthDialogShown = Promise.resolve(); + if (OSKeyStore.canReauth()) { + osAuthDialogShown = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(true); + } await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () { let loginItem = content.document.querySelector("login-item"); - let revealCheckbox = loginItem.shadowRoot.querySelector( - ".reveal-password-checkbox" + Assert.ok( + !loginItem.dataset.editing, + "Not in edit mode before clicking 'Edit'" ); - revealCheckbox.click(); + let editButton = loginItem.shadowRoot.querySelector("edit-button"); + editButton.click(); }); + await osAuthDialogShown; - info("OS auth dialog shown and canceled"); - await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () { - let loginItem = content.document.querySelector("login-item"); - let revealCheckbox = loginItem.shadowRoot.querySelector( - ".reveal-password-checkbox" + info("OS auth dialog shown and authenticated"); + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async () => { + await ContentTaskUtils.waitForCondition( + () => content.document.querySelector("login-item").dataset.editing, + "login item should be in 'edit' mode" ); + }); + BrowserTestUtils.removeTab(gBrowser.selectedTab); +}); + +add_task(async function test_osAuth_shown_on_reveal_password() { + if (!OSKeyStoreTestUtils.canTestOSKeyStoreLogin()) { Assert.ok( - !revealCheckbox.checked, - "reveal checkbox should be unchecked if OS auth dialog canceled" + true, + `skipping test since oskeystore cannot be automated in this environment` ); + return; + } + await BrowserTestUtils.openNewForegroundTab({ + gBrowser, + url: "about:logins", }); - osAuthDialogShown = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(true); + let osAuthDialogShown = Promise.resolve(); + if (OSKeyStore.canReauth()) { + osAuthDialogShown = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(true); + } await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () { let loginItem = content.document.querySelector("login-item"); let revealCheckbox = loginItem.shadowRoot.querySelector( @@ -68,10 +171,70 @@ add_task(async function test() { "reveal checkbox should be checked if OS auth dialog authenticated" ); }); + BrowserTestUtils.removeTab(gBrowser.selectedTab); +}); - info("'Edit' shouldn't show the prompt since the user has authenticated now"); +add_task(async function test_osAuth_shown_on_copy_password() { + if (!OSKeyStoreTestUtils.canTestOSKeyStoreLogin()) { + Assert.ok( + true, + `skipping test since oskeystore cannot be automated in this environment` + ); + return; + } + await BrowserTestUtils.openNewForegroundTab({ + gBrowser, + url: "about:logins", + }); + let osAuthDialogShown = Promise.resolve(); + if (OSKeyStore.canReauth()) { + osAuthDialogShown = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(true); + } await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () { let loginItem = content.document.querySelector("login-item"); + let copyPassword = loginItem.shadowRoot.querySelector( + "copy-password-button" + ); + copyPassword.click(); + }); + await osAuthDialogShown; + info("OS auth dialog shown and authenticated"); + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () { + info("Password was copied to clipboard"); + }); + BrowserTestUtils.removeTab(gBrowser.selectedTab); +}); + +add_task(async function test_osAuth_not_shown_within_expiration_time() { + if (!OSKeyStoreTestUtils.canTestOSKeyStoreLogin()) { + Assert.ok( + true, + `skipping test since oskeystore cannot be automated in this environment` + ); + return; + } + await BrowserTestUtils.openNewForegroundTab({ + gBrowser, + url: "about:logins", + }); + let osAuthDialogShown = Promise.resolve(); + if (OSKeyStore.canReauth()) { + osAuthDialogShown = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(true); + } + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () { + let loginItem = content.document.querySelector("login-item"); + let copyPassword = loginItem.shadowRoot.querySelector( + "copy-password-button" + ); + copyPassword.click(); + }); + await osAuthDialogShown; + info("OS auth dialog shown and authenticated"); + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () { + info( + "'Edit' shouldn't show the prompt since the user has authenticated now" + ); + let loginItem = content.document.querySelector("login-item"); Assert.ok( !loginItem.dataset.editing, "Not in edit mode before clicking 'Edit'" @@ -85,16 +248,43 @@ add_task(async function test() { ); Assert.ok(loginItem.dataset.editing, "In edit mode"); }); - - info("Test that the OS auth prompt is shown after about:logins is reopened"); BrowserTestUtils.removeTab(gBrowser.selectedTab); +}); + +add_task(async function test_osAuth_shown_after_expiration_timeout() { + if (!OSKeyStoreTestUtils.canTestOSKeyStoreLogin()) { + Assert.ok( + true, + `skipping test since oskeystore cannot be automated in this environment` + ); + return; + } await BrowserTestUtils.openNewForegroundTab({ gBrowser, url: "about:logins", }); + let osAuthDialogShown = Promise.resolve(); + if (OSKeyStore.canReauth()) { + osAuthDialogShown = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(true); + } + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () { + let loginItem = content.document.querySelector("login-item"); + let copyPassword = loginItem.shadowRoot.querySelector( + "copy-password-button" + ); + copyPassword.click(); + }); + await osAuthDialogShown; + info("OS auth dialog shown and authenticated"); + + // Show OS auth dialog since the timeout will have expired + + if (OSKeyStore.canReauth()) { + osAuthDialogShown = forceAuthTimeoutAndWaitForOSKeyStoreLogin({ + loginResult: true, + }); + } - // Show OS auth dialog since the page has been reloaded. - osAuthDialogShown = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(false); await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () { let loginItem = content.document.querySelector("login-item"); let revealCheckbox = loginItem.shadowRoot.querySelector( @@ -103,63 +293,91 @@ add_task(async function test() { revealCheckbox.click(); }); await osAuthDialogShown; - info("OS auth dialog shown and canceled"); + info("OS auth dialog shown and authenticated"); + await BrowserTestUtils.removeTab(gBrowser.selectedTab); +}); - // Show OS auth dialog since the previous attempt was canceled - osAuthDialogShown = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(true); +add_task(async function test_osAuth_shown_on_reload() { + if (!OSKeyStoreTestUtils.canTestOSKeyStoreLogin()) { + Assert.ok( + true, + `skipping test since oskeystore cannot be automated in this environment` + ); + return; + } + await BrowserTestUtils.openNewForegroundTab({ + gBrowser, + url: "about:logins", + }); + let osAuthDialogShown = Promise.resolve(); + if (OSKeyStore.canReauth()) { + osAuthDialogShown = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(true); + } await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () { let loginItem = content.document.querySelector("login-item"); - let revealCheckbox = loginItem.shadowRoot.querySelector( - ".reveal-password-checkbox" + let copyPassword = loginItem.shadowRoot.querySelector( + "copy-password-button" ); - revealCheckbox.click(); - info("clicking on reveal checkbox to hide the password"); - revealCheckbox.click(); + copyPassword.click(); }); await osAuthDialogShown; - info("OS auth dialog shown and passed"); + info("OS auth dialog shown and authenticated"); - // Show OS auth dialog since the timeout will have expired - osAuthDialogShown = forceAuthTimeoutAndWaitForOSKeyStoreLogin({ - loginResult: true, + info("Test that the OS auth prompt is shown after about:logins is reopened"); + BrowserTestUtils.removeTab(gBrowser.selectedTab); + await BrowserTestUtils.openNewForegroundTab({ + gBrowser, + url: "about:logins", }); + + // Show OS auth dialog since the page has been reloaded. + osAuthDialogShown = Promise.resolve(); + if (OSKeyStore.canReauth()) { + osAuthDialogShown = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(true); + } await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () { let loginItem = content.document.querySelector("login-item"); let revealCheckbox = loginItem.shadowRoot.querySelector( ".reveal-password-checkbox" ); - info("clicking on reveal checkbox to reveal password"); revealCheckbox.click(); }); - info("waiting for os auth dialog"); await osAuthDialogShown; - info("OS auth dialog shown and passed after timeout expiration"); - - // Disable the OS auth feature and confirm the prompt doesn't appear - await SpecialPowers.pushPrefEnv({ - set: [["signon.management.page.os-auth.enabled", false]], - }); - info("Reload about:logins to reset the timeout"); + info("OS auth dialog shown and authenticated"); BrowserTestUtils.removeTab(gBrowser.selectedTab); +}); + +add_task(async function test_osAuth_shown_again_on_cancel() { + if (!OSKeyStoreTestUtils.canTestOSKeyStoreLogin()) { + Assert.ok( + true, + `skipping test since oskeystore cannot be automated in this environment` + ); + return; + } await BrowserTestUtils.openNewForegroundTab({ gBrowser, url: "about:logins", }); - - info("'Edit' shouldn't show the prompt since the feature has been disabled"); + let osAuthDialogShown = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(false); await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () { let loginItem = content.document.querySelector("login-item"); - Assert.ok( - !loginItem.dataset.editing, - "Not in edit mode before clicking 'Edit'" + let revealCheckbox = loginItem.shadowRoot.querySelector( + ".reveal-password-checkbox" ); - let editButton = loginItem.shadowRoot.querySelector("edit-button"); - editButton.click(); - - await ContentTaskUtils.waitForCondition( - () => loginItem.dataset.editing, - "waiting for 'edit' mode" + revealCheckbox.click(); + }); + await osAuthDialogShown; + info("OS auth dialog shown and canceled"); + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () { + let loginItem = content.document.querySelector("login-item"); + let revealCheckbox = loginItem.shadowRoot.querySelector( + ".reveal-password-checkbox" + ); + Assert.ok( + !revealCheckbox.checked, + "reveal checkbox should be unchecked if OS auth dialog canceled" ); - Assert.ok(loginItem.dataset.editing, "In edit mode"); }); + BrowserTestUtils.removeTab(gBrowser.selectedTab); }); diff --git a/browser/components/aboutlogins/tests/browser/browser_removeAllDialog.js b/browser/components/aboutlogins/tests/browser/browser_removeAllDialog.js index c5879ceeaf..bcd09ca9a2 100644 --- a/browser/components/aboutlogins/tests/browser/browser_removeAllDialog.js +++ b/browser/components/aboutlogins/tests/browser/browser_removeAllDialog.js @@ -2,8 +2,6 @@ * http://creativecommons.org/publicdomain/zero/1.0/ */ /* eslint-disable mozilla/no-arbitrary-setTimeout */ -const OS_REAUTH_PREF = "signon.management.page.os-auth.enabled"; - async function openRemoveAllDialog(browser) { await SimpleTest.promiseFocus(browser); await BrowserTestUtils.synthesizeMouseAtCenter("menu-button", {}, browser); @@ -80,9 +78,11 @@ async function waitForRemoveAllLogins() { } add_setup(async function () { - await SpecialPowers.pushPrefEnv({ - set: [[OS_REAUTH_PREF, false]], - }); + // Undo mocking from head.js + sinon.restore(); + + let oldPrefValue = LoginHelper.getOSAuthEnabled(PASSWORDS_OS_REAUTH_PREF); + LoginHelper.setOSAuthEnabled(PASSWORDS_OS_REAUTH_PREF, false); await BrowserTestUtils.openNewForegroundTab({ gBrowser, url: "about:logins", @@ -90,7 +90,7 @@ add_setup(async function () { registerCleanupFunction(async () => { BrowserTestUtils.removeTab(gBrowser.selectedTab); Services.logins.removeAllUserFacingLogins(); - await SpecialPowers.popPrefEnv(); + LoginHelper.setOSAuthEnabled(PASSWORDS_OS_REAUTH_PREF, oldPrefValue); }); TEST_LOGIN1 = await addLogin(TEST_LOGIN1); }); diff --git a/browser/components/aboutlogins/tests/browser/browser_tabKeyNav.js b/browser/components/aboutlogins/tests/browser/browser_tabKeyNav.js index 890d39a316..6e2047e8e5 100644 --- a/browser/components/aboutlogins/tests/browser/browser_tabKeyNav.js +++ b/browser/components/aboutlogins/tests/browser/browser_tabKeyNav.js @@ -100,14 +100,6 @@ add_task(async function test_tab_key_nav() { expectedSelector ); - // By default, MacOS will skip over certain text controls, such as links. - if ( - content.window.navigator.platform.toLowerCase().includes("mac") && - expectedElement.tagName === "A" - ) { - continue; - } - const actualElement = getFocusedElement(); Assert.equal( @@ -126,13 +118,6 @@ add_task(async function test_tab_key_nav() { content.document, expectedSelector ); - // By default, MacOS will skip over certain text controls, such as links. - if ( - content.window.navigator.platform.toLowerCase().includes("mac") && - expectedElement.tagName === "A" - ) { - continue; - } const actualElement = getFocusedElement(); Assert.equal( diff --git a/browser/components/aboutlogins/tests/browser/browser_updateLogin.js b/browser/components/aboutlogins/tests/browser/browser_updateLogin.js index 686b3951a1..192ff0270d 100644 --- a/browser/components/aboutlogins/tests/browser/browser_updateLogin.js +++ b/browser/components/aboutlogins/tests/browser/browser_updateLogin.js @@ -124,7 +124,10 @@ add_task(async function test_login_item() { } let browser = gBrowser.selectedBrowser; - let reauthObserved = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(true); + let reauthObserved = Promise.resolve(); + if (OSKeyStore.canReauth()) { + reauthObserved = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(true); + } await SpecialPowers.spawn( browser, [LoginHelper.loginToVanillaObject(TEST_LOGIN1)], @@ -163,9 +166,11 @@ add_task(async function test_login_item() { ], test_discard_dialog ); - reauthObserved = forceAuthTimeoutAndWaitForOSKeyStoreLogin({ - loginResult: true, - }); + if (OSKeyStore.canReauth()) { + reauthObserved = forceAuthTimeoutAndWaitForOSKeyStoreLogin({ + loginResult: true, + }); + } await SpecialPowers.spawn(browser, [], async () => { let loginItem = Cu.waiveXrays(content.document.querySelector("login-item")); let editButton = loginItem.shadowRoot @@ -184,9 +189,11 @@ add_task(async function test_login_item() { ], test_discard_dialog ); - reauthObserved = forceAuthTimeoutAndWaitForOSKeyStoreLogin({ - loginResult: true, - }); + if (OSKeyStore.canReauth()) { + reauthObserved = forceAuthTimeoutAndWaitForOSKeyStoreLogin({ + loginResult: true, + }); + } await SpecialPowers.spawn(browser, [], async () => { let loginItem = Cu.waiveXrays(content.document.querySelector("login-item")); let editButton = loginItem.shadowRoot @@ -289,9 +296,11 @@ add_task(async function test_login_item() { ); } ); - reauthObserved = forceAuthTimeoutAndWaitForOSKeyStoreLogin({ - loginResult: true, - }); + if (OSKeyStore.canReauth()) { + reauthObserved = forceAuthTimeoutAndWaitForOSKeyStoreLogin({ + loginResult: true, + }); + } await SpecialPowers.spawn(browser, [], async () => { let loginItem = Cu.waiveXrays(content.document.querySelector("login-item")); let editButton = loginItem.shadowRoot @@ -360,9 +369,11 @@ add_task(async function test_login_item() { "Password field width should be correctly updated" ); }); - reauthObserved = forceAuthTimeoutAndWaitForOSKeyStoreLogin({ - loginResult: true, - }); + if (OSKeyStore.canReauth()) { + reauthObserved = forceAuthTimeoutAndWaitForOSKeyStoreLogin({ + loginResult: true, + }); + } await SpecialPowers.spawn(browser, [], async () => { let loginItem = Cu.waiveXrays(content.document.querySelector("login-item")); let editButton = loginItem.shadowRoot diff --git a/browser/components/aboutlogins/tests/browser/head.js b/browser/components/aboutlogins/tests/browser/head.js index 82d3cf2062..22ab7ef964 100644 --- a/browser/components/aboutlogins/tests/browser/head.js +++ b/browser/components/aboutlogins/tests/browser/head.js @@ -13,6 +13,24 @@ let { _AboutLogins } = ChromeUtils.importESModule( let { OSKeyStoreTestUtils } = ChromeUtils.importESModule( "resource://testing-common/OSKeyStoreTestUtils.sys.mjs" ); + +const { OSKeyStore } = ChromeUtils.importESModule( + "resource://gre/modules/OSKeyStore.sys.mjs" +); + +let { sinon } = ChromeUtils.importESModule( + "resource://testing-common/Sinon.sys.mjs" +); + +// Always pretend OS Auth is enabled in this dir. +if (OSKeyStoreTestUtils.canTestOSKeyStoreLogin() && OSKeyStore.canReauth()) { + // Enable OS reauth so we can test it. + sinon.stub(LoginHelper, "getOSAuthEnabled").returns(true); + registerCleanupFunction(() => { + sinon.restore(); + }); +} + var { LoginTestUtils } = ChromeUtils.importESModule( "resource://testing-common/LoginTestUtils.sys.mjs" ); @@ -53,6 +71,15 @@ let TEST_LOGIN3 = new nsLoginInfo( ); TEST_LOGIN3.QueryInterface(Ci.nsILoginMetaInfo).timePasswordChanged = 123456; +const PASSWORDS_OS_REAUTH_PREF = "signon.management.page.os-auth.optout"; +const CryptoErrors = { + USER_CANCELED_PASSWORD: "User canceled primary password entry", + ENCRYPTION_FAILURE: "Couldn't encrypt string", + INVALID_ARG_ENCRYPT: "Need at least one plaintext to encrypt", + INVALID_ARG_DECRYPT: "Need at least one ciphertext to decrypt", + DECRYPTION_FAILURE: "Couldn't decrypt string", +}; + async function addLogin(login) { const result = await Services.logins.addLoginAsync(login); registerCleanupFunction(() => { @@ -153,6 +180,12 @@ add_setup(async function setup_head() { // Ignore MarionetteEvents error (Bug 1730837, Bug 1710079). return; } + if (msg.errorMessage.includes(CryptoErrors.DECRYPTION_FAILURE)) { + // Ignore decyption errors, we want to test if decryption failed + // But we cannot use try / catch in the test to catch this for some reason + // Bug 1403081 and Bug 1877720 + return; + } Assert.ok(false, msg.message || msg.errorMessage); }); diff --git a/browser/components/aboutlogins/tests/chrome/test_confirm_delete_dialog.html b/browser/components/aboutlogins/tests/chrome/test_confirm_delete_dialog.html index 68a58aee4f..afbae0c310 100644 --- a/browser/components/aboutlogins/tests/chrome/test_confirm_delete_dialog.html +++ b/browser/components/aboutlogins/tests/chrome/test_confirm_delete_dialog.html @@ -24,6 +24,7 @@ Test the confirmation-dialog component </pre> <script> /** Test the confirmation-dialog component **/ +let isWin = navigator.platform.includes("Win"); let options = { title: "confirm-delete-dialog-title", @@ -65,7 +66,7 @@ add_task(async function test_initial_focus() { add_task(async function test_tab_focus() { gConfirmationDialog.show(options); ok(!gConfirmationDialog.hidden, "The dialog should be visible"); - sendKey("TAB"); + synthesizeKey("VK_TAB", { shiftKey: !isWin }); is(gConfirmationDialog.shadowRoot.activeElement, cancelButton, "After opening the dialog and tabbing once, the cancel button should be focused"); gConfirmationDialog.hide(); @@ -86,7 +87,7 @@ add_task(async function test_enter_key_to_cancel() { add_task(async function test_enter_key_to_confirm() { let showPromise = gConfirmationDialog.show(options); ok(!gConfirmationDialog.hidden, "The dialog should be visible"); - sendKey("TAB"); + synthesizeKey("VK_TAB", { shiftKey: !isWin }); sendKey("RETURN"); try { await showPromise; |