feat: Early support for unsynced windoiws, b=no-bug, c=workspaces

This commit is contained in:
Mr. M
2025-12-03 17:57:46 +01:00
parent 5d964511aa
commit 22f2b1f29b
5 changed files with 101 additions and 31 deletions

View File

@@ -1,5 +1,5 @@
diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/components/tabbrowser/content/tabbrowser.js
index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..3acfbc6d434340f0a629eec7edd9e186f0d2bce5 100644
index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..a932324e780e3c86c504c8b7aa8d433b04ef78d6 100644
--- a/browser/components/tabbrowser/content/tabbrowser.js
+++ b/browser/components/tabbrowser/content/tabbrowser.js
@@ -386,6 +386,7 @@
@@ -715,7 +715,17 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..3acfbc6d434340f0a629eec7edd9e186
if (this.tabs.length == 1) {
return null;
}
@@ -6319,7 +6470,7 @@
@@ -6213,7 +6364,8 @@
AppConstants.BROWSER_CHROME_URL,
"_blank",
options,
- aTab
+ aTab,
+ "zen-unsynced"
);
}
@@ -6319,7 +6471,7 @@
* `true` if element is a `<tab-group>`
*/
isTabGroup(element) {
@@ -724,7 +734,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..3acfbc6d434340f0a629eec7edd9e186
}
/**
@@ -6404,8 +6555,8 @@
@@ -6404,8 +6556,8 @@
}
// Don't allow mixing pinned and unpinned tabs.
@@ -735,7 +745,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..3acfbc6d434340f0a629eec7edd9e186
} else {
tabIndex = Math.max(tabIndex, this.pinnedTabCount);
}
@@ -6431,10 +6582,16 @@
@@ -6431,10 +6583,16 @@
this.#handleTabMove(
element,
() => {
@@ -754,7 +764,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..3acfbc6d434340f0a629eec7edd9e186
if (neighbor && this.isTab(element) && tabIndex > element._tPos) {
neighbor.after(element);
} else {
@@ -6492,23 +6649,28 @@
@@ -6492,23 +6650,28 @@
#moveTabNextTo(element, targetElement, moveBefore = false, metricsContext) {
if (this.isTabGroupLabel(targetElement)) {
targetElement = targetElement.group;
@@ -789,7 +799,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..3acfbc6d434340f0a629eec7edd9e186
} else if (!element.pinned && targetElement && targetElement.pinned) {
// If the caller asks to move an unpinned element next to a pinned
// tab, move the unpinned element to be the first unpinned element
@@ -6521,14 +6683,34 @@
@@ -6521,14 +6684,34 @@
// move the tab group right before the first unpinned tab.
// 4. Moving a tab group and the first unpinned tab is grouped:
// move the tab group right before the first unpinned tab's tab group.
@@ -825,7 +835,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..3acfbc6d434340f0a629eec7edd9e186
element.pinned
? this.tabContainer.pinnedTabsContainer
: this.tabContainer;
@@ -6537,7 +6719,7 @@
@@ -6537,7 +6720,7 @@
element,
() => {
if (moveBefore) {
@@ -834,7 +844,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..3acfbc6d434340f0a629eec7edd9e186
} else if (targetElement) {
targetElement.after(element);
} else {
@@ -6607,10 +6789,10 @@
@@ -6607,10 +6790,10 @@
* @param {TabMetricsContext} [metricsContext]
*/
moveTabToGroup(aTab, aGroup, metricsContext) {
@@ -847,7 +857,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..3acfbc6d434340f0a629eec7edd9e186
return;
}
if (aTab.group && aTab.group.id === aGroup.id) {
@@ -6656,6 +6838,7 @@
@@ -6656,6 +6839,7 @@
let state = {
tabIndex: tab._tPos,
@@ -855,7 +865,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..3acfbc6d434340f0a629eec7edd9e186
};
if (tab.visible) {
state.elementIndex = tab.elementIndex;
@@ -6682,7 +6865,7 @@
@@ -6682,7 +6866,7 @@
let changedTabGroup =
previousTabState.tabGroupId != currentTabState.tabGroupId;
@@ -864,7 +874,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..3acfbc6d434340f0a629eec7edd9e186
tab.dispatchEvent(
new CustomEvent("TabMove", {
bubbles: true,
@@ -6723,6 +6906,10 @@
@@ -6723,6 +6907,10 @@
moveActionCallback();
@@ -875,7 +885,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..3acfbc6d434340f0a629eec7edd9e186
// Clear tabs cache after moving nodes because the order of tabs may have
// changed.
this.tabContainer._invalidateCachedTabs();
@@ -7623,7 +7810,7 @@
@@ -7623,7 +7811,7 @@
// preventDefault(). It will still raise the window if appropriate.
break;
}
@@ -884,7 +894,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..3acfbc6d434340f0a629eec7edd9e186
window.focus();
aEvent.preventDefault();
break;
@@ -7640,7 +7827,6 @@
@@ -7640,7 +7828,6 @@
}
case "TabGroupCollapse":
aEvent.target.tabs.forEach(tab => {
@@ -892,7 +902,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..3acfbc6d434340f0a629eec7edd9e186
});
break;
case "TabGroupCreateByUser":
@@ -8589,6 +8775,7 @@
@@ -8589,6 +8776,7 @@
aWebProgress.isTopLevel
) {
this.mTab.setAttribute("busy", "true");
@@ -900,7 +910,7 @@ index 42027bfa55eab8ea9298a7d425f2ded45188f7f3..3acfbc6d434340f0a629eec7edd9e186
gBrowser._tabAttrModified(this.mTab, ["busy"]);
this.mTab._notselectedsinceload = !this.mTab.selected;
}
@@ -9623,7 +9810,7 @@ var TabContextMenu = {
@@ -9623,7 +9811,7 @@ var TabContextMenu = {
);
contextUnpinSelectedTabs.hidden =
!this.contextTab.pinned || !this.multiselected;

View File

@@ -0,0 +1,22 @@
diff --git a/docshell/base/BrowsingContextWebProgress.cpp b/docshell/base/BrowsingContextWebProgress.cpp
index cc5bf6de7bf4875acf0c572549dc30ff06ac7f6f..4e0f3648f551fe9507a654d19f0a99e455418aa9 100644
--- a/docshell/base/BrowsingContextWebProgress.cpp
+++ b/docshell/base/BrowsingContextWebProgress.cpp
@@ -54,7 +54,7 @@ NS_IMETHODIMP BrowsingContextWebProgress::AddProgressListener(
if (mListenerInfoList.Contains(listener)) {
// The listener is already registered!
- return NS_ERROR_FAILURE;
+ return NS_OK;
}
mListenerInfoList.AppendElement(ListenerInfo(listener, aNotifyMask));
@@ -68,7 +68,7 @@ NS_IMETHODIMP BrowsingContextWebProgress::RemoveProgressListener(
return NS_ERROR_INVALID_ARG;
}
- return mListenerInfoList.RemoveElement(listener) ? NS_OK : NS_ERROR_FAILURE;
+ return mListenerInfoList.RemoveElement(listener) ? NS_OK : NS_OK;
}
NS_IMETHODIMP BrowsingContextWebProgress::GetBrowsingContextXPCOM(

View File

@@ -145,13 +145,19 @@ class nsZenSessionManager {
}
restoreNewWindow(aWindow, SessionStoreInternal) {
if (aWindow.gZenWorkspaces?.privateWindowOrDisabled) {
return;
}
lazy.SessionSaver.run().then(() => {
const state = lazy.SessionStore.getCurrentState(true);
const windows = state.windows || {};
let newWindow = Cu.cloneInto(windows[0], {});
delete newWindow.selected;
const newState = { windows: [newWindow] };
//SessionStoreInternal.restoreWindows(aWindow, newState, {});
aWindow._zenRestorePromise = new Promise((resolve) => {
SessionStoreInternal.restoreWindows(aWindow, newState, {});
resolve();
});
});
}
}

View File

@@ -80,7 +80,12 @@ class nsZenWindowSync {
#browserWindows = {
*[Symbol.iterator]() {
for (let window of lazy.BrowserWindowTracker.orderedWindows) {
if (window.__SSi && !window.closed && window.gZenStartup.isReady) {
if (
window.__SSi &&
!window.closed &&
window.gZenStartup.isReady &&
!window.gZenWorkspaces?.privateWindowOrDisabled
) {
yield window;
}
}
@@ -109,9 +114,23 @@ class nsZenWindowSync {
* @param {Window} aWindow - The browser window that is about to be shown.
*/
#onWindowBeforeShow(aWindow) {
// There are 2 possibilities to know if we are trying to open
// a new *unsynced* window:
// 1. We are passing `zen-unsynced` in the window arguments.
// 2. We are trying to open a link in a new window where other synced
// windows already exist
if (
aWindow.arguments.some((arg) => arg === 'zen-unsynced') ||
(typeof aWindow.arguments[0] === 'string' &&
aWindow.arguments.length > 1 &&
[...this.#browserWindows].length > 0)
) {
aWindow.document.documentElement.setAttribute('zen-unsynced-window', 'true');
return;
}
aWindow.gZenWindowSync = this;
for (let eventName of EVENTS) {
aWindow.addEventListener(eventName, this);
aWindow.addEventListener(eventName, this, true);
}
}
@@ -174,7 +193,7 @@ class nsZenWindowSync {
handleEvent(aEvent) {
const window = aEvent.currentTarget.ownerGlobal;
if (!window.gZenStartup.isReady) {
if (!window.gZenStartup.isReady || window.gZenWorkspaces?.privateWindowOrDisabled) {
return;
}
if (this.#eventHandlingContext.window && this.#eventHandlingContext.window !== window) {
@@ -619,7 +638,13 @@ class nsZenWindowSync {
this.onTabSwitchOrWindowFocus(aEvent.target.ownerGlobal, previousTab);
}
on_unload() {}
on_unload(aEvent) {
const window = aEvent.target.ownerGlobal;
for (let eventName of EVENTS) {
window.removeEventListener(eventName, this);
}
delete window.gZenWindowSync;
}
on_TabGroupCreate(aEvent) {
const tabGroup = aEvent.target;

View File

@@ -55,6 +55,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
this.promiseDBInitialized,
this.promisePinnedInitialized,
SessionStore.promiseAllWindowsRestored,
window._zenRestorePromise,
]);
}
@@ -835,8 +836,12 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
return PrivateBrowsingUtils.isWindowPrivate(window);
}
get currentWindowIsSyncing() {
return !document.documentElement.hasAttribute('zen-unsynced-window') && !this.isPrivateWindow;
}
get privateWindowOrDisabled() {
return this.isPrivateWindow || !this.shouldHaveWorkspaces;
return !this.shouldHaveWorkspaces || !this.currentWindowIsSyncing;
}
get workspaceEnabled() {
@@ -865,12 +870,12 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
return this._workspaceCache;
}
if (this.isPrivateWindow) {
if (!this.currentWindowIsSyncing) {
this._workspaceCache = {
workspaces: this._privateWorkspace ? [this._privateWorkspace] : [],
workspaces: this._tempWorkspace ? [this._tempWorkspace] : [],
lastChangeTimestamp: 0,
};
this._activeWorkspace = this._privateWorkspace?.uuid;
this._activeWorkspace = this._tempWorkspace?.uuid;
return this._workspaceCache;
}
@@ -1111,7 +1116,9 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
shouldCloseWindow() {
return (
!window.toolbar.visible || Services.prefs.getBoolPref('browser.tabs.closeWindowWithLastTab')
!window.toolbar.visible ||
Services.prefs.getBoolPref('browser.tabs.closeWindowWithLastTab') ||
this.privateWindowOrDisabled
);
}
@@ -1388,7 +1395,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
}
async _propagateWorkspaceData({ ignoreStrip = false, clearCache = true, onInit = false } = {}) {
const currentWindowIsPrivate = this.isPrivateWindow;
const currentWindowIsPrivate = !this.currentWindowIsSyncing;
if (onInit) {
if (currentWindowIsPrivate) return;
return await this._propagateWorkspaceDataForWindow(this.ownerWindow, {
@@ -1401,7 +1408,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
// For example, when the window is in private browsing mode.
if (
!browser.gZenWorkspaces.workspaceEnabled ||
browser.gZenWorkspaces.isPrivateWindow !== currentWindowIsPrivate
!browser.gZenWorkspaces.currentWindowIsSyncing !== currentWindowIsPrivate
) {
return;
}
@@ -2450,8 +2457,8 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
if (!this.workspaceEnabled) {
return;
}
if (this.isPrivateWindow) {
name = 'Private ' + name;
if (!this.currentWindowIsSyncing) {
name = this.isPrivateWindow ? 'Private ' + name : gZenUIManager.generateUuidv4();
}
// get extra tabs remaning (e.g. on new profiles) and just move them to the new workspace
const extraTabs = Array.from(gBrowser.tabContainer.arrowScrollbox.children).filter(
@@ -2468,8 +2475,8 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
!dontChange,
containerTabId
);
if (this.isPrivateWindow) {
this._privateWorkspace = workspaceData;
if (!this.currentWindowIsSyncing) {
this._tempWorkspace = workspaceData;
} else {
await this.saveWorkspace(workspaceData, dontChange);
}