Merge inbound to mozilla-central. a=merge
authorGurzau Raul <rgurzau@mozilla.com>
Wed, 10 Oct 2018 07:35:14 +0300
changeset 488774 bf31de5be0dcd71f3257485c66db5627ec3ed205
parent 488773 b85ace8c5339f5f24e7d104b4a8146dc92bb694d (current diff)
parent 488688 3854b434407792df13fad738667b042e2ebb0449 (diff)
child 488775 436a8eff6195462f6df195f3dcbbb3fbfe5a7e6b
child 488782 f5681e1f56e6d77989c3f63f9a017f5c94bd74af
child 488803 fc14ec1e17384de01a6d439b5cdcb1ce166f5118
push id246
push userfmarier@mozilla.com
push dateSat, 13 Oct 2018 00:15:40 +0000
reviewersmerge
milestone64.0a1
Merge inbound to mozilla-central. a=merge
devtools/client/debugger/test/mochitest/addon1.xpi
devtools/client/debugger/test/mochitest/addon2.xpi
devtools/client/debugger/test/mochitest/browser_dbg_chrome-create.js
devtools/client/debugger/test/mochitest/browser_dbg_chrome-debugging.js
devtools/client/debugger/test/mochitest/browser_dbg_global-method-override.js
devtools/client/debugger/test/mochitest/browser_dbg_globalactor.js
devtools/client/debugger/test/mochitest/browser_dbg_listaddons.js
devtools/client/debugger/test/mochitest/browser_dbg_listtabs-01.js
devtools/client/debugger/test/mochitest/browser_dbg_listtabs-02.js
devtools/client/debugger/test/mochitest/browser_dbg_listtabs-03.js
devtools/client/debugger/test/mochitest/browser_dbg_multiple-windows.js
devtools/client/debugger/test/mochitest/browser_dbg_navigation.js
devtools/client/debugger/test/mochitest/browser_dbg_target-scoped-actor-01.js
devtools/client/debugger/test/mochitest/browser_dbg_target-scoped-actor-02.js
devtools/client/debugger/test/mochitest/doc_global-method-override.html
js/src/jit-test/tests/basic/bug-1271507-2.js
modules/libpref/init/all.js
testing/web-platform/meta/css/css-contain/contain-layout-baseline-001.html.ini
testing/web-platform/meta/css/css-contain/contain-layout-flexbox-001.html.ini
testing/web-platform/meta/css/css-contain/contain-size-021.html.ini
testing/web-platform/meta/css/css-contain/contain-size-023.html.ini
testing/web-platform/meta/css/css-contain/contain-size-027.html.ini
testing/web-platform/meta/css/css-contain/contain-size-baseline-001.html.ini
testing/web-platform/meta/css/css-contain/contain-size-flexbox-001.html.ini
--- a/browser/base/content/test/performance/browser_preferences_usage.js
+++ b/browser/base/content/test/performance/browser_preferences_usage.js
@@ -1,11 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
+const DEFAULT_PROCESS_COUNT = Services.prefs.getDefaultBranch(null).getIntPref("dom.ipc.processCount");
+
 /**
  * A test that checks whether any preference getter from the given list
  * of stats was called more often than the max parameter.
  *
  * @param {Array}  stats - an array of [prefName, accessCount] tuples
  * @param {Number} max - the maximum number of times any of the prefs should
  *                 have been called.
  * @param {Object} whitelist (optional) - an object that defines
@@ -115,17 +117,20 @@ add_task(async function startup() {
 
   ok(startupRecorder.data.prefStats, "startupRecorder has prefStats");
 
   checkPrefGetters(startupRecorder.data.prefStats, max, whitelist);
 });
 
 // This opens 10 tabs and checks pref getters.
 add_task(async function open_10_tabs() {
-  let max = 15;
+  // This is somewhat arbitrary. When we had a default of 4 content processes
+  // the value was 15. We need to scale it as we increase the number of
+  // content processes so we approximate with 4 * process_count.
+  const max = 4 * DEFAULT_PROCESS_COUNT;
 
   let whitelist = {
     "layout.css.dpi": {
       max: 35,
     },
     "browser.zoom.full": {
       min: 10,
       max: 25,
@@ -141,20 +146,16 @@ add_task(async function open_10_tabs() {
     "security.insecure_connection_text.enabled": {
       min: 10,
       max: 18,
     },
     "security.insecure_connection_text.pbmode.enabled": {
       min: 10,
       max: 18,
     },
-    "dom.ipc.processCount": {
-      min: 10,
-      max: 15,
-    },
     "browser.startup.record": {
       max: 20,
     },
     "browser.tabs.remote.logSwitchTiming": {
       max: 25,
     },
     "network.loadinfo.skip_type_assertion": {
       // This is accessed in debug only.
--- a/browser/base/content/test/tabcrashed/browser.ini
+++ b/browser/base/content/test/tabcrashed/browser.ini
@@ -1,14 +1,14 @@
 [DEFAULT]
 skip-if = (!e10s || !crashreporter)
 support-files =
   head.js
 
 [browser_autoSubmitRequest.js]
 [browser_clearEmail.js]
 [browser_noPermanentKey.js]
-skip-if = (os == "linux") || verify # Bug 1383315
+skip-if = true # Bug 1383315
 [browser_showForm.js]
 [browser_shown.js]
 skip-if = (verify && !debug && (os == 'win'))
 [browser_shownRestartRequired.js]
 [browser_withoutDump.js]
--- a/browser/components/extensions/test/browser/browser_ext_slow_script.js
+++ b/browser/components/extensions/test/browser/browser_ext_slow_script.js
@@ -1,26 +1,28 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
+const DEFAULT_PROCESS_COUNT = Services.prefs.getDefaultBranch(null).getIntPref("dom.ipc.processCount");
+
 add_task(async function test_slow_content_script() {
   // Make sure we get a new process for our tab, or our reportProcessHangs
   // preferences value won't apply to it.
   await SpecialPowers.pushPrefEnv({
     set: [
       ["dom.ipc.processCount", 1],
       ["dom.ipc.keepProcessesAlive.web", 0],
     ],
   });
   await SpecialPowers.popPrefEnv();
 
   await SpecialPowers.pushPrefEnv({
     set: [
-      ["dom.ipc.processCount", 8],
+      ["dom.ipc.processCount", DEFAULT_PROCESS_COUNT * 2],
       ["dom.ipc.processPrelaunch.enabled", false],
       ["dom.ipc.reportProcessHangs", true],
     ],
   });
 
   let extension = ExtensionTestUtils.loadExtension({
     useAddonManager: "temporary",
 
--- a/browser/components/preferences/in-content/main.xul
+++ b/browser/components/preferences/in-content/main.xul
@@ -608,16 +608,17 @@
         <menupopup>
           <menuitem label="1" value="1"/>
           <menuitem label="2" value="2"/>
           <menuitem label="3" value="3"/>
           <menuitem label="4" value="4"/>
           <menuitem label="5" value="5"/>
           <menuitem label="6" value="6"/>
           <menuitem label="7" value="7"/>
+          <menuitem label="8" value="8"/>
         </menupopup>
       </menulist>
     </hbox>
     <description id="contentProcessCountEnabledDescription" class="tip-caption" data-l10n-id="performance-limit-content-process-enabled-desc"/>
     <description id="contentProcessCountDisabledDescription" class="tip-caption" data-l10n-id="performance-limit-content-process-blocked-desc">
       <html:a class="text-link" data-l10n-name="learn-more" href="https://wiki.mozilla.org/Electrolysis"/>
     </description>
   </vbox>
--- a/browser/components/preferences/in-content/tests/browser_performance_e10srollout.js
+++ b/browser/components/preferences/in-content/tests/browser_performance_e10srollout.js
@@ -39,30 +39,32 @@ add_task(async function testPrefsAreDefa
 
   BrowserTestUtils.removeTab(gBrowser.selectedTab);
 
   Services.prefs.clearUserPref("dom.ipc.processCount");
   Services.prefs.setBoolPref("browser.preferences.defaultPerformanceSettings.enabled", true);
 });
 
 add_task(async function testPrefsSetByUser() {
-  Services.prefs.setIntPref("dom.ipc.processCount", DEFAULT_PROCESS_COUNT + 2);
+  const kNewCount = DEFAULT_PROCESS_COUNT - 2;
+
+  Services.prefs.setIntPref("dom.ipc.processCount", kNewCount);
   Services.prefs.setBoolPref("browser.preferences.defaultPerformanceSettings.enabled", false);
 
   let prefs = await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
   is(prefs.selectedPane, "paneGeneral", "General pane was selected");
 
   let doc = gBrowser.contentDocument;
   let performanceSettings = doc.querySelector("#performanceSettings");
   is(performanceSettings.hidden, false, "performance settings section is shown");
 
   let contentProcessCount = doc.querySelector("#contentProcessCount");
   is(contentProcessCount.disabled, false, "process count control should be enabled");
-  is(Services.prefs.getIntPref("dom.ipc.processCount"), DEFAULT_PROCESS_COUNT + 2, "process count should be the set value");
-  is(contentProcessCount.selectedItem.value, DEFAULT_PROCESS_COUNT + 2, "selected item should be the set one");
+  is(Services.prefs.getIntPref("dom.ipc.processCount"), kNewCount, "process count should be the set value");
+  is(contentProcessCount.selectedItem.value, kNewCount, "selected item should be the set one");
 
   let useRecommendedPerformanceSettings = doc.querySelector("#useRecommendedPerformanceSettings");
   useRecommendedPerformanceSettings.click();
 
   is(Services.prefs.getBoolPref("browser.preferences.defaultPerformanceSettings.enabled"), true,
     "pref value should be true after clicking on checkbox");
   is(Services.prefs.getIntPref("dom.ipc.processCount"), DEFAULT_PROCESS_COUNT,
     "process count should be default value");
--- a/devtools/client/debugger/new/test/mochitest/browser.ini
+++ b/devtools/client/debugger/new/test/mochitest/browser.ini
@@ -619,16 +619,17 @@ support-files =
   examples/doc-sourcemaps2.html
   examples/doc-sourcemaps3.html
   examples/doc-sourcemap-bogus.html
   examples/doc-sources.html
   examples/doc-strict.html
   examples/doc-pause-points.html
   examples/doc-return-values.html
   examples/doc-wasm-sourcemaps.html
+  examples/doc_global-method-override.html
   examples/asm.js
   examples/async.js
   examples/bogus-map.js
   examples/entry.js
   examples/exceptions.js
   examples/long.js
   examples/math.min.js
   examples/nested/nested-source.js
@@ -681,16 +682,18 @@ skip-if = (verify && !debug && (os == 'l
 skip-if = (os == "win" && ccov) # Bug 1424154
 [browser_dbg-debugger-buttons.js]
 [browser_dbg-editor-gutter.js]
 [browser_dbg-editor-select.js]
 [browser_dbg-editor-highlight.js]
 [browser_dbg-ember-quickstart.js]
 [browser_dbg-expressions.js]
 [browser_dbg-expressions-error.js]
+[browser_dbg_global-method-override.js]
+skip-if = e10s && debug
 [browser_dbg-iframes.js]
 [browser_dbg-inline-cache.js]
 [browser_dbg-keyboard-navigation.js]
 [browser_dbg-keyboard-shortcuts.js]
 skip-if = os == "linux" # bug 1351952
 [browser_dbg-layout-changes.js]
 [browser_dbg-outline.js]
 skip-if = verify
rename from devtools/client/debugger/test/mochitest/browser_dbg_global-method-override.js
rename to devtools/client/debugger/new/test/mochitest/browser_dbg_global-method-override.js
--- a/devtools/client/debugger/test/mochitest/browser_dbg_global-method-override.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg_global-method-override.js
@@ -1,26 +1,18 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that scripts that override properties of the global object, like
- * toString don't break the debugger. The test page used to cause the debugger
+ * toString, don't break the debugger. The test page used to cause the debugger
  * to throw when trying to attach to the thread actor.
  */
 
-const TAB_URL = EXAMPLE_URL + "doc_global-method-override.html";
+"use strict";
 
-function test() {
-  let options = {
-    source: TAB_URL,
-    line: 1
-  };
-  initDebugger(TAB_URL, options).then(([aTab, aPanel]) => {
-    let gDebugger = aPanel.panelWin;
-    ok(gDebugger, "Should have a debugger available.");
-    is(gDebugger.gThreadClient.state, "attached", "Debugger should be attached.");
-
-    closeDebuggerAndFinish(aPanel);
-  });
-}
+add_task(async function() {
+  const dbg = await initDebugger("doc_global-method-override.html");
+  ok(dbg, "Should have a debugger available.");
+  is(dbg.toolbox.threadClient.state, "attached", "Debugger should be attached.");
+});
rename from devtools/client/debugger/test/mochitest/doc_global-method-override.html
rename to devtools/client/debugger/new/test/mochitest/examples/doc_global-method-override.html
--- a/devtools/client/debugger/test/mochitest/browser.ini
+++ b/devtools/client/debugger/test/mochitest/browser.ini
@@ -1,18 +1,16 @@
 # Tests in this directory are split into two manifests (this and browser2.ini)
 # to facilitate better chunking; see bug 1294489.
 
 [DEFAULT]
 tags = devtools
 subsuite = devtools
 skip-if = (os == 'linux' && debug && bits == 32)
 support-files =
-  addon1.xpi
-  addon2.xpi
   addon4.xpi
   addon5.xpi
   addon-webext-contentscript.xpi
   addon-source/browser_dbg_addon5/*
   code_binary_search.coffee
   code_binary_search.js
   code_binary_search.map
   code_breakpoints-break-on-last-line-of-script-on-reload.js
@@ -65,17 +63,16 @@ support-files =
   doc_empty-tab-02.html
   doc_event-listeners-01.html
   doc_event-listeners-02.html
   doc_event-listeners-03.html
   doc_frame-parameters.html
   doc_function-display-name.html
   doc_function-jump.html
   doc_function-search.html
-  doc_global-method-override.html
   doc_iframes.html
   doc_included-script.html
   doc_inline-debugger-statement.html
   doc_inline-script.html
   doc_large-array-buffer.html
   doc_listworkers-tab.html
   doc_map-set.html
   doc_minified.html
@@ -142,20 +139,16 @@ skip-if = e10s && debug
 [browser_dbg_breakpoints-eval.js]
 skip-if = e10s && debug
 [browser_dbg_breakpoints-new-script.js]
 skip-if = e10s && debug
 [browser_dbg_breakpoints-other-tabs.js]
 skip-if = e10s && debug
 [browser_dbg_bug-896139.js]
 skip-if = e10s && debug
-[browser_dbg_chrome-create.js]
-skip-if = (e10s && debug) || (verify && os == "linux") # Exit code mismatch with verify
-[browser_dbg_chrome-debugging.js]
-skip-if = e10s && debug
 [browser_dbg_clean-exit.js]
 skip-if = true # Bug 1044985 (racy test)
 [browser_dbg_closure-inspection.js]
 skip-if = e10s && debug
 [browser_dbg_conditional-breakpoints-01.js]
 skip-if = e10s && debug
 [browser_dbg_conditional-breakpoints-02.js]
 skip-if = e10s && debug
@@ -188,32 +181,17 @@ skip-if = e10s && debug
 [browser_dbg_event-listeners-01.js]
 skip-if = e10s && debug
 [browser_dbg_event-listeners-02.js]
 skip-if = e10s && debug
 [browser_dbg_event-listeners-03.js]
 skip-if = e10s && debug
 [browser_dbg_file-reload.js]
 skip-if = e10s && debug
-[browser_dbg_global-method-override.js]
-skip-if = e10s && debug
-[browser_dbg_globalactor.js]
-skip-if = e10s
 [browser_dbg_host-layout.js]
 skip-if = e10s && debug
 [browser_dbg_jump-to-function-definition.js]
 skip-if = e10s && debug
 [browser_dbg_iframes.js]
 skip-if = e10s # TODO
 [browser_dbg_interrupts.js]
 skip-if = e10s && debug
-[browser_dbg_listaddons.js]
-skip-if = e10s && debug
-tags = addons
-[browser_dbg_listtabs-01.js]
-[browser_dbg_listtabs-02.js]
-skip-if = true # Never worked for remote frames, needs a mock DebuggerServerConnection
-[browser_dbg_listtabs-03.js]
-skip-if = e10s && debug
 [browser_dbg_listworkers.js]
-[browser_dbg_multiple-windows.js]
-[browser_dbg_navigation.js]
-skip-if = e10s && debug
--- a/devtools/client/debugger/test/mochitest/browser2.ini
+++ b/devtools/client/debugger/test/mochitest/browser2.ini
@@ -1,18 +1,16 @@
 # Tests in this directory are split into two manifests (this and browser.ini)
 # to facilitate better chunking; see bug 1294489.
 
 [DEFAULT]
 tags = devtools
 subsuite = devtools
 skip-if = (os == 'linux' && debug && bits == 32)
 support-files =
-  addon1.xpi
-  addon2.xpi
   addon4.xpi
   addon5.xpi
   addon-webext-contentscript.xpi
   addon-source/browser_dbg_addon5/*
   code_binary_search.coffee
   code_binary_search.js
   code_binary_search.map
   code_breakpoints-break-on-last-line-of-script-on-reload.js
@@ -65,17 +63,16 @@ support-files =
   doc_empty-tab-02.html
   doc_event-listeners-01.html
   doc_event-listeners-02.html
   doc_event-listeners-03.html
   doc_frame-parameters.html
   doc_function-display-name.html
   doc_function-jump.html
   doc_function-search.html
-  doc_global-method-override.html
   doc_iframes.html
   doc_included-script.html
   doc_inline-debugger-statement.html
   doc_inline-script.html
   doc_large-array-buffer.html
   doc_listworkers-tab.html
   doc_map-set.html
   doc_minified.html
@@ -154,18 +151,16 @@ uses-unsafe-cpows = true
 [browser_dbg_sources-iframe-reload.js]
 uses-unsafe-cpows = true
 skip-if = (os == "linux" && debug && bits == 64) #Bug 1455225, disable on Linux x64 debug for frequent failures
 [browser_dbg_sources-bookmarklet.js]
 uses-unsafe-cpows = true
 skip-if = e10s && debug
 [browser_dbg_split-console-paused-reload.js]
 skip-if = true # Bug 1288348 - previously e10s && debug
-[browser_dbg_target-scoped-actor-01.js]
-[browser_dbg_target-scoped-actor-02.js]
 [browser_dbg_terminate-on-tab-close.js]
 uses-unsafe-cpows = true
 skip-if = e10s && debug
 [browser_dbg_worker-console-01.js]
 skip-if = true # bug 1368569
 [browser_dbg_worker-console-02.js]
 skip-if = e10s && debug
 [browser_dbg_worker-console-03.js]
deleted file mode 100644
--- a/devtools/client/debugger/test/mochitest/browser_dbg_chrome-create.js
+++ /dev/null
@@ -1,70 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests that a chrome debugger can be created in a new process.
- */
-
-// There are shutdown issues for which multiple rejections are left uncaught.
-// See bug 1018184 for resolving these issues.
-const { PromiseTestUtils } = scopedCuImport("resource://testing-common/PromiseTestUtils.jsm");
-PromiseTestUtils.whitelistRejectionsGlobally(/File closed/);
-PromiseTestUtils.whitelistRejectionsGlobally(/NS_ERROR_FAILURE/);
-
-var gProcess;
-
-function test() {
-  // Windows XP and 8.1 test slaves are terribly slow at this test.
-  requestLongerTimeout(5);
-  Services.prefs.setBoolPref("devtools.chrome.enabled", true);
-  Services.prefs.setBoolPref("devtools.debugger.remote-enabled", true);
-
-  initChromeDebugger(aOnClose).then(aProcess => {
-    gProcess = aProcess;
-
-    info("Starting test...");
-    performTest();
-  });
-}
-
-function performTest() {
-  ok(gProcess._dbgProcess,
-    "The remote debugger process wasn't created properly!");
-  ok(gProcess._dbgProcess.exitCode == null,
-    "The remote debugger process isn't running!");
-  is(typeof gProcess._dbgProcess.pid, "number",
-    "The remote debugger process doesn't have a pid (?!)");
-
-  info("process location: " + gProcess._dbgProcess.location);
-  info("process pid: " + gProcess._dbgProcess.pid);
-  info("process name: " + gProcess._dbgProcess.processName);
-  info("process sig: " + gProcess._dbgProcess.processSignature);
-
-  ok(gProcess._dbgProfilePath,
-    "The remote debugger profile wasn't created properly!");
-  is(gProcess._dbgProfilePath, OS.Path.join(OS.Constants.Path.profileDir, "chrome_debugger_profile"),
-     "The remote debugger profile isn't where we expect it!");
-
-  info("profile path: " + gProcess._dbgProfilePath);
-
-  gProcess.close();
-}
-
-function aOnClose() {
-  is(gProcess._dbgProcess.exitCode, (Services.appinfo.OS == "WINNT" ? -9 : -15),
-    "The remote debugger process didn't die cleanly.");
-
-  info("process exit value: " + gProcess._dbgProcess.exitValue);
-
-  info("profile path: " + gProcess._dbgProfilePath);
-
-  finish();
-}
-
-registerCleanupFunction(function () {
-  Services.prefs.clearUserPref("devtools.chrome.enabled");
-  Services.prefs.clearUserPref("devtools.debugger.remote-enabled");
-  gProcess = null;
-});
deleted file mode 100644
--- a/devtools/client/debugger/test/mochitest/browser_dbg_chrome-debugging.js
+++ /dev/null
@@ -1,89 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests that chrome debugging works.
- */
-
-const TAB_URL = EXAMPLE_URL + "doc_inline-debugger-statement.html";
-
-var gClient, gThreadClient;
-var gAttached = promise.defer();
-var gNewChromeSource = promise.defer();
-
-var { DevToolsLoader } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
-var customLoader = new DevToolsLoader();
-customLoader.invisibleToDebugger = true;
-var { DebuggerServer } = customLoader.require("devtools/server/main");
-
-function test() {
-  DebuggerServer.init();
-  DebuggerServer.registerAllActors();
-  DebuggerServer.allowChromeProcess = true;
-
-  let transport = DebuggerServer.connectPipe();
-  gClient = new DebuggerClient(transport);
-  gClient.connect().then(([aType, aTraits]) => {
-    is(aType, "browser",
-      "Root actor should identify itself as a browser.");
-
-    promise.all([gAttached.promise, gNewChromeSource.promise])
-      .then(resumeAndCloseConnection)
-      .then(finish)
-      .catch(aError => {
-        ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
-      });
-
-    testParentProcessTargetActor();
-  });
-}
-
-function testParentProcessTargetActor() {
-  gClient.getProcess().then(aResponse => {
-    let actor = aResponse.form.actor;
-    gClient.attachTarget(actor).then(([response, tabClient]) => {
-      tabClient.attachThread(null).then(([aResponse, aThreadClient]) => {
-        gThreadClient = aThreadClient;
-        gThreadClient.addListener("newSource", onNewSource);
-
-        if (aResponse.error) {
-          ok(false, "Couldn't attach to the chrome debugger.");
-          gAttached.reject();
-        } else {
-          ok(true, "Attached to the chrome debugger.");
-          gAttached.resolve();
-
-          // Ensure that a new chrome global will be created.
-          gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, "about:mozilla");
-        }
-      });
-    });
-  });
-}
-
-function onNewSource(aEvent, aPacket) {
-  if (aPacket.source.url.startsWith("chrome:")) {
-    ok(true, "Received a new chrome source: " + aPacket.source.url);
-
-    gThreadClient.removeListener("newSource", onNewSource);
-    gNewChromeSource.resolve();
-  }
-}
-
-function resumeAndCloseConnection() {
-  let deferred = promise.defer();
-  gThreadClient.resume(() => deferred.resolve(gClient.close()));
-  return deferred.promise;
-}
-
-registerCleanupFunction(function () {
-  gClient = null;
-  gThreadClient = null;
-  gAttached = null;
-  gNewChromeSource = null;
-
-  customLoader = null;
-  DebuggerServer = null;
-});
--- a/devtools/client/preferences/devtools-client.js
+++ b/devtools/client/preferences/devtools-client.js
@@ -302,24 +302,35 @@ pref("devtools.hud.loglimit", 10000);
 pref("devtools.editor.tabsize", 2);
 pref("devtools.editor.expandtab", true);
 pref("devtools.editor.keymap", "default");
 pref("devtools.editor.autoclosebrackets", true);
 pref("devtools.editor.detectindentation", true);
 pref("devtools.editor.enableCodeFolding", true);
 pref("devtools.editor.autocomplete", true);
 
+// The width of the viewport.
+pref("devtools.responsive.viewport.width", 320);
+// The height of the viewport.
+pref("devtools.responsive.viewport.height", 480);
+// The pixel ratio of the viewport.
+pref("devtools.responsive.viewport.pixelRatio", 0);
 // Whether or not the viewports are left aligned.
 pref("devtools.responsive.leftAlignViewport.enabled", false);
 // Whether to reload when touch simulation is toggled
 pref("devtools.responsive.reloadConditions.touchSimulation", false);
 // Whether to reload when user agent is changed
 pref("devtools.responsive.reloadConditions.userAgent", false);
 // Whether to show the notification about reloading to apply emulation
 pref("devtools.responsive.reloadNotification.enabled", true);
+// Whether or not touch simulation is enabled.
+pref("devtools.responsive.touchSimulation.enabled", false);
+// The user agent of the viewport.
+pref("devtools.responsive.userAgent", "");
+
 // Whether to show the settings onboarding tooltip only in release or beta builds.
 #if defined(RELEASE_OR_BETA)
 pref("devtools.responsive.show-setting-tooltip", true);
 #else
 pref("devtools.responsive.show-setting-tooltip", false);
 #endif
 // Show the custom user agent input in Nightly builds.
 #if defined(NIGHTLY_BUILD)
--- a/devtools/client/responsive.html/actions/devices.js
+++ b/devtools/client/responsive.html/actions/devices.js
@@ -1,28 +1,32 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const Services = require("Services");
+const asyncStorage = require("devtools/shared/async-storage");
 
 const {
   ADD_DEVICE,
   ADD_DEVICE_TYPE,
   LOAD_DEVICE_LIST_START,
   LOAD_DEVICE_LIST_ERROR,
   LOAD_DEVICE_LIST_END,
   REMOVE_DEVICE,
   UPDATE_DEVICE_DISPLAYED,
   UPDATE_DEVICE_MODAL,
 } = require("./index");
+const { post } = require("../utils/message");
 
 const { addDevice, getDevices, removeDevice } = require("devtools/client/shared/devices");
+const { changeUserAgent, toggleTouchSimulation } = require("./ui");
+const { changeDevice, changePixelRatio, resizeViewport } = require("./viewports");
 
 const DISPLAYED_DEVICES_PREF = "devtools.responsive.html.displayedDeviceList";
 
 /**
  * Returns an object containing the user preference of displayed devices.
  *
  * @return {Object} containing two Sets:
  * - added: Names of the devices that were explicitly enabled by the user
@@ -155,16 +159,55 @@ module.exports = {
       if (!devices.TYPES.find(type => type == "custom")) {
         dispatch(module.exports.addDeviceType("custom"));
       }
 
       dispatch({ type: LOAD_DEVICE_LIST_END });
     };
   },
 
+  restoreDeviceState() {
+    return async function(dispatch, getState) {
+      const deviceState = await asyncStorage.getItem("devtools.responsive.deviceState");
+      if (!deviceState) {
+        return;
+      }
+
+      const { id, device: deviceName, deviceType } = deviceState;
+      const devices = getState().devices;
+
+      if (!devices.types.includes(deviceType)) {
+        // Can't find matching device type.
+        return;
+      }
+
+      const device = devices[deviceType].find(d => d.name === deviceName);
+      if (!device) {
+        // Can't find device with the same device name.
+        return;
+      }
+
+      post(window, {
+        type: "viewport-resize",
+        width: device.width,
+        height: device.height,
+      });
+      post(window, {
+        type: "change-device",
+        device,
+      });
+
+      dispatch(resizeViewport(id, device.width, device.height));
+      dispatch(changeDevice(id, device.name, deviceType));
+      dispatch(changePixelRatio(id, device.pixelRatio));
+      dispatch(changeUserAgent(device.userAgent));
+      dispatch(toggleTouchSimulation(device.touch));
+    };
+  },
+
   updateDeviceModal(isOpen, modalOpenedFromViewport = null) {
     return {
       type: UPDATE_DEVICE_MODAL,
       isOpen,
       modalOpenedFromViewport,
     };
   },
 
--- a/devtools/client/responsive.html/actions/viewports.js
+++ b/devtools/client/responsive.html/actions/viewports.js
@@ -1,16 +1,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* eslint-env browser */
 
 "use strict";
 
+const asyncStorage = require("devtools/shared/async-storage");
+
 const {
   ADD_VIEWPORT,
   CHANGE_DEVICE,
   CHANGE_PIXEL_RATIO,
   REMOVE_DEVICE_ASSOCIATION,
   RESIZE_VIEWPORT,
   ROTATE_VIEWPORT
 } = require("./index");
@@ -28,21 +30,30 @@ module.exports = {
       userContextId,
     };
   },
 
   /**
    * Change the viewport device.
    */
   changeDevice(id, device, deviceType) {
-    return {
-      type: CHANGE_DEVICE,
-      id,
-      device,
-      deviceType,
+    return async function(dispatch) {
+      dispatch({
+        type: CHANGE_DEVICE,
+        id,
+        device,
+        deviceType,
+      });
+
+      try {
+        await asyncStorage.setItem("devtools.responsive.deviceState",
+          { id, device, deviceType });
+      } catch (e) {
+        console.error(e);
+      }
     };
   },
 
   /**
    * Change the viewport pixel ratio.
    */
   changePixelRatio(id, pixelRatio = 0) {
     return {
@@ -51,20 +62,25 @@ module.exports = {
       pixelRatio,
     };
   },
 
   /**
    * Remove the viewport's device assocation.
    */
   removeDeviceAssociation(id) {
-    post(window, "remove-device-association");
-    return {
-      type: REMOVE_DEVICE_ASSOCIATION,
-      id,
+    return async function(dispatch) {
+      post(window, "remove-device-association");
+
+      dispatch({
+        type: REMOVE_DEVICE_ASSOCIATION,
+        id,
+      });
+
+      await asyncStorage.removeItem("devtools.responsive.deviceState");
     };
   },
 
   /**
    * Resize the viewport.
    */
   resizeViewport(id, width, height) {
     return {
--- a/devtools/client/responsive.html/index.js
+++ b/devtools/client/responsive.html/index.js
@@ -17,17 +17,17 @@ const Telemetry = require("devtools/clie
 const { createFactory, createElement } =
   require("devtools/client/shared/vendor/react");
 const ReactDOM = require("devtools/client/shared/vendor/react-dom");
 const { Provider } = require("devtools/client/shared/vendor/react-redux");
 
 const message = require("./utils/message");
 const App = createFactory(require("./components/App"));
 const Store = require("./store");
-const { loadDevices } = require("./actions/devices");
+const { loadDevices, restoreDeviceState } = require("./actions/devices");
 const { addViewport, resizeViewport } = require("./actions/viewports");
 const { changeDisplayPixelRatio } = require("./actions/ui");
 
 // Exposed for use by tests
 window.require = require;
 
 const bootstrap = {
 
@@ -73,17 +73,19 @@ const bootstrap = {
 };
 
 // manager.js sends a message to signal init
 message.wait(window, "init").then(() => bootstrap.init());
 
 // manager.js sends a message to signal init is done, which can be used for delayed
 // startup work that shouldn't block initial load
 message.wait(window, "post-init").then(() => {
-  bootstrap.dispatch(loadDevices());
+  bootstrap.store.dispatch(loadDevices()).then(() => {
+    bootstrap.dispatch(restoreDeviceState());
+  });
 });
 
 window.addEventListener("unload", function() {
   bootstrap.destroy();
 }, {once: true});
 
 // Allows quick testing of actions from the console
 window.dispatch = action => bootstrap.dispatch(action);
--- a/devtools/client/responsive.html/manager.js
+++ b/devtools/client/responsive.html/manager.js
@@ -2,35 +2,36 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { Ci } = require("chrome");
 const promise = require("promise");
 const Services = require("Services");
+const asyncStorage = require("devtools/shared/async-storage");
 const EventEmitter = require("devtools/shared/event-emitter");
 
-const TOOL_URL = "chrome://devtools/content/responsive.html/index.xhtml";
-
 loader.lazyRequireGetter(this, "DebuggerClient", "devtools/shared/client/debugger-client", true);
 loader.lazyRequireGetter(this, "DebuggerServer", "devtools/server/main", true);
 loader.lazyRequireGetter(this, "throttlingProfiles", "devtools/client/shared/components/throttling/profiles");
 loader.lazyRequireGetter(this, "SettingOnboardingTooltip", "devtools/client/responsive.html/setting-onboarding-tooltip");
 loader.lazyRequireGetter(this, "swapToInnerBrowser", "devtools/client/responsive.html/browser/swap", true);
 loader.lazyRequireGetter(this, "startup", "devtools/client/responsive.html/utils/window", true);
 loader.lazyRequireGetter(this, "message", "devtools/client/responsive.html/utils/message");
 loader.lazyRequireGetter(this, "showNotification", "devtools/client/responsive.html/utils/notification", true);
 loader.lazyRequireGetter(this, "l10n", "devtools/client/responsive.html/utils/l10n");
 loader.lazyRequireGetter(this, "EmulationFront", "devtools/shared/fronts/emulation", true);
 loader.lazyRequireGetter(this, "PriorityLevels", "devtools/client/shared/components/NotificationBox", true);
 loader.lazyRequireGetter(this, "TargetFactory", "devtools/client/framework/target", true);
 loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
 loader.lazyRequireGetter(this, "Telemetry", "devtools/client/shared/telemetry");
 
+const TOOL_URL = "chrome://devtools/content/responsive.html/index.xhtml";
+
 const RELOAD_CONDITION_PREF_PREFIX = "devtools.responsive.reloadConditions.";
 const RELOAD_NOTIFICATION_PREF = "devtools.responsive.reloadNotification.enabled";
 const SHOW_SETTING_TOOLTIP_PREF = "devtools.responsive.show-setting-tooltip";
 
 function debug(msg) {
   // console.log(`RDM manager: ${msg}`);
 }
 
@@ -356,16 +357,19 @@ ResponsiveUI.prototype = {
     // Notify the inner browser to start the frame script
     debug("Wait until start frame script");
     await message.request(this.toolWindow, "start-frame-script");
 
     // Get the protocol ready to speak with emulation actor
     debug("Wait until RDP server connect");
     await this.connectToServer();
 
+    // Restore the previous state of RDM.
+    await this.restoreState();
+
     // Show the settings onboarding tooltip
     if (Services.prefs.getBoolPref(SHOW_SETTING_TOOLTIP_PREF)) {
       this.settingOnboardingTooltip =
         new SettingOnboardingTooltip(ui.toolWindow.document);
     }
 
     // Non-blocking message to tool UI to start any delayed init activities
     message.post(this.toolWindow, "post-init");
@@ -530,17 +534,17 @@ ResponsiveUI.prototype = {
         break;
       case "content-resize":
         this.onContentResize(event);
         break;
       case "exit":
         this.onExit();
         break;
       case "remove-device-association":
-        this.onRemoveDeviceAssociation(event);
+        this.onRemoveDeviceAssociation();
         break;
       case "viewport-resize":
         this.onViewportResize(event);
         break;
     }
   },
 
   async onChangeDevice(event) {
@@ -599,17 +603,17 @@ ResponsiveUI.prototype = {
     });
   },
 
   onExit() {
     const { browserWindow, tab } = this;
     ResponsiveUIManager.closeIfNeeded(browserWindow, tab);
   },
 
-  async onRemoveDeviceAssociation(event) {
+  async onRemoveDeviceAssociation() {
     let reloadNeeded = false;
     await this.updateDPPX();
     reloadNeeded |= await this.updateUserAgent() &&
                     this.reloadOnChange("userAgent");
     reloadNeeded |= await this.updateTouchSimulation() &&
                     this.reloadOnChange("touchSimulation");
     if (reloadNeeded) {
       this.getViewportBrowser().reload();
@@ -622,16 +626,50 @@ ResponsiveUI.prototype = {
     const { width, height } = event.data;
     this.emit("viewport-resize", {
       width,
       height,
     });
   },
 
   /**
+   * Restores the previous state of RDM.
+   */
+  async restoreState() {
+    const deviceState = await asyncStorage.getItem("devtools.responsive.deviceState");
+    if (deviceState) {
+      // Return if there is a device state to restore, this will be done when the
+      // device list is loaded after the post-init.
+      return;
+    }
+
+    const pixelRatio =
+      Services.prefs.getIntPref("devtools.responsive.viewport.pixelRatio", 0);
+    const touchSimulationEnabled =
+      Services.prefs.getBoolPref("devtools.responsive.touchSimulation.enabled", false);
+    const userAgent = Services.prefs.getCharPref("devtools.responsive.userAgent", "");
+
+    let reloadNeeded = false;
+
+    await this.updateDPPX(pixelRatio);
+
+    if (touchSimulationEnabled) {
+      reloadNeeded |= await this.updateTouchSimulation(touchSimulationEnabled) &&
+                      this.reloadOnChange("touchSimulation");
+    }
+    if (userAgent) {
+      reloadNeeded |= await this.updateUserAgent(userAgent) &&
+                      this.reloadOnChange("userAgent");
+    }
+    if (reloadNeeded) {
+      this.getViewportBrowser().reload();
+    }
+  },
+
+  /**
    * Set or clear the emulated device pixel ratio.
    *
    * @return boolean
    *         Whether a reload is needed to apply the change.
    *         (This is always immediate, so it's always false.)
    */
   async updateDPPX(dppx) {
     if (!dppx) {
--- a/devtools/client/responsive.html/reducers/ui.js
+++ b/devtools/client/responsive.html/reducers/ui.js
@@ -15,44 +15,48 @@ const {
   TOGGLE_TOUCH_SIMULATION,
   TOGGLE_USER_AGENT_INPUT,
 } = require("../actions/index");
 
 const LEFT_ALIGNMENT_ENABLED = "devtools.responsive.leftAlignViewport.enabled";
 const RELOAD_ON_TOUCH_SIMULATION = "devtools.responsive.reloadConditions.touchSimulation";
 const RELOAD_ON_USER_AGENT = "devtools.responsive.reloadConditions.userAgent";
 const SHOW_USER_AGENT_INPUT = "devtools.responsive.showUserAgentInput";
+const TOUCH_SIMULATION_ENABLED = "devtools.responsive.touchSimulation.enabled";
+const USER_AGENT = "devtools.responsive.userAgent";
 
 const INITIAL_UI = {
   // The pixel ratio of the display.
   displayPixelRatio: 0,
   // Whether or not the viewports are left aligned.
-  leftAlignmentEnabled: Services.prefs.getBoolPref(LEFT_ALIGNMENT_ENABLED),
+  leftAlignmentEnabled: Services.prefs.getBoolPref(LEFT_ALIGNMENT_ENABLED, false),
   // Whether or not to reload when touch simulation is toggled.
-  reloadOnTouchSimulation: Services.prefs.getBoolPref(RELOAD_ON_TOUCH_SIMULATION),
+  reloadOnTouchSimulation: Services.prefs.getBoolPref(RELOAD_ON_TOUCH_SIMULATION, false),
   // Whether or not to reload when user agent is changed.
-  reloadOnUserAgent: Services.prefs.getBoolPref(RELOAD_ON_USER_AGENT),
+  reloadOnUserAgent: Services.prefs.getBoolPref(RELOAD_ON_USER_AGENT, false),
   // Whether or not to show the user agent input in the toolbar.
-  showUserAgentInput: Services.prefs.getBoolPref(SHOW_USER_AGENT_INPUT),
+  showUserAgentInput: Services.prefs.getBoolPref(SHOW_USER_AGENT_INPUT, false),
   // Whether or not touch simulation is enabled.
-  touchSimulationEnabled: false,
+  touchSimulationEnabled: Services.prefs.getBoolPref(TOUCH_SIMULATION_ENABLED, false),
   // The user agent of the viewport.
-  userAgent: "",
+  userAgent: Services.prefs.getCharPref(USER_AGENT, ""),
 };
 
 const reducers = {
 
   [CHANGE_DISPLAY_PIXEL_RATIO](ui, { displayPixelRatio }) {
     return {
       ...ui,
       displayPixelRatio,
     };
   },
 
   [CHANGE_USER_AGENT](ui, { userAgent }) {
+    Services.prefs.setCharPref(USER_AGENT, userAgent);
+
     return {
       ...ui,
       userAgent,
     };
   },
 
   [TOGGLE_LEFT_ALIGNMENT](ui, { enabled }) {
     const leftAlignmentEnabled = enabled !== undefined ?
@@ -86,16 +90,18 @@ const reducers = {
 
     return {
       ...ui,
       reloadOnUserAgent,
     };
   },
 
   [TOGGLE_TOUCH_SIMULATION](ui, { enabled }) {
+    Services.prefs.setBoolPref(TOUCH_SIMULATION_ENABLED, enabled);
+
     return {
       ...ui,
       touchSimulationEnabled: enabled,
     };
   },
 
   [TOGGLE_USER_AGENT_INPUT](ui, { enabled }) {
     const showUserAgentInput = enabled !== undefined ?
--- a/devtools/client/responsive.html/reducers/viewports.js
+++ b/devtools/client/responsive.html/reducers/viewports.js
@@ -1,33 +1,39 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
+const Services = require("Services");
+
 const {
   ADD_VIEWPORT,
   CHANGE_DEVICE,
   CHANGE_PIXEL_RATIO,
   REMOVE_DEVICE_ASSOCIATION,
   RESIZE_VIEWPORT,
   ROTATE_VIEWPORT,
 } = require("../actions/index");
 
+const VIEWPORT_WIDTH_PREF = "devtools.responsive.viewport.width";
+const VIEWPORT_HEIGHT_PREF = "devtools.responsive.viewport.height";
+const VIEWPORT_PIXEL_RATIO_PREF = "devtools.responsive.viewport.pixelRatio";
+
 let nextViewportId = 0;
 
 const INITIAL_VIEWPORTS = [];
 const INITIAL_VIEWPORT = {
   id: nextViewportId++,
   device: "",
   deviceType: "",
-  height: 480,
-  width: 320,
-  pixelRatio: 0,
+  height: Services.prefs.getIntPref(VIEWPORT_HEIGHT_PREF, 480),
+  width: Services.prefs.getIntPref(VIEWPORT_WIDTH_PREF, 320),
+  pixelRatio: Services.prefs.getIntPref(VIEWPORT_PIXEL_RATIO_PREF, 0),
   userContextId: 0,
 };
 
 const reducers = {
 
   [ADD_VIEWPORT](viewports, { userContextId }) {
     // For the moment, there can be at most one viewport.
     if (viewports.length === 1) {
@@ -58,16 +64,18 @@ const reducers = {
   },
 
   [CHANGE_PIXEL_RATIO](viewports, { id, pixelRatio }) {
     return viewports.map(viewport => {
       if (viewport.id !== id) {
         return viewport;
       }
 
+      Services.prefs.setIntPref(VIEWPORT_PIXEL_RATIO_PREF, pixelRatio);
+
       return {
         ...viewport,
         pixelRatio,
       };
     });
   },
 
   [REMOVE_DEVICE_ASSOCIATION](viewports, { id }) {
@@ -93,16 +101,19 @@ const reducers = {
       if (!height) {
         height = viewport.height;
       }
 
       if (!width) {
         width = viewport.width;
       }
 
+      Services.prefs.setIntPref(VIEWPORT_WIDTH_PREF, width);
+      Services.prefs.setIntPref(VIEWPORT_HEIGHT_PREF, height);
+
       return {
         ...viewport,
         height,
         width,
       };
     });
   },
 
--- a/devtools/client/responsive.html/test/browser/browser.ini
+++ b/devtools/client/responsive.html/test/browser/browser.ini
@@ -27,16 +27,17 @@ support-files =
 [browser_contextual_identity.js]
 [browser_device_change.js]
 [browser_device_custom_remove.js]
 [browser_device_custom.js]
 [browser_device_modal_error.js]
 [browser_device_modal_exit.js]
 [browser_device_modal_submit.js]
 [browser_device_pixel_ratio_change.js]
+[browser_device_state_restore.js]
 [browser_device_width.js]
 [browser_exit_button.js]
 [browser_ext_messaging.js]
 tags = devtools webextensions
 [browser_favicon.js]
 [browser_frame_script_active.js]
 [browser_hide_container.js]
 [browser_menu_item_01.js]
@@ -46,16 +47,17 @@ tags = devtools webextensions
 skip-if = true # Bug 1413765
 [browser_network_throttling.js]
 [browser_page_state.js]
 [browser_permission_doorhanger.js]
 tags = devtools geolocation
 skip-if = true # Bug 1413765
 [browser_preloaded_newtab.js]
 [browser_screenshot_button.js]
+[browser_state_restore.js]
 [browser_tab_close.js]
 [browser_tab_remoteness_change.js]
 [browser_target_blank.js]
 [browser_telemetry_activate_rdm.js]
 [browser_toolbox_computed_view.js]
 [browser_toolbox_rule_view.js]
 [browser_toolbox_rule_view_reload.js]
 [browser_toolbox_swap_browsers.js]
--- a/devtools/client/responsive.html/test/browser/browser_device_change.js
+++ b/devtools/client/responsive.html/test/browser/browser_device_change.js
@@ -79,18 +79,20 @@ add_task(async function() {
   const tab = await addTab(TEST_URL);
   const { ui } = await openRDM(tab);
 
   const { store } = ui.toolWindow;
 
   reloadOnUAChange(true);
 
   // Wait until the viewport has been added and the device list has been loaded
-  await waitUntilState(store, state => state.viewports.length == 1
-    && state.devices.listState == Types.loadableState.LOADED);
+  await waitUntilState(store, state =>
+    state.viewports.length == 1 &&
+    state.viewports[0].device === "Laptop (1366 x 768)" &&
+    state.devices.listState == Types.loadableState.LOADED);
 
   // Select device with custom UA
   let reloaded = waitForViewportLoad(ui);
   await selectDevice(ui, "Fake Phone RDM Test");
   await reloaded;
   await waitForViewportResizeTo(ui, testDevice.width, testDevice.height);
   info("Should have device UA now that device is applied");
   await testUserAgent(ui, testDevice.userAgent);
@@ -103,22 +105,8 @@ add_task(async function() {
   // Ensure UA is reset to default after closing RDM
   info("Should have default UA after closing RDM");
   await testUserAgentFromBrowser(tab.linkedBrowser, DEFAULT_UA);
 
   await removeTab(tab);
 
   reloadOnUAChange(false);
 });
-
-function testViewportDimensions(ui, w, h) {
-  const viewport = ui.toolWindow.document.querySelector(".viewport-content");
-
-  is(ui.toolWindow.getComputedStyle(viewport).getPropertyValue("width"),
-     `${w}px`, `Viewport should have width of ${w}px`);
-  is(ui.toolWindow.getComputedStyle(viewport).getPropertyValue("height"),
-     `${h}px`, `Viewport should have height of ${h}px`);
-}
-
-async function testDevicePixelRatio(ui, expected) {
-  const dppx = await getViewportDevicePixelRatio(ui);
-  is(dppx, expected, `devicePixelRatio should be set to ${expected}`);
-}
new file mode 100644
--- /dev/null
+++ b/devtools/client/responsive.html/test/browser/browser_device_state_restore.js
@@ -0,0 +1,81 @@
+/* Any copyright is dedicated to the Public Domain.
+http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that the previous selected device is restored when reopening RDM.
+
+const TEST_URL = "data:text/html;charset=utf-8,";
+const DEFAULT_DPPX = window.devicePixelRatio;
+
+/* eslint-disable max-len */
+const TEST_DEVICE = {
+  "name": "Apple iPhone 6s",
+  "width": 375,
+  "height": 667,
+  "pixelRatio": 2,
+  "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/600.1.3 (KHTML, like Gecko) Version/8.0 Mobile/12A4345d Safari/600.1.4",
+  "touch": true,
+  "firefoxOS": false,
+  "os": "ios",
+  "featured": true
+};
+/* eslint-enable max-len */
+
+const Types = require("devtools/client/responsive.html/types");
+
+addRDMTask(TEST_URL, async function({ ui }) {
+  const { store } = ui.toolWindow;
+
+  reloadOnUAChange(true);
+
+  // Wait until the viewport has been added and the device list has been loaded
+  await waitUntilState(store, state => state.viewports.length == 1
+    && state.devices.listState == Types.loadableState.LOADED);
+
+  info("Checking the default RDM state.");
+  testViewportDeviceMenuLabel(ui, "Responsive");
+  testViewportDimensions(ui, 320, 480);
+  await testUserAgent(ui, DEFAULT_UA);
+  await testDevicePixelRatio(ui, DEFAULT_DPPX);
+  await testTouchEventsOverride(ui, false);
+
+  info("Select a device");
+  const reloaded = waitForViewportLoad(ui);
+  await selectDevice(ui, TEST_DEVICE.name);
+  await reloaded;
+  await waitForViewportResizeTo(ui, TEST_DEVICE.width, TEST_DEVICE.height);
+
+  info("Checking the RDM device state.");
+  testViewportDeviceMenuLabel(ui, TEST_DEVICE.name);
+  await testUserAgent(ui, TEST_DEVICE.userAgent);
+  await testDevicePixelRatio(ui, TEST_DEVICE.pixelRatio);
+  await testTouchEventsOverride(ui, TEST_DEVICE.touch);
+
+  reloadOnUAChange(false);
+});
+
+addRDMTask(TEST_URL, async function({ ui }) {
+  const { store } = ui.toolWindow;
+
+  reloadOnUAChange(true);
+
+  info("Reopening RDM and checking that the previous device state is restored.");
+
+  // Wait until the viewport has been added and the device list has been loaded
+  await waitUntilState(store, state =>
+    state.viewports.length == 1 &&
+    state.viewports[0].device === TEST_DEVICE.name &&
+    state.devices.listState == Types.loadableState.LOADED);
+  await waitForViewportResizeTo(ui, TEST_DEVICE.width, TEST_DEVICE.height);
+  await waitForViewportLoad(ui);
+
+  info("Checking the restored RDM state.");
+  testViewportDeviceMenuLabel(ui, TEST_DEVICE.name);
+  testViewportDimensions(ui, TEST_DEVICE.width, TEST_DEVICE.height);
+  await testUserAgent(ui, TEST_DEVICE.userAgent);
+  await testDevicePixelRatio(ui, TEST_DEVICE.pixelRatio);
+  await testTouchEventsOverride(ui, TEST_DEVICE.touch);
+
+  reloadOnUAChange(false);
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/responsive.html/test/browser/browser_state_restore.js
@@ -0,0 +1,56 @@
+/* Any copyright is dedicated to the Public Domain.
+http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that the previous viewport size, user agent, ddppx and touch simulation properties
+// are restored when reopening RDM.
+
+const TEST_URL = "data:text/html;charset=utf-8,";
+const DEFAULT_DPPX = window.devicePixelRatio;
+const NEW_DPPX = DEFAULT_DPPX + 1;
+const NEW_USER_AGENT = "Mozilla/5.0 (Mobile; rv:39.0) Gecko/39.0 Firefox/39.0";
+
+addRDMTask(TEST_URL, async function({ ui, manager }) {
+  const { store } = ui.toolWindow;
+
+  reloadOnTouchChange(true);
+  reloadOnUAChange(true);
+
+  await waitUntilState(store, state => state.viewports.length == 1);
+
+  info("Checking the default RDM state.");
+  testViewportDeviceMenuLabel(ui, "Responsive");
+  testViewportDimensions(ui, 320, 480);
+  await testUserAgent(ui, DEFAULT_UA);
+  await testDevicePixelRatio(ui, DEFAULT_DPPX);
+  await testTouchEventsOverride(ui, false);
+
+  info("Changing the RDM size, dppx, ua and toggle ON touch simulations.");
+  await setViewportSize(ui, manager, 90, 500);
+  await selectDevicePixelRatio(ui, NEW_DPPX);
+  await toggleTouchSimulation(ui);
+  await changeUserAgentInput(ui, NEW_USER_AGENT);
+
+  reloadOnTouchChange(false);
+  reloadOnUAChange(false);
+});
+
+addRDMTask(TEST_URL, async function({ ui }) {
+  const { store } = ui.toolWindow;
+
+  reloadOnTouchChange(true);
+  reloadOnUAChange(true);
+
+  info("Reopening RDM and checking that the previous device state is restored.");
+
+  await waitUntilState(store, state => state.viewports.length == 1);
+
+  testViewportDimensions(ui, 90, 500);
+  await testUserAgent(ui, NEW_USER_AGENT);
+  await testDevicePixelRatio(ui, NEW_DPPX);
+  await testTouchEventsOverride(ui, true);
+
+  reloadOnTouchChange(false);
+  reloadOnUAChange(false);
+});
--- a/devtools/client/responsive.html/test/browser/head.js
+++ b/devtools/client/responsive.html/test/browser/head.js
@@ -60,17 +60,23 @@ Services.prefs.setBoolPref("devtools.res
 registerCleanupFunction(async () => {
   Services.prefs.clearUserPref("devtools.devices.url");
   Services.prefs.clearUserPref("devtools.responsive.reloadNotification.enabled");
   Services.prefs.clearUserPref("devtools.responsive.html.displayedDeviceList");
   Services.prefs.clearUserPref("devtools.responsive.reloadConditions.touchSimulation");
   Services.prefs.clearUserPref("devtools.responsive.reloadConditions.userAgent");
   Services.prefs.clearUserPref("devtools.responsive.show-setting-tooltip");
   Services.prefs.clearUserPref("devtools.responsive.showUserAgentInput");
+  Services.prefs.clearUserPref("devtools.responsive.touchSimulation.enabled");
+  Services.prefs.clearUserPref("devtools.responsive.userAgent");
+  Services.prefs.clearUserPref("devtools.responsive.viewport.height");
+  Services.prefs.clearUserPref("devtools.responsive.viewport.pixelRatio");
+  Services.prefs.clearUserPref("devtools.responsive.viewport.width");
   await asyncStorage.removeItem("devtools.devices.url_cache");
+  await asyncStorage.removeItem("devtools.responsive.deviceState");
   await removeLocalDevices();
 });
 
 /**
  * Open responsive design mode for the given tab.
  */
 var openRDM = async function(tab) {
   info("Opening responsive design mode");
@@ -372,16 +378,21 @@ function addDeviceForTest(device) {
 }
 
 async function waitForClientClose(ui) {
   info("Waiting for RDM debugger client to close");
   await ui.client.addOneTimeListener("closed");
   info("RDM's debugger client is now closed");
 }
 
+async function testDevicePixelRatio(ui, expected) {
+  const dppx = await getViewportDevicePixelRatio(ui);
+  is(dppx, expected, `devicePixelRatio should be set to ${expected}`);
+}
+
 async function testTouchEventsOverride(ui, expected) {
   const { document } = ui.toolWindow;
   const touchButton = document.getElementById("touch-simulation-button");
 
   const flag = await ui.emulationFront.getTouchEventsOverride();
   is(flag === Ci.nsIDocShell.TOUCHEVENTS_OVERRIDE_ENABLED, expected,
     `Touch events override should be ${expected ? "enabled" : "disabled"}`);
   is(touchButton.classList.contains("checked"), expected,
@@ -419,16 +430,25 @@ async function testUserAgent(ui, expecte
 
 async function testUserAgentFromBrowser(browser, expected) {
   const ua = await ContentTask.spawn(browser, {}, async function() {
     return content.navigator.userAgent;
   });
   is(ua, expected, `UA should be set to ${expected}`);
 }
 
+function testViewportDimensions(ui, w, h) {
+  const viewport = ui.toolWindow.document.querySelector(".viewport-content");
+
+  is(ui.toolWindow.getComputedStyle(viewport).getPropertyValue("width"),
+     `${w}px`, `Viewport should have width of ${w}px`);
+  is(ui.toolWindow.getComputedStyle(viewport).getPropertyValue("height"),
+     `${h}px`, `Viewport should have height of ${h}px`);
+}
+
 async function changeUserAgentInput(ui, value) {
   const { Simulate } =
     ui.toolWindow.require("devtools/client/shared/vendor/react-dom-test-utils");
   const { document, store } = ui.toolWindow;
 
   const userAgentInput = document.getElementById("user-agent-input");
   userAgentInput.value = value;
   Simulate.change(userAgentInput);
rename from devtools/client/debugger/test/mochitest/addon1.xpi
rename to devtools/client/shared/test/addon1.xpi
rename from devtools/client/debugger/test/mochitest/addon2.xpi
rename to devtools/client/shared/test/addon2.xpi
--- a/devtools/client/shared/test/browser.ini
+++ b/devtools/client/shared/test/browser.ini
@@ -1,31 +1,37 @@
 [DEFAULT]
 tags = devtools
 subsuite = devtools
 support-files =
+  addon1.xpi
+  addon2.xpi
   browser_devices.json
   doc_cubic-bezier-01.html
   doc_cubic-bezier-02.html
+  doc_empty-tab-01.html
+  doc_empty-tab-02.html
   doc_filter-editor-01.html
   doc_html_tooltip-02.xul
   doc_html_tooltip-03.xul
   doc_html_tooltip-04.xul
   doc_html_tooltip-05.xul
   doc_html_tooltip.xul
   doc_html_tooltip_arrow-01.xul
   doc_html_tooltip_arrow-02.xul
   doc_html_tooltip_doorhanger-01.xul
   doc_html_tooltip_doorhanger-02.xul
   doc_html_tooltip_hover.xul
   doc_html_tooltip_rtl.xul
   doc_inplace-editor_autocomplete_offset.xul
   doc_layoutHelpers-getBoxQuads.html
   doc_layoutHelpers.html
   doc_options-view.xul
+  doc_script-switching-01.html
+  doc_script-switching-02.html
   doc_spectrum.html
   doc_tableWidget_basic.html
   doc_tableWidget_keyboard_interaction.xul
   doc_tableWidget_mouse_interaction.xul
   doc_templater_basic.html
   dummy.html
   frame-script-utils.js
   head.js
@@ -33,28 +39,31 @@ support-files =
   helper_html_tooltip.js
   helper_inplace_editor.js
   leakhunt.js
   shared-head.js
   shared-redux-head.js
   telemetry-test-helpers.js
   test-actor-registry.js
   test-actor.js
+  testactors.js
   !/devtools/client/responsive.html/test/browser/devices.json
 
 [browser_autocomplete_popup.js]
 [browser_css_angle.js]
 [browser_css_color.js]
 [browser_cubic-bezier-01.js]
 [browser_cubic-bezier-02.js]
 [browser_cubic-bezier-03.js]
 [browser_cubic-bezier-04.js]
 [browser_cubic-bezier-05.js]
 [browser_cubic-bezier-06.js]
 [browser_cubic-bezier-07.js]
+[browser_dbg_globalactor.js]
+skip-if = e10s
 [browser_filter-editor-01.js]
 [browser_filter-editor-02.js]
 [browser_filter-editor-03.js]
 [browser_filter-editor-04.js]
 [browser_filter-editor-05.js]
 [browser_filter-editor-06.js]
 [browser_filter-editor-07.js]
 [browser_filter-editor-08.js]
@@ -199,8 +208,21 @@ skip-if = !e10s || os == "win" # RDM onl
 [browser_telemetry_toolboxtabs_webaudioeditor.js]
 [browser_telemetry_toolboxtabs_webconsole.js]
 [browser_treeWidget_basic.js]
 [browser_treeWidget_keyboard_interaction.js]
 [browser_treeWidget_mouse_interaction.js]
 [browser_devices.js]
 skip-if = verify
 [browser_theme_switching.js]
+[browser_dbg_listaddons.js]
+skip-if = e10s && debug
+tags = addons
+[browser_dbg_listtabs-01.js]
+[browser_dbg_listtabs-02.js]
+skip-if = true # Never worked for remote frames, needs a mock DebuggerServerConnection
+[browser_dbg_listtabs-03.js]
+skip-if = e10s && debug
+[browser_dbg_multiple-windows.js]
+[browser_dbg_navigation.js]
+skip-if = e10s && debug
+[browser_dbg_target-scoped-actor-01.js]
+[browser_dbg_target-scoped-actor-02.js]
\ No newline at end of file
rename from devtools/client/debugger/test/mochitest/browser_dbg_globalactor.js
rename to devtools/client/shared/test/browser_dbg_globalactor.js
--- a/devtools/client/debugger/test/mochitest/browser_dbg_globalactor.js
+++ b/devtools/client/shared/test/browser_dbg_globalactor.js
@@ -2,17 +2,23 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Check extension-added global actor API.
  */
 
-const ACTORS_URL = CHROME_URL + "testactors.js";
+"use strict";
+
+var { DebuggerServer } = require("devtools/server/main");
+var { ActorRegistry } = require("devtools/server/actors/utils/actor-registry");
+var { DebuggerClient } = require("devtools/shared/client/debugger-client");
+
+const ACTORS_URL = EXAMPLE_URL + "testactors.js";
 
 add_task(async function() {
   DebuggerServer.init();
   DebuggerServer.registerAllActors();
 
   ActorRegistry.registerModule(ACTORS_URL, {
     prefix: "testOne",
     constructor: "TestActor1",
@@ -38,17 +44,17 @@ add_task(async function() {
   response = await client.request({ to: globalActor, type: "ping" });
   is(response.pong, "pong", "Actor should respond to requests.");
 
   // Make sure that lazily-created actors are created only once.
   let count = 0;
   for (const connID of Object.getOwnPropertyNames(DebuggerServer._connections)) {
     const conn = DebuggerServer._connections[connID];
     const actorPrefix = conn._prefix + "testOne";
-    for (let pool of conn._extraPools) {
+    for (const pool of conn._extraPools) {
       for (const actor of pool.poolChildren()) {
         if (actor.actorID.startsWith(actorPrefix)) {
           count++;
         }
       }
     }
   }
 
rename from devtools/client/debugger/test/mochitest/browser_dbg_listaddons.js
rename to devtools/client/shared/test/browser_dbg_listaddons.js
--- a/devtools/client/debugger/test/mochitest/browser_dbg_listaddons.js
+++ b/devtools/client/shared/test/browser_dbg_listaddons.js
@@ -1,110 +1,163 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
+"use strict";
+
+var { DebuggerServer } = require("devtools/server/main");
+var { DebuggerClient } = require("devtools/shared/client/debugger-client");
+
+const chromeRegistry =
+  Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIChromeRegistry);
+const DEBUGGER_CHROME_URL = "chrome://mochitests/content/browser/devtools/client/shared/test/";
+const DEBUGGER_CHROME_URI = Services.io.newURI(DEBUGGER_CHROME_URL);
+
 /**
  * Make sure the listAddons request works as specified.
  */
 const ADDON1_ID = "jid1-oBAwBoE5rSecNg@jetpack";
 const ADDON1_PATH = "addon1.xpi";
 const ADDON2_ID = "jid1-qjtzNGV8xw5h2A@jetpack";
 const ADDON2_PATH = "addon2.xpi";
 
-var gAddon1, gAddon1Actor, gAddon2, gAddon2Actor, gClient;
+var gAddon1, gAddon1Actor, gAddon2, gClient;
 
 function test() {
   DebuggerServer.init();
   DebuggerServer.registerAllActors();
 
-  let transport = DebuggerServer.connectPipe();
+  const transport = DebuggerServer.connectPipe();
   gClient = new DebuggerClient(transport);
   gClient.connect().then(([aType, aTraits]) => {
     is(aType, "browser",
       "Root actor should identify itself as a browser.");
 
     promise.resolve(null)
       .then(testFirstAddon)
       .then(testSecondAddon)
       .then(testRemoveFirstAddon)
       .then(testRemoveSecondAddon)
       .then(() => gClient.close())
       .then(finish)
-      .catch(aError => {
-        ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
+      .catch(error => {
+        ok(false, "Got an error: " + error.message + "\n" + error.stack);
       });
   });
 }
 
 function testFirstAddon() {
   let addonListChanged = false;
   gClient.addOneTimeListener("addonListChanged", () => {
     addonListChanged = true;
   });
 
-  return addTemporaryAddon(ADDON1_PATH).then(aAddon => {
-    gAddon1 = aAddon;
+  return addTemporaryAddon(ADDON1_PATH).then(addon => {
+    gAddon1 = addon;
 
-    return getAddonActorForId(gClient, ADDON1_ID).then(aGrip => {
+    return getAddonActorForId(gClient, ADDON1_ID).then(grip => {
       ok(!addonListChanged, "Should not yet be notified that list of addons changed.");
-      ok(aGrip, "Should find an addon actor for addon1.");
-      gAddon1Actor = aGrip.actor;
+      ok(grip, "Should find an addon actor for addon1.");
+      gAddon1Actor = grip.actor;
     });
   });
 }
 
 function testSecondAddon() {
   let addonListChanged = false;
-  gClient.addOneTimeListener("addonListChanged", function () {
+  gClient.addOneTimeListener("addonListChanged", function() {
     addonListChanged = true;
   });
 
-  return addTemporaryAddon(ADDON2_PATH).then(aAddon => {
-    gAddon2 = aAddon;
+  return addTemporaryAddon(ADDON2_PATH).then(addon => {
+    gAddon2 = addon;
 
-    return getAddonActorForId(gClient, ADDON1_ID).then(aFirstGrip => {
-      return getAddonActorForId(gClient, ADDON2_ID).then(aSecondGrip => {
+    return getAddonActorForId(gClient, ADDON1_ID).then(fistGrip => {
+      return getAddonActorForId(gClient, ADDON2_ID).then(secondGrip => {
         ok(addonListChanged, "Should be notified that list of addons changed.");
-        is(aFirstGrip.actor, gAddon1Actor, "First addon's actor shouldn't have changed.");
-        ok(aSecondGrip, "Should find a addon actor for the second addon.");
-        gAddon2Actor = aSecondGrip.actor;
+        is(fistGrip.actor, gAddon1Actor, "First addon's actor shouldn't have changed.");
+        ok(secondGrip, "Should find a addon actor for the second addon.");
       });
     });
   });
 }
 
 function testRemoveFirstAddon() {
   let addonListChanged = false;
-  gClient.addOneTimeListener("addonListChanged", function () {
+  gClient.addOneTimeListener("addonListChanged", function() {
     addonListChanged = true;
   });
 
   return removeAddon(gAddon1).then(() => {
-    return getAddonActorForId(gClient, ADDON1_ID).then(aGrip => {
+    return getAddonActorForId(gClient, ADDON1_ID).then(grip => {
       ok(addonListChanged, "Should be notified that list of addons changed.");
-      ok(!aGrip, "Shouldn't find a addon actor for the first addon anymore.");
+      ok(!grip, "Shouldn't find a addon actor for the first addon anymore.");
     });
   });
 }
 
 function testRemoveSecondAddon() {
   let addonListChanged = false;
-  gClient.addOneTimeListener("addonListChanged", function () {
+  gClient.addOneTimeListener("addonListChanged", function() {
     addonListChanged = true;
   });
 
   return removeAddon(gAddon2).then(() => {
-    return getAddonActorForId(gClient, ADDON2_ID).then(aGrip => {
+    return getAddonActorForId(gClient, ADDON2_ID).then(grip => {
       ok(addonListChanged, "Should be notified that list of addons changed.");
-      ok(!aGrip, "Shouldn't find a addon actor for the second addon anymore.");
+      ok(!grip, "Shouldn't find a addon actor for the second addon anymore.");
     });
   });
 }
 
-registerCleanupFunction(function () {
+registerCleanupFunction(function() {
   gAddon1 = null;
   gAddon1Actor = null;
   gAddon2 = null;
-  gAddon2Actor = null;
   gClient = null;
 });
+
+function getAddonURIFromPath(path) {
+  const chromeURI = Services.io.newURI(path, null, DEBUGGER_CHROME_URI);
+  return chromeRegistry.convertChromeURL(chromeURI).QueryInterface(Ci.nsIFileURL);
+}
+
+function addTemporaryAddon(path) {
+  const addonFile = getAddonURIFromPath(path).file;
+  info("Installing addon: " + addonFile.path);
+
+  return AddonManager.installTemporaryAddon(addonFile);
+}
+
+function getAddonActorForId(client, addonId) {
+  info("Get addon actor for ID: " + addonId);
+  const deferred = promise.defer();
+
+  client.listAddons(response => {
+    const addonTargetActor = response.addons.filter(grip => grip.id == addonId).pop();
+    info("got addon actor for ID: " + addonId);
+    deferred.resolve(addonTargetActor);
+  });
+
+  return deferred.promise;
+}
+
+function removeAddon(addon) {
+  info("Removing addon.");
+
+  const deferred = promise.defer();
+
+  const listener = {
+    onUninstalled: function(uninstalledAddon) {
+      if (uninstalledAddon != addon) {
+        return;
+      }
+      AddonManager.removeAddonListener(listener);
+      deferred.resolve();
+    }
+  };
+  AddonManager.addAddonListener(listener);
+  addon.uninstall();
+
+  return deferred.promise;
+}
rename from devtools/client/debugger/test/mochitest/browser_dbg_listtabs-01.js
rename to devtools/client/shared/test/browser_dbg_listtabs-01.js
--- a/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-01.js
+++ b/devtools/client/shared/test/browser_dbg_listtabs-01.js
@@ -1,96 +1,112 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
+"use strict";
+
 /**
  * Make sure the listTabs request works as specified.
  */
 
+var { DebuggerServer } = require("devtools/server/main");
+var { DebuggerClient } = require("devtools/shared/client/debugger-client");
+
 const TAB1_URL = EXAMPLE_URL + "doc_empty-tab-01.html";
 const TAB2_URL = EXAMPLE_URL + "doc_empty-tab-02.html";
 
 var gTab1, gTab1Actor, gTab2, gTab2Actor, gClient;
 
 function test() {
   DebuggerServer.init();
   DebuggerServer.registerAllActors();
 
-  let transport = DebuggerServer.connectPipe();
+  const transport = DebuggerServer.connectPipe();
   gClient = new DebuggerClient(transport);
   gClient.connect().then(([aType, aTraits]) => {
     is(aType, "browser",
       "Root actor should identify itself as a browser.");
 
     promise.resolve(null)
       .then(testFirstTab)
       .then(testSecondTab)
       .then(testRemoveTab)
       .then(testAttachRemovedTab)
       .then(() => gClient.close())
       .then(finish)
-      .catch(aError => {
-        ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
+      .catch(error => {
+        ok(false, "Got an error: " + error.message + "\n" + error.stack);
       });
   });
 }
 
 function testFirstTab() {
-  return addTab(TAB1_URL).then(aTab => {
-    gTab1 = aTab;
+  return addTab(TAB1_URL).then(tab => {
+    gTab1 = tab;
 
-    return getTargetActorForUrl(gClient, TAB1_URL).then(aGrip => {
-      ok(aGrip, "Should find a target actor for the first tab.");
-      gTab1Actor = aGrip.actor;
+    return getTargetActorForUrl(gClient, TAB1_URL).then(grip => {
+      ok(grip, "Should find a target actor for the first tab.");
+      gTab1Actor = grip.actor;
     });
   });
 }
 
 function testSecondTab() {
-  return addTab(TAB2_URL).then(aTab => {
-    gTab2 = aTab;
+  return addTab(TAB2_URL).then(tab => {
+    gTab2 = tab;
 
-    return getTargetActorForUrl(gClient, TAB1_URL).then(aFirstGrip => {
-      return getTargetActorForUrl(gClient, TAB2_URL).then(aSecondGrip => {
-        is(aFirstGrip.actor, gTab1Actor, "First tab's actor shouldn't have changed.");
-        ok(aSecondGrip, "Should find a target actor for the second tab.");
-        gTab2Actor = aSecondGrip.actor;
+    return getTargetActorForUrl(gClient, TAB1_URL).then(firstGrip => {
+      return getTargetActorForUrl(gClient, TAB2_URL).then(secondGrip => {
+        is(firstGrip.actor, gTab1Actor, "First tab's actor shouldn't have changed.");
+        ok(secondGrip, "Should find a target actor for the second tab.");
+        gTab2Actor = secondGrip.actor;
       });
     });
   });
 }
 
 function testRemoveTab() {
   return removeTab(gTab1).then(() => {
-    return getTargetActorForUrl(gClient, TAB1_URL).then(aGrip => {
-      ok(!aGrip, "Shouldn't find a target actor for the first tab anymore.");
+    return getTargetActorForUrl(gClient, TAB1_URL).then(grip => {
+      ok(!grip, "Shouldn't find a target actor for the first tab anymore.");
     });
   });
 }
 
 function testAttachRemovedTab() {
   return removeTab(gTab2).then(() => {
-    let deferred = promise.defer();
+    const deferred = promise.defer();
 
-    gClient.addListener("paused", (aEvent, aPacket) => {
+    gClient.addListener("paused", () => {
       ok(false, "Attaching to an exited target actor shouldn't generate a pause.");
       deferred.reject();
     });
 
-    gClient.request({ to: gTab2Actor, type: "attach" }, aResponse => {
-      is(aResponse.error, "connectionClosed",
+    gClient.request({ to: gTab2Actor, type: "attach" }, response => {
+      is(response.error, "connectionClosed",
          "Connection is gone since the tab was removed.");
       deferred.resolve();
     });
 
     return deferred.promise;
   });
 }
 
-registerCleanupFunction(function () {
+registerCleanupFunction(function() {
   gTab1 = null;
   gTab1Actor = null;
   gTab2 = null;
   gTab2Actor = null;
   gClient = null;
 });
+
+function getTargetActorForUrl(client, url) {
+  const deferred = promise.defer();
+
+  client.listTabs().then(response => {
+    const targetActor = response.tabs.filter(grip => grip.url == url).pop();
+    deferred.resolve(targetActor);
+  });
+
+  return deferred.promise;
+}
rename from devtools/client/debugger/test/mochitest/browser_dbg_listtabs-02.js
rename to devtools/client/shared/test/browser_dbg_listtabs-02.js
--- a/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-02.js
+++ b/devtools/client/shared/test/browser_dbg_listtabs-02.js
@@ -3,16 +3,17 @@
 
 "use strict";
 
 /**
  * Make sure the root actor's live tab list implementation works as specified.
  */
 
 var { BrowserTabList } = require("devtools/server/actors/webbrowser");
+var { DebuggerServer } = require("devtools/server/main");
 
 var gTestPage = "data:text/html;charset=utf-8," + encodeURIComponent(
   "<title>JS Debugger BrowserTabList test page</title><body>Yo.</body>");
 
 // The tablist object whose behavior we observe.
 var gTabList;
 var gFirstActor, gActorA;
 var gTabA, gTabB, gTabC;
@@ -49,62 +50,70 @@ function test() {
     .then(checkSingleTab)
     .then(finishUp);
 }
 
 function checkSingleTab() {
   return gTabList.getList().then(targetActors => {
     is(targetActors.length, 1, "initial tab list: contains initial tab");
     gFirstActor = targetActors[0];
-    is(gFirstActor.url, "about:blank", "initial tab list: initial tab URL is 'about:blank'");
+    is(
+      gFirstActor.url,
+      "about:blank",
+      "initial tab list: initial tab URL is 'about:blank'"
+    );
     is(gFirstActor.title, "New Tab", "initial tab list: initial tab title is 'New Tab'");
   });
 }
 
 function addTabA() {
-  return addTab(gTestPage).then(aTab => {
-    gTabA = aTab;
+  return addTab(gTestPage).then(tab => {
+    gTabA = tab;
   });
 }
 
 function testTabA() {
   is(onListChangedCount, 1, "onListChanged handler call count");
 
   return gTabList.getList().then(targetActors => {
     targetActors = new Set(targetActors);
     is(targetActors.size, 2, "gTabA opened: two tabs in list");
     ok(targetActors.has(gFirstActor), "gTabA opened: initial tab present");
 
     info("actors: " + [...targetActors].map(a => a.url));
     gActorA = [...targetActors].filter(a => a !== gFirstActor)[0];
     ok(gActorA.url.match(/^data:text\/html;/), "gTabA opened: new tab URL");
-    is(gActorA.title, "JS Debugger BrowserTabList test page", "gTabA opened: new tab title");
+    is(
+      gActorA.title,
+      "JS Debugger BrowserTabList test page",
+      "gTabA opened: new tab title"
+    );
   });
 }
 
 function addTabB() {
-  return addTab(gTestPage).then(aTab => {
-    gTabB = aTab;
+  return addTab(gTestPage).then(tab => {
+    gTabB = tab;
   });
 }
 
 function testTabB() {
   is(onListChangedCount, 2, "onListChanged handler call count");
 
   return gTabList.getList().then(targetActors => {
     targetActors = new Set(targetActors);
     is(targetActors.size, 3, "gTabB opened: three tabs in list");
   });
 }
 
 function removeTabA() {
-  let deferred = promise.defer();
+  const deferred = promise.defer();
 
-  once(gBrowser.tabContainer, "TabClose").then(aEvent => {
-    ok(!aEvent.detail.adoptedBy, "This was a normal tab close");
+  once(gBrowser.tabContainer, "TabClose").then(event => {
+    ok(!event.detail.adoptedBy, "This was a normal tab close");
 
     // Let the actor's TabClose handler finish first.
     executeSoon(deferred.resolve);
   }, false);
 
   removeTab(gTabA);
   return deferred.promise;
 }
@@ -115,40 +124,44 @@ function testTabClosed() {
   gTabList.getList().then(targetActors => {
     targetActors = new Set(targetActors);
     is(targetActors.size, 2, "gTabA closed: two tabs in list");
     ok(targetActors.has(gFirstActor), "gTabA closed: initial tab present");
 
     info("actors: " + [...targetActors].map(a => a.url));
     gActorA = [...targetActors].filter(a => a !== gFirstActor)[0];
     ok(gActorA.url.match(/^data:text\/html;/), "gTabA closed: new tab URL");
-    is(gActorA.title, "JS Debugger BrowserTabList test page", "gTabA closed: new tab title");
+    is(
+      gActorA.title,
+      "JS Debugger BrowserTabList test page",
+      "gTabA closed: new tab title"
+    );
   });
 }
 
 function addTabC() {
-  return addTab(gTestPage).then(aTab => {
-    gTabC = aTab;
+  return addTab(gTestPage).then(tab => {
+    gTabC = tab;
   });
 }
 
 function testTabC() {
   is(onListChangedCount, 4, "onListChanged handler call count");
 
   gTabList.getList().then(targetActors => {
     targetActors = new Set(targetActors);
     is(targetActors.size, 3, "gTabC opened: three tabs in list");
   });
 }
 
 function removeTabC() {
-  let deferred = promise.defer();
+  const deferred = promise.defer();
 
-  once(gBrowser.tabContainer, "TabClose").then(aEvent => {
-    ok(aEvent.detail.adoptedBy, "This was a tab closed by moving");
+  once(gBrowser.tabContainer, "TabClose").then(event => {
+    ok(event.detail.adoptedBy, "This was a tab closed by moving");
 
     // Let the actor's TabClose handler finish first.
     executeSoon(deferred.resolve);
   }, false);
 
   gNewWindow = gBrowser.replaceTabWithWindow(gTabC);
   return deferred.promise;
 }
@@ -159,25 +172,29 @@ function testNewWindow() {
   return gTabList.getList().then(targetActors => {
     targetActors = new Set(targetActors);
     is(targetActors.size, 3, "gTabC closed: three tabs in list");
     ok(targetActors.has(gFirstActor), "gTabC closed: initial tab present");
 
     info("actors: " + [...targetActors].map(a => a.url));
     gActorA = [...targetActors].filter(a => a !== gFirstActor)[0];
     ok(gActorA.url.match(/^data:text\/html;/), "gTabC closed: new tab URL");
-    is(gActorA.title, "JS Debugger BrowserTabList test page", "gTabC closed: new tab title");
+    is(
+      gActorA.title,
+      "JS Debugger BrowserTabList test page",
+      "gTabC closed: new tab title"
+    );
   });
 }
 
 function removeNewWindow() {
-  let deferred = promise.defer();
+  const deferred = promise.defer();
 
-  once(gNewWindow, "unload").then(aEvent => {
-    ok(!aEvent.detail, "This was a normal window close");
+  once(gNewWindow, "unload").then(event => {
+    ok(!event.detail, "This was a normal window close");
 
     // Let the actor's TabClose handler finish first.
     executeSoon(deferred.resolve);
   }, false);
 
   gNewWindow.close();
   return deferred.promise;
 }
@@ -188,25 +205,29 @@ function testWindowClosed() {
   return gTabList.getList().then(targetActors => {
     targetActors = new Set(targetActors);
     is(targetActors.size, 2, "gNewWindow closed: two tabs in list");
     ok(targetActors.has(gFirstActor), "gNewWindow closed: initial tab present");
 
     info("actors: " + [...targetActors].map(a => a.url));
     gActorA = [...targetActors].filter(a => a !== gFirstActor)[0];
     ok(gActorA.url.match(/^data:text\/html;/), "gNewWindow closed: new tab URL");
-    is(gActorA.title, "JS Debugger BrowserTabList test page", "gNewWindow closed: new tab title");
+    is(
+      gActorA.title,
+      "JS Debugger BrowserTabList test page",
+      "gNewWindow closed: new tab title"
+    );
   });
 }
 
 function removeTabB() {
-  let deferred = promise.defer();
+  const deferred = promise.defer();
 
-  once(gBrowser.tabContainer, "TabClose").then(aEvent => {
-    ok(!aEvent.detail.adoptedBy, "This was a normal tab close");
+  once(gBrowser.tabContainer, "TabClose").then(event => {
+    ok(!event.detail.adoptedBy, "This was a normal tab close");
 
     // Let the actor's TabClose handler finish first.
     executeSoon(deferred.resolve);
   }, false);
 
   removeTab(gTabB);
   return deferred.promise;
 }
rename from devtools/client/debugger/test/mochitest/browser_dbg_listtabs-03.js
rename to devtools/client/shared/test/browser_dbg_listtabs-03.js
--- a/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-03.js
+++ b/devtools/client/shared/test/browser_dbg_listtabs-03.js
@@ -1,59 +1,61 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
+"use strict";
+
 /**
  * Make sure the listTabs request works as specified.
  */
 
+var { DebuggerServer } = require("devtools/server/main");
+var { DebuggerClient } = require("devtools/shared/client/debugger-client");
+var { Task } = require("devtools/shared/task");
+
 const TAB1_URL = EXAMPLE_URL + "doc_empty-tab-01.html";
 
-var gTab1, gTab1Actor, gTab2, gTab2Actor, gClient;
+var gClient;
 
 function test() {
   DebuggerServer.init();
   DebuggerServer.registerAllActors();
 
-  let transport = DebuggerServer.connectPipe();
+  const transport = DebuggerServer.connectPipe();
   gClient = new DebuggerClient(transport);
   gClient.connect().then(Task.async(function* ([aType, aTraits]) {
     is(aType, "browser",
       "Root actor should identify itself as a browser.");
-    let tab = yield addTab(TAB1_URL);
+    const tab = yield addTab(TAB1_URL);
 
     let { tabs } = yield gClient.listTabs();
     is(tabs.length, 2, "Should be two tabs");
-    let tabGrip = tabs.filter(a => a.url == TAB1_URL).pop();
+    const tabGrip = tabs.filter(a => a.url == TAB1_URL).pop();
     ok(tabGrip, "Should have an actor for the tab");
 
     let response = yield gClient.request({ to: tabGrip.actor, type: "attach" });
     is(response.type, "tabAttached", "Should have attached");
 
     response = yield gClient.listTabs();
     tabs = response.tabs;
 
     response = yield gClient.request({ to: tabGrip.actor, type: "detach" });
     is(response.type, "detached", "Should have detached");
 
-    let newGrip = tabs.filter(a => a.url == TAB1_URL).pop();
+    const newGrip = tabs.filter(a => a.url == TAB1_URL).pop();
     is(newGrip.actor, tabGrip.actor, "Should have the same actor for the same tab");
 
     response = yield gClient.request({ to: tabGrip.actor, type: "attach" });
     is(response.type, "tabAttached", "Should have attached");
     response = yield gClient.request({ to: tabGrip.actor, type: "detach" });
     is(response.type, "detached", "Should have detached");
 
     yield removeTab(tab);
     yield gClient.close();
     finish();
   }));
 }
 
-registerCleanupFunction(function () {
-  gTab1 = null;
-  gTab1Actor = null;
-  gTab2 = null;
-  gTab2Actor = null;
+registerCleanupFunction(function() {
   gClient = null;
 });
rename from devtools/client/debugger/test/mochitest/browser_dbg_multiple-windows.js
rename to devtools/client/shared/test/browser_dbg_multiple-windows.js
--- a/devtools/client/debugger/test/mochitest/browser_dbg_multiple-windows.js
+++ b/devtools/client/shared/test/browser_dbg_multiple-windows.js
@@ -1,18 +1,23 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
+"use strict";
+
 /**
  * Make sure that the debugger attaches to the right tab when multiple windows
  * are open.
  */
 
+var { DebuggerServer } = require("devtools/server/main");
+var { DebuggerClient } = require("devtools/shared/client/debugger-client");
+
 const TAB1_URL = EXAMPLE_URL + "doc_script-switching-01.html";
 const TAB2_URL = EXAMPLE_URL + "doc_script-switching-02.html";
 
 add_task(async function() {
   DebuggerServer.init();
   DebuggerServer.registerAllActors();
 
   const transport = DebuggerServer.connectPipe();
@@ -82,21 +87,29 @@ async function testFocusFirst(client) {
 async function testRemoveTab(client, win, tab) {
   win.close();
 
   // give it time to close
   await new Promise(resolve => executeSoon(resolve));
   await continue_remove_tab(client, tab);
 }
 
-async function continue_remove_tab(client, tab)
-{
+async function continue_remove_tab(client, tab) {
   removeTab(tab);
 
   const response = await client.listTabs();
   // Verify that tabs are no longer included in listTabs.
   const foundTab1 = response.tabs.some(grip => grip.url == TAB1_URL);
   const foundTab2 = response.tabs.some(grip => grip.url == TAB2_URL);
   ok(!foundTab1, "Tab1 should be gone.");
   ok(!foundTab2, "Tab2 should be gone.");
 
   is(response.selected, 0, "The original tab is selected.");
 }
+
+function addWindow(url) {
+  info("Adding window: " + url);
+  return promise.resolve(getChromeWindow(window.open(url)));
+}
+
+function getChromeWindow(win) {
+  return win.docShell.rootTreeItem.domWindow;
+}
rename from devtools/client/debugger/test/mochitest/browser_dbg_navigation.js
rename to devtools/client/shared/test/browser_dbg_navigation.js
--- a/devtools/client/debugger/test/mochitest/browser_dbg_navigation.js
+++ b/devtools/client/shared/test/browser_dbg_navigation.js
@@ -1,73 +1,99 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
+"use strict";
+
 /**
  * Check tab attach/navigation.
  */
 
+var { DebuggerServer } = require("devtools/server/main");
+var { DebuggerClient } = require("devtools/shared/client/debugger-client");
+
 const TAB1_URL = EXAMPLE_URL + "doc_empty-tab-01.html";
-const TAB2_URL = EXAMPLE_URL + "doc_empty-tab-02.html";
+const TAB2_FILE = "doc_empty-tab-02.html";
+const TAB2_URL = EXAMPLE_URL + TAB2_FILE;
 
 var gClient;
 
 function test() {
   DebuggerServer.init();
   DebuggerServer.registerAllActors();
 
-  let transport = DebuggerServer.connectPipe();
+  const transport = DebuggerServer.connectPipe();
   gClient = new DebuggerClient(transport);
   gClient.connect().then(([aType, aTraits]) => {
     is(aType, "browser",
       "Root actor should identify itself as a browser.");
 
     addTab(TAB1_URL)
       .then(() => attachTargetActorForUrl(gClient, TAB1_URL))
       .then(testNavigate)
       .then(testDetach)
       .then(finish)
-      .catch(aError => {
-        ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
+      .catch(error => {
+        ok(false, "Got an error: " + error.message + "\n" + error.stack);
       });
   });
 }
 
 function testNavigate([aGrip, aResponse]) {
-  let outstanding = [promise.defer(), promise.defer()];
+  const outstanding = [promise.defer(), promise.defer()];
 
-  gClient.addListener("tabNavigated", function onTabNavigated(aEvent, aPacket) {
-    is(aPacket.url, TAB2_URL,
+  gClient.addListener("tabNavigated", function onTabNavigated(event, packet) {
+    is(packet.url.split("/").pop(), TAB2_FILE,
       "Got a tab navigation notification.");
 
-    if (aPacket.state == "start") {
+    info(JSON.stringify(packet));
+    info(JSON.stringify(event));
+
+    if (packet.state == "start") {
       ok(true, "Tab started to navigate.");
       outstanding[0].resolve();
     } else {
       ok(true, "Tab finished navigating.");
       gClient.removeListener("tabNavigated", onTabNavigated);
       outstanding[1].resolve();
     }
   });
 
   BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TAB2_URL);
   return promise.all(outstanding.map(e => e.promise))
                 .then(() => aGrip.actor);
 }
 
-function testDetach(aActor) {
-  let deferred = promise.defer();
+function testDetach(actor) {
+  const deferred = promise.defer();
 
-  gClient.addOneTimeListener("tabDetached", (aType, aPacket) => {
+  gClient.addOneTimeListener("tabDetached", (type, packet) => {
     ok(true, "Got a tab detach notification.");
-    is(aPacket.from, aActor, "tab detach message comes from the expected actor");
+    is(packet.from, actor, "tab detach message comes from the expected actor");
     deferred.resolve(gClient.close());
   });
 
   removeTab(gBrowser.selectedTab);
   return deferred.promise;
 }
 
-registerCleanupFunction(function () {
+registerCleanupFunction(function() {
   gClient = null;
 });
+
+async function attachTargetActorForUrl(client, url) {
+  const grip = await getTargetActorForUrl(client, url);
+  const [ response ] = await client.attachTarget(grip.actor);
+  return [grip, response];
+}
+
+function getTargetActorForUrl(client, url) {
+  const deferred = promise.defer();
+
+  client.listTabs().then(response => {
+    const targetActor = response.tabs.filter(grip => grip.url == url).pop();
+    deferred.resolve(targetActor);
+  });
+
+  return deferred.promise;
+}
rename from devtools/client/debugger/test/mochitest/browser_dbg_target-scoped-actor-01.js
rename to devtools/client/shared/test/browser_dbg_target-scoped-actor-01.js
--- a/devtools/client/debugger/test/mochitest/browser_dbg_target-scoped-actor-01.js
+++ b/devtools/client/shared/test/browser_dbg_target-scoped-actor-01.js
@@ -1,18 +1,23 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
+"use strict";
+
 /**
  * Check target-scoped actor lifetimes.
  */
 
-const ACTORS_URL = CHROME_URL + "testactors.js";
+var { DebuggerServer } = require("devtools/server/main");
+var { DebuggerClient } = require("devtools/shared/client/debugger-client");
+
+const ACTORS_URL = EXAMPLE_URL + "testactors.js";
 const TAB_URL = EXAMPLE_URL + "doc_empty-tab-01.html";
 
 add_task(async function test() {
   await addTab(TAB_URL);
 
   DebuggerServer.init();
   DebuggerServer.registerAllActors();
 
@@ -37,8 +42,25 @@ async function testTargetScopedActor(cli
   ok(grip.testOneActor,
     "Found the test target-scoped actor.");
   ok(grip.testOneActor.includes("testOne"),
     "testOneActor's actorPrefix should be used.");
 
   const response = await client.request({ to: grip.testOneActor, type: "ping" });
   is(response.pong, "pong", "Actor should respond to requests.");
 }
+
+async function attachTargetActorForUrl(client, url) {
+  const grip = await getTargetActorForUrl(client, url);
+  const [ response ] = await client.attachTarget(grip.actor);
+  return [grip, response];
+}
+
+function getTargetActorForUrl(client, url) {
+  const deferred = promise.defer();
+
+  client.listTabs().then(response => {
+    const targetActor = response.tabs.filter(grip => grip.url == url).pop();
+    deferred.resolve(targetActor);
+  });
+
+  return deferred.promise;
+}
rename from devtools/client/debugger/test/mochitest/browser_dbg_target-scoped-actor-02.js
rename to devtools/client/shared/test/browser_dbg_target-scoped-actor-02.js
--- a/devtools/client/debugger/test/mochitest/browser_dbg_target-scoped-actor-02.js
+++ b/devtools/client/shared/test/browser_dbg_target-scoped-actor-02.js
@@ -1,18 +1,23 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
+"use strict";
+
 /**
  * Check target-scoped actor lifetimes.
  */
 
-const ACTORS_URL = CHROME_URL + "testactors.js";
+var { DebuggerServer } = require("devtools/server/main");
+var { DebuggerClient } = require("devtools/shared/client/debugger-client");
+
+const ACTORS_URL = EXAMPLE_URL + "testactors.js";
 const TAB_URL = EXAMPLE_URL + "doc_empty-tab-01.html";
 
 add_task(async function() {
   await addTab(TAB_URL);
 
   DebuggerServer.init();
   DebuggerServer.registerAllActors();
 
@@ -48,8 +53,25 @@ async function testTargetScopedActor(cli
 async function closeTab(client, grip) {
   await removeTab(gBrowser.selectedTab);
   await Assert.rejects(
     client.request({ to: grip.testOneActor, type: "ping" }),
     err => err.message === `'ping' active request packet to '${grip.testOneActor}' ` +
                            `can't be sent as the connection just closed.`,
     "testOneActor went away.");
 }
+
+async function attachTargetActorForUrl(client, url) {
+  const grip = await getTargetActorForUrl(client, url);
+  const [ response ] = await client.attachTarget(grip.actor);
+  return [grip, response];
+}
+
+function getTargetActorForUrl(client, url) {
+  const deferred = promise.defer();
+
+  client.listTabs().then(response => {
+    const targetActor = response.tabs.filter(grip => grip.url == url).pop();
+    deferred.resolve(targetActor);
+  });
+
+  return deferred.promise;
+}
copy from devtools/client/debugger/test/mochitest/doc_empty-tab-01.html
copy to devtools/client/shared/test/doc_empty-tab-01.html
copy from devtools/client/debugger/test/mochitest/doc_empty-tab-02.html
copy to devtools/client/shared/test/doc_empty-tab-02.html
copy from devtools/client/debugger/test/mochitest/doc_script-switching-01.html
copy to devtools/client/shared/test/doc_script-switching-01.html
copy from devtools/client/debugger/test/mochitest/doc_script-switching-02.html
copy to devtools/client/shared/test/doc_script-switching-02.html
--- a/devtools/client/shared/test/head.js
+++ b/devtools/client/shared/test/head.js
@@ -11,16 +11,18 @@
 Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/devtools/client/shared/test/shared-head.js", this);
 
 const {DOMHelpers} = ChromeUtils.import("resource://devtools/client/shared/DOMHelpers.jsm", {});
 const {Hosts} = require("devtools/client/framework/toolbox-hosts");
 
 const TEST_URI_ROOT = "http://example.com/browser/devtools/client/shared/test/";
 const OPTIONS_VIEW_URL = TEST_URI_ROOT + "doc_options-view.xul";
 
+const EXAMPLE_URL = "chrome://mochitests/content/browser/devtools/client/shared/test/";
+
 function catchFail(func) {
   return function() {
     try {
       return func.apply(null, arguments);
     } catch (ex) {
       ok(false, ex);
       console.error(ex);
       finish();
copy from devtools/client/debugger/test/mochitest/testactors.js
copy to devtools/client/shared/test/testactors.js
--- a/devtools/client/debugger/test/mochitest/testactors.js
+++ b/devtools/client/shared/test/testactors.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-function TestActor1(aConnection, aTab) {
-  this.conn = aConnection;
-  this.tab = aTab;
+function TestActor1(connection, tab) {
+  this.conn = connection;
+  this.tab = tab;
 }
 
 TestActor1.prototype = {
   actorPrefix: "testOne",
 
   grip: function TA1_grip() {
     return { actor: this.actorID,
              test: "TestActor1" };
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -4449,20 +4449,17 @@ nsGlobalWindowOuter::MakeScriptDialogTit
 
   aOutTitle.Truncate();
 
   // Try to get a host from the running principal -- this will do the
   // right thing for javascript: and data: documents.
 
   nsCOMPtr<nsIURI> uri;
   nsresult rv = aSubjectPrincipal->GetURI(getter_AddRefs(uri));
-  // Note - The check for the current JSContext here isn't necessarily sensical.
-  // It's just designed to preserve existing behavior during a mass-conversion
-  // patch.
-  if (NS_SUCCEEDED(rv) && uri && nsContentUtils::GetCurrentJSContext()) {
+  if (NS_SUCCEEDED(rv) && uri) {
     // remove user:pass for privacy and spoof prevention
 
     nsCOMPtr<nsIURIFixup> fixup(do_GetService(NS_URIFIXUP_CONTRACTID));
     if (fixup) {
       nsCOMPtr<nsIURI> fixedURI;
       rv = fixup->CreateExposableURI(uri, getter_AddRefs(fixedURI));
       if (NS_SUCCEEDED(rv) && fixedURI) {
         nsAutoCString host;
@@ -5617,35 +5614,33 @@ already_AddRefed<nsPIDOMWindowOuter>
 nsGlobalWindowOuter::GetFramesOuter()
 {
   RefPtr<nsPIDOMWindowOuter> frames(this);
   FlushPendingNotifications(FlushType::ContentAndNotify);
   return frames.forget();
 }
 
 nsGlobalWindowInner*
-nsGlobalWindowOuter::CallerInnerWindow()
-{
-  JSContext *cx = nsContentUtils::GetCurrentJSContext();
-  NS_ENSURE_TRUE(cx, nullptr);
+nsGlobalWindowOuter::CallerInnerWindow(JSContext* aCx)
+{
   nsIGlobalObject* global = GetIncumbentGlobal();
   NS_ENSURE_TRUE(global, nullptr);
-  JS::Rooted<JSObject*> scope(cx, global->GetGlobalJSObject());
+  JS::Rooted<JSObject*> scope(aCx, global->GetGlobalJSObject());
   NS_ENSURE_TRUE(scope, nullptr);
 
   // When Jetpack runs content scripts inside a sandbox, it uses
   // sandboxPrototype to make them appear as though they're running in the
   // scope of the page. So when a content script invokes postMessage, it expects
   // the |source| of the received message to be the window set as the
   // sandboxPrototype. This used to work incidentally for unrelated reasons, but
   // now we need to do some special handling to support it.
   if (xpc::IsSandbox(scope)) {
-    JSAutoRealm ar(cx, scope);
-    JS::Rooted<JSObject*> scopeProto(cx);
-    bool ok = JS_GetPrototype(cx, scope, &scopeProto);
+    JSAutoRealm ar(aCx, scope);
+    JS::Rooted<JSObject*> scopeProto(aCx);
+    bool ok = JS_GetPrototype(aCx, scope, &scopeProto);
     NS_ENSURE_TRUE(ok, nullptr);
     if (scopeProto && xpc::IsSandboxPrototypeProxy(scopeProto) &&
         (scopeProto = js::CheckedUnwrap(scopeProto, /* stopAtWindowProxy = */ false)))
     {
       global = xpc::NativeGlobal(scopeProto);
       NS_ENSURE_TRUE(global, nullptr);
     }
   }
@@ -5667,17 +5662,17 @@ nsGlobalWindowOuter::PostMessageMozOuter
   // Window.postMessage is an intentional subversion of the same-origin policy.
   // As such, this code must be particularly careful in the information it
   // exposes to calling code.
   //
   // http://www.whatwg.org/specs/web-apps/current-work/multipage/section-crossDocumentMessages.html
   //
 
   // First, get the caller's window
-  RefPtr<nsGlobalWindowInner> callerInnerWin = CallerInnerWindow();
+  RefPtr<nsGlobalWindowInner> callerInnerWin = CallerInnerWindow(aCx);
   nsIPrincipal* callerPrin;
   if (callerInnerWin) {
     // Compute the caller's origin either from its principal or, in the case the
     // principal doesn't carry a URI (e.g. the system principal), the caller's
     // document.  We must get this now instead of when the event is created and
     // dispatched, because ultimately it is the identity of the calling window
     // *now* that determines who sent the message (and not an identity which might
     // have changed due to intervening navigations).
--- a/dom/base/nsGlobalWindowOuter.h
+++ b/dom/base/nsGlobalWindowOuter.h
@@ -802,17 +802,18 @@ protected:
   void DropOuterWindowDocs();
   void CleanUp();
   void ClearControllers();
   // Outer windows only.
   void FinalClose();
 
   inline void MaybeClearInnerWindow(nsGlobalWindowInner* aExpectedInner);
 
-  nsGlobalWindowInner *CallerInnerWindow();
+  // We need a JSContext to get prototypes inside CallerInnerWindow.
+  nsGlobalWindowInner* CallerInnerWindow(JSContext* aCx);
 
   // Get the parent, returns null if this is a toplevel window
   nsPIDOMWindowOuter* GetParentInternal();
 
 public:
   // popup tracking
   bool IsPopupSpamWindow();
 
--- a/dom/base/test/browser_force_process_selector.js
+++ b/dom/base/test/browser_force_process_selector.js
@@ -1,23 +1,20 @@
 "use strict";
 
 const CONTENT_CREATED = "ipc:content-created";
 
 // Make sure that BTU.withNewTab({ ..., forceNewProcess: true }) loads
 // new tabs in their own process.
 async function spawnNewAndTest(recur, pids) {
-  let processCreated = TestUtils.topicObserved(CONTENT_CREATED);
   await BrowserTestUtils.withNewTab({ gBrowser, url: "about:blank", forceNewProcess: true },
                                     async function(browser) {
-      // Make sure our new browser is in its own process. The processCreated
-      // promise should have already resolved by this point.
-      await processCreated;
+      // Make sure our new browser is in its own process.
       let newPid = browser.frameLoader.tabParent.osPid;
-      ok(!pids.has(newPid), "new tab is in its own process");
+      ok(!pids.has(newPid), "new tab is in its own process: " + recur);
       pids.add(newPid);
 
       if (recur) {
         await spawnNewAndTest(recur - 1, pids);
       } else {
         let observer = () => {
           ok(false, "shouldn't have created a new process");
         };
--- a/gfx/angle/checkout/include/EGL/eglext_angle.h
+++ b/gfx/angle/checkout/include/EGL/eglext_angle.h
@@ -181,11 +181,16 @@ EGLAPI EGLint EGLAPIENTRY eglProgramCach
 #define EGL_TEXTURE_INTERNAL_FORMAT_ANGLE 0x345D
 #endif /* EGL_ANGLE_iosurface_client_buffer */
 
 #ifndef EGL_ANGLE_create_context_extensions_enabled
 #define EGL_ANGLE_create_context_extensions_enabled 1
 #define EGL_EXTENSIONS_ENABLED_ANGLE 0x345F
 #endif /* EGL_ANGLE_create_context_extensions_enabled */
 
+#ifndef EGL_MOZ_create_context_provoking_vertex_dont_care
+#define EGL_MOZ_create_context_provoking_vertex_dont_care 1
+#define EGL_CONTEXT_PROVOKING_VERTEX_DONT_CARE_MOZ 0x6000
+#endif /* EGL_MOZ_create_context_provoking_vertex_dont_care */
+
 // clang-format on
 
 #endif  // INCLUDE_EGL_EGLEXT_ANGLE_
--- a/gfx/angle/checkout/out/gen/angle/id/commit.h
+++ b/gfx/angle/checkout/out/gen/angle/id/commit.h
@@ -1,3 +1,3 @@
-#define ANGLE_COMMIT_HASH "4fb8d7f978ad"
+#define ANGLE_COMMIT_HASH "598f2502e3a4"
 #define ANGLE_COMMIT_HASH_SIZE 12
-#define ANGLE_COMMIT_DATE "2018-09-28 14:28:35 -0700"
+#define ANGLE_COMMIT_DATE "2018-10-09 13:54:05 -0700"
--- a/gfx/angle/checkout/src/libANGLE/Caps.cpp
+++ b/gfx/angle/checkout/src/libANGLE/Caps.cpp
@@ -1313,16 +1313,17 @@ DisplayExtensions::DisplayExtensions()
       pixelFormatFloat(false),
       surfacelessContext(false),
       displayTextureShareGroup(false),
       createContextClientArrays(false),
       programCacheControl(false),
       robustResourceInitialization(false),
       iosurfaceClientBuffer(false),
       createContextExtensionsEnabled(false),
+      provokingVertexDontCare(false),
       presentationTime(false)
 {
 }
 
 std::vector<std::string> DisplayExtensions::getStrings() const
 {
     std::vector<std::string> extensionStrings;
 
@@ -1360,16 +1361,17 @@ std::vector<std::string> DisplayExtensio
     InsertExtensionString("EGL_EXT_pixel_format_float",                          pixelFormatFloat,                   &extensionStrings);
     InsertExtensionString("EGL_KHR_surfaceless_context",                         surfacelessContext,                 &extensionStrings);
     InsertExtensionString("EGL_ANGLE_display_texture_share_group",               displayTextureShareGroup,           &extensionStrings);
     InsertExtensionString("EGL_ANGLE_create_context_client_arrays",              createContextClientArrays,          &extensionStrings);
     InsertExtensionString("EGL_ANGLE_program_cache_control",                     programCacheControl,                &extensionStrings);
     InsertExtensionString("EGL_ANGLE_robust_resource_initialization",            robustResourceInitialization,       &extensionStrings);
     InsertExtensionString("EGL_ANGLE_iosurface_client_buffer",                   iosurfaceClientBuffer,              &extensionStrings);
     InsertExtensionString("EGL_ANGLE_create_context_extensions_enabled",         createContextExtensionsEnabled,     &extensionStrings);
+    InsertExtensionString("EGL_MOZ_create_context_provoking_vertex_dont_care",   provokingVertexDontCare,            &extensionStrings);
     InsertExtensionString("EGL_ANDROID_presentation_time",                       presentationTime,                   &extensionStrings);
     // TODO(jmadill): Enable this when complete.
     //InsertExtensionString("KHR_create_context_no_error",                       createContextNoError,               &extensionStrings);
     // clang-format on
 
     return extensionStrings;
 }
 
--- a/gfx/angle/checkout/src/libANGLE/Caps.h
+++ b/gfx/angle/checkout/src/libANGLE/Caps.h
@@ -771,17 +771,20 @@ struct DisplayExtensions
     bool robustResourceInitialization;
 
     // EGL_ANGLE_iosurface_client_buffer
     bool iosurfaceClientBuffer;
 
     // EGL_ANGLE_create_context_extensions_enabled
     bool createContextExtensionsEnabled;
 
-    // EGL_ANDROID_presentation_time
+    // EGL_MOZ_create_context_provoking_vertex_dont_care
+    bool provokingVertexDontCare;
+
+  // EGL_ANDROID_presentation_time
     bool presentationTime;
 };
 
 struct DeviceExtensions
 {
     DeviceExtensions();
 
     // Generate a vector of supported extension strings
--- a/gfx/angle/checkout/src/libANGLE/Context.cpp
+++ b/gfx/angle/checkout/src/libANGLE/Context.cpp
@@ -213,16 +213,21 @@ bool GetBindGeneratesResource(const egl:
     return (attribs.get(EGL_CONTEXT_BIND_GENERATES_RESOURCE_CHROMIUM, EGL_TRUE) == EGL_TRUE);
 }
 
 bool GetClientArraysEnabled(const egl::AttributeMap &attribs)
 {
     return (attribs.get(EGL_CONTEXT_CLIENT_ARRAYS_ENABLED_ANGLE, EGL_TRUE) == EGL_TRUE);
 }
 
+bool GetProvokingVertexDontCare(const egl::AttributeMap &attribs)
+{
+    return (attribs.get(EGL_CONTEXT_PROVOKING_VERTEX_DONT_CARE_MOZ, EGL_FALSE) == EGL_TRUE);
+}
+
 bool GetRobustResourceInit(const egl::AttributeMap &attribs)
 {
     return (attribs.get(EGL_ROBUST_RESOURCE_INITIALIZATION_ANGLE, EGL_FALSE) == EGL_TRUE);
 }
 
 std::string GetObjectLabelFromPointer(GLsizei length, const GLchar *label)
 {
     std::string labelName;
@@ -353,16 +358,17 @@ Context::Context(rx::EGLImplFactory *imp
       mResetStrategy(GetResetStrategy(attribs)),
       mRobustAccess(GetRobustAccess(attribs)),
       mSurfacelessSupported(displayExtensions.surfacelessContext),
       mExplicitContextAvailable(clientExtensions.explicitContext),
       mCurrentSurface(static_cast<egl::Surface *>(EGL_NO_SURFACE)),
       mCurrentDisplay(static_cast<egl::Display *>(EGL_NO_DISPLAY)),
       mWebGLContext(GetWebGLContext(attribs)),
       mExtensionsEnabled(GetExtensionsEnabled(attribs, mWebGLContext)),
+      mProvokingVertexDontCare(GetProvokingVertexDontCare(attribs)),
       mMemoryProgramCache(memoryProgramCache),
       mVertexArrayObserverBinding(this, kVertexArraySubjectIndex),
       mDrawFramebufferObserverBinding(this, kDrawFramebufferSubjectIndex),
       mReadFramebufferObserverBinding(this, kReadFramebufferSubjectIndex),
       mScratchBuffer(1000u),
       mZeroFilledBuffer(1000u),
       mThreadPool(nullptr)
 {
--- a/gfx/angle/checkout/src/libANGLE/Context.h
+++ b/gfx/angle/checkout/src/libANGLE/Context.h
@@ -1558,16 +1558,19 @@ class Context final : public egl::Labele
     std::shared_ptr<angle::WorkerThreadPool> getWorkerThreadPool() const { return mThreadPool; }
 
     const StateCache &getStateCache() const { return mStateCache; }
 
     void onSubjectStateChange(const Context *context,
                               angle::SubjectIndex index,
                               angle::SubjectMessage message) override;
 
+    // Do we care about the order of the provoking vertex?
+    bool provokingVertexDontCare() const { return mProvokingVertexDontCare; }
+
   private:
     void initialize();
 
     bool noopDraw(PrimitiveMode mode, GLsizei count);
     bool noopDrawInstanced(PrimitiveMode mode, GLsizei count, GLsizei instanceCount);
 
     Error prepareForDraw(PrimitiveMode mode);
     Error prepareForClear(GLbitfield mask);
@@ -1677,16 +1680,17 @@ class Context final : public egl::Labele
     GLenum mResetStrategy;
     const bool mRobustAccess;
     const bool mSurfacelessSupported;
     const bool mExplicitContextAvailable;
     egl::Surface *mCurrentSurface;
     egl::Display *mCurrentDisplay;
     const bool mWebGLContext;
     const bool mExtensionsEnabled;
+    const bool mProvokingVertexDontCare;
     MemoryProgramCache *mMemoryProgramCache;
 
     State::DirtyObjects mDrawDirtyObjects;
     State::DirtyObjects mPathOperationDirtyObjects;
 
     StateCache mStateCache;
 
     State::DirtyBits mTexImageDirtyBits;
--- a/gfx/angle/checkout/src/libANGLE/Display.cpp
+++ b/gfx/angle/checkout/src/libANGLE/Display.cpp
@@ -1087,16 +1087,19 @@ void Display::initDisplayExtensions()
     mDisplayExtensions.getAllProcAddresses = true;
 
     // Enable program cache control since it is not back-end dependent.
     mDisplayExtensions.programCacheControl = true;
 
     // Request extension is implemented in the ANGLE frontend
     mDisplayExtensions.createContextExtensionsEnabled = true;
 
+    // Enable provoking vertex order don't care since it is not back-end dependent.
+    mDisplayExtensions.provokingVertexDontCare = true;
+
     mDisplayExtensionString = GenerateExtensionsString(mDisplayExtensions);
 }
 
 bool Display::isValidNativeWindow(EGLNativeWindowType window) const
 {
     return mImplementation->isValidNativeWindow(window);
 }
 
--- a/gfx/angle/checkout/src/libANGLE/MemoryProgramCache.cpp
+++ b/gfx/angle/checkout/src/libANGLE/MemoryProgramCache.cpp
@@ -203,41 +203,41 @@ MemoryProgramCache::MemoryProgramCache(s
 {
 }
 
 MemoryProgramCache::~MemoryProgramCache()
 {
 }
 
 // static
-LinkResult MemoryProgramCache::Deserialize(const Context *context,
+std::unique_ptr<rx::LinkEvent> MemoryProgramCache::Deserialize(const Context *context,
                                            const Program *program,
                                            ProgramState *state,
                                            const uint8_t *binary,
                                            size_t length,
                                            InfoLog &infoLog)
 {
     BinaryInputStream stream(binary, length);
 
     unsigned char commitString[ANGLE_COMMIT_HASH_SIZE];
     stream.readBytes(commitString, ANGLE_COMMIT_HASH_SIZE);
     if (memcmp(commitString, ANGLE_COMMIT_HASH, sizeof(unsigned char) * ANGLE_COMMIT_HASH_SIZE) !=
         0)
     {
         infoLog << "Invalid program binary version.";
-        return false;
+        return std::make_unique<rx::LinkEventDone>(false);
     }
 
     int majorVersion = stream.readInt<int>();
     int minorVersion = stream.readInt<int>();
     if (majorVersion != context->getClientMajorVersion() ||
         minorVersion != context->getClientMinorVersion())
     {
         infoLog << "Cannot load program binaries across different ES context versions.";
-        return false;
+        return std::make_unique<rx::LinkEventDone>(false);
     }
 
     state->mComputeShaderLocalSize[0] = stream.readInt<int>();
     state->mComputeShaderLocalSize[1] = stream.readInt<int>();
     state->mComputeShaderLocalSize[2] = stream.readInt<int>();
 
     state->mGeometryShaderInputPrimitiveType  = stream.readEnum<PrimitiveMode>();
     state->mGeometryShaderOutputPrimitiveType = stream.readEnum<PrimitiveMode>();
@@ -339,17 +339,17 @@ LinkResult MemoryProgramCache::Deseriali
 
     unsigned int transformFeedbackVaryingCount = stream.readInt<unsigned int>();
 
     // Reject programs that use transform feedback varyings if the hardware cannot support them.
     if (transformFeedbackVaryingCount > 0 &&
         context->getWorkarounds().disableProgramCachingForTransformFeedback)
     {
         infoLog << "Current driver does not support transform feedback in binary programs.";
-        return false;
+        return std::make_unique<rx::LinkEventDone>(false);
     }
 
     ASSERT(state->mLinkedTransformFeedbackVaryings.empty());
     for (unsigned int transformFeedbackVaryingIndex = 0;
          transformFeedbackVaryingIndex < transformFeedbackVaryingCount;
          ++transformFeedbackVaryingIndex)
     {
         sh::Varying varying;
@@ -654,20 +654,19 @@ LinkResult MemoryProgramCache::getProgra
                                           ProgramHash *hashOut)
 {
     ComputeHash(context, program, hashOut);
     const angle::MemoryBuffer *binaryProgram = nullptr;
     LinkResult result(false);
     if (get(*hashOut, &binaryProgram))
     {
         InfoLog infoLog;
-        ANGLE_TRY_RESULT(Deserialize(context, program, state, binaryProgram->data(),
-                                     binaryProgram->size(), infoLog),
-                         result);
-        ANGLE_HISTOGRAM_BOOLEAN("GPU.ANGLE.ProgramCache.LoadBinarySuccess", result.getResult());
+        auto event = Deserialize(context, program, state, binaryProgram->data(),
+                                 binaryProgram->size(), infoLog);
+        result = event->wait();
         if (!result.getResult())
         {
             // Cache load failed, evict.
             if (mIssuedWarnings++ < kWarningLimit)
             {
                 WARN() << "Failed to load binary from cache: " << infoLog.str();
 
                 if (mIssuedWarnings == kWarningLimit)
--- a/gfx/angle/checkout/src/libANGLE/MemoryProgramCache.h
+++ b/gfx/angle/checkout/src/libANGLE/MemoryProgramCache.h
@@ -10,16 +10,17 @@
 #ifndef LIBANGLE_MEMORY_PROGRAM_CACHE_H_
 #define LIBANGLE_MEMORY_PROGRAM_CACHE_H_
 
 #include <array>
 
 #include "common/MemoryBuffer.h"
 #include "libANGLE/Error.h"
 #include "libANGLE/SizedMRUCache.h"
+#include "libANGLE/renderer/ProgramImpl.h"
 
 namespace gl
 {
 // 160-bit SHA-1 hash key.
 constexpr size_t kProgramHashLength = 20;
 using ProgramHash                   = std::array<uint8_t, kProgramHashLength>;
 }  // namespace gl
 
@@ -56,22 +57,22 @@ class MemoryProgramCache final : angle::
     ~MemoryProgramCache();
 
     // Writes a program's binary to the output memory buffer.
     static void Serialize(const Context *context,
                           const Program *program,
                           angle::MemoryBuffer *binaryOut);
 
     // Loads program state according to the specified binary blob.
-    static LinkResult Deserialize(const Context *context,
-                                  const Program *program,
-                                  ProgramState *state,
-                                  const uint8_t *binary,
-                                  size_t length,
-                                  InfoLog &infoLog);
+    static std::unique_ptr<rx::LinkEvent> Deserialize(const Context *context,
+                                                      const Program *program,
+                                                      ProgramState *state,
+                                                      const uint8_t *binary,
+                                                      size_t length,
+                                                      InfoLog &infoLog);
 
     static void ComputeHash(const Context *context, const Program *program, ProgramHash *hashOut);
 
     // Check if the cache contains a binary matching the specified program.
     bool get(const ProgramHash &programHash, const angle::MemoryBuffer **programOut);
 
     // For querying the contents of the cache.
     bool getAt(size_t index, ProgramHash *hashOut, const angle::MemoryBuffer **programOut);
--- a/gfx/angle/checkout/src/libANGLE/Program.cpp
+++ b/gfx/angle/checkout/src/libANGLE/Program.cpp
@@ -1285,17 +1285,17 @@ bool Program::isLinked() const
 
 void Program::resolveLinkImpl()
 {
     ASSERT(mLinkingState.get());
 
     mLinked           = mLinkingState->linkEvent->wait();
     mLinkResolved     = true;
     auto linkingState = std::move(mLinkingState);
-    if (!mLinked)
+    if (!mLinked || !linkingState->context)
     {
         return;
     }
 
     initInterfaceBlockBindings();
 
     // According to GLES 3.0/3.1 spec for LinkProgram and UseProgram,
     // Only successfully linked program can replace the executables.
@@ -1438,18 +1438,21 @@ Error Program::loadBinary(const Context 
     ASSERT(binaryFormat == GL_PROGRAM_BINARY_ANGLE);
     if (binaryFormat != GL_PROGRAM_BINARY_ANGLE)
     {
         mInfoLog << "Invalid program binary format.";
         return NoError();
     }
 
     const uint8_t *bytes = reinterpret_cast<const uint8_t *>(binary);
-    ANGLE_TRY_RESULT(
-        MemoryProgramCache::Deserialize(context, this, &mState, bytes, length, mInfoLog), mLinked);
+
+    mLinkingState.reset(new LinkingState());
+    mLinkingState->linkEvent =
+        MemoryProgramCache::Deserialize(context, this, &mState, bytes, length, mInfoLog);
+    mLinkResolved = false;
 
     // Currently we require the full shader text to compute the program hash.
     // TODO(jmadill): Store the binary in the internal program cache.
 
     for (size_t uniformBlockIndex = 0; uniformBlockIndex < mState.mUniformBlocks.size();
          ++uniformBlockIndex)
     {
         mDirtyBits.set(uniformBlockIndex);
--- a/gfx/angle/checkout/src/libANGLE/renderer/ProgramImpl.h
+++ b/gfx/angle/checkout/src/libANGLE/renderer/ProgramImpl.h
@@ -61,19 +61,19 @@ class LinkEventDone final : public LinkE
 
 class ProgramImpl : angle::NonCopyable
 {
   public:
     ProgramImpl(const gl::ProgramState &state) : mState(state) {}
     virtual ~ProgramImpl() {}
     virtual gl::Error destroy(const gl::Context *context) { return gl::NoError(); }
 
-    virtual gl::LinkResult load(const gl::Context *context,
-                                gl::InfoLog &infoLog,
-                                gl::BinaryInputStream *stream) = 0;
+    virtual std::unique_ptr<LinkEvent> load(const gl::Context *context,
+                                            gl::InfoLog &infoLog,
+                                            gl::BinaryInputStream *stream) = 0;
     virtual void save(const gl::Context *context, gl::BinaryOutputStream *stream) = 0;
     virtual void setBinaryRetrievableHint(bool retrievable) = 0;
     virtual void setSeparable(bool separable)               = 0;
 
     virtual std::unique_ptr<LinkEvent> link(const gl::Context *context,
                                             const gl::ProgramLinkedResources &resources,
                                             gl::InfoLog &infoLog)          = 0;
     virtual GLboolean validate(const gl::Caps &caps, gl::InfoLog *infoLog) = 0;
--- a/gfx/angle/checkout/src/libANGLE/renderer/d3d/ProgramD3D.cpp
+++ b/gfx/angle/checkout/src/libANGLE/renderer/d3d/ProgramD3D.cpp
@@ -578,25 +578,26 @@ bool ProgramD3D::usesPointSpriteEmulatio
     return mUsesPointSize && mRenderer->getMajorShaderModel() >= 4;
 }
 
 bool ProgramD3D::usesGeometryShaderForPointSpriteEmulation() const
 {
     return usesPointSpriteEmulation() && !usesInstancedPointSpriteEmulation();
 }
 
-bool ProgramD3D::usesGeometryShader(gl::PrimitiveMode drawMode) const
+bool ProgramD3D::usesGeometryShader(const gl::Context *context,
+                                    gl::PrimitiveMode drawMode) const
 {
     if (mHasANGLEMultiviewEnabled && !mRenderer->canSelectViewInVertexShader())
     {
         return true;
     }
     if (drawMode != gl::PrimitiveMode::Points)
     {
-        return mUsesFlatInterpolation;
+        return !context->provokingVertexDontCare() && mUsesFlatInterpolation;
     }
     return usesGeometryShaderForPointSpriteEmulation();
 }
 
 bool ProgramD3D::usesInstancedPointSpriteEmulation() const
 {
     return mRenderer->getWorkarounds().useInstancedPointSpriteEmulation;
 }
@@ -730,40 +731,284 @@ gl::RangeUI ProgramD3D::getUsedImageRang
             return readonly ? mUsedComputeReadonlyImageRange : mUsedComputeImageRange;
         // TODO(xinghua.cao@intel.com): add image range of vertex shader and pixel shader.
         default:
             UNREACHABLE();
             return {0, 0};
     }
 }
 
-gl::LinkResult ProgramD3D::load(const gl::Context *context,
-                                gl::InfoLog &infoLog,
-                                gl::BinaryInputStream *stream)
+class ProgramD3D::GetExecutableTask : public Closure
+{
+public:
+    GetExecutableTask(ProgramD3D *program, const gl::Context *context)
+        : mProgram(program),
+        mResult(angle::Result::Continue()),
+        mInfoLog(),
+        mExecutable(nullptr),
+        mContext(context)
+    {
+    }
+
+    virtual angle::Result run() = 0;
+
+    void operator()() override { mResult = run(); }
+
+    angle::Result getResult() const { return mResult; }
+    const gl::InfoLog &getInfoLog() const { return mInfoLog; }
+    ShaderExecutableD3D *getExecutable() { return mExecutable; }
+
+protected:
+    ProgramD3D * mProgram;
+    angle::Result mResult;
+    gl::InfoLog mInfoLog;
+    ShaderExecutableD3D *mExecutable;
+    const gl::Context *mContext;
+};
+
+class ProgramD3D::GetLoadExecutableTask : public ProgramD3D::GetExecutableTask
+{
+public:
+    GetLoadExecutableTask(ProgramD3D *program, const gl::Context *context,
+                          const unsigned char* shaderFunction, unsigned int shaderSize,
+                          bool separateAttribs)
+        : GetExecutableTask(program, context)
+        , mShaderFunction(shaderFunction)
+        , mShaderSize(shaderSize)
+        , mSeparateAttribs(separateAttribs)
+    {
+    }
+
+    void InternalizeData()
+    {
+        mOwnedData.assign(mShaderFunction, mShaderFunction + mShaderSize);
+        mShaderFunction = mOwnedData.data();
+    }
+
+    const unsigned char *mShaderFunction;
+    const unsigned int mShaderSize;
+    const bool mSeparateAttribs;
+    std::vector<unsigned char> mOwnedData;
+};
+
+class ProgramD3D::GetLoadVertexExecutableTask : public ProgramD3D::GetLoadExecutableTask
+{
+public:
+    GetLoadVertexExecutableTask(ProgramD3D *program, const gl::Context *context,
+                                const unsigned char* shaderFunction, unsigned int shaderSize,
+                                bool separateAttribs, gl::InputLayout& layout)
+        : GetLoadExecutableTask(program, context, shaderFunction, shaderSize, separateAttribs)
+        , mLayout(layout)
+    {
+    }
+    angle::Result run() override
+    {
+        ANGLE_TRY(
+            mProgram->loadVertexExecutable(mContext, &mExecutable, mShaderFunction,
+                                           mShaderSize, mSeparateAttribs, mLayout));
+
+        return angle::Result::Continue();
+    }
+
+    gl::InputLayout mLayout;
+};
+
+class ProgramD3D::GetLoadPixelExecutableTask : public ProgramD3D::GetLoadExecutableTask
+{
+public:
+    GetLoadPixelExecutableTask(ProgramD3D *program, const gl::Context *context,
+                               const unsigned char* shaderFunction, unsigned int shaderSize,
+                               bool separateAttribs, std::vector<GLenum>& outputs)
+        : GetLoadExecutableTask(program, context, shaderFunction, shaderSize, separateAttribs)
+        , mOutputs(outputs)
+    {
+    }
+    angle::Result run() override
+    {
+        ANGLE_TRY(
+            mProgram->loadPixelExecutable(mContext, &mExecutable, mShaderFunction,
+                                          mShaderSize, mSeparateAttribs, mOutputs));
+
+        return angle::Result::Continue();
+    }
+
+    std::vector<GLenum> mOutputs;
+};
+
+angle::Result ProgramD3D::loadVertexExecutable(const gl::Context *context,
+                                               ShaderExecutableD3D **outExecutable,
+                                               const unsigned char *shaderFunction,
+                                               unsigned int shaderSize,
+                                               bool separateAttribs,
+                                               gl::InputLayout& layout)
+{
+    ANGLE_TRY(mRenderer->loadExecutable(context, shaderFunction, shaderSize,
+                                        gl::ShaderType::Vertex, mStreamOutVaryings,
+                                        separateAttribs, outExecutable));
+
+    // generated converted input layout
+    VertexExecutable::Signature signature;
+    VertexExecutable::getSignature(mRenderer, layout, &signature);
+
+    // add new binary
+    mVertexExecutables.push_back(std::unique_ptr<VertexExecutable>(
+        new VertexExecutable(layout, signature, *outExecutable)));
+
+    return angle::Result::Continue();
+}
+
+angle::Result ProgramD3D::loadPixelExecutable(const gl::Context *context,
+                                              ShaderExecutableD3D **outExecutable,
+                                              const unsigned char *shaderFunction,
+                                              unsigned int shaderSize,
+                                              bool separateAttribs,
+                                              std::vector<GLenum>& outputs)
+{
+    ANGLE_TRY(mRenderer->loadExecutable(context, shaderFunction, shaderSize,
+                                        gl::ShaderType::Fragment, mStreamOutVaryings,
+                                        separateAttribs, outExecutable));
+
+    // add new binary
+    mPixelExecutables.push_back(
+        std::unique_ptr<PixelExecutable>(new PixelExecutable(outputs, *outExecutable)));
+
+    return angle::Result::Continue();
+}
+
+// The LinkEvent implementation for linking a rendering(VS, FS, GS) program.
+class ProgramD3D::GraphicsProgramLinkEvent final : public LinkEvent
+{
+public:
+    GraphicsProgramLinkEvent(gl::InfoLog &infoLog,
+        std::shared_ptr<WorkerThreadPool> workerPool,
+        std::shared_ptr<ProgramD3D::GetExecutableTask> vertexTask,
+        std::shared_ptr<ProgramD3D::GetExecutableTask> pixelTask,
+        std::shared_ptr<ProgramD3D::GetExecutableTask> geometryTask,
+        bool useGS,
+        const ShaderD3D *vertexShader,
+        const ShaderD3D *fragmentShader)
+        : mInfoLog(infoLog),
+        mWorkerPool(workerPool),
+        mVertexTask(vertexTask),
+        mPixelTask(pixelTask),
+        mGeometryTask(geometryTask),
+        mWaitEvents(
+            { { std::shared_ptr<WaitableEvent>(workerPool->postWorkerTask(mVertexTask)),
+            std::shared_ptr<WaitableEvent>(workerPool->postWorkerTask(mPixelTask)),
+            std::shared_ptr<WaitableEvent>(workerPool->postWorkerTask(mGeometryTask)) } }),
+        mUseGS(useGS),
+        mVertexShader(vertexShader),
+        mFragmentShader(fragmentShader)
+    {
+    }
+
+    bool wait() override
+    {
+        WaitableEvent::WaitMany(&mWaitEvents);
+
+        if (!checkTask(mVertexTask.get()) || !checkTask(mPixelTask.get()) ||
+            !checkTask(mGeometryTask.get()))
+        {
+            return false;
+        }
+
+        ShaderExecutableD3D *defaultVertexExecutable = mVertexTask->getExecutable();
+        ShaderExecutableD3D *defaultPixelExecutable = mPixelTask->getExecutable();
+        ShaderExecutableD3D *pointGS = mGeometryTask->getExecutable();
+
+        if (mUseGS && pointGS)
+        {
+            // Geometry shaders are currently only used internally, so there is no corresponding
+            // shader object at the interface level. For now the geometry shader debug info is
+            // prepended to the vertex shader.
+            mVertexShader->appendDebugInfo("// GEOMETRY SHADER BEGIN\n\n");
+            mVertexShader->appendDebugInfo(pointGS->getDebugInfo());
+            mVertexShader->appendDebugInfo("\nGEOMETRY SHADER END\n\n\n");
+        }
+
+        if (defaultVertexExecutable && mVertexShader)
+        {
+            mVertexShader->appendDebugInfo(defaultVertexExecutable->getDebugInfo());
+        }
+
+        if (defaultPixelExecutable && mFragmentShader)
+        {
+            mFragmentShader->appendDebugInfo(defaultPixelExecutable->getDebugInfo());
+        }
+
+        bool isLinked = (defaultVertexExecutable && defaultPixelExecutable && (!mUseGS || pointGS));
+        if (!isLinked)
+        {
+            mInfoLog << "Failed to create D3D Shaders";
+        }
+        return isLinked;
+    }
+
+    bool isLinking() override
+    {
+        for (auto &event : mWaitEvents)
+        {
+            if (!event->isReady())
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+private:
+    bool checkTask(ProgramD3D::GetExecutableTask *task)
+    {
+        if (!task->getInfoLog().empty())
+        {
+            mInfoLog << task->getInfoLog().str();
+        }
+        auto result = task->getResult();
+        if (result.isError())
+        {
+            return false;
+        }
+        return true;
+    }
+
+    gl::InfoLog &mInfoLog;
+    std::shared_ptr<WorkerThreadPool> mWorkerPool;
+    std::shared_ptr<ProgramD3D::GetExecutableTask> mVertexTask;
+    std::shared_ptr<ProgramD3D::GetExecutableTask> mPixelTask;
+    std::shared_ptr<ProgramD3D::GetExecutableTask> mGeometryTask;
+    std::array<std::shared_ptr<WaitableEvent>, 3> mWaitEvents;
+    bool mUseGS;
+    const ShaderD3D *mVertexShader;
+    const ShaderD3D *mFragmentShader;
+};
+
+std::unique_ptr<LinkEvent> ProgramD3D::load(const gl::Context *context,
+                                            gl::InfoLog &infoLog,
+                                            gl::BinaryInputStream *stream)
 {
     // TODO(jmadill): Use Renderer from contextImpl.
 
     reset();
 
     DeviceIdentifier binaryDeviceIdentifier = {0};
     stream->readBytes(reinterpret_cast<unsigned char *>(&binaryDeviceIdentifier),
                       sizeof(DeviceIdentifier));
 
     DeviceIdentifier identifier = mRenderer->getAdapterIdentifier();
     if (memcmp(&identifier, &binaryDeviceIdentifier, sizeof(DeviceIdentifier)) != 0)
     {
         infoLog << "Invalid program binary, device configuration has changed.";
-        return false;
+        return std::make_unique<LinkEventDone>(false);
     }
 
     int compileFlags = stream->readInt<int>();
     if (compileFlags != ANGLE_COMPILE_OPTIMIZATION_LEVEL)
     {
         infoLog << "Mismatched compilation flags.";
-        return false;
+        return std::make_unique<LinkEventDone>(false);
     }
 
     for (int &index : mAttribLocationToD3DSemantic)
     {
         stream->readInt(&index);
     }
 
     for (gl::ShaderType shaderType : gl::AllShaderTypes())
@@ -811,17 +1056,17 @@ gl::LinkResult ProgramD3D::load(const gl
     mUsedComputeImageRange = gl::RangeUI(computeImageRangeLow, computeImageRangeHigh);
     mUsedComputeReadonlyImageRange =
         gl::RangeUI(computeReadonlyImageRangeLow, computeReadonlyImageRangeHigh);
 
     const unsigned int uniformCount = stream->readInt<unsigned int>();
     if (stream->error())
     {
         infoLog << "Invalid program binary.";
-        return false;
+        return std::make_unique<LinkEventDone>(false);
     }
 
     const auto &linkedUniforms = mState.getUniforms();
     ASSERT(mD3DUniforms.empty());
     for (unsigned int uniformIndex = 0; uniformIndex < uniformCount; uniformIndex++)
     {
         const gl::LinkedUniform &linkedUniform = linkedUniforms[uniformIndex];
 
@@ -838,17 +1083,17 @@ gl::LinkResult ProgramD3D::load(const gl
 
         mD3DUniforms.push_back(d3dUniform);
     }
 
     const unsigned int blockCount = stream->readInt<unsigned int>();
     if (stream->error())
     {
         infoLog << "Invalid program binary.";
-        return false;
+        return std::make_unique<LinkEventDone>(false);
     }
 
     ASSERT(mD3DUniformBlocks.empty());
     for (unsigned int blockIndex = 0; blockIndex < blockCount; ++blockIndex)
     {
         D3DUniformBlock uniformBlock;
         for (gl::ShaderType shaderType : gl::AllShaderTypes())
         {
@@ -894,135 +1139,155 @@ gl::LinkResult ProgramD3D::load(const gl
     }
 
     stream->readString(&mGeometryShaderPreamble);
 
     const unsigned char *binary = reinterpret_cast<const unsigned char *>(stream->data());
 
     bool separateAttribs = (mState.getTransformFeedbackBufferMode() == GL_SEPARATE_ATTRIBS);
 
+    std::vector<std::shared_ptr<GetLoadExecutableTask>> tasks;
+    auto FlushTasks = [&]() {
+        for (auto& task : tasks) {
+            angle::Result result = task->run();
+
+            if (result.isError()) {
+                return std::make_unique<LinkEventDone>(result);
+            }
+
+            ShaderExecutableD3D *shaderExecutable = task->getExecutable();
+
+            if (!shaderExecutable)
+            {
+                infoLog << "Could not create shader.";
+                return std::make_unique<LinkEventDone>(false);
+            }
+        }
+        tasks.clear();
+        return std::unique_ptr<LinkEventDone>(nullptr);
+    };
+
     const unsigned int vertexShaderCount = stream->readInt<unsigned int>();
     for (unsigned int vertexShaderIndex = 0; vertexShaderIndex < vertexShaderCount;
          vertexShaderIndex++)
     {
         size_t inputLayoutSize = stream->readInt<size_t>();
         gl::InputLayout inputLayout(inputLayoutSize, gl::VERTEX_FORMAT_INVALID);
 
         for (size_t inputIndex = 0; inputIndex < inputLayoutSize; inputIndex++)
         {
             inputLayout[inputIndex] = stream->readInt<gl::VertexFormatType>();
         }
 
-        unsigned int vertexShaderSize             = stream->readInt<unsigned int>();
+        unsigned int vertexShaderSize = stream->readInt<unsigned int>();
         const unsigned char *vertexShaderFunction = binary + stream->offset();
 
-        ShaderExecutableD3D *shaderExecutable = nullptr;
-
-        ANGLE_TRY(mRenderer->loadExecutable(context, vertexShaderFunction, vertexShaderSize,
-                                            gl::ShaderType::Vertex, mStreamOutVaryings,
-                                            separateAttribs, &shaderExecutable));
-
-        if (!shaderExecutable)
-        {
-            infoLog << "Could not create vertex shader.";
-            return false;
-        }
-
-        // generated converted input layout
-        VertexExecutable::Signature signature;
-        VertexExecutable::getSignature(mRenderer, inputLayout, &signature);
-
-        // add new binary
-        mVertexExecutables.push_back(std::unique_ptr<VertexExecutable>(
-            new VertexExecutable(inputLayout, signature, shaderExecutable)));
-
+        tasks.emplace_back(std::make_shared<GetLoadVertexExecutableTask>(this, context, vertexShaderFunction,
+                                                                         vertexShaderSize, separateAttribs, inputLayout));
         stream->skip(vertexShaderSize);
     }
 
     const size_t pixelShaderCount = stream->readInt<unsigned int>();
     for (size_t pixelShaderIndex = 0; pixelShaderIndex < pixelShaderCount; pixelShaderIndex++)
     {
         const size_t outputCount = stream->readInt<unsigned int>();
         std::vector<GLenum> outputs(outputCount);
         for (size_t outputIndex = 0; outputIndex < outputCount; outputIndex++)
         {
             stream->readInt(&outputs[outputIndex]);
         }
 
         const size_t pixelShaderSize             = stream->readInt<unsigned int>();
         const unsigned char *pixelShaderFunction = binary + stream->offset();
-        ShaderExecutableD3D *shaderExecutable    = nullptr;
-
-        ANGLE_TRY(mRenderer->loadExecutable(context, pixelShaderFunction, pixelShaderSize,
-                                            gl::ShaderType::Fragment, mStreamOutVaryings,
-                                            separateAttribs, &shaderExecutable));
-
-        if (!shaderExecutable)
-        {
-            infoLog << "Could not create pixel shader.";
-            return false;
-        }
-
-        // add new binary
-        mPixelExecutables.push_back(
-            std::unique_ptr<PixelExecutable>(new PixelExecutable(outputs, shaderExecutable)));
-
+
+        tasks.emplace_back(std::make_shared<GetLoadPixelExecutableTask>(this, context, pixelShaderFunction,
+                                                                        pixelShaderSize, separateAttribs, outputs));
         stream->skip(pixelShaderSize);
     }
 
     for (auto &geometryExe : mGeometryExecutables)
     {
         unsigned int geometryShaderSize = stream->readInt<unsigned int>();
         if (geometryShaderSize == 0)
         {
             continue;
         }
 
+        auto failure = FlushTasks();
+        if (failure) {
+            return std::move(failure);
+        }
+
         const unsigned char *geometryShaderFunction = binary + stream->offset();
 
         ShaderExecutableD3D *geometryExecutable = nullptr;
-        ANGLE_TRY(mRenderer->loadExecutable(context, geometryShaderFunction, geometryShaderSize,
-                                            gl::ShaderType::Geometry, mStreamOutVaryings,
-                                            separateAttribs, &geometryExecutable));
+        angle::Result result = mRenderer->loadExecutable(context, geometryShaderFunction, geometryShaderSize,
+                                                         gl::ShaderType::Geometry, mStreamOutVaryings,
+                                                         separateAttribs, &geometryExecutable);
+        if (result.isError()) {
+            return std::make_unique<LinkEventDone>(result);
+        }
 
         if (!geometryExecutable)
         {
             infoLog << "Could not create geometry shader.";
-            return false;
+            return std::make_unique<LinkEventDone>(false);
         }
 
         geometryExe.reset(geometryExecutable);
 
         stream->skip(geometryShaderSize);
     }
 
     unsigned int computeShaderSize = stream->readInt<unsigned int>();
     if (computeShaderSize > 0)
     {
+        auto failure = FlushTasks();
+        if (failure) {
+            return std::move(failure);
+        }
+
         const unsigned char *computeShaderFunction = binary + stream->offset();
 
         ShaderExecutableD3D *computeExecutable = nullptr;
-        ANGLE_TRY(mRenderer->loadExecutable(context, computeShaderFunction, computeShaderSize,
-                                            gl::ShaderType::Compute, std::vector<D3DVarying>(),
-                                            false, &computeExecutable));
+        angle::Result result = mRenderer->loadExecutable(context, computeShaderFunction, computeShaderSize,
+                                                         gl::ShaderType::Compute, std::vector<D3DVarying>(),
+                                                         false, &computeExecutable);
+        if (result.isError()) {
+            return std::make_unique<LinkEventDone>(result);
+        }
 
         if (!computeExecutable)
         {
             infoLog << "Could not create compute shader.";
-            return false;
+            return std::make_unique<LinkEventDone>(false);
         }
 
         mComputeExecutable.reset(computeExecutable);
     }
 
     initializeUniformStorage(mState.getLinkedShaderStages());
 
     dirtyAllUniforms();
 
-    return true;
+    if (tasks.size() == 2 && vertexShaderCount == 1 && pixelShaderCount == 1) {
+        auto geometryTask = std::make_shared<GetGeometryExecutableTask>(this, context);
+        tasks[0]->InternalizeData();
+        tasks[1]->InternalizeData();
+        return std::make_unique<GraphicsProgramLinkEvent>(infoLog, context->getWorkerThreadPool(),
+            tasks[0], tasks[1], geometryTask, false,
+            nullptr, nullptr);
+    } else {
+        auto result = FlushTasks();
+        if (result) {
+          return std::move(result);
+        }
+    }
+
+    return std::make_unique<LinkEventDone>(true);
 }
 
 void ProgramD3D::save(const gl::Context *context, gl::BinaryOutputStream *stream)
 {
     // Output the DeviceIdentifier before we output any shader code
     // When we load the binary again later, we can validate the device identifier before trying to
     // compile any HLSL
     DeviceIdentifier binaryIdentifier = mRenderer->getAdapterIdentifier();
@@ -1292,17 +1557,17 @@ angle::Result ProgramD3D::getGeometryExe
                                                                 gl::InfoLog *infoLog)
 {
     if (outExecutable)
     {
         *outExecutable = nullptr;
     }
 
     // Return a null shader if the current rendering doesn't use a geometry shader
-    if (!usesGeometryShader(drawMode))
+    if (!usesGeometryShader(context, drawMode))
     {
         return angle::Result::Continue();
     }
 
     gl::PrimitiveMode geometryShaderType = GetGeometryShaderTypeFromDrawMode(drawMode);
 
     if (mGeometryExecutables[geometryShaderType])
     {
@@ -1340,43 +1605,16 @@ angle::Result ProgramD3D::getGeometryExe
 
     if (outExecutable)
     {
         *outExecutable = mGeometryExecutables[geometryShaderType].get();
     }
     return result;
 }
 
-class ProgramD3D::GetExecutableTask : public Closure
-{
-  public:
-    GetExecutableTask(ProgramD3D *program, const gl::Context *context)
-        : mProgram(program),
-          mResult(angle::Result::Continue()),
-          mInfoLog(),
-          mExecutable(nullptr),
-          mContext(context)
-    {
-    }
-
-    virtual angle::Result run() = 0;
-
-    void operator()() override { mResult = run(); }
-
-    angle::Result getResult() const { return mResult; }
-    const gl::InfoLog &getInfoLog() const { return mInfoLog; }
-    ShaderExecutableD3D *getExecutable() { return mExecutable; }
-
-  protected:
-    ProgramD3D *mProgram;
-    angle::Result mResult;
-    gl::InfoLog mInfoLog;
-    ShaderExecutableD3D *mExecutable;
-    const gl::Context *mContext;
-};
 
 class ProgramD3D::GetVertexExecutableTask : public ProgramD3D::GetExecutableTask
 {
   public:
     GetVertexExecutableTask(ProgramD3D *program, const gl::Context *context)
         : GetExecutableTask(program, context)
     {
     }
@@ -1430,17 +1668,17 @@ class ProgramD3D::GetGeometryExecutableT
         : GetExecutableTask(program, context)
     {
     }
 
     angle::Result run() override
     {
         // Auto-generate the geometry shader here, if we expect to be using point rendering in
         // D3D11.
-        if (mProgram->usesGeometryShader(gl::PrimitiveMode::Points))
+        if (mProgram->usesGeometryShader(mContext, gl::PrimitiveMode::Points))
         {
             ANGLE_TRY(mProgram->getGeometryExecutableForPrimitiveType(
                 mContext, gl::PrimitiveMode::Points, &mExecutable, &mInfoLog));
         }
 
         return angle::Result::Continue();
     }
 };
@@ -1450,137 +1688,31 @@ angle::Result ProgramD3D::getComputeExec
     if (outExecutable)
     {
         *outExecutable = mComputeExecutable.get();
     }
 
     return angle::Result::Continue();
 }
 
-// The LinkEvent implementation for linking a rendering(VS, FS, GS) program.
-class ProgramD3D::GraphicsProgramLinkEvent final : public LinkEvent
-{
-  public:
-    GraphicsProgramLinkEvent(gl::InfoLog &infoLog,
-                             std::shared_ptr<WorkerThreadPool> workerPool,
-                             std::shared_ptr<ProgramD3D::GetVertexExecutableTask> vertexTask,
-                             std::shared_ptr<ProgramD3D::GetPixelExecutableTask> pixelTask,
-                             std::shared_ptr<ProgramD3D::GetGeometryExecutableTask> geometryTask,
-                             bool useGS,
-                             const ShaderD3D *vertexShader,
-                             const ShaderD3D *fragmentShader)
-        : mInfoLog(infoLog),
-          mWorkerPool(workerPool),
-          mVertexTask(vertexTask),
-          mPixelTask(pixelTask),
-          mGeometryTask(geometryTask),
-          mWaitEvents(
-              {{std::shared_ptr<WaitableEvent>(workerPool->postWorkerTask(mVertexTask)),
-                std::shared_ptr<WaitableEvent>(workerPool->postWorkerTask(mPixelTask)),
-                std::shared_ptr<WaitableEvent>(workerPool->postWorkerTask(mGeometryTask))}}),
-          mUseGS(useGS),
-          mVertexShader(vertexShader),
-          mFragmentShader(fragmentShader)
-    {
-    }
-
-    bool wait() override
-    {
-        WaitableEvent::WaitMany(&mWaitEvents);
-
-        if (!checkTask(mVertexTask.get()) || !checkTask(mPixelTask.get()) ||
-            !checkTask(mGeometryTask.get()))
-        {
-            return false;
-        }
-
-        ShaderExecutableD3D *defaultVertexExecutable = mVertexTask->getExecutable();
-        ShaderExecutableD3D *defaultPixelExecutable  = mPixelTask->getExecutable();
-        ShaderExecutableD3D *pointGS                 = mGeometryTask->getExecutable();
-
-        if (mUseGS && pointGS)
-        {
-            // Geometry shaders are currently only used internally, so there is no corresponding
-            // shader object at the interface level. For now the geometry shader debug info is
-            // prepended to the vertex shader.
-            mVertexShader->appendDebugInfo("// GEOMETRY SHADER BEGIN\n\n");
-            mVertexShader->appendDebugInfo(pointGS->getDebugInfo());
-            mVertexShader->appendDebugInfo("\nGEOMETRY SHADER END\n\n\n");
-        }
-
-        if (defaultVertexExecutable)
-        {
-            mVertexShader->appendDebugInfo(defaultVertexExecutable->getDebugInfo());
-        }
-
-        if (defaultPixelExecutable)
-        {
-            mFragmentShader->appendDebugInfo(defaultPixelExecutable->getDebugInfo());
-        }
-
-        bool isLinked = (defaultVertexExecutable && defaultPixelExecutable && (!mUseGS || pointGS));
-        if (!isLinked)
-        {
-            mInfoLog << "Failed to create D3D Shaders";
-        }
-        return isLinked;
-    }
-
-    bool isLinking() override
-    {
-        for (auto &event : mWaitEvents)
-        {
-            if (!event->isReady())
-            {
-                return true;
-            }
-        }
-        return false;
-    }
-
-  private:
-    bool checkTask(ProgramD3D::GetExecutableTask *task)
-    {
-        if (!task->getInfoLog().empty())
-        {
-            mInfoLog << task->getInfoLog().str();
-        }
-        auto result = task->getResult();
-        if (result.isError())
-        {
-            return false;
-        }
-        return true;
-    }
-
-    gl::InfoLog &mInfoLog;
-    std::shared_ptr<WorkerThreadPool> mWorkerPool;
-    std::shared_ptr<ProgramD3D::GetVertexExecutableTask> mVertexTask;
-    std::shared_ptr<ProgramD3D::GetPixelExecutableTask> mPixelTask;
-    std::shared_ptr<ProgramD3D::GetGeometryExecutableTask> mGeometryTask;
-    std::array<std::shared_ptr<WaitableEvent>, 3> mWaitEvents;
-    bool mUseGS;
-    const ShaderD3D *mVertexShader;
-    const ShaderD3D *mFragmentShader;
-};
 
 std::unique_ptr<LinkEvent> ProgramD3D::compileProgramExecutables(const gl::Context *context,
                                                                  gl::InfoLog &infoLog)
 {
     // Ensure the compiler is initialized to avoid race conditions.
     gl::Error result = mRenderer->ensureHLSLCompilerInitialized(context);
     if (result.isError())
     {
         return std::make_unique<LinkEventDone>(result);
     }
 
     auto vertexTask   = std::make_shared<GetVertexExecutableTask>(this, context);
     auto pixelTask    = std::make_shared<GetPixelExecutableTask>(this, context);
     auto geometryTask = std::make_shared<GetGeometryExecutableTask>(this, context);
-    bool useGS        = usesGeometryShader(gl::PrimitiveMode::Points);
+    bool useGS        = usesGeometryShader(context, gl::PrimitiveMode::Points);
     const ShaderD3D *vertexShaderD3D =
         GetImplAs<ShaderD3D>(mState.getAttachedShader(gl::ShaderType::Vertex));
     const ShaderD3D *fragmentShaderD3D =
         GetImplAs<ShaderD3D>(mState.getAttachedShader(gl::ShaderType::Fragment));
 
     return std::make_unique<GraphicsProgramLinkEvent>(infoLog, context->getWorkerThreadPool(),
                                                       vertexTask, pixelTask, geometryTask, useGS,
                                                       vertexShaderD3D, fragmentShaderD3D);
@@ -2753,19 +2885,20 @@ void ProgramD3D::setPathFragmentInputGen
     UNREACHABLE();
 }
 
 bool ProgramD3D::hasVertexExecutableForCachedInputLayout()
 {
     return mCachedVertexExecutableIndex.valid();
 }
 
-bool ProgramD3D::hasGeometryExecutableForPrimitiveType(gl::PrimitiveMode drawMode)
+bool ProgramD3D::hasGeometryExecutableForPrimitiveType(const gl::Context* context,
+                                                       gl::PrimitiveMode drawMode)
 {
-    if (!usesGeometryShader(drawMode))
+    if (!usesGeometryShader(context, drawMode))
     {
         // No shader necessary mean we have the required (null) executable.
         return true;
     }
 
     gl::PrimitiveMode geometryShaderType = GetGeometryShaderTypeFromDrawMode(drawMode);
     return mGeometryExecutables[geometryShaderType].get() != nullptr;
 }
--- a/gfx/angle/checkout/src/libANGLE/renderer/d3d/ProgramD3D.h
+++ b/gfx/angle/checkout/src/libANGLE/renderer/d3d/ProgramD3D.h
@@ -169,37 +169,50 @@ class ProgramD3D : public ProgramImpl
     GLint getImageMapping(gl::ShaderType type,
                           unsigned int imageIndex,
                           bool readonly,
                           const gl::Caps &caps) const;
     gl::RangeUI getUsedImageRange(gl::ShaderType type, bool readonly) const;
 
     bool usesPointSize() const { return mUsesPointSize; }
     bool usesPointSpriteEmulation() const;
-    bool usesGeometryShader(gl::PrimitiveMode drawMode) const;
+	bool usesGeometryShader(const gl::Context *context,
+		                    gl::PrimitiveMode drawMode) const;
     bool usesGeometryShaderForPointSpriteEmulation() const;
     bool usesInstancedPointSpriteEmulation() const;
 
-    gl::LinkResult load(const gl::Context *context,
-                        gl::InfoLog &infoLog,
-                        gl::BinaryInputStream *stream) override;
+    std::unique_ptr<LinkEvent> load(const gl::Context *context,
+                                    gl::InfoLog &infoLog,
+                                    gl::BinaryInputStream *stream) override;
     void save(const gl::Context *context, gl::BinaryOutputStream *stream) override;
     void setBinaryRetrievableHint(bool retrievable) override;
     void setSeparable(bool separable) override;
 
     angle::Result getVertexExecutableForCachedInputLayout(const gl::Context *context,
                                                           ShaderExecutableD3D **outExectuable,
                                                           gl::InfoLog *infoLog);
     angle::Result getGeometryExecutableForPrimitiveType(const gl::Context *context,
                                                         gl::PrimitiveMode drawMode,
                                                         ShaderExecutableD3D **outExecutable,
                                                         gl::InfoLog *infoLog);
     angle::Result getPixelExecutableForCachedOutputLayout(const gl::Context *context,
                                                           ShaderExecutableD3D **outExectuable,
                                                           gl::InfoLog *infoLog);
+    angle::Result loadVertexExecutable(const gl::Context *context,
+                                       ShaderExecutableD3D **outExecutable,
+                                       const unsigned char *shaderFunction,
+                                       unsigned int shaderSize,
+                                       bool separateAttribs,
+                                       gl::InputLayout& layout);
+    angle::Result loadPixelExecutable(const gl::Context *context,
+                                      ShaderExecutableD3D **outExecutable,
+                                      const unsigned char *shaderFunction,
+                                      unsigned int shaderSize,
+                                      bool separateAttribs,
+                                      std::vector<GLenum>& layout);
     angle::Result getComputeExecutable(ShaderExecutableD3D **outExecutable);
     std::unique_ptr<LinkEvent> link(const gl::Context *context,
                                     const gl::ProgramLinkedResources &resources,
                                     gl::InfoLog &infoLog) override;
     GLboolean validate(const gl::Caps &caps, gl::InfoLog *infoLog) override;
 
     void setPathFragmentInputGen(const std::string &inputName,
                                  GLenum genMode,
@@ -279,17 +292,18 @@ class ProgramD3D : public ProgramImpl
 
     void updateCachedInputLayout(Serial associatedSerial, const gl::State &state);
     void updateCachedOutputLayout(const gl::Context *context, const gl::Framebuffer *framebuffer);
 
     bool isSamplerMappingDirty() { return mDirtySamplerMapping; }
 
     // Checks if we need to recompile certain shaders.
     bool hasVertexExecutableForCachedInputLayout();
-    bool hasGeometryExecutableForPrimitiveType(gl::PrimitiveMode drawMode);
+    bool hasGeometryExecutableForPrimitiveType(const gl::Context *context,
+                                               gl::PrimitiveMode drawMode);
     bool hasPixelExecutableForCachedOutputLayout();
 
     bool anyShaderUniformsDirty() const { return mShaderUniformsDirty.any(); }
 
     bool areShaderUniformsDirty(gl::ShaderType shaderType) const
     {
         return mShaderUniformsDirty[shaderType];
     }
@@ -304,16 +318,19 @@ class ProgramD3D : public ProgramImpl
     }
 
   private:
     // These forward-declared tasks are used for multi-thread shader compiles.
     class GetExecutableTask;
     class GetVertexExecutableTask;
     class GetPixelExecutableTask;
     class GetGeometryExecutableTask;
+    class GetLoadExecutableTask;
+    class GetLoadVertexExecutableTask;
+    class GetLoadPixelExecutableTask;
     class GraphicsProgramLinkEvent;
 
     class VertexExecutable
     {
       public:
         enum HLSLAttribType
         {
             FLOAT,
--- a/gfx/angle/checkout/src/libANGLE/renderer/d3d/d3d11/Context11.cpp
+++ b/gfx/angle/checkout/src/libANGLE/renderer/d3d/d3d11/Context11.cpp
@@ -497,17 +497,17 @@ angle::Result Context11::triggerDrawCall
     const auto *drawFBO    = glState.getDrawFramebuffer();
     gl::Program *program   = glState.getProgram();
     ProgramD3D *programD3D = GetImplAs<ProgramD3D>(program);
 
     programD3D->updateCachedInputLayout(va11->getCurrentStateSerial(), glState);
     programD3D->updateCachedOutputLayout(context, drawFBO);
 
     bool recompileVS = !programD3D->hasVertexExecutableForCachedInputLayout();
-    bool recompileGS = !programD3D->hasGeometryExecutableForPrimitiveType(drawMode);
+    bool recompileGS = !programD3D->hasGeometryExecutableForPrimitiveType(context, drawMode);
     bool recompilePS = !programD3D->hasPixelExecutableForCachedOutputLayout();
 
     if (!recompileVS && !recompileGS && !recompilePS)
     {
         return angle::Result::Continue();
     }
 
     // Load the compiler if necessary and recompile the programs.
@@ -528,17 +528,17 @@ angle::Result Context11::triggerDrawCall
         }
     }
 
     if (recompileGS)
     {
         ShaderExecutableD3D *geometryExe = nullptr;
         ANGLE_TRY(programD3D->getGeometryExecutableForPrimitiveType(context, drawMode, &geometryExe,
                                                                     &infoLog));
-        if (!programD3D->hasGeometryExecutableForPrimitiveType(drawMode))
+        if (!programD3D->hasGeometryExecutableForPrimitiveType(context, drawMode))
         {
             ASSERT(infoLog.getLength() > 0);
             ERR() << "Error compiling dynamic geometry executable: " << infoLog.str();
             ANGLE_TRY_HR(this, E_FAIL, "Error compiling dynamic geometry executable");
         }
     }
 
     if (recompilePS)
--- a/gfx/angle/checkout/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
+++ b/gfx/angle/checkout/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
@@ -1460,17 +1460,17 @@ angle::Result Renderer11::drawArrays(con
     // Note: vertex indexes can be arbitrarily large.
     UINT clampedVertexCount = params.getClampedVertexCount<UINT>();
 
     const auto &glState = context->getGLState();
     if (glState.getCurrentTransformFeedback() && glState.isTransformFeedbackActiveUnpaused())
     {
         ANGLE_TRY(markTransformFeedbackUsage(context));
 
-        if (programD3D->usesGeometryShader(params.mode()))
+        if (programD3D->usesGeometryShader(context, params.mode()))
         {
             return drawWithGeometryShaderAndTransformFeedback(
                 context, params.mode(), adjustedInstanceCount, clampedVertexCount);
         }
     }
 
     switch (params.mode())
     {
--- a/gfx/angle/checkout/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp
+++ b/gfx/angle/checkout/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp
@@ -2655,17 +2655,17 @@ angle::Result StateManager11::syncProgra
     ANGLE_TRY(context11->triggerDrawCallProgramRecompilation(context, drawMode));
 
     const auto &glState = context->getGLState();
 
     mProgramD3D->updateCachedInputLayout(mVertexArray11->getCurrentStateSerial(), glState);
 
     // Binaries must be compiled before the sync.
     ASSERT(mProgramD3D->hasVertexExecutableForCachedInputLayout());
-    ASSERT(mProgramD3D->hasGeometryExecutableForPrimitiveType(drawMode));
+    ASSERT(mProgramD3D->hasGeometryExecutableForPrimitiveType(context, drawMode));
     ASSERT(mProgramD3D->hasPixelExecutableForCachedOutputLayout());
 
     ShaderExecutableD3D *vertexExe = nullptr;
     ANGLE_TRY(mProgramD3D->getVertexExecutableForCachedInputLayout(context, &vertexExe, nullptr));
 
     ShaderExecutableD3D *pixelExe = nullptr;
     ANGLE_TRY(mProgramD3D->getPixelExecutableForCachedOutputLayout(context, &pixelExe, nullptr));
 
--- a/gfx/angle/checkout/src/libANGLE/validationEGL.cpp
+++ b/gfx/angle/checkout/src/libANGLE/validationEGL.cpp
@@ -979,16 +979,30 @@ Error ValidateCreateContext(Display *dis
                 }
                 if (value != EGL_TRUE && value != EGL_FALSE)
                 {
                     return EglBadAttribute() << "EGL_EXTENSIONS_ENABLED_ANGLE must be "
                                                 "either EGL_TRUE or EGL_FALSE.";
                 }
                 break;
 
+            case EGL_CONTEXT_PROVOKING_VERTEX_DONT_CARE_MOZ:
+                if (!display->getExtensions().provokingVertexDontCare)
+                {
+                    return EglBadAttribute()
+                           << "Attribute EGL_CONTEXT_PROVOKING_VERTEX_DONT_CARE_MOZ "
+                              "requires EGL_MOZ_create_context_provoking_vertex_dont_care.";
+                }
+                if (value != EGL_TRUE && value != EGL_FALSE)
+                {
+                    return EglBadAttribute() << "EGL_CONTEXT_PROVOKING_VERTEX_DONT_CARE_MOZ "
+                                              "must be either EGL_TRUE or EGL_FALSE.";
+                }
+                break;
+
             default:
                 return EglBadAttribute() << "Unknown attribute.";
         }
     }
 
     switch (clientMajorVersion)
     {
         case 1:
--- a/gfx/angle/cherry_picks.txt
+++ b/gfx/angle/cherry_picks.txt
@@ -1,8 +1,51 @@
+commit 598f2502e3a41b76d90037e7858c43c18e66399d
+Merge: 8212058a6 e15a25c6f
+Author: Jeff Gilbert <jdashg@gmail.com>
+Date:   Tue Oct 9 13:54:05 2018 -0700
+
+    Merge pull request #13 from mattwoodrow/async-load-program
+    
+    Support async glProgramBInary
+
+commit 8212058a6c6339ba541f9aabb076900cd31448ef
+Merge: 4fb8d7f97 895294a05
+Author: Jeff Gilbert <jdashg@gmail.com>
+Date:   Tue Oct 9 13:52:08 2018 -0700
+
+    Merge pull request #14 from mattwoodrow/provoking-vertex
+    
+    Add support for EGL_MOZ_create_context_provoking_vertex_dont_care
+
+commit 895294a057c022f0db77975a19419abbec4c9226
+Author: Dan Glastonbury <dan.glastonbury@gmail.com>
+Date:   Tue Oct 9 10:02:46 2018 +1300
+
+    Add support for EGL_MOZ_create_context_provoking_vertex_dont_care
+    extension
+
+commit e15a25c6fa1d910115ce6fd7a03fa5ffece8a054
+Author: Matt Woodrow <matt.woodrow@gmail.com>
+Date:   Tue Oct 9 09:56:16 2018 +1300
+
+    Support async Program::load for ProgramD3D.
+
+commit 2555cc62e340cef6a2744b7f52f3e8b94aa635e1
+Author: Matt Woodrow <matt.woodrow@gmail.com>
+Date:   Tue Oct 9 09:53:40 2018 +1300
+
+    Move task definitions to earlier in the file.
+
+commit 04adbfe1a2bfad21c549a0d30349d1da620e556c
+Author: Matt Woodrow <matt.woodrow@gmail.com>
+Date:   Tue Oct 9 09:52:42 2018 +1300
+
+    Make ProgramImpl::load() return a task and support asynchronously waiting for it to complete.
+
 commit 4fb8d7f978adda36086377ea7846951faa9f6bd3
 Author: Jeff Gilbert <jgilbert@mozilla.com>
 Date:   Wed Sep 26 18:04:05 2018 -0700
 
     Polyfill BitCount for ARM/ARM64 on MSVC.
     
     Also _WIN64 implies _WIN32.
     
--- a/js/src/jit-test/tests/TypedObject/Bug981650.js
+++ b/js/src/jit-test/tests/TypedObject/Bug981650.js
@@ -1,9 +1,6 @@
 // Fuzz bug 981650: Test creating an array type based on an instance of
 // that same type.
 
-if (typeof TypedObject === "undefined")
-  quit();
-
 var T = TypedObject;
 var v = new T.ArrayType(T.int32, 10);
 new v(v);
--- a/js/src/jit-test/tests/TypedObject/aggregate-set-neutered.js
+++ b/js/src/jit-test/tests/TypedObject/aggregate-set-neutered.js
@@ -1,14 +1,11 @@
 // Bug 991981. Check for various quirks when setting a field of a typed object
 // during which set operation the underlying buffer is detached.
 
-if (typeof TypedObject === "undefined")
-  quit();
-
 load(libdir + "asserts.js")
 
 var StructType = TypedObject.StructType;
 var uint32 = TypedObject.uint32;
 
 function main()
 {
   var Point = new StructType({ x: uint32, y: uint32 });
--- a/js/src/jit-test/tests/TypedObject/array-hasproperty.js
+++ b/js/src/jit-test/tests/TypedObject/array-hasproperty.js
@@ -1,11 +1,8 @@
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
-
 var array = new (TypedObject.uint8.array(5));
 
 for (var i = 0; i < array.length; i++)
     assertEq(i in array, true);
 
 for (var v of [20, 300, -1, 5, -10, Math.pow(2, 32) - 1, -Math.pow(2, 32)])
     assertEq(v in array, false);
 
--- a/js/src/jit-test/tests/TypedObject/bug1004527.js
+++ b/js/src/jit-test/tests/TypedObject/bug1004527.js
@@ -1,9 +1,6 @@
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
-
 var { ArrayType, StructType, uint32 } = TypedObject;
 var L = 1024;
 var Matrix = uint32.array(L, 2);
 var matrix = new Matrix();
 evaluate("for (var i = 0; i < L; i++) matrix[i][0] = (function d() {});",
 	 { isRunOnce: true });
--- a/js/src/jit-test/tests/TypedObject/bug1096016.js
+++ b/js/src/jit-test/tests/TypedObject/bug1096016.js
@@ -1,11 +1,8 @@
-if (typeof TypedObject === "undefined")
-  quit();
-
 var T = TypedObject;
 var ObjectStruct = new T.StructType({f: T.Object});
 var o = new ObjectStruct();
 function testGC(o, p) {
     for (var i = 0; i < 5; i++) {
         minorgc();
         o.f >>=  p;
     }
--- a/js/src/jit-test/tests/TypedObject/bug1096023.js
+++ b/js/src/jit-test/tests/TypedObject/bug1096023.js
@@ -1,11 +1,8 @@
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
-
 enableGeckoProfiling();
 var T = TypedObject;
 function check(results, ctor) {
   for (var i = 0; i < results.length; i++)
   var S = new T.StructType({f: ctor});
   for (var i = 0; i < results.length; i++) {
     var s = new S({f: results[i][1]});
   }
--- a/js/src/jit-test/tests/TypedObject/bug1098961.js
+++ b/js/src/jit-test/tests/TypedObject/bug1098961.js
@@ -1,9 +1,6 @@
-if (!this.hasOwnProperty("TypedObject"))
-    quit();
-
 Array.prototype[Symbol.iterator] = function*() {
     for (var i = 3; --i >= 0;) {
         yield this[i]
     }
 }
 new TypedObject.ArrayType(TypedObject.int32, 0).build(1, x => 1)
--- a/js/src/jit-test/tests/TypedObject/bug1100202.js
+++ b/js/src/jit-test/tests/TypedObject/bug1100202.js
@@ -1,11 +1,8 @@
-if (typeof TypedObject === "undefined")
-  quit();
-
 (function() {
     Object
 })()
 var {
     Object
 } = TypedObject
 function f() {
     Object(Symbol)
--- a/js/src/jit-test/tests/TypedObject/bug1102329.js
+++ b/js/src/jit-test/tests/TypedObject/bug1102329.js
@@ -1,11 +1,8 @@
-if (typeof TypedObject === "undefined")
-  quit();
-
 A = Array.bind()
 var {
     StructType
 } = TypedObject
 var A = new StructType({});
 (function() {
     new A
     for (var i = 0; i < 9; i++) {}
--- a/js/src/jit-test/tests/TypedObject/bug1103273-1.js
+++ b/js/src/jit-test/tests/TypedObject/bug1103273-1.js
@@ -1,11 +1,8 @@
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
-
 gczeal(2);
 var Vec3u16Type = TypedObject.uint16.array(3);
 function foo_u16(n) {
     if (n == 0)
         return;
     var i = 0;
     var vec = new Vec3u16Type([i, i+1, i+2]);
     var sum = vec[0] + vec[1] + vec[(/[]/g )];
--- a/js/src/jit-test/tests/TypedObject/bug1103273-2.js
+++ b/js/src/jit-test/tests/TypedObject/bug1103273-2.js
@@ -1,11 +1,8 @@
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
-
 var Vec3u16Type = TypedObject.uint16.array(3);
 
 function foo() {
     var x = 0;
     for (var i = 0; i < 3; i++) {
         var obj = new Uint16Array(3);
         var buf = obj.buffer;
         var arr = new Uint8Array(buf, 3);
--- a/js/src/jit-test/tests/TypedObject/bug1232159.js
+++ b/js/src/jit-test/tests/TypedObject/bug1232159.js
@@ -1,11 +1,8 @@
-if (!this.hasOwnProperty("TypedObject"))
-    quit();
-
 Function.prototype.prototype = function() {}
 
 var type = TypedObject.uint8.array(4).array(4);
 var x = new type([
     [, , , 0],
     [, , , 0],
     [, , , 0],
     [, , , 0]
--- a/js/src/jit-test/tests/TypedObject/bug1265690.js
+++ b/js/src/jit-test/tests/TypedObject/bug1265690.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this) || !this.hasOwnProperty("TypedObject"))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 lfCodeBuffer = `
     ArrayType = TypedObject.ArrayType;
     var StructType = TypedObject.StructType;
     float32 = TypedObject.float32;
     Point = new ArrayType(float32, 3);
     var Line = new StructType({ Point });
     new ArrayType(Line, 3);
 `;
--- a/js/src/jit-test/tests/TypedObject/bug1369774.js
+++ b/js/src/jit-test/tests/TypedObject/bug1369774.js
@@ -1,7 +1,5 @@
-if (!this.hasOwnProperty("TypedObject"))
-    quit();
 var T = TypedObject;
 var S = new T.StructType({f: T.Object});
 var o = new S();
 for (var i = 0; i < 15; i++)
     o.f = null;
--- a/js/src/jit-test/tests/TypedObject/bug920463.js
+++ b/js/src/jit-test/tests/TypedObject/bug920463.js
@@ -1,11 +1,8 @@
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
-
 var StructType = TypedObject.StructType;
 var float64 = TypedObject.float64;
 
 var PointType3 = new StructType({ x: float64, y: float64});
 function xPlusY(p) {
   return p.x + p.y;
 }
 var N = 30000;
--- a/js/src/jit-test/tests/TypedObject/bug950458.js
+++ b/js/src/jit-test/tests/TypedObject/bug950458.js
@@ -1,11 +1,8 @@
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
-
 // Test for fuzz condition failure. Cause of the failure
 // was that we were not adding a type barrier after the
 // creation of derived typed objects. When run in --ion-eager
 // mode, arr[i] (below) would yield a derived typed object
 // with an empty type set, which would then fail sanity
 // checking assertions.
 //
 // Public domain.
--- a/js/src/jit-test/tests/TypedObject/bug953111.js
+++ b/js/src/jit-test/tests/TypedObject/bug953111.js
@@ -1,16 +1,13 @@
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  */
 
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
-
 // bug 953111
 
 var A = TypedObject.uint8.array(0);
 var a = new A();
 a.forEach(function(val, i) {});
 
 // bug 951356 (dup, but a dup that is more likely to crash)
 
--- a/js/src/jit-test/tests/TypedObject/bug959119.js
+++ b/js/src/jit-test/tests/TypedObject/bug959119.js
@@ -1,19 +1,16 @@
 // This test exposed a bug in float32 optimization.
 // The (inlined and optimized) code for `add()` created
 // MDiv instructions specialized to integers, which was
 // then "respecialized" to float32, leading to internal
 // assertion errors.
 //
 // Public domain.
 
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
-
 var {StructType,uint8,float32} = TypedObject;
 var RgbColor2 = new StructType({r: uint8, g: float32, b: uint8});
 RgbColor2.prototype.add = function(c) {
   this.g += c;
   this.b += c;
 };
 var gray = new RgbColor2({r: 129, g: 128, b: 127});
 gray.add(1);
--- a/js/src/jit-test/tests/TypedObject/bug969159.js
+++ b/js/src/jit-test/tests/TypedObject/bug969159.js
@@ -1,9 +1,6 @@
 // Test access to a 0-sized element (in this case,
 // a zero-length array).
 
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
-
 var AA = TypedObject.uint8.array(0.).array(5);
 var aa = new AA();
 var aa0 = aa[0];
--- a/js/src/jit-test/tests/TypedObject/bug970285.js
+++ b/js/src/jit-test/tests/TypedObject/bug970285.js
@@ -1,11 +1,8 @@
 // |jit-test| error:TypeError
 
-if (!this.hasOwnProperty("TypedObject"))
-  throw new TypeError();
-
 // Test that we detect invalid lengths supplied to unsized array
 // constructor. Public domain.
 
 var AA = TypedObject.uint8.array(2147483647).array();
 var aa = new AA(-1);
 
--- a/js/src/jit-test/tests/TypedObject/bug973563.js
+++ b/js/src/jit-test/tests/TypedObject/bug973563.js
@@ -1,9 +1,6 @@
 // Test that empty sized structs don't trigger any assertion failures.
 // Public domain.
 
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
-
 var PointType = new TypedObject.StructType({});
 var LineType = new TypedObject.StructType({source: PointType, target: PointType});
 var fromAToB = new LineType({source: {x: 22, y: 44}, target: {x: 66, y: 88}});
--- a/js/src/jit-test/tests/TypedObject/bug976530.js
+++ b/js/src/jit-test/tests/TypedObject/bug976530.js
@@ -1,10 +1,7 @@
 // |jit-test| error:Error
 
 // Test that we don't permit structs whose fields exceed 32 bits.
 
-if (!this.hasOwnProperty("TypedObject"))
-  throw new Error();
-
 var Vec3u16Type = TypedObject.uint16.array((1073741823));
 var PairVec3u16Type = new TypedObject.StructType({ fst: Vec3u16Type, snd: Vec3u16Type });
 new PairVec3u16Type();
--- a/js/src/jit-test/tests/TypedObject/common-array-prototypes.js
+++ b/js/src/jit-test/tests/TypedObject/common-array-prototypes.js
@@ -1,12 +1,8 @@
-
-if (typeof TypedObject === "undefined")
-    quit();
-
 // Test the relationships between prototypes for array typed objects.
 
 var arrA = new TypedObject.ArrayType(TypedObject.int32, 10);
 var arrB = new TypedObject.ArrayType(TypedObject.int32, 20);
 var arrC = new TypedObject.ArrayType(TypedObject.int8, 10);
 
 assertEq(arrA.prototype == arrB.prototype, true);
 assertEq(arrA.prototype == arrC.prototype, false);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/TypedObject/directives.txt
@@ -0,0 +1,1 @@
+|jit-test| skip-if: !this.hasOwnProperty("TypedObject")
--- a/js/src/jit-test/tests/TypedObject/function-names.js
+++ b/js/src/jit-test/tests/TypedObject/function-names.js
@@ -1,7 +1,3 @@
-
-if (typeof TypedObject === "undefined")
-    quit();
-
 // Make sure some builtin TypedObject functions are given sensible names.
 assertEq(TypedObject.ArrayType.name, "ArrayType");
 assertEq(TypedObject.StructType.name, "StructType");
--- a/js/src/jit-test/tests/TypedObject/fuzz1.js
+++ b/js/src/jit-test/tests/TypedObject/fuzz1.js
@@ -1,10 +1,7 @@
 // |jit-test| error:RangeError
 
-if (!this.hasOwnProperty("TypedObject"))
-  throw new RangeError();
-
 function* eval() {
     yield(undefined)
 }
 new TypedObject.StructType();
 eval();
--- a/js/src/jit-test/tests/TypedObject/fuzz10.js
+++ b/js/src/jit-test/tests/TypedObject/fuzz10.js
@@ -1,8 +1,5 @@
 // |jit-test| error:Error
 
-if (!this.hasOwnProperty("TypedObject"))
-  throw new Error("type too large");
-
 var AA = TypedObject.uint8.array(2147483647).array(5);
 var aa = new AA();
 var aa0 = aa[0];
--- a/js/src/jit-test/tests/TypedObject/fuzz11.js
+++ b/js/src/jit-test/tests/TypedObject/fuzz11.js
@@ -1,13 +1,10 @@
 // |jit-test| error:Error
 
-if (!this.hasOwnProperty("TypedObject"))
-  throw new Error("type too large");
-
 var A = TypedObject.uint8.array(2147483647);
 var S = new TypedObject.StructType({a: A,
                                     b: A,
                                     c: A,
                                     d: A,
                                     e: A});
 var aa = new S();
 var aa0 = aa.a;
--- a/js/src/jit-test/tests/TypedObject/fuzz2.js
+++ b/js/src/jit-test/tests/TypedObject/fuzz2.js
@@ -1,4 +1,1 @@
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
-
 new TypedObject.StructType([]);
--- a/js/src/jit-test/tests/TypedObject/fuzz4.js
+++ b/js/src/jit-test/tests/TypedObject/fuzz4.js
@@ -1,10 +1,7 @@
 // |jit-test| error:Error;
 
-if (!this.hasOwnProperty("TypedObject"))
-  throw new Error();
-
 var A = TypedObject.uint8.array(10);
 var a = new A();
 a.forEach(function(val, i) {
   assertEq(arguments[5], a);
 });
--- a/js/src/jit-test/tests/TypedObject/fuzz5.js
+++ b/js/src/jit-test/tests/TypedObject/fuzz5.js
@@ -1,8 +1,5 @@
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
-
 var Color = new TypedObject.StructType({r: TypedObject.uint8,
                                         g: TypedObject.uint8,
                                         b: TypedObject.uint8});
 var white2 = new Color({r: 255, toString: null, b: 253});
 
--- a/js/src/jit-test/tests/TypedObject/fuzz6.js
+++ b/js/src/jit-test/tests/TypedObject/fuzz6.js
@@ -1,6 +1,3 @@
 // |jit-test| error:TypeError
 
-if (!this.hasOwnProperty("TypedObject"))
-  throw new TypeError();
-
 new TypedObject.StructType(RegExp);
--- a/js/src/jit-test/tests/TypedObject/fuzz7.js
+++ b/js/src/jit-test/tests/TypedObject/fuzz7.js
@@ -1,4 +1,1 @@
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
-
 new TypedObject.StructType(RegExp());
--- a/js/src/jit-test/tests/TypedObject/fuzz8.js
+++ b/js/src/jit-test/tests/TypedObject/fuzz8.js
@@ -1,6 +1,2 @@
 // |jit-test| error:Error
-
-if (!this.hasOwnProperty("TypedObject"))
-  throw new Error();
-
 TypedObject.uint8.array(.0000000009);
--- a/js/src/jit-test/tests/TypedObject/fuzz9.js
+++ b/js/src/jit-test/tests/TypedObject/fuzz9.js
@@ -1,10 +1,6 @@
 // |jit-test| error: TypeError
-
-if (!this.hasOwnProperty("TypedObject"))
-  throw new TypeError();
-
 var Vec3 = TypedObject.float32.array(3);
 var Sprite = Vec3.array(3);
 var mario = new Sprite();
 mario[/\u00ee[]/] = new Vec3([1, 0, 0]);
 
--- a/js/src/jit-test/tests/TypedObject/inlineopaque.js
+++ b/js/src/jit-test/tests/TypedObject/inlineopaque.js
@@ -1,11 +1,8 @@
-if (!this.hasOwnProperty("TypedObject"))
-    quit();
-
 var TO = TypedObject;
 
 var PointType = new TO.StructType({x: TO.float64, y: TO.float64, name:TO.string});
 var LineType = new TO.StructType({from: PointType, to: PointType});
 
 function testBasic(gc) {
     var line = new LineType();
     var from = line.from;
--- a/js/src/jit-test/tests/TypedObject/jit-complex.js
+++ b/js/src/jit-test/tests/TypedObject/jit-complex.js
@@ -1,14 +1,11 @@
 // Test that we can optimize stuff like line.target.x without
 // creating an intermediate object.
 
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
-
 setJitCompilerOption("ion.warmup.trigger", 30);
 
 var PointType = new TypedObject.StructType({x: TypedObject.float64,
                                             y: TypedObject.float64});
 var LineType = new TypedObject.StructType({source: PointType,
                                            target: PointType});
 
 function manhattanDistance(line) {
--- a/js/src/jit-test/tests/TypedObject/jit-prefix.js
+++ b/js/src/jit-test/tests/TypedObject/jit-prefix.js
@@ -1,11 +1,8 @@
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
-
 setJitCompilerOption("ion.warmup.trigger", 30);
 
 var PointType2 =
   new TypedObject.StructType({
     x: TypedObject.float64,
     y: TypedObject.float64});
 
 var PointType3 =
--- a/js/src/jit-test/tests/TypedObject/jit-read-float64.js
+++ b/js/src/jit-test/tests/TypedObject/jit-read-float64.js
@@ -1,11 +1,8 @@
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
-
 var PointType = new TypedObject.StructType({x: TypedObject.float64,
                                             y: TypedObject.float64,
                                             z: TypedObject.float64});
 
 function foo() {
   for (var i = 0; i < 30000; i += 3) {
     var pt = new PointType({x: i, y: i+1, z: i+2});
     var sum = pt.x + pt.y + pt.z;
--- a/js/src/jit-test/tests/TypedObject/jit-read-int.js
+++ b/js/src/jit-test/tests/TypedObject/jit-read-int.js
@@ -1,11 +1,8 @@
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
-
 var PointType = new TypedObject.StructType({x: TypedObject.uint32,
                                             y: TypedObject.uint32,
                                             z: TypedObject.uint32});
 
 function foo() {
   for (var i = 0; i < 30000; i += 3) {
     var pt = new PointType({x: i, y: i+1, z: i+2});
     var sum = pt.x + pt.y + pt.z;
--- a/js/src/jit-test/tests/TypedObject/jit-read-many.js
+++ b/js/src/jit-test/tests/TypedObject/jit-read-many.js
@@ -1,16 +1,13 @@
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  */
 
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
-
 // Test a single function (`bar`) being used with arrays that are all
 // of known length, but not the same length.
 
 setJitCompilerOption("ion.warmup.trigger", 30);
 
 var N0 = 50;
 var N1 = 100;
 var N2 = 150;
--- a/js/src/jit-test/tests/TypedObject/jit-read-u16-from-mdim-array.js
+++ b/js/src/jit-test/tests/TypedObject/jit-read-u16-from-mdim-array.js
@@ -1,16 +1,13 @@
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  */
 
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
-
 var T = TypedObject;
 var PointType = T.uint16.array(3);
 var VecPointType = PointType.array(3);
 
 function foo() {
   for (var i = 0; i < 10000; i += 9) {
     var vec = new VecPointType([
       [i,   i+1, i+2],
--- a/js/src/jit-test/tests/TypedObject/jit-read-u16-from-struct-array-in-struct.js
+++ b/js/src/jit-test/tests/TypedObject/jit-read-u16-from-struct-array-in-struct.js
@@ -1,16 +1,13 @@
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  */
 
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
-
 var PointType = new TypedObject.StructType({x: TypedObject.uint16,
                                             y: TypedObject.uint16,
                                             z: TypedObject.uint16});
 
 var VecPointType = PointType.array(3);
 
 var PairVecType = new TypedObject.StructType({fst: VecPointType,
                                               snd: VecPointType});
--- a/js/src/jit-test/tests/TypedObject/jit-read-u16-from-struct-array.js
+++ b/js/src/jit-test/tests/TypedObject/jit-read-u16-from-struct-array.js
@@ -1,16 +1,13 @@
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  */
 
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
-
 var PointType = new TypedObject.StructType({x: TypedObject.uint16,
                                             y: TypedObject.uint16,
                                             z: TypedObject.uint16});
 
 var VecPointType = PointType.array(3);
 
 function foo() {
   for (var i = 0; i < 10000; i += 9) {
--- a/js/src/jit-test/tests/TypedObject/jit-read-u16-from-u16-array-in-struct.js
+++ b/js/src/jit-test/tests/TypedObject/jit-read-u16-from-u16-array-in-struct.js
@@ -1,16 +1,13 @@
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  */
 
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
-
 var Vec3u16Type = TypedObject.uint16.array(3);
 var PairVec3u16Type = new TypedObject.StructType({fst: Vec3u16Type,
                                                   snd: Vec3u16Type});
 
 function foo_u16() {
   for (var i = 0; i < 15000; i += 6) {
     var p = new PairVec3u16Type({fst: [i, i+1, i+2],
                                  snd: [i+3,i+4,i+5]});
--- a/js/src/jit-test/tests/TypedObject/jit-read-u16-from-u16-array.js
+++ b/js/src/jit-test/tests/TypedObject/jit-read-u16-from-u16-array.js
@@ -1,16 +1,13 @@
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  */
 
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
-
 var Vec3u16Type = TypedObject.uint16.array(3);
 
 function foo_u16() {
   for (var i = 0; i < 30000; i += 3) {
     var vec = new Vec3u16Type([i, i+1, i+2]);
     var sum = vec[0] + vec[1] + vec[2];
     assertEq(sum, 3*i + 3);
   }
--- a/js/src/jit-test/tests/TypedObject/jit-read-u32-from-mdim-array.js
+++ b/js/src/jit-test/tests/TypedObject/jit-read-u32-from-mdim-array.js
@@ -1,16 +1,13 @@
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  */
 
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
-
 var PointType = TypedObject.uint32.array(3);
 var VecPointType = PointType.array(3);
 
 function foo() {
   for (var i = 0; i < 10000; i += 9) {
     var vec = new VecPointType([
       [i,   i+1, i+2],
       [i+3, i+4, i+5],
--- a/js/src/jit-test/tests/TypedObject/jit-read-u32-from-struct-array-in-struct.js
+++ b/js/src/jit-test/tests/TypedObject/jit-read-u32-from-struct-array-in-struct.js
@@ -1,16 +1,13 @@
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  */
 
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
-
 var PointType = new TypedObject.StructType({x: TypedObject.uint32,
                                             y: TypedObject.uint32,
                                             z: TypedObject.uint32});
 
 var VecPointType = PointType.array(3);
 
 var PairVecType = new TypedObject.StructType({fst: VecPointType,
                                               snd: VecPointType});
--- a/js/src/jit-test/tests/TypedObject/jit-read-u32-from-struct-array.js
+++ b/js/src/jit-test/tests/TypedObject/jit-read-u32-from-struct-array.js
@@ -1,16 +1,13 @@
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  */
 
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
-
 var PointType = new TypedObject.StructType({x: TypedObject.uint32,
                                             y: TypedObject.uint32,
                                             z: TypedObject.uint32});
 
 var VecPointType = PointType.array(3);
 
 function foo() {
   for (var i = 0; i < 10000; i += 9) {
--- a/js/src/jit-test/tests/TypedObject/jit-read-u32-from-u32-array-in-struct.js
+++ b/js/src/jit-test/tests/TypedObject/jit-read-u32-from-u32-array-in-struct.js
@@ -1,16 +1,13 @@
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  */
 
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
-
 var Vec3u32Type = TypedObject.uint32.array(3);
 var PairVec3u32Type = new TypedObject.StructType({fst: Vec3u32Type,
                                                   snd: Vec3u32Type});
 
 function foo_u32() {
   for (var i = 0; i < 15000; i += 6) {
     var p = new PairVec3u32Type({fst: [i, i+1, i+2],
                                  snd: [i+3,i+4,i+5]});
--- a/js/src/jit-test/tests/TypedObject/jit-read-u32-from-u32-array.js
+++ b/js/src/jit-test/tests/TypedObject/jit-read-u32-from-u32-array.js
@@ -1,16 +1,13 @@
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  */
 
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
-
 var Vec3u32Type = TypedObject.uint32.array(3);
 
 function foo_u32() {
   for (var i = 0; i < 30000; i += 3) {
     var vec = new Vec3u32Type([i, i+1, i+2]);
     var sum = vec[0] + vec[1] + vec[2];
     assertEq(sum, 3*i + 3);
   }
--- a/js/src/jit-test/tests/TypedObject/jit-read-unsized.js
+++ b/js/src/jit-test/tests/TypedObject/jit-read-unsized.js
@@ -1,16 +1,13 @@
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  */
 
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
-
 setJitCompilerOption("ion.warmup.trigger", 30);
 
 var N = 100;
 var T = TypedObject;
 var Point = new T.StructType({x: T.uint32, y: T.uint32, z: T.uint32});
 var PointArray = Point.array(N);
 
 function bar(array, i, x, y, z) {
--- a/js/src/jit-test/tests/TypedObject/jit-write-references-2.js
+++ b/js/src/jit-test/tests/TypedObject/jit-write-references-2.js
@@ -1,11 +1,9 @@
-if (typeof TypedObject === "undefined" || typeof Intl === "undefined")
-    quit();
-
+// |jit-test| skip-if: typeof Intl === 'undefined'
 try {
     gczeal(4)
 } catch (exc) {}
 var T = TypedObject;
 var ValueStruct = new T.StructType({
     f: T.Any
 })
 var v = new ValueStruct;
--- a/js/src/jit-test/tests/TypedObject/jit-write-references.js
+++ b/js/src/jit-test/tests/TypedObject/jit-write-references.js
@@ -1,11 +1,8 @@
-if (typeof TypedObject === "undefined")
-    quit();
-
 var T = TypedObject;
 
 var ObjectStruct = new T.StructType({f: T.Object});
 var StringStruct = new T.StructType({f: T.string});
 var ValueStruct = new T.StructType({f: T.Any});
 
 // Suppress ion compilation of the global script.
 with({}){}
--- a/js/src/jit-test/tests/TypedObject/jit-write-u16-into-mdim-array.js
+++ b/js/src/jit-test/tests/TypedObject/jit-write-u16-into-mdim-array.js
@@ -1,16 +1,13 @@
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  */
 
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
-
 setJitCompilerOption("ion.warmup.trigger", 30);
 
 var PointType = TypedObject.uint16.array(3);
 var VecPointType = PointType.array(3);
 
 function foo() {
   for (var i = 0; i < 5000; i += 10) {
     var vec = new VecPointType();
--- a/js/src/jit-test/tests/TypedObject/jit-write-u16-into-u16-array-in-struct.js
+++ b/js/src/jit-test/tests/TypedObject/jit-write-u16-into-u16-array-in-struct.js
@@ -1,16 +1,13 @@
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  */
 
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
-
 setJitCompilerOption("ion.warmup.trigger", 30);
 
 var Vec3u16Type = TypedObject.uint16.array(3);
 var PairVec3u16Type = new TypedObject.StructType({fst: Vec3u16Type,
                                                   snd: Vec3u16Type});
 
 function foo_u16() {
   for (var i = 0; i < 5000; i += 10) {
--- a/js/src/jit-test/tests/TypedObject/jit-write-u16-into-u16-array.js
+++ b/js/src/jit-test/tests/TypedObject/jit-write-u16-into-u16-array.js
@@ -1,16 +1,13 @@
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  */
 
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
-
 setJitCompilerOption("ion.warmup.trigger", 30);
 
 var Vec3u16Type = TypedObject.uint16.array(3);
 
 function foo_u16() {
   for (var i = 0; i < 5000; i += 10) {
     var vec = new Vec3u16Type();
     // making index non-trivially dependent on |i| to foil compiler optimization
--- a/js/src/jit-test/tests/TypedObject/jit-write-u32-into-mdim-array.js
+++ b/js/src/jit-test/tests/TypedObject/jit-write-u32-into-mdim-array.js
@@ -1,16 +1,13 @@
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  */
 
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
-
 setJitCompilerOption("ion.warmup.trigger", 30);
 
 var PointType = TypedObject.uint32.array(3);
 var VecPointType = PointType.array(3);
 
 function foo() {
   for (var i = 0; i < 5000; i += 10) {
     var vec = new VecPointType();
--- a/js/src/jit-test/tests/TypedObject/jit-write-u32-into-u32-array-in-struct.js
+++ b/js/src/jit-test/tests/TypedObject/jit-write-u32-into-u32-array-in-struct.js
@@ -1,16 +1,13 @@
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  */
 
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
-
 setJitCompilerOption("ion.warmup.trigger", 30);
 
 var Vec3u32Type = TypedObject.uint32.array(3);
 var PairVec3u32Type = new TypedObject.StructType({fst: Vec3u32Type,
                                                   snd: Vec3u32Type});
 
 function foo_u32() {
   for (var i = 0; i < 5000; i += 10) {
--- a/js/src/jit-test/tests/TypedObject/jit-write-u32-into-u32-array.js
+++ b/js/src/jit-test/tests/TypedObject/jit-write-u32-into-u32-array.js
@@ -1,16 +1,13 @@
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  */
 
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
-
 setJitCompilerOption("ion.warmup.trigger", 30);
 
 var Vec3u32Type = TypedObject.uint32.array(3);
 
 function foo_u32() {
   for (var i = 0; i < 5000; i += 10) {
     var vec = new Vec3u32Type();
     // making index non-trivially dependent on |i| to foil compiler optimization
--- a/js/src/jit-test/tests/TypedObject/neutertypedobj.js
+++ b/js/src/jit-test/tests/TypedObject/neutertypedobj.js
@@ -1,11 +1,8 @@
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
-
 var {StructType, uint32} = TypedObject;
 var S = new StructType({f: uint32, g: uint32});
 
 function readFromS(s) {
   return s.f + s.g;
 }
 
 function main() {
--- a/js/src/jit-test/tests/TypedObject/neutertypedobjsizedarray.js
+++ b/js/src/jit-test/tests/TypedObject/neutertypedobjsizedarray.js
@@ -1,16 +1,13 @@
 // Test the case where we detach the buffer underlying a fixed-sized array.
 // This is a bit of a tricky case because we cannot (necessarily) fold
 // the detached check into the bounds check, as we obtain the bounds
 // directly from the type.
 
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
-
 var {StructType, uint32} = TypedObject;
 var S = new StructType({f: uint32, g: uint32});
 var A = S.array(10);
 
 function readFrom(a) {
   return a[2].f + a[2].g;
 }
 
--- a/js/src/jit-test/tests/TypedObject/neutertypedobjunsizedarray.js
+++ b/js/src/jit-test/tests/TypedObject/neutertypedobjunsizedarray.js
@@ -1,14 +1,11 @@
 // Test the case where we detach the buffer underlying a variable-length array.
 // Here we can fold the detached check into the bounds check.
 
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
-
 var {StructType, uint32} = TypedObject;
 var S = new StructType({f: uint32, g: uint32});
 var A = S.array(10);
 
 function readFrom(a) {
   return a[2].f + a[2].g;
 }
 
--- a/js/src/jit-test/tests/TypedObject/prototypes.js
+++ b/js/src/jit-test/tests/TypedObject/prototypes.js
@@ -1,15 +1,12 @@
 // API Surface Test: check that mutating prototypes
 // of type objects has no effect, and that mutating
 // the prototypes of typed objects is an error.
 
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
-
 load(libdir + "asserts.js");
 
 var {StructType, uint32, Object, Any, objectType} = TypedObject;
 
 function main() { // once a C programmer, always a C programmer.
   var Uints = new StructType({f: uint32, g: uint32});
   var p = Uints.prototype;
   Uints.prototype = {}; // no effect
--- a/js/src/jit-test/tests/TypedObject/set-property-with-prototype.js
+++ b/js/src/jit-test/tests/TypedObject/set-property-with-prototype.js
@@ -1,12 +1,8 @@
-
-if (typeof TypedObject === "undefined")
-    quit();
-
 // Test the behavior of property sets on typed objects when they are a
 // prototype or their prototype has a setter.
 var TO = TypedObject;
 
 function assertThrows(fun, errorType) {
   try {
     fun();
     assertEq(true, false, "Expected error, but none was thrown");
--- a/js/src/jit-test/tests/asm.js/bug1161298.js
+++ b/js/src/jit-test/tests/asm.js/bug1161298.js
@@ -1,12 +1,11 @@
-// The length exceeds INT32_MAX and should be rejected.
+// |jit-test| skip-if: !this.SharedArrayBuffer
 
-if (!this.SharedArrayBuffer)
-    quit(0);
+// The length exceeds INT32_MAX and should be rejected.
 
 var failed = false;
 try {
     var sab = new SharedArrayBuffer((2147483648));
 }
 catch (e) {
     failed = true;
 }
--- a/js/src/jit-test/tests/asm.js/bug1219954.js
+++ b/js/src/jit-test/tests/asm.js/bug1219954.js
@@ -1,13 +1,10 @@
-// |jit-test| slow
+// |jit-test| slow; skip-if: !('oomTest' in this)
 "use strict";
 
-if (!('oomTest' in this))
-    quit();
-
 let g = (function() {
     "use asm";
     function f() {}
     return f;
 })();
 
 oomTest(() => "" + g);
--- a/js/src/jit-test/tests/asm.js/bug1385428.js
+++ b/js/src/jit-test/tests/asm.js/bug1385428.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 loadFile(`
 try {
   Array.prototype.splice.call({ get length() {
     "use asm"
     function f() {}
     return f;
 } });
--- a/js/src/jit-test/tests/asm.js/bug1421565.js
+++ b/js/src/jit-test/tests/asm.js/bug1421565.js
@@ -1,18 +1,12 @@
-// |jit-test| --ion-offthread-compile=off
+// |jit-test| --ion-offthread-compile=off; skip-if: !isAsmJSCompilationAvailable() || !('oomTest' in this)
 
 load(libdir + "asm.js");
 
-if (!isAsmJSCompilationAvailable())
-    quit(0);
-
-if (!('oomTest' in this))
-    quit();
-
 oomTest(
   function() {
     eval(`
       function f(stdlib, foreign, buffer) {
         "use asm";
          var i32 = new stdlib.Int32Array(buffer);
          function set(v) {
            v=v|0;
--- a/js/src/jit-test/tests/asm.js/bug1493475.js
+++ b/js/src/jit-test/tests/asm.js/bug1493475.js
@@ -1,9 +1,8 @@
-if (helperThreadCount() == 0)
-    quit();
+// |jit-test| skip-if: helperThreadCount() === 0
 
 offThreadCompileScript("\
     (function(stdlib, foreign) {\
         \"use asm\";\
         function() {};\
     })();\
 ");
--- a/js/src/jit-test/tests/asm.js/oom-helper-thread-plus-validation-error.js
+++ b/js/src/jit-test/tests/asm.js/oom-helper-thread-plus-validation-error.js
@@ -1,10 +1,9 @@
-if (typeof oomAfterAllocations !== 'function' || typeof evaluate !== 'function')
-    quit();
+// |jit-test| skip-if: !('oomAfterAllocations' in this)
 
 oomAfterAllocations(10, 2);
 evaluate(`function mod(stdlib, ffi, heap) {
     "use asm";
     function f3(k) {
         k = k | 0;
     }
     function g3(k) {}
--- a/js/src/jit-test/tests/asm.js/oom-helper-thread.js
+++ b/js/src/jit-test/tests/asm.js/oom-helper-thread.js
@@ -1,8 +1,4 @@
-// |jit-test| exitstatus: 3
-
-if (typeof oomAfterAllocations !== 'function') {
-    quit(3);
-}
+// |jit-test| exitstatus: 3; skip-if: !('oomAfterAllocations' in this)
 
 oomAfterAllocations(50, 2);
 eval("(function() {'use asm'; function f() { return +pow(.0, .0) })")
--- a/js/src/jit-test/tests/asm.js/testBug1057248.js
+++ b/js/src/jit-test/tests/asm.js/testBug1057248.js
@@ -1,10 +1,9 @@
-if (!this.SharedArrayBuffer || !isAsmJSCompilationAvailable())
-  quit();
+// |jit-test| skip-if: !this.SharedArrayBuffer || !isAsmJSCompilationAvailable()
 
 Random = {
     weighted: function(wa) {
         var a = [];
         for (var i = 0; i < wa.length; ++i) {
             for (var j = 0; j < wa[i]; ++j) {}
         }
     }
--- a/js/src/jit-test/tests/asm.js/testBug1111327.js
+++ b/js/src/jit-test/tests/asm.js/testBug1111327.js
@@ -1,11 +1,10 @@
-load(libdir + "asm.js");
-
+// |jit-test| skip-if: !getBuildConfiguration()['arm-simulator']
 // Single-step profiling currently only works in the ARM simulator
-if (!getBuildConfiguration()["arm-simulator"])
-    quit();
+
+load(libdir + "asm.js");
 
 enableGeckoProfiling();
 enableSingleStepProfiling();
 var m = asmCompile(USE_ASM + 'function f() {} return f');
 asmLink(m)();
 asmLink(m)();
--- a/js/src/jit-test/tests/asm.js/testBug1117235.js
+++ b/js/src/jit-test/tests/asm.js/testBug1117235.js
@@ -1,8 +1,7 @@
-load(libdir + "asserts.js");
+// |jit-test| skip-if: helperThreadCount() === 0
 
-if (helperThreadCount() === 0)
-  quit(0);
+load(libdir + "asserts.js");
 
 options('werror');
 offThreadCompileScript("function f() {'use asm'}");
 assertThrowsInstanceOf(()=>runOffThreadScript(), TypeError);
--- a/js/src/jit-test/tests/asm.js/testBug1147144.js
+++ b/js/src/jit-test/tests/asm.js/testBug1147144.js
@@ -1,14 +1,13 @@
+// |jit-test| skip-if: !isAsmJSCompilationAvailable()
+
 load(libdir + 'asm.js');
 load(libdir + 'asserts.js');
 
-if (!isAsmJSCompilationAvailable())
-    quit();
-
 setDiscardSource(true)
 assertEq(eval(`(function asmModule() { "use asm"; function asmFun() {} return asmFun })`).toString(), "function asmModule() {\n    [native code]\n}");
 assertEq(eval(`(function asmModule() { "use asm"; function asmFun() {} return asmFun })`).toSource(), "(function asmModule() {\n    [native code]\n})");
 assertEq(eval(`(function asmModule() { "use asm"; function asmFun() {} return asmFun })`)().toString(), "function asmFun() {\n    [native code]\n}");
 assertEq(eval(`(function asmModule() { "use asm"; function asmFun() {} return asmFun })`)().toSource(), "function asmFun() {\n    [native code]\n}");
 assertEq((evaluate(`function asmModule1() { "use asm"; function asmFun() {} return asmFun }`), asmModule1).toString(), "function asmModule1() {\n    [native code]\n}");
 assertEq((evaluate(`function asmModule2() { "use asm"; function asmFun() {} return asmFun }`), asmModule2).toSource(), "function asmModule2() {\n    [native code]\n}");
 assertEq((evaluate(`function asmModule3() { "use asm"; function asmFun() {} return asmFun }`), asmModule3)().toString(), "function asmFun() {\n    [native code]\n}");
--- a/js/src/jit-test/tests/asm.js/testBug1255954.js
+++ b/js/src/jit-test/tests/asm.js/testBug1255954.js
@@ -1,13 +1,11 @@
-// |jit-test| slow
+// |jit-test| slow; skip-if: !('oomTest' in this)
 
 const USE_ASM = '"use asm";';
-if (!('oomTest' in this))
-    quit();
 function asmCompile() {
     var f = Function.apply(null, arguments);
 }
 oomTest(() => {
     try {
         function f(b) {}
     } catch (exc0) {}
     f(asmCompile(USE_ASM + "function f() { var i=42; return i|0; for(;1;) {} return 0 } return f"));
--- a/js/src/jit-test/tests/asm.js/testBug893519.js
+++ b/js/src/jit-test/tests/asm.js/testBug893519.js
@@ -1,11 +1,6 @@
-// |jit-test| error:Error
-
-if (!isAsmJSCompilationAvailable()) {
-    throw new Error('this test expects an error to be thrown, here it is');
-    quit();
-}
+// |jit-test| error:Error; skip-if: !isAsmJSCompilationAvailable()
 
 var g = newGlobal();
 evaluate("function h() { function f() { 'use asm'; function g() { return 42 } return g } return f }", { global:g});
 var h = clone(g.h);
 assertEq(h()()(), 42);
--- a/js/src/jit-test/tests/asm.js/testBullet.js
+++ b/js/src/jit-test/tests/asm.js/testBullet.js
@@ -1,15 +1,15 @@
+// |jit-test| skip-if: !isAsmJSCompilationAvailable()
+
 // Test a big fat asm.js module. First load/compile/cache bullet.js in a
 // separate process and then load it again in this process, which should be a
 // cache hit.
 
 setCachingEnabled(true);
-if (!isAsmJSCompilationAvailable())
-    quit();
 
 // Note: if you get some failure in this test, it probably has to do with
 // bullet.js and not the nestedShell() call, so try first commenting out
 // nestedShell() (and the loadedFromCache assertion) to see if the error
 // reproduces.
 var code = "setIonCheckGraphCoherency(false); setCachingEnabled(true); load('" + libdir + "bullet.js'); runBullet()";
 nestedShell("--js-cache", "--no-js-cache-per-process", "--execute=" + code);
 setIonCheckGraphCoherency(false);
--- a/js/src/jit-test/tests/asm.js/testCaching.js
+++ b/js/src/jit-test/tests/asm.js/testCaching.js
@@ -1,12 +1,14 @@
+// |jit-test| skip-if: !isAsmJSCompilationAvailable()
+
 load(libdir + "asm.js");
 
 setCachingEnabled(true);
-if (!isAsmJSCompilationAvailable() || !isCachingEnabled())
+if (!isCachingEnabled())
     quit();
 
 var body1 = "'use asm'; function f() { return 42 } function ff() { return 43 } return f";
 var m = new Function(body1);
 assertEq(isAsmJSModule(m), true);
 assertEq(m()(), 42);
 var m = new Function(body1);
 assertEq(isAsmJSModuleLoadedFromCache(m), true);
--- a/js/src/jit-test/tests/asm.js/testNeuter.js
+++ b/js/src/jit-test/tests/asm.js/testNeuter.js
@@ -1,14 +1,13 @@
+// |jit-test| skip-if: !isAsmJSCompilationAvailable()
+
 load(libdir + "asm.js");
 load(libdir + "asserts.js");
 
-if (!isAsmJSCompilationAvailable())
-    quit();
-
 var m = asmCompile('stdlib', 'foreign', 'buffer',
                   `"use asm";
                    var i32 = new stdlib.Int32Array(buffer);
                    function set(i,j) {
                        i=i|0;
                        j=j|0;
                        i32[i>>2] = j;
                    }
--- a/js/src/jit-test/tests/asm.js/testParallelCompile.js
+++ b/js/src/jit-test/tests/asm.js/testParallelCompile.js
@@ -1,26 +1,20 @@
-load(libdir + "asm.js");
+// |jit-test| skip-if: !isAsmJSCompilationAvailable() || helperThreadCount() === 0
 
-if (!isAsmJSCompilationAvailable())
-    quit();
+load(libdir + "asm.js");
 
 var module = "'use asm';\n";
 for (var i = 0; i < 100; i++) {
     module += "function f" + i + "(i) {\n";
     module += "  i=i|0; var j=0; j=(i+1)|0; i=(j-4)|0; i=(i+j)|0; return i|0\n";
     module += "}\n";
 }
 module += "return f0";
 var script = "(function() {\n" + module + "})";
 
 for (var i = 0; i < 10; i++) {
-    try {
-        offThreadCompileScript(script);
-        var f = new Function(module);
-        var g = runOffThreadScript();
-        assertEq(isAsmJSModule(f), true);
-        assertEq(isAsmJSModule(g), true);
-    } catch (e) {
-        // ignore spurious error when offThreadCompileScript can't run in
-        // parallel
-    }
+    offThreadCompileScript(script);
+    var f = new Function(module);
+    var g = runOffThreadScript();
+    assertEq(isAsmJSModule(f), true);
+    assertEq(isAsmJSModule(g), true);
 }
--- a/js/src/jit-test/tests/asm.js/testProfiling.js
+++ b/js/src/jit-test/tests/asm.js/testProfiling.js
@@ -1,19 +1,14 @@
+// |jit-test| skip-if: !isAsmJSCompilationAvailable() || !getBuildConfiguration()['arm-simulator']
+// Single-step profiling currently only works in the ARM simulator
+
 load(libdir + "asm.js");
 load(libdir + "asserts.js");
 
-// Run test only for asm.js
-if (!isAsmJSCompilationAvailable())
-    quit();
-
-// Single-step profiling currently only works in the ARM simulator
-if (!getBuildConfiguration()["arm-simulator"])
-    quit();
-
 function checkSubSequence(got, expect)
 {
     var got_i = 0;
     EXP: for (var exp_i = 0; exp_i < expect.length; exp_i++) {
         var item = expect[exp_i];
         // Scan for next match in got.
         while (got_i < got.length) {
             if (got[got_i++] == expect[exp_i])
--- a/js/src/jit-test/tests/asm.js/testStealing.js
+++ b/js/src/jit-test/tests/asm.js/testStealing.js
@@ -1,14 +1,13 @@
+// |jit-test| skip-if: !isAsmJSCompilationAvailable()
+
 load(libdir + "asm.js");
 load(libdir + "asserts.js");
 
-if (!isAsmJSCompilationAvailable())
-    quit();
-
 var code = USE_ASM + "var i32 = new stdlib.Int32Array(buf); function f() { return i32[0]|0 } return f";
 
 var ab = new ArrayBuffer(BUF_MIN);
 new Int32Array(ab)[0] = 42;
 
 var f = asmLink(asmCompile('stdlib', 'ffi', 'buf', code), this, null, ab);
 assertEq(f(), 42);
 
--- a/js/src/jit-test/tests/asm.js/testZOOB.js
+++ b/js/src/jit-test/tests/asm.js/testZOOB.js
@@ -1,17 +1,14 @@
-// |jit-test|
+// |jit-test| skip-if: !isAsmJSCompilationAvailable()
 load(libdir + "asm.js");
 load(libdir + "asserts.js");
 
-if (!isAsmJSCompilationAvailable())
-    quit();
-
 // This test runs a lot of code and is very slow with --ion-eager. Use a minimum
-// Ion warmup trigger of 2 to avoid timeouts.
+// Ion warmup trigger of 5 to avoid timeouts.
 if (getJitCompilerOptions()["ion.warmup.trigger"] < 5)
     setJitCompilerOption("ion.warmup.trigger", 5);
 
 var ab = new ArrayBuffer(BUF_MIN);
 
 // Compute a set of interesting indices.
 indices = [0]
 for (var i of [4,1024,BUF_MIN,Math.pow(2,30),Math.pow(2,31),Math.pow(2,32),Math.pow(2,33)]) {
--- a/js/src/jit-test/tests/atomics/basic-tests.js
+++ b/js/src/jit-test/tests/atomics/basic-tests.js
@@ -555,10 +555,9 @@ function runTests() {
     // Misc
     testIsLockFree();
     testIsLockFree2();
     testWeirdIndices();
 
     assertEq(Atomics[Symbol.toStringTag], "Atomics");
 }
 
-if (this.Atomics && this.SharedArrayBuffer)
-    runTests();
+runTests();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/atomics/directives.txt
@@ -0,0 +1,1 @@
+|jit-test| skip-if: !this.SharedArrayBuffer || !this.Atomics
--- a/js/src/jit-test/tests/atomics/inline-add.js
+++ b/js/src/jit-test/tests/atomics/inline-add.js
@@ -13,19 +13,16 @@ function add(ta) {
     var x = ta[0];
     Atomics.add(ta, 86, 6);
     var y = ta[1];
     var z = y + 1;
     var w = x + z;
     return w;
 }
 
-if (!this.SharedArrayBuffer || !this.Atomics)
-    quit(0);
-
 var sab = new SharedArrayBuffer(4096);
 var ia = new Int32Array(sab);
 for ( var i=0, limit=ia.length ; i < limit ; i++ )
     ia[i] = 37;
 var v = 0;
 for ( var i=0 ; i < 1000 ; i++ )
     v += add(ia);
 //print(v);
--- a/js/src/jit-test/tests/atomics/inline-add2.js
+++ b/js/src/jit-test/tests/atomics/inline-add2.js
@@ -13,19 +13,16 @@
 //  - loads and stores are not moved across the add
 //
 // Be sure to run with --ion-eager --ion-offthread-compile=off.
 
 function add(ta) {
     return Atomics.add(ta, 86, 6);
 }
 
-if (!this.SharedArrayBuffer || !this.Atomics)
-    quit(0);
-
 var sab = new SharedArrayBuffer(4096);
 var ia = new Uint32Array(sab);
 for ( var i=0, limit=ia.length ; i < limit ; i++ )
     ia[i] = 0xdeadbeef;		// Important: Not an int32-capable value
 var v = 0;
 for ( var i=0 ; i < 1000 ; i++ )
     v += add(ia);
 //print(v);
--- a/js/src/jit-test/tests/atomics/inline-cmpxchg.js
+++ b/js/src/jit-test/tests/atomics/inline-cmpxchg.js
@@ -13,19 +13,16 @@ function cmpxchg(ta) {
     var x = ta[0];
     Atomics.compareExchange(ta, 86, 37, 42);
     var y = ta[1];
     var z = y + 1;
     var w = x + z;
     return w;
 }
 
-if (!this.SharedArrayBuffer || !this.Atomics)
-    quit(0);
-
 var sab = new SharedArrayBuffer(4096);
 var ia = new Int32Array(sab);
 for ( var i=0, limit=ia.length ; i < limit ; i++ )
     ia[i] = 37;
 var v = 0;
 for ( var i=0 ; i < 1000 ; i++ )
     v += cmpxchg(ia);
 //print(v);
--- a/js/src/jit-test/tests/atomics/mutual-exclusion.js
+++ b/js/src/jit-test/tests/atomics/mutual-exclusion.js
@@ -1,23 +1,14 @@
+// |jit-test| skip-if: helperThreadCount() === 0
+
 // Let a few threads hammer on memory with atomics to provoke errors
 // in exclusion work.  This test is not 100% fail-safe: the test may
 // pass despite a bug, but this is unlikely.
 
-if (!(this.SharedArrayBuffer && this.getSharedObject && this.setSharedObject && this.evalInWorker))
-    quit(0);
-
-try {
-    // This will fail with --no-threads.
-    evalInWorker("37");
-}
-catch (e) {
-    quit(0);
-}
-
 // Map an Int32Array on shared memory.  The first location is used as
 // a counter, each worker counts up on exit and the main thread will
 // wait until the counter reaches the number of workers.  The other
 // elements are contended accumulators where we count up and down very
 // rapidly and for a long time, any failure in mutual exclusion should
 // lead to errors in the result.  (For example, the test fails almost
 // immediately when I disable simulation of mutual exclusion in the
 // ARM simulator.)
--- a/js/src/jit-test/tests/atomics/optimization-tests.js
+++ b/js/src/jit-test/tests/atomics/optimization-tests.js
@@ -6,19 +6,16 @@
 // It's useful to look at the code generated for this test with -D to
 // the JS shell.
 //
 // Bug 1138348 - unconstrained use of byte registers on x64
 // Bug 1077014 - code generation for Atomic operations for effect,
 //               all platforms
 // Bug 1141121 - immediate operand in atomic operations on x86/x64
 
-if (!(this.Atomics && this.SharedArrayBuffer))
-    quit(0);
-
 var sum = 0;
 
 function f(ia, k) {
     // For effect, variable value.  The generated code on x86/x64
     // should be one LOCK ADDB.  (On ARM, there should be no
     // sign-extend of the current value in the cell, otherwise this is
     // still a LDREX/STREX loop.)
     Atomics.add(ia, 0, k);
--- a/js/src/jit-test/tests/atomics/store-does-not-truncate-returnval.js
+++ b/js/src/jit-test/tests/atomics/store-does-not-truncate-returnval.js
@@ -1,11 +1,8 @@
-if (!this.SharedArrayBuffer)
-    quit(0);
-
 var ia = new Int32Array(new SharedArrayBuffer(4));
 
 // Atomics.store() returns the input value converted to integer as if
 // by ToInteger.
 //
 // JIT and interpreter have different paths here, so loop a little to
 // trigger the JIT properly.
 
--- a/js/src/jit-test/tests/auto-regress/bug1263558.js
+++ b/js/src/jit-test/tests/auto-regress/bug1263558.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 evalcx(`
     eval('\
         var appendToActual = function(s) {};\
         gczeal = function() {};\
         gcslice = function() {};\
         selectforgc = function() {};\
         if (!("verifyprebarriers" in this)) {\
--- a/js/src/jit-test/tests/auto-regress/bug1263865.js
+++ b/js/src/jit-test/tests/auto-regress/bug1263865.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 loadFile("");
 loadFile("");
 loadFile("Array.prototype.splice.call(1)");
 function loadFile(lfVarx) {
     parseInt("1");
     oomTest(function() {
         eval(lfVarx);
--- a/js/src/jit-test/tests/auto-regress/bug1263879.js
+++ b/js/src/jit-test/tests/auto-regress/bug1263879.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 var lines = `
 
 
 
 "".replace([[2], 3])
 `.split('\n');
 var code = "";
--- a/js/src/jit-test/tests/auto-regress/bug1264823.js
+++ b/js/src/jit-test/tests/auto-regress/bug1264823.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 loadFile("");
 loadFile("");
 loadFile(` function lalala() {}
     new Map([[1, 2]]).forEach(lalala)
     `);
 function loadFile(lfVarx) {
     return oomTest(function() {
--- a/js/src/jit-test/tests/auto-regress/bug1268034.js
+++ b/js/src/jit-test/tests/auto-regress/bug1268034.js
@@ -1,7 +1,6 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 oomTest(function() {
     offThreadCompileScript("");
 });
 "".match();
--- a/js/src/jit-test/tests/auto-regress/bug1269074.js
+++ b/js/src/jit-test/tests/auto-regress/bug1269074.js
@@ -1,6 +1,3 @@
-// |jit-test| allow-oom
-
-if (!('oomTest' in this))
-    quit();
+// |jit-test| allow-oom; skip-if: !('oomTest' in this)
 
 evalcx('oomTest(function() { Array(...""); })', newGlobal());
--- a/js/src/jit-test/tests/auto-regress/bug1375446.js
+++ b/js/src/jit-test/tests/auto-regress/bug1375446.js
@@ -1,12 +1,9 @@
-// |jit-test| allow-oom
-
-if (!('oomTest' in this))
-    quit();
+// |jit-test| allow-oom; skip-if: !('oomTest' in this)
 
 loadFile(`
     disassemble(function() {
 	return assertDeepEq(x.concat(obj), [1, 2, 3, "hey"]);
     })
 `);
 function loadFile(lfVarx) {
     oomTest(new Function(lfVarx));
--- a/js/src/jit-test/tests/auto-regress/bug1462341.js
+++ b/js/src/jit-test/tests/auto-regress/bug1462341.js
@@ -1,12 +1,9 @@
-// |jit-test| allow-oom
-
-if (!('oomTest' in this))
-  quit();
+// |jit-test| allow-oom; skip-if: !('oomTest' in this)
 
 loadFile(`
   switch (0) {
     case (-1):
   }
 `);
 function loadFile(lfVarx) {
   oomTest(function() {
--- a/js/src/jit-test/tests/auto-regress/bug1466626-1.js
+++ b/js/src/jit-test/tests/auto-regress/bug1466626-1.js
@@ -1,8 +1,7 @@
-if (!("oomTest" in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 oomTest(function() {
     for (var i = 0; i < 10; ++i) {
         Promise.resolve().then();
     }
 });
--- a/js/src/jit-test/tests/auto-regress/bug1466626-2.js
+++ b/js/src/jit-test/tests/auto-regress/bug1466626-2.js
@@ -1,10 +1,9 @@
-if (!("oomTest" in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 var globals = [];
 for (var i = 0; i < 24; ++i) {
     var g = newGlobal();
     g.eval(`
         function f(){}
         var env = {};
     `);
--- a/js/src/jit-test/tests/auto-regress/bug1466626-3.js
+++ b/js/src/jit-test/tests/auto-regress/bug1466626-3.js
@@ -1,10 +1,9 @@
-if (!("oomTest" in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 var g = newGlobal();
 
 var i = 0;
 oomTest(function() {
     var s = String.fromCharCode((i++) & 0xffff);
     var r = "";
     for (var j = 0; j < 1000; ++j) {
--- a/js/src/jit-test/tests/auto-regress/bug1466626-4.js
+++ b/js/src/jit-test/tests/auto-regress/bug1466626-4.js
@@ -1,10 +1,9 @@
-if (!("oomTest" in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 var source = "{";
 for (var i = 0; i < 120; ++i)
     source += `function f${i}(){}`
 source += "}";
 
 oomTest(function() {
     Function(source);
--- a/js/src/jit-test/tests/baseline/bug1209585.js
+++ b/js/src/jit-test/tests/baseline/bug1209585.js
@@ -1,13 +1,9 @@
-if (helperThreadCount() == 0)
-    quit();
-
-if (!("oomAtAllocation" in this && "resetOOMFailure" in this))
-    quit();
+// |jit-test| skip-if: helperThreadCount() === 0 || !('oomAtAllocation' in this)
 
 if ("gczeal" in this)
     gczeal(0);
 
 eval("g=function() {}")
 var lfGlobal = newGlobal();
 for (lfLocal in this) {
     if (!(lfLocal in lfGlobal)) {
--- a/js/src/jit-test/tests/baseline/bug1247862.js
+++ b/js/src/jit-test/tests/baseline/bug1247862.js
@@ -1,10 +1,9 @@
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
+// |jit-test| skip-if: !this.hasOwnProperty("TypedObject")
 
 var T = TypedObject;
 ValueStruct = new T.StructType({
     f: T.int32,
     g: T.Any
 });
 var v = new ValueStruct;
 for (var i = 0; i < 2; i++) {
--- a/js/src/jit-test/tests/baseline/bug1344334.js
+++ b/js/src/jit-test/tests/baseline/bug1344334.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 function f(s) {
     s + "x";
     s.indexOf("y") === 0;
     oomTest(new Function(s));
 }
 var s = `
     class TestClass { constructor() {} }
--- a/js/src/jit-test/tests/baseline/bug1491337.js
+++ b/js/src/jit-test/tests/baseline/bug1491337.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 oomTest(new Function(`
   let kJSEmbeddingMaxTypes = 1000000;
   let kJSEmbeddingMaxFunctions = 1000000;
   let kJSEmbeddingMaxImports = 100000;
   const known_failures = {};
   function test(func, description) {
     known_failures[description]
--- a/js/src/jit-test/tests/baseline/bug1491350.js
+++ b/js/src/jit-test/tests/baseline/bug1491350.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this))
-  quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 oomTest(new Function(`
   var a = ['p', 'q', 'r', 's', 't'];
   var o = {p:1, q:2, r:3, s:4, t:5};
   for (var i in o)
     delete o[i];
   for (var i of a)
     o.hasOwnProperty(undefined + this, false);
--- a/js/src/jit-test/tests/basic/bug-1198090.js
+++ b/js/src/jit-test/tests/basic/bug-1198090.js
@@ -1,12 +1,9 @@
-// |jit-test| allow-oom
-
-if (!('oomAtAllocation' in this))
-    quit();
+// |jit-test| allow-oom; skip-if: !('oomAtAllocation' in this)
 
 for (let a of [
         null, function() {}, function() {}, null, function() {}, function() {},
         function() {}, null, null, null, null, null, null, null, null,
         function() {}, null, null, null, function() {}
     ]) {
     oomAtAllocation(5);
 }
deleted file mode 100644
--- a/js/src/jit-test/tests/basic/bug-1271507-2.js
+++ /dev/null
@@ -1,4 +0,0 @@
-// |jit-test| allow-oom
-
-// Adapted from randomly chosen testcase: js/src/jit-test/tests/debug/bug-1238610.js
-load(libdir + "../tests/basic/bug-1271507.js");
--- a/js/src/jit-test/tests/basic/bug-1271507.js
+++ b/js/src/jit-test/tests/basic/bug-1271507.js
@@ -1,12 +1,9 @@
-// |jit-test| allow-oom
-if (typeof oomAfterAllocations != "function")
-  quit();
-
+// |jit-test| allow-oom; skip-if: typeof oomAfterAllocations !== 'function'
 lfcode = new Array();
 oomAfterAllocations(100);
 loadFile(file);
 lfGlobal = newGlobal()
 for (lfLocal in this)
   if (!(lfLocal in lfGlobal))
     lfGlobal[lfLocal] = this[lfLocal]
 offThreadCompileScript(lfVarx)
--- a/js/src/jit-test/tests/basic/bug1207863.js
+++ b/js/src/jit-test/tests/basic/bug1207863.js
@@ -1,12 +1,9 @@
-// |jit-test| allow-oom; allow-unhandlable-oom
-
-if (!("oomAtAllocation" in this && "resetOOMFailure" in this))
-    quit();
+// |jit-test| allow-oom; allow-unhandlable-oom; skip-if: !("oomAtAllocation" in this && "resetOOMFailure" in this)
 
 function oomTest(f) {
     var i = 1;
     do {
         try {
             oomAtAllocation(i);
             f();
         } catch (e) {
--- a/js/src/jit-test/tests/basic/bug1234414.js
+++ b/js/src/jit-test/tests/basic/bug1234414.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this))
-  quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 oomTest(() => {
     var max = 400;
     function f(b) {
         if (b) {
             f(b - 1);
         } else eval('"use strict"; const z = w; z = 1 + w; c = 5');
     }
--- a/js/src/jit-test/tests/basic/bug1240502.js
+++ b/js/src/jit-test/tests/basic/bug1240502.js
@@ -1,3 +1,2 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 oomTest(() => eval(`Array(..."ABC")`));
--- a/js/src/jit-test/tests/basic/bug1263868.js
+++ b/js/src/jit-test/tests/basic/bug1263868.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 function g(f, params) {
     entryPoints(params);
 }
 function entry1() {};
 s = "g(entry1, {function: entry1});";
 f(s);
 f(s);
 function f(x) {
--- a/js/src/jit-test/tests/basic/bug1264954.js
+++ b/js/src/jit-test/tests/basic/bug1264954.js
@@ -1,8 +1,7 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 function f(x) {
     oomTest(() => eval(x));
 }
 f("");
 f("");
 f(`eval([   "x = \`\${new Error.lineNumber}" ].join())`);
--- a/js/src/jit-test/tests/basic/bug1265693.js
+++ b/js/src/jit-test/tests/basic/bug1265693.js
@@ -1,4 +1,2 @@
-if (!('oomTest' in this))
-    quit();
-
+// |jit-test| skip-if: !('oomTest' in this)
 oomTest(Function("Function.hasOwnProperty(1.1)"));
--- a/js/src/jit-test/tests/basic/bug1278839.js
+++ b/js/src/jit-test/tests/basic/bug1278839.js
@@ -1,4 +1,3 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 for (var i=0; i<2; i++)
     oomTest(() => eval("setJitCompilerOption(eval + Function, 0);"));
--- a/js/src/jit-test/tests/basic/bug1285227.js
+++ b/js/src/jit-test/tests/basic/bug1285227.js
@@ -1,5 +1,4 @@
-if (helperThreadCount() === 0)
-    quit();
+// |jit-test| skip-if: helperThreadCount() === 0
 evalInWorker(`
     (new WeakMap).set(FakeDOMObject.prototype, this)
 `);
--- a/js/src/jit-test/tests/basic/bug1296016.js
+++ b/js/src/jit-test/tests/basic/bug1296016.js
@@ -1,4 +1,3 @@
-if (helperThreadCount() === 0)
-    quit(0);
+// |jit-test| skip-if: helperThreadCount() === 0
 offThreadCompileScript(``);
 evalInWorker(`runOffThreadScript()`);
--- a/js/src/jit-test/tests/basic/bug1296249.js
+++ b/js/src/jit-test/tests/basic/bug1296249.js
@@ -1,10 +1,7 @@
-// |jit-test| slow
-if (!('oomTest' in this))
-    quit();
-
+// |jit-test| slow; skip-if: !('oomTest' in this)
 function f(x) {
     new Int32Array(x);
 }
 
 f(0);
 oomTest(() => f(2147483647));
--- a/js/src/jit-test/tests/basic/bug1300904.js
+++ b/js/src/jit-test/tests/basic/bug1300904.js
@@ -1,7 +1,6 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 Object.getOwnPropertyNames(this);
 oomTest(function() {
     this[0] = null;
     Object.freeze(this);
 });
--- a/js/src/jit-test/tests/basic/bug1341326.js
+++ b/js/src/jit-test/tests/basic/bug1341326.js
@@ -1,10 +1,9 @@
-if (helperThreadCount() == 0)
-    quit();
+// |jit-test| skip-if: helperThreadCount() === 0
 function eval(source) {
     offThreadCompileModule(source);
 }
 var N = 10000;
 var left = repeat_str('(1&', N);
 var right = repeat_str(')', N);
 var str = 'actual = '.concat(left, '1', right, ';');
 eval(str);
--- a/js/src/jit-test/tests/basic/bug1344265.js
+++ b/js/src/jit-test/tests/basic/bug1344265.js
@@ -1,5 +1,3 @@
-// |jit-test| allow-unhandlable-oom; allow-oom
-if (!('oomAfterAllocations' in this))
-    quit();
+// |jit-test| allow-unhandlable-oom; allow-oom; skip-if: !('oomAfterAllocations' in this)
 oomAfterAllocations(1);
 newExternalString("a");
--- a/js/src/jit-test/tests/basic/bug1348407.js
+++ b/js/src/jit-test/tests/basic/bug1348407.js
@@ -1,6 +1,5 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 x = evalcx("lazy");
 oomTest(function () {
     x.eval("1");
 });
--- a/js/src/jit-test/tests/basic/bug1355573.js
+++ b/js/src/jit-test/tests/basic/bug1355573.js
@@ -1,10 +1,9 @@
-if (getBuildConfiguration().debug === true)
-    quit(0);
+// |jit-test| skip-if: getBuildConfiguration().debug === true
 function f(){};
 Object.defineProperty(f, "name", {value: "a".repeat((1<<28)-1)});
 var ex = null;
 try {
     len = f.bind().name.length;
 } catch (e) {
     ex = e;
 }
--- a/js/src/jit-test/tests/basic/bug1411294.js
+++ b/js/src/jit-test/tests/basic/bug1411294.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 oomTest(function() {
   eval(`var clonebuffer = serialize("abc");
   clonebuffer.clonebuffer = "\
 \\x00\\x00\\x00\\x00\\b\\x00\\xFF\\xFF\\f\
 \\x00\\x00\\x00\\x03\\x00\\xFF\\xFF\\x00\\x00\\x00\\x00\\x00\\x00\\x00\
 \\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xF0?\\x00\\x00\\x00\\\x00\\x00\
 \\x00\\xFF\\xFF"
   var obj = deserialize(clonebuffer)
--- a/js/src/jit-test/tests/basic/bug1447996.js
+++ b/js/src/jit-test/tests/basic/bug1447996.js
@@ -1,10 +1,10 @@
-if (!('stackTest' in this))
-    quit();
+// |jit-test| skip-if: !('stackTest' in this)
+
 var x = 0;
 function f() {
     var s = "abcdef(((((((a|b)a|b)a|b)a|b)a|b)a|b)a|b)" + x;
     res = "abcdefa".match(new RegExp(s));
     x++;
 }
 f();
 stackTest(f, true);
--- a/js/src/jit-test/tests/basic/bug1459258.js
+++ b/js/src/jit-test/tests/basic/bug1459258.js
@@ -1,8 +1,7 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 oomTest(function() {
     return [0, Math.PI, NaN, Infinity, true, false, Symbol(), Math.tan,
             Reflect, Proxy, print, assertEq, Array, String, Boolean, Number, parseInt,
             parseFloat, Math.sin, Math.cos, Math.abs, Math.pow, Math.sqrt,
             Uint8Array, Int8Array, Int32Array, Int16Array, Uint16Array];
 });
--- a/js/src/jit-test/tests/basic/bug1470732.js
+++ b/js/src/jit-test/tests/basic/bug1470732.js
@@ -1,11 +1,9 @@
-if (helperThreadCount() === 0)
-    quit();
-
+// |jit-test| skip-if: helperThreadCount() === 0
 var i = 0;
 while(i++ < 500) {
   evalInWorker(`
     assertFloat32(0x23456789 | 0, false);
   `);
   let m = parseModule("");
   m.declarationInstantiation();
 }
--- a/js/src/jit-test/tests/basic/dumpStringRepresentation.js
+++ b/js/src/jit-test/tests/basic/dumpStringRepresentation.js
@@ -1,14 +1,13 @@
+// |jit-test| skip-if: typeof dumpStringRepresentation !== 'function'
+
 // Try the dumpStringRepresentation shell function on various types of
 // strings, and make sure it doesn't crash.
 
-if (typeof dumpStringRepresentation !== 'function')
-  quit(0);
-
 print("Empty string:");
 dumpStringRepresentation("");
 
 print("\nResult of coercion to string:");
 dumpStringRepresentation();
 
 print("\nString with an index value:");
 dumpStringRepresentation((12345).toString());
--- a/js/src/jit-test/tests/basic/evalInWorker-interrupt.js
+++ b/js/src/jit-test/tests/basic/evalInWorker-interrupt.js
@@ -1,3 +1,2 @@
-if (helperThreadCount() === 0)
-    quit();
+// |jit-test| skip-if: helperThreadCount() === 0
 evalInWorker("setInterruptCallback(function() {}); timeout(1000);");
--- a/js/src/jit-test/tests/basic/evalInWorker-jit-options.js
+++ b/js/src/jit-test/tests/basic/evalInWorker-jit-options.js
@@ -1,10 +1,9 @@
-if (helperThreadCount() === 0)
-    quit();
+// |jit-test| skip-if: helperThreadCount() === 0
 var fun = function() {
     var ex;
     try {
         setJitCompilerOption("baseline.warmup.trigger", 5);
     } catch(e) {
         ex = e;
     }
     assertEq(ex && ex.toString().includes("Can't set"), true);
--- a/js/src/jit-test/tests/basic/evaluate-worker.js
+++ b/js/src/jit-test/tests/basic/evaluate-worker.js
@@ -1,12 +1,9 @@
-// |jit-test| slow
-
-if (typeof evalInWorker == "undefined")
-    quit();
+// |jit-test| slow; skip-if: typeof evalInWorker === 'undefined'
 
 gcslice(11);
 evalInWorker("print('helo world');");
 for (i = 0; i < 100000; i++) {}
 
 evalInWorker("\
   for (var i = 0; i < 10; i++) { \
     var o = {}; \
--- a/js/src/jit-test/tests/basic/inflate-oom.js
+++ b/js/src/jit-test/tests/basic/inflate-oom.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 function test() {
     function foo() {
         return 1;
     };
     oomTest(() => {
         gc();
         foo.toString();
     });
--- a/js/src/jit-test/tests/basic/offThreadCompileScript-01.js
+++ b/js/src/jit-test/tests/basic/offThreadCompileScript-01.js
@@ -1,18 +1,17 @@
+// |jit-test| skip-if: helperThreadCount() === 0
+
 // Any copyright is dedicated to the Public Domain.
 // http://creativecommons.org/licenses/publicdomain/
 
 // Test off-thread parsing.
 
 load(libdir + 'asserts.js');
 
-if (helperThreadCount() === 0)
-  quit(0);
-
 offThreadCompileScript('Math.sin(Math.PI/2)');
 assertEq(runOffThreadScript(), 1);
 
 offThreadCompileScript('a string which cannot be reduced to the start symbol');
 assertThrowsInstanceOf(runOffThreadScript, SyntaxError);
 
 offThreadCompileScript('smerg;');
 assertThrowsInstanceOf(runOffThreadScript, ReferenceError);
--- a/js/src/jit-test/tests/basic/offThreadCompileScript-02.js
+++ b/js/src/jit-test/tests/basic/offThreadCompileScript-02.js
@@ -1,12 +1,11 @@
-// Test offThreadCompileScript option handling.
+// |jit-test| skip-if: helperThreadCount() === 0
 
-if (helperThreadCount() === 0)
-  quit(0);
+// Test offThreadCompileScript option handling.
 
 offThreadCompileScript('Error()');
 assertEq(!!runOffThreadScript().stack.match(/^@<string>:1:1\n/), true);
 
 offThreadCompileScript('Error()',
                        { fileName: "candelabra", lineNumber: 6502 });
 assertEq(!!runOffThreadScript().stack.match(/^@candelabra:6502:1\n/), true);
 
--- a/js/src/jit-test/tests/basic/testApplyArrayInline.js
+++ b/js/src/jit-test/tests/basic/testApplyArrayInline.js
@@ -1,17 +1,16 @@
+// |jit-test| skip-if: !this.getJitCompilerOptions() || !getJitCompilerOptions()['ion.enable']
+
 // Test inlining in Ion of fun.apply(..., array).
 
 setJitCompilerOption("ion.warmup.trigger", 50);
 setJitCompilerOption("offthread-compilation.enable", 0);
 gcPreserveCode();
 
-if (!this.getJitCompilerOptions() || !getJitCompilerOptions()['ion.enable'])
-    quit(0);
-
 var itercount = 1000;
 var warmup = 100;
 
 // Force Ion to do something predictable without having to wait
 // forever for it.
 
 if (getJitCompilerOptions()["ion.warmup.trigger"] > warmup)
     setJitCompilerOption("ion.warmup.trigger", warmup);
--- a/js/src/jit-test/tests/basic/testBug756919.js
+++ b/js/src/jit-test/tests/basic/testBug756919.js
@@ -1,11 +1,9 @@
-if (!('oomTest' in this))
-    quit();
-
+// |jit-test| skip-if: !('oomTest' in this)
 function test(x) {
     var upvar = "";
     function f() { upvar += ""; }
     if (x > 0)
         test(x - 1);
     eval('');
 }
 
--- a/js/src/jit-test/tests/basic/testOOMInAutoEnterCompartment.js
+++ b/js/src/jit-test/tests/basic/testOOMInAutoEnterCompartment.js
@@ -1,11 +1,10 @@
-// |jit-test| slow
-// This test is too slow to run with ASan in a debug configuration
-if (getBuildConfiguration()['asan'] && getBuildConfiguration()['debug']) quit(0);
+// |jit-test| slow; skip-if: (getBuildConfiguration()['asan'] && getBuildConfiguration()['debug'])
+// This test is too slow to run at all with ASan in a debug configuration
 
 function fatty() {
     try {
         fatty();
     } catch (e) {
         foo();
     }
 }
--- a/js/src/jit-test/tests/basic/withSourceHook.js
+++ b/js/src/jit-test/tests/basic/withSourceHook.js
@@ -1,18 +1,15 @@
+// |jit-test| skip-if: typeof withSourceHook !== 'function'
+
 // Check that withSourceHook passes URLs, propagates exceptions, and
 // properly restores the original source hooks.
 
 load(libdir + 'asserts.js');
 
-// withSourceHook isn't defined if you pass the shell the --fuzzing-safe
-// option. Skip this test silently, to avoid spurious failures.
-if (typeof withSourceHook != 'function')
-  quit(0);
-
 var log = '';
 
 // Establish an outermost source hook.
 withSourceHook(function (url) {
   log += 'o';
   assertEq(url, 'outer');
   return '(function outer() { 3; })';
 }, function () {
--- a/js/src/jit-test/tests/bug1213574.js
+++ b/js/src/jit-test/tests/bug1213574.js
@@ -1,10 +1,9 @@
-if (helperThreadCount() === 0)
-    quit(0);
+// |jit-test| skip-if: helperThreadCount() === 0
 
 var lfGlobal = newGlobal();
 lfGlobal.offThreadCompileScript(`{ let x; throw 42; }`);
 try {
     lfGlobal.runOffThreadScript();
 } catch (e) {
 }
 
--- a/js/src/jit-test/tests/bug1366925.js
+++ b/js/src/jit-test/tests/bug1366925.js
@@ -1,10 +1,9 @@
-// JS shell shutdown ordering
-
+// |jit-test| skip-if: helperThreadCount() === 0
 // Avoid crashing with --no-threads
-if (helperThreadCount() == 0)
-  quit();
+
+// JS shell shutdown ordering
 
 evalInWorker(`
   var lfGlobal = newGlobal();
   lfGlobal.offThreadCompileScript(\`{ let x; throw 42; }\`);
 `);
--- a/js/src/jit-test/tests/bug1490638.js
+++ b/js/src/jit-test/tests/bug1490638.js
@@ -1,10 +1,8 @@
-// |jit-test| allow-overrecursed
-if (helperThreadCount() == 0)
-    quit();
+// |jit-test| allow-overrecursed; skip-if: helperThreadCount() === 0
 
 evalInWorker(`
   function f() {
     f.apply([], new Array(20000));
   }
   f()
 `);
--- a/js/src/jit-test/tests/debug/Debugger-allowUnobservedAsmJS-02.js
+++ b/js/src/jit-test/tests/debug/Debugger-allowUnobservedAsmJS-02.js
@@ -1,14 +1,14 @@
+// |jit-test| skip-if: helperThreadCount() === 0
+
 // Debugger.allowUnobservedAsmJS with off-thread parsing.
 
 load(libdir + "asm.js");
 
-if (helperThreadCount() == 0)
-    quit();
 
 var g = newGlobal();
 g.parent = this;
 g.eval("dbg = new Debugger(parent);");
 
 assertEq(g.dbg.allowUnobservedAsmJS, false);
 
 enableLastWarning();
--- a/js/src/jit-test/tests/debug/Debugger-onNewPromise-01.js
+++ b/js/src/jit-test/tests/debug/Debugger-onNewPromise-01.js
@@ -1,12 +1,10 @@
 // Test that the onNewPromise hook gets called when promises are allocated in
 // the scope of debuggee globals.
-if (!('Promise' in this))
-    quit(0);
 
 var g = newGlobal();
 var dbg = new Debugger();
 var gw = dbg.addDebuggee(g);
 
 
 let promisesFound = [];
 dbg.onNewPromise = p => { promisesFound.push(p); };
--- a/js/src/jit-test/tests/debug/Debugger-onNewPromise-02.js
+++ b/js/src/jit-test/tests/debug/Debugger-onNewPromise-02.js
@@ -1,11 +1,9 @@
 // onNewPromise handlers fire, until they are removed.
-if (!('Promise' in this))
-    quit(0);
 
 var g = newGlobal();
 var dbg = new Debugger(g);
 var log;
 
 log = '';
 new g.Promise(function (){});
 assertEq(log, '');
--- a/js/src/jit-test/tests/debug/Debugger-onNewPromise-03.js
+++ b/js/src/jit-test/tests/debug/Debugger-onNewPromise-03.js
@@ -1,11 +1,9 @@
 // onNewPromise handlers on different Debugger instances are independent.
-if (!('Promise' in this))
-    quit(0);
 
 var g = newGlobal();
 var dbg1 = new Debugger(g);
 var log1;
 function h1(promise) {
   log1 += 'n';
   assertEq(promise.seen, undefined);
   promise.seen = true;
--- a/js/src/jit-test/tests/debug/Debugger-onNewPromise-04.js
+++ b/js/src/jit-test/tests/debug/Debugger-onNewPromise-04.js
@@ -1,12 +1,9 @@
 // An onNewPromise handler can disable itself.
-if (!('Promise' in this))
-    quit(0);
-
 var g = newGlobal();
 var dbg = new Debugger(g);
 var log;
 
 dbg.onNewPromise = function (promise) {
   log += 'n';
   dbg.onNewPromise = undefined;
 };
--- a/js/src/jit-test/tests/debug/Debugger-onNewPromise-05.js
+++ b/js/src/jit-test/tests/debug/Debugger-onNewPromise-05.js
@@ -1,13 +1,10 @@
 // Creating a promise within an onNewPromise handler causes a recursive handler
 // invocation.
-if (!('Promise' in this))
-    quit(0);
-
 var g = newGlobal();
 var dbg = new Debugger();
 var gw = dbg.addDebuggee(g);
 var log;
 var depth;
 
 dbg.onNewPromise = function (promise) {
   log += '('; depth++;
--- a/js/src/jit-test/tests/debug/Debugger-onNewPromise-06.js
+++ b/js/src/jit-test/tests/debug/Debugger-onNewPromise-06.js
@@ -1,11 +1,9 @@
 // Resumption values from onNewPromise handlers are disallowed.
-if (!('Promise' in this))
-    quit(0);
 
 load(libdir + 'asserts.js');
 
 var g = newGlobal();
 var dbg = new Debugger(g);
 var log;
 
 dbg.onNewPromise = function (g) { log += 'n'; return undefined; };
--- a/js/src/jit-test/tests/debug/Debugger-onNewPromise-07.js
+++ b/js/src/jit-test/tests/debug/Debugger-onNewPromise-07.js
@@ -1,12 +1,10 @@
 // Errors in onNewPromise handlers are reported correctly, and don't mess up the
 // promise creation.
-if (!('Promise' in this))
-    quit(0);
 
 var g = newGlobal();
 var dbg = new Debugger(g);
 
 let e;
 dbg.uncaughtExceptionHook = ee => { e = ee; };
 dbg.onNewPromise = () => { throw new Error("woops!"); };
 
--- a/js/src/jit-test/tests/debug/Debugger-onPromiseSettled-01.js
+++ b/js/src/jit-test/tests/debug/Debugger-onPromiseSettled-01.js
@@ -1,11 +1,9 @@
 // Test that the onPromiseSettled hook gets called when a promise settles.
-if (!('Promise' in this))
-    quit(0);
 
 var g = newGlobal();
 var dbg = new Debugger();
 var gw = dbg.addDebuggee(g);
 
 let log = "";
 let pw;
 dbg.onPromiseSettled = pw_ => {
--- a/js/src/jit-test/tests/debug/Debugger-onPromiseSettled-02.js
+++ b/js/src/jit-test/tests/debug/Debugger-onPromiseSettled-02.js
@@ -1,11 +1,9 @@
 // onPromiseSettled handlers fire, until they are removed.
-if (!('Promise' in this))
-    quit(0);
 
 var g = newGlobal();
 var dbg = new Debugger(g);
 var log;
 
 log = '';
 g.settlePromiseNow(new g.Promise(function (){}));
 assertEq(log, '');
--- a/js/src/jit-test/tests/debug/Debugger-onPromiseSettled-03.js
+++ b/js/src/jit-test/tests/debug/Debugger-onPromiseSettled-03.js
@@ -1,12 +1,9 @@
 // onPromiseSettled handlers on different Debugger instances are independent.
-if (!('Promise' in this))
-    quit(0);
-
 var g = newGlobal();
 var dbg1 = new Debugger(g);
 var log1;
 function h1(promise) {
   log1 += 's';
   assertEq(promise.seen, undefined);
   promise.seen = true;
 }
--- a/js/src/jit-test/tests/debug/Debugger-onPromiseSettled-04.js
+++ b/js/src/jit-test/tests/debug/Debugger-onPromiseSettled-04.js
@@ -1,12 +1,9 @@
 // An onPromiseSettled handler can disable itself.
-if (!('Promise' in this))
-    quit(0);
-
 var g = newGlobal();
 var dbg = new Debugger(g);
 var log;
 
 dbg.onPromiseSettled = function (promise) {
   log += 's';
   dbg.onPromiseSettled = undefined;
 };
--- a/js/src/jit-test/tests/debug/Debugger-onPromiseSettled-05.js
+++ b/js/src/jit-test/tests/debug/Debugger-onPromiseSettled-05.js
@@ -1,12 +1,10 @@
 // Settling a promise within an onPromiseSettled handler causes a recursive
 // handler invocation.
-if (!('Promise' in this))
-    quit(0);
 
 var g = newGlobal();
 var dbg = new Debugger();
 var gw = dbg.addDebuggee(g);
 var log;
 var depth;
 
 dbg.onPromiseSettled = function (promise) {
--- a/js/src/jit-test/tests/debug/Debugger-onPromiseSettled-06.js
+++ b/js/src/jit-test/tests/debug/Debugger-onPromiseSettled-06.js
@@ -1,11 +1,9 @@
 // Resumption values from onPromiseSettled handlers are disallowed.
-if (!('Promise' in this))
-    quit(0);
 
 load(libdir + 'asserts.js');
 
 var g = newGlobal();
 var dbg = new Debugger(g);
 var log;
 
 dbg.onPromiseSettled = function (g) { log += 's'; return undefined; };
--- a/js/src/jit-test/tests/debug/Memory-drainAllocationsLog-13.js
+++ b/js/src/jit-test/tests/debug/Memory-drainAllocationsLog-13.js
@@ -1,16 +1,14 @@
+// |jit-test| skip-if: helperThreadCount() === 0
+
 // Test that we don't crash while logging allocations and there is
 // off-main-thread compilation. OMT compilation will allocate functions and
 // regexps, but we just punt on measuring that accurately.
 
-if (helperThreadCount() === 0) {
-  quit(0);
-}
-
 const root = newGlobal();
 root.eval("this.dbg = new Debugger()");
 root.dbg.addDebuggee(this);
 root.dbg.memory.trackingAllocationSites = true;
 
 offThreadCompileScript(
   "function foo() {\n" +
   "  print('hello world');\n" +
--- a/js/src/jit-test/tests/debug/Memory-drainAllocationsLog-15.js
+++ b/js/src/jit-test/tests/debug/Memory-drainAllocationsLog-15.js
@@ -1,12 +1,9 @@
 // Test drainAllocationsLog() and [[Class]] names.
-if (!('Promise' in this))
-    quit(0);
-
 const root = newGlobal();
 const dbg = new Debugger();
 const wrappedRoot = dbg.addDebuggee(root)
 
 root.eval(
   `
   this.tests = [
     { expected: "Object",    test: () => new Object    },
--- a/js/src/jit-test/tests/debug/Script-format-01.js
+++ b/js/src/jit-test/tests/debug/Script-format-01.js
@@ -1,22 +1,18 @@
+// |jit-test| skip-if: !wasmDebuggingIsSupported()
+
 // Tests that JavaScript scripts have a "js" format and wasm scripts have a
 // "wasm" format.
 
-if (!wasmDebuggingIsSupported())
-    quit(0);
-
 var g = newGlobal();
 var dbg = new Debugger(g);
 
 var gotScript;
 dbg.onNewScript = (script) => {
   gotScript = script;
 };
 
 g.eval(`(() => {})()`);
 assertEq(gotScript.format, "js");
 
-if (!wasmIsSupported())
-  quit();
-
 g.eval(`o = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary('(module (func) (export "" 0))')));`);
 assertEq(gotScript.format, "wasm");
--- a/js/src/jit-test/tests/debug/Source-element-03.js
+++ b/js/src/jit-test/tests/debug/Source-element-03.js
@@ -1,14 +1,13 @@
+// |jit-test| skip-if: helperThreadCount() === 0
+
 // Owning elements and attribute names are attached to scripts compiled
 // off-thread.
 
-if (helperThreadCount() === 0)
-  quit(0);
-
 var g = newGlobal();
 var dbg = new Debugger;
 var gDO = dbg.addDebuggee(g);
 
 var elt = new g.Object;
 var eltDO = gDO.makeDebuggeeValue(elt);
 
 var log = '';
--- a/js/src/jit-test/tests/debug/Source-introductionType.js
+++ b/js/src/jit-test/tests/debug/Source-introductionType.js
@@ -1,12 +1,11 @@
-// Check that scripts' introduction types are properly marked.
+// |jit-test| skip-if: helperThreadCount() === 0
 
-if (helperThreadCount() === 0)
-  quit(0);
+// Check that scripts' introduction types are properly marked.
 
 var g = newGlobal();
 var dbg = new Debugger();
 var gDO = dbg.addDebuggee(g);
 var log;
 
 // (Indirect) eval.
 dbg.onDebuggerStatement = function (frame) {
--- a/js/src/jit-test/tests/debug/Source-text-lazy.js
+++ b/js/src/jit-test/tests/debug/Source-text-lazy.js
@@ -1,18 +1,17 @@
+// |jit-test| skip-if: typeof withSourceHook !== 'function'
+// withSourceHook isn't defined if you pass the shell the --fuzzing-safe
+// option. Skip this test silently, to avoid spurious failures.
+
 /*
  * Debugger.Source.prototype.text should correctly retrieve the source for
  * code compiled with CompileOptions::LAZY_SOURCE.
  */
 
-// withSourceHook isn't defined if you pass the shell the --fuzzing-safe
-// option. Skip this test silently, to avoid spurious failures.
-if (typeof withSourceHook != 'function')
-  quit(0);
-
 let g = newGlobal();
 let dbg = new Debugger(g);
 
 function test(source) {
   // To ensure that we're getting the value the source hook returns, make
   // it differ from the actual source.
   let frobbed = source.replace(/debugger/, 'reggubed');
   let log = '';
--- a/js/src/jit-test/tests/debug/breakpoint-oom-01.js
+++ b/js/src/jit-test/tests/debug/breakpoint-oom-01.js
@@ -1,16 +1,15 @@
+// |jit-test| skip-if: !('oomTest' in this)
+
 // Test for OOM hitting a breakpoint in a generator.
 //
 // (The purpose is to test OOM-handling in the code that creates the
 // Debugger.Frame object and associates it with the generator object.)
 
-if (!('oomTest' in this))
-    quit();
-
 let g = newGlobal();
 g.eval(`\
     function* gen(x) {  // line 1
         x++;            // 2
         yield x;        // 3
     }                   // 4
 `);
 
--- a/js/src/jit-test/tests/debug/bug-1238610.js
+++ b/js/src/jit-test/tests/debug/bug-1238610.js
@@ -1,15 +1,9 @@
-// |jit-test| allow-oom
-
-if (!('oomAfterAllocations' in this))
-    quit();
-
-if (helperThreadCount() === 0)
-    quit(0);
+// |jit-test| allow-oom; skip-if: !('oomAfterAllocations' in this) || helperThreadCount() === 0
 
 lfcode = new Array();
 dbg = Debugger();
 dbg.onEnterFrame = function() {};
 g = newGlobal();
 lfcode.push(`
   oomAfterAllocations(100);
   new Number();
--- a/js/src/jit-test/tests/debug/bug-1248162.js
+++ b/js/src/jit-test/tests/debug/bug-1248162.js
@@ -1,12 +1,9 @@
-// |jit-test| allow-oom
-
-if (typeof oomTest !== "function")
-  quit();
+// |jit-test| allow-oom; skip-if: !('oomTest' in this)
 
 // Adapted from randomly chosen test: js/src/jit-test/tests/debug/Debugger-onNewGlobalObject-01.js
 for (var i = 0; i < 9; ++i) {
   var dbg = new Debugger;
   dbg.onNewGlobalObject = function() {};
 }
 // jsfunfuzz-generated
 oomTest(function() {
--- a/js/src/jit-test/tests/debug/bug-1260725.js
+++ b/js/src/jit-test/tests/debug/bug-1260725.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this))
-  quit()
+// |jit-test| skip-if: !('oomTest' in this)
 
 var dbg = new Debugger;
 dbg.onNewGlobalObject = function(global) {
   dbg.memory.takeCensus({});
 };
 oomTest(function() {
   newGlobal({sameZoneAs: this})
 });
--- a/js/src/jit-test/tests/debug/bug1108159.js
+++ b/js/src/jit-test/tests/debug/bug1108159.js
@@ -1,12 +1,9 @@
-// |jit-test| slow
-
-if (helperThreadCount() == 0)
-  quit(0);
+// |jit-test| slow; skip-if: helperThreadCount() === 0
 
 var s = '';
 for (var i = 0; i < 70000; i++)
  {
     s += 'function x' + i + '() { x' + i + '(); }\n';
 }
 evaluate(s);
 var g = newGlobal();
--- a/js/src/jit-test/tests/debug/bug1216261.js
+++ b/js/src/jit-test/tests/debug/bug1216261.js
@@ -1,12 +1,9 @@
-// |jit-test| exitstatus: 3
-
-if (!('oomAfterAllocations' in this))
-    quit(3);
+// |jit-test| exitstatus: 3; skip-if: !('oomAfterAllocations' in this)
 
 var g = newGlobal();
 var dbg = new Debugger(g);
 dbg.onDebuggerStatement = function(frame) {
     oomAfterAllocations(5);
     // OOMs here, and possibly again in the error reporter when trying to
     // report the OOM, so the shell just exits with code 3.
     frame.older.eval("escaped = function() { return y }");
--- a/js/src/jit-test/tests/debug/bug1219905.js
+++ b/js/src/jit-test/tests/debug/bug1219905.js
@@ -1,14 +1,11 @@
-// |jit-test| allow-oom
+// |jit-test| allow-oom; skip-if: !('oomTest' in this)
 
 // We need allow-oom here because the debugger reports an uncaught exception if
 // it hits OOM calling the exception unwind hook. This causes the shell to exit
 // with the OOM reason.
 
-if (!('oomTest' in this))
-    quit();
-
 var g = newGlobal();
 g.parent = this;
 g.eval("new Debugger(parent).onExceptionUnwind = function() {}");
 let finished = false;
 oomTest(() => l, false);
--- a/js/src/jit-test/tests/debug/bug1240546.js
+++ b/js/src/jit-test/tests/debug/bug1240546.js
@@ -1,12 +1,9 @@
-// |jit-test| allow-oom
-
-if (!('oomAfterAllocations' in this))
-  quit();
+// |jit-test| allow-oom; skip-if: !('oomAfterAllocations' in this)
 
 var g = newGlobal();
 g.debuggeeGlobal = this;
 g.eval("(" + function() {
     oomAfterAllocations(100);
     var dbg = Debugger(debuggeeGlobal);
     dbg.onEnterFrame = function(frame) {}
 } + ")()");
--- a/js/src/jit-test/tests/debug/bug1240803.js
+++ b/js/src/jit-test/tests/debug/bug1240803.js
@@ -1,13 +1,9 @@
-// |jit-test| allow-oom
-
-
-if (!('oomAfterAllocations' in this))
-  quit();
+// |jit-test| allow-oom; skip-if: !('oomAfterAllocations' in this)
 
 (function() {
     g = newGlobal()
     dbg = new Debugger
     g.toggle = function(d) {
         if (d) {
             dbg.addDebuggee(g);
             dbg.getNewestFrame();
--- a/js/src/jit-test/tests/debug/bug1242111.js
+++ b/js/src/jit-test/tests/debug/bug1242111.js
@@ -1,11 +1,8 @@
-// |jit-test| allow-oom
-
-if (!('oomAfterAllocations' in this))
-  quit();
+// |jit-test| allow-oom; skip-if: !('oomAfterAllocations' in this)
 
 var g = newGlobal();
 g.debuggeeGlobal = [];
 g.eval("(" + function() {
     oomAfterAllocations(57);
     Debugger(debuggeeGlobal).onEnterFrame = function() {}
 } + ")()");
--- a/js/src/jit-test/tests/debug/bug1245862.js
+++ b/js/src/jit-test/tests/debug/bug1245862.js
@@ -1,12 +1,9 @@
-// |jit-test| allow-oom
-
-if (!('oomAfterAllocations' in this))
-  quit();
+// |jit-test| allow-oom; skip-if: !('oomAfterAllocations' in this)
 
 var g = newGlobal();
 var dbg = new Debugger;
 g.h = function h(d) {
   if (d) {
     dbg.addDebuggee(g);
     var f = dbg.getNewestFrame().older;
     f.st_p1((oomAfterAllocations(10)) + "foo = 'string of 42'");
--- a/js/src/jit-test/tests/debug/bug1251919.js
+++ b/js/src/jit-test/tests/debug/bug1251919.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 // jsfunfuzz-generated
 fullcompartmentchecks(true);
 // Adapted from randomly chosen test: js/src/jit-test/tests/debug/bug-1248162.js
 var dbg = new Debugger;
 dbg.onNewGlobalObject = function() {};
 oomTest(function() {
     newGlobal({sameZoneAs: this});
--- a/js/src/jit-test/tests/debug/bug1254123.js
+++ b/js/src/jit-test/tests/debug/bug1254123.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this))
-  quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 evaluate(`
 function ERROR(msg) {
   throw new Error("boom");
 }
 for (var i = 0; i < 9; ++ i) {
   var dbg = new Debugger;
   dbg.onNewGlobalObject = ERROR;
--- a/js/src/jit-test/tests/debug/bug1254190.js
+++ b/js/src/jit-test/tests/debug/bug1254190.js
@@ -1,12 +1,9 @@
-// |jit-test| slow
-
-if (!('oomTest' in this))
-    quit();
+// |jit-test| slow; skip-if: !('oomTest' in this)
 
 var g = newGlobal();
 var dbg = new Debugger(g);
 dbg.onNewScript = function (s) {
   log += dbg.findScripts({ source: s.source }).length;
 }
 log = "";
 oomTest(() => {
--- a/js/src/jit-test/tests/debug/bug1254578.js
+++ b/js/src/jit-test/tests/debug/bug1254578.js
@@ -1,12 +1,9 @@
-// |jit-test| slow
-
-if (!('oomTest' in this))
-    quit();
+// |jit-test| slow; skip-if: !('oomTest' in this)
 
 var g = newGlobal();
 g.debuggeeGlobal = this;
 g.eval("(" + function() {
     dbg = new Debugger(debuggeeGlobal);
     dbg.onExceptionUnwind = function(frame, exc) {
         var s = '!';
         for (var f = frame; f; f = f.older)
--- a/js/src/jit-test/tests/debug/bug1257045.js
+++ b/js/src/jit-test/tests/debug/bug1257045.js
@@ -1,10 +1,9 @@
-if (!wasmDebuggingIsSupported())
-  quit();
+// |jit-test| skip-if: !wasmDebuggingIsSupported()
 
 fullcompartmentchecks(true);
 var g = newGlobal();
 var dbg = new Debugger(g);
 dbg.onNewScript = (function(script) {
     s = script;
 })
 g.eval(`new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary('(module (func) (export "" 0))')));`);
--- a/js/src/jit-test/tests/debug/bug1264961.js
+++ b/js/src/jit-test/tests/debug/bug1264961.js
@@ -1,12 +1,9 @@
-// |jit-test| slow;
-
-if (!('oomTest' in this))
-  quit();
+// |jit-test| slow; skip-if: !('oomTest' in this)
 
 loadFile(`
   var o = {}
   var global = this;
   var p = new Proxy(o, {
     "deleteProperty": function (await , key) {
       var g = newGlobal({sameZoneAs: this});
       g.parent = global;
--- a/js/src/jit-test/tests/debug/bug1272908.js
+++ b/js/src/jit-test/tests/debug/bug1272908.js
@@ -1,12 +1,9 @@
-// |jit-test| slow
-
-if (!('oomTest' in this))
-    quit();
+// |jit-test| slow; skip-if: !('oomTest' in this)
 
 // Adapted from randomly chosen test: js/src/jit-test/tests/modules/bug-1233915.js
 g = newGlobal();
 g.parent = this;
 g.eval("(" + function() {
     Debugger(parent).onExceptionUnwind = function(frame) {
     frame.eval("")
     } } + ")()");
--- a/js/src/jit-test/tests/debug/bug1300528.js
+++ b/js/src/jit-test/tests/debug/bug1300528.js
@@ -1,12 +1,11 @@
-load(libdir + "asserts.js");
+// |jit-test| skip-if: helperThreadCount() === 0
 
-if (helperThreadCount() === 0)
-  quit(0);
+load(libdir + "asserts.js");
 
 function BigInteger(a, b, c) {}
 function montConvert(x) {
     var r = new BigInteger(null);
     return r;
 }
 var ba = new Array();
 a = new BigInteger(ba);
--- a/js/src/jit-test/tests/debug/bug1331064.js
+++ b/js/src/jit-test/tests/debug/bug1331064.js
@@ -1,15 +1,12 @@
-// |jit-test| test-also-no-wasm-baseline; exitstatus: 3
+// |jit-test| test-also-no-wasm-baseline; exitstatus: 3; skip-if: !wasmDebuggingIsSupported()
 
 load(libdir + "asserts.js");
 
-if (!wasmDebuggingIsSupported())
-    quit(3);
-
 var g = newGlobal();
 g.parent = this;
 g.eval("new Debugger(parent).onExceptionUnwind = function () {  some_error; };");
 
 var module = new WebAssembly.Module(wasmTextToBinary(`
 (module
     (import $imp "a" "b" (result i32))
     (func $call (result i32) (call 0))
--- a/js/src/jit-test/tests/debug/bug1332493.js
+++ b/js/src/jit-test/tests/debug/bug1332493.js
@@ -1,20 +1,14 @@
-// |jit-test| test-also-no-wasm-baseline; exitstatus: 3
+// |jit-test| test-also-no-wasm-baseline; exitstatus: 3; skip-if: !wasmDebuggingIsSupported()
 // Checking in debug frame is initialized properly during stack overflow.
 
-if (!wasmDebuggingIsSupported())
-    quit(3);
-
 var dbg;
 (function () { dbg = new (newGlobal().Debugger)(this); })();
 
-if (!wasmIsSupported())
-     throw "TestComplete";
-
 var m = new WebAssembly.Module(wasmTextToBinary(`(module
     (import "a" "b" (result f64))
     ;; function that allocated large space for locals on the stack
     (func (export "f") ${Array(200).join("(param f64)")} (result f64) (call 0))
 )`));
 var f = () => i.exports.f();
 var i = new WebAssembly.Instance(m, {a: {b: f}});
 f();
--- a/js/src/jit-test/tests/debug/bug1343579.js
+++ b/js/src/jit-test/tests/debug/bug1343579.js
@@ -1,16 +1,13 @@
-// |jit-test| test-also-no-wasm-baseline
+// |jit-test| test-also-no-wasm-baseline; skip-if: !wasmDebuggingIsSupported()
 // Checking if Debugger.Script.isInCatchScope return false for wasm.
 
 load(libdir + "wasm.js");
 
-if (!wasmDebuggingIsSupported())
-    quit();
-
 var results;
 wasmRunWithDebugger(
     '(module (memory 1) ' +
     '(func $func0 i32.const 1000000 i32.load drop) ' +
     '(func (export "test") call $func0))',
     undefined,
     function ({dbg, wasmScript}) {
         results = [];
--- a/js/src/jit-test/tests/debug/bug1351059.js
+++ b/js/src/jit-test/tests/debug/bug1351059.js
@@ -1,16 +1,15 @@
+// |jit-test| skip-if: !wasmDebuggingIsSupported()
+
 // Tests that onEnterFrame events are enabled when Debugger callbacks set
 // before Instance creation.
 
 load(libdir + "asserts.js");
 
-if (!wasmDebuggingIsSupported())
-  quit();
-
 var g = newGlobal();
 g.parent = this;
 g.onEnterFrameCalled = false;
 g.eval(`
     var dbg = new Debugger(parent);
     dbg.onEnterFrame = frame => {
       onEnterFrameCalled = true;
     };
--- a/js/src/jit-test/tests/debug/bug1370905.js
+++ b/js/src/jit-test/tests/debug/bug1370905.js
@@ -1,12 +1,9 @@
-// |jit-test| allow-oom
-
-if (!('oomTest' in this))
-    quit();
+// |jit-test| allow-oom; skip-if: !('oomTest' in this)
 
 function x() {
     var global = newGlobal({sameZoneAs: this});
     global.eval('function f() { debugger; }');
     var debug = new Debugger(global);
     var foo;
     debug.onDebuggerStatement = function(frame) {
         foo = frame.arguments[0];
--- a/js/src/jit-test/tests/debug/bug1404710.js
+++ b/js/src/jit-test/tests/debug/bug1404710.js
@@ -1,10 +1,9 @@
-if (!('stackTest' in this))
-    quit();
+// |jit-test| skip-if: !('stackTest' in this)
 stackTest(new Function(`
     var g = newGlobal();
     var dbg = new Debugger(g);
     dbg.onDebuggerStatement = function (frame) {
         frame.evalWithBindings("x", {x: 2}).return;
     };
     g.eval("function f(y) { debugger; }");
     g.f(3);
--- a/js/src/jit-test/tests/debug/bug1434391.js
+++ b/js/src/jit-test/tests/debug/bug1434391.js
@@ -1,9 +1,8 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 var g = newGlobal();
 var dbg = new Debugger();
 var gw = dbg.addDebuggee(g);
 oomTest(function () {
     assertEq(gw.executeInGlobal("(42).toString(0)").throw.errorMessageName, "JSMSG_BAD_RADIX");
 });
--- a/js/src/jit-test/tests/debug/inspect-wrapped-promise.js
+++ b/js/src/jit-test/tests/debug/inspect-wrapped-promise.js
@@ -1,11 +1,8 @@
-if (typeof Promise === "undefined")
-    quit(0);
-
 load(libdir + "asserts.js");
 
 let g = newGlobal();
 let dbg = new Debugger();
 let gw = dbg.addDebuggee(g);
 
 g.promise1 = new Promise(() => {});
 g.promise2 = Promise.resolve(42);
--- a/js/src/jit-test/tests/debug/onNewScript-off-main-thread-01.js
+++ b/js/src/jit-test/tests/debug/onNewScript-off-main-thread-01.js
@@ -1,12 +1,11 @@
-// We still get onNewScript notifications for code compiled off the main thread.
+// |jit-test| skip-if: helperThreadCount() === 0
 
-if (helperThreadCount() === 0)
-  quit(0);
+// We still get onNewScript notifications for code compiled off the main thread.
 
 var g = newGlobal();
 var dbg = new Debugger(g);
 
 var log;
 dbg.onNewScript = function (s) {
   log += 's';
   assertEq(s.source.text, '"t" + "wine"');
--- a/js/src/jit-test/tests/debug/onNewScript-off-main-thread-02.js
+++ b/js/src/jit-test/tests/debug/onNewScript-off-main-thread-02.js
@@ -1,10 +1,9 @@
-if (helperThreadCount() === 0)
-  quit(0);
+// |jit-test| skip-if: helperThreadCount() === 0
 
 var global = newGlobal();
 var dbg = new Debugger(global);
 
 dbg.onNewScript = function (s) {
   if (s.url === "<string>")
     assertEq(s.getChildScripts().length, 1);
 };
--- a/js/src/jit-test/tests/debug/wasm-01.js
+++ b/js/src/jit-test/tests/debug/wasm-01.js
@@ -1,12 +1,11 @@
-// Tests that wasm module scripts are available via findScripts.
+// |jit-test| skip-if: !wasmDebuggingIsSupported()
 
-if (!wasmDebuggingIsSupported())
-  quit();
+// Tests that wasm module scripts are available via findScripts.
 
 var g = newGlobal();
 g.eval(`o = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary('(module (func) (export "" 0))')));`);
 
 function isWasm(script) { return script.format === "wasm"; }
 
 var dbg = new Debugger(g);
 var foundScripts1 = dbg.findScripts().filter(isWasm);
--- a/js/src/jit-test/tests/debug/wasm-02.js
+++ b/js/src/jit-test/tests/debug/wasm-02.js
@@ -1,12 +1,11 @@
-// Tests that wasm module scripts are available via onNewScript.
+// |jit-test| skip-if: !wasmDebuggingIsSupported()
 
-if (!wasmDebuggingIsSupported())
-  quit();
+// Tests that wasm module scripts are available via onNewScript.
 
 var g = newGlobal();
 var dbg = new Debugger(g);
 
 var gotScript;
 dbg.onNewScript = (script) => {
   gotScript = script;
 }
--- a/js/src/jit-test/tests/debug/wasm-03.js
+++ b/js/src/jit-test/tests/debug/wasm-03.js
@@ -1,15 +1,14 @@
+// |jit-test| skip-if: !wasmDebuggingIsSupported()
+
 // Tests that wasm module scripts have synthesized sources.
 
 load(libdir + "asserts.js");
 
-if (!wasmDebuggingIsSupported())
-  quit();
-
 var g = newGlobal();
 var dbg = new Debugger(g);
 
 var s;
 dbg.onNewScript = (script) => {
   s = script;
 }
 
--- a/js/src/jit-test/tests/debug/wasm-04.js
+++ b/js/src/jit-test/tests/debug/wasm-04.js
@@ -1,15 +1,14 @@
+// |jit-test| skip-if: !wasmDebuggingIsSupported()
+
 // Tests that wasm module scripts throw for everything except text.
 
 load(libdir + "asserts.js");
 
-if (!wasmDebuggingIsSupported())
-  quit();
-
 var g = newGlobal();
 var dbg = new Debugger(g);
 
 var s;
 dbg.onNewScript = (script) => {
   s = script;
 }
 
--- a/js/src/jit-test/tests/debug/wasm-06-onEnterFrame-null.js
+++ b/js/src/jit-test/tests/debug/wasm-06-onEnterFrame-null.js
@@ -1,16 +1,13 @@
-// |jit-test| test-also-no-wasm-baseline; exitstatus: 3
+// |jit-test| test-also-no-wasm-baseline; exitstatus: 3; skip-if: !wasmDebuggingIsSupported()
 // Checking resumption values for 'null' at onEnterFrame.
 
 load(libdir + "asserts.js");
 
-if (!wasmDebuggingIsSupported())
-     quit(3);
-
 var g = newGlobal('');
 var dbg = new Debugger();
 dbg.addDebuggee(g);
 sandbox.eval(`
 var wasm = wasmTextToBinary('(module (func (nop)) (export "test" 0))');
 var m = new WebAssembly.Instance(new WebAssembly.Module(wasm));`);
 dbg.onEnterFrame = function (frame) {
     if (frame.type !== "wasmcall") return;
--- a/js/src/jit-test/tests/debug/wasm-06-onPop-null.js
+++ b/js/src/jit-test/tests/debug/wasm-06-onPop-null.js
@@ -1,16 +1,13 @@
-// |jit-test| test-also-no-wasm-baseline; exitstatus: 3
+// |jit-test| test-also-no-wasm-baseline; exitstatus: 3; skip-if: !wasmDebuggingIsSupported()
 // Checking resumption values for 'null' at frame's onPop.
 
 load(libdir + "asserts.js");
 
-if (!wasmDebuggingIsSupported())
-    quit(3);
-
 var g = newGlobal('');
 var dbg = new Debugger();
 dbg.addDebuggee(g);
 sandbox.eval(`
 var wasm = wasmTextToBinary('(module (func (nop)) (export "test" 0))');
 var m = new WebAssembly.Instance(new WebAssembly.Module(wasm));`);
 dbg.onEnterFrame = function (frame) {
     if (frame.type !== "wasmcall") return;
--- a/js/src/jit-test/tests/debug/wasm-06.js
+++ b/js/src/jit-test/tests/debug/wasm-06.js
@@ -1,16 +1,13 @@
-// |jit-test| test-also-no-wasm-baseline; error: TestComplete
+// |jit-test| test-also-no-wasm-baseline; error: TestComplete; skip-if: !wasmDebuggingIsSupported()
 // Tests that wasm module scripts raises onEnterFrame and onLeaveFrame events.
 
 load(libdir + "asserts.js");
 
-if (!wasmDebuggingIsSupported())
-    throw "TestComplete";
-
 function runWasmWithDebugger(wast, lib, init, done) {
     let g = newGlobal('');
     let dbg = new Debugger(g);
 
     g.eval(`
 var wasm = wasmTextToBinary('${wast}');
 var lib = ${lib || 'undefined'};
 var m = new WebAssembly.Instance(new WebAssembly.Module(wasm), lib);`);
--- a/js/src/jit-test/tests/debug/wasm-07.js
+++ b/js/src/jit-test/tests/debug/wasm-07.js
@@ -1,18 +1,16 @@
-// |jit-test| test-also-no-wasm-baseline
+// |jit-test| test-also-no-wasm-baseline; skip-if: !wasmDebuggingIsSupported()
+
 // Checking existence of all frame.offset references during onEnterFrame,
 // onLeaveFrame and onStep events in the source code, and that we can
 // potentially resolve offset back to the line/column.
 
 load(libdir + "wasm.js");
 
-if (!wasmDebuggingIsSupported())
-    quit();
-
 var offsets;
 wasmRunWithDebugger(
     '(module (func (nop) (nop)) (export "test" 0))',
     undefined,
     function ({dbg}) {
         offsets = [];
         dbg.onEnterFrame = function (frame) {
             if (frame.type != 'wasmcall') {
--- a/js/src/jit-test/tests/debug/wasm-08.js
+++ b/js/src/jit-test/tests/debug/wasm-08.js
@@ -1,16 +1,13 @@
-// |jit-test| test-also-no-wasm-baseline
+// |jit-test| test-also-no-wasm-baseline; skip-if: !wasmDebuggingIsSupported()
 // Checking if we destroying work registers by breakpoint/step handler.
 
 load(libdir + "wasm.js");
 
-if (!wasmDebuggingIsSupported())
-    quit();
-
 // Running the following code compiled from C snippet:
 //
 //     signed func0(signed n) {
 //         double a = 1; float b = 0; signed c = 1; long long d = 1;
 //         for (;n > 0; n--) {
 //             a *= c; b += c; c++; d <<= 1;
 //         }
 //         return (signed)a + (signed)b + c + (signed)d;
--- a/js/src/jit-test/tests/debug/wasm-09.js
+++ b/js/src/jit-test/tests/debug/wasm-09.js
@@ -1,16 +1,13 @@
-// |jit-test| test-also-no-wasm-baseline
+// |jit-test| test-also-no-wasm-baseline; skip-if: !wasmDebuggingIsSupported()
 // Tests debugEnabled state of wasm when allowUnobservedAsmJS == true.
 
 load(libdir + "asserts.js");
 
-if (!wasmDebuggingIsSupported())
-    quit();
-
 // Checking that there are no offsets are present in a wasm instance script for
 // which debug mode was not enabled.
 function getWasmScriptWithoutAllowUnobservedAsmJS(wast) {
     var sandbox = newGlobal('');
     var dbg = new Debugger();
     dbg.allowUnobservedAsmJS = true;
     dbg.addDebuggee(sandbox);
     sandbox.eval(`
--- a/js/src/jit-test/tests/debug/wasm-10.js
+++ b/js/src/jit-test/tests/debug/wasm-10.js
@@ -1,17 +1,14 @@
-// |jit-test| test-also-no-wasm-baseline
+// |jit-test| test-also-no-wasm-baseline; skip-if: !wasmDebuggingIsSupported()
 // Tests that wasm module scripts has inspectable locals.
 
 load(libdir + "wasm.js");
 load(libdir + 'eqArrayHelper.js');
 
-if (!wasmDebuggingIsSupported())
-    quit();
-
 function monitorLocalValues(wast, lib, expected) {
     function setupFrame(frame) {
         var locals = {};
         framesLocals.push(locals);
         frame.environment.names().forEach(n => {
             locals[n] = [frame.environment.getVariable(n)];
         });
         frame.onStep = function () {
--- a/js/src/jit-test/tests/debug/wasm-11.js
+++ b/js/src/jit-test/tests/debug/wasm-11.js
@@ -1,15 +1,13 @@
-// |jit-test| test-also-no-wasm-baseline
+// |jit-test| test-also-no-wasm-baseline; skip-if: !wasmDebuggingIsSupported()
+
 // Test single-stepping where the TLS register can be evicted by a non-trivial
 // function body.
 
-if (!wasmDebuggingIsSupported())
-  quit();
-
 var g = newGlobal();
 g.parent = this;
 g.eval(`
     var dbg = new Debugger(parent);
 `);
 
 var i = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(`
     (module
--- a/js/src/jit-test/tests/debug/wasm-12.js
+++ b/js/src/jit-test/tests/debug/wasm-12.js
@@ -1,13 +1,11 @@
-// |jit-test| test-also-no-wasm-baseline
-// Tests that wasm module scripts have special URLs.
+// |jit-test| test-also-no-wasm-baseline; skip-if: !wasmDebuggingIsSupported()
 
-if (!wasmDebuggingIsSupported())
-  quit();
+// Tests that wasm module scripts have special URLs.
 
 var g = newGlobal();
 var dbg = new Debugger(g);
 g.eval(`
 function initWasm(s) { return new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(s))); }
 o1 = initWasm('(module (func) (export "" 0))');
 o2 = initWasm('(module (func) (func) (export "" 1))');
 `);
--- a/js/src/jit-test/tests/debug/wasm-binary-sources.js
+++ b/js/src/jit-test/tests/debug/wasm-binary-sources.js
@@ -1,16 +1,15 @@
+// |jit-test| skip-if: !wasmDebuggingIsSupported()
+
 // Tests that wasm module scripts have access to binary sources.
 
 load(libdir + "asserts.js");
 load(libdir + "array-compare.js");
 
-if (!wasmDebuggingIsSupported())
-  quit();
-
 var g = newGlobal();
 var dbg = new Debugger(g);
 
 var s;
 dbg.onNewScript = (script) => {
   s = script;
 }
 
--- a/js/src/jit-test/tests/debug/wasm-breakpoint.js
+++ b/js/src/jit-test/tests/debug/wasm-breakpoint.js
@@ -1,16 +1,13 @@
-// |jit-test| test-also-no-wasm-baseline
+// |jit-test| test-also-no-wasm-baseline; skip-if: !wasmDebuggingIsSupported()
 // Tests that wasm module scripts handles basic breakpoint operations.
 
 load(libdir + "wasm.js");
 
-if (!wasmDebuggingIsSupported())
-    quit();
-
 function runTest(wast, initFunc, doneFunc) {
     let g = newGlobal('');
     let dbg = new Debugger(g);
 
     g.eval(`
 var { binary, offsets } = wasmTextToBinary('${wast}', /* offsets */ true);
 var m = new WebAssembly.Instance(new WebAssembly.Module(binary));`);
 
--- a/js/src/jit-test/tests/debug/wasm-get-return.js
+++ b/js/src/jit-test/tests/debug/wasm-get-return.js
@@ -1,17 +1,14 @@
-// |jit-test| test-also-no-wasm-baseline
+// |jit-test| test-also-no-wasm-baseline; skip-if: !wasmDebuggingIsSupported()
 // Tests that wasm frame opPop event can access function resumption value.
 
 load(libdir + "wasm.js");
 load(libdir + 'eqArrayHelper.js');
 
-if (!wasmDebuggingIsSupported())
-  quit();
-
 function monitorFrameOnPopReturns(wast, expected) {
     var values = [];
     wasmRunWithDebugger(
         wast,
         undefined,
         function ({dbg}) {
             dbg.onEnterFrame = function (frame) {
                 if (frame.type != 'wasmcall') return;
--- a/js/src/jit-test/tests/debug/wasm-getAllColumnOffsets.js
+++ b/js/src/jit-test/tests/debug/wasm-getAllColumnOffsets.js
@@ -1,17 +1,15 @@
-// |jit-test| test-also-no-wasm-baseline
+// |jit-test| test-also-no-wasm-baseline; skip-if: !wasmDebuggingIsSupported()
+
 // Tests that wasm module scripts have column and line to bytecode offset
 // information when source text is generated.
 
 load(libdir + "asserts.js");
 
-if (!wasmDebuggingIsSupported())
-    quit();
-
 // Checking if experimental format generates internal source map to binary file
 // by querying debugger scripts getAllColumnOffsets.
 function getAllOffsets(wast) {
   var sandbox = newGlobal('');
   var dbg = new Debugger();
   dbg.addDebuggee(sandbox);
   dbg.allowWasmBinarySource = true;
   sandbox.eval(`
--- a/js/src/jit-test/tests/debug/wasm-onExceptionUnwind-gc.js
+++ b/js/src/jit-test/tests/debug/wasm-onExceptionUnwind-gc.js
@@ -1,11 +1,9 @@
-
-if (!wasmDebuggingIsSupported())
-    quit();
+// |jit-test| skip-if: !wasmDebuggingIsSupported()
 
 var sandbox = newGlobal();
 var dbg = new Debugger(sandbox);
 var counter = 0;
 dbg.onExceptionUnwind = (frame, value) => {
     if (frame.type !== "wasmcall")
         return;
     if (++counter != 2)
--- a/js/src/jit-test/tests/debug/wasm-responseurls.js
+++ b/js/src/jit-test/tests/debug/wasm-responseurls.js
@@ -1,15 +1,12 @@
-// |jit-test| test-also-no-wasm-baseline
+// |jit-test| test-also-no-wasm-baseline; skip-if: !wasmDebuggingIsSupported()
 // Tests that wasm module can accept URL and sourceMapURL from response
 // when instantiateStreaming is used.
 
-if (!wasmDebuggingIsSupported())
-  quit();
-
 try {
     WebAssembly.compileStreaming();
 } catch (err) {
     assertEq(String(err).indexOf("not supported with --no-threads") !== -1, true);
     quit();
 }
 
 load(libdir + "asserts.js");
--- a/js/src/jit-test/tests/debug/wasm-sourceMappingURL.js
+++ b/js/src/jit-test/tests/debug/wasm-sourceMappingURL.js
@@ -1,13 +1,11 @@
-// |jit-test| test-also-no-wasm-baseline
-// Tests that wasm module sourceMappingURL section is parsed.
+// |jit-test| test-also-no-wasm-baseline; skip-if: !wasmDebuggingIsSupported()
 
-if (!wasmDebuggingIsSupported())
-  quit();
+// Tests that wasm module sourceMappingURL section is parsed.
 
 load(libdir + "asserts.js");
 load(libdir + "wasm-binary.js");
 
 var g = newGlobal();
 var dbg = new Debugger(g);
 
 var gotScript;
--- a/js/src/jit-test/tests/debug/wasm-step.js
+++ b/js/src/jit-test/tests/debug/wasm-step.js
@@ -1,16 +1,14 @@
-// |jit-test| test-also-no-wasm-baseline
+// |jit-test| test-also-no-wasm-baseline; skip-if: !wasmDebuggingIsSupported()
+
 // Tests that wasm module scripts raises onEnterFrame and onLeaveFrame events.
 
 load(libdir + "wasm.js");
 
-if (!wasmDebuggingIsSupported())
-    quit();
-
 // Checking if we stop at every wasm instruction during step.
 var onEnterFrameCalled, onLeaveFrameCalled, onStepCalled;
 wasmRunWithDebugger(
     '(module (func (nop) (nop)) (export "test" 0))',
     undefined,
     function ({dbg}) {
         onEnterFrameCalled = 0;
         onLeaveFrameCalled = 0;
--- a/js/src/jit-test/tests/gc/bug-1053676.js
+++ b/js/src/jit-test/tests/gc/bug-1053676.js
@@ -1,10 +1,9 @@
-// |jit-test| --ion-eager;
-if (typeof Symbol !== "function") quit(0);
+// |jit-test| --ion-eager
 
 var x
 (function() {
     x
 }());
 verifyprebarriers();
 x = x * 0
 x = Symbol();
--- a/js/src/jit-test/tests/gc/bug-1108007.js
+++ b/js/src/jit-test/tests/gc/bug-1108007.js
@@ -1,24 +1,23 @@
-// |jit-test| --no-threads; --no-ion; --no-baseline
-if (!("gczeal" in this))
-    quit();
+// |jit-test| --no-threads; --no-ion; --no-baseline; skip-if: !('gczeal' in this)
+
 gczeal(2);
 (function() {
     evaluate(cacheEntry((function() {
         return "".toSource()
     })()), Object.create({ global: newGlobal({ cloneSingletons: true }) }, {
         saveBytecode: {
             value: true
         }
     }))
 })();
 [[0], [0], [0], [0], [0], [0], [0], [0], [0], [0],
- [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], 
- [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], 
- [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], 
- [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], 
- [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], 
+ [0], [0], [0], [0], [0], [0], [0], [0], [0], [0],
+ [0], [0], [0], [0], [0], [0], [0], [0], [0], [0],
+ [0], [0], [0], [0], [0], [0], [0], [0], [0], [0],
  [0], [0], [0], [0], [0], [0], [0], [0], [0], [0],
  [0], [0], [0], [0], [0], [0], [0], [0], [0], [0],
- [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], 
- [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], 
+ [0], [0], [0], [0], [0], [0], [0], [0], [0], [0],
+ [0], [0], [0], [0], [0], [0], [0], [0], [0], [0],
+ [0], [0], [0], [0], [0], [0], [0], [0], [0], [0],
+ [0], [0], [0], [0], [0], [0], [0], [0], [0], [0],
  [0], [0], [0], [0]];
--- a/js/src/jit-test/tests/gc/bug-1123648.js
+++ b/js/src/jit-test/tests/gc/bug-1123648.js
@@ -1,5 +1,4 @@
-if (!("ctypes" in this))
-   quit();
+// |jit-test| skip-if: !('ctypes' in this)
 
 gczeal(14, 1);
 ctypes.FunctionType(ctypes.default_abi, ctypes.void_t, []).ptr;
--- a/js/src/jit-test/tests/gc/bug-1137341.js
+++ b/js/src/jit-test/tests/gc/bug-1137341.js
@@ -1,10 +1,9 @@
-if (helperThreadCount() == 0)
-   quit();
+// |jit-test| skip-if: helperThreadCount() === 0
 
 gczeal(0);
 gc();
 
 schedulegc(this);
 startgc(0, "shrinking");
 var g = newGlobal();
 g.offThreadCompileScript('debugger;', {});
--- a/js/src/jit-test/tests/gc/bug-1138390.js
+++ b/js/src/jit-test/tests/gc/bug-1138390.js
@@ -1,17 +1,9 @@
-if (!("startgc" in this &&
-      "offThreadCompileScript" in this &&
-      "runOffThreadScript" in this))
-{
-    quit();
-}
-
-if (helperThreadCount() == 0)
-    quit();
+// |jit-test| skip-if: helperThreadCount() === 0
 
 gczeal(0);
 gc();
 
 // Start an incremental GC that includes the atoms zone
 startgc(0);
 var g = newGlobal();
 
--- a/js/src/jit-test/tests/gc/bug-1148383.js
+++ b/js/src/jit-test/tests/gc/bug-1148383.js
@@ -1,12 +1,10 @@
 // This testcase tests setting object metadata for objects created from JIT
 // code.
-if (!("getJitCompilerOptions" in this))
-  quit();
 opts = getJitCompilerOptions();
 if (!opts['ion.enable'] || !opts['baseline.enable'])
   quit();
 
 function TestCase() {}
 function reportCompare () {
   var output = "";
   var testcase = new TestCase();
--- a/js/src/jit-test/tests/gc/bug-1155455.js
+++ b/js/src/jit-test/tests/gc/bug-1155455.js
@@ -1,11 +1,9 @@
-// |jit-test| error: TypeError
-if (!("gczeal" in this))
-    quit();
+// |jit-test| error: TypeError; skip-if !('gczeal' in this)
 var g = newGlobal();
 gczeal(10, 2)
 var dbg = Debugger(g);
 dbg.onDebuggerStatement = function (frame1) {
     function hit(frame2) {
       return hit[0] = "mutated";
     }
     var s = frame1.script;
--- a/js/src/jit-test/tests/gc/bug-1161968.js
+++ b/js/src/jit-test/tests/gc/bug-1161968.js
@@ -1,12 +1,11 @@
-// This test case is a simplified version of debug/Source-invisible.js.
+// |jit-test| skip-if: !('gczeal' in this)
 
-if (!'gczeal' in this)
-    quit();
+// This test case is a simplified version of debug/Source-invisible.js.
 
 gczeal(2,21);
 
 var gi = newGlobal();
 gi.eval('function f() {}');
 
 var gv = newGlobal();
 gv.f = gi.f;
--- a/js/src/jit-test/tests/gc/bug-1165966.js
+++ b/js/src/jit-test/tests/gc/bug-1165966.js
@@ -1,8 +1,6 @@
-// |jit-test| --no-ion
-if (!('oomTest' in this))
-    quit();
+// |jit-test| --no-ion; skip-if: !('oomTest' in this)
 
 var g = newGlobal();
 oomTest(function() {
     Debugger(g);
 });
--- a/js/src/jit-test/tests/gc/bug-1171909.js
+++ b/js/src/jit-test/tests/gc/bug-1171909.js
@@ -1,4 +1,3 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 oomTest((function(x) { assertEq(x + y + ex, 25); }));
--- a/js/src/jit-test/tests/gc/bug-1175755.js
+++ b/js/src/jit-test/tests/gc/bug-1175755.js
@@ -1,9 +1,6 @@
-// |jit-test| allow-oom; allow-unhandlable-oom
-
-if (!('oomAfterAllocations' in this))
-    quit();
+// |jit-test| allow-oom; allow-unhandlable-oom; skip-if: !('oomAfterAllocations' in this)
 
 setGCCallback({
     action: "majorGC",
 });
 oomAfterAllocations(50);
--- a/js/src/jit-test/tests/gc/bug-1177778.js
+++ b/js/src/jit-test/tests/gc/bug-1177778.js
@@ -1,10 +1,9 @@
-if (!("setGCCallback" in this))
-    quit();
+// |jit-test| skip-if: !('setGCCallback' in this)
 
 setGCCallback({
     action: "majorGC",
     phases: "both"
 });
 var g = newGlobal();
 var dbg = new Debugger;
 var gw = dbg.addDebuggee(g);
--- a/js/src/jit-test/tests/gc/bug-1188290.js
+++ b/js/src/jit-test/tests/gc/bug-1188290.js
@@ -1,16 +1,15 @@
+// |jit-test| skip-if: !this.hasOwnProperty("TypedObject") || typeof minorgc !== 'function'
+
 load(libdir + "immutable-prototype.js");
 
 if (globalPrototypeChainIsMutable())
     this.__proto__ = [];
 
-if (!this.hasOwnProperty("TypedObject") || typeof minorgc !== 'function')
-    quit();
-
 var T = TypedObject;
 var ObjectStruct = new T.StructType({f: T.Object});
 var o = new ObjectStruct();
 
 minorgc();
 
 function writeObject(o, v) {
     o.f = v;
--- a/js/src/jit-test/tests/gc/bug-1191576.js
+++ b/js/src/jit-test/tests/gc/bug-1191576.js
@@ -1,12 +1,9 @@
-// |jit-test| allow-oom
-
-if (!('gczeal' in this && 'oomAfterAllocations' in this))
-    quit();
+// |jit-test| allow-oom; skip-if: !('gczeal' in this && 'oomAfterAllocations' in this)
 
 var lfcode = new Array();
 gczeal(14);
 loadFile(`
 for (let e of Object.values(newGlobal())) {
     if (oomAfterAllocations(100))
         continue;
 }
--- a/js/src/jit-test/tests/gc/bug-1206677.js
+++ b/js/src/jit-test/tests/gc/bug-1206677.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this) || helperThreadCount() === 0)
-  quit(0);
+// |jit-test| skip-if: !('oomTest' in this) || helperThreadCount() === 0
 
 var lfGlobal = newGlobal();
 for (lfLocal in this) {
     if (!(lfLocal in lfGlobal)) {
         lfGlobal[lfLocal] = this[lfLocal];
     }
 }
 const script = 'oomTest(() => getBacktrace({args: true, locals: "123795", thisprops: true}));';
--- a/js/src/jit-test/tests/gc/bug-1208994.js
+++ b/js/src/jit-test/tests/gc/bug-1208994.js
@@ -1,4 +1,3 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 oomTest(() => getBacktrace({args: oomTest[load+1], locals: true, thisprops: true}));
--- a/js/src/jit-test/tests/gc/bug-1209001.js
+++ b/js/src/jit-test/tests/gc/bug-1209001.js
@@ -1,4 +1,3 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 oomTest(() => parseModule('import v from "mod";'));
--- a/js/src/jit-test/tests/gc/bug-1210607.js
+++ b/js/src/jit-test/tests/gc/bug-1210607.js
@@ -1,9 +1,6 @@
-// |jit-test| allow-oom
-
-if (!('oomAfterAllocations' in this))
-    quit();
+// |jit-test| allow-oom; skip-if: !('oomAfterAllocations' in this)
 
 var g = newGlobal();
 x = Debugger(g);
 selectforgc(g);
 oomAfterAllocations(1);
--- a/js/src/jit-test/tests/gc/bug-1214006.js
+++ b/js/src/jit-test/tests/gc/bug-1214006.js
@@ -1,10 +1,7 @@
-// |jit-test| allow-oom
-
-if (!('oomTest' in this))
-    quit();
+// |jit-test| allow-oom; skip-if: !('oomTest' in this)
 
 function f() {
     eval("(function() y)()");
 }
 oomTest(f);
 fullcompartmentchecks(true);
--- a/js/src/jit-test/tests/gc/bug-1214781.js
+++ b/js/src/jit-test/tests/gc/bug-1214781.js
@@ -1,9 +1,7 @@
-// |jit-test| allow-unhandlable-oom
-if (!('oomTest' in this))
-    quit();
+// |jit-test| allow-unhandlable-oom; skip-if: !('oomTest' in this)
 
 try {
     gcparam("maxBytes", gcparam("gcBytes"));
     newGlobal("");
 } catch (e) {};
 oomTest(function() {})
--- a/js/src/jit-test/tests/gc/bug-1214846.js
+++ b/js/src/jit-test/tests/gc/bug-1214846.js
@@ -1,7 +1,6 @@
-if (!('oomTest' in this) || helperThreadCount() === 0)
-    quit();
+// |jit-test| skip-if: !('oomTest' in this) || helperThreadCount() === 0
 
 enableGeckoProfiling();
 var s = newGlobal();
 s.offThreadCompileScript('oomTest(() => {});');
 s.runOffThreadScript();
--- a/js/src/jit-test/tests/gc/bug-1215363-1.js
+++ b/js/src/jit-test/tests/gc/bug-1215363-1.js
@@ -1,4 +1,3 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 oomTest(() => parseModule(10));
--- a/js/src/jit-test/tests/gc/bug-1215363-2.js
+++ b/js/src/jit-test/tests/gc/bug-1215363-2.js
@@ -1,7 +1,6 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 var lfcode = new Array();
 oomTest((function(x) {
     assertEq(...Object);
 }));
--- a/js/src/jit-test/tests/gc/bug-1215363-3.js
+++ b/js/src/jit-test/tests/gc/bug-1215363-3.js
@@ -1,5 +1,4 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 var lfcode = new Array();
 oomTest(() => getBacktrace({}));
--- a/js/src/jit-test/tests/gc/bug-1216607.js
+++ b/js/src/jit-test/tests/gc/bug-1216607.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 enableGeckoProfilingWithSlowAssertions();
 try {
 (function() {
    while (n--) {
    }
 })();
 } catch(exc1) {}
--- a/js/src/jit-test/tests/gc/bug-1221359.js
+++ b/js/src/jit-test/tests/gc/bug-1221359.js
@@ -1,7 +1,6 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 oomTest(() => getBacktrace({
     locals: true,
     thisprops: true
 }));
--- a/js/src/jit-test/tests/gc/bug-1221747.js
+++ b/js/src/jit-test/tests/gc/bug-1221747.js
@@ -1,8 +1,6 @@
-// |jit-test| --dump-bytecode
-if (!('oomTest' in this))
-    quit();
+// |jit-test| --dump-bytecode; skip-if: !('oomTest' in this)
 
 function f() {
     eval("(function() {})()");
 }
 oomTest(f);
--- a/js/src/jit-test/tests/gc/bug-1223021.js
+++ b/js/src/jit-test/tests/gc/bug-1223021.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 function f() {
     return this === null;
 };
 
 function g() {
     if (!f.apply(9)) {}
 }
--- a/js/src/jit-test/tests/gc/bug-1224710.js
+++ b/js/src/jit-test/tests/gc/bug-1224710.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 oomTest(function() {
     eval("\
 	function g() {\
 	    \"use asm\";\
 	    function f(d) {\
 		d = +d;\
 		print(.0 + d);\
--- a/js/src/jit-test/tests/gc/bug-1226888.js
+++ b/js/src/jit-test/tests/gc/bug-1226888.js
@@ -1,10 +1,9 @@
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
+// |jit-test| skip-if: !this.hasOwnProperty("TypedObject")
 
 setJitCompilerOption('ion.forceinlineCaches', 1);
 // Adapted from randomly chosen test: js/src/jit-test/tests/TypedObject/jit-write-references.js
 with({}) {}
 v = new new TypedObject.StructType({
     f: TypedObject.Any
 })
 gc();
--- a/js/src/jit-test/tests/gc/bug-1226896.js
+++ b/js/src/jit-test/tests/gc/bug-1226896.js
@@ -1,9 +1,6 @@
-// |jit-test| --ion-pgo=on
-
-if (!('oomTest' in this))
-   quit();
+// |jit-test| --ion-pgo=on; skip-if: !('oomTest' in this)
 
 oomTest(() => {
     var g = newGlobal({sameZoneAs: this});
     g.eval("(function() {})()");
 });
--- a/js/src/jit-test/tests/gc/bug-1231386.js
+++ b/js/src/jit-test/tests/gc/bug-1231386.js
@@ -1,12 +1,9 @@
-// |jit-test| slow
-
-if (!('oomTest' in this))
-    quit();
+// |jit-test| slow; skip-if: !('oomTest' in this)
 
 function f1() {}
 function f2() {}
 r = [function() {}, function() {}, [], function() {}, f1, function() {}, f2];
 l = [0];
 function f3() {
     return {
         a: 0
--- a/js/src/jit-test/tests/gc/bug-1232386.js
+++ b/js/src/jit-test/tests/gc/bug-1232386.js
@@ -1,11 +1,9 @@
-// |jit-test| allow-oom
-if (!('oomTest' in this))
-    quit();
+// |jit-test| allow-oom; skip-if: !('oomTest' in this)
 
 var dbg = new Debugger;
 dbg.onNewGlobalObject = function(global) {
     global.seen = true;
 };
 
 oomTest(function() {
     newGlobal({sameZoneAs: this})
--- a/js/src/jit-test/tests/gc/bug-1234410.js
+++ b/js/src/jit-test/tests/gc/bug-1234410.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 enableGeckoProfiling();
 oomTest(() => {
     try {
         for (var quit of oomTest.gcparam("//").ArrayBuffer(1)) {}
     } catch (e) {}
 });
 
--- a/js/src/jit-test/tests/gc/bug-1236473.js
+++ b/js/src/jit-test/tests/gc/bug-1236473.js
@@ -1,7 +1,6 @@
-if (!('oomTest' in this))
-  quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 oomTest(() => {
     offThreadCompileScript(`try {} catch (NaN) {}`);
     runOffThreadScript();
 });
--- a/js/src/jit-test/tests/gc/bug-1238555.js
+++ b/js/src/jit-test/tests/gc/bug-1238555.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this))
-  quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 oomTest(
   function x() {
     try {
       eval('let ')
     } catch (ex) {
       (function() {})()
     }
--- a/js/src/jit-test/tests/gc/bug-1238575-2.js
+++ b/js/src/jit-test/tests/gc/bug-1238575-2.js
@@ -1,4 +1,3 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 oomTest(() => evalInWorker("1"));
--- a/js/src/jit-test/tests/gc/bug-1238575.js
+++ b/js/src/jit-test/tests/gc/bug-1238575.js
@@ -1,8 +1,5 @@
-// |jit-test| allow-oom; allow-unhandlable-oom
-
-if (!('oomAfterAllocations' in this))
-    quit();
+// |jit-test| allow-oom; allow-unhandlable-oom; skip-if: !('oomAfterAllocations' in this)
 
 oomAfterAllocations(5)
 gcslice(11);
 evalInWorker("1");
--- a/js/src/jit-test/tests/gc/bug-1238582.js
+++ b/js/src/jit-test/tests/gc/bug-1238582.js
@@ -1,4 +1,3 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 oomTest(() => { let a = [2147483651]; [a[0], a[undefined]].sort(); });
--- a/js/src/jit-test/tests/gc/bug-1240503.js
+++ b/js/src/jit-test/tests/gc/bug-1240503.js
@@ -1,8 +1,7 @@
-if (!('oomTest' in this))
-  quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 function arrayProtoOutOfRange() {
     for (let [] = () => r, get;;)
         var r = f(i % 2 ? a : b);
 }
 oomTest(arrayProtoOutOfRange);
--- a/js/src/jit-test/tests/gc/bug-1240527.js
+++ b/js/src/jit-test/tests/gc/bug-1240527.js
@@ -1,14 +1,7 @@
-if (typeof offThreadCompileScript !== 'function' ||
-    typeof runOffThreadScript !== 'function' ||
-    typeof oomTest !== 'function' ||
-    typeof fullcompartmentchecks !== 'function' ||
-    helperThreadCount() === 0)
-{
-    quit(0);
-}
+// |jit-test| skip-if: helperThreadCount() === 0 || !('oomTest' in this)
 
 offThreadCompileScript(`
  oomTest(() => "".search(/d/));
  fullcompartmentchecks(3);
 `);
 runOffThreadScript();
--- a/js/src/jit-test/tests/gc/bug-1241731.js
+++ b/js/src/jit-test/tests/gc/bug-1241731.js
@@ -1,4 +1,3 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 oomTest(() => serialize(0, [{}]));
--- a/js/src/jit-test/tests/gc/bug-1242812.js
+++ b/js/src/jit-test/tests/gc/bug-1242812.js
@@ -1,5 +1,4 @@
-if (!('oomTest' in this))
-   quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 var lfcode = new Array();
 oomTest(() => { let a = [2147483651]; [-1, 0, 1, 31, 32].sort(); });
--- a/js/src/jit-test/tests/gc/bug-1245520.js
+++ b/js/src/jit-test/tests/gc/bug-1245520.js
@@ -1,5 +1,4 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 var t = {};
 oomTest(() => serialize(t));
--- a/js/src/jit-test/tests/gc/bug-1252103.js
+++ b/js/src/jit-test/tests/gc/bug-1252103.js
@@ -1,14 +1,13 @@
+// |jit-test| skip-if: !this.hasOwnProperty("TypedObject")
+
 // Bug 1252103: Inline typed array objects need delayed metadata collection.
 // Shouldn't crash.
 
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
-
 function foo() {
     enableTrackAllocations();
     gczeal(2, 10);
     TO = TypedObject;
     PointType = new TO.StructType({
         y: TO.float64,
         name: TO.string
     })
--- a/js/src/jit-test/tests/gc/bug-1252154.js
+++ b/js/src/jit-test/tests/gc/bug-1252154.js
@@ -1,11 +1,10 @@
+// |jit-test| skip-if: !this.hasOwnProperty("TypedObject")
+
 // Bug 1252154: Inline typed array objects need delayed metadata collection.
 // Shouldn't crash.
 
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
-
 gczeal(7,1);
 enableShellAllocationMetadataBuilder();
 var T = TypedObject;
 var AT = new T.ArrayType(T.Any,10);
 var v = new AT();
--- a/js/src/jit-test/tests/gc/bug-1252329.js
+++ b/js/src/jit-test/tests/gc/bug-1252329.js
@@ -1,12 +1,9 @@
-// |jit-test| allow-oom
-
-if (helperThreadCount() == 0 || !('oomAfterAllocations' in this))
-    quit();
+// |jit-test| allow-oom; skip-if: helperThreadCount() == 0 || !('oomAfterAllocations' in this)
 
 var lfcode = new Array();
 lfcode.push("5");
 lfcode.push(`
 gczeal(8, 2);
 try {
     [new String, y]
 } catch (e) {}
--- a/js/src/jit-test/tests/gc/bug-1253124.js
+++ b/js/src/jit-test/tests/gc/bug-1253124.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 for (let i = 0; i < 10; i++)
     toPrimitive = Date.prototype[Symbol.toPrimitive];
 assertThrowsInstanceOf(() =>  0);
 obj = {};
 oomTest(() => assertThrowsInstanceOf(() => toPrimitive.call(obj, "boolean")));
 function assertThrowsInstanceOf(f) {
     f();
--- a/js/src/jit-test/tests/gc/bug-1259306.js
+++ b/js/src/jit-test/tests/gc/bug-1259306.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 let runCount = 0;
 oomTest(() => {
     if (runCount < 5) {
         let lfGlobal = newGlobal();
         var lfVarx = `
             gczeal(8, 1);
             try {
--- a/js/src/jit-test/tests/gc/bug-1261329.js
+++ b/js/src/jit-test/tests/gc/bug-1261329.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 print = function() {}
 function k() { return dissrc(print); }
 function j() { return k(); }
 function h() { return j(); }
 function f() { return h(); }
 f();
 oomTest(() => f())
--- a/js/src/jit-test/tests/gc/bug-1263862.js
+++ b/js/src/jit-test/tests/gc/bug-1263862.js
@@ -1,9 +1,8 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 function loadFile(lfVarx) {
   oomTest(() => eval(lfVarx));
 }
 for (var i = 0; i < 10; ++i) {
   loadFile(`"use strict"; const s = () => s;`);
 }
--- a/js/src/jit-test/tests/gc/bug-1263871.js
+++ b/js/src/jit-test/tests/gc/bug-1263871.js
@@ -1,9 +1,8 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 lfLogBuffer = `this[''] = function() {}`;
 loadFile(lfLogBuffer);
 loadFile(lfLogBuffer);
 function loadFile(lfVarx) {
     return oomTest(function() { return parseModule(lfVarx); });
 }
--- a/js/src/jit-test/tests/gc/bug-1263884.js
+++ b/js/src/jit-test/tests/gc/bug-1263884.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 oomTest(function() {
   eval(`
     var argObj = function () { return arguments }()
     for (var p in argObj);
     delete argObj.callee;
   `);
 });
--- a/js/src/jit-test/tests/gc/bug-1271110.js
+++ b/js/src/jit-test/tests/gc/bug-1271110.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 gczeal(0);
 
 var x1 = [];
 var x2 = [];
 var x3 = [];
 var x4 = [];
 (function() {
--- a/js/src/jit-test/tests/gc/bug-1280588.js
+++ b/js/src/jit-test/tests/gc/bug-1280588.js
@@ -1,6 +1,5 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 var x = [];
 oomTest(() => setGCCallback({ action: "minorGC" }));
 oomTest(() => setGCCallback({ action: "majorGC" }));
--- a/js/src/jit-test/tests/gc/bug-1280889.js
+++ b/js/src/jit-test/tests/gc/bug-1280889.js
@@ -1,9 +1,8 @@
-if (helperThreadCount() == 0)
-    quit();
+// |jit-test| skip-if: helperThreadCount() === 0
 
 evalInWorker(`
   function f() {
     fullcompartmentchecks(f);
   }
   try { f(); } catch(e) {}
 `);
--- a/js/src/jit-test/tests/gc/bug-1282986.js
+++ b/js/src/jit-test/tests/gc/bug-1282986.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 var lfLogBuffer = `
 evalInWorker(\`
         try { oomAfterAllocations(2); } catch(e) {}
     \`);
 `;
 loadFile("");
 loadFile(lfLogBuffer);
--- a/js/src/jit-test/tests/gc/bug-1286244.js
+++ b/js/src/jit-test/tests/gc/bug-1286244.js
@@ -1,16 +1,8 @@
-if (typeof verifyprebarriers != 'function' ||
-    typeof offThreadCompileScript != 'function')
-    quit();
+// |jit-test| skip-if: !getBuildConfiguration()['has-gczeal'] || helperThreadCount() === 0
 
-try {
-    // This will fail with --no-threads.
-    verifyprebarriers();
-    var lfGlobal = newGlobal();
-    lfGlobal.offThreadCompileScript(`
-      version(185);
-    `);
-}
-catch (e) {
-    quit(0);
-}
-
+// This will fail with --no-threads.
+verifyprebarriers();
+var lfGlobal = newGlobal();
+lfGlobal.offThreadCompileScript(`
+  version(185);
+`);
--- a/js/src/jit-test/tests/gc/bug-1287399.js
+++ b/js/src/jit-test/tests/gc/bug-1287399.js
@@ -1,10 +1,9 @@
-if (typeof gczeal != "function" || helperThreadCount() === 0)
-	quit(0);
+// |jit-test| skip-if: typeof gczeal !== 'function' || helperThreadCount() === 0
 
 var lfGlobal = newGlobal();
 gczeal(4);
 for (lfLocal in this) {}
 lfGlobal.offThreadCompileScript(`
   var desc = {
     value: 'bar',
     value: false,
--- a/js/src/jit-test/tests/gc/bug-1287869.js
+++ b/js/src/jit-test/tests/gc/bug-1287869.js
@@ -1,8 +1,7 @@
-if (!('gczeal' in this))
-    quit();
+// |jit-test| skip-if: !('gczeal' in this)
 
 gczeal(16);
 let a = [];
 for (let i = 0; i < 1000; i++)
     a.push({x: i});
 gc();
--- a/js/src/jit-test/tests/gc/bug-1292564.js
+++ b/js/src/jit-test/tests/gc/bug-1292564.js
@@ -1,12 +1,9 @@
-// |jit-test| allow-oom
-
-if (!('oomTest' in this))
-    quit();
+// |jit-test| allow-oom; skip-if: !('oomTest' in this)
 
 oomTest(() => {
     let global = newGlobal({sameZoneAs: this});
     Debugger(global).onDebuggerStatement = function (frame) {
         frame.eval("f")
     }
     global.eval("debugger")
 }, false);
--- a/js/src/jit-test/tests/gc/bug-1298356.js
+++ b/js/src/jit-test/tests/gc/bug-1298356.js
@@ -1,7 +1,6 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 /x/;
 oomTest(function(){
     offThreadCompileScript('');
 })
--- a/js/src/jit-test/tests/gc/bug-1301496.js
+++ b/js/src/jit-test/tests/gc/bug-1301496.js
@@ -1,8 +1,7 @@
-if (helperThreadCount() == 0)
-    quit();
+// |jit-test| skip-if: helperThreadCount() === 0
 gczeal(0);
 startgc(1, 'shrinking');
 offThreadCompileScript("");
 // Adapted from randomly chosen test: js/src/jit-test/tests/parser/bug-1263355-13.js
 gczeal(9);
 newGlobal();
--- a/js/src/jit-test/tests/gc/bug-1303015.js
+++ b/js/src/jit-test/tests/gc/bug-1303015.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 var x = ``.split();
 oomTest(function() {
     var lfGlobal = newGlobal({sameZoneAs: this});
     for (lfLocal in this) {
         if (!(lfLocal in lfGlobal)) {
                 lfGlobal[lfLocal] = this[lfLocal];
         }
--- a/js/src/jit-test/tests/gc/bug-1305220.js
+++ b/js/src/jit-test/tests/gc/bug-1305220.js
@@ -1,11 +1,10 @@
-// |jit-test| allow-oom
-if (!('oomAfterAllocations' in this))
-    quit();
+// |jit-test| allow-oom; skip-if: !('oomAfterAllocations' in this)
+
 s = newGlobal();
 evalcx("\
     gczeal(10, 2);\
     k = {\
         [Symbol]() {}\
     };\
 ", s);
 gczeal(0);
--- a/js/src/jit-test/tests/gc/bug-1308048.js
+++ b/js/src/jit-test/tests/gc/bug-1308048.js
@@ -1,10 +1,9 @@
-if (helperThreadCount() == 0)
-    quit();
+// |jit-test| skip-if: helperThreadCount() === 0
 
 m = 'x';
 for (var i = 0; i < 10; i++)
     m += m;
 offThreadCompileScript("", ({elementAttributeName: m}));
 var n = newGlobal();
 gczeal(2,1);
 n.runOffThreadScript();
--- a/js/src/jit-test/tests/gc/bug-1310589.js
+++ b/js/src/jit-test/tests/gc/bug-1310589.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 a = o = s = r = []
 o2 = s2 = r2 = g2 = f2 = m2 = Map
 e2 = Set
 v2 = b2 = new ArrayBuffer
 t2 = new Uint8ClampedArray
 minorgc()
 x = /x/
--- a/js/src/jit-test/tests/gc/bug-1311060.js
+++ b/js/src/jit-test/tests/gc/bug-1311060.js
@@ -1,3 +1,2 @@
-if (helperThreadCount() === 0)
-   quit();
+// |jit-test| skip-if: helperThreadCount() === 0
 evalInWorker(`schedulegc("s1");`);
--- a/js/src/jit-test/tests/gc/bug-1315946.js
+++ b/js/src/jit-test/tests/gc/bug-1315946.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 // Don't run a full oomTest because it takes ages - a few iterations are
 // sufficient to trigger the bug.
 let i = 0;
 
 oomTest(Function(`
     if (i < 10) {
         i++;
--- a/js/src/jit-test/tests/gc/bug-1322648.js
+++ b/js/src/jit-test/tests/gc/bug-1322648.js
@@ -1,10 +1,9 @@
-if (helperThreadCount() == 0)
-    quit();
+// |jit-test| skip-if: helperThreadCount() === 0
 gczeal(0);
 print = function(s) {}
 startgc(1);
 offThreadCompileScript("");
 gczeal(10, 3);
 for (var count = 0; count < 20; count++) {
     print(count);
 }
--- a/js/src/jit-test/tests/gc/bug-1323868.js
+++ b/js/src/jit-test/tests/gc/bug-1323868.js
@@ -1,7 +1,6 @@
-// |jit-test| allow-oom
-if (helperThreadCount() == 0)
-    quit();
+// |jit-test| allow-oom; skip-if: helperThreadCount() === 0
+
 gczeal(0);
 startgc(8301);
 offThreadCompileScript("(({a,b,c}))");
 gcparam("maxBytes", gcparam("gcBytes"));
--- a/js/src/jit-test/tests/gc/bug-1324512.js
+++ b/js/src/jit-test/tests/gc/bug-1324512.js
@@ -1,10 +1,9 @@
-if (helperThreadCount() === 0)
-    quit();
+// |jit-test| skip-if: helperThreadCount() === 0
 
 evalInWorker(`
     if (!('gczeal' in this))
         quit();
     try {
       gczeal(2,1);
       throw new Error();
     } catch (e) {
--- a/js/src/jit-test/tests/gc/bug-1325551.js
+++ b/js/src/jit-test/tests/gc/bug-1325551.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 let g = newGlobal();
 let dbg = new Debugger;
 let gw = dbg.addDebuggee(g);
 g.eval("function f(){}");
 oomTest(() => {
     gw.makeDebuggeeValue(g.f).script.source.sourceMapURL = 'a';
 });
--- a/js/src/jit-test/tests/gc/bug-1328251.js
+++ b/js/src/jit-test/tests/gc/bug-1328251.js
@@ -1,10 +1,9 @@
-if (helperThreadCount() == 0)
-    quit();
+// |jit-test| skip-if: helperThreadCount() === 0
 
 evalInWorker(`
     if (!('gczeal' in this))
         quit();
     gczeal(2);
     for (let i = 0; i < 30; i++) {
         var a = [1, 2, 3];
         a.indexOf(1);
--- a/js/src/jit-test/tests/gc/bug-1332773.js
+++ b/js/src/jit-test/tests/gc/bug-1332773.js
@@ -1,10 +1,9 @@
-if (helperThreadCount() == 0)
-    quit();
+// |jit-test| skip-if: helperThreadCount() === 0
 
 evalInWorker(`
 var gTestcases = new Array();
 typeof document != "object" || !document.location.href.match(/jsreftest.html/);
 gczeal(4, 10);
 f = ([a = class target extends b {}, b] = [void 0]) => {};
 f()
 `)
--- a/js/src/jit-test/tests/gc/bug-1340010.js
+++ b/js/src/jit-test/tests/gc/bug-1340010.js
@@ -1,12 +1,9 @@
-if (helperThreadCount() === 0)
-    quit();
-if (!('deterministicgc' in this))
-    quit();
+// |jit-test| skip-if: helperThreadCount() === 0 || !('deterministicgc' in this)
 gczeal(0);
 
 gc();
 function weighted(wa) {
     var a = [];
     for (var i = 0; i < 30; ++i) {
         for (var j = 0; j < 20; ++j) {
             a.push(0);
--- a/js/src/jit-test/tests/gc/bug-1371908.js
+++ b/js/src/jit-test/tests/gc/bug-1371908.js
@@ -1,6 +1,6 @@
-if (helperThreadCount() === 0)
-    quit();
+// |jit-test| skip-if: helperThreadCount() === 0
+
 gczeal(0);
 offThreadCompileScript("");
 startgc(0);
 runOffThreadScript();
--- a/js/src/jit-test/tests/gc/bug-1374797.js
+++ b/js/src/jit-test/tests/gc/bug-1374797.js
@@ -1,12 +1,11 @@
-// Exercise triggering GC of atoms zone while off-thread parsing is happening.
+// |jit-test| skip-if: helperThreadCount() === 0
 
-if (helperThreadCount() === 0)
-   quit();
+// Exercise triggering GC of atoms zone while off-thread parsing is happening.
 
 gczeal(0);
 
 // Reduce some GC parameters so that we can trigger a GC more easily.
 gcparam('lowFrequencyHeapGrowth', 120);
 gcparam('highFrequencyHeapGrowthMin', 120);
 gcparam('highFrequencyHeapGrowthMax', 120);
 gcparam('allocationThreshold', 1);
--- a/js/src/jit-test/tests/gc/bug-1382431.js
+++ b/js/src/jit-test/tests/gc/bug-1382431.js
@@ -1,6 +1,5 @@
-if (helperThreadCount() === 0)
-   quit();
+// |jit-test| skip-if: helperThreadCount() === 0
 
 var fe = "vv";
 for (i = 0; i < 24; i++) fe += fe;
 offThreadCompileScript(fe, {});
--- a/js/src/jit-test/tests/gc/bug-1384047.js
+++ b/js/src/jit-test/tests/gc/bug-1384047.js
@@ -1,5 +1,4 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 newGlobal();
 evalcx("oomTest(newGlobal);", newGlobal());
--- a/js/src/jit-test/tests/gc/bug-1390087.js
+++ b/js/src/jit-test/tests/gc/bug-1390087.js
@@ -1,10 +1,9 @@
-if (helperThreadCount() === 0)
-    quit();
+// |jit-test| skip-if: helperThreadCount() === 0
 
 setGCCallback({
     action: "majorGC"
 });
 gcparam("allocationThreshold", 1);
 offThreadCompileScript("");
 for (let i = 0; i < 40000; i++)
     Symbol.for(i);
--- a/js/src/jit-test/tests/gc/bug-1401141.js
+++ b/js/src/jit-test/tests/gc/bug-1401141.js
@@ -1,12 +1,9 @@
-if (!('gczeal' in this))
-    quit();
-if (helperThreadCount() == 0)
-    quit();
+// |jit-test| skip-if: !('gczeal' in this) || helperThreadCount() === 0
 
 gczeal(15,1);
 setGCCallback({
   action: "majorGC",
 });
 gcslice(3);
 var lfGlobal = newGlobal();
 lfGlobal.offThreadCompileScript("");
--- a/js/src/jit-test/tests/gc/bug-1411302.js
+++ b/js/src/jit-test/tests/gc/bug-1411302.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 let lfPreamble = `
     value:{
 `;
 try {
     evaluate("");
     evalInWorker("");
 } catch (exc) {}
--- a/js/src/jit-test/tests/gc/bug-1435295.js
+++ b/js/src/jit-test/tests/gc/bug-1435295.js
@@ -1,11 +1,8 @@
-if (helperThreadCount() === 0)
-    quit();
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: helperThreadCount() === 0 || !('oomTest' in this)
 
 oomTest(new Function(`function execOffThread(source) {
     offThreadCompileModule(source);
     return finishOffThreadModule();
 }
 b = execOffThread("[1, 2, 3]")
 `));
--- a/js/src/jit-test/tests/gc/bug-1439284.js
+++ b/js/src/jit-test/tests/gc/bug-1439284.js
@@ -1,10 +1,10 @@
-if (helperThreadCount() === 0)
-    quit();
+// |jit-test| skip-if: helperThreadCount() === 0
+
 gcparam('allocationThreshold', 1);
 setGCCallback({
     action: "majorGC",
 });
 offThreadCompileScript(('Boolean.prototype.toSource.call(new String())'));
 for (let i = 0; i < 10; i++) {
     for (let j = 0; j < 10000; j++) Symbol.for(i + 10 * j);
 }
--- a/js/src/jit-test/tests/gc/bug-1449887.js
+++ b/js/src/jit-test/tests/gc/bug-1449887.js
@@ -1,4 +1,3 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 oomTest(function() { x, 0, { z: function() {} } });
--- a/js/src/jit-test/tests/gc/bug-1456536.js
+++ b/js/src/jit-test/tests/gc/bug-1456536.js
@@ -1,3 +1,3 @@
-if (!('oomTest' in this))
-   quit();
+// |jit-test| skip-if: !('oomTest' in this)
+
 oomTest(new Function(`let a = grayRoot();`));
--- a/js/src/jit-test/tests/gc/bug-1459860.js
+++ b/js/src/jit-test/tests/gc/bug-1459860.js
@@ -1,10 +1,8 @@
-// |jit-test| allow-overrecursed
-if (helperThreadCount() === 0)
-    quit();
+// |jit-test| allow-overrecursed; skip-if: helperThreadCount() === 0
 function eval(source) {
     offThreadCompileModule(source);
     let get = (eval("function w(){}") ++);
 };
 gczeal(21, 10);
 gczeal(11, 8);
 eval("");
--- a/js/src/jit-test/tests/gc/bug-1461027.js
+++ b/js/src/jit-test/tests/gc/bug-1461027.js
@@ -1,7 +1,6 @@
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
+// |jit-test| skip-if: !this.hasOwnProperty("TypedObject")
 
 for (var i = 0; i < 99; i++) {
 	    w = new TypedObject.ArrayType(TypedObject.int32, 100).build(function() {});
 }
 relazifyFunctions();
--- a/js/src/jit-test/tests/gc/bug-1461448.js
+++ b/js/src/jit-test/tests/gc/bug-1461448.js
@@ -1,10 +1,9 @@
-if (helperThreadCount() === 0)
-    quit();
+// |jit-test| skip-if: helperThreadCount() === 0
 
 gczeal(0);
 
 let lfPreamble = `
   var lfOffThreadGlobal = newGlobal();
   for (lfLocal in this)
     try {} catch(lfVare5) {}
 `;
--- a/js/src/jit-test/tests/gc/bug-1462337.js
+++ b/js/src/jit-test/tests/gc/bug-1462337.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 oomTest(function() {
     grayRoot().x = Object.create((obj[name]++));
 });
 oomTest(function() {
     gczeal(9);
     gcslice(new.target);
 });
--- a/js/src/jit-test/tests/gc/bug-1472734.js
+++ b/js/src/jit-test/tests/gc/bug-1472734.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this) || helperThreadCount() === 0)
-    quit();
+// |jit-test| skip-if: !('oomTest' in this) || helperThreadCount() === 0
 
 try {
     oomTest(function() {
       eval(`
         function eval(source) {
           offThreadCompileModule(source);
           minorgc();
         }
--- a/js/src/jit-test/tests/gc/bug-1481093.js
+++ b/js/src/jit-test/tests/gc/bug-1481093.js
@@ -1,8 +1,7 @@
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
+// |jit-test| skip-if: !this.hasOwnProperty("TypedObject")
 
 v = new new TypedObject.StructType({
     f: TypedObject.Any
 })
 gczeal(14);
 var lfOffThreadGlobal = newGlobal();
--- a/js/src/jit-test/tests/gc/bug-1490042.js
+++ b/js/src/jit-test/tests/gc/bug-1490042.js
@@ -1,12 +1,9 @@
-// |jit-test| --no-ion; --no-baseline
-
-if (!('gcstate' in this))
-    quit();
+// |jit-test| --no-ion; --no-baseline; skip-if: !('gcstate' in this)
 
 gczeal(0);
 
 // Create a bunch of ObjectGroups with TypeNewScript attached.
 const count = 1000;
 let c = [];
 let a = [];
 for (let i = 0; i < count; i++) {
--- a/js/src/jit-test/tests/gc/bug-948423.js
+++ b/js/src/jit-test/tests/gc/bug-948423.js
@@ -1,21 +1,20 @@
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
+// |jit-test| skip-if: !this.hasOwnProperty("TypedObject")
 
 var ArrayType = TypedObject.ArrayType;
 var StructType = TypedObject.StructType;
 var uint8 = TypedObject.uint8;
 var uint32 = TypedObject.uint32;
 var ObjectType = TypedObject.Object;
 function runTests() {
   (function DimensionLinkedToUndimension() {
     var FiveUintsA = uint32.array(5);
     var FiveUintsB = uint32.array(5);
-    assertEq(true, 
+    assertEq(true,
 	FiveUintsA.equivalent(FiveUintsB)
 	);
   })();
   (function PrototypeHierarchy() {
     schedulegc(3);
     var Uint8s = uint8.array(5);
   })();
 }
--- a/js/src/jit-test/tests/gc/bug-978802.js
+++ b/js/src/jit-test/tests/gc/bug-978802.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 oomTest(() => {
     try {
         var max = 400;
         function f(b) {
             if (b) {
                 f(b - 1);
             } else {
--- a/js/src/jit-test/tests/gc/bug1146213.js
+++ b/js/src/jit-test/tests/gc/bug1146213.js
@@ -1,20 +1,9 @@
-if (!("setGCCallback" in this &&
-      "schedulegc" in this &&
-      "gcslice" in this &&
-      "newGlobal" in this &&
-      "helperThreadCount" in this))
-{
-    quit();
-}
-
-if (helperThreadCount() == 0)
-    quit();
-
+// |jit-test| skip-if: !(getBuildConfiguration()['has-gczeal']) || helperThreadCount() === 0
 setGCCallback({
   action: "majorGC",
 });
 schedulegc(this)
 gcslice(3)
 var lfGlobal = newGlobal();
 lfGlobal.offThreadCompileScript("");
 lfGlobal.runOffThreadScript();
--- a/js/src/jit-test/tests/gc/bug1191756.js
+++ b/js/src/jit-test/tests/gc/bug1191756.js
@@ -1,10 +1,9 @@
-if (typeof 'oomAtAllocation' === 'undefined')
-    quit();
+// |jit-test| skip-if: typeof 'oomAtAllocation' === 'undefined'
 
 function fn(i) {
     if (i == 3)
       return ["isFinite"].map(function (i) {});
     return [];
 }
 
 try {
--- a/js/src/jit-test/tests/gc/bug1246607.js
+++ b/js/src/jit-test/tests/gc/bug1246607.js
@@ -1,10 +1,9 @@
-if (typeof oomTest !== 'function' || typeof Intl !== 'object')
-    quit();
+// |jit-test| skip-if: typeof oomTest !== 'function' || typeof Intl !== 'object'
 
 oomTest(() => {
     try {
         new Intl.DateTimeFormat;
         x1 = 0;
     }  catch (e) {
         switch (1) {
             case 0:
--- a/js/src/jit-test/tests/gc/bug1283169.js
+++ b/js/src/jit-test/tests/gc/bug1283169.js
@@ -1,5 +1,5 @@
-if (helperThreadCount() === 0)
-    quit(0);
+// |jit-test| skip-if: helperThreadCount() === 0
+
 gczeal(0);
 startgc(45);
 offThreadCompileScript("print(1)");
--- a/js/src/jit-test/tests/gc/bug1285186.js
+++ b/js/src/jit-test/tests/gc/bug1285186.js
@@ -1,6 +1,5 @@
-if (helperThreadCount() === 0)
-    quit();
+// |jit-test| skip-if: helperThreadCount() === 0
 gczeal(10);
 newGlobal();
 offThreadCompileScript("let x = 1;");
 abortgc();
--- a/js/src/jit-test/tests/gc/bug1285490.js
+++ b/js/src/jit-test/tests/gc/bug1285490.js
@@ -1,4 +1,3 @@
-if (helperThreadCount() === 0)
-    quit();
+// |jit-test| skip-if: helperThreadCount() === 0
 gczeal(4);
 offThreadCompileScript("let x = 1;");
--- a/js/src/jit-test/tests/gc/bug1287063.js
+++ b/js/src/jit-test/tests/gc/bug1287063.js
@@ -1,4 +1,4 @@
-if (helperThreadCount() === 0)
-    quit();
+// |jit-test| skip-if: helperThreadCount() === 0
+
 schedulegc("");
 offThreadCompileScript("");
--- a/js/src/jit-test/tests/gc/bug1326343-gcstats.js
+++ b/js/src/jit-test/tests/gc/bug1326343-gcstats.js
@@ -1,7 +1,6 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 setJitCompilerOption('baseline.warmup.trigger', 4);
 oomTest((function () {
     gcslice(0);
 }))
--- a/js/src/jit-test/tests/gc/bug1337324.js
+++ b/js/src/jit-test/tests/gc/bug1337324.js
@@ -1,6 +1,5 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 oomTest(function () {
     offThreadCompileModule('');
     finishOffThreadModule();
 });
--- a/js/src/jit-test/tests/gc/bug1471949.js
+++ b/js/src/jit-test/tests/gc/bug1471949.js
@@ -1,8 +1,5 @@
-// |jit-test| allow-oom
-
-if (!('oomAfterAllocations' in this))
-    quit();
+// |jit-test| allow-oom; skip-if: !('oomAfterAllocations' in this)
 
 gczeal(15);
 oomAfterAllocations(5);
 gcslice(11);
--- a/js/src/jit-test/tests/gc/incremental-abort.js
+++ b/js/src/jit-test/tests/gc/incremental-abort.js
@@ -1,12 +1,11 @@
-// Test aborting an incremental GC in all possible states
+// |jit-test| skip-if: !getBuildConfiguration()['has-gczeal']
 
-if (!("gcstate" in this && "gczeal" in this && "abortgc" in this))
-    quit();
+// Test aborting an incremental GC in all possible states
 
 gczeal(0);
 gc();
 
 function testAbort(zoneCount, objectCount, sliceCount, abortState)
 {
     // Allocate objectCount objects in zoneCount zones and run a incremental
     // shrinking GC.
--- a/js/src/jit-test/tests/gc/incremental-compacting.js
+++ b/js/src/jit-test/tests/gc/incremental-compacting.js
@@ -1,14 +1,13 @@
+// |jit-test| skip-if: !("gcstate" in this && "gczeal" in this)
+
 // Exercise incremental compacting GC
 // Run with MOZ_GCTIMER to see the timings
 
-if (!("gcstate" in this && "gczeal" in this))
-    quit();
-
 gczeal(0);
 
 function testCompacting(zoneCount, objectCount, sliceCount)
 {
     // Allocate objectCount objects in zoneCount zones
     // On linux64 debug builds we will move them all
     // Run compacting GC with multiple slices
 
--- a/js/src/jit-test/tests/gc/incremental-state.js
+++ b/js/src/jit-test/tests/gc/incremental-state.js
@@ -1,12 +1,11 @@
+// |jit-test| skip-if: !('gcstate' in this)
+
 // Test expected state changes during collection.
-if (!("gcstate" in this))
-    quit();
-
 gczeal(0);
 
 // Non-incremental GC.
 gc();
 assertEq(gcstate(), "NotActive");
 
 // Incremental GC in minimal slice. Note that finalization always uses zero-
 // sized slices while background finalization is on-going, so we need to loop.
--- a/js/src/jit-test/tests/gc/oomInArrayProtoTest.js
+++ b/js/src/jit-test/tests/gc/oomInArrayProtoTest.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 function arrayProtoOutOfRange() {
     function f(obj) {
         return typeof obj[15];
     }
 
     function test() {
         var a = [1, 2];
--- a/js/src/jit-test/tests/gc/oomInByteSize.js
+++ b/js/src/jit-test/tests/gc/oomInByteSize.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 oomTest(() => byteSize({}));
 oomTest(() => byteSize({ w: 1, x: 2, y: 3 }));
 oomTest(() => byteSize({ w:1, x:2, y:3, z:4, a:6, 0:0, 1:1, 2:2 }));
 oomTest(() => byteSize([1, 2, 3]));
 oomTest(() => byteSize(function () {}));
 
 function f1() {
--- a/js/src/jit-test/tests/gc/oomInDebugger.js
+++ b/js/src/jit-test/tests/gc/oomInDebugger.js
@@ -1,5 +1,4 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 var g = newGlobal();
 oomTest(() => Debugger(g));
--- a/js/src/jit-test/tests/gc/oomInDtoa.js
+++ b/js/src/jit-test/tests/gc/oomInDtoa.js
@@ -1,4 +1,3 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 oomTest(function() { return 1e300; })
--- a/js/src/jit-test/tests/gc/oomInExceptionHandlerBailout.js
+++ b/js/src/jit-test/tests/gc/oomInExceptionHandlerBailout.js
@@ -1,11 +1,9 @@
-if (!('oomTest' in this))
-    quit();
-
+// |jit-test| skip-if: !('oomTest' in this)
 oomTest(() => {
     let x = 0;
     try {
         for (let i = 0; i < 100; i++) {
             if (i == 99)
                 throw "foo";
             x += i;
         }
--- a/js/src/jit-test/tests/gc/oomInFindPath.js
+++ b/js/src/jit-test/tests/gc/oomInFindPath.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 var o = { w: { x: { y: { z: {} } } } };
 oomTest(() => findPath(o, o.w.x.y.z));
 
 var a = [ , o ];
 oomTest(() => findPath(a, o));
 
 function C() {}
--- a/js/src/jit-test/tests/gc/oomInFormatStackDump.js
+++ b/js/src/jit-test/tests/gc/oomInFormatStackDump.js
@@ -1,4 +1,3 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 oomTest(() => getBacktrace({args: true, locals: true, thisprops: true}));
--- a/js/src/jit-test/tests/gc/oomInGetJumpLabelForBranch.js
+++ b/js/src/jit-test/tests/gc/oomInGetJumpLabelForBranch.js
@@ -1,4 +1,3 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 oomTest(() => getBacktrace({thisprops: gc() && delete addDebuggee.enabled}));
--- a/js/src/jit-test/tests/gc/oomInNewGlobal.js
+++ b/js/src/jit-test/tests/gc/oomInNewGlobal.js
@@ -1,4 +1,3 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 oomTest(newGlobal);
--- a/js/src/jit-test/tests/gc/oomInOffTheadCompile.js
+++ b/js/src/jit-test/tests/gc/oomInOffTheadCompile.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this) || helperThreadCount() === 0)
-    quit();
+// |jit-test| skip-if: !('oomTest' in this) || helperThreadCount() === 0
 
 oomTest(() => {
     offThreadCompileScript(
         `
         function f(x) {
             if (x == 0)
                 return "foobar";
             return 1 + f(x - 1);
--- a/js/src/jit-test/tests/gc/oomInOffTheadCompile2.js
+++ b/js/src/jit-test/tests/gc/oomInOffTheadCompile2.js
@@ -1,7 +1,6 @@
-if (!('oomTest' in this) || helperThreadCount() === 0)
-    quit();
+// |jit-test| skip-if: !('oomTest' in this) || helperThreadCount() === 0
 
 oomTest(() => {
     offThreadCompileScript("function a(x) {");
     runOffThreadScript();
 });
--- a/js/src/jit-test/tests/gc/oomInOffTheadCompile3.js
+++ b/js/src/jit-test/tests/gc/oomInOffTheadCompile3.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this) || helperThreadCount() === 0)
-    quit();
+// |jit-test| skip-if: !('oomTest' in this) || helperThreadCount() === 0
 
 oomTest(() => {
     offThreadCompileScript(`
         function f(x) {
             class of extends ("ABCDEFGHIJK") {
                 test() { return true; };
                 static get() {};
                 static get() {};
--- a/js/src/jit-test/tests/gc/oomInParseAsmJS.js
+++ b/js/src/jit-test/tests/gc/oomInParseAsmJS.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 function parseAsmJS() {
     eval(`function m(stdlib)
           {
             "use asm";
             var abs = stdlib.Math.abs;
             function f(d)
             {
--- a/js/src/jit-test/tests/gc/oomInParseFunction.js
+++ b/js/src/jit-test/tests/gc/oomInParseFunction.js
@@ -1,4 +1,3 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 oomTest(() => eval("function f() {}"));
--- a/js/src/jit-test/tests/gc/oomInRegExp.js
+++ b/js/src/jit-test/tests/gc/oomInRegExp.js
@@ -1,6 +1,5 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 oomTest(() => assertEq("foobar\xff5baz\u1200".search(/bar\u0178\d/i), 3));
 oomTest(() => assertEq((/(?!(?!(?!6)[\Wc]))/i).test(), false));
 oomTest(() => assertEq((/bar\u0178\d/i).exec("foobar\xff5baz\u1200") != null, true));
--- a/js/src/jit-test/tests/gc/oomInRegExp2.js
+++ b/js/src/jit-test/tests/gc/oomInRegExp2.js
@@ -1,6 +1,5 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 oomTest(() => assertEq("foobar\xff5baz\u1200".search(/bar\u0178\d/i), 3), {keepFailing: true});
 oomTest(() => assertEq((/(?!(?!(?!6)[\Wc]))/i).test(), false), {keepFailing: true});
 oomTest(() => assertEq((/bar\u0178\d/i).exec("foobar\xff5baz\u1200") != null, true), {keepFailing: true});
--- a/js/src/jit-test/tests/gc/oomInWeakMap.js
+++ b/js/src/jit-test/tests/gc/oomInWeakMap.js
@@ -1,7 +1,6 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 oomTest(function () {
     eval(`var wm = new WeakMap();
          wm.set({}, 'FOO').get(false);`);
 });
--- a/js/src/jit-test/tests/heap-analysis/byteSize-of-object.js
+++ b/js/src/jit-test/tests/heap-analysis/byteSize-of-object.js
@@ -1,22 +1,21 @@
+// |jit-test| skip-if: !getBuildConfiguration()['moz-memory']
+// Run this test only if we're using jemalloc. Other malloc implementations
+// exhibit surprising behaviors. For example, 32-bit Fedora builds have
+// non-deterministic allocation sizes.
+
 // Check that JS::ubi::Node::size returns reasonable results for objects.
 
 // We actually hard-code specific sizes into this test, even though they're
 // implementation details, because in practice there are only two architecture
 // variants to consider (32-bit and 64-bit), and if these sizes change, that's
 // something SpiderMonkey hackers really want to know; they're supposed to be
 // stable.
 
-// Run this test only if we're using jemalloc. Other malloc implementations
-// exhibit surprising behaviors. For example, 32-bit Fedora builds have
-// non-deterministic allocation sizes.
-if (!getBuildConfiguration()['moz-memory'])
-  quit(0);
-
 if (getBuildConfiguration()['pointer-byte-size'] == 4)
   var s = (s32, s64) => s32
 else
   var s = (s32, s64) => s64
 
 function tenure(obj) {
   gc();
   return obj;
--- a/js/src/jit-test/tests/heap-analysis/byteSize-of-string.js
+++ b/js/src/jit-test/tests/heap-analysis/byteSize-of-string.js
@@ -1,22 +1,22 @@
+// |jit-test| skip-if: !getBuildConfiguration()['moz-memory']
+// Run this test only if we're using jemalloc. Other malloc implementations
+// exhibit surprising behaviors. For example, 32-bit Fedora builds have
+// non-deterministic allocation sizes.
+
 // Check JS::ubi::Node::size results for strings.
 
 // We actually hard-code specific sizes into this test, even though they're
 // implementation details, because in practice there are only two architecture
 // variants to consider (32-bit and 64-bit), and if these sizes change, that's
 // something SpiderMonkey hackers really want to know; they're supposed to be
 // stable.
 
-// Run this test only if we're using jemalloc. Other malloc implementations
-// exhibit surprising behaviors. For example, 32-bit Fedora builds have
-// non-deterministic allocation sizes.
 var config = getBuildConfiguration();
-if (!config['moz-memory'])
-  quit(0);
 
 gczeal(0); // Need to control when tenuring happens
 
 // Hack to skip this test if strings are not allocated in the nursery.
 {
   const sample_nursery = "x" + "abc".substr(1);
   let nursery_enabled = true;
   const before = byteSize(sample_nursery);
--- a/js/src/jit-test/tests/heap-analysis/byteSize-of-symbol.js
+++ b/js/src/jit-test/tests/heap-analysis/byteSize-of-symbol.js
@@ -1,22 +1,22 @@
+// |jit-test| skip-if: !getBuildConfiguration()['moz-memory']
+// Run this test only if we're using jemalloc. Other malloc implementations
+// exhibit surprising behaviors. For example, 32-bit Fedora builds have
+// non-deterministic allocation sizes.
+
 // Check JS::ubi::Node::size results for symbols.
 
 // We actually hard-code specific sizes into this test, even though they're
 // implementation details, because in practice there are only two architecture
 // variants to consider (32-bit and 64-bit), and if these sizes change, that's
 // something SpiderMonkey hackers really want to know; they're supposed to be
 // stable.
 
-// Run this test only if we're using jemalloc. Other malloc implementations
-// exhibit surprising behaviors. For example, 32-bit Fedora builds have
-// non-deterministic allocation sizes.
 var config = getBuildConfiguration();
-if (!config['moz-memory'])
-  quit(0);
 
 const SIZE_OF_SYMBOL = config['pointer-byte-size'] == 4 ? 16 : 16;
 
 // Without a description.
 assertEq(byteSize(Symbol()), SIZE_OF_SYMBOL);
 
 // With a description.
 assertEq(byteSize(Symbol("This is a relatively long description to be passed to "
--- a/js/src/jit-test/tests/ion/array-push-multiple-frozen.js
+++ b/js/src/jit-test/tests/ion/array-push-multiple-frozen.js
@@ -1,9 +1,9 @@
-// |jit-test| --no-threads
+// |jit-test| --no-threads; skip-if: !('oomAtAllocation' in this)
 
 // This test case check's Ion ability to recover from an allocation failure in
 // the inlining of Array.prototype.push, when given multiple arguments. Note,
 // that the following are not equivalent in case of failures:
 //
 //   arr = [];
 //   arr.push(1,2,3); // OOM ---> arr == []
 //
@@ -15,18 +15,16 @@
 function canIoncompile() {
   while (true) {
     var i = inIon();
     if (i)
       return i;
   }
 }
 
-if (!("oomAtAllocation" in this))
-  quit();
 if (canIoncompile() != true)
   quit();
 if ("gczeal" in this)
   gczeal(0);
 
 function pushLimits(limit, offset, arr, freeze) {
   arr = arr || [];
   arr.push(0,1,2,3,4,5,6,7,8,9);
--- a/js/src/jit-test/tests/ion/array-push-multiple-with-funapply.js
+++ b/js/src/jit-test/tests/ion/array-push-multiple-with-funapply.js
@@ -1,9 +1,9 @@
-// |jit-test| --no-threads
+// |jit-test| --no-threads; skip-if: !('oomAtAllocation' in this)
 
 // This test case check's Ion ability to inline Array.prototype.push, when
 // fun.apply is used and inlined with the set of arguments of the current
 // function. Note, that the following are not equivalent in case of failures:
 //
 //   arr = [];
 //   arr.push(1,2,3); // OOM ---> arr == []
 //
@@ -15,18 +15,16 @@
 function canIoncompile() {
   while (true) {
     var i = inIon();
     if (i)
       return i;
   }
 }
 
-if (!("oomAtAllocation" in this))
-  quit();
 if (canIoncompile() != true)
   quit();
 if ("gczeal" in this)
   gczeal(0);
 
 function pushLimits(limit, offset) {
   function pusher() {
     Array.prototype.push.apply(arr, arguments)
--- a/js/src/jit-test/tests/ion/array-push-multiple.js
+++ b/js/src/jit-test/tests/ion/array-push-multiple.js
@@ -1,9 +1,9 @@
-// |jit-test| --no-threads
+// |jit-test| --no-threads; skip-if: !('oomAtAllocation' in this)
 
 // This test case check's Ion ability to recover from an allocation failure in
 // the inlining of Array.prototype.push, when given multiple arguments. Note,
 // that the following are not equivalent in case of failures:
 //
 //   arr = [];
 //   arr.push(1,2,3); // OOM ---> arr == []
 //
@@ -15,18 +15,16 @@
 function canIoncompile() {
   while (true) {
     var i = inIon();
     if (i)
       return i;
   }
 }
 
-if (!("oomAtAllocation" in this))
-  quit();
 if (canIoncompile() != true)
   quit();
 if ("gczeal" in this)
   gczeal(0);
 
 function pushLimits(limit, offset) {
   var arr = [0,1,2,3,4,5,6,7,8,9];
   arr.length = offset;
--- a/js/src/jit-test/tests/ion/bug1006899.js
+++ b/js/src/jit-test/tests/ion/bug1006899.js
@@ -1,10 +1,9 @@
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
+// |jit-test| skip-if: !this.hasOwnProperty("TypedObject")
 
 this.__defineGetter__("x",
   function() {
     return this;
   }
 );
 function callback(obj) {}
 enableShellAllocationMetadataBuilder();
--- a/js/src/jit-test/tests/ion/bug1207413.js
+++ b/js/src/jit-test/tests/ion/bug1207413.js
@@ -1,10 +1,9 @@
-if (typeof oomAfterAllocations !== 'function')
-    quit();
+// |jit-test| skip-if: !('oomAfterAllocations' in this)
 
 function first(a) {
     return a[0];
 }
 
 try {
     first([function() {}]);
     first([function() {}]);
--- a/js/src/jit-test/tests/ion/bug1216157.js
+++ b/js/src/jit-test/tests/ion/bug1216157.js
@@ -1,10 +1,10 @@
-if (!('oomAfterAllocations' in this))
-    quit();
+// |jit-test| skip-if: !('oomAfterAllocations' in this)
+
 gcslice(0); // Start IGC, but don't mark anything.
 function f(str) {
     for (var i = 0; i < 10; i++) {
         arr = /foo(ba(r))?/.exec(str);
         var x = arr[oomAfterAllocations(100)] + " " + arr[1] + " " + 1899;
     }
 }
 try {
--- a/js/src/jit-test/tests/ion/bug1233331.js
+++ b/js/src/jit-test/tests/ion/bug1233331.js
@@ -1,10 +1,9 @@
-if (typeof oomTest !== 'function')
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 x = 0;
 try {
     a;
     b;
 } catch (e) {}
 var g = newGlobal();
 oomTest(function() {
--- a/js/src/jit-test/tests/ion/bug1240521.js
+++ b/js/src/jit-test/tests/ion/bug1240521.js
@@ -1,12 +1,9 @@
-// |jit-test| allow-oom
-
-if (!('oomAfterAllocations' in this))
-    quit();
+// |jit-test| allow-oom; skip-if: !('oomAfterAllocations' in this)
 
 var egc = 138;
 function SwitchTest(value) {
     switch (value) {
         case 0:
             break
         case new Number:
             result = 8
--- a/js/src/jit-test/tests/ion/bug1264948.js
+++ b/js/src/jit-test/tests/ion/bug1264948.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 loadFile(`
   T = TypedObject
   ObjectStruct = new T.StructType({f: T.Object})
   var o = new ObjectStruct
   function testGC(p) {
     for (; i < 5; i++)
         whatever.push;
--- a/js/src/jit-test/tests/ion/bug1269756.js
+++ b/js/src/jit-test/tests/ion/bug1269756.js
@@ -1,8 +1,7 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 oomTest(function() {
     m = parseModule(`while (x && NaN) prototype; let x`);
     m.declarationInstantiation();
     m.evaluation();
 })
--- a/js/src/jit-test/tests/ion/bug1282944.js
+++ b/js/src/jit-test/tests/ion/bug1282944.js
@@ -1,12 +1,9 @@
-// |jit-test| --ion-eager
-
-if (helperThreadCount() === 0)
-  quit(0);
+// |jit-test| --ion-eager; skip-if: helperThreadCount() === 0
 
 // (1) Poison an element in the ionLazyLinkList with a builder whose
 //     script is in a different compartment.
 evaluate('offThreadCompileScript("var x = -1"); runOffThreadScript()',
          { global: newGlobal() });
 
 // (2) Spam the ionLazyLinkList with pending builders until it pops off the one
 //     for the other compartment's script.
--- a/js/src/jit-test/tests/ion/bug1284491.js
+++ b/js/src/jit-test/tests/ion/bug1284491.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 loadFile(`
   function SwitchTest(){
     switch(value) {
       case 0:break
       case isNaN: break
     }
   }
--- a/js/src/jit-test/tests/ion/bug1348777.js
+++ b/js/src/jit-test/tests/ion/bug1348777.js
@@ -1,11 +1,9 @@
-
-if (typeof TypedObject === 'undefined')
-    quit();
+// |jit-test| skip-if: !this.hasOwnProperty("TypedObject")
 
 var uint8 = TypedObject.uint8;
 function check(v) {
     return v.toSource();
 }
 function test() {
     var fake1 = {};
     var fake2 = [];
--- a/js/src/jit-test/tests/ion/bug1394505.js
+++ b/js/src/jit-test/tests/ion/bug1394505.js
@@ -1,12 +1,9 @@
-if (helperThreadCount() === 0)
-    quit();
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: helperThreadCount() === 0 || !('oomTest' in this)
 
 for (let j = 0; j < 50; j++) {
     if (j === 1)
         oomTest(function() {});
     evalInWorker(`
         for (let i = 0; i < 30; i++)
             relazifyFunctions();
     `);
--- a/js/src/jit-test/tests/ion/bug1492574.js
+++ b/js/src/jit-test/tests/ion/bug1492574.js
@@ -1,11 +1,9 @@
-if (!('oomTest' in this)) {
-    quit();
-}
+// |jit-test| skip-if: !('oomTest' in this)
 
 function foo() {}
 function foooooooooooooooooooooooooooooooo() {}
 function fn(s) {
     var o = {a:1}
     eval(("f" + s) + "()");
     if (!('a' in o)) {
         print("unreachable");
--- a/js/src/jit-test/tests/ion/bug980860.js
+++ b/js/src/jit-test/tests/ion/bug980860.js
@@ -1,10 +1,9 @@
-if (typeof TypedObject === 'undefined')
-    quit();
+// |jit-test| skip-if: !this.hasOwnProperty("TypedObject")
 
 var StructType = TypedObject.StructType;
 var uint8 = TypedObject.uint8;
 
 function check(c) {
   assertEq(c.r, 129);
 }
 
--- a/js/src/jit-test/tests/ion/inlining/TypedObject-ObjectIsTypeDescr-multi.js
+++ b/js/src/jit-test/tests/ion/inlining/TypedObject-ObjectIsTypeDescr-multi.js
@@ -1,8 +1,10 @@
+// |jit-test| skip-if: !this.TypedObject
+
 /* -*- tab-width: 8; indent-tabs-mode: nil; js-indent-level: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* Used to verify that the JIT resolves the ObjectIsTypeDescr tests
  * internal to Type.toSource().
  *
@@ -13,19 +15,16 @@
  * Load this into the js shell with IONFLAGS=logs, then exit and run
  * iongraph.  You're looking for a smallish function within the
  * "self-hosted" domain.  Look for a call to ObjectIsTypeDescr far
  * down in the graph for pass00, with a call to DescrToSource in a
  * subsequent block (all of this is at the mercy of the way the code
  * is currently written).
  */
 
-if (!this.TypedObject)
-  quit();
-
 var T = TypedObject;
 var ST1 = new T.StructType({x:T.int32});
 var ST2 = new T.StructType({x:T.float64});
 
 function check(v) {
     return v.toSource();
 }
 
--- a/js/src/jit-test/tests/ion/inlining/TypedObject-ObjectIsTypeDescr-unknown.js
+++ b/js/src/jit-test/tests/ion/inlining/TypedObject-ObjectIsTypeDescr-unknown.js
@@ -1,8 +1,10 @@
+// |jit-test| skip-if: !this.TypedObject
+
 /* -*- tab-width: 8; indent-tabs-mode: nil; js-indent-level: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* Used to verify that the JIT resolves the ObjectIsTypeDescr tests
  * internal to Type.toSource().
  *
@@ -13,19 +15,16 @@
  * Load this into the js shell with IONFLAGS=logs, then exit and run
  * iongraph.  You're looking for a smallish function within the
  * "self-hosted" domain.  Look for a call to ObjectIsTypeDescr far
  * down in the graph for pass00, with a call to DescrToSource in a
  * subsequent block (all of this is at the mercy of the way the code
  * is currently written).
  */
 
-if (!this.TypedObject)
-  quit();
-
 var T = TypedObject;
 var ST = new T.StructType({x:T.int32});
 
 function check(v) {
     return v.toSource();
 }
 
 function test() {
--- a/js/src/jit-test/tests/ion/inlining/TypedObject-ObjectIsTypeDescr-wrong-multi.js
+++ b/js/src/jit-test/tests/ion/inlining/TypedObject-ObjectIsTypeDescr-wrong-multi.js
@@ -1,8 +1,10 @@
+// |jit-test| skip-if: !this.TypedObject
+
 /* -*- tab-width: 8; indent-tabs-mode: nil; js-indent-level: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* Used to verify that the JIT resolves the ObjectIsTypeDescr tests
  * internal to Type.toSource().
  *
@@ -13,19 +15,16 @@
  * Load this into the js shell with IONFLAGS=logs, then exit and run
  * iongraph.  You're looking for a smallish function within the
  * "self-hosted" domain.  Look for a call to ObjectIsTypeDescr far
  * down in the graph for pass00, with a call to DescrToSource in a
  * subsequent block (all of this is at the mercy of the way the code
  * is currently written).
  */
 
-if (!this.TypedObject)
-  quit();
-
 var T = TypedObject;
 var ST = new T.StructType({x:T.int32});
 
 function check(v) {
     return v.toSource();
 }
 
 function test() {
--- a/js/src/jit-test/tests/ion/inlining/TypedObject-ObjectIsTypeDescr-wrong.js
+++ b/js/src/jit-test/tests/ion/inlining/TypedObject-ObjectIsTypeDescr-wrong.js
@@ -1,8 +1,10 @@
+// |jit-test| skip-if: !this.TypedObject
+
 /* -*- tab-width: 8; indent-tabs-mode: nil; js-indent-level: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* Used to verify that the JIT resolves the ObjectIsTypeDescr tests
  * internal to Type.toSource().
  *
@@ -13,19 +15,16 @@
  * Load this into the js shell with IONFLAGS=logs, then exit and run
  * iongraph.  You're looking for a smallish function within the
  * "self-hosted" domain.  Look for a call to ObjectIsTypeDescr far
  * down in the graph for pass00, with a call to DescrToSource in a
  * subsequent block (all of this is at the mercy of the way the code
  * is currently written).
  */
 
-if (!this.TypedObject)
-  quit();
-
 var T = TypedObject;
 var ST = new T.StructType({x:T.int32});
 
 function check(v) {
     return v.toSource();
 }
 
 function test() {
--- a/js/src/jit-test/tests/ion/inlining/TypedObject-ObjectIsTypeDescr.js
+++ b/js/src/jit-test/tests/ion/inlining/TypedObject-ObjectIsTypeDescr.js
@@ -1,8 +1,10 @@
+// |jit-test| skip-if: !this.TypedObject
+
 /* -*- tab-width: 8; indent-tabs-mode: nil; js-indent-level: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* Used to verify that the JIT resolves the ObjectIsTypeDescr tests
  * internal to Type.toSource().
  *
@@ -13,19 +15,16 @@
  * Load this into the js shell with IONFLAGS=logs, then exit and run
  * iongraph.  You're looking for a smallish function within the
  * "self-hosted" domain.  Look for a call to ObjectIsTypeDescr far
  * down in the graph for pass00, with a call to DescrToSource in a
  * subsequent block (all of this is at the mercy of the way the code
  * is currently written).
  */
 
-if (!this.TypedObject)
-  quit();
-
 var T = TypedObject;
 var ST = new T.StructType({x:T.int32});
 
 function check(v) {
     return v.toSource();
 }
 
 function test() {
--- a/js/src/jit-test/tests/ion/inlining/TypedObject-ObjectIsTypedObject-multi.js
+++ b/js/src/jit-test/tests/ion/inlining/TypedObject-ObjectIsTypedObject-multi.js
@@ -1,8 +1,10 @@
+// |jit-test| skip-if: !this.TypedObject
+
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* Used to verify that the JIT resolves the ObjectIsTypedObject tests
  * used in the TO.objectType() method, among other places.
  *
@@ -14,21 +16,16 @@
  * iongraph.  You're looking for a smallish function within the
  * "self-hosted" domain.  Look for a call to ObjectIsTypedObject far
  * down in the graph for pass00, with a subgraph before it that looks
  * like it's comparing something to a string and to null (this is the
  * inlining of IsObject).  (All of this is at the mercy of the way the
  * code is currently written.)
  */
 
-if (!this.TypedObject) {
-    print("No TypedObject, skipping");
-    quit();
-}
-
 var T = TypedObject;
 var ST1 = new T.StructType({x:T.int32});
 var ST2 = new T.StructType({f:T.float64});
 var v1 = new ST1({x:10});
 var v2 = new ST2({f:3.14159});
 
 function check(v) {
   return T.objectType(v);
--- a/js/src/jit-test/tests/ion/inlining/TypedObject-ObjectIsTypedObject-unknown.js
+++ b/js/src/jit-test/tests/ion/inlining/TypedObject-ObjectIsTypedObject-unknown.js
@@ -1,8 +1,10 @@
+// |jit-test| skip-if: !this.TypedObject
+
 /* -*- tab-width: 8; indent-tabs-mode: nil; js-indent-level: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* Used to verify that the JIT resolves the ObjectIsTypedObject tests
  * used in the TO.objectType() method, among other places.
  *
@@ -16,21 +18,16 @@
  * iongraph.  You're looking for a smallish function within the
  * "self-hosted" domain.  Look for a call to ObjectIsTypedObject far
  * down in the graph for pass00, with a subgraph before it that looks
  * like it's comparing something to a string and to null (this is the
  * inlining of IsObject).  (All of this is at the mercy of the way the
  * code is currently written.)
  */
 
-if (!this.TypedObject) {
-    print("No TypedObject, skipping");
-    quit();
-}
-
 var T = TypedObject;
 var ST1 = new T.StructType({x:T.int32});
 var v1 = new ST1({x:10});
 
 function check(v) {
     return T.objectType(v);
 }
 
--- a/js/src/jit-test/tests/ion/inlining/TypedObject-ObjectIsTypedObject.js
+++ b/js/src/jit-test/tests/ion/inlining/TypedObject-ObjectIsTypedObject.js
@@ -1,8 +1,10 @@
+// |jit-test| skip-if: !this.TypedObject
+
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* Used to verify that the JIT resolves the ObjectIsTypedObject tests
  * used in the TO.objectType() method, among other places.
  *
@@ -14,21 +16,16 @@
  * iongraph.  You're looking for a smallish function within the
  * "self-hosted" domain.  Look for a call to ObjectIsTypedObject far
  * down in the graph for pass00, with a subgraph before it that looks
  * like it's comparing something to a string and to null (this is the
  * inlining of IsObject).  (All of this is at the mercy of the way the
  * code is currently written.)
  */
 
-if (!this.TypedObject) {
-    print("No TypedObject, skipping");
-    quit();
-}
-
 var T = TypedObject;
 var ST = new T.StructType({x:T.int32});
 var v = new ST({x:10});
 
 function check(v) {
   return T.objectType(v);
 }
 
--- a/js/src/jit-test/tests/ion/inlining/TypedObject-TypeDescrIsSimpleType.js
+++ b/js/src/jit-test/tests/ion/inlining/TypedObject-TypeDescrIsSimpleType.js
@@ -1,8 +1,10 @@
+// |jit-test| skip-if: !this.TypedObject
+
 /* -*- tab-width: 8; indent-tabs-mode: nil; js-indent-level: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* Testing TypeDescrIsSimpleType() is tricky because it's not exposed.
  * However, the implementation of <typed-object>.build() must use it.
  *
@@ -12,21 +14,16 @@
  * To verify that inlining happens:
  *
  * Run this with IONFLAGS=logs, generate pdfs with iongraph, and then
  * try running "pdfgrep TypeDescrIsSimpleType func*pass00*.pdf", this
  * might net a couple of functions that are likely candidates for
  * manual inspection.
  */
 
-if (!this.TypedObject) {
-    print("No TypedObject, skipping");
-    quit();
-}
-
 var T = TypedObject;
 var AT = new T.ArrayType(T.uint32, 100);
 
 function check() {
     return AT.build(x => x+1);
 }
 
 function test() {
--- a/js/src/jit-test/tests/ion/scalar-replacement-bug1138693.js
+++ b/js/src/jit-test/tests/ion/scalar-replacement-bug1138693.js
@@ -1,10 +1,9 @@
-if (!this.hasOwnProperty("TypedObject"))
-  quit();
+// |jit-test| skip-if: !this.hasOwnProperty("TypedObject")
 
 var T = TypedObject;
 var ST = new T.StructType({x:T.int32});
 function check(v) {
     return v.toSource();
 }
 function test() {
     var fake = { toSource: ST.toSource };
--- a/js/src/jit-test/tests/ion/scalar-replacement-oom.js
+++ b/js/src/jit-test/tests/ion/scalar-replacement-oom.js
@@ -1,10 +1,9 @@
-if (typeof oomAtAllocation !== 'function')
-    quit();
+// |jit-test| skip-if: !('oomAtAllocation' in this)
 
 var lfcode = new Array();
 function k(a, f_arg, b, c) {
     for (var i = 0; i < 5; ++i) {
         f_arg(i + a);
     }
 }
 function t() {
--- a/js/src/jit-test/tests/latin1/asm.js
+++ b/js/src/jit-test/tests/latin1/asm.js
@@ -1,12 +1,14 @@
+// |jit-test| skip-if: !isAsmJSCompilationAvailable()
+
 load(libdir + "asm.js");
 
 setCachingEnabled(true);
-if (!isAsmJSCompilationAvailable() || !isCachingEnabled())
+if (!isCachingEnabled())
     quit();
 
 // Test Latin1 and TwoByte PropertyName serialization.
 
 // Latin1
 var body1 = "'use asm'; function funName() { return 42 } return funName";
 var m = new Function(body1);
 assertEq(isAsmJSModule(m), true);
--- a/js/src/jit-test/tests/modules/bug-1219044.js
+++ b/js/src/jit-test/tests/modules/bug-1219044.js
@@ -1,5 +1,4 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 oomTest(() => parseModule('import v from "mod";'));
 fullcompartmentchecks(true);
--- a/js/src/jit-test/tests/modules/bug-1372258.js
+++ b/js/src/jit-test/tests/modules/bug-1372258.js
@@ -1,10 +1,9 @@
-if (helperThreadCount() == 0)
-    quit();
+// |jit-test| skip-if: helperThreadCount() === 0
 
 // Overwrite built-in parseModule with off-thread module parser.
 function parseModule(source) {
     offThreadCompileModule(source);
     return finishOffThreadModule();
 }
 
 // Test case derived from: js/src/jit-test/tests/modules/many-imports.js
--- a/js/src/jit-test/tests/modules/bug-1402535.js
+++ b/js/src/jit-test/tests/modules/bug-1402535.js
@@ -1,7 +1,6 @@
-if (!('stackTest' in this))
-   quit();
+// |jit-test| skip-if: !('stackTest' in this)
 
 stackTest(function() {
     let m = parseModule(``);
     m.declarationInstantiation();
 });
--- a/js/src/jit-test/tests/modules/bug-1402649.js
+++ b/js/src/jit-test/tests/modules/bug-1402649.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this))
-   quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 loadFile(`
 function parseAndEvaluate(source) {
     let m = parseModule(source);
     m.declarationInstantiation();
 }
 parseAndEvaluate("async function a() { await 2 + 3; }")
 `);
--- a/js/src/jit-test/tests/modules/bug-1420420-3.js
+++ b/js/src/jit-test/tests/modules/bug-1420420-3.js
@@ -1,8 +1,7 @@
-if (!('stackTest' in this))
-   quit();
+// |jit-test| skip-if: !('stackTest' in this)
 
 let a = parseModule(`throw new Error`);
 a.declarationInstantiation();
 stackTest(function() {
     a.evaluation();
 });
--- a/js/src/jit-test/tests/modules/bug-1435327.js
+++ b/js/src/jit-test/tests/modules/bug-1435327.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 lfLogBuffer = `
   let moduleRepo = {};
   setModuleResolveHook(function(x, specifier) {
         return moduleRepo[specifier];
   });
   let c = moduleRepo['c'] = parseModule("");
   let d = moduleRepo['d'] = parseModule("import { a } from 'c'; a;");
--- a/js/src/jit-test/tests/modules/bug-1466487.js
+++ b/js/src/jit-test/tests/modules/bug-1466487.js
@@ -1,7 +1,7 @@
-if (helperThreadCount() === 0)
-    quit();
+// |jit-test| skip-if: helperThreadCount() === 0
+
 evalInWorker(`
     let m = parseModule("import.meta;");
     m.declarationInstantiation();
     m.evaluation();
 `);
--- a/js/src/jit-test/tests/modules/eval-module-oom.js
+++ b/js/src/jit-test/tests/modules/eval-module-oom.js
@@ -1,12 +1,11 @@
-// OOM tests for module parsing.
+// |jit-test| skip-if: !('oomTest' in this)
 
-if (!('oomTest' in this))
-    quit();
+// OOM tests for module parsing.
 
 load(libdir + "dummyModuleResolveHook.js");
 
 const sa =
 `export default 20;
  export let a = 22;
  export function f(x, y) { return x + y }
 `;
--- a/js/src/jit-test/tests/modules/import-meta-oom.js
+++ b/js/src/jit-test/tests/modules/import-meta-oom.js
@@ -1,6 +1,3 @@
-// |jit-test| module
-
-if (typeof oomTest !== "function")
-    quit();
+// |jit-test| module; skip-if: !('oomTest' in this)
 
 oomTest(() => import.meta);
--- a/js/src/jit-test/tests/modules/missing-export-offthread.js
+++ b/js/src/jit-test/tests/modules/missing-export-offthread.js
@@ -1,8 +1,7 @@
-if (helperThreadCount() == 0)
-    quit();
+// |jit-test| skip-if: helperThreadCount() === 0
 
 load(libdir + "asserts.js")
 
 // Don't assert.
 offThreadCompileModule("export { x };");
 assertThrowsInstanceOf(() => finishOffThreadModule(), SyntaxError);
--- a/js/src/jit-test/tests/modules/off-thread-compile.js
+++ b/js/src/jit-test/tests/modules/off-thread-compile.js
@@ -1,12 +1,11 @@
-// Test off thread module compilation.
+// |jit-test| skip-if: helperThreadCount() === 0
 
-if (helperThreadCount() == 0)
-    quit();
+// Test off thread module compilation.
 
 load(libdir + "asserts.js");
 load(libdir + "dummyModuleResolveHook.js");
 
 function offThreadParseAndEvaluate(source) {
     offThreadCompileModule(source);
     let m = finishOffThreadModule();
     m.declarationInstantiation();
--- a/js/src/jit-test/tests/parser/bug-1263355-44.js
+++ b/js/src/jit-test/tests/parser/bug-1263355-44.js
@@ -1,9 +1,8 @@
-if (!('oomTest' in this))
-  quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 // Adapted from randomly chosen test: js/src/jit-test/tests/profiler/bug1231925.js
 "use strict";
 enableGeckoProfiling();
 oomTest(function() {
     eval("(function() {})()");
 });
--- a/js/src/jit-test/tests/parser/bug-1263355-48.js
+++ b/js/src/jit-test/tests/parser/bug-1263355-48.js
@@ -1,10 +1,9 @@
-if (helperThreadCount() == 0)
-  quit();
+// |jit-test| skip-if: helperThreadCount() === 0
 
 function eval(source) {
     offThreadCompileModule(source);
     let m = finishOffThreadModule();
     m.declarationInstantiation();
     return m.evaluation();
 }
 function runTestCase(testcase) {
--- a/js/src/jit-test/tests/parser/bug-1324773-2.js
+++ b/js/src/jit-test/tests/parser/bug-1324773-2.js
@@ -1,10 +1,10 @@
-if (!('gczeal' in this))
-    quit();
+// |jit-test| skip-if: !('gczeal' in this)
+
 var lfGlobal = newGlobal();
 lfGlobal.evaluate(`
     for (var i = 0; i < 600; i++)
         eval('function f' + i + '() {}');
 `);
 var lfGlobal = newGlobal();
 lfGlobal.evaluate(`
     if (!('gczeal' in this))
--- a/js/src/jit-test/tests/parser/bug-1324773.js
+++ b/js/src/jit-test/tests/parser/bug-1324773.js
@@ -1,10 +1,10 @@
-if (!('gczeal' in this))
-    quit();
+// |jit-test| skip-if: !('gczeal' in this)
+
 var lfGlobal = newGlobal();
 lfGlobal.evaluate(`
     for (var i = 0; i < 600; i++)
         eval('function f' + i + '() {}');
 `);
 var lfGlobal = newGlobal();
 lfGlobal.evaluate(`
     if (!('gczeal' in this))
--- a/js/src/jit-test/tests/parser/bug-1431353-2.js
+++ b/js/src/jit-test/tests/parser/bug-1431353-2.js
@@ -1,14 +1,13 @@
+// |jit-test| skip-if: helperThreadCount() === 0
+
 // Test off-thread parsing correctly fixes up prototypes of special objects when
 // merging back to the target compartment.
 
-if (helperThreadCount() === 0)
-    quit();
-
 function execOffThread(source)
 {
     offThreadCompileScript(source);
     return runOffThreadScript();
 }
 
 function parseModuleOffThread(source)
 {
--- a/js/src/jit-test/tests/parser/bug-1431353.js
+++ b/js/src/jit-test/tests/parser/bug-1431353.js
@@ -1,12 +1,11 @@
-// Test multiple concurrent off-thread parse jobs.
+// |jit-test| skip-if: helperThreadCount() === 0
 
-if (helperThreadCount() === 0)
-    quit();
+// Test multiple concurrent off-thread parse jobs.
 
 function assertFails(f) {
     let failed = false;
     try {
         f();
     } catch (e) {
         failed = true;
     }
--- a/js/src/jit-test/tests/parser/bug-1433014.js
+++ b/js/src/jit-test/tests/parser/bug-1433014.js
@@ -1,11 +1,6 @@
-if (helperThreadCount() === 0)
-    quit();
-
-if (!('oomTest' in this))
-    quit();
-
+// |jit-test| skip-if: helperThreadCount() === 0 || !('oomTest' in this)
 options('strict');
 evaluate(`
     oomTest(() => {
         offThreadCompileScript("");
     });`);
--- a/js/src/jit-test/tests/parser/bug-1465695.js
+++ b/js/src/jit-test/tests/parser/bug-1465695.js
@@ -1,4 +1,3 @@
-if (helperThreadCount() === 0)
-    quit();
+// |jit-test| skip-if: helperThreadCount() === 0
 for (let x = 0; x < 99; ++x)
     evalInWorker("newGlobal().offThreadCompileScript(\"{}\");");
--- a/js/src/jit-test/tests/parser/bug-1470992.js
+++ b/js/src/jit-test/tests/parser/bug-1470992.js
@@ -1,5 +1,4 @@
-if (helperThreadCount() === 0)
-    quit();
+// |jit-test| skip-if: helperThreadCount() === 0
 
 offThreadCompileModule("export { x };");
 gcslice(10);
--- a/js/src/jit-test/tests/parser/bug1461034.js
+++ b/js/src/jit-test/tests/parser/bug1461034.js
@@ -1,3 +1,2 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 oomTest(function(){s[-1]});
--- a/js/src/jit-test/tests/profiler/bug1211962.js
+++ b/js/src/jit-test/tests/profiler/bug1211962.js
@@ -1,11 +1,9 @@
-// |jit-test| slow;
-if (!('oomTest' in this))
-    quit();
+// |jit-test| slow; skip-if: !('oomTest' in this)
 
 enableGeckoProfiling();
 var lfGlobal = newGlobal();
 for (lfLocal in this) {
     lfGlobal[lfLocal] = this[lfLocal];
 }
 const script = 'oomTest(() => getBacktrace({args: true, locals: "123795", thisprops: true}));';
 lfGlobal.offThreadCompileScript(script);
--- a/js/src/jit-test/tests/profiler/bug1231925.js
+++ b/js/src/jit-test/tests/profiler/bug1231925.js
@@ -1,7 +1,6 @@
-if (!('oomTest' in this))
-  quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 enableGeckoProfiling();
 oomTest(function() {
     eval("(function() {})()")
 });
--- a/js/src/jit-test/tests/profiler/bug1242840.js
+++ b/js/src/jit-test/tests/profiler/bug1242840.js
@@ -1,10 +1,9 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
 
 enableGeckoProfiling();
 oomTest(() => {
     try {
         for (quit of ArrayBuffer);
     } catch (e) {
         switch (1) {
             case 0:
--- a/js/src/jit-test/tests/profiler/bug1352507-1.js
+++ b/js/src/jit-test/tests/profiler/bug1352507-1.js
@@ -1,3 +1,3 @@
-if (helperThreadCount() === 0)
-    quit();
+// |jit-test| skip-if: helperThreadCount() === 0
+
 evalInWorker("enableGeckoProfiling()");
--- a/js/src/jit-test/tests/realms/switch-realms-classhook.js
+++ b/js/src/jit-test/tests/realms/switch-realms-classhook.js
@@ -1,10 +1,10 @@
-if (!this.hasOwnProperty("TypedObject"))
-    quit();
+// |jit-test| skip-if: !this.hasOwnProperty("TypedObject")
+
 function test() {
     var g = newGlobal({sameCompartmentAs: this});
     var S = g.evaluate("new TypedObject.StructType({x: TypedObject.uint32});");
     for (var i = 0; i < 30; i++) {
         var o = new S();
         assertEq(objectGlobal(o), g);
     }
 }
--- a/js/src/jit-test/tests/regexp/bug1445907.js
+++ b/js/src/jit-test/tests/regexp/bug1445907.js
@@ -1,15 +1,14 @@
+// |jit-test| skip-if: !wasmIsSupported()
+
 // On ARM64, we failed to save x28 properly when generating code for the regexp
 // matcher.
 //
 // There's wasm and Debugger code here because the combination forces the use of
 // x28 and exposes the bug when running on the simulator.
 
-if (!wasmIsSupported())
-    quit();
-
 var g = newGlobal('');
 var dbg = new Debugger(g);
 g.eval(`var m = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary('(module (func (export "test")))')))`);
 var re = /./;
 dbg.onEnterFrame = function(frame) { re.exec("x") };
 result = g.eval("m.exports.test()");
--- a/js/src/jit-test/tests/regexp/clone-statics.js
+++ b/js/src/jit-test/tests/regexp/clone-statics.js
@@ -1,10 +1,9 @@
-if (helperThreadCount() === 0)
-  quit(0);
+// |jit-test| skip-if: helperThreadCount() === 0
 
 offThreadCompileScript(`
   function foo(x, {}) {
     do {
       re = /erwe/;
       if (x === 1)
         re.x = 1;
       else
--- a/js/src/jit-test/tests/regexp_parse/Assertion.js
+++ b/js/src/jit-test/tests/regexp_parse/Assertion.js
@@ -1,11 +1,8 @@
-if (typeof parseRegExp === 'undefined')
-    quit();
-
 load(libdir + "regexp_parse.js");
 
 test_mix("^", no_multiline_flags,
          Assertion("START_OF_INPUT"));
 test_mix("^", multiline_flags,
          Assertion("START_OF_LINE"));
 
 test_mix("$", no_multiline_flags,
--- a/js/src/jit-test/tests/regexp_parse/Atom.js
+++ b/js/src/jit-test/tests/regexp_parse/Atom.js
@@ -1,11 +1,8 @@
-if (typeof parseRegExp === 'undefined')
-    quit();
-
 load(libdir + "regexp_parse.js");
 
 test_mix("a", all_flags,
          Atom("a"));
 test_mix("abc\u3042\u3044\u3046", all_flags,
          Atom("abc\u3042\u3044\u3046"));
 
 // raw brace
--- a/js/src/jit-test/tests/regexp_parse/Atom_CharacterClassEscape.js
+++ b/js/src/jit-test/tests/regexp_parse/Atom_CharacterClassEscape.js
@@ -1,11 +1,8 @@
-if (typeof parseRegExp === 'undefined')
-    quit();
-
 load(libdir + "regexp_parse.js");
 
 test_mix("\\d", all_flags,
          CharacterClass([["0", "9"]]));
 
 test_mix("\\D", no_unicode_flags,
          CharacterClass([
              ["\u0000", "/"],
--- a/js/src/jit-test/tests/regexp_parse/Atom_ControlEscape.js
+++ b/js/src/jit-test/tests/regexp_parse/Atom_ControlEscape.js
@@ -1,11 +1,8 @@
-if (typeof parseRegExp === 'undefined')
-    quit();
-
 load(libdir + "regexp_parse.js");
 
 test_mix("\\f", all_flags,
          Atom("\u000c"));
 
 test_mix("\\n", all_flags,
          Atom("\u000a"));
 
--- a/js/src/jit-test/tests/regexp_parse/Atom_ControlLetter.js
+++ b/js/src/jit-test/tests/regexp_parse/Atom_ControlLetter.js
@@ -1,11 +1,8 @@
-if (typeof parseRegExp === 'undefined')
-    quit();
-
 load(libdir + "regexp_parse.js");
 
 test_mix("\\ca", all_flags,
          Atom("\u0001"));
 test_mix("\\cz", all_flags,
          Atom("\u001a"));
 test_mix("\\cA", all_flags,
          Atom("\u0001"));
--- a/js/src/jit-test/tests/regexp_parse/Atom_DecimalEscape.js
+++ b/js/src/jit-test/tests/regexp_parse/Atom_DecimalEscape.js
@@ -1,11 +1,8 @@
-if (typeof parseRegExp === 'undefined')
-    quit();
-
 load(libdir + "regexp_parse.js");
 
 // LegacyOctalEscapeSequence
 
 test_mix("\\1", no_unicode_flags,
          Atom("\u0001"));
 test_mix("\\2", no_unicode_flags,
          Atom("\u0002"));
--- a/js/src/jit-test/tests/regexp_parse/Atom_HexEscapeSequence.js
+++ b/js/src/jit-test/tests/regexp_parse/Atom_HexEscapeSequence.js
@@ -1,11 +1,8 @@
-if (typeof parseRegExp === 'undefined')
-    quit();
-
 load(libdir + "regexp_parse.js");
 
 test_mix("\\x00", all_flags,
          Atom("\u0000"));
 test_mix("\\xFF", all_flags,
          Atom("\u00FF"));
 
 test_mix("\\x0", no_unicode_flags,
--- a/js/src/jit-test/tests/regexp_parse/Atom_IdentityEscape.js
+++ b/js/src/jit-test/tests/regexp_parse/Atom_IdentityEscape.js
@@ -1,11 +1,8 @@
-if (typeof parseRegExp === 'undefined')
-    quit();
-
 load(libdir + "regexp_parse.js");
 
 // SyntaxCharacter
 
 test("\\^", all_flags,
      Atom("^"));
 test("\\$", all_flags,
      Atom("$"));
--- a/js/src/jit-test/tests/regexp_parse/Atom_Null.js
+++ b/js/src/jit-test/tests/regexp_parse/Atom_Null.js
@@ -1,7 +1,4 @@
-if (typeof parseRegExp === 'undefined')
-    quit();
-
 load(libdir + "regexp_parse.js");
 
 test_mix("\\0", all_flags,
          Atom("\u0000"));
--- a/js/src/jit-test/tests/regexp_parse/Atom_RegExpUnicodeEscapeSequence.js
+++ b/js/src/jit-test/tests/regexp_parse/Atom_RegExpUnicodeEscapeSequence.js
@@ -1,11 +1,8 @@
-if (typeof parseRegExp === 'undefined')
-    quit();
-
 load(libdir + "regexp_parse.js");
 
 // LeadSurrogate TrailSurrogate
 
 test("\\uD83D\\uDC38", all_flags,
      Atom("\uD83D\uDC38"));
 test("X\\uD83D\\uDC38Y", no_unicode_flags,
      Atom("X\uD83D\uDC38Y"));
--- a/js/src/jit-test/tests/regexp_parse/Capture.js
+++ b/js/src/jit-test/tests/regexp_parse/Capture.js
@@ -1,11 +1,8 @@
-if (typeof parseRegExp === 'undefined')
-    quit();
-
 load(libdir + "regexp_parse.js");
 
 test("()", all_flags,
      Capture(1, Empty()));
 
 test("(a)", all_flags,
      Capture(1, Atom("a")));
 
--- a/js/src/jit-test/tests/regexp_parse/CharacterClass.js
+++ b/js/src/jit-test/tests/regexp_parse/CharacterClass.js
@@ -1,11 +1,8 @@
-if (typeof parseRegExp === 'undefined')
-    quit();
-
 load(libdir + "regexp_parse.js");
 
 test_mix("[]", all_flags,
          NegativeCharacterClass([
              ["\u0000", "\uFFFF"]
          ]));
 
 test("[a]", all_flags,
--- a/js/src/jit-test/tests/regexp_parse/CharacterClass_CharacterClassEscape.js
+++ b/js/src/jit-test/tests/regexp_parse/CharacterClass_CharacterClassEscape.js
@@ -1,11 +1,8 @@
-if (typeof parseRegExp === 'undefined')
-    quit();
-
 load(libdir + "regexp_parse.js");
 
 test("[\\d]", all_flags,
      CharacterClass([["0", "9"]]));
 
 test("[\\D]", no_unicode_flags,
      CharacterClass([
          ["\u0000", "/"],
--- a/js/src/jit-test/tests/regexp_parse/CharacterClass_ClassEscape.js
+++ b/js/src/jit-test/tests/regexp_parse/CharacterClass_ClassEscape.js
@@ -1,11 +1,8 @@
-if (typeof parseRegExp === 'undefined')
-    quit();
-
 load(libdir + "regexp_parse.js");
 
 test("[\b]", all_flags,
      CharacterClass([
          ["\u0008", "\u0008"]
      ]));
 test("[\-]", all_flags,
      CharacterClass([
--- a/js/src/jit-test/tests/regexp_parse/CharacterClass_ControlEscape.js
+++ b/js/src/jit-test/tests/regexp_parse/CharacterClass_ControlEscape.js
@@ -1,11 +1,8 @@
-if (typeof parseRegExp === 'undefined')
-    quit();
-
 load(libdir + "regexp_parse.js");
 
 test("[\\f]", all_flags,
      CharacterClass([
          ["\u000c", "\u000c"]
      ]));
 
 test("[\\n]", all_flags,
--- a/js/src/jit-test/tests/regexp_parse/CharacterClass_ControlLetter.js
+++ b/js/src/jit-test/tests/regexp_parse/CharacterClass_ControlLetter.js
@@ -1,11 +1,8 @@
-if (typeof parseRegExp === 'undefined')
-    quit();
-
 load(libdir + "regexp_parse.js");
 
 test("[\\ca]", all_flags,
      CharacterClass([
          ["\u0001", "\u0001"]
      ]));
 test("[\\cz]", all_flags,
      CharacterClass([
--- a/js/src/jit-test/tests/regexp_parse/CharacterClass_HexEscapeSequence.js
+++ b/js/src/jit-test/tests/regexp_parse/CharacterClass_HexEscapeSequence.js
@@ -1,11 +1,8 @@
-if (typeof parseRegExp === 'undefined')
-    quit();
-
 load(libdir + "regexp_parse.js");
 
 test("[\\x00]", all_flags,
      CharacterClass([
          ["\u0000", "\u0000"]
      ]));
 test("[\\xff]", all_flags,
      CharacterClass([
--- a/js/src/jit-test/tests/regexp_parse/CharacterClass_Null.js
+++ b/js/src/jit-test/tests/regexp_parse/CharacterClass_Null.js
@@ -1,9 +1,6 @@
-if (typeof parseRegExp === 'undefined')
-    quit();
-
 load(libdir + "regexp_parse.js");
 
 test("[\\0]", all_flags,
      CharacterClass([
          ["\u0000", "\u0000"]
      ]));
--- a/js/src/jit-test/tests/regexp_parse/CharacterClass_RegExpUnicodeEscapeSequence.js
+++ b/js/src/jit-test/tests/regexp_parse/CharacterClass_RegExpUnicodeEscapeSequence.js
@@ -1,11 +1,8 @@
-if (typeof parseRegExp === 'undefined')
-    quit();
-
 load(libdir + "regexp_parse.js");
 
 // LeadSurrogate TrailSurrogate
 
 test("[X\\uD83D\\uDC38Y]", no_unicode_flags,
      CharacterClass([
          ["X", "X"],
          ["\uD83D", "\uD83D"],
--- a/js/src/jit-test/tests/regexp_parse/Disjunction.js
+++ b/js/src/jit-test/tests/regexp_parse/Disjunction.js
@@ -1,11 +1,8 @@
-if (typeof parseRegExp === 'undefined')
-    quit();
-
 load(libdir + "regexp_parse.js");
 
 test("a|\u3042", all_flags,
      Disjunction([
          Atom("a"),
          Atom("\u3042")
      ]));
 
--- a/js/src/jit-test/tests/regexp_parse/Empty.js
+++ b/js/src/jit-test/tests/regexp_parse/Empty.js
@@ -1,7 +1,4 @@
-if (typeof parseRegExp === 'undefined')
-    quit();
-
 load(libdir + "regexp_parse.js");
 
 test("", all_flags,
      Empty());
--- a/js/src/jit-test/tests/regexp_parse/Everything.js
+++ b/js/src/jit-test/tests/regexp_parse/Everything.js
@@ -1,11 +1,8 @@
-if (typeof parseRegExp === 'undefined')
-    quit();
-
 load(libdir + "regexp_parse.js");
 
 test_mix(".", no_unicode_flags,
          CharacterClass([
              ["\u0000", "\u0009"],
              ["\u000b", "\u000c"],
              ["\u000e", "\u2027"],
              ["\u202A", "\uFFFF"]
--- a/js/src/jit-test/tests/regexp_parse/Group.js
+++ b/js/src/jit-test/tests/regexp_parse/Group.js
@@ -1,11 +1,8 @@
-if (typeof parseRegExp === 'undefined')
-    quit();
-
 load(libdir + "regexp_parse.js");
 
 test("(?:)", all_flags,
      Empty());
 test("(?:a)", all_flags,
      Atom("a"));
 test("X(?:a)Y", all_flags,
      Text([
--- a/js/src/jit-test/tests/regexp_parse/Lookahead.js
+++ b/js/src/jit-test/tests/regexp_parse/Lookahead.js
@@ -1,11 +1,8 @@
-if (typeof parseRegExp === 'undefined')
-    quit();
-
 load(libdir + "regexp_parse.js");
 
 test_mix("(?=abc)", all_flags,
          Lookahead(Atom("abc")));
 
 test_mix("(?!abc)", all_flags,
          NegativeLookahead(Atom("abc")));
 
--- a/js/src/jit-test/tests/regexp_parse/MatchOnly.js
+++ b/js/src/jit-test/tests/regexp_parse/MatchOnly.js
@@ -1,11 +1,8 @@
-if (typeof parseRegExp === 'undefined')
-    quit();
-
 load(libdir + "regexp_parse.js");
 
 // Leading and trailing .* are ignored if match_only==true.
 
 test_match_only(".*abc", all_flags,
                 Atom("abc"));
 test_match_only("abc.*", all_flags,
                 Atom("abc"));
--- a/js/src/jit-test/tests/regexp_parse/Quantifier.js
+++ b/js/src/jit-test/tests/regexp_parse/Quantifier.js
@@ -1,11 +1,8 @@
-if (typeof parseRegExp === 'undefined')
-    quit();
-
 load(libdir + "regexp_parse.js");
 
 test_mix("a*", all_flags,
          Quantifier(0, kInfinity, "GREEDY", Atom("a")));
 test_mix("a*?", all_flags,
          Quantifier(0, kInfinity, "NON_GREEDY", Atom("a")));
 
 test_mix("a+", all_flags,
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/regexp_parse/directives.txt
@@ -0,0 +1,1 @@
+|jit-test| skip-if: typeof parseRegExp === 'undefined'
--- a/js/src/jit-test/tests/saved-stacks/bug-1445973-quick.js
+++ b/js/src/jit-test/tests/saved-stacks/bug-1445973-quick.js
@@ -1,9 +1,9 @@
-// |jit-test| --no-baseline
+// |jit-test| --no-baseline; skip-if: !('oomTest' in this)
 //
 // For background, see the comments for LiveSavedFrameCache in js/src/vm/Stack.h.
 //
 // The cache would like to assert that, assuming the cache hasn't been
 // completely flushed due to a compartment mismatch, if a stack frame's
 // hasCachedSavedFrame bit is set, then that frame does indeed have an entry in
 // the cache.
 //
@@ -14,19 +14,16 @@
 // to OOM, then we are left with no cache entry for that frame.
 //
 // The fix for 1445973 is simply to clear the frame's bit when we remove the
 // cache entry for a pc mismatch. Previously the code neglected to do this, but
 // usually got away with it because the cache would be re-populated. OOM fuzzing
 // interrupted the code at the proper place and revealed the crash, but did so
 // with a test that took 90s to run. This test runs in a fraction of a second.
 
-if (!('oomTest' in this))
-    quit();
-
 function f() {
   // Ensure that we will try to allocate fresh SavedFrame objects.
   clearSavedFrames();
 
   // Ensure that all frames have their hasCachedSavedFrame bits set.
   saveStack();
 
   try {
--- a/js/src/jit-test/tests/saved-stacks/oom-in-save-stack.js
+++ b/js/src/jit-test/tests/saved-stacks/oom-in-save-stack.js
@@ -1,4 +1,4 @@
-if (!('oomTest' in this))
-    quit();
+// |jit-test| skip-if: !('oomTest' in this)
+
 let s = saveStack();
 oomTest(() => { saveStack(); });
--- a/js/src/jit-test/tests/sharedbuf/asm-link.js
+++ b/js/src/jit-test/tests/sharedbuf/asm-link.js
@@ -1,15 +1,15 @@
+// |jit-test| skip-if: !this.SharedArrayBuffer
+
 // Don't assert on linking.
 // Provide superficial functionality.
 
 function $(stdlib, foreign, heap) {
     "use asm";
     var f64 = new stdlib.Float64Array(heap);
     function f() { var v=0.0; v=+f64[0]; return +v; }
     return f
 }
 
-if (this.SharedArrayBuffer) {
-    var heap = new SharedArrayBuffer(65536);
-    (new Float64Array(heap))[0] = 3.14159;
-    assertEq($(this, {}, heap)(), 3.14159);
-}
+var heap = new SharedArrayBuffer(65536);
+(new Float64Array(heap))[0] = 3.14159;
+assertEq($(this, {}, heap)(), 3.14159);
--- a/js/src/jit-test/tests/sharedbuf/byteLength.js
+++ b/js/src/jit-test/tests/sharedbuf/byteLength.js
@@ -1,9 +1,8 @@
-// SharedArrayBuffer.prototype.byteLength
+// |jit-test| skip-if: !this.SharedArrayBuffer
 
-if (!this.SharedArrayBuffer)
-    quit(0);
+// SharedArrayBuffer.prototype.byteLength
 
 load(libdir + "asserts.js");
 
 let buffer = new SharedArrayBuffer(137);
 assertEq(buffer.byteLength, 137);
--- a/js/src/jit-test/tests/sharedbuf/gc-one-view.js
+++ b/js/src/jit-test/tests/sharedbuf/gc-one-view.js
@@ -1,10 +1,11 @@
+// |jit-test| skip-if: !this.SharedArrayBuffer
+
 // Test tracing of a single linked ArrayBufferViewObject.
 
 function f() {
     var x = new SharedArrayBuffer(0x1000);
     var y = new Int32Array(x);
     gc();
 }
 
-if (this.SharedArrayBuffer)
-    f();
+f();
--- a/js/src/jit-test/tests/sharedbuf/gc-two-views.js
+++ b/js/src/jit-test/tests/sharedbuf/gc-two-view