Merge inbound to mozilla-central. a=merge
authorMargareta Eliza Balazs <ebalazs@mozilla.com>
Wed, 28 Mar 2018 00:48:11 +0300
changeset 410314 56d6db4ad38c869d0bbc2aea449a4a382f109163
parent 410313 e62adc04f769d092791dd5edd36832cecb718cf9 (current diff)
parent 410219 adfa3b56cbdc68827ad9603fd338ab321632858b (diff)
child 410315 aaa66e08731c17fc10fc2f7c5e2c439a31b8b5a9
child 410360 c33772b8aecadd6251a75af23cf653995064dfd7
push id101443
push userebalazs@mozilla.com
push dateTue, 27 Mar 2018 22:01:06 +0000
treeherdermozilla-inbound@aaa66e08731c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone61.0a1
first release with
nightly linux32
56d6db4ad38c / 61.0a1 / 20180327220126 / files
nightly linux64
56d6db4ad38c / 61.0a1 / 20180327220126 / files
nightly mac
56d6db4ad38c / 61.0a1 / 20180327220126 / files
nightly win32
56d6db4ad38c / 61.0a1 / 20180327220126 / files
nightly win64
56d6db4ad38c / 61.0a1 / 20180327220126 / 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 inbound to mozilla-central. a=merge
browser/base/content/test/performance/browser_windowopen_flicker.js
js/src/jit-test/tests/basic/bug1341321.js
js/src/jit-test/tests/basic/bug1341358.js
js/src/jit-test/tests/basic/bug1344315.js
js/src/jit-test/tests/basic/bug1411947.js
js/src/jit-test/tests/basic/bug1412298.js
js/src/jit-test/tests/basic/cooperative-threading-interrupt.js
js/src/jit-test/tests/basic/cooperative-threading.js
js/src/jit-test/tests/parser/bug-1433013.js
js/src/jit-test/tests/profiler/bug1352507-2.js
js/src/jit-test/tests/promise/cooperative-thread.js
js/src/jsapi.cpp
js/src/jsapi.h
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1067,16 +1067,20 @@ pref("security.sandbox.windows.log.stack
 #endif
 
 // This controls the strength of the Windows GPU process sandbox.  Changes
 // will require restart.
 // For information on what the level number means, see
 // SetSecurityLevelForGPUProcess() in
 // security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp
 pref("security.sandbox.gpu.level", 0);
+
+// Controls whether we disable win32k for the GMP processes.
+// true means that win32k system calls are not permitted.
+pref("security.sandbox.gmp.win32k-disable", true);
 #endif
 
 #if defined(XP_MACOSX) && defined(MOZ_SANDBOX) && defined(MOZ_CONTENT_SANDBOX)
 // This pref is discussed in bug 1083344, the naming is inspired from its
 // Windows counterpart, but on Mac it's an integer which means:
 // 0 -> "no sandbox" (nightly only)
 // 1 -> "preliminary content sandboxing enabled: write access to
 //       home directory is prevented"
--- a/browser/base/content/aboutNetError.xhtml
+++ b/browser/base/content/aboutNetError.xhtml
@@ -137,18 +137,16 @@
           // Toggling the advanced panel must ensure that the debugging
           // information panel is hidden as well, since it's opened by the
           // error code link in the advanced panel.
           var div = document.getElementById("certificateErrorDebugInformation");
           div.style.display = "none";
         }
 
         disallowCertOverridesIfNeeded();
-
-        document.getElementById("badCertTechnicalInfo").textContent = getDescription();
       }
 
       function disallowCertOverridesIfNeeded() {
         var cssClass = getCSSClass();
         // Disallow overrides if this is a Strict-Transport-Security
         // host and the cert is bad (STS Spec section 7.3) or if the
         // certerror is in a frame (bug 633691).
         if (cssClass == "badStsCert" || window != top) {
@@ -195,17 +193,17 @@
         }
 
         // eslint-disable-next-line no-unsanitized/property
         document.querySelector(".title-text").innerHTML = errTitle.innerHTML;
 
         var sd = document.getElementById("errorShortDescText");
         if (sd) {
           if (gIsCertError) {
-          // eslint-disable-next-line no-unsanitized/property
+            // eslint-disable-next-line no-unsanitized/property
             sd.innerHTML = errDesc.innerHTML;
           } else {
             sd.textContent = getDescription();
           }
         }
         if (showCaptivePortalUI) {
           initPageCaptivePortal();
           return;
@@ -215,17 +213,17 @@
           return;
         }
         addAutofocus("errorTryAgain");
 
         document.body.classList.add("neterror");
 
         var ld = document.getElementById("errorLongDesc");
         if (ld) {
-        // eslint-disable-next-line no-unsanitized/property
+          // eslint-disable-next-line no-unsanitized/property
           ld.innerHTML = errDesc.innerHTML;
         }
 
         if (err == "sslv3Used") {
           document.getElementById("learnMoreContainer").style.display = "block";
           let learnMoreLink = document.getElementById("learnMoreLink");
           learnMoreLink.href = "https://support.mozilla.org/kb/how-resolve-sslv3-error-messages-firefox";
           document.body.className = "certerror";
@@ -260,23 +258,24 @@
 
         if (err == "cspBlocked") {
           // Remove the "Try again" button for CSP violations, since it's
           // almost certainly useless. (Bug 553180)
           document.getElementById("netErrorButtonContainer").style.display = "none";
         }
 
         window.addEventListener("AboutNetErrorOptions", function(evt) {
-        // Pinning errors are of type nssFailure2
+          // Pinning errors are of type nssFailure2
           if (getErrorCode() == "nssFailure2") {
+            let shortDesc = document.getElementById("errorShortDescText").textContent;
             document.getElementById("learnMoreContainer").style.display = "block";
             let learnMoreLink = document.getElementById("learnMoreLink");
             // nssFailure2 also gets us other non-overrideable errors. Choose
             // a "learn more" link based on description:
-            if (getDescription().includes("MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE")) {
+            if (shortDesc.includes("MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE")) {
               learnMoreLink.href = "https://support.mozilla.org/kb/certificate-pinning-reports";
             }
 
             var options = JSON.parse(evt.detail);
             if (options && options.enabled) {
               var checkbox = document.getElementById("automaticallyReportInFuture");
               showCertificateErrorReporting();
               if (options.automatic) {
@@ -292,17 +291,17 @@
                 });
             }
             const hasPrefStyleError = [
               "interrupted", // This happens with subresources that are above the max tls
               "SSL_ERROR_PROTOCOL_VERSION_ALERT",
               "SSL_ERROR_UNSUPPORTED_VERSION",
               "SSL_ERROR_NO_CYPHER_OVERLAP",
               "SSL_ERROR_NO_CIPHERS_SUPPORTED"
-            ].some((substring) => getDescription().includes(substring));
+            ].some((substring) => shortDesc.includes(substring));
             // If it looks like an error that is user config based
             if (getErrorCode() == "nssFailure2" && hasPrefStyleError && options && options.changedCertPrefs) {
               showPrefChangeContainer();
             }
           }
           if (getErrorCode() == "sslv3Used") {
             document.getElementById("advancedButton").style.display = "none";
           }
@@ -317,33 +316,29 @@
           // again won't help.
           document.getElementById("errorTryAgain").style.display = "none";
 
           var container = document.getElementById("errorLongDesc");
           for (var span of container.querySelectorAll("span.hostname")) {
             span.textContent = document.location.hostname;
           }
         }
-
-        addDomainErrorLinks();
       }
 
       function initPageCaptivePortal() {
         document.body.className = "captiveportal";
         document.getElementById("openPortalLoginPageButton")
                 .addEventListener("click", () => {
           let event = new CustomEvent("AboutNetErrorOpenCaptivePortal", {bubbles: true});
           document.dispatchEvent(event);
         });
 
         addAutofocus("openPortalLoginPageButton");
         setupAdvancedButton();
 
-        addDomainErrorLinks();
-
         // When the portal is freed, an event is generated by the frame script
         // that we can pick up and attempt to reload the original page.
         window.addEventListener("AboutNetErrorCaptivePortalFreed", () => {
           document.location.reload();
         });
       }
 
       function initPageCertError() {
@@ -373,18 +368,16 @@
 
             // set the checkbox
             checkbox.checked = !!options.automatic;
           }
         }, true, true);
 
         let event = new CustomEvent("AboutNetErrorLoad", {bubbles: true});
         document.getElementById("advancedButton").dispatchEvent(event);
-
-        addDomainErrorLinks();
       }
 
       /* Only do autofocus if we're the toplevel frame; otherwise we
          don't want to call attention to ourselves!  The key part is
          that autofocus happens on insertion into the tree, so we
          can remove the button, add @autofocus, and reinsert the
          button.
       */
@@ -393,139 +386,16 @@
             var button = document.getElementById(buttonId);
             var parent = button.parentNode;
             button.remove();
             button.setAttribute("autofocus", "true");
             parent.insertAdjacentElement(position, button);
         }
       }
 
-      /* Try to preserve the links contained in the error description, like
-         the error code.
-
-         Also, in the case of SSL error pages about domain mismatch, see if
-         we can hyperlink the user to the correct site.  We don't want
-         to do this generically since it allows MitM attacks to redirect
-         users to a site under attacker control, but in certain cases
-         it is safe (and helpful!) to do so.  Bug 402210
-      */
-      function addDomainErrorLinks() {
-        // Rather than textContent, we need to treat description as HTML
-        var sdid = gIsCertError ? "badCertTechnicalInfo" : "errorShortDescText";
-        var sd = document.getElementById(sdid);
-        if (sd) {
-          var desc = getDescription();
-
-          // sanitize description text - see bug 441169
-
-          // First, find the index of the <a> tags we care about, being
-          // careful not to use an over-greedy regex.
-          var codeRe = /<a id="errorCode" title="([^"]+)">/;
-          var codeResult = codeRe.exec(desc);
-          var domainRe = /<a id="cert_domain_link" title="([^"]+)">/;
-          var domainResult = domainRe.exec(desc);
-
-          // The order of these links in the description is fixed in
-          // TransportSecurityInfo.cpp:formatOverridableCertErrorMessage.
-          var firstResult = domainResult;
-          if (!domainResult)
-            firstResult = codeResult;
-          if (!firstResult)
-            return;
-          // Remove sd's existing children
-          sd.textContent = "";
-
-          // Everything up to the first link should be text content.
-          sd.appendChild(document.createTextNode(desc.slice(0, firstResult.index)));
-
-          // Now create the actual links.
-          if (domainResult) {
-            createLink(sd, "cert_domain_link", domainResult[1]);
-            // Append text for anything between the two links.
-            sd.appendChild(document.createTextNode(desc.slice(desc.indexOf("</a>") + "</a>".length, codeResult.index)));
-          }
-          createLink(sd, "errorCode", codeResult[1]);
-
-          // Finally, append text for anything after the last closing </a>.
-          sd.appendChild(document.createTextNode(desc.slice(desc.lastIndexOf("</a>") + "</a>".length)));
-        }
-
-        if (gIsCertError) {
-          // Initialize the error code link embedded in the error message to
-          // display debug information about the cert error.
-          var errorCode = document.getElementById("errorCode");
-          if (errorCode) {
-            errorCode.href = "javascript:void(0)";
-            errorCode.addEventListener("click", () => {
-              let debugInfo = document.getElementById("certificateErrorDebugInformation");
-              debugInfo.style.display = "block";
-              debugInfo.scrollIntoView({block: "start", behavior: "smooth"});
-            });
-          }
-        }
-
-        // Initialize the cert domain link.
-        var link = document.getElementById("cert_domain_link");
-        if (!link)
-          return;
-
-        var okHost = link.getAttribute("title");
-        var thisHost = document.location.hostname;
-        var proto = document.location.protocol;
-
-        // If okHost is a wildcard domain ("*.example.com") let's
-        // use "www" instead.  "*.example.com" isn't going to
-        // get anyone anywhere useful. bug 432491
-        okHost = okHost.replace(/^\*\./, "www.");
-
-        /* case #1:
-         * example.com uses an invalid security certificate.
-         *
-         * The certificate is only valid for www.example.com
-         *
-         * Make sure to include the "." ahead of thisHost so that
-         * a MitM attack on paypal.com doesn't hyperlink to "notpaypal.com"
-         *
-         * We'd normally just use a RegExp here except that we lack a
-         * library function to escape them properly (bug 248062), and
-         * domain names are famous for having '.' characters in them,
-         * which would allow spurious and possibly hostile matches.
-         */
-        if (okHost.endsWith("." + thisHost))
-          link.href = proto + okHost;
-
-        /* case #2:
-         * browser.garage.maemo.org uses an invalid security certificate.
-         *
-         * The certificate is only valid for garage.maemo.org
-         */
-        if (thisHost.endsWith("." + okHost))
-          link.href = proto + okHost;
-
-        // If we set a link, meaning there's something helpful for
-        // the user here, expand the section by default
-        if (link.href && getCSSClass() != "expertBadCert") {
-          toggleDisplay(document.getElementById("badCertAdvancedPanel"));
-          if (gIsCertError) {
-            // Toggling the advanced panel must ensure that the debugging
-            // information panel is hidden as well, since it's opened by the
-            // error code link in the advanced panel.
-            var div = document.getElementById("certificateErrorDebugInformation");
-            div.style.display = "none";
-          }
-        }
-      }
-
-      function createLink(el, id, text) {
-        var anchorEl = document.createElement("a");
-        anchorEl.setAttribute("id", id);
-        anchorEl.setAttribute("title", text);
-        anchorEl.appendChild(document.createTextNode(text));
-        el.appendChild(anchorEl);
-      }
     ]]></script>
   </head>
 
   <body dir="&locale.dir;">
     <!-- ERROR ITEM CONTAINER (removed during loading to avoid bug 39098) -->
     <div id="errorContainer">
       <div id="errorPageTitlesContainer">
         <span id="ept_nssBadCert">&certerror.pagetitle1;</span>
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -3028,26 +3028,27 @@ var BrowserOnClick = {
         goBackFromErrorPage();
       break;
     }
   },
 
   onCertError(browser, elementId, isTopFrame, location, securityInfoAsString, frameId) {
     let secHistogram = Services.telemetry.getHistogramById("SECURITY_UI");
     let securityInfo;
+    let sslStatus;
 
     switch (elementId) {
       case "exceptionDialogButton":
         if (isTopFrame) {
           secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_BAD_CERT_TOP_CLICK_ADD_EXCEPTION);
         }
 
         securityInfo = getSecurityInfo(securityInfoAsString);
-        let sslStatus = securityInfo.QueryInterface(Ci.nsISSLStatusProvider)
-                                    .SSLStatus;
+        sslStatus = securityInfo.QueryInterface(Ci.nsISSLStatusProvider)
+                                .SSLStatus;
         let params = { exceptionAdded: false,
                        sslStatus };
 
         try {
           switch (Services.prefs.getIntPref("browser.ssl_override_behavior")) {
             case 2 : // Pre-fetch & pre-populate
               params.prefetchCert = true;
             case 1 : // Pre-populate
@@ -3074,22 +3075,38 @@ var BrowserOnClick = {
         break;
 
       case "advancedButton":
         if (isTopFrame) {
           secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_BAD_CERT_TOP_UNDERSTAND_RISKS);
         }
 
         securityInfo = getSecurityInfo(securityInfoAsString);
+        sslStatus = securityInfo.QueryInterface(Ci.nsISSLStatusProvider)
+                                .SSLStatus;
         let errorInfo = getDetailedCertErrorInfo(location,
                                                  securityInfo);
+        let validityInfo = {
+          notAfter: sslStatus.serverCert.validity.notAfter,
+          notBefore: sslStatus.serverCert.validity.notBefore,
+          notAfterLocalTime: sslStatus.serverCert.validity.notAfterLocalTime,
+          notBeforeLocalTime: sslStatus.serverCert.validity.notBeforeLocalTime,
+        };
         browser.messageManager.sendAsyncMessage("CertErrorDetails", {
             code: securityInfo.errorCode,
             info: errorInfo,
-            frameId,
+            codeString: securityInfo.errorCodeString,
+            certIsUntrusted: sslStatus.isUntrusted,
+            certIsSelfSigned: sslStatus.serverCert.isSelfSigned,
+            certSubjectAltNames: sslStatus.serverCert.subjectAltNames,
+            validity: validityInfo,
+            url: location,
+            isDomainMismatch: sslStatus.isDomainMismatch,
+            isNotValidAtThisTime: sslStatus.isNotValidAtThisTime,
+            frameId
         });
         break;
 
       case "copyToClipboard":
         const gClipboardHelper = Cc["@mozilla.org/widget/clipboardhelper;1"]
                                     .getService(Ci.nsIClipboardHelper);
         securityInfo = getSecurityInfo(securityInfoAsString);
         let detailedInfo = getDetailedCertErrorInfo(location,
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -27,16 +27,23 @@ XPCOMUtils.defineLazyModuleGetters(this,
   PlacesUIUtils: "resource:///modules/PlacesUIUtils.jsm",
   SafeBrowsing: "resource://gre/modules/SafeBrowsing.jsm",
   Utils: "resource://gre/modules/sessionstore/Utils.jsm",
   WebNavigationFrames: "resource://gre/modules/WebNavigationFrames.jsm",
   Feeds: "resource:///modules/Feeds.jsm",
   ContextMenu: "resource:///modules/ContextMenu.jsm",
 });
 
+XPCOMUtils.defineLazyGetter(this, "gPipNSSBundle", function() {
+  return Services.strings.createBundle("chrome://pipnss/locale/pipnss.properties");
+});
+XPCOMUtils.defineLazyGetter(this, "gNSSErrorsBundle", function() {
+  return Services.strings.createBundle("chrome://pipnss/locale/nsserrors.properties");
+});
+
 // TabChildGlobal
 var global = this;
 
 var contextMenu = this.contextMenu = new ContextMenu(global);
 
 // Load the form validation popup handler
 var formSubmitObserver = new FormSubmitObserver(content, this);
 
@@ -65,22 +72,32 @@ addEventListener("blur", function(event)
   LoginManagerContent.onUsernameInput(event);
 });
 
 const SEC_ERROR_BASE          = Ci.nsINSSErrorsService.NSS_SEC_ERROR_BASE;
 const MOZILLA_PKIX_ERROR_BASE = Ci.nsINSSErrorsService.MOZILLA_PKIX_ERROR_BASE;
 
 const SEC_ERROR_EXPIRED_CERTIFICATE                = SEC_ERROR_BASE + 11;
 const SEC_ERROR_UNKNOWN_ISSUER                     = SEC_ERROR_BASE + 13;
+const SEC_ERROR_UNTRUSTED_ISSUER                   = SEC_ERROR_BASE + 20;
+const SEC_ERROR_UNTRUSTED_CERT                     = SEC_ERROR_BASE + 21;
 const SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE         = SEC_ERROR_BASE + 30;
+const SEC_ERROR_CA_CERT_INVALID                    = SEC_ERROR_BASE + 36;
 const SEC_ERROR_OCSP_FUTURE_RESPONSE               = SEC_ERROR_BASE + 131;
 const SEC_ERROR_OCSP_OLD_RESPONSE                  = SEC_ERROR_BASE + 132;
+const SEC_ERROR_REUSED_ISSUER_AND_SERIAL           = SEC_ERROR_BASE + 138;
+const SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED  = SEC_ERROR_BASE + 176;
 const MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE = MOZILLA_PKIX_ERROR_BASE + 5;
 const MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE = MOZILLA_PKIX_ERROR_BASE + 6;
 
+
+const SSL_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SSL_ERROR_BASE;
+const SSL_ERROR_SSL_DISABLED  = SSL_ERROR_BASE + 20;
+const SSL_ERROR_SSL2_DISABLED  = SSL_ERROR_BASE + 14;
+
 const PREF_SERVICES_SETTINGS_CLOCK_SKEW_SECONDS = "services.settings.clock_skew_seconds";
 const PREF_SERVICES_SETTINGS_LAST_FETCHED       = "services.settings.last_update_seconds";
 
 const PREF_SSL_IMPACT_ROOTS = ["security.tls.version.", "security.ssl3."];
 
 
 function getSerializedSecurityInfo(docShell) {
   let serhelper = Cc["@mozilla.org/network/serialization-helper;1"]
@@ -275,21 +292,199 @@ var AboutNetAndCertErrorListener = {
       notAfter = Math.min(notAfter, cert.validity.notAfter);
     }
     // nsIX509Cert reports in PR_Date terms, which uses microseconds. Convert:
     notBefore /= 1000;
     notAfter /= 1000;
     return {notBefore, notAfter};
   },
 
+  _setTechDetails(input, doc) {
+    // CSS class and error code are set from nsDocShell.
+    let searchParams = new URLSearchParams(doc.documentURI.split("?")[1]);
+    let cssClass = searchParams.get("s");
+    let error = searchParams.get("e");
+    let technicalInfo = doc.getElementById("badCertTechnicalInfo");
+    technicalInfo.textContent = "";
+
+    let uri = Services.io.newURI(input.data.url);
+    let hostString = uri.host;
+    if (uri.port != 443 && uri.port != -1) {
+      hostString = uri.hostPort;
+    }
+
+    let msg1 = gPipNSSBundle.formatStringFromName("certErrorIntro",
+                                                  [hostString], 1);
+    msg1 += "\n\n";
+
+    if (input.data.certIsUntrusted && !input.data.certIsSelfSigned) {
+      switch (input.data.code) {
+        case SEC_ERROR_UNKNOWN_ISSUER:
+          msg1 += gPipNSSBundle.GetStringFromName("certErrorTrust_UnknownIssuer") + "\n";
+          msg1 += gPipNSSBundle.GetStringFromName("certErrorTrust_UnknownIssuer2") + "\n";
+          msg1 += gPipNSSBundle.GetStringFromName("certErrorTrust_UnknownIssuer3") + "\n";
+          break;
+        case SEC_ERROR_CA_CERT_INVALID:
+          msg1 += gPipNSSBundle.GetStringFromName("certErrorTrust_CaInvalid") + "\n";
+          break;
+        case SEC_ERROR_UNTRUSTED_ISSUER:
+          msg1 += gPipNSSBundle.GetStringFromName("certErrorTrust_Issuer") + "\n";
+          break;
+        case SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED:
+          msg1 += gPipNSSBundle.GetStringFromName("certErrorTrust_SignatureAlgorithmDisabled") + "\n";
+          break;
+        case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
+          msg1 += gPipNSSBundle.GetStringFromName("certErrorTrust_ExpiredIssuer") + "\n";
+          break;
+        default:
+          msg1 += gPipNSSBundle.GetStringFromName("certErrorTrust_Untrusted") + "\n";
+      }
+    }
+    if (input.data.certIsUntrusted && input.data.certIsSelfSigned) {
+      msg1 += gPipNSSBundle.GetStringFromName("certErrorTrust_SelfSigned") + "\n";
+    }
+
+    technicalInfo.appendChild(doc.createTextNode(msg1));
+
+    if (input.data.isDomainMismatch) {
+      let subjectAltNames = input.data.certSubjectAltNames.split(",");
+      let numSubjectAltNames = subjectAltNames.length;
+      let msgPrefix = "";
+      if (numSubjectAltNames != 0) {
+        if (numSubjectAltNames == 1) {
+          msgPrefix = gPipNSSBundle.GetStringFromName("certErrorMismatchSinglePrefix");
+
+          // Let's check if we want to make this a link.
+          let okHost = input.data.certSubjectAltNames;
+          let href = "";
+          let thisHost = doc.location.hostname;
+          let proto = doc.location.protocol + "//";
+          // If okHost is a wildcard domain ("*.example.com") let's
+          // use "www" instead.  "*.example.com" isn't going to
+          // get anyone anywhere useful. bug 432491
+          okHost = okHost.replace(/^\*\./, "www.");
+          /* case #1:
+           * example.com uses an invalid security certificate.
+           *
+           * The certificate is only valid for www.example.com
+           *
+           * Make sure to include the "." ahead of thisHost so that
+           * a MitM attack on paypal.com doesn't hyperlink to "notpaypal.com"
+           *
+           * We'd normally just use a RegExp here except that we lack a
+           * library function to escape them properly (bug 248062), and
+           * domain names are famous for having '.' characters in them,
+           * which would allow spurious and possibly hostile matches.
+           */
+          if (okHost.endsWith("." + thisHost)) {
+            href = proto + okHost;
+          }
+          /* case #2:
+           * browser.garage.maemo.org uses an invalid security certificate.
+           *
+           * The certificate is only valid for garage.maemo.org
+           */
+          if (thisHost.endsWith("." + okHost)) {
+            href = proto + okHost;
+          }
+
+          // If we set a link, meaning there's something helpful for
+          // the user here, expand the section by default
+          if (href && cssClass != "expertBadCert") {
+            doc.getElementById("badCertAdvancedPanel").style.display = "block";
+            if (error == "nssBadCert") {
+              // Toggling the advanced panel must ensure that the debugging
+              // information panel is hidden as well, since it's opened by the
+              // error code link in the advanced panel.
+              var div = doc.getElementById("certificateErrorDebugInformation");
+              div.style.display = "none";
+            }
+          }
+
+          // Set the link if we want it.
+          if (href) {
+            let referrerlink = doc.createElement("a");
+            referrerlink.append(input.data.certSubjectAltNames);
+            referrerlink.title = input.data.certSubjectAltNames;
+            referrerlink.id = "cert_domain_link";
+            referrerlink.href = href;
+            let fragment = BrowserUtils.getLocalizedFragment(doc, msgPrefix,
+                                                             referrerlink);
+            technicalInfo.appendChild(fragment);
+          } else {
+            let fragment = BrowserUtils.getLocalizedFragment(doc,
+                                                             msgPrefix,
+                                                             input.data.certSubjectAltNames);
+            technicalInfo.appendChild(fragment);
+          }
+          technicalInfo.append("\n");
+        } else {
+          let msg = gPipNSSBundle.GetStringFromName("certErrorMismatchMultiple") + "\n";
+          for (let i = 0; i < numSubjectAltNames; i++) {
+            msg += subjectAltNames[i];
+            if (i != (numSubjectAltNames - 1)) {
+              msg += ", ";
+            }
+          }
+          technicalInfo.append(msg + "\n");
+        }
+      } else {
+        let msg = gPipNSSBundle.formatStringFromName("certErrorMismatch",
+                                                     [hostString], 1);
+        technicalInfo.append(msg + "\n");
+      }
+    }
+
+    if (input.data.isNotValidAtThisTime) {
+      let nowTime = new Date().getTime() * 1000;
+      let dateOptions = { year: "numeric", month: "long", day: "numeric", hour: "numeric", minute: "numeric" };
+      let now = new Services.intl.DateTimeFormat(undefined, dateOptions).format(new Date());
+      let msg = "";
+      if (input.data.validity.notBefore) {
+        if (nowTime > input.data.validity.notAfter) {
+          msg += gPipNSSBundle.formatStringFromName("certErrorExpiredNow",
+                                                    [input.data.validity.notAfterLocalTime, now], 2) + "\n";
+        } else {
+          msg += gPipNSSBundle.formatStringFromName("certErrorNotYetValidNow",
+                                                    [input.data.validity.notBeforeLocalTime, now], 2) + "\n";
+        }
+      } else {
+        // If something goes wrong, we assume the cert expired.
+        msg += gPipNSSBundle.formatStringFromName("certErrorExpiredNow",
+                                                  ["", now], 2) + "\n";
+      }
+      technicalInfo.append(msg);
+    }
+    technicalInfo.append("\n");
+
+    // Add link to certificate and error message.
+    let linkPrefix = gPipNSSBundle.GetStringFromName("certErrorCodePrefix3");
+    let detailLink = doc.createElement("a");
+    detailLink.append(input.data.codeString);
+    detailLink.title = input.data.codeString;
+    detailLink.id = "errorCode";
+    let fragment = BrowserUtils.getLocalizedFragment(doc, linkPrefix, detailLink);
+    technicalInfo.appendChild(fragment);
+    var errorCode = doc.getElementById("errorCode");
+    if (errorCode) {
+      errorCode.href = "javascript:void(0)";
+      errorCode.addEventListener("click", () => {
+        let debugInfo = doc.getElementById("certificateErrorDebugInformation");
+        debugInfo.style.display = "block";
+        debugInfo.scrollIntoView({block: "start", behavior: "smooth"});
+      });
+    }
+  },
+
   onCertErrorDetails(msg, docShell) {
     let doc = docShell.document;
 
     let div = doc.getElementById("certificateErrorText");
     div.textContent = msg.data.info;
+    this._setTechDetails(msg, doc);
     let learnMoreLink = doc.getElementById("learnMoreLink");
     let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL");
 
     switch (msg.data.code) {
       case SEC_ERROR_UNKNOWN_ISSUER:
         learnMoreLink.href = baseURL + "security-error";
         break;
 
@@ -405,23 +600,79 @@ var AboutNetAndCertErrorListener = {
       if (Services.prefs.prefHasUserValue(prefName)) {
         return true;
       }
     }
 
     return false;
   },
 
+   _getErrorMessageFromCode(securityInfo, doc) {
+     let uri = Services.io.newURI(doc.location);
+     let hostString = uri.host;
+     if (uri.port != 443 && uri.port != -1) {
+       hostString = uri.hostPort;
+     }
+
+     let id_str = "";
+     switch (securityInfo.errorCode) {
+       case SSL_ERROR_SSL_DISABLED:
+         id_str = "PSMERR_SSL_Disabled";
+         break;
+       case SSL_ERROR_SSL2_DISABLED:
+         id_str = "PSMERR_SSL2_Disabled";
+         break;
+       case SEC_ERROR_REUSED_ISSUER_AND_SERIAL:
+         id_str = "PSMERR_HostReusedIssuerSerial";
+         break;
+     }
+     let nss_error_id_str = securityInfo.errorCodeString;
+     let msg2 = "";
+     if (id_str) {
+       msg2 = gPipNSSBundle.GetStringFromName(id_str) + "\n";
+     } else if (nss_error_id_str) {
+       msg2 = gNSSErrorsBundle.GetStringFromName(nss_error_id_str) + "\n";
+     }
+
+     if (!msg2) {
+       // We couldn't get an error message. Use the error string.
+       // Note that this is different from before where we used PR_ErrorToString.
+       msg2 = nss_error_id_str;
+     }
+     let msg = gPipNSSBundle.formatStringFromName("SSLConnectionErrorPrefix2",
+                                                  [hostString, msg2], 2);
+
+     if (nss_error_id_str) {
+       msg += gPipNSSBundle.formatStringFromName("certErrorCodePrefix3",
+                                                 [nss_error_id_str], 1) + "\n";
+     }
+     return msg;
+   },
+
   onPageLoad(originalTarget, win) {
     // Values for telemtery bins: see TLS_ERROR_REPORT_UI in Histograms.json
     const TLS_ERROR_REPORT_TELEMETRY_UI_SHOWN = 0;
 
     if (this.isAboutCertError(win.document)) {
       ClickEventHandler.onCertError(originalTarget, win);
     }
+    if (this.isAboutNetError(win.document)) {
+      let docShell = win.document.docShell;
+      if (docShell) {
+        let {securityInfo} = docShell.failedChannel;
+        // We don't have a securityInfo when this is for example a DNS error.
+        if (securityInfo) {
+          securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
+          let msg = this._getErrorMessageFromCode(securityInfo,
+                                                  win.document);
+          let id = win.document.getElementById("errorShortDescText");
+          id.textContent = msg;
+        }
+      }
+    }
 
     let automatic = Services.prefs.getBoolPref("security.ssl.errorReporting.automatic");
     win.dispatchEvent(new win.CustomEvent("AboutNetErrorOptions", {
       detail: JSON.stringify({
         enabled: Services.prefs.getBoolPref("security.ssl.errorReporting.enabled"),
         changedCertPrefs: this.changedCertPrefs(),
         automatic
       })
--- a/browser/base/content/test/about/browser_aboutCertError.js
+++ b/browser/base/content/test/about/browser_aboutCertError.js
@@ -143,16 +143,27 @@ add_task(async function checkBadStsCert(
     let exceptionButtonHidden = await ContentTask.spawn(browser, {frame: useFrame}, async function({frame}) {
       let doc = frame ? content.document.querySelector("iframe").contentDocument : content.document;
       let exceptionButton = doc.getElementById("exceptionDialogButton");
       return exceptionButton.hidden;
     });
 
     ok(exceptionButtonHidden, "Exception button is hidden");
 
+    let message = await ContentTask.spawn(browser, {frame: useFrame}, async function({frame}) {
+      let doc = frame ? content.document.querySelector("iframe").contentDocument : content.document;
+      let advancedButton = doc.getElementById("advancedButton");
+      advancedButton.click();
+      return doc.getElementById("badCertTechnicalInfo").textContent;
+    });
+    ok(message.includes("SSL_ERROR_BAD_CERT_DOMAIN"), "Didn't find SSL_ERROR_BAD_CERT_DOMAIN.");
+    ok(message.includes("The certificate is only valid for"), "Didn't find error message.");
+    ok(message.includes("uses an invalid security certificate"), "Didn't find error message.");
+    ok(message.includes("badchain.include-subdomains.pinning.example.com"), "Didn't find domain in error message.");
+
     BrowserTestUtils.removeTab(gBrowser.selectedTab);
   }
 });
 
 // This checks that the appinfo.appBuildID starts with a date string,
 // which is required for the misconfigured system time check.
 add_task(async function checkAppBuildIDIsDate() {
   let appBuildID = Services.appinfo.appBuildID;
--- a/browser/base/content/test/performance/browser_windowopen.js
+++ b/browser/base/content/test/performance/browser_windowopen.js
@@ -82,17 +82,17 @@ add_task(async function() {
                rects.toSource());
           return [];
         }
 
         return rects;
       },
       exceptions: [
         {name: "bug 1421463 - reload toolbar icon shouldn't flicker",
-         condition: r => r.h == 13 && inRange(r.w, 14, 16) && // icon size
+         condition: r => inRange(r.h, 13, 14) && inRange(r.w, 14, 16) && // icon size
                          inRange(r.y1, 40, 80) && // in the toolbar
                          // near the left side of the screen
                          // The reload icon is shifted on devedition builds
                          // where there's an additional devtools toolbar icon.
                          AppConstants.MOZ_DEV_EDITION ? inRange(r.x1, 100, 120) :
                                                         inRange(r.x1, 65, 100)
         },
       ]
deleted file mode 100644
--- a/browser/base/content/test/performance/browser_windowopen_flicker.js
+++ /dev/null
@@ -1,136 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-/*
- * This test ensures that there is no unexpected flicker
- * when opening new windows.
- */
-
-add_task(async function() {
-  // Flushing all caches helps to ensure that we get consistent
-  // behaviour when opening a new window, even if windows have been
-  // opened in previous tests.
-  Services.obs.notifyObservers(null, "startupcache-invalidate");
-  Services.obs.notifyObservers(null, "chrome-flush-skin-caches");
-  Services.obs.notifyObservers(null, "chrome-flush-caches");
-
-  let win = window.openDialog("chrome://browser/content/", "_blank",
-                              "chrome,all,dialog=no,remote,suppressanimation",
-                              "about:home");
-
-  // Avoid showing the remotecontrol UI.
-  await new Promise(resolve => {
-    win.addEventListener("DOMContentLoaded", () => {
-      delete win.Marionette;
-      win.Marionette = {running: false};
-      resolve();
-    }, {once: true});
-  });
-
-  let canvas = win.document.createElementNS("http://www.w3.org/1999/xhtml",
-                                            "canvas");
-  canvas.mozOpaque = true;
-  let ctx = canvas.getContext("2d", {alpha: false, willReadFrequently: true});
-
-  let frames = [];
-
-  let afterPaintListener = event => {
-    let width, height;
-    canvas.width = width = win.innerWidth;
-    canvas.height = height = win.innerHeight;
-    ctx.drawWindow(win, 0, 0, width, height, "white",
-                   ctx.DRAWWINDOW_DO_NOT_FLUSH | ctx.DRAWWINDOW_DRAW_VIEW |
-                   ctx.DRAWWINDOW_ASYNC_DECODE_IMAGES |
-                   ctx.DRAWWINDOW_USE_WIDGET_LAYERS);
-    frames.push({data: Cu.cloneInto(ctx.getImageData(0, 0, width, height).data, {}),
-                 width, height});
-  };
-  win.addEventListener("MozAfterPaint", afterPaintListener);
-
-  await TestUtils.topicObserved("browser-delayed-startup-finished",
-                                subject => subject == win);
-
-  await BrowserTestUtils.firstBrowserLoaded(win, false);
-  await BrowserTestUtils.browserStopped(win.gBrowser.selectedBrowser, "about:home");
-
-  await new Promise(resolve => {
-    // 10 is an arbitrary value here, it needs to be at least 2 to avoid
-    // races with code initializing itself using idle callbacks.
-    (function waitForIdle(count = 10) {
-      if (!count) {
-        resolve();
-        return;
-      }
-      Services.tm.idleDispatchToMainThread(() => {
-        waitForIdle(count - 1);
-      });
-    })();
-  });
-  win.removeEventListener("MozAfterPaint", afterPaintListener);
-
-  let unexpectedRects = 0;
-  let alreadyFocused = false;
-  for (let i = 1; i < frames.length; ++i) {
-    let frame = frames[i], previousFrame = frames[i - 1];
-    let rects = compareFrames(frame, previousFrame);
-
-    // The first screenshot we get in OSX / Windows shows an unfocused browser
-    // window for some reason. See bug 1445161.
-    //
-    // We'll assume the changes we are seeing are due to this focus change if
-    // there are at least 5 areas that changed near the top of the screen, but
-    // will only ignore this once (hence the alreadyFocused variable).
-    if (!alreadyFocused && rects.length > 5 && rects.every(r => r.y2 < 100)) {
-      alreadyFocused = true;
-      todo(false,
-           "bug 1445161 - the window should be focused at first paint, " + rects.toSource());
-      continue;
-    }
-
-    rects = rects.filter(rect => {
-      let inRange = (val, min, max) => min <= val && val <= max;
-      let width = frame.width;
-
-      let exceptions = [
-        {name: "bug 1421463 - reload toolbar icon shouldn't flicker",
-         condition: r => r.h == 13 && inRange(r.w, 14, 16) && // icon size
-                         inRange(r.y1, 40, 80) && // in the toolbar
-                         // near the left side of the screen
-                         // The reload icon is shifted on devedition builds
-                         // where there's an additional devtools toolbar icon.
-                         AppConstants.MOZ_DEV_EDITION ? inRange(r.x1, 100, 120) :
-                                                        inRange(r.x1, 65, 100)
-        },
-      ];
-
-      let rectText = `${rect.toSource()}, window width: ${width}`;
-      for (let e of exceptions) {
-        if (e.condition(rect)) {
-          todo(false, e.name + ", " + rectText);
-          return false;
-        }
-      }
-
-      ok(false, "unexpected changed rect: " + rectText);
-      return true;
-    });
-    if (!rects.length) {
-      info("ignoring identical frame");
-      continue;
-    }
-
-    // Before dumping a frame with unexpected differences for the first time,
-    // ensure at least one previous frame has been logged so that it's possible
-    // to see the differences when examining the log.
-    if (!unexpectedRects) {
-      dumpFrame(previousFrame);
-    }
-    unexpectedRects += rects.length;
-    dumpFrame(frame);
-  }
-  is(unexpectedRects, 0, "should have 0 unknown flickering areas");
-
-  await BrowserTestUtils.closeWindow(win);
-});
--- a/browser/base/content/test/static/browser_misused_characters_in_strings.js
+++ b/browser/base/content/test/static/browser_misused_characters_in_strings.js
@@ -104,24 +104,16 @@ let gWhitelist = [{
     file: "xbl.properties",
     key: "CommandNotInChrome",
     type: "double-quote"
   }, {
     file: "dom.properties",
     key: "PatternAttributeCompileFailure",
     type: "single-quote"
   }, {
-    file: "pipnss.properties",
-    key: "certErrorMismatchSingle2",
-    type: "double-quote"
-  }, {
-    file: "pipnss.properties",
-    key: "certErrorCodePrefix2",
-    type: "double-quote"
-  }, {
     file: "aboutSupport.dtd",
     key: "aboutSupport.pageSubtitle",
     type: "single-quote"
   }, {
     file: "aboutSupport.dtd",
     key: "aboutSupport.userJSDescription",
     type: "single-quote"
   }, {
--- a/browser/components/extensions/test/browser/browser-common.ini
+++ b/browser/components/extensions/test/browser/browser-common.ini
@@ -37,17 +37,16 @@ support-files =
   ../../../../../toolkit/components/extensions/test/mochitest/redirection.sjs
   ../../../../../toolkit/components/reader/test/readerModeNonArticle.html
   ../../../../../toolkit/components/reader/test/readerModeArticle.html
 
 [browser_ext_addon_debugging_netmonitor.js]
 [browser_ext_browserAction_area.js]
 [browser_ext_browserAction_experiment.js]
 [browser_ext_browserAction_context.js]
-skip-if = os == 'win' || os == 'mac' # Bug 1405453
 [browser_ext_browserAction_contextMenu.js]
 # bug 1369197
 skip-if = os == 'linux'
 [browser_ext_browserAction_disabled.js]
 [browser_ext_browserAction_pageAction_icon.js]
 [browser_ext_browserAction_pageAction_icon_permissions.js]
 [browser_ext_browserAction_popup.js]
 skip-if = (debug && os == 'linux' && bits == 32) || (os == 'win' && !debug) # Bug 1313372, win: Bug 1285500
--- a/browser/modules/ThemeVariableMap.jsm
+++ b/browser/modules/ThemeVariableMap.jsm
@@ -1,31 +1,115 @@
 /* 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/. */
 
 var EXPORTED_SYMBOLS = ["ThemeVariableMap"];
 
 const ThemeVariableMap = [
-  ["--lwt-accent-color-inactive", "accentcolorInactive"],
-  ["--lwt-background-alignment", "backgroundsAlignment"],
-  ["--lwt-background-tiling", "backgroundsTiling"],
-  ["--tab-loading-fill", "tab_loading", "tabbrowser-tabs"],
-  ["--lwt-tab-text", "tab_text"],
-  ["--tab-line-color", "tab_line", "tabbrowser-tabs"],
-  ["--toolbar-bgcolor", "toolbarColor"],
-  ["--toolbar-color", "toolbar_text"],
-  ["--url-and-searchbar-background-color", "toolbar_field"],
-  ["--url-and-searchbar-color", "toolbar_field_text"],
-  ["--lwt-toolbar-field-border-color", "toolbar_field_border"],
-  ["--urlbar-separator-color", "toolbar_field_separator"],
-  ["--tabs-border-color", "toolbar_top_separator", "navigator-toolbox"],
-  ["--lwt-toolbar-vertical-separator", "toolbar_vertical_separator"],
-  ["--toolbox-border-bottom-color", "toolbar_bottom_separator"],
-  ["--lwt-toolbarbutton-icon-fill", "icon_color"],
-  ["--lwt-toolbarbutton-icon-fill-attention", "icon_attention_color"],
-  ["--lwt-toolbarbutton-hover-background", "button_background_hover"],
-  ["--lwt-toolbarbutton-active-background", "button_background_active"],
-  ["--lwt-selected-tab-background-color", "tab_selected"],
-  ["--lwt-toolbar-field-focus", "toolbar_field_focus"],
-  ["--lwt-toolbar-field-focus-color", "toolbar_field_text_focus"],
-  ["--lwt-toolbar-field-focus-border-color", "toolbar_field_border_focus"],
+  ["--lwt-accent-color-inactive", {
+    lwtProperty: "accentcolorInactive"
+  }],
+  ["--lwt-background-alignment", {
+    isColor: false,
+    lwtProperty: "backgroundsAlignment"
+  }],
+  ["--lwt-background-tiling", {
+    isColor: false,
+    lwtProperty: "backgroundsTiling"
+  }],
+  ["--tab-loading-fill", {
+    lwtProperty: "tab_loading",
+    optionalElementID: "tabbrowser-tabs"
+  }],
+  ["--lwt-tab-text", {
+    lwtProperty: "tab_text"
+  }],
+  ["--tab-line-color", {
+    lwtProperty: "tab_line",
+    optionalElementID: "tabbrowser-tabs"
+  }],
+  ["--toolbar-bgcolor", {
+    lwtProperty: "toolbarColor"
+  }],
+  ["--toolbar-color", {
+    lwtProperty: "toolbar_text"
+  }],
+  ["--url-and-searchbar-background-color", {
+    lwtProperty: "toolbar_field"
+  }],
+  ["--url-and-searchbar-color", {
+    lwtProperty: "toolbar_field_text"
+  }],
+  ["--lwt-toolbar-field-border-color", {
+    lwtProperty: "toolbar_field_border"
+  }],
+  ["--lwt-toolbar-field-focus", {
+    lwtProperty: "toolbar_field_focus"
+  }],
+  ["--lwt-toolbar-field-focus-color", {
+    lwtProperty: "toolbar_field_text_focus"
+  }],
+  ["--lwt-toolbar-field-focus-border-color", {
+    lwtProperty: "toolbar_field_border_focus"
+  }],
+  ["--urlbar-separator-color", {
+    lwtProperty: "toolbar_field_separator"
+  }],
+  ["--tabs-border-color", {
+    lwtProperty: "toolbar_top_separator",
+    optionalElementID: "navigator-toolbox"
+  }],
+  ["--lwt-toolbar-vertical-separator", {
+    lwtProperty: "toolbar_vertical_separator"
+  }],
+  ["--toolbox-border-bottom-color", {
+    lwtProperty: "toolbar_bottom_separator"
+  }],
+  ["--lwt-toolbarbutton-icon-fill", {
+    lwtProperty: "icon_color"
+  }],
+  ["--lwt-toolbarbutton-icon-fill-attention", {
+    lwtProperty: "icon_attention_color"
+  }],
+  ["--lwt-toolbarbutton-hover-background", {
+    lwtProperty: "button_background_hover"
+  }],
+  ["--lwt-toolbarbutton-active-background", {
+    lwtProperty: "button_background_active"
+  }],
+  ["--lwt-selected-tab-background-color", {
+    lwtProperty: "tab_selected"
+  }],
+  ["--autocomplete-popup-background", {
+    lwtProperty: "popup"
+  }],
+  ["--autocomplete-popup-color", {
+    lwtProperty: "popup_text",
+    processColor(rgbaChannels, element) {
+      const secondaryVariable = "--autocomplete-popup-secondary-color";
+
+      if (!rgbaChannels) {
+        element.removeAttribute("lwt-popup-brighttext");
+        element.style.removeProperty(secondaryVariable);
+        return null;
+      }
+
+      let {r, g, b, a} = rgbaChannels;
+      let luminance = 0.2125 * r + 0.7154 * g + 0.0721 * b;
+
+      if (luminance <= 110) {
+        element.removeAttribute("lwt-popup-brighttext");
+      } else {
+        element.setAttribute("lwt-popup-brighttext", "true");
+      }
+
+      element.style.setProperty(secondaryVariable, `rgba(${r}, ${g}, ${b}, 0.5)`);
+      return `rgba(${r}, ${g}, ${b}, ${a})`;
+    }
+  }],
+  ["--autocomplete-popup-highlight-background", {
+    lwtProperty: "popup_highlight"
+  }],
+  ["--autocomplete-popup-highlight-color", {
+    lwtProperty: "popup_highlight_text"
+  }],
 ];
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -40,16 +40,18 @@
   --tab-line-color: highlight;
 }
 
 :root:-moz-lwtheme {
   --toolbar-bgcolor: rgba(255,255,255,.4);
   --toolbar-bgimage: none;
 
   --toolbox-border-bottom-color: rgba(0,0,0,.3);
+
+  --panel-separator-color: hsla(210,4%,10%,.14);
 }
 
 #menubar-items {
   -moz-box-orient: vertical; /* for flex hack */
 }
 
 #main-menubar {
   -moz-box-flex: 1; /* make menu items expand to fill toolbar height */
--- a/browser/themes/shared/searchbar.inc.css
+++ b/browser/themes/shared/searchbar.inc.css
@@ -44,17 +44,17 @@
 }
 
 .search-panel-header {
   font-weight: normal;
   background-color: var(--arrowpanel-dimmed);
   border-top: 1px solid var(--panel-separator-color);
   margin: 0;
   padding: 3px 6px;
-  color: GrayText;
+  color: var(--autocomplete-popup-secondary-color);
 }
 
 .search-panel-header > label {
   margin-top: 2px !important;
   margin-bottom: 1px !important;
 }
 
 .search-panel-current-input > label {
@@ -79,17 +79,17 @@
   min-width: 48px;
   height: 32px;
   margin: 0;
   padding: 0;
   background: linear-gradient(transparent 15%, var(--panel-separator-color) 15%, var(--panel-separator-color) 85%, transparent 85%);
   background-size: 1px auto;
   background-repeat: no-repeat;
   background-position: right center;
-  color: GrayText;
+  color: var(--autocomplete-popup-secondary-color);
 }
 
 .searchbar-engine-one-off-item:-moz-locale-dir(rtl) {
   background-position-x: left;
 }
 
 .searchbar-engine-one-off-item:not(.last-row) {
   box-sizing: content-box;
--- a/browser/themes/shared/toolbarbuttons.inc.css
+++ b/browser/themes/shared/toolbarbuttons.inc.css
@@ -38,18 +38,18 @@
 :root:-moz-lwtheme-darktext,
 toolbar:not([brighttext]) {
   --toolbarbutton-hover-background: var(--lwt-toolbarbutton-hover-background, hsla(240,5%,5%,.1));
   --toolbarbutton-active-background: var(--lwt-toolbarbutton-active-background, hsla(240,5%,5%,.15));
 }
 
 :root:-moz-lwtheme-brighttext,
 toolbar[brighttext] {
-  --toolbarbutton-hover-background: var(--lwt-toolbarbutton-hover-background, hsla(0,0%,100%,.2));
-  --toolbarbutton-active-background: var(--lwt-toolbarbutton-active-background, hsla(0,0%,100%,.3));
+  --toolbarbutton-hover-background: var(--lwt-toolbarbutton-hover-background, hsla(0,0%,70%,.4));
+  --toolbarbutton-active-background: var(--lwt-toolbarbutton-active-background, hsla(0,0%,70%,.6));
 
   --backbutton-background: var(--toolbarbutton-hover-background);
   --backbutton-hover-background: var(--toolbarbutton-active-background);
   --backbutton-active-background: hsla(0,0%,100%,.4);
 }
 
 /* ::::: primary toolbar buttons ::::: */
 
--- a/browser/themes/shared/urlbar-autocomplete.inc.css
+++ b/browser/themes/shared/urlbar-autocomplete.inc.css
@@ -4,16 +4,30 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 %endif
 
 :root {
   --autocomplete-popup-background: -moz-field;
   --autocomplete-popup-color: -moz-fieldtext;
   --autocomplete-popup-highlight-background: Highlight;
   --autocomplete-popup-highlight-color: HighlightText;
+  --autocomplete-popup-secondary-color: GrayText;
+}
+
+:root:-moz-lwtheme {
+  --autocomplete-popup-background: #fff;
+  --autocomplete-popup-color: #0c0c0d;
+  --autocomplete-popup-secondary-color: #737373;
+  --urlbar-popup-url-color: hsl(210, 77%, 47%);
+  --urlbar-popup-action-color: hsl(178, 100%, 28%);
+}
+
+:root[lwt-popup-brighttext] {
+  --urlbar-popup-url-color: #0a84ff;
+  --urlbar-popup-action-color: #30e60b;
 }
 
 #PopupAutoCompleteRichResult,
 #PopupSearchAutoComplete {
   background: var(--autocomplete-popup-background);
   color: var(--autocomplete-popup-color);
 }
 
@@ -43,17 +57,17 @@
 
 :root[uidensity=touch] #PopupAutoCompleteRichResult .autocomplete-richlistitem {
   min-height: 40px;
 }
 
 /* Awesomebar popup items */
 
 .ac-separator:not([selected=true]) {
-  color: GrayText;
+  color: var(--autocomplete-popup-secondary-color);
 }
 
 .ac-url:not([selected=true]) {
   color: var(--urlbar-popup-url-color);
 }
 
 .ac-action:not([selected=true]) {
   color: var(--urlbar-popup-action-color);
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -62,16 +62,18 @@
   }
 }
 
 :root:-moz-lwtheme {
   --toolbar-bgcolor: rgba(255,255,255,.4);
   --toolbar-bgimage: none;
 
   --toolbox-border-bottom-color: rgba(0,0,0,.3);
+
+  --panel-separator-color: hsla(210,4%,10%,.14);
 }
 
 #navigator-toolbox:-moz-lwtheme {
   --tabs-border-color: rgba(0,0,0,.3);
 }
 
 #menubar-items {
   -moz-box-orient: vertical; /* for flex hack */
--- a/build/clang-plugin/CustomMatchers.h
+++ b/build/clang-plugin/CustomMatchers.h
@@ -176,17 +176,17 @@ AST_MATCHER(CXXRecordDecl, needsMemMovab
 
 AST_MATCHER(CXXConstructorDecl, isInterestingImplicitCtor) {
   const CXXConstructorDecl *Declaration = Node.getCanonicalDecl();
   return
       // Skip constructors in system headers
       !ASTIsInSystemHeader(Declaration->getASTContext(), *Declaration) &&
       // Skip ignored namespaces and paths
       !isInIgnoredNamespaceForImplicitCtor(Declaration) &&
-      !isIgnoredPathForImplicitCtor(Declaration) &&
+      !inThirdPartyPath(Declaration) &&
       // We only want Converting constructors
       Declaration->isConvertingConstructor(false) &&
       // We don't want copy of move constructors, as those are allowed to be
       // implicit
       !Declaration->isCopyOrMoveConstructor() &&
       // We don't want deleted constructors.
       !Declaration->isDeleted();
 }
--- a/build/clang-plugin/Utils.h
+++ b/build/clang-plugin/Utils.h
@@ -172,47 +172,16 @@ inline bool isInIgnoredNamespaceForImpli
   }
 
   return Name == "std" ||             // standard C++ lib
          Name == "__gnu_cxx" ||       // gnu C++ lib
          Name == "google_breakpad" || // breakpad
          Name == "testing";           // gtest
 }
 
-inline bool isIgnoredPathForImplicitCtor(const Decl *Declaration) {
-  SourceLocation Loc = Declaration->getLocation();
-  const SourceManager &SM = Declaration->getASTContext().getSourceManager();
-  SmallString<1024> FileName = SM.getFilename(Loc);
-  llvm::sys::fs::make_absolute(FileName);
-  llvm::sys::path::reverse_iterator Begin = llvm::sys::path::rbegin(FileName),
-                                    End = llvm::sys::path::rend(FileName);
-  for (; Begin != End; ++Begin) {
-    if (Begin->compare_lower(StringRef("skia")) == 0 ||
-        Begin->compare_lower(StringRef("sfntly")) == 0 ||
-        Begin->compare_lower(StringRef("angle")) == 0 ||
-        Begin->compare_lower(StringRef("harfbuzz")) == 0 ||
-        Begin->compare_lower(StringRef("hunspell")) == 0 ||
-        Begin->compare_lower(StringRef("scoped_ptr.h")) == 0 ||
-        Begin->compare_lower(StringRef("graphite2")) == 0 ||
-        Begin->compare_lower(StringRef("icu")) == 0 ||
-        Begin->compare_lower(StringRef("libcubeb")) == 0 ||
-        Begin->compare_lower(StringRef("libstagefright")) == 0 ||
-        Begin->compare_lower(StringRef("cairo")) == 0 ||
-        Begin->compare_lower(StringRef("pdfium")) == 0) {
-      return true;
-    }
-    if (Begin->compare_lower(StringRef("chromium")) == 0) {
-      // Ignore security/sandbox/chromium but not ipc/chromium.
-      ++Begin;
-      return Begin != End && Begin->compare_lower(StringRef("sandbox")) == 0;
-    }
-  }
-  return false;
-}
-
 inline bool isIgnoredPathForImplicitConversion(const Decl *Declaration) {
   Declaration = Declaration->getCanonicalDecl();
   SourceLocation Loc = Declaration->getLocation();
   const SourceManager &SM = Declaration->getASTContext().getSourceManager();
   SmallString<1024> FileName = SM.getFilename(Loc);
   llvm::sys::fs::make_absolute(FileName);
   llvm::sys::path::reverse_iterator Begin = llvm::sys::path::rbegin(FileName),
                                     End = llvm::sys::path::rend(FileName);
--- a/devtools/client/debugger/new/README.mozilla
+++ b/devtools/client/debugger/new/README.mozilla
@@ -1,13 +1,13 @@
 This is the debugger.html project output.
 See https://github.com/devtools-html/debugger.html
 
-Version 27.0
+Version 28.0
 
-Comparison: https://github.com/devtools-html/debugger.html/compare/release-26...release-27
+Comparison: https://github.com/devtools-html/debugger.html/compare/release-28...release-27
 
 Packages:
 - babel-plugin-transform-es2015-modules-commonjs @6.26.0
 - babel-preset-react @6.24.1
 - react @16.2.0
 - react-dom @16.2.0
 - webpack @3.11.0
--- a/devtools/client/debugger/new/debugger.css
+++ b/devtools/client/debugger/new/debugger.css
@@ -84,16 +84,17 @@
 
 .shortcuts-list li {
   font-size: 12px;
   color: var(--theme-body-color);
   padding-top: 5px;
   display: flex;
   justify-content: space-between;
   border: 1px solid transparent;
+  white-space: pre;
 }
 
 @media (max-width: 640px) {
   .shortcuts-section {
     width: 100%;
   }
 }
 /* This Source Code Form is subject to the terms of the Mozilla Public
@@ -581,25 +582,27 @@ menuseparator {
 .arrow,
 .worker,
 .refresh,
 .shortcut,
 .add-button {
   fill: var(--theme-splitter-color);
 }
 
-.file,
 .folder,
-.domain {
+.domain,
+.source-icon,
+.file {
   background-color: var(--theme-comment);
 }
 
 .worker,
 .file,
-.folder {
+.folder,
+.source-icon {
   position: relative;
   top: 2px;
 }
 
 .domain,
 .worker,
 .refresh,
 .add-button {
@@ -610,38 +613,56 @@ menuseparator {
 .worker svg,
 .refresh svg,
 .shortcut svg,
 .add-button svg {
   width: 15px;
 }
 
 img.domain,
-img.folder {
+img.folder,
+img.source-icon {
   width: 15px;
   height: 15px;
 }
 
 img.domain {
   mask: url("chrome://devtools/skin/images/debugger/domain.svg") no-repeat;
 }
 
 img.folder {
   mask: url("chrome://devtools/skin/images/debugger/folder.svg") no-repeat;
 }
 
+img.coffeescript {
+  mask: url("chrome://devtools/skin/images/debugger/coffeescript.svg") no-repeat;
+}
+
+img.javascript {
+  mask: url("chrome://devtools/skin/images/debugger/javascript.svg") no-repeat;
+}
+
+img.react {
+  mask: url("chrome://devtools/skin/images/debugger/react.svg") no-repeat;
+}
+
+img.typescript {
+  mask: url("chrome://devtools/skin/images/debugger/typescript.svg") no-repeat;
+}
+
 img.file {
   mask: url("chrome://devtools/skin/images/debugger/file.svg") no-repeat;
   width: 13px;
   height: 13px;
 }
 
 img.domain,
 img.folder,
-img.file {
+img.file,
+img.source-icon {
   mask-size: 100%;
   margin-inline-end: 5px;
   display: inline-block;
 }
 
 .refresh svg,
 .shortcut svg,
 .worker svg {
@@ -3660,16 +3681,18 @@ img.ignore-exceptions {
 
 .secondary-panes {
   overflow: auto;
   display: flex;
   flex-direction: column;
   flex: 1;
   white-space: nowrap;
   --breakpoint-expression-right-clear-space: 36px;
+  -moz-user-select: none;
+  user-select: none;
 }
 
 /*
   We apply overflow to the container with the commandbar.
   This allows the commandbar to remain fixed when scrolling
   until the content completely ends. Not just the height of
   the wrapper.
   Ref: https://github.com/devtools-html/debugger.html/issues/3426
@@ -3946,22 +3969,21 @@ html .welcomebox .toggle-button-end.coll
   -moz-user-select: none;
   user-select: none;
 }
 
 .result-list li {
   color: var(--theme-body-color);
   padding: 4px 13px;
   display: flex;
-  justify-content: space-between;
 }
 
 .result-list.big li {
   padding: 10px;
-  flex-direction: column;
+  flex-direction: row;
   border-bottom: 1px solid var(--theme-splitter-color);
 }
 
 .result-list li:hover {
   background: var(--theme-tab-toolbar-background);
 }
 
 .result-list li.selected {
@@ -3983,16 +4005,19 @@ html .welcomebox .toggle-button-end.coll
 
 .theme-dark .result-list li.selected {
   background: var(--grey-70);
 }
 
 .result-list li .title {
   line-height: 1.5em;
   word-break: break-all;
+  text-overflow: ellipsis;
+  overflow: hidden;
+  white-space: nowrap;
 }
 
 .result-list li.selected .title {
   color: white;
 }
 
 .result-list.big li.selected {
   background-color: var(--theme-selection-background);
@@ -4006,16 +4031,20 @@ html .welcomebox .toggle-button-end.coll
 .result-list.big li.selected .subtitle .highlight {
   color: white;
   font-weight: bold;
 }
 
 .result-list.big li .subtitle {
   word-break: break-all;
   color: var(--theme-body-color-inactive);
+  margin-left: 15px;
+  text-overflow: ellipsis;
+  overflow: hidden;
+  white-space: nowrap;
 }
 
 .result-list.big li .subtitle {
   line-height: 1.5em;
 }
 
 .theme-dark .result-list.big li.selected .subtitle {
   color: white;
--- a/devtools/client/debugger/new/debugger.js
+++ b/devtools/client/debugger/new/debugger.js
@@ -2335,17 +2335,17 @@ function trimUrlQuery(url) {
 }
 
 function shouldPrettyPrint(source) {
   if (!source) {
     return false;
   }
   const _isPretty = isPretty(source);
   const _isJavaScript = isJavaScript(source);
-  const isOriginal = (0, _devtoolsSourceMap.isOriginalId)(source.get("id"));
+  const isOriginal = (0, _devtoolsSourceMap.isOriginalId)(source.id);
   const hasSourceMap = source.get("sourceMapURL");
 
   if (_isPretty || isOriginal || hasSourceMap || !_isJavaScript) {
     return false;
   }
 
   return true;
 }
@@ -2356,36 +2356,36 @@ function shouldPrettyPrint(source) {
  *
  * @return boolean
  *         True if the source is likely javascript.
  *
  * @memberof utils/source
  * @static
  */
 function isJavaScript(source) {
-  const url = source.get("url");
-  const contentType = source.get("contentType");
+  const url = source.url;
+  const contentType = source.contentType;
   return url && /\.(jsm|js)?$/.test(trimUrlQuery(url)) || !!(contentType && contentType.includes("javascript"));
 }
 
 /**
  * @memberof utils/source
  * @static
  */
 function isPretty(source) {
-  const url = source.get("url");
+  const url = source.url;
   return isPrettyURL(url);
 }
 
 function isPrettyURL(url) {
   return url ? /formatted$/.test(url) : false;
 }
 
 function isThirdParty(source) {
-  const url = source.get("url");
+  const url = source.url;
   if (!source || !url) {
     return false;
   }
 
   return !!url.match(/(node_modules|bower_components)/);
 }
 
 /**
@@ -2690,17 +2690,16 @@ Object.keys(_createEditor).forEach(funct
 exports.shouldShowPrettyPrint = shouldShowPrettyPrint;
 exports.shouldShowFooter = shouldShowFooter;
 exports.traverseResults = traverseResults;
 exports.toEditorLine = toEditorLine;
 exports.toEditorPosition = toEditorPosition;
 exports.toEditorRange = toEditorRange;
 exports.toSourceLine = toSourceLine;
 exports.scrollToColumn = scrollToColumn;
-exports.toSourceLocation = toSourceLocation;
 exports.markText = markText;
 exports.lineAtHeight = lineAtHeight;
 exports.getSourceLocationFromMouseEvent = getSourceLocationFromMouseEvent;
 exports.forEachLine = forEachLine;
 exports.removeLineClass = removeLineClass;
 exports.clearLineClass = clearLineClass;
 exports.getTextForLine = getTextForLine;
 exports.getCursorLine = getCursorLine;
@@ -2791,23 +2790,16 @@ function isVisible(codeMirror, top, left
   const inXView = withinBounds(left, scrollArea.left, scrollArea.left + (scrollArea.clientWidth - 30) - charWidth);
 
   const fontHeight = codeMirror.defaultTextHeight();
   const inYView = withinBounds(top, scrollArea.top, scrollArea.top + scrollArea.clientHeight - fontHeight);
 
   return inXView && inYView;
 }
 
-function toSourceLocation(sourceId, location) {
-  return {
-    line: toSourceLine(sourceId, location.line),
-    column: (0, _wasm.isWasm)(sourceId) ? undefined : location.column
-  };
-}
-
 function markText(editor, className, { start, end }) {
   return editor.codeMirror.markText({ ch: start.column, line: start.line }, { ch: end.column, line: end.line }, { className });
 }
 
 function lineAtHeight(editor, sourceId, event) {
   const editorLine = editor.codeMirror.lineAtHeight(event.clientY);
   return toSourceLine(sourceId, editorLine);
 }
@@ -3728,21 +3720,21 @@ function getNewSelectedSourceId(state, a
 
     if (selectedSource) {
       return selectedSource.id;
     }
 
     return "";
   }
 
-  const tabUrls = state.sources.tabs.toJS();
+  const tabUrls = state.sources.tabs;
   const leftNeighborIndex = Math.max(tabUrls.indexOf(selectedTabUrl) - 1, 0);
   const lastAvailbleTabIndex = availableTabs.size - 1;
   const newSelectedTabIndex = Math.min(leftNeighborIndex, lastAvailbleTabIndex);
-  const availableTab = availableTabs.toJS()[newSelectedTabIndex];
+  const availableTab = availableTabs.get(newSelectedTabIndex);
   const tabSource = getSourceByUrlInSources(state.sources.sources, availableTab);
 
   if (tabSource) {
     return tabSource.id;
   }
 
   return "";
 }
@@ -4913,26 +4905,26 @@ function getSymbols(state, source) {
 
 function hasSymbols(state, source) {
   const symbols = getSymbols(state, source);
 
   if (!symbols) {
     return false;
   }
 
-  return !symbols.loading;
+  return !symbols.hasOwnProperty("loading");
 }
 
 function isSymbolsLoading(state, source) {
   const symbols = getSymbols(state, source);
   if (!symbols) {
     return false;
   }
 
-  return !!symbols.loading;
+  return symbols.hasOwnProperty("loading");
 }
 
 function isEmptyLineInSource(state, line, selectedSource) {
   const emptyLines = getEmptyLines(state, selectedSource);
   return emptyLines && emptyLines.includes(line);
 }
 
 function getEmptyLines(state, source) {
@@ -6321,17 +6313,17 @@ function deleteExpression(expression) {
 /**
  *
  * @memberof actions/pause
  * @param {number} selectedFrameId
  * @static
  */
 function evaluateExpressions() {
   return async function ({ dispatch, getState, client }) {
-    const expressions = (0, _selectors.getExpressions)(getState()).toJS();
+    const expressions = (0, _selectors.getExpressions)(getState());
     for (const expression of expressions) {
       await dispatch(evaluateExpression(expression));
     }
   };
 }
 
 function evaluateExpression(expression) {
   return async function ({ dispatch, getState, client, sourceMaps }) {
@@ -7733,17 +7725,20 @@ function containsPosition(a, b) {
 
   return startsBefore && endsAfter;
 } /* 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/>. */
 
 function findClosestScope(functions, location) {
   return functions.reduce((found, currNode) => {
-    if (currNode.name === "anonymous" || !containsPosition(currNode.location, location)) {
+    if (currNode.name === "anonymous" || !containsPosition(currNode.location, {
+      line: location.line,
+      column: location.column || 0
+    })) {
       return found;
     }
 
     if (!found) {
       return currNode;
     }
 
     if (found.location.start.line > currNode.location.start.line) {
@@ -9026,17 +9021,17 @@ const _prettyPrint = dispatcher.task("pr
 async function prettyPrint({ source, url }) {
   const indent = 2;
 
   (0, _assert2.default)((0, _source.isJavaScript)(source), "Can't prettify non-javascript files.");
 
   return await _prettyPrint({
     url,
     indent,
-    sourceText: source.get("text")
+    sourceText: source.text
   });
 }
 
 /***/ }),
 
 /***/ 1432:
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -9130,20 +9125,20 @@ function closeProjectSearch() {
 }
 
 function searchSources(query) {
   return async ({ dispatch, getState }) => {
     await dispatch(clearSearchResults());
     await dispatch(addSearchQuery(query));
     dispatch(updateSearchStatus(_projectTextSearch.statusType.fetching));
     const sources = (0, _selectors.getSources)(getState());
-    const validSources = sources.valueSeq().filter(source => !(0, _selectors.hasPrettySource)(getState(), source.get("id")) && !(0, _source.isThirdParty)(source));
+    const validSources = sources.valueSeq().filter(source => !(0, _selectors.hasPrettySource)(getState(), source.id) && !(0, _source.isThirdParty)(source));
     for (const source of validSources) {
       await dispatch((0, _sources.loadSourceText)(source));
-      await dispatch(searchSource(source.get("id"), query));
+      await dispatch(searchSource(source.id, query));
     }
     dispatch(updateSearchStatus(_projectTextSearch.statusType.done));
   };
 }
 
 function searchSource(sourceId, query) {
   return async ({ dispatch, getState }) => {
     const sourceRecord = (0, _selectors.getSource)(getState(), sourceId);
@@ -13527,25 +13522,25 @@ function createSyncData(id, pendingBreak
   return { breakpoint, previousLocation };
 }
 
 // we have three forms of syncing: disabled syncing, existing server syncing
 // and adding a new breakpoint
 async function syncClientBreakpoint(getState, client, sourceMaps, sourceId, pendingBreakpoint) {
   (0, _breakpoint.assertPendingBreakpoint)(pendingBreakpoint);
 
-  const source = (0, _selectors.getSource)(getState(), sourceId).toJS();
+  const source = (0, _selectors.getSource)(getState(), sourceId);
   const generatedSourceId = sourceMaps.isOriginalId(sourceId) ? (0, _devtoolsSourceMap.originalToGeneratedId)(sourceId) : sourceId;
 
   const { location, astLocation } = pendingBreakpoint;
   const previousLocation = _extends({}, location, { sourceId });
 
   const scopedLocation = await makeScopedLocation(astLocation, previousLocation, source);
 
-  const scopedGeneratedLocation = await (0, _sourceMaps.getGeneratedLocation)(getState(), source, scopedLocation, sourceMaps);
+  const scopedGeneratedLocation = await (0, _sourceMaps.getGeneratedLocation)(getState(), source.toJS(), scopedLocation, sourceMaps);
 
   // this is the generatedLocation of the pending breakpoint, with
   // the source id updated to reflect the new connection
   const generatedLocation = _extends({}, pendingBreakpoint.generatedLocation, {
     sourceId: generatedSourceId
   });
 
   const isSameLocation = !(0, _breakpoint.locationMoved)(generatedLocation, scopedGeneratedLocation);
@@ -13629,17 +13624,17 @@ function SearchState() {
   this.overlay = null;
   this.results = [];
 }
 
 /**
  * @memberof utils/source-search
  * @static
  */
-function getSearchState(cm, query, modifiers) {
+function getSearchState(cm, query) {
   const state = cm.state.search || (cm.state.search = new SearchState());
   return state;
 }
 
 function isWhitespace(query) {
   return !query.match(/\S/);
 }
 
@@ -13735,21 +13730,21 @@ function doSearch(ctx, rev, query, keepS
   const { cm } = ctx;
   if (!cm) {
     return;
   }
   const defaultIndex = { line: -1, ch: -1 };
 
   return cm.operation(function () {
     if (!query || isWhitespace(query)) {
-      clearSearch(cm, query, modifiers);
-      return;
-    }
-
-    const state = getSearchState(cm, query, modifiers);
+      clearSearch(cm, query);
+      return;
+    }
+
+    const state = getSearchState(cm, query);
     const isNewQuery = state.query !== query;
     state.query = query;
 
     updateOverlay(cm, state, query, modifiers);
     updateCursor(cm, state, keepSelection);
     const searchLocation = searchNext(ctx, rev, query, isNewQuery, modifiers);
 
     return searchLocation ? searchLocation.from : defaultIndex;
@@ -13769,17 +13764,17 @@ function getCursorPos(newQuery, rev, sta
  *
  * @memberof utils/source-search
  * @static
  */
 function searchNext(ctx, rev, query, newQuery, modifiers) {
   const { cm, ed } = ctx;
   let nextMatch;
   cm.operation(function () {
-    const state = getSearchState(cm, query, modifiers);
+    const state = getSearchState(cm, query);
     const pos = getCursorPos(newQuery, rev, state);
 
     if (!state.query) {
       return;
     }
 
     let cursor = getSearchCursor(cm, state.query, pos, modifiers);
 
@@ -13806,31 +13801,31 @@ function searchNext(ctx, rev, query, new
 }
 
 /**
  * Remove overlay.
  *
  * @memberof utils/source-search
  * @static
  */
-function removeOverlay(ctx, query, modifiers) {
-  const state = getSearchState(ctx.cm, query, modifiers);
+function removeOverlay(ctx, query) {
+  const state = getSearchState(ctx.cm, query);
   ctx.cm.removeOverlay(state.overlay);
   const { line, ch } = ctx.cm.getCursor();
   ctx.cm.doc.setSelection({ line, ch }, { line, ch }, { scroll: false });
 }
 
 /**
  * Clears the currently saved search.
  *
  * @memberof utils/source-search
  * @static
  */
-function clearSearch(cm, query, modifiers) {
-  const state = getSearchState(cm, query, modifiers);
+function clearSearch(cm, query) {
+  const state = getSearchState(cm, query);
 
   state.results = [];
 
   if (!state.query) {
     return;
   }
   cm.removeOverlay(state.overlay);
   state.query = null;
@@ -13838,17 +13833,17 @@ function clearSearch(cm, query, modifier
 
 /**
  * Starts a new search.
  *
  * @memberof utils/source-search
  * @static
  */
 function find(ctx, query, keepSelection, modifiers) {
-  clearSearch(ctx.cm, query, modifiers);
+  clearSearch(ctx.cm, query);
   return doSearch(ctx, false, query, keepSelection, modifiers);
 }
 
 /**
  * Finds the next item based on the currently saved search.
  *
  * @memberof utils/source-search
  * @static
@@ -14170,17 +14165,17 @@ var _ui = __webpack_require__(1385);
 
 /* 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/>. */
 
 function doSearch(query, editor) {
   return ({ getState, dispatch }) => {
     const selectedSource = (0, _selectors.getSelectedSource)(getState());
-    if (!selectedSource || !selectedSource.get("text")) {
+    if (!selectedSource || !selectedSource.text) {
       return;
     }
 
     dispatch(setFileSearchQuery(query));
     dispatch(searchContents(query, editor));
   };
 }
 
@@ -14209,23 +14204,23 @@ function updateSearchResults(characterIn
   };
 }
 
 function searchContents(query, editor) {
   return async ({ getState, dispatch }) => {
     const modifiers = (0, _selectors.getFileSearchModifiers)(getState());
     const selectedSource = (0, _selectors.getSelectedSource)(getState());
 
-    if (!query || !editor || !selectedSource || !selectedSource.get("text") || !modifiers) {
+    if (!query || !editor || !selectedSource || !selectedSource.text || !modifiers) {
       return;
     }
 
     const ctx = { ed: editor, cm: editor.codeMirror };
     const _modifiers = modifiers.toJS();
-    const matches = await (0, _search.getMatches)(query, selectedSource.get("text"), _modifiers);
+    const matches = await (0, _search.getMatches)(query, selectedSource.text, _modifiers);
 
     const res = (0, _editor.find)(ctx, query, true, _modifiers);
     if (!res) {
       return;
     }
 
     const { ch, line } = res;
 
@@ -14259,22 +14254,21 @@ function traverseResults(rev, editor) {
       const { ch, line } = results;
       dispatch(updateSearchResults(ch, line, matchedLocations));
     }
   };
 }
 
 function closeFileSearch(editor) {
   return ({ getState, dispatch }) => {
-    const modifiers = (0, _selectors.getFileSearchModifiers)(getState());
     const query = (0, _selectors.getFileSearchQuery)(getState());
 
-    if (editor && modifiers) {
+    if (editor) {
       const ctx = { ed: editor, cm: editor.codeMirror };
-      (0, _editor.removeOverlay)(ctx, query, modifiers.toJS());
+      (0, _editor.removeOverlay)(ctx, query);
     }
 
     dispatch(setFileSearchQuery(""));
     dispatch((0, _ui.closeActiveSearch)());
     dispatch((0, _ui.clearHighlightLineRange)());
   };
 }
 
@@ -16779,17 +16773,18 @@ var _initialiseProps = function () {
     if (!(0, _sourcesTree.nodeHasChildren)(item)) {
       const obj = item.contents.get("id");
       const source = sources.get(obj);
       if (source && source.get("isBlackBoxed")) {
         return _react2.default.createElement("img", { className: "blackBox" });
       }
 
       const sourceType = sourceTypes[(0, _sourcesTree.getExtension)(source)];
-      return _react2.default.createElement("img", { className: sourceType || "file" });
+      const classNames = (0, _classnames2.default)("source-icon", sourceType || "file");
+      return _react2.default.createElement("img", { className: classNames });
     }
 
     return _react2.default.createElement("img", { className: "folder" });
   };
 
   this.onContextMenu = (event, item) => {
     const copySourceUri2Label = L10N.getStr("copySourceUri2");
     const copySourceUri2Key = L10N.getStr("copySourceUri2.accesskey");
@@ -17072,17 +17067,17 @@ class Editor extends _react.PureComponen
         conditionalPanelLine,
         closeConditionalPanel,
         addOrToggleDisabledBreakpoint,
         toggleBreakpoint,
         continueToHere
       } = this.props;
 
       // ignore right clicks in the gutter
-      if (ev.ctrlKey && ev.button === 0 || ev.which === 3 || selectedSource && selectedSource.get("isBlackBoxed") || !selectedSource) {
+      if (ev.ctrlKey && ev.button === 0 || ev.which === 3 || selectedSource && selectedSource.isBlackBoxed || !selectedSource) {
         return;
       }
 
       if (conditionalPanelLine) {
         return closeConditionalPanel();
       }
 
       if (gutter === "CodeMirror-foldgutter") {
@@ -17572,17 +17567,17 @@ class SourceFooter extends _react.PureCo
   blackBoxButton() {
     const { selectedSource, toggleBlackBox } = this.props;
     const sourceLoaded = selectedSource && (0, _source.isLoaded)(selectedSource);
 
     if (!sourceLoaded) {
       return;
     }
 
-    const blackboxed = selectedSource.get("isBlackBoxed");
+    const blackboxed = selectedSource.isBlackBoxed;
 
     const tooltip = L10N.getStr("sourceFooter.blackbox");
     const type = "black-box";
 
     return _react2.default.createElement(
       "button",
       {
         onClick: () => toggleBlackBox(selectedSource.toJS()),
@@ -17596,17 +17591,17 @@ class SourceFooter extends _react.PureCo
       },
       _react2.default.createElement("img", { className: "blackBox" })
     );
   }
 
   blackBoxSummary() {
     const { selectedSource } = this.props;
 
-    if (!selectedSource || !selectedSource.get("isBlackBoxed")) {
+    if (!selectedSource || !selectedSource.isBlackBoxed) {
       return;
     }
 
     return _react2.default.createElement(
       "span",
       { className: "blackbox-summary" },
       L10N.getStr("sourceFooter.blackboxed")
     );
@@ -17783,20 +17778,20 @@ class SearchBar extends _react.Component
   constructor(props) {
     super(props);
 
     this.onEscape = e => {
       this.closeSearch(e);
     };
 
     this.clearSearch = () => {
-      const { editor: ed, query, modifiers } = this.props;
-      if (ed && modifiers) {
+      const { editor: ed, query } = this.props;
+      if (ed) {
         const ctx = { ed, cm: ed.codeMirror };
-        (0, _editor.removeOverlay)(ctx, query, modifiers.toJS());
+        (0, _editor.removeOverlay)(ctx, query);
       }
     };
 
     this.closeSearch = e => {
       const { closeFileSearch, editor, searchOn } = this.props;
       if (editor && searchOn) {
         this.clearSearch();
         closeFileSearch(editor);
@@ -21981,17 +21976,17 @@ function getMenuItems(event, {
   const selectionText = editor.codeMirror.getSelection().trim();
   const sourceLocation = (0, _editor.getSourceLocationFromMouseEvent)(editor, selectedLocation, event);
   const isTextSelected = editor.codeMirror.somethingSelected();
 
   // localizations
   const blackboxKey = L10N.getStr("sourceFooter.blackbox.accesskey");
   const blackboxLabel = L10N.getStr("sourceFooter.blackbox");
   const unblackboxLabel = L10N.getStr("sourceFooter.unblackbox");
-  const toggleBlackBoxLabel = selectedSource.get("isBlackBoxed") ? unblackboxLabel : blackboxLabel;
+  const toggleBlackBoxLabel = selectedSource.isBlackBoxed ? unblackboxLabel : blackboxLabel;
   const copyFunctionKey = L10N.getStr("copyFunction.accesskey");
   const copyFunctionLabel = L10N.getStr("copyFunction.label");
   const copySourceKey = L10N.getStr("copySource.accesskey");
   const copySourceLabel = L10N.getStr("copySource");
   const copyToClipboardKey = L10N.getStr("copyToClipboard.accesskey");
   const copyToClipboardLabel = L10N.getStr("copyToClipboard.label");
   const copySourceUri2Key = L10N.getStr("copySourceUri2.accesskey");
   const copySourceUri2Label = L10N.getStr("copySourceUri2");
@@ -22005,33 +22000,33 @@ function getMenuItems(event, {
 
   // menu items
 
   const copyToClipboardItem = {
     id: "node-menu-copy-to-clipboard",
     label: copyToClipboardLabel,
     accesskey: copyToClipboardKey,
     disabled: false,
-    click: () => (0, _clipboard.copyToTheClipboard)(selectedSource.get("text"))
+    click: () => (0, _clipboard.copyToTheClipboard)(selectedSource.text)
   };
 
   const copySourceItem = {
     id: "node-menu-copy-source",
     label: copySourceLabel,
     accesskey: copySourceKey,
     disabled: selectionText.length === 0,
     click: () => (0, _clipboard.copyToTheClipboard)(selectionText)
   };
 
   const copySourceUri2Item = {
     id: "node-menu-copy-source-url",
     label: copySourceUri2Label,
     accesskey: copySourceUri2Key,
     disabled: false,
-    click: () => (0, _clipboard.copyToTheClipboard)((0, _source.getRawSourceURL)(selectedSource.get("url")))
+    click: () => (0, _clipboard.copyToTheClipboard)((0, _source.getRawSourceURL)(selectedSource.url))
   };
 
   const sourceId = selectedSource.get("id");
   const sourceLine = (0, _editor.toSourceLine)(sourceId, line);
 
   const functionText = getFunctionText(sourceLine);
   const copyFunctionItem = {
     id: "node-menu-copy-function",
@@ -22820,17 +22815,17 @@ function isCurrentlyPausedAtBreakpoint(f
 }
 
 function getBreakpointFilename(source) {
   return source && source.toJS ? (0, _source.getFilename)(source.toJS()) : "";
 }
 
 function renderSourceLocation(source, line, column) {
   const filename = getBreakpointFilename(source);
-  const isWasm = source && source.get("isWasm");
+  const isWasm = source && source.isWasm;
   const columnVal = _prefs.features.columnBreakpoints && column ? `:${column}` : "";
   const bpLocation = isWasm ? `0x${line.toString(16).toUpperCase()}` : `${line}${columnVal}`;
 
   if (!filename) {
     return null;
   }
 
   return _react2.default.createElement(
@@ -22940,17 +22935,17 @@ function updateLocation(sources, frame, 
   const locationId = (0, _breakpoint.makeLocationId)(bp.location);
 
   const location = _extends({}, bp.location, { source });
   const localBP = _extends({}, bp, { location, locationId, isCurrentlyPaused });
 
   return localBP;
 }
 
-const _getBreakpoints = (0, _reselect.createSelector)(_selectors.getBreakpoints, _selectors.getSources, _selectors.getTopFrame, _selectors.getPauseReason, (breakpoints, sources, frame, why) => breakpoints.map(bp => updateLocation(sources, frame, why, bp)).filter(bp => bp.location.source && !bp.location.source.get("isBlackBoxed")));
+const _getBreakpoints = (0, _reselect.createSelector)(_selectors.getBreakpoints, _selectors.getSources, _selectors.getTopFrame, _selectors.getPauseReason, (breakpoints, sources, frame, why) => breakpoints.map(bp => updateLocation(sources, frame, why, bp)).filter(bp => bp.location.source && !bp.location.source.isBlackBoxed));
 
 exports.default = (0, _reactRedux.connect)((state, props) => ({ breakpoints: _getBreakpoints(state) }), dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(Breakpoints);
 
 /***/ }),
 
 /***/ 1601:
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -24996,20 +24991,20 @@ class Tabs extends _react.PureComponent 
 
   constructor(props) {
     super(props);
 
     this.renderDropdownSource = source => {
       const { selectSource } = this.props;
       const filename = (0, _source.getFilename)(source.toJS());
 
-      const onClick = () => selectSource(source.get("id"));
+      const onClick = () => selectSpecificSource(source.id);
       return _react2.default.createElement(
         "li",
-        { key: source.get("id"), onClick: onClick },
+        { key: source.id, onClick: onClick },
         _react2.default.createElement("img", { className: `dropdown-icon ${this.getIconClass(source)}` }),
         filename
       );
     };
 
     this.state = {
       dropdownShown: false,
       hiddenTabs: I.List()
@@ -25043,33 +25038,33 @@ class Tabs extends _react.PureComponent 
     if (!this.refs.sourceTabs) {
       return;
     }
     const { selectedSource, tabSources, moveTab } = this.props;
     const sourceTabEls = this.refs.sourceTabs.children;
     const hiddenTabs = (0, _tabs.getHiddenTabs)(tabSources, sourceTabEls);
 
     if ((0, _ui.isVisible)() && hiddenTabs.indexOf(selectedSource) !== -1) {
-      return moveTab(selectedSource.get("url"), 0);
+      return moveTab(selectedSource.url, 0);
     }
 
     this.setState({ hiddenTabs });
   }
 
   toggleSourcesDropdown(e) {
     this.setState(prevState => ({
       dropdownShown: !prevState.dropdownShown
     }));
   }
 
   getIconClass(source) {
     if ((0, _source.isPretty)(source)) {
       return "prettyPrint";
     }
-    if (source.get("isBlackBoxed")) {
+    if (source.isBlackBoxed) {
       return "blackBox";
     }
     return "file";
   }
 
   renderTabs() {
     const { tabSources } = this.props;
     if (!tabSources) {
@@ -25656,17 +25651,17 @@ var _devtoolsSourceMap = __webpack_requi
 function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
 
 function mapScopes(scopes, frame) {
   return async function ({ dispatch, getState, client, sourceMaps }) {
     const generatedSourceRecord = (0, _selectors.getSource)(getState(), frame.generatedLocation.sourceId);
 
     const sourceRecord = (0, _selectors.getSource)(getState(), frame.location.sourceId);
 
-    const shouldMapScopes = _prefs.features.mapScopes && !generatedSourceRecord.get("isWasm") && !sourceRecord.get("isPrettyPrinted") && !(0, _devtoolsSourceMap.isGeneratedId)(frame.location.sourceId);
+    const shouldMapScopes = _prefs.features.mapScopes && !generatedSourceRecord.isWasm && !sourceRecord.isPrettyPrinted && !(0, _devtoolsSourceMap.isGeneratedId)(frame.location.sourceId);
 
     await dispatch({
       type: "MAP_SCOPES",
       frame,
       [_promise.PROMISE]: async function () {
         if (!shouldMapScopes) {
           return null;
         }
@@ -26103,17 +26098,17 @@ function formatSymbol(symbol) {
     title: symbol.name,
     subtitle: `${symbol.location.start.line}`,
     value: symbol.name,
     location: symbol.location
   };
 }
 
 function formatSymbols(symbols) {
-  if (!symbols) {
+  if (!symbols || symbols.loading) {
     return { variables: [], functions: [] };
   }
 
   const { variables, functions } = symbols;
 
   return {
     variables: variables.map(formatSymbol),
     functions: functions.map(formatSymbol)
@@ -26133,23 +26128,23 @@ function formatShortcutResults() {
     value: L10N.getStr("gotoLineModal.title"),
     title: `: ${L10N.getStr("gotoLineModal.placeholder")}`,
     id: ":"
   }];
 }
 
 function formatSources(sources) {
   return sources.valueSeq().filter(source => !(0, _source.isPretty)(source)).map(source => {
-    const sourcePath = (0, _source.getSourcePath)(source.get("url"));
+    const sourcePath = (0, _source.getSourcePath)(source.url);
     return {
       value: sourcePath,
-      title: sourcePath.split("/").pop(),
-      subtitle: (0, _utils.endTruncateStr)(sourcePath, 100),
-      id: source.get("id"),
-      url: source.get("url")
+      title: sourcePath.split("/").pop().split("?")[0],
+      subtitle: (0, _utils.endTruncateStr)(sourcePath, 100).replace(sourcePath.split("/").pop(), "").slice(1, -1),
+      id: source.id,
+      url: source.url
     };
   }).filter(({ value }) => value != "").toJS();
 }
 
 /***/ }),
 
 /***/ 1637:
 /***/ (function(module, exports, __webpack_require__) {
@@ -26834,34 +26829,34 @@ exports.continueToHere = continueToHere;
 var _selectors = __webpack_require__(3590);
 
 var _breakpoints = __webpack_require__(1396);
 
 var _commands = __webpack_require__(1637);
 
 function continueToHere(line) {
   return async function ({ dispatch, getState }) {
-    const source = (0, _selectors.getSelectedSource)(getState()).toJS();
-
-    if (!(0, _selectors.isPaused)(getState())) {
+    const selectedSource = (0, _selectors.getSelectedSource)(getState());
+
+    if (!(0, _selectors.isPaused)(getState()) || !selectedSource) {
       return;
     }
 
     const selectedFrame = (0, _selectors.getSelectedFrame)(getState());
     const debugLine = selectedFrame.location.line;
     if (debugLine == line) {
       return;
     }
 
     const action = (0, _selectors.getCanRewind)(getState()) && line < debugLine ? _commands.rewind : _commands.resume;
 
     await dispatch((0, _breakpoints.addHiddenBreakpoint)({
       line,
       column: undefined,
-      sourceId: source.id
+      sourceId: selectedSource.id
     }));
 
     dispatch(action());
   };
 } /* 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/>. */
 
@@ -27390,19 +27385,17 @@ class QuickOpenModal extends _react.Comp
       if (newQuery === "") {
         return results;
       }
       newQuery = query.replace(/[@:#?]/gi, " ");
 
       return results.map(result => {
         return _extends({}, result, {
           title: this.renderHighlight(result.title, (0, _path.basename)(newQuery), "title")
-        }, result.subtitle != null && !this.isSymbolSearch() ? {
-          subtitle: this.renderHighlight(result.subtitle, newQuery, "subtitle")
-        } : null);
+        });
       });
     };
 
     this.renderLoading = () => {
       const { symbolsLoading } = this.props;
 
       if ((this.isFunctionQuery() || this.isVariableQuery()) && symbolsLoading) {
         return _react2.default.createElement(
@@ -29887,18 +29880,18 @@ const SAMPLE_SIZE = 50; /* This Source C
                          * 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/>. */
 
 const INDENT_COUNT_THRESHOLD = 5;
 const CHARACTER_LIMIT = 250;
 const _minifiedCache = new Map();
 
 function isMinified(source) {
-  if (_minifiedCache.has(source.get("id"))) {
-    return _minifiedCache.get(source.get("id"));
+  if (_minifiedCache.has(source.id)) {
+    return _minifiedCache.get(source.id);
   }
 
   let text = source.get("text");
   if (!text) {
     return false;
   }
 
   let lineEndIndex = 0;
@@ -30276,30 +30269,35 @@ function updatePreview(target, editor) {
       return;
     }
 
     if (!(0, _selectors.isLineInScope)(getState(), tokenPos.line)) {
       return;
     }
 
     const source = (0, _selectors.getSelectedSource)(getState());
-    const symbols = (0, _selectors.getSymbols)(getState(), source.toJS());
+    const symbols = (0, _selectors.getSymbols)(getState(), source);
 
     let match;
     if (!symbols || symbols.loading) {
       match = (0, _getExpression.getExpressionFromCoords)(editor.codeMirror, tokenPos);
     } else {
       match = (0, _ast.findBestMatchExpression)(symbols, tokenPos);
     }
 
     if (!match || !match.expression) {
       return;
     }
 
     const { expression, location } = match;
+
+    if ((0, _preview.isConsole)(expression)) {
+      return;
+    }
+
     dispatch(setPreview(expression, location, tokenPos, cursorPos));
   };
 }
 
 function setPreview(expression, location, tokenPos, cursorPos) {
   return async ({ dispatch, getState, client, sourceMaps }) => {
     await dispatch({
       type: "SET_PREVIEW",
@@ -30925,30 +30923,30 @@ var _pause = __webpack_require__(1639);
 
 var _selectors = __webpack_require__(3590);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
 function createPrettySource(sourceId) {
   return async ({ dispatch, getState, sourceMaps }) => {
     const source = (0, _selectors.getSource)(getState(), sourceId);
-    const url = (0, _source.getPrettySourceURL)(source.get("url"));
+    const url = (0, _source.getPrettySourceURL)(source.url);
     const id = await sourceMaps.generatedToOriginalId(sourceId, url);
 
     const prettySource = {
       url,
       id,
       isPrettyPrinted: true,
       contentType: "text/javascript",
       loadedState: "loading"
     };
     dispatch({ type: "ADD_SOURCE", source: prettySource });
 
     const { code, mappings } = await (0, _prettyPrint.prettyPrint)({ source, url });
-    await sourceMaps.applySourceMap(source.get("id"), url, code, mappings);
+    await sourceMaps.applySourceMap(source.id, url, code, mappings);
 
     const loadedPrettySource = _extends({}, prettySource, {
       text: code,
       loadedState: "loaded"
     });
 
     (0, _parser.setSource)(loadedPrettySource);
 
@@ -30979,17 +30977,17 @@ function togglePrettyPrint(sourceId) {
 
     if (!(0, _source.isLoaded)(source)) {
       await dispatch((0, _loadSourceText.loadSourceText)(source));
     }
 
     (0, _assert2.default)(sourceMaps.isGeneratedId(sourceId), "Pretty-printing only allowed on generated sources");
 
     const selectedLocation = (0, _selectors.getSelectedLocation)(getState());
-    const url = (0, _source.getPrettySourceURL)(source.get("url"));
+    const url = (0, _source.getPrettySourceURL)(source.url);
     const prettySource = (0, _selectors.getSourceByURL)(getState(), url);
 
     const options = {};
     if (selectedLocation) {
       options.location = await sourceMaps.getOriginalLocation(selectedLocation);
     }
 
     if (prettySource) {
@@ -31456,17 +31454,17 @@ var _selectors = __webpack_require__(359
  *
  * @memberof actions/sources
  * @static
  */
 function selectSourceURL(url, options = {}) {
   return async ({ dispatch, getState }) => {
     const source = (0, _selectors.getSourceByURL)(getState(), url);
     if (source) {
-      const sourceId = source.get("id");
+      const sourceId = source.id;
       const location = (0, _location.createLocation)(_extends({}, options.location, { sourceId }));
       // flow is unable to comprehend that if an options.location object
       // exists, that we have a valid Location object, and if it doesnt,
       // we have a valid { sourceId: string } object. So we are overriding
       // the error
       // $FlowIgnore
       await dispatch(selectLocation(location, options.tabIndex));
     } else {
@@ -31526,17 +31524,17 @@ function selectLocation(location) {
     const selectedSource = (0, _selectors.getSelectedSource)(getState());
     if (!selectedSource) {
       return;
     }
 
     const sourceId = selectedSource.get("id");
     if (_prefs.prefs.autoPrettyPrint && !(0, _selectors.getPrettySource)(getState(), sourceId) && (0, _source.shouldPrettyPrint)(selectedSource) && (0, _source.isMinified)(selectedSource)) {
       await dispatch((0, _prettyPrint.togglePrettyPrint)(sourceId));
-      dispatch((0, _tabs.closeTab)(source.get("url")));
+      dispatch((0, _tabs.closeTab)(source.url));
     }
 
     dispatch((0, _ast.setSymbols)(sourceId));
     dispatch((0, _ast.setOutOfScopeLocations)());
   };
 }
 
 /**
@@ -31884,16 +31882,17 @@ function createEditor() {
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 exports.isImmutable = isImmutable;
 exports.isReactComponent = isReactComponent;
+exports.isConsole = isConsole;
 
 
 const IMMUTABLE_FIELDS = ["_root", "__ownerID", "__altered", "__hash"]; /* 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/>. */
 
 function isImmutable(result) {
   if (!result || !result.preview) {
@@ -31916,16 +31915,21 @@ function isReactComponent(result) {
   const ownProperties = result.preview.ownProperties;
   if (!ownProperties) {
     return;
   }
 
   return Object.keys(ownProperties).includes("_reactInternalInstance") || Object.keys(ownProperties).includes("_reactInternalFiber");
 }
 
+function isConsole(expression) {
+  return (/^console/.test(expression)
+  );
+}
+
 /***/ }),
 
 /***/ 1808:
 /***/ (function(module, exports) {
 
 module.exports = "<!-- 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/. --><svg viewBox=\"0 0 256 247\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" preserveAspectRatio=\"xMidYMid\"><defs><radialGradient cx=\"78.7636112%\" cy=\"37.8476394%\" fx=\"78.7636112%\" fy=\"37.8476394%\" r=\"89.8725577%\" id=\"radialGradient-1\"><stop stop-color=\"#F80090\" offset=\"0%\"></stop><stop stop-color=\"#4D008E\" offset=\"100%\"></stop></radialGradient><radialGradient cx=\"68.7389016%\" cy=\"4.39833672%\" fx=\"68.7389016%\" fy=\"4.39833672%\" r=\"81.7284786%\" id=\"radialGradient-2\"><stop stop-color=\"#57008E\" offset=\"0%\"></stop><stop stop-color=\"#5C008E\" offset=\"29.1746283%\"></stop><stop stop-color=\"#F80090\" offset=\"100%\"></stop></radialGradient><linearGradient x1=\"18.2386532%\" y1=\"0%\" x2=\"81.1591125%\" y2=\"84.3374763%\" id=\"linearGradient-3\"><stop stop-color=\"#F70090\" offset=\"0%\"></stop><stop stop-color=\"#E50090\" offset=\"66.9712865%\"></stop><stop stop-color=\"#D6008F\" stop-opacity=\"0.2\" offset=\"82.7147533%\"></stop><stop stop-color=\"#C10090\" stop-opacity=\"0\" offset=\"100%\"></stop></linearGradient><linearGradient x1=\"64.9060589%\" y1=\"71.5585538%\" x2=\"44.2897699%\" y2=\"50%\" id=\"linearGradient-4\"><stop stop-color=\"#B2008F\" stop-opacity=\"0.151340138\" offset=\"0%\"></stop><stop stop-color=\"#F70090\" stop-opacity=\"0.4\" offset=\"40.0350765%\"></stop><stop stop-color=\"#F60090\" stop-opacity=\"0.891668\" offset=\"64.8995536%\"></stop><stop stop-color=\"#FF0090\" offset=\"100%\"></stop></linearGradient></defs><g><path d=\"M16.6852208,157.125328 C3.56690702,87.3798324 38.2363025,20.1145078 117.808706,11.1662199 C106.835616,-0.558801732 91.8452087,-0.646905628 84.9481697,0.779380087 C72.770288,4.66044372 73.1525932,12.540855 59.3390152,22.7199675 C45.6064437,30.5634307 38.7094156,24.5568182 28.7057455,32.6879515 C18.7234849,40.7583874 25.6888528,59.2851732 21.5022823,62.8870857 C17.3464381,70.0905489 4.45500952,76.5077264 2.10834286,85.6062545 C0.168948918,97.2420641 7.37241212,105.553752 7.09535584,115.527778 C7.92652468,123.839467 -1.17920693,128.539449 0.129052814,135.275796 C4.0477368,146.281025 11.600845,152.904887 15.1615723,155.958047 C15.9781085,156.533531 16.8404881,157.95083 16.6852208,157.125328 L16.6852208,157.125328 Z\" fill=\"#FF0090\"></path><path d=\"M158.275491,60.578542 C155.368486,60.578542 153.011422,58.2214776 153.011422,55.3144727 C153.011422,52.4074679 155.368486,50.0504035 158.275491,50.0504035 C161.182496,50.0504035 163.53956,52.4074679 163.53956,55.3144727 C163.53956,58.2214776 161.182496,60.578542 158.275491,60.578542 L158.275491,60.578542 Z M19.7566405,164.732808 C7.1500258,104.116773 46.1602355,53.4676156 121.704062,78.4026805 C166.031404,104.334594 221.793282,102.646102 224.307422,85.8832 C230.514061,65.7878769 196.047681,24.3767065 144.515214,13.5715117 C42.2814476,-6.37654026 -12.8335943,104.116774 19.7566405,164.732808 L19.7566405,164.732808 Z\" fill=\"url(#radialGradient-1)\"></path><path d=\"M187.458604,171.493257 C202.639072,173.137863 217.048769,169.494573 230.402327,158.61014 C210.228197,181.112651 185.002777,192.426521 156.059262,195.505171 C169.878829,207.254019 183.20579,212.546348 195.955366,210.281136 C160.528734,220.05679 130.847947,209.296529 94.7424273,173.340673 C92.8517347,183.020022 103.074741,198.100667 113.611745,207.727264 C52.4742909,181.221845 47.1143627,98.6544556 121.66531,78.3442237 C44.3844415,41.214641 0.686373501,113.357693 22.1558444,172.485931 C43.1623368,218.026693 99.1402667,253.085223 160.492163,245.3753 C190.292928,241.7251 234.79401,221.178935 252.973664,172.485931 C240.160919,183.983766 217.257941,193.997836 207.037617,194.765984 C241.628648,177.478781 260.301586,148.103896 255.060336,107.955387 C247.895106,125.013742 238.441392,138.114625 226.616076,147.112305 C251.735653,107.955387 247.425219,87.716426 228.832526,65.4732398 C242.131228,102.044668 224.928249,142.633967 187.458604,171.493257 L187.458604,171.493257 Z\" fill=\"url(#radialGradient-2)\"></path><path d=\"M169.707072,213.625541 C167.082407,213.13513 175.656929,217.098842 159.079366,212.710316 C142.501804,208.32179 125.622502,204.092744 94.7424273,173.340673 C92.8517347,183.020022 103.074741,198.100667 113.611745,207.727264 C142.056275,227.564927 122.711866,218.286797 166.051946,233.269481 C169.52976,226.346862 169.707072,220.195346 169.707072,213.625541 L169.707072,213.625541 Z\" fill=\"url(#linearGradient-3)\"></path><path d=\"M114.601372,57.8510108 C114.601372,57.8510108 118.369452,52.2893628 119.836219,49.7810251 C121.633641,46.7072319 124.393939,41.104618 124.393939,41.104618 C124.393939,41.104618 95.389611,31.6417749 88.2716448,30.4871665 C66.1450215,36.2308801 66.0645022,45.5009559 78.435065,59.690116 C79.8114806,61.2693368 114.601372,57.8510108 114.601372,57.8510108 L114.601372,57.8510108 Z\" fill=\"url(#linearGradient-4)\"></path></g></svg>"
 
 /***/ }),
@@ -32776,17 +32780,17 @@ function getSourceAnnotation(source, sou
 
   if (framework) {
     return _react2.default.createElement("img", { className: framework.toLowerCase() });
   }
 
   if ((0, _source.isPretty)(source)) {
     return _react2.default.createElement("img", { className: "prettyPrint" });
   }
-  if (source.get("isBlackBoxed")) {
+  if (source.isBlackBoxed) {
     return _react2.default.createElement("img", { className: "blackBox" });
   }
 }
 
 function getTabMenuItems() {
   return {
     closeTab: {
       id: "node-menu-close-tab",
@@ -33513,17 +33517,17 @@ class Tab extends _react.PureComponent {
         }
       }),
       hidden: () => tabSources.size === 1 || tabSources.some((t, i) => t === tab && tabSources.size - 1 === i)
     }, {
       item: _extends({}, tabMenuItems.closeAllTabs, { click: () => closeTabs(tabURLs) })
     }, { item: { type: "separator" } }, {
       item: _extends({}, tabMenuItems.copyToClipboard, {
         disabled: selectedSource.get("id") !== tab,
-        click: () => (0, _clipboard.copyToTheClipboard)(sourceTab.get("text"))
+        click: () => (0, _clipboard.copyToTheClipboard)(sourceTab.text)
       })
     }, {
       item: _extends({}, tabMenuItems.copySourceUri2, {
         click: () => (0, _clipboard.copyToTheClipboard)((0, _source.getRawSourceURL)(sourceTab.get("url")))
       })
     }];
 
     items.push({
@@ -33554,33 +33558,33 @@ class Tab extends _react.PureComponent {
       selectedSource,
       selectSource,
       closeTab,
       source,
       sourceMetaData
     } = this.props;
     const src = source.toJS();
     const filename = (0, _source.getFilename)(src);
-    const sourceId = source.get("id");
+    const sourceId = source.id;
     const active = selectedSource && sourceId == selectedSource.get("id") && !this.isProjectSearchEnabled() && !this.isSourceSearchEnabled();
     const isPrettyCode = (0, _source.isPretty)(source);
     const sourceAnnotation = (0, _tabs.getSourceAnnotation)(source, sourceMetaData);
 
     function onClickClose(e) {
       e.stopPropagation();
-      closeTab(source.get("url"));
+      closeTab(source.url);
     }
 
     function handleTabClick(e) {
       e.preventDefault();
       e.stopPropagation();
 
       // Accommodate middle click to close tab
       if (e.button === 1) {
-        return closeTab(source.get("url"));
+        return closeTab(source.url);
       }
 
       return selectSource(sourceId);
     }
 
     const className = (0, _classnames2.default)("source-tab", {
       active,
       pretty: isPrettyCode
@@ -33609,17 +33613,17 @@ class Tab extends _react.PureComponent {
   }
 }
 exports.default = (0, _reactRedux.connect)((state, props) => {
   const selectedSource = (0, _selectors.getSelectedSource)(state);
   const { source } = props;
   return {
     tabSources: (0, _selectors.getSourcesForTabs)(state),
     selectedSource: selectedSource,
-    sourceMetaData: (0, _selectors.getSourceMetaData)(state, source.get("id")),
+    sourceMetaData: (0, _selectors.getSourceMetaData)(state, source.id),
     activeSearch: (0, _selectors.getActiveSearch)(state)
   };
 }, dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(Tab);
 
 /***/ }),
 
 /***/ 22:
 /***/ (function(module, exports) {
@@ -33633,32 +33637,30 @@ module.exports = __WEBPACK_EXTERNAL_MODU
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 exports.getTokenLocation = getTokenLocation;
-/* 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/>. */
-
 function getTokenLocation(codeMirror, tokenEl) {
   const { left, top, width, height } = tokenEl.getBoundingClientRect();
   const { line, ch } = codeMirror.coordsChar({
     left: left + width / 2,
     top: top + height / 2
   });
 
   return {
     line: line + 1,
     column: ch
   };
-}
+} /* 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/>. */
 
 /***/ }),
 
 /***/ 2245:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
--- a/devtools/client/debugger/new/parser-worker.js
+++ b/devtools/client/debugger/new/parser-worker.js
@@ -992,17 +992,20 @@ let ASTs = new Map();
 function _parse(code, opts) {
   return babylon.parse(code, _extends({}, opts, {
     tokens: true
   }));
 }
 
 const sourceOptions = {
   generated: {
-    tokens: true
+    tokens: true,
+    plugins: [
+      "objectRestSpread"
+    ]
   },
   original: {
     sourceType: "unambiguous",
     tokens: true,
     plugins: ["jsx", "flow", "doExpressions", "objectRestSpread", "classProperties", "exportDefaultFrom", "exportNamespaceFrom", "asyncGenerators", "functionBind", "functionSent", "dynamicImport"]
   }
 };
 
@@ -1041,16 +1044,21 @@ function getAst(sourceId) {
   let ast = {};
   const { contentType } = source;
   if (contentType == "text/html") {
     ast = (0, _parseScriptTags2.default)(source.text, htmlParser) || {};
   } else if (contentType && contentType.match(/(javascript|jsx)/)) {
     const type = source.id.includes("original") ? "original" : "generated";
     const options = sourceOptions[type];
     ast = parse(source.text, options);
+  } else if (contentType && contentType.match(/typescript/)) {
+    const options = _extends({}, sourceOptions.original, {
+      plugins: [...sourceOptions.original.plugins.filter(p => p !== "flow" && p !== "decorators" && p !== "decorators2"), "decorators", "typescript"]
+    });
+    ast = parse(source.text, options);
   }
 
   ASTs.set(source.id, ast);
   return ast;
 }
 
 function clearASTs() {
   ASTs = new Map();
@@ -1288,75 +1296,28 @@ function getVariables(dec) {
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
-exports.getClosestExpression = getClosestExpression;
 exports.getClosestPath = getClosestPath;
 
-var _types = __webpack_require__(2268);
-
-var t = _interopRequireWildcard(_types);
-
 var _simplePath = __webpack_require__(3591);
 
 var _simplePath2 = _interopRequireDefault(_simplePath);
 
 var _ast = __webpack_require__(1375);
 
-var _helpers = __webpack_require__(1411);
-
 var _contains = __webpack_require__(1456);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
-function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
-
-function getNodeValue(node) {
-  if (t.isThisExpression(node)) {
-    return "this";
-  }
-
-  return node.name;
-} /* 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/>. */
-
-function getClosestMemberExpression(sourceId, token, location) {
-  const closest = getClosestPath(sourceId, location).find(path => t.isMemberExpression(path.node) && path.node.property.name === token);
-
-  if (closest) {
-    const memberExpression = (0, _helpers.getMemberExpression)(closest.node);
-    return {
-      expression: memberExpression,
-      location: closest.node.loc
-    };
-  }
-  return null;
-}
-
-function getClosestExpression(sourceId, token, location) {
-  const memberExpression = getClosestMemberExpression(sourceId, token, location);
-  if (memberExpression) {
-    return memberExpression;
-  }
-
-  const path = getClosestPath(sourceId, location);
-  if (!path || !path.node) {
-    return;
-  }
-
-  const { node } = path;
-  return { expression: getNodeValue(node), location: node.loc };
-}
-
 function getClosestPath(sourceId, location) {
   let closestPath = null;
 
   (0, _ast.traverseAst)(sourceId, {
     enter(node, ancestors) {
       if ((0, _contains.nodeContainsPosition)(node, location)) {
         const path = (0, _simplePath2.default)(ancestors);
 
@@ -1367,17 +1328,19 @@ function getClosestPath(sourceId, locati
     }
   });
 
   if (!closestPath) {
     throw new Error("Assertion failure - This should always fine a path");
   }
 
   return closestPath;
-}
+} /* 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/>. */
 
 /***/ }),
 
 /***/ 1456:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
@@ -1903,18 +1866,16 @@ function clearSources() {
 /***/ }),
 
 /***/ 1618:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
-var _closest = __webpack_require__(1455);
-
 var _getSymbols = __webpack_require__(1457);
 
 var _ast = __webpack_require__(1375);
 
 var _getScopes = __webpack_require__(2413);
 
 var _getScopes2 = _interopRequireDefault(_getScopes);
 
@@ -1937,22 +1898,23 @@ var _pausePoints = __webpack_require__(3
 var _mapOriginalExpression = __webpack_require__(3613);
 
 var _mapOriginalExpression2 = _interopRequireDefault(_mapOriginalExpression);
 
 var _devtoolsUtils = __webpack_require__(1363);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
-const { workerHandler } = _devtoolsUtils.workerUtils; /* 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/>. */
+/* 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/>. */
+
+const { workerHandler } = _devtoolsUtils.workerUtils;
 
 self.onmessage = workerHandler({
-  getClosestExpression: _closest.getClosestExpression,
   findOutOfScopeLocations: _findOutOfScopeLocations2.default,
   getSymbols: _getSymbols.getSymbols,
   getScopes: _getScopes2.default,
   clearSymbols: _getSymbols.clearSymbols,
   clearScopes: _getScopes.clearScopes,
   clearASTs: _ast.clearASTs,
   hasSource: _sources.hasSource,
   setSource: _sources.setSource,
@@ -2309,18 +2271,16 @@ var _extends = Object.assign || function
                                                                                                                                                                                                                                                                    * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 exports.getNextStep = getNextStep;
 
 var _types = __webpack_require__(2268);
 
 var t = _interopRequireWildcard(_types);
 
-var _types2 = __webpack_require__(1627);
-
 var _closest = __webpack_require__(1455);
 
 var _helpers = __webpack_require__(1411);
 
 function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
 
 function getNextStep(sourceId, pausedPosition) {
   const currentExpression = getSteppableExpression(sourceId, pausedPosition);
@@ -2331,54 +2291,46 @@ function getNextStep(sourceId, pausedPos
   const currentStatement = currentExpression.find(p => {
     return p.inList && t.isStatement(p.node);
   });
 
   if (!currentStatement) {
     throw new Error("Assertion failure - this should always find at least Program");
   }
 
-  return _getNextStep(currentStatement, pausedPosition);
+  return _getNextStep(currentStatement, sourceId, pausedPosition);
 }
 
 function getSteppableExpression(sourceId, pausedPosition) {
   const closestPath = (0, _closest.getClosestPath)(sourceId, pausedPosition);
 
   if (!closestPath) {
     return null;
   }
 
   if ((0, _helpers.isAwaitExpression)(closestPath) || (0, _helpers.isYieldExpression)(closestPath)) {
     return closestPath;
   }
 
   return closestPath.find(p => t.isAwaitExpression(p.node) || t.isYieldExpression(p.node));
 }
 
-function _getNextStep(statement, position) {
+function _getNextStep(statement, sourceId, position) {
   const nextStatement = statement.getSibling(1);
   if (nextStatement) {
     return _extends({}, nextStatement.node.loc.start, {
-      sourceId: position.sourceId
+      sourceId: sourceId
     });
   }
 
   return null;
 }
 
 /***/ }),
 
-/***/ 1627:
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-/***/ }),
-
 /***/ 1629:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
@@ -19488,17 +19440,33 @@ const scopeCollectionVisitor = {
               declaration: {
                 start: fromBabelLocation(node.loc.start, state.sourceId),
                 end: fromBabelLocation(node.loc.end, state.sourceId)
               }
             }]
           };
         }
       });
-    } else if (t.isIdentifier(node) && t.isReferenced(node, parentNode)) {
+    } else if (t.isTSEnumDeclaration(node)) {
+      state.scope.bindings[node.id.name] = {
+        type: "const",
+        refs: [{
+          type: "decl",
+          start: fromBabelLocation(node.id.loc.start, state.sourceId),
+          end: fromBabelLocation(node.id.loc.end, state.sourceId),
+          declaration: {
+            start: fromBabelLocation(node.loc.start, state.sourceId),
+            end: fromBabelLocation(node.loc.end, state.sourceId)
+          }
+        }]
+      };
+    } else if (t.isIdentifier(node) && t.isReferenced(node, parentNode) &&
+    // Babel doesn't cover this in 'isReferenced' yet, but it should
+    // eventually.
+    !t.isTSEnumMember(parentNode, { id: node })) {
       let freeVariables = state.freeVariables.get(node.name);
       if (!freeVariables) {
         freeVariables = [];
         state.freeVariables.set(node.name, freeVariables);
       }
 
       freeVariables.push({
         type: "ref",
@@ -21099,72 +21067,84 @@ exports.getPausePoints = getPausePoints;
 var _ast = __webpack_require__(1375);
 
 var _types = __webpack_require__(2268);
 
 var t = _interopRequireWildcard(_types);
 
 function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
 
+/* 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/>. */
+
 const isControlFlow = node => t.isForStatement(node) || t.isWhileStatement(node) || t.isIfStatement(node);
 
 const isAssignment = node => t.isVariableDeclarator(node) || t.isAssignmentExpression(node);
 
 const isImport = node => t.isImport(node) || t.isImportDeclaration(node);
 const isReturn = node => t.isReturnStatement(node);
 const inExpression = parent => t.isArrayExpression(parent.node) || t.isObjectProperty(parent.node) || t.isCallExpression(parent.node) || t.isTemplateLiteral(parent.node);
 
 function getPausePoints(sourceId) {
   const state = [];
   (0, _ast.traverseAst)(sourceId, { enter: onEnter }, state);
   return state;
 }
 
-function formatNode(location, types) {
-  return { location, types };
-}
-
 function onEnter(node, ancestors, state) {
   const parent = ancestors[ancestors.length - 1];
 
-  if (isAssignment(node) || isImport(node) || isControlFlow(node)) {
-    state.push(formatNode(node.loc.start, { breakpoint: true, stepOver: true }));
+  if (isAssignment(node) || isImport(node) || isControlFlow(node) || t.isDebuggerStatement(node)) {
+    addPoint(state, node.loc.start);
   }
 
   if (isReturn(node)) {
     if (t.isCallExpression(node.argument)) {
-      state.push(formatNode(node.loc.start, { breakpoint: false, stepOver: false }));
-    } else {
-      state.push(formatNode(node.loc.start, { breakpoint: true, stepOver: true }));
+      addEmptyPoint(state, node.loc.start);
+    } else {
+      addPoint(state, node.loc.start);
     }
   }
 
   if (t.isCallExpression(node)) {
-    state.push(formatNode(node.loc.start, {
+    addPoint(state, node.loc.start, {
       breakpoint: true,
 
       // NOTE: we do not want to land inside an expression e.g. [], {}, call
       stepOver: !inExpression(parent)
-    }));
-  }
-
-  if (t.isDebuggerStatement(node)) {
-    state.push(formatNode(node.loc.start, { breakpoint: true, stepOver: true }));
+    });
   }
 
   if (t.isFunction(node)) {
     const { line, column } = node.loc.end;
-    state.push(formatNode(node.loc.start, { breakpoint: true }));
-    state.push(formatNode({ line, column: column - 1 }, { breakpoint: true, stepOver: true }));
+    addBreakPoint(state, node.loc.start);
+    addPoint(state, { line, column: column - 1 });
   }
 
   if (t.isProgram(node)) {
     const lastStatement = node.body[node.body.length - 1];
-    state.push(formatNode(lastStatement.loc.end, { breakpoint: true, stepOver: true }));
-  }
+    addPoint(state, lastStatement.loc.end);
+  }
+}
+
+function formatNode(location, types) {
+  return { location, types };
+}
+
+function addPoint(state, location, types = { breakpoint: true, stepOver: true }) {
+  state.push(formatNode(location, types));
+}
+
+function addEmptyPoint(state, location) {
+  addPoint(state, location, { breakpoint: false, stepOver: false });
+}
+
+function addBreakPoint(state, location) {
+  addPoint(state, location, { breakpoint: true, stepOver: false });
 }
 
 /***/ }),
 
 /***/ 3613:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
@@ -40580,9 +40560,9 @@ function listCacheHas(key) {
 }
 
 module.exports = listCacheHas;
 
 
 /***/ })
 
 /******/ });
-});
\ No newline at end of file
+});
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-editor-highlight.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-editor-highlight.js
@@ -35,11 +35,11 @@ add_task(async function() {
   selectSource(dbg, "simple1.js", 6);
 
   // Make sure the source is in the loading state, wait for it to be
   // fully loaded, and check the highlighted line.
   const simple1 = findSource(dbg, "simple1.js");
   is(getSource(getState(), simple1.id).get("loadedState"), "loading");
 
   await waitForSelectedSource(dbg, "simple1.js");
-  ok(getSource(getState(), simple1.id).get("text"));
+  ok(getSource(getState(), simple1.id).text);
   assertHighlightLocation(dbg, "simple1.js", 6);
 });
--- a/devtools/client/debugger/new/test/mochitest/head.js
+++ b/devtools/client/debugger/new/test/mochitest/head.js
@@ -254,17 +254,17 @@ function waitForSelectedSource(dbg, url)
         return false;
       }
 
       if (!url) {
         return true;
       }
 
       const newSource = findSource(dbg, url, { silent: true });
-      if (newSource.id != source.get("id")) {
+      if (newSource.id != source.id) {
         return false;
       }
 
       // wait for async work to be done
       const hasSymbols = dbg.selectors.hasSymbols(state, source);
       const hasSourceMetaData = dbg.selectors.hasSourceMetaData(state, source.id);
       return hasSymbols && hasSourceMetaData;
     },
@@ -309,17 +309,17 @@ function assertPausedLocation(dbg) {
   ok(isVisibleInEditor(dbg, getCM(dbg).display.gutters), "gutter is visible");
 }
 
 function assertDebugLine(dbg, line) {
   // Check the debug line
   const lineInfo = getCM(dbg).lineInfo(line - 1);
   const source = dbg.selectors.getSelectedSource(dbg.getState());
   if (source && source.get("loadedState") == "loading") {
-    const url = source.get("url");
+    const url = source.url;
     ok(
       false,
       `Looks like the source ${url} is still loading. Try adding waitForLoadedSource in the test.`
     );
     return;
   }
 
   ok(
@@ -461,17 +461,17 @@ function isSelectedFrameSelected(dbg, st
     return false;
   }
 
   const isLoaded = source.has("loadedState") && sourceUtils.isLoaded(source);
   if (!isLoaded) {
     return false;
   }
 
-  return source.get("id") == sourceId;
+  return source.id == sourceId;
 }
 
 function createDebuggerContext(toolbox) {
   const panel = toolbox.getPanel("jsdebugger");
   const win = panel.panelWin;
   const { store, client, selectors, actions } = panel.getVarsForTests();
 
   return {
--- a/devtools/client/jar.mn
+++ b/devtools/client/jar.mn
@@ -303,28 +303,31 @@ devtools.jar:
     skin/images/firebug/command-rulers.svg (themes/images/firebug/command-rulers.svg)
     skin/images/firebug/command-noautohide.svg (themes/images/firebug/command-noautohide.svg)
 
     # Debugger
     skin/images/debugger/arrow.svg (themes/images/debugger/arrow.svg)
     skin/images/debugger/back.svg (themes/images/debugger/back.svg)
     skin/images/debugger/blackBox.svg (themes/images/debugger/blackBox.svg)
     skin/images/debugger/close.svg (themes/images/debugger/close.svg)
+    skin/images/debugger/coffeescript.svg (themes/images/debugger/coffeescript.svg)
     skin/images/debugger/domain.svg (themes/images/debugger/domain.svg)
     skin/images/debugger/file.svg (themes/images/debugger/file.svg)
     skin/images/debugger/folder.svg (themes/images/debugger/folder.svg)
     skin/images/debugger/forward.svg (themes/images/debugger/forward.svg)
+    skin/images/debugger/javascript.svg (themes/images/debugger/javascript.svg)
     skin/images/debugger/pause-exceptions.svg (themes/images/debugger/pause-exceptions.svg)
     skin/images/debugger/pause.svg (themes/images/debugger/pause.svg)
     skin/images/debugger/prettyPrint.svg (themes/images/debugger/prettyPrint.svg)
     skin/images/debugger/react.svg (themes/images/debugger/react.svg)
     skin/images/debugger/resume.svg (themes/images/debugger/resume.svg)
     skin/images/debugger/stepIn.svg (themes/images/debugger/stepIn.svg)
     skin/images/debugger/stepOut.svg (themes/images/debugger/stepOut.svg)
     skin/images/debugger/stepOver.svg (themes/images/debugger/stepOver.svg)
+    skin/images/debugger/typescript.svg (themes/images/debugger/typescript.svg)
 
     # Netmonitor
     content/netmonitor/src/assets/styles/httpi.css (netmonitor/src/assets/styles/httpi.css)
     content/netmonitor/src/assets/styles/MdnLink.css (netmonitor/src/assets/styles/MdnLink.css)
     content/netmonitor/src/assets/styles/netmonitor.css (netmonitor/src/assets/styles/netmonitor.css)
     content/netmonitor/src/assets/styles/NetworkDetailsPanel.css (netmonitor/src/assets/styles/NetworkDetailsPanel.css)
     content/netmonitor/src/assets/styles/RequestList.css (netmonitor/src/assets/styles/RequestList.css)
     content/netmonitor/src/assets/styles/StatisticsPanel.css (netmonitor/src/assets/styles/StatisticsPanel.css)
--- a/devtools/client/netmonitor/src/components/SecurityPanel.js
+++ b/devtools/client/netmonitor/src/components/SecurityPanel.js
@@ -169,19 +169,17 @@ class SecurityPanel extends Component {
               fingerprint.sha1 || NOT_AVAILABLE,
           },
           [CERTIFICATE_TRANSPARENCY_LABEL]:
           securityInfo.certificateTransparency || NOT_AVAILABLE,
         },
       };
     } else {
       object = {
-        [ERROR_LABEL]:
-          new DOMParser().parseFromString(securityInfo.errorMessage, "text/html")
-            .body.textContent || NOT_AVAILABLE
+        [ERROR_LABEL]: securityInfo.errorMessage || NOT_AVAILABLE
       };
     }
 
     return div({ className: "panel-container security-panel" },
       PropertiesView({
         object,
         renderValue: (props) => this.renderValue(props, securityInfo.weaknessReasons),
         enableFilter: false,
new file mode 100644
--- /dev/null
+++ b/devtools/client/themes/images/debugger/coffeescript.svg
@@ -0,0 +1,6 @@
+<!-- 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/. -->
+<svg viewBox="0 0 128 128">
+<path d="M50.3 29.6c11.7-1 15-8.5 28.7-9.8 6.7-.6 11 .8 11.4 3.1.4 2.2-2.9 3.7-7 4-5.6.6-8-1.5-8.4-3.4-4.1.4-4.8 2.2-4.6 3.5.4 2.4 5.5 4.7 14.1 3.9 9.8-.8 13-4.6 12.2-8.5-1-5-8.5-9.2-22-8-17.3 1.6-17.2 9.5-28.9 10.5-4.8.4-7.5-.7-8-2.6-.3-1.9 2-2.8 4.8-3 2.6-.2 5.7.2 7.2 1 1.1-.6 1.5-1.1 1.3-1.8-.4-1.8-4-2.6-8.5-2.2-8.7.8-8.7 4.7-8.4 6.4 1.1 4.7 7.8 7.7 16.1 6.9zM108.9 49.4c-10.8 2.5-24.6 4.1-41.2 4.1-16.9 0-30.7-1.8-41.5-4.1-9.6-2.5-14.8-5.2-16.6-8 .9 6.3 2.5 12.4 4.6 18.2-2.4 1.5-4.7 3.5-6.7 6-3.8 4.8-5.5 10.4-5.2 15.9.3 5.5 3 10 7.3 13.5 4.5 3.5 9.3 4.5 14.8 3.5 2.1-.3 4.5-1.5 6.6-2.1-4.5 0-8.3-1.5-12.1-4.5-4.1-3-7-7.3-7.6-12.4-1-4.8 0-9.3 2.7-13.2.6-.8 1.2-1.4 1.9-2 1.5 3.8 3.3 7.4 5.2 10.9 4.1 6.3 8.3 11.8 12.4 17.7 1.8 3.5 3 7 3.8 10.4 2.7 3.8 6.6 6.5 11.4 7.9 5.9 2.1 12.1 2.9 18.4 2.9h.7c6.3 0 12.9-1 19-3 4.5-1.5 8.3-4 11.1-8h.3c.7-3 1.8-6.8 3.5-10.3 4.1-5.9 8.3-11.4 12.4-17.7 5.5-10 9.3-21.4 11.4-33.6-2.1 3-7.3 5.8-16.6 7.9zM26.2 40.7c10.8 2.7 24.6 4.1 41.2 4.1 16.9 0 30.4-1.5 41.2-4.1 11.4-2.7 16.9-6.3 16.9-9.6 0-2.5-2.5-4.8-7-6.6 1 .7 1.8 1.8 1.8 3 0 3.5-5.2 6.3-15.6 8.6-9.6 2.1-22 3.6-37 3.6-14.5 0-27.4-1.5-36.7-3.5-10-2.5-15.3-5.2-15.3-8.6 0-1.5.7-2.7 2.7-4.1-6.3 2.5-9.6 4.5-9.6 7.6.3 3.5 5.9 7 17.4 9.6z"></path>
+</svg> 
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/devtools/client/themes/images/debugger/javascript.svg
@@ -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/. -->
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 28 28">
+<path d="M9.633 7.968h3.751v10.514c0 4.738-2.271 6.392-5.899 6.392-0.888 0-2.024-0.148-2.764-0.395l0.42-3.036c0.518 0.173 1.185 0.296 1.925 0.296 1.58 0 2.567-0.716 2.567-3.282v-10.489zM16.641 20.753c0.987 0.518 2.567 1.037 4.171 1.037 1.728 0 2.641-0.716 2.641-1.826 0-1.012-0.79-1.629-2.789-2.32-2.764-0.987-4.59-2.517-4.59-4.961 0-2.838 2.394-4.985 6.293-4.985 1.9 0 3.258 0.37 4.245 0.839l-0.839 3.011c-0.642-0.321-1.851-0.79-3.455-0.79-1.629 0-2.419 0.765-2.419 1.604 0 1.061 0.913 1.53 3.085 2.369 2.937 1.086 4.294 2.616 4.294 4.985 0 2.789-2.122 5.158-6.688 5.158-1.9 0-3.776-0.518-4.714-1.037l0.765-3.085z"></path>
+</svg>
+
new file mode 100644
--- /dev/null
+++ b/devtools/client/themes/images/debugger/typescript.svg
@@ -0,0 +1,6 @@
+<!-- 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/. -->
+<svg viewBox="0 0 128 128">
+<path xmlns="http://www.w3.org/2000/svg" class="cls-2" id="original-2" d="M 1.5 63.91 v 62.5 h 125 V 1.41 H 1.5 Z m 100.73 -5 a 15.56 15.56 0 0 1 7.82 4.5 a 20.58 20.58 0 0 1 3 4 c 0 0.16 -5.4 3.81 -8.69 5.85 c -0.12 0.08 -0.6 -0.44 -1.13 -1.23 a 7.09 7.09 0 0 0 -5.87 -3.53 c -3.79 -0.26 -6.23 1.73 -6.21 5 a 4.58 4.58 0 0 0 0.54 2.34 c 0.83 1.73 2.38 2.76 7.24 4.86 c 8.95 3.85 12.78 6.39 15.16 10 c 2.66 4 3.25 10.46 1.45 15.24 c -2 5.2 -6.9 8.73 -13.83 9.9 a 38.32 38.32 0 0 1 -9.52 -0.1 a 23 23 0 0 1 -12.72 -6.63 c -1.15 -1.27 -3.39 -4.58 -3.25 -4.82 a 9.34 9.34 0 0 1 1.15 -0.73 L 82 101 l 3.59 -2.08 l 0.75 1.11 a 16.78 16.78 0 0 0 4.74 4.54 c 4 2.1 9.46 1.81 12.16 -0.62 a 5.43 5.43 0 0 0 0.69 -6.92 c -1 -1.39 -3 -2.56 -8.59 -5 c -6.45 -2.78 -9.23 -4.5 -11.77 -7.24 a 16.48 16.48 0 0 1 -3.43 -6.25 a 25 25 0 0 1 -0.22 -8 c 1.33 -6.23 6 -10.58 12.82 -11.87 A 31.66 31.66 0 0 1 102.23 58.93 Z M 72.89 64.15 l 0 5.12 H 56.66 V 115.5 H 45.15 V 69.26 H 28.88 v -5 A 49.19 49.19 0 0 1 29 59.09 C 29.08 59 39 59 51 59 L 72.83 59 Z" data-name="original" />
+</svg> 
\ No newline at end of file
--- a/devtools/shared/webconsole/network-helper.js
+++ b/devtools/shared/webconsole/network-helper.js
@@ -520,18 +520,17 @@ var NetworkHelper = {
    *         Returns an object containing following members:
    *          - state: The security of the connection used to fetch this
    *                   request. Has one of following string values:
    *                    * "insecure": the connection was not secure (only http)
    *                    * "weak": the connection has minor security issues
    *                    * "broken": secure connection failed (e.g. expired cert)
    *                    * "secure": the connection was properly secured.
    *          If state == broken:
-   *            - errorMessage: full error message from
-   *                            nsITransportSecurityInfo.
+   *            - errorMessage: error code string.
    *          If state == secure:
    *            - protocolVersion: one of TLSv1, TLSv1.1, TLSv1.2, TLSv1.3.
    *            - cipherSuite: the cipher suite used in this connection.
    *            - cert: information about certificate used in this connection.
    *                    See parseCertificateInfo for the contents.
    *            - hsts: true if host uses Strict Transport Security,
    *                    false otherwise
    *            - hpkp: true if host uses Public Key Pinning, false otherwise
@@ -705,17 +704,17 @@ var NetworkHelper = {
         DevToolsUtils.reportException("NetworkHelper.parseSecurityInfo",
           "Could not get HSTS/HPKP status as hostname is not available.");
         info.hsts = false;
         info.hpkp = false;
       }
     } else {
       // The connection failed.
       info.state = "broken";
-      info.errorMessage = securityInfo.errorMessage;
+      info.errorMessage = securityInfo.errorCodeString;
     }
 
     return info;
   },
 
   /**
    * Takes an nsIX509Cert and returns an object with certificate information.
    *
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -4615,98 +4615,96 @@ nsDocShell::DisplayLoadError(nsresult aE
       uint32_t securityState;
       tsi->GetSecurityState(&securityState);
       if (securityState & nsIWebProgressListener::STATE_USES_SSL_3) {
         error = "sslv3Used";
         addHostPort = true;
       } else if (securityState & nsIWebProgressListener::STATE_USES_WEAK_CRYPTO) {
         error = "weakCryptoUsed";
         addHostPort = true;
-      } else {
-        // Usually we should have aFailedChannel and get a detailed message
-        tsi->GetErrorMessage(getter_Copies(messageStr));
       }
     } else {
       // No channel, let's obtain the generic error message
       if (nsserr) {
         nsserr->GetErrorMessage(aError, messageStr);
       }
     }
-    if (!messageStr.IsEmpty()) {
-      if (errorClass == nsINSSErrorsService::ERROR_CLASS_BAD_CERT) {
-        error = "nssBadCert";
-
-        // If this is an HTTP Strict Transport Security host or a pinned host
-        // and the certificate is bad, don't allow overrides (RFC 6797 section
-        // 12.1, HPKP draft spec section 2.6).
-        uint32_t flags =
-          UsePrivateBrowsing() ? nsISocketProvider::NO_PERMANENT_STORAGE : 0;
-        bool isStsHost = false;
-        bool isPinnedHost = false;
-        if (XRE_IsParentProcess()) {
-          nsCOMPtr<nsISiteSecurityService> sss =
-            do_GetService(NS_SSSERVICE_CONTRACTID, &rv);
-          NS_ENSURE_SUCCESS(rv, rv);
-          rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, aURI,
-                                flags, mOriginAttributes, nullptr, nullptr,
-                                &isStsHost);
-          NS_ENSURE_SUCCESS(rv, rv);
-          rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HPKP, aURI,
-                                flags, mOriginAttributes, nullptr, nullptr,
-                                &isPinnedHost);
-          NS_ENSURE_SUCCESS(rv, rv);
-        } else {
-          mozilla::dom::ContentChild* cc =
-            mozilla::dom::ContentChild::GetSingleton();
-          mozilla::ipc::URIParams uri;
-          SerializeURI(aURI, uri);
-          cc->SendIsSecureURI(nsISiteSecurityService::HEADER_HSTS, uri, flags,
-                              mOriginAttributes, &isStsHost);
-          cc->SendIsSecureURI(nsISiteSecurityService::HEADER_HPKP, uri, flags,
-                              mOriginAttributes, &isPinnedHost);
-        }
-
-        if (Preferences::GetBool(
-              "browser.xul.error_pages.expert_bad_cert", false)) {
-          cssClass.AssignLiteral("expertBadCert");
-        }
-
-        // HSTS/pinning takes precedence over the expert bad cert pref. We
-        // never want to show the "Add Exception" button for these sites.
-        // In the future we should differentiate between an HSTS host and a
-        // pinned host and display a more informative message to the user.
-        if (isStsHost || isPinnedHost) {
-          cssClass.AssignLiteral("badStsCert");
-        }
-
-        uint32_t bucketId;
-        if (isStsHost) {
-          // measuring STS separately allows us to measure click through
-          // rates easily
-          bucketId = nsISecurityUITelemetry::WARNING_BAD_CERT_TOP_STS;
-        } else {
-          bucketId = nsISecurityUITelemetry::WARNING_BAD_CERT_TOP;
-        }
-
-        // See if an alternate cert error page is registered
-        nsAutoCString alternateErrorPage;
-        nsresult rv =
-          Preferences::GetCString("security.alternate_certificate_error_page",
-                                  alternateErrorPage);
-        if (NS_SUCCEEDED(rv)) {
-          errorPage.Assign(alternateErrorPage);
-        }
-
-        if (!IsFrame() && errorPage.EqualsIgnoreCase("certerror")) {
-          Telemetry::Accumulate(mozilla::Telemetry::SECURITY_UI, bucketId);
-        }
-
+    // We don't have a message string here anymore but DisplayLoadError
+    // requires a non-empty messageStr.
+    messageStr.Truncate();
+    messageStr.AssignLiteral(u" ");
+    if (errorClass == nsINSSErrorsService::ERROR_CLASS_BAD_CERT) {
+      error = "nssBadCert";
+
+      // If this is an HTTP Strict Transport Security host or a pinned host
+      // and the certificate is bad, don't allow overrides (RFC 6797 section
+      // 12.1, HPKP draft spec section 2.6).
+      uint32_t flags =
+        UsePrivateBrowsing() ? nsISocketProvider::NO_PERMANENT_STORAGE : 0;
+      bool isStsHost = false;
+      bool isPinnedHost = false;
+      if (XRE_IsParentProcess()) {
+        nsCOMPtr<nsISiteSecurityService> sss =
+          do_GetService(NS_SSSERVICE_CONTRACTID, &rv);
+        NS_ENSURE_SUCCESS(rv, rv);
+        rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, aURI,
+                              flags, mOriginAttributes, nullptr, nullptr,
+                              &isStsHost);
+        NS_ENSURE_SUCCESS(rv, rv);
+        rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HPKP, aURI,
+                              flags, mOriginAttributes, nullptr, nullptr,
+                              &isPinnedHost);
+        NS_ENSURE_SUCCESS(rv, rv);
       } else {
-        error = "nssFailure2";
-      }
+        mozilla::dom::ContentChild* cc =
+          mozilla::dom::ContentChild::GetSingleton();
+        mozilla::ipc::URIParams uri;
+        SerializeURI(aURI, uri);
+        cc->SendIsSecureURI(nsISiteSecurityService::HEADER_HSTS, uri, flags,
+                            mOriginAttributes, &isStsHost);
+        cc->SendIsSecureURI(nsISiteSecurityService::HEADER_HPKP, uri, flags,
+                            mOriginAttributes, &isPinnedHost);
+      }
+
+      if (Preferences::GetBool(
+            "browser.xul.error_pages.expert_bad_cert", false)) {
+        cssClass.AssignLiteral("expertBadCert");
+      }
+
+      // HSTS/pinning takes precedence over the expert bad cert pref. We
+      // never want to show the "Add Exception" button for these sites.
+      // In the future we should differentiate between an HSTS host and a
+      // pinned host and display a more informative message to the user.
+      if (isStsHost || isPinnedHost) {
+        cssClass.AssignLiteral("badStsCert");
+      }
+
+      uint32_t bucketId;
+      if (isStsHost) {
+        // measuring STS separately allows us to measure click through
+        // rates easily
+        bucketId = nsISecurityUITelemetry::WARNING_BAD_CERT_TOP_STS;
+      } else {
+        bucketId = nsISecurityUITelemetry::WARNING_BAD_CERT_TOP;
+      }
+
+      // See if an alternate cert error page is registered
+      nsAutoCString alternateErrorPage;
+      nsresult rv =
+        Preferences::GetCString("security.alternate_certificate_error_page",
+                                alternateErrorPage);
+      if (NS_SUCCEEDED(rv)) {
+        errorPage.Assign(alternateErrorPage);
+      }
+
+      if (!IsFrame() && errorPage.EqualsIgnoreCase("certerror")) {
+        Telemetry::Accumulate(mozilla::Telemetry::SECURITY_UI, bucketId);
+      }
+    } else {
+      error = "nssFailure2";
     }
   } else if (NS_ERROR_PHISHING_URI == aError ||
              NS_ERROR_MALWARE_URI == aError ||
              NS_ERROR_UNWANTED_URI == aError ||
              NS_ERROR_HARMFUL_URI == aError) {
     nsAutoCString host;
     aURI->GetHost(host);
     CopyUTF8toUTF16(host, formatStrs[0]);
--- a/dom/base/nsTextFragment.cpp
+++ b/dom/base/nsTextFragment.cpp
@@ -357,16 +357,20 @@ nsTextFragment::CopyTo(char16_t *aDest, 
     }
   }
 }
 
 bool
 nsTextFragment::Append(const char16_t* aBuffer, uint32_t aLength,
                        bool aUpdateBidi, bool aForce2b)
 {
+  if (!aLength) {
+    return true;
+  }
+
   // This is a common case because some callsites create a textnode
   // with a value by creating the node and then calling AppendData.
   if (mState.mLength == 0) {
     return SetTo(aBuffer, aLength, aUpdateBidi, aForce2b);
   }
 
   // Should we optimize for aData.Length() == 0?
 
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -1211,23 +1211,24 @@ InitIds(JSContext* cx, const NativePrope
 }
 
 #undef INIT_IDS_IF_DEFINED
 
 bool
 QueryInterface(JSContext* cx, unsigned argc, JS::Value* vp)
 {
   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
-  JS::Rooted<JS::Value> thisv(cx, JS_THIS(cx, vp));
-  if (thisv.isNull())
+  if (!args.thisv().isObject()) {
+    JS_ReportErrorASCII(cx, "QueryInterface called on incompatible non-object");
     return false;
+  }
 
   // Get the object. It might be a security wrapper, in which case we do a checked
   // unwrap.
-  JS::Rooted<JSObject*> origObj(cx, &thisv.toObject());
+  JS::Rooted<JSObject*> origObj(cx, &args.thisv().toObject());
   JS::Rooted<JSObject*> obj(cx, js::CheckedUnwrap(origObj,
                                                   /* stopAtWindowProxy = */ false));
   if (!obj) {
       JS_ReportErrorASCII(cx, "Permission denied to access object");
       return false;
   }
 
   // Switch this to UnwrapDOMObjectToISupports once our global objects are
@@ -1264,17 +1265,17 @@ QueryInterface(JSContext* cx, unsigned a
   }
 
   nsCOMPtr<nsISupports> unused;
   nsresult rv = native->QueryInterface(*iid->GetID(), getter_AddRefs(unused));
   if (NS_FAILED(rv)) {
     return Throw(cx, rv);
   }
 
-  *vp = thisv;
+  args.rval().set(args.thisv());
   return true;
 }
 
 void
 GetInterfaceImpl(JSContext* aCx, nsIInterfaceRequestor* aRequestor,
                  nsWrapperCache* aCache, nsIJSID* aIID,
                  JS::MutableHandle<JS::Value> aRetval, ErrorResult& aError)
 {
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -1826,17 +1826,17 @@ private:
   RefPtr<MediaManager> mManager; // get ref to this when creating the runnable
 };
 
 #if defined(ANDROID)
 class GetUserMediaRunnableWrapper : public Runnable
 {
 public:
   // This object must take ownership of task
-  GetUserMediaRunnableWrapper(GetUserMediaTask* task)
+  explicit GetUserMediaRunnableWrapper(GetUserMediaTask* task)
     : Runnable("GetUserMediaRunnableWrapper")
     , mTask(task) {
   }
 
   ~GetUserMediaRunnableWrapper() {
   }
 
   NS_IMETHOD Run() override {
--- a/dom/media/eme/mediadrm/MediaDrmProxySupport.cpp
+++ b/dom/media/eme/mediadrm/MediaDrmProxySupport.cpp
@@ -22,17 +22,17 @@ LogModule* GetMDRMNLog() {
 class MediaDrmJavaCallbacksSupport
   : public MediaDrmProxy::NativeMediaDrmProxyCallbacks::Natives<MediaDrmJavaCallbacksSupport>
 {
 public:
   typedef MediaDrmProxy::NativeMediaDrmProxyCallbacks::Natives<MediaDrmJavaCallbacksSupport> MediaDrmProxyNativeCallbacks;
   using MediaDrmProxyNativeCallbacks::DisposeNative;
   using MediaDrmProxyNativeCallbacks::AttachNative;
 
-  MediaDrmJavaCallbacksSupport(DecryptorProxyCallback* aDecryptorProxyCallback)
+  explicit MediaDrmJavaCallbacksSupport(DecryptorProxyCallback* aDecryptorProxyCallback)
     : mDecryptorProxyCallback(aDecryptorProxyCallback)
   {
     MOZ_ASSERT(aDecryptorProxyCallback);
   }
   /*
    * Native implementation, called by Java.
    */
   void OnSessionCreated(int aCreateSessionToken,
--- a/dom/media/eme/mediadrm/MediaDrmProxySupport.h
+++ b/dom/media/eme/mediadrm/MediaDrmProxySupport.h
@@ -26,17 +26,17 @@ enum MediaDrmSessionType {
   #define MDRMN_LOG(x, ...) MOZ_LOG(GetMDRMNLog(), mozilla::LogLevel::Debug,\
     ("[MediaDrmProxySupport][%s]" x, __FUNCTION__, ##__VA_ARGS__))
 #endif
 
 class MediaDrmProxySupport final
 {
 public:
 
-  MediaDrmProxySupport(const nsAString& aKeySystem);
+  explicit MediaDrmProxySupport(const nsAString& aKeySystem);
   ~MediaDrmProxySupport();
 
   /*
   * APIs to act as GMPDecryptorAPI, discarding unnecessary calls.
   */
   nsresult Init(DecryptorProxyCallback* aCallback);
 
   void CreateSession(uint32_t aCreateSessionToken,
--- a/dom/media/hls/HLSDecoder.cpp
+++ b/dom/media/hls/HLSDecoder.cpp
@@ -29,17 +29,17 @@ class HLSResourceCallbacksSupport
   : public GeckoHLSResourceWrapper::Callbacks::Natives<HLSResourceCallbacksSupport>
 {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(HLSResourceCallbacksSupport)
 public:
   typedef GeckoHLSResourceWrapper::Callbacks::Natives<HLSResourceCallbacksSupport> NativeCallbacks;
   using NativeCallbacks::DisposeNative;
   using NativeCallbacks::AttachNative;
 
-  HLSResourceCallbacksSupport(HLSDecoder* aResource);
+  explicit HLSResourceCallbacksSupport(HLSDecoder* aResource);
   void Detach();
   void OnDataArrived();
   void OnError(int aErrorCode);
 
 private:
   ~HLSResourceCallbacksSupport() {}
   Mutex mMutex;
   HLSDecoder* mDecoder;
--- a/dom/media/hls/HLSDemuxer.cpp
+++ b/dom/media/hls/HLSDemuxer.cpp
@@ -68,17 +68,17 @@ class HLSDemuxer::HLSDemuxerCallbacksSup
  : public GeckoHLSDemuxerWrapper::Callbacks::Natives<HLSDemuxerCallbacksSupport>
 {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(HLSDemuxerCallbacksSupport)
 public:
   typedef GeckoHLSDemuxerWrapper::Callbacks::Natives<HLSDemuxerCallbacksSupport> NativeCallbacks;
   using NativeCallbacks::DisposeNative;
   using NativeCallbacks::AttachNative;
 
-  HLSDemuxerCallbacksSupport(HLSDemuxer* aDemuxer)
+  explicit HLSDemuxerCallbacksSupport(HLSDemuxer* aDemuxer)
     : mMutex("HLSDemuxerCallbacksSupport")
     , mDemuxer(aDemuxer)
   {
     MOZ_ASSERT(mDemuxer);
   }
 
   void OnInitialized(bool aHasAudio, bool aHasVideo)
   {
--- a/dom/media/platforms/android/AndroidDecoderModule.h
+++ b/dom/media/platforms/android/AndroidDecoderModule.h
@@ -14,17 +14,17 @@ class AndroidDecoderModule : public Plat
 {
 public:
   already_AddRefed<MediaDataDecoder>
   CreateVideoDecoder(const CreateDecoderParams& aParams) override;
 
   already_AddRefed<MediaDataDecoder>
   CreateAudioDecoder(const CreateDecoderParams& aParams) override;
 
-  AndroidDecoderModule(CDMProxy* aProxy = nullptr);
+  explicit AndroidDecoderModule(CDMProxy* aProxy = nullptr);
 
   bool SupportsMimeType(const nsACString& aMimeType,
                         DecoderDoctorDiagnostics* aDiagnostics) const override;
 
 private:
   virtual ~AndroidDecoderModule() { }
   RefPtr<MediaDrmCDMProxy> mProxy;
 };
--- a/dom/media/platforms/android/RemoteDataDecoder.cpp
+++ b/dom/media/platforms/android/RemoteDataDecoder.cpp
@@ -88,17 +88,17 @@ public:
     int64_t mDurationUs;
     gfx::IntSize mImageSize;
     gfx::IntSize mDisplaySize;
   };
 
   class CallbacksSupport final : public JavaCallbacksSupport
   {
   public:
-    CallbacksSupport(RemoteVideoDecoder* aDecoder) : mDecoder(aDecoder) { }
+    explicit CallbacksSupport(RemoteVideoDecoder* aDecoder) : mDecoder(aDecoder) { }
 
     void HandleInput(int64_t aTimestamp, bool aProcessed) override
     {
       mDecoder->UpdateInputStatus(aTimestamp, aProcessed);
     }
 
     void HandleOutput(Sample::Param aSample) override
     {
@@ -311,17 +311,17 @@ public:
   {
     return ConversionRequired::kNeedAnnexB;
   }
 
 private:
   class CallbacksSupport final : public JavaCallbacksSupport
   {
   public:
-    CallbacksSupport(RemoteAudioDecoder* aDecoder) : mDecoder(aDecoder) { }
+    explicit CallbacksSupport(RemoteAudioDecoder* aDecoder) : mDecoder(aDecoder) { }
 
     void HandleInput(int64_t aTimestamp, bool aProcessed) override
     {
       mDecoder->UpdateInputStatus(aTimestamp, aProcessed);
     }
 
     void HandleOutput(Sample::Param aSample) override
     {
--- a/dom/plugins/base/nsJSNPRuntime.cpp
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -1595,20 +1595,21 @@ CallNPMethodInternal(JSContext *cx, JS::
 
   return ReportExceptionIfPending(cx);
 }
 
 static bool
 CallNPMethod(JSContext *cx, unsigned argc, JS::Value *vp)
 {
   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
-  JS::Rooted<JSObject*> obj(cx, JS_THIS_OBJECT(cx, vp));
-  if (!obj)
-      return false;
-
+  if (!args.thisv().isObject()) {
+    ThrowJSExceptionASCII(cx, "plug-in method called on incompatible non-object");
+    return false;
+  }
+  JS::Rooted<JSObject*> obj(cx, &args.thisv().toObject());
   return CallNPMethodInternal(cx, obj, args.length(), args.array(), vp, false);
 }
 
 bool
 NPObjWrapperProxyHandler::getOwnPropertyDescriptor(JSContext* cx, JS::Handle<JSObject*> proxy,
                                                    JS::Handle<jsid> id,
                                                    JS::MutableHandle<JS::PropertyDescriptor> desc) const
 {
--- a/dom/svg/SVGClipPathElement.cpp
+++ b/dom/svg/SVGClipPathElement.cpp
@@ -48,15 +48,22 @@ SVGClipPathElement::ClipPathUnits()
 
 nsSVGElement::EnumAttributesInfo
 SVGClipPathElement::GetEnumInfo()
 {
   return EnumAttributesInfo(mEnumAttributes, sEnumInfo,
                             ArrayLength(sEnumInfo));
 }
 
+bool
+SVGClipPathElement::IsUnitsObjectBoundingBox() const
+{
+  return mEnumAttributes[CLIPPATHUNITS].GetAnimValue() == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX;
+}
+
+
 //----------------------------------------------------------------------
 // nsIDOMNode methods
 
 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGClipPathElement)
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/svg/SVGClipPathElement.h
+++ b/dom/svg/SVGClipPathElement.h
@@ -32,16 +32,20 @@ protected:
 
 public:
   virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult,
                          bool aPreallocateChildren) const override;
 
   // WebIDL
   already_AddRefed<SVGAnimatedEnumeration> ClipPathUnits();
 
+  // This is an internal method that does not flush style, and thus
+  // the answer may be out of date if there's a pending style flush.
+  bool IsUnitsObjectBoundingBox() const;
+
 protected:
 
   enum { CLIPPATHUNITS };
   nsSVGEnum mEnumAttributes[1];
   static EnumInfo sEnumInfo[1];
 
   virtual EnumAttributesInfo GetEnumInfo() override;
 };
--- a/gfx/gl/AndroidNativeWindow.h
+++ b/gfx/gl/AndroidNativeWindow.h
@@ -17,22 +17,22 @@
 namespace mozilla {
 namespace gl {
 
 class AndroidNativeWindow {
 public:
   AndroidNativeWindow() : mNativeWindow(nullptr) {
   }
 
-  AndroidNativeWindow(java::sdk::Surface::Param aSurface) {
+  explicit AndroidNativeWindow(java::sdk::Surface::Param aSurface) {
     mNativeWindow = ANativeWindow_fromSurface(jni::GetEnvForThread(),
                                               aSurface.Get());
   }
 
-  AndroidNativeWindow(java::GeckoSurface::Param aSurface) {
+  explicit AndroidNativeWindow(java::GeckoSurface::Param aSurface) {
     auto surf = java::sdk::Surface::LocalRef(java::sdk::Surface::Ref::From(aSurface));
     mNativeWindow = ANativeWindow_fromSurface(jni::GetEnvForThread(),
                                               surf.Get());
   }
 
   ~AndroidNativeWindow() {
     if (mNativeWindow) {
       ANativeWindow_release(mNativeWindow);
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -197,28 +197,30 @@ class GLContext
 public:
     MOZ_DECLARE_WEAKREFERENCE_TYPENAME(GLContext)
     static MOZ_THREAD_LOCAL(uintptr_t) sCurrentContext;
 
     bool mImplicitMakeCurrent;
     bool mUseTLSIsCurrent;
 
     class TlsScope final {
-        GLContext* const mGL;
+        const WeakPtr<GLContext> mGL;
         const bool mWasTlsOk;
     public:
         explicit TlsScope(GLContext* const gl)
             : mGL(gl)
             , mWasTlsOk(gl->mUseTLSIsCurrent)
         {
             mGL->mUseTLSIsCurrent = true;
         }
 
         ~TlsScope() {
-            mGL->mUseTLSIsCurrent = mWasTlsOk;
+            if (mGL) {
+                mGL->mUseTLSIsCurrent = mWasTlsOk;
+            }
         }
     };
 
 // -----------------------------------------------------------------------------
 // basic getters
 public:
 
     /**
--- a/gfx/skia/skia/include/private/SkTDArray.h
+++ b/gfx/skia/skia/include/private/SkTDArray.h
@@ -17,17 +17,17 @@ template <typename T> class SkTDArray {
 public:
     SkTDArray() : fArray(nullptr), fReserve(0), fCount(0) {}
     SkTDArray(const T src[], int count) {
         SkASSERT(src || count == 0);
 
         fReserve = fCount = 0;
         fArray = nullptr;
         if (count) {
-            fArray = (T*)sk_malloc_throw(count * sizeof(T));
+            fArray = (T*)sk_malloc_throw(count, sizeof(T));
             memcpy(fArray, src, sizeof(T) * count);
             fReserve = fCount = count;
         }
     }
     SkTDArray(const SkTDArray<T>& src) : fArray(nullptr), fReserve(0), fCount(0) {
         SkTDArray<T> tmp(src.fArray, src.fCount);
         this->swap(tmp);
     }
@@ -348,41 +348,43 @@ public:
         SkASSERT((fReserve == 0 && fArray == nullptr) ||
                  (fReserve > 0 && fArray != nullptr));
         SkASSERT(fCount <= fReserve);
     }
 #endif
 
     void shrinkToFit() {
         fReserve = fCount;
-        fArray = (T*)sk_realloc_throw(fArray, fReserve * sizeof(T));
+        fArray = (T*)sk_realloc_throw(fArray, fReserve, sizeof(T));
     }
 
 private:
     T*      fArray;
     int     fReserve;
     int     fCount;
 
     /**
      *  Adjusts the number of elements in the array.
      *  This is the same as calling setCount(count() + delta).
      */
     void adjustCount(int delta) {
+        SkASSERT_RELEASE(fCount <= std::numeric_limits<int>::max() - delta);
         this->setCount(fCount + delta);
     }
 
     /**
      *  Increase the storage allocation such that it can hold (fCount + extra)
      *  elements.
      *  It never shrinks the allocation, and it may increase the allocation by
      *  more than is strictly required, based on a private growth heuristic.
      *
      *  note: does NOT modify fCount
      */
     void resizeStorageToAtLeast(int count) {
         SkASSERT(count > fReserve);
+        SkASSERT_RELEASE(count <= std::numeric_limits<int>::max() - std::numeric_limits<int>::max() / 5 - 4);
         fReserve = count + 4;
         fReserve += fReserve / 4;
-        fArray = (T*)sk_realloc_throw(fArray, fReserve * sizeof(T));
+        fArray = (T*)sk_realloc_throw(fArray, fReserve, sizeof(T));
     }
 };
 
 #endif
--- a/gfx/thebes/gfxFT2FontList.cpp
+++ b/gfx/thebes/gfxFT2FontList.cpp
@@ -73,17 +73,17 @@ BuildKeyNameFromFontName(nsAString &aNam
 // creating a temporary face if the entry does not have one yet.
 // This allows us to read font names, tables, etc if necessary
 // without permanently instantiating a freetype face and consuming
 // memory long-term.
 // This may fail (resulting in a null FT_Face), e.g. if it fails to
 // allocate memory to uncompress a font from omnijar.
 class AutoFTFace {
 public:
-    AutoFTFace(FT2FontEntry* aFontEntry)
+    explicit AutoFTFace(FT2FontEntry* aFontEntry)
         : mFace(nullptr), mFontDataBuf(nullptr), mOwnsFace(false)
     {
         if (aFontEntry->mFTFace) {
             mFace = aFontEntry->mFTFace;
             return;
         }
 
         NS_ASSERTION(!aFontEntry->mFilename.IsEmpty(),
--- a/gfx/thebes/gfxFT2FontList.h
+++ b/gfx/thebes/gfxFT2FontList.h
@@ -19,17 +19,17 @@ using mozilla::dom::FontListEntry;
 
 class FontNameCache;
 typedef struct FT_FaceRec_* FT_Face;
 class nsZipArchive;
 
 class FT2FontEntry : public gfxFontEntry
 {
 public:
-    FT2FontEntry(const nsAString& aFaceName) :
+    explicit FT2FontEntry(const nsAString& aFaceName) :
         gfxFontEntry(aFaceName),
         mFTFace(nullptr),
         mFontFace(nullptr),
         mFTFontIndex(0)
     {
     }
 
     ~FT2FontEntry();
@@ -103,17 +103,17 @@ public:
     uint8_t   mFTFontIndex;
 
     mozilla::ThreadSafeWeakPtr<mozilla::gfx::UnscaledFontFreeType> mUnscaledFont;
 };
 
 class FT2FontFamily : public gfxFontFamily
 {
 public:
-    FT2FontFamily(const nsAString& aName) :
+    explicit FT2FontFamily(const nsAString& aName) :
         gfxFontFamily(aName) { }
 
     // Append this family's faces to the IPC fontlist
     void AddFacesToFontList(InfallibleTArray<FontListEntry>* aFontList);
 };
 
 class gfxFT2FontList : public gfxPlatformFontList
 {
--- a/gfx/thebes/gfxFT2Fonts.h
+++ b/gfx/thebes/gfxFT2Fonts.h
@@ -36,17 +36,17 @@ public: // new functions
     virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                                         FontCacheSizes* aSizes) const override;
 
 protected:
     struct CachedGlyphData {
         CachedGlyphData()
             : glyphIndex(0xffffffffU) { }
 
-        CachedGlyphData(uint32_t gid)
+        explicit CachedGlyphData(uint32_t gid)
             : glyphIndex(gid) { }
 
         uint32_t glyphIndex;
         int32_t lsbDelta;
         int32_t rsbDelta;
         int32_t xAdvance;
     };
 
--- a/image/AnimationFrameBuffer.cpp
+++ b/image/AnimationFrameBuffer.cpp
@@ -72,39 +72,44 @@ AnimationFrameBuffer::Insert(RawAccessFr
     // (probably an empty frame) with the new frame.
     MOZ_ASSERT(MayDiscard());
     MOZ_ASSERT(mInsertIndex < mFrames.Length());
 
     if (mInsertIndex > 0) {
       MOZ_ASSERT(!mFrames[mInsertIndex]);
       mFrames[mInsertIndex] = Move(aFrame);
     }
-  } else {
-    if (mInsertIndex == mFrames.Length()) {
-      // We are still on the first pass of the animation decoding, so this is
-      // the first time we have seen this frame.
-      mFrames.AppendElement(Move(aFrame));
-    } else if (mInsertIndex > 0) {
-      // We were forced to restart an animation before we decoded the last
-      // frame. Thus we might need to insert, even on a "first pass."
-      MOZ_ASSERT(mInsertIndex < mFrames.Length());
-      MOZ_ASSERT(!mFrames[mInsertIndex]);
-      mFrames[mInsertIndex] = Move(aFrame);
-    }
+  } else if (mInsertIndex == mFrames.Length()) {
+    // We are still on the first pass of the animation decoding, so this is
+    // the first time we have seen this frame.
+    mFrames.AppendElement(Move(aFrame));
 
     if (mInsertIndex == mThreshold) {
-      // We just tripped over the threshold, and on the first pass of the
-      // decoding; this is our chance to do any clearing of already displayed
-      // frames. After this, we only need to release as we advance.
+      // We just tripped over the threshold for the first time. This is our
+      // chance to do any clearing of already displayed frames. After this,
+      // we only need to release as we advance or force a restart.
       MOZ_ASSERT(MayDiscard());
       MOZ_ASSERT(mGetIndex < mInsertIndex);
       for (size_t i = 1; i < mGetIndex; ++i) {
         RawAccessFrameRef discard = Move(mFrames[i]);
       }
     }
+  } else if (mInsertIndex > 0) {
+    // We were forced to restart an animation before we decoded the last
+    // frame. If we were discarding frames, then we tossed what we had
+    // except for the first frame.
+    MOZ_ASSERT(mInsertIndex < mFrames.Length());
+    MOZ_ASSERT(!mFrames[mInsertIndex]);
+    MOZ_ASSERT(MayDiscard());
+    mFrames[mInsertIndex] = Move(aFrame);
+  } else { // mInsertIndex == 0
+    // We were forced to restart an animation before we decoded the last
+    // frame. We don't need the redecoded first frame because we always keep
+    // the original.
+    MOZ_ASSERT(MayDiscard());
   }
 
   MOZ_ASSERT(mFrames[mInsertIndex]);
   ++mInsertIndex;
 
   // Ensure we only request more decoded frames if we actually need them. If we
   // need to advance to a certain point in the animation on behalf of the owner,
   // then do so. This ensures we keep decoding. If the batch size is really
--- a/image/AnimationSurfaceProvider.cpp
+++ b/image/AnimationSurfaceProvider.cpp
@@ -289,17 +289,20 @@ AnimationSurfaceProvider::CheckForNewFra
       justGotFirstFrame = true;
     }
   }
 
   if (justGotFirstFrame) {
     AnnounceSurfaceAvailable();
   }
 
-  return continueDecoding;
+  // If we are shutting down, we want to ensure we release the thread as soon
+  // as possible. The animation may advance even during shutdown, which keeps
+  // us decoding, and thus blocking the decode pool during teardown.
+  return continueDecoding && !DecodePool::Singleton()->IsShuttingDown();
 }
 
 bool
 AnimationSurfaceProvider::CheckForNewFrameAtTerminalState()
 {
   mDecodingMutex.AssertCurrentThreadOwns();
   MOZ_ASSERT(mDecoder);
 
@@ -339,17 +342,20 @@ AnimationSurfaceProvider::CheckForNewFra
       justGotFirstFrame = true;
     }
   }
 
   if (justGotFirstFrame) {
     AnnounceSurfaceAvailable();
   }
 
-  return continueDecoding;
+  // If we are shutting down, we want to ensure we release the thread as soon
+  // as possible. The animation may advance even during shutdown, which keeps
+  // us decoding, and thus blocking the decode pool during teardown.
+  return continueDecoding && !DecodePool::Singleton()->IsShuttingDown();
 }
 
 void
 AnimationSurfaceProvider::AnnounceSurfaceAvailable()
 {
   mFramesMutex.AssertNotCurrentThreadOwns();
   MOZ_ASSERT(mImage);
 
--- a/image/DecodePool.cpp
+++ b/image/DecodePool.cpp
@@ -112,16 +112,22 @@ public:
       mMonitor.NotifyAll();
     }
 
     for (uint32_t i = 0 ; i < threads.Length() ; ++i) {
       threads[i]->Shutdown();
     }
   }
 
+  bool IsShuttingDown() const
+  {
+    MonitorAutoLock lock(mMonitor);
+    return mShuttingDown;
+  }
+
   /// Pushes a new decode work item.
   void PushWork(IDecodingTask* aTask)
   {
     MOZ_ASSERT(aTask);
     RefPtr<IDecodingTask> task(aTask);
 
     MonitorAutoLock lock(mMonitor);
 
@@ -237,17 +243,17 @@ private:
     Work work;
     work.mType = Work::Type::SHUTDOWN;
     return work;
   }
 
   nsThreadPoolNaming mThreadNaming;
 
   // mMonitor guards everything below.
-  Monitor mMonitor;
+  mutable Monitor mMonitor;
   nsTArray<RefPtr<IDecodingTask>> mHighPriorityQueue;
   nsTArray<RefPtr<IDecodingTask>> mLowPriorityQueue;
   nsTArray<nsCOMPtr<nsIThread>> mThreads;
   PRIntervalTime mIdleTimeout;
   uint8_t mMaxIdleThreads;   // Maximum number of workers when idle.
   uint8_t mAvailableThreads; // How many new threads can be created.
   uint8_t mIdleThreads; // How many created threads are waiting.
   bool mShuttingDown;
@@ -427,16 +433,22 @@ DecodePool::Observe(nsISupports*, const 
 
   if (ioThread) {
     ioThread->Shutdown();
   }
 
   return NS_OK;
 }
 
+bool
+DecodePool::IsShuttingDown() const
+{
+  return mImpl->IsShuttingDown();
+}
+
 void
 DecodePool::AsyncRun(IDecodingTask* aTask)
 {
   MOZ_ASSERT(aTask);
   mImpl->PushWork(aTask);
 }
 
 bool
--- a/image/DecodePool.h
+++ b/image/DecodePool.h
@@ -50,16 +50,20 @@ public:
 
   /// Returns the singleton instance.
   static DecodePool* Singleton();
 
   /// @return the number of processor cores we have available. This is not the
   /// same as the number of decoding threads we're actually using.
   static uint32_t NumberOfCores();
 
+  /// True if the DecodePool is being shutdown. This may only be called by
+  /// threads from the pool to check if they should keep working or not.
+  bool IsShuttingDown() const;
+
   /// Ask the DecodePool to run @aTask asynchronously and return immediately.
   void AsyncRun(IDecodingTask* aTask);
 
   /**
    * Run @aTask synchronously if the task would prefer it. It's up to the task
    * itself to make this decision; @see IDecodingTask::ShouldPreferSyncRun(). If
    * @aTask doesn't prefer it, just run @aTask asynchronously and return
    * immediately.
--- a/image/Downscaler.cpp
+++ b/image/Downscaler.cpp
@@ -190,23 +190,24 @@ Downscaler::CommitRow()
     int32_t filterOffset = 0;
     int32_t filterLength = 0;
     mYFilter.GetFilterOffsetAndLength(mCurrentOutLine,
                                       &filterOffset, &filterLength);
 
     int32_t inLineToRead = filterOffset + mLinesInBuffer;
     MOZ_ASSERT(mCurrentInLine <= inLineToRead, "Reading past end of input");
     if (mCurrentInLine == inLineToRead) {
+      MOZ_RELEASE_ASSERT(mLinesInBuffer < mWindowCapacity, "Need more rows than capacity!");
       mXFilter.ConvolveHorizontally(mRowBuffer.get(), mWindow[mLinesInBuffer++], mHasAlpha);
     }
 
     MOZ_ASSERT(mCurrentOutLine < mTargetSize.height,
                "Writing past end of output");
 
-    while (mLinesInBuffer == filterLength) {
+    while (mLinesInBuffer >= filterLength) {
       DownscaleInputLine();
 
       if (mCurrentOutLine == mTargetSize.height) {
         break;  // We're done.
       }
 
       mYFilter.GetFilterOffsetAndLength(mCurrentOutLine,
                                         &filterOffset, &filterLength);
@@ -292,18 +293,23 @@ Downscaler::DownscaleInputLine()
   mYFilter.GetFilterOffsetAndLength(mCurrentOutLine,
                                     &newFilterOffset, &newFilterLength);
 
   int diff = newFilterOffset - filterOffset;
   MOZ_ASSERT(diff >= 0, "Moving backwards in the filter?");
 
   // Shift the buffer. We're just moving pointers here, so this is cheap.
   mLinesInBuffer -= diff;
-  mLinesInBuffer = max(mLinesInBuffer, 0);
-  for (int32_t i = 0; i < mLinesInBuffer; ++i) {
-    swap(mWindow[i], mWindow[filterLength - mLinesInBuffer + i]);
+  mLinesInBuffer = min(max(mLinesInBuffer, 0), mWindowCapacity);
+
+  // If we already have enough rows to satisfy the filter, there is no need
+  // to swap as we won't be writing more before the next convolution.
+  if (filterLength > mLinesInBuffer) {
+    for (int32_t i = 0; i < mLinesInBuffer; ++i) {
+      swap(mWindow[i], mWindow[filterLength - mLinesInBuffer + i]);
+    }
   }
 }
 
 
 
 } // namespace image
 } // namespace mozilla
--- a/image/DownscalingFilter.h
+++ b/image/DownscalingFilter.h
@@ -227,23 +227,24 @@ protected:
     int32_t filterOffset = 0;
     int32_t filterLength = 0;
     mYFilter.GetFilterOffsetAndLength(mOutputRow,
                                       &filterOffset, &filterLength);
 
     int32_t inputRowToRead = filterOffset + mRowsInWindow;
     MOZ_ASSERT(mInputRow <= inputRowToRead, "Reading past end of input");
     if (mInputRow == inputRowToRead) {
+      MOZ_RELEASE_ASSERT(mRowsInWindow < mWindowCapacity, "Need more rows than capacity!");
       mXFilter.ConvolveHorizontally(mRowBuffer.get(), mWindow[mRowsInWindow++], mHasAlpha);
     }
 
     MOZ_ASSERT(mOutputRow < mNext.InputSize().height,
                "Writing past end of output");
 
-    while (mRowsInWindow == filterLength) {
+    while (mRowsInWindow >= filterLength) {
       DownscaleInputRow();
 
       if (mOutputRow == mNext.InputSize().height) {
         break;  // We're done.
       }
 
       mYFilter.GetFilterOffsetAndLength(mOutputRow,
                                         &filterOffset, &filterLength);
@@ -292,19 +293,24 @@ private:
     mYFilter.GetFilterOffsetAndLength(mOutputRow,
                                       &newFilterOffset, &newFilterLength);
 
     int diff = newFilterOffset - filterOffset;
     MOZ_ASSERT(diff >= 0, "Moving backwards in the filter?");
 
     // Shift the buffer. We're just moving pointers here, so this is cheap.
     mRowsInWindow -= diff;
-    mRowsInWindow = std::max(mRowsInWindow, 0);
-    for (int32_t i = 0; i < mRowsInWindow; ++i) {
-      std::swap(mWindow[i], mWindow[filterLength - mRowsInWindow + i]);
+    mRowsInWindow = std::min(std::max(mRowsInWindow, 0), mWindowCapacity);
+
+    // If we already have enough rows to satisfy the filter, there is no need
+    // to swap as we won't be writing more before the next convolution.
+    if (filterLength > mRowsInWindow) {
+      for (int32_t i = 0; i < mRowsInWindow; ++i) {
+        std::swap(mWindow[i], mWindow[filterLength - mRowsInWindow + i]);
+      }
     }
   }
 
   void ReleaseWindow()
   {
     if (!mWindow) {
       return;
     }
--- a/ipc/glue/MessagePump.h
+++ b/ipc/glue/MessagePump.h
@@ -172,17 +172,17 @@ private:
  * Android UI thread it is necessary to have a non-looping MessagePump. This class enables
  * forwarding of nsIRunnables from MessageLoop::PostTask_Helper to the registered
  * nsIEventTarget with out the need to control the event loop. The only member function
  * that should be invoked is GetXPCOMThread. All other member functions will invoke MOZ_CRASH
 */
 class MessagePumpForAndroidUI : public base::MessagePump {
 
 public:
-  MessagePumpForAndroidUI(nsIEventTarget* aEventTarget)
+  explicit MessagePumpForAndroidUI(nsIEventTarget* aEventTarget)
     : mEventTarget(aEventTarget)
   { }
 
   virtual void Run(Delegate* delegate);
   virtual void Quit();
   virtual void ScheduleWork();
   virtual void ScheduleDelayedWork(const base::TimeTicks& delayed_work_time);
   virtual nsIEventTarget* GetXPCOMThread()
--- a/ipc/glue/WindowsMessageLoop.cpp
+++ b/ipc/glue/WindowsMessageLoop.cpp
@@ -672,39 +672,50 @@ TimeoutHasExpired(const TimeoutData& aDa
 }
 
 } // namespace
 
 namespace mozilla {
 namespace ipc {
 namespace windows {
 
+static bool
+ProcessTypeRequiresWinEventHook()
+{
+  switch (XRE_GetProcessType()) {
+    case GeckoProcessType_GMPlugin:
+      return false;
+    default:
+      return true;
+  }
+}
+
 void
 InitUIThread()
 {
   // If we aren't setup before a call to NotifyWorkerThread, we'll hang
   // on startup.
   if (!gUIThreadId) {
     gUIThreadId = GetCurrentThreadId();
   }
 
   MOZ_ASSERT(gUIThreadId);
   MOZ_ASSERT(gUIThreadId == GetCurrentThreadId(),
              "Called InitUIThread multiple times on different threads!");
 
-  if (!gWinEventHook) {
+  if (!gWinEventHook && ProcessTypeRequiresWinEventHook()) {
     gWinEventHook = SetWinEventHook(EVENT_OBJECT_CREATE, EVENT_OBJECT_DESTROY,
                                     NULL, &WinEventHook, GetCurrentProcessId(),
                                     gUIThreadId, WINEVENT_OUTOFCONTEXT);
+    MOZ_ASSERT(gWinEventHook);
 
     // We need to execute this after setting the hook in case the OLE window
     // already existed.
     gCOMWindow = FindCOMWindow();
   }
-  MOZ_ASSERT(gWinEventHook);
 }
 
 } // namespace windows
 } // namespace ipc
 } // namespace mozilla
 
 // See SpinInternalEventLoop below
 MessageChannel::SyncStackFrame::SyncStackFrame(MessageChannel* channel, bool interrupt)
--- a/ipc/testshell/XPCShellEnvironment.cpp
+++ b/ipc/testshell/XPCShellEnvironment.cpp
@@ -127,21 +127,18 @@ Dump(JSContext *cx, unsigned argc, JS::V
 
 static bool
 Load(JSContext *cx,
      unsigned argc,
      JS::Value *vp)
 {
     JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
 
-    JS::Rooted<JSObject*> obj(cx, JS_THIS_OBJECT(cx, vp));
-    if (!obj)
-        return false;
-
-    if (!JS_IsGlobalObject(obj)) {
+    JS::RootedValue thisv(cx, args.computeThis(cx));
+    if (!thisv.isObject() || !JS_IsGlobalObject(&thisv.toObject())) {
         JS_ReportErrorASCII(cx, "Trying to load() into a non-global object");
         return false;
     }
 
     for (unsigned i = 0; i < args.length(); i++) {
         JS::Rooted<JSString*> str(cx, JS::ToString(cx, args[i]));
         if (!str)
             return false;
--- a/js/public/CallArgs.h
+++ b/js/public/CallArgs.h
@@ -340,54 +340,9 @@ CallArgsFromSp(unsigned stackSlots, Valu
                bool ignoresReturnValue = false)
 {
     return CallArgs::create(stackSlots - constructing, sp - stackSlots, constructing,
                             ignoresReturnValue);
 }
 
 } // namespace JS
 
-/*
- * Macros to hide interpreter stack layout details from a JSNative using its
- * JS::Value* vp parameter.  DO NOT USE THESE!  Instead use JS::CallArgs and
- * friends, above.  These macros will be removed when we change JSNative to
- * take a const JS::CallArgs&.
- */
-
-/*
- * Return |this| if |this| is an object.  Otherwise, return the global object
- * if |this| is null or undefined, and finally return a boxed version of any
- * other primitive.
- *
- * Note: if this method returns null, an error has occurred and must be
- * propagated or caught.
- */
-MOZ_ALWAYS_INLINE JS::Value
-JS_THIS(JSContext* cx, JS::Value* vp)
-{
-    return vp[1].isPrimitive() ? JS::detail::ComputeThis(cx, vp) : vp[1];
-}
-
-/*
- * A note on JS_THIS_OBJECT: no equivalent method is part of the CallArgs
- * interface, and we're unlikely to add one (functions shouldn't be implicitly
- * exposing the global object to arbitrary callers).  Continue using |vp|
- * directly for this case, but be aware this API will eventually be replaced
- * with a function that operates directly upon |args.thisv()|.
- */
-#define JS_THIS_OBJECT(cx,vp)   (JS_THIS(cx,vp).toObjectOrNull())
-
-/*
- * |this| is passed to functions in ES5 without change.  Functions themselves
- * do any post-processing they desire to box |this|, compute the global object,
- * &c.  This macro retrieves a function's unboxed |this| value.
- *
- * This macro must not be used in conjunction with JS_THIS or JS_THIS_OBJECT,
- * or vice versa.  Either use the provided this value with this macro, or
- * compute the boxed |this| value using those.  JS_THIS_VALUE must not be used
- * if the function is being called as a constructor.
- *
- * But: DO NOT USE THIS!  Instead use JS::CallArgs::thisv(), above.
- *
- */
-#define JS_THIS_VALUE(cx,vp)    ((vp)[1])
-
 #endif /* js_CallArgs_h */
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -869,18 +869,18 @@ class JS_PUBLIC_API(AutoGCRooter)
 
     ~AutoGCRooter() {
         MOZ_ASSERT(this == *stackTop);
         *stackTop = down;
     }
 
     /* Implemented in gc/RootMarking.cpp. */
     inline void trace(JSTracer* trc);
-    static void traceAll(const js::CooperatingContext& target, JSTracer* trc);
-    static void traceAllWrappers(const js::CooperatingContext& target, JSTracer* trc);
+    static void traceAll(JSContext* cx, JSTracer* trc);
+    static void traceAllWrappers(JSContext* cx, JSTracer* trc);
 
   protected:
     AutoGCRooter * const down;
 
     /*
      * Discriminates actual subclass of this being used.  If non-negative, the
      * subclass roots an array of values of the length stored in this field.
      * If negative, meaning is indicated by the corresponding value in the enum
--- a/js/src/builtin/intl/LangTagMappingsGenerated.js
+++ b/js/src/builtin/intl/LangTagMappingsGenerated.js
@@ -1,12 +1,12 @@
 // Generated by make_intl_data.py. DO NOT EDIT.
 
 // Mappings from complete tags to preferred values.
-// Derived from IANA Language Subtag Registry, file date 2018-01-25.
+// Derived from IANA Language Subtag Registry, file date 2018-03-20.
 // https://www.iana.org/assignments/language-subtag-registry
 var langTagMappings = {
     "art-lojban": "jbo",
     "cel-gaulish": "cel-gaulish",
     "en-gb-oed": "en-GB-oxendict",
     "i-ami": "ami",
     "i-bnn": "bnn",
     "i-default": "i-default",
@@ -54,17 +54,17 @@ var langTagMappings = {
     "zh-min": "zh-min",
     "zh-min-nan": "nan",
     "zh-wuu": "wuu",
     "zh-xiang": "hsn",
     "zh-yue": "yue",
 };
 
 // Mappings from language subtags to preferred values.
-// Derived from IANA Language Subtag Registry, file date 2018-01-25.
+// Derived from IANA Language Subtag Registry, file date 2018-03-20.
 // https://www.iana.org/assignments/language-subtag-registry
 var languageMappings = {
     "aam": "aas",
     "adp": "dz",
     "aue": "ktz",
     "ayx": "nun",
     "bgm": "bcg",
     "bjd": "drl",
@@ -102,16 +102,17 @@ var languageMappings = {
     "lii": "raq",
     "lmm": "rmx",
     "meg": "cir",
     "mo": "ro",
     "mst": "mry",
     "mwj": "vaj",
     "myt": "mry",
     "nad": "xny",
+    "ncp": "kdz",
     "nnx": "ngv",
     "nts": "pij",
     "oun": "vaj",
     "pcr": "adx",
     "pmc": "huw",
     "pmu": "phr",
     "ppa": "bfy",
     "ppr": "lcq",
@@ -137,32 +138,32 @@ var languageMappings = {
     "ybd": "rki",
     "yma": "lrr",
     "ymt": "mtm",
     "yos": "zom",
     "yuu": "yug",
 };
 
 // Mappings from region subtags to preferred values.
-// Derived from IANA Language Subtag Registry, file date 2018-01-25.
+// Derived from IANA Language Subtag Registry, file date 2018-03-20.
 // https://www.iana.org/assignments/language-subtag-registry
 var regionMappings = {
     "BU": "MM",
     "DD": "DE",
     "FX": "FR",
     "TP": "TL",
     "YD": "YE",
     "ZR": "CD",
 };
 
 // Mappings from extlang subtags to preferred values.
 // All current deprecated extlang subtags have the form `<prefix>-<extlang>`
 // and their preferred value is exactly equal to `<extlang>`. So each key in
 // extlangMappings acts both as the extlang subtag and its preferred value.
-// Derived from IANA Language Subtag Registry, file date 2018-01-25.
+// Derived from IANA Language Subtag Registry, file date 2018-03-20.
 // https://www.iana.org/assignments/language-subtag-registry
 var extlangMappings = {
     "aao": "ar",
     "abh": "ar",
     "abv": "ar",
     "acm": "ar",
     "acq": "ar",
     "acw": "ar",
@@ -278,24 +279,24 @@ var extlangMappings = {
     "kvk": "sgn",
     "kvr": "ms",
     "kxd": "ms",
     "lbs": "sgn",
     "lce": "ms",
     "lcf": "ms",
     "liw": "ms",
     "lls": "sgn",
-    "lsg": "sgn",
     "lsl": "sgn",
     "lso": "sgn",
     "lsp": "sgn",
     "lst": "sgn",
     "lsy": "sgn",
     "ltg": "lv",
     "lvs": "lv",
+    "lws": "sgn",
     "lzh": "zh",
     "max": "ms",
     "mdl": "sgn",
     "meo": "ms",
     "mfa": "ms",
     "mfb": "ms",
     "mfs": "sgn",
     "min": "ms",
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -272,17 +272,17 @@ namespace PointerType {
   bool ContentsGetter(JSContext* cx, const JS::CallArgs& args);
   bool ContentsSetter(JSContext* cx, const JS::CallArgs& args);
 
   static bool IsNull(JSContext* cx, unsigned argc, Value* vp);
   static bool Increment(JSContext* cx, unsigned argc, Value* vp);
   static bool Decrement(JSContext* cx, unsigned argc, Value* vp);
   // The following is not an instance function, since we don't want to expose arbitrary
   // pointer arithmetic at this moment.
-  static bool OffsetBy(JSContext* cx, const CallArgs& args, int offset);
+  static bool OffsetBy(JSContext* cx, const CallArgs& args, int offset, const char* name);
 } // namespace PointerType
 
 namespace ArrayType {
   bool IsArrayType(HandleValue v);
   bool IsArrayOrArrayType(HandleValue v);
 
   static bool Create(JSContext* cx, unsigned argc, Value* vp);
   static bool ConstructData(JSContext* cx, HandleObject obj, const CallArgs& args);
@@ -1980,16 +1980,27 @@ VariadicArgumentTypeError(JSContext* cx,
   char indexStr[16];
   SprintfLiteral(indexStr, "%u", index + 1);
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_VARG_TYPE_ERROR, indexStr, valStr);
   return false;
 }
 
+MOZ_MUST_USE JSObject*
+GetThisObject(JSContext* cx, const CallArgs& args, const char* msg)
+{
+  if (!args.thisv().isObject()) {
+    IncompatibleThisProto(cx, msg, args.thisv());
+    return nullptr;
+  }
+
+  return &args.thisv().toObject();
+}
+
 static JSObject*
 InitCTypeClass(JSContext* cx, HandleObject ctypesObj)
 {
   JSFunction* fun = JS_DefineFunction(cx, ctypesObj, "CType", ConstructAbstract, 0,
                                       CTYPESCTOR_FLAGS);
   if (!fun)
     return nullptr;
 
@@ -4926,17 +4937,17 @@ CType::PtrGetter(JSContext* cx, const JS
   args.rval().setObject(*pointerType);
   return true;
 }
 
 bool
 CType::CreateArray(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
-  RootedObject baseType(cx, JS_THIS_OBJECT(cx, vp));
+  RootedObject baseType(cx, GetThisObject(cx, args, "CType.prototype.array"));
   if (!baseType)
     return false;
   if (!CType::IsCType(baseType)) {
     return IncompatibleThisProto(cx, "CType.prototype.array", args.thisv());
   }
 
   // Construct and return a new ArrayType object.
   if (args.length() > 1) {
@@ -4957,17 +4968,17 @@ CType::CreateArray(JSContext* cx, unsign
   args.rval().setObject(*result);
   return true;
 }
 
 bool
 CType::ToString(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
-  RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
+  RootedObject obj(cx, GetThisObject(cx, args, "CType.prototype.toString"));
   if (!obj)
     return false;
   if (!CType::IsCType(obj) && !CType::IsCTypeProto(obj)) {
     return IncompatibleThisProto(cx, "CType.prototype.toString",
                                  InformalValueTypeName(args.thisv()));
   }
 
   // Create the appropriate string depending on whether we're sCTypeClass or
@@ -4988,17 +4999,17 @@ CType::ToString(JSContext* cx, unsigned 
   args.rval().setString(result);
   return true;
 }
 
 bool
 CType::ToSource(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
-  JSObject* obj = JS_THIS_OBJECT(cx, vp);
+  JSObject* obj = GetThisObject(cx, args, "CType.prototype.toSource");
   if (!obj)
     return false;
   if (!CType::IsCType(obj) && !CType::IsCTypeProto(obj)) {
     return IncompatibleThisProto(cx, "CType.prototype.toSource",
                                  InformalValueTypeName(args.thisv()));
   }
 
   // Create the appropriate string depending on whether we're sCTypeClass or
@@ -5076,17 +5087,17 @@ ABI::IsABI(JSObject* obj)
 bool
 ABI::ToSource(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   if (args.length() != 0) {
     return ArgumentLengthError(cx, "ABI.prototype.toSource", "no", "s");
   }
 
-  JSObject* obj = JS_THIS_OBJECT(cx, vp);
+  JSObject* obj = GetThisObject(cx, args, "ABI.prototype.toSource");
   if (!obj)
     return false;
   if (!ABI::IsABI(obj)) {
     return IncompatibleThisProto(cx, "ABI.prototype.toSource",
                                  InformalValueTypeName(args.thisv()));
   }
 
   JSString* result;
@@ -5295,17 +5306,17 @@ PointerType::TargetTypeGetter(JSContext*
   MOZ_ASSERT(args.rval().isObject());
   return true;
 }
 
 bool
 PointerType::IsNull(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
-  RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
+  RootedObject obj(cx, GetThisObject(cx, args, "PointerType.prototype.isNull"));
   if (!obj)
     return false;
   if (!CData::IsCDataMaybeUnwrap(&obj)) {
     return IncompatibleThisProto(cx, "PointerType.prototype.isNull",
                                  args.thisv());
   }
 
   // Get pointer type and base type.
@@ -5316,39 +5327,27 @@ PointerType::IsNull(JSContext* cx, unsig
   }
 
   void* data = *static_cast<void**>(CData::GetData(obj));
   args.rval().setBoolean(data == nullptr);
   return true;
 }
 
 bool
-PointerType::OffsetBy(JSContext* cx, const CallArgs& args, int offset)
-{
-  RootedObject obj(cx, JS_THIS_OBJECT(cx, args.base()));
+PointerType::OffsetBy(JSContext* cx, const CallArgs& args, int offset, const char* name)
+{
+  RootedObject obj(cx, GetThisObject(cx, args, name));
   if (!obj)
     return false;
-  if (!CData::IsCDataMaybeUnwrap(&obj)) {
-    if (offset == 1) {
-      return IncompatibleThisProto(cx, "PointerType.prototype.increment",
-                                   args.thisv());
-    }
-    return IncompatibleThisProto(cx, "PointerType.prototype.decrement",
-                                 args.thisv());
-  }
+  if (!CData::IsCDataMaybeUnwrap(&obj))
+    return IncompatibleThisProto(cx, name, args.thisv());
 
   RootedObject typeObj(cx, CData::GetCType(obj));
-  if (CType::GetTypeCode(typeObj) != TYPE_pointer) {
-    if (offset == 1) {
-      return IncompatibleThisType(cx, "PointerType.prototype.increment",
-                                  "non-PointerType CData", args.thisv());
-    }
-    return IncompatibleThisType(cx, "PointerType.prototype.decrement",
-                                "non-PointerType CData", args.thisv());
-  }
+  if (CType::GetTypeCode(typeObj) != TYPE_pointer)
+    return IncompatibleThisType(cx, name, "non-PointerType CData", args.thisv());
 
   RootedObject baseType(cx, PointerType::GetBaseType(typeObj));
   if (!CType::IsSizeDefined(baseType)) {
     return UndefinedSizePointerError(cx, "modify", obj);
   }
 
   size_t elementSize = CType::GetSize(baseType);
   char* data = static_cast<char*>(*static_cast<void**>(CData::GetData(obj)));
@@ -5362,24 +5361,24 @@ PointerType::OffsetBy(JSContext* cx, con
   args.rval().setObject(*result);
   return true;
 }
 
 bool
 PointerType::Increment(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
-  return OffsetBy(cx, args, 1);
+  return OffsetBy(cx, args, 1, "PointerType.prototype.increment");
 }
 
 bool
 PointerType::Decrement(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
-  return OffsetBy(cx, args, -1);
+  return OffsetBy(cx, args, -1, "PointerType.prototype.decrement");
 }
 
 bool
 PointerType::ContentsGetter(JSContext* cx, const JS::CallArgs& args)
 {
   RootedObject obj(cx, &args.thisv().toObject());
   RootedObject baseType(cx, GetBaseType(CData::GetCType(obj)));
   if (!CType::IsSizeDefined(baseType)) {
@@ -5854,17 +5853,17 @@ ArrayType::Setter(JSContext* cx, HandleO
     return false;
   return result.succeed();
 }
 
 bool
 ArrayType::AddressOfElement(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
-  RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
+  RootedObject obj(cx, GetThisObject(cx, args, "ArrayType.prototype.addressOfElement"));
   if (!obj)
     return false;
   if (!CData::IsCDataMaybeUnwrap(&obj)) {
     return IncompatibleThisProto(cx, "ArrayType.prototype.addressOfElement",
                                  args.thisv());
   }
 
   RootedObject typeObj(cx, CData::GetCType(obj));
@@ -6257,17 +6256,17 @@ StructType::BuildFFIType(JSContext* cx, 
 
   return Move(ffiType);
 }
 
 bool
 StructType::Define(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
-  RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
+  RootedObject obj(cx, GetThisObject(cx, args, "StructType.prototype.define"));
   if (!obj)
     return false;
   if (!CType::IsCType(obj)) {
     return IncompatibleThisProto(cx, "StructType.prototype.define",
                                  args.thisv());
   }
   if (CType::GetTypeCode(obj) != TYPE_struct) {
     return IncompatibleThisType(cx, "StructType.prototype.define",
@@ -6549,20 +6548,21 @@ StructType::FieldSetter(JSContext* cx, u
   return ImplicitConvert(cx, args.get(0), field->mType, data, ConversionType::Setter, nullptr,
                          nullptr, 0, typeObj, field->mIndex);
 }
 
 bool
 StructType::AddressOfField(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
-  RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
+  RootedObject obj(cx, GetThisObject(cx, args, "StructType.prototype.addressOfField"));
   if (!obj)
     return false;
- if (!CData::IsCDataMaybeUnwrap(&obj)) {
+
+  if (!CData::IsCDataMaybeUnwrap(&obj)) {
     return IncompatibleThisProto(cx, "StructType.prototype.addressOfField",
                                  args.thisv());
   }
 
   JSObject* typeObj = CData::GetCType(obj);
   if (CType::GetTypeCode(typeObj) != TYPE_struct) {
     return IncompatibleThisType(cx, "StructType.prototype.addressOfField",
                                 "non-StructType CData", args.thisv());
@@ -7803,17 +7803,17 @@ CData::ValueSetter(JSContext* cx, const 
 bool
 CData::Address(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   if (args.length() != 0) {
     return ArgumentLengthError(cx, "CData.prototype.address", "no", "s");
   }
 
-  RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
+  RootedObject obj(cx, GetThisObject(cx, args, "CData.prototype.address"));
   if (!obj)
     return false;
   if (!IsCDataMaybeUnwrap(&obj)) {
     return IncompatibleThisProto(cx, "CData.prototype.address", args.thisv());
   }
 
   RootedObject typeObj(cx, CData::GetCType(obj));
   RootedObject pointerType(cx, PointerType::CreateInternal(cx, typeObj));
@@ -7911,17 +7911,17 @@ static bool
 ReadStringCommon(JSContext* cx, InflateUTF8Method inflateUTF8, unsigned argc,
                  Value* vp, const char* funName)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   if (args.length() != 0) {
     return ArgumentLengthError(cx, funName, "no", "s");
   }
 
-  RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
+  RootedObject obj(cx, GetThisObject(cx, args, funName));
   if (!obj) {
     return IncompatibleThisProto(cx, funName, args.thisv());
   }
   if (!CData::IsCDataMaybeUnwrap(&obj)) {
       if (!CDataFinalizer::IsCDataFinalizer(obj)) {
           return IncompatibleThisProto(cx, funName, args.thisv());
       }
 
@@ -8060,17 +8060,17 @@ CData::GetSourceString(JSContext* cx, Ha
 bool
 CData::ToSource(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   if (args.length() != 0) {
     return ArgumentLengthError(cx, "CData.prototype.toSource", "no", "s");
   }
 
-  RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
+  RootedObject obj(cx, GetThisObject(cx, args, "CData.prototype.toSource"));
   if (!obj)
     return false;
   if (!CData::IsCDataMaybeUnwrap(&obj) && !CData::IsCDataProto(obj)) {
     return IncompatibleThisProto(cx, "CData.prototype.toSource",
                                  InformalValueTypeName(args.thisv()));
   }
 
   JSString* result;
@@ -8105,17 +8105,17 @@ CData::LastErrorGetter(JSContext* cx, co
   return true;
 }
 #endif // defined(XP_WIN)
 
 bool
 CDataFinalizer::Methods::ToSource(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
-  RootedObject objThis(cx, JS_THIS_OBJECT(cx, vp));
+  RootedObject objThis(cx, GetThisObject(cx, args, "CDataFinalizer.prototype.toSource"));
   if (!objThis)
     return false;
   if (!CDataFinalizer::IsCDataFinalizer(objThis)) {
     return IncompatibleThisProto(cx, "CDataFinalizer.prototype.toSource",
                                  InformalValueTypeName(args.thisv()));
   }
 
   CDataFinalizer::Private* p = (CDataFinalizer::Private*)
@@ -8164,17 +8164,17 @@ CDataFinalizer::Methods::ToSource(JSCont
   args.rval().setString(strMessage);
   return true;
 }
 
 bool
 CDataFinalizer::Methods::ToString(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
-  JSObject* objThis = JS_THIS_OBJECT(cx, vp);
+  JSObject* objThis = GetThisObject(cx, args, "CDataFinalizer.prototype.toString");
   if (!objThis)
     return false;
   if (!CDataFinalizer::IsCDataFinalizer(objThis)) {
     return IncompatibleThisProto(cx, "CDataFinalizer.prototype.toString",
                                  InformalValueTypeName(args.thisv()));
   }
 
   JSString* strMessage;
@@ -8484,17 +8484,17 @@ bool
 CDataFinalizer::Methods::Forget(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   if (args.length() != 0) {
     return ArgumentLengthError(cx, "CDataFinalizer.prototype.forget", "no",
                                "s");
   }
 
-  RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
+  RootedObject obj(cx, GetThisObject(cx, args, "CDataFinalizer.prototype.forget"));
   if (!obj)
     return false;
   if (!CDataFinalizer::IsCDataFinalizer(obj)) {
     return IncompatibleThisProto(cx, "CDataFinalizer.prototype.forget",
                                  args.thisv());
   }
 
   CDataFinalizer::Private* p = (CDataFinalizer::Private*)
@@ -8531,17 +8531,17 @@ bool
 CDataFinalizer::Methods::Dispose(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   if (args.length() != 0) {
     return ArgumentLengthError(cx, "CDataFinalizer.prototype.dispose", "no",
                                "s");
   }
 
-  RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
+  RootedObject obj(cx, GetThisObject(cx, args, "CDataFinalizer.prototype.dispose"));
   if (!obj)
     return false;
   if (!CDataFinalizer::IsCDataFinalizer(obj)) {
     return IncompatibleThisProto(cx, "CDataFinalizer.prototype.dispose",
                                  args.thisv());
   }
 
   CDataFinalizer::Private* p = (CDataFinalizer::Private*)
@@ -8814,17 +8814,17 @@ Int64::IsInt64(JSObject* obj)
 {
   return JS_GetClass(obj) == &sInt64Class;
 }
 
 bool
 Int64::ToString(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
-  RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
+  RootedObject obj(cx, GetThisObject(cx, args, "Int64.prototype.toString"));
   if (!obj)
     return false;
   if (!Int64::IsInt64(obj)) {
     if (!CData::IsCDataMaybeUnwrap(&obj)) {
       return IncompatibleThisProto(cx, "Int64.prototype.toString",
                                    InformalValueTypeName(args.thisv()));
     }
     return IncompatibleThisType(cx, "Int64.prototype.toString",
@@ -8833,17 +8833,17 @@ Int64::ToString(JSContext* cx, unsigned 
 
   return Int64Base::ToString(cx, obj, args, false);
 }
 
 bool
 Int64::ToSource(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
-  RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
+  RootedObject obj(cx, GetThisObject(cx, args, "Int64.prototype.toSource"));
   if (!obj)
     return false;
   if (!Int64::IsInt64(obj)) {
     if (!CData::IsCDataMaybeUnwrap(&obj)) {
       return IncompatibleThisProto(cx, "Int64.prototype.toSource",
                                    InformalValueTypeName(args.thisv()));
     }
     return IncompatibleThisType(cx, "Int64.prototype.toSource",
@@ -8998,17 +8998,17 @@ UInt64::IsUInt64(JSObject* obj)
 {
   return JS_GetClass(obj) == &sUInt64Class;
 }
 
 bool
 UInt64::ToString(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
-  RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
+  RootedObject obj(cx, GetThisObject(cx, args, "UInt64.prototype.toString"));
   if (!obj)
     return false;
   if (!UInt64::IsUInt64(obj)) {
     if (!CData::IsCDataMaybeUnwrap(&obj)) {
       return IncompatibleThisProto(cx, "UInt64.prototype.toString",
                                    InformalValueTypeName(args.thisv()));
     }
     return IncompatibleThisType(cx, "UInt64.prototype.toString",
@@ -9017,17 +9017,17 @@ UInt64::ToString(JSContext* cx, unsigned
 
   return Int64Base::ToString(cx, obj, args, true);
 }
 
 bool
 UInt64::ToSource(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
-  RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
+  RootedObject obj(cx, GetThisObject(cx, args, "UInt64.prototype.toSource"));
   if (!obj)
     return false;
   if (!UInt64::IsUInt64(obj)) {
     if (!CData::IsCDataMaybeUnwrap(&obj)) {
       return IncompatibleThisProto(cx, "UInt64.prototype.toSource",
                                    InformalValueTypeName(args.thisv()));
     }
     return IncompatibleThisType(cx, "UInt64.prototype.toSource",
--- a/js/src/ctypes/CTypes.h
+++ b/js/src/ctypes/CTypes.h
@@ -176,16 +176,18 @@ extern size_t
 GetDeflatedUTF8StringLength(JSContext* maybecx, const CharT* chars,
                             size_t charsLength);
 
 template <typename CharT>
 MOZ_MUST_USE bool
 DeflateStringToUTF8Buffer(JSContext* maybecx, const CharT* src, size_t srclen,
                           char* dst, size_t* dstlenp);
 
+MOZ_MUST_USE JSObject*
+GetThisObject(JSContext* cx, const CallArgs& args, const char* msg);
 
 /*******************************************************************************
 ** Function and struct API definitions
 *******************************************************************************/
 
 MOZ_ALWAYS_INLINE void
 ASSERT_OK(bool ok)
 {
--- a/js/src/ctypes/Library.cpp
+++ b/js/src/ctypes/Library.cpp
@@ -208,19 +208,20 @@ Library::Finalize(JSFreeOp* fop, JSObjec
 {
   UnloadLibrary(obj);
 }
 
 bool
 Library::Open(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
-  JSObject* ctypesObj = JS_THIS_OBJECT(cx, vp);
+  JSObject* ctypesObj = GetThisObject(cx, args, "ctypes.open");
   if (!ctypesObj)
     return false;
+
   if (!IsCTypesGlobal(ctypesObj)) {
     JS_ReportErrorASCII(cx, "not a ctypes object");
     return false;
   }
 
   if (args.length() != 1 || args[0].isUndefined()) {
     JS_ReportErrorASCII(cx, "open requires a single argument");
     return false;
@@ -234,20 +235,21 @@ Library::Open(JSContext* cx, unsigned ar
   return true;
 }
 
 bool
 Library::Close(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
 
-  RootedObject obj(cx);
-  if (args.thisv().isObject())
-    obj = &args.thisv().toObject();
-  if (!obj || !IsLibrary(obj)) {
+  RootedObject obj(cx, GetThisObject(cx, args, "ctypes.close"));
+  if (!obj)
+    return false;
+
+  if (!IsLibrary(obj)) {
     JS_ReportErrorASCII(cx, "not a library");
     return false;
   }
 
   if (args.length() != 0) {
     JS_ReportErrorASCII(cx, "close doesn't take any arguments");
     return false;
   }
@@ -259,19 +261,21 @@ Library::Close(JSContext* cx, unsigned a
   args.rval().setUndefined();
   return true;
 }
 
 bool
 Library::Declare(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
-  RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
+
+  RootedObject obj(cx, GetThisObject(cx, args, "ctypes.declare"));
   if (!obj)
     return false;
+
   if (!IsLibrary(obj)) {
     JS_ReportErrorASCII(cx, "not a library");
     return false;
   }
 
   PRLibrary* library = GetLibrary(obj);
   if (!library) {
     JS_ReportErrorASCII(cx, "library not open");
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -3239,17 +3239,17 @@ GCRuntime::triggerGC(JS::gcreason::Reaso
      */
     if (!CurrentThreadCanAccessRuntime(rt))
         return false;
 
     /* GC is already running. */
     if (JS::CurrentThreadIsHeapCollecting())
         return false;
 
-    JS::PrepareForFullGC(rt->activeContextFromOwnThread());
+    JS::PrepareForFullGC(rt->mainContextFromOwnThread());
     requestMajorGC(reason);
     return true;
 }
 
 void
 GCRuntime::maybeAllocTriggerZoneGC(Zone* zone, const AutoLockGC& lock)
 {
     MOZ_ASSERT(!JS::CurrentThreadIsHeapCollecting());
@@ -3336,17 +3336,17 @@ GCRuntime::triggerZoneGC(Zone* zone, JS:
 
 void
 GCRuntime::maybeGC(Zone* zone)
 {
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
 
 #ifdef JS_GC_ZEAL
     if (hasZealMode(ZealMode::Alloc) || hasZealMode(ZealMode::RootsChange)) {
-        JS::PrepareForFullGC(rt->activeContextFromOwnThread());
+        JS::PrepareForFullGC(rt->mainContextFromOwnThread());
         gc(GC_NORMAL, JS::gcreason::DEBUG_GC);
         return;
     }
 #endif
 
     if (gcIfRequested())
         return;
 
@@ -3961,21 +3961,20 @@ GCRuntime::purgeRuntime()
         comp->purge();
 
     for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
         zone->atomCache().clearAndShrink();
         zone->externalStringCache().purge();
         zone->functionToStringCache().purge();
     }
 
-    for (const CooperatingContext& target : rt->cooperatingContexts()) {
-        freeUnusedLifoBlocksAfterSweeping(&target.context()->tempLifoAlloc());
-        target.context()->interpreterStack().purge(rt);
-        target.context()->frontendCollectionPool().purge();
-    }
+    JSContext* cx = rt->mainContextFromOwnThread();
+    freeUnusedLifoBlocksAfterSweeping(&cx->tempLifoAlloc());
+    cx->interpreterStack().purge(rt);
+    cx->frontendCollectionPool().purge();
 
     rt->caches().purge();
 
     if (auto cache = rt->maybeThisRuntimeSharedImmutableStrings())
         cache->purge();
 
     MOZ_ASSERT(unmarkGrayStack.empty());
     unmarkGrayStack.clearAndFree();
@@ -7015,18 +7014,17 @@ GCRuntime::incrementalCollectSlice(Slice
         incrementalState = State::Mark;
 
         if (isIncremental && useZeal && hasZealMode(ZealMode::IncrementalRootsThenFinish))
             break;
 
         MOZ_FALLTHROUGH;
 
       case State::Mark:
-        for (const CooperatingContext& target : rt->cooperatingContexts())
-            AutoGCRooter::traceAllWrappers(target, &marker);
+        AutoGCRooter::traceAllWrappers(rt->mainContextFromOwnThread(), &marker);
 
         /* If we needed delayed marking for gray roots, then collect until done. */
         if (isIncremental && !hasValidGrayRootsBuffer()) {
             budget.makeUnlimited();
             isIncremental = false;
             stats().nonincremental(AbortReason::GrayRootBufferingFailed);
         }
 
@@ -7463,17 +7461,17 @@ GCRuntime::maybeDoCycleCollection()
     for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
         ++compartmentsTotal;
         GlobalObject* global = c->unsafeUnbarrieredMaybeGlobal();
         if (global && global->isMarkedGray())
             ++compartmentsGray;
     }
     double grayFraction = double(compartmentsGray) / double(compartmentsTotal);
     if (grayFraction > ExcessiveGrayCompartments || compartmentsGray > LimitGrayCompartments)
-        callDoCycleCollectionCallback(rt->activeContextFromOwnThread());
+        callDoCycleCollectionCallback(rt->mainContextFromOwnThread());
 }
 
 void
 GCRuntime::checkCanCallAPI()
 {
     MOZ_RELEASE_ASSERT(CurrentThreadCanAccessRuntime(rt));
 
     /* If we attempt to invoke the GC while we are running in the GC, assert. */
@@ -7551,17 +7549,17 @@ GCRuntime::collect(bool nonincrementalBy
          *    not collected (see the large comment in beginMarkPhase)
          */
         repeat = false;
         if (!isIncrementalGCInProgress()) {
             if (wasReset) {
                 repeat = true;
             } else if (rootsRemoved && IsShutdownGC(reason)) {
                 /* Need to re-schedule all zones for GC. */
-                JS::PrepareForFullGC(rt->activeContextFromOwnThread());
+                JS::PrepareForFullGC(rt->mainContextFromOwnThread());
                 repeat = true;
                 reason = JS::gcreason::ROOTS_REMOVED;
             } else if (shouldRepeatForDeadZone(reason)) {
                 repeat = true;
                 reason = JS::gcreason::COMPARTMENT_REVIVED;
             }
          }
     } while (repeat);
@@ -7667,36 +7665,36 @@ ZonesSelected(JSRuntime* rt)
     return false;
 }
 
 void
 GCRuntime::startDebugGC(JSGCInvocationKind gckind, SliceBudget& budget)
 {
     MOZ_ASSERT(!isIncrementalGCInProgress());
     if (!ZonesSelected(rt))
-        JS::PrepareForFullGC(rt->activeContextFromOwnThread());
+        JS::PrepareForFullGC(rt->mainContextFromOwnThread());
     invocationKind = gckind;
     collect(false, budget, JS::gcreason::DEBUG_GC);
 }
 
 void
 GCRuntime::debugGCSlice(SliceBudget& budget)
 {
     MOZ_ASSERT(isIncrementalGCInProgress());
     if (!ZonesSelected(rt))
-        JS::PrepareForIncrementalGC(rt->activeContextFromOwnThread());
+        JS::PrepareForIncrementalGC(rt->mainContextFromOwnThread());
     collect(false, budget, JS::gcreason::DEBUG_GC);
 }
 
 /* Schedule a full GC unless a zone will already be collected. */
 void
 js::PrepareForDebugGC(JSRuntime* rt)
 {
     if (!ZonesSelected(rt))
-        JS::PrepareForFullGC(rt->activeContextFromOwnThread());
+        JS::PrepareForFullGC(rt->mainContextFromOwnThread());
 }
 
 void
 GCRuntime::onOutOfMallocMemory()
 {
     // Stop allocating new chunks.
     allocTask.cancel(GCParallelTask::CancelAndWait);
 
@@ -7859,20 +7857,17 @@ js::NewCompartment(JSContext* cx, JSPrin
         group = rt->gc.systemZoneGroup;
         break;
       case JS::NewZoneInExistingZoneGroup:
         group = static_cast<ZoneGroup*>(options.creationOptions().zonePointer());
         MOZ_ASSERT(group);
         break;
     }
 
-    if (group) {
-        // Take over ownership of the group while we create the compartment/zone.
-        group->enter(cx);
-    } else {
+    if (!group) {
         MOZ_ASSERT(!zone);
         group = cx->new_<ZoneGroup>(rt);
         if (!group)
             return nullptr;
 
         groupHolder.reset(group);
 
         if (!group->init()) {
@@ -7932,23 +7927,21 @@ js::NewCompartment(JSContext* cx, JSPrin
             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;
-            group->setUseExclusiveLocking();
         }
     }
 
     zoneHolder.forget();
     groupHolder.forget();
-    group->leave();
     return compartment.forget();
 }
 
 void
 gc::MergeCompartments(JSCompartment* source, JSCompartment* target)
 {
     JSRuntime* rt = source->runtimeFromActiveCooperatingThread();
     rt->gc.mergeCompartments(source, target);
@@ -7964,17 +7957,17 @@ GCRuntime::mergeCompartments(JSCompartme
     // also implies that the compartment is not visible to the debugger.
     MOZ_ASSERT(source->creationOptions_.mergeable());
     MOZ_ASSERT(source->creationOptions_.invisibleToDebugger());
 
     MOZ_ASSERT(!source->hasBeenEntered());
     MOZ_ASSERT(source->zone()->compartments().length() == 1);
     MOZ_ASSERT(source->zone()->group()->zones().length() == 1);
 
-    JSContext* cx = rt->activeContextFromOwnThread();
+    JSContext* cx = rt->mainContextFromOwnThread();
 
     MOZ_ASSERT(!source->zone()->wasGCStarted());
     JS::AutoAssertNoGC nogc(cx);
 
     AutoTraceSession session(rt);
 
     // Cleanup tables and other state in the source compartment that will be
     // meaningless after merging into the target compartment.
@@ -8227,17 +8220,16 @@ void PreventGCDuringInteractiveDebug()
 
 #endif
 
 void
 js::ReleaseAllJITCode(FreeOp* fop)
 {
     js::CancelOffThreadIonCompile(fop->runtime());
 
-    JSRuntime::AutoProhibitActiveContextChange apacc(fop->runtime());
     for (ZonesIter zone(fop->runtime(), SkipAtoms); !zone.done(); zone.next()) {
         zone->setPreservingCode(false);
         zone->discardJitCode(fop);
     }
 }
 
 void
 ArenaLists::adoptArenas(JSRuntime* rt, ArenaLists* fromArenaLists, bool targetZoneIsCollecting)
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -742,27 +742,26 @@ js::Nursery::collect(JS::gcreason::Reaso
     bool shouldPretenure = (validPromotionRate && promotionRate > 0.6) ||
         IsFullStoreBufferReason(reason);
 
     if (shouldPretenure) {
         JSContext* cx = TlsContext.get();
         for (auto& entry : tenureCounts.entries) {
             if (entry.count >= 3000) {
                 ObjectGroup* group = entry.group;
-                if (group->canPreTenure() && group->zone()->group()->canEnterWithoutYielding(cx)) {
+                if (group->canPreTenure()) {
                     AutoCompartment ac(cx, group);
                     group->setShouldPreTenure(cx);
                     pretenureCount++;
                 }
             }
         }
     }
     for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
         if (shouldPretenure && zone->allocNurseryStrings && zone->tenuredStrings >= 30 * 1000) {
-            JSRuntime::AutoProhibitActiveContextChange apacc(rt);
             CancelOffThreadIonCompile(zone);
             bool preserving = zone->isPreservingCode();
             zone->setPreservingCode(false);
             zone->discardJitCode(rt->defaultFreeOp());
             zone->setPreservingCode(preserving);
             for (CompartmentsInZoneIter c(zone); !c.done(); c.next()) {
                 if (jit::JitCompartment* jitComp = c->jitCompartment()) {
                     jitComp->discardStubs();
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -75,19 +75,19 @@ JS_FOR_EACH_TRACEKIND(TRACE_ROOTS)
 
 void
 JS::RootingContext::traceStackRoots(JSTracer* trc)
 {
     TraceStackRoots(trc, stackRoots_);
 }
 
 static void
-TraceExactStackRoots(const CooperatingContext& target, JSTracer* trc)
+TraceExactStackRoots(JSContext* cx, JSTracer* trc)
 {
-    target.context()->traceStackRoots(trc);
+    cx->traceStackRoots(trc);
 }
 
 template <typename T, TraceFunction<T> TraceFn = TraceNullableRoot>
 static inline void
 TracePersistentRootedList(JSTracer* trc, mozilla::LinkedList<PersistentRooted<void*>>& list,
                          const char* name)
 {
     for (PersistentRooted<void*>* r : list)
@@ -191,26 +191,26 @@ AutoGCRooter::trace(JSTracer* trc)
     }
 
     MOZ_ASSERT(tag_ >= 0);
     if (Value* vp = static_cast<AutoArrayRooter*>(this)->array)
         TraceRootRange(trc, tag_, vp, "JS::AutoArrayRooter.array");
 }
 
 /* static */ void
-AutoGCRooter::traceAll(const CooperatingContext& target, JSTracer* trc)
+AutoGCRooter::traceAll(JSContext* cx, JSTracer* trc)
 {
-    for (AutoGCRooter* gcr = target.context()->autoGCRooters_; gcr; gcr = gcr->down)
+    for (AutoGCRooter* gcr = cx->autoGCRooters_; gcr; gcr = gcr->down)
         gcr->trace(trc);
 }
 
 /* static */ void
-AutoGCRooter::traceAllWrappers(const CooperatingContext& target, JSTracer* trc)
+AutoGCRooter::traceAllWrappers(JSContext* cx, JSTracer* trc)
 {
-    for (AutoGCRooter* gcr = target.context()->autoGCRooters_; gcr; gcr = gcr->down) {
+    for (AutoGCRooter* gcr = cx->autoGCRooters_; gcr; gcr = gcr->down) {
         if (gcr->tag_ == WRAPVECTOR || gcr->tag_ == WRAPPER)
             gcr->trace(trc);
     }
 }
 
 void
 StackShape::trace(JSTracer* trc)
 {
@@ -314,47 +314,45 @@ void
 js::gc::GCRuntime::traceRuntimeCommon(JSTracer* trc, TraceOrMarkRuntime traceOrMark,
                                       AutoTraceSession& session)
 {
     MOZ_ASSERT(!TlsContext.get()->suppressGC);
 
     {
         gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::MARK_STACK);
 
-        JSContext* cx = TlsContext.get();
-        for (const CooperatingContext& target : rt->cooperatingContexts()) {
-            // Trace active interpreter and JIT stack roots.
-            TraceInterpreterActivations(cx, target, trc);
-            jit::TraceJitActivations(cx, target, trc);
+        JSContext* cx = rt->mainContextFromOwnThread();
+
+        // Trace active interpreter and JIT stack roots.
+        TraceInterpreterActivations(cx, trc);
+        jit::TraceJitActivations(cx, trc);
 
-            // Trace legacy C stack roots.
-            AutoGCRooter::traceAll(target, trc);
+        // Trace legacy C stack roots.
+        AutoGCRooter::traceAll(cx, trc);
 
-            // Trace C stack roots.
-            TraceExactStackRoots(target, trc);
-        }
+        // Trace C stack roots.
+        TraceExactStackRoots(cx, trc);
 
         for (RootRange r = rootsHash.ref().all(); !r.empty(); r.popFront()) {
             const RootEntry& entry = r.front();
             TraceRoot(trc, entry.key(), entry.value());
         }
     }
 
     // Trace runtime global roots.
     TracePersistentRooted(rt, trc);
 
     // Trace the self-hosting global compartment.
     rt->traceSelfHostingGlobal(trc);
 
     // Trace the shared Intl data.
     rt->traceSharedIntlData(trc);
 
-    // Trace anything in any of the cooperating threads.
-    for (const CooperatingContext& target : rt->cooperatingContexts())
-        target.context()->trace(trc);
+    // Trace the JSContext.
+    rt->mainContextFromOwnThread()->trace(trc);
 
     // Trace all compartment roots, but not the compartment itself; it is
     // traced via the parent pointer if traceRoots actually traces anything.
     for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next())
         c->traceRoots(trc, traceOrMark);
 
     // Trace helper thread roots.
     HelperThreadState().trace(trc, session);
--- a/js/src/gc/Verifier.cpp
+++ b/js/src/gc/Verifier.cpp
@@ -176,18 +176,17 @@ NextNode(VerifyNode* node)
 void
 gc::GCRuntime::startVerifyPreBarriers()
 {
     if (verifyPreData || isIncrementalGCInProgress())
         return;
 
     if (IsIncrementalGCUnsafe(rt) != AbortReason::None ||
         TlsContext.get()->keepAtoms ||
-        rt->hasHelperThreadZones() ||
-        rt->cooperatingContexts().length() != 1)
+        rt->hasHelperThreadZones())
     {
         return;
     }
 
     number++;
 
     VerifyPreTracer* trc = js_new<VerifyPreTracer>(rt);
     if (!trc)
@@ -329,17 +328,17 @@ gc::GCRuntime::endVerifyPreBarriers()
 {
     VerifyPreTracer* trc = verifyPreData;
 
     if (!trc)
         return;
 
     MOZ_ASSERT(!JS::IsGenerationalGCEnabled(rt));
 
-    AutoPrepareForTracing prep(rt->activeContextFromOwnThread());
+    AutoPrepareForTracing prep(rt->mainContextFromOwnThread());
 
     bool compartmentCreated = false;
 
     /* We need to disable barriers before tracing, which may invoke barriers. */
     for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
         if (!zone->needsIncrementalBarrier())
             compartmentCreated = true;
 
--- a/js/src/gc/ZoneGroup.cpp
+++ b/js/src/gc/ZoneGroup.cpp
@@ -12,18 +12,17 @@
 #include "vm/JSContext.h"
 
 using namespace js;
 
 namespace js {
 
 ZoneGroup::ZoneGroup(JSRuntime* runtime)
   : runtime(runtime),
-    ownerContext_(TlsContext.get()),
-    enterCount(1),
+    helperThreadOwnerContext_(nullptr),
     zones_(this),
     helperThreadUse(HelperThreadUse::None),
 #ifdef DEBUG
     ionBailAfter_(this, 0),
 #endif
     jitZoneGroup(this, nullptr),
     debuggerList_(this),
     numFinishedBuilders(0),
@@ -55,61 +54,28 @@ ZoneGroup::~ZoneGroup()
 
     js_delete(jitZoneGroup.ref());
 
     if (this == runtime->gc.systemZoneGroup)
         runtime->gc.systemZoneGroup = nullptr;
 }
 
 void
-ZoneGroup::enter(JSContext* cx)
+ZoneGroup::setHelperThreadOwnerContext(JSContext* cx)
 {
-    if (ownerContext().context() == cx) {
-        MOZ_ASSERT(enterCount);
-    } else {
-        if (useExclusiveLocking()) {
-            MOZ_ASSERT(!usedByHelperThread());
-            while (ownerContext().context() != nullptr) {
-                cx->yieldToEmbedding();
-            }
-        }
-        MOZ_RELEASE_ASSERT(ownerContext().context() == nullptr);
-        MOZ_ASSERT(enterCount == 0);
-        ownerContext_ = CooperatingContext(cx);
-        if (cx->generationalDisabled)
-            nursery().disable();
-
-        // Finish any Ion compilations in this zone group, in case compilation
-        // finished for some script in this group while no thread was in this
-        // group.
-        jit::AttachFinishedCompilations(this, nullptr);
-    }
-    enterCount++;
-}
-
-void
-ZoneGroup::leave()
-{
-    MOZ_ASSERT(ownedByCurrentThread());
-    MOZ_ASSERT(enterCount);
-    if (--enterCount == 0)
-        ownerContext_ = CooperatingContext(nullptr);
+    MOZ_ASSERT_IF(cx, TlsContext.get() == cx);
+    helperThreadOwnerContext_ = cx;
 }
 
 bool
-ZoneGroup::canEnterWithoutYielding(JSContext* cx)
+ZoneGroup::ownedByCurrentHelperThread()
 {
-    return ownerContext().context() == cx || ownerContext().context() == nullptr;
-}
-
-bool
-ZoneGroup::ownedByCurrentThread()
-{
+    MOZ_ASSERT(usedByHelperThread());
     MOZ_ASSERT(TlsContext.get());
-    return ownerContext().context() == TlsContext.get();
+    return helperThreadOwnerContext_ == TlsContext.get();
 }
 
 ZoneGroup::IonBuilderList&
 ZoneGroup::ionLazyLinkList()
 {
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime),
                "Should only be mutated by the active thread.");
     return ionLazyLinkList_.ref();
@@ -151,31 +117,8 @@ ZoneGroup::deleteEmptyZone(Zone* zone)
             zone->destroy(runtime->defaultFreeOp());
             return;
         }
     }
     MOZ_CRASH("Zone not found");
 }
 
 } // namespace js
-
-JS::AutoRelinquishZoneGroups::AutoRelinquishZoneGroups(JSContext* cx)
-  : cx(cx)
-{
-    MOZ_ASSERT(cx == TlsContext.get());
-
-    AutoEnterOOMUnsafeRegion oomUnsafe;
-    for (ZoneGroupsIter group(cx->runtime()); !group.done(); group.next()) {
-        while (group->ownerContext().context() == cx) {
-            group->leave();
-            if (!enterList.append(group))
-                oomUnsafe.crash("AutoRelinquishZoneGroups");
-        }
-    }
-}
-
-JS::AutoRelinquishZoneGroups::~AutoRelinquishZoneGroups()
-{
-    for (size_t i = 0; i < enterList.length(); i++) {
-        ZoneGroup* group = static_cast<ZoneGroup*>(enterList[i]);
-        group->enter(cx);
-    }
-}
--- a/js/src/gc/ZoneGroup.h
+++ b/js/src/gc/ZoneGroup.h
@@ -28,34 +28,23 @@ typedef Vector<JS::Zone*, 4, SystemAlloc
 // entered the zone group.
 
 class ZoneGroup
 {
   public:
     JSRuntime* const runtime;
 
   private:
-    // The context with exclusive access to this zone group.
-    UnprotectedData<CooperatingContext> ownerContext_;
-
-    // The number of times the context has entered this zone group.
-    UnprotectedData<size_t> enterCount;
-
-    // If this flag is true, then we may need to block before entering this zone
-    // group. Blocking happens using JSContext::yieldToEmbedding.
-    UnprotectedData<bool> useExclusiveLocking_;
+    // The helper thread context with exclusive access to this zone group, if
+    // usedByHelperThread(), or nullptr when on the main thread.
+    UnprotectedData<JSContext*> helperThreadOwnerContext_;
 
   public:
-    CooperatingContext& ownerContext() { return ownerContext_.ref(); }
-    void* addressOfOwnerContext() { return &ownerContext_.ref().cx; }
-
-    void enter(JSContext* cx);
-    void leave();
-    bool canEnterWithoutYielding(JSContext* cx);
-    bool ownedByCurrentThread();
+    bool ownedByCurrentHelperThread();
+    void setHelperThreadOwnerContext(JSContext* cx);
 
     // All zones in the group.
   private:
     ZoneGroupOrGCTaskData<ZoneVector> zones_;
   public:
     ZoneVector& zones() { return zones_.ref(); }
 
   private:
@@ -96,20 +85,16 @@ class ZoneGroup
     bool init();
 
     inline Nursery& nursery();
     inline gc::StoreBuffer& storeBuffer();
 
     inline bool isCollecting();
     inline bool isGCScheduled();
 
-    // See the useExclusiveLocking_ field above.
-    void setUseExclusiveLocking() { useExclusiveLocking_ = true; }
-    bool useExclusiveLocking() { return useExclusiveLocking_; }
-
     // Delete an empty zone after its contents have been merged.
     void deleteEmptyZone(Zone* zone);
 
 #ifdef DEBUG
   private:
     // The number of possible bailing places encounters before forcefully bailing
     // in that place. Zero means inactive.
     ZoneGroupData<uint32_t> ionBailAfter_;
--- a/js/src/irregexp/NativeRegExpMacroAssembler.cpp
+++ b/js/src/irregexp/NativeRegExpMacroAssembler.cpp
@@ -162,19 +162,17 @@ NativeRegExpMacroAssembler::GenerateCode
     // Actually emit code to start a new stack frame.
     masm.reserveStack(frameSize);
     masm.checkStackAlignment();
 
     // Check if we have space on the stack. Use the *NoInterrupt stack limit to
     // avoid failing repeatedly when the regex code is called from Ion JIT code,
     // see bug 1208819.
     Label stack_ok;
-    void* context_addr = cx->zone()->group()->addressOfOwnerContext();
-    masm.loadPtr(AbsoluteAddress(context_addr), temp0);
-    Address limit_addr(temp0, offsetof(JSContext, jitStackLimitNoInterrupt));
+    AbsoluteAddress limit_addr(cx->addressOfJitStackLimitNoInterrupt());
     masm.branchStackPtrRhs(Assembler::Below, limit_addr, &stack_ok);
 
     // Exit with an exception. There is not enough space on the stack
     // for our working registers.
     masm.movePtr(ImmWord(RegExpRunStatus_Error), temp0);
     masm.jump(&return_temp0);
 
     masm.bind(&stack_ok);
@@ -279,19 +277,17 @@ NativeRegExpMacroAssembler::GenerateCode
                        ImmWord(register_offset(num_saved_registers_)), &init_loop);
     } else {
         // Unroll the loop.
         for (int i = 0; i < num_saved_registers_; i++)
             masm.storePtr(temp0, register_location(i));
     }
 
     // Initialize backtrack stack pointer.
-    size_t baseOffset = offsetof(JSContext, regexpStack) + RegExpStack::offsetOfBase();
-    masm.loadPtr(AbsoluteAddress(context_addr), backtrack_stack_pointer);
-    masm.loadPtr(Address(backtrack_stack_pointer, baseOffset), backtrack_stack_pointer);
+    masm.loadPtr(AbsoluteAddress(cx->regexpStack.ref().addressOfBase()), backtrack_stack_pointer);
     masm.storePtr(backtrack_stack_pointer,
                   Address(masm.getStackPointer(), offsetof(FrameData, backtrackStackBase)));
 
     masm.jump(&start_label_);
 
     // Exit code:
     if (success_label_.used()) {
         MOZ_ASSERT(num_saved_registers_ > 0);
@@ -486,20 +482,17 @@ NativeRegExpMacroAssembler::GenerateCode
         Label return_from_overflow_handler;
         masm.branchTest32(Assembler::Zero, temp0, temp0, &return_from_overflow_handler);
 
         // Otherwise, store the new backtrack stack base and recompute the new
         // top of the stack.
         Address backtrackStackBaseAddress(temp2, offsetof(FrameData, backtrackStackBase));
         masm.subPtr(backtrackStackBaseAddress, backtrack_stack_pointer);
 
-        void* context_addr = cx->zone()->group()->addressOfOwnerContext();
-        size_t baseOffset = offsetof(JSContext, regexpStack) + RegExpStack::offsetOfBase();
-        masm.loadPtr(AbsoluteAddress(context_addr), temp1);
-        masm.loadPtr(Address(temp1, baseOffset), temp1);
+        masm.loadPtr(AbsoluteAddress(cx->regexpStack.ref().addressOfBase()), temp1);
         masm.storePtr(temp1, backtrackStackBaseAddress);
         masm.addPtr(temp1, backtrack_stack_pointer);
 
         // Resume execution in calling code.
         masm.bind(&return_from_overflow_handler);
         masm.abiret();
     }
 
@@ -570,20 +563,18 @@ NativeRegExpMacroAssembler::AdvanceRegis
 
 void
 NativeRegExpMacroAssembler::Backtrack()
 {
     JitSpew(SPEW_PREFIX "Backtrack");
 
     // Check for an interrupt.
     Label noInterrupt;
-    void* contextAddr = cx->zone()->group()->addressOfOwnerContext();
-    masm.loadPtr(AbsoluteAddress(contextAddr), temp0);
     masm.branch32(Assembler::Equal,
-                  Address(temp0, offsetof(JSContext, interruptRegExpJit_)),
+                  AbsoluteAddress(cx->addressOfInterruptRegExpJit()),
                   Imm32(0),
                   &noInterrupt);
     masm.movePtr(ImmWord(RegExpRunStatus_Error), temp0);
     masm.jump(&exit_label_);
     masm.bind(&noInterrupt);
 
     // Pop code location from backtrack stack and jump to location.
     PopBacktrack(temp0);
@@ -1140,21 +1131,20 @@ NativeRegExpMacroAssembler::PopBacktrack
 }
 
 void
 NativeRegExpMacroAssembler::CheckBacktrackStackLimit()
 {
     JitSpew(SPEW_PREFIX "CheckBacktrackStackLimit");
 
     Label no_stack_overflow;
-    void* context_addr = cx->zone()->group()->addressOfOwnerContext();
-    size_t limitOffset = offsetof(JSContext, regexpStack) + RegExpStack::offsetOfLimit();
-    masm.loadPtr(AbsoluteAddress(context_addr), temp1);
-    masm.branchPtr(Assembler::AboveOrEqual, Address(temp1, limitOffset),
-                   backtrack_stack_pointer, &no_stack_overflow);
+    masm.branchPtr(Assembler::AboveOrEqual,
+                   AbsoluteAddress(cx->regexpStack.ref().addressOfLimit()),
+                   backtrack_stack_pointer,
+                   &no_stack_overflow);
 
     // Copy the stack pointer before the call() instruction modifies it.
     masm.moveStackPtrTo(temp2);
 
     masm.call(&stack_overflow_label_);
     masm.bind(&no_stack_overflow);
 
     // Exit with an exception if the call failed.
--- a/js/src/irregexp/RegExpStack.h
+++ b/js/src/irregexp/RegExpStack.h
@@ -77,16 +77,19 @@ class RegExpStack
 
     // Attempts to grow the stack by at least kStackLimitSlack entries.
     bool grow();
 
     // Address of allocated memory.
     static size_t offsetOfBase() { return offsetof(RegExpStack, base_); }
     static size_t offsetOfLimit() { return offsetof(RegExpStack, limit_); }
 
+    void* addressOfBase() { return &base_; }
+    void* addressOfLimit() { return &limit_; }
+
     void* base() { return base_; }
     void* limit() { return limit_; }
 
   private:
     // Artificial limit used when no memory has been allocated.
     static const uintptr_t kMemoryTop = static_cast<uintptr_t>(-1);
 
     // Minimal size of allocated stack area, in bytes.
deleted file mode 100644
--- a/js/src/jit-test/tests/basic/bug1341321.js
+++ /dev/null
@@ -1,20 +0,0 @@
-if (helperThreadCount() == 0)
-    quit();
-
-// The new Debugger here should throw but we don't have a way to verify this
-// (exceptions that worker threads throw do not cause the test to fail).
-evalInCooperativeThread('cooperativeYield(); var dbg = new Debugger();');
-
-var dbg = new Debugger;
-assertEq(dbg.addAllGlobalsAsDebuggees(), undefined);
-
-function assertThrows(f) {
-    var exception = false;
-    try { f(); } catch (e) { exception = true; }
-    assertEq(exception, true);
-}
-
-var dbg = new Debugger;
-dbg.onNewGlobalObject = function(global) {};
-assertThrows(() => evalInCooperativeThread("var x = 3"));
-assertThrows(cooperativeYield);
deleted file mode 100644
--- a/js/src/jit-test/tests/basic/bug1341358.js
+++ /dev/null
@@ -1,8 +0,0 @@
-if (helperThreadCount() == 0)
-    quit();
-evalInCooperativeThread("var x = 3");
-let PromiseCtor = Promise;
-let promises = [];
-let p = new PromiseCtor(function(res_, rej_) {});
-promises.push(p);
-let allPromise = getWaitForAllPromise(promises);
deleted file mode 100644
--- a/js/src/jit-test/tests/basic/bug1344315.js
+++ /dev/null
@@ -1,3 +0,0 @@
-if (helperThreadCount() === 0)
-  quit();
-evalInCooperativeThread('cooperativeYield(); var dbg = new Debugger();');
deleted file mode 100644
--- a/js/src/jit-test/tests/basic/bug1411947.js
+++ /dev/null
@@ -1,6 +0,0 @@
-if (helperThreadCount() === 0)
-  quit();
-evalInCooperativeThread(`
-startgc(9469, "shrinking");
-offThreadCompileScript("");
-`);
deleted file mode 100644
--- a/js/src/jit-test/tests/basic/bug1412298.js
+++ /dev/null
@@ -1,6 +0,0 @@
-if (helperThreadCount() === 0)
-  quit();
-evalInCooperativeThread(`
-      const dbg = new Debugger();
-      evalInWorker("");
-`);
deleted file mode 100644
--- a/js/src/jit-test/tests/basic/cooperative-threading-interrupt.js
+++ /dev/null
@@ -1,16 +0,0 @@
-if (helperThreadCount() === 0)
-  quit();
-setInterruptCallback(function() { print("MainThread Interrupt!"); cooperativeYield(); return true; });
-evalInCooperativeThread('\
-  setInterruptCallback(function() { print("Worker Interrupt!"); cooperativeYield(); return true; });\
-  for (var i = 0; i < 10; i++) {\
-     print("Worker: " + i);\
-     interruptIf(true);\
-  }\
-  print("Worker Done!");\
-');
-for (var i = 0; i < 10; i++) {
-    print("MainThread: " + i);
-    interruptIf(true);
-}
-print("MainThread Done!");
deleted file mode 100644
--- a/js/src/jit-test/tests/basic/cooperative-threading.js
+++ /dev/null
@@ -1,7 +0,0 @@
-if (helperThreadCount() === 0)
-  quit();
-evalInCooperativeThread("var x = 3");
-evalInCooperativeThread("cooperativeYield()");
-cooperativeYield();
-evalInCooperativeThread("cooperativeYield(); gc();");
-gc();
--- a/js/src/jit-test/tests/ctypes/incompatible-abi.js
+++ b/js/src/jit-test/tests/ctypes/incompatible-abi.js
@@ -1,9 +1,9 @@
 load(libdir + 'asserts.js');
 
 function test() {
   assertTypeErrorMessage(() => { ctypes.default_abi.toSource.call(1); },
-                         "ABI.prototype.toSource called on incompatible Number");
+                         "ABI.prototype.toSource called on incompatible object, got the number 1");
 }
 
 if (typeof ctypes === "object")
   test();
--- a/js/src/jit-test/tests/ctypes/incompatible-array.js
+++ b/js/src/jit-test/tests/ctypes/incompatible-array.js
@@ -1,13 +1,13 @@
 load(libdir + 'asserts.js');
 
 function test() {
   assertTypeErrorMessage(() => { ctypes.int32_t.array.call(1); },
-                         "CType.prototype.array called on incompatible object, got the object (new Number(1))");
+                         "CType.prototype.array called on incompatible object, got the number 1");
   assertTypeErrorMessage(() => { ctypes.int32_t.array(10)().addressOfElement.call(1); },
-                         "ArrayType.prototype.addressOfElement called on incompatible object, got the object (new Number(1))");
+                         "ArrayType.prototype.addressOfElement called on incompatible object, got the number 1");
   assertTypeErrorMessage(() => { ctypes.int32_t.array(10)().addressOfElement.call(ctypes.int32_t(0)); },
                          "ArrayType.prototype.addressOfElement called on non-ArrayType CData, got ctypes.int32_t(0)");
 }
 
 if (typeof ctypes === "object")
   test();
--- a/js/src/jit-test/tests/ctypes/incompatible-cdata.js
+++ b/js/src/jit-test/tests/ctypes/incompatible-cdata.js
@@ -1,20 +1,20 @@
 load(libdir + 'asserts.js');
 
 function test() {
   assertTypeErrorMessage(() => { ctypes.int32_t(0).address.call(1); },
-                         "CData.prototype.address called on incompatible object, got the object (new Number(1))");
+                         "CData.prototype.address called on incompatible object, got the number 1");
   assertTypeErrorMessage(() => { ctypes.char.array(10)("abc").readString.call(1); },
-                         "CData.prototype.readString called on incompatible object, got the object (new Number(1))");
+                         "CData.prototype.readString called on incompatible object, got the number 1");
 
   assertTypeErrorMessage(() => { ctypes.char.array(10)("abc").readStringReplaceMalformed.call(1); },
-                         "CData.prototype.readStringReplaceMalformed called on incompatible object, got the object (new Number(1))");
+                         "CData.prototype.readStringReplaceMalformed called on incompatible object, got the number 1");
   assertTypeErrorMessage(() => { ctypes.int32_t(0).toSource.call(1); },
-                         "CData.prototype.toSource called on incompatible Number");
+                         "CData.prototype.toSource called on incompatible object, got the number 1");
 
   let p = Object.getPrototypeOf(ctypes.int32_t());
   let o = {};
   Object.setPrototypeOf(o, p);
   assertTypeErrorMessage(() => { o.readString(); },
                          "CData.prototype.readString called on incompatible object, got <<error converting value to string>>");
   assertTypeErrorMessage(() => { o.readStringReplaceMalformed(); },
                          "CData.prototype.readStringReplaceMalformed called on incompatible object, got <<error converting value to string>>");
--- a/js/src/jit-test/tests/ctypes/incompatible-ctype.js
+++ b/js/src/jit-test/tests/ctypes/incompatible-ctype.js
@@ -1,11 +1,11 @@
 load(libdir + 'asserts.js');
 
 function test() {
   assertTypeErrorMessage(() => { ctypes.int32_t.toString.call(1); },
-                         "CType.prototype.toString called on incompatible Number");
+                         "CType.prototype.toString called on incompatible object, got the number 1");
   assertTypeErrorMessage(() => { ctypes.int32_t.toSource.call(1); },
-                         "CType.prototype.toSource called on incompatible Number");
+                         "CType.prototype.toSource called on incompatible object, got the number 1");
 }
 
 if (typeof ctypes === "object")
   test();
--- a/js/src/jit-test/tests/ctypes/incompatible-finalizer.js
+++ b/js/src/jit-test/tests/ctypes/incompatible-finalizer.js
@@ -1,20 +1,20 @@
 load(libdir + 'asserts.js');
 
 function test() {
   let fin = ctypes.CDataFinalizer(ctypes.int32_t(0), ctypes.FunctionType(ctypes.default_abi, ctypes.int32_t, [ctypes.int32_t]).ptr(x => x));
   assertTypeErrorMessage(() => { fin.toSource.call(1); },
-                         "CDataFinalizer.prototype.toSource called on incompatible Number");
+                         "CDataFinalizer.prototype.toSource called on incompatible object, got the number 1");
   assertTypeErrorMessage(() => { fin.toString.call(1); },
-                         "CDataFinalizer.prototype.toString called on incompatible Number");
+                         "CDataFinalizer.prototype.toString called on incompatible object, got the number 1");
   assertTypeErrorMessage(() => { fin.forget.call(1); },
-                         "CDataFinalizer.prototype.forget called on incompatible object, got the object (new Number(1))");
+                         "CDataFinalizer.prototype.forget called on incompatible object, got the number 1");
   assertTypeErrorMessage(() => { fin.dispose.call(1); },
-                         "CDataFinalizer.prototype.dispose called on incompatible object, got the object (new Number(1))");
+                         "CDataFinalizer.prototype.dispose called on incompatible object, got the number 1");
   fin.forget();
 
   assertTypeErrorMessage(() => { fin.readString(); },
                          "CDataFinalizer.prototype.readString called on empty CDataFinalizer");
   assertTypeErrorMessage(() => { fin.dispose(); },
                          "CDataFinalizer.prototype.dispose called on empty CDataFinalizer");
   assertTypeErrorMessage(() => { fin.forget(); },
                          "CDataFinalizer.prototype.forget called on empty CDataFinalizer");
--- a/js/src/jit-test/tests/ctypes/incompatible-int64.js
+++ b/js/src/jit-test/tests/ctypes/incompatible-int64.js
@@ -1,24 +1,24 @@
 load(libdir + 'asserts.js');
 
 function test() {
   assertTypeErrorMessage(() => { ctypes.Int64(0).toString.call(1); },
-                         "Int64.prototype.toString called on incompatible Number");
+                         "Int64.prototype.toString called on incompatible object, got the number 1");
   assertTypeErrorMessage(() => { ctypes.Int64(0).toString.call(ctypes.int32_t(0)); },
                          "Int64.prototype.toString called on non-Int64 CData");
   assertTypeErrorMessage(() => { ctypes.Int64(0).toSource.call(1); },
-                         "Int64.prototype.toSource called on incompatible Number");
+                         "Int64.prototype.toSource called on incompatible object, got the number 1");
   assertTypeErrorMessage(() => { ctypes.Int64(0).toSource.call(ctypes.int32_t(0)); },
                          "Int64.prototype.toSource called on non-Int64 CData");
 
   assertTypeErrorMessage(() => { ctypes.UInt64(0).toString.call(1); },
-                         "UInt64.prototype.toString called on incompatible Number");
+                         "UInt64.prototype.toString called on incompatible object, got the number 1");
   assertTypeErrorMessage(() => { ctypes.UInt64(0).toString.call(ctypes.int32_t(0)); },
                          "UInt64.prototype.toString called on non-UInt64 CData");
   assertTypeErrorMessage(() => { ctypes.UInt64(0).toSource.call(1); },
-                         "UInt64.prototype.toSource called on incompatible Number");
+                         "UInt64.prototype.toSource called on incompatible object, got the number 1");
   assertTypeErrorMessage(() => { ctypes.UInt64(0).toSource.call(ctypes.int32_t(0)); },
                          "UInt64.prototype.toSource called on non-UInt64 CData");
 }
 
 if (typeof ctypes === "object")
   test();
--- a/js/src/jit-test/tests/ctypes/incompatible-pointer.js
+++ b/js/src/jit-test/tests/ctypes/incompatible-pointer.js
@@ -1,19 +1,19 @@
 load(libdir + 'asserts.js');
 
 function test() {
   assertTypeErrorMessage(() => { ctypes.int32_t.ptr(0).isNull.call(1); },
-                         "PointerType.prototype.isNull called on incompatible object, got the object (new Number(1))");
+                         "PointerType.prototype.isNull called on incompatible object, got the number 1");
   assertTypeErrorMessage(() => { ctypes.int32_t.ptr(0).isNull.call({}); },
                          "PointerType.prototype.isNull called on incompatible object, got the object ({})");
   assertTypeErrorMessage(() => { ctypes.int32_t.ptr(0).increment.call(1); },
-                         "PointerType.prototype.increment called on incompatible object, got the object (new Number(1))");
+                         "PointerType.prototype.increment called on incompatible object, got the number 1");
   assertTypeErrorMessage(() => { ctypes.int32_t.ptr(0).increment.call(ctypes.int32_t(0)); },
                          "PointerType.prototype.increment called on non-PointerType CData, got ctypes.int32_t(0)");
   assertTypeErrorMessage(() => { ctypes.int32_t.ptr(0).decrement.call(1); },
-                         "PointerType.prototype.decrement called on incompatible object, got the object (new Number(1))");
+                         "PointerType.prototype.decrement called on incompatible object, got the number 1");
   assertTypeErrorMessage(() => { ctypes.int32_t.ptr(0).decrement.call(ctypes.int32_t(0)); },
                          "PointerType.prototype.decrement called on non-PointerType CData, got ctypes.int32_t(0)");
 }
 
 if (typeof ctypes === "object")
   test();
--- a/js/src/jit-test/tests/ctypes/incompatible-struct.js
+++ b/js/src/jit-test/tests/ctypes/incompatible-struct.js
@@ -1,13 +1,13 @@
 load(libdir + 'asserts.js');
 
 function test() {
   assertTypeErrorMessage(() => { ctypes.StructType("a").define.call(1); },
-                         "StructType.prototype.define called on incompatible object, got the object (new Number(1))");
+                         "StructType.prototype.define called on incompatible object, got the number 1");
   assertTypeErrorMessage(() => { ctypes.StructType("a").define.call(ctypes.int32_t); },
                          "StructType.prototype.define called on non-StructType, got ctypes.int32_t");
 
   let p = Object.getPrototypeOf(ctypes.StructType("a", [ { "x": ctypes.int32_t, } ])());
   let o = {};
   Object.setPrototypeOf(o, p);
   assertTypeErrorMessage(() => { let a = o.x; },
                          "StructType property getter called on incompatible object, got <<error converting value to string>>");
@@ -17,15 +17,15 @@ function test() {
   o = ctypes.int32_t(0);
   Object.setPrototypeOf(o, p);
   assertTypeErrorMessage(() => { let a = o.x; },
                          "StructType property getter called on non-StructType CData, got ctypes.int32_t(0)");
   assertTypeErrorMessage(() => { o.x = 1; },
                          "StructType property setter called on non-StructType CData, got ctypes.int32_t(0)");
 
   assertTypeErrorMessage(() => { ctypes.StructType("a", [])().addressOfField.call(1); },
-                         "StructType.prototype.addressOfField called on incompatible object, got the object (new Number(1))");
+                         "StructType.prototype.addressOfField called on incompatible object, got the number 1");
   assertTypeErrorMessage(() => { ctypes.StructType("a", [])().addressOfField.call(ctypes.int32_t(0)); },
                          "StructType.prototype.addressOfField called on non-StructType CData, got ctypes.int32_t(0)");
 }
 
 if (typeof ctypes === "object")
   test();
deleted file mode 100644
--- a/js/src/jit-test/tests/parser/bug-1433013.js
+++ /dev/null
@@ -1,6 +0,0 @@
-if (helperThreadCount() === 0)
-    quit();
-
-offThreadCompileScript("");
-evalInCooperativeThread("");
-runOffThreadScript();
deleted file mode 100644
--- a/js/src/jit-test/tests/profiler/bug1352507-2.js
+++ /dev/null
@@ -1,3 +0,0 @@
-if (helperThreadCount() === 0)
-    quit();
-evalInCooperativeThread("enableGeckoProfiling()");
deleted file mode 100644
--- a/js/src/jit-test/tests/promise/cooperative-thread.js
+++ /dev/null
@@ -1,8 +0,0 @@
-if (helperThreadCount() === 0)
-    quit();
-
-evalInCooperativeThread(`
-    (new Promise(function(resolve, reject) { resolve(); })).then(() => {});
-
-    drainJobQueue();
-    `);
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -548,20 +548,18 @@ BaselineCompiler::emitStackCheck(bool ea
     Label forceCall;
     if (!earlyCheck && needsEarlyStackCheck()) {
         masm.branchTest32(Assembler::NonZero,
                           frame.addressOfFlags(),
                           Imm32(BaselineFrame::OVER_RECURSED),
                           &forceCall);
     }
 
-    void* contextAddr = cx->zone()->group()->addressOfOwnerContext();
-    masm.loadPtr(AbsoluteAddress(contextAddr), R0.scratchReg());
     masm.branchPtr(Assembler::BelowOrEqual,
-                   Address(R0.scratchReg(), offsetof(JSContext, jitStackLimit)), R1.scratchReg(),
+                   AbsoluteAddress(cx->addressOfJitStackLimit()), R1.scratchReg(),
                    &skipCall);
 
     if (!earlyCheck && needsEarlyStackCheck())
         masm.bind(&forceCall);
 
     prepareVMCall();
     pushArg(Imm32(earlyCheck));
     pushArg(Imm32(tolerance));
@@ -697,20 +695,18 @@ static const VMFunction InterruptCheckIn
     FunctionInfo<InterruptCheckFn>(InterruptCheck, "InterruptCheck");
 
 bool
 BaselineCompiler::emitInterruptCheck()
 {
     frame.syncStack(0);
 
     Label done;
-    void* context = cx->zone()->group()->addressOfOwnerContext();
-    masm.loadPtr(AbsoluteAddress(context), R0.scratchReg());
     masm.branch32(Assembler::Equal,
-                  Address(R0.scratchReg(), offsetof(JSContext, interrupt_)), Imm32(0),
+                  AbsoluteAddress(cx->addressOfInterrupt()), Imm32(0),
                   &done);
 
     prepareVMCall();
     if (!callVM(InterruptCheckInfo))
         return false;
 
     masm.bind(&done);
     return true;
--- a/js/src/jit/BaselineDebugModeOSR.cpp
+++ b/js/src/jit/BaselineDebugModeOSR.cpp
@@ -339,17 +339,17 @@ static void
 SpewPatchStubFrame(ICStub* oldStub, ICStub* newStub)
 {
     JitSpew(JitSpew_BaselineDebugModeOSR,
             "Patch   stub %p -> %p on BaselineStub frame (%s)",
             oldStub, newStub, newStub ? ICStub::KindString(newStub->kind()) : "exception handler");
 }
 
 static void
-PatchBaselineFramesForDebugMode(JSContext* cx, const CooperatingContext& target,
+PatchBaselineFramesForDebugMode(JSContext* cx,
                                 const Debugger::ExecutionObservableSet& obs,
                                 const ActivationIterator& activation,
                                 DebugModeOSREntryVector& entries, size_t* start)
 {
     //
     // Recompile Patching Overview
     //
     // When toggling debug mode with live baseline scripts on the stack, we
@@ -415,17 +415,17 @@ PatchBaselineFramesForDebugMode(JSContex
                 // the baseline frame here, we resume right after the IC
                 // returns.
                 //
                 // Since we're using the same IC stub code, we can resume
                 // directly to the IC resume address.
                 uint8_t* retAddr = bl->returnAddressForIC(bl->icEntryFromPCOffset(pcOffset));
                 SpewPatchBaselineFrame(prev->returnAddress(), retAddr, script, kind, pc);
                 DebugModeOSRVolatileJitFrameIter::forwardLiveIterators(
-                    target, prev->returnAddress(), retAddr);
+                    cx, prev->returnAddress(), retAddr);
                 prev->setReturnAddress(retAddr);
                 entryIndex++;
                 break;
             }
 
             if (kind == ICEntry::Kind_Invalid) {
                 // Case H above.
                 //
@@ -444,17 +444,17 @@ PatchBaselineFramesForDebugMode(JSContex
                 uint8_t* retAddr;
                 if (cx->runtime()->geckoProfiler().enabled())
                     retAddr = bl->nativeCodeForPC(script, pc);
                 else
                     retAddr = nullptr;
                 SpewPatchBaselineFrameFromExceptionHandler(prev->returnAddress(), retAddr,
                                                            script, pc);
                 DebugModeOSRVolatileJitFrameIter::forwardLiveIterators(
-                    target, prev->returnAddress(), retAddr);
+                    cx, prev->returnAddress(), retAddr);
                 prev->setReturnAddress(retAddr);
                 entryIndex++;
                 break;
             }
 
             // Case F above.
             //
             // We undo a previous recompile by handling cases B, C, D, E, I or J
@@ -845,25 +845,23 @@ bool
 jit::RecompileOnStackBaselineScriptsForDebugMode(JSContext* cx,
                                                  const Debugger::ExecutionObservableSet& obs,
                                                  Debugger::IsObserving observing)
 {
     // First recompile the active scripts on the stack and patch the live
     // frames.
     Vector<DebugModeOSREntry> entries(cx);
 
-    for (const CooperatingContext& target : cx->runtime()->cooperatingContexts()) {
-        for (ActivationIterator iter(cx, target); !iter.done(); ++iter) {
-            if (iter->isJit()) {
-                if (!CollectJitStackScripts(cx, obs, iter, entries))
-                    return false;
-            } else if (iter->isInterpreter()) {
-                if (!CollectInterpreterStackScripts(cx, obs, iter, entries))
-                    return false;
-            }
+    for (ActivationIterator iter(cx); !iter.done(); ++iter) {
+        if (iter->isJit()) {
+            if (!CollectJitStackScripts(cx, obs, iter, entries))
+                return false;
+        } else if (iter->isInterpreter()) {
+            if (!CollectInterpreterStackScripts(cx, obs, iter, entries))
+                return false;
         }
     }
 
     if (entries.empty())
         return true;
 
     // When the profiler is enabled, we need to have suppressed sampling,
     // since the basline jit scripts are in a state of flux.
@@ -902,23 +900,21 @@ jit::RecompileOnStackBaselineScriptsForD
 
     for (UniqueScriptOSREntryIter iter(entries); !iter.done(); ++iter) {
         const DebugModeOSREntry& entry = iter.entry();
         if (entry.recompiled())
             BaselineScript::Destroy(cx->runtime()->defaultFreeOp(), entry.oldBaselineScript);
     }
 
     size_t processed = 0;
-    for (const CooperatingContext& target : cx->runtime()->cooperatingContexts()) {
-        for (ActivationIterator iter(cx, target); !iter.done(); ++iter) {
-            if (iter->isJit())
-                PatchBaselineFramesForDebugMode(cx, target, obs, iter, entries, &processed);
-            else if (iter->isInterpreter())
-                SkipInterpreterFrameEntries(obs, iter, &processed);
-        }
+    for (ActivationIterator iter(cx); !iter.done(); ++iter) {
+        if (iter->isJit())
+            PatchBaselineFramesForDebugMode(cx, obs, iter, entries, &processed);
+        else if (iter->isInterpreter())
+            SkipInterpreterFrameEntries(obs, iter, &processed);
     }
     MOZ_ASSERT(processed == entries.length());
 
     return true;
 }
 
 void
 BaselineDebugModeOSRInfo::popValueInto(PCMappingSlotInfo::SlotLocation loc, Value* vp)
@@ -1177,15 +1173,15 @@ JitRuntime::generateBaselineDebugModeOSR
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "BaselineDebugModeOSRHandler");
 #endif
 
     return code;
 }
 
 /* static */ void
-DebugModeOSRVolatileJitFrameIter::forwardLiveIterators(const CooperatingContext& cx,
+DebugModeOSRVolatileJitFrameIter::forwardLiveIterators(JSContext* cx,
                                                        uint8_t* oldAddr, uint8_t* newAddr)
 {
     DebugModeOSRVolatileJitFrameIter* iter;
-    for (iter = cx.context()->liveVolatileJitFrameIter_; iter; iter = iter->prev)
+    for (iter = cx->liveVolatileJitFrameIter_; iter; iter = iter->prev)
         iter->asJSJit().exchangeReturnAddressIfMatch(oldAddr, newAddr);
 }
--- a/js/src/jit/BaselineDebugModeOSR.h
+++ b/js/src/jit/BaselineDebugModeOSR.h
@@ -100,17 +100,17 @@ class DebugModeOSRVolatileJitFrameIter :
         *stack = this;
     }
 
     ~DebugModeOSRVolatileJitFrameIter() {
         MOZ_ASSERT(*stack == this);
         *stack = prev;
     }
 
-    static void forwardLiveIterators(const CooperatingContext& target,
+    static void forwardLiveIterators(JSContext* cx,
                                      uint8_t* oldAddr, uint8_t* newAddr);
 };
 
 //
 // Auxiliary info to help the DebugModeOSRHandler fix up state.
 //
 struct BaselineDebugModeOSRInfo
 {
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -1213,15 +1213,13 @@ MarkActiveBaselineScripts(JSContext* cx,
 }
 
 void
 jit::MarkActiveBaselineScripts(Zone* zone)
 {
     if (zone->isAtomsZone())
         return;
     JSContext* cx = TlsContext.get();
-    for (const CooperatingContext& target : cx->runtime()->cooperatingContexts()) {
-        for (JitActivationIterator iter(cx, target); !iter.done(); ++iter) {
-            if (iter->compartment()->zone() == zone)
-                MarkActiveBaselineScripts(cx, iter);
-        }
+    for (JitActivationIterator iter(cx); !iter.done(); ++iter) {
+        if (iter->compartment()->zone() == zone)
+            MarkActiveBaselineScripts(cx, iter);
     }
 }
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -5257,23 +5257,19 @@ CodeGenerator::visitCheckOverRecursed(LC
     // C functions may then violate the limit without any checking.
     //
     // Since Ion frames exist on the C stack, the stack limit may be
     // dynamically set by JS_SetThreadStackLimit() and JS_SetNativeStackQuota().
 
     CheckOverRecursedFailure* ool = new(alloc()) CheckOverRecursedFailure(lir);
     addOutOfLineCode(ool, lir->mir());
 
-    Register temp = ToRegister(lir->temp());
-
     // Conditional forward (unlikely) branch to failure.
-    const void* contextAddr = gen->compartment->zone()->addressOfJSContext();
-    masm.loadPtr(AbsoluteAddress(contextAddr), temp);
-    masm.branchStackPtrRhs(Assembler::AboveOrEqual,
-                           Address(temp, offsetof(JSContext, jitStackLimit)), ool->entry());
+    const void* limitAddr = gen->runtime->addressOfJitStackLimit();
+    masm.branchStackPtrRhs(Assembler::AboveOrEqual, AbsoluteAddress(limitAddr), ool->entry());
     masm.bind(ool->rejoin());
 }
 
 typedef bool (*DefVarFn)(JSContext*, HandlePropertyName, unsigned, HandleObject);
 static const VMFunction DefVarInfo = FunctionInfo<DefVarFn>(DefVar, "DefVar");
 
 void
 CodeGenerator::visitDefVar(LDefVar* lir)
@@ -12912,22 +12908,18 @@ CodeGenerator::visitInterruptCheck(LInte
 
         lir->setOolEntry(ool->entry());
         masm.bind(ool->rejoin());
         return;
     }
 
     OutOfLineCode* ool = oolCallVM(InterruptCheckInfo, lir, ArgList(), StoreNothing());
 
-    Register temp = ToRegister(lir->temp());
-
-    const void* contextAddr = gen->compartment->zone()->addressOfJSContext();
-    masm.loadPtr(AbsoluteAddress(contextAddr), temp);
-    masm.branch32(Assembler::NotEqual, Address(temp, offsetof(JSContext, interrupt_)),
-                  Imm32(0), ool->entry());
+    const void* interruptAddr = gen->runtime->addressOfInterrupt();
+    masm.branch32(Assembler::NotEqual, AbsoluteAddress(interruptAddr), Imm32(0), ool->entry());
     masm.bind(ool->rejoin());
 }
 
 void
 CodeGenerator::visitWasmInterruptCheck(LWasmInterruptCheck* lir)
 {
     MOZ_ASSERT(gen->compilingWasm());
 
--- a/js/src/jit/CompileWrappers.cpp
+++ b/js/src/jit/CompileWrappers.cpp
@@ -97,19 +97,31 @@ CompileRuntime::positiveInfinityValue()
 
 const WellKnownSymbols&
 CompileRuntime::wellKnownSymbols()
 {
     return *runtime()->wellKnownSymbols;
 }
 
 const void*
-CompileRuntime::addressOfActiveJSContext()
+CompileRuntime::mainContextPtr()
+{
+    return runtime()->mainContextFromAnyThread();
+}
+
+const void*
+CompileRuntime::addressOfJitStackLimit()
 {
-    return runtime()->addressOfActiveContext();
+    return runtime()->mainContextFromAnyThread()->addressOfJitStackLimit();
+}
+
+const void*
+CompileRuntime::addressOfInterrupt()
+{
+    return runtime()->mainContextFromAnyThread()->addressOfInterrupt();
 }
 
 #ifdef DEBUG
 bool
 CompileRuntime::isInsideNursery(gc::Cell* cell)
 {
     return UninlinedIsInsideNursery(cell);
 }
@@ -155,22 +167,16 @@ CompileZone::isAtomsZone()
 const void*
 CompileZone::addressOfIonBailAfter()
 {
     return zone()->group()->addressOfIonBailAfter();
 }
 #endif
 
 const void*
-CompileZone::addressOfJSContext()
-{
-    return zone()->group()->addressOfOwnerContext();
-}
-
-const void*
 CompileZone::addressOfNeedsIncrementalBarrier()
 {
     return zone()->addressOfNeedsIncrementalBarrier();
 }
 
 const void*
 CompileZone::addressOfFreeList(gc::AllocKind allocKind)
 {
--- a/js/src/jit/CompileWrappers.h
+++ b/js/src/jit/CompileWrappers.h
@@ -41,17 +41,20 @@ class CompileRuntime
     bool profilingScripts();
 
     const JSAtomState& names();
     const PropertyName* emptyString();
     const StaticStrings& staticStrings();
     const Value& NaNValue();
     const Value& positiveInfinityValue();
     const WellKnownSymbols& wellKnownSymbols();
-    const void* addressOfActiveJSContext();
+
+    const void* mainContextPtr();
+    const void* addressOfJitStackLimit();
+    const void* addressOfInterrupt();
 
 #ifdef DEBUG
     bool isInsideNursery(gc::Cell* cell);
 #endif
 
     // DOM callbacks must be threadsafe (and will hopefully be removed soon).
     const DOMCallbacks* DOMcallbacks();
 
@@ -67,17 +70,16 @@ class CompileZone
 
     CompileRuntime* runtime();
     bool isAtomsZone();
 
 #ifdef DEBUG
     const void* addressOfIonBailAfter();
 #endif
 
-    const void* addressOfJSContext();
     const void* addressOfNeedsIncrementalBarrier();
     const void* addressOfFreeList(gc::AllocKind allocKind);
     const void* addressOfNurseryPosition();
     const void* addressOfStringNurseryPosition();
     const void* addressOfNurseryCurrentEnd();
     const void* addressOfStringNurseryCurrentEnd();
 
     bool nurseryExists();
--- a/js/src/jit/ExecutableAllocator.cpp
+++ b/js/src/jit/ExecutableAllocator.cpp
@@ -300,17 +300,17 @@ ExecutableAllocator::reprotectAll(Protec
         reprotectPool(rt_, r.front(), protection);
 }
 
 /* static */ void
 ExecutableAllocator::reprotectPool(JSRuntime* rt, ExecutablePool* pool, ProtectionSetting protection)
 {
     // Don't race with reprotectAll called from the signal handler.
     MOZ_ASSERT(rt->jitRuntime()->preventBackedgePatching() ||
-               rt->activeContext()->handlingJitInterrupt());
+               rt->mainContextFromAnyThread()->handlingJitInterrupt());
 
     char* start = pool->m_allocation.pages;
     if (!ReprotectRegion(start, pool->m_freePtr - start, protection))
         MOZ_CRASH();
 }
 
 /* static */ void
 ExecutableAllocator::poisonCode(JSRuntime* rt, JitPoisonRangeVector& ranges)
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -2939,22 +2939,20 @@ jit::InvalidateAll(FreeOp* fop, Zone* zo
     // The caller should previously have cancelled off thread compilation.
 #ifdef DEBUG
     for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next())
         MOZ_ASSERT(!HasOffThreadIonCompile(comp));
 #endif
     if (zone->isAtomsZone())
         return;
     JSContext* cx = TlsContext.get();
-    for (const CooperatingContext& target : cx->runtime()->cooperatingContexts()) {
-        for (JitActivationIterator iter(cx, target); !iter.done(); ++iter) {
-            if (iter->compartment()->zone() == zone) {
-                JitSpew(JitSpew_IonInvalidate, "Invalidating all frames for GC");
-                InvalidateActivation(fop, iter, true);
-            }
+    for (JitActivationIterator iter(cx); !iter.done(); ++iter) {
+        if (iter->compartment()->zone() == zone) {
+            JitSpew(JitSpew_IonInvalidate, "Invalidating all frames for GC");
+            InvalidateActivation(fop, iter, true);
         }
     }
 }
 
 
 void
 jit::Invalidate(TypeZone& types, FreeOp* fop,
                 const RecompileInfoVector& invalid, bool resetUses,
@@ -2987,28 +2985,19 @@ jit::Invalidate(TypeZone& types, FreeOp*
         numInvalidations++;
     }
 
     if (!numInvalidations) {
         JitSpew(JitSpew_IonInvalidate, " No IonScript invalidation.");
         return;
     }
 
-    // This method can be called both during GC and during the course of normal
-    // script execution. In the former case this class will already be on the
-    // stack, and in the latter case the invalidations will all be on the
-    // current thread's stack, but the assertion under ActivationIterator can't
-    // tell that this is a thread local use of the iterator.
-    JSRuntime::AutoProhibitActiveContextChange apacc(fop->runtime());
-
     JSContext* cx = TlsContext.get();
-    for (const CooperatingContext& target : cx->runtime()->cooperatingContexts()) {
-        for (JitActivationIterator iter(cx, target); !iter.done(); ++iter)
-            InvalidateActivation(fop, iter, false);
-    }
+    for (JitActivationIterator iter(cx); !iter.done(); ++iter)
+        InvalidateActivation(fop, iter, false);
 
     // Drop the references added above. If a script was never active, its
     // IonScript will be immediately destroyed. Otherwise, it will be held live
     // until its last invalidated frame is destroyed.
     for (size_t i = 0; i < invalid.length(); i++) {
         CompilerOutput* co = invalid[i].compilerOutput(types);
         if (!co)
             continue;
@@ -3350,19 +3339,18 @@ jit::JitSupportsAtomics()
 
 // If you change these, please also change the comment in TempAllocator.
 /* static */ const size_t TempAllocator::BallastSize            = 16 * 1024;
 /* static */ const size_t TempAllocator::PreferredLifoChunkSize = 32 * 1024;
 
 static void
 RedirectIonBackedgesToInterruptCheck(JSContext* cx)
 {
-    // Jitcode may only be modified on the runtime's active thread.
-    if (cx != cx->runtime()->activeContext())
-        return;
+    // Jitcode may only be modified on the runtime's main thread.
+    MOZ_ASSERT(cx == cx->runtime()->mainContextFromAnyThread());
 
     // The faulting thread is suspended so we can access cx fields that can
     // normally only be accessed by the cx's active thread.
     AutoNoteSingleThreadedRegion anstr;
 
     Zone* zone = cx->zoneRaw();
     if (zone && !zone->isAtomsZone()) {
         jit::JitRuntime* jitRuntime = cx->runtime()->jitRuntime();
--- a/js/src/jit/JitFrames.cpp
+++ b/js/src/jit/JitFrames.cpp
@@ -1296,33 +1296,31 @@ TraceJitActivation(JSTracer* trc, JitAct
         } else {
             MOZ_ASSERT(frames.isWasm());
             frames.asWasm().instance()->trace(trc);
         }
     }
 }
 
 void
-TraceJitActivations(JSContext* cx, const CooperatingContext& target, JSTracer* trc)
+TraceJitActivations(JSContext* cx, JSTracer* trc)
 {
-    for (JitActivationIterator activations(cx, target); !activations.done(); ++activations)
+    for (JitActivationIterator activations(cx); !activations.done(); ++activations)
         TraceJitActivation(trc, activations->asJit());
 }
 
 void
 UpdateJitActivationsForMinorGC(JSRuntime* rt)
 {
     MOZ_ASSERT(JS::CurrentThreadIsHeapMinorCollecting());
     JSContext* cx = TlsContext.get();
-    for (const CooperatingContext& target : rt->cooperatingContexts()) {
-        for (JitActivationIterator activations(cx, target); !activations.done(); ++activations) {
-            for (OnlyJSJitFrameIter iter(activations); !iter.done(); ++iter) {
-                if (iter.frame().type() == JitFrame_IonJS)
-                    UpdateIonJSFrameForMinorGC(iter.frame());
-            }
+    for (JitActivationIterator activations(cx); !activations.done(); ++activations) {
+        for (OnlyJSJitFrameIter iter(activations); !iter.done(); ++iter) {
+            if (iter.frame().type() == JitFrame_IonJS)
+                UpdateIonJSFrameForMinorGC(iter.frame());
         }
     }
 }
 
 void
 GetPcScript(JSContext* cx, JSScript** scriptRes, jsbytecode** pcRes)
 {
     JitSpew(JitSpew_IonSnapshots, "Recover PC & Script from the last frame.");
--- a/js/src/jit/JitFrames.h
+++ b/js/src/jit/JitFrames.h
@@ -280,17 +280,17 @@ struct ResumeFromException
 
     BaselineBailoutInfo* bailoutInfo;
 };
 
 void HandleException(ResumeFromException* rfe);
 
 void EnsureBareExitFrame(JitActivation* act, JitFrameLayout* frame);
 
-void TraceJitActivations(JSContext* cx, const CooperatingContext& target, JSTracer* trc);
+void TraceJitActivations(JSContext* cx, JSTracer* trc);
 
 void UpdateJitActivationsForMinorGC(JSRuntime* rt);
 
 static inline uint32_t
 EncodeFrameHeaderSize(size_t headerSize)
 {
     MOZ_ASSERT((headerSize % sizeof(uintptr_t)) == 0);
 
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -179,17 +179,17 @@ LIRGenerator::visitTableSwitch(MTableSwi
         tempInt = temp(LDefinition::GENERAL);
     }
     add(newLTableSwitch(index, tempInt, tableswitch));
 }
 
 void
 LIRGenerator::visitCheckOverRecursed(MCheckOverRecursed* ins)
 {
-    LCheckOverRecursed* lir = new(alloc()) LCheckOverRecursed(temp());
+    LCheckOverRecursed* lir = new(alloc()) LCheckOverRecursed();
     add(lir, ins);
     assignSafepoint(lir, ins);
 }
 
 void
 LIRGenerator::visitDefVar(MDefVar* ins)
 {
     LDefVar* lir = new(alloc()) LDefVar(useRegisterAtStart(ins->environmentChain()));
@@ -2728,17 +2728,17 @@ LIRGenerator::visitHomeObjectSuperBase(M
     auto lir = new(alloc()) LHomeObjectSuperBase(useRegister(ins->homeObject()));
     define(lir, ins);
     assignSafepoint(lir, ins);
 }
 
 void
 LIRGenerator::visitInterruptCheck(MInterruptCheck* ins)
 {
-    LInstruction* lir = new(alloc()) LInterruptCheck(temp());
+    LInstruction* lir = new(alloc()) LInterruptCheck();
     add(lir, ins);
     assignSafepoint(lir, ins);
 }
 
 void
 LIRGenerator::visitWasmInterruptCheck(MWasmInterruptCheck* ins)
 {
     auto* lir = new(alloc()) LWasmInterruptCheck(useRegisterAtStart(ins->tlsPtr()));
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -1773,29 +1773,17 @@ MacroAssembler::typeOfObject(Register ob
 
     jump(isCallable);
 }
 
 void
 MacroAssembler::loadJSContext(Register dest)
 {
     JitContext* jcx = GetJitContext();
-    CompileCompartment* compartment = jcx->compartment;
-    if (compartment->zone()->isAtomsZone()) {
-        // If we are in the atoms zone then we are generating a runtime wide
-        // trampoline which can run in any zone. Load the context which is
-        // currently running using cooperative scheduling in the runtime.
-        // (This will need to be fixed when we have preemptive scheduling,
-        // bug 1323066).
-        loadPtr(AbsoluteAddress(jcx->runtime->addressOfActiveJSContext()), dest);
-    } else {
-        // If we are in a specific zone then the current context will be stored
-        // in the containing zone group.
-        loadPtr(AbsoluteAddress(compartment->zone()->addressOfJSContext()), dest);
-    }
+    movePtr(ImmPtr(jcx->runtime->mainContextPtr()), dest);
 }
 
 void
 MacroAssembler::guardGroupHasUnanalyzedNewScript(Register group, Register scratch, Label* fail)
 {
     Label noNewScript;
     load32(Address(group, ObjectGroup::offsetOfFlags()), scratch);
     and32(Imm32(OBJECT_FLAG_ADDENDUM_MASK), scratch);
--- a/js/src/jit/arm/Assembler-arm.cpp
+++ b/js/src/jit/arm/Assembler-arm.cpp
@@ -710,17 +710,17 @@ Assembler::PatchableJumpAddress(JitCode*
 
 class RelocationIterator
 {
     CompactBufferReader reader_;
     // Offset in bytes.
     uint32_t offset_;
 
   public:
-    RelocationIterator(CompactBufferReader& reader)
+    explicit RelocationIterator(CompactBufferReader& reader)
       : reader_(reader)
     { }
 
     bool read() {
         if (!reader_.more())
             return false;
         offset_ = reader_.readUnsigned();
         return true;
@@ -2817,17 +2817,17 @@ struct PoolHeader : Instruction
         uint32_t ONES : 16;
 
         Header(int size_, bool isNatural_)
           : size(size_),
             isNatural(isNatural_),
             ONES(0xffff)
         { }
 
-        Header(const Instruction* i) {
+        explicit Header(const Instruction* i) {
             JS_STATIC_ASSERT(sizeof(Header) == sizeof(uint32_t));
             memcpy(this, i, sizeof(Header));
             MOZ_ASSERT(ONES == 0xffff);
         }
 
         uint32_t raw() const {
             JS_STATIC_ASSERT(sizeof(Header) == sizeof(uint32_t));
             uint32_t dest;
--- a/js/src/jit/arm/MoveEmitter-arm.h
+++ b/js/src/jit/arm/MoveEmitter-arm.h
@@ -45,17 +45,17 @@ class MoveEmitterARM
     void emitDoubleMove(const MoveOperand& from, const MoveOperand& to);
     void breakCycle(const MoveOperand& from, const MoveOperand& to,
                     MoveOp::Type type, uint32_t slot);
     void completeCycle(const MoveOperand& from, const MoveOperand& to,
                        MoveOp::Type type, uint32_t slot);
     void emit(const MoveOp& move);
 
   public:
-    MoveEmitterARM(MacroAssembler& masm);
+    explicit MoveEmitterARM(MacroAssembler& masm);
     ~MoveEmitterARM();
     void emit(const MoveResolver& moves);
     void finish();
 
     void setScratchRegister(Register reg) {}
 };
 
 typedef MoveEmitterARM MoveEmitter;
--- a/js/src/jit/arm64/MacroAssembler-arm64-inl.h
+++ b/js/src/jit/arm64/MacroAssembler-arm64-inl.h
@@ -2113,16 +2113,28 @@ MacroAssemblerCompat::branchStackPtrRhs(
     const ARMRegister scratch = temps.AcquireX();
     Ldr(scratch, toMemOperand(lhs));
     // Cmp disallows SP as the rhs, so flip the operands and invert the
     // condition.
     Cmp(GetStackPointer64(), scratch);
     B(label, Assembler::InvertCondition(cond));
 }
 
+void
+MacroAssemblerCompat::branchStackPtrRhs(Condition cond, AbsoluteAddress lhs, Label* label)
+{
+    vixl::UseScratchRegisterScope temps(this);
+    const ARMRegister scratch = temps.AcquireX();
+    movePtr(ImmPtr(lhs.addr), scratch.asUnsized());
+    // Cmp disallows SP as the rhs, so flip the operands and invert the
+    // condition.
+    Cmp(GetStackPointer64(), scratch);
+    B(label, Assembler::InvertCondition(cond));
+}
+
 // If source is a double, load into dest.
 // If source is int32, convert to double and store in dest.
 // Else, branch to failure.
 void
 MacroAssemblerCompat::ensureDouble(const ValueOperand& source, FloatRegister dest, Label* failure)
 {
     Label isDouble, done;
 
--- a/js/src/jit/arm64/MacroAssembler-arm64.h
+++ b/js/src/jit/arm64/MacroAssembler-arm64.h
@@ -981,16 +981,17 @@ class MacroAssemblerCompat : public vixl
 
     inline void loadStackPtr(const Address& src);
     inline void storeStackPtr(const Address& dest);
 
     // StackPointer testing functions.
     inline void branchTestStackPtr(Condition cond, Imm32 rhs, Label* label);
     inline void branchStackPtr(Condition cond, Register rhs, Label* label);
     inline void branchStackPtrRhs(Condition cond, Address lhs, Label* label);
+    inline void branchStackPtrRhs(Condition cond, AbsoluteAddress lhs, Label* label);
 
     void testPtr(Register lhs, Register rhs) {
         Tst(ARMRegister(lhs, 64), Operand(ARMRegister(rhs, 64)));
     }
     void test32(Register lhs, Register rhs) {
         Tst(ARMRegister(lhs, 32), Operand(ARMRegister(rhs, 32)));
     }
     void test32(const Address& addr, Imm32 imm) {
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -1476,30 +1476,24 @@ class LInitPropGetterSetter : public LCa
         return getOperand(1);
     }
 
     MInitPropGetterSetter* mir() const {
         return mir_->toInitPropGetterSetter();
     }
 };
 
-class LCheckOverRecursed : public LInstructionHelper<0, 0, 1>
+class LCheckOverRecursed : public LInstructionHelper<0, 0, 0>
 {
   public:
     LIR_HEADER(CheckOverRecursed)
 
-    explicit LCheckOverRecursed(const LDefinition& temp)
-      : LInstructionHelper(classOpcode)
-    {
-        setTemp(0, temp);
-    }
-
-    const LDefinition* temp() {
-        return getTemp(0);
-    }
+    LCheckOverRecursed()
+      : LInstructionHelper(classOpcode)
+    {}
 
     MCheckOverRecursed* mir() const {
         return mir_->toCheckOverRecursed();
     }
 };
 
 class LWasmTrap : public LInstructionHelper<0, 0, 0>
 {
@@ -1612,60 +1606,53 @@ class LRotateI64 : public details::Rotat
     static const size_t Input = 0;
     static const size_t Count = INT64_PIECES;
 
     const LInt64Allocation input() { return getInt64Operand(Input); }
     const LDefinition* temp() { return getTemp(0); }
     LAllocation* count() { return getOperand(Count); }
 };
 
-class LInterruptCheck : public LInstructionHelper<0, 0, 1>
+class LInterruptCheck : public LInstructionHelper<0, 0, 0>
 {
     Label* oolEntry_;
 
     // Whether this is an implicit interrupt check. Implicit interrupt checks
     // use a patchable backedge and signal handlers instead of an explicit
     // cx->interrupt check.
     bool implicit_;
 
   public:
     LIR_HEADER(InterruptCheck)
 
-    explicit LInterruptCheck(const LDefinition& temp)
+    LInterruptCheck()
       : LInstructionHelper(classOpcode),
         oolEntry_(nullptr),
         implicit_(false)
-    {
-        setTemp(0, temp);
-    }
+    {}
 
     Label* oolEntry() {
         MOZ_ASSERT(implicit_);
         return oolEntry_;
     }
 
     void setOolEntry(Label* oolEntry) {
         MOZ_ASSERT(implicit_);
         oolEntry_ = oolEntry;
     }
     MInterruptCheck* mir() const {
         return mir_->toInterruptCheck();
     }
 
     void setImplicit() {
         implicit_ = true;
-        setTemp(0, LDefinition::BogusTemp());
     }
     bool implicit() const {
         return implicit_;
     }
-
-    const LDefinition* temp() {
-        return getTemp(0);
-    }
 };
 
 class LWasmInterruptCheck : public LInstructionHelper<0, 1, 0>
 {
   public:
     LIR_HEADER(WasmInterruptCheck)
 
     explicit LWasmInterruptCheck(const LAllocation& tlsData)
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -480,29 +480,29 @@ JS_NewContext(uint32_t maxbytes, uint32_
         parentRuntime = parentRuntime->parentRuntime;
 
     return NewContext(maxbytes, maxNurseryBytes, parentRuntime);
 }
 
 JS_PUBLIC_API(JSContext*)
 JS_NewCooperativeContext(JSContext* siblingContext)
 {
-    return NewCooperativeContext(siblingContext);
+    MOZ_CRASH("Cooperative scheduling is unsupported");
 }
 
 JS_PUBLIC_API(void)
 JS_YieldCooperativeContext(JSContext* cx)
 {
-    YieldCooperativeContext(cx);
+    MOZ_CRASH("Cooperative scheduling is unsupported");
 }
 
 JS_PUBLIC_API(void)
 JS_ResumeCooperativeContext(JSContext* cx)
 {
-    ResumeCooperativeContext(cx);
+    MOZ_CRASH("Cooperative scheduling is unsupported");
 }
 
 JS_PUBLIC_API(void)
 JS_DestroyContext(JSContext* cx)
 {
     DestroyContext(cx);
 }
 
@@ -574,25 +574,16 @@ JS_GetParentRuntime(JSContext* cx)
 }
 
 JS_PUBLIC_API(JSRuntime*)
 JS_GetRuntime(JSContext* cx)
 {
     return cx->runtime();
 }
 
-JS_PUBLIC_API(void)
-JS::SetSingleThreadedExecutionCallbacks(JSContext* cx,
-                                        BeginSingleThreadedExecutionCallback begin,
-                                        EndSingleThreadedExecutionCallback end)
-{
-    cx->runtime()->beginSingleThreadedExecutionCallback = begin;
-    cx->runtime()->endSingleThreadedExecutionCallback = end;
-}
-
 JS_PUBLIC_API(JS::ContextOptions&)
 JS::ContextOptionsRef(JSContext* cx)
 {
     return cx->options();
 }
 
 JS_PUBLIC_API(bool)
 JS::InitSelfHostedCode(JSContext* cx)
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -860,33 +860,16 @@ JS_ResumeCooperativeContext(JSContext* c
 
 // Create a new context on this thread for cooperative multithreading in the
 // same runtime as siblingContext. Called on a runtime (as indicated by
 // siblingContet) which has no active context, on success the new context will
 // become the runtime's active context.
 extern JS_PUBLIC_API(JSContext*)
 JS_NewCooperativeContext(JSContext* siblingContext);
 
-namespace JS {
-
-// Class to relinquish exclusive access to all zone groups in use by this
-// thread. This allows other cooperative threads to enter the zone groups
-// and modify their contents.
-struct AutoRelinquishZoneGroups
-{
-    explicit AutoRelinquishZoneGroups(JSContext* cx);
-    ~AutoRelinquishZoneGroups();
-
-  private:
-    JSContext* cx;
-    mozilla::Vector<void*> enterList;
-};
-
-} // namespace JS
-
 // Destroy a context allocated with JS_NewContext or JS_NewCooperativeContext.
 // The context must be the current active context in the runtime, and after
 // this call the runtime will have no active context.
 extern JS_PUBLIC_API(void)
 JS_DestroyContext(JSContext* cx);
 
 JS_PUBLIC_API(void*)
 JS_GetContextPrivate(JSContext* cx);
@@ -904,41 +887,16 @@ extern JS_PUBLIC_API(void)
 JS_BeginRequest(JSContext* cx);
 
 extern JS_PUBLIC_API(void)
 JS_EndRequest(JSContext* cx);
 
 extern JS_PUBLIC_API(void)
 JS_SetFutexCanWait(JSContext* cx);
 
-namespace JS {
-
-// Single threaded execution callbacks are used to notify API clients that a
-// feature is in use on a context's runtime that is not yet compatible with
-// cooperatively multithreaded execution.
-//
-// Between a call to BeginSingleThreadedExecutionCallback and a corresponding
-// call to EndSingleThreadedExecutionCallback, only one thread at a time may
-// enter compartments in the runtime. The begin callback may yield as necessary
-// to permit other threads to finish up what they're doing, while the end
-// callback may not yield or otherwise operate on the runtime (it may be called
-// during GC).
-//
-// These callbacks may be left unspecified for runtimes which only ever have a
-// single context.
-typedef void (*BeginSingleThreadedExecutionCallback)(JSContext* cx);
-typedef void (*EndSingleThreadedExecutionCallback)(JSContext* cx);
-
-extern JS_PUBLIC_API(void)
-SetSingleThreadedExecutionCallbacks(JSContext* cx,
-                                    BeginSingleThreadedExecutionCallback begin,
-                                    EndSingleThreadedExecutionCallback end);
-
-} // namespace JS
-
 namespace js {
 
 void
 AssertHeapIsIdle();
 
 } /* namespace js */
 
 class MOZ_RAII JSAutoRequest
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -1265,17 +1265,17 @@ js::GetAnyCompartmentInZone(JS::Zone* zo
     CompartmentsInZoneIter comp(zone);
     MOZ_ASSERT(!comp.done());
     return comp.get();
 }
 
 void
 JS::ObjectPtr::finalize(JSRuntime* rt)
 {
-    if (IsIncrementalBarrierNeeded(rt->activeContextFromOwnThread()))
+    if (IsIncrementalBarrierNeeded(rt->mainContextFromOwnThread()))
         IncrementalPreWriteBarrier(value);
     value = nullptr;
 }
 
 void
 JS::ObjectPtr::finalize(JSContext* cx)
 {
     finalize(cx->runtime());
@@ -1539,18 +1539,17 @@ JS_FRIEND_API(void)
 js::SetCooperativeYieldCallback(JSContext* cx, YieldCallback callback)
 {
     cx->setYieldCallback(callback);
 }
 
 JS_FRIEND_API(bool)
 js::SystemZoneAvailable(JSContext* cx)
 {
-    CooperatingContext& owner = cx->runtime()->gc.systemZoneGroup->ownerContext();
-    return owner.context() == nullptr;
+    return true;
 }
 
 static LogCtorDtor sLogCtor = nullptr;
 static LogCtorDtor sLogDtor = nullptr;
 
 JS_FRIEND_API(void)
 js::SetLogCtorDtorFunctions(LogCtorDtor ctor, LogCtorDtor dtor)
 {
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -88,18 +88,16 @@ typedef JSConstScalarSpec<double> JSCons
 typedef JSConstScalarSpec<int32_t> JSConstIntegerSpec;
 
 namespace js {
 namespace gc {
 class AutoTraceSession;
 class StoreBuffer;
 } // namespace gc
 
-class CooperatingContext;
-
 inline JSCompartment* GetContextCompartment(const JSContext* cx);
 inline JS::Zone* GetContextZone(const JSContext* cx);
 
 // Whether the current thread is permitted access to any part of the specified
 // runtime or zone.
 JS_FRIEND_API(bool)
 CurrentThreadCanAccessRuntime(const JSRuntime* rt);
 
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -1735,19 +1735,16 @@ ConvertTranscodeResultToJSException(JSCo
 
       case JS::TranscodeResult_Throw:
         MOZ_ASSERT(cx->isExceptionPending());
         return false;
     }
 }
 
 static bool
-CooperativeThreadMayYield(JSContext* cx);
-
-static bool
 Evaluate(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() < 1 || args.length() > 2) {
         JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
                                   args.length() < 1 ? JSSMSG_NOT_ENOUGH_ARGS : JSSMSG_TOO_MANY_ARGS,
                                   "evaluate");
@@ -1822,62 +1819,16 @@ Evaluate(JSContext* cx, unsigned argc, V
             }
             if (!global || !(JS_GetClass(global)->flags & JSCLASS_IS_GLOBAL)) {
                 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
                                           "\"global\" passed to evaluate()", "not a global object");
                 return false;
             }
         }
 
-        if (!JS_GetProperty(cx, opts, "zoneGroup", &v))
-            return false;
-        if (!v.isUndefined()) {
-            if (global != JS_GetGlobalForObject(cx, &args.callee())) {
-                JS_ReportErrorASCII(cx, "zoneGroup and global cannot both be specified.");
-                return false;
-            }
-
-            // Find all eligible globals to execute in.
-            JS::AutoObjectVector eligibleGlobals(cx);
-            for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) {
-                // Compartments without globals and the self hosting global may
-                // not be entered.
-                if (!c->maybeGlobal() || cx->runtime()->isSelfHostingGlobal(c->maybeGlobal()))
-                    continue;
-
-                // Globals in zone groups which are not in use by a cooperative
-                // thread may be entered.
-                if (!c->zone()->group()->ownerContext().context()) {
-                    if (!eligibleGlobals.append(c->maybeGlobal()))
-                        return false;
-                }
-
-                // Globals in zone groups which use exclusive locking may be
-                // entered, in which case this thread will yield until the zone
-                // group is available.
-                if (c->zone()->group()->useExclusiveLocking() && CooperativeThreadMayYield(cx)) {
-                    if (!eligibleGlobals.append(c->maybeGlobal()))
-                        return false;
-                }
-            }
-
-            if (eligibleGlobals.empty()) {
-                JS_ReportErrorASCII(cx, "zoneGroup can only be used if another"
-                                    " cooperative thread has called cooperativeYield(true).");
-                return false;
-            }
-
-            // Pick an eligible global to use based on the value of the zoneGroup property.
-            int32_t which;
-            if (!ToInt32(cx, v, &which))
-                return false;
-            which = Min<int32_t>(Max(which, 0), eligibleGlobals.length() - 1);
-            global = eligibleGlobals[which];
-        }
-
         if (!JS_GetProperty(cx, opts, "catchTermination", &v))
             return false;
         if (!v.isUndefined())
             catchTermination = ToBoolean(v);
 
         if (!JS_GetProperty(cx, opts, "loadBytecode", &v))
             return false;
         if (!v.isUndefined())
@@ -3535,161 +3486,16 @@ EvalInContext(JSContext* cx, unsigned ar
     }
 
     if (!cx->compartment()->wrap(cx, args.rval()))
         return false;
 
     return true;
 }
 
-struct CooperationState
-{
-    CooperationState()
-      : lock(mutexid::ShellThreadCooperation)
-      , idle(false)
-      , numThreads(0)
-      , yieldCount(0)
-      , singleThreaded(false)
-    {}
-
-    Mutex lock;
-    ConditionVariable cvar;
-    bool idle;
-    size_t numThreads;
-    uint64_t yieldCount;
-    bool singleThreaded;
-};
-static CooperationState* cooperationState = nullptr;
-
-static void
-CooperativeBeginWait(JSContext* cx)
-{
-    MOZ_ASSERT(cx == TlsContext.get());
-    JS_YieldCooperativeContext(cx);
-}
-
-static void
-CooperativeEndWait(JSContext* cx)
-{
-    MOZ_ASSERT(cx == TlsContext.get());
-    LockGuard<Mutex> lock(cooperationState->lock);
-
-    cooperationState->cvar.wait(lock, [&] { return cooperationState->idle; });
-
-    JS_ResumeCooperativeContext(cx);
-    cooperationState->idle = false;
-    cooperationState->yieldCount++;
-    cooperationState->cvar.notify_all();
-}
-
-static void
-CooperativeYield(bool terminating = false)
-{
-    LockGuard<Mutex> lock(cooperationState->lock);
-    MOZ_ASSERT(!cooperationState->idle);
-    cooperationState->idle = true;
-    cooperationState->cvar.notify_all();
-
-    // Wait until another thread takes over control before returning, if there
-    // is another thread to do so.
-    if (!terminating && cooperationState->numThreads) {
-        uint64_t count = cooperationState->yieldCount;
-        cooperationState->cvar.wait(lock, [&] { return cooperationState->yieldCount != count; });
-    }
-}
-
-static bool
-CooperativeThreadMayYield(JSContext* cx)
-{
-    if (!cx->runtime()->gc.canChangeActiveContext(cx))
-        return false;
-
-    if (GetShellContext(cx)->isWorker)
-        return false;
-
-    if (cooperationState->singleThreaded)
-        return false;
-
-    // To avoid contention issues between threads, yields are not allowed while
-    // a thread has access to zone groups other than its original one, i.e. if
-    // the thread is inside an evaluate() call with a different zone group.
-    // This is not a limit which the browser has, but is necessary in the
-    // shell: the shell can have arbitrary interleavings between cooperative
-    // threads, whereas the browser has more control over which threads are
-    // running at different times.
-    for (ZoneGroupsIter group(cx->runtime()); !group.done(); group.next()) {
-        if (group->ownerContext().context() == cx && group != cx->zone()->group())
-            return false;
-    }
-
-    return true;
-}
-
-static void
-CooperativeYieldCallback(JSContext* cx)
-{
-    MOZ_ASSERT(CooperativeThreadMayYield(cx));
-    CooperativeBeginWait(cx);
-    CooperativeYield();
-    CooperativeEndWait(cx);
-}
-
-static bool
-CooperativeYieldThread(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-
-    if (!CooperativeThreadMayYield(cx)) {
-        JS_ReportErrorASCII(cx, "Yielding is not currently allowed");
-        return false;
-    }
-
-    {
-        Maybe<JS::AutoRelinquishZoneGroups> artzg;
-        if ((args.length() > 0) && ToBoolean(args[0]))
-            artzg.emplace(cx);
-
-        CooperativeBeginWait(cx);
-        CooperativeYield();
-        CooperativeEndWait(cx);
-    }
-
-    args.rval().setUndefined();
-    return true;
-}
-
-static void
-CooperativeBeginSingleThreadedExecution(JSContext* cx)
-{
-    MOZ_ASSERT(!cooperationState->singleThreaded);
-
-    // Yield until all other threads have exited any zone groups they are in.
-    while (true) {
-        bool done = true;
-        for (ZoneGroupsIter group(cx->runtime()); !group.done(); group.next()) {
-            if (!group->ownedByCurrentThread() && group->ownerContext().context())
-                done = false;
-        }
-        if (done)
-            break;
-        CooperativeBeginWait(cx);
-        CooperativeYield();
-        CooperativeEndWait(cx);
-    }
-
-    cooperationState->singleThreaded = true;
-}
-
-static void
-CooperativeEndSingleThreadedExecution(JSContext* cx)
-{
-    if (cooperationState)
-        cooperationState->singleThreaded = false;
-}
-
 static bool
 EnsureGeckoProfilingStackInstalled(JSContext* cx, ShellContext* sc)
 {
     if (cx->geckoProfiler().installed()) {
         MOZ_ASSERT(sc->geckoProfilingStack);
         return true;
     }
 
@@ -3702,62 +3508,49 @@ EnsureGeckoProfilingStackInstalled(JSCon
 
     SetContextProfilingStack(cx, sc->geckoProfilingStack.get());
     return true;
 }
 
 struct WorkerInput
 {
     JSRuntime* parentRuntime;
-    JSContext* siblingContext;
     char16_t* chars;
     size_t length;
 
     WorkerInput(JSRuntime* parentRuntime, char16_t* chars, size_t length)
-      : parentRuntime(parentRuntime), siblingContext(nullptr), chars(chars), length(length)
-    {}
-
-    WorkerInput(JSContext* siblingContext, char16_t* chars, size_t length)
-      : parentRuntime(nullptr), siblingContext(siblingContext), chars(chars), length(length)
+      : parentRuntime(parentRuntime), chars(chars), length(length)
     {}
 
     ~WorkerInput() {
         js_free(chars);
     }
 };
 
 static void SetWorkerContextOptions(JSContext* cx);
 static bool ShellBuildId(JS::BuildIdCharVector* buildId);
 
 static void
 WorkerMain(void* arg)
 {
     WorkerInput* input = (WorkerInput*) arg;
-    MOZ_ASSERT(!!input->parentRuntime != !!input->siblingContext);
-
-    JSContext* cx = input->parentRuntime
-         ? JS_NewContext(8L * 1024L * 1024L, 2L * 1024L * 1024L, input->parentRuntime)
-         : JS_NewCooperativeContext(input->siblingContext);
+    MOZ_ASSERT(input->parentRuntime);
+
+    JSContext* cx = JS_NewContext(8L * 1024L * 1024L, 2L * 1024L * 1024L, input->parentRuntime);
     if (!cx)
         return;
 
-    SetCooperativeYieldCallback(cx, CooperativeYieldCallback);
-
     ShellContext* sc = js_new<ShellContext>(cx);
     if (!sc)
         return;
 
     auto guard = mozilla::MakeScopeExit([&] {
         CancelOffThreadJobsForContext(cx);
         JS_DestroyContext(cx);
         js_delete(sc);
-        if (input->siblingContext) {
-            cooperationState->numThreads--;
-            CooperativeYield(/* terminating = */ true);
-        }
         js_delete(input);
     });
 
     if (input->parentRuntime)
         sc->isWorker = true;
     JS_SetContextPrivate(cx, sc);
     SetWorkerContextOptions(cx);
     JS::SetBuildIdOp(cx, ShellBuildId);
@@ -3785,18 +3578,16 @@ WorkerMain(void* arg)
         MOZ_ALWAYS_TRUE(EnsureGeckoProfilingStackInstalled(cx, sc));
     }
 
     do {
         JSAutoRequest ar(cx);
 
         JS::CompartmentOptions compartmentOptions;
         SetStandardCompartmentOptions(compartmentOptions);
-        if (input->siblingContext)
-            compartmentOptions.creationOptions().setNewZoneInNewZoneGroup();
 
         RootedObject global(cx, NewGlobalObject(cx, compartmentOptions, nullptr));
         if (!global)
             break;
 
         JSAutoCompartment ac(cx, global);
 
         JS::CompileOptions options(cx);
@@ -3826,17 +3617,17 @@ class MOZ_RAII AutoLockWorkerThreads : p
     AutoLockWorkerThreads()
       : Base(*workerThreadsLock)
     {
         MOZ_ASSERT(workerThreadsLock);
     }
 };
 
 static bool
-EvalInThread(JSContext* cx, unsigned argc, Value* vp, bool cooperative)
+EvalInWorker(JSContext* cx, unsigned argc, Value* vp)
 {
     if (!CanUseExtraThreads()) {
         JS_ReportErrorASCII(cx, "Can't create threads with --no-threads");
         return false;
     }
 
     CallArgs args = CallArgsFromVp(argc, vp);
     if (!args.get(0).isString()) {
@@ -3846,35 +3637,16 @@ EvalInThread(JSContext* cx, unsigned arg
 
 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
     if (cx->runningOOMTest) {
         JS_ReportErrorASCII(cx, "Can't create threads while running simulated OOM test");
         return false;
     }
 #endif
 
-    if (cooperative && GetShellContext(cx)->isWorker) {
-        // Disallowing cooperative multithreading in worker runtimes allows
-        // yield state to be process wide, and some other simplifications.
-        // When we have a better idea of how cooperative multithreading will be
-        // used in the browser this restriction might be relaxed.
-        JS_ReportErrorASCII(cx, "Cooperative multithreading in worker runtimes is not supported");
-        return false;
-    }
-
-    if (cooperative && !cx->runtime()->gc.canChangeActiveContext(cx)) {
-        JS_ReportErrorASCII(cx, "Cooperating multithreading context switches are not currently allowed");
-        return false;
-    }
-
-    if (cooperative && cooperationState->singleThreaded) {
-        JS_ReportErrorASCII(cx, "Creating cooperative threads is not allowed while single threaded");
-        return false;
-    }
-
     if (!args[0].toString()->ensureLinear(cx))
         return false;
 
     if (!workerThreadsLock) {
         workerThreadsLock = js_new<Mutex>(mutexid::ShellWorkerThreads);
         if (!workerThreadsLock) {
             ReportOutOfMemory(cx);
             return false;
@@ -3886,66 +3658,42 @@ EvalInThread(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 =
-        cooperative
-        ? js_new<WorkerInput>(cx, chars, str->length())
-        : js_new<WorkerInput>(JS_GetParentRuntime(cx), chars, str->length());
+    WorkerInput* input = js_new<WorkerInput>(JS_GetParentRuntime(cx), chars, str->length());
     if (!input) {
         ReportOutOfMemory(cx);
         return false;
     }
 
-    if (cooperative) {
-        cooperationState->numThreads++;
-        CooperativeBeginWait(cx);
-    }
-
     Thread* thread;
     {
         AutoEnterOOMUnsafeRegion oomUnsafe;
         thread = js_new<Thread>(Thread::Options().setStackSize(gMaxStackSize + 128 * 1024));
         if (!thread || !thread->init(WorkerMain, input))
             oomUnsafe.crash("EvalInThread");
     }
 
-    if (cooperative) {
-        CooperativeEndWait(cx);
-    } else {
-        AutoLockWorkerThreads alwt;
-        if (!workerThreads.append(thread)) {
-            ReportOutOfMemory(cx);
-            thread->join();
-            return false;
-        }
+    AutoLockWorkerThreads alwt;
+    if (!workerThreads.append(thread)) {
+        ReportOutOfMemory(cx);
+        thread->join();
+        return false;
     }
 
     args.rval().setUndefined();
     return true;
 }
 
 static bool
-EvalInWorker(JSContext* cx, unsigned argc, Value* vp)
-{
-    return EvalInThread(cx, argc, vp, false);
-}
-
-static bool
-EvalInCooperativeThread(JSContext* cx, unsigned argc, Value* vp)
-{
-    return EvalInThread(cx, argc, vp, true);
-}
-
-static bool
 ShapeOf(JSContext* cx, unsigned argc, JS::Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (!args.get(0).isObject()) {
         JS_ReportErrorASCII(cx, "shapeOf: object expected");
         return false;
     }
     JSObject* obj = &args[0].toObject();
@@ -4117,23 +3865,16 @@ ScheduleWatchdog(JSContext* cx, double t
     return true;
 }
 
 static void
 KillWorkerThreads(JSContext* cx)
 {
     MOZ_ASSERT_IF(!CanUseExtraThreads(), workerThreads.empty());
 
-    // Yield until all other cooperative threads in the main runtime finish.
-    while (cooperationState->numThreads) {
-        CooperativeBeginWait(cx);
-        CooperativeYield();
-        CooperativeEndWait(cx);
-    }
-
     if (!workerThreadsLock) {
         MOZ_ASSERT(workerThreads.empty());
         return;
     }
 
     while (true) {
         // We need to leave the AutoLockWorkerThreads scope before we call
         // js::Thread::join, to avoid deadlocks when AutoLockWorkerThreads is
@@ -4147,19 +3888,16 @@ KillWorkerThreads(JSContext* cx)
         }
         thread->join();
     }
 
     workerThreads.clearAndFree();
 
     js_delete(workerThreadsLock);
     workerThreadsLock = nullptr;
-
-    js_delete(cooperationState);
-    cooperationState = nullptr;
 }
 
 static void
 CancelExecution(JSContext* cx)
 {
     ShellContext* sc = GetShellContext(cx);
     sc->serviceInterrupt = true;
     JS_RequestInterruptCallback(cx);
@@ -5721,35 +5459,22 @@ HasCopyOnWriteElements(JSContext* cx, un
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().setBoolean(args.get(0).isObject() &&
                            args[0].toObject().isNative() &&
                            args[0].toObject().as<NativeObject>().denseElementsAreCopyOnWrite());
     return true;
 }
 
-// Set the profiling stack for each cooperating context in a runtime.
-static bool
-EnsureAllContextProfilingStacks(JSContext* cx)
-{
-    for (const CooperatingContext& target : cx->runtime()->cooperatingContexts()) {
-        ShellContext* sc = GetShellContext(target.context());
-        if (!EnsureGeckoProfilingStackInstalled(target.context(), sc))
-            return false;
-    }
-
-    return true;
-}
-
 static bool
 EnableGeckoProfiling(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
-    if (!EnsureAllContextProfilingStacks(cx))
+    if (!EnsureGeckoProfilingStackInstalled(cx, GetShellContext(cx)))
         return false;
 
     cx->runtime()->geckoProfiler().enableSlowAssertions(false);
     cx->runtime()->geckoProfiler().enable(true);
 
     args.rval().setUndefined();
     return true;
 }
@@ -5766,17 +5491,17 @@ EnableGeckoProfilingWithSlowAssertions(J
         if (cx->runtime()->geckoProfiler().slowAssertionsEnabled())
             return true;
 
         // Slow assertions are off.  Disable profiling before re-enabling
         // with slow assertions on.
         cx->runtime()->geckoProfiler().enable(false);
     }
 
-    if (!EnsureAllContextProfilingStacks(cx))
+    if (!EnsureGeckoProfilingStackInstalled(cx, GetShellContext(cx)))
         return false;
 
     cx->runtime()->geckoProfiler().enableSlowAssertions(true);
     cx->runtime()->geckoProfiler().enable(true);
 
     return true;
 }
 
@@ -6967,26 +6692,16 @@ static const JSFunctionSpecWithHelp shel
 "  Evaluate s in optional sandbox object o.\n"
 "  if (s == '' && !o) return new o with eager standard classes\n"
 "  if (s == 'lazy' && !o) return new o with lazy standard classes"),
 
     JS_FN_HELP("evalInWorker", EvalInWorker, 1, 0,
 "evalInWorker(str)",
 "  Evaluate 'str' in a separate thread with its own runtime.\n"),
 
-    JS_FN_HELP("evalInCooperativeThread", EvalInCooperativeThread, 1, 0,
-"evalInCooperativeThread(str)",
-"  Evaluate 'str' in a separate cooperatively scheduled thread using the same runtime.\n"),
-
-    JS_FN_HELP("cooperativeYield", CooperativeYieldThread, 1, 0,
-"cooperativeYield(leaveZoneGroup)",
-"  Yield execution to another cooperatively scheduled thread using the same runtime.\n"
-"  If leaveZoneGroup is specified then other threads may execute code in the\n"
-"  current thread's zone group via evaluate(..., {zoneGroup:N}).\n"),
-
     JS_FN_HELP("getSharedArrayBuffer", GetSharedArrayBuffer, 0, 0,
 "getSharedArrayBuffer()",
 "  Retrieve the SharedArrayBuffer object from the cross-worker mailbox.\n"
 "  The object retrieved may not be identical to the object that was\n"
 "  installed, but it references the same shared memory.\n"
 "  getSharedArrayBuffer performs an ordering memory barrier.\n"),
 
     JS_FN_HELP("setSharedArrayBuffer", SetSharedArrayBuffer, 0, 0,
@@ -7977,20 +7692,23 @@ static const JSClass* GetDomClass() {
     return &dom_class;
 }
 #endif
 
 static bool
 dom_genericGetter(JSContext* cx, unsigned argc, JS::Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
-    if (!obj)
-        return false;
-
+
+    if (!args.thisv().isObject()) {
+        args.rval().setUndefined();
+        return true;
+    }
+
+    RootedObject obj(cx, &args.thisv().toObject());
     if (JS_GetClass(obj) != &dom_class) {
         args.rval().set(UndefinedValue());
         return true;
     }
 
     JS::Value val = js::GetReservedSlot(obj, DOM_OBJECT_SLOT);
 
     const JSJitInfo* info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
@@ -7998,22 +7716,24 @@ dom_genericGetter(JSContext* cx, unsigne
     JSJitGetterOp getter = info->getter;
     return getter(cx, obj, val.toPrivate(), JSJitGetterCallArgs(args));
 }
 
 static bool
 dom_genericSetter(JSContext* cx, unsigned argc, JS::Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
-    if (!obj)
-        return false;
-
     MOZ_ASSERT(args.length() == 1);
 
+    if (!args.thisv().isObject()) {
+        args.rval().setUndefined();
+        return true;
+    }
+
+    RootedObject obj(cx, &args.thisv().toObject());
     if (JS_GetClass(obj) != &dom_class) {
         args.rval().set(UndefinedValue());
         return true;
     }
 
     JS::Value val = js::GetReservedSlot(obj, DOM_OBJECT_SLOT);
 
     const JSJitInfo* info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
@@ -8024,20 +7744,23 @@ dom_genericSetter(JSContext* cx, unsigne
     args.rval().set(UndefinedValue());
     return true;
 }
 
 static bool
 dom_genericMethod(JSContext* cx, unsigned argc, JS::Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
-    if (!obj)
-        return false;
-
+
+    if (!args.thisv().isObject()) {
+        args.rval().setUndefined();
+        return true;
+    }
+
+    RootedObject obj(cx, &args.thisv().toObject());
     if (JS_GetClass(obj) != &dom_class) {
         args.rval().set(UndefinedValue());
         return true;
     }
 
     JS::Value val = js::GetReservedSlot(obj, DOM_OBJECT_SLOT);
 
     const JSJitInfo* info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
@@ -9406,22 +9129,16 @@ main(int argc, char** argv, char** envp)
         JS_SetGCParameter(cx, JSGC_DYNAMIC_HEAP_GROWTH, 1);
         JS_SetGCParameter(cx, JSGC_DYNAMIC_MARK_SLICE, 1);
         JS_SetGCParameter(cx, JSGC_SLICE_TIME_BUDGET, 10);
     }
 #endif
 
     js::SetPreserveWrapperCallback(cx, DummyPreserveWrapperCallback);
 
-    cooperationState = js_new<CooperationState>();
-    JS::SetSingleThreadedExecutionCallbacks(cx,
-                                            CooperativeBeginSingleThreadedExecution,
-                                            CooperativeEndSingleThreadedExecution);
-    SetCooperativeYieldCallback(cx, CooperativeYieldCallback);
-
     result = Shell(cx, &op, envp);
 
 #ifdef DEBUG
     if (OOM_printAllocationCount)
         printf("OOM max count: %" PRIu64 "\n", js::oom::counter);
 #endif
 
     JS_SetGrayGCRootsTracer(cx, nullptr, nullptr);
--- a/js/src/threading/ProtectedData.cpp
+++ b/js/src/threading/ProtectedData.cpp
@@ -74,17 +74,17 @@ void
 CheckZoneGroup<Helper>::check() const
 {
     if (OnHelperThread<Helper>())
         return;
 
     JSContext* cx = TlsContext.get();
     if (group) {
         if (group->usedByHelperThread()) {
-            MOZ_ASSERT(group->ownedByCurrentThread());
+            MOZ_ASSERT(group->ownedByCurrentHelperThread());
         } else {
             // This check is disabled on windows for the same reason as in
             // CheckActiveThread.
 #ifndef XP_WIN
             // In a cooperatively scheduled runtime the active thread is
             // permitted access to all zone groups --- even those it has not
             // entered --- for GC and similar purposes. Since all other
             // cooperative threads are suspended, these accesses are threadsafe
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -713,19 +713,19 @@ Debugger::~Debugger()
     /*
      * We don't have to worry about locking here since Debugger is not
      * background finalized.
      */
     JSContext* cx = TlsContext.get();
     if (onNewGlobalObjectWatchersLink.mPrev ||
         onNewGlobalObjectWatchersLink.mNext ||
         cx->runtime()->onNewGlobalObjectWatchers().begin() == JSRuntime::WatchersList::Iterator(this))
+    {
         cx->runtime()->onNewGlobalObjectWatchers().remove(this);
-
-    cx->runtime()->endSingleThreadedExecution(cx);
+    }
 }
 
 bool
 Debugger::init(JSContext* cx)
 {
     if (!debuggees.init() ||
         !debuggeeZones.init() ||
         !frames.init() ||
@@ -2606,34 +2606,32 @@ UpdateExecutionObservabilityOfScriptsInZ
         }
     }
 
     // Code below this point must be infallible to ensure the active bit of
     // BaselineScripts is in a consistent state.
     //
     // Mark active baseline scripts in the observable set so that they don't
     // get discarded. They will be recompiled.
-    for (const CooperatingContext& target : cx->runtime()->cooperatingContexts()) {
-        for (JitActivationIterator actIter(cx, target); !actIter.done(); ++actIter) {
-            if (actIter->compartment()->zone() != zone)
-                continue;
-
-            for (OnlyJSJitFrameIter iter(actIter); !iter.done(); ++iter) {
-                const jit::JSJitFrameIter& frame = iter.frame();
-                switch (frame.type()) {
-                  case JitFrame_BaselineJS:
-                    MarkBaselineScriptActiveIfObservable(frame.script(), obs);
-                    break;
-                  case JitFrame_IonJS:
-                    MarkBaselineScriptActiveIfObservable(frame.script(), obs);
-                    for (InlineFrameIterator inlineIter(cx, &frame); inlineIter.more(); ++inlineIter)
-                        MarkBaselineScriptActiveIfObservable(inlineIter.script(), obs);
-                    break;
-                  default:;
-                }
+    for (JitActivationIterator actIter(cx); !actIter.done(); ++actIter) {
+        if (actIter->compartment()->zone() != zone)
+            continue;
+
+        for (OnlyJSJitFrameIter iter(actIter); !iter.done(); ++iter) {
+            const jit::JSJitFrameIter& frame = iter.frame();
+            switch (frame.type()) {
+              case JitFrame_BaselineJS:
+                MarkBaselineScriptActiveIfObservable(frame.script(), obs);
+                break;
+              case JitFrame_IonJS:
+                MarkBaselineScriptActiveIfObservable(frame.script(), obs);
+                for (InlineFrameIterator inlineIter(cx, &frame); inlineIter.more(); ++inlineIter)
+                    MarkBaselineScriptActiveIfObservable(inlineIter.script(), obs);
+                break;
+              default:;
             }
         }
     }
 
     // Iterate through the scripts again and finish discarding
     // BaselineScripts. This must be done as a separate phase as we can only
     // discard the BaselineScript on scripts that have no IonScript.
     for (size_t i = 0; i < scripts.length(); i++) {
@@ -3934,34 +3932,21 @@ Debugger::construct(JSContext* cx, unsig
     RootedNativeObject obj(cx, NewNativeObjectWithGivenProto(cx, &Debugger::class_, proto,
                                                              TenuredObject));
     if (!obj)
         return false;
     for (unsigned slot = JSSLOT_DEBUG_PROTO_START; slot < JSSLOT_DEBUG_PROTO_STOP; slot++)
         obj->setReservedSlot(slot, proto->getReservedSlot(slot));
     obj->setReservedSlot(JSSLOT_DEBUG_MEMORY_INSTANCE, NullValue());
 
-    // Debuggers currently require single threaded execution. A debugger may be
-    // used to debug content in other zone groups, and may be used to observe
-    // all activity in the runtime via hooks like OnNewGlobalObject.
-    if (!cx->runtime()->beginSingleThreadedExecution(cx)) {
-        JS_ReportErrorASCII(cx, "Cannot ensure single threaded execution in Debugger");
-        return false;
-    }
-
     Debugger* debugger;
     {
         /* Construct the underlying C++ object. */
         auto dbg = cx->make_unique<Debugger>(cx, obj.get());
-        if (!dbg) {
-            JS::AutoSuppressGCAnalysis nogc; // Suppress warning about |dbg|.
-            cx->runtime()->endSingleThreadedExecution(cx);
-            return false;
-        }
-        if (!dbg->init(cx))
+        if (!dbg || !dbg->init(cx))
             return false;
 
         debugger = dbg.release();
         obj->setPrivate(debugger); // owns the released pointer
     }
 
     /* Add the initial debuggees, if any. */
     for (unsigned i = 0; i < args.length(); i++) {
--- a/js/src/vm/GeckoProfiler-inl.h
+++ b/js/src/vm/GeckoProfiler-inl.h
@@ -37,17 +37,16 @@ class MOZ_RAII AutoSuppressProfilerSampl
   public:
     explicit AutoSuppressProfilerSampling(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
 
     ~AutoSuppressProfilerSampling();
 
   private:
     JSContext* cx_;
     bool previouslyEnabled_;
-    JSRuntime::AutoProhibitActiveContextChange prohibitContextChange_;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 MOZ_ALWAYS_INLINE
 GeckoProfilerEntryMarker::GeckoProfilerEntryMarker(JSContext* cx,
                                                    JSScript* script
                                                    MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
   : profiler_(&cx->geckoProfiler())
--- a/js/src/vm/GeckoProfiler.cpp
+++ b/js/src/vm/GeckoProfiler.cpp
@@ -83,23 +83,18 @@ GetTopProfilingJitFrame(Activation* act)
     jit::JSJitProfilingFrameIterator jitIter((jit::CommonFrameLayout*) iter.frame().fp());
     MOZ_ASSERT(!jitIter.done());
     return jitIter.fp();
 }
 
 void
 GeckoProfilerRuntime::enable(bool enabled)
 {
-#ifdef DEBUG
-    // All cooperating contexts must have profile stacks installed before the
-    // profiler can be enabled. Cooperating threads created while the profiler
-    // is enabled must have stacks set before they execute any JS.
-    for (const CooperatingContext& target : rt->cooperatingContexts())
-        MOZ_ASSERT(target.context()->geckoProfiler().installed());
-#endif
+    JSContext* cx = rt->mainContextFromAnyThread();
+    MOZ_ASSERT(cx->geckoProfiler().installed());
 
     if (enabled_ == enabled)
         return;
 
     /*
      * Ensure all future generated code will be instrumented, or that all
      * currently instrumented code is discarded
      */
@@ -107,58 +102,54 @@ GeckoProfilerRuntime::enable(bool enable
 
     // This function is called when the Gecko profiler makes a new Sampler
     // (and thus, a new circular buffer). Set all current entries in the
     // JitcodeGlobalTable as expired and reset the buffer range start.
     if (rt->hasJitRuntime() && rt->jitRuntime()->hasJitcodeGlobalTable())
         rt->jitRuntime()->getJitcodeGlobalTable()->setAllEntriesAsExpired();
     rt->setProfilerSampleBufferRangeStart(0);
 
-    // Ensure that lastProfilingFrame is null for all threads before 'enabled' becomes true.
-    for (const CooperatingContext& target : rt->cooperatingContexts()) {
-        if (target.context()->jitActivation) {
-            target.context()->jitActivation->setLastProfilingFrame(nullptr);
-            target.context()->jitActivation->setLastProfilingCallSite(nullptr);
-        }
+    // Ensure that lastProfilingFrame is null for the main thread.
+    if (cx->jitActivation) {
+        cx->jitActivation->setLastProfilingFrame(nullptr);
+        cx->jitActivation->setLastProfilingCallSite(nullptr);
     }
 
     enabled_ = enabled;
 
     /* Toggle Gecko Profiler-related jumps on baseline jitcode.
      * The call to |ReleaseAllJITCode| above will release most baseline jitcode, but not
      * jitcode for scripts with active frames on the stack.  These scripts need to have
      * their profiler state toggled so they behave properly.
      */
     jit::ToggleBaselineProfiling(rt, enabled);
 
     /* Update lastProfilingFrame to point to the top-most JS jit-frame currently on
      * stack.
      */
-    for (const CooperatingContext& target : rt->cooperatingContexts()) {
-        if (target.context()->jitActivation) {
-            // Walk through all activations, and set their lastProfilingFrame appropriately.
-            if (enabled) {
-                Activation* act = target.context()->activation();
-                void* lastProfilingFrame = GetTopProfilingJitFrame(act);
+    if (cx->jitActivation) {
+        // Walk through all activations, and set their lastProfilingFrame appropriately.
+        if (enabled) {
+            Activation* act = cx->activation();
+            void* lastProfilingFrame = GetTopProfilingJitFrame(act);
 
-                jit::JitActivation* jitActivation = target.context()->jitActivation;
-                while (jitActivation) {
-                    jitActivation->setLastProfilingFrame(lastProfilingFrame);
-                    jitActivation->setLastProfilingCallSite(nullptr);
+            jit::JitActivation* jitActivation = cx->jitActivation;
+            while (jitActivation) {
+                jitActivation->setLastProfilingFrame(lastProfilingFrame);
+                jitActivation->setLastProfilingCallSite(nullptr);
 
-                    jitActivation = jitActivation->prevJitActivation();
-                    lastProfilingFrame = GetTopProfilingJitFrame(jitActivation);
-                }
-            } else {
-                jit::JitActivation* jitActivation = target.context()->jitActivation;
-                while (jitActivation) {
-                    jitActivation->setLastProfilingFrame(nullptr);
-                    jitActivation->setLastProfilingCallSite(nullptr);
-                    jitActivation = jitActivation->prevJitActivation();
-                }
+                jitActivation = jitActivation->prevJitActivation();
+                lastProfilingFrame = GetTopProfilingJitFrame(jitActivation);
+            }
+        } else {
+            jit::JitActivation* jitActivation = cx->jitActivation;
+            while (jitActivation) {
+                jitActivation->setLastProfilingFrame(nullptr);
+                jitActivation->setLastProfilingCallSite(nullptr);
+                jitActivation = jitActivation->prevJitActivation();
             }
         }
     }
 
     // WebAssembly code does not need to be released, but profiling string
     // labels have to be generated so that they are available during async
     // profiling stack iteration.
     for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next())
@@ -426,22 +417,18 @@ ProfileEntry::script() const
     MOZ_ASSERT(isJs());
     auto script = reinterpret_cast<JSScript*>(spOrScript.operator void*());
     if (!script)
         return nullptr;
 
     // If profiling is supressed then we can't trust the script pointers to be
     // valid as they could be in the process of being moved by a compacting GC
     // (although it's still OK to get the runtime from them).
-    //
-    // We only need to check the active context here, as
-    // AutoSuppressProfilerSampling prohibits the runtime's active context from
-    // being changed while it exists.
-    JSContext* cx = script->runtimeFromAnyThread()->activeContext();
-    if (!cx || !cx->isProfilerSamplingEnabled())
+    JSContext* cx = script->runtimeFromAnyThread()->mainContextFromAnyThread();
+    if (!cx->isProfilerSamplingEnabled())
         return nullptr;
 
     MOZ_ASSERT(!IsForwarded(script));
     return script;
 }
 
 JS_FRIEND_API(jsbytecode*)
 ProfileEntry::pc() const
@@ -485,18 +472,17 @@ js::RegisterContextProfilingEventMarker(
 {
     MOZ_ASSERT(cx->runtime()->geckoProfiler().enabled());
     cx->runtime()->geckoProfiler().setEventMarker(fn);
 }
 
 AutoSuppressProfilerSampling::AutoSuppressProfilerSampling(JSContext* cx
                                                            MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
   : cx_(cx),
-    previouslyEnabled_(cx->isProfilerSamplingEnabled()),
-    prohibitContextChange_(cx->runtime())
+    previouslyEnabled_(cx->isProfilerSamplingEnabled())
 {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     if (previouslyEnabled_)
         cx_->disableProfilerSampling();
 }
 
 AutoSuppressProfilerSampling::~AutoSuppressProfilerSampling()
 {
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -2,16 +2,17 @@
  * 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 "vm/HelperThreads.h"
 
 #include "mozilla/Maybe.h"
+#include "mozilla/ScopeExit.h"
 #include "mozilla/Unused.h"
 
 #include "builtin/Promise.h"
 #include "frontend/BytecodeCompiler.h"
 #include "gc/GCInternals.h"
 #include "jit/IonBuilder.h"
 #include "js/Utility.h"
 #include "threading/CpuCount.h"
@@ -1824,29 +1825,26 @@ HelperThread::handleIonWorkload(AutoLock
         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.
+    // 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.
     //
     // 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);
+    rt->mainContextFromAnyThread()->requestInterrupt(JSContext::RequestInterruptCanWait);
 
     currentTask.reset();
 
     // Notify the active thread in case it is waiting for the compilation to finish.
     HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
 }
 
 void
@@ -1915,16 +1913,23 @@ HelperThread::handleParseWorkload(AutoLo
     currentTask.emplace(HelperThreadState().parseWorklist(locked).popCopy());
     ParseTask* task = parseTask();
 
     {
         AutoUnlockHelperThreadState unlock(locked);
         AutoSetContextRuntime ascr(task->parseGlobal->runtimeFromAnyThread());
 
         JSContext* cx = TlsContext.get();
+
+        ZoneGroup* zoneGroup = task->parseGlobal->zoneFromAnyThread()->group();
+        zoneGroup->setHelperThreadOwnerContext(cx);
+        auto resetOwnerContext = mozilla::MakeScopeExit([&] {
+            zoneGroup->setHelperThreadOwnerContext(nullptr);
+        });
+
         AutoCompartment ac(cx, task->parseGlobal);
 
         task->parse(cx);
 
         cx->frontendCollectionPool().purge();
     }
 
     // The callback is invoked while we are still off thread.
--- a/js/src/vm/Initialization.cpp
+++ b/js/src/vm/Initialization.cpp
@@ -26,18 +26,17 @@
 #include "unicode/utypes.h"
 #endif // ENABLE_INTL_API
 #include "vm/DateTime.h"
 #include "vm/HelperThreads.h"
 #include "vm/Runtime.h"
 #include "vm/Time.h"
 #include "vm/TraceLogging.h"
 #include "vtune/VTuneWrapper.h"
-#include "wasm/WasmBuiltins.h"
-#include "wasm/WasmInstance.h"
+#include "wasm/WasmProcess.h"
 
 using JS::detail::InitState;
 using JS::detail::libraryInitState;
 using js::FutexThread;
 
 InitState JS::detail::libraryInitState;
 
 #ifdef DEBUG
@@ -101,17 +100,17 @@ JS::detail::InitWithFailureDiagnostic(bo
 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
     RETURN_IF_FAIL(js::oom::InitThreadType());
 #endif
 
     js::InitMallocAllocator();
 
     RETURN_IF_FAIL(js::Mutex::Init());
 
-    RETURN_IF_FAIL(js::wasm::InitInstanceStaticData());
+    RETURN_IF_FAIL(js::wasm::Init());
 
     js::gc::InitMemorySubsystem(); // Ensure gc::SystemPageSize() works.
 
     RETURN_IF_FAIL(js::jit::InitProcessExecutableMemory());
 
     RETURN_IF_FAIL(js::MemoryProtectionExceptionHandler::install());
 
     RETURN_IF_FAIL(js::jit::InitializeIon());
@@ -168,18 +167,17 @@ JS_ShutDown(void)
 
 #ifdef JS_TRACE_LOGGING
     js::DestroyTraceLoggerThreadState();
     js::DestroyTraceLoggerGraphState();
 #endif
 
     js::MemoryProtectionExceptionHandler::uninstall();
 
-    js::wasm::ShutDownInstanceStaticData();
-    js::wasm::ShutDownProcessStaticData();
+    js::wasm::ShutDown();
 
     js::Mutex::ShutDown();
 
     // The only difficult-to-address reason for the restriction that you can't
     // call JS_Init/stuff/JS_ShutDown multiple times is the Windows PRMJ
     // NowInit initialization code, which uses PR_CallOnce to initialize the
     // PRMJ_Now subsystem.  (For reinitialization to be permitted, we'd need to
     // "reset" the called-once status -- doable, but more trouble than it's
@@ -195,17 +193,16 @@ JS_ShutDown(void)
 
 #ifdef MOZ_VTUNE
     js::vtune::Shutdown();
 #endif // MOZ_VTUNE
 
     js::FinishDateTimeState();
 
     if (!JSRuntime::hasLiveRuntimes()) {
-        js::wasm::ReleaseBuiltinThunks();
         js::jit::ReleaseProcessExecutableMemory();
         MOZ_ASSERT(!js::LiveMappedBufferCount());
     }
 
     js::ShutDownMallocAllocator();
 
     libraryInitState = InitState::ShutDown;
 }
--- a/js/src/vm/JSCompartment.cpp
+++ b/js/src/vm/JSCompartment.cpp
@@ -53,17 +53,16 @@ JSCompartment::JSCompartment(Zone* zone,
     marked(true),
     warnedAboutExprClosure(false),
     warnedAboutStringGenericsMethods(0),
 #ifdef DEBUG
     firedOnNewGlobalObject(false),
 #endif
     global_(nullptr),
     enterCompartmentDepth(0),
-    globalHolds(0),
     performanceMonitoring(runtime_),
     data(nullptr),
     realmData(nullptr),
     allocationMetadataBuilder(nullptr),
     lastAnimationTime(0),
     regExps(),
     arraySpeciesLookup(),
     globalWriteBarriered(0),
--- a/js/src/vm/JSCompartment.h
+++ b/js/src/vm/JSCompartment.h
@@ -629,37 +629,29 @@ struct JSCompartment
     void mark() { marked = true; }
 
   private:
     friend struct JSRuntime;
     friend struct JSContext;
     js::ReadBarrieredGlobalObject global_;
 
     unsigned                     enterCompartmentDepth;
-    unsigned                     globalHolds;
 
   public:
     js::PerformanceGroupHolder performanceMonitoring;
 
     void enter() {
         enterCompartmentDepth++;
     }
     void leave() {
         enterCompartmentDepth--;
     }
     bool hasBeenEntered() const { return !!enterCompartmentDepth; }
 
-    void holdGlobal() {
-        globalHolds++;
-    }
-    void releaseGlobal() {
-        MOZ_ASSERT(globalHolds > 0);
-        globalHolds--;
-    }
-    bool shouldTraceGlobal() const { return globalHolds > 0 || hasBeenEntered(); }
+    bool shouldTraceGlobal() const { return hasBeenEntered(); }
 
     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_; }
 
--- a/js/src/vm/JSContext-inl.h
+++ b/js/src/vm/JSContext-inl.h
@@ -474,19 +474,16 @@ JSContext::runningWithTrustedPrincipals(
 }
 
 inline void
 JSContext::enterNonAtomsCompartment(JSCompartment* c)
 {
     enterCompartmentDepth_++;
 
     MOZ_ASSERT(!c->zone()->isAtomsZone());
-    c->holdGlobal();
-    enterZoneGroup(c->zone()->group());
-    c->releaseGlobal();
 
     c->enter();
     setCompartment(c, nullptr);
 }
 
 inline void
 JSContext::enterAtomsCompartment(JSCompartment* c,
                                  const js::AutoLockForExclusiveAccess& lock)
@@ -521,21 +518,18 @@ JSContext::leaveCompartment(
 {
     MOZ_ASSERT(hasEnteredCompartment());
     enterCompartmentDepth_--;
 
     // Only call leave() after we've setCompartment()-ed away from the current
     // compartment.
     JSCompartment* startingCompartment = compartment_;
     setCompartment(oldCompartment, maybeLock);
-    if (startingCompartment) {
+    if (startingCompartment)
         startingCompartment->leave();
-        if (!startingCompartment->zone()->isAtomsZone())
-            leaveZoneGroup(startingCompartment->zone()->group());
-    }
 }
 
 inline void
 JSContext::setCompartment(JSCompartment* comp,
                           const js::AutoLockForExclusiveAccess* maybeLock /* = nullptr */)
 {
     // Only one thread can be in the atoms compartment at a time.
     MOZ_ASSERT_IF(runtime_->isAtomsCompartment(comp), maybeLock != nullptr);
@@ -546,39 +540,25 @@ JSContext::setCompartment(JSCompartment*
     MOZ_ASSERT_IF(comp && !runtime_->isAtomsCompartment(comp),
                   !comp->zone()->isAtomsZone());
 
     // Both the current and the new compartment should be properly marked as
     // entered at this point.
     MOZ_ASSERT_IF(compartment_, compartment_->hasBeenEntered());
     MOZ_ASSERT_IF(comp, comp->hasBeenEntered());
 
-    // This context must have exclusive access to the zone's group.
+    // This thread must have exclusive access to the zone.
     MOZ_ASSERT_IF(comp && !comp->zone()->isAtomsZone(),
-                  comp->zone()->group()->ownedByCurrentThread());
+                  CurrentThreadCanAccessZone(comp->zone()));
 
     compartment_ = comp;
     zone_ = comp ? comp->zone() : nullptr;
     arenas_ = zone_ ? &zone_->arenas : nullptr;
 }
 
-inline void
-JSContext::enterZoneGroup(js::ZoneGroup* group)
-{
-    MOZ_ASSERT(this == js::TlsContext.get());
-    group->enter(this);
-}
-
-inline void
-JSContext::leaveZoneGroup(js::ZoneGroup* group)
-{
-    MOZ_ASSERT(this == js::TlsContext.get());
-    group->leave();
-}
-
 inline JSScript*
 JSContext::currentScript(jsbytecode** ppc,
                          MaybeAllowCrossCompartment allowCrossCompartment) const
 {
     if (ppc)
         *ppc = nullptr;
 
     js::Activation* act = activation();
--- a/js/src/vm/JSContext.cpp
+++ b/js/src/vm/JSContext.cpp
@@ -173,49 +173,16 @@ js::NewContext(uint32_t maxBytes, uint32
         js_delete(cx);
         js_delete(runtime);
         return nullptr;
     }
 
     return cx;
 }
 
-JSContext*
-js::NewCooperativeContext(JSContext* siblingContext)
-{
-    MOZ_RELEASE_ASSERT(!TlsContext.get());
-
-    JSRuntime* runtime = siblingContext->runtime();
-
-    JSContext* cx = js_new<JSContext>(runtime, JS::ContextOptions());
-    if (!cx || !cx->init(ContextKind::Cooperative)) {
-        js_delete(cx);
-        return nullptr;
-    }
-
-    runtime->setNewbornActiveContext(cx);
-    return cx;
-}
-
-void
-js::YieldCooperativeContext(JSContext* cx)
-{
-    MOZ_ASSERT(cx == TlsContext.get());
-    MOZ_ASSERT(cx->runtime()->activeContext() == cx);
-    cx->runtime()->setActiveContext(nullptr);
-}
-
-void
-js::ResumeCooperativeContext(JSContext* cx)
-{
-    MOZ_ASSERT(cx == TlsContext.get());
-    MOZ_ASSERT(cx->runtime()->activeContext() == nullptr);
-    cx->runtime()->setActiveContext(cx);
-}
-
 static void
 FreeJobQueueHandling(JSContext* cx)
 {
     if (!cx->jobQueue)
         return;
 
     cx->jobQueue->reset();
     FreeOp* fop = cx->defaultFreeOp();
@@ -238,39 +205,24 @@ js::DestroyContext(JSContext* cx)
     // 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());
 
     FreeJobQueueHandling(cx);
 
-    if (cx->runtime()->cooperatingContexts().length() == 1) {
-        // Flush promise tasks executing in helper threads early, before any parts
-        // of the JSRuntime that might be visible to helper threads are torn down.
-        cx->runtime()->offThreadPromiseState.ref().shutdown(cx);
+    // Flush promise tasks executing in helper threads early, before any parts
+    // of the JSRuntime that might be visible to helper threads are torn down.
+    cx->runtime()->offThreadPromiseState.ref().shutdown(cx);
 
-        // Destroy the runtime along with its last context.
-        cx->runtime()->destroyRuntime();
-        js_delete(cx->runtime());
-        js_delete_poison(cx);
-    } 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);
-
-        cx->runtime()->deleteActiveContext(cx);
-    }
+    // Destroy the runtime along with its last context.
+    cx->runtime()->destroyRuntime();
+    js_delete(cx->runtime());
+    js_delete_poison(cx);
 }
 
 void
 JS::RootingContext::checkNoGCRooters() {
 #ifdef DEBUG
     for (auto const& stackRootPtr : stackRoots_)
         MOZ_ASSERT(stackRootPtr == nullptr);
 #endif
--- a/js/src/vm/JSContext.h
+++ b/js/src/vm/JSContext.h
@@ -218,19 +218,16 @@ struct JSContext : public JS::RootingCon
 
   public:
     template <typename T>
     inline void enterCompartmentOf(const T& target);
     inline void enterNullCompartment();
     inline void leaveCompartment(JSCompartment* oldCompartment,
                                  const js::AutoLockForExclusiveAccess* maybeLock = nullptr);
 
-    inline void enterZoneGroup(js::ZoneGroup* group);
-    inline void leaveZoneGroup(js::ZoneGroup* group);
-
     void setHelperThread(js::HelperThread* helperThread);
     js::HelperThread* helperThread() const { return helperThread_; }
 
     bool isNurseryAllocSuppressed() const {
         return nurserySuppressions_;
     }
 
     // Threads may freely access any data in their compartment and zone.
@@ -853,16 +850,29 @@ struct JSContext : public JS::RootingCon
     void finishHandlingJitInterrupt() {
         MOZ_ASSERT(handlingJitInterrupt_);
         handlingJitInterrupt_ = false;
     }
     bool handlingJitInterrupt() const {
         return handlingJitInterrupt_;
     }
 
+    void* addressOfInterrupt() {
+        return &interrupt_;
+    }
+    void* addressOfInterruptRegExpJit() {
+        return &interruptRegExpJit_;
+    }
+    void* addressOfJitStackLimit() {
+        return &jitStackLimit;
+    }
+    void* addressOfJitStackLimitNoInterrupt() {
+        return &jitStackLimitNoInterrupt;
+    }
+
     /* Futex state, used by Atomics.wait() and Atomics.wake() on the Atomics object */
     js::FutexThread fx;
 
     // Buffer for OSR from baseline to Ion. To avoid holding on to this for
     // too long, it's also freed in EnterBaseline (after returning from JIT code).
     js::ThreadLocalData<uint8_t*> osrTempData_;
 
     uint8_t* allocateOsrTempData(size_t size);
@@ -930,20 +940,20 @@ JSContext::boolToResult(bool ok)
         MOZ_ASSERT(!isExceptionPending());
         MOZ_ASSERT(!isPropagatingForcedReturn());
         return JS::Ok();
     }
     return JS::Result<>(reportedError);
 }
 
 inline JSContext*
-JSRuntime::activeContextFromOwnThread()
+JSRuntime::mainContextFromOwnThread()
 {
-    MOZ_ASSERT(activeContext() == js::TlsContext.get());
-    return activeContext();
+    MOZ_ASSERT(mainContextFromAnyThread() == js::TlsContext.get());
+    return mainContextFromAnyThread();
 }
 
 namespace js {
 
 struct MOZ_RAII AutoResolving {
   public:
     enum Kind {
         LOOKUP,
@@ -981,25 +991,16 @@ struct MOZ_RAII AutoResolving {
 
 /*
  * Create and destroy functions for JSContext, which is manually allocated
  * and exclusively owned.
  */
 extern JSContext*
 NewContext(uint32_t maxBytes, uint32_t maxNurseryBytes, JSRuntime* parentRuntime);
 
-extern JSContext*
-NewCooperativeContext(JSContext* siblingContext);
-
-extern void
-YieldCooperativeContext(JSContext* cx);
-
-extern void
-ResumeCooperativeContext(JSContext* cx);
-
 extern void
 DestroyContext(JSContext* cx);
 
 enum ErrorArgumentsType {
     ArgumentsAreUnicode,
     ArgumentsAreASCII,
     ArgumentsAreLatin1,
     ArgumentsAreUTF8
--- a/js/src/vm/JSScript.cpp
+++ b/js/src/vm/JSScript.cpp
@@ -4123,37 +4123,34 @@ JSScript::argumentsOptimizationFailed(JS
      * MagicValue(JS_OPTIMIZED_ARGUMENTS) on the stack. However, there are
      * three things that need fixup:
      *  - there may be any number of activations of this script that don't have
      *    an argsObj that now need one.
      *  - jit code compiled (and possible active on the stack) with the static
      *    assumption of !script->needsArgsObj();
      *  - type inference data for the script assuming script->needsArgsObj
      */
-    JSRuntime::AutoProhibitActiveContextChange apacc(cx->runtime());
-    for (const CooperatingContext& target : cx->runtime()->cooperatingContexts()) {
-        for (AllScriptFramesIter i(cx, target); !i.done(); ++i) {
-            /*
-             * We cannot reliably create an arguments object for Ion activations of
-             * this script.  To maintain the invariant that "script->needsArgsObj
-             * implies fp->hasArgsObj", the Ion bail mechanism will create an
-             * arguments object right after restoring the BaselineFrame and before
-             * entering Baseline code (in jit::FinishBailoutToBaseline).
-             */
-            if (i.isIon())
-                continue;
-            AbstractFramePtr frame = i.abstractFramePtr();
-            if (frame.isFunctionFrame() && frame.script() == script) {
-                /* We crash on OOM since cleaning up here would be complicated. */
-                AutoEnterOOMUnsafeRegion oomUnsafe;
-                ArgumentsObject* argsobj = ArgumentsObject::createExpected(cx, frame);
-                if (!argsobj)
-                    oomUnsafe.crash("JSScript::argumentsOptimizationFailed");
-                SetFrameArgumentsObject(cx, frame, script, argsobj);
-            }
+    for (AllScriptFramesIter i(cx); !i.done(); ++i) {
+        /*
+         * We cannot reliably create an arguments object for Ion activations of
+         * this script.  To maintain the invariant that "script->needsArgsObj
+         * implies fp->hasArgsObj", the Ion bail mechanism will create an
+         * arguments object right after restoring the BaselineFrame and before
+         * entering Baseline code (in jit::FinishBailoutToBaseline).
+         */
+        if (i.isIon())
+            continue;
+        AbstractFramePtr frame = i.abstractFramePtr();
+        if (frame.isFunctionFrame() && frame.script() == script) {
+            /* We crash on OOM since cleaning up here would be complicated. */
+            AutoEnterOOMUnsafeRegion oomUnsafe;
+            ArgumentsObject* argsobj = ArgumentsObject::createExpected(cx, frame);
+            if (!argsobj)
+                oomUnsafe.crash("JSScript::argumentsOptimizationFailed");
+            SetFrameArgumentsObject(cx, frame, script, argsobj);
         }
     }
 
     return true;
 }
 
 bool
 JSScript::formalIsAliased(unsigned argSlot)
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -87,22 +87,17 @@ ReturnZeroSize(const void* p)
 }
 
 JSRuntime::JSRuntime(JSRuntime* parentRuntime)
   : parentRuntime(parentRuntime),
 #ifdef DEBUG
     updateChildRuntimeCount(parentRuntime),
     initialized_(false),
 #endif
-    activeContext_(nullptr),
-    activeContextChangeProhibited_(0),
-    singleThreadedExecutionRequired_(0),
-    startingSingleThreadedExecution_(false),
-    beginSingleThreadedExecutionCallback(nullptr),
-    endSingleThreadedExecutionCallback(nullptr),
+    mainContext_(nullptr),
     profilerSampleBufferRangeStart_(0),
     telemetryCallback(nullptr),
     consumeStreamCallback(nullptr),
     readableStreamDataRequestCallback(nullptr),
     readableStreamWriteIntoReadRequestCallback(nullptr),
     readableStreamCancelCallback(nullptr),
     readableStreamClosedCallback(nullptr),
     readableStreamErroredCallback(nullptr),
@@ -206,19 +201,17 @@ JSRuntime::init(JSContext* cx, uint32_t 
 #ifdef DEBUG
     MOZ_ASSERT(!initialized_);
     initialized_ = true;
 #endif
 
     if (CanUseExtraThreads() && !EnsureHelperThreadsInitialized())
         return false;
 
-    activeContext_ = cx;
-    if (!cooperatingContexts().append(cx))
-        return false;
+    mainContext_ = cx;
 
     defaultFreeOp_ = js_new<js::FreeOp>(this);
     if (!defaultFreeOp_)
         return false;
 
     if (!gc.init(maxbytes, maxNurseryBytes))
         return false;
 
@@ -343,99 +336,16 @@ JSRuntime::destroyRuntime()
     js_free(defaultLocale);
     js_delete(jitRuntime_.ref());
 
 #ifdef DEBUG
     initialized_ = false;
 #endif
 }
 
-static void
-CheckCanChangeActiveContext(JSRuntime* rt)
-{
-    // The runtime might not currently have an active context, in which case
-    // the accesses below to ActiveThreadData data would not normally be
-    // allowed. Suppress protected data checks so these accesses will be
-    // tolerated --- if the active context is null then we're about to set it
-    // to the current thread.
-    AutoNoteSingleThreadedRegion anstr;
-
-    MOZ_RELEASE_ASSERT(!rt->activeContextChangeProhibited());
-    MOZ_RELEASE_ASSERT(!rt->activeContext() || rt->gc.canChangeActiveContext(rt->activeContext()));
-
-    if (rt->singleThreadedExecutionRequired()) {
-        for (ZoneGroupsIter group(rt); !group.done(); group.next())
-            MOZ_RELEASE_ASSERT(group->ownerContext().context() == nullptr);
-    }
-}
-
-void
-JSRuntime::setActiveContext(JSContext* cx)
-{
-    CheckCanChangeActiveContext(this);
-    MOZ_ASSERT_IF(cx, cx->isCooperativelyScheduled());
-
-    activeContext_ = cx;
-}
-
-void
-JSRuntime::setNewbornActiveContext(JSContext* cx)
-{
-    CheckCanChangeActiveContext(this);
-
-    activeContext_ = cx;
-
-    AutoEnterOOMUnsafeRegion oomUnsafe;
-    if (!cooperatingContexts().append(cx))
-        oomUnsafe.crash("Add cooperating context");
-}
-
-void
-JSRuntime::deleteActiveContext(JSContext* cx)
-{
-    CheckCanChangeActiveContext(this);
-    MOZ_ASSERT(cx == activeContext());
-
-    js_delete_poison(cx);
-    activeContext_ = nullptr;
-}
-
-bool
-JSRuntime::beginSingleThreadedExecution(JSContext* cx)
-{
-    if (singleThreadedExecutionRequired_ == 0) {
-        if (startingSingleThreadedExecution_)
-            return false;
-        startingSingleThreadedExecution_ = true;
-        if (beginSingleThreadedExecutionCallback)
-            beginSingleThreadedExecutionCallback(cx);
-        MOZ_ASSERT(startingSingleThreadedExecution_);
-        startingSingleThreadedExecution_ = false;
-    }
-
-    singleThreadedExecutionRequired_++;
-
-    for (ZoneGroupsIter group(this); !group.done(); group.next()) {
-        MOZ_RELEASE_ASSERT(group->ownedByCurrentThread() ||
-                           group->ownerContext().context() == nullptr);
-    }
-
-    return true;
-}
-
-void
-JSRuntime::endSingleThreadedExecution(JSContext* cx)
-{
-    MOZ_ASSERT(singleThreadedExecutionRequired_);
-    if (--singleThreadedExecutionRequired_ == 0) {
-        if (endSingleThreadedExecutionCallback)
-            endSingleThreadedExecutionCallback(cx);
-    }
-}
-
 void
 JSRuntime::addTelemetry(int id, uint32_t sample, const char* key)
 {
     if (telemetryCallback)
         (*telemetryCallback)(id, sample, key);
 }
 
 void
@@ -469,27 +379,25 @@ JSRuntime::addSizeOfIncludingThis(mozill
     }
 
     if (!parentRuntime) {
         rtSizes->atomsTable += mallocSizeOf(staticStrings);
         rtSizes->atomsTable += mallocSizeOf(commonNames);
         rtSizes->atomsTable += permanentAtoms->sizeOfIncludingThis(mallocSizeOf);
     }
 
-    for (const CooperatingContext& target : cooperatingContexts()) {
-        JSContext* cx = target.context();
-        rtSizes->contexts += mallocSizeOf(cx);
-        rtSizes->contexts += cx->sizeOfExcludingThis(mallocSizeOf);
-        rtSizes->temporary += cx->tempLifoAlloc().sizeOfExcludingThis(mallocSizeOf);
-        rtSizes->interpreterStack += cx->interpreterStack().sizeOfExcludingThis(mallocSizeOf);
+    JSContext* cx = mainContextFromAnyThread();
+    rtSizes->contexts += mallocSizeOf(cx);
+    rtSizes->contexts += cx->sizeOfExcludingThis(mallocSizeOf);
+    rtSizes->temporary += cx->tempLifoAlloc().sizeOfExcludingThis(mallocSizeOf);
+    rtSizes->interpreterStack += cx->interpreterStack().sizeOfExcludingThis(mallocSizeOf);
 #ifdef JS_TRACE_LOGGING
-        if (cx->traceLogger)
-            rtSizes->tracelogger += cx->traceLogger->sizeOfIncludingThis(mallocSizeOf);
+    if (cx->traceLogger)
+        rtSizes->tracelogger += cx->traceLogger->sizeOfIncludingThis(mallocSizeOf);
 #endif
-    }
 
     if (MathCache* cache = caches().maybeGetMathCache())
         rtSizes->mathCache += cache->sizeOfIncludingThis(mallocSizeOf);
 
     rtSizes->uncompressedSourceCache +=
         caches().uncompressedSourceCache.sizeOfExcludingThis(mallocSizeOf);
 
     rtSizes->gc.nurseryCommitted += gc.nursery().sizeOfHeapCommitted();
@@ -624,17 +532,17 @@ JSContext::handleInterrupt()
 }
 
 bool
 JSRuntime::setDefaultLocale(const char* locale)
 {
     if (!locale)
         return false;
     resetDefaultLocale();
-    defaultLocale = JS_strdup(activeContextFromOwnThread(), locale);
+    defaultLocale = JS_strdup(mainContextFromOwnThread(), locale);
     return defaultLocale != nullptr;
 }
 
 void
 JSRuntime::resetDefaultLocale()
 {
     js_free(defaultLocale);
     defaultLocale = nullptr;
@@ -647,17 +555,17 @@ JSRuntime::getDefaultLocale()
         return defaultLocale;
 
     const char* locale = setlocale(LC_ALL, nullptr);
 
     // convert to a well-formed BCP 47 language tag
     if (!locale || !strcmp(locale, "C"))
         locale = "und";
 
-    char* lang = JS_strdup(activeContextFromOwnThread(), locale);
+    char* lang = JS_strdup(mainContextFromOwnThread(), locale);
     if (!lang)
         return nullptr;
 
     char* p;
     if ((p = strchr(lang, '.')))
         *p = '\0';
     while ((p = strchr(lang, '_')))
         *p = '-';
@@ -903,25 +811,25 @@ JSRuntime::clearUsedByHelperThread(Zone*
     JSContext* cx = TlsContext.get();
     if (gc.fullGCForAtomsRequested() && cx->canCollectAtoms())
         gc.triggerFullGCForAtoms(cx);
 }
 
 bool
 js::CurrentThreadCanAccessRuntime(const JSRuntime* rt)
 {
-    return rt->activeContext() == TlsContext.get();
+    return rt->mainContextFromAnyThread() == TlsContext.get();
 }
 
 bool
 js::CurrentThreadCanAccessZone(Zone* zone)
 {
     // Helper thread zones can only be used by their owning thread.
     if (zone->usedByHelperThread())
-        return zone->group()->ownedByCurrentThread();
+        return zone->group()->ownedByCurrentHelperThread();
 
     // Other zones can only be accessed by the runtime's active context.
     return CurrentThreadCanAccessRuntime(zone->runtime_);
 }
 
 #ifdef DEBUG
 bool
 js::CurrentThreadIsPerformingGC()
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -100,43 +100,28 @@ class CompileRuntime;
 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.
+// - The main thread is capable of running JS. There's at most one main thread
+//   per runtime.
 //
 // - 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.
+//   on the main thread (or main threads, since all runtimes in a process share
+//   helper 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.
 
 } /* namespace js */
 
 namespace JS {
 struct RuntimeSizes;
 } // namespace JS
 
 /* Various built-in or commonly-used names pinned on first context. */
@@ -273,79 +258,25 @@ struct JSRuntime : public js::MallocProv
     AutoUpdateChildRuntimeCount updateChildRuntimeCount;
 #endif
 
   private:
 #ifdef DEBUG
     js::WriteOnceData<bool> initialized_;
 #endif
 
-    // The context for the thread which currently has exclusive access to most
-    // contents of the runtime. When execution on the runtime is cooperatively
-    // scheduled, this is the thread which is currently running.
-    mozilla::Atomic<JSContext*, mozilla::ReleaseAcquire> activeContext_;
-
-    // All contexts participating in cooperative scheduling. All threads other
-    // than |activeContext_| are suspended.
-    js::ActiveThreadData<js::Vector<js::CooperatingContext, 4, js::SystemAllocPolicy>> cooperatingContexts_;
-
-    // Count of AutoProhibitActiveContextChange instances on the active context.
-    js::ActiveThreadData<size_t> activeContextChangeProhibited_;
-
-    // Count of beginSingleThreadedExecution() calls that have occurred with no
-    // matching endSingleThreadedExecution().
-    js::ActiveThreadData<size_t> singleThreadedExecutionRequired_;
-
-    // Whether some thread has called beginSingleThreadedExecution() and we are
-    // in the associated callback (which may execute JS on other threads).
-    js::ActiveThreadData<bool> startingSingleThreadedExecution_;
+    // The JSContext* for the runtime's main thread. Immutable after this is set
+    // in JSRuntime::init.
+    JSContext* mainContext_;
 
   public:
-    JSContext* activeContext() const { return activeContext_; }
-    const void* addressOfActiveContext() { return &activeContext_; }
-
-    void setActiveContext(JSContext* cx);
-    void setNewbornActiveContext(JSContext* cx);
-    void deleteActiveContext(JSContext* cx);
-
-    inline JSContext* activeContextFromOwnThread();
-
-    js::Vector<js::CooperatingContext, 4, js::SystemAllocPolicy>& cooperatingContexts() {
-        return cooperatingContexts_.ref();
-    }
-
-    class MOZ_RAII AutoProhibitActiveContextChange
-    {
-        JSRuntime* rt;
+    JSContext* mainContextFromAnyThread() const { return mainContext_; }
+    const void* addressOfMainContext() { return &mainContext_; }
 
-      public:
-        explicit AutoProhibitActiveContextChange(JSRuntime* rt)
-          : rt(rt)
-        {
-            rt->activeContextChangeProhibited_++;
-        }
-
-        ~AutoProhibitActiveContextChange()
-        {
-            rt->activeContextChangeProhibited_--;
-        }
-    };
-
-    bool activeContextChangeProhibited() { return activeContextChangeProhibited_; }
-    bool singleThreadedExecutionRequired() { return singleThreadedExecutionRequired_; }
-
-    js::ActiveThreadData<JS::BeginSingleThreadedExecutionCallback> beginSingleThreadedExecutionCallback;
-    js::ActiveThreadData<JS::EndSingleThreadedExecutionCallback> endSingleThreadedExecutionCallback;
-
-    // Ensure there is only a single thread interacting with this runtime.
-    // beginSingleThreadedExecution() returns false if some context has already
-    // started forcing this runtime to be single threaded. Calls to these
-    // functions must be balanced.
-    bool beginSingleThreadedExecution(JSContext* cx);
-    void endSingleThreadedExecution(JSContext* cx);
+    inline JSContext* mainContextFromOwnThread();
 
     /*
      * The start of the range stored in the profiler sample buffer, as measured
      * after the most recent sample.
      * All JitcodeGlobalTable entries referenced from a given sample are
      * assigned the buffer position of the START of the sample. The buffer
      * entries that reference the JitcodeGlobalTable entries will only ever be
      * read from the buffer while the entire sample is still inside the buffer;
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -399,17 +399,17 @@ SavedFrame::protoAccessors[] = {
 
 /* static */ void
 SavedFrame::finalize(FreeOp* fop, JSObject* obj)
 {
     MOZ_ASSERT(fop->onActiveCooperatingThread());
     JSPrincipals* p = obj->as<SavedFrame>().getPrincipals();
     if (p) {
         JSRuntime* rt = obj->runtimeFromActiveCooperatingThread();
-        JS_DropPrincipals(rt->activeContextFromOwnThread(), p);
+        JS_DropPrincipals(rt->mainContextFromOwnThread(), p);
     }
 }
 
 JSAtom*
 SavedFrame::getSource()
 {
     const Value& v = getReservedSlot(JSSLOT_SOURCE);
     JSString* s = v.toString();
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -401,19 +401,19 @@ TraceInterpreterActivation(JSTracer* trc
 {
     for (InterpreterFrameIterator frames(act); !frames.done(); ++frames) {
         InterpreterFrame* fp = frames.frame();
         fp->trace(trc, frames.sp(), frames.pc());
     }
 }
 
 void
-js::TraceInterpreterActivations(JSContext* cx, const CooperatingContext& target, JSTracer* trc)
+js::TraceInterpreterActivations(JSContext* cx, JSTracer* trc)
 {
-    for (ActivationIterator iter(cx, target); !iter.done(); ++iter) {
+    for (ActivationIterator iter(cx); !iter.done(); ++iter) {
         Activation* act = iter.activation();
         if (act->isInterpreter())
             TraceInterpreterActivation(trc, act->asInterpreter());
     }
 }
 
 /*****************************************************************************/
 
@@ -730,52 +730,29 @@ FrameIter::Data::Data(JSContext* cx, Deb
     state_(DONE),
     pc_(nullptr),
     interpFrames_(nullptr),
     activations_(cx),
     ionInlineFrameNo_(0)
 {
 }
 
-FrameIter::Data::Data(JSContext* cx, const CooperatingContext& target,
-                      DebuggerEvalOption debuggerEvalOption)
-  : cx_(cx),
-    debuggerEvalOption_(debuggerEvalOption),
-    principals_(nullptr),
-    state_(DONE),
-    pc_(nullptr),
-    interpFrames_(nullptr),
-    activations_(cx, target),
-    ionInlineFrameNo_(0)
-{
-}
-
 FrameIter::Data::Data(const FrameIter::Data& other)
   : cx_(other.cx_),
     debuggerEvalOption_(other.debuggerEvalOption_),
     principals_(other.principals_),
     state_(other.state_),
     pc_(other.pc_),
     interpFrames_(other.interpFrames_),
     activations_(other.activations_),
     jitFrames_(other.jitFrames_),
     ionInlineFrameNo_(other.ionInlineFrameNo_)
 {
 }
 
-FrameIter::FrameIter(JSContext* cx, const CooperatingContext& target,
-                     DebuggerEvalOption debuggerEvalOption)
-  : data_(cx, target, debuggerEvalOption),
-    ionInlineFrames_(cx, (js::jit::JSJitFrameIter*) nullptr)
-{
-    // settleOnActivation can only GC if principals are given.
-    JS::AutoSuppressGCAnalysis nogc;
-    settleOnActivation();
-}
-
 FrameIter::FrameIter(JSContext* cx, DebuggerEvalOption debuggerEvalOption)
   : data_(cx, debuggerEvalOption, nullptr),
     ionInlineFrames_(cx, (js::jit::JSJitFrameIter*) nullptr)
 {
     // settleOnActivation can only GC if principals are given.
     JS::AutoSuppressGCAnalysis nogc;
     settleOnActivation();
 }
@@ -1805,32 +1782,16 @@ Activation::unregisterProfiling()
 }
 
 ActivationIterator::ActivationIterator(JSContext* cx)
   : activation_(cx->activation_)
 {
     MOZ_ASSERT(cx == TlsContext.get());
 }
 
-ActivationIterator::ActivationIterator(JSContext* cx, const CooperatingContext& target)
-{
-    MOZ_ASSERT(cx == TlsContext.get());
-
-    // If target was specified --- even if it is the same as cx itself --- then
-    // we must be in a scope where changes of the active context are prohibited.
-    // Otherwise our state would be corrupted if the target thread resumed
-    // execution while we are iterating over its state.
-    MOZ_ASSERT(cx->runtime()->activeContextChangeProhibited() ||
-               !cx->runtime()->gc.canChangeActiveContext(cx));
-
-    // Tolerate a null target context, in case we are iterating over the
-    // activations for a zone group that is not in use by any thread.
-    activation_ = target.context() ? target.context()->activation_.ref() : nullptr;
-}
-
 ActivationIterator&
 ActivationIterator::operator++()
 {
     MOZ_ASSERT(activation_);
     activation_ = activation_->prev();
     return *this;
 }
 
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -908,34 +908,17 @@ class InterpreterStack
 
     inline void purge(JSRuntime* rt);
 
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
         return allocator_.sizeOfExcludingThis(mallocSizeOf);
     }
 };
 
-// CooperatingContext is a wrapper for a JSContext that is participating in
-// cooperative scheduling and may be different from the current thread. It is
-// in place to make it clearer when we might be operating on another thread,
-// and harder to accidentally pass in another thread's context to an API that
-// expects the current thread's context.
-class CooperatingContext
-{
-    JSContext* cx;
-
-  public:
-    explicit CooperatingContext(JSContext* cx) : cx(cx) {}
-    JSContext* context() const { return cx; }
-
-    // For &cx. The address should not be taken for other CooperatingContexts.
-    friend class ZoneGroup;
-};
-
-void TraceInterpreterActivations(JSContext* cx, const CooperatingContext& target, JSTracer* trc);
+void TraceInterpreterActivations(JSContext* cx, JSTracer* trc);
 
 /*****************************************************************************/
 
 /** Base class for all function call args. */
 class AnyInvokeArgs : public JS::CallArgs
 {
 };
 
@@ -1601,21 +1584,16 @@ class InterpreterActivation : public Act
 class ActivationIterator
 {
   protected:
     Activation* activation_;
 
   public:
     explicit ActivationIterator(JSContext* cx);
 
-    // ActivationIterator can be used to iterate over a different thread's
-    // activations, for use by the GC, invalidation, and other operations that
-    // don't have a user-visible effect on the target thread's JS behavior.
-    ActivationIterator(JSContext* cx, const CooperatingContext& target);
-
     ActivationIterator& operator++();
 
     Activation* operator->() const {
         return activation_;
     }
     Activation* activation() const {
         return activation_;
     }
@@ -1847,22 +1825,16 @@ class JitActivationIterator : public Act
 
   public:
     explicit JitActivationIterator(JSContext* cx)
       : ActivationIterator(cx)
     {
         settle();
     }
 
-    JitActivationIterator(JSContext* cx, const CooperatingContext& target)
-      : ActivationIterator(cx, target)
-    {
-        settle();
-    }
-
     JitActivationIterator& operator++() {
         ActivationIterator::operator++();
         settle();
         return *this;
     }
 };
 
 } // namespace jit
@@ -2043,23 +2015,21 @@ class FrameIter
 
         InterpreterFrameIterator interpFrames_;
         ActivationIterator activations_;
 
         JitFrameIter jitFrames_;
         unsigned ionInlineFrameNo_;
 
         Data(JSContext* cx, DebuggerEvalOption debuggerEvalOption, JSPrincipals* principals);
-        Data(JSContext* cx, const CooperatingContext& target, DebuggerEvalOption debuggerEvalOption);
         Data(const Data& other);
     };
 
     explicit FrameIter(JSContext* cx,
                        DebuggerEvalOption = FOLLOW_DEBUGGER_EVAL_PREV_LINK);
-    FrameIter(JSContext* cx, const CooperatingContext&, DebuggerEvalOption);
     FrameIter(JSContext* cx, DebuggerEvalOption, JSPrincipals*);
     FrameIter(const FrameIter& iter);
     MOZ_IMPLICIT FrameIter(const Data& data);
     MOZ_IMPLICIT FrameIter(AbstractFramePtr frame);
 
     bool done() const { return data_.state_ == DONE; }
 
     // -------------------------------------------------------
@@ -2217,24 +2187,16 @@ class ScriptFrameIter : public FrameIter
     explicit ScriptFrameIter(JSContext* cx,
                              DebuggerEvalOption debuggerEvalOption = FOLLOW_DEBUGGER_EVAL_PREV_LINK)
       : FrameIter(cx, debuggerEvalOption)
     {
         settle();
     }
 
     ScriptFrameIter(JSContext* cx,
-                     const CooperatingContext& target,
-                     DebuggerEvalOption debuggerEvalOption)
-       : FrameIter(cx, target, debuggerEvalOption)
-    {
-        settle();
-    }
-
-    ScriptFrameIter(JSContext* cx,
                     DebuggerEvalOption debuggerEvalOption,
                     JSPrincipals* prin)
       : FrameIter(cx, debuggerEvalOption, prin)
     {
         settle();
     }
 
     ScriptFrameIter(const ScriptFrameIter& iter) : FrameIter(iter) { settle(); }
@@ -2346,20 +2308,16 @@ class AllFramesIter : public FrameIter
  * See also AllFramesIter and ScriptFrameIter.
  */
 class AllScriptFramesIter : public ScriptFrameIter
 {
   public:
     explicit AllScriptFramesIter(JSContext* cx)
       : ScriptFrameIter(cx, ScriptFrameIter::IGNORE_DEBUGGER_EVAL_PREV_LINK)
     {}
-
-    explicit AllScriptFramesIter(JSContext* cx, const CooperatingContext& target)
-      : ScriptFrameIter(cx, target, ScriptFrameIter::IGNORE_DEBUGGER_EVAL_PREV_LINK)
-    {}
 };
 
 /* Popular inline definitions. */
 
 inline JSScript*
 FrameIter::script() const
 {
     MOZ_ASSERT(!done());
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -3974,102 +3974,99 @@ TypeNewScript::rollbackPartiallyInitiali
 
     if (!initializerList)
         return false;
 
     bool found = false;
 
     RootedFunction function(cx, this->function());
     Vector<uint32_t, 32> pcOffsets(cx);
-    JSRuntime::AutoProhibitActiveContextChange apacc(cx->runtime());
-    for (const CooperatingContext& target : cx->runtime()->cooperatingContexts()) {
-        for (AllScriptFramesIter iter(cx, target); !iter.done(); ++iter) {
-            {
-                AutoEnterOOMUnsafeRegion oomUnsafe;
-                if (!pcOffsets.append(iter.script()->pcToOffset(iter.pc())))
-                    oomUnsafe.crash("rollbackPartiallyInitializedObjects");
-            }
-
-            if (!iter.isConstructing() || !iter.matchCallee(cx, function))
-                continue;
-
-            // Derived class constructors initialize their this-binding later and
-            // we shouldn't run the definite properties analysis on them.
-            MOZ_ASSERT(!iter.script()->isDerivedClassConstructor());
-
-            Value thisv = iter.thisArgument(cx);
-            if (!thisv.isObject() ||
-                thisv.toObject().hasLazyGroup() ||
-                thisv.toObject().group() != group)
-            {
-                continue;
-            }
-
-            if (thisv.toObject().is<UnboxedPlainObject>()) {
-                AutoEnterOOMUnsafeRegion oomUnsafe;
-                if (!UnboxedPlainObject::convertToNative(cx, &thisv.toObject()))
-                    oomUnsafe.crash("rollbackPartiallyInitializedObjects");
-            }
-
-            // Found a matching frame.
-            RootedPlainObject obj(cx, &thisv.toObject().as<PlainObject>());
-
-            // Whether all identified 'new' properties have been initialized.
-            bool finished = false;
-
-            // If not finished, number of properties that have been added.
-            uint32_t numProperties = 0;
-
-            // Whether the current SETPROP is within an inner frame which has
-            // finished entirely.
-            bool pastProperty = false;
-
-            // Index in pcOffsets of the outermost frame.
-            int callDepth = pcOffsets.length() - 1;
-
-            // Index in pcOffsets of the frame currently being checked for a SETPROP.
-            int setpropDepth = callDepth;
-
-            for (Initializer* init = initializerList;; init++) {
-                if (init->kind == Initializer::SETPROP) {
-                    if (!pastProperty && pcOffsets[setpropDepth] < init->offset) {
-                        // Have not yet reached this setprop.
-                        break;
-                    }
-                    // This setprop has executed, reset state for the next one.
-                    numProperties++;
-                    pastProperty = false;
-                    setpropDepth = callDepth;
-                } else if (init->kind == Initializer::SETPROP_FRAME) {
-                    if (!pastProperty) {
-                        if (pcOffsets[setpropDepth] < init->offset) {
-                            // Have not yet reached this inner call.
-                            break;
-                        } else if (pcOffsets[setpropDepth] > init->offset) {
-                            // Have advanced past this inner call.
-                            pastProperty = true;
-                        } else if (setpropDepth == 0) {
-                            // Have reached this call but not yet in it.
-                            break;
-                        } else {
-                            // Somewhere inside this inner call.
-                            setpropDepth--;
-                        }
-                    }
-                } else {
-                    MOZ_ASSERT(init->kind == Initializer::DONE);
-                    finished = true;
+    for (AllScriptFramesIter iter(cx); !iter.done(); ++iter) {
+        {
+            AutoEnterOOMUnsafeRegion oomUnsafe;
+            if (!pcOffsets.append(iter.script()->pcToOffset(iter.pc())))
+                oomUnsafe.crash("rollbackPartiallyInitializedObjects");
+        }
+
+        if (!iter.isConstructing() || !iter.matchCallee(cx, function))
+            continue;
+
+        // Derived class constructors initialize their this-binding later and
+        // we shouldn't run the definite properties analysis on them.
+        MOZ_ASSERT(!iter.script()->isDerivedClassConstructor());
+
+        Value thisv = iter.thisArgument(cx);
+        if (!thisv.isObject() ||
+            thisv.toObject().hasLazyGroup() ||
+            thisv.toObject().group() != group)
+        {
+            continue;
+        }
+
+        if (thisv.toObject().is<UnboxedPlainObject>()) {
+            AutoEnterOOMUnsafeRegion oomUnsafe;
+            if (!UnboxedPlainObject::convertToNative(cx, &thisv.toObject()))
+                oomUnsafe.crash("rollbackPartiallyInitializedObjects");
+        }
+
+        // Found a matching frame.
+        RootedPlainObject obj(cx, &thisv.toObject().as<PlainObject>());
+
+        // Whether all identified 'new' properties have been initialized.
+        bool finished = false;
+
+        // If not finished, number of properties that have been added.
+        uint32_t numProperties = 0;
+
+        // Whether the current SETPROP is within an inner frame which has
+        // finished entirely.
+        bool pastProperty = false;
+
+        // Index in pcOffsets of the outermost frame.
+        int callDepth = pcOffsets.length() - 1;
+
+        // Index in pcOffsets of the frame currently being checked for a SETPROP.
+        int setpropDepth = callDepth;
+
+        for (Initializer* init = initializerList;; init++) {
+            if (init->kind == Initializer::SETPROP) {
+                if (!pastProperty && pcOffsets[setpropDepth] < init->offset) {
+                    // Have not yet reached this setprop.
                     break;
                 }
+                // This setprop has executed, reset state for the next one.
+                numProperties++;
+                pastProperty = false;
+                setpropDepth = callDepth;
+            } else if (init->kind == Initializer::SETPROP_FRAME) {
+                if (!pastProperty) {
+                    if (pcOffsets[setpropDepth] < init->offset) {
+                        // Have not yet reached this inner call.
+                        break;
+                    } else if (pcOffsets[setpropDepth] > init->offset) {
+                        // Have advanced past this inner call.
+                        pastProperty = true;
+                    } else if (setpropDepth == 0) {
+                        // Have reached this call but not yet in it.
+                        break;
+                    } else {
+                        // Somewhere inside this inner call.
+                        setpropDepth--;
+                    }
+                }
+            } else {
+                MOZ_ASSERT(init->kind == Initializer::DONE);
+                finished = true;
+                break;
             }
-
-            if (!finished) {
-                (void) NativeObject::rollbackProperties(cx, obj, numProperties);
-                found = true;
-            }
+        }
+
+        if (!finished) {
+            (void) NativeObject::rollbackProperties(cx, obj, numProperties);
+            found = true;
         }
     }
 
     return found;
 }
 
 void
 TypeNewScript::trace(JSTracer* trc)
@@ -4614,17 +4611,16 @@ AutoClearTypeInferenceStateOnOOM::AutoCl
 
 AutoClearTypeInferenceStateOnOOM::~AutoClearTypeInferenceStateOnOOM()
 {
     zone->types.setSweepingTypes(false);
 
     if (oom) {
         JSRuntime* rt = zone->runtimeFromActiveCooperatingThread();
         js::CancelOffThreadIonCompile(rt);
-        JSRuntime::AutoProhibitActiveContextChange apacc(rt);
         zone->setPreservingCode(false);
         zone->discardJitCode(rt->defaultFreeOp(), /* discardBaselineCode = */ false);
         zone->types.clearAllNewScriptsOnOOM();
     }
 }
 
 #ifdef DEBUG
 void
--- a/js/src/wasm/WasmInstance.cpp
+++ b/js/src/wasm/WasmInstance.cpp
@@ -82,25 +82,25 @@ class SigIdSet
             map_.remove(p);
         }
     }
 };
 
 ExclusiveData<SigIdSet>* sigIdSet = nullptr;
 
 bool
-js::wasm::InitInstanceStaticData()
+js::wasm::InitSignatureSet()
 {
     MOZ_ASSERT(!sigIdSet);
     sigIdSet = js_new<ExclusiveData<SigIdSet>>(mutexid::WasmSigIdSet);
     return sigIdSet != nullptr;
 }
 
 void
-js::wasm::ShutDownInstanceStaticData()
+js::wasm::ReleaseSignatureSet()
 {
     MOZ_ASSERT(sigIdSet);
     js_delete(sigIdSet);
     sigIdSet = nullptr;
 }
 
 const void**
 Instance::addressOfSigId(const SigIdDesc& sigId) const
--- a/js/src/wasm/WasmInstance.h
+++ b/js/src/wasm/WasmInstance.h
@@ -168,15 +168,15 @@ class Instance
     static uint32_t currentMemory_i32(Instance* instance);
     static int32_t wait_i32(Instance* instance, uint32_t byteOffset, int32_t value, int64_t timeout);
     static int32_t wait_i64(Instance* instance, uint32_t byteOffset, int64_t value, int64_t timeout);
     static int32_t wake(Instance* instance, uint32_t byteOffset, int32_t count);
 };
 
 typedef UniquePtr<Instance> UniqueInstance;
 
-bool InitInstanceStaticData();
-void ShutDownInstanceStaticData();
+bool InitSignatureSet();
+void ReleaseSignatureSet();
 
 } // namespace wasm
 } // namespace js
 
 #endif // wasm_instance_h
--- a/js/src/wasm/WasmJS.cpp
+++ b/js/src/wasm/WasmJS.cpp
@@ -1017,52 +1017,59 @@ WasmInstanceObject::create(JSContext* cx
     }
 
     UniquePtr<ScopeMap> scopes = js::MakeUnique<ScopeMap>(cx->zone());
     if (!scopes || !scopes->init()) {
         ReportOutOfMemory(cx);
         return nullptr;
     }
 
-    AutoSetNewObjectMetadata metadata(cx);
-    RootedWasmInstanceObject obj(cx, NewObjectWithGivenProto<WasmInstanceObject>(cx, proto));
-    if (!obj)
-        return nullptr;
-
     uint32_t indirectGlobals = 0;
 
 #if defined(ENABLE_WASM_GLOBAL) && defined(EARLY_BETA_OR_EARLIER)
     for (uint32_t i = 0; i < globalObjs.length(); i++) {
         if (globalObjs[i] && globals[i].isIndirect())
             indirectGlobals++;
     }
 #endif
 
-    Rooted<UniquePtr<WasmGlobalObjectVector>> indirectGlobalObjs(cx, js::MakeUnique<WasmGlobalObjectVector>());
-    if (!indirectGlobalObjs || !indirectGlobalObjs->resize(indirectGlobals))
+    Rooted<UniquePtr<WasmGlobalObjectVector>> indirectGlobalObjs(cx,
+        js::MakeUnique<WasmGlobalObjectVector>());
+    if (!indirectGlobalObjs || !indirectGlobalObjs->resize(indirectGlobals)) {
+        ReportOutOfMemory(cx);
         return nullptr;
+    }
 
 #if defined(ENABLE_WASM_GLOBAL) && defined(EARLY_BETA_OR_EARLIER)
     {
         uint32_t next = 0;
         for (uint32_t i = 0; i < globalObjs.length(); i++) {
             if (globalObjs[i] && globals[i].isIndirect())
                 (*indirectGlobalObjs)[next++] = globalObjs[i];
         }
     }
 #endif
 
-    obj->setReservedSlot(EXPORTS_SLOT, PrivateValue(exports.release()));
-    obj->setReservedSlot(SCOPES_SLOT, PrivateValue(scopes.release()));
-    obj->setReservedSlot(GLOBALS_SLOT, PrivateValue(indirectGlobalObjs.release()));
-    obj->setReservedSlot(INSTANCE_SCOPE_SLOT, UndefinedValue());
-    MOZ_ASSERT(obj->isNewborn());
+    AutoSetNewObjectMetadata metadata(cx);
+    RootedWasmInstanceObject obj(cx, NewObjectWithGivenProto<WasmInstanceObject>(cx, proto));
+    if (!obj)
+        return nullptr;
 
     MOZ_ASSERT(obj->isTenured(), "assumed by WasmTableObject write barriers");
 
+    // Finalization assumes these slots are always initialized:
+    obj->initReservedSlot(EXPORTS_SLOT, PrivateValue(exports.release()));
+    obj->initReservedSlot(SCOPES_SLOT, PrivateValue(scopes.release()));
+    obj->initReservedSlot(GLOBALS_SLOT, PrivateValue(indirectGlobalObjs.release()));
+    obj->initReservedSlot(INSTANCE_SCOPE_SLOT, UndefinedValue());
+
+    // The INSTANCE_SLOT may not be initialized if Instance allocation fails,
+    // leading to an observable "newborn" state in tracing/finalization.
+    MOZ_ASSERT(obj->isNewborn());
+
     // Root the Instance via WasmInstanceObject before any possible GC.
     auto* instance = cx->new_<Instance>(cx,
                                         obj,
                                         code,
                                         Move(debug),
                                         Move(tlsData),
                                         memory,
                                         Move(tables),
--- a/js/src/wasm/WasmProcess.cpp
+++ b/js/src/wasm/WasmProcess.cpp
@@ -16,17 +16,19 @@
  * limitations under the License.
  */
 
 #include "wasm/WasmProcess.h"
 
 #include "mozilla/BinarySearch.h"
 
 #include "vm/MutexIDs.h"
+#include "wasm/WasmBuiltins.h"
 #include "wasm/WasmCode.h"
+#include "wasm/WasmInstance.h"
 
 using namespace js;
 using namespace wasm;
 
 using mozilla::BinarySearchIf;
 
 // Per-process map from values of program-counter (pc) to CodeSegments.
 //
@@ -240,13 +242,27 @@ wasm::LookupCodeSegment(const void* pc, 
 
 const Code*
 wasm::LookupCode(const void* pc, const CodeRange** cr /* = nullptr */)
 {
     const CodeSegment* found = LookupCodeSegment(pc, cr);
     return found ? &found->code() : nullptr;
 }
 
+bool
+wasm::Init()
+{
+    return InitSignatureSet();
+}
+
 void
-wasm::ShutDownProcessStaticData()
+wasm::ShutDown()
 {
+    // If there are live runtimes then we are already pretty much leaking the
+    // world, so to avoid spurious assertions (which are valid and valuable when
+    // there are not live JSRuntimes), don't bother releasing anything here.
+    if (JSRuntime::hasLiveRuntimes())
+        return;
+
+    ReleaseSignatureSet();
+    ReleaseBuiltinThunks();
     processCodeSegmentMap.freeAll();
 }
--- a/js/src/wasm/WasmProcess.h
+++ b/js/src/wasm/WasmProcess.h
@@ -47,15 +47,21 @@ extern mozilla::Atomic<bool> CodeExists;
 // via pc in the methods described above.
 
 bool
 RegisterCodeSegment(const CodeSegment* cs);
 
 void
 UnregisterCodeSegment(const CodeSegment* cs);
 
+// Called once before/after the last VM execution which could execute or compile
+// wasm.
+
+bool
+Init();
+
 void
-ShutDownProcessStaticData();
+ShutDown();
 
 } // namespace wasm
 } // namespace js
 
 #endif // wasm_process_h
--- a/js/xpconnect/src/ExportHelpers.cpp
+++ b/js/xpconnect/src/ExportHelpers.cpp
@@ -283,40 +283,42 @@ FunctionForwarder(JSContext* cx, unsigne
     FunctionForwarderOptions options(cx, optionsObj);
     if (!options.Parse())
         return false;
 
     // Grab and unwrap the underlying callable.
     RootedValue v(cx, js::GetFunctionNativeReserved(&args.callee(), 0));
     RootedObject unwrappedFun(cx, js::UncheckedUnwrap(&v.toObject()));
 
-    RootedObject thisObj(cx, args.isConstructing() ? nullptr : JS_THIS_OBJECT(cx, vp));
+    RootedValue thisVal(cx, NullValue());
+    if (!args.isConstructing()) {
+        thisVal.set(args.computeThis(cx));
+    }
+
     {
         // We manually implement the contents of CrossCompartmentWrapper::call
         // here, because certain function wrappers (notably content->nsEP) are
         // not callable.
         JSAutoCompartment ac(cx, unwrappedFun);
-
-        RootedValue thisVal(cx, ObjectOrNullValue(thisObj));
-        if (!CheckSameOriginArg(cx, options, thisVal) || !JS_WrapObject(cx, &thisObj))
+        if (!CheckSameOriginArg(cx, options, thisVal) || !JS_WrapValue(cx, &thisVal))
             return false;
 
         for (size_t n = 0;  n < args.length(); ++n) {
             if (!CheckSameOriginArg(cx, options, args[n]) || !JS_WrapValue(cx, args[n]))
                 return false;
         }
 
         RootedValue fval(cx, ObjectValue(*unwrappedFun));
         if (args.isConstructing()) {
             RootedObject obj(cx);
             if (!JS::Construct(cx, fval, args, &obj))
                 return false;
             args.rval().setObject(*obj);
         } else {
-            if (!JS_CallFunctionValue(cx, thisObj, fval, args, args.rval()))
+            if (!JS::Call(cx, thisVal, fval, args, args.rval()))
                 return false;
         }
     }
 
     // Rewrap the return value into our compartment.
     return JS_WrapValue(cx, args.rval());
 }
 
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -210,21 +210,23 @@ SandboxImport(JSContext* cx, unsigned ar
     JS_MarkCrossZoneIdValue(cx, StringValue(funname));
 
     RootedId id(cx);
     if (!JS_StringToId(cx, funname, &id))
         return false;
 
     // We need to resolve the this object, because this function is used
     // unbound and should still work and act on the original sandbox.
-    RootedObject thisObject(cx, JS_THIS_OBJECT(cx, vp));
-    if (!thisObject) {
+
+    RootedValue thisv(cx, args.computeThis(cx));
+    if (!thisv.isObject()) {
         XPCThrower::Throw(NS_ERROR_UNEXPECTED, cx);
         return false;
     }
+    RootedObject thisObject(cx, &thisv.toObject());
     if (!JS_SetPropertyById(cx, thisObject, id, args[0]))
         return false;
 
     args.rval().setUndefined();
     return true;
 }
 
 static bool
--- a/js/xpconnect/src/XPCShellImpl.cpp
+++ b/js/xpconnect/src/XPCShellImpl.cpp
@@ -332,21 +332,18 @@ Dump(JSContext* cx, unsigned argc, Value
     return true;
 }
 
 static bool
 Load(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
-    JS::Rooted<JSObject*> obj(cx, JS_THIS_OBJECT(cx, vp));
-    if (!obj)
-        return false;
-
-    if (!JS_IsGlobalObject(obj)) {
+    RootedValue thisv(cx, args.computeThis(cx));
+    if (!thisv.isObject() || !JS_IsGlobalObject(&thisv.toObject())) {
         JS_ReportErrorASCII(cx, "Trying to load() into a non-global object");
         return false;
     }
 
     RootedString str(cx);
     for (unsigned i = 0; i < args.length(); i++) {
         str = ToString(cx, args[i]);
         if (!str)
--- a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
@@ -64,19 +64,23 @@ ToStringGuts(XPCCallContext& ccx)
 }
 
 /***************************************************************************/
 
 static bool
 XPC_WN_Shared_ToString(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
-    if (!obj)
+
+    RootedValue thisv(cx, args.computeThis(cx));
+    if (!thisv.isObject()) {
+        JS_ReportErrorASCII(cx, "Called on incompatible |this|");
         return false;
+    }
+    RootedObject obj(cx, &thisv.toObject());
 
     XPCCallContext ccx(cx, obj);
     if (!ccx.IsValid())
         return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);
     ccx.SetName(ccx.GetContext()->GetStringID(XPCJSContext::IDX_TO_STRING));
     ccx.SetArgsAndResultPtr(args.length(), args.array(), vp);
     return ToStringGuts(ccx);
 }
@@ -171,19 +175,21 @@ GetDoubleWrappedJSObject(XPCCallContext&
 // This is the getter native function we use to handle 'wrappedJSObject' for
 // double wrapped JSObjects.
 
 static bool
 XPC_WN_DoubleWrappedGetter(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
-    RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
-    if (!obj)
+    if (!args.thisv().isObject()) {
+        JS_ReportErrorASCII(cx, "xpconnect double wrapped getter called on incompatible non-object");
         return false;
+    }
+    RootedObject obj(cx, &args.thisv().toObject());
 
     XPCCallContext ccx(cx, obj);
     XPCWrappedNative* wrapper = ccx.GetWrapper();
     THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
 
     MOZ_ASSERT(JS_TypeOfValue(cx, args.calleev()) == JSTYPE_FUNCTION, "bad function");
 
     RootedObject realObject(cx, GetDoubleWrappedJSObject(ccx, wrapper));
@@ -889,19 +895,22 @@ FixUpThisIfBroken(JSObject* obj, JSObjec
 
 bool
 XPC_WN_CallMethod(JSContext* cx, unsigned argc, Value* vp)
 {
     JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
     MOZ_ASSERT(JS_TypeOfValue(cx, args.calleev()) == JSTYPE_FUNCTION, "bad function");
     RootedObject funobj(cx, &args.callee());
 
-    RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
-    if (!obj)
+    RootedValue thisv(cx, args.computeThis(cx));
+    if (!thisv.isObject()) {
+        JS_ReportErrorASCII(cx, "Called on incompatible |this|");
         return false;
+    }
+    RootedObject obj(cx, &thisv.toObject());
 
     obj = FixUpThisIfBroken(obj, funobj);
     XPCCallContext ccx(cx, obj, funobj, JSID_VOIDHANDLE, args.length(),
                        args.array(), vp);
     XPCWrappedNative* wrapper = ccx.GetWrapper();
     THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
 
     RefPtr<XPCNativeInterface> iface;
@@ -915,19 +924,21 @@ XPC_WN_CallMethod(JSContext* cx, unsigne
 
 bool
 XPC_WN_GetterSetter(JSContext* cx, unsigned argc, Value* vp)
 {
     JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
     MOZ_ASSERT(JS_TypeOfValue(cx, args.calleev()) == JSTYPE_FUNCTION, "bad function");
     RootedObject funobj(cx, &args.callee());
 
-    RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
-    if (!obj)
+    if (!args.thisv().isObject()) {
+        JS_ReportErrorASCII(cx, "xpconnect getter/setter called on incompatible non-object");
         return false;
+    }
+    RootedObject obj(cx, &args.thisv().toObject());
 
     obj = FixUpThisIfBroken(obj, funobj);
     XPCCallContext ccx(cx, obj, funobj, JSID_VOIDHANDLE, args.length(),
                        args.array(), vp);
     XPCWrappedNative* wrapper = ccx.GetWrapper();
     THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
 
     RefPtr<XPCNativeInterface> iface;
--- a/layout/svg/nsSVGUtils.cpp
+++ b/layout/svg/nsSVGUtils.cpp
@@ -1168,18 +1168,17 @@ nsSVGUtils::GetBBox(nsIFrame* aFrame, ui
     if (effectProperties.HasInvalidClipPath()) {
       bbox = gfxRect(0, 0, 0, 0);
     } else {
       nsSVGClipPathFrame *clipPathFrame =
         effectProperties.GetClipPathFrame();
       if (clipPathFrame) {
         SVGClipPathElement *clipContent =
           static_cast<SVGClipPathElement*>(clipPathFrame->GetContent());
-        RefPtr<SVGAnimatedEnumeration> units = clipContent->ClipPathUnits();
-        if (units->AnimVal() == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
+        if (clipContent->IsUnitsObjectBoundingBox()) {
           matrix.PreTranslate(gfxPoint(x, y));
           matrix.PreScale(width, height);
         } else if (aFrame->IsSVGForeignObjectFrame()) {
           matrix = gfxMatrix();
         }
         matrix = clipContent->PrependLocalTransformsTo(matrix, eUserSpaceToParent);
         bbox =
           clipPathFrame->GetBBoxForClipPathFrame(bbox, matrix).ToThebesRect();
--- a/media/webrtc/signaling/src/media-conduit/WebrtcMediaCodecVP8VideoCodec.cpp
+++ b/media/webrtc/signaling/src/media-conduit/WebrtcMediaCodecVP8VideoCodec.cpp
@@ -42,17 +42,17 @@ static const char* wmcLogTag ="WebrtcMed
 #ifdef LOGTAG
 #undef LOGTAG
 #endif
 #define LOGTAG wmcLogTag
 
 class CallbacksSupport final : public JavaCallbacksSupport
 {
 public:
-  CallbacksSupport(webrtc::EncodedImageCallback* aCallback)
+  explicit CallbacksSupport(webrtc::EncodedImageCallback* aCallback)
     : mCallback(aCallback)
     , mCritSect(webrtc::CriticalSectionWrapper::CreateCriticalSection())
     , mPictureId(0) {
     CSFLogDebug(LOGTAG,  "%s %p", __FUNCTION__, this);
     memset(&mEncodedImage, 0, sizeof(mEncodedImage));
   }
 
   ~CallbacksSupport() {
@@ -630,17 +630,17 @@ public:
 protected:
   virtual ~WebrtcAndroidMediaCodec() {
   }
 
 private:
 class OutputDrain : public MediaCodecOutputDrain
   {
   public:
-    OutputDrain(WebrtcAndroidMediaCodec* aMediaCodec)
+    explicit OutputDrain(WebrtcAndroidMediaCodec* aMediaCodec)
       : MediaCodecOutputDrain()
       , mMediaCodec(aMediaCodec)
     {}
 
   protected:
     virtual bool DrainOutput() override
     {
       return (mMediaCodec->DrainOutput(mInputFrames, mMonitor) == NS_OK);
--- a/memory/mozalloc/mozalloc_abort.cpp
+++ b/memory/mozalloc/mozalloc_abort.cpp
@@ -12,16 +12,17 @@
 #endif
 #ifdef MOZ_WIDGET_ANDROID
 # include "APKOpen.h"
 # include "dlfcn.h"
 #endif
 #include <stdio.h>
 
 #include "mozilla/Assertions.h"
+#include "mozilla/Sprintf.h"
 
 void
 mozalloc_abort(const char* const msg)
 {
 #ifndef ANDROID
     fputs(msg, stderr);
     fputs("\n", stderr);
 #else
@@ -45,18 +46,18 @@ void fillAbortMessage(char (&msg)[N], ui
     dladdr(reinterpret_cast<void*>(retAddress), &info);
 
     const char* const module = info.dli_fname ? info.dli_fname : "";
     const char* const base_module = strrchr(module, '/');
     const void* const module_offset =
         reinterpret_cast<void*>(retAddress - uintptr_t(info.dli_fbase));
     const char* const sym = info.dli_sname ? info.dli_sname : "";
 
-    snprintf(msg, sizeof(msg), "abort() called from %s:%p (%s)",
-             base_module ? base_module + 1 : module, module_offset, sym);
+    SprintfLiteral(msg, "abort() called from %s:%p (%s)",
+                   base_module ? base_module + 1 : module, module_offset, sym);
 }
 #endif
 
 #if defined(XP_UNIX) && !defined(MOZ_ASAN)
 // Define abort() here, so that it is used instead of the system abort(). This
 // lets us control the behavior when aborting, in order to get better results
 // on *NIX platforms. See mozalloc_abort for details.
 //
--- a/mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java
+++ b/mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java
@@ -605,23 +605,25 @@ public class CustomTabsActivity extends 
     }
 
     @Override
     public void onLoadRequest(final GeckoSession session, final String urlStr,
                                  final int target,
                                  final GeckoSession.Response<Boolean> response) {
         if (target != GeckoSession.NavigationDelegate.TARGET_WINDOW_NEW) {
             response.respond(false);
+            return;
         }
 
         final Uri uri = Uri.parse(urlStr);
         if (uri == null) {
             // We can't handle this, so deny it.
             Log.w(LOGTAG, "Failed to parse URL for navigation: " + urlStr);
             response.respond(true);
+            return;
         }
 
         // Always use Fennec for these schemes.
         if ("http".equals(uri.getScheme()) || "https".equals(uri.getScheme()) ||
             "data".equals(uri.getScheme()) || "blob".equals(uri.getScheme())) {
             final Intent intent = new Intent(this, BrowserApp.class);
             intent.setAction(Intent.ACTION_VIEW);
             intent.setData(uri);
--- a/mobile/android/base/java/org/mozilla/gecko/webapps/WebAppActivity.java
+++ b/mobile/android/base/java/org/mozilla/gecko/webapps/WebAppActivity.java
@@ -373,27 +373,30 @@ public class WebAppActivity extends AppC
     public void onLoadRequest(final GeckoSession session, final String urlStr,
                               final int target,
                               final GeckoSession.Response<Boolean> response) {
         final Uri uri = Uri.parse(urlStr);
         if (uri == null) {
             // We can't really handle this, so deny it?
             Log.w(LOGTAG, "Failed to parse URL for navigation: " + urlStr);
             response.respond(true);
+            return;
         }
 
         if (mManifest.isInScope(uri) && target != TARGET_WINDOW_NEW) {
             // This is in scope and wants to load in the same frame, so
             // let Gecko handle it.
             response.respond(false);
+            return;
         }
 
         if ("javascript".equals(uri.getScheme())) {
             // These URIs will fail the scope check but should still be loaded in the PWA.
             response.respond(false);
+            return;
         }
 
         if ("http".equals(uri.getScheme()) || "https".equals(uri.getScheme()) ||
             "data".equals(uri.getScheme()) || "blob".equals(uri.getScheme())) {
             final CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder()
                 .addDefaultShareMenuItem()
                 .setStartAnimations(this, R.anim.slide_in_right, R.anim.slide_out_left)
                 .setExitAnimations(this, R.anim.slide_in_left, R.anim.slide_out_right);
--- a/mobile/android/chrome/content/aboutCertError.xhtml
+++ b/mobile/android/chrome/content/aboutCertError.xhtml
@@ -45,153 +45,44 @@
         // s is optional, if no match just return nothing
         if (!matches || matches.length < 2)
           return "";
 
         // parenthetical match is the second entry
         return decodeURIComponent(matches[1]);
       }
 
-      function getDescription() {
-        var url = document.documentURI;
-        var desc = url.search(/d\=/);
-
-        // desc == -1 if not found; if so, return an empty string
-        // instead of what would turn out to be portions of the URI
-        if (desc == -1)
-          return "";
-
-        return decodeURIComponent(url.slice(desc + 2));
-      }
-
       function initPage() {
         // Replace the "#1" string in the intro with the hostname.  Trickier
         // than it might seem since we want to preserve the <b> tags, but
         // not allow for any injection by just using innerHTML.  Instead,
         // just find the right target text node.
         var intro = document.getElementById("introContentP1");
         function replaceWithHost(node) {
           if (node.textContent == "#1")
             node.textContent = location.host;
           else
             for (var i = 0; i < node.childNodes.length; i++)
               replaceWithHost(node.childNodes[i]);
         }
-  replaceWithHost(intro);
+        replaceWithHost(intro);
 
         if (getCSSClass() == "expertBadCert") {
           toggle("technicalContent");
           toggle("expertContent");
         }
 
         // Disallow overrides if this is a Strict-Transport-Security
         // host and the cert is bad (STS Spec section 7.3) or if the
         // certerror is in a frame (bug 633691).
         if (getCSSClass() == "badStsCert" || window != top)
           document.getElementById("expertContent").setAttribute("hidden", "true");
 
-        var tech = document.getElementById("technicalContentText");
-        if (tech)
-          tech.textContent = getDescription();
-
-        addDomainErrorLinks();
-      }
-
-      /* Try to preserve the links contained in the error description, like
-         the error code.
-
-         Also, in the case of SSL error pages about domain mismatch, see if
-         we can hyperlink the user to the correct site.  We don't want
-         to do this generically since it allows MitM attacks to redirect
-         users to a site under attacker control, but in certain cases
-         it is safe (and helpful!) to do so.  Bug 402210
-      */
-      function addDomainErrorLinks() {
-        // Rather than textContent, we need to treat description as HTML
-        var sd = document.getElementById("technicalContentText");
-        if (sd) {
-          var desc = getDescription();
-
-          // sanitize description text - see bug 441169
-
-          // First, find the index of the <a> tags we care about, being
-          // careful not to use an over-greedy regex.
-          var codeRe = /<a id="errorCode" title="([^"]+)">/;
-          var codeResult = codeRe.exec(desc);
-          var domainRe = /<a id="cert_domain_link" title="([^"]+)">/;
-          var domainResult = domainRe.exec(desc);
-
-          // The order of these links in the description is fixed in
-          // TransportSecurityInfo.cpp:formatOverridableCertErrorMessage.
-          var firstResult = domainResult;
-          if (!domainResult)
-            firstResult = codeResult;
-          if (!firstResult)
-            return;
-
-          // Remove sd's existing children
-          sd.textContent = "";
-
-          // Everything up to the first link should be text content.
-          sd.appendChild(document.createTextNode(desc.slice(0, firstResult.index)));
-
-          // Now create the actual links.
-          if (domainResult) {
-            createLink(sd, "cert_domain_link", domainResult[1]);
-            // Append text for anything between the two links.
-            sd.appendChild(document.createTextNode(desc.slice(desc.indexOf("</a>") + "</a>".length, codeResult.index)));
-          }
-          createLink(sd, "errorCode", codeResult[1]);
-
-          // Finally, append text for anything after the last closing </a>.
-          sd.appendChild(document.createTextNode(desc.slice(desc.lastIndexOf("</a>") + "</a>".length)));
-        }
-
-        // Then initialize the cert domain link.
-        var link = document.getElementById("cert_domain_link");
-        if (!link)
-          return;
-
-        var okHost = link.getAttribute("title");
-        var thisHost = document.location.hostname;
-        var proto = document.location.protocol;
-
-        // If okHost is a wildcard domain ("*.example.com") let's
-        // use "www" instead.  "*.example.com" isn't going to
-        // get anyone anywhere useful. bug 432491
-        okHost = okHost.replace(/^\*\./, "www.");
-
-        /* case #1:
-         * example.com uses an invalid security certificate.
-         *
-         * The certificate is only valid for www.example.com
-         *
-         * Make sure to include the "." ahead of thisHost so that
-         * a MitM attack on paypal.com doesn't hyperlink to "notpaypal.com"
-         *
-         * We'd normally just use a RegExp here except that we lack a
-         * library function to escape them properly (bug 248062), and
-         * domain names are famous for having '.' characters in them,
-         * which would allow spurious and possibly hostile matches.
-         */
-        if (okHost.endsWith("." + thisHost))
-          link.href = proto + okHost;
-
-        /* case #2:
-         * browser.garage.maemo.org uses an invalid security certificate.
-         *
-         * The certificate is only valid for garage.maemo.org
-         */
-        if (thisHost.endsWith("." + okHost))
-          link.href = proto + okHost;
-
-        // If we set a link, meaning there's something helpful for
-        // the user here, expand the section by default
-        if (link.href && getCSSClass() != "expertBadCert")
-          toggle("technicalContent");
+        var event = new CustomEvent("AboutCertErrorLoad", {bubbles: true});
+        document.dispatchEvent(event);
       }
 
       function createLink(el, id, text) {
         var anchorEl = document.createElement("a");
         anchorEl.setAttribute("id", id);
         anchorEl.setAttribute("title", text);
         anchorEl.appendChild(document.createTextNode(text));
         el.appendChild(anchorEl);
--- a/mobile/android/chrome/content/content.js
+++ b/mobile/android/chrome/content/content.js
@@ -5,16 +5,23 @@
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 ChromeUtils.defineModuleGetter(this, "AboutReader", "resource://gre/modules/AboutReader.jsm");
 ChromeUtils.defineModuleGetter(this, "ReaderMode", "resource://gre/modules/ReaderMode.jsm");
 ChromeUtils.defineModuleGetter(this, "LoginManagerContent", "resource://gre/modules/LoginManagerContent.jsm");
 
+XPCOMUtils.defineLazyGetter(this, "gPipNSSBundle", function() {
+  return Services.strings.createBundle("chrome://pipnss/locale/pipnss.properties");
+});
+XPCOMUtils.defineLazyGetter(this, "gNSSErrorsBundle", function() {
+  return Services.strings.createBundle("chrome://pipnss/locale/nsserrors.properties");
+});
+
 var dump = ChromeUtils.import("resource://gre/modules/AndroidLog.jsm", {}).AndroidLog.d.bind(null, "Content");
 
 var global = this;
 
 var AboutBlockedSiteListener = {
   init(chromeGlobal) {
     addEventListener("AboutBlockedLoaded", this, false, true);
   },
@@ -59,16 +66,312 @@ var AboutBlockedSiteListener = {
 
     let anchorEl = content.document.getElementById("advisory_provider");
     anchorEl.setAttribute("href", advisoryUrl);
     anchorEl.textContent = advisoryLinkText;
   },
 };
 AboutBlockedSiteListener.init();
 
+/* The following code, in particular AboutCertErrorListener and
+ * AboutNetErrorListener, is mostly copied from content browser.js and content.js.
+ * Certificate error handling should be unified to remove this duplicated code.
+ */
+
+const SEC_ERROR_BASE          = Ci.nsINSSErrorsService.NSS_SEC_ERROR_BASE;
+const MOZILLA_PKIX_ERROR_BASE = Ci.nsINSSErrorsService.MOZILLA_PKIX_ERROR_BASE;
+
+const SEC_ERROR_EXPIRED_CERTIFICATE                = SEC_ERROR_BASE + 11;
+const SEC_ERROR_UNKNOWN_ISSUER                     = SEC_ERROR_BASE + 13;
+const SEC_ERROR_UNTRUSTED_ISSUER                   = SEC_ERROR_BASE + 20;
+const SEC_ERROR_UNTRUSTED_CERT                     = SEC_ERROR_BASE + 21;
+const SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE         = SEC_ERROR_BASE + 30;
+const SEC_ERROR_CA_CERT_INVALID                    = SEC_ERROR_BASE + 36;
+const SEC_ERROR_OCSP_FUTURE_RESPONSE               = SEC_ERROR_BASE + 131;
+const SEC_ERROR_OCSP_OLD_RESPONSE                  = SEC_ERROR_BASE + 132;
+const SEC_ERROR_REUSED_ISSUER_AND_SERIAL           = SEC_ERROR_BASE + 138;
+const SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED  = SEC_ERROR_BASE + 176;
+const MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE = MOZILLA_PKIX_ERROR_BASE + 5;
+const MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE = MOZILLA_PKIX_ERROR_BASE + 6;
+
+
+const SSL_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SSL_ERROR_BASE;
+const SSL_ERROR_SSL_DISABLED  = SSL_ERROR_BASE + 20;
+const SSL_ERROR_SSL2_DISABLED  = SSL_ERROR_BASE + 14;
+
+var AboutNetErrorListener = {
+  init(chromeGlobal) {
+    addEventListener("AboutNetErrorLoad", this, false, true);
+  },
+
+  get isNetErrorSite() {
+    return content.document.documentURI.startsWith("about:neterror");
+  },
+
+  _getErrorMessageFromCode(securityInfo, doc) {
+    let uri = Services.io.newURI(doc.location);
+    let hostString = uri.host;
+    if (uri.port != 443 && uri.port != -1) {
+      hostString += ":" + uri.port;
+    }
+
+    let id_str = "";
+    switch (securityInfo.errorCode) {
+      case SSL_ERROR_SSL_DISABLED:
+        id_str = "PSMERR_SSL_Disabled";
+        break;
+      case SSL_ERROR_SSL2_DISABLED:
+        id_str = "PSMERR_SSL2_Disabled";
+        break;
+      case SEC_ERROR_REUSED_ISSUER_AND_SERIAL:
+        id_str = "PSMERR_HostReusedIssuerSerial";
+        break;
+    }
+    let nss_error_id_str = securityInfo.errorCodeString;
+    let msg2 = "";
+    if (id_str) {
+      msg2 = gPipNSSBundle.GetStringFromName(id_str) + "\n";
+    } else if (nss_error_id_str) {
+      msg2 = gNSSErrorsBundle.GetStringFromName(nss_error_id_str) + "\n";
+    }
+
+    if (!msg2) {
+      // We couldn't get an error message. Use the error string.
+      // Note that this is different from before where we used PR_ErrorToString.
+      msg2 = nss_error_id_str;
+    }
+    let msg = gPipNSSBundle.formatStringFromName("SSLConnectionErrorPrefix2",
+                                                 [hostString, msg2], 2);
+
+    if (nss_error_id_str) {
+      msg += gPipNSSBundle.formatStringFromName("certErrorCodePrefix3",
+                                                [nss_error_id_str], 1);
+    }
+    return msg;
+  },
+
+  handleEvent(aEvent) {
+    if (!this.isNetErrorSite) {
+      return;
+    }
+
+    if (aEvent.type != "AboutNetErrorLoad") {
+      return;
+    }
+
+    let {securityInfo} = docShell.failedChannel;
+    // We don't have a securityInfo when this is for example a DNS error.
+    if (securityInfo) {
+      securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
+      let msg = this._getErrorMessageFromCode(securityInfo,
+                                              aEvent.originalTarget.ownerGlobal);
+      let id = content.document.getElementById("errorShortDescText");
+      id.textContent = msg;
+    }
+  },
+};
+AboutNetErrorListener.init();
+
+var AboutCertErrorListener = {
+  init(chromeGlobal) {
+    addEventListener("AboutCertErrorLoad", this, false, true);
+  },
+
+  get isCertErrorSite() {
+    return content.document.documentURI.startsWith("about:certerror");
+  },
+
+  _setTechDetailsMsgPart1(hostString, sslStatus, technicalInfo, doc) {
+    let msg = gPipNSSBundle.formatStringFromName("certErrorIntro",
+                                                 [hostString], 1);
+    msg += "\n\n";
+
+    if (sslStatus.isUntrusted && !sslStatus.serverCert.isSelfSigned) {
+      switch (securityInfo.errorCode) {
+        case SEC_ERROR_UNKNOWN_ISSUER:
+          msg += gPipNSSBundle.GetStringFromName("certErrorTrust_Un