diff --git a/locales/en-US/browser/browser/preferences/zen-preferences.ftl b/locales/en-US/browser/browser/preferences/zen-preferences.ftl index ea49e2517..d053124e2 100644 --- a/locales/en-US/browser/browser/preferences/zen-preferences.ftl +++ b/locales/en-US/browser/browser/preferences/zen-preferences.ftl @@ -56,6 +56,9 @@ pane-settings-workspaces-title = Workspaces zen-tabs-unloader-enabled = .label = Enable Tab Unloader +zen-tabs-close-on-back-with-no-history = + .label = Close tab and switch to its owner tab (or most recently used tab) when going back with no history + zen-look-and-feel-compact-toolbar-themed = .label = Use themed background for compact toolbar diff --git a/prefs/zen/zen.yaml b/prefs/zen/zen.yaml index 469622931..c44a0e626 100644 --- a/prefs/zen/zen.yaml +++ b/prefs/zen/zen.yaml @@ -34,3 +34,6 @@ - name: zen.tabs.open-pinned-in-new-tab value: true + +- name: zen.tabs.close-on-back-with-no-history + value: true diff --git a/src/browser/base/content/browser-commands-js.patch b/src/browser/base/content/browser-commands-js.patch index a475e0749..3c0aabfee 100644 --- a/src/browser/base/content/browser-commands-js.patch +++ b/src/browser/base/content/browser-commands-js.patch @@ -1,8 +1,19 @@ diff --git a/browser/base/content/browser-commands.js b/browser/base/content/browser-commands.js -index 939ca497b882b3f4200141ba1b6764fb5c846f45..36c830a503b004c0a332340f686c86cad68e9381 100644 +index 939ca497b882b3f4200141ba1b6764fb5c846f45..779cba5ff3df856a246321a36caa3725c054a314 100644 --- a/browser/base/content/browser-commands.js +++ b/browser/base/content/browser-commands.js -@@ -315,6 +315,10 @@ var BrowserCommands = { +@@ -14,6 +14,10 @@ var BrowserCommands = { + const where = BrowserUtils.whereToOpenLink(aEvent, false, true); + + if (where == "current") { ++ if (!gBrowser.webNavigation.canGoBack && gZenCommonActions.shouldCloseTabOnBack()) { ++ gBrowser.removeTab(gBrowser.selectedTab); ++ return; ++ } + try { + gBrowser.goBack(); + } catch (ex) {} +@@ -315,6 +319,10 @@ var BrowserCommands = { } } @@ -13,7 +24,7 @@ index 939ca497b882b3f4200141ba1b6764fb5c846f45..36c830a503b004c0a332340f686c86ca // A notification intended to be useful for modular peformance tracking // starting as close as is reasonably possible to the time when the user // expressed the intent to open a new tab. Since there are a lot of -@@ -399,6 +403,11 @@ var BrowserCommands = { +@@ -399,6 +407,11 @@ var BrowserCommands = { return; } @@ -25,7 +36,7 @@ index 939ca497b882b3f4200141ba1b6764fb5c846f45..36c830a503b004c0a332340f686c86ca // Keyboard shortcuts that would close a tab that is pinned select the first // unpinned tab instead. if ( -@@ -406,8 +415,8 @@ var BrowserCommands = { +@@ -406,8 +419,8 @@ var BrowserCommands = { (event.ctrlKey || event.metaKey || event.altKey) && gBrowser.selectedTab.pinned ) { diff --git a/src/browser/base/content/browser-gestureSupport-js.patch b/src/browser/base/content/browser-gestureSupport-js.patch new file mode 100644 index 000000000..ef205d5df --- /dev/null +++ b/src/browser/base/content/browser-gestureSupport-js.patch @@ -0,0 +1,13 @@ +diff --git a/browser/base/content/browser-gestureSupport.js b/browser/base/content/browser-gestureSupport.js +index a28d54bf72c0e6495b9586f220d1859aac794936..66154668b9f85ffbaacea1e8351370659260227b 100644 +--- a/browser/base/content/browser-gestureSupport.js ++++ b/browser/base/content/browser-gestureSupport.js +@@ -832,7 +832,7 @@ var gHistorySwipeAnimation = { + * @return true if there is a previous page in history, false otherwise. + */ + canGoBack: function HSA_canGoBack() { +- return gBrowser.webNavigation.canGoBack; ++ return gBrowser.webNavigation.canGoBack || gZenCommonActions.shouldCloseTabOnBack(); + }, + + /** diff --git a/src/browser/base/content/browser-js.patch b/src/browser/base/content/browser-js.patch index 8d2658275..4165d22e5 100644 --- a/src/browser/base/content/browser-js.patch +++ b/src/browser/base/content/browser-js.patch @@ -1,5 +1,5 @@ diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js -index b4b79e7fb3228ba91bd8afa08659be0d88883725..9e1d59b6a736107e55bda73686a4a627e4c0ba7c 100644 +index b4b79e7fb3228ba91bd8afa08659be0d88883725..b4801e2a3076139622d58f81943e61cd61ee1828 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -31,6 +31,7 @@ ChromeUtils.defineESModuleGetters(this, { @@ -10,7 +10,21 @@ index b4b79e7fb3228ba91bd8afa08659be0d88883725..9e1d59b6a736107e55bda73686a4a627 DevToolsSocketStatus: "resource://devtools/shared/security/DevToolsSocketStatus.sys.mjs", DownloadUtils: "resource://gre/modules/DownloadUtils.sys.mjs", -@@ -2298,6 +2299,8 @@ var XULBrowserWindow = { +@@ -822,7 +823,12 @@ function UpdateBackForwardCommands(aWebNavigation) { + + var backDisabled = backCommand.hasAttribute("disabled"); + var forwardDisabled = forwardCommand.hasAttribute("disabled"); +- if (backDisabled == aWebNavigation.canGoBack) { ++ var canGoBack = aWebNavigation.canGoBack; ++ if (!canGoBack) { ++ canGoBack = gZenCommonActions.shouldCloseTabOnBack(); ++ } ++ ++ if (backDisabled == canGoBack) { + if (backDisabled) { + backCommand.removeAttribute("disabled"); + } else { +@@ -2298,6 +2304,8 @@ var XULBrowserWindow = { AboutReaderParent.updateReaderButton(gBrowser.selectedBrowser); TranslationsParent.onLocationChange(gBrowser.selectedBrowser); @@ -19,7 +33,7 @@ index b4b79e7fb3228ba91bd8afa08659be0d88883725..9e1d59b6a736107e55bda73686a4a627 PictureInPicture.updateUrlbarToggle(gBrowser.selectedBrowser); if (!gMultiProcessBrowser) { -@@ -3809,7 +3812,7 @@ function warnAboutClosingWindow() { +@@ -3809,7 +3817,7 @@ function warnAboutClosingWindow() { if (!isPBWindow && !toolbar.visible) { return gBrowser.warnAboutClosingTabs( @@ -28,7 +42,7 @@ index b4b79e7fb3228ba91bd8afa08659be0d88883725..9e1d59b6a736107e55bda73686a4a627 gBrowser.closingTabsEnum.ALL ); } -@@ -3849,7 +3852,7 @@ function warnAboutClosingWindow() { +@@ -3849,7 +3857,7 @@ function warnAboutClosingWindow() { return ( isPBWindow || gBrowser.warnAboutClosingTabs( @@ -37,7 +51,7 @@ index b4b79e7fb3228ba91bd8afa08659be0d88883725..9e1d59b6a736107e55bda73686a4a627 gBrowser.closingTabsEnum.ALL ) ); -@@ -3874,7 +3877,7 @@ function warnAboutClosingWindow() { +@@ -3874,7 +3882,7 @@ function warnAboutClosingWindow() { AppConstants.platform != "macosx" || isPBWindow || gBrowser.warnAboutClosingTabs( @@ -46,7 +60,7 @@ index b4b79e7fb3228ba91bd8afa08659be0d88883725..9e1d59b6a736107e55bda73686a4a627 gBrowser.closingTabsEnum.ALL ) ); -@@ -4796,6 +4799,9 @@ var ConfirmationHint = { +@@ -4796,6 +4804,9 @@ var ConfirmationHint = { MozXULElement.insertFTLIfNeeded("toolkit/branding/brandings.ftl"); MozXULElement.insertFTLIfNeeded("browser/confirmationHints.ftl"); document.l10n.setAttributes(this._message, messageId, options.l10nArgs); diff --git a/src/browser/components/preferences/zenTabsManagement.inc.xhtml b/src/browser/components/preferences/zenTabsManagement.inc.xhtml index b8693566f..2943b863b 100644 --- a/src/browser/components/preferences/zenTabsManagement.inc.xhtml +++ b/src/browser/components/preferences/zenTabsManagement.inc.xhtml @@ -26,6 +26,9 @@ + f.apply(this, args), delay); }; }, + + /** + * Determines if a tab should be closed when navigating back with no history. + * Only tabs with an owner that are not pinned and not empty are eligible. + * Respects the user preference zen.tabs.close-on-back-with-no-history. + * + * @return {boolean} True if the tab should be closed on back + */ + shouldCloseTabOnBack() { + if (!Services.prefs.getBoolPref('zen.tabs.close-on-back-with-no-history', true)) { + return false; + } + const tab = gBrowser.selectedTab; + return Boolean(tab.owner && !tab.pinned && !tab.hasAttribute('zen-empty-tab')); + }, }; diff --git a/src/zen/tabs/ZenPinnedTabManager.mjs b/src/zen/tabs/ZenPinnedTabManager.mjs index eebfadf06..779f3566b 100644 --- a/src/zen/tabs/ZenPinnedTabManager.mjs +++ b/src/zen/tabs/ZenPinnedTabManager.mjs @@ -778,10 +778,6 @@ return; } const existingPin = this._pinsCache.find((p) => p.uuid === pin.uuid); - if (existingPin && existingPin === pin) { - // We want to avoid unnecessary writes - return; - } if (existingPin) { Object.assign(existingPin, pin); } else { diff --git a/src/zen/tabs/ZenPinnedTabsStorage.mjs b/src/zen/tabs/ZenPinnedTabsStorage.mjs index 425dbf2d1..bc11213f7 100644 --- a/src/zen/tabs/ZenPinnedTabsStorage.mjs +++ b/src/zen/tabs/ZenPinnedTabsStorage.mjs @@ -2,6 +2,8 @@ // 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/. var ZenPinnedTabsStorage = { + _saveCache: [], + async init() { await this._ensureTable(); }, @@ -74,6 +76,22 @@ var ZenPinnedTabsStorage = { }, async savePin(pin, notifyObservers = true) { + // If we find the exact same pin in the cache, skip saving + const existingIndex = this._saveCache.findIndex((cachedPin) => cachedPin.uuid === pin.uuid); + if (existingIndex !== -1) { + const existingPin = this._saveCache[existingIndex]; + const isSame = Object.keys(pin).every((key) => pin[key] === existingPin[key]); + if (isSame) { + return; // No changes, skip saving + } else { + // Update the cached pin + this._saveCache[existingIndex] = pin; + } + } else { + // Add to cache + this._saveCache.push(pin); + } + const changedUUIDs = new Set(); await PlacesUtils.withConnectionWrapper('ZenPinnedTabsStorage.savePin', async (db) => { @@ -389,6 +407,11 @@ var ZenPinnedTabsStorage = { }, async removePin(uuid, notifyObservers = true) { + const cachedIndex = this._saveCache.findIndex((cachedPin) => cachedPin.uuid === uuid); + if (cachedIndex !== -1) { + this._saveCache.splice(cachedIndex, 1); + } + const changedUUIDs = [uuid]; await PlacesUtils.withConnectionWrapper('ZenPinnedTabsStorage.removePin', async (db) => {