From 47181da49e18f8822ae95566c227c986106f0520 Mon Sep 17 00:00:00 2001 From: "mr. m" <91018726+mr-cheffy@users.noreply.github.com> Date: Sat, 27 Sep 2025 19:04:59 +0200 Subject: [PATCH] feat: Improved startup performance and flashes, p=#10588, c=common, tabs, workspaces --- src/zen/ZenComponents.manifest | 30 +++++++++ src/zen/common/ZenStartup.mjs | 36 +++++------ src/zen/moz.build | 4 ++ src/zen/tabs/zen-tabs/vertical-tabs.css | 4 +- src/zen/workspaces/ZenWorkspaces.mjs | 84 ++++++++++++------------- 5 files changed, 91 insertions(+), 67 deletions(-) create mode 100644 src/zen/ZenComponents.manifest diff --git a/src/zen/ZenComponents.manifest b/src/zen/ZenComponents.manifest new file mode 100644 index 000000000..bb5094206 --- /dev/null +++ b/src/zen/ZenComponents.manifest @@ -0,0 +1,30 @@ +# nsBrowserGlue.js + +# This component must restrict its registration for the app-startup category +# to the specific list of apps that use it so it doesn't get loaded in xpcshell. +# Thus we restrict it to these apps: +# +# browser: {ec8030f7-c20a-464f-9b0e-13a3a9e97384} +# +# The first rule of running code during startup is: don't. +# +# We take performance very seriously and ideally your component/feature should +# initialize only when needed. +# +# If you have established that you really must run code during startup, +# available entrypoints are: +# +# - registering a `browser-idle-startup` category entry for your JS module (or +# even a "best effort" user idle task, see `BrowserGlue.sys.mjs`) +# - registering a `browser-window-delayed-startup` category entry for your JS +# module. **Note that this is invoked for each browser window.** +# - registering a `browser-before-ui-startup` category entry if you really really +# need to. This will run code before the first browser window appears on the +# screen and make Firefox seem slow, so please don't do it unless absolutely +# necessary. + +#ifdef XP_UNIX + #ifndef XP_MACOSX + #define UNIX_BUT_NOT_MAC + #endif +#endif diff --git a/src/zen/common/ZenStartup.mjs b/src/zen/common/ZenStartup.mjs index 3fa3d061c..ad4c4e775 100644 --- a/src/zen/common/ZenStartup.mjs +++ b/src/zen/common/ZenStartup.mjs @@ -2,22 +2,25 @@ // 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/. { - var gZenStartup = new (class { + class ZenStartupManager { #watermarkIgnoreElements = ['zen-toast-container']; #hasInitializedLayout = false; isReady = false; - async init() { - // important: We do this to ensure that some firefox components - // are initialized before we start our own initialization. - // please, do not remove this line and if you do, make sure to - // test the startup process. - await new Promise((resolve) => setTimeout(resolve, 0)); - this.openWatermark(); - this.#initBrowserBackground(); - this.#changeSidebarLocation(); - this.#zenInitBrowserLayout(); + constructor() { + gZenWorkspaces.init(); + + window.addEventListener( + 'MozBeforeInitialXULLayout', + () => { + this.openWatermark(); + this.#zenInitBrowserLayout(); + this.#initBrowserBackground(); + this.#changeSidebarLocation(); + }, + { once: true } + ); } #initBrowserBackground() { @@ -57,7 +60,6 @@ document.getElementById('zen-appcontent-wrapper').prepend(deckTemplate); } - gZenWorkspaces.init(); setTimeout(() => { gZenUIManager.init(); this.#checkForWelcomePage(); @@ -223,13 +225,7 @@ }); }); } - })(); + } - window.addEventListener( - 'MozBeforeInitialXULLayout', - () => { - gZenStartup.init(); - }, - { once: true } - ); + window.gZenStartup = new ZenStartupManager(); } diff --git a/src/zen/moz.build b/src/zen/moz.build index 56782122f..ac8965447 100644 --- a/src/zen/moz.build +++ b/src/zen/moz.build @@ -10,3 +10,7 @@ DIRS += [ "urlbar", "toolkit", ] + +EXTRA_PP_COMPONENTS += [ + "ZenComponents.manifest", +] diff --git a/src/zen/tabs/zen-tabs/vertical-tabs.css b/src/zen/tabs/zen-tabs/vertical-tabs.css index 0012ebfd6..1a7cb15e6 100644 --- a/src/zen/tabs/zen-tabs/vertical-tabs.css +++ b/src/zen/tabs/zen-tabs/vertical-tabs.css @@ -418,13 +418,13 @@ overflow-y: auto; height: 100%; - :root[zen-workspace-id][zen-sidebar-expanded='true'] & { + :root[zen-sidebar-expanded='true'] & { margin-left: calc(-1 * var(--zen-toolbox-padding)); width: calc(100% + var(--zen-toolbox-padding) * 2); } } -:root[zen-workspace-id] #pinned-tabs-container { +#pinned-tabs-container { display: none; } diff --git a/src/zen/workspaces/ZenWorkspaces.mjs b/src/zen/workspaces/ZenWorkspaces.mjs index ea881aeff..1fb1476c9 100644 --- a/src/zen/workspaces/ZenWorkspaces.mjs +++ b/src/zen/workspaces/ZenWorkspaces.mjs @@ -51,7 +51,7 @@ var gZenWorkspaces = new (class extends nsZenMultiWindowFeature { await Promise.all([this.promiseDBInitialized, this.promisePinnedInitialized]); } - async init() { + init() { // Initialize tab selection state this._tabSelectionState = { inProgress: false, @@ -118,12 +118,8 @@ var gZenWorkspaces = new (class extends nsZenMultiWindowFeature { this.popupOpenHandler = this._popupOpenHandler.bind(this); window.addEventListener('resize', this.onWindowResize.bind(this)); - this.addPopupListeners(); - await this.#waitForPromises(); - await this._workspaces(); - - await this.afterLoadInit(); + this.afterLoadInit(); } log(...args) { @@ -136,11 +132,13 @@ var gZenWorkspaces = new (class extends nsZenMultiWindowFeature { if (!this._hasInitializedTabsStrip) { await this.delayedStartup(); } - this._initializeWorkspaceTabContextMenus(); + this.#initializeWorkspaceTabContextMenus(); await this.initializeWorkspaces(); await this.promiseSectionsInitialized; // Non UI related initializations + this.addPopupListeners(); + if ( Services.prefs.getBoolPref('zen.workspaces.swipe-actions', false) && this.workspaceEnabled && @@ -311,6 +309,7 @@ var gZenWorkspaces = new (class extends nsZenMultiWindowFeature { } async _createDefaultWorkspaceIfNeeded() { + await this.#waitForPromises(); const workspaces = await this._workspaces(); if (!workspaces.workspaces.length) { await this.createAndSaveWorkspace('Space', null, true); @@ -401,46 +400,42 @@ var gZenWorkspaces = new (class extends nsZenMultiWindowFeature { async initializeTabsStripSections() { await SessionStore.promiseInitialized; await SessionStore.promiseAllWindowsRestored; + await gZenSessionStore.promiseInitialized; const perifery = document.getElementById('tabbrowser-arrowscrollbox-periphery'); perifery.setAttribute('hidden', 'true'); - await new Promise((resolve) => { - setTimeout(async () => { - const tabs = gBrowser.tabContainer.allTabs; - const workspaces = await this._workspaces(); - for (const workspace of workspaces.workspaces) { - await this._createWorkspaceTabsSection(workspace, tabs); - } - 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); - } + const tabs = gBrowser.tabContainer.allTabs; + const workspaces = await this._workspaces(); + for (const workspace of workspaces.workspaces) { + await this._createWorkspaceTabsSection(workspace, tabs); + } + 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; } - gBrowser.tabContainer._invalidateCachedTabs(); + // before to the last child (perifery) + defaultSelectedContainer.insertBefore(tab, defaultSelectedContainer.lastChild); } - perifery.setAttribute('hidden', 'true'); - this._hasInitializedTabsStrip = true; - this.registerPinnedResizeObserver(); - this._fixIndicatorsNames(workspaces); - this._resolveSectionsInitialized(); - resolve(); - }, 0); - }); + } + gBrowser.tabContainer._invalidateCachedTabs(); + } + perifery.setAttribute('hidden', 'true'); + this._hasInitializedTabsStrip = true; + this.registerPinnedResizeObserver(); + this._fixIndicatorsNames(workspaces); + this._resolveSectionsInitialized(); } getEssentialsSection(container = 0) { @@ -920,7 +915,6 @@ var gZenWorkspaces = new (class extends nsZenMultiWindowFeature { async initializeWorkspaces() { let activeWorkspace = await this.getActiveWorkspace(); this.activeWorkspace = activeWorkspace?.uuid; - await gZenSessionStore.promiseInitialized; try { if (activeWorkspace) { window.gZenThemePicker = new nsZenThemePicker(); @@ -2660,7 +2654,7 @@ var gZenWorkspaces = new (class extends nsZenMultiWindowFeature { await this.changeWorkspace(nextWorkspace, { whileScrolling }); } - _initializeWorkspaceTabContextMenus() { + #initializeWorkspaceTabContextMenus() { if (this.privateWindowOrDisabled) { const commandsToDisable = [ 'cmd_zenOpenFolderCreation',