tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 054316229cb666e286a8c9746623e2e44509b084
parent 6635506128b3227346d012389dd994aae44e94a8
Author: hannajones <hjones@mozilla.com>
Date:   Tue, 16 Dec 2025 18:36:53 +0000

Bug 1971835 - add config-based account settings r=akulyk,fluent-reviewers,desktop-theme-reviewers,bolsson

Differential Revision: https://phabricator.services.mozilla.com/D275086

Diffstat:
Mbrowser/components/preferences/main.js | 105+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mbrowser/components/preferences/sync.inc.xhtml | 3+++
Mbrowser/components/preferences/sync.js | 242+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
Mbrowser/locales/en-US/browser/preferences/preferences.ftl | 36++++++++++++++++++++++++++++++++++++
Mbrowser/themes/shared/preferences/preferences.css | 10++++++++++
Apython/l10n/fluent_migrations/bug_1971835_sync_preferences_update.py | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Mtoolkit/content/widgets/moz-box-common.css | 3+++
7 files changed, 419 insertions(+), 31 deletions(-)

diff --git a/browser/components/preferences/main.js b/browser/components/preferences/main.js @@ -3511,6 +3511,111 @@ SettingGroupManager.registerGroups({ }, ], }, + account: { + inProgress: true, + l10nId: "account-group-label", + headingLevel: 2, + items: [ + { + id: "noFxaAccountGroup", + control: "moz-box-group", + items: [ + { + id: "noFxaAccount", + control: "placeholder-message", + l10nId: "account-placeholder", + controlAttrs: { + imagesrc: "chrome://global/skin/illustrations/security-error.svg", + }, + }, + { + id: "noFxaSignIn", + control: "moz-box-link", + l10nId: "sync-signedout-account-short", + }, + ], + }, + { + id: "fxaSignedInGroup", + control: "moz-box-group", + items: [ + { + id: "fxaLoginVerified", + control: "moz-box-item", + l10nId: "sync-account-signed-in", + l10nArgs: { email: "" }, + iconSrc: "chrome://browser/skin/fxa/avatar-color.svg", + controlAttrs: { + layout: "large-icon", + }, + }, + { + id: "verifiedManage", + control: "moz-box-link", + l10nId: "sync-manage-account2", + controlAttrs: { + href: "https://accounts.firefox.com/settings", + }, + }, + { + id: "fxaUnlinkButton", + control: "moz-box-button", + l10nId: "sync-sign-out2", + }, + ], + }, + { + id: "fxaUnverifiedGroup", + control: "moz-box-group", + items: [ + { + id: "fxaLoginUnverified", + control: "placeholder-message", + l10nId: "sync-signedin-unverified2", + l10nArgs: { email: "" }, + controlAttrs: { + imagesrc: "chrome://global/skin/illustrations/security-error.svg", + }, + }, + { + id: "verifyFxaAccount", + control: "moz-box-link", + l10nId: "sync-verify-account", + }, + { + id: "unverifiedUnlinkFxaAccount", + control: "moz-box-button", + l10nId: "sync-remove-account", + }, + ], + }, + { + id: "fxaLoginRejectedGroup", + control: "moz-box-group", + items: [ + { + id: "fxaLoginRejected", + control: "placeholder-message", + l10nId: "sync-signedin-login-failure2", + l10nArgs: { email: "" }, + controlAttrs: { + imagesrc: "chrome://global/skin/illustrations/security-error.svg", + }, + }, + { + id: "rejectReSignIn", + control: "moz-box-link", + l10nId: "sync-sign-in", + }, + { + id: "rejectUnlinkFxaAccount", + control: "moz-box-button", + l10nId: "sync-remove-account", + }, + ], + }, + ], + }, }); /** diff --git a/browser/components/preferences/sync.inc.xhtml b/browser/components/preferences/sync.inc.xhtml @@ -246,6 +246,9 @@ </vbox> </deck> +<!-- Mozilla account --> +<html:setting-group groupid="account" hidden="true" data-category="paneSync"/> + <!-- Sync --> <html:setting-group groupid="sync" hidden="true" data-category="paneSync"/> diff --git a/browser/components/preferences/sync.js b/browser/components/preferences/sync.js @@ -225,9 +225,37 @@ var SyncHelpers = new (class SyncHelpers { ); this.replaceTabWithUrl(url); } + + /** + * Attempts to take the user through the sign in flow by opening the web content + * with the given entrypoint as a query parameter + * + * @param {string} entrypoint + * An string appended to the query parameters, used in telemetry to differentiate + * different entrypoints to accounts + */ + async reSignIn(entrypoint) { + const url = await FxAccounts.config.promiseConnectAccountURI(entrypoint); + this.replaceTabWithUrl(url); + } + + async verifyFirefoxAccount() { + return this.reSignIn("preferences-reverify"); + } + + /** + * Disconnect the account, including everything linked. + * + * @param {boolean} confirm + * Whether to show a confirmation dialog before disconnecting + */ + unlinkFirefoxAccount(confirm) { + window.browsingContext.topChromeWindow.gSync.disconnect({ + confirm, + }); + } })(); -// Sync section Preferences.addSetting({ id: "uiStateUpdate", setup(emitChange) { @@ -236,7 +264,171 @@ Preferences.addSetting({ }, }); -// Sync section - no Firefox account +// Mozilla accounts section + +// Logged out of Mozilla account +Preferences.addSetting({ + id: "noFxaAccountGroup", + deps: ["uiStateUpdate"], + visible() { + return SyncHelpers.uiStateStatus == UIState.STATUS_NOT_CONFIGURED; + }, +}); +Preferences.addSetting({ + id: "noFxaAccount", +}); +Preferences.addSetting({ + id: "noFxaSignIn", + onUserClick: () => { + SyncHelpers.signIn(); + }, +}); + +// Logged in and verified and all is good +Preferences.addSetting({ + id: "fxaSignedInGroup", + deps: ["uiStateUpdate"], + visible() { + return SyncHelpers.uiStateStatus == UIState.STATUS_SIGNED_IN; + }, +}); +Preferences.addSetting({ + id: "fxaLoginVerified", + deps: ["uiStateUpdate"], + _failedAvatarURLs: new Set(), + getControlConfig(config, _, setting) { + let state = SyncHelpers.uiState; + + if (state.displayName) { + config.l10nId = "sync-account-signed-in-display-name"; + config.l10nArgs = { + name: state.displayName, + email: state.email || "", + }; + } else { + config.l10nId = "sync-account-signed-in"; + config.l10nArgs = { + email: state.email || "", + }; + } + + // Reset the image to default avatar if we encounter an error. + if (this._failedAvatarURLs.has(state.avatarURL)) { + config.iconSrc = "chrome://browser/skin/fxa/avatar-color.svg"; + return config; + } + + if (state.avatarURL && !state.avatarIsDefault) { + config.iconSrc = state.avatarURL; + let img = new Image(); + img.onerror = () => { + this._failedAvatarURLs.add(state.avatarURL); + setting.onChange(); + }; + img.src = state.avatarURL; + } + return config; + }, +}); +Preferences.addSetting( + class extends Preferences.AsyncSetting { + static id = "verifiedManage"; + + setup() { + Weave.Svc.Obs.add(UIState.ON_UPDATE, this.emitChange); + return () => Weave.Svc.Obs.remove(UIState.ON_UPDATE, this.emitChange); + } + + // The "manage account" link embeds the uid, so we need to update this + // if the account state changes. + async getControlConfig() { + let href = await FxAccounts.config.promiseManageURI( + SyncHelpers.getEntryPoint() + ); + return { + controlAttrs: { + href: href ?? "https://accounts.firefox.com/settings", + }, + }; + } + } +); + +Preferences.addSetting({ + id: "fxaUnlinkButton", + onUserClick: () => { + SyncHelpers.unlinkFirefoxAccount(true); + }, +}); + +// Logged in to an unverified account +Preferences.addSetting({ + id: "fxaUnverifiedGroup", + deps: ["uiStateUpdate"], + visible() { + return SyncHelpers.uiStateStatus == UIState.STATUS_NOT_VERIFIED; + }, +}); +Preferences.addSetting({ + id: "fxaLoginUnverified", + deps: ["uiStateUpdate"], + getControlConfig(config) { + let state = SyncHelpers.uiState; + config.l10nArgs = { + email: state.email || "", + }; + return config; + }, +}); +Preferences.addSetting({ + id: "verifyFxaAccount", + onUserClick: () => { + SyncHelpers.verifyFirefoxAccount(); + }, +}); +Preferences.addSetting({ + id: "unverifiedUnlinkFxaAccount", + onUserClick: () => { + /* no warning as account can't have previously synced */ + SyncHelpers.unlinkFirefoxAccount(false); + }, +}); + +// Logged in locally but server rejected credentials +Preferences.addSetting({ + id: "fxaLoginRejectedGroup", + deps: ["uiStateUpdate"], + visible() { + return SyncHelpers.uiStateStatus == UIState.STATUS_LOGIN_FAILED; + }, +}); +Preferences.addSetting({ + id: "fxaLoginRejected", + deps: ["uiStateUpdate"], + getControlConfig(config) { + let state = SyncHelpers.uiState; + config.l10nArgs = { + email: state.email || "", + }; + return config; + }, +}); +Preferences.addSetting({ + id: "rejectReSignIn", + onUserClick: () => { + SyncHelpers.reSignIn(SyncHelpers.getEntryPoint()); + }, +}); +Preferences.addSetting({ + id: "rejectUnlinkFxaAccount", + onUserClick: () => { + SyncHelpers.unlinkFirefoxAccount(true); + }, +}); + +//Sync section + +//Sync section - no Firefox account Preferences.addSetting({ id: "syncNoFxaSignIn", deps: ["uiStateUpdate"], @@ -505,6 +697,7 @@ var gSyncPane = { _init() { initSettingGroup("sync"); + initSettingGroup("account"); Weave.Svc.Obs.add(UIState.ON_UPDATE, this.updateWeavePrefs, this); @@ -631,22 +824,20 @@ var gSyncPane = { return false; }); setEventListener("fxaUnlinkButton", "command", function () { - gSyncPane.unlinkFirefoxAccount(true); + SyncHelpers.unlinkFirefoxAccount(true); }); - setEventListener( - "verifyFxaAccount", - "command", - gSyncPane.verifyFirefoxAccount + setEventListener("verifyFxaAccount", "command", () => + SyncHelpers.verifyFirefoxAccount() ); setEventListener("unverifiedUnlinkFxaAccount", "command", function () { /* no warning as account can't have previously synced */ - gSyncPane.unlinkFirefoxAccount(false); + SyncHelpers.unlinkFirefoxAccount(false); }); setEventListener("rejectReSignIn", "command", function () { - gSyncPane.reSignIn(SyncHelpers.getEntryPoint()); + SyncHelpers.reSignIn(SyncHelpers.getEntryPoint()); }); setEventListener("rejectUnlinkFxaAccount", "command", function () { - gSyncPane.unlinkFirefoxAccount(true); + SyncHelpers.unlinkFirefoxAccount(true); }); setEventListener("fxaSyncComputerName", "keypress", function (e) { if (e.keyCode == KeyEvent.DOM_VK_RETURN) { @@ -814,16 +1005,16 @@ var gSyncPane = { win.switchToTabHavingURI(url, true, options); }, - /** - * Attempts to take the user through the sign in flow by opening the web content - * with the given entrypoint as a query parameter - * - * @param entrypoint: An string appended to the query parameters, used in telemtry to differentiate - * different entrypoints to accounts - */ - async reSignIn(entrypoint) { - const url = await FxAccounts.config.promiseConnectAccountURI(entrypoint); - SyncHelpers.replaceTabWithUrl(url); + // Replace the current tab with the specified URL. + replaceTabWithUrl(url) { + // Get the <browser> element hosting us. + let browser = window.docShell.chromeEventHandler; + // And tell it to load our URL. + browser.loadURI(Services.io.newURI(url), { + triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal( + {} + ), + }); }, clickOrSpaceOrEnterPressed(event) { @@ -853,17 +1044,6 @@ var gSyncPane = { } }, - async verifyFirefoxAccount() { - return this.reSignIn("preferences-reverify"); - }, - - // Disconnect the account, including everything linked. - unlinkFirefoxAccount(confirm) { - window.browsingContext.topChromeWindow.gSync.disconnect({ - confirm, - }); - }, - pairAnotherDevice() { gSubDialog.open( "chrome://browser/content/preferences/fxaPairDevice.xhtml", diff --git a/browser/locales/en-US/browser/preferences/preferences.ftl b/browser/locales/en-US/browser/preferences/preferences.ftl @@ -997,6 +997,13 @@ containers-remove-button = sync-group-label = .label = Sync +account-group-label = + .label = { -vendor-short-name } account + +account-placeholder = + .label = You’re not signed in + .description = Sign in to keep your data private, encrypted, and synced across devices. + ## Firefox account - Signed out. Note that "Sync" and "Firefox account" are now ## more discrete ("signed in" no longer means "and sync is connected"). @@ -1011,6 +1018,10 @@ sync-signedout-account-signin-4 = .label = Sign in to your account to start syncing .accesskey = i +sync-signedout-account-short = + .label = Sign in + .accesskey = i + # This message contains two links and two icon images. # `<img data-l10n-name="android-icon"/>` - Android logo icon # `<a data-l10n-name="android-link">` - Link to Android Download @@ -1037,15 +1048,40 @@ sync-sign-out = .label = Sign Out… .accesskey = g +sync-sign-out2 = + .label = Sign out + .accesskey = g + sync-manage-account = Manage account .accesskey = o +sync-manage-account2 = + .label = Manage account + .accesskey = o + ## Variables ## $email (string) - Email used for Firefox account +## $name (string) - Name used for Firefox account + +sync-account-signed-in = + .label = { $email } + +sync-account-signed-in-display-name = + .label = { $name } + .description = { $email } sync-signedin-unverified = { $email } is not verified. + +sync-signedin-unverified2 = + .label = { $email } isn’t confirmed yet + .description = Check your inbox to confirm your account and make it official. + sync-signedin-login-failure = Please sign in to reconnect { $email } +sync-signedin-login-failure2 = + .label = You’re signed out of { $email } + .description = Sign back in to reconnect and start syncing your data. + ## sync-verify-account = diff --git a/browser/themes/shared/preferences/preferences.css b/browser/themes/shared/preferences/preferences.css @@ -792,6 +792,16 @@ button#noFxaSignIn { margin-inline: 4px 8px; } +#fxaLoginVerified { + --box-icon-size: calc(var(--icon-size-xlarge) * 2); + --box-icon-border-radius: var(--border-radius-circle); + + &[description] { + --box-label-alignment: end; + --box-description-alignment: start; + } +} + #syncStatusContainer { margin-top: 32px; } diff --git a/python/l10n/fluent_migrations/bug_1971835_sync_preferences_update.py b/python/l10n/fluent_migrations/bug_1971835_sync_preferences_update.py @@ -0,0 +1,51 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +import re +from fluent.migrate.transforms import TransformPattern, COPY_PATTERN +import fluent.syntax.ast as FTL + + +class STRIP_ELLIPSIS(TransformPattern): + def visit_TextElement(self, node): + node.value = re.sub(r"(?:…|\.\.\.)$", "", node.value) + return node + + +def migrate(ctx): + """Bug 1971835 - Update account preferences strings, part {index}.""" + + source = "browser/browser/preferences/preferences.ftl" + + ctx.add_transforms( + source, + source, + [ + FTL.Message( + id=FTL.Identifier("sync-sign-out2"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=STRIP_ELLIPSIS(source, "sync-sign-out.label"), + ), + FTL.Attribute( + id=FTL.Identifier("accesskey"), + value=COPY_PATTERN(source, "sync-sign-out.accesskey"), + ), + ], + ), + FTL.Message( + id=FTL.Identifier("sync-manage-account2"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=COPY_PATTERN(source, "sync-manage-account"), + ), + FTL.Attribute( + id=FTL.Identifier("accesskey"), + value=COPY_PATTERN(source, "sync-manage-account.accesskey"), + ), + ], + ), + ], + ) diff --git a/toolkit/content/widgets/moz-box-common.css b/toolkit/content/widgets/moz-box-common.css @@ -52,6 +52,7 @@ .label { grid-area: label; font-weight: var(--box-label-font-weight, normal); + align-self: var(--box-label-alignment); } .icon { @@ -61,6 +62,7 @@ -moz-context-properties: fill, stroke; fill: var(--box-icon-fill); stroke: var(--box-icon-stroke); + border-radius: var(--box-icon-border-radius); &:not(.nav-icon) { fill: var(--box-icon-start-fill, var(--box-icon-fill)); @@ -73,6 +75,7 @@ display: flex; justify-content: center; gap: var(--space-small); + align-self: var(--box-description-alignment); } }