gh-14956: Check if the closing tab exists on the tab list before switching (gh-15312)

This commit is contained in:
mr. m
2026-09-09 13:13:40 +02:00
committed by mr. m
parent 5025b9056d
commit 2cfa61b633
13 changed files with 71 additions and 48 deletions

View File

@@ -23,7 +23,6 @@ jobs:
fail-fast: false
matrix:
arch: [x86_64, aarch64]
runs-on: ${{ matrix.arch == 'x86_64' && 'macos-15-intel' || 'macos-26' }}
steps:

View File

@@ -37,6 +37,8 @@ bash ./scripts/mar_sign.sh -i
ulimit -n 4096
export ZEN_DISABLE_BOOTSTRAP=1
if command -v Xvfb &> /dev/null; then
if ! test "$ZEN_CROSS_COMPILING"; then
Xvfb :2 -nolisten tcp -noreset -screen 0 1024x768x24 &

View File

@@ -27,7 +27,6 @@ jobs:
fail-fast: false
matrix:
arch: [x86_64, aarch64]
runs-on: ${{ inputs.release-branch == 'release' && 'windows-latest' || 'windows-latest' }}
steps:

View File

@@ -53,7 +53,10 @@ fi
if test "$ZEN_RELEASE"; then
ac_add_options --enable-clang-plugin
ac_add_options --enable-bootstrap=-sccache
if ! test "$ZEN_DISABLE_BOOTSTRAP"; then
ac_add_options --enable-bootstrap=-sccache
fi
ac_add_options --enable-optimize
@@ -69,16 +72,11 @@ if test "$ZEN_RELEASE"; then
ac_add_options --disable-geckodriver
ac_add_options --disable-rust-tests
if ! test "$ZEN_DISABLE_LTO"; then
# only enable full LTO when ZEN_RELEASE_BRANCH is 'release'
if test "$ZEN_RELEASE_BRANCH" = "release"; then
# TODO: make it "full" once we have the resources to build it
export MOZ_LTO=cross,full
ac_add_options --enable-lto=cross,full
else
export MOZ_LTO=cross,thin
ac_add_options --enable-lto=cross,thin
fi
if test "$ZEN_DISABLE_LTO"; then
ac_add_options --disable-lto
else
export MOZ_LTO=cross,thin
ac_add_options --enable-lto=cross,thin
fi
mk_add_options MOZILLA_OFFICIAL=1

View File

@@ -9,6 +9,9 @@ if test "$ZEN_RELEASE"; then
# Enable Profile Guided Optimization
if ! test "$ZEN_GA_DISABLE_PGO"; then
export MOZ_LTO=cross,full
ac_add_options --enable-lto=cross,full
if test "$ZEN_GA_GENERATE_PROFILE"; then
mk_add_options "export MOZ_AUTOMATION_PACKAGE_GENERATED_SOURCES=0"
ac_add_options --enable-profile-generate=cross

View File

@@ -45,6 +45,9 @@ if test "$ZEN_CROSS_COMPILING"; then
ac_add_options --enable-profile-use=cross
ac_add_options --with-pgo-profile-path=$(echo ~)/artifact/merged.profdata
ac_add_options --with-pgo-jarlog=$(echo ~)/artifact/en-US.log
export MOZ_LTO=cross,full
ac_add_options --enable-lto=cross,full
fi
fi
fi

View File

@@ -741,7 +741,6 @@ var gZenWorkspacesSettings = {
Services.prefs.addObserver("zen.glance.enabled", tabsUnloaderPrefListener); // We can use the same listener for both prefs
Services.prefs.addObserver("zen.workspaces.separate-essentials", tabsUnloaderPrefListener);
Services.prefs.addObserver("zen.glance.activation-method", tabsUnloaderPrefListener);
Services.prefs.addObserver("zen.window-sync.sync-only-pinned-tabs", tabsUnloaderPrefListener);
Services.prefs.addObserver(
"zen.tabs.ctrl-tab.ignore-essential-tabs",
@@ -750,7 +749,6 @@ var gZenWorkspacesSettings = {
Services.prefs.addObserver("browser.ctrlTab.sortByRecentlyUsed", toggleZenCycleByAttrWarning);
window.addEventListener("unload", () => {
Services.prefs.removeObserver("zen.glance.enabled", tabsUnloaderPrefListener);
Services.prefs.removeObserver("zen.glance.activation-method", tabsUnloaderPrefListener);
Services.prefs.removeObserver("zen.workspaces.separate-essentials", tabsUnloaderPrefListener);
Services.prefs.removeObserver(
"zen.window-sync.sync-only-pinned-tabs",

View File

@@ -751,7 +751,7 @@ index 43180487a2fac15b7a65fac94f233eeed9094990..ef6d30eb681caeae48d15280687b3bc3
}
/**
@@ -6422,6 +6618,7 @@
@@ -6422,11 +6618,12 @@
}
let excludeTabs = new Set(aExcludeTabs);
@@ -759,6 +759,12 @@ index 43180487a2fac15b7a65fac94f233eeed9094990..ef6d30eb681caeae48d15280687b3bc3
// If this tab has a successor, it should be selectable, since
// hiding or closing a tab removes that tab as a successor.
if (aTab.successor && !excludeTabs.has(aTab.successor)) {
- return aTab.successor;
+ return gZenWorkspaces.findTabToBlur(aTab.successor);
}
if (
@@ -6434,13 +6631,13 @@
!excludeTabs.has(aTab.owner) &&
Services.prefs.getBoolPref("browser.tabs.selectOwnerOnClose")

View File

@@ -70,8 +70,10 @@ export class ZenBoostsChild extends JSWindowActorChild {
static PREVENTABLE_SET = new Set(ZenBoostsChild.PREVENTABLE_EVENTS);
actorCreated() {
this.#applyBoostForPageIfAvailable();
handleEvent(event) {
if (event.type === "DOMDocElementInserted") {
this.#applyBoostForPageIfAvailable();
}
}
didDestroy() {
@@ -330,6 +332,22 @@ export class ZenBoostsChild extends JSWindowActorChild {
return this.sendQuery("ZenBoost:GetBoostForDomain", domain);
}
/**
* Sets a synced BrowsingContext field only when its value changes. Every
* setter commits a transaction that is broadcast to the whole browsing
* context group, even when the value is unchanged, so this avoids three
* such broadcasts on every page load that has no boost.
*
* @param {BrowsingContext} browsingContext
* @param {string} field - The synced field name.
* @param {number | boolean} value
*/
#setSyncedField(browsingContext, field, value) {
if (browsingContext[field] !== value) {
browsingContext[field] = value;
}
}
/**
* Applies the boost settings for the current page if available.
*
@@ -360,7 +378,11 @@ export class ZenBoostsChild extends JSWindowActorChild {
sizeOverride: boostData.sizeOverride,
});
browsingContext.isZenBoostsInverted = boostData.smartInvert;
this.#setSyncedField(
browsingContext,
"isZenBoostsInverted",
!!boostData.smartInvert
);
if (boostData.enableColorBoost) {
let primaryColor;
if (boostData.autoTheme) {
@@ -396,18 +418,21 @@ export class ZenBoostsChild extends JSWindowActorChild {
boostData
);
}
browsingContext.zenBoostsData = primaryColor;
this.#setSyncedField(browsingContext, "zenBoostsData", primaryColor);
// The complementary accent is derived in the backend by rotating the
// primary accent's hue by this delta (in degrees).
browsingContext.zenBoostsComplementaryRotation =
boostData.secondaryDotAngleDegDelta ?? 0;
this.#setSyncedField(
browsingContext,
"zenBoostsComplementaryRotation",
boostData.secondaryDotAngleDegDelta ?? 0
);
return;
}
} else {
browsingContext.isZenBoostsInverted = false;
this.#setSyncedField(browsingContext, "isZenBoostsInverted", false);
}
browsingContext.zenBoostsData = 0;
browsingContext.zenBoostsComplementaryRotation = 0;
this.#setSyncedField(browsingContext, "zenBoostsData", 0);
this.#setSyncedField(browsingContext, "zenBoostsComplementaryRotation", 0);
}
/**

View File

@@ -42,7 +42,6 @@ let JSWINDOWACTORS = {
child: {
esModuleURI: "resource:///actors/ZenGlanceChild.sys.mjs",
events: {
DOMContentLoaded: {},
mousedown: {
capture: true,
},
@@ -86,9 +85,7 @@ if (!Services.appinfo.inSafeMode) {
child: {
esModuleURI: "resource:///actors/ZenBoostsChild.sys.mjs",
events: {
// Needed to let the actor be created, please don't remove
// without checking if boosts still work without it, thanks <3
DOMWindowCreated: {},
DOMDocElementInserted: {},
},
},
safeForUntrustedWebProcess: true,

View File

@@ -17,12 +17,18 @@ XPCOMUtils.defineLazyPreferenceGetter(
true
);
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"activationMethod",
"zen.glance.activation-method",
"ctrl"
);
// A small threshold to allow for minor mouse jitter during a normal click.
// Anything beyond this is likely an intentional drag (like selecting text).
const CLICK_DRAG_THRESHOLD_PX = 4;
export class ZenGlanceChild extends JSWindowActorChild {
#activationMethod;
#mouseDownX = null;
#mouseDownY = null;
@@ -37,12 +43,6 @@ export class ZenGlanceChild extends JSWindowActorChild {
}
}
async #initActivationMethod() {
this.#activationMethod = await this.sendQuery(
"ZenGlance:GetActivationMethod"
);
}
#ensureOnlyKeyModifiers(event) {
return !(event.ctrlKey ^ event.altKey ^ event.shiftKey ^ event.metaKey);
}
@@ -156,7 +156,7 @@ export class ZenGlanceChild extends JSWindowActorChild {
) {
return;
}
const activationMethod = this.#activationMethod;
const activationMethod = lazy.activationMethod;
if (activationMethod === "ctrl" && !event.ctrlKey) {
return;
} else if (activationMethod === "alt" && !event.altKey) {
@@ -184,8 +184,4 @@ export class ZenGlanceChild extends JSWindowActorChild {
this.contentWindow.document.body,
});
}
async on_DOMContentLoaded() {
await this.#initActivationMethod();
}
}

View File

@@ -11,12 +11,6 @@ export class ZenGlanceParent extends JSWindowActorParent {
async receiveMessage(message) {
switch (message.name) {
case "ZenGlance:GetActivationMethod": {
return Services.prefs.getStringPref(
"zen.glance.activation-method",
"ctrl"
);
}
case "ZenGlance:OpenGlance": {
this.openGlance(this.browsingContext.topChromeWindow, message.data);
break;

View File

@@ -2911,7 +2911,10 @@ class nsZenWorkspaces {
}
findTabToBlur(tab) {
if ((!this._shouldChangeToTab(tab) || !tab) && this._emptyTab) {
if (
(!tab || !this._shouldChangeToTab(tab) || !gBrowser.tabs.includes(tab)) &&
this._emptyTab
) {
return this._emptyTab;
}
return tab;