tor-browser

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

browser_parsable_script.js (4810B)


      1 /* Any copyright is dedicated to the Public Domain.
      2 * http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 /* This list allows pre-existing or 'unfixable' JS issues to remain, while we
      5 * detect newly occurring issues in shipping JS. It is a list of regexes
      6 * matching files which have errors:
      7 */
      8 
      9 requestLongerTimeout(2);
     10 
     11 const kAllowlist = new Set([
     12  /browser\/content\/browser\/places\/controller.js$/,
     13 ]);
     14 
     15 // Normally we would use reflect.sys.mjs to get Reflect.parse. However, if
     16 // we do that, then all the AST data is allocated in reflect.sys.mjs's
     17 // zone. That exposes a bug in our GC. The GC collects reflect.sys.mjs's
     18 // zone but not the zone in which our test code lives (since no new
     19 // data is being allocated in it). The cross-compartment wrappers in
     20 // our zone that point to the AST data never get collected, and so the
     21 // AST data itself is never collected. We need to GC both zones at
     22 // once to fix the problem.
     23 const init = Cc["@mozilla.org/jsreflect;1"].createInstance();
     24 init();
     25 
     26 /**
     27 * Check if an error should be ignored due to matching one of the allowlist
     28 * objects.
     29 *
     30 * @param uri the uri to check against the allowlist
     31 * @return true if the uri should be skipped, false otherwise.
     32 */
     33 function uriIsAllowed(uri) {
     34  for (let allowlistItem of kAllowlist) {
     35    if (allowlistItem.test(uri.spec)) {
     36      return true;
     37    }
     38  }
     39  return false;
     40 }
     41 
     42 function parsePromise(uri, parseTarget) {
     43  let promise = new Promise(resolve => {
     44    let xhr = new XMLHttpRequest();
     45    xhr.open("GET", uri, true);
     46    xhr.onreadystatechange = function () {
     47      if (this.readyState == this.DONE) {
     48        let scriptText = this.responseText;
     49        try {
     50          info(`Checking ${parseTarget} ${uri}`);
     51          let parseOpts = {
     52            source: uri,
     53            target: parseTarget,
     54          };
     55          Reflect.parse(scriptText, parseOpts);
     56          resolve(true);
     57        } catch (ex) {
     58          let errorMsg = "Script error reading " + uri + ": " + ex;
     59          ok(false, errorMsg);
     60          resolve(false);
     61        }
     62      }
     63    };
     64    xhr.onerror = error => {
     65      ok(false, "XHR error reading " + uri + ": " + error);
     66      resolve(false);
     67    };
     68    xhr.overrideMimeType("application/javascript");
     69    xhr.send(null);
     70  });
     71  return promise;
     72 }
     73 
     74 add_task(async function checkAllTheJS() {
     75  // In debug builds, even on a fast machine, collecting the file list may take
     76  // more than 30 seconds, and parsing all files may take four more minutes.
     77  // For this reason, this test must be explictly requested in debug builds by
     78  // using the "--setpref parse=<filter>" argument to mach.  You can specify:
     79  //  - A case-sensitive substring of the file name to test (slow).
     80  //  - A single absolute URI printed out by a previous run (fast).
     81  //  - An empty string to run the test on all files (slowest).
     82  let parseRequested = Services.prefs.prefHasUserValue("parse");
     83  let parseValue = parseRequested && Services.prefs.getCharPref("parse");
     84  if (SpecialPowers.isDebugBuild) {
     85    if (!parseRequested) {
     86      ok(
     87        true,
     88        "Test disabled on debug build. To run, execute: ./mach" +
     89          " mochitest-browser --setpref parse=<case_sensitive_filter>" +
     90          " browser/base/content/test/general/browser_parsable_script.js"
     91      );
     92      return;
     93    }
     94    // Request a 15 minutes timeout (30 seconds * 30) for debug builds.
     95    requestLongerTimeout(30);
     96  }
     97 
     98  let uris;
     99  // If an absolute URI is specified on the command line, use it immediately.
    100  if (parseValue && parseValue.includes(":")) {
    101    uris = [NetUtil.newURI(parseValue)];
    102  } else {
    103    let appDir = Services.dirsvc.get("GreD", Ci.nsIFile);
    104    // This asynchronously produces a list of URLs (sadly, mostly sync on our
    105    // test infrastructure because it runs against jarfiles there, and
    106    // our zipreader APIs are all sync)
    107    let startTimeMs = Date.now();
    108    info("Collecting URIs");
    109    uris = await generateURIsFromDirTree(appDir, [".js", ".jsm", ".mjs"]);
    110    info("Collected URIs in " + (Date.now() - startTimeMs) + "ms");
    111 
    112    // Apply the filter specified on the command line, if any.
    113    if (parseValue) {
    114      uris = uris.filter(uri => {
    115        if (uri.spec.includes(parseValue)) {
    116          return true;
    117        }
    118        info("Not checking filtered out " + uri.spec);
    119        return false;
    120      });
    121    }
    122  }
    123 
    124  // We create an array of promises so we can parallelize all our parsing
    125  // and file loading activity:
    126  await PerfTestHelpers.throttledMapPromises(uris, uri => {
    127    if (uriIsAllowed(uri)) {
    128      info("Not checking allowlisted " + uri.spec);
    129      return undefined;
    130    }
    131    let target = "script";
    132    if (uriIsESModule(uri)) {
    133      target = "module";
    134    }
    135    return parsePromise(uri.spec, target);
    136  });
    137  ok(true, "All files parsed");
    138 });