Merge m-i to m-c, a=merge
authorPhil Ringnalda <philringnalda@gmail.com>
Sat, 11 Feb 2017 17:42:19 -0800
changeset 389050 09ef6a4f41186ea6951830d1506f741005dcc46f
parent 389005 43e17d5a149245dc87323290cddbb19d4b1343d4 (current diff)
parent 389049 a4df3aa93c77d99b7a8e96f3e90ecc29c5b36575 (diff)
child 389051 e1c08ac0638bee9981c00ad798e0247abc6f15ba
child 389059 faba0400cbe2c88149122071fb4bdab6b70939a4
child 389084 aa57bb7012067857cb02ef695ce3fd76a1c65783
push id7198
push userjlorenzo@mozilla.com
push dateTue, 18 Apr 2017 12:07:49 +0000
treeherdermozilla-beta@d57aa49c3948 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone54.0a1
first release with
nightly linux32
09ef6a4f4118 / 54.0a1 / 20170212110301 / files
nightly linux64
09ef6a4f4118 / 54.0a1 / 20170212110301 / files
nightly mac
09ef6a4f4118 / 54.0a1 / 20170212030213 / files
nightly win32
09ef6a4f4118 / 54.0a1 / 20170212030213 / files
nightly win64
09ef6a4f4118 / 54.0a1 / 20170212030213 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-i to m-c, a=merge
browser/app/profile/firefox.js
browser/extensions/formautofill/FormAutofillParent.jsm
browser/extensions/formautofill/moz.build
browser/extensions/formautofill/test/browser/browser.ini
devtools/client/inspector/layout/reducers/index.js
devtools/client/inspector/layout/store.js
dom/canvas/WebGL1ContextUniforms.cpp
dom/canvas/WebGL2ContextVertices.cpp
js/src/jit/VMFunctions.cpp
testing/profiles/prefs_general.js
--- a/.eslintignore
+++ b/.eslintignore
@@ -9,17 +9,16 @@ obj*/**
 # below.
 addon-sdk/**
 build/**
 caps/**
 chrome/**
 config/**
 db/**
 docshell/**
-dom/**
 editor/**
 embedding/**
 extensions/**
 gfx/**
 gradle/**
 hal/**
 image/**
 intl/**
@@ -148,16 +147,87 @@ devtools/client/debugger/test/mochitest/
 devtools/client/debugger/test/mochitest/code_math.min.js
 devtools/client/debugger/test/mochitest/code_math_bogus_map.js
 devtools/client/debugger/test/mochitest/code_ugly*
 devtools/client/debugger/test/mochitest/code_worker-source-map.js
 devtools/client/framework/test/code_ugly*
 devtools/server/tests/unit/babel_and_browserify_script_with_source_map.js
 devtools/server/tests/unit/setBreakpoint*
 
+# dom/ exclusions
+dom/animation/**
+dom/archivereader/**
+dom/asmjscache/**
+dom/audiochannel/**
+dom/base/**
+dom/battery/**
+dom/bindings/**
+dom/broadcastchannel/**
+dom/browser-element/**
+dom/cache/**
+dom/canvas/**
+dom/commandhandler/**
+dom/console/**
+dom/crypto/**
+dom/devicestorage/**
+dom/encoding/**
+dom/events/**
+dom/fetch/**
+dom/file/**
+dom/filehandle/**
+dom/filesystem/**
+dom/flyweb/**
+dom/gamepad/**
+dom/geolocation/**
+dom/grid/**
+dom/html/**
+dom/imptests/**
+dom/interfaces/**
+dom/ipc/**
+dom/json/**
+dom/jsurl/**
+dom/locales/**
+dom/manifest/**
+dom/mathml/**
+dom/media/**
+dom/messagechannel/**
+dom/network/**
+dom/notification/**
+dom/offline/**
+dom/performance/**
+dom/permission/**
+dom/plugins/**
+dom/power/**
+dom/presentation/**
+dom/promise/**
+dom/push/**
+dom/quota/**
+dom/res/**
+dom/secureelement/**
+dom/security/**
+dom/smil/**
+dom/storage/**
+dom/svg/**
+dom/system/**
+dom/tests/**
+dom/time/**
+dom/u2f/**
+dom/url/**
+dom/vr/**
+dom/webauthn/**
+dom/webbrowserpersist/**
+dom/webidl/**
+dom/workers/**
+dom/worklet/**
+dom/xbl/**
+dom/xhr/**
+dom/xml/**
+dom/xslt/**
+dom/xul/**
+
 # Exclude everything but self-hosted JS
 js/ductwork/**
 js/examples/**
 js/ipc/**
 js/public/**
 js/xpconnect/**
 js/src/devtools/**
 js/src/octane/**
--- a/accessible/ipc/DocAccessibleParent.cpp
+++ b/accessible/ipc/DocAccessibleParent.cpp
@@ -529,17 +529,16 @@ DocAccessibleParent::SetCOMProxy(const R
   // Make sure that we're not racing with a tab shutdown
   auto tab = static_cast<dom::TabParent*>(Manager());
   MOZ_ASSERT(tab);
   if (tab->IsDestroyed()) {
     return;
   }
 
   Accessible* outerDoc = OuterDocOfRemoteBrowser();
-  MOZ_ASSERT(outerDoc);
 
   IAccessible* rawNative = nullptr;
   if (outerDoc) {
     outerDoc->GetNativeInterface((void**) &rawNative);
     MOZ_ASSERT(rawNative);
   }
 
   IAccessibleHolder::COMPtrType ptr(rawNative);
--- a/accessible/ipc/win/ProxyAccessible.cpp
+++ b/accessible/ipc/win/ProxyAccessible.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* 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/. */
 
 #include "Accessible2.h"
 #include "ProxyAccessible.h"
+#include "ia2AccessibleRelation.h"
 #include "ia2AccessibleValue.h"
 #include "IGeckoCustom.h"
 #include "mozilla/a11y/DocAccessibleParent.h"
 #include "DocAccessible.h"
 #include "mozilla/a11y/DocManager.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/Unused.h"
@@ -75,16 +76,22 @@ struct InterfaceIID<IAccessibleHyperlink
 };
 
 template<>
 struct InterfaceIID<IGeckoCustom>
 {
   static REFIID Value() { return IID_IGeckoCustom; }
 };
 
+template<>
+struct InterfaceIID<IAccessible2_2>
+{
+  static REFIID Value() { return IID_IAccessible2_2; }
+};
+
 /**
  * Get the COM proxy for this proxy accessible and QueryInterface it with the
  * correct IID
  */
 template<typename Interface>
 static already_AddRefed<Interface>
 QueryInterface(const ProxyAccessible* aProxy)
 {
@@ -358,16 +365,54 @@ ProxyAccessible::Attributes(nsTArray<Att
     return;
   }
 
   ConvertBSTRAttributesToArray(nsDependentString((wchar_t*)attrs,
                                                  attrsWrap.length()),
                                aAttrs);
 }
 
+nsTArray<ProxyAccessible*>
+ProxyAccessible::RelationByType(RelationType aType) const
+{
+  RefPtr<IAccessible2_2> acc = QueryInterface<IAccessible2_2>(this);
+  if (!acc) {
+    nsTArray<ProxyAccessible*>();
+  }
+
+  _bstr_t relationType;
+  for (uint32_t idx = 0; idx < ArrayLength(sRelationTypePairs); idx++) {
+    if (aType == sRelationTypePairs[idx].first) {
+      relationType = sRelationTypePairs[idx].second;
+      break;
+    }
+  }
+
+  if (!relationType) {
+    nsTArray<ProxyAccessible*>();
+  }
+
+  IUnknown** targets;
+  long nTargets = 0;
+  HRESULT hr = acc->get_relationTargetsOfType(relationType, 0, &targets, &nTargets);
+  if (FAILED(hr)) {
+    nsTArray<ProxyAccessible*>();
+  }
+
+  nsTArray<ProxyAccessible*> proxies;
+  for (long idx = 0; idx < nTargets; idx++) {
+    IUnknown* target = targets[idx];
+    proxies.AppendElement(GetProxyFor(Document(), target));
+    target->Release();
+  }
+  CoTaskMemFree(targets);
+
+  return Move(proxies);
+}
+
 double
 ProxyAccessible::CurValue()
 {
   RefPtr<IAccessibleValue> acc = QueryInterface<IAccessibleValue>(this);
   if (!acc) {
     return UnspecifiedNaN<double>();
   }
 
--- a/accessible/tests/browser/e10s/browser.ini
+++ b/accessible/tests/browser/e10s/browser.ini
@@ -16,17 +16,17 @@ support-files =
 # Caching tests
 [browser_caching_attributes.js]
 skip-if = e10s && os == 'win' # Bug 1288839
 [browser_caching_description.js]
 skip-if = e10s && os == 'win' && os_version == '5.1'
 [browser_caching_name.js]
 skip-if = e10s && os == 'win' && os_version == '5.1'
 [browser_caching_relations.js]
-skip-if = e10s && os == 'win' # Bug 1288839
+skip-if = e10s && os == 'win' && os_version == '5.1'
 [browser_caching_states.js]
 skip-if = e10s && os == 'win' && os_version == '5.1'
 [browser_caching_value.js]
 skip-if = e10s && os == 'win' && os_version == '5.1'
 
 # Events tests
 [browser_events_caretmove.js]
 skip-if = e10s && os == 'win' && os_version == '5.1'
--- a/accessible/xpcom/xpcAccessible.cpp
+++ b/accessible/xpcom/xpcAccessible.cpp
@@ -499,26 +499,22 @@ xpcAccessible::GetRelationByType(uint32_
     return NS_ERROR_FAILURE;
 
   if (IntlGeneric().IsAccessible()) {
     Relation rel = Intl()->RelationByType(static_cast<RelationType>(aType));
     NS_ADDREF(*aRelation = new nsAccessibleRelation(aType, &rel));
     return NS_OK;
   }
 
-#if defined(XP_WIN)
-  return NS_ERROR_NOT_IMPLEMENTED;
-#else
   ProxyAccessible* proxy = IntlGeneric().AsProxy();
   nsTArray<ProxyAccessible*> targets =
     proxy->RelationByType(static_cast<RelationType>(aType));
   NS_ADDREF(*aRelation = new nsAccessibleRelation(aType, &targets));
 
   return NS_OK;
-#endif
 }
 
 NS_IMETHODIMP
 xpcAccessible::GetRelations(nsIArray** aRelations)
 {
   NS_ENSURE_ARG_POINTER(aRelations);
   *aRelations = nullptr;
 
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1574,14 +1574,15 @@ pref("browser.crashReports.unsubmittedCh
 #ifdef NIGHTLY_BUILD
 // Enable the (fairly costly) client/server validation on nightly only. The other prefs
 // controlling validation are located in /services/sync/services-sync.js
 pref("services.sync.validation.enabled", true);
 #endif
 
 // Preferences for the form autofill system extension
 pref("browser.formautofill.experimental", false);
+pref("browser.formautofill.enabled", false);
 
 // Enable safebrowsing v4 tables (suffixed by "-proto") update.
 #ifdef NIGHTLY_BUILD
 pref("urlclassifier.malwareTable", "goog-malware-shavar,goog-unwanted-shavar,goog-malware-proto,goog-unwanted-proto,test-malware-simple,test-unwanted-simple");
 pref("urlclassifier.phishTable", "goog-phish-shavar,goog-phish-proto,test-phish-simple");
 #endif
--- a/browser/components/extensions/schemas/devtools.json
+++ b/browser/components/extensions/schemas/devtools.json
@@ -7,10 +7,15 @@
         "properties": {
           "devtools_page": {
             "$ref": "ExtensionURL",
             "optional": true
           }
         }
       }
     ]
+  },
+  {
+    "namespace": "devtools",
+    "allowedContexts": ["devtools", "devtools_only"],
+    "defaultContexts": ["devtools", "devtools_only"]
   }
 ]
--- a/browser/components/extensions/test/browser/browser_ext_tabs_captureVisibleTab.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_captureVisibleTab.js
@@ -136,18 +136,18 @@ add_task(function* testCaptureVisibleTab
 
 add_task(function* testCaptureVisibleTabPermissions() {
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       "permissions": ["tabs"],
     },
 
     background() {
-      browser.test.assertFalse("captureVisibleTab" in browser.tabs,
-                               'Extension without "<all_tabs>" permission should not have access to captureVisibleTab');
+      browser.test.assertEq(undefined, browser.tabs.captureVisibleTab,
+                               'Extension without "<all_urls>" permission should not have access to captureVisibleTab');
       browser.test.notifyPass("captureVisibleTabPermissions");
     },
   });
 
   yield extension.startup();
 
   yield extension.awaitFinish("captureVisibleTabPermissions");
 
--- a/browser/components/syncedtabs/test/xpcshell/head.js
+++ b/browser/components/syncedtabs/test/xpcshell/head.js
@@ -8,22 +8,22 @@ XPCOMUtils.defineLazyGetter(this, "FxAcc
 });
 
 Cu.import("resource://gre/modules/Timer.jsm");
 
 do_get_profile(); // fxa needs a profile directory for storage.
 
 // Create a window polyfill so sinon can load
 let window = {
-    document: {},
-    location: {},
-    setTimeout,
-    setInterval,
-    clearTimeout,
-    clearinterval: clearInterval
+  document: {},
+  location: {},
+  setTimeout,
+  setInterval,
+  clearTimeout,
+  clearInterval,
 };
 let self = window;
 
 // Load mocking/stubbing library, sinon
 // docs: http://sinonjs.org/docs/
 /* global sinon */
 let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader);
 loader.loadSubScript("resource://testing-common/sinon-1.16.1.js");
--- a/browser/extensions/formautofill/FormAutofillParent.jsm
+++ b/browser/extensions/formautofill/FormAutofillParent.jsm
@@ -26,42 +26,115 @@
  */
 
 /* exported FormAutofillParent */
 
 "use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
+Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "OS",
                                   "resource://gre/modules/osfile.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ProfileStorage",
                                   "resource://formautofill/ProfileStorage.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "FormAutofillPreferences",
+                                  "resource://formautofill/FormAutofillPreferences.jsm");
 
 const PROFILE_JSON_FILE_NAME = "autofill-profiles.json";
+const ENABLED_PREF = "browser.formautofill.enabled";
 
-let FormAutofillParent = {
+function FormAutofillParent() {
+}
+
+FormAutofillParent.prototype = {
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIObserver]),
+
   _profileStore: null,
 
   /**
+   * Whether Form Autofill is enabled in preferences.
+   * Caches the latest value of this._getStatus().
+   */
+  _enabled: false,
+
+  /**
    * Initializes ProfileStorage and registers the message handler.
    */
   init() {
-    let storePath =
-      OS.Path.join(OS.Constants.Path.profileDir, PROFILE_JSON_FILE_NAME);
-
+    let storePath = OS.Path.join(OS.Constants.Path.profileDir, PROFILE_JSON_FILE_NAME);
     this._profileStore = new ProfileStorage(storePath);
     this._profileStore.initialize();
 
-    let mm = Cc["@mozilla.org/globalmessagemanager;1"]
-               .getService(Ci.nsIMessageListenerManager);
-    mm.addMessageListener("FormAutofill:PopulateFieldValues", this);
-    mm.addMessageListener("FormAutofill:GetProfiles", this);
+    Services.obs.addObserver(this, "advanced-pane-loaded", false);
+
+    // Observing the pref (and storage) changes
+    Services.prefs.addObserver(ENABLED_PREF, this, false);
+    this._enabled = this._getStatus();
+    // Force to trigger the onStatusChanged function for setting listeners properly
+    // while initizlization
+    this._onStatusChanged();
+    Services.mm.addMessageListener("FormAutofill:getEnabledStatus", this);
+  },
+
+  observe(subject, topic, data) {
+    switch (topic) {
+      case "advanced-pane-loaded": {
+        let formAutofillPreferences = new FormAutofillPreferences();
+        let document = subject.document;
+        let prefGroup = formAutofillPreferences.init(document);
+        let parentNode = document.getElementById("mainPrefPane");
+        let insertBeforeNode = document.getElementById("locationBarGroup");
+        parentNode.insertBefore(prefGroup, insertBeforeNode);
+        break;
+      }
+
+      case "nsPref:changed": {
+        // Observe pref changes and update _enabled cache if status is changed.
+        let currentStatus = this._getStatus();
+        if (currentStatus !== this._enabled) {
+          this._enabled = currentStatus;
+          this._onStatusChanged();
+        }
+        break;
+      }
+
+      default: {
+        throw new Error(`FormAutofillParent: Unexpected topic observed: ${topic}`);
+      }
+    }
+  },
+
+  /**
+   * Add/remove message listener and broadcast the status to frames while the
+   * form autofill status changed.
+   */
+  _onStatusChanged() {
+    if (this._enabled) {
+      Services.mm.addMessageListener("FormAutofill:PopulateFieldValues", this);
+      Services.mm.addMessageListener("FormAutofill:GetProfiles", this);
+    } else {
+      Services.mm.removeMessageListener("FormAutofill:PopulateFieldValues", this);
+      Services.mm.removeMessageListener("FormAutofill:GetProfiles", this);
+    }
+
+    Services.mm.broadcastAsyncMessage("FormAutofill:enabledStatus", this._enabled);
+  },
+
+  /**
+   * Query pref (and storage) status to determine the overall status for
+   * form autofill feature.
+   *
+   * @returns {boolean} status of form autofill feature
+   */
+  _getStatus() {
+    return Services.prefs.getBoolPref(ENABLED_PREF);
   },
 
   /**
    * Handles the message coming from FormAutofillContent.
    *
    * @param   {string} message.name The name of the message.
    * @param   {object} message.data The data of the message.
    * @param   {nsIFrameMessageManager} message.target Caller's message manager.
@@ -69,16 +142,20 @@ let FormAutofillParent = {
   receiveMessage({name, data, target}) {
     switch (name) {
       case "FormAutofill:PopulateFieldValues":
         this._populateFieldValues(data, target);
         break;
       case "FormAutofill:GetProfiles":
         this._getProfiles(data, target);
         break;
+      case "FormAutofill:getEnabledStatus":
+        target.messageManager.sendAsyncMessage("FormAutofill:enabledStatus",
+                                               this._enabled);
+        break;
     }
   },
 
   /**
    * Returns the instance of ProfileStorage. To avoid syncing issues, anyone
    * who needs to access the profile should request the instance by this instead
    * of creating a new one.
    *
@@ -94,20 +171,20 @@ let FormAutofillParent = {
    * @private
    */
   _uninit() {
     if (this._profileStore) {
       this._profileStore._saveImmediately();
       this._profileStore = null;
     }
 
-    let mm = Cc["@mozilla.org/globalmessagemanager;1"]
-               .getService(Ci.nsIMessageListenerManager);
-    mm.removeMessageListener("FormAutofill:PopulateFieldValues", this);
-    mm.removeMessageListener("FormAutofill:GetProfiles", this);
+    Services.mm.removeMessageListener("FormAutofill:PopulateFieldValues", this);
+    Services.mm.removeMessageListener("FormAutofill:GetProfiles", this);
+    Services.obs.removeObserver(this, "advanced-pane-loaded");
+    Services.prefs.removeObserver(ENABLED_PREF, this);
   },
 
   /**
    * Populates the field values and notifies content to fill in. Exception will
    * be thrown if there's no matching profile.
    *
    * @private
    * @param  {string} data.guid
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/FormAutofillPreferences.jsm
@@ -0,0 +1,136 @@
+/* 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/. */
+
+/**
+ * Injects the form autofill section into about:preferences.
+ */
+
+"use strict";
+
+this.EXPORTED_SYMBOLS = ["FormAutofillPreferences"];
+
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+const PREF_AUTOFILL_ENABLED = "browser.formautofill.enabled";
+const BUNDLE_URI = "chrome://formautofill/locale/formautofill.properties";
+
+Cu.import("resource://gre/modules/Services.jsm");
+
+function FormAutofillPreferences() {
+  this.bundle = Services.strings.createBundle(BUNDLE_URI);
+}
+
+FormAutofillPreferences.prototype = {
+  /**
+   * Check if Form Autofill feature is enabled.
+   *
+   * @returns {boolean}
+   */
+  get isAutofillEnabled() {
+    return Services.prefs.getBoolPref(PREF_AUTOFILL_ENABLED);
+  },
+
+  /**
+   * Check if the current page is Preferences/Privacy.
+   *
+   * @returns {boolean}
+   */
+  get isPrivacyPane() {
+    return this.refs.document.location.href == "about:preferences#privacy";
+  },
+
+  /**
+   * Create the Form Autofill preference group.
+   *
+   * @param   {XULDocument} document
+   * @returns {XULElement}
+   */
+  init(document) {
+    const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+    let formAutofillGroup = document.createElementNS(XUL_NS, "groupbox");
+    let caption = document.createElementNS(XUL_NS, "caption");
+    let captionLabel = document.createElementNS(XUL_NS, "label");
+    let hbox = document.createElementNS(XUL_NS, "hbox");
+    let enabledCheckbox = document.createElementNS(XUL_NS, "checkbox");
+    let spacer = document.createElementNS(XUL_NS, "spacer");
+    let savedProfilesBtn = document.createElementNS(XUL_NS, "button");
+
+    this.refs = {
+      document,
+      formAutofillGroup,
+      enabledCheckbox,
+      savedProfilesBtn,
+    };
+
+    formAutofillGroup.id = "formAutofillGroup";
+    formAutofillGroup.hidden = !this.isPrivacyPane;
+    // Use .setAttribute because HTMLElement.dataset is not available on XUL elements
+    formAutofillGroup.setAttribute("data-category", "panePrivacy");
+
+    captionLabel.textContent = this.bundle.GetStringFromName("preferenceGroupTitle");
+    savedProfilesBtn.setAttribute("label", this.bundle.GetStringFromName("savedProfiles"));
+    enabledCheckbox.setAttribute("label", this.bundle.GetStringFromName("enableProfileAutofill"));
+
+    // Manually set the checked state
+    if (this.isAutofillEnabled) {
+      enabledCheckbox.setAttribute("checked", true);
+    }
+
+    spacer.flex = 1;
+
+    formAutofillGroup.appendChild(caption);
+    caption.appendChild(captionLabel);
+    formAutofillGroup.appendChild(hbox);
+    hbox.appendChild(enabledCheckbox);
+    hbox.appendChild(spacer);
+    hbox.appendChild(savedProfilesBtn);
+
+    this.attachEventListeners();
+
+    return formAutofillGroup;
+  },
+
+  /**
+   * Remove event listeners and the preference group.
+   */
+  uninit() {
+    this.detachEventListeners();
+    this.refs.formAutofillGroup.remove();
+  },
+
+  /**
+   * Handle events
+   *
+   * @param  {DOMEvent} event
+   */
+  handleEvent(event) {
+    switch (event.type) {
+      case "command": {
+        let target = event.target;
+
+        if (target == this.refs.enabledCheckbox) {
+          // Set preference directly instead of relying on <Preference>
+          Services.prefs.setBoolPref(PREF_AUTOFILL_ENABLED, target.checked);
+        } else if (target == this.refs.savedProfilesBtn) {
+          // TODO: Open Saved Profiles dialog
+        }
+        break;
+      }
+    }
+  },
+
+  /**
+   * Attach event listener
+   */
+  attachEventListeners() {
+    this.refs.formAutofillGroup.addEventListener("command", this);
+  },
+
+  /**
+   * Remove event listener
+   */
+  detachEventListeners() {
+    this.refs.formAutofillGroup.removeEventListener("command", this);
+  },
+};
--- a/browser/extensions/formautofill/bootstrap.js
+++ b/browser/extensions/formautofill/bootstrap.js
@@ -16,15 +16,16 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 
 function startup() {
   // Besides this pref, we'll need dom.forms.autocomplete.experimental enabled
   // as well to make sure form autocomplete works correctly.
   if (!Services.prefs.getBoolPref("browser.formautofill.experimental")) {
     return;
   }
 
-  FormAutofillParent.init();
+  let parent = new FormAutofillParent();
+  parent.init();
   Services.mm.loadFrameScript("chrome://formautofill/content/FormAutofillContent.js", true);
 }
 
 function shutdown() {}
 function install() {}
 function uninstall() {}
--- a/browser/extensions/formautofill/content/FormAutofillContent.js
+++ b/browser/extensions/formautofill/content/FormAutofillContent.js
@@ -335,19 +335,26 @@ let ProfileAutocomplete = {
 
 /**
  * Handles content's interactions.
  *
  * NOTE: Declares it by "var" to make it accessible in unit tests.
  */
 var FormAutofillContent = {
   init() {
-    ProfileAutocomplete.ensureRegistered();
+    addEventListener("DOMContentLoaded", this);
 
-    addEventListener("DOMContentLoaded", this);
+    addMessageListener("FormAutofill:enabledStatus", (result) => {
+      if (result.data) {
+        ProfileAutocomplete.ensureRegistered();
+      } else {
+        ProfileAutocomplete.ensureUnregistered();
+      }
+    });
+    sendAsyncMessage("FormAutofill:getEnabledStatus");
   },
 
   handleEvent(evt) {
     if (!evt.isTrusted) {
       return;
     }
 
     switch (evt.type) {
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/locale/en-US/formautofill.properties
@@ -0,0 +1,7 @@
+# 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/.
+
+preferenceGroupTitle = Form Autofill
+enableProfileAutofill = Enable Profile Autofill
+savedProfiles = Saved Profiles…
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/locale/jar.mn
@@ -0,0 +1,8 @@
+#filter substitution
+# 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/.
+
+@AB_CD@.jar:
+% locale formautofill @AB_CD@ %locale/@AB_CD@/
+  locale/@AB_CD@/                (en-US/*)
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/locale/moz.build
@@ -0,0 +1,7 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+JAR_MANIFESTS += ['jar.mn']
--- a/browser/extensions/formautofill/moz.build
+++ b/browser/extensions/formautofill/moz.build
@@ -2,16 +2,18 @@
 # vim: set filetype=python:
 # 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/.
 
 DEFINES['MOZ_APP_VERSION'] = CONFIG['MOZ_APP_VERSION']
 DEFINES['MOZ_APP_MAXVERSION'] = CONFIG['MOZ_APP_MAXVERSION']
 
+DIRS += ['locale']
+
 FINAL_TARGET_FILES.features['formautofill@mozilla.org'] += [
   'bootstrap.js'
 ]
 
 FINAL_TARGET_PP_FILES.features['formautofill@mozilla.org'] += [
   'install.rdf.in'
 ]
 
--- a/browser/extensions/formautofill/test/browser/browser.ini
+++ b/browser/extensions/formautofill/test/browser/browser.ini
@@ -1,3 +1,4 @@
 [DEFAULT]
 
 [browser_check_installed.js]
+[browser_privacyPreferences.js]
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/test/browser/browser_privacyPreferences.js
@@ -0,0 +1,49 @@
+"use strict";
+
+const PAGE_PREFS = "about:preferences";
+const PAGE_PRIVACY = PAGE_PREFS + "#privacy";
+const GROUP_AUTOFILL = "#formAutofillGroup";
+const CHECKBOX_AUTOFILL = GROUP_AUTOFILL + " checkbox";
+const PREF_AUTOFILL_ENABLED = "browser.formautofill.enabled";
+const TEST_SELECTORS = {
+  group: GROUP_AUTOFILL,
+  checkbox: CHECKBOX_AUTOFILL,
+};
+
+// Visibility of form autofill group should be hidden when opening
+// preferences page.
+add_task(function* test_aboutPreferences() {
+  yield BrowserTestUtils.withNewTab({gBrowser, url: PAGE_PREFS}, function* (browser) {
+    yield ContentTask.spawn(browser, TEST_SELECTORS, (args) => {
+      is(content.document.querySelector(args.group).hidden, true,
+        "Form Autofill group should be hidden");
+    });
+  });
+});
+
+// Visibility of form autofill group should be visible when opening
+// directly to privacy page. Checkbox is not checked by default.
+add_task(function* test_aboutPreferencesPrivacy() {
+  yield BrowserTestUtils.withNewTab({gBrowser, url: PAGE_PRIVACY}, function* (browser) {
+    yield ContentTask.spawn(browser, TEST_SELECTORS, (args) => {
+      is(content.document.querySelector(args.group).hidden, false,
+        "Form Autofill group should be visible");
+      is(content.document.querySelector(args.checkbox).checked, false,
+        "Checkbox should be unchecked");
+    });
+  });
+});
+
+// Checkbox should be checked when form autofill is enabled.
+add_task(function* test_autofillEnabledCheckbox() {
+  SpecialPowers.pushPrefEnv({set: [[PREF_AUTOFILL_ENABLED, true]]});
+
+  yield BrowserTestUtils.withNewTab({gBrowser, url: PAGE_PRIVACY}, function* (browser) {
+    yield ContentTask.spawn(browser, TEST_SELECTORS, (args) => {
+      is(content.document.querySelector(args.group).hidden, false,
+        "Form Autofill group should be visible");
+      is(content.document.querySelector(args.checkbox).checked, true,
+        "Checkbox should be checked when Form Autofill is enabled");
+    });
+  });
+});
--- a/browser/extensions/formautofill/test/unit/head.js
+++ b/browser/extensions/formautofill/test/unit/head.js
@@ -1,28 +1,36 @@
 /**
  * Provides infrastructure for automated formautofill components tests.
  */
 
-/* exported loadFormAutofillContent, getTempFile */
+/* exported loadFormAutofillContent, getTempFile, sinon */
 
 "use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/NetUtil.jsm");
 Cu.import("resource://testing-common/MockDocument.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "DownloadPaths",
                                   "resource://gre/modules/DownloadPaths.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
                                   "resource://gre/modules/FileUtils.jsm");
 
+do_get_profile();
+
+// Setup the environment for sinon.
+Cu.import("resource://gre/modules/Timer.jsm");
+let self = {}; // eslint-disable-line no-unused-vars
+var sinon;
+Services.scriptloader.loadSubScript("resource://testing-common/sinon-1.16.1.js");
+
 // Load our bootstrap extension manifest so we can access our chrome/resource URIs.
 const EXTENSION_ID = "formautofill@mozilla.org";
 let extensionDir = Services.dirsvc.get("GreD", Ci.nsIFile);
 extensionDir.append("browser");
 extensionDir.append("features");
 extensionDir.append(EXTENSION_ID);
 // If the unpacked extension doesn't exist, use the packed version.
 if (!extensionDir.exists()) {
@@ -35,16 +43,18 @@ Components.manager.addBootstrappedManife
 // used, on Windows these might still be pending deletion on the physical file
 // system.  Thus, start from a new base number every time, to make a collision
 // with a file that is still pending deletion highly unlikely.
 let gFileCounter = Math.floor(Math.random() * 1000000);
 
 function loadFormAutofillContent() {
   let facGlobal = {
     addEventListener() {},
+    addMessageListener() {},
+    sendAsyncMessage() {},
   };
   let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
                .getService(Ci.mozIJSSubScriptLoader);
   loader.loadSubScriptWithOptions("chrome://formautofill/content/FormAutofillContent.js", {
     target: facGlobal,
   });
 
   return facGlobal;
@@ -78,18 +88,18 @@ function getTempFile(leafName) {
     if (file.exists()) {
       file.remove(false);
     }
   });
 
   return file;
 }
 
-add_task(function* test_common_initialize() {
+add_task(function* head_initialize() {
   Services.prefs.setBoolPref("browser.formautofill.experimental", true);
   Services.prefs.setBoolPref("dom.forms.autocomplete.experimental", true);
 
   // Clean up after every test.
-  do_register_cleanup(() => {
+  do_register_cleanup(function head_cleanup() {
     Services.prefs.clearUserPref("browser.formautofill.experimental");
     Services.prefs.clearUserPref("dom.forms.autocomplete.experimental");
   });
 });
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/test/unit/test_enabledStatus.js
@@ -0,0 +1,50 @@
+/*
+ * Test for status handling in Form Autofill Parent.
+ */
+
+"use strict";
+
+Cu.import("resource://formautofill/FormAutofillParent.jsm");
+
+add_task(function* test_enabledStatus_init() {
+  let formAutofillParent = new FormAutofillParent();
+  sinon.spy(formAutofillParent, "_onStatusChanged");
+
+  // Default status is false before initialization
+  do_check_eq(formAutofillParent._enabled, false);
+
+  formAutofillParent.init();
+  do_check_eq(formAutofillParent._onStatusChanged.called, true);
+
+  formAutofillParent._uninit();
+});
+
+add_task(function* test_enabledStatus_observe() {
+  let formAutofillParent = new FormAutofillParent();
+  sinon.stub(formAutofillParent, "_getStatus");
+  sinon.spy(formAutofillParent, "_onStatusChanged");
+
+  // _enabled = _getStatus() => No need to trigger onStatusChanged
+  formAutofillParent._enabled = true;
+  formAutofillParent._getStatus.returns(true);
+  formAutofillParent.observe(null, "nsPref:changed", "browser.formautofill.enabled");
+  do_check_eq(formAutofillParent._onStatusChanged.called, false);
+
+  // _enabled != _getStatus() => Need to trigger onStatusChanged
+  formAutofillParent._getStatus.returns(false);
+  formAutofillParent.observe(null, "nsPref:changed", "browser.formautofill.enabled");
+  do_check_eq(formAutofillParent._onStatusChanged.called, true);
+});
+
+add_task(function* test_enabledStatus_getStatus() {
+  let formAutofillParent = new FormAutofillParent();
+  do_register_cleanup(function cleanup() {
+    Services.prefs.clearUserPref("browser.formautofill.enabled");
+  });
+
+  Services.prefs.setBoolPref("browser.formautofill.enabled", true);
+  do_check_eq(formAutofillParent._getStatus(), true);
+
+  Services.prefs.setBoolPref("browser.formautofill.enabled", false);
+  do_check_eq(formAutofillParent._getStatus(), false);
+});
--- a/browser/extensions/formautofill/test/unit/test_populateFieldValues.js
+++ b/browser/extensions/formautofill/test/unit/test_populateFieldValues.js
@@ -29,34 +29,35 @@ const TEST_PROFILE = {
   "address-level1": "MA",
   postalCode: "02139",
   country: "US",
   tel: "+1 617 253 5702",
   email: "timbl@w3.org",
 };
 
 add_task(function* test_populateFieldValues() {
-  FormAutofillParent.init();
+  let formAutofillParent = new FormAutofillParent();
+  formAutofillParent.init();
 
-  let store = FormAutofillParent.getProfileStore();
+  let store = formAutofillParent.getProfileStore();
   do_check_neq(store, null);
 
   store.get = function(guid) {
     do_check_eq(guid, TEST_GUID);
     return store._clone(TEST_PROFILE);
   };
 
   let notifyUsedCalledCount = 0;
   store.notifyUsed = function(guid) {
     do_check_eq(guid, TEST_GUID);
     notifyUsedCalledCount++;
   };
 
   yield new Promise((resolve) => {
-    FormAutofillParent.receiveMessage({
+    formAutofillParent.receiveMessage({
       name: "FormAutofill:PopulateFieldValues",
       data: {
         guid: TEST_GUID,
         fields: TEST_FIELDS,
       },
       target: {
         sendAsyncMessage(name, data) {
           do_check_eq(name, "FormAutofill:fillForm");
@@ -73,28 +74,29 @@ add_task(function* test_populateFieldVal
           resolve();
         },
       },
     });
   });
 
   do_check_eq(notifyUsedCalledCount, 1);
 
-  FormAutofillParent._uninit();
-  do_check_null(FormAutofillParent.getProfileStore());
+  formAutofillParent._uninit();
+  do_check_null(formAutofillParent.getProfileStore());
 });
 
 add_task(function* test_populateFieldValues_with_invalid_guid() {
-  FormAutofillParent.init();
+  let formAutofillParent = new FormAutofillParent();
+  formAutofillParent.init();
 
   Assert.throws(() => {
-    FormAutofillParent.receiveMessage({
+    formAutofillParent.receiveMessage({
       name: "FormAutofill:PopulateFieldValues",
       data: {
         guid: "invalid-guid",
         fields: TEST_FIELDS,
       },
       target: {},
     });
   }, /No matching profile\./);
 
-  FormAutofillParent._uninit();
+  formAutofillParent._uninit();
 });
--- a/browser/extensions/formautofill/test/unit/xpcshell.ini
+++ b/browser/extensions/formautofill/test/unit/xpcshell.ini
@@ -1,12 +1,13 @@
 [DEFAULT]
 firefox-appdir = browser
 head = head.js
 support-files =
 
 [test_autofillFormFields.js]
 [test_collectFormFields.js]
+[test_enabledStatus.js]
 [test_getFormInputDetails.js]
 [test_markAsAutofillField.js]
 [test_populateFieldValues.js]
 [test_profileAutocompleteResult.js]
 [test_profileStorage.js]
--- a/browser/locales/Makefile.in
+++ b/browser/locales/Makefile.in
@@ -92,16 +92,19 @@ searchplugins:: $(list-json)
 
 DEFINES += -DBOOKMARKS_INCLUDE_DIR=$(dir $(call MERGE_FILE,profile/bookmarks.inc))
 
 libs-%:
 	$(NSINSTALL) -D $(DIST)/install
 	@$(MAKE) -C ../../toolkit/locales libs-$* XPI_ROOT_APPID='$(XPI_ROOT_APPID)'
 	@$(MAKE) -C ../../services/sync/locales AB_CD=$* XPI_NAME=locale-$*
 	@$(MAKE) -C ../../extensions/spellcheck/locales AB_CD=$* XPI_NAME=locale-$*
+ifndef RELEASE_OR_BETA
+	@$(MAKE) -C ../extensions/formautofill/locale AB_CD=$* XPI_NAME=locale-$*
+endif
 	@$(MAKE) -C ../extensions/pocket/locale AB_CD=$* XPI_NAME=locale-$*
 ifndef RELEASE_OR_BETA
 	@$(MAKE) -C ../extensions/presentation/locale AB_CD=$* XPI_NAME=locale-$*
 endif
 	@$(MAKE) -C ../../intl/locales AB_CD=$* XPI_NAME=locale-$*
 	@$(MAKE) -C ../../devtools/client/locales AB_CD=$* XPI_NAME=locale-$* XPI_ROOT_APPID='$(XPI_ROOT_APPID)'
 	@$(MAKE) -B searchplugins AB_CD=$* XPI_NAME=locale-$*
 	@$(MAKE) libs AB_CD=$* XPI_NAME=locale-$* PREF_DIR=$(PREF_DIR)
--- a/devtools/client/devtools-startup.js
+++ b/devtools/client/devtools-startup.js
@@ -99,22 +99,24 @@ DevToolsStartup.prototype = {
 
   _isRemoteDebuggingEnabled() {
     let remoteDebuggingEnabled = false;
     try {
       remoteDebuggingEnabled = kDebuggerPrefs.every(pref => {
         return Services.prefs.getBoolPref(pref);
       });
     } catch (ex) {
+      let { console } = Cu.import("resource://gre/modules/Console.jsm", {});
       console.error(ex);
       return false;
     }
     if (!remoteDebuggingEnabled) {
       let errorMsg = "Could not run chrome debugger! You need the following " +
                      "prefs to be set to true: " + kDebuggerPrefs.join(", ");
+      let { console } = Cu.import("resource://gre/modules/Console.jsm", {});
       console.error(new Error(errorMsg));
       // Dump as well, as we're doing this from a commandline, make sure people
       // don't miss it:
       dump(errorMsg + "\n");
     }
     return remoteDebuggingEnabled;
   },
 
--- a/devtools/client/inspector/inspector.js
+++ b/devtools/client/inspector/inspector.js
@@ -29,16 +29,18 @@ const {InspectorSearch} = require("devto
 const {RuleViewTool} = require("devtools/client/inspector/rules/rules");
 const HighlightersOverlay = require("devtools/client/inspector/shared/highlighters-overlay");
 const {ToolSidebar} = require("devtools/client/inspector/toolsidebar");
 const MarkupView = require("devtools/client/inspector/markup/markup");
 const {CommandUtils} = require("devtools/client/shared/developer-toolbar");
 const {ViewHelpers} = require("devtools/client/shared/widgets/view-helpers");
 const clipboardHelper = require("devtools/shared/platform/clipboard");
 
+const Store = require("devtools/client/inspector/store");
+
 const {LocalizationHelper, localizeMarkup} = require("devtools/shared/l10n");
 const INSPECTOR_L10N =
       new LocalizationHelper("devtools/client/locales/inspector.properties");
 const TOOLBOX_L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");
 
 // Sidebar dimensions
 const INITIAL_SIDEBAR_SIZE = 350;
 
@@ -87,16 +89,17 @@ const PORTRAIT_MODE_WIDTH = 700;
 function Inspector(toolbox) {
   this._toolbox = toolbox;
   this._target = toolbox.target;
   this.panelDoc = window.document;
   this.panelWin = window;
   this.panelWin.inspector = this;
 
   this.highlighters = new HighlightersOverlay(this);
+  this.store = Store();
   this.telemetry = new Telemetry();
 
   this.nodeMenuTriggerInfo = null;
 
   this._handleRejectionIfNotDestroyed = this._handleRejectionIfNotDestroyed.bind(this);
   this._onBeforeNavigate = this._onBeforeNavigate.bind(this);
   this.onNewRoot = this.onNewRoot.bind(this);
   this._onContextMenu = this._onContextMenu.bind(this);
@@ -905,17 +908,16 @@ Inspector.prototype = {
     if (this.walker) {
       this.walker.off("new-root", this.onNewRoot);
       this.pageStyle = null;
     }
 
     this.cancelUpdate();
 
     this.target.off("will-navigate", this._onBeforeNavigate);
-
     this.target.off("thread-paused", this.updateDebuggerPausedWarning);
     this.target.off("thread-resumed", this.updateDebuggerPausedWarning);
     this._toolbox.off("select", this.updateDebuggerPausedWarning);
 
     if (this.ruleview) {
       this.ruleview.destroy();
     }
 
@@ -937,31 +939,31 @@ Inspector.prototype = {
       }
     });
 
     this.sidebar.off("select", this._setDefaultSidebar);
     let sidebarDestroyer = this.sidebar.destroy();
 
     this.teardownSplitter();
 
-    this.sidebar = null;
-
     this.teardownToolbar();
     this.breadcrumbs.destroy();
     this.selection.off("new-node-front", this.onNewSelection);
     this.selection.off("detached-front", this.onDetached);
 
     let markupDestroyer = this._destroyMarkup();
 
-    this.panelWin.inspector = null;
-    this.target = null;
+    this._toolbox = null;
+    this.breadcrumbs = null;
     this.panelDoc = null;
+    this.panelWin.inspector = null;
     this.panelWin = null;
-    this.breadcrumbs = null;
-    this._toolbox = null;
+    this.sidebar = null;
+    this.store = null;
+    this.target = null;
 
     this.highlighters.destroy();
     this.highlighters = null;
 
     this.search.destroy();
     this.search = null;
     this.searchBox = null;
 
--- a/devtools/client/inspector/layout/layout.js
+++ b/devtools/client/inspector/layout/layout.js
@@ -21,33 +21,32 @@ const {
   updateGrids,
 } = require("./actions/grids");
 const {
   updateShowGridLineNumbers,
   updateShowInfiniteLines,
 } = require("./actions/highlighter-settings");
 
 const App = createFactory(require("./components/App"));
-const Store = require("./store");
 
 const EditingSession = require("./utils/editing-session");
 
 const { LocalizationHelper } = require("devtools/shared/l10n");
 const INSPECTOR_L10N =
   new LocalizationHelper("devtools/client/locales/inspector.properties");
 
 const NUMERIC = /^-?[\d\.]+$/;
 const SHOW_GRID_LINE_NUMBERS = "devtools.gridinspector.showGridLineNumbers";
 const SHOW_INFINITE_LINES_PREF = "devtools.gridinspector.showInfiniteLines";
 
 function LayoutView(inspector, window) {
   this.document = window.document;
   this.highlighters = inspector.highlighters;
   this.inspector = inspector;
-  this.store = null;
+  this.store = inspector.store;
   this.walker = this.inspector.walker;
 
   this.updateBoxModel = this.updateBoxModel.bind(this);
 
   this.onGridLayoutChange = this.onGridLayoutChange.bind(this);
   this.onHighlighterChange = this.onHighlighterChange.bind(this);
   this.onNewSelection = this.onNewSelection.bind(this);
   this.onSidebarSelect = this.onSidebarSelect.bind(this);
@@ -67,17 +66,16 @@ LayoutView.prototype = {
    * the redux store and adding the view into the inspector sidebar.
    */
   init: Task.async(function* () {
     if (!this.inspector) {
       return;
     }
 
     this.layoutInspector = yield this.inspector.walker.getLayoutInspector();
-    let store = this.store = Store();
 
     this.loadHighlighterSettings();
 
     let app = App({
       /**
        * Shows the box model properties under the box model if true, otherwise, hidden by
        * default.
        */
@@ -227,17 +225,17 @@ LayoutView.prototype = {
           if (grid.highlighted) {
             this.highlighters.showGridHighlighter(grid.nodeFront, highlighterSettings);
           }
         }
       },
     });
 
     let provider = createElement(Provider, {
-      store,
+      store: this.store,
       id: "layoutview",
       title: INSPECTOR_L10N.getStr("inspector.sidebar.layoutViewTitle2"),
       key: "layoutview",
     }, app);
 
     let defaultTab = Services.prefs.getCharPref("devtools.inspector.activeSidebar");
 
     this.inspector.addSidebarTab(
--- a/devtools/client/inspector/layout/moz.build
+++ b/devtools/client/inspector/layout/moz.build
@@ -8,11 +8,10 @@ DIRS += [
     'actions',
     'components',
     'reducers',
     'utils',
 ]
 
 DevToolsModules(
     'layout.js',
-    'store.js',
     'types.js',
 )
deleted file mode 100644
--- a/devtools/client/inspector/layout/reducers/index.js
+++ /dev/null
@@ -1,9 +0,0 @@
-/* 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";
-
-exports.boxModel = require("./box-model");
-exports.grids = require("./grids");
-exports.highlighterSettings = require("./highlighter-settings");
--- a/devtools/client/inspector/layout/reducers/moz.build
+++ b/devtools/client/inspector/layout/reducers/moz.build
@@ -3,10 +3,9 @@
 # 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/.
 
 DevToolsModules(
     'box-model.js',
     'grids.js',
     'highlighter-settings.js',
-    'index.js',
 )
--- a/devtools/client/inspector/moz.build
+++ b/devtools/client/inspector/moz.build
@@ -12,15 +12,17 @@ DIRS += [
     'shared'
 ]
 
 DevToolsModules(
     'breadcrumbs.js',
     'inspector-commands.js',
     'inspector-search.js',
     'panel.js',
+    'reducers.js',
+    'store.js',
     'toolsidebar.js',
 )
 
 BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
 
-with Files('**'):
-    BUG_COMPONENT = ('Firefox', 'Developer Tools: Inspector')
+with Files('**'):
+    BUG_COMPONENT = ('Firefox', 'Developer Tools: Inspector')
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/reducers.js
@@ -0,0 +1,12 @@
+/* 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";
+
+// This file exposes the Redux reducers of the box model, grid and grid highlighter
+// settings.
+
+exports.boxModel = require("devtools/client/inspector/layout/reducers/box-model");
+exports.grids = require("devtools/client/inspector/layout/reducers/grids");
+exports.highlighterSettings = require("devtools/client/inspector/layout/reducers/highlighter-settings");
rename from devtools/client/inspector/layout/store.js
rename to devtools/client/inspector/store.js
--- a/devtools/client/inspector/layout/store.js
+++ b/devtools/client/inspector/store.js
@@ -1,17 +1,17 @@
 /* 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 { combineReducers } = require("devtools/client/shared/vendor/redux");
 const createStore = require("devtools/client/shared/redux/create-store");
-const reducers = require("./reducers/index");
+const reducers = require("devtools/client/inspector/reducers");
 const flags = require("devtools/shared/flags");
 
 module.exports = function () {
   let shouldLog = false;
   let history;
 
   // If testing, store the action history in an array
   // we'll later attach to the store
--- a/docshell/base/PendingGlobalHistoryEntry.cpp
+++ b/docshell/base/PendingGlobalHistoryEntry.cpp
@@ -34,17 +34,17 @@ PendingGlobalHistoryEntry::ApplyChanges(
   nsresult rv;
   for (const URIVisit& visit : mVisits) {
     rv = aHistory->VisitURI(visit.mURI, visit.mLastVisitedURI, visit.mFlags);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   mVisits.Clear();
 
   for (const URITitle& title : mTitles) {
-    aHistory->SetURITitle(title.mURI, title.mTitle);
+    rv = aHistory->SetURITitle(title.mURI, title.mTitle);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   mTitles.Clear();
 
   return NS_OK;
 }
 
 nsresult
--- a/dom/base/nsFrameMessageManager.cpp
+++ b/dom/base/nsFrameMessageManager.cpp
@@ -1650,17 +1650,17 @@ nsMessageManagerScriptExecutor::InitChil
 {
   AutoSafeJSContext cx;
   nsContentUtils::GetSecurityManager()->GetSystemPrincipal(getter_AddRefs(mPrincipal));
 
   nsIXPConnect* xpc = nsContentUtils::XPConnect();
   const uint32_t flags = nsIXPConnect::INIT_JS_STANDARD_CLASSES;
 
   JS::CompartmentOptions options;
-  options.creationOptions().setZone(JS::SystemZone);
+  options.creationOptions().setSystemZone();
   options.behaviors().setVersion(JSVERSION_LATEST);
 
   if (xpc::SharedMemoryEnabled()) {
     options.creationOptions().setSharedMemoryAndAtomicsEnabled(true);
   }
 
   nsresult rv =
     xpc->InitClassesWithNewWrappedGlobal(cx, aScope, mPrincipal,
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -2731,17 +2731,17 @@ CreateNativeGlobalForInner(JSContext* aC
   // Sometimes add-ons load their own XUL windows, either as separate top-level
   // windows or inside a browser element. In such cases we want to tag the
   // window's compartment with the add-on ID. See bug 1092156.
   if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
     options.creationOptions().setAddonId(MapURIToAddonID(aURI));
   }
 
   if (top && top->GetGlobalJSObject()) {
-    options.creationOptions().setSameZoneAs(top->GetGlobalJSObject());
+    options.creationOptions().setExistingZone(top->GetGlobalJSObject());
   }
 
   options.creationOptions().setSecureContext(aIsSecureContext);
 
   xpc::InitGlobalObjectOptions(options, aPrincipal);
 
   // Determine if we need the Components object.
   bool needComponents = nsContentUtils::IsSystemPrincipal(aPrincipal) ||
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -106,17 +106,18 @@ const size_t gStackSize = 8192;
 
 // The default amount of time to wait from the user being idle to starting a
 // shrinking GC.
 #define NS_DEAULT_INACTIVE_GC_DELAY 300000 // ms
 
 // Maximum amount of time that should elapse between incremental GC slices
 #define NS_INTERSLICE_GC_DELAY      100 // ms
 
-// If we haven't painted in 100ms, we allow for a longer GC budget
+// If we haven't painted in 100ms, or we're in e10s parent process and
+// user isn't active, we allow for a longer GC budget.
 #define NS_INTERSLICE_GC_BUDGET     40 // ms
 
 // The amount of time we wait between a request to CC (after GC ran)
 // and doing the actual CC.
 #define NS_CC_DELAY                 6000 // ms
 
 #define NS_CC_SKIPPABLE_DELAY       250 // ms
 
@@ -183,17 +184,18 @@ static uint32_t sRemovedPurples = 0;
 static uint32_t sForgetSkippableBeforeCC = 0;
 static uint32_t sPreviousSuspectedCount = 0;
 static uint32_t sCleanupsSinceLastGC = UINT32_MAX;
 static bool sNeedsFullCC = false;
 static bool sNeedsFullGC = false;
 static bool sNeedsGCAfterCC = false;
 static bool sIncrementalCC = false;
 static bool sDidPaintAfterPreviousICCSlice = false;
-
+static bool sUserActive = false;
+static int32_t sActiveIntersliceGCBudget = 0; // ms;
 static nsScriptNameSpaceManager *gNameSpaceManager;
 
 static PRTime sFirstCollectionTime;
 
 static JSContext* sContext;
 
 static bool sIsInitialized;
 static bool sDidShutdown;
@@ -343,20 +345,22 @@ nsJSEnvironmentObserver::Observe(nsISupp
       nsJSContext::CycleCollectNow();
       if (NeedsGCAfterCC()) {
         nsJSContext::GarbageCollectNow(JS::gcreason::MEM_PRESSURE,
                                        nsJSContext::NonIncrementalGC,
                                        nsJSContext::ShrinkingGC);
       }
     }
   } else if (!nsCRT::strcmp(aTopic, "user-interaction-inactive")) {
+    sUserActive = false;
     if (sCompactOnUserInactive) {
       nsJSContext::PokeShrinkingGC();
     }
   } else if (!nsCRT::strcmp(aTopic, "user-interaction-active")) {
+    sUserActive = true;
     nsJSContext::KillShrinkingGCTimer();
     if (sIsCompactingOnUserInactive) {
       JS::AbortIncrementalGC(sContext);
     }
     MOZ_ASSERT(!sIsCompactingOnUserInactive);
   } else if (!nsCRT::strcmp(aTopic, "quit-application") ||
              !nsCRT::strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
     sShuttingDown = true;
@@ -1721,20 +1725,23 @@ nsJSContext::EndCycleCollectionCallback(
   gCCStats.Clear();
 }
 
 // static
 void
 InterSliceGCTimerFired(nsITimer *aTimer, void *aClosure)
 {
   nsJSContext::KillInterSliceGCTimer();
+  bool e10sParent = XRE_IsParentProcess() && BrowserTabsRemoteAutostart();
+  int64_t budget = e10sParent && sUserActive && sActiveIntersliceGCBudget ?
+    sActiveIntersliceGCBudget : NS_INTERSLICE_GC_BUDGET;
   nsJSContext::GarbageCollectNow(JS::gcreason::INTER_SLICE_GC,
                                  nsJSContext::IncrementalGC,
                                  nsJSContext::NonShrinkingGC,
-                                 NS_INTERSLICE_GC_BUDGET);
+                                 budget);
 }
 
 // static
 void
 GCTimerFired(nsITimer *aTimer, void *aClosure)
 {
   nsJSContext::KillGCTimer();
   uintptr_t reason = reinterpret_cast<uintptr_t>(aClosure);
@@ -2315,16 +2322,17 @@ SetMemoryGCModePrefChangedCallback(const
 }
 
 static void
 SetMemoryGCSliceTimePrefChangedCallback(const char* aPrefName, void* aClosure)
 {
   int32_t pref = Preferences::GetInt(aPrefName, -1);
   // handle overflow and negative pref values
   if (pref > 0 && pref < 100000)
+    sActiveIntersliceGCBudget = pref;
     JS_SetGCParameter(sContext, JSGC_SLICE_TIME_BUDGET, pref);
 }
 
 static void
 SetMemoryGCCompactingPrefChangedCallback(const char* aPrefName, void* aClosure)
 {
   bool pref = Preferences::GetBool(aPrefName);
   JS_SetGCParameter(sContext, JSGC_COMPACTING_ENABLED, pref);
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -2384,17 +2384,17 @@ class MethodDefiner(PropertyDefiner):
                                 "that generate @@iterator, such as "
                                 "maplike/setlike or aliased functions." %
                                 self.descriptor.interface.identifier.name)
             self.regular.append({
                 "name": "@@iterator",
                 "methodInfo": False,
                 "selfHostedName": "ArrayValues",
                 "length": 0,
-                "flags": "JSPROP_ENUMERATE",
+                "flags": "0", # Not enumerable, per spec.
                 "condition": MemberCondition()
             })
 
         if (static and
             not unforgeable and
             descriptor.interface.hasInterfaceObject() and
             NeedsGeneratedHasInstance(descriptor)):
             self.regular.append({
@@ -3068,35 +3068,38 @@ class CGCreateInterfaceObjectsMethod(CGA
 
             def defineAlias(alias):
                 if alias == "@@iterator":
                     symbolJSID = "SYMBOL_TO_JSID(JS::GetWellKnownSymbol(aCx, JS::SymbolCode::iterator))"
                     getSymbolJSID = CGGeneric(fill("JS::Rooted<jsid> iteratorId(aCx, ${symbolJSID});",
                                                    symbolJSID=symbolJSID))
                     defineFn = "JS_DefinePropertyById"
                     prop = "iteratorId"
+                    enumFlags = "0" # Not enumerable, per spec.
                 elif alias.startswith("@@"):
                     raise TypeError("Can't handle any well-known Symbol other than @@iterator")
                 else:
                     getSymbolJSID = None
                     defineFn = "JS_DefineProperty"
                     prop = '"%s"' % alias
-                return CGList([
-                    getSymbolJSID,
                     # XXX If we ever create non-enumerable properties that can
                     #     be aliased, we should consider making the aliases
                     #     match the enumerability of the property being aliased.
+                    enumFlags = "JSPROP_ENUMERATE"
+                return CGList([
+                    getSymbolJSID,
                     CGGeneric(fill(
                         """
-                        if (!${defineFn}(aCx, proto, ${prop}, aliasedVal, JSPROP_ENUMERATE)) {
+                        if (!${defineFn}(aCx, proto, ${prop}, aliasedVal, ${enumFlags})) {
                           $*{failureCode}
                         }
                         """,
                         defineFn=defineFn,
                         prop=prop,
+                        enumFlags=enumFlags,
                         failureCode=failureCode))
                 ], "\n")
 
             def defineAliasesFor(m):
                 return CGList([
                     CGGeneric(fill(
                         """
                         if (!JS_GetProperty(aCx, proto, \"${prop}\", &aliasedVal)) {
--- a/dom/bindings/ErrorResult.h
+++ b/dom/bindings/ErrorResult.h
@@ -181,16 +181,27 @@ public:
   void SuppressException();
 
   // Use StealNSResult() when you want to safely convert the TErrorResult to
   // an nsresult that you will then return to a caller.  This will
   // SuppressException(), since there will no longer be a way to report it.
   nsresult StealNSResult() {
     nsresult rv = ErrorCode();
     SuppressException();
+    // Don't propagate out our internal error codes that have special meaning.
+    if (rv == NS_ERROR_TYPE_ERR ||
+        rv == NS_ERROR_RANGE_ERR ||
+        rv == NS_ERROR_DOM_JS_EXCEPTION ||
+        rv == NS_ERROR_DOM_DOMEXCEPTION) {
+      // What about NS_ERROR_DOM_EXCEPTION_ON_JSCONTEXT?  I guess that can be
+      // legitimately passed on through....
+      // What to pick here?
+      return NS_ERROR_DOM_INVALID_STATE_ERR;
+    }
+
     return rv;
   }
 
   // Use MaybeSetPendingException to convert a TErrorResult to a pending
   // exception on the given JSContext.  This is the normal "throw an exception"
   // codepath.
   //
   // The return value is false if the TErrorResult represents success, true
@@ -390,17 +401,25 @@ private:
     MOZ_ASSERT(!IsErrorWithMessage(), "Don't overwrite errors with message");
     MOZ_ASSERT(aRv != NS_ERROR_DOM_JS_EXCEPTION, "Use ThrowJSException()");
     MOZ_ASSERT(!IsJSException(), "Don't overwrite JS exceptions");
     MOZ_ASSERT(aRv != NS_ERROR_DOM_DOMEXCEPTION, "Use ThrowDOMException()");
     MOZ_ASSERT(!IsDOMException(), "Don't overwrite DOM exceptions");
     MOZ_ASSERT(aRv != NS_ERROR_XPC_NOT_ENOUGH_ARGS, "May need to bring back ThrowNotEnoughArgsError");
     MOZ_ASSERT(aRv != NS_ERROR_DOM_EXCEPTION_ON_JSCONTEXT,
                "Use NoteJSContextException");
-    mResult = aRv;
+    // Don't trust people anyway, though.
+    if (aRv == NS_ERROR_TYPE_ERR ||
+        aRv == NS_ERROR_RANGE_ERR ||
+        aRv == NS_ERROR_DOM_JS_EXCEPTION ||
+        aRv == NS_ERROR_DOM_DOMEXCEPTION) {
+      mResult = NS_ERROR_UNEXPECTED;
+    } else {
+      mResult = aRv;
+    }
   }
 
   void ClearMessage();
   void ClearDOMExceptionInfo();
 
   // ClearUnionData will try to clear the data in our
   // mMessage/mJSException/mDOMExceptionInfo union.  After this the union may be
   // in an uninitialized state (e.g. mMessage or mDOMExceptionInfo may be
--- a/dom/bindings/SimpleGlobalObject.cpp
+++ b/dom/bindings/SimpleGlobalObject.cpp
@@ -105,17 +105,17 @@ SimpleGlobalObject::Create(GlobalType gl
 
     JS::CompartmentOptions options;
     options.creationOptions()
            .setInvisibleToDebugger(true)
            // Put our SimpleGlobalObjects in the system zone, so we won't create
            // lots of zones for what are probably very short-lived
            // compartments.  This should help them be GCed quicker and take up
            // less memory before they're GCed.
-          .setZone(JS::SystemZone);
+           .setSystemZone();
 
     if (NS_IsMainThread()) {
       nsCOMPtr<nsIPrincipal> principal = nsNullPrincipal::Create();
       options.creationOptions().setTrace(xpc::TraceXPCGlobal);
       global = xpc::CreateGlobalObject(cx, js::Jsvalify(&SimpleGlobalClass),
                                        nsJSPrincipals::get(principal),
                                        options);
     } else {
--- a/dom/canvas/TexUnpackBlob.cpp
+++ b/dom/canvas/TexUnpackBlob.cpp
@@ -309,25 +309,26 @@ TexUnpackBlob::ConvertIfNeeded(WebGLCont
         return true;
 
     const auto& dstIsPremult = webgl->mPixelStore_PremultiplyAlpha;
     const auto srcOrigin = (webgl->mPixelStore_FlipY ? gl::OriginPos::TopLeft
                                                      : gl::OriginPos::BottomLeft);
     const auto dstOrigin = gl::OriginPos::BottomLeft;
 
     if (srcFormat != dstFormat) {
-        webgl->GenerateWarning("%s: Conversion requires pixel reformatting.", funcName);
+        webgl->GeneratePerfWarning("%s: Conversion requires pixel reformatting.",
+                                   funcName);
     } else if (mSrcIsPremult != dstIsPremult) {
-        webgl->GenerateWarning("%s: Conversion requires change in"
-                               "alpha-premultiplication.",
-                               funcName);
+        webgl->GeneratePerfWarning("%s: Conversion requires change in"
+                                   "alpha-premultiplication.",
+                                   funcName);
     } else if (srcOrigin != dstOrigin) {
-        webgl->GenerateWarning("%s: Conversion requires y-flip.", funcName);
+        webgl->GeneratePerfWarning("%s: Conversion requires y-flip.", funcName);
     } else if (srcStride != dstStride) {
-        webgl->GenerateWarning("%s: Conversion requires change in stride.", funcName);
+        webgl->GeneratePerfWarning("%s: Conversion requires change in stride.", funcName);
     } else {
         return true;
     }
 
     ////
 
     const auto dstTotalBytes = CheckedUint32(rowCount) * dstStride;
     if (!dstTotalBytes.isValid()) {
@@ -637,19 +638,19 @@ TexUnpackImage::TexOrSubImage(bool isSub
             break;
         }
 
         // Blitting was successful, so we're done!
         *out_error = 0;
         return true;
     } while (false);
 
-    webgl->GenerateWarning("%s: Failed to hit GPU-copy fast-path. Falling back to CPU"
-                           " upload.",
-                           funcName);
+    webgl->GeneratePerfWarning("%s: Failed to hit GPU-copy fast-path. Falling back to CPU"
+                               " upload.",
+                               funcName);
 
     const RefPtr<gfx::SourceSurface> surf = mImage->GetAsSourceSurface();
 
     RefPtr<gfx::DataSourceSurface> dataSurf;
     if (surf) {
         // WARNING: OSX can lose our MakeCurrent here.
         dataSurf = surf->GetDataSurface();
     }
--- a/dom/html/test/test_formSubmission.html
+++ b/dom/html/test/test_formSubmission.html
@@ -676,19 +676,17 @@ function setDisabled(list, state) {
   Array.prototype.forEach.call(list, function(e) {
     e.disabled = state;
   });
 }
 
 var gen;
 function onFilesSet() {
   gen = runTest();
-  addLoadEvent(function() {
-    gen.next();
-  });
+  gen.next();
 }
 
 function* runTest() {
   // Set up the expectedSub array
   fileReader1 = new FileReader;
   fileReader1.readAsBinaryString(myFile1);
   fileReader2 = new FileReader;
   fileReader2.readAsBinaryString(myFile2);
--- a/dom/indexedDB/test/helpers.js
+++ b/dom/indexedDB/test/helpers.js
@@ -39,17 +39,17 @@ function clearAllDatabases(callback) {
   let request = qms.clearStoragesForPrincipal(principal);
   let cb = SpecialPowers.wrapCallback(callback);
   request.callback = cb;
 }
 
 var testHarnessGenerator = testHarnessSteps();
 testHarnessGenerator.next();
 
-function testHarnessSteps() {
+function* testHarnessSteps() {
   function nextTestHarnessStep(val) {
     testHarnessGenerator.next(val);
   }
 
   let testScriptPath;
   let testScriptFilename;
 
   let scripts = document.getElementsByTagName("script");
--- a/dom/indexedDB/test/unit/test_bad_origin_directory.js
+++ b/dom/indexedDB/test/unit/test_bad_origin_directory.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const url = "ftp://ftp.example.com";
   const name = "test_bad_origin_directory.js";
 
   let ios = SpecialPowers.Cc["@mozilla.org/network/io-service;1"]
                          .getService(SpecialPowers.Ci.nsIIOService);
 
   let uri = ios.newURI(url);
--- a/dom/indexedDB/test/unit/test_bug1056939.js
+++ b/dom/indexedDB/test/unit/test_bug1056939.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const dbName1 = "upgrade_test";
   const dbName2 = "testing.foobar";
   const dbName3 = "xxxxxxx.xxxxxx";
 
   clearAllDatabases(continueToNextStepSync);
   yield undefined;
 
--- a/dom/indexedDB/test/unit/test_cleanup_transaction.js
+++ b/dom/indexedDB/test/unit/test_cleanup_transaction.js
@@ -2,17 +2,17 @@
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var disableWorkerTest = "Need a way to set temporary prefs from a worker";
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const spec = "http://foo.com";
   const name =
     this.window ? window.location.pathname : "test_quotaExceeded_recovery";
   const objectStoreName = "foo";
 
   // We want 32 MB database, but there's the group limit so we need to
   // multiply by 5.
--- a/dom/indexedDB/test/unit/test_cursor_cycle.js
+++ b/dom/indexedDB/test/unit/test_cursor_cycle.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const Bob = { ss: "237-23-7732", name: "Bob" };
 
   let request = indexedDB.open(this.window ? window.location.pathname : "Splendid Test", 1);
   request.onerror = errorHandler;
   request.onupgradeneeded = grabEventAndContinueHandler;
   let event = yield undefined;
 
--- a/dom/indexedDB/test/unit/test_database_close_without_onclose.js
+++ b/dom/indexedDB/test/unit/test_database_close_without_onclose.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const name = this.window ? window.location.pathname :
                              "test_database_close_without_onclose.js";
 
   const checkpointSleepTimeSec = 10;
 
   let openRequest = indexedDB.open(name, 1);
   openRequest.onerror = errorHandler;
--- a/dom/indexedDB/test/unit/test_defaultStorageUpgrade.js
+++ b/dom/indexedDB/test/unit/test_defaultStorageUpgrade.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const openParams = [
     // This one lives in storage/default/http+++localhost
     { url: "http://localhost", dbName: "dbA", dbVersion: 1 },
 
     // This one lives in storage/default/http+++www.mozilla.org
     { url: "http://www.mozilla.org", dbName: "dbB", dbVersion: 1 },
 
--- a/dom/indexedDB/test/unit/test_file_copy_failure.js
+++ b/dom/indexedDB/test/unit/test_file_copy_failure.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const name = "test_file_copy_failure.js";
   const objectStoreName = "Blobs";
   const blob = getBlob(getView(1024));
 
   info("Opening database");
 
   let request = indexedDB.open(name);
--- a/dom/indexedDB/test/unit/test_globalObjects_ipc.js
+++ b/dom/indexedDB/test/unit/test_globalObjects_ipc.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   // Test for IDBKeyRange and indexedDB availability in ipcshell.
   run_test_in_child("./GlobalObjectsChild.js", function() {
     do_test_finished();
     continueToNextStep();
   });
   yield undefined;
 
--- a/dom/indexedDB/test/unit/test_globalObjects_other.js
+++ b/dom/indexedDB/test/unit/test_globalObjects_other.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   let ioService =
     Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
 
   function getSpec(filename) {
     let file = do_get_file(filename);
     let uri = ioService.newFileURI(file);
     return uri.spec;
--- a/dom/indexedDB/test/unit/test_globalObjects_xpc.js
+++ b/dom/indexedDB/test/unit/test_globalObjects_xpc.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const name = "Splendid Test";
 
   // Test for IDBKeyRange and indexedDB availability in xpcshell.
   let keyRange = IDBKeyRange.only(42);
   ok(keyRange, "Got keyRange");
 
   let request = indexedDB.open(name, 1);
--- a/dom/indexedDB/test/unit/test_idbSubdirUpgrade.js
+++ b/dom/indexedDB/test/unit/test_idbSubdirUpgrade.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const openParams = [
     // This one lives in storage/default/http+++www.mozilla.org
     { url: "http://www.mozilla.org", dbName: "dbB", dbVersion: 1 },
 
     // This one lives in storage/default/1007+t+https+++developer.cdn.mozilla.net
     { appId: 1007, inIsolatedMozBrowser: true, url: "https://developer.cdn.mozilla.net",
       dbName: "dbN", dbVersion: 1 },
--- a/dom/indexedDB/test/unit/test_idle_maintenance.js
+++ b/dom/indexedDB/test/unit/test_idle_maintenance.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   let uri = Cc["@mozilla.org/network/io-service;1"].
             getService(Ci.nsIIOService).
             newURI("https://www.example.com");
   let ssm = Cc["@mozilla.org/scriptsecuritymanager;1"]
               .getService(Ci.nsIScriptSecurityManager);
   let principal = ssm.createCodebasePrincipal(uri, {});
 
--- a/dom/indexedDB/test/unit/test_invalidate.js
+++ b/dom/indexedDB/test/unit/test_invalidate.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const databaseName =
     ("window" in this) ? window.location.pathname : "Test";
 
   let dbCount = 0;
 
   // Test invalidating during a versionchange transaction.
   info("Opening database " + ++dbCount);
--- a/dom/indexedDB/test/unit/test_metadata2Restore.js
+++ b/dom/indexedDB/test/unit/test_metadata2Restore.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const openParams = [
     // This one lives in storage/permanent/chrome
     // The .metadata-v2 file was intentionally removed for this origin directory
     // to test restoring.
     { dbName: "dbA",
       dbOptions: { version: 1, storage: "persistent" } },
 
--- a/dom/indexedDB/test/unit/test_metadataRestore.js
+++ b/dom/indexedDB/test/unit/test_metadataRestore.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const openParams = [
     // This one lives in storage/permanent/chrome
     { dbName: "dbA",
       dbOptions: { version: 1, storage: "persistent" } },
 
     // This one lives in storage/temporary/http+++localhost
     { url: "http://localhost", dbName: "dbB",
--- a/dom/indexedDB/test/unit/test_mutableFileUpgrade.js
+++ b/dom/indexedDB/test/unit/test_mutableFileUpgrade.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const dbNames = [
     "No files",
     "Blobs and mutable files"
   ]
   const version = 1;
   const objectStoreName = "test";
 
--- a/dom/indexedDB/test/unit/test_oldDirectories.js
+++ b/dom/indexedDB/test/unit/test_oldDirectories.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   // This lives in storage/default/http+++www.mozilla.org
   const url = "http://www.mozilla.org";
   const dbName = "dbC";
   const dbVersion = 1;
 
   let ios = SpecialPowers.Cc["@mozilla.org/network/io-service;1"]
                          .getService(SpecialPowers.Ci.nsIIOService);
--- a/dom/indexedDB/test/unit/test_open_for_principal.js
+++ b/dom/indexedDB/test/unit/test_open_for_principal.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const name = this.window ? window.location.pathname : "Splendid Test";
 
   const objectStoreName = "Foo";
 
   const data = { key: 1, value: "bar" };
 
   let request = indexedDB.open(name, 1);
--- a/dom/indexedDB/test/unit/test_persistenceType.js
+++ b/dom/indexedDB/test/unit/test_persistenceType.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const name = "Splendid Test";
   const version = 1;
 
   const objectStoreName = "Foo";
   const data = { key: 1, value: "bar" };
 
   try {
--- a/dom/indexedDB/test/unit/test_quotaExceeded_recovery.js
+++ b/dom/indexedDB/test/unit/test_quotaExceeded_recovery.js
@@ -2,17 +2,17 @@
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var disableWorkerTest = "Need a way to set temporary prefs from a worker";
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const spec = "http://foo.com";
   const name =
     this.window ? window.location.pathname : "test_quotaExceeded_recovery";
   const objectStoreName = "foo";
 
   const android = mozinfo.os == "android";
 
--- a/dom/indexedDB/test/unit/test_schema18upgrade.js
+++ b/dom/indexedDB/test/unit/test_schema18upgrade.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const testName = "schema18upgrade";
   const testKeys = [
     -1/0,
     -1.7e308,
     -10000,
     -2,
     -1.5,
--- a/dom/indexedDB/test/unit/test_schema21upgrade.js
+++ b/dom/indexedDB/test/unit/test_schema21upgrade.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const testName = "schema21upgrade";
   const testKeys = [
     -1/0,
     -1.7e308,
     -10000,
     -2,
     -1.5,
--- a/dom/indexedDB/test/unit/test_schema23upgrade.js
+++ b/dom/indexedDB/test/unit/test_schema23upgrade.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const openParams = [
     // This one lives in storage/default/http+++www.mozilla.org
     { url: "http://www.mozilla.org", dbName: "dbB", dbVersion: 1 },
 
     // This one lives in storage/default/1007+t+https+++developer.cdn.mozilla.net
     { appId: 1007, inIsolatedMozBrowser: true, url: "https://developer.cdn.mozilla.net",
       dbName: "dbN", dbVersion: 1 },
--- a/dom/indexedDB/test/unit/test_setVersion_events.js
+++ b/dom/indexedDB/test/unit/test_setVersion_events.js
@@ -15,18 +15,22 @@ function* testSteps()
   // Sanity checks
   ok(request instanceof IDBRequest, "Request should be an IDBRequest");
   ok(request instanceof IDBOpenDBRequest, "Request should be an IDBOpenDBRequest");
   ok(request instanceof EventTarget, "Request should be an EventTarget");
   is(request.source, null, "Request should have no source");
   try {
     request.result;
     ok(false, "Getter should have thrown!");
-  } catch (e if e.result == 0x8053000b /* NS_ERROR_DOM_INVALID_STATE_ERR */) {
-    ok(true, "Getter threw the right exception");
+  } catch (e) {
+    if (e.result == 0x8053000b /* NS_ERROR_DOM_INVALID_STATE_ERR */) {
+      ok(true, "Getter threw the right exception");
+    } else {
+      throw e;
+    }
   }
 
   request.onerror = errorHandler;
   request.onsuccess = grabEventAndContinueHandler;
   let event = yield undefined;
 
   let versionChangeEventCount = 0;
   let db1, db2, db3;
--- a/dom/indexedDB/test/unit/test_snappyUpgrade.js
+++ b/dom/indexedDB/test/unit/test_snappyUpgrade.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const name = "test_snappyUpgrade.js";
   const objectStoreName = "test";
   const testString = "Lorem ipsum his ponderum delicatissimi ne, at noster dolores urbanitas pro, cibo elaboraret no his. Ea dicunt maiorum usu. Ad appareat facilisis mediocritatem eos. Tale graeci mentitum in eos, hinc insolens at nam. Graecis nominavi aliquyam eu vix. Id solet assentior sadipscing pro. Et per atqui graecis, usu quot viris repudiandae ei, mollis evertitur an nam. At nam dolor ignota, liber labore omnesque ea mei, has movet voluptaria in. Vel an impetus omittantur. Vim movet option salutandi ex, ne mei ignota corrumpit. Mucius comprehensam id per. Est ea putant maiestatis.";
 
   info("Installing profile");
 
   clearAllDatabases(continueToNextStepSync);
--- a/dom/indexedDB/test/unit/test_storagePersistentUpgrade.js
+++ b/dom/indexedDB/test/unit/test_storagePersistentUpgrade.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const openParams = [
     // This one lives in storage/default/http+++www.mozilla.org
     { url: "http://www.mozilla.org", dbName: "dbB", dbVersion: 1 },
 
     // This one lives in storage/default/1007+t+https+++developer.cdn.mozilla.net
     { appId: 1007, inIsolatedMozBrowser: true, url: "https://developer.cdn.mozilla.net",
       dbName: "dbN", dbVersion: 1 },
--- a/dom/indexedDB/test/unit/test_temporary_storage.js
+++ b/dom/indexedDB/test/unit/test_temporary_storage.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const name = this.window ?
                window.location.pathname :
                "test_temporary_storage.js";
   const finalVersion = 2;
 
   const tempStorageLimitKB = 1024;
   const checkpointSleepTimeSec = 5;
--- a/dom/indexedDB/test/unit/test_wasm_recompile.js
+++ b/dom/indexedDB/test/unit/test_wasm_recompile.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const name = "test_wasm_recompile.js";
 
   const objectStoreName = "Wasm";
 
   const wasmData = { key: 1, wasm: null };
 
   // The goal of this test is to prove that wasm is recompiled and the on-disk
--- a/dom/indexedDB/test/unit/test_writer_starvation.js
+++ b/dom/indexedDB/test/unit/test_writer_starvation.js
@@ -7,17 +7,17 @@ if (!this.window) {
   this.runTest = function() {
     todo(false, "Test disabled in xpcshell test suite for now");
     finishTest();
   }
 }
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const name = this.window ? window.location.pathname : "Splendid Test";
 
   // Needs to be enough to saturate the thread pool.
   const SYNC_REQUEST_COUNT = 25;
 
   let request = indexedDB.open(name, 1);
   request.onerror = errorHandler;
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1338,17 +1338,17 @@ ContentParent::ShutDownMessageManager()
       CHILD_PROCESS_SHUTDOWN_MESSAGE, false,
       nullptr, nullptr, nullptr, nullptr);
 
   mMessageManager->Disconnect();
   mMessageManager = nullptr;
 }
 
 void
-ContentParent::MarkAsDead()
+ContentParent::MarkAsTroubled()
 {
   if (sBrowserContentParents) {
     nsTArray<ContentParent*>* contentParents =
       sBrowserContentParents->Get(mRemoteType);
     if (contentParents) {
       contentParents->RemoveElement(this);
       if (contentParents->IsEmpty()) {
         sBrowserContentParents->Remove(mRemoteType);
@@ -1362,17 +1362,23 @@ ContentParent::MarkAsDead()
 
   if (sPrivateContent) {
     sPrivateContent->RemoveElement(this);
     if (!sPrivateContent->Length()) {
       delete sPrivateContent;
       sPrivateContent = nullptr;
     }
   }
-
+  mIsAvailable = false;
+}
+
+void
+ContentParent::MarkAsDead()
+{
+  MarkAsTroubled();
   mIsAlive = false;
 }
 
 void
 ContentParent::OnChannelError()
 {
   RefPtr<ContentParent> content(this);
   PContentParent::OnChannelError();
@@ -1662,18 +1668,18 @@ ContentParent::ActorDestroy(ActorDestroy
 
 bool
 ContentParent::ShouldKeepProcessAlive() const
 {
   if (!sBrowserContentParents) {
     return false;
   }
 
-  // If we have already been marked as dead, don't prevent shutdown.
-  if (!IsAlive()) {
+  // If we have already been marked as troubled/dead, don't prevent shutdown.
+  if (!IsAvailable()) {
     return false;
   }
 
   auto contentParents = sBrowserContentParents->Get(mRemoteType);
   if (!contentParents) {
     return false;
   }
 
@@ -1807,16 +1813,17 @@ ContentParent::GetTestShellSingleton()
 
 void
 ContentParent::InitializeMembers()
 {
   mSubprocess = nullptr;
   mChildID = gContentChildID++;
   mGeolocationWatchID = -1;
   mNumDestroyingTabs = 0;
+  mIsAvailable = true;
   mIsAlive = true;
   mSendPermissionUpdates = false;
   mCalledClose = false;
   mCalledKillHard = false;
   mCreatedPairedMinidumps = false;
   mShutdownPending = false;
   mIPCOpen = true;
   mHangMonitorActor = nullptr;
@@ -4700,19 +4707,24 @@ ContentParent::RecvNotifyPushSubscriptio
   PushSubscriptionModifiedDispatcher dispatcher(aScope, aPrincipal);
   Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObservers()));
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 ContentParent::RecvNotifyLowMemory()
 {
+  MarkAsTroubled();
+
+  Telemetry::ScalarAdd(Telemetry::ScalarID::DOM_CONTENTPROCESS_TROUBLED_DUE_TO_MEMORY, 1);
+
 #ifdef MOZ_CRASHREPORTER
   nsThread::SaveMemoryReportNearOOM(nsThread::ShouldSaveMemoryReport::kForceReport);
 #endif
+
   return IPC_OK();
 }
 
 /* static */ void
 ContentParent::BroadcastBlobURLRegistration(const nsACString& aURI,
                                             BlobImpl* aBlobImpl,
                                             nsIPrincipal* aPrincipal,
                                             ContentParent* aIgnoreThisCP)
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -349,16 +349,20 @@ public:
   DeallocateTabId(const TabId& aTabId,
                   const ContentParentId& aCpId,
                   bool aMarkedDestroying);
 
   void ReportChildAlreadyBlocked();
 
   bool RequestRunToCompletion();
 
+  bool IsAvailable() const
+  {
+    return mIsAvailable;
+  }
   bool IsAlive() const override;
 
   virtual bool IsForBrowser() const override
   {
     return mIsForBrowser;
   }
 
   GeckoChildProcessHost* Process() const
@@ -610,16 +614,24 @@ protected:
   virtual void ActorDestroy(ActorDestroyReason why) override;
 
   bool ShouldContinueFromReplyTimeout() override;
 
   void OnVarChanged(const GfxVarUpdate& aVar) override;
   void OnCompositorUnexpectedShutdown() override;
 
 private:
+  /**
+   * A map of the remote content process type to a list of content parents
+   * currently available to host *new* tabs/frames of that type.
+   *
+   * If a content process is identified as troubled or dead, it will be
+   * removed from this list, but will still be in the sContentParents list for
+   * the GetAll/GetAllEvenIfDead APIs.
+   */
   static nsClassHashtable<nsStringHashKey, nsTArray<ContentParent*>>* sBrowserContentParents;
   static nsTArray<ContentParent*>* sPrivateContent;
   static StaticAutoPtr<LinkedList<ContentParent> > sContentParents;
 
   static void JoinProcessesIOThread(const nsTArray<ContentParent*>* aProcesses,
                                     Monitor* aMonitor, bool* aDone);
 
   static hal::ProcessPriority GetInitialProcessPriority(Element* aFrameElement);
@@ -690,16 +702,22 @@ private:
 
   /**
    * Decide whether the process should be kept alive even when it would normally
    * be shut down, for example when all its tabs are closed.
    */
   bool ShouldKeepProcessAlive() const;
 
   /**
+   * Mark this ContentParent as "troubled". This means that it is still alive,
+   * but it won't be returned for new tabs in GetNewOrUsedBrowserProcess.
+   */
+  void MarkAsTroubled();
+
+  /**
    * Mark this ContentParent as dead for the purposes of Get*().
    * This method is idempotent.
    */
   void MarkAsDead();
 
   /**
    * How we will shut down this ContentParent and its subprocess.
    */
@@ -1133,20 +1151,22 @@ private:
   // that even content processes that are 100% blocked (say from
   // SIGSTOP), are still killed eventually.  This task enforces that
   // timer.
   nsCOMPtr<nsITimer> mForceKillTimer;
   // How many tabs we're waiting to finish their destruction
   // sequence.  Precisely, how many TabParents have called
   // NotifyTabDestroying() but not called NotifyTabDestroyed().
   int32_t mNumDestroyingTabs;
-  // True only while this is ready to be used to host remote tabs.
-  // This must not be used for new purposes after mIsAlive goes to
-  // false, but some previously scheduled IPC traffic may still pass
-  // through.
+  // True only while this process is in "good health" and may be used for
+  // new remote tabs.
+  bool mIsAvailable;
+  // True only while remote content is being actively used from this process.
+  // After mIsAlive goes to false, some previously scheduled IPC traffic may
+  // still pass through.
   bool mIsAlive;
 
   bool mSendPermissionUpdates;
   bool mIsForBrowser;
 
   // These variables track whether we've called Close() and KillHard() on our
   // channel.
   bool mCalledClose;
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -1070,20 +1070,20 @@ public:
     // in cycles that were broken during CC shutdown.
     nsCycleCollector_shutdown();
 
     // The CC is shut down, and the superclass destructor will GC, so make sure
     // we don't try to CC again.
     mWorkerPrivate = nullptr;
   }
 
-  nsresult Initialize(JSContext* aParentContext)
+  nsresult Initialize(JSRuntime* aParentRuntime)
   {
     nsresult rv =
-      CycleCollectedJSContext::Initialize(aParentContext,
+      CycleCollectedJSContext::Initialize(aParentRuntime,
                                           WORKER_DEFAULT_RUNTIME_HEAPSIZE,
                                           WORKER_DEFAULT_NURSERY_SIZE);
      if (NS_WARN_IF(NS_FAILED(rv))) {
        return rv;
      }
 
     JSContext* cx = Context();
 
@@ -1182,17 +1182,17 @@ public:
 private:
   WorkerPrivate* mWorkerPrivate;
 };
 
 class WorkerThreadPrimaryRunnable final : public Runnable
 {
   WorkerPrivate* mWorkerPrivate;
   RefPtr<WorkerThread> mThread;
-  JSContext* mParentContext;
+  JSRuntime* mParentRuntime;
 
   class FinishedRunnable final : public Runnable
   {
     RefPtr<WorkerThread> mThread;
 
   public:
     explicit FinishedRunnable(already_AddRefed<WorkerThread> aThread)
     : Runnable("WorkerThreadPrimaryRunnable::FinishedRunnable")
@@ -1208,18 +1208,18 @@ class WorkerThreadPrimaryRunnable final 
     { }
 
     NS_DECL_NSIRUNNABLE
   };
 
 public:
   WorkerThreadPrimaryRunnable(WorkerPrivate* aWorkerPrivate,
                               WorkerThread* aThread,
-                              JSContext* aParentContext)
-  : mWorkerPrivate(aWorkerPrivate), mThread(aThread), mParentContext(aParentContext)
+                              JSRuntime* aParentRuntime)
+  : mWorkerPrivate(aWorkerPrivate), mThread(aThread), mParentRuntime(aParentRuntime)
   {
     MOZ_ASSERT(aWorkerPrivate);
     MOZ_ASSERT(aThread);
   }
 
   NS_DECL_ISUPPORTS_INHERITED
 
 private:
@@ -1888,17 +1888,17 @@ RuntimeService::ScheduleWorker(WorkerPri
 
   if (NS_FAILED(thread->SetPriority(priority))) {
     NS_WARNING("Could not set the thread's priority!");
   }
 
   JSContext* cx = CycleCollectedJSContext::Get()->Context();
   nsCOMPtr<nsIRunnable> runnable =
     new WorkerThreadPrimaryRunnable(aWorkerPrivate, thread,
-                                    JS_GetParentContext(cx));
+                                    JS_GetParentRuntime(cx));
   if (NS_FAILED(thread->DispatchPrimaryRunnable(friendKey, runnable.forget()))) {
     UnregisterWorker(aWorkerPrivate);
     return false;
   }
 
   return true;
 }
 
@@ -2856,17 +2856,17 @@ WorkerThreadPrimaryRunnable::Run()
   SetThreadHelper threadHelper(mWorkerPrivate, mThread);
 
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   {
     nsCycleCollector_startup();
 
     WorkerJSContext context(mWorkerPrivate);
-    nsresult rv = context.Initialize(mParentContext);
+    nsresult rv = context.Initialize(mParentRuntime);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     JSContext* cx = context.Context();
 
     if (!InitJSContextForWorker(mWorkerPrivate, cx)) {
       // XXX need to fire an error at parent.
--- a/extensions/cookie/test/unit/head_cookies.js
+++ b/extensions/cookie/test/unit/head_cookies.js
@@ -37,26 +37,25 @@ function do_check_throws(f, result, stac
 }
 
 // Helper to step a generator function and catch a StopIteration exception.
 function do_run_generator(generator)
 {
   try {
     generator.next();
   } catch (e) {
-    if (e != StopIteration)
-      do_throw("caught exception " + e, Components.stack.caller);
+    do_throw("caught exception " + e, Components.stack.caller);
   }
 }
 
 // Helper to finish a generator function test.
 function do_finish_generator_test(generator)
 {
   do_execute_soon(function() {
-    generator.close();
+    generator.return();
     do_test_finished();
   });
 }
 
 function _observer(generator, topic) {
   Services.obs.addObserver(this, topic, false);
 
   this.generator = generator;
--- a/extensions/cookie/test/unit/test_cookies_async_failure.js
+++ b/extensions/cookie/test/unit/test_cookies_async_failure.js
@@ -22,22 +22,22 @@ var test_generator = do_run_test();
 
 function run_test() {
   do_test_pending();
   do_run_generator(test_generator);
 }
 
 function finish_test() {
   do_execute_soon(function() {
-    test_generator.close();
+    test_generator.return();
     do_test_finished();
   });
 }
 
-function do_run_test() {
+function* do_run_test() {
   // Set up a profile.
   this.profile = do_get_profile();
 
   // Allow all cookies.
   Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
 
   // Get the cookie file and the backup file.
   do_check_false(do_get_cookie_file(profile).exists());
@@ -113,17 +113,17 @@ function do_corrupt_db(file)
   }
   ostream.flush();
   ostream.close();
 
   do_check_eq(file.clone().fileSize, size);
   return size;
 }
 
-function run_test_1(generator)
+function* run_test_1(generator)
 {
   // Load the profile and populate it.
   let uri = NetUtil.newURI("http://foo.com/");
   Services.cookies.setCookieString(uri, null, "oh=hai; max-age=1000", null);
 
   // Close the profile.
   do_close_profile(sub_generator);
   yield;
@@ -202,17 +202,17 @@ function run_test_1(generator)
   // Clean up.
   do_get_cookie_file(profile).remove(false);
   do_get_backup_file(profile).remove(false);
   do_check_false(do_get_cookie_file(profile).exists());
   do_check_false(do_get_backup_file(profile).exists());
   do_run_generator(generator);
 }
 
-function run_test_2(generator)
+function* run_test_2(generator)
 {
   // Load the profile and populate it.
   do_load_profile();
   for (let i = 0; i < 3000; ++i) {
     let uri = NetUtil.newURI("http://" + i + ".com/");
     Services.cookies.setCookieString(uri, null, "oh=hai; max-age=1000", null);
   }
 
@@ -268,17 +268,17 @@ function run_test_2(generator)
   // Clean up.
   do_get_cookie_file(profile).remove(false);
   do_get_backup_file(profile).remove(false);
   do_check_false(do_get_cookie_file(profile).exists());
   do_check_false(do_get_backup_file(profile).exists());
   do_run_generator(generator);
 }
 
-function run_test_3(generator)
+function* run_test_3(generator)
 {
   // Set the maximum cookies per base domain limit to a large value, so that
   // corrupting the database is easier.
   Services.prefs.setIntPref("network.cookie.maxPerHost", 3000);
 
   // Load the profile and populate it.
   do_load_profile();
   for (let i = 0; i < 10; ++i) {
@@ -363,17 +363,17 @@ function run_test_3(generator)
   // Clean up.
   do_get_cookie_file(profile).remove(false);
   do_get_backup_file(profile).remove(false);
   do_check_false(do_get_cookie_file(profile).exists());
   do_check_false(do_get_backup_file(profile).exists());
   do_run_generator(generator);
 }
 
-function run_test_4(generator)
+function* run_test_4(generator)
 {
   // Load the profile and populate it.
   do_load_profile();
   for (let i = 0; i < 3000; ++i) {
     let uri = NetUtil.newURI("http://" + i + ".com/");
     Services.cookies.setCookieString(uri, null, "oh=hai; max-age=1000", null);
   }
 
@@ -431,17 +431,17 @@ function run_test_4(generator)
   // Clean up.
   do_get_cookie_file(profile).remove(false);
   do_get_backup_file(profile).remove(false);
   do_check_false(do_get_cookie_file(profile).exists());
   do_check_false(do_get_backup_file(profile).exists());
   do_run_generator(generator);
 }
 
-function run_test_4(generator)
+function* run_test_4(generator)
 {
   // Load the profile and populate it.
   do_load_profile();
   for (let i = 0; i < 3000; ++i) {
     let uri = NetUtil.newURI("http://" + i + ".com/");
     Services.cookies.setCookieString(uri, null, "oh=hai; max-age=1000", null);
   }
 
@@ -503,17 +503,17 @@ function run_test_4(generator)
   // Clean up.
   do_get_cookie_file(profile).remove(false);
   do_get_backup_file(profile).remove(false);
   do_check_false(do_get_cookie_file(profile).exists());
   do_check_false(do_get_backup_file(profile).exists());
   do_run_generator(generator);
 }
 
-function run_test_5(generator)
+function* run_test_5(generator)
 {
   // Load the profile and populate it.
   do_load_profile();
   let uri = NetUtil.newURI("http://bar.com/");
   Services.cookies.setCookieString(uri, null, "oh=hai; path=/; max-age=1000",
     null);
   for (let i = 0; i < 3000; ++i) {
     let uri = NetUtil.newURI("http://" + i + ".com/");
--- a/extensions/cookie/test/unit/test_cookies_persistence.js
+++ b/extensions/cookie/test/unit/test_cookies_persistence.js
@@ -9,22 +9,22 @@ var test_generator = do_run_test();
 
 function run_test() {
   do_test_pending();
   test_generator.next();
 }
 
 function finish_test() {
   do_execute_soon(function() {
-    test_generator.close();
+    test_generator.return();
     do_test_finished();
   });
 }
 
-function do_run_test() {
+function* do_run_test() {
   // Set up a profile.
   let profile = do_get_profile();
 
   // Create URIs and channels pointing to foo.com and bar.com.
   // We will use these to put foo.com into first and third party contexts.
   var spec1 = "http://foo.com/foo.html";
   var spec2 = "http://bar.com/bar.html";
   var uri1 = NetUtil.newURI(spec1);
--- a/extensions/cookie/test/unit/test_cookies_privatebrowsing.js
+++ b/extensions/cookie/test/unit/test_cookies_privatebrowsing.js
@@ -7,27 +7,27 @@ var test_generator = do_run_test();
 
 function run_test() {
   do_test_pending();
   do_run_generator(test_generator);
 }
 
 function finish_test() {
   do_execute_soon(function() {
-    test_generator.close();
+    test_generator.return();
     do_test_finished();
   });
 }
 
 function make_channel(url) {
   return NetUtil.newChannel({uri: url, loadUsingSystemPrincipal: true})
                 .QueryInterface(Ci.nsIHttpChannel);
 }
 
-function do_run_test() {
+function* do_run_test() {
   // Set up a profile.
   let profile = do_get_profile();
 
   // Test with cookies enabled.
   Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
 
   // Create URIs pointing to foo.com and bar.com.
   let uri1 = NetUtil.newURI("http://foo.com/foo.html");
--- a/extensions/cookie/test/unit/test_cookies_profile_close.js
+++ b/extensions/cookie/test/unit/test_cookies_profile_close.js
@@ -7,22 +7,22 @@ var test_generator = do_run_test();
 
 function run_test() {
   do_test_pending();
   test_generator.next();
 }
 
 function finish_test() {
   do_execute_soon(function() {
-    test_generator.close();
+    test_generator.return();
     do_test_finished();
   });
 }
 
-function do_run_test() {
+function* do_run_test() {
   // Set up a profile.
   let profile = do_get_profile();
 
   // Allow all cookies.
   Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
 
   // Start the cookieservice.
   Services.cookies;
--- a/extensions/cookie/test/unit/test_cookies_read.js
+++ b/extensions/cookie/test/unit/test_cookies_read.js
@@ -9,22 +9,22 @@ var CMAX = 1000;    // # of cookies to c
 
 function run_test() {
   do_test_pending();
   test_generator.next();
 }
 
 function finish_test() {
   do_execute_soon(function() {
-    test_generator.close();
+    test_generator.return();
     do_test_finished();
   });
 }
 
-function do_run_test() {
+function* do_run_test() {
   // Set up a profile.
   let profile = do_get_profile();
 
   // Allow all cookies.
   Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
 
   // Start the cookieservice, to force creation of a database.
   Services.cookies;
--- a/extensions/cookie/test/unit/test_cookies_sync_failure.js
+++ b/extensions/cookie/test/unit/test_cookies_sync_failure.js
@@ -24,22 +24,22 @@ var test_generator = do_run_test();
 
 function run_test() {
   do_test_pending();
   do_run_generator(test_generator);
 }
 
 function finish_test() {
   do_execute_soon(function() {
-    test_generator.close();
+    test_generator.return();
     do_test_finished();
   });
 }
 
-function do_run_test() {
+function* do_run_test() {
   // Set up a profile.
   this.profile = do_get_profile();
 
   // Allow all cookies.
   Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
 
   // Get the cookie file and the backup file.
   this.cookieFile = profile.clone();
@@ -131,17 +131,17 @@ function create_garbage_file(file)
 function check_garbage_file(file)
 {
   do_check_true(file.exists());
   do_check_eq(file.fileSize, garbage.length);
   file.remove(false);
   do_check_false(file.exists());
 }
 
-function run_test_1(generator)
+function* run_test_1(generator)
 {
   // Create a garbage database file.
   create_garbage_file(cookieFile);
 
   // Load the profile and populate it.
   let uri = NetUtil.newURI("http://foo.com/");
   Services.cookies.setCookieString(uri, null, "oh=hai; max-age=1000", null);
 
@@ -160,17 +160,17 @@ function run_test_1(generator)
   yield;
 
   // Clean up.
   cookieFile.remove(false);
   do_check_false(cookieFile.exists());
   do_run_generator(generator);
 }
 
-function run_test_2(generator)
+function* run_test_2(generator)
 {
   // Load the profile and populate it.
   do_load_profile();
   let uri = NetUtil.newURI("http://foo.com/");
   Services.cookies.setCookieString(uri, null, "oh=hai; max-age=1000", null);
 
   // Fake a profile change.
   do_close_profile(sub_generator);
@@ -191,17 +191,17 @@ function run_test_2(generator)
   yield;
 
   // Clean up.
   cookieFile.remove(false);
   do_check_false(cookieFile.exists());
   do_run_generator(generator);
 }
 
-function run_test_3(generator, schema)
+function* run_test_3(generator, schema)
 {
   // Manually create a schema 2 database, populate it, and set the schema
   // version to the desired number.
   let schema2db = new CookieDatabaseConnection(do_get_cookie_file(profile), 2);
   schema2db.insertCookie(cookie);
   schema2db.db.schemaVersion = schema;
   schema2db.close();
 
@@ -219,17 +219,17 @@ function run_test_3(generator, schema)
   db.close();
 
   // Clean up.
   cookieFile.remove(false);
   do_check_false(cookieFile.exists());
   do_run_generator(generator);
 }
 
-function run_test_4_exists(generator, schema, stmt)
+function* run_test_4_exists(generator, schema, stmt)
 {
   // Manually create a database, populate it, and add the desired column.
   let db = new CookieDatabaseConnection(do_get_cookie_file(profile), schema);
   db.insertCookie(cookie);
   db.db.executeSimpleSQL(stmt);
   db.close();
 
   // Load the profile and check that migration fails.
@@ -249,17 +249,17 @@ function run_test_4_exists(generator, sc
   // Clean up.
   cookieFile.remove(false);
   backupFile.remove(false);
   do_check_false(cookieFile.exists());
   do_check_false(backupFile.exists());
   do_run_generator(generator);
 }
 
-function run_test_4_baseDomain(generator)
+function* run_test_4_baseDomain(generator)
 {
   // Manually create a database and populate it with a bad host.
   let db = new CookieDatabaseConnection(do_get_cookie_file(profile), 2);
   let badCookie = new Cookie("oh", "hai", ".", "/", this.futureExpiry, this.now,
     this.now, false, false, false);
   db.insertCookie(badCookie);
   db.close();
 
--- a/extensions/cookie/test/unit/test_cookies_thirdparty_session.js
+++ b/extensions/cookie/test/unit/test_cookies_thirdparty_session.js
@@ -9,22 +9,22 @@ var test_generator = do_run_test();
 
 function run_test() {
   do_test_pending();
   test_generator.next();
 }
 
 function finish_test() {
   do_execute_soon(function() {
-    test_generator.close();
+    test_generator.return();
     do_test_finished();
   });
 }
 
-function do_run_test() {
+function* do_run_test() {
   // Set up a profile.
   let profile = do_get_profile();
 
   // Create URIs and channels pointing to foo.com and bar.com.
   // We will use these to put foo.com into first and third party contexts.
   var spec1 = "http://foo.com/foo.html";
   var spec2 = "http://bar.com/bar.html";
   var uri1 = NetUtil.newURI(spec1);
--- a/extensions/cookie/test/unit/test_domain_eviction.js
+++ b/extensions/cookie/test/unit/test_domain_eviction.js
@@ -12,17 +12,17 @@ function run_test()
   do_run_generator(test_generator);
 }
 
 function continue_test()
 {
   do_run_generator(test_generator);
 }
 
-function do_run_test()
+function* do_run_test()
 {
   // Set the base domain limit to 50 so we have a known value.
   Services.prefs.setIntPref("network.cookie.maxPerHost", 50);
 
   let futureExpiry = Math.floor(Date.now() / 1000 + 1000);
 
   // test eviction under the 50 cookies per base domain limit. this means
   // that cookies for foo.com and bar.foo.com should count toward this limit,
--- a/extensions/cookie/test/unit/test_eviction.js
+++ b/extensions/cookie/test/unit/test_eviction.js
@@ -21,17 +21,17 @@ function repeat_test()
   // The test is probably going to fail because setting a batch of cookies took
   // a significant fraction of 'gPurgeAge'. Compensate by rerunning the
   // test with a larger purge age.
   do_check_true(gPurgeAge < 64);
   gPurgeAge *= 2;
   gShortExpiry *= 2;
 
   do_execute_soon(function() {
-    test_generator.close();
+    test_generator.return();
     test_generator = do_run_test();
     do_run_generator(test_generator);
   });
 }
 
 // Purge threshold, in seconds.
 var gPurgeAge = 1;
 
@@ -48,17 +48,17 @@ function get_purge_delay()
 
 // Required delay to ensure a cookie set with an expiry time 'gShortExpiry' into
 // the future will have expired.
 function get_expiry_delay()
 {
   return gShortExpiry * 1000 + 100;
 }
 
-function do_run_test()
+function* do_run_test()
 {
   // Set up a profile.
   let profile = do_get_profile();
 
   // twiddle prefs to convenient values for this test
   Services.prefs.setIntPref("network.cookie.purgeAge", gPurgeAge);
   Services.prefs.setIntPref("network.cookie.maxNumber", 100);
 
--- a/extensions/cookie/test/unit/test_permmanager_expiration.js
+++ b/extensions/cookie/test/unit/test_permmanager_expiration.js
@@ -10,17 +10,17 @@ function run_test() {
   test_generator.next();
 }
 
 function continue_test()
 {
   do_run_generator(test_generator);
 }
 
-function do_run_test() {
+function* do_run_test() {
   // Set up a profile.
   let profile = do_get_profile();
 
   let pm = Services.perms;
   let permURI = NetUtil.newURI("http://example.com");
   let principal = Services.scriptSecurityManager.createCodebasePrincipal(permURI, {});
 
   let now = Number(Date.now());
--- a/extensions/cookie/test/unit/test_permmanager_migrate_4-7.js
+++ b/extensions/cookie/test/unit/test_permmanager_migrate_4-7.js
@@ -12,17 +12,17 @@ function GetPermissionsFile(profile)
   file.append(PERMISSIONS_FILE_NAME);
   return file;
 }
 
 function run_test() {
   run_next_test();
 }
 
-add_task(function test() {
+add_task(function* test() {
   /* Create and set up the permissions database */
   let profile = do_get_profile();
 
   let db = Services.storage.openDatabase(GetPermissionsFile(profile));
   db.schemaVersion = 4;
 
   db.executeSimpleSQL(
     "CREATE TABLE moz_hosts (" +
--- a/extensions/cookie/test/unit/test_permmanager_migrate_5-7a.js
+++ b/extensions/cookie/test/unit/test_permmanager_migrate_5-7a.js
@@ -12,17 +12,17 @@ function GetPermissionsFile(profile)
   file.append(PERMISSIONS_FILE_NAME);
   return file;
 }
 
 function run_test() {
   run_next_test();
 }
 
-add_task(function test() {
+add_task(function* test() {
   /* Create and set up the permissions database */
   let profile = do_get_profile();
 
   let db = Services.storage.openDatabase(GetPermissionsFile(profile));
   db.schemaVersion = 5;
 
   /*
    * V5 table
--- a/extensions/cookie/test/unit/test_permmanager_migrate_6-7a.js
+++ b/extensions/cookie/test/unit/test_permmanager_migrate_6-7a.js
@@ -12,17 +12,17 @@ function GetPermissionsFile(profile)
   file.append(PERMISSIONS_FILE_NAME);
   return file;
 }
 
 function run_test() {
   run_next_test();
 }
 
-add_task(function test() {
+add_task(function* test() {
   /* Create and set up the permissions database */
   let profile = do_get_profile();
 
   let db = Services.storage.openDatabase(GetPermissionsFile(profile));
   db.schemaVersion = 6;
 
   /*
    * V5 table
--- a/extensions/cookie/test/unit/test_permmanager_migrate_7-8.js
+++ b/extensions/cookie/test/unit/test_permmanager_migrate_7-8.js
@@ -12,17 +12,17 @@ function GetPermissionsFile(profile)
   file.append(PERMISSIONS_FILE_NAME);
   return file;
 }
 
 function run_test() {
   run_next_test();
 }
 
-add_task(function test() {
+add_task(function* test() {
   /* Create and set up the permissions database */
   let profile = do_get_profile();
 
   let db = Services.storage.openDatabase(GetPermissionsFile(profile));
   db.schemaVersion = 7;
 
   /*
    * V5 table
--- a/extensions/cookie/test/unit/test_permmanager_notifications.js
+++ b/extensions/cookie/test/unit/test_permmanager_notifications.js
@@ -11,17 +11,17 @@ function run_test() {
   test_generator.next();
 }
 
 function continue_test()
 {
   do_run_generator(test_generator);
 }
 
-function do_run_test() {
+function* do_run_test() {
   // Set up a profile.
   let profile = do_get_profile();
 
   let pm = Services.perms;
   let now = Number(Date.now());
   let permType = "test/expiration-perm";
   let ssm = Cc["@mozilla.org/scriptsecuritymanager;1"]
               .getService(Ci.nsIScriptSecurityManager);
--- a/extensions/cookie/test/unit/test_permmanager_removesince.js
+++ b/extensions/cookie/test/unit/test_permmanager_removesince.js
@@ -10,17 +10,17 @@ function run_test() {
   test_generator.next();
 }
 
 function continue_test()
 {
   do_run_generator(test_generator);
 }
 
-function do_run_test() {
+function* do_run_test() {
   // Set up a profile.
   let profile = do_get_profile();
 
   let pm = Services.perms;
 
   // to help with testing edge-cases, we will arrange for .removeAllSince to
   // remove *all* permissions from one principal and one permission from another.
   let permURI1 = NetUtil.newURI("http://example.com");
--- a/extensions/cookie/test/unit/test_schema_2_migration.js
+++ b/extensions/cookie/test/unit/test_schema_2_migration.js
@@ -8,22 +8,22 @@ var test_generator = do_run_test();
 
 function run_test() {
   do_test_pending();
   test_generator.next();
 }
 
 function finish_test() {
   do_execute_soon(function() {
-    test_generator.close();
+    test_generator.return();
     do_test_finished();
   });
 }
 
-function do_run_test() {
+function* do_run_test() {
   // Set up a profile.
   let profile = do_get_profile();
 
   // Create a schema 2 database.
   let schema2db = new CookieDatabaseConnection(do_get_cookie_file(profile), 2);
 
   let now = Date.now() * 1000;
   let futureExpiry = Math.round(now / 1e6 + 1000);
--- a/extensions/cookie/test/unit/test_schema_3_migration.js
+++ b/extensions/cookie/test/unit/test_schema_3_migration.js
@@ -8,22 +8,22 @@ var test_generator = do_run_test();
 
 function run_test() {
   do_test_pending();
   test_generator.next();
 }
 
 function finish_test() {
   do_execute_soon(function() {
-    test_generator.close();
+    test_generator.return();
     do_test_finished();
   });
 }
 
-function do_run_test() {
+function* do_run_test() {
   // Set up a profile.
   let profile = do_get_profile();
 
   // Create a schema 3 database.
   let schema3db = new CookieDatabaseConnection(do_get_cookie_file(profile), 3);
 
   let now = Date.now() * 1000;
   let futureExpiry = Math.round(now / 1e6 + 1000);
--- a/ipc/testshell/XPCShellEnvironment.cpp
+++ b/ipc/testshell/XPCShellEnvironment.cpp
@@ -446,17 +446,17 @@ XPCShellEnvironment::Init()
     RefPtr<BackstagePass> backstagePass;
     rv = NS_NewBackstagePass(getter_AddRefs(backstagePass));
     if (NS_FAILED(rv)) {
         NS_ERROR("Failed to create backstage pass!");
         return false;
     }
 
     JS::CompartmentOptions options;
-    options.creationOptions().setZone(JS::SystemZone);
+    options.creationOptions().setSystemZone();
     options.behaviors().setVersion(JSVERSION_LATEST);
     if (xpc::SharedMemoryEnabled())
         options.creationOptions().setSharedMemoryAndAtomicsEnabled(true);
 
     nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
     rv = xpc->InitClassesWithNewWrappedGlobal(cx,
                                               static_cast<nsIGlobalObject *>(backstagePass),
                                               principal, 0,
--- a/js/moz.configure
+++ b/js/moz.configure
@@ -140,26 +140,29 @@ js_option('--enable-profiling', env='MOZ
 def profiling(value):
     if value:
         return True
 
 add_old_configure_assignment('MOZ_PROFILING', profiling)
 
 @depends(profiling, target)
 def imply_vtune(value, target):
-    if value and (target.kernel == 'WINNT' or (target.kernel == 'Linux' and
-                                               target.os == 'GNU')):
+    ok_cpu    = target.cpu in ['x86', 'x86_64']
+    ok_kernel = target.kernel == 'WINNT' or \
+                (target.kernel == 'Linux' and target.os == 'GNU')
+
+    if value and ok_cpu and ok_kernel:
         return True
 
 set_config('MOZ_PROFILING', profiling)
 set_define('MOZ_PROFILING', profiling)
 imply_option('--enable-vtune', imply_vtune, reason='--enable-profiling')
 
 
-js_option('--enable-vtune', env='MOZ_VTUNE', help='Enable vtune profiling')
+js_option('--enable-vtune', env='MOZ_VTUNE', help='Enable VTune profiling')
 
 @depends('--enable-vtune')
 def vtune(value):
     if value:
         return True
 
 set_config('MOZ_VTUNE', vtune)
 set_define('MOZ_VTUNE', vtune)
--- a/js/public/GCAPI.h
+++ b/js/public/GCAPI.h
@@ -692,17 +692,17 @@ ExposeScriptToActiveJS(JSScript* script)
 }
 
 /*
  * If a GC is currently marking, mark the string black.
  */
 static MOZ_ALWAYS_INLINE void
 MarkStringAsLive(Zone* zone, JSString* string)
 {
-    JSRuntime* rt = JS::shadow::Zone::asShadowZone(zone)->runtimeFromMainThread();
+    JSRuntime* rt = JS::shadow::Zone::asShadowZone(zone)->runtimeFromActiveCooperatingThread();
     js::gc::MarkGCThingAsLive(rt, GCCellPtr(string));
 }
 
 /*
  * Internal to Firefox.
  *
  * Note: this is not related to the PokeGC in nsJSEnvironment.
  */
--- a/js/public/HeapAPI.h
+++ b/js/public/HeapAPI.h
@@ -119,17 +119,17 @@ struct Zone
     }
 
     JSTracer* barrierTracer() {
         MOZ_ASSERT(needsIncrementalBarrier_);
         MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(runtime_));
         return barrierTracer_;
     }
 
-    JSRuntime* runtimeFromMainThread() const {
+    JSRuntime* runtimeFromActiveCooperatingThread() const {
         MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(runtime_));
         return runtime_;
     }
 
     // Note: Unrestricted access to the zone's runtime from an arbitrary
     // thread can easily lead to races. Use this method very carefully.
     JSRuntime* runtimeFromAnyThread() const {
         return runtime_;
--- a/js/public/Utility.h
+++ b/js/public/Utility.h
@@ -50,21 +50,21 @@ JS_Assert(const char* s, const char* fil
 
 namespace js {
 namespace oom {
 
 /*
  * To make testing OOM in certain helper threads more effective,
  * allow restricting the OOM testing to a certain helper thread
  * type. This allows us to fail e.g. in off-thread script parsing
- * without causing an OOM in the main thread first.
+ * without causing an OOM in the active thread first.
  */
 enum ThreadType {
     THREAD_TYPE_NONE = 0,       // 0
-    THREAD_TYPE_MAIN,           // 1
+    THREAD_TYPE_COOPERATING,    // 1
     THREAD_TYPE_WASM,           // 2
     THREAD_TYPE_ION,            // 3
     THREAD_TYPE_PARSE,          // 4
     THREAD_TYPE_COMPRESS,       // 5
     THREAD_TYPE_GCHELPER,       // 6
     THREAD_TYPE_GCPARALLEL,     // 7
     THREAD_TYPE_PROMISE_TASK,   // 8
     THREAD_TYPE_MAX             // Used to check shell function arguments
--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -1040,17 +1040,17 @@ js::intl_Collator(JSContext* cx, unsigne
     MOZ_ASSERT(!args.isConstructing());
 
     return Collator(cx, args);
 }
 
 void
 CollatorObject::finalize(FreeOp* fop, JSObject* obj)
 {
-    MOZ_ASSERT(fop->onMainThread());
+    MOZ_ASSERT(fop->onActiveCooperatingThread());
 
     const Value& slot = obj->as<CollatorObject>().getReservedSlot(CollatorObject::UCOLLATOR_SLOT);
     if (UCollator* coll = static_cast<UCollator*>(slot.toPrivate()))
         ucol_close(coll);
 }
 
 static JSObject*
 CreateCollatorPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global)
@@ -1473,17 +1473,17 @@ js::intl_NumberFormat(JSContext* cx, uns
     // cannot be used with "new", but it still has to be treated as a
     // constructor.
     return NumberFormat(cx, args, true);
 }
 
 void
 NumberFormatObject::finalize(FreeOp* fop, JSObject* obj)
 {
-    MOZ_ASSERT(fop->onMainThread());
+    MOZ_ASSERT(fop->onActiveCooperatingThread());
 
     const Value& slot =
         obj->as<NumberFormatObject>().getReservedSlot(NumberFormatObject::UNUMBER_FORMAT_SLOT);
     if (UNumberFormat* nf = static_cast<UNumberFormat*>(slot.toPrivate()))
         unum_close(nf);
 }
 
 static JSObject*
@@ -2412,17 +2412,17 @@ js::intl_DateTimeFormat(JSContext* cx, u
     // cannot be used with "new", but it still has to be treated as a
     // constructor.
     return DateTimeFormat(cx, args, true);
 }
 
 void
 DateTimeFormatObject::finalize(FreeOp* fop, JSObject* obj)
 {
-    MOZ_ASSERT(fop->onMainThread());
+    MOZ_ASSERT(fop->onActiveCooperatingThread());
 
     const Value& slot =
         obj->as<DateTimeFormatObject>().getReservedSlot(DateTimeFormatObject::UDATE_FORMAT_SLOT);
     if (UDateFormat* df = static_cast<UDateFormat*>(slot.toPrivate()))
         udat_close(df);
 }
 
 static JSObject*
@@ -3453,17 +3453,17 @@ PluralRules(JSContext* cx, unsigned argc
 
     args.rval().setObject(*pluralRules);
     return true;
 }
 
 void
 PluralRulesObject::finalize(FreeOp* fop, JSObject* obj)
 {
-    MOZ_ASSERT(fop->onMainThread());
+    MOZ_ASSERT(fop->onActiveCooperatingThread());
 
     const Value& slot =
         obj->as<PluralRulesObject>().getReservedSlot(PluralRulesObject::UPLURAL_RULES_SLOT);
     if (UPluralRules* pr = static_cast<UPluralRules*>(slot.toPrivate()))
         uplrules_close(pr);
 }
 
 static JSObject*
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -204,17 +204,17 @@ MapIteratorObject::create(JSContext* cx,
     iterobj->setSlot(RangeSlot, PrivateValue(range));
     iterobj->setSlot(KindSlot, Int32Value(int32_t(kind)));
     return iterobj;
 }
 
 void
 MapIteratorObject::finalize(FreeOp* fop, JSObject* obj)
 {
-    MOZ_ASSERT(fop->onMainThread());
+    MOZ_ASSERT(fop->onActiveCooperatingThread());
     fop->delete_(MapIteratorObjectRange(static_cast<NativeObject*>(obj)));
 }
 
 bool
 MapIteratorObject::next(Handle<MapIteratorObject*> mapIterator, HandleArrayObject resultPairObj,
                         JSContext* cx)
 {
     // Check invariants for inlined _GetNextMapEntryForIterator.
@@ -543,17 +543,17 @@ MapObject::create(JSContext* cx, HandleO
     mapObj->setPrivate(map.release());
     mapObj->setReservedSlot(NurseryKeysSlot, PrivateValue(nullptr));
     return mapObj;
 }
 
 void
 MapObject::finalize(FreeOp* fop, JSObject* obj)
 {
-    MOZ_ASSERT(fop->onMainThread());
+    MOZ_ASSERT(fop->onActiveCooperatingThread());
     if (ValueMap* map = obj->as<MapObject>().getData())
         fop->delete_(map);
 }
 
 bool
 MapObject::construct(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
@@ -941,17 +941,17 @@ SetIteratorObject::create(JSContext* cx,
     iterobj->setSlot(RangeSlot, PrivateValue(range));
     iterobj->setSlot(KindSlot, Int32Value(int32_t(kind)));
     return iterobj;
 }
 
 void
 SetIteratorObject::finalize(FreeOp* fop, JSObject* obj)
 {
-    MOZ_ASSERT(fop->onMainThread());
+    MOZ_ASSERT(fop->onActiveCooperatingThread());
     fop->delete_(SetIteratorObjectRange(static_cast<NativeObject*>(obj)));
 }
 
 bool
 SetIteratorObject::next(Handle<SetIteratorObject*> setIterator, HandleArrayObject resultObj,
                         JSContext* cx)
 {
     // Check invariants for inlined _GetNextSetEntryForIterator.
@@ -1135,17 +1135,17 @@ SetObject::trace(JSTracer* trc, JSObject
         for (ValueSet::Range r = set->all(); !r.empty(); r.popFront())
             TraceKey(r, r.front(), trc);
     }
 }
 
 void
 SetObject::finalize(FreeOp* fop, JSObject* obj)
 {
-    MOZ_ASSERT(fop->onMainThread());
+    MOZ_ASSERT(fop->onActiveCooperatingThread());
     SetObject* setobj = static_cast<SetObject*>(obj);
     if (ValueSet* set = setobj->getData())
         fop->delete_(set);
 }
 
 bool
 SetObject::isBuiltinAdd(HandleValue add, JSContext* cx)
 {
--- a/js/src/builtin/ModuleObject.cpp
+++ b/js/src/builtin/ModuleObject.cpp
@@ -603,17 +603,17 @@ ModuleObject::create(JSContext* cx)
 
     self->initReservedSlot(FunctionDeclarationsSlot, PrivateValue(funDecls));
     return self;
 }
 
 /* static */ void
 ModuleObject::finalize(js::FreeOp* fop, JSObject* obj)
 {
-    MOZ_ASSERT(fop->maybeOffMainThread());
+    MOZ_ASSERT(fop->maybeOnHelperThread());
     ModuleObject* self = &obj->as<ModuleObject>();
     if (self->hasImportBindings())
         fop->delete_(&self->importBindings());
     if (IndirectBindingMap* bindings = self->namespaceBindings())
         fop->delete_(bindings);
     if (FunctionDeclarationVector* funDecls = self->functionDeclarations())
         fop->delete_(funDecls);
 }
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -342,16 +342,17 @@ MinorGC(JSContext* cx, unsigned argc, Va
     cx->minorGC(JS::gcreason::API);
     args.rval().setUndefined();
     return true;
 }
 
 #define FOR_EACH_GC_PARAM(_)                                                    \
     _("maxBytes",                   JSGC_MAX_BYTES,                      true)  \
     _("maxMallocBytes",             JSGC_MAX_MALLOC_BYTES,               true)  \
+    _("maxNurseryBytes",            JSGC_MAX_NURSERY_BYTES,              true)  \
     _("gcBytes",                    JSGC_BYTES,                          false) \
     _("gcNumber",                   JSGC_NUMBER,                         false) \
     _("mode",                       JSGC_MODE,                           true)  \
     _("unusedChunks",               JSGC_UNUSED_CHUNKS,                  false) \
     _("totalChunks",                JSGC_TOTAL_CHUNKS,                   false) \
     _("sliceTimeBudget",            JSGC_SLICE_TIME_BUDGET,              true)  \
     _("markStackLimit",             JSGC_MARK_STACK_LIMIT,               true)  \
     _("highFrequencyTimeLimit",     JSGC_HIGH_FREQUENCY_TIME_LIMIT,      true)  \
@@ -416,19 +417,26 @@ GCParameter(JSContext* cx, unsigned argc
         return true;
     }
 
     if (!info.writable) {
         JS_ReportErrorASCII(cx, "Attempt to change read-only parameter %s", info.name);
         return false;
     }
 
-    if (disableOOMFunctions && (param == JSGC_MAX_BYTES || param == JSGC_MAX_MALLOC_BYTES)) {
-        args.rval().setUndefined();
-        return true;
+    if (disableOOMFunctions) {
+        switch (param) {
+          case JSGC_MAX_BYTES:
+          case JSGC_MAX_MALLOC_BYTES:
+          case JSGC_MAX_NURSERY_BYTES:
+            args.rval().setUndefined();
+            return true;
+          default:
+            break;
+        }
     }
 
     double d;
     if (!ToNumber(cx, args[1], &d))
         return false;
 
     if (d < 0 || d > UINT32_MAX) {
         JS_ReportErrorASCII(cx, "Parameter value out of range");
@@ -1330,17 +1338,17 @@ SetupOOMFailure(JSContext* cx, bool fail
     if (!JS::ToInt32(cx, args.get(0), &count))
         return false;
 
     if (count <= 0) {
         JS_ReportErrorASCII(cx, "OOM cutoff should be positive");
         return false;
     }
 
-    uint32_t targetThread = js::oom::THREAD_TYPE_MAIN;
+    uint32_t targetThread = js::oom::THREAD_TYPE_COOPERATING;
     if (args.length() > 1 && !ToUint32(cx, args[1], &targetThread))
         return false;
 
     if (targetThread == js::oom::THREAD_TYPE_NONE || targetThread >= js::oom::THREAD_TYPE_MAX) {
         JS_ReportErrorASCII(cx, "Invalid thread type specified");
         return false;
     }
 
@@ -1404,23 +1412,23 @@ OOMTest(JSContext* cx, unsigned argc, Va
         args.rval().setUndefined();
         return true;
     }
 
     RootedFunction function(cx, &args[0].toObject().as<JSFunction>());
 
     bool verbose = EnvVarIsDefined("OOM_VERBOSE");
 
-    unsigned threadStart = oom::THREAD_TYPE_MAIN;
+    unsigned threadStart = oom::THREAD_TYPE_COOPERATING;
     unsigned threadEnd = oom::THREAD_TYPE_MAX;
 
     // Test a single thread type if specified by the OOM_THREAD environment variable.
     int threadOption = 0;
     if (EnvVarAsInt("OOM_THREAD", &threadOption)) {
-        if (threadOption < oom::THREAD_TYPE_MAIN || threadOption > oom::THREAD_TYPE_MAX) {
+        if (threadOption < oom::THREAD_TYPE_COOPERATING || threadOption > oom::THREAD_TYPE_MAX) {
             JS_ReportErrorASCII(cx, "OOM_THREAD value out of range.");
             return false;
         }
 
         threadStart = threadOption;
         threadEnd = threadOption + 1;
     }
 
@@ -4581,28 +4589,26 @@ gc::ZealModeHelpText),
 
     JS_FN_HELP("detachArrayBuffer", DetachArrayBuffer, 1, 0,
 "detachArrayBuffer(buffer)",
 "  Detach the given ArrayBuffer object from its memory, i.e. as if it\n"
 "  had been transferred to a WebWorker."),
 
     JS_FN_HELP("helperThreadCount", HelperThreadCount, 0, 0,
 "helperThreadCount()",
-"  Returns the number of helper threads available for off-main-thread tasks."),
+"  Returns the number of helper threads available for off-thread tasks."),
 
 #ifdef JS_TRACE_LOGGING
     JS_FN_HELP("startTraceLogger", EnableTraceLogger, 0, 0,
 "startTraceLogger()",
-"  Start logging the mainThread.\n"
-"  Note: tracelogging starts automatically. Disable it by setting environment variable\n"
-"  TLOPTIONS=disableMainThread"),
+"  Start logging this thread.\n"),
 
     JS_FN_HELP("stopTraceLogger", DisableTraceLogger, 0, 0,
 "stopTraceLogger()",
-"  Stop logging the mainThread."),
+"  Stop logging this thread."),
 #endif
 
     JS_FN_HELP("reportOutOfMemory", ReportOutOfMemory, 0, 0,
 "reportOutOfMemory()",
 "  Report OOM, then clear the exception and return undefined. For crash testing."),
 
     JS_FN_HELP("throwOutOfMemory", ThrowOutOfMemory, 0, 0,
 "throwOutOfMemory()",
--- a/js/src/builtin/WeakMapObject.cpp
+++ b/js/src/builtin/WeakMapObject.cpp
@@ -228,17 +228,17 @@ WeakMap_trace(JSTracer* trc, JSObject* o
 {
     if (ObjectValueMap* map = obj->as<WeakMapObject>().getMap())
         map->trace(trc);
 }
 
 static void
 WeakMap_finalize(FreeOp* fop, JSObject* obj)
 {
-    MOZ_ASSERT(fop->maybeOffMainThread());
+    MOZ_ASSERT(fop->maybeOnHelperThread());
     if (ObjectValueMap* map = obj->as<WeakMapObject>().getMap()) {
 #ifdef DEBUG
         map->~ObjectValueMap();
         memset(static_cast<void*>(map), 0xdc, sizeof(*map));
         fop->free_(map);
 #else
         fop->delete_(map);
 #endif
--- a/js/src/devtools/rootAnalysis/annotations.js
+++ b/js/src/devtools/rootAnalysis/annotations.js
@@ -217,16 +217,20 @@ var ignoreFunctions = {
     // nsTHashtable<T>::s_MatchEntry for its matchEntry function pointer, but
     // there is no mechanism for that. So we will just annotate a particularly
     // troublesome logging-related usage.
     "EntryType* nsTHashtable<EntryType>::PutEntry(nsTHashtable<EntryType>::KeyType, const fallible_t&) [with EntryType = nsBaseHashtableET<nsCharPtrHashKey, nsAutoPtr<mozilla::LogModule> >; nsTHashtable<EntryType>::KeyType = const char*; nsTHashtable<EntryType>::fallible_t = mozilla::fallible_t]" : true,
     "EntryType* nsTHashtable<EntryType>::GetEntry(nsTHashtable<EntryType>::KeyType) const [with EntryType = nsBaseHashtableET<nsCharPtrHashKey, nsAutoPtr<mozilla::LogModule> >; nsTHashtable<EntryType>::KeyType = const char*]" : true,
     "EntryType* nsTHashtable<EntryType>::PutEntry(nsTHashtable<EntryType>::KeyType) [with EntryType = nsBaseHashtableET<nsPtrHashKey<const mozilla::BlockingResourceBase>, nsAutoPtr<mozilla::DeadlockDetector<mozilla::BlockingResourceBase>::OrderingEntry> >; nsTHashtable<EntryType>::KeyType = const mozilla::BlockingResourceBase*]" : true,
     "EntryType* nsTHashtable<EntryType>::GetEntry(nsTHashtable<EntryType>::KeyType) const [with EntryType = nsBaseHashtableET<nsPtrHashKey<const mozilla::BlockingResourceBase>, nsAutoPtr<mozilla::DeadlockDetector<mozilla::BlockingResourceBase>::OrderingEntry> >; nsTHashtable<EntryType>::KeyType = const mozilla::BlockingResourceBase*]" : true,
 
+    // VTune internals that lazy-load a shared library and make IndirectCalls.
+    "iJIT_IsProfilingActive" : true,
+    "iJIT_NotifyEvent": true,
+
     // The big hammers.
     "PR_GetCurrentThread" : true,
     "calloc" : true,
 };
 
 function extraGCFunctions() {
     return ["ffi_call"];
 }
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -513,22 +513,22 @@ frontend::CreateScriptSourceObject(JSCon
         if (!ScriptSourceObject::initFromOptions(cx, sso, options))
             return nullptr;
     }
 
     return sso;
 }
 
 // CompileScript independently returns the ScriptSourceObject (SSO) for the
-// compile.  This is used by off-main-thread script compilation (OMT-SC).
+// compile.  This is used by off-thread script compilation (OT-SC).
 //
-// OMT-SC cannot initialize the SSO when it is first constructed because the
+// OT-SC cannot initialize the SSO when it is first constructed because the
 // SSO is allocated initially in a separate compartment.
 //
-// After OMT-SC, the separate compartment is merged with the main compartment,
+// After OT-SC, the separate compartment is merged with the main compartment,
 // at which point the JSScripts created become observable by the debugger via
 // memory-space scanning.
 //
 // Whatever happens to the top-level script compilation (even if it fails and
 // returns null), we must finish initializing the SSO.  This is because there
 // may be valid inner scripts observable by the debugger which reference the
 // partially-initialized SSO.
 class MOZ_STACK_CLASS AutoInitializeSourceObject
@@ -605,17 +605,17 @@ frontend::CompileModule(JSContext* cx, c
         return nullptr;
 
     LifoAlloc& alloc = cx->tempLifoAlloc();
     RootedModuleObject module(cx, CompileModule(cx, options, srcBuf, alloc));
     if (!module)
         return nullptr;
 
     // This happens in GlobalHelperThreadState::finishModuleParseTask() when a
-    // module is compiled off main thread.
+    // module is compiled off thread.
     if (!ModuleObject::Freeze(cx, module))
         return nullptr;
 
     return module;
 }
 
 bool
 frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const char16_t* chars, size_t length)
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -3484,17 +3484,17 @@ BytecodeEmitter::maybeSetSourceMap()
 
     return true;
 }
 
 void
 BytecodeEmitter::tellDebuggerAboutCompiledScript(JSContext* cx)
 {
     // Note: when parsing off thread the resulting scripts need to be handed to
-    // the debugger after rejoining to the main thread.
+    // the debugger after rejoining to the active thread.
     if (cx->helperThread())
         return;
 
     // Lazy scripts are never top level (despite always being invoked with a
     // nullptr parent), and so the hook should never be fired.
     if (emitterMode != LazyFunction && !parent)
         Debugger::onNewScript(cx, script);
 }
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -3124,17 +3124,17 @@ Parser<ParseHandler>::functionDefinition
     if (handler.canSkipLazyInnerFunctions()) {
         if (!skipLazyInnerFunction(pn, kind, tryAnnexB))
             return null();
         return pn;
     }
 
     RootedObject proto(context);
     if (generatorKind == StarGenerator) {
-        // If we are off the main thread, the generator meta-objects have
+        // If we are off thread, the generator meta-objects have
         // already been created by js::StartOffThreadParseTask, so cx will not
         // be necessary.
         JSContext* cx = context->helperThread() ? nullptr : context;
         proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, context->global());
         if (!proto)
             return null();
     }
     RootedFunction fun(context, newFunction(funName, kind, generatorKind, asyncKind, proto));
@@ -7979,17 +7979,17 @@ typename ParseHandler::Node
 Parser<ParseHandler>::generatorComprehensionLambda(unsigned begin)
 {
     Node genfn = handler.newFunctionExpression();
     if (!genfn)
         return null();
 
     ParseContext* outerpc = pc;
 
-    // If we are off the main thread, the generator meta-objects have
+    // If we are off thread, the generator meta-objects have
     // already been created by js::StartOffThreadParseScript, so cx will not
     // be necessary.
     RootedObject proto(context);
     JSContext* cx = context->helperThread() ? nullptr : context;
     proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, context->global());
     if (!proto)
         return null();
 
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -643,18 +643,19 @@ TokenStream::reportCompileErrorNumberVA(
 {
     bool warning = JSREPORT_IS_WARNING(flags);
 
     if (warning && options().werrorOption) {
         flags &= ~JSREPORT_WARNING;
         warning = false;
     }
 
-    // On the main thread, report the error immediately. When compiling off
-    // thread, save the error so that the main thread can report it later.
+    // On the active thread, report the error immediately. When compiling off
+    // thread, save the error so that the thread finishing the parse can report
+    // it later.
     CompileError tempErr;
     CompileError* tempErrPtr = &tempErr;
     if (cx->helperThread() && !cx->addPendingCompileError(&tempErrPtr))
         return false;
     CompileError& err = *tempErrPtr;
 
     err.flags = flags;
     err.errorNumber = errorNumber;
--- a/js/src/gc/Allocator.cpp
+++ b/js/src/gc/Allocator.cpp
@@ -34,17 +34,17 @@ js::Allocate(JSContext* cx, AllocKind ki
 
     MOZ_ASSERT(thingSize == Arena::thingSize(kind));
     MOZ_ASSERT(thingSize >= sizeof(JSObject_Slots0));
     static_assert(sizeof(JSObject_Slots0) >= CellSize,
                   "All allocations must be at least the allocator-imposed minimum size.");
 
     MOZ_ASSERT_IF(nDynamicSlots != 0, clasp->isNative() || clasp->isProxy());
 
-    // Off-main-thread alloc cannot trigger GC or make runtime assertions.
+    // Off-thread alloc cannot trigger GC or make runtime assertions.
     if (cx->helperThread()) {
         JSObject* obj = GCRuntime::tryNewTenuredObject<NoGC>(cx, kind, thingSize, nDynamicSlots);
         if (MOZ_UNLIKELY(allowGC && !obj))
             ReportOutOfMemory(cx);
         return obj;
     }
 
     JSRuntime* rt = cx->runtime();
@@ -279,54 +279,54 @@ GCRuntime::startBackgroundAllocTaskIfIdl
 }
 
 /* static */ TenuredCell*
 GCRuntime::refillFreeListFromAnyThread(JSContext* cx, AllocKind thingKind, size_t thingSize)
 {
     cx->arenas()->checkEmptyFreeList(thingKind);
 
     if (!cx->helperThread())
-        return refillFreeListFromMainThread(cx, thingKind, thingSize);
+        return refillFreeListFromActiveCooperatingThread(cx, thingKind, thingSize);
 
-    return refillFreeListOffMainThread(cx, thingKind);
+    return refillFreeListFromHelperThread(cx, thingKind);
 }
 
 /* static */ TenuredCell*
-GCRuntime::refillFreeListFromMainThread(JSContext* cx, AllocKind thingKind, size_t thingSize)
+GCRuntime::refillFreeListFromActiveCooperatingThread(JSContext* cx, AllocKind thingKind, size_t thingSize)
 {
-    // It should not be possible to allocate on the main thread while we are
+    // It should not be possible to allocate on the active thread while we are
     // inside a GC.
     Zone *zone = cx->zone();
     MOZ_ASSERT(!JS::CurrentThreadIsHeapBusy(), "allocating while under GC");
 
     AutoMaybeStartBackgroundAllocation maybeStartBGAlloc;
     return cx->arenas()->allocateFromArena(zone, thingKind, CheckThresholds, maybeStartBGAlloc);
 }
 
 /* static */ TenuredCell*
-GCRuntime::refillFreeListOffMainThread(JSContext* cx, AllocKind thingKind)
+GCRuntime::refillFreeListFromHelperThread(JSContext* cx, AllocKind thingKind)
 {
-    // A GC may be happening on the main thread, but zones used by off thread
+    // A GC may be happening on the active thread, but zones used by off thread
     // tasks are never collected.
     Zone* zone = cx->zone();
     MOZ_ASSERT(!zone->wasGCStarted());
 
     AutoMaybeStartBackgroundAllocation maybeStartBGAlloc;
     return cx->arenas()->allocateFromArena(zone, thingKind, CheckThresholds, maybeStartBGAlloc);
 }
 
 /* static */ TenuredCell*
 GCRuntime::refillFreeListInGC(Zone* zone, AllocKind thingKind)
 {
     /*
      * Called by compacting GC to refill a free list while we are in a GC.
      */
 
     zone->arenas.checkEmptyFreeList(thingKind);
-    mozilla::DebugOnly<JSRuntime*> rt = zone->runtimeFromMainThread();
+    mozilla::DebugOnly<JSRuntime*> rt = zone->runtimeFromActiveCooperatingThread();
     MOZ_ASSERT(JS::CurrentThreadIsHeapCollecting());
     MOZ_ASSERT_IF(!JS::CurrentThreadIsHeapMinorCollecting(), !rt->gc.isBackgroundSweeping());
 
     AutoMaybeStartBackgroundAllocation maybeStartBackgroundAllocation;
     return zone->arenas.allocateFromArena(zone, thingKind, DontCheckThresholds,
                                           maybeStartBackgroundAllocation);
 }
 
--- a/js/src/gc/Barrier.cpp
+++ b/js/src/gc/Barrier.cpp
@@ -17,19 +17,19 @@
 #include "vm/EnvironmentObject.h"
 #include "vm/SharedArrayObject.h"
 #include "vm/Symbol.h"
 #include "wasm/WasmJS.h"
 
 namespace js {
 
 bool
-RuntimeFromMainThreadIsHeapMajorCollecting(JS::shadow::Zone* shadowZone)
+RuntimeFromActiveCooperatingThreadIsHeapMajorCollecting(JS::shadow::Zone* shadowZone)
 {
-    MOZ_ASSERT(CurrentThreadCanAccessRuntime(shadowZone->runtimeFromMainThread()));
+    MOZ_ASSERT(CurrentThreadCanAccessRuntime(shadowZone->runtimeFromActiveCooperatingThread()));
     return JS::CurrentThreadIsHeapMajorCollecting();
 }
 
 #ifdef DEBUG
 
 bool
 IsMarkedBlack(NativeObject* obj)
 {
@@ -142,19 +142,19 @@ MovableCellHasher<T>::ensureHash(const L
 template <typename T>
 /* static */ HashNumber
 MovableCellHasher<T>::hash(const Lookup& l)
 {
     if (!l)
         return 0;
 
     // We have to access the zone from-any-thread here: a worker thread may be
-    // cloning a self-hosted object from the main-thread-runtime-owned self-
-    // hosting zone into the off-main-thread runtime. The zone's uid lock will
-    // protect against multiple workers doing this simultaneously.
+    // cloning a self-hosted object from the main runtime's self- hosting zone
+    // into another runtime. The zone's uid lock will protect against multiple
+    // workers doing this simultaneously.
     MOZ_ASSERT(CurrentThreadCanAccessZone(l->zoneFromAnyThread()) ||
                l->zoneFromAnyThread()->isSelfHostingZone());
 
     return l->zoneFromAnyThread()->getHashCodeInfallible(l);
 }
 
 template <typename T>
 /* static */ bool
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -67,18 +67,18 @@ class ChunkPool
         Chunk* get() const { return current_; }
         operator Chunk*() const { return get(); }
         Chunk* operator->() const { return get(); }
       private:
         Chunk* current_;
     };
 };
 
-// Performs extra allocation off the main thread so that when memory is
-// required on the main thread it will already be available and waiting.
+// Performs extra allocation off thread so that when memory is required on the
+// active thread it will already be available and waiting.
 class BackgroundAllocTask : public GCParallelTask
 {
     // Guarded by the GC lock.
     GCLockData<ChunkPool&> chunkPool_;
 
     const bool enabled_;
 
   public:
@@ -862,19 +862,19 @@ class GCRuntime
     void arenaAllocatedDuringGC(JS::Zone* zone, Arena* arena);
 
     // Allocator internals
     MOZ_MUST_USE bool gcIfNeededPerAllocation(JSContext* cx);
     template <typename T>
     static void checkIncrementalZoneState(JSContext* cx, T* t);
     static TenuredCell* refillFreeListFromAnyThread(JSContext* cx, AllocKind thingKind,
                                                     size_t thingSize);
-    static TenuredCell* refillFreeListFromMainThread(JSContext* cx, AllocKind thingKind,
-                                                     size_t thingSize);
-    static TenuredCell* refillFreeListOffMainThread(JSContext* cx, AllocKind thingKind);
+    static TenuredCell* refillFreeListFromActiveCooperatingThread(JSContext* cx, AllocKind thingKind,
+                                                                  size_t thingSize);
+    static TenuredCell* refillFreeListFromHelperThread(JSContext* cx, AllocKind thingKind);
 
     /*
      * Return the list of chunks that can be released outside the GC lock.
      * Must be called either during the GC or with the GC lock taken.
      */
     friend class BackgroundDecommitTask;
     ChunkPool expireEmptyChunkPool(const AutoLockGC& lock);
     void freeEmptyChunks(JSRuntime* rt, const AutoLockGC& lock);
@@ -975,18 +975,19 @@ class GCRuntime
 
     void callFinalizeCallbacks(FreeOp* fop, JSFinalizeStatus status) const;
     void callWeakPointerZoneGroupCallbacks() const;
     void callWeakPointerCompartmentCallbacks(JSCompartment* comp) const;
 
   public:
     JSRuntime* const rt;
 
-    /* Embedders can use this zone however they wish. */
+    /* Embedders can use this zone and group however they wish. */
     UnprotectedData<JS::Zone*> systemZone;
+    UnprotectedData<ZoneGroup*> systemZoneGroup;
 
     // List of all zone groups (protected by the GC lock).
     ActiveThreadOrGCTaskData<ZoneGroupVector> groups;
 
     // The unique atoms zone, which has no zone group.
     WriteOnceData<Zone*> atomsZone;
 
   private:
@@ -1270,17 +1271,17 @@ class GCRuntime
 
     /* Always preserve JIT code during GCs, for testing. */
     ActiveThreadData<bool> alwaysPreserveCode;
 
 #ifdef DEBUG
     ActiveThreadData<bool> arenasEmptyAtShutdown;
 #endif
 
-    /* Synchronize GC heap access among GC helper threads and main threads. */
+    /* Synchronize GC heap access among GC helper threads and active threads. */
     friend class js::AutoLockGC;
     js::Mutex lock;
 
     BackgroundAllocTask allocTask;
     BackgroundDecommitTask decommitTask;
 
     GCHelperState helperState;
 
--- a/js/src/gc/Heap.h
+++ b/js/src/gc/Heap.h
@@ -27,29 +27,23 @@
 #include "gc/Memory.h"
 #include "js/GCAPI.h"
 #include "js/HeapAPI.h"
 #include "js/RootingAPI.h"
 #include "js/TracingAPI.h"
 
 struct JSRuntime;
 
-namespace JS {
-namespace shadow {
-struct Runtime;
-} // namespace shadow
-} // namespace JS
-
 namespace js {
 
 class AutoLockGC;
 class FreeOp;
 
 extern bool
-RuntimeFromMainThreadIsHeapMajorCollecting(JS::shadow::Zone* shadowZone);
+RuntimeFromActiveCooperatingThreadIsHeapMajorCollecting(JS::shadow::Zone* shadowZone);
 
 #ifdef DEBUG
 
 // Barriers can't be triggered during backend Ion compilation, which may run on
 // a helper thread.
 extern bool
 CurrentThreadIsIonCompiling();
 #endif
@@ -251,23 +245,21 @@ class TenuredCell;
 // A GC cell is the base class for all GC things.
 struct Cell
 {
   public:
     MOZ_ALWAYS_INLINE bool isTenured() const { return !IsInsideNursery(this); }
     MOZ_ALWAYS_INLINE const TenuredCell& asTenured() const;
     MOZ_ALWAYS_INLINE TenuredCell& asTenured();
 
-    inline JSRuntime* runtimeFromMainThread() const;
-    inline JS::shadow::Runtime* shadowRuntimeFromMainThread() const;
+    inline JSRuntime* runtimeFromActiveCooperatingThread() const;
 
     // Note: Unrestricted access to the runtime of a GC thing from an arbitrary
     // thread can easily lead to races. Use this method very carefully.
     inline JSRuntime* runtimeFromAnyThread() const;
-    inline JS::shadow::Runtime* shadowRuntimeFromAnyThread() const;
 
     // May be overridden by GC thing kinds that have a compartment pointer.
     inline JSCompartment* maybeCompartment() const { return nullptr; }
 
     inline StoreBuffer* storeBuffer() const;
 
     inline JS::TraceKind getTraceKind() const;
 
@@ -1065,17 +1057,17 @@ class HeapUsage
      * the top-level usage container.
      */
     HeapUsage* const parent_;
 
     /*
      * The approximate number of bytes in use on the GC heap, to the nearest
      * ArenaSize. This does not include any malloc data. It also does not
      * include not-actively-used addresses that are still reserved at the OS
-     * level for GC usage. It is atomic because it is updated by both the main
+     * level for GC usage. It is atomic because it is updated by both the active
      * and GC helper threads.
      */
     mozilla::Atomic<size_t, mozilla::ReleaseAcquire> gcBytes_;
 
   public:
     explicit HeapUsage(HeapUsage* parent)
       : parent_(parent),
         gcBytes_(0)
@@ -1136,41 +1128,29 @@ Cell::asTenured() const
 MOZ_ALWAYS_INLINE TenuredCell&
 Cell::asTenured()
 {
     MOZ_ASSERT(isTenured());
     return *static_cast<TenuredCell*>(this);
 }
 
 inline JSRuntime*
-Cell::runtimeFromMainThread() const
+Cell::runtimeFromActiveCooperatingThread() const
 {
     JSRuntime* rt = chunk()->trailer.runtime;
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
     return rt;
 }
 
-inline JS::shadow::Runtime*
-Cell::shadowRuntimeFromMainThread() const
-{
-    return reinterpret_cast<JS::shadow::Runtime*>(runtimeFromMainThread());
-}
-
 inline JSRuntime*
 Cell::runtimeFromAnyThread() const
 {
     return chunk()->trailer.runtime;
 }
 
-inline JS::shadow::Runtime*
-Cell::shadowRuntimeFromAnyThread() const
-{
-    return reinterpret_cast<JS::shadow::Runtime*>(runtimeFromAnyThread());
-}
-
 inline uintptr_t
 Cell::address() const
 {
     uintptr_t addr = uintptr_t(this);
     MOZ_ASSERT(addr % CellSize == 0);
     MOZ_ASSERT(Chunk::withinValidRange(addr));
     return addr;
 }
@@ -1300,32 +1280,32 @@ TenuredCell::readBarrier(TenuredCell* th
 {
     MOZ_ASSERT(!CurrentThreadIsIonCompiling());
     MOZ_ASSERT(thing);
 
     // It would be good if barriers were never triggered during collection, but
     // at the moment this can happen e.g. when rekeying tables containing
     // read-barriered GC things after a moving GC.
     //
-    // TODO: Fix this and assert we're not collecting if we're on the main
+    // TODO: Fix this and assert we're not collecting if we're on the active
     // thread.
 
     JS::shadow::Zone* shadowZone = thing->shadowZoneFromAnyThread();
     if (shadowZone->needsIncrementalBarrier()) {
-        // Barriers are only enabled on the main thread and are disabled while collecting.
-        MOZ_ASSERT(!RuntimeFromMainThreadIsHeapMajorCollecting(shadowZone));
+        // Barriers are only enabled on the active thread and are disabled while collecting.
+        MOZ_ASSERT(!RuntimeFromActiveCooperatingThreadIsHeapMajorCollecting(shadowZone));
         Cell* tmp = thing;
         TraceManuallyBarrieredGenericPointerEdge(shadowZone->barrierTracer(), &tmp, "read barrier");
         MOZ_ASSERT(tmp == thing);
     }
 
     if (thing->isMarked(GRAY)) {
-        // There shouldn't be anything marked grey unless we're on the main thread.
+        // There shouldn't be anything marked grey unless we're on the active thread.
         MOZ_ASSERT(CurrentThreadCanAccessRuntime(thing->runtimeFromAnyThread()));
-        if (!RuntimeFromMainThreadIsHeapMajorCollecting(shadowZone))
+        if (!RuntimeFromActiveCooperatingThreadIsHeapMajorCollecting(shadowZone))
             UnmarkGrayCellRecursively(thing, thing->getTraceKind());
     }
 }
 
 void
 AssertSafeToSkipBarrier(TenuredCell* thing);
 
 /* static */ MOZ_ALWAYS_INLINE void
@@ -1335,30 +1315,30 @@ TenuredCell::writeBarrierPre(TenuredCell
     if (!thing)
         return;
 
 #ifdef JS_GC_ZEAL
     // When verifying pre barriers we need to switch on all barriers, even
     // those on the Atoms Zone. Normally, we never enter a parse task when
     // collecting in the atoms zone, so will filter out atoms below.
     // Unfortuantely, If we try that when verifying pre-barriers, we'd never be
-    // able to handle OMT parse tasks at all as we switch on the verifier any
-    // time we're not doing GC. This would cause us to deadlock, as OMT parsing
+    // able to handle off thread parse tasks at all as we switch on the verifier any
+    // time we're not doing GC. This would cause us to deadlock, as off thread parsing
     // is meant to resume after GC work completes. Instead we filter out any
-    // OMT barriers that reach us and assert that they would normally not be
+    // off thread barriers that reach us and assert that they would normally not be
     // possible.
     if (!CurrentThreadCanAccessRuntime(thing->runtimeFromAnyThread())) {
         AssertSafeToSkipBarrier(thing);
         return;
     }
 #endif
 
     JS::shadow::Zone* shadowZone = thing->shadowZoneFromAnyThread();
     if (shadowZone->needsIncrementalBarrier()) {
-        MOZ_ASSERT(!RuntimeFromMainThreadIsHeapMajorCollecting(shadowZone));
+        MOZ_ASSERT(!RuntimeFromActiveCooperatingThreadIsHeapMajorCollecting(shadowZone));
         Cell* tmp = thing;
         TraceManuallyBarrieredGenericPointerEdge(shadowZone->barrierTracer(), &tmp, "pre barrier");
         MOZ_ASSERT(tmp == thing);
     }
 }
 
 static MOZ_ALWAYS_INLINE void
 AssertValidToSkipBarrier(TenuredCell* thing)
--- a/js/src/gc/Iteration.cpp
+++ b/js/src/gc/Iteration.cpp
@@ -119,17 +119,17 @@ js::IterateGrayObjects(Zone* zone, GCThi
     MOZ_ASSERT(!JS::CurrentThreadIsHeapBusy());
     AutoPrepareForTracing prep(TlsContext.get(), SkipAtoms);
     ::IterateGrayObjects(zone, cellCallback, data);
 }
 
 void
 js::IterateGrayObjectsUnderCC(Zone* zone, GCThingCallback cellCallback, void* data)
 {
-    mozilla::DebugOnly<JSRuntime*> rt = zone->runtimeFromMainThread();
+    mozilla::DebugOnly<JSRuntime*> rt = zone->runtimeFromActiveCooperatingThread();
     MOZ_ASSERT(JS::CurrentThreadIsHeapCycleCollecting());
     MOZ_ASSERT(!rt->gc.isIncrementalGCInProgress());
     ::IterateGrayObjects(zone, cellCallback, data);
 }
 
 JS_PUBLIC_API(void)
 JS_IterateCompartments(JSContext* cx, void* data,
                        JSIterateCompartmentCallback compartmentCallback)
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -248,17 +248,17 @@ js::CheckTracedThing(JSTracer* trc, T* t
      * Try to assert that the thing is allocated.
      *
      * We would like to assert that the thing is not in the free list, but this
      * check is very slow. Instead we check whether the thing has been poisoned:
      * if it has not then we assume it is allocated, but if it has then it is
      * either free or uninitialized in which case we check the free list.
      *
      * Further complications are that background sweeping may be running and
-     * concurrently modifiying the free list and that tracing is done off main
+     * concurrently modifiying the free list and that tracing is done off
      * thread during compacting GC and reading the contents of the thing by
      * IsThingPoisoned would be racy in this case.
      */
     MOZ_ASSERT_IF(JS::CurrentThreadIsHeapBusy() &&
                   !zone->isGCCompacting() &&
                   !rt->gc.isBackgroundSweeping(),
                   !IsThingPoisoned(thing) || !InFreeList(thing->asTenured().arena(), thing));
 #endif
@@ -2995,17 +2995,17 @@ UnmarkGrayTracer::onChild(const JS::GCCe
 }
 
 template <typename T>
 static bool
 TypedUnmarkGrayCellRecursively(T* t)
 {
     MOZ_ASSERT(t);
 
-    JSRuntime* rt = t->runtimeFromMainThread();
+    JSRuntime* rt = t->runtimeFromActiveCooperatingThread();
     MOZ_ASSERT(!JS::CurrentThreadIsHeapCollecting());
     MOZ_ASSERT(!JS::CurrentThreadIsHeapCycleCollecting());
 
     bool unmarkedArg = false;
     if (t->isTenured()) {
         if (!t->asTenured().isMarked(GRAY))
             return false;
 
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -548,17 +548,16 @@ js::Nursery::maybeEndProfile(ProfileKey 
     if (enableProfiling_)
         endProfile(key);
 }
 
 void
 js::Nursery::collect(JS::gcreason::Reason reason)
 {
     MOZ_ASSERT(!TlsContext.get()->suppressGC);
-    MOZ_RELEASE_ASSERT(zoneGroup()->ownedByCurrentThread());
 
     if (!isEnabled() || isEmpty()) {
         // Our barriers are not always exact, and there may be entries in the
         // storebuffer even when the nursery is disabled or empty. It's not safe
         // to keep these entries as they may refer to tenured cells which may be
         // freed after this point.
         zoneGroup()->storeBuffer().clear();
     }
@@ -797,17 +796,17 @@ js::Nursery::freeMallocedBuffers()
     {
         AutoLockHelperThreadState lock;
         freeMallocedBuffersTask->joinWithLockHeld(lock);
         freeMallocedBuffersTask->transferBuffersToFree(mallocedBuffers, lock);
         started = freeMallocedBuffersTask->startWithLockHeld(lock);
     }
 
     if (!started)
-        freeMallocedBuffersTask->runFromMainThread(zoneGroup()->runtime);
+        freeMallocedBuffersTask->runFromActiveCooperatingThread(zoneGroup()->runtime);
 
     MOZ_ASSERT(mallocedBuffers.empty());
 }
 
 void
 js::Nursery::waitBackgroundFreeEnd()
 {
     // We may finishRoots before nursery init if runtime init fails.
--- a/js/src/gc/Statistics.h
+++ b/js/src/gc/Statistics.h
@@ -472,17 +472,17 @@ struct MOZ_RAII AutoPhase
       : stats(stats), task(&task), phase(phase), enabled(true)
     {
         if (enabled)
             stats.beginPhase(phase);
     }
 
     ~AutoPhase() {
         if (enabled) {
-            // Bug 1309651 - we only record mainthread time (including time
+            // Bug 1309651 - we only record active thread time (including time
             // spent waiting to join with helper threads), but should start
             // recording total work on helper threads sometime by calling
             // endParallelPhase here if task is nonnull.
             stats.endPhase(phase);
         }
     }
 
     Statistics& stats;
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -100,17 +100,17 @@ bool Zone::init(bool isSystemArg)
 void
 Zone::setNeedsIncrementalBarrier(bool needs, ShouldUpdateJit updateJit)
 {
     if (updateJit == UpdateJit && needs != jitUsingBarriers_) {
         jit::ToggleBarriers(this, needs);
         jitUsingBarriers_ = needs;
     }
 
-    MOZ_ASSERT_IF(needs && isAtomsZone(), !runtimeFromMainThread()->exclusiveThreadsPresent());
+    MOZ_ASSERT_IF(needs && isAtomsZone(), !runtimeFromActiveCooperatingThread()->exclusiveThreadsPresent());
     MOZ_ASSERT_IF(needs, canCollect());
     needsIncrementalBarrier_ = needs;
 }
 
 void
 Zone::resetGCMallocBytes()
 {
     gcMallocBytes = ptrdiff_t(gcMaxMallocBytes);
@@ -269,18 +269,18 @@ Zone::discardJitCode(FreeOp* fop, bool d
          */
         if (discardBaselineCode) {
             jitZone()->optimizedStubSpace()->freeAllAfterMinorGC(this);
             jitZone()->purgeIonCacheIRStubInfo();
         }
 
         /*
          * Free all control flow graphs that are cached on BaselineScripts.
-         * Assuming this happens on the mainthread and all control flow
-         * graph reads happen on the mainthread, this is save.
+         * Assuming this happens on the active thread and all control flow
+         * graph reads happen on the active thread, this is safe.
          */
         jitZone()->cfgSpace()->lifoAlloc().freeAll();
     }
 }
 
 #ifdef JSGC_HASH_TABLE_CHECKS
 void
 JS::Zone::checkUniqueIdTableAfterMovingGC()
@@ -290,17 +290,17 @@ JS::Zone::checkUniqueIdTableAfterMovingG
 }
 #endif
 
 uint64_t
 Zone::gcNumber()
 {
     // Zones in use by exclusive threads are not collected, and threads using
     // them cannot access the main runtime's gcNumber without racing.
-    return usedByExclusiveThread ? 0 : runtimeFromMainThread()->gc.gcNumber();
+    return usedByExclusiveThread ? 0 : runtimeFromActiveCooperatingThread()->gc.gcNumber();
 }
 
 js::jit::JitZone*
 Zone::createJitZone(JSContext* cx)
 {
     MOZ_ASSERT(!jitZone_);
 
     if (!cx->runtime()->getJitRuntime(cx))
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -185,17 +185,17 @@ struct Zone : public JS::shadow::Zone,
 
     bool isTooMuchMalloc() const { return gcMallocBytes <= 0; }
     void onTooMuchMalloc();
 
     MOZ_MUST_USE void* onOutOfMemory(js::AllocFunction allocFunc, size_t nbytes,
                                                void* reallocPtr = nullptr) {
         if (!js::CurrentThreadCanAccessRuntime(runtime_))
             return nullptr;
-        return runtimeFromMainThread()->onOutOfMemory(allocFunc, nbytes, reallocPtr);
+        return runtimeFromActiveCooperatingThread()->onOutOfMemory(allocFunc, nbytes, reallocPtr);
     }
     void reportAllocationOverflow() { js::ReportAllocationOverflow(nullptr); }
 
     void beginSweepTypes(js::FreeOp* fop, bool releaseTypes);
 
     bool hasMarkedCompartments();
 
     void scheduleGC() { MOZ_ASSERT(!CurrentThreadIsHeapBusy()); gcScheduled_ = true; }
@@ -221,17 +221,17 @@ struct Zone : public JS::shadow::Zone,
         MOZ_ASSERT(CurrentThreadIsHeapBusy());
         MOZ_ASSERT_IF(state != NoGC, canCollect());
         gcState_ = state;
         if (state == Finished)
             notifyObservingDebuggers();
     }
 
     bool isCollecting() const {
-        MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtimeFromMainThread()));
+        MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtimeFromActiveCooperatingThread()));
         return isCollectingFromAnyThread();
     }
 
     bool isCollectingFromAnyThread() const {
         if (CurrentThreadIsHeapCollecting())
             return gcState_ != NoGC;
         else
             return needsIncrementalBarrier();
@@ -262,17 +262,17 @@ struct Zone : public JS::shadow::Zone,
 
     // Get a number that is incremented whenever this zone is collected, and
     // possibly at other times too.
     uint64_t gcNumber();
 
     bool compileBarriers() const { return compileBarriers(needsIncrementalBarrier()); }
     bool compileBarriers(bool needsIncrementalBarrier) const {
         return needsIncrementalBarrier ||
-               runtimeFromMainThread()->hasZealMode(js::gc::ZealMode::VerifierPre);
+               runtimeFromActiveCooperatingThread()->hasZealMode(js::gc::ZealMode::VerifierPre);
     }
 
     enum ShouldUpdateJit { DontUpdateJit, UpdateJit };
     void setNeedsIncrementalBarrier(bool needs, ShouldUpdateJit updateJit);
     const bool* addressOfNeedsIncrementalBarrier() const { return &needsIncrementalBarrier_; }
 
     js::jit::JitZone* getJitZone(JSContext* cx) { return jitZone_ ? jitZone_ : createJitZone(cx); }
     js::jit::JitZone* jitZone() { return jitZone_; }
@@ -534,17 +534,17 @@ struct Zone : public JS::shadow::Zone,
         return uniqueIds().has(cell);
     }
 
     // Transfer an id from another cell. This must only be called on behalf of a
     // moving GC. This method is infallible.
     void transferUniqueId(js::gc::Cell* tgt, js::gc::Cell* src) {
         MOZ_ASSERT(src != tgt);
         MOZ_ASSERT(!IsInsideNursery(tgt));
-        MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtimeFromMainThread()));
+        MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtimeFromActiveCooperatingThread()));
         MOZ_ASSERT(js::CurrentThreadCanAccessZone(this));
         uniqueIds().rekeyIfMoved(src, tgt);
     }
 
     // Remove any unique id associated with this Cell.
     void removeUniqueId(js::gc::Cell* cell) {
         MOZ_ASSERT(js::CurrentThreadCanAccessZone(this));
         uniqueIds().remove(cell);
@@ -552,25 +552,21 @@ struct Zone : public JS::shadow::Zone,
 
     // When finished parsing off-thread, transfer any UIDs we created in the
     // off-thread zone into the target zone.
     void adoptUniqueIds(JS::Zone* source) {
         js::AutoEnterOOMUnsafeRegion oomUnsafe;
         for (js::gc::UniqueIdMap::Enum e(source->uniqueIds()); !e.empty(); e.popFront()) {
             MOZ_ASSERT(!uniqueIds().has(e.front().key()));
             if (!uniqueIds().put(e.front().key(), e.front().value()))
-                oomUnsafe.crash("failed to transfer unique ids from off-main-thread");
+                oomUnsafe.crash("failed to transfer unique ids from off-thread");
         }
         source->uniqueIds().clear();
     }
 
-    JSContext* contextFromMainThread() {
-        return runtime_->contextFromMainThread();
-    }
-
 #ifdef JSGC_HASH_TABLE_CHECKS
     // Assert that the UniqueId table has been redirected successfully.
     void checkUniqueIdTableAfterMovingGC();
 #endif
 
     bool keepShapeTables() const {
         return keepShapeTables_;
     }
--- a/js/src/gc/ZoneGroup.cpp
+++ b/js/src/gc/ZoneGroup.cpp
@@ -43,28 +43,33 @@ ZoneGroup::init(size_t maxNurseryBytes)
         return false;
 
     return true;
 }
 
 ZoneGroup::~ZoneGroup()
 {
     js_delete(jitZoneGroup.ref());
+
+    if (this == runtime->gc.systemZoneGroup)
+        runtime->gc.systemZoneGroup = nullptr;
 }
 
 void
 ZoneGroup::enter()
 {
     JSContext* cx = TlsContext.get();
     if (ownerContext().context() == cx) {
         MOZ_ASSERT(enterCount);
     } else {
         MOZ_ASSERT(ownerContext().context() == nullptr);
         MOZ_ASSERT(enterCount == 0);
         ownerContext_ = CooperatingContext(cx);
+        if (cx->generationalDisabled)
+            nursery().disable();
     }
     enterCount++;
 }
 
 void
 ZoneGroup::leave()
 {
     MOZ_ASSERT(ownedByCurrentThread());
--- a/js/src/gc/ZoneGroup.h
+++ b/js/src/gc/ZoneGroup.h
@@ -125,47 +125,11 @@ class ZoneGroup
 
   private:
     /* Linked list of all Debugger objects in the group. */
     ZoneGroupData<mozilla::LinkedList<js::Debugger>> debuggerList_;
   public:
     mozilla::LinkedList<js::Debugger>& debuggerList() { return debuggerList_.ref(); }
 };
 
-class MOZ_RAII AutoAccessZoneGroup
-{
-    ZoneGroup* group;
-
-  public:
-    explicit AutoAccessZoneGroup(ZoneGroup* group)
-      : group(group)
-    {
-        group->enter();
-    }
-
-    ~AutoAccessZoneGroup() {
-        group->leave();
-    }
-};
-
-class MOZ_RAII AutoAccessZoneGroups
-{
-    Vector<ZoneGroup*, 4, SystemAllocPolicy> acquiredGroups;
-
-  public:
-    AutoAccessZoneGroups() {}
-
-    ~AutoAccessZoneGroups() {
-        for (size_t i = 0; i < acquiredGroups.length(); i++)
-            acquiredGroups[i]->leave();
-    }
-
-    void access(ZoneGroup* group) {
-        group->enter();
-        AutoEnterOOMUnsafeRegion oomUnsafe;
-        if (!acquiredGroups.append(group))
-            oomUnsafe.crash("acquiredGroups.append failed");
-    }
-};
-
 } // namespace js
 
 #endif // gc_Zone_h
--- a/js/src/irregexp/NativeRegExpMacroAssembler.cpp
+++ b/js/src/irregexp/NativeRegExpMacroAssembler.cpp
@@ -31,16 +31,19 @@
 #include "irregexp/NativeRegExpMacroAssembler.h"
 
 #include "irregexp/RegExpStack.h"
 #include "jit/Linker.h"
 #ifdef JS_ION_PERF
 # include "jit/PerfSpewer.h"
 #endif
 #include "vm/MatchPairs.h"
+#ifdef MOZ_VTUNE
+# include "vtune/VTuneWrapper.h"
+#endif
 
 #include "jit/MacroAssembler-inl.h"
 
 using namespace js;
 using namespace js::irregexp;
 using namespace js::jit;
 
 /*
@@ -494,16 +497,20 @@ NativeRegExpMacroAssembler::GenerateCode
         ReportOutOfMemory(cx);
         return RegExpCode();
     }
 
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "RegExp");
 #endif
 
+#ifdef MOZ_VTUNE
+    vtune::MarkRegExp(code, match_only);
+#endif
+
     for (size_t i = 0; i < labelPatches.length(); i++) {
         LabelPatch& v = labelPatches[i];
         MOZ_ASSERT(!v.label);
         Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, v.patchOffset),
                                            ImmPtr(code->raw() + v.labelOffset),
                                            ImmPtr(0));
     }
 
--- a/js/src/irregexp/RegExpEngine.cpp
+++ b/js/src/irregexp/RegExpEngine.cpp
@@ -2249,17 +2249,20 @@ BoyerMoorePositionInfo::Set(int characte
 {
     SetInterval(Interval(character, character));
 }
 
 void
 BoyerMoorePositionInfo::SetInterval(const Interval& interval)
 {
     s_ = AddRange(s_, kSpaceRanges, kSpaceRangeCount, interval);
-    w_ = AddRange(w_, kWordRanges, kWordRangeCount, interval);
+    if (unicode_ignore_case_)
+        w_ = AddRange(w_, kIgnoreCaseWordRanges, kIgnoreCaseWordRangeCount, interval);
+    else
+        w_ = AddRange(w_, kWordRanges, kWordRangeCount, interval);
     d_ = AddRange(d_, kDigitRanges, kDigitRangeCount, interval);
     surrogate_ =
         AddRange(surrogate_, kSurrogateRanges, kSurrogateRangeCount, interval);
     if (interval.to() - interval.from() >= kMapSize - 1) {
         if (map_count_ != kMapSize) {
             map_count_ = kMapSize;
             for (int i = 0; i < kMapSize; i++)
                 map_[i] = true;
@@ -2286,21 +2289,22 @@ BoyerMoorePositionInfo::SetAll()
         for (int i = 0; i < kMapSize; i++)
             map_[i] = true;
     }
 }
 
 BoyerMooreLookahead::BoyerMooreLookahead(LifoAlloc* alloc, size_t length, RegExpCompiler* compiler)
   : length_(length), compiler_(compiler), bitmaps_(*alloc)
 {
+    bool unicode_ignore_case = compiler->unicode() && compiler->ignore_case();
     max_char_ = MaximumCharacter(compiler->ascii());
 
     bitmaps_.reserve(length);
     for (size_t i = 0; i < length; i++)
-        bitmaps_.append(alloc->newInfallible<BoyerMoorePositionInfo>(alloc));
+        bitmaps_.append(alloc->newInfallible<BoyerMoorePositionInfo>(alloc, unicode_ignore_case));
 }
 
 // Find the longest range of lookahead that has the fewest number of different
 // characters that can occur at a given position.  Since we are optimizing two
 // different parameters at once this is a tradeoff.
 bool BoyerMooreLookahead::FindWorthwhileInterval(int* from, int* to) {
   int biggest_points = 0;
   // If more than 32 characters out of 128 can occur it is unlikely that we can
@@ -2956,25 +2960,32 @@ EmitNotInSurrogatePair(RegExpCompiler* c
 
     assembler->Bind(&ok);
     on_success->Emit(compiler, &new_trace);
 }
 
 // Check for [0-9A-Z_a-z].
 static void
 EmitWordCheck(RegExpMacroAssembler* assembler,
-              jit::Label* word, jit::Label* non_word, bool fall_through_on_word)
+              jit::Label* word, jit::Label* non_word, bool fall_through_on_word,
+              bool unicode_ignore_case)
 {
-    if (assembler->CheckSpecialCharacterClass(fall_through_on_word ? 'w' : 'W',
+    if (!unicode_ignore_case &&
+        assembler->CheckSpecialCharacterClass(fall_through_on_word ? 'w' : 'W',
                                               fall_through_on_word ? non_word : word))
     {
         // Optimized implementation available.
         return;
     }
 
+    if (unicode_ignore_case) {
+        assembler->CheckCharacter(0x017F, word);
+        assembler->CheckCharacter(0x212A, word);
+    }
+
     assembler->CheckCharacterGT('z', non_word);
     assembler->CheckCharacterLT('0', non_word);
     assembler->CheckCharacterGT('a' - 1, word);
     assembler->CheckCharacterLT('9' + 1, word);
     assembler->CheckCharacterLT('A', non_word);
     assembler->CheckCharacterLT('Z' + 1, word);
 
     if (fall_through_on_word)
@@ -3013,17 +3024,18 @@ AssertionNode::EmitBoundaryCheck(RegExpC
     bool at_boundary = (assertion_type_ == AssertionNode::AT_BOUNDARY);
     if (next_is_word_character == Trace::UNKNOWN) {
         jit::Label before_non_word;
         jit::Label before_word;
         if (trace->characters_preloaded() != 1) {
             assembler->LoadCurrentCharacter(trace->cp_offset(), &before_non_word);
         }
         // Fall through on non-word.
-        EmitWordCheck(assembler, &before_word, &before_non_word, false);
+        EmitWordCheck(assembler, &before_word, &before_non_word, false,
+                      compiler->unicode() && compiler->ignore_case());
         // Next character is not a word character.
         assembler->Bind(&before_non_word);
         jit::Label ok;
         BacktrackIfPrevious(compiler, trace, at_boundary ? kIsNonWord : kIsWord);
         assembler->JumpOrBacktrack(&ok);
 
         assembler->Bind(&before_word);
         BacktrackIfPrevious(compiler, trace, at_boundary ? kIsWord : kIsNonWord);
@@ -3053,17 +3065,18 @@ AssertionNode::BacktrackIfPrevious(RegEx
     if (new_trace.cp_offset() == 0) {
         // The start of input counts as a non-word character, so the question is
         // decided if we are at the start.
         assembler->CheckAtStart(non_word);
     }
     // We already checked that we are not at the start of input so it must be
     // OK to load the previous character.
     assembler->LoadCurrentCharacter(new_trace.cp_offset() - 1, &dummy, false);
-    EmitWordCheck(assembler, word, non_word, backtrack_if_previous == kIsNonWord);
+    EmitWordCheck(assembler, word, non_word, backtrack_if_previous == kIsNonWord,
+                  compiler->unicode() && compiler->ignore_case());
 
     assembler->Bind(&fall_through);
     on_success()->Emit(compiler, &new_trace);
 }
 
 void
 AssertionNode::GetQuickCheckDetails(QuickCheckDetails* details,
                                     RegExpCompiler* compiler,
--- a/js/src/irregexp/RegExpEngine.h
+++ b/js/src/irregexp/RegExpEngine.h
@@ -1190,23 +1190,24 @@ ContainedInLattice
 AddRange(ContainedInLattice a,
          const int* ranges,
          int ranges_size,
          Interval new_range);
 
 class BoyerMoorePositionInfo
 {
   public:
-    explicit BoyerMoorePositionInfo(LifoAlloc* alloc)
+    explicit BoyerMoorePositionInfo(LifoAlloc* alloc, bool unicode_ignore_case)
       : map_(*alloc),
         map_count_(0),
         w_(kNotYet),
         s_(kNotYet),
         d_(kNotYet),
-        surrogate_(kNotYet)
+        surrogate_(kNotYet),
+        unicode_ignore_case_(unicode_ignore_case)
     {
         map_.reserve(kMapSize);
         for (int i = 0; i < kMapSize; i++)
             map_.append(false);
     }
 
     bool& at(int i) { return map_[i]; }
 
@@ -1223,16 +1224,19 @@ class BoyerMoorePositionInfo
 
   private:
     InfallibleVector<bool, 0> map_;
     int map_count_;  // Number of set bits in the map.
     ContainedInLattice w_;  // The \w character class.
     ContainedInLattice s_;  // The \s character class.
     ContainedInLattice d_;  // The \d character class.
     ContainedInLattice surrogate_;  // Surrogate UTF-16 code units.
+
+    // True if the RegExp has unicode and ignoreCase flags.
+    bool unicode_ignore_case_;
 };
 
 typedef InfallibleVector<BoyerMoorePositionInfo*, 1> BoyerMoorePositionInfoVector;
 
 class BoyerMooreLookahead
 {
   public:
     BoyerMooreLookahead(LifoAlloc* alloc, size_t length, RegExpCompiler* compiler);
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -30,16 +30,19 @@
 #include "vm/TraceLogging.h"
 
 #include "jsscriptinlines.h"
 
 #include "jit/BaselineFrameInfo-inl.h"
 #include "jit/MacroAssembler-inl.h"
 #include "vm/Interpreter-inl.h"
 #include "vm/NativeObject-inl.h"
+#ifdef MOZ_VTUNE
+# include "vtune/VTuneWrapper.h"
+#endif
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::AssertedCast;
 
 BaselineCompiler::BaselineCompiler(JSContext* cx, TempAllocator& alloc, JSScript* script)
   : BaselineCompilerSpecific(cx, alloc, script),
@@ -220,20 +223,16 @@ BaselineCompiler::compile()
 
     baselineScript->setMethod(code);
     baselineScript->setTemplateEnvironment(templateEnv);
 
     JitSpew(JitSpew_BaselineScripts, "Created BaselineScript %p (raw %p) for %s:%" PRIuSIZE,
             (void*) baselineScript.get(), (void*) code->raw(),
             script->filename(), script->lineno());
 
-#ifdef JS_ION_PERF
-    writePerfSpewerBaselineProfile(script, code);
-#endif
-
     MOZ_ASSERT(pcMappingIndexEntries.length() > 0);
     baselineScript->copyPCMappingIndexEntries(&pcMappingIndexEntries[0]);
 
     MOZ_ASSERT(pcEntries.length() > 0);
     baselineScript->copyPCMappingEntries(pcEntries);
 
     // Copy IC entries
     if (icEntries_.length())
@@ -302,16 +301,24 @@ BaselineCompiler::compile()
         }
 
         // Mark the jitcode as having a bytecode map.
         code->setHasBytecodeMap();
     }
 
     script->setBaselineScript(cx->runtime(), baselineScript.release());
 
+#ifdef JS_ION_PERF
+    writePerfSpewerBaselineProfile(script, code);
+#endif
+
+#ifdef MOZ_VTUNE
+    vtune::MarkScript(code, script, "baseline");
+#endif
+
     return Method_Compiled;
 }
 
 void
 BaselineCompiler::emitInitializeLocals()
 {
     // Initialize all locals to |undefined|. Lexical bindings are temporal
     // dead zoned in bytecode.
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -1010,17 +1010,17 @@ CanOptimizeDenseOrUnboxedArraySetElem(JS
     *isAddingCaseOut = false;
     *protoDepthOut = 0;
 
     // Some initial sanity checks.
     if (initLength < oldInitLength || capacity < oldCapacity)
         return false;
 
     // Unboxed arrays need to be able to emit floating point code.
-    if (obj->is<UnboxedArrayObject>() && !obj->runtimeFromMainThread()->jitSupportsFloatingPoint)
+    if (obj->is<UnboxedArrayObject>() && !obj->runtimeFromActiveCooperatingThread()->jitSupportsFloatingPoint)
         return false;
 
     Shape* shape = obj->maybeShape();
 
     // Cannot optimize if the shape changed.
     if (oldShape != shape)
         return false;
 
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -48,16 +48,19 @@
 #include "vm/Unicode.h"
 
 #include "jsboolinlines.h"
 
 #include "jit/MacroAssembler-inl.h"
 #include "jit/shared/CodeGenerator-shared-inl.h"
 #include "jit/shared/Lowering-shared-inl.h"
 #include "vm/Interpreter-inl.h"
+#ifdef MOZ_VTUNE
+# include "vtune/VTuneWrapper.h"
+#endif
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::AssertedCast;
 using mozilla::DebugOnly;
 using mozilla::FloatingPoint;
 using mozilla::Maybe;
@@ -1806,16 +1809,19 @@ JitCompartment::generateRegExpMatcherStu
     AutoFlushICache afc("RegExpMatcherStub");
     JitCode* code = linker.newCode<CanGC>(cx, OTHER_CODE);
     if (!code)
         return nullptr;
 
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "RegExpMatcherStub");
 #endif
+#ifdef MOZ_VTUNE
+    vtune::MarkStub(code, "RegExpMatcherStub");
+#endif
 
     if (cx->zone()->needsIncrementalBarrier())
         code->togglePreBarriers(true, DontReprotect);
 
     return code;
 }
 
 class OutOfLineRegExpMatcher : public OutOfLineCodeBase<CodeGenerator>
@@ -1963,16 +1969,19 @@ JitCompartment::generateRegExpSearcherSt
     AutoFlushICache afc("RegExpSearcherStub");
     JitCode* code = linker.newCode<CanGC>(cx, OTHER_CODE);
     if (!code)
         return nullptr;
 
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "RegExpSearcherStub");
 #endif
+#ifdef MOZ_VTUNE
+    vtune::MarkStub(code, "RegExpSearcherStub");
+#endif
 
     if (cx->zone()->needsIncrementalBarrier())
         code->togglePreBarriers(true, DontReprotect);
 
     return code;
 }
 
 class OutOfLineRegExpSearcher : public OutOfLineCodeBase<CodeGenerator>
@@ -2111,16 +2120,19 @@ JitCompartment::generateRegExpTesterStub
     AutoFlushICache afc("RegExpTesterStub");
     JitCode* code = linker.newCode<CanGC>(cx, OTHER_CODE);
     if (!code)
         return nullptr;
 
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "RegExpTesterStub");
 #endif
+#ifdef MOZ_VTUNE
+    vtune::MarkStub(code, "RegExpTesterStub");
+#endif
 
     if (cx->zone()->needsIncrementalBarrier())
         code->togglePreBarriers(true, DontReprotect);
 
     return code;
 }
 
 class OutOfLineRegExpTester : public OutOfLineCodeBase<CodeGenerator>
@@ -7716,16 +7728,19 @@ JitCompartment::generateStringConcatStub
 
     Linker linker(masm);
     AutoFlushICache afc("StringConcatStub");
     JitCode* code = linker.newCode<CanGC>(cx, OTHER_CODE);
 
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "StringConcatStub");
 #endif
+#ifdef MOZ_VTUNE
+    vtune::MarkStub(code, "StringConcatStub");
+#endif
 
     return code;
 }
 
 JitCode*
 JitRuntime::generateMallocStub(JSContext* cx)
 {
     const Register regReturn = CallTempReg0;
@@ -7757,16 +7772,19 @@ JitRuntime::generateMallocStub(JSContext
 
     Linker linker(masm);
     AutoFlushICache afc("MallocStub");
     JitCode* code = linker.newCode<NoGC>(cx, OTHER_CODE);
 
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "MallocStub");
 #endif
+#ifdef MOZ_VTUNE
+    vtune::MarkStub(code, "MallocStub");
+#endif
 
     return code;
 }
 
 JitCode*
 JitRuntime::generateFreeStub(JSContext* cx)
 {
     const Register regSlots = CallTempReg0;
@@ -7793,16 +7811,19 @@ JitRuntime::generateFreeStub(JSContext* 
 
     Linker linker(masm);
     AutoFlushICache afc("FreeStub");
     JitCode* code = linker.newCode<NoGC>(cx, OTHER_CODE);
 
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "FreeStub");
 #endif
+#ifdef MOZ_VTUNE
+    vtune::MarkStub(code, "FreeStub");
+#endif
 
     return code;
 }
 
 
 JitCode*
 JitRuntime::generateLazyLinkStub(JSContext* cx)
 {
@@ -7832,16 +7853,19 @@ JitRuntime::generateLazyLinkStub(JSConte
 
     Linker linker(masm);
     AutoFlushICache afc("LazyLinkStub");
     JitCode* code = linker.newCode<NoGC>(cx, OTHER_CODE);
 
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "LazyLinkStub");
 #endif
+#ifdef MOZ_VTUNE
+    vtune::MarkStub(code, "LazyLinkStub");
+#endif
     return code;
 }
 
 bool
 JitRuntime::generateTLEventVM(JSContext* cx, MacroAssembler& masm, const VMFunction& f,
                               bool enter)
 {
 #ifdef JS_TRACE_LOGGING
@@ -9900,16 +9924,20 @@ CodeGenerator::link(JSContext* cx, Compi
 
     ionScript->setDeoptTable(deoptTable_);
 
 #if defined(JS_ION_PERF)
     if (PerfEnabled())
         perfSpewer_.writeProfile(script, code, masm);
 #endif
 
+#ifdef MOZ_VTUNE
+    vtune::MarkScript(code, script, "ion");
+#endif
+
     // for marking during GC.
     if (safepointIndices_.length())
         ionScript->copySafepointIndices(&safepointIndices_[0], masm);
     if (safepoints_.size())
         ionScript->copySafepoints(&safepoints_);
 
     // for reconvering from an Ion Frame.
     if (bailouts_.length())
--- a/js/src/jit/CompileInfo.h
+++ b/js/src/jit/CompileInfo.h
@@ -531,17 +531,17 @@ class CompileInfo
     unsigned nslots_;
     mozilla::Maybe<unsigned> thisSlotForDerivedClassConstructor_;
     JSScript* script_;
     JSFunction* fun_;
     jsbytecode* osrPc_;
     AnalysisMode analysisMode_;
 
     // Whether a script needs an arguments object is unstable over compilation
-    // since the arguments optimization could be marked as failed on the main
+    // since the arguments optimization could be marked as failed on the active
     // thread, so cache a value here and use it throughout for consistency.
     bool scriptNeedsArgsObj_;
 
     // Record the state of previous bailouts in order to prevent compiling the
     // same function identically the next time.
     bool hadOverflowBailout_;
 
     bool mayReadFrameArgsDirectly_;
--- a/js/src/jit/CompileWrappers.cpp
+++ b/js/src/jit/CompileWrappers.cpp
@@ -246,17 +246,17 @@ CompileCompartment::jitCompartment()
 {
     return compartment()->jitCompartment();
 }
 
 const GlobalObject*
 CompileCompartment::maybeGlobal()
 {
     // This uses unsafeUnbarrieredMaybeGlobal() so as not to trigger the read
-    // barrier on the global from off the main thread.  This is safe because we
+    // barrier on the global from off thread.  This is safe because we
     // abort Ion compilation when we GC.
     return compartment()->unsafeUnbarrieredMaybeGlobal();
 }
 
 bool
 CompileCompartment::hasAllocationMetadataBuilder()
 {
     return compartment()->hasAllocationMetadataBuilder();
--- a/js/src/jit/CompileWrappers.h
+++ b/js/src/jit/CompileWrappers.h
@@ -11,17 +11,17 @@
 
 namespace js {
 namespace jit {
 
 class JitRuntime;
 
 // During Ion compilation we need access to various bits of the current
 // compartment, runtime and so forth. However, since compilation can run off
-// thread while the main thread is actively mutating the VM, this access needs
+// thread while the active thread is mutating the VM, this access needs
 // to be restricted. The classes below give the compiler an interface to access
 // all necessary information in a threadsafe fashion.
 
 class CompileRuntime
 {
     JSRuntime* runtime();
 
   public:
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -46,16 +46,19 @@
 #include "jit/ScalarReplacement.h"
 #include "jit/Sink.h"
 #include "jit/StupidAllocator.h"
 #include "jit/ValueNumbering.h"
 #include "jit/WasmBCE.h"
 #include "vm/Debugger.h"
 #include "vm/HelperThreads.h"
 #include "vm/TraceLogging.h"
+#ifdef MOZ_VTUNE
+# include "vtune/VTuneWrapper.h"
+#endif
 
 #include "jscompartmentinlines.h"
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
 
 #include "jit/JitFrames-inl.h"
 #include "jit/shared/Lowering-shared-inl.h"
 #include "vm/Debugger-inl.h"
@@ -366,17 +369,17 @@ JSContext::freeOsrTempData()
     js_free(osrTempData_);
     osrTempData_ = nullptr;
 }
 
 void
 JitZoneGroup::patchIonBackedges(JSContext* cx, BackedgeTarget target)
 {
     if (target == BackedgeLoopHeader) {
-        // We must be on the main thread. The caller must use
+        // We must be on the active thread. The caller must use
         // AutoPreventBackedgePatching to ensure we don't reenter.
         MOZ_ASSERT(cx->runtime()->jitRuntime()->preventBackedgePatching());
         MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
     } else {
         // We must be called from InterruptRunningJitCode, or a signal handler
         // triggered there. rt->handlingJitInterrupt() ensures we can't reenter
         // this code.
         MOZ_ASSERT(!cx->runtime()->jitRuntime()->preventBackedgePatching());
@@ -805,16 +808,20 @@ JitCode::finalize(FreeOp* fop)
 #ifdef DEBUG
     JSRuntime* rt = fop->runtime();
     if (hasBytecodeMap_) {
         MOZ_ASSERT(rt->jitRuntime()->hasJitcodeGlobalTable());
         MOZ_ASSERT(!rt->jitRuntime()->getJitcodeGlobalTable()->lookup(raw()));
     }
 #endif
 
+#ifdef MOZ_VTUNE
+    vtune::UnmarkCode(this);
+#endif
+
     MOZ_ASSERT(pool_);
 
     // With W^X JIT code, reprotecting memory for each JitCode instance is
     // slow, so we record the ranges and poison them later all at once. It's
     // safe to ignore OOM here, it just means we won't poison the code.
     if (fop->appendJitPoisonRange(JitPoisonRange(pool_, code_ - headerSize_,
                                                  headerSize_ + bufferSize_)))
     {
@@ -823,16 +830,17 @@ JitCode::finalize(FreeOp* fop)
     code_ = nullptr;
 
     // Code buffers are stored inside ExecutablePools. Pools are refcounted.
     // Releasing the pool may free it. Horrible hack: if we are using perf
     // integration, we don't want to reuse code addresses, so we just leak the
     // memory instead.
     if (!PerfEnabled())
         pool_->release(headerSize_ + bufferSize_, CodeKind(kind_));
+
     pool_ = nullptr;
 }
 
 void
 JitCode::togglePreBarriers(bool enabled, ReprotectCode reprotect)
 {
     uint8_t* start = code_ + preBarrierTableOffset();
     CompactBufferReader reader(start, start + preBarrierTableBytes_);
@@ -1374,17 +1382,17 @@ IonScript::unlinkFromRuntime(FreeOp* fop
     // called during destruction, and may be additionally called when the
     // script is invalidated.
     backedgeEntries_ = 0;
 }
 
 void
 jit::ToggleBarriers(JS::Zone* zone, bool needs)
 {
-    JSRuntime* rt = zone->runtimeFromMainThread();
+    JSRuntime* rt = zone->runtimeFromActiveCooperatingThread();
     if (!rt->hasJitRuntime())
         return;
 
     for (auto script = zone->cellIter<JSScript>(); !script.done(); script.next()) {
         if (script->hasIonScript())
             script->ionScript()->toggleBarriers(needs);
         if (script->hasBaselineScript())
             script->baselineScript()->toggleBarriers(needs);
@@ -2417,18 +2425,18 @@ CheckScript(JSContext* cx, JSScript* scr
 static MethodStatus
 CheckScriptSize(JSContext* cx, JSScript* script)
 {
     if (!JitOptions.limitScriptSize)
         return Method_Compiled;
 
     uint32_t numLocalsAndArgs = NumLocalsAndArgs(script);
 
-    if (script->length() > MAX_MAIN_THREAD_SCRIPT_SIZE ||
-        numLocalsAndArgs > MAX_MAIN_THREAD_LOCALS_AND_ARGS)
+    if (script->length() > MAX_ACTIVE_THREAD_SCRIPT_SIZE ||
+        numLocalsAndArgs > MAX_ACTIVE_THREAD_LOCALS_AND_ARGS)
     {
         if (!OffThreadCompilationAvailable(cx)) {
             JitSpew(JitSpew_IonAbort, "Script too large (%" PRIuSIZE " bytes) (%u locals/args)",
                     script->length(), numLocalsAndArgs);
             TrackIonAbort(cx, script, script->code(), "too large");
             return Method_CantCompile;
         }
     }
@@ -2536,19 +2544,19 @@ Compile(JSContext* cx, HandleScript scri
 
 } // namespace jit
 } // namespace js
 
 bool
 jit::OffThreadCompilationAvailable(JSContext* cx)
 {
     // Even if off thread compilation is enabled, compilation must still occur
-    // on the main thread in some cases.
+    // on the active thread in some cases.
     //
-    // Require cpuCount > 1 so that Ion compilation jobs and main-thread
+    // Require cpuCount > 1 so that Ion compilation jobs and active-thread
     // execution are not competing for the same resources.
     return cx->runtime()->canUseOffthreadIonCompilation()
         && HelperThreadState().cpuCount > 1
         && CanUseExtraThreads();
 }
 
 MethodStatus
 jit::CanEnter(JSContext* cx, RunState& state)
--- a/js/src/jit/Ion.h
+++ b/js/src/jit/Ion.h
@@ -70,17 +70,17 @@ class JitContext
     JitContext(JSContext* cx, TempAllocator* temp);
     JitContext(CompileRuntime* rt, CompileCompartment* comp, TempAllocator* temp);
     JitContext(CompileRuntime* rt, TempAllocator* temp);
     explicit JitContext(CompileRuntime* rt);
     explicit JitContext(TempAllocator* temp);
     JitContext();
     ~JitContext();
 
-    // Running context when executing on the main thread. Not available during
+    // Running context when executing on the active thread. Not available during
     // compilation.
     JSContext* cx;
 
     // Allocator for temporary memory during compilation.
     TempAllocator* temp;
 
     // Wrappers with information about the current runtime/compartment for use
     // during compilation.
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -7004,17 +7004,17 @@ IonBuilder::ensureDefiniteTypeSet(MDefin
     current->add(filter);
     return filter;
 }
 
 static size_t
 NumFixedSlots(JSObject* object)
 {
     // Note: we can't use object->numFixedSlots() here, as this will read the
-    // shape and can race with the main thread if we are building off thread.
+    // shape and can race with the active thread if we are building off thread.
     // The allocation kind and object class (which goes through the type) can
     // be read freely, however.
     gc::AllocKind kind = object->asTenured().getAllocKind();
     return gc::GetGCKindSlots(kind, object->getClass());
 }
 
 static bool
 IsUninitializedGlobalLexicalSlot(JSObject* obj, PropertyName* name)
@@ -13071,17 +13071,17 @@ IonBuilder::storeReferenceTypedObjectVal
     current->add(store);
     return true;
 }
 
 JSObject*
 IonBuilder::checkNurseryObject(JSObject* obj)
 {
     // If we try to use any nursery pointers during compilation, make sure that
-    // the main thread will cancel this compilation before performing a minor
+    // the active thread will cancel this compilation before performing a minor
     // GC. All constants used during compilation should either go through this
     // function or should come from a type set (which has a similar barrier).
     if (obj && IsInsideNursery(obj)) {
         compartment->zone()->setMinorGCShouldCancelIonCompilations();
         IonBuilder* builder = this;
         while (builder) {
             builder->setNotSafeForMinorGC();
             builder = builder->callerBuilder_;
--- a/js/src/jit/IonOptimizationLevels.cpp
+++ b/js/src/jit/IonOptimizationLevels.cpp
@@ -35,18 +35,18 @@ OptimizationInfo::initNormalOptimization
     gvn_ = true;
     rangeAnalysis_ = true;
     reordering_ = true;
     sincos_ = true;
     sink_ = true;
 
     registerAllocator_ = RegisterAllocator_Backtracking;
 
-    inlineMaxBytecodePerCallSiteMainThread_ = 550;
-    inlineMaxBytecodePerCallSiteOffThread_ = 1100;
+    inlineMaxBytecodePerCallSiteActiveCooperatingThread_ = 550;
+    inlineMaxBytecodePerCallSiteHelperThread_ = 1100;
     inlineMaxCalleeInlinedBytecodeLength_ = 3550;
     inlineMaxTotalBytecodeLength_ = 85000;
     inliningMaxCallerBytecodeLength_ = 1600;
     maxInlineDepth_ = 3;
     scalarReplacement_ = true;
     smallFunctionMaxInlineDepth_ = 10;
     compilerWarmUpThreshold_ = CompilerWarmupThreshold;
     compilerSmallFunctionWarmUpThreshold_ = CompilerSmallFunctionWarmupThreshold;
@@ -88,27 +88,27 @@ OptimizationInfo::compilerWarmUpThreshol
         warmUpThreshold = JitOptions.forcedDefaultIonWarmUpThreshold.ref();
 
     if (JitOptions.isSmallFunction(script)) {
         warmUpThreshold = compilerSmallFunctionWarmUpThreshold_;
         if (JitOptions.forcedDefaultIonSmallFunctionWarmUpThreshold.isSome())
             warmUpThreshold = JitOptions.forcedDefaultIonSmallFunctionWarmUpThreshold.ref();
     }
 
-    // If the script is too large to compile on the main thread, we can still
+    // If the script is too large to compile on the active thread, we can still
     // compile it off thread. In these cases, increase the warm-up counter
     // threshold to improve the compilation's type information and hopefully
     // avoid later recompilation.
 
-    if (script->length() > MAX_MAIN_THREAD_SCRIPT_SIZE)
-        warmUpThreshold *= (script->length() / (double) MAX_MAIN_THREAD_SCRIPT_SIZE);
+    if (script->length() > MAX_ACTIVE_THREAD_SCRIPT_SIZE)
+        warmUpThreshold *= (script->length() / (double) MAX_ACTIVE_THREAD_SCRIPT_SIZE);
 
     uint32_t numLocalsAndArgs = NumLocalsAndArgs(script);
-    if (numLocalsAndArgs > MAX_MAIN_THREAD_LOCALS_AND_ARGS)
-        warmUpThreshold *= (numLocalsAndArgs / (double) MAX_MAIN_THREAD_LOCALS_AND_ARGS);
+    if (numLocalsAndArgs > MAX_ACTIVE_THREAD_LOCALS_AND_ARGS)
+        warmUpThreshold *= (numLocalsAndArgs / (double) MAX_ACTIVE_THREAD_LOCALS_AND_ARGS);
 
     if (!pc || JitOptions.eagerCompilation)
         return warmUpThreshold;
 
     // It's more efficient to enter outer loops, rather than inner loops, via OSR.
     // To accomplish this, we use a slightly higher threshold for inner loops.
     // Note that the loop depth is always > 0 so we will prefer non-OSR over OSR.
     uint32_t loopDepth = LoopEntryDepthHint(pc);
--- a/js/src/jit/IonOptimizationLevels.h
+++ b/js/src/jit/IonOptimizationLevels.h
@@ -93,19 +93,19 @@ class OptimizationInfo
     // Toggles whether sink is used.
     bool sink_;
 
     // Describes which register allocator to use.
     IonRegisterAllocator registerAllocator_;
 
     // The maximum total bytecode size of an inline call site. We use a lower
     // value if off-thread compilation is not available, to avoid stalling the
-    // main thread.
-    uint32_t inlineMaxBytecodePerCallSiteOffThread_;
-    uint32_t inlineMaxBytecodePerCallSiteMainThread_;
+    // active thread.
+    uint32_t inlineMaxBytecodePerCallSiteHelperThread_;
+    uint32_t inlineMaxBytecodePerCallSiteActiveCooperatingThread_;
 
     // The maximum value we allow for baselineScript->inlinedBytecodeLength_
     // when inlining.
     uint16_t inlineMaxCalleeInlinedBytecodeLength_;
 
     // The maximum bytecode length we'll inline in a single compilation.
     uint32_t inlineMaxTotalBytecodeLength_;
 
@@ -243,18 +243,18 @@ class OptimizationInfo
     bool isSmallFunction(JSScript* script) const;
 
     uint32_t maxInlineDepth() const {
         return maxInlineDepth_;
     }
 
     uint32_t inlineMaxBytecodePerCallSite(bool offThread) const {
         return (offThread || !JitOptions.limitScriptSize)
-               ? inlineMaxBytecodePerCallSiteOffThread_
-               : inlineMaxBytecodePerCallSiteMainThread_;
+               ? inlineMaxBytecodePerCallSiteHelperThread_
+               : inlineMaxBytecodePerCallSiteActiveCooperatingThread_;
     }
 
     uint16_t inlineMaxCalleeInlinedBytecodeLength() const {
         return inlineMaxCalleeInlinedBytecodeLength_;
     }
 
     uint32_t inlineMaxTotalBytecodeLength() const {
         return inlineMaxTotalBytecodeLength_;
--- a/js/src/jit/JitCompartment.h
+++ b/js/src/jit/JitCompartment.h
@@ -325,17 +325,17 @@ class JitZoneGroup
     };
 
   private:
     // Whether patchable backedges currently jump to the loop header or the
     // interrupt check.
     ZoneGroupData<BackedgeTarget> backedgeTarget_;
 
     // List of all backedges in all Ion code. The backedge edge list is accessed
-    // asynchronously when the main thread is paused and preventBackedgePatching_
+    // asynchronously when the active thread is paused and preventBackedgePatching_
     // is false. Thus, the list must only be mutated while preventBackedgePatching_
     // is true.
     ZoneGroupData<InlineList<PatchableBackedge>> backedgeList_;
     InlineList<PatchableBackedge>& backedgeList() { return backedgeList_.ref(); }
 
   public:
     explicit JitZoneGroup(ZoneGroup* group);
 
@@ -496,17 +496,17 @@ class JitCompartment
 
         // This function is used by Eager Simd Unbox phase, so we cannot use the
         // read barrier. For more information, see the comment above
         // CodeGenerator::simdRefreshTemplatesDuringLink_ .
         return tpl.unbarrieredGet();
     }
 
     // This function is used to call the read barrier, to mark the SIMD template
-    // type as used. This function can only be called from the main thread.
+    // type as used. This function can only be called from the active thread.
     void registerSimdTemplateObjectFor(SimdType type) {
         ReadBarrieredObject& tpl = simdTemplateObjects_[type];
         MOZ_ASSERT(tpl.unbarrieredGet());
         tpl.get();
     }
 
     JitCode* getStubCode(uint32_t key) {
         ICStubCodeMap::AddPtr p = stubCodes_->lookupForAdd(key);
@@ -645,17 +645,17 @@ class MOZ_STACK_CLASS AutoWritableJitCod
         rt_->toggleAutoWritableJitCodeActive(true);
         if (!ExecutableAllocator::makeWritable(addr_, size_))
             MOZ_CRASH();
     }
     AutoWritableJitCode(void* addr, size_t size)
       : AutoWritableJitCode(TlsContext.get()->runtime(), addr, size)
     {}
     explicit AutoWritableJitCode(JitCode* code)
-      : AutoWritableJitCode(code->runtimeFromMainThread(), code->raw(), code->bufferSize())
+      : AutoWritableJitCode(code->runtimeFromActiveCooperatingThread(), code->raw(), code->bufferSize())
     {}
     ~AutoWritableJitCode() {
         if (!ExecutableAllocator::makeExecutable(addr_, size_))
             MOZ_CRASH();
         rt_->toggleAutoWritableJitCodeActive(false);
     }
 };
 
--- a/js/src/jit/JitFrames.cpp
+++ b/js/src/jit/JitFrames.cpp
@@ -2755,17 +2755,17 @@ JitFrameIterator::verifyReturnAddressUsi
     MOZ_ASSERT(returnAddressToFp_ != nullptr);
 
     // Only handle Ion frames for now.
     if (type_ != JitFrame_IonJS && type_ != JitFrame_BaselineJS)
         return true;
 
     JSRuntime* rt = TlsContext.get()->runtime();
 
-    // Don't verify on non-main-thread.
+    // Don't verify while off thread.
     if (!CurrentThreadCanAccessRuntime(rt))
         return true;
 
     // Don't verify if sampling is being suppressed.
     if (!TlsContext.get()->isProfilerSamplingEnabled())
         return true;
 
     if (JS::CurrentThreadIsHeapMinorCollecting())
--- a/js/src/jit/JitOptions.h
+++ b/js/src/jit/JitOptions.h
@@ -11,19 +11,19 @@
 
 #include "jit/IonTypes.h"
 #include "js/TypeDecls.h"
 
 namespace js {
 namespace jit {
 
 // Longer scripts can only be compiled off thread, as these compilations
-// can be expensive and stall the main thread for too long.
-static const uint32_t MAX_MAIN_THREAD_SCRIPT_SIZE = 2 * 1000;
-static const uint32_t MAX_MAIN_THREAD_LOCALS_AND_ARGS = 256;
+// can be expensive and stall the active thread for too long.
+static const uint32_t MAX_ACTIVE_THREAD_SCRIPT_SIZE = 2 * 1000;
+static const uint32_t MAX_ACTIVE_THREAD_LOCALS_AND_ARGS = 256;
 
 // Possible register allocators which may be used.
 enum IonRegisterAllocator {
     RegisterAllocator_Backtracking,
     RegisterAllocator_Testbed,
     RegisterAllocator_Stupid
 };
 
--- a/js/src/jit/JitSpewer.cpp
+++ b/js/src/jit/JitSpewer.cpp
@@ -206,17 +206,17 @@ IonSpewer::init()
     return true;
 }
 
 void
 IonSpewer::beginFunction()
 {
     // If we are doing a synchronous logging then we spew everything as we go,
     // as this is useful in case of failure during the compilation. On the other
-    // hand, it is recommended to disabled off main thread compilation.
+    // hand, it is recommended to disable off thread compilation.
     if (!getAsyncLogging() && !firstFunction_) {
         LockGuard<Mutex> guard(outputLock_);
         jsonOutput_.put(","); // separate functions
     }
 }
 
 void
 IonSpewer::spewPass(GraphSpewer* gs)
--- a/js/src/jit/JitcodeMap.cpp
+++ b/js/src/jit/JitcodeMap.cpp
@@ -449,18 +449,17 @@ JitcodeGlobalTable::lookupForSamplerInfa
         JitcodeGlobalEntry& rejoinEntry = RejoinEntry(rt, entry->ionCacheEntry(), ptr);
         rejoinEntry.setGeneration(sampleBufferGen);
     }
 
     // JitcodeGlobalEntries are marked at the end of the mark phase. A read
     // barrier is not needed. Any JS frames sampled during the sweep phase of
     // the GC must be on stack, and on-stack frames must already be marked at
     // the beginning of the sweep phase. It's not possible to assert this here
-    // as we may not be running on the main thread when called from the gecko
-    // profiler.
+    // as we may not be off thread when called from the gecko profiler.
 
     return *entry;
 }
 
 JitcodeGlobalEntry*
 JitcodeGlobalTable::lookupInternal(void* ptr)
 {
     JitcodeGlobalEntry query = JitcodeGlobalEntry::MakeQuery(ptr);
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -887,22 +887,22 @@ MakeUnknownTypeSet()
 }
 
 #ifdef DEBUG
 
 bool
 jit::IonCompilationCanUseNurseryPointers()
 {
     // If we are doing backend compilation, which could occur on a helper
-    // thread but might actually be on the main thread, check the flag set on
+    // thread but might actually be on the active thread, check the flag set on
     // the JSContext by AutoEnterIonCompilation.
     if (CurrentThreadIsIonCompiling())
         return !CurrentThreadIsIonCompilingSafeForMinorGC();
 
-    // Otherwise, we must be on the main thread during MIR construction. The
+    // Otherwise, we must be on the active thread during MIR construction. The
     // store buffer must have been notified that minor GCs must cancel pending
     // or in progress Ion compilations.
     JSContext* cx = TlsContext.get();
     return cx->zone()->group()->storeBuffer().cancelIonCompilations();
 }
 
 #endif // DEBUG
 
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -3981,17 +3981,17 @@ class MInitElemGetterSetter
   public:
     INSTRUCTION_HEADER(InitElemGetterSetter)
     TRIVIAL_NEW_WRAPPERS
     NAMED_OPERANDS((0, object), (1, idValue), (2, value))
 
 };
 
 // WrappedFunction wraps a JSFunction so it can safely be used off-thread.
-// In particular, a function's flags can be modified on the main thread as
+// In particular, a function's flags can be modified on the active thread as
 // functions are relazified and delazified, so we must be careful not to access
 // these flags off-thread.
 class WrappedFunction : public TempObject
 {
     CompilerFunction fun_;
     uint16_t nargs_;
     bool isNative_ : 1;
     bool isConstructor_ : 1;
@@ -8346,17 +8346,17 @@ class MSubstr
         return AliasSet::None();
     }
 };
 
 struct LambdaFunctionInfo
 {
     // The functions used in lambdas are the canonical original function in
     // the script, and are immutable except for delazification. Record this
-    // information while still on the main thread to avoid races.
+    // information while still on the active thread to avoid races.
     CompilerFunction fun;
     uint16_t flags;
     uint16_t nargs;
     gc::Cell* scriptOrLazyScript;
     bool singletonType;
     bool useSingletonForClone;
 
     explicit LambdaFunctionInfo(JSFunction* fun)
--- a/js/src/jit/MIRGenerator.h
+++ b/js/src/jit/MIRGenerator.h
@@ -75,17 +75,17 @@ class MIRGenerator
     mozilla::GenericErrorResult<AbortReason> abort(AbortReason r);
     mozilla::GenericErrorResult<AbortReason>
     abort(AbortReason r, const char* message, ...) MOZ_FORMAT_PRINTF(3, 4);
 
     mozilla::GenericErrorResult<AbortReason>
     abortFmt(AbortReason r, const char* message, va_list ap);
 
     // Collect the evaluation result of phases after IonBuilder, such that
-    // off-main-thread compilation can report what error got encountered.
+    // off-thread compilation can report what error got encountered.
     void setOffThreadStatus(AbortReasonOr<Ok> result) {
         MOZ_ASSERT(offThreadStatus_.isOk());
         offThreadStatus_ = result;
     }
     AbortReasonOr<Ok> getOffThreadStatus() const {
         return offThreadStatus_;
     }
 
@@ -108,17 +108,17 @@ class MIRGenerator
 
     bool safeForMinorGC() const {
         return safeForMinorGC_;
     }
     void setNotSafeForMinorGC() {
         safeForMinorGC_ = false;
     }
 
-    // Whether the main thread is trying to cancel this build.
+    // Whether the active thread is trying to cancel this build.
     bool shouldCancel(const char* why) {
         maybePause();
         return cancelBuild_;
     }
     void cancel() {
         cancelBuild_ = true;
     }
 
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -158,17 +158,17 @@ void
 MacroAssembler::guardObjectType(Register obj, const TypeSet* types,
                                 Register scratch, Label* miss)
 {
     MOZ_ASSERT(!types->unknown());
     MOZ_ASSERT(!types->hasType(TypeSet::AnyObjectType()));
     MOZ_ASSERT_IF(types->getObjectCount() > 0, scratch != InvalidReg);
 
     // Note: this method elides read barriers on values read from type sets, as
-    // this may be called off the main thread during Ion compilation. This is
+    // this may be called off thread during Ion compilation. This is
     // safe to do as the final JitCode object will be allocated during the
     // incremental GC (or the compilation canceled before we start sweeping),
     // see CodeGenerator::link. Other callers should use TypeSet::readBarrier
     // to trigger the barrier on the contents of type sets passed in here.
     Label matched;
 
     BranchGCPtr lastBranch;
     MOZ_ASSERT(!lastBranch.isInitialized());
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -1202,17 +1202,17 @@ AutoDetectInvalidation::setReturnOverrid
 
 void
 AssertValidObjectPtr(JSContext* cx, JSObject* obj)
 {
 #ifdef DEBUG
     // Check what we can, so that we'll hopefully assert/crash if we get a
     // bogus object (pointer).
     MOZ_ASSERT(obj->compartment() == cx->compartment());
-    MOZ_ASSERT(obj->runtimeFromMainThread() == cx->runtime());
+    MOZ_ASSERT(obj->runtimeFromActiveCooperatingThread() == cx->runtime());
 
     MOZ_ASSERT_IF(!obj->hasLazyGroup() && obj->maybeShape(),
                   obj->group()->clasp() == obj->maybeShape()->getObjectClass());
 
     if (obj->isTenured()) {
         MOZ_ASSERT(obj->isAligned());
         gc::AllocKind kind = obj->asTenured().getAllocKind();
         MOZ_ASSERT(gc::IsObjectAllocKind(kind));
--- a/js/src/jit/arm/Bailouts-arm.cpp
+++ b/js/src/jit/arm/Bailouts-arm.cpp
@@ -84,17 +84,17 @@ BailoutFrameInfo::BailoutFrameInfo(const
     attachOnJitActivation(activations);
 
     if (bailout->frameClass() == FrameSizeClass::None()) {
         snapshotOffset_ = bailout->snapshotOffset();
         return;
     }
 
     // Compute the snapshot offset from the bailout ID.
-    JSRuntime* rt = activation->compartment()->runtimeFromMainThread();
+    JSRuntime* rt = activation->compartment()->runtimeFromActiveCooperatingThread();
     JitCode* code = rt->jitRuntime()->getBailoutTable(bailout->frameClass());
     uintptr_t tableOffset = bailout->tableOffset();
     uintptr_t tableStart = reinterpret_cast<uintptr_t>(Assembler::BailoutTableStart(code->raw()));
 
     MOZ_ASSERT(tableOffset >= tableStart &&
                tableOffset < tableStart + code->instructionsSize());
     MOZ_ASSERT((tableOffset - tableStart) % BAILOUT_TABLE_ENTRY_SIZE == 0);
 
--- a/js/src/jit/mips32/Bailouts-mips32.cpp
+++ b/js/src/jit/mips32/Bailouts-mips32.cpp
@@ -27,17 +27,17 @@ BailoutFrameInfo::BailoutFrameInfo(const
     attachOnJitActivation(activations);
 
     if (bailout->frameClass() == FrameSizeClass::None()) {
         snapshotOffset_ = bailout->snapshotOffset();
         return;
     }
 
     // Compute the snapshot offset from the bailout ID.
-    JSRuntime* rt = activation->compartment()->runtimeFromMainThread();
+    JSRuntime* rt = activation->compartment()->runtimeFromActiveCooperatingThread();
     JitCode* code = rt->jitRuntime()->getBailoutTable(bailout->frameClass());
     uintptr_t tableOffset = bailout->tableOffset();
     uintptr_t tableStart = reinterpret_cast<uintptr_t>(code->raw());
 
     MOZ_ASSERT(tableOffset >= tableStart &&
                tableOffset < tableStart + code->instructionsSize());
     MOZ_ASSERT((tableOffset - tableStart) % BAILOUT_TABLE_ENTRY_SIZE == 0);
 
--- a/js/src/jit/x64/Trampoline-x64.cpp
+++ b/js/src/jit/x64/Trampoline-x64.cpp
@@ -8,16 +8,19 @@
 #include "jit/JitCompartment.h"
 #include "jit/JitFrames.h"
 #include "jit/Linker.h"
 #ifdef JS_ION_PERF
 # include "jit/PerfSpewer.h"
 #endif
 #include "jit/VMFunctions.h"
 #include "jit/x64/SharedICHelpers-x64.h"
+#ifdef MOZ_VTUNE
+# include "vtune/VTuneWrapper.h"
+#endif
 
 #include "jit/MacroAssembler-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::IsPowerOfTwo;
 
@@ -335,16 +338,19 @@ JitRuntime::generateEnterJIT(JSContext* 
     masm.ret();
 
     Linker linker(masm);
     JitCode* code = linker.newCode<NoGC>(cx, OTHER_CODE);
 
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "EnterJIT");
 #endif
+#ifdef MOZ_VTUNE
+    vtune::MarkStub(code, "EnterJIT");
+#endif
 
     return code;
 }
 
 JitCode*
 JitRuntime::generateInvalidator(JSContext* cx)
 {
     AutoJitContextAlloc ajca(cx);
@@ -384,16 +390,19 @@ JitRuntime::generateInvalidator(JSContex
     masm.jmp(bailoutTail);
 
     Linker linker(masm);
     JitCode* code = linker.newCode<NoGC>(cx, OTHER_CODE);
 
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "Invalidator");
 #endif
+#ifdef MOZ_VTUNE
+    vtune::MarkStub(code, "Invalidator");
+#endif
 
     return code;
 }
 
 JitCode*
 JitRuntime::generateArgumentsRectifier(JSContext* cx, void** returnAddrOut)
 {
     // Do not erase the frame pointer in this function.
@@ -543,16 +552,19 @@ JitRuntime::generateArgumentsRectifier(J
     masm.ret();
 
     Linker linker(masm);
     JitCode* code = linker.newCode<NoGC>(cx, OTHER_CODE);
 
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "ArgumentsRectifier");
 #endif
+#ifdef MOZ_VTUNE
+    vtune::MarkStub(code, "ArgumentsRectifier");
+#endif
 
     if (returnAddrOut)
         *returnAddrOut = (void*)(code->raw() + returnOffset);
     return code;
 }
 
 static void
 PushBailoutFrame(MacroAssembler& masm, Register spArg)
@@ -628,16 +640,19 @@ JitRuntime::generateBailoutHandler(JSCon
     GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID);
 
     Linker linker(masm);
     JitCode* code = linker.newCode<NoGC>(cx, OTHER_CODE);
 
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "BailoutHandler");
 #endif
+#ifdef MOZ_VTUNE
+    vtune::MarkStub(code, "BailoutHandler");
+#endif
 
     return code;
 }
 
 JitCode*
 JitRuntime::generateVMWrapper(JSContext* cx, const VMFunction& f)
 {
     MOZ_ASSERT(functionWrappers_);
@@ -813,16 +828,19 @@ JitRuntime::generateVMWrapper(JSContext*
     Linker linker(masm);
     JitCode* wrapper = linker.newCode<NoGC>(cx, OTHER_CODE);
     if (!wrapper)
         return nullptr;
 
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(wrapper, "VMWrapper");
 #endif
+#ifdef MOZ_VTUNE
+    vtune::MarkStub(wrapper, "VMWrapper");
+#endif
 
     // linker.newCode may trigger a GC and sweep functionWrappers_ so we have to
     // use relookupOrAdd instead of add.
     if (!functionWrappers_->relookupOrAdd(p, &f, wrapper))
         return nullptr;
 
     return wrapper;
 }
@@ -849,16 +867,19 @@ JitRuntime::generatePreBarrier(JSContext
     masm.ret();
 
     Linker linker(masm);
     JitCode* code = linker.newCode<NoGC>(cx, OTHER_CODE);
 
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "PreBarrier");
 #endif
+#ifdef MOZ_VTUNE
+    vtune::MarkStub(code, "PreBarrier");
+#endif
 
     return code;
 }
 
 typedef bool (*HandleDebugTrapFn)(JSContext*, BaselineFrame*, uint8_t*, bool*);
 static const VMFunction HandleDebugTrapInfo =
     FunctionInfo<HandleDebugTrapFn>(HandleDebugTrap, "HandleDebugTrap");
 
@@ -925,16 +946,19 @@ JitRuntime::generateDebugTrapHandler(JSC
     masm.ret();
 
     Linker linker(masm);
     JitCode* codeDbg = linker.newCode<NoGC>(cx, OTHER_CODE);
 
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(codeDbg, "DebugTrapHandler");
 #endif
+#ifdef MOZ_VTUNE
+    vtune::MarkStub(codeDbg, "DebugTrapHandler");
+#endif
 
     return codeDbg;
 }
 
 JitCode*
 JitRuntime::generateExceptionTailStub(JSContext* cx, void* handler)
 {
     MacroAssembler masm;
@@ -942,16 +966,19 @@ JitRuntime::generateExceptionTailStub(JS
     masm.handleFailureWithHandlerTail(handler);
 
     Linker linker(masm);
     JitCode* code = linker.newCode<NoGC>(cx, OTHER_CODE);
 
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "ExceptionTailStub");
 #endif
+#ifdef MOZ_VTUNE
+    vtune::MarkStub(code, "ExceptionTailStub");
+#endif
 
     return code;
 }
 
 JitCode*
 JitRuntime::generateBailoutTailStub(JSContext* cx)
 {
     MacroAssembler masm;
@@ -959,16 +986,19 @@ JitRuntime::generateBailoutTailStub(JSCo
     masm.generateBailoutTail(rdx, r9);
 
     Linker linker(masm);
     JitCode* code = linker.newCode<NoGC>(cx, OTHER_CODE);
 
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "BailoutTailStub");
 #endif
+#ifdef MOZ_VTUNE
+    vtune::MarkStub(code, "BailoutTailStub");
+#endif
 
     return code;
 }
 
 JitCode*
 JitRuntime::generateProfilerExitFrameTailStub(JSContext* cx)
 {
     MacroAssembler masm;
@@ -1293,11 +1323,14 @@ JitRuntime::generateProfilerExitFrameTai
     }
 
     Linker linker(masm);
     JitCode* code = linker.newCode<NoGC>(cx, OTHER_CODE);
 
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "ProfilerExitFrameStub");
 #endif
+#ifdef MOZ_VTUNE
+    vtune::MarkStub(code, "ProfilerExitFrameStub");
+#endif
 
     return code;
 }
--- a/js/src/jit/x86/Bailouts-x86.cpp
+++ b/js/src/jit/x86/Bailouts-x86.cpp
@@ -80,17 +80,17 @@ BailoutFrameInfo::BailoutFrameInfo(const
     attachOnJitActivation(activations);
 
     if (bailout->frameClass() == FrameSizeClass::None()) {
         snapshotOffset_ = bailout->snapshotOffset();
         return;
     }
 
     // Compute the snapshot offset from the bailout ID.
-    JSRuntime* rt = activation->compartment()->runtimeFromMainThread();
+    JSRuntime* rt = activation->compartment()->runtimeFromActiveCooperatingThread();
     JitCode* code = rt->jitRuntime()->getBailoutTable(bailout->frameClass());
     uintptr_t tableOffset = bailout->tableOffset();
     uintptr_t tableStart = reinterpret_cast<uintptr_t>(code->raw());
 
     MOZ_ASSERT(tableOffset >= tableStart &&
                tableOffset < tableStart + code->instructionsSize());
     MOZ_ASSERT((tableOffset - tableStart) % BAILOUT_TABLE_ENTRY_SIZE == 0);
 
--- a/js/src/jit/x86/Trampoline-x86.cpp
+++ b/js/src/jit/x86/Trampoline-x86.cpp
@@ -14,16 +14,19 @@
 #include "jit/JitFrames.h"
 #include "jit/JitSpewer.h"
 #include "jit/Linker.h"
 #ifdef JS_ION_PERF
 # include "jit/PerfSpewer.h"
 #endif
 #include "jit/VMFunctions.h"
 #include "jit/x86/SharedICHelpers-x86.h"
+#ifdef MOZ_VTUNE
+# include "vtune/VTuneWrapper.h"
+#endif
 
 #include "jsscriptinlines.h"
 
 #include "jit/MacroAssembler-inl.h"
 
 using mozilla::IsPowerOfTwo;
 
 using namespace js;
@@ -380,16 +383,19 @@ JitRuntime::generateInvalidator(JSContex
 
     Linker linker(masm);
     JitCode* code = linker.newCode<NoGC>(cx, OTHER_CODE);
     JitSpew(JitSpew_IonInvalidate, "   invalidation thunk created at %p", (void*) code->raw());
 
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "Invalidator");
 #endif
+#ifdef MOZ_VTUNE
+    vtune::MarkStub(code, "Invalidator");
+#endif
 
     return code;
 }
 
 JitCode*
 JitRuntime::generateArgumentsRectifier(JSContext* cx, void** returnAddrOut)
 {
     MacroAssembler masm(cx);
@@ -541,16 +547,19 @@ JitRuntime::generateArgumentsRectifier(J
     masm.ret();
 
     Linker linker(masm);
     JitCode* code = linker.newCode<NoGC>(cx, OTHER_CODE);
 
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "ArgumentsRectifier");
 #endif
+#ifdef MOZ_VTUNE
+    vtune::MarkStub(code, "ArgumentsRectifier");
+#endif
 
     if (returnAddrOut)
         *returnAddrOut = (void*) (code->raw() + returnOffset);
     return code;
 }
 
 static void
 PushBailoutFrame(MacroAssembler& masm, uint32_t frameClass, Register spArg)
@@ -640,17 +649,20 @@ JitRuntime::generateBailoutTable(JSConte
     masm.bind(&bailout);
 
     GenerateBailoutThunk(cx, masm, frameClass);
 
     Linker linker(masm);
     JitCode* code = linker.newCode<NoGC>(cx, OTHER_CODE);
 
 #ifdef JS_ION_PERF
-    writePerfSpewerJitCodeProfile(code, "BailoutHandler");
+    writePerfSpewerJitCodeProfile(code, "BailoutTable");
+#endif
+#ifdef MOZ_VTUNE
+    vtune::MarkStub(code, "BailoutTable");
 #endif
 
     return code;
 }
 
 JitCode*
 JitRuntime::generateBailoutHandler(JSContext* cx)
 {
@@ -658,16 +670,19 @@ JitRuntime::generateBailoutHandler(JSCon
     GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID);
 
     Linker linker(masm);
     JitCode* code = linker.newCode<NoGC>(cx, OTHER_CODE);
 
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "BailoutHandler");
 #endif
+#ifdef MOZ_VTUNE
+    vtune::MarkStub(code, "BailoutHandler");
+#endif
 
     return code;
 }
 
 JitCode*
 JitRuntime::generateVMWrapper(JSContext* cx, const VMFunction& f)
 {
     MOZ_ASSERT(functionWrappers_);
@@ -838,16 +853,19 @@ JitRuntime::generateVMWrapper(JSContext*
     Linker linker(masm);
     JitCode* wrapper = linker.newCode<NoGC>(cx, OTHER_CODE);
     if (!wrapper)
         return nullptr;
 
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(wrapper, "VMWrapper");
 #endif
+#ifdef MOZ_VTUNE
+    vtune::MarkStub(wrapper, "VMWrapper");
+#endif
 
     // linker.newCode may trigger a GC and sweep functionWrappers_ so we have to
     // use relookupOrAdd instead of add.
     if (!functionWrappers_->relookupOrAdd(p, &f, wrapper))
         return nullptr;
 
     return wrapper;
 }
@@ -879,16 +897,19 @@ JitRuntime::generatePreBarrier(JSContext
     masm.ret();
 
     Linker linker(masm);
     JitCode* code = linker.newCode<NoGC>(cx, OTHER_CODE);
 
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "PreBarrier");
 #endif
+#ifdef MOZ_VTUNE
+    vtune::MarkStub(code, "PreBarrier");
+#endif
 
     return code;
 }
 
 typedef bool (*HandleDebugTrapFn)(JSContext*, BaselineFrame*, uint8_t*, bool*);
 static const VMFunction HandleDebugTrapInfo =
     FunctionInfo<HandleDebugTrapFn>(HandleDebugTrap, "HandleDebugTrap");
 
@@ -955,16 +976,19 @@ JitRuntime::generateDebugTrapHandler(JSC
     masm.ret();
 
     Linker linker(masm);
     JitCode* codeDbg = linker.newCode<NoGC>(cx, OTHER_CODE);
 
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(codeDbg, "DebugTrapHandler");
 #endif
+#ifdef MOZ_VTUNE
+    vtune::MarkStub(codeDbg, "DebugTrapHandler");
+#endif
 
     return codeDbg;
 }
 
 JitCode*
 JitRuntime::generateExceptionTailStub(JSContext* cx, void* handler)
 {
     MacroAssembler masm;
@@ -972,16 +996,19 @@ JitRuntime::generateExceptionTailStub(JS
     masm.handleFailureWithHandlerTail(handler);
 
     Linker linker(masm);
     JitCode* code = linker.newCode<NoGC>(cx, OTHER_CODE);
 
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "ExceptionTailStub");
 #endif
+#ifdef MOZ_VTUNE
+    vtune::MarkStub(code, "ExceptionTailStub");
+#endif
 
     return code;
 }
 
 JitCode*
 JitRuntime::generateBailoutTailStub(JSContext* cx)
 {
     MacroAssembler masm;
@@ -989,16 +1016,19 @@ JitRuntime::generateBailoutTailStub(JSCo
     masm.generateBailoutTail(edx, ecx);
 
     Linker linker(masm);
     JitCode* code = linker.newCode<NoGC>(cx, OTHER_CODE);
 
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "BailoutTailStub");
 #endif
+#ifdef MOZ_VTUNE
+    vtune::MarkStub(code, "BailoutTailStub");
+#endif
 
     return code;
 }
 
 JitCode*
 JitRuntime::generateProfilerExitFrameTailStub(JSContext* cx)
 {
     MacroAssembler masm;
@@ -1326,11 +1356,14 @@ JitRuntime::generateProfilerExitFrameTai
     }
 
     Linker linker(masm);
     JitCode* code = linker.newCode<NoGC>(cx, OTHER_CODE);
 
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "ProfilerExitFrameStub");
 #endif
+#ifdef MOZ_VTUNE
+    vtune::MarkStub(code, "ProfilerExitFrameStub");
+#endif
 
     return code;
 }
--- a/js/src/jsapi-tests/testHashTable.cpp
+++ b/js/src/jsapi-tests/testHashTable.cpp
@@ -373,17 +373,17 @@ LookupWithDefaultUntilResize() {
 
     return true;
 }
 
 BEGIN_TEST(testHashMapLookupWithDefaultOOM)
 {
     uint32_t timeToFail;
     for (timeToFail = 1; timeToFail < 1000; timeToFail++) {
-        js::oom::SimulateOOMAfter(timeToFail, js::oom::THREAD_TYPE_MAIN, false);
+        js::oom::SimulateOOMAfter(timeToFail, js::oom::THREAD_TYPE_COOPERATING, false);
         LookupWithDefaultUntilResize();
     }
 
     js::oom::ResetSimulatedOOM();
     return true;
 }
 
 END_TEST(testHashMapLookupWithDefaultOOM)
--- a/js/src/jsapi-tests/testOOM.cpp
+++ b/js/src/jsapi-tests/testOOM.cpp
@@ -31,17 +31,17 @@ END_TEST(testOOM)
 #ifdef DEBUG  // js::oom functions are only available in debug builds.
 
 const uint32_t maxAllocsPerTest = 100;
 
 #define START_OOM_TEST(name)                                                  \
     testName = name;                                                          \
     printf("Test %s: started\n", testName);                                   \
     for (oomAfter = 1; oomAfter < maxAllocsPerTest; ++oomAfter) {             \
-    js::oom::SimulateOOMAfter(oomAfter, js::oom::THREAD_TYPE_MAIN, true)
+    js::oom::SimulateOOMAfter(oomAfter, js::oom::THREAD_TYPE_COOPERATING, true)
 
 #define OOM_TEST_FINISHED                                                     \
     {                                                                         \
         printf("Test %s: finished with %" PRIu64 " allocations\n",            \
                testName, oomAfter - 1);                                       \
         break;                                                                \
     }
 
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -458,28 +458,24 @@ JS::isGCEnabled()
 {
     return !TlsContext.get()->suppressGC;
 }
 #else
 JS_FRIEND_API(bool) JS::isGCEnabled() { return true; }
 #endif
 
 JS_PUBLIC_API(JSContext*)
-JS_NewContext(uint32_t maxbytes, uint32_t maxNurseryBytes, JSContext* parentContext)
+JS_NewContext(uint32_t maxbytes, uint32_t maxNurseryBytes, JSRuntime* parentRuntime)
 {
     MOZ_ASSERT(JS::detail::libraryInitState == JS::detail::InitState::Running,
                "must call JS_Init prior to creating any JSContexts");
 
     // Make sure that all parent runtimes are the topmost parent.
-    JSRuntime* parentRuntime = nullptr;
-    if (parentContext) {
-        parentRuntime = parentContext->runtime();
-        while (parentRuntime && parentRuntime->parentRuntime)
-            parentRuntime = parentRuntime->parentRuntime;
-    }
+    while (parentRuntime && parentRuntime->parentRuntime)
+        parentRuntime = parentRuntime->parentRuntime;
 
     return NewContext(maxbytes, maxNurseryBytes, parentRuntime);
 }
 
 JS_PUBLIC_API(void)
 JS_DestroyContext(JSContext* cx)
 {
     DestroyContext(cx);
@@ -541,20 +537,20 @@ JS_BeginRequest(JSContext* cx)
 JS_PUBLIC_API(void)
 JS_EndRequest(JSContext* cx)
 {
     MOZ_ASSERT(cx->outstandingRequests != 0);
     cx->outstandingRequests--;
     StopRequest(cx);
 }
 
-JS_PUBLIC_API(JSContext*)
-JS_GetParentContext(JSContext* cx)
-{
-    return cx->runtime()->parentRuntime ? cx->runtime()->parentRuntime->unsafeContextFromAnyThread() : cx;
+JS_PUBLIC_API(JSRuntime*)
+JS_GetParentRuntime(JSContext* cx)
+{
+    return cx->runtime()->parentRuntime ? cx->runtime()->parentRuntime : cx->runtime();
 }
 
 JS_PUBLIC_API(JSVersion)
 JS_GetVersion(JSContext* cx)
 {
     return VersionNumber(cx->findVersion());
 }
 
@@ -1738,26 +1734,52 @@ JS_GetConstructor(JSContext* cx, HandleO
 
 bool
 JS::CompartmentBehaviors::extraWarnings(JSContext* cx) const
 {
     return extraWarningsOverride_.get(cx->options().extraWarnings());
 }
 
 JS::CompartmentCreationOptions&
-JS::CompartmentCreationOptions::setZone(ZoneSpecifier spec)
-{
-    zone_.spec = spec;
+JS::CompartmentCreationOptions::setSystemZone()
+{
+    zoneSpec_ = JS::SystemZone;
+    zonePointer_ = nullptr;
+    return *this;
+}
+
+JS::CompartmentCreationOptions&
+JS::CompartmentCreationOptions::setExistingZone(JSObject* obj)
+{
+    zoneSpec_ = JS::ExistingZone;
+    zonePointer_ = obj->zone();
     return *this;
 }
 
 JS::CompartmentCreationOptions&
-JS::CompartmentCreationOptions::setSameZoneAs(JSObject* obj)
-{
-    zone_.pointer = static_cast<void*>(obj->zone());
+JS::CompartmentCreationOptions::setNewZoneInNewZoneGroup()
+{
+    zoneSpec_ = JS::NewZoneInNewZoneGroup;
+    zonePointer_ = nullptr;
+    return *this;
+}
+
+JS::CompartmentCreationOptions&
+JS::CompartmentCreationOptions::setNewZoneInSystemZoneGroup()
+{
+    zoneSpec_ = JS::NewZoneInSystemZoneGroup;
+    zonePointer_ = nullptr;
+    return *this;
+}
+
+JS::CompartmentCreationOptions&
+JS::CompartmentCreationOptions::setNewZoneInExistingZoneGroup(JSObject* obj)
+{
+    zoneSpec_ = JS::NewZoneInExistingZoneGroup;
+    zonePointer_ = obj->zone()->group();
     return *this;
 }
 
 const JS::CompartmentCreationOptions&
 JS::CompartmentCreationOptionsRef(JSCompartment* compartment)
 {
     return compartment->creationOptions();
 }
@@ -4081,23 +4103,23 @@ JS_PUBLIC_API(bool)
 JS::CanCompileOffThread(JSContext* cx, const ReadOnlyCompileOptions& options, size_t length)
 {
     static const size_t TINY_LENGTH = 5 * 1000;
     static const size_t HUGE_LENGTH = 100 * 1000;
 
     // These are heuristics which the caller may choose to ignore (e.g., for
     // testing purposes).
     if (!options.forceAsync) {
-        // Compiling off the main thread inolves creating a new Zone and other
+        // Compiling off the active thread inolves creating a new Zone and other
         // significant overheads.  Don't bother if the script is tiny.
         if (length < TINY_LENGTH)
             return false;
 
         // If the parsing task would have to wait for GC to complete, it'll probably
-        // be faster to just start it synchronously on the main thread unless the
+        // be faster to just start it synchronously on the active thread unless the
         // script is huge.
         if (OffThreadParsingMustWaitForGC(cx->runtime()) && length < HUGE_LENGTH)
             return false;
     }
 
     return cx->runtime()->canUseParallelParsing() && CanUseExtraThreads();
 }
 
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -976,29 +976,29 @@ JS_IsBuiltinFunctionConstructor(JSFuncti
  * be created, in a single-threaded fashion.  Otherwise the behavior of the
  * library is undefined.
  * See: http://developer.mozilla.org/en/docs/Category:JSAPI_Reference
  */
 
 extern JS_PUBLIC_API(JSContext*)
 JS_NewContext(uint32_t maxbytes,
               uint32_t maxNurseryBytes = JS::DefaultNurseryBytes,
-              JSContext* parentContext = nullptr);
+              JSRuntime* parentRuntime = nullptr);
 
 extern JS_PUBLIC_API(void)
 JS_DestroyContext(JSContext* cx);
 
 JS_PUBLIC_API(void*)
 JS_GetContextPrivate(JSContext* cx);
 
 JS_PUBLIC_API(void)
 JS_SetContextPrivate(JSContext* cx, void* data);
 
-extern JS_PUBLIC_API(JSContext*)
-JS_GetParentContext(JSContext* cx);
+extern JS_PUBLIC_API(JSRuntime*)
+JS_GetParentRuntime(JSContext* cx);
 
 extern JS_PUBLIC_API(void)
 JS_BeginRequest(JSContext* cx);
 
 extern JS_PUBLIC_API(void)
 JS_EndRequest(JSContext* cx);
 
 extern JS_PUBLIC_API(void)
@@ -1705,16 +1705,19 @@ JS_UpdateWeakPointerAfterGCUnbarriered(J
 
 typedef enum JSGCParamKey {
     /** Maximum nominal heap before last ditch GC. */
     JSGC_MAX_BYTES          = 0,
 
     /** Number of JS_malloc bytes before last ditch GC. */
     JSGC_MAX_MALLOC_BYTES   = 1,
 
+    /** Maximum size of the generational GC nurseries. */
+    JSGC_MAX_NURSERY_BYTES  = 2,
+
     /** Amount of bytes allocated by the GC. */
     JSGC_BYTES = 3,
 
     /** Number of times GC has been invoked. Includes both major and minor GC. */
     JSGC_NUMBER = 4,
 
     /** Select GC mode. */
     JSGC_MODE = 6,
@@ -2173,45 +2176,59 @@ extern JS_PUBLIC_API(void*)
 JS_GetInstancePrivate(JSContext* cx, JS::Handle<JSObject*> obj, const JSClass* clasp,
                       JS::CallArgs* args);
 
 extern JS_PUBLIC_API(JSObject*)
 JS_GetConstructor(JSContext* cx, JS::Handle<JSObject*> proto);
 
 namespace JS {
 
+// Specification for which zone a newly created compartment should use.
 enum ZoneSpecifier {
-    FreshZone = 0,
-    SystemZone = 1
+    // Use the single runtime wide system zone. The meaning of this zone is
+    // left to the embedder.
+    SystemZone,
+
+    // Use a particular existing zone.
+    ExistingZone,
+
+    // Create a new zone with its own new zone group.
+    NewZoneInNewZoneGroup,
+
+    // Create a new zone in the same zone group as the system zone.
+    NewZoneInSystemZoneGroup,
+
+    // Create a new zone in the same zone group as another existing zone.
+    NewZoneInExistingZoneGroup
 };
 
 /**
  * CompartmentCreationOptions specifies options relevant to creating a new
  * compartment, that are either immutable characteristics of that compartment
  * or that are discarded after the compartment has been created.
  *
  * Access to these options on an existing compartment is read-only: if you
  * need particular selections, make them before you create the compartment.
  */
 class JS_PUBLIC_API(CompartmentCreationOptions)
 {
   public:
     CompartmentCreationOptions()
       : addonId_(nullptr),
         traceGlobal_(nullptr),
+        zoneSpec_(NewZoneInSystemZoneGroup),
+        zonePointer_(nullptr),
         invisibleToDebugger_(false),
         mergeable_(false),
         preserveJitCode_(false),
         cloneSingletons_(false),
         experimentalNumberFormatFormatToPartsEnabled_(false),
         sharedMemoryAndAtomics_(false),
         secureContext_(false)
-    {
-        zone_.spec = JS::FreshZone;
-    }
+    {}
 
     // A null add-on ID means that the compartment is not associated with an
     // add-on.
     JSAddonId* addonIdOrNull() const { return addonId_; }
     CompartmentCreationOptions& setAddonId(JSAddonId* id) {
         addonId_ = id;
         return *this;
     }
@@ -2219,23 +2236,25 @@ class JS_PUBLIC_API(CompartmentCreationO
     JSTraceOp getTrace() const {
         return traceGlobal_;
     }
     CompartmentCreationOptions& setTrace(JSTraceOp op) {
         traceGlobal_ = op;
         return *this;
     }
 
-    void* zonePointer() const {
-        MOZ_ASSERT(uintptr_t(zone_.pointer) > uintptr_t(JS::SystemZone));
-        return zone_.pointer;
-    }
-    ZoneSpecifier zoneSpecifier() const { return zone_.spec; }
-    CompartmentCreationOptions& setZone(ZoneSpecifier spec);
-    CompartmentCreationOptions& setSameZoneAs(JSObject* obj);
+    void* zonePointer() const { return zonePointer_; }
+    ZoneSpecifier zoneSpecifier() const { return zoneSpec_; }
+
+    // Set the zone to use for the compartment. See ZoneSpecifier above.
+    CompartmentCreationOptions& setSystemZone();
+    CompartmentCreationOptions& setExistingZone(JSObject* obj);
+    CompartmentCreationOptions& setNewZoneInNewZoneGroup();
+    CompartmentCreationOptions& setNewZoneInSystemZoneGroup();
+    CompartmentCreationOptions& setNewZoneInExistingZoneGroup(JSObject* obj);
 
     // Certain scopes (i.e. XBL compilation scopes) are implementation details
     // of the embedding, and references to them should never leak out to script.
     // This flag causes the this compartment to skip firing onNewGlobalObject
     // and makes addDebuggee a no-op for this global.
     bool invisibleToDebugger() const { return invisibleToDebugger_; }
     CompartmentCreationOptions& setInvisibleToDebugger(bool flag) {
         invisibleToDebugger_ = flag;
@@ -2294,20 +2313,18 @@ class JS_PUBLIC_API(CompartmentCreationO
     CompartmentCreationOptions& setSecureContext(bool flag) {
         secureContext_ = flag;
         return *this;
     }
 
   private:
     JSAddonId* addonId_;
     JSTraceOp traceGlobal_;
-    union {
-        ZoneSpecifier spec;
-        void* pointer; // js::Zone* is not exposed in the API.
-    } zone_;
+    ZoneSpecifier zoneSpec_;
+    void* zonePointer_; // Per zoneSpec_, either a Zone, ZoneGroup, or null.
     bool invisibleToDebugger_;
     bool mergeable_;
     bool preserveJitCode_;
     bool cloneSingletons_;
     bool experimentalNumberFormatFormatToPartsEnabled_;
     bool sharedMemoryAndAtomics_;
     bool secureContext_;
 };
@@ -4136,19 +4153,19 @@ CompileForNonSyntacticScope(JSContext* c
 extern JS_PUBLIC_API(bool)
 CanCompileOffThread(JSContext* cx, const ReadOnlyCompileOptions& options, size_t length);
 
 /*
  * Off thread compilation control flow.
  *
  * After successfully triggering an off thread compile of a script, the
  * callback will eventually be invoked with the specified data and a token
- * for the compilation. The callback will be invoked while off the main thread,
+ * for the compilation. The callback will be invoked while off thread,
  * so must ensure that its operations are thread safe. Afterwards, one of the
- * following functions must be invoked on the main thread:
+ * following functions must be invoked on the runtime's active thread:
  *
  * - FinishOffThreadScript, to get the result script (or nullptr on failure).
  * - CancelOffThreadScript, to free the resources without creating a script.
  *
  * The characters passed in to CompileOffThread must remain live until the
  * callback is invoked, and the resulting script will be rooted until the call
  * to FinishOffThreadScript.
  */
@@ -5978,17 +5995,17 @@ DecodeScript(JSContext* cx, TranscodeBuf
              size_t cursorIndex = 0);
 
 extern JS_PUBLIC_API(TranscodeResult)
 DecodeInterpretedFunction(JSContext* cx, TranscodeBuffer& buffer, JS::MutableHandleFunction funp,
                           size_t cursorIndex = 0);
 
 // Register an encoder on the given script source, such that all functions can
 // be encoded as they are parsed. This strategy is used to avoid blocking the
-// main thread in a non-interruptible way.
+// active thread in a non-interruptible way.
 //
 // The |script| argument of |StartIncrementalEncoding| and
 // |FinishIncrementalEncoding| should be the top-level script returned either as
 // an out-param of any of the |Compile| functions, or the result of
 // |FinishOffThreadScript|.
 //
 // The |buffer| argument should not be used before until
 // |FinishIncrementalEncoding| is called on the same script, and returns
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -171,20 +171,38 @@ js::DestroyContext(JSContext* cx)
 {
     JS_AbortIfWrongThread(cx);
 
     if (cx->outstandingRequests != 0)
         MOZ_CRASH("Attempted to destroy a context while it is in a request.");
 
     cx->checkNoGCRooters();
 
-    js_delete(cx->ionPcScriptCache.ref());
+    // Cancel all off thread Ion compiles before destroying a cooperative
+    // context. Completed Ion compiles may try to interrupt arbitrary
+    // cooperative contexts which they have read off the owner context of a
+    // zone group. See HelperThread::handleIonWorkload.
+    CancelOffThreadIonCompile(cx->runtime());
 
-    cx->runtime()->destroyRuntime();
-    js_delete(cx->runtime());
+    if (cx->runtime()->cooperatingContexts().length() == 1) {
+        // Destroy the runtime along with its last context.
+        cx->runtime()->destroyRuntime();
+        js_delete(cx->runtime());
+    } else {
+        DebugOnly<bool> found = false;
+        for (size_t i = 0; i < cx->runtime()->cooperatingContexts().length(); i++) {
+            CooperatingContext& target = cx->runtime()->cooperatingContexts()[i];
+            if (cx == target.context()) {
+                cx->runtime()->cooperatingContexts().erase(&target);
+                found = true;
+                break;
+            }
+        }
+        MOZ_ASSERT(found);
+    }
 
     js_delete_poison(cx);
 }
 
 void
 JS::RootingContext::checkNoGCRooters() {
 #ifdef DEBUG
     for (auto const& stackRootPtr : stackRoots_)
@@ -1029,16 +1047,18 @@ JSContext::~JSContext()
 #ifdef XP_WIN
     if (threadNative_)
         CloseHandle((HANDLE)threadNative_.ref());
 #endif
 
     /* Free the stuff hanging off of cx. */
     MOZ_ASSERT(!resolvingList);
 
+    js_delete(ionPcScriptCache.ref());
+
     if (dtoaState)
         DestroyDtoaState(dtoaState);
 
     fx.destroyInstance();
     freeOsrTempData();
 
 #ifdef JS_SIMULATOR
     js::jit::Simulator::Destroy(simulator_);
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -444,22 +444,22 @@ struct JSContext : public JS::RootingCon
     js::ThreadLocalData<bool> ionCompiling;
 
     // Whether this thread is actively Ion compiling in a context where a minor
     // GC could happen simultaneously. If this is true, this thread cannot use
     // any pointers into the nursery.
     js::ThreadLocalData<bool> ionCompilingSafeForMinorGC;
 
     // Whether this thread is currently performing GC.  This thread could be the
-    // main thread or a helper thread while the main thread is running the
+    // active thread or a helper thread while the active thread is running the
     // collector.
     js::ThreadLocalData<bool> performingGC;
 
     // Whether this thread is currently sweeping GC things.  This thread could
-    // be the main thread or a helper thread while the main thread is running
+    // be the active thread or a helper thread while the active thread is running
     // the mutator.  This is used to assert that destruction of GCPtr only
     // happens when we are sweeping.
     js::ThreadLocalData<bool> gcSweeping;
 
     // Whether this thread is performing work in the background for a runtime's
     // GCHelperState.
     js::ThreadLocalData<bool> gcHelperStateThread;
 
@@ -511,19 +511,19 @@ struct JSContext : public JS::RootingCon
 
     // Count of AutoDisableGenerationalGC instances on the thread's stack.
     js::ThreadLocalData<unsigned> generationalDisabled;
 
     // Some code cannot tolerate compacting GC so it can be disabled temporarily
     // with AutoDisableCompactingGC which uses this counter.
     js::ThreadLocalData<unsigned> compactingDisabledCount;
 
-    // Count of AutoKeepAtoms instances on the main thread's stack. When any
+    // Count of AutoKeepAtoms instances on the current thread's stack. When any
     // instances exist, atoms in the runtime will not be collected. Threads
-    // off the main thread do not increment this value, but the presence
+    // parsing off the active thread do not increment this value, but the presence
     // of any such threads also inhibits collection of atoms. We don't scan the
     // stacks of exclusive threads, so we need to avoid collecting their
     // objects in another way. The only GC thing pointers they have are to
     // their exclusive compartment (which is not collected) or to the atoms
     // compartment. Therefore, we avoid collecting the atoms compartment when
     // exclusive threads are running.
     js::ThreadLocalData<unsigned> keepAtoms;
 
@@ -774,30 +774,30 @@ struct JSContext : public JS::RootingCon
 
     mozilla::Atomic<uint32_t, mozilla::Relaxed> interrupt_;
 
     enum InterruptMode {
         RequestInterruptUrgent,
         RequestInterruptCanWait
     };
 
-    // Any thread can call requestInterrupt() to request that the main JS thread
+    // Any thread can call requestInterrupt() to request that this thread
     // stop running and call the interrupt callback (allowing the interrupt
-    // callback to halt execution). To stop the main JS thread, requestInterrupt
+    // callback to halt execution). To stop this thread, requestInterrupt
     // sets two fields: interrupt_ (set to true) and jitStackLimit_ (set to
     // UINTPTR_MAX). The JS engine must continually poll one of these fields
     // and call handleInterrupt if either field has the interrupt value. (The
     // point of setting jitStackLimit_ to UINTPTR_MAX is that JIT code already
     // needs to guard on jitStackLimit_ in every function prologue to avoid
     // stack overflow, so we avoid a second branch on interrupt_ by setting
     // jitStackLimit_ to a value that is guaranteed to fail the guard.)
     //
     // Note that the writes to interrupt_ and jitStackLimit_ use a Relaxed
     // Atomic so, while the writes are guaranteed to eventually be visible to
-    // the main thread, it can happen in any order. handleInterrupt calls the
+    // this thread, it can happen in any order. handleInterrupt calls the
     // interrupt callback if either is set, so it really doesn't matter as long
     // as the JS engine is continually polling at least one field. In corner
     // cases, this relaxed ordering could lead to an interrupt handler being
     // called twice in succession after a single requestInterrupt call, but
     // that's fine.
     void requestInterrupt(InterruptMode mode);
     bool handleInterrupt();
 
@@ -1129,19 +1129,19 @@ class MOZ_RAII AutoLockForExclusiveAcces
 {
     JSRuntime* runtime;
 
     void init(JSRuntime* rt) {
         runtime = rt;
         if (runtime->numExclusiveThreads) {
             runtime->exclusiveAccessLock.lock();
         } else {
-            MOZ_ASSERT(!runtime->mainThreadHasExclusiveAccess);
+            MOZ_ASSERT(!runtime->activeThreadHasExclusiveAccess);
 #ifdef DEBUG
-            runtime->mainThreadHasExclusiveAccess = true;
+            runtime->activeThreadHasExclusiveAccess = true;
 #endif
         }
     }
 
   public:
     explicit AutoLockForExclusiveAccess(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
         init(cx->runtime());
@@ -1149,19 +1149,19 @@ class MOZ_RAII AutoLockForExclusiveAcces
     explicit AutoLockForExclusiveAccess(JSRuntime* rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM) {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
         init(rt);
     }
     ~AutoLockForExclusiveAccess() {
         if (runtime->numExclusiveThreads) {
             runtime->exclusiveAccessLock.unlock();
         } else {
-            MOZ_ASSERT(runtime->mainThreadHasExclusiveAccess);
+            MOZ_ASSERT(runtime->activeThreadHasExclusiveAccess);
 #ifdef DEBUG
-            runtime->mainThreadHasExclusiveAccess = false;
+            runtime->activeThreadHasExclusiveAccess = false;
 #endif
         }
     }
 
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 class MOZ_RAII AutoKeepAtoms
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -98,17 +98,17 @@ JSCompartment::JSCompartment(Zone* zone,
                   creationOptions_.invisibleToDebugger());
 }
 
 JSCompartment::~JSCompartment()
 {
     reportTelemetry();
 
     // Write the code coverage information in a file.
-    JSRuntime* rt = runtimeFromMainThread();
+    JSRuntime* rt = runtimeFromActiveCooperatingThread();
     if (rt->lcovOutput().isEnabled())
         rt->lcovOutput().writeLCovResult(lcovOutput);
 
     js_delete(jitCompartment_);
     js_delete(watchpointMap);
     js_delete(scriptCountsMap);
     js_delete(debugScriptMap);
     js_delete(debugEnvs);
@@ -1119,17 +1119,17 @@ JSCompartment::ensureDelazifyScriptsForD
 void
 JSCompartment::updateDebuggerObservesFlag(unsigned flag)
 {
     MOZ_ASSERT(isDebuggee());
     MOZ_ASSERT(flag == DebuggerObservesAllExecution ||
                flag == DebuggerObservesCoverage ||
                flag == DebuggerObservesAsmJS);
 
-    GlobalObject* global = zone()->runtimeFromMainThread()->gc.isForegroundSweeping()
+    GlobalObject* global = zone()->runtimeFromActiveCooperatingThread()->gc.isForegroundSweeping()
                            ? unsafeUnbarrieredMaybeGlobal()
                            : maybeGlobal();
     const GlobalObject::DebuggerVector* v = global->getDebuggers();
     for (auto p = v->begin(); p != v->end(); p++) {
         Debugger* dbg = *p;
         if (flag == DebuggerObservesAllExecution ? dbg->observesAllExecution() :
             flag == DebuggerObservesCoverage ? dbg->observesCoverage() :
             dbg->observesAsmJS())
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -390,31 +390,27 @@ struct JSCompartment
 
     JS::Zone* zone() { return zone_; }
     const JS::Zone* zone() const { return zone_; }
 
     const JS::CompartmentCreationOptions& creationOptions() const { return creationOptions_; }
     JS::CompartmentBehaviors& behaviors() { return behaviors_; }
     const JS::CompartmentBehaviors& behaviors() const { return behaviors_; }
 
-    JSRuntime* runtimeFromMainThread() const {
+    JSRuntime* runtimeFromActiveCooperatingThread() const {
         MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime_));
         return runtime_;
     }
 
     // Note: Unrestricted access to the zone's runtime from an arbitrary
     // thread can easily lead to races. Use this method very carefully.
     JSRuntime* runtimeFromAnyThread() const {
         return runtime_;
     }
 
-    JSContext* contextFromMainThread() const {
-        return runtime_->contextFromMainThread();
-    }
-
     /*
      * Nb: global_ might be nullptr, if (a) it's the atoms compartment, or
      * (b) the compartment's global has been collected.  The latter can happen
      * if e.g. a string in a compartment is rooted but no object is, and thus
      * the global isn't rooted, and thus the global can be finalized while the
      * compartment lives on.
      *
      * In contrast, JSObject::global() is infallible because marking a JSObject
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -319,17 +319,17 @@ js::ComputeStackString(JSContext* cx)
         return nullptr;
 
     return str.get();
 }
 
 static void
 exn_finalize(FreeOp* fop, JSObject* obj)
 {
-    MOZ_ASSERT(fop->maybeOffMainThread());
+    MOZ_ASSERT(fop->maybeOnHelperThread());
     if (JSErrorReport* report = obj->as<ErrorObject>().getErrorReport())
         fop->free_(report);
 }
 
 JSErrorReport*
 js::ErrorFromException(JSContext* cx, HandleObject objArg)
 {
     // It's ok to UncheckedUnwrap here, since all we do is get the
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -164,17 +164,17 @@ JS_FRIEND_API(void)
 JS_SetCompartmentPrincipals(JSCompartment* compartment, JSPrincipals* principals)
 {
     // Short circuit if there's no change.
     if (principals == compartment->principals())
         return;
 
     // Any compartment with the trusted principals -- and there can be
     // multiple -- is a system compartment.
-    const JSPrincipals* trusted = compartment->runtimeFromMainThread()->trustedPrincipals();
+    const JSPrincipals* trusted = compartment->runtimeFromActiveCooperatingThread()->trustedPrincipals();
     bool isSystem = principals && principals == trusted;
 
     // Clear out the old principals, if any.
     if (compartment->principals()) {
         JS_DropPrincipals(TlsContext.get(), compartment->principals());
         compartment->setPrincipals(nullptr);
         // We'd like to assert that our new principals is always same-origin
         // with the old one, but JSPrincipals doesn't give us a way to do that.
@@ -385,17 +385,17 @@ js::AssertSameCompartment(JSObject* objA
 }
 #endif
 
 JS_FRIEND_API(void)
 js::NotifyAnimationActivity(JSObject* obj)
 {
     int64_t timeNow = PRMJ_Now();
     obj->compartment()->lastAnimationTime = timeNow;
-    obj->runtimeFromMainThread()->lastAnimationTime = timeNow;
+    obj->runtimeFromActiveCooperatingThread()->lastAnimationTime = timeNow;
 }
 
 JS_FRIEND_API(uint32_t)
 js::GetObjectSlotSpan(JSObject* obj)
 {
     return obj->as<NativeObject>().slotSpan();
 }
 
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -613,17 +613,17 @@ js::XDRInterpretedFunction(XDRState<mode
     if ((firstword & HasAtom) && !XDRAtom(xdr, &atom))
         return false;
     if (!xdr->codeUint32(&flagsword))
         return false;
 
     if (mode == XDR_DECODE) {
         RootedObject proto(cx);
         if (firstword & IsStarGenerator) {
-            // If we are off the main thread, the generator meta-objects have
+            // If we are off thread, the generator meta-objects have
             // already been created by js::StartOffThreadParseTask, so
             // JSContext* will not be necessary.
             JSContext* context = cx->helperThread() ? nullptr : cx;
             proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(context, cx->global());
             if (!proto)
                 return false;
         }
 
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -319,17 +319,17 @@ FOR_EACH_ALLOCKIND(EXPAND_THINGS_PER_ARE
 
 struct js::gc::FinalizePhase
 {
     gcstats::Phase statsPhase;
     AllocKinds kinds;
 };
 
 /*
- * Finalization order for GC things swept incrementally on the main thrad.
+ * Finalization order for GC things swept incrementally on the active thread.
  */
 static const FinalizePhase IncrementalFinalizePhases[] = {
     {
         gcstats::PHASE_SWEEP_STRING, {
             AllocKind::EXTERNAL_STRING
         }
     },
     {
@@ -503,22 +503,22 @@ FinalizeTypedArenas(FreeOp* fop,
                     Arena** src,
                     SortedArenaList& dest,
                     AllocKind thingKind,
                     SliceBudget& budget,
                     ArenaLists::KeepArenasEnum keepArenas)
 {
     // When operating in the foreground, take the lock at the top.
     Maybe<AutoLockGC> maybeLock;
-    if (fop->onMainThread())
+    if (fop->onActiveCooperatingThread())
         maybeLock.emplace(fop->runtime());
 
     // During background sweeping free arenas are released later on in
     // sweepBackgroundThings().
-    MOZ_ASSERT_IF(!fop->onMainThread(), keepArenas == ArenaLists::KEEP_ARENAS);
+    MOZ_ASSERT_IF(!fop->onActiveCooperatingThread(), keepArenas == ArenaLists::KEEP_ARENAS);
 
     size_t thingSize = Arena::thingSize(thingKind);
     size_t thingsPerArena = Arena::thingsPerArena(thingKind);
 
     while (Arena* arena = *src) {
         *src = arena->next;
         size_t nmarked = arena->finalize<T>(fop, thingKind, thingSize);
         size_t nfree = thingsPerArena - nmarked;
@@ -805,16 +805,17 @@ GCRuntime::releaseArena(Arena* arena, co
     if (isBackgroundSweeping())
         arena->zone->threshold.updateForRemovedArena(tunables);
     return arena->chunk()->releaseArena(rt, arena, lock);
 }
 
 GCRuntime::GCRuntime(JSRuntime* rt) :
     rt(rt),
     systemZone(nullptr),
+    systemZoneGroup(nullptr),
     atomsZone(nullptr),
     stats_(rt),
     marker(rt),
     usage(nullptr),
     mMemProfiler(rt),
     maxMallocBytes(0),
     nextCellUniqueId_(LargestTaggedNullCellPointer + 1), // Ensure disjoint from null tagged pointers.
     numArenasFreeCommitted(0),
@@ -1039,16 +1040,17 @@ GCRuntime::init(uint32_t maxbytes, uint3
     {
         AutoLockGC lock(rt);
 
         /*
          * Separate gcMaxMallocBytes from gcMaxBytes but initialize to maxbytes
          * for default backward API compatibility.
          */
         MOZ_ALWAYS_TRUE(tunables.setParameter(JSGC_MAX_BYTES, maxbytes, lock));
+        MOZ_ALWAYS_TRUE(tunables.setParameter(JSGC_MAX_NURSERY_BYTES, maxNurseryBytes, lock));
         setMaxMallocBytes(maxbytes);
 
         const char* size = getenv("JSGC_MARK_STACK_LIMIT");
         if (size)
             setMarkStackLimit(atoi(size), lock);
 
         jitReleaseNumber = majorGCNumber + JIT_SCRIPT_RELEASE_TYPES_PERIOD;
     }
@@ -1160,16 +1162,19 @@ GCSchedulingTunables::setParameter(JSGCP
 {
     // Limit heap growth factor to one hundred times size of current heap.
     const double MaxHeapGrowthFactor = 100;
 
     switch(key) {
       case JSGC_MAX_BYTES:
         gcMaxBytes_ = value;
         break;
+      case JSGC_MAX_NURSERY_BYTES:
+        gcMaxNurseryBytes_ = value;
+        break;
       case JSGC_HIGH_FREQUENCY_TIME_LIMIT:
         highFrequencyThresholdUsec_ = value * PRMJ_USEC_PER_MSEC;
         break;
       case JSGC_HIGH_FREQUENCY_LOW_LIMIT: {
         uint64_t newLimit = (uint64_t)value * 1024 * 1024;
         if (newLimit == UINT64_MAX)
             return false;
         highFrequencyLowLimitBytes_ = newLimit;
@@ -1994,17 +1999,17 @@ ShouldRelocateZone(size_t arenaCount, si
 
     return (relocCount * 100.0) / arenaCount >= MIN_ZONE_RECLAIM_PERCENT;
 }
 
 bool
 ArenaLists::relocateArenas(Zone* zone, Arena*& relocatedListOut, JS::gcreason::Reason reason,
                            SliceBudget& sliceBudget, gcstats::Statistics& stats)
 {
-    // This is only called from the main thread while we are doing a GC, so
+    // This is only called from the active thread while we are doing a GC, so
     // there is no need to lock.
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime_));
     MOZ_ASSERT(runtime_->gc.isHeapCompacting());
     MOZ_ASSERT(!runtime_->gc.isBackgroundSweeping());
 
     // Clear all the free lists.
     purge();
 
@@ -2123,17 +2128,17 @@ MovingTracer::onScopeEdge(Scope** scopep
     Scope* scope = *scopep;
     if (scope->runtimeFromAnyThread() == runtime() && IsForwarded(scope))
         *scopep = Forwarded(scope);
 }
 
 void
 Zone::prepareForCompacting()
 {
-    FreeOp* fop = runtimeFromMainThread()->defaultFreeOp();
+    FreeOp* fop = runtimeFromActiveCooperatingThread()->defaultFreeOp();
     discardJitCode(fop);
 }
 
 void
 GCRuntime::sweepTypesAfterCompacting(Zone* zone)
 {
     FreeOp* fop = rt->defaultFreeOp();
     zone->beginSweepTypes(fop, rt->gc.releaseObservedTypes && !zone->isPreservingCode());
@@ -2405,17 +2410,17 @@ GCRuntime::updateCellPointers(MovingTrac
 
         for (size_t i = 0; i < bgTaskCount && !bgArenas.done(); i++) {
             bgTasks[i].emplace(rt, &bgArenas, lock);
             startTask(*bgTasks[i], gcstats::PHASE_COMPACT_UPDATE_CELLS, lock);
             tasksStarted = i;
         }
     }
 
-    fgTask->runFromMainThread(rt);
+    fgTask->runFromActiveCooperatingThread(rt);
 
     {
         AutoLockHelperThreadState lock;
 
         for (size_t i = 0; i < tasksStarted; i++)
             joinTask(*bgTasks[i], gcstats::PHASE_COMPACT_UPDATE_CELLS, lock);
     }
 }
@@ -2971,17 +2976,17 @@ Nursery::requestMinorGC(JS::gcreason::Re
     // See comment in requestMajorGC.
     TlsContext.get()->requestInterrupt(JSContext::RequestInterruptCanWait);
 }
 
 bool
 GCRuntime::triggerGC(JS::gcreason::Reason reason)
 {
     /*
-     * Don't trigger GCs if this is being called off the main thread from
+     * Don't trigger GCs if this is being called off the active thread from
      * onTooMuchMalloc().
      */
     if (!CurrentThreadCanAccessRuntime(rt))
         return false;
 
     /* GC is already running. */
     if (JS::CurrentThreadIsHeapCollecting())
         return false;
@@ -3112,33 +3117,33 @@ GCRuntime::startDecommit()
     {
         AutoLockGC lock(rt);
 
         // Verify that all entries in the empty chunks pool are already decommitted.
         for (ChunkPool::Iter chunk(emptyChunks(lock)); !chunk.done(); chunk.next())
             MOZ_ASSERT(!chunk->info.numArenasFreeCommitted);
 
         // Since we release the GC lock while doing the decommit syscall below,
-        // it is dangerous to iterate the available list directly, as the main
+        // it is dangerous to iterate the available list directly, as the active
         // thread could modify it concurrently. Instead, we build and pass an
         // explicit Vector containing the Chunks we want to visit.
         MOZ_ASSERT(availableChunks(lock).verify());
         for (ChunkPool::Iter iter(availableChunks(lock)); !iter.done(); iter.next()) {
             if (!toDecommit.append(iter.get())) {
                 // The OOM handler does a full, immediate decommit.
                 return onOutOfMallocMemory(lock);
             }
         }
     }
     decommitTask.setChunksToScan(toDecommit);
 
     if (sweepOnBackgroundThread && decommitTask.start())
         return;
 
-    decommitTask.runFromMainThread(rt);
+    decommitTask.runFromActiveCooperatingThread(rt);
 }
 
 void
 js::gc::BackgroundDecommitTask::setChunksToScan(ChunkVector &chunks)
 {
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime()));
     MOZ_ASSERT(!isRunning());
     MOZ_ASSERT(toDecommit.ref().empty());
@@ -3193,17 +3198,17 @@ GCRuntime::sweepBackgroundThings(ZoneLis
                     ArenaLists::backgroundFinalize(&fop, arenas, &emptyArenas);
             }
         }
     }
 
     AutoLockGC lock(rt);
 
     // Release swept areans, dropping and reaquiring the lock every so often to
-    // avoid blocking the main thread from allocating chunks.
+    // avoid blocking the active thread from allocating chunks.
     static const size_t LockReleasePeriod = 32;
     size_t releaseCount = 0;
     Arena* next;
     for (Arena* arena = emptyArenas; arena; arena = next) {
         next = arena->next;
         rt->gc.releaseArena(arena, lock);
         releaseCount++;
         if (releaseCount % LockReleasePeriod == 0) {
@@ -3364,17 +3369,17 @@ GCHelperState::waitBackgroundSweepEnd()
         waitForBackgroundThread(lock);
     if (!rt->gc.isIncrementalGCInProgress())
         rt->gc.assertBackgroundSweepingFinished();
 }
 
 void
 GCHelperState::doSweep(AutoLockGC& lock)
 {
-    // The main thread may call queueZonesForBackgroundSweep() while this is
+    // The active thread may call queueZonesForBackgroundSweep() while this is
     // running so we must check there is no more work to do before exiting.
 
     do {
         while (!rt->gc.backgroundSweepZones.ref().isEmpty()) {
             AutoSetThreadIsSweeping threadIsSweeping;
 
             ZoneList zones;
             zones.transferFrom(rt->gc.backgroundSweepZones.ref());
@@ -3447,17 +3452,17 @@ JS::Zone::sweepUniqueIds(js::FreeOp* fop
  * |keepAtleastOne| is false. If some objects remain in the zone so that it
  * cannot be deleted, then we set |keepAtleastOne| to true, which prohibits
  * SweepCompartments from deleting every compartment. Instead, it preserves an
  * arbitrary compartment in the zone.
  */
 void
 Zone::sweepCompartments(FreeOp* fop, bool keepAtleastOne, bool destroyingRuntime)
 {
-    JSRuntime* rt = runtimeFromMainThread();
+    JSRuntime* rt = runtimeFromActiveCooperatingThread();
     JSDestroyCompartmentCallback callback = rt->destroyCompartmentCallback;
 
     JSCompartment** read = compartments().begin();
     JSCompartment** end = compartments().end();
     JSCompartment** write = read;
     bool foundOne = false;
     while (read < end) {
         JSCompartment* comp = *read++;
@@ -3545,19 +3550,17 @@ GCRuntime::sweepZoneGroups(FreeOp* fop, 
     ZoneGroup** read = groups.ref().begin();
     ZoneGroup** end = groups.ref().end();
     ZoneGroup** write = read;
 
     while (read < end) {
         ZoneGroup* group = *read++;
         sweepZones(fop, group, destroyingRuntime);
 
-        // For now, the singleton zone group is not destroyed until the runtime
-        // itself is, bug 1323066.
-        if (group->zones().empty() && group != rt->zoneGroupFromMainThread()) {
+        if (group->zones().empty()) {
             MOZ_ASSERT(numActiveZoneIters == 0);
             fop->delete_(group);
         } else {
             *write++ = group;
         }
     }
     groups.ref().shrinkTo(write - groups.ref().begin());
 }
@@ -3751,17 +3754,17 @@ GCRuntime::checkForCompartmentMismatches
 static void
 RelazifyFunctions(Zone* zone, AllocKind kind)
 {
     MOZ_ASSERT(kind == AllocKind::FUNCTION ||
                kind == AllocKind::FUNCTION_EXTENDED);
 
     AutoAssertEmptyNursery empty(TlsContext.get());
 
-    JSRuntime* rt = zone->runtimeFromMainThread();
+    JSRuntime* rt = zone->runtimeFromActiveCooperatingThread();
     for (auto i = zone->cellIter<JSObject>(kind, empty); !i.done(); i.next()) {
         JSFunction* fun = &i->as<JSFunction>();
         if (fun->hasScript())
             fun->maybeRelazify(rt);
     }
 }
 
 static bool
@@ -3837,19 +3840,19 @@ GCRuntime::beginMarkPhase(JS::gcreason::
      * If keepAtoms() is true then either an instance of AutoKeepAtoms is
      * currently on the stack or parsing is currently happening on another
      * thread. In either case we don't have information about which atoms are
      * roots, so we must skip collecting atoms.
      *
      * Note that only affects the first slice of an incremental GC since root
      * marking is completed before we return to the mutator.
      *
-     * Off-main-thread parsing is inhibited after the start of GC which prevents
+     * Off-thread parsing is inhibited after the start of GC which prevents
      * races between creating atoms during parsing and sweeping atoms on the
-     * main thread.
+     * active thread.
      *
      * Otherwise, we always schedule a GC in the atoms zone so that atoms which
      * the other collected zones are using are marked, and we can update the
      * set of atoms in use by the other collected zones at the end of the GC.
      */
     if (!TlsContext.get()->keepAtoms || rt->exclusiveThreadsPresent()) {
         Zone* atomsZone = rt->atomsCompartment(lock)->zone();
         if (atomsZone->isGCScheduled()) {
@@ -4469,17 +4472,17 @@ JSCompartment::findOutgoingEdges(ZoneCom
 
 void
 Zone::findOutgoingEdges(ZoneComponentFinder& finder)
 {
     /*
      * Any compartment may have a pointer to an atom in the atoms
      * compartment, and these aren't in the cross compartment map.
      */
-    JSRuntime* rt = runtimeFromMainThread();
+    JSRuntime* rt = runtimeFromActiveCooperatingThread();
     Zone* atomsZone = rt->atomsCompartment(finder.lock)->zone();
     if (atomsZone->isGCMarking())
         finder.addEdgeTo(atomsZone);
 
     for (CompartmentsInZoneIter comp(this); !comp.done(); comp.next())
         comp->findOutgoingEdges(finder);
 
     for (ZoneSet::Range r = gcZoneGroupEdges().all(); !r.empty(); r.popFront()) {
@@ -4963,48 +4966,48 @@ SweepMiscTask::run()
 }
 
 void
 GCRuntime::startTask(GCParallelTask& task, gcstats::Phase phase, AutoLockHelperThreadState& locked)
 {
     if (!task.startWithLockHeld(locked)) {
         AutoUnlockHelperThreadState unlock(locked);
         gcstats::AutoPhase ap(stats(), phase);
-        task.runFromMainThread(rt);
+        task.runFromActiveCooperatingThread(rt);
     }
 }
 
 void
 GCRuntime::joinTask(GCParallelTask& task, gcstats::Phase phase, AutoLockHelperThreadState& locked)
 {
     gcstats::AutoPhase ap(stats(), task, phase);
     task.joinWithLockHeld(locked);
 }
 
 using WeakCacheTaskVector = mozilla::Vector<SweepWeakCacheTask, 0, SystemAllocPolicy>;
 
 static void
-SweepWeakCachesFromMainThread(JSRuntime* rt)
+SweepWeakCachesFromActiveCooperatingThread(JSRuntime* rt)
 {
     for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
         for (JS::WeakCache<void*>* cache : zone->weakCaches()) {
             SweepWeakCacheTask task(rt, *cache);
-            task.runFromMainThread(rt);
+            task.runFromActiveCooperatingThread(rt);
         }
     }
 }
 
 static WeakCacheTaskVector
 PrepareWeakCacheTasks(JSRuntime* rt)
 {
     WeakCacheTaskVector out;
     for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
         for (JS::WeakCache<void*>* cache : zone->weakCaches()) {
             if (!out.append(SweepWeakCacheTask(rt, *cache))) {
-                SweepWeakCachesFromMainThread(rt);
+                SweepWeakCachesFromActiveCooperatingThread(rt);
                 return WeakCacheTaskVector();
             }
         }
     }
     return out;
 }
 
 void
@@ -5090,17 +5093,17 @@ GCRuntime::beginSweepingZoneGroup(AutoLo
             startTask(sweepCCWrappersTask, gcstats::PHASE_SWEEP_CC_WRAPPER, helperLock);
             startTask(sweepObjectGroupsTask, gcstats::PHASE_SWEEP_TYPE_OBJECT, helperLock);
             startTask(sweepRegExpsTask, gcstats::PHASE_SWEEP_REGEXP, helperLock);
             startTask(sweepMiscTask, gcstats::PHASE_SWEEP_MISC, helperLock);
             for (auto& task : sweepCacheTasks)
                 startTask(task, gcstats::PHASE_SWEEP_MISC, helperLock);
         }
 
-        // The remainder of the of the tasks run in parallel on the main
+        // The remainder of the of the tasks run in parallel on the active
         // thread until we join, below.
         {
             gcstats::AutoPhase ap(stats(), gcstats::PHASE_SWEEP_MISC);
 
             // Cancel any active or pending off thread compilations.
             js::CancelOffThreadIonCompile(rt, JS::Zone::Sweep);
 
             for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) {
@@ -5148,17 +5151,17 @@ GCRuntime::beginSweepingZoneGroup(AutoLo
 
         {
             gcstats::AutoPhase ap(stats(), gcstats::PHASE_SWEEP_BREAKPOINT);
             for (GCZoneGroupIter zone(rt); !zone.done(); zone.next())
                 zone->sweepUniqueIds(&fop);
         }
     }
 
-    // Rejoin our off-main-thread tasks.
+    // Rejoin our off-thread tasks.
     if (sweepingAtoms) {
         AutoLockHelperThreadState helperLock;
         joinTask(sweepAtomsTask, gcstats::PHASE_SWEEP_ATOMS, helperLock);
     }
 
     {
         gcstats::AutoPhase ap(stats(), gcstats::PHASE_SWEEP_COMPARTMENTS);
         gcstats::AutoSCC scc(stats(), zoneGroupIndex);
@@ -6164,17 +6167,16 @@ GCRuntime::budgetIncrementalGC(bool noni
     return IncrementalResult::Ok;
 }
 
 namespace {
 
 class AutoScheduleZonesForGC
 {
     JSRuntime* rt_;
-    AutoAccessZoneGroups aazg;
 
   public:
     explicit AutoScheduleZonesForGC(JSRuntime* rt) : rt_(rt) {
         for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
             if (rt->gc.gcMode() == JSGC_MODE_GLOBAL)
                 zone->scheduleGC();
 
             /* This is a heuristic to avoid resets. */
@@ -6182,19 +6184,16 @@ class AutoScheduleZonesForGC
                 zone->scheduleGC();
 
             /* This is a heuristic to reduce the total number of collections. */
             if (zone->usage.gcBytes() >=
                 zone->threshold.allocTrigger(rt->gc.schedulingState.inHighFrequencyGCMode()))
             {
                 zone->scheduleGC();
             }
-
-            if (zone->isGCScheduled() && !zone->isAtomsZone())
-                aazg.access(zone->group());
         }
     }
 
     ~AutoScheduleZonesForGC() {
         for (ZonesIter zone(rt_, WithAtoms); !zone.done(); zone.next())
             zone->unscheduleGC();
     }
 };
@@ -6258,17 +6257,17 @@ GCRuntime::gcCycle(bool nonincrementalBy
 
     majorGCTriggerReason = JS::gcreason::NO_REASON;
     interFrameGC = true;
 
     number++;
     if (!isIncrementalGCInProgress())
         incMajorGcNumber();
 
-    // It's ok if threads other than the main thread have suppressGC set, as
+    // It's ok if threads other than the active thread have suppressGC set, as
     // they are operating on zones which will not be collected from here.
     MOZ_ASSERT(!TlsContext.get()->suppressGC);
 
     // Assert if this is a GC unsafe region.
     TlsContext.get()->verifyIsSafeToGC();
 
     {
         gcstats::AutoPhase ap(stats(), gcstats::PHASE_WAIT_BACKGROUND_THREAD);
@@ -6282,17 +6281,17 @@ GCRuntime::gcCycle(bool nonincrementalBy
 
         // We must also wait for background allocation to finish so we can
         // avoid taking the GC lock when manipulating the chunks during the GC.
         // The background alloc task can run between slices, so we must wait
         // for it at the start of every slice.
         allocTask.cancel(GCParallelTask::CancelAndWait);
     }
 
-    // We don't allow off-main-thread parsing to start while we're doing an
+    // We don't allow off-thread parsing to start while we're doing an
     // incremental GC.
     MOZ_ASSERT_IF(rt->activeGCInAtomsZone(), !rt->exclusiveThreadsPresent());
 
     auto result = budgetIncrementalGC(nonincrementalByAPI, reason, budget, session.lock);
 
     // If an ongoing incremental GC was reset, we may need to restart.
     if (result == IncrementalResult::Reset) {
         MOZ_ASSERT(!isIncrementalGCInProgress());
@@ -6748,25 +6747,71 @@ js::gc::FinishGC(JSContext* cx)
 
 AutoPrepareForTracing::AutoPrepareForTracing(JSContext* cx, ZoneSelector selector)
 {
     js::gc::FinishGC(cx);
     session_.emplace(cx->runtime());
 }
 
 JSCompartment*
-js::NewCompartment(JSContext* cx, Zone* zone, JSPrincipals* principals,
+js::NewCompartment(JSContext* cx, JSPrincipals* principals,
                    const JS::CompartmentOptions& options)
 {
     JSRuntime* rt = cx->runtime();
     JS_AbortIfWrongThread(cx);
 
+    ScopedJSDeletePtr<ZoneGroup> groupHolder;
     ScopedJSDeletePtr<Zone> zoneHolder;
+
+    Zone* zone = nullptr;
+    ZoneGroup* group = nullptr;
+    JS::ZoneSpecifier zoneSpec = options.creationOptions().zoneSpecifier();
+    switch (zoneSpec) {
+      case JS::SystemZone:
+        // systemZone and possibly systemZoneGroup might be null here, in which
+        // case we'll make a zone/group and set these fields below.
+        zone = rt->gc.systemZone;
+        group = rt->gc.systemZoneGroup;
+        break;
+      case JS::ExistingZone:
+        zone = static_cast<Zone*>(options.creationOptions().zonePointer());
+        MOZ_ASSERT(zone);
+        group = zone->group();
+        break;
+      case JS::NewZoneInNewZoneGroup:
+        break;
+      case JS::NewZoneInSystemZoneGroup:
+        // As above, systemZoneGroup might be null here.
+        group = rt->gc.systemZoneGroup;
+        break;
+      case JS::NewZoneInExistingZoneGroup:
+        group = static_cast<ZoneGroup*>(options.creationOptions().zonePointer());
+        MOZ_ASSERT(group);
+        break;
+    }
+
+    if (!group) {
+        MOZ_ASSERT(!zone);
+        group = cx->new_<ZoneGroup>(rt);
+        if (!group)
+            return nullptr;
+
+        groupHolder.reset(group);
+
+        if (!group->init(rt->gc.tunables.gcMaxNurseryBytes())) {
+            ReportOutOfMemory(cx);
+            return nullptr;
+        }
+
+        if (cx->generationalDisabled)
+            group->nursery().disable();
+    }
+
     if (!zone) {
-        zone = cx->new_<Zone>(cx->runtime(), rt->zoneGroupFromMainThread());
+        zone = cx->new_<Zone>(cx->runtime(), group);
         if (!zone)
             return nullptr;
 
         zoneHolder.reset(zone);
 
         const JSPrincipals* trusted = rt->trustedPrincipals();
         bool isSystem = principals && principals == trusted;
         if (!zone->init(isSystem)) {
@@ -6784,37 +6829,60 @@ js::NewCompartment(JSContext* cx, Zone* 
 
     AutoLockGC lock(rt);
 
     if (!zone->compartments().append(compartment.get())) {
         ReportOutOfMemory(cx);
         return nullptr;
     }
 
-    if (zoneHolder && zoneHolder->group() && !zoneHolder->group()->zones().append(zone)) {
-        ReportOutOfMemory(cx);
-        return nullptr;
+    if (zoneHolder) {
+        if (!group->zones().append(zone)) {
+            ReportOutOfMemory(cx);
+            return nullptr;
+        }
+
+        // Lazily set the runtime's sytem zone.
+        if (zoneSpec == JS::SystemZone) {
+            MOZ_RELEASE_ASSERT(!rt->gc.systemZone);
+            rt->gc.systemZone = zone;
+            zone->isSystem = true;
+        }
+    }
+
+    if (groupHolder) {
+        if (!rt->gc.groups.ref().append(group)) {
+            ReportOutOfMemory(cx);
+            return nullptr;
+        }
+
+        // Lazily set the runtime's system zone group.
+        if (zoneSpec == JS::SystemZone || zoneSpec == JS::NewZoneInSystemZoneGroup) {
+            MOZ_RELEASE_ASSERT(!rt->gc.systemZoneGroup);
+            rt->gc.systemZoneGroup = group;
+        }
     }
 
     zoneHolder.forget();
+    groupHolder.forget();
     return compartment.forget();
 }
 
 void
 gc::MergeCompartments(JSCompartment* source, JSCompartment* target)
 {
     // The source compartment must be specifically flagged as mergable.  This
     // also implies that the compartment is not visible to the debugger.
     MOZ_ASSERT(source->creationOptions_.mergeable());
     MOZ_ASSERT(source->creationOptions_.invisibleToDebugger());
 
     MOZ_ASSERT(source->creationOptions().addonIdOrNull() ==
                target->creationOptions().addonIdOrNull());
 
-    JSContext* cx = source->runtimeFromMainThread()->activeContextFromOwnThread();
+    JSContext* cx = source->runtimeFromActiveCooperatingThread()->activeContextFromOwnThread();
 
     AutoPrepareForTracing prepare(cx, SkipAtoms);
 
     // Cleanup tables and other state in the source compartment that will be
     // meaningless after merging into the target compartment.
 
     source->clearTables();
     source->zone()->clearTables();
@@ -7762,17 +7830,17 @@ js::gc::detail::CellIsMarkedGrayIfKnown(
     // We ignore the gray marking state of cells and return false in two cases:
     //
     // 1) When OOM has caused us to clear the gcGrayBitsValid_ flag.
     //
     // 2) When we are in an incremental GC and examine a cell that is in a zone
     // that is not being collected. Gray targets of CCWs that are marked black
     // by a barrier will eventually be marked black in the next GC slice.
     auto tc = &cell->asTenured();
-    auto rt = tc->runtimeFromMainThread();
+    auto rt = tc->runtimeFromActiveCooperatingThread();
     if (!rt->gc.areGrayBitsValid() ||
         (rt->gc.isIncrementalGCInProgress() && !tc->zone()->wasGCStarted()))
     {
         return false;
     }
 
     return detail::CellIsMarkedGray(tc);
 }
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -165,17 +165,17 @@ IsBackgroundFinalized(AllocKind kind)
 }
 
 static inline bool
 CanBeFinalizedInBackground(AllocKind kind, const Class* clasp)
 {
     MOZ_ASSERT(IsObjectAllocKind(kind));
     /* If the class has no finalizer or a finalizer that is safe to call on
      * a different thread, we change the alloc kind. For example,
-     * AllocKind::OBJECT0 calls the finalizer on the main thread,
+     * AllocKind::OBJECT0 calls the finalizer on the active thread,
      * AllocKind::OBJECT0_BACKGROUND calls the finalizer on the gcHelperThread.
      * IsBackgroundFinalized is called to prevent recursively incrementing
      * the alloc kind; kind may already be a background finalize kind.
      */
     return (!IsBackgroundFinalized(kind) &&
             (!clasp->hasFinalize() || (clasp->flags & JSCLASS_BACKGROUND_FINALIZE)));
 }
 
@@ -850,33 +850,33 @@ NotifyGCNukeWrapper(JSObject* o);
 extern unsigned
 NotifyGCPreSwap(JSObject* a, JSObject* b);
 
 extern void
 NotifyGCPostSwap(JSObject* a, JSObject* b, unsigned preResult);
 
 /*
  * Helper state for use when JS helper threads sweep and allocate GC thing kinds
- * that can be swept and allocated off the main thread.
+ * that can be swept and allocated off thread.
  *
  * In non-threadsafe builds, all actual sweeping and allocation is performed
- * on the main thread, but GCHelperState encapsulates this from clients as
+ * on the active thread, but GCHelperState encapsulates this from clients as
  * much as possible.
  */
 class GCHelperState
 {
     enum State {
         IDLE,
         SWEEPING
     };
 
     // Associated runtime.
     JSRuntime* const rt;
 
-    // Condvar for notifying the main thread when work has finished. This is
+    // Condvar for notifying the active thread when work has finished. This is
     // associated with the runtime's GC lock --- the worker thread state
     // condvars can't be used here due to lock ordering issues.
     js::ConditionVariable done;
 
     // Activity for the helper to do, protected by the GC lock.
     ActiveThreadOrGCTaskData<State> state_;
 
     // Whether work is being performed on some thread.
@@ -981,18 +981,18 @@ class GCParallelTask
     bool start();
     void join();
 
     // If multiple tasks are to be started or joined at once, it is more
     // efficient to take the helper thread lock once and use these methods.
     bool startWithLockHeld(AutoLockHelperThreadState& locked);
     void joinWithLockHeld(AutoLockHelperThreadState& locked);
 
-    // Instead of dispatching to a helper, run the task on the main thread.
-    void runFromMainThread(JSRuntime* rt);
+    // Instead of dispatching to a helper, run the task on the current thread.
+    void runFromActiveCooperatingThread(JSRuntime* rt);
 
     // Dispatch a cancelation request.
     enum CancelMode { CancelNoWait, CancelAndWait};
     void cancel(CancelMode mode = CancelNoWait) {
         cancel_ = true;
         if (mode == CancelAndWait)
             join();
     }
@@ -1052,17 +1052,17 @@ typedef void (*IterateScriptCallback)(JS
 extern void
 IterateScripts(JSContext* cx, JSCompartment* compartment,
                void* data, IterateScriptCallback scriptCallback);
 
 extern void
 FinalizeStringRT(JSRuntime* rt, JSString* str);
 
 JSCompartment*
-NewCompartment(JSContext* cx, JS::Zone* zone, JSPrincipals* principals,
+NewCompartment(JSContext* cx, JSPrincipals* principals,
                const JS::CompartmentOptions& options);
 
 namespace gc {
 
 /*
  * Merge all contents of source into target. This can only be used if source is
  * the only compartment in its zone.
  */
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -474,17 +474,17 @@ class JSObject : public js::gc::Cell
     inline js::GlobalObject& global() const;
 
     // In some rare cases the global object's compartment's global may not be
     // the same global object. For this reason, we need to take extra care when
     // tracing.
     //
     // These cases are:
     //  1) The off-thread parsing task uses a dummy global since it cannot
-    //     share with the actual global being used concurrently on the main
+    //     share with the actual global being used concurrently on the active
     //     thread.
     //  2) A GC may occur when creating the GlobalObject, in which case the
     //     compartment global pointer may not yet be set. In this case there is
     //     nothing interesting to trace in the compartment.
     inline bool isOwnGlobal(JSTracer*) const;
     inline js::GlobalObject* globalForTracing(JSTracer*) const;
 
     /*
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -69,17 +69,17 @@ JSObject::ensureShape(JSContext* cx)
 inline void
 JSObject::finalize(js::FreeOp* fop)
 {
     js::probes::FinalizeObject(this);
 
 #ifdef DEBUG
     MOZ_ASSERT(isTenured());
     if (!IsBackgroundFinalized(asTenured().getAllocKind())) {
-        /* Assert we're on the main thread. */
+        /* Assert we're on the active thread. */
         MOZ_ASSERT(CurrentThreadCanAccessZone(zone()));
     }
 #endif
 
     const js::Class* clasp = getClass();
     js::NativeObject* nobj = nullptr;
     if (clasp->isNative())
         nobj = &as<js::NativeObject>();
@@ -294,17 +294,17 @@ ClassCanHaveFixedData(const Class* clasp
 // returned in place of the pointer passed. If a GC occurs, the returned pointer
 // may be the passed pointer, relocated by GC. If no GC could occur, it's just
 // passed through. We root nothing unless necessary.
 static MOZ_ALWAYS_INLINE MOZ_MUST_USE JSObject*
 SetNewObjectMetadata(JSContext* cx, JSObject* obj)
 {
     MOZ_ASSERT(!cx->compartment()->hasObjectPendingMetadata());
 
-    // The metadata builder is invoked for each object created on the main
+    // The metadata builder is invoked for each object created on the active
     // thread, except when analysis/compilation is active, to avoid recursion.
     if (!cx->helperThread()) {
         if (MOZ_UNLIKELY((size_t)cx->compartment()->hasAllocationMetadataBuilder()) &&
             !cx->zone()->suppressAllocationMetadataBuilder)
         {
             // Don't collect metadata on objects that represent metadata.
             AutoSuppressAllocationMetadataBuilder suppressMetadata(cx);
 
@@ -332,17 +332,17 @@ JSObject::create(JSContext* cx, js::gc::
                   js::gc::GetGCKindSlots(kind, clasp) == shape->numFixedSlots());
 
 #ifdef DEBUG
     static const uint32_t FinalizeMask = JSCLASS_FOREGROUND_FINALIZE | JSCLASS_BACKGROUND_FINALIZE;
     uint32_t flags = clasp->flags;
     uint32_t finalizeFlags = flags & FinalizeMask;
 
     // Classes with a finalizer must specify whether instances will be finalized
-    // on the main thread or in the background, except proxies whose behaviour
+    // on the active thread or in the background, except proxies whose behaviour
     // depends on the target object.
     if (clasp->hasFinalize() && !clasp->isProxy()) {
         MOZ_ASSERT(finalizeFlags == JSCLASS_FOREGROUND_FINALIZE ||
                    finalizeFlags == JSCLASS_BACKGROUND_FINALIZE);
         MOZ_ASSERT((finalizeFlags == JSCLASS_BACKGROUND_FINALIZE) == IsBackgroundFinalized(kind));
     } else {
         MOZ_ASSERT(finalizeFlags == 0);
     }
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -46,16 +46,19 @@
 #include "vm/ArgumentsObject.h"
 #include "vm/Compression.h"
 #include "vm/Debugger.h"
 #include "vm/Opcodes.h"
 #include "vm/SelfHosting.h"
 #include "vm/Shape.h"
 #include "vm/SharedImmutableStringsCache.h"
 #include "vm/Xdr.h"
+#ifdef MOZ_VTUNE
+# include "vtune/VTuneWrapper.h"
+#endif
 
 #include "jsfuninlines.h"
 #include "jsobjinlines.h"
 
 #include "vm/EnvironmentObject-inl.h"
 #include "vm/NativeObject-inl.h"
 #include "vm/SharedImmutableStringsCache-inl.h"
 #include "vm/Stack-inl.h"
@@ -1324,17 +1327,17 @@ ScriptSourceObject::trace(JSTracer* trc,
             sso->setReservedSlot(INTRODUCTION_SCRIPT_SLOT, PrivateValue(script));
         }
     }
 }
 
 void
 ScriptSourceObject::finalize(FreeOp* fop, JSObject* obj)
 {
-    MOZ_ASSERT(fop->onMainThread());
+    MOZ_ASSERT(fop->onActiveCooperatingThread());
     ScriptSourceObject* sso = &obj->as<ScriptSourceObject>();
 
     // If code coverage is enabled, record the filename associated with this
     // source object.
     if (fop->runtime()->lcovOutput().isEnabled())
         sso->compartment()->lcovOutput.collectSourceFile(sso->compartment(), sso);
 
     sso->source()->decref();
@@ -1799,18 +1802,18 @@ ScriptSource::setSourceCopy(JSContext* c
     //    faster than compression which increases latency (this case is handled
     //    in Parser::stringLiteral).
     //
     // Lastly, since the parsing thread will eventually perform a blocking wait
     // on the compression task's thread, require that there are at least 2
     // helper threads:
     //  - If we are on a helper thread, there must be another helper thread to
     //    execute our compression task.
-    //  - If we are on the main thread, there must be at least two helper
-    //    threads since at most one helper thread can be blocking on the main
+    //  - If we are on the active thread, there must be at least two helper
+    //    threads since at most one helper thread can be blocking on the active
     //    thread (see HelperThreadState::canStartParseTask) which would cause a
     //    deadlock if there wasn't a second helper thread that could make
     //    progress on our compression task.
     bool canCompressOffThread =
         HelperThreadState().cpuCount > 1 &&
         HelperThreadState().threadCount >= 2 &&
         CanUseExtraThreads();
     const size_t TINY_SCRIPT = 256;
@@ -2527,16 +2530,20 @@ JSScript::Create(JSContext* cx, const Re
 
     script->version = options.version;
     MOZ_ASSERT(script->getVersion() == options.version);     // assert that no overflow occurred
 
     script->setSourceObject(sourceObject);
     script->sourceStart_ = bufStart;
     script->sourceEnd_ = bufEnd;
 
+#ifdef MOZ_VTUNE
+    script->vtuneMethodId_ = vtune::GenerateUniqueMethodID();
+#endif
+
     return script;
 }
 
 static inline uint8_t*
 AllocScriptData(JS::Zone* zone, size_t size)
 {
     if (!size)
         return nullptr;
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -355,18 +355,18 @@ class UncompressedSourceCache
 
 class ScriptSource
 {
     friend struct SourceCompressionTask;
 
     uint32_t refs;
 
     // Note: while ScriptSources may be compressed off thread, they are only
-    // modified by the main thread, and all members are always safe to access
-    // on the main thread.
+    // modified by the active thread, and all members are always safe to access
+    // on the active thread.
 
     // Indicate which field in the |data| union is active.
 
     struct Missing { };
 
     struct Uncompressed
     {
         SharedImmutableTwoByteString string;
@@ -885,16 +885,24 @@ class JSScript : public js::gc::TenuredC
     uint32_t        nslots_;    /* slots plus maximum stack depth */
 
     uint32_t        bodyScopeIndex_; /* index into the scopes array of the body scope */
 
     /* Range of characters in scriptSource which contains this script's source. */
     uint32_t        sourceStart_;
     uint32_t        sourceEnd_;
 
+#ifdef MOZ_VTUNE
+    // Unique Method ID passed to the VTune profiler, or 0 if unset.
+    // Allows attribution of different jitcode to the same source script.
+    uint32_t        vtuneMethodId_;
+    // Extra padding to maintain JSScript as a multiple of gc::CellSize.
+    uint32_t        __vtune_unused_padding_;
+#endif
+
     // Number of times the script has been called or has had backedges taken.
     // When running in ion, also increased for any inlined scripts. Reset if
     // the script's JIT code is forcibly discarded.
     mozilla::Atomic<uint32_t, mozilla::Relaxed> warmUpCount;
 
     // 16-bit fields.
 
     uint16_t        warmUpResetCount; /* Number of times the |warmUpCount| was
@@ -1551,16 +1559,20 @@ class JSScript : public js::gc::TenuredC
     }
     js::ScriptSourceObject& scriptSourceUnwrap() const;
     js::ScriptSource* scriptSource() const;
     js::ScriptSource* maybeForwardedScriptSource() const;
     bool mutedErrors() const { return scriptSource()->mutedErrors(); }
     const char* filename() const { return scriptSource()->filename(); }
     const char* maybeForwardedFilename() const { return maybeForwardedScriptSource()->filename(); }
 
+#ifdef MOZ_VTUNE
+    uint32_t vtuneMethodID() const { return vtuneMethodId_; }
+#endif
+
   public:
 
     /* Return whether this script was compiled for 'eval' */
     bool isForEval() const {
         MOZ_ASSERT_IF(isCachedEval() || isActiveEval(), bodyScope()->is<js::EvalScope>());
         return isCachedEval() || isActiveEval();
     }
 
--- a/js/src/jsutil.cpp
+++ b/js/src/jsutil.cpp
@@ -65,17 +65,17 @@ SimulateOOMAfter(uint64_t allocations, u
     MOZ_ASSERT(thread > js::oom::THREAD_TYPE_NONE && thread < js::oom::THREAD_TYPE_MAX);
     targetThread = thread;
     maxAllocations = counter + allocations;
     failAlways = always;
 }
 
 void
 ResetSimulatedOOM() {
-    if (targetThread != THREAD_TYPE_NONE && targetThread != THREAD_TYPE_MAIN)
+    if (targetThread != THREAD_TYPE_NONE && targetThread != THREAD_TYPE_COOPERATING)
         HelperThreadState().waitForAllThreads();
     targetThread = THREAD_TYPE_NONE;
     maxAllocations = UINT64_MAX;
     failAlways = false;
 }
 
 
 } // namespace oom
--- a/js/src/jsweakmap.h
+++ b/js/src/jsweakmap.h
@@ -281,17 +281,17 @@ class WeakMap : public HashMap<Key, Valu
         JSWeakmapKeyDelegateOp op = key->getClass()->extWeakmapKeyDelegateOp();
         if (!op)
             return nullptr;
 
         JSObject* obj = op(key);
         if (!obj)
             return nullptr;
 
-        MOZ_ASSERT(obj->runtimeFromMainThread() == zone()->runtimeFromMainThread());
+        MOZ_ASSERT(obj->runtimeFromActiveCooperatingThread() == zone()->runtimeFromActiveCooperatingThread());
         return obj;
     }
 
     JSObject* getDelegate(JSScript* script) const {
         return nullptr;
     }
 
   private:
@@ -299,17 +299,17 @@ class WeakMap : public HashMap<Key, Valu
     void exposeGCThingToActiveJS(JSObject* obj) const { JS::ExposeObjectToActiveJS(obj); }
 
     bool keyNeedsMark(JSObject* key) const {
         JSObject* delegate = getDelegate(key);
         /*
          * Check if the delegate is marked with any color to properly handle
          * gray marking when the key's delegate is black and the map is gray.
          */
-        return delegate && gc::IsMarkedUnbarriered(zone()->runtimeFromMainThread(), &delegate);
+        return delegate && gc::IsMarkedUnbarriered(zone()->runtimeFromActiveCooperatingThread(), &delegate);
     }
 
     bool keyNeedsMark(JSScript* script) const {
         return false;
     }
 
     bool findZoneEdges() override {
         // This is overridden by ObjectValueMap.
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -611,17 +611,19 @@ if CONFIG['JS_HAS_CTYPES']:
     if not CONFIG['MOZ_SYSTEM_FFI']:
         LOCAL_INCLUDES += [
             '!ctypes/libffi/include',
             'ctypes/libffi/src/%s' % CONFIG['FFI_TARGET_DIR'],
         ]
 
 if CONFIG['MOZ_VTUNE']:
     SOURCES += [
-        'vtune/jitprofiling.c'
+        'vtune/ittnotify_static.c',
+        'vtune/jitprofiling.c',
+        'vtune/VTuneWrapper.cpp',
     ]
 
 if CONFIG['HAVE_LINUX_PERF_EVENT_H']:
     SOURCES += [
         'perf/pm_linux.cpp'
     ]
     if CONFIG['LINUX_HEADERS_INCLUDES']:
         SOURCES['perf/pm_linux.cpp'].flags += [CONFIG['LINUX_HEADERS_INCLUDES']]
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -3359,37 +3359,37 @@ EvalInContext(JSContext* cx, unsigned ar
     if (!cx->compartment()->wrap(cx, args.rval()))
         return false;
 
     return true;
 }
 
 struct WorkerInput
 {
-    JSContext* context;
+    JSRuntime* parentRuntime;
     char16_t* chars;
     size_t length;
 
-    WorkerInput(JSContext* context, char16_t* chars, size_t length)
-      : context(context), chars(chars), length(length)
+    WorkerInput(JSRuntime* parentRuntime, char16_t* chars, size_t length)
+      : parentRuntime(parentRuntime), chars(chars), length(length)
     {}
 
     ~WorkerInput() {
         js_free(chars);
     }
 };
 
 static void SetWorkerContextOptions(JSContext* cx);
 
 static void
 WorkerMain(void* arg)
 {
     WorkerInput* input = (WorkerInput*) arg;
 
-    JSContext* cx = JS_NewContext(8L * 1024L * 1024L, 2L * 1024L * 1024L, input->context);
+    JSContext* cx = JS_NewContext(8L * 1024L * 1024L, 2L * 1024L * 1024L, input->parentRuntime);
     if (!cx) {
         js_delete(input);
         return;
     }
 
     UniquePtr<ShellContext> sc = MakeUnique<ShellContext>(cx);
     if (!sc) {
         JS_DestroyContext(cx);
@@ -3508,17 +3508,17 @@ EvalInWorker(JSContext* cx, unsigned arg
     char16_t* chars = (char16_t*) js_malloc(str->length() * sizeof(char16_t));
     if (!chars) {
         ReportOutOfMemory(cx);
         return false;
     }
 
     CopyChars(chars, *str);
 
-    WorkerInput* input = js_new<WorkerInput>(JS_GetParentContext(cx), chars, str->length());
+    WorkerInput* input = js_new<WorkerInput>(JS_GetParentRuntime(cx), chars, str->length());
     if (!input) {
         ReportOutOfMemory(cx);
         return false;
     }
 
     auto thread = js_new<Thread>(Thread::Options().setStackSize(gMaxStackSize + 128 * 1024));
     if (!thread || !thread->init(WorkerMain, input)) {
         ReportOutOfMemory(cx);
@@ -4841,17 +4841,17 @@ NewGlobal(JSContext* cx, unsigned argc, 
         if (!JS_GetProperty(cx, opts, "experimentalNumberFormatFormatToPartsEnabled", &v))
             return false;
         if (v.isBoolean())
             creationOptions.setExperimentalNumberFormatFormatToPartsEnabled(v.toBoolean());
 
         if (!JS_GetProperty(cx, opts, "sameZoneAs", &v))
             return false;
         if (v.isObject())
-            creationOptions.setSameZoneAs(UncheckedUnwrap(&v.toObject()));
+            creationOptions.setExistingZone(UncheckedUnwrap(&v.toObject()));
 
         if (!JS_GetProperty(cx, opts, "disableLazyParsing", &v))
             return false;
         if (v.isBoolean())
             behaviors.setDisableLazyParsing(v.toBoolean());
 
         if (!JS_GetProperty(cx, opts, "principal", &v))
             return false;
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/RegExp/unicode-ignoreCase-word-boundary.js
@@ -0,0 +1,25 @@
+var BUGNUMBER = 1338373;
+var summary = "Word boundary should match U+017F and U+212A in unicode+ignoreCase.";
+
+assertEq(/\b/iu.test('\u017F'), true);
+assertEq(/\b/i.test('\u017F'), false);
+assertEq(/\b/u.test('\u017F'), false);
+assertEq(/\b/.test('\u017F'), false);
+
+assertEq(/\b/iu.test('\u212A'), true);
+assertEq(/\b/i.test('\u212A'), false);
+assertEq(/\b/u.test('\u212A'), false);
+assertEq(/\b/.test('\u212A'), false);
+
+assertEq(/\B/iu.test('\u017F'), false);
+assertEq(/\B/i.test('\u017F'), true);
+assertEq(/\B/u.test('\u017F'), true);
+assertEq(/\B/.test('\u017F'), true);
+
+assertEq(/\B/iu.test('\u212A'), false);
+assertEq(/\B/i.test('\u212A'), true);
+assertEq(/\B/u.test('\u212A'), true);
+assertEq(/\B/.test('\u212A'), true);
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
--- a/js/src/threading/ProtectedData.cpp
+++ b/js/src/threading/ProtectedData.cpp
@@ -12,98 +12,98 @@
 #include "vm/HelperThreads.h"
 
 namespace js {
 
 #ifdef DEBUG
 
 /* static */ mozilla::Atomic<size_t> AutoNoteSingleThreadedRegion::count(0);
 
-template <AllowedBackgroundThread Background>
+template <AllowedHelperThread Helper>
 static inline bool
-OnBackgroundThread()
+OnHelperThread()
 {
-    if (Background == AllowedBackgroundThread::IonCompile || Background == AllowedBackgroundThread::GCTaskOrIonCompile) {
+    if (Helper == AllowedHelperThread::IonCompile || Helper == AllowedHelperThread::GCTaskOrIonCompile) {
         if (CurrentThreadIsIonCompiling())
             return true;
     }
 
-    if (Background == AllowedBackgroundThread::GCTask || Background == AllowedBackgroundThread::GCTaskOrIonCompile) {
+    if (Helper == AllowedHelperThread::GCTask || Helper == AllowedHelperThread::GCTaskOrIonCompile) {
         if (TlsContext.get()->performingGC || TlsContext.get()->runtime()->gc.onBackgroundThread())
             return true;
     }
 
     return false;
 }
 
-template <AllowedBackgroundThread Background>
+template <AllowedHelperThread Helper>
 void
-CheckActiveThread<Background>::check() const
+CheckActiveThread<Helper>::check() const
 {
     // When interrupting a thread on Windows, changes are made to the runtime
     // and active thread's state from another thread while the active thread is
     // suspended. We need a way to mark these accesses as being tantamount to
     // accesses by the active thread. See bug 1323066.
 #ifndef XP_WIN
-    if (OnBackgroundThread<Background>())
+    if (OnHelperThread<Helper>())
         return;
 
     JSContext* cx = TlsContext.get();
     MOZ_ASSERT(cx == cx->runtime()->activeContext());
 #endif // XP_WIN
 }
 
-template class CheckActiveThread<AllowedBackgroundThread::None>;
-template class CheckActiveThread<AllowedBackgroundThread::GCTask>;
-template class CheckActiveThread<AllowedBackgroundThread::IonCompile>;
+template class CheckActiveThread<AllowedHelperThread::None>;
+template class CheckActiveThread<AllowedHelperThread::GCTask>;
+template class CheckActiveThread<AllowedHelperThread::IonCompile>;
 
-template <AllowedBackgroundThread Background>
+template <AllowedHelperThread Helper>
 void
-CheckZoneGroup<Background>::check() const
+CheckZoneGroup<Helper>::check() const
 {
-    if (OnBackgroundThread<Background>())
+    if (OnHelperThread<Helper>())
         return;
 
     if (group) {
         // This check is disabled for now because helper thread parse tasks
-        // access data in the same zone group that the single main thread is
+        // access data in the same zone group that the single active thread is
         // using. This will be fixed soon (bug 1323066).
         //MOZ_ASSERT(group->context && group->context == TlsContext.get());
     } else {
         // |group| will be null for data in the atoms zone. This is protected
         // by the exclusive access lock.
         MOZ_ASSERT(TlsContext.get()->runtime()->currentThreadHasExclusiveAccess());
     }
 }
 
-template class CheckZoneGroup<AllowedBackgroundThread::None>;
-template class CheckZoneGroup<AllowedBackgroundThread::GCTask>;
-template class CheckZoneGroup<AllowedBackgroundThread::IonCompile>;
-template class CheckZoneGroup<AllowedBackgroundThread::GCTaskOrIonCompile>;
+template class CheckZoneGroup<AllowedHelperThread::None>;
+template class CheckZoneGroup<AllowedHelperThread::GCTask>;
+template class CheckZoneGroup<AllowedHelperThread::IonCompile>;
+template class CheckZoneGroup<AllowedHelperThread::GCTaskOrIonCompile>;
 
-template <GlobalLock Lock, AllowedBackgroundThread Background>
+template <GlobalLock Lock, AllowedHelperThread Helper>
 void
-CheckGlobalLock<Lock, Background>::check() const
+CheckGlobalLock<Lock, Helper>::check() const
 {
-    if (OnBackgroundThread<Background>())
+    if (OnHelperThread<Helper>())
         return;
 
     switch (Lock) {
       case GlobalLock::GCLock:
         MOZ_ASSERT(TlsContext.get()->runtime()->gc.currentThreadHasLockedGC());
         break;
       case GlobalLock::ExclusiveAccessLock:
         MOZ_ASSERT(TlsContext.get()->runtime()->currentThreadHasExclusiveAccess());
         break;
       case GlobalLock::HelperThreadLock:
         MOZ_ASSERT(HelperThreadState().isLockedByCurrentThread());
         break;
     }
 }
 
-template class CheckGlobalLock<GlobalLock::GCLock, AllowedBackgroundThread::None>;
-template class CheckGlobalLock<GlobalLock::ExclusiveAccessLock, AllowedBackgroundThread::None>;
-template class CheckGlobalLock<GlobalLock::ExclusiveAccessLock, AllowedBackgroundThread::GCTask>;
-template class CheckGlobalLock<GlobalLock::HelperThreadLock, AllowedBackgroundThread::None>;
+template class CheckGlobalLock<GlobalLock::GCLock, AllowedHelperThread::None>;
+template class CheckGlobalLock<GlobalLock::ExclusiveAccessLock, AllowedHelperThread::None>;
+template class CheckGlobalLock<GlobalLock::ExclusiveAccessLock, AllowedHelperThread::GCTask>;
+template class CheckGlobalLock<GlobalLock::HelperThreadLock, AllowedHelperThread::None>;
 
 #endif // DEBUG
 
 } // namespace js
--- a/js/src/threading/ProtectedData.h
+++ b/js/src/threading/ProtectedData.h
@@ -186,49 +186,49 @@ class CheckThreadLocal
     }
 #endif
 };
 
 // Data which may only be accessed by the thread on which it is created.
 template <typename T>
 using ThreadLocalData = ProtectedDataNoCheckArgs<CheckThreadLocal, T>;
 
-// Enum describing which background threads (GC tasks or Ion compilations) may
+// Enum describing which helper threads (GC tasks or Ion compilations) may
 // access data even though they do not have exclusive access to any zone group.
-enum class AllowedBackgroundThread
+enum class AllowedHelperThread
 {
     None,
     GCTask,
     IonCompile,
     GCTaskOrIonCompile
 };
 
-template <AllowedBackgroundThread Background>
+template <AllowedHelperThread Helper>
 class CheckActiveThread
 {
   public:
     void check() const;
 };
 
 // Data which may only be accessed by the runtime's cooperatively scheduled
 // active thread.
 template <typename T>
 using ActiveThreadData =
-    ProtectedDataNoCheckArgs<CheckActiveThread<AllowedBackgroundThread::None>, T>;
+    ProtectedDataNoCheckArgs<CheckActiveThread<AllowedHelperThread::None>, T>;
 
 // Data which may only be accessed by the runtime's cooperatively scheduled
 // active thread, or by various helper thread tasks.
 template <typename T>
 using ActiveThreadOrGCTaskData =
-    ProtectedDataNoCheckArgs<CheckActiveThread<AllowedBackgroundThread::GCTask>, T>;
+    ProtectedDataNoCheckArgs<CheckActiveThread<AllowedHelperThread::GCTask>, T>;
 template <typename T>
 using ActiveThreadOrIonCompileData =
-    ProtectedDataNoCheckArgs<CheckActiveThread<AllowedBackgroundThread::IonCompile>, T>;
+    ProtectedDataNoCheckArgs<CheckActiveThread<AllowedHelperThread::IonCompile>, T>;
 
-template <AllowedBackgroundThread Background>
+template <AllowedHelperThread Helper>
 class CheckZoneGroup
 {
 #ifdef DEBUG
     ZoneGroup* group;
 
   public:
     explicit CheckZoneGroup(ZoneGroup* group) : group(group) {}
     void check() const;
@@ -237,68 +237,68 @@ class CheckZoneGroup
     explicit CheckZoneGroup(ZoneGroup* group) {}
 #endif
 };
 
 // Data which may only be accessed by threads with exclusive access to the
 // associated zone group.
 template <typename T>
 using ZoneGroupData =
-    ProtectedDataZoneGroupArg<CheckZoneGroup<AllowedBackgroundThread::None>, T>;
+    ProtectedDataZoneGroupArg<CheckZoneGroup<AllowedHelperThread::None>, T>;
 
 // Data which may only be accessed by threads with exclusive access to the
 // associated zone group, or by various helper thread tasks.
 template <typename T>
 using ZoneGroupOrGCTaskData =
-    ProtectedDataZoneGroupArg<CheckZoneGroup<AllowedBackgroundThread::GCTask>, T>;
+    ProtectedDataZoneGroupArg<CheckZoneGroup<AllowedHelperThread::GCTask>, T>;
 template <typename T>
 using ZoneGroupOrIonCompileData =
-    ProtectedDataZoneGroupArg<CheckZoneGroup<AllowedBackgroundThread::IonCompile>, T>;
+    ProtectedDataZoneGroupArg<CheckZoneGroup<AllowedHelperThread::IonCompile>, T>;
 template <typename T>
 using ZoneGroupOrGCTaskOrIonCompileData =
-    ProtectedDataZoneGroupArg<CheckZoneGroup<AllowedBackgroundThread::GCTaskOrIonCompile>, T>;
+    ProtectedDataZoneGroupArg<CheckZoneGroup<AllowedHelperThread::GCTaskOrIonCompile>, T>;
 
 // Runtime wide locks which might protect some data.
 enum class GlobalLock
 {
     GCLock,
     ExclusiveAccessLock,
     HelperThreadLock
 };
 
-template <GlobalLock Lock, AllowedBackgroundThread Background>
+template <GlobalLock Lock, AllowedHelperThread Helper>
 class CheckGlobalLock
 {
 #ifdef DEBUG
   public:
     void check() const;
 #endif
 };
 
 // Data which may only be accessed while holding the GC lock.
 template <typename T>
 using GCLockData =
-    ProtectedDataNoCheckArgs<CheckGlobalLock<GlobalLock::GCLock, AllowedBackgroundThread::None>, T>;
+    ProtectedDataNoCheckArgs<CheckGlobalLock<GlobalLock::GCLock, AllowedHelperThread::None>, T>;
 
 // Data which may only be accessed while holding the exclusive access lock.
 template <typename T>
 using ExclusiveAccessLockData =
-    ProtectedDataNoCheckArgs<CheckGlobalLock<GlobalLock::ExclusiveAccessLock, AllowedBackgroundThread::None>, T>;
+    ProtectedDataNoCheckArgs<CheckGlobalLock<GlobalLock::ExclusiveAccessLock, AllowedHelperThread::None>, T>;
 
 // Data which may only be accessed while holding the exclusive access lock or
 // by GC helper thread tasks (at which point a foreground thread should be
 // holding the exclusive access lock, though we do not check this).
 template <typename T>
 using ExclusiveAccessLockOrGCTaskData =
-    ProtectedDataNoCheckArgs<CheckGlobalLock<GlobalLock::ExclusiveAccessLock, AllowedBackgroundThread::GCTask>, T>;
+    ProtectedDataNoCheckArgs<CheckGlobalLock<GlobalLock::ExclusiveAccessLock, AllowedHelperThread::GCTask>, T>;
 
 // Data which may only be accessed while holding the helper thread lock.
 template <typename T>
 using HelperThreadLockData =
-    ProtectedDataNoCheckArgs<CheckGlobalLock<GlobalLock::HelperThreadLock, AllowedBackgroundThread::None>, T>;
+    ProtectedDataNoCheckArgs<CheckGlobalLock<GlobalLock::HelperThreadLock, AllowedHelperThread::None>, T>;
 
 // Class for protected data that is only written to once. 'const' may sometimes
 // be usable instead of this class, but in cases where the data cannot be set
 // to its final value in its constructor this class is helpful. Protected data
 // checking only occurs when writes are performed, not reads. Steps may need to
 // be taken to ensure that reads do not occur until the written value is fully
 // initialized, as such guarantees are not provided by this class.
 template <typename Check, typename T>
@@ -351,17 +351,17 @@ class ProtectedDataWriteOnce
 // Data that is written once with no requirements for exclusive access when
 // that write occurs.
 template <typename T>
 using WriteOnceData = ProtectedDataWriteOnce<CheckUnprotected, T>;
 
 // Data that is written once, and only while holding the exclusive access lock.
 template <typename T>
 using ExclusiveAccessLockWriteOnceData =
-    ProtectedDataWriteOnce<CheckGlobalLock<GlobalLock::ExclusiveAccessLock, AllowedBackgroundThread::None>, T>;
+    ProtectedDataWriteOnce<CheckGlobalLock<GlobalLock::ExclusiveAccessLock, AllowedHelperThread::None>, T>;
 
 #undef DECLARE_ASSIGNMENT_OPERATOR
 #undef DECLARE_ONE_BOOL_OPERATOR
 #undef DECLARE_BOOL_OPERATORS
 
 } // namespace js
 
 #endif // threading_ProtectedData_h
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -3294,17 +3294,17 @@ Debugger::findZoneEdges(Zone* zone, js::
             finder.addEdgeTo(w);
         }
     }
 }
 
 /* static */ void
 Debugger::finalize(FreeOp* fop, JSObject* obj)
 {
-    MOZ_ASSERT(fop->onMainThread());
+    MOZ_ASSERT(fop->onActiveCooperatingThread());
 
     Debugger* dbg = fromJSObject(obj);
     if (!dbg)
         return;
     fop->delete_(dbg);
 }
 
 const ClassOps Debugger::classOps_ = {
@@ -8271,17 +8271,17 @@ DebuggerFrame_maybeDecrementFrameScriptS
     } else {
         frame.script()->decrementStepModeCount(fop);
     }
 }
 
 static void
 DebuggerFrame_finalize(FreeOp* fop, JSObject* obj)
 {
-    MOZ_ASSERT(fop->maybeOffMainThread());
+    MOZ_ASSERT(fop->maybeOnHelperThread());
     DebuggerFrame_freeScriptFrameIterData(fop, obj);
     OnStepHandler* onStepHandler = obj->as<DebuggerFrame>().onStepHandler();
     if (onStepHandler)
        onStepHandler->drop();
     OnPopHandler* onPopHandler = obj->as<DebuggerFrame>().onPopHandler();
     if (onPopHandler)
        onPopHandler->drop();
 }
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -341,37 +341,20 @@ GlobalObject::createInternal(JSContext* 
 /* static */ GlobalObject*
 GlobalObject::new_(JSContext* cx, const Class* clasp, JSPrincipals* principals,
                    JS::OnNewGlobalHookOption hookOption,
                    const JS::CompartmentOptions& options)
 {
     MOZ_ASSERT(!cx->isExceptionPending());
     MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
 
-    JSRuntime* rt = cx->runtime();
-
-    auto zoneSpecifier = options.creationOptions().zoneSpecifier();
-    Zone* zone;
-    if (zoneSpecifier == JS::SystemZone)
-        zone = rt->gc.systemZone;
-    else if (zoneSpecifier == JS::FreshZone)
-        zone = nullptr;
-    else
-        zone = static_cast<Zone*>(options.creationOptions().zonePointer());
-
-    JSCompartment* compartment = NewCompartment(cx, zone, principals, options);
+    JSCompartment* compartment = NewCompartment(cx, principals, options);
     if (!compartment)
         return nullptr;
 
-    // Lazily create the system zone.
-    if (!rt->gc.systemZone && zoneSpecifier == JS::SystemZone) {
-        rt->gc.systemZone = compartment->zone();
-        rt->gc.systemZone->isSystem = true;
-    }
-
     Rooted<GlobalObject*> global(cx);
     {
         AutoCompartment ac(cx, compartment);
         global = GlobalObject::createInternal(cx, clasp);
         if (!global)
             return nullptr;
 
         if (hookOption == JS::FireOnNewGlobalHook)
@@ -652,17 +635,17 @@ js::DefineToStringTag(JSContext *cx, Han
     RootedId toStringTagId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().toStringTag));
     RootedValue tagString(cx, StringValue(tag));
     return DefineProperty(cx, obj, toStringTagId, tagString, nullptr, nullptr, JSPROP_READONLY);
 }
 
 static void
 GlobalDebuggees_finalize(FreeOp* fop, JSObject* obj)
 {
-    MOZ_ASSERT(fop->maybeOffMainThread());
+    MOZ_ASSERT(fop->maybeOnHelperThread());
     fop->delete_((GlobalObject::DebuggerVector*) obj->as<NativeObject>().getPrivate());
 }
 
 static const ClassOps
 GlobalDebuggees_classOps = {
     nullptr,
     nullptr,
     nullptr,
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -121,18 +121,18 @@ FinishOffThreadIonCompile(jit::IonBuilde
         oomUnsafe.crash("FinishOffThreadIonCompile");
 }
 
 static JSRuntime*
 GetSelectorRuntime(CompilationSelector selector)
 {
     struct Matcher
     {
-        JSRuntime* match(JSScript* script)    { return script->runtimeFromMainThread(); }
-        JSRuntime* match(JSCompartment* comp) { return comp->runtimeFromMainThread(); }
+        JSRuntime* match(JSScript* script)    { return script->runtimeFromActiveCooperatingThread(); }
+        JSRuntime* match(JSCompartment* comp) { return comp->runtimeFromActiveCooperatingThread(); }
         JSRuntime* match(ZonesInState zbs)    { return zbs.runtime; }
         JSRuntime* match(JSRuntime* runtime)  { return runtime; }
         JSRuntime* match(AllCompilations all) { return nullptr; }
     };
 
     return selector.match(Matcher());
 }
 
@@ -262,17 +262,17 @@ js::HasOffThreadIonCompile(JSCompartment
 
     GlobalHelperThreadState::IonBuilderVector& finished = HelperThreadState().ionFinishedList(lock);
     for (size_t i = 0; i < finished.length(); i++) {
         jit::IonBuilder* builder = finished[i];
         if (builder->script()->compartment() == comp)
             return true;
     }
 
-    jit::IonBuilder* builder = comp->runtimeFromMainThread()->ionLazyLinkList().getFirst();
+    jit::IonBuilder* builder = comp->runtimeFromActiveCooperatingThread()->ionLazyLinkList().getFirst();
     while (builder) {
         if (builder->script()->compartment() == comp)
             return true;
         builder = builder->getNext();
     }
 
     return false;
 }
@@ -456,17 +456,17 @@ js::CancelOffThreadParses(JSRuntime* rt)
                     inProgress = true;
             }
             if (!inProgress)
                 break;
         }
         HelperThreadState().wait(lock, GlobalHelperThreadState::CONSUMER);
     }
 
-    // Clean up any parse tasks which haven't been finished by the main thread.
+    // Clean up any parse tasks which haven't been finished by the active thread.
     GlobalHelperThreadState::ParseTaskVector& finished = HelperThreadState().parseFinishedList(lock);
     while (true) {
         bool found = false;
         for (size_t i = 0; i < finished.length(); i++) {
             ParseTask* task = finished[i];
             if (task->runtimeMatches(rt)) {
                 found = true;
                 AutoUnlockHelperThreadState unlock(lock);
@@ -536,29 +536,29 @@ CreateGlobalForOffThreadParse(JSContext*
 
     JS::CompartmentOptions compartmentOptions(currentCompartment->creationOptions(),
                                               currentCompartment->behaviors());
 
     auto& creationOptions = compartmentOptions.creationOptions();
 
     creationOptions.setInvisibleToDebugger(true)
                    .setMergeable(true)
-                   .setZone(JS::FreshZone);
+                   .setNewZoneInSystemZoneGroup();
 
     // Don't falsely inherit the host's global trace hook.
     creationOptions.setTrace(nullptr);
 
     JSObject* global = JS_NewGlobalObject(cx, &parseTaskGlobalClass, nullptr,
                                           JS::FireOnNewGlobalHook, compartmentOptions);
     if (!global)
         return nullptr;
 
     JS_SetCompartmentPrincipals(global->compartment(), currentCompartment->principals());
 
-    // Initialize all classes required for parsing while still on the main
+    // Initialize all classes required for parsing while still on the active
     // thread, for both the target and the new global so that prototype
     // pointers can be changed infallibly after parsing finishes.
     if (!EnsureParserCreatedClasses(cx, kind))
         return nullptr;
     {
         AutoCompartment ac(cx, global);
         if (!EnsureParserCreatedClasses(cx, kind))
             return nullptr;
@@ -1175,17 +1175,17 @@ js::GCParallelTask::joinWithLockHeld(Aut
 void
 js::GCParallelTask::join()
 {
     AutoLockHelperThreadState helperLock;
     joinWithLockHeld(helperLock);
 }
 
 void
-js::GCParallelTask::runFromMainThread(JSRuntime* rt)
+js::GCParallelTask::runFromActiveCooperatingThread(JSRuntime* rt)
 {
     MOZ_ASSERT(state == NotStarted);
     MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(rt));
     mozilla::TimeStamp timeStart = mozilla::TimeStamp::Now();
     run();
     duration_ = mozilla::TimeStamp::Now() - timeStart;
 }
 
@@ -1448,17 +1448,17 @@ HelperThread::destroy()
 
 /* static */
 void
 HelperThread::ThreadMain(void* arg)
 {
     ThisThread::SetName("JS Helper");
 
     //See bug 1104658.
-    //Set the FPU control word to be the same as the main thread's, or math
+    //Set the FPU control word to be the same as the active thread's, or math
     //computations on this thread may use incorrect precision rules during
     //Ion compilation.
     FIX_FPU();
 
     static_cast<HelperThread*>(arg)->threadLoop();
     Mutex::ShutDown();
 }
 
@@ -1483,17 +1483,17 @@ HelperThread::handleWasmWorkload(AutoLoc
         success = HelperThreadState().wasmFinishedList(locked).append(task);
 
     // On failure, note the failure for harvesting by the parent.
     if (!success) {
         HelperThreadState().noteWasmFailure(locked);
         HelperThreadState().setWasmError(locked, Move(error));
     }
 
-    // Notify the main thread in case it's waiting.
+    // Notify the active thread in case it's waiting.
     HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
     currentTask.reset();
 }
 
 void
 HelperThread::handlePromiseTaskWorkload(AutoLockHelperThreadState& locked)
 {
     MOZ_ASSERT(HelperThreadState().canStartPromiseTask(locked));
@@ -1512,17 +1512,17 @@ HelperThread::handlePromiseTaskWorkload(
             // be destroyed on its runtime's thread. Add it to a list of tasks
             // to delete before the next GC.
             AutoEnterOOMUnsafeRegion oomUnsafe;
             if (!task->runtime()->promiseTasksToDestroy.lock()->append(task))
                 oomUnsafe.crash("handlePromiseTaskWorkload");
         }
     }
 
-    // Notify the main thread in case it's waiting.
+    // Notify the active thread in case it's waiting.
     HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
     currentTask.reset();
 }
 
 void
 HelperThread::handleIonWorkload(AutoLockHelperThreadState& locked)
 {
     MOZ_ASSERT(HelperThreadState().canStartIonCompile(locked));
@@ -1560,26 +1560,35 @@ HelperThread::handleIonWorkload(AutoLock
         AutoSetContextRuntime ascr(rt);
         jit::JitContext jctx(jit::CompileRuntime::get(rt),
                              jit::CompileCompartment::get(builder->script()->compartment()),
                              &builder->alloc());
         builder->setBackgroundCodegen(jit::CompileBackEnd(builder));
     }
 
     FinishOffThreadIonCompile(builder, locked);
+
+    // Ping any thread currently operating on the compiled script's zone group
+    // so that the compiled code can be incorporated at the next interrupt
+    // callback. Don't interrupt Ion code for this, as this incorporation can
+    // be delayed indefinitely without affecting performance as long as the
+    // active thread is actually executing Ion code.
+    //
+    // This must happen before the current task is reset. DestroyContext
+    // cancels in progress Ion compilations before destroying its target
+    // context, and after we reset the current task we are no longer considered
+    // to be Ion compiling.
+    JSContext* target = builder->script()->zoneFromAnyThread()->group()->ownerContext().context();
+    if (target)
+        target->requestInterrupt(JSContext::RequestInterruptCanWait);
+
     currentTask.reset();
     pause = false;
 
-    // Ping the main thread so that the compiled code can be incorporated
-    // at the next interrupt callback. Don't interrupt Ion code for this, as
-    // this incorporation can be delayed indefinitely without affecting
-    // performance as long as the main thread is actually executing Ion code.
-    rt->unsafeContextFromAnyThread()->requestInterrupt(JSContext::RequestInterruptCanWait);
-
-    // Notify the main thread in case it is waiting for the compilation to finish.
+    // Notify the active thread in case it is waiting for the compilation to finish.
     HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
 
     // When finishing Ion compilation jobs, we can start unpausing compilation
     // threads that were paused to restrict the number of active compilations.
     // Only unpause one at a time, to make sure we don't exceed the restriction.
     // Since threads are currently only paused for Ion compilations, this
     // strategy will eventually unpause all paused threads, regardless of how
     // many there are, since each thread we unpause will eventually finish and
@@ -1676,30 +1685,30 @@ HelperThread::handleParseWorkload(AutoLo
     JSContext* oldcx = TlsContext.get();
     TlsContext.set(task->cx);
     {
         AutoUnlockHelperThreadState unlock(locked);
         task->parse();
     }
     TlsContext.set(oldcx);
 
-    // The callback is invoked while we are still off the main thread.
+    // The callback is invoked while we are still off thread.
     task->callback(task, task->callbackData);
 
     // FinishOffThreadScript will need to be called on the script to
     // migrate it into the correct compartment.
     {
         AutoEnterOOMUnsafeRegion oomUnsafe;
         if (!HelperThreadState().parseFinishedList(locked).append(task))
             oomUnsafe.crash("handleParseWorkload");
     }
 
     currentTask.reset();
 
-    // Notify the main thread in case it is waiting for the parse/emit to finish.
+    // Notify the active thread in case it is waiting for the parse/emit to finish.
     HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
 }
 
 void
 HelperThread::handleCompressionWorkload(AutoLockHelperThreadState& locked)
 {
     MOZ_ASSERT(HelperThreadState().canStartCompressionTask(locked));
     MOZ_ASSERT(idle());
@@ -1715,17 +1724,17 @@ HelperThread::handleCompressionWorkload(
         AutoTraceLog logCompile(logger, TraceLogger_CompressSource);
 
         task->result = task->work();
     }
 
     task->helperThread = nullptr;
     currentTask.reset();
 
-    // Notify the main thread in case it is waiting for the compression to finish.
+    // Notify the active thread in case it is waiting for the compression to finish.
     HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
 }
 
 bool
 js::StartOffThreadCompression(JSContext* cx, SourceCompressionTask* task)
 {
     AutoLockHelperThreadState lock;
 
--- a/js/src/vm/HelperThreads.h
+++ b/js/src/vm/HelperThreads.h
@@ -1,16 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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/. */
 
 /*
- * Definitions for managing off-main-thread work using a process wide list
+ * Definitions for managing off-thread work using a process wide list
  * of worklist items and pool of threads. Worklist items are engine internal,
  * and are distinct from e.g. web workers.
  */
 
 #ifndef vm_HelperThreads_h
 #define vm_HelperThreads_h
 
 #include "mozilla/Attributes.h"
@@ -224,17 +224,17 @@ class GlobalHelperThreadState
         uint32_t n = numWasmFailedJobs;
         numWasmFailedJobs = 0;
         return n;
     }
     UniqueChars harvestWasmError(const AutoLockHelperThreadState&) {
         return Move(firstWasmError);
     }
     void noteWasmFailure(const AutoLockHelperThreadState&) {
-        // Be mindful to signal the main thread after calling this function.
+        // Be mindful to signal the active thread after calling this function.
         numWasmFailedJobs++;
     }
     void setWasmError(const AutoLockHelperThreadState&, UniqueChars error) {
         if (!firstWasmError)
             firstWasmError = Move(error);
     }
     bool wasmFailed(const AutoLockHelperThreadState&) {
         return bool(numWasmFailedJobs);
@@ -247,17 +247,17 @@ class GlobalHelperThreadState
                                    Handle<GlobalObject*> global,
                                    JSCompartment* dest);
 
     void trace(JSTracer* trc);
 
   private:
     /*
      * Number of wasm jobs that encountered failure for the active module.
-     * Their parent is logically the main thread, and this number serves for harvesting.
+     * Their parent is logically the active thread, and this number serves for harvesting.
      */
     uint32_t numWasmFailedJobs;
     /*
      * Error string from wasm validation. Arbitrarily choose to keep the first one that gets
      * reported. Nondeterministic if multiple threads have errors.
      */
     UniqueChars firstWasmError;
 
@@ -585,17 +585,17 @@ struct ParseTask
             size_t cursor;
         };
     };
     LifoAlloc alloc;
 
     // Rooted pointer to the global object used by 'cx'.
     JSObject* exclusiveContextGlobal;
 
-    // Callback invoked off the main thread when the parse finishes.
+    // Callback invoked off thread when the parse finishes.
     JS::OffThreadCompileCallback callback;
     void* callbackData;
 
     // Holds the final script between the invocation of the callback and the
     // point where FinishOffThreadScript is called, which will destroy the
     // ParseTask.
     JSScript* script;
 
--- a/js/src/vm/Initialization.cpp
+++ b/js/src/vm/Initialization.cpp
@@ -92,17 +92,17 @@ JS::detail::InitWithFailureDiagnostic(bo
 #ifdef DEBUG
     CheckMessageParameterCounts();
 #endif
 
     RETURN_IF_FAIL(js::TlsContext.init());
 
 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
     RETURN_IF_FAIL(js::oom::InitThreadType());
-    js::oom::SetThreadType(js::oom::THREAD_TYPE_MAIN);
+    js::oom::SetThreadType(js::oom::THREAD_TYPE_COOPERATING);
 #endif
 
     RETURN_IF_FAIL(js::Mutex::Init());
 
     RETURN_IF_FAIL(js::wasm::InitInstanceStaticData());
 
     js::gc::InitMemorySubsystem(); // Ensure gc::SystemPageSize() works.
     RETURN_IF_FAIL(js::jit::InitProcessExecutableMemory());
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -263,18 +263,18 @@ js::NativeObject::lookupPure(jsid id)
     return Shape::searchNoHashify(lastProperty(), id);
 }
 
 uint32_t
 js::NativeObject::numFixedSlotsForCompilation() const
 {
     // This is an alternative method for getting the number of fixed slots in an
     // object. It requires more logic and memory accesses than numFixedSlots()
-    // but is safe to be called from the compilation thread, even if the main
-    // thread is actively mutating the VM.
+    // but is safe to be called from the compilation thread, even if the active
+    // thread is mutating the VM.
 
     // The compiler does not have access to nursery things.
     MOZ_ASSERT(!IsInsideNursery(this));
 
     if (this->is<ArrayObject>())
         return 0;
 
     gc::AllocKind kind = asTenured().getAllocKind();
@@ -1651,17 +1651,17 @@ bool
 js::NativeDefineProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
                          HandleValue value, JSGetterOp getter, JSSetterOp setter,
                          unsigned attrs)
 {
     ObjectOpResult result;
     if (!NativeDefineProperty(cx, obj, id, value, getter, setter, attrs, result))
         return false;
     if (!result) {
-        // Off-main-thread callers should not get here: they must call this
+        // Off-thread callers should not get here: they must call this
         // function only with known-valid arguments. Populating a new
         // PlainObject with configurable properties is fine.
         MOZ_ASSERT(!cx->helperThread());
         result.reportError(cx, obj, id);
         return false;
     }
     return true;
 }
--- a/js/src/vm/ObjectGroup-inl.h
+++ b/js/src/vm/ObjectGroup-inl.h
@@ -10,17 +10,17 @@
 #include "vm/ObjectGroup.h"
 
 namespace js {
 
 inline bool
 ObjectGroup::needsSweep()
 {
     // Note: this can be called off thread during compacting GCs, in which case
-    // nothing will be running on the main thread.
+    // nothing will be running on the active thread.
     return generation() != zoneFromAnyThread()->types.generation;
 }
 
 inline void
 ObjectGroup::maybeSweep(AutoClearTypeInferenceStateOnOOM* oom)
 {
     if (needsSweep())
         sweep(oom);
--- a/js/src/vm/PIC.cpp
+++ b/js/src/vm/PIC.cpp
@@ -271,17 +271,17 @@ js::ForOfPIC::Chain::sweep(FreeOp* fop)
         stubs_ = next;
     }
     fop->delete_(this);
 }
 
 static void
 ForOfPIC_finalize(FreeOp* fop, JSObject* obj)
 {
-    MOZ_ASSERT(fop->maybeOffMainThread());
+    MOZ_ASSERT(fop->maybeOnHelperThread());
     if (ForOfPIC::Chain* chain = ForOfPIC::fromJSObject(&obj->as<NativeObject>()))
         chain->sweep(fop);
 }
 
 static void
 ForOfPIC_traceObject(JSTracer* trc, JSObject* obj)
 {
     if (ForOfPIC::Chain* chain = ForOfPIC::fromJSObject(&obj->as<NativeObject>()))
--- a/js/src/vm/RegExpStatics.cpp
+++ b/js/src/vm/RegExpStatics.cpp
@@ -17,17 +17,17 @@ using namespace js;
  * per-global and not leak, we create a js::Class to wrap the C++ instance and
  * provide an appropriate finalizer. We lazily create and store an instance of
  * that js::Class in a global reserved slot.
  */
 
 static void
 resc_finalize(FreeOp* fop, JSObject* obj)
 {
-    MOZ_ASSERT(fop->onMainThread());
+    MOZ_ASSERT(fop->onActiveCooperatingThread());
     RegExpStatics* res = static_cast<RegExpStatics*>(obj->as<RegExpStaticsObject>().getPrivate());
     fop->delete_(res);
 }
 
 static void
 resc_trace(JSTracer* trc, JSObject* obj)
 {
     void* pdata = obj->as<RegExpStaticsObject>().getPrivate();
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -126,30 +126,28 @@ JSRuntime::JSRuntime(JSRuntime* parentRu
     trustedPrincipals_(nullptr),
     wrapObjectCallbacks(&DefaultWrapObjectCallbacks),
     preserveWrapperCallback(nullptr),
     scriptEnvironmentPreparer(nullptr),
     ctypesActivityCallback(nullptr),
     windowProxyClass_(nullptr),
     exclusiveAccessLock(mutexid::RuntimeExclusiveAccess),
 #ifdef DEBUG
-    mainThreadHasExclusiveAccess(false),
+    activeThreadHasExclusiveAccess(false),
 #endif
     numExclusiveThreads(0),
     numCompartments(0),
     localeCallbacks(nullptr),
     defaultLocale(nullptr),
     defaultVersion_(JSVERSION_DEFAULT),
     profilingScripts(false),
     scriptAndCountsVector(nullptr),
     lcovOutput_(),
     jitRuntime_(nullptr),
     selfHostingGlobal_(nullptr),
-    singletonContext(nullptr),
-    singletonZoneGroup(nullptr),
     gc(thisFromCtor()),
     gcInitialized(false),
     NaNValue(DoubleNaNValue()),
     negativeInfinityValue(DoubleValue(NegativeInfinity<double>())),
     positiveInfinityValue(DoubleValue(PositiveInfinity<double>())),
     emptyString(nullptr),
     defaultFreeOp_(nullptr),
 #if !EXPOSE_INTL_API
@@ -194,34 +192,23 @@ JSRuntime::init(JSContext* cx, uint32_t 
 {
     if (CanUseExtraThreads() && !EnsureHelperThreadsInitialized())
         return false;
 
     activeContext_ = cx;
     if (!cooperatingContexts().append(cx))
         return false;
 
-    singletonContext = cx;
-
     defaultFreeOp_ = js_new<js::FreeOp>(this);
     if (!defaultFreeOp_)
         return false;
 
-    ScopedJSDeletePtr<ZoneGroup> zoneGroup(js_new<ZoneGroup>(this));
-    if (!zoneGroup)
-        return false;
-    singletonZoneGroup = zoneGroup;
-
     if (!gc.init(maxbytes, maxNurseryBytes))
         return false;
 
-    if (!zoneGroup->init(maxNurseryBytes) || !gc.groups.ref().append(zoneGroup))
-        return false;
-    zoneGroup.forget();
-
     ScopedJSDeletePtr<Zone> atomsZone(new_<Zone>(this, nullptr));
     if (!atomsZone || !atomsZone->init(true))
         return false;
 
     JS::CompartmentOptions options;
     ScopedJSDeletePtr<JSCompartment> atomsCompartment(new_<JSCompartment>(atomsZone.get(), options));
     if (!atomsCompartment || !atomsCompartment->init(nullptr))
         return false;
@@ -284,17 +271,17 @@ JSRuntime::destroyRuntime()
             FinishGC(cx);
 
         /* Free source hook early, as its destructor may want to delete roots. */
         sourceHook = nullptr;
 
         /*
          * Cancel any pending, in progress or completed Ion compilations and
          * parse tasks. Waiting for wasm and compression tasks is done
-         * synchronously (on the main thread or during parse tasks), so no
+         * synchronously (on the active thread or during parse tasks), so no
          * explicit canceling is needed for these.
          */
         CancelOffThreadIonCompile(this);
         CancelOffThreadParses(this);
 
         /* Remove persistent GC roots. */
         gc.finishRoots();
 
@@ -337,18 +324,16 @@ JSRuntime::destroyRuntime()
 
     js_delete(defaultFreeOp_.ref());
 
     js_free(defaultLocale);
     js_delete(jitRuntime_.ref());
 
     DebugOnly<size_t> oldCount = liveRuntimesCount--;
     MOZ_ASSERT(oldCount > 0);
-
-    js_delete(zoneGroupFromMainThread());
 }
 
 void
 JSRuntime::setActiveContext(JSContext* cx)
 {
     MOZ_ASSERT_IF(cx, isCooperatingContext(cx));
     MOZ_RELEASE_ASSERT(!activeContextChangeProhibited());
     MOZ_RELEASE_ASSERT(gc.canChangeActiveContext(cx));
@@ -792,17 +777,17 @@ js::CurrentThreadCanAccessRuntime(const 
 }
 
 bool
 js::CurrentThreadCanAccessZone(Zone* zone)
 {
     if (CurrentThreadCanAccessRuntime(zone->runtime_))
         return true;
 
-    // Only zones in use by an exclusive thread can be used off the main thread.
+    // Only zones in use by an exclusive thread can be used off thread.
     // We don't keep track of which thread owns such zones though, so this check
     // is imperfect.
     return zone->usedByExclusiveThread;
 }
 
 #ifdef DEBUG
 bool
 js::CurrentThreadIsPerformingGC()
@@ -825,33 +810,33 @@ JS::IsProfilingEnabledForContext(JSConte
     MOZ_ASSERT(cx);
     return cx->runtime()->geckoProfiler().enabled();
 }
 
 JSRuntime::IonBuilderList&
 JSRuntime::ionLazyLinkList()
 {
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(this),
-               "Should only be mutated by the main thread.");
+               "Should only be mutated by the active thread.");
     return ionLazyLinkList_.ref();
 }
 
 void
 JSRuntime::ionLazyLinkListRemove(jit::IonBuilder* builder)
 {
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(this),
-               "Should only be mutated by the main thread.");
+               "Should only be mutated by the active thread.");
     MOZ_ASSERT(ionLazyLinkListSize_ > 0);
 
     builder->removeFrom(ionLazyLinkList());
     ionLazyLinkListSize_--;
 
     MOZ_ASSERT(ionLazyLinkList().isEmpty() == (ionLazyLinkListSize_ == 0));
 }
 
 void
 JSRuntime::ionLazyLinkListAdd(jit::IonBuilder* builder)
 {
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(this),
-               "Should only be mutated by the main thread.");
+               "Should only be mutated by the active thread.");
     ionLazyLinkList().insertFront(builder);
     ionLazyLinkListSize_++;
 }
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -109,16 +109,46 @@ class CompileRuntime;
 
 #ifdef JS_SIMULATOR_ARM64
 typedef vixl::Simulator Simulator;
 #elif defined(JS_SIMULATOR)
 class Simulator;
 #endif
 } // namespace jit
 
+// JS Engine Threading
+//
+// Multiple threads may interact with a JS runtime. JS has run-to-completion
+// semantics, which means that scripts cannot observe changes in behavior
+// due to activities performed on other threads (there is an exception to this
+// for shared array buffers and related APIs).
+//
+// The main way we ensure that run-to-completion semantics are preserved is
+// by dividing content into zone groups. Pieces of web content will be in the
+// the same zone group if they have the same tab/origin or can otherwise
+// observe changes in each other via Window.opener and so forth. When a thread
+// executes JS in a zone group, it acquires that group --- including exclusive
+// access to most of the group's content --- and does not relinquish control of
+// the zone group until the script finishes executing.
+//
+// Threads interacting with a runtime are divided into two categories:
+//
+// - Cooperating threads are capable of running JS. At most one cooperating
+//   thread may be |active| at a time in a runtime, but they may yield control
+//   to each other so that their execution is interleaved. As described above,
+//   each thread owns the zone groups it is operating on so that this
+//   interleaving does not cause observable changes in a script's behavior.
+//
+// - Helper threads do not run JS, and are controlled or triggered by activity
+//   in the cooperating threads. Helper threads may have exclusive access to
+//   zone groups created for them, for parsing and similar tasks, but their
+//   activities do not cause observable changes in script behaviors. Activity
+//   on helper threads may be referred to as happening 'off thread' or on a
+//   background thread in some parts of the VM.
+
 /*
  * A FreeOp can do one thing: free memory. For convenience, it has delete_
  * convenience methods that also call destructors.
  *
  * FreeOp is passed to finalizers and other sweep-phase hooks so that we do not
  * need to pass a JSContext to those hooks.
  */
 class FreeOp : public JSFreeOp
@@ -129,23 +159,23 @@ class FreeOp : public JSFreeOp
   public:
     static FreeOp* get(JSFreeOp* fop) {
         return static_cast<FreeOp*>(fop);
     }
 
     explicit FreeOp(JSRuntime* maybeRuntime);
     ~FreeOp();
 
-    bool onMainThread() const {
+    bool onActiveCooperatingThread() const {
         return runtime_ != nullptr;
     }
 
-    bool maybeOffMainThread() const {
-        // Sometimes background finalization happens on the main thread so
-        // runtime_ being null doesn't always mean we are off the main thread.
+    bool maybeOnHelperThread() const {
+        // Sometimes background finalization happens on the active thread so
+        // runtime_ being null doesn't always mean we are off thread.
         return !runtime_;
     }
 
     bool isDefaultFreeOp() const;
 
     inline void free_(void* p);
     inline void freeLater(void* p);
 
@@ -529,45 +559,45 @@ struct JSRuntime : public js::MallocProv
     JSCList& onNewGlobalObjectWatchers() { return onNewGlobalObjectWatchers_.ref(); }
 
   private:
     /*
      * Lock taken when using per-runtime or per-zone data that could otherwise
      * be accessed simultaneously by multiple threads.
      *
      * Locking this only occurs if there is actually a thread other than the
-     * main thread which could access such data.
+     * active thread which could access such data.
      */
     js::Mutex exclusiveAccessLock;
 #ifdef DEBUG
-    bool mainThreadHasExclusiveAccess;
+    bool activeThreadHasExclusiveAccess;
 #endif
 
-    /* Number of non-main threads with exclusive access to some zone. */
+    /* Number of non-cooperating threads with exclusive access to some zone. */
     js::UnprotectedData<size_t> numExclusiveThreads;
 
     friend class js::AutoLockForExclusiveAccess;
 
   public:
     void setUsedByExclusiveThread(JS::Zone* zone);
     void clearUsedByExclusiveThread(JS::Zone* zone);
 
     bool exclusiveThreadsPresent() const {
         return numExclusiveThreads > 0;
     }
 
 #ifdef DEBUG
     bool currentThreadHasExclusiveAccess() const {
-        return (!exclusiveThreadsPresent() && mainThreadHasExclusiveAccess) ||
+        return (!exclusiveThreadsPresent() && activeThreadHasExclusiveAccess) ||
             exclusiveAccessLock.ownedByCurrentThread();
     }
 #endif
 
     // How many compartments there are across all zones. This number includes
-    // off main thread context compartments, so it isn't necessarily equal to the
+    // off thread context compartments, so it isn't necessarily equal to the
     // number of compartments visited by CompartmentsIter.
     js::ActiveThreadData<size_t> numCompartments;
 
     /* Locale-specific callbacks for string conversion. */
     js::ActiveThreadData<const JSLocaleCallbacks*> localeCallbacks;
 
     /* Default locale for Internationalization API */
     js::ActiveThreadData<char*> defaultLocale;
@@ -611,34 +641,16 @@ struct JSRuntime : public js::MallocProv
     }
     js::jit::JitRuntime* jitRuntime() const {
         return jitRuntime_.ref();
     }
     bool hasJitRuntime() const {
         return !!jitRuntime_;
     }
 
-    // These will be removed soon.
-  private:
-    JSContext* singletonContext;
-    js::ZoneGroup* singletonZoneGroup;
-  public:
-
-    JSContext* unsafeContextFromAnyThread() const { return singletonContext; }
-    JSContext* contextFromMainThread() const {
-        MOZ_ASSERT(CurrentThreadCanAccessRuntime(this));
-        return singletonContext;
-    }
-
-    js::ZoneGroup* zoneGroupFromAnyThread() const { return singletonZoneGroup; }
-    js::ZoneGroup* zoneGroupFromMainThread() const {
-        MOZ_ASSERT(CurrentThreadCanAccessRuntime(this));
-        return singletonZoneGroup;
-    }
-
   private:
     // Used to generate random keys for hash tables.
     mozilla::Maybe<mozilla::non_crypto::XorShift128PlusRNG> randomKeyGenerator_;
     mozilla::non_crypto::XorShift128PlusRNG& randomKeyGenerator();
 
   public:
     mozilla::HashCodeScrambler randomHashCodeScrambler();
     mozilla::non_crypto::XorShift128PlusRNG forkRandomKeyGenerator();
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -362,20 +362,20 @@ SavedFrame::protoAccessors[] = {
     JS_PSG("asyncParent", SavedFrame::asyncParentProperty, 0),
     JS_PSG("parent", SavedFrame::parentProperty, 0),
     JS_PS_END
 };
 
 /* static */ void
 SavedFrame::finalize(FreeOp* fop, JSObject* obj)
 {
-    MOZ_ASSERT(fop->onMainThread());
+    MOZ_ASSERT(fop->onActiveCooperatingThread());
     JSPrincipals* p = obj->as<SavedFrame>().getPrincipals();
     if (p) {
-        JSRuntime* rt = obj->runtimeFromMainThread();
+        JSRuntime* rt = obj->runtimeFromActiveCooperatingThread();
         JS_DropPrincipals(rt->activeContextFromOwnThread(), p);
     }
 }
 
 JSAtom*
 SavedFrame::getSource()
 {
     const Value& v = getReservedSlot(JSSLOT_SOURCE);
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -2740,20 +2740,20 @@ js::FillSelfHostingCompileOptions(Compil
 
 GlobalObject*
 JSRuntime::createSelfHostingGlobal(JSContext* cx)
 {
     MOZ_ASSERT(!cx->isExceptionPending());
     MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
 
     JS::CompartmentOptions options;
-    options.creationOptions().setZone(JS::FreshZone);
+    options.creationOptions().setNewZoneInSystemZoneGroup();
     options.behaviors().setDiscardSource(true);
 
-    JSCompartment* compartment = NewCompartment(cx, nullptr, nullptr, options);
+    JSCompartment* compartment = NewCompartment(cx, nullptr, options);
     if (!compartment)
         return nullptr;
 
     static const ClassOps shgClassOps = {
         nullptr, nullptr, nullptr, nullptr,
         nullptr, nullptr, nullptr, nullptr,
         nullptr, nullptr, nullptr,
         JS_GlobalObjectTraceHook
@@ -3064,17 +3064,17 @@ CloneString(JSContext* cx, JSFlatString*
 
 static JSObject*
 CloneObject(JSContext* cx, HandleNativeObject selfHostedObject)
 {
 #ifdef DEBUG
     // Object hash identities are owned by the hashed object, which may be on a
     // different thread than the clone target. In theory, these objects are all
     // tenured and will not be compacted; however, we simply avoid the issue
-    // altogether by skipping the cycle-detection when off the main thread.
+    // altogether by skipping the cycle-detection when off thread.
     mozilla::Maybe<AutoCycleDetector> detect;
     if (js::CurrentThreadCanAccessZone(selfHostedObject->zoneFromAnyThread())) {
         detect.emplace(cx, selfHostedObject);
         if (!detect->init())
             return nullptr;
         if (detect->foundCycle())
             MOZ_CRASH("SelfHosted cloning cannot handle cyclic object graphs.");
     }
--- a/js/src/vm/Shape.cpp
+++ b/js/src/vm/Shape.cpp
@@ -917,17 +917,17 @@ NativeObject::putProperty(JSContext* cx,
      * Can't fail now, so free the previous incarnation's slot if the new shape
      * has no slot. But we do not need to free oldSlot (and must not, as trying
      * to will botch an assertion in JSObject::freeSlot) if the new last
      * property (shape here) has a slotSpan that does not cover it.
      */
     if (hadSlot && !shape->hasSlot()) {
         if (oldSlot < obj->slotSpan())
             obj->freeSlot(cx, oldSlot);
-        /* Note: The optimization based on propertyRemovals is only relevant to the main thread. */
+        /* Note: The optimization based on propertyRemovals is only relevant to the active thread. */
         if (!cx->helperThread())
             ++cx->propertyRemovals;
     }
 
     obj->checkShapeConsistency();
 
     return shape;
 }
@@ -1700,17 +1700,17 @@ EmptyShape::insertInitialShape(JSContext
 
     /*
      * This affects the shape that will be produced by the various NewObject
      * methods, so clear any cache entry referring to the old shape. This is
      * not required for correctness: the NewObject must always check for a
      * nativeEmpty() result and generate the appropriate properties if found.
      * Clearing the cache entry avoids this duplicate regeneration.
      *
-     * Clearing is not necessary when this context is running off the main
+     * Clearing is not necessary when this context is running off
      * thread, as it will not use the new object cache for allocations.
      */
     if (!cx->helperThread())
         cx->caches().newObjectCache.invalidateEntriesForShape(cx, shape, proto);
 }
 
 void
 Zone::fixupInitialShapeTable()
--- a/js/src/vm/SharedArrayObject.cpp
+++ b/js/src/vm/SharedArrayObject.cpp
@@ -294,17 +294,17 @@ SharedArrayBufferObject::rawBufferObject
     Value v = getReservedSlot(RAWBUF_SLOT);
     MOZ_ASSERT(!v.isUndefined());
     return reinterpret_cast<SharedArrayRawBuffer*>(v.toPrivate());
 }
 
 void
 SharedArrayBufferObject::Finalize(FreeOp* fop, JSObject* obj)
 {
-    MOZ_ASSERT(fop->maybeOffMainThread());
+    MOZ_ASSERT(fop->maybeOnHelperThread());
 
     SharedArrayBufferObject& buf = obj->as<SharedArrayBufferObject>();
 
     // Detect the case of failure during SharedArrayBufferObject creation,
     // which causes a SharedArrayRawBuffer to never be attached.
     Value v = buf.getReservedSlot(RAWBUF_SLOT);
     if (!v.isUndefined()) {
         buf.rawBufferObject()->dropReference();
--- a/js/src/vm/String.cpp
+++ b/js/src/vm/String.cpp
@@ -42,17 +42,17 @@ JSString::sizeOfExcludingThis(mozilla::M
 
     // JSDependentString: do nothing, we'll count the chars when we hit the base string.
     if (isDependent())
         return 0;
 
     // JSExternalString: Ask the embedding to tell us what's going on.  If it
     // doesn't want to say, don't count, the chars could be stored anywhere.
     if (isExternal()) {
-        if (auto* cb = runtimeFromMainThread()->externalStringSizeofCallback.ref()) {
+        if (auto* cb = runtimeFromActiveCooperatingThread()->externalStringSizeofCallback.ref()) {
             // Our callback isn't supposed to cause GC.
             JS::AutoSuppressGCAnalysis nogc;
             return cb(this, mallocSizeOf);
         }
         return 0;
     }
 
     MOZ_ASSERT(isFlat());
--- a/js/src/vm/Symbol.cpp
+++ b/js/src/vm/Symbol.cpp
@@ -40,17 +40,17 @@ Symbol::new_(JSContext* cx, JS::SymbolCo
     JSAtom* atom = nullptr;
     if (description) {
         atom = AtomizeString(cx, description);
         if (!atom)
             return nullptr;
     }
 
     // Lock to allocate. If symbol allocation becomes a bottleneck, this can
-    // probably be replaced with an assertion that we're on the main thread.
+    // probably be replaced with an assertion that we're on the active thread.
     AutoLockForExclusiveAccess lock(cx);
     Symbol* sym;
     {
         AutoCompartment ac(cx, cx->atomsCompartment(lock), &lock);
         sym = newInternal(cx, code, cx->compartment()->randomHashCode(), atom, lock);
     }
     cx->markAtom(sym);
     return sym;
--- a/js/src/vm/TraceLogging.cpp
+++ b/js/src/vm/TraceLogging.cpp
@@ -842,30 +842,30 @@ TraceLoggerThreadState::init()
     const char* options = getenv("TLOPTIONS");
     if (options) {
         if (strstr(options, "help")) {
             fflush(nullptr);
             printf(
                 "\n"
                 "usage: TLOPTIONS=option,option,option,... where options can be:\n"
                 "\n"
-                "  EnableMainThread        Start logging the main thread immediately.\n"
+                "  EnableCooperatingThread Start logging cooperating threads immediately.\n"
                 "  EnableOffThread         Start logging helper threads immediately.\n"
                 "  EnableGraph             Enable spewing the tracelogging graph to a file.\n"
                 "  Errors                  Report errors during tracing to stderr.\n"
             );
             printf("\n");
             exit(0);
             /*NOTREACHED*/
         }
 
-        if (strstr(options, "EnableMainThread"))
-            mainThreadEnabled = true;
+        if (strstr(options, "EnableActiveThread"))
+            cooperatingThreadEnabled = true;
         if (strstr(options, "EnableOffThread"))
-            offThreadEnabled = true;
+            helperThreadEnabled = true;
         if (strstr(options, "EnableGraph"))
             graphSpewingEnabled = true;
         if (strstr(options, "Errors"))
             spewErrors = true;
     }
 
     if (!pointerMap.init())
         return false;
@@ -958,17 +958,17 @@ TraceLoggerThreadState::forCurrentThread
         }
 
         threadLoggers.insertFront(logger);
         cx->traceLogger = logger;
 
         if (graphSpewingEnabled)
             logger->initGraph();
 
-        if (CurrentHelperThread() ? offThreadEnabled : mainThreadEnabled)
+        if (CurrentHelperThread() ? helperThreadEnabled : cooperatingThreadEnabled)
             logger->enable();
     }
 
     return cx->traceLogger;
 }
 
 void
 TraceLoggerThreadState::destroyLogger(TraceLoggerThread* logger)
--- a/js/src/vm/TraceLogging.h
+++ b/js/src/vm/TraceLogging.h
@@ -313,18 +313,18 @@ class TraceLoggerThread : public mozilla
 class TraceLoggerThreadState
 {
 #ifdef JS_TRACE_LOGGING
 #ifdef DEBUG
     bool initialized;
 #endif
 
     bool enabledTextIds[TraceLogger_Last];
-    bool mainThreadEnabled;
-    bool offThreadEnabled;
+    bool cooperatingThreadEnabled;
+    bool helperThreadEnabled;
     bool graphSpewingEnabled;
     bool spewErrors;
     mozilla::LinkedList<TraceLoggerThread> threadLoggers;
 
     typedef HashMap<const void*,
                     TraceLoggerEventPayload*,
                     PointerHasher<const void*, 3>,
                     SystemAllocPolicy> PointerHashMap;
@@ -340,18 +340,18 @@ class TraceLoggerThreadState
     uint64_t startupTime;
     Mutex lock;
 
     TraceLoggerThreadState()
       :
 #ifdef DEBUG
         initialized(false),
 #endif
-        mainThreadEnabled(false),
-        offThreadEnabled(false),
+        cooperatingThreadEnabled(false),
+        helperThreadEnabled(false),
         graphSpewingEnabled(false),
         spewErrors(false),
         nextTextId(TraceLogger_Last),
         startupTime(0),
         lock(js::mutexid::TraceLoggerThreadState)
     { }
 
     bool init();
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -1018,40 +1018,40 @@ TypeSet::intersectSets(TemporaryTypeSet*
 // Compiler constraints
 /////////////////////////////////////////////////////////////////////
 
 // Compiler constraints overview
 //
 // Constraints generated during Ion compilation capture assumptions made about
 // heap properties that will trigger invalidation of the resulting Ion code if
 // the constraint is violated. Constraints can only be attached to type sets on
-// the main thread, so to allow compilation to occur almost entirely off thread
+// the active thread, so to allow compilation to occur almost entirely off thread
 // the generation is split into two phases.
 //
 // During compilation, CompilerConstraint values are constructed in a list,
 // recording the heap property type set which was read from and its expected
 // contents, along with the assumption made about those contents.
 //
-// At the end of compilation, when linking the result on the main thread, the
+// At the end of compilation, when linking the result on the active thread, the
 // list of compiler constraints are read and converted to type constraints and
 // attached to the type sets. If the property type sets have changed so that the
 // assumptions no longer hold then the compilation is aborted and its result
 // discarded.
 
 // Superclass of all constraints generated during Ion compilation. These may
-// be allocated off the main thread, using the current JIT context's allocator.
+// be allocated off thread, using the current JIT context's allocator.
 class CompilerConstraint
 {
   public:
     // Property being queried by the compiler.
     HeapTypeSetKey property;
 
     // Contents of the property at the point when the query was performed. This
     // may differ from the actual property types later in compilation as the
-    // main thread performs side effects.
+    // active thread performs side effects.
     TemporaryTypeSet* expected;
 
     CompilerConstraint(LifoAlloc* alloc, const HeapTypeSetKey& property)
       : property(property),
         expected(property.maybeTypes() ? property.maybeTypes()->clone(alloc) : nullptr)
     {}
 
     // Generate the type constraint recording the assumption made by this
@@ -1306,17 +1306,17 @@ TypeSet::ObjectKey::property(jsid id)
     return property;
 }
 
 void
 TypeSet::ObjectKey::ensureTrackedProperty(JSContext* cx, jsid id)
 {
     // If we are accessing a lazily defined property which actually exists in
     // the VM and has not been instantiated yet, instantiate it now if we are
-    // on the main thread and able to do so.
+    // on the active thread and able to do so.
     if (!JSID_IS_VOID(id) && !JSID_IS_EMPTY(id)) {
         MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
         if (isSingleton()) {
             JSObject* obj = singleton();
             if (obj->isNative() && obj->as<NativeObject>().containsPure(id))
                 EnsureTrackPropertyTypes(cx, obj, id);
         }
     }
@@ -1530,17 +1530,17 @@ js::InvalidateCompilerOutputsForScript(J
                 co.invalidate();
         }
     }
 }
 
 static void
 CheckDefinitePropertiesTypeSet(JSContext* cx, TemporaryTypeSet* frozen, StackTypeSet* actual)
 {
-    // The definite properties analysis happens on the main thread, so no new
+    // The definite properties analysis happens on the active thread, so no new
     // types can have been added to actual. The analysis may have updated the
     // contents of |frozen| though with new speculative types, and these need
     // to be reflected in |actual| for AddClearDefiniteFunctionUsesInScript
     // to work.
     if (!frozen->isSubset(actual)) {
         TypeSet::TypeList list;
         frozen->enumerateTypes(&list);
 
@@ -3078,17 +3078,17 @@ ObjectGroup::clearNewScript(JSContext* c
                 Property* prop = getProperty(i);
                 if (!prop)
                     continue;
                 if (prop->types.definiteProperty())
                     prop->types.setNonDataProperty(cx);
             }
         }
     } else {
-        // Threads off the main thread are not allowed to run scripts.
+        // Helper threads are not allowed to run scripts.
         MOZ_ASSERT(!cx->activation());
     }
 
     js_delete(newScript);
     markStateChange(cx);
 }
 
 void
@@ -4319,17 +4319,17 @@ ObjectGroup::sweep(AutoClearTypeInferenc
 
     AssertGCStateForSweep(zone());
 
     Maybe<AutoClearTypeInferenceStateOnOOM> fallbackOOM;
     EnsureHasAutoClearTypeInferenceStateOnOOM(oom, zone(), fallbackOOM);
 
     if (maybeUnboxedLayout()) {
         // Remove unboxed layouts that are about to be finalized from the
-        // compartment wide list while we are still on the main thread.
+        // compartment wide list while we are still on the active thread.
         ObjectGroup* group = this;
         if (IsAboutToBeFinalizedUnbarriered(&group))
             unboxedLayout().detachFromCompartment();
 
         if (unboxedLayout().newScript())
             unboxedLayout().newScript()->sweep();
 
         // Discard constructor code to avoid holding onto ExecutablePools.
@@ -4592,17 +4592,17 @@ AutoClearTypeInferenceStateOnOOM::AutoCl
     zone->types.setSweepingTypes(true);
 }
 
 AutoClearTypeInferenceStateOnOOM::~AutoClearTypeInferenceStateOnOOM()
 {
     zone->types.setSweepingTypes(false);
 
     if (oom) {
-        JSRuntime* rt = zone->runtimeFromMainThread();
+        JSRuntime* rt = zone->runtimeFromActiveCooperatingThread();
         js::CancelOffThreadIonCompile(rt);
         zone->setPreservingCode(false);
         zone->discardJitCode(rt->defaultFreeOp(), /* discardBaselineCode = */ false);
         zone->types.clearAllNewScriptsOnOOM();
     }
 }
 
 #ifdef DEBUG
--- a/js/src/vm/TypeInference.h
+++ b/js/src/vm/TypeInference.h
@@ -1181,20 +1181,20 @@ InvalidateCompilerOutputsForScript(JSCon
 
 // Update the actual types in any scripts queried by constraints with any
 // speculative types added during the definite properties analysis.
 void
 FinishDefinitePropertiesAnalysis(JSContext* cx, CompilerConstraintList* constraints);
 
 // Representation of a heap type property which may or may not be instantiated.
 // Heap properties for singleton types are instantiated lazily as they are used
-// by the compiler, but this is only done on the main thread. If we are
+// by the compiler, but this is only done on the active thread. If we are
 // compiling off thread and use a property which has not yet been instantiated,
 // it will be treated as empty and non-configured and will be instantiated when
-// rejoining to the main thread. If it is in fact not empty, the compilation
+// rejoining to the active thread. If it is in fact not empty, the compilation
 // will fail; to avoid this, we try to instantiate singleton property types
 // during generation of baseline caches.
 class HeapTypeSetKey
 {
     friend class TypeSet::ObjectKey;
 
     // Object and property being accessed.
     TypeSet::ObjectKey* object_;
--- a/js/src/vtune/README
+++ b/js/src/vtune/README
@@ -1,10 +1,11 @@
-VTune files imported from VTune Amplifier XE 2013 Rev 11.
+VTune files imported from VTune Amplifier XE 2017 Rev 1.0.486011.
 
 To update these files, copy the following from a VTune install directory:
-    sdk/src/ittnotify/ittnotify_config.h
-    sdk/src/ittnotify/ittnotify_types.h
-    sdk/src/ittnotify/jitprofiling.c
+    sdk/src/ittnotify/*
+
+    include/ittnotify.h
     include/jitprofiling.h
+    include/legacy/ittnotify.h
 
 If the license has changed, update the "VTune License" section of
     toolkit/content/license.html
new file mode 100644
--- /dev/null
+++ b/js/src/vtune/VTuneWrapper.cpp
@@ -0,0 +1,149 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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/. */
+
+#include "vtune/VTuneWrapper.h"
+
+#include "mozilla/Sprintf.h"
+
+#include "jscntxt.h"
+#include "jscompartment.h"
+#include "jsgc.h"
+
+#include "vm/Shape.h"
+#include "wasm/WasmCode.h"
+
+#ifdef MOZ_VTUNE
+
+namespace js {
+namespace vtune {
+
+uint32_t
+GenerateUniqueMethodID()
+{
+    return (uint32_t)iJIT_GetNewMethodID();
+}
+
+
+// Stubs and trampolines are created on engine initialization and are never unloaded.
+void
+MarkStub(const js::jit::JitCode* code, const char* name)
+{
+    if (!IsProfilingActive())
+        return;
+
+    iJIT_Method_Load_V2 method = {0};
+    method.method_id = GenerateUniqueMethodID();
+    method.method_name = const_cast<char*>(name);
+    method.method_load_address = code->raw();
+    method.method_size = code->instructionsSize();
+    method.module_name = const_cast<char*>("jitstubs");
+
+    iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED_V2, (void*)&method);
+}
+
+
+void
+MarkRegExp(const js::jit::JitCode* code, bool match_only)
+{
+    if (!IsProfilingActive())
+        return;
+
+    iJIT_Method_Load_V2 method = {0};
+    method.method_id = GenerateUniqueMethodID();
+    method.method_load_address = code->raw();
+    method.method_size = code->instructionsSize();
+
+    if (match_only)
+        method.method_name = const_cast<char*>("regexp (match-only)");
+    else
+        method.method_name = const_cast<char*>("regexp (normal)");
+
+    method.module_name = const_cast<char*>("irregexp");
+
+    int ok = iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED_V2, (void*)&method);
+    if (ok != 1)
+        printf("[!] VTune Integration: Failed to load method.\n");
+}
+
+
+void
+MarkScript(const js::jit::JitCode* code, const JSScript* script, const char* module)
+{
+    if (!IsProfilingActive())
+        return;
+
+    iJIT_Method_Load_V2 method = {0};
+    method.method_id = script->vtuneMethodID();
+    method.method_load_address = code->raw();
+    method.method_size = code->instructionsSize();
+    method.module_name = const_cast<char*>(module);
+
+    // Line numbers begin at 1, but columns begin at 0.
+    // Text editors start at 1,1 so fixup is performed to match.
+    char namebuf[512];
+    SprintfLiteral(namebuf, "%s:%zu:%zu",
+                   script->filename(), script->lineno(), script->column() + 1);
+
+    method.method_name = &namebuf[0];
+
+    int ok = iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED_V2, (void*)&method);
+    if (ok != 1)
+        printf("[!] VTune Integration: Failed to load method.\n");
+}
+
+
+void
+MarkWasm(const js::wasm::CodeSegment& cs, const char* name, void* start, uintptr_t size)
+{
+    if (!IsProfilingActive())
+        return;
+
+    iJIT_Method_Load_V2 method = {0};
+    method.method_id = cs.vtune_method_id_;
+    method.method_name = const_cast<char*>(name);
+    method.method_load_address = start;
+    method.method_size = (unsigned)size;
+    method.module_name = const_cast<char*>("wasm");
+
+    int ok = iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED_V2, (void*)&method);
+    if (ok != 1)
+        printf("[!] VTune Integration: Failed to load method.\n");
+}
+
+
+void
+UnmarkCode(const js::jit::JitCode* code)
+{
+    UnmarkBytes(code->raw(), (unsigned)code->instructionsSize());
+}
+
+
+void
+UnmarkBytes(void* bytes, unsigned size)
+{
+    if (!IsProfilingActive())
+        return;
+
+    // It appears that the method_id is not required for unloading.
+    iJIT_Method_Load method = {0};
+    method.method_load_address = bytes;
+    method.method_size = size;
+
+    // The iJVM_EVENT_TYPE_METHOD_UNLOAD_START event is undocumented.
+    // VTune appears to happily accept unload events even for untracked JitCode.
+    int ok = iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_UNLOAD_START, (void*)&method);
+
+    // Assertions aren't reported in VTune: instead, they immediately end profiling
+    // with no warning that a crash occurred. This can generate misleading profiles.
+    // So instead, print out a message to stdout (which VTune does not redirect).
+    if (ok != 1)
+        printf("[!] VTune Integration: Failed to unload method.\n");
+}
+
+} // namespace vtune
+} // namespace js
+
+#endif // MOZ_VTUNE
--- a/js/src/vtune/VTuneWrapper.h
+++ b/js/src/vtune/VTuneWrapper.h
@@ -1,18 +1,57 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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/. */
 
-#ifndef vtunewrapper_h
-#define vtunewrapper_h
+#ifndef vtune_vtunewrapper_h
+#define vtune_vtunewrapper_h
+
+#ifdef MOZ_VTUNE
 
 #include "vtune/jitprofiling.h"
 
+#include "jsgc.h"
+#include "jsscript.h"
+
+#include "jit/IonCode.h"
+#include "wasm/WasmCode.h"
+
+namespace js {
+namespace vtune {
+
+// VTune profiling may be attached/detached at any time, but there is no API for
+// attaching a callback to execute at attachment time. Methods compiled before
+// VTune was most recently attached therefore do not provide any information to VTune.
 inline bool
-IsVTuneProfilingActive()
+IsProfilingActive()
 {
     return iJIT_IsProfilingActive() == iJIT_SAMPLING_ON;
 }
 
-#endif // vtunewrapper_h
+// Wrapper exists in case we need locking in the future.
+uint32_t GenerateUniqueMethodID();
+
+void MarkStub(const js::jit::JitCode* code, const char* name);
+
+void MarkRegExp(const js::jit::JitCode* code, bool match_only);
+
+void MarkScript(const js::jit::JitCode* code,
+                const JSScript* script,
+                const char* module);
+
+void MarkWasm(const js::wasm::CodeSegment& cs,
+              const char* name,
+              void* start,
+              uintptr_t size);
+
+void UnmarkCode(const js::jit::JitCode* code);
+
+void UnmarkBytes(void* bytes, unsigned size);
+
+} // namespace vtune
+} // namespace js
+
+#endif // MOZ_VTUNE
+
+#endif // vtune_vtunewrapper_h
new file mode 100644
--- /dev/null
+++ b/js/src/vtune/disable_warnings.h
@@ -0,0 +1,77 @@
+/* <copyright>
+  This file is provided under a dual BSD/GPLv2 license.  When using or
+  redistributing this file, you may do so under either license.
+
+  GPL LICENSE SUMMARY
+
+  Copyright (c) 2005-2014 Intel Corporation. All rights reserved.
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of version 2 of the GNU General Public License as
+  published by the Free Software Foundation.
+
+  This program is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+  The full GNU General Public License is included in this distribution
+  in the file called LICENSE.GPL.
+
+  Contact Information:
+  http://software.intel.com/en-us/articles/intel-vtune-amplifier-xe/
+
+  BSD LICENSE
+
+  Copyright (c) 2005-2014 Intel Corporation. All rights reserved.
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in
+      the documentation and/or other materials provided with the
+      distribution.
+    * Neither the name of Intel Corporation nor the names of its
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+</copyright> */
+
+#include "vtune/ittnotify_config.h"
+
+#if ITT_PLATFORM==ITT_PLATFORM_WIN
+
+#pragma warning (disable: 593)   /* parameter "XXXX" was set but never used                 */
+#pragma warning (disable: 344)   /* typedef name has already been declared (with same type) */
+#pragma warning (disable: 174)   /* expression has no effect                                */
+#pragma warning (disable: 4127)  /* conditional expression is constant                      */
+#pragma warning (disable: 4306)  /* conversion from '?' to '?' of greater size              */
+
+#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+
+#if defined __INTEL_COMPILER
+
+#pragma warning (disable: 869)  /* parameter "XXXXX" was never referenced                  */
+#pragma warning (disable: 1418) /* external function definition with no prior declaration  */
+#pragma warning (disable: 1419) /* external declaration in primary source file             */
+
+#endif /* __INTEL_COMPILER */
new file mode 100644
--- /dev/null
+++ b/js/src/vtune/ittnotify.h
@@ -0,0 +1,4123 @@
+/* <copyright>
+  This file is provided under a dual BSD/GPLv2 license.  When using or
+  redistributing this file, you may do so under either license.
+
+  GPL LICENSE SUMMARY
+
+  Copyright (c) 2005-2014 Intel Corporation. All rights reserved.
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of version 2 of the GNU General Public License as
+  published by the Free Software Foundation.
+
+  This program is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+  The full GNU General Public License is included in this distribution
+  in the file called LICENSE.GPL.
+
+  Contact Information:
+  http://software.intel.com/en-us/articles/intel-vtune-amplifier-xe/
+
+  BSD LICENSE
+
+  Copyright (c) 2005-2014 Intel Corporation. All rights reserved.
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in
+      the documentation and/or other materials provided with the
+      distribution.
+    * Neither the name of Intel Corporation nor the names of its
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+</copyright> */
+#ifndef _ITTNOTIFY_H_
+#define _ITTNOTIFY_H_
+
+/**
+@file
+@brief Public User API functions and types
+@mainpage
+
+The ITT API is used to annotate a user's program with additional information
+that can be used by correctness and performance tools. The user inserts
+calls in their program. Those calls generate information that is collected
+at runtime, and used by Intel(R) Threading Tools.
+
+@section API Concepts
+The following general concepts are used throughout the API.
+
+@subsection Unicode Support
+Many API functions take character string arguments. On Windows, there
+are two versions of each such function. The function name is suffixed
+by W if Unicode support is enabled, and by A otherwise. Any API function
+that takes a character string argument adheres to this convention.
+
+@subsection Conditional Compilation
+Many users prefer having an option to modify ITT API code when linking it
+inside their runtimes. ITT API header file provides a mechanism to replace
+ITT API function names inside your code with empty strings. To do this,
+define the macros INTEL_NO_ITTNOTIFY_API during compilation and remove the
+static library from the linker script.
+
+@subsection Domains
+[see domains]
+Domains provide a way to separate notification for different modules or
+libraries in a program. Domains are specified by dotted character strings,
+e.g. TBB.Internal.Control.
+
+A mechanism (to be specified) is provided to enable and disable
+domains. By default, all domains are enabled.
+@subsection Named Entities and Instances
+Named entities (frames, regions, tasks, and markers) communicate
+information about the program to the analysis tools. A named entity often
+refers to a section of program code, or to some set of logical concepts
+that the programmer wants to group together.
+
+Named entities relate to the programmer's static view of the program. When
+the program actually executes, many instances of a given named entity
+may be created.
+
+The API annotations denote instances of named entities. The actual
+named entities are displayed using the analysis tools. In other words,
+the named entities come into existence when instances are created.
+
+Instances of named entities may have instance identifiers (IDs). Some
+API calls use instance identifiers to create relationships between
+different instances of named entities. Other API calls associate data
+with instances of named entities.
+
+Some named entities must always have instance IDs. In particular, regions
+and frames always have IDs. Task and markers need IDs only if the ID is
+needed in another API call (such as adding a relation or metadata).
+
+The lifetime of instance IDs is distinct from the lifetime of
+instances. This allows various relationships to be specified separate
+from the actual execution of instances. This flexibility comes at the
+expense of extra API calls.
+
+The same ID may not be reused for different instances, unless a previous
+[ref] __itt_id_destroy call for that ID has been issued.
+*/
+
+/** @cond exclude_from_documentation */
+#ifndef ITT_OS_WIN
+#  define ITT_OS_WIN   1
+#endif /* ITT_OS_WIN */
+
+#ifndef ITT_OS_LINUX
+#  define ITT_OS_LINUX 2
+#endif /* ITT_OS_LINUX */
+
+#ifndef ITT_OS_MAC
+#  define ITT_OS_MAC   3
+#endif /* ITT_OS_MAC */
+
+#ifndef ITT_OS_FREEBSD
+#  define ITT_OS_FREEBSD   4
+#endif /* ITT_OS_FREEBSD */
+
+#ifndef ITT_OS
+#  if defined WIN32 || defined _WIN32
+#    define ITT_OS ITT_OS_WIN
+#  elif defined( __APPLE__ ) && defined( __MACH__ )
+#    define ITT_OS ITT_OS_MAC
+#  elif defined( __FreeBSD__ )
+#    define ITT_OS ITT_OS_FREEBSD
+#  else
+#    define ITT_OS ITT_OS_LINUX
+#  endif
+#endif /* ITT_OS */
+
+#ifndef ITT_PLATFORM_WIN
+#  define ITT_PLATFORM_WIN 1
+#endif /* ITT_PLATFORM_WIN */
+
+#ifndef ITT_PLATFORM_POSIX
+#  define ITT_PLATFORM_POSIX 2
+#endif /* ITT_PLATFORM_POSIX */
+
+#ifndef ITT_PLATFORM_MAC
+#  define ITT_PLATFORM_MAC 3
+#endif /* ITT_PLATFORM_MAC */
+
+#ifndef ITT_PLATFORM_FREEBSD
+#  define ITT_PLATFORM_FREEBSD 4
+#endif /* ITT_PLATFORM_FREEBSD */
+
+#ifndef ITT_PLATFORM
+#  if ITT_OS==ITT_OS_WIN
+#    define ITT_PLATFORM ITT_PLATFORM_WIN
+#  elif ITT_OS==ITT_OS_MAC
+#    define ITT_PLATFORM ITT_PLATFORM_MAC
+#  elif ITT_OS==ITT_OS_FREEBSD
+#    define ITT_PLATFORM ITT_PLATFORM_FREEBSD
+#  else
+#    define ITT_PLATFORM ITT_PLATFORM_POSIX
+#  endif
+#endif /* ITT_PLATFORM */
+
+#if defined(_UNICODE) && !defined(UNICODE)
+#define UNICODE
+#endif
+
+#include <stddef.h>
+#if ITT_PLATFORM==ITT_PLATFORM_WIN
+#include <tchar.h>
+#else  /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+#include <stdint.h>
+#if defined(UNICODE) || defined(_UNICODE)
+#include <wchar.h>
+#endif /* UNICODE || _UNICODE */
+#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+
+#ifndef ITTAPI_CDECL
+#  if ITT_PLATFORM==ITT_PLATFORM_WIN
+#    define ITTAPI_CDECL __cdecl
+#  else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+#    if defined _M_IX86 || defined __i386__
+#      define ITTAPI_CDECL __attribute__ ((cdecl))
+#    else  /* _M_IX86 || __i386__ */
+#      define ITTAPI_CDECL /* actual only on x86 platform */
+#    endif /* _M_IX86 || __i386__ */
+#  endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+#endif /* ITTAPI_CDECL */
+
+#ifndef STDCALL
+#  if ITT_PLATFORM==ITT_PLATFORM_WIN
+#    define STDCALL __stdcall
+#  else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+#    if defined _M_IX86 || defined __i386__
+#      define STDCALL __attribute__ ((stdcall))
+#    else  /* _M_IX86 || __i386__ */
+#      define STDCALL /* supported only on x86 platform */
+#    endif /* _M_IX86 || __i386__ */
+#  endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+#endif /* STDCALL */
+
+#define ITTAPI    ITTAPI_CDECL
+#define LIBITTAPI ITTAPI_CDECL
+
+/* TODO: Temporary for compatibility! */
+#define ITTAPI_CALL    ITTAPI_CDECL
+#define LIBITTAPI_CALL ITTAPI_CDECL
+
+#if ITT_PLATFORM==ITT_PLATFORM_WIN
+/* use __forceinline (VC++ specific) */
+#define ITT_INLINE           __forceinline
+#define ITT_INLINE_ATTRIBUTE /* nothing */
+#else  /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+/*
+ * Generally, functions are not inlined unless optimization is specified.
+ * For functions declared inline, this attribute inlines the function even
+ * if no optimization level was specified.
+ */
+#ifdef __STRICT_ANSI__
+#define ITT_INLINE           static
+#define ITT_INLINE_ATTRIBUTE __attribute__((unused))
+#else  /* __STRICT_ANSI__ */
+#define ITT_INLINE           static inline
+#define ITT_INLINE_ATTRIBUTE __attribute__((always_inline, unused))
+#endif /* __STRICT_ANSI__ */
+#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+/** @endcond */
+
+#ifdef INTEL_ITTNOTIFY_ENABLE_LEGACY
+#  if ITT_PLATFORM==ITT_PLATFORM_WIN
+#    pragma message("WARNING!!! Deprecated API is used. Please undefine INTEL_ITTNOTIFY_ENABLE_LEGACY macro")
+#  else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+#    warning "Deprecated API is used. Please undefine INTEL_ITTNOTIFY_ENABLE_LEGACY macro"
+#  endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+#  include "vtune/legacy/ittnotify.h"
+#endif /* INTEL_ITTNOTIFY_ENABLE_LEGACY */
+
+/** @cond exclude_from_documentation */
+/* Helper macro for joining tokens */
+#define ITT_JOIN_AUX(p,n) p##n
+#define ITT_JOIN(p,n)     ITT_JOIN_AUX(p,n)
+
+#ifdef ITT_MAJOR
+#undef ITT_MAJOR
+#endif
+#ifdef ITT_MINOR
+#undef ITT_MINOR
+#endif
+#define ITT_MAJOR     3
+#define ITT_MINOR     0
+
+/* Standard versioning of a token with major and minor version numbers */
+#define ITT_VERSIONIZE(x)    \
+    ITT_JOIN(x,              \
+    ITT_JOIN(_,              \
+    ITT_JOIN(ITT_MAJOR,      \
+    ITT_JOIN(_, ITT_MINOR))))
+
+#ifndef INTEL_ITTNOTIFY_PREFIX
+#  define INTEL_ITTNOTIFY_PREFIX __itt_
+#endif /* INTEL_ITTNOTIFY_PREFIX */
+#ifndef INTEL_ITTNOTIFY_POSTFIX
+#  define INTEL_ITTNOTIFY_POSTFIX _ptr_
+#endif /* INTEL_ITTNOTIFY_POSTFIX */
+
+#define ITTNOTIFY_NAME_AUX(n) ITT_JOIN(INTEL_ITTNOTIFY_PREFIX,n)
+#define ITTNOTIFY_NAME(n)     ITT_VERSIONIZE(ITTNOTIFY_NAME_AUX(ITT_JOIN(n,INTEL_ITTNOTIFY_POSTFIX)))
+
+#define ITTNOTIFY_VOID(n) (!ITTNOTIFY_NAME(n)) ? (void)0 : ITTNOTIFY_NAME(n)
+#define ITTNOTIFY_DATA(n) (!ITTNOTIFY_NAME(n)) ?       0 : ITTNOTIFY_NAME(n)
+
+#define ITTNOTIFY_VOID_D0(n,d)       (!(d)->flags) ? (void)0 : (!ITTNOTIFY_NAME(n)) ? (void)0 : ITTNOTIFY_NAME(n)(d)
+#define ITTNOTIFY_VOID_D1(n,d,x)     (!(d)->flags) ? (void)0 : (!ITTNOTIFY_NAME(n)) ? (void)0 : ITTNOTIFY_NAME(n)(d,x)
+#define ITTNOTIFY_VOID_D2(n,d,x,y)   (!(d)->flags) ? (void)0 : (!ITTNOTIFY_NAME(n)) ? (void)0 : ITTNOTIFY_NAME(n)(d,x,y)
+#define ITTNOTIFY_VOID_D3(n,d,x,y,z) (!(d)->flags) ? (void)0 : (!ITTNOTIFY_NAME(n)) ? (void)0 : ITTNOTIFY_NAME(n)(d,x,y,z)
+#define ITTNOTIFY_VOID_D4(n,d,x,y,z,a)     (!(d)->flags) ? (void)0 : (!ITTNOTIFY_NAME(n)) ? (void)0 : ITTNOTIFY_NAME(n)(d,x,y,z,a)
+#define ITTNOTIFY_VOID_D5(n,d,x,y,z,a,b)   (!(d)->flags) ? (void)0 : (!ITTNOTIFY_NAME(n)) ? (void)0 : ITTNOTIFY_NAME(n)(d,x,y,z,a,b)
+#define ITTNOTIFY_VOID_D6(n,d,x,y,z,a,b,c) (!(d)->flags) ? (void)0 : (!ITTNOTIFY_NAME(n)) ? (void)0 : ITTNOTIFY_NAME(n)(d,x,y,z,a,b,c)
+#define ITTNOTIFY_DATA_D0(n,d)       (!(d)->flags) ?       0 : (!ITTNOTIFY_NAME(n)) ?       0 : ITTNOTIFY_NAME(n)(d)
+#define ITTNOTIFY_DATA_D1(n,d,x)     (!(d)->flags) ?       0 : (!ITTNOTIFY_NAME(n)) ?       0 : ITTNOTIFY_NAME(n)(d,x)
+#define ITTNOTIFY_DATA_D2(n,d,x,y)   (!(d)->flags) ?       0 : (!ITTNOTIFY_NAME(n)) ?       0 : ITTNOTIFY_NAME(n)(d,x,y)
+#define ITTNOTIFY_DATA_D3(n,d,x,y,z) (!(d)->flags) ?       0 : (!ITTNOTIFY_NAME(n)) ?       0 : ITTNOTIFY_NAME(n)(d,x,y,z)
+#define ITTNOTIFY_DATA_D4(n,d,x,y,z,a)     (!(d)->flags) ? 0 : (!ITTNOTIFY_NAME(n)) ?       0 : ITTNOTIFY_NAME(n)(d,x,y,z,a)
+#define ITTNOTIFY_DATA_D5(n,d,x,y,z,a,b)   (!(d)->flags) ? 0 : (!ITTNOTIFY_NAME(n)) ?       0 : ITTNOTIFY_NAME(n)(d,x,y,z,a,b)
+#define ITTNOTIFY_DATA_D6(n,d,x,y,z,a,b,c) (!(d)->flags) ? 0 : (!ITTNOTIFY_NAME(n)) ?       0 : ITTNOTIFY_NAME(n)(d,x,y,z,a,b,c)
+
+#ifdef ITT_STUB
+#undef ITT_STUB
+#endif
+#ifdef ITT_STUBV
+#undef ITT_STUBV
+#endif
+#define ITT_STUBV(api,type,name,args)                             \
+    typedef type (api* ITT_JOIN(ITTNOTIFY_NAME(name),_t)) args;   \
+    extern ITT_JOIN(ITTNOTIFY_NAME(name),_t) ITTNOTIFY_NAME(name);
+#define ITT_STUB ITT_STUBV
+/** @endcond */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/** @cond exclude_from_gpa_documentation */
+/**
+ * @defgroup public Public API
+ * @{
+ * @}
+ */
+
+/**
+ * @defgroup control Collection Control
+ * @ingroup public
+ * General behavior: application continues to run, but no profiling information is being collected
+ *
+ * Pausing occurs not only for the current thread but for all process as well as spawned processes
+ * - Intel(R) Parallel Inspector and Intel(R) Inspector XE:
+ *   - Does not analyze or report errors that involve memory access.
+ *   - Other errors are reported as usual. Pausing data collection in
+ *     Intel(R) Parallel Inspector and Intel(R) Inspector XE
+ *     only pauses tracing and analyzing memory access.
+ *     It does not pause tracing or analyzing threading APIs.
+ *   .
+ * - Intel(R) Parallel Amplifier and Intel(R) VTune(TM) Amplifier XE:
+ *   - Does continue to record when new threads are started.
+ *   .
+ * - Other effects:
+ *   - Possible reduction of runtime overhead.
+ *   .
+ * @{
+ */
+/** @brief Pause collection */
+void ITTAPI __itt_pause(void);
+/** @brief Resume collection */
+void ITTAPI __itt_resume(void);
+/** @brief Detach collection */
+void ITTAPI __itt_detach(void);
+
+/** @cond exclude_from_documentation */
+#ifndef INTEL_NO_MACRO_BODY
+#ifndef INTEL_NO_ITTNOTIFY_API
+ITT_STUBV(ITTAPI, void, pause,  (void))
+ITT_STUBV(ITTAPI, void, resume, (void))
+ITT_STUBV(ITTAPI, void, detach, (void))
+#define __itt_pause      ITTNOTIFY_VOID(pause)
+#define __itt_pause_ptr  ITTNOTIFY_NAME(pause)
+#define __itt_resume     ITTNOTIFY_VOID(resume)
+#define __itt_resume_ptr ITTNOTIFY_NAME(resume)
+#define __itt_detach     ITTNOTIFY_VOID(detach)
+#define __itt_detach_ptr ITTNOTIFY_NAME(detach)
+#else  /* INTEL_NO_ITTNOTIFY_API */
+#define __itt_pause()
+#define __itt_pause_ptr  0
+#define __itt_resume()
+#define __itt_resume_ptr 0
+#define __itt_detach()
+#define __itt_detach_ptr 0
+#endif /* INTEL_NO_ITTNOTIFY_API */
+#else  /* INTEL_NO_MACRO_BODY */
+#define __itt_pause_ptr  0
+#define __itt_resume_ptr 0
+#define __itt_detach_ptr 0
+#endif /* INTEL_NO_MACRO_BODY */
+/** @endcond */
+/** @} control group */
+/** @endcond */
+
+/**
+ * @defgroup threads Threads
+ * @ingroup public
+ * Give names to threads
+ * @{
+ */
+/**
+ * @brief Sets thread name of calling thread
+ * @param[in] name - name of thread
+ */
+#if ITT_PLATFORM==ITT_PLATFORM_WIN
+void ITTAPI __itt_thread_set_nameA(const char    *name);
+void ITTAPI __itt_thread_set_nameW(const wchar_t *name);
+#if defined(UNICODE) || defined(_UNICODE)
+#  define __itt_thread_set_name     __itt_thread_set_nameW
+#  define __itt_thread_set_name_ptr __itt_thread_set_nameW_ptr
+#else /* UNICODE */
+#  define __itt_thread_set_name     __itt_thread_set_nameA
+#  define __itt_thread_set_name_ptr __itt_thread_set_nameA_ptr
+#endif /* UNICODE */
+#else  /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+void ITTAPI __itt_thread_set_name(const char *name);
+#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+
+/** @cond exclude_from_documentation */
+#ifndef INTEL_NO_MACRO_BODY
+#ifndef INTEL_NO_ITTNOTIFY_API
+#if ITT_PLATFORM==ITT_PLATFORM_WIN
+ITT_STUBV(ITTAPI, void, thread_set_nameA, (const char    *name))
+ITT_STUBV(ITTAPI, void, thread_set_nameW, (const wchar_t *name))
+#else  /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+ITT_STUBV(ITTAPI, void, thread_set_name,  (const char    *name))
+#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+#if ITT_PLATFORM==ITT_PLATFORM_WIN
+#define __itt_thread_set_nameA     ITTNOTIFY_VOID(thread_set_nameA)
+#define __itt_thread_set_nameA_ptr ITTNOTIFY_NAME(thread_set_nameA)
+#define __itt_thread_set_nameW     ITTNOTIFY_VOID(thread_set_nameW)
+#define __itt_thread_set_nameW_ptr ITTNOTIFY_NAME(thread_set_nameW)
+#else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+#define __itt_thread_set_name     ITTNOTIFY_VOID(thread_set_name)
+#define __itt_thread_set_name_ptr ITTNOTIFY_NAME(thread_set_name)
+#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+#else  /* INTEL_NO_ITTNOTIFY_API */
+#if ITT_PLATFORM==ITT_PLATFORM_WIN
+#define __itt_thread_set_nameA(name)
+#define __itt_thread_set_nameA_ptr 0
+#define __itt_thread_set_nameW(name)
+#define __itt_thread_set_nameW_ptr 0
+#else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+#define __itt_thread_set_name(name)
+#define __itt_thread_set_name_ptr 0
+#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+#endif /* INTEL_NO_ITTNOTIFY_API */
+#else  /* INTEL_NO_MACRO_BODY */
+#if ITT_PLATFORM==ITT_PLATFORM_WIN
+#define __itt_thread_set_nameA_ptr 0
+#define __itt_thread_set_nameW_ptr 0
+#else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+#define __itt_thread_set_name_ptr 0
+#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+#endif /* INTEL_NO_MACRO_BODY */
+/** @endcond */
+
+/** @cond exclude_from_gpa_documentation */
+
+/**
+ * @brief Mark current thread as ignored from this point on, for the duration of its existence.
+ */
+void ITTAPI __itt_thread_ignore(void);
+
+/** @cond exclude_from_documentation */
+#ifndef INTEL_NO_MACRO_BODY
+#ifndef INTEL_NO_ITTNOTIFY_API
+ITT_STUBV(ITTAPI, void, thread_ignore, (void))
+#define __itt_thread_ignore     ITTNOTIFY_VOID(thread_ignore)
+#define __itt_thread_ignore_ptr ITTNOTIFY_NAME(thread_ignore)
+#else  /* INTEL_NO_ITTNOTIFY_API */
+#define __itt_thread_ignore()
+#define __itt_thread_ignore_ptr 0
+#endif /* INTEL_NO_ITTNOTIFY_API */
+#else  /* INTEL_NO_MACRO_BODY */
+#define __itt_thread_ignore_ptr 0
+#endif /* INTEL_NO_MACRO_BODY */
+/** @endcond */
+/** @} threads group */
+
+/**
+ * @defgroup suppress Error suppression
+ * @ingroup public
+ * General behavior: application continues to run, but errors are suppressed
+ *
+ * @{
+ */
+
+/*****************************************************************//**
+ * @name group of functions used for error suppression in correctness tools
+ *********************************************************************/
+/** @{ */
+/**
+ * @hideinitializer
+ * @brief possible value for suppression mask
+ */
+#define __itt_suppress_all_errors 0x7fffffff
+
+/**
+ * @hideinitializer
+ * @brief possible value for suppression mask (suppresses errors from threading analysis)
+ */
+#define __itt_suppress_threading_errors 0x000000ff
+
+/**
+ * @hideinitializer
+ * @brief possible value for suppression mask (suppresses errors from memory analysis)
+ */
+#define __itt_suppress_memory_errors 0x0000ff00
+
+/**
+ * @brief Start suppressing errors identified in mask on this thread
+ */
+void ITTAPI __itt_suppress_push(unsigned int mask);
+
+/** @cond exclude_from_documentation */
+#ifndef INTEL_NO_MACRO_BODY
+#ifndef INTEL_NO_ITTNOTIFY_API
+ITT_STUBV(ITTAPI, void, suppress_push, (unsigned int mask))
+#define __itt_suppress_push     ITTNOTIFY_VOID(suppress_push)
+#define __itt_suppress_push_ptr ITTNOTIFY_NAME(suppress_push)
+#else  /* INTEL_NO_ITTNOTIFY_API */
+#define __itt_suppress_push(mask)
+#define __itt_suppress_push_ptr 0
+#endif /* INTEL_NO_ITTNOTIFY_API */
+#else  /* INTEL_NO_MACRO_BODY */
+#define __itt_suppress_push_ptr 0
+#endif /* INTEL_NO_MACRO_BODY */
+/** @endcond */
+
+/**
+ * @brief Undo the effects of the matching call to __itt_suppress_push
+ */
+void ITTAPI __itt_suppress_pop(void);
+
+/** @cond exclude_from_documentation */
+#ifndef INTEL_NO_MACRO_BODY
+#ifndef INTEL_NO_ITTNOTIFY_API
+ITT_STUBV(ITTAPI, void, suppress_pop, (void))
+#define __itt_suppress_pop     ITTNOTIFY_VOID(suppress_pop)
+#define __itt_suppress_pop_ptr ITTNOTIFY_NAME(suppress_pop)
+#else  /* INTEL_NO_ITTNOTIFY_API */
+#define __itt_suppress_pop()
+#define __itt_suppress_pop_ptr 0
+#endif /* INTEL_NO_ITTNOTIFY_API */
+#else  /* INTEL_NO_MACRO_BODY */
+#define __itt_suppress_pop_ptr 0
+#endif /* INTEL_NO_MACRO_BODY */
+/** @endcond */
+
+/**
+ * @enum __itt_model_disable
+ * @brief Enumerator for the disable methods
+ */
+typedef enum __itt_suppress_mode {
+    __itt_unsuppress_range,
+    __itt_suppress_range
+} __itt_suppress_mode_t;
+
+/**
+ * @brief Mark a range of memory for error suppression or unsuppression for error types included in mask
+ */
+void ITTAPI __itt_suppress_mark_range(__itt_suppress_mode_t mode, unsigned int mask, void * address, size_t size);
+
+/** @cond exclude_from_documentation */
+#ifndef INTEL_NO_MACRO_BODY
+#ifndef INTEL_NO_ITTNOTIFY_API
+ITT_STUBV(ITTAPI, void, suppress_mark_range, (__itt_suppress_mode_t mode, unsigned int mask, void * address, size_t size))
+#define __itt_suppress_mark_range     ITTNOTIFY_VOID(suppress_mark_range)
+#define __itt_suppress_mark_range_ptr ITTNOTIFY_NAME(suppress_mark_range)
+#else  /* INTEL_NO_ITTNOTIFY_API */
+#define __itt_suppress_mark_range(mask)
+#define __itt_suppress_mark_range_ptr 0
+#endif /* INTEL_NO_ITTNOTIFY_API */
+#else  /* INTEL_NO_MACRO_BODY */
+#define __itt_suppress_mark_range_ptr 0
+#endif /* INTEL_NO_MACRO_BODY */
+/** @endcond */
+
+/**
+ * @brief Undo the effect of a matching call to __itt_suppress_mark_range.   If not matching
+ *        call is found, nothing is changed.
+ */
+void ITTAPI __itt_suppress_clear_range(__itt_suppress_mode_t mode, unsigned int mask, void * address, size_t size);
+
+/** @cond exclude_from_documentation */
+#ifndef INTEL_NO_MACRO_BODY
+#ifndef INTEL_NO_ITTNOTIFY_API
+ITT_STUBV(ITTAPI, void, suppress_clear_range, (__itt_suppress_mode_t mode, unsigned int mask, void * address, size_t size))
+#define __itt_suppress_clear_range     ITTNOTIFY_VOID(suppress_clear_range)
+#define __itt_suppress_clear_range_ptr ITTNOTIFY_NAME(suppress_clear_range)
+#else  /* INTEL_NO_ITTNOTIFY_API */
+#define __itt_suppress_clear_range(mask)
+#define __itt_suppress_clear_range_ptr 0
+#endif /* INTEL_NO_ITTNOTIFY_API */
+#else  /* INTEL_NO_MACRO_BODY */
+#define __itt_suppress_clear_range_ptr 0
+#endif /* INTEL_NO_MACRO_BODY */
+/** @endcond */
+/** @} */
+/** @} suppress group */
+
+/**
+ * @defgroup sync Synchronization
+ * @ingroup public
+ * Indicate user-written synchronization code
+ * @{
+ */
+/**
+ * @hideinitializer
+ * @brief possible value of attribute argument for sync object type
+ */
+#define __itt_attr_barrier 1
+
+/**
+ * @hideinitializer
+ * @brief possible value of attribute argument for sync object type
+ */
+#define __itt_attr_mutex   2
+
+/**
+@brief Name a synchronization object
+@param[in] addr       Handle for the synchronization object. You should
+use a real address to uniquely identify the synchronization object.
+@param[in] objtype    null-terminated object type string. If NULL is
+passed, the name will be "User Synchronization".
+@param[in] objname    null-terminated object name string. If NULL,
+no name will be assigned to the object.
+@param[in] attribute  one of [#__itt_attr_barrier, #__itt_attr_mutex]
+ */
+
+#if ITT_PLATFORM==ITT_PLATFORM_WIN
+void ITTAPI __itt_sync_createA(void *addr, const char    *objtype, const char    *objname, int attribute);
+void ITTAPI __itt_sync_createW(void *addr, const wchar_t *objtype, const wchar_t *objname, int attribute);
+#if defined(UNICODE) || defined(_UNICODE)
+#  define __itt_sync_create     __itt_sync_createW
+#  define __itt_sync_create_ptr __itt_sync_createW_ptr
+#else /* UNICODE */
+#  define __itt_sync_create     __itt_sync_createA
+#  define __itt_sync_create_ptr __itt_sync_createA_ptr
+#endif /* UNICODE */
+#else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+void ITTAPI __itt_sync_create (void *addr, const char *objtype, const char *objname, int attribute);
+#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+
+/** @cond exclude_from_documentation */
+#ifndef INTEL_NO_MACRO_BODY
+#ifndef INTEL_NO_ITTNOTIFY_API
+#if ITT_PLATFORM==ITT_PLATFORM_WIN
+ITT_STUBV(ITTAPI, void, sync_createA, (void *addr, const char    *objtype, const char    *objname, int attribute))
+ITT_STUBV(ITTAPI, void, sync_createW, (void *addr, const wchar_t *objtype, const wchar_t *objname, int attribute))
+#else  /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+ITT_STUBV(ITTAPI, void, sync_create,  (void *addr, const char*    objtype, const char*    objname, int attribute))
+#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+#if ITT_PLATFORM==ITT_PLATFORM_WIN
+#define __itt_sync_createA     ITTNOTIFY_VOID(sync_createA)
+#define __itt_sync_createA_ptr ITTNOTIFY_NAME(sync_createA)
+#define __itt_sync_createW     ITTNOTIFY_VOID(sync_createW)
+#define __itt_sync_createW_ptr ITTNOTIFY_NAME(sync_createW)
+#else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+#define __itt_sync_create     ITTNOTIFY_VOID(sync_create)
+#define __itt_sync_create_ptr ITTNOTIFY_NAME(sync_create)
+#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+#else  /* INTEL_NO_ITTNOTIFY_API */
+#if ITT_PLATFORM==ITT_PLATFORM_WIN
+#define __itt_sync_createA(addr, objtype, objname, attribute)
+#define __itt_sync_createA_ptr 0
+#define __itt_sync_createW(addr, objtype, objname, attribute)
+#define __itt_sync_createW_ptr 0
+#else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+#define __itt_sync_create(addr, objtype, objname, attribute)
+#define __itt_sync_create_ptr 0
+#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+#endif /* INTEL_NO_ITTNOTIFY_API */
+#else  /* INTEL_NO_MACRO_BODY */
+#if ITT_PLATFOR