282 lines
8.9 KiB
JavaScript
282 lines
8.9 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/. */
|
|
|
|
// We use importESModule here instead of static import so that the Karma test
|
|
// environment won't choke on these module. This is because the Karma test
|
|
// environment already stubs out XPCOMUtils, AppConstants and RemoteSettings,
|
|
// and overrides importESModule to be a no-op (which can't be done for a static
|
|
// import statement).
|
|
|
|
// eslint-disable-next-line mozilla/use-static-import
|
|
const { XPCOMUtils } = ChromeUtils.importESModule(
|
|
"resource://gre/modules/XPCOMUtils.sys.mjs"
|
|
);
|
|
|
|
// eslint-disable-next-line mozilla/use-static-import
|
|
const { MESSAGE_TYPE_HASH: msg } = ChromeUtils.importESModule(
|
|
"resource:///modules/asrouter/ActorConstants.mjs"
|
|
);
|
|
|
|
const lazy = {};
|
|
|
|
ChromeUtils.defineESModuleGetters(lazy, {
|
|
ClientID: "resource://gre/modules/ClientID.sys.mjs",
|
|
AboutWelcomeTelemetry:
|
|
"resource:///modules/aboutwelcome/AboutWelcomeTelemetry.sys.mjs",
|
|
EnrollmentType: "resource://nimbus/ExperimentAPI.sys.mjs",
|
|
NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs",
|
|
TelemetrySession: "resource://gre/modules/TelemetrySession.sys.mjs",
|
|
UpdateUtils: "resource://gre/modules/UpdateUtils.sys.mjs",
|
|
});
|
|
ChromeUtils.defineLazyGetter(
|
|
lazy,
|
|
"Telemetry",
|
|
() => new lazy.AboutWelcomeTelemetry()
|
|
);
|
|
|
|
ChromeUtils.defineLazyGetter(
|
|
lazy,
|
|
"browserSessionId",
|
|
() => lazy.TelemetrySession.getMetadata("").sessionId
|
|
);
|
|
|
|
export const PREF_IMPRESSION_ID =
|
|
"browser.newtabpage.activity-stream.impressionId";
|
|
|
|
export class ASRouterTelemetry {
|
|
constructor() {
|
|
this._impressionId = this.getOrCreateImpressionId();
|
|
XPCOMUtils.defineLazyPreferenceGetter(
|
|
this,
|
|
"telemetryEnabled",
|
|
"browser.newtabpage.activity-stream.telemetry",
|
|
false
|
|
);
|
|
}
|
|
|
|
get telemetryClientId() {
|
|
Object.defineProperty(this, "telemetryClientId", {
|
|
value: lazy.ClientID.getClientID(),
|
|
});
|
|
return this.telemetryClientId;
|
|
}
|
|
|
|
getOrCreateImpressionId() {
|
|
let impressionId = Services.prefs.getCharPref(PREF_IMPRESSION_ID, "");
|
|
if (!impressionId) {
|
|
impressionId = String(Services.uuid.generateUUID());
|
|
Services.prefs.setCharPref(PREF_IMPRESSION_ID, impressionId);
|
|
}
|
|
return impressionId;
|
|
}
|
|
/**
|
|
* Check if it is in the CFR experiment cohort by querying against the
|
|
* experiment manager of Messaging System
|
|
*
|
|
* @return {bool}
|
|
*/
|
|
get isInCFRCohort() {
|
|
return !!lazy.NimbusFeatures.cfr.getEnrollmentMetadata(
|
|
lazy.EnrollmentType.EXPERIMENT
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Create a ping for AS router event. The client_id is set to "n/a" by default,
|
|
* different component can override this by its own telemetry collection policy.
|
|
*/
|
|
async createASRouterEvent(action) {
|
|
let event = {
|
|
...action.data,
|
|
addon_version: Services.appinfo.appBuildID,
|
|
locale: Services.locale.appLocaleAsBCP47,
|
|
};
|
|
|
|
if (event.event_context && typeof event.event_context === "object") {
|
|
event.event_context = JSON.stringify(event.event_context);
|
|
}
|
|
switch (event.action) {
|
|
case "cfr_user_event":
|
|
event = await this.applyCFRPolicy(event);
|
|
break;
|
|
case "badge_user_event":
|
|
event = await this.applyToolbarBadgePolicy(event);
|
|
break;
|
|
case "infobar_user_event":
|
|
event = await this.applyInfoBarPolicy(event);
|
|
break;
|
|
case "spotlight_user_event":
|
|
event = await this.applySpotlightPolicy(event);
|
|
break;
|
|
case "toast_notification_user_event":
|
|
event = await this.applyToastNotificationPolicy(event);
|
|
break;
|
|
case "moments_user_event":
|
|
event = await this.applyMomentsPolicy(event);
|
|
break;
|
|
case "menu_message_user_event":
|
|
event = await this.applyMenuMessagePolicy(event);
|
|
break;
|
|
case "asrouter_undesired_event":
|
|
event = this.applyUndesiredEventPolicy(event);
|
|
break;
|
|
case "newtab_message_user_event":
|
|
event = await this.applyNewtabMessagePolicy(event);
|
|
break;
|
|
default:
|
|
event = { ping: event };
|
|
break;
|
|
}
|
|
return event;
|
|
}
|
|
|
|
/**
|
|
* Per Bug 1484035, CFR metrics comply with following policies:
|
|
* 1). In release, it collects impression_id and bucket_id
|
|
* 2). In prerelease, it collects client_id and message_id
|
|
* 3). In shield experiments conducted in release, it collects client_id and message_id
|
|
* 4). In Private Browsing windows, unless in experiment, collects impression_id and bucket_id
|
|
*/
|
|
async applyCFRPolicy(ping) {
|
|
if (
|
|
(lazy.UpdateUtils.getUpdateChannel(true) === "release" ||
|
|
ping.is_private) &&
|
|
!this.isInCFRCohort
|
|
) {
|
|
ping.message_id = "n/a";
|
|
ping.impression_id = this._impressionId;
|
|
} else {
|
|
ping.client_id = await this.telemetryClientId;
|
|
}
|
|
delete ping.action;
|
|
delete ping.is_private;
|
|
return { ping, pingType: "cfr" };
|
|
}
|
|
|
|
/**
|
|
* Per Bug 1482134, all the metrics for What's New panel use client_id in
|
|
* all the release channels
|
|
*/
|
|
async applyToolbarBadgePolicy(ping) {
|
|
ping.client_id = await this.telemetryClientId;
|
|
ping.browser_session_id = lazy.browserSessionId;
|
|
// Attach page info to `event_context` if there is a session associated with this ping
|
|
delete ping.action;
|
|
return { ping, pingType: "toolbar-badge" };
|
|
}
|
|
|
|
async applyInfoBarPolicy(ping) {
|
|
ping.client_id = await this.telemetryClientId;
|
|
ping.browser_session_id = lazy.browserSessionId;
|
|
delete ping.action;
|
|
return { ping, pingType: "infobar" };
|
|
}
|
|
|
|
async applySpotlightPolicy(ping) {
|
|
ping.client_id = await this.telemetryClientId;
|
|
ping.browser_session_id = lazy.browserSessionId;
|
|
delete ping.action;
|
|
return { ping, pingType: "spotlight" };
|
|
}
|
|
|
|
async applyToastNotificationPolicy(ping) {
|
|
ping.client_id = await this.telemetryClientId;
|
|
ping.browser_session_id = lazy.browserSessionId;
|
|
delete ping.action;
|
|
return { ping, pingType: "toast_notification" };
|
|
}
|
|
|
|
async applyMenuMessagePolicy(ping) {
|
|
ping.client_id = await this.telemetryClientId;
|
|
ping.browser_session_id = lazy.browserSessionId;
|
|
delete ping.action;
|
|
return { ping, pingType: "menu" };
|
|
}
|
|
|
|
/**
|
|
* Per Bug 1484035, Moments metrics comply with following policies:
|
|
* 1). In release, it collects impression_id, and treats bucket_id as message_id
|
|
* 2). In prerelease, it collects client_id and message_id
|
|
* 3). In shield experiments conducted in release, it collects client_id and message_id
|
|
*/
|
|
async applyMomentsPolicy(ping) {
|
|
if (
|
|
lazy.UpdateUtils.getUpdateChannel(true) === "release" &&
|
|
!this.isInCFRCohort
|
|
) {
|
|
ping.message_id = "n/a";
|
|
ping.impression_id = this._impressionId;
|
|
} else {
|
|
ping.client_id = await this.telemetryClientId;
|
|
}
|
|
delete ping.action;
|
|
return { ping, pingType: "moments" };
|
|
}
|
|
|
|
async applyNewtabMessagePolicy(ping) {
|
|
ping.client_id = await this.telemetryClientId;
|
|
ping.browser_session_id = lazy.browserSessionId;
|
|
ping.addon_version = Services.appinfo.appBuildID;
|
|
ping.locale = Services.locale.appLocaleAsBCP47;
|
|
delete ping.action;
|
|
return { ping, pingType: "newtab_message" };
|
|
}
|
|
|
|
applyUndesiredEventPolicy(ping) {
|
|
ping.impression_id = this._impressionId;
|
|
delete ping.action;
|
|
return { ping, pingType: "undesired-events" };
|
|
}
|
|
|
|
async handleASRouterUserEvent(action) {
|
|
const { ping, pingType } = await this.createASRouterEvent(action);
|
|
if (!pingType) {
|
|
console.error("Unknown ping type for ASRouter telemetry");
|
|
return;
|
|
}
|
|
|
|
// Now that the action has become a ping, we can echo it to Glean.
|
|
if (this.telemetryEnabled) {
|
|
lazy.Telemetry.submitGleanPingForPing({ ...ping, pingType });
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This function is used by ActivityStreamStorage to report errors
|
|
* trying to access IndexedDB.
|
|
*/
|
|
SendASRouterUndesiredEvent(data) {
|
|
this.handleASRouterUserEvent({
|
|
data: { ...data, action: "asrouter_undesired_event" },
|
|
});
|
|
}
|
|
|
|
onAction(action) {
|
|
switch (action.type) {
|
|
// The remaining action types come from ASRouter, which doesn't use
|
|
// Actions from Actions.mjs, but uses these other custom strings.
|
|
case msg.TOOLBAR_BADGE_TELEMETRY:
|
|
// Intentional fall-through
|
|
case msg.TOOLBAR_PANEL_TELEMETRY:
|
|
// Intentional fall-through
|
|
case msg.MOMENTS_PAGE_TELEMETRY:
|
|
// Intentional fall-through
|
|
case msg.DOORHANGER_TELEMETRY:
|
|
// Intentional fall-through
|
|
case msg.INFOBAR_TELEMETRY:
|
|
// Intentional fall-through
|
|
case msg.SPOTLIGHT_TELEMETRY:
|
|
// Intentional fall-through
|
|
case msg.TOAST_NOTIFICATION_TELEMETRY:
|
|
// Intentional fall-through
|
|
case msg.MENU_MESSAGE_TELEMETRY:
|
|
// Intentional fall-through
|
|
case msg.NEWTAB_MESSAGE_TELEMETRY:
|
|
// Intentional fall-through
|
|
case msg.AS_ROUTER_TELEMETRY_USER_EVENT:
|
|
this.handleASRouterUserEvent(action);
|
|
break;
|
|
}
|
|
}
|
|
}
|