Files
sousa-gecko/browser/components/urlbar/UrlbarProviderUnitConversion.sys.mjs
T
Dão Gottwald c5e7b74727 Bug 2051959 - [IPC Urlbar] Phase 6: Make the provider engagement hooks work on the message path. r=daleharvey
Provider `onEngagement` hooks run parent-side (in `UrlbarProvidersManager`, from
the parent controller). On the actor message path the parent controller has no
child, so `controller.input`/`controller.view` are undefined and `details.element`
is dropped on the wire, breaking the hooks that reach them (`GlobalActions`,
`Autofill`, `ActionsSearchMode`, `Calculator`, `ContextualSearch`,
`UnitConversion`).

Resolve the element reads from data that survives the wire: `GlobalActions` reads
the picked action key from `details.pickedActionKey` (resolved content-side by
the telemetry snapshot); `QuickActions`' `pickAction` and `ActionsSearchMode`
take the action key and input length from the result payload instead of the
picked element, and `ActionsSearchMode` recomputes the disabled state from the
action; `UnitConversion` copies its conversion output from the result payload
instead of the picked element's text.

Route the input/view side effects to the content side: the parent-side
`UrlbarChildControllerProxy` exposes `input`/`view` stand-ins whose allowed
methods (`search`, `_setValue`, `startQuery`, `close`, `startTail150`) forward to
the real content objects as an `InvokeContentAction` message, so providers keep
calling `controller.input.X()`/`controller.view.X()` unchanged. `ContextualSearch`
passes the engine's name and aliases (all `search()` reads) as a plain object so
it stays structured-cloneable.

`this.input` is now a forwarding stand-in on the message path, so the
`receiveResults` first-result guard checks for the `onFirstResult` method rather
than mere truthiness (it only runs on the in-process input; the content side runs
`onFirstResult` itself).

The underscore-prefixed `UrlbarInput._setValue` is forwarded as-is here; a
follow-up renames it to `setValue` now that it's part of the actor-facing
surface.

`browser_glean_telemetry_reenter.js` exercises a provider `onEngagement` that
blurs the input. On the message path the hook fires only after the pick's
engagement has been recorded and its session cleared, so the blur's abandonment
record is a no-op rather than the synchronous `record()` re-entry the direct path
sees; the re-entry assertion is gated to the direct path. Both paths still record
exactly one engagement and no abandonment.

The default in-process path is unaffected.

Differential Revision: https://phabricator.services.mozilla.com/D310360
2026-07-15 07:59:53 +00:00

163 lines
4.3 KiB
JavaScript

/* 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/. */
/**
* Provide unit converter.
*/
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
import { UnitConverterSimple } from "moz-src:///browser/components/urlbar/unitconverters/UnitConverterSimple.sys.mjs";
import { UnitConverterTemperature } from "moz-src:///browser/components/urlbar/unitconverters/UnitConverterTemperature.sys.mjs";
import { UnitConverterTimezone } from "moz-src:///browser/components/urlbar/unitconverters/UnitConverterTimezone.sys.mjs";
import {
UrlbarProvider,
UrlbarUtils,
} from "moz-src:///browser/components/urlbar/UrlbarUtils.sys.mjs";
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
UrlbarPrefs: "moz-src:///browser/components/urlbar/UrlbarPrefs.sys.mjs",
UrlbarResult: "chrome://browser/content/urlbar/UrlbarResult.mjs",
UrlbarShared: "chrome://browser/content/urlbar/UrlbarShared.mjs",
});
XPCOMUtils.defineLazyServiceGetter(
lazy,
"ClipboardHelper",
"@mozilla.org/widget/clipboardhelper;1",
Ci.nsIClipboardHelper
);
const CONVERTERS = [
new UnitConverterSimple(),
new UnitConverterTemperature(),
new UnitConverterTimezone(),
];
const DYNAMIC_RESULT_TYPE = "unitConversion";
const VIEW_TEMPLATE = {
attributes: {
selectable: true,
},
children: [
{
name: "content",
tag: "span",
classList: ["urlbarView-no-wrap"],
children: [
{
name: "icon",
tag: "img",
classList: ["urlbarView-favicon"],
attributes: {
src: "chrome://global/skin/icons/edit-copy.svg",
},
},
{
name: "output",
tag: "strong",
attributes: {
dir: "ltr",
},
},
{
name: "action",
tag: "span",
},
],
},
],
};
/**
* Provide a feature that converts given units.
*/
export class UrlbarProviderUnitConversion extends UrlbarProvider {
constructor() {
super();
}
/**
* @returns {Values<typeof UrlbarUtils.PROVIDER_TYPE>}
*/
get type() {
return UrlbarUtils.PROVIDER_TYPE.PROFILE;
}
/**
* Whether the provider should be invoked for the given context. If this
* method returns false, the providers manager won't start a query with this
* provider, to save on resources.
*
* @param {UrlbarQueryContext} queryContext
* The query context object.
*/
async isActive({ searchString }) {
if (!lazy.UrlbarPrefs.get("unitConversion.enabled")) {
return false;
}
for (const converter of CONVERTERS) {
const result = converter.convert(searchString);
if (result) {
this._activeResult = result;
return true;
}
}
this._activeResult = null;
return false;
}
getViewTemplate(_result) {
return VIEW_TEMPLATE;
}
/**
* This is called only for dynamic result types, when the urlbar view updates
* the view of one of the results of the provider. It should return an object
* describing the view update.
*
* @param {UrlbarResult} result The result whose view will be updated.
* @returns {object} An object describing the view update.
*/
getViewUpdate(result) {
return {
output: {
textContent: result.payload.output,
},
action: {
l10n: { id: "urlbar-result-action-copy-to-clipboard" },
},
};
}
/**
* Starts querying.
*
* @param {UrlbarQueryContext} queryContext
* @param {(provider: UrlbarProvider, result: UrlbarResult) => void} addCallback
* Callback invoked by the provider to add a new result.
*/
startQuery(queryContext, addCallback) {
const result = new lazy.UrlbarResult({
type: lazy.UrlbarShared.RESULT_TYPE.DYNAMIC,
source: lazy.UrlbarShared.RESULT_SOURCE.OTHER_LOCAL,
suggestedIndex: lazy.UrlbarPrefs.get("unitConversion.suggestedIndex"),
payload: {
dynamicType: DYNAMIC_RESULT_TYPE,
output: this._activeResult,
input: queryContext.searchString,
},
});
addCallback(this, result);
}
onEngagement(queryContext, controller, details) {
lazy.ClipboardHelper.copyString(details.result.payload.output);
}
}