summaryrefslogtreecommitdiffstats
path: root/browser/components/newtab/aboutwelcome/lib/AboutWelcomeTelemetry.jsm
blob: 7725ae6c2250434722c22390a4e950429b0c6dd0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
/* 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/. */
"use strict";

const EXPORTED_SYMBOLS = ["AboutWelcomeTelemetry"];
const { XPCOMUtils } = ChromeUtils.importESModule(
  "resource://gre/modules/XPCOMUtils.sys.mjs"
);

const lazy = {};

ChromeUtils.defineESModuleGetters(lazy, {
  ClientID: "resource://gre/modules/ClientID.sys.mjs",
  TelemetrySession: "resource://gre/modules/TelemetrySession.sys.mjs",
});

XPCOMUtils.defineLazyModuleGetters(lazy, {
  PingCentre: "resource:///modules/PingCentre.jsm",
  AttributionCode: "resource:///modules/AttributionCode.jsm",
});
XPCOMUtils.defineLazyPreferenceGetter(
  lazy,
  "structuredIngestionEndpointBase",
  "browser.newtabpage.activity-stream.telemetry.structuredIngestion.endpoint",
  ""
);
XPCOMUtils.defineLazyGetter(lazy, "telemetryClientId", () =>
  lazy.ClientID.getClientID()
);
XPCOMUtils.defineLazyGetter(
  lazy,
  "browserSessionId",
  () => lazy.TelemetrySession.getMetadata("").sessionId
);
const TELEMETRY_TOPIC = "about:welcome";
const PING_TYPE = "onboarding";
const PING_VERSION = "1";
const STRUCTURED_INGESTION_NAMESPACE_MS = "messaging-system";

class AboutWelcomeTelemetry {
  constructor() {
    XPCOMUtils.defineLazyPreferenceGetter(
      this,
      "telemetryEnabled",
      "browser.newtabpage.activity-stream.telemetry",
      false
    );
  }

  /**
   * Lazily initialize PingCentre for Activity Stream to send pings
   */
  get pingCentre() {
    Object.defineProperty(this, "pingCentre", {
      value: new lazy.PingCentre({ topic: TELEMETRY_TOPIC }),
    });
    return this.pingCentre;
  }

  _generateStructuredIngestionEndpoint() {
    const uuid = Services.uuid.generateUUID().toString();
    // Structured Ingestion does not support the UUID generated by Services.uuid,
    // because it contains leading and trailing braces. Need to trim them first.
    const docID = uuid.slice(1, -1);
    const extension = `${STRUCTURED_INGESTION_NAMESPACE_MS}/${PING_TYPE}/${PING_VERSION}/${docID}`;
    return `${lazy.structuredIngestionEndpointBase}/${extension}`;
  }

  /**
   * Attach browser attribution data to a ping payload.
   *
   * It intentionally queries the *cached* attribution data other than calling
   * `getAttrDataAsync()` in order to minimize the overhead here.
   * For the same reason, we are not querying the attribution data from
   * `TelemetryEnvironment.currentEnvironment.settings`.
   *
   * In practice, it's very likely that the attribution data is already read
   * and cached at some point by `AboutWelcomeParent`, so it should be able to
   * read the cached results for the most if not all of the pings.
   */
  _maybeAttachAttribution(ping) {
    const attribution = lazy.AttributionCode.getCachedAttributionData();
    if (attribution && Object.keys(attribution).length) {
      ping.attribution = attribution;
    }
    return ping;
  }

  async _createPing(event) {
    if (event.event_context && typeof event.event_context === "object") {
      event.event_context = JSON.stringify(event.event_context);
    }
    let ping = {
      ...event,
      addon_version: Services.appinfo.appBuildID,
      locale: Services.locale.appLocaleAsBCP47,
      client_id: await lazy.telemetryClientId,
      browser_session_id: lazy.browserSessionId,
    };

    return this._maybeAttachAttribution(ping);
  }

  async sendTelemetry(event) {
    if (!this.telemetryEnabled) {
      return;
    }

    const ping = await this._createPing(event);
    this.pingCentre.sendStructuredIngestionPing(
      ping,
      this._generateStructuredIngestionEndpoint()
    );
  }
}