mirror of
https://github.com/zen-browser/desktop.git
synced 2026-09-25 05:36:26 +00:00
gh-15522: Fixed toggling folder animations really fast freezing (gh-15532)
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
diff --git a/browser/base/content/browser-init.js b/browser/base/content/browser-init.js
|
||||
index 0aa4baea8e5e6d742fc6cd3333068e7eb64b5bbc..057bb2386420edd31154979338e71d00a51a7266 100644
|
||||
index 2f3f46d8d82dc90ebca8d64ace9d6bf1ee127937..163eb027b8cca1d43af24fff762e951e6f1030fc 100644
|
||||
--- a/browser/base/content/browser-init.js
|
||||
+++ b/browser/base/content/browser-init.js
|
||||
@@ -307,6 +307,7 @@ var gBrowserInit = {
|
||||
@@ -279,6 +279,7 @@ var gBrowserInit = {
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
@@ -10,11 +10,3 @@ index 0aa4baea8e5e6d742fc6cd3333068e7eb64b5bbc..057bb2386420edd31154979338e71d00
|
||||
gBrowser.addEventListener("DOMUpdateBlockedPopups", e =>
|
||||
PopupAndRedirectBlockerObserver.handleEvent(e)
|
||||
);
|
||||
@@ -501,6 +502,7 @@ var gBrowserInit = {
|
||||
|
||||
this._handleURIToLoad();
|
||||
|
||||
+ gZenWorkspaces.selectStartPage();
|
||||
Services.obs.addObserver(gIdentityHandler, "perm-changed");
|
||||
Services.obs.addObserver(gRemoteControl, "devtools-socket");
|
||||
Services.obs.addObserver(gRemoteControl, "marionette-listening");
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/browser/components/sessionstore/SessionStore.sys.mjs b/browser/components/sessionstore/SessionStore.sys.mjs
|
||||
index 52fc803050cd257d6de52b1e4a92f3211cbaba88..a9841832ddce540dc0f9b14c29e5eadac7510935 100644
|
||||
index 52fc803050cd257d6de52b1e4a92f3211cbaba88..ecfcef5dabb7056886972651cd35835564684a0e 100644
|
||||
--- a/browser/components/sessionstore/SessionStore.sys.mjs
|
||||
+++ b/browser/components/sessionstore/SessionStore.sys.mjs
|
||||
@@ -111,6 +111,11 @@ const TAB_EVENTS = [
|
||||
@@ -308,20 +308,37 @@ index 52fc803050cd257d6de52b1e4a92f3211cbaba88..a9841832ddce540dc0f9b14c29e5eada
|
||||
tabbrowser.removeTab(tabbrowser.tabs[i]);
|
||||
}
|
||||
}
|
||||
@@ -5529,6 +5580,12 @@ class _SessionStore {
|
||||
savedTabGroup => !openTabGroupIdsInWindow.has(savedTabGroup.id)
|
||||
);
|
||||
}
|
||||
@@ -5504,6 +5555,11 @@ class _SessionStore {
|
||||
this.#prefBranch.getBoolPref("sessionstore.restore_tabs_lazily") &&
|
||||
this.#restore_on_demand;
|
||||
|
||||
+ if (winData.isZenUnsynced) {
|
||||
+ aWindow.document.documentElement.setAttribute("zen-unsynced-window", "true");
|
||||
+ }
|
||||
+ aWindow.gZenFolders?.restoreDataFromSessionStore(winData.folders);
|
||||
+ aWindow.gZenWorkspaces?.restoreWorkspacesFromSessionStore(winData);
|
||||
+
|
||||
this.#log.debug(
|
||||
`restoreWindow, will restore ${winData.tabs.length} tabs and ${
|
||||
winData.groups?.length ?? 0
|
||||
@@ -5529,6 +5585,8 @@ class _SessionStore {
|
||||
savedTabGroup => !openTabGroupIdsInWindow.has(savedTabGroup.id)
|
||||
);
|
||||
}
|
||||
+ aWindow.gZenFolders?.restoreDataFromSessionStore(winData.folders);
|
||||
+ aWindow.gZenViewSplitter?.restoreDataFromSessionStore(winData.splitViewData);
|
||||
|
||||
// Move the originally open tabs to the end.
|
||||
if (initialTabs) {
|
||||
@@ -6098,6 +6155,25 @@ class _SessionStore {
|
||||
@@ -5630,6 +5688,8 @@ class _SessionStore {
|
||||
// set smoothScroll back to the original value
|
||||
arrowScrollbox.smoothScroll = smoothScroll;
|
||||
|
||||
+ aWindow.gZenWorkspaces?.onWindowRestored();
|
||||
+
|
||||
Glean.sessionRestore.restoreWindow.stopAndAccumulate(timerId);
|
||||
}
|
||||
|
||||
@@ -6098,6 +6158,25 @@ class _SessionStore {
|
||||
|
||||
// Most of tabData has been restored, now continue with restoring
|
||||
// attributes that may trigger external events.
|
||||
@@ -347,7 +364,7 @@ index 52fc803050cd257d6de52b1e4a92f3211cbaba88..a9841832ddce540dc0f9b14c29e5eada
|
||||
|
||||
if (tabData.pinned) {
|
||||
tabbrowser.pinTab(tab);
|
||||
@@ -6272,6 +6348,9 @@ class _SessionStore {
|
||||
@@ -6272,6 +6351,9 @@ class _SessionStore {
|
||||
aWindow.gURLBar.readOnly = false;
|
||||
}
|
||||
}
|
||||
@@ -357,7 +374,7 @@ index 52fc803050cd257d6de52b1e4a92f3211cbaba88..a9841832ddce540dc0f9b14c29e5eada
|
||||
|
||||
let promiseParts = Promise.withResolvers();
|
||||
const wasMinimized = aWindow.windowState == aWindow.STATE_MINIMIZED;
|
||||
@@ -6774,6 +6853,7 @@ class _SessionStore {
|
||||
@@ -6774,6 +6856,7 @@ class _SessionStore {
|
||||
*/
|
||||
#isCmdLineEmpty(aWindow, aState) {
|
||||
var pinnedOnly =
|
||||
@@ -365,7 +382,7 @@ index 52fc803050cd257d6de52b1e4a92f3211cbaba88..a9841832ddce540dc0f9b14c29e5eada
|
||||
aState.windows &&
|
||||
aState.windows.every(win => win.tabs.every(tab => tab.pinned));
|
||||
|
||||
@@ -7116,7 +7196,7 @@ class _SessionStore {
|
||||
@@ -7116,7 +7199,7 @@ class _SessionStore {
|
||||
|
||||
let groupsToSave = new Map();
|
||||
for (let tIndex = 0; tIndex < window.tabs.length; ) {
|
||||
@@ -374,7 +391,7 @@ index 52fc803050cd257d6de52b1e4a92f3211cbaba88..a9841832ddce540dc0f9b14c29e5eada
|
||||
// Adjust window.selected
|
||||
if (tIndex + 1 < window.selected) {
|
||||
window.selected -= 1;
|
||||
@@ -7131,7 +7211,7 @@ class _SessionStore {
|
||||
@@ -7131,7 +7214,7 @@ class _SessionStore {
|
||||
);
|
||||
// We don't want to increment tIndex here.
|
||||
continue;
|
||||
@@ -383,7 +400,7 @@ index 52fc803050cd257d6de52b1e4a92f3211cbaba88..a9841832ddce540dc0f9b14c29e5eada
|
||||
// Convert any open groups into saved groups.
|
||||
let groupStateToSave = window.groups.find(
|
||||
groupState => groupState.id == window.tabs[tIndex].groupId
|
||||
@@ -7691,7 +7771,6 @@ class _SessionStore {
|
||||
@@ -7691,7 +7774,6 @@ class _SessionStore {
|
||||
timer.initWithCallback(
|
||||
() => {
|
||||
if (beats <= 0) {
|
||||
@@ -391,7 +408,7 @@ index 52fc803050cd257d6de52b1e4a92f3211cbaba88..a9841832ddce540dc0f9b14c29e5eada
|
||||
Glean.sessionRestore.shutdownFlushAllOutcomes.timed_out.add(1);
|
||||
deferred.resolve();
|
||||
}
|
||||
@@ -8174,6 +8253,7 @@ class _SessionStore {
|
||||
@@ -8174,6 +8256,7 @@ class _SessionStore {
|
||||
if (
|
||||
!savedTabGroupState.tabs.length ||
|
||||
this.getSavedTabGroup(savedTabGroupState.id)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/browser/components/tabbrowser/Tabbrowser.sys.mjs b/browser/components/tabbrowser/Tabbrowser.sys.mjs
|
||||
index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089db94a6d3 100644
|
||||
index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..ddb15aeafab54a595b8f710b2234f21411d4e99a 100644
|
||||
--- a/browser/components/tabbrowser/Tabbrowser.sys.mjs
|
||||
+++ b/browser/components/tabbrowser/Tabbrowser.sys.mjs
|
||||
@@ -484,6 +484,7 @@ export class Tabbrowser {
|
||||
@@ -504,7 +504,24 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4943,8 +5121,9 @@ export class Tabbrowser {
|
||||
@@ -4889,6 +5067,16 @@ export class Tabbrowser {
|
||||
) {
|
||||
let tabs = [];
|
||||
let tabsFragment = this.document.createDocumentFragment();
|
||||
+ let zenWorkspaces = this.documentGlobal.gZenWorkspaces;
|
||||
+ let zenCollectsFragments =
|
||||
+ !!zenWorkspaces?.canCollectSessionRestoreFragments;
|
||||
+ if (zenCollectsFragments) {
|
||||
+ zenWorkspaces.beginSessionRestoreFragments();
|
||||
+ }
|
||||
+ let zenFragmentFor = tabData =>
|
||||
+ zenCollectsFragments
|
||||
+ ? zenWorkspaces.getSessionRestoreFragment(tabData)
|
||||
+ : tabsFragment;
|
||||
let tabToSelect = null;
|
||||
let hiddenTabs = new Map();
|
||||
/** @type {Map<TabGroupId, TabGroupWorkingData>} */
|
||||
@@ -4943,8 +5131,9 @@ export class Tabbrowser {
|
||||
}
|
||||
|
||||
// Add a new tab if needed.
|
||||
@@ -516,7 +533,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
|
||||
let url = "about:blank";
|
||||
if (tabData.entries?.length) {
|
||||
@@ -4977,8 +5156,10 @@ export class Tabbrowser {
|
||||
@@ -4977,8 +5166,10 @@ export class Tabbrowser {
|
||||
insertTab: false,
|
||||
skipLoad: true,
|
||||
preferredRemoteType,
|
||||
@@ -528,7 +545,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
if (select) {
|
||||
tabToSelect = tab;
|
||||
}
|
||||
@@ -5000,7 +5181,8 @@ export class Tabbrowser {
|
||||
@@ -5000,7 +5191,8 @@ export class Tabbrowser {
|
||||
this.pinTab(tab);
|
||||
// Then ensure all the tab open/pinning information is sent.
|
||||
this.#fireTabOpen(tab, {});
|
||||
@@ -538,7 +555,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
let { groupId } = tabData;
|
||||
const tabGroup = tabGroupWorkingData.get(groupId);
|
||||
// if a tab refers to a tab group we don't know, skip any group
|
||||
@@ -5020,7 +5202,10 @@ export class Tabbrowser {
|
||||
@@ -5020,9 +5212,19 @@ export class Tabbrowser {
|
||||
tabGroup.stateData.id,
|
||||
tabGroup.stateData.color,
|
||||
tabGroup.stateData.collapsed,
|
||||
@@ -548,9 +565,55 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
+ tabGroup.stateData.essential,
|
||||
+ tabGroup.stateData.splitView,
|
||||
);
|
||||
tabsFragment.appendChild(tabGroup.node);
|
||||
- tabsFragment.appendChild(tabGroup.node);
|
||||
+ // A group lives in the space of the tabs it holds, but its own
|
||||
+ // pinned and essential state is what decides its section.
|
||||
+ zenFragmentFor({
|
||||
+ zenWorkspace: tabData.zenWorkspace,
|
||||
+ userContextId: tabData.userContextId,
|
||||
+ pinned: tabGroup.stateData.pinned ?? tabData.pinned,
|
||||
+ zenEssential: tabGroup.stateData.essential ?? tabData.zenEssential,
|
||||
+ }).appendChild(tabGroup.node);
|
||||
}
|
||||
@@ -5080,9 +5265,18 @@ export class Tabbrowser {
|
||||
}
|
||||
} else {
|
||||
@@ -5032,14 +5234,14 @@ export class Tabbrowser {
|
||||
}
|
||||
|
||||
if (!splitView) {
|
||||
- tabsFragment.appendChild(tab);
|
||||
+ zenFragmentFor(tabData).appendChild(tab);
|
||||
} else if (splitView?.node) {
|
||||
// Treat split views as a unit for hiding purposes -- both tabs and
|
||||
// the split view wrapper itself should be hidden.
|
||||
if (splitView.tabs.some(t => t.hidden)) {
|
||||
splitView.node.toggleAttribute("hidden", true);
|
||||
}
|
||||
- tabsFragment.appendChild(splitView.node);
|
||||
+ zenFragmentFor(tabData).appendChild(splitView.node);
|
||||
}
|
||||
|
||||
if (tabWasReused) {
|
||||
@@ -5051,7 +5253,17 @@ export class Tabbrowser {
|
||||
}
|
||||
|
||||
// inject the top-level tab and tab group DOM nodes
|
||||
- this.tabContainer.appendChild(tabsFragment);
|
||||
+ if (zenCollectsFragments) {
|
||||
+ zenWorkspaces.flushSessionRestoreFragments();
|
||||
+ // allTabs only reaches the active space, so #updateTabsAfterInsert below
|
||||
+ // would leave every tab restored into another space without a position.
|
||||
+ // Session order is what SessionStore indexes these tabs by anyway.
|
||||
+ for (let index = 0; index < tabs.length; index++) {
|
||||
+ tabs[index]._index = index;
|
||||
+ }
|
||||
+ } else {
|
||||
+ this.tabContainer.appendChild(tabsFragment);
|
||||
+ }
|
||||
|
||||
// inject tab DOM nodes into the now-connected tab group and split view DOM nodes
|
||||
for (const tabGroup of tabGroupWorkingData.values()) {
|
||||
@@ -5080,9 +5292,18 @@ export class Tabbrowser {
|
||||
// to remove the old selected tab.
|
||||
if (tabToSelect) {
|
||||
let leftoverTab = this.selectedTab;
|
||||
@@ -569,7 +632,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
|
||||
if (tabs.length > 1 || !tabs[0].selected) {
|
||||
this.#updateTabsAfterInsert();
|
||||
@@ -5313,11 +5507,17 @@ export class Tabbrowser {
|
||||
@@ -5313,11 +5534,17 @@ export class Tabbrowser {
|
||||
if (ownerTab) {
|
||||
tab.owner = ownerTab;
|
||||
}
|
||||
@@ -588,7 +651,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
let insertRelatedAfterCurrent = Services.prefs.getBoolPref(
|
||||
"browser.tabs.insertRelatedAfterCurrent"
|
||||
);
|
||||
@@ -5332,7 +5532,7 @@ export class Tabbrowser {
|
||||
@@ -5332,7 +5559,7 @@ export class Tabbrowser {
|
||||
(insertRelatedAfterCurrent && lastRelatedTab) ||
|
||||
openerTab ||
|
||||
this.selectedTab;
|
||||
@@ -597,7 +660,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
tabGroup = previousTab.group;
|
||||
}
|
||||
if (
|
||||
@@ -5348,7 +5548,7 @@ export class Tabbrowser {
|
||||
@@ -5348,7 +5575,7 @@ export class Tabbrowser {
|
||||
previousTab.splitview
|
||||
) + 1;
|
||||
} else if (previousTab.visible) {
|
||||
@@ -606,7 +669,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
} else if (previousTab == this.documentGlobal.FirefoxViewHandler.tab) {
|
||||
elementIndex = 0;
|
||||
}
|
||||
@@ -5376,14 +5576,14 @@ export class Tabbrowser {
|
||||
@@ -5376,14 +5603,14 @@ export class Tabbrowser {
|
||||
}
|
||||
// Ensure index is within bounds.
|
||||
if (tab.pinned) {
|
||||
@@ -625,7 +688,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
|
||||
if (pinned && !itemAfter?.pinned) {
|
||||
itemAfter = null;
|
||||
@@ -5400,7 +5600,7 @@ export class Tabbrowser {
|
||||
@@ -5400,7 +5627,7 @@ export class Tabbrowser {
|
||||
|
||||
this.tabContainer._invalidateCachedTabs();
|
||||
|
||||
@@ -634,7 +697,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
if (
|
||||
(this.isTab(itemAfter) && itemAfter.group == tabGroup) ||
|
||||
this.isSplitViewWrapper(itemAfter)
|
||||
@@ -5431,7 +5631,11 @@ export class Tabbrowser {
|
||||
@@ -5431,7 +5658,11 @@ export class Tabbrowser {
|
||||
const tabContainer = pinned
|
||||
? this.tabContainer.pinnedTabsContainer
|
||||
: this.tabContainer;
|
||||
@@ -646,7 +709,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
}
|
||||
|
||||
if (tab.group?.collapsed) {
|
||||
@@ -5446,6 +5650,7 @@ export class Tabbrowser {
|
||||
@@ -5446,6 +5677,7 @@ export class Tabbrowser {
|
||||
if (pinned) {
|
||||
this.#updateTabBarForPinnedTabs();
|
||||
}
|
||||
@@ -654,7 +717,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
|
||||
this.documentGlobal.TabBarVisibility.update();
|
||||
}
|
||||
@@ -6025,6 +6230,7 @@ export class Tabbrowser {
|
||||
@@ -6025,6 +6257,7 @@ export class Tabbrowser {
|
||||
metricsContext,
|
||||
} = {}
|
||||
) {
|
||||
@@ -662,7 +725,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
// When 'closeWindowWithLastTab' pref is enabled, closing all tabs
|
||||
// can be considered equivalent to closing the window.
|
||||
if (
|
||||
@@ -6135,6 +6341,7 @@ export class Tabbrowser {
|
||||
@@ -6135,6 +6368,7 @@ export class Tabbrowser {
|
||||
closedTabCount -= 1;
|
||||
}
|
||||
}
|
||||
@@ -670,7 +733,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
|
||||
if (closedTabCount > 0) {
|
||||
this.recordTabMetrics(
|
||||
@@ -6236,6 +6443,14 @@ export class Tabbrowser {
|
||||
@@ -6236,6 +6470,14 @@ export class Tabbrowser {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -685,7 +748,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
let isVisibleTab = aTab.visible;
|
||||
// We have to sample the tab width now, since #beginRemoveTab might
|
||||
// end up modifying the DOM in such a way that aTab gets a new
|
||||
@@ -6244,6 +6459,9 @@ export class Tabbrowser {
|
||||
@@ -6244,6 +6486,9 @@ export class Tabbrowser {
|
||||
let tabWidth =
|
||||
this.documentGlobal.windowUtils.getBoundsWithoutFlushing(aTab).width;
|
||||
let isLastTab = this.#isLastTabInWindow(aTab);
|
||||
@@ -695,7 +758,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
if (
|
||||
!this.#beginRemoveTab(aTab, {
|
||||
closeWindowFastpath: true,
|
||||
@@ -6254,13 +6472,14 @@ export class Tabbrowser {
|
||||
@@ -6254,13 +6499,14 @@ export class Tabbrowser {
|
||||
metricsContext,
|
||||
})
|
||||
) {
|
||||
@@ -711,7 +774,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
let lockTabSizing =
|
||||
!this.tabContainer.verticalMode &&
|
||||
!aTab.pinned &&
|
||||
@@ -6291,7 +6510,13 @@ export class Tabbrowser {
|
||||
@@ -6291,7 +6537,13 @@ export class Tabbrowser {
|
||||
// We're not animating, so we can cancel the animation stopwatch.
|
||||
Glean.browserTabclose.timeAnim.cancel(aTab._closeTimeAnimTimerId);
|
||||
aTab._closeTimeAnimTimerId = null;
|
||||
@@ -726,7 +789,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -6331,7 +6556,9 @@ export class Tabbrowser {
|
||||
@@ -6331,7 +6583,9 @@ export class Tabbrowser {
|
||||
get #shouldCloseWindowWithLastTab() {
|
||||
return (
|
||||
!this.documentGlobal.toolbar.visible ||
|
||||
@@ -737,7 +800,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6347,7 +6574,7 @@ export class Tabbrowser {
|
||||
@@ -6347,7 +6601,7 @@ export class Tabbrowser {
|
||||
*/
|
||||
#isLastTabInWindow(tab) {
|
||||
for (const otherTab of this.tabs) {
|
||||
@@ -746,7 +809,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -6488,6 +6715,7 @@ export class Tabbrowser {
|
||||
@@ -6488,6 +6742,7 @@ export class Tabbrowser {
|
||||
|
||||
newTab = true;
|
||||
}
|
||||
@@ -754,7 +817,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
aTab._endRemoveArgs = [closeWindow, newTab];
|
||||
|
||||
// swapBrowsersAndCloseOther will take care of closing the window without animation.
|
||||
@@ -6542,13 +6770,7 @@ export class Tabbrowser {
|
||||
@@ -6542,13 +6797,7 @@ export class Tabbrowser {
|
||||
}
|
||||
|
||||
if (newTab) {
|
||||
@@ -769,7 +832,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
} else {
|
||||
this.documentGlobal.TabBarVisibility.update();
|
||||
}
|
||||
@@ -6701,6 +6923,7 @@ export class Tabbrowser {
|
||||
@@ -6701,6 +6950,7 @@ export class Tabbrowser {
|
||||
this.tabs[i]._index = i;
|
||||
}
|
||||
|
||||
@@ -777,7 +840,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
if (!this.#windowIsClosing) {
|
||||
// update tab close buttons state
|
||||
this.tabContainer._updateCloseButtons();
|
||||
@@ -6891,6 +7114,7 @@ export class Tabbrowser {
|
||||
@@ -6891,6 +7141,7 @@ export class Tabbrowser {
|
||||
memory_after: await getTotalMemoryUsage(),
|
||||
time_to_unload_in_ms: timeElapsed,
|
||||
});
|
||||
@@ -785,7 +848,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -6937,11 +7161,12 @@ export class Tabbrowser {
|
||||
@@ -6937,11 +7188,12 @@ export class Tabbrowser {
|
||||
}
|
||||
|
||||
let excludeTabs = new Set(aExcludeTabs);
|
||||
@@ -799,7 +862,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
}
|
||||
|
||||
if (
|
||||
@@ -6949,13 +7174,13 @@ export class Tabbrowser {
|
||||
@@ -6949,13 +7201,13 @@ export class Tabbrowser {
|
||||
!excludeTabs.has(aTab.owner) &&
|
||||
Services.prefs.getBoolPref("browser.tabs.selectOwnerOnClose")
|
||||
) {
|
||||
@@ -815,7 +878,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
);
|
||||
|
||||
if (Services.prefs.getBoolPref("browser.tabs.selectMRUOnClose", false)) {
|
||||
@@ -6970,6 +7195,13 @@ export class Tabbrowser {
|
||||
@@ -6970,6 +7222,13 @@ export class Tabbrowser {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -829,7 +892,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
let tab = this.tabContainer.findNextTab(aTab, {
|
||||
direction: 1,
|
||||
filter: _tab => remainingTabs.includes(_tab),
|
||||
@@ -6983,7 +7215,7 @@ export class Tabbrowser {
|
||||
@@ -6983,7 +7242,7 @@ export class Tabbrowser {
|
||||
}
|
||||
|
||||
if (tab) {
|
||||
@@ -838,7 +901,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
}
|
||||
|
||||
// If no qualifying visible tab was found, see if there is a tab in
|
||||
@@ -7004,7 +7236,7 @@ export class Tabbrowser {
|
||||
@@ -7004,7 +7263,7 @@ export class Tabbrowser {
|
||||
});
|
||||
}
|
||||
|
||||
@@ -847,7 +910,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
}
|
||||
|
||||
#blurTab(aTab) {
|
||||
@@ -7021,7 +7253,7 @@ export class Tabbrowser {
|
||||
@@ -7021,7 +7280,7 @@ export class Tabbrowser {
|
||||
* @returns {boolean}
|
||||
* False if swapping isn't permitted, true otherwise.
|
||||
*/
|
||||
@@ -856,7 +919,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
// Do not allow transfering a private tab to a non-private window
|
||||
// and vice versa.
|
||||
if (
|
||||
@@ -7080,6 +7312,7 @@ export class Tabbrowser {
|
||||
@@ -7080,6 +7339,7 @@ export class Tabbrowser {
|
||||
// fire the beforeunload event in the process. Close the other
|
||||
// window if this was its last tab.
|
||||
if (
|
||||
@@ -864,7 +927,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
!remoteBrowser.#beginRemoveTab(aOtherTab, {
|
||||
adoptedByTab: aOurTab,
|
||||
closeWindowWithLastTab: true,
|
||||
@@ -7091,7 +7324,7 @@ export class Tabbrowser {
|
||||
@@ -7091,7 +7351,7 @@ export class Tabbrowser {
|
||||
// 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.
|
||||
@@ -873,7 +936,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
if (closeWindow) {
|
||||
let win = aOtherTab.documentGlobal;
|
||||
win.windowUtils.suppressAnimation(true);
|
||||
@@ -7231,11 +7464,13 @@ export class Tabbrowser {
|
||||
@@ -7231,11 +7491,13 @@ export class Tabbrowser {
|
||||
}
|
||||
|
||||
// Finish tearing down the tab that's going away.
|
||||
@@ -887,7 +950,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
|
||||
this.setTabTitle(aOurTab);
|
||||
|
||||
@@ -7484,10 +7719,10 @@ export class Tabbrowser {
|
||||
@@ -7484,10 +7746,10 @@ export class Tabbrowser {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -900,7 +963,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
aTab.selected ||
|
||||
aTab.closing ||
|
||||
// Tabs that are sharing the screen, microphone or camera cannot be hidden.
|
||||
@@ -7576,7 +7811,8 @@ export class Tabbrowser {
|
||||
@@ -7576,7 +7838,8 @@ export class Tabbrowser {
|
||||
* @param {object} [aOptions={}]
|
||||
* Key-value pairs that will be serialized into the features string.
|
||||
*/
|
||||
@@ -910,7 +973,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
if (this.tabs.length == 1) {
|
||||
return null;
|
||||
}
|
||||
@@ -7593,7 +7829,7 @@ export class Tabbrowser {
|
||||
@@ -7593,7 +7856,7 @@ export class Tabbrowser {
|
||||
// tell a new window to take the "dropped" tab
|
||||
let args = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
|
||||
args.appendElement(aTab.splitview ?? aTab);
|
||||
@@ -919,7 +982,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
private: lazy.PrivateBrowsingUtils.isWindowPrivate(this.documentGlobal),
|
||||
features: Object.entries(aOptions)
|
||||
.map(([key, value]) => `${key}=${value}`)
|
||||
@@ -7601,6 +7837,8 @@ export class Tabbrowser {
|
||||
@@ -7601,6 +7864,8 @@ export class Tabbrowser {
|
||||
openerWindow: this.documentGlobal,
|
||||
args,
|
||||
});
|
||||
@@ -928,7 +991,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -7734,7 +7972,7 @@ export class Tabbrowser {
|
||||
@@ -7734,7 +7999,7 @@ export class Tabbrowser {
|
||||
* @returns {element is MozTabbrowserTabGroup}
|
||||
*/
|
||||
isTabGroup(element) {
|
||||
@@ -937,7 +1000,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -7807,8 +8045,8 @@ export class Tabbrowser {
|
||||
@@ -7807,8 +8072,8 @@ export class Tabbrowser {
|
||||
}
|
||||
|
||||
// Don't allow mixing pinned and unpinned tabs.
|
||||
@@ -948,7 +1011,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
} else {
|
||||
tabIndex = Math.max(tabIndex, this.pinnedTabCount);
|
||||
}
|
||||
@@ -7854,8 +8092,8 @@ export class Tabbrowser {
|
||||
@@ -7854,8 +8119,8 @@ export class Tabbrowser {
|
||||
this.#handleTabMove(
|
||||
element,
|
||||
() => {
|
||||
@@ -959,7 +1022,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
neighbor = neighbor.group;
|
||||
}
|
||||
if (neighbor?.splitview) {
|
||||
@@ -7866,6 +8104,12 @@ export class Tabbrowser {
|
||||
@@ -7866,6 +8131,12 @@ export class Tabbrowser {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -972,7 +1035,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
|
||||
if (movingForwards && neighbor) {
|
||||
neighbor.after(element);
|
||||
@@ -7939,23 +8183,31 @@ export class Tabbrowser {
|
||||
@@ -7939,23 +8210,31 @@ export class Tabbrowser {
|
||||
) {
|
||||
if (this.isTabGroupLabel(targetElement)) {
|
||||
targetElement = targetElement.group;
|
||||
@@ -1010,7 +1073,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
} 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
|
||||
@@ -7968,12 +8220,35 @@ export class Tabbrowser {
|
||||
@@ -7968,12 +8247,35 @@ export class Tabbrowser {
|
||||
// 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.
|
||||
@@ -1047,7 +1110,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
|
||||
// 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.
|
||||
@@ -7982,6 +8257,7 @@ export class Tabbrowser {
|
||||
@@ -7982,6 +8284,7 @@ export class Tabbrowser {
|
||||
}
|
||||
|
||||
let getContainer = () =>
|
||||
@@ -1055,7 +1118,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
element.pinned
|
||||
? this.tabContainer.pinnedTabsContainer
|
||||
: this.tabContainer;
|
||||
@@ -7990,11 +8266,15 @@ export class Tabbrowser {
|
||||
@@ -7990,11 +8293,15 @@ export class Tabbrowser {
|
||||
element,
|
||||
() => {
|
||||
if (moveBefore) {
|
||||
@@ -1072,7 +1135,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
}
|
||||
},
|
||||
{ metricsContext }
|
||||
@@ -8070,11 +8350,15 @@ export class Tabbrowser {
|
||||
@@ -8070,11 +8377,15 @@ export class Tabbrowser {
|
||||
* The context for the operation for telemetry purposes.
|
||||
*/
|
||||
moveTabToExistingGroup(aTab, aGroup, { metricsContext } = {}) {
|
||||
@@ -1091,7 +1154,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
}
|
||||
if (aTab.group && aTab.group.id === aGroup.id) {
|
||||
return;
|
||||
@@ -8153,6 +8437,7 @@ export class Tabbrowser {
|
||||
@@ -8153,6 +8464,7 @@ export class Tabbrowser {
|
||||
|
||||
let state = {
|
||||
tabIndex: tab.index,
|
||||
@@ -1099,7 +1162,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
};
|
||||
if (tab.visible) {
|
||||
state.elementIndex = tab.elementIndex;
|
||||
@@ -8190,7 +8475,7 @@ export class Tabbrowser {
|
||||
@@ -8190,7 +8502,7 @@ export class Tabbrowser {
|
||||
let changedSplitView =
|
||||
previousTabState.splitViewId != currentTabState.splitViewId;
|
||||
|
||||
@@ -1108,7 +1171,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
tab.dispatchEvent(
|
||||
new this.documentGlobal.CustomEvent("TabMove", {
|
||||
bubbles: true,
|
||||
@@ -8248,6 +8533,10 @@ export class Tabbrowser {
|
||||
@@ -8248,6 +8560,10 @@ export class Tabbrowser {
|
||||
|
||||
moveActionCallback();
|
||||
|
||||
@@ -1119,7 +1182,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
// Clear tabs cache after moving nodes because the order of tabs may have
|
||||
// changed.
|
||||
this.tabContainer._invalidateCachedTabs();
|
||||
@@ -8307,7 +8596,22 @@ export class Tabbrowser {
|
||||
@@ -8307,7 +8623,22 @@ export class Tabbrowser {
|
||||
* @returns {object}
|
||||
* The new tab in the current window, null if the tab couldn't be adopted.
|
||||
*/
|
||||
@@ -1143,7 +1206,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
// 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
|
||||
@@ -8350,6 +8654,8 @@ export class Tabbrowser {
|
||||
@@ -8350,6 +8681,8 @@ export class Tabbrowser {
|
||||
}
|
||||
params.skipLoad = true;
|
||||
let newTab = this.addWebTab("about:blank", params);
|
||||
@@ -1152,7 +1215,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
|
||||
aTab.container.tabDragAndDrop.finishAnimateTabMove();
|
||||
|
||||
@@ -9189,7 +9495,7 @@ export class Tabbrowser {
|
||||
@@ -9189,7 +9522,7 @@ export class Tabbrowser {
|
||||
// preventDefault(). It will still raise the window if appropriate.
|
||||
return;
|
||||
}
|
||||
@@ -1161,7 +1224,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
this.documentGlobal.focus();
|
||||
aEvent.preventDefault();
|
||||
}
|
||||
@@ -9206,7 +9512,6 @@ export class Tabbrowser {
|
||||
@@ -9206,7 +9539,6 @@ export class Tabbrowser {
|
||||
|
||||
on_TabGroupCollapse(aEvent) {
|
||||
aEvent.target.tabs.forEach(tab => {
|
||||
@@ -1169,7 +1232,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
});
|
||||
}
|
||||
|
||||
@@ -9554,7 +9859,9 @@ export class Tabbrowser {
|
||||
@@ -9554,7 +9886,9 @@ export class Tabbrowser {
|
||||
|
||||
let filter = this.#tabFilters.get(tab);
|
||||
if (filter) {
|
||||
@@ -1179,7 +1242,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
|
||||
let listener = this.#tabListeners.get(tab);
|
||||
if (listener) {
|
||||
@@ -10354,6 +10661,7 @@ class TabProgressListener {
|
||||
@@ -10354,6 +10688,7 @@ class TabProgressListener {
|
||||
aWebProgress.isTopLevel
|
||||
) {
|
||||
this._tab.setAttribute("busy", "true");
|
||||
@@ -1187,7 +1250,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
this.#tabbrowser._tabAttrModified(this._tab, ["busy"]);
|
||||
this._tab._notselectedsinceload = !this._tab.selected;
|
||||
}
|
||||
@@ -10434,6 +10742,7 @@ class TabProgressListener {
|
||||
@@ -10434,6 +10769,7 @@ class TabProgressListener {
|
||||
// known defaults. Note we use the original URL since about:newtab
|
||||
// redirects to a prerendered page.
|
||||
const shouldRemoveFavicon =
|
||||
@@ -1195,7 +1258,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
|
||||
!this._browser.mIconURL &&
|
||||
!ignoreBlank &&
|
||||
!(originalLocation.spec in FAVICON_DEFAULTS);
|
||||
@@ -10610,13 +10919,6 @@ class TabProgressListener {
|
||||
@@ -10610,13 +10946,6 @@ class TabProgressListener {
|
||||
this._browser.originalURI = aRequest.originalURI;
|
||||
}
|
||||
|
||||
|
||||
@@ -73,6 +73,10 @@ class nsZenEmojiPicker extends nsZenDOMOperatedFeature {
|
||||
this.#changePage(false);
|
||||
} else if (event.target.id === "PanelUI-zen-emojis-picker-change-svg") {
|
||||
this.#changePage(true);
|
||||
} else if (event.target.classList.contains("zen-emojis-picker-emoji")) {
|
||||
this.#selectEmoji(event.target.getAttribute("label"));
|
||||
} else if (event.target.classList.contains("zen-emojis-picker-svg")) {
|
||||
this.#selectEmoji(this.getSVGURL(event.target.getAttribute("icon")));
|
||||
}
|
||||
break;
|
||||
case "input":
|
||||
@@ -176,19 +180,17 @@ class nsZenEmojiPicker extends nsZenDOMOperatedFeature {
|
||||
this.searchInput.value = "";
|
||||
const allowEmojis = !this.#panel.hasAttribute("only-svg-icons");
|
||||
if (allowEmojis) {
|
||||
const emojiList = this.emojiList;
|
||||
const emojis = document.createDocumentFragment();
|
||||
for (const emoji of this.#emojis) {
|
||||
const item = document.createXULElement("toolbarbutton");
|
||||
item.className = "toolbarbutton-1 zen-emojis-picker-emoji";
|
||||
item.setAttribute("label", emoji.emoji);
|
||||
item.setAttribute("tooltiptext", "");
|
||||
item.addEventListener("command", () => {
|
||||
this.#selectEmoji(emoji.emoji);
|
||||
});
|
||||
emojiList.appendChild(item);
|
||||
emojis.appendChild(item);
|
||||
}
|
||||
this.emojiList.replaceChildren(emojis);
|
||||
}
|
||||
const svgList = this.svgList;
|
||||
const icons = document.createDocumentFragment();
|
||||
for (const icon of SVG_ICONS) {
|
||||
const item = document.createXULElement("toolbarbutton");
|
||||
item.className = "toolbarbutton-1 zen-emojis-picker-svg";
|
||||
@@ -196,11 +198,9 @@ class nsZenEmojiPicker extends nsZenDOMOperatedFeature {
|
||||
item.setAttribute("tooltiptext", "");
|
||||
item.style.listStyleImage = `url(${this.getSVGURL(icon)})`;
|
||||
item.setAttribute("icon", icon);
|
||||
item.addEventListener("command", () => {
|
||||
this.#selectEmoji(this.getSVGURL(icon));
|
||||
});
|
||||
svgList.appendChild(item);
|
||||
icons.appendChild(item);
|
||||
}
|
||||
this.svgList.replaceChildren(icons);
|
||||
}
|
||||
|
||||
#onPopupShown(event) {
|
||||
@@ -220,10 +220,8 @@ class nsZenEmojiPicker extends nsZenDOMOperatedFeature {
|
||||
}
|
||||
this.#clearEmojis();
|
||||
|
||||
const emojiList = this.emojiList;
|
||||
emojiList.innerHTML = "";
|
||||
|
||||
this.svgList.innerHTML = "";
|
||||
this.emojiList.replaceChildren();
|
||||
this.svgList.replaceChildren();
|
||||
|
||||
if (!this.#hasSelection) {
|
||||
this.#currentPromiseReject?.(
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
overflow: clip;
|
||||
|
||||
:root:not([zen-no-padding="true"]) &:not(.zen-glance-overlay) {
|
||||
box-shadow: var(--zen-big-shadow);
|
||||
/* stylelint-disable-next-line property-no-unknown */
|
||||
corner-shape: round;
|
||||
border-radius: calc(var(--zen-native-inner-radius) / var(--zen-squircle-value));
|
||||
|
||||
@@ -657,6 +657,7 @@
|
||||
--urlbarview-row-padding-block: 12px;
|
||||
--urlbarView-row-gutter: 0px;
|
||||
|
||||
border-block: 2px solid transparent;
|
||||
color: light-dark(rgba(0, 0, 0, 0.7), rgba(255, 255, 255, 0.7)) !important;
|
||||
|
||||
&:hover,
|
||||
@@ -698,7 +699,7 @@
|
||||
}
|
||||
|
||||
#urlbar-results {
|
||||
max-height: 244px;
|
||||
max-height: 259px;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: none;
|
||||
margin-block-start: calc(var(--urlbarView-results-padding) - 2px);
|
||||
|
||||
@@ -1597,6 +1597,54 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the animation running on an element and pins the value it had
|
||||
* reached as an inline style.
|
||||
*
|
||||
* @param {Element} item - The element to settle.
|
||||
* @param {string} prop - The camel cased property to pin.
|
||||
* @returns {string|undefined} The value the property was left at, or
|
||||
* undefined if nothing was animating it.
|
||||
*/
|
||||
#settleAnimation(item, prop) {
|
||||
const animation = this.#itemAnimations.get(item);
|
||||
if (
|
||||
animation?.playState !== "running" &&
|
||||
animation?.playState !== "paused"
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
const value = window.getComputedStyle(item)[prop];
|
||||
animation.cancel();
|
||||
item.style[prop] = value;
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Measures how far up the group container has to be shifted to be fully
|
||||
* collapsed, ignoring any collapse offset already applied to it.
|
||||
*
|
||||
* @param {Element} tabsContainer - The container to measure.
|
||||
* @param {Element} groupStart - The element carrying the collapse offset.
|
||||
* @returns {number} The height to shift the container by.
|
||||
*/
|
||||
#measureCollapsedShift(tabsContainer, groupStart) {
|
||||
const currentMargin = groupStart.style.marginTop;
|
||||
if (!parseFloat(currentMargin)) {
|
||||
// The container is not shifted, it already measures its full height.
|
||||
return this.#calculateHeightShift(tabsContainer, []);
|
||||
}
|
||||
groupStart.style.marginTop = "0px";
|
||||
// Note that these have to flush, the margin has just been changed.
|
||||
let heightShift = tabsContainer.getBoundingClientRect().height;
|
||||
if (tabsContainer.separatorElement) {
|
||||
heightShift -=
|
||||
tabsContainer.separatorElement.getBoundingClientRect().height;
|
||||
}
|
||||
groupStart.style.marginTop = currentMargin;
|
||||
return heightShift;
|
||||
}
|
||||
|
||||
#calculateHeightShift(tabsContainer, selectedTabs) {
|
||||
let heightShift = 0;
|
||||
if (selectedTabs.length) {
|
||||
@@ -1641,10 +1689,10 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
if (selectedTabs.length) {
|
||||
tabsContainer.removeAttribute("hidden");
|
||||
}
|
||||
const collapsedHeight = this.#calculateHeightShift(
|
||||
tabsContainer,
|
||||
selectedTabs
|
||||
);
|
||||
const startMargin = this.#settleAnimation(groupStart, "marginTop");
|
||||
const collapsedHeight = selectedTabs.length
|
||||
? 0
|
||||
: this.#measureCollapsedShift(tabsContainer, groupStart);
|
||||
|
||||
if (selectedTabs.length) {
|
||||
for (let i = 0; i < groupItems.length; i++) {
|
||||
@@ -1697,10 +1745,10 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
...this.#createAnimation(
|
||||
groupStart,
|
||||
{
|
||||
marginTop: -(
|
||||
collapsedHeight +
|
||||
4 * (selectedTabs.length === 0 ? 1 : 0)
|
||||
),
|
||||
marginTop: [
|
||||
startMargin,
|
||||
-(collapsedHeight + 4 * (selectedTabs.length === 0 ? 1 : 0)),
|
||||
],
|
||||
},
|
||||
{ duration, ease: "easeInOut" }
|
||||
)
|
||||
@@ -1709,16 +1757,15 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
gBrowser.tabContainer._invalidateCachedVisibleTabs();
|
||||
this.#animationCount += 1;
|
||||
await Promise.all(animations);
|
||||
if (this.#animationCount) {
|
||||
this.#animationCount -= 1;
|
||||
this.#animationCount -= 1;
|
||||
if (this.#animationCount || !group.collapsed) {
|
||||
return;
|
||||
}
|
||||
// Prevent hiding if we spam the group animations
|
||||
if (!selectedTabs.length && !this.#animationCount) {
|
||||
if (!selectedTabs.length) {
|
||||
tabsContainer.setAttribute("hidden", true);
|
||||
this.styleCleanup(itemsToHide);
|
||||
this.#queueCollapsedRelayout(group);
|
||||
}
|
||||
|
||||
this.styleCleanup(itemsToHide);
|
||||
}
|
||||
|
||||
async animateExpand(group) {
|
||||
|
||||
@@ -1006,6 +1006,9 @@ export class nsZenSessionManager {
|
||||
aWindow.gZenWorkspaces.restoreWorkspacesFromSessionStore({
|
||||
spaces: this.#sidebarWithoutCloning.spaces || [],
|
||||
});
|
||||
// There is no restore coming for this window, so nothing else is going to
|
||||
// tell the spaces their tabs are in place.
|
||||
aWindow.gZenWorkspaces.onWindowRestored();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -133,11 +133,11 @@ class nsZenWorkspaceIcons extends MozXULElement {
|
||||
|
||||
async #updateIcons() {
|
||||
const workspaces = gZenWorkspaces.getWorkspaces();
|
||||
this.innerHTML = "";
|
||||
const icons = document.createDocumentFragment();
|
||||
for (const workspace of workspaces) {
|
||||
const button = this.#createWorkspaceIcon(workspace);
|
||||
this.appendChild(button);
|
||||
icons.appendChild(this.#createWorkspaceIcon(workspace));
|
||||
}
|
||||
this.replaceChildren(icons);
|
||||
if (workspaces.length <= 1) {
|
||||
this.setAttribute("dont-show", "true");
|
||||
} else {
|
||||
@@ -169,7 +169,7 @@ class nsZenWorkspaceIcons extends MozXULElement {
|
||||
for (const button of buttons) {
|
||||
if (button.getAttribute("zen-workspace-id") == uuid) {
|
||||
selected = i;
|
||||
} else {
|
||||
} else if (button.hasAttribute("active")) {
|
||||
button.removeAttribute("active");
|
||||
}
|
||||
i++;
|
||||
|
||||
@@ -76,16 +76,6 @@ class nsZenWorkspaces {
|
||||
this._resolveInitialized = resolve;
|
||||
});
|
||||
|
||||
async #waitForPromises() {
|
||||
if (this.privateWindowOrDisabled) {
|
||||
return;
|
||||
}
|
||||
await Promise.all([
|
||||
this.promisePinnedInitialized,
|
||||
SessionStore.promiseAllWindowsRestored,
|
||||
]);
|
||||
}
|
||||
|
||||
async init() {
|
||||
// Initialize workspace change mutex
|
||||
this._workspaceChangeInProgress = false;
|
||||
@@ -152,8 +142,9 @@ class nsZenWorkspaces {
|
||||
this.addPopupListeners();
|
||||
|
||||
if (this.privateWindowOrDisabled) {
|
||||
await this.#waitForPromises();
|
||||
await this.restoreWorkspacesFromSessionStore({});
|
||||
await delayedStartupPromise;
|
||||
this.restoreWorkspacesFromSessionStore({});
|
||||
await this.onWindowRestored();
|
||||
}
|
||||
|
||||
if (!this.privateWindowOrDisabled) {
|
||||
@@ -300,6 +291,9 @@ class nsZenWorkspaces {
|
||||
inBackground: true,
|
||||
userContextId: 0,
|
||||
_forZenEmptyTab: true,
|
||||
skipAnimation: true,
|
||||
skipBackgroundNotify: true,
|
||||
bulkOrderedOpen: true,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -371,39 +365,27 @@ class nsZenWorkspaces {
|
||||
"tabbrowser-arrowscrollbox-periphery"
|
||||
);
|
||||
perifery.setAttribute("hidden", "true");
|
||||
const tabs = gBrowser.tabContainer.allTabs;
|
||||
const markupTabs = gBrowser.tabContainer.allTabs;
|
||||
const workspaces = this.getWorkspaces();
|
||||
const spaceElements = document.createDocumentFragment();
|
||||
for (const workspace of workspaces) {
|
||||
this.#createWorkspaceTabsSection(workspace, tabs);
|
||||
spaceElements.appendChild(this.#createWorkspaceElement(workspace));
|
||||
}
|
||||
if (tabs.length) {
|
||||
const defaultSelectedContainer = this.workspaceElement(
|
||||
this.activeWorkspace
|
||||
)?.querySelector(".zen-workspace-normal-tabs-section");
|
||||
const pinnedContainer = this.workspaceElement(
|
||||
this.activeWorkspace
|
||||
)?.querySelector(".zen-workspace-pinned-tabs-section");
|
||||
// New profile with no workspaces does not have a default selected container
|
||||
if (defaultSelectedContainer) {
|
||||
for (const tab of tabs) {
|
||||
if (tab.hasAttribute("zen-essential")) {
|
||||
this.getEssentialsSection(tab).appendChild(tab);
|
||||
continue;
|
||||
} else if (tab.pinned) {
|
||||
pinnedContainer.insertBefore(tab, pinnedContainer.lastChild);
|
||||
continue;
|
||||
}
|
||||
// before to the last child (perifery)
|
||||
defaultSelectedContainer.insertBefore(
|
||||
tab,
|
||||
defaultSelectedContainer.lastChild
|
||||
);
|
||||
}
|
||||
}
|
||||
document
|
||||
.getElementById("tabbrowser-arrowscrollbox")
|
||||
.appendChild(spaceElements);
|
||||
this._hasInitializedTabsStrip = true;
|
||||
for (const tab of markupTabs) {
|
||||
// Routes into the active space's strip now that one exists.
|
||||
gBrowser.tabContainer.appendChild(tab);
|
||||
}
|
||||
if (markupTabs.length) {
|
||||
gBrowser.tabContainer._invalidateCachedTabs();
|
||||
}
|
||||
for (const workspace of workspaces) {
|
||||
this.workspaceElement(workspace.uuid)?.checkPinsExistence();
|
||||
}
|
||||
perifery.setAttribute("hidden", "true");
|
||||
this._hasInitializedTabsStrip = true;
|
||||
this._fixIndicatorsNames(workspaces);
|
||||
}
|
||||
|
||||
@@ -455,15 +437,20 @@ class nsZenWorkspaces {
|
||||
return this.getEssentialsSection(this.getCurrentSpaceContainerId());
|
||||
}
|
||||
|
||||
#createWorkspaceTabsSection(workspace, tabs = []) {
|
||||
#createWorkspaceElement(workspace) {
|
||||
const workspaceWrapper = document.createXULElement("zen-workspace");
|
||||
const container = document.getElementById("tabbrowser-arrowscrollbox");
|
||||
workspaceWrapper.id = workspace.uuid;
|
||||
if (this.activeWorkspace === workspace.uuid) {
|
||||
workspaceWrapper.active = true;
|
||||
}
|
||||
return workspaceWrapper;
|
||||
}
|
||||
|
||||
container.appendChild(workspaceWrapper);
|
||||
#createWorkspaceTabsSection(workspace, tabs = []) {
|
||||
const workspaceWrapper = this.#createWorkspaceElement(workspace);
|
||||
document
|
||||
.getElementById("tabbrowser-arrowscrollbox")
|
||||
.appendChild(workspaceWrapper);
|
||||
this.#organizeTabsToWorkspaceSections(workspace, workspaceWrapper, tabs);
|
||||
workspaceWrapper.checkPinsExistence();
|
||||
}
|
||||
@@ -512,6 +499,85 @@ class nsZenWorkspaces {
|
||||
}
|
||||
}
|
||||
|
||||
/** @type {Map<Element, DocumentFragment>|null} */
|
||||
#sessionRestoreFragments = null;
|
||||
|
||||
get canCollectSessionRestoreFragments() {
|
||||
return (
|
||||
this._hasInitializedTabsStrip &&
|
||||
!!this.activeWorkspaceElement?.tabsContainer
|
||||
);
|
||||
}
|
||||
|
||||
beginSessionRestoreFragments() {
|
||||
this.#sessionRestoreFragments = new Map();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} aTabData
|
||||
* Session store data of the tab, or of the first tab of the folder,
|
||||
* that is about to be inserted.
|
||||
* @returns {DocumentFragment} The fragment for the section it belongs to.
|
||||
*/
|
||||
getSessionRestoreFragment(aTabData) {
|
||||
const container = this.#sessionRestoreContainer(aTabData);
|
||||
let fragment = this.#sessionRestoreFragments.get(container);
|
||||
if (!fragment) {
|
||||
fragment = document.createDocumentFragment();
|
||||
this.#sessionRestoreFragments.set(container, fragment);
|
||||
}
|
||||
return fragment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts every collected fragment into its section, one insertion per
|
||||
* section instead of one move per restored tab.
|
||||
*/
|
||||
flushSessionRestoreFragments() {
|
||||
if (!this.#sessionRestoreFragments) {
|
||||
return;
|
||||
}
|
||||
const filledSpaces = new Set();
|
||||
for (const [container, fragment] of this.#sessionRestoreFragments) {
|
||||
container.insertBefore(fragment, this.#sessionRestoreAnchor(container));
|
||||
const spaceElement = container.closest("zen-workspace");
|
||||
if (spaceElement) {
|
||||
filledSpaces.add(spaceElement);
|
||||
}
|
||||
}
|
||||
this.#sessionRestoreFragments = null;
|
||||
for (const spaceElement of filledSpaces) {
|
||||
spaceElement.checkPinsExistence();
|
||||
}
|
||||
gBrowser.tabContainer._invalidateCachedTabs();
|
||||
}
|
||||
|
||||
#sessionRestoreContainer(aTabData) {
|
||||
if (aTabData.zenEssential) {
|
||||
return this.getEssentialsSection(aTabData.userContextId);
|
||||
}
|
||||
let spaceElement = this.workspaceElement(aTabData.zenWorkspace);
|
||||
if (!spaceElement?.tabsContainer) {
|
||||
// The space is gone, or the tab never had one. Keep it in the active
|
||||
// space, #clearAnyZombieTabs takes care of it later on if need be.
|
||||
spaceElement = this.activeWorkspaceElement;
|
||||
}
|
||||
return aTabData.pinned
|
||||
? spaceElement.pinnedTabsContainer
|
||||
: spaceElement.tabsContainer;
|
||||
}
|
||||
|
||||
#sessionRestoreAnchor(aContainer) {
|
||||
// Essentials are shared between spaces, so a restore only ever adds to
|
||||
// what is already there. Every other section gets the restored tabs in
|
||||
// front of whatever the window started with, which is the tab the startup
|
||||
// page opened with and which belongs at the end.
|
||||
if (aContainer.classList.contains("zen-essentials-container")) {
|
||||
return null;
|
||||
}
|
||||
return aContainer.firstChild;
|
||||
}
|
||||
|
||||
initializeWorkspaceNavigation() {
|
||||
this.#setupAppCommandHandlers();
|
||||
this.#setupSidebarHandlers();
|
||||
@@ -796,41 +862,68 @@ class nsZenWorkspaces {
|
||||
console.error("gZenWorkspaces: Error initializing theme picker", e);
|
||||
}
|
||||
this.#initializeTabsStripSections();
|
||||
return this.promiseInitialized;
|
||||
}
|
||||
|
||||
#hasFinishedInitialization = false;
|
||||
|
||||
async onWindowRestored() {
|
||||
if (this.#hasFinishedInitialization || !this.workspaceEnabled) {
|
||||
return;
|
||||
}
|
||||
this.#hasFinishedInitialization = true;
|
||||
const activeWorkspace = this.getActiveWorkspace();
|
||||
if (!this.privateWindowOrDisabled) {
|
||||
await this.promisePinnedInitialized;
|
||||
}
|
||||
this.#initializeEmptyTab();
|
||||
return (async () => {
|
||||
await this.#waitForPromises();
|
||||
this.#afterLoadInit();
|
||||
await this.#initializeWorkspaceBookmarks();
|
||||
await this.changeWorkspace(activeWorkspace, { onInit: true });
|
||||
this.#fixTabPositions();
|
||||
this.onWindowResize();
|
||||
this._resolveInitialized();
|
||||
this.#clearAnyZombieTabs(); // Dont call with await
|
||||
delete this._resolveInitialized;
|
||||
this.#afterLoadInit();
|
||||
await this.#initializeWorkspaceBookmarks();
|
||||
await this.changeWorkspace(activeWorkspace, { onInit: true });
|
||||
this.#fixTabPositions();
|
||||
this.onWindowResize();
|
||||
// The spaces hold their tabs now, so the tab the user is waiting for can
|
||||
// be selected without waiting on anything else.
|
||||
try {
|
||||
await this.selectStartPage();
|
||||
} catch (e) {
|
||||
console.error("gZenWorkspaces: Error selecting the start page", e);
|
||||
}
|
||||
this._resolveInitialized();
|
||||
this.#clearAnyZombieTabs(); // Dont call with await
|
||||
delete this._resolveInitialized;
|
||||
|
||||
const tabUpdateListener = this.updateTabsContainers.bind(this);
|
||||
window.addEventListener("TabOpen", tabUpdateListener);
|
||||
window.addEventListener("TabClose", tabUpdateListener);
|
||||
window.addEventListener("TabAddedToEssentials", tabUpdateListener);
|
||||
window.addEventListener("TabRemovedFromEssentials", tabUpdateListener);
|
||||
window.addEventListener("TabPinned", tabUpdateListener);
|
||||
window.addEventListener("TabUnpinned", tabUpdateListener);
|
||||
window.addEventListener("aftercustomization", tabUpdateListener);
|
||||
window.addEventListener("TabSelect", this.onLocationChange.bind(this));
|
||||
window.addEventListener(
|
||||
"TabBrowserInserted",
|
||||
this.onTabBrowserInserted.bind(this)
|
||||
);
|
||||
const tabUpdateListener = this.updateTabsContainers.bind(this);
|
||||
window.addEventListener("TabOpen", tabUpdateListener);
|
||||
window.addEventListener("TabClose", tabUpdateListener);
|
||||
window.addEventListener("TabAddedToEssentials", tabUpdateListener);
|
||||
window.addEventListener("TabRemovedFromEssentials", tabUpdateListener);
|
||||
window.addEventListener("TabPinned", tabUpdateListener);
|
||||
window.addEventListener("TabUnpinned", tabUpdateListener);
|
||||
window.addEventListener("aftercustomization", tabUpdateListener);
|
||||
window.addEventListener("TabSelect", this.onLocationChange.bind(this));
|
||||
window.addEventListener(
|
||||
"TabBrowserInserted",
|
||||
this.onTabBrowserInserted.bind(this)
|
||||
);
|
||||
|
||||
this.updateWorkspacesChangeContextMenu();
|
||||
})();
|
||||
this.updateWorkspacesChangeContextMenu();
|
||||
}
|
||||
|
||||
async selectStartPage() {
|
||||
if (!this.workspaceEnabled || gZenUIManager.testingEnabled) {
|
||||
if (
|
||||
!this.workspaceEnabled ||
|
||||
gZenUIManager.testingEnabled ||
|
||||
this.#hasSelectedStartPage
|
||||
) {
|
||||
return;
|
||||
}
|
||||
await this.promiseInitialized;
|
||||
// #hasInitialized only goes up once the whole init promise settles, so a
|
||||
// second restore arriving before that would otherwise run this again.
|
||||
this.#hasSelectedStartPage = true;
|
||||
// _handleURIToLoad selects the tab it loads the startup URI into, so
|
||||
// picking ours before delayed startup is done just gets overwritten.
|
||||
await delayedStartupPromise;
|
||||
let resolveSelectPromise;
|
||||
let selectPromise = new Promise(resolve => {
|
||||
resolveSelectPromise = resolve;
|
||||
@@ -921,8 +1014,12 @@ class nsZenWorkspaces {
|
||||
|
||||
// Wait for the next event loop to ensure that the startup focus logic by
|
||||
// firefox has finished doing it's thing.
|
||||
setTimeout(() => {
|
||||
if (document.documentElement.hasAttribute("zen-welcome-stage")) {
|
||||
const focusTimer = setTimeout(() => {
|
||||
if (
|
||||
window.closed ||
|
||||
!window.docShell ||
|
||||
document.documentElement.hasAttribute("zen-welcome-stage")
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (gZenVerticalTabsManager._canReplaceNewTab && shownEmptyTab) {
|
||||
@@ -933,6 +1030,11 @@ class nsZenWorkspaces {
|
||||
gBrowser.selectedBrowser.focus();
|
||||
}
|
||||
});
|
||||
// A window torn down before this runs would leave the focus work in
|
||||
// flight against a destroyed urlbar.
|
||||
window.addEventListener("unload", () => clearTimeout(focusTimer), {
|
||||
once: true,
|
||||
});
|
||||
|
||||
if (
|
||||
!gZenVerticalTabsManager._canReplaceNewTab &&
|
||||
@@ -961,6 +1063,8 @@ class nsZenWorkspaces {
|
||||
);
|
||||
}
|
||||
|
||||
#hasSelectedStartPage = false;
|
||||
|
||||
handleInitialTab(tab, isEmpty) {
|
||||
if (gZenUIManager.testingEnabled || !this.workspaceEnabled) {
|
||||
return;
|
||||
@@ -2552,7 +2656,9 @@ class nsZenWorkspaces {
|
||||
#createWorkspaceData(name, icon, containerTabId = 0) {
|
||||
if (!this.currentWindowIsSyncing) {
|
||||
containerTabId =
|
||||
parseInt(gBrowser.selectedTab.getAttribute("usercontextid")) || 0;
|
||||
parseInt(
|
||||
globalThis.gBrowser?.selectedTab?.getAttribute("usercontextid")
|
||||
) || 0;
|
||||
let label =
|
||||
ContextualIdentityService.getUserContextLabel(containerTabId) ||
|
||||
"Default";
|
||||
|
||||
@@ -20,6 +20,8 @@ support-files = [
|
||||
|
||||
["browser_folder_issue_9981.js"]
|
||||
|
||||
["browser_folder_issue_15522.js"]
|
||||
|
||||
["browser_folder_level_checks.js"]
|
||||
|
||||
["browser_folder_max_subfolders.js"]
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
https://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
const TOGGLE_DELAY = 90;
|
||||
|
||||
function folderHeight(folder) {
|
||||
return folder.getBoundingClientRect().height;
|
||||
}
|
||||
|
||||
async function waitForFolderSettled(folder, message) {
|
||||
let previous = null;
|
||||
await TestUtils.waitForCondition(() => {
|
||||
const height = folderHeight(folder);
|
||||
const settled = previous !== null && Math.abs(height - previous) < 1;
|
||||
previous = height;
|
||||
return settled;
|
||||
}, message);
|
||||
}
|
||||
|
||||
add_task(async function test_Issue_15522() {
|
||||
const tab1 = BrowserTestUtils.addTab(gBrowser, "about:blank");
|
||||
const tab2 = BrowserTestUtils.addTab(gBrowser, "about:blank");
|
||||
const folder = await gZenFolders.createFolder([tab1, tab2], {
|
||||
renameFolder: false,
|
||||
label: "folder",
|
||||
});
|
||||
await waitForFolderSettled(folder, "Folder should be done opening");
|
||||
const expandedHeight = folderHeight(folder);
|
||||
|
||||
folder.collapsed = true;
|
||||
await waitForFolderSettled(folder, "Folder should be done collapsing");
|
||||
const collapsedHeight = folderHeight(folder);
|
||||
Assert.less(
|
||||
collapsedHeight,
|
||||
expandedHeight,
|
||||
"Collapsing the folder should hide its tabs"
|
||||
);
|
||||
|
||||
folder.collapsed = false;
|
||||
await waitForFolderSettled(folder, "Folder should be done expanding");
|
||||
Assert.lessOrEqual(
|
||||
Math.abs(folderHeight(folder) - expandedHeight),
|
||||
1,
|
||||
"Expanding the folder should show its tabs again"
|
||||
);
|
||||
|
||||
folder.collapsed = true;
|
||||
await new Promise(resolve => setTimeout(resolve, TOGGLE_DELAY));
|
||||
folder.collapsed = false;
|
||||
await new Promise(resolve => setTimeout(resolve, TOGGLE_DELAY));
|
||||
folder.collapsed = true;
|
||||
|
||||
await waitForFolderSettled(folder, "Folder should be done collapsing");
|
||||
ok(folder.collapsed, "Folder should be collapsed after spamming toggles");
|
||||
Assert.lessOrEqual(
|
||||
Math.abs(folderHeight(folder) - collapsedHeight),
|
||||
1,
|
||||
"Folder should be fully collapsed instead of stuck half open"
|
||||
);
|
||||
|
||||
await removeFolder(folder);
|
||||
});
|
||||
Reference in New Issue
Block a user