Merge inbound to mozilla-central. a=merge
authorGurzau Raul <rgurzau@mozilla.com>
Wed, 10 Oct 2018 07:35:14 +0300
changeset 496141 bf31de5be0dcd71f3257485c66db5627ec3ed205
parent 496096 b85ace8c5339f5f24e7d104b4a8146dc92bb694d (current diff)
parent 496140 3854b434407792df13fad738667b042e2ebb0449 (diff)
child 496142 f5681e1f56e6d77989c3f63f9a017f5c94bd74af
child 496151 fc14ec1e17384de01a6d439b5cdcb1ce166f5118
child 496227 436a8eff6195462f6df195f3dcbbb3fbfe5a7e6b
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone64.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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;
<