From 598c299e180d478f987ce886115cbfb35f219830 Mon Sep 17 00:00:00 2001 From: "mr. m" <91018726+mr-cheffy@users.noreply.github.com> Date: Sun, 19 Apr 2026 16:14:25 +0200 Subject: [PATCH] no-bug: Add `Move to folder` context menu item (gh-13315) --- .../browser/browser/zen-vertical-tabs.ftl | 4 + scripts/mar_sign.sh | 25 +++++-- .../sessionstore/SessionStore-sys-mjs.patch | 10 ++- .../tabbrowser/content/tabbrowser-js.patch | 74 ++++++++++--------- src/zen/folders/ZenFolders.mjs | 73 +++++++++++++++++- .../sessionstore/ZenSessionManager.sys.mjs | 5 ++ src/zen/tabs/ZenPinnedTabManager.mjs | 32 +------- 7 files changed, 152 insertions(+), 71 deletions(-) diff --git a/locales/en-US/browser/browser/zen-vertical-tabs.ftl b/locales/en-US/browser/browser/zen-vertical-tabs.ftl index f54a0d3b0..73e4a2a03 100644 --- a/locales/en-US/browser/browser/zen-vertical-tabs.ftl +++ b/locales/en-US/browser/browser/zen-vertical-tabs.ftl @@ -19,6 +19,10 @@ zen-toolbar-context-compact-mode-hide-both = .label = Hide both .accesskey = H +zen-toolbar-context-move-to-folder = + .label = Move to Folder... + .accesskey = M + zen-toolbar-context-new-folder = .label = New Folder .accesskey = N diff --git a/scripts/mar_sign.sh b/scripts/mar_sign.sh index e0c02802b..6a9fac2e0 100644 --- a/scripts/mar_sign.sh +++ b/scripts/mar_sign.sh @@ -17,13 +17,16 @@ generate_certs() { openssl genrsa -out private_key.pem 4096 # 2. Generate self-signed certificate (required for PKCS#12 bundling) + # RFC 5280 "no well-defined expiration" sentinel: 99991231235959Z openssl req -new -x509 \ -key private_key.pem \ -out cert.pem \ - -subj "/CN=MAR Signing" + -subj "/CN=MAR Signing" \ + -not_before 20000101000000Z \ + -not_after 99991231235959Z - # 3. Export public key as SPKI DER (for embedding in updater) - openssl rsa -in private_key.pem -pubout -outform DER -out public_key.der + # 3. Export certificate as DER (for embedding in updater) + openssl x509 -in cert.pem -outform DER -out public_key.der cd .. mkdir -p "$CERT_PATH_DIR" @@ -35,9 +38,10 @@ generate_certs() { base64 -w 0 "$CERT_PATH_DIR"/cert.pem > "$CERT_PATH_DIR"/env/ZEN_SIGNING_CERT_PEM_BASE64 base64 -w 0 "$CERT_PATH_DIR"/private_key.pem > "$CERT_PATH_DIR"/env/ZEN_SIGNING_PRIVATE_KEY_PEM_BASE64 - # Verify public key - openssl rsa -in "$CERT_PATH_DIR"/public_key.der \ - -pubin -inform DER -text -noout + # Make sure no private keys or certs are left + # in the public_key.der file, which is the only one that + # should be distributed and embedded in the updater + openssl x509 -in "$CERT_PATH_DIR"/public_key.der -inform DER -noout -text > /dev/null rm -rf temp } @@ -196,6 +200,15 @@ sign_mars() { "$SIGNMAR" -d "$NSS_CONFIG_DIR" -n "mar_sig" -s "$mar_file" "$mar_file".signed echo "Signed $mar_file. Verifying signature..." "$SIGNMAR" -d "$NSS_CONFIG_DIR" -n "mar_sig" -v "$mar_file".signed + if [ $? -ne 0 ]; then + echo "Signature verification failed for $mar_file.signed" >&2 + exit 1 + fi + "$SIGNMAR" -D "build/signing/public_key.der" -v "$mar_file".signed + if [ $? -ne 0 ]; then + echo "Public key verification failed for $mar_file.signed" >&2 + exit 1 + fi mv "$mar_file".signed "$mar_file" echo "Successfully signed $mar_file" update_manifests "$mar_file" diff --git a/src/browser/components/sessionstore/SessionStore-sys-mjs.patch b/src/browser/components/sessionstore/SessionStore-sys-mjs.patch index 27134f020..84ecce245 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 71921cec2b6aa0f103856c31254fd6c4affefccb..d39f2bd587e9aa320d69f94a2bd30be909c3e608 100644 +index 71921cec2b6aa0f103856c31254fd6c4affefccb..ed13d7e2aa5ff1199872fedbc26494aae463ace4 100644 --- a/browser/components/sessionstore/SessionStore.sys.mjs +++ b/browser/components/sessionstore/SessionStore.sys.mjs @@ -129,6 +129,9 @@ const TAB_EVENTS = [ @@ -358,3 +358,11 @@ index 71921cec2b6aa0f103856c31254fd6c4affefccb..d39f2bd587e9aa320d69f94a2bd30be9 Glean.sessionRestore.shutdownFlushAllOutcomes.timed_out.add(1); deferred.resolve(); } +@@ -8552,6 +8614,7 @@ var SessionStoreInternal = { + if ( + !savedTabGroupState.tabs.length || + this.getSavedTabGroup(savedTabGroupState.id) ++ && false + ) { + return; + } diff --git a/src/browser/components/tabbrowser/content/tabbrowser-js.patch b/src/browser/components/tabbrowser/content/tabbrowser-js.patch index 5becce548..35b9a51e4 100644 --- a/src/browser/components/tabbrowser/content/tabbrowser-js.patch +++ b/src/browser/components/tabbrowser/content/tabbrowser-js.patch @@ -1,5 +1,5 @@ diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/components/tabbrowser/content/tabbrowser.js -index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..2e1151dd1ac42b01529e746ee3b45c35e66ff55a 100644 +index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..451feb3d412cbf20f2a65bfa73bdbafb7506bdc5 100644 --- a/browser/components/tabbrowser/content/tabbrowser.js +++ b/browser/components/tabbrowser/content/tabbrowser.js @@ -413,6 +413,7 @@ @@ -683,7 +683,15 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..2e1151dd1ac42b01529e746ee3b45c35 if (!this._windowIsClosing) { // update tab close buttons state this.tabContainer._updateCloseButtons(); -@@ -6225,6 +6393,7 @@ +@@ -6180,6 +6348,7 @@ + memory_after: await getTotalMemoryUsage(), + time_to_unload_in_ms: timeElapsed, + }); ++ return true; + } + + /** +@@ -6225,6 +6394,7 @@ } let excludeTabs = new Set(aExcludeTabs); @@ -691,7 +699,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..2e1151dd1ac42b01529e746ee3b45c35 // If this tab has a successor, it should be selectable, since // hiding or closing a tab removes that tab as a successor. -@@ -6237,15 +6406,22 @@ +@@ -6237,15 +6407,22 @@ !excludeTabs.has(aTab.owner) && Services.prefs.getBoolPref("browser.tabs.selectOwnerOnClose") ) { @@ -716,7 +724,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..2e1151dd1ac42b01529e746ee3b45c35 let tab = this.tabContainer.findNextTab(aTab, { direction: 1, filter: _tab => remainingTabs.includes(_tab), -@@ -6259,7 +6435,7 @@ +@@ -6259,7 +6436,7 @@ } if (tab) { @@ -725,7 +733,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..2e1151dd1ac42b01529e746ee3b45c35 } // If no qualifying visible tab was found, see if there is a tab in -@@ -6280,7 +6456,7 @@ +@@ -6280,7 +6457,7 @@ }); } @@ -734,7 +742,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..2e1151dd1ac42b01529e746ee3b45c35 } _blurTab(aTab) { -@@ -6291,7 +6467,7 @@ +@@ -6291,7 +6468,7 @@ * @returns {boolean} * False if swapping isn't permitted, true otherwise. */ @@ -743,7 +751,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..2e1151dd1ac42b01529e746ee3b45c35 // Do not allow transfering a private tab to a non-private window // and vice versa. if ( -@@ -6345,6 +6521,7 @@ +@@ -6345,6 +6522,7 @@ // fire the beforeunload event in the process. Close the other // window if this was its last tab. if ( @@ -751,7 +759,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..2e1151dd1ac42b01529e746ee3b45c35 !remoteBrowser._beginRemoveTab(aOtherTab, { adoptedByTab: aOurTab, closeWindowWithLastTab: true, -@@ -6356,7 +6533,7 @@ +@@ -6356,7 +6534,7 @@ // If this is the last tab of the window, hide the window // immediately without animation before the docshell swap, to avoid // about:blank being painted. @@ -760,7 +768,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..2e1151dd1ac42b01529e746ee3b45c35 if (closeWindow) { let win = aOtherTab.ownerGlobal; win.windowUtils.suppressAnimation(true); -@@ -6484,11 +6661,13 @@ +@@ -6484,11 +6662,13 @@ } // Finish tearing down the tab that's going away. @@ -774,7 +782,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..2e1151dd1ac42b01529e746ee3b45c35 this.setTabTitle(aOurTab); -@@ -6690,10 +6869,10 @@ +@@ -6690,10 +6870,10 @@ SessionStore.deleteCustomTabValue(aTab, "hiddenBy"); } @@ -787,7 +795,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..2e1151dd1ac42b01529e746ee3b45c35 aTab.selected || aTab.closing || // Tabs that are sharing the screen, microphone or camera cannot be hidden. -@@ -6753,7 +6932,8 @@ +@@ -6753,7 +6933,8 @@ * @param {object} [aOptions={}] * Key-value pairs that will be serialized into the features string. */ @@ -797,7 +805,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..2e1151dd1ac42b01529e746ee3b45c35 if (this.tabs.length == 1) { return null; } -@@ -6770,7 +6950,7 @@ +@@ -6770,7 +6951,7 @@ // tell a new window to take the "dropped" tab let args = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray); args.appendElement(aTab.splitview ?? aTab); @@ -806,7 +814,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..2e1151dd1ac42b01529e746ee3b45c35 private: PrivateBrowsingUtils.isWindowPrivate(window), features: Object.entries(aOptions) .map(([key, value]) => `${key}=${value}`) -@@ -6778,6 +6958,8 @@ +@@ -6778,6 +6959,8 @@ openerWindow: window, args, }); @@ -815,7 +823,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..2e1151dd1ac42b01529e746ee3b45c35 } /** -@@ -6890,7 +7072,7 @@ +@@ -6890,7 +7073,7 @@ * `true` if element is a `` */ isTabGroup(element) { @@ -824,7 +832,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..2e1151dd1ac42b01529e746ee3b45c35 } /** -@@ -6975,8 +7157,8 @@ +@@ -6975,8 +7158,8 @@ } // Don't allow mixing pinned and unpinned tabs. @@ -835,7 +843,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..2e1151dd1ac42b01529e746ee3b45c35 } else { tabIndex = Math.max(tabIndex, this.pinnedTabCount); } -@@ -7005,13 +7187,19 @@ +@@ -7005,13 +7188,19 @@ this.#handleTabMove( element, () => { @@ -857,7 +865,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..2e1151dd1ac42b01529e746ee3b45c35 let useAfter = false; if (this.isTab(element)) { useAfter = neighbor && tabIndex > element._tPos; -@@ -7076,23 +7264,31 @@ +@@ -7076,23 +7265,31 @@ #moveTabNextTo(element, targetElement, moveBefore = false, metricsContext) { if (this.isTabGroupLabel(targetElement)) { targetElement = targetElement.group; @@ -895,7 +903,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..2e1151dd1ac42b01529e746ee3b45c35 } else if (!element.pinned && targetElement && targetElement.pinned) { // If the caller asks to move an unpinned element next to a pinned // tab, move the unpinned element to be the first unpinned element -@@ -7105,12 +7301,35 @@ +@@ -7105,12 +7302,35 @@ // move the tab group right before the first unpinned tab. // 4. Moving a tab group and the first unpinned tab is grouped: // move the tab group right before the first unpinned tab's tab group. @@ -932,7 +940,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..2e1151dd1ac42b01529e746ee3b45c35 // We want to include the splitview wrapper if it's the targetElement, but // not in the case where we want to reverse tabs within the same splitview. -@@ -7119,6 +7338,7 @@ +@@ -7119,6 +7339,7 @@ } let getContainer = () => @@ -940,7 +948,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..2e1151dd1ac42b01529e746ee3b45c35 element.pinned ? this.tabContainer.pinnedTabsContainer : this.tabContainer; -@@ -7127,11 +7347,15 @@ +@@ -7127,11 +7348,15 @@ element, () => { if (moveBefore) { @@ -957,7 +965,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..2e1151dd1ac42b01529e746ee3b45c35 } }, metricsContext -@@ -7205,10 +7429,10 @@ +@@ -7205,10 +7430,10 @@ * @param {TabMetricsContext} [metricsContext] */ moveTabToExistingGroup(aTab, aGroup, metricsContext) { @@ -970,7 +978,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..2e1151dd1ac42b01529e746ee3b45c35 return; } if (aTab.group && aTab.group.id === aGroup.id) { -@@ -7281,6 +7505,7 @@ +@@ -7281,6 +7506,7 @@ let state = { tabIndex: tab._tPos, @@ -978,7 +986,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..2e1151dd1ac42b01529e746ee3b45c35 }; if (tab.visible) { state.elementIndex = tab.elementIndex; -@@ -7312,7 +7537,7 @@ +@@ -7312,7 +7538,7 @@ let changedSplitView = previousTabState.splitViewId != currentTabState.splitViewId; @@ -987,7 +995,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..2e1151dd1ac42b01529e746ee3b45c35 tab.dispatchEvent( new CustomEvent("TabMove", { bubbles: true, -@@ -7354,6 +7579,10 @@ +@@ -7354,6 +7580,10 @@ moveActionCallback(); @@ -998,7 +1006,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..2e1151dd1ac42b01529e746ee3b45c35 // Clear tabs cache after moving nodes because the order of tabs may have // changed. this.tabContainer._invalidateCachedTabs(); -@@ -7404,7 +7633,22 @@ +@@ -7404,7 +7634,22 @@ * @returns {object} * The new tab in the current window, null if the tab couldn't be adopted. */ @@ -1022,7 +1030,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..2e1151dd1ac42b01529e746ee3b45c35 // Swap the dropped tab with a new one we create and then close // it in the other window (making it seem to have moved between // windows). We also ensure that the tab we create to swap into has -@@ -7447,6 +7691,8 @@ +@@ -7447,6 +7692,8 @@ } params.skipLoad = true; let newTab = this.addWebTab("about:blank", params); @@ -1031,7 +1039,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..2e1151dd1ac42b01529e746ee3b45c35 aTab.container.tabDragAndDrop.finishAnimateTabMove(); -@@ -8149,7 +8395,7 @@ +@@ -8149,7 +8396,7 @@ // preventDefault(). It will still raise the window if appropriate. return; } @@ -1040,7 +1048,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..2e1151dd1ac42b01529e746ee3b45c35 window.focus(); aEvent.preventDefault(); } -@@ -8166,7 +8412,6 @@ +@@ -8166,7 +8413,6 @@ on_TabGroupCollapse(aEvent) { aEvent.target.tabs.forEach(tab => { @@ -1048,7 +1056,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..2e1151dd1ac42b01529e746ee3b45c35 }); } -@@ -8500,7 +8745,9 @@ +@@ -8500,7 +8746,9 @@ let filter = this._tabFilters.get(tab); if (filter) { @@ -1058,7 +1066,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..2e1151dd1ac42b01529e746ee3b45c35 let listener = this._tabListeners.get(tab); if (listener) { -@@ -9306,6 +9553,7 @@ +@@ -9306,6 +9554,7 @@ aWebProgress.isTopLevel ) { this.mTab.setAttribute("busy", "true"); @@ -1066,7 +1074,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..2e1151dd1ac42b01529e746ee3b45c35 gBrowser._tabAttrModified(this.mTab, ["busy"]); this.mTab._notselectedsinceload = !this.mTab.selected; } -@@ -9386,6 +9634,7 @@ +@@ -9386,6 +9635,7 @@ // known defaults. Note we use the original URL since about:newtab // redirects to a prerendered page. const shouldRemoveFavicon = @@ -1074,7 +1082,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..2e1151dd1ac42b01529e746ee3b45c35 !this.mBrowser.mIconURL && !ignoreBlank && !(originalLocation.spec in FAVICON_DEFAULTS); -@@ -9560,13 +9809,6 @@ +@@ -9560,13 +9810,6 @@ this.mBrowser.originalURI = aRequest.originalURI; } @@ -1088,7 +1096,7 @@ index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..2e1151dd1ac42b01529e746ee3b45c35 } let userContextId = this.mBrowser.getAttribute("usercontextid") || 0; -@@ -10450,7 +10692,7 @@ var TabContextMenu = { +@@ -10450,7 +10693,7 @@ var TabContextMenu = { ); contextUnpinSelectedTabs.hidden = !this.contextTab.pinned || !this.multiselected; diff --git a/src/zen/folders/ZenFolders.mjs b/src/zen/folders/ZenFolders.mjs index 08a60a24c..fe2b7c2c7 100644 --- a/src/zen/folders/ZenFolders.mjs +++ b/src/zen/folders/ZenFolders.mjs @@ -65,9 +65,14 @@ class nsZenFolders extends nsZenDOMOperatedFeature { } #initContextMenu() { - const contextMenuItems = window.MozXULElement.parseXULToFragment( - `` - ); + const contextMenuItems = window.MozXULElement.parseXULToFragment(` + + + + + + + `); document.getElementById("context_moveTabToGroup").before(contextMenuItems); const contextMenuItemsToolbar = window.MozXULElement.parseXULToFragment( ` @@ -227,6 +232,7 @@ class nsZenFolders extends nsZenDOMOperatedFeature { document .getElementById("zen-context-menu-new-folder-toolbar") .addEventListener("command", onNewFolder); + this.#initMoveTabToFolder(); SessionStore.promiseInitialized.then(() => { gBrowser.tabContainer.addEventListener( "dragstart", @@ -235,6 +241,67 @@ class nsZenFolders extends nsZenDOMOperatedFeature { }); } + #initMoveTabToFolder() { + const moveTabToFolderMenu = document.getElementById( + "context_zenMoveToFolder" + ); + moveTabToFolderMenu.addEventListener("popupshowing", () => { + const separator = moveTabToFolderMenu.querySelector("menuseparator"); + let tabs = TabContextMenu.contextTab?.multiselected + ? gBrowser.selectedTabs + : [TabContextMenu.contextTab]; + let groups = gBrowser.tabGroups.filter(group => { + const isZenFolder = group?.isZenFolder; + const isLiveFolder = group?.isLiveFolder; + const spaceId = group?.getAttribute("zen-workspace-id"); + if ( + !isZenFolder || + isLiveFolder || + spaceId !== gZenWorkspaces.activeWorkspace + ) { + return false; + } + return !tabs.some(tab => tab.group === group); + }); + separator.hidden = groups.length === 0; + for (const group of groups) { + const icon = group.iconURL; + const menuItem = document.createXULElement("menuitem"); + menuItem.setAttribute("label", group.label); + menuItem.classList.add("context-zen-move-to-folder-item"); + if (icon) { + menuItem.setAttribute("image", icon); + } + menuItem._group = group; + separator.before(menuItem); + } + }); + + moveTabToFolderMenu.addEventListener("popuphidden", () => { + const items = moveTabToFolderMenu.querySelectorAll( + ".context-zen-move-to-folder-item" + ); + for (const item of items) { + delete item._group; + item.remove(); + } + }); + + moveTabToFolderMenu.addEventListener("command", event => { + if (!event.target.classList.contains("context-zen-move-to-folder-item")) { + return; + } + const group = event.target._group; + if (!group) { + return; + } + let tabs = TabContextMenu.contextTab?.multiselected + ? gBrowser.selectedTabs + : [TabContextMenu.contextTab]; + group.addTabs(tabs); + }); + } + handleEvent(aEvent) { let methodName = `on_${aEvent.type}`; if (methodName in this) { diff --git a/src/zen/sessionstore/ZenSessionManager.sys.mjs b/src/zen/sessionstore/ZenSessionManager.sys.mjs index 791d24c68..ae2321a76 100644 --- a/src/zen/sessionstore/ZenSessionManager.sys.mjs +++ b/src/zen/sessionstore/ZenSessionManager.sys.mjs @@ -357,6 +357,11 @@ export class nsZenSessionManager { if (this._shouldRunMigration) { initialState = this.#runStateMigration(initialState); } + // Clear the memory of the groups saved in the session file, + // as we don't really need them anyways. + if (initialState?.savedGroups) { + initialState.savedGroups = []; + } if (!lazy.gWindowSyncEnabled) { if (initialState?.windows?.length && this.#shouldRestoreOnlyPinned) { this.log("Window sync disabled, restoring only pinned tabs"); diff --git a/src/zen/tabs/ZenPinnedTabManager.mjs b/src/zen/tabs/ZenPinnedTabManager.mjs index 7270eef67..2b6376a88 100644 --- a/src/zen/tabs/ZenPinnedTabManager.mjs +++ b/src/zen/tabs/ZenPinnedTabManager.mjs @@ -285,8 +285,6 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature { return; } - const selectedTabs = pinnedTabs.filter(tab => tab.selected); - event.stopPropagation(); event.preventDefault(); @@ -366,14 +364,14 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature { return; } } - await gBrowser.explicitUnloadTabs(pinnedTabs); + let successful = await gBrowser.explicitUnloadTabs(pinnedTabs); + if (!successful) { + return; + } for (const tab of pinnedTabs) { tab.removeAttribute("discarded"); } } - if (selectedTabs.length) { - this._handleTabSwitch(selectedTabs[0]); - } if (behavior.includes("reset")) { for (const tab of pinnedTabs) { this.#resetTabToStoredState(tab); @@ -392,28 +390,6 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature { } } - _handleTabSwitch(selectedTab) { - if (selectedTab !== gBrowser.selectedTab) { - return; - } - const findNextTab = direction => - gBrowser.tabContainer.findNextTab(selectedTab, { - direction, - filter: tab => !tab.hidden && !tab.pinned, - }); - - let nextTab = findNextTab(1) || findNextTab(-1); - - if (!nextTab) { - gZenWorkspaces.selectEmptyTab(); - return; - } - - if (nextTab) { - gBrowser.selectedTab = nextTab; - } - } - #resetTabToStoredState(tab) { const state = this.#getTabState(tab);