tor-browser

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

helpers.js (22765B)


      1 /**
      2 * Any copyright is dedicated to the Public Domain.
      3 * http://creativecommons.org/publicdomain/zero/1.0/
      4 */
      5 
      6 /* eslint-disable mozilla/no-comparison-or-assignment-inside-ok */
      7 
      8 // testSteps is expected to be defined by the test using this file.
      9 /* global testSteps:false */
     10 
     11 var testGenerator;
     12 if (testSteps.constructor.name === "GeneratorFunction") {
     13  testGenerator = testSteps();
     14 }
     15 // The test js is shared between xpcshell (which has no SpecialPowers object)
     16 // and content mochitests (where the |Components| object is accessible only as
     17 // SpecialPowers.Components). Expose Components if necessary here to make things
     18 // work everywhere.
     19 //
     20 // Even if the real |Components| doesn't exist, we might shim in a simple JS
     21 // placebo for compat. An easy way to differentiate this from the real thing
     22 // is whether the property is read-only or not.
     23 var c = Object.getOwnPropertyDescriptor(this, "Components");
     24 if ((!c || !c.value || c.writable) && typeof SpecialPowers === "object") {
     25  // eslint-disable-next-line no-global-assign
     26  Components = SpecialPowers.wrap(SpecialPowers.Components);
     27 }
     28 
     29 function executeSoon(aFun) {
     30  SpecialPowers.Services.tm.dispatchToMainThread({
     31    run() {
     32      aFun();
     33    },
     34  });
     35 }
     36 
     37 function clearAllDatabases(callback) {
     38  let qms = SpecialPowers.Services.qms;
     39  let principal = SpecialPowers.wrap(document).effectiveStoragePrincipal;
     40  let request = qms.clearStoragesForPrincipal(principal);
     41  let cb = SpecialPowers.wrapCallback(callback);
     42  request.callback = cb;
     43 }
     44 
     45 var testHarnessGenerator = testHarnessSteps();
     46 testHarnessGenerator.next();
     47 
     48 function* testHarnessSteps() {
     49  function nextTestHarnessStep(val) {
     50    testHarnessGenerator.next(val);
     51  }
     52 
     53  let testScriptPath;
     54  let testScriptFilename;
     55 
     56  let scripts = document.getElementsByTagName("script");
     57  for (let i = 0; i < scripts.length; i++) {
     58    let src = scripts[i].src;
     59    let match = src.match(/indexedDB\/test\/unit\/(test_[^\/]+\.js)$/);
     60    if (match && match.length == 2) {
     61      testScriptPath = src;
     62      testScriptFilename = match[1];
     63      break;
     64    }
     65  }
     66 
     67  yield undefined;
     68 
     69  info("Running" + (testScriptFilename ? " '" + testScriptFilename + "'" : ""));
     70 
     71  info("Pushing preferences");
     72 
     73  SpecialPowers.pushPrefEnv(
     74    {
     75      set: [
     76        ["dom.indexedDB.testing", true],
     77        ["dom.indexedDB.experimental", true],
     78        ["javascript.options.wasm_baselinejit", true], // This can be removed when on by default
     79      ],
     80    },
     81    nextTestHarnessStep
     82  );
     83  yield undefined;
     84 
     85  info("Pushing permissions");
     86 
     87  SpecialPowers.pushPermissions(
     88    [
     89      {
     90        type: "indexedDB",
     91        allow: true,
     92        context: document,
     93      },
     94    ],
     95    nextTestHarnessStep
     96  );
     97  yield undefined;
     98 
     99  info("Clearing old databases");
    100 
    101  clearAllDatabases(nextTestHarnessStep);
    102  yield undefined;
    103 
    104  if (testScriptFilename && !window.disableWorkerTest) {
    105    // For the AsyncFunction, handle the executing sequece using
    106    // add_task(). For the GeneratorFunction, we just handle the sequence
    107    // manually.
    108    if (testSteps.constructor.name === "AsyncFunction") {
    109      add_task(function workerTestSteps() {
    110        return executeWorkerTestAndCleanUp(testScriptPath);
    111      });
    112    } else {
    113      ok(
    114        testSteps.constructor.name === "GeneratorFunction",
    115        "Unsupported function type"
    116      );
    117      executeWorkerTestAndCleanUp(testScriptPath).then(nextTestHarnessStep);
    118 
    119      yield undefined;
    120    }
    121  } else if (testScriptFilename) {
    122    todo(
    123      false,
    124      "Skipping test in a worker because it is explicitly disabled: " +
    125        window.disableWorkerTest
    126    );
    127  } else {
    128    todo(
    129      false,
    130      "Skipping test in a worker because it's not structured properly"
    131    );
    132  }
    133 
    134  info("Running test in main thread");
    135 
    136  // Now run the test script in the main thread.
    137  if (testSteps.constructor.name === "AsyncFunction") {
    138    // Register a callback to clean up databases because it's the only way for
    139    // add_task() to clean them right before the SimpleTest.FinishTest
    140    SimpleTest.registerCleanupFunction(async function () {
    141      await new Promise(function (resolve, reject) {
    142        clearAllDatabases(function (result) {
    143          if (result.resultCode == SpecialPowers.Cr.NS_OK) {
    144            resolve(result);
    145          } else {
    146            reject(result.resultCode);
    147          }
    148        });
    149      });
    150    });
    151 
    152    add_task(testSteps);
    153  } else {
    154    testGenerator.next();
    155 
    156    yield undefined;
    157  }
    158 }
    159 
    160 if (!window.runTest) {
    161  window.runTest = function () {
    162    SimpleTest.waitForExplicitFinish();
    163    testHarnessGenerator.next();
    164  };
    165 }
    166 
    167 function finishTest() {
    168  ok(
    169    testSteps.constructor.name === "GeneratorFunction",
    170    "Async/await tests shouldn't call finishTest()"
    171  );
    172  SimpleTest.executeSoon(function () {
    173    clearAllDatabases(function () {
    174      SimpleTest.finish();
    175    });
    176  });
    177 }
    178 
    179 function browserRunTest() {
    180  testGenerator.next();
    181 }
    182 
    183 function browserFinishTest() {}
    184 
    185 function grabEventAndContinueHandler(event) {
    186  testGenerator.next(event);
    187 }
    188 
    189 function continueToNextStep() {
    190  SimpleTest.executeSoon(function () {
    191    testGenerator.next();
    192  });
    193 }
    194 
    195 function continueToNextStepSync() {
    196  testGenerator.next();
    197 }
    198 
    199 function errorHandler(event) {
    200  ok(false, "indexedDB error, '" + event.target.error.name + "'");
    201  finishTest();
    202 }
    203 
    204 // For error callbacks where the argument is not an event object.
    205 function errorCallbackHandler(err) {
    206  ok(false, "got unexpected error callback: " + err);
    207  finishTest();
    208 }
    209 
    210 function expectUncaughtException(expecting) {
    211  SimpleTest.expectUncaughtException(expecting);
    212 }
    213 
    214 function browserErrorHandler(event) {
    215  browserFinishTest();
    216  throw new Error("indexedDB error (" + event.code + "): " + event.message);
    217 }
    218 
    219 function unexpectedSuccessHandler() {
    220  ok(false, "Got success, but did not expect it!");
    221  finishTest();
    222 }
    223 
    224 function expectedErrorHandler(name) {
    225  return function (event) {
    226    is(event.type, "error", "Got an error event");
    227    is(event.target.error.name, name, "Expected error was thrown.");
    228    event.preventDefault();
    229    grabEventAndContinueHandler(event);
    230  };
    231 }
    232 
    233 function ExpectError(name, preventDefault) {
    234  this._name = name;
    235  this._preventDefault = preventDefault;
    236 }
    237 ExpectError.prototype = {
    238  handleEvent(event) {
    239    is(event.type, "error", "Got an error event");
    240    is(event.target.error.name, this._name, "Expected error was thrown.");
    241    if (this._preventDefault) {
    242      event.preventDefault();
    243      event.stopPropagation();
    244    }
    245    grabEventAndContinueHandler(event);
    246  },
    247 };
    248 
    249 function compareKeys(_k1_, _k2_) {
    250  let t = typeof _k1_;
    251  if (t != typeof _k2_) {
    252    return false;
    253  }
    254 
    255  if (t !== "object") {
    256    return _k1_ === _k2_;
    257  }
    258 
    259  if (_k1_ instanceof Date) {
    260    return _k2_ instanceof Date && _k1_.getTime() === _k2_.getTime();
    261  }
    262 
    263  if (_k1_ instanceof Array) {
    264    if (!(_k2_ instanceof Array) || _k1_.length != _k2_.length) {
    265      return false;
    266    }
    267 
    268    for (let i = 0; i < _k1_.length; ++i) {
    269      if (!compareKeys(_k1_[i], _k2_[i])) {
    270        return false;
    271      }
    272    }
    273 
    274    return true;
    275  }
    276 
    277  if (_k1_ instanceof ArrayBuffer) {
    278    if (!(_k2_ instanceof ArrayBuffer)) {
    279      return false;
    280    }
    281 
    282    function arrayBuffersAreEqual(a, b) {
    283      if (a.byteLength != b.byteLength) {
    284        return false;
    285      }
    286      let ui8b = new Uint8Array(b);
    287      return new Uint8Array(a).every((val, i) => val === ui8b[i]);
    288    }
    289 
    290    return arrayBuffersAreEqual(_k1_, _k2_);
    291  }
    292 
    293  return false;
    294 }
    295 
    296 function removePermission(type, url) {
    297  if (!url) {
    298    url = window.document;
    299  }
    300  SpecialPowers.removePermission(type, url);
    301 }
    302 
    303 function gc() {
    304  SpecialPowers.forceGC();
    305  SpecialPowers.forceCC();
    306 }
    307 
    308 function scheduleGC() {
    309  SpecialPowers.exactGC(continueToNextStep);
    310 }
    311 
    312 // Assert that eventually a condition becomes true, running a garbage
    313 // collection between evaluations. Fails, after a high number of iterations
    314 // without a successful evaluation of the condition.
    315 function* assertEventuallyWithGC(conditionFunctor, message) {
    316  const maxGC = 100;
    317  for (let i = 0; i < maxGC; ++i) {
    318    let result =
    319      conditionFunctor.constructor.name === "GeneratorFunction"
    320        ? yield* conditionFunctor()
    321        : conditionFunctor();
    322    if (result) {
    323      ok(true, message + " (after " + i + " garbage collections)");
    324      return;
    325    }
    326    SpecialPowers.exactGC(continueToNextStep);
    327    yield undefined;
    328  }
    329  ok(false, message + " (even after " + maxGC + " garbage collections)");
    330 }
    331 
    332 // Asserts that a functor `f` throws an exception that is an instance of
    333 // `ctor`. If it doesn't throw, or throws a different type of exception, this
    334 // throws an Error, including the optional `msg` given.
    335 // Otherwise, it returns the message of the exception.
    336 //
    337 // TODO This is DUPLICATED from https://searchfox.org/mozilla-central/rev/cfd1cc461f1efe0d66c2fdc17c024a203d5a2fd8/js/src/tests/shell.js#163
    338 // This should be moved to a more generic place, as it is in no way specific
    339 // to IndexedDB.
    340 function assertThrowsInstanceOf(f, ctor, msg) {
    341  var fullmsg;
    342  try {
    343    f();
    344  } catch (exc) {
    345    if (exc instanceof ctor) {
    346      return exc.message;
    347    }
    348    fullmsg = `Assertion failed: expected exception ${ctor.name}, got ${exc}`;
    349  }
    350 
    351  if (fullmsg === undefined) {
    352    fullmsg = `Assertion failed: expected exception ${ctor.name}, no exception thrown`;
    353  }
    354  if (msg !== undefined) {
    355    fullmsg += " - " + msg;
    356  }
    357 
    358  throw new Error(fullmsg);
    359 }
    360 
    361 function isWasmSupported() {
    362  let testingFunctions = SpecialPowers.Cu.getJSTestingFunctions();
    363  return testingFunctions.wasmIsSupported();
    364 }
    365 
    366 function getWasmModule(_binary_) {
    367  let module = new WebAssembly.Module(_binary_);
    368  return module;
    369 }
    370 
    371 function expectingSuccess(request) {
    372  return new Promise(function (resolve, reject) {
    373    request.onerror = function (event) {
    374      ok(false, "indexedDB error, '" + event.target.error.name + "'");
    375      reject(event);
    376    };
    377    request.onsuccess = function (event) {
    378      resolve(event);
    379    };
    380    request.onupgradeneeded = function (event) {
    381      ok(false, "Got upgrade, but did not expect it!");
    382      reject(event);
    383    };
    384  });
    385 }
    386 
    387 function expectingUpgrade(request) {
    388  return new Promise(function (resolve, reject) {
    389    request.onerror = function (event) {
    390      ok(false, "indexedDB error, '" + event.target.error.name + "'");
    391      reject(event);
    392    };
    393    request.onupgradeneeded = function (event) {
    394      resolve(event);
    395    };
    396    request.onsuccess = function (event) {
    397      ok(false, "Got success, but did not expect it!");
    398      reject(event);
    399    };
    400  });
    401 }
    402 
    403 function expectingError(request, errorName) {
    404  return new Promise(function (resolve, reject) {
    405    request.onerror = function (event) {
    406      is(errorName, event.target.error.name, "Correct exception type");
    407      event.stopPropagation();
    408      resolve(event);
    409    };
    410    request.onsuccess = function (event) {
    411      ok(false, "Got success, but did not expect it!");
    412      reject(event);
    413    };
    414    request.onupgradeneeded = function (event) {
    415      ok(false, "Got upgrade, but did not expect it!");
    416      reject(event);
    417    };
    418  });
    419 }
    420 
    421 function requestSucceeded(request, optionalSyncSuccessCallback) {
    422  return new Promise(function (resolve, reject) {
    423    request.onerror = function (event) {
    424      ok(false, "indexedDB error, '" + event.target.error.name + "'");
    425      reject(event);
    426    };
    427    request.onsuccess = function (event) {
    428      if (optionalSyncSuccessCallback) {
    429        optionalSyncSuccessCallback();
    430      }
    431      resolve(event);
    432    };
    433  });
    434 }
    435 
    436 function workerScript() {
    437  "use strict";
    438 
    439  self.wasmSupported = false;
    440 
    441  self.repr = function (_thing_) {
    442    if (typeof _thing_ == "undefined") {
    443      return "undefined";
    444    }
    445 
    446    let str;
    447 
    448    try {
    449      str = _thing_ + "";
    450    } catch (e) {
    451      return "[" + typeof _thing_ + "]";
    452    }
    453 
    454    if (typeof _thing_ == "function") {
    455      str = str.replace(/^\s+/, "");
    456      let idx = str.indexOf("{");
    457      if (idx != -1) {
    458        str = str.substr(0, idx) + "{...}";
    459      }
    460    }
    461 
    462    return str;
    463  };
    464 
    465  self.ok = function (_condition_, _name_, _diag_) {
    466    self.postMessage({
    467      op: "ok",
    468      condition: !!_condition_,
    469      name: _name_,
    470      diag: _diag_,
    471    });
    472  };
    473 
    474  self.is = function (_a_, _b_, _name_) {
    475    let pass = _a_ == _b_;
    476    let diag = pass ? "" : "got " + repr(_a_) + ", expected " + repr(_b_);
    477    ok(pass, _name_, diag);
    478  };
    479 
    480  self.isnot = function (_a_, _b_, _name_) {
    481    let pass = _a_ != _b_;
    482    let diag = pass ? "" : "didn't expect " + repr(_a_) + ", but got it";
    483    ok(pass, _name_, diag);
    484  };
    485 
    486  self.todo = function (_condition_, _name_, _diag_) {
    487    self.postMessage({
    488      op: "todo",
    489      condition: !!_condition_,
    490      name: _name_,
    491      diag: _diag_,
    492    });
    493  };
    494 
    495  self.info = function (_msg_) {
    496    self.postMessage({ op: "info", msg: _msg_ });
    497  };
    498 
    499  self.executeSoon = function (_fun_) {
    500    var channel = new MessageChannel();
    501    channel.port1.postMessage("");
    502    channel.port2.onmessage = function () {
    503      _fun_();
    504    };
    505  };
    506 
    507  self.finishTest = function () {
    508    self.ok(
    509      testSteps.constructor.name === "GeneratorFunction",
    510      "Async/await tests shouldn't call finishTest()"
    511    );
    512    if (self._expectingUncaughtException) {
    513      self.ok(
    514        false,
    515        "expectUncaughtException was called but no uncaught " +
    516          "exception was detected!"
    517      );
    518    }
    519    self.postMessage({ op: "done" });
    520  };
    521 
    522  self.grabEventAndContinueHandler = function (_event_) {
    523    testGenerator.next(_event_);
    524  };
    525 
    526  self.continueToNextStep = function () {
    527    executeSoon(function () {
    528      testGenerator.next();
    529    });
    530  };
    531 
    532  self.continueToNextStepSync = function () {
    533    testGenerator.next();
    534  };
    535 
    536  self.errorHandler = function (_event_) {
    537    ok(false, "indexedDB error, '" + _event_.target.error.name + "'");
    538    finishTest();
    539  };
    540 
    541  self.unexpectedSuccessHandler = function () {
    542    ok(false, "Got success, but did not expect it!");
    543    finishTest();
    544  };
    545 
    546  self.expectedErrorHandler = function (_name_) {
    547    return function (_event_) {
    548      is(_event_.type, "error", "Got an error event");
    549      is(_event_.target.error.name, _name_, "Expected error was thrown.");
    550      _event_.preventDefault();
    551      grabEventAndContinueHandler(_event_);
    552    };
    553  };
    554 
    555  self.ExpectError = function (_name_, _preventDefault_) {
    556    this._name = _name_;
    557    this._preventDefault = _preventDefault_;
    558  };
    559  self.ExpectError.prototype = {
    560    handleEvent(_event_) {
    561      is(_event_.type, "error", "Got an error event");
    562      is(_event_.target.error.name, this._name, "Expected error was thrown.");
    563      if (this._preventDefault) {
    564        _event_.preventDefault();
    565        _event_.stopPropagation();
    566      }
    567      grabEventAndContinueHandler(_event_);
    568    },
    569  };
    570 
    571  // TODO this is duplicate from the global compareKeys function defined above,
    572  // this duplication should be avoided (bug 1565986)
    573  self.compareKeys = function (_k1_, _k2_) {
    574    let t = typeof _k1_;
    575    if (t != typeof _k2_) {
    576      return false;
    577    }
    578 
    579    if (t !== "object") {
    580      return _k1_ === _k2_;
    581    }
    582 
    583    if (_k1_ instanceof Date) {
    584      return _k2_ instanceof Date && _k1_.getTime() === _k2_.getTime();
    585    }
    586 
    587    if (_k1_ instanceof Array) {
    588      if (!(_k2_ instanceof Array) || _k1_.length != _k2_.length) {
    589        return false;
    590      }
    591 
    592      for (let i = 0; i < _k1_.length; ++i) {
    593        if (!compareKeys(_k1_[i], _k2_[i])) {
    594          return false;
    595        }
    596      }
    597 
    598      return true;
    599    }
    600 
    601    if (_k1_ instanceof ArrayBuffer) {
    602      if (!(_k2_ instanceof ArrayBuffer)) {
    603        return false;
    604      }
    605 
    606      function arrayBuffersAreEqual(a, b) {
    607        if (a.byteLength != b.byteLength) {
    608          return false;
    609        }
    610        let ui8b = new Uint8Array(b);
    611        return new Uint8Array(a).every((val, i) => val === ui8b[i]);
    612      }
    613 
    614      return arrayBuffersAreEqual(_k1_, _k2_);
    615    }
    616 
    617    return false;
    618  };
    619 
    620  self.getRandomBuffer = function (_size_) {
    621    let buffer = new ArrayBuffer(_size_);
    622    is(buffer.byteLength, _size_, "Correct byte length");
    623    let view = new Uint8Array(buffer);
    624    for (let i = 0; i < _size_; i++) {
    625      view[i] = parseInt(Math.random() * 255);
    626    }
    627    return buffer;
    628  };
    629 
    630  self._expectingUncaughtException = false;
    631  self.expectUncaughtException = function (_expecting_) {
    632    self._expectingUncaughtException = !!_expecting_;
    633    self.postMessage({
    634      op: "expectUncaughtException",
    635      expecting: !!_expecting_,
    636    });
    637  };
    638 
    639  self._clearAllDatabasesCallback = undefined;
    640  self.clearAllDatabases = function (_callback_) {
    641    self._clearAllDatabasesCallback = _callback_;
    642    self.postMessage({ op: "clearAllDatabases" });
    643  };
    644 
    645  self.onerror = function (_message_, _file_, _line_) {
    646    if (self._expectingUncaughtException) {
    647      self._expectingUncaughtException = false;
    648      ok(
    649        true,
    650        "Worker: expected exception [" +
    651          _file_ +
    652          ":" +
    653          _line_ +
    654          "]: '" +
    655          _message_ +
    656          "'"
    657      );
    658      return false;
    659    }
    660    ok(
    661      false,
    662      "Worker: uncaught exception [" +
    663        _file_ +
    664        ":" +
    665        _line_ +
    666        "]: '" +
    667        _message_ +
    668        "'"
    669    );
    670    self.finishTest();
    671    self.close();
    672    return true;
    673  };
    674 
    675  self.isWasmSupported = function () {
    676    return self.wasmSupported;
    677  };
    678 
    679  self.getWasmModule = function (_binary_) {
    680    let module = new WebAssembly.Module(_binary_);
    681    return module;
    682  };
    683 
    684  self.verifyWasmModule = function (_module) {
    685    self.todo(false, "Need a verifyWasmModule implementation on workers");
    686    self.continueToNextStep();
    687  };
    688 
    689  self.onmessage = function (_event_) {
    690    let message = _event_.data;
    691    switch (message.op) {
    692      case "load":
    693        info("Worker: loading " + JSON.stringify(message.files));
    694        self.importScripts(message.files);
    695        self.postMessage({ op: "loaded" });
    696        break;
    697 
    698      case "start":
    699        self.wasmSupported = message.wasmSupported;
    700        executeSoon(async function () {
    701          info("Worker: starting tests");
    702          if (testSteps.constructor.name === "AsyncFunction") {
    703            await testSteps();
    704            if (self._expectingUncaughtException) {
    705              self.ok(
    706                false,
    707                "expectUncaughtException was called but no " +
    708                  "uncaught exception was detected!"
    709              );
    710            }
    711            self.postMessage({ op: "done" });
    712          } else {
    713            ok(
    714              testSteps.constructor.name === "GeneratorFunction",
    715              "Unsupported function type"
    716            );
    717            testGenerator.next();
    718          }
    719        });
    720        break;
    721 
    722      case "clearAllDatabasesDone":
    723        info("Worker: all databases are cleared");
    724        if (self._clearAllDatabasesCallback) {
    725          self._clearAllDatabasesCallback();
    726        }
    727        break;
    728 
    729      default:
    730        throw new Error(
    731          "Received a bad message from parent: " + JSON.stringify(message)
    732        );
    733    }
    734  };
    735 
    736  self.expectingSuccess = function (_request_) {
    737    return new Promise(function (_resolve_, _reject_) {
    738      _request_.onerror = function (_event_) {
    739        ok(false, "indexedDB error, '" + _event_.target.error.name + "'");
    740        _reject_(_event_);
    741      };
    742      _request_.onsuccess = function (_event_) {
    743        _resolve_(_event_);
    744      };
    745      _request_.onupgradeneeded = function (_event_) {
    746        ok(false, "Got upgrade, but did not expect it!");
    747        _reject_(_event_);
    748      };
    749    });
    750  };
    751 
    752  self.expectingUpgrade = function (_request_) {
    753    return new Promise(function (_resolve_, _reject_) {
    754      _request_.onerror = function (_event_) {
    755        ok(false, "indexedDB error, '" + _event_.target.error.name + "'");
    756        _reject_(_event_);
    757      };
    758      _request_.onupgradeneeded = function (_event_) {
    759        _resolve_(_event_);
    760      };
    761      _request_.onsuccess = function (_event_) {
    762        ok(false, "Got success, but did not expect it!");
    763        _reject_(_event_);
    764      };
    765    });
    766  };
    767 
    768  self.postMessage({ op: "ready" });
    769 }
    770 
    771 async function executeWorkerTestAndCleanUp(testScriptPath) {
    772  info("Running test in a worker");
    773 
    774  let workerScriptBlob = new Blob(["(" + workerScript.toString() + ")();"], {
    775    type: "text/javascript",
    776  });
    777  let workerScriptURL = URL.createObjectURL(workerScriptBlob);
    778 
    779  let worker;
    780  try {
    781    await new Promise(function (resolve, reject) {
    782      worker = new Worker(workerScriptURL);
    783 
    784      worker._expectingUncaughtException = false;
    785      worker.onerror = function (event) {
    786        if (worker._expectingUncaughtException) {
    787          ok(true, "Worker had an expected error: " + event.message);
    788          worker._expectingUncaughtException = false;
    789          event.preventDefault();
    790          return;
    791        }
    792        ok(false, "Worker had an error: " + event.message);
    793        worker.terminate();
    794        reject();
    795      };
    796 
    797      worker.onmessage = function (event) {
    798        let message = event.data;
    799        switch (message.op) {
    800          case "ok":
    801            SimpleTest.ok(
    802              message.condition,
    803              `${message.name}: ${message.diag}`
    804            );
    805            break;
    806 
    807          case "todo":
    808            todo(message.condition, message.name, message.diag);
    809            break;
    810 
    811          case "info":
    812            info(message.msg);
    813            break;
    814 
    815          case "ready":
    816            worker.postMessage({ op: "load", files: [testScriptPath] });
    817            break;
    818 
    819          case "loaded":
    820            worker.postMessage({
    821              op: "start",
    822              wasmSupported: isWasmSupported(),
    823            });
    824            break;
    825 
    826          case "done":
    827            ok(true, "Worker finished");
    828            resolve();
    829            break;
    830 
    831          case "expectUncaughtException":
    832            worker._expectingUncaughtException = message.expecting;
    833            break;
    834 
    835          case "clearAllDatabases":
    836            clearAllDatabases(function () {
    837              worker.postMessage({ op: "clearAllDatabasesDone" });
    838            });
    839            break;
    840 
    841          default:
    842            ok(
    843              false,
    844              "Received a bad message from worker: " + JSON.stringify(message)
    845            );
    846            reject();
    847        }
    848      };
    849    });
    850 
    851    URL.revokeObjectURL(workerScriptURL);
    852  } catch (e) {
    853    info("Unexpected thing happened: " + e);
    854  }
    855 
    856  return new Promise(function (resolve) {
    857    info("Cleaning up the databases");
    858 
    859    if (worker._expectingUncaughtException) {
    860      ok(
    861        false,
    862        "expectUncaughtException was called but no uncaught " +
    863          "exception was detected!"
    864      );
    865    }
    866 
    867    worker.terminate();
    868    worker = null;
    869 
    870    clearAllDatabases(resolve);
    871  });
    872 }