From d3cca04c15b38d796547ff3d8ca982772fb025a3 Mon Sep 17 00:00:00 2001 From: "Mr. M" Date: Sun, 14 Sep 2025 21:31:04 +0200 Subject: [PATCH] feat: Urlbar now supports 'switch space' and 'extensions', b=no-bug, c=common, kbs --- .../urlbar/UrlbarView-sys-mjs.patch | 24 ++-- src/zen/common/styles/zen-urlbar.css | 64 +++++++-- src/zen/kbs/ZenKeyboardShortcuts.mjs | 3 + src/zen/urlbar/ZenUBActionsProvider.sys.mjs | 132 ++++++++++++++++-- src/zen/urlbar/ZenUBGlobalActions.sys.mjs | 13 +- 5 files changed, 199 insertions(+), 37 deletions(-) diff --git a/src/browser/components/urlbar/UrlbarView-sys-mjs.patch b/src/browser/components/urlbar/UrlbarView-sys-mjs.patch index acf8fdfd8..43515e50a 100644 --- a/src/browser/components/urlbar/UrlbarView-sys-mjs.patch +++ b/src/browser/components/urlbar/UrlbarView-sys-mjs.patch @@ -1,5 +1,5 @@ diff --git a/browser/components/urlbar/UrlbarView.sys.mjs b/browser/components/urlbar/UrlbarView.sys.mjs -index fdbab8806fd320f4aacec46a42c8ef953580d00c..5ed31d5dbfa2e2041e6616f6b036d67928e64114 100644 +index fdbab8806fd320f4aacec46a42c8ef953580d00c..f327ca63ff4c99f7aa9acbddc69d2d8b80442d35 100644 --- a/browser/components/urlbar/UrlbarView.sys.mjs +++ b/browser/components/urlbar/UrlbarView.sys.mjs @@ -613,7 +613,7 @@ export class UrlbarView { @@ -11,27 +11,25 @@ index fdbab8806fd320f4aacec46a42c8ef953580d00c..5ed31d5dbfa2e2041e6616f6b036d679 // Try to reuse the cached top-sites context. If it's not cached, then // there will be a gap of time between when the input is focused and // when the view opens that can be perceived as flicker. -@@ -823,6 +823,19 @@ export class UrlbarView { +@@ -823,7 +823,16 @@ export class UrlbarView { // them, resembling tab-to-search. In that case, the input value is // still associated with the first result. this.input.setResultForCurrentValue(firstResult); -+ } else if (firstResult.payload.zenAction) { -+ this.#selectElement(this.getFirstSelectableElement(), { -+ updateInput: false, -+ setAccessibleFocus: -+ this.controller._userSelectionBehavior == "arrow", -+ }); -+ this.window.setTimeout(() => { +- } ++ } ++ this.window.setTimeout(() => { ++ if (queryContext.results[0].payload.zenAction) { + this.#selectElement(this.getFirstSelectableElement(), { + updateInput: false, + setAccessibleFocus: + this.controller._userSelectionBehavior == "arrow", + }); -+ }, 140); - } ++ } ++ }, 10); } -@@ -1341,7 +1354,7 @@ export class UrlbarView { + // Announce tab-to-search results to screen readers as the user types. +@@ -1341,7 +1350,7 @@ export class UrlbarView { includeHiddenExposures: true, }); let canBeVisible = @@ -40,7 +38,7 @@ index fdbab8806fd320f4aacec46a42c8ef953580d00c..5ed31d5dbfa2e2041e6616f6b036d679 if (result.isHiddenExposure) { if (canBeVisible) { this.controller.engagementEvent.addExposure( -@@ -3189,7 +3202,7 @@ export class UrlbarView { +@@ -3189,7 +3198,7 @@ export class UrlbarView { } #enableOrDisableRowWrap() { diff --git a/src/zen/common/styles/zen-urlbar.css b/src/zen/common/styles/zen-urlbar.css index fe49658f8..9ac50f559 100644 --- a/src/zen/common/styles/zen-urlbar.css +++ b/src/zen/common/styles/zen-urlbar.css @@ -563,21 +563,52 @@ button.popup-notification-dropmarker { } .urlbarView-shortcutContent { - border-radius: 4px; - padding: 6px 8px; - font-size: 10px; - font-weight: 600; text-transform: uppercase; margin-left: auto; - margin-top: auto; - margin-bottom: auto; + background-color: color-mix(in srgb, var(--zen-branding-bg-reverse), transparent 95%); + padding: 6px 8px; box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1); + font-size: 10px; &:empty { display: none !important; } } +.urlbarView-prettyName, +.urlbarView-shortcutContent { + border-radius: 4px; + font-weight: 600; + margin-top: auto; + margin-bottom: auto; +} + +.urlbarView-prettyName { + padding: 4px 6px; + background-color: color-mix(in srgb, var(--zen-branding-bg-reverse), transparent 90%); + margin-left: 6px; + font-size: 12px; + align-items: center; + gap: 6px; + display: flex; + color: color-mix(in srgb, var(--zen-primary-color), currentColor 95%); + + & img { + -moz-context-properties: fill; + fill: currentColor; + width: 16px; + height: 16px; + + &[workspaceIcon] { + scale: 1.4; + } + } + + &[hidden] { + display: none !important; + } +} + .urlbarView-row[has-action]:is([type='switchtab'], [type='remotetab'], [type='clipboard']) { & .urlbarView-action:last-child { margin-left: auto !important; @@ -635,14 +666,15 @@ button.popup-notification-dropmarker { --urlbarView-item-inline-padding: 8px; --urlbarView-item-block-padding: 10px; - &:hover.urlbarView-favicon, - &:hover, - & .urlbarView-shortcutContent { - background-color: color-mix( - in srgb, - var(--zen-branding-bg-reverse) 5%, - transparent 95% - ) !important; + &:hover { + &, + & .urlbarView-favicon { + background-color: color-mix( + in srgb, + var(--zen-branding-bg-reverse) 5%, + transparent 95% + ) !important; + } } &[selected] { @@ -669,6 +701,10 @@ button.popup-notification-dropmarker { & .urlbarView-shortcutContent { background-color: rgba(255, 255, 255, 0.9) !important; } + + & .urlbarView-prettyName { + background-color: color-mix(in srgb, var(--zen-branding-bg-reverse), transparent 80%); + } } } diff --git a/src/zen/kbs/ZenKeyboardShortcuts.mjs b/src/zen/kbs/ZenKeyboardShortcuts.mjs index 245de5c0e..b8630ad6e 100644 --- a/src/zen/kbs/ZenKeyboardShortcuts.mjs +++ b/src/zen/kbs/ZenKeyboardShortcuts.mjs @@ -1377,6 +1377,9 @@ var gZenKeyboardShortcutsManager = { * @returns {string|null} The shortcut as a string or null if not found */ getShortcutDisplayFromCommand(command) { + if (!command) { + return null; + } const shortcut = this.getShortcutFromCommand(command); if (shortcut) { return shortcut.toUserString(); diff --git a/src/zen/urlbar/ZenUBActionsProvider.sys.mjs b/src/zen/urlbar/ZenUBActionsProvider.sys.mjs index ccda325d0..26cc09228 100644 --- a/src/zen/urlbar/ZenUBActionsProvider.sys.mjs +++ b/src/zen/urlbar/ZenUBActionsProvider.sys.mjs @@ -5,6 +5,7 @@ import { XPCOMUtils } from 'resource://gre/modules/XPCOMUtils.sys.mjs'; import { UrlbarProvider, UrlbarUtils } from 'resource:///modules/UrlbarUtils.sys.mjs'; import { globalActions } from 'resource:///modules/ZenUBGlobalActions.sys.mjs'; +import { ExtensionCommon } from 'resource://gre/modules/ExtensionCommon.sys.mjs'; const lazy = {}; @@ -21,6 +22,7 @@ ChromeUtils.defineESModuleGetters(lazy, { UrlbarTokenizer: 'resource:///modules/UrlbarTokenizer.sys.mjs', QueryScorer: 'resource:///modules/UrlbarProviderInterventions.sys.mjs', BrowserWindowTracker: 'resource:///modules/BrowserWindowTracker.sys.mjs', + AddonManager: 'resource://gre/modules/AddonManager.sys.mjs', }); XPCOMUtils.defineLazyPreferenceGetter( @@ -30,6 +32,8 @@ XPCOMUtils.defineLazyPreferenceGetter( true ); +let { makeWidgetId } = ExtensionCommon; + /** * A provider that lets the user view all available global actions for a query. */ @@ -63,12 +67,67 @@ export class ZenUrlbarProviderGlobalActions extends UrlbarProvider { ); } + #getWorkspaceActions(window) { + if (window.gZenWorkspaces.privateWindowOrDisabled) { + return []; + } + const workspaces = window.gZenWorkspaces._workspaceCache?.workspaces; + if (!workspaces?.length) { + return []; + } + let actions = []; + const activeSpaceUUID = window.gZenWorkspaces.activeWorkspace; + for (const workspace of workspaces) { + if (workspace.uuid !== activeSpaceUUID) { + const accentColor = window.gZenWorkspaces + .workspaceElement(workspace.uuid) + ?.style.getPropertyValue('--zen-primary-color'); + actions.push({ + label: 'Focus on', + extraPayload: { + workspaceId: workspace.uuid, + prettyName: workspace.name, + prettyIcon: workspace.icon, + accentColor, + }, + icon: 'chrome://browser/skin/zen-icons/forward.svg', + }); + } + } + return actions; + } + + async #getExtensionActions(window) { + const addons = await lazy.AddonManager.getAddonsByTypes(['extension']); + return addons + .filter( + (addon) => + addon.isActive && + !addon.isSystem && + window.document.getElementById(makeWidgetId(addon.id) + '-BAP') + ) + .map((addon) => { + return { + icon: 'chrome://browser/skin/zen-icons/extension.svg', + label: 'Extension', + extraPayload: { + extensionId: addon.id, + prettyName: addon.name, + prettyIcon: addon.iconURL, + }, + }; + }); + } + /** * @param {Window} window The window to check available actions for. * @returns All the available global actions. */ - #getAvailableActions(window) { - return globalActions.filter((a) => a.isAvailable(window)); + async #getAvailableActions(window) { + return globalActions + .filter((a) => a.isAvailable(window)) + .concat(this.#getWorkspaceActions(window)) + .concat(await this.#getExtensionActions(window)); } /** @@ -77,12 +136,12 @@ export class ZenUrlbarProviderGlobalActions extends UrlbarProvider { * @param {string} query The user's search query. * */ - #findMatchingActions(query) { + async #findMatchingActions(query) { const window = lazy.BrowserWindowTracker.getTopWindow(); - const actions = this.#getAvailableActions(window); + const actions = await this.#getAvailableActions(window); let results = []; for (let action of actions) { - const label = action.label; + const label = action.extraPayload?.prettyName || action.label; const score = this.#calculateFuzzyScore(label, query); if (score > MINIMUM_QUERY_SCORE) { results.push({ @@ -156,7 +215,7 @@ export class ZenUrlbarProviderGlobalActions extends UrlbarProvider { return; } - const actionsResults = this.#findMatchingActions(query); + const actionsResults = await this.#findMatchingActions(query); if (!actionsResults.length) { return; } @@ -174,6 +233,7 @@ export class ZenUrlbarProviderGlobalActions extends UrlbarProvider { shortcutContent: ownerGlobal.gZenKeyboardShortcutsManager.getShortcutDisplayFromCommand( action.command ), + ...action.extraPayload, }); let result = new lazy.UrlbarResult( @@ -207,6 +267,9 @@ export class ZenUrlbarProviderGlobalActions extends UrlbarProvider { * @returns {object} An object describing the view update. */ getViewUpdate(result) { + const prettyIconIsSvg = + result.payload.prettyIcon && + (result.payload.prettyIcon.endsWith('.svg') || result.payload.prettyIcon.endsWith('.png')); return { icon: { attributes: { @@ -220,6 +283,27 @@ export class ZenUrlbarProviderGlobalActions extends UrlbarProvider { shortcutContent: { textContent: result.payload.shortcutContent || '', }, + prettyName: { + attributes: { + hidden: !result.payload.prettyName, + style: `--zen-primary-color: ${result.payload.accentColor || 'currentColor'}`, + }, + }, + prettyNameStrong: { + textContent: result.payload.prettyName + ? prettyIconIsSvg || !result.payload.prettyIcon + ? result.payload.prettyName + : `${result.payload.prettyIcon} ${result.payload.prettyName}` + : '', + attributes: { dir: 'ltr' }, + }, + prettyNameIcon: { + attributes: { + src: result.payload.prettyIcon || '', + hidden: !prettyIconIsSvg || !result.payload.prettyIcon, + workspaceIcon: !!result.payload.workspaceId, + }, + }, }; } @@ -245,6 +329,23 @@ export class ZenUrlbarProviderGlobalActions extends UrlbarProvider { }, ], }, + { + tag: 'span', + classList: ['urlbarView-prettyName'], + hidden: true, + name: 'prettyName', + children: [ + { + tag: 'img', + name: 'prettyNameIcon', + attributes: { hidden: true }, + }, + { + name: 'prettyNameStrong', + tag: 'strong', + }, + ], + }, { name: 'shortcutContent', tag: 'span', @@ -258,14 +359,27 @@ export class ZenUrlbarProviderGlobalActions extends UrlbarProvider { const result = details.result; const payload = result.payload; const command = payload.zenCommand; - if (!command) { - return; - } const ownerGlobal = details.element.ownerGlobal; if (typeof command === 'function') { command(ownerGlobal); return; } + // Switch workspace if theres a workspaceId in the payload. + if (payload.workspaceId) { + ownerGlobal.gZenWorkspaces.changeWorkspaceWithID(payload.workspaceId); + return; + } + if (payload.extensionId) { + const widgetId = makeWidgetId(payload.extensionId) + '-BAP'; + const node = ownerGlobal.document.getElementById(widgetId); + if (node) { + node.doCommand(); + } + return; + } + if (!command) { + return; + } const commandToRun = ownerGlobal.document.getElementById(command); if (commandToRun) { ownerGlobal.gBrowser.selectedBrowser.focus(); diff --git a/src/zen/urlbar/ZenUBGlobalActions.sys.mjs b/src/zen/urlbar/ZenUBGlobalActions.sys.mjs index 40a273731..0656d1834 100644 --- a/src/zen/urlbar/ZenUBGlobalActions.sys.mjs +++ b/src/zen/urlbar/ZenUBGlobalActions.sys.mjs @@ -37,11 +37,22 @@ const globalActionsTemplate = [ command: (window) => window.openPreferences(), icon: 'chrome://browser/skin/zen-icons/settings.svg', }, + { + label: 'Open New Window', + command: 'cmd_newNavigator', + icon: 'chrome://browser/skin/zen-icons/window.svg', + }, + { + label: 'Open Private Window', + command: 'Tools:PrivateBrowsing', + icon: 'chrome://browser/skin/zen-icons/private-window.svg', + }, ]; export const globalActions = globalActionsTemplate.map((action) => ({ - ...action, isAvailable: (window) => { return window.document.getElementById(action.command)?.getAttribute('disabled') !== 'true'; }, + extraPayload: {}, + ...action, }));