Merge inbound to mozilla-central r=merge a=merge
authorTiberius Oros <toros@mozilla.com>
Tue, 21 Nov 2017 11:55:23 +0200
changeset 392833 72ee4800d4156931c89b58bd807af4a3083702bb
parent 392832 d65aba303d138b832145027f03904c70624b967b (current diff)
parent 392774 effb563bb7e5692cf7d911725b5870f1b7f6c41c (diff)
child 392834 60d0f3ee0c43120fcf1f048127df7d41782abf92
child 392910 92ac20cda9f4d9fca22db416e3a0f0c45f5d9f87
child 392974 472098f21676535d02474ae40c36cb1e96dd5c8d
push id97527
push usertoros@mozilla.com
push dateTue, 21 Nov 2017 10:20:05 +0000
treeherdermozilla-inbound@60d0f3ee0c43 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone59.0a1
first release with
nightly linux32
72ee4800d415 / 59.0a1 / 20171121100129 / files
nightly linux64
72ee4800d415 / 59.0a1 / 20171121100129 / files
nightly mac
72ee4800d415 / 59.0a1 / 20171121100129 / files
nightly win32
72ee4800d415 / 59.0a1 / 20171121100129 / files
nightly win64
72ee4800d415 / 59.0a1 / 20171121100129 / 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 r=merge a=merge
--- a/browser/base/content/test/general/browser_new_http_window_opened_from_file_tab.js
+++ b/browser/base/content/test/general/browser_new_http_window_opened_from_file_tab.js
@@ -19,33 +19,33 @@ add_task(async function() {
   // Set pref to open in new window.
   Services.prefs.setIntPref("browser.link.open_newwindow", 2);
   registerCleanupFunction(function() {
     Services.prefs.clearUserPref("browser.link.open_newwindow");
   });
 
   // Open new http window from JavaScript in file:// page and check that we get
   // a new window with the correct page and features.
-  let promiseNewWindow = BrowserTestUtils.waitForNewWindow(true, TEST_HTTP);
+  let promiseNewWindow = BrowserTestUtils.waitForNewWindow(TEST_HTTP);
   await ContentTask.spawn(browser, TEST_HTTP, uri => {
     content.open(uri, "_blank");
   });
   let win = await promiseNewWindow;
   registerCleanupFunction(async function() {
     await BrowserTestUtils.closeWindow(win);
   });
   ok(win, "Check that an http window loaded when using window.open.");
   ok(win.menubar.visible,
      "Check that the menu bar on the new window is visible.");
   ok(win.toolbar.visible,
      "Check that the tool bar on the new window is visible.");
 
   // Open new http window from a link in file:// page and check that we get a
   // new window with the correct page and features.
-  promiseNewWindow = BrowserTestUtils.waitForNewWindow(true, TEST_HTTP);
+  promiseNewWindow = BrowserTestUtils.waitForNewWindow(TEST_HTTP);
   await BrowserTestUtils.synthesizeMouseAtCenter("#linkToExample", {}, browser);
   let win2 = await promiseNewWindow;
   registerCleanupFunction(async function() {
     await BrowserTestUtils.closeWindow(win2);
   });
   ok(win2, "Check that an http window loaded when using link.");
   ok(win2.menubar.visible,
      "Check that the menu bar on the new window is visible.");
--- a/browser/base/content/test/popups/browser_popupUI.js
+++ b/browser/base/content/test/popups/browser_popupUI.js
@@ -1,13 +1,13 @@
 function test() {
   waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({ set: [[ "dom.disable_open_during_load", false ]] });
 
-  let popupOpened = BrowserTestUtils.waitForNewWindow(true, "about:blank");
+  let popupOpened = BrowserTestUtils.waitForNewWindow("about:blank");
   BrowserTestUtils.openNewForegroundTab(gBrowser,
     "data:text/html,<html><script>popup=open('about:blank','','width=300,height=200')</script>"
   );
   popupOpened.then((win) => testPopupUI(win));
 }
 
 function testPopupUI(win) {
   var doc = win.document;
--- a/browser/base/content/test/sidebar/browser_sidebar_adopt.js
+++ b/browser/base/content/test/sidebar/browser_sidebar_adopt.js
@@ -40,17 +40,17 @@ add_task(async function testEventsReceiv
   await initialFocus;
 
   ok(true, "SidebarShown and SidebarFocused events fired on a new window");
 });
 
 add_task(async function testEventReceivedInNewWindow() {
   info("Opening a new window and expecting the SidebarFocused event to not fire");
 
-  let promiseNewWindow = BrowserTestUtils.waitForNewWindow(false);
+  let promiseNewWindow = BrowserTestUtils.waitForNewWindow();
   BrowserTestUtils.openNewBrowserWindow({opener: window});
   let win = await promiseNewWindow;
 
   let adoptedShown = BrowserTestUtils.waitForEvent(win, "SidebarShown");
   win.addEventListener("SidebarFocused", failIfSidebarFocusedFires);
 
   registerCleanupFunction(async function() {
     win.removeEventListener("SidebarFocused", failIfSidebarFocusedFires);
--- a/browser/components/customizableui/test/browser_open_from_popup.js
+++ b/browser/components/customizableui/test/browser_open_from_popup.js
@@ -1,15 +1,15 @@
 "use strict";
 
 /**
  * Check that opening customize mode in a popup opens it in the main window.
  */
 add_task(async function open_customize_mode_from_popup() {
-  let promiseWindow = BrowserTestUtils.waitForNewWindow(true);
+  let promiseWindow = BrowserTestUtils.waitForNewWindow();
   ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
     content.window.open("about:blank", "_blank", "height=300,toolbar=no");
   });
   let win = await promiseWindow;
   let customizePromise = BrowserTestUtils.waitForEvent(gNavToolbox, "customizationready");
   win.gCustomizeMode.enter();
   await customizePromise;
   ok(document.documentElement.hasAttribute("customizing"),
--- a/browser/components/extensions/test/browser/browser_ext_windows.js
+++ b/browser/components/extensions/test/browser/browser_ext_windows.js
@@ -89,17 +89,17 @@ add_task(async function testWindowTitle(
       permissions: ["tabs"],
     },
   });
 
   await extension.startup();
   let {Management: {global: {windowTracker}}} = Cu.import("resource://gre/modules/Extension.jsm", {});
 
   async function createApiWin(options) {
-    let promiseLoaded = BrowserTestUtils.waitForNewWindow(true, START_URL);
+    let promiseLoaded = BrowserTestUtils.waitForNewWindow(START_URL);
     extension.sendMessage("create", options);
     let apiWin = await extension.awaitMessage("created");
     let realWin = windowTracker.getWindow(apiWin.id);
     await promiseLoaded;
     let expectedPreface = options.titlePreface ? options.titlePreface : "";
     ok(realWin.document.title.startsWith(expectedPreface),
        "Created window has the expected title preface.");
     ok(realWin.document.title.includes(START_TITLE),
--- a/browser/components/privatebrowsing/test/browser/browser_oa_private_browsing_window.js
+++ b/browser/components/privatebrowsing/test/browser/browser_oa_private_browsing_window.js
@@ -1,17 +1,17 @@
 "use strict";
 
 const PATH = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "http://example.com");
 const TEST_PAGE = PATH + "file_triggeringprincipal_oa.html";
 const DUMMY_PAGE = PATH + "empty_file.html";
 
 add_task(async function test_principal_right_click_open_link_in_new_private_win() {
   await BrowserTestUtils.withNewTab(TEST_PAGE, async function(browser) {
-    let promiseNewWindow = BrowserTestUtils.waitForNewWindow(true, DUMMY_PAGE);
+    let promiseNewWindow = BrowserTestUtils.waitForNewWindow(DUMMY_PAGE);
 
     // simulate right-click open link in new private window
     BrowserTestUtils.waitForEvent(document, "popupshown", false, event => {
       document.getElementById("context-openlinkprivate").doCommand();
       event.target.hidePopup();
       return true;
     });
     BrowserTestUtils.synthesizeMouseAtCenter("#checkPrincipalOA",
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_newtab_from_popup.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_newtab_from_popup.js
@@ -35,17 +35,17 @@ add_task(async function test_private_pop
   // First, open a private browsing window, and load our
   // testing page.
   let privBrowser = privWin.gBrowser.selectedBrowser;
   await BrowserTestUtils.loadURI(privBrowser, WINDOW_BODY);
   await BrowserTestUtils.browserLoaded(privBrowser);
 
   // Next, click on the link in the testing page, and ensure
   // that a private popup window is opened.
-  let openedPromise = BrowserTestUtils.waitForNewWindow(true, POPUP_LINK);
+  let openedPromise = BrowserTestUtils.waitForNewWindow(POPUP_LINK);
 
   await BrowserTestUtils.synthesizeMouseAtCenter("#first", {}, privBrowser);
   let popupWin = await openedPromise;
   ok(PrivateBrowsingUtils.isWindowPrivate(popupWin),
      "Popup window was private.");
 
   // Now click on the link in the popup, and ensure that a new
   // tab is opened in the original private browsing window.
--- a/browser/components/sessionstore/test/browser_394759_behavior.js
+++ b/browser/components/sessionstore/test/browser_394759_behavior.js
@@ -21,17 +21,17 @@
  */
 function testWindows(windowsToOpen, expectedResults) {
   return (async function() {
     for (let winData of windowsToOpen) {
       let features = "chrome,dialog=no," +
                      (winData.isPopup ? "all=no" : "all");
       let url = "http://example.com/?window=" + windowsToOpen.length;
 
-      let openWindowPromise = BrowserTestUtils.waitForNewWindow(true, url);
+      let openWindowPromise = BrowserTestUtils.waitForNewWindow(url);
       openDialog(getBrowserURL(), "", features, url);
       let win = await openWindowPromise;
       await BrowserTestUtils.closeWindow(win);
     }
 
     let closedWindowData = JSON.parse(ss.getClosedWindowData());
     let numPopups = closedWindowData.filter(function(el, i, arr) {
       return el.isPopup;
new file mode 100644
--- /dev/null
+++ b/browser/config/tooltool-manifests/linux64/jsshell.manifest
@@ -0,0 +1,16 @@
+[
+  {
+    "size": 479280,
+    "digest": "dd7e9d67fc858f670502a16266963b15d8fb9a2fa4fc638ba1a27e17318b84ade5a9c1284783905dfbe113ce59690c9232c182e0466d53a37182e8aedea5530f",
+    "algorithm": "sha512",
+    "filename": "breakpad-tools.tar.xz",
+    "unpack": true
+  },
+  {
+    "size": 1737944,
+    "visibility": "public",
+    "digest": "76d704f0bfa110f5ea298b87200e34ec09d039b9d1a59ec819fc8e02b2cf073af32a4536dca33a3813f037a557fd0669b48a063c7a920f6308b307148029d41f",
+    "algorithm": "sha512",
+    "filename": "linux64-minidump_stackwalk"
+  }
+]
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -329,17 +329,16 @@ uint32_t nsContentUtils::sHandlingInputT
 
 uint32_t nsContentUtils::sCookiesLifetimePolicy = nsICookieService::ACCEPT_NORMALLY;
 uint32_t nsContentUtils::sCookiesBehavior = nsICookieService::BEHAVIOR_ACCEPT;
 
 nsHtml5StringParser* nsContentUtils::sHTMLFragmentParser = nullptr;
 nsIParser* nsContentUtils::sXMLFragmentParser = nullptr;
 nsIFragmentContentSink* nsContentUtils::sXMLFragmentSink = nullptr;
 bool nsContentUtils::sFragmentParsingActive = false;
-nsISerialEventTarget* nsContentUtils::sStableStateEventTarget = nullptr;
 
 #if !(defined(DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
 bool nsContentUtils::sDOMWindowDumpEnabled;
 #endif
 
 bool nsContentUtils::sDoNotTrackEnabled = false;
 
 mozilla::LazyLogModule nsContentUtils::sDOMDumpLog("Dump");
@@ -522,62 +521,16 @@ class SameOriginCheckerImpl final : publ
 {
   ~SameOriginCheckerImpl() = default;
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSICHANNELEVENTSINK
   NS_DECL_NSIINTERFACEREQUESTOR
 };
 
-class StableStateEventTarget final : public nsISerialEventTarget
-{
-public:
-  NS_DECL_THREADSAFE_ISUPPORTS
-  NS_DECL_NSIEVENTTARGET_FULL
-private:
-  ~StableStateEventTarget() {}
-};
-
-NS_IMPL_ISUPPORTS(StableStateEventTarget, nsISerialEventTarget);
-
-bool
-StableStateEventTarget::IsOnCurrentThreadInfallible()
-{
-  return true;
-}
-
-NS_IMETHODIMP
-StableStateEventTarget::IsOnCurrentThread(bool* aResult)
-{
-  *aResult = true;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-StableStateEventTarget::Dispatch(already_AddRefed<nsIRunnable> aEvent, uint32_t aFlags)
-{
-  if (NS_WARN_IF(!CycleCollectedJSContext::Get())) {
-    return NS_ERROR_UNEXPECTED;
-  }
-  nsContentUtils::RunInStableState(Move(aEvent));
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-StableStateEventTarget::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags)
-{
-  return Dispatch(nsCOMPtr<nsIRunnable>(aEvent).forget(), aFlags);
-}
-
-NS_IMETHODIMP
-StableStateEventTarget::DelayedDispatch(already_AddRefed<nsIRunnable> aEvent, uint32_t aDelay)
-{
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
 } // namespace
 
 /**
  * This class is used to determine whether or not the user is currently
  * interacting with the browser. It listens to observer events to toggle the
  * value of the sUserActive static.
  *
  * This class is an internal implementation detail.
@@ -793,19 +746,16 @@ nsContentUtils::Init()
 
   nsDependentCString buildID(mozilla::PlatformBuildID());
   sJSBytecodeMimeType = new nsCString(NS_LITERAL_CSTRING("javascript/moz-bytecode-") + buildID);
 
   Element::InitCCCallbacks();
 
   Unused << nsRFPService::GetOrCreate();
 
-  RefPtr<StableStateEventTarget> stableStateEventTarget = new StableStateEventTarget();
-  stableStateEventTarget.forget(&sStableStateEventTarget);
-
   nsCOMPtr<nsIUUIDGenerator> uuidGenerator =
     do_GetService("@mozilla.org/uuid-generator;1", &rv);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   uuidGenerator.forget(&sUUIDGenerator);
 
   if (XRE_IsParentProcess()) {
@@ -2208,18 +2158,16 @@ nsContentUtils::Shutdown()
   delete sModifierSeparator;
   sModifierSeparator = nullptr;
 
   delete sJSBytecodeMimeType;
   sJSBytecodeMimeType = nullptr;
 
   NS_IF_RELEASE(sSameOriginChecker);
 
-  NS_IF_RELEASE(sStableStateEventTarget);
-
   if (sUserInteractionObserver) {
     sUserInteractionObserver->Shutdown();
     NS_RELEASE(sUserInteractionObserver);
   }
 
   HTMLInputElement::Shutdown();
   nsMappedAttributes::Shutdown();
 }
@@ -5814,23 +5762,16 @@ nsContentUtils::RunInMetastableState(alr
 /* static */
 bool
 nsContentUtils::IsInStableOrMetaStableState()
 {
   MOZ_ASSERT(CycleCollectedJSContext::Get(), "Must be on a script thread!");
   return CycleCollectedJSContext::Get()->IsInStableOrMetaStableState();
 }
 
-/* static */
-nsISerialEventTarget*
-nsContentUtils::GetStableStateEventTarget()
-{
-  return sStableStateEventTarget;
-}
-
 /*
  * Helper function for nsContentUtils::ProcessViewportInfo.
  *
  * Handles a single key=value pair. If it corresponds to a valid viewport
  * attribute, add it to the document header data. No validation is done on the
  * value itself (this is done at display time).
  */
 static void ProcessViewportToken(nsIDocument *aDocument,
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -1972,26 +1972,16 @@ public:
    */
   static void RunInMetastableState(already_AddRefed<nsIRunnable> aRunnable);
 
   /**
    * Returns true if we are doing StableState/MetastableState.
    */
   static bool IsInStableOrMetaStableState();
 
-  /**
-   * Returns a nsISerialEventTarget which will run any event dispatched to it
-   * once the event loop has reached a "stable state". Runnables dispatched to
-   * this event target must not cause any queued events to be processed (i.e.
-   * must not spin the event loop).
-   *
-   * See RunInStableState for more information about stable states
-   */
-  static nsISerialEventTarget* GetStableStateEventTarget();
-
   /* Process viewport META data. This gives us information for the scale
    * and zoom of a page on mobile devices. We stick the information in
    * the document header and use it later on after rendering.
    *
    * See Bug #436083
    */
   static nsresult ProcessViewportInfo(nsIDocument *aDocument,
                                       const nsAString &viewportInfo);
@@ -3425,18 +3415,16 @@ private:
   static nsIParser* sXMLFragmentParser;
   static nsIFragmentContentSink* sXMLFragmentSink;
 
   /**
    * True if there's a fragment parser activation on the stack.
    */
   static bool sFragmentParsingActive;
 
-  static nsISerialEventTarget* sStableStateEventTarget;
-
   static nsString* sShiftText;
   static nsString* sControlText;
   static nsString* sMetaText;
   static nsString* sOSText;
   static nsString* sAltText;
   static nsString* sModifierSeparator;
 
   // Alternate data mime type, used by the ScriptLoader to register and read the
--- a/dom/cache/CacheStreamControlChild.cpp
+++ b/dom/cache/CacheStreamControlChild.cpp
@@ -123,17 +123,17 @@ CacheStreamControlChild::OpenStream(cons
   // MozPromise resolve runnable is already in the event queue when the
   // worker wants to shut down.
   RefPtr<CacheWorkerHolder> holder = GetWorkerHolder();
 
   SendOpenStream(aId)->Then(GetCurrentThreadSerialEventTarget(), __func__,
   [aResolver, holder](const OptionalIPCStream& aOptionalStream) {
     nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(aOptionalStream);
     aResolver(Move(stream));
-  }, [aResolver, holder](PromiseRejectReason aReason) {
+  }, [aResolver, holder](ResponseRejectReason aReason) {
     aResolver(nullptr);
   });
 }
 
 void
 CacheStreamControlChild::NoteClosedAfterForget(const nsID& aId)
 {
   NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild);
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -882,54 +882,148 @@ ContentChild::ProvideWindowCommon(TabChi
   SetEventTargetForActor(newChild, target);
 
   Unused << SendPBrowserConstructor(
     // We release this ref in DeallocPBrowserChild
     RefPtr<TabChild>(newChild).forget().take(),
     tabId, TabId(0), *ipcContext, aChromeFlags,
     GetID(), IsForBrowser());
 
-  nsTArray<FrameScriptInfo> frameScripts;
-  nsCString urlToLoad;
 
   PRenderFrameChild* renderFrame = newChild->SendPRenderFrameConstructor();
-  TextureFactoryIdentifier textureFactoryIdentifier;
-  uint64_t layersId = 0;
-  CompositorOptions compositorOptions;
-  uint32_t maxTouchPoints = 0;
-  DimensionInfo dimensionInfo;
 
   nsCOMPtr<nsPIDOMWindowInner> parentTopInnerWindow;
   if (aParent) {
     nsCOMPtr<nsPIDOMWindowOuter> parentTopWindow =
       nsPIDOMWindowOuter::From(aParent)->GetTop();
     if (parentTopWindow) {
       parentTopInnerWindow = parentTopWindow->GetCurrentInnerWindow();
     }
   }
 
+  // Set to true when we're ready to return from this function.
+  bool ready = false;
+
+  // NOTE: Capturing by reference here is safe, as this function won't return
+  // until one of these callbacks is called.
+  auto resolve = [&] (const CreatedWindowInfo& info) {
+    MOZ_RELEASE_ASSERT(NS_IsMainThread());
+    rv = info.rv();
+    *aWindowIsNew = info.windowOpened();
+    nsTArray<FrameScriptInfo> frameScripts(info.frameScripts());
+    nsCString urlToLoad = info.urlToLoad();
+    TextureFactoryIdentifier textureFactoryIdentifier = info.textureFactoryIdentifier();
+    uint64_t layersId = info.layersId();
+    CompositorOptions compositorOptions = info.compositorOptions();
+    uint32_t maxTouchPoints = info.maxTouchPoints();
+    DimensionInfo dimensionInfo = info.dimensions();
+
+    // Once this function exits, we should try to exit the nested event loop.
+    ready = true;
+
+    // NOTE: We have to handle this immediately in the resolve callback in order
+    // to make sure that we don't process any more IPC messages before returning
+    // from ProvideWindowCommon.
+
+    // Handle the error which we got back from the parent process, if we got
+    // one.
+    if (NS_FAILED(rv)) {
+      return;
+    }
+
+    if (!*aWindowIsNew) {
+      rv = NS_ERROR_ABORT;
+      return;
+    }
+
+    // If the TabChild has been torn down, we don't need to do this anymore.
+    if (NS_WARN_IF(!newChild->IPCOpen() || newChild->IsDestroyed())) {
+      rv = NS_ERROR_ABORT;
+      return;
+    }
+
+    if (layersId == 0) { // if renderFrame is invalid.
+      renderFrame = nullptr;
+    }
+
+    ShowInfo showInfo(EmptyString(), false, false, true, false, 0, 0, 0);
+    auto* opener = nsPIDOMWindowOuter::From(aParent);
+    nsIDocShell* openerShell;
+    if (opener && (openerShell = opener->GetDocShell())) {
+      nsCOMPtr<nsILoadContext> context = do_QueryInterface(openerShell);
+      showInfo = ShowInfo(EmptyString(), false,
+                          context->UsePrivateBrowsing(), true, false,
+                          aTabOpener->WebWidget()->GetDPI(),
+                          aTabOpener->WebWidget()->RoundsWidgetCoordinatesTo(),
+                          aTabOpener->WebWidget()->GetDefaultScale().scale);
+    }
+
+    newChild->SetMaxTouchPoints(maxTouchPoints);
+
+    // Set the opener window for this window before we start loading the document
+    // inside of it. We have to do this before loading the remote scripts, because
+    // they can poke at the document and cause the nsDocument to be created before
+    // the openerwindow
+    nsCOMPtr<mozIDOMWindowProxy> windowProxy = do_GetInterface(newChild->WebNavigation());
+    if (!aForceNoOpener && windowProxy && aParent) {
+      nsPIDOMWindowOuter* outer = nsPIDOMWindowOuter::From(windowProxy);
+      nsPIDOMWindowOuter* parent = nsPIDOMWindowOuter::From(aParent);
+      outer->SetOpenerWindow(parent, *aWindowIsNew);
+    }
+
+    // Unfortunately we don't get a window unless we've shown the frame.  That's
+    // pretty bogus; see bug 763602.
+    newChild->DoFakeShow(textureFactoryIdentifier, layersId, compositorOptions,
+                        renderFrame, showInfo);
+
+    newChild->RecvUpdateDimensions(dimensionInfo);
+
+    for (size_t i = 0; i < frameScripts.Length(); i++) {
+      FrameScriptInfo& info = frameScripts[i];
+      if (!newChild->RecvLoadRemoteScript(info.url(), info.runInGlobalScope())) {
+        MOZ_CRASH();
+      }
+    }
+
+    if (!urlToLoad.IsEmpty()) {
+      newChild->RecvLoadURL(urlToLoad, showInfo);
+    }
+
+    nsCOMPtr<mozIDOMWindowProxy> win = do_GetInterface(newChild->WebNavigation());
+    win.forget(aReturn);
+  };
+
+  // NOTE: Capturing by reference here is safe, as this function won't return
+  // until one of these callbacks is called.
+  auto reject = [&] (ResponseRejectReason) {
+    MOZ_RELEASE_ASSERT(NS_IsMainThread());
+    NS_WARNING("windowCreated promise rejected");
+    rv = NS_ERROR_NOT_AVAILABLE;
+    ready = true;
+  };
+
   // Send down the request to open the window.
-  RefPtr<CreateWindowPromise> windowCreated;
   if (aIframeMoz) {
     MOZ_ASSERT(aTabOpener);
     nsAutoCString url;
     if (aURI) {
       aURI->GetSpec(url);
     } else {
       // We can't actually send a nullptr up as the URI, since IPDL doesn't let us
       // send nullptr's for primitives. We indicate that the nsString for the URI
       // should be converted to a nullptr by voiding the string.
       url.SetIsVoid(true);
     }
 
     // NOTE: BrowserFrameOpenWindowPromise is the same type as
     // CreateWindowPromise, and this code depends on that fact.
-    windowCreated =
-      newChild->SendBrowserFrameOpenWindow(aTabOpener, renderFrame, NS_ConvertUTF8toUTF16(url),
-                                           name, NS_ConvertUTF8toUTF16(features));
+    newChild->SendBrowserFrameOpenWindow(aTabOpener, renderFrame,
+                                         NS_ConvertUTF8toUTF16(url),
+                                         name, NS_ConvertUTF8toUTF16(features),
+                                         Move(resolve), Move(reject));
   } else {
     nsAutoCString baseURIString;
     float fullZoom;
     nsCOMPtr<nsIPrincipal> triggeringPrincipal;
     rv = GetWindowParamsFromParent(aParent, baseURIString, &fullZoom,
                                    getter_AddRefs(triggeringPrincipal));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
@@ -937,59 +1031,23 @@ ContentChild::ProvideWindowCommon(TabChi
 
     OptionalURIParams uriToLoad;
     if (aURI) {
       SerializeURI(aURI, uriToLoad);
     } else {
       uriToLoad = mozilla::void_t();
     }
 
-    windowCreated =
-      SendCreateWindow(aTabOpener, newChild, renderFrame,
-                       aChromeFlags, aCalledFromJS, aPositionSpecified,
-                       aSizeSpecified,
-                       uriToLoad,
-                       features,
-                       baseURIString,
-                       fullZoom,
-                       Principal(triggeringPrincipal));
+    SendCreateWindow(aTabOpener, newChild, renderFrame,
+                     aChromeFlags, aCalledFromJS, aPositionSpecified,
+                     aSizeSpecified, uriToLoad, features, baseURIString,
+                     fullZoom, Principal(triggeringPrincipal),
+                     Move(resolve), Move(reject));
   }
 
-  // Await the promise being resolved. When the promise is resolved, we'll set
-  // the `ready` local variable, which will cause us to exit our nested event
-  // loop.
-  //
-  // NOTE: We need to run this callback on the StableStateEventTarget because we
-  // need to resolve our runnable and exit from the nested event loop before
-  // processing any events which were sent after the reply to CreateWindow was
-  // sent.
-  bool ready = false;
-  windowCreated->Then(nsContentUtils::GetStableStateEventTarget(), __func__,
-                      [&] (const CreatedWindowInfo& info) {
-                        MOZ_RELEASE_ASSERT(NS_IsMainThread(),
-                                           "windowCreated->Then must run on the main thread");
-                        rv = info.rv();
-                        *aWindowIsNew = info.windowOpened();
-                        frameScripts = info.frameScripts();
-                        urlToLoad = info.urlToLoad();
-                        textureFactoryIdentifier = info.textureFactoryIdentifier();
-                        layersId = info.layersId();
-                        compositorOptions = info.compositorOptions();
-                        maxTouchPoints = info.maxTouchPoints();
-                        dimensionInfo = info.dimensions();
-                        ready = true;
-                      },
-                      [&] (const CreateWindowPromise::RejectValueType aReason) {
-                        MOZ_RELEASE_ASSERT(NS_IsMainThread(),
-                                           "windowCreated->Then must run on the main thread");
-                        NS_WARNING("windowCreated promise rejected");
-                        rv = NS_ERROR_NOT_AVAILABLE;
-                        ready = true;
-                      });
-
   // =======================
   // Begin Nested Event Loop
   // =======================
 
   // We have to wait for a response from either SendCreateWindow or
   // SendBrowserFrameOpenWindow with information we're going to need to return
   // from this function, So we spin a nested event loop until they get back to
   // us.
@@ -1024,81 +1082,19 @@ ContentChild::ProvideWindowCommon(TabChi
   if (parentTopInnerWindow) {
     parentTopInnerWindow->Resume();
   }
 
   // =====================
   // End Nested Event Loop
   // =====================
 
-  // Handle the error which we got back from the parent process, if we got
-  // one.
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  if (!*aWindowIsNew) {
-    return NS_ERROR_ABORT;
-  }
-
-  // If the TabChild has been torn down, we don't need to do this anymore.
-  if (NS_WARN_IF(!newChild->IPCOpen() || newChild->IsDestroyed())) {
-    return NS_ERROR_ABORT;
-  }
-
-  if (layersId == 0) { // if renderFrame is invalid.
-    renderFrame = nullptr;
-  }
-
-  ShowInfo showInfo(EmptyString(), false, false, true, false, 0, 0, 0);
-  auto* opener = nsPIDOMWindowOuter::From(aParent);
-  nsIDocShell* openerShell;
-  if (opener && (openerShell = opener->GetDocShell())) {
-    nsCOMPtr<nsILoadContext> context = do_QueryInterface(openerShell);
-    showInfo = ShowInfo(EmptyString(), false,
-                        context->UsePrivateBrowsing(), true, false,
-                        aTabOpener->WebWidget()->GetDPI(),
-                        aTabOpener->WebWidget()->RoundsWidgetCoordinatesTo(),
-                        aTabOpener->WebWidget()->GetDefaultScale().scale);
-  }
-
-  newChild->SetMaxTouchPoints(maxTouchPoints);
-
-  // Set the opener window for this window before we start loading the document
-  // inside of it. We have to do this before loading the remote scripts, because
-  // they can poke at the document and cause the nsDocument to be created before
-  // the openerwindow
-  nsCOMPtr<mozIDOMWindowProxy> windowProxy = do_GetInterface(newChild->WebNavigation());
-  if (!aForceNoOpener && windowProxy && aParent) {
-    nsPIDOMWindowOuter* outer = nsPIDOMWindowOuter::From(windowProxy);
-    nsPIDOMWindowOuter* parent = nsPIDOMWindowOuter::From(aParent);
-    outer->SetOpenerWindow(parent, *aWindowIsNew);
-  }
-
-  // Unfortunately we don't get a window unless we've shown the frame.  That's
-  // pretty bogus; see bug 763602.
-  newChild->DoFakeShow(textureFactoryIdentifier, layersId, compositorOptions,
-                       renderFrame, showInfo);
-
-  newChild->RecvUpdateDimensions(dimensionInfo);
-
-  for (size_t i = 0; i < frameScripts.Length(); i++) {
-    FrameScriptInfo& info = frameScripts[i];
-    if (!newChild->RecvLoadRemoteScript(info.url(), info.runInGlobalScope())) {
-      MOZ_CRASH();
-    }
-  }
-
-  if (!urlToLoad.IsEmpty()) {
-    newChild->RecvLoadURL(urlToLoad, showInfo);
-  }
-
-  nsCOMPtr<mozIDOMWindowProxy> win = do_GetInterface(newChild->WebNavigation());
-  win.forget(aReturn);
-  return NS_OK;
+  // We should have the results already set by the callbacks.
+  MOZ_ASSERT_IF(NS_SUCCEEDED(rv), *aReturn);
+  return rv;
 }
 
 void
 ContentChild::GetProcessName(nsAString& aName) const
 {
   aName.Assign(mProcessName);
 }
 
--- a/dom/tests/browser/browser_noopener.js
+++ b/dom/tests/browser/browser_noopener.js
@@ -39,17 +39,17 @@ async function doTests(private, containe
   for (let test of TESTS) {
     const testid = `${test.id} (private=${private}, container=${container}, alwaysNewWindow=${alwaysNewWindow})`;
     let originalTab = BrowserTestUtils.addTab(window.gBrowser, TEST_URL, tabOpenOptions);
     await BrowserTestUtils.browserLoaded(originalTab.linkedBrowser);
     await BrowserTestUtils.switchTab(window.gBrowser, originalTab);
 
     let waitFor;
     if (test.newWindow || alwaysNewWindow) {
-      waitFor = BrowserTestUtils.waitForNewWindow(window.gBrowser, TARGET_URL, true);
+      waitFor = BrowserTestUtils.waitForNewWindow(TARGET_URL);
       // Confirm that this window has private browsing set if we're doing a private browsing test
     } else {
       waitFor = BrowserTestUtils.waitForNewTab(window.gBrowser, TARGET_URL, true);
     }
 
     BrowserTestUtils.synthesizeMouseAtCenter(test.id, {}, window.gBrowser.getBrowserForTab(originalTab));
 
     let tab;
--- a/ipc/glue/MessageChannel.cpp
+++ b/ipc/glue/MessageChannel.cpp
@@ -479,36 +479,36 @@ private:
 
     // Next item in mChan->mTransactionStack.
     AutoEnterTransaction *mNext;
 
     // Pointer the a reply received for this message, if one was received.
     UniquePtr<IPC::Message> mReply;
 };
 
-class PromiseReporter final : public nsIMemoryReporter
+class PendingResponseReporter final : public nsIMemoryReporter
 {
-    ~PromiseReporter() {}
+    ~PendingResponseReporter() {}
 public:
     NS_DECL_THREADSAFE_ISUPPORTS
 
     NS_IMETHOD
     CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData,
                    bool aAnonymize) override
     {
         MOZ_COLLECT_REPORT(
-            "unresolved-ipc-promises", KIND_OTHER, UNITS_COUNT, MessageChannel::gUnresolvedPromises,
-            "Outstanding IPC async message promises that is still not resolved.");
+            "unresolved-ipc-responses", KIND_OTHER, UNITS_COUNT, MessageChannel::gUnresolvedResponses,
+            "Outstanding IPC async message responses that are still not resolved.");
         return NS_OK;
     }
 };
 
-NS_IMPL_ISUPPORTS(PromiseReporter, nsIMemoryReporter)
-
-Atomic<size_t> MessageChannel::gUnresolvedPromises;
+NS_IMPL_ISUPPORTS(PendingResponseReporter, nsIMemoryReporter)
+
+Atomic<size_t> MessageChannel::gUnresolvedResponses;
 
 MessageChannel::MessageChannel(const char* aName,
                                IToplevelProtocol *aListener)
   : mName(aName),
     mListener(aListener),
     mChannelState(ChannelClosed),
     mSide(UnknownSide),
     mLink(nullptr),
@@ -549,17 +549,17 @@ MessageChannel::MessageChannel(const cha
 
 #ifdef OS_WIN
     mEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr);
     MOZ_RELEASE_ASSERT(mEvent, "CreateEvent failed! Nothing is going to work!");
 #endif
 
     static Atomic<bool> registered;
     if (registered.compareExchange(false, true)) {
-        RegisterStrongMemoryReporter(new PromiseReporter());
+        RegisterStrongMemoryReporter(new PendingResponseReporter());
     }
 }
 
 MessageChannel::~MessageChannel()
 {
     MOZ_COUNT_DTOR(ipc::MessageChannel);
     IPC_ASSERT(mCxxStackFrames.empty(), "mismatched CxxStackFrame ctor/dtors");
 #ifdef OS_WIN
@@ -712,23 +712,21 @@ MessageChannel::Clear()
     if (gParentProcessBlocker == this) {
         gParentProcessBlocker = nullptr;
     }
 
     if (mWorkerLoop) {
         mWorkerLoop->RemoveDestructionObserver(this);
     }
 
-    gUnresolvedPromises -= mPendingPromises.size();
-    for (auto& pair : mPendingPromises) {
-        pair.second.mRejectFunction(pair.second.mPromise,
-                                    PromiseRejectReason::ChannelClosed,
-                                    __func__);
+    gUnresolvedResponses -= mPendingResponses.size();
+    for (auto& pair : mPendingResponses) {
+        pair.second.get()->Reject(ResponseRejectReason::ChannelClosed);
     }
-    mPendingPromises.clear();
+    mPendingResponses.clear();
 
     mWorkerLoop = nullptr;
     delete mLink;
     mLink = nullptr;
 
     mOnChannelConnectedTask->Cancel();
 
     if (mChannelErrorTask) {
@@ -946,46 +944,43 @@ MessageChannel::StopPostponingSends()
     }
 
     // We unset this after SendMessage so we can make correct thread
     // assertions in MessageLink.
     mIsPostponingSends = false;
     mPostponedSends.clear();
 }
 
-already_AddRefed<MozPromiseRefcountable>
-MessageChannel::PopPromise(const Message& aMsg)
+UniquePtr<MessageChannel::UntypedCallbackHolder>
+MessageChannel::PopCallback(const Message& aMsg)
 {
-    auto iter = mPendingPromises.find(aMsg.seqno());
-    if (iter != mPendingPromises.end()) {
-        PromiseHolder ret = iter->second;
-        mPendingPromises.erase(iter);
-        gUnresolvedPromises--;
-        return ret.mPromise.forget();
+    auto iter = mPendingResponses.find(aMsg.seqno());
+    if (iter != mPendingResponses.end()) {
+        UniquePtr<MessageChannel::UntypedCallbackHolder> ret = Move(iter->second);
+        mPendingResponses.erase(iter);
+        gUnresolvedResponses--;
+        return ret;
     }
     return nullptr;
 }
 
 void
-MessageChannel::RejectPendingPromisesForActor(ActorIdType aActorId)
+MessageChannel::RejectPendingResponsesForActor(ActorIdType aActorId)
 {
-  auto itr = mPendingPromises.begin();
-  while (itr != mPendingPromises.end()) {
-    if (itr->second.mActorId != aActorId) {
+  auto itr = mPendingResponses.begin();
+  while (itr != mPendingResponses.end()) {
+    if (itr->second.get()->mActorId != aActorId) {
       ++itr;
       continue;
     }
-    auto& promise = itr->second.mPromise;
-    itr->second.mRejectFunction(promise,
-                                PromiseRejectReason::ActorDestroyed,
-                                __func__);
+    itr->second.get()->Reject(ResponseRejectReason::ActorDestroyed);
     // Take special care of advancing the iterator since we are
     // removing it while iterating.
-    itr = mPendingPromises.erase(itr);
-    gUnresolvedPromises--;
+    itr = mPendingResponses.erase(itr);
+    gUnresolvedResponses--;
   }
 }
 
 class BuildIDMessage : public IPC::Message
 {
 public:
     BuildIDMessage()
         : IPC::Message(MSG_ROUTING_NONE, BUILD_ID_MESSAGE_TYPE)
--- a/ipc/glue/MessageChannel.h
+++ b/ipc/glue/MessageChannel.h
@@ -61,24 +61,29 @@ enum class SyncSendError {
     NotConnectedBeforeSend,
     DisconnectedDuringSend,
     CancelledBeforeSend,
     CancelledAfterSend,
     TimedOut,
     ReplyError,
 };
 
-enum class PromiseRejectReason {
+enum class ResponseRejectReason {
     SendError,
     ChannelClosed,
     HandlerRejected,
     ActorDestroyed,
     EndGuard_,
 };
 
+template<typename T>
+using ResolveCallback = std::function<void (T&&)>;
+
+using RejectCallback = std::function<void (ResponseRejectReason)>;
+
 enum ChannelState {
     ChannelClosed,
     ChannelOpening,
     ChannelConnected,
     ChannelTimeout,
     ChannelClosing,
     ChannelError
 };
@@ -95,30 +100,55 @@ class MessageChannel : HasResultCodes, M
 
     typedef mozilla::Monitor Monitor;
 
     // We could templatize the actor type but it would unnecessarily
     // expand the code size. Using the actor address as the
     // identifier is already good enough.
     typedef void* ActorIdType;
 
-    struct PromiseHolder
+public:
+    struct UntypedCallbackHolder
     {
-        RefPtr<MozPromiseRefcountable> mPromise;
+        UntypedCallbackHolder(ActorIdType aActorId,
+                              RejectCallback&& aReject)
+            : mActorId(aActorId)
+            , mReject(Move(aReject))
+        {}
 
-        // For rejecting and removing the pending promises when a
-        // subprotocol is destoryed.
+        virtual ~UntypedCallbackHolder() {}
+
+        void Reject(ResponseRejectReason aReason) {
+            mReject(aReason);
+        }
+
         ActorIdType mActorId;
+        RejectCallback mReject;
+    };
 
-        std::function<void(MozPromiseRefcountable*,
-                           PromiseRejectReason,
-                           const char*)> mRejectFunction;
+    template<typename Value>
+    struct CallbackHolder : public UntypedCallbackHolder
+    {
+        CallbackHolder(ActorIdType aActorId,
+                       ResolveCallback<Value>&& aResolve,
+                       RejectCallback&& aReject)
+            : UntypedCallbackHolder(aActorId, Move(aReject))
+            , mResolve(Move(aResolve))
+        {}
+
+        void Resolve(Value&& aReason) {
+            mResolve(Move(aReason));
+        }
+
+        ResolveCallback<Value> mResolve;
     };
-    static Atomic<size_t> gUnresolvedPromises;
-    friend class PromiseReporter;
+
+private:
+    static Atomic<size_t> gUnresolvedResponses;
+    friend class PendingResponseReporter;
 
   public:
     static const int32_t kNoTimeout;
 
     typedef IPC::Message Message;
     typedef IPC::MessageInfo MessageInfo;
     typedef mozilla::ipc::Transport Transport;
 
@@ -183,35 +213,34 @@ class MessageChannel : HasResultCodes, M
     };
     void SetChannelFlags(ChannelFlags aFlags) { mFlags = aFlags; }
     ChannelFlags GetChannelFlags() { return mFlags; }
 
     // Asynchronously send a message to the other side of the channel
     bool Send(Message* aMsg);
 
     // Asynchronously send a message to the other side of the channel
-    // and wait for asynchronous reply
-    template<typename Promise>
-    bool Send(Message* aMsg, Promise* aPromise, ActorIdType aActorId) {
+    // and wait for asynchronous reply.
+    template<typename Value>
+    void Send(Message* aMsg,
+              ActorIdType aActorId,
+              ResolveCallback<Value>&& aResolve,
+              RejectCallback&& aReject) {
         int32_t seqno = NextSeqno();
         aMsg->set_seqno(seqno);
         if (!Send(aMsg)) {
-            return false;
+            aReject(ResponseRejectReason::SendError);
+            return;
         }
-        PromiseHolder holder;
-        holder.mPromise = aPromise;
-        holder.mActorId = aActorId;
-        holder.mRejectFunction = [](MozPromiseRefcountable* aRejectPromise,
-                                    PromiseRejectReason aReason,
-                                    const char* aRejectSite) {
-            static_cast<Promise*>(aRejectPromise)->Reject(aReason, aRejectSite);
-        };
-        mPendingPromises.insert(std::make_pair(seqno, Move(holder)));
-        gUnresolvedPromises++;
-        return true;
+
+        UniquePtr<UntypedCallbackHolder> callback =
+            MakeUnique<CallbackHolder<Value>>(
+                aActorId, Move(aResolve), Move(aReject));
+        mPendingResponses.insert(std::make_pair(seqno, Move(callback)));
+        gUnresolvedResponses++;
     }
 
     void SendBuildID();
 
     // Asynchronously deliver a message back to this side of the
     // channel
     bool Echo(Message* aMsg);
 
@@ -221,22 +250,22 @@ class MessageChannel : HasResultCodes, M
     // Make an Interrupt call to the other side of the channel
     bool Call(Message* aMsg, Message* aReply);
 
     // Wait until a message is received
     bool WaitForIncomingMessage();
 
     bool CanSend() const;
 
-    // Remove and return a promise that needs reply
-    already_AddRefed<MozPromiseRefcountable> PopPromise(const Message& aMsg);
+    // Remove and return a callback that needs reply
+    UniquePtr<UntypedCallbackHolder> PopCallback(const Message& aMsg);
 
-    // Used to reject and remove pending promises owned by the given
+    // Used to reject and remove pending responses owned by the given
     // actor when it's about to be destroyed.
-    void RejectPendingPromisesForActor(ActorIdType aActorId);
+    void RejectPendingResponsesForActor(ActorIdType aActorId);
 
     // If sending a sync message returns an error, this function gives a more
     // descriptive error message.
     SyncSendError LastSendError() const {
         AssertWorkerThread();
         return mLastSendError;
     }
 
@@ -576,17 +605,17 @@ class MessageChannel : HasResultCodes, M
         bool mScheduled : 1;
     };
 
     bool ShouldRunMessage(const Message& aMsg);
     void RunMessage(MessageTask& aTask);
 
     typedef LinkedList<RefPtr<MessageTask>> MessageQueue;
     typedef std::map<size_t, Message> MessageMap;
-    typedef std::map<size_t, PromiseHolder> PromiseMap;
+    typedef std::map<size_t, UniquePtr<UntypedCallbackHolder>> CallbackMap;
     typedef IPC::Message::msgid_t msgid_t;
 
     void WillDestroyCurrentMessageLoop() override;
 
   private:
     // This will be a string literal, so lifetime is not an issue.
     const char* mName;
 
@@ -787,18 +816,18 @@ class MessageChannel : HasResultCodes, M
     // mMonitor.
     bool mIsWaitingForIncoming;
 
     // Map of replies received "out of turn", because of Interrupt
     // in-calls racing with replies to outstanding in-calls.  See
     // https://bugzilla.mozilla.org/show_bug.cgi?id=521929.
     MessageMap mOutOfTurnReplies;
 
-    // Map of async Promises that are still waiting replies.
-    PromiseMap mPendingPromises;
+    // Map of async Callbacks that are still waiting replies.
+    CallbackMap mPendingResponses;
 
     // Stack of Interrupt in-calls that were deferred because of race
     // conditions.
     std::stack<Message> mDeferred;
 
 #ifdef OS_WIN
     HANDLE mEvent;
 #endif
@@ -830,16 +859,16 @@ class MessageChannel : HasResultCodes, M
 void
 CancelCPOWs();
 
 } // namespace ipc
 } // namespace mozilla
 
 namespace IPC {
 template <>
-struct ParamTraits<mozilla::ipc::PromiseRejectReason>
-    : public ContiguousEnumSerializer<mozilla::ipc::PromiseRejectReason,
-                                      mozilla::ipc::PromiseRejectReason::SendError,
-                                      mozilla::ipc::PromiseRejectReason::EndGuard_>
+struct ParamTraits<mozilla::ipc::ResponseRejectReason>
+    : public ContiguousEnumSerializer<mozilla::ipc::ResponseRejectReason,
+                                      mozilla::ipc::ResponseRejectReason::SendError,
+                                      mozilla::ipc::ResponseRejectReason::EndGuard_>
 { };
 } // namespace IPC
 
 #endif  // ifndef ipc_glue_MessageChannel_h
--- a/ipc/ipdl/ipdl/lower.py
+++ b/ipc/ipdl/ipdl/lower.py
@@ -337,17 +337,17 @@ def _makePromise(returns, side, resolver
     if len(returns) > 1:
         resolvetype = _tuple([d.bareType(side) for d in returns])
     else:
         resolvetype = returns[0].bareType(side)
 
     needmove = not all(d.isCopyable() for d in returns)
 
     return _promise(resolvetype,
-                    _PromiseRejectReason.Type(),
+                    _ResponseRejectReason.Type(),
                     ExprLiteral.TRUE if needmove else ExprLiteral.FALSE,
                     resolver=resolver)
 
 def _makeResolver(returns, side):
     if len(returns) > 1:
         resolvetype = _tuple([d.moveType(side) for d in returns])
     else:
         resolvetype = returns[0].moveType(side)
@@ -512,25 +512,25 @@ class _DestroyReason:
     def Type():  return Type('ActorDestroyReason')
 
     Deletion = ExprVar('Deletion')
     AncestorDeletion = ExprVar('AncestorDeletion')
     NormalShutdown = ExprVar('NormalShutdown')
     AbnormalShutdown = ExprVar('AbnormalShutdown')
     FailedConstructor = ExprVar('FailedConstructor')
 
-class _PromiseRejectReason:
+class _ResponseRejectReason:
     @staticmethod
     def Type():
-        return Type('PromiseRejectReason')
-
-    SendError = ExprVar('PromiseRejectReason::SendError')
-    ChannelClosed = ExprVar('PromiseRejectReason::ChannelClosed')
-    HandlerRejected = ExprVar('PromiseRejectReason::HandlerRejected')
-    ActorDestroyed = ExprVar('PromiseRejectReason::ActorDestroyed')
+        return Type('ResponseRejectReason')
+
+    SendError = ExprVar('ResponseRejectReason::SendError')
+    ChannelClosed = ExprVar('ResponseRejectReason::ChannelClosed')
+    HandlerRejected = ExprVar('ResponseRejectReason::HandlerRejected')
+    ActorDestroyed = ExprVar('ResponseRejectReason::ActorDestroyed')
 
 
 ##-----------------------------------------------------------------------------
 ## Intermediate representation (IR) nodes used during lowering
 
 class _ConvertToCxxType(TypeVisitor):
     def __init__(self, side, fq):
         self.side = side
@@ -1015,22 +1015,37 @@ class MessageDecl(ipdl.ast.MessageDecl):
                 return Decl(d.moveType(side), d.name)
             elif sems is 'out':
                 return Decl(d.outType(side), d.name)
             else: assert 0
 
         def makeResolverDecl(returns):
             return Decl(Type(self.resolverName(), ref=2), 'aResolve')
 
+        def makeCallbackResolveDecl(returns):
+            if len(returns) > 1:
+                resolvetype = _tuple([d.bareType(side) for d in returns])
+            else:
+                resolvetype = returns[0].bareType(side)
+
+            return Decl(Type("mozilla::ipc::ResolveCallback", T=resolvetype, ref=2),
+                        'aResolve')
+
+        def makeCallbackRejectDecl(returns):
+            return Decl(Type("mozilla::ipc::RejectCallback", ref=2), 'aReject')
+
         cxxparams = [ ]
         if paramsems is not None:
             cxxparams.extend([ makeDecl(d, paramsems) for d in self.params ])
 
         if returnsems is 'promise' and self.returns:
             pass
+        elif returnsems is 'callback' and self.returns:
+            cxxparams.extend([ makeCallbackResolveDecl(self.returns),
+                               makeCallbackRejectDecl(self.returns) ])
         elif returnsems is 'resolver' and self.returns:
             cxxparams.extend([ makeResolverDecl(self.returns) ])
         elif returnsems is not None:
             cxxparams.extend([ makeDecl(r, returnsems) for r in self.returns ])
 
         if not implicit and self.decl.type.hasImplicitActorParam():
             cxxparams = cxxparams[1:]
 
@@ -1319,18 +1334,18 @@ with some new IPDL/C++ nodes that are tu
                                 Typedef(Type('mozilla::ipc::ProtocolId'),
                                         'ProtocolId'),
                                 Typedef(Type('mozilla::ipc::Transport'),
                                         'Transport'),
                                 Typedef(Type('mozilla::ipc::Endpoint'),
                                         'Endpoint', ['FooSide']),
                                 Typedef(Type('mozilla::ipc::TransportDescriptor'),
                                         'TransportDescriptor'),
-                                Typedef(Type('mozilla::ipc::PromiseRejectReason'),
-                                        'PromiseRejectReason') ])
+                                Typedef(Type('mozilla::ipc::ResponseRejectReason'),
+                                        'ResponseRejectReason') ])
         self.protocolName = None
 
     def visitTranslationUnit(self, tu):
         if tu not in self.visitedTus:
             self.visitedTus.add(tu)
             ipdl.ast.Visitor.visitTranslationUnit(self, tu)
             if not isinstance(tu, TranslationUnit):
                 TranslationUnit.upgrade(tu)
@@ -3141,23 +3156,23 @@ class _GenerateProtocolActorCode(ipdl.as
                                   args=[ kidsvar ])),
                 foreachdestroy,
             ])
             destroysubtree.addstmt(block)
 
         if len(ptype.manages):
             destroysubtree.addstmt(Whitespace.NL)
 
-        # Reject pending promises for actor before calling ActorDestroy().
-        rejectPendingPromiseMethod = ExprSelect(self.protocol.callGetChannel(),
-                                                '->',
-                                                'RejectPendingPromisesForActor')
-        destroysubtree.addstmts([ Whitespace('// Reject owning pending promises.\n',
+        # Reject pending responses for actor before calling ActorDestroy().
+        rejectPendingResponsesMethod = ExprSelect(self.protocol.callGetChannel(),
+                                                  '->',
+                                                  'RejectPendingResponsesForActor')
+        destroysubtree.addstmts([ Whitespace('// Reject owning pending responses.\n',
                                              indent=1),
-                                  StmtExpr(ExprCall(rejectPendingPromiseMethod,
+                                  StmtExpr(ExprCall(rejectPendingResponsesMethod,
                                                     args=[ ExprVar('this') ])),
                                   Whitespace.NL
                                  ])
 
         destroysubtree.addstmts([ Whitespace('// Finally, destroy "us".\n',
                                              indent=1),
                                   StmtExpr(ExprCall(_destroyMethod(),
                                                     args=[ whyvar ]))
@@ -3858,16 +3873,17 @@ class _GenerateProtocolActorCode(ipdl.as
         return block
 
 
     def visitMessageDecl(self, md):
         isctor = md.decl.type.isCtor()
         isdtor = md.decl.type.isDtor()
         decltype = md.decl.type
         sendmethod = None
+        promisesendmethod = None
         helpermethod = None
         recvlbl, recvcase = None, None
 
         def addRecvCase(lbl, case):
             if decltype.isAsync():
                 self.asyncSwitch.addcase(lbl, case)
             elif decltype.isSync():
                 self.syncSwitch.addcase(lbl, case)
@@ -3885,26 +3901,29 @@ class _GenerateProtocolActorCode(ipdl.as
                 sendmethod, (recvlbl, recvcase) = self.genAsyncCtor(md)
             elif isctor:
                 sendmethod = self.genBlockingCtorMethod(md)
             elif isdtor and isasync:
                 sendmethod, (recvlbl, recvcase) = self.genAsyncDtor(md)
             elif isdtor:
                 sendmethod = self.genBlockingDtorMethod(md)
             elif isasync:
-                sendmethod, (recvlbl, recvcase) = self.genAsyncSendMethod(md)
+                sendmethod, promisesendmethod, (recvlbl, recvcase) = \
+                    self.genAsyncSendMethod(md)
             else:
                 sendmethod = self.genBlockingSendMethod(md)
 
         # XXX figure out what to do here
         if isdtor and md.decl.type.constructedType().isToplevel():
             sendmethod = None
 
         if sendmethod is not None:
             self.cls.addstmts([ sendmethod, Whitespace.NL ])
+        if promisesendmethod is not None:
+            self.cls.addstmts([ promisesendmethod, Whitespace.NL ])
         if recvcase is not None:
             addRecvCase(recvlbl, recvcase)
             recvlbl, recvcase = None, None
 
         if self.receivesMessage(md):
             if isctor:
                 recvlbl, recvcase = self.genCtorRecvCase(md)
             elif isdtor:
@@ -4112,65 +4131,85 @@ class _GenerateProtocolActorCode(ipdl.as
     def dtorEpilogue(self, md, actorexpr):
         return self.destroyActor(md, actorexpr)
 
     def genRecvAsyncReplyCase(self, md):
         lbl = CaseLabel(md.pqReplyId())
         case = StmtBlock()
         resolve, reason, prologue, desrej, desstmts = self.deserializeAsyncReply(
             md, self.side, errfnRecv, errfnSentinel(_Result.ValuError))
-        ifnotpromise = StmtIf(ExprNot(ExprVar('promise')))
-        ifnotpromise.addifstmts(errfnRecv("Error unknown promise",
+        ifnocallback = StmtIf(ExprNot(ExprVar('callback')))
+        ifnocallback.addifstmts(errfnRecv("Error unknown callback",
                                           _Result.ProcessingError))
-        promise = _makePromise(md.returns, self.side, resolver=True)
-        promiseptr = _makePromise(md.returns, self.side, resolver=True)
-        promiseptr.ptr = 1
-        getpromise = [ Whitespace.NL,
-                       StmtDecl(Decl(_refptr(promise), 'promise'),
-                                init=ExprCall(ExprSelect(ExprCall(ExprSelect(self.protocol.callGetChannel(), '->', 'PopPromise'),
-                                                                  args=[ self.msgvar ]),
-                                                         '.', Type('downcast', T=promise)))),
-                       ifnotpromise ]
+
         if len(md.returns) > 1:
+            resolvetype = _tuple([d.bareType(self.side) for d in md.returns])
             resolvearg = ExprCall(ExprVar('MakeTuple'),
                                   args=[ExprMove(p.var()) for p in md.returns])
         else:
+            resolvetype = md.returns[0].bareType(self.side)
             resolvearg = ExprMove(md.returns[0].var())
 
-        resolvepromise = [ StmtExpr(ExprCall(ExprSelect(ExprVar('promise'), '->', 'Resolve'),
-                                             args=[ resolvearg,
-                                                    ExprVar('__func__')])) ]
-        rejectpromise = [ StmtExpr(ExprCall(ExprSelect(ExprVar('promise'), '->', 'Reject'),
-                                            args=[ reason, ExprVar('__func__') ])) ]
+        untypedcallback = Type("MessageChannel::UntypedCallbackHolder")
+        callbackptr = Type("MessageChannel::CallbackHolder", T=resolvetype)
+        callbackptr.ptr = 1
+        callback = ExprVar('callback')
+
+        getcallback = [ Whitespace.NL,
+                        # Get the untyped callback object by calling PopCallback()
+                        StmtDecl(Decl(_uniqueptr(untypedcallback), 'untypedCallback'),
+                                 init=ExprCall(ExprSelect(self.protocol.callGetChannel(),
+                                                          '->', 'PopCallback'),
+                                               args=[ self.msgvar ])),
+                        # Cast the untyped callback pointer to the correct poiner type
+                        StmtDecl(Decl(callbackptr, callback.name),
+                                 init=ExprCast(ExprCall(ExprSelect(ExprVar('untypedCallback'),
+                                                                   '.', 'get')),
+                                               callbackptr,
+                                               static=1)),
+                        ifnocallback ]
+
+        resolvecallback = [ StmtExpr(ExprCall(ExprSelect(callback, '->', 'Resolve'),
+                                              args=[ resolvearg ])) ]
+        rejectcallback = [ StmtExpr(ExprCall(ExprSelect(callback, '->', 'Reject'),
+                                             args=[ reason ])) ]
         ifresolve = StmtIf(resolve)
         ifresolve.addifstmts(desstmts)
-        ifresolve.addifstmts(resolvepromise)
+        ifresolve.addifstmts(resolvecallback)
         ifresolve.addelsestmts(desrej)
-        ifresolve.addelsestmts(rejectpromise)
+        ifresolve.addelsestmts(rejectcallback)
         case.addstmts(prologue)
-        case.addstmts(getpromise)
+        case.addstmts(getcallback)
         case.addstmt(ifresolve)
         case.addstmt(StmtReturn(_Result.Processed))
         return (lbl, case)
 
     def genAsyncSendMethod(self, md):
         method = MethodDefn(self.makeSendMethodDecl(md))
         msgvar, stmts = self.makeMessage(md, errfnSend)
         retvar, sendstmts = self.sendAsync(md, msgvar)
 
         method.addstmts(stmts
                         +[ Whitespace.NL ]
                         + self.genVerifyMessage(md.decl.type.verify, md.params,
                                                 errfnSend, ExprVar('msg__'))
                         + sendstmts
                         +[ StmtReturn(retvar) ])
 
-        (lbl, case) = self.genRecvAsyncReplyCase(md) if md.returns else (None, None)
-        return method, (lbl, case)
-
+        # Add the promise overload if we need one.
+        if md.returns:
+            promisemethod = MethodDefn(self.makeSendMethodDecl(md, promise=True))
+            stmts = self.sendAsyncWithPromise(md)
+            promisemethod.addstmts(stmts)
+
+            (lbl, case) = self.genRecvAsyncReplyCase(md)
+        else:
+            (promisemethod, lbl, case) = (None, None, None)
+
+        return method, promisemethod, (lbl, case)
 
     def genBlockingSendMethod(self, md, fromActor=None):
         method = MethodDefn(self.makeSendMethodDecl(md))
 
         msgvar, serstmts = self.makeMessage(md, errfnSend, fromActor)
         replyvar = self.replyvar
 
         sendok, sendstmts = self.sendBlocking(md, msgvar, replyvar)
@@ -4339,21 +4378,21 @@ class _GenerateProtocolActorCode(ipdl.as
                                'aParam')
             destructexpr = ExprCall(ExprVar('Tie'),
                                     args=[ p.var() for p in md.returns ])
         else:
             resolvedecl = Decl(md.returns[0].moveType(self.side), 'aParam')
             destructexpr = md.returns[0].var()
         selfvar = ExprVar('self__')
         ifactorisdead = StmtIf(ExprNot(selfvar))
-        ifactorisdead.addifstmts([_printWarningMessage("Not resolving promise because actor is dead."),
+        ifactorisdead.addifstmts([_printWarningMessage("Not resolving response because actor is dead."),
                                   StmtReturn()])
         ifactorisdestroyed = StmtIf(ExprBinary(self.protocol.stateVar(), '==',
                                                self.protocol.deadState()))
-        ifactorisdestroyed.addifstmts([_printWarningMessage("Not resolving promise because actor is destroyed."),
+        ifactorisdestroyed.addifstmts([_printWarningMessage("Not resolving response because actor is destroyed."),
                                   StmtReturn()])
         returnifactorisdead = [ ifactorisdead,
                                 ifactorisdestroyed ]
         resolverfn = ExprLambda([ExprVar.THIS, selfvar, routingId, seqno],
                                  [resolvedecl])
         resolverfn.addstmts(returnifactorisdead
                             + [ StmtDecl(Decl(Type.BOOL, resolve.name),
                                          init=ExprLiteral.TRUE) ]
@@ -4365,25 +4404,25 @@ class _GenerateProtocolActorCode(ipdl.as
                                                        args=[ routingId ])) ]
                             + [ self.checkedWrite(None, resolve, self.replyvar,
                                                   sentinelKey=resolve.name) ]
                             + [ self.checkedWrite(r.ipdltype, r.var(), self.replyvar,
                                                   sentinelKey=r.name)
                                  for r in md.returns ])
         resolverfn.addstmts(sendmsg)
 
-        makepromise = [ Whitespace.NL,
-                        StmtDecl(Decl(Type.INT32, seqno.name),
-                                 init=ExprCall(ExprSelect(self.msgvar, '.', 'seqno'))),
-                        StmtDecl(Decl(Type('WeakPtr', T=ExprVar(self.clsname)),
-                                      selfvar.name),
-                                 init=ExprVar.THIS),
-                        StmtDecl(Decl(resolvertype, 'resolver'),
-                                 init=resolverfn) ]
-        return makepromise
+        makeresolver = [ Whitespace.NL,
+                         StmtDecl(Decl(Type.INT32, seqno.name),
+                                  init=ExprCall(ExprSelect(self.msgvar, '.', 'seqno'))),
+                         StmtDecl(Decl(Type('WeakPtr', T=ExprVar(self.clsname)),
+                                       selfvar.name),
+                                  init=ExprVar.THIS),
+                         StmtDecl(Decl(resolvertype, 'resolver'),
+                                  init=resolverfn) ]
+        return makeresolver
 
     def makeReply(self, md, errfn, routingId):
         if routingId is None:
             routingId = self.protocol.routingId()
         # TODO special cases for async ctor/dtor replies
         if not md.decl.type.hasReply():
             return [ ]
         if md.decl.type.isAsync() and md.decl.type.hasReply():
@@ -4500,17 +4539,17 @@ class _GenerateProtocolActorCode(ipdl.as
         isctor = md.decl.type.isCtor()
         resolve = ExprVar('resolve__')
         reason = ExprVar('reason__')
         desresolve = [ StmtDecl(Decl(Type.BOOL, resolve.name)),
                        self.checkedRead(None, ExprAddrOf(resolve), msgexpr,
                                         ExprAddrOf(itervar),
                                         errfn, "'%s'" % resolve.name,
                                         sentinelKey=resolve.name, errfnSentinel=errfnSent) ]
-        desrej = [ StmtDecl(Decl(_PromiseRejectReason.Type(), reason.name)),
+        desrej = [ StmtDecl(Decl(_ResponseRejectReason.Type(), reason.name)),
                    self.checkedRead(None, ExprAddrOf(reason), msgexpr,
                                     ExprAddrOf(itervar),
                                     errfn, "'%s'" % reason.name,
                                     sentinelKey=reason.name, errfnSentinel=errfnSent),
                    self.endRead(msgvar, itervar) ]
         prologue = ([
             self.logMessage(md, msgexpr, 'Received ',
                             receiving=True),
@@ -4576,47 +4615,37 @@ class _GenerateProtocolActorCode(ipdl.as
                                  sentinelKey=r.name, errfnSentinel=errfnSentinel)
                 for r in md.returns ]
             + [ self.endRead(self.replyvar, itervar) ])
 
         return stmts
 
     def sendAsync(self, md, msgexpr, actor=None):
         sendok = ExprVar('sendok__')
-        retvar = sendok
-        if md.returns:
-            retpromise = ExprVar('promise__')
-            promise = _makePromise(md.returns, self.side, resolver=True)
-            promisedecl = [ Whitespace.NL,
-                            StmtDecl(Decl(_refptr(promise), retpromise.name),
-                                     init=ExprNew(promise, args=[ExprVar('__func__')])) ]
-            rejectifsendok = StmtIf(ExprNot(sendok))
-            rejectifsendok.addifstmts(
-                [ StmtExpr(ExprCall(ExprSelect(retpromise, '->', 'Reject'),
-                                    args=[ _PromiseRejectReason.SendError,
-                                           ExprVar('__func__') ])) ])
+        resolvefn = ExprVar('aResolve')
+        rejectfn = ExprVar('aReject')
+
         sendargs = [ msgexpr ]
         stmts = [ Whitespace.NL,
                   self.logMessage(md, msgexpr, 'Sending ', actor),
                   self.profilerLabel(md) ] + self.transition(md, actor)
-
+        stmts.append(Whitespace.NL)
+
+        # Generate the actual call expression.
+        send = ExprSelect(self.protocol.callGetChannel(actor), '->', 'Send')
         if md.returns:
-            sendargs.append(ExprCall(ExprSelect(retpromise, '.', 'get')))
-            sendargs.append(ExprVar('this'))
-            stmts.extend(promisedecl)
-            retvar = retpromise
-
-        stmts.extend([ Whitespace.NL,
-                       StmtDecl(Decl(Type.BOOL, sendok.name),
-                                init=ExprCall(
-                                    ExprSelect(self.protocol.callGetChannel(actor),
-                                               '->', 'Send'),
-                                    args=sendargs)) ])
-        if md.returns:
-            stmts.append(rejectifsendok)
+            stmts.append(StmtExpr(ExprCall(send, args=[ msgexpr,
+                                                        ExprVar('this'),
+                                                        ExprMove(resolvefn),
+                                                        ExprMove(rejectfn) ])))
+            retvar = None
+        else:
+            stmts.append(StmtDecl(Decl(Type.BOOL, sendok.name),
+                                  init=ExprCall(send, args=[ msgexpr ])))
+            retvar = sendok
 
         return (retvar, stmts)
 
     def sendBlocking(self, md, msgexpr, replyexpr, actor=None):
         sendok = ExprVar('sendok__')
         return (
             sendok,
             ([ Whitespace.NL,
@@ -4633,16 +4662,52 @@ class _GenerateProtocolActorCode(ipdl.as
                                       ExprCall(ExprSelect(self.protocol.callGetChannel(actor),
                                                           '->',
                                                           _sendPrefix(md.decl.type)),
                                                args=[ msgexpr, ExprAddrOf(replyexpr) ]))),
                 ])
             ])
         )
 
+    def sendAsyncWithPromise(self, md):
+        # Create a new promise, and forward to the callback send overload.
+        retpromise = ExprVar('promise__')
+        promise = _makePromise(md.returns, self.side, resolver=True)
+        stmts = [ Whitespace.NL,
+                  StmtDecl(Decl(_refptr(promise), retpromise.name),
+                           init=ExprNew(promise, args=[ExprVar('__func__')])) ]
+
+        if len(md.returns) > 1:
+            resolvetype = _tuple([d.bareType(self.side) for d in md.returns])
+        else:
+            resolvetype = md.returns[0].bareType(self.side)
+        resolvetype.ref = 2
+
+        resolvefn = ExprLambda([ retpromise ],
+                               [ Decl(resolvetype, "aValue") ])
+        resolvefn.addstmts([
+            StmtExpr(ExprCall(ExprSelect(retpromise, '->', 'Resolve'),
+                              args=[ ExprMove(ExprVar('aValue')),
+                                     ExprVar('__func__') ])),
+        ])
+
+        rejectfn = ExprLambda([ retpromise ],
+                              [ Decl(_ResponseRejectReason.Type(), "aReason") ])
+        rejectfn.addstmts([
+            StmtExpr(ExprCall(ExprSelect(retpromise, '->', 'Reject'),
+                              args=[ ExprVar('aReason'),
+                                     ExprVar('__func__') ])),
+        ])
+
+        args = [ p.var() for p in md.params ] + [ resolvefn, rejectfn ]
+        stmts += [ Whitespace.NL,
+                   StmtExpr(ExprCall(ExprVar(md.sendMethod().name), args=args)),
+                   StmtReturn(retpromise) ]
+        return stmts
+
     def callAllocActor(self, md, retsems, side):
         return ExprCall(
             _allocMethod(md.decl.type.constructedType(), side),
             args=md.makeCxxArgs(retsems=retsems, retcallsems='out',
                                 implicit=0))
 
     def callActorDestroy(self, actorexpr, why=_DestroyReason.Deletion):
         return ExprCall(ExprSelect(actorexpr, '->', 'DestroySubtree'),
@@ -4681,29 +4746,34 @@ class _GenerateProtocolActorCode(ipdl.as
         ])
         return [ failif ]
 
     def makeDtorMethodDecl(self, md):
         decl = self.makeSendMethodDecl(md)
         decl.static = 1
         return decl
 
-    def makeSendMethodDecl(self, md):
+    def makeSendMethodDecl(self, md, promise=False):
         implicit = md.decl.type.hasImplicitActorParam()
         if md.decl.type.isAsync() and md.returns:
-            returnsems = 'promise'
-            rettype = _refptr(Type(md.promiseName()))
+            if promise:
+                returnsems = 'promise'
+                rettype = _refptr(Type(md.promiseName()))
+            else:
+                returnsems = 'callback'
+                rettype = Type.VOID
         else:
+            assert not promise
             returnsems = 'out'
             rettype = Type.BOOL
         decl = MethodDecl(
             md.sendMethod().name,
             params=md.makeCxxParams(paramsems='in', returnsems=returnsems,
                                     side=self.side, implicit=implicit),
-            warn_unused=(self.side == 'parent'),
+            warn_unused=(self.side == 'parent' and returnsems != 'callback'),
             ret=rettype)
         if md.decl.type.isCtor():
             decl.ret = md.actorDecl().bareType(self.side)
         return decl
 
     def logMessage(self, md, msgptr, pfx, actor=None, receiving=False):
         actorname = _actorName(self.protocol.name, self.side)
 
--- a/ipc/ipdl/test/cxx/TestAsyncReturns.cpp
+++ b/ipc/ipdl/test/cxx/TestAsyncReturns.cpp
@@ -26,34 +26,47 @@ TestAsyncReturnsParent::~TestAsyncReturn
 
 void
 TestAsyncReturnsParent::Main()
 {
   SendNoReturn()->Then(MessageLoop::current()->SerialEventTarget(), __func__,
                        [](bool unused) {
                          fail("resolve handler should not be called");
                        },
-                       [](PromiseRejectReason aReason) {
+                       [](ResponseRejectReason aReason) {
                          // MozPromise asserts in debug build if the
                          // handler is not called
-                         if (aReason != PromiseRejectReason::ChannelClosed) {
+                         if (aReason != ResponseRejectReason::ChannelClosed) {
                            fail("reject with wrong reason");
                          }
                          passed("reject handler called on channel close");
                        });
   SendPing()->Then(MessageLoop::current()->SerialEventTarget(), __func__,
                    [this](bool one) {
                      if (one) {
                        passed("take one argument");
                      } else {
                        fail("get one argument but has wrong value");
                      }
-                     Close();
+
+                     // Also try with the callback-based API.
+                     SendPing(
+                       [this](bool one) {
+                         if (one) {
+                           passed("take one argument");
+                         } else {
+                           fail("get one argument but has wrong value");
+                         }
+                         Close();
+                       },
+                       [](ResponseRejectReason aReason) {
+                         fail("sending Ping");
+                       });
                    },
-                   [](PromiseRejectReason aReason) {
+                   [](ResponseRejectReason aReason) {
                      fail("sending Ping");
                    });
 }
 
 
 mozilla::ipc::IPCResult
 TestAsyncReturnsParent::RecvPong(PongResolver&& aResolve)
 {
@@ -89,17 +102,17 @@ TestAsyncReturnsChild::RecvPing(PingReso
                    [aResolve](const Tuple<uint32_t, uint32_t>& aParam) {
                      if (Get<0>(aParam) == sMagic1 && Get<1>(aParam) == sMagic2) {
                        passed("take two arguments");
                      } else {
                        fail("get two argument but has wrong value");
                      }
                      aResolve(true);
                    },
-                   [](PromiseRejectReason aReason) {
+                   [](ResponseRejectReason aReason) {
                      fail("sending Pong");
                    });
   return IPC_OK();
 }
 
 
 } // namespace _ipdltest
 } // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/js/src/devtools/automation/README
@@ -0,0 +1,37 @@
+autospider.py is intended both as the harness for running automation builds, as
+well as a way to easily reproduce automation builds on a developer workstation.
+Some brave souls also use it as the basis for doing their normal local builds.
+
+Basic usage:
+
+  ./js/src/devtools/automation/autospider.py plain
+
+The script may be run from any directory. This will configure and build the
+source, then run a series of tests. See the --help message for many different
+ways of suppressing various actions or changing the output.
+
+The different possible build+test configurations are controlled mostly by JSON
+files in a variants/ directory (eg there is a .../variants/plain file for the
+above command).
+
+In automation, the test jobs will run with a dynamically loaded library that
+catches crashes and generates minidumps, so that autospider.py can produce a
+readable stack trace at the end of the run. Currently this library is only
+available on linux64, and is built via the following procedure:
+
+    % git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
+    % export PATH=$PATH:$(pwd)/depot_tools
+    % mkdir breakpad
+    % cd breakpad
+    % fetch breakpad
+    % cd src
+    % git fetch https://github.com/hotsphink/breakpad injector
+    % git checkout injector
+    % cd ..
+    % mkdir obj
+    % cd obj
+    % ../src/configure
+    % mkdir ../root
+    % make install DESTDIR=$(pwd)/../root
+
+The shared library will now be in root/usr/local/lib64/libbreakpadinjector.so
--- a/js/src/devtools/automation/autospider.py
+++ b/js/src/devtools/automation/autospider.py
@@ -18,17 +18,17 @@ from threading import Timer
 Dirs = namedtuple('Dirs', ['scripts', 'js_src', 'source', 'tooltool'])
 
 
 def directories(pathmodule, cwd, fixup=lambda s: s):
     scripts = pathmodule.join(fixup(cwd), fixup(pathmodule.dirname(__file__)))
     js_src = pathmodule.abspath(pathmodule.join(scripts, "..", ".."))
     source = pathmodule.abspath(pathmodule.join(js_src, "..", ".."))
     tooltool = pathmodule.abspath(env.get('TOOLTOOL_CHECKOUT',
-                                          pathmodule.join(source, "..")))
+                                          pathmodule.join(source, "..", "..")))
     return Dirs(scripts, js_src, source, tooltool)
 
 # Some scripts will be called with sh, which cannot use backslashed
 # paths. So for direct subprocess.* invocation, use normal paths from
 # DIR, but when running under the shell, use POSIX style paths.
 DIR = directories(os.path, os.getcwd())
 PDIR = directories(posixpath, os.environ["PWD"],
                    fixup=lambda s: re.sub(r'^(\w):', r'/\1', s))
@@ -87,17 +87,16 @@ parser.add_argument('variant', type=str,
 args = parser.parse_args()
 
 OBJDIR = args.objdir
 OUTDIR = os.path.join(OBJDIR, "out")
 POBJDIR = posixpath.join(PDIR.source, args.objdir)
 AUTOMATION = env.get('AUTOMATION', False)
 MAKE = env.get('MAKE', 'make')
 MAKEFLAGS = env.get('MAKEFLAGS', '-j6' + ('' if AUTOMATION else ' -s'))
-UNAME_M = subprocess.check_output(['uname', '-m']).strip()
 
 
 def set_vars_from_script(script, vars):
     '''Run a shell script, then dump out chosen environment variables. The build
        system uses shell scripts to do some configuration that we need to
        borrow. On Windows, the script itself must output the variable settings
        (in the form "export FOO=<value>"), since otherwise there will be
        problems with mismatched Windows/POSIX formats.
@@ -202,17 +201,17 @@ if word_bits is None and args.platform:
     platform_arch = args.platform.split('-')[0]
     if platform_arch in ('win32', 'linux'):
         word_bits = 32
     elif platform_arch in ('win64', 'linux64'):
         word_bits = 64
 
 # Fall back to the word size of the host.
 if word_bits is None:
-    word_bits = 64 if UNAME_M == 'x86_64' else 32
+    word_bits = 64 if platform.architecture()[0] == '64bit' else 32
 
 if 'compiler' in variant:
     compiler = variant['compiler']
 elif platform.system() == 'Darwin':
     compiler = 'clang'
 elif platform.system() == 'Windows':
     compiler = 'cl'
 else:
@@ -251,21 +250,21 @@ elif platform.system() == 'Windows':
                          ['PATH', 'INCLUDE', 'LIB', 'LIBPATH', 'CC', 'CXX',
                           'WINDOWSSDKDIR'])
 
 # Configure flags, based on word length and cross-compilation
 if word_bits == 32:
     if platform.system() == 'Windows':
         CONFIGURE_ARGS += ' --target=i686-pc-mingw32 --host=i686-pc-mingw32'
     elif platform.system() == 'Linux':
-        if UNAME_M != 'arm':
+        if not platform.machine().startswith('arm'):
             CONFIGURE_ARGS += ' --target=i686-pc-linux --host=i686-pc-linux'
 
     # Add SSE2 support for x86/x64 architectures.
-    if UNAME_M != 'arm':
+    if not platform.machine().startswith('arm'):
         if platform.system() == 'Windows':
             sse_flags = '-arch:SSE2'
         else:
             sse_flags = '-msse -msse2 -mfpmath=sse'
         env['CCFLAGS'] = '{0} {1}'.format(env.get('CCFLAGS', ''), sse_flags)
         env['CXXFLAGS'] = '{0} {1}'.format(env.get('CXXFLAGS', ''), sse_flags)
 else:
     if platform.system() == 'Windows':
@@ -284,38 +283,60 @@ timer = Timer(args.timeout, killall)
 timer.daemon = True
 timer.start()
 
 ensure_dir_exists(OBJDIR, clobber=not args.dep and not args.nobuild)
 ensure_dir_exists(OUTDIR, clobber=not args.keep)
 
 
 def run_command(command, check=False, **kwargs):
-    proc = Popen(command, cwd=OBJDIR, **kwargs)
+    kwargs.setdefault('cwd', OBJDIR)
+    proc = Popen(command, **kwargs)
     ACTIVE_PROCESSES.add(proc)
     stdout, stderr = None, None
     try:
         stdout, stderr = proc.communicate()
     finally:
         ACTIVE_PROCESSES.discard(proc)
     status = proc.wait()
     if check and status != 0:
         raise subprocess.CalledProcessError(status, command, output=stderr)
     return stdout, stderr, status
 
 # Add in environment variable settings for this variant. Normally used to
 # modify the flags passed to the shell or to set the GC zeal mode.
 for k, v in variant.get('env', {}).items():
-    env[k] = v.format(
+    env[k.encode('ascii')] = v.encode('ascii').format(
         DIR=DIR.scripts,
         TOOLTOOL_CHECKOUT=DIR.tooltool,
         MOZ_UPLOAD_DIR=env['MOZ_UPLOAD_DIR'],
         OUTDIR=OUTDIR,
     )
 
+if AUTOMATION:
+    # Currently only supported on linux64.
+    if platform.system() == 'Linux' and platform.machine() == 'x86_64':
+        use_minidump = variant.get('use_minidump', True)
+    else:
+        use_minidump = False
+else:
+    use_minidump = False
+
+if use_minidump:
+    env.setdefault('MINIDUMP_SAVE_PATH', env['MOZ_UPLOAD_DIR'])
+    injector_lib = None
+    if platform.system() == 'Linux':
+        injector_lib = os.path.join(DIR.tooltool, 'breakpad-tools', 'libbreakpadinjector.so')
+        env.setdefault('MINIDUMP_STACKWALK',
+                       os.path.join(DIR.tooltool, 'linux64-minidump_stackwalk'))
+    elif platform.system() == 'Darwin':
+        injector_lib = os.path.join(DIR.tooltool, 'breakpad-tools', 'breakpadinjector.dylib')
+    if not injector_lib or not os.path.exists(injector_lib):
+        use_minidump=False
+
 def need_updating_configure(configure):
     if not os.path.exists(configure):
         return True
 
     dep_files = [
         os.path.join(DIR.js_src, 'configure.in'),
         os.path.join(DIR.js_src, 'old-configure.in'),
     ]
@@ -337,20 +358,33 @@ if not args.nobuild:
 
     # Run configure
     if not args.noconf:
         run_command(['sh', '-c', posixpath.join(PDIR.js_src, 'configure') + ' ' + CONFIGURE_ARGS], check=True)
 
     # Run make
     run_command('%s -w %s' % (MAKE, MAKEFLAGS), shell=True, check=True)
 
+    if use_minidump:
+        # Convert symbols to breakpad format.
+        hostdir = os.path.join(OBJDIR, "dist", "host", "bin")
+        os.makedirs(hostdir)
+        shutil.copy(os.path.join(DIR.tooltool, "breakpad-tools", "dump_syms"),
+                    os.path.join(hostdir, 'dump_syms'))
+        run_command([
+            'make',
+            'recurse_syms',
+            'MOZ_SOURCE_REPO=file://' + DIR.source,
+            'RUST_TARGET=0', 'RUSTC_COMMIT=0'
+        ], check=True)
+
 COMMAND_PREFIX = []
 # On Linux, disable ASLR to make shell builds a bit more reproducible.
 if subprocess.call("type setarch >/dev/null 2>&1", shell=True) == 0:
-    COMMAND_PREFIX.extend(['setarch', UNAME_M, '-R'])
+    COMMAND_PREFIX.extend(['setarch', platform.machine(), '-R'])
 
 
 def run_test_command(command, **kwargs):
     _, _, status = run_command(COMMAND_PREFIX + command, check=False, **kwargs)
     return status
 
 test_suites = set(['jstests', 'jittest', 'jsapitests', 'checks'])
 
@@ -387,31 +421,43 @@ if 'all' in args.skip_tests.split(","):
     test_suites = []
 
 # Bug 1391877 - Windows test runs are getting mysterious timeouts when run
 # through taskcluster, but only when running multiple jit-test jobs in
 # parallel. Work around them for now.
 if platform.system() == 'Windows':
     env['JITTEST_EXTRA_ARGS'] = "-j1 " + env.get('JITTEST_EXTRA_ARGS', '')
 
+if use_minidump:
+    # Set up later js invocations to run with the breakpad injector loaded.
+    # Originally, I intended for this to be used with LD_PRELOAD, but when
+    # cross-compiling from 64- to 32-bit, that will fail and produce stderr
+    # output when running any 64-bit commands, which breaks eg mozconfig
+    # processing. So use the --dll command line mechanism universally.
+    for v in ('JSTESTS_EXTRA_ARGS', 'JITTEST_EXTRA_ARGS'):
+        env[v] = "--args='--dll %s' %s" % (injector_lib, env.get(v, ''))
+
 # Always run all enabled tests, even if earlier ones failed. But return the
 # first failed status.
 results = []
 
 # 'checks' is a superset of 'check-style'.
 if 'checks' in test_suites:
     results.append(run_test_command([MAKE, 'check']))
 elif 'check-style' in test_suites:
     results.append(run_test_command([MAKE, 'check-style']))
 
 if 'jittest' in test_suites:
     results.append(run_test_command([MAKE, 'check-jit-test']))
 if 'jsapitests' in test_suites:
     jsapi_test_binary = os.path.join(OBJDIR, 'dist', 'bin', 'jsapi-tests')
-    st = run_test_command([jsapi_test_binary])
+    test_env = env.copy()
+    if use_minidump and platform.system() == 'Linux':
+        test_env['LD_PRELOAD'] = injector_lib
+    st = run_test_command([jsapi_test_binary], env=test_env)
     if st < 0:
         print("PROCESS-CRASH | jsapi-tests | application crashed")
         print("Return code: {}".format(st))
     results.append(st)
 if 'jstests' in test_suites:
     results.append(run_test_command([MAKE, 'check-jstests']))
 
 # FIXME bug 1291449: This would be unnecessary if we could run msan with -mllvm
@@ -493,11 +539,21 @@ if args.variant in ('tsan', 'msan'):
     # distinguished only by pid of the JS process running within each test, so
     # given the 16-bit limitation of pids, it's totally possible that some of
     # these files will be lost due to being overwritten.
     command = ['tar', '-C', OUTDIR, '-zcf',
                os.path.join(env['MOZ_UPLOAD_DIR'], '%s.tar.gz' % args.variant)]
     command += files
     subprocess.call(command)
 
+# Generate stacks from minidumps.
+if use_minidump:
+    venv_python = os.path.join(OBJDIR, "_virtualenv", "bin", "python")
+    run_command([
+        venv_python,
+        os.path.join(DIR.source, "testing/mozbase/mozcrash/mozcrash/mozcrash.py"),
+        os.getenv("TMPDIR", "/tmp"),
+        os.path.join(OBJDIR, "dist/crashreporter-symbols"),
+    ])
+
 for st in results:
     if st != 0:
         sys.exit(st)
--- a/js/src/devtools/automation/variants/arm-sim
+++ b/js/src/devtools/automation/variants/arm-sim
@@ -1,6 +1,7 @@
 {
     "configure-args": "--enable-stdcxx-compat --enable-simulator=arm --target=i686-pc-linux --host=i686-pc-linux",
     "optimize": true,
     "debug": true,
-    "bits": 32
+    "bits": 32,
+    "use_minidump": false
 }
--- a/js/src/devtools/automation/variants/asan
+++ b/js/src/devtools/automation/variants/asan
@@ -1,9 +1,10 @@
 {
     "configure-args": "--enable-debug-symbols='-gline-tables-only' --disable-jemalloc --enable-address-sanitizer",
     "optimize": true,
     "debug": false,
     "compiler": "clang",
     "env": {
         "LLVM_SYMBOLIZER": "{TOOLTOOL_CHECKOUT}/clang/bin/llvm-symbolizer"
-    }
+    },
+    "use_minidump": false
 }
--- a/js/src/devtools/automation/variants/fuzzing
+++ b/js/src/devtools/automation/variants/fuzzing
@@ -2,10 +2,11 @@
     "configure-args": "--enable-fuzzing --enable-debug-symbols='-gline-tables-only -gdwarf-2' --disable-jemalloc --enable-address-sanitizer",
     "optimize": true,
     "debug": false,
     "compiler": "clang",
     "env": {
         "JITTEST_EXTRA_ARGS": "--jitflags=none",
         "JSTESTS_EXTRA_ARGS": "--jitflags=none",
         "LLVM_SYMBOLIZER": "{TOOLTOOL_CHECKOUT}/clang/bin/llvm-symbolizer"
-    }
+    },
+    "use_minidump": false
 }
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -7,16 +7,17 @@
 #include "gc/Marking-inl.h"
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/IntegerRange.h"
 #include "mozilla/ReentrancyGuard.h"
 #include "mozilla/ScopeExit.h"
 #include "mozilla/TypeTraits.h"
 
+#include "jsfriendapi.h"
 #include "jsprf.h"
 
 #include "builtin/ModuleObject.h"
 #include "gc/GCInternals.h"
 #include "gc/Policy.h"
 #include "jit/IonCode.h"
 #include "js/SliceBudget.h"
 #include "vm/ArgumentsObject.h"
@@ -1749,16 +1750,25 @@ GCMarker::processMarkStackTop(SliceBudge
             return;
         }
 
         const Value& v = *vp++;
         if (v.isString()) {
             traverseEdge(obj, v.toString());
         } else if (v.isObject()) {
             JSObject* obj2 = &v.toObject();
+#ifdef DEBUG
+            if (!obj2) {
+                fprintf(stderr,
+                        "processMarkStackTop found ObjectValue(nullptr) "
+                        "at %zu Values from end of array in object:\n",
+                        end - (vp - 1));
+                DumpObject(obj);
+            }
+#endif
             CheckForCompartmentMismatch(obj, obj2);
             if (mark(obj2)) {
                 // Save the rest of this value array for later and start scanning obj2's children.
                 pushValueArray(obj, vp, end);
                 obj = obj2;
                 goto scan_obj;
             }
         } else if (v.isSymbol()) {
--- a/js/src/jit-test/jit_test.py
+++ b/js/src/jit-test/jit_test.py
@@ -362,17 +362,17 @@ def main(argv):
             subprocess.call(debug_cmd + tc.command(prefix, jittests.LIB_DIR, jittests.MODULE_DIR))
             if options.debugger == 'rr':
                 subprocess.call(['rr', 'replay'])
         sys.exit()
 
     try:
         ok = None
         if options.remote:
-            ok = jittests.run_tests_remote(job_list, job_count, prefix, options)
+            ok = jittests.run_tests(job_list, job_count, prefix, options, remote=True)
         else:
             with change_env(test_environment):
                 ok = jittests.run_tests(job_list, job_count, prefix, options)
         if not ok:
             sys.exit(2)
     except OSError:
         if not os.path.exists(prefix[0]):
             print("JS shell argument: file does not exist:"
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -1590,17 +1590,17 @@ jit::BailoutIonToBaseline(JSContext* cx,
         propagatingExceptionForDebugMode = false;
     }
 
     JitSpew(JitSpew_BaselineBailouts, "  Reading from snapshot offset %u size %zu",
             iter.snapshotOffset(), iter.ionScript()->snapshotsListSize());
 
     if (!excInfo)
         iter.ionScript()->incNumBailouts();
-    iter.script()->updateBaselineOrIonRaw(cx->runtime());
+    iter.script()->updateJitCodeRaw(cx->runtime());
 
     // Allocate buffer to hold stack replacement data.
     BaselineStackBuilder builder(iter, 1024);
     if (!builder.init()) {
         ReportOutOfMemory(cx);
         return BAILOUT_RETURN_FATAL_ERROR;
     }
     JitSpew(JitSpew_BaselineBailouts, "  Incoming frame ptr = %p", builder.startFrame());
--- a/js/src/jit/BaselineCacheIRCompiler.cpp
+++ b/js/src/jit/BaselineCacheIRCompiler.cpp
@@ -610,25 +610,25 @@ BaselineCacheIRCompiler::emitCallScripte
 
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     Address getterAddr(stubAddress(reader.stubOffset()));
 
     AutoScratchRegister code(allocator, masm);
     AutoScratchRegister callee(allocator, masm);
     AutoScratchRegister scratch(allocator, masm);
 
-    // First, ensure our getter is non-lazy and has JIT code.
+    // First, ensure our getter is non-lazy.
     {
         FailurePath* failure;
         if (!addFailurePath(&failure))
             return false;
 
         masm.loadPtr(getterAddr, callee);
         masm.branchIfFunctionHasNoScript(callee, failure->label());
-        masm.loadJitCodeRaw(callee, code, failure->label());
+        masm.loadJitCodeRaw(callee, code);
     }
 
     allocator.discardStack(masm);
 
     AutoStubFrame stubFrame(*this);
     stubFrame.enter(masm, scratch);
 
     // Align the stack such that the JitFrameLayout is aligned on
@@ -1708,26 +1708,25 @@ BaselineCacheIRCompiler::emitCallScripte
 {
     AutoScratchRegister scratch1(allocator, masm);
     AutoScratchRegister scratch2(allocator, masm);
 
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     Address setterAddr(stubAddress(reader.stubOffset()));
     ValueOperand val = allocator.useValueRegister(masm, reader.valOperandId());
 
-    // First, ensure our setter is non-lazy and has JIT code. This also loads
-    // the callee in scratch1.
+    // First, ensure our setter is non-lazy. This also loads the callee in
+    // scratch1.
     {
         FailurePath* failure;
         if (!addFailurePath(&failure))
             return false;
 
         masm.loadPtr(setterAddr, scratch1);
         masm.branchIfFunctionHasNoScript(scratch1, failure->label());
-        masm.loadJitCodeRaw(scratch1, scratch2, failure->label());
     }
 
     allocator.discardStack(masm);
 
     AutoStubFrame stubFrame(*this);
     stubFrame.enter(masm, scratch2);
 
     // Align the stack such that the JitFrameLayout is aligned on
@@ -1748,17 +1747,17 @@ BaselineCacheIRCompiler::emitCallScripte
     masm.Push(scratch1);
 
     // Push frame descriptor.
     masm.Push(scratch2);
 
     // Load callee->nargs in scratch2 and the JIT code in scratch.
     Label noUnderflow;
     masm.load16ZeroExtend(Address(scratch1, JSFunction::offsetOfNargs()), scratch2);
-    masm.loadJitCodeRaw(scratch1, scratch1, nullptr);
+    masm.loadJitCodeRaw(scratch1, scratch1);
 
     // Handle arguments underflow.
     masm.branch32(Assembler::BelowOrEqual, scratch2, Imm32(1), &noUnderflow);
     {
         // Call the arguments rectifier.
         TrampolinePtr argumentsRectifier = cx_->runtime()->jitRuntime()->getArgumentsRectifier();
         masm.movePtr(argumentsRectifier, scratch1);
     }
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -1846,17 +1846,17 @@ TryAttachFunApplyStub(JSContext* cx, ICC
 {
     if (argc != 2)
         return true;
 
     if (!thisv.isObject() || !thisv.toObject().is<JSFunction>())
         return true;
     RootedFunction target(cx, &thisv.toObject().as<JSFunction>());
 
-    bool isScripted = target->hasJITCode();
+    bool isScripted = target->hasScript();
 
     // right now, only handle situation where second argument is |arguments|
     if (argv[1].isMagic(JS_OPTIMIZED_ARGUMENTS) && !script->needsArgsObj()) {
         if (isScripted && !stub->hasStub(ICStub::Call_ScriptedApplyArguments)) {
             JitSpew(JitSpew_BaselineIC, "  Generating Call_ScriptedApplyArguments stub");
 
             ICCall_ScriptedApplyArguments::Compiler compiler(
                 cx, typeMonitorFallback->firstMonitorStub(), script->pcToOffset(pc));
@@ -2201,23 +2201,16 @@ TryAttachCallStub(JSContext* cx, ICCall_
         // If callee is not an interpreted constructor, we have to throw.
         if (constructing && !fun->isConstructor())
             return true;
 
         // Likewise, if the callee is a class constructor, we have to throw.
         if (!constructing && fun->isClassConstructor())
             return true;
 
-        if (!fun->hasJITCode()) {
-            // Don't treat this as an unoptimizable case, as we'll add a stub
-            // when the callee becomes hot.
-            *handled = true;
-            return true;
-        }
-
         // Check if this stub chain has already generalized scripted calls.
         if (stub->scriptedStubsAreGeneralized()) {
             JitSpew(JitSpew_BaselineIC, "  Chain already has generalized scripted call stub!");
             return true;
         }
 
         if (stub->scriptedStubCount() >= ICCall_Fallback::MAX_SCRIPTED_STUBS) {
             // Create a Call_AnyScripted stub.
@@ -2860,17 +2853,16 @@ ICCallStubCompiler::guardFunApply(MacroA
     regs.takeUnchecked(target);
 
     masm.branchTestObjClass(Assembler::NotEqual, target, regs.getAny(), &JSFunction::class_,
                             failure);
 
     Register temp = regs.takeAny();
     masm.branchIfFunctionHasNoScript(target, failure);
     masm.branchFunctionKind(Assembler::Equal, JSFunction::ClassConstructor, callee, temp, failure);
-    masm.loadJitCodeRaw(target, temp, failure);
     regs.add(temp);
     return target;
 }
 
 void
 ICCallStubCompiler::pushCallerArguments(MacroAssembler& masm, AllocatableGeneralRegisterSet regs)
 {
     // Initialize copyReg to point to start caller arguments vector.
@@ -3118,33 +3110,27 @@ ICCallScriptedCompiler::generateStubCode
                                     regs.getAny(), &failure);
         }
     }
 
     // Load the start of the target JitCode.
     Register code;
     if (!isConstructing_) {
         code = regs.takeAny();
-        masm.loadJitCodeRaw(callee, code, &failure);
-    } else {
-        masm.loadPtr(Address(callee, JSFunction::offsetOfScript()), callee);
-        Address scriptCode(callee, JSScript::offsetOfBaselineOrIonRaw());
-        masm.branchPtr(Assembler::Equal, scriptCode, ImmPtr(nullptr), &failure);
+        masm.loadJitCodeRaw(callee, code);
     }
 
     // We no longer need R1.
     regs.add(R1);
 
     // Push a stub frame so that we can perform a non-tail call.
     enterStubFrame(masm, regs.getAny());
     if (canUseTailCallReg)
         regs.add(ICTailCallReg);
 
-    Label failureLeaveStubFrame;
-
     if (isConstructing_) {
         // Save argc before call.
         masm.push(argcReg);
 
         // Stack now looks like:
         //      [..., Callee, ThisV, Arg0V, ..., ArgNV, NewTarget, StubFrameHeader, ArgC ]
         masm.loadValue(Address(masm.getStackPointer(), STUB_FRAME_SIZE + sizeof(size_t)), R1);
         masm.push(masm.extractObject(R1, ExtractTemp0));
@@ -3213,17 +3199,17 @@ ICCallScriptedCompiler::generateStubCode
             BaseValueIndex calleeSlot3(masm.getStackPointer(), argcReg, nonArgsSkip + STUB_FRAME_SIZE);
             masm.loadValue(calleeSlot3, R0);
         }
         callee = masm.extractObject(R0, ExtractTemp0);
         regs.add(R0);
         regs.takeUnchecked(callee);
 
         code = regs.takeAny();
-        masm.loadJitCodeRaw(callee, code, &failureLeaveStubFrame);
+        masm.loadJitCodeRaw(callee, code);
 
         // Release callee register, but don't add ExtractTemp0 back into the pool
         // ExtractTemp0 is used later, and if it's allocated to some other register at that
         // point, it will get clobbered when used.
         if (callee != ExtractTemp0)
             regs.add(callee);
 
         if (canUseTailCallReg)
@@ -3314,23 +3300,16 @@ ICCallScriptedCompiler::generateStubCode
         masm.bind(&skipThisReplace);
     }
 
     leaveStubFrame(masm, true);
 
     // Enter type monitor IC to type-check result.
     EmitEnterTypeMonitorIC(masm);
 
-    // Leave stub frame and restore argc for the next stub.
-    assumeStubFrame(masm);
-    masm.bind(&failureLeaveStubFrame);
-    leaveStubFrame(masm, false);
-    if (argcReg != R0.scratchReg())
-        masm.movePtr(argcReg, R0.scratchReg());
-
     masm.bind(&failure);
     EmitStubGuardFailure(masm);
     return true;
 }
 
 typedef bool (*CopyArrayFn)(JSContext*, HandleArrayObject, MutableHandleValue);
 static const VMFunction CopyArrayInfo = FunctionInfo<CopyArrayFn>(CopyArray, "CopyArray");
 
@@ -3732,17 +3711,17 @@ ICCall_ScriptedApplyArray::Compiler::gen
     masm.load32(Address(argcReg, ObjectElements::offsetOfInitializedLength()), argcReg);
 
     masm.Push(argcReg);
     masm.Push(target);
     masm.Push(scratch);
 
     // Load nargs into scratch for underflow check, and then load jitcode pointer into target.
     masm.load16ZeroExtend(Address(target, JSFunction::offsetOfNargs()), scratch);
-    masm.loadJitCodeRaw(target, target, nullptr);
+    masm.loadJitCodeRaw(target, target);
 
     // Handle arguments underflow.
     Label noUnderflow;
     masm.branch32(Assembler::AboveOrEqual, argcReg, scratch, &noUnderflow);
     {
         // Call the arguments rectifier.
         TrampolinePtr argumentsRectifier = cx->runtime()->jitRuntime()->getArgumentsRectifier();
         masm.movePtr(argumentsRectifier, target);
@@ -3820,17 +3799,17 @@ ICCall_ScriptedApplyArguments::Compiler:
     masm.loadPtr(Address(BaselineFrameReg, 0), argcReg);
     masm.loadPtr(Address(argcReg, BaselineFrame::offsetOfNumActualArgs()), argcReg);
     masm.Push(argcReg);
     masm.Push(target);
     masm.Push(scratch);
 
     // Load nargs into scratch for underflow check, and then load jitcode pointer into target.
     masm.load16ZeroExtend(Address(target, JSFunction::offsetOfNargs()), scratch);
-    masm.loadJitCodeRaw(target, target, nullptr);
+    masm.loadJitCodeRaw(target, target);
 
     // Handle arguments underflow.
     Label noUnderflow;
     masm.branch32(Assembler::AboveOrEqual, argcReg, scratch, &noUnderflow);
     {
         // Call the arguments rectifier.
         TrampolinePtr argumentsRectifier = cx->runtime()->jitRuntime()->getArgumentsRectifier();
         masm.movePtr(argumentsRectifier, target);
@@ -3888,17 +3867,17 @@ ICCall_ScriptedFunCall::Compiler::genera
     masm.branchTestObjClass(Assembler::NotEqual, callee, regs.getAny(), &JSFunction::class_,
                             &failure);
     masm.branchIfFunctionHasNoScript(callee, &failure);
     masm.branchFunctionKind(Assembler::Equal, JSFunction::ClassConstructor,
                             callee, regs.getAny(), &failure);
 
     // Load the start of the target JitCode.
     Register code = regs.takeAny();
-    masm.loadJitCodeRaw(callee, code, &failure);
+    masm.loadJitCodeRaw(callee, code);
 
     // We no longer need R1.
     regs.add(R1);
 
     // Push a stub frame so that we can perform a non-tail call.
     enterStubFrame(masm, regs.getAny());
     if (canUseTailCallReg)
         regs.add(ICTailCallReg);
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -1123,17 +1123,17 @@ jit::FinishDiscardBaselineScript(FreeOp*
 
         // The baseline caches have been wiped out, so the script will need to
         // warm back up before it can be inlined during Ion compilation.
         script->baselineScript()->clearIonCompiledOrInlined();
         return;
     }
 
     BaselineScript* baseline = script->baselineScript();
-    script->setBaselineScript(nullptr, nullptr);
+    script->setBaselineScript(fop->runtime(), nullptr);
     BaselineScript::Destroy(fop, baseline);
 }
 
 void
 jit::AddSizeOfBaselineData(JSScript* script, mozilla::MallocSizeOf mallocSizeOf, size_t* data,
                            size_t* fallbackStubs)
 {
     if (script->hasBaselineScript())
--- a/js/src/jit/BaselineJIT.h
+++ b/js/src/jit/BaselineJIT.h
@@ -503,34 +503,34 @@ struct BaselineScript
     bool hasPendingIonBuilder() const {
         return !!pendingBuilder_;
     }
 
     js::jit::IonBuilder* pendingIonBuilder() {
         MOZ_ASSERT(hasPendingIonBuilder());
         return pendingBuilder_;
     }
-    void setPendingIonBuilder(JSRuntime* maybeRuntime, JSScript* script, js::jit::IonBuilder* builder) {
+    void setPendingIonBuilder(JSRuntime* rt, JSScript* script, js::jit::IonBuilder* builder) {
         MOZ_ASSERT(script->baselineScript() == this);
         MOZ_ASSERT(!builder || !hasPendingIonBuilder());
 
         if (script->isIonCompilingOffThread())
-            script->setIonScript(maybeRuntime, ION_PENDING_SCRIPT);
+            script->setIonScript(rt, ION_PENDING_SCRIPT);
 
         pendingBuilder_ = builder;
 
         // lazy linking cannot happen during asmjs to ion.
         clearDependentWasmImports();
 
-        script->updateBaselineOrIonRaw(maybeRuntime);
+        script->updateJitCodeRaw(rt);
     }
-    void removePendingIonBuilder(JSScript* script) {
-        setPendingIonBuilder(nullptr, script, nullptr);
+    void removePendingIonBuilder(JSRuntime* rt, JSScript* script) {
+        setPendingIonBuilder(rt, script, nullptr);
         if (script->maybeIonScript() == ION_PENDING_SCRIPT)
-            script->setIonScript(nullptr, nullptr);
+            script->setIonScript(rt, nullptr);
     }
 
     const ControlFlowGraph* controlFlowGraph() const {
         return controlFlowGraph_;
     }
 
     void setControlFlowGraph(ControlFlowGraph* controlFlowGraph) {
         controlFlowGraph_ = controlFlowGraph;
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -372,17 +372,17 @@ IsCacheableGetPropCallScripted(JSObject*
     // See IsCacheableGetPropCallNative.
     if (IsWindow(obj))
         return false;
 
     JSFunction& getter = shape->getterValue().toObject().as<JSFunction>();
     if (getter.isNative())
         return false;
 
-    if (!getter.hasJITCode()) {
+    if (!getter.hasScript()) {
         if (isTemporarilyUnoptimizable)
             *isTemporarilyUnoptimizable = true;
         return false;
     }
 
     if (getter.isClassConstructor())
         return false;
 
@@ -688,17 +688,17 @@ EmitCallGetterResultNoGuards(CacheIRWrit
         writer.callNativeGetterResult(receiverId, target);
         writer.typeMonitorResult();
         return;
     }
 
     MOZ_ASSERT(IsCacheableGetPropCallScripted(obj, holder, shape));
 
     JSFunction* target = &shape->getterValue().toObject().as<JSFunction>();
-    MOZ_ASSERT(target->hasJITCode());
+    MOZ_ASSERT(target->hasScript());
     writer.callScriptedGetterResult(receiverId, target);
     writer.typeMonitorResult();
 }
 
 static void
 EmitCallGetterResult(CacheIRWriter& writer, JSObject* obj, JSObject* holder, Shape* shape,
                      ObjOperandId objId, ObjOperandId receiverId, ICState::Mode mode)
 {
@@ -3161,17 +3161,17 @@ IsCacheableSetPropCallScripted(JSObject*
 
     if (!shape->setterObject() || !shape->setterObject()->is<JSFunction>())
         return false;
 
     JSFunction& setter = shape->setterObject()->as<JSFunction>();
     if (setter.isNative())
         return false;
 
-    if (!setter.hasJITCode()) {
+    if (!setter.hasScript()) {
         if (isTemporarilyUnoptimizable)
             *isTemporarilyUnoptimizable = true;
         return false;
     }
 
     if (setter.isClassConstructor())
         return false;
 
@@ -3213,17 +3213,17 @@ EmitCallSetterNoGuards(CacheIRWriter& wr
         writer.callNativeSetter(objId, target, rhsId);
         writer.returnFromIC();
         return;
     }
 
     MOZ_ASSERT(IsCacheableSetPropCallScripted(obj, holder, shape));
 
     JSFunction* target = &shape->setterValue().toObject().as<JSFunction>();
-    MOZ_ASSERT(target->hasJITCode());
+    MOZ_ASSERT(target->hasScript());
     writer.callScriptedSetter(objId, target, rhsId);
     writer.returnFromIC();
 }
 
 bool
 SetPropIRGenerator::tryAttachSetter(HandleObject obj, ObjOperandId objId, HandleId id,
                                     ValOperandId rhsId)
 {
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -4264,17 +4264,17 @@ CodeGenerator::visitCallGeneric(LCallGen
     if (call->mir()->isConstructing()) {
         masm.branchIfNotInterpretedConstructor(calleereg, nargsreg, &invoke);
     } else {
         masm.branchIfFunctionHasNoScript(calleereg, &invoke);
         masm.branchFunctionKind(Assembler::Equal, JSFunction::ClassConstructor, calleereg, objreg, &invoke);
     }
 
     // Knowing that calleereg is a non-native function, load the jit code.
-    masm.loadJitCodeRaw(calleereg, objreg, &invoke);
+    masm.loadJitCodeRaw(calleereg, objreg);
 
     // Nestle the StackPointer up to the argument vector.
     masm.freeStack(unusedStack);
 
     // Construct the IonFramePrefix.
     uint32_t descriptor = MakeFrameDescriptor(masm.framePushed(), JitFrame_IonJS,
                                               JitFrameLayout::Size());
     masm.Push(Imm32(call->numActualArgs()));
@@ -4374,19 +4374,19 @@ CodeGenerator::visitCallKnown(LCallKnown
     MOZ_ASSERT_IF(target->isClassConstructor(), call->isConstructing());
 
     // The calleereg is known to be a non-native function, but might point to
     // a LazyScript instead of a JSScript.
     masm.branchIfFunctionHasNoScript(calleereg, &uncompiled);
 
     // Load non-native jitcode from the script.
     if (call->mir()->needsArgCheck())
-        masm.loadJitCodeRaw(calleereg, objreg, &uncompiled);
+        masm.loadJitCodeRaw(calleereg, objreg);
     else
-        masm.loadJitCodeNoArgCheck(calleereg, objreg, &uncompiled);
+        masm.loadJitCodeNoArgCheck(calleereg, objreg);
 
     // Nestle the StackPointer up to the argument vector.
     masm.freeStack(unusedStack);
 
     // Construct the IonFramePrefix.
     uint32_t descriptor = MakeFrameDescriptor(masm.framePushed(), JitFrame_IonJS,
                                               JitFrameLayout::Size());
     masm.Push(Imm32(call->numActualArgs()));
@@ -4676,17 +4676,17 @@ CodeGenerator::emitApplyGeneric(T* apply
     // Guard that calleereg is an interpreted function with a JSScript.
     masm.branchIfFunctionHasNoScript(calleereg, &invoke);
 
     // Guard that calleereg is not a class constrcuctor
     masm.branchFunctionKind(Assembler::Equal, JSFunction::ClassConstructor,
                             calleereg, objreg, &invoke);
 
     // Knowing that calleereg is a non-native function, load script's jitcode.
-    masm.loadJitCodeRaw(calleereg, objreg, &invoke);
+    masm.loadJitCodeRaw(calleereg, objreg);
 
     // Call with an Ion frame or a rectifier frame.
     {
         // Create the frame descriptor.
         unsigned pushed = masm.framePushed();
         Register stackSpace = extraStackSpace;
         masm.addPtr(Imm32(pushed), stackSpace);
         masm.makeFrameDescriptor(stackSpace, JitFrame_IonJS, JitFrameLayout::Size());
@@ -8024,16 +8024,50 @@ JitRuntime::generateLazyLinkStub(MacroAs
     // CodeGenerator can push it back on the stack with pushReturnAddress.
     masm.popReturnAddress();
 #endif
     masm.jump(ReturnReg);
 
     lazyLinkStubEndOffset_ = masm.currentOffset();
 }
 
+void
+JitRuntime::generateInterpreterStub(MacroAssembler& masm)
+{
+    interpreterStubOffset_ = startTrampolineCode(masm);
+
+#ifdef JS_USE_LINK_REGISTER
+    masm.pushReturnAddress();
+#endif
+
+    AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile());
+    Register temp0 = regs.takeAny();
+    Register temp1 = regs.takeAny();
+    Register temp2 = regs.takeAny();
+
+    masm.loadJSContext(temp0);
+    masm.enterFakeExitFrame(temp0, temp2, ExitFrameType::InterpreterStub);
+    masm.moveStackPtrTo(temp1);
+
+    masm.setupUnalignedABICall(temp2);
+    masm.passABIArg(temp0);
+    masm.passABIArg(temp1);
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, InvokeFromInterpreterStub), MoveOp::GENERAL,
+                     CheckUnsafeCallWithABI::DontCheckHasExitFrame);
+
+    masm.branchIfFalseBool(ReturnReg, masm.failureLabel());
+    masm.leaveExitFrame();
+
+    // InvokeFromInterpreterStub stores the return value in argv[0], where the
+    // caller stored |this|.
+    masm.loadValue(Address(masm.getStackPointer(), JitFrameLayout::offsetOfThis()),
+                   JSReturnOperand);
+    masm.ret();
+}
+
 bool
 JitRuntime::generateTLEventVM(JSContext* cx, MacroAssembler& masm, const VMFunction& f,
                               bool enter)
 {
 #ifdef JS_TRACE_LOGGING
     bool vmEventEnabled = TraceLogTextIdEnabled(TraceLogger_VM);
     bool vmSpecificEventEnabled = TraceLogTextIdEnabled(TraceLogger_VMSpecific);
 
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -197,16 +197,19 @@ JitRuntime::JitRuntime(JSRuntime* rt)
     exceptionTailOffset_(0),
     bailoutTailOffset_(0),
     profilerExitFrameTailOffset_(0),
     enterJITOffset_(0),
     bailoutHandlerOffset_(0),
     argumentsRectifierOffset_(0),
     argumentsRectifierReturnOffset_(0),
     invalidatorOffset_(0),
+    lazyLinkStubOffset_(0),
+    lazyLinkStubEndOffset_(0),
+    interpreterStubOffset_(0),
     debugTrapHandler_(nullptr),
     baselineDebugModeOSRHandler_(nullptr),
     trampolineCode_(nullptr),
     functionWrappers_(nullptr),
     preventBackedgePatching_(false),
     jitcodeGlobalTable_(nullptr)
 {
 }
@@ -306,16 +309,19 @@ JitRuntime::initialize(JSContext* cx, Au
     generateMallocStub(masm);
 
     JitSpew(JitSpew_Codegen, "# Emitting free stub");
     generateFreeStub(masm);
 
     JitSpew(JitSpew_Codegen, "# Emitting lazy link stub");
     generateLazyLinkStub(masm);
 
+    JitSpew(JitSpew_Codegen, "# Emitting interpreter stub");
+    generateInterpreterStub(masm);
+
     JitSpew(JitSpew_Codegen, "# Emitting VM function wrappers");
     for (VMFunction* fun = VMFunction::functions; fun; fun = fun->next) {
         if (functionWrappers_->has(fun)) {
             // Duplicate VMFunction definition. See VMFunction::hash.
             continue;
         }
         JitSpew(JitSpew_Codegen, "# VM function wrapper");
         if (!generateVMWrapper(cx, masm, *fun))
@@ -484,21 +490,23 @@ jit::FreeIonBuilder(IonBuilder* builder)
     js_delete(builder->backgroundCodegen());
     js_delete(builder->alloc().lifoAlloc());
 }
 
 void
 jit::FinishOffThreadBuilder(JSRuntime* runtime, IonBuilder* builder,
                             const AutoLockHelperThreadState& locked)
 {
+    MOZ_ASSERT(runtime);
+
     // Clean the references to the pending IonBuilder, if we just finished it.
     if (builder->script()->baselineScript()->hasPendingIonBuilder() &&
         builder->script()->baselineScript()->pendingIonBuilder() == builder)
     {
-        builder->script()->baselineScript()->removePendingIonBuilder(builder->script());
+        builder->script()->baselineScript()->removePendingIonBuilder(runtime, builder->script());
     }
 
     // If the builder is still in one of the helper thread list, then remove it.
     if (builder->isInList())
         builder->script()->zone()->group()->ionLazyLinkListRemove(builder);
 
     // Clear the recompiling flag of the old ionScript, since we continue to
     // use the old ionScript if recompiling fails.
@@ -557,17 +565,17 @@ jit::LinkIonScript(JSContext* cx, Handle
     IonBuilder* builder;
 
     {
         AutoLockHelperThreadState lock;
 
         // Get the pending builder from the Ion frame.
         MOZ_ASSERT(calleeScript->hasBaselineScript());
         builder = calleeScript->baselineScript()->pendingIonBuilder();
-        calleeScript->baselineScript()->removePendingIonBuilder(calleeScript);
+        calleeScript->baselineScript()->removePendingIonBuilder(cx->runtime(), calleeScript);
 
         // Remove from pending.
         cx->zone()->group()->ionLazyLinkListRemove(builder);
     }
 
     {
         AutoEnterAnalysis enterTypes(cx);
         if (!LinkBackgroundCodeGen(cx, builder)) {
@@ -591,19 +599,19 @@ jit::LazyLinkTopActivation()
     JSContext* cx = TlsContext.get();
     JSJitFrameIter frame(cx);
     LazyLinkExitFrameLayout* ll = frame.exitFrame()->as<LazyLinkExitFrameLayout>();
     RootedScript calleeScript(cx, ScriptFromCalleeToken(ll->jsFrame()->calleeToken()));
 
     LinkIonScript(cx, calleeScript);
 
     MOZ_ASSERT(calleeScript->hasBaselineScript());
-    MOZ_ASSERT(calleeScript->baselineOrIonRawPointer());
-
-    return calleeScript->baselineOrIonRawPointer();
+    MOZ_ASSERT(calleeScript->jitCodeRaw());
+
+    return calleeScript->jitCodeRaw();
 }
 
 /* static */ void
 JitRuntime::Trace(JSTracer* trc, AutoLockForExclusiveAccess& lock)
 {
     MOZ_ASSERT(!JS::CurrentThreadIsHeapMinorCollecting());
 
     // Shared stubs are allocated in the atoms compartment, so do not iterate
@@ -2972,17 +2980,17 @@ jit::Invalidate(TypeZone& types, FreeOp*
             continue;
         MOZ_ASSERT(co->isValid());
 
         JSScript* script = co->script();
         IonScript* ionScript = co->ion();
         if (!ionScript)
             continue;
 
-        script->setIonScript(nullptr, nullptr);
+        script->setIonScript(cx->runtime(), nullptr);
         ionScript->decrementInvalidationCount(fop);
         co->invalidate();
         numInvalidations--;
 
         // Wait for the scripts to get warm again before doing another
         // compile, unless we are recompiling *because* a script got hot
         // (resetUses is false).
         if (resetUses)
@@ -3065,17 +3073,17 @@ FinishInvalidationOf(FreeOp* fop, JSScri
 }
 
 void
 jit::FinishInvalidation(FreeOp* fop, JSScript* script)
 {
     // In all cases, nullptr out script->ion to avoid re-entry.
     if (script->hasIonScript()) {
         IonScript* ion = script->ionScript();
-        script->setIonScript(nullptr, nullptr);
+        script->setIonScript(fop->runtime(), nullptr);
         FinishInvalidationOf(fop, script, ion);
     }
 }
 
 void
 jit::ForbidCompilation(JSContext* cx, JSScript* script)
 {
     JitSpew(JitSpew_IonAbort, "Disabling Ion compilation of script %s:%zu",
--- a/js/src/jit/IonCacheIRCompiler.cpp
+++ b/js/src/jit/IonCacheIRCompiler.cpp
@@ -1036,21 +1036,20 @@ IonCacheIRCompiler::emitCallScriptedGett
                                      JitFrameLayout::Size());
     masm.Push(Imm32(0)); // argc
     masm.Push(scratch);
     masm.Push(Imm32(descriptor));
 
     // Check stack alignment. Add sizeof(uintptr_t) for the return address.
     MOZ_ASSERT(((masm.framePushed() + sizeof(uintptr_t)) % JitStackAlignment) == 0);
 
-    // The getter has JIT code now and we will only discard the getter's JIT
-    // code when discarding all JIT code in the Zone, so we can assume it'll
-    // still have JIT code.
-    MOZ_ASSERT(target->hasJITCode());
-    masm.loadJitCodeRaw(scratch, scratch, nullptr);
+    // The getter currently has a non-lazy script. We will only relazify when
+    // we do a shrinking GC and when that happens we will also purge IC stubs.
+    MOZ_ASSERT(target->hasScript());
+    masm.loadJitCodeRaw(scratch, scratch);
     masm.callJit(scratch);
     masm.storeCallResultValue(output);
 
     masm.freeStack(masm.framePushed() - framePushedBefore);
     return true;
 }
 
 bool
@@ -2063,21 +2062,20 @@ IonCacheIRCompiler::emitCallScriptedSett
                                      JitFrameLayout::Size());
     masm.Push(Imm32(1)); // argc
     masm.Push(scratch);
     masm.Push(Imm32(descriptor));
 
     // Check stack alignment. Add sizeof(uintptr_t) for the return address.
     MOZ_ASSERT(((masm.framePushed() + sizeof(uintptr_t)) % JitStackAlignment) == 0);
 
-    // The setter has JIT code now and we will only discard the setter's JIT
-    // code when discarding all JIT code in the Zone, so we can assume it'll
-    // still have JIT code.
-    MOZ_ASSERT(target->hasJITCode());
-    masm.loadJitCodeRaw(scratch, scratch, nullptr);
+    // The setter currently has a non-lazy script. We will only relazify when
+    // we do a shrinking GC and when that happens we will also purge IC stubs.
+    MOZ_ASSERT(target->hasScript());
+    masm.loadJitCodeRaw(scratch, scratch);
     masm.callJit(scratch);
 
     masm.freeStack(masm.framePushed() - framePushedBefore);
     return true;
 }
 
 typedef bool (*SetArrayLengthFn)(JSContext*, HandleObject, HandleValue, bool);
 static const VMFunction SetArrayLengthInfo =
--- a/js/src/jit/Jit.cpp
+++ b/js/src/jit/Jit.cpp
@@ -122,41 +122,42 @@ EnterJit(JSContext* cx, RunState& state,
     return EnterJitStatus::Ok;
 }
 
 EnterJitStatus
 js::jit::MaybeEnterJit(JSContext* cx, RunState& state)
 {
     JSScript* script = state.script();
 
-    uint8_t* code = script->baselineOrIonRawPointer();
+    uint8_t* code = script->jitCodeRaw();
     do {
-        // If we have JIT-code, we can just use it. Note that Baseline code
-        // contains warm-up checks in the prologue to Ion-compile if needed.
-        if (code)
+        // Make sure we have a BaselineScript: we don't want to call the
+        // interpreter stub here. Note that Baseline code contains warm-up
+        // checks in the prologue to Ion-compile if needed.
+        if (script->hasBaselineScript())
             break;
 
         // Try to Ion-compile.
         if (jit::IsIonEnabled(cx)) {
             jit::MethodStatus status = jit::CanEnterIon(cx, state);
             if (status == jit::Method_Error)
                 return EnterJitStatus::Error;
             if (status == jit::Method_Compiled) {
-                code = script->baselineOrIonRawPointer();
+                code = script->jitCodeRaw();
                 break;
             }
         }
 
         // Try to Baseline-compile.
         if (jit::IsBaselineEnabled(cx)) {
             jit::MethodStatus status = jit::CanEnterBaselineMethod(cx, state);
             if (status == jit::Method_Error)
                 return EnterJitStatus::Error;
             if (status == jit::Method_Compiled) {
-                code = script->baselineOrIonRawPointer();
+                code = script->jitCodeRaw();
                 break;
             }
         }
 
         return EnterJitStatus::NotEntered;
     } while (false);
 
     return EnterJit(cx, state, code);
--- a/js/src/jit/JitCompartment.h
+++ b/js/src/jit/JitCompartment.h
@@ -131,16 +131,19 @@ class JitRuntime
     // Thunk to call malloc/free.
     ExclusiveAccessLockWriteOnceData<uint32_t> mallocStubOffset_;
     ExclusiveAccessLockWriteOnceData<uint32_t> freeStubOffset_;
 
     // Thunk called to finish compilation of an IonScript.
     ExclusiveAccessLockWriteOnceData<uint32_t> lazyLinkStubOffset_;
     ExclusiveAccessLockWriteOnceData<uint32_t> lazyLinkStubEndOffset_;
 
+    // Thunk to enter the interpreter from JIT code.
+    ExclusiveAccessLockWriteOnceData<uint32_t> interpreterStubOffset_;
+
     // Thunk used by the debugger for breakpoint and step mode.
     ExclusiveAccessLockWriteOnceData<JitCode*> debugTrapHandler_;
 
     // Thunk used to fix up on-stack recompile of baseline scripts.
     ExclusiveAccessLockWriteOnceData<JitCode*> baselineDebugModeOSRHandler_;
     ExclusiveAccessLockWriteOnceData<void*> baselineDebugModeOSRHandlerNoFrameRegPopAddr_;
 
     // Code for trampolines and VMFunction wrappers.
@@ -155,16 +158,17 @@ class JitRuntime
     // patch backedges, as some thread is busy modifying data structures.
     mozilla::Atomic<bool> preventBackedgePatching_;
 
     // Global table of jitcode native address => bytecode address mappings.
     UnprotectedData<JitcodeGlobalTable*> jitcodeGlobalTable_;
 
   private:
     void generateLazyLinkStub(MacroAssembler& masm);
+    void generateInterpreterStub(MacroAssembler& masm);
     void generateProfilerExitFrameTailStub(MacroAssembler& masm, Label* profilerExitTail);
     void generateExceptionTailStub(MacroAssembler& masm, void* handler, Label* profilerExitTail);
     void generateBailoutTailStub(MacroAssembler& masm, Label* bailoutTail);
     void generateEnterJIT(JSContext* cx, MacroAssembler& masm);
     void generateArgumentsRectifier(MacroAssembler& masm);
     BailoutTable generateBailoutTable(MacroAssembler& masm, Label* bailoutTail, uint32_t frameClass);
     void generateBailoutHandler(MacroAssembler& masm, Label* bailoutTail);
     void generateInvalidator(MacroAssembler& masm, Label* bailoutTail);
@@ -309,16 +313,19 @@ class JitRuntime
     }
 
     TrampolinePtr lazyLinkStub() const {
         return trampolineCode(lazyLinkStubOffset_);
     }
     TrampolinePtr lazyLinkStubEnd() const {
         return trampolineCode(lazyLinkStubEndOffset_);
     }
+    TrampolinePtr interpreterStub() const {
+        return trampolineCode(interpreterStubOffset_);
+    }
 
     bool hasJitcodeGlobalTable() const {
         return jitcodeGlobalTable_ != nullptr;
     }
 
     JitcodeGlobalTable* getJitcodeGlobalTable() {
         MOZ_ASSERT(hasJitcodeGlobalTable());
         return jitcodeGlobalTable_;
--- a/js/src/jit/JitFrames.cpp
+++ b/js/src/jit/JitFrames.cpp
@@ -828,36 +828,33 @@ ReadAllocation(const JSJitFrameIter& fra
         Register reg = a->toGeneralReg()->reg();
         return frame.machineState().read(reg);
     }
     return *frame.jsFrame()->slotRef(SafepointSlotEntry(a));
 }
 #endif
 
 static void
-TraceThisAndArguments(JSTracer* trc, const JSJitFrameIter& frame)
+TraceThisAndArguments(JSTracer* trc, const JSJitFrameIter& frame, JitFrameLayout* layout)
 {
     // Trace |this| and any extra actual arguments for an Ion frame. Tracinging
     // of formal arguments is taken care of by the frame's safepoint/snapshot,
     // except when the script might have lazy arguments or rest, in which case
     // we trace them as well. We also have to trace formals if we have a
-    // LazyLink frame.
-
-    JitFrameLayout* layout = frame.isExitFrameLayout<LazyLinkExitFrameLayout>()
-                             ? frame.exitFrame()->as<LazyLinkExitFrameLayout>()->jsFrame()
-                             : frame.jsFrame();
+    // LazyLink frame or an InterpreterStub frame.
 
     if (!CalleeTokenIsFunction(layout->calleeToken()))
         return;
 
     size_t nargs = layout->numActualArgs();
     size_t nformals = 0;
 
     JSFunction* fun = CalleeTokenToFunction(layout->calleeToken());
     if (!frame.isExitFrameLayout<LazyLinkExitFrameLayout>() &&
+        !frame.isExitFrameLayout<InterpreterStubExitFrameLayout>() &&
         !fun->nonLazyScript()->mayReadFrameArgsDirectly())
     {
         nformals = fun->nargs();
     }
 
     size_t newTargetOffset = Max(nargs, fun->nargs());
 
     Value* argv = layout->argv();
@@ -900,17 +897,17 @@ TraceIonJSFrame(JSTracer* trc, const JSJ
         // This frame has been invalidated, meaning that its IonScript is no
         // longer reachable through the callee token (JSFunction/JSScript->ion
         // is now nullptr or recompiled). Manually trace it here.
         IonScript::Trace(trc, ionScript);
     } else {
         ionScript = frame.ionScriptFromCalleeToken();
     }
 
-    TraceThisAndArguments(trc, frame);
+    TraceThisAndArguments(trc, frame, frame.jsFrame());
 
     const SafepointIndex* si = ionScript->getSafepointIndex(frame.returnAddressToFp());
 
     SafepointReader safepoint(ionScript, si);
 
     // Scan through slots which contain pointers (or on punboxing systems,
     // actual values).
     SafepointSlotEntry entry;
@@ -958,17 +955,17 @@ static void
 TraceBailoutFrame(JSTracer* trc, const JSJitFrameIter& frame)
 {
     JitFrameLayout* layout = (JitFrameLayout*)frame.fp();
 
     layout->replaceCalleeToken(TraceCalleeToken(trc, layout->calleeToken()));
 
     // We have to trace the list of actual arguments, as only formal arguments
     // are represented in the Snapshot.
-    TraceThisAndArguments(trc, frame);
+    TraceThisAndArguments(trc, frame, frame.jsFrame());
 
     // Under a bailout, do not have a Safepoint to only iterate over GC-things.
     // Thus we use a SnapshotIterator to trace all the locations which would be
     // used to reconstruct the Baseline frame.
     //
     // Note that at the time where this function is called, we have not yet
     // started to reconstruct baseline frames.
 
@@ -1144,21 +1141,28 @@ TraceJitExitFrame(JSTracer* trc, const J
             TraceRootRange(trc, len, vp, "ion-dom-args");
         } else {
             TraceRoot(trc, dom->vp(), "ion-dom-args");
         }
         return;
     }
 
     if (frame.isExitFrameLayout<LazyLinkExitFrameLayout>()) {
-        LazyLinkExitFrameLayout* ll = frame.exitFrame()->as<LazyLinkExitFrameLayout>();
-        JitFrameLayout* layout = ll->jsFrame();
+        auto* layout = frame.exitFrame()->as<LazyLinkExitFrameLayout>();
+        JitFrameLayout* jsLayout = layout->jsFrame();
+        jsLayout->replaceCalleeToken(TraceCalleeToken(trc, jsLayout->calleeToken()));
+        TraceThisAndArguments(trc, frame, jsLayout);
+        return;
+    }
 
-        layout->replaceCalleeToken(TraceCalleeToken(trc, layout->calleeToken()));
-        TraceThisAndArguments(trc, frame);
+    if (frame.isExitFrameLayout<InterpreterStubExitFrameLayout>()) {
+        auto* layout = frame.exitFrame()->as<InterpreterStubExitFrameLayout>();
+        JitFrameLayout* jsLayout = layout->jsFrame();
+        jsLayout->replaceCalleeToken(TraceCalleeToken(trc, jsLayout->calleeToken()));
+        TraceThisAndArguments(trc, frame, jsLayout);
         return;
     }
 
     if (frame.isBareExit()) {
         // Nothing to trace. Fake exit frame pushed for VM functions with
         // nothing to trace on the stack.
         return;
     }
@@ -2410,17 +2414,17 @@ AssertJitStackInvariants(JSContext* cx)
             bool isScriptedCallee = false;
             for (; !frames.done(); ++frames) {
                 size_t calleeFp = reinterpret_cast<size_t>(frames.fp());
                 size_t callerFp = reinterpret_cast<size_t>(frames.prevFp());
                 MOZ_ASSERT(callerFp >= calleeFp);
                 prevFrameSize = frameSize;
                 frameSize = callerFp - calleeFp;
 
-                if (frames.prevType() == JitFrame_Rectifier) {
+                if (frames.isScripted() && frames.prevType() == JitFrame_Rectifier) {
                     MOZ_RELEASE_ASSERT(frameSize % JitStackAlignment == 0,
                       "The rectifier frame should keep the alignment");
 
                     size_t expectedFrameSize = 0
 #if defined(JS_CODEGEN_X86)
                         + sizeof(void*) /* frame pointer */
 #endif
                         + sizeof(Value) * (frames.callee()->nargs() +
--- a/js/src/jit/JitFrames.h
+++ b/js/src/jit/JitFrames.h
@@ -472,16 +472,17 @@ enum class ExitFrameType : uint8_t
 {
     CallNative        = 0x0,
     ConstructNative   = 0x1,
     IonDOMGetter      = 0x2,
     IonDOMSetter      = 0x3,
     IonDOMMethod      = 0x4,
     IonOOLNative      = 0x5,
     IonOOLProxy       = 0x6,
+    InterpreterStub   = 0xFC,
     VMFunction        = 0xFD,
     LazyLink          = 0xFE,
     Bare              = 0xFF,
 };
 
 // GC related data used to keep alive data surrounding the Exit frame.
 class ExitFooterFrame
 {
@@ -853,16 +854,46 @@ inline LazyLinkExitFrameLayout*
 ExitFrameLayout::as<LazyLinkExitFrameLayout>()
 {
     MOZ_ASSERT(is<LazyLinkExitFrameLayout>());
     uint8_t* sp = reinterpret_cast<uint8_t*>(this);
     sp -= LazyLinkExitFrameLayout::offsetOfExitFrame();
     return reinterpret_cast<LazyLinkExitFrameLayout*>(sp);
 }
 
+class InterpreterStubExitFrameLayout
+{
+  protected: // silence clang warning about unused private fields
+    ExitFooterFrame footer_;
+    JitFrameLayout exit_;
+
+  public:
+    static ExitFrameType Type() { return ExitFrameType::InterpreterStub; }
+
+    static inline size_t Size() {
+        return sizeof(InterpreterStubExitFrameLayout);
+    }
+    inline JitFrameLayout* jsFrame() {
+        return &exit_;
+    }
+    static size_t offsetOfExitFrame() {
+        return offsetof(InterpreterStubExitFrameLayout, exit_);
+    }
+};
+
+template <>
+inline InterpreterStubExitFrameLayout*
+ExitFrameLayout::as<InterpreterStubExitFrameLayout>()
+{
+    MOZ_ASSERT(is<InterpreterStubExitFrameLayout>());
+    uint8_t* sp = reinterpret_cast<uint8_t*>(this);
+    sp -= InterpreterStubExitFrameLayout::offsetOfExitFrame();
+    return reinterpret_cast<InterpreterStubExitFrameLayout*>(sp);
+}
+
 class ICStub;
 
 class JitStubFrameLayout : public CommonFrameLayout
 {
     // Info on the stack
     //
     // --------------------
     // |JitStubFrameLayout|
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -1666,31 +1666,27 @@ MacroAssembler::assertRectifierFramePare
         branch32(Assembler::Equal, frameType, Imm32(JitFrame_CppToJSJit), &checkOk);
         assumeUnreachable("Unrecognized frame type preceding RectifierFrame.");
         bind(&checkOk);
     }
 #endif
 }
 
 void
-MacroAssembler::loadJitCodeRaw(Register func, Register dest, Label* failure)
+MacroAssembler::loadJitCodeRaw(Register func, Register dest)
 {
     loadPtr(Address(func, JSFunction::offsetOfScript()), dest);
-    loadPtr(Address(dest, JSScript::offsetOfBaselineOrIonRaw()), dest);
-    if (failure)
-        branchTestPtr(Assembler::Zero, dest, dest, failure);
+    loadPtr(Address(dest, JSScript::offsetOfJitCodeRaw()), dest);
 }
 
 void
-MacroAssembler::loadJitCodeNoArgCheck(Register func, Register dest, Label* failure)
+MacroAssembler::loadJitCodeNoArgCheck(Register func, Register dest)
 {
     loadPtr(Address(func, JSFunction::offsetOfScript()), dest);
-    loadPtr(Address(dest, JSScript::offsetOfBaselineOrIonSkipArgCheck()), dest);
-    if (failure)
-        branchTestPtr(Assembler::Zero, dest, dest, failure);
+    loadPtr(Address(dest, JSScript::offsetOfJitCodeSkipArgCheck()), dest);
 }
 
 void
 MacroAssembler::loadBaselineFramePtr(Register framePtr, Register dest)
 {
     if (framePtr != dest)
         movePtr(framePtr, dest);
     subPtr(Imm32(BaselineFrame::Size()), dest);
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -1912,18 +1912,18 @@ class MacroAssembler : public MacroAssem
     // provided and enabled, then instrumentation will be emitted around call
     // sites.
     bool emitProfilingInstrumentation_;
 
     // Record locations of the call sites.
     Vector<CodeOffset, 0, SystemAllocPolicy> profilerCallSites_;
 
   public:
-    void loadJitCodeRaw(Register callee, Register dest, Label* failure);
-    void loadJitCodeNoArgCheck(Register callee, Register dest, Label* failure);
+    void loadJitCodeRaw(Register callee, Register dest);
+    void loadJitCodeNoArgCheck(Register callee, Register dest);
 
     void loadBaselineFramePtr(Register framePtr, Register dest);
 
     void pushBaselineFramePtr(Register framePtr, Register scratch) {
         loadBaselineFramePtr(framePtr, scratch);
         push(scratch);
     }
 
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -116,16 +116,45 @@ bool
 InvokeFunctionShuffleNewTarget(JSContext* cx, HandleObject obj, uint32_t numActualArgs,
                                uint32_t numFormalArgs, Value* argv, MutableHandleValue rval)
 {
     MOZ_ASSERT(numFormalArgs > numActualArgs);
     argv[1 + numActualArgs] = argv[1 + numFormalArgs];
     return InvokeFunction(cx, obj, true, false, numActualArgs, argv, rval);
 }
 
+bool
+InvokeFromInterpreterStub(JSContext* cx, InterpreterStubExitFrameLayout* frame)
+{
+    JitFrameLayout* jsFrame = frame->jsFrame();
+    CalleeToken token = jsFrame->calleeToken();
+
+    Value* argv = jsFrame->argv();
+    uint32_t numActualArgs = jsFrame->numActualArgs();
+    bool constructing = CalleeTokenIsConstructing(token);
+    RootedFunction fun(cx, CalleeTokenToFunction(token));
+
+    // Ensure new.target immediately follows the actual arguments (the arguments
+    // rectifier added padding). See also InvokeFunctionShuffleNewTarget.
+    if (constructing && numActualArgs < fun->nargs())
+        argv[1 + numActualArgs] = argv[1 + fun->nargs()];
+
+    RootedValue rval(cx);
+    if (!InvokeFunction(cx, fun, constructing,
+                        /* ignoresReturnValue = */ false,
+                        numActualArgs, argv, &rval))
+    {
+        return false;
+    }
+
+    // Overwrite |this| with the return value.
+    argv[0] = rval;
+    return true;
+}
+
 #ifdef JS_SIMULATOR
 static bool
 CheckSimulatorRecursionLimitWithExtra(JSContext* cx, uint32_t extra)
 {
     if (cx->simulator()->overRecursedWithExtra(extra)) {
         ReportOverRecursed(cx);
         return false;
     }
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -662,16 +662,19 @@ class AutoDetectInvalidation
 
 MOZ_MUST_USE bool
 InvokeFunction(JSContext* cx, HandleObject obj0, bool constructing, bool ignoresReturnValue,
                uint32_t argc, Value* argv, MutableHandleValue rval);
 MOZ_MUST_USE bool
 InvokeFunctionShuffleNewTarget(JSContext* cx, HandleObject obj, uint32_t numActualArgs,
                                uint32_t numFormalArgs, Value* argv, MutableHandleValue rval);
 
+class InterpreterStubExitFrameLayout;
+bool InvokeFromInterpreterStub(JSContext* cx, InterpreterStubExitFrameLayout* frame);
+
 bool CheckOverRecursed(JSContext* cx);
 bool CheckOverRecursedWithExtra(JSContext* cx, BaselineFrame* frame,
                                 uint32_t extra, uint32_t earlyCheck);
 
 JSObject* BindVar(JSContext* cx, HandleObject scopeChain);
 MOZ_MUST_USE bool
 DefVar(JSContext* cx, HandlePropertyName dn, unsigned attrs, HandleObject scopeChain);
 MOZ_MUST_USE bool
--- a/js/src/jit/arm/Trampoline-arm.cpp
+++ b/js/src/jit/arm/Trampoline-arm.cpp
@@ -522,19 +522,18 @@ JitRuntime::generateArgumentsRectifier(M
     masm.makeFrameDescriptor(r6, JitFrame_Rectifier, JitFrameLayout::Size());
 
     // Construct JitFrameLayout.
     masm.ma_push(r0); // actual arguments.
     masm.ma_push(r1); // callee token
     masm.ma_push(r6); // frame descriptor.
 
     // Call the target function.
-    // Note that this code assumes the function is JITted.
     masm.andPtr(Imm32(CalleeTokenMask), r1);
-    masm.loadJitCodeRaw(r1, r3, nullptr);
+    masm.loadJitCodeRaw(r1, r3);
     argumentsRectifierReturnOffset_ = masm.callJitNoProfiler(r3);
 
     // arg1
     //  ...
     // argN
     // num actual args
     // callee token
     // sizeDescriptor     <- sp now
--- a/js/src/jit/arm64/Trampoline-arm64.cpp
+++ b/js/src/jit/arm64/Trampoline-arm64.cpp
@@ -392,17 +392,17 @@ JitRuntime::generateArgumentsRectifier(M
     // Make that into a frame descriptor.
     masm.makeFrameDescriptor(r6, JitFrame_Rectifier, JitFrameLayout::Size());
 
     masm.push(r0,  // Number of actual arguments.
               r1,  // Callee token.
               r6); // Frame descriptor.
 
     // Load the address of the code that is getting called.
-    masm.loadJitCodeRaw(r5, r3, nullptr);
+    masm.loadJitCodeRaw(r5, r3);
     argumentsRectifierReturnOffset_ = masm.callJitNoProfiler(r3);
 
     // Clean up!
     // Get the size of the stack frame, and clean up the later fixed frame.
     masm.Ldr(x4, MemOperand(masm.GetStackPointer64(), 24, vixl::PostIndex));
 
     // Now that the size of the stack frame sans the fixed frame has been loaded,
     // add that onto the stack pointer.
--- a/js/src/jit/mips32/Trampoline-mips32.cpp
+++ b/js/src/jit/mips32/Trampoline-mips32.cpp
@@ -499,19 +499,18 @@ JitRuntime::generateArgumentsRectifier(M
     // Push actual arguments.
     masm.storePtr(numActArgsReg, Address(StackPointer, 2 * sizeof(uintptr_t)));
     // Push callee token.
     masm.storePtr(calleeTokenReg, Address(StackPointer, sizeof(uintptr_t)));
     // Push frame descriptor.
     masm.storePtr(t0, Address(StackPointer, 0));
 
     // Call the target function.
-    // Note that this code assumes the function is JITted.
     masm.andPtr(Imm32(CalleeTokenMask), calleeTokenReg);
-    masm.loadJitCodeRaw(calleeTokenReg, t1, nullptr);
+    masm.loadJitCodeRaw(calleeTokenReg, t1);
     argumentsRectifierReturnOffset_ = masm.callJitNoProfiler(t1);
 
     // arg1
     //  ...
     // argN
     // num actual args
     // callee token
     // sizeDescriptor     <- sp now
--- a/js/src/jit/mips64/Trampoline-mips64.cpp
+++ b/js/src/jit/mips64/Trampoline-mips64.cpp
@@ -544,19 +544,18 @@ JitRuntime::generateArgumentsRectifier(M
     // Push actual arguments.
     masm.storePtr(numActArgsReg, Address(StackPointer, 2 * sizeof(uintptr_t)));
     // Push callee token.
     masm.storePtr(calleeTokenReg, Address(StackPointer, sizeof(uintptr_t)));
     // Push frame descriptor.
     masm.storePtr(t2, Address(StackPointer, 0));
 
     // Call the target function.
-    // Note that this code assumes the function is JITted.
     masm.andPtr(Imm32(uint32_t(CalleeTokenMask)), calleeTokenReg);
-    masm.loadJitCodeRaw(calleeTokenReg, t1, nullptr);
+    masm.loadJitCodeRaw(calleeTokenReg, t1);
     argumentsRectifierReturnOffset_ = masm.callJitNoProfiler(t1);
 
     // Remove the rectifier frame.
     // t2 <- descriptor with FrameType.
     masm.loadPtr(Address(StackPointer, 0), t2);
     masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), t2); // t2 <- descriptor.
 
     // Discard descriptor, calleeToken and number of actual arguments.
--- a/js/src/jit/x64/Trampoline-x64.cpp
+++ b/js/src/jit/x64/Trampoline-x64.cpp
@@ -505,19 +505,18 @@ JitRuntime::generateArgumentsRectifier(M
     masm.makeFrameDescriptor(r9, JitFrame_Rectifier, JitFrameLayout::Size());
 
     // Construct JitFrameLayout.
     masm.push(rdx); // numActualArgs
     masm.push(rax); // callee token
     masm.push(r9); // descriptor
 
     // Call the target function.
-    // Note that this code assumes the function is JITted.
     masm.andq(Imm32(uint32_t(CalleeTokenMask)), rax);
-    masm.loadJitCodeRaw(rax, rax, nullptr);
+    masm.loadJitCodeRaw(rax, rax);
     argumentsRectifierReturnOffset_ = masm.callJitNoProfiler(rax);
 
     // Remove the rectifier frame.
     masm.pop(r9);             // r9 <- descriptor with FrameType.
     masm.shrq(Imm32(FRAMESIZE_SHIFT), r9);
     masm.pop(r11);            // Discard calleeToken.
     masm.pop(r11);            // Discard numActualArgs.
     masm.addq(r9, rsp);       // Discard pushed arguments.
--- a/js/src/jit/x86/Trampoline-x86.cpp
+++ b/js/src/jit/x86/Trampoline-x86.cpp
@@ -499,19 +499,18 @@ JitRuntime::generateArgumentsRectifier(M
     masm.makeFrameDescriptor(ebx, JitFrame_Rectifier, JitFrameLayout::Size());
 
     // Construct JitFrameLayout.
     masm.push(edx); // number of actual arguments
     masm.push(eax); // callee token
     masm.push(ebx); // descriptor
 
     // Call the target function.
-    // Note that this assumes the function is JITted.
     masm.andl(Imm32(CalleeTokenMask), eax);
-    masm.loadJitCodeRaw(eax, eax, nullptr);
+    masm.loadJitCodeRaw(eax, eax);
     argumentsRectifierReturnOffset_ = masm.callJitNoProfiler(eax);
 
     // Remove the rectifier frame.
     masm.pop(ebx);            // ebx <- descriptor with FrameType.
     masm.shrl(Imm32(FRAMESIZE_SHIFT), ebx); // ebx <- descriptor.
     masm.pop(edi);            // Discard calleeToken.
     masm.pop(edi);            // Discard number of actual arguments.
 
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -11,16 +11,19 @@
 #include "jsapi.h"
 
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/Sprintf.h"
 
 #include <ctype.h>
+#ifdef __linux__
+# include <dlfcn.h>
+#endif
 #include <stdarg.h>
 #include <string.h>
 #include <sys/stat.h>
 
 #include "jsarray.h"
 #include "jsatom.h"
 #include "jsbool.h"
 #include "jscntxt.h"
@@ -645,16 +648,21 @@ JS::InitSelfHostedCode(JSContext* cx)
     AutoNoteSingleThreadedRegion anstr;
 
     JSRuntime* rt = cx->runtime();
 
     JSAutoRequest ar(cx);
     if (!rt->initializeAtoms(cx))
         return false;
 
+#ifndef JS_CODEGEN_NONE
+    if (!rt->getJitRuntime(cx))
+        return false;
+#endif
+
     if (!rt->initSelfHosting(cx))
         return false;
 
     if (!rt->parentRuntime && !rt->transformToPermanentAtoms(cx))
         return false;
 
     return true;
 }
@@ -7740,8 +7748,22 @@ js::SetStackFormat(JSContext* cx, js::St
     cx->runtime()->setStackFormat(format);
 }
 
 JS_PUBLIC_API(js::StackFormat)
 js::GetStackFormat(JSContext* cx)
 {
     return cx->runtime()->stackFormat();
 }
+
+namespace js {
+
+JS_PUBLIC_API(void)
+NoteIntentionalCrash()
+{
+#ifdef __linux__
+    static bool* addr = reinterpret_cast<bool*>(dlsym(RTLD_DEFAULT, "gBreakpadInjectorEnabled"));
+    if (addr)
+        *addr = false;
+#endif
+}
+
+} // namespace js
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -6763,16 +6763,23 @@ typedef bool
 extern JS_PUBLIC_API(bool)
 SetStopwatchCommitCallback(JSContext*, StopwatchCommitCallback, void*);
 
 typedef bool
 (*GetGroupsCallback)(JSContext*, PerformanceGroupVector&, void*);
 extern JS_PUBLIC_API(bool)
 SetGetPerformanceGroupsCallback(JSContext*, GetGroupsCallback, void*);
 
+/**
+ * Hint that we expect a crash. Currently, the only thing that cares is the
+ * breakpad injector, which (if loaded) will suppress minidump generation.
+ */
+extern JS_PUBLIC_API(void)
+NoteIntentionalCrash();
+
 } /* namespace js */
 
 namespace js {
 
 enum class CompletionKind {
     Normal,
     Return,
     Throw
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -1641,16 +1641,17 @@ CompartmentChecker::check(AbstractFrameP
         check(frame.environmentChain());
 }
 #endif
 
 void
 AutoEnterOOMUnsafeRegion::crash(const char* reason)
 {
     char msgbuf[1024];
+    js::NoteIntentionalCrash();
     SprintfLiteral(msgbuf, "[unhandlable oom] %s", reason);
     MOZ_ReportAssertionFailure(msgbuf, __FILE__, __LINE__);
     MOZ_CRASH();
 }
 
 AutoEnterOOMUnsafeRegion::AnnotateOOMAllocationSizeCallback
 AutoEnterOOMUnsafeRegion::annotateOOMSizeCallback = nullptr;
 
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -1408,41 +1408,37 @@ JSCompartment::addSizeOfIncludingThis(mo
     auto callback = runtime_->sizeOfIncludingThisCompartmentCallback;
     if (callback)
         *privateData += callback(mallocSizeOf, this);
 }
 
 void
 JSCompartment::reportTelemetry()
 {
-    // Only report telemetry for web content and add-ons, not chrome JS.
-    if (isSystem_)
+    // Only report telemetry for web content, not add-ons or chrome JS.
+    if (creationOptions_.addonIdOrNull() || isSystem_)
         return;
 
     // Hazard analysis can't tell that the telemetry callbacks don't GC.
     JS::AutoSuppressGCAnalysis nogc;
 
-    int id = creationOptions_.addonIdOrNull()
-             ? JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_ADDONS
-             : JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT;
-
     // Call back into Firefox's Telemetry reporter.
     for (size_t i = 0; i < size_t(DeprecatedLanguageExtension::Count); i++) {
         if (sawDeprecatedLanguageExtension[i])
-            runtime_->addTelemetry(id, i);
+            runtime_->addTelemetry(JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT, i);
     }
 }
 
 void
 JSCompartment::addTelemetry(const char* filename, DeprecatedLanguageExtension e)
 {
-    // Only report telemetry for web content and add-ons, not chrome JS.
-    if (isSystem_)
+    // Only report telemetry for web content, not add-ons or chrome JS.
+    if (creationOptions_.addonIdOrNull() || isSystem_)
         return;
-    if (!creationOptions_.addonIdOrNull() && (!filename || strncmp(filename, "http", 4) != 0))
+    if (!filename || strncmp(filename, "http", 4) != 0)
         return;
 
     sawDeprecatedLanguageExtension[size_t(e)] = true;
 }
 
 HashNumber
 JSCompartment::randomHashCode()
 {
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -147,17 +147,16 @@ enum {
     JS_TELEMETRY_GC_SCC_SWEEP_TOTAL_MS,
     JS_TELEMETRY_GC_SCC_SWEEP_MAX_PAUSE_MS,
     JS_TELEMETRY_GC_MINOR_REASON,
     JS_TELEMETRY_GC_MINOR_REASON_LONG,
     JS_TELEMETRY_GC_MINOR_US,
     JS_TELEMETRY_GC_NURSERY_BYTES,
     JS_TELEMETRY_GC_PRETENURE_COUNT,
     JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT,
-    JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_ADDONS,
     JS_TELEMETRY_ADDON_EXCEPTIONS,
     JS_TELEMETRY_PRIVILEGED_PARSER_COMPILE_LAZY_AFTER_MS,
     JS_TELEMETRY_WEB_PARSER_COMPILE_LAZY_AFTER_MS,
     JS_TELEMETRY_END
 };
 
 typedef void
 (*JSAccumulateTelemetryDataCallback)(int id, uint32_t sample, const char* key);
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1244,24 +1244,24 @@ ScriptCounts::getThrowCounts(size_t offs
     PCCounts searched = PCCounts(offset);
     PCCounts* elem = std::lower_bound(throwCounts_.begin(), throwCounts_.end(), searched);
     if (elem == throwCounts_.end() || elem->pcOffset() != offset)
         elem = throwCounts_.insert(elem, searched);
     return elem;
 }
 
 void
-JSScript::setIonScript(JSRuntime* maybeRuntime, js::jit::IonScript* ionScript)
+JSScript::setIonScript(JSRuntime* rt, js::jit::IonScript* ionScript)
 {
     MOZ_ASSERT_IF(ionScript != ION_DISABLED_SCRIPT, !baselineScript()->hasPendingIonBuilder());
     if (hasIonScript())
         js::jit::IonScript::writeBarrierPre(zone(), ion);
     ion = ionScript;
     MOZ_ASSERT_IF(hasIonScript(), hasBaselineScript());
-    updateBaselineOrIonRaw(maybeRuntime);
+    updateJitCodeRaw(rt);
 }
 
 js::PCCounts*
 JSScript::maybeGetPCCounts(jsbytecode* pc) {
     MOZ_ASSERT(containsPC(pc));
     return getScriptCounts().maybeGetPCCounts(pcToOffset(pc));
 }
 
@@ -2695,16 +2695,22 @@ JSScript::Create(JSContext* cx, const Re
     RootedScript script(cx, Allocate<JSScript>(cx));
     if (!script)
         return nullptr;
 
     PodZero(script.get());
 
     script->initCompartment(cx);
 
+#ifndef JS_CODEGEN_NONE
+    uint8_t* stubEntry = cx->runtime()->jitRuntime()->interpreterStub().value;
+    script->jitCodeRaw_ = stubEntry;
+    script->jitCodeSkipArgCheck_ = stubEntry;
+#endif
+
     script->selfHosted_ = options.selfHostingMode;
     script->noScriptRval_ = options.noScriptRval;
     script->treatAsRunOnce_ = options.isRunOnce;
 
     script->setSourceObject(sourceObject);
     if (cx->runtime()->lcovOutput().isEnabled() && !script->initScriptName(cx))
         return nullptr;
     script->sourceStart_ = bufStart;
@@ -4468,33 +4474,35 @@ LazyScript::hasUncompiledEnclosingScript
     if (!enclosingScope() || !enclosingScope()->is<FunctionScope>())
         return false;
 
     JSFunction* fun = enclosingScope()->as<FunctionScope>().canonicalFunction();
     return !fun->hasScript() || fun->hasUncompiledScript() || !fun->nonLazyScript()->code();
 }
 
 void
-JSScript::updateBaselineOrIonRaw(JSRuntime* maybeRuntime)
+JSScript::updateJitCodeRaw(JSRuntime* rt)
 {
+    MOZ_ASSERT(rt);
     if (hasBaselineScript() && baseline->hasPendingIonBuilder()) {
-        MOZ_ASSERT(maybeRuntime);
         MOZ_ASSERT(!isIonCompilingOffThread());
-        baselineOrIonRaw = maybeRuntime->jitRuntime()->lazyLinkStub().value;
-        baselineOrIonSkipArgCheck = maybeRuntime->jitRuntime()->lazyLinkStub().value;
+        jitCodeRaw_ = rt->jitRuntime()->lazyLinkStub().value;
+        jitCodeSkipArgCheck_ = jitCodeRaw_;
     } else if (hasIonScript()) {
-        baselineOrIonRaw = ion->method()->raw();
-        baselineOrIonSkipArgCheck = ion->method()->raw() + ion->getSkipArgCheckEntryOffset();
+        jitCodeRaw_ = ion->method()->raw();
+        jitCodeSkipArgCheck_ = jitCodeRaw_ + ion->getSkipArgCheckEntryOffset();
     } else if (hasBaselineScript()) {
-        baselineOrIonRaw = baseline->method()->raw();
-        baselineOrIonSkipArgCheck = baseline->method()->raw();
+        jitCodeRaw_ = baseline->method()->raw();
+        jitCodeSkipArgCheck_ = jitCodeRaw_;
     } else {
-        baselineOrIonRaw = nullptr;
-        baselineOrIonSkipArgCheck = nullptr;
+        jitCodeRaw_ = rt->jitRuntime()->interpreterStub().value;
+        jitCodeSkipArgCheck_ = jitCodeRaw_;
     }
+    MOZ_ASSERT(jitCodeRaw_);
+    MOZ_ASSERT(jitCodeSkipArgCheck_);
 }
 
 bool
 JSScript::hasLoops()
 {
     if (!hasTrynotes())
         return false;
     JSTryNote* tn = trynotes()->vector;
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -910,21 +910,21 @@ class JSScript : public js::gc::TenuredC
 
     /* Information attached by Baseline. */
     js::jit::BaselineScript* baseline;
 
     /* Information used to re-lazify a lazily-parsed interpreted function. */
     js::LazyScript* lazyScript;
 
     /*
-     * Pointer to either baseline->method()->raw() or ion->method()->raw(), or
-     * nullptr if there's no Baseline or Ion script.
+     * Pointer to baseline->method()->raw(), ion->method()->raw(), the JIT's
+     * EnterInterpreter stub, or the lazy link stub. Must be non-null.
      */
-    uint8_t* baselineOrIonRaw;
-    uint8_t* baselineOrIonSkipArgCheck;
+    uint8_t* jitCodeRaw_;
+    uint8_t* jitCodeSkipArgCheck_;
 
     // 32-bit fields.
 
     uint32_t        dataSize_;  /* size of the used part of the data array */
 
     uint32_t        lineno_;    /* base line number of script */
     uint32_t        column_;    /* base column of script, optionally set */
 
@@ -1542,48 +1542,48 @@ class JSScript : public js::gc::TenuredC
         return ion;
     }
     js::jit::IonScript* maybeIonScript() const {
         return ion;
     }
     js::jit::IonScript* const* addressOfIonScript() const {
         return &ion;
     }
-    void setIonScript(JSRuntime* maybeRuntime, js::jit::IonScript* ionScript);
+    void setIonScript(JSRuntime* rt, js::jit::IonScript* ionScript);
 
     bool hasBaselineScript() const {
         bool res = baseline && baseline != BASELINE_DISABLED_SCRIPT;
         MOZ_ASSERT_IF(!res, !ion || ion == ION_DISABLED_SCRIPT);
         return res;
     }
     bool canBaselineCompile() const {
         return baseline != BASELINE_DISABLED_SCRIPT;
     }
     js::jit::BaselineScript* baselineScript() const {
         MOZ_ASSERT(hasBaselineScript());
         return baseline;
     }
-    inline void setBaselineScript(JSRuntime* maybeRuntime, js::jit::BaselineScript* baselineScript);
-
-    void updateBaselineOrIonRaw(JSRuntime* maybeRuntime);
+    inline void setBaselineScript(JSRuntime* rt, js::jit::BaselineScript* baselineScript);
+
+    void updateJitCodeRaw(JSRuntime* rt);
 
     static size_t offsetOfBaselineScript() {
         return offsetof(JSScript, baseline);
     }
     static size_t offsetOfIonScript() {
         return offsetof(JSScript, ion);
     }
-    static size_t offsetOfBaselineOrIonRaw() {
-        return offsetof(JSScript, baselineOrIonRaw);
+    static size_t offsetOfJitCodeRaw() {
+        return offsetof(JSScript, jitCodeRaw_);
     }
-    uint8_t* baselineOrIonRawPointer() const {
-        return baselineOrIonRaw;
+    static size_t offsetOfJitCodeSkipArgCheck() {
+        return offsetof(JSScript, jitCodeSkipArgCheck_);
     }
-    static size_t offsetOfBaselineOrIonSkipArgCheck() {
-        return offsetof(JSScript, baselineOrIonSkipArgCheck);
+    uint8_t* jitCodeRaw() const {
+        return jitCodeRaw_;
     }
 
     bool isRelazifiable() const {
         return (selfHosted() || lazyScript) && !hasInnerFunctions_ && !types_ &&
                !isGenerator() && !isAsync() &&
                !isDefaultClassConstructor() &&
                !hasBaselineScript() && !hasAnyIonScript() &&
                !doNotRelazify_;
--- a/js/src/jsscriptinlines.h
+++ b/js/src/jsscriptinlines.h
@@ -173,24 +173,24 @@ JSScript::initialEnvironmentShape() cons
 
 inline JSPrincipals*
 JSScript::principals()
 {
     return compartment()->principals();
 }
 
 inline void
-JSScript::setBaselineScript(JSRuntime* maybeRuntime, js::jit::BaselineScript* baselineScript)
+JSScript::setBaselineScript(JSRuntime* rt, js::jit::BaselineScript* baselineScript)
 {
     if (hasBaselineScript())
         js::jit::BaselineScript::writeBarrierPre(zone(), baseline);
     MOZ_ASSERT(!ion || ion == ION_DISABLED_SCRIPT);
     baseline = baselineScript;
     resetWarmUpResetCounter();
-    updateBaselineOrIonRaw(maybeRuntime);
+    updateJitCodeRaw(rt);
 }
 
 inline bool
 JSScript::ensureHasAnalyzedArgsUsage(JSContext* cx)
 {
     if (analyzedArgsUsage())
         return true;
     return js::jit::AnalyzeArgumentsUsage(cx, this);
--- a/js/src/make-source-package.sh
+++ b/js/src/make-source-package.sh
@@ -146,16 +146,20 @@ case $cmd in
     cp -pPR \
         ${TOPSRCDIR}/layout/tools/reftest/reftest \
         ${tgtpath}/layout/tools/reftest
     ${MKDIR} -p ${tgtpath}/toolkit/mozapps/installer
     cp -pPR \
         ${TOPSRCDIR}/toolkit/mozapps/installer/package-name.mk \
         ${TOPSRCDIR}/toolkit/mozapps/installer/upload-files.mk \
         ${tgtpath}/toolkit/mozapps/installer
+    ${MKDIR} -p ${tgtpath}/toolkit/crashreporter/tools
+    cp -pPR \
+        ${TOPSRCDIR}/toolkit/crashreporter/tools/symbolstore.py \
+        ${tgtpath}/toolkit/crashreporter/tools
     ${MKDIR} -p ${tgtpath}/mozglue
     cp -pPR \
         ${TOPSRCDIR}/mozglue/build \
         ${TOPSRCDIR}/mozglue/misc \
         ${TOPSRCDIR}/mozglue/moz.build \
         ${tgtpath}/mozglue
     ${MKDIR} -p ${tgtpath}/memory
     cp -pPR \
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -14,16 +14,19 @@
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/mozalloc.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/ScopeExit.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/TimeStamp.h"
 
 #include <chrono>
+#ifdef __linux__
+# include <dlfcn.h>
+#endif
 #ifdef XP_WIN
 # include <direct.h>
 # include <process.h>
 #endif
 #include <errno.h>
 #include <fcntl.h>
 #if defined(XP_WIN)
 # include <io.h>     /* for isatty() */
@@ -57,16 +60,20 @@
 #include "jsprf.h"
 #include "jsscript.h"
 #include "jstypes.h"
 #include "jsutil.h"
 #ifdef XP_WIN
 # include "jswin.h"
 #endif
 #include "jswrapper.h"
+#ifndef __linux__
+# include "prerror.h"
+# include "prlink.h"
+#endif
 #include "shellmoduleloader.out.h"
 
 #include "builtin/ModuleObject.h"
 #include "builtin/RegExp.h"
 #include "builtin/TestingFunctions.h"
 #include "frontend/Parser.h"
 #include "gc/GCInternals.h"
 #include "jit/arm/Simulator-arm.h"
@@ -123,16 +130,33 @@ using mozilla::MakeScopeExit;
 using mozilla::Maybe;
 using mozilla::Nothing;
 using mozilla::NumberEqualsInt32;
 using mozilla::PodCopy;
 using mozilla::PodEqual;
 using mozilla::TimeDuration;
 using mozilla::TimeStamp;
 
+// Avoid an unnecessary NSPR dependency on Linux just for the shell.
+#ifdef __linux__
+typedef void PRLibrary;
+
+static PRLibrary*
+PR_LoadLibrary(const char* path)
+{
+    return dlopen(path, RTLD_LAZY | RTLD_GLOBAL);
+}
+
+static void
+PR_UnloadLibrary(PRLibrary* dll)
+{
+    dlclose(dll);
+}
+#endif
+
 enum JSShellExitCode {
     EXITCODE_RUNTIME_ERROR      = 3,
     EXITCODE_FILE_NOT_FOUND     = 4,
     EXITCODE_OUT_OF_MEMORY      = 5,
     EXITCODE_TIMEOUT            = 6
 };
 
 /*
@@ -3160,16 +3184,24 @@ Crash(JSContext* cx, unsigned argc, Valu
     if (args.length() == 0)
         MOZ_CRASH("forced crash");
     RootedString message(cx, JS::ToString(cx, args[0]));
     if (!message)
         return false;
     char* utf8chars = JS_EncodeStringToUTF8(cx, message);
     if (!utf8chars)
         return false;
+    if (args.get(1).isObject()) {
+        RootedValue v(cx);
+        RootedObject opts(cx, &args[1].toObject());
+        if (!JS_GetProperty(cx, opts, "suppress_minidump", &v))
+            return false;
+        if (v.isBoolean() && v.toBoolean())
+            js::NoteIntentionalCrash();
+    }
 #ifndef DEBUG
     MOZ_ReportCrash(utf8chars, __FILE__, __LINE__);
 #endif
     MOZ_CRASH_UNSAFE_OOL(utf8chars);
 }
 
 static bool
 GetSLX(JSContext* cx, unsigned argc, Value* vp)
@@ -7038,18 +7070,22 @@ TestAssertRecoveredOnBailout,
 "getMarks()",
 "  Return an array of strings representing the current state of the mark\n"
 "  bits ('gray' or 'black', or 'dead' if the object has been collected)\n"
 "  for the objects registered via addMarkObservers. Note that some of the\n"
 "  objects tested may be from different compartments than the one in which\n"
 "  this function runs."),
 
     JS_FN_HELP("crash", Crash, 0, 0,
-"crash([message])",
-"  Crashes the process with a MOZ_CRASH."),
+"crash([message, [{disable_minidump:true}]])",
+"  Crashes the process with a MOZ_CRASH, optionally providing a message.\n"
+"  An options object may be passed as the second argument. If the key\n"
+"  'suppress_minidump' is set to true, then a minidump will not be\n"
+"  generated by the crash (which only has an effect if the breakpad\n"
+"  dumping library is loaded.)"),
 
     JS_FN_HELP("setARMHwCapFlags", SetARMHwCapFlags, 1, 0,
 "setARMHwCapFlags(\"flag1,flag2 flag3\")",
 "  On non-ARM, no-op. On ARM, set the hardware capabilities. The list of \n"
 "  flags is available by calling this function with \"help\" as the flag's name"),
 
     JS_FN_HELP("wasmLoop", WasmLoop, 2, 0,
 "wasmLoop(filename, imports)",
@@ -8590,16 +8626,44 @@ PreInit()
         // instead of hanging automation.
         UINT newMode = SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX;
         UINT prevMode = SetErrorMode(newMode);
         SetErrorMode(prevMode | newMode);
     }
 #endif
 }
 
+class AutoLibraryLoader {
+    Vector<PRLibrary*, 4, SystemAllocPolicy> libraries;
+
+  public:
+
+    ~AutoLibraryLoader() {
+        for (auto dll : libraries) {
+            PR_UnloadLibrary(dll);
+        }
+    }
+
+    PRLibrary* load(const char* path) {
+        PRLibrary* dll = PR_LoadLibrary(path);
+        if (!dll) {
+#ifdef __linux__
+            fprintf(stderr, "LoadLibrary '%s' failed: %s\n", path, dlerror());
+#else
+            fprintf(stderr, "LoadLibrary '%s' failed with code %d\n", path, PR_GetError());
+#endif
+            MOZ_CRASH("Failed to load library");
+        }
+
+        MOZ_ALWAYS_TRUE(libraries.append(dll));
+        return dll;
+    }
+};
+
+
 int
 main(int argc, char** argv, char** envp)
 {
     PreInit();
 
     sArgc = argc;
     sArgv = argv;
 
@@ -8792,16 +8856,18 @@ main(int argc, char** argv, char** envp)
         || !op.addIntOption('\0', "nursery-size", "SIZE-MB", "Set the maximum nursery size in MB", 16)
 #ifdef JS_GC_ZEAL
         || !op.addStringOption('z', "gc-zeal", "LEVEL(;LEVEL)*[,N]", gc::ZealModeHelpText)
 #else
         || !op.addStringOption('z', "gc-zeal", "LEVEL(;LEVEL)*[,N]", "option ignored in non-gc-zeal builds")
 #endif
         || !op.addStringOption('\0', "module-load-path", "DIR", "Set directory to load modules from")
         || !op.addBoolOption('\0', "no-async-stacks", "Disable async stacks")
+        || !op.addMultiStringOption('\0', "dll", "LIBRARY", "Dynamically load LIBRARY")
+        || !op.addBoolOption('\0', "suppress-minidump", "Suppress crash minidumps")
     )
     {
         return EXIT_FAILURE;
     }
 
     op.setArgTerminatesOptions("script", true);
     op.setArgCapturesRest("scriptArgs");
 
@@ -8841,16 +8907,27 @@ main(int argc, char** argv, char** envp)
         js::jit::CPUInfo::SetAVXEnabled();
         PropagateFlagToNestedShells("--enable-avx");
     }
 #endif
 
     if (op.getBoolOption("no-threads"))
         js::DisableExtraThreads();
 
+    AutoLibraryLoader loader;
+    MultiStringRange dllPaths = op.getMultiStringOption("dll");
+    while (!dllPaths.empty()) {
+        char* path = dllPaths.front();
+        loader.load(path);
+        dllPaths.popFront();
+    }
+
+    if (op.getBoolOption("suppress-minidump"))
+        js::NoteIntentionalCrash();
+
     if (!InitSharedArrayBufferMailbox())
         return 1;
 
     // The fake CPU count must be set before initializing the Runtime,
     // which spins up the thread pool.
     int32_t cpuCount = op.getIntOption("cpu-count"); // What we're really setting
     if (cpuCount < 0)
         cpuCount = op.getIntOption("thread-count");  // Legacy name
--- a/js/src/tests/jstests.py
+++ b/js/src/tests/jstests.py
@@ -372,17 +372,17 @@ def main():
         cmd = test_gen.next().get_command(prefix)
         if options.show_cmd:
             print(list2cmdline(cmd))
         with changedir(test_dir), change_env(test_environment):
             call(cmd)
         return 0
 
     with changedir(test_dir), change_env(test_environment):
-        results = ResultsSink(options, test_count)
+        results = ResultsSink('jstests', options, test_count)
         try:
             for out in run_all_tests(test_gen, prefix, results.pb, options):
                 results.push(out)
             results.finish(True)
         except KeyboardInterrupt:
             results.finish(False)
 
         return 0 if results.all_passed() else 1
--- a/js/src/tests/lib/jittests.py
+++ b/js/src/tests/lib/jittests.py
@@ -14,16 +14,17 @@ import StringIO
 
 if sys.platform.startswith('linux') or sys.platform.startswith('darwin'):
     from tasks_unix import run_all_tests
 else:
     from tasks_win import run_all_tests
 
 from progressbar import ProgressBar, NullProgressBar
 from results import TestOutput
+from structuredlog import TestLogger
 
 TESTS_LIB_DIR = os.path.dirname(os.path.abspath(__file__))
 JS_DIR = os.path.dirname(os.path.dirname(TESTS_LIB_DIR))
 TOP_SRC_DIR = os.path.dirname(os.path.dirname(JS_DIR))
 TEST_DIR = os.path.join(JS_DIR, 'jit-test', 'tests')
 LIB_DIR = os.path.join(JS_DIR, 'jit-test', 'lib') + os.path.sep
 MODULE_DIR = os.path.join(JS_DIR, 'jit-test', 'modules') + os.path.sep
 JS_CACHE_DIR = os.path.join(JS_DIR, 'jit-test', '.js-cache')
@@ -322,16 +323,20 @@ class JitTest:
             cmd += ['--module', path]
         elif self.test_reflect_stringify is None:
             cmd += ['-f', path]
         else:
             cmd += ['--', self.test_reflect_stringify, "--check", path]
 
         if self.valgrind:
             cmd = self.VALGRIND_CMD + cmd
+
+        if self.allow_unhandlable_oom or self.expect_crash:
+            cmd += ['--suppress-minidump']
+
         return cmd
 
     # The test runner expects this to be set to give to get_command.
     js_cmd_prefix = None
     def get_command(self, prefix):
         """Shim for the test runner."""
         return self.command(prefix, LIB_DIR, MODULE_DIR)
 
@@ -458,17 +463,17 @@ def check_output(out, err, rc, timed_out
         # forces the exit status.
         if test.expect_status != 0 and options.unusable_error_status:
             return True
 
         return False
 
     return True
 
-def print_automation_format(ok, res):
+def print_automation_format(ok, res, slog):
     # Output test failures in a parsable format suitable for automation, eg:
     # TEST-RESULT | filename.js | Failure description (code N, args "--foobar")
     #
     # Example:
     # TEST-PASS | foo/bar/baz.js | (code 0, args "--ion-eager")
     # TEST-UNEXPECTED-FAIL | foo/bar/baz.js | TypeError: or something (code -9, args "--no-ion")
     # INFO exit-status     : 3
     # INFO timed-out       : False
@@ -478,16 +483,24 @@ def print_automation_format(ok, res):
     # INFO stderr         2> TypeError: or something
     # TEST-UNEXPECTED-FAIL | jit_test.py: Test execution interrupted by user
     result = "TEST-PASS" if ok else "TEST-UNEXPECTED-FAIL"
     message = "Success" if ok else res.describe_failure()
     jitflags = " ".join(res.test.jitflags)
     print("{} | {} | {} (code {}, args \"{}\") [{:.1f} s]".format(
         result, res.test.relpath_top, message, res.rc, jitflags, res.dt))
 
+    details = {
+        'message': message,
+        'extra': {
+            'jitflags': jitflags,
+        }
+    }
+    slog.test(res.test.relpath_tests, 'PASS' if ok else 'FAIL', res.dt, **details)
+
     # For failed tests, print as much information as we have, to aid debugging.
     if ok:
         return
     print("INFO exit-status     : {}".format(res.rc))
     print("INFO timed-out       : {}".format(res.timed_out))
     for line in res.out.splitlines():
         print("INFO stdout          > " + line.strip())
     for line in res.err.splitlines():
@@ -552,17 +565,17 @@ def create_progressbar(num_tests, option
             {'value': 'PASS',    'color': 'green'},
             {'value': 'FAIL',    'color': 'red'},
             {'value': 'TIMEOUT', 'color': 'blue'},
             {'value': 'SKIP',    'color': 'brightgray'},
         ]
         return ProgressBar(num_tests, fmt)
     return NullProgressBar()
 
-def process_test_results(results, num_tests, pb, options):
+def process_test_results(results, num_tests, pb, options, slog):
     failures = []
     timeouts = 0
     complete = False
     output_dict = {}
     doing = 'before starting'
 
     if num_tests == 0:
         pb.finish(True)
@@ -601,50 +614,66 @@ def process_test_results(results, num_te
                 failures.append(res)
                 if res.timed_out:
                     pb.message("TIMEOUT - {}".format(res.test.relpath_tests))
                     timeouts += 1
                 else:
                     pb.message("FAIL - {}".format(res.test.relpath_tests))
 
             if options.format == 'automation':
-                print_automation_format(ok, res)
+                print_automation_format(ok, res, slog)
 
             n = i + 1
             pb.update(n, {
                 'PASS': n - len(failures),
                 'FAIL': len(failures),
                 'TIMEOUT': timeouts,
                 'SKIP': 0
             })
         complete = True
     except KeyboardInterrupt:
         print("TEST-UNEXPECTED-FAIL | jit_test.py" +
               " : Test execution interrupted by user")
 
     pb.finish(True)
     return print_test_summary(num_tests, failures, complete, doing, options)
 
-def run_tests(tests, num_tests, prefix, options):
+def run_tests(tests, num_tests, prefix, options, remote=False):
+    slog = None
+    if options.format == 'automation':
+        slog = TestLogger("jittests")
+        slog.suite_start()
+
+    if remote:
+        ok = run_tests_remote(tests, num_tests, prefix, options, slog)
+    else:
+        ok = run_tests_local(tests, num_tests, prefix, options, slog)
+
+    if slog:
+        slog.suite_end()
+
+    return ok
+
+def run_tests_local(tests, num_tests, prefix, options, slog):
     # The jstests tasks runner requires the following options. The names are
     # taken from the jstests options processing code, which are frequently
     # subtly different from the options jit-tests expects. As such, we wrap
     # them here, as needed.
     AdaptorOptions = namedtuple("AdaptorOptions", [
         "worker_count", "passthrough", "timeout", "output_fp",
         "hide_progress", "run_skipped", "show_cmd"])
     shim_options = AdaptorOptions(options.max_jobs, False, options.timeout,
                                   sys.stdout, False, True, options.show_cmd)
 
     # The test runner wants the prefix as a static on the Test class.
     JitTest.js_cmd_prefix = prefix
 
     pb = create_progressbar(num_tests, options)
     gen = run_all_tests(tests, prefix, pb, shim_options)
-    ok = process_test_results(gen, num_tests, pb, options)
+    ok = process_test_results(gen, num_tests, pb, options, slog)
     return ok
 
 def get_remote_results(tests, device, prefix, options):
     from mozdevice import devicemanager
 
     try:
         for i in xrange(0, options.repeat):
             for test in tests:
@@ -667,17 +696,17 @@ def push_libs(options, device):
             device.pushFile(os.path.join(options.local_lib, file), remote_file)
 
 def push_progs(options, device, progs):
     for local_file in progs:
         remote_file = posixpath.join(options.remote_test_root,
                                      os.path.basename(local_file))
         device.pushFile(local_file, remote_file)
 
-def run_tests_remote(tests, num_tests, prefix, options):
+def run_tests_remote(tests, num_tests, prefix, options, slog):
     # Setup device with everything needed to run our tests.
     from mozdevice import devicemanagerADB
 
     if options.device_ip:
         dm = devicemanagerADB.DeviceManagerADB(
             options.device_ip, options.device_port,
             deviceSerial=options.device_serial,
             packageName=None,
@@ -708,17 +737,17 @@ def run_tests_remote(tests, num_tests, p
 
     dm.pushDir(os.path.dirname(TEST_DIR), options.remote_test_root,
                timeout=600)
     prefix[0] = os.path.join(options.remote_test_root, 'js')
 
     # Run all tests.
     pb = create_progressbar(num_tests, options)
     gen = get_remote_results(tests, dm, prefix, options)
-    ok = process_test_results(gen, num_tests, pb, options)
+    ok = process_test_results(gen, num_tests, pb, options, slog)
     return ok
 
 def platform_might_be_android():
     try:
         # The python package for SL4A provides an |android| module.
         # If that module is present, we're likely in SL4A-python on
         # device.  False positives and negatives are possible,
         # however.
--- a/js/src/tests/lib/results.py
+++ b/js/src/tests/lib/results.py
@@ -1,13 +1,15 @@
 from __future__ import print_function
 
+import pipes
 import re
+
 from progressbar import NullProgressBar, ProgressBar
-import pipes
+from structuredlog import TestLogger
 
 # subprocess.list2cmdline does not properly escape for sh-like shells
 def escape_cmdline(args):
     return ' '.join([pipes.quote(a) for a in args])
 
 class TestOutput:
     """Output from a test run."""
     def __init__(self, test, cmd, out, err, rc, dt, timed_out):
@@ -101,19 +103,22 @@ class TestResult:
         return cls(test, result, results)
 
 class TestDuration:
     def __init__(self, test, duration):
         self.test = test
         self.duration = duration
 
 class ResultsSink:
-    def __init__(self, options, testcount):
+    def __init__(self, testsuite, options, testcount):
         self.options = options
         self.fp = options.output_fp
+        if self.options.format == 'automation':
+            self.slog = TestLogger(testsuite)
+            self.slog.suite_start()
 
         self.groups = {}
         self.output_dict = {}
         self.counts = {'PASS': 0, 'FAIL': 0, 'TIMEOUT': 0, 'SKIP': 0}
         self.slow_tests = []
         self.n = 0
 
         if options.hide_progress:
@@ -202,31 +207,32 @@ class ResultsSink:
                         tup = (sub_ok, result.test.expect, result.test.random)
                         label = self.LABELS[tup][0]
                         if label == 'TEST-UNEXPECTED-PASS':
                             label = 'TEST-PASS (EXPECTED RANDOM)'
                         self.print_automation_result(
                             label, result.test, time=output.dt,
                             message=msg)
                 tup = (result.result, result.test.expect, result.test.random)
-                self.print_automation_result(
-                    self.LABELS[tup][0], result.test, time=output.dt)
+                self.print_automation_result(self.LABELS[tup][0], result.test, time=output.dt)
                 return
 
             if dev_label:
                 def singular(label):
                     return "FIXED" if label == "FIXES" else label[:-1]
                 self.pb.message("{} - {}".format(singular(dev_label),
                                                  output.test.path))
 
         self.pb.update(self.n, self.counts)
 
     def finish(self, completed):
         self.pb.finish(completed)
-        if not self.options.format == 'automation':
+        if self.options.format == 'automation':
+            self.slog.suite_end()
+        else:
             self.list(completed)
 
     # Conceptually, this maps (test result x test expection) to text labels.
     #      key   is (result, expect, random)
     #      value is (automation label, dev test category)
     LABELS = {
         (TestResult.CRASH, False, False): ('TEST-UNEXPECTED-FAIL',               'REGRESSIONS'),
         (TestResult.CRASH, False, True):  ('TEST-UNEXPECTED-FAIL',               'REGRESSIONS'),
@@ -297,8 +303,18 @@ class ResultsSink:
         if message:
             result += " | " + message
         if skip:
             result += ' | (SKIP)'
         if time > self.options.timeout:
             result += ' | (TIMEOUT)'
         result += ' [{:.1f} s]'.format(time)
         print(result)
+
+        details = { 'extra': {} }
+        if self.options.shell_args:
+            details['extra']['shell_args'] = self.options.shell_args
+        details['extra']['jitflags'] = test.jitflags
+        if message:
+            details['message'] = message
+        status = 'FAIL' if 'TEST-UNEXPECTED' in label else 'PASS'
+
+        self.slog.test(test.path, status, time or 0, **details)
new file mode 100644
--- /dev/null
+++ b/js/src/tests/lib/structuredlog.py
@@ -0,0 +1,51 @@
+# produce mozlog-compatible log messages, following the spec at https://mozbase.readthedocs.io/en/latest/mozlog.html
+
+import json
+import os
+
+from time import time
+
+class TestLogger(object):
+    def __init__(self, source, threadname='main'):
+        self.template = {
+            'source': source,
+            'thread': threadname,
+            'pid': os.getpid(),
+        }
+
+    def _record(self, **kwargs):
+        record = self.template.copy()
+        record.update(**kwargs)
+        if 'time' not in record:
+            record['time'] = time()
+        return record
+
+    def _log_obj(self, obj):
+        print(json.dumps(obj))
+
+    def _log(self, **kwargs):
+        self._log_obj(self._record(**kwargs))
+
+    def suite_start(self):
+        self._log(action='suite_start', tests=[])
+
+    def suite_end(self):
+        self._log(action='suite_end')
+
+    def test_start(self, testname):
+        self._log(action='test_start', test=testname)
+
+    def test_end(self, testname, status):
+        self._log(action='test_end', test=testname, status=status)
+
+    def test(self, testname, status, duration, **details):
+        record = self._record(action='test_start', test=testname)
+        end_time = record['time']
+        record['time'] -= duration
+        self._log_obj(record)
+
+        record['action'] = 'test_end'
+        record['time'] = end_time
+        record['status'] = status
+        record.update(**details)
+        self._log_obj(record)
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -307,17 +307,17 @@ CancelOffThreadIonCompileLocked(const Co
     } while (cancelled);
 
     /* Cancel code generation for any completed entries. */
     GlobalHelperThreadState::IonBuilderVector& finished = HelperThreadState().ionFinishedList(lock);
     for (size_t i = 0; i < finished.length(); i++) {
         jit::IonBuilder* builder = finished[i];
         if (IonBuilderMatches(selector, builder)) {
             builder->script()->zoneFromAnyThread()->group()->numFinishedBuilders--;
-            jit::FinishOffThreadBuilder(nullptr, builder, lock);
+            jit::FinishOffThreadBuilder(builder->script()->runtimeFromAnyThread(), builder, lock);
             HelperThreadState().remove(finished, &i);
         }
     }
 
     /* Cancel lazy linking for pending builders (attached to the ionScript). */
     if (discardLazyLinkList) {
         MOZ_ASSERT(!selector.is<AllCompilations>());
         JSRuntime* runtime = GetSelectorRuntime(selector);
--- a/js/src/wasm/WasmStubs.cpp
+++ b/js/src/wasm/WasmStubs.cpp
@@ -755,17 +755,17 @@ GenerateImportJitExit(MacroAssembler& ma
 
     // 6. Check if we need to rectify arguments
     masm.load16ZeroExtend(Address(callee, JSFunction::offsetOfNargs()), scratch);
 
     Label rectify;
     masm.branch32(Assembler::Above, scratch, Imm32(fi.sig().args().length()), &rectify);
 
     // 7. If we haven't rectified arguments, load callee executable entry point
-    masm.loadJitCodeNoArgCheck(callee, callee, nullptr);
+    masm.loadJitCodeNoArgCheck(callee, callee);
 
     Label rejoinBeforeCall;
     masm.bind(&rejoinBeforeCall);
 
     AssertStackAlignment(masm, JitStackAlignment, sizeOfRetAddr);
     masm.callJitNoProfiler(callee);
 
     // Note that there might be a GC thing in the JSReturnOperand now.
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -2656,19 +2656,16 @@ AccumulateTelemetryCallback(int id, uint
         Telemetry::Accumulate(Telemetry::GC_NURSERY_BYTES, sample);
         break;
       case JS_TELEMETRY_GC_PRETENURE_COUNT:
         Telemetry::Accumulate(Telemetry::GC_PRETENURE_COUNT, sample);
         break;
       case JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT:
         Telemetry::Accumulate(Telemetry::JS_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT, sample);
         break;
-      case JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_ADDONS:
-        Telemetry::Accumulate(Telemetry::JS_DEPRECATED_LANGUAGE_EXTENSIONS_IN_ADDONS, sample);
-        break;
       case JS_TELEMETRY_ADDON_EXCEPTIONS:
         Telemetry::Accumulate(Telemetry::JS_TELEMETRY_ADDON_EXCEPTIONS, nsDependentCString(key), sample);
         break;
       case JS_TELEMETRY_PRIVILEGED_PARSER_COMPILE_LAZY_AFTER_MS:
         Telemetry::Accumulate(Telemetry::JS_PRIVILEGED_PARSER_COMPILE_LAZY_AFTER_MS, sample);
         break;
       case JS_TELEMETRY_WEB_PARSER_COMPILE_LAZY_AFTER_MS:
         Telemetry::Accumulate(Telemetry::JS_WEB_PARSER_COMPILE_LAZY_AFTER_MS, sample);
--- a/modules/libpref/Preferences.cpp
+++ b/modules/libpref/Preferences.cpp
@@ -119,33 +119,55 @@ using namespace mozilla;
 
 typedef nsTArray<nsCString> PrefSaveData;
 
 // 1 MB should be enough for everyone.
 static const uint32_t MAX_PREF_LENGTH = 1 * 1024 * 1024;
 // Actually, 4kb should be enough for everyone.
 static const uint32_t MAX_ADVISABLE_PREF_LENGTH = 4 * 1024;
 
-union PrefValue {
-  const char* mStringVal;
-  int32_t mIntVal;
-  bool mBoolVal;
-};
-
-// Preference flags, including the native type of the preference. Changing any
-// of these values will require modifying the code inside of PrefTypeFlags
-// class.
 enum class PrefType
 {
   Invalid = 0,
   String = 1,
   Int = 2,
   Bool = 3,
 };
 
+union PrefValue {
+  const char* mStringVal;
+  int32_t mIntVal;
+  bool mBoolVal;
+
+  bool Equals(PrefType aType, PrefValue aValue)
+  {
+    switch (aType) {
+      case PrefType::String: {
+        if (mStringVal && aValue.mStringVal) {
+          return strcmp(mStringVal, aValue.mStringVal) == 0;
+        }
+        if (!mStringVal && !aValue.mStringVal) {
+          return true;
+        }
+        return false;
+      }
+
+      case PrefType::Int:
+        return mIntVal == aValue.mIntVal;
+
+      case PrefType::Bool:
+        return mBoolVal == aValue.mBoolVal;
+
+      case PrefType::Invalid:
+      default:
+        MOZ_CRASH("Unhandled enum value");
+    }
+  }
+};
+
 #ifdef DEBUG
 const char*
 PrefTypeToString(PrefType aType)
 {
   switch (aType) {
     case PrefType::Invalid:
       return "INVALID";
     case PrefType::String:
@@ -155,16 +177,74 @@ PrefTypeToString(PrefType aType)
     case PrefType::Bool:
       return "bool";
     default:
       MOZ_CRASH("Unhandled enum value");
   }
 }
 #endif
 
+// Assign to aResult a quoted, escaped copy of aOriginal.
+static void
+StrEscape(const char* aOriginal, nsCString& aResult)
+{
+  if (aOriginal == nullptr) {
+    aResult.AssignLiteral("\"\"");
+    return;
+  }
+
+  // JavaScript does not allow quotes, slashes, or line terminators inside
+  // strings so we must escape them. ECMAScript defines four line terminators,
+  // but we're only worrying about \r and \n here.  We currently feed our pref
+  // script to the JS interpreter as Latin-1 so  we won't encounter \u2028
+  // (line separator) or \u2029 (paragraph separator).
+  //
+  // WARNING: There are hints that we may be moving to storing prefs as utf8.
+  // If we ever feed them to the JS compiler as UTF8 then we'll have to worry
+  // about the multibyte sequences that would be interpreted as \u2028 and
+  // \u2029.
+  const char* p;
+
+  aResult.Assign('"');
+
+  // Paranoid worst case all slashes will free quickly.
+  for (p = aOriginal; *p; ++p) {
+    switch (*p) {
+      case '\n':
+        aResult.AppendLiteral("\\n");
+        break;
+
+      case '\r':
+        aResult.AppendLiteral("\\r");
+        break;
+
+      case '\\':
+        aResult.AppendLiteral("\\\\");
+        break;
+
+      case '\"':
+        aResult.AppendLiteral("\\\"");
+        break;
+
+      default:
+        aResult.Append(*p);
+        break;
+    }
+  }
+
+  aResult.Append('"');
+}
+
+enum
+{
+  kPrefSetDefault = 1,
+  kPrefForceSet = 2,
+  kPrefSticky = 4,
+};
+
 static ArenaAllocator<8192, 1> gPrefNameArena;
 
 class PrefHashEntry : public PLDHashEntryHdr
 {
 public:
   PrefHashEntry(const char* aName, PrefType aType)
   {
     mName = ArenaStrdup(aName, gPrefNameArena);
@@ -227,16 +307,79 @@ public:
     }
 
     // Don't need to free this because it's allocated in memory owned by
     // gPrefNameArena.
     pref->mName = nullptr;
     memset(aEntry, 0, aTable->EntrySize());
   }
 
+  nsresult GetBoolValue(PrefValueKind aKind, bool* aResult)
+  {
+    if (!IsTypeBool()) {
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    if (aKind == PrefValueKind::Default || IsLocked() || !HasUserValue()) {
+      // Do we have a default?
+      if (!HasDefaultValue()) {
+        return NS_ERROR_UNEXPECTED;
+      }
+      *aResult = mDefaultValue.mBoolVal;
+    } else {
+      *aResult = mUserValue.mBoolVal;
+    }
+
+    return NS_OK;
+  }
+
+  nsresult GetIntValue(PrefValueKind aKind, int32_t* aResult)
+  {
+    if (!IsTypeInt()) {
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    if (aKind == PrefValueKind::Default || IsLocked() || !HasUserValue()) {
+      // Do we have a default?
+      if (!HasDefaultValue()) {
+        return NS_ERROR_UNEXPECTED;
+      }
+      *aResult = mDefaultValue.mIntVal;
+    } else {
+      *aResult = mUserValue.mIntVal;
+    }
+
+    return NS_OK;
+  }
+
+  nsresult GetCStringValue(PrefValueKind aKind, nsACString& aResult)
+  {
+    if (!IsTypeString()) {
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    const char* stringVal = nullptr;
+    if (aKind == PrefValueKind::Default || IsLocked() || !HasUserValue()) {
+      // Do we have a default?
+      if (!HasDefaultValue()) {
+        return NS_ERROR_UNEXPECTED;
+      }
+      stringVal = mDefaultValue.mStringVal;
+    } else {
+      stringVal = mUserValue.mStringVal;
+    }
+
+    if (!stringVal) {
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    aResult = stringVal;
+    return NS_OK;
+  }
+
 private:
   static void AssignPrefValueToDomPrefValue(PrefType aType,
                                             PrefValue* aValue,
                                             dom::PrefValue* aDomValue)
   {
     switch (aType) {
       case PrefType::String:
         *aDomValue = nsDependentCString(aValue->mStringVal);
@@ -302,16 +445,17 @@ public:
       if (strlen(stringVal) > MAX_ADVISABLE_PREF_LENGTH) {
         return false;
       }
     }
 
     return true;
   }
 
+private:
   // Overwrite the type and value of an existing preference. Caller must ensure
   // that they are not changing the type of a preference that has a default
   // value.
   void ReplaceValue(PrefValueKind aKind, PrefType aNewType, PrefValue aNewValue)
   {
     PrefValue* value =
       aKind == PrefValueKind::Default ? &mDefaultValue : &mUserValue;
 
@@ -324,16 +468,86 @@ public:
     if (aNewType == PrefType::String) {
       MOZ_ASSERT(aNewValue.mStringVal);
       value->mStringVal = moz_xstrdup(aNewValue.mStringVal);
     } else {
       *value = aNewValue;
     }
   }
 
+public:
+  void SetValue(PrefType aType,
+                PrefValue aValue,
+                uint32_t aFlags,
+                bool* aValueChanged,
+                bool* aDirty)
+  {
+    if (aFlags & kPrefSetDefault) {
+      if (!IsLocked()) {
+        // ?? change of semantics?
+        if (!HasDefaultValue() || !mDefaultValue.Equals(aType, aValue)) {
+          ReplaceValue(PrefValueKind::Default, aType, aValue);
+          SetHasDefaultValue(true);
+          if (aFlags & kPrefSticky) {
+            SetIsSticky(true);
+          }
+          if (!HasUserValue()) {
+            *aValueChanged = true;
+          }
+        }
+        // What if we change the default to be the same as the user value?
+        // Should we clear the user value?
+      }
+    } else {
+      // If new value is same as the default value and it's not a "sticky"
+      // pref, then un-set the user value. Otherwise, set the user value only
+      // if it has changed.
+      if (HasDefaultValue() && !IsSticky() &&
+          mDefaultValue.Equals(aType, aValue) && !(aFlags & kPrefForceSet)) {
+        if (HasUserValue()) {
+          // XXX should we free a user-set string value if there is one?
+          SetHasUserValue(false);
+          if (!IsLocked()) {
+            *aDirty = true;
+            *aValueChanged = true;
+          }
+        }
+      } else if (!HasUserValue() || !IsType(aType) ||
+                 !mUserValue.Equals(aType, aValue)) {
+        ReplaceValue(PrefValueKind::User, aType, aValue);
+        SetHasUserValue(true);
+        if (!IsLocked()) {
+          *aDirty = true;
+          *aValueChanged = true;
+        }
+      }
+    }
+  }
+
+  // Returns false if this pref doesn't have a user value worth saving.
+  bool UserValueToStringForSaving(nsCString& aStr)
+  {
+    if (HasUserValue() && (!HasDefaultValue() || IsSticky() ||
+                           !mDefaultValue.Equals(Type(), mUserValue))) {
+      if (IsTypeString()) {
+        StrEscape(mUserValue.mStringVal, aStr);
+
+      } else if (IsTypeInt()) {
+        aStr.AppendInt(mUserValue.mIntVal);
+
+      } else if (IsTypeBool()) {
+        aStr = mUserValue.mBoolVal ? "true" : "false";
+      }
+      return true;
+    }
+
+    // Do not save default prefs that haven't changed.
+    return false;
+  }
+
   size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf)
   {
     // Note: mName is allocated in gPrefNameArena, measured elsewhere.
     size_t n = 0;
     if (IsTypeString()) {
       if (HasDefaultValue()) {
         n += aMallocSizeOf(mDefaultValue.mStringVal);
       }
@@ -351,17 +565,16 @@ private:
   uint32_t mType : 2;
   uint32_t mIsSticky : 1;
   uint32_t mIsLocked : 1;
   uint32_t mHasUserValue : 1;
   uint32_t mHasDefaultValue : 1;
 
   const char* mName;
 
-public:
   PrefValue mDefaultValue;
   PrefValue mUserValue;
 };
 
 struct CallbackNode
 {
   const char* mDomain;
 
@@ -395,118 +608,40 @@ static PLDHashTableOps pref_HashTableOps
   nullptr,
 };
 
 //---------------------------------------------------------------------------
 
 static PrefHashEntry*
 pref_HashTableLookup(const char* aPrefName);
 
-static bool
-pref_ValueChanged(PrefValue aOldValue, PrefValue aNewValue, PrefType aType);
-
 static nsresult
 pref_DoCallback(const char* aChangedPref);
 
-enum
-{
-  kPrefSetDefault = 1,
-  kPrefForceSet = 2,
-  kPrefSticky = 4,
-};
-
 #define PREF_HASHTABLE_INITIAL_LENGTH 1024
 
-// Assign to aResult a quoted, escaped copy of aOriginal.
-static void
-StrEscape(const char* aOriginal, nsCString& aResult)
-{
-  if (aOriginal == nullptr) {
-    aResult.AssignLiteral("\"\"");
-    return;
-  }
-
-  // JavaScript does not allow quotes, slashes, or line terminators inside
-  // strings so we must escape them. ECMAScript defines four line terminators,
-  // but we're only worrying about \r and \n here.  We currently feed our pref
-  // script to the JS interpreter as Latin-1 so  we won't encounter \u2028
-  // (line separator) or \u2029 (paragraph separator).
-  //
-  // WARNING: There are hints that we may be moving to storing prefs as utf8.
-  // If we ever feed them to the JS compiler as UTF8 then we'll have to worry
-  // about the multibyte sequences that would be interpreted as \u2028 and
-  // \u2029.
-  const char* p;
-
-  aResult.Assign('"');
-
-  // Paranoid worst case all slashes will free quickly.
-  for (p = aOriginal; *p; ++p) {
-    switch (*p) {
-      case '\n':
-        aResult.AppendLiteral("\\n");
-        break;
-
-      case '\r':
-        aResult.AppendLiteral("\\r");
-        break;
-
-      case '\\':
-        aResult.AppendLiteral("\\\\");
-        break;
-
-      case '\"':
-        aResult.AppendLiteral("\\\"");
-        break;
-
-      default:
-        aResult.Append(*p);
-        break;
-    }
-  }
-
-  aResult.Append('"');
-}
-
 static PrefSaveData
 pref_savePrefs()
 {
   PrefSaveData savedPrefs(gHashTable->EntryCount());
 
   for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) {
     auto pref = static_cast<PrefHashEntry*>(iter.Get());
 
-    // where we're getting our pref from
-    PrefValue* sourceValue;
-
-    if (pref->HasUserValue() &&
-        (pref_ValueChanged(
-           pref->mDefaultValue, pref->mUserValue, pref->Type()) ||
-         !pref->HasDefaultValue() || pref->IsSticky())) {
-      sourceValue = &pref->mUserValue;
-    } else {
-      // do not save default prefs that haven't changed
+    nsAutoCString prefValueStr;
+    if (!pref->UserValueToStringForSaving(prefValueStr)) {
       continue;
     }
 
-    nsAutoCString prefName;
-    StrEscape(pref->Name(), prefName);
-
-    nsAutoCString prefValue;
-    if (pref->IsTypeString()) {
-      StrEscape(sourceValue->mStringVal, prefValue);
-
-    } else if (pref->IsTypeInt()) {
-      prefValue.AppendInt(sourceValue->mIntVal);
-
-    } else if (pref->IsTypeBool()) {
-      prefValue = sourceValue->mBoolVal ? "true" : "false";
-    }
-
-    nsPrintfCString str("user_pref(%s, %s);", prefName.get(), prefValue.get());
+    nsAutoCString prefNameStr;
+    StrEscape(pref->Name(), prefNameStr);
+
+    nsPrintfCString str(
+      "user_pref(%s, %s);", prefNameStr.get(), prefValueStr.get());
+
     savedPrefs.AppendElement(str);
   }
 
   return savedPrefs;
 }
 
 static bool
 PREF_HasUserPref(const char* aPrefName)
@@ -598,47 +733,19 @@ PREF_LockPref(const char* aPrefName, boo
   } else if (pref->IsLocked()) {
     pref->SetIsLocked(false);
     pref_DoCallback(aPrefName);
   }
 
   return NS_OK;
 }
 
-//
-// Hash table functions
-//
-
-static bool
-pref_ValueChanged(PrefValue aOldValue, PrefValue aNewValue, PrefType aType)
-{
-  bool changed = true;
-  switch (aType) {
-    case PrefType::String:
-      if (aOldValue.mStringVal && aNewValue.mStringVal) {
-        changed = (strcmp(aOldValue.mStringVal, aNewValue.mStringVal) != 0);
-      }
-      break;
-
-    case PrefType::Int:
-      changed = aOldValue.mIntVal != aNewValue.mIntVal;
-      break;
-
-    case PrefType::Bool:
-      changed = aOldValue.mBoolVal != aNewValue.mBoolVal;
-      break;
-
-    case PrefType::Invalid:
-    default:
-      changed = false;
-      break;
-  }
-
-  return changed;
-}
+  //
+  // Hash table functions
+  //
 
 #ifdef DEBUG
 
 static pref_initPhase gPhase = START;
 
 struct StringComparator
 {
   const char* mPrefName;
@@ -735,60 +842,22 @@ pref_SetPref(const char* aPrefName,
         aPrefName,
         PrefTypeToString(pref->Type()),
         PrefTypeToString(aType))
         .get());
 
     return NS_ERROR_UNEXPECTED;
   }
 
-  bool valueChanged = false;
-  if (aFlags & kPrefSetDefault) {
-    if (!pref->IsLocked()) {
-      // ?? change of semantics?
-      if (pref_ValueChanged(pref->mDefaultValue, aValue, aType) ||
-          !pref->HasDefaultValue()) {
-        pref->ReplaceValue(PrefValueKind::Default, aType, aValue);
-        pref->SetHasDefaultValue(true);
-        if (aFlags & kPrefSticky) {
-          pref->SetIsSticky(true);
-        }
-        if (!pref->HasUserValue()) {
-          valueChanged = true;
-        }
-      }
-      // What if we change the default to be the same as the user value?
-      // Should we clear the user value?
-    }
-  } else {
-    // If new value is same as the default value and it's not a "sticky" pref,
-    // then un-set the user value. Otherwise, set the user value only if it has
-    // changed.
-    if (pref->HasDefaultValue() && !pref->IsSticky() &&
-        !pref_ValueChanged(pref->mDefaultValue, aValue, aType) &&
-        !(aFlags & kPrefForceSet)) {
-      if (pref->HasUserValue()) {
-        // XXX should we free a user-set string value if there is one?
-        pref->SetHasUserValue(false);
-        if (!pref->IsLocked()) {
-          Preferences::HandleDirty();
-          valueChanged = true;
-        }
-      }
-    } else if (!pref->HasUserValue() || !pref->IsType(aType) ||
-               pref_ValueChanged(pref->mUserValue, aValue, aType)) {
-      pref->ReplaceValue(PrefValueKind::User, aType, aValue);
-      pref->SetHasUserValue(true);
-      if (!pref->IsLocked()) {
-        Preferences::HandleDirty();
-        valueChanged = true;
-      }
-    }
+  bool valueChanged = false, handleDirty = false;
+  pref->SetValue(aType, aValue, aFlags, &valueChanged, &handleDirty);
+
+  if (handleDirty) {
+    Preferences::HandleDirty();
   }
-
   if (valueChanged) {
     return pref_DoCallback(aPrefName);
   }
 
   return NS_OK;
 }
 
 // Bool function that returns whether or not the preference is locked and
@@ -4437,61 +4506,29 @@ Preferences::InitInitialObjects()
 
 /* static */ nsresult
 Preferences::GetBool(const char* aPrefName, bool* aResult, PrefValueKind aKind)
 {
   NS_PRECONDITION(aResult, "aResult must not be NULL");
   NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
 
   PrefHashEntry* pref = pref_HashTableLookup(aPrefName);
-  if (!pref || !pref->IsTypeBool()) {
-    return NS_ERROR_UNEXPECTED;
-  }
-
-  if (aKind == PrefValueKind::Default || pref->IsLocked() ||
-      !pref->HasUserValue()) {
-
-    // Do we have a default?
-    if (!pref->HasDefaultValue()) {
-      return NS_ERROR_UNEXPECTED;
-    }
-    *aResult = pref->mDefaultValue.mBoolVal;
-  } else {
-    *aResult = pref->mUserValue.mBoolVal;
-  }
-
-  return NS_OK;
+  return pref ? pref->GetBoolValue(aKind, aResult) : NS_ERROR_UNEXPECTED;
 }
 
 /* static */ nsresult
 Preferences::GetInt(const char* aPrefName,
                     int32_t* aResult,
                     PrefValueKind aKind)
 {
   NS_PRECONDITION(aResult, "aResult must not be NULL");
   NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
 
   PrefHashEntry* pref = pref_HashTableLookup(aPrefName);
-  if (!pref || !pref->IsTypeInt()) {
-    return NS_ERROR_UNEXPECTED;
-  }
-
-  if (aKind == PrefValueKind::Default || pref->IsLocked() ||
-      !pref->HasUserValue()) {
-
-    // Do we have a default?
-    if (!pref->HasDefaultValue()) {
-      return NS_ERROR_UNEXPECTED;
-    }
-    *aResult = pref->mDefaultValue.mIntVal;
-  } else {
-    *aResult = pref->mUserValue.mIntVal;
-  }
-
-  return NS_OK;
+  return pref ? pref->GetIntValue(aKind, aResult) : NS_ERROR_UNEXPECTED;
 }
 
 /* static */ nsresult
 Preferences::GetFloat(const char* aPrefName,
                       float* aResult,
                       PrefValueKind aKind)
 {
   NS_PRECONDITION(aResult, "aResult must not be NULL");
@@ -4509,39 +4546,17 @@ Preferences::GetCString(const char* aPre
                         nsACString& aResult,
                         PrefValueKind aKind)
 {
   NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
 
   aResult.SetIsVoid(true);
 
   PrefHashEntry* pref = pref_HashTableLookup(aPrefName);
-  if (!pref || !pref->IsTypeString()) {
-    return NS_ERROR_UNEXPECTED;
-  }
-
-  const char* stringVal = nullptr;
-  if (aKind == PrefValueKind::Default || pref->IsLocked() ||
-      !pref->HasUserValue()) {
-
-    // Do we have a default?
-    if (!pref->HasDefaultValue()) {
-      return NS_ERROR_UNEXPECTED;
-    }
-    stringVal = pref->mDefaultValue.mStringVal;
-  } else {
-    stringVal = pref->mUserValue.mStringVal;
-  }
-
-  if (!stringVal) {
-    return NS_ERROR_UNEXPECTED;
-  }
-
-  aResult = stringVal;
-  return NS_OK;
+  return pref ? pref->GetCStringValue(aKind, aResult) : NS_ERROR_UNEXPECTED;
 }
 
 /* static */ nsresult
 Preferences::GetString(const char* aPrefName,
                        nsAString& aResult,
                        PrefValueKind aKind)
 {
   nsAutoCString result;
--- a/netwerk/protocol/res/ExtensionProtocolHandler.cpp
+++ b/netwerk/protocol/res/ExtensionProtocolHandler.cpp
@@ -223,17 +223,17 @@ ExtensionStreamGetter::GetAsync(nsIStrea
   if (mIsJarChannel) {
     // Request an FD for this moz-extension URI
     gNeckoChild->SendGetExtensionFD(uri)->Then(
       mMainThreadEventTarget,
       __func__,
       [self] (const FileDescriptor& fd) {
         self->OnFD(fd);
       },
-      [self] (const mozilla::ipc::PromiseRejectReason) {
+      [self] (const mozilla::ipc::ResponseRejectReason) {
         self->OnFD(FileDescriptor());
       }
     );
     return Ok();
   }
 
   // Request an input stream for this moz-extension URI
   gNeckoChild->SendGetExtensionStream(uri)->Then(
@@ -241,17 +241,17 @@ ExtensionStreamGetter::GetAsync(nsIStrea
     __func__,
     [self] (const OptionalIPCStream& stream) {
       nsCOMPtr<nsIInputStream> inputStream;
       if (stream.type() == OptionalIPCStream::OptionalIPCStream::TIPCStream) {
         inputStream = ipc::DeserializeIPCStream(stream);
       }
       self->OnStream(inputStream);
     },
-    [self] (const mozilla::ipc::PromiseRejectReason) {
+    [self] (const mozilla::ipc::ResponseRejectReason) {
       self->OnStream(nullptr);
     }
   );
   return Ok();
 }
 
 static void
 CancelRequest(nsIStreamListener* aListener,
--- a/taskcluster/ci/spidermonkey/kind.yml
+++ b/taskcluster/ci/spidermonkey/kind.yml
@@ -71,67 +71,67 @@ jobs:
         index:
             job-name: sm-package-opt
         treeherder:
             symbol: SM-tc(pkg)
         worker:
             max-run-time: 36000
             docker-image: {in-tree: desktop-build}
             env:
-                TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/linux64/releng.manifest"
+                TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/linux64/jsshell.manifest"
         run:
             using: spidermonkey-package
             spidermonkey-variant: plain
 
     sm-mozjs-sys/debug:
         description: "Build js/src as the mozjs_sys Rust crate"
         index:
             job-name: sm-mozjs-sys-debug
         treeherder:
             platform: linux64/debug
             symbol: SM-tc(mozjs-crate)
         worker:
             max-run-time: 36000
             docker-image: {in-tree: desktop-build}
             env:
-                TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/linux64/releng.manifest"
+                TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/linux64/jsshell.manifest"
         run:
             using: spidermonkey-mozjs-crate
             spidermonkey-variant: plain
         run-on-projects: ['integration', 'release', 'try']
 
     sm-rust-bindings/debug:
         description: "Build and test the Rust bindings for SpiderMonkey"
         index:
             job-name: sm-rust-bindings-debug
         treeherder:
             symbol: SM-tc(rust)
             tier: 2
         worker:
             max-run-time: 36000
             docker-image: {in-tree: desktop-build}
             env:
-                TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/linux64/releng.manifest"
+                TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/linux64/jsshell.manifest"
         run:
             using: spidermonkey-rust-bindings
             spidermonkey-variant: plain
         run-on-projects: ['integration', 'release', 'try']
 
     sm-plain/debug:
         description: "Spidermonkey Plain"
         index:
             job-name: sm-plaindebug-debug
         treeherder:
             platform: linux64/debug
             symbol: SM-tc(p)
         worker:
             max-run-time: 36000
             docker-image: {in-tree: desktop-build}
             env:
-                TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/linux64/releng.manifest"
+                TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/linux64/jsshell.manifest"
         run:
             spidermonkey-variant: plaindebug
 
     sm-plain-windows/debug:
         description: "Spidermonkey Plain"
         index:
             job-name: sm-win-plaindebug-debug
         treeherder:
@@ -150,17 +150,17 @@ jobs:
         index:
             job-name: sm-plain-opt
         treeherder:
             symbol: SM-tc(p)
         worker:
             max-run-time: 36000
             docker-image: {in-tree: desktop-build}
             env:
-                TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/linux64/releng.manifest"
+                TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/linux64/jsshell.manifest"
         run:
             spidermonkey-variant: plain
 
     sm-plain-windows/opt:
         description: "Spidermonkey Plain"
         index:
             job-name: sm-win-plain-opt
         treeherder:
@@ -195,46 +195,46 @@ jobs:
             job-name: sm-arm64-sim-debug
         treeherder:
             platform: linux64/debug
             symbol: SM-tc(arm64)
         worker:
             max-run-time: 36000
             docker-image: {in-tree: desktop-build}
             env:
-                TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/linux64/releng.manifest"
+                TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/linux64/jsshell.manifest"
         run:
             spidermonkey-variant: arm64-sim
 
     sm-asan/opt:
         description: "Spidermonkey Address Sanitizer"
         index:
             job-name: sm-asan-opt
         treeherder:
             symbol: SM-tc(asan)
         worker:
             max-run-time: 36000
             docker-image: {in-tree: desktop-build}
             env:
-                TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/linux64/releng.manifest"
+                TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/linux64/jsshell.manifest"
         run:
             spidermonkey-variant: asan
 
     sm-compacting/debug:
         description: "Spidermonkey Compacting"
         index:
             job-name: sm-compacting-debug
         treeherder:
             platform: linux64/debug
             symbol: SM-tc(cgc)
         worker:
             max-run-time: 36000
             docker-image: {in-tree: desktop-build}
             env:
-                TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/linux64/releng.manifest"
+                TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/linux64/jsshell.manifest"
         run:
             spidermonkey-variant: compacting
 
     sm-compacting-windows/debug:
         description: "Spidermonkey Compacting"
         index:
             job-name: sm-win-compacting-debug
         treeherder:
@@ -253,17 +253,17 @@ jobs:
         index:
             job-name: sm-msan-opt
         treeherder:
             symbol: SM-tc(msan)
         worker:
             max-run-time: 36000
             docker-image: {in-tree: desktop-build}
             env:
-                TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/linux64/releng.manifest"
+                TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/linux64/jsshell.manifest"
         run:
             spidermonkey-variant: msan
 
     sm-tsan/opt:
         description: "Spidermonkey Thread Sanitizer"
         index:
             job-name: sm-tsan-opt
         treeherder:
@@ -283,41 +283,41 @@ jobs:
             job-name: sm-rootanalysis-debug
         treeherder:
             platform: linux64/debug
             symbol: SM-tc(r)
         worker:
             max-run-time: 36000
             docker-image: {in-tree: desktop-build}
             env:
-                TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/linux64/releng.manifest"
+                TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/linux64/jsshell.manifest"
         run:
             spidermonkey-variant: rootanalysis
 
     sm-nonunified/debug:
         description: "Spidermonkey Non-Unified Debug"
         index:
             job-name: sm-nonunified-debug
         treeherder:
             platform: linux64/debug
             symbol: SM-tc(nu)
         worker:
             max-run-time: 36000
             docker-image: {in-tree: desktop-build}
             env:
-                TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/linux64/releng.manifest"
+                TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/linux64/jsshell.manifest"
         run:
             spidermonkey-variant: nonunified
 
     sm-fuzzing/opt:
         description: "Spidermonkey Fuzzing"
         index:
             job-name: sm-fuzzing
         treeherder:
             platform: linux64/opt
             symbol: SM-tc(f)
         worker:
             max-run-time: 36000
             docker-image: {in-tree: desktop-build}
             env:
-                TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/linux64/releng.manifest"
+                TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/linux64/jsshell.manifest"
         run:
             spidermonkey-variant: fuzzing
--- a/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm
+++ b/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm
@@ -461,30 +461,26 @@ this.BrowserTestUtils = {
       };
       tabbrowser.addTabsProgressListener(progressListener);
     });
   },
 
   /**
    * Waits for the next browser window to open and be fully loaded.
    *
-   * @param {bool} delayedStartup (optional)
-   *        Whether or not to wait for the browser-delayed-startup-finished
-   *        observer notification before resolving. Defaults to true.
    * @param {string} initialBrowserLoaded (optional)
    *        If set, we will wait until the initial browser in the new
    *        window has loaded a particular page. If unset, the initial
    *        browser may or may not have finished loading its first page
    *        when the resulting Promise resolves.
    * @return {Promise}
    *         A Promise which resolves the next time that a DOM window
    *         opens and the delayed startup observer notification fires.
    */
-  async waitForNewWindow(delayedStartup=true,
-                                          initialBrowserLoaded=null) {
+  async waitForNewWindow(initialBrowserLoaded=null) {
     let win = await this.domWindowOpened();
 
     let promises = [
       TestUtils.topicObserved("browser-delayed-startup-finished",
                               subject => subject == win),
     ];
 
     if (initialBrowserLoaded) {
--- a/toolkit/components/extensions/webrequest/StreamFilter.cpp
+++ b/toolkit/components/extensions/webrequest/StreamFilter.cpp
@@ -90,17 +90,17 @@ StreamFilter::Connect()
     RefPtr<StreamFilter> self(this);
 
     cc->SendInitStreamFilter(mChannelId, addonId)->Then(
       GetCurrentThreadSerialEventTarget(),
       __func__,
       [=] (mozilla::ipc::Endpoint<PStreamFilterChild>&& aEndpoint) {
         self->FinishConnect(Move(aEndpoint));
       },
-      [=] (mozilla::ipc::PromiseRejectReason aReason) {
+      [=] (mozilla::ipc::ResponseRejectReason aReason) {
         self->mActor->RecvInitialized(false);
       });
   } else {
     mozilla::ipc::Endpoint<PStreamFilterChild> endpoint;
     Unused << StreamFilterParent::Create(nullptr, mChannelId, addonId, &endpoint);
 
     // Always dispatch asynchronously so JS callers have a chance to attach
     // event listeners before we dispatch events.
--- a/toolkit/components/remote/nsRemoteService.cpp
+++ b/toolkit/components/remote/nsRemoteService.cpp
@@ -1,17 +1,19 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:expandtab:shiftwidth=2:tabstop=8:
  */
 /* 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 "nsGTKRemoteService.h"
+#ifdef MOZ_ENABLE_DBUS
 #include "nsDBusRemoteService.h"
+#endif
 #include "nsRemoteService.h"
 
 #include <gtk/gtk.h>
 #include <gdk/gdk.h>
 #include <gdk/gdkx.h>
 
 #include "nsIServiceManager.h"
 #include "nsIAppShellService.h"
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -1311,24 +1311,16 @@
   "JS_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT": {
     "record_in_processes": ["main", "content"],
     "alert_emails": ["jdemooij@mozilla.com"],
     "expires_in_version": "never",
     "kind": "enumerated",
     "n_values": 10,
     "description": "Use of SpiderMonkey's deprecated language extensions in web content: ForEach=0 (obsolete), DestructuringForIn=1 (obsolete), LegacyGenerator=2 (obsolete), ExpressionClosure=3, LetBlock=4 (obsolete), LetExpression=5 (obsolete), NoSuchMethod=6 (obsolete), FlagsArgument=7 (obsolete), RegExpSourceProp=8 (obsolete), RestoredRegExpStatics=9 (obsolete), BlockScopeFunRedecl=10 (obsolete)"
   },
-  "JS_DEPRECATED_LANGUAGE_EXTENSIONS_IN_ADDONS": {
-    "record_in_processes": ["main", "content"],
-    "alert_emails": ["jdemooij@mozilla.com"],
-    "expires_in_version": "never",
-    "kind": "enumerated",
-    "n_values": 10,
-    "description": "Use of SpiderMonkey's deprecated language extensions in add-ons: ForEach=0 (obsolete), DestructuringForIn=1 (obsolete), LegacyGenerator=2 (obsolete), ExpressionClosure=3, LetBlock=4 (obsolete), LetExpression=5 (obsolete), NoSuchMethod=6 (obsolete), FlagsArgument=7 (obsolete), RegExpSourceProp=8 (obsolete), RestoredRegExpStatics=9 (obsolete), BlockScopeFunRedecl=10 (obsolete)"
-  },
   "JS_PRIVILEGED_PARSER_COMPILE_LAZY_AFTER_MS": {
     "record_in_processes": ["main", "content"],
     "alert_emails": ["dteller@mozilla.com"],
     "expires_in_version": "70",
     "bug_numbers": [1343483],
     "kind": "exponential",
     "low": 10,
     "high": 10000,
--- a/toolkit/components/telemetry/histogram-whitelists.json
+++ b/toolkit/components/telemetry/histogram-whitelists.json
@@ -956,17 +956,16 @@
     "IMAGE_DECODE_SPEED_GIF",
     "IMAGE_DECODE_SPEED_JPEG",
     "IMAGE_DECODE_SPEED_PNG",
     "IMAGE_DECODE_TIME",
     "INNERWINDOWS_WITH_MUTATION_LISTENERS",
     "IPC_SAME_PROCESS_MESSAGE_COPY_OOM_KB",
     "IPC_TRANSACTION_CANCEL",
     "IPV4_AND_IPV6_ADDRESS_CONNECTIVITY",
-    "JS_DEPRECATED_LANGUAGE_EXTENSIONS_IN_ADDONS",
     "JS_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT",
     "JS_TELEMETRY_ADDON_EXCEPTIONS",
     "LINK_ICON_SIZES_ATTR_DIMENSION",
     "LINK_ICON_SIZES_ATTR_USAGE",
     "LOCALDOMSTORAGE_CLEAR_BLOCKING_MS",
     "LOCALDOMSTORAGE_GETALLKEYS_BLOCKING_MS",
     "LOCALDOMSTORAGE_GETKEY_BLOCKING_MS",
     "LOCALDOMSTORAGE_GETLENGTH_BLOCKING_MS",
--- a/toolkit/components/windowwatcher/test/browser_new_content_window_from_chrome_principal.js
+++ b/toolkit/components/windowwatcher/test/browser_new_content_window_from_chrome_principal.js
@@ -8,17 +8,17 @@
 add_task(async function test_chrome_opens_window() {
   // This magic value of 2 means that by default, when content tries
   // to open a new window, it'll actually open in a new window instead
   // of a new tab.
   await SpecialPowers.pushPrefEnv({"set": [
     ["browser.link.open_newwindow", 2],
   ]});
 
-  let newWinPromise = BrowserTestUtils.waitForNewWindow(true, "http://example.com/");
+  let newWinPromise = BrowserTestUtils.waitForNewWindow("http://example.com/");
 
   await ContentTask.spawn(gBrowser.selectedBrowser, null, async function() {
     content.open("http://example.com/", "_blank");
   });
 
   let win = await newWinPromise;
   let browser = win.gBrowser.selectedBrowser;
 
--- a/tools/profiler/gecko/nsProfiler.cpp
+++ b/tools/profiler/gecko/nsProfiler.cpp
@@ -621,17 +621,17 @@ nsProfiler::StartGathering(double aSince
 
   mPendingProfiles = profiles.Length();
   RefPtr<nsProfiler> self = this;
   for (auto profile : profiles) {
     profile->Then(GetMainThreadSerialEventTarget(), __func__,
       [self](const nsCString& aResult) {
         self->GatheredOOPProfile(aResult);
       },
-      [self](ipc::PromiseRejectReason aReason) {
+      [self](ipc::ResponseRejectReason aReason) {
         self->GatheredOOPProfile(NS_LITERAL_CSTRING(""));
       });
   }
   if (!mPendingProfiles) {
     FinishGathering();
   }
 
   return promise;
--- a/tools/profiler/public/ProfilerParent.h
+++ b/tools/profiler/public/ProfilerParent.h
@@ -30,17 +30,17 @@ class ProfilerParentTracker;
 // and handles shutdown.
 class ProfilerParent final : public PProfilerParent
 {
 public:
   NS_INLINE_DECL_REFCOUNTING(ProfilerParent)
 
   static mozilla::ipc::Endpoint<PProfilerChild> CreateForProcess(base::ProcessId aOtherPid);
 
-  typedef MozPromise<nsCString, PromiseRejectReason, false> SingleProcessProfilePromise;
+  typedef MozPromise<nsCString, ResponseRejectReason, false> SingleProcessProfilePromise;
 
   // The following static methods can be called on any thread, but they are
   // no-ops on anything other than the main thread.
   // If called on the main thread, the call will be broadcast to all
   // registered processes (all processes for which we have a ProfilerParent
   // object).
   // At the moment, the main process always calls these methods on the main
   // thread, and that's the only process in which we need to forward these
--- a/widget/PuppetWidget.cpp
+++ b/widget/PuppetWidget.cpp
@@ -798,17 +798,17 @@ PuppetWidget::NotifyIMEOfFocusChange(con
   IMENotificationRequests(IMENotificationRequests::NOTIFY_ALL);
   RefPtr<PuppetWidget> self = this;
   mTabChild->SendNotifyIMEFocus(mContentCache, aIMENotification)->Then(
     mTabChild->TabGroup()->EventTargetFor(TaskCategory::UI),
     __func__,
     [self] (IMENotificationRequests aRequests) {
       self->mIMENotificationRequestsOfParent = aRequests;
     },
-    [self] (mozilla::ipc::PromiseRejectReason aReason) {
+    [self] (mozilla::ipc::ResponseRejectReason aReason) {
       NS_WARNING("SendNotifyIMEFocus got rejected.");
     });
 
   return NS_OK;
 }
 
 nsresult
 PuppetWidget::NotifyIMEOfCompositionUpdate(
--- a/xpcom/string/nsStringBuffer.h
+++ b/xpcom/string/nsStringBuffer.h
@@ -7,16 +7,32 @@
 #ifndef nsStringBuffer_h__
 #define nsStringBuffer_h__
 
 #include <atomic>
 #include "mozilla/MemoryReporting.h"
 
 template<class T> struct already_AddRefed;
 
+/*
+ * Add a canary field to protect against double-frees of nsStringBuffer and
+ * other potential heap corruptions.  We intend to back this out before 58 hits
+ * beta.
+ */
+#if (defined(DEBUG) || defined(NIGHTLY_BUILD)) && !defined(MOZ_ASAN)
+# define STRING_BUFFER_CANARY 1
+#endif
+
+#ifdef STRING_BUFFER_CANARY
+enum nsStringBufferCanary : uint32_t {
+  CANARY_OK = 0xaf57c8fa,
+  CANARY_POISON = 0x534dc0f5
+};
+#endif
+
 /**
  * This structure precedes the string buffers "we" allocate.  It may be the
  * case that nsTAString::mData does not point to one of these special
  * buffers.  The mDataFlags member variable distinguishes the buffer type.
  *
  * When this header is in use, it enables reference counting, and capacity
  * tracking.  NOTE: A string buffer can be modified only if its reference
  * count is 1.
@@ -24,16 +40,20 @@ template<class T> struct already_AddRefe
 class nsStringBuffer
 {
 private:
   friend class CheckStaticAtomSizes;
 
   std::atomic<uint32_t> mRefCount;
   uint32_t mStorageSize;
 
+#ifdef STRING_BUFFER_CANARY
+  uint32_t mCanary;
+#endif
+
 public:
 
   /**
    * Allocates a new string buffer, with given size in bytes and a
    * reference count of one.  When the string buffer is no longer needed,
    * it should be released via Release.
    *
    * It is up to the caller to set the bytes corresponding to the string
@@ -73,17 +93,22 @@ public:
 
   /**
    * This method returns the string buffer corresponding to the given data
    * pointer.  The data pointer must have been returned previously by a
    * call to the nsStringBuffer::Data method.
    */
   static nsStringBuffer* FromData(void* aData)
   {
-    return reinterpret_cast<nsStringBuffer*>(aData) - 1;
+    nsStringBuffer* sb = reinterpret_cast<nsStringBuffer*>(aData) - 1;
+#ifdef STRING_BUFFER_CANARY
+    if (MOZ_UNLIKELY(sb->mCanary != CANARY_OK))
+      sb->FromDataCanaryCheckFailed();
+#endif
+    return sb;
   }
 
   /**
    * This method returns the data pointer for this string buffer.
    */
   void* Data() const
   {
     return const_cast<char*>(reinterpret_cast<const char*>(this + 1));
@@ -170,11 +195,20 @@ public:
    * unshared.
    *
    * WARNING: Only use this if you really know what you are doing, because
    * it can easily lead to double-counting strings.  If you do use them,
    * please explain clearly in a comment why it's safe and won't lead to
    * double-counting.
    */
   size_t SizeOfIncludingThisEvenIfShared(mozilla::MallocSizeOf aMallocSizeOf) const;
+
+#ifdef STRING_BUFFER_CANARY
+  /*
+   * Called by FromData if the canary check failed.  This is out-of-line in
+   * nsSubstring.cpp so that MOZ_CRASH_UNSAFE_PRINTF is available via #includes.
+   * It is not available in FromData due to #include-order.
+   */
+  void FromDataCanaryCheckFailed() const;
+#endif
 };
 
 #endif /* !defined(nsStringBuffer_h__ */
--- a/xpcom/string/nsSubstring.cpp
+++ b/xpcom/string/nsSubstring.cpp
@@ -15,32 +15,46 @@
 #include <stdio.h>
 #endif
 
 #include <stdlib.h>
 #include "nsSubstring.h"
 #include "nsString.h"
 #include "nsStringBuffer.h"
 #include "nsDependentString.h"
+#include "nsPrintfCString.h"
 #include "nsMemory.h"
 #include "prprf.h"
 #include "nsStaticAtom.h"
 #include "nsCOMPtr.h"
 
 #include "mozilla/IntegerPrintfMacros.h"
 #ifdef XP_WIN
 #include <windows.h>
 #include <process.h>
 #define getpid() _getpid()
 #define pthread_self() GetCurrentThreadId()
 #else
 #include <pthread.h>
 #include <unistd.h>
 #endif
 
+#ifdef STRING_BUFFER_CANARY
+#define CHECK_STRING_BUFFER_CANARY(c)                                     \
+  do {                                                                    \
+    if ((c) != CANARY_OK) {                                               \
+      MOZ_CRASH_UNSAFE_PRINTF("Bad canary value %d", c);                  \
+    }                                                                     \
+  } while(0)
+#else
+#define CHECK_STRING_BUFFER_CANARY(c)                                     \
+  do {                                                                    \
+  } while(0)
+#endif
+
 using mozilla::Atomic;
 
 // ---------------------------------------------------------------------------
 
 static const char16_t gNullChar = 0;
 
 char* const nsCharTraits<char>::sEmptyBuffer =
   (char*)const_cast<char16_t*>(&gNullChar);
@@ -200,28 +214,33 @@ nsStringBuffer::AddRef()
     ;
   STRING_STAT_INCREMENT(Share);
   NS_LOG_ADDREF(this, count, "nsStringBuffer", sizeof(*this));
 }
 
 void
 nsStringBuffer::Release()
 {
+  CHECK_STRING_BUFFER_CANARY(mCanary);
+
   // Since this may be the last release on this thread, we need
   // release semantics so that prior writes on this thread are visible
   // to the thread that destroys the object when it reads mValue with
   // acquire semantics.
   uint32_t count = mRefCount.fetch_sub(1, std::memory_order_release) - 1;
   NS_LOG_RELEASE(this, count, "nsStringBuffer");
   if (count == 0) {
     // We're going to destroy the object on this thread, so we need
     // acquire semantics to synchronize with the memory released by
     // the last release on other threads, that is, to ensure that
     // writes prior to that release are now visible on this thread.
     count = mRefCount.load(std::memory_order_acquire);
+#ifdef STRING_BUFFER_CANARY
+    mCanary = CANARY_POISON;
+#endif
 
     STRING_STAT_INCREMENT(Free);
     free(this); // we were allocated with |malloc|
   }
 }
 
 /**
  * Alloc returns a pointer to a new string header with set capacity.
@@ -236,26 +255,30 @@ nsStringBuffer::Alloc(size_t aSize)
 
   nsStringBuffer* hdr =
     (nsStringBuffer*)malloc(sizeof(nsStringBuffer) + aSize);
   if (hdr) {
     STRING_STAT_INCREMENT(Alloc);
 
     hdr->mRefCount = 1;
     hdr->mStorageSize = aSize;
+#ifdef STRING_BUFFER_CANARY
+    hdr->mCanary = CANARY_OK;
+#endif
     NS_LOG_ADDREF(hdr, 1, "nsStringBuffer", sizeof(*hdr));
   }
   return dont_AddRef(hdr);
 }
 
 nsStringBuffer*
 nsStringBuffer::Realloc(nsStringBuffer* aHdr, size_t aSize)
 {
   STRING_STAT_INCREMENT(Realloc);
 
+  CHECK_STRING_BUFFER_CANARY(aHdr->mCanary);
   NS_ASSERTION(aSize != 0, "zero capacity allocation not allowed");
   NS_ASSERTION(sizeof(nsStringBuffer) + aSize <= size_t(uint32_t(-1)) &&
                sizeof(nsStringBuffer) + aSize > aSize,
                "mStorageSize will truncate");
 
   // no point in trying to save ourselves if we hit this assertion
   NS_ASSERTION(!aHdr->IsReadonly(), "|Realloc| attempted on readonly string");
 
@@ -344,16 +367,24 @@ nsStringBuffer::SizeOfIncludingThisIfUns
 }
 
 size_t
 nsStringBuffer::SizeOfIncludingThisEvenIfShared(mozilla::MallocSizeOf aMallocSizeOf) const
 {
   return aMallocSizeOf(this);
 }
 
+#ifdef STRING_BUFFER_CANARY
+void
+nsStringBuffer::FromDataCanaryCheckFailed() const
+{
+  MOZ_CRASH_UNSAFE_PRINTF("Bad canary value %d in FromData", mCanary);
+}
+#endif
+
 // ---------------------------------------------------------------------------
 
 // define nsAString
 #include "nsTSubstring.cpp"
 
 // Provide rust bindings to the nsA[C]String types
 extern "C" {