mirror of
https://github.com/zen-browser/desktop.git
synced 2026-08-24 15:31:34 +00:00
gh-14470: Fixed moz-remote-urls not being synced correctly (gh-15043)
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
diff --git a/browser/components/preferences/preferences.js b/browser/components/preferences/preferences.js
|
||||
index c76a0647e14bac806d9428483bb565b3e10c31e7..2188f7808aca090f92574c7f5c55a28a9c5bb3f8 100644
|
||||
index a76699af75252bec31e17b562463c37236cbbfe5..6355a6ee92754846ffcdd123ac49545d520470c7 100644
|
||||
--- a/browser/components/preferences/preferences.js
|
||||
+++ b/browser/components/preferences/preferences.js
|
||||
@@ -131,6 +131,7 @@ ChromeUtils.defineLazyGetter(this, "gSubDialog", function () {
|
||||
@@ -132,6 +132,7 @@ ChromeUtils.defineLazyGetter(this, "gSubDialog", function () {
|
||||
styleSheets: [
|
||||
"chrome://browser/skin/preferences/dialog.css",
|
||||
"chrome://browser/skin/preferences/preferences.css",
|
||||
@@ -10,7 +10,7 @@ index c76a0647e14bac806d9428483bb565b3e10c31e7..2188f7808aca090f92574c7f5c55a28a
|
||||
],
|
||||
resizeCallback: async ({ title, frame }) => {
|
||||
// Search within main document and highlight matched keyword.
|
||||
@@ -437,6 +438,8 @@ const CONFIG_PANES = Object.freeze({
|
||||
@@ -462,6 +463,8 @@ const CONFIG_PANES = Object.freeze({
|
||||
tabsBrowsing: {
|
||||
l10nId: "tabs-browsing-section",
|
||||
groupIds: [
|
||||
@@ -19,7 +19,7 @@ index c76a0647e14bac806d9428483bb565b3e10c31e7..2188f7808aca090f92574c7f5c55a28a
|
||||
"browserLayout",
|
||||
"tabs",
|
||||
"pageNavigation",
|
||||
@@ -483,7 +486,7 @@ function register_module(categoryName, categoryObject) {
|
||||
@@ -509,7 +512,7 @@ function register_module(categoryName, categoryObject) {
|
||||
}
|
||||
this._initted = true;
|
||||
let template = document.getElementById("template-" + categoryName);
|
||||
@@ -28,7 +28,7 @@ index c76a0647e14bac806d9428483bb565b3e10c31e7..2188f7808aca090f92574c7f5c55a28a
|
||||
// Replace the template element with the nodes inside of it.
|
||||
template.replaceWith(template.content);
|
||||
|
||||
@@ -527,6 +530,10 @@ function init_all() {
|
||||
@@ -553,6 +556,10 @@ function init_all() {
|
||||
register_module("paneHome", gHomePane);
|
||||
register_module("paneSearch", gSearchPane);
|
||||
register_module("panePrivacy", gPrivacyPane);
|
||||
@@ -39,15 +39,3 @@ index c76a0647e14bac806d9428483bb565b3e10c31e7..2188f7808aca090f92574c7f5c55a28a
|
||||
|
||||
// Restore the cached Firefox Labs nav button visibility so it shows
|
||||
// immediately when recipes are expected to be available, before
|
||||
@@ -656,9 +663,9 @@ async function gotoPref(
|
||||
let redesignEnabled = srdSectionPrefs.all;
|
||||
let categories = document.getElementById("categories");
|
||||
const kDefaultCategoryInternalName = redesignEnabled
|
||||
- ? "paneSync"
|
||||
+ ? "paneTabsBrowsing"
|
||||
: "paneGeneral";
|
||||
- const kDefaultCategory = redesignEnabled ? "sync" : "general";
|
||||
+ const kDefaultCategory = redesignEnabled ? "tabsBrowsing" : "general";
|
||||
let hash = document.location.hash;
|
||||
let category = aCategory || hash.substring(1) || kDefaultCategoryInternalName;
|
||||
|
||||
|
||||
@@ -368,7 +368,7 @@ class nsZenLiveFoldersManager {
|
||||
},
|
||||
],
|
||||
content: {
|
||||
width: "310px",
|
||||
width: "312px",
|
||||
position: "callout",
|
||||
title_logo: {
|
||||
imageURL: icon,
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
canonicalJSON,
|
||||
LAYOUT_RECORD_ID,
|
||||
RECORD_KINDS,
|
||||
syncableIconUrl,
|
||||
ZenSpacesSyncModel,
|
||||
} from "resource:///modules/zen/ZenSpacesSyncModel.sys.mjs";
|
||||
|
||||
@@ -379,6 +380,13 @@ class nsZenSpacesSyncApplier {
|
||||
folder.label = data.name;
|
||||
}
|
||||
|
||||
if (data.live && !folder.isLiveFolder) {
|
||||
// The folder synced before its provider config was available and
|
||||
// materialized as a plain folder; upgrade it so this profile
|
||||
// projects the config too instead of clobbering it with live: null.
|
||||
folder.isLiveFolder = true;
|
||||
}
|
||||
|
||||
if ((folder.iconURL || null) !== (data.icon || null)) {
|
||||
win.gZenFolders.setFolderUserIcon(folder, data.icon || null);
|
||||
// Window sync picks this up and mirrors the icon everywhere else.
|
||||
@@ -508,7 +516,7 @@ class nsZenSpacesSyncApplier {
|
||||
lazy.ZenWindowSync.setPinnedInitialState(
|
||||
tab,
|
||||
{ url: data.url, title: data.title || "" },
|
||||
data.icon || undefined
|
||||
syncableIconUrl(data.icon) || undefined
|
||||
);
|
||||
}
|
||||
|
||||
@@ -518,12 +526,15 @@ class nsZenSpacesSyncApplier {
|
||||
} else {
|
||||
tab.removeAttribute("zenDefaultUserContextId");
|
||||
}
|
||||
// Records from before icon normalization can still carry wrapped or
|
||||
// remote urls setIcon would refuse.
|
||||
const icon = syncableIconUrl(data.icon);
|
||||
tab.zenStaticLabel =
|
||||
typeof data.staticLabel === "string" ? data.staticLabel : undefined;
|
||||
tab.zenStaticIcon = data.hasStaticIcon && data.icon ? data.icon : undefined;
|
||||
if (data.icon) {
|
||||
tab.zenStaticIcon = data.hasStaticIcon && icon ? icon : undefined;
|
||||
if (icon) {
|
||||
try {
|
||||
win.gBrowser.setIcon(tab, data.icon);
|
||||
win.gBrowser.setIcon(tab, icon);
|
||||
// The tab is lazy, so its session state cache keeps the (empty)
|
||||
// image it was created with: it is only cleared once a restore
|
||||
// finishes, which never happens for lazy tabs, and it would hide
|
||||
@@ -547,15 +558,16 @@ class nsZenSpacesSyncApplier {
|
||||
* @param {object} data
|
||||
*/
|
||||
#updateTabIdentity(win, tab, data) {
|
||||
const icon = syncableIconUrl(data.icon);
|
||||
const initial = tab._zenPinnedInitialState;
|
||||
const identityChanged =
|
||||
initial?.entry?.url !== data.url ||
|
||||
(initial?.entry?.title || "") !== (data.title || "");
|
||||
if (identityChanged || (initial?.image || "") !== (data.icon || "")) {
|
||||
if (identityChanged || syncableIconUrl(initial?.image || "") !== icon) {
|
||||
lazy.ZenWindowSync.setPinnedInitialState(
|
||||
tab,
|
||||
{ url: data.url, title: data.title || "" },
|
||||
data.icon || undefined
|
||||
icon || undefined
|
||||
);
|
||||
}
|
||||
if (identityChanged && !tab.linkedPanel) {
|
||||
@@ -580,10 +592,12 @@ class nsZenSpacesSyncApplier {
|
||||
}
|
||||
}
|
||||
}
|
||||
tab.zenStaticIcon = data.hasStaticIcon && data.icon ? data.icon : undefined;
|
||||
if (data.icon && tab.getAttribute("image") !== data.icon) {
|
||||
tab.zenStaticIcon = data.hasStaticIcon && icon ? icon : undefined;
|
||||
// Compare normalized: setIcon rewraps svg data: icons into
|
||||
// moz-remote-image urls when writing the attribute.
|
||||
if (icon && syncableIconUrl(tab.getAttribute("image")) !== icon) {
|
||||
try {
|
||||
win.gBrowser.setIcon(tab, data.icon);
|
||||
win.gBrowser.setIcon(tab, icon);
|
||||
} catch (e) {
|
||||
console.error("ZenSpacesSync: failed to set tab icon", e);
|
||||
}
|
||||
@@ -619,7 +633,7 @@ class nsZenSpacesSyncApplier {
|
||||
},
|
||||
];
|
||||
state.index = 1;
|
||||
state.image = data.icon || undefined;
|
||||
state.image = syncableIconUrl(data.icon) || undefined;
|
||||
delete state.scroll;
|
||||
win.SessionStore.setTabState(tab, state);
|
||||
} catch (e) {
|
||||
|
||||
@@ -36,6 +36,36 @@ const BUILTIN_GUID_PREFIX = "builtin-";
|
||||
const STORE_FILE_NAME = "zen-spaces-sync.json";
|
||||
const STORE_VERSION = 1;
|
||||
|
||||
// Everything setIcon accepts without a loading principal.
|
||||
const LOCAL_ICON_PROTOCOLS = ["data:", "chrome:", "about:", "resource:"];
|
||||
|
||||
/**
|
||||
* Normalizes a tab icon for syncing. The image attribute can hold a
|
||||
* moz-remote-image: wrapper around the actual (svg data:) favicon, carrying
|
||||
* a per-process contentParentId that means nothing on another device — sync
|
||||
* the wrapped URL instead. Anything else setIcon would reject as remote is
|
||||
* dropped.
|
||||
*
|
||||
* @param {?string} icon
|
||||
* @returns {string}
|
||||
*/
|
||||
export function syncableIconUrl(icon) {
|
||||
if (!icon || typeof icon !== "string") {
|
||||
return "";
|
||||
}
|
||||
if (icon.startsWith("moz-remote-image:")) {
|
||||
try {
|
||||
const uri = Services.io.newURI(icon);
|
||||
icon = new URLSearchParams(uri.query).get("url") || "";
|
||||
} catch (e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
return LOCAL_ICON_PROTOCOLS.some(protocol => icon.startsWith(protocol))
|
||||
? icon
|
||||
: "";
|
||||
}
|
||||
|
||||
function sortedClone(value) {
|
||||
if (Array.isArray(value)) {
|
||||
return value.map(sortedClone);
|
||||
@@ -201,7 +231,7 @@ class nsZenSpacesSyncModel {
|
||||
if (!url || url === "about:blank") {
|
||||
return null;
|
||||
}
|
||||
const icon = initial?.image || tabData.image || "";
|
||||
const icon = syncableIconUrl(initial?.image || tabData.image || "");
|
||||
return { url, title: title || "", icon };
|
||||
}
|
||||
|
||||
@@ -357,6 +387,7 @@ class nsZenSpacesSyncModel {
|
||||
}
|
||||
|
||||
const map = new Map();
|
||||
const pending = new Set();
|
||||
const ctx = this.#projectionContext(sidebar);
|
||||
const { tabs, folders, splits, splitIds, splitParents, splitWs } = ctx;
|
||||
|
||||
@@ -425,6 +456,10 @@ class nsZenSpacesSyncModel {
|
||||
} catch (e) {
|
||||
console.error("ZenSpacesSync: failed to project live folder", e);
|
||||
}
|
||||
if (!live) {
|
||||
pending.add(fid);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
map.set(fid, {
|
||||
kind: RECORD_KINDS.FOLDER,
|
||||
@@ -486,10 +521,20 @@ class nsZenSpacesSyncModel {
|
||||
});
|
||||
}
|
||||
|
||||
this.#cache = { stamp, map };
|
||||
this.#cache = { stamp, map, pending };
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ids that are locally present but deliberately not projected this cycle
|
||||
* (live folders whose provider config isn't available yet). The diff must
|
||||
* not read their absence as a deletion.
|
||||
*/
|
||||
#pendingIds() {
|
||||
this.projections();
|
||||
return this.#cache.pending;
|
||||
}
|
||||
|
||||
#digestCache = null;
|
||||
|
||||
/**
|
||||
@@ -536,6 +581,7 @@ class nsZenSpacesSyncModel {
|
||||
computeChangedIDs() {
|
||||
const uploaded = this.#data().uploaded;
|
||||
const current = this.#digestAll();
|
||||
const pending = this.#pendingIds();
|
||||
const now = Date.now() / 1000;
|
||||
const changes = {};
|
||||
for (const [id, digest] of current) {
|
||||
@@ -544,7 +590,7 @@ class nsZenSpacesSyncModel {
|
||||
}
|
||||
}
|
||||
for (const id of Object.keys(uploaded)) {
|
||||
if (!current.has(id)) {
|
||||
if (!current.has(id) && !pending.has(id)) {
|
||||
changes[id] = now;
|
||||
}
|
||||
}
|
||||
@@ -554,13 +600,14 @@ class nsZenSpacesSyncModel {
|
||||
hasPendingChanges() {
|
||||
const uploaded = this.#data().uploaded;
|
||||
const current = this.#digestAll();
|
||||
const pending = this.#pendingIds();
|
||||
for (const [id, digest] of current) {
|
||||
if (uploaded[id] !== digest) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for (const id of Object.keys(uploaded)) {
|
||||
if (!current.has(id)) {
|
||||
if (!current.has(id) && !pending.has(id)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user