Fix #157: Add Firefox UI telemetry probes
* Add `visit_supported_site` and `hide_toolbar_button` probes. * `hide_toolbar_button` required adding a new experimental API, `customizeUI`, which allows the extension to be notified when the Firefox CustomizeUI module detects the `onWidgetRemoved` event. This event fires any time a widget is removed from the chrome, including browserAction buttons. The widget is identified by a widgetId. * Update METRICS.md to move `uninstall` probe to Appendix, since it is handled by the Addons Manager's event telemetry already. * Create a new ./src/telemetry/content.js file to handle sending messages to the background telemetry script to record events from content scripts.
This commit is contained in:
Родитель
ce3f137d26
Коммит
f2022d4dd0
|
@ -261,16 +261,7 @@ Fired when the user clicks an undo button in a Product Card in the browserAction
|
|||
|
||||
### `uninstall`
|
||||
|
||||
Fired when the user uninstalls the extension.
|
||||
|
||||
#### Payload properties
|
||||
|
||||
- `methods`: String
|
||||
- `'uninstall'`
|
||||
- `objects`: String
|
||||
- `'uninstall'`
|
||||
- `extra_keys`: Object
|
||||
- `'tracked_prods'`
|
||||
See Appendix A.
|
||||
|
||||
### `hide_toolbar_button`
|
||||
|
||||
|
@ -381,3 +372,48 @@ No telemetry will be sent from the extension in the following additional cases:
|
|||
- The user is in a [Private Browsing](https://support.mozilla.org/en-US/kb/private-browsing-use-firefox-without-history?redirectlocale=en-US&redirectslug=Private+Browsing) window
|
||||
- Preference: `browser.privatebrowsing.autostart`
|
||||
- [`windows.Window`](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/windows/Window) property: `window.incognito`
|
||||
|
||||
|
||||
## Appendices
|
||||
|
||||
### Appendix A: `uninstall`
|
||||
|
||||
Fired when the user uninstalls the extension.
|
||||
|
||||
This event, along with all other add-on lifecycle events, is recorded by the Addons Manager's event telemetry in Firefox. It will exist as part of the `main` ping under `payload.processes.parent.events` as an array in the `events` array. This event will be fired under the `addonsManager` telemetry category.
|
||||
|
||||
#### Sample Ping
|
||||
|
||||
Note: This is a sample ping. The exact value for the extension ID may differ, though the other values are correct.
|
||||
|
||||
```javascript
|
||||
{
|
||||
"type": "main",
|
||||
// ...
|
||||
"payload": {
|
||||
// ...
|
||||
"processes": {
|
||||
// ...
|
||||
"parent": {
|
||||
// ...
|
||||
"events": [
|
||||
[
|
||||
9792,
|
||||
"addonsManager",
|
||||
"uninstall",
|
||||
"extension",
|
||||
"shopping-testpilot@mozilla.org", // the extension ID
|
||||
{
|
||||
"source": "testpilot"
|
||||
// ...
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
// ...
|
||||
}
|
||||
// ...
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
|
|
@ -16,7 +16,7 @@ import {handleWebRequest, updatePrices} from 'commerce/background/price_updates'
|
|||
import store from 'commerce/state';
|
||||
import {checkMigrations} from 'commerce/state/migrations';
|
||||
import {loadStateFromStorage} from 'commerce/state/sync';
|
||||
import {registerEvents} from 'commerce/telemetry/extension';
|
||||
import {registerEvents, handleWidgetRemoved} from 'commerce/telemetry/extension';
|
||||
|
||||
(async function main() {
|
||||
registerEvents();
|
||||
|
@ -37,6 +37,9 @@ import {registerEvents} from 'commerce/telemetry/extension';
|
|||
// Open the product page when an alert notification is clicked.
|
||||
browser.notifications.onClicked.addListener(handleNotificationClicked);
|
||||
|
||||
// Record hide_toolbar_button event when the toolbar button is hidden.
|
||||
browser.customizableUI.onWidgetRemoved.addListener(handleWidgetRemoved);
|
||||
|
||||
// Enable content scripts now that the background listener is registered.
|
||||
// Store the return value globally to avoid destroying it, which would
|
||||
// unregister the content scripts.
|
||||
|
|
|
@ -19,12 +19,19 @@
|
|||
import {handleConfigMessage} from 'commerce/config/background';
|
||||
import {handleBrowserActionOpened} from 'commerce/background/browser_action';
|
||||
import {handleExtractedProductData} from 'commerce/background/extraction';
|
||||
import {recordEvent} from 'commerce/telemetry/extension';
|
||||
|
||||
// sendMessage/onMessage handlers
|
||||
|
||||
export const messageHandlers = new Map([
|
||||
['extracted-product', handleExtractedProductData],
|
||||
['config', handleConfigMessage],
|
||||
['telemetry', async message => recordEvent(
|
||||
message.data.method,
|
||||
message.data.object,
|
||||
message.data.value,
|
||||
message.data.extra,
|
||||
)],
|
||||
]);
|
||||
|
||||
export async function handleMessage(message, sender) {
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/* 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/. */
|
||||
/* global ChromeUtils ExtensionAPI ExtensionCommon */
|
||||
|
||||
this.customizableUI = class extends ExtensionAPI {
|
||||
getAPI(context) {
|
||||
ChromeUtils.import('resource://gre/modules/ExtensionCommon.jsm');
|
||||
const {EventManager} = ExtensionCommon;
|
||||
const {CustomizableUI} = ChromeUtils.import('resource:///modules/CustomizableUI.jsm', {});
|
||||
return {
|
||||
customizableUI: {
|
||||
onWidgetRemoved: new EventManager(
|
||||
context,
|
||||
'customizableUI.onWidgetRemoved',
|
||||
(fire) => {
|
||||
const toolbarButton = {
|
||||
onWidgetRemoved(widgetId) {
|
||||
fire.async(widgetId);
|
||||
},
|
||||
};
|
||||
CustomizableUI.addListener(toolbarButton);
|
||||
return () => {
|
||||
CustomizableUI.removeListener(toolbarButton);
|
||||
};
|
||||
},
|
||||
).api(),
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
|
@ -0,0 +1,19 @@
|
|||
[
|
||||
{
|
||||
"namespace": "customizableUI",
|
||||
"events": [
|
||||
{
|
||||
"name": "onWidgetRemoved",
|
||||
"type": "function",
|
||||
"description": "Fired when a widget is removed from the browser chrome",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "widgetId",
|
||||
"description": "The unique identifier for the widget",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
|
@ -13,6 +13,7 @@ import extractProductWithFathom from 'commerce/extraction/fathom';
|
|||
import extractProductWithFallback from 'commerce/extraction/selector';
|
||||
import extractProductWithOpenGraph from 'commerce/extraction/open_graph';
|
||||
import {shouldExtract} from 'commerce/privacy';
|
||||
import recordEvent from 'commerce/telemetry/content';
|
||||
|
||||
/**
|
||||
* Extraction methods are given the document object for the page, and must
|
||||
|
@ -89,6 +90,11 @@ async function attemptExtraction() {
|
|||
return;
|
||||
}
|
||||
|
||||
// Record visit_supported_site event
|
||||
if (!isBackgroundUpdate) {
|
||||
await recordEvent('visit_supported_site', 'supported_site');
|
||||
}
|
||||
|
||||
// Extract immediately, and again if the readyState changes.
|
||||
let extractedProduct = await attemptExtraction();
|
||||
document.addEventListener('readystatechange', async () => {
|
||||
|
|
|
@ -37,6 +37,14 @@
|
|||
"telemetry"
|
||||
],
|
||||
"experiment_apis": {
|
||||
"customizableUI": {
|
||||
"schema": "experiment_apis/customizableUI/schema.json",
|
||||
"parent": {
|
||||
"scopes": ["addon_parent"],
|
||||
"script": "experiment_apis/customizableUI/api.js",
|
||||
"paths": [["customizableUI"]]
|
||||
}
|
||||
},
|
||||
"shoppingPrefs": {
|
||||
"schema": "experiment_apis/shoppingPrefs/schema.json",
|
||||
"parent": {
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
/* 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/. */
|
||||
|
||||
/**
|
||||
* Communication from content scripts to the background page for recording
|
||||
* telemetry events.
|
||||
* @module
|
||||
*/
|
||||
|
||||
export default async function recordEvent(method, object, value = null, extra = null) {
|
||||
await browser.runtime.sendMessage({
|
||||
type: 'telemetry',
|
||||
data: {
|
||||
method,
|
||||
object,
|
||||
value,
|
||||
extra,
|
||||
},
|
||||
});
|
||||
}
|
|
@ -212,3 +212,14 @@ export async function getBadgeType() {
|
|||
return 'unknown';
|
||||
}
|
||||
}
|
||||
|
||||
export async function handleWidgetRemoved(widgetId) {
|
||||
const addonId = (await browser.management.getSelf()).id;
|
||||
// widgetId replaces '@' and '.' in the addonId with _
|
||||
const modifiedAddonId = addonId.replace(/[@.+]/g, '_');
|
||||
if (`${modifiedAddonId}-browser-action` === widgetId) {
|
||||
await recordEvent('hide_toolbar_button', 'toolbar_button', null, {
|
||||
badge_type: await getBadgeType(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче