mirror of
https://github.com/zen-browser/desktop.git
synced 2025-11-13 22:08:55 +00:00
Compare commits
25 Commits
stable
...
window-syn
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eefc8cb20c | ||
|
|
12c921fd87 | ||
|
|
e419c4dc9f | ||
|
|
68b37ac736 | ||
|
|
7f225ac3ee | ||
|
|
3e39ef2538 | ||
|
|
e5517eb164 | ||
|
|
c4dd470864 | ||
|
|
bf1b0dcd48 | ||
|
|
76acc8b0e4 | ||
|
|
1b83b77cad | ||
|
|
79ff574978 | ||
|
|
af20a65fa1 | ||
|
|
4a7f8fc9c0 | ||
|
|
a738a829de | ||
|
|
240a031e38 | ||
|
|
9bc7b9ce4e | ||
|
|
86006c8891 | ||
|
|
a55b1c7495 | ||
|
|
6e6337a95b | ||
|
|
6b12153c8a | ||
|
|
f6922ef2ba | ||
|
|
91f5d58fbc | ||
|
|
7a4cdaa45c | ||
|
|
81e854a89f |
@@ -36,7 +36,7 @@
|
|||||||
value: '@IS_TWILIGHT@'
|
value: '@IS_TWILIGHT@'
|
||||||
|
|
||||||
- name: zen.theme.hide-unified-extensions-button
|
- name: zen.theme.hide-unified-extensions-button
|
||||||
value: true
|
value: false
|
||||||
|
|
||||||
# ==== Mark: border radius ====
|
# ==== Mark: border radius ====
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,6 @@
|
|||||||
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenFolders.mjs"></script>
|
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenFolders.mjs"></script>
|
||||||
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenMods.mjs"></script>
|
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenMods.mjs"></script>
|
||||||
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenCompactMode.mjs"></script>
|
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenCompactMode.mjs"></script>
|
||||||
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenPinnedTabsStorage.mjs"></script>
|
|
||||||
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenWorkspacesStorage.mjs"></script>
|
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenWorkspacesStorage.mjs"></script>
|
||||||
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenPinnedTabManager.mjs"></script>
|
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenPinnedTabManager.mjs"></script>
|
||||||
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenGradientGenerator.mjs"></script>
|
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenGradientGenerator.mjs"></script>
|
||||||
@@ -58,3 +57,4 @@
|
|||||||
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenDownloadAnimation.mjs"></script>
|
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenDownloadAnimation.mjs"></script>
|
||||||
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenEmojiPicker.mjs"></script>
|
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenEmojiPicker.mjs"></script>
|
||||||
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenWorkspaceCreation.mjs"></script>
|
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenWorkspaceCreation.mjs"></script>
|
||||||
|
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenWindowSyncing.mjs"></script>
|
||||||
|
|||||||
@@ -41,6 +41,7 @@
|
|||||||
content/browser/zen-components/ZenWorkspaceIcons.mjs (../../zen/workspaces/ZenWorkspaceIcons.mjs)
|
content/browser/zen-components/ZenWorkspaceIcons.mjs (../../zen/workspaces/ZenWorkspaceIcons.mjs)
|
||||||
content/browser/zen-components/ZenWorkspace.mjs (../../zen/workspaces/ZenWorkspace.mjs)
|
content/browser/zen-components/ZenWorkspace.mjs (../../zen/workspaces/ZenWorkspace.mjs)
|
||||||
content/browser/zen-components/ZenWorkspaces.mjs (../../zen/workspaces/ZenWorkspaces.mjs)
|
content/browser/zen-components/ZenWorkspaces.mjs (../../zen/workspaces/ZenWorkspaces.mjs)
|
||||||
|
content/browser/zen-components/ZenWindowSyncing.mjs (../../zen/workspaces/ZenWindowSyncing.mjs)
|
||||||
content/browser/zen-components/ZenWorkspaceCreation.mjs (../../zen/workspaces/ZenWorkspaceCreation.mjs)
|
content/browser/zen-components/ZenWorkspaceCreation.mjs (../../zen/workspaces/ZenWorkspaceCreation.mjs)
|
||||||
content/browser/zen-components/ZenWorkspacesStorage.mjs (../../zen/workspaces/ZenWorkspacesStorage.mjs)
|
content/browser/zen-components/ZenWorkspacesStorage.mjs (../../zen/workspaces/ZenWorkspacesStorage.mjs)
|
||||||
content/browser/zen-components/ZenWorkspacesSync.mjs (../../zen/workspaces/ZenWorkspacesSync.mjs)
|
content/browser/zen-components/ZenWorkspacesSync.mjs (../../zen/workspaces/ZenWorkspacesSync.mjs)
|
||||||
@@ -50,7 +51,6 @@
|
|||||||
|
|
||||||
content/browser/zen-components/ZenKeyboardShortcuts.mjs (../../zen/kbs/ZenKeyboardShortcuts.mjs)
|
content/browser/zen-components/ZenKeyboardShortcuts.mjs (../../zen/kbs/ZenKeyboardShortcuts.mjs)
|
||||||
|
|
||||||
content/browser/zen-components/ZenPinnedTabsStorage.mjs (../../zen/tabs/ZenPinnedTabsStorage.mjs)
|
|
||||||
content/browser/zen-components/ZenPinnedTabManager.mjs (../../zen/tabs/ZenPinnedTabManager.mjs)
|
content/browser/zen-components/ZenPinnedTabManager.mjs (../../zen/tabs/ZenPinnedTabManager.mjs)
|
||||||
* content/browser/zen-styles/zen-tabs.css (../../zen/tabs/zen-tabs.css)
|
* content/browser/zen-styles/zen-tabs.css (../../zen/tabs/zen-tabs.css)
|
||||||
content/browser/zen-styles/zen-tabs/vertical-tabs.css (../../zen/tabs/zen-tabs/vertical-tabs.css)
|
content/browser/zen-styles/zen-tabs/vertical-tabs.css (../../zen/tabs/zen-tabs/vertical-tabs.css)
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
diff --git a/browser/components/sessionstore/SessionFile.sys.mjs b/browser/components/sessionstore/SessionFile.sys.mjs
|
||||||
|
index 31140cb8be3b529a0952ca8dc55165690b0e2120..605c9e0aa84da0a2d3171a0573e8cd95e27bd0c4 100644
|
||||||
|
--- a/browser/components/sessionstore/SessionFile.sys.mjs
|
||||||
|
+++ b/browser/components/sessionstore/SessionFile.sys.mjs
|
||||||
|
@@ -22,6 +22,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
|
||||||
|
RunState: "resource:///modules/sessionstore/RunState.sys.mjs",
|
||||||
|
SessionStore: "resource:///modules/sessionstore/SessionStore.sys.mjs",
|
||||||
|
SessionWriter: "resource:///modules/sessionstore/SessionWriter.sys.mjs",
|
||||||
|
+ ZenSessionStore: "resource:///modules/zen/ZenSessionManager.sys.mjs",
|
||||||
|
});
|
||||||
|
|
||||||
|
const PREF_UPGRADE_BACKUP = "browser.sessionstore.upgradeBackup.latestBuildID";
|
||||||
|
@@ -380,7 +381,7 @@ var SessionFileInternal = {
|
||||||
|
this._readOrigin = result.origin;
|
||||||
|
|
||||||
|
result.noFilesFound = noFilesFound;
|
||||||
|
-
|
||||||
|
+ await lazy.ZenSessionStore.readFile();
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
diff --git a/browser/components/sessionstore/SessionStartup.sys.mjs b/browser/components/sessionstore/SessionStartup.sys.mjs
|
||||||
|
index be23213ae9ec7e59358a17276c6c3764d38d9996..ca5a8ccc916ceeab5140f1278d15233cefbe5815 100644
|
||||||
|
--- a/browser/components/sessionstore/SessionStartup.sys.mjs
|
||||||
|
+++ b/browser/components/sessionstore/SessionStartup.sys.mjs
|
||||||
|
@@ -40,6 +40,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
|
||||||
|
StartupPerformance:
|
||||||
|
"resource:///modules/sessionstore/StartupPerformance.sys.mjs",
|
||||||
|
sessionStoreLogger: "resource:///modules/sessionstore/SessionLogger.sys.mjs",
|
||||||
|
+ ZenSessionStore: "resource:///modules/zen/ZenSessionManager.sys.mjs",
|
||||||
|
});
|
||||||
|
|
||||||
|
const STATE_RUNNING_STR = "running";
|
||||||
|
@@ -179,6 +180,8 @@ export var SessionStartup = {
|
||||||
|
this._initialState = parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ lazy.ZenSessionStore.onFileRead(this._initialState);
|
||||||
|
+
|
||||||
|
if (this._initialState == null) {
|
||||||
|
// No valid session found.
|
||||||
|
this._sessionType = this.NO_SESSION;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
diff --git a/browser/components/sessionstore/SessionStore.sys.mjs b/browser/components/sessionstore/SessionStore.sys.mjs
|
diff --git a/browser/components/sessionstore/SessionStore.sys.mjs b/browser/components/sessionstore/SessionStore.sys.mjs
|
||||||
index 1cdbc0f41bf5b55dfbbd850cb618c6d870f7a261..f13ed1412bb7ae6623aa2605d7691d10540eb659 100644
|
index 1cdbc0f41bf5b55dfbbd850cb618c6d870f7a261..7cd5b92c1b1ddbaea89de5e9627fc5bc3315aa98 100644
|
||||||
--- a/browser/components/sessionstore/SessionStore.sys.mjs
|
--- a/browser/components/sessionstore/SessionStore.sys.mjs
|
||||||
+++ b/browser/components/sessionstore/SessionStore.sys.mjs
|
+++ b/browser/components/sessionstore/SessionStore.sys.mjs
|
||||||
@@ -127,6 +127,8 @@ const TAB_EVENTS = [
|
@@ -127,6 +127,8 @@ const TAB_EVENTS = [
|
||||||
@@ -11,7 +11,15 @@ index 1cdbc0f41bf5b55dfbbd850cb618c6d870f7a261..f13ed1412bb7ae6623aa2605d7691d10
|
|||||||
];
|
];
|
||||||
|
|
||||||
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||||
@@ -1911,6 +1913,8 @@ var SessionStoreInternal = {
|
@@ -196,6 +198,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
|
||||||
|
TabStateCache: "resource:///modules/sessionstore/TabStateCache.sys.mjs",
|
||||||
|
TabStateFlusher: "resource:///modules/sessionstore/TabStateFlusher.sys.mjs",
|
||||||
|
setTimeout: "resource://gre/modules/Timer.sys.mjs",
|
||||||
|
+ ZenSessionStore: "resource:///modules/zen/ZenSessionManager.sys.mjs",
|
||||||
|
});
|
||||||
|
|
||||||
|
ChromeUtils.defineLazyGetter(lazy, "blankURI", () => {
|
||||||
|
@@ -1911,6 +1914,8 @@ var SessionStoreInternal = {
|
||||||
case "TabPinned":
|
case "TabPinned":
|
||||||
case "TabUnpinned":
|
case "TabUnpinned":
|
||||||
case "SwapDocShells":
|
case "SwapDocShells":
|
||||||
@@ -20,7 +28,7 @@ index 1cdbc0f41bf5b55dfbbd850cb618c6d870f7a261..f13ed1412bb7ae6623aa2605d7691d10
|
|||||||
this.saveStateDelayed(win);
|
this.saveStateDelayed(win);
|
||||||
break;
|
break;
|
||||||
case "TabGroupCreate":
|
case "TabGroupCreate":
|
||||||
@@ -2151,7 +2155,6 @@ var SessionStoreInternal = {
|
@@ -2151,7 +2156,6 @@ var SessionStoreInternal = {
|
||||||
if (closedWindowState) {
|
if (closedWindowState) {
|
||||||
let newWindowState;
|
let newWindowState;
|
||||||
if (
|
if (
|
||||||
@@ -28,7 +36,18 @@ index 1cdbc0f41bf5b55dfbbd850cb618c6d870f7a261..f13ed1412bb7ae6623aa2605d7691d10
|
|||||||
!lazy.SessionStartup.willRestore()
|
!lazy.SessionStartup.willRestore()
|
||||||
) {
|
) {
|
||||||
// We want to split the window up into pinned tabs and unpinned tabs.
|
// We want to split the window up into pinned tabs and unpinned tabs.
|
||||||
@@ -2384,11 +2387,9 @@ var SessionStoreInternal = {
|
@@ -2215,6 +2219,10 @@ var SessionStoreInternal = {
|
||||||
|
});
|
||||||
|
this._shouldRestoreLastSession = false;
|
||||||
|
}
|
||||||
|
+ else if (!aInitialState && isRegularWindow) {
|
||||||
|
+ aInitialState = lazy.ZenSessionStore.getNewWindowData();
|
||||||
|
+ this.restoreWindows(aWindow, aInitialState, {});
|
||||||
|
+ }
|
||||||
|
|
||||||
|
if (this._restoreLastWindow && aWindow.toolbar.visible) {
|
||||||
|
// always reset (if not a popup window)
|
||||||
|
@@ -2384,11 +2392,9 @@ var SessionStoreInternal = {
|
||||||
tabbrowser.selectedTab.label;
|
tabbrowser.selectedTab.label;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,7 +59,7 @@ index 1cdbc0f41bf5b55dfbbd850cb618c6d870f7a261..f13ed1412bb7ae6623aa2605d7691d10
|
|||||||
|
|
||||||
// Store the window's close date to figure out when each individual tab
|
// Store the window's close date to figure out when each individual tab
|
||||||
// was closed. This timestamp should allow re-arranging data based on how
|
// was closed. This timestamp should allow re-arranging data based on how
|
||||||
@@ -3373,7 +3374,7 @@ var SessionStoreInternal = {
|
@@ -3373,7 +3379,7 @@ var SessionStoreInternal = {
|
||||||
if (!isPrivateWindow && tabState.isPrivate) {
|
if (!isPrivateWindow && tabState.isPrivate) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -49,7 +68,7 @@ index 1cdbc0f41bf5b55dfbbd850cb618c6d870f7a261..f13ed1412bb7ae6623aa2605d7691d10
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4089,6 +4090,11 @@ var SessionStoreInternal = {
|
@@ -4089,6 +4095,11 @@ var SessionStoreInternal = {
|
||||||
Math.min(tabState.index, tabState.entries.length)
|
Math.min(tabState.index, tabState.entries.length)
|
||||||
);
|
);
|
||||||
tabState.pinned = false;
|
tabState.pinned = false;
|
||||||
@@ -61,7 +80,7 @@ index 1cdbc0f41bf5b55dfbbd850cb618c6d870f7a261..f13ed1412bb7ae6623aa2605d7691d10
|
|||||||
|
|
||||||
if (inBackground === false) {
|
if (inBackground === false) {
|
||||||
aWindow.gBrowser.selectedTab = newTab;
|
aWindow.gBrowser.selectedTab = newTab;
|
||||||
@@ -4525,6 +4531,7 @@ var SessionStoreInternal = {
|
@@ -4525,6 +4536,7 @@ var SessionStoreInternal = {
|
||||||
// Append the tab if we're opening into a different window,
|
// Append the tab if we're opening into a different window,
|
||||||
tabIndex: aSource == aTargetWindow ? pos : Infinity,
|
tabIndex: aSource == aTargetWindow ? pos : Infinity,
|
||||||
pinned: state.pinned,
|
pinned: state.pinned,
|
||||||
@@ -69,7 +88,7 @@ index 1cdbc0f41bf5b55dfbbd850cb618c6d870f7a261..f13ed1412bb7ae6623aa2605d7691d10
|
|||||||
userContextId: state.userContextId,
|
userContextId: state.userContextId,
|
||||||
skipLoad: true,
|
skipLoad: true,
|
||||||
preferredRemoteType,
|
preferredRemoteType,
|
||||||
@@ -5374,7 +5381,7 @@ var SessionStoreInternal = {
|
@@ -5374,7 +5386,7 @@ var SessionStoreInternal = {
|
||||||
|
|
||||||
for (let i = tabbrowser.pinnedTabCount; i < tabbrowser.tabs.length; i++) {
|
for (let i = tabbrowser.pinnedTabCount; i < tabbrowser.tabs.length; i++) {
|
||||||
let tab = tabbrowser.tabs[i];
|
let tab = tabbrowser.tabs[i];
|
||||||
@@ -78,7 +97,7 @@ index 1cdbc0f41bf5b55dfbbd850cb618c6d870f7a261..f13ed1412bb7ae6623aa2605d7691d10
|
|||||||
removableTabs.push(tab);
|
removableTabs.push(tab);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5434,7 +5441,7 @@ var SessionStoreInternal = {
|
@@ -5434,7 +5446,7 @@ var SessionStoreInternal = {
|
||||||
}
|
}
|
||||||
|
|
||||||
let workspaceID = aWindow.getWorkspaceID();
|
let workspaceID = aWindow.getWorkspaceID();
|
||||||
@@ -87,7 +106,7 @@ index 1cdbc0f41bf5b55dfbbd850cb618c6d870f7a261..f13ed1412bb7ae6623aa2605d7691d10
|
|||||||
winData.workspaceID = workspaceID;
|
winData.workspaceID = workspaceID;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -5625,11 +5632,12 @@ var SessionStoreInternal = {
|
@@ -5625,11 +5637,12 @@ var SessionStoreInternal = {
|
||||||
}
|
}
|
||||||
|
|
||||||
let tabbrowser = aWindow.gBrowser;
|
let tabbrowser = aWindow.gBrowser;
|
||||||
@@ -101,7 +120,7 @@ index 1cdbc0f41bf5b55dfbbd850cb618c6d870f7a261..f13ed1412bb7ae6623aa2605d7691d10
|
|||||||
// update the internal state data for this window
|
// update the internal state data for this window
|
||||||
for (let tab of tabs) {
|
for (let tab of tabs) {
|
||||||
if (tab == aWindow.FirefoxViewHandler.tab) {
|
if (tab == aWindow.FirefoxViewHandler.tab) {
|
||||||
@@ -5640,6 +5648,7 @@ var SessionStoreInternal = {
|
@@ -5640,6 +5653,7 @@ var SessionStoreInternal = {
|
||||||
tabsData.push(tabData);
|
tabsData.push(tabData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,7 +128,7 @@ index 1cdbc0f41bf5b55dfbbd850cb618c6d870f7a261..f13ed1412bb7ae6623aa2605d7691d10
|
|||||||
// update tab group state for this window
|
// update tab group state for this window
|
||||||
winData.groups = [];
|
winData.groups = [];
|
||||||
for (let tabGroup of aWindow.gBrowser.tabGroups) {
|
for (let tabGroup of aWindow.gBrowser.tabGroups) {
|
||||||
@@ -5652,7 +5661,7 @@ var SessionStoreInternal = {
|
@@ -5652,7 +5666,7 @@ var SessionStoreInternal = {
|
||||||
// a window is closed, point to the first item in the tab strip instead (it will never be the Firefox View tab,
|
// a window is closed, point to the first item in the tab strip instead (it will never be the Firefox View tab,
|
||||||
// since it's only inserted into the tab strip after it's selected).
|
// since it's only inserted into the tab strip after it's selected).
|
||||||
if (aWindow.FirefoxViewHandler.tab?.selected) {
|
if (aWindow.FirefoxViewHandler.tab?.selected) {
|
||||||
@@ -118,7 +137,7 @@ index 1cdbc0f41bf5b55dfbbd850cb618c6d870f7a261..f13ed1412bb7ae6623aa2605d7691d10
|
|||||||
winData.title = tabbrowser.tabs[0].label;
|
winData.title = tabbrowser.tabs[0].label;
|
||||||
}
|
}
|
||||||
winData.selected = selectedIndex;
|
winData.selected = selectedIndex;
|
||||||
@@ -5764,8 +5773,8 @@ var SessionStoreInternal = {
|
@@ -5764,8 +5778,8 @@ var SessionStoreInternal = {
|
||||||
// selectTab represents.
|
// selectTab represents.
|
||||||
let selectTab = 0;
|
let selectTab = 0;
|
||||||
if (overwriteTabs) {
|
if (overwriteTabs) {
|
||||||
@@ -129,7 +148,7 @@ index 1cdbc0f41bf5b55dfbbd850cb618c6d870f7a261..f13ed1412bb7ae6623aa2605d7691d10
|
|||||||
selectTab = Math.min(selectTab, winData.tabs.length);
|
selectTab = Math.min(selectTab, winData.tabs.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5808,6 +5817,8 @@ var SessionStoreInternal = {
|
@@ -5808,6 +5822,8 @@ var SessionStoreInternal = {
|
||||||
winData.tabs,
|
winData.tabs,
|
||||||
winData.groups ?? []
|
winData.groups ?? []
|
||||||
);
|
);
|
||||||
@@ -138,7 +157,7 @@ index 1cdbc0f41bf5b55dfbbd850cb618c6d870f7a261..f13ed1412bb7ae6623aa2605d7691d10
|
|||||||
this._log.debug(
|
this._log.debug(
|
||||||
`restoreWindow, createTabsForSessionRestore returned ${tabs.length} tabs`
|
`restoreWindow, createTabsForSessionRestore returned ${tabs.length} tabs`
|
||||||
);
|
);
|
||||||
@@ -6371,6 +6382,25 @@ var SessionStoreInternal = {
|
@@ -6371,6 +6387,25 @@ var SessionStoreInternal = {
|
||||||
|
|
||||||
// Most of tabData has been restored, now continue with restoring
|
// Most of tabData has been restored, now continue with restoring
|
||||||
// attributes that may trigger external events.
|
// attributes that may trigger external events.
|
||||||
@@ -152,8 +171,8 @@ index 1cdbc0f41bf5b55dfbbd850cb618c6d870f7a261..f13ed1412bb7ae6623aa2605d7691d10
|
|||||||
+ if (tabData.zenHasStaticLabel) {
|
+ if (tabData.zenHasStaticLabel) {
|
||||||
+ tab.setAttribute("zen-has-static-label", "true");
|
+ tab.setAttribute("zen-has-static-label", "true");
|
||||||
+ }
|
+ }
|
||||||
+ if (tabData.zenPinnedId) {
|
+ if (tabData.zenSyncId) {
|
||||||
+ tab.setAttribute("zen-pin-id", tabData.zenPinnedId);
|
+ tab.setAttribute("zen-sync-id", tabData.zenSyncId);
|
||||||
+ }
|
+ }
|
||||||
+ if (tabData.zenDefaultUserContextId) {
|
+ if (tabData.zenDefaultUserContextId) {
|
||||||
+ tab.setAttribute("zenDefaultUserContextId", true);
|
+ tab.setAttribute("zenDefaultUserContextId", true);
|
||||||
@@ -164,7 +183,7 @@ index 1cdbc0f41bf5b55dfbbd850cb618c6d870f7a261..f13ed1412bb7ae6623aa2605d7691d10
|
|||||||
|
|
||||||
if (tabData.pinned) {
|
if (tabData.pinned) {
|
||||||
tabbrowser.pinTab(tab);
|
tabbrowser.pinTab(tab);
|
||||||
@@ -7289,7 +7319,7 @@ var SessionStoreInternal = {
|
@@ -7289,7 +7324,7 @@ var SessionStoreInternal = {
|
||||||
|
|
||||||
let groupsToSave = new Map();
|
let groupsToSave = new Map();
|
||||||
for (let tIndex = 0; tIndex < window.tabs.length; ) {
|
for (let tIndex = 0; tIndex < window.tabs.length; ) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
diff --git a/browser/components/sessionstore/TabState.sys.mjs b/browser/components/sessionstore/TabState.sys.mjs
|
diff --git a/browser/components/sessionstore/TabState.sys.mjs b/browser/components/sessionstore/TabState.sys.mjs
|
||||||
index 82721356d191055bec0d4b0ca49e481221988801..1ea5c394c704da295149443d7794961a12f2060b 100644
|
index 82721356d191055bec0d4b0ca49e481221988801..d1323fe17c995611ebdfe2869b0ccd2d45bcfa11 100644
|
||||||
--- a/browser/components/sessionstore/TabState.sys.mjs
|
--- a/browser/components/sessionstore/TabState.sys.mjs
|
||||||
+++ b/browser/components/sessionstore/TabState.sys.mjs
|
+++ b/browser/components/sessionstore/TabState.sys.mjs
|
||||||
@@ -85,7 +85,22 @@ class _TabState {
|
@@ -85,7 +85,22 @@ class _TabState {
|
||||||
@@ -7,7 +7,7 @@ index 82721356d191055bec0d4b0ca49e481221988801..1ea5c394c704da295149443d7794961a
|
|||||||
}
|
}
|
||||||
|
|
||||||
+ tabData.zenWorkspace = tab.getAttribute("zen-workspace-id");
|
+ tabData.zenWorkspace = tab.getAttribute("zen-workspace-id");
|
||||||
+ tabData.zenPinnedId = tab.getAttribute("zen-pin-id");
|
+ tabData.zenSyncId = tab.getAttribute("zen-sync-id");
|
||||||
+ tabData.zenEssential = tab.getAttribute("zen-essential");
|
+ tabData.zenEssential = tab.getAttribute("zen-essential");
|
||||||
+ tabData.pinned = tabData.pinned || tabData.zenEssential;
|
+ tabData.pinned = tabData.pinned || tabData.zenEssential;
|
||||||
+ tabData.zenDefaultUserContextId = tab.getAttribute("zenDefaultUserContextId");
|
+ tabData.zenDefaultUserContextId = tab.getAttribute("zenDefaultUserContextId");
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
diff --git a/browser/components/tabbrowser/content/drag-and-drop.js b/browser/components/tabbrowser/content/drag-and-drop.js
|
diff --git a/browser/components/tabbrowser/content/drag-and-drop.js b/browser/components/tabbrowser/content/drag-and-drop.js
|
||||||
index c97c661656868bc7faaf7c66c9702eea056aafe1..3a2b4bd1a017697539a3b81432199812307b9a04 100644
|
index c97c661656868bc7faaf7c66c9702eea056aafe1..aa9f0ed071e4898cac2b98a4d81e95df6a41b8dd 100644
|
||||||
--- a/browser/components/tabbrowser/content/drag-and-drop.js
|
--- a/browser/components/tabbrowser/content/drag-and-drop.js
|
||||||
+++ b/browser/components/tabbrowser/content/drag-and-drop.js
|
+++ b/browser/components/tabbrowser/content/drag-and-drop.js
|
||||||
@@ -32,6 +32,9 @@
|
@@ -32,6 +32,9 @@
|
||||||
@@ -149,8 +149,8 @@ index c97c661656868bc7faaf7c66c9702eea056aafe1..3a2b4bd1a017697539a3b81432199812
|
|||||||
this.#tabbrowserTabs.verticalMode &&
|
this.#tabbrowserTabs.verticalMode &&
|
||||||
- tab.pinned &&
|
- tab.pinned &&
|
||||||
- this.#tabbrowserTabs.hasAttribute("expanded") &&
|
- this.#tabbrowserTabs.hasAttribute("expanded") &&
|
||||||
+ tab.hasAttribute("zen-essential") &&
|
+ (tab.hasAttribute("zen-essential")) &&
|
||||||
+ (this.#tabbrowserTabs.hasAttribute("expanded") || document.documentElement.hasAttribute("zen-sidebar-expanded")) &&
|
+ (this.hasAttribute("expanded") || document.documentElement.hasAttribute("zen-sidebar-expanded")) &&
|
||||||
!this.#tabbrowserTabs.expandOnHover
|
!this.#tabbrowserTabs.expandOnHover
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
diff --git a/browser/components/tabbrowser/content/tab.js b/browser/components/tabbrowser/content/tab.js
|
diff --git a/browser/components/tabbrowser/content/tab.js b/browser/components/tabbrowser/content/tab.js
|
||||||
index 4c1a48424316b29d27ae2bc8b64004df41c87bb6..f1ff9bf0947127a8e9115357cedac577b5fad08c 100644
|
index 4c1a48424316b29d27ae2bc8b64004df41c87bb6..ce54ed0c8a93d5521a436c55c9432c090b0420ac 100644
|
||||||
--- a/browser/components/tabbrowser/content/tab.js
|
--- a/browser/components/tabbrowser/content/tab.js
|
||||||
+++ b/browser/components/tabbrowser/content/tab.js
|
+++ b/browser/components/tabbrowser/content/tab.js
|
||||||
@@ -21,6 +21,7 @@
|
@@ -21,6 +21,7 @@
|
||||||
@@ -121,15 +121,7 @@ index 4c1a48424316b29d27ae2bc8b64004df41c87bb6..f1ff9bf0947127a8e9115357cedac577
|
|||||||
on_click(event) {
|
on_click(event) {
|
||||||
if (event.button != 0) {
|
if (event.button != 0) {
|
||||||
return;
|
return;
|
||||||
@@ -572,6 +594,7 @@
|
@@ -584,6 +606,14 @@
|
||||||
)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
+ gZenPinnedTabManager._removePinnedAttributes(this, true);
|
|
||||||
gBrowser.removeTab(this, {
|
|
||||||
animate: true,
|
|
||||||
triggeringEvent: event,
|
|
||||||
@@ -584,6 +607,14 @@
|
|
||||||
// (see tabbrowser-tabs 'click' handler).
|
// (see tabbrowser-tabs 'click' handler).
|
||||||
gBrowser.tabContainer._blockDblClick = true;
|
gBrowser.tabContainer._blockDblClick = true;
|
||||||
}
|
}
|
||||||
@@ -144,7 +136,7 @@ index 4c1a48424316b29d27ae2bc8b64004df41c87bb6..f1ff9bf0947127a8e9115357cedac577
|
|||||||
}
|
}
|
||||||
|
|
||||||
on_dblclick(event) {
|
on_dblclick(event) {
|
||||||
@@ -607,6 +638,8 @@
|
@@ -607,6 +637,8 @@
|
||||||
animate: true,
|
animate: true,
|
||||||
triggeringEvent: event,
|
triggeringEvent: event,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/components/tabbrowser/content/tabbrowser.js
|
diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/components/tabbrowser/content/tabbrowser.js
|
||||||
index c0eafd4faf8d57b8486c5bf8917375850ec8147e..2ab3908f421d6bc126eb7a0f886646ad0c761fb5 100644
|
index c0eafd4faf8d57b8486c5bf8917375850ec8147e..30c8fd7e978eb3036f35b17ae3f6ea4cd44d980e 100644
|
||||||
--- a/browser/components/tabbrowser/content/tabbrowser.js
|
--- a/browser/components/tabbrowser/content/tabbrowser.js
|
||||||
+++ b/browser/components/tabbrowser/content/tabbrowser.js
|
+++ b/browser/components/tabbrowser/content/tabbrowser.js
|
||||||
@@ -450,15 +450,64 @@
|
@@ -450,15 +450,64 @@
|
||||||
@@ -420,10 +420,10 @@ index c0eafd4faf8d57b8486c5bf8917375850ec8147e..2ab3908f421d6bc126eb7a0f886646ad
|
|||||||
+ gZenWorkspaces._initialTab._shouldRemove = true;
|
+ gZenWorkspaces._initialTab._shouldRemove = true;
|
||||||
+ }
|
+ }
|
||||||
+ }
|
+ }
|
||||||
}
|
+ }
|
||||||
+ else {
|
+ else {
|
||||||
+ gZenWorkspaces._tabToRemoveForEmpty = this.selectedTab;
|
+ gZenWorkspaces._tabToRemoveForEmpty = this.selectedTab;
|
||||||
+ }
|
}
|
||||||
+ this._hasAlreadyInitializedZenSessionStore = true;
|
+ this._hasAlreadyInitializedZenSessionStore = true;
|
||||||
|
|
||||||
if (tabs.length > 1 || !tabs[0].selected) {
|
if (tabs.length > 1 || !tabs[0].selected) {
|
||||||
@@ -498,17 +498,7 @@ index c0eafd4faf8d57b8486c5bf8917375850ec8147e..2ab3908f421d6bc126eb7a0f886646ad
|
|||||||
|
|
||||||
TabBarVisibility.update();
|
TabBarVisibility.update();
|
||||||
}
|
}
|
||||||
@@ -4635,6 +4763,9 @@
|
@@ -4896,6 +5024,7 @@
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ for (let tab of selectedTabs) {
|
|
||||||
+ gZenPinnedTabManager._removePinnedAttributes(tab, true);
|
|
||||||
+ }
|
|
||||||
this.removeTabs(selectedTabs, { isUserTriggered, telemetrySource });
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -4896,6 +5027,7 @@
|
|
||||||
telemetrySource,
|
telemetrySource,
|
||||||
} = {}
|
} = {}
|
||||||
) {
|
) {
|
||||||
@@ -516,7 +506,7 @@ index c0eafd4faf8d57b8486c5bf8917375850ec8147e..2ab3908f421d6bc126eb7a0f886646ad
|
|||||||
// When 'closeWindowWithLastTab' pref is enabled, closing all tabs
|
// When 'closeWindowWithLastTab' pref is enabled, closing all tabs
|
||||||
// can be considered equivalent to closing the window.
|
// can be considered equivalent to closing the window.
|
||||||
if (
|
if (
|
||||||
@@ -4985,6 +5117,7 @@
|
@@ -4985,6 +5114,7 @@
|
||||||
if (lastToClose) {
|
if (lastToClose) {
|
||||||
this.removeTab(lastToClose, aParams);
|
this.removeTab(lastToClose, aParams);
|
||||||
}
|
}
|
||||||
@@ -524,7 +514,7 @@ index c0eafd4faf8d57b8486c5bf8917375850ec8147e..2ab3908f421d6bc126eb7a0f886646ad
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
@@ -5023,6 +5156,12 @@
|
@@ -5023,6 +5153,12 @@
|
||||||
aTab._closeTimeNoAnimTimerId = Glean.browserTabclose.timeNoAnim.start();
|
aTab._closeTimeNoAnimTimerId = Glean.browserTabclose.timeNoAnim.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -537,7 +527,7 @@ index c0eafd4faf8d57b8486c5bf8917375850ec8147e..2ab3908f421d6bc126eb7a0f886646ad
|
|||||||
// Handle requests for synchronously removing an already
|
// Handle requests for synchronously removing an already
|
||||||
// asynchronously closing tab.
|
// asynchronously closing tab.
|
||||||
if (!animate && aTab.closing) {
|
if (!animate && aTab.closing) {
|
||||||
@@ -5037,6 +5176,9 @@
|
@@ -5037,6 +5173,9 @@
|
||||||
// state).
|
// state).
|
||||||
let tabWidth = window.windowUtils.getBoundsWithoutFlushing(aTab).width;
|
let tabWidth = window.windowUtils.getBoundsWithoutFlushing(aTab).width;
|
||||||
let isLastTab = this.#isLastTabInWindow(aTab);
|
let isLastTab = this.#isLastTabInWindow(aTab);
|
||||||
@@ -547,7 +537,7 @@ index c0eafd4faf8d57b8486c5bf8917375850ec8147e..2ab3908f421d6bc126eb7a0f886646ad
|
|||||||
if (
|
if (
|
||||||
!this._beginRemoveTab(aTab, {
|
!this._beginRemoveTab(aTab, {
|
||||||
closeWindowFastpath: true,
|
closeWindowFastpath: true,
|
||||||
@@ -5085,7 +5227,13 @@
|
@@ -5085,7 +5224,13 @@
|
||||||
// We're not animating, so we can cancel the animation stopwatch.
|
// We're not animating, so we can cancel the animation stopwatch.
|
||||||
Glean.browserTabclose.timeAnim.cancel(aTab._closeTimeAnimTimerId);
|
Glean.browserTabclose.timeAnim.cancel(aTab._closeTimeAnimTimerId);
|
||||||
aTab._closeTimeAnimTimerId = null;
|
aTab._closeTimeAnimTimerId = null;
|
||||||
@@ -562,7 +552,7 @@ index c0eafd4faf8d57b8486c5bf8917375850ec8147e..2ab3908f421d6bc126eb7a0f886646ad
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5219,7 +5367,7 @@
|
@@ -5219,7 +5364,7 @@
|
||||||
closeWindowWithLastTab != null
|
closeWindowWithLastTab != null
|
||||||
? closeWindowWithLastTab
|
? closeWindowWithLastTab
|
||||||
: !window.toolbar.visible ||
|
: !window.toolbar.visible ||
|
||||||
@@ -571,7 +561,7 @@ index c0eafd4faf8d57b8486c5bf8917375850ec8147e..2ab3908f421d6bc126eb7a0f886646ad
|
|||||||
|
|
||||||
if (closeWindow) {
|
if (closeWindow) {
|
||||||
// We've already called beforeunload on all the relevant tabs if we get here,
|
// We've already called beforeunload on all the relevant tabs if we get here,
|
||||||
@@ -5243,6 +5391,7 @@
|
@@ -5243,6 +5388,7 @@
|
||||||
|
|
||||||
newTab = true;
|
newTab = true;
|
||||||
}
|
}
|
||||||
@@ -579,7 +569,7 @@ index c0eafd4faf8d57b8486c5bf8917375850ec8147e..2ab3908f421d6bc126eb7a0f886646ad
|
|||||||
aTab._endRemoveArgs = [closeWindow, newTab];
|
aTab._endRemoveArgs = [closeWindow, newTab];
|
||||||
|
|
||||||
// swapBrowsersAndCloseOther will take care of closing the window without animation.
|
// swapBrowsersAndCloseOther will take care of closing the window without animation.
|
||||||
@@ -5283,13 +5432,7 @@
|
@@ -5283,13 +5429,7 @@
|
||||||
aTab._mouseleave();
|
aTab._mouseleave();
|
||||||
|
|
||||||
if (newTab) {
|
if (newTab) {
|
||||||
@@ -594,7 +584,7 @@ index c0eafd4faf8d57b8486c5bf8917375850ec8147e..2ab3908f421d6bc126eb7a0f886646ad
|
|||||||
} else {
|
} else {
|
||||||
TabBarVisibility.update();
|
TabBarVisibility.update();
|
||||||
}
|
}
|
||||||
@@ -5422,6 +5565,7 @@
|
@@ -5422,6 +5562,7 @@
|
||||||
this.tabs[i]._tPos = i;
|
this.tabs[i]._tPos = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -602,7 +592,7 @@ index c0eafd4faf8d57b8486c5bf8917375850ec8147e..2ab3908f421d6bc126eb7a0f886646ad
|
|||||||
if (!this._windowIsClosing) {
|
if (!this._windowIsClosing) {
|
||||||
// update tab close buttons state
|
// update tab close buttons state
|
||||||
this.tabContainer._updateCloseButtons();
|
this.tabContainer._updateCloseButtons();
|
||||||
@@ -5643,6 +5787,7 @@
|
@@ -5643,6 +5784,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
let excludeTabs = new Set(aExcludeTabs);
|
let excludeTabs = new Set(aExcludeTabs);
|
||||||
@@ -610,7 +600,7 @@ index c0eafd4faf8d57b8486c5bf8917375850ec8147e..2ab3908f421d6bc126eb7a0f886646ad
|
|||||||
|
|
||||||
// If this tab has a successor, it should be selectable, since
|
// If this tab has a successor, it should be selectable, since
|
||||||
// hiding or closing a tab removes that tab as a successor.
|
// hiding or closing a tab removes that tab as a successor.
|
||||||
@@ -5655,13 +5800,13 @@
|
@@ -5655,13 +5797,13 @@
|
||||||
!excludeTabs.has(aTab.owner) &&
|
!excludeTabs.has(aTab.owner) &&
|
||||||
Services.prefs.getBoolPref("browser.tabs.selectOwnerOnClose")
|
Services.prefs.getBoolPref("browser.tabs.selectOwnerOnClose")
|
||||||
) {
|
) {
|
||||||
@@ -626,7 +616,7 @@ index c0eafd4faf8d57b8486c5bf8917375850ec8147e..2ab3908f421d6bc126eb7a0f886646ad
|
|||||||
);
|
);
|
||||||
|
|
||||||
let tab = this.tabContainer.findNextTab(aTab, {
|
let tab = this.tabContainer.findNextTab(aTab, {
|
||||||
@@ -5677,7 +5822,7 @@
|
@@ -5677,7 +5819,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tab) {
|
if (tab) {
|
||||||
@@ -635,7 +625,7 @@ index c0eafd4faf8d57b8486c5bf8917375850ec8147e..2ab3908f421d6bc126eb7a0f886646ad
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If no qualifying visible tab was found, see if there is a tab in
|
// If no qualifying visible tab was found, see if there is a tab in
|
||||||
@@ -5698,7 +5843,7 @@
|
@@ -5698,7 +5840,7 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -644,7 +634,7 @@ index c0eafd4faf8d57b8486c5bf8917375850ec8147e..2ab3908f421d6bc126eb7a0f886646ad
|
|||||||
}
|
}
|
||||||
|
|
||||||
_blurTab(aTab) {
|
_blurTab(aTab) {
|
||||||
@@ -6104,10 +6249,10 @@
|
@@ -6104,10 +6246,10 @@
|
||||||
SessionStore.deleteCustomTabValue(aTab, "hiddenBy");
|
SessionStore.deleteCustomTabValue(aTab, "hiddenBy");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -657,7 +647,7 @@ index c0eafd4faf8d57b8486c5bf8917375850ec8147e..2ab3908f421d6bc126eb7a0f886646ad
|
|||||||
aTab.selected ||
|
aTab.selected ||
|
||||||
aTab.closing ||
|
aTab.closing ||
|
||||||
// Tabs that are sharing the screen, microphone or camera cannot be hidden.
|
// Tabs that are sharing the screen, microphone or camera cannot be hidden.
|
||||||
@@ -6166,6 +6311,7 @@
|
@@ -6166,6 +6308,7 @@
|
||||||
* @param {MozTabbrowserTab|MozTabbrowserTabGroup|MozTabbrowserTabGroup.labelElement} aTab
|
* @param {MozTabbrowserTab|MozTabbrowserTabGroup|MozTabbrowserTabGroup.labelElement} aTab
|
||||||
*/
|
*/
|
||||||
replaceTabWithWindow(aTab, aOptions) {
|
replaceTabWithWindow(aTab, aOptions) {
|
||||||
@@ -665,7 +655,7 @@ index c0eafd4faf8d57b8486c5bf8917375850ec8147e..2ab3908f421d6bc126eb7a0f886646ad
|
|||||||
if (this.tabs.length == 1) {
|
if (this.tabs.length == 1) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -6299,7 +6445,7 @@
|
@@ -6299,7 +6442,7 @@
|
||||||
* `true` if element is a `<tab-group>`
|
* `true` if element is a `<tab-group>`
|
||||||
*/
|
*/
|
||||||
isTabGroup(element) {
|
isTabGroup(element) {
|
||||||
@@ -674,7 +664,7 @@ index c0eafd4faf8d57b8486c5bf8917375850ec8147e..2ab3908f421d6bc126eb7a0f886646ad
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -6375,8 +6521,8 @@
|
@@ -6375,8 +6518,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't allow mixing pinned and unpinned tabs.
|
// Don't allow mixing pinned and unpinned tabs.
|
||||||
@@ -685,7 +675,7 @@ index c0eafd4faf8d57b8486c5bf8917375850ec8147e..2ab3908f421d6bc126eb7a0f886646ad
|
|||||||
} else {
|
} else {
|
||||||
tabIndex = Math.max(tabIndex, this.pinnedTabCount);
|
tabIndex = Math.max(tabIndex, this.pinnedTabCount);
|
||||||
}
|
}
|
||||||
@@ -6402,10 +6548,16 @@
|
@@ -6402,10 +6545,16 @@
|
||||||
this.#handleTabMove(
|
this.#handleTabMove(
|
||||||
element,
|
element,
|
||||||
() => {
|
() => {
|
||||||
@@ -704,7 +694,7 @@ index c0eafd4faf8d57b8486c5bf8917375850ec8147e..2ab3908f421d6bc126eb7a0f886646ad
|
|||||||
if (neighbor && this.isTab(element) && tabIndex > element._tPos) {
|
if (neighbor && this.isTab(element) && tabIndex > element._tPos) {
|
||||||
neighbor.after(element);
|
neighbor.after(element);
|
||||||
} else {
|
} else {
|
||||||
@@ -6463,23 +6615,28 @@
|
@@ -6463,23 +6612,28 @@
|
||||||
#moveTabNextTo(element, targetElement, moveBefore = false, metricsContext) {
|
#moveTabNextTo(element, targetElement, moveBefore = false, metricsContext) {
|
||||||
if (this.isTabGroupLabel(targetElement)) {
|
if (this.isTabGroupLabel(targetElement)) {
|
||||||
targetElement = targetElement.group;
|
targetElement = targetElement.group;
|
||||||
@@ -739,7 +729,7 @@ index c0eafd4faf8d57b8486c5bf8917375850ec8147e..2ab3908f421d6bc126eb7a0f886646ad
|
|||||||
} else if (!element.pinned && targetElement && targetElement.pinned) {
|
} else if (!element.pinned && targetElement && targetElement.pinned) {
|
||||||
// If the caller asks to move an unpinned element next to a 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
|
// tab, move the unpinned element to be the first unpinned element
|
||||||
@@ -6492,14 +6649,34 @@
|
@@ -6492,14 +6646,34 @@
|
||||||
// move the tab group right before the first unpinned tab.
|
// move the tab group right before the first unpinned tab.
|
||||||
// 4. Moving a tab group and the first unpinned tab is grouped:
|
// 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.
|
// move the tab group right before the first unpinned tab's tab group.
|
||||||
@@ -775,7 +765,7 @@ index c0eafd4faf8d57b8486c5bf8917375850ec8147e..2ab3908f421d6bc126eb7a0f886646ad
|
|||||||
element.pinned
|
element.pinned
|
||||||
? this.tabContainer.pinnedTabsContainer
|
? this.tabContainer.pinnedTabsContainer
|
||||||
: this.tabContainer;
|
: this.tabContainer;
|
||||||
@@ -6508,7 +6685,7 @@
|
@@ -6508,7 +6682,7 @@
|
||||||
element,
|
element,
|
||||||
() => {
|
() => {
|
||||||
if (moveBefore) {
|
if (moveBefore) {
|
||||||
@@ -784,7 +774,7 @@ index c0eafd4faf8d57b8486c5bf8917375850ec8147e..2ab3908f421d6bc126eb7a0f886646ad
|
|||||||
} else if (targetElement) {
|
} else if (targetElement) {
|
||||||
targetElement.after(element);
|
targetElement.after(element);
|
||||||
} else {
|
} else {
|
||||||
@@ -6580,10 +6757,10 @@
|
@@ -6580,10 +6754,10 @@
|
||||||
* @param {TabMetricsContext} [metricsContext]
|
* @param {TabMetricsContext} [metricsContext]
|
||||||
*/
|
*/
|
||||||
moveTabToGroup(aTab, aGroup, metricsContext) {
|
moveTabToGroup(aTab, aGroup, metricsContext) {
|
||||||
@@ -797,7 +787,7 @@ index c0eafd4faf8d57b8486c5bf8917375850ec8147e..2ab3908f421d6bc126eb7a0f886646ad
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (aTab.group && aTab.group.id === aGroup.id) {
|
if (aTab.group && aTab.group.id === aGroup.id) {
|
||||||
@@ -6613,6 +6790,7 @@
|
@@ -6613,6 +6787,7 @@
|
||||||
|
|
||||||
let state = {
|
let state = {
|
||||||
tabIndex: tab._tPos,
|
tabIndex: tab._tPos,
|
||||||
@@ -805,7 +795,7 @@ index c0eafd4faf8d57b8486c5bf8917375850ec8147e..2ab3908f421d6bc126eb7a0f886646ad
|
|||||||
};
|
};
|
||||||
if (tab.visible) {
|
if (tab.visible) {
|
||||||
state.elementIndex = tab.elementIndex;
|
state.elementIndex = tab.elementIndex;
|
||||||
@@ -6639,7 +6817,7 @@
|
@@ -6639,7 +6814,7 @@
|
||||||
let changedTabGroup =
|
let changedTabGroup =
|
||||||
previousTabState.tabGroupId != currentTabState.tabGroupId;
|
previousTabState.tabGroupId != currentTabState.tabGroupId;
|
||||||
|
|
||||||
@@ -814,7 +804,7 @@ index c0eafd4faf8d57b8486c5bf8917375850ec8147e..2ab3908f421d6bc126eb7a0f886646ad
|
|||||||
tab.dispatchEvent(
|
tab.dispatchEvent(
|
||||||
new CustomEvent("TabMove", {
|
new CustomEvent("TabMove", {
|
||||||
bubbles: true,
|
bubbles: true,
|
||||||
@@ -6676,6 +6854,10 @@
|
@@ -6676,6 +6851,10 @@
|
||||||
|
|
||||||
moveActionCallback();
|
moveActionCallback();
|
||||||
|
|
||||||
@@ -825,7 +815,7 @@ index c0eafd4faf8d57b8486c5bf8917375850ec8147e..2ab3908f421d6bc126eb7a0f886646ad
|
|||||||
// Clear tabs cache after moving nodes because the order of tabs may have
|
// Clear tabs cache after moving nodes because the order of tabs may have
|
||||||
// changed.
|
// changed.
|
||||||
this.tabContainer._invalidateCachedTabs();
|
this.tabContainer._invalidateCachedTabs();
|
||||||
@@ -7576,7 +7758,7 @@
|
@@ -7576,7 +7755,7 @@
|
||||||
// preventDefault(). It will still raise the window if appropriate.
|
// preventDefault(). It will still raise the window if appropriate.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -834,7 +824,7 @@ index c0eafd4faf8d57b8486c5bf8917375850ec8147e..2ab3908f421d6bc126eb7a0f886646ad
|
|||||||
window.focus();
|
window.focus();
|
||||||
aEvent.preventDefault();
|
aEvent.preventDefault();
|
||||||
break;
|
break;
|
||||||
@@ -7593,7 +7775,6 @@
|
@@ -7593,7 +7772,6 @@
|
||||||
}
|
}
|
||||||
case "TabGroupCollapse":
|
case "TabGroupCollapse":
|
||||||
aEvent.target.tabs.forEach(tab => {
|
aEvent.target.tabs.forEach(tab => {
|
||||||
@@ -842,7 +832,7 @@ index c0eafd4faf8d57b8486c5bf8917375850ec8147e..2ab3908f421d6bc126eb7a0f886646ad
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case "TabGroupCreateByUser":
|
case "TabGroupCreateByUser":
|
||||||
@@ -8542,6 +8723,7 @@
|
@@ -8542,6 +8720,7 @@
|
||||||
aWebProgress.isTopLevel
|
aWebProgress.isTopLevel
|
||||||
) {
|
) {
|
||||||
this.mTab.setAttribute("busy", "true");
|
this.mTab.setAttribute("busy", "true");
|
||||||
@@ -850,7 +840,7 @@ index c0eafd4faf8d57b8486c5bf8917375850ec8147e..2ab3908f421d6bc126eb7a0f886646ad
|
|||||||
gBrowser._tabAttrModified(this.mTab, ["busy"]);
|
gBrowser._tabAttrModified(this.mTab, ["busy"]);
|
||||||
this.mTab._notselectedsinceload = !this.mTab.selected;
|
this.mTab._notselectedsinceload = !this.mTab.selected;
|
||||||
}
|
}
|
||||||
@@ -9543,7 +9725,7 @@ var TabContextMenu = {
|
@@ -9543,7 +9722,7 @@ var TabContextMenu = {
|
||||||
);
|
);
|
||||||
contextUnpinSelectedTabs.hidden =
|
contextUnpinSelectedTabs.hidden =
|
||||||
!this.contextTab.pinned || !this.multiselected;
|
!this.contextTab.pinned || !this.multiselected;
|
||||||
@@ -859,11 +849,3 @@ index c0eafd4faf8d57b8486c5bf8917375850ec8147e..2ab3908f421d6bc126eb7a0f886646ad
|
|||||||
// Build Ask Chat items
|
// Build Ask Chat items
|
||||||
TabContextMenu.GenAI.buildTabMenu(
|
TabContextMenu.GenAI.buildTabMenu(
|
||||||
document.getElementById("context_askChat"),
|
document.getElementById("context_askChat"),
|
||||||
@@ -9863,6 +10045,7 @@ var TabContextMenu = {
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
+ gZenPinnedTabManager._removePinnedAttributes(this.contextTab, true);
|
|
||||||
gBrowser.removeTab(this.contextTab, {
|
|
||||||
animate: true,
|
|
||||||
...gBrowser.TabMetrics.userTriggeredContext(
|
|
||||||
|
|||||||
@@ -13,3 +13,4 @@
|
|||||||
category app-startup nsBrowserGlue @mozilla.org/browser/browserglue;1 application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
|
category app-startup nsBrowserGlue @mozilla.org/browser/browserglue;1 application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
|
||||||
|
|
||||||
#include common/Components.manifest
|
#include common/Components.manifest
|
||||||
|
#include sessionstore/SessionComponents.manifest
|
||||||
|
|||||||
@@ -15,8 +15,8 @@
|
|||||||
if (tabData.zenWorkspace) {
|
if (tabData.zenWorkspace) {
|
||||||
tab.setAttribute('zen-workspace-id', tabData.zenWorkspace);
|
tab.setAttribute('zen-workspace-id', tabData.zenWorkspace);
|
||||||
}
|
}
|
||||||
if (tabData.zenPinnedId) {
|
if (tabData.zenSyncId) {
|
||||||
tab.setAttribute('zen-pin-id', tabData.zenPinnedId);
|
tab.setAttribute('zen-sync-id', tabData.zenSyncId);
|
||||||
}
|
}
|
||||||
if (tabData.zenHasStaticLabel) {
|
if (tabData.zenHasStaticLabel) {
|
||||||
tab.setAttribute('zen-has-static-label', 'true');
|
tab.setAttribute('zen-has-static-label', 'true');
|
||||||
|
|||||||
@@ -1303,14 +1303,6 @@ var gZenVerticalTabsManager = {
|
|||||||
} else {
|
} else {
|
||||||
gBrowser.setTabTitle(this._tabEdited);
|
gBrowser.setTabTitle(this._tabEdited);
|
||||||
}
|
}
|
||||||
if (this._tabEdited.getAttribute('zen-pin-id')) {
|
|
||||||
// Update pin title in storage
|
|
||||||
await gZenPinnedTabManager.updatePinTitle(
|
|
||||||
this._tabEdited,
|
|
||||||
this._tabEdited.label,
|
|
||||||
!!newName
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Maybe add some confetti here?!?
|
// Maybe add some confetti here?!?
|
||||||
gZenUIManager.motion.animate(
|
gZenUIManager.motion.animate(
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
:root:not([inDOMFullscreen='true']):not([chromehidden~='location']):not([chromehidden~='toolbar']) {
|
:root:not([inDOMFullscreen='true']):not([chromehidden~='location']):not([chromehidden~='toolbar']) {
|
||||||
& #tabbrowser-tabbox #tabbrowser-tabpanels .browserSidebarContainer {
|
& #tabbrowser-tabbox #tabbrowser-tabpanels .browserSidebarContainer {
|
||||||
overflow-y: clip;
|
overflow: clip;
|
||||||
|
|
||||||
:root:not([zen-no-padding='true']) &:not(.zen-glance-overlay) {
|
:root:not([zen-no-padding='true']) &:not(.zen-glance-overlay) {
|
||||||
border-radius: var(--zen-native-inner-radius);
|
border-radius: var(--zen-native-inner-radius);
|
||||||
|
|||||||
@@ -492,7 +492,7 @@ body > #confetti {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media -moz-pref('zen.theme.hide-unified-extensions-button') {
|
@media (-moz-pref('zen.theme.hide-unified-extensions-button')) {
|
||||||
#unified-extensions-button {
|
#unified-extensions-button {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -150,7 +150,6 @@
|
|||||||
for (let tab of this.allItems.reverse()) {
|
for (let tab of this.allItems.reverse()) {
|
||||||
tab = tab.group.hasAttribute('split-view-group') ? tab.group : tab;
|
tab = tab.group.hasAttribute('split-view-group') ? tab.group : tab;
|
||||||
if (tab.hasAttribute('zen-empty-tab')) {
|
if (tab.hasAttribute('zen-empty-tab')) {
|
||||||
await ZenPinnedTabsStorage.removePin(tab.getAttribute('zen-pin-id'));
|
|
||||||
gBrowser.removeTab(tab);
|
gBrowser.removeTab(tab);
|
||||||
} else {
|
} else {
|
||||||
gBrowser.ungroupTab(tab);
|
gBrowser.ungroupTab(tab);
|
||||||
@@ -160,7 +159,6 @@
|
|||||||
|
|
||||||
async delete() {
|
async delete() {
|
||||||
for (const tab of this.allItemsRecursive) {
|
for (const tab of this.allItemsRecursive) {
|
||||||
await ZenPinnedTabsStorage.removePin(tab.getAttribute('zen-pin-id'));
|
|
||||||
if (tab.hasAttribute('zen-empty-tab')) {
|
if (tab.hasAttribute('zen-empty-tab')) {
|
||||||
// Manually remove the empty tabs as removeTabs() inside removeTabGroup
|
// Manually remove the empty tabs as removeTabs() inside removeTabGroup
|
||||||
// does ignore them.
|
// does ignore them.
|
||||||
|
|||||||
@@ -508,9 +508,6 @@
|
|||||||
tabs = [emptyTab, ...filteredTabs];
|
tabs = [emptyTab, ...filteredTabs];
|
||||||
|
|
||||||
const folder = this._createFolderNode(options);
|
const folder = this._createFolderNode(options);
|
||||||
if (options.initialPinId) {
|
|
||||||
folder.setAttribute('zen-pin-id', options.initialPinId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.insertAfter) {
|
if (options.insertAfter) {
|
||||||
options.insertAfter.after(folder);
|
options.insertAfter.after(folder);
|
||||||
@@ -940,7 +937,7 @@
|
|||||||
if (!parentFolder && folder.hasAttribute('split-view-group')) continue;
|
if (!parentFolder && folder.hasAttribute('split-view-group')) continue;
|
||||||
const emptyFolderTabs = folder.tabs
|
const emptyFolderTabs = folder.tabs
|
||||||
.filter((tab) => tab.hasAttribute('zen-empty-tab'))
|
.filter((tab) => tab.hasAttribute('zen-empty-tab'))
|
||||||
.map((tab) => tab.getAttribute('zen-pin-id'));
|
.map((tab) => tab.getAttribute('zen-sync-id'));
|
||||||
|
|
||||||
let prevSiblingInfo = null;
|
let prevSiblingInfo = null;
|
||||||
const prevSibling = folder.previousElementSibling;
|
const prevSibling = folder.previousElementSibling;
|
||||||
@@ -949,8 +946,8 @@
|
|||||||
if (prevSibling) {
|
if (prevSibling) {
|
||||||
if (gBrowser.isTabGroup(prevSibling)) {
|
if (gBrowser.isTabGroup(prevSibling)) {
|
||||||
prevSiblingInfo = { type: 'group', id: prevSibling.id };
|
prevSiblingInfo = { type: 'group', id: prevSibling.id };
|
||||||
} else if (gBrowser.isTab(prevSibling) && prevSibling.hasAttribute('zen-pin-id')) {
|
} else if (gBrowser.isTab(prevSibling) && prevSibling.hasAttribute('zen-sync-id')) {
|
||||||
const zenPinId = prevSibling.getAttribute('zen-pin-id');
|
const zenPinId = prevSibling.getAttribute('zen-sync-id');
|
||||||
prevSiblingInfo = { type: 'tab', id: zenPinId };
|
prevSiblingInfo = { type: 'tab', id: zenPinId };
|
||||||
} else {
|
} else {
|
||||||
prevSiblingInfo = { type: 'start', id: null };
|
prevSiblingInfo = { type: 'start', id: null };
|
||||||
@@ -969,7 +966,7 @@
|
|||||||
prevSiblingInfo: prevSiblingInfo,
|
prevSiblingInfo: prevSiblingInfo,
|
||||||
emptyTabIds: emptyFolderTabs,
|
emptyTabIds: emptyFolderTabs,
|
||||||
userIcon: userIcon?.getAttribute('href'),
|
userIcon: userIcon?.getAttribute('href'),
|
||||||
pinId: folder.getAttribute('zen-pin-id'),
|
syncId: folder.getAttribute('zen-sync-id'),
|
||||||
// note: We shouldn't be using the workspace-id anywhere, we are just
|
// note: We shouldn't be using the workspace-id anywhere, we are just
|
||||||
// remembering it for the pinned tabs manager to use it later.
|
// remembering it for the pinned tabs manager to use it later.
|
||||||
workspaceId: folder.getAttribute('zen-workspace-id'),
|
workspaceId: folder.getAttribute('zen-workspace-id'),
|
||||||
@@ -996,9 +993,9 @@
|
|||||||
tabFolderWorkingData.set(folderData.id, workingData);
|
tabFolderWorkingData.set(folderData.id, workingData);
|
||||||
|
|
||||||
const oldGroup = document.getElementById(folderData.id);
|
const oldGroup = document.getElementById(folderData.id);
|
||||||
folderData.emptyTabIds.forEach((zenPinId) => {
|
folderData.emptyTabIds.forEach((zenSyncId) => {
|
||||||
oldGroup
|
oldGroup
|
||||||
?.querySelector(`tab[zen-pin-id="${zenPinId}"]`)
|
?.querySelector(`tab[zen-sync-id="${zenSyncId}"]`)
|
||||||
?.setAttribute('zen-empty-tab', true);
|
?.setAttribute('zen-empty-tab', true);
|
||||||
});
|
});
|
||||||
if (oldGroup) {
|
if (oldGroup) {
|
||||||
@@ -1011,7 +1008,7 @@
|
|||||||
saveOnWindowClose: folderData.saveOnWindowClose,
|
saveOnWindowClose: folderData.saveOnWindowClose,
|
||||||
workspaceId: folderData.workspaceId,
|
workspaceId: folderData.workspaceId,
|
||||||
});
|
});
|
||||||
folder.setAttribute('zen-pin-id', folderData.pinId);
|
folder.setAttribute('zen-sync-id', folderData.syncId);
|
||||||
workingData.node = folder;
|
workingData.node = folder;
|
||||||
oldGroup.before(folder);
|
oldGroup.before(folder);
|
||||||
} else {
|
} else {
|
||||||
@@ -1044,7 +1041,7 @@
|
|||||||
switch (stateData?.prevSiblingInfo?.type) {
|
switch (stateData?.prevSiblingInfo?.type) {
|
||||||
case 'tab': {
|
case 'tab': {
|
||||||
const tab = parentWorkingData.node.querySelector(
|
const tab = parentWorkingData.node.querySelector(
|
||||||
`[zen-pin-id="${stateData.prevSiblingInfo.id}"]`
|
`[zen-sync-id="${stateData.prevSiblingInfo.id}"]`
|
||||||
);
|
);
|
||||||
tab.after(node);
|
tab.after(node);
|
||||||
break;
|
break;
|
||||||
@@ -1156,7 +1153,10 @@
|
|||||||
let dragDownThreshold =
|
let dragDownThreshold =
|
||||||
Services.prefs.getIntPref('zen.view.drag-and-drop.drop-inside-lower-threshold') / 100;
|
Services.prefs.getIntPref('zen.view.drag-and-drop.drop-inside-lower-threshold') / 100;
|
||||||
|
|
||||||
const dropElementGroup = dropElement;
|
const dropElementGroup = dropElement.group;
|
||||||
|
if (!dropElementGroup) {
|
||||||
|
return { dropElement, colorCode, dropBefore };
|
||||||
|
}
|
||||||
const isSplitGroup = dropElement?.group?.hasAttribute('split-view-group');
|
const isSplitGroup = dropElement?.group?.hasAttribute('split-view-group');
|
||||||
let firstGroupElem =
|
let firstGroupElem =
|
||||||
dropElementGroup.querySelector('.zen-tab-group-start').nextElementSibling;
|
dropElementGroup.querySelector('.zen-tab-group-start').nextElementSibling;
|
||||||
|
|||||||
@@ -13,4 +13,5 @@ DIRS += [
|
|||||||
"tests",
|
"tests",
|
||||||
"urlbar",
|
"urlbar",
|
||||||
"toolkit",
|
"toolkit",
|
||||||
|
"sessionstore",
|
||||||
]
|
]
|
||||||
|
|||||||
6
src/zen/sessionstore/SessionComponents.manifest
Normal file
6
src/zen/sessionstore/SessionComponents.manifest
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
# Browser global components initializing before UI startup
|
||||||
|
category browser-before-ui-startup resource:///modules/zen/ZenSessionManager.sys.mjs ZenSessionStore.init
|
||||||
40
src/zen/sessionstore/ZenSessionFile.sys.mjs
Normal file
40
src/zen/sessionstore/ZenSessionFile.sys.mjs
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
// Note that changing this hidden pref will make the previous session file
|
||||||
|
// unused, causing a new session file to be created on next write.
|
||||||
|
const SHOULD_COMPRESS_FILE = Services.prefs.getBoolPref('zen.session-store.compress-file', true);
|
||||||
|
|
||||||
|
const FILE_NAME = SHOULD_COMPRESS_FILE ? 'zen-sessions.jsonlz4' : 'zen-sessions.json';
|
||||||
|
|
||||||
|
export class nsZenSessionFile {
|
||||||
|
#path = PathUtils.join(PathUtils.profileDir, FILE_NAME);
|
||||||
|
#sidebar = [];
|
||||||
|
|
||||||
|
async read() {
|
||||||
|
try {
|
||||||
|
const data = await IOUtils.readJSON(this.#path, { compress: SHOULD_COMPRESS_FILE });
|
||||||
|
this.#sidebar = data.sidebar || [];
|
||||||
|
} catch {
|
||||||
|
// File doesn't exist yet, that's fine.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get sidebar() {
|
||||||
|
return this.#sidebar;
|
||||||
|
}
|
||||||
|
|
||||||
|
set sidebar(data) {
|
||||||
|
this.#sidebar = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
async #write(data) {
|
||||||
|
await IOUtils.writeJSON(this.#path, data, { compress: SHOULD_COMPRESS_FILE });
|
||||||
|
}
|
||||||
|
|
||||||
|
async store() {
|
||||||
|
const data = { sidebar: this.#sidebar };
|
||||||
|
await this.#write(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
167
src/zen/sessionstore/ZenSessionManager.sys.mjs
Normal file
167
src/zen/sessionstore/ZenSessionManager.sys.mjs
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
const lazy = {};
|
||||||
|
|
||||||
|
ChromeUtils.defineESModuleGetters(lazy, {
|
||||||
|
nsZenSessionFile: 'resource:///modules/zen/ZenSessionFile.sys.mjs',
|
||||||
|
PrivateBrowsingUtils: 'resource://gre/modules/PrivateBrowsingUtils.sys.mjs',
|
||||||
|
BrowserWindowTracker: 'resource:///modules/BrowserWindowTracker.sys.mjs',
|
||||||
|
TabGroupState: 'resource:///modules/sessionstore/TabGroupState.sys.mjs',
|
||||||
|
SessionStore: 'resource:///modules/sessionstore/SessionStore.sys.mjs',
|
||||||
|
SessionSaver: 'resource:///modules/sessionstore/SessionSaver.sys.mjs',
|
||||||
|
});
|
||||||
|
|
||||||
|
const LAZY_COLLECT_THRESHOLD = 5 * 60 * 1000; // 5 minutes
|
||||||
|
const OBSERVING = ['sessionstore-state-write-complete', 'browser-window-before-show'];
|
||||||
|
|
||||||
|
class nsZenSessionManager {
|
||||||
|
#file;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.#file = new lazy.nsZenSessionFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called from SessionComponents.manifest on app-startup
|
||||||
|
init() {
|
||||||
|
this.#initObservers();
|
||||||
|
}
|
||||||
|
|
||||||
|
async readFile() {
|
||||||
|
await this.#file.read();
|
||||||
|
}
|
||||||
|
|
||||||
|
onFileRead(initialState) {
|
||||||
|
for (const winData of initialState.windows || []) {
|
||||||
|
this.restoreWindowData(winData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#initObservers() {
|
||||||
|
for (let topic of OBSERVING) {
|
||||||
|
Services.obs.addObserver(this, topic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get #sidebar() {
|
||||||
|
return this.#file.sidebar;
|
||||||
|
}
|
||||||
|
|
||||||
|
set #sidebar(data) {
|
||||||
|
this.#file.sidebar = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
observe(aSubject, aTopic) {
|
||||||
|
switch (aTopic) {
|
||||||
|
case 'sessionstore-state-write-complete': {
|
||||||
|
this.#saveState(true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'browser-window-before-show': // catch new windows
|
||||||
|
this.#onBeforeBrowserWindowShown(aSubject);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Handles the browser-window-before-show observer notification. */
|
||||||
|
#onBeforeBrowserWindowShown(aWindow) {
|
||||||
|
// TODO: Initialize new window
|
||||||
|
void aWindow;
|
||||||
|
}
|
||||||
|
|
||||||
|
get #topMostWindow() {
|
||||||
|
return lazy.BrowserWindowTracker.getTopWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the current session state. Collects data and writes to disk.
|
||||||
|
*
|
||||||
|
* @param forceUpdateAllWindows (optional)
|
||||||
|
* Forces us to recollect data for all windows and will bypass and
|
||||||
|
* update the corresponding caches.
|
||||||
|
*/
|
||||||
|
async #saveState(forceUpdateAllWindows = false) {
|
||||||
|
if (lazy.PrivateBrowsingUtils.permanentPrivateBrowsing) {
|
||||||
|
// Don't save (or even collect) anything in permanent private
|
||||||
|
// browsing mode
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Collect an initial snapshot of window data before we do the flush.
|
||||||
|
const window = this.#topMostWindow;
|
||||||
|
// We don't have any normal windows or no windows at all
|
||||||
|
if (!window) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.#collectWindowData(this.#topMostWindow, forceUpdateAllWindows);
|
||||||
|
this.#file.store();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collects session data for a given window.
|
||||||
|
*
|
||||||
|
* @param window
|
||||||
|
* The window to collect data for.
|
||||||
|
* @param forceUpdate
|
||||||
|
* Forces us to recollect data and will bypass and update the
|
||||||
|
* corresponding caches.
|
||||||
|
*/
|
||||||
|
#collectWindowData(window, forceUpdate = false) {
|
||||||
|
let sidebarData = this.#sidebar;
|
||||||
|
if (!sidebarData || forceUpdate) {
|
||||||
|
sidebarData = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it hasn't changed, don't update.
|
||||||
|
if (
|
||||||
|
!forceUpdate &&
|
||||||
|
sidebarData.lastCollected &&
|
||||||
|
Date.now() - sidebarData.lastCollected < LAZY_COLLECT_THRESHOLD
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sidebarData.lastCollected = Date.now();
|
||||||
|
this.#collectTabsData(window, sidebarData);
|
||||||
|
this.#sidebar = sidebarData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collects session data for all tabs in a given window.
|
||||||
|
*
|
||||||
|
* @param aWindow
|
||||||
|
* The window to collect tab data for.
|
||||||
|
* @param winData
|
||||||
|
* The window data object to populate.
|
||||||
|
*/
|
||||||
|
#collectTabsData(aWindow, sidebarData) {
|
||||||
|
const winData = lazy.SessionStore.getWindowState(aWindow).windows[0];
|
||||||
|
if (!winData) return;
|
||||||
|
sidebarData.tabs = winData.tabs;
|
||||||
|
sidebarData.folders = winData.folders;
|
||||||
|
sidebarData.splitViewData = winData.splitViewData;
|
||||||
|
sidebarData.groups = winData.groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
restoreWindowData(aWindowData) {
|
||||||
|
const sidebar = this.#file.sidebar;
|
||||||
|
if (!sidebar) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
aWindowData.tabs = sidebar.tabs || [];
|
||||||
|
aWindowData.splitViewData = sidebar.splitViewData;
|
||||||
|
aWindowData.folders = sidebar.folders;
|
||||||
|
aWindowData.groups = sidebar.groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
getNewWindowData() {
|
||||||
|
lazy.SessionSaver.run();
|
||||||
|
const state = lazy.SessionStore.getCurrentState(true);
|
||||||
|
const windows = state.windows || {};
|
||||||
|
let newWindow = Cu.cloneInto(windows[0], {});
|
||||||
|
return { windows: [newWindow] };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ZenSessionStore = new nsZenSessionManager();
|
||||||
8
src/zen/sessionstore/moz.build
Normal file
8
src/zen/sessionstore/moz.build
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
EXTRA_JS_MODULES.zen += [
|
||||||
|
"ZenSessionFile.sys.mjs",
|
||||||
|
"ZenSessionManager.sys.mjs",
|
||||||
|
]
|
||||||
@@ -5,23 +5,7 @@
|
|||||||
const lazy = {};
|
const lazy = {};
|
||||||
|
|
||||||
class ZenPinnedTabsObserver {
|
class ZenPinnedTabsObserver {
|
||||||
static ALL_EVENTS = [
|
static ALL_EVENTS = ['TabPinned', 'TabUnpinned'];
|
||||||
'TabPinned',
|
|
||||||
'TabUnpinned',
|
|
||||||
'TabMove',
|
|
||||||
'TabGroupCreate',
|
|
||||||
'TabGroupRemoved',
|
|
||||||
'TabGroupMoved',
|
|
||||||
'ZenFolderRenamed',
|
|
||||||
'ZenFolderIconChanged',
|
|
||||||
'TabGroupCollapse',
|
|
||||||
'TabGroupExpand',
|
|
||||||
'TabGrouped',
|
|
||||||
'TabUngrouped',
|
|
||||||
'ZenFolderChangedWorkspace',
|
|
||||||
'TabAddedToEssentials',
|
|
||||||
'TabRemovedFromEssentials',
|
|
||||||
];
|
|
||||||
|
|
||||||
#listeners = [];
|
#listeners = [];
|
||||||
|
|
||||||
@@ -101,24 +85,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
onTabIconChanged(tab, url = null) {
|
onTabIconChanged(tab, url = null) {
|
||||||
|
tab.dispatchEvent(new CustomEvent('ZenTabIconChanged', { bubbles: true, detail: { tab } }));
|
||||||
const iconUrl = url ?? tab.iconImage.src;
|
const iconUrl = url ?? tab.iconImage.src;
|
||||||
if (!iconUrl && tab.hasAttribute('zen-pin-id')) {
|
|
||||||
try {
|
|
||||||
setTimeout(async () => {
|
|
||||||
const favicon = await this.getFaviconAsBase64(tab.linkedBrowser.currentURI);
|
|
||||||
if (favicon) {
|
|
||||||
gBrowser.setIcon(tab, favicon);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch {
|
|
||||||
// Handle error
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (tab.hasAttribute('zen-essential')) {
|
if (tab.hasAttribute('zen-essential')) {
|
||||||
tab.style.setProperty('--zen-essential-tab-icon', `url(${iconUrl})`);
|
tab.style.setProperty('--zen-essential-tab-icon', `url(${iconUrl})`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
_onTabResetPinButton(event, tab) {
|
_onTabResetPinButton(event, tab) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
@@ -148,261 +120,6 @@
|
|||||||
return lazy.zenTabsEssentialsMax;
|
return lazy.zenTabsEssentialsMax;
|
||||||
}
|
}
|
||||||
|
|
||||||
async refreshPinnedTabs({ init = false } = {}) {
|
|
||||||
if (!this.enabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await ZenPinnedTabsStorage.promiseInitialized;
|
|
||||||
await this.#initializePinsCache();
|
|
||||||
setTimeout(async () => {
|
|
||||||
// Execute in a separate task to avoid blocking the main thread
|
|
||||||
await SessionStore.promiseAllWindowsRestored;
|
|
||||||
await gZenWorkspaces.promiseInitialized;
|
|
||||||
await this.#initializePinnedTabs(init);
|
|
||||||
if (init) {
|
|
||||||
this._hasFinishedLoading = true;
|
|
||||||
}
|
|
||||||
}, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
async #initializePinsCache() {
|
|
||||||
try {
|
|
||||||
// Get pin data
|
|
||||||
const pins = await ZenPinnedTabsStorage.getPins();
|
|
||||||
|
|
||||||
// Enhance pins with favicons
|
|
||||||
this._pinsCache = await Promise.all(
|
|
||||||
pins.map(async (pin) => {
|
|
||||||
try {
|
|
||||||
if (pin.isGroup) {
|
|
||||||
return pin; // Skip groups for now
|
|
||||||
}
|
|
||||||
const image = await this.getFaviconAsBase64(Services.io.newURI(pin.url));
|
|
||||||
return {
|
|
||||||
...pin,
|
|
||||||
iconUrl: image || null,
|
|
||||||
};
|
|
||||||
} catch {
|
|
||||||
// If favicon fetch fails, continue without icon
|
|
||||||
return {
|
|
||||||
...pin,
|
|
||||||
iconUrl: null,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} catch (ex) {
|
|
||||||
console.error('Failed to initialize pins cache:', ex);
|
|
||||||
this._pinsCache = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
this.log(`Initialized pins cache with ${this._pinsCache.length} pins`);
|
|
||||||
return this._pinsCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
#finishedInitializingPins() {
|
|
||||||
if (this.hasInitializedPins) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._resolvePinnedInitializedInternal();
|
|
||||||
delete this._resolvePinnedInitializedInternal;
|
|
||||||
this.hasInitializedPins = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
async #initializePinnedTabs(init = false) {
|
|
||||||
const pins = this._pinsCache;
|
|
||||||
if (!pins?.length || !init) {
|
|
||||||
this.#finishedInitializingPins();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const pinnedTabsByUUID = new Map();
|
|
||||||
const pinsToCreate = new Set(pins.map((p) => p.uuid));
|
|
||||||
|
|
||||||
// First pass: identify existing tabs and remove those without pins
|
|
||||||
for (let tab of gZenWorkspaces.allStoredTabs) {
|
|
||||||
const pinId = tab.getAttribute('zen-pin-id');
|
|
||||||
if (!pinId) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pinsToCreate.has(pinId)) {
|
|
||||||
// This is a valid pinned tab that matches a pin
|
|
||||||
pinnedTabsByUUID.set(pinId, tab);
|
|
||||||
pinsToCreate.delete(pinId);
|
|
||||||
|
|
||||||
if (lazy.zenPinnedTabRestorePinnedTabsToPinnedUrl && init) {
|
|
||||||
this._resetTabToStoredState(tab);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// This is a pinned tab that no longer has a corresponding pin
|
|
||||||
gBrowser.removeTab(tab);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const group of gZenWorkspaces.allTabGroups) {
|
|
||||||
const pinId = group.getAttribute('zen-pin-id');
|
|
||||||
if (!pinId) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (pinsToCreate.has(pinId)) {
|
|
||||||
// This is a valid pinned group that matches a pin
|
|
||||||
pinsToCreate.delete(pinId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Second pass: For every existing tab, update its label
|
|
||||||
// and set 'zen-has-static-label' attribute if it's been edited
|
|
||||||
for (let pin of pins) {
|
|
||||||
const tab = pinnedTabsByUUID.get(pin.uuid);
|
|
||||||
if (!tab) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
tab.removeAttribute('zen-has-static-label'); // So we can set it again
|
|
||||||
if (pin.title && pin.editedTitle) {
|
|
||||||
gBrowser._setTabLabel(tab, pin.title, { beforeTabOpen: true });
|
|
||||||
tab.setAttribute('zen-has-static-label', 'true');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const groups = new Map();
|
|
||||||
const pendingTabsInsideGroups = {};
|
|
||||||
|
|
||||||
// Third pass: create new tabs for pins that don't have tabs
|
|
||||||
for (let pin of pins) {
|
|
||||||
try {
|
|
||||||
if (!pinsToCreate.has(pin.uuid)) {
|
|
||||||
continue; // Skip pins that already have tabs
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pin.isGroup) {
|
|
||||||
const tabs = [];
|
|
||||||
// If there's already existing tabs, let's use them
|
|
||||||
for (const [uuid, existingTab] of pinnedTabsByUUID) {
|
|
||||||
const pinObject = this._pinsCache.find((p) => p.uuid === uuid);
|
|
||||||
if (pinObject && pinObject.parentUuid === pin.uuid) {
|
|
||||||
tabs.push(existingTab);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// We still need to iterate through pending tabs since the database
|
|
||||||
// query doesn't guarantee the order of insertion
|
|
||||||
for (const [parentUuid, folderTabs] of Object.entries(pendingTabsInsideGroups)) {
|
|
||||||
if (parentUuid === pin.uuid) {
|
|
||||||
tabs.push(...folderTabs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const group = gZenFolders.createFolder(tabs, {
|
|
||||||
label: pin.title,
|
|
||||||
collapsed: pin.isFolderCollapsed,
|
|
||||||
initialPinId: pin.uuid,
|
|
||||||
workspaceId: pin.workspaceUuid,
|
|
||||||
insertAfter:
|
|
||||||
groups.get(pin.parentUuid)?.querySelector('.tab-group-container')?.lastChild ||
|
|
||||||
null,
|
|
||||||
});
|
|
||||||
gZenFolders.setFolderUserIcon(group, pin.folderIcon);
|
|
||||||
groups.set(pin.uuid, group);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let params = {
|
|
||||||
skipAnimation: true,
|
|
||||||
allowInheritPrincipal: false,
|
|
||||||
skipBackgroundNotify: true,
|
|
||||||
userContextId: pin.containerTabId || 0,
|
|
||||||
createLazyBrowser: true,
|
|
||||||
skipLoad: true,
|
|
||||||
noInitialLabel: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create and initialize the tab
|
|
||||||
let newTab = gBrowser.addTrustedTab(pin.url, params);
|
|
||||||
newTab.setAttribute('zenDefaultUserContextId', true);
|
|
||||||
|
|
||||||
// Set initial label/title
|
|
||||||
if (pin.title) {
|
|
||||||
gBrowser.setInitialTabTitle(newTab, pin.title);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the icon if we have it cached
|
|
||||||
if (pin.iconUrl) {
|
|
||||||
gBrowser.setIcon(newTab, pin.iconUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
newTab.setAttribute('zen-pin-id', pin.uuid);
|
|
||||||
|
|
||||||
if (pin.workspaceUuid) {
|
|
||||||
newTab.setAttribute('zen-workspace-id', pin.workspaceUuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pin.isEssential) {
|
|
||||||
newTab.setAttribute('zen-essential', 'true');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pin.editedTitle) {
|
|
||||||
newTab.setAttribute('zen-has-static-label', 'true');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize browser state if needed
|
|
||||||
if (!newTab.linkedBrowser._remoteAutoRemoved) {
|
|
||||||
let state = {
|
|
||||||
entries: [
|
|
||||||
{
|
|
||||||
url: pin.url,
|
|
||||||
title: pin.title,
|
|
||||||
triggeringPrincipal_base64: E10SUtils.SERIALIZED_SYSTEMPRINCIPAL,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
userContextId: pin.containerTabId || 0,
|
|
||||||
image: pin.iconUrl,
|
|
||||||
};
|
|
||||||
|
|
||||||
SessionStore.setTabState(newTab, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.log(`Created new pinned tab for pin ${pin.uuid} (isEssential: ${pin.isEssential})`);
|
|
||||||
gBrowser.pinTab(newTab);
|
|
||||||
|
|
||||||
if (pin.parentUuid) {
|
|
||||||
const parentGroup = groups.get(pin.parentUuid);
|
|
||||||
if (parentGroup) {
|
|
||||||
parentGroup.querySelector('.tab-group-container').appendChild(newTab);
|
|
||||||
} else {
|
|
||||||
if (pendingTabsInsideGroups[pin.parentUuid]) {
|
|
||||||
pendingTabsInsideGroups[pin.parentUuid].push(newTab);
|
|
||||||
} else {
|
|
||||||
pendingTabsInsideGroups[pin.parentUuid] = [newTab];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!pin.isEssential) {
|
|
||||||
const container = gZenWorkspaces.workspaceElement(
|
|
||||||
pin.workspaceUuid
|
|
||||||
)?.pinnedTabsContainer;
|
|
||||||
if (container) {
|
|
||||||
container.insertBefore(newTab, container.lastChild);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
gZenWorkspaces.getEssentialsSection(pin.containerTabId).appendChild(newTab);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
gBrowser.tabContainer._invalidateCachedTabs();
|
|
||||||
newTab.initialize();
|
|
||||||
} catch (ex) {
|
|
||||||
console.error('Failed to initialize pinned tabs:', ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
this.#finishedInitializingPins();
|
|
||||||
}, 0);
|
|
||||||
|
|
||||||
gBrowser._updateTabBarForPinnedTabs();
|
|
||||||
gZenUIManager.updateTabsToolbar();
|
|
||||||
}
|
|
||||||
|
|
||||||
_onPinnedTabEvent(action, event) {
|
_onPinnedTabEvent(action, event) {
|
||||||
if (!this.enabled) return;
|
if (!this.enabled) return;
|
||||||
const tab = event.target;
|
const tab = event.target;
|
||||||
@@ -412,238 +129,22 @@
|
|||||||
}
|
}
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case 'TabPinned':
|
case 'TabPinned':
|
||||||
case 'TabAddedToEssentials':
|
|
||||||
tab._zenClickEventListener = this._zenClickEventListener;
|
tab._zenClickEventListener = this._zenClickEventListener;
|
||||||
tab.addEventListener('click', tab._zenClickEventListener);
|
tab.addEventListener('click', tab._zenClickEventListener);
|
||||||
this._setPinnedAttributes(tab);
|
|
||||||
break;
|
break;
|
||||||
case 'TabRemovedFromEssentials':
|
|
||||||
if (tab.pinned) {
|
|
||||||
this.#onTabMove(tab);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// [Fall through]
|
// [Fall through]
|
||||||
case 'TabUnpinned':
|
case 'TabUnpinned':
|
||||||
this._removePinnedAttributes(tab);
|
|
||||||
if (tab._zenClickEventListener) {
|
if (tab._zenClickEventListener) {
|
||||||
tab.removeEventListener('click', tab._zenClickEventListener);
|
tab.removeEventListener('click', tab._zenClickEventListener);
|
||||||
delete tab._zenClickEventListener;
|
delete tab._zenClickEventListener;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'TabMove':
|
|
||||||
this.#onTabMove(tab);
|
|
||||||
break;
|
|
||||||
case 'TabGroupCreate':
|
|
||||||
this.#onTabGroupCreate(event);
|
|
||||||
break;
|
|
||||||
case 'TabGroupRemoved':
|
|
||||||
this.#onTabGroupRemoved(event);
|
|
||||||
break;
|
|
||||||
case 'TabGroupMoved':
|
|
||||||
this.#onTabGroupMoved(event);
|
|
||||||
break;
|
|
||||||
case 'ZenFolderRenamed':
|
|
||||||
case 'ZenFolderIconChanged':
|
|
||||||
case 'TabGroupCollapse':
|
|
||||||
case 'TabGroupExpand':
|
|
||||||
case 'ZenFolderChangedWorkspace':
|
|
||||||
this.#updateGroupInfo(event.originalTarget, action);
|
|
||||||
break;
|
|
||||||
case 'TabGrouped':
|
|
||||||
this.#onTabGrouped(event);
|
|
||||||
break;
|
|
||||||
case 'TabUngrouped':
|
|
||||||
this.#onTabUngrouped(event);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
console.warn('ZenPinnedTabManager: Unhandled tab event', action);
|
console.warn('ZenPinnedTabManager: Unhandled tab event', action);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async #onTabGroupCreate(event) {
|
|
||||||
const group = event.originalTarget;
|
|
||||||
if (!group.isZenFolder) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (group.hasAttribute('zen-pin-id')) {
|
|
||||||
return; // Group already exists in storage
|
|
||||||
}
|
|
||||||
const workspaceId = group.getAttribute('zen-workspace-id');
|
|
||||||
let id = await ZenPinnedTabsStorage.createGroup(
|
|
||||||
group.name,
|
|
||||||
group.iconURL,
|
|
||||||
group.collapsed,
|
|
||||||
workspaceId,
|
|
||||||
group.getAttribute('zen-pin-id'),
|
|
||||||
group._pPos
|
|
||||||
);
|
|
||||||
group.setAttribute('zen-pin-id', id);
|
|
||||||
for (const tab of group.tabs) {
|
|
||||||
// Only add it if the tab is directly under the group
|
|
||||||
if (
|
|
||||||
tab.pinned &&
|
|
||||||
tab.hasAttribute('zen-pin-id') &&
|
|
||||||
tab.group === group &&
|
|
||||||
this.hasInitializedPins
|
|
||||||
) {
|
|
||||||
const tabPinId = tab.getAttribute('zen-pin-id');
|
|
||||||
await ZenPinnedTabsStorage.addTabToGroup(tabPinId, id, /* position */ tab._pPos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await this.refreshPinnedTabs();
|
|
||||||
}
|
|
||||||
|
|
||||||
async #onTabGrouped(event) {
|
|
||||||
const tab = event.detail;
|
|
||||||
const group = tab.group;
|
|
||||||
if (!group.isZenFolder) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const pinId = group.getAttribute('zen-pin-id');
|
|
||||||
const tabPinId = tab.getAttribute('zen-pin-id');
|
|
||||||
const tabPin = this._pinsCache?.find((p) => p.uuid === tabPinId);
|
|
||||||
if (!tabPin || !tabPin.group) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ZenPinnedTabsStorage.addTabToGroup(tabPinId, pinId, /* position */ tab._pPos);
|
|
||||||
}
|
|
||||||
|
|
||||||
async #onTabUngrouped(event) {
|
|
||||||
const tab = event.detail;
|
|
||||||
const group = tab.group;
|
|
||||||
if (!group?.isZenFolder) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const tabPinId = tab.getAttribute('zen-pin-id');
|
|
||||||
const tabPin = this._pinsCache?.find((p) => p.uuid === tabPinId);
|
|
||||||
if (!tabPin) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ZenPinnedTabsStorage.removeTabFromGroup(tabPinId, /* position */ tab._pPos);
|
|
||||||
}
|
|
||||||
|
|
||||||
async #updateGroupInfo(group, action) {
|
|
||||||
if (!group?.isZenFolder) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const pinId = group.getAttribute('zen-pin-id');
|
|
||||||
const groupPin = this._pinsCache?.find((p) => p.uuid === pinId);
|
|
||||||
if (groupPin) {
|
|
||||||
groupPin.title = group.name;
|
|
||||||
groupPin.folderIcon = group.iconURL;
|
|
||||||
groupPin.isFolderCollapsed = group.collapsed;
|
|
||||||
groupPin.position = group._pPos;
|
|
||||||
groupPin.parentUuid = group.group?.getAttribute('zen-pin-id') || null;
|
|
||||||
groupPin.workspaceUuid = group.getAttribute('zen-workspace-id') || null;
|
|
||||||
await this.savePin(groupPin);
|
|
||||||
switch (action) {
|
|
||||||
case 'ZenFolderRenamed':
|
|
||||||
case 'ZenFolderIconChanged':
|
|
||||||
case 'TabGroupCollapse':
|
|
||||||
case 'TabGroupExpand':
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
for (const item of group.allItems) {
|
|
||||||
if (gBrowser.isTabGroup(item)) {
|
|
||||||
await this.#updateGroupInfo(item, action);
|
|
||||||
} else {
|
|
||||||
await this.#onTabMove(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async #onTabGroupRemoved(event) {
|
|
||||||
const group = event.originalTarget;
|
|
||||||
if (!group.isZenFolder) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await ZenPinnedTabsStorage.removePin(group.getAttribute('zen-pin-id'));
|
|
||||||
group.removeAttribute('zen-pin-id');
|
|
||||||
}
|
|
||||||
|
|
||||||
async #onTabGroupMoved(event) {
|
|
||||||
const group = event.originalTarget;
|
|
||||||
if (!group.isZenFolder) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const newIndex = group._pPos;
|
|
||||||
const pinId = group.getAttribute('zen-pin-id');
|
|
||||||
if (!pinId) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (const tab of group.allItemsRecursive) {
|
|
||||||
if (tab.pinned && tab.getAttribute('zen-pin-id') === pinId) {
|
|
||||||
const pin = this._pinsCache.find((p) => p.uuid === pinId);
|
|
||||||
if (pin) {
|
|
||||||
pin.position = tab._pPos;
|
|
||||||
pin.parentUuid = tab.group?.getAttribute('zen-pin-id') || null;
|
|
||||||
pin.workspaceUuid = group.getAttribute('zen-workspace-id');
|
|
||||||
await this.savePin(pin, false);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const groupPin = this._pinsCache?.find((p) => p.uuid === pinId);
|
|
||||||
if (groupPin) {
|
|
||||||
groupPin.position = newIndex;
|
|
||||||
groupPin.parentUuid = group.group?.getAttribute('zen-pin-id');
|
|
||||||
groupPin.workspaceUuid = group.getAttribute('zen-workspace-id');
|
|
||||||
await this.savePin(groupPin);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async #onTabMove(tab) {
|
|
||||||
if (!tab.pinned || !this._pinsCache) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const allTabs = [...gBrowser.tabs, ...gBrowser.tabGroups];
|
|
||||||
for (let i = 0; i < allTabs.length; i++) {
|
|
||||||
const otherTab = allTabs[i];
|
|
||||||
if (
|
|
||||||
otherTab.pinned &&
|
|
||||||
otherTab.getAttribute('zen-pin-id') !== tab.getAttribute('zen-pin-id')
|
|
||||||
) {
|
|
||||||
const actualPin = this._pinsCache.find(
|
|
||||||
(pin) => pin.uuid === otherTab.getAttribute('zen-pin-id')
|
|
||||||
);
|
|
||||||
if (!actualPin) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
actualPin.position = otherTab._pPos;
|
|
||||||
actualPin.workspaceUuid = otherTab.getAttribute('zen-workspace-id');
|
|
||||||
actualPin.parentUuid = otherTab.group?.getAttribute('zen-pin-id') || null;
|
|
||||||
await this.savePin(actualPin, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const actualPin = this._pinsCache.find((pin) => pin.uuid === tab.getAttribute('zen-pin-id'));
|
|
||||||
|
|
||||||
if (!actualPin) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
actualPin.position = tab._pPos;
|
|
||||||
actualPin.isEssential = tab.hasAttribute('zen-essential');
|
|
||||||
actualPin.parentUuid = tab.group?.getAttribute('zen-pin-id') || null;
|
|
||||||
actualPin.workspaceUuid = tab.getAttribute('zen-workspace-id') || null;
|
|
||||||
|
|
||||||
// There was a bug where the title and hasStaticLabel attribute were not being set
|
|
||||||
// This is a workaround to fix that
|
|
||||||
if (tab.hasAttribute('zen-has-static-label')) {
|
|
||||||
actualPin.editedTitle = true;
|
|
||||||
actualPin.title = tab.label;
|
|
||||||
}
|
|
||||||
await this.savePin(actualPin);
|
|
||||||
tab.dispatchEvent(
|
|
||||||
new CustomEvent('ZenPinnedTabMoved', {
|
|
||||||
detail: { tab },
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async _onTabClick(e) {
|
async _onTabClick(e) {
|
||||||
const tab = e.target?.closest('tab');
|
const tab = e.target?.closest('tab');
|
||||||
if (e.button === 1 && tab) {
|
if (e.button === 1 && tab) {
|
||||||
@@ -673,106 +174,10 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const browser = tab.linkedBrowser;
|
|
||||||
|
|
||||||
const pin = this._pinsCache.find((pin) => pin.uuid === tab.getAttribute('zen-pin-id'));
|
|
||||||
|
|
||||||
if (!pin) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const userContextId = tab.getAttribute('usercontextid');
|
|
||||||
|
|
||||||
pin.title = tab.label || browser.contentTitle;
|
|
||||||
pin.url = browser.currentURI.spec;
|
|
||||||
pin.workspaceUuid = tab.getAttribute('zen-workspace-id');
|
|
||||||
pin.userContextId = userContextId ? parseInt(userContextId, 10) : 0;
|
|
||||||
|
|
||||||
await this.savePin(pin);
|
|
||||||
this.resetPinChangedUrl(tab);
|
this.resetPinChangedUrl(tab);
|
||||||
await this.refreshPinnedTabs();
|
|
||||||
gZenUIManager.showToast('zen-pinned-tab-replaced');
|
gZenUIManager.showToast('zen-pinned-tab-replaced');
|
||||||
}
|
}
|
||||||
|
|
||||||
async _setPinnedAttributes(tab) {
|
|
||||||
if (
|
|
||||||
tab.hasAttribute('zen-pin-id') ||
|
|
||||||
!this._hasFinishedLoading ||
|
|
||||||
tab.hasAttribute('zen-empty-tab')
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.log(`Setting pinned attributes for tab ${tab.linkedBrowser.currentURI.spec}`);
|
|
||||||
const browser = tab.linkedBrowser;
|
|
||||||
|
|
||||||
const uuid = gZenUIManager.generateUuidv4();
|
|
||||||
const userContextId = tab.getAttribute('usercontextid');
|
|
||||||
|
|
||||||
let entry = null;
|
|
||||||
|
|
||||||
if (tab.getAttribute('zen-pinned-entry')) {
|
|
||||||
entry = JSON.parse(tab.getAttribute('zen-pinned-entry'));
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.savePin({
|
|
||||||
uuid,
|
|
||||||
title: entry?.title || tab.label || browser.contentTitle,
|
|
||||||
url: entry?.url || browser.currentURI.spec,
|
|
||||||
containerTabId: userContextId ? parseInt(userContextId, 10) : 0,
|
|
||||||
workspaceUuid: tab.getAttribute('zen-workspace-id'),
|
|
||||||
isEssential: tab.getAttribute('zen-essential') === 'true',
|
|
||||||
parentUuid: tab.group?.getAttribute('zen-pin-id') || null,
|
|
||||||
position: tab._pPos,
|
|
||||||
});
|
|
||||||
|
|
||||||
tab.setAttribute('zen-pin-id', uuid);
|
|
||||||
tab.dispatchEvent(
|
|
||||||
new CustomEvent('ZenPinnedTabCreated', {
|
|
||||||
detail: { tab },
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
// This is used while migrating old pins to new system - we don't want to refresh when migrating
|
|
||||||
if (tab.getAttribute('zen-pinned-entry')) {
|
|
||||||
tab.removeAttribute('zen-pinned-entry');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.onLocationChange(browser);
|
|
||||||
await this.refreshPinnedTabs();
|
|
||||||
}
|
|
||||||
|
|
||||||
async _removePinnedAttributes(tab, isClosing = false) {
|
|
||||||
tab.removeAttribute('zen-has-static-label');
|
|
||||||
if (!tab.getAttribute('zen-pin-id') || this._temporarilyUnpiningEssential) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Services.startup.shuttingDown || window.skipNextCanClose) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.log(`Removing pinned attributes for tab ${tab.getAttribute('zen-pin-id')}`);
|
|
||||||
await ZenPinnedTabsStorage.removePin(tab.getAttribute('zen-pin-id'));
|
|
||||||
this.resetPinChangedUrl(tab);
|
|
||||||
|
|
||||||
if (!isClosing) {
|
|
||||||
tab.removeAttribute('zen-pin-id');
|
|
||||||
tab.removeAttribute('zen-essential'); // Just in case
|
|
||||||
|
|
||||||
if (!tab.hasAttribute('zen-workspace-id') && gZenWorkspaces.workspaceEnabled) {
|
|
||||||
const workspace = await gZenWorkspaces.getActiveWorkspace();
|
|
||||||
tab.setAttribute('zen-workspace-id', workspace.uuid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await this.refreshPinnedTabs();
|
|
||||||
tab.dispatchEvent(
|
|
||||||
new CustomEvent('ZenPinnedTabRemoved', {
|
|
||||||
detail: { tab },
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
_initClosePinnedTabShortcut() {
|
_initClosePinnedTabShortcut() {
|
||||||
let cmdClose = document.getElementById('cmd_close');
|
let cmdClose = document.getElementById('cmd_close');
|
||||||
|
|
||||||
@@ -781,21 +186,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async savePin(pin, notifyObservers = true) {
|
|
||||||
if (!this.hasInitializedPins && !gZenUIManager.testingEnabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const existingPin = this._pinsCache.find((p) => p.uuid === pin.uuid);
|
|
||||||
if (existingPin) {
|
|
||||||
Object.assign(existingPin, pin);
|
|
||||||
} else {
|
|
||||||
// We shouldn't need it, but just in case there's
|
|
||||||
// a race condition while making new pinned tabs.
|
|
||||||
this._pinsCache.push(pin);
|
|
||||||
}
|
|
||||||
await ZenPinnedTabsStorage.savePin(pin, notifyObservers);
|
|
||||||
}
|
|
||||||
|
|
||||||
async onCloseTabShortcut(
|
async onCloseTabShortcut(
|
||||||
event,
|
event,
|
||||||
selectedTab = gBrowser.selectedTab,
|
selectedTab = gBrowser.selectedTab,
|
||||||
@@ -842,7 +232,6 @@
|
|||||||
switch (behavior) {
|
switch (behavior) {
|
||||||
case 'close': {
|
case 'close': {
|
||||||
for (const tab of pinnedTabs) {
|
for (const tab of pinnedTabs) {
|
||||||
this._removePinnedAttributes(tab, true);
|
|
||||||
gBrowser.removeTab(tab, { animate: true });
|
gBrowser.removeTab(tab, { animate: true });
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1018,12 +407,6 @@
|
|||||||
tab.removeAttribute('zen-workspace-id');
|
tab.removeAttribute('zen-workspace-id');
|
||||||
}
|
}
|
||||||
if (tab.pinned && tab.hasAttribute('zen-pin-id')) {
|
if (tab.pinned && tab.hasAttribute('zen-pin-id')) {
|
||||||
const pin = this._pinsCache.find((pin) => pin.uuid === tab.getAttribute('zen-pin-id'));
|
|
||||||
if (pin) {
|
|
||||||
pin.isEssential = true;
|
|
||||||
pin.workspaceUuid = null;
|
|
||||||
this.savePin(pin);
|
|
||||||
}
|
|
||||||
gBrowser.zenHandleTabMove(tab, () => {
|
gBrowser.zenHandleTabMove(tab, () => {
|
||||||
if (tab.ownerGlobal !== window) {
|
if (tab.ownerGlobal !== window) {
|
||||||
tab = gBrowser.adoptTab(tab, {
|
tab = gBrowser.adoptTab(tab, {
|
||||||
@@ -1414,11 +797,8 @@
|
|||||||
return document.documentElement.getAttribute('zen-sidebar-expanded') === 'true';
|
return document.documentElement.getAttribute('zen-sidebar-expanded') === 'true';
|
||||||
}
|
}
|
||||||
|
|
||||||
async updatePinTitle(tab, newTitle, isEdited = true, notifyObservers = true) {
|
async updatePinTitle(tab, newTitle, isEdited = true) {
|
||||||
const uuid = tab.getAttribute('zen-pin-id');
|
const uuid = tab.getAttribute('zen-pin-id');
|
||||||
await ZenPinnedTabsStorage.updatePinTitle(uuid, newTitle, isEdited, notifyObservers);
|
|
||||||
|
|
||||||
await this.refreshPinnedTabs();
|
|
||||||
|
|
||||||
const browsers = Services.wm.getEnumerator('navigator:browser');
|
const browsers = Services.wm.getEnumerator('navigator:browser');
|
||||||
|
|
||||||
@@ -1563,6 +943,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
async onTabLabelChanged(tab) {
|
async onTabLabelChanged(tab) {
|
||||||
|
tab.dispatchEvent(new CustomEvent('ZenTabLabelChanged', { detail: { tab } }));
|
||||||
if (!this._pinsCache) {
|
if (!this._pinsCache) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
308
src/zen/workspaces/ZenWindowSyncing.mjs
Normal file
308
src/zen/workspaces/ZenWindowSyncing.mjs
Normal file
@@ -0,0 +1,308 @@
|
|||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
{
|
||||||
|
class nsZenWorkspaceWindowSync extends nsZenMultiWindowFeature {
|
||||||
|
#ignoreNextEvents = false;
|
||||||
|
#waitForPromise = null;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
if (!window.closed) {
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async init() {
|
||||||
|
await gZenWorkspaces.promiseInitialized;
|
||||||
|
this.#makeSureAllTabsHaveIds();
|
||||||
|
this.#setUpEventListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
#makeSureAllTabsHaveIds() {
|
||||||
|
const allTabs = gZenWorkspaces.allStoredTabs;
|
||||||
|
for (const tab of allTabs) {
|
||||||
|
if (!tab.hasAttribute('zen-sync-id') && !tab.hasAttribute('zen-empty-tab')) {
|
||||||
|
const tabId = gZenUIManager.generateUuidv4();
|
||||||
|
tab.setAttribute('zen-sync-id', tabId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#setUpEventListeners() {
|
||||||
|
const kEvents = [
|
||||||
|
'TabClose',
|
||||||
|
'TabOpen',
|
||||||
|
'TabMove',
|
||||||
|
|
||||||
|
'TabPinned',
|
||||||
|
'TabUnpinned',
|
||||||
|
|
||||||
|
'TabAddedToEssentials',
|
||||||
|
'TabRemovedFromEssentials',
|
||||||
|
|
||||||
|
'TabHide',
|
||||||
|
'TabShow',
|
||||||
|
|
||||||
|
'ZenTabIconChanged',
|
||||||
|
'ZenTabLabelChanged',
|
||||||
|
|
||||||
|
'TabGroupCreate',
|
||||||
|
'TabGroupRemoved',
|
||||||
|
'TabGrouped',
|
||||||
|
'TabUngrouped',
|
||||||
|
'TabGroupMoved',
|
||||||
|
];
|
||||||
|
const eventListener = this.#handleEvent.bind(this);
|
||||||
|
for (const event of kEvents) {
|
||||||
|
window.addEventListener(event, eventListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('unload', () => {
|
||||||
|
for (const event of kEvents) {
|
||||||
|
window.removeEventListener(event, eventListener);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#handleEvent(event) {
|
||||||
|
this.#propagateToOtherWindows(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
async #propagateToOtherWindows(event) {
|
||||||
|
if (this.#ignoreNextEvents) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.#waitForPromise) {
|
||||||
|
await this.#waitForPromise;
|
||||||
|
}
|
||||||
|
this.#waitForPromise = new Promise((resolve) => {
|
||||||
|
this.foreachWindowAsActive(async (browser) => {
|
||||||
|
if (browser.gZenWorkspaceWindowSync && !this.windowIsActive(browser)) {
|
||||||
|
await browser.gZenWorkspaceWindowSync.onExternalTabEvent(event);
|
||||||
|
}
|
||||||
|
}).then(() => {
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async onExternalTabEvent(event) {
|
||||||
|
this.#ignoreNextEvents = true;
|
||||||
|
switch (event.type) {
|
||||||
|
case 'TabClose':
|
||||||
|
this.#onTabClose(event);
|
||||||
|
break;
|
||||||
|
case 'TabOpen':
|
||||||
|
await this.#onTabOpen(event);
|
||||||
|
break;
|
||||||
|
case 'TabPinned':
|
||||||
|
this.#onTabPinned(event);
|
||||||
|
break;
|
||||||
|
case 'TabUnpinned':
|
||||||
|
this.#onTabUnpinned(event);
|
||||||
|
break;
|
||||||
|
case 'TabAddedToEssentials':
|
||||||
|
this.#onTabAddedToEssentials(event);
|
||||||
|
break;
|
||||||
|
case 'TabRemovedFromEssentials':
|
||||||
|
this.#onTabRemovedFromEssentials(event);
|
||||||
|
break;
|
||||||
|
case 'TabHide':
|
||||||
|
this.#onTabHide(event);
|
||||||
|
break;
|
||||||
|
case 'TabShow':
|
||||||
|
this.#onTabShow(event);
|
||||||
|
break;
|
||||||
|
case 'TabMove':
|
||||||
|
case 'TabGroupMoved':
|
||||||
|
this.#onTabMove(event);
|
||||||
|
break;
|
||||||
|
case 'ZenTabIconChanged':
|
||||||
|
this.#onTabIconChanged(event);
|
||||||
|
break;
|
||||||
|
case 'ZenTabLabelChanged':
|
||||||
|
this.#onTabLabelChanged(event);
|
||||||
|
break;
|
||||||
|
case 'TabGroupCreate':
|
||||||
|
this.#onTabGroupCreate(event);
|
||||||
|
break;
|
||||||
|
case 'TabGroupRemoved':
|
||||||
|
case 'TabGrouped':
|
||||||
|
case 'TabUngrouped':
|
||||||
|
// Tab grouping changes are automatically synced by Firefox
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.warn(`Unhandled event type: ${event.type}`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this.#ignoreNextEvents = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#getTabId(tab) {
|
||||||
|
return tab.getAttribute('zen-sync-id');
|
||||||
|
}
|
||||||
|
|
||||||
|
#getTabWithId(tabId) {
|
||||||
|
for (const tab of gZenWorkspaces.allStoredTabs) {
|
||||||
|
if (this.#getTabId(tab) === tabId) {
|
||||||
|
return tab;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
#onTabClose(event) {
|
||||||
|
const targetTab = event.target;
|
||||||
|
const tabId = this.#getTabId(targetTab);
|
||||||
|
const tabToClose = this.#getTabWithId(tabId);
|
||||||
|
if (tabToClose) {
|
||||||
|
gBrowser.removeTab(tabToClose);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#onTabPinned(event) {
|
||||||
|
const targetTab = event.target;
|
||||||
|
if (targetTab.hasAttribute('zen-essential')) {
|
||||||
|
return this.#onTabAddedToEssentials(event);
|
||||||
|
}
|
||||||
|
const tabId = this.#getTabId(targetTab);
|
||||||
|
const elementIndex = targetTab.elementIndex;
|
||||||
|
const tabToPin = this.#getTabWithId(tabId);
|
||||||
|
if (tabToPin) {
|
||||||
|
gBrowser.pinTab(tabToPin);
|
||||||
|
gBrowser.moveTabTo(tabToPin, { elementIndex, forceUngrouped: !!targetTab.group });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#onTabUnpinned(event) {
|
||||||
|
const targetTab = event.target;
|
||||||
|
const tabId = this.#getTabId(targetTab);
|
||||||
|
const tabToUnpin = this.#getTabWithId(tabId);
|
||||||
|
if (tabToUnpin) {
|
||||||
|
gBrowser.unpinTab(tabToUnpin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#onTabIconChanged(event) {
|
||||||
|
this.#updateTabIconAndLabel(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
#onTabLabelChanged(event) {
|
||||||
|
this.#updateTabIconAndLabel(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
#updateTabIconAndLabel(event) {
|
||||||
|
const targetTab = event.target;
|
||||||
|
const tabId = this.#getTabId(targetTab);
|
||||||
|
const tabToChange = this.#getTabWithId(tabId);
|
||||||
|
if (tabToChange && tabToChange.hasAttribute('pending')) {
|
||||||
|
gBrowser.setIcon(tabToChange, gBrowser.getIcon(targetTab));
|
||||||
|
gBrowser._setTabLabel(tabToChange, targetTab.label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#onTabAddedToEssentials(event) {
|
||||||
|
const targetTab = event.target;
|
||||||
|
const tabId = this.#getTabId(targetTab);
|
||||||
|
const tabToAdd = this.#getTabWithId(tabId);
|
||||||
|
if (tabToAdd) {
|
||||||
|
gZenPinnedTabManager.addToEssentials(tabToAdd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#onTabRemovedFromEssentials(event) {
|
||||||
|
const targetTab = event.target;
|
||||||
|
const tabId = this.#getTabId(targetTab);
|
||||||
|
const tabToRemove = this.#getTabWithId(tabId);
|
||||||
|
if (tabToRemove) {
|
||||||
|
gZenPinnedTabManager.removeFromEssentials(tabToRemove);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#onTabHide(event) {
|
||||||
|
const targetTab = event.target;
|
||||||
|
const tabId = this.#getTabId(targetTab);
|
||||||
|
const tabToHide = this.#getTabWithId(tabId);
|
||||||
|
if (tabToHide) {
|
||||||
|
gBrowser.hideTab(tabToHide);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#onTabShow(event) {
|
||||||
|
const targetTab = event.target;
|
||||||
|
const tabId = this.#getTabId(targetTab);
|
||||||
|
const tabToShow = this.#getTabWithId(tabId);
|
||||||
|
if (tabToShow) {
|
||||||
|
gBrowser.showTab(tabToShow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#onTabMove(event) {
|
||||||
|
const targetTab = event.target;
|
||||||
|
const tabId = this.#getTabId(targetTab);
|
||||||
|
const tabToMove = this.#getTabWithId(tabId);
|
||||||
|
const workspaceId = targetTab.getAttribute('zen-workspace-id');
|
||||||
|
const isEssential = targetTab.hasAttribute('zen-essential');
|
||||||
|
if (tabToMove) {
|
||||||
|
let tabSibling = targetTab.previousElementSibling;
|
||||||
|
let isFirst = false;
|
||||||
|
if (!tabSibling?.hasAttribute('zen-sync-id')) {
|
||||||
|
isFirst = true;
|
||||||
|
}
|
||||||
|
gBrowser.zenHandleTabMove(tabToMove, () => {
|
||||||
|
if (isFirst) {
|
||||||
|
let container;
|
||||||
|
if (isEssential) {
|
||||||
|
container = gZenWorkspaces.getEssentialsSection(tabToMove);
|
||||||
|
} else {
|
||||||
|
const workspaceElement = gZenWorkspaces.workspaceElement(workspaceId);
|
||||||
|
container = tabToMove.pinned
|
||||||
|
? workspaceElement.pinnedTabsContainer
|
||||||
|
: workspaceElement.tabsContainer;
|
||||||
|
}
|
||||||
|
container.insertBefore(tabToMove, container.firstChild);
|
||||||
|
} else {
|
||||||
|
let relativeTab = gZenWorkspaces.allStoredTabs.find((tab) => {
|
||||||
|
return this.#getTabId(tab) === this.#getTabId(tabSibling);
|
||||||
|
});
|
||||||
|
if (relativeTab) {
|
||||||
|
relativeTab.after(tabToMove);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async #onTabOpen(event) {
|
||||||
|
const targetTab = event.target;
|
||||||
|
const isPinned = targetTab.pinned;
|
||||||
|
const isEssential = isPinned && targetTab.hasAttribute('zen-essential');
|
||||||
|
if (!this.#getTabId(targetTab) && !targetTab.hasAttribute('zen-empty-tab')) {
|
||||||
|
const tabId = gZenUIManager.generateUuidv4();
|
||||||
|
targetTab.setAttribute('zen-sync-id', tabId);
|
||||||
|
}
|
||||||
|
const duplicatedTab = gBrowser.addTrustedTab(targetTab.linkedBrowser.currentURI.spec, {
|
||||||
|
createLazyBrowser: true,
|
||||||
|
essential: isEssential,
|
||||||
|
pinned: isPinned,
|
||||||
|
});
|
||||||
|
if (!isEssential) {
|
||||||
|
gZenWorkspaces.moveTabToWorkspace(
|
||||||
|
duplicatedTab,
|
||||||
|
targetTab.getAttribute('zen-workspace-id')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
duplicatedTab.setAttribute('zen-sync-id', targetTab.getAttribute('zen-sync-id'));
|
||||||
|
}
|
||||||
|
|
||||||
|
#onTabGroupCreate(event) {
|
||||||
|
void event;
|
||||||
|
//const targetGroup = event.target;
|
||||||
|
//const isSplitView = targetGroup.classList.contains('zen-split-view');
|
||||||
|
//const isFolder = targetGroup.isZenFolder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.gZenWorkspaceWindowSync = new nsZenWorkspaceWindowSync();
|
||||||
|
}
|
||||||
@@ -932,7 +932,6 @@ var gZenWorkspaces = new (class extends nsZenMultiWindowFeature {
|
|||||||
await this.workspaceBookmarks();
|
await this.workspaceBookmarks();
|
||||||
await this.initializeTabsStripSections();
|
await this.initializeTabsStripSections();
|
||||||
this._initializeEmptyTab();
|
this._initializeEmptyTab();
|
||||||
await gZenPinnedTabManager.refreshPinnedTabs({ init: true });
|
|
||||||
await this.changeWorkspace(activeWorkspace, { onInit: true });
|
await this.changeWorkspace(activeWorkspace, { onInit: true });
|
||||||
this.#fixTabPositions();
|
this.#fixTabPositions();
|
||||||
this.onWindowResize();
|
this.onWindowResize();
|
||||||
@@ -1471,11 +1470,6 @@ var gZenWorkspaces = new (class extends nsZenMultiWindowFeature {
|
|||||||
!tab.hasAttribute('zen-empty-tab') &&
|
!tab.hasAttribute('zen-empty-tab') &&
|
||||||
!tab.hasAttribute('zen-essential')
|
!tab.hasAttribute('zen-essential')
|
||||||
);
|
);
|
||||||
for (const tab of tabs) {
|
|
||||||
if (tab.pinned) {
|
|
||||||
await ZenPinnedTabsStorage.removePin(tab.getAttribute('zen-pin-id'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
gBrowser.removeTabs(tabs, {
|
gBrowser.removeTabs(tabs, {
|
||||||
animate: false,
|
animate: false,
|
||||||
skipSessionStore: true,
|
skipSessionStore: true,
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ export default [
|
|||||||
'ZenWorkspaceBookmarksStorage',
|
'ZenWorkspaceBookmarksStorage',
|
||||||
|
|
||||||
'gZenPinnedTabManager',
|
'gZenPinnedTabManager',
|
||||||
'ZenPinnedTabsStorage',
|
|
||||||
|
|
||||||
'gZenEmojiPicker',
|
'gZenEmojiPicker',
|
||||||
'gZenSessionStore',
|
'gZenSessionStore',
|
||||||
|
|||||||
Reference in New Issue
Block a user