tor-browser

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

xpcshell-head-parent-process.js (17976B)


      1 /**
      2 * Any copyright is dedicated to the Public Domain.
      3 * http://creativecommons.org/publicdomain/zero/1.0/
      4 */
      5 
      6 // Tests using testGenerator are expected to define it themselves.
      7 // Testing functions are expected to call testSteps and its type should either
      8 // be GeneratorFunction or AsyncFunction
      9 /* global testGenerator, testSteps:false */
     10 
     11 var { classes: Cc, interfaces: Ci, utils: Cu } = Components;
     12 
     13 if (!("self" in this)) {
     14  this.self = this;
     15 }
     16 
     17 var bufferCache = [];
     18 
     19 function is(a, b, msg) {
     20  Assert.equal(a, b, msg);
     21 }
     22 
     23 function ok(cond, msg) {
     24  Assert.ok(!!cond, msg);
     25 }
     26 
     27 function isnot(a, b, msg) {
     28  Assert.notEqual(a, b, msg);
     29 }
     30 
     31 function todo(condition) {
     32  todo_check_true(condition);
     33 }
     34 
     35 function run_test() {
     36  runTest();
     37 }
     38 
     39 if (!this.runTest) {
     40  this.runTest = function () {
     41    if (SpecialPowers.isMainProcess()) {
     42      // XPCShell does not get a profile by default.
     43      do_get_profile();
     44 
     45      enableTesting();
     46      enableExperimental();
     47    }
     48 
     49    // In order to support converting tests to using async functions from using
     50    // generator functions, we detect async functions by checking the name of
     51    // function's constructor.
     52    Assert.ok(
     53      typeof testSteps === "function",
     54      "There should be a testSteps function"
     55    );
     56    if (testSteps.constructor.name === "AsyncFunction") {
     57      // Do run our existing cleanup function that would normally be called by
     58      // the generator's call to finishTest().
     59      registerCleanupFunction(function () {
     60        if (SpecialPowers.isMainProcess()) {
     61          resetTesting();
     62        }
     63      });
     64 
     65      add_task(testSteps);
     66 
     67      // Since we defined run_test, we must invoke run_next_test() to start the
     68      // async test.
     69      run_next_test();
     70    } else {
     71      Assert.ok(
     72        testSteps.constructor.name === "GeneratorFunction",
     73        "Unsupported function type"
     74      );
     75 
     76      do_test_pending();
     77      testGenerator.next();
     78    }
     79  };
     80 }
     81 
     82 function finishTest() {
     83  if (SpecialPowers.isMainProcess()) {
     84    resetExperimental();
     85    resetTesting();
     86  }
     87 
     88  SpecialPowers.removeFiles();
     89 
     90  executeSoon(function () {
     91    do_test_finished();
     92  });
     93 }
     94 
     95 function grabEventAndContinueHandler(event) {
     96  testGenerator.next(event);
     97 }
     98 
     99 function continueToNextStep() {
    100  executeSoon(function () {
    101    testGenerator.next();
    102  });
    103 }
    104 
    105 function errorHandler(event) {
    106  try {
    107    dump("indexedDB error: " + event.target.error.name);
    108  } catch (e) {
    109    dump("indexedDB error: " + e);
    110  }
    111  Assert.ok(false);
    112  finishTest();
    113 }
    114 
    115 function unexpectedSuccessHandler() {
    116  Assert.ok(false);
    117  finishTest();
    118 }
    119 
    120 function expectedErrorHandler(name) {
    121  return function (event) {
    122    Assert.equal(event.type, "error");
    123    Assert.equal(event.target.error.name, name);
    124    event.preventDefault();
    125    grabEventAndContinueHandler(event);
    126  };
    127 }
    128 
    129 function expectUncaughtException() {
    130  // This is dummy for xpcshell test.
    131 }
    132 
    133 function ExpectError(name, preventDefault) {
    134  this._name = name;
    135  this._preventDefault = preventDefault;
    136 }
    137 ExpectError.prototype = {
    138  handleEvent(event) {
    139    Assert.equal(event.type, "error");
    140    Assert.equal(this._name, event.target.error.name);
    141    if (this._preventDefault) {
    142      event.preventDefault();
    143      event.stopPropagation();
    144    }
    145    grabEventAndContinueHandler(event);
    146  },
    147 };
    148 
    149 function continueToNextStepSync() {
    150  testGenerator.next();
    151 }
    152 
    153 // TODO compareKeys is duplicated in ../helpers.js, can we import that here?
    154 // the same applies to many other functions in this file
    155 // this duplication should be avoided (bug 1565986)
    156 function compareKeys(k1, k2) {
    157  let t = typeof k1;
    158  if (t != typeof k2) {
    159    return false;
    160  }
    161 
    162  if (t !== "object") {
    163    return k1 === k2;
    164  }
    165 
    166  if (k1 instanceof Date) {
    167    return k2 instanceof Date && k1.getTime() === k2.getTime();
    168  }
    169 
    170  if (k1 instanceof Array) {
    171    if (!(k2 instanceof Array) || k1.length != k2.length) {
    172      return false;
    173    }
    174 
    175    for (let i = 0; i < k1.length; ++i) {
    176      if (!compareKeys(k1[i], k2[i])) {
    177        return false;
    178      }
    179    }
    180 
    181    return true;
    182  }
    183 
    184  if (k1 instanceof ArrayBuffer) {
    185    if (!(k2 instanceof ArrayBuffer)) {
    186      return false;
    187    }
    188 
    189    function arrayBuffersAreEqual(a, b) {
    190      if (a.byteLength != b.byteLength) {
    191        return false;
    192      }
    193      let ui8b = new Uint8Array(b);
    194      return new Uint8Array(a).every((val, i) => val === ui8b[i]);
    195    }
    196 
    197    return arrayBuffersAreEqual(k1, k2);
    198  }
    199 
    200  return false;
    201 }
    202 
    203 function addPermission() {
    204  throw new Error("addPermission");
    205 }
    206 
    207 function removePermission() {
    208  throw new Error("removePermission");
    209 }
    210 
    211 function allowIndexedDB() {
    212  throw new Error("allowIndexedDB");
    213 }
    214 
    215 function disallowIndexedDB() {
    216  throw new Error("disallowIndexedDB");
    217 }
    218 
    219 function enableExperimental() {
    220  SpecialPowers.setBoolPref("dom.indexedDB.experimental", true);
    221 }
    222 
    223 function resetExperimental() {
    224  SpecialPowers.clearUserPref("dom.indexedDB.experimental");
    225 }
    226 
    227 function enableTesting() {
    228  SpecialPowers.setBoolPref("dom.quotaManager.testing", true);
    229  SpecialPowers.setBoolPref("dom.indexedDB.testing", true);
    230 }
    231 
    232 function resetTesting() {
    233  SpecialPowers.clearUserPref("dom.indexedDB.testing");
    234  SpecialPowers.clearUserPref("dom.quotaManager.testing");
    235 }
    236 
    237 function gc() {
    238  Cu.forceGC();
    239  Cu.forceCC();
    240 }
    241 
    242 function scheduleGC() {
    243  SpecialPowers.exactGC(continueToNextStep);
    244 }
    245 
    246 function setTimeout(fun, timeout) {
    247  let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
    248  var event = {
    249    notify() {
    250      fun();
    251    },
    252  };
    253  timer.initWithCallback(event, timeout, Ci.nsITimer.TYPE_ONE_SHOT);
    254  return timer;
    255 }
    256 
    257 function initStorage() {
    258  return Services.qms.init();
    259 }
    260 
    261 function initPersistentOrigin(principal) {
    262  return Services.qms.initializePersistentOrigin(principal);
    263 }
    264 
    265 function resetOrClearAllDatabases(callback, clear) {
    266  if (!SpecialPowers.isMainProcess()) {
    267    throw new Error("clearAllDatabases not implemented for child processes!");
    268  }
    269 
    270  let request;
    271 
    272  if (clear) {
    273    request = Services.qms.clear();
    274  } else {
    275    request = Services.qms.reset();
    276  }
    277 
    278  request.callback = callback;
    279 
    280  return request;
    281 }
    282 
    283 function resetAllDatabases(callback) {
    284  return resetOrClearAllDatabases(callback, false);
    285 }
    286 
    287 function clearAllDatabases(callback) {
    288  return resetOrClearAllDatabases(callback, true);
    289 }
    290 
    291 function installPackagedProfile(packageName) {
    292  let profileDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
    293 
    294  let currentDir = Services.dirsvc.get("CurWorkD", Ci.nsIFile);
    295 
    296  let packageFile = currentDir.clone();
    297  packageFile.append(packageName + ".zip");
    298 
    299  let zipReader = Cc["@mozilla.org/libjar/zip-reader;1"].createInstance(
    300    Ci.nsIZipReader
    301  );
    302  zipReader.open(packageFile);
    303 
    304  let entryNames = [];
    305  for (let entry of zipReader.findEntries(null)) {
    306    if (entry != "create_db.html") {
    307      entryNames.push(entry);
    308    }
    309  }
    310  entryNames.sort();
    311 
    312  for (let entryName of entryNames) {
    313    let zipentry = zipReader.getEntry(entryName);
    314 
    315    let file = profileDir.clone();
    316    let split = entryName.split("/");
    317    for (let i = 0; i < split.length; i++) {
    318      file.append(split[i]);
    319    }
    320 
    321    if (zipentry.isDirectory) {
    322      file.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt("0755", 8));
    323    } else {
    324      let istream = zipReader.getInputStream(entryName);
    325 
    326      var ostream = Cc[
    327        "@mozilla.org/network/file-output-stream;1"
    328      ].createInstance(Ci.nsIFileOutputStream);
    329      ostream.init(file, -1, parseInt("0644", 8), 0);
    330 
    331      let bostream = Cc[
    332        "@mozilla.org/network/buffered-output-stream;1"
    333      ].createInstance(Ci.nsIBufferedOutputStream);
    334      bostream.init(ostream, 32768);
    335 
    336      bostream.writeFrom(istream, istream.available());
    337 
    338      istream.close();
    339      bostream.close();
    340    }
    341  }
    342 
    343  zipReader.close();
    344 }
    345 
    346 function getChromeFilesDir() {
    347  let profileDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
    348 
    349  let idbDir = profileDir.clone();
    350  idbDir.append("storage");
    351  idbDir.append("permanent");
    352  idbDir.append("chrome");
    353  idbDir.append("idb");
    354 
    355  let idbEntries = idbDir.directoryEntries;
    356  while (idbEntries.hasMoreElements()) {
    357    let file = idbEntries.nextFile;
    358    if (file.isDirectory()) {
    359      return file;
    360    }
    361  }
    362 
    363  throw new Error("files directory doesn't exist!");
    364 }
    365 
    366 function getView(size) {
    367  let buffer = new ArrayBuffer(size);
    368  let view = new Uint8Array(buffer);
    369  is(buffer.byteLength, size, "Correct byte length");
    370  return view;
    371 }
    372 
    373 function getRandomView(size) {
    374  let view = getView(size);
    375  for (let i = 0; i < size; i++) {
    376    view[i] = parseInt(Math.random() * 255);
    377  }
    378  return view;
    379 }
    380 
    381 function getBlob(str) {
    382  return new Blob([str], { type: "type/text" });
    383 }
    384 
    385 function getFile(name, type, str) {
    386  return new File([str], name, { type });
    387 }
    388 
    389 function isWasmSupported() {
    390  let testingFunctions = Cu.getJSTestingFunctions();
    391  return testingFunctions.wasmIsSupported();
    392 }
    393 
    394 function getWasmModule(binary) {
    395  let module = new WebAssembly.Module(binary);
    396  return module;
    397 }
    398 
    399 function compareBuffers(buffer1, buffer2) {
    400  if (buffer1.byteLength != buffer2.byteLength) {
    401    return false;
    402  }
    403 
    404  let view1 = buffer1 instanceof Uint8Array ? buffer1 : new Uint8Array(buffer1);
    405  let view2 = buffer2 instanceof Uint8Array ? buffer2 : new Uint8Array(buffer2);
    406  for (let i = 0; i < buffer1.byteLength; i++) {
    407    if (view1[i] != view2[i]) {
    408      return false;
    409    }
    410  }
    411  return true;
    412 }
    413 
    414 function verifyBuffers(buffer1, buffer2) {
    415  ok(compareBuffers(buffer1, buffer2), "Correct buffer data");
    416 }
    417 
    418 function verifyBlob(blob1, blob2) {
    419  is(Blob.isInstance(blob1), true, "Instance of nsIDOMBlob");
    420  is(File.isInstance(blob1), File.isInstance(blob2), "Instance of DOM File");
    421  is(blob1.size, blob2.size, "Correct size");
    422  is(blob1.type, blob2.type, "Correct type");
    423  if (File.isInstance(blob2)) {
    424    is(blob1.name, blob2.name, "Correct name");
    425  }
    426 
    427  let buffer1;
    428  let buffer2;
    429 
    430  for (let i = 0; i < bufferCache.length; i++) {
    431    if (bufferCache[i].blob == blob2) {
    432      buffer2 = bufferCache[i].buffer;
    433      break;
    434    }
    435  }
    436 
    437  if (!buffer2) {
    438    let reader = new FileReader();
    439    reader.readAsArrayBuffer(blob2);
    440    reader.onload = function (event) {
    441      buffer2 = event.target.result;
    442      bufferCache.push({ blob: blob2, buffer: buffer2 });
    443      if (buffer1) {
    444        verifyBuffers(buffer1, buffer2);
    445        testGenerator.next();
    446      }
    447    };
    448  }
    449 
    450  let reader = new FileReader();
    451  reader.readAsArrayBuffer(blob1);
    452  reader.onload = function (event) {
    453    buffer1 = event.target.result;
    454    if (buffer2) {
    455      verifyBuffers(buffer1, buffer2);
    456      testGenerator.next();
    457    }
    458  };
    459 }
    460 
    461 function verifyView(view1, view2) {
    462  is(view1.byteLength, view2.byteLength, "Correct byteLength");
    463  verifyBuffers(view1, view2);
    464 }
    465 
    466 function grabFileUsageAndContinueHandler(request) {
    467  testGenerator.next(request.result.fileUsage);
    468 }
    469 
    470 function getCurrentUsage(usageHandler) {
    471  let principal = Cc["@mozilla.org/systemprincipal;1"].createInstance(
    472    Ci.nsIPrincipal
    473  );
    474  Services.qms.getUsageForPrincipal(principal, usageHandler);
    475 }
    476 
    477 function setTemporaryStorageLimit(limit) {
    478  const pref = "dom.quotaManager.temporaryStorage.fixedLimit";
    479  if (limit) {
    480    info("Setting temporary storage limit to " + limit);
    481    SpecialPowers.setIntPref(pref, limit);
    482  } else {
    483    info("Removing temporary storage limit");
    484    SpecialPowers.clearUserPref(pref);
    485  }
    486 }
    487 
    488 function setDataThreshold(threshold) {
    489  info("Setting data threshold to " + threshold);
    490  SpecialPowers.setIntPref("dom.indexedDB.dataThreshold", threshold);
    491 }
    492 
    493 function resetDataThreshold() {
    494  info("Clearing data threshold pref");
    495  SpecialPowers.clearUserPref("dom.indexedDB.dataThreshold");
    496 }
    497 
    498 function setMaxStructuredCloneSize(aSize) {
    499  info("Setting maximal structured clone size to " + aSize);
    500  SpecialPowers.setIntPref("dom.indexedDB.maxStructuredCloneSize", aSize);
    501 }
    502 
    503 function setMaxSerializedMsgSize(aSize) {
    504  info("Setting maximal size of a serialized message to " + aSize);
    505  SpecialPowers.setIntPref("dom.indexedDB.maxSerializedMsgSize", aSize);
    506 }
    507 
    508 function enablePreprocessing() {
    509  info("Setting preprocessing pref");
    510  SpecialPowers.setBoolPref("dom.indexedDB.preprocessing", true);
    511 }
    512 
    513 function resetPreprocessing() {
    514  info("Clearing preprocessing pref");
    515  SpecialPowers.clearUserPref("dom.indexedDB.preprocessing");
    516 }
    517 
    518 function getSystemPrincipal() {
    519  return Cc["@mozilla.org/systemprincipal;1"].createInstance(Ci.nsIPrincipal);
    520 }
    521 
    522 function getPrincipal(url) {
    523  let uri = Services.io.newURI(url);
    524  return Services.scriptSecurityManager.createContentPrincipal(uri, {});
    525 }
    526 
    527 class RequestError extends Error {
    528  constructor(resultCode, resultName) {
    529    super(`Request failed (code: ${resultCode}, name: ${resultName})`);
    530    this.name = "RequestError";
    531    this.resultCode = resultCode;
    532    this.resultName = resultName;
    533  }
    534 }
    535 
    536 async function requestFinished(request) {
    537  await new Promise(function (resolve) {
    538    request.callback = function () {
    539      resolve();
    540    };
    541  });
    542 
    543  if (request.resultCode !== Cr.NS_OK) {
    544    throw new RequestError(request.resultCode, request.resultName);
    545  }
    546 
    547  return request.result;
    548 }
    549 
    550 // TODO: Rename to openDBRequestSucceeded ?
    551 function expectingSuccess(request) {
    552  return new Promise(function (resolve, reject) {
    553    request.onerror = function (event) {
    554      ok(false, "indexedDB error, '" + event.target.error.name + "'");
    555      reject(event);
    556    };
    557    request.onsuccess = function (event) {
    558      resolve(event);
    559    };
    560    request.onupgradeneeded = function (event) {
    561      ok(false, "Got upgrade, but did not expect it!");
    562      reject(event);
    563    };
    564  });
    565 }
    566 
    567 // TODO: Rename to openDBRequestUpgradeNeeded ?
    568 function expectingUpgrade(request) {
    569  return new Promise(function (resolve, reject) {
    570    request.onerror = function (event) {
    571      ok(false, "indexedDB error, '" + event.target.error.name + "'");
    572      reject(event);
    573    };
    574    request.onupgradeneeded = function (event) {
    575      resolve(event);
    576    };
    577    request.onsuccess = function (event) {
    578      ok(false, "Got success, but did not expect it!");
    579      reject(event);
    580    };
    581  });
    582 }
    583 
    584 function requestSucceeded(request, optionalSyncSuccessCallback) {
    585  return new Promise(function (resolve, reject) {
    586    request.onerror = function (event) {
    587      ok(false, "indexedDB error, '" + event.target.error.name + "'");
    588      reject(event);
    589    };
    590    request.onsuccess = function (event) {
    591      if (optionalSyncSuccessCallback) {
    592        optionalSyncSuccessCallback();
    593      }
    594      resolve(event);
    595    };
    596  });
    597 }
    598 
    599 // Given a "/"-delimited path relative to the profile directory,
    600 // return an nsIFile representing the path.  This does not test
    601 // for the existence of the file or parent directories.
    602 // It is safe even on Windows where the directory separator is not "/",
    603 // but make sure you're not passing in a "\"-delimited path.
    604 function getRelativeFile(relativePath) {
    605  let profileDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
    606 
    607  let file = profileDir.clone();
    608  relativePath.split("/").forEach(function (component) {
    609    file.append(component);
    610  });
    611 
    612  return file;
    613 }
    614 
    615 const isInChaosMode = () => {
    616  return !!parseInt(Services.env.get("MOZ_CHAOSMODE"), 16);
    617 };
    618 
    619 var SpecialPowers = {
    620  isMainProcess() {
    621    return (
    622      Services.appinfo.processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT
    623    );
    624  },
    625  notifyObservers(subject, topic, data) {
    626    Services.obs.notifyObservers(subject, topic, data);
    627  },
    628  notifyObserversInParentProcess(subject, topic, data) {
    629    if (subject) {
    630      throw new Error("Can't send subject to another process!");
    631    }
    632    return this.notifyObservers(subject, topic, data);
    633  },
    634  getBoolPref(prefName) {
    635    return Services.prefs.getBoolPref(prefName);
    636  },
    637  setBoolPref(prefName, value) {
    638    Services.prefs.setBoolPref(prefName, value);
    639  },
    640  setIntPref(prefName, value) {
    641    Services.prefs.setIntPref(prefName, value);
    642  },
    643  clearUserPref(prefName) {
    644    Services.prefs.clearUserPref(prefName);
    645  },
    646  // Copied (and slightly adjusted) from testing/specialpowers/api.js
    647  exactGC(callback) {
    648    let count = 0;
    649 
    650    function doPreciseGCandCC() {
    651      function scheduledGCCallback() {
    652        Cu.forceCC();
    653 
    654        if (++count < 3) {
    655          doPreciseGCandCC();
    656        } else {
    657          callback();
    658        }
    659      }
    660 
    661      Cu.schedulePreciseGC(scheduledGCCallback);
    662    }
    663 
    664    doPreciseGCandCC();
    665  },
    666 
    667  get Cc() {
    668    return Cc;
    669  },
    670 
    671  get Ci() {
    672    return Ci;
    673  },
    674 
    675  get Cu() {
    676    return Cu;
    677  },
    678 
    679  // Based on SpecialPowersObserver.prototype.receiveMessage
    680  createFiles(requests, callback) {
    681    let filePaths = [];
    682    if (!this._createdFiles) {
    683      this._createdFiles = [];
    684    }
    685    let createdFiles = this._createdFiles;
    686    let promises = [];
    687    requests.forEach(function (request) {
    688      const filePerms = 0o666;
    689      let testFile = Services.dirsvc.get("ProfD", Ci.nsIFile);
    690      if (request.name) {
    691        testFile.append(request.name);
    692      } else {
    693        testFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, filePerms);
    694      }
    695      let outStream = Cc[
    696        "@mozilla.org/network/file-output-stream;1"
    697      ].createInstance(Ci.nsIFileOutputStream);
    698      outStream.init(
    699        testFile,
    700        0x02 | 0x08 | 0x20, // PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE
    701        filePerms,
    702        0
    703      );
    704      if (request.data) {
    705        outStream.write(request.data, request.data.length);
    706        outStream.close();
    707      }
    708      promises.push(
    709        File.createFromFileName(testFile.path, request.options).then(
    710          function (file) {
    711            filePaths.push(file);
    712          }
    713        )
    714      );
    715      createdFiles.push(testFile);
    716    });
    717 
    718    Promise.all(promises).then(function () {
    719      setTimeout(function () {
    720        callback(filePaths);
    721      }, 0);
    722    });
    723  },
    724 
    725  removeFiles() {
    726    if (this._createdFiles) {
    727      this._createdFiles.forEach(function (testFile) {
    728        try {
    729          testFile.remove(false);
    730        } catch (e) {}
    731      });
    732      this._createdFiles = null;
    733    }
    734  },
    735 };