mirror of
https://github.com/zen-browser/desktop.git
synced 2025-07-07 23:00:01 +02:00
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:
parent
6f5d20fd49
commit
ebfc885745
24 changed files with 358 additions and 378 deletions
2
l10n
2
l10n
|
@ -1 +1 @@
|
|||
Subproject commit 4d0df55d514b54626faa1f2fc06eb020feeb7f6d
|
||||
Subproject commit 644474b8c92e306288d835698eb6714081a650d8
|
|
@ -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');
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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"
|
||||
|
|
13
src/browser/components/tabbrowser/TabUnloader-sys-mjs.patch
Normal file
13
src/browser/components/tabbrowser/TabUnloader-sys-mjs.patch
Normal 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;
|
||||
}
|
||||
|
|
@ -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 @@
|
||||
}
|
||||
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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':
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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"]
|
||||
|
|
23
src/zen/tests/glance/browser_glance_select_parent.js
Normal file
23
src/zen/tests/glance/browser_glance_select_parent.js
Normal 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);
|
||||
});
|
||||
});
|
|
@ -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"]
|
||||
|
|
|
@ -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;
|
||||
|
|
34
src/zen/tests/pinned/browser_pinned_close.js
Normal file
34
src/zen/tests/pinned/browser_pinned_close.js
Normal 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;
|
||||
});
|
||||
});
|
51
src/zen/tests/pinned/browser_pinned_nounload_reset.js
Normal file
51
src/zen/tests/pinned/browser_pinned_nounload_reset.js
Normal 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;
|
||||
});
|
||||
});
|
51
src/zen/tests/pinned/browser_pinned_reset_noswitch.js
Normal file
51
src/zen/tests/pinned/browser_pinned_reset_noswitch.js
Normal 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;
|
||||
});
|
||||
});
|
51
src/zen/tests/pinned/browser_pinned_switch.js
Normal file
51
src/zen/tests/pinned/browser_pinned_switch.js
Normal 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;
|
||||
});
|
||||
});
|
49
src/zen/tests/pinned/browser_pinned_unload_changed.js
Normal file
49
src/zen/tests/pinned/browser_pinned_unload_changed.js
Normal 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;
|
||||
});
|
||||
});
|
48
src/zen/tests/pinned/browser_pinned_unload_noreset.js
Normal file
48
src/zen/tests/pinned/browser_pinned_unload_noreset.js
Normal 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;
|
||||
});
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue