tor-browser

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

head.js (8785B)


      1 "use strict";
      2 
      3 const { TelemetryTestUtils } = ChromeUtils.importESModule(
      4  "resource://testing-common/TelemetryTestUtils.sys.mjs"
      5 );
      6 
      7 /* eslint-disable mozilla/no-redeclare-with-import-autofix */
      8 const { ContentTaskUtils } = ChromeUtils.importESModule(
      9  "resource://testing-common/ContentTaskUtils.sys.mjs"
     10 );
     11 
     12 /**
     13 * Returns a Promise that resolves once a crash report has
     14 * been submitted. This function will also test the crash
     15 * reports extra data to see if it matches expectedExtra.
     16 *
     17 * @param expectedExtra (object)
     18 *        An Object whose key-value pairs will be compared
     19 *        against the key-value pairs in the extra data of the
     20 *        crash report. A test failure will occur if there is
     21 *        a mismatch.
     22 *
     23 *        If the value of the key-value pair is "null", this will
     24 *        be interpreted as "this key should not be included in the
     25 *        extra data", and will cause a test failure if it is detected
     26 *        in the crash report.
     27 *
     28 *        Note that this will ignore any keys that are not included
     29 *        in expectedExtra. It's possible that the crash report
     30 *        will contain other extra information that is not
     31 *        compared against.
     32 * @returns Promise
     33 */
     34 function promiseCrashReport(expectedExtra = {}) {
     35  return (async function () {
     36    info("Starting wait on crash-report-status");
     37    let [subject] = await TestUtils.topicObserved(
     38      "crash-report-status",
     39      (unused, data) => {
     40        return data == "success";
     41      }
     42    );
     43    info("Topic observed!");
     44 
     45    if (!(subject instanceof Ci.nsIPropertyBag2)) {
     46      throw new Error("Subject was not a Ci.nsIPropertyBag2");
     47    }
     48 
     49    let remoteID = getPropertyBagValue(subject, "serverCrashID");
     50    if (!remoteID) {
     51      throw new Error("Report should have a server ID");
     52    }
     53 
     54    let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
     55    file.initWithPath(Services.crashmanager._submittedDumpsDir);
     56    file.append(remoteID + ".txt");
     57    if (!file.exists()) {
     58      throw new Error("Report should have been received by the server");
     59    }
     60 
     61    file.remove(false);
     62 
     63    let extra = getPropertyBagValue(subject, "extra");
     64    if (!(extra instanceof Ci.nsIPropertyBag2)) {
     65      throw new Error("extra was not a Ci.nsIPropertyBag2");
     66    }
     67 
     68    info("Iterating crash report extra keys");
     69    for (let { name: key } of extra.enumerator) {
     70      let value = extra.getPropertyAsAString(key);
     71      if (key in expectedExtra) {
     72        if (expectedExtra[key] == null) {
     73          ok(false, `Got unexpected key ${key} with value ${value}`);
     74        } else {
     75          is(
     76            value,
     77            expectedExtra[key],
     78            `Crash report had the right extra value for ${key}`
     79          );
     80        }
     81      }
     82    }
     83  })();
     84 }
     85 
     86 function promiseCrashReportFail() {
     87  return (async function () {
     88    info("Starting wait on crash-report-status");
     89    await TestUtils.topicObserved("crash-report-status", (unused, data) => {
     90      return data == "failed";
     91    });
     92    info("Topic observed!");
     93  })();
     94 }
     95 
     96 /**
     97 * For an nsIPropertyBag, returns the value for a given
     98 * key.
     99 *
    100 * @param bag
    101 *        The nsIPropertyBag to retrieve the value from
    102 * @param key
    103 *        The key that we want to get the value for from the
    104 *        bag
    105 * @returns The value corresponding to the key from the bag,
    106 *          or null if the value could not be retrieved (for
    107 *          example, if no value is set at that key).
    108 */
    109 function getPropertyBagValue(bag, key) {
    110  try {
    111    let val = bag.getProperty(key);
    112    return val;
    113  } catch (e) {
    114    if (e.result != Cr.NS_ERROR_FAILURE) {
    115      throw e;
    116    }
    117  }
    118 
    119  return null;
    120 }
    121 
    122 /**
    123 * Sets up the browser to send crash reports to the local crash report
    124 * testing server.
    125 */
    126 async function setupLocalCrashReportServer() {
    127  const SERVER_URL =
    128    // eslint-disable-next-line @microsoft/sdl/no-insecure-url
    129    "http://example.com/browser/toolkit/crashreporter/test/browser/crashreport.sjs";
    130 
    131  // The test harness sets MOZ_CRASHREPORTER_NO_REPORT, which disables crash
    132  // reports.  This test needs them enabled.  The test also needs a mock
    133  // report server, and fortunately one is already set up by toolkit/
    134  // crashreporter/test/Makefile.in.  Assign its URL to MOZ_CRASHREPORTER_URL,
    135  // which CrashSubmit.sys.mjs uses as a server override.
    136  let noReport = Services.env.get("MOZ_CRASHREPORTER_NO_REPORT");
    137  let serverUrl = Services.env.get("MOZ_CRASHREPORTER_URL");
    138  Services.env.set("MOZ_CRASHREPORTER_NO_REPORT", "");
    139  Services.env.set("MOZ_CRASHREPORTER_URL", SERVER_URL);
    140 
    141  registerCleanupFunction(function () {
    142    Services.env.set("MOZ_CRASHREPORTER_NO_REPORT", noReport);
    143    Services.env.set("MOZ_CRASHREPORTER_URL", serverUrl);
    144  });
    145 }
    146 
    147 /**
    148 * Monkey patches TabCrashHandler.getDumpID to return null in order to test
    149 * about:tabcrashed when a dump is not available.
    150 */
    151 function prepareNoDump() {
    152  let originalGetDumpID = TabCrashHandler.getDumpID;
    153  TabCrashHandler.getDumpID = function () {
    154    return null;
    155  };
    156  registerCleanupFunction(() => {
    157    TabCrashHandler.getDumpID = originalGetDumpID;
    158  });
    159 }
    160 
    161 const kBuildidMatchEnv = "MOZ_BUILDID_MATCH_DONTSEND";
    162 
    163 function setBuildidMatchDontSendEnv() {
    164  info("Setting " + kBuildidMatchEnv + "=1");
    165  Services.env.set(kBuildidMatchEnv, "1");
    166 }
    167 
    168 function unsetBuildidMatchDontSendEnv() {
    169  info("Unsetting " + kBuildidMatchEnv);
    170  Services.env.set(kBuildidMatchEnv, "0");
    171 }
    172 
    173 const kBuildidMismatchEnv = "MOZ_FORCE_BUILDID_MISMATCH";
    174 
    175 function setBuildidMismatchEnv() {
    176  info("Setting " + kBuildidMismatchEnv + "=1");
    177  Services.env.set(kBuildidMismatchEnv, "1");
    178 }
    179 
    180 function unsetBuildidMismatchEnv() {
    181  info("Unsetting " + kBuildidMismatchEnv);
    182  Services.env.set(kBuildidMismatchEnv, "0");
    183 }
    184 
    185 function getEventPromise(eventName, eventKind) {
    186  return new Promise(function (resolve) {
    187    info("Installing event listener (" + eventKind + ")");
    188    window.addEventListener(
    189      eventName,
    190      () => {
    191        ok(true, "Received " + eventName + " (" + eventKind + ") event");
    192        info("Call resolve() for " + eventKind + " event");
    193        resolve();
    194      },
    195      { once: true }
    196    );
    197    info("Installed event listener (" + eventKind + ")");
    198  });
    199 }
    200 
    201 async function openNewTab(forceCrash) {
    202  const PAGE =
    203    "data:text/html,<html><body>A%20regular,%20everyday,%20normal%20page.";
    204 
    205  let options = {
    206    gBrowser,
    207    PAGE,
    208    waitForLoad: false,
    209    waitForStateStop: false,
    210    forceNewProcess: true,
    211  };
    212 
    213  let tab = await BrowserTestUtils.openNewForegroundTab(options);
    214  if (forceCrash === true) {
    215    let browser = tab.linkedBrowser;
    216    await BrowserTestUtils.crashFrame(
    217      browser,
    218      /* shouldShowTabCrashPage */ false,
    219      /* shouldClearMinidumps */ true,
    220      /* BrowsingContext */ null
    221    );
    222  }
    223 
    224  return tab;
    225 }
    226 
    227 async function closeTab(tab) {
    228  await TestUtils.waitForTick();
    229  BrowserTestUtils.removeTab(tab);
    230 }
    231 
    232 const kInterval = 100; /* ms */
    233 const kRetries = 5;
    234 
    235 /**
    236 * This function waits until utility scalars are reported into the
    237 * scalar snapshot.
    238 */
    239 async function waitForProcessScalars(name) {
    240  await ContentTaskUtils.waitForCondition(
    241    () => {
    242      const scalars = TelemetryTestUtils.getProcessScalars("parent");
    243      return Object.keys(scalars).includes(name);
    244    },
    245    `Waiting for ${name} scalars to have been set`,
    246    kInterval,
    247    kRetries
    248  );
    249 }
    250 
    251 async function getTelemetry(name) {
    252  try {
    253    await waitForProcessScalars(name);
    254    const scalars = TelemetryTestUtils.getProcessScalars("parent");
    255    return scalars[name];
    256  } catch (ex) {
    257    const msg = `Waiting for ${name} scalars to have been set`;
    258    if (ex.indexOf(msg) === 0) {
    259      return undefined;
    260    }
    261    throw ex;
    262  }
    263 }
    264 
    265 async function getFalsePositiveTelemetry() {
    266  return await getTelemetry(
    267    "dom.contentprocess.buildID_mismatch_false_positive"
    268  );
    269 }
    270 
    271 async function getTrueMismatchTelemetry() {
    272  return await getTelemetry("dom.contentprocess.buildID_mismatch");
    273 }
    274 
    275 // The logic bound to dom.ipc.processPrelaunch.enabled will react to value
    276 // changes: https://searchfox.org/mozilla-central/rev/ecd91b104714a8b2584a4c03175be50ccb3a7c67/dom/ipc/PreallocatedProcessManager.cpp#171-195
    277 // So we force flip to ensure we have no dangling process.
    278 async function forceCleanProcesses() {
    279  const origPrefValue = SpecialPowers.getBoolPref(
    280    "dom.ipc.processPrelaunch.enabled"
    281  );
    282  await SpecialPowers.setBoolPref(
    283    "dom.ipc.processPrelaunch.enabled",
    284    !origPrefValue
    285  );
    286  await SpecialPowers.setBoolPref(
    287    "dom.ipc.processPrelaunch.enabled",
    288    origPrefValue
    289  );
    290  const currPrefValue = SpecialPowers.getBoolPref(
    291    "dom.ipc.processPrelaunch.enabled"
    292  );
    293  Assert.strictEqual(
    294    currPrefValue,
    295    origPrefValue,
    296    "processPrelaunch properly re-enabled"
    297  );
    298 }