tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

browser_ext_browserAction_telemetry.js (11352B)


      1 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
      2 /* vim: set sts=2 sw=2 et tw=80: */
      3 "use strict";
      4 
      5 const TIMING_HISTOGRAM = "WEBEXT_BROWSERACTION_POPUP_OPEN_MS";
      6 const TIMING_HISTOGRAM_KEYED = "WEBEXT_BROWSERACTION_POPUP_OPEN_MS_BY_ADDONID";
      7 const RESULT_HISTOGRAM = "WEBEXT_BROWSERACTION_POPUP_PRELOAD_RESULT_COUNT";
      8 const RESULT_HISTOGRAM_KEYED =
      9  "WEBEXT_BROWSERACTION_POPUP_PRELOAD_RESULT_COUNT_BY_ADDONID";
     10 
     11 const EXTENSION_ID1 = "@test-extension1";
     12 const EXTENSION_ID2 = "@test-extension2";
     13 
     14 // Keep this in sync with the order in Histograms.json for
     15 // WEBEXT_BROWSERACTION_POPUP_PRELOAD_RESULT_COUNT
     16 const CATEGORIES = ["popupShown", "clearAfterHover", "clearAfterMousedown"];
     17 const GLEAN_RESULT_LABELS = [...CATEGORIES, "__other__"];
     18 
     19 function assertGleanPreloadResultLabelCounter(expectedLabelsValue) {
     20  for (const label of GLEAN_RESULT_LABELS) {
     21    const expectedLabelValue = expectedLabelsValue[label];
     22    Assert.deepEqual(
     23      Glean.extensionsCounters.browserActionPreloadResult[label].testGetValue(),
     24      expectedLabelValue,
     25      `Expect Glean browserActionPreloadResult metric label ${label} to be ${
     26        expectedLabelValue > 0 ? expectedLabelValue : "empty"
     27      }`
     28    );
     29  }
     30 }
     31 
     32 function assertGleanPreloadResultLabelCounterEmpty() {
     33  // All empty labels passed to the other helpers to make it
     34  // assert that all labels are empty.
     35  assertGleanPreloadResultLabelCounter({});
     36 }
     37 
     38 /**
     39 * Takes a Telemetry histogram snapshot and makes sure
     40 * that the index for that value (as defined by CATEGORIES)
     41 * has a count of 1, and that it's the only value that
     42 * has been incremented.
     43 *
     44 * @param {object} snapshot
     45 *        The Telemetry histogram snapshot to examine.
     46 * @param {string} category
     47 *        The category in CATEGORIES whose index we expect to have
     48 *        been set to 1.
     49 */
     50 function assertOnlyOneTypeSet(snapshot, category) {
     51  let categoryIndex = CATEGORIES.indexOf(category);
     52  Assert.equal(
     53    snapshot.values[categoryIndex],
     54    1,
     55    `Should have seen the ${category} count increment.`
     56  );
     57  // Use Array.prototype.reduce to sum up all of the
     58  // snapshot.count entries
     59  Assert.equal(
     60    Object.values(snapshot.values).reduce((a, b) => a + b, 0),
     61    1,
     62    "Should only be 1 collected value."
     63  );
     64 }
     65 
     66 add_task(async function testBrowserActionTelemetryTiming() {
     67  let extensionOptions = {
     68    manifest: {
     69      browser_action: {
     70        default_popup: "popup.html",
     71        default_area: "navbar",
     72        browser_style: true,
     73      },
     74    },
     75 
     76    files: {
     77      "popup.html": `<!DOCTYPE html><html><head><meta charset="utf-8"></head><body><div></div></body></html>`,
     78    },
     79  };
     80  let extension1 = ExtensionTestUtils.loadExtension({
     81    ...extensionOptions,
     82    manifest: {
     83      ...extensionOptions.manifest,
     84      browser_specific_settings: {
     85        gecko: { id: EXTENSION_ID1 },
     86      },
     87    },
     88  });
     89  let extension2 = ExtensionTestUtils.loadExtension({
     90    ...extensionOptions,
     91    manifest: {
     92      ...extensionOptions.manifest,
     93      browser_specific_settings: {
     94        gecko: { id: EXTENSION_ID2 },
     95      },
     96    },
     97  });
     98 
     99  let histogram = Services.telemetry.getHistogramById(TIMING_HISTOGRAM);
    100  let histogramKeyed = Services.telemetry.getKeyedHistogramById(
    101    TIMING_HISTOGRAM_KEYED
    102  );
    103 
    104  histogram.clear();
    105  histogramKeyed.clear();
    106  Services.fog.testResetFOG();
    107 
    108  is(
    109    histogram.snapshot().sum,
    110    0,
    111    `No data recorded for histogram: ${TIMING_HISTOGRAM}.`
    112  );
    113  is(
    114    Object.keys(histogramKeyed).length,
    115    0,
    116    `No data recorded for histogram: ${TIMING_HISTOGRAM_KEYED}.`
    117  );
    118  Assert.deepEqual(
    119    Glean.extensionsTiming.browserActionPopupOpen.testGetValue(),
    120    undefined,
    121    "No data recorded for glean metric extensionsTiming.browserActionPopupOpen"
    122  );
    123 
    124  await extension1.startup();
    125  await extension2.startup();
    126 
    127  is(
    128    histogram.snapshot().sum,
    129    0,
    130    `No data recorded for histogram after startup: ${TIMING_HISTOGRAM}.`
    131  );
    132  is(
    133    Object.keys(histogramKeyed).length,
    134    0,
    135    `No data recorded for histogram after startup: ${TIMING_HISTOGRAM_KEYED}.`
    136  );
    137  Assert.deepEqual(
    138    Glean.extensionsTiming.browserActionPopupOpen.testGetValue(),
    139    undefined,
    140    "No data recorded for glean metric extensionsTiming.browserActionPopupOpen"
    141  );
    142 
    143  clickBrowserAction(extension1);
    144  await awaitExtensionPanel(extension1);
    145  let sumOld = histogram.snapshot().sum;
    146  Assert.greater(
    147    sumOld,
    148    0,
    149    `Data recorded for first extension for histogram: ${TIMING_HISTOGRAM}.`
    150  );
    151 
    152  let oldKeyedSnapshot = histogramKeyed.snapshot();
    153  Assert.deepEqual(
    154    Object.keys(oldKeyedSnapshot),
    155    [EXTENSION_ID1],
    156    `Data recorded for first extension for histogram: ${TIMING_HISTOGRAM_KEYED}.`
    157  );
    158  Assert.greater(
    159    oldKeyedSnapshot[EXTENSION_ID1].sum,
    160    0,
    161    `Data recorded for first extension for histogram: ${TIMING_HISTOGRAM_KEYED}.`
    162  );
    163 
    164  let gleanSumOld =
    165    Glean.extensionsTiming.browserActionPopupOpen.testGetValue()?.sum;
    166  Assert.greater(
    167    gleanSumOld,
    168    0,
    169    "Data recorded for first extension on glean metric extensionsTiming.browserActionPopupOpen"
    170  );
    171 
    172  await closeBrowserAction(extension1);
    173 
    174  clickBrowserAction(extension2);
    175  await awaitExtensionPanel(extension2);
    176  let sumNew = histogram.snapshot().sum;
    177  Assert.greater(
    178    sumNew,
    179    sumOld,
    180    `Data recorded for second extension for histogram: ${TIMING_HISTOGRAM}.`
    181  );
    182  sumOld = sumNew;
    183 
    184  let gleanSumNew =
    185    Glean.extensionsTiming.browserActionPopupOpen.testGetValue()?.sum;
    186  Assert.greater(
    187    gleanSumNew,
    188    gleanSumOld,
    189    "Data recorded for second extension on glean metric extensionsTiming.browserActionPopupOpen"
    190  );
    191  gleanSumOld = gleanSumNew;
    192 
    193  let newKeyedSnapshot = histogramKeyed.snapshot();
    194  Assert.deepEqual(
    195    Object.keys(newKeyedSnapshot).sort(),
    196    [EXTENSION_ID1, EXTENSION_ID2],
    197    `Data recorded for second extension for histogram: ${TIMING_HISTOGRAM_KEYED}.`
    198  );
    199  Assert.greater(
    200    newKeyedSnapshot[EXTENSION_ID2].sum,
    201    0,
    202    `Data recorded for second extension for histogram: ${TIMING_HISTOGRAM_KEYED}.`
    203  );
    204  is(
    205    newKeyedSnapshot[EXTENSION_ID1].sum,
    206    oldKeyedSnapshot[EXTENSION_ID1].sum,
    207    `Data recorded for first extension should not change for histogram: ${TIMING_HISTOGRAM_KEYED}.`
    208  );
    209  oldKeyedSnapshot = newKeyedSnapshot;
    210 
    211  await closeBrowserAction(extension2);
    212 
    213  clickBrowserAction(extension2);
    214  await awaitExtensionPanel(extension2);
    215  sumNew = histogram.snapshot().sum;
    216  Assert.greater(
    217    sumNew,
    218    sumOld,
    219    `Data recorded for second opening of popup for histogram: ${TIMING_HISTOGRAM}.`
    220  );
    221  sumOld = sumNew;
    222 
    223  gleanSumNew =
    224    Glean.extensionsTiming.browserActionPopupOpen.testGetValue()?.sum;
    225  Assert.greater(
    226    gleanSumNew,
    227    gleanSumOld,
    228    "Data recorded for second popup opening on glean metric extensionsTiming.browserActionPopupOpen"
    229  );
    230  gleanSumOld = gleanSumNew;
    231 
    232  newKeyedSnapshot = histogramKeyed.snapshot();
    233  Assert.greater(
    234    newKeyedSnapshot[EXTENSION_ID2].sum,
    235    oldKeyedSnapshot[EXTENSION_ID2].sum,
    236    `Data recorded for second opening of popup for histogram: ${TIMING_HISTOGRAM_KEYED}.`
    237  );
    238  is(
    239    newKeyedSnapshot[EXTENSION_ID1].sum,
    240    oldKeyedSnapshot[EXTENSION_ID1].sum,
    241    `Data recorded for first extension should not change for histogram: ${TIMING_HISTOGRAM_KEYED}.`
    242  );
    243  oldKeyedSnapshot = newKeyedSnapshot;
    244 
    245  await closeBrowserAction(extension2);
    246 
    247  clickBrowserAction(extension1);
    248  await awaitExtensionPanel(extension1);
    249  sumNew = histogram.snapshot().sum;
    250  Assert.greater(
    251    sumNew,
    252    sumOld,
    253    `Data recorded for third opening of popup for histogram: ${TIMING_HISTOGRAM}.`
    254  );
    255 
    256  gleanSumNew =
    257    Glean.extensionsTiming.browserActionPopupOpen.testGetValue()?.sum;
    258  Assert.greater(
    259    gleanSumNew,
    260    gleanSumOld,
    261    "Data recorded for third popup opening on glean metric extensionsTiming.browserActionPopupOpen"
    262  );
    263 
    264  newKeyedSnapshot = histogramKeyed.snapshot();
    265  Assert.greater(
    266    newKeyedSnapshot[EXTENSION_ID1].sum,
    267    oldKeyedSnapshot[EXTENSION_ID1].sum,
    268    `Data recorded for second opening of popup for histogram: ${TIMING_HISTOGRAM_KEYED}.`
    269  );
    270  is(
    271    newKeyedSnapshot[EXTENSION_ID2].sum,
    272    oldKeyedSnapshot[EXTENSION_ID2].sum,
    273    `Data recorded for second extension should not change for histogram: ${TIMING_HISTOGRAM_KEYED}.`
    274  );
    275 
    276  await closeBrowserAction(extension1);
    277 
    278  await extension1.unload();
    279  await extension2.unload();
    280 });
    281 
    282 add_task(async function testBrowserActionTelemetryResults() {
    283  let extensionOptions = {
    284    manifest: {
    285      browser_specific_settings: {
    286        gecko: { id: EXTENSION_ID1 },
    287      },
    288      browser_action: {
    289        default_popup: "popup.html",
    290        default_area: "navbar",
    291        browser_style: true,
    292      },
    293    },
    294 
    295    files: {
    296      "popup.html": `<!DOCTYPE html><html><head><meta charset="utf-8"></head><body><div></div></body></html>`,
    297    },
    298  };
    299  let extension = ExtensionTestUtils.loadExtension(extensionOptions);
    300 
    301  let histogram = Services.telemetry.getHistogramById(RESULT_HISTOGRAM);
    302  let histogramKeyed = Services.telemetry.getKeyedHistogramById(
    303    RESULT_HISTOGRAM_KEYED
    304  );
    305 
    306  histogram.clear();
    307  histogramKeyed.clear();
    308  Services.fog.testResetFOG();
    309 
    310  is(
    311    histogram.snapshot().sum,
    312    0,
    313    `No data recorded for histogram: ${RESULT_HISTOGRAM}.`
    314  );
    315  is(
    316    Object.keys(histogramKeyed).length,
    317    0,
    318    `No data recorded for histogram: ${RESULT_HISTOGRAM_KEYED}.`
    319  );
    320  assertGleanPreloadResultLabelCounterEmpty();
    321 
    322  await extension.startup();
    323 
    324  // Make sure the mouse isn't hovering over the browserAction widget to start.
    325  EventUtils.synthesizeMouseAtCenter(gURLBar, { type: "mouseover" }, window);
    326 
    327  let widget = getBrowserActionWidget(extension).forWindow(window);
    328 
    329  // Hover the mouse over the browserAction widget and then move it away.
    330  EventUtils.synthesizeMouseAtCenter(
    331    widget.node,
    332    { type: "mouseover", button: 0 },
    333    window
    334  );
    335  EventUtils.synthesizeMouseAtCenter(
    336    widget.node,
    337    { type: "mouseout", button: 0 },
    338    window
    339  );
    340  EventUtils.synthesizeMouseAtCenter(
    341    document.documentElement,
    342    { type: "mousemove" },
    343    window
    344  );
    345 
    346  assertOnlyOneTypeSet(histogram.snapshot(), "clearAfterHover");
    347  assertGleanPreloadResultLabelCounter({ clearAfterHover: 1 });
    348 
    349  let keyedSnapshot = histogramKeyed.snapshot();
    350  Assert.deepEqual(
    351    Object.keys(keyedSnapshot),
    352    [EXTENSION_ID1],
    353    `Data recorded for histogram: ${RESULT_HISTOGRAM_KEYED}.`
    354  );
    355  assertOnlyOneTypeSet(keyedSnapshot[EXTENSION_ID1], "clearAfterHover");
    356 
    357  histogram.clear();
    358  histogramKeyed.clear();
    359  Services.fog.testResetFOG();
    360 
    361  // TODO: Create a test for cancel after mousedown.
    362  // This is tricky because calling mouseout after mousedown causes a
    363  // "Hover" event to be added to the queue in ext-browserAction.js.
    364 
    365  clickBrowserAction(extension);
    366  await awaitExtensionPanel(extension);
    367 
    368  assertOnlyOneTypeSet(histogram.snapshot(), "popupShown");
    369  assertGleanPreloadResultLabelCounter({ popupShown: 1 });
    370 
    371  keyedSnapshot = histogramKeyed.snapshot();
    372  Assert.deepEqual(
    373    Object.keys(keyedSnapshot),
    374    [EXTENSION_ID1],
    375    `Data recorded for histogram: ${RESULT_HISTOGRAM_KEYED}.`
    376  );
    377  assertOnlyOneTypeSet(keyedSnapshot[EXTENSION_ID1], "popupShown");
    378 
    379  await closeBrowserAction(extension);
    380 
    381  await extension.unload();
    382 });