Compare commits

...
4 Commits
19 changed files with 613 additions and 209 deletions
@@ -78,6 +78,7 @@ library-downloads-opening-in = { PLATFORM() ->
[windows] Opening in File Explorer… [windows] Opening in File Explorer…
*[other] Opening in file manager… *[other] Opening in file manager…
} }
library-downloads-open-when-done = Opens when finished
library-downloads-cancel-button = library-downloads-cancel-button =
.title = Cancel download .title = Cancel download
library-downloads-filter-title = Filter Downloads… library-downloads-filter-title = Filter Downloads…
+2 -10
View File
@@ -1,8 +1,8 @@
diff --git a/browser/base/content/browser-init.js b/browser/base/content/browser-init.js 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 --- a/browser/base/content/browser-init.js
+++ b/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() { onLoad() {
@@ -10,11 +10,3 @@ index 0aa4baea8e5e6d742fc6cd3333068e7eb64b5bbc..057bb2386420edd31154979338e71d00
gBrowser.addEventListener("DOMUpdateBlockedPopups", e => gBrowser.addEventListener("DOMUpdateBlockedPopups", e =>
PopupAndRedirectBlockerObserver.handleEvent(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 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 --- a/browser/components/sessionstore/SessionStore.sys.mjs
+++ b/browser/components/sessionstore/SessionStore.sys.mjs +++ b/browser/components/sessionstore/SessionStore.sys.mjs
@@ -111,6 +111,11 @@ const TAB_EVENTS = [ @@ -111,6 +111,11 @@ const TAB_EVENTS = [
@@ -308,20 +308,37 @@ index 52fc803050cd257d6de52b1e4a92f3211cbaba88..a9841832ddce540dc0f9b14c29e5eada
tabbrowser.removeTab(tabbrowser.tabs[i]); tabbrowser.removeTab(tabbrowser.tabs[i]);
} }
} }
@@ -5529,6 +5580,12 @@ class _SessionStore { @@ -5504,6 +5555,11 @@ class _SessionStore {
savedTabGroup => !openTabGroupIdsInWindow.has(savedTabGroup.id) this.#prefBranch.getBoolPref("sessionstore.restore_tabs_lazily") &&
); this.#restore_on_demand;
}
+ if (winData.isZenUnsynced) { + if (winData.isZenUnsynced) {
+ aWindow.document.documentElement.setAttribute("zen-unsynced-window", "true"); + aWindow.document.documentElement.setAttribute("zen-unsynced-window", "true");
+ } + }
+ aWindow.gZenFolders?.restoreDataFromSessionStore(winData.folders);
+ aWindow.gZenWorkspaces?.restoreWorkspacesFromSessionStore(winData); + 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); + aWindow.gZenViewSplitter?.restoreDataFromSessionStore(winData.splitViewData);
// Move the originally open tabs to the end. // Move the originally open tabs to the end.
if (initialTabs) { 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 // Most of tabData has been restored, now continue with restoring
// attributes that may trigger external events. // attributes that may trigger external events.
@@ -347,7 +364,7 @@ index 52fc803050cd257d6de52b1e4a92f3211cbaba88..a9841832ddce540dc0f9b14c29e5eada
if (tabData.pinned) { if (tabData.pinned) {
tabbrowser.pinTab(tab); tabbrowser.pinTab(tab);
@@ -6272,6 +6348,9 @@ class _SessionStore { @@ -6272,6 +6351,9 @@ class _SessionStore {
aWindow.gURLBar.readOnly = false; aWindow.gURLBar.readOnly = false;
} }
} }
@@ -357,7 +374,7 @@ index 52fc803050cd257d6de52b1e4a92f3211cbaba88..a9841832ddce540dc0f9b14c29e5eada
let promiseParts = Promise.withResolvers(); let promiseParts = Promise.withResolvers();
const wasMinimized = aWindow.windowState == aWindow.STATE_MINIMIZED; const wasMinimized = aWindow.windowState == aWindow.STATE_MINIMIZED;
@@ -6774,6 +6853,7 @@ class _SessionStore { @@ -6774,6 +6856,7 @@ class _SessionStore {
*/ */
#isCmdLineEmpty(aWindow, aState) { #isCmdLineEmpty(aWindow, aState) {
var pinnedOnly = var pinnedOnly =
@@ -365,7 +382,7 @@ index 52fc803050cd257d6de52b1e4a92f3211cbaba88..a9841832ddce540dc0f9b14c29e5eada
aState.windows && aState.windows &&
aState.windows.every(win => win.tabs.every(tab => tab.pinned)); 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(); let groupsToSave = new Map();
for (let tIndex = 0; tIndex < window.tabs.length; ) { for (let tIndex = 0; tIndex < window.tabs.length; ) {
@@ -374,7 +391,7 @@ index 52fc803050cd257d6de52b1e4a92f3211cbaba88..a9841832ddce540dc0f9b14c29e5eada
// Adjust window.selected // Adjust window.selected
if (tIndex + 1 < window.selected) { if (tIndex + 1 < window.selected) {
window.selected -= 1; window.selected -= 1;
@@ -7131,7 +7211,7 @@ class _SessionStore { @@ -7131,7 +7214,7 @@ class _SessionStore {
); );
// We don't want to increment tIndex here. // We don't want to increment tIndex here.
continue; continue;
@@ -383,7 +400,7 @@ index 52fc803050cd257d6de52b1e4a92f3211cbaba88..a9841832ddce540dc0f9b14c29e5eada
// Convert any open groups into saved groups. // Convert any open groups into saved groups.
let groupStateToSave = window.groups.find( let groupStateToSave = window.groups.find(
groupState => groupState.id == window.tabs[tIndex].groupId groupState => groupState.id == window.tabs[tIndex].groupId
@@ -7691,7 +7771,6 @@ class _SessionStore { @@ -7691,7 +7774,6 @@ class _SessionStore {
timer.initWithCallback( timer.initWithCallback(
() => { () => {
if (beats <= 0) { if (beats <= 0) {
@@ -391,7 +408,7 @@ index 52fc803050cd257d6de52b1e4a92f3211cbaba88..a9841832ddce540dc0f9b14c29e5eada
Glean.sessionRestore.shutdownFlushAllOutcomes.timed_out.add(1); Glean.sessionRestore.shutdownFlushAllOutcomes.timed_out.add(1);
deferred.resolve(); deferred.resolve();
} }
@@ -8174,6 +8253,7 @@ class _SessionStore { @@ -8174,6 +8256,7 @@ class _SessionStore {
if ( if (
!savedTabGroupState.tabs.length || !savedTabGroupState.tabs.length ||
this.getSavedTabGroup(savedTabGroupState.id) this.getSavedTabGroup(savedTabGroupState.id)
@@ -1,5 +1,5 @@
diff --git a/browser/components/tabbrowser/Tabbrowser.sys.mjs b/browser/components/tabbrowser/Tabbrowser.sys.mjs 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 --- a/browser/components/tabbrowser/Tabbrowser.sys.mjs
+++ b/browser/components/tabbrowser/Tabbrowser.sys.mjs +++ b/browser/components/tabbrowser/Tabbrowser.sys.mjs
@@ -484,6 +484,7 @@ export class Tabbrowser { @@ -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. // Add a new tab if needed.
@@ -516,7 +533,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
let url = "about:blank"; let url = "about:blank";
if (tabData.entries?.length) { if (tabData.entries?.length) {
@@ -4977,8 +5156,10 @@ export class Tabbrowser { @@ -4977,8 +5166,10 @@ export class Tabbrowser {
insertTab: false, insertTab: false,
skipLoad: true, skipLoad: true,
preferredRemoteType, preferredRemoteType,
@@ -528,7 +545,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
if (select) { if (select) {
tabToSelect = tab; tabToSelect = tab;
} }
@@ -5000,7 +5181,8 @@ export class Tabbrowser { @@ -5000,7 +5191,8 @@ export class Tabbrowser {
this.pinTab(tab); this.pinTab(tab);
// Then ensure all the tab open/pinning information is sent. // Then ensure all the tab open/pinning information is sent.
this.#fireTabOpen(tab, {}); this.#fireTabOpen(tab, {});
@@ -538,7 +555,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
let { groupId } = tabData; let { groupId } = tabData;
const tabGroup = tabGroupWorkingData.get(groupId); const tabGroup = tabGroupWorkingData.get(groupId);
// if a tab refers to a tab group we don't know, skip any group // 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.id,
tabGroup.stateData.color, tabGroup.stateData.color,
tabGroup.stateData.collapsed, tabGroup.stateData.collapsed,
@@ -548,9 +565,55 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
+ tabGroup.stateData.essential, + tabGroup.stateData.essential,
+ tabGroup.stateData.splitView, + 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. // to remove the old selected tab.
if (tabToSelect) { if (tabToSelect) {
let leftoverTab = this.selectedTab; let leftoverTab = this.selectedTab;
@@ -569,7 +632,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
if (tabs.length > 1 || !tabs[0].selected) { if (tabs.length > 1 || !tabs[0].selected) {
this.#updateTabsAfterInsert(); this.#updateTabsAfterInsert();
@@ -5313,11 +5507,17 @@ export class Tabbrowser { @@ -5313,11 +5534,17 @@ export class Tabbrowser {
if (ownerTab) { if (ownerTab) {
tab.owner = ownerTab; tab.owner = ownerTab;
} }
@@ -588,7 +651,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
let insertRelatedAfterCurrent = Services.prefs.getBoolPref( let insertRelatedAfterCurrent = Services.prefs.getBoolPref(
"browser.tabs.insertRelatedAfterCurrent" "browser.tabs.insertRelatedAfterCurrent"
); );
@@ -5332,7 +5532,7 @@ export class Tabbrowser { @@ -5332,7 +5559,7 @@ export class Tabbrowser {
(insertRelatedAfterCurrent && lastRelatedTab) || (insertRelatedAfterCurrent && lastRelatedTab) ||
openerTab || openerTab ||
this.selectedTab; this.selectedTab;
@@ -597,7 +660,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
tabGroup = previousTab.group; tabGroup = previousTab.group;
} }
if ( if (
@@ -5348,7 +5548,7 @@ export class Tabbrowser { @@ -5348,7 +5575,7 @@ export class Tabbrowser {
previousTab.splitview previousTab.splitview
) + 1; ) + 1;
} else if (previousTab.visible) { } else if (previousTab.visible) {
@@ -606,7 +669,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
} else if (previousTab == this.documentGlobal.FirefoxViewHandler.tab) { } else if (previousTab == this.documentGlobal.FirefoxViewHandler.tab) {
elementIndex = 0; elementIndex = 0;
} }
@@ -5376,14 +5576,14 @@ export class Tabbrowser { @@ -5376,14 +5603,14 @@ export class Tabbrowser {
} }
// Ensure index is within bounds. // Ensure index is within bounds.
if (tab.pinned) { if (tab.pinned) {
@@ -625,7 +688,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
if (pinned && !itemAfter?.pinned) { if (pinned && !itemAfter?.pinned) {
itemAfter = null; itemAfter = null;
@@ -5400,7 +5600,7 @@ export class Tabbrowser { @@ -5400,7 +5627,7 @@ export class Tabbrowser {
this.tabContainer._invalidateCachedTabs(); this.tabContainer._invalidateCachedTabs();
@@ -634,7 +697,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
if ( if (
(this.isTab(itemAfter) && itemAfter.group == tabGroup) || (this.isTab(itemAfter) && itemAfter.group == tabGroup) ||
this.isSplitViewWrapper(itemAfter) this.isSplitViewWrapper(itemAfter)
@@ -5431,7 +5631,11 @@ export class Tabbrowser { @@ -5431,7 +5658,11 @@ export class Tabbrowser {
const tabContainer = pinned const tabContainer = pinned
? this.tabContainer.pinnedTabsContainer ? this.tabContainer.pinnedTabsContainer
: this.tabContainer; : this.tabContainer;
@@ -646,7 +709,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
} }
if (tab.group?.collapsed) { if (tab.group?.collapsed) {
@@ -5446,6 +5650,7 @@ export class Tabbrowser { @@ -5446,6 +5677,7 @@ export class Tabbrowser {
if (pinned) { if (pinned) {
this.#updateTabBarForPinnedTabs(); this.#updateTabBarForPinnedTabs();
} }
@@ -654,7 +717,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
this.documentGlobal.TabBarVisibility.update(); this.documentGlobal.TabBarVisibility.update();
} }
@@ -6025,6 +6230,7 @@ export class Tabbrowser { @@ -6025,6 +6257,7 @@ export class Tabbrowser {
metricsContext, metricsContext,
} = {} } = {}
) { ) {
@@ -662,7 +725,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
// When 'closeWindowWithLastTab' pref is enabled, closing all tabs // When 'closeWindowWithLastTab' pref is enabled, closing all tabs
// can be considered equivalent to closing the window. // can be considered equivalent to closing the window.
if ( if (
@@ -6135,6 +6341,7 @@ export class Tabbrowser { @@ -6135,6 +6368,7 @@ export class Tabbrowser {
closedTabCount -= 1; closedTabCount -= 1;
} }
} }
@@ -670,7 +733,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
if (closedTabCount > 0) { if (closedTabCount > 0) {
this.recordTabMetrics( this.recordTabMetrics(
@@ -6236,6 +6443,14 @@ export class Tabbrowser { @@ -6236,6 +6470,14 @@ export class Tabbrowser {
return; return;
} }
@@ -685,7 +748,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
let isVisibleTab = aTab.visible; let isVisibleTab = aTab.visible;
// We have to sample the tab width now, since #beginRemoveTab might // 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 // 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 = let tabWidth =
this.documentGlobal.windowUtils.getBoundsWithoutFlushing(aTab).width; this.documentGlobal.windowUtils.getBoundsWithoutFlushing(aTab).width;
let isLastTab = this.#isLastTabInWindow(aTab); let isLastTab = this.#isLastTabInWindow(aTab);
@@ -695,7 +758,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
if ( if (
!this.#beginRemoveTab(aTab, { !this.#beginRemoveTab(aTab, {
closeWindowFastpath: true, closeWindowFastpath: true,
@@ -6254,13 +6472,14 @@ export class Tabbrowser { @@ -6254,13 +6499,14 @@ export class Tabbrowser {
metricsContext, metricsContext,
}) })
) { ) {
@@ -711,7 +774,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
let lockTabSizing = let lockTabSizing =
!this.tabContainer.verticalMode && !this.tabContainer.verticalMode &&
!aTab.pinned && !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. // We're not animating, so we can cancel the animation stopwatch.
Glean.browserTabclose.timeAnim.cancel(aTab._closeTimeAnimTimerId); Glean.browserTabclose.timeAnim.cancel(aTab._closeTimeAnimTimerId);
aTab._closeTimeAnimTimerId = null; aTab._closeTimeAnimTimerId = null;
@@ -726,7 +789,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
return; return;
} }
@@ -6331,7 +6556,9 @@ export class Tabbrowser { @@ -6331,7 +6583,9 @@ export class Tabbrowser {
get #shouldCloseWindowWithLastTab() { get #shouldCloseWindowWithLastTab() {
return ( return (
!this.documentGlobal.toolbar.visible || !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) { #isLastTabInWindow(tab) {
for (const otherTab of this.tabs) { for (const otherTab of this.tabs) {
@@ -746,7 +809,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
return false; return false;
} }
} }
@@ -6488,6 +6715,7 @@ export class Tabbrowser { @@ -6488,6 +6742,7 @@ export class Tabbrowser {
newTab = true; newTab = true;
} }
@@ -754,7 +817,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
aTab._endRemoveArgs = [closeWindow, newTab]; aTab._endRemoveArgs = [closeWindow, newTab];
// swapBrowsersAndCloseOther will take care of closing the window without animation. // 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) { if (newTab) {
@@ -769,7 +832,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
} else { } else {
this.documentGlobal.TabBarVisibility.update(); this.documentGlobal.TabBarVisibility.update();
} }
@@ -6701,6 +6923,7 @@ export class Tabbrowser { @@ -6701,6 +6950,7 @@ export class Tabbrowser {
this.tabs[i]._index = i; this.tabs[i]._index = i;
} }
@@ -777,7 +840,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
if (!this.#windowIsClosing) { if (!this.#windowIsClosing) {
// update tab close buttons state // update tab close buttons state
this.tabContainer._updateCloseButtons(); this.tabContainer._updateCloseButtons();
@@ -6891,6 +7114,7 @@ export class Tabbrowser { @@ -6891,6 +7141,7 @@ export class Tabbrowser {
memory_after: await getTotalMemoryUsage(), memory_after: await getTotalMemoryUsage(),
time_to_unload_in_ms: timeElapsed, 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); let excludeTabs = new Set(aExcludeTabs);
@@ -799,7 +862,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
} }
if ( if (
@@ -6949,13 +7174,13 @@ export class Tabbrowser { @@ -6949,13 +7201,13 @@ export class Tabbrowser {
!excludeTabs.has(aTab.owner) && !excludeTabs.has(aTab.owner) &&
Services.prefs.getBoolPref("browser.tabs.selectOwnerOnClose") Services.prefs.getBoolPref("browser.tabs.selectOwnerOnClose")
) { ) {
@@ -815,7 +878,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
); );
if (Services.prefs.getBoolPref("browser.tabs.selectMRUOnClose", false)) { 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, { let tab = this.tabContainer.findNextTab(aTab, {
direction: 1, direction: 1,
filter: _tab => remainingTabs.includes(_tab), filter: _tab => remainingTabs.includes(_tab),
@@ -6983,7 +7215,7 @@ export class Tabbrowser { @@ -6983,7 +7242,7 @@ export class Tabbrowser {
} }
if (tab) { if (tab) {
@@ -838,7 +901,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
} }
// If no qualifying visible tab was found, see if there is a tab in // 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) { #blurTab(aTab) {
@@ -7021,7 +7253,7 @@ export class Tabbrowser { @@ -7021,7 +7280,7 @@ export class Tabbrowser {
* @returns {boolean} * @returns {boolean}
* False if swapping isn't permitted, true otherwise. * 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 // Do not allow transfering a private tab to a non-private window
// and vice versa. // and vice versa.
if ( 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 // fire the beforeunload event in the process. Close the other
// window if this was its last tab. // window if this was its last tab.
if ( if (
@@ -864,7 +927,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
!remoteBrowser.#beginRemoveTab(aOtherTab, { !remoteBrowser.#beginRemoveTab(aOtherTab, {
adoptedByTab: aOurTab, adoptedByTab: aOurTab,
closeWindowWithLastTab: true, 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 // If this is the last tab of the window, hide the window
// immediately without animation before the docshell swap, to avoid // immediately without animation before the docshell swap, to avoid
// about:blank being painted. // about:blank being painted.
@@ -873,7 +936,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
if (closeWindow) { if (closeWindow) {
let win = aOtherTab.documentGlobal; let win = aOtherTab.documentGlobal;
win.windowUtils.suppressAnimation(true); 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. // Finish tearing down the tab that's going away.
@@ -887,7 +950,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
this.setTabTitle(aOurTab); 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.selected ||
aTab.closing || aTab.closing ||
// Tabs that are sharing the screen, microphone or camera cannot be hidden. // 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={}] * @param {object} [aOptions={}]
* Key-value pairs that will be serialized into the features string. * Key-value pairs that will be serialized into the features string.
*/ */
@@ -910,7 +973,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
if (this.tabs.length == 1) { if (this.tabs.length == 1) {
return null; 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 // tell a new window to take the "dropped" tab
let args = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray); let args = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
args.appendElement(aTab.splitview ?? aTab); args.appendElement(aTab.splitview ?? aTab);
@@ -919,7 +982,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
private: lazy.PrivateBrowsingUtils.isWindowPrivate(this.documentGlobal), private: lazy.PrivateBrowsingUtils.isWindowPrivate(this.documentGlobal),
features: Object.entries(aOptions) features: Object.entries(aOptions)
.map(([key, value]) => `${key}=${value}`) .map(([key, value]) => `${key}=${value}`)
@@ -7601,6 +7837,8 @@ export class Tabbrowser { @@ -7601,6 +7864,8 @@ export class Tabbrowser {
openerWindow: this.documentGlobal, openerWindow: this.documentGlobal,
args, 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} * @returns {element is MozTabbrowserTabGroup}
*/ */
isTabGroup(element) { 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. // Don't allow mixing pinned and unpinned tabs.
@@ -948,7 +1011,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
} else { } else {
tabIndex = Math.max(tabIndex, this.pinnedTabCount); tabIndex = Math.max(tabIndex, this.pinnedTabCount);
} }
@@ -7854,8 +8092,8 @@ export class Tabbrowser { @@ -7854,8 +8119,8 @@ export class Tabbrowser {
this.#handleTabMove( this.#handleTabMove(
element, element,
() => { () => {
@@ -959,7 +1022,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
neighbor = neighbor.group; neighbor = neighbor.group;
} }
if (neighbor?.splitview) { if (neighbor?.splitview) {
@@ -7866,6 +8104,12 @@ export class Tabbrowser { @@ -7866,6 +8131,12 @@ export class Tabbrowser {
return; return;
} }
} }
@@ -972,7 +1035,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
if (movingForwards && neighbor) { if (movingForwards && neighbor) {
neighbor.after(element); neighbor.after(element);
@@ -7939,23 +8183,31 @@ export class Tabbrowser { @@ -7939,23 +8210,31 @@ export class Tabbrowser {
) { ) {
if (this.isTabGroupLabel(targetElement)) { if (this.isTabGroupLabel(targetElement)) {
targetElement = targetElement.group; targetElement = targetElement.group;
@@ -1010,7 +1073,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
} else if (!element.pinned && targetElement && targetElement.pinned) { } else if (!element.pinned && targetElement && targetElement.pinned) {
// If the caller asks to move an unpinned element next to a 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 // 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. // move the tab group right before the first unpinned tab.
// 4. Moving a tab group and the first unpinned tab is grouped: // 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. // 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 // 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. // 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 = () => let getContainer = () =>
@@ -1055,7 +1118,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
element.pinned element.pinned
? this.tabContainer.pinnedTabsContainer ? this.tabContainer.pinnedTabsContainer
: this.tabContainer; : this.tabContainer;
@@ -7990,11 +8266,15 @@ export class Tabbrowser { @@ -7990,11 +8293,15 @@ export class Tabbrowser {
element, element,
() => { () => {
if (moveBefore) { if (moveBefore) {
@@ -1072,7 +1135,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
} }
}, },
{ metricsContext } { metricsContext }
@@ -8070,11 +8350,15 @@ export class Tabbrowser { @@ -8070,11 +8377,15 @@ export class Tabbrowser {
* The context for the operation for telemetry purposes. * The context for the operation for telemetry purposes.
*/ */
moveTabToExistingGroup(aTab, aGroup, { metricsContext } = {}) { moveTabToExistingGroup(aTab, aGroup, { metricsContext } = {}) {
@@ -1091,7 +1154,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
} }
if (aTab.group && aTab.group.id === aGroup.id) { if (aTab.group && aTab.group.id === aGroup.id) {
return; return;
@@ -8153,6 +8437,7 @@ export class Tabbrowser { @@ -8153,6 +8464,7 @@ export class Tabbrowser {
let state = { let state = {
tabIndex: tab.index, tabIndex: tab.index,
@@ -1099,7 +1162,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
}; };
if (tab.visible) { if (tab.visible) {
state.elementIndex = tab.elementIndex; state.elementIndex = tab.elementIndex;
@@ -8190,7 +8475,7 @@ export class Tabbrowser { @@ -8190,7 +8502,7 @@ export class Tabbrowser {
let changedSplitView = let changedSplitView =
previousTabState.splitViewId != currentTabState.splitViewId; previousTabState.splitViewId != currentTabState.splitViewId;
@@ -1108,7 +1171,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
tab.dispatchEvent( tab.dispatchEvent(
new this.documentGlobal.CustomEvent("TabMove", { new this.documentGlobal.CustomEvent("TabMove", {
bubbles: true, bubbles: true,
@@ -8248,6 +8533,10 @@ export class Tabbrowser { @@ -8248,6 +8560,10 @@ export class Tabbrowser {
moveActionCallback(); moveActionCallback();
@@ -1119,7 +1182,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
// Clear tabs cache after moving nodes because the order of tabs may have // Clear tabs cache after moving nodes because the order of tabs may have
// changed. // changed.
this.tabContainer._invalidateCachedTabs(); this.tabContainer._invalidateCachedTabs();
@@ -8307,7 +8596,22 @@ export class Tabbrowser { @@ -8307,7 +8623,22 @@ export class Tabbrowser {
* @returns {object} * @returns {object}
* The new tab in the current window, null if the tab couldn't be adopted. * 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 // 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 // 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 // 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; params.skipLoad = true;
let newTab = this.addWebTab("about:blank", params); let newTab = this.addWebTab("about:blank", params);
@@ -1152,7 +1215,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
aTab.container.tabDragAndDrop.finishAnimateTabMove(); 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. // preventDefault(). It will still raise the window if appropriate.
return; return;
} }
@@ -1161,7 +1224,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
this.documentGlobal.focus(); this.documentGlobal.focus();
aEvent.preventDefault(); aEvent.preventDefault();
} }
@@ -9206,7 +9512,6 @@ export class Tabbrowser { @@ -9206,7 +9539,6 @@ export class Tabbrowser {
on_TabGroupCollapse(aEvent) { on_TabGroupCollapse(aEvent) {
aEvent.target.tabs.forEach(tab => { 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); let filter = this.#tabFilters.get(tab);
if (filter) { if (filter) {
@@ -1179,7 +1242,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
let listener = this.#tabListeners.get(tab); let listener = this.#tabListeners.get(tab);
if (listener) { if (listener) {
@@ -10354,6 +10661,7 @@ class TabProgressListener { @@ -10354,6 +10688,7 @@ class TabProgressListener {
aWebProgress.isTopLevel aWebProgress.isTopLevel
) { ) {
this._tab.setAttribute("busy", "true"); this._tab.setAttribute("busy", "true");
@@ -1187,7 +1250,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
this.#tabbrowser._tabAttrModified(this._tab, ["busy"]); this.#tabbrowser._tabAttrModified(this._tab, ["busy"]);
this._tab._notselectedsinceload = !this._tab.selected; 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 // known defaults. Note we use the original URL since about:newtab
// redirects to a prerendered page. // redirects to a prerendered page.
const shouldRemoveFavicon = const shouldRemoveFavicon =
@@ -1195,7 +1258,7 @@ index f68e37926001384d0c8e80a3c163dc89e9c4ee5c..a9a5dfbc38348f2c070f22af2954a089
!this._browser.mIconURL && !this._browser.mIconURL &&
!ignoreBlank && !ignoreBlank &&
!(originalLocation.spec in FAVICON_DEFAULTS); !(originalLocation.spec in FAVICON_DEFAULTS);
@@ -10610,13 +10919,6 @@ class TabProgressListener { @@ -10610,13 +10946,6 @@ class TabProgressListener {
this._browser.originalURI = aRequest.originalURI; this._browser.originalURI = aRequest.originalURI;
} }
+12 -14
View File
@@ -73,6 +73,10 @@ class nsZenEmojiPicker extends nsZenDOMOperatedFeature {
this.#changePage(false); this.#changePage(false);
} else if (event.target.id === "PanelUI-zen-emojis-picker-change-svg") { } else if (event.target.id === "PanelUI-zen-emojis-picker-change-svg") {
this.#changePage(true); 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; break;
case "input": case "input":
@@ -176,19 +180,17 @@ class nsZenEmojiPicker extends nsZenDOMOperatedFeature {
this.searchInput.value = ""; this.searchInput.value = "";
const allowEmojis = !this.#panel.hasAttribute("only-svg-icons"); const allowEmojis = !this.#panel.hasAttribute("only-svg-icons");
if (allowEmojis) { if (allowEmojis) {
const emojiList = this.emojiList; const emojis = document.createDocumentFragment();
for (const emoji of this.#emojis) { for (const emoji of this.#emojis) {
const item = document.createXULElement("toolbarbutton"); const item = document.createXULElement("toolbarbutton");
item.className = "toolbarbutton-1 zen-emojis-picker-emoji"; item.className = "toolbarbutton-1 zen-emojis-picker-emoji";
item.setAttribute("label", emoji.emoji); item.setAttribute("label", emoji.emoji);
item.setAttribute("tooltiptext", ""); item.setAttribute("tooltiptext", "");
item.addEventListener("command", () => { emojis.appendChild(item);
this.#selectEmoji(emoji.emoji);
});
emojiList.appendChild(item);
} }
this.emojiList.replaceChildren(emojis);
} }
const svgList = this.svgList; const icons = document.createDocumentFragment();
for (const icon of SVG_ICONS) { for (const icon of SVG_ICONS) {
const item = document.createXULElement("toolbarbutton"); const item = document.createXULElement("toolbarbutton");
item.className = "toolbarbutton-1 zen-emojis-picker-svg"; item.className = "toolbarbutton-1 zen-emojis-picker-svg";
@@ -196,11 +198,9 @@ class nsZenEmojiPicker extends nsZenDOMOperatedFeature {
item.setAttribute("tooltiptext", ""); item.setAttribute("tooltiptext", "");
item.style.listStyleImage = `url(${this.getSVGURL(icon)})`; item.style.listStyleImage = `url(${this.getSVGURL(icon)})`;
item.setAttribute("icon", icon); item.setAttribute("icon", icon);
item.addEventListener("command", () => { icons.appendChild(item);
this.#selectEmoji(this.getSVGURL(icon));
});
svgList.appendChild(item);
} }
this.svgList.replaceChildren(icons);
} }
#onPopupShown(event) { #onPopupShown(event) {
@@ -220,10 +220,8 @@ class nsZenEmojiPicker extends nsZenDOMOperatedFeature {
} }
this.#clearEmojis(); this.#clearEmojis();
const emojiList = this.emojiList; this.emojiList.replaceChildren();
emojiList.innerHTML = ""; this.svgList.replaceChildren();
this.svgList.innerHTML = "";
if (!this.#hasSelection) { if (!this.#hasSelection) {
this.#currentPromiseReject?.( this.#currentPromiseReject?.(
@@ -9,6 +9,7 @@
overflow: clip; overflow: clip;
:root:not([zen-no-padding="true"]) &:not(.zen-glance-overlay) { :root:not([zen-no-padding="true"]) &:not(.zen-glance-overlay) {
box-shadow: var(--zen-big-shadow);
/* stylelint-disable-next-line property-no-unknown */ /* stylelint-disable-next-line property-no-unknown */
corner-shape: round; corner-shape: round;
border-radius: calc(var(--zen-native-inner-radius) / var(--zen-squircle-value)); border-radius: calc(var(--zen-native-inner-radius) / var(--zen-squircle-value));
+2 -1
View File
@@ -657,6 +657,7 @@
--urlbarview-row-padding-block: 12px; --urlbarview-row-padding-block: 12px;
--urlbarView-row-gutter: 0px; --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; color: light-dark(rgba(0, 0, 0, 0.7), rgba(255, 255, 255, 0.7)) !important;
&:hover, &:hover,
@@ -698,7 +699,7 @@
} }
#urlbar-results { #urlbar-results {
max-height: 244px; max-height: 259px;
overflow-y: auto; overflow-y: auto;
scrollbar-width: none; scrollbar-width: none;
margin-block-start: calc(var(--urlbarView-results-padding) - 2px); margin-block-start: calc(var(--urlbarView-results-padding) - 2px);
+60 -13
View File
@@ -1597,6 +1597,54 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
return true; 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) { #calculateHeightShift(tabsContainer, selectedTabs) {
let heightShift = 0; let heightShift = 0;
if (selectedTabs.length) { if (selectedTabs.length) {
@@ -1641,10 +1689,10 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
if (selectedTabs.length) { if (selectedTabs.length) {
tabsContainer.removeAttribute("hidden"); tabsContainer.removeAttribute("hidden");
} }
const collapsedHeight = this.#calculateHeightShift( const startMargin = this.#settleAnimation(groupStart, "marginTop");
tabsContainer, const collapsedHeight = selectedTabs.length
selectedTabs ? 0
); : this.#measureCollapsedShift(tabsContainer, groupStart);
if (selectedTabs.length) { if (selectedTabs.length) {
for (let i = 0; i < groupItems.length; i++) { for (let i = 0; i < groupItems.length; i++) {
@@ -1697,10 +1745,10 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
...this.#createAnimation( ...this.#createAnimation(
groupStart, groupStart,
{ {
marginTop: -( marginTop: [
collapsedHeight + startMargin,
4 * (selectedTabs.length === 0 ? 1 : 0) -(collapsedHeight + 4 * (selectedTabs.length === 0 ? 1 : 0)),
), ],
}, },
{ duration, ease: "easeInOut" } { duration, ease: "easeInOut" }
) )
@@ -1709,16 +1757,15 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
gBrowser.tabContainer._invalidateCachedVisibleTabs(); gBrowser.tabContainer._invalidateCachedVisibleTabs();
this.#animationCount += 1; this.#animationCount += 1;
await Promise.all(animations); await Promise.all(animations);
if (this.#animationCount) {
this.#animationCount -= 1; this.#animationCount -= 1;
if (this.#animationCount || !group.collapsed) {
return; return;
} }
// Prevent hiding if we spam the group animations if (!selectedTabs.length) {
if (!selectedTabs.length && !this.#animationCount) {
tabsContainer.setAttribute("hidden", true); tabsContainer.setAttribute("hidden", true);
}
this.styleCleanup(itemsToHide); this.styleCleanup(itemsToHide);
this.#queueCollapsedRelayout(group);
}
} }
async animateExpand(group) { async animateExpand(group) {
+42 -3
View File
@@ -235,7 +235,31 @@ class ZenLibraryDownloadStack {
.finally(() => download.target.refresh()); .finally(() => download.target.refresh());
} }
/**
* @param {object} download
* @returns {boolean} Whether it will be opened as soon as it is here
*/
#opensWhenDone(download) {
return !download.stopped && !!download.launchWhenSucceeded;
}
/**
* Clicking a download that is still coming in asks for it to be opened as
* soon as it is here, the way the downloads panel does.
*
* @param {object} download
*/
#toggleOpenWhenDone(download) {
download.launchWhenSucceeded = !download.launchWhenSucceeded;
download._launchedFromPanel = download.launchWhenSucceeded;
this.#updateList();
}
#openDownload(download) { #openDownload(download) {
if (!download.stopped) {
this.#toggleOpenWhenDone(download);
return;
}
if (download.succeeded) { if (download.succeeded) {
lazy.DownloadsCommon.openDownload(download).catch(console.error); lazy.DownloadsCommon.openDownload(download).catch(console.error);
} else if (download.source?.url) { } else if (download.source?.url) {
@@ -274,14 +298,29 @@ class ZenLibraryDownloadStack {
); );
entry.querySelector(".zen-library-download-list-title").textContent = entry.querySelector(".zen-library-download-list-title").textContent =
this.#fileName(download); this.#fileName(download);
entry.querySelector(".zen-library-download-list-subtitle").textContent = const subtitle = entry.querySelector(
this.#statusText(download); ".zen-library-download-list-subtitle"
);
if (this.#opensWhenDone(download)) {
this.#window.document.l10n.setAttributes(
subtitle,
"library-downloads-open-when-done"
);
} else {
subtitle.removeAttribute("data-l10n-id");
subtitle.textContent = this.#statusText(download);
}
entry.toggleAttribute("downloading", !download.stopped); entry.toggleAttribute("downloading", !download.stopped);
entry.toggleAttribute("open-when-done", this.#opensWhenDone(download));
}); });
this.#window
.promiseDocumentFlushed(() => this.#list.getBoundingClientRect().height)
.then(height => {
this.#tabs?.style.setProperty( this.#tabs?.style.setProperty(
"--zen-library-stack-height", "--zen-library-stack-height",
`${this.#list.getBoundingClientRect().height}px` `${height}px`
); );
});
} }
#updateBadge(badge, download) { #updateBadge(badge, download) {
@@ -326,7 +326,34 @@ export class ZenLibraryDownloadsSection extends ZenLibrarySearchSection {
lazy.DownloadsCommon.showDownloadedFile(this.#file(download)); lazy.DownloadsCommon.showDownloadedFile(this.#file(download));
} }
/**
* Whether a download that is still coming in will be opened the moment it
* finishes.
*
* @param {object} download
* @returns {boolean}
*/
#opensWhenDone(download) {
return !download.stopped && !!download.launchWhenSucceeded;
}
/**
* Clicking a download that is still coming in asks for it to be opened as
* soon as it is here, the way the downloads panel does.
*
* @param {object} download
*/
#toggleOpenWhenDone(download) {
download.launchWhenSucceeded = !download.launchWhenSucceeded;
download._launchedFromPanel = download.launchWhenSucceeded;
this.requestUpdate();
}
#onRowClick(download) { #onRowClick(download) {
if (!download.stopped) {
this.#toggleOpenWhenDone(download);
return;
}
if (!this.#hasFile(download)) { if (!this.#hasFile(download)) {
return; return;
} }
@@ -516,15 +543,22 @@ export class ZenLibraryDownloadsSection extends ZenLibrarySearchSection {
// Rendering // Rendering
#renderSubtitle(download) { #renderSubtitle(download) {
const statusNode = let statusNode;
download === this.#openingDownload if (download === this.#openingDownload) {
? html`<span statusNode = html`<span
class="zen-library-download-status" class="zen-library-download-status"
data-l10n-id="library-downloads-opening-in" data-l10n-id="library-downloads-opening-in"
></span>` ></span>`;
: html`<span class="zen-library-download-status" } else if (this.#opensWhenDone(download)) {
statusNode = html`<span
class="zen-library-download-status"
data-l10n-id="library-downloads-open-when-done"
></span>`;
} else {
statusNode = html`<span class="zen-library-download-status"
>${this.#statusText(download)}</span >${this.#statusText(download)}</span
>`; >`;
}
return html` return html`
<span class="zen-library-row-subtitle"> <span class="zen-library-row-subtitle">
${statusNode} ${statusNode}
@@ -584,6 +618,7 @@ export class ZenLibraryDownloadsSection extends ZenLibrarySearchSection {
?pending=${pending} ?pending=${pending}
?indeterminate=${pending && !download.hasProgress} ?indeterminate=${pending && !download.hasProgress}
?paused=${pending && download.stopped} ?paused=${pending && download.stopped}
?open-when-done=${this.#opensWhenDone(download)}
?opening=${download === this.#openingDownload} ?opening=${download === this.#openingDownload}
@click=${() => this.#onRowClick(download)} @click=${() => this.#onRowClick(download)}
@contextmenu=${event => { @contextmenu=${event => {
+5
View File
@@ -882,6 +882,11 @@ zen-library:not([open]) :is(.zen-library-search-header, .zen-library-filter-head
display: none; display: none;
} }
.zen-library-row[pending][open-when-done]:not(:hover)
.zen-library-download-status {
display: inline;
}
.zen-library-row[pending]:not(:hover) .zen-library-row-subtitle::before { .zen-library-row[pending]:not(:hover) .zen-library-row-subtitle::before {
content: ""; content: "";
display: block; display: block;
@@ -1006,6 +1006,9 @@ export class nsZenSessionManager {
aWindow.gZenWorkspaces.restoreWorkspacesFromSessionStore({ aWindow.gZenWorkspaces.restoreWorkspacesFromSessionStore({
spaces: this.#sidebarWithoutCloning.spaces || [], 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();
} }
/** /**
+4 -4
View File
@@ -133,11 +133,11 @@ class nsZenWorkspaceIcons extends MozXULElement {
async #updateIcons() { async #updateIcons() {
const workspaces = gZenWorkspaces.getWorkspaces(); const workspaces = gZenWorkspaces.getWorkspaces();
this.innerHTML = ""; const icons = document.createDocumentFragment();
for (const workspace of workspaces) { for (const workspace of workspaces) {
const button = this.#createWorkspaceIcon(workspace); icons.appendChild(this.#createWorkspaceIcon(workspace));
this.appendChild(button);
} }
this.replaceChildren(icons);
if (workspaces.length <= 1) { if (workspaces.length <= 1) {
this.setAttribute("dont-show", "true"); this.setAttribute("dont-show", "true");
} else { } else {
@@ -169,7 +169,7 @@ class nsZenWorkspaceIcons extends MozXULElement {
for (const button of buttons) { for (const button of buttons) {
if (button.getAttribute("zen-workspace-id") == uuid) { if (button.getAttribute("zen-workspace-id") == uuid) {
selected = i; selected = i;
} else { } else if (button.hasAttribute("active")) {
button.removeAttribute("active"); button.removeAttribute("active");
} }
i++; i++;
+155 -49
View File
@@ -76,16 +76,6 @@ class nsZenWorkspaces {
this._resolveInitialized = resolve; this._resolveInitialized = resolve;
}); });
async #waitForPromises() {
if (this.privateWindowOrDisabled) {
return;
}
await Promise.all([
this.promisePinnedInitialized,
SessionStore.promiseAllWindowsRestored,
]);
}
async init() { async init() {
// Initialize workspace change mutex // Initialize workspace change mutex
this._workspaceChangeInProgress = false; this._workspaceChangeInProgress = false;
@@ -152,8 +142,9 @@ class nsZenWorkspaces {
this.addPopupListeners(); this.addPopupListeners();
if (this.privateWindowOrDisabled) { if (this.privateWindowOrDisabled) {
await this.#waitForPromises(); await delayedStartupPromise;
await this.restoreWorkspacesFromSessionStore({}); this.restoreWorkspacesFromSessionStore({});
await this.onWindowRestored();
} }
if (!this.privateWindowOrDisabled) { if (!this.privateWindowOrDisabled) {
@@ -300,6 +291,9 @@ class nsZenWorkspaces {
inBackground: true, inBackground: true,
userContextId: 0, userContextId: 0,
_forZenEmptyTab: true, _forZenEmptyTab: true,
skipAnimation: true,
skipBackgroundNotify: true,
bulkOrderedOpen: true,
}); });
} }
@@ -371,39 +365,27 @@ class nsZenWorkspaces {
"tabbrowser-arrowscrollbox-periphery" "tabbrowser-arrowscrollbox-periphery"
); );
perifery.setAttribute("hidden", "true"); perifery.setAttribute("hidden", "true");
const tabs = gBrowser.tabContainer.allTabs; const markupTabs = gBrowser.tabContainer.allTabs;
const workspaces = this.getWorkspaces(); const workspaces = this.getWorkspaces();
const spaceElements = document.createDocumentFragment();
for (const workspace of workspaces) { 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(); gBrowser.tabContainer._invalidateCachedTabs();
} }
for (const workspace of workspaces) {
this.workspaceElement(workspace.uuid)?.checkPinsExistence();
}
perifery.setAttribute("hidden", "true"); perifery.setAttribute("hidden", "true");
this._hasInitializedTabsStrip = true;
this._fixIndicatorsNames(workspaces); this._fixIndicatorsNames(workspaces);
} }
@@ -455,15 +437,20 @@ class nsZenWorkspaces {
return this.getEssentialsSection(this.getCurrentSpaceContainerId()); return this.getEssentialsSection(this.getCurrentSpaceContainerId());
} }
#createWorkspaceTabsSection(workspace, tabs = []) { #createWorkspaceElement(workspace) {
const workspaceWrapper = document.createXULElement("zen-workspace"); const workspaceWrapper = document.createXULElement("zen-workspace");
const container = document.getElementById("tabbrowser-arrowscrollbox");
workspaceWrapper.id = workspace.uuid; workspaceWrapper.id = workspace.uuid;
if (this.activeWorkspace === workspace.uuid) { if (this.activeWorkspace === workspace.uuid) {
workspaceWrapper.active = true; 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); this.#organizeTabsToWorkspaceSections(workspace, workspaceWrapper, tabs);
workspaceWrapper.checkPinsExistence(); 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() { initializeWorkspaceNavigation() {
this.#setupAppCommandHandlers(); this.#setupAppCommandHandlers();
this.#setupSidebarHandlers(); this.#setupSidebarHandlers();
@@ -796,14 +862,33 @@ class nsZenWorkspaces {
console.error("gZenWorkspaces: Error initializing theme picker", e); console.error("gZenWorkspaces: Error initializing theme picker", e);
} }
this.#initializeTabsStripSections(); 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(); this.#initializeEmptyTab();
return (async () => {
await this.#waitForPromises();
this.#afterLoadInit(); this.#afterLoadInit();
await this.#initializeWorkspaceBookmarks(); await this.#initializeWorkspaceBookmarks();
await this.changeWorkspace(activeWorkspace, { onInit: true }); await this.changeWorkspace(activeWorkspace, { onInit: true });
this.#fixTabPositions(); this.#fixTabPositions();
this.onWindowResize(); 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._resolveInitialized();
this.#clearAnyZombieTabs(); // Dont call with await this.#clearAnyZombieTabs(); // Dont call with await
delete this._resolveInitialized; delete this._resolveInitialized;
@@ -823,14 +908,22 @@ class nsZenWorkspaces {
); );
this.updateWorkspacesChangeContextMenu(); this.updateWorkspacesChangeContextMenu();
})();
} }
async selectStartPage() { async selectStartPage() {
if (!this.workspaceEnabled || gZenUIManager.testingEnabled) { if (
!this.workspaceEnabled ||
gZenUIManager.testingEnabled ||
this.#hasSelectedStartPage
) {
return; 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 resolveSelectPromise;
let selectPromise = new Promise(resolve => { let selectPromise = new Promise(resolve => {
resolveSelectPromise = resolve; resolveSelectPromise = resolve;
@@ -921,8 +1014,12 @@ class nsZenWorkspaces {
// Wait for the next event loop to ensure that the startup focus logic by // Wait for the next event loop to ensure that the startup focus logic by
// firefox has finished doing it's thing. // firefox has finished doing it's thing.
setTimeout(() => { const focusTimer = setTimeout(() => {
if (document.documentElement.hasAttribute("zen-welcome-stage")) { if (
window.closed ||
!window.docShell ||
document.documentElement.hasAttribute("zen-welcome-stage")
) {
return; return;
} }
if (gZenVerticalTabsManager._canReplaceNewTab && shownEmptyTab) { if (gZenVerticalTabsManager._canReplaceNewTab && shownEmptyTab) {
@@ -933,6 +1030,11 @@ class nsZenWorkspaces {
gBrowser.selectedBrowser.focus(); 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 ( if (
!gZenVerticalTabsManager._canReplaceNewTab && !gZenVerticalTabsManager._canReplaceNewTab &&
@@ -961,6 +1063,8 @@ class nsZenWorkspaces {
); );
} }
#hasSelectedStartPage = false;
handleInitialTab(tab, isEmpty) { handleInitialTab(tab, isEmpty) {
if (gZenUIManager.testingEnabled || !this.workspaceEnabled) { if (gZenUIManager.testingEnabled || !this.workspaceEnabled) {
return; return;
@@ -2552,7 +2656,9 @@ class nsZenWorkspaces {
#createWorkspaceData(name, icon, containerTabId = 0) { #createWorkspaceData(name, icon, containerTabId = 0) {
if (!this.currentWindowIsSyncing) { if (!this.currentWindowIsSyncing) {
containerTabId = containerTabId =
parseInt(gBrowser.selectedTab.getAttribute("usercontextid")) || 0; parseInt(
globalThis.gBrowser?.selectedTab?.getAttribute("usercontextid")
) || 0;
let label = let label =
ContextualIdentityService.getUserContextLabel(containerTabId) || ContextualIdentityService.getUserContextLabel(containerTabId) ||
"Default"; "Default";
-5
View File
@@ -145,10 +145,6 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
return; return;
} }
const tab = event.target; const tab = event.target;
if (this._ignoreNextTabPinnedEvent) {
delete this._ignoreNextTabPinnedEvent;
return;
}
switch (action) { switch (action) {
case "TabPinned": case "TabPinned":
tab._zenClickEventListener = this._zenClickEventListener; tab._zenClickEventListener = this._zenClickEventListener;
@@ -558,7 +554,6 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
}); });
} else { } else {
gBrowser.pinTab(tab); gBrowser.pinTab(tab);
this._ignoreNextTabPinnedEvent = true;
} }
tab.setAttribute("zenDefaultUserContextId", true); tab.setAttribute("zenDefaultUserContextId", true);
if (tab.selected) { if (tab.selected) {
+2
View File
@@ -20,6 +20,8 @@ support-files = [
["browser_folder_issue_9981.js"] ["browser_folder_issue_9981.js"]
["browser_folder_issue_15522.js"]
["browser_folder_level_checks.js"] ["browser_folder_level_checks.js"]
["browser_folder_max_subfolders.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);
});
@@ -5,6 +5,8 @@
ChromeUtils.defineESModuleGetters(this, { ChromeUtils.defineESModuleGetters(this, {
globalActions: "resource:///modules/ZenUBGlobalActions.sys.mjs", globalActions: "resource:///modules/ZenUBGlobalActions.sys.mjs",
SessionStore: "resource:///modules/sessionstore/SessionStore.sys.mjs",
TabStateFlusher: "resource:///modules/sessionstore/TabStateFlusher.sys.mjs",
UrlbarTestUtils: "resource://testing-common/UrlbarTestUtils.sys.mjs", UrlbarTestUtils: "resource://testing-common/UrlbarTestUtils.sys.mjs",
}); });
@@ -37,3 +39,29 @@ add_task(async function test_Ub_Actions_Search() {
); );
} }
}); });
add_task(async function test_Ub_Actions_Exact_Match_Ranks_First() {
// With a closed tab in history, "Reopen Closed Tab" is available too, and it
// contains "close tab". The exact label must still be the first action.
const tab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
"https://example.com"
);
await TabStateFlusher.flush(tab.linkedBrowser);
BrowserTestUtils.removeTab(tab);
await TestUtils.waitForCondition(
() => SessionStore.getClosedTabCount(window) > 0,
"Waiting for the closed tab to be recorded"
);
const closeTab = globalActions.find(a => a.command === "cmd_close");
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
waitForFocus,
value: closeTab.label,
});
let { result } = await UrlbarTestUtils.getRowAt(window, 1);
Assert.equal(result.providerName, "ZenUrlbarProviderGlobalActions");
Assert.equal(result.payload.title, closeTab.label);
SessionStore.forgetClosedTab(window, 0);
});
+9 -2
View File
@@ -18,6 +18,13 @@ const MAX_RECENT_ACTIONS = 5;
const MINIMUM_QUERY_SCORE = 92; const MINIMUM_QUERY_SCORE = 92;
const MINIMUM_PREFIXED_QUERY_SCORE = 30; const MINIMUM_PREFIXED_QUERY_SCORE = 30;
// Exact and prefix matches must outrank any fuzzy match. The fuzzy score grows
// quadratically with a run of consecutive matches, so a longer label that merely
// contains the query ("Reopen Closed Tab" for "close tab") could beat the label
// that is exactly the query.
const EXACT_MATCH_SCORE = 1_000_000;
const PREFIX_MATCH_SCORE = 100_000;
ChromeUtils.defineESModuleGetters(lazy, { ChromeUtils.defineESModuleGetters(lazy, {
UrlbarResult: "chrome://browser/content/urlbar/UrlbarResult.mjs", UrlbarResult: "chrome://browser/content/urlbar/UrlbarResult.mjs",
BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.sys.mjs", BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.sys.mjs",
@@ -298,11 +305,11 @@ export class ZenUrlbarProviderGlobalActions extends UrlbarProvider {
} }
// 1. Exact match gets the highest score. // 1. Exact match gets the highest score.
if (targetLower === queryLower) { if (targetLower === queryLower) {
return 200; return EXACT_MATCH_SCORE;
} }
// 2. Exact prefix matches are heavily prioritized. // 2. Exact prefix matches are heavily prioritized.
if (targetLower.startsWith(queryLower)) { if (targetLower.startsWith(queryLower)) {
return 100 + queryLen; return PREFIX_MATCH_SCORE + queryLen;
} }
let score = 0; let score = 0;
let queryIndex = 0; let queryIndex = 0;