summaryrefslogtreecommitdiffstats
path: root/browser/base/content/test/about/browser_aboutCertError_telemetry.js
blob: 61ec8afcbf94ab546339edfe2049bab80faa5cd8 (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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

requestLongerTimeout(2);

const BAD_CERT = "https://expired.example.com/";
const BAD_STS_CERT =
  "https://badchain.include-subdomains.pinning.example.com:443";

add_task(async function checkTelemetryClickEvents() {
  info("Loading a bad cert page and verifying telemetry click events arrive.");

  let oldCanRecord = Services.telemetry.canRecordExtended;
  Services.telemetry.canRecordExtended = true;

  registerCleanupFunction(() => {
    Services.telemetry.canRecordExtended = oldCanRecord;
  });

  // For obvious reasons event telemetry in the content processes updates with
  // the main processs asynchronously, so we need to wait for the main process
  // to catch up through the entire test.

  // There's an arbitrary interval of 2 seconds in which the content
  // processes sync their event data with the parent process, we wait
  // this out to ensure that we clear everything that is left over from
  // previous tests and don't receive random events in the middle of our tests.
  // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
  await new Promise(c => setTimeout(c, 2000));

  // Clear everything.
  Services.telemetry.clearEvents();
  await TestUtils.waitForCondition(() => {
    let events = Services.telemetry.snapshotEvents(
      Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS,
      true
    ).content;
    return !events || !events.length;
  });

  // Now enable recording our telemetry. Even if this is disabled, content
  // processes will send event telemetry to the parent, thus we needed to ensure
  // we waited and cleared first. Sigh.
  Services.telemetry.setEventRecordingEnabled("security.ui.certerror", true);

  for (let useFrame of [false, true]) {
    let recordedObjects = [
      "advanced_button",
      "learn_more_link",
      "error_code_link",
      "clipboard_button_top",
      "clipboard_button_bot",
      "return_button_top",
    ];

    recordedObjects.push("return_button_adv");
    if (!useFrame) {
      recordedObjects.push("exception_button");
    }

    for (let object of recordedObjects) {
      let tab = await openErrorPage(BAD_CERT, useFrame);
      let browser = tab.linkedBrowser;

      let loadEvents = await TestUtils.waitForCondition(() => {
        let events = Services.telemetry.snapshotEvents(
          Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS,
          true
        ).content;
        if (events && events.length) {
          events = events.filter(
            e => e[1] == "security.ui.certerror" && e[2] == "load"
          );
          if (
            events.length == 1 &&
            events[0][5].is_frame == useFrame.toString()
          ) {
            return events;
          }
        }
        return null;
      }, "recorded telemetry for the load");

      is(
        loadEvents.length,
        1,
        `recorded telemetry for the load testing ${object}, useFrame: ${useFrame}`
      );

      let bc = browser.browsingContext;
      if (useFrame) {
        bc = bc.children[0];
      }

      await SpecialPowers.spawn(bc, [object], async function (objectId) {
        let doc = content.document;

        await ContentTaskUtils.waitForCondition(
          () => doc.body.classList.contains("certerror"),
          "Wait for certerror to be loaded"
        );

        let domElement = doc.querySelector(`[data-telemetry-id='${objectId}']`);
        domElement.click();
      });

      let clickEvents = await TestUtils.waitForCondition(() => {
        let events = Services.telemetry.snapshotEvents(
          Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS,
          true
        ).content;
        if (events && events.length) {
          events = events.filter(
            e =>
              e[1] == "security.ui.certerror" &&
              e[2] == "click" &&
              e[3] == object
          );
          if (
            events.length == 1 &&
            events[0][5].is_frame == useFrame.toString()
          ) {
            return events;
          }
        }
        return null;
      }, "Has captured telemetry events.");

      is(
        clickEvents.length,
        1,
        `recorded telemetry for the click on ${object}, useFrame: ${useFrame}`
      );

      // We opened an extra tab for the SUMO page, need to close it.
      if (object == "learn_more_link") {
        BrowserTestUtils.removeTab(gBrowser.selectedTab);
      }

      if (object == "exception_button") {
        let certOverrideService = Cc[
          "@mozilla.org/security/certoverride;1"
        ].getService(Ci.nsICertOverrideService);
        certOverrideService.clearValidityOverride(
          "expired.example.com",
          -1,
          {}
        );
      }

      BrowserTestUtils.removeTab(gBrowser.selectedTab);
    }
  }

  let enableCertErrorUITelemetry = Services.prefs.getBoolPref(
    "security.certerrors.recordEventTelemetry"
  );
  Services.telemetry.setEventRecordingEnabled(
    "security.ui.certerror",
    enableCertErrorUITelemetry
  );
});