no-bug: Add workspaces search (gh-12745)

Co-authored-by: mr. m <91018726+mr-cheffy@users.noreply.github.com>
Co-authored-by: jababda <jababda@hotmail.com>
This commit is contained in:
Ezhik
2026-04-29 03:56:16 +09:00
committed by GitHub
parent c0620a95ef
commit 18c15ac0b8
10 changed files with 263 additions and 17 deletions

View File

@@ -79,6 +79,7 @@ zen-icons-picker-svg =
.label = Icons
urlbar-search-mode-zen_actions = Actions
urlbar-search-mode-workspaces = { zen-panel-ui-workspaces-text }
zen-site-data-settings = Settings
zen-generic-manage = Manage

View File

@@ -39,3 +39,6 @@
- name: zen.urlbar.suggestions.quick-actions
value: true
- name: browser.urlbar.shortcuts.workspaces
value: true

View File

@@ -1,12 +1,21 @@
diff --git a/browser/components/urlbar/UrlbarPrefs.sys.mjs b/browser/components/urlbar/UrlbarPrefs.sys.mjs
index 2d21248256c6c2bfb8dac958133c10e3251ef564..6645211ef09518b41cb737e3186fbd0162ecf700 100644
index 2d21248256c6c2bfb8dac958133c10e3251ef564..f788bd10ec2c08e4b27b77cd3bb0489fb04e8b7a 100644
--- a/browser/components/urlbar/UrlbarPrefs.sys.mjs
+++ b/browser/components/urlbar/UrlbarPrefs.sys.mjs
@@ -799,6 +799,7 @@ function makeDefaultResultGroups({ showSearchSuggestionsFirst }) {
@@ -462,6 +462,7 @@ const PREF_URLBAR_DEFAULTS = /** @type {PreferenceDefinition[]} */ ([
["shortcuts.tabs", true],
["shortcuts.history", true],
["shortcuts.actions", true],
+ ["shortcuts.workspaces", true],
// Boolean to determine if the providers defined in `exposureResults`
// should be displayed in search results. This can be set by a
@@ -799,6 +800,8 @@ function makeDefaultResultGroups({ showSearchSuggestionsFirst }) {
*/
let rootGroup = {
children: [
+ { children: [{ group: lazy.UrlbarUtils.RESULT_GROUP.ZEN_ACTION }] },
+ { children: [{ group: lazy.UrlbarUtils.RESULT_GROUP.ZEN_WORKSPACE }] },
// heuristic
{
maxResultCount: 1,

View File

@@ -1,5 +1,5 @@
diff --git a/browser/components/urlbar/UrlbarProvidersManager.sys.mjs b/browser/components/urlbar/UrlbarProvidersManager.sys.mjs
index 08455d8d5da233639ccebc0e77c0810fb4f674c3..78d0e875978b568b79646489c28b125a44ea79fa 100644
index 08455d8d5da233639ccebc0e77c0810fb4f674c3..da8092b561c3dd8864e57f5a52a1a643db29ace1 100644
--- a/browser/components/urlbar/UrlbarProvidersManager.sys.mjs
+++ b/browser/components/urlbar/UrlbarProvidersManager.sys.mjs
@@ -913,6 +913,7 @@ export class Query {
@@ -10,3 +10,26 @@ index 08455d8d5da233639ccebc0e77c0810fb4f674c3..78d0e875978b568b79646489c28b125a
(!this.context.trimmedSearchString ||
(!this.context.searchMode.engineName && !result.autofill))
) {
@@ -1043,6 +1044,7 @@ function updateSourcesIfEmpty(context) {
lazy.UrlbarTokenizer.TYPE.RESTRICT_TITLE,
lazy.UrlbarTokenizer.TYPE.RESTRICT_URL,
lazy.UrlbarTokenizer.TYPE.RESTRICT_ACTION,
+ lazy.UrlbarTokenizer.TYPE.RESTRICT_WORKSPACE,
].includes(t.type)
);
@@ -1100,6 +1102,14 @@ function updateSourcesIfEmpty(context) {
acceptedSources.push(source);
}
break;
+ case lazy.UrlbarUtils.RESULT_SOURCE.WORKSPACES:
+ if (
+ restrictTokenType === lazy.UrlbarTokenizer.TYPE.RESTRICT_WORKSPACE ||
+ !restrictTokenType
+ ) {
+ acceptedSources.push(source);
+ }
+ break;
case lazy.UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL:
case lazy.UrlbarUtils.RESULT_SOURCE.ADDON:
default:

View File

@@ -0,0 +1,28 @@
diff --git a/browser/components/urlbar/UrlbarTokenizer.sys.mjs b/browser/components/urlbar/UrlbarTokenizer.sys.mjs
index d4af0ee5138a69139b94d898fb07e2345172f025..f750aae3f9f0a849ca009784510575b2b7119e6d 100644
--- a/browser/components/urlbar/UrlbarTokenizer.sys.mjs
+++ b/browser/components/urlbar/UrlbarTokenizer.sys.mjs
@@ -66,6 +66,7 @@ export var UrlbarTokenizer = {
// `looksLikeOrigin()` returned `LOOKS_LIKE_ORIGIN.OTHER` for this token. It
// may or may not be an origin.
POSSIBLE_ORIGIN_BUT_SEARCH_ALLOWED: 12,
+ RESTRICT_WORKSPACE: 13,
}),
// The special characters below can be typed into the urlbar to restrict
@@ -83,6 +84,7 @@ export var UrlbarTokenizer = {
TITLE: "#",
URL: "$",
ACTION: ">",
+ WORKSPACE: "`",
}),
// The keys of characters in RESTRICT that will enter search mode.
@@ -97,6 +99,7 @@ export var UrlbarTokenizer = {
if (lazy.UrlbarPrefs.get("scotchBonnet.enableOverride")) {
keys.push(this.RESTRICT.ACTION);
}
+ keys.push(this.RESTRICT.WORKSPACE);
return new Set(keys);
},

View File

@@ -1,29 +1,50 @@
diff --git a/browser/components/urlbar/UrlbarUtils.sys.mjs b/browser/components/urlbar/UrlbarUtils.sys.mjs
index 64afd613f454edd7786fcc1e2f307a582e4d5f51..b4af9cc2fbddba2c5229e8ffee7b9c0c06c3e1d0 100644
index 64afd613f454edd7786fcc1e2f307a582e4d5f51..50e2dd129d4f2e8f0b07e29639d660cd08ee7318 100644
--- a/browser/components/urlbar/UrlbarUtils.sys.mjs
+++ b/browser/components/urlbar/UrlbarUtils.sys.mjs
@@ -85,6 +85,7 @@ export var UrlbarUtils = {
@@ -85,6 +85,8 @@ export var UrlbarUtils = {
RESTRICT_SEARCH_KEYWORD: "restrictSearchKeyword",
SUGGESTED_INDEX: "suggestedIndex",
TAIL_SUGGESTION: "tailSuggestion",
+ ZEN_ACTION: "zenAction",
+ ZEN_WORKSPACE: "zenWorkspace",
}),
// Defines provider types.
@@ -146,6 +147,7 @@ export var UrlbarUtils = {
@@ -146,6 +148,8 @@ export var UrlbarUtils = {
OTHER_NETWORK: 6,
ADDON: 7,
ACTIONS: 8,
+ ZEN_ACTIONS: 9,
+ WORKSPACES: 10,
}),
// Per-result exposure telemetry.
@@ -587,6 +589,8 @@ export var UrlbarUtils = {
@@ -295,6 +299,14 @@ export var UrlbarUtils = {
telemetryLabel: "actions",
uiLabel: "urlbar-searchmode-actions",
},
+ {
+ source: this.RESULT_SOURCE.WORKSPACES,
+ restrict: lazy.UrlbarTokenizer.RESTRICT.WORKSPACE,
+ icon: "chrome://browser/skin/zen-icons/selectable/layers.svg",
+ pref: "shortcuts.workspaces",
+ telemetryLabel: "workspaces",
+ uiLabel: "urlbar-searchmode-workspaces",
+ },
]);
},
@@ -587,6 +599,12 @@ export var UrlbarUtils = {
return this.RESULT_GROUP.HEURISTIC_FALLBACK;
case "UrlbarProviderHistoryUrlHeuristic":
return this.RESULT_GROUP.HEURISTIC_HISTORY_URL;
+ case "ZenUrlbarProviderGlobalActions":
+ return this.RESULT_GROUP.ZEN_ACTION;
+ if (result.source == this.RESULT_SOURCE.WORKSPACES) {
+ return this.RESULT_GROUP.ZEN_WORKSPACE;
+ } else {
+ return this.RESULT_GROUP.ZEN_ACTION;
+ }
case "UrlbarProviderOmnibox":
return this.RESULT_GROUP.HEURISTIC_OMNIBOX;
case "UrlbarProviderRestrictKeywordsAutofill":

View File

@@ -796,7 +796,8 @@
--fp-enabled: 1;
}
#alltabs-button {
#alltabs-button,
#urlbar-engine-one-off-item-workspaces {
list-style-image: url("chrome://browser/skin/tabs.svg") !important;
}

View File

@@ -5,3 +5,4 @@
[DEFAULT]
["browser_ub_actions_search.js"]
["browser_workspace_restrict_search.js"]

View File

@@ -0,0 +1,142 @@
/* 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",
UrlbarUtils: "moz-src:///browser/components/urlbar/UrlbarUtils.sys.mjs",
});
UrlbarTestUtils.init(this);
add_task(async function test_Workspace_Search_OneOff_Pref() {
const oneOffSearchButtons = UrlbarTestUtils.getOneOffSearchButtons(window);
async function openPopupAndWaitForRebuild(value = "") {
oneOffSearchButtons.invalidateCache();
const rebuildPromise = BrowserTestUtils.waitForEvent(
oneOffSearchButtons,
"rebuild"
);
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
waitForFocus,
value,
});
await rebuildPromise;
}
function getWorkspaceShortcut() {
return [...oneOffSearchButtons.localButtons].find(
button => button.source == UrlbarUtils.RESULT_SOURCE.WORKSPACES
);
}
try {
await SpecialPowers.pushPrefEnv({
set: [["zen.urlbar.hide-one-offs", false]],
});
await openPopupAndWaitForRebuild();
ok(
getWorkspaceShortcut(),
"The workspace shortcut should be visible when the pref is enabled"
);
await UrlbarTestUtils.promisePopupClose(window);
await SpecialPowers.pushPrefEnv({
set: [["browser.urlbar.shortcuts.workspaces", false]],
});
try {
await openPopupAndWaitForRebuild();
ok(
!getWorkspaceShortcut(),
"The workspace shortcut should be hidden when the pref is disabled"
);
await UrlbarTestUtils.promisePopupClose(window);
} finally {
await SpecialPowers.popPrefEnv();
}
} finally {
await SpecialPowers.popPrefEnv();
if (UrlbarTestUtils.isPopupOpen(window)) {
await UrlbarTestUtils.promisePopupClose(window);
}
}
});
add_task(async function test_Workspace_Restrict_Search() {
const originalWorkspaceId = gZenWorkspaces.activeWorkspace;
const workspaceName = "zen-urlbar-workspace-proof-617db8";
await gZenWorkspaces.createAndSaveWorkspace(workspaceName);
const createdWorkspace = gZenWorkspaces
.getWorkspaces()
.find(workspace => workspace.name == workspaceName);
ok(createdWorkspace, "Created the workspace used by the urlbar test");
registerCleanupFunction(async function () {
if (UrlbarTestUtils.isPopupOpen(window)) {
await UrlbarTestUtils.promisePopupClose(window, () =>
EventUtils.synthesizeKey("KEY_Escape")
);
}
if (gZenWorkspaces.activeWorkspace != originalWorkspaceId) {
await gZenWorkspaces.changeWorkspace(originalWorkspaceId);
}
if (
gZenWorkspaces
.getWorkspaces()
.some(workspace => workspace.uuid == createdWorkspace.uuid)
) {
await gZenWorkspaces.removeWorkspace(createdWorkspace.uuid);
}
});
await gZenWorkspaces.changeWorkspace(originalWorkspaceId);
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
waitForFocus,
value: "` " + workspaceName,
});
// Wait for the second search started when the typed token enters search mode.
await UrlbarTestUtils.promiseSearchComplete(window);
ok(gURLBar.searchMode, "The urlbar should enter search mode");
Assert.equal(
gURLBar.searchMode.source,
UrlbarUtils.RESULT_SOURCE.WORKSPACES,
"The typed token should enter workspace search mode"
);
Assert.equal(
gURLBar.searchMode.entry,
"typed",
"The workspace search mode should be entered by typing the token"
);
Assert.equal(gURLBar.value, workspaceName, "The token should be stripped");
const resultCount = UrlbarTestUtils.getResultCount(window);
ok(resultCount > 0, "Should show at least one workspace result");
const resultDetails = [];
for (let i = 0; i < resultCount; i++) {
resultDetails.push(await UrlbarTestUtils.getDetailsOfResultAt(window, i));
}
ok(
resultDetails.every(
({ result, source }) =>
source == UrlbarUtils.RESULT_SOURCE.WORKSPACES &&
result.providerName == "ZenUrlbarProviderGlobalActions"
),
"Typing the workspace token should limit results to workspace actions"
);
Assert.equal(
resultDetails[0].result.payload.prettyName,
workspaceName,
"The matching workspace should be shown first"
);
});

View File

@@ -155,6 +155,7 @@ export class ZenUrlbarProviderGlobalActions extends UrlbarProvider {
*/
async isActive(queryContext) {
return (
queryContext.searchMode?.source == UrlbarUtils.RESULT_SOURCE.WORKSPACES ||
queryContext.searchMode?.source ==
UrlbarUtils.RESULT_SOURCE.ZEN_ACTIONS ||
(lazy.enabledPref &&
@@ -241,10 +242,13 @@ export class ZenUrlbarProviderGlobalActions extends UrlbarProvider {
*
* @param {string} query The user's search query.
* @param {boolean} isPrefixed Whether the query is prefixed.
* @param {boolean} isWorkspaceSearch Whether this is a workspace search query
*/
async #findMatchingActions(query, isPrefixed) {
async #findMatchingActions(query, isPrefixed, isWorkspaceSearch) {
const window = lazy.BrowserWindowTracker.getTopWindow();
const actions = await this.#getAvailableActions(window);
const actions = isWorkspaceSearch
? this.#getWorkspaceActions(window)
: await this.#getAvailableActions(window);
let results = [];
for (let action of actions) {
if (isPrefixed && query.length < 1) {
@@ -341,13 +345,21 @@ export class ZenUrlbarProviderGlobalActions extends UrlbarProvider {
async startQuery(queryContext, addCallback) {
const query = queryContext.trimmedLowerCaseSearchString;
const isWorkspaceSearch =
queryContext.searchMode?.source == UrlbarUtils.RESULT_SOURCE.WORKSPACES;
const isPrefixed =
isWorkspaceSearch ||
queryContext.searchMode?.source == UrlbarUtils.RESULT_SOURCE.ZEN_ACTIONS;
if (!query && !isPrefixed) {
return;
}
const actionsResults = await this.#findMatchingActions(query, isPrefixed);
const actionsResults = await this.#findMatchingActions(
query,
isPrefixed,
isWorkspaceSearch
);
if (!actionsResults.length) {
return;
}
@@ -361,9 +373,12 @@ export class ZenUrlbarProviderGlobalActions extends UrlbarProvider {
zenCommand: action.command,
dynamicType: DYNAMIC_TYPE_NAME,
zenAction: true,
query: isPrefixed
? action.label.trimStart()
: queryContext.searchString,
// eslint-disable-next-line no-nested-ternary
query: isWorkspaceSearch
? action.extraPayload.prettyName
: isPrefixed
? action.label.trimStart()
: queryContext.searchString,
icon: action.icon,
shortcutContent:
ownerGlobal.gZenKeyboardShortcutsManager.getShortcutDisplayFromCommand(
@@ -378,7 +393,9 @@ export class ZenUrlbarProviderGlobalActions extends UrlbarProvider {
!isPrefixed;
let result = new lazy.UrlbarResult({
type: UrlbarUtils.RESULT_TYPE.DYNAMIC,
source: UrlbarUtils.RESULT_SOURCE.ZEN_ACTIONS,
source: isWorkspaceSearch
? UrlbarUtils.RESULT_SOURCE.WORKSPACES
: UrlbarUtils.RESULT_SOURCE.ZEN_ACTIONS,
payload,
highlights: payloadHighlights,
heuristic: shouldBePrioritized,
@@ -398,7 +415,7 @@ export class ZenUrlbarProviderGlobalActions extends UrlbarProvider {
zenUrlbarResultsLearner
.sortCommandsByPriority(finalResults)
.forEach(result => {
if (isPrefixed && i === 0 && query.length > 1) {
if (isPrefixed && !isWorkspaceSearch && i === 0 && query.length > 1) {
result.heuristic = true;
delete result.suggestedIndex;
}