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
163 lines
4.3 KiB
JavaScript
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);
|
|
}
|
|
}
|