From 8ea65cba48263a7d655cb0aceeaea1b28648a7c5 Mon Sep 17 00:00:00 2001 From: "mr. m" <91018726+mr-cheffy@users.noreply.github.com> Date: Mon, 1 Jun 2026 12:24:16 +0200 Subject: [PATCH] gh-12112: Fixed auto focus for new windows not working (gh-13976) --- .github/workflows/sync-upstream.yml | 2 +- .../urlbar/content/UrlbarInput-mjs.patch | 2 +- src/browser/themes/shared/zen-icons/icons.css | 3 +- .../common/styles/zen-single-components.css | 4 +- src/zen/spaces/ZenSpaceManager.mjs | 1 + src/zen/tests/spaces/browser.toml | 2 + src/zen/tests/spaces/browser_issue_10455.js | 27 ++++-- src/zen/tests/spaces/browser_issue_12112.js | 84 +++++++++++++++++++ src/zen/tests/spaces/browser_private_mode.js | 10 ++- src/zen/tests/urlbar/browser.toml | 1 + src/zen/tests/urlbar/browser_issue_7385.js | 31 ++++--- .../browser_single_toolbar_blur_revert.js | 82 ++++++++++++++++++ 12 files changed, 221 insertions(+), 28 deletions(-) create mode 100644 src/zen/tests/spaces/browser_issue_12112.js create mode 100644 src/zen/tests/urlbar/browser_single_toolbar_blur_revert.js diff --git a/.github/workflows/sync-upstream.yml b/.github/workflows/sync-upstream.yml index e4bca9156..e2662de21 100644 --- a/.github/workflows/sync-upstream.yml +++ b/.github/workflows/sync-upstream.yml @@ -129,7 +129,7 @@ jobs: token: ${{ secrets.DEPLOY_KEY }} commit-message: "chore: Sync upstream to `Firefox ${{ steps.build-data.outputs.version }}`" branch: "chore/upstream-sync" - title: "no-bug: Sync upstream Firefox to version ${{ steps.build-data.outputs.version }}" + title: "no-bug: Sync upstream Firefox to version `${{ steps.build-data.outputs.version }}`" body: | This PR syncs the upstream Firefox to version ${{ steps.build-data.outputs.version }}. diff --git a/src/browser/components/urlbar/content/UrlbarInput-mjs.patch b/src/browser/components/urlbar/content/UrlbarInput-mjs.patch index f4e335a6d..f33a99404 100644 --- a/src/browser/components/urlbar/content/UrlbarInput-mjs.patch +++ b/src/browser/components/urlbar/content/UrlbarInput-mjs.patch @@ -138,7 +138,7 @@ index d6615ec5a29f3e3327ac4171f3fc5d9a69bd09fe..c166b7de23c35716bf8c51b6b9c72f77 + if (this._zenHandleUrlbarClose) { + this._zenHandleUrlbarClose(); -+ } else if (!this._untrimmedValue || this.searchMode) { ++ } else if (!this._untrimmedValue || this.searchMode || this.window.gZenVerticalTabsManager._hasSetSingleToolbar) { + // Restore the current page URL when the urlbar is empty on blur + this.window.setTimeout(() => { + this.handleRevert(); diff --git a/src/browser/themes/shared/zen-icons/icons.css b/src/browser/themes/shared/zen-icons/icons.css index ed143a42b..bf563df95 100644 --- a/src/browser/themes/shared/zen-icons/icons.css +++ b/src/browser/themes/shared/zen-icons/icons.css @@ -520,7 +520,8 @@ list-style-image: url("permissions-fill.svg"); } &[boosting] image { - color: var(--color-accent-primary); + fill-opacity: 1 !important; + color: var(--zen-sidebar-themed-icon-fill); list-style-image: url("permissions-fill.svg"); } diff --git a/src/zen/common/styles/zen-single-components.css b/src/zen/common/styles/zen-single-components.css index 02a2d64d2..d9687232d 100644 --- a/src/zen/common/styles/zen-single-components.css +++ b/src/zen/common/styles/zen-single-components.css @@ -495,7 +495,7 @@ border-radius: 99px; width: var(--size-item-large); height: var(--size-item-large); - background: var(--button-background-color-primary); + background: var(--zen-sidebar-themed-icon-fill); opacity: 0.6; transition: transform 0.12s ease-in-out, @@ -625,7 +625,7 @@ color: var(--button-primary-color); &::before { - background: var(--button-background-color-primary); + background: var(--zen-sidebar-themed-icon-fill); } } diff --git a/src/zen/spaces/ZenSpaceManager.mjs b/src/zen/spaces/ZenSpaceManager.mjs index 36ca9c50e..bad548890 100644 --- a/src/zen/spaces/ZenSpaceManager.mjs +++ b/src/zen/spaces/ZenSpaceManager.mjs @@ -861,6 +861,7 @@ class nsZenWorkspaces { "Selecting empty tab because startup page didnt select a valid tab" ); this.selectEmptyTab(); + initialTabWasEmpty = true; } this.log("Removing empty tab added by startup page"); this._removedByStartupPage = true; diff --git a/src/zen/tests/spaces/browser.toml b/src/zen/tests/spaces/browser.toml index deacec1f7..99c7db003 100644 --- a/src/zen/tests/spaces/browser.toml +++ b/src/zen/tests/spaces/browser.toml @@ -14,6 +14,8 @@ support-files = [ ["browser_issue_10455.js"] +["browser_issue_12112.js"] + ["browser_issue_8699.js"] ["browser_issue_9900.js"] diff --git a/src/zen/tests/spaces/browser_issue_10455.js b/src/zen/tests/spaces/browser_issue_10455.js index ed7644d39..962d99dfa 100644 --- a/src/zen/tests/spaces/browser_issue_10455.js +++ b/src/zen/tests/spaces/browser_issue_10455.js @@ -4,16 +4,24 @@ "use strict"; add_task(async function test_Issue_10455() { - debugger; await SpecialPowers.pushPrefEnv({ - set: [["browser.tabs.closeWindowWithLastTab", true]], + set: [ + ["browser.tabs.closeWindowWithLastTab", true], + ["zen.testing.enabled", false], + ["zen.window-sync.enabled", false], + ], }); - debugger; let newWindow = await BrowserTestUtils.openNewBrowserWindow(); await newWindow.gZenWorkspaces.promiseInitialized; const unloadEvent = BrowserTestUtils.waitForEvent(newWindow, "unload"); + Assert.equal( + newWindow.gBrowser.tabs.length, + 3, + "New window should have three tabs" + ); + newWindow.BrowserCommands.closeTabOrWindow(); newWindow.BrowserCommands.closeTabOrWindow(); await unloadEvent; @@ -22,14 +30,23 @@ add_task(async function test_Issue_10455() { }); add_task(async function test_Issue_10455_Dont_Close() { - debugger; await SpecialPowers.pushPrefEnv({ - set: [["browser.tabs.closeWindowWithLastTab", false]], + set: [ + ["browser.tabs.closeWindowWithLastTab", false], + ["zen.testing.enabled", false], + ["zen.window-sync.enabled", false], + ], }); let newWindow = await BrowserTestUtils.openNewBrowserWindow(); await newWindow.gZenWorkspaces.promiseInitialized; + Assert.equal( + newWindow.gBrowser.tabs.length, + 3, + "New window should have three tabs" + ); + newWindow.BrowserCommands.closeTabOrWindow(); newWindow.BrowserCommands.closeTabOrWindow(); Assert.strictEqual( newWindow.gBrowser.tabs.length, diff --git a/src/zen/tests/spaces/browser_issue_12112.js b/src/zen/tests/spaces/browser_issue_12112.js new file mode 100644 index 000000000..c17218065 --- /dev/null +++ b/src/zen/tests/spaces/browser_issue_12112.js @@ -0,0 +1,84 @@ +/* Any copyright is dedicated to the Public Domain. + https://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_setup(async function () { + await SpecialPowers.pushPrefEnv({ + set: [["zen.urlbar.replace-newtab", false]], + }); + registerCleanupFunction(async () => { + await SpecialPowers.popPrefEnv(); + }); +}); + +add_task( + async function test_focuses_urlbar_on_startup_without_replace_newtab() { + await gZenWorkspaces.promiseInitialized; + Assert.ok( + !gZenVerticalTabsManager._canReplaceNewTab, + "Precondition: zen.urlbar.replace-newtab is disabled" + ); + + const originalTab = gBrowser.selectedTab; + const originalOpenLocation = window.openLocation; + const originalTestingEnabled = gZenUIManager.testingEnabled; + + let openLocationCalls = 0; + window.openLocation = () => { + openLocationCalls++; + }; + + // selectStartPage() and selectEmptyTab() are both no-ops while testing mode + // is enabled; temporarily disable it to exercise the real startup path. + gZenUIManager.testingEnabled = false; + + // The tab the startup page leaves selected, which Zen wants to replace. + const tabToRemove = BrowserTestUtils.addTab(gBrowser, "about:blank", { + skipAnimation: true, + }); + gBrowser.selectedTab = tabToRemove; + gZenWorkspaces._tabToRemoveForEmpty = tabToRemove; + delete gZenWorkspaces._tabToSelect; + delete gZenWorkspaces._shouldOverrideTabs; + delete gZenWorkspaces._initialTab; + + try { + await gZenWorkspaces.selectStartPage(); + + await TestUtils.waitForCondition( + () => openLocationCalls > 0, + "openLocation() should be called to focus the address bar" + ); + + Assert.equal( + openLocationCalls, + 1, + "The address bar was focused via openLocation()" + ); + Assert.ok( + !gBrowser.selectedTab.hasAttribute("zen-empty-tab"), + "A fallback homepage tab is selected (no zen-empty-tab attribute), so " + + "the focus decision came from initialTabWasEmpty, not shownEmptyTab" + ); + Assert.ok( + !gBrowser.tabs.includes(tabToRemove), + "The empty tab added by the startup page was removed" + ); + } finally { + window.openLocation = originalOpenLocation; + gZenUIManager.testingEnabled = originalTestingEnabled; + delete gZenWorkspaces._tabToRemoveForEmpty; + + // Remove any tab created by the startup path, then restore the original. + for (const tab of [...gBrowser.tabs]) { + if (tab !== originalTab && !tab.hasAttribute("zen-empty-tab")) { + BrowserTestUtils.removeTab(tab); + } + } + if (!originalTab.closing) { + gBrowser.selectedTab = originalTab; + } + } + } +); diff --git a/src/zen/tests/spaces/browser_private_mode.js b/src/zen/tests/spaces/browser_private_mode.js index e2c0e19fd..b37677d6d 100644 --- a/src/zen/tests/spaces/browser_private_mode.js +++ b/src/zen/tests/spaces/browser_private_mode.js @@ -5,7 +5,10 @@ add_task(async function test_Private_Mode() { await SpecialPowers.pushPrefEnv({ - set: [["privacy.userContext.enabled", true]], + set: [ + ["privacy.userContext.enabled", true], + ["zen.testing.enabled", false], + ], }); let privateWindow = await BrowserTestUtils.openNewBrowserWindow({ @@ -13,6 +16,11 @@ add_task(async function test_Private_Mode() { }); await privateWindow.gZenWorkspaces.promiseInitialized; + Assert.ok( + privateWindow.gBrowser.selectedTab.hasAttribute("zen-empty-tab"), + "Private window should start with a zen empty tab" + ); + await BrowserTestUtils.closeWindow(privateWindow); await SpecialPowers.popPrefEnv(); }); diff --git a/src/zen/tests/urlbar/browser.toml b/src/zen/tests/urlbar/browser.toml index 4f7b7194e..e2c4bbb40 100644 --- a/src/zen/tests/urlbar/browser.toml +++ b/src/zen/tests/urlbar/browser.toml @@ -3,6 +3,7 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. [DEFAULT] +prefs = ["browser.urlbar.closeOnWindowBlur=false"] support-files = [ "head.js", "!/browser/components/urlbar/tests/browser/head.js", diff --git a/src/zen/tests/urlbar/browser_issue_7385.js b/src/zen/tests/urlbar/browser_issue_7385.js index b77187e9d..37c3c7f7c 100644 --- a/src/zen/tests/urlbar/browser_issue_7385.js +++ b/src/zen/tests/urlbar/browser_issue_7385.js @@ -9,27 +9,24 @@ ChromeUtils.defineESModuleGetters(this, { add_task(async function test_Selection_Remains_Double_Toolbar() { await goToMultipleLayouts(async () => { - const untrimmedValue = "https://example.com"; + const untrimmedValue = "https://example.com/"; let trimmedValue = UrlbarTestUtils.trimURL(untrimmedValue); - gURLBar._setValue(untrimmedValue, { - allowTrim: true, - valueIsTyped: false, - }); - gURLBar.blur(); await SimpleTest.promiseFocus(window); - Assert.equal(gURLBar.value, trimmedValue, "Value has been trimmed"); - await selectWithMouseDrag(100, 200); + await BrowserTestUtils.withNewTab(untrimmedValue, async () => { + Assert.equal(gURLBar.value, trimmedValue, "Value has been trimmed"); + await selectWithMouseDrag(10, 20); - Assert.greater(gURLBar.selectionStart, 0, "Selection start is positive."); - Assert.greater( - gURLBar.selectionEnd, - gURLBar.selectionStart, - "Selection is not empty." - ); + Assert.greater(gURLBar.selectionStart, 0, "Selection start is positive."); + Assert.greater( + gURLBar.selectionEnd, + gURLBar.selectionStart, + "Selection is not empty." + ); - Assert.equal(gURLBar.value, untrimmedValue, `Value should be untrimmed`); + Assert.equal(gURLBar.value, untrimmedValue, `Value should be untrimmed`); - gURLBar.handleRevert(); - gURLBar.view.close(); + gURLBar.handleRevert(); + gURLBar.view.close(); + }); }); }); diff --git a/src/zen/tests/urlbar/browser_single_toolbar_blur_revert.js b/src/zen/tests/urlbar/browser_single_toolbar_blur_revert.js new file mode 100644 index 000000000..f35dadf64 --- /dev/null +++ b/src/zen/tests/urlbar/browser_single_toolbar_blur_revert.js @@ -0,0 +1,82 @@ +/* Any copyright is dedicated to the Public Domain. + https://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +ChromeUtils.defineESModuleGetters(this, { + UrlbarTestUtils: "resource://testing-common/UrlbarTestUtils.sys.mjs", +}); + +const PAGE_URL = "https://example.com/"; +const TYPED_VALUE = "zen blur revert test"; + +async function typeIntoUrlbar() { + await UrlbarTestUtils.promiseAutocompleteResultPopup({ + window, + value: TYPED_VALUE, + fireInputEvent: true, + }); + Assert.equal( + gURLBar.value, + TYPED_VALUE, + "The typed value is present while the address bar is focused" + ); +} + +add_task(async function test_single_toolbar_reverts_typed_value_on_blur() { + await TestUtils.waitForCondition( + () => gZenVerticalTabsManager._hasSetSingleToolbar, + "The default layout should be single-toolbar" + ); + + await BrowserTestUtils.withNewTab(PAGE_URL, async () => { + await typeIntoUrlbar(); + + await UrlbarTestUtils.promisePopupClose(window, () => gURLBar.blur()); + await SimpleTest.promiseFocus(window); + await new Promise(resolve => setTimeout(resolve)); + + await TestUtils.waitForCondition( + () => gURLBar.value !== TYPED_VALUE, + "The address bar should revert away from the typed value on blur" + ); + + Assert.ok( + gURLBar.value.includes("example.com"), + `Reverted to the page URL (got "${gURLBar.value}")` + ); + Assert.notEqual( + gURLBar.value, + TYPED_VALUE, + "Single-toolbar blur did not retain the typed value" + ); + }); + + gURLBar.handleRevert(); +}); + +add_task(async function test_double_toolbar_keeps_typed_value_on_blur() { + await SpecialPowers.pushPrefEnv({ + set: [["zen.view.use-single-toolbar", false]], + }); + await TestUtils.waitForCondition( + () => !gZenVerticalTabsManager._hasSetSingleToolbar, + "The layout should switch to double-toolbar" + ); + + await BrowserTestUtils.withNewTab(PAGE_URL, async () => { + await typeIntoUrlbar(); + + await UrlbarTestUtils.promisePopupClose(window, () => gURLBar.blur()); + await SimpleTest.promiseFocus(window); + + Assert.equal( + gURLBar.value, + TYPED_VALUE, + "Double-toolbar blur keeps the typed value (no forced revert)" + ); + }); + + gURLBar.handleRevert(); + await SpecialPowers.popPrefEnv(); +});