Skip to content
This repository has been archived by the owner on Dec 3, 2020. It is now read-only.

Commit

Permalink
Fix #157: Add Firefox UI telemetry probes
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
biancadanforth authored and Michael Kelly committed Oct 23, 2018
1 parent ce3f137 commit f2022d4
Show file tree
Hide file tree
Showing 9 changed files with 153 additions and 11 deletions.
56 changes: 46 additions & 10 deletions docs/METRICS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`

Expand Down Expand Up @@ -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",
"[email protected]", // the extension ID
{
"source": "testpilot"
// ...
}
]
]
}
// ...
}
// ...
}
// ...
}
```
5 changes: 4 additions & 1 deletion src/background/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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.
Expand Down
7 changes: 7 additions & 0 deletions src/background/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
31 changes: 31 additions & 0 deletions src/experiment_apis/customizableUI/api.js
Original file line number Diff line number Diff line change
@@ -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(),
},
};
}
};
19 changes: 19 additions & 0 deletions src/experiment_apis/customizableUI/schema.json
Original file line number Diff line number Diff line change
@@ -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"
}
]
}
]
}
]
6 changes: 6 additions & 0 deletions src/extraction/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 () => {
Expand Down
8 changes: 8 additions & 0 deletions src/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
21 changes: 21 additions & 0 deletions src/telemetry/content.js
Original file line number Diff line number Diff line change
@@ -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,
},
});
}
11 changes: 11 additions & 0 deletions src/telemetry/extension.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
});
}
}

0 comments on commit f2022d4

Please sign in to comment.