diff --git a/src/zen/spaces/ZenSpaceBookmarksStorage.js b/src/zen/spaces/ZenSpaceBookmarksStorage.js index e9d94e06c6..f4e577370f 100644 --- a/src/zen/spaces/ZenSpaceBookmarksStorage.js +++ b/src/zen/spaces/ZenSpaceBookmarksStorage.js @@ -13,6 +13,9 @@ window.ZenWorkspaceBookmarksStorage = { if (!window.gZenWorkspaces) { return; } + this.promiseInitialized = new Promise(resolve => { + this._resolveInitialized = resolve; + }); await this._ensureTable(); }, @@ -54,9 +57,12 @@ window.ZenWorkspaceBookmarksStorage = { // Create index for changes tracking await db.execute(` - CREATE INDEX IF NOT EXISTS idx_bookmarks_workspaces_changes - ON zen_bookmarks_workspaces_changes(bookmark_guid, workspace_uuid) - `); + CREATE INDEX IF NOT EXISTS idx_bookmarks_workspaces_changes + ON zen_bookmarks_workspaces_changes(bookmark_guid, workspace_uuid) + `); + + this._resolveInitialized(); + delete this._resolveInitialized; } ); }, @@ -68,6 +74,7 @@ window.ZenWorkspaceBookmarksStorage = { */ async updateLastChangeTimestamp(db) { const now = Date.now(); + await this.promiseInitialized; await db.execute( ` INSERT OR REPLACE INTO moz_meta (key, value) @@ -84,6 +91,7 @@ window.ZenWorkspaceBookmarksStorage = { */ async getLastChangeTimestamp() { const db = await this.lazy.PlacesUtils.promiseDBConnection(); + await this.promiseInitialized; const result = await db.executeCached(` SELECT value FROM moz_meta WHERE key = 'zen_bookmarks_workspaces_last_change' `); @@ -91,16 +99,21 @@ window.ZenWorkspaceBookmarksStorage = { }, async getBookmarkWorkspaces(bookmarkGuid) { + await this.promiseInitialized; const db = await this.lazy.PlacesUtils.promiseDBConnection(); - - const rows = await db.execute( - ` + let rows = []; + try { + rows = await db.execute( + ` SELECT workspace_uuid FROM zen_bookmarks_workspaces WHERE bookmark_guid = :bookmark_guid `, - { bookmark_guid: bookmarkGuid } - ); + { bookmark_guid: bookmarkGuid } + ); + } catch (e) { + console.error("Error fetching bookmark workspaces:", e); + } return rows.map(row => row.getResultByName("workspace_uuid")); }, @@ -117,8 +130,8 @@ window.ZenWorkspaceBookmarksStorage = { * } */ async getBookmarkGuidsByWorkspace() { + await this.promiseInitialized; const db = await this.lazy.PlacesUtils.promiseDBConnection(); - const rows = await db.execute(` SELECT workspace_uuid, GROUP_CONCAT(bookmark_guid) as bookmark_guids FROM zen_bookmarks_workspaces @@ -141,6 +154,7 @@ window.ZenWorkspaceBookmarksStorage = { * @returns {Promise} An object mapping bookmark+workspace pairs to their change data. */ async getChangedIDs() { + await this.promiseInitialized; const db = await this.lazy.PlacesUtils.promiseDBConnection(); const rows = await db.execute(` SELECT bookmark_guid, workspace_uuid, change_type, timestamp @@ -162,6 +176,7 @@ window.ZenWorkspaceBookmarksStorage = { * Clear all recorded changes. */ async clearChangedIDs() { + await this.promiseInitialized; await this.lazy.PlacesUtils.withConnectionWrapper( "ZenWorkspaceBookmarksStorage.clearChangedIDs", async db => { diff --git a/src/zen/spaces/ZenSpaceManager.mjs b/src/zen/spaces/ZenSpaceManager.mjs index 094045d89f..e1d619ab88 100644 --- a/src/zen/spaces/ZenSpaceManager.mjs +++ b/src/zen/spaces/ZenSpaceManager.mjs @@ -1261,12 +1261,23 @@ class nsZenWorkspaces { } removeWorkspace(windowID) { + let { promise, resolve } = Promise.withResolvers(); + this.#deleteWorkspaceOwnedTabs(windowID); let workspacesData = this.getWorkspaces(); // Remove the workspace from the cache workspacesData = workspacesData.filter( workspace => workspace.uuid !== windowID ); + window.addEventListener( + "ZenWorkspacesUIUpdate", + () => { + resolve(); + }, + { once: true } + ); this.#propagateWorkspaceData(workspacesData); + gBrowser.tabContainer._invalidateCachedVisibleTabs(); + return promise; } isWorkspaceActive(workspace) { @@ -1472,6 +1483,18 @@ class nsZenWorkspaces { }); } + #deleteWorkspaceOwnedTabs(workspaceID) { + const tabs = this.allStoredTabs.filter( + tab => + tab.getAttribute("zen-workspace-id") === workspaceID && + !tab.hasAttribute("zen-essential") && + !(tab.hasAttribute("zen-empty-tab") && !tab.group) + ); + gBrowser.removeTabs(tabs, { + closeWindowWithLastTab: false, + }); + } + async unloadWorkspace() { const workspaceId = this.#contextMenuData?.workspaceId || this.activeWorkspace; @@ -1659,7 +1682,7 @@ class nsZenWorkspaces { onInit, previousWorkspace.uuid ); - if (tabToSelect.linkedBrowser) { + if (tabToSelect?.linkedBrowser) { gBrowser.warmupTab(tabToSelect); } diff --git a/src/zen/tests/moz.build b/src/zen/tests/moz.build index d8529fe677..17ebbab072 100644 --- a/src/zen/tests/moz.build +++ b/src/zen/tests/moz.build @@ -9,13 +9,13 @@ BROWSER_CHROME_MANIFESTS += [ "glance/browser.toml", "live-folders/browser.toml", "pinned/browser.toml", + "spaces/browser.toml", "split_view/browser.toml", "tabs/browser.toml", "ub-actions/browser.toml", "urlbar/browser.toml", "welcome/browser.toml", "window_sync/browser.toml", - "workspaces/browser.toml", ] DIRS += [ diff --git a/src/zen/tests/workspaces/browser.toml b/src/zen/tests/spaces/browser.toml similarity index 100% rename from src/zen/tests/workspaces/browser.toml rename to src/zen/tests/spaces/browser.toml diff --git a/src/zen/tests/workspaces/browser_basic_workspaces.js b/src/zen/tests/spaces/browser_basic_workspaces.js similarity index 96% rename from src/zen/tests/workspaces/browser_basic_workspaces.js rename to src/zen/tests/spaces/browser_basic_workspaces.js index e3ec5f6e3a..c12a1bc3e8 100644 --- a/src/zen/tests/workspaces/browser_basic_workspaces.js +++ b/src/zen/tests/spaces/browser_basic_workspaces.js @@ -26,7 +26,7 @@ add_task(async function test_Check_Creation() { await gZenWorkspaces.removeWorkspace(gZenWorkspaces.activeWorkspace); const workspacesAfterRemove = gZenWorkspaces.getWorkspaces(); Assert.strictEqual( - workspacesAfterRemove.workspaces.length, + workspacesAfterRemove.length, 1, "One workspace should exist." ); diff --git a/src/zen/tests/workspaces/browser_change_to_empty.js b/src/zen/tests/spaces/browser_change_to_empty.js similarity index 100% rename from src/zen/tests/workspaces/browser_change_to_empty.js rename to src/zen/tests/spaces/browser_change_to_empty.js diff --git a/src/zen/tests/workspaces/browser_double_click_newtab.js b/src/zen/tests/spaces/browser_double_click_newtab.js similarity index 100% rename from src/zen/tests/workspaces/browser_double_click_newtab.js rename to src/zen/tests/spaces/browser_double_click_newtab.js diff --git a/src/zen/tests/workspaces/browser_issue_10455.js b/src/zen/tests/spaces/browser_issue_10455.js similarity index 98% rename from src/zen/tests/workspaces/browser_issue_10455.js rename to src/zen/tests/spaces/browser_issue_10455.js index a4cf4115df..bf6b6de6cf 100644 --- a/src/zen/tests/workspaces/browser_issue_10455.js +++ b/src/zen/tests/spaces/browser_issue_10455.js @@ -4,6 +4,7 @@ "use strict"; add_task(async function test_Issue_10455() { + debugger; await SpecialPowers.pushPrefEnv({ set: [["browser.tabs.closeWindowWithLastTab", true]], }); @@ -24,6 +25,7 @@ add_task(async function test_Issue_10455() { }); add_task(async function test_Issue_10455_Dont_Close() { + debugger; await SpecialPowers.pushPrefEnv({ set: [["browser.tabs.closeWindowWithLastTab", false]], }); diff --git a/src/zen/tests/workspaces/browser_issue_8699.js b/src/zen/tests/spaces/browser_issue_8699.js similarity index 100% rename from src/zen/tests/workspaces/browser_issue_8699.js rename to src/zen/tests/spaces/browser_issue_8699.js diff --git a/src/zen/tests/workspaces/browser_issue_9900.js b/src/zen/tests/spaces/browser_issue_9900.js similarity index 100% rename from src/zen/tests/workspaces/browser_issue_9900.js rename to src/zen/tests/spaces/browser_issue_9900.js diff --git a/src/zen/tests/workspaces/browser_overflow_scrollbox.js b/src/zen/tests/spaces/browser_overflow_scrollbox.js similarity index 100% rename from src/zen/tests/workspaces/browser_overflow_scrollbox.js rename to src/zen/tests/spaces/browser_overflow_scrollbox.js diff --git a/src/zen/tests/workspaces/browser_private_mode.js b/src/zen/tests/spaces/browser_private_mode.js similarity index 100% rename from src/zen/tests/workspaces/browser_private_mode.js rename to src/zen/tests/spaces/browser_private_mode.js diff --git a/src/zen/tests/workspaces/browser_private_mode_startup.js b/src/zen/tests/spaces/browser_private_mode_startup.js similarity index 100% rename from src/zen/tests/workspaces/browser_private_mode_startup.js rename to src/zen/tests/spaces/browser_private_mode_startup.js diff --git a/src/zen/tests/workspaces/browser_workspace_bookmarks.js b/src/zen/tests/spaces/browser_workspace_bookmarks.js similarity index 100% rename from src/zen/tests/workspaces/browser_workspace_bookmarks.js rename to src/zen/tests/spaces/browser_workspace_bookmarks.js diff --git a/src/zen/tests/workspaces/head.js b/src/zen/tests/spaces/head.js similarity index 100% rename from src/zen/tests/workspaces/head.js rename to src/zen/tests/spaces/head.js diff --git a/src/zen/tests/workspaces/browser_workspace_unload.js b/src/zen/tests/workspaces/browser_workspace_unload.js deleted file mode 100644 index ec3954962d..0000000000 --- a/src/zen/tests/workspaces/browser_workspace_unload.js +++ /dev/null @@ -1,132 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - https://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -// verify that workspace unloading works -add_task(async function test_UnloadWorkspace_WithMultipleTabs() { - const workspaceId = - await gZenWorkspaces.createAndSaveWorkspace("Test Workspace 1"); - const tabs = []; - for (let i = 0; i < 3; i++) { - const tab = await BrowserTestUtils.openNewForegroundTab( - window.gBrowser, - `data:text/html,Workspace Tab ${i}`, - true, - { skipAnimation: true } - ); - tab.setAttribute("zen-workspace-id", workspaceId); - tabs.push(tab); - } - - for (const tab of tabs) { - ok(!tab.hasAttribute("pending"), "Tab should not be pending before unload"); - ok(tab.linkedPanel, "Tab should have linked panel before unload"); - } - - await gZenWorkspaces.unloadWorkspace(); - - for (const tab of tabs) { - ok(tab.hasAttribute("pending"), "Tab should be pending after unload"); - ok(!tab.linkedPanel, "Tab should not have linked panel after unload"); - } - - await gZenWorkspaces.removeWorkspace(workspaceId); -}); - -// verify that essential tabs are not unloaded -add_task(async function test_UnloadWorkspace_WithEssentialTabs() { - const workspaceId = - await gZenWorkspaces.createAndSaveWorkspace("Test Workspace 2"); - - const regularTab = await BrowserTestUtils.openNewForegroundTab( - window.gBrowser, - "data:text/html,Hi! I am a Regular Tab", - true, - { skipAnimation: true } - ); - regularTab.setAttribute("zen-workspace-id", workspaceId); - - const essentialTab = await BrowserTestUtils.openNewForegroundTab( - window.gBrowser, - "data:text/html,Hi! I am an Essential Tab", - true, - { skipAnimation: true } - ); - essentialTab.setAttribute("zen-workspace-id", workspaceId); - essentialTab.setAttribute("zen-essential", "true"); - - await gZenWorkspaces.unloadWorkspace(); - - ok(regularTab.hasAttribute("pending"), "Regular tab should be unloaded"); - ok(!regularTab.linkedPanel, "Regular tab should not have linked panel"); - - ok( - !essentialTab.hasAttribute("pending"), - "Essential tab should not be unloaded" - ); - ok(essentialTab.linkedPanel, "Essential tab should still have linked panel"); - - await gZenWorkspaces.removeWorkspace(workspaceId); -}); - -// only tabs from the targeted workspace should be unloaded -add_task(async function test_UnloadWorkspace_TargetedWorkspaceIsolation() { - const inActiveWorkspaceId = await gZenWorkspaces.createAndSaveWorkspace( - "Test In-Active Workspace" - ); - const activeWorkspaceId = await gZenWorkspaces.createAndSaveWorkspace( - "Test Active Workspace" - ); - - const inActiveWorkspaceTabs = []; - for (let i = 0; i < 2; i++) { - const tab = await BrowserTestUtils.openNewForegroundTab( - window.gBrowser, - `data:text/html,In-Active Workspace Tab ${i}`, - true, - { skipAnimation: true } - ); - tab.setAttribute("zen-workspace-id", inActiveWorkspaceId); - inActiveWorkspaceTabs.push(tab); - } - - const activeWorkspaceTabs = []; - for (let i = 0; i < 2; i++) { - const tab = await BrowserTestUtils.openNewForegroundTab( - window.gBrowser, - `data:text/html,Active Workspace Tab ${i}`, - true, - { skipAnimation: true } - ); - tab.setAttribute("zen-workspace-id", activeWorkspaceId); - activeWorkspaceTabs.push(tab); - } - - await gZenWorkspaces.unloadWorkspace(); // this unloads the latest created workspace -> activeWorkspaceId - - for (const tab of activeWorkspaceTabs) { - ok( - tab.hasAttribute("pending"), - "Active workspace tab should be pending after unload" - ); - ok( - !tab.linkedPanel, - "Active workspace tab should not have linked panel after unload" - ); - } - - for (const tab of inActiveWorkspaceTabs) { - ok( - !tab.hasAttribute("pending"), - "In-Active workspace tab should NOT be pending after unload" - ); - ok( - tab.linkedPanel, - "In-Active workspace tab should still have linked panel after unload" - ); - } - - await gZenWorkspaces.removeWorkspace(inActiveWorkspaceId); - await gZenWorkspaces.removeWorkspace(activeWorkspaceId); -});