diff --git a/src/browser/base/content/zen-preloaded.inc.xhtml b/src/browser/base/content/zen-preloaded.inc.xhtml
index ab5354092..76f32fd5f 100644
--- a/src/browser/base/content/zen-preloaded.inc.xhtml
+++ b/src/browser/base/content/zen-preloaded.inc.xhtml
@@ -6,7 +6,6 @@
# the window is fully loaded.
# Make sure they are loaded before the global-scripts.inc file.
-
diff --git a/src/browser/components/places/content/editBookmark-js.patch b/src/browser/components/places/content/editBookmark-js.patch
index 100ee182d..92392184f 100644
--- a/src/browser/components/places/content/editBookmark-js.patch
+++ b/src/browser/components/places/content/editBookmark-js.patch
@@ -1,5 +1,5 @@
diff --git a/browser/components/places/content/editBookmark.js b/browser/components/places/content/editBookmark.js
-index f562f19741d882d92365da531b55e2810a0e79ea..9339e1158b074c41fc19bf91cbfde3c4016594b9 100644
+index f562f19741d882d92365da531b55e2810a0e79ea..a68ce8191314845c589f3a9f14b56028e0532628 100644
--- a/browser/components/places/content/editBookmark.js
+++ b/browser/components/places/content/editBookmark.js
@@ -387,6 +387,10 @@ var gEditItemOverlay = {
@@ -31,34 +31,11 @@ index f562f19741d882d92365da531b55e2810a0e79ea..9339e1158b074c41fc19bf91cbfde3c4
}
break;
}
-@@ -1280,6 +1288,148 @@ var gEditItemOverlay = {
+@@ -1280,6 +1288,128 @@ var gEditItemOverlay = {
get bookmarkState() {
return this._bookmarkState;
},
+
-+ async _initWorkspaceSelector() {
-+ if(document.documentElement.getAttribute("windowtype") === "Places:Organizer") {
-+ return;
-+ }
-+ this._workspaces = await ZenWorkspacesStorage.getWorkspaces();
-+
-+ const selectElement = this._workspaceSelect;
-+
-+ // Clear any existing options
-+ while (selectElement.firstChild) {
-+ selectElement.removeChild(selectElement.firstChild);
-+ }
-+
-+ // For each workspace, create an option element
-+ for (let workspace of this._workspaces) {
-+ const option = document.createElementNS("http://www.w3.org/1999/xhtml", "option");
-+ option.textContent = workspace.name;
-+ option.value = workspace.uuid;
-+ selectElement.appendChild(option);
-+ }
-+
-+ selectElement.disabled = this.readOnly;
-+ },
+ async onWorkspaceSelectionChange(event) {
+ if(document.documentElement.getAttribute("windowtype") === "Places:Organizer") {
+ return;
@@ -129,7 +106,10 @@ index f562f19741d882d92365da531b55e2810a0e79ea..9339e1158b074c41fc19bf91cbfde3c4
+ if(document.documentElement.getAttribute("windowtype") === "Places:Organizer") {
+ return;
+ }
-+ this._workspaces = await ZenWorkspacesStorage.getWorkspaces();
++ const { ZenSessionStore } = ChromeUtils.importESModule(
++ "resource:///modules/zen/ZenSessionManager.sys.mjs"
++ );
++ this._workspaces = ZenSessionStore.getClonedSpaces();
+ const workspaceList = this._workspaceList;
+ if(aInfo.node?.bookmarkGuid) {
+ this._selectedWorkspaces = await ZenWorkspaceBookmarksStorage.getBookmarkWorkspaces(aInfo.node.bookmarkGuid);
@@ -180,7 +160,7 @@ index f562f19741d882d92365da531b55e2810a0e79ea..9339e1158b074c41fc19bf91cbfde3c4
};
ChromeUtils.defineLazyGetter(gEditItemOverlay, "_folderTree", () => {
-@@ -1318,6 +1468,9 @@ for (let elt of [
+@@ -1318,6 +1448,9 @@ for (let elt of [
"locationField",
"keywordField",
"tagsField",
diff --git a/src/browser/components/sessionstore/SessionStore-sys-mjs.patch b/src/browser/components/sessionstore/SessionStore-sys-mjs.patch
index 1d9c6ab95..c8cbd23a3 100644
--- a/src/browser/components/sessionstore/SessionStore-sys-mjs.patch
+++ b/src/browser/components/sessionstore/SessionStore-sys-mjs.patch
@@ -1,5 +1,5 @@
diff --git a/browser/components/sessionstore/SessionStore.sys.mjs b/browser/components/sessionstore/SessionStore.sys.mjs
-index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..cc9185308c83abfa173041a10edbcb9bbfa3d264 100644
+index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..5ce4eb0b21cf4ed2b8e7c6ad0c57e77416a2ab48 100644
--- a/browser/components/sessionstore/SessionStore.sys.mjs
+++ b/browser/components/sessionstore/SessionStore.sys.mjs
@@ -127,6 +127,8 @@ const TAB_EVENTS = [
@@ -56,9 +56,9 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..cc9185308c83abfa173041a10edbcb9b
+ for (let window of this._browserWindows) {
+ windowPromises.push(lazy.TabStateFlusher.flushWindow(window));
+ }
-+ aWindow._zenPromiseNewWindowRestored = new Promise((resolve) => Promise.all(windowPromises).finally(() => {
-+ lazy.ZenSessionStore.restoreNewWindow(aWindow, this, resolve);
-+ }));
++ Promise.all(windowPromises).finally(() => {
++ lazy.ZenSessionStore.restoreNewWindow(aWindow, this);
++ });
+ }
if (this._restoreLastWindow && aWindow.toolbar.visible) {
@@ -137,7 +137,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..cc9185308c83abfa173041a10edbcb9b
// window data is still in _statesToRestore
continue;
}
-@@ -5625,11 +5650,15 @@ var SessionStoreInternal = {
+@@ -5625,11 +5650,16 @@ var SessionStoreInternal = {
}
let tabbrowser = aWindow.gBrowser;
@@ -150,11 +150,12 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..cc9185308c83abfa173041a10edbcb9b
+ winData.activeZenSpace = aWindow.gZenWorkspaces?.activeWorkspace || null;
+ winData.splitViewData = aWindow.gZenViewSplitter?.storeDataForSessionStore();
+ winData.folders = aWindow.gZenFolders?.storeDataForSessionStore() || [];
++ winData.spaces = aWindow.gZenWorkspaces?.getWorkspaces();
+
// update the internal state data for this window
for (let tab of tabs) {
if (tab == aWindow.FirefoxViewHandler.tab) {
-@@ -5652,7 +5681,7 @@ var SessionStoreInternal = {
+@@ -5652,7 +5682,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,
// since it's only inserted into the tab strip after it's selected).
if (aWindow.FirefoxViewHandler.tab?.selected) {
@@ -163,7 +164,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..cc9185308c83abfa173041a10edbcb9b
winData.title = tabbrowser.tabs[0].label;
}
winData.selected = selectedIndex;
-@@ -5765,8 +5794,8 @@ var SessionStoreInternal = {
+@@ -5765,8 +5795,8 @@ var SessionStoreInternal = {
// selectTab represents.
let selectTab = 0;
if (overwriteTabs) {
@@ -174,17 +175,17 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..cc9185308c83abfa173041a10edbcb9b
selectTab = Math.min(selectTab, winData.tabs.length);
}
-@@ -5809,6 +5838,9 @@ var SessionStoreInternal = {
+@@ -5809,6 +5839,9 @@ var SessionStoreInternal = {
winData.tabs,
winData.groups ?? []
);
+ aWindow.gZenFolders?.restoreDataFromSessionStore(winData.folders);
+ aWindow.gZenViewSplitter?.restoreDataFromSessionStore(winData.splitViewData);
-+ aWindow.gZenWorkspaces.activeWorkspace = winData.activeZenSpace || null;
++ aWindow.gZenWorkspaces?.restoreWorkspacesFromSessionStore(winData);
this._log.debug(
`restoreWindow, createTabsForSessionRestore returned ${tabs.length} tabs`
);
-@@ -6372,6 +6404,25 @@ var SessionStoreInternal = {
+@@ -6372,6 +6405,25 @@ var SessionStoreInternal = {
// Most of tabData has been restored, now continue with restoring
// attributes that may trigger external events.
@@ -210,7 +211,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..cc9185308c83abfa173041a10edbcb9b
if (tabData.pinned) {
tabbrowser.pinTab(tab);
-@@ -7290,7 +7341,7 @@ var SessionStoreInternal = {
+@@ -7290,7 +7342,7 @@ var SessionStoreInternal = {
let groupsToSave = new Map();
for (let tIndex = 0; tIndex < window.tabs.length; ) {
@@ -219,7 +220,7 @@ index 2c2f43bf743ef458b378e85e9ed44a971711e1d9..cc9185308c83abfa173041a10edbcb9b
// Adjust window.selected
if (tIndex + 1 < window.selected) {
window.selected -= 1;
-@@ -7305,7 +7356,7 @@ var SessionStoreInternal = {
+@@ -7305,7 +7357,7 @@ var SessionStoreInternal = {
);
// We don't want to increment tIndex here.
continue;
diff --git a/src/zen/common/modules/ZenStartup.mjs b/src/zen/common/modules/ZenStartup.mjs
index 942dd27ee..d85531296 100644
--- a/src/zen/common/modules/ZenStartup.mjs
+++ b/src/zen/common/modules/ZenStartup.mjs
@@ -12,12 +12,7 @@ class ZenStartup {
isReady = false;
- async init() {
- // important: We do this to ensure that some firefox components
- // are initialized before we start our own initialization.
- // please, do not remove this line and if you do, make sure to
- // test the startup process.
- await new Promise((resolve) => setTimeout(resolve, 0));
+ init() {
this.openWatermark();
this.#initBrowserBackground();
this.#changeSidebarLocation();
@@ -97,6 +92,7 @@ class ZenStartup {
// Just in case we didn't get the right size.
gZenUIManager.updateTabsToolbar();
this.closeWatermark();
+ document.getElementById('tabbrowser-arrowscrollbox').setAttribute('orient', 'vertical');
this.isReady = true;
});
}
diff --git a/src/zen/common/zen-sets.js b/src/zen/common/zen-sets.js
index b7b6c00ad..25cfe4b01 100644
--- a/src/zen/common/zen-sets.js
+++ b/src/zen/common/zen-sets.js
@@ -6,138 +6,135 @@ document.addEventListener(
'MozBeforeInitialXULLayout',
() => {
// defined in browser-sets.inc
- document
- .getElementById('zenCommandSet')
-
- .addEventListener('command', (event) => {
- switch (event.target.id) {
- case 'cmd_zenCompactModeToggle':
- gZenCompactModeManager.toggle();
- break;
- case 'cmd_zenCompactModeShowSidebar':
- gZenCompactModeManager.toggleSidebar();
- break;
- case 'cmd_toggleCompactModeIgnoreHover':
- gZenCompactModeManager.toggle(true);
- break;
- case 'cmd_zenWorkspaceForward':
- gZenWorkspaces.changeWorkspaceShortcut();
- break;
- case 'cmd_zenWorkspaceBackward':
- gZenWorkspaces.changeWorkspaceShortcut(-1);
- break;
- case 'cmd_zenSplitViewGrid':
- gZenViewSplitter.toggleShortcut('grid');
- break;
- case 'cmd_zenSplitViewVertical':
- gZenViewSplitter.toggleShortcut('vsep');
- break;
- case 'cmd_zenSplitViewHorizontal':
- gZenViewSplitter.toggleShortcut('hsep');
- break;
- case 'cmd_zenSplitViewUnsplit':
- gZenViewSplitter.toggleShortcut('unsplit');
- break;
- case 'cmd_zenSplitViewContextMenu':
- gZenViewSplitter.contextSplitTabs();
- break;
- case 'cmd_zenCopyCurrentURLMarkdown':
- gZenCommonActions.copyCurrentURLAsMarkdownToClipboard();
- break;
- case 'cmd_zenCopyCurrentURL':
- gZenCommonActions.copyCurrentURLToClipboard();
- break;
- case 'cmd_zenPinnedTabReset':
- gZenPinnedTabManager.resetPinnedTab(gBrowser.selectedTab);
- break;
- case 'cmd_zenPinnedTabResetNoTab':
- gZenPinnedTabManager.resetPinnedTab();
- break;
- case 'cmd_zenToggleSidebar':
- gZenVerticalTabsManager.toggleExpand();
- break;
- case 'cmd_zenOpenZenThemePicker':
- gZenThemePicker.openThemePicker(event);
- break;
- case 'cmd_zenChangeWorkspaceTab':
- gZenWorkspaces.changeTabWorkspace(
- event.sourceEvent.target.getAttribute('zen-workspace-id')
- );
- break;
- case 'cmd_zenToggleTabsOnRight':
- gZenVerticalTabsManager.toggleTabsOnRight();
- break;
- case 'cmd_zenSplitViewLinkInNewTab':
- gZenViewSplitter.splitLinkInNewTab();
- break;
- case 'cmd_zenNewEmptySplit':
- setTimeout(() => {
- gZenViewSplitter.createEmptySplit();
- }, 0);
- break;
- case 'cmd_zenReplacePinnedUrlWithCurrent':
- gZenPinnedTabManager.replacePinnedUrlWithCurrent();
- break;
- case 'cmd_contextZenAddToEssentials':
- gZenPinnedTabManager.addToEssentials();
- break;
- case 'cmd_contextZenRemoveFromEssentials':
- gZenPinnedTabManager.removeEssentials();
- break;
- case 'cmd_zenCtxDeleteWorkspace':
- gZenWorkspaces.contextDeleteWorkspace(event);
- break;
- case 'cmd_zenChangeWorkspaceName':
- gZenVerticalTabsManager.renameTabStart({
- target: gZenWorkspaces.activeWorkspaceIndicator.querySelector(
- '.zen-current-workspace-indicator-name'
- ),
- });
- break;
- case 'cmd_zenChangeWorkspaceIcon':
- gZenWorkspaces.changeWorkspaceIcon();
- break;
- case 'cmd_zenReorderWorkspaces':
- gZenUIManager.showToast('zen-workspaces-how-to-reorder-title', {
- timeout: 9000,
- descriptionId: 'zen-workspaces-how-to-reorder-desc',
- });
- break;
- case 'cmd_zenOpenWorkspaceCreation':
- gZenWorkspaces.openWorkspaceCreation(event);
- break;
- case 'cmd_zenOpenFolderCreation':
- gZenFolders.createFolder([], {
- renameFolder: true,
- });
- break;
- case 'cmd_zenTogglePinTab': {
- const currentTab = gBrowser.selectedTab;
- if (currentTab && !currentTab.hasAttribute('zen-empty-tab')) {
- if (currentTab.pinned) {
- gBrowser.unpinTab(currentTab);
- } else {
- gBrowser.pinTab(currentTab);
- }
+ document.getElementById('zenCommandSet').addEventListener('command', (event) => {
+ switch (event.target.id) {
+ case 'cmd_zenCompactModeToggle':
+ gZenCompactModeManager.toggle();
+ break;
+ case 'cmd_zenCompactModeShowSidebar':
+ gZenCompactModeManager.toggleSidebar();
+ break;
+ case 'cmd_toggleCompactModeIgnoreHover':
+ gZenCompactModeManager.toggle(true);
+ break;
+ case 'cmd_zenWorkspaceForward':
+ gZenWorkspaces.changeWorkspaceShortcut();
+ break;
+ case 'cmd_zenWorkspaceBackward':
+ gZenWorkspaces.changeWorkspaceShortcut(-1);
+ break;
+ case 'cmd_zenSplitViewGrid':
+ gZenViewSplitter.toggleShortcut('grid');
+ break;
+ case 'cmd_zenSplitViewVertical':
+ gZenViewSplitter.toggleShortcut('vsep');
+ break;
+ case 'cmd_zenSplitViewHorizontal':
+ gZenViewSplitter.toggleShortcut('hsep');
+ break;
+ case 'cmd_zenSplitViewUnsplit':
+ gZenViewSplitter.toggleShortcut('unsplit');
+ break;
+ case 'cmd_zenSplitViewContextMenu':
+ gZenViewSplitter.contextSplitTabs();
+ break;
+ case 'cmd_zenCopyCurrentURLMarkdown':
+ gZenCommonActions.copyCurrentURLAsMarkdownToClipboard();
+ break;
+ case 'cmd_zenCopyCurrentURL':
+ gZenCommonActions.copyCurrentURLToClipboard();
+ break;
+ case 'cmd_zenPinnedTabReset':
+ gZenPinnedTabManager.resetPinnedTab(gBrowser.selectedTab);
+ break;
+ case 'cmd_zenPinnedTabResetNoTab':
+ gZenPinnedTabManager.resetPinnedTab();
+ break;
+ case 'cmd_zenToggleSidebar':
+ gZenVerticalTabsManager.toggleExpand();
+ break;
+ case 'cmd_zenOpenZenThemePicker':
+ gZenThemePicker.openThemePicker(event);
+ break;
+ case 'cmd_zenChangeWorkspaceTab':
+ gZenWorkspaces.changeTabWorkspace(
+ event.sourceEvent.target.getAttribute('zen-workspace-id')
+ );
+ break;
+ case 'cmd_zenToggleTabsOnRight':
+ gZenVerticalTabsManager.toggleTabsOnRight();
+ break;
+ case 'cmd_zenSplitViewLinkInNewTab':
+ gZenViewSplitter.splitLinkInNewTab();
+ break;
+ case 'cmd_zenNewEmptySplit':
+ setTimeout(() => {
+ gZenViewSplitter.createEmptySplit();
+ }, 0);
+ break;
+ case 'cmd_zenReplacePinnedUrlWithCurrent':
+ gZenPinnedTabManager.replacePinnedUrlWithCurrent();
+ break;
+ case 'cmd_contextZenAddToEssentials':
+ gZenPinnedTabManager.addToEssentials();
+ break;
+ case 'cmd_contextZenRemoveFromEssentials':
+ gZenPinnedTabManager.removeEssentials();
+ break;
+ case 'cmd_zenCtxDeleteWorkspace':
+ gZenWorkspaces.contextDeleteWorkspace(event);
+ break;
+ case 'cmd_zenChangeWorkspaceName':
+ gZenVerticalTabsManager.renameTabStart({
+ target: gZenWorkspaces.activeWorkspaceIndicator.querySelector(
+ '.zen-current-workspace-indicator-name'
+ ),
+ });
+ break;
+ case 'cmd_zenChangeWorkspaceIcon':
+ gZenWorkspaces.changeWorkspaceIcon();
+ break;
+ case 'cmd_zenReorderWorkspaces':
+ gZenUIManager.showToast('zen-workspaces-how-to-reorder-title', {
+ timeout: 9000,
+ descriptionId: 'zen-workspaces-how-to-reorder-desc',
+ });
+ break;
+ case 'cmd_zenOpenWorkspaceCreation':
+ gZenWorkspaces.openWorkspaceCreation(event);
+ break;
+ case 'cmd_zenOpenFolderCreation':
+ gZenFolders.createFolder([], {
+ renameFolder: true,
+ });
+ break;
+ case 'cmd_zenTogglePinTab': {
+ const currentTab = gBrowser.selectedTab;
+ if (currentTab && !currentTab.hasAttribute('zen-empty-tab')) {
+ if (currentTab.pinned) {
+ gBrowser.unpinTab(currentTab);
+ } else {
+ gBrowser.pinTab(currentTab);
}
- break;
}
- case 'cmd_zenCloseUnpinnedTabs':
- gZenWorkspaces.closeAllUnpinnedTabs();
- break;
- case 'cmd_zenUnloadWorkspace': {
- gZenWorkspaces.unloadWorkspace();
- break;
- }
- default:
- gZenGlanceManager.handleMainCommandSet(event);
- if (event.target.id.startsWith('cmd_zenWorkspaceSwitch')) {
- const index = parseInt(event.target.id.replace('cmd_zenWorkspaceSwitch', ''), 10) - 1;
- gZenWorkspaces.shortcutSwitchTo(index);
- }
- break;
+ break;
}
- });
+ case 'cmd_zenCloseUnpinnedTabs':
+ gZenWorkspaces.closeAllUnpinnedTabs();
+ break;
+ case 'cmd_zenUnloadWorkspace': {
+ gZenWorkspaces.unloadWorkspace();
+ break;
+ }
+ default:
+ gZenGlanceManager.handleMainCommandSet(event);
+ if (event.target.id.startsWith('cmd_zenWorkspaceSwitch')) {
+ const index = parseInt(event.target.id.replace('cmd_zenWorkspaceSwitch', ''), 10) - 1;
+ gZenWorkspaces.shortcutSwitchTo(index);
+ }
+ break;
+ }
+ });
},
{ once: true }
);
diff --git a/src/zen/sessionstore/ZenSessionManager.sys.mjs b/src/zen/sessionstore/ZenSessionManager.sys.mjs
index f4e57c09c..724087d98 100644
--- a/src/zen/sessionstore/ZenSessionManager.sys.mjs
+++ b/src/zen/sessionstore/ZenSessionManager.sys.mjs
@@ -76,6 +76,39 @@ export class nsZenSessionManager {
}
}
+ /**
+ * Gets the spaces data from the Places database for migration.
+ * This is only called once during the first run after updating
+ * to a version that uses the new session manager.
+ */
+ async #getSpacesFromDBForMigration() {
+ try {
+ const { PlacesUtils } = ChromeUtils.importESModule(
+ 'resource://gre/modules/PlacesUtils.sys.mjs'
+ );
+ const db = await PlacesUtils.promiseDBConnection();
+ const rows = await db.executeCached('SELECT * FROM zen_workspaces ORDER BY created_at ASC');
+ this._migrationSpaceData = rows.map((row) => ({
+ uuid: row.getResultByName('uuid'),
+ name: row.getResultByName('name'),
+ icon: row.getResultByName('icon'),
+ containerTabId: row.getResultByName('container_id') ?? 0,
+ position: row.getResultByName('position'),
+ theme: row.getResultByName('theme_type')
+ ? {
+ type: row.getResultByName('theme_type'),
+ gradientColors: JSON.parse(row.getResultByName('theme_colors')),
+ opacity: row.getResultByName('theme_opacity'),
+ rotation: row.getResultByName('theme_rotation'),
+ texture: row.getResultByName('theme_texture'),
+ }
+ : null,
+ }));
+ } catch {
+ /* ignore errors during migration */
+ }
+ }
+
/**
* Reads the session file and populates the sidebar object.
* This should be only called once at startup.
@@ -83,7 +116,12 @@ export class nsZenSessionManager {
*/
async readFile() {
try {
- await this.#file.load();
+ let promises = [];
+ promises.push(this.#file.load());
+ if (!Services.prefs.getBoolPref(MIGRATION_PREF, false)) {
+ promises.push(this.#getSpacesFromDBForMigration());
+ }
+ await Promise.all(promises);
} catch (e) {
console.error('ZenSessionManager: Failed to read session file', e);
}
@@ -104,6 +142,10 @@ export class nsZenSessionManager {
// gotten the opportunity to save the session yet.
if (!Services.prefs.getBoolPref(MIGRATION_PREF, false)) {
Services.prefs.setBoolPref(MIGRATION_PREF, true);
+ for (const winData of initialState.windows || []) {
+ winData.spaces = this._migrationSpaceData || [];
+ }
+ delete this._migrationSpaceData;
return;
}
// If there's no initial state, nothing to restore. This would
@@ -226,6 +268,7 @@ export class nsZenSessionManager {
sidebarData.folders = state.windows[0].folders;
sidebarData.splitViewData = state.windows[0].splitViewData;
sidebarData.groups = state.windows[0].groups;
+ sidebarData.spaces = state.windows[0].spaces;
}
/**
@@ -245,6 +288,7 @@ export class nsZenSessionManager {
aWindowData.splitViewData = sidebar.splitViewData;
aWindowData.folders = sidebar.folders;
aWindowData.groups = sidebar.groups;
+ aWindowData.spaces = sidebar.spaces;
}
/**
@@ -263,9 +307,9 @@ export class nsZenSessionManager {
* into a race condition if we try to restore the window synchronously
* here.
*/
- restoreNewWindow(aWindow, SessionStoreInternal, resolvePromise) {
+ restoreNewWindow(aWindow, SessionStoreInternal) {
if (aWindow.gZenWorkspaces?.privateWindowOrDisabled) {
- return resolvePromise();
+ return;
}
this.log('Restoring new window with Zen session data');
const state = lazy.SessionStore.getCurrentState(true);
@@ -300,17 +344,24 @@ export class nsZenSessionManager {
const newState = { windows: [newWindow] };
this.log(`Cloning window with ${newWindow.tabs.length} tabs`);
- aWindow.addEventListener(
- 'SSWindowRestored',
- () => {
- lazy.setTimeout(resolvePromise);
- },
- { once: true }
- );
-
SessionStoreInternal._deferredInitialState = newState;
SessionStoreInternal.initializeWindow(aWindow, newState);
}
+
+ /**
+ * Gets the cloned spaces data from the sidebar object.
+ * This is used during migration to restore spaces into
+ * the initial session state.
+ *
+ * @returns {Array} The cloned spaces data.
+ */
+ getClonedSpaces() {
+ const sidebar = this.#sidebar;
+ if (!sidebar || !sidebar.spaces) {
+ return [];
+ }
+ return Cu.cloneInto(sidebar.spaces, {});
+ }
}
export const ZenSessionStore = new nsZenSessionManager();
diff --git a/src/zen/sessionstore/ZenWindowSync.sys.mjs b/src/zen/sessionstore/ZenWindowSync.sys.mjs
index 72944ad0e..c5eef9f15 100644
--- a/src/zen/sessionstore/ZenWindowSync.sys.mjs
+++ b/src/zen/sessionstore/ZenWindowSync.sys.mjs
@@ -742,24 +742,11 @@ class nsZenWindowSync {
/* Mark: Public API */
- shouldLoadTab(aTab) {
- if (!lazy.gWindowSyncEnabled) {
- // Since we are never going to sync the tab, we can always load it.
- return true;
- }
- if (aTab._zenContentsVisible) {
- // This tab is already active in this window.
- return true;
- }
- // We don't want to trigger a new browser kick-off if there's
- // another window where this tab is already active.
- return !this.#getActiveTabFromOtherWindows(
- aTab.ownerGlobal,
- aTab.id,
- (tab) => tab?._zenContentsVisible
- );
- }
-
+ /**
+ * Sets the initial pinned state for a tab across all windows.
+ *
+ * @param {Object} aTab - The tab to set the pinned state for.
+ */
setPinnedTabState(aTab) {
const state = this.#getTabState(aTab);
const initialState = {
@@ -774,6 +761,23 @@ class nsZenWindowSync {
});
}
+ /**
+ * Propagates the workspaces to all windows.
+ * @param {Array} aWorkspaces - The workspaces to propagate.
+ */
+ propagateWorkspacesToAllWindows(aWorkspaces) {
+ this.#runOnAllWindows(null, (win) => {
+ win.gZenWorkspaces.propagateWorkspaces(aWorkspaces);
+ });
+ }
+
+ /**
+ * Moves all tabs from a window to a synced workspace in another window.
+ * If no synced window exists, creates a new one.
+ *
+ * @param {Window} aWindow - The window to move tabs from.
+ * @param {string} aWorkspaceId - The ID of the workspace to move tabs to.
+ */
moveTabsToSyncedWorkspace(aWindow, aWorkspaceId) {
const tabsToMove = aWindow.gZenWorkspaces.allStoredTabs.filter(
(tab) => !tab.hasAttribute('zen-empty-tab')
diff --git a/src/zen/tabs/ZenPinnedTabManager.mjs b/src/zen/tabs/ZenPinnedTabManager.mjs
index c39c439d4..182fa63eb 100644
--- a/src/zen/tabs/ZenPinnedTabManager.mjs
+++ b/src/zen/tabs/ZenPinnedTabManager.mjs
@@ -64,7 +64,7 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
this._resolvePinnedInitializedInternal = resolve;
});
- async init() {
+ init() {
if (!this.enabled) {
return;
}
diff --git a/src/zen/tests/container_essentials/browser_container_auto_switch.js b/src/zen/tests/container_essentials/browser_container_auto_switch.js
index b46279668..1e9a94826 100644
--- a/src/zen/tests/container_essentials/browser_container_auto_switch.js
+++ b/src/zen/tests/container_essentials/browser_container_auto_switch.js
@@ -6,7 +6,7 @@
add_task(async function test_Container_Essentials_Auto_Swithc() {
await gZenWorkspaces.createAndSaveWorkspace('Container Profile 1', undefined, false, 1);
const workspaces = await gZenWorkspaces._workspaces();
- ok(workspaces.workspaces.length === 2, 'Two workspaces should exist.');
+ ok(workspaces.length === 2, 'Two workspaces should exist.');
let newTab = BrowserTestUtils.addTab(gBrowser, 'about:blank', {
skipAnimation: true,
@@ -27,11 +27,11 @@ add_task(async function test_Container_Essentials_Auto_Swithc() {
const newWorkspaceUUID = gZenWorkspaces.activeWorkspace;
Assert.equal(
gZenWorkspaces.activeWorkspace,
- workspaces.workspaces[1].uuid,
+ workspaces[1].uuid,
'The new workspace should be active.'
);
// Change to the original workspace, there should be no essential tabs
- await gZenWorkspaces.changeWorkspace(workspaces.workspaces[0]);
+ await gZenWorkspaces.changeWorkspace(workspaces[0]);
await gZenWorkspaces.removeWorkspace(newWorkspaceUUID);
});
diff --git a/src/zen/tests/container_essentials/browser_container_specific_essentials.js b/src/zen/tests/container_essentials/browser_container_specific_essentials.js
index a376c0165..3d0b82ec8 100644
--- a/src/zen/tests/container_essentials/browser_container_specific_essentials.js
+++ b/src/zen/tests/container_essentials/browser_container_specific_essentials.js
@@ -6,9 +6,9 @@
add_task(async function test_Check_Creation() {
await gZenWorkspaces.createAndSaveWorkspace('Container Profile 1', undefined, false, 1);
const workspaces = await gZenWorkspaces._workspaces();
- ok(workspaces.workspaces.length === 2, 'Two workspaces should exist.');
+ ok(workspaces.length === 2, 'Two workspaces should exist.');
- await gZenWorkspaces.changeWorkspace(workspaces.workspaces[1]);
+ await gZenWorkspaces.changeWorkspace(workspaces[1]);
let newTab = BrowserTestUtils.addTab(gBrowser, 'about:blank', {
skipAnimation: true,
userContextId: 1,
@@ -28,7 +28,7 @@ add_task(async function test_Check_Creation() {
const newWorkspaceUUID = gZenWorkspaces.activeWorkspace;
// Change to the original workspace, there should be no essential tabs
- await gZenWorkspaces.changeWorkspace(workspaces.workspaces[0]);
+ await gZenWorkspaces.changeWorkspace(workspaces[0]);
ok(
!gBrowser.tabs.find(
(t) => t.hasAttribute('zen-essential') && t.getAttribute('usercontextid') == 1
diff --git a/src/zen/tests/workspaces/browser_basic_workspaces.js b/src/zen/tests/workspaces/browser_basic_workspaces.js
index 2a6ac2017..dc0bc7767 100644
--- a/src/zen/tests/workspaces/browser_basic_workspaces.js
+++ b/src/zen/tests/workspaces/browser_basic_workspaces.js
@@ -9,9 +9,9 @@ add_task(async function test_Check_Creation() {
const currentWorkspaceUUID = gZenWorkspaces.activeWorkspace;
await gZenWorkspaces.createAndSaveWorkspace('Test Workspace 2');
const workspaces = await gZenWorkspaces._workspaces();
- ok(workspaces.workspaces.length === 2, 'Two workspaces should exist.');
+ ok(workspaces.length === 2, 'Two workspaces should exist.');
ok(
- currentWorkspaceUUID !== workspaces.workspaces[1].uuid,
+ currentWorkspaceUUID !== workspaces[1].uuid,
'The new workspace should be different from the current one.'
);
@@ -26,7 +26,7 @@ add_task(async function test_Check_Creation() {
const workspacesAfterRemove = await gZenWorkspaces._workspaces();
ok(workspacesAfterRemove.workspaces.length === 1, 'One workspace should exist.');
ok(
- workspacesAfterRemove.workspaces[0].uuid === currentWorkspaceUUID,
+ workspacesAfterRemove[0].uuid === currentWorkspaceUUID,
'The workspace should be the one we started with.'
);
ok(gBrowser.tabs.length === 2, 'There should be one tab.');
diff --git a/src/zen/tests/workspaces/browser_change_to_empty.js b/src/zen/tests/workspaces/browser_change_to_empty.js
index c7f6f90b7..9913a988f 100644
--- a/src/zen/tests/workspaces/browser_change_to_empty.js
+++ b/src/zen/tests/workspaces/browser_change_to_empty.js
@@ -19,6 +19,6 @@ add_task(async function test_Change_To_Empty() {
);
const workspacesAfterRemove = await gZenWorkspaces._workspaces();
- ok(workspacesAfterRemove.workspaces.length === 1, 'One workspace should exist.');
+ ok(workspacesAfterRemove.length === 1, 'One workspace should exist.');
ok(gBrowser.tabs.length === 2, 'There should be two tabs.');
});
diff --git a/src/zen/tests/workspaces/browser_workspace_bookmarks.js b/src/zen/tests/workspaces/browser_workspace_bookmarks.js
index 02734f5dd..4a9e7ec48 100644
--- a/src/zen/tests/workspaces/browser_workspace_bookmarks.js
+++ b/src/zen/tests/workspaces/browser_workspace_bookmarks.js
@@ -107,9 +107,9 @@ add_task(async function test_workspace_bookmark() {
await withBookmarksShowing(async () => {
await gZenWorkspaces.createAndSaveWorkspace('Test Workspace 2');
const workspaces = await gZenWorkspaces._workspaces();
- ok(workspaces.workspaces.length === 2, 'Two workspaces should exist.');
- const firstWorkspace = workspaces.workspaces[0];
- const secondWorkspace = workspaces.workspaces[1];
+ ok(workspaces.length === 2, 'Two workspaces should exist.');
+ const firstWorkspace = workspaces[0];
+ const secondWorkspace = workspaces[1];
ok(
firstWorkspace.uuid !== secondWorkspace.uuid,
'The new workspace should be different from the current one.'
diff --git a/src/zen/urlbar/ZenUBGlobalActions.sys.mjs b/src/zen/urlbar/ZenUBGlobalActions.sys.mjs
index 12d288346..bc161200a 100644
--- a/src/zen/urlbar/ZenUBGlobalActions.sys.mjs
+++ b/src/zen/urlbar/ZenUBGlobalActions.sys.mjs
@@ -76,7 +76,7 @@ const globalActionsTemplate = [
command: 'cmd_zenWorkspaceForward',
icon: 'chrome://browser/skin/zen-icons/forward.svg',
isAvailable: (window) => {
- return window.gZenWorkspaces._workspaceCache.workspaces.length > 1;
+ return window.gZenWorkspaces._workspaceCache.length > 1;
},
},
{
@@ -85,7 +85,7 @@ const globalActionsTemplate = [
icon: 'chrome://browser/skin/zen-icons/back.svg',
isAvailable: (window) => {
// This also covers the case of being in private mode
- return window.gZenWorkspaces._workspaceCache.workspaces.length > 1;
+ return window.gZenWorkspaces._workspaceCache.length > 1;
},
},
{
diff --git a/src/zen/workspaces/ZenGradientGenerator.mjs b/src/zen/workspaces/ZenGradientGenerator.mjs
index 6e52f5a78..a1495d4ed 100644
--- a/src/zen/workspaces/ZenGradientGenerator.mjs
+++ b/src/zen/workspaces/ZenGradientGenerator.mjs
@@ -1332,7 +1332,7 @@ export class nsZenThemePicker extends nsZenMultiWindowFeature {
}
// Do not rebuild if the workspace is not the same as the current one
- const windowWorkspace = await browser.gZenWorkspaces.getActiveWorkspace();
+ const windowWorkspace = browser.gZenWorkspaces.getActiveWorkspace();
if (windowWorkspace.uuid !== uuid) {
return;
}
@@ -1630,13 +1630,12 @@ export class nsZenThemePicker extends nsZenMultiWindowFeature {
};
});
const gradient = nsZenThemePicker.getTheme(colors, this.currentOpacity, this.currentTexture);
- let currentWorkspace = await gZenWorkspaces.getActiveWorkspace();
+ let currentWorkspace = gZenWorkspaces.getActiveWorkspace();
if (!skipSave) {
- await ZenWorkspacesStorage.saveWorkspaceTheme(currentWorkspace.uuid, gradient);
- await gZenWorkspaces._propagateWorkspaceData();
+ currentWorkspace.theme = gradient;
+ gZenWorkspaces.saveWorkspace(currentWorkspace);
gZenUIManager.showToast('zen-panel-ui-gradient-generator-saved-message');
- currentWorkspace = await gZenWorkspaces.getActiveWorkspace();
}
await this.onWorkspaceChange(currentWorkspace, skipSave, skipSave ? gradient : null);
@@ -1691,7 +1690,7 @@ export class nsZenThemePicker extends nsZenMultiWindowFeature {
invalidateGradientCache() {
this.#gradientsCache = {};
- window.dispatchEvent(new Event('ZenGradientCacheChanged'));
+ window.dispatchEvent(new Event('ZenGradientCacheChanged', { bubbles: true }));
}
getGradientForWorkspace(workspace) {
diff --git a/src/zen/workspaces/ZenWorkspace.mjs b/src/zen/workspaces/ZenWorkspace.mjs
index 05078a0b0..7cb1af16d 100644
--- a/src/zen/workspaces/ZenWorkspace.mjs
+++ b/src/zen/workspaces/ZenWorkspace.mjs
@@ -231,7 +231,7 @@ class nsZenWorkspace extends MozXULElement {
if (newName === '') {
return;
}
- let workspaces = (await gZenWorkspaces.getWorkspaces()).workspaces;
+ let workspaces = gZenWorkspaces.getWorkspaces();
let workspaceData = workspaces.find((workspace) => workspace.uuid === this.workspaceUuid);
workspaceData.name = newName;
await gZenWorkspaces.saveWorkspace(workspaceData);
@@ -286,28 +286,27 @@ class nsZenWorkspace extends MozXULElement {
const popup = document.getElementById('zenMoveTabsToSyncedWorkspacePopup');
popup.innerHTML = '';
- gZenWorkspaces.getWorkspaces(true).then((workspaces) => {
- for (const workspace of workspaces.workspaces) {
- const item = gZenWorkspaces.generateMenuItemForWorkspace(workspace);
- item.addEventListener('command', async () => {
- const { ZenWindowSync } = ChromeUtils.importESModule(
- 'resource:///modules/zen/ZenWindowSync.sys.mjs'
- );
- ZenWindowSync.moveTabsToSyncedWorkspace(window, workspace.uuid);
- });
- popup.appendChild(item);
- }
+ const workspaces = gZenWorkspaces.getWorkspaces(true);
+ for (const workspace of workspaces) {
+ const item = gZenWorkspaces.generateMenuItemForWorkspace(workspace);
+ item.addEventListener('command', async () => {
+ const { ZenWindowSync } = ChromeUtils.importESModule(
+ 'resource:///modules/zen/ZenWindowSync.sys.mjs'
+ );
+ ZenWindowSync.moveTabsToSyncedWorkspace(window, workspace.uuid);
+ });
+ popup.appendChild(item);
+ }
- button.setAttribute('open', 'true');
- popup.addEventListener(
- 'popuphidden',
- () => {
- button.removeAttribute('open');
- },
- { once: true }
- );
- popup.openPopup(button, 'after_start', 0, 0, true /* isContextMenu */);
- });
+ button.setAttribute('open', 'true');
+ popup.addEventListener(
+ 'popuphidden',
+ () => {
+ button.removeAttribute('open');
+ },
+ { once: true }
+ );
+ popup.openPopup(button, 'after_start', 0, 0, true /* isContextMenu */);
}
}
diff --git a/src/zen/workspaces/ZenWorkspaceCreation.mjs b/src/zen/workspaces/ZenWorkspaceCreation.mjs
index cbaeed4c2..d037bade7 100644
--- a/src/zen/workspaces/ZenWorkspaceCreation.mjs
+++ b/src/zen/workspaces/ZenWorkspaceCreation.mjs
@@ -199,7 +199,7 @@ class nsZenWorkspaceCreation extends MozXULElement {
}
async onCreateButtonCommand() {
- const workspace = await gZenWorkspaces.getActiveWorkspace();
+ const workspace = gZenWorkspaces.getActiveWorkspace();
workspace.name = this.inputName.value.trim();
workspace.icon = this.inputIcon.image || this.inputIcon.label || undefined;
workspace.containerTabId = this.currentProfile;
@@ -320,8 +320,8 @@ class nsZenWorkspaceCreation extends MozXULElement {
this.remove();
gZenUIManager.updateTabsToolbar();
- const workspace = await gZenWorkspaces.getActiveWorkspace();
- await gZenWorkspaces._organizeWorkspaceStripLocations(workspace, true);
+ const workspace = gZenWorkspaces.getActiveWorkspace();
+ await gZenWorkspaces._organizeWorkspaceStripLocations(workspace);
await gZenWorkspaces.updateTabsContainers();
await gZenUIManager.motion.animate(
diff --git a/src/zen/workspaces/ZenWorkspaceIcons.mjs b/src/zen/workspaces/ZenWorkspaceIcons.mjs
index d56f6ecea..9672c4a27 100644
--- a/src/zen/workspaces/ZenWorkspaceIcons.mjs
+++ b/src/zen/workspaces/ZenWorkspaceIcons.mjs
@@ -126,13 +126,13 @@ class nsZenWorkspaceIcons extends MozXULElement {
}
async #updateIcons() {
- const workspaces = await gZenWorkspaces.getWorkspaces();
+ const workspaces = gZenWorkspaces.getWorkspaces();
this.innerHTML = '';
- for (const workspace of workspaces.workspaces) {
+ for (const workspace of workspaces) {
const button = this.#createWorkspaceIcon(workspace);
this.appendChild(button);
}
- if (workspaces.workspaces.length <= 1) {
+ if (workspaces.length <= 1) {
this.setAttribute('dont-show', 'true');
} else {
this.removeAttribute('dont-show');
@@ -168,6 +168,9 @@ class nsZenWorkspaceIcons extends MozXULElement {
}
i++;
}
+ if (selected == -1) {
+ return;
+ }
buttons[selected].setAttribute('active', true);
this.scrollLeft = buttons[selected].offsetLeft - 10;
this.setAttribute('selected', selected);
diff --git a/src/zen/workspaces/ZenWorkspaces.mjs b/src/zen/workspaces/ZenWorkspaces.mjs
index 6ef191205..dc1f29623 100644
--- a/src/zen/workspaces/ZenWorkspaces.mjs
+++ b/src/zen/workspaces/ZenWorkspaces.mjs
@@ -2,10 +2,9 @@
// 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/.
-import { nsZenMultiWindowFeature } from 'chrome://browser/content/zen-components/ZenCommonUtils.mjs';
import { nsZenThemePicker } from 'chrome://browser/content/zen-components/ZenGradientGenerator.mjs';
-class nsZenWorkspaces extends nsZenMultiWindowFeature {
+class nsZenWorkspaces {
/**
* Stores workspace IDs and their last selected tabs.
*/
@@ -13,6 +12,8 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
#inChangingWorkspace = false;
draggedElement = null;
+ #hasInitialized = false;
+
#canDebug = Services.prefs.getBoolPref('zen.workspaces.debug', false);
#activeWorkspace = '';
@@ -32,18 +33,10 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
'BMB_mobileBookmarks',
];
- promiseDBInitialized = new Promise((resolve) => {
- this._resolveDBInitialized = resolve;
- });
-
promisePinnedInitialized = new Promise((resolve) => {
this._resolvePinnedInitialized = resolve;
});
- promiseSectionsInitialized = new Promise((resolve) => {
- this._resolveSectionsInitialized = resolve;
- });
-
promiseInitialized = new Promise((resolve) => {
this._resolveInitialized = resolve;
});
@@ -52,11 +45,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
if (this.privateWindowOrDisabled) {
return;
}
- await Promise.all([
- this.promiseDBInitialized,
- this.promisePinnedInitialized,
- SessionStore.promiseAllWindowsRestored,
- ]);
+ await Promise.all([this.promisePinnedInitialized, SessionStore.promiseAllWindowsRestored]);
}
async init() {
@@ -129,8 +118,6 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
this.addPopupListeners();
await this.#waitForPromises();
- await this.getWorkspaces();
-
await this.afterLoadInit();
}
@@ -145,8 +132,6 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
await this.delayedStartup();
}
this._initializeWorkspaceTabContextMenus();
- await this.initializeWorkspaces();
- await this.promiseSectionsInitialized;
// Non UI related initializations
if (
@@ -157,24 +142,6 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
this.initializeGestureHandlers();
this.initializeWorkspaceNavigation();
}
-
- if (!this.privateWindowOrDisabled) {
- const observerFunction = async () => {
- this._workspaceBookmarksCache = null;
- await this.workspaceBookmarks();
- this._invalidateBookmarkContainers();
- };
- Services.obs.addObserver(this, 'weave:engine:sync:finish');
- Services.obs.addObserver(observerFunction, 'workspace-bookmarks-updated');
- window.addEventListener(
- 'unload',
- () => {
- Services.obs.removeObserver(this, 'weave:engine:sync:finish');
- Services.obs.removeObserver(observerFunction, 'workspace-bookmarks-updated');
- },
- { once: true }
- );
- }
}
// Validate browser state before tab operations
@@ -315,14 +282,8 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
});
};
this._pinnedTabsResizeObserver = new ResizeObserver(onResize);
- await this._createDefaultWorkspaceIfNeeded();
- }
-
- async _createDefaultWorkspaceIfNeeded() {
- const workspaces = await this.getWorkspaces();
- if (!workspaces.workspaces.length) {
- await this.createAndSaveWorkspace('Space', null, true);
- this._workspaceCache = null;
+ if (this.privateWindowOrDisabled) {
+ await this.restoreWorkspacesFromSessionStore({});
}
}
@@ -411,47 +372,38 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
await SessionStore.promiseAllWindowsRestored;
const perifery = document.getElementById('tabbrowser-arrowscrollbox-periphery');
perifery.setAttribute('hidden', 'true');
- await new Promise((resolve) => {
- setTimeout(() => {
- setTimeout(async () => {
- await window._zenPromiseNewWindowRestored;
- const tabs = gBrowser.tabContainer.allTabs;
- const workspaces = await this.getWorkspaces();
- for (const workspace of workspaces.workspaces) {
- await this._createWorkspaceTabsSection(workspace, tabs);
+ const tabs = gBrowser.tabContainer.allTabs;
+ const workspaces = this.getWorkspaces();
+ for (const workspace of workspaces) {
+ await this.#createWorkspaceTabsSection(workspace, tabs);
+ }
+ if (tabs.length) {
+ const defaultSelectedContainer = this.workspaceElement(this.activeWorkspace)?.querySelector(
+ '.zen-workspace-normal-tabs-section'
+ );
+ const pinnedContainer = this.workspaceElement(this.activeWorkspace).querySelector(
+ '.zen-workspace-pinned-tabs-section'
+ );
+ // New profile with no workspaces does not have a default selected container
+ if (defaultSelectedContainer) {
+ for (const tab of tabs) {
+ if (tab.hasAttribute('zen-essential')) {
+ this.getEssentialsSection(tab).appendChild(tab);
+ continue;
+ } else if (tab.pinned) {
+ pinnedContainer.insertBefore(tab, pinnedContainer.lastChild);
+ continue;
}
- if (tabs.length) {
- const defaultSelectedContainer = this.workspaceElement(
- this.activeWorkspace
- )?.querySelector('.zen-workspace-normal-tabs-section');
- const pinnedContainer = this.workspaceElement(this.activeWorkspace).querySelector(
- '.zen-workspace-pinned-tabs-section'
- );
- // New profile with no workspaces does not have a default selected container
- if (defaultSelectedContainer) {
- for (const tab of tabs) {
- if (tab.hasAttribute('zen-essential')) {
- this.getEssentialsSection(tab).appendChild(tab);
- continue;
- } else if (tab.pinned) {
- pinnedContainer.insertBefore(tab, pinnedContainer.lastChild);
- continue;
- }
- // before to the last child (perifery)
- defaultSelectedContainer.insertBefore(tab, defaultSelectedContainer.lastChild);
- }
- }
- gBrowser.tabContainer._invalidateCachedTabs();
- }
- perifery.setAttribute('hidden', 'true');
- this._hasInitializedTabsStrip = true;
- this.registerPinnedResizeObserver();
- this._fixIndicatorsNames(workspaces);
- this._resolveSectionsInitialized();
- resolve();
- });
- });
- });
+ // before to the last child (perifery)
+ defaultSelectedContainer.insertBefore(tab, defaultSelectedContainer.lastChild);
+ }
+ }
+ gBrowser.tabContainer._invalidateCachedTabs();
+ }
+ perifery.setAttribute('hidden', 'true');
+ this._hasInitializedTabsStrip = true;
+ this.registerPinnedResizeObserver();
+ this._fixIndicatorsNames(workspaces);
}
getEssentialsSection(container = 0) {
@@ -489,7 +441,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
return this.getEssentialsSection(currentWorkspace?.containerTabId);
}
- async _createWorkspaceTabsSection(workspace, tabs = []) {
+ async #createWorkspaceTabsSection(workspace, tabs = []) {
const workspaceWrapper = document.createXULElement('zen-workspace');
const container = document.getElementById('tabbrowser-arrowscrollbox');
workspaceWrapper.id = workspace.uuid;
@@ -794,6 +746,13 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
}
set activeWorkspace(value) {
+ if (value === this.#activeWorkspace) {
+ return;
+ }
+ const spaces = this.getWorkspaces();
+ if (!spaces.some((ws) => ws.uuid === value)) {
+ value = spaces[0]?.uuid || '';
+ }
this.#activeWorkspace = value;
if (this.privateWindowOrDisabled) {
return;
@@ -801,27 +760,6 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
Services.prefs.setStringPref('zen.workspaces.active', value);
}
- async observe(subject, topic, data) {
- if (topic === 'weave:engine:sync:finish' && data === 'workspaces') {
- try {
- const lastChangeTimestamp = await ZenWorkspacesStorage.getLastChangeTimestamp();
-
- if (
- !this._workspaceCache ||
- !this._workspaceCache.lastChangeTimestamp ||
- lastChangeTimestamp > this._workspaceCache.lastChangeTimestamp
- ) {
- await this._propagateWorkspaceData();
-
- const currentWorkspace = await this.getActiveWorkspace();
- await gZenThemePicker.onWorkspaceChange(currentWorkspace);
- }
- } catch (error) {
- console.error('Error updating workspaces after sync:', error);
- }
- }
- }
-
get shouldHaveWorkspaces() {
if (typeof this._shouldHaveWorkspaces === 'undefined') {
let chromeFlags = window.docShell.treeOwner
@@ -866,52 +804,24 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
getWorkspaceFromId(id) {
try {
- return this._workspaceCache.workspaces.find((workspace) => workspace.uuid === id);
+ return this._workspaceCache.find((workspace) => workspace.uuid === id);
} catch {
return null;
}
}
- async getWorkspaces(lieToMe = false) {
- if (this._workspaceCache && !lieToMe) {
- return this._workspaceCache;
+ getWorkspaces(lieToMe = false) {
+ if (lieToMe) {
+ const { ZenSessionStore } = ChromeUtils.importESModule(
+ 'resource:///modules/zen/ZenSessionManager.sys.mjs'
+ );
+ return ZenSessionStore.getClonedSpaces();
}
-
- if (!this.currentWindowIsSyncing && !lieToMe) {
- this._workspaceCache = {
- workspaces: this._tempWorkspace ? [this._tempWorkspace] : [],
- lastChangeTimestamp: 0,
- };
+ if (!this.currentWindowIsSyncing) {
+ this._workspaceCache = this._tempWorkspace ? [this._tempWorkspace] : [];
this.#activeWorkspace = this._tempWorkspace?.uuid;
return this._workspaceCache;
}
-
- const [workspaces, lastChangeTimestamp] = await Promise.all([
- ZenWorkspacesStorage.getWorkspaces(),
- ZenWorkspacesStorage.getLastChangeTimestamp(),
- ]);
-
- this._workspaceCache = { workspaces, lastChangeTimestamp };
- // Get the active workspace ID from preferences
- const activeWorkspaceId = this.activeWorkspace;
-
- if (!lieToMe) {
- if (activeWorkspaceId) {
- const activeWorkspace = this.getWorkspaceFromId(activeWorkspaceId);
- // Set the active workspace ID to the first one if the one with selected id doesn't exist
- if (!activeWorkspace) {
- this.activeWorkspace = this._workspaceCache.workspaces[0]?.uuid;
- }
- } else {
- // Set the active workspace ID to the first one if active workspace doesn't exist
- this.activeWorkspace = this._workspaceCache.workspaces[0]?.uuid;
- }
- }
-
- // sort by position
- this._workspaceCache.workspaces.sort(
- (a, b) => (a.position ?? Infinity) - (b.position ?? Infinity)
- );
return this._workspaceCache;
}
@@ -938,8 +848,17 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
return this._workspaceCache;
}
+ async restoreWorkspacesFromSessionStore(aWinData) {
+ this._workspaceCache = aWinData.spaces || [
+ await this.createAndSaveWorkspace('Space', undefined, true),
+ ];
+ this.activeWorkspace = aWinData.activeZenSpace || this._workspaceCache[0].uuid;
+ await this.initializeWorkspaces();
+ this.#hasInitialized = true;
+ }
+
async initializeWorkspaces() {
- let activeWorkspace = await this.getActiveWorkspace();
+ let activeWorkspace = this.getActiveWorkspace();
this.activeWorkspace = activeWorkspace?.uuid;
await gZenSessionStore.promiseInitialized;
try {
@@ -950,15 +869,15 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
} catch (e) {
console.error('gZenWorkspaces: Error initializing theme picker', e);
}
- await this.workspaceBookmarks();
await this.#initializeTabsStripSections();
this.#initializeEmptyTab();
+ await this.workspaceBookmarks();
await this.changeWorkspace(activeWorkspace, { onInit: true });
this.#fixTabPositions();
this.onWindowResize();
this._resolveInitialized();
this.#clearAnyZombieTabs(); // Dont call with await
- delete window._zenPromiseNewWindowRestored;
+ delete this._resolveInitialized;
const tabUpdateListener = this.updateTabsContainers.bind(this);
window.addEventListener('TabOpen', tabUpdateListener);
@@ -969,8 +888,9 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
window.addEventListener('TabUnpinned', tabUpdateListener);
window.addEventListener('aftercustomization', tabUpdateListener);
window.addEventListener('TabSelect', this.onLocationChange.bind(this));
-
window.addEventListener('TabBrowserInserted', this.onTabBrowserInserted.bind(this));
+
+ this.#updateWorkspacesChangeContextMenu();
}
async selectStartPage() {
@@ -1134,13 +1054,13 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
async #clearAnyZombieTabs() {
const tabs = this.allStoredTabs;
- const workspaces = await this.getWorkspaces();
+ const workspaces = this.getWorkspaces();
for (let tab of tabs) {
const workspaceID = tab.getAttribute('zen-workspace-id');
if (
(workspaceID &&
!tab.hasAttribute('zen-essential') &&
- !workspaces.workspaces.find((workspace) => workspace.uuid === workspaceID)) ||
+ !workspaces.find((workspace) => workspace.uuid === workspaceID)) ||
// Also remove empty tabs that are supposed to be from parent folders but
// they dont exist anymore
(tab.pinned && tab.hasAttribute('zen-empty-tab') && !tab.group)
@@ -1282,7 +1202,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
}
if (!this.#contextMenuData.workspaceId) {
separator.hidden = false;
- for (const workspace of [...this._workspaceCache.workspaces].reverse()) {
+ for (const workspace of [...this._workspaceCache].reverse()) {
const item = this.generateMenuItemForWorkspace(workspace);
item.addEventListener('command', (e) => {
this.changeWorkspaceWithID(e.target.closest('menuitem').getAttribute('zen-workspace-id'));
@@ -1316,48 +1236,35 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
});
}
- async saveWorkspace(workspaceData, preventPropagation = false) {
+ saveWorkspace(workspaceData) {
if (this.privateWindowOrDisabled) {
return;
}
- await ZenWorkspacesStorage.saveWorkspace(workspaceData);
- if (!preventPropagation) {
- await this._propagateWorkspaceData();
- await this._updateWorkspacesChangeContextMenu();
+ const workspacesData = this.getWorkspaces();
+ const index = workspacesData.findIndex((ws) => ws.uuid === workspaceData.uuid);
+ if (index !== -1) {
+ workspacesData[index] = workspaceData;
+ } else {
+ workspacesData.push(workspaceData);
}
+ this.#propagateWorkspaceData();
}
- async removeWorkspace(windowID) {
- let workspacesData = await this.getWorkspaces();
- await this.changeWorkspace(
- workspacesData.workspaces.find((workspace) => workspace.uuid !== windowID)
- );
- await this.#deleteAllTabsInWorkspace(windowID);
- delete this.lastSelectedWorkspaceTabs[windowID];
- await ZenWorkspacesStorage.removeWorkspace(windowID);
+ removeWorkspace(windowID) {
+ let workspacesData = this.getWorkspaces();
// Remove the workspace from the cache
- this._workspaceCache.workspaces = this._workspaceCache.workspaces.filter(
- (workspace) => workspace.uuid !== windowID
- );
- await this._propagateWorkspaceData();
- await this._updateWorkspacesChangeContextMenu();
- this.workspaceElement(windowID)?.remove();
- this.onWindowResize();
- this.registerPinnedResizeObserver();
+ workspacesData = workspacesData.filter((workspace) => workspace.uuid !== windowID);
+ this.#propagateWorkspaceData(workspacesData);
}
isWorkspaceActive(workspace) {
return workspace.uuid === this.activeWorkspace;
}
- async getActiveWorkspace() {
- const workspaces = await this.getWorkspaces();
- return (
- workspaces.workspaces.find((workspace) => workspace.uuid === this.activeWorkspace) ??
- workspaces.workspaces[0]
- );
+ getActiveWorkspace() {
+ return this.getActiveWorkspaceFromCache();
}
- // Workspaces dialog UI management
+
workspaceHasIcon(workspace) {
return workspace.icon && workspace.icon !== '';
}
@@ -1380,65 +1287,75 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
);
}
- async _propagateWorkspaceDataForWindow(browser, { ignoreStrip = false, clearCache = true } = {}) {
- if (clearCache) {
- browser.gZenWorkspaces._workspaceCache = null;
- browser.gZenWorkspaces._workspaceBookmarksCache = null;
- }
- let workspaces = await browser.gZenWorkspaces.getWorkspaces();
- browser.document
- .getElementById('cmd_zenCtxDeleteWorkspace')
- .setAttribute('disabled', workspaces.workspaces.length <= 1);
- if (clearCache) {
- browser.dispatchEvent(
- new CustomEvent('ZenWorkspacesUIUpdate', {
- bubbles: true,
- detail: { activeIndex: browser.gZenWorkspaces.activeWorkspace },
- })
- );
- for (const workspace of workspaces.workspaces) {
- // Add workspace elements if they dont exist on other windows
- if (!browser.gZenWorkspaces.workspaceElement(workspace.uuid)) {
- await browser.gZenWorkspaces._createWorkspaceTabsSection(workspace);
- }
- }
- }
- await browser.gZenWorkspaces.workspaceBookmarks();
- if (!ignoreStrip) {
- browser.gZenWorkspaces._fixIndicatorsNames(workspaces);
+ #propagateWorkspaceData(aSpaceData = null) {
+ if (!this.#hasInitialized || this.privateWindowOrDisabled) {
+ return;
}
+ window.gZenWindowSync.propagateWorkspacesToAllWindows(aSpaceData ?? this._workspaceCache);
}
- async _propagateWorkspaceData({ ignoreStrip = false, clearCache = true, onInit = false } = {}) {
- const currentWindowIsPrivate = !this.currentWindowIsSyncing;
- if (onInit) {
- if (currentWindowIsPrivate) return;
- return await this._propagateWorkspaceDataForWindow(this.ownerWindow, {
- ignoreStrip,
- clearCache,
- });
- }
- await this.foreachWindowAsActive(async (browser) => {
- // Do not update the window if workspaces are not enabled in it.
- // For example, when the window is in private browsing mode.
+ propagateWorkspaces(aWorkspaces) {
+ const previousWorkspaces = this._workspaceCache || [];
+ this._workspaceCache = aWorkspaces;
+ let hasChanged = false;
+ // Remove any workspace elements here that no longer exist
+ for (const previousWorkspace of previousWorkspaces) {
if (
- !browser.gZenWorkspaces.workspaceEnabled ||
- !browser.gZenWorkspaces.currentWindowIsSyncing !== currentWindowIsPrivate
+ this.workspaceElement(previousWorkspace.uuid) &&
+ !aWorkspaces.find((w) => w.uuid === previousWorkspace.uuid)
) {
- return;
+ if (this.isWorkspaceActive(previousWorkspace)) {
+ // If the removed workspace was active, switch to another one
+ const newActiveWorkspace =
+ aWorkspaces.find((w) => w.uuid !== previousWorkspace.uuid) || null;
+ this.changeWorkspace(newActiveWorkspace);
+ }
+ this.workspaceElement(previousWorkspace.uuid)?.remove();
+ delete this.lastSelectedWorkspaceTabs[previousWorkspace.uuid];
+ hasChanged = true;
}
- this._propagateWorkspaceDataForWindow(browser, {
- ignoreStrip,
- clearCache,
- }).catch(console.error);
+ }
+ // Add any new workspace elements here
+ for (const workspace of aWorkspaces) {
+ if (!this.workspaceElement(workspace.uuid)) {
+ this.#createWorkspaceTabsSection(workspace).catch((e) => {
+ console.error('Error creating workspace tabs section:', e);
+ });
+ hasChanged = true;
+ }
+ }
+ // Order the workspace elements correctly
+ let previousElement = null;
+ for (const workspace of aWorkspaces) {
+ const workspaceElement = this.workspaceElement(workspace.uuid);
+ if (workspaceElement) {
+ if (previousElement === null) {
+ gZenUIManager.tabsWrapper.insertBefore(
+ workspaceElement,
+ gZenUIManager.tabsWrapper.firstChild
+ );
+ hasChanged = true;
+ } else if (previousElement.nextSibling !== workspaceElement) {
+ gZenUIManager.tabsWrapper.insertBefore(workspaceElement, previousElement.nextSibling);
+ hasChanged = true;
+ }
+ previousElement = workspaceElement;
+ }
+ }
+ if (hasChanged) {
+ this.#fireSpaceUIUpdate();
+ }
+ this._organizeWorkspaceStripLocations(this.getActiveWorkspaceFromCache()).finally(() => {
+ this.updateTabsContainers();
});
+ this.#updateWorkspacesChangeContextMenu();
}
async reorderWorkspace(id, newPosition) {
if (this.privateWindowOrDisabled) {
return;
}
- const workspaces = (await this.getWorkspaces()).workspaces;
+ const workspaces = this.getWorkspaces();
const workspace = workspaces.find((w) => w.uuid === id);
if (!workspace) {
console.warn(`Workspace with ID ${id} not found for reordering.`);
@@ -1457,26 +1374,13 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
return;
}
workspaces.splice(newPosition, 0, workspace);
- // Update the positions in the storage
- await ZenWorkspacesStorage.updateWorkspacePositions(workspaces);
// Propagate the changes
- await this._propagateWorkspaceData();
- }
-
- async moveWorkspace(draggedWorkspaceId, targetWorkspaceId) {
- const workspaces = (await this.getWorkspaces()).workspaces;
- const draggedIndex = workspaces.findIndex((w) => w.uuid === draggedWorkspaceId);
- const draggedWorkspace = workspaces.splice(draggedIndex, 1)[0];
- const targetIndex = workspaces.findIndex((w) => w.uuid === targetWorkspaceId);
- workspaces.splice(targetIndex, 0, draggedWorkspace);
-
- await ZenWorkspacesStorage.updateWorkspacePositions(workspaces);
- await this._propagateWorkspaceData();
+ this.#propagateWorkspaceData();
}
async openWorkspaceCreation() {
let createForm;
- const previousWorkspace = await this.getActiveWorkspace();
+ const previousWorkspace = this.getActiveWorkspace();
document.documentElement.setAttribute('zen-creating-workspace', 'true');
await this.createAndSaveWorkspace('Space', undefined, false, 0, {
beforeChangeCallback: async (workspace) => {
@@ -1490,22 +1394,6 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
createForm.finishSetup();
}
- // Workspaces management
-
- async #deleteAllTabsInWorkspace(workspaceID) {
- const tabs = Array.from(this.allStoredTabs).filter(
- (tab) =>
- tab.getAttribute('zen-workspace-id') === workspaceID &&
- !tab.hasAttribute('zen-empty-tab') &&
- !tab.hasAttribute('zen-essential')
- );
- gBrowser.removeTabs(tabs, {
- animate: false,
- skipSessionStore: true,
- closeWindowWithLastTab: false,
- });
- }
-
#unpinnedTabsInWorkspace(workspaceID) {
return Array.from(this.allStoredTabs).filter(
(tab) => tab.getAttribute('zen-workspace-id') === workspaceID && tab.visible && !tab.pinned
@@ -1669,7 +1557,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
workspace,
{ onInit = false, alwaysChange = false, whileScrolling = false } = {}
) {
- const previousWorkspace = await this.getActiveWorkspace();
+ const previousWorkspace = this.getActiveWorkspace();
alwaysChange = alwaysChange || onInit;
this.activeWorkspace = workspace.uuid;
if (previousWorkspace && previousWorkspace.uuid === workspace.uuid && !alwaysChange) {
@@ -1677,11 +1565,11 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
return;
}
- const workspaces = await this.getWorkspaces();
+ const workspaces = this.getWorkspaces();
gZenFolders.cancelPopupTimer();
// Refresh tab cache
- for (const otherWorkspace of workspaces.workspaces) {
+ for (const otherWorkspace of workspaces) {
const container = this.workspaceElement(otherWorkspace.uuid);
container.active = otherWorkspace.uuid === workspace.uuid;
}
@@ -1703,9 +1591,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
gBrowser.warmupTab(tabToSelect);
// Update UI and state
- const previousWorkspaceIndex = workspaces.workspaces.findIndex(
- (w) => w.uuid === previousWorkspace.uuid
- );
+ const previousWorkspaceIndex = workspaces.findIndex((w) => w.uuid === previousWorkspace.uuid);
await this._updateWorkspaceState(workspace, onInit, tabToSelect, {
previousWorkspaceIndex,
previousWorkspace,
@@ -1793,8 +1679,8 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
return;
}
this._organizingWorkspaceStrip = true;
- const workspaces = await this.getWorkspaces();
- let workspaceIndex = workspaces.workspaces.findIndex((w) => w.uuid === workspace.uuid);
+ const workspaces = this.getWorkspaces();
+ let workspaceIndex = workspaces.findIndex((w) => w.uuid === workspace.uuid);
if (!justMove) {
this._fixIndicatorsNames(workspaces);
}
@@ -1803,10 +1689,10 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
);
const workspaceContextId = workspace.containerTabId;
const nextWorkspaceContextId =
- workspaces.workspaces[workspaceIndex + (offsetPixels > 0 ? -1 : 1)]?.containerTabId;
- for (const otherWorkspace of workspaces.workspaces) {
+ workspaces[workspaceIndex + (offsetPixels > 0 ? -1 : 1)]?.containerTabId;
+ for (const otherWorkspace of workspaces) {
const element = this.workspaceElement(otherWorkspace.uuid);
- const newTransform = -(workspaceIndex - workspaces.workspaces.indexOf(otherWorkspace)) * 100;
+ const newTransform = -(workspaceIndex - workspaces.indexOf(otherWorkspace)) * 100;
element.style.transform = `translateX(${newTransform + offsetPixels / 2}%)`;
}
// Hide other essentials with different containerTabId
@@ -1840,7 +1726,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
}
if (offsetPixels) {
// Find the next workspace we are scrolling to
- const nextWorkspace = workspaces.workspaces[workspaceIndex + (offsetPixels > 0 ? -1 : 1)];
+ const nextWorkspace = workspaces[workspaceIndex + (offsetPixels > 0 ? -1 : 1)];
if (nextWorkspace) {
const {
gradient: nextGradient,
@@ -1911,7 +1797,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
}
_fixIndicatorsNames(workspaces) {
- for (const workspace of workspaces.workspaces) {
+ for (const workspace of workspaces) {
const workspaceIndicator = this.workspaceElement(workspace.uuid)?.indicator;
this.updateWorkspaceIndicator(workspace, workspaceIndicator);
}
@@ -1927,12 +1813,12 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
const kGlobalAnimationDuration = 0.2;
this._animatingChange = true;
const animations = [];
- const workspaces = await this.getWorkspaces();
- const newWorkspaceIndex = workspaces.workspaces.findIndex((w) => w.uuid === newWorkspace.uuid);
+ const workspaces = this.getWorkspaces();
+ const newWorkspaceIndex = workspaces.findIndex((w) => w.uuid === newWorkspace.uuid);
const isGoingLeft = newWorkspaceIndex <= previousWorkspaceIndex;
const clonedEssentials = [];
if (shouldAnimate && this.shouldAnimateEssentials && previousWorkspace) {
- for (const workspace of workspaces.workspaces) {
+ for (const workspace of workspaces) {
const essentialsContainer = this.getEssentialsSection(workspace.containerTabId);
if (clonedEssentials[clonedEssentials.length - 1]?.contextId == workspace.containerTabId) {
clonedEssentials[clonedEssentials.length - 1].repeat++;
@@ -2000,9 +1886,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
}
const existingTransform = element.style.transform;
const elementWorkspaceId = element.id;
- const elementWorkspaceIndex = workspaces.workspaces.findIndex(
- (w) => w.uuid === elementWorkspaceId
- );
+ const elementWorkspaceIndex = workspaces.findIndex((w) => w.uuid === elementWorkspaceId);
const offset = -(newWorkspaceIndex - elementWorkspaceIndex) * 100;
const newTransform = `translateX(${offset}%)`;
if (shouldAnimate) {
@@ -2039,10 +1923,8 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
// Get a list of essentials containers that are in between the first and last workspace
const essentialsContainersInBetween = clonedEssentials.filter((cloned) => {
const essentialsWorkspaces = cloned.workspaces;
- const firstIndex = workspaces.workspaces.findIndex(
- (w) => w.uuid === essentialsWorkspaces[0].uuid
- );
- const lastIndex = workspaces.workspaces.findIndex(
+ const firstIndex = workspaces.findIndex((w) => w.uuid === essentialsWorkspaces[0].uuid);
+ const lastIndex = workspaces.findIndex(
(w) => w.uuid === essentialsWorkspaces[essentialsWorkspaces.length - 1].uuid
);
@@ -2071,10 +1953,10 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
// will slide in from the right
// Get the index from first and last workspace
- const firstWorkspaceIndex = workspaces.workspaces.findIndex(
+ const firstWorkspaceIndex = workspaces.findIndex(
(w) => w.uuid === essentialsWorkspaces[0].uuid
);
- const lastWorkspaceIndex = workspaces.workspaces.findIndex(
+ const lastWorkspaceIndex = workspaces.findIndex(
(w) => w.uuid === essentialsWorkspaces[essentialsWorkspaces.length - 1].uuid
);
cloned.originalContainer.style.removeProperty('transform');
@@ -2230,7 +2112,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
tab,
currentWorkspace.uuid,
currentWorkspace.containerTabId,
- await this.getWorkspaces()
+ this.getWorkspaces()
);
}
@@ -2265,9 +2147,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
return (
!tabContextId ||
tabContextId === '0' ||
- !workspaces.workspaces.some(
- (workspace) => workspace.containerTabId === parseInt(tabContextId, 10)
- )
+ !workspaces.some((workspace) => workspace.containerTabId === parseInt(tabContextId, 10))
);
}
}
@@ -2289,7 +2169,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
const lastSelectedTab = this.lastSelectedWorkspaceTabs[workspace.uuid];
const containerId = workspace.containerTabId?.toString();
- const workspaces = await this.getWorkspaces();
+ const workspaces = this.getWorkspaces();
// Save current tab as last selected for old workspace if it shouldn't be visible in new workspace
if (oldWorkspaceId && oldWorkspaceId !== workspace.uuid) {
@@ -2349,9 +2229,6 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
gBrowser.tabContainer.arrowScrollbox = this.activeScrollbox;
// Update workspace UI
- await this._updateWorkspacesChangeContextMenu();
- await this._propagateWorkspaceData({ clearCache: false, onInit });
-
gZenThemePicker.onWorkspaceChange(workspace);
gZenUIManager.tabsWrapper.scrollbarWidth = 'none';
@@ -2396,15 +2273,19 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
tab.setAttribute('zen-workspace-id', workspace.uuid);
}
}
- window.dispatchEvent(
- new CustomEvent('ZenWorkspacesUIUpdate', {
- bubbles: true,
- detail: { activeIndex: workspace.uuid },
- })
- );
+ this.#fireSpaceUIUpdate();
}
}
+ #fireSpaceUIUpdate() {
+ window.dispatchEvent(
+ new CustomEvent('ZenWorkspacesUIUpdate', {
+ bubbles: true,
+ detail: { activeIndex: this.activeWorkspace },
+ })
+ );
+ }
+
async _fixCtrlTabBehavior() {
ctrlTab.uninit();
ctrlTab.readPref();
@@ -2420,9 +2301,9 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
}
}
- async _updateWorkspacesChangeContextMenu() {
+ #updateWorkspacesChangeContextMenu() {
if (gZenWorkspaces.privateWindowOrDisabled) return;
- const workspaces = await this.getWorkspaces();
+ const workspaces = this.getWorkspaces();
const menuPopup = document.getElementById('context-zen-change-workspace-tab-menu-popup');
if (!menuPopup) {
@@ -2430,9 +2311,9 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
}
menuPopup.innerHTML = '';
- const activeWorkspace = await this.getActiveWorkspace();
+ const activeWorkspace = this.getActiveWorkspace();
- for (let workspace of workspaces.workspaces) {
+ for (let workspace of workspaces) {
const menuItem = document.createXULElement('menuitem');
menuItem.setAttribute('label', workspace.name);
menuItem.setAttribute('zen-workspace-id', workspace.uuid);
@@ -2456,7 +2337,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
};
if (moveTabs) {
this.#prepareNewWorkspace(workspace);
- await this._createWorkspaceTabsSection(workspace, tabs);
+ await this.#createWorkspaceTabsSection(workspace, tabs);
await this._organizeWorkspaceStripLocations(workspace);
}
return workspace;
@@ -2493,7 +2374,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
if (!this.currentWindowIsSyncing) {
this._tempWorkspace = workspaceData;
} else {
- await this.saveWorkspace(workspaceData, dontChange);
+ this.saveWorkspace(workspaceData);
}
if (!dontChange) {
if (beforeChangeCallback) {
@@ -2598,9 +2479,9 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
const workspacesIds = [];
if (entry.target.closest('#zen-essentials')) {
// Get all workspaces that have the same userContextId
- const activeWorkspace = await this.getActiveWorkspace();
+ const activeWorkspace = this.getActiveWorkspace();
const userContextId = activeWorkspace.containerTabId;
- const workspaces = this._workspaceCache.workspaces.filter(
+ const workspaces = this._workspaceCache.filter(
(w) => w.containerTabId === userContextId && w.uuid !== originalWorkspaceId
);
workspacesIds.push(...workspaces.map((w) => w.uuid));
@@ -2663,7 +2544,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
return;
}
- let activeWorkspace = await this.getActiveWorkspace();
+ let activeWorkspace = this.getActiveWorkspace();
if (!activeWorkspace) {
return;
}
@@ -2702,7 +2583,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
}
if (!isEssential) {
- const activeWorkspace = await this.getActiveWorkspace();
+ const activeWorkspace = this.getActiveWorkspace();
if (!activeWorkspace) {
return;
}
@@ -2726,16 +2607,13 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
// Context menu management
async contextChangeContainerTab(event) {
this._organizingWorkspaceStrip = true;
- let workspaces = await this.getWorkspaces();
- let workspace = workspaces.workspaces.find(
+ let workspaces = this.getWorkspaces();
+ let workspace = workspaces.find(
(workspace) => workspace.uuid === (this.#contextMenuData?.workspaceId || this.activeWorkspace)
);
let userContextId = parseInt(event.target.getAttribute('data-usercontextid'));
workspace.containerTabId = userContextId + 0; // +0 to convert to number
- await this.saveWorkspace(workspace);
- await this._organizeWorkspaceStripLocations(this.getActiveWorkspaceFromCache(), true);
- await this.updateTabsContainers();
- this.tabContainer._invalidateCachedTabs();
+ this.saveWorkspace(workspace);
}
async closeAllUnpinnedTabs() {
@@ -2767,7 +2645,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
},
]);
if (Services.prompt.confirm(null, title, body)) {
- await this.removeWorkspace(workspaceId);
+ this.removeWorkspace(workspaceId);
}
}
@@ -2780,21 +2658,21 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
async changeWorkspaceShortcut(offset = 1, whileScrolling = false) {
// Cycle through workspaces
- let workspaces = await this.getWorkspaces();
- let activeWorkspace = await this.getActiveWorkspace();
- let workspaceIndex = workspaces.workspaces.indexOf(activeWorkspace);
+ let workspaces = this.getWorkspaces();
+ let activeWorkspace = this.getActiveWorkspace();
+ let workspaceIndex = workspaces.indexOf(activeWorkspace);
// note: offset can be negative
let targetIndex = workspaceIndex + offset;
if (this.shouldWrapAroundNavigation) {
// Add length to handle negative indices and loop
- targetIndex = (targetIndex + workspaces.workspaces.length) % workspaces.workspaces.length;
+ targetIndex = (targetIndex + workspaces.length) % workspaces.length;
} else {
// Clamp within bounds to disable looping
- targetIndex = Math.max(0, Math.min(workspaces.workspaces.length - 1, targetIndex));
+ targetIndex = Math.max(0, Math.min(workspaces.length - 1, targetIndex));
}
- let nextWorkspace = workspaces.workspaces[targetIndex];
+ let nextWorkspace = workspaces[targetIndex];
await this.changeWorkspace(nextWorkspace, { whileScrolling });
}
@@ -2844,10 +2722,8 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
this.lastSelectedWorkspaceTabs[workspaceID] = gZenGlanceManager.getTabOrGlanceParent(
tabs[tabs.length - 1]
);
- const workspaces = await this.getWorkspaces();
- await this.changeWorkspace(
- workspaces.workspaces.find((workspace) => workspace.uuid === workspaceID)
- );
+ const workspaces = this.getWorkspaces();
+ await this.changeWorkspace(workspaces.find((workspace) => workspace.uuid === workspaceID));
}
// Tab browser utilities
@@ -2860,11 +2736,11 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
if (
this.shouldForceContainerTabsToWorkspace &&
typeof userContextId !== 'undefined' &&
- this._workspaceCache?.workspaces &&
+ this._workspaceCache &&
!fromExternal
) {
// Find all workspaces that match the given userContextId
- const matchingWorkspaces = this._workspaceCache.workspaces.filter(
+ const matchingWorkspaces = this._workspaceCache.filter(
(workspace) => workspace.containerTabId === userContextId
);
@@ -2903,12 +2779,12 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
}
async shortcutSwitchTo(index) {
- const workspaces = await this.getWorkspaces();
+ const workspaces = this.getWorkspaces();
// The index may be out of bounds, if it doesnt exist, don't do anything
- if (index >= workspaces.workspaces.length || index < 0) {
+ if (index >= workspaces.length || index < 0) {
return;
}
- const workspaceToSwitch = workspaces.workspaces[index];
+ const workspaceToSwitch = workspaces[index];
await this.changeWorkspace(workspaceToSwitch);
}
@@ -2952,7 +2828,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
pinnedContainers = [document.getElementById('pinned-tabs-container')];
normalContainers = [this.activeWorkspaceStrip];
} else {
- let workspaces = Array.from(this._workspaceCache?.workspaces || []);
+ let workspaces = Array.from(this._workspaceCache || []);
// Make the active workspace first
workspaces = workspaces.sort((a, b) =>
a.uuid === this.activeWorkspace ? -1 : b.uuid === this.activeWorkspace ? 1 : 0
@@ -2998,7 +2874,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
}
const pinnedContainers = [];
const normalContainers = [];
- for (const workspace of this._workspaceCache.workspaces) {
+ for (const workspace of this._workspaceCache) {
const container = this.workspaceElement(workspace.uuid);
if (container) {
pinnedContainers.push(container.pinnedTabsContainer);
@@ -3096,7 +2972,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
// Find first workspace with the same container
const containerTabId = parseInt(tab.parentNode.getAttribute('container'));
// +0 to convert to number
- workspaceToSwitch = this._workspaceCache.workspaces.find(
+ workspaceToSwitch = this._workspaceCache.find(
(workspace) => workspace.containerTabId + 0 === containerTabId
);
} else {
@@ -3134,7 +3010,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
return 0;
}
const activeWorkspace = this.activeWorkspace;
- const workspace = workspaces.workspaces.find((workspace) => workspace.uuid === activeWorkspace);
+ const workspace = workspaces.find((workspace) => workspace.uuid === activeWorkspace);
return workspace.containerTabId;
}
diff --git a/src/zen/workspaces/ZenWorkspacesStorage.mjs b/src/zen/workspaces/ZenWorkspacesStorage.mjs
index 43d183d39..63097e240 100644
--- a/src/zen/workspaces/ZenWorkspacesStorage.mjs
+++ b/src/zen/workspaces/ZenWorkspacesStorage.mjs
@@ -2,437 +2,20 @@
// 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/.
-window.ZenWorkspacesStorage = {
+// Integration of workspace-specific bookmarks into Places
+window.ZenWorkspaceBookmarksStorage = {
lazy: {},
async init() {
ChromeUtils.defineESModuleGetters(this.lazy, {
PlacesUtils: 'resource://gre/modules/PlacesUtils.sys.mjs',
- Weave: 'resource://services-sync/main.sys.mjs',
});
-
if (!window.gZenWorkspaces) return;
await this._ensureTable();
- await ZenWorkspaceBookmarksStorage.init();
},
async _ensureTable() {
await this.lazy.PlacesUtils.withConnectionWrapper(
- 'ZenWorkspacesStorage._ensureTable',
- async (db) => {
- // Create the main workspaces table if it doesn't exist
- await db.execute(`
- CREATE TABLE IF NOT EXISTS zen_workspaces (
- id INTEGER PRIMARY KEY,
- uuid TEXT UNIQUE NOT NULL,
- name TEXT NOT NULL,
- icon TEXT,
- container_id INTEGER,
- position INTEGER NOT NULL DEFAULT 0,
- created_at INTEGER NOT NULL,
- updated_at INTEGER NOT NULL
- )
- `);
-
- // Add new columns if they don't exist
- // SQLite doesn't have a direct "ADD COLUMN IF NOT EXISTS" syntax,
- // so we need to check if the columns exist first
- const columns = await db.execute(`PRAGMA table_info(zen_workspaces)`);
- const columnNames = columns.map((row) => row.getResultByName('name'));
-
- // Helper function to add column if it doesn't exist
- const addColumnIfNotExists = async (columnName, definition) => {
- if (!columnNames.includes(columnName)) {
- await db.execute(`ALTER TABLE zen_workspaces ADD COLUMN ${columnName} ${definition}`);
- }
- };
-
- // Add each new column if it doesn't exist
- await addColumnIfNotExists('theme_type', 'TEXT');
- await addColumnIfNotExists('theme_colors', 'TEXT');
- await addColumnIfNotExists('theme_opacity', 'REAL');
- await addColumnIfNotExists('theme_rotation', 'INTEGER');
- await addColumnIfNotExists('theme_texture', 'REAL');
-
- // Create an index on the uuid column
- await db.execute(`
- CREATE INDEX IF NOT EXISTS idx_zen_workspaces_uuid ON zen_workspaces(uuid)
- `);
-
- // Create the changes tracking table if it doesn't exist
- await db.execute(`
- CREATE TABLE IF NOT EXISTS zen_workspaces_changes (
- uuid TEXT PRIMARY KEY,
- timestamp INTEGER NOT NULL
- )
- `);
-
- // Create an index on the uuid column for changes tracking table
- await db.execute(`
- CREATE INDEX IF NOT EXISTS idx_zen_workspaces_changes_uuid ON zen_workspaces_changes(uuid)
- `);
-
- if (!this.lazy.Weave.Service.engineManager.get('workspaces')) {
- this.lazy.Weave.Service.engineManager.register(ZenWorkspacesEngine);
- await ZenWorkspacesStorage.migrateWorkspacesFromJSON();
- }
-
- gZenWorkspaces._resolveDBInitialized();
- }
- );
- },
-
- async migrateWorkspacesFromJSON() {
- const oldWorkspacesPath = PathUtils.join(
- PathUtils.profileDir,
- 'zen-workspaces',
- 'Workspaces.json'
- );
- if (await IOUtils.exists(oldWorkspacesPath)) {
- console.info('ZenWorkspacesStorage: Migrating workspaces from JSON...');
- const oldWorkspaces = await IOUtils.readJSON(oldWorkspacesPath);
- if (oldWorkspaces.workspaces) {
- for (const workspace of oldWorkspaces.workspaces) {
- await this.saveWorkspace(workspace);
- }
- }
- await IOUtils.remove(oldWorkspacesPath);
- }
- },
-
- /**
- * Private helper method to notify observers with a list of changed UUIDs.
- * @param {string} event - The observer event name.
- * @param {Array} uuids - Array of changed workspace UUIDs.
- */
- _notifyWorkspacesChanged(event, uuids) {
- if (uuids.length === 0) return; // No changes to notify
-
- // Convert the array of UUIDs to a JSON string
- const data = JSON.stringify(uuids);
-
- Services.obs.notifyObservers(null, event, data);
- },
-
- async saveWorkspace(workspace, notifyObservers = true) {
- const changedUUIDs = new Set();
-
- await this.lazy.PlacesUtils.withConnectionWrapper(
- 'ZenWorkspacesStorage.saveWorkspace',
- async (db) => {
- await db.executeTransaction(async () => {
- const now = Date.now();
-
- let newPosition;
- if ('position' in workspace && Number.isFinite(workspace.position)) {
- newPosition = workspace.position;
- } else {
- // Get the maximum position
- const maxPositionResult = await db.execute(
- `SELECT MAX("position") as max_position FROM zen_workspaces`
- );
- const maxPosition = maxPositionResult[0].getResultByName('max_position') || 0;
- newPosition = maxPosition + 1000; // Add a large increment to avoid frequent reordering
- }
-
- // Insert or replace the workspace
- await db.executeCached(
- `
- INSERT OR REPLACE INTO zen_workspaces (
- uuid, name, icon, container_id, created_at, updated_at, "position",
- theme_type, theme_colors, theme_opacity, theme_rotation, theme_texture
- ) VALUES (
- :uuid, :name, :icon, :container_id,
- COALESCE((SELECT created_at FROM zen_workspaces WHERE uuid = :uuid), :now),
- :now,
- :position,
- :theme_type, :theme_colors, :theme_opacity, :theme_rotation, :theme_texture
- )
- `,
- {
- uuid: workspace.uuid,
- name: workspace.name,
- icon: workspace.icon || null,
- container_id: workspace.containerTabId || null,
- now,
- position: newPosition,
- theme_type: workspace.theme?.type || null,
- theme_colors: workspace.theme ? JSON.stringify(workspace.theme.gradientColors) : null,
- theme_opacity: workspace.theme?.opacity || null,
- theme_rotation: workspace.theme?.rotation || null,
- theme_texture: workspace.theme?.texture || null,
- }
- );
-
- // Record the change
- await db.execute(
- `
- INSERT OR REPLACE INTO zen_workspaces_changes (uuid, timestamp)
- VALUES (:uuid, :timestamp)
- `,
- {
- uuid: workspace.uuid,
- timestamp: Math.floor(now / 1000),
- }
- );
-
- changedUUIDs.add(workspace.uuid);
-
- await this.updateLastChangeTimestamp(db);
- });
- }
- );
-
- if (notifyObservers) {
- this._notifyWorkspacesChanged('zen-workspace-updated', Array.from(changedUUIDs));
- }
- },
-
- async getWorkspaces() {
- const db = await this.lazy.PlacesUtils.promiseDBConnection();
- const rows = await db.executeCached(`
- SELECT * FROM zen_workspaces ORDER BY created_at ASC
- `);
- return rows.map((row) => ({
- uuid: row.getResultByName('uuid'),
- name: row.getResultByName('name'),
- icon: row.getResultByName('icon'),
- containerTabId: row.getResultByName('container_id') ?? 0,
- position: row.getResultByName('position'),
- theme: row.getResultByName('theme_type')
- ? {
- type: row.getResultByName('theme_type'),
- gradientColors: JSON.parse(row.getResultByName('theme_colors')),
- opacity: row.getResultByName('theme_opacity'),
- rotation: row.getResultByName('theme_rotation'),
- texture: row.getResultByName('theme_texture'),
- }
- : null,
- }));
- },
-
- async removeWorkspace(uuid, notifyObservers = true) {
- const changedUUIDs = [uuid];
-
- await this.lazy.PlacesUtils.withConnectionWrapper(
- 'ZenWorkspacesStorage.removeWorkspace',
- async (db) => {
- await db.execute(
- `
- DELETE FROM zen_workspaces WHERE uuid = :uuid
- `,
- { uuid }
- );
-
- // Record the removal as a change
- const now = Date.now();
- await db.execute(
- `
- INSERT OR REPLACE INTO zen_workspaces_changes (uuid, timestamp)
- VALUES (:uuid, :timestamp)
- `,
- {
- uuid,
- timestamp: Math.floor(now / 1000),
- }
- );
-
- await this.updateLastChangeTimestamp(db);
- }
- );
-
- if (notifyObservers) {
- this._notifyWorkspacesChanged('zen-workspace-removed', changedUUIDs);
- }
- },
-
- async wipeAllWorkspaces() {
- await this.lazy.PlacesUtils.withConnectionWrapper(
- 'ZenWorkspacesStorage.wipeAllWorkspaces',
- async (db) => {
- await db.execute(`DELETE FROM zen_workspaces`);
- await db.execute(`DELETE FROM zen_workspaces_changes`);
- await this.updateLastChangeTimestamp(db);
- }
- );
- },
-
- async markChanged(uuid) {
- await this.lazy.PlacesUtils.withConnectionWrapper(
- 'ZenWorkspacesStorage.markChanged',
- async (db) => {
- const now = Date.now();
- await db.execute(
- `
- INSERT OR REPLACE INTO zen_workspaces_changes (uuid, timestamp)
- VALUES (:uuid, :timestamp)
- `,
- {
- uuid,
- timestamp: Math.floor(now / 1000),
- }
- );
- }
- );
- },
-
- async saveWorkspaceTheme(uuid, theme, notifyObservers = true) {
- const changedUUIDs = [uuid];
- await this.lazy.PlacesUtils.withConnectionWrapper('saveWorkspaceTheme', async (db) => {
- await db.execute(
- `
- UPDATE zen_workspaces
- SET
- theme_type = :type,
- theme_colors = :colors,
- theme_opacity = :opacity,
- theme_rotation = :rotation,
- theme_texture = :texture,
- updated_at = :now
- WHERE uuid = :uuid
- `,
- {
- type: theme.type,
- colors: JSON.stringify(theme.gradientColors),
- opacity: theme.opacity,
- rotation: theme.rotation,
- texture: theme.texture,
- now: Date.now(),
- uuid,
- }
- );
-
- await this.markChanged(uuid);
- await this.updateLastChangeTimestamp(db);
- });
-
- if (notifyObservers) {
- this._notifyWorkspacesChanged('zen-workspace-updated', changedUUIDs);
- }
- },
-
- async getChangedIDs() {
- const db = await this.lazy.PlacesUtils.promiseDBConnection();
- const rows = await db.execute(`
- SELECT uuid, timestamp FROM zen_workspaces_changes
- `);
- const changes = {};
- for (const row of rows) {
- changes[row.getResultByName('uuid')] = row.getResultByName('timestamp');
- }
- return changes;
- },
-
- async clearChangedIDs() {
- await this.lazy.PlacesUtils.withConnectionWrapper(
- 'ZenWorkspacesStorage.clearChangedIDs',
- async (db) => {
- await db.execute(`DELETE FROM zen_workspaces_changes`);
- }
- );
- },
-
- shouldReorderWorkspaces(before, current, after) {
- const minGap = 1; // Minimum allowed gap between positions
- return (
- (before !== null && current - before < minGap) || (after !== null && after - current < minGap)
- );
- },
-
- async reorderAllWorkspaces(db, changedUUIDs) {
- const workspaces = await db.execute(`
- SELECT uuid
- FROM zen_workspaces
- ORDER BY "position" ASC
- `);
-
- for (let i = 0; i < workspaces.length; i++) {
- const newPosition = (i + 1) * 1000; // Use large increments
- await db.execute(
- `
- UPDATE zen_workspaces
- SET "position" = :newPosition
- WHERE uuid = :uuid
- `,
- { newPosition, uuid: workspaces[i].getResultByName('uuid') }
- );
- changedUUIDs.add(workspaces[i].getResultByName('uuid'));
- }
- },
-
- async updateLastChangeTimestamp(db) {
- const now = Date.now();
- await db.execute(
- `
- INSERT OR REPLACE INTO moz_meta (key, value)
- VALUES ('zen_workspaces_last_change', :now)
- `,
- { now }
- );
- },
-
- async getLastChangeTimestamp() {
- const db = await this.lazy.PlacesUtils.promiseDBConnection();
- const result = await db.executeCached(`
- SELECT value FROM moz_meta WHERE key = 'zen_workspaces_last_change'
- `);
- return result.length ? parseInt(result[0].getResultByName('value'), 10) : 0;
- },
-
- async updateWorkspacePositions(workspaces) {
- const changedUUIDs = new Set();
-
- await this.lazy.PlacesUtils.withConnectionWrapper(
- 'ZenWorkspacesStorage.updateWorkspacePositions',
- async (db) => {
- await db.executeTransaction(async () => {
- const now = Date.now();
-
- for (let i = 0; i < workspaces.length; i++) {
- const workspace = workspaces[i];
- const newPosition = (i + 1) * 1000;
-
- await db.execute(
- `
- UPDATE zen_workspaces
- SET "position" = :newPosition
- WHERE uuid = :uuid
- `,
- { newPosition, uuid: workspace.uuid }
- );
-
- changedUUIDs.add(workspace.uuid);
-
- // Record the change
- await db.execute(
- `
- INSERT OR REPLACE INTO zen_workspaces_changes (uuid, timestamp)
- VALUES (:uuid, :timestamp)
- `,
- {
- uuid: workspace.uuid,
- timestamp: Math.floor(now / 1000),
- }
- );
- }
-
- await this.updateLastChangeTimestamp(db);
- });
- }
- );
-
- this._notifyWorkspacesChanged('zen-workspace-updated', Array.from(changedUUIDs));
- },
-};
-
-// Integration of workspace-specific bookmarks into Places
-window.ZenWorkspaceBookmarksStorage = {
- async init() {
- await this._ensureTable();
- },
-
- async _ensureTable() {
- await ZenWorkspacesStorage.lazy.PlacesUtils.withConnectionWrapper(
'ZenWorkspaceBookmarksStorage.init',
async (db) => {
// Create table using GUIDs instead of IDs
@@ -498,7 +81,7 @@ window.ZenWorkspaceBookmarksStorage = {
* @returns {Promise} The timestamp of the last change.
*/
async getLastChangeTimestamp() {
- const db = await ZenWorkspacesStorage.lazy.PlacesUtils.promiseDBConnection();
+ const db = await this.lazy.PlacesUtils.promiseDBConnection();
const result = await db.executeCached(`
SELECT value FROM moz_meta WHERE key = 'zen_bookmarks_workspaces_last_change'
`);
@@ -506,7 +89,7 @@ window.ZenWorkspaceBookmarksStorage = {
},
async getBookmarkWorkspaces(bookmarkGuid) {
- const db = await ZenWorkspacesStorage.lazy.PlacesUtils.promiseDBConnection();
+ const db = await this.lazy.PlacesUtils.promiseDBConnection();
const rows = await db.execute(
`
@@ -531,7 +114,7 @@ window.ZenWorkspaceBookmarksStorage = {
* }
*/
async getBookmarkGuidsByWorkspace() {
- const db = await ZenWorkspacesStorage.lazy.PlacesUtils.promiseDBConnection();
+ const db = await this.lazy.PlacesUtils.promiseDBConnection();
const rows = await db.execute(`
SELECT workspace_uuid, GROUP_CONCAT(bookmark_guid) as bookmark_guids
@@ -554,7 +137,7 @@ window.ZenWorkspaceBookmarksStorage = {
* @returns {Promise