feat: Replace zen's tab unloader with firefox's one. Also added more tests, b=(no-bug), c=common, tabs, tests, glance

This commit is contained in:
Mr. M 2025-05-23 21:01:33 +02:00
parent 6f5d20fd49
commit ebfc885745
No known key found for this signature in database
GPG key ID: 6292C4C8F8652B18
24 changed files with 358 additions and 378 deletions

2
l10n

@ -1 +1 @@
Subproject commit 4d0df55d514b54626faa1f2fc06eb020feeb7f6d
Subproject commit 644474b8c92e306288d835698eb6714081a650d8

View file

@ -97,10 +97,6 @@ pref('zen.keyboard.shortcuts.disable-mainkeyset-clear', false); // for debugging
pref('zen.themes.updated-value-observer', false);
pref('zen.tab-unloader.enabled', true);
pref('zen.tab-unloader.timeout-minutes', 40);
pref('zen.tab-unloader.excluded-urls', "example.com,example.org");
pref('zen.pinned-tab-manager.debug', false);
pref('zen.pinned-tab-manager.restore-pinned-tabs-to-pinned-url', false);
pref('zen.pinned-tab-manager.close-shortcut-behavior', 'reset-unload-switch');

View file

@ -1,5 +1,5 @@
diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js
index 6d664917a5a3bb1cea8a747e42e8bc0065ec999e..7fe04b426a8dd3c8dbb71065da047f8f48b8e84c 100644
index 6d664917a5a3bb1cea8a747e42e8bc0065ec999e..5059293ebfa29d646455b7a3505bd6eae408ba64 100644
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -33,6 +33,7 @@ ChromeUtils.defineESModuleGetters(this, {
@ -10,19 +10,18 @@ index 6d664917a5a3bb1cea8a747e42e8bc0065ec999e..7fe04b426a8dd3c8dbb71065da047f8f
DevToolsSocketStatus:
"resource://devtools/shared/security/DevToolsSocketStatus.sys.mjs",
DownloadUtils: "resource://gre/modules/DownloadUtils.sys.mjs",
@@ -2340,6 +2341,11 @@ var XULBrowserWindow = {
@@ -2340,6 +2341,10 @@ var XULBrowserWindow = {
AboutReaderParent.updateReaderButton(gBrowser.selectedBrowser);
TranslationsParent.onLocationChange(gBrowser.selectedBrowser);
+ gZenViewSplitter.onLocationChange(gBrowser.selectedBrowser);
+ gZenWorkspaces.onLocationChange(gBrowser.selectedBrowser);
+ gZenTabUnloader.onLocationChange(gBrowser.selectedBrowser);
+ gZenPinnedTabManager.onLocationChange(gBrowser.selectedBrowser);
+
PictureInPicture.updateUrlbarToggle(gBrowser.selectedBrowser);
if (!gMultiProcessBrowser) {
@@ -4816,7 +4822,7 @@ function switchToTabHavingURI(
@@ -4816,7 +4821,7 @@ function switchToTabHavingURI(
ignoreQueryString || replaceQueryString,
ignoreFragmentWhenComparing
);
@ -31,7 +30,7 @@ index 6d664917a5a3bb1cea8a747e42e8bc0065ec999e..7fe04b426a8dd3c8dbb71065da047f8f
for (let i = 0; i < browsers.length; i++) {
let browser = browsers[i];
let browserCompare = cleanURL(
@@ -4859,7 +4865,7 @@ function switchToTabHavingURI(
@@ -4859,7 +4864,7 @@ function switchToTabHavingURI(
}
if (!doAdopt) {

View file

@ -34,7 +34,6 @@
<script src="chrome://browser/content/zen-components/ZenThemesCommon.mjs"></script>
<script src="chrome://browser/content/zen-components/ZenThemesImporter.mjs"></script>
<script src="chrome://browser/content/zen-components/ZenCompactMode.mjs"></script>
<script src="chrome://browser/content/zen-components/ZenTabUnloader.mjs"></script>
<script src="chrome://browser/content/zen-components/ZenPinnedTabsStorage.mjs"></script>
<script src="chrome://browser/content/zen-components/ZenWorkspacesStorage.mjs"></script>
<script src="chrome://browser/content/zen-components/ZenPinnedTabManager.mjs"></script>

View file

@ -50,7 +50,6 @@
content/browser/zen-components/ZenKeyboardShortcuts.mjs (../../zen/kbs/ZenKeyboardShortcuts.mjs)
content/browser/zen-components/ZenTabUnloader.mjs (../../zen/tabs/ZenTabUnloader.mjs)
content/browser/zen-components/ZenPinnedTabsStorage.mjs (../../zen/tabs/ZenPinnedTabsStorage.mjs)
content/browser/zen-components/ZenPinnedTabManager.mjs (../../zen/tabs/ZenPinnedTabManager.mjs)
* content/browser/zen-styles/zen-tabs.css (../../zen/tabs/zen-tabs.css)

View file

@ -43,10 +43,6 @@
<command id="cmd_zenCopyCurrentURL" />
<command id="cmd_zenCopyCurrentURLMarkdown" />
<command id="cmd_zenUnloadTab" />
<command id="cmd_zenPreventUnloadTab" />
<command id="cmd_zenIgnoreUnloadTab" />
</commandset>
<keyset id="zenKeyset"></keyset>

View file

@ -37,18 +37,7 @@
<checkbox id="zenTabsUnloadActivate"
data-l10n-id="zen-tabs-unloader-enabled"
preference="zen.tab-unloader.enabled"/>
<label><html:h2 data-l10n-id="zen-tabs-unloader-unload-delay"/></label>
<hbox id="zenTabsUnloadDelayContainer">
<description class="description-deemphasized" data-l10n-id="zen-tabs-unloader-unload-delay-description" />
<html:input id="zenTabsUnloadDelay"
type="number"
min="1"
max="1000"
preference="zen.tab-unloader.timeout-minutes"/>
</hbox>
preference="browser.tabs.unloadOnLowMemory"/>
</groupbox>
<hbox id="zenPinnedTabsManagerCategory"

View file

@ -0,0 +1,13 @@
diff --git a/browser/components/tabbrowser/TabUnloader.sys.mjs b/browser/components/tabbrowser/TabUnloader.sys.mjs
index 44846cc902fd3fc17d12be38ac9abccb47a12f23..c715c7cf47486066b3fd6f92bf78bc8a720759bc 100644
--- a/browser/components/tabbrowser/TabUnloader.sys.mjs
+++ b/browser/components/tabbrowser/TabUnloader.sys.mjs
@@ -52,7 +52,7 @@ let CRITERIA_WEIGHT = 1;
*/
let DefaultTabUnloaderMethods = {
isNonDiscardable(tab, weight) {
- if (tab.undiscardable || tab.selected) {
+ if (tab.undiscardable || tab.selected || tab.zenModeActive) {
return weight;
}

View file

@ -1,5 +1,5 @@
diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/components/tabbrowser/content/tabbrowser.js
index d5aa64842a35c6697263c63fd3a0571b64b01344..bcae8a44593d7b3e5efdd999b4c2f5bcac221632 100644
index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b032cf200f1 100644
--- a/browser/components/tabbrowser/content/tabbrowser.js
+++ b/browser/components/tabbrowser/content/tabbrowser.js
@@ -413,11 +413,41 @@
@ -491,17 +491,6 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..bcae8a44593d7b3e5efdd999b4c2f5bc
if (!this._windowIsClosing) {
if (wasPinned) {
this.tabContainer._positionPinnedTabs();
@@ -5141,8 +5266,8 @@
return closedCount;
}
- async explicitUnloadTabs(tabs) {
- let unloadBlocked = await this.runBeforeUnloadForTabs(tabs);
+ async explicitUnloadTabs(tabs, skipPermitUnload = false) {
+ let unloadBlocked = skipPermitUnload ? false : await this.runBeforeUnloadForTabs(tabs);
if (unloadBlocked) {
return;
}
@@ -5230,6 +5355,7 @@
}

View file

@ -1051,8 +1051,7 @@ menuitem[id='placesContext_new:separator'] {
--menu-image: url('close-all.svg');
}
#context_zenUnloadTab,
#context_zenUnloadWebPanel,
#context_unloadTab,
#context_zenTabActions {
--menu-image: url('close-all.svg');
}

View file

@ -6,7 +6,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
class ZenUIMigration {
PREF_NAME = 'zen.migration.version';
MIGRATION_VERSION = 3;
MIGRATION_VERSION = 4;
init(isNewProfile, win) {
if (!isNewProfile) {
@ -33,6 +33,9 @@ class ZenUIMigration {
if (this._migrationVersion < 3) {
this._migrateV3(win);
}
if (this._migrationVersion < 4) {
this._migrateV4(win);
}
}
clearVariables() {
@ -82,6 +85,13 @@ class ZenUIMigration {
win.CustomizableUI.removeWidgetFromArea(widgetId);
}
}
_migrateV4(win) {
Services.prefs.setBoolPref(
'browser.tabs.unloadOnLowMemory',
Services.prefs.getBoolPref('zen.tab-unloader.enabled', true)
);
}
}
export var gZenUIMigration = new ZenUIMigration();

View file

@ -88,15 +88,6 @@ document.addEventListener(
case 'cmd_zenRemoveFromEssentials':
gZenPinnedTabManager.removeEssentials();
break;
case 'cmd_zenUnloadTab':
gZenTabUnloader.unloadTab();
break;
case 'cmd_zenPreventUnloadTab':
gZenTabUnloader.preventUnloadTab();
break;
case 'cmd_zenIgnoreUnloadTab':
gZenTabUnloader.ignoreUnloadTab();
break;
default:
if (event.target.id.startsWith('cmd_zenWorkspaceSwitch')) {
const index = parseInt(event.target.id.replace('cmd_zenWorkspaceSwitch', ''), 10) - 1;

View file

@ -397,10 +397,10 @@
);
}
_onTabClick(e) {
async _onTabClick(e) {
const tab = e.target?.closest('tab');
if (e.button === 1 && tab) {
this._onCloseTabShortcut(e, tab);
await this._onCloseTabShortcut(e, tab);
}
}
@ -533,7 +533,7 @@
}
}
_onCloseTabShortcut(
async _onCloseTabShortcut(
event,
selectedTab = gBrowser.selectedTab,
behavior = lazy.zenPinnedTabCloseShortcutBehavior
@ -554,22 +554,17 @@
case 'unload-switch':
case 'reset-switch':
case 'switch':
let { permitUnload } = selectedTab.linkedBrowser?.permitUnload();
if (!permitUnload) {
return;
}
this._handleTabSwitch(selectedTab);
if (behavior.includes('reset')) {
this._resetTabToStoredState(selectedTab);
}
if (behavior.includes('unload')) {
if (selectedTab.hasAttribute('glance-id')) {
break;
}
// Do not unload about:* pages
if (!selectedTab.linkedBrowser?.currentURI.spec.startsWith('about:')) {
gZenTabUnloader.explicitUnloadTabs([selectedTab], { permitUnload });
await gBrowser.explicitUnloadTabs([selectedTab]);
}
if (selectedTab.selected) {
this._handleTabSwitch(selectedTab);
}
if (behavior.includes('reset')) {
this._resetTabToStoredState(selectedTab);
}
break;
case 'reset':

View file

@ -1,310 +0,0 @@
{
const lazy = {};
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
'zenTabUnloaderEnabled',
'zen.tab-unloader.enabled',
false
);
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
'zenTabUnloaderTimeout',
'zen.tab-unloader.timeout-minutes',
20
);
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
'zenTabUnloaderExcludedUrls',
'zen.tab-unloader.excluded-urls',
''
);
const ZEN_TAB_UNLOADER_DEFAULT_EXCLUDED_URLS = [
'^about:',
'^chrome:',
'^devtools:',
'^file:',
'^resource:',
'^view-source:',
'^view-image:',
];
class ZenTabsObserver {
static ALL_EVENTS = [
'TabAttrModified',
'TabPinned',
'TabUnpinned',
'TabShow',
'TabHide',
'TabOpen',
'TabClose',
'TabSelect',
'TabMultiSelect',
];
#listeners = [];
constructor() {
this.#listenAllEvents();
}
#listenAllEvents() {
const eventListener = this.#eventListener.bind(this);
for (const event of ZenTabsObserver.ALL_EVENTS) {
window.addEventListener(event, eventListener);
}
window.addEventListener('unload', () => {
for (const event of ZenTabsObserver.ALL_EVENTS) {
window.removeEventListener(event, eventListener);
}
});
}
#eventListener(event) {
for (const listener of this.#listeners) {
listener(event.type, event);
}
}
addTabsListener(listener) {
this.#listeners.push(listener);
}
}
class ZenTabsIntervalUnloader {
static INTERVAL = 1000 * 60; // 1 minute
interval = null;
/** @type {ZenTabUnloader} */
unloader = null;
constructor(unloader) {
this.unloader = unloader;
this.interval = setInterval(
this.intervalListener.bind(this),
ZenTabsIntervalUnloader.INTERVAL
);
}
intervalListener() {
if (!lazy.zenTabUnloaderEnabled) {
return;
}
const currentTimestamp = Date.now();
const tabs = gZenWorkspaces.allStoredTabs;
for (let i = 0; i < tabs.length; i++) {
const tab = tabs[i];
if (this.unloader.canUnloadTab(tab, currentTimestamp)) {
this.unloader.unload(tab);
}
}
}
}
class ZenTabUnloader extends ZenDOMOperatedFeature {
static ACTIVITY_MODIFIERS = ['muted', 'soundplaying', 'label', 'attention'];
#excludedUrls = [];
#compiledExcludedUrls = [];
#lastCheckedUrlTimestamp = 0;
constructor() {
super();
this.#excludedUrls = this.lazyExcludeUrls;
if (!lazy.zenTabUnloaderEnabled) {
return;
}
this.intervalUnloader = new ZenTabsIntervalUnloader(this);
}
init() {
if (!lazy.zenTabUnloaderEnabled) {
return;
}
this.observer = new ZenTabsObserver();
this.observer.addTabsListener(this.onTabEvent.bind(this));
}
onTabEvent(action, event) {
const tab = event.target;
switch (action) {
case 'TabPinned':
case 'TabUnpinned':
case 'TabShow':
case 'TabHide':
break;
case 'TabAttrModified':
this.handleTabAttrModified(tab, event);
break;
case 'TabOpen':
this.handleTabOpen(tab);
break;
case 'TabClose':
this.handleTabClose(tab);
break;
case 'TabSelect':
case 'TabMultiSelect':
this.updateTabActivity(tab);
break;
default:
console.warn('ZenTabUnloader: Unhandled tab event', action);
break;
}
}
onLocationChange(browser) {
const tab = browser.ownerGlobal.gBrowser.getTabForBrowser(browser);
this.updateTabActivity(tab);
}
handleTabClose(tab) {
tab.lastActivity = null;
}
handleTabOpen(tab) {
this.updateTabActivity(tab);
}
handleTabAttrModified(tab, event) {
for (const modifier of ZenTabUnloader.ACTIVITY_MODIFIERS) {
if (event.detail.changed.includes(modifier)) {
this.updateTabActivity(tab);
break;
}
}
}
updateTabActivity(tab) {
const currentTimestamp = Date.now();
tab.lastActivity = currentTimestamp;
}
get lazyExcludeUrls() {
return [
...ZEN_TAB_UNLOADER_DEFAULT_EXCLUDED_URLS,
...lazy.zenTabUnloaderExcludedUrls.split(',').map((url) => url.trim()),
];
}
arraysEqual(a, b) {
if (a === b) return true;
if (a == null || b == null) return false;
if (a.length !== b.length) return false;
const currentTimestamp = Date.now();
if (currentTimestamp - this.#lastCheckedUrlTimestamp < 5 * 1000) {
return true;
}
this.#lastCheckedUrlTimestamp = currentTimestamp;
// If you don't care about the order of the elements inside
// the array, you should sort both arrays here.
// Please note that calling sort on an array will modify that array.
// you might want to clone your array first.
for (let i = 0; i < a.length; ++i) {
if (a[i] !== b[i]) return false;
}
return true;
}
get excludedUrls() {
// Check if excludedrls is the same as the pref value
const excludedUrls = this.lazyExcludeUrls;
if (
!this.arraysEqual(this.#excludedUrls, excludedUrls) ||
!this.#compiledExcludedUrls.length
) {
this.#excludedUrls = excludedUrls;
this.#compiledExcludedUrls = excludedUrls.map((url) => new RegExp(url));
}
return this.#compiledExcludedUrls;
}
unload(tab, skipPermitUnload = false) {
gBrowser.explicitUnloadTabs([tab], skipPermitUnload);
tab.removeAttribute('linkedpanel');
}
unloadTab() {
const tabs = TabContextMenu.contextTab.multiselected
? gBrowser.selectedTabs
: [TabContextMenu.contextTab];
this.explicitUnloadTabs(tabs);
}
explicitUnloadTabs(tabs, extraArgs = {}) {
for (let i = 0; i < tabs.length; i++) {
if (this.canUnloadTab(tabs[i], Date.now(), true, extraArgs)) {
this.unload(tabs[i], true);
}
}
}
preventUnloadTab() {
const tabs = TabContextMenu.contextTab.multiselected
? gBrowser.selectedTabs
: [TabContextMenu.contextTab];
for (let i = 0; i < tabs.length; i++) {
const tab = tabs[i];
tab.zenIgnoreUnload = true;
}
}
ignoreUnloadTab() {
const tabs = TabContextMenu.contextTab.multiselected
? gBrowser.selectedTabs
: [TabContextMenu.contextTab];
for (let i = 0; i < tabs.length; i++) {
const tab = tabs[i];
tab.zenIgnoreUnload = false;
}
}
canUnloadTab(tab, currentTimestamp, ignoreTimestamp = false, extraArgs = {}) {
if (
(tab.pinned && !ignoreTimestamp) ||
tab.selected ||
(tab.multiselected && !ignoreTimestamp) ||
(tab.hasAttribute('busy') && !ignoreTimestamp) ||
!tab.linkedPanel ||
tab.splitView ||
tab.group?.hasAttribute('split-view-group') ||
tab.attention ||
tab.hasAttribute('glance-id') ||
tab.linkedBrowser?.zenModeActive ||
(tab.pictureinpicture && !ignoreTimestamp) ||
(tab.soundPlaying && !ignoreTimestamp) ||
(tab.zenIgnoreUnload && !ignoreTimestamp) ||
(this.excludedUrls.some((url) => url.test(tab.linkedBrowser?.currentURI.spec)) &&
tab.linkedBrowser?.currentURI.spec !== 'about:blank')
) {
return false;
}
if (ignoreTimestamp) {
return this._tabPermitsUnload(tab, extraArgs);
}
const lastActivity = tab.lastActivity;
if (!lastActivity) {
return false;
}
const diff = currentTimestamp - lastActivity;
// Check if the tab has been inactive for more than the timeout
return (
diff > lazy.zenTabUnloaderTimeout * 60 * 1000 && this._tabPermitsUnload(tab, extraArgs)
);
}
_tabPermitsUnload(tab, extraArgs) {
return typeof extraArgs.permitUnload === 'undefined'
? tab.linkedBrowser?.permitUnload()?.permitUnload
: extraArgs.permitUnload;
}
}
window.gZenTabUnloader = new ZenTabUnloader();
}

View file

@ -8,3 +8,4 @@ support-files = [
["browser_glance_close.js"]
["browser_glance_next_tab.js"]
["browser_glance_prev_tab.js"]
["browser_glance_select_parent.js"]

View file

@ -0,0 +1,23 @@
/* Any copyright is dedicated to the Public Domain.
https://creativecommons.org/publicdomain/zero/1.0/ */
'use strict';
add_task(async function test_Glance_Select_Parent() {
await openGlanceOnTab(async (glanceTab) => {
ok(
glanceTab.hasAttribute('zen-glance-tab'),
'The glance tab should have the zen-glance-tab attribute'
);
await BrowserTestUtils.openNewForegroundTab(window.gBrowser, 'https://example.com/', true, {
skipAnimation: true,
});
const tabToRemove = gBrowser.selectedTab;
gBrowser.selectedTab = gZenGlanceManager.getTabOrGlanceParent(glanceTab);
await BrowserTestUtils.waitForCondition(() => {
return glanceTab.selected;
});
ok(true, 'The glance tab should be selected');
await BrowserTestUtils.removeTab(tabToRemove);
});
});

View file

@ -1,3 +1,10 @@
["browser_pinned_unload_changed.js"]
["browser_pinned_unload_noreset.js"]
["browser_pinned_nounload_reset.js"]
["browser_pinned_switch.js"]
["browser_pinned_reset_noswitch.js"]
["browser_pinned_close.js"]
["browser_pinned_changed.js"]
["browser_pinned_created.js"]
["browser_pinned_edit_label.js"]

View file

@ -3,7 +3,7 @@
'use strict';
add_task(async function test_Create_Pinned() {
add_task(async function test_Changed_Pinned() {
let resolvePromise;
const promise = new Promise((resolve) => {
resolvePromise = resolve;

View file

@ -0,0 +1,34 @@
/* Any copyright is dedicated to the Public Domain.
https://creativecommons.org/publicdomain/zero/1.0/ */
'use strict';
add_task(async function test_Unload_NoReset_Pinned() {
await SpecialPowers.pushPrefEnv({
set: [['zen.pinned-tab-manager.close-shortcut-behavior', 'close']],
});
let resolvePromise;
const promise = new Promise((resolve) => {
resolvePromise = resolve;
});
await BrowserTestUtils.withNewTab({ gBrowser, url: 'https://example.com/1' }, async (browser) => {
const tab = gBrowser.getTabForBrowser(browser);
tab.addEventListener(
'ZenPinnedTabCreated',
async function (event) {
const pinTabID = tab.getAttribute('zen-pin-id');
ok(pinTabID, 'The tab should have a zen-pin-id attribute after being pinned');
document.getElementById('cmd_close').doCommand();
setTimeout(() => {
ok(tab.closing, 'The tab should be closing after being closed');
resolvePromise();
}, 100);
},
{ once: true }
);
gBrowser.pinTab(tab);
await promise;
});
});

View file

@ -0,0 +1,51 @@
/* Any copyright is dedicated to the Public Domain.
https://creativecommons.org/publicdomain/zero/1.0/ */
'use strict';
add_task(async function test_NoUnload_Changed_Pinned() {
await SpecialPowers.pushPrefEnv({
set: [['zen.pinned-tab-manager.close-shortcut-behavior', 'reset-switch']],
});
let resolvePromise;
const promise = new Promise((resolve) => {
resolvePromise = resolve;
});
await BrowserTestUtils.withNewTab({ gBrowser, url: 'https://example.com/1' }, async (browser) => {
const tab = gBrowser.getTabForBrowser(browser);
tab.addEventListener(
'ZenPinnedTabCreated',
async function (event) {
const pinTabID = tab.getAttribute('zen-pin-id');
ok(pinTabID, 'The tab should have a zen-pin-id attribute after being pinned');
BrowserTestUtils.startLoadingURIString(browser, 'https://example.com/2');
await BrowserTestUtils.browserLoaded(browser, false, 'https://example.com/2');
setTimeout(() => {
ok(
tab.hasAttribute('zen-pinned-changed'),
'The tab should have a zen-pinned-changed attribute after being pinned'
);
document.getElementById('cmd_close').doCommand();
setTimeout(() => {
ok(
!tab.hasAttribute('zen-pinned-changed'),
'The tab should not have a zen-pinned-changed attribute after being closed'
);
ok(
!tab.hasAttribute('discarded'),
'The tab should not be discarded after being closed'
);
ok(tab != gBrowser.selectedTab, 'The tab should not be selected after being closed');
resolvePromise();
}, 100);
}, 0);
},
{ once: true }
);
gBrowser.pinTab(tab);
await promise;
});
});

View file

@ -0,0 +1,51 @@
/* Any copyright is dedicated to the Public Domain.
https://creativecommons.org/publicdomain/zero/1.0/ */
'use strict';
add_task(async function test_Unload_NoReset_Pinned() {
await SpecialPowers.pushPrefEnv({
set: [['zen.pinned-tab-manager.close-shortcut-behavior', 'reset']],
});
let resolvePromise;
const promise = new Promise((resolve) => {
resolvePromise = resolve;
});
await BrowserTestUtils.withNewTab({ gBrowser, url: 'https://example.com/1' }, async (browser) => {
const tab = gBrowser.getTabForBrowser(browser);
tab.addEventListener(
'ZenPinnedTabCreated',
async function (event) {
const pinTabID = tab.getAttribute('zen-pin-id');
ok(pinTabID, 'The tab should have a zen-pin-id attribute after being pinned');
BrowserTestUtils.startLoadingURIString(browser, 'https://example.com/2');
await BrowserTestUtils.browserLoaded(browser, false, 'https://example.com/2');
setTimeout(() => {
ok(
tab.hasAttribute('zen-pinned-changed'),
'The tab should have a zen-pinned-changed attribute after being pinned'
);
document.getElementById('cmd_close').doCommand();
setTimeout(() => {
ok(
!tab.hasAttribute('zen-pinned-changed'),
'The tab should not have a zen-pinned-changed attribute after being closed'
);
ok(
!tab.hasAttribute('discarded'),
'The tab should not be discarded after being closed'
);
ok(tab === gBrowser.selectedTab, 'The tab should not be selected after being closed');
resolvePromise();
}, 100);
}, 0);
},
{ once: true }
);
gBrowser.pinTab(tab);
await promise;
});
});

View file

@ -0,0 +1,51 @@
/* Any copyright is dedicated to the Public Domain.
https://creativecommons.org/publicdomain/zero/1.0/ */
'use strict';
add_task(async function test_Unload_NoReset_Pinned() {
await SpecialPowers.pushPrefEnv({
set: [['zen.pinned-tab-manager.close-shortcut-behavior', 'switch']],
});
let resolvePromise;
const promise = new Promise((resolve) => {
resolvePromise = resolve;
});
await BrowserTestUtils.withNewTab({ gBrowser, url: 'https://example.com/1' }, async (browser) => {
const tab = gBrowser.getTabForBrowser(browser);
tab.addEventListener(
'ZenPinnedTabCreated',
async function (event) {
const pinTabID = tab.getAttribute('zen-pin-id');
ok(pinTabID, 'The tab should have a zen-pin-id attribute after being pinned');
BrowserTestUtils.startLoadingURIString(browser, 'https://example.com/2');
await BrowserTestUtils.browserLoaded(browser, false, 'https://example.com/2');
setTimeout(() => {
ok(
tab.hasAttribute('zen-pinned-changed'),
'The tab should have a zen-pinned-changed attribute after being pinned'
);
document.getElementById('cmd_close').doCommand();
setTimeout(() => {
ok(
tab.hasAttribute('zen-pinned-changed'),
'The tab should not have a zen-pinned-changed attribute after being closed'
);
ok(
!tab.hasAttribute('discarded'),
'The tab should not be discarded after being closed'
);
ok(tab != gBrowser.selectedTab, 'The tab should not be selected after being closed');
resolvePromise();
}, 100);
}, 0);
},
{ once: true }
);
gBrowser.pinTab(tab);
await promise;
});
});

View file

@ -0,0 +1,49 @@
/* Any copyright is dedicated to the Public Domain.
https://creativecommons.org/publicdomain/zero/1.0/ */
'use strict';
add_task(async function test_Unload_Changed_Pinned() {
await SpecialPowers.pushPrefEnv({
set: [['zen.pinned-tab-manager.close-shortcut-behavior', 'reset-unload-switch']],
});
let resolvePromise;
const promise = new Promise((resolve) => {
resolvePromise = resolve;
});
await BrowserTestUtils.withNewTab({ gBrowser, url: 'https://example.com/1' }, async (browser) => {
const tab = gBrowser.getTabForBrowser(browser);
tab.addEventListener(
'ZenPinnedTabCreated',
async function (event) {
const pinTabID = tab.getAttribute('zen-pin-id');
ok(pinTabID, 'The tab should have a zen-pin-id attribute after being pinned');
BrowserTestUtils.startLoadingURIString(browser, 'https://example.com/2');
await BrowserTestUtils.browserLoaded(browser, false, 'https://example.com/2');
setTimeout(() => {
ok(
tab.hasAttribute('zen-pinned-changed'),
'The tab should have a zen-pinned-changed attribute after being pinned'
);
document.getElementById('cmd_close').doCommand();
setTimeout(() => {
ok(
!tab.hasAttribute('zen-pinned-changed'),
'The tab should not have a zen-pinned-changed attribute after being closed'
);
ok(tab.hasAttribute('discarded'), 'The tab should not be discarded after being closed');
ok(tab != gBrowser.selectedTab, 'The tab should not be selected after being closed');
resolvePromise();
}, 100);
}, 0);
},
{ once: true }
);
gBrowser.pinTab(tab);
await promise;
});
});

View file

@ -0,0 +1,48 @@
/* Any copyright is dedicated to the Public Domain.
https://creativecommons.org/publicdomain/zero/1.0/ */
'use strict';
add_task(async function test_Unload_NoReset_Pinned() {
await SpecialPowers.pushPrefEnv({
set: [['zen.pinned-tab-manager.close-shortcut-behavior', 'unload-switch']],
});
let resolvePromise;
const promise = new Promise((resolve) => {
resolvePromise = resolve;
});
await BrowserTestUtils.withNewTab({ gBrowser, url: 'https://example.com/1' }, async (browser) => {
const tab = gBrowser.getTabForBrowser(browser);
tab.addEventListener(
'ZenPinnedTabCreated',
async function (event) {
const pinTabID = tab.getAttribute('zen-pin-id');
ok(pinTabID, 'The tab should have a zen-pin-id attribute after being pinned');
BrowserTestUtils.startLoadingURIString(browser, 'https://example.com/2');
await BrowserTestUtils.browserLoaded(browser, false, 'https://example.com/2');
setTimeout(() => {
ok(
tab.hasAttribute('zen-pinned-changed'),
'The tab should have a zen-pinned-changed attribute after being pinned'
);
document.getElementById('cmd_close').doCommand();
setTimeout(() => {
ok(
tab.hasAttribute('zen-pinned-changed'),
'The tab should not have a zen-pinned-changed attribute after being closed'
);
ok(tab.hasAttribute('discarded'), 'The tab should not be discarded after being closed');
ok(tab != gBrowser.selectedTab, 'The tab should not be selected after being closed');
resolvePromise();
}, 100);
}, 0);
},
{ once: true }
);
gBrowser.pinTab(tab);
await promise;
});
});