mirror of
https://github.com/zen-browser/desktop.git
synced 2026-09-25 13:38:18 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
44552e5583 | ||
|
|
a92282dec4 | ||
|
|
7dfcd34fc5 | ||
|
|
22604abe11 |
@@ -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…
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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 => {
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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++;
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
});
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user