From 4b4a659c7bc1931ec810254386c89e313800ae3d Mon Sep 17 00:00:00 2001 From: "mr. m" <91018726+mr-cheffy@users.noreply.github.com> Date: Fri, 18 Sep 2026 00:37:43 +0200 Subject: [PATCH] gh-15437: Fixed command actions on the urlbar not working (gh-15449) --- .../urlbar/content/UrlbarInputBase-mjs.patch | 4 +- .../urlbar/content/UrlbarShared-mjs.patch | 10 +- src/zen/common/modules/ZenUIManager.mjs | 18 +- src/zen/folders/ZenFolders.mjs | 25 +++ src/zen/share/ZenShareManager.mjs | 12 +- src/zen/urlbar/ZenUBProvider.sys.mjs | 1 + src/zen/urlbar/ZenUBSidebarProvider.sys.mjs | 212 ++++++++++++++++++ src/zen/urlbar/moz.build | 1 + 8 files changed, 265 insertions(+), 18 deletions(-) create mode 100644 src/zen/urlbar/ZenUBSidebarProvider.sys.mjs diff --git a/src/browser/components/urlbar/content/UrlbarInputBase-mjs.patch b/src/browser/components/urlbar/content/UrlbarInputBase-mjs.patch index 0266dbb0d..50cdf8b18 100644 --- a/src/browser/components/urlbar/content/UrlbarInputBase-mjs.patch +++ b/src/browser/components/urlbar/content/UrlbarInputBase-mjs.patch @@ -1,5 +1,5 @@ diff --git a/browser/components/urlbar/content/UrlbarInputBase.mjs b/browser/components/urlbar/content/UrlbarInputBase.mjs -index 8be35b58c5369e123ca69426e852e3849bf75513..9357aaadee0f8d2dab170e2d5e0b57282d909bf5 100644 +index 8be35b58c5369e123ca69426e852e3849bf75513..778d8aecce4da6748ecbf8b86a38c2da6e01bec5 100644 --- a/browser/components/urlbar/content/UrlbarInputBase.mjs +++ b/browser/components/urlbar/content/UrlbarInputBase.mjs @@ -83,6 +83,12 @@ if (lazy) { @@ -397,7 +397,7 @@ index 8be35b58c5369e123ca69426e852e3849bf75513..9357aaadee0f8d2dab170e2d5e0b5728 // Also ignore mousedown on the urlbarView context menu: opening/closing the // view is already handled by the result opening flow. - if (event.target.closest?.("tab, #urlbarView-context-menu")) { -+ if (event.target.closest?.("tab, #urlbarView-context-menu") || event.target.closest?.("#tabs-newtab-button")) { ++ if (event.target.closest?.("tab, #urlbarView-context-menu") || event.target.closest?.("menupopup") || event.target.closest?.("#tabs-newtab-button")) { break; } diff --git a/src/browser/components/urlbar/content/UrlbarShared-mjs.patch b/src/browser/components/urlbar/content/UrlbarShared-mjs.patch index e2622c8f6..8b50596a8 100644 --- a/src/browser/components/urlbar/content/UrlbarShared-mjs.patch +++ b/src/browser/components/urlbar/content/UrlbarShared-mjs.patch @@ -1,5 +1,5 @@ diff --git a/browser/components/urlbar/content/UrlbarShared.mjs b/browser/components/urlbar/content/UrlbarShared.mjs -index 0ac5330223c84fbb98880a5c2d83ae58a8f3079e..f9d3d27333f5552404bd8a1138fb1e5d6150a3d8 100644 +index 0ac5330223c84fbb98880a5c2d83ae58a8f3079e..ff488cf5a2c8b7857a8e145e40aaf861cfa655ec 100644 --- a/browser/components/urlbar/content/UrlbarShared.mjs +++ b/browser/components/urlbar/content/UrlbarShared.mjs @@ -158,6 +158,7 @@ export const UrlbarShared = { @@ -71,3 +71,11 @@ index 0ac5330223c84fbb98880a5c2d83ae58a8f3079e..f9d3d27333f5552404bd8a1138fb1e5d case "UrlbarProviderOmnibox": return UrlbarShared.RESULT_GROUP.HEURISTIC_OMNIBOX; case "UrlbarProviderRestrictKeywordsAutofill": +@@ -1060,6 +1080,7 @@ export const UrlbarShared = { + case "UrlbarProviderAboutPages": + return UrlbarShared.RESULT_GROUP.ABOUT_PAGES; + case "UrlbarProviderInputHistory": ++ case "ZenUrlbarProviderSidebar": + return UrlbarShared.RESULT_GROUP.INPUT_HISTORY; + case "UrlbarProviderQuickSuggest": + return UrlbarShared.RESULT_GROUP.GENERAL_PARENT; diff --git a/src/zen/common/modules/ZenUIManager.mjs b/src/zen/common/modules/ZenUIManager.mjs index a6ebe1bd9..ddccbc665 100644 --- a/src/zen/common/modules/ZenUIManager.mjs +++ b/src/zen/common/modules/ZenUIManager.mjs @@ -146,6 +146,20 @@ window.gZenUIManager = { }); }, + /** + * Shakes an element from side to side to catch the user's eye. + * + * @param {Element} element + * @param {number} delay Milliseconds to wait before shaking. + */ + shakeElement(element, delay = 0) { + return this.elementAnimate( + element, + { x: [0, -12, 8, -4, 2, 0] }, + { duration: 600, delay, easing: "ease-out" } + ); + }, + _addNewCustomizableButtonsIfNeeded() { const kPref = "zen.ui.migration.compact-mode-button-added"; let navbarPlacements = CustomizableUI.getWidgetIdsInArea( @@ -1595,10 +1609,6 @@ window.gZenVerticalTabsManager = { }, rebuildURLBarMenus() { - if (document.getElementById("paste-and-go")) { - return; - } - gURLBar._initCopyCutController(); gURLBar.updatePlaceholder(); }, diff --git a/src/zen/folders/ZenFolders.mjs b/src/zen/folders/ZenFolders.mjs index c33f21114..bd8a91044 100644 --- a/src/zen/folders/ZenFolders.mjs +++ b/src/zen/folders/ZenFolders.mjs @@ -2229,6 +2229,31 @@ class nsZenFolders extends nsZenDOMOperatedFeature { ); } + /** + * Switches to the folder's space, expands its parents (root first) and the + * folder itself, then jiggles it so it's easy to spot. + * + * @param {MozTabbrowserTabGroup} folder The folder to reveal. + */ + async revealFolder(folder) { + const workspaceId = folder.getAttribute("zen-workspace-id"); + if (workspaceId && workspaceId != gZenWorkspaces.activeWorkspace) { + await gZenWorkspaces.changeWorkspaceWithID(workspaceId); + } + let collapsedRoot = folder.rootMostCollapsedFolder; + const wasCollapsed = !!collapsedRoot; + while (collapsedRoot) { + collapsedRoot.collapsed = false; + collapsedRoot = folder.rootMostCollapsedFolder; + } + const label = folder.labelElement.parentElement; + label.scrollIntoView({ block: "nearest", behavior: "smooth" }); + await gZenUIManager.shakeElement( + label, + wasCollapsed ? this.#folderRevealDuration * 1000 : 0 + ); + } + styleCleanup(items) { items.forEach(item => { item.style.removeProperty("opacity"); diff --git a/src/zen/share/ZenShareManager.mjs b/src/zen/share/ZenShareManager.mjs index 63d17490f..5e7556d50 100644 --- a/src/zen/share/ZenShareManager.mjs +++ b/src/zen/share/ZenShareManager.mjs @@ -437,17 +437,7 @@ class nsZenShareManager extends nsZenDOMOperatedFeature { // Shake the new split group in the sidebar. const group = tabs[0]?.group; if (group?.hasAttribute("split-view-group")) { - requestAnimationFrame(() => { - gZenUIManager.motion - .animate( - group, - { x: [-32, 0] }, - { type: "spring", bounce: 0.5, duration: 1.5 } - ) - .then(() => { - group.style.removeProperty("transform"); - }); - }); + gZenUIManager.shakeElement(group); } } diff --git a/src/zen/urlbar/ZenUBProvider.sys.mjs b/src/zen/urlbar/ZenUBProvider.sys.mjs index 0ff005228..2a508cbe2 100644 --- a/src/zen/urlbar/ZenUBProvider.sys.mjs +++ b/src/zen/urlbar/ZenUBProvider.sys.mjs @@ -9,6 +9,7 @@ ChromeUtils.defineESModuleGetters(lazy, { /* eslint-disable mozilla/valid-lazy */ ZenUrlbarProviderGlobalActions: "resource:///modules/ZenUBActionsProvider.sys.mjs", + ZenUrlbarProviderSidebar: "resource:///modules/ZenUBSidebarProvider.sys.mjs", }); export function registerZenUrlbarProviders() { diff --git a/src/zen/urlbar/ZenUBSidebarProvider.sys.mjs b/src/zen/urlbar/ZenUBSidebarProvider.sys.mjs new file mode 100644 index 000000000..422d8a096 --- /dev/null +++ b/src/zen/urlbar/ZenUBSidebarProvider.sys.mjs @@ -0,0 +1,212 @@ +/* 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/. */ + +import { + UrlbarProvider, + UrlbarUtils, +} from "moz-src:///browser/components/urlbar/UrlbarUtils.sys.mjs"; +import { UrlbarShared } from "chrome://browser/content/urlbar/UrlbarShared.mjs"; + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + UrlbarResult: "chrome://browser/content/urlbar/UrlbarResult.mjs", + BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.sys.mjs", + UrlbarPrefs: "moz-src:///browser/components/urlbar/UrlbarPrefs.sys.mjs", + UrlbarProviderOpenTabs: + "moz-src:///browser/components/urlbar/UrlbarProviderOpenTabs.sys.mjs", + ZenSessionStore: "resource:///modules/zen/ZenSessionManager.sys.mjs", +}); + +const DYNAMIC_TYPE_NAME = "zen-folders"; + +export class ZenUrlbarProviderSidebar extends UrlbarProvider { + get name() { + return "ZenUrlbarProviderSidebar"; + } + + /** + * @returns {Values} + */ + get type() { + return UrlbarShared.PROVIDER_TYPE.PROFILE; + } + + async isActive(queryContext) { + // The sidebar data never holds tabs from private windows. + return ( + !queryContext.isPrivate && + !!queryContext.tokens.length && + queryContext.sources.includes(UrlbarShared.RESULT_SOURCE.TABS) && + lazy.UrlbarPrefs.get("suggest.openpage") + ); + } + + async startQuery(queryContext, addCallback) { + const sidebar = lazy.ZenSessionStore.getSidebarData(); + if (!sidebar) { + return; + } + const tokens = queryContext.tokens.map(t => t.lowerCaseValue); + const matches = text => { + text = text.toLowerCase(); + return tokens.every(token => text.includes(token)); + }; + this.#addFolders(sidebar, matches, addCallback); + this.#addTabs(sidebar, matches, queryContext, addCallback); + } + + #addTabs(sidebar, matches, queryContext, addCallback) { + const openTabUrls = lazy.UrlbarProviderOpenTabs.getOpenTabUrls(); + const activeSpace = + lazy.BrowserWindowTracker.getTopWindow().gZenWorkspaces.activeWorkspace; + let resultsAdded = 0; + for (const tabData of sidebar.tabs || []) { + // Essentials have no space, they are part of every one of them. + if (!tabData.zenEssential && tabData.zenWorkspace != activeSpace) { + continue; + } + const entries = tabData.entries || []; + const entry = entries[(tabData.index || entries.length) - 1]; + const label = tabData.zenStaticLabel || entry?.title; + if (typeof label !== "string" || !label || !matches(label)) { + continue; + } + const url = entry?.url; + if (!url || url == queryContext.currentPage) { + // Don't suggest switching to the current tab. + continue; + } + const userContextId = tabData.userContextId || 0; + // The sidebar data can lag behind a navigation, only offer tabs that + // are still registered as open with this url. + const openTab = [...(openTabUrls.get(url) || [])].find( + ([contextId]) => contextId == userContextId + ); + if (!openTab) { + continue; + } + addCallback( + this, + new lazy.UrlbarResult({ + type: UrlbarShared.RESULT_TYPE.TAB_SWITCH, + source: UrlbarShared.RESULT_SOURCE.TABS, + payload: { + url, + title: label, + icon: UrlbarShared.getIconForUrl(url), + userContext: UrlbarUtils.getUserContextData(userContextId), + tabGroup: openTab[1], + action: lazy.UrlbarPrefs.get("secondaryActions.switchToTab") + ? UrlbarUtils.createTabSwitchSecondaryAction(userContextId) + : undefined, + }, + highlights: { + url: UrlbarShared.HIGHLIGHT.TYPED, + title: UrlbarShared.HIGHLIGHT.TYPED, + }, + }) + ); + if (++resultsAdded == queryContext.maxResults) { + return; + } + } + } + + #addFolders(sidebar, matches, addCallback) { + const folders = new Map( + (sidebar.folders || []).map(folder => [folder.id, folder]) + ); + for (const folder of folders.values()) { + if (folder.splitViewGroup || !folder.name || !matches(folder.name)) { + continue; + } + const space = sidebar.spaces?.find(s => s.uuid == folder.workspaceId); + const path = []; + for ( + let parent = folders.get(folder.parentId); + parent; + parent = folders.get(parent.parentId) + ) { + path.unshift(parent.name); + } + if (space?.name) { + path.unshift(space.name); + } + addCallback( + this, + new lazy.UrlbarResult({ + type: UrlbarShared.RESULT_TYPE.DYNAMIC, + source: UrlbarShared.RESULT_SOURCE.TABS, + payload: { + dynamicType: DYNAMIC_TYPE_NAME, + zenFolderId: folder.id, + title: folder.name, + icon: + folder.userIcon || "chrome://browser/skin/zen-icons/folder.svg", + path: path.join(" / "), + }, + }) + ); + } + } + + getViewUpdate(result) { + return { + icon: { attributes: { src: result.payload.icon } }, + titleStrong: { + textContent: result.payload.title, + attributes: { dir: "ltr" }, + }, + path: { + textContent: result.payload.path, + attributes: { dir: "ltr", hidden: !result.payload.path }, + }, + }; + } + + getViewTemplate() { + return { + attributes: { + selectable: true, + }, + children: [ + { + name: "icon", + tag: "img", + classList: ["urlbarView-favicon"], + }, + { + name: "title", + tag: "span", + classList: ["urlbarView-title"], + children: [ + { + name: "titleStrong", + tag: "strong", + }, + ], + }, + { + name: "path", + tag: "span", + classList: ["urlbarView-prettyName"], + }, + ], + }; + } + + onEngagement(queryContext, controller, details) { + const { zenFolderId } = details.result.payload; + if (!zenFolderId) { + return; + } + const ownerGlobal = details.element.documentGlobal; + ownerGlobal.gBrowser.selectedBrowser.focus(); + const folder = ownerGlobal.document.getElementById(zenFolderId); + if (folder?.isZenFolder) { + ownerGlobal.gZenFolders.revealFolder(folder); + } + } +} diff --git a/src/zen/urlbar/moz.build b/src/zen/urlbar/moz.build index f321a86bb..c8d1781e5 100644 --- a/src/zen/urlbar/moz.build +++ b/src/zen/urlbar/moz.build @@ -8,4 +8,5 @@ EXTRA_JS_MODULES += [ "ZenUBGlobalActions.sys.mjs", "ZenUBProvider.sys.mjs", "ZenUBResultsLearner.sys.mjs", + "ZenUBSidebarProvider.sys.mjs", ]