feat: Stop rendering sub-layers when swaping browsers, b=no-bug, c=common

This commit is contained in:
Mr. M
2025-12-04 17:05:39 +01:00
parent 899d590329
commit 73f408c9e1
3 changed files with 65 additions and 17 deletions

View File

@@ -0,0 +1,6 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
- name: zen.window-sync.enabled
value: true

View File

@@ -68,3 +68,7 @@
pointer-events: none;
z-index: 2;
}
browser[zen-pseudo-hidden='true'] {
-moz-subtree-hidden-only-visually: 1 !important;
}

View File

@@ -2,14 +2,19 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
import { XPCOMUtils } from 'resource://gre/modules/XPCOMUtils.sys.mjs';
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
BrowserWindowTracker: 'resource:///modules/BrowserWindowTracker.sys.mjs',
SessionStore: 'resource:///modules/sessionstore/SessionStore.sys.mjs',
TabStateFlusher: 'resource:///modules/sessionstore/TabStateFlusher.sys.mjs',
ZenSessionStore: 'resource:///modules/zen/ZenSessionManager.sys.mjs',
});
XPCOMUtils.defineLazyPreferenceGetter(lazy, 'gWindowSyncEnabled', 'zen.window-sync.enabled');
const OBSERVING = ['browser-window-before-show'];
const EVENTS = [
'TabOpen',
@@ -93,6 +98,9 @@ class nsZenWindowSync {
};
init() {
if (!lazy.gWindowSyncEnabled) {
return;
}
for (let topic of OBSERVING) {
Services.obs.addObserver(this, topic);
}
@@ -173,13 +181,18 @@ class nsZenWindowSync {
*
* @param {Window} aWindow - The browser window to exclude.
* @param {Function} aCallback - The callback function to run on each window.
* @returns {any} The value returned by the callback function, if any.
*/
#runOnAllWindows(aWindow, aCallback) {
for (let window of this.#browserWindows) {
if (window !== aWindow) {
aCallback(window);
let value = aCallback(window);
if (value) {
return value;
}
}
}
return null;
}
observe(aSubject, aTopic) {
@@ -411,6 +424,12 @@ class nsZenWindowSync {
* @param {Object} aOtherTab - The tab in the other window.
*/
#swapBrowserDocSheellsInner(aOurTab, aOtherTab, focus = true) {
// Load about:blank
if (aOurTab.linkedBrowser?.currentURI.spec !== 'about:blank') {
aOurTab.linkedBrowser.loadURI(Services.io.newURI('about:blank'), {
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
});
}
aOurTab.ownerGlobal.gBrowser.swapBrowsersAndCloseOther(aOurTab, aOtherTab, false);
const kAttributesToRemove = ['muted', 'soundplaying', 'sharing', 'pictureinpicture'];
// swapBrowsersAndCloseOther already takes care of transferring attributes like 'muted',
@@ -424,6 +443,10 @@ class nsZenWindowSync {
aOurTab.linkedBrowser.blur();
aOurTab.ownerGlobal.gBrowser._adjustFocusAfterTabSwitch(aOurTab);
}
for (const tab of [aOurTab, aOtherTab]) {
// Ensure the tab's state is flushed after the swap.
lazy.TabStateFlusher.flush(tab.linkedBrowser);
}
}
/**
@@ -462,13 +485,11 @@ class nsZenWindowSync {
// Run a reflow to ensure the image is rendered before hiding the browser.
void img.getBoundingClientRect();
await loadPromise;
otherBrowser.style.opacity = 0;
otherBrowser.style.pointerEvents = 'none';
otherBrowser.setAttribute('zen-pseudo-hidden', 'true');
}
this.#maybeRemovePseudoImageForBrowser(ourBrowser);
ourBrowser.style.opacity = '';
ourBrowser.style.pointerEvents = '';
ourBrowser.removeAttribute('zen-pseudo-hidden');
}
/**
@@ -512,15 +533,12 @@ class nsZenWindowSync {
* @returns {Object|null} The active tab from other windows if found, otherwise null.
*/
#getActiveTabFromOtherWindows(aWindow, aTabId, filter = (tab) => tab?._zenContentsVisible) {
for (let window of this.#browserWindows) {
if (window !== aWindow) {
const tab = this.#getItemFromWindow(window, aTabId);
if (filter(tab)) {
return tab;
}
return this.#runOnAllWindows(aWindow, (win) => {
const tab = this.#getItemFromWindow(win, aTabId);
if (filter(tab)) {
return tab;
}
}
return null;
});
}
/**
@@ -555,7 +573,7 @@ class nsZenWindowSync {
* @param {Window} aWindow - The window that triggered the event.
* @param {Object} aPreviousTab - The previously selected tab.
*/
onTabSwitchOrWindowFocus(aWindow, aPreviousTab = null) {
#onTabSwitchOrWindowFocus(aWindow, aPreviousTab = null) {
const selectedTab = aWindow.gBrowser.selectedTab;
if (aPreviousTab?._zenContentsVisible) {
const otherTabToShow = this.#getActiveTabFromOtherWindows(
@@ -591,6 +609,26 @@ class nsZenWindowSync {
this.#syncItemForAllWindows(item, flags);
}
/* Mark: Public API */
shouldLoadTab(aTab) {
if (!lazy.gWindowSyncEnabled) {
// Since we are never going to sync the tab, we can always load it.
return true;
}
if (aTab._zenContentsVisible) {
// This tab is already active in this window.
return true;
}
// We don't want to trigger a new browser kick-off if there's
// another window where this tab is already active.
return !this.#getActiveTabFromOtherWindows(
aTab.ownerGlobal,
aTab.id,
(tab) => tab?._zenContentsVisible
);
}
/* Mark: Event Handlers */
on_TabOpen(aEvent) {
@@ -661,12 +699,12 @@ class nsZenWindowSync {
on_focus(aEvent) {
const { ownerGlobal: window } = aEvent.target;
if (this.#lastFocusedWindow?.deref() === window) {
if (!window.gBrowser || this.#lastFocusedWindow?.deref() === window) {
return;
}
this.#lastFocusedWindow = new WeakRef(window);
this.#lastSelectedTab = new WeakRef(window.gBrowser.selectedTab);
this.onTabSwitchOrWindowFocus(window);
this.#onTabSwitchOrWindowFocus(window);
}
on_TabSelect(aEvent) {
@@ -676,7 +714,7 @@ class nsZenWindowSync {
}
this.#lastSelectedTab = new WeakRef(tab);
const previousTab = aEvent.detail.previousTab;
this.onTabSwitchOrWindowFocus(aEvent.target.ownerGlobal, previousTab);
this.#onTabSwitchOrWindowFocus(aEvent.target.ownerGlobal, previousTab);
}
on_unload(aEvent) {