tor-browser

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

commit 1eced8664b90328443b6232de76c861511f53c64
parent d8eb790ee4bb2b3f687ef21057421d1a86aae6df
Author: Anna Kulyk <akulyk@mozilla.com>
Date:   Thu, 11 Dec 2025 19:21:32 +0000

Bug 1971841 - Part 3: Convert Sync section to config-based prefs r=fluent-reviewers,desktop-theme-reviewers,hjones,bolsson

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

Diffstat:
Mbrowser/components/preferences/main.js | 95+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mbrowser/components/preferences/sync.inc.xhtml | 6+++++-
Mbrowser/components/preferences/sync.js | 531+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Mbrowser/components/preferences/tests/browser_sync_chooseWhatToSync.js | 2+-
Mbrowser/components/preferences/widgets/sync-device-name/sync-device-name.mjs | 2++
Mbrowser/locales/en-US/browser/preferences/preferences.ftl | 38++++++++++++++++++++++++++++++++++++++
Mbrowser/modules/BrowserUsageTelemetry.sys.mjs | 1+
Mbrowser/themes/shared/preferences/preferences.css | 20+++++++++++++++++++-
Apython/l10n/fluent_migrations/bug_1971841_sync_settings_redesign.py | 93+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtoolkit/content/widgets/moz-box-common.css | 5+++++
10 files changed, 655 insertions(+), 138 deletions(-)

diff --git a/browser/components/preferences/main.js b/browser/components/preferences/main.js @@ -3125,6 +3125,101 @@ SettingGroupManager.registerGroups({ }, ], }, + sync: { + inProgress: true, + l10nId: "sync-group-label", + headingLevel: 2, + items: [ + { + id: "syncNoFxaSignIn", + l10nId: "sync-signedout-account-signin-4", + control: "moz-box-link", + iconSrc: "chrome://global/skin/icons/warning.svg", + controlAttrs: { + id: "noFxaSignIn", + }, + }, + { + id: "syncConfigured", + control: "moz-box-group", + items: [ + { + id: "syncStatus", + l10nId: "prefs-syncing-on-2", + control: "moz-box-item", + iconSrc: "chrome://global/skin/icons/check-filled.svg", + items: [ + { + id: "syncNow", + control: "moz-button", + l10nId: "prefs-sync-now-button-2", + slot: "actions", + }, + { + id: "syncing", + control: "moz-button", + l10nId: "prefs-syncing-button-2", + slot: "actions", + }, + ], + }, + { + id: "syncEnginesList", + control: "sync-engines-list", + }, + { + id: "syncChangeOptions", + control: "moz-box-button", + l10nId: "sync-manage-options-2", + }, + ], + }, + { + id: "syncNotConfigured", + l10nId: "prefs-syncing-off-2", + control: "moz-box-item", + iconSrc: "chrome://global/skin/icons/warning.svg", + items: [ + { + id: "syncSetup", + control: "moz-button", + l10nId: "prefs-sync-turn-on-syncing-2", + slot: "actions", + }, + ], + }, + { + id: "fxaDeviceNameSection", + l10nId: "sync-device-name-header-2", + control: "moz-fieldset", + controlAttrs: { + ".headingLevel": 3, + }, + items: [ + { + id: "fxaDeviceNameGroup", + control: "moz-box-group", + items: [ + { + id: "fxaDeviceName", + control: "sync-device-name", + }, + { + id: "fxaConnectAnotherDevice", + l10nId: "sync-connect-another-device-2", + control: "moz-box-link", + iconSrc: "chrome://browser/skin/device-phone.svg", + controlAttrs: { + id: "connect-another-device", + href: "https://accounts.firefox.com/pair", + }, + }, + ], + }, + ], + }, + ], + }, }); /** diff --git a/browser/components/preferences/sync.inc.xhtml b/browser/components/preferences/sync.inc.xhtml @@ -13,7 +13,7 @@ <html:h1 data-l10n-id="pane-sync-title3"/> </hbox> -<deck id="weavePrefsDeck" data-category="paneSync" hidden="true" +<deck id="weavePrefsDeck" data-category="paneSync" data-srd-groupid="sync" hidden="true" data-hidden-from-search="true"> <groupbox id="noFxaAccount"> <hbox> @@ -245,6 +245,10 @@ </vbox> </vbox> </deck> + +<!-- Sync --> +<html:setting-group groupid="sync" hidden="true" data-category="paneSync"/> + <!-- Firefox Backup --> <hbox id="backupCategory" class="subcategory" diff --git a/browser/components/preferences/sync.js b/browser/components/preferences/sync.js @@ -30,6 +30,389 @@ ChromeUtils.defineESModuleGetters(lazy, { BackupService: "resource:///modules/backup/BackupService.sys.mjs", }); +Preferences.addAll([ + // sync + { id: "services.sync.engine.bookmarks", type: "bool" }, + { id: "services.sync.engine.history", type: "bool" }, + { id: "services.sync.engine.tabs", type: "bool" }, + { id: "services.sync.engine.passwords", type: "bool" }, + { id: "services.sync.engine.addresses", type: "bool" }, + { id: "services.sync.engine.creditcards", type: "bool" }, + { id: "services.sync.engine.addons", type: "bool" }, + { id: "services.sync.engine.prefs", type: "bool" }, +]); + +/** + * A helper class for managing sync related UI behavior. + */ +var SyncHelpers = new (class SyncHelpers { + /** + * href for Connect another device link. + * + * @type {string} + */ + connectAnotherDeviceHref = ""; + + /** + * Returns the current global UIState. + * + * @type {object} + * @readonly + */ + get uiState() { + let state = UIState.get(); + return state; + } + + /** + * Retrieves the current UI state status from the global UIState. + * + * @type {string} + * @readonly + */ + get uiStateStatus() { + return this.uiState.status; + } + + /** + * Whether Sync is currently enabled in the UIState. + * + * @type {boolean} + * @readonly + */ + get isSyncEnabled() { + return this.uiState.syncEnabled; + } + + /** + * Extracts and sanitizes the `entrypoint` parameter from the current document URL. + * + * @returns {string} The sanitized entry point name. + */ + getEntryPoint() { + let params = URL.fromURI(document.documentURIObject).searchParams; + let entryPoint = params.get("entrypoint") || "preferences"; + entryPoint = entryPoint.replace(/[^-.\w]/g, ""); + return entryPoint; + } + + /** + * Replace the current tab with the specified URL. + * + * @param {string} 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( + {} + ), + }); + } + + /** + * Opens the "Choose What to Sync" dialog and handles user interaction. + * + * @param {boolean} isSyncConfigured + * Whether Sync is already configured for this profile. + * @param {string|null} [why=null] + * Optional reason or event name indicating why the dialog was opened. + * @returns {Promise<void>} + * Resolves when the dialog flow and any post-actions have completed. + */ + async _chooseWhatToSync(isSyncConfigured, why = null) { + // Record the user opening the choose what to sync menu. + fxAccounts.telemetry.recordOpenCWTSMenu(why).catch(err => { + console.error("Failed to record open CWTS menu event", err); + }); + + // Assuming another device is syncing and we're not, + // we update the engines selection so the correct + // checkboxes are pre-filed. + if (!isSyncConfigured) { + try { + await Weave.Service.updateLocalEnginesState(); + } catch (err) { + console.error("Error updating the local engines state", err); + } + } + let params = {}; + if (isSyncConfigured) { + // If we are already syncing then we also offer to disconnect. + params.disconnectFun = () => this.disconnectSync(); + } + gSubDialog.open( + "chrome://browser/content/preferences/dialogs/syncChooseWhatToSync.xhtml", + { + closingCallback: event => { + if (event.detail.button == "accept") { + // Sync wasn't previously configured, but the user has accepted + // so we want to now start syncing! + if (!isSyncConfigured) { + fxAccounts.telemetry + .recordConnection(["sync"], "ui") + .then(() => { + return Weave.Service.configure(); + }) + .catch(err => { + console.error("Failed to enable sync", err); + }); + } else { + // User is already configured and have possibly changed the engines they want to + // sync, so we should let the server know immediately + // if the user is currently syncing, we queue another sync after + // to ensure we caught their updates + Services.tm.dispatchToMainThread(() => { + Weave.Service.queueSync("cwts"); + }); + } + } + // When the modal closes we want to remove any query params + // so it doesn't open on subsequent visits (and will reload) + const browser = window.docShell.chromeEventHandler; + browser.loadURI(Services.io.newURI("about:preferences#sync"), { + triggeringPrincipal: + Services.scriptSecurityManager.getSystemPrincipal(), + }); + }, + }, + params /* aParams */ + ); + } + + // Disconnect sync, leaving the account connected. + disconnectSync() { + return window.browsingContext.topChromeWindow.gSync.disconnect({ + confirm: true, + disconnectAccount: false, + }); + } + + async setupSync() { + try { + const hasKeys = await fxAccounts.keys.hasKeysForScope(SCOPE_APP_SYNC); + if (hasKeys) { + // User has keys - open the choose what to sync dialog + this._chooseWhatToSync(false, "setupSync"); + } else { + // User signed in via third-party auth without sync keys. + // Redirect to FxA to create a password and generate sync keys. + // canConnectAccount() checks if the Primary Password is locked and + // prompts the user to unlock it. Returns false if the user cancels. + if (!(await FxAccounts.canConnectAccount())) { + return; + } + const url = await FxAccounts.config.promiseConnectAccountURI( + this.getEntryPoint() + ); + this.replaceTabWithUrl(url); + } + } catch (err) { + console.error("Failed to check for sync keys", err); + // Fallback to opening CWTS dialog + this._chooseWhatToSync(false, "setupSync"); + } + } + + async signIn() { + if (!(await FxAccounts.canConnectAccount())) { + return; + } + const url = await FxAccounts.config.promiseConnectAccountURI( + this.getEntryPoint() + ); + this.replaceTabWithUrl(url); + } +})(); + +// Sync section +Preferences.addSetting({ + id: "uiStateUpdate", + setup(emitChange) { + Weave.Svc.Obs.add(UIState.ON_UPDATE, emitChange); + return () => Weave.Svc.Obs.remove(UIState.ON_UPDATE, emitChange); + }, +}); + +// Sync section - no Firefox account +Preferences.addSetting({ + id: "syncNoFxaSignIn", + deps: ["uiStateUpdate"], + visible() { + return SyncHelpers.uiStateStatus === UIState.STATUS_NOT_CONFIGURED; + }, + onUserClick: () => { + SyncHelpers.signIn(); + }, +}); + +// Sync section - Syncing is OFF +Preferences.addSetting({ + id: "syncNotConfigured", + deps: ["uiStateUpdate"], + visible() { + return ( + SyncHelpers.uiStateStatus === UIState.STATUS_SIGNED_IN && + !SyncHelpers.isSyncEnabled + ); + }, +}); +Preferences.addSetting({ + id: "syncSetup", + onUserClick: () => SyncHelpers.setupSync(), +}); + +// Sync section - Syncing is ON +Preferences.addSetting({ + id: "syncConfigured", + deps: ["uiStateUpdate"], + visible() { + return ( + SyncHelpers.uiStateStatus === UIState.STATUS_SIGNED_IN && + SyncHelpers.isSyncEnabled + ); + }, +}); + +Preferences.addSetting({ + id: "syncStatus", +}); +Preferences.addSetting({ + id: "syncNow", + deps: ["uiStateUpdate"], + onUserClick() { + Weave.Service.sync({ why: "aboutprefs" }); + }, + visible: () => !SyncHelpers.uiState.syncing, + // Bug 2004864 - add tooltip +}); +Preferences.addSetting({ + id: "syncing", + deps: ["uiStateUpdate"], + disabled: () => SyncHelpers.uiState.syncing, + visible: () => SyncHelpers.uiState.syncing, +}); + +const SYNC_ENGINE_SETTINGS = [ + { + id: "syncBookmarks", + pref: "services.sync.engine.bookmarks", + type: "bookmarks", + }, + { id: "syncHistory", pref: "services.sync.engine.history", type: "history" }, + { id: "syncTabs", pref: "services.sync.engine.tabs", type: "tabs" }, + { + id: "syncPasswords", + pref: "services.sync.engine.passwords", + type: "passwords", + }, + { + id: "syncAddresses", + pref: "services.sync.engine.addresses", + type: "addresses", + }, + { + id: "syncPayments", + pref: "services.sync.engine.creditcards", + type: "payments", + }, + { id: "syncAddons", pref: "services.sync.engine.addons", type: "addons" }, + { id: "syncSettings", pref: "services.sync.engine.prefs", type: "settings" }, +]; + +SYNC_ENGINE_SETTINGS.forEach(({ id, pref }) => { + Preferences.addSetting({ id, pref }); +}); + +Preferences.addSetting({ + id: "syncEnginesList", + deps: SYNC_ENGINE_SETTINGS.map(({ id }) => id), + getControlConfig(config, deps) { + const engines = SYNC_ENGINE_SETTINGS.filter( + ({ id }) => deps[id]?.value + ).map(({ type }) => type); + + return { + ...config, + controlAttrs: { + ...config.controlAttrs, + ".engines": engines, + }, + }; + }, +}); + +Preferences.addSetting({ + id: "syncChangeOptions", + onUserClick: () => { + SyncHelpers._chooseWhatToSync(true, "manageSyncSettings"); + }, +}); + +// Sync section - Device name +Preferences.addSetting({ + id: "fxaDeviceNameSection", + deps: ["uiStateUpdate"], + visible() { + return SyncHelpers.uiStateStatus !== UIState.STATUS_NOT_CONFIGURED; + }, +}); +Preferences.addSetting({ + id: "fxaDeviceNameGroup", +}); +Preferences.addSetting({ + id: "fxaDeviceName", + deps: ["uiStateUpdate"], + get: () => Weave.Service.clientsEngine.localName, + set(val) { + Weave.Service.clientsEngine.localName = val; + }, + disabled() { + return SyncHelpers.uiStateStatus !== UIState.STATUS_SIGNED_IN; + }, + getControlConfig(config) { + if (config.controlAttrs?.defaultvalue) { + return config; + } + const deviceDefaultLocalName = fxAccounts?.device?.getDefaultLocalName(); + if (deviceDefaultLocalName) { + return { + ...config, + controlAttrs: { + ...config.controlAttrs, + defaultvalue: deviceDefaultLocalName, + }, + }; + } + return config; + }, +}); +Preferences.addSetting({ + id: "fxaConnectAnotherDevice", + getControlConfig(config) { + if (SyncHelpers.connectAnotherDeviceHref) { + return { + ...config, + controlAttrs: { + ...config.controlAttrs, + href: SyncHelpers.connectAnotherDeviceHref, + }, + }; + } + return config; + }, + setup(emitChange) { + FxAccounts.config + .promiseConnectDeviceURI(SyncHelpers.getEntryPoint()) + .then(connectURI => { + SyncHelpers.connectAnotherDeviceHref = connectURI; + emitChange(); + }); + }, +}); + var gSyncPane = { get page() { return document.getElementById("weavePrefsDeck").selectedIndex; @@ -121,6 +504,8 @@ var gSyncPane = { }, _init() { + initSettingGroup("sync"); + Weave.Svc.Obs.add(UIState.ON_UPDATE, this.updateWeavePrefs, this); window.addEventListener("unload", () => { @@ -128,12 +513,13 @@ var gSyncPane = { }); FxAccounts.config - .promiseConnectDeviceURI(this._getEntryPoint()) + .promiseConnectDeviceURI(SyncHelpers.getEntryPoint()) .then(connectURI => { document .getElementById("connect-another-device") .setAttribute("href", connectURI); }); + // Links for mobile devices. for (let platform of ["android", "ios"]) { let url = @@ -164,7 +550,7 @@ var gSyncPane = { if (location.href.includes("action=pair")) { gSyncPane.pairAnotherDevice(); } else if (location.href.includes("action=choose-what-to-sync")) { - gSyncPane._chooseWhatToSync(false, "callToAction"); + SyncHelpers._chooseWhatToSync(false, "callToAction"); } } }, @@ -241,7 +627,7 @@ var gSyncPane = { this._focusAfterComputerNameTextbox(); }); setEventListener("noFxaSignIn", "command", function () { - gSyncPane.signIn(); + SyncHelpers.signIn(); return false; }); setEventListener("fxaUnlinkButton", "command", function () { @@ -257,7 +643,7 @@ var gSyncPane = { gSyncPane.unlinkFirefoxAccount(false); }); setEventListener("rejectReSignIn", "command", function () { - gSyncPane.reSignIn(this._getEntryPoint()); + gSyncPane.reSignIn(SyncHelpers.getEntryPoint()); }); setEventListener("rejectUnlinkFxaAccount", "command", function () { gSyncPane.unlinkFirefoxAccount(true); @@ -269,34 +655,9 @@ var gSyncPane = { document.getElementById("fxaCancelChangeDeviceName").click(); } }); - setEventListener("syncSetup", "command", async function () { - // Check if the user has sync keys before opening CWTS - try { - const hasKeys = await fxAccounts.keys.hasKeysForScope(SCOPE_APP_SYNC); - if (hasKeys) { - // User has keys - open the choose what to sync dialog - this._chooseWhatToSync(false, "setupSync"); - } else { - // User signed in via third-party auth without sync keys. - // Redirect to FxA to create a password and generate sync keys. - // canConnectAccount() checks if the Primary Password is locked and - // prompts the user to unlock it. Returns false if the user cancels. - if (!(await FxAccounts.canConnectAccount())) { - return; - } - const url = await FxAccounts.config.promiseConnectAccountURI( - this._getEntryPoint() - ); - this.replaceTabWithUrl(url); - } - } catch (err) { - console.error("Failed to check for sync keys", err); - // Fallback to opening CWTS dialog - this._chooseWhatToSync(false, "setupSync"); - } - }); + setEventListener("syncSetup", "command", () => SyncHelpers.setupSync()); setEventListener("syncChangeOptions", "command", function () { - this._chooseWhatToSync(true, "manageSyncSettings"); + SyncHelpers._chooseWhatToSync(true, "manageSyncSettings"); }); setEventListener("syncNow", "command", function () { // syncing can take a little time to send the "started" notification, so @@ -320,14 +681,12 @@ var gSyncPane = { }, updateSyncUI() { - const state = UIState.get(); - const isSyncEnabled = state.syncEnabled; let syncStatusTitle = document.getElementById("syncStatusTitle"); let syncNowButton = document.getElementById("syncNow"); let syncNotConfiguredEl = document.getElementById("syncNotConfigured"); let syncConfiguredEl = document.getElementById("syncConfigured"); - if (isSyncEnabled) { + if (SyncHelpers.isSyncEnabled) { syncStatusTitle.setAttribute("data-l10n-id", "prefs-syncing-on"); syncNowButton.hidden = false; syncConfiguredEl.hidden = false; @@ -340,67 +699,6 @@ var gSyncPane = { } }, - async _chooseWhatToSync(isSyncConfigured, why = null) { - // Record the user opening the choose what to sync menu. - fxAccounts.telemetry.recordOpenCWTSMenu(why).catch(err => { - console.error("Failed to record open CWTS menu event", err); - }); - - // Assuming another device is syncing and we're not, - // we update the engines selection so the correct - // checkboxes are pre-filed. - if (!isSyncConfigured) { - try { - await Weave.Service.updateLocalEnginesState(); - } catch (err) { - console.error("Error updating the local engines state", err); - } - } - let params = {}; - if (isSyncConfigured) { - // If we are already syncing then we also offer to disconnect. - params.disconnectFun = () => this.disconnectSync(); - } - gSubDialog.open( - "chrome://browser/content/preferences/dialogs/syncChooseWhatToSync.xhtml", - { - closingCallback: event => { - if (event.detail.button == "accept") { - // Sync wasn't previously configured, but the user has accepted - // so we want to now start syncing! - if (!isSyncConfigured) { - fxAccounts.telemetry - .recordConnection(["sync"], "ui") - .then(() => { - this.updateSyncUI(); - return Weave.Service.configure(); - }) - .catch(err => { - console.error("Failed to enable sync", err); - }); - } else { - // User is already configured and have possibly changed the engines they want to - // sync, so we should let the server know immediately - // if the user is currently syncing, we queue another sync after - // to ensure we caught their updates - Services.tm.dispatchToMainThread(() => { - Weave.Service.queueSync("cwts"); - }); - } - } - // When the modal closes we want to remove any query params - // so it doesn't open on subsequent visits (and will reload) - const browser = window.docShell.chromeEventHandler; - browser.loadURI(Services.io.newURI("about:preferences#sync"), { - triggeringPrincipal: - Services.scriptSecurityManager.getSystemPrincipal(), - }); - }, - }, - params /* aParams */ - ); - }, - _updateSyncNow(syncing) { let butSyncNow = document.getElementById("syncNow"); let fluentID = syncing ? "prefs-syncing-button" : "prefs-sync-now-button"; @@ -494,7 +792,7 @@ var gSyncPane = { // The "manage account" link embeds the uid, so we need to update this // if the account state changes. FxAccounts.config - .promiseManageURI(this._getEntryPoint()) + .promiseManageURI(SyncHelpers.getEntryPoint()) .then(accountsManageURI => { document .getElementById("verifiedManage") @@ -507,13 +805,6 @@ var gSyncPane = { this.updateSyncUI(); }, - _getEntryPoint() { - let params = URL.fromURI(document.documentURIObject).searchParams; - let entryPoint = params.get("entrypoint") || "preferences"; - entryPoint = entryPoint.replace(/[^-.\w]/g, ""); - return entryPoint; - }, - openContentInBrowser(url, options) { let win = Services.wm.getMostRecentWindow("navigator:browser"); if (!win) { @@ -523,28 +814,6 @@ var gSyncPane = { win.switchToTabHavingURI(url, true, options); }, - // 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( - {} - ), - }); - }, - - async signIn() { - if (!(await FxAccounts.canConnectAccount())) { - return; - } - const url = await FxAccounts.config.promiseConnectAccountURI( - this._getEntryPoint() - ); - 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 @@ -554,7 +823,7 @@ var gSyncPane = { */ async reSignIn(entrypoint) { const url = await FxAccounts.config.promiseConnectAccountURI(entrypoint); - this.replaceTabWithUrl(url); + SyncHelpers.replaceTabWithUrl(url); }, clickOrSpaceOrEnterPressed(event) { @@ -571,7 +840,7 @@ var gSyncPane = { openChangeProfileImage(event) { if (this.clickOrSpaceOrEnterPressed(event)) { FxAccounts.config - .promiseChangeAvatarURI(this._getEntryPoint()) + .promiseChangeAvatarURI(SyncHelpers.getEntryPoint()) .then(url => { this.openContentInBrowser(url, { replaceQueryString: true, @@ -595,14 +864,6 @@ var gSyncPane = { }); }, - // Disconnect sync, leaving the account connected. - disconnectSync() { - return window.browsingContext.topChromeWindow.gSync.disconnect({ - confirm: true, - disconnectAccount: false, - }); - }, - pairAnotherDevice() { gSubDialog.open( "chrome://browser/content/preferences/fxaPairDevice.xhtml", diff --git a/browser/components/preferences/tests/browser_sync_chooseWhatToSync.js b/browser/components/preferences/tests/browser_sync_chooseWhatToSync.js @@ -409,7 +409,7 @@ async function runWithCWTSDialog(test) { let promiseSubDialogLoaded = promiseLoadSubDialog( "chrome://browser/content/preferences/dialogs/syncChooseWhatToSync.xhtml" ); - gBrowser.contentWindow.gSyncPane._chooseWhatToSync(true); + gBrowser.contentWindow.SyncHelpers._chooseWhatToSync(true); let win = await promiseSubDialogLoaded; diff --git a/browser/components/preferences/widgets/sync-device-name/sync-device-name.mjs b/browser/components/preferences/widgets/sync-device-name/sync-device-name.mjs @@ -66,6 +66,8 @@ class SyncDeviceName extends MozLitElement { this.value = inputVal === "" ? this.defaultValue : inputVal; this._isInEditMode = false; this.setFocus(); + + this.dispatchEvent(new Event("change", { bubbles: true })); } /** diff --git a/browser/locales/en-US/browser/preferences/preferences.ftl b/browser/locales/en-US/browser/preferences/preferences.ftl @@ -980,6 +980,11 @@ containers-settings-button = containers-remove-button = .label = Remove +## Account and sync + +sync-group-label = + .label = Sync + ## Firefox account - Signed out. Note that "Sync" and "Firefox account" are now ## more discrete ("signed in" no longer means "and sync is connected"). @@ -990,6 +995,10 @@ sync-signedout-account-signin3 = .label = Sign in to sync… .accesskey = i +sync-signedout-account-signin-4 = + .label = Sign in to your account to start syncing + .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 @@ -1043,21 +1052,40 @@ sync-sign-in = prefs-syncing-on = Syncing: ON +prefs-syncing-on-2 = + .label = Syncing is ON + prefs-syncing-off = Syncing: OFF +prefs-syncing-off-2 = + .label = Syncing is OFF + .description = Turn on sync to get your your bookmarks, passwords, history, and more on any device. + prefs-sync-turn-on-syncing = .label = Turn on syncing… .accesskey = s +prefs-sync-turn-on-syncing-2 = + .label = Turn on syncing + .accesskey = s + prefs-sync-offer-setup-label2 = Synchronize your bookmarks, history, tabs, passwords, add-ons, and settings across all your devices. prefs-sync-now-button = .label = Sync Now .accesskey = N +prefs-sync-now-button-2 = + .label = Sync now + .accesskey = N + prefs-syncing-button = .label = Syncing… +prefs-syncing-button-2 = + .label = Syncing… + .title = Sync now + ## The list of things currently syncing. sync-syncing-across-devices-heading = You are syncing these items across all your connected devices: @@ -1081,6 +1109,10 @@ sync-manage-options = .label = Manage sync… .accesskey = M +sync-manage-options-2 = + .label = Manage synced data + .accesskey = M + ## The "Choose what to sync" dialog. sync-choose-what-to-sync-dialog4 = @@ -1133,6 +1165,9 @@ sync-engine-settings = sync-device-name-header = Device Name +sync-device-name-header-2 = + .label = Device Name + # Variables: # $placeholder (string) - The placeholder text of the input sync-device-name-input = @@ -1157,6 +1192,9 @@ sync-device-name-save = sync-connect-another-device = Connect another device +sync-connect-another-device-2 = + .label = Connect another device + ## Privacy Section privacy-header = Browser Privacy diff --git a/browser/modules/BrowserUsageTelemetry.sys.mjs b/browser/modules/BrowserUsageTelemetry.sys.mjs @@ -82,6 +82,7 @@ const UI_TARGET_CHANGE_ELEMENTS = new Set([ "moz-input-search", "moz-input-text", "moz-visual-picker-item", + "sync-device-name", ]); const UI_TARGET_COMMAND_ELEMENTS = new Set([ "menuitem", diff --git a/browser/themes/shared/preferences/preferences.css b/browser/themes/shared/preferences/preferences.css @@ -736,10 +736,28 @@ html|dialog, margin-block: 0 4px; } -#noFxaSignIn { +button#noFxaSignIn { margin-inline-start: 8px; } +#syncConfigured { + --box-icon-start-fill: var(--icon-color-success); +} + +#syncNotConfigured, +#noFxaSignIn { + --box-icon-start-fill: var(--icon-color-warning); +} + +#syncSetup { + white-space: nowrap; +} + +#syncNow, +#syncing { + min-width: 150px; +} + .fxaSyncIllustration { list-style-image: url(chrome://browser/skin/fxa/sync-devices.svg); width: 312px; diff --git a/python/l10n/fluent_migrations/bug_1971841_sync_settings_redesign.py b/python/l10n/fluent_migrations/bug_1971841_sync_settings_redesign.py @@ -0,0 +1,93 @@ +# 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 1971841 - Convert Sync section to config-based prefs, part {index}""" + + path = "browser/browser/preferences/preferences.ftl" + + ctx.add_transforms( + path, + path, + [ + FTL.Message( + id=FTL.Identifier("sync-group-label"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=COPY_PATTERN(path, "pane-sync-title3"), + ), + ], + ), + FTL.Message( + id=FTL.Identifier("prefs-sync-turn-on-syncing-2"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=STRIP_ELLIPSIS(path, "prefs-sync-turn-on-syncing.label"), + ), + FTL.Attribute( + id=FTL.Identifier("accesskey"), + value=COPY_PATTERN( + path, "prefs-sync-turn-on-syncing.accesskey" + ), + ), + ], + ), + FTL.Message( + id=FTL.Identifier("prefs-sync-now-button-2"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=COPY_PATTERN(path, "prefs-sync-now-button.label"), + ), + FTL.Attribute( + id=FTL.Identifier("accesskey"), + value=COPY_PATTERN(path, "prefs-sync-now-button.accesskey"), + ), + ], + ), + FTL.Message( + id=FTL.Identifier("prefs-syncing-button-2"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=COPY_PATTERN(path, "prefs-syncing-button.label"), + ), + FTL.Attribute( + id=FTL.Identifier("title"), + value=COPY_PATTERN(path, "prefs-sync-now-button.label"), + ), + ], + ), + FTL.Message( + id=FTL.Identifier("sync-device-name-header-2"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=COPY_PATTERN(path, "sync-device-name-header"), + ), + ], + ), + FTL.Message( + id=FTL.Identifier("sync-connect-another-device-2"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=COPY_PATTERN(path, "sync-connect-another-device"), + ), + ], + ), + ], + ) diff --git a/toolkit/content/widgets/moz-box-common.css b/toolkit/content/widgets/moz-box-common.css @@ -61,6 +61,11 @@ -moz-context-properties: fill, stroke; fill: var(--box-icon-fill); stroke: var(--box-icon-stroke); + + &:not(.nav-icon) { + fill: var(--box-icon-start-fill, var(--box-icon-fill)); + stroke: var(--box-icon-start-stroke, var(--box-icon-stroke)); + } } .description {