From a2b1fed31985170f29708c8fd7893ee07eb4d3a6 Mon Sep 17 00:00:00 2001 From: jneuberger Date: Wed, 13 Dec 2023 23:10:05 +0000 Subject: [PATCH] Bug 1869024 - P1. Convert strings in LoginManagerPrompter from passwordmgr.properties to Fluent r=credential-management-reviewers,fluent-reviewers,dimi,bolsson Part 1 of migrating passwordmgr.properties to fluent: Converting the strings from LoginManagerPrompter 1. The following strings [saveLoginMsg2, saveLoginMsgNoUser2, saveLoginButtonDeny.label, saveLoginButtonDeny.accesskey, updateLoginMsg3, updateLoginMsgNoUser3, updateLoginButtonDelete.label, updateLoginButtonDelete.accesskey] are not migrated as the messages are changed. They get replaced by [save-password-message, save-password-button-deny, update-password-messsage, update-password-button-delete] 2. The remaining string from LoginManagerPrompter are migrated by the python script. Differential Revision: https://phabricator.services.mozilla.com/D195931 --- .../bug_1869024_passwordmgr.py | 122 +++++++++++++ .../passwordmgr/LoginManagerPrompter.sys.mjs | 168 +++++++++--------- .../browser/browser_doorhanger_remembering.js | 8 +- .../chrome/passwordmgr/passwordmgr.properties | 29 +-- .../en-US/toolkit/passwordmgr/passwordmgr.ftl | 40 +++++ 5 files changed, 249 insertions(+), 118 deletions(-) create mode 100644 python/l10n/fluent_migrations/bug_1869024_passwordmgr.py create mode 100644 toolkit/locales/en-US/toolkit/passwordmgr/passwordmgr.ftl diff --git a/python/l10n/fluent_migrations/bug_1869024_passwordmgr.py b/python/l10n/fluent_migrations/bug_1869024_passwordmgr.py new file mode 100644 index 000000000000..2cfe7151dc40 --- /dev/null +++ b/python/l10n/fluent_migrations/bug_1869024_passwordmgr.py @@ -0,0 +1,122 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +import fluent.syntax.ast as FTL +from fluent.migrate.transforms import COPY, REPLACE +from fluent.migrate.helpers import VARIABLE_REFERENCE + + +def migrate(ctx): + """Bug 1869024 - Convert passwordmgr.properties to Fluent, part {index}.""" + + source = "toolkit/chrome/passwordmgr/passwordmgr.properties" + target = "toolkit/toolkit/passwordmgr/passwordmgr.ftl" + ctx.add_transforms( + target, + target, + [ + FTL.Message( + id=FTL.Identifier("password-manager-save-password-button-allow"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=COPY(source, "saveLoginButtonAllow.label"), + ), + FTL.Attribute( + id=FTL.Identifier("accesskey"), + value=COPY(source, "saveLoginButtonAllow.accesskey"), + ), + ], + ), + FTL.Message( + id=FTL.Identifier("password-manager-save-password-button-never"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=COPY(source, "saveLoginButtonNever.label"), + ), + FTL.Attribute( + id=FTL.Identifier("accesskey"), + value=COPY(source, "saveLoginButtonNever.accesskey"), + ), + ], + ), + FTL.Message( + id=FTL.Identifier("password-manager-update-login-add-username"), + value=COPY(source, "updateLoginMsgAddUsername2"), + ), + FTL.Message( + id=FTL.Identifier("password-manager-password-password-button-allow"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=COPY(source, "updateLoginButtonText"), + ), + FTL.Attribute( + id=FTL.Identifier("accesskey"), + value=COPY(source, "updateLoginButtonAccessKey"), + ), + ], + ), + FTL.Message( + id=FTL.Identifier("password-manager-update-password-button-deny"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=COPY(source, "updateLoginButtonDeny.label"), + ), + FTL.Attribute( + id=FTL.Identifier("accesskey"), + value=COPY(source, "updateLoginButtonDeny.accesskey"), + ), + ], + ), + FTL.Message( + id=FTL.Identifier("password-manager-no-username-placeholder"), + value=COPY(source, "noUsernamePlaceholder"), + ), + FTL.Message( + id=FTL.Identifier("password-manager-toggle-password"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=COPY(source, "togglePasswordLabel"), + ), + FTL.Attribute( + id=FTL.Identifier("accesskey"), + value=COPY(source, "togglePasswordAccessKey2"), + ), + ], + ), + FTL.Message( + id=FTL.Identifier("password-manager-confirm-password-change"), + value=COPY(source, "passwordChangeTitle"), + ), + FTL.Message( + id=FTL.Identifier("password-manager-select-username"), + value=COPY(source, "userSelectText2"), + ), + FTL.Message( + id=FTL.Identifier("password-manager-save-password-message"), + value=REPLACE( + source, + "saveLoginMsgNoUser2", + { + "%1$S": VARIABLE_REFERENCE("host"), + }, + normalize_printf=True, + ), + ), + FTL.Message( + id=FTL.Identifier("password-manager-update-password-message"), + value=REPLACE( + source, + "updateLoginMsgNoUser3", + { + "%1$S": VARIABLE_REFERENCE("host"), + }, + normalize_printf=True, + ), + ), + ], + ) diff --git a/toolkit/components/passwordmgr/LoginManagerPrompter.sys.mjs b/toolkit/components/passwordmgr/LoginManagerPrompter.sys.mjs index e50d265cbd61..c962bf97e223 100644 --- a/toolkit/components/passwordmgr/LoginManagerPrompter.sys.mjs +++ b/toolkit/components/passwordmgr/LoginManagerPrompter.sys.mjs @@ -8,8 +8,6 @@ import { showConfirmation } from "resource://gre/modules/FillHelpers.sys.mjs"; const lazy = {}; -/* eslint-disable block-scoped-var, no-var */ - ChromeUtils.defineESModuleGetters(lazy, { LoginHelper: "resource://gre/modules/LoginHelper.sys.mjs", }); @@ -21,10 +19,8 @@ XPCOMUtils.defineLazyServiceGetter( "nsIAutoCompleteSimpleSearch" ); -ChromeUtils.defineLazyGetter(lazy, "strBundle", () => { - return Services.strings.createBundle( - "chrome://passwordmgr/locale/passwordmgr.properties" - ); +ChromeUtils.defineLazyGetter(lazy, "l10n", () => { + return new Localization(["toolkit/passwordmgr/passwordmgr.ftl"], true); }); const LoginInfo = Components.Constructor( @@ -201,35 +197,24 @@ export class LoginManagerPrompter { `Got autoSavedLoginGuid: ${autoSavedLoginGuid} and autoFilledLoginGuid ${autoFilledLoginGuid}.` ); - let saveMsgNames = { - prompt: login.username === "" ? "saveLoginMsgNoUser2" : "saveLoginMsg2", - buttonLabel: "saveLoginButtonAllow.label", - buttonAccessKey: "saveLoginButtonAllow.accesskey", - secondaryButtonLabel: "saveLoginButtonDeny.label", - secondaryButtonAccessKey: "saveLoginButtonDeny.accesskey", + let saveMessageIds = { + prompt: "password-manager-save-password-message", + mainButton: "password-manager-save-password-button-allow", + secondaryButton: "password-manager-save-password-button-deny", }; - let changeMsgNames = { - prompt: - login.username === "" ? "updateLoginMsgNoUser3" : "updateLoginMsg3", - buttonLabel: "updateLoginButtonText", - buttonAccessKey: "updateLoginButtonAccessKey", - secondaryButtonLabel: "updateLoginButtonDeny.label", - secondaryButtonAccessKey: "updateLoginButtonDeny.accesskey", + let changeMessageIds = { + prompt: messageStringID ?? "password-manager-update-password-message", + mainButton: "password-manager-password-password-button-allow", + secondaryButton: "password-manager-update-password-button-deny", }; - let initialMsgNames = - type == "password-save" ? saveMsgNames : changeMsgNames; - - if (messageStringID) { - changeMsgNames.prompt = messageStringID; - } + let initialMessageIds = + type == "password-save" ? saveMessageIds : changeMessageIds; + let promptId = initialMessageIds.prompt; let host = this._getShortDisplayHost(login.origin); - let promptMsg = - type == "password-save" - ? this._getLocalizedString(saveMsgNames.prompt, [host]) - : this._getLocalizedString(changeMsgNames.prompt, [host]); + let promptMessage = lazy.l10n.formatValueSync(promptId, { host }); let histogramName = type == "password-save" @@ -280,23 +265,23 @@ export class LoginManagerPrompter { foundLogins, autoSavedLoginGuid ); - let msgNames = !logins.length ? saveMsgNames : changeMsgNames; + let messageIds = !logins.length ? saveMessageIds : changeMessageIds; // Update the label based on whether this will be a new login or not. - let label = this._getLocalizedString(msgNames.buttonLabel); - let accessKey = this._getLocalizedString(msgNames.buttonAccessKey); + + let mainButton = this.getLabelAndAccessKey(messageIds.mainButton); // Update the labels for the next time the panel is opened. - currentNotification.mainAction.label = label; - currentNotification.mainAction.accessKey = accessKey; + currentNotification.mainAction.label = mainButton.label; + currentNotification.mainAction.accessKey = mainButton.accessKey; // Update the labels in real time if the notification is displayed. let element = [...currentNotification.owner.panel.childNodes].find( n => n.notification == currentNotification ); if (element) { - element.setAttribute("buttonlabel", label); - element.setAttribute("buttonaccesskey", accessKey); + element.setAttribute("buttonlabel", mainButton.label); + element.setAttribute("buttonaccesskey", mainButton.accessKey); updateButtonStatus(element); } }; @@ -485,10 +470,12 @@ export class LoginManagerPrompter { PWMGR_PROMPT_UPDATE_ACTION: true, }; + let mainButton = this.getLabelAndAccessKey(initialMessageIds.mainButton); + // The main action is the "Save" or "Update" button. let mainAction = { - label: this._getLocalizedString(initialMsgNames.buttonLabel), - accessKey: this._getLocalizedString(initialMsgNames.buttonAccessKey), + label: mainButton.label, + accessKey: mainButton.accessKey, callback: async () => { const eventTypeMapping = { "password-save": { @@ -555,12 +542,14 @@ export class LoginManagerPrompter { }, }; + let secondaryButton = this.getLabelAndAccessKey( + initialMessageIds.secondaryButton + ); + let secondaryActions = [ { - label: this._getLocalizedString(initialMsgNames.secondaryButtonLabel), - accessKey: this._getLocalizedString( - initialMsgNames.secondaryButtonAccessKey - ), + label: secondaryButton.label, + accessKey: secondaryButton.accessKey, callback: () => { histogram.add(PROMPT_NOTNOW_OR_DONTUPDATE); Services.obs.notifyObservers( @@ -574,9 +563,12 @@ export class LoginManagerPrompter { ]; // Include a "Never for this site" button when saving a new password. if (type == "password-save") { + let neverSaveButton = this.getLabelAndAccessKey( + "password-manager-save-password-button-never" + ); secondaryActions.push({ - label: this._getLocalizedString("saveLoginButtonNever.label"), - accessKey: this._getLocalizedString("saveLoginButtonNever.accesskey"), + label: neverSaveButton.label, + accessKey: neverSaveButton.accessKey, callback: () => { histogram.add(PROMPT_NEVER); Services.obs.notifyObservers( @@ -590,13 +582,15 @@ export class LoginManagerPrompter { }); } + let updatePasswordButtonDelete = this.getLabelAndAccessKey( + "password-manager-update-password-button-delete" + ); + // Include a "Delete this login" button when updating an existing password if (type == "password-change") { secondaryActions.push({ - label: this._getLocalizedString("updateLoginButtonDelete.label"), - accessKey: this._getLocalizedString( - "updateLoginButtonDelete.accesskey" - ), + label: updatePasswordButtonDelete.label, + accessKey: updatePasswordButtonDelete.accessKey, callback: async () => { histogram.add(PROMPT_DELETE); Services.obs.notifyObservers( @@ -623,10 +617,11 @@ export class LoginManagerPrompter { }); } - let usernamePlaceholder = this._getLocalizedString("noUsernamePlaceholder"); - let togglePasswordLabel = this._getLocalizedString("togglePasswordLabel"); - let togglePasswordAccessKey = this._getLocalizedString( - "togglePasswordAccessKey2" + let usernamePlaceholder = lazy.l10n.formatValueSync( + "password-manager-no-username-placeholder" + ); + let togglePassword = this.getLabelAndAccessKey( + "password-manager-toggle-password" ); // .wrappedJSObject needed here -- see bug 422974 comment 5. @@ -715,8 +710,9 @@ export class LoginManagerPrompter { ) ) { toggleBtn.addEventListener("command", onVisibilityToggle); - toggleBtn.setAttribute("label", togglePasswordLabel); - toggleBtn.setAttribute("accesskey", togglePasswordAccessKey); + + toggleBtn.setAttribute("label", togglePassword.label); + toggleBtn.setAttribute("accesskey", togglePassword.accessKey); let hideToggle = lazy.LoginHelper.isPrimaryPasswordSet() || @@ -728,7 +724,8 @@ export class LoginManagerPrompter { // one that is already saved and we don't want to reveal // it as the submitter of this form may not be the account // owner, they may just be using the saved password. - (messageStringID == "updateLoginMsgAddUsername2" && + (messageStringID == + "password-manager-update-login-add-username" && login.timePasswordChanged < Date.now() - VISIBILITY_TOGGLE_MAX_PW_AGE_MS); toggleBtn.hidden = hideToggle; @@ -790,7 +787,7 @@ export class LoginManagerPrompter { let notification = PopupNotifications.show( browser, notificationID, - promptMsg, + promptMessage, "password-notification-icon", mainAction, secondaryActions, @@ -857,7 +854,7 @@ export class LoginManagerPrompter { // If the saved password matches the password we're prompting with then we // are only prompting to let the user add a username since there was one in // the form. Change the message so the purpose of the prompt is clearer. - messageStringID = "updateLoginMsgAddUsername2"; + messageStringID = "password-manager-update-login-add-username"; } let notification = LoginManagerPrompter._showLoginCaptureDoorhanger( @@ -906,19 +903,21 @@ export class LoginManagerPrompter { `Prompting user to change passowrd for username with count: ${logins.length}.` ); - var usernames = logins.map( - l => l.username || LoginManagerPrompter._getLocalizedString("noUsername") + const noUsernamePlaceholder = lazy.l10n.formatValueSync( + "password-manager-no-username-placeholder" ); - var dialogText = - LoginManagerPrompter._getLocalizedString("userSelectText2"); - var dialogTitle = LoginManagerPrompter._getLocalizedString( - "passwordChangeTitle" + const usernames = logins.map(l => l.username || noUsernamePlaceholder); + const dialogText = lazy.l10n.formatValueSync( + "password-manager-select-username" ); - var selectedIndex = { value: null }; + const dialogTitle = lazy.l10n.formatValueSync( + "password-manager-confirm-password-change" + ); + const selectedIndex = { value: null }; // If user selects ok, outparam.value is set to the index // of the selected username. - var ok = Services.prompt.select( + const ok = Services.prompt.select( browser.ownerGlobal, dialogTitle, dialogText, @@ -927,9 +926,9 @@ export class LoginManagerPrompter { ); if (ok) { // Now that we know which login to use, modify its password. - var selectedLogin = logins[selectedIndex.value]; + const selectedLogin = logins[selectedIndex.value]; lazy.log.debug(`Updating password for origin: ${aNewLogin.origin}.`); - var newLoginWithUsername = Cc[ + const newLoginWithUsername = Cc[ "@mozilla.org/login-manager/loginInfo;1" ].createInstance(Ci.nsILoginInfo); newLoginWithUsername.init( @@ -951,8 +950,8 @@ export class LoginManagerPrompter { * Helper method to update and persist an existing nsILoginInfo object with new property values. */ static _updateLogin(login, aNewLogin) { - var now = Date.now(); - var propBag = Cc["@mozilla.org/hash-property-bag;1"].createInstance( + const now = Date.now(); + const propBag = Cc["@mozilla.org/hash-property-bag;1"].createInstance( Ci.nsIWritablePropertyBag ); propBag.setProperty("formActionOrigin", aNewLogin.formActionOrigin); @@ -973,21 +972,18 @@ export class LoginManagerPrompter { } /** - * Can be called as: - * _getLocalizedString("key1"); - * _getLocalizedString("key2", ["arg1"]); - * _getLocalizedString("key3", ["arg1", "arg2"]); - * (etc) - * - * Returns the localized string for the specified key, - * formatted if required. + * Retrieves the message of the given id from fluent + * and extracts the label and accesskey * + * @param {String} id message id + * @returns label and accesskey */ - static _getLocalizedString(key, formatArgs) { - if (formatArgs) { - return lazy.strBundle.formatStringFromName(key, formatArgs); - } - return lazy.strBundle.GetStringFromName(key); + static getLabelAndAccessKey(id) { + const msg = lazy.l10n.formatMessagesSync([id])[0]; + return { + label: msg.attributes.find(x => x.name == "label").value, + accessKey: msg.attributes.find(x => x.name == "accesskey").value, + }; } /** @@ -996,14 +992,14 @@ export class LoginManagerPrompter { * "ftp://www.site.co.uk" --> "site.co.uk". */ static _getShortDisplayHost(aURIString) { - var displayHost; + let displayHost; - var idnService = Cc["@mozilla.org/network/idn-service;1"].getService( + const idnService = Cc["@mozilla.org/network/idn-service;1"].getService( Ci.nsIIDNService ); try { - var uri = Services.io.newURI(aURIString); - var baseDomain = Services.eTLD.getBaseDomain(uri); + const uri = Services.io.newURI(aURIString); + const baseDomain = Services.eTLD.getBaseDomain(uri); displayHost = idnService.convertToDisplayIDN(baseDomain, {}); } catch (e) { lazy.log.warn(`Couldn't process supplied URIString: ${aURIString}`); diff --git a/toolkit/components/passwordmgr/test/browser/browser_doorhanger_remembering.js b/toolkit/components/passwordmgr/test/browser/browser_doorhanger_remembering.js index 26b9fd1258e7..c2e18daabb0a 100644 --- a/toolkit/components/passwordmgr/test/browser/browser_doorhanger_remembering.js +++ b/toolkit/components/passwordmgr/test/browser/browser_doorhanger_remembering.js @@ -634,7 +634,7 @@ add_task(async function test_changeUPLoginOnUPForm_dont() { Assert.ok(!notif.dismissed, "doorhanger is not dismissed"); Assert.equal( notif.message, - "Update login for example.com?", + "Update password for example.com?", "Check message" ); @@ -675,7 +675,7 @@ add_task(async function test_changeUPLoginOnUPForm_remove() { Assert.ok(!notif.dismissed, "doorhanger is not dismissed"); Assert.equal( notif.message, - "Update login for example.com?", + "Update password for example.com?", "Check message" ); @@ -716,7 +716,7 @@ add_task(async function test_changeUPLoginOnUPForm_change() { Assert.ok(!notif.dismissed, "doorhanger is not dismissed"); Assert.equal( notif.message, - "Update login for example.com?", + "Update password for example.com?", "Check message" ); @@ -854,7 +854,7 @@ add_task(async function test_checkUPSaveText() { Assert.ok(notif, "got notification popup"); // Check the text, which comes from the localized saveLoginMsg string. let notificationText = notif.message; - let expectedText = "Save login for example.com?"; + let expectedText = "Save password for example.com?"; Assert.equal( notificationText, expectedText, diff --git a/toolkit/locales/en-US/chrome/passwordmgr/passwordmgr.properties b/toolkit/locales/en-US/chrome/passwordmgr/passwordmgr.properties index 0366f11b34c3..63e851f5df44 100644 --- a/toolkit/locales/en-US/chrome/passwordmgr/passwordmgr.properties +++ b/toolkit/locales/en-US/chrome/passwordmgr/passwordmgr.properties @@ -4,27 +4,7 @@ rememberPassword = Use Password Manager to remember this password. savePasswordTitle = Confirm -# LOCALIZATION NOTE (saveLoginMsg2, saveLoginMsgNoUser2): -# %S is the login's hostname. -saveLoginMsg2 = Save login for %S? -saveLoginMsgNoUser2 = Save password for %S? -saveLoginButtonAllow.label = Save -saveLoginButtonAllow.accesskey = S -saveLoginButtonDeny.label = Don’t save -saveLoginButtonDeny.accesskey = D -saveLoginButtonNever.label = Never save -saveLoginButtonNever.accesskey = e -# LOCALIZATION NOTE (updateLoginMsg3, updateLoginMsgNoUser3): -# %S is the login's hostname. -updateLoginMsg3 = Update login for %S? -updateLoginMsgNoUser3 = Update password for %S? -updateLoginMsgAddUsername2 = Add username to saved password? -updateLoginButtonText = Update -updateLoginButtonAccessKey = U -updateLoginButtonDeny.label = Don’t update -updateLoginButtonDeny.accesskey = D -updateLoginButtonDelete.label = Remove saved login -updateLoginButtonDelete.accesskey = R + # LOCALIZATION NOTE (rememberPasswordMsg): # 1st string is the username for the login, 2nd is the login's hostname. # Note that long usernames may be truncated. @@ -32,20 +12,13 @@ rememberPasswordMsg = Would you like to remember the password for “%1$S” on # 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 -togglePasswordLabel=Show password -togglePasswordAccessKey2=h notNowButtonText = &Not Now neverForSiteButtonText = Ne&ver for This Site rememberButtonText = &Remember -passwordChangeTitle = Confirm Password Change # LOCALIZATION NOTE (updatePasswordMsg): # String is the username for the login. updatePasswordMsg = Would you like to update the saved password for “%S”? updatePasswordMsgNoUser = Would you like to update the saved password? -userSelectText2 = Select which login to update: loginsDescriptionAll2=Logins for the following sites are stored on your computer # LOCALIZATION NOTE (useASecurelyGeneratedPassword): diff --git a/toolkit/locales/en-US/toolkit/passwordmgr/passwordmgr.ftl b/toolkit/locales/en-US/toolkit/passwordmgr/passwordmgr.ftl new file mode 100644 index 000000000000..767730658596 --- /dev/null +++ b/toolkit/locales/en-US/toolkit/passwordmgr/passwordmgr.ftl @@ -0,0 +1,40 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# 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/. + +## Save and update password doorhanger + +# Variables +# $host (String) - Hostname for which the password is saved for. +password-manager-save-password-message = Save password for { $host }? +password-manager-save-password-button-deny = + .label = Not now + .accesskey = N +password-manager-save-password-button-allow = + .label = Save + .accesskey = S +password-manager-save-password-button-never = + .label = Never save + .accesskey = e + +# Variables +# $host (String) - Hostname for which the password is updated for. +password-manager-update-password-message = Update password for { $host }? +password-manager-update-password-button-delete = + .label = Remove saved password + .accesskey = R +password-manager-update-login-add-username = Add username to saved password? +password-manager-password-password-button-allow = + .label = Update + .accesskey = U +password-manager-update-password-button-deny = + .label = Don’t update + .accesskey = D + +# This is displayed in place of the username when it is missing. +password-manager-no-username-placeholder = No username +password-manager-toggle-password = + .label = Show password + .accesskey = h +password-manager-confirm-password-change = Confirm Password Change +password-manager-select-username = Select which login to update: