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:
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 {