tor-browser

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

head.js (6051B)


      1 /* Any copyright is dedicated to the Public Domain.
      2 * http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 /* Shorthand constructors to construct an nsI(Local)File and zip reader: */
      7 const LocalFile = new Components.Constructor(
      8  "@mozilla.org/file/local;1",
      9  Ci.nsIFile,
     10  "initWithPath"
     11 );
     12 const ZipReader = new Components.Constructor(
     13  "@mozilla.org/libjar/zip-reader;1",
     14  "nsIZipReader",
     15  "open"
     16 );
     17 
     18 const IS_ALPHA = /^[a-z]+$/i;
     19 
     20 var { PerfTestHelpers } = ChromeUtils.importESModule(
     21  "resource://testing-common/PerfTestHelpers.sys.mjs"
     22 );
     23 
     24 const kESModuleList = new Set([
     25  /browser\/lockwise-card.js$/,
     26  /browser\/monitor-card.js$/,
     27  /browser\/proxy-card.js$/,
     28  /browser\/vpn-card.js$/,
     29  /toolkit\/content\/global\/certviewer\/components\/.*\.js$/,
     30  /toolkit\/content\/global\/certviewer\/.*\.js$/,
     31  /toolkit\/content\/global\/ml\/transformers.*\.js$/,
     32  /chrome\/pdfjs\/content\/web\/.*\.js$/,
     33 ]);
     34 
     35 /**
     36 * Check if a URI should be parsed as an ES module.
     37 *
     38 * @param uri the uri to check against the ES module list
     39 * @return true if the uri should be parsed as a module, otherwise parse it as a script.
     40 */
     41 function uriIsESModule(uri) {
     42  if (uri.filePath.endsWith(".mjs")) {
     43    return true;
     44  }
     45 
     46  for (let allowlistItem of kESModuleList) {
     47    if (allowlistItem.test(uri.spec)) {
     48      return true;
     49    }
     50  }
     51  return false;
     52 }
     53 
     54 /**
     55 * Returns a promise that is resolved with a list of files that have one of the
     56 * extensions passed, represented by their nsIURI objects, which exist inside
     57 * the directory passed.
     58 *
     59 * @param dir the directory which to scan for files (nsIFile)
     60 * @param extensions the extensions of files we're interested in (Array).
     61 */
     62 function generateURIsFromDirTree(dir, extensions) {
     63  if (!Array.isArray(extensions)) {
     64    extensions = [extensions];
     65  }
     66  let dirQueue = [dir.path];
     67  return (async function () {
     68    let rv = [];
     69    while (dirQueue.length) {
     70      let nextDir = dirQueue.shift();
     71      let { subdirs, files } = await iterateOverPath(nextDir, extensions);
     72      dirQueue.push(...subdirs);
     73      rv.push(...files);
     74    }
     75    return rv;
     76  })();
     77 }
     78 
     79 /**
     80 * Iterate over the children of |path| and find subdirectories and files with
     81 * the given extension.
     82 *
     83 * This function recurses into ZIP and JAR archives as well.
     84 *
     85 * @param {string} path The path to check.
     86 * @param {string[]} extensions The file extensions we're interested in.
     87 *
     88 * @returns {Promise<object>}
     89 *           A promise that resolves to an object containing the following
     90 *           properties:
     91 *           - files: an array of nsIURIs corresponding to
     92 *             files that match the extensions passed
     93 *           - subdirs: an array of paths for subdirectories we need to recurse
     94 *             into (handled by generateURIsFromDirTree above)
     95 */
     96 async function iterateOverPath(path, extensions) {
     97  const children = await IOUtils.getChildren(path);
     98 
     99  const files = [];
    100  const subdirs = [];
    101 
    102  for (const entry of children) {
    103    let stat;
    104    try {
    105      stat = await IOUtils.stat(entry);
    106    } catch (error) {
    107      if (error.name === "NotFoundError") {
    108        // Ignore symlinks from prior builds to subsequently removed files
    109        continue;
    110      }
    111      throw error;
    112    }
    113 
    114    if (stat.type === "directory") {
    115      subdirs.push(entry);
    116    } else if (extensions.some(extension => entry.endsWith(extension))) {
    117      if (await IOUtils.exists(entry)) {
    118        const spec = PathUtils.toFileURI(entry);
    119        files.push(Services.io.newURI(spec));
    120      }
    121    } else if (
    122      entry.endsWith(".ja") ||
    123      entry.endsWith(".jar") ||
    124      entry.endsWith(".zip") ||
    125      entry.endsWith(".xpi")
    126    ) {
    127      const file = new LocalFile(entry);
    128      for (const extension of extensions) {
    129        files.push(...generateEntriesFromJarFile(file, extension));
    130      }
    131    }
    132  }
    133 
    134  return { files, subdirs };
    135 }
    136 
    137 /* Helper function to generate a URI spec (NB: not an nsIURI yet!)
    138 * given an nsIFile object */
    139 function getURLForFile(file) {
    140  let fileHandler = Services.io.getProtocolHandler("file");
    141  fileHandler = fileHandler.QueryInterface(Ci.nsIFileProtocolHandler);
    142  return fileHandler.getURLSpecFromActualFile(file);
    143 }
    144 
    145 /**
    146 * A generator that generates nsIURIs for particular files found in jar files
    147 * like omni.ja.
    148 *
    149 * @param jarFile an nsIFile object for the jar file that needs checking.
    150 * @param extension the extension we're interested in.
    151 */
    152 function* generateEntriesFromJarFile(jarFile, extension) {
    153  let zr = new ZipReader(jarFile);
    154  const kURIStart = getURLForFile(jarFile);
    155 
    156  for (let entry of zr.findEntries("*" + extension + "$")) {
    157    // Ignore the JS cache which is stored in omni.ja
    158    if (entry.startsWith("jsloader") || entry.startsWith("jssubloader")) {
    159      continue;
    160    }
    161    let entryURISpec = "jar:" + kURIStart + "!/" + entry;
    162    yield Services.io.newURI(entryURISpec);
    163  }
    164  zr.close();
    165 }
    166 
    167 function fetchFile(uri) {
    168  return new Promise(resolve => {
    169    let xhr = new XMLHttpRequest();
    170    xhr.responseType = "text";
    171    xhr.open("GET", uri, true);
    172    xhr.onreadystatechange = function () {
    173      if (this.readyState != this.DONE) {
    174        return;
    175      }
    176      try {
    177        resolve(this.responseText);
    178      } catch (ex) {
    179        ok(false, `Script error reading ${uri}: ${ex}`);
    180        resolve("");
    181      }
    182    };
    183    xhr.onerror = error => {
    184      ok(false, `XHR error reading ${uri}: ${error}`);
    185      resolve("");
    186    };
    187    xhr.send(null);
    188  });
    189 }
    190 
    191 /**
    192 * Returns whether or not a word (presumably in en-US) is capitalized per
    193 * expectations.
    194 *
    195 * @param {string} word The single word to check.
    196 * @param {boolean} expectCapitalized True if the word should be capitalized.
    197 * @returns {boolean} True if the word matches the expected capitalization.
    198 */
    199 function hasExpectedCapitalization(word, expectCapitalized) {
    200  let firstChar = word[0];
    201  if (!IS_ALPHA.test(firstChar)) {
    202    return true;
    203  }
    204 
    205  let isCapitalized = firstChar == firstChar.toLocaleUpperCase("en-US");
    206  return isCapitalized == expectCapitalized;
    207 }