From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../browser_context_menu_generated_password.js | 482 +++++++++++++++++++++ 1 file changed, 482 insertions(+) create mode 100644 toolkit/components/passwordmgr/test/browser/browser_context_menu_generated_password.js (limited to 'toolkit/components/passwordmgr/test/browser/browser_context_menu_generated_password.js') diff --git a/toolkit/components/passwordmgr/test/browser/browser_context_menu_generated_password.js b/toolkit/components/passwordmgr/test/browser/browser_context_menu_generated_password.js new file mode 100644 index 0000000000..4e4edb7b14 --- /dev/null +++ b/toolkit/components/passwordmgr/test/browser/browser_context_menu_generated_password.js @@ -0,0 +1,482 @@ +/** + * Test the password manager context menu item can fill password fields with a generated password. + */ + +/* eslint no-shadow:"off" */ + +"use strict"; + +// The origin for the test URIs. +const TEST_ORIGIN = "https://example.com"; +const FORM_PAGE_PATH = + "/browser/toolkit/components/passwordmgr/test/browser/form_basic_login.html"; +const CONTEXT_MENU = document.getElementById("contentAreaContextMenu"); + +const passwordInputSelector = "#form-basic-password"; + +registerCleanupFunction(async function cleanup_resetPrefs() { + await SpecialPowers.popPrefEnv(); +}); + +add_setup(async function () { + await SpecialPowers.pushPrefEnv({ + set: [ + ["signon.generation.available", true], + ["signon.generation.enabled", true], + ], + }); + // assert that there are no logins + let logins = Services.logins.getAllLogins(); + Assert.equal(logins.length, 0, "There are no logins"); +}); + +add_task(async function test_hidden_by_prefs() { + await SpecialPowers.pushPrefEnv({ + set: [ + ["signon.generation.available", true], + ["signon.generation.enabled", false], + ], + }); + + // test that the generated password option is not present when the feature is not enabled + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: TEST_ORIGIN + FORM_PAGE_PATH, + }, + async function (browser) { + await SimpleTest.promiseFocus(browser.ownerGlobal); + + await openPasswordContextMenu(browser, passwordInputSelector); + let generatedPasswordItem = document.getElementById( + "fill-login-generated-password" + ); + Assert.ok( + !BrowserTestUtils.is_visible(generatedPasswordItem), + "generated password item is hidden" + ); + + CONTEXT_MENU.hidePopup(); + } + ); + await SpecialPowers.popPrefEnv(); +}); + +add_task(async function test_fill_hidden_by_login_saving_disabled() { + // test that the generated password option is not present when the user + // disabled password saving for the site. + Services.logins.setLoginSavingEnabled(TEST_ORIGIN, false); + + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: TEST_ORIGIN + FORM_PAGE_PATH, + }, + async function (browser) { + await SimpleTest.promiseFocus(browser.ownerGlobal); + + await openPasswordContextMenu(browser, passwordInputSelector); + let generatedPasswordItem = document.getElementById( + "fill-login-generated-password" + ); + Assert.ok( + !BrowserTestUtils.is_visible(generatedPasswordItem), + "generated password item is hidden" + ); + + CONTEXT_MENU.hidePopup(); + } + ); + + Services.logins.setLoginSavingEnabled(TEST_ORIGIN, true); +}); + +add_task(async function test_fill_hidden_by_locked_primary_password() { + // test that the generated password option is not present when the user + // didn't unlock the primary password. + LoginTestUtils.primaryPassword.enable(); + + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: TEST_ORIGIN + FORM_PAGE_PATH, + }, + async function (browser) { + await SimpleTest.promiseFocus(browser.ownerGlobal); + + await openPasswordContextMenu( + browser, + passwordInputSelector, + () => false + ); + let generatedPasswordItem = document.getElementById( + "fill-login-generated-password" + ); + Assert.ok( + BrowserTestUtils.is_visible(generatedPasswordItem), + "generated password item is visible" + ); + Assert.ok( + generatedPasswordItem.disabled, + "generated password item is disabled" + ); + + CONTEXT_MENU.hidePopup(); + } + ); + + LoginTestUtils.primaryPassword.disable(); +}); + +add_task(async function fill_generated_password_empty_field() { + // test that we can fill with generated password into an empty password field + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: TEST_ORIGIN + FORM_PAGE_PATH, + }, + async function (browser) { + await SimpleTest.promiseFocus(browser.ownerGlobal); + await SpecialPowers.spawn( + browser, + [[passwordInputSelector]], + function checkInitialFieldValue(inputSelector) { + const input = content.document.querySelector(inputSelector); + Assert.equal(input.value.length, 0, "Password field is empty"); + Assert.ok( + !input.matches(":autofill"), + "Password field should not be highlighted" + ); + } + ); + + await doFillGeneratedPasswordContextMenuItem( + browser, + passwordInputSelector + ); + await SpecialPowers.spawn( + browser, + [[passwordInputSelector]], + function checkFinalFieldValue(inputSelector) { + let { LoginTestUtils: LTU } = ChromeUtils.importESModule( + "resource://testing-common/LoginTestUtils.sys.mjs" + ); + const input = content.document.querySelector(inputSelector); + Assert.equal( + input.value.length, + LTU.generation.LENGTH, + "Password field was filled with generated password" + ); + Assert.ok( + input.matches(":autofill"), + "Password field should be highlighted" + ); + LTU.loginField.checkPasswordMasked(input, false, "after fill"); + + info("cleaing the field"); + input.setUserInput(""); + } + ); + + let acPopup = document.getElementById("PopupAutoComplete"); + await openACPopup(acPopup, browser, passwordInputSelector); + + let pwgenItem = acPopup.querySelector( + `[originaltype="generatedPassword"]` + ); + Assert.ok( + !pwgenItem || EventUtils.isHidden(pwgenItem), + "pwgen item should no longer be shown" + ); + + await closePopup(acPopup); + } + ); +}); + +add_task(async function fill_generated_password_nonempty_field() { + // test that we can fill with generated password into an non-empty password field + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: TEST_ORIGIN + FORM_PAGE_PATH, + }, + async function (browser) { + await SimpleTest.promiseFocus(browser.ownerGlobal); + await changeContentFormValues(browser, { + [passwordInputSelector]: "aa", + }); + await SpecialPowers.spawn( + browser, + [[passwordInputSelector]], + function checkInitialFieldValue(inputSelector) { + const input = content.document.querySelector(inputSelector); + Assert.ok( + !input.matches(":autofill"), + "Password field should not be highlighted" + ); + } + ); + + await doFillGeneratedPasswordContextMenuItem( + browser, + passwordInputSelector + ); + await SpecialPowers.spawn( + browser, + [[passwordInputSelector]], + function checkFinalFieldValue(inputSelector) { + let { LoginTestUtils: LTU } = ChromeUtils.importESModule( + "resource://testing-common/LoginTestUtils.sys.mjs" + ); + const input = content.document.querySelector(inputSelector); + Assert.equal( + input.value.length, + LTU.generation.LENGTH, + "Password field was filled with generated password" + ); + Assert.ok( + input.matches(":autofill"), + "Password field should be highlighted" + ); + LTU.loginField.checkPasswordMasked(input, false, "after fill"); + } + ); + } + ); + LoginTestUtils.clearData(); + LoginTestUtils.resetGeneratedPasswordsCache(); +}); + +add_task(async function fill_generated_password_with_matching_logins() { + // test that we can fill a generated password when there are matching logins + let login = LoginTestUtils.testData.formLogin({ + origin: "https://example.com", + formActionOrigin: "https://example.com", + username: "username", + password: "pass1", + }); + let storageChangedPromised = TestUtils.topicObserved( + "passwordmgr-storage-changed", + (_, data) => data == "addLogin" + ); + await Services.logins.addLoginAsync(login); + await storageChangedPromised; + + let formFilled = listenForTestNotification("FormProcessed"); + + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: TEST_ORIGIN + FORM_PAGE_PATH, + }, + async function (browser) { + await SimpleTest.promiseFocus(browser.ownerGlobal); + await formFilled; + await SpecialPowers.spawn( + browser, + [[passwordInputSelector]], + function checkInitialFieldValue(inputSelector) { + Assert.equal( + content.document.querySelector(inputSelector).value, + "pass1", + "Password field has initial value" + ); + } + ); + + await doFillGeneratedPasswordContextMenuItem( + browser, + passwordInputSelector + ); + await SpecialPowers.spawn( + browser, + [[passwordInputSelector]], + function checkFinalFieldValue(inputSelector) { + let { LoginTestUtils: LTU } = ChromeUtils.importESModule( + "resource://testing-common/LoginTestUtils.sys.mjs" + ); + const input = content.document.querySelector(inputSelector); + Assert.equal( + input.value.length, + LTU.generation.LENGTH, + "Password field was filled with generated password" + ); + Assert.ok( + input.matches(":autofill"), + "Password field should be highlighted" + ); + LTU.loginField.checkPasswordMasked(input, false, "after fill"); + } + ); + + await openPasswordContextMenu(browser, passwordInputSelector); + + // Execute the command of the first login menuitem found at the context menu. + let passwordChangedPromise = ContentTask.spawn( + browser, + null, + async function () { + let passwordInput = content.document.getElementById( + "form-basic-password" + ); + await ContentTaskUtils.waitForEvent(passwordInput, "input"); + } + ); + + let popupMenu = document.getElementById("fill-login-popup"); + let firstLoginItem = + popupMenu.getElementsByClassName("context-login-item")[0]; + firstLoginItem.doCommand(); + + await passwordChangedPromise; + + let contextMenu = document.getElementById("contentAreaContextMenu"); + contextMenu.hidePopup(); + + // Blur the field to trigger a 'change' event. + await BrowserTestUtils.synthesizeKey("KEY_Tab", undefined, browser); + await BrowserTestUtils.synthesizeKey( + "KEY_Tab", + { shiftKey: true }, + browser + ); + + await SpecialPowers.spawn( + browser, + [[passwordInputSelector]], + function checkFieldNotGeneratedPassword(inputSelector) { + let { LoginTestUtils: LTU } = ChromeUtils.importESModule( + "resource://testing-common/LoginTestUtils.sys.mjs" + ); + const input = content.document.querySelector(inputSelector); + Assert.equal( + input.value, + "pass1", + "Password field was filled with the saved password" + ); + LTU.loginField.checkPasswordMasked( + input, + true, + "after fill of a saved login" + ); + } + ); + } + ); + + let logins = Services.logins.getAllLogins(); + Assert.equal(logins.length, 2, "Check 2 logins"); + isnot( + logins[0].password, + logins[1].password, + "Generated password shouldn't have changed to match the filled password" + ); + + Services.logins.removeAllUserFacingLogins(); + LoginTestUtils.resetGeneratedPasswordsCache(); +}); + +add_task(async function test_edited_generated_password_in_new_tab() { + // test that we can fill the generated password into an empty password field, + // edit it, and then fill the edited password. + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: TEST_ORIGIN + FORM_PAGE_PATH, + }, + async function (browser) { + await SimpleTest.promiseFocus(browser.ownerGlobal); + await SpecialPowers.spawn( + browser, + [[passwordInputSelector]], + function checkInitialFieldValue(inputSelector) { + const input = content.document.querySelector(inputSelector); + Assert.equal(input.value.length, 0, "Password field is empty"); + Assert.ok( + !input.matches(":autofill"), + "Password field should not be highlighted" + ); + } + ); + + await doFillGeneratedPasswordContextMenuItem( + browser, + passwordInputSelector + ); + await SpecialPowers.spawn( + browser, + [[passwordInputSelector]], + function checkAndEditFieldValue(inputSelector) { + let { LoginTestUtils: LTU } = ChromeUtils.importESModule( + "resource://testing-common/LoginTestUtils.sys.mjs" + ); + const input = content.document.querySelector(inputSelector); + Assert.equal( + input.value.length, + LTU.generation.LENGTH, + "Password field was filled with generated password" + ); + Assert.ok( + input.matches(":autofill"), + "Password field should be highlighted" + ); + LTU.loginField.checkPasswordMasked(input, false, "after fill"); + } + ); + + await BrowserTestUtils.sendChar("!", browser); + await BrowserTestUtils.sendChar("@", browser); + let storageChangedPromised = TestUtils.topicObserved( + "passwordmgr-storage-changed", + (_, data) => data == "modifyLogin" + ); + await BrowserTestUtils.synthesizeKey("KEY_Tab", undefined, browser); + info("Waiting for storage update"); + await storageChangedPromised; + } + ); + + info("Now fill again in a new tab and ensure the edited password is used"); + + // Disable autofill in the new tab + await SpecialPowers.pushPrefEnv({ + set: [["signon.autofillForms", false]], + }); + + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: TEST_ORIGIN + FORM_PAGE_PATH, + }, + async function (browser) { + await SimpleTest.promiseFocus(browser.ownerGlobal); + + await doFillGeneratedPasswordContextMenuItem( + browser, + passwordInputSelector + ); + + await SpecialPowers.spawn( + browser, + [[passwordInputSelector]], + function checkAndEditFieldValue(inputSelector) { + let { LoginTestUtils: LTU } = ChromeUtils.importESModule( + "resource://testing-common/LoginTestUtils.sys.mjs" + ); + const input = content.document.querySelector(inputSelector); + Assert.equal( + input.value.length, + LTU.generation.LENGTH + 2, + "Password field was filled with edited generated password" + ); + LTU.loginField.checkPasswordMasked(input, false, "after fill"); + } + ); + } + ); + + LoginTestUtils.clearData(); + LoginTestUtils.resetGeneratedPasswordsCache(); + await SpecialPowers.popPrefEnv(); +}); -- cgit v1.2.3