Merge inbound to mozilla-central r=merge a=merge
authorshindli <shindli@mozilla.com>
Wed, 22 Nov 2017 23:29:44 +0200
changeset 393232 960f50c2e0a991ab2ab313132e69fb2c96cb7866
parent 393231 f5dafe8991b323b824768bc2e9bc0979f2f7f8a8 (current diff)
parent 393147 6e00d3b7df15e2dd07a0bd3c7bd174c8730f493d (diff)
child 393233 c01eab6a9e8065c42b69ef4a5e9616cdb90bdace
child 393300 4540bbf4d1ab2a30c4d1bfb20687209ee841b8d6
push id97613
push usershindli@mozilla.com
push dateWed, 22 Nov 2017 22:22:51 +0000
treeherdermozilla-inbound@c01eab6a9e80 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone59.0a1
first release with
nightly linux32
960f50c2e0a9 / 59.0a1 / 20171122220056 / files
nightly linux64
960f50c2e0a9 / 59.0a1 / 20171122220056 / files
nightly mac
960f50c2e0a9 / 59.0a1 / 20171122220056 / files
nightly win32
960f50c2e0a9 / 59.0a1 / 20171122220056 / files
nightly win64
960f50c2e0a9 / 59.0a1 / 20171122220056 / 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
dom/power/test/mochitest.ini
dom/power/test/test_bug957893.html
dom/power/test/test_bug957899.html
dom/power/test/test_bug957899_iframe.html
dom/power/test/test_wakelock_not_exposed.html
dom/webauthn/u2f-hid-rs/src/khmatcher.rs
dom/webauthn/u2f-hid-rs/src/linux/devicemap.rs
dom/webauthn/u2f-hid-rs/src/macos/devicemap.rs
dom/webauthn/u2f-hid-rs/src/windows/devicemap.rs
dom/webidl/MozWakeLock.webidl
dom/workers/test/serviceworkers/test_devtools_serviceworker_interception.html
--- a/accessible/windows/msaa/Compatibility.cpp
+++ b/accessible/windows/msaa/Compatibility.cpp
@@ -156,21 +156,22 @@ DetectInSendMessageExCompat(PEXCEPTION_P
       sVectoredExceptionHandler = nullptr;
     }
   }
   return EXCEPTION_CONTINUE_SEARCH;
 }
 
 uint32_t Compatibility::sConsumers = Compatibility::UNKNOWN;
 
-void
-Compatibility::Init()
+/**
+ * This function is safe to call multiple times.
+ */
+/* static */ void
+Compatibility::InitConsumers()
 {
-  // Note we collect some AT statistics/telemetry here for convenience.
-
   HMODULE jawsHandle = ::GetModuleHandleW(L"jhook");
   if (jawsHandle)
     sConsumers |= (IsModuleVersionLessThan(jawsHandle, 19, 0)) ?
                    OLDJAWS : JAWS;
 
   if (::GetModuleHandleW(L"gwm32inc"))
     sConsumers |= WE;
 
@@ -197,17 +198,31 @@ Compatibility::Init()
     sConsumers |= YOUDAO;
 
   if (::GetModuleHandleW(L"uiautomation") ||
       ::GetModuleHandleW(L"uiautomationcore"))
     sConsumers |= UIAUTOMATION;
 
   // If we have a known consumer remove the unknown bit.
   if (sConsumers != Compatibility::UNKNOWN)
-    sConsumers ^= Compatibility::UNKNOWN;
+    sConsumers &= ~Compatibility::UNKNOWN;
+}
+
+/* static */ bool
+Compatibility::HasKnownNonUiaConsumer()
+{
+  InitConsumers();
+  return sConsumers & ~(Compatibility::UNKNOWN | UIAUTOMATION);
+}
+
+void
+Compatibility::Init()
+{
+  // Note we collect some AT statistics/telemetry here for convenience.
+  InitConsumers();
 
 #ifdef MOZ_CRASHREPORTER
   CrashReporter::
     AnnotateCrashReport(NS_LITERAL_CSTRING("AccessibilityInProcClient"),
                         nsPrintfCString("0x%X", sConsumers));
 #endif
 
   // Gather telemetry
--- a/accessible/windows/msaa/Compatibility.h
+++ b/accessible/windows/msaa/Compatibility.h
@@ -52,21 +52,28 @@ public:
    */
   static void GetHumanReadableConsumersStr(nsAString &aResult);
 
   /**
    * Initialize compatibility mode information.
    */
   static void Init();
 
+  /**
+   * return true if a known, non-UIA a11y consumer is present
+   */
+  static bool HasKnownNonUiaConsumer();
+
 private:
   Compatibility();
   Compatibility(const Compatibility&);
   Compatibility& operator = (const Compatibility&);
 
+  static void InitConsumers();
+
   /**
    * List of detected consumers of a11y (used for statistics/telemetry and compat)
    */
   enum {
     NVDA = 1 << 0,
     JAWS = 1 << 1,
     OLDJAWS = 1 << 2,
     WE = 1 << 3,
--- a/accessible/windows/msaa/LazyInstantiator.cpp
+++ b/accessible/windows/msaa/LazyInstantiator.cpp
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "LazyInstantiator.h"
 
 #include "MainThreadUtils.h"
 #include "mozilla/a11y/Accessible.h"
+#include "mozilla/a11y/Compatibility.h"
 #include "mozilla/a11y/Platform.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/mscom/MainThreadRuntime.h"
 #include "mozilla/mscom/Registration.h"
 #include "mozilla/UniquePtr.h"
 #include "nsAccessibilityService.h"
 #include "nsWindowsHelpers.h"
 #include "nsCOMPtr.h"
@@ -226,42 +227,76 @@ LazyInstantiator::GetClientExecutableNam
     return false;
   }
 
   file.forget(aOutClientExe);
   return NS_SUCCEEDED(rv);
 }
 
 /**
+ * This is the blocklist for known "bad" DLLs that instantiate a11y.
+ */
+static const wchar_t* gBlockedInprocDlls[] = {
+  L"dtvhooks.dll",  // RealPlayer, bug 1418535
+  L"dtvhooks64.dll" // RealPlayer, bug 1418535
+};
+
+/**
+ * Check for the presence of any known "bad" injected DLLs that may be trying
+ * to instantiate a11y.
+ *
+ * @return true to block a11y instantiation, otherwise false to continue
+ */
+bool
+LazyInstantiator::IsBlockedInjection()
+{
+  if (Compatibility::HasKnownNonUiaConsumer()) {
+    // If we already see a known AT, don't block a11y instantiation
+    return false;
+  }
+
+  for (size_t index = 0, len = ArrayLength(gBlockedInprocDlls); index < len;
+       ++index) {
+    if (::GetModuleHandleW(gBlockedInprocDlls[index])) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+/**
  * Given a remote client's thread ID, determine whether we should proceed with
  * a11y instantiation. This is where telemetry should be gathered and any
  * potential blocking of unwanted a11y clients should occur.
  *
  * @return true if we should instantiate a11y
  */
 bool
 LazyInstantiator::ShouldInstantiate(const DWORD aClientTid)
 {
   if (!aClientTid) {
     // aClientTid == 0 implies that this is either an in-process call, or else
     // we failed to retrieve information about the remote caller.
-    // We should always default to instantiating a11y in this case.
-    return true;
+    // We should always default to instantiating a11y in this case, provided
+    // that we don't see any known bad injected DLLs.
+    return !IsBlockedInjection();
   }
 
   nsCOMPtr<nsIFile> clientExe;
   if (!GetClientExecutableName(aClientTid, getter_AddRefs(clientExe))) {
 #if defined(MOZ_TELEMETRY_REPORTING)
     AccumulateTelemetry(NS_LITERAL_STRING("(Failed to retrieve client image name)"));
 #endif // defined(MOZ_TELEMETRY_REPORTING)
     // We should return true as a failsafe
     return true;
   }
 
-  // Blocklist checks should go here. return false if we should not instantiate.
+  // Blocklist checks for external clients should go here.
+  // return false if we should not instantiate.
   /*
   if (ClientShouldBeBlocked(clientExe)) {
     return false;
   }
   */
 
   // Store full path to executable for support purposes.
   nsAutoString filePath;
--- a/accessible/windows/msaa/LazyInstantiator.h
+++ b/accessible/windows/msaa/LazyInstantiator.h
@@ -78,16 +78,17 @@ public:
 
   // IServiceProvider
   STDMETHODIMP QueryService(REFGUID aServiceId, REFIID aServiceIid, void** aOutInterface) override;
 
 private:
   explicit LazyInstantiator(HWND aHwnd);
   ~LazyInstantiator();
 
+  bool IsBlockedInjection();
   bool ShouldInstantiate(const DWORD aClientTid);
 
   bool GetClientExecutableName(const DWORD aClientTid, nsIFile** aOutClientExe);
 #if defined(MOZ_TELEMETRY_REPORTING) || defined(MOZ_CRASHREPORTER)
   class AccumulateRunnable final : public Runnable
   {
   public:
     explicit AccumulateRunnable(LazyInstantiator* aObj)
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -7577,18 +7577,18 @@
         if (draggedTab && dropEffect == "copy") {
           // copy the dropped tab (wherever it's from)
           let newIndex = this._getDropIndex(event, false);
           let newTab = this.tabbrowser.duplicateTab(draggedTab);
           this.tabbrowser.moveTabTo(newTab, newIndex);
           if (draggedTab.parentNode != this || event.shiftKey)
             this.selectedItem = newTab;
         } else if (draggedTab && draggedTab.parentNode == this) {
-          let oldTranslateX = draggedTab._dragData.translateX;
-          let tabWidth = draggedTab._dragData.tabWidth;
+          let oldTranslateX = Math.round(draggedTab._dragData.translateX);
+          let tabWidth = Math.round(draggedTab._dragData.tabWidth);
           let translateOffset = oldTranslateX % tabWidth;
           let newTranslateX = oldTranslateX - translateOffset;
           if (oldTranslateX > 0 && translateOffset > tabWidth / 2) {
             newTranslateX += tabWidth;
           } else if (oldTranslateX < 0 && -translateOffset > tabWidth / 2) {
             newTranslateX -= tabWidth;
           }
 
new file mode 100644
--- /dev/null
+++ b/build/build-clang/clang-6-pre-linux64.json
@@ -0,0 +1,20 @@
+{
+    "llvm_revision": "317840",
+    "stages": "3",
+    "build_libcxx": true,
+    "build_type": "Release",
+    "assertions": false,
+    "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/trunk",
+    "clang_repo": "https://llvm.org/svn/llvm-project/cfe/trunk",
+    "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/trunk",
+    "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/trunk",
+    "libcxxabi_repo": "https://llvm.org/svn/llvm-project/libcxxabi/trunk",
+    "python_path": "/usr/bin/python2.7",
+    "gcc_dir": "/builds/worker/workspace/build/src/gcc",
+    "cc": "/builds/worker/workspace/build/src/gcc/bin/gcc",
+    "cxx": "/builds/worker/workspace/build/src/gcc/bin/g++",
+    "as": "/builds/worker/workspace/build/src/gcc/bin/gcc",
+    "patches": [
+      "find_symbolizer_linux.patch"
+    ]
+}
new file mode 100644
--- /dev/null
+++ b/build/build-clang/find_symbolizer_linux.patch
@@ -0,0 +1,58 @@
+We currently need this patch because ASan only searches PATH to find the
+llvm-symbolizer binary to symbolize ASan traces. On testing machines, this
+can be installed in PATH easily. However, for e.g. the ASan Nightly Project,
+where we ship an ASan build, including llvm-symbolizer, to the user, we
+cannot expect llvm-symbolizer to be on PATH. Instead, we should try to look
+it up next to the binary. This patch implements the functionality for Linux
+only until there is similar functionality provided upstream.
+
+diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_file.cc b/compiler-rt/lib/sanitizer_common/sanitizer_file.cc
+index cde54bf..8daade1 100644
+--- a/compiler-rt/lib/sanitizer_common/sanitizer_file.cc
++++ b/compiler-rt/lib/sanitizer_common/sanitizer_file.cc
+@@ -21,6 +21,10 @@
+ #include "sanitizer_common.h"
+ #include "sanitizer_file.h"
+ 
++#if SANITIZER_LINUX
++#include "sanitizer_posix.h"
++#endif
++
+ namespace __sanitizer {
+ 
+ void CatastrophicErrorWrite(const char *buffer, uptr length) {
+@@ -156,6 +160,34 @@ char *FindPathToBinary(const char *name) {
+     if (*end == '\0') break;
+     beg = end + 1;
+   }
++
++#if SANITIZER_LINUX
++  // If we cannot find the requested binary in PATH, we should try to locate
++  // it next to the binary, in case it is shipped with the build itself
++  // (e.g. llvm-symbolizer shipped with sanitizer build to symbolize on client.
++  if (internal_readlink("/proc/self/exe", buffer.data(), kMaxPathLength) < 0)
++    return nullptr;
++
++  uptr buf_len = internal_strlen(buffer.data());
++
++  /* Avoid using dirname() here */
++  while (buf_len > 0) {
++    if (buffer[buf_len - 1] == '/')
++      break;
++    buf_len--;
++  }
++
++  if (!buf_len)
++    return nullptr;
++
++  if (buf_len + name_len + 1 <= kMaxPathLength) {
++    internal_memcpy(&buffer[buf_len], name, name_len);
++    buffer[buf_len + name_len] = '\0';
++    if (FileExists(buffer.data()))
++      return internal_strdup(buffer.data());
++  }
++#endif
++
+   return nullptr;
+ }
+ 
--- a/build/sanitizers/asan_blacklist_win.txt
+++ b/build/sanitizers/asan_blacklist_win.txt
@@ -1,20 +1,20 @@
 # This is originally copied from Chromium tools/memory/asan/blacklist_win.txt.
 # The rules in this file are only applied at compile time. If you can modify the
 # source in question, consider function attributes to disable instrumentation.
 
 # Bug 1200740 - ASan crash due to child process function interceptions
 # Sandbox executes some of its code before the ASan RTL gets initialized and
 # maps shadow memory.  As a result, instrumented code tries to access unavailable
 # shadow memory and faults.
-fun:*TargetNtSetInformationThread@20
-fun:*TargetNtOpenThreadToken@20
-fun:*TargetNtOpenThreadTokenEx@24
-fun:*TargetNtMapViewOfSection@44
+fun:*TargetNtSetInformationThread*
+fun:*TargetNtOpenThreadToken*
+fun:*TargetNtOpenThreadTokenEx*
+fun:*TargetNtMapViewOfSection*
 fun:*AutoProtectMemory*sandbox*
 fun:*EatResolverThunk*sandbox*
 fun:*InterceptionAgent*sandbox*
 fun:*ResolverThunk*sandbox*
 fun:*Target*SandboxFactory*sandbox*
 fun:*ProcessState*sandbox*
 src:*pe_image.h
 src:*pe_image.cc
--- a/devtools/client/inspector/test/browser.ini
+++ b/devtools/client/inspector/test/browser.ini
@@ -116,16 +116,17 @@ skip-if = os == 'win' # bug 1413442
 [browser_inspector_highlighter-keybinding_03.js]
 [browser_inspector_highlighter-keybinding_04.js]
 [browser_inspector_highlighter-measure_01.js]
 [browser_inspector_highlighter-measure_02.js]
 [browser_inspector_highlighter-options.js]
 [browser_inspector_highlighter-preview.js]
 [browser_inspector_highlighter-rulers_01.js]
 [browser_inspector_highlighter-rulers_02.js]
+[browser_inspector_highlighter-rulers_03.js]
 [browser_inspector_highlighter-selector_01.js]
 [browser_inspector_highlighter-selector_02.js]
 [browser_inspector_highlighter-xbl.js]
 [browser_inspector_highlighter-zoom.js]
 [browser_inspector_iframe-navigation.js]
 [browser_inspector_infobar_01.js]
 [browser_inspector_infobar_02.js]
 [browser_inspector_infobar_03.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/test/browser_inspector_highlighter-rulers_03.js
@@ -0,0 +1,62 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+// Test the creation of the viewport infobar and makes sure if resizes correctly
+
+const TEST_URL = "data:text/html;charset=utf-8," +
+                 "<div style='position:absolute;left: 0; top: 0; " +
+                 "width: 20px; height: 50px'></div>";
+
+const ID = "rulers-highlighter-";
+
+var {Toolbox} = require("devtools/client/framework/toolbox");
+
+add_task(function* () {
+  let { inspector, testActor } = yield openInspectorForURL(TEST_URL);
+  let front = inspector.inspector;
+
+  let highlighter = yield front.getHighlighterByType("RulersHighlighter");
+
+  yield isShown(highlighter, inspector, testActor);
+  yield hasRightLabelsContent(highlighter, inspector, testActor);
+  yield resizeInspector(highlighter, inspector, testActor);
+  yield hasRightLabelsContent(highlighter, inspector, testActor);
+
+  yield highlighter.finalize();
+});
+
+function* isShown(highlighterFront, inspector, testActor) {
+  info("Checking that the viewport infobar is displayed");
+  // the rulers doesn't need any node, but as highligher it seems mandatory
+  // ones, so the body is given
+  let body = yield getNodeFront("body", inspector);
+  yield highlighterFront.show(body);
+
+  let hidden = yield testActor.getHighlighterNodeAttribute(
+    `${ID}viewport-infobar-container`, "hidden", highlighterFront);
+
+  isnot(hidden, "true", "viewport infobar is visible after show");
+}
+
+function* hasRightLabelsContent(highlighterFront, inspector, testActor) {
+  info("Checking the rulers dimension tooltip have the proper text");
+
+  let dimensionText = yield testActor.getHighlighterNodeTextContent(
+    `${ID}viewport-infobar-container`, highlighterFront);
+
+  let windowDimensions = yield testActor.getWindowDimensions();
+  let windowHeight = Math.round(windowDimensions.height);
+  let windowWidth = Math.round(windowDimensions.width);
+  let windowText = windowHeight + "px \u00D7 " + windowWidth + "px";
+
+  is(dimensionText, windowText, "Dimension text was created successfully");
+}
+
+function* resizeInspector(highlighterFront, inspector, testActor) {
+  info("Docking the toolbox to the side of the browser to change the window size");
+  let toolbox = inspector.toolbox;
+  yield toolbox.switchHost(Toolbox.HostType.SIDE);
+}
--- a/devtools/server/actors/highlighters.css
+++ b/devtools/server/actors/highlighters.css
@@ -381,16 +381,27 @@
   text-anchor: start;
 }
 
 :-moz-native-anonymous .rulers-highlighter-vertical-labels > text {
   transform: rotate(-90deg);
   text-anchor: end;
 }
 
+:-moz-native-anonymous .rulers-highlighter-viewport-infobar-container {
+  shape-rendering: crispEdges;
+  background-color: rgba(255, 255, 255, 0.7);
+  font: var(--highlighter-font-family);
+  position: fixed;
+  top: 30px;
+  right: 0px;
+  font-size: 12px;
+  padding: 4px;
+}
+
 /* Measuring Tool Highlighter */
 
 :-moz-native-anonymous .measuring-tool-highlighter-root {
   position: absolute;
   top: 0;
   left: 0;
   pointer-events: auto;
   cursor: crosshair;
--- a/devtools/server/actors/highlighters/rulers.js
+++ b/devtools/server/actors/highlighters/rulers.js
@@ -187,16 +187,26 @@ RulersHighlighter.prototype = {
         hidden: "true"
       },
       prefix
     });
 
     createRuler("x", RULERS_MAX_X_AXIS);
     createRuler("y", RULERS_MAX_Y_AXIS);
 
+    createNode(window, {
+      parent: container,
+      attributes: {
+        "class": "viewport-infobar-container",
+        "id": "viewport-infobar-container",
+        "position": "top"
+      },
+      prefix
+    });
+
     return container;
   },
 
   handleEvent: function (event) {
     switch (event.type) {
       case "scroll":
         this._onScroll(event);
         break;
@@ -232,16 +242,18 @@ RulersHighlighter.prototype = {
     let zoom = getCurrentZoom(window);
     let isZoomChanged = zoom !== this._zoom;
 
     if (isZoomChanged) {
       this._zoom = zoom;
       this.updateViewport();
     }
 
+    this.updateViewportInfobar();
+
     setIgnoreLayoutChanges(false, window.document.documentElement);
 
     this._rafID = window.requestAnimationFrame(() => this._update());
   },
 
   _cancelUpdate: function () {
     if (this._rafID) {
       this.env.window.cancelAnimationFrame(this._rafID);
@@ -260,16 +272,24 @@ RulersHighlighter.prototype = {
     // where on non high dpi monitor would be 1.
     let minWidth = 1 / pixelRatio;
     let strokeWidth = Math.min(minWidth, minWidth / this._zoom);
 
     this.markup.getElement(this.ID_CLASS_PREFIX + "root").setAttribute("style",
       `stroke-width:${strokeWidth};`);
   },
 
+  updateViewportInfobar: function () {
+    let { window } = this.env;
+    let { innerHeight, innerWidth } = window;
+    let infobarId = this.ID_CLASS_PREFIX + "viewport-infobar-container";
+    let textContent = innerHeight + "px \u00D7 " + innerWidth + "px";
+    this.markup.getElement(infobarId).setTextContent(textContent);
+  },
+
   destroy: function () {
     this.hide();
 
     let { pageListenerTarget } = this.env;
 
     if (pageListenerTarget) {
       pageListenerTarget.removeEventListener("scroll", this);
       pageListenerTarget.removeEventListener("pagehide", this);
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -8,16 +8,21 @@
 
 #include <algorithm>
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/Casting.h"
+#include "mozilla/dom/ClientChannelHelper.h"
+#include "mozilla/dom/ClientHandle.h"
+#include "mozilla/dom/ClientInfo.h"
+#include "mozilla/dom/ClientManager.h"
+#include "mozilla/dom/ClientSource.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/HTMLAnchorElement.h"
 #include "mozilla/dom/PendingGlobalHistoryEntry.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/ProfileTimelineMarkerBinding.h"
 #include "mozilla/dom/ScreenOrientation.h"
 #include "mozilla/dom/ToJSValue.h"
@@ -1685,19 +1690,20 @@ nsDocShell::LoadStream(nsIInputStream* a
   mLoadType = loadType;
 
   if (!triggeringPrincipal) {
     triggeringPrincipal = nsContentUtils::GetSystemPrincipal();
   }
 
   // build up a channel for this stream.
   nsCOMPtr<nsIChannel> channel;
+  nsCOMPtr<nsIInputStream> stream = aStream;
   nsresult rv = NS_NewInputStreamChannel(getter_AddRefs(channel),
                                          uri,
-                                         aStream,
+                                         stream.forget(),
                                          triggeringPrincipal,
                                          nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
                                          nsIContentPolicy::TYPE_OTHER,
                                          aContentType,
                                          aContentCharset);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
 
   nsCOMPtr<nsIURILoader> uriLoader(do_GetService(NS_URI_LOADER_CONTRACTID));
@@ -3385,16 +3391,90 @@ nsDocShell::GetParent(nsIDocShellTreeIte
 already_AddRefed<nsDocShell>
 nsDocShell::GetParentDocshell()
 {
   nsCOMPtr<nsIDocShell> docshell = do_QueryInterface(GetAsSupports(mParent));
   return docshell.forget().downcast<nsDocShell>();
 }
 
 void
+nsDocShell::MaybeCreateInitialClientSource(nsIPrincipal* aPrincipal)
+{
+  // If there is an existing document then there is no need to create
+  // a client for a future initial about:blank document.
+  if (mScriptGlobal && mScriptGlobal->GetExtantDoc()) {
+    MOZ_DIAGNOSTIC_ASSERT(
+      mScriptGlobal->GetCurrentInnerWindowInternal()->GetClientInfo().isSome());
+    MOZ_DIAGNOSTIC_ASSERT(!mInitialClientSource);
+    return;
+  }
+
+  // Don't recreate the initial client source.  We call this multiple times
+  // when DoChannelLoad() is called before CreateAboutBlankContentViewer.
+  if (mInitialClientSource) {
+    return;
+  }
+
+  // Don't pre-allocate the client when we are sandboxed.  The inherited
+  // principal does not take sandboxing into account.
+  // TODO: Refactor sandboxing principal code out so we can use it here.
+  if (!aPrincipal && (mSandboxFlags & SANDBOXED_ORIGIN)) {
+    return;
+  }
+
+  nsIPrincipal* principal = aPrincipal ? aPrincipal
+                                       : GetInheritedPrincipal(false);
+
+  // Sometimes there is no principal available when we are called from
+  // CreateAboutBlankContentViewer.  For example, sometimes the principal
+  // is only extracted from the load context after the document is created
+  // in nsDocument::ResetToURI().  Ideally we would do something similar
+  // here, but for now lets just avoid the issue by not preallocating the
+  // client.
+  if (!principal) {
+    return;
+  }
+
+  nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
+  if (!win) {
+    return;
+  }
+
+  mInitialClientSource =
+    ClientManager::CreateSource(ClientType::Window,
+                                win->EventTargetFor(TaskCategory::Other),
+                                principal);
+
+  // Mark the initial client as execution ready, but owned by the docshell.
+  // If the client is actually used this will cause ClientSource to force
+  // the creation of the initial about:blank by calling nsDocShell::GetDocument().
+  mInitialClientSource->DocShellExecutionReady(this);
+}
+
+Maybe<ClientInfo>
+nsDocShell::GetInitialClientInfo() const
+{
+  if (mInitialClientSource) {
+    Maybe<ClientInfo> result;
+    result.emplace(mInitialClientSource->Info());
+    return Move(result);
+  }
+
+  nsGlobalWindowInner* innerWindow =
+    mScriptGlobal ? mScriptGlobal->GetCurrentInnerWindowInternal() : nullptr;
+  nsIDocument* doc = innerWindow ? innerWindow->GetExtantDoc() : nullptr;
+
+  if (!doc || !doc->IsInitialDocument()) {
+    return Maybe<ClientInfo>();
+  }
+
+  return innerWindow->GetClientInfo();
+}
+
+void
 nsDocShell::RecomputeCanExecuteScripts()
 {
   bool old = mCanExecuteScripts;
   RefPtr<nsDocShell> parent = GetParentDocshell();
 
   // If we have no tree owner, that means that we've been detached from the
   // docshell tree (this is distinct from having no parent dochshell, which
   // is the case for root docshells). It would be nice to simply disallow
@@ -5916,16 +5996,19 @@ nsDocShell::Destroy()
       const char* msg = mItemType == typeContent ?
         NS_WEBNAVIGATION_DESTROY : NS_CHROME_WEBNAVIGATION_DESTROY;
       serv->NotifyObservers(GetAsSupports(this), msg, nullptr);
     }
   }
 
   mIsBeingDestroyed = true;
 
+  // Brak the cycle with the initial client, if present.
+  mInitialClientSource.reset();
+
   // Make sure we don't record profile timeline markers anymore
   SetRecordProfileTimelineMarkers(false);
 
   // Remove our pref observers
   if (mObserveErrorPages) {
     mObserveErrorPages = false;
   }
 
@@ -7751,16 +7834,20 @@ nsDocShell::EndPageLoad(nsIWebProgress* 
         internalLoadGroup->OnEndPageLoad(aChannel);
       }
     }
   }
 
   // Timing is picked up by the window, we don't need it anymore
   mTiming = nullptr;
 
+  // Make sure to discard the initial client if we never created the initial
+  // about:blank document.
+  mInitialClientSource.reset();
+
   // clean up reload state for meta charset
   if (eCharsetReloadRequested == mCharsetReloadState) {
     mCharsetReloadState = eCharsetReloadStopOrigional;
   } else {
     mCharsetReloadState = eCharsetReloadInit;
   }
 
   // Save a pointer to the currently-loading history entry.
@@ -8247,16 +8334,19 @@ nsDocShell::CreateAboutBlankContentViewe
       if (aPrincipal) {
         principal = NullPrincipal::CreateWithInheritedAttributes(aPrincipal);
       } else {
         principal = NullPrincipal::CreateWithInheritedAttributes(this);
       }
     } else {
       principal = aPrincipal;
     }
+
+    MaybeCreateInitialClientSource(principal);
+
     // generate (about:blank) document to load
     blankDoc = nsContentDLF::CreateBlankDocument(mLoadGroup, principal, this);
     if (blankDoc) {
       // Hack: set the base URI manually, since this document never
       // got Reset() with a channel.
       blankDoc->SetBaseURI(aBaseURI);
 
       // Copy our sandbox flags to the document. These are immutable
@@ -11742,16 +11832,34 @@ nsDocShell::DoChannelLoad(nsIChannel* aC
 
   uint32_t openFlags = 0;
   if (mLoadType == LOAD_LINK) {
     openFlags |= nsIURILoader::IS_CONTENT_PREFERRED;
   }
   if (!mAllowContentRetargeting) {
     openFlags |= nsIURILoader::DONT_RETARGET;
   }
+
+  nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
+  NS_ENSURE_TRUE(win, NS_ERROR_FAILURE);
+
+  MaybeCreateInitialClientSource();
+
+  // Since we are loading a document we need to make sure the proper reserved
+  // and initial client data is stored on the nsILoadInfo.  The
+  // ClientChannelHelper does this and ensures that it is propagated properly
+  // on redirects.  We pass no reserved client here so that the helper will
+  // create the reserved ClientSource if necessary.
+  Maybe<ClientInfo> noReservedClient;
+  rv = AddClientChannelHelper(aChannel,
+                              Move(noReservedClient),
+                              GetInitialClientInfo(),
+                              win->EventTargetFor(TaskCategory::Other));
+  NS_ENSURE_SUCCESS(rv, rv);
+
   rv = aURILoader->OpenURI(aChannel, openFlags, this);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // We're about to load a new page and it may take time before necko
   // gives back any data, so main thread might have a chance to process a
   // collector slice
   nsJSContext::MaybeRunNextCollectorSlice(this, JS::gcreason::DOCSHELL);
 
@@ -15144,16 +15252,22 @@ nsDocShell::InFrameSwap()
     if (shell->mInFrameSwap) {
       return true;
     }
     shell = shell->GetParentDocshell();
   } while (shell);
   return false;
 }
 
+UniquePtr<ClientSource>
+nsDocShell::TakeInitialClientSource()
+{
+  return Move(mInitialClientSource);
+}
+
 NS_IMETHODIMP
 nsDocShell::IssueWarning(uint32_t aWarning, bool aAsError)
 {
   if (mContentViewer) {
     nsCOMPtr<nsIDocument> doc = mContentViewer->GetDocument();
     if (doc) {
       doc->WarnOnceAbout(nsIDocument::DeprecatedOperations(aWarning), aAsError);
     }
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -14,16 +14,17 @@
 #include "nsIBaseWindow.h"
 #include "nsINetworkInterceptController.h"
 #include "nsIScrollable.h"
 #include "nsITextScroll.h"
 #include "nsIContentViewerContainer.h"
 #include "nsIDOMStorageManager.h"
 #include "nsDocLoader.h"
 #include "mozilla/BasePrincipal.h"
+#include "mozilla/Maybe.h"
 #include "mozilla/Move.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/WeakPtr.h"
 #include "mozilla/TimeStamp.h"
 #include "GeckoProfiler.h"
 #include "mozilla/dom/ProfileTimelineMarkerBinding.h"
 #include "mozilla/LinkedList.h"
 #include "jsapi.h"
@@ -64,16 +65,18 @@
 #include "nsIDeprecationWarner.h"
 #include "nsILoadURIDelegate.h"
 
 namespace mozilla {
 class Encoding;
 class HTMLEditor;
 enum class TaskCategory;
 namespace dom {
+class ClientInfo;
+class ClientSource;
 class EventTarget;
 class PendingGlobalHistoryEntry;
 typedef uint32_t ScreenOrientationInternal;
 } // namespace dom
 } // namespace mozilla
 
 class nsDocShell;
 class nsDOMNavigationTiming;
@@ -818,16 +821,36 @@ protected:
 
   nsIChannel* GetCurrentDocChannel();
 
   bool ShouldBlockLoadingForBackButton();
 
   // Convenience method for getting our parent docshell. Can return null
   already_AddRefed<nsDocShell> GetParentDocshell();
 
+  // Possibly create a ClientSource object to represent an initial about:blank
+  // window that has not been allocated yet.  Normally we try not to create
+  // this about:blank window until something calls GetDocument().  We still need
+  // the ClientSource to exist for this conceptual window, though.
+  //
+  // The ClientSource is created with the given principal if specified.  If
+  // the principal is not provided we will attempt to inherit it when we
+  // are sure it will match what the real about:blank window principal
+  // would have been.  There are some corner cases where we cannot easily
+  // determine the correct principal and will not create the ClientSource.
+  // In these cases the initial about:blank will appear to not exist until
+  // its real document and window are created.
+  void MaybeCreateInitialClientSource(nsIPrincipal* aPrincipal = nullptr);
+
+  // Return the ClientInfo for the initial about:blank window, if it exists
+  // or we have speculatively created a ClientSource in
+  // MaybeCreateInitialClientSource().  This can return a ClientInfo object
+  // even if GetExtantDoc() returns nullptr.
+  mozilla::Maybe<mozilla::dom::ClientInfo> GetInitialClientInfo() const;
+
 protected:
   nsresult GetCurScrollPos(int32_t aScrollOrientation, int32_t* aCurPos);
   nsresult SetCurScrollPosEx(int32_t aCurHorizontalPos,
                              int32_t aCurVerticalPos);
 
   // Override the parent setter from nsDocLoader
   virtual nsresult SetDocLoaderParent(nsDocLoader* aLoader) override;
 
@@ -1145,16 +1168,18 @@ private:
   nsTObserverArray<nsWeakPtr> mReflowObservers;
   nsTObserverArray<nsWeakPtr> mScrollObservers;
   nsCString mOriginalUriString;
   nsWeakPtr mOpener;
   mozilla::OriginAttributes mOriginAttributes;
 
   mozilla::UniquePtr<mozilla::dom::PendingGlobalHistoryEntry> mPrerenderGlobalHistory;
 
+  mozilla::UniquePtr<mozilla::dom::ClientSource> mInitialClientSource;
+
   // A depth count of how many times NotifyRunToCompletionStart
   // has been called without a matching NotifyRunToCompletionStop.
   uint32_t mJSRunToCompletionDepth;
 
   // Whether or not touch events are overridden. Possible values are defined
   // as constants in the nsIDocShell.idl file.
   uint32_t mTouchEventsOverride;
 
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -7,34 +7,39 @@
 #include "domstubs.idl"
 #include "nsIDocShellTreeItem.idl"
 #include "nsIRequest.idl"
 
 %{ C++
 #include "js/TypeDecls.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/NotNull.h"
+#include "mozilla/UniquePtr.h"
 #include "nsCOMPtr.h"
 #include "nsIURI.h"
 class nsPresContext;
 class nsIPresShell;
 namespace mozilla {
 class Encoding;
 class HTMLEditor;
+namespace dom {
+class ClientSource;
+} // namespace dom
 }
 %}
 
 /**
  * The nsIDocShell interface.
  */
 
 [ptr] native nsPresContext(nsPresContext);
 [ptr] native nsIPresShell(nsIPresShell);
 [ref] native MaybeURI(mozilla::Maybe<nsCOMPtr<nsIURI>>);
 [ref] native Encoding(const mozilla::Encoding*);
+      native UniqueClientSource(mozilla::UniquePtr<mozilla::dom::ClientSource>);
 
 interface nsIURI;
 interface nsIChannel;
 interface nsIContentViewer;
 interface nsIDOMEventTarget;
 interface nsIDocShellLoadInfo;
 interface nsIEditor;
 interface nsIEditingSession;
@@ -1167,16 +1172,27 @@ interface nsIDocShell : nsIDocShellTreeI
    */
   attribute boolean useTrackingProtection;
 
  /**
   * Fire a dummy location change event asynchronously.
   */
   [noscript] void dispatchLocationChangeEvent();
 
+  /**
+   * Take ownership of the ClientSource representing an initial about:blank
+   * document that was never needed.  As an optimization we avoid creating
+   * this document if no code calls GetDocument(), but we still need a
+   * ClientSource object to represent the about:blank window.  This may return
+   * nullptr; for example if the docshell has created a real window and document
+   * already.
+   */
+  [noscript, nostdcall, notxpcom]
+  UniqueClientSource TakeInitialClientSource();
+
 %{C++
   /**
    * These methods call nsDocShell::GetHTMLEditorInternal() and
    * nsDocShell::SetHTMLEditorInternal() with static_cast.
    */
   mozilla::HTMLEditor* GetHTMLEditor();
   nsresult SetHTMLEditor(mozilla::HTMLEditor* aHTMLEditor);
 %}
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -1405,36 +1405,16 @@ Navigator::PublishServer(const nsAString
                    },
                    [domPromise] (nsresult aStatus) {
                      domPromise->MaybeReject(aStatus);
                    });
 
   return domPromise.forget();
 }
 
-already_AddRefed<WakeLock>
-Navigator::RequestWakeLock(const nsAString &aTopic, ErrorResult& aRv)
-{
-  if (!mWindow) {
-    aRv.Throw(NS_ERROR_UNEXPECTED);
-    return nullptr;
-  }
-
-  RefPtr<power::PowerManagerService> pmService =
-    power::PowerManagerService::GetInstance();
-  // Maybe it went away for some reason... Or maybe we're just called
-  // from our XPCOM method.
-  if (!pmService) {
-    aRv.Throw(NS_ERROR_UNEXPECTED);
-    return nullptr;
-  }
-
-  return pmService->NewWakeLock(aTopic, mWindow, aRv);
-}
-
 already_AddRefed<LegacyMozTCPSocket>
 Navigator::MozTCPSocket()
 {
   RefPtr<LegacyMozTCPSocket> socket = new LegacyMozTCPSocket(GetWindow());
   return socket.forget();
 }
 
 void
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -178,18 +178,16 @@ public:
   uint64_t HardwareConcurrency();
   bool CpuHasSSE2();
   bool TaintEnabled()
   {
     return false;
   }
   void AddIdleObserver(MozIdleObserver& aObserver, ErrorResult& aRv);
   void RemoveIdleObserver(MozIdleObserver& aObserver, ErrorResult& aRv);
-  already_AddRefed<WakeLock> RequestWakeLock(const nsAString &aTopic,
-                                             ErrorResult& aRv);
 
   DesktopNotificationCenter* GetMozNotification(ErrorResult& aRv);
   already_AddRefed<LegacyMozTCPSocket> MozTCPSocket();
   network::Connection* GetConnection(ErrorResult& aRv);
   MediaDevices* GetMediaDevices(ErrorResult& aRv);
 
   void GetGamepads(nsTArray<RefPtr<Gamepad> >& aGamepads, ErrorResult& aRv);
   GamepadServiceTest* RequestGamepadServiceTest();
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -1,14 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "mozilla/dom/ClientManager.h"
+#include "mozilla/dom/ClientSource.h"
+
 #define FORWARD_TO_OUTER(method, args, err_rval)                        \
   PR_BEGIN_MACRO                                                        \
   MOZ_RELEASE_ASSERT(IsInnerWindow());                                  \
   nsGlobalWindowOuter *outer = GetOuterWindowInternal();                \
   if (!HasActiveDocument()) {                                           \
     NS_WARNING(outer ?                                                  \
                "Inner window does not have active document." :          \
                "No outer window available!");                           \
@@ -1095,16 +1098,19 @@ nsGlobalWindowInner::FreeInnerObjects()
   DisableGamepadUpdates();
   mHasGamepad = false;
   mGamepads.Clear();
   DisableVRUpdates();
   mHasVREvents = false;
   mHasVRDisplayActivateEvents = false;
   mVRDisplays.Clear();
 
+  // This breaks a cycle between the window and the ClientSource object.
+  mClientSource.reset();
+
   if (mTabChild) {
     while (mBeforeUnloadListenerCount-- > 0) {
       mTabChild->BeforeUnloadRemoved();
     }
   }
 }
 
 //*****************************************************************************
@@ -1221,17 +1227,16 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSessionStorage)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mApplicationCache)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSuspendedDoc)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIndexedDB)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPrincipal)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTabChild)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDoc)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleService)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWakeLock)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleRequestExecutor)
   for (IdleRequest* request : tmp->mIdleRequestCallbacks) {
     cb.NoteNativeChild(request, NS_CYCLE_COLLECTION_PARTICIPANT(IdleRequest));
   }
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleObservers)
 
@@ -1313,17 +1318,16 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mApplicationCache)
   }
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSuspendedDoc)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mIndexedDB)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPrincipal)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mTabChild)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDoc)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleService)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mWakeLock)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleObservers)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mGamepads)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCacheStorage)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mVRDisplays)
 
   // Unlink stuff from nsPIDOMWindow
@@ -1349,16 +1353,18 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell)
 
   tmp->UnlinkHostObjectURIs();
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleRequestExecutor)
   tmp->DisableIdleCallbackRequests();
 
+  tmp->mClientSource.reset();
+
   if (tmp->IsChromeWindow()) {
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeFields.mBrowserDOMWindow)
     if (tmp->mChromeFields.mMessageManager) {
       static_cast<nsFrameMessageManager*>(
         tmp->mChromeFields.mMessageManager.get())->Disconnect();
       NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeFields.mMessageManager)
     }
     tmp->DisconnectAndClearGroupMessageManagers();
@@ -1514,16 +1520,91 @@ nsGlobalWindowInner::InnerSetNewDocument
 
   Telemetry::Accumulate(Telemetry::INNERWINDOWS_WITH_MUTATION_LISTENERS,
                         mMutationBits ? 1 : 0);
 
   // Clear our mutation bitfield.
   mMutationBits = 0;
 }
 
+nsresult
+nsGlobalWindowInner::EnsureClientSource()
+{
+  MOZ_DIAGNOSTIC_ASSERT(mDoc);
+
+  bool newClientSource = false;
+
+  nsCOMPtr<nsIChannel> channel = mDoc->GetChannel();
+  nsCOMPtr<nsILoadInfo> loadInfo = channel ? channel->GetLoadInfo() : nullptr;
+
+  // Try to get the reserved client from the LoadInfo.  A Client is
+  // reserved at the start of the channel load if there is not an
+  // initial about:blank document that will be reused.  It is also
+  // created if the channel load encounters a cross-origin redirect.
+  if (loadInfo) {
+    UniquePtr<ClientSource> reservedClient = loadInfo->TakeReservedClientSource();
+    if (reservedClient) {
+      mClientSource.reset();
+      mClientSource = Move(reservedClient);
+      newClientSource = true;
+    }
+  }
+
+  // We don't have a LoadInfo reserved client, but maybe we should
+  // be inheriting an initial one from the docshell.  This means
+  // that the docshell started the channel load before creating the
+  // initial about:blank document.  This is an optimization, though,
+  // and it created an initial Client as a placeholder for the document.
+  // In this case we want to inherit this placeholder Client here.
+  if (!mClientSource) {
+    nsIDocShell* docshell = GetDocShell();
+    if (docshell) {
+      mClientSource = docshell->TakeInitialClientSource();
+      if (mClientSource) {
+        newClientSource = true;
+      }
+    }
+  }
+
+  // If we don't have a reserved client or an initial client, then create
+  // one now.  This can happen in certain cases where we avoid preallocating
+  // the client in the docshell.  This mainly occurs in situations where
+  // the principal is not clearly inherited from the parent; e.g. sandboxed
+  // iframes, window.open(), etc.
+  if (!mClientSource) {
+    mClientSource = ClientManager::CreateSource(ClientType::Window,
+                                                EventTargetFor(TaskCategory::Other),
+                                                mDoc->NodePrincipal());
+    if (NS_WARN_IF(!mClientSource)) {
+      return NS_ERROR_FAILURE;
+    }
+    newClientSource = true;
+  }
+
+  // Its possible that we got a client just after being frozen in
+  // the bfcache.  In that case freeze the client immediately.
+  if (newClientSource && IsFrozen()) {
+    mClientSource->Freeze();
+  }
+
+  return NS_OK;
+}
+
+nsresult
+nsGlobalWindowInner::ExecutionReady()
+{
+  nsresult rv = EnsureClientSource();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = mClientSource->WindowExecutionReady(AsInner());
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
 void
 nsGlobalWindowInner::SetOpenerWindow(nsPIDOMWindowOuter* aOpener,
                                      bool aOriginalOpener)
 {
   FORWARD_TO_OUTER_VOID(SetOpenerWindow, (aOpener, aOriginalOpener));
 }
 
 void
@@ -2005,16 +2086,22 @@ nsPIDOMWindowInner::Thaw()
 }
 
 void
 nsPIDOMWindowInner::SyncStateFromParentWindow()
 {
   nsGlobalWindowInner::Cast(this)->SyncStateFromParentWindow();
 }
 
+Maybe<ClientInfo>
+nsPIDOMWindowInner::GetClientInfo() const
+{
+  return Move(nsGlobalWindowInner::Cast(this)->GetClientInfo());
+}
+
 void
 nsGlobalWindowInner::UpdateTopInnerWindow()
 {
   if (!IsInnerWindow() || IsTopInnerWindow() || !mTopInnerWindow) {
     return;
   }
 
   mTopInnerWindow->UpdateWebSocketCount(-(int32_t)mNumOfOpenWebSockets);
@@ -5982,16 +6069,19 @@ nsGlobalWindowInner::FreezeInternal()
   MOZ_ASSERT(mSuspendDepth >= mFreezeDepth);
   if (mFreezeDepth != 1) {
     return;
   }
 
   mozilla::dom::workers::FreezeWorkersForWindow(this);
 
   mTimeoutManager->Freeze();
+  if (mClientSource) {
+    mClientSource->Freeze();
+  }
 
   if (IsInnerWindow()) {
     NotifyDOMWindowFrozen(this);
   }
 }
 
 void
 nsGlobalWindowInner::Thaw()
@@ -6013,16 +6103,19 @@ nsGlobalWindowInner::ThawInternal()
 
   MOZ_ASSERT(mFreezeDepth != 0);
   mFreezeDepth -= 1;
   MOZ_ASSERT(mSuspendDepth >= mFreezeDepth);
   if (mFreezeDepth != 0) {
     return;
   }
 
+  if (mClientSource) {
+    mClientSource->Thaw();
+  }
   mTimeoutManager->Thaw();
 
   mozilla::dom::workers::ThawWorkersForWindow(this);
 
   if (IsInnerWindow()) {
     NotifyDOMWindowThawed(this);
   }
 }
@@ -6116,16 +6209,27 @@ nsGlobalWindowInner::CallOnChildren(Meth
     if (!mDoc || !frame || mDoc != frame->OwnerDoc() || !inner) {
       continue;
     }
 
     (inner->*aMethod)();
   }
 }
 
+Maybe<ClientInfo>
+nsGlobalWindowInner::GetClientInfo() const
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  Maybe<ClientInfo> clientInfo;
+  if (mClientSource) {
+    clientInfo.emplace(mClientSource->Info());
+  }
+  return Move(clientInfo);
+}
+
 nsresult
 nsGlobalWindowInner::FireDelayedDOMEvents()
 {
   if (mApplicationCache) {
     static_cast<nsDOMOfflineResourceList*>(mApplicationCache.get())->FirePendingEvents();
   }
 
   // Fires an offline status event if the offline status has changed
--- a/dom/base/nsGlobalWindowInner.h
+++ b/dom/base/nsGlobalWindowInner.h
@@ -92,16 +92,17 @@ class DialogValueHolder;
 
 namespace mozilla {
 class AbstractThread;
 class DOMEventTargetHelper;
 class ThrottledEventQueue;
 namespace dom {
 class BarProp;
 struct ChannelPixelLayout;
+class ClientSource;
 class Console;
 class Crypto;
 class CustomElementRegistry;
 class DocGroup;
 class External;
 class Function;
 class Gamepad;
 enum class ImageBitmapFormat : uint8_t;
@@ -366,16 +367,18 @@ public:
   void Suspend();
   void Resume();
   virtual bool IsSuspended() const override;
   void Freeze();
   void Thaw();
   virtual bool IsFrozen() const override;
   void SyncStateFromParentWindow();
 
+  mozilla::Maybe<mozilla::dom::ClientInfo> GetClientInfo() const;
+
   virtual nsresult FireDelayedDOMEvents() override;
 
   virtual nsresult SetNewDocument(nsIDocument *aDocument,
                                   nsISupports *aState,
                                   bool aForceReuseInnerWindow) override;
 
   virtual void SetOpenerWindow(nsPIDOMWindowOuter* aOpener,
                                bool aOriginalOpener) override;
@@ -1160,16 +1163,19 @@ protected:
 
   void FreeInnerObjects();
   nsGlobalWindowInner *CallerInnerWindow();
 
   // Only to be called on an inner window.
   // aDocument must not be null.
   void InnerSetNewDocument(JSContext* aCx, nsIDocument* aDocument);
 
+  nsresult EnsureClientSource();
+  nsresult ExecutionReady();
+
   // Inner windows only.
   nsresult DefineArgumentsProperty(nsIArray *aArguments);
 
   // Get the parent, returns null if this is a toplevel window
   nsPIDOMWindowOuter* GetParentInternal();
 
 public:
   // popup tracking
@@ -1565,16 +1571,18 @@ protected:
   // When non-zero, the document should receive a vrdisplayactivate event
   // after loading.  The value is the ID of the VRDisplay that content should
   // begin presentation on.
   uint32_t mAutoActivateVRDisplayID; // Outer windows only
   int64_t mBeforeUnloadListenerCount; // Inner windows only
 
   RefPtr<mozilla::dom::IntlUtils> mIntlUtils;
 
+  mozilla::UniquePtr<mozilla::dom::ClientSource> mClientSource;
+
   static InnerWindowByIdTable* sInnerWindowsById;
 
   // Members in the mChromeFields member should only be used in chrome windows.
   // All accesses to this field should be guarded by a check of mIsChrome.
   struct ChromeFields {
     ChromeFields()
       : mGroupMessageManagers(1)
     {}
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -1129,17 +1129,16 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSessionStorage)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mApplicationCache)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSuspendedDoc)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIndexedDB)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPrincipal)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTabChild)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDoc)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleService)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWakeLock)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleRequestExecutor)
   for (IdleRequest* request : tmp->mIdleRequestCallbacks) {
     cb.NoteNativeChild(request, NS_CYCLE_COLLECTION_PARTICIPANT(IdleRequest));
   }
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleObservers)
 
@@ -1214,17 +1213,16 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mApplicationCache)
   }
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSuspendedDoc)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mIndexedDB)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPrincipal)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mTabChild)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDoc)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleService)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mWakeLock)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleObservers)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mGamepads)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCacheStorage)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mVRDisplays)
 
   // Unlink stuff from nsPIDOMWindow
@@ -1927,24 +1925,32 @@ nsGlobalWindowOuter::SetNewDocument(nsID
         newInnerWindow->mDoc = aDocument;
 
         // The storage objects contain the URL of the window. We have to
         // recreate them when the innerWindow is reused.
         newInnerWindow->mLocalStorage = nullptr;
         newInnerWindow->mSessionStorage = nullptr;
 
         newInnerWindow->ClearDocumentDependentSlots(cx);
+
+        // When replacing an initial about:blank document we call
+        // ExecutionReady again to update the client creation URL.
+        rv = newInnerWindow->ExecutionReady();
+        NS_ENSURE_SUCCESS(rv, rv);
       }
     } else {
       newInnerWindow->InnerSetNewDocument(cx, aDocument);
 
       // Initialize DOM classes etc on the inner window.
       JS::Rooted<JSObject*> obj(cx, newInnerGlobal);
       rv = kungFuDeathGrip->InitClasses(obj);
       NS_ENSURE_SUCCESS(rv, rv);
+
+      rv = newInnerWindow->ExecutionReady();
+      NS_ENSURE_SUCCESS(rv, rv);
     }
 
     // If the document comes from a JAR, check if the channel was determined
     // to be unsafe. If so, permanently disable script on the compartment by
     // calling Block() and throwing away the key.
     nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(aDocument->GetChannel());
     if (jarChannel && jarChannel->GetIsUnsafe()) {
       xpc::Scriptability::Get(newInnerGlobal).Block();
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -41,16 +41,17 @@ class nsPIWindowRoot;
 class nsXBLPrototypeHandler;
 
 typedef uint32_t SuspendTypes;
 
 namespace mozilla {
 class ThrottledEventQueue;
 namespace dom {
 class AudioContext;
+class ClientInfo;
 class DocGroup;
 class TabGroup;
 class Element;
 class Performance;
 class ServiceWorkerRegistration;
 class Timeout;
 class TimeoutManager;
 class CustomElementRegistry;
@@ -926,16 +927,18 @@ public:
 
   // Increase/Decrease the number of open WebSockets.
   void UpdateWebSocketCount(int32_t aDelta);
 
   // Return true if there are any open WebSockets that could block
   // timeout-throttling.
   bool HasOpenWebSockets() const;
 
+  mozilla::Maybe<mozilla::dom::ClientInfo> GetClientInfo() const;
+
   mozilla::dom::TabGroup* TabGroup();
 protected:
   void CreatePerformanceObjectIfNeeded();
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsPIDOMWindowInner, NS_PIDOMWINDOWINNER_IID)
 
 // NB: It's very very important that these two classes have identical vtables
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -606,20 +606,16 @@ DOMInterfaces = {
     'nativeType': 'mozilla::dom::HTMLCanvasPrintState',
 },
 
 'MozChannel': {
     'nativeType': 'nsIChannel',
     'notflattened': True
 },
 
-'MozWakeLock': {
-    'nativeType': 'mozilla::dom::WakeLock',
-},
-
 'MozTimeManager': {
     'nativeType': 'mozilla::dom::time::TimeManager',
 },
 
 'MozStorageAsyncStatementParams': {
     'headerFile': 'mozilla/storage/mozStorageAsyncStatementParams.h',
     'nativeType': 'mozilla::storage::AsyncStatementParams',
 },
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/ClientChannelHelper.cpp
@@ -0,0 +1,256 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ClientChannelHelper.h"
+
+#include "ClientManager.h"
+#include "ClientSource.h"
+#include "MainThreadUtils.h"
+#include "mozilla/dom/ServiceWorkerDescriptor.h"
+#include "mozilla/ipc/BackgroundUtils.h"
+#include "nsContentUtils.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
+#include "nsIChannel.h"
+#include "nsIChannelEventSink.h"
+#include "nsIDocShell.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+
+namespace mozilla {
+namespace dom {
+
+using mozilla::ipc::PrincipalInfoToPrincipal;
+
+namespace {
+
+class ClientChannelHelper final : public nsIInterfaceRequestor
+                                , public nsIChannelEventSink
+{
+  nsCOMPtr<nsIInterfaceRequestor> mOuter;
+  nsCOMPtr<nsISerialEventTarget> mEventTarget;
+
+  ~ClientChannelHelper() = default;
+
+  NS_IMETHOD
+  GetInterface(const nsIID& aIID, void** aResultOut) override
+  {
+    if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
+      *aResultOut = static_cast<nsIChannelEventSink*>(this);
+      NS_ADDREF_THIS();
+      return NS_OK;
+    }
+
+    if (mOuter) {
+      return mOuter->GetInterface(aIID, aResultOut);
+    }
+
+    return NS_ERROR_NO_INTERFACE;
+  }
+
+  NS_IMETHOD
+  AsyncOnChannelRedirect(nsIChannel* aOldChannel,
+                         nsIChannel* aNewChannel,
+                         uint32_t aFlags,
+                         nsIAsyncVerifyRedirectCallback *aCallback) override
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    nsCOMPtr<nsILoadInfo> oldLoadInfo;
+    nsresult rv = aOldChannel->GetLoadInfo(getter_AddRefs(oldLoadInfo));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsILoadInfo> newLoadInfo;
+    rv = aNewChannel->GetLoadInfo(getter_AddRefs(newLoadInfo));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = nsContentUtils::CheckSameOrigin(aOldChannel, aNewChannel);
+    if (NS_WARN_IF(NS_FAILED(rv) && rv != NS_ERROR_DOM_BAD_URI)) {
+      return rv;
+    }
+
+    UniquePtr<ClientSource> reservedClient = oldLoadInfo->TakeReservedClientSource();
+
+    // If its a same-origin redirect we just move our reserved client to the
+    // new channel.
+    if (NS_SUCCEEDED(rv)) {
+      if (reservedClient) {
+        newLoadInfo->GiveReservedClientSource(Move(reservedClient));
+      }
+
+      // It seems sometimes necko passes two channels with the same LoadInfo.
+      // We only need to move the reserved/initial ClientInfo over if we
+      // actually have a different LoadInfo.
+      else if (oldLoadInfo != newLoadInfo) {
+        const Maybe<ClientInfo>& reservedClientInfo =
+          oldLoadInfo->GetReservedClientInfo();
+
+        const Maybe<ClientInfo>& initialClientInfo =
+          oldLoadInfo->GetInitialClientInfo();
+
+        MOZ_DIAGNOSTIC_ASSERT(reservedClientInfo.isNothing() ||
+                              initialClientInfo.isNothing());
+
+        if (reservedClientInfo.isSome()) {
+          newLoadInfo->SetReservedClientInfo(reservedClientInfo.ref());
+        }
+
+        if (initialClientInfo.isSome()) {
+          newLoadInfo->SetInitialClientInfo(initialClientInfo.ref());
+        }
+      }
+
+      // Make sure we keep the service worker controller on same-origin
+      // internal redirects.
+      if (oldLoadInfo != newLoadInfo &&
+          aFlags & nsIChannelEventSink::REDIRECT_INTERNAL) {
+        const Maybe<ServiceWorkerDescriptor>& controller = oldLoadInfo->GetController();
+        if (controller.isSome()) {
+          newLoadInfo->SetController(controller.ref());
+        }
+      }
+    }
+
+    // If it's a cross-origin redirect then we discard the old reserved client
+    // and create a new one.
+    else {
+      // If CheckSameOrigin() worked, then the security manager must exist.
+      nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
+      MOZ_DIAGNOSTIC_ASSERT(ssm);
+
+      nsCOMPtr<nsIPrincipal> principal;
+      rv = ssm->GetChannelResultPrincipal(aNewChannel, getter_AddRefs(principal));
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      // Create the new ClientSource.  This should only happen for window
+      // Clients since support cross-origin redirects are blocked by the
+      // same-origin security policy.
+      reservedClient.reset();
+      reservedClient = ClientManager::CreateSource(ClientType::Window,
+                                                   mEventTarget, principal);
+
+      newLoadInfo->GiveReservedClientSource(Move(reservedClient));
+    }
+
+    nsCOMPtr<nsIChannelEventSink> outerSink = do_GetInterface(mOuter);
+    if (outerSink) {
+      return outerSink->AsyncOnChannelRedirect(aOldChannel, aNewChannel, aFlags,
+                                               aCallback);
+    }
+
+    aCallback->OnRedirectVerifyCallback(NS_OK);
+    return NS_OK;
+  }
+
+public:
+  ClientChannelHelper(nsIInterfaceRequestor* aOuter,
+                      nsISerialEventTarget* aEventTarget)
+    : mOuter(aOuter)
+    , mEventTarget(aEventTarget)
+  {
+  }
+
+  NS_DECL_ISUPPORTS
+};
+
+NS_IMPL_ISUPPORTS(ClientChannelHelper, nsIInterfaceRequestor,
+                                       nsIChannelEventSink);
+
+} // anonymous namespace
+
+nsresult
+AddClientChannelHelper(nsIChannel* aChannel,
+                       Maybe<ClientInfo>&& aReservedClientInfo,
+                       Maybe<ClientInfo>&& aInitialClientInfo,
+                       nsISerialEventTarget* aEventTarget)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  Maybe<ClientInfo> initialClientInfo(Move(aInitialClientInfo));
+  Maybe<ClientInfo> reservedClientInfo(Move(aReservedClientInfo));
+  MOZ_DIAGNOSTIC_ASSERT(reservedClientInfo.isNothing() ||
+                        initialClientInfo.isNothing());
+
+  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
+  NS_ENSURE_TRUE(loadInfo, NS_ERROR_FAILURE);
+
+  nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
+  NS_ENSURE_TRUE(ssm, NS_ERROR_FAILURE);
+
+  nsCOMPtr<nsIPrincipal> channelPrincipal;
+  nsresult rv = ssm->GetChannelResultPrincipal(aChannel, getter_AddRefs(channelPrincipal));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Only allow the initial ClientInfo to be set if the current channel
+  // principal matches.
+  if (initialClientInfo.isSome()) {
+    nsCOMPtr<nsIPrincipal> initialPrincipal =
+      PrincipalInfoToPrincipal(initialClientInfo.ref().PrincipalInfo(), nullptr);
+
+    bool equals = false;
+    rv = initialPrincipal ? initialPrincipal->Equals(channelPrincipal, &equals)
+                          : NS_ERROR_FAILURE;
+    if (NS_FAILED(rv) || !equals) {
+      initialClientInfo.reset();
+    }
+  }
+
+  // Only allow the reserved ClientInfo to be set if the current channel
+  // principal matches.
+  if (reservedClientInfo.isSome()) {
+    nsCOMPtr<nsIPrincipal> reservedPrincipal =
+      PrincipalInfoToPrincipal(reservedClientInfo.ref().PrincipalInfo(), nullptr);
+
+    bool equals = false;
+    rv = reservedPrincipal ? reservedPrincipal->Equals(channelPrincipal, &equals)
+                           : NS_ERROR_FAILURE;
+    if (NS_FAILED(rv) || !equals) {
+      reservedClientInfo.reset();
+    }
+  }
+
+  nsCOMPtr<nsIInterfaceRequestor> outerCallbacks;
+  rv = aChannel->GetNotificationCallbacks(getter_AddRefs(outerCallbacks));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  UniquePtr<ClientSource> reservedClient;
+  if (initialClientInfo.isNothing() && reservedClientInfo.isNothing()) {
+    // Wait to reserve the client until we are reasonably sure this method
+    // will succeed.  We should only follow this path for window clients.
+    // Workers should always provide a reserved ClientInfo since their
+    // ClientSource object is owned by a different thread.
+    reservedClient = ClientManager::CreateSource(ClientType::Window,
+                                                 aEventTarget,
+                                                 channelPrincipal);
+    NS_ENSURE_TRUE(reservedClient, NS_ERROR_FAILURE);
+  }
+
+  RefPtr<ClientChannelHelper> helper =
+    new ClientChannelHelper(outerCallbacks, aEventTarget);
+
+  // Only set the callbacks helper if we are able to reserve the client
+  // successfully.
+  rv = aChannel->SetNotificationCallbacks(helper);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Finally preserve the various client values on the nsILoadInfo once the
+  // redirect helper has been added to the channel.
+  if (reservedClient) {
+    loadInfo->GiveReservedClientSource(Move(reservedClient));
+  }
+
+  if (initialClientInfo.isSome()) {
+    loadInfo->SetInitialClientInfo(initialClientInfo.ref());
+  }
+
+  if (reservedClientInfo.isSome()) {
+    loadInfo->SetReservedClientInfo(reservedClientInfo.ref());
+  }
+
+  return NS_OK;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/ClientChannelHelper.h
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _mozilla_dom_ClientChannelHelper_h
+#define _mozilla_dom_ClientChannelHelper_h
+
+#include "mozilla/Maybe.h"
+#include "nsError.h"
+
+class nsIChannel;
+class nsISerialEventTarget;
+
+namespace mozilla {
+namespace dom {
+
+class ClientInfo;
+
+// Attach a redirect listener to the given nsIChannel that will manage
+// the various client values on the channel's LoadInfo.  This will
+// properly handle creating a new ClientSource on cross-origin redirect
+// and propagate the current reserved/initial client on same-origin
+// redirect.
+nsresult
+AddClientChannelHelper(nsIChannel* aChannel,
+                       Maybe<ClientInfo>&& aReservedClientInfo,
+                       Maybe<ClientInfo>&& aInitialClientInfo,
+                       nsISerialEventTarget* aEventTarget);
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // _mozilla_dom_ClientChannelHelper_h
--- a/dom/clients/manager/ClientManagerService.cpp
+++ b/dom/clients/manager/ClientManagerService.cpp
@@ -121,17 +121,18 @@ ClientManagerService::FindSource(const n
   AssertIsOnBackgroundThread();
 
   auto entry = mSourceTable.Lookup(aID);
   if (!entry) {
     return nullptr;
   }
 
   ClientSourceParent* source = entry.Data();
-  if (!MatchPrincipalInfo(source->Info().PrincipalInfo(), aPrincipalInfo)) {
+  if (source->IsFrozen() ||
+      !MatchPrincipalInfo(source->Info().PrincipalInfo(), aPrincipalInfo)) {
     return nullptr;
   }
 
   return source;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/clients/manager/ClientSource.cpp
+++ b/dom/clients/manager/ClientSource.cpp
@@ -230,16 +230,32 @@ ClientSource::DocShellExecutionReady(nsI
 
   ClientSourceExecutionReadyArgs args(NS_LITERAL_CSTRING("about:blank"),
                                       frameType);
   ExecutionReady(args);
 
   return NS_OK;
 }
 
+void
+ClientSource::Freeze()
+{
+  MaybeExecute([](PClientSourceChild* aActor) {
+    aActor->SendFreeze();
+  });
+}
+
+void
+ClientSource::Thaw()
+{
+  MaybeExecute([](PClientSourceChild* aActor) {
+    aActor->SendThaw();
+  });
+}
+
 const ClientInfo&
 ClientSource::Info() const
 {
   return mClientInfo;
 }
 
 nsISerialEventTarget*
 ClientSource::EventTarget() const
--- a/dom/clients/manager/ClientSource.h
+++ b/dom/clients/manager/ClientSource.h
@@ -77,16 +77,22 @@ public:
   WorkerExecutionReady(mozilla::dom::workers::WorkerPrivate* aWorkerPrivate);
 
   nsresult
   WindowExecutionReady(nsPIDOMWindowInner* aInnerWindow);
 
   nsresult
   DocShellExecutionReady(nsIDocShell* aDocShell);
 
+  void
+  Freeze();
+
+  void
+  Thaw();
+
   const ClientInfo&
   Info() const;
 
   nsISerialEventTarget*
   EventTarget() const;
 };
 
 } // namespace dom
--- a/dom/clients/manager/ClientSourceParent.cpp
+++ b/dom/clients/manager/ClientSourceParent.cpp
@@ -106,16 +106,40 @@ ClientSourceParent::RecvExecutionReady(c
 
   for (ClientHandleParent* handle : mHandleList) {
     Unused << handle->SendExecutionReady(mClientInfo.ToIPC());
   }
 
   return IPC_OK();
 };
 
+IPCResult
+ClientSourceParent::RecvFreeze()
+{
+  MOZ_DIAGNOSTIC_ASSERT(!mFrozen);
+  mFrozen = true;
+
+  // Frozen clients should not be observable.  Act as if the client has
+  // been destroyed.
+  nsTArray<ClientHandleParent*> handleList(mHandleList);
+  for (ClientHandleParent* handle : handleList) {
+    Unused << ClientHandleParent::Send__delete__(handle);
+  }
+
+  return IPC_OK();
+}
+
+IPCResult
+ClientSourceParent::RecvThaw()
+{
+  MOZ_DIAGNOSTIC_ASSERT(mFrozen);
+  mFrozen = false;
+  return IPC_OK();
+}
+
 void
 ClientSourceParent::ActorDestroy(ActorDestroyReason aReason)
 {
   DebugOnly<bool> removed = mService->RemoveSource(this);
   MOZ_ASSERT(removed);
 
   nsTArray<ClientHandleParent*> handleList(mHandleList);
   for (ClientHandleParent* handle : handleList) {
@@ -139,16 +163,17 @@ ClientSourceParent::DeallocPClientSource
   delete aActor;
   return true;
 }
 
 ClientSourceParent::ClientSourceParent(const ClientSourceConstructorArgs& aArgs)
   : mClientInfo(aArgs.id(), aArgs.type(), aArgs.principalInfo(), aArgs.creationTime())
   , mService(ClientManagerService::GetOrCreateInstance())
   , mExecutionReady(false)
+  , mFrozen(false)
 {
 }
 
 ClientSourceParent::~ClientSourceParent()
 {
   MOZ_DIAGNOSTIC_ASSERT(mHandleList.IsEmpty());
 }
 
@@ -173,20 +198,27 @@ ClientSourceParent::Init()
 }
 
 const ClientInfo&
 ClientSourceParent::Info() const
 {
   return mClientInfo;
 }
 
+bool
+ClientSourceParent::IsFrozen() const
+{
+  return mFrozen;
+}
+
 void
 ClientSourceParent::AttachHandle(ClientHandleParent* aClientHandle)
 {
   MOZ_DIAGNOSTIC_ASSERT(aClientHandle);
+  MOZ_DIAGNOSTIC_ASSERT(!mFrozen);
   MOZ_ASSERT(!mHandleList.Contains(aClientHandle));
   mHandleList.AppendElement(aClientHandle);
 }
 
 void
 ClientSourceParent::DetachHandle(ClientHandleParent* aClientHandle)
 {
   MOZ_DIAGNOSTIC_ASSERT(aClientHandle);
--- a/dom/clients/manager/ClientSourceParent.h
+++ b/dom/clients/manager/ClientSourceParent.h
@@ -16,27 +16,34 @@ class ClientHandleParent;
 class ClientManagerService;
 
 class ClientSourceParent final : public PClientSourceParent
 {
   ClientInfo mClientInfo;
   RefPtr<ClientManagerService> mService;
   nsTArray<ClientHandleParent*> mHandleList;
   bool mExecutionReady;
+  bool mFrozen;
 
   void
   KillInvalidChild();
 
   // PClientSourceParent
   mozilla::ipc::IPCResult
   RecvTeardown() override;
 
   mozilla::ipc::IPCResult
   RecvExecutionReady(const ClientSourceExecutionReadyArgs& aArgs) override;
 
+  mozilla::ipc::IPCResult
+  RecvFreeze() override;
+
+  mozilla::ipc::IPCResult
+  RecvThaw() override;
+
   void
   ActorDestroy(ActorDestroyReason aReason) override;
 
   PClientSourceOpParent*
   AllocPClientSourceOpParent(const ClientOpConstructorArgs& aArgs) override;
 
   bool
   DeallocPClientSourceOpParent(PClientSourceOpParent* aActor) override;
@@ -46,16 +53,19 @@ public:
   ~ClientSourceParent();
 
   void
   Init();
 
   const ClientInfo&
   Info() const;
 
+  bool
+  IsFrozen() const;
+
   void
   AttachHandle(ClientHandleParent* aClientSource);
 
   void
   DetachHandle(ClientHandleParent* aClientSource);
 };
 
 } // namespace dom
--- a/dom/clients/manager/PClientSource.ipdl
+++ b/dom/clients/manager/PClientSource.ipdl
@@ -17,16 +17,18 @@ sync protocol PClientSource
 {
   manager PClientManager;
 
   manages PClientSourceOp;
 
 parent:
   async Teardown();
   async ExecutionReady(ClientSourceExecutionReadyArgs aArgs);
+  async Freeze();
+  async Thaw();
 
 child:
   async PClientSourceOp(ClientOpConstructorArgs aArgs);
 
   async __delete__();
 };
 
 } // namespace dom
--- a/dom/clients/manager/moz.build
+++ b/dom/clients/manager/moz.build
@@ -1,28 +1,30 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXPORTS.mozilla.dom += [
+  'ClientChannelHelper.h',
   'ClientHandle.h',
   'ClientInfo.h',
   'ClientIPCUtils.h',
   'ClientManager.h',
   'ClientManagerActors.h',
   'ClientOpenWindowOpActors.h',
   'ClientOpPromise.h',
   'ClientSource.h',
   'ClientState.h',
   'ClientThing.h',
 ]
 
 UNIFIED_SOURCES += [
+  'ClientChannelHelper.cpp',
   'ClientHandle.cpp',
   'ClientHandleChild.cpp',
   'ClientHandleOpChild.cpp',
   'ClientHandleOpParent.cpp',
   'ClientHandleParent.cpp',
   'ClientInfo.cpp',
   'ClientManager.cpp',
   'ClientManagerActors.cpp',
--- a/dom/events/IMEStateManager.cpp
+++ b/dom/events/IMEStateManager.cpp
@@ -1274,16 +1274,19 @@ IMEStateManager::SetIMEState(const IMESt
   NS_ENSURE_TRUE_VOID(aWidget);
 
   InputContext context;
   context.mIMEState = aState;
   context.mOrigin = aOrigin;
   context.mMayBeIMEUnaware = context.mIMEState.IsEditable() &&
     sCheckForIMEUnawareWebApps && MayBeIMEUnawareWebApp(aContent);
 
+  context.mHasHandledUserInput =
+    aPresContext && aPresContext->PresShell()->HasHandledUserInput();
+
   context.mInPrivateBrowsing =
     aPresContext &&
     nsContentUtils::IsInPrivateBrowsing(aPresContext->Document());
 
   if (aContent &&
       aContent->IsAnyOfHTMLElements(nsGkAtoms::input, nsGkAtoms::textarea)) {
     if (!aContent->IsHTMLElement(nsGkAtoms::textarea)) {
       // <input type=number> has an anonymous <input type=text> descendant
--- a/dom/fetch/FetchConsumer.cpp
+++ b/dom/fetch/FetchConsumer.cpp
@@ -468,17 +468,17 @@ FetchBodyConsumer<Derived>::BeginConsume
   if (mShuttingDown) {
     // We haven't started yet, but we have been terminated. AutoFailConsumeBody
     // will dispatch a runnable to release resources.
     return;
   }
 
   nsCOMPtr<nsIInputStreamPump> pump;
   nsresult rv = NS_NewInputStreamPump(getter_AddRefs(pump),
-                                      mBodyStream, 0, 0, false,
+                                      mBodyStream.forget(), 0, 0, false,
                                       mMainThreadEventTarget);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
   }
 
   RefPtr<ConsumeBodyDoneObserver<Derived>> p =
    new ConsumeBodyDoneObserver<Derived>(this);
 
--- a/dom/fetch/FetchConsumer.h
+++ b/dom/fetch/FetchConsumer.h
@@ -98,17 +98,19 @@ private:
   nsCOMPtr<nsIThread> mTargetThread;
   nsCOMPtr<nsIEventTarget> mMainThreadEventTarget;
 
 #ifdef DEBUG
   // This is used only to check if the body has been correctly consumed.
   RefPtr<FetchBody<Derived>> mBody;
 #endif
 
+  // This is nullified when the consuming of the body starts.
   nsCOMPtr<nsIInputStream> mBodyStream;
+
   MutableBlobStorage::MutableBlobStorageType mBlobStorageType;
   nsCString mBodyMimeType;
 
   // Set when consuming the body is attempted on a worker.
   // Unset when consumption is done/aborted.
   // This WorkerHolder keeps alive the consumer via a cycle.
   UniquePtr<workers::WorkerHolder> mWorkerHolder;
 
--- a/dom/file/nsHostObjectProtocolHandler.cpp
+++ b/dom/file/nsHostObjectProtocolHandler.cpp
@@ -843,17 +843,17 @@ nsHostObjectProtocolHandler::NewChannel2
   }
 
   nsAutoString contentType;
   blobImpl->GetType(contentType);
 
   nsCOMPtr<nsIChannel> channel;
   rv = NS_NewInputStreamChannelInternal(getter_AddRefs(channel),
                                         uri,
-                                        stream,
+                                        stream.forget(),
                                         NS_ConvertUTF16toUTF8(contentType),
                                         EmptyCString(), // aContentCharset
                                         aLoadInfo);
   if (NS_WARN_IF(rv.Failed())) {
     return rv.StealNSResult();
   }
 
   if (blobImpl->IsFile()) {
--- a/dom/jsurl/nsJSProtocolHandler.cpp
+++ b/dom/jsurl/nsJSProtocolHandler.cpp
@@ -405,19 +405,20 @@ nsresult nsJSChannel::Init(nsIURI* aURI,
 
     // Create the nsIStreamIO layer used by the nsIStreamIOChannel.
     mIOThunk = new nsJSThunk();
 
     // Create a stock input stream channel...
     // Remember, until AsyncOpen is called, the script will not be evaluated
     // and the underlying Input Stream will not be created...
     nsCOMPtr<nsIChannel> channel;
+    RefPtr<nsJSThunk> thunk = mIOThunk;
     rv = NS_NewInputStreamChannelInternal(getter_AddRefs(channel),
                                           aURI,
-                                          mIOThunk,
+                                          thunk.forget(),
                                           NS_LITERAL_CSTRING("text/html"),
                                           EmptyCString(),
                                           aLoadInfo);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = mIOThunk->Init(aURI);
     if (NS_SUCCEEDED(rv)) {
         mStreamChannel = channel;
--- a/dom/media/tests/mochitest/mochitest.ini
+++ b/dom/media/tests/mochitest/mochitest.ini
@@ -98,29 +98,29 @@ skip-if = toolkit == 'android' # no scre
 skip-if = android_version == '18' # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_audioCodecs.html]
 skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_basicAudio.html]
 skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_checkPacketDumpHook.html]
 skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_basicAudioNATSrflx.html]
-skip-if = toolkit == 'android' || (os == 'linux' && debug) # websockets don't work on android (bug 1266217), linux hang (bug 1339568)
+skip-if = toolkit == 'android' || (os == 'linux' && (debug || asan)) # websockets don't work on android (bug 1266217), linux hang (bug 1339568)
 [test_peerConnection_basicAudioNATRelay.html]
-skip-if = toolkit == 'android' || (os == 'linux' && debug) # websockets don't work on android (bug 1266217), linux hang (bug 1339568)
+skip-if = toolkit == 'android' || (os == 'linux' && (debug || asan)) # websockets don't work on android (bug 1266217), linux hang (bug 1339568)
 [test_peerConnection_basicAudioNATRelayTCP.html]
-skip-if = toolkit == 'android' || (os == 'linux' && debug) # websockets don't work on android (bug 1266217), linux hang (bug 1339568)
+skip-if = toolkit == 'android' || (os == 'linux' && (debug || asan)) # websockets don't work on android (bug 1266217), linux hang (bug 1339568)
 [test_peerConnection_basicAudioNATRelayTLS.html]
 skip-if = true # need pyopenssl on builders, see bug 1323439 # toolkit == 'android' # websockets don't work on android (bug 1266217)
 [test_peerConnection_basicAudioRequireEOC.html]
 skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_basicAudioPcmaPcmuOnly.html]
 skip-if = android_version == '18'
 [test_peerConnection_basicAudioDynamicPtMissingRtpmap.html]
-skip-if = (android_version == '18') || (os == 'linux' && debug) # android(Bug 1189784, timeouts on 4.3 emulator), linux hang (bug 1339568)
+skip-if = (android_version == '18') || (os == 'linux' && (debug || asan)) # android(Bug 1189784, timeouts on 4.3 emulator), linux hang (bug 1339568)
 [test_peerConnection_basicAudioVerifyRtpHeaderExtensions.html]
 skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_basicAudioVideo.html]
 skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_basicAudioVideoCombined.html]
 skip-if = toolkit == 'android'  # Bug 1189784
 [test_peerConnection_basicAudioVideoNoBundle.html]
 skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
--- a/dom/power/WakeLock.cpp
+++ b/dom/power/WakeLock.cpp
@@ -2,63 +2,53 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WakeLock.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/Event.h" // for nsIDOMEvent::InternalDOMEvent()
-#include "mozilla/dom/MozWakeLockBinding.h"
 #include "mozilla/Hal.h"
 #include "mozilla/HalWakeLock.h"
 #include "nsError.h"
 #include "nsIDocument.h"
 #include "nsIDOMWindow.h"
 #include "nsIDOMEvent.h"
 #include "nsPIDOMWindow.h"
 #include "nsIPropertyBag2.h"
 
 using namespace mozilla::hal;
 
 namespace mozilla {
 namespace dom {
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WakeLock)
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WakeLock)
-  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+NS_INTERFACE_MAP_BEGIN(WakeLock)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMEventListener)
   NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
 NS_INTERFACE_MAP_END
 
-NS_IMPL_CYCLE_COLLECTING_ADDREF(WakeLock)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(WakeLock)
+NS_IMPL_ADDREF(WakeLock)
+NS_IMPL_RELEASE(WakeLock)
 
 WakeLock::WakeLock()
   : mLocked(false)
   , mHidden(true)
   , mContentParentID(CONTENT_PROCESS_ID_UNKNOWN)
 {
 }
 
 WakeLock::~WakeLock()
 {
   DoUnlock();
   DetachEventListener();
 }
 
-JSObject*
-WakeLock::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
-{
-  return MozWakeLockBinding::Wrap(aCx, this, aGivenProto);
-}
-
 nsresult
 WakeLock::Init(const nsAString &aTopic, nsPIDOMWindowInner* aWindow)
 {
   // Don't Init() a WakeLock twice.
   MOZ_ASSERT(mTopic.IsEmpty());
 
   if (aTopic.IsEmpty()) {
     return NS_ERROR_INVALID_ARG;
--- a/dom/power/WakeLock.h
+++ b/dom/power/WakeLock.h
@@ -19,26 +19,24 @@ class nsPIDOMWindowInner;
 
 namespace mozilla {
 namespace dom {
 
 class ContentParent;
 
 class WakeLock final
   : public nsIDOMEventListener
-  , public nsWrapperCache
   , public nsIObserver
   , public nsSupportsWeakReference
 {
 public:
   NS_DECL_NSIDOMEVENTLISTENER
   NS_DECL_NSIOBSERVER
 
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(WakeLock, nsIDOMEventListener)
+  NS_DECL_ISUPPORTS
 
   // Note: WakeLock lives for the lifetime of the document in order to avoid
   // exposing GC behavior to pages. This means that
   // |var foo = navigator.requestWakeLock('cpu'); foo = null;|
   // doesn't unlock the 'cpu' resource.
 
   WakeLock();
 
@@ -51,19 +49,16 @@ public:
   // dies, the lock is released.  A wake lock initialized via this method is
   // always considered visible.
   nsresult Init(const nsAString &aTopic, ContentParent* aContentParent);
 
   // WebIDL methods
 
   nsPIDOMWindowInner* GetParentObject() const;
 
-  virtual JSObject*
-  WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
-
   void GetTopic(nsAString& aTopic);
 
   void Unlock(ErrorResult& aRv);
 
 private:
   virtual ~WakeLock();
 
   void     DoUnlock();
--- a/dom/power/moz.build
+++ b/dom/power/moz.build
@@ -26,10 +26,8 @@ EXPORTS.mozilla.dom.power += [
 UNIFIED_SOURCES += [
     'PowerManagerService.cpp',
     'WakeLock.cpp',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
-
-MOCHITEST_MANIFESTS += ['test/mochitest.ini']
deleted file mode 100644
--- a/dom/power/test/mochitest.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[test_bug957893.html]
-[test_bug957899.html]
-support-files = test_bug957899_iframe.html
-[test_wakelock_not_exposed.html]
deleted file mode 100644
--- a/dom/power/test/test_bug957893.html
+++ /dev/null
@@ -1,24 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Test bug 957893 - Crash in WakeLock</title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-</head>
-<body>
- <script type="application/javascript">
-  SimpleTest.waitForExplicitFinish();
-  function test() {
-    try {
-      var wl = navigator.requestWakeLock('');
-      ok(false, "RequestWakeLock throws an exception!");
-    } catch(e) {
-      ok(true, "RequestWakeLock throws an exception!");
-    }
-    info("Still alive!");
-    SimpleTest.finish();
-  }
-  SpecialPowers.pushPrefEnv({"set": [["dom.wakelock.enabled", true]]}, test);
- </script>
-</body>
-</html>
deleted file mode 100644
--- a/dom/power/test/test_bug957899.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Test bug 957899 - Crash in WakeLock</title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-</head>
-<body>
- <script type="application/javascript">
-  SimpleTest.waitForExplicitFinish();
-  // This test loads in an iframe, to ensure that the navigator instance is
-  // loaded with the correct value of the preference.
-  SpecialPowers.pushPrefEnv({"set": [["dom.wakelock.enabled", true]]}, () => {
-    let iframe = document.createElement("iframe");
-    iframe.id = "f1";
-    iframe.src = "test_bug957899_iframe.html";
-    document.body.appendChild(iframe);
-  });
- </script>
-</body>
-</html>
deleted file mode 100644
--- a/dom/power/test/test_bug957899_iframe.html
+++ /dev/null
@@ -1,16 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Test bug 957899 - Crash in WakeLock</title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-</head>
-<body>
- <script type="application/javascript">
-  var wl = navigator.requestWakeLock('a');
-  window.parent.ok(wl, "WakeLock created!");
-  window.parent.ok(!(wl instanceof XPathEvaluator), "Crashing?");
-  window.parent.SimpleTest.finish();
- </script>
-</body>
-</html>
deleted file mode 100644
--- a/dom/power/test/test_wakelock_not_exposed.html
+++ /dev/null
@@ -1,17 +0,0 @@
-<!--
-    https://bugzilla.mozilla.org/show_bug.cgi?id=963366
--->
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Test navigator.requestWakeLock is not exposed to non-B2G platform</title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-</head>
-<body>
- <script type="application/javascript">
-   ok(navigator.requestWakeLock === undefined,
-      "navigator.requestWakeLock is not exposed to non-B2G platform");
- </script>
-</body>
-</html>
deleted file mode 100644
--- a/dom/webauthn/u2f-hid-rs/src/khmatcher.rs
+++ /dev/null
@@ -1,156 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-use std::collections::{HashMap, HashSet};
-use std::collections::hash_map::IterMut;
-use std::hash::Hash;
-
-// The KeyHandleMatcher is a helper class that tracks which key handles belong
-// to which device (token). It is initialized with a list of key handles we
-// were passed by the U2F or WebAuthn API.
-//
-// 'a = the lifetime of key handles, we don't want to own them
-//  K = the type we use to identify devices on this platform (path, device_ref)
-pub struct KeyHandleMatcher<'a, K> {
-    key_handles: &'a Vec<Vec<u8>>,
-    map: HashMap<K, Vec<&'a Vec<u8>>>,
-}
-
-impl<'a, K> KeyHandleMatcher<'a, K>
-where
-    K: Clone + Eq + Hash,
-{
-    pub fn new(key_handles: &'a Vec<Vec<u8>>) -> Self {
-        Self {
-            key_handles,
-            map: HashMap::new(),
-        }
-    }
-
-    // `update()` will iterate all devices and ignore the ones we've already
-    // checked. It will then call the given closure exactly once for each key
-    // handle and device combination to let the caller decide whether we have
-    // a match.
-    //
-    // If a device was removed since the last `update()` call we'll remove it
-    // from the internal map as well to ensure we query a device that reuses a
-    // dev_ref or path next time.
-    //
-    // TODO In theory, it might be possible to replace a token between
-    // `update()` calls while reusing the device_ref/path. We should refactor
-    // this part of the code and probably merge the KeyHandleMatcher into the
-    // DeviceMap and Monitor somehow so that we query key handle/token
-    // assignments right when we start tracking a new device.
-    pub fn update<F, V>(&mut self, devices: IterMut<K, V>, is_match: F)
-    where
-        F: Fn(&mut V, &Vec<u8>) -> bool,
-    {
-        // Collect all currently known device references.
-        let mut to_remove: HashSet<K> = self.map.keys().cloned().collect();
-
-        for (dev_ref, device) in devices {
-            // This device is still connected.
-            to_remove.remove(dev_ref);
-
-            // Skip devices we've already seen.
-            if self.map.contains_key(dev_ref) {
-                continue;
-            }
-
-            let mut matches = vec![];
-
-            // Collect all matching key handles.
-            for key_handle in self.key_handles {
-                if is_match(device, key_handle) {
-                    matches.push(key_handle);
-                }
-            }
-
-            self.map.insert((*dev_ref).clone(), matches);
-        }
-
-        // Remove devices that disappeared since the last call.
-        for dev_ref in to_remove {
-            self.map.remove(&dev_ref);
-        }
-    }
-
-    // `get()` allows retrieving key handle/token assignments that were
-    // process by calls to `update()` before.
-    pub fn get(&self, dev_ref: &K) -> &Vec<&'a Vec<u8>> {
-        self.map.get(dev_ref).expect("unknown device")
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::KeyHandleMatcher;
-
-    use std::collections::HashMap;
-
-    #[test]
-    fn test() {
-        // Three key handles.
-        let khs = vec![
-            vec![0x01, 0x02, 0x03, 0x04],
-            vec![0x02, 0x03, 0x04, 0x05],
-            vec![0x03, 0x04, 0x05, 0x06],
-        ];
-
-        // Start with three devices.
-        let mut map = HashMap::new();
-        map.insert("device1", 1);
-        map.insert("device2", 2);
-        map.insert("device3", 3);
-
-        // Assign key handles to devices.
-        let mut khm = KeyHandleMatcher::new(&khs);
-        khm.update(map.iter_mut(), |device, key_handle| *device > key_handle[0]);
-
-        // Check assignments.
-        assert!(khm.get(&"device1").is_empty());
-        assert_eq!(*khm.get(&"device2"), vec![&vec![0x01, 0x02, 0x03, 0x04]]);
-        assert_eq!(
-            *khm.get(&"device3"),
-            vec![&vec![0x01, 0x02, 0x03, 0x04], &vec![0x02, 0x03, 0x04, 0x05]]
-        );
-
-        // Check we don't check a device twice.
-        map.insert("device4", 4);
-        khm.update(map.iter_mut(), |device, key_handle| {
-            assert_eq!(*device, 4);
-            key_handle[0] & 1 == 1
-        });
-
-        // Check assignments.
-        assert_eq!(
-            *khm.get(&"device4"),
-            vec![&vec![0x01, 0x02, 0x03, 0x04], &vec![0x03, 0x04, 0x05, 0x06]]
-        );
-
-        // Remove device #2.
-        map.remove("device2");
-        khm.update(map.iter_mut(), |_, _| {
-            assert!(false);
-            false
-        });
-
-        // Re-insert device #2 matching different key handles.
-        map.insert("device2", 2);
-        khm.update(map.iter_mut(), |device, _| {
-            assert_eq!(*device, 2);
-            true
-        });
-
-        // Check assignments.
-        assert_eq!(
-            *khm.get(&"device2"),
-            vec![
-                &vec![0x01, 0x02, 0x03, 0x04],
-                &vec![0x02, 0x03, 0x04, 0x05],
-                &vec![0x03, 0x04, 0x05, 0x06],
-            ]
-        );
-    }
-}
--- a/dom/webauthn/u2f-hid-rs/src/lib.rs
+++ b/dom/webauthn/u2f-hid-rs/src/lib.rs
@@ -22,19 +22,16 @@ pub mod platform;
 #[cfg(any(target_os = "windows"))]
 #[path = "windows/mod.rs"]
 pub mod platform;
 
 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
 #[path = "stub/mod.rs"]
 pub mod platform;
 
-#[cfg(target_os = "windows")]
-mod khmatcher;
-
 #[macro_use]
 extern crate log;
 extern crate rand;
 extern crate libc;
 extern crate boxfnonce;
 extern crate runloop;
 
 mod consts;
deleted file mode 100644
--- a/dom/webauthn/u2f-hid-rs/src/linux/devicemap.rs
+++ /dev/null
@@ -1,50 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-use std::collections::hash_map::IterMut;
-use std::collections::HashMap;
-use std::ffi::OsString;
-
-use platform::device::Device;
-use platform::monitor::Event;
-use u2fprotocol::u2f_init_device;
-
-pub struct DeviceMap {
-    map: HashMap<OsString, Device>,
-}
-
-impl DeviceMap {
-    pub fn new() -> Self {
-        Self { map: HashMap::new() }
-    }
-
-    pub fn iter_mut(&mut self) -> IterMut<OsString, Device> {
-        self.map.iter_mut()
-    }
-
-    pub fn process_event(&mut self, event: Event) {
-        match event {
-            Event::Add(path) => self.add(path),
-            Event::Remove(path) => self.remove(path),
-        }
-    }
-
-    fn add(&mut self, path: OsString) {
-        if self.map.contains_key(&path) {
-            return;
-        }
-
-        // Create and try to open the device.
-        if let Ok(mut dev) = Device::new(path.clone()) {
-            if dev.is_u2f() && u2f_init_device(&mut dev) {
-                self.map.insert(path, dev);
-            }
-        }
-    }
-
-    fn remove(&mut self, path: OsString) {
-        // Ignore errors.
-        let _ = self.map.remove(&path);
-    }
-}
deleted file mode 100644
--- a/dom/webauthn/u2f-hid-rs/src/macos/devicemap.rs
+++ /dev/null
@@ -1,51 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-use std::collections::hash_map::IterMut;
-use std::collections::HashMap;
-
-use u2fprotocol::u2f_init_device;
-
-use platform::monitor::Event;
-use platform::device::Device;
-use platform::iokit::*;
-
-pub struct DeviceMap {
-    map: HashMap<IOHIDDeviceRef, Device>,
-}
-
-impl DeviceMap {
-    pub fn new() -> Self {
-        Self { map: HashMap::new() }
-    }
-
-    pub fn iter_mut(&mut self) -> IterMut<IOHIDDeviceRef, Device> {
-        self.map.iter_mut()
-    }
-
-    pub fn process_event(&mut self, event: Event) {
-        match event {
-            Event::Add(dev) => self.add(dev),
-            Event::Remove(dev) => self.remove(dev),
-        }
-    }
-
-    fn add(&mut self, device_ref: IOHIDDeviceRef) {
-        if self.map.contains_key(&device_ref) {
-            return;
-        }
-
-        // Create the device.
-        let mut dev = Device::new(device_ref);
-
-        if u2f_init_device(&mut dev) {
-            self.map.insert(device_ref, dev);
-        }
-    }
-
-    fn remove(&mut self, device_ref: IOHIDDeviceRef) {
-        // Ignore errors.
-        let _ = self.map.remove(&device_ref);
-    }
-}
deleted file mode 100644
--- a/dom/webauthn/u2f-hid-rs/src/windows/devicemap.rs
+++ /dev/null
@@ -1,49 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-use std::collections::hash_map::IterMut;
-use std::collections::HashMap;
-
-use platform::device::Device;
-use platform::monitor::Event;
-use u2fprotocol::u2f_init_device;
-
-pub struct DeviceMap {
-    map: HashMap<String, Device>,
-}
-
-impl DeviceMap {
-    pub fn new() -> Self {
-        Self { map: HashMap::new() }
-    }
-
-    pub fn iter_mut(&mut self) -> IterMut<String, Device> {
-        self.map.iter_mut()
-    }
-
-    pub fn process_event(&mut self, event: Event) {
-        match event {
-            Event::Add(path) => self.add(path),
-            Event::Remove(path) => self.remove(path),
-        }
-    }
-
-    fn add(&mut self, path: String) {
-        if self.map.contains_key(&path) {
-            return;
-        }
-
-        // Create and try to open the device.
-        if let Ok(mut dev) = Device::new(path.clone()) {
-            if dev.is_u2f() && u2f_init_device(&mut dev) {
-                self.map.insert(path, dev);
-            }
-        }
-    }
-
-    fn remove(&mut self, path: String) {
-        // Ignore errors.
-        let _ = self.map.remove(&path);
-    }
-}
--- a/dom/webauthn/u2f-hid-rs/src/windows/mod.rs
+++ b/dom/webauthn/u2f-hid-rs/src/windows/mod.rs
@@ -1,170 +1,149 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use std::thread;
 use std::time::Duration;
 
 mod device;
-mod devicemap;
 mod monitor;
+mod transaction;
 mod winapi;
 
 use consts::PARAMETER_SIZE;
-use khmatcher::KeyHandleMatcher;
-use runloop::RunLoop;
+use platform::device::Device;
+use platform::transaction::Transaction;
 use util::{io_err, OnceCallback};
-use u2fprotocol::{u2f_register, u2f_sign, u2f_is_keyhandle_valid};
+use u2fprotocol::{u2f_init_device, u2f_register, u2f_sign, u2f_is_keyhandle_valid};
 
-use self::devicemap::DeviceMap;
-use self::monitor::Monitor;
-
+#[derive(Default)]
 pub struct PlatformManager {
-    // Handle to the thread loop.
-    thread: Option<RunLoop>,
+    transaction: Option<Transaction>,
 }
 
 impl PlatformManager {
     pub fn new() -> Self {
-        Self { thread: None }
+        Default::default()
     }
 
     pub fn register(
         &mut self,
         timeout: u64,
         challenge: Vec<u8>,
         application: Vec<u8>,
         key_handles: Vec<Vec<u8>>,
         callback: OnceCallback<Vec<u8>>,
     ) {
         // Abort any prior register/sign calls.
         self.cancel();
 
         let cbc = callback.clone();
 
-        let thread = RunLoop::new_with_timeout(
-            move |alive| {
-                let mut devices = DeviceMap::new();
-                let monitor = try_or!(Monitor::new(), |e| callback.call(Err(e)));
-                let mut matches = KeyHandleMatcher::new(&key_handles);
+        let transaction = Transaction::new(timeout, cbc.clone(), move |path, alive| {
+            // Create a new device.
+            let dev = &mut match Device::new(path) {
+                Ok(dev) => dev,
+                _ => return,
+            };
 
-                while alive() && monitor.alive() {
-                    // Add/remove devices.
-                    for event in monitor.events() {
-                        devices.process_event(event);
-                    }
+            // Try initializing it.
+            if !dev.is_u2f() || !u2f_init_device(dev) {
+                return;
+            }
 
-                    // Query newly added devices.
-                    matches.update(devices.iter_mut(), |device, key_handle| {
-                        u2f_is_keyhandle_valid(device, &challenge, &application, key_handle)
-                            .unwrap_or(false /* no match on failure */)
-                    });
+            // Iterate the exclude list and see if there are any matches.
+            // Abort the state machine if we found a valid key handle.
+            if key_handles.iter().any(|key_handle| {
+                u2f_is_keyhandle_valid(dev, &challenge, &application, key_handle)
+                    .unwrap_or(false) /* no match on failure */
+            })
+            {
+                return;
+            }
 
-                    // Iterate all devices that don't match any of the handles
-                    // in the exclusion list and try to register.
-                    for (path, device) in devices.iter_mut() {
-                        if matches.get(path).is_empty() {
-                            if let Ok(bytes) = u2f_register(device, &challenge, &application) {
-                                callback.call(Ok(bytes));
-                                return;
-                            }
-                        }
-                    }
-
-                    // Wait a little before trying again.
-                    thread::sleep(Duration::from_millis(100));
+            while alive() {
+                if let Ok(bytes) = u2f_register(dev, &challenge, &application) {
+                    callback.call(Ok(bytes));
+                    break;
                 }
 
-                callback.call(Err(io_err("aborted or timed out")));
-            },
-            timeout,
-        );
+                // Sleep a bit before trying again.
+                thread::sleep(Duration::from_millis(100));
+            }
+        });
 
-        self.thread = Some(try_or!(thread, |_| {
-            cbc.call(Err(io_err("couldn't create runloop")));
+        self.transaction = Some(try_or!(transaction, |_| {
+            cbc.call(Err(io_err("couldn't create transaction")));
         }));
     }
 
     pub fn sign(
         &mut self,
         timeout: u64,
         challenge: Vec<u8>,
         application: Vec<u8>,
         key_handles: Vec<Vec<u8>>,
         callback: OnceCallback<(Vec<u8>, Vec<u8>)>,
     ) {
         // Abort any prior register/sign calls.
         self.cancel();
 
         let cbc = callback.clone();
 
-        let thread = RunLoop::new_with_timeout(
-            move |alive| {
-                let mut devices = DeviceMap::new();
-                let monitor = try_or!(Monitor::new(), |e| callback.call(Err(e)));
-                let mut matches = KeyHandleMatcher::new(&key_handles);
+        let transaction = Transaction::new(timeout, cbc.clone(), move |path, alive| {
+            // Create a new device.
+            let dev = &mut match Device::new(path) {
+                Ok(dev) => dev,
+                _ => return,
+            };
 
-                while alive() && monitor.alive() {
-                    // Add/remove devices.
-                    for event in monitor.events() {
-                        devices.process_event(event);
-                    }
-
-                    // Query newly added devices.
-                    matches.update(devices.iter_mut(), |device, key_handle| {
-                        u2f_is_keyhandle_valid(device, &challenge, &application, key_handle)
-                            .unwrap_or(false /* no match on failure */)
-                    });
-
-                    // Iterate all devices.
-                    for (path, device) in devices.iter_mut() {
-                        let key_handles = matches.get(path);
+            // Try initializing it.
+            if !dev.is_u2f() || !u2f_init_device(dev) {
+                return;
+            }
 
-                        // If the device matches none of the given key handles
-                        // then just make it blink with bogus data.
-                        if key_handles.is_empty() {
-                            let blank = vec![0u8; PARAMETER_SIZE];
-                            if let Ok(_) = u2f_register(device, &blank, &blank) {
-                                callback.call(Err(io_err("invalid key")));
-                                return;
-                            }
-
-                            continue;
-                        }
+            // Find all matching key handles.
+            let key_handles = key_handles
+                .iter()
+                .filter(|key_handle| {
+                    u2f_is_keyhandle_valid(dev, &challenge, &application, key_handle)
+                        .unwrap_or(false) /* no match on failure */
+                })
+                .collect::<Vec<_>>();
 
-                        // Otherwise, try to sign.
-                        for key_handle in key_handles {
-                            if let Ok(bytes) = u2f_sign(
-                                device,
-                                &challenge,
-                                &application,
-                                key_handle,
-                            )
-                            {
-                                callback.call(Ok((key_handle.to_vec(), bytes)));
-                                return;
-                            }
+            while alive() {
+                // If the device matches none of the given key handles
+                // then just make it blink with bogus data.
+                if key_handles.is_empty() {
+                    let blank = vec![0u8; PARAMETER_SIZE];
+                    if let Ok(_) = u2f_register(dev, &blank, &blank) {
+                        callback.call(Err(io_err("invalid key")));
+                        break;
+                    }
+                } else {
+                    // Otherwise, try to sign.
+                    for key_handle in &key_handles {
+                        if let Ok(bytes) = u2f_sign(dev, &challenge, &application, key_handle) {
+                            callback.call(Ok((key_handle.to_vec(), bytes)));
+                            break;
                         }
                     }
-
-                    // Wait a little before trying again.
-                    thread::sleep(Duration::from_millis(100));
                 }
 
-                callback.call(Err(io_err("aborted or timed out")));
-            },
-            timeout,
-        );
+                // Sleep a bit before trying again.
+                thread::sleep(Duration::from_millis(100));
+            }
+        });
 
-        self.thread = Some(try_or!(thread, |_| {
-            cbc.call(Err(io_err("couldn't create runloop")));
+        self.transaction = Some(try_or!(transaction, |_| {
+            cbc.call(Err(io_err("couldn't create transaction")));
         }));
     }
 
     // This might block.
     pub fn cancel(&mut self) {
-        if let Some(thread) = self.thread.take() {
-            thread.cancel();
+        if let Some(mut transaction) = self.transaction.take() {
+            transaction.cancel();
         }
     }
 }
--- a/dom/webauthn/u2f-hid-rs/src/windows/monitor.rs
+++ b/dom/webauthn/u2f-hid-rs/src/windows/monitor.rs
@@ -1,86 +1,89 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-use std::collections::HashSet;
-use std::error::Error;
+use platform::winapi::DeviceInfoSet;
+use runloop::RunLoop;
+use std::collections::{HashMap, HashSet};
 use std::io;
 use std::iter::FromIterator;
-use std::sync::mpsc::{channel, Receiver, TryIter};
+use std::sync::Arc;
 use std::thread;
 use std::time::Duration;
 
-use runloop::RunLoop;
-use super::winapi::DeviceInfoSet;
-
-pub fn io_err(msg: &str) -> io::Error {
-    io::Error::new(io::ErrorKind::Other, msg)
-}
-
-pub fn to_io_err<T: Error>(err: T) -> io::Error {
-    io_err(err.description())
-}
-
-pub enum Event {
-    Add(String),
-    Remove(String),
-}
-
-pub struct Monitor {
-    // Receive events from the thread.
-    rx: Receiver<Event>,
-    // Handle to the thread loop.
-    thread: RunLoop,
+pub struct Monitor<F>
+where
+    F: Fn(String, &Fn() -> bool) + Sync,
+{
+    runloops: HashMap<String, RunLoop>,
+    new_device_cb: Arc<F>,
 }
 
-impl Monitor {
-    pub fn new() -> io::Result<Self> {
-        let (tx, rx) = channel();
-
-        let thread = RunLoop::new(move |alive| -> io::Result<()> {
-            let mut stored = HashSet::new();
-
-            while alive() {
-                let device_info_set = DeviceInfoSet::new()?;
-                let devices = HashSet::from_iter(device_info_set.devices());
+impl<F> Monitor<F>
+where
+    F: Fn(String, &Fn() -> bool) + Send + Sync + 'static,
+{
+    pub fn new(new_device_cb: F) -> Self {
+        Self {
+            runloops: HashMap::new(),
+            new_device_cb: Arc::new(new_device_cb),
+        }
+    }
 
-                // Remove devices that are gone.
-                for path in stored.difference(&devices) {
-                    tx.send(Event::Remove(path.clone())).map_err(to_io_err)?;
-                }
+    pub fn run(&mut self, alive: &Fn() -> bool) -> io::Result<()> {
+        let mut stored = HashSet::new();
+
+        while alive() {
+            let device_info_set = DeviceInfoSet::new()?;
+            let devices = HashSet::from_iter(device_info_set.devices());
 
-                // Add devices that were plugged in.
-                for path in devices.difference(&stored) {
-                    tx.send(Event::Add(path.clone())).map_err(to_io_err)?;
-                }
+            // Remove devices that are gone.
+            for path in stored.difference(&devices) {
+                self.remove_device(path);
+            }
 
-                // Remember the new set.
-                stored = devices;
-
-                // Wait a little before looking for devices again.
-                thread::sleep(Duration::from_millis(100));
+            // Add devices that were plugged in.
+            for path in devices.difference(&stored) {
+                self.add_device(path);
             }
 
-            Ok(())
-        })?;
+            // Remember the new set.
+            stored = devices;
 
-        Ok(Self {
-            rx: rx,
-            thread: thread,
-        })
+            // Wait a little before looking for devices again.
+            thread::sleep(Duration::from_millis(100));
+        }
+
+        // Remove all tracked devices.
+        self.remove_all_devices();
+
+        Ok(())
     }
 
-    pub fn events<'a>(&'a self) -> TryIter<'a, Event> {
-        self.rx.try_iter()
+    fn add_device(&mut self, path: &String) {
+        let f = self.new_device_cb.clone();
+        let path = path.clone();
+        let key = path.clone();
+
+        let runloop = RunLoop::new(move |alive| if alive() {
+            f(path, alive);
+        });
+
+        if let Ok(runloop) = runloop {
+            self.runloops.insert(key, runloop);
+        }
     }
 
-    pub fn alive(&self) -> bool {
-        self.thread.alive()
+    fn remove_device(&mut self, path: &String) {
+        if let Some(runloop) = self.runloops.remove(path) {
+            runloop.cancel();
+        }
+    }
+
+    fn remove_all_devices(&mut self) {
+        while !self.runloops.is_empty() {
+            let path = self.runloops.keys().next().unwrap().clone();
+            self.remove_device(&path);
+        }
     }
 }
-
-impl Drop for Monitor {
-    fn drop(&mut self) {
-        self.thread.cancel();
-    }
-}
new file mode 100644
--- /dev/null
+++ b/dom/webauthn/u2f-hid-rs/src/windows/transaction.rs
@@ -0,0 +1,42 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use platform::monitor::Monitor;
+use runloop::RunLoop;
+use std::io;
+use util::{io_err, OnceCallback};
+
+pub struct Transaction {
+    // Handle to the thread loop.
+    thread: Option<RunLoop>,
+}
+
+impl Transaction {
+    pub fn new<F, T>(timeout: u64, callback: OnceCallback<T>, new_device_cb: F) -> io::Result<Self>
+    where
+        F: Fn(String, &Fn() -> bool) + Sync + Send + 'static,
+        T: 'static,
+    {
+        let thread = RunLoop::new_with_timeout(
+            move |alive| {
+                // Create a new device monitor.
+                let mut monitor = Monitor::new(new_device_cb);
+
+                // Start polling for new devices.
+                try_or!(monitor.run(alive), |e| callback.call(Err(e)));
+
+                // Send an error, if the callback wasn't called already.
+                callback.call(Err(io_err("aborted or timed out")));
+            },
+            timeout,
+        )?;
+
+        Ok(Self { thread: Some(thread) })
+    }
+
+    pub fn cancel(&mut self) {
+        // This must never be None.
+        self.thread.take().unwrap().cancel();
+    }
+}
--- a/dom/webidl/Clients.webidl
+++ b/dom/webidl/Clients.webidl
@@ -26,11 +26,13 @@ dictionary ClientQueryOptions {
   boolean includeUncontrolled = false;
   ClientType type = "window";
 };
 
 enum ClientType {
   "window",
   "worker",
   "sharedworker",
+  // https://github.com/w3c/ServiceWorker/issues/1036
+  "serviceworker",
   "all"
 };
 
deleted file mode 100644
--- a/dom/webidl/MozWakeLock.webidl
+++ /dev/null
@@ -1,18 +0,0 @@
-/* -*- Mode: IDL; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-[Pref="dom.wakelock.enabled", Func="Navigator::HasWakeLockSupport"]
-interface MozWakeLock
-{
-    readonly attribute DOMString topic;
-
-    /**
-     * Release the wake lock.
-     * @throw NS_ERROR_DOM_INVALID_STATE_ERR if already unlocked.
-     */
-    [Throws]
-    void unlock();
-};
--- a/dom/webidl/Navigator.webidl
+++ b/dom/webidl/Navigator.webidl
@@ -201,47 +201,16 @@ partial interface Navigator {
 
   /**
    * Navigator requests to remove an idle observer from the existing window.
    */
   [Throws, ChromeOnly]
   void removeIdleObserver(MozIdleObserver aIdleObserver);
 
   /**
-   * Request a wake lock for a resource.
-   *
-   * A page holds a wake lock to request that a resource not be turned
-   * off (or otherwise made unavailable).
-   *
-   * The topic is the name of a resource that might be made unavailable for
-   * various reasons. For example, on a mobile device the power manager might
-   * decide to turn off the screen after a period of idle time to save power.
-   *
-   * The resource manager checks the lock state of a topic before turning off
-   * the associated resource. For example, a page could hold a lock on the
-   * "screen" topic to prevent the screensaver from appearing or the screen
-   * from turning off.
-   *
-   * The resource manager defines what each topic means and sets policy.  For
-   * example, the resource manager might decide to ignore 'screen' wake locks
-   * held by pages which are not visible.
-   *
-   * One topic can be locked multiple times; it is considered released only when
-   * all locks on the topic have been released.
-   *
-   * The returned MozWakeLock object is a token of the lock.  You can
-   * unlock the lock via the object's |unlock| method.  The lock is released
-   * automatically when its associated window is unloaded.
-   *
-   * @param aTopic resource name
-   */
-  [Throws, Pref="dom.wakelock.enabled", Func="Navigator::HasWakeLockSupport", UnsafeInPrerendering]
-  MozWakeLock requestWakeLock(DOMString aTopic);
-
-  /**
    * Make CPU instruction subset information available for UpdateUtils.
    */
   [ChromeOnly]
   readonly attribute boolean cpuHasSSE2;
 };
 
 // nsIDOMNavigatorDesktopNotification
 partial interface Navigator {
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -700,17 +700,16 @@ WEBIDL_FILES = [
     'MimeTypeArray.webidl',
     'MouseEvent.webidl',
     'MouseScrollEvent.webidl',
     'MozSelfSupport.webidl',
     'MozStorageAsyncStatementParams.webidl',
     'MozStorageStatementParams.webidl',
     'MozStorageStatementRow.webidl',
     'MozTimeManager.webidl',
-    'MozWakeLock.webidl',
     'MutationEvent.webidl',
     'MutationObserver.webidl',
     'NamedNodeMap.webidl',
     'NativeOSFileInternals.webidl',
     'NetDashboard.webidl',
     'NetworkInformation.webidl',
     'NetworkOptions.webidl',
     'NodeFilter.webidl',
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -45,16 +45,18 @@
 #include "mozilla/LoadContext.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "mozilla/dom/CacheBinding.h"
 #include "mozilla/dom/cache/CacheTypes.h"
 #include "mozilla/dom/cache/Cache.h"
 #include "mozilla/dom/cache/CacheStorage.h"
 #include "mozilla/dom/ChannelInfo.h"
+#include "mozilla/dom/ClientChannelHelper.h"
+#include "mozilla/dom/ClientInfo.h"
 #include "mozilla/dom/Exceptions.h"
 #include "mozilla/dom/InternalResponse.h"
 #include "mozilla/dom/nsCSPService.h"
 #include "mozilla/dom/nsCSPUtils.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseNativeHandler.h"
 #include "mozilla/dom/Response.h"
 #include "mozilla/dom/ScriptLoader.h"
@@ -254,16 +256,17 @@ struct ScriptLoadInfo
   // resolution.
   RefPtr<Promise> mCachePromise;
 
   // The reader stream the cache entry should be filled from, for those cases
   // when we're going to have an mCachePromise.
   nsCOMPtr<nsIInputStream> mCacheReadStream;
 
   nsCOMPtr<nsIChannel> mChannel;
+  Maybe<ClientInfo> mReservedClientInfo;
   char16_t* mScriptTextBuf;
   size_t mScriptTextLength;
 
   nsresult mLoadResult;
   bool mLoadingFinished;
   bool mExecutionScheduled;
   bool mExecutionResult;
 
@@ -984,16 +987,27 @@ private:
     // We don't care about progress so just use the simple stream loader for
     // OnStreamComplete notification only.
     nsCOMPtr<nsIStreamLoader> loader;
     rv = NS_NewStreamLoader(getter_AddRefs(loader), listener);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
+    if (IsMainWorkerScript()) {
+      MOZ_DIAGNOSTIC_ASSERT(loadInfo.mReservedClientInfo.isSome());
+      rv = AddClientChannelHelper(channel,
+                                  Move(loadInfo.mReservedClientInfo),
+                                  Maybe<ClientInfo>(),
+                                  mWorkerPrivate->HybridEventTarget());
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+    }
+
     if (loadInfo.mCacheStatus != ScriptLoadInfo::ToBeCached) {
       rv = channel->AsyncOpen2(loader);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     } else {
       nsCOMPtr<nsIOutputStream> writer;
 
@@ -1720,17 +1734,17 @@ CacheScriptLoader::ResolvedCallback(JSCo
                                      Move(mPrincipalInfo), mCSPHeaderValue,
                                      mCSPReportOnlyHeaderValue,
                                      mReferrerPolicyHeaderValue);
     return;
   }
 
   MOZ_ASSERT(!mPump);
   rv = NS_NewInputStreamPump(getter_AddRefs(mPump),
-                             inputStream,
+                             inputStream.forget(),
                              0, /* default segsize */
                              0, /* default segcount */
                              false, /* default closeWhenDone */
                              mMainThreadEventTarget);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     Fail(rv);
     return;
   }
@@ -1962,16 +1976,22 @@ ScriptExecutorRunnable::WorkerRun(JSCont
                                     loadInfo.mLoadResult, loadInfo.mURL);
       // Top level scripts only!
       if (mIsWorkerScript) {
         aWorkerPrivate->MaybeDispatchLoadFailedRunnable();
       }
       return true;
     }
 
+    // If this is a top level script that succeeded, then mark the
+    // Client execution ready.
+    if (mIsWorkerScript) {
+      aWorkerPrivate->ExecutionReady();
+    }
+
     NS_ConvertUTF16toUTF8 filename(loadInfo.mURL);
 
     JS::CompileOptions options(aCx);
     options.setFileAndLine(filename.get(), 1)
            .setNoScriptRval(true);
 
     MOZ_ASSERT(loadInfo.mMutedErrorFlag.isSome());
     options.setMutedErrors(loadInfo.mMutedErrorFlag.valueOr(true));
@@ -2256,16 +2276,20 @@ LoadMainScript(WorkerPrivate* aWorkerPri
                ErrorResult& aRv)
 {
   nsTArray<ScriptLoadInfo> loadInfos;
 
   ScriptLoadInfo* info = loadInfos.AppendElement();
   info->mURL = aScriptURL;
   info->mLoadFlags = aWorkerPrivate->GetLoadFlags();
 
+  // We are loading the main script, so the worker's Client must be
+  // reserved.
+  info->mReservedClientInfo.emplace(aWorkerPrivate->GetClientInfo());
+
   LoadAllScripts(aWorkerPrivate, loadInfos, true, aWorkerScriptType, aRv);
 }
 
 void
 Load(WorkerPrivate* aWorkerPrivate,
      const nsTArray<nsString>& aScriptURLs, WorkerScriptType aWorkerScriptType,
      ErrorResult& aRv)
 {
--- a/dom/workers/ServiceWorkerScriptCache.cpp
+++ b/dom/workers/ServiceWorkerScriptCache.cpp
@@ -1155,17 +1155,17 @@ CompareCache::ManageValueResult(JSContex
   MOZ_ASSERT(response->Ok());
 
   nsCOMPtr<nsIInputStream> inputStream;
   response->GetBody(getter_AddRefs(inputStream));
   MOZ_ASSERT(inputStream);
 
   MOZ_ASSERT(!mPump);
   rv = NS_NewInputStreamPump(getter_AddRefs(mPump),
-                             inputStream,
+                             inputStream.forget(),
                              0, /* default segsize */
                              0, /* default segcount */
                              false, /* default closeWhenDone */
                              SystemGroup::EventTargetFor(TaskCategory::Other));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     Finish(rv, false);
     return;
   }
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -42,16 +42,18 @@
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/ContentEvents.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/Likely.h"
 #include "mozilla/LoadContext.h"
 #include "mozilla/Move.h"
 #include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/ClientManager.h"
+#include "mozilla/dom/ClientSource.h"
 #include "mozilla/dom/Console.h"
 #include "mozilla/dom/DocGroup.h"
 #include "mozilla/dom/ErrorEvent.h"
 #include "mozilla/dom/ErrorEventBinding.h"
 #include "mozilla/dom/Exceptions.h"
 #include "mozilla/dom/ExtendableMessageEventBinding.h"
 #include "mozilla/dom/FunctionBinding.h"
 #include "mozilla/dom/IndexedDatabaseManager.h"
@@ -593,16 +595,18 @@ private:
   // run we have not yet done our load so don't know things like our final
   // principal and whatnot.
 
   virtual bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
   {
     aWorkerPrivate->AssertIsOnWorkerThread();
 
+    aWorkerPrivate->EnsureClientSource();
+
     ErrorResult rv;
     scriptloader::LoadMainScript(aWorkerPrivate, mScriptURL, WorkerScript, rv);
     rv.WouldReportJSException();
     // Explicitly ignore NS_BINDING_ABORTED on rv.  Or more precisely, still
     // return false and don't SetWorkerScriptExecutedSuccessfully() in that
     // case, but don't throw anything on aCx.  The idea is to not dispatch error
     // events if our load is canceled with that error code.
     if (rv.ErrorCodeIs(NS_BINDING_ABORTED)) {
@@ -663,16 +667,18 @@ private:
 
     WorkerDebuggerGlobalScope* globalScope =
       aWorkerPrivate->CreateDebuggerGlobalScope(aCx);
     if (!globalScope) {
       NS_WARNING("Failed to make global!");
       return false;
     }
 
+    aWorkerPrivate->EnsureClientSource();
+
     JS::Rooted<JSObject*> global(aCx, globalScope->GetWrapper());
 
     ErrorResult rv;
     JSAutoCompartment ac(aCx, global);
     scriptloader::LoadMainScript(aWorkerPrivate, mScriptURL,
                                  DebuggerScript, rv);
     rv.WouldReportJSException();
     // Explicitly ignore NS_BINDING_ABORTED on rv.  Or more precisely, still
@@ -5123,16 +5129,18 @@ WorkerPrivate::DoRunLoop(JSContext* aCx)
       }
 
       // If we're supposed to die then we should exit the loop.
       if (currentStatus == Killing) {
         // Flush uncaught rejections immediately, without
         // waiting for a next tick.
         PromiseDebugging::FlushUncaughtRejections();
 
+        mClientSource = nullptr;
+
         ShutdownGCTimers();
 
         DisableMemoryReporter();
 
         {
           MutexAutoLock lock(mMutex);
 
           mStatus = Dead;
@@ -5285,16 +5293,63 @@ WorkerPrivate::ControlEventTarget()
 
 nsISerialEventTarget*
 WorkerPrivate::HybridEventTarget()
 {
   return mWorkerHybridEventTarget;
 }
 
 void
+WorkerPrivate::EnsureClientSource()
+{
+  AssertIsOnWorkerThread();
+
+  if (mClientSource) {
+    return;
+  }
+
+  ClientType type;
+  switch(Type()) {
+    case WorkerTypeDedicated:
+      type = ClientType::Worker;
+      break;
+    case WorkerTypeShared:
+      type = ClientType::Sharedworker;
+      break;
+    case WorkerTypeService:
+      type = ClientType::Serviceworker;
+      break;
+    default:
+      MOZ_CRASH("unknown worker type!");
+  }
+
+  mClientSource = ClientManager::CreateSource(type, mWorkerHybridEventTarget,
+                                              GetPrincipalInfo());
+  if (mFrozen) {
+    mClientSource->Freeze();
+  }
+}
+
+const ClientInfo&
+WorkerPrivate::GetClientInfo() const
+{
+  AssertIsOnWorkerThread();
+  MOZ_DIAGNOSTIC_ASSERT(mClientSource);
+  return mClientSource->Info();
+}
+
+void
+WorkerPrivate::ExecutionReady()
+{
+  AssertIsOnWorkerThread();
+  MOZ_DIAGNOSTIC_ASSERT(mClientSource);
+  mClientSource->WorkerExecutionReady(this);
+}
+
+void
 WorkerPrivate::InitializeGCTimers()
 {
   AssertIsOnWorkerThread();
 
   // We need a timer for GC. The basic plan is to run a non-shrinking GC
   // periodically (PERIODIC_GC_TIMER_DELAY_SEC) while the worker is running.
   // Once the worker goes idle we set a short (IDLE_GC_TIMER_DELAY_SEC) timer to
   // run a shrinking GC. If the worker receives more messages then the short
@@ -5623,16 +5678,20 @@ WorkerPrivate::ClearDebuggerEventQueue()
 
 bool
 WorkerPrivate::FreezeInternal()
 {
   AssertIsOnWorkerThread();
 
   NS_ASSERTION(!mFrozen, "Already frozen!");
 
+  if (mClientSource) {
+    mClientSource->Freeze();
+  }
+
   mFrozen = true;
 
   for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
     mChildWorkers[index]->Freeze(nullptr);
   }
 
   return true;
 }
@@ -5644,16 +5703,21 @@ WorkerPrivate::ThawInternal()
 
   NS_ASSERTION(mFrozen, "Not yet frozen!");
 
   for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
     mChildWorkers[index]->Thaw(nullptr);
   }
 
   mFrozen = false;
+
+  if (mClientSource) {
+    mClientSource->Thaw();
+  }
+
   return true;
 }
 
 void
 WorkerPrivate::TraverseTimeouts(nsCycleCollectionTraversalCallback& cb)
 {
   for (uint32_t i = 0; i < mTimeouts.Length(); ++i) {
     TimeoutInfo* tmp = mTimeouts[i];
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -58,16 +58,18 @@ template<class T> class nsMainThreadPtrH
 
 namespace JS {
 struct RuntimeStats;
 } // namespace JS
 
 namespace mozilla {
 class ThrottledEventQueue;
 namespace dom {
+class ClientInfo;
+class ClientSource;
 class Function;
 class MessagePort;
 class MessagePortIdentifier;
 class PromiseNativeHandler;
 class StructuredCloneHolder;
 class WorkerDebuggerGlobalScope;
 class WorkerGlobalScope;
 struct WorkerOptions;
@@ -1055,16 +1057,17 @@ class WorkerPrivate : public WorkerPriva
   // fired on the main thread if the worker script fails to load
   nsCOMPtr<nsIRunnable> mLoadFailedRunnable;
 
   JS::UniqueChars mDefaultLocale; // nulled during worker JSContext init
   TimeStamp mKillTime;
   uint32_t mErrorHandlerRecursionCount;
   uint32_t mNextTimeoutId;
   Status mStatus;
+  UniquePtr<ClientSource> mClientSource;
   bool mFrozen;
   bool mTimerRunning;
   bool mRunningExpiredTimeouts;
   bool mPendingEventQueueClearing;
   bool mCancelAllPendingRunnables;
   bool mPeriodicGCTimerRunning;
   bool mIdleGCTimerRunning;
   bool mWorkerScriptExecutedSuccessfully;
@@ -1489,16 +1492,25 @@ public:
   // Get an event target that will attempt to dispatch a normal WorkerRunnable,
   // but if that fails will then fall back to a control runnable.
   nsISerialEventTarget*
   HybridEventTarget();
 
   void
   DumpCrashInformation(nsACString& aString);
 
+  void
+  EnsureClientSource();
+
+  const ClientInfo&
+  GetClientInfo() const;
+
+  void
+  ExecutionReady();
+
 private:
   WorkerPrivate(WorkerPrivate* aParent,
                 const nsAString& aScriptURL, bool aIsChromeWorker,
                 WorkerType aWorkerType, const nsAString& aWorkerName,
                 const nsACString& aServiceWorkerScope,
                 WorkerLoadInfo& aLoadInfo);
 
   bool
--- a/dom/workers/test/serviceworkers/browser.ini
+++ b/dom/workers/test/serviceworkers/browser.ini
@@ -1,18 +1,20 @@
 [DEFAULT]
 support-files =
   browser_base_force_refresh.html
   browser_cached_force_refresh.html
   download/window.html
   download/worker.js
+  fetch.js
   file_multie10s_update.html
   file_userContextId_openWindow.js
   force_refresh_browser_worker.js
   empty.html
   server_multie10s_update.sjs
 
+[browser_devtools_serviceworker_interception.js]
 [browser_force_refresh.js]
 [browser_download.js]
 [browser_multie10s_update.js]
 skip-if = !e10s || os != "win" # Bug 1404914
 [browser_userContextId_openWindow.js]
 skip-if = !e10s
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/browser_devtools_serviceworker_interception.js
@@ -0,0 +1,262 @@
+"use strict";
+
+const { classes: Cc, interfaces: Ci, results: Cr } = Components;
+
+const BASE_URI =
+  "http://mochi.test:8888/browser/dom/workers/test/serviceworkers/";
+const emptyDoc = BASE_URI + "empty.html";
+const fakeDoc = BASE_URI + "fake.html";
+const helloDoc = BASE_URI + "hello.html";
+
+const CROSS_URI = "http://example.com/browser/dom/workers/test/serviceworkers/";
+const crossRedirect = CROSS_URI + "redirect";
+const crossHelloDoc = CROSS_URI + "hello.html";
+
+const sw = BASE_URI + "fetch.js";
+
+// XXXtt: We should be able to move this check to chrome process after we move
+// the interception logic to chrome process.
+async function checkObserverInContent(aInput) {
+  let interceptedChannel = null;
+
+  // We always get two channels which receive the "http-on-stop-request"
+  // notification if the service worker hijacks the request and respondWith an
+  // another fetch. One is for the "outer" window request when the other one is
+  // for the "inner" service worker request. We used to distinguish them by the
+  // channel.URI.spec, but it doesn't work fine with internal redirect case. The
+  // reason is that the two different channels will have the same channel.URI
+  // if it's an internal redirect. Therefore, distinguish them by the order.
+  let waitForSecondOnStopRequest = aInput.redirect;
+
+  let promiseResolve;
+
+  function observer(aSubject) {
+    let channel = aSubject.QueryInterface(Ci.nsIChannel);
+    // Since we cannot make sure that the network event triggered by the fetch()
+    // in this testcase is the very next event processed by ObserverService, we
+    // have to wait until we catch the one we want.
+    if (!(aInput.redirect && channel.URI.spec.includes(aInput.redirect)) &&
+        !(!aInput.redirect && channel.URI.spec.includes(aInput.url))) {
+      return;
+    }
+
+    if (waitForSecondOnStopRequest) {
+      waitForSecondOnStopRequest = false;
+      return;
+    }
+
+    // Wait for the service worker to intercept the request if it's expected to
+    // be intercepted
+    if (aInput.intercepted && interceptedChannel === null) {
+      return;
+    } else if (interceptedChannel) {
+      ok(aInput.intercepted,
+         "Service worker intercepted the channel as expected");
+    } else {
+      ok(!aInput.intercepted, "The channel doesn't be intercepted");
+    }
+
+    var tc = interceptedChannel
+               ? interceptedChannel.QueryInterface(Ci.nsITimedChannel)
+               : aSubject.QueryInterface(Ci.nsITimedChannel);
+
+    // Check service worker related timings.
+    var serviceWorkerTimings = [{start: tc.launchServiceWorkerStartTime,
+                                 end:   tc.launchServiceWorkerEndTime},
+                                {start: tc.dispatchFetchEventStartTime,
+                                 end:   tc.dispatchFetchEventEndTime},
+                                {start: tc.handleFetchEventStartTime,
+                                 end:   tc.handleFetchEventEndTime}];
+    if (aInput.swPresent) {
+      serviceWorkerTimings.reduce((aPreviousTimings, aCurrentTimings) => {
+        ok(aPreviousTimings.start !== 0, "Start time check.");
+        ok(aPreviousTimings.start <= aCurrentTimings.start,
+           "Start time order check.");
+        ok(aPreviousTimings.end <= aCurrentTimings.end,
+           "End time order check.");
+        ok(aCurrentTimings.start <= aCurrentTimings.end,
+           "Start time should be smaller than end time.");
+        return aCurrentTimings;
+      });
+    } else {
+      serviceWorkerTimings.forEach(aTimings => {
+        is(aTimings.start, 0, "SW timings should be 0.");
+        is(aTimings.end, 0, "SW timings should be 0.");
+      });
+    }
+
+    // Check network related timings.
+    var networkTimings = [tc.domainLookupStartTime,
+                          tc.domainLookupEndTime,
+                          tc.connectStartTime,
+                          tc.connectEndTime,
+                          tc.requestStartTime,
+                          tc.responseStartTime,
+                          tc.responseEndTime];
+    if (aInput.fetch) {
+      networkTimings.reduce((aPreviousTiming, aCurrentTiming) => {
+        ok(aPreviousTiming <= aCurrentTiming, "Checking network timings");
+        return aCurrentTiming;
+      });
+    } else {
+      networkTimings.forEach(aTiming => is(aTiming, 0,
+                                           "Network timings should be 0."));
+    }
+
+    interceptedChannel = null;
+    Services.obs.removeObserver(observer, topic);
+    promiseResolve();
+  }
+
+  function addInterceptedChannel(aSubject) {
+    let channel = aSubject.QueryInterface(Ci.nsIChannel);
+    if (!channel.URI.spec.includes(aInput.url)) {
+      return;
+    }
+
+    // Hold the interceptedChannel until checking timing information.
+    // Note: It's a interceptedChannel in the type of httpChannel
+    interceptedChannel = channel;
+    Services.obs.removeObserver(addInterceptedChannel, topic_SW);
+  }
+
+  const topic = "http-on-stop-request";
+  const topic_SW = "service-worker-synthesized-response";
+
+  Services.obs.addObserver(observer, topic);
+  if (aInput.intercepted) {
+    Services.obs.addObserver(addInterceptedChannel, topic_SW);
+  }
+
+  await new Promise(resolve => { promiseResolve = resolve; });
+}
+
+async function contentFetch(aURL) {
+  if (aURL.includes("redirect")) {
+    await content.window.fetch(aURL, { mode: "no-cors" });
+    return;
+  }
+  await content.window.fetch(aURL);
+}
+
+async function registerSWAndWaitForActive(aServiceWorker) {
+  let swr =
+    await content.navigator.serviceWorker.register(aServiceWorker,
+                                                   { scope:"empty.html" });
+  await new Promise(resolve => {
+    let worker = swr.installing || swr.waiting || swr.active;
+    if (worker.state === 'activated') {
+      return resolve();
+    }
+
+    worker.addEventListener('statechange', () => {
+      if (worker.state === 'activated') {
+        return resolve();
+      }
+    });
+  });
+
+  await swr.active.postMessage('claim');
+
+  await new Promise(resolve => {
+    if (content.navigator.serviceWorker.controller) {
+      return resolve();
+    }
+
+    content.navigator.serviceWorker.addEventListener('controllerchange',
+                                                     resolve, { once: true });
+  });
+}
+
+async function unregisterSW() {
+  let swr = await content.navigator.serviceWorker.getRegistration();
+  swr.unregister();
+}
+
+add_task(async function test_serivce_worker_interception() {
+  info("Setting the prefs to having e10s enabled");
+  await SpecialPowers.pushPrefEnv({"set": [
+    // Make sure observer and testing function run in the same process
+    ["dom.ipc.processCount", 1],
+    ["dom.serviceWorkers.enabled", true],
+    ["dom.serviceWorkers.testing.enabled", true],
+  ]});
+
+  waitForExplicitFinish();
+
+  info("Open the tab");
+  let tab = BrowserTestUtils.addTab(gBrowser, emptyDoc);
+  let tabBrowser = gBrowser.getBrowserForTab(tab);
+  await BrowserTestUtils.browserLoaded(tabBrowser);
+
+  info("Open the tab for observing");
+  let tab_observer = BrowserTestUtils.addTab(gBrowser, emptyDoc);
+  let tabBrowser_observer = gBrowser.getBrowserForTab(tab_observer);
+  await BrowserTestUtils.browserLoaded(tabBrowser_observer);
+
+  let testcases = [
+    {
+      url: helloDoc,
+      swPresent: false,
+      intercepted: false,
+      fetch: true
+    },
+    {
+      url: fakeDoc,
+      swPresent: true,
+      intercepted: true,
+      fetch: false // should use HTTP cache
+    },
+    { // Bypass http cache
+      url: helloDoc + "?ForBypassingHttpCache=" + Date.now(),
+      swPresent: true,
+      intercepted: false,
+      fetch: true
+    },
+    { // no-cors mode redirect to no-cors mode (trigger internal redirect)
+      url: crossRedirect + "?url=" + crossHelloDoc + "&mode=no-cors",
+      swPresent: true,
+      redirect: "hello.html",
+      intercepted: true,
+      fetch: true
+    }
+  ];
+
+  info("Test 1: Verify simple fetch");
+  let promise = ContentTask.spawn(tabBrowser_observer,
+                                  testcases[0],
+                                  checkObserverInContent);
+  await ContentTask.spawn(tabBrowser, testcases[0].url, contentFetch);
+  await promise;
+
+  info("Register a service worker");
+  await ContentTask.spawn(tabBrowser, sw, registerSWAndWaitForActive);
+
+  info("Test 2: Verify simple hijack");
+  promise = ContentTask.spawn(tabBrowser_observer,
+                              testcases[1],
+                              checkObserverInContent);
+  await ContentTask.spawn(tabBrowser, testcases[1].url, contentFetch);
+  await promise;
+
+  info("Test 3: Verify fetch without using http cache");
+  promise = ContentTask.spawn(tabBrowser_observer,
+                              testcases[2],
+                              checkObserverInContent);
+  await ContentTask.spawn(tabBrowser, testcases[2].url, contentFetch);
+  await promise;
+
+  info("Test 4: make a internal redirect");
+  promise = ContentTask.spawn(tabBrowser_observer,
+                              testcases[3],
+                              checkObserverInContent);
+  await ContentTask.spawn(tabBrowser, testcases[3].url, contentFetch);
+  await promise;
+
+  info("Clean up");
+  await ContentTask.spawn(tabBrowser, undefined, unregisterSW);
+
+  gBrowser.removeTab(tab);
+  gBrowser.removeTab(tab_observer);
+});
+
--- a/dom/workers/test/serviceworkers/chrome.ini
+++ b/dom/workers/test/serviceworkers/chrome.ini
@@ -8,14 +8,13 @@ support-files =
   serviceworker.html
   serviceworkerinfo_iframe.html
   serviceworkermanager_iframe.html
   serviceworkerregistrationinfo_iframe.html
   utils.js
   worker.js
   worker2.js
 
-[test_devtools_serviceworker_interception.html]
 [test_devtools_track_serviceworker_time.html]
 [test_privateBrowsing.html]
 [test_serviceworkerinfo.xul]
 [test_serviceworkermanager.xul]
 [test_serviceworkerregistrationinfo.xul]
--- a/dom/workers/test/serviceworkers/fetch.js
+++ b/dom/workers/test/serviceworkers/fetch.js
@@ -1,13 +1,33 @@
+function get_query_params(url) {
+  var search = (new URL(url)).search;
+  if (!search) {
+    return {};
+  }
+  var ret = {};
+  var params = search.substring(1).split('&');
+  params.forEach(function(param) {
+      var element = param.split('=');
+      ret[decodeURIComponent(element[0])] = decodeURIComponent(element[1]);
+  });
+  return ret;
+}
+
 addEventListener('fetch', function(event) {
-  if (event.request.url.indexOf("fail.html") !== -1) {
-    event.respondWith(fetch("hello.html", {"integrity": "abc"}));
-  } else if (event.request.url.indexOf("fake.html") !== -1) {
-    event.respondWith(fetch("hello.html"));
+  if (event.request.url.includes('fail.html')) {
+    event.respondWith(fetch('hello.html', { integrity: 'abc' }));
+  } else if (event.request.url.includes('fake.html')) {
+    event.respondWith(fetch('hello.html'));
+  } else if (event.request.url.includes('redirect')) {
+    let param = get_query_params(event.request.url);
+    let url = param['url'];
+    let mode = param['mode'];
+
+    event.respondWith(fetch(url, { mode: mode }));
   }
 });
 
 addEventListener('message', function(event) {
   if (event.data === 'claim') {
     event.waitUntil(clients.claim());
   }
 });
deleted file mode 100644
--- a/dom/workers/test/serviceworkers/test_devtools_serviceworker_interception.html
+++ /dev/null
@@ -1,189 +0,0 @@
-<!--
-  Any copyright is dedicated to the Public Domain.
-  http://creativecommons.org/publicdomain/zero/1.0/
--->
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Bug 1168875 - test devtools serviceworker interception.</title>
-  <script type="application/javascript"
-          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet"
-        type="text/css"
-        href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
-</head>
-<body>
-<p id="display"></p>
-<div id="content" style="display: none"></div>
-<pre id="test"></pre>
-<script src="utils.js"></script>
-<script class="testbody" type="text/javascript">
-
-// Constants
-const Ci = Components.interfaces;
-const workerScope = "http://mochi.test:8888/chrome/dom/workers/test/serviceworkers/";
-const workerURL = workerScope + "fetch.js";
-const contentPage = workerScope + "hello.html";
-
-function createTestWindow(aURL) {
-  var mainwindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                         .getInterface(Ci.nsIWebNavigation)
-                         .QueryInterface(Ci.nsIDocShellTreeItem)
-                         .rootTreeItem
-                         .QueryInterface(Ci.nsIInterfaceRequestor)
-                         .getInterface(Ci.nsIDOMWindow);
-  var win = mainwindow.OpenBrowserWindow(contentPage);
-
-  return new Promise(aResolve => {
-    win.addEventListener("DOMContentLoaded", function callback() {
-      if (win.content.location.href != aURL) {
-        win.gBrowser.loadURI(aURL);
-        return;
-      }
-
-      win.removeEventListener("DOMContentLoaded", callback);
-      aResolve(win.content);
-    });
-  });
-}
-
-function executeTest(aWindow) {
-  var registration;
-
-  return Promise.resolve()
-    // Should not be intercepted.
-    .then(_ => fetchAndCheckTimedChannel(aWindow, false, true, "hello.html"))
-
-    // Regist a service worker.
-    .then(_ => register(aWindow, workerURL, workerScope))
-    .then(r => registration = r)
-
-    // If this test is re-run then we may end up resurrecting the previous
-    // registration and worker.  In those cases we will have an active instead
-    // of installing.  This happens because because the test window itself
-    // is controlled.  If we were using iframes we could ensure the registration
-    // was removed before ending the test.
-    .then(_ => waitForState(registration.installing || registration.active, 'activated'))
-
-    // When run consecutively we sometime end up resurrecting a previous
-    // service worker.  In that case our active event does not run and claim
-    // the window.  So do the claim for a message event instead.
-    .then(_ => registration.active.postMessage('claim'))
-    .then(_ => waitForControlled(aWindow))
-
-    // Should be intercepted and synthesized.
-    .then(_ => fetchAndCheckTimedChannel(aWindow, true, false, "fake.html"))
-
-    // Should be intercepted but still fetch from network.
-    .then(_ => fetchAndCheckTimedChannel(aWindow, true, true,
-                                         "hello.html?ForBypassingHttpCache=" + Date.now()))
-
-    // Tear down
-    .then(_ => registration.unregister())
-    .then(_ => aWindow.close());
-}
-
-function register(aWindow, aURL, aScope) {
-  return aWindow.navigator.serviceWorker.register(aURL, {scope: aScope})
-    .then(r => {
-      var worker = r.installing;
-      return new Promise(function(aResolve) {
-        worker.onstatechange = function() {
-          if (worker.state == "activated") {
-            aResolve(r);
-          }
-        }
-      });
-    });
-}
-
-function fetchAndCheckTimedChannel(aWindow, aIntercepted, aFetch, aURL) {
-  var resolveFunction;
-  var promise = new Promise(aResolve => resolveFunction = aResolve);
-
-  var topic = "http-on-stop-request";
-
-  function observer(aSubject) {
-    var channel = aSubject.QueryInterface(Ci.nsIChannel);
-
-    // Since we cannot make sure that the network event triggered by the fetch()
-    // in this testcase is the very next event processed by ObserverService, we
-    // have to wait until we catch the one we want.
-    if (!channel.URI.spec.endsWith(aURL)) {
-      return;
-    }
-
-    var tc = aSubject.QueryInterface(Ci.nsITimedChannel);
-
-    // Check service worker related timings.
-    var serviceWorkerTimings = [{start: tc.launchServiceWorkerStartTime,
-                                 end:   tc.launchServiceWorkerEndTime},
-                                {start: tc.dispatchFetchEventStartTime,
-                                 end:   tc.dispatchFetchEventEndTime},
-                                {start: tc.handleFetchEventStartTime,
-                                 end:   tc.handleFetchEventEndTime}];
-    if (aIntercepted) {
-      serviceWorkerTimings.reduce((aPreviousTimings, aCurrentTimings) => {
-        ok(aPreviousTimings.start <= aCurrentTimings.start,
-           "Start time order check.");
-        ok(aPreviousTimings.end <= aCurrentTimings.end,
-           "End time order check.");
-        ok(aCurrentTimings.start <= aCurrentTimings.end,
-           "Start time should be smaller than end time.");
-        return aCurrentTimings;
-      });
-    } else {
-      serviceWorkerTimings.forEach(aTimings => {
-        is(aTimings.start, 0, "SW timings should be 0.");
-        is(aTimings.end, 0, "SW timings should be 0.");
-      });
-    }
-
-    // Check network related timings.
-    var networkTimings = [tc.domainLookupStartTime,
-                          tc.domainLookupEndTime,
-                          tc.connectStartTime,
-                          tc.connectEndTime,
-                          tc.requestStartTime,
-                          tc.responseStartTime,
-                          tc.responseEndTime];
-    if (aFetch) {
-      networkTimings.reduce((aPreviousTiming, aCurrentTiming) => {
-        ok(aPreviousTiming <= aCurrentTiming, "Checking network timings");
-        return aCurrentTiming;
-      });
-    } else {
-      networkTimings.forEach(aTiming => is(aTiming, 0,
-                                           "Network timings should be 0."));
-    }
-
-    SpecialPowers.removeObserver(observer, topic);
-    resolveFunction();
-  }
-
-  SpecialPowers.addObserver(observer, topic);
-
-  // return promise;
-  return Promise.all([aWindow.fetch(aURL), promise]);
-}
-
-function runTest() {
-  return Promise.resolve()
-    .then(_ => createTestWindow(contentPage))
-    .then(w => executeTest(w))
-    .catch(e => ok(false, "Some test failed with error " + e))
-    .then(_ => SimpleTest.finish());
-}
-
-SimpleTest.waitForExplicitFinish();
-SpecialPowers.pushPrefEnv({"set": [
-  ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-  ["dom.serviceWorkers.enabled", true],
-  ["dom.serviceWorkers.testing.enabled", true],
-]}, runTest);
-
-</script>
-</pre>
-</body>
-</html>
-
--- a/dom/worklet/Worklet.cpp
+++ b/dom/worklet/Worklet.cpp
@@ -125,17 +125,17 @@ public:
     nsCOMPtr<nsIInputStream> inputStream;
     response->GetBody(getter_AddRefs(inputStream));
     if (!inputStream) {
       RejectPromises(NS_ERROR_DOM_NETWORK_ERR);
       return;
     }
 
     nsCOMPtr<nsIInputStreamPump> pump;
-    rv = NS_NewInputStreamPump(getter_AddRefs(pump), inputStream);
+    rv = NS_NewInputStreamPump(getter_AddRefs(pump), inputStream.forget());
     if (NS_WARN_IF(NS_FAILED(rv))) {
       RejectPromises(rv);
       return;
     }
 
     nsCOMPtr<nsIStreamLoader> loader;
     rv = NS_NewStreamLoader(getter_AddRefs(loader), this);
     if (NS_WARN_IF(NS_FAILED(rv))) {
--- a/gfx/2d/DrawTargetD2D1.cpp
+++ b/gfx/2d/DrawTargetD2D1.cpp
@@ -52,16 +52,17 @@ DrawTargetD2D1::DrawTargetD2D1()
 {
 }
 
 DrawTargetD2D1::~DrawTargetD2D1()
 {
   PopAllClips();
 
   if (mSnapshot) {
+    MutexAutoLock lock(*mSnapshotLock);
     // We may hold the only reference. MarkIndependent will clear mSnapshot;
     // keep the snapshot object alive so it doesn't get destroyed while
     // MarkIndependent is running.
     RefPtr<SourceSurfaceD2D1> deathGrip = mSnapshot;
     // mSnapshot can be treated as independent of this DrawTarget since we know
     // this DrawTarget won't change again.
     deathGrip->MarkIndependent();
     // mSnapshot will be cleared now.
@@ -85,16 +86,19 @@ DrawTargetD2D1::~DrawTargetD2D1()
        iter != mDependingOnTargets.end(); iter++) {
     (*iter)->mDependentTargets.erase(this);
   }
 }
 
 already_AddRefed<SourceSurface>
 DrawTargetD2D1::Snapshot()
 {
+  if (!mSnapshotLock) {
+    mSnapshotLock = make_shared<Mutex>("DrawTargetD2D1::mSnapshotLock");
+  }
   if (mSnapshot) {
     RefPtr<SourceSurface> snapshot(mSnapshot);
     return snapshot.forget();
   }
   PopAllClips();
 
   Flush();
 
@@ -1269,16 +1273,17 @@ DrawTargetD2D1::CleanupD2D()
     mFactory = nullptr;
   }
 }
 
 void
 DrawTargetD2D1::MarkChanged()
 {
   if (mSnapshot) {
+    MutexAutoLock lock(*mSnapshotLock);
     if (mSnapshot->hasOneRef()) {
       // Just destroy it, since no-one else knows about it.
       mSnapshot = nullptr;
     } else {
       mSnapshot->DrawTargetWillChange();
       // The snapshot will no longer depend on this target.
       MOZ_ASSERT(!mSnapshot);
     }
--- a/gfx/2d/DrawTargetD2D1.h
+++ b/gfx/2d/DrawTargetD2D1.h
@@ -275,16 +275,17 @@ private:
   PushedLayer& CurrentLayer()
   {
     return mPushedLayers.back();
   }
 
   // The latest snapshot of this surface. This needs to be told when this
   // target is modified. We keep it alive as a cache.
   RefPtr<SourceSurfaceD2D1> mSnapshot;
+  std::shared_ptr<Mutex> mSnapshotLock;
   // A list of targets we need to flush when we're modified.
   TargetSet mDependentTargets;
   // A list of targets which have this object in their mDependentTargets set
   TargetSet mDependingOnTargets;
 
   uint32_t mUsedCommandListsSincePurge;
   // When a BlendEffect has been drawn to a command list, and that command list is
   // subsequently used -again- as an input to a blend effect for a command list,
--- a/gfx/2d/SourceSurfaceD2D1.cpp
+++ b/gfx/2d/SourceSurfaceD2D1.cpp
@@ -18,16 +18,19 @@ SourceSurfaceD2D1::SourceSurfaceD2D1(ID2
   , mDC(aDC)
   , mDevice(Factory::GetD2D1Device())
   , mDrawTarget(aDT)
 {
   aImage->QueryInterface((ID2D1Bitmap1**)getter_AddRefs(mRealizedBitmap));
 
   mFormat = aFormat;
   mSize = aSize;
+  if (aDT) {
+    mSnapshotLock = aDT->mSnapshotLock;
+  }
 }
 
 SourceSurfaceD2D1::~SourceSurfaceD2D1()
 {
 }
 
 bool
 SourceSurfaceD2D1::IsValid() const
@@ -104,16 +107,19 @@ SourceSurfaceD2D1::EnsureRealizedBitmap(
   dc->EndDraw();
 
   return true;
 }
 
 void
 SourceSurfaceD2D1::DrawTargetWillChange()
 {
+  MOZ_ASSERT(mSnapshotLock);
+  mSnapshotLock->AssertCurrentThreadOwns();
+
   // At this point in time this should always be true here.
   MOZ_ASSERT(mRealizedBitmap);
 
   RefPtr<ID2D1Bitmap1> oldBitmap = mRealizedBitmap;
 
   D2D1_BITMAP_PROPERTIES1 props;
   props.dpiX = 96;
   props.dpiY = 96;
--- a/gfx/2d/SourceSurfaceD2D1.h
+++ b/gfx/2d/SourceSurfaceD2D1.h
@@ -57,16 +57,17 @@ private:
   RefPtr<ID2D1Bitmap1> mRealizedBitmap;
   RefPtr<ID2D1DeviceContext> mDC;
   // Keep this around to verify whether out image is still valid in the future.
   RefPtr<ID2D1Device> mDevice;
 
   SurfaceFormat mFormat;
   IntSize mSize;
   DrawTargetD2D1* mDrawTarget;
+  std::shared_ptr<Mutex> mSnapshotLock;
 };
 
 class DataSourceSurfaceD2D1 : public DataSourceSurface
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurfaceD2D1)
   DataSourceSurfaceD2D1(ID2D1Bitmap1 *aMappableBitmap, SurfaceFormat aFormat);
   ~DataSourceSurfaceD2D1();
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -42,16 +42,17 @@
 #include "nsString.h"                   // for nsString, nsAutoCString, etc
 #include "ScopedGLHelpers.h"
 #include "GLReadTexImageHelper.h"
 #include "GLBlitTextureImageHelper.h"
 #include "HeapCopyOfStackArray.h"
 
 #if MOZ_WIDGET_ANDROID
 #include "TexturePoolOGL.h"
+#include "GeneratedJNIWrappers.h"
 #endif
 
 #include "GeckoProfiler.h"
 
 namespace mozilla {
 
 using namespace std;
 using namespace gfx;
@@ -669,16 +670,17 @@ CompositorOGL::BeginFrame(const nsIntReg
     MakeCurrent();
   }
 
   mPixelsPerFrame = width * height;
   mPixelsFilled = 0;
 
 #ifdef MOZ_WIDGET_ANDROID
   TexturePoolOGL::Fill(gl());
+  java::GeckoSurfaceTexture::DestroyUnused((int64_t)mGLContext.get());
 #endif
 
   // Default blend function implements "OVER"
   mGLContext->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA,
                                  LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA);
   mGLContext->fEnable(LOCAL_GL_BLEND);
 
   RefPtr<CompositingRenderTargetOGL> rt =
--- a/gfx/layers/wr/AsyncImagePipelineManager.cpp
+++ b/gfx/layers/wr/AsyncImagePipelineManager.cpp
@@ -176,16 +176,17 @@ AsyncImagePipelineManager::UpdateImageKe
 
   // The non-external image code path falls back to converting the texture into
   // an rgb image.
   auto numKeys = useExternalImage ? texture->NumSubTextures() : 1;
 
   // If we already had a texture and the format hasn't changed, better to reuse the image keys
   // than create new ones.
   bool canUpdate = !!previousTexture
+                   && previousTexture->GetSize() == texture->GetSize()
                    && previousTexture->GetFormat() == texture->GetFormat()
                    && aPipeline->mKeys.Length() == numKeys;
 
   if (!canUpdate) {
     for (auto key : aPipeline->mKeys) {
       aResources.DeleteImage(key);
     }
     aPipeline->mKeys.Clear();
--- a/gfx/skia/skia/include/core/SkPathRef.h
+++ b/gfx/skia/skia/include/core/SkPathRef.h
@@ -11,17 +11,17 @@
 
 #include "../private/SkAtomics.h"
 #include "../private/SkTDArray.h"
 #include "SkMatrix.h"
 #include "SkPoint.h"
 #include "SkRRect.h"
 #include "SkRect.h"
 #include "SkRefCnt.h"
-#include <stddef.h> // ptrdiff_t
+#include "../private/SkTemplates.h"
 
 class SkRBuffer;
 class SkWBuffer;
 
 /**
  * Holds the path verbs and points. It is versioned by a generation ID. None of its public methods
  * modify the contents. To modify or append to the verbs/points wrap the SkPathRef in an
  * SkPathRef::Editor object. Installing the editor resets the generation ID. It also performs
@@ -428,41 +428,45 @@ private:
     SkPoint* growForVerb(int /*SkPath::Verb*/ verb, SkScalar weight);
 
     /**
      * Ensures that the free space available in the path ref is >= size. The verb and point counts
      * are not changed.
      */
     void makeSpace(size_t size) {
         SkDEBUGCODE(this->validate();)
-        ptrdiff_t growSize = size - fFreeSpace;
-        if (growSize <= 0) {
+        if (size <= fFreeSpace) {
             return;
         }
+        size_t growSize = size - fFreeSpace;
         size_t oldSize = this->currSize();
         // round to next multiple of 8 bytes
         growSize = (growSize + 7) & ~static_cast<size_t>(7);
         // we always at least double the allocation
-        if (static_cast<size_t>(growSize) < oldSize) {
+        if (growSize < oldSize) {
             growSize = oldSize;
         }
         if (growSize < kMinSize) {
             growSize = kMinSize;
         }
-        size_t newSize = oldSize + growSize;
+        constexpr size_t maxSize = std::numeric_limits<size_t>::max();
+        size_t newSize;
+        if (growSize <= maxSize - oldSize) {
+            newSize = oldSize + growSize;
+        } else {
+            SK_ABORT("Path too big.");
+        }
         // Note that realloc could memcpy more than we need. It seems to be a win anyway. TODO:
         // encapsulate this.
         fPoints = reinterpret_cast<SkPoint*>(sk_realloc_throw(fPoints, newSize));
         size_t oldVerbSize = fVerbCnt * sizeof(uint8_t);
-        void* newVerbsDst = reinterpret_cast<void*>(
-                                reinterpret_cast<intptr_t>(fPoints) + newSize - oldVerbSize);
-        void* oldVerbsSrc = reinterpret_cast<void*>(
-                                reinterpret_cast<intptr_t>(fPoints) + oldSize - oldVerbSize);
+        void* newVerbsDst = SkTAddOffset<void>(fPoints, newSize - oldVerbSize);
+        void* oldVerbsSrc = SkTAddOffset<void>(fPoints, oldSize - oldVerbSize);
         memmove(newVerbsDst, oldVerbsSrc, oldVerbSize);
-        fVerbs = reinterpret_cast<uint8_t*>(reinterpret_cast<intptr_t>(fPoints) + newSize);
+        fVerbs = SkTAddOffset<uint8_t>(fPoints, newSize);
         fFreeSpace += growSize;
         SkDEBUGCODE(this->validate();)
     }
 
     /**
      * Private, non-const-ptr version of the public function verbsMemBegin().
      */
     uint8_t* verbsMemWritable() {
--- a/gfx/skia/skia/src/core/SkArenaAlloc.cpp
+++ b/gfx/skia/skia/src/core/SkArenaAlloc.cpp
@@ -3,16 +3,17 @@
  *
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
 
 #include <algorithm>
 #include <cstddef>
 #include "SkArenaAlloc.h"
+#include "SkTypes.h"
 
 static char* end_chain(char*) { return nullptr; }
 
 char* SkArenaAlloc::SkipPod(char* footerEnd) {
     char* objEnd = footerEnd - (sizeof(Footer) + sizeof(int32_t));
     int32_t skip;
     memmove(&skip, objEnd, sizeof(int32_t));
     return objEnd - skip;
@@ -90,29 +91,41 @@ void SkArenaAlloc::installUint32Footer(F
 }
 
 void SkArenaAlloc::ensureSpace(uint32_t size, uint32_t alignment) {
     constexpr uint32_t headerSize = sizeof(Footer) + sizeof(ptrdiff_t);
     // The chrome c++ library we use does not define std::max_align_t.
     // This must be conservative to add the right amount of extra memory to handle the alignment
     // padding.
     constexpr uint32_t alignof_max_align_t = 8;
-    uint32_t objSizeAndOverhead = size + headerSize + sizeof(Footer);
+    constexpr uint32_t maxSize = std::numeric_limits<uint32_t>::max();
+    constexpr uint32_t overhead = headerSize + sizeof(Footer);
+    SkASSERT_RELEASE(size <= maxSize - overhead);
+    uint32_t objSizeAndOverhead = size + overhead;
     if (alignment > alignof_max_align_t) {
-        objSizeAndOverhead += alignment - 1;
+        uint32_t alignmentOverhead = alignment - 1;
+        SkASSERT_RELEASE(objSizeAndOverhead <= maxSize - alignmentOverhead);
+        objSizeAndOverhead += alignmentOverhead;
     }
 
-    uint32_t allocationSize = std::max(objSizeAndOverhead, fExtraSize * fFib0);
-    fFib0 += fFib1;
-    std::swap(fFib0, fFib1);
+    uint32_t minAllocationSize;
+    if (fExtraSize <= maxSize / fFib0) {
+        minAllocationSize = fExtraSize * fFib0;
+        fFib0 += fFib1;
+        std::swap(fFib0, fFib1);
+    } else {
+        minAllocationSize = maxSize;
+    }
+    uint32_t allocationSize = std::max(objSizeAndOverhead, minAllocationSize);
 
     // Round up to a nice size. If > 32K align to 4K boundary else up to max_align_t. The > 32K
     // heuristic is from the JEMalloc behavior.
     {
         uint32_t mask = allocationSize > (1 << 15) ? (1 << 12) - 1 : 16 - 1;
+        SkASSERT_RELEASE(allocationSize <= maxSize - mask);
         allocationSize = (allocationSize + mask) & ~mask;
     }
 
     char* newBlock = new char[allocationSize];
 
     auto previousDtor = fDtorCursor;
     fCursor = newBlock;
     fDtorCursor = newBlock;
--- a/gfx/skia/skia/src/core/SkArenaAlloc.h
+++ b/gfx/skia/skia/src/core/SkArenaAlloc.h
@@ -152,24 +152,27 @@ private:
 
     char* allocObject(uint32_t size, uint32_t alignment);
 
     char* allocObjectWithFooter(uint32_t sizeIncludingFooter, uint32_t alignment);
 
     template <typename T>
     char* commonArrayAlloc(uint32_t count) {
         char* objStart;
+        SkASSERT_RELEASE(count <= std::numeric_limits<uint32_t>::max() / sizeof(T));
         uint32_t arraySize = SkTo<uint32_t>(count * sizeof(T));
         uint32_t alignment = SkTo<uint32_t>(alignof(T));
 
         if (skstd::is_trivially_destructible<T>::value) {
             objStart = this->allocObject(arraySize, alignment);
             fCursor = objStart + arraySize;
         } else {
-            uint32_t totalSize = arraySize + sizeof(Footer) + sizeof(uint32_t);
+            constexpr uint32_t overhead = sizeof(Footer) + sizeof(uint32_t);
+            SkASSERT_RELEASE(arraySize <= std::numeric_limits<uint32_t>::max() - overhead);
+            uint32_t totalSize = arraySize + overhead;
             objStart = this->allocObjectWithFooter(totalSize, alignment);
 
             // Can never be UB because max value is alignof(T).
             uint32_t padding = SkTo<uint32_t>(objStart - fCursor);
 
             // Advance to end of array to install footer.?
             fCursor = objStart + arraySize;
             this->installUint32Footer(
--- a/gfx/thebes/gfxFcPlatformFontList.cpp
+++ b/gfx/thebes/gfxFcPlatformFontList.cpp
@@ -1481,20 +1481,21 @@ gfxFcPlatformFontList::InitFontListForPl
         return NS_OK;
     }
 
     mLastConfig = FcConfigGetCurrent();
 
     UniquePtr<SandboxPolicy> policy;
 
 #ifdef MOZ_CONTENT_SANDBOX
-    // Create a temporary SandboxPolicy to check font paths; use a fake PID
-    // to avoid picking up any PID-specific rules by accident.
+    // If read sandboxing is enabled, create a temporary SandboxPolicy to
+    // check font paths; use a fake PID to avoid picking up any PID-specific
+    // rules by accident.
     SandboxBrokerPolicyFactory policyFactory;
-    if (GetEffectiveContentSandboxLevel() > 0 &&
+    if (GetEffectiveContentSandboxLevel() > 2 &&
         !PR_GetEnv("MOZ_DISABLE_CONTENT_SANDBOX")) {
         policy = policyFactory.GetContentPolicy(-1, false);
     }
 #endif
 
     // iterate over available fonts
     FcFontSet* systemFonts = FcConfigGetFonts(nullptr, FcSetSystem);
     AddFontSetFamilies(systemFonts, policy.get(), /* aAppFonts = */ false);
--- a/gfx/thebes/gfxPrefs.cpp
+++ b/gfx/thebes/gfxPrefs.cpp
@@ -15,16 +15,27 @@
 #include "mozilla/gfx/GPUProcessManager.h"
 
 using namespace mozilla;
 
 nsTArray<gfxPrefs::Pref*>* gfxPrefs::sGfxPrefList = nullptr;
 gfxPrefs* gfxPrefs::sInstance = nullptr;
 bool gfxPrefs::sInstanceHasBeenDestroyed = false;
 
+gfxPrefs&
+gfxPrefs::CreateAndInitializeSingleton() {
+  MOZ_ASSERT(!sInstanceHasBeenDestroyed,
+             "Should never recreate a gfxPrefs instance!");
+  sGfxPrefList = new nsTArray<Pref*>();
+  sInstance = new gfxPrefs;
+  sInstance->Init();
+  MOZ_ASSERT(SingletonExists());
+  return *sInstance;
+}
+
 void
 gfxPrefs::DestroySingleton()
 {
   if (sInstance) {
     delete sInstance;
     sInstance = nullptr;
     sInstanceHasBeenDestroyed = true;
   }
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -758,29 +758,24 @@ private:
   // WARNING:
   // Please make sure that you've added your new preference to the list above in alphabetical order.
   // Please do not just append it to the end of the list.
 
 public:
   // Manage the singleton:
   static gfxPrefs& GetSingleton()
   {
-    MOZ_ASSERT(!sInstanceHasBeenDestroyed, "Should never recreate a gfxPrefs instance!");
-    if (!sInstance) {
-      sGfxPrefList = new nsTArray<Pref*>();
-      sInstance = new gfxPrefs;
-      sInstance->Init();
-    }
-    MOZ_ASSERT(SingletonExists());
-    return *sInstance;
+    return sInstance ? *sInstance : CreateAndInitializeSingleton();
   }
   static void DestroySingleton();
   static bool SingletonExists();
 
 private:
+  static gfxPrefs& CreateAndInitializeSingleton();
+
   static gfxPrefs* sInstance;
   static bool sInstanceHasBeenDestroyed;
   static nsTArray<Pref*>* sGfxPrefList;
 
 private:
   // The constructor cannot access GetSingleton(), since sInstance (necessarily)
   // has not been assigned yet. Follow-up initialization that needs GetSingleton()
   // must be added to Init().
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -39,16 +39,17 @@ type WrEpoch = Epoch;
 /// cbindgen:derive-lt=true
 /// cbindgen:derive-lte=true
 /// cbindgen:derive-neq=true
 type WrIdNamespace = IdNamespace;
 
 /// cbindgen:field-names=[mNamespace, mHandle]
 type WrPipelineId = PipelineId;
 /// cbindgen:field-names=[mNamespace, mHandle]
+/// cbindgen:derive-neq=true
 type WrImageKey = ImageKey;
 /// cbindgen:field-names=[mNamespace, mHandle]
 pub type WrFontKey = FontKey;
 /// cbindgen:field-names=[mNamespace, mHandle]
 type WrFontInstanceKey = FontInstanceKey;
 /// cbindgen:field-names=[mNamespace, mHandle]
 type WrYuvColorSpace = YuvColorSpace;
 /// cbindgen:field-names=[mNamespace, mHandle]
--- a/image/SurfaceCache.h
+++ b/image/SurfaceCache.h
@@ -69,17 +69,17 @@ public:
   }
 
   SurfaceKey CloneWithSize(const IntSize& aSize) const
   {
     return SurfaceKey(aSize, mSVGContext, mPlayback, mFlags);
   }
 
   const IntSize& Size() const { return mSize; }
-  Maybe<SVGImageContext> SVGContext() const { return mSVGContext; }
+  const Maybe<SVGImageContext>& SVGContext() const { return mSVGContext; }
   PlaybackType Playback() const { return mPlayback; }
   SurfaceFlags Flags() const { return mFlags; }
 
 private:
   SurfaceKey(const IntSize& aSize,
              const Maybe<SVGImageContext>& aSVGContext,
              PlaybackType aPlayback,
              SurfaceFlags aFlags)
--- a/image/decoders/icon/android/nsIconChannel.cpp
+++ b/image/decoders/icon/android/nsIconChannel.cpp
@@ -113,17 +113,17 @@ moz_icon_to_channel(nsIURI* aURI, const 
   NS_ENSURE_SUCCESS(rv, rv);
 
   // nsIconProtocolHandler::NewChannel2 will provide the correct loadInfo for
   // this iconChannel. Use the most restrictive security settings for the
   // temporary loadInfo to make sure the channel can not be openend.
   nsCOMPtr<nsIPrincipal> nullPrincipal = NullPrincipal::Create();
   return NS_NewInputStreamChannel(aChannel,
                                   aURI,
-                                  stream,
+                                  stream.forget(),
                                   nullPrincipal,
                                   nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED,
                                   nsIContentPolicy::TYPE_INTERNAL_IMAGE,
                                   NS_LITERAL_CSTRING(IMAGE_ICON_MS));
 }
 
 nsresult
 nsIconChannel::Init(nsIURI* aURI)
--- a/image/decoders/icon/gtk/nsIconChannel.cpp
+++ b/image/decoders/icon/gtk/nsIconChannel.cpp
@@ -104,17 +104,17 @@ moz_gdk_pixbuf_to_channel(GdkPixbuf* aPi
   NS_ENSURE_SUCCESS(rv, rv);
 
   // nsIconProtocolHandler::NewChannel2 will provide the correct loadInfo for
   // this iconChannel. Use the most restrictive security settings for the
   // temporary loadInfo to make sure the channel can not be openend.
   nsCOMPtr<nsIPrincipal> nullPrincipal = NullPrincipal::Create();
   return NS_NewInputStreamChannel(aChannel,
                                   aURI,
-                                  stream,
+                                  stream.forget(),
                                   nullPrincipal,
                                   nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED,
                                   nsIContentPolicy::TYPE_INTERNAL_IMAGE,
                                   NS_LITERAL_CSTRING(IMAGE_ICON_MS));
 }
 
 static GtkWidget* gProtoWindow = nullptr;
 static GtkWidget* gStockImageWidget = nullptr;
--- a/js/public/HeapAPI.h
+++ b/js/public/HeapAPI.h
@@ -324,25 +324,29 @@ DispatchTyped(F f, GCCellPtr thing, Args
 
 namespace js {
 namespace gc {
 namespace detail {
 
 static MOZ_ALWAYS_INLINE uintptr_t*
 GetGCThingMarkBitmap(const uintptr_t addr)
 {
+    // Note: the JIT pre-barrier trampolines inline this code. Update that
+    // code too when making changes here!
     MOZ_ASSERT(addr);
     const uintptr_t bmap_addr = (addr & ~ChunkMask) | ChunkMarkBitmapOffset;
     return reinterpret_cast<uintptr_t*>(bmap_addr);
 }
 
 static MOZ_ALWAYS_INLINE void
 GetGCThingMarkWordAndMask(const uintptr_t addr, ColorBit colorBit,
                           uintptr_t** wordp, uintptr_t* maskp)
 {
+    // Note: the JIT pre-barrier trampolines inline this code. Update that
+    // code too when making changes here!
     MOZ_ASSERT(addr);
     const size_t bit = (addr & js::gc::ChunkMask) / js::gc::CellBytesPerMarkBit +
                        static_cast<uint32_t>(colorBit);
     MOZ_ASSERT(bit < js::gc::ChunkMarkBitmapBits);
     uintptr_t* bitmap = GetGCThingMarkBitmap(addr);
     const uintptr_t nbits = sizeof(*bitmap) * CHAR_BIT;
     *maskp = uintptr_t(1) << (bit % nbits);
     *wordp = &bitmap[bit / nbits];
--- a/js/public/StructuredClone.h
+++ b/js/public/StructuredClone.h
@@ -359,17 +359,17 @@ class JS_PUBLIC_API(JSAutoStructuredClon
 // The range of tag values the application may use for its own custom object types.
 #define JS_SCTAG_USER_MIN  ((uint32_t) 0xFFFF8000)
 #define JS_SCTAG_USER_MAX  ((uint32_t) 0xFFFFFFFF)
 
 #define JS_SCERR_RECURSION 0
 #define JS_SCERR_TRANSFERABLE 1
 #define JS_SCERR_DUP_TRANSFERABLE 2
 #define JS_SCERR_UNSUPPORTED_TYPE 3
-#define JS_SCERR_SAB_TRANSFERABLE 4
+#define JS_SCERR_SHMEM_TRANSFERABLE 4
 
 JS_PUBLIC_API(bool)
 JS_ReadUint32Pair(JSStructuredCloneReader* r, uint32_t* p1, uint32_t* p2);
 
 JS_PUBLIC_API(bool)
 JS_ReadBytes(JSStructuredCloneReader* r, void* p, size_t len);
 
 JS_PUBLIC_API(bool)
--- a/js/src/builtin/AtomicsObject.cpp
+++ b/js/src/builtin/AtomicsObject.cpp
@@ -43,16 +43,17 @@
  *
  * If none of those options are available then the build must disable
  * shared memory, or compilation will fail with a predictable error.
  */
 
 #include "builtin/AtomicsObject.h"
 
 #include "mozilla/Atomics.h"
+#include "mozilla/CheckedInt.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/ScopeExit.h"
 #include "mozilla/Unused.h"
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "jsnum.h"
@@ -86,23 +87,16 @@ ReportOutOfRange(JSContext* cx)
 {
     // Use JSMSG_BAD_INDEX here, it is what ToIndex uses for some cases that it
     // reports directly.
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
     return false;
 }
 
 static bool
-ReportCannotWait(JSContext* cx)
-{
-    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_ATOMICS_WAIT_NOT_ALLOWED);
-    return false;
-}
-
-static bool
 GetSharedTypedArray(JSContext* cx, HandleValue v,
                     MutableHandle<TypedArrayObject*> viewp)
 {
     if (!v.isObject())
         return ReportBadArrayType(cx);
     if (!v.toObject().is<TypedArrayObject>())
         return ReportBadArrayType(cx);
     viewp.set(&v.toObject().as<TypedArrayObject>());
@@ -530,22 +524,20 @@ js::atomics_isLockFree(JSContext* cx, un
 //
 // To test this, either run on eg Raspberry Pi Model 1, or invoke the ARM
 // simulator build with ARMHWCAP=vfp set.  Do not set any other flags; other
 // vfp/neon flags force ARMv7 to be set.
 
 int32_t
 js::atomics_add_asm_callout(wasm::Instance* instance, int32_t vt, int32_t offset, int32_t value)
 {
-    SharedMem<void*> heap = instance->memoryBase().cast<void*>();
-    size_t heapLength = instance->memoryLength();
-
-    if (size_t(offset) >= heapLength)
+    if (size_t(offset) >= instance->memory()->volatileMemoryLength())
         return 0;
 
+    SharedMem<void*> heap = instance->memoryBase().cast<void*>();
     switch (Scalar::Type(vt)) {
       case Scalar::Int8:
         return PerformAdd::operate(heap.cast<int8_t*>() + offset, value);
       case Scalar::Uint8:
         return PerformAdd::operate(heap.cast<uint8_t*>() + offset, value);
       case Scalar::Int16:
         return PerformAdd::operate(heap.cast<int16_t*>() + (offset >> 1), value);
       case Scalar::Uint16:
@@ -553,22 +545,20 @@ js::atomics_add_asm_callout(wasm::Instan
       default:
         MOZ_CRASH("Invalid size");
     }
 }
 
 int32_t
 js::atomics_sub_asm_callout(wasm::Instance* instance, int32_t vt, int32_t offset, int32_t value)
 {
-    SharedMem<void*> heap = instance->memoryBase().cast<void*>();
-    size_t heapLength = instance->memoryLength();
-
-    if (size_t(offset) >= heapLength)
+    if (size_t(offset) >= instance->memory()->volatileMemoryLength())
         return 0;
 
+    SharedMem<void*> heap = instance->memoryBase().cast<void*>();
     switch (Scalar::Type(vt)) {
       case Scalar::Int8:
         return PerformSub::operate(heap.cast<int8_t*>() + offset, value);
       case Scalar::Uint8:
         return PerformSub::operate(heap.cast<uint8_t*>() + offset, value);
       case Scalar::Int16:
         return PerformSub::operate(heap.cast<int16_t*>() + (offset >> 1), value);
       case Scalar::Uint16:
@@ -576,22 +566,20 @@ js::atomics_sub_asm_callout(wasm::Instan
       default:
         MOZ_CRASH("Invalid size");
     }
 }
 
 int32_t
 js::atomics_and_asm_callout(wasm::Instance* instance, int32_t vt, int32_t offset, int32_t value)
 {
-    SharedMem<void*> heap = instance->memoryBase().cast<void*>();
-    size_t heapLength = instance->memoryLength();
-
-    if (size_t(offset) >= heapLength)
+    if (size_t(offset) >= instance->memory()->volatileMemoryLength())
         return 0;
 
+    SharedMem<void*> heap = instance->memoryBase().cast<void*>();
     switch (Scalar::Type(vt)) {
       case Scalar::Int8:
         return PerformAnd::operate(heap.cast<int8_t*>() + offset, value);
       case Scalar::Uint8:
         return PerformAnd::operate(heap.cast<uint8_t*>() + offset, value);
       case Scalar::Int16:
         return PerformAnd::operate(heap.cast<int16_t*>() + (offset >> 1), value);
       case Scalar::Uint16:
@@ -599,22 +587,20 @@ js::atomics_and_asm_callout(wasm::Instan
       default:
         MOZ_CRASH("Invalid size");
     }
 }
 
 int32_t
 js::atomics_or_asm_callout(wasm::Instance* instance, int32_t vt, int32_t offset, int32_t value)
 {
-    SharedMem<void*> heap = instance->memoryBase().cast<void*>();
-    size_t heapLength = instance->memoryLength();
-
-    if (size_t(offset) >= heapLength)
+    if (size_t(offset) >= instance->memory()->volatileMemoryLength())
         return 0;
 
+    SharedMem<void*> heap = instance->memoryBase().cast<void*>();
     switch (Scalar::Type(vt)) {
       case Scalar::Int8:
         return PerformOr::operate(heap.cast<int8_t*>() + offset, value);
       case Scalar::Uint8:
         return PerformOr::operate(heap.cast<uint8_t*>() + offset, value);
       case Scalar::Int16:
         return PerformOr::operate(heap.cast<int16_t*>() + (offset >> 1), value);
       case Scalar::Uint16:
@@ -622,22 +608,20 @@ js::atomics_or_asm_callout(wasm::Instanc
       default:
         MOZ_CRASH("Invalid size");
     }
 }
 
 int32_t
 js::atomics_xor_asm_callout(wasm::Instance* instance, int32_t vt, int32_t offset, int32_t value)
 {
-    SharedMem<void*> heap = instance->memoryBase().cast<void*>();
-    size_t heapLength = instance->memoryLength();
-
-    if (size_t(offset) >= heapLength)
+    if (size_t(offset) >= instance->memory()->volatileMemoryLength())
         return 0;
 
+    SharedMem<void*> heap = instance->memoryBase().cast<void*>();
     switch (Scalar::Type(vt)) {
       case Scalar::Int8:
         return PerformXor::operate(heap.cast<int8_t*>() + offset, value);
       case Scalar::Uint8:
         return PerformXor::operate(heap.cast<uint8_t*>() + offset, value);
       case Scalar::Int16:
         return PerformXor::operate(heap.cast<int16_t*>() + (offset >> 1), value);
       case Scalar::Uint16:
@@ -645,22 +629,20 @@ js::atomics_xor_asm_callout(wasm::Instan
       default:
         MOZ_CRASH("Invalid size");
     }
 }
 
 int32_t
 js::atomics_xchg_asm_callout(wasm::Instance* instance, int32_t vt, int32_t offset, int32_t value)
 {
-    SharedMem<void*> heap = instance->memoryBase().cast<void*>();
-    size_t heapLength = instance->memoryLength();
-
-    if (size_t(offset) >= heapLength)
+    if (size_t(offset) >= instance->memory()->volatileMemoryLength())
         return 0;
 
+    SharedMem<void*> heap = instance->memoryBase().cast<void*>();
     switch (Scalar::Type(vt)) {
       case Scalar::Int8:
         return ExchangeOrStore<DoExchange>(Scalar::Int8, value, heap, offset);
       case Scalar::Uint8:
         return ExchangeOrStore<DoExchange>(Scalar::Uint8, value, heap, offset);
       case Scalar::Int16:
         return ExchangeOrStore<DoExchange>(Scalar::Int16, value, heap, offset>>1);
       case Scalar::Uint16:
@@ -668,22 +650,20 @@ js::atomics_xchg_asm_callout(wasm::Insta
       default:
         MOZ_CRASH("Invalid size");
     }
 }
 
 int32_t
 js::atomics_cmpxchg_asm_callout(wasm::Instance* instance, int32_t vt, int32_t offset, int32_t oldval, int32_t newval)
 {
-    SharedMem<void*> heap = instance->memoryBase().cast<void*>();
-    size_t heapLength = instance->memoryLength();
-
-    if (size_t(offset) >= heapLength)
+    if (size_t(offset) >= instance->memory()->volatileMemoryLength())
         return 0;
 
+    SharedMem<void*> heap = instance->memoryBase().cast<void*>();
     switch (Scalar::Type(vt)) {
       case Scalar::Int8:
         return CompareExchange(Scalar::Int8, oldval, newval, heap, offset);
       case Scalar::Uint8:
         return CompareExchange(Scalar::Uint8, oldval, newval, heap, offset);
       case Scalar::Int16:
         return CompareExchange(Scalar::Int16, oldval, newval, heap, offset>>1);
       case Scalar::Uint16:
@@ -741,16 +721,77 @@ class AutoLockFutexAPI
         unique_.reset();
     }
 
     js::UniqueLock<js::Mutex>& unique() { return *unique_; }
 };
 
 } // namespace js
 
+template<typename T>
+static FutexThread::WaitResult
+AtomicsWait(JSContext* cx, SharedArrayRawBuffer* sarb, uint32_t byteOffset, T value,
+            const mozilla::Maybe<mozilla::TimeDuration>& timeout)
+{
+    // Validation and other guards should ensure that this does not happen.
+    MOZ_ASSERT(sarb, "wait is only applicable to shared memory");
+
+    if (!cx->fx.canWait()) {
+        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_ATOMICS_WAIT_NOT_ALLOWED);
+        return FutexThread::WaitResult::Error;
+    }
+
+    SharedMem<T*> addr = sarb->dataPointerShared().cast<T*>() + (byteOffset / sizeof(T));
+
+    // This lock also protects the "waiters" field on SharedArrayRawBuffer,
+    // and it provides the necessary memory fence.
+    AutoLockFutexAPI lock;
+
+    if (jit::AtomicOperations::loadSafeWhenRacy(addr) != value)
+        return FutexThread::WaitResult::NotEqual;
+
+    FutexWaiter w(byteOffset, cx);
+    if (FutexWaiter* waiters = sarb->waiters()) {
+        w.lower_pri = waiters;
+        w.back = waiters->back;
+        waiters->back->lower_pri = &w;
+        waiters->back = &w;
+    } else {
+        w.lower_pri = w.back = &w;
+        sarb->setWaiters(&w);
+    }
+
+    FutexThread::WaitResult retval = cx->fx.wait(cx, lock.unique(), timeout);
+
+    if (w.lower_pri == &w) {
+        sarb->setWaiters(nullptr);
+    } else {
+        w.lower_pri->back = w.back;
+        w.back->lower_pri = w.lower_pri;
+        if (sarb->waiters() == &w)
+            sarb->setWaiters(w.lower_pri);
+    }
+
+    return retval;
+}
+
+FutexThread::WaitResult
+js::atomics_wait_impl(JSContext* cx, SharedArrayRawBuffer* sarb, uint32_t byteOffset,
+                      int32_t value, const mozilla::Maybe<mozilla::TimeDuration>& timeout)
+{
+    return AtomicsWait(cx, sarb, byteOffset, value, timeout);
+}
+
+FutexThread::WaitResult
+js::atomics_wait_impl(JSContext* cx, SharedArrayRawBuffer* sarb, uint32_t byteOffset,
+                      int64_t value, const mozilla::Maybe<mozilla::TimeDuration>& timeout)
+{
+    return AtomicsWait(cx, sarb, byteOffset, value, timeout);
+}
+
 bool
 js::atomics_wait(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     HandleValue objv = args.get(0);
     HandleValue idxv = args.get(1);
     HandleValue valv = args.get(2);
     HandleValue timeoutv = args.get(3);
@@ -775,65 +816,72 @@ js::atomics_wait(JSContext* cx, unsigned
         if (!mozilla::IsNaN(timeout_ms)) {
             if (timeout_ms < 0)
                 timeout = mozilla::Some(mozilla::TimeDuration::FromSeconds(0.0));
             else if (!mozilla::IsInfinite(timeout_ms))
                 timeout = mozilla::Some(mozilla::TimeDuration::FromMilliseconds(timeout_ms));
         }
     }
 
-    if (!cx->fx.canWait())
-        return ReportCannotWait(cx);
+    Rooted<SharedArrayBufferObject*> sab(cx, view->bufferShared());
+    // The computation will not overflow because range checks have been
+    // performed.
+    uint32_t byteOffset = offset * sizeof(int32_t) +
+                          (view->viewDataShared().cast<uint8_t*>().unwrap(/* arithmetic */) -
+                           sab->dataPointerShared().unwrap(/* arithmetic */));
 
-    // This lock also protects the "waiters" field on SharedArrayRawBuffer,
-    // and it provides the necessary memory fence.
+    switch (atomics_wait_impl(cx, sab->rawBufferObject(), byteOffset, value, timeout)) {
+      case FutexThread::WaitResult::NotEqual:
+        r.setString(cx->names().futexNotEqual);
+        return true;
+      case FutexThread::WaitResult::OK:
+        r.setString(cx->names().futexOK);
+        return true;
+      case FutexThread::WaitResult::TimedOut:
+        r.setString(cx->names().futexTimedOut);
+        return true;
+      case FutexThread::WaitResult::Error:
+        return false;
+      default:
+        MOZ_CRASH("Should not happen");
+    }
+}
+
+int64_t
+js::atomics_wake_impl(SharedArrayRawBuffer* sarb, uint32_t byteOffset, int64_t count)
+{
+    // Validation should ensure this does not happen.
+    MOZ_ASSERT(sarb, "wake is only applicable to shared memory");
+
     AutoLockFutexAPI lock;
 
-    SharedMem<int32_t*> addr = view->viewDataShared().cast<int32_t*>() + offset;
-    if (jit::AtomicOperations::loadSafeWhenRacy(addr) != value) {
-        r.setString(cx->names().futexNotEqual);
-        return true;
+    int64_t woken = 0;
+
+    FutexWaiter* waiters = sarb->waiters();
+    if (waiters && count) {
+        FutexWaiter* iter = waiters;
+        do {
+            FutexWaiter* c = iter;
+            iter = iter->lower_pri;
+            if (c->offset != byteOffset || !c->cx->fx.isWaiting())
+                continue;
+            c->cx->fx.wake(FutexThread::WakeExplicit);
+            // Overflow will be a problem only in two cases:
+            // (1) 128-bit systems with substantially more than 2^64 bytes of
+            //     memory per process, and a very lightweight
+            //     Atomics.waitAsync().  Obviously a future problem.
+            // (2) Bugs.
+            MOZ_RELEASE_ASSERT(woken < INT64_MAX);
+            ++woken;
+            if (count > 0)
+                --count;
+        } while (count && iter != waiters);
     }
 
-    Rooted<SharedArrayBufferObject*> sab(cx, view->bufferShared());
-    SharedArrayRawBuffer* sarb = sab->rawBufferObject();
-
-    FutexWaiter w(offset, cx);
-    if (FutexWaiter* waiters = sarb->waiters()) {
-        w.lower_pri = waiters;
-        w.back = waiters->back;
-        waiters->back->lower_pri = &w;
-        waiters->back = &w;
-    } else {
-        w.lower_pri = w.back = &w;
-        sarb->setWaiters(&w);
-    }
-
-    FutexThread::WaitResult result = FutexThread::FutexOK;
-    bool retval = cx->fx.wait(cx, lock.unique(), timeout, &result);
-    if (retval) {
-        switch (result) {
-          case FutexThread::FutexOK:
-            r.setString(cx->names().futexOK);
-            break;
-          case FutexThread::FutexTimedOut:
-            r.setString(cx->names().futexTimedOut);
-            break;
-        }
-    }
-
-    if (w.lower_pri == &w) {
-        sarb->setWaiters(nullptr);
-    } else {
-        w.lower_pri->back = w.back;
-        w.back->lower_pri = w.lower_pri;
-        if (sarb->waiters() == &w)
-            sarb->setWaiters(w.lower_pri);
-    }
-    return retval;
+    return woken;
 }
 
 bool
 js::atomics_wake(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     HandleValue objv = args.get(0);
     HandleValue idxv = args.get(1);
@@ -843,47 +891,37 @@ js::atomics_wake(JSContext* cx, unsigned
     Rooted<TypedArrayObject*> view(cx, nullptr);
     if (!GetSharedTypedArray(cx, objv, &view))
         return false;
     if (view->type() != Scalar::Int32)
         return ReportBadArrayType(cx);
     uint32_t offset;
     if (!GetTypedArrayIndex(cx, idxv, view, &offset))
         return false;
-    double count;
+    int64_t count;
     if (countv.isUndefined()) {
-        count = mozilla::PositiveInfinity<double>();
+        count = -1;
     } else {
-        if (!ToInteger(cx, countv, &count))
+        double dcount;
+        if (!ToInteger(cx, countv, &dcount))
             return false;
-        if (count < 0.0)
-            count = 0.0;
+        if (dcount < 0.0)
+            dcount = 0.0;
+        count = dcount > INT64_MAX ? -1 : int64_t(dcount);
     }
 
-    AutoLockFutexAPI lock;
-
     Rooted<SharedArrayBufferObject*> sab(cx, view->bufferShared());
-    SharedArrayRawBuffer* sarb = sab->rawBufferObject();
-    int32_t woken = 0;
+    // The computation will not overflow because range checks have been
+    // performed.
+    uint32_t byteOffset = offset * sizeof(int32_t) +
+                          (view->viewDataShared().cast<uint8_t*>().unwrap(/* arithmetic */) -
+                           sab->dataPointerShared().unwrap(/* arithmetic */));
 
-    FutexWaiter* waiters = sarb->waiters();
-    if (waiters && count > 0) {
-        FutexWaiter* iter = waiters;
-        do {
-            FutexWaiter* c = iter;
-            iter = iter->lower_pri;
-            if (c->offset != offset || !c->cx->fx.isWaiting())
-                continue;
-            c->cx->fx.wake(FutexThread::WakeExplicit);
-            ++woken;
-            --count;
-        } while (count > 0 && iter != waiters);
-    }
+    r.setNumber(double(atomics_wake_impl(sab->rawBufferObject(), byteOffset, count)));
 
-    r.setInt32(woken);
     return true;
 }
 
 /* static */ bool
 js::FutexThread::initialize()
 {
     MOZ_ASSERT(!lock_);
     lock_ = js_new<js::Mutex>(mutexid::FutexThread);
@@ -949,41 +987,41 @@ js::FutexThread::isWaiting()
     // WaitingNotifiedForInterrupt for a short time before it actually
     // wakes up and goes into WaitingInterrupted.  In those states the
     // worker is still waiting, and if an explicit wake arrives the
     // worker transitions to Woken.  See further comments in
     // FutexThread::wait().
     return state_ == Waiting || state_ == WaitingInterrupted || state_ == WaitingNotifiedForInterrupt;
 }
 
-bool
+FutexThread::WaitResult
 js::FutexThread::wait(JSContext* cx, js::UniqueLock<js::Mutex>& locked,
-                       mozilla::Maybe<mozilla::TimeDuration>& timeout, WaitResult* result)
+                      const mozilla::Maybe<mozilla::TimeDuration>& timeout)
 {
     MOZ_ASSERT(&cx->fx == this);
     MOZ_ASSERT(cx->fx.canWait());
     MOZ_ASSERT(state_ == Idle || state_ == WaitingInterrupted);
 
     // Disallow waiting when a runtime is processing an interrupt.
     // See explanation below.
 
     if (state_ == WaitingInterrupted) {
         UnlockGuard<Mutex> unlock(locked);
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_ATOMICS_WAIT_NOT_ALLOWED);
-        return false;
+        return WaitResult::Error;
     }
 
     // Go back to Idle after returning.
     auto onFinish = mozilla::MakeScopeExit([&] {
         state_ = Idle;
     });
 
     const bool isTimed = timeout.isSome();
 
-    auto finalEnd = timeout.map([](mozilla::TimeDuration& timeout) {
+    auto finalEnd = timeout.map([](const mozilla::TimeDuration& timeout) {
         return mozilla::TimeStamp::Now() + timeout;
     });
 
 
     // 4000s is about the longest timeout slice that is guaranteed to
     // work cross-platform.
     auto maxSlice = mozilla::TimeDuration::FromSeconds(4000.0);
 
@@ -1005,26 +1043,23 @@ js::FutexThread::wait(JSContext* cx, js:
             cond_->wait(locked);
         }
 
         switch (state_) {
           case FutexThread::Waiting:
             // Timeout or spurious wakeup.
             if (isTimed) {
                 auto now = mozilla::TimeStamp::Now();
-                if (now >= *finalEnd) {
-                    *result = FutexTimedOut;
-                    return true;
-                }
+                if (now >= *finalEnd)
+                    return WaitResult::TimedOut;
             }
             break;
 
           case FutexThread::Woken:
-            *result = FutexOK;
-            return true;
+            return WaitResult::OK;
 
           case FutexThread::WaitingNotifiedForInterrupt:
             // The interrupt handler may reenter the engine.  In that case
             // there are two complications:
             //
             // - The waiting thread is not actually waiting on the
             //   condition variable so we have to record that it
             //   should be woken when the interrupt handler returns.
@@ -1050,22 +1085,20 @@ js::FutexThread::wait(JSContext* cx, js:
             //   expedient.  Other solutions exist, see bug #1131943.  The
             //   code that performs the check is above, at the head of
             //   this function.
 
             state_ = WaitingInterrupted;
             {
                 UnlockGuard<Mutex> unlock(locked);
                 if (!cx->handleInterrupt())
-                    return false;
+                    return WaitResult::Error;
             }
-            if (state_ == Woken) {
-                *result = FutexOK;
-                return true;
-            }
+            if (state_ == Woken)
+                return WaitResult::OK;
             break;
 
           default:
             MOZ_CRASH("Bad FutexState in wait()");
         }
     }
 }
 
--- a/js/src/builtin/AtomicsObject.h
+++ b/js/src/builtin/AtomicsObject.h
@@ -64,35 +64,37 @@ public:
     void destroyInstance();
 
     // Parameters to wake().
     enum WakeReason {
         WakeExplicit,           // Being asked to wake up by another thread
         WakeForJSInterrupt      // Interrupt requested
     };
 
-    // Result code from wait().
-    enum WaitResult {
-        FutexOK,
-        FutexTimedOut
+    // Result codes from wait() and atomics_wait_impl().
+    enum class WaitResult {
+        Error,                  // Error has been reported, just propagate error signal
+        NotEqual,               // Did not wait because the values differed
+        OK,                     // Waited and was woken
+        TimedOut                // Waited and timed out
     };
 
     // Block the calling thread and wait.
     //
     // The futex lock must be held around this call.
     //
     // The timeout is the number of milliseconds, with fractional
     // times allowed; specify mozilla::Nothing() for an indefinite
     // wait.
     //
     // wait() will not wake up spuriously.  It will return true and
     // set *result to a return code appropriate for
     // Atomics.wait() on success, and return false on error.
-    MOZ_MUST_USE bool wait(JSContext* cx, js::UniqueLock<js::Mutex>& locked,
-                           mozilla::Maybe<mozilla::TimeDuration>& timeout, WaitResult* result);
+    MOZ_MUST_USE WaitResult wait(JSContext* cx, js::UniqueLock<js::Mutex>& locked,
+                                 const mozilla::Maybe<mozilla::TimeDuration>& timeout);
 
     // Wake the thread this is associated with.
     //
     // The futex lock must be held around this call.  (The sleeping
     // thread will not wake up until the caller of Atomics.wake()
     // releases the lock.)
     //
     // If the thread is not waiting then this method does nothing.
@@ -146,11 +148,28 @@ public:
 
     // A flag that controls whether waiting is allowed.
     ThreadLocalData<bool> canWait_;
 };
 
 JSObject*
 InitAtomicsClass(JSContext* cx, HandleObject obj);
 
+// Go to sleep if the int32_t value at the given address equals `value`.
+MOZ_MUST_USE FutexThread::WaitResult
+atomics_wait_impl(JSContext* cx, SharedArrayRawBuffer* sarb, uint32_t byteOffset, int32_t value,
+                  const mozilla::Maybe<mozilla::TimeDuration>& timeout);
+
+// Go to sleep if the int64_t value at the given address equals `value`.
+MOZ_MUST_USE FutexThread::WaitResult
+atomics_wait_impl(JSContext* cx, SharedArrayRawBuffer* sarb, uint32_t byteOffset, int64_t value,
+                  const mozilla::Maybe<mozilla::TimeDuration>& timeout);
+
+// Wake some waiters on the given address.  If `count` is negative then wake
+// all.  The return value is nonnegative and is the number of waiters woken.  If
+// the number of waiters woken exceeds INT64_MAX then this never returns.  If
+// `count` is nonnegative then the return value is never greater than `count`.
+MOZ_MUST_USE int64_t
+atomics_wake_impl(SharedArrayRawBuffer* sarb, uint32_t byteOffset, int64_t count);
+
 }  /* namespace js */
 
 #endif /* builtin_AtomicsObject_h */
--- a/js/src/builtin/ModuleObject.cpp
+++ b/js/src/builtin/ModuleObject.cpp
@@ -1348,18 +1348,18 @@ ModuleBuilder::processExport(frontend::P
     MOZ_ASSERT(pn->isKind(PNK_EXPORT) || pn->isKind(PNK_EXPORT_DEFAULT));
     MOZ_ASSERT(pn->getArity() == (pn->isKind(PNK_EXPORT) ? PN_UNARY : PN_BINARY));
 
     bool isDefault = pn->getKind() == PNK_EXPORT_DEFAULT;
     ParseNode* kid = isDefault ? pn->pn_left : pn->pn_kid;
 
     if (isDefault && pn->pn_right) {
         // This is an export default containing an expression.
-        RootedAtom localName(cx_, cx_->names().starDefaultStar);
-        RootedAtom exportName(cx_, cx_->names().default_);
+        HandlePropertyName localName = cx_->names().default_;
+        HandlePropertyName exportName = cx_->names().default_;
         return appendExportEntry(exportName, localName);
     }
 
     switch (kid->getKind()) {
       case PNK_EXPORT_SPEC_LIST:
         MOZ_ASSERT(!isDefault);
         for (ParseNode* spec = kid->pn_head; spec; spec = spec->pn_next) {
             MOZ_ASSERT(spec->isKind(PNK_EXPORT_SPEC));
--- a/js/src/builtin/Sorting.js
+++ b/js/src/builtin/Sorting.js
@@ -257,24 +257,16 @@ function MoveHoles(sparse, sparseLen, de
     for (var i = 0; i < denseLen; i++)
         sparse[i] = dense[i];
     for (var j = denseLen; j < sparseLen; j++)
         delete sparse[j];
 }
 
 // Iterative, bottom up, mergesort.
 function MergeSort(array, len, comparefn) {
-    // Until recently typed arrays had no sort method. To work around that
-    // many users passed them to Array.prototype.sort. Now that we have a
-    // typed array specific sorting method it makes sense to divert to it
-    // when possible.
-    if (IsPossiblyWrappedTypedArray(array)) {
-        return callFunction(TypedArraySort, array, comparefn);
-    }
-
     // To save effort we will do all of our work on a dense list,
     // then create holes at the end.
     var denseList = [];
     var denseLen = 0;
 
     for (var i = 0; i < len; i++) {
         if (i in array)
             _DefineDataProperty(denseList, denseLen++, array[i]);
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -27,17 +27,16 @@
 #include "builtin/Promise.h"
 #include "builtin/SelfHostingDefines.h"
 #ifdef DEBUG
 #include "frontend/TokenStream.h"
 #include "irregexp/RegExpAST.h"
 #include "irregexp/RegExpEngine.h"
 #include "irregexp/RegExpParser.h"
 #endif
-#include "jit/AtomicOperations.h"
 #include "jit/InlinableNatives.h"
 #include "js/Debug.h"
 #include "js/HashTable.h"
 #include "js/StructuredClone.h"
 #include "js/UbiNode.h"
 #include "js/UbiNodeBreadthFirst.h"
 #include "js/UbiNodeShortestPaths.h"
 #include "js/UniquePtr.h"
@@ -546,26 +545,16 @@ static bool
 WasmThreadsSupported(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 #ifdef ENABLE_WASM_THREAD_OPS
     bool isSupported = wasm::HasSupport(cx);
 #else
     bool isSupported = false;
 #endif
-
-    // NOTE!  When we land thread support, the following test and its comment
-    // should be moved into wasm::HasSupport() or wasm::HasCompilerSupport().
-
-    // Wasm threads require 8-byte lock-free atomics.  This guard will
-    // effectively disable Wasm support for some older devices, such as early
-    // ARMv6 and older MIPS.
-
-    isSupported = isSupported && jit::AtomicOperations::isLockfree8();
-
     args.rval().setBoolean(isSupported);
     return true;
 }
 
 static bool
 WasmCompileMode(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
@@ -3159,17 +3148,17 @@ SharedMemoryEnabled(JSContext* cx, unsig
     args.rval().setBoolean(cx->compartment()->creationOptions().getSharedMemoryAndAtomicsEnabled());
     return true;
 }
 
 static bool
 SharedArrayRawBufferCount(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    args.rval().setInt32(SharedArrayRawBuffer::liveBuffers());
+    args.rval().setInt32(LiveMappedBufferCount());
     return true;
 }
 
 static bool
 SharedArrayRawBufferRefcount(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() != 1 || !args[0].isObject()) {
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -10579,16 +10579,39 @@ BytecodeEmitter::emitClass(ParseNode* pn
     // The CONSTRUCTOR is left on stack if this is an expression.
 
     MOZ_ALWAYS_TRUE(sc->setLocalStrictMode(savedStrictness));
 
     return true;
 }
 
 bool
+BytecodeEmitter::emitExportDefault(ParseNode* pn)
+{
+    if (!emitTree(pn->pn_left))
+        return false;
+
+    if (pn->pn_right) {
+        if (!emitLexicalInitialization(pn->pn_right))
+            return false;
+
+        if (pn->pn_left->isDirectRHSAnonFunction()) {
+            HandlePropertyName name = cx->names().default_;
+            if (!setOrEmitSetFunName(pn->pn_left, name, FunctionPrefixKind::None))
+                return false;
+        }
+
+        if (!emit1(JSOP_POP))
+            return false;
+    }
+
+    return true;
+}
+
+bool
 BytecodeEmitter::emitTree(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage::WantValue */,
                           EmitLineNumberNote emitLineNote /* = EMIT_LINENOTE */)
 {
     if (!CheckRecursionLimit(cx))
         return false;
 
     EmitLevelManager elm(this);
 
@@ -10875,24 +10898,18 @@ BytecodeEmitter::emitTree(ParseNode* pn,
         if (pn->pn_kid->getKind() != PNK_EXPORT_SPEC_LIST) {
             if (!emitTree(pn->pn_kid))
                 return false;
         }
         break;
 
       case PNK_EXPORT_DEFAULT:
         MOZ_ASSERT(sc->isModuleContext());
-        if (!emitTree(pn->pn_kid))
-            return false;
-        if (pn->pn_right) {
-            if (!emitLexicalInitialization(pn->pn_right))
-                return false;
-            if (!emit1(JSOP_POP))
-                return false;
-        }
+        if (!emitExportDefault(pn))
+            return false;
         break;
 
       case PNK_EXPORT_FROM:
         MOZ_ASSERT(sc->isModuleContext());
         break;
 
       case PNK_CALLSITEOBJ:
         if (!emitCallSiteObject(pn))
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -827,14 +827,16 @@ struct MOZ_STACK_CLASS BytecodeEmitter
     MOZ_MUST_USE bool emitSuperPropOp(ParseNode* pn, JSOp op, bool isCall = false);
     MOZ_MUST_USE bool emitSuperElemOperands(ParseNode* pn,
                                             EmitElemOption opts = EmitElemOption::Get);
     MOZ_MUST_USE bool emitSuperElemOp(ParseNode* pn, JSOp op, bool isCall = false);
 
     MOZ_MUST_USE bool emitCallee(ParseNode* callee, ParseNode* call, bool spread, bool* callop);
 
     MOZ_MUST_USE bool emitPipeline(ParseNode* pn);
+
+    MOZ_MUST_USE bool emitExportDefault(ParseNode* pn);
 };
 
 } /* namespace frontend */
 } /* namespace js */
 
 #endif /* frontend_BytecodeEmitter_h */
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -3857,17 +3857,17 @@ Parser<ParseHandler, CharT>::functionStm
     }
 
     RootedPropertyName name(context);
     if (TokenKindIsPossibleIdentifier(tt)) {
         name = bindingIdentifier(yieldHandling);
         if (!name)
             return null();
     } else if (defaultHandling == AllowDefaultName) {
-        name = context->names().starDefaultStar;
+        name = context->names().default_;
         tokenStream.ungetToken();
     } else {
         /* Unnamed function expressions are forbidden in statement context. */
         error(JSMSG_UNNAMED_FUNCTION_STMT);
         return null();
     }
 
     // Note the declared name and check for early errors.
@@ -5689,26 +5689,29 @@ Parser<ParseHandler, CharT>::exportDefau
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::exportDefaultAssignExpr(uint32_t begin)
 {
     if (!abortIfSyntaxParser())
         return null();
 
-    RootedPropertyName name(context, context->names().starDefaultStar);
+    HandlePropertyName name = context->names().default_;
     Node nameNode = newName(name);
     if (!nameNode)
         return null();
     if (!noteDeclaredName(name, DeclarationKind::Const, pos()))
         return null();
 
     Node kid = assignExpr(InAllowed, YieldIsName, TripledotProhibited);
     if (!kid)
         return null();
+
+    handler.checkAndSetIsDirectRHSAnonFunction(kid);
+
     if (!matchOrInsertSemicolon())
         return null();
 
     Node node = handler.newExportDefaultDeclaration(kid, nameNode, TokenPos(begin, pos().end));
     if (!node)
         return null();
 
     if (!processExport(node))
@@ -7041,17 +7044,17 @@ Parser<ParseHandler, CharT>::classDefini
 
     RootedPropertyName name(context);
     if (TokenKindIsPossibleIdentifier(tt)) {
         name = bindingIdentifier(yieldHandling);
         if (!name)
             return null();
     } else if (classContext == ClassStatement) {
         if (defaultHandling == AllowDefaultName) {
-            name = context->names().starDefaultStar;
+            name = context->names().default_;
             tokenStream.ungetToken();
         } else {
             // Class statements must have a bound name
             error(JSMSG_UNNAMED_CLASS_STMT);
             return null();
         }
     } else {
         // Make sure to put it back, whatever it was
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -1755,17 +1755,17 @@ GCMarker::processMarkStackTop(SliceBudge
             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));
+                        size_t(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;
--- a/js/src/jit-test/tests/asm.js/testAsmJSWasmMixing.js
+++ b/js/src/jit-test/tests/asm.js/testAsmJSWasmMixing.js
@@ -18,8 +18,32 @@ if (!getBuildConfiguration().x64 && isSi
     assertAsmLinkFail(simdJS, this, null, asmJSBuf);
     assertAsmLinkFail(simdJS, this, null, wasmMem.buffer);
 
     var simdJSBuf = new ArrayBuffer(BUF_MIN);
     asmLink(simdJS, this, null, simdJSBuf);
     asmLink(simdJS, this, null, simdJSBuf);  // multiple SIMD.js instantiations succeed
     assertAsmLinkFail(asmJS, this, null, simdJSBuf);  // but not asm.js
 }
+
+setJitCompilerOption('asmjs.atomics.enable', 1);
+
+var sharedAsmJS = asmCompile('stdlib', 'ffis', 'buf',
+			     USE_ASM +
+			     'var i32 = new stdlib.Int32Array(buf);' +
+			     'var aload = stdlib.Atomics.load;' + // Declare shared memory
+			     'return {}');
+
+// A growable memory cannot be used for asm.js (nor would you think that is a
+// reasonable thing to do).
+
+{
+    let sharedWasmMem = new WebAssembly.Memory({initial:2, maximum:3, shared:true});
+    assertAsmLinkFail(sharedAsmJS, this, null, sharedWasmMem.buffer);
+}
+
+// A fixed-length shared memory cannot be used for asm.js, even though you might
+// think that ought to be possible.
+
+{
+    let sharedWasmMem = new WebAssembly.Memory({initial:2, maximum:2, shared:true});
+    assertAsmLinkFail(sharedAsmJS, this, null, sharedWasmMem.buffer);
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/assign-primitive-proxy-class-error.js
@@ -0,0 +1,14 @@
+'use strict';
+
+load(libdir + 'asserts.js');
+
+let hook = {}
+let Base = function() {}
+Base.prototype = new Proxy(Base.prototype, hook);
+class Derived extends Base {
+    testPrimitiveReceiver() {
+        super.foo = "Derived";
+    }
+}
+assertTypeErrorMessage(() => Derived.prototype.testPrimitiveReceiver.call(null),
+                       `can't assign to property "foo" on ({}): not an object`)
--- a/js/src/jit-test/tests/modules/export-declaration.js
+++ b/js/src/jit-test/tests/modules/export-declaration.js
@@ -311,17 +311,17 @@ program([
         null,
         false
     )
 ]).assert(parseAsModule("export let a = 1, b = 2;"));
 
 program([
     exportDeclaration(
         functionDeclaration(
-            ident("*default*"),
+            ident("default"),
             [],
             blockStatement([])
         ),
         null,
         null,
         true
     )
 ]).assert(parseAsModule("export default function() {}"));
@@ -337,17 +337,17 @@ program([
         null,
         true
     )
 ]).assert(parseAsModule("export default function foo() {}"));
 
 program([
     exportDeclaration(
         classDeclaration(
-            ident("*default*")
+            ident("default")
         ),
         null,
         null,
         true
     )
 ]).assert(parseAsModule("export default class { constructor() {} }"));
 
 program([
--- a/js/src/jit-test/tests/modules/export-entries.js
+++ b/js/src/jit-test/tests/modules/export-entries.js
@@ -35,21 +35,21 @@ testLocalExportEntries(
     [{exportName: 'foo', moduleRequest: null, importName: null, localName: 'foo'}]);
 
 testLocalExportEntries(
     'export default function f() {};',
     [{exportName: 'default', moduleRequest: null, importName: null, localName: 'f'}]);
 
 testLocalExportEntries(
     'export default function() {};',
-    [{exportName: 'default', moduleRequest: null, importName: null, localName: '*default*'}]);
+    [{exportName: 'default', moduleRequest: null, importName: null, localName: 'default'}]);
 
 testLocalExportEntries(
     'export default 42;',
-    [{exportName: 'default', moduleRequest: null, importName: null, localName: '*default*'}]);
+    [{exportName: 'default', moduleRequest: null, importName: null, localName: 'default'}]);
 
 testLocalExportEntries(
     'let x = 1; export {x};',
     [{exportName: 'x', moduleRequest: null, importName: null, localName: 'x'}]);
 
 testLocalExportEntries(
     'let v = 1; export {v as x};',
     [{exportName: 'x', moduleRequest: null, importName: null, localName: 'v'}]);
--- a/js/src/jit-test/tests/modules/module-environment.js
+++ b/js/src/jit-test/tests/modules/module-environment.js
@@ -18,17 +18,17 @@ testInitialEnvironment("if (true) { let 
 testInitialEnvironment("if (true) { var x = 1; }", []);
 testInitialEnvironment('function x() {}', ['x']);
 testInitialEnvironment("class x { constructor() {} }", []);
 
 // Exported bindings must be present in the environment.
 testInitialEnvironment('export var x = 1;', ['x']);
 testInitialEnvironment('export let x = 1;', ['x']);
 testInitialEnvironment('export default function x() {};', ['x']);
-testInitialEnvironment('export default 1;', ['*default*']);
-testInitialEnvironment('export default function() {};', ['*default*']);
+testInitialEnvironment('export default 1;', ['default']);
+testInitialEnvironment('export default function() {};', ['default']);
 testInitialEnvironment("export class x { constructor() {} }", ['x']);
 testInitialEnvironment('export default class x { constructor() {} };', ['x']);
-testInitialEnvironment('export default class { constructor() {} };', ['*default*']);
+testInitialEnvironment('export default class { constructor() {} };', ['default']);
 
 // Imports: namespace imports are present.
 testInitialEnvironment('import { x } from "m";', []);
 testInitialEnvironment('import * as x from "m";', ['x']);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/atomic.js
@@ -0,0 +1,572 @@
+if (!wasmThreadsSupported())
+    quit(0);
+
+const oob = /index out of bounds/;
+const unaligned = /unaligned memory access/;
+const RuntimeError = WebAssembly.RuntimeError;
+
+// Check that the output of wasmTextToBinary verifies correctly.
+
+let SHARED = 'shared';
+let UNSHARED = '';
+let sharedError = 'memory with atomic operations without shared memory';
+
+for (let [type,view] of [['i32','8_u'],['i32','16_u'],['i32',''],['i64','8_u'],['i64','16_u'],['i64','32_u'],['i64','']]) {
+    {
+	let text = (shared) => `(module (memory 1 1 ${shared})
+				 (func (result ${type}) (${type}.atomic.load${view} (i32.const 0)))
+				 (export "" 0))`;
+	assertEq(valText(text(SHARED)), true);
+	assertEq(valText(text(UNSHARED)), false);
+    }
+
+    {
+	let text = (shared) => `(module (memory 1 1 ${shared})
+				 (func (${type}.atomic.store${view} (i32.const 0) (${type}.const 1)))
+				 (export "" 0))`;
+	assertEq(valText(text(SHARED)), true);
+	assertEq(valText(text(UNSHARED)), false);
+    }
+
+    {
+	let text = (shared) => `(module (memory 1 1 ${shared})
+				 (func (result ${type})
+				  (${type}.atomic.rmw${view}.cmpxchg (i32.const 0) (${type}.const 1) (${type}.const 2)))
+				 (export "" 0))`;
+	assertEq(valText(text(SHARED)), true);
+	assertEq(valText(text(UNSHARED)), false);
+    }
+
+    for (let op of ['add','and','or','sub','xor','xchg']) {
+	// Operate with appropriately-typed value 1 on address 0
+	let text = (shared) => `(module (memory 1 1 ${shared})
+				 (func (result ${type}) (${type}.atomic.rmw${view}.${op} (i32.const 0) (${type}.const 1)))
+				 (export "" 0))`;
+
+	assertEq(valText(text(SHARED)), true);
+	assertEq(valText(text(UNSHARED)), false);
+    }
+}
+
+for (let type of ['i32', 'i64']) {
+    let text = (shared) => `(module (memory 1 1 ${shared})
+			     (func (result i32) (${type}.atomic.wait (i32.const 0) (${type}.const 1) (i64.const -1)))
+			     (export "" 0))`;
+    assertEq(valText(text(SHARED)), true);
+    assertEq(valText(text(UNSHARED)), false);
+}
+
+{
+    let text = (shared) => `(module (memory 1 1 ${shared})
+			     (func (result i32) (atomic.wake (i32.const 0) (i32.const 1)))
+			     (export "" 0))`;
+    assertEq(valText(text(SHARED)), true);
+    assertEq(valText(text(UNSHARED)), false);
+}
+
+// Required explicit alignment for WAIT is the size of the datum
+
+for (let [type,align,good] of [['i32',1,false],['i32',2,false],['i32',4,true],['i32',8,false],
+			       ['i64',1,false],['i64',2,false],['i64',4,false],['i64',8,true]])
+{
+    let text = `(module (memory 1 1 shared)
+		 (func (result i32) (${type}.atomic.wait align=${align} (i32.const 0) (${type}.const 1) (i64.const -1)))
+		 (export "" 0))`;
+    assertEq(valText(text), good);
+}
+
+// Required explicit alignment for WAKE is 4
+
+for (let align of [1, 2, 4, 8]) {
+    let text = `(module (memory 1 1 shared)
+		 (func (result i32) (atomic.wake align=${align} (i32.const 0) (i32.const 1)))
+		 (export "" 0))`;
+    assertEq(valText(text), align == 4);
+}
+
+// Check that the text output is sane.
+
+for (let [type,view] of [['i32','8_u'],['i32','16_u'],['i32',''],['i64','8_u'],['i64','16_u'],['i64','32_u'],['i64','']]) {
+    let addr = "i32.const 48";
+    let value = `${type}.const 1`;
+    let value2 = `${type}.const 2`;
+    for (let op of ["add", "and", "or", "xor", "xchg"]) {
+	let operator = `${type}.atomic.rmw${view}.${op}`;
+	let text = `(module (memory 1 1 shared)
+		     (func (result ${type}) (${operator} (${addr}) (${value})))
+		     (export "" 0))`;
+	checkRoundTrip(text, [addr, value, operator]);
+    }
+    {
+	let operator = `${type}.atomic.rmw${view}.cmpxchg`;
+	let text = `(module (memory 1 1 shared)
+		     (func (result ${type}) (${operator} (${addr}) (${value}) (${value2})))
+		     (export "" 0))`;
+	checkRoundTrip(text, [addr, value, value2, operator]);
+    }
+    {
+	let operator = `${type}.atomic.load${view}`;
+	let text = `(module (memory 1 1 shared)
+		     (func (result ${type}) (${operator} (${addr})))
+		     (export "" 0))`;
+	checkRoundTrip(text, [addr, operator]);
+    }
+    {
+	let operator = `${type}.atomic.store${view}`;
+	let text = `(module (memory 1 1 shared)
+		     (func (${operator} (${addr}) (${value})))
+		     (export "" 0))`;
+	checkRoundTrip(text, [addr, value, operator]);
+    }
+}
+
+for (let type of ['i32', 'i64']) {
+    let addr = "i32.const 48";
+    let operator = `${type}.atomic.wait`
+    let value = `${type}.const 1`;
+    let timeout = "i64.const 314159";
+    let text = `(module (memory 1 1 shared)
+		 (func (result i32) (${operator} (${addr}) (${value}) (${timeout})))
+		 (export "" 0))`;
+    checkRoundTrip(text, [addr, value, timeout, operator]);
+}
+
+{
+    let addr = "i32.const 48";
+    let operator = "atomic.wake"
+    let count = "i32.const 1";
+    let text = `(module (memory 1 1 shared)
+		 (func (result i32) (${operator} (${addr}) (${count})))
+		 (export "" 0))`;
+    checkRoundTrip(text, [addr, count, operator]);
+}
+
+function valText(text) {
+    return WebAssembly.validate(wasmTextToBinary(text));
+}
+
+function checkRoundTrip(text, ss) {
+    let input = wasmTextToBinary(text);
+    let output = wasmBinaryToText(input).split("\n").map(String.trim);
+    for (let s of output) {
+	if (ss.length == 0)
+	    break;
+	if (s.match(ss[0]))
+	    ss.shift();
+    }
+    assertEq(ss.length, 0);
+}
+
+// Test that atomic operations work.
+
+function I64(hi, lo) {
+    this.high = hi;
+    this.low = lo;
+}
+I64.prototype.toString = function () {
+    return "(" + this.high.toString(16) + " " + this.low.toString(16) + ")";
+}
+
+function Uint64Array(arg) {
+    let buffer = arg;
+    if (typeof arg == "number")
+	buffer = new ArrayBuffer(arg*8);
+    this.buf = buffer;
+    this.elem = new Uint32Array(buffer);
+}
+
+Uint64Array.BYTES_PER_ELEMENT = 8;
+
+Uint8Array.prototype.read = function (n) { return this[n] }
+Uint16Array.prototype.read = function (n) { return this[n] }
+Uint32Array.prototype.read = function (n) { return this[n] }
+Uint64Array.prototype.read = function (n) {
+    return new I64(this.elem[n*2+1], this.elem[n*2]);
+}
+
+Uint8Array.prototype.write = function (n,v) { this[n] = v }
+Uint16Array.prototype.write = function (n,v) { this[n] = v }
+Uint32Array.prototype.write = function (n,v) { this[n] = v}
+Uint64Array.prototype.write = function (n,v) {
+    if (typeof v == "number") {
+	// Note, this chops v if v is too large
+	this.elem[n*2] = v;
+	this.elem[n*2+1] = 0;
+    } else {
+	this.elem[n*2] = v.low;
+	this.elem[n*2+1] = v.high;
+    }
+}
+
+// Widen a one-byte value to a k-byte value where k is TA's width.
+// Complementation leads to better error checking, probably.
+
+function widen(TA, value, complement = true) {
+    let n = value;
+    let s = "";
+    for ( let i=0; i < Math.min(TA.BYTES_PER_ELEMENT, 4); i++ ) {
+	let v = (256 + n).toString(16);
+	s = s + v.substring(v.length-2);
+	if (complement)
+	    n = ~n;
+    }
+    if (TA.BYTES_PER_ELEMENT == 8)
+	s = s + s;
+    s = "0x" + s;
+
+    n = value;
+    let num = 0;
+    for ( let i=0; i < Math.min(TA.BYTES_PER_ELEMENT, 4); i++ ) {
+	num = (num << 8) | (n & 255);
+	if (complement)
+	    n = ~n;
+    }
+    num = num >>> 0;
+
+    if (TA.BYTES_PER_ELEMENT == 8) {
+	return [s, new I64(num, num)];
+    } else {
+	return [s, num];
+    }
+}
+
+// Atomic RMW ops are sometimes used for effect, sometimes for their value, and
+// in SpiderMonkey code generation differs for the two cases, so we need to test
+// both.  Also, there may be different paths for constant addresses/operands and
+// variable ditto, so test as many combinations as possible.
+
+var RMWOperation =
+{
+    loadStoreModule(type, view, address, operand) {
+	let bin = wasmTextToBinary(
+	    `(module
+	      (memory (import "" "memory") 1 1 shared)
+	      (func (export "st") (param i32)
+	       (${type}.atomic.store${view} ${address} ${operand}))
+	      (func $ld (param i32) (result ${type})
+	       (${type}.atomic.load${view} ${address}))
+	      (func (export "ld") (param i32) (result i32)
+	       (${type}.eq (call $ld (get_local 0)) ${operand})))`);
+	let mod = new WebAssembly.Module(bin);
+	let mem = new WebAssembly.Memory({initial: 1, maximum: 1, shared: true});
+	let ins = new WebAssembly.Instance(mod, {"": {memory: mem}});
+	return [mem, ins.exports.ld, ins.exports.st];
+    },
+
+    opModuleEffect(type, view, address, op, operand, ignored) {
+	let bin = wasmTextToBinary(
+	    `(module
+	      (memory (import "" "memory") 1 1 shared)
+	      (func (export "f") (param i32) (result i32)
+	       (drop (${type}.atomic.rmw${view}.${op} ${address} ${operand}))
+	       (i32.const 1)))`);
+	let mod = new WebAssembly.Module(bin);
+	let mem = new WebAssembly.Memory({initial: 1, maximum: 1, shared: true});
+	let ins = new WebAssembly.Instance(mod, {"": {memory: mem}});
+	return [mem, ins.exports.f];
+    },
+
+    opModuleReturned(type, view, address, op, operand, expected) {
+	let bin = wasmTextToBinary(
+	    `(module
+	      (memory (import "" "memory") 1 1 shared)
+	      (func $_f (param i32) (result ${type})
+	       (${type}.atomic.rmw${view}.${op} ${address} ${operand}))
+	      (func (export "f") (param i32) (result i32)
+	       (${type}.eq (call $_f (get_local 0)) (${type}.const ${expected}))))`);
+	let mod = new WebAssembly.Module(bin);
+	let mem = new WebAssembly.Memory({initial: 1, maximum: 1, shared: true});
+	let ins = new WebAssembly.Instance(mod, {"": {memory: mem}});
+	return [mem, ins.exports.f];
+    },
+
+    cmpxchgModuleEffect(type, view, address, operand1, operand2, ignored) {
+	let bin = wasmTextToBinary(
+	    `(module
+	      (memory (import "" "memory") 1 1 shared)
+	      (func (export "f") (param i32)
+	       (drop (${type}.atomic.rmw${view}.cmpxchg ${address} ${operand1} ${operand2}))))`);
+	let mod = new WebAssembly.Module(bin);
+	let mem = new WebAssembly.Memory({initial: 1, maximum: 1, shared: true});
+	let ins = new WebAssembly.Instance(mod, {"": {memory: mem}});
+	return [mem, ins.exports.f];
+    },
+
+    cmpxchgModuleReturned(type, view, address, operand1, operand2, expected) {
+	let bin = wasmTextToBinary(
+	    `(module
+	      (memory (import "" "memory") 1 1 shared)
+	      (func $_f (param i32) (result ${type})
+	       (${type}.atomic.rmw${view}.cmpxchg ${address} ${operand1} ${operand2}))
+	      (func (export "f") (param i32) (result i32)
+	       (${type}.eq (call $_f (get_local 0)) (${type}.const ${expected}))))`);
+	let mod = new WebAssembly.Module(bin);
+	let mem = new WebAssembly.Memory({initial: 1, maximum: 1, shared: true});
+	let ins = new WebAssembly.Instance(mod, {"": {memory: mem}});
+	return [mem, ins.exports.f];
+    },
+
+    assertZero(array, LOC) {
+	for ( let i=0 ; i < 100 ; i++ ) {
+	    if (i != LOC)
+		assertNum(array.read(i), 0);
+	}
+    },
+
+    run() {
+	const LOC = 13;		// The cell we operate on
+	const OPD1 = 37;	// Sometimes we'll put an operand here
+	const OPD2 = 42;	// Sometimes we'll put another operand here
+
+	for ( let [type, variations] of
+	          [["i32", [[Uint8Array,"8_u"], [Uint16Array,"16_u"], [Uint32Array,""]]],
+		   ["i64", [[Uint8Array,"8_u"], [Uint16Array,"16_u"], [Uint32Array,"32_u"], [Uint64Array,""]]]] )
+	{
+	    for ( let [TA, view] of variations )
+	    {
+		for ( let addr of [`(i32.const ${LOC * TA.BYTES_PER_ELEMENT})`,
+				   `(get_local 0)`] )
+		{
+		    for ( let [initial, operand] of [[0x12, 0x37]] )
+		    {
+			let [opd_str, opd_num] = widen(TA, operand);
+			for ( let rhs of [`(${type}.const ${opd_str})`,
+					  `(${type}.load${view} (i32.const ${OPD1 * TA.BYTES_PER_ELEMENT}))`] )
+			{
+			    let [mem, ld, st] = this.loadStoreModule(type, view, addr, rhs);
+			    let array = new TA(mem.buffer);
+			    array.write(OPD1, opd_num);
+			    array.write(LOC, initial);
+			    st(LOC * TA.BYTES_PER_ELEMENT);
+			    let res = ld(LOC * TA.BYTES_PER_ELEMENT);
+			    assertEq(res, 1);
+			    assertNum(array.read(LOC), opd_num);
+			    array.write(OPD1, 0);
+			    this.assertZero(array, LOC);
+			}
+		    }
+
+		    for ( let [op, initial, operand, expected] of [["add", 37, 5, 42],
+								   ["sub", 42, 5, 37],
+								   ["and", 0x45, 0x13, 0x01],
+								   ["or",  0x45, 0x13, 0x57],
+								   ["xor", 0x45, 0x13, 0x56],
+								   ["xchg", 0x45, 0x13, 0x13]] )
+		    {
+			let complement = op == "xchg";
+			let [ini_str, ini_num] = widen(TA, initial, complement);
+			let [opd_str, opd_num] = widen(TA, operand, complement);
+			let [exp_str, exp_num] = widen(TA, expected, complement);
+			for ( let rhs of [`(${type}.const ${opd_str})`,
+					  `(${type}.load${view} (i32.const ${OPD1 * TA.BYTES_PER_ELEMENT}))`] )
+			{
+			    for ( let [generateIt, checkIt] of [["opModuleEffect", false], ["opModuleReturned", true]] )
+			    {
+				let [mem, f] = this[generateIt](type, view, addr, op, rhs, ini_str);
+				let array = new TA(mem.buffer);
+				array.write(OPD1, opd_num);
+				array.write(LOC, ini_num);
+				let res = f(LOC * TA.BYTES_PER_ELEMENT);
+				if (checkIt)
+				    assertEq(res, 1);
+				assertNum(array.read(LOC), exp_num);
+				array.write(OPD1, 0);
+				this.assertZero(array, LOC);
+			    }
+			}
+		    }
+
+		    for ( let [initial, operand1, operand2, expected] of [[33, 33, 44, 44], [33, 44, 55, 33]] )
+		    {
+			let [ini_str, ini_num] = widen(TA, initial);
+			let [opd1_str, opd1_num] = widen(TA, operand1);
+			let [opd2_str, opd2_num] = widen(TA, operand2);
+			let [exp_str, exp_num] = widen(TA, expected);
+			for ( let op1 of [`(${type}.const ${opd1_str})`,
+					  `(${type}.load${view} (i32.const ${OPD1 * TA.BYTES_PER_ELEMENT}))`] )
+			{
+			    for ( let op2 of [`(${type}.const ${opd2_str})`,
+					      `(${type}.load${view} (i32.const ${OPD2 * TA.BYTES_PER_ELEMENT}))`] )
+			    {
+				for ( let [generateIt, checkIt] of [["cmpxchgModuleEffect", false], ["cmpxchgModuleReturned", true]] )
+				{
+				    let [mem, f] = this[generateIt](type, view, addr, op1, op2, ini_str);
+				    let array = new TA(mem.buffer);
+				    array.write(OPD1, opd1_num);
+				    array.write(OPD2, opd2_num);
+				    array.write(LOC, ini_num);
+				    let res = f(LOC * TA.BYTES_PER_ELEMENT);
+				    if (checkIt)
+					assertEq(res, 1);
+				    assertNum(array.read(13), exp_num);
+				    array.write(OPD1, 0);
+				    array.write(OPD2, 0);
+				    this.assertZero(array, LOC);
+				}
+			    }
+			}
+		    }
+		}
+	    }
+	}
+    }
+};
+
+RMWOperation.run();
+
+function assertNum(a, b) {
+    if (typeof a == "number" && typeof b == "number")
+	assertEq(a, b);
+    else if (typeof a == "number") {
+	assertEq(a, b.low);
+	assertEq(0, b.high);
+    } else if (typeof b == "number") {
+	assertEq(a.low, b);
+	assertEq(a.high, 0);
+    } else {
+	assertEq(a.high, b.high);
+	assertEq(a.low, b.low);
+    }
+}
+
+// Test bounds and alignment checking on atomic ops
+
+var BoundsAndAlignment =
+{
+    loadModule(type, ext, offset) {
+	return wasmEvalText(
+	    `(module
+	      (memory 1 1 shared)
+	      (func $0 (param i32) (result ${type})
+	       (${type}.atomic.load${ext} offset=${offset} (get_local 0)))
+	      (func (export "f") (param i32)
+	       (drop (call $0 (get_local 0)))))
+	    `).exports.f;
+    },
+
+    loadModuleIgnored(type, ext, offset) {
+	return wasmEvalText(
+	    `(module
+	      (memory 1 1 shared)
+	      (func (export "f") (param i32)
+	       (drop (${type}.atomic.load${ext} offset=${offset} (get_local 0)))))
+	    `).exports.f;
+    },
+
+    storeModule(type, ext, offset) {
+	return wasmEvalText(
+	    `(module
+	      (memory 1 1 shared)
+	      (func (export "f") (param i32)
+	       (${type}.atomic.store${ext} offset=${offset} (get_local 0) (${type}.const 37))))
+	    `).exports.f;
+    },
+
+    opModule(type, ext, offset, op) {
+	return wasmEvalText(
+	    `(module
+	      (memory 1 1 shared)
+	      (func $0 (param i32) (result ${type})
+	       (${type}.atomic.rmw${ext}.${op} offset=${offset} (get_local 0) (${type}.const 37)))
+	      (func (export "f") (param i32)
+	       (drop (call $0 (get_local 0)))))
+	    `).exports.f;
+    },
+
+    opModuleForEffect(type, ext, offset, op) {
+	return wasmEvalText(
+	    `(module
+	      (memory 1 1 shared)
+	      (func (export "f") (param i32)
+	       (drop (${type}.atomic.rmw${ext}.${op} offset=${offset} (get_local 0) (${type}.const 37)))))
+	    `).exports.f;
+    },
+
+    cmpxchgModule(type, ext, offset) {
+	return wasmEvalText(
+	    `(module
+	      (memory 1 1 shared)
+	      (func $0 (param i32) (result ${type})
+	       (${type}.atomic.rmw${ext}.cmpxchg offset=${offset} (get_local 0) (${type}.const 37) (${type}.const 42)))
+	      (func (export "f") (param i32)
+	       (drop (call $0 (get_local 0)))))
+	    `).exports.f;
+    },
+
+    run() {
+	for ( let [type, variations] of [["i32", [["8_u", 1], ["16_u", 2], ["", 4]]],
+					 ["i64", [["8_u",1], ["16_u",2], ["32_u",4], ["",8]]]] )
+	{
+	    for ( let [ext,size] of variations )
+	    {
+		// Aligned but out-of-bounds
+		let addrs = [[65536, 0, oob], [65536*2, 0, oob], [65532, 4, oob],
+			     [65533, 3, oob], [65534, 2, oob], [65535, 1, oob]];
+
+		// In-bounds but unaligned
+		for ( let i=1 ; i < size ; i++ )
+		    addrs.push([65520, i, unaligned]);
+
+		// Both out-of-bounds and unaligned.  The spec leaves it unspecified
+		// whether we see the OOB message or the unaligned message (they are
+		// both "traps").  In Firefox, the unaligned check comes first.
+		for ( let i=1 ; i < size ; i++ )
+		    addrs.push([65536, i, unaligned]);
+
+		for ( let [ base, offset, re ] of addrs )
+		{
+		    assertErrorMessage(() => this.loadModule(type, ext, offset)(base), RuntimeError, re);
+		    assertErrorMessage(() => this.loadModuleIgnored(type, ext, offset)(base), RuntimeError, re);
+		    assertErrorMessage(() => this.storeModule(type, ext, offset)(base), RuntimeError, re);
+		    for ( let op of [ "add", "sub", "and", "or", "xor", "xchg" ]) {
+			assertErrorMessage(() => this.opModule(type, ext, offset, op)(base), RuntimeError, re);
+			assertErrorMessage(() => this.opModuleForEffect(type, ext, offset, op)(base), RuntimeError, re);
+		    }
+		    assertErrorMessage(() => this.cmpxchgModule(type, ext, offset)(base), RuntimeError, re);
+		}
+	    }
+	}
+    }
+}
+
+BoundsAndAlignment.run();
+
+// Bounds and alignment checks on wait and wake
+
+assertErrorMessage(() => wasmEvalText(`(module (memory 1 1 shared)
+					(func (param i32) (result i32)
+					 (i32.atomic.wait (get_local 0) (i32.const 1) (i64.const -1)))
+					(export "" 0))`).exports[""](65536),
+		   RuntimeError, oob);
+
+assertErrorMessage(() => wasmEvalText(`(module (memory 1 1 shared)
+					(func (param i32) (result i32)
+					 (i64.atomic.wait (get_local 0) (i64.const 1) (i64.const -1)))
+					(export "" 0))`).exports[""](65536),
+		   RuntimeError, oob);
+
+assertErrorMessage(() => wasmEvalText(`(module (memory 1 1 shared)
+					(func (param i32) (result i32)
+					 (i32.atomic.wait (get_local 0) (i32.const 1) (i64.const -1)))
+					(export "" 0))`).exports[""](65501),
+		   RuntimeError, unaligned);
+
+assertErrorMessage(() => wasmEvalText(`(module (memory 1 1 shared)
+					(func (param i32) (result i32)
+					 (i64.atomic.wait (get_local 0) (i64.const 1) (i64.const -1)))
+					(export "" 0))`).exports[""](65501),
+		   RuntimeError, unaligned);
+
+assertErrorMessage(() => wasmEvalText(`(module (memory 1 1 shared)
+					(func (param i32) (result i32)
+					 (atomic.wake (get_local 0) (i32.const 1)))
+					(export "" 0))`).exports[""](65536),
+		   RuntimeError, oob);
+
+// Minimum run-time alignment for WAKE is 4
+for (let addr of [1,2,3,5,6,7]) {
+    assertErrorMessage(() => wasmEvalText(`(module (memory 1 1 shared)
+					    (func (export "f") (param i32) (result i32)
+					     (atomic.wake (get_local 0) (i32.const 1))))`).exports.f(addr),
+		       RuntimeError, unaligned);
+}
--- a/js/src/jit-test/tests/wasm/binary.js
+++ b/js/src/jit-test/tests/wasm/binary.js
@@ -439,23 +439,41 @@ for (var bad of [0xff, 0, 1, 0x3f])
 // Ensure all invalid opcodes rejected
 for (let i = FirstInvalidOpcode; i <= LastInvalidOpcode; i++) {
     let binary = moduleWithSections([v2vSigSection, declSection([0]), bodySection([funcBody({locals:[], body:[i]})])]);
     assertErrorMessage(() => wasmEval(binary), CompileError, /unrecognized opcode/);
     assertEq(WebAssembly.validate(binary), false);
 }
 
 // Prefixed opcodes
+
+function checkIllegalPrefixed(prefix, opcode) {
+    let binary = moduleWithSections([v2vSigSection, declSection([0]), bodySection([funcBody({locals:[], body:[prefix, opcode]})])]);
+    assertErrorMessage(() => wasmEval(binary), CompileError, /unrecognized opcode/);
+    assertEq(WebAssembly.validate(binary), false);
+}
+
+// Illegal AtomicPrefix opcodes
+//
+// June 2017 threads draft:
+//
+//  0x00 .. 0x02 are wait/wake ops
+//  0x10 .. 0x4f are primitive atomic ops
+
+for (let i = 3; i < 0x10; i++)
+    checkIllegalPrefixed(AtomicPrefix, i);
+
+for (let i = 0x4f; i < 0x100; i++)
+    checkIllegalPrefixed(AtomicPrefix, i);
+
+// Illegal MozPrefix opcodes (all of them)
+for (let i = 0; i < 256; i++)
+    checkIllegalPrefixed(MozPrefix, i);
+
 for (let prefix of [AtomicPrefix, MozPrefix]) {
-    for (let i = 0; i <= 255; i++) {
-        let binary = moduleWithSections([v2vSigSection, declSection([0]), bodySection([funcBody({locals:[], body:[prefix, i]})])]);
-        assertErrorMessage(() => wasmEval(binary), CompileError, /unrecognized opcode/);
-        assertEq(WebAssembly.validate(binary), false);
-    }
-
     // Prefix without a subsequent opcode
     let binary = moduleWithSections([v2vSigSection, declSection([0]), bodySection([funcBody({locals:[], body:[prefix]})])]);
     assertErrorMessage(() => wasmEval(binary), CompileError, /unrecognized opcode/);
     assertEq(WebAssembly.validate(binary), false);
 }
 
 // Checking stack trace.
 function runStackTraceTest(moduleName, funcNames, expectedName) {
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/memory-cloning.js
@@ -0,0 +1,87 @@
+// Basic structured cloning tests (specific to SpiderMonkey shell)
+
+if (!wasmThreadsSupported())
+    quit(0);
+
+// Should *not* be possible to serialize and deserialize memories that are not
+// shared, whether we transfer them or not.
+
+{
+    let mem1 = new WebAssembly.Memory({initial: 2, maximum: 4});
+    assertErrorMessage(() => serialize(mem1),
+		       TypeError,
+		       /unsupported type for structured data/);
+    assertErrorMessage(() => serialize(mem1, [mem1]),
+		       TypeError,
+		       /invalid transferable array for structured clone/);
+}
+
+// Should be possible to serialize and deserialize memories that are shared, and
+// observe shared effects.
+
+{
+    let mem1 = new WebAssembly.Memory({initial: 2, maximum: 4, shared: true});
+    let buf1 = mem1.buffer;
+
+    // Serialization and deserialization of shared memories work:
+
+    let mem2 = deserialize(serialize(mem1));
+    assertEq(mem2 instanceof WebAssembly.Memory, true);
+    let buf2 = mem2.buffer;
+    assertEq(buf2 instanceof SharedArrayBuffer, true);
+
+    assertEq(buf1 !== buf2, true);
+    assertEq(buf1.byteLength, buf2.byteLength);
+
+    // Effects to one buffer must be reflected in the other:
+
+    let v1 = new Int32Array(buf1);
+    let v2 = new Int32Array(buf2);
+
+    v1[37] = 0x12345678;
+    assertEq(v2[37], 0x12345678);
+
+    // Growth in a memory is reflected in its clone:
+
+    let index = 2*65536 + 200;
+    let access = wasmEvalText(`(module
+				(memory (import "" "memory") 2 4 shared)
+				(func (export "l") (result i32)
+				 (i32.load (i32.const ${index}))))`,
+			      {"": {memory: mem2}}).exports.l;
+
+    // initially mem2 cannot be accessed at index
+    assertErrorMessage(access, WebAssembly.RuntimeError, /out of bounds/);
+
+    // then we grow mem1
+    wasmEvalText(`(module
+		   (memory (import "" "memory") 2 4 shared)
+		   (func (export "g") (drop (grow_memory (i32.const 1)))))`,
+		 {"": {memory: mem1}}).exports.g();
+
+    // after growing mem1, mem2 can be accessed at index
+    assertEq(access(), 0);
+}
+
+// Should not be possible to transfer a shared memory
+
+{
+    let mem1 = new WebAssembly.Memory({initial: 2, maximum: 4, shared: true});
+    assertErrorMessage(() => serialize(mem1, [mem1]),
+		       TypeError,
+		       /Shared memory objects must not be in the transfer list/);
+
+}
+
+// When serializing and deserializing a SAB extracted from a memory, the length
+// of the SAB should not change even if the memory was grown after serialization
+// and before deserialization.
+
+{
+    let mem = new WebAssembly.Memory({initial: 2, maximum: 4, shared: true});
+    let buf = mem.buffer;
+    let clonedbuf = serialize(buf);
+    mem.grow(1);
+    let buf2 = deserialize(clonedbuf);
+    assertEq(buf.byteLength, buf2.byteLength);
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/memory-sharing-off.js
@@ -0,0 +1,26 @@
+// |jit-test| --shared-memory=off
+
+if (!wasmThreadsSupported())
+    quit(0);
+
+// A module using shared memory should be convertable from text to binary even
+// if shared memory is disabled.
+
+var bin = wasmTextToBinary('(module (memory 1 1 shared))');
+
+// But we should not be able to validate it:
+
+assertEq(WebAssembly.validate(bin), false);
+
+// Nor to compile it:
+
+assertErrorMessage(() => new WebAssembly.Module(bin),
+		   WebAssembly.CompileError,
+		   /shared memory is disabled/);
+
+// We also should not be able to create a shared memory by itself:
+
+assertErrorMessage(() => new WebAssembly.Memory({initial: 1, maximum: 1, shared: true}),
+		   WebAssembly.LinkError,
+		   /shared memory is disabled/);
+
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/memory-sharing.js
@@ -0,0 +1,211 @@
+if (!wasmThreadsSupported())
+    quit(0);
+
+const WASMPAGE = 65536;
+
+// A shared memory should yield a SharedArrayBuffer of appropriate length
+
+{
+    let mem = new WebAssembly.Memory({initial: 2, maximum: 4, shared: true});
+    assertEq(mem.buffer instanceof SharedArrayBuffer, true);
+    assertEq(mem.buffer.byteLength, WASMPAGE*2);
+}
+
+// Ditto, when the memory was created by instantiation and exported
+
+{
+    let text = `(module
+		 (memory (export "memory") 1 2 shared)
+		 (func (export "l0") (result i32) (i32.load (i32.const 0))))`;
+    let mod = new WebAssembly.Module(wasmTextToBinary(text));
+    let ins = new WebAssembly.Instance(mod);
+    let mem = ins.exports.memory;
+    let buf = mem.buffer;
+    assertEq(buf instanceof SharedArrayBuffer, true);
+    assertEq(buf.byteLength, WASMPAGE);
+}
+
+// Shared memory requires a maximum size
+
+{
+    assertErrorMessage(() => new WebAssembly.Memory({initial: 2, shared: true}),
+		       TypeError,
+		       /'shared' is true but maximum is not specified/);
+}
+
+// Ditto, syntactically
+
+{
+    let text = `(module
+		 (memory 1 shared)
+		 (func (export "l0") (result i32) (i32.load (i32.const 0))))`;
+    assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(text)),
+		       WebAssembly.CompileError,
+		       /maximum length required for shared memory/);
+}
+
+// Ditto, in the binary.  The flags field can be 0 (unshared, min only), 1
+// (unshared, min and max), or 3 (shared, min and max), but not 2 (shared, min
+// only).  So construct a module that has that, and make sure it's rejected.
+
+{
+    let bin = new Uint8Array([0x00, 0x61, 0x73, 0x6d,
+			      0x01, 0x00, 0x00, 0x00,
+			      0x05,                   // Memory
+			      0x03,                   // Section size
+			      0x01,                   // One memory
+			      0x02,		      // Shared, min only (illegal)
+			      0x01]);		      // Min
+    assertErrorMessage(() => new WebAssembly.Module(bin),
+		       WebAssembly.CompileError,
+		       /maximum length required for shared memory/);
+}
+
+// Importing shared memory and being provided with shared should work
+
+{
+    let text = `(module
+		 (memory (import "" "memory") 1 1 shared)
+		 (func (export "id") (param i32) (result i32) (get_local 0)))`;
+    let mod = new WebAssembly.Module(wasmTextToBinary(text));
+    let mem = new WebAssembly.Memory({initial: 1, maximum: 1, shared: true});
+    let ins = new WebAssembly.Instance(mod, {"": {memory: mem}});
+    assertEq(ins.exports.id(0x12345678), 0x12345678);
+}
+
+// Importing shared memory but being provided with unshared should fail
+
+{
+    let text = `(module
+		 (memory (import "" "memory") 1 1 shared)
+		 (func (export "id") (param i32) (result i32) (get_local 0)))`;
+    let mod = new WebAssembly.Module(wasmTextToBinary(text));
+    let mem = new WebAssembly.Memory({initial: 1, maximum: 1});
+    assertErrorMessage(() => new WebAssembly.Instance(mod, {"": {memory: mem}}),
+		       WebAssembly.LinkError,
+		       /unshared memory but shared required/);
+}
+
+// Importing unshared memory but being provided with shared should fail
+
+{
+    let text = `(module
+		 (memory (import "" "memory") 1 1)
+		 (func (export "id") (param i32) (result i32) (get_local 0)))`;
+    let mod = new WebAssembly.Module(wasmTextToBinary(text));
+    let mem = new WebAssembly.Memory({initial: 1, maximum: 1, shared: true});
+    assertErrorMessage(() => new WebAssembly.Instance(mod, {"": {memory: mem}}),
+		       WebAssembly.LinkError,
+		       /shared memory but unshared required/);
+}
+
+// Importing shared memory and being provided with shared memory with
+// incompatible parameters should fail
+
+{
+    let text = `(module
+		 (memory (import "" "memory") 2 4 shared)
+		 (func (export "id") (param i32) (result i32) (get_local 0)))`;
+    let mod = new WebAssembly.Module(wasmTextToBinary(text));
+
+    // some cases that are non-matching are allowed, eg, initial > declared min
+
+    // initial < declared min
+    let mem3 = new WebAssembly.Memory({initial: 1, maximum: 4, shared: true});
+    assertErrorMessage(() => new WebAssembly.Instance(mod, {"": {memory: mem3}}),
+		       WebAssembly.LinkError,
+		       /imported Memory with incompatible size/);
+
+    // initial > declared max
+    let mem4 = new WebAssembly.Memory({initial: 5, maximum: 8, shared: true});
+    assertErrorMessage(() => new WebAssembly.Instance(mod, {"": {memory: mem4}}),
+		       WebAssembly.LinkError,
+		       /imported Memory with incompatible size/);
+
+    // max > declared max
+    let mem5 = new WebAssembly.Memory({initial: 2, maximum: 8, shared: true});
+    assertErrorMessage(() => new WebAssembly.Instance(mod, {"": {memory: mem5}}),
+		       WebAssembly.LinkError,
+		       /imported Memory with incompatible maximum size/);
+}
+
+
+// basic current_memory and grow_memory operation, with bounds checking near the
+// valid/invalid boundary
+
+{
+    let text = `(module
+		 (memory (export "memory") 2 4 shared)
+		 (func (export "c") (result i32) current_memory)
+		 (func (export "g") (result i32) (grow_memory (i32.const 1)))
+		 (func (export "l") (param i32) (result i32) (i32.load (get_local 0)))
+		 (func (export "s") (param i32) (param i32) (i32.store (get_local 0) (get_local 1))))`;
+    let mod = new WebAssembly.Module(wasmTextToBinary(text));
+    let ins = new WebAssembly.Instance(mod);
+    let exp = ins.exports;
+    let mem = exp.memory;
+
+    let b1 = mem.buffer;
+    assertEq(exp.c(), 2);
+    assertEq(b1.byteLength, WASMPAGE*2);
+    assertEq(mem.buffer === b1, true);   // current_memory does not affect buffer
+    exp.s(WASMPAGE*2-4, 0x12345678)	 // access near end
+    assertEq(exp.l(WASMPAGE*2-4), 0x12345678);
+    assertErrorMessage(() => exp.l(WASMPAGE*2), // beyond current end (but below max)
+		       WebAssembly.RuntimeError,
+		       /index out of bounds/);
+    assertEq(exp.g(), 2);
+    assertEq(b1.byteLength, WASMPAGE*2); // growing does not affect existing buffer length
+    let b2 = mem.buffer;
+    assertEq(b1 !== b2, true);	         // growing produces a new buffer
+    assertEq(b2.byteLength, WASMPAGE*3); // new buffer has appropriate length
+    assertEq(exp.c(), 3);
+    exp.s(WASMPAGE*3-4, 0x12344321);     // access near new end
+    assertEq(exp.l(WASMPAGE*3-4), 0x12344321);
+    assertErrorMessage(() => exp.l(WASMPAGE*3), // beyond current end (but below max)
+		       WebAssembly.RuntimeError,
+		       /index out of bounds/);
+    assertEq(exp.g(), 3);
+    assertEq(b2.byteLength, WASMPAGE*3); // growing does not affect existing buffer length
+    let b3 = mem.buffer;
+    assertEq(b2 !== b3, true);	         // growing produces a new buffer
+    assertEq(b3.byteLength, WASMPAGE*4); // new buffer has appropriate length
+    assertEq(exp.c(), 4);
+    exp.s(WASMPAGE*4-4, 0x12121212);     // access near new end
+    assertEq(exp.l(WASMPAGE*4-4), 0x12121212);
+    assertErrorMessage(() => exp.l(WASMPAGE*4), // beyond current end (and beyond max)
+		       WebAssembly.RuntimeError,
+		       /index out of bounds/);
+    assertEq(exp.g(), -1);
+    assertEq(exp.c(), 4);                // failure to grow -> no change
+    let b4 = mem.buffer;
+    assertEq(b3 === b4, true);	         // failure to grow -> same ol' buffer
+    assertEq(exp.g(), -1);               // we can fail repeatedly
+}
+
+// Test the grow() API with shared memory.  In the implementation this API
+// shares almost all code with the wasm instruction, so don't bother going deep.
+
+{
+    let mem = new WebAssembly.Memory({initial: 2, maximum: 4, shared: true});
+    let buf = mem.buffer;
+    assertEq(mem.grow(1), 2);
+    assertEq(buf.byteLength, WASMPAGE*2);
+    assertEq(mem.grow(1), 3);
+    assertErrorMessage(() => mem.grow(1), RangeError, /failed to grow memory/);
+}
+
+// Initializing shared memory with data
+
+{
+    let text = `(module
+		 (memory (import "" "memory") 2 4 shared)
+		 (data (i32.const 0) "abcdefghijklmnopqrstuvwxyz")
+		 (func (export "l") (param i32) (result i32) (i32.load8_u (get_local 0))))`;
+    let mod = new WebAssembly.Module(wasmTextToBinary(text));
+    let mem = new WebAssembly.Memory({initial: 2, maximum: 4, shared: true});
+    let ins = new WebAssembly.Instance(mod, {"": {memory: mem}});
+    let exp = ins.exports;
+    assertEq(exp.l(12), "a".charCodeAt(0) + 12);
+}
+
--- a/js/src/jit-test/tests/wasm/regress/proxy-has-trap-table.js
+++ b/js/src/jit-test/tests/wasm/regress/proxy-has-trap-table.js
@@ -1,10 +1,11 @@
 assertErrorMessage(() => {
     var desc = {
         element: "anyfunc",
+        initial: 1
     };
     var proxy = new Proxy({}, {
         has: true
     });
     Object.setPrototypeOf(desc, proxy);
     let table = new WebAssembly.Table(desc);
 }, TypeError, /proxy handler's has trap/);
--- a/js/src/jit-test/tests/wasm/spec/jsapi.js
+++ b/js/src/jit-test/tests/wasm/spec/jsapi.js
@@ -413,16 +413,20 @@ test(() => {
 test(() => {
     const memoryDesc = Object.getOwnPropertyDescriptor(WebAssembly, 'Memory');
     assert_equals(Memory, memoryDesc.value);
     assert_equals(Memory.length, 1);
     assert_equals(Memory.name, "Memory");
     assertThrows(() => Memory(), TypeError);
     assertThrows(() => new Memory(1), TypeError);
     assertThrows(() => new Memory({initial:{valueOf() { throw new Error("here")}}}), Error);
+    assertThrows(() => new Memory({}), RangeError);
+    assertThrows(() => new Memory({initial:NaN}), RangeError);
+    assertThrows(() => new Memory({initial:undefined}), RangeError);
+    assertThrows(() => new Memory({initial:"abc"}), RangeError);
     assertThrows(() => new Memory({initial:-1}), RangeError);
     assertThrows(() => new Memory({initial:Math.pow(2,32)}), RangeError);
     assertThrows(() => new Memory({initial:1, maximum: Math.pow(2,32)/Math.pow(2,14) }), RangeError);
     assertThrows(() => new Memory({initial:2, maximum:1 }), RangeError);
     assertThrows(() => new Memory({maximum: -1 }), RangeError);
     assert_equals(new Memory({initial:1}) instanceof Memory, true);
     assert_equals(new Memory({initial:1.5}).buffer.byteLength, WasmPage);
 }, "'WebAssembly.Memory' constructor function");
@@ -475,16 +479,20 @@ test(() => {
 }, "'WebAssembly.Memory.prototype.grow' data property");
 
 test(() => {
     const memGrowDesc = Object.getOwnPropertyDescriptor(memoryProto, 'grow');
     const memGrow = memGrowDesc.value;
     assert_equals(memGrow.length, 1);
     assertThrows(() => memGrow.call(), TypeError);
     assertThrows(() => memGrow.call({}), TypeError);
+    assertThrows(() => memGrow.call(mem1), RangeError);
+    assertThrows(() => memGrow.call(mem1, NaN), RangeError);
+    assertThrows(() => memGrow.call(mem1, undefined), RangeError);
+    assertThrows(() => memGrow.call(mem1, "abc"), RangeError);
     assertThrows(() => memGrow.call(mem1, -1), RangeError);
     assertThrows(() => memGrow.call(mem1, Math.pow(2,32)), RangeError);
     var mem = new Memory({initial:1, maximum:2});
     var buf = mem.buffer;
     assert_equals(buf.byteLength, WasmPage);
     assert_equals(mem.grow(0), 1);
     assert_equals(buf !== mem.buffer, true);
     assert_equals(buf.byteLength, 0);
@@ -514,16 +522,20 @@ test(() => {
     assert_equals(Table.length, 1);
     assert_equals(Table.name, "Table");
     assertThrows(() => Table(), TypeError);
     assertThrows(() => new Table(1), TypeError);
     assertThrows(() => new Table({initial:1, element:1}), TypeError);
     assertThrows(() => new Table({initial:1, element:"any"}), TypeError);
     assertThrows(() => new Table({initial:1, element:{valueOf() { return "anyfunc" }}}), TypeError);
     assertThrows(() => new Table({initial:{valueOf() { throw new Error("here")}}, element:"anyfunc"}), Error);
+    assertThrows(() => new Table({element:"anyfunc"}), RangeError);
+    assertThrows(() => new Table({initial:NaN, element:"anyfunc"}), RangeError);
+    assertThrows(() => new Table({initial:undefined, element:"anyfunc"}), RangeError);
+    assertThrows(() => new Table({initial:"abc", element:"anyfunc"}), RangeError);
     assertThrows(() => new Table({initial:-1, element:"anyfunc"}), RangeError);
     assertThrows(() => new Table({initial:Math.pow(2,32), element:"anyfunc"}), RangeError);
     assertThrows(() => new Table({initial:2, maximum:1, element:"anyfunc"}), RangeError);
     assertThrows(() => new Table({initial:2, maximum:Math.pow(2,32), element:"anyfunc"}), RangeError);
     assert_equals(new Table({initial:1, element:"anyfunc"}) instanceof Table, true);
     assert_equals(new Table({initial:1.5, element:"anyfunc"}) instanceof Table, true);
     assert_equals(new Table({initial:1, maximum:1.5, element:"anyfunc"}) instanceof Table, true);
     assert_equals(new Table({initial:1, maximum:Math.pow(2,32)-1, element:"anyfunc"}) instanceof Table, true);
@@ -581,16 +593,20 @@ test(() => {
     const getDesc = Object.getOwnPropertyDescriptor(tableProto, 'get');
     const get = getDesc.value;
     assert_equals(get.length, 1);
     assertThrows(() => get.call(), TypeError);
     assertThrows(() => get.call({}), TypeError);
     assert_equals(get.call(tbl1, 0), null);
     assert_equals(get.call(tbl1, 1), null);
     assert_equals(get.call(tbl1, 1.5), null);
+    assertThrows(() => get.call(tbl1), RangeError);
+    assertThrows(() => get.call(tbl1, NaN), RangeError);
+    assertThrows(() => get.call(tbl1, undefined), RangeError);
+    assertThrows(() => get.call(tbl1, "abc"), RangeError);
     assertThrows(() => get.call(tbl1, 2), RangeError);
     assertThrows(() => get.call(tbl1, 2.5), RangeError);
     assertThrows(() => get.call(tbl1, -1), RangeError);
     assertThrows(() => get.call(tbl1, Math.pow(2,33)), RangeError);
     assertThrows(() => get.call(tbl1, {valueOf() { throw new Error("hi") }}), Error);
 }, "'WebAssembly.Table.prototype.get' method");
 
 test(() => {
@@ -602,16 +618,19 @@ test(() => {
 
 test(() => {
     const setDesc = Object.getOwnPropertyDescriptor(tableProto, 'set');
     const set = setDesc.value;
     assert_equals(set.length, 2);
     assertThrows(() => set.call(), TypeError);
     assertThrows(() => set.call({}), TypeError);
     assertThrows(() => set.call(tbl1, 0), TypeError);
+    assertThrows(() => set.call(tbl1, NaN, null), RangeError);
+    assertThrows(() => set.call(tbl1, undefined, null), RangeError);
+    assertThrows(() => set.call(tbl1, "abc", null), RangeError);
     assertThrows(() => set.call(tbl1, 2, null), RangeError);
     assertThrows(() => set.call(tbl1, -1, null), RangeError);
     assertThrows(() => set.call(tbl1, Math.pow(2,33), null), RangeError);
     assertThrows(() => set.call(tbl1, 0, undefined), TypeError);
     assertThrows(() => set.call(tbl1, 0, {}), TypeError);
     assertThrows(() => set.call(tbl1, 0, function() {}), TypeError);
     assertThrows(() => set.call(tbl1, 0, Math.sin), TypeError);
     assertThrows(() => set.call(tbl1, {valueOf() { throw Error("hai") }}, null), Error);
@@ -627,16 +646,20 @@ test(() => {
 }, "'WebAssembly.Table.prototype.grow' data property");
 
 test(() => {
     const tblGrowDesc = Object.getOwnPropertyDescriptor(tableProto, 'grow');
     const tblGrow = tblGrowDesc.value;
     assert_equals(tblGrow.length, 1);
     assertThrows(() => tblGrow.call(), TypeError);
     assertThrows(() => tblGrow.call({}), TypeError);
+    assertThrows(() => tblGrow.call(tbl1), RangeError);
+    assertThrows(() => tblGrow.call(tbl1, NaN), RangeError);
+    assertThrows(() => tblGrow.call(tbl1, undefined), RangeError);
+    assertThrows(() => tblGrow.call(tbl1, "abc"), RangeError);
     assertThrows(() => tblGrow.call(tbl1, -1), RangeError);
     assertThrows(() => tblGrow.call(tbl1, Math.pow(2,32)), RangeError);
     var tbl = new Table({element:"anyfunc", initial:1, maximum:2});
     assert_equals(tbl.length, 1);
     assert_equals(tbl.grow(0), 1);
     assert_equals(tbl.length, 1);
     assert_equals(tbl.grow(1), 1);
     assert_equals(tbl.length, 2);
--- a/js/src/jit/AliasAnalysisShared.cpp
+++ b/js/src/jit/AliasAnalysisShared.cpp
@@ -137,18 +137,18 @@ GetObject(const MDefinition* ins)
       case MDefinition::Opcode::CompareExchangeTypedArrayElement:
       case MDefinition::Opcode::AtomicExchangeTypedArrayElement:
       case MDefinition::Opcode::AtomicTypedArrayElementBinop:
       case MDefinition::Opcode::AsmJSLoadHeap:
       case MDefinition::Opcode::AsmJSStoreHeap:
       case MDefinition::Opcode::WasmLoadTls:
       case MDefinition::Opcode::WasmLoad:
       case MDefinition::Opcode::WasmStore:
-      case MDefinition::Opcode::AsmJSCompareExchangeHeap:
-      case MDefinition::Opcode::AsmJSAtomicBinopHeap:
+      case MDefinition::Opcode::WasmCompareExchangeHeap:
+      case MDefinition::Opcode::WasmAtomicBinopHeap:
       case MDefinition::Opcode::WasmLoadGlobalVar:
       case MDefinition::Opcode::WasmStoreGlobalVar:
       case MDefinition::Opcode::ArrayJoin:
         return nullptr;
       default:
 #ifdef DEBUG
         // Crash when the default aliasSet is overriden, but when not added in the list above.
         if (!ins->getAliasSet().isStore() || ins->getAliasSet().flags() != AliasSet::Flag::Any)
--- a/js/src/jit/AlignmentMaskAnalysis.cpp
+++ b/js/src/jit/AlignmentMaskAnalysis.cpp
@@ -76,18 +76,18 @@ AnalyzeAsmHeapAddress(MDefinition* ptr, 
 bool
 AlignmentMaskAnalysis::analyze()
 {
     for (ReversePostorderIterator block(graph_.rpoBegin()); block != graph_.rpoEnd(); block++) {
         for (MInstructionIterator i = block->begin(); i != block->end(); i++) {
             if (!graph_.alloc().ensureBallast())
                 return false;
 
-            // Note that we don't check for MAsmJSCompareExchangeHeap
-            // or MAsmJSAtomicBinopHeap, because the backend and the OOB
+            // Note that we don't check for MWasmCompareExchangeHeap
+            // or MWasmAtomicBinopHeap, because the backend and the OOB
             // mechanism don't support non-zero offsets for them yet.
             if (i->isAsmJSLoadHeap())
                 AnalyzeAsmHeapAddress(i->toAsmJSLoadHeap()->base(), graph_);
             else if (i->isAsmJSStoreHeap())
                 AnalyzeAsmHeapAddress(i->toAsmJSStoreHeap()->base(), graph_);
         }
     }
     return true;
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -2187,30 +2187,37 @@ TryAttachCallStub(JSContext* cx, ICCall_
                 return true;
             }
         }
         return true;
     }
 
     RootedFunction fun(cx, &obj->as<JSFunction>());
 
-    if (fun->hasScript()) {
+    if (fun->isInterpreted()) {
         // Never attach optimized scripted call stubs for JSOP_FUNAPPLY.
         // MagicArguments may escape the frame through them.
         if (op == JSOP_FUNAPPLY)
             return true;
 
         // 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->hasScript()) {
+            // Don't treat this as an unoptimizable case, as we'll add a stub
+            // when the callee is delazified.
+            *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.
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -8003,35 +8003,37 @@ JitRuntime::generateLazyLinkStub(MacroAs
     lazyLinkStubOffset_ = 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, temp0, ExitFrameType::LazyLink);
-
-    masm.setupUnalignedABICall(temp0);
+    masm.enterFakeExitFrame(temp0, temp2, ExitFrameType::LazyLink);
+    masm.moveStackPtrTo(temp1);
+
+    masm.setupUnalignedABICall(temp2);
     masm.passABIArg(temp0);
+    masm.passABIArg(temp1);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, LazyLinkTopActivation), MoveOp::GENERAL,
                      CheckUnsafeCallWithABI::DontCheckHasExitFrame);
 
     masm.leaveExitFrame();
 
 #ifdef JS_USE_LINK_REGISTER
     // Restore the return address such that the emitPrologue function of the
     // 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
@@ -12367,16 +12369,25 @@ CodeGenerator::visitWasmBoundsCheck(LWas
     Register ptr = ToRegister(ins->ptr());
     Register boundsCheckLimit = ToRegister(ins->boundsCheckLimit());
     masm.wasmBoundsCheck(Assembler::AboveOrEqual, ptr, boundsCheckLimit,
                          trap(mir, wasm::Trap::OutOfBounds));
 #endif
 }
 
 void
+CodeGenerator::visitWasmAlignmentCheck(LWasmAlignmentCheck* ins)
+{
+    const MWasmAlignmentCheck* mir = ins->mir();
+    Register ptr = ToRegister(ins->ptr());
+    masm.branchTest32(Assembler::NonZero, ptr, Imm32(mir->byteSize() - 1),
+                      trap(mir, wasm::Trap::UnalignedAccess));
+}
+
+void
 CodeGenerator::visitWasmLoadTls(LWasmLoadTls* ins)
 {
     switch (ins->mir()->type()) {
       case MIRType::Pointer:
         masm.loadPtr(Address(ToRegister(ins->tlsPtr()), ins->mir()->offset()),
                      ToRegister(ins->output()));
         break;
       case MIRType::Int32:
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -448,16 +448,17 @@ class CodeGenerator final : public CodeG
     void emitAssertResultV(const ValueOperand output, const TemporaryTypeSet* typeset);
     void emitAssertObjectOrStringResult(Register input, MIRType type, const TemporaryTypeSet* typeset);
 
     void visitInterruptCheck(LInterruptCheck* lir);
     void visitOutOfLineInterruptCheckImplicit(OutOfLineInterruptCheckImplicit* ins);
     void visitWasmTrap(LWasmTrap* lir);
     void visitWasmLoadTls(LWasmLoadTls* ins);
     void visitWasmBoundsCheck(LWasmBoundsCheck* ins);
+    void visitWasmAlignmentCheck(LWasmAlignmentCheck* ins);
     void visitRecompileCheck(LRecompileCheck* ins);
     void visitRotate(LRotate* ins);
 
     void visitRandom(LRandom* ins);
     void visitSignExtendInt32(LSignExtendInt32* ins);
 
 #ifdef DEBUG
     void emitDebugForceBailing(LInstruction* lir);
--- a/js/src/jit/EffectiveAddressAnalysis.cpp
+++ b/js/src/jit/EffectiveAddressAnalysis.cpp
@@ -257,18 +257,18 @@ EffectiveAddressAnalysis::analyzeAsmJSHe
 bool
 EffectiveAddressAnalysis::analyze()
 {
     for (ReversePostorderIterator block(graph_.rpoBegin()); block != graph_.rpoEnd(); block++) {
         for (MInstructionIterator i = block->begin(); i != block->end(); i++) {
             if (!graph_.alloc().ensureBallast())
                 return false;
 
-            // Note that we don't check for MAsmJSCompareExchangeHeap
-            // or MAsmJSAtomicBinopHeap, because the backend and the OOB
+            // Note that we don't check for MWasmCompareExchangeHeap
+            // or MWasmAtomicBinopHeap, because the backend and the OOB
             // mechanism don't support non-zero offsets for them yet
             // (TODO bug 1254935).
             if (i->isLsh())
                 AnalyzeLsh(graph_.alloc(), i->toLsh());
             else if (i->isLoadUnboxedScalar())
                 AnalyzeLoadUnboxedScalar(graph_.alloc(), i->toLoadUnboxedScalar());
             else if (i->isAsmJSLoadHeap())
                 analyzeAsmJSHeapAccess(i->toAsmJSLoadHeap());
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -54,16 +54,17 @@
 #include "vtune/VTuneWrapper.h"
 
 #include "jscompartmentinlines.h"
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
 
 #include "gc/Iteration-inl.h"
 #include "jit/JitFrames-inl.h"
+#include "jit/MacroAssembler-inl.h"
 #include "jit/shared/Lowering-shared-inl.h"
 #include "vm/Debugger-inl.h"
 #include "vm/EnvironmentObject-inl.h"
 #include "vm/Stack-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
@@ -198,17 +199,16 @@ JitRuntime::JitRuntime(JSRuntime* rt)
     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)
 {
@@ -224,16 +224,17 @@ JitRuntime::~JitRuntime()
 }
 
 uint32_t
 JitRuntime::startTrampolineCode(MacroAssembler& masm)
 {
     masm.assumeUnreachable("Shouldn't get here");
     masm.flushBuffer();
     masm.haltingAlign(CodeAlignment);
+    masm.setFramePushed(0);
     return masm.currentOffset();
 }
 
 bool
 JitRuntime::initialize(JSContext* cx, AutoLockForExclusiveAccess& lock)
 {
     AutoAtomsCompartment ac(cx, lock);
 
@@ -588,23 +589,19 @@ jit::LinkIonScript(JSContext* cx, Handle
 
     {
         AutoLockHelperThreadState lock;
         FinishOffThreadBuilder(cx->runtime(), builder, lock);
     }
 }
 
 uint8_t*
-jit::LazyLinkTopActivation()
+jit::LazyLinkTopActivation(JSContext* cx, LazyLinkExitFrameLayout* frame)
 {
-    // First frame should be an exit frame.
-    JSContext* cx = TlsContext.get();
-    JSJitFrameIter frame(cx);
-    LazyLinkExitFrameLayout* ll = frame.exitFrame()->as<LazyLinkExitFrameLayout>();
-    RootedScript calleeScript(cx, ScriptFromCalleeToken(ll->jsFrame()->calleeToken()));
+    RootedScript calleeScript(cx, ScriptFromCalleeToken(frame->jsFrame()->calleeToken()));
 
     LinkIonScript(cx, calleeScript);
 
     MOZ_ASSERT(calleeScript->hasBaselineScript());
     MOZ_ASSERT(calleeScript->jitCodeRaw());
 
     return calleeScript->jitCodeRaw();
 }
@@ -2798,27 +2795,18 @@ InvalidateActivation(FreeOp* fop, const 
             JitSpew(JitSpew_IonInvalidate, "#%zu wasm frames @ %p", frameno, frame.fp());
             break;
         }
 #endif // JS_JITSPEW
 
         if (!frame.isIonScripted())
             continue;
 
-        JitRuntime* jrt = fop->runtime()->jitRuntime();
-
-        bool calledFromLinkStub = false;
-        if (frame.returnAddressToFp() >= jrt->lazyLinkStub().value &&
-            frame.returnAddressToFp() < jrt->lazyLinkStubEnd().value)
-        {
-            calledFromLinkStub = true;
-        }
-
         // See if the frame has already been invalidated.
-        if (!calledFromLinkStub && frame.checkInvalidation())
+        if (frame.checkInvalidation())
             continue;
 
         JSScript* script = frame.script();
         if (!script->hasIonScript())
             continue;
 
         if (!invalidateAll && !script->ionScript()->invalidated())
             continue;
@@ -2865,19 +2853,18 @@ InvalidateActivation(FreeOp* fop, const 
             // We're about to remove edges from the JSScript to gcthings
             // embedded in the JitCode. Perform one final trace of the
             // JitCode for the incremental GC, as it must know about
             // those edges.
             ionCode->traceChildren(zone->barrierTracer());
         }
         ionCode->setInvalidated();
 
-        // Don't adjust OSI points in the linkStub (which don't exist), or in a
-        // bailout path.
-        if (calledFromLinkStub || frame.isBailoutJS())
+        // Don't adjust OSI points in a bailout path.
+        if (frame.isBailoutJS())
             continue;
 
         // Write the delta (from the return address offset to the
         // IonScript pointer embedded into the invalidation epilogue)
         // where the safepointed call instruction used to be. We rely on
         // the call sequence causing the safepoint being >= the size of
         // a uint32, which is checked during safepoint index
         // construction.
--- a/js/src/jit/Ion.h
+++ b/js/src/jit/Ion.h
@@ -133,29 +133,30 @@ void Invalidate(JSContext* cx, const Rec
                 bool cancelOffThread = true);
 void Invalidate(JSContext* cx, JSScript* script, bool resetUses = true,
                 bool cancelOffThread = true);
 
 class IonBuilder;
 class MIRGenerator;
 class LIRGraph;
 class CodeGenerator;
+class LazyLinkExitFrameLayout;
 
 MOZ_MUST_USE bool OptimizeMIR(MIRGenerator* mir);
 LIRGraph* GenerateLIR(MIRGenerator* mir);
 CodeGenerator* GenerateCode(MIRGenerator* mir, LIRGraph* lir);
 CodeGenerator* CompileBackEnd(MIRGenerator* mir);
 
 void AttachFinishedCompilations(ZoneGroup* group, JSContext* maybecx);
 void FinishOffThreadBuilder(JSRuntime* runtime, IonBuilder* builder,
                             const AutoLockHelperThreadState& lock);
 void FreeIonBuilder(IonBuilder* builder);
 
 void LinkIonScript(JSContext* cx, HandleScript calleescript);
-uint8_t* LazyLinkTopActivation();
+uint8_t* LazyLinkTopActivation(JSContext* cx, LazyLinkExitFrameLayout* frame);
 
 static inline bool
 IsIonEnabled(JSContext* cx)
 {
     // The ARM64 Ion engine is not yet implemented.
 #if defined(JS_CODEGEN_NONE) || defined(JS_CODEGEN_ARM64)
     return false;
 #else
--- a/js/src/jit/IonTypes.h
+++ b/js/src/jit/IonTypes.h
@@ -846,17 +846,29 @@ enum ABIFunctionType
        (ArgType_General << (ArgType_Shift * 2)) |
        (ArgType_Double  << (ArgType_Shift * 3)),
 
     // int f(int, double, int, int)
     Args_Int_IntDoubleIntInt = Args_General0 |
         (ArgType_General << (ArgType_Shift * 1)) |
         (ArgType_General << (ArgType_Shift * 2)) |
         (ArgType_Double  << (ArgType_Shift * 3)) |
-        (ArgType_General << (ArgType_Shift * 4))
+        (ArgType_General << (ArgType_Shift * 4)),
+
+    Args_Int_GeneralGeneralGeneralInt64 = Args_General0 |
+        (ArgType_General << (ArgType_Shift * 1)) |
+        (ArgType_General << (ArgType_Shift * 2)) |
+        (ArgType_General << (ArgType_Shift * 3)) |
+        (ArgType_Int64 << (ArgType_Shift * 4)),
+
+    Args_Int_GeneralGeneralInt64Int64 = Args_General0 |
+        (ArgType_General << (ArgType_Shift * 1)) |
+        (ArgType_General << (ArgType_Shift * 2)) |
+        (ArgType_Int64 << (ArgType_Shift * 3)) |
+        (ArgType_Int64 << (ArgType_Shift * 4))
 };
 
 enum class BarrierKind : uint32_t {
     // No barrier is needed.
     NoBarrier,
 
     // The barrier only has to check the value's type tag is in the TypeSet.
     // Specific object types don't have to be checked.
--- a/js/src/jit/JitCompartment.h
+++ b/js/src/jit/JitCompartment.h
@@ -129,17 +129,16 @@ class JitRuntime
     ExclusiveAccessLockWriteOnceData<uint32_t> objectGroupPreBarrierOffset_;
 
     // 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.
@@ -310,19 +309,16 @@ class JitRuntime
 
     TrampolinePtr freeStub() const {
         return trampolineCode(freeStubOffset_);
     }
 
     TrampolinePtr lazyLinkStub() const {
         return trampolineCode(lazyLinkStubOffset_);
     }
-    TrampolinePtr lazyLinkStubEnd() const {
-        return trampolineCode(lazyLinkStubEndOffset_);
-    }
     TrampolinePtr interpreterStub() const {
         return trampolineCode(interpreterStubOffset_);
     }
 
     bool hasJitcodeGlobalTable() const {
         return jitcodeGlobalTable_ != nullptr;
     }
 
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -4452,16 +4452,26 @@ LIRGenerator::visitWasmBoundsCheck(MWasm
 
     auto* lir = new(alloc()) LWasmBoundsCheck(useRegisterAtStart(index),
                                               useRegisterAtStart(boundsCheckLimit));
     add(lir, ins);
 #endif
 }
 
 void
+LIRGenerator::visitWasmAlignmentCheck(MWasmAlignmentCheck* ins)
+{
+    MDefinition* index = ins->index();
+    MOZ_ASSERT(index->type() == MIRType::Int32);
+
+    auto* lir = new(alloc()) LWasmAlignmentCheck(useRegisterAtStart(index));
+    add(lir, ins);
+}
+
+void
 LIRGenerator::visitWasmLoadGlobalVar(MWasmLoadGlobalVar* ins)
 {
     if (ins->type() == MIRType::Int64) {
 #ifdef JS_PUNBOX64
         LAllocation tlsPtr = useRegisterAtStart(ins->tlsPtr());
 #else
         LAllocation tlsPtr = useRegister(ins->tlsPtr());
 #endif
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -298,16 +298,17 @@ class LIRGenerator : public LIRGenerator
     void visitIsArray(MIsArray* ins);
     void visitIsTypedArray(MIsTypedArray* ins);
     void visitIsObject(MIsObject* ins);
     void visitHasClass(MHasClass* ins);
     void visitObjectClassToString(MObjectClassToString* ins);
     void visitWasmAddOffset(MWasmAddOffset* ins);
     void visitWasmLoadTls(MWasmLoadTls* ins);
     void visitWasmBoundsCheck(MWasmBoundsCheck* ins);
+    void visitWasmAlignmentCheck(MWasmAlignmentCheck* ins);
     void visitWasmLoadGlobalVar(MWasmLoadGlobalVar* ins);
     void visitWasmStoreGlobalVar(MWasmStoreGlobalVar* ins);
     void visitWasmParameter(MWasmParameter* ins);
     void visitWasmReturn(MWasmReturn* ins);
     void visitWasmReturnVoid(MWasmReturnVoid* ins);
     void visitWasmStackArg(MWasmStackArg* ins);
     void visitWasmCall(MWasmCall* ins);
     void visitSetDOMProperty(MSetDOMProperty* ins);
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -5326,16 +5326,25 @@ MWasmAddOffset::foldsTo(TempAllocator& a
     ptr += offset();
 
     if (!ptr.isValid())
         return this;
 
     return MConstant::New(alloc, Int32Value(ptr.value()));
 }
 
+bool
+MWasmAlignmentCheck::congruentTo(const MDefinition* ins) const
+{
+    if (!ins->isWasmAlignmentCheck())
+        return false;
+    const MWasmAlignmentCheck* check = ins->toWasmAlignmentCheck();
+    return byteSize_ == check->byteSize() && congruentIfOperandsEqual(check);
+}
+
 MDefinition::AliasType
 MAsmJSLoadHeap::mightAlias(const MDefinition* def) const
 {
     if (def->isAsmJSStoreHeap()) {
         const MAsmJSStoreHeap* store = def->toAsmJSStoreHeap();
         if (store->accessType() != accessType())
             return AliasType::MayAlias;
         if (!base()->isConstant() || !store->base()->isConstant())
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -14181,16 +14181,54 @@ class MWasmAddOffset
     uint32_t offset() const {
         return offset_;
     }
     wasm::BytecodeOffset bytecodeOffset() const {
         return bytecodeOffset_;
     }
 };
 
+class MWasmAlignmentCheck
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
+{
+    uint32_t byteSize_;
+    wasm::BytecodeOffset bytecodeOffset_;
+
+    explicit MWasmAlignmentCheck(MDefinition* index, uint32_t byteSize,
+                                 wasm::BytecodeOffset bytecodeOffset)
+      : MUnaryInstruction(classOpcode, index),
+        byteSize_(byteSize),
+        bytecodeOffset_(bytecodeOffset)
+    {
+        MOZ_ASSERT(mozilla::IsPowerOfTwo(byteSize));
+        // Alignment check is effectful: it throws for unaligned.
+        setGuard();
+    }
+
+  public:
+    INSTRUCTION_HEADER(WasmAlignmentCheck)
+    TRIVIAL_NEW_WRAPPERS
+    NAMED_OPERANDS((0, index))
+
+    bool congruentTo(const MDefinition* ins) const override;
+
+    AliasSet getAliasSet() const override {
+        return AliasSet::None();
+    }
+
+    uint32_t byteSize() const {
+        return byteSize_;
+    }
+
+    wasm::BytecodeOffset bytecodeOffset() const {
+        return bytecodeOffset_;
+    }
+};
+
 class MWasmLoad
   : public MVariadicInstruction, // memoryBase is nullptr on some platforms
     public NoTypePolicy::Data
 {
     wasm::MemoryAccessDesc access_;
 
     explicit MWasmLoad(const wasm::MemoryAccessDesc& access, MIRType resultType)
       : MVariadicInstruction(classOpcode),
@@ -14418,46 +14456,46 @@ class MAsmJSStoreHeap
     MDefinition* memoryBase() const { return getOperand(memoryBaseIndex_); }
     MDefinition* boundsCheckLimit() const { return getOperand(boundsCheckIndex_); }
 
     AliasSet getAliasSet() const override {
         return AliasSet::Store(AliasSet::WasmHeap);
     }
 };
 
-class MAsmJSCompareExchangeHeap
+class MWasmCompareExchangeHeap
   : public MVariadicInstruction,
     public NoTypePolicy::Data
 {
     wasm::MemoryAccessDesc access_;
     wasm::BytecodeOffset bytecodeOffset_;
 
-    explicit MAsmJSCompareExchangeHeap(const wasm::MemoryAccessDesc& access,
-                                       wasm::BytecodeOffset bytecodeOffset)
+    explicit MWasmCompareExchangeHeap(const wasm::MemoryAccessDesc& access,
+                                      wasm::BytecodeOffset bytecodeOffset)
       : MVariadicInstruction(classOpcode),
         access_(access),
         bytecodeOffset_(bytecodeOffset)
     {
         setGuard(); // Not removable
-        setResultType(MIRType::Int32);
-    }
-
-  public:
-    INSTRUCTION_HEADER(AsmJSCompareExchangeHeap)
-
-    static MAsmJSCompareExchangeHeap* New(TempAllocator& alloc,
-                                          wasm::BytecodeOffset bytecodeOffset,
-                                          MDefinition* memoryBase,
-                                          MDefinition* base,
-                                          const wasm::MemoryAccessDesc& access,
-                                          MDefinition* oldv,
-                                          MDefinition* newv,
-                                          MDefinition* tls)
-    {
-        MAsmJSCompareExchangeHeap* cas = new(alloc) MAsmJSCompareExchangeHeap(access, bytecodeOffset);
+        setResultType(ScalarTypeToMIRType(access.type()));
+    }
+
+  public:
+    INSTRUCTION_HEADER(WasmCompareExchangeHeap)
+
+    static MWasmCompareExchangeHeap* New(TempAllocator& alloc,
+                                         wasm::BytecodeOffset bytecodeOffset,
+                                         MDefinition* memoryBase,
+                                         MDefinition* base,
+                                         const wasm::MemoryAccessDesc& access,
+                                         MDefinition* oldv,
+                                         MDefinition* newv,
+                                         MDefinition* tls)
+    {
+        MWasmCompareExchangeHeap* cas = new(alloc) MWasmCompareExchangeHeap(access, bytecodeOffset);
         if (!cas->init(alloc, 4 + !!memoryBase))
             return nullptr;
         cas->initOperand(0, base);
         cas->initOperand(1, oldv);
         cas->initOperand(2, newv);
         cas->initOperand(3, tls);
         if (memoryBase)
             cas->initOperand(4, memoryBase);
@@ -14473,45 +14511,45 @@ class MAsmJSCompareExchangeHeap
     MDefinition* tls() const { return getOperand(3); }
     MDefinition* memoryBase() const { return getOperand(4); }
 
     AliasSet getAliasSet() const override {
         return AliasSet::Store(AliasSet::WasmHeap);
     }
 };
 
-class MAsmJSAtomicExchangeHeap
+class MWasmAtomicExchangeHeap
   : public MVariadicInstruction,
     public NoTypePolicy::Data
 {
     wasm::MemoryAccessDesc access_;
     wasm::BytecodeOffset bytecodeOffset_;
 
-    explicit MAsmJSAtomicExchangeHeap(const wasm::MemoryAccessDesc& access,
-                                      wasm::BytecodeOffset bytecodeOffset)
+    explicit MWasmAtomicExchangeHeap(const wasm::MemoryAccessDesc& access,
+                                     wasm::BytecodeOffset bytecodeOffset)
       : MVariadicInstruction(classOpcode),
         access_(access),
         bytecodeOffset_(bytecodeOffset)
     {
         setGuard();             // Not removable
-        setResultType(MIRType::Int32);
-    }
-
-  public:
-    INSTRUCTION_HEADER(AsmJSAtomicExchangeHeap)
-
-    static MAsmJSAtomicExchangeHeap* New(TempAllocator& alloc,
-                                         wasm::BytecodeOffset bytecodeOffset,
-                                         MDefinition* memoryBase,
-                                         MDefinition* base,
-                                         const wasm::MemoryAccessDesc& access,
-                                         MDefinition* value,
-                                         MDefinition* tls)
-    {
-        MAsmJSAtomicExchangeHeap* xchg = new(alloc) MAsmJSAtomicExchangeHeap(access, bytecodeOffset);
+        setResultType(ScalarTypeToMIRType(access.type()));
+    }
+
+  public:
+    INSTRUCTION_HEADER(WasmAtomicExchangeHeap)
+
+    static MWasmAtomicExchangeHeap* New(TempAllocator& alloc,
+                                        wasm::BytecodeOffset bytecodeOffset,
+                                        MDefinition* memoryBase,
+                                        MDefinition* base,
+                                        const wasm::MemoryAccessDesc& access,
+                                        MDefinition* value,
+                                        MDefinition* tls)
+    {
+        MWasmAtomicExchangeHeap* xchg = new(alloc) MWasmAtomicExchangeHeap(access, bytecodeOffset);
         if (!xchg->init(alloc, 3 + !!memoryBase))
             return nullptr;
 
         xchg->initOperand(0, base);
         xchg->initOperand(1, value);
         xchg->initOperand(2, tls);
         if (memoryBase)
             xchg->initOperand(3, memoryBase);
@@ -14527,48 +14565,48 @@ class MAsmJSAtomicExchangeHeap
     MDefinition* tls() const { return getOperand(2); }
     MDefinition* memoryBase() const { return getOperand(3); }
 
     AliasSet getAliasSet() const override {
         return AliasSet::Store(AliasSet::WasmHeap);
     }
 };
 
-class MAsmJSAtomicBinopHeap
+class MWasmAtomicBinopHeap
   : public MVariadicInstruction,
     public NoTypePolicy::Data
 {
     AtomicOp op_;
     wasm::MemoryAccessDesc access_;
     wasm::BytecodeOffset bytecodeOffset_;
 
-    explicit MAsmJSAtomicBinopHeap(AtomicOp op, const wasm::MemoryAccessDesc& access,
-                                   wasm::BytecodeOffset bytecodeOffset)
+    explicit MWasmAtomicBinopHeap(AtomicOp op, const wasm::MemoryAccessDesc& access,
+                                  wasm::BytecodeOffset bytecodeOffset)
       : MVariadicInstruction(classOpcode),
         op_(op),
         access_(access),
         bytecodeOffset_(bytecodeOffset)
     {
         setGuard();         // Not removable
-        setResultType(MIRType::Int32);
-    }
-
-  public:
-    INSTRUCTION_HEADER(AsmJSAtomicBinopHeap)
-
-    static MAsmJSAtomicBinopHeap* New(TempAllocator& alloc,
-                                      wasm::BytecodeOffset bytecodeOffset,
-                                      AtomicOp op,
-                                      MDefinition* memoryBase,
-                                      MDefinition* base,
-                                      const wasm::MemoryAccessDesc& access,
-                                      MDefinition* v,
-                                      MDefinition* tls)
-    {
-        MAsmJSAtomicBinopHeap* binop = new(alloc) MAsmJSAtomicBinopHeap(op, access, bytecodeOffset);
+        setResultType(ScalarTypeToMIRType(access.type()));
+    }
+
+  public:
+    INSTRUCTION_HEADER(WasmAtomicBinopHeap)
+
+    static MWasmAtomicBinopHeap* New(TempAllocator& alloc,
+                                     wasm::BytecodeOffset bytecodeOffset,
+                                     AtomicOp op,
+                                     MDefinition* memoryBase,
+                                     MDefinition* base,
+                                     const wasm::MemoryAccessDesc& access,
+                                     MDefinition* v,
+                                     MDefinition* tls)
+    {
+        MWasmAtomicBinopHeap* binop = new(alloc) MWasmAtomicBinopHeap(op, access, bytecodeOffset);
         if (!binop->init(alloc, 3 + !!memoryBase))
             return nullptr;
 
         binop->initOperand(0, base);
         binop->initOperand(1, v);
         binop->initOperand(2, tls);
         if (memoryBase)
             binop->initOperand(3, memoryBase);
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -301,21 +301,22 @@ namespace jit {
     _(CheckIsCallable)                                                      \
     _(CheckObjCoercible)                                                    \
     _(DebugCheckSelfHosted)                                                 \
     _(FinishBoundFunctionInit)                                              \
     _(IsPackedArray)                                                        \
     _(GetPrototypeOf)                                                       \
     _(AsmJSLoadHeap)                                                        \
     _(AsmJSStoreHeap)                                                       \
-    _(AsmJSCompareExchangeHeap)                                             \
-    _(AsmJSAtomicExchangeHeap)                                              \
-    _(AsmJSAtomicBinopHeap)                                                 \
+    _(WasmCompareExchangeHeap)                                              \
+    _(WasmAtomicExchangeHeap)                                               \
+    _(WasmAtomicBinopHeap)                                                  \
     _(WasmNeg)                                                              \
     _(WasmBoundsCheck)                                                      \
+    _(WasmAlignmentCheck)                                                   \
     _(WasmLoadTls)                                                          \
     _(WasmAddOffset)                                                        \
     _(WasmLoad)                                                             \
     _(WasmStore)                                                            \
     _(WasmTrap)                                                             \
     _(WasmTruncateToInt32)                                                  \
     _(WasmUnsignedToDouble)                                                 \
     _(WasmUnsignedToFloat32)                                                \
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -3120,16 +3120,128 @@ MacroAssembler::wasmEmitStackCheck(Regis
     loadPtr(Address(WasmTlsReg, offsetof(wasm::TlsData, addressOfContext)), scratch);
     loadPtr(Address(scratch, 0), scratch);
     branchPtr(Assembler::AboveOrEqual,
               Address(scratch, offsetof(JSContext, jitStackLimitNoInterrupt)),
               sp,
               onOverflow);
 }
 
+void
+MacroAssembler::emitPreBarrierFastPath(JSRuntime* rt, MIRType type, Register temp1, Register temp2,
+                                       Register temp3, Label* noBarrier)
+{
+    MOZ_ASSERT(temp1 != PreBarrierReg);
+    MOZ_ASSERT(temp2 != PreBarrierReg);
+    MOZ_ASSERT(temp3 != PreBarrierReg);
+
+    // Load the GC thing in temp1.
+    if (type == MIRType::Value) {
+        unboxNonDouble(Address(PreBarrierReg, 0), temp1);
+    } else {
+        MOZ_ASSERT(type == MIRType::Object ||
+                   type == MIRType::String ||
+                   type == MIRType::Shape ||
+                   type == MIRType::ObjectGroup);
+        loadPtr(Address(PreBarrierReg, 0), temp1);
+    }
+
+#ifdef DEBUG
+    // The caller should have checked for null pointers.
+    Label nonZero;
+    branchTestPtr(Assembler::NonZero, temp1, temp1, &nonZero);
+    assumeUnreachable("JIT pre-barrier: unexpected nullptr");
+    bind(&nonZero);
+#endif
+
+    // Load the chunk address in temp2.
+    movePtr(ImmWord(~gc::ChunkMask), temp2);
+    andPtr(temp1, temp2);
+
+    // If the GC thing is in the nursery, we don't need to barrier it.
+    if (type == MIRType::Value || type == MIRType::Object) {
+        branch32(Assembler::Equal, Address(temp2, gc::ChunkLocationOffset),
+                 Imm32(int32_t(gc::ChunkLocation::Nursery)), noBarrier);
+    } else {
+#ifdef DEBUG
+        Label isTenured;
+        branch32(Assembler::NotEqual, Address(temp2, gc::ChunkLocationOffset),
+                 Imm32(int32_t(gc::ChunkLocation::Nursery)), &isTenured);
+        assumeUnreachable("JIT pre-barrier: unexpected nursery pointer");
+        bind(&isTenured);
+#endif
+    }
+
+    // If it's a permanent atom or symbol from a parent runtime we don't
+    // need to barrier it.
+    if (type == MIRType::Value || type == MIRType::String) {
+        branchPtr(Assembler::NotEqual, Address(temp2, gc::ChunkRuntimeOffset),
+                  ImmPtr(rt), noBarrier);
+    } else {
+#ifdef DEBUG
+        Label thisRuntime;
+        branchPtr(Assembler::Equal, Address(temp2, gc::ChunkRuntimeOffset),
+                  ImmPtr(rt), &thisRuntime);
+        assumeUnreachable("JIT pre-barrier: unexpected runtime");
+        bind(&thisRuntime);
+#endif
+    }
+
+    // Determine the bit index and store in temp1.
+    //
+    // bit = (addr & js::gc::ChunkMask) / js::gc::CellBytesPerMarkBit +
+    //        static_cast<uint32_t>(colorBit);
+    static_assert(gc::CellBytesPerMarkBit == 8,
+                  "Calculation below relies on this");
+    static_assert(size_t(gc::ColorBit::BlackBit) == 0,
+                  "Calculation below relies on this");
+    andPtr(Imm32(gc::ChunkMask), temp1);
+    rshiftPtr(Imm32(3), temp1);
+
+    static const size_t nbits = sizeof(uintptr_t) * CHAR_BIT;
+    static_assert(nbits == JS_BITS_PER_WORD,
+                  "Calculation below relies on this");
+
+    // Load the bitmap word in temp2.
+    //
+    // word = chunk.bitmap[bit / nbits];
+    movePtr(temp1, temp3);
+#if JS_BITS_PER_WORD == 64
+    rshiftPtr(Imm32(6), temp1);
+    loadPtr(BaseIndex(temp2, temp1, TimesEight, gc::ChunkMarkBitmapOffset), temp2);
+#else
+    rshiftPtr(Imm32(5), temp1);
+    loadPtr(BaseIndex(temp2, temp1, TimesFour, gc::ChunkMarkBitmapOffset), temp2);
+#endif
+
+    // Load the mask in temp1.
+    //
+    // mask = uintptr_t(1) << (bit % nbits);
+    andPtr(Imm32(nbits - 1), temp3);
+    move32(Imm32(1), temp1);
+#ifdef JS_CODEGEN_X64
+    MOZ_ASSERT(temp3 == rcx);
+    shlq_cl(temp1);
+#elif JS_CODEGEN_X86
+    MOZ_ASSERT(temp3 == ecx);
+    shll_cl(temp1);
+#elif JS_CODEGEN_ARM
+    ma_lsl(temp3, temp1, temp1);
+#elif JS_CODEGEN_ARM64
+    Lsl(ARMRegister(temp1, 64), ARMRegister(temp1, 64), ARMRegister(temp3, 64));
+#elif JS_CODEGEN_NONE
+    MOZ_CRASH();
+#else
+# error "Unknown architecture"
+#endif
+
+    // No barrier is needed if the bit is set, |word & mask != 0|.
+    branchTestPtr(Assembler::NonZero, temp2, temp1, noBarrier);
+}
+
 //}}} check_macroassembler_style
 
 void
 MacroAssembler::loadWasmTlsRegFromFrame(Register dest)
 {
     loadPtr(Address(getStackPointer(), framePushed() + offsetof(wasm::Frame, tls)), dest);
 }
 
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -1498,16 +1498,19 @@ class MacroAssembler : public MacroAssem
     // Emit the out-of-line trap code to which trapping jumps/branches are
     // bound. This should be called once per function after all other codegen,
     // including "normal" OutOfLineCode.
     void wasmEmitTrapOutOfLineCode();
 
     // Perform a stack-overflow test, branching to the given Label on overflow.
     void wasmEmitStackCheck(Register sp, Register scratch, Label* onOverflow);
 
+    void emitPreBarrierFastPath(JSRuntime* rt, MIRType type, Register temp1, Register temp2,
+                                Register temp3, Label* noBarrier);
+
   public:
     // ========================================================================
     // Clamping functions.
 
     inline void clampIntToUint8(Register reg) PER_SHARED_ARCH;
 
     //}}} check_macroassembler_style
   public:
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -1436,47 +1436,47 @@ ObjectIsCallable(JSObject* obj)
 bool
 ObjectIsConstructor(JSObject* obj)
 {
     AutoUnsafeCallWithABI unsafe;
     return obj->isConstructor();
 }
 
 void
-MarkValueFromIon(JSRuntime* rt, Value* vp)
+MarkValueFromJit(JSRuntime* rt, Value* vp)
 {
     AutoUnsafeCallWithABI unsafe;
     TraceManuallyBarrieredEdge(&rt->gc.marker, vp, "write barrier");
 }
 
 void
-MarkStringFromIon(JSRuntime* rt, JSString** stringp)
+MarkStringFromJit(JSRuntime* rt, JSString** stringp)
 {
     AutoUnsafeCallWithABI unsafe;
     MOZ_ASSERT(*stringp);
     TraceManuallyBarrieredEdge(&rt->gc.marker, stringp, "write barrier");
 }
 
 void
-MarkObjectFromIon(JSRuntime* rt, JSObject** objp)
+MarkObjectFromJit(JSRuntime* rt, JSObject** objp)
 {
     AutoUnsafeCallWithABI unsafe;
     MOZ_ASSERT(*objp);
     TraceManuallyBarrieredEdge(&rt->gc.marker, objp, "write barrier");
 }
 
 void
-MarkShapeFromIon(JSRuntime* rt, Shape** shapep)
+MarkShapeFromJit(JSRuntime* rt, Shape** shapep)
 {
     AutoUnsafeCallWithABI unsafe;
     TraceManuallyBarrieredEdge(&rt->gc.marker, shapep, "write barrier");
 }
 
 void
-MarkObjectGroupFromIon(JSRuntime* rt, ObjectGroup** groupp)
+MarkObjectGroupFromJit(JSRuntime* rt, ObjectGroup** groupp)
 {
     AutoUnsafeCallWithABI unsafe;
     TraceManuallyBarrieredEdge(&rt->gc.marker, groupp, "write barrier");
 }
 
 bool
 ThrowRuntimeLexicalError(JSContext* cx, unsigned errorNumber)
 {
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -845,37 +845,37 @@ MOZ_MUST_USE bool SetDenseElement(JSCont
                                   HandleValue value, bool strict);
 
 void AssertValidObjectPtr(JSContext* cx, JSObject* obj);
 void AssertValidObjectOrNullPtr(JSContext* cx, JSObject* obj);
 void AssertValidStringPtr(JSContext* cx, JSString* str);
 void AssertValidSymbolPtr(JSContext* cx, JS::Symbol* sym);
 void AssertValidValue(JSContext* cx, Value* v);
 
-void MarkValueFromIon(JSRuntime* rt, Value* vp);
-void MarkStringFromIon(JSRuntime* rt, JSString** stringp);
-void MarkObjectFromIon(JSRuntime* rt, JSObject** objp);
-void MarkShapeFromIon(JSRuntime* rt, Shape** shapep);
-void MarkObjectGroupFromIon(JSRuntime* rt, ObjectGroup** groupp);
+void MarkValueFromJit(JSRuntime* rt, Value* vp);
+void MarkStringFromJit(JSRuntime* rt, JSString** stringp);
+void MarkObjectFromJit(JSRuntime* rt, JSObject** objp);
+void MarkShapeFromJit(JSRuntime* rt, Shape** shapep);
+void MarkObjectGroupFromJit(JSRuntime* rt, ObjectGroup** groupp);
 
 // Helper for generatePreBarrier.
 inline void*
-IonMarkFunction(MIRType type)
+JitMarkFunction(MIRType type)
 {
     switch (type) {
       case MIRType::Value:
-        return JS_FUNC_TO_DATA_PTR(void*, MarkValueFromIon);
+        return JS_FUNC_TO_DATA_PTR(void*, MarkValueFromJit);
       case MIRType::String:
-        return JS_FUNC_TO_DATA_PTR(void*, MarkStringFromIon);
+        return JS_FUNC_TO_DATA_PTR(void*, MarkStringFromJit);
       case MIRType::Object:
-        return JS_FUNC_TO_DATA_PTR(void*, MarkObjectFromIon);
+        return JS_FUNC_TO_DATA_PTR(void*, MarkObjectFromJit);
       case MIRType::Shape:
-        return JS_FUNC_TO_DATA_PTR(void*, MarkShapeFromIon);
+        return JS_FUNC_TO_DATA_PTR(void*, MarkShapeFromJit);
       case MIRType::ObjectGroup:
-        return JS_FUNC_TO_DATA_PTR(void*, MarkObjectGroupFromIon);
+        return JS_FUNC_TO_DATA_PTR(void*, MarkObjectGroupFromJit);
       default: MOZ_CRASH();
     }
 }
 
 bool ObjectIsCallable(JSObject* obj);
 bool ObjectIsConstructor(JSObject* obj);
 
 MOZ_MUST_USE bool
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -2524,39 +2524,39 @@ CodeGeneratorARM::visitAsmJSStoreHeap(LA
             ScratchRegisterScope scratch(masm);
             Register value = ToRegister(ins->value());
             masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, ptrReg, value, scratch, Offset, cond);
         }
     }
 }
 
 void
-CodeGeneratorARM::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins)
+CodeGeneratorARM::visitWasmCompareExchangeHeap(LWasmCompareExchangeHeap* ins)
 {
-    MAsmJSCompareExchangeHeap* mir = ins->mir();
-    MOZ_ASSERT(mir->access().offset() == 0);
+    MWasmCompareExchangeHeap* mir = ins->mir();
 
     Scalar::Type vt = mir->access().type();
     const LAllocation* ptr = ins->ptr();
     Register ptrReg = ToRegister(ptr);
-    BaseIndex srcAddr(HeapReg, ptrReg, TimesOne);
+    BaseIndex srcAddr(HeapReg, ptrReg, TimesOne, mir->access().offset());
+
     MOZ_ASSERT(ins->addrTemp()->isBogusTemp());
 
     Register oldval = ToRegister(ins->oldValue());
     Register newval = ToRegister(ins->newValue());
 
     masm.compareExchangeToTypedIntArray(vt == Scalar::Uint32 ? Scalar::Int32 : vt,
                                         srcAddr, oldval, newval, InvalidReg,
                                         ToAnyRegister(ins->output()));
 }
 
 void
-CodeGeneratorARM::visitAsmJSCompareExchangeCallout(LAsmJSCompareExchangeCallout* ins)
+CodeGeneratorARM::visitWasmCompareExchangeCallout(LWasmCompareExchangeCallout* ins)
 {
-    const MAsmJSCompareExchangeHeap* mir = ins->mir();
+    const MWasmCompareExchangeHeap* mir = ins->mir();
     MOZ_ASSERT(mir->access().offset() == 0);
 
     Register ptr = ToRegister(ins->ptr());
     Register oldval = ToRegister(ins->oldval());
     Register newval = ToRegister(ins->newval());
     Register tls = ToRegister(ins->tls());
     Register instance = ToRegister(ins->getTemp(0));
     Register viewType = ToRegister(ins->getTemp(1));
@@ -2571,35 +2571,34 @@ CodeGeneratorARM::visitAsmJSCompareExcha
     masm.passABIArg(viewType);
     masm.passABIArg(ptr);
     masm.passABIArg(oldval);
     masm.passABIArg(newval);
     masm.callWithABI(mir->bytecodeOffset(), wasm::SymbolicAddress::AtomicCmpXchg);
 }
 
 void
-CodeGeneratorARM::visitAsmJSAtomicExchangeHeap(LAsmJSAtomicExchangeHeap* ins)
+CodeGeneratorARM::visitWasmAtomicExchangeHeap(LWasmAtomicExchangeHeap* ins)
 {
-    MAsmJSAtomicExchangeHeap* mir = ins->mir();
-    MOZ_ASSERT(mir->access().offset() == 0);
+    MWasmAtomicExchangeHeap* mir = ins->mir();
 
     Scalar::Type vt = mir->access().type();
     Register ptrReg = ToRegister(ins->ptr());
     Register value = ToRegister(ins->value());
-    BaseIndex srcAddr(HeapReg, ptrReg, TimesOne);
+    BaseIndex srcAddr(HeapReg, ptrReg, TimesOne, mir->access().offset());
     MOZ_ASSERT(ins->addrTemp()->isBogusTemp());
 
     masm.atomicExchangeToTypedIntArray(vt == Scalar::Uint32 ? Scalar::Int32 : vt,
                                        srcAddr, value, InvalidReg, ToAnyRegister(ins->output()));
 }
 
 void
-CodeGeneratorARM::visitAsmJSAtomicExchangeCallout(LAsmJSAtomicExchangeCallout* ins)
+CodeGeneratorARM::visitWasmAtomicExchangeCallout(LWasmAtomicExchangeCallout* ins)
 {
-    const MAsmJSAtomicExchangeHeap* mir = ins->mir();
+    const MWasmAtomicExchangeHeap* mir = ins->mir();
     MOZ_ASSERT(mir->access().offset() == 0);
 
     Register ptr = ToRegister(ins->ptr());
     Register value = ToRegister(ins->value());
     Register tls = ToRegister(ins->tls());
     Register instance = ToRegister(ins->getTemp(0));
     Register viewType = ToRegister(ins->getTemp(1));
 
@@ -2612,68 +2611,66 @@ CodeGeneratorARM::visitAsmJSAtomicExchan
     masm.passABIArg(instance);
     masm.passABIArg(viewType);
     masm.passABIArg(ptr);
     masm.passABIArg(value);
     masm.callWithABI(mir->bytecodeOffset(), wasm::SymbolicAddress::AtomicXchg);
 }
 
 void
-CodeGeneratorARM::visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap* ins)
+CodeGeneratorARM::visitWasmAtomicBinopHeap(LWasmAtomicBinopHeap* ins)
 {
-    MAsmJSAtomicBinopHeap* mir = ins->mir();
-    MOZ_ASSERT(mir->access().offset() == 0);
+    MWasmAtomicBinopHeap* mir = ins->mir();
     MOZ_ASSERT(mir->hasUses());
 
     Scalar::Type vt = mir->access().type();
     Register ptrReg = ToRegister(ins->ptr());
     Register flagTemp = ToRegister(ins->flagTemp());
     const LAllocation* value = ins->value();
     AtomicOp op = mir->operation();
     MOZ_ASSERT(ins->addrTemp()->isBogusTemp());
 
-    BaseIndex srcAddr(HeapReg, ptrReg, TimesOne);
+    BaseIndex srcAddr(HeapReg, ptrReg, TimesOne, mir->access().offset());
 
     if (value->isConstant()) {
         atomicBinopToTypedIntArray(op, vt == Scalar::Uint32 ? Scalar::Int32 : vt,
                                    Imm32(ToInt32(value)), srcAddr, flagTemp, InvalidReg,
                                    ToAnyRegister(ins->output()));
     } else {
         atomicBinopToTypedIntArray(op, vt == Scalar::Uint32 ? Scalar::Int32 : vt,
                                    ToRegister(value), srcAddr, flagTemp, InvalidReg,
                                    ToAnyRegister(ins->output()));
     }
 }
 
 void
-CodeGeneratorARM::visitAsmJSAtomicBinopHeapForEffect(LAsmJSAtomicBinopHeapForEffect* ins)
+CodeGeneratorARM::visitWasmAtomicBinopHeapForEffect(LWasmAtomicBinopHeapForEffect* ins)
 {
-    MAsmJSAtomicBinopHeap* mir = ins->mir();
-    MOZ_ASSERT(mir->access().offset() == 0);
+    MWasmAtomicBinopHeap* mir = ins->mir();
     MOZ_ASSERT(!mir->hasUses());
 
     Scalar::Type vt = mir->access().type();
     Register ptrReg = ToRegister(ins->ptr());
     Register flagTemp = ToRegister(ins->flagTemp());
     const LAllocation* value = ins->value();
     AtomicOp op = mir->operation();
     MOZ_ASSERT(ins->addrTemp()->isBogusTemp());
 
-    BaseIndex srcAddr(HeapReg, ptrReg, TimesOne);
+    BaseIndex srcAddr(HeapReg, ptrReg, TimesOne, mir->access().offset());
 
     if (value->isConstant())
         atomicBinopToTypedIntArray(op, vt, Imm32(ToInt32(value)), srcAddr, flagTemp);
     else
         atomicBinopToTypedIntArray(op, vt, ToRegister(value), srcAddr, flagTemp);
 }
 
 void
-CodeGeneratorARM::visitAsmJSAtomicBinopCallout(LAsmJSAtomicBinopCallout* ins)
+CodeGeneratorARM::visitWasmAtomicBinopCallout(LWasmAtomicBinopCallout* ins)
 {
-    const MAsmJSAtomicBinopHeap* mir = ins->mir();
+    const MWasmAtomicBinopHeap* mir = ins->mir();
     MOZ_ASSERT(mir->access().offset() == 0);
 
     Register ptr = ToRegister(ins->ptr());
     Register value = ToRegister(ins->value());
     Register tls = ToRegister(ins->tls());
     Register instance = ToRegister(ins->getTemp(0));
     Register viewType = ToRegister(ins->getTemp(1));
 
@@ -3468,8 +3465,72 @@ CodeGeneratorARM::visitTestI64AndBranch(
 {
     Register64 input = ToRegister64(lir->getInt64Operand(0));
 
     masm.as_cmp(input.high, Imm8(0));
     jumpToBlock(lir->ifTrue(), Assembler::NonZero);
     masm.as_cmp(input.low, Imm8(0));
     emitBranch(Assembler::NonZero, lir->ifTrue(), lir->ifFalse());
 }
+
+void
+CodeGeneratorARM::visitWasmAtomicLoadI64(LWasmAtomicLoadI64* lir)
+{
+    Register ptr = ToRegister(lir->ptr());
+    Register64 output = ToOutRegister64(lir);
+    Register64 tmp(InvalidReg, InvalidReg);
+
+    BaseIndex addr(HeapReg, ptr, TimesOne, lir->mir()->access().offset());
+    masm.atomicLoad64(addr, tmp, output);
+}
+
+void
+CodeGeneratorARM::visitWasmAtomicStoreI64(LWasmAtomicStoreI64* lir)
+{
+    Register ptr = ToRegister(lir->ptr());
+    Register64 value = ToRegister64(lir->value());
+    Register64 tmp(ToRegister(lir->tmpHigh()), ToRegister(lir->tmpLow()));
+
+    BaseIndex addr(HeapReg, ptr, TimesOne, lir->mir()->access().offset());
+    masm.atomicExchange64(addr, value, tmp);
+}
+
+void
+CodeGeneratorARM::visitWasmCompareExchangeI64(LWasmCompareExchangeI64* lir)
+{
+    Register ptr = ToRegister(lir->ptr());
+    Register64 expected = ToRegister64(lir->expected());
+    Register64 replacement = ToRegister64(lir->replacement());
+    Register64 out = ToOutRegister64(lir);
+
+    BaseIndex addr(HeapReg, ptr, TimesOne, lir->mir()->access().offset());
+    masm.compareExchange64(addr, expected, replacement, out);
+}
+
+void
+CodeGeneratorARM::visitWasmAtomicBinopI64(LWasmAtomicBinopI64* lir)
+{
+    Register ptr = ToRegister(lir->ptr());
+    Register64 value = ToRegister64(lir->value());
+    Register64 out = ToOutRegister64(lir);
+
+    BaseIndex addr(HeapReg, ptr, TimesOne, lir->access().offset());
+    Register64 tmp(ToRegister(lir->tmpHigh()), ToRegister(lir->tmpLow()));
+    switch (lir->operation()) {
+      case AtomicFetchAddOp: masm.atomicFetchAdd64(value, addr, tmp, out); break;
+      case AtomicFetchSubOp: masm.atomicFetchSub64(value, addr, tmp, out); break;
+      case AtomicFetchAndOp: masm.atomicFetchAnd64(value, addr, tmp, out); break;
+      case AtomicFetchOrOp:  masm.atomicFetchOr64(value, addr, tmp, out); break;
+      case AtomicFetchXorOp: masm.atomicFetchXor64(value, addr, tmp, out); break;
+      default:               MOZ_CRASH();
+    }
+}
+
+void
+CodeGeneratorARM::visitWasmAtomicExchangeI64(LWasmAtomicExchangeI64* lir)
+{
+    Register ptr = ToRegister(lir->ptr());
+    Register64 value = ToRegister64(lir->value());
+    Register64 out = ToOutRegister64(lir);
+
+    BaseIndex addr(HeapReg, ptr, TimesOne, lir->access().offset());
+    masm.atomicExchange64(addr, value, out);
+}
--- a/js/src/jit/arm/CodeGenerator-arm.h
+++ b/js/src/jit/arm/CodeGenerator-arm.h
@@ -243,23 +243,23 @@ class CodeGeneratorARM : public CodeGene
     void visitWasmUnalignedLoadI64(LWasmUnalignedLoadI64* ins);
     void visitWasmAddOffset(LWasmAddOffset* ins);
     void visitWasmStore(LWasmStore* ins);
     void visitWasmStoreI64(LWasmStoreI64* ins);
     void visitWasmUnalignedStore(LWasmUnalignedStore* ins);
     void visitWasmUnalignedStoreI64(LWasmUnalignedStoreI64* ins);
     void visitAsmJSLoadHeap(LAsmJSLoadHeap* ins);
     void visitAsmJSStoreHeap(LAsmJSStoreHeap* ins);
-    void visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins);
-    void visitAsmJSCompareExchangeCallout(LAsmJSCompareExchangeCallout* ins);
-    void visitAsmJSAtomicExchangeHeap(LAsmJSAtomicExchangeHeap* ins);
-    void visitAsmJSAtomicExchangeCallout(LAsmJSAtomicExchangeCallout* ins);
-    void visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap* ins);
-    void visitAsmJSAtomicBinopHeapForEffect(LAsmJSAtomicBinopHeapForEffect* ins);
-    void visitAsmJSAtomicBinopCallout(LAsmJSAtomicBinopCallout* ins);
+    void visitWasmCompareExchangeHeap(LWasmCompareExchangeHeap* ins);
+    void visitWasmCompareExchangeCallout(LWasmCompareExchangeCallout* ins);
+    void visitWasmAtomicExchangeHeap(LWasmAtomicExchangeHeap* ins);
+    void visitWasmAtomicExchangeCallout(LWasmAtomicExchangeCallout* ins);
+    void visitWasmAtomicBinopHeap(LWasmAtomicBinopHeap* ins);
+    void visitWasmAtomicBinopHeapForEffect(LWasmAtomicBinopHeapForEffect* ins);
+    void visitWasmAtomicBinopCallout(LWasmAtomicBinopCallout* ins);
     void visitWasmStackArg(LWasmStackArg* ins);
     void visitWasmTruncateToInt32(LWasmTruncateToInt32* ins);
     void visitOutOfLineWasmTruncateCheck(OutOfLineWasmTruncateCheck* ool);
     void visitCopySignD(LCopySignD* ins);
     void visitCopySignF(LCopySignF* ins);
 
     void visitMemoryBarrier(LMemoryBarrier* ins);
 
@@ -273,16 +273,22 @@ class CodeGeneratorARM : public CodeGene
                                     const T& mem, Register flagTemp, Register outTemp,
                                     AnyRegister output);
 
     // Generating no result.
     template<typename S, typename T>
     void atomicBinopToTypedIntArray(AtomicOp op, Scalar::Type arrayType, const S& value,
                                     const T& mem, Register flagTemp);
 
+    void visitWasmAtomicLoadI64(LWasmAtomicLoadI64* lir);
+    void visitWasmAtomicStoreI64(LWasmAtomicStoreI64* lir);
+    void visitWasmCompareExchangeI64(LWasmCompareExchangeI64* lir);
+    void visitWasmAtomicBinopI64(LWasmAtomicBinopI64* lir);
+    void visitWasmAtomicExchangeI64(LWasmAtomicExchangeI64* lir);
+
   protected:
     void visitEffectiveAddress(LEffectiveAddress* ins);
     void visitUDiv(LUDiv* ins);
     void visitUMod(LUMod* ins);
     void visitSoftUDivOrMod(LSoftUDivOrMod* ins);
 
   public:
     // Unimplemented SIMD instructions
--- a/js/src/jit/arm/LIR-arm.h
+++ b/js/src/jit/arm/LIR-arm.h
@@ -457,23 +457,23 @@ class LSoftUDivOrMod : public LBinaryCal
         setOperand(1, rhs);
     }
 
     MInstruction* mir() {
         return mir_->toInstruction();
     }
 };
 
-class LAsmJSCompareExchangeCallout : public LCallInstructionHelper<1, 4, 2>
+class LWasmCompareExchangeCallout : public LCallInstructionHelper<1, 4, 2>
 {
   public:
-    LIR_HEADER(AsmJSCompareExchangeCallout)
-    LAsmJSCompareExchangeCallout(const LAllocation& ptr, const LAllocation& oldval,
-                                 const LAllocation& newval, const LAllocation& tls,
-                                 const LDefinition& temp1, const LDefinition& temp2)
+    LIR_HEADER(WasmCompareExchangeCallout)
+    LWasmCompareExchangeCallout(const LAllocation& ptr, const LAllocation& oldval,
+                                const LAllocation& newval, const LAllocation& tls,
+                                const LDefinition& temp1, const LDefinition& temp2)
     {
         setOperand(0, ptr);
         setOperand(1, oldval);
         setOperand(2, newval);
         setOperand(3, tls);
         setTemp(0, temp1);
         setTemp(1, temp2);
     }
@@ -485,29 +485,29 @@ class LAsmJSCompareExchangeCallout : pub
     }
     const LAllocation* newval() {
         return getOperand(2);
     }
     const LAllocation* tls() {
         return getOperand(3);
     }
 
-    const MAsmJSCompareExchangeHeap* mir() const {
-        return mir_->toAsmJSCompareExchangeHeap();
+    const MWasmCompareExchangeHeap* mir() const {
+        return mir_->toWasmCompareExchangeHeap();
     }
 };
 
-class LAsmJSAtomicExchangeCallout : public LCallInstructionHelper<1, 3, 2>
+class LWasmAtomicExchangeCallout : public LCallInstructionHelper<1, 3, 2>
 {
   public:
-    LIR_HEADER(AsmJSAtomicExchangeCallout)
+    LIR_HEADER(WasmAtomicExchangeCallout)
 
-    LAsmJSAtomicExchangeCallout(const LAllocation& ptr, const LAllocation& value,
-                                const LAllocation& tls, const LDefinition& temp1,
-                                const LDefinition& temp2)
+    LWasmAtomicExchangeCallout(const LAllocation& ptr, const LAllocation& value,
+                               const LAllocation& tls, const LDefinition& temp1,
+                               const LDefinition& temp2)
     {
         setOperand(0, ptr);
         setOperand(1, value);
         setOperand(2, tls);
         setTemp(0, temp1);
         setTemp(1, temp2);
     }
     const LAllocation* ptr() {
@@ -515,28 +515,28 @@ class LAsmJSAtomicExchangeCallout : publ
     }
     const LAllocation* value() {
         return getOperand(1);
     }
     const LAllocation* tls() {
         return getOperand(2);
     }
 
-    const MAsmJSAtomicExchangeHeap* mir() const {
-        return mir_->toAsmJSAtomicExchangeHeap();
+    const MWasmAtomicExchangeHeap* mir() const {
+        return mir_->toWasmAtomicExchangeHeap();
     }
 };
 
-class LAsmJSAtomicBinopCallout : public LCallInstructionHelper<1, 3, 2>
+class LWasmAtomicBinopCallout : public LCallInstructionHelper<1, 3, 2>
 {
   public:
-    LIR_HEADER(AsmJSAtomicBinopCallout)
-    LAsmJSAtomicBinopCallout(const LAllocation& ptr, const LAllocation& value,
-                             const LAllocation& tls, const LDefinition& temp1,
-                             const LDefinition& temp2)
+    LIR_HEADER(WasmAtomicBinopCallout)
+    LWasmAtomicBinopCallout(const LAllocation& ptr, const LAllocation& value,
+                            const LAllocation& tls, const LDefinition& temp1,
+                            const LDefinition& temp2)
     {
         setOperand(0, ptr);
         setOperand(1, value);
         setOperand(2, tls);
         setTemp(0, temp1);
         setTemp(1, temp2);
     }
     const LAllocation* ptr() {
@@ -544,18 +544,18 @@ class LAsmJSAtomicBinopCallout : public 
     }
     const LAllocation* value() {
         return getOperand(1);
     }
     const LAllocation* tls() {
         return getOperand(2);
     }
 
-    const MAsmJSAtomicBinopHeap* mir() const {
-        return mir_->toAsmJSAtomicBinopHeap();
+    const MWasmAtomicBinopHeap* mir() const {
+        return mir_->toWasmAtomicBinopHeap();
     }
 };
 
 class LWasmTruncateToInt64 : public LCallInstructionHelper<INT64_PIECES, 1, 0>
 {
   public:
     LIR_HEADER(WasmTruncateToInt64);
 
@@ -676,12 +676,153 @@ class LWasmUnalignedStoreI64 : public de
     LWasmUnalignedStoreI64(const LAllocation& ptr, const LInt64Allocation& value,
                            const LDefinition& ptrCopy, const LDefinition& valueHelper)
       : LWasmUnalignedStoreBase(ptr, ptrCopy, valueHelper)
     {
         setInt64Operand(1, value);
     }
 };
 
+class LWasmAtomicLoadI64 : public LInstructionHelper<INT64_PIECES, 1, 0>
+{
+  public:
+    LIR_HEADER(WasmAtomicLoadI64);
+
+    LWasmAtomicLoadI64(const LAllocation& ptr) {
+        setOperand(0, ptr);
+    }
+
+    MWasmLoad* mir() const {
+        return mir_->toWasmLoad();
+    }
+    const LAllocation* ptr() {
+        return getOperand(0);
+    }
+};
+
+class LWasmAtomicStoreI64 : public LInstructionHelper<0, 1 + INT64_PIECES, 2>
+{
+  public:
+    LIR_HEADER(WasmAtomicStoreI64);
+
+    LWasmAtomicStoreI64(const LAllocation& ptr, const LInt64Allocation& value,
+                        const LDefinition& tmpLow, const LDefinition& tmpHigh)
+    {
+        setOperand(0, ptr);
+        setInt64Operand(1, value);
+        setTemp(0, tmpLow);
+        setTemp(1, tmpHigh);
+    }
+
+    MWasmStore* mir() const {
+        return mir_->toWasmStore();
+    }
+    const LAllocation* ptr() {
+        return getOperand(0);
+    }
+    const LInt64Allocation value() {
+        return getInt64Operand(1);
+    }
+    const LDefinition* tmpLow() {
+        return getTemp(0);
+    }
+    const LDefinition* tmpHigh() {
+        return getTemp(1);
+    }
+};
+
+class LWasmCompareExchangeI64 : public LInstructionHelper<INT64_PIECES, 1 + 2*INT64_PIECES, 0>
+{
+  public:
+    LIR_HEADER(WasmCompareExchangeI64);
+
+    LWasmCompareExchangeI64(const LAllocation& ptr, const LInt64Allocation& expected,
+                            const LInt64Allocation& replacement)
+    {
+        setOperand(0, ptr);
+        setInt64Operand(1, expected);
+        setInt64Operand(1 + INT64_PIECES, replacement);
+    }
+
+    MWasmCompareExchangeHeap* mir() const {
+        return mir_->toWasmCompareExchangeHeap();
+    }
+    const LAllocation* ptr() {
+        return getOperand(0);
+    }
+    const LInt64Allocation expected() {
+        return getInt64Operand(1);
+    }
+    const LInt64Allocation replacement() {
+        return getInt64Operand(1 + INT64_PIECES);
+    }
+};
+
+class LWasmAtomicBinopI64 : public LInstructionHelper<INT64_PIECES, 1 + INT64_PIECES, 2>
+{
+    const wasm::MemoryAccessDesc& access_;
+    AtomicOp op_;
+
+  public:
+    LIR_HEADER(WasmAtomicBinopI64);
+
+    LWasmAtomicBinopI64(const LAllocation& ptr, const LInt64Allocation& value,
+                        const LDefinition& tmpLow, const LDefinition& tmpHigh,
+                        const wasm::MemoryAccessDesc& access, AtomicOp op)
+      : access_(access),
+        op_(op)
+    {
+        setOperand(0, ptr);
+        setInt64Operand(1, value);
+        setTemp(0, tmpLow);
+        setTemp(1, tmpHigh);
+    }
+
+    const LAllocation* ptr() {
+        return getOperand(0);
+    }
+    const LInt64Allocation value() {
+        return getInt64Operand(1);
+    }
+    const wasm::MemoryAccessDesc& access() {
+        return access_;
+    }
+    AtomicOp operation() const {
+        return op_;
+    }
+    const LDefinition* tmpLow() {
+        return getTemp(0);
+    }
+    const LDefinition* tmpHigh() {
+        return getTemp(1);
+    }
+};
+
+class LWasmAtomicExchangeI64 : public LInstructionHelper<INT64_PIECES, 1 + INT64_PIECES, 0>
+{
+    const wasm::MemoryAccessDesc& access_;
+
+  public:
+    LIR_HEADER(WasmAtomicExchangeI64);
+
+    LWasmAtomicExchangeI64(const LAllocation& ptr, const LInt64Allocation& value,
+                           const wasm::MemoryAccessDesc& access)
+      : access_(access)
+    {
+        setOperand(0, ptr);
+        setInt64Operand(1, value);
+    }
+
+    const LAllocation* ptr() {
+        return getOperand(0);
+    }
+    const LInt64Allocation value() {
+        return getInt64Operand(1);
+    }
+    const wasm::MemoryAccessDesc& access() {
+        return access_;
+    }
+};
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_arm_LIR_arm_h */
--- a/js/src/jit/arm/LOpcodes-arm.h
+++ b/js/src/jit/arm/LOpcodes-arm.h
@@ -12,21 +12,26 @@
 #define LIR_CPU_OPCODE_LIST(_)     \
     _(BoxFloatingPoint)            \
     _(SoftDivI)                    \
     _(SoftModI)                    \
     _(ModMaskI)                    \
     _(UDiv)                        \
     _(UMod)                        \
     _(SoftUDivOrMod)               \
-    _(AsmJSCompareExchangeCallout) \
-    _(AsmJSAtomicExchangeCallout)  \
-    _(AsmJSAtomicBinopCallout)     \
+    _(WasmCompareExchangeCallout)  \
+    _(WasmAtomicExchangeCallout)   \
+    _(WasmAtomicBinopCallout)      \
     _(DivOrModI64)                 \
     _(UDivOrModI64)                \
     _(WasmTruncateToInt64)         \
+    _(WasmAtomicLoadI64)           \
+    _(WasmAtomicStoreI64)          \
+    _(WasmCompareExchangeI64)      \
+    _(WasmAtomicBinopI64)          \
+    _(WasmAtomicExchangeI64)       \
     _(WasmUnalignedLoad)           \
     _(WasmUnalignedStore)          \
     _(WasmUnalignedLoadI64)        \
     _(WasmUnalignedStoreI64)       \
     _(Int64ToFloatingPointCall)
 
 #endif /* jit_arm_LOpcodes_arm_h */
--- a/js/src/jit/arm/Lowering-arm.cpp
+++ b/js/src/jit/arm/Lowering-arm.cpp
@@ -616,19 +616,28 @@ LIRGeneratorARM::visitWasmUnsignedToFloa
 }
 
 void
 LIRGeneratorARM::visitWasmLoad(MWasmLoad* ins)
 {
     MDefinition* base = ins->base();
     MOZ_ASSERT(base->type() == MIRType::Int32);
 
+    if (ins->access().type() == Scalar::Int64 && ins->access().isAtomic()) {
+        auto* lir = new(alloc()) LWasmAtomicLoadI64(useRegisterAtStart(base));
+        defineInt64Fixed(lir, ins, LInt64Allocation(LAllocation(AnyRegister(IntArgReg1)),
+                                                    LAllocation(AnyRegister(IntArgReg0))));
+        return;
+    }
+
     LAllocation ptr = useRegisterAtStart(base);
 
     if (IsUnaligned(ins->access())) {
+        MOZ_ASSERT(!ins->access().isAtomic());
+
         // Unaligned access expected! Revert to a byte load.
         LDefinition ptrCopy = tempCopy(base, 0);
 
         LDefinition noTemp = LDefinition::BogusTemp();
         if (ins->type() == MIRType::Int64) {
             auto* lir = new(alloc()) LWasmUnalignedLoadI64(ptr, ptrCopy, temp(), noTemp, noTemp);
             defineInt64(lir, ins);
             return;
@@ -665,19 +674,32 @@ LIRGeneratorARM::visitWasmLoad(MWasmLoad
 }
 
 void
 LIRGeneratorARM::visitWasmStore(MWasmStore* ins)
 {
     MDefinition* base = ins->base();
     MOZ_ASSERT(base->type() == MIRType::Int32);
 
+    if (ins->access().type() == Scalar::Int64 && ins->access().isAtomic()) {
+        auto* lir = new(alloc()) LWasmAtomicStoreI64(useRegister(base),
+                                                     useInt64Fixed(ins->value(),
+                                                                   Register64(IntArgReg1,
+                                                                              IntArgReg0)),
+                                                     tempFixed(IntArgReg2),
+                                                     tempFixed(IntArgReg3));
+        add(lir, ins);
+        return;
+    }
+
     LAllocation ptr = useRegisterAtStart(base);
 
     if (IsUnaligned(ins->access())) {
+        MOZ_ASSERT(!ins->access().isAtomic());
+
         // Unaligned access expected! Revert to a byte store.
         LDefinition ptrCopy = tempCopy(base, 0);
 
         MIRType valueType = ins->value()->type();
         if (valueType == MIRType::Int64) {
             LInt64Allocation value = useInt64RegisterAtStart(ins->value());
             auto* lir = new(alloc()) LWasmUnalignedStoreI64(ptr, value, ptrCopy, temp());
             add(lir, ins);
@@ -888,100 +910,135 @@ LIRGeneratorARM::visitCompareExchangeTyp
 
     LCompareExchangeTypedArrayElement* lir =
         new(alloc()) LCompareExchangeTypedArrayElement(elements, index, oldval, newval, tempDef);
 
     define(lir, ins);
 }
 
 void
-LIRGeneratorARM::visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap* ins)
+LIRGeneratorARM::visitWasmCompareExchangeHeap(MWasmCompareExchangeHeap* ins)
 {
-    MOZ_ASSERT(ins->access().type() < Scalar::Float32);
-    MOZ_ASSERT(ins->access().offset() == 0);
-
     MDefinition* base = ins->base();
     MOZ_ASSERT(base->type() == MIRType::Int32);
 
+    if (ins->access().type() == Scalar::Int64) {
+        auto* lir = new(alloc()) LWasmCompareExchangeI64(useRegister(base),
+                                                         useInt64Register(ins->oldValue()),
+                                                         useInt64Fixed(ins->newValue(),
+                                                                       Register64(IntArgReg3,
+                                                                                  IntArgReg2)));
+        defineInt64Fixed(lir, ins, LInt64Allocation(LAllocation(AnyRegister(IntArgReg1)),
+                                                    LAllocation(AnyRegister(IntArgReg0))));
+        return;
+    }
+
+    MOZ_ASSERT(ins->access().type() < Scalar::Float32);
+
     if (byteSize(ins->access().type()) != 4 && !HasLDSTREXBHD()) {
-        LAsmJSCompareExchangeCallout* lir =
-            new(alloc()) LAsmJSCompareExchangeCallout(useFixedAtStart(base, IntArgReg2),
-                                                      useFixedAtStart(ins->oldValue(), IntArgReg3),
-                                                      useFixedAtStart(ins->newValue(), CallTempReg0),
-                                                      useFixedAtStart(ins->tls(), WasmTlsReg),
-                                                      tempFixed(IntArgReg0),
-                                                      tempFixed(IntArgReg1));
+        MOZ_ASSERT(ins->access().offset() == 0);
+        LWasmCompareExchangeCallout* lir =
+            new(alloc()) LWasmCompareExchangeCallout(useFixedAtStart(base, IntArgReg2),
+                                                     useFixedAtStart(ins->oldValue(), IntArgReg3),
+                                                     useFixedAtStart(ins->newValue(), CallTempReg0),
+                                                     useFixedAtStart(ins->tls(), WasmTlsReg),
+                                                     tempFixed(IntArgReg0),
+                                                     tempFixed(IntArgReg1));
         defineReturn(lir, ins);
         return;
     }
 
-    LAsmJSCompareExchangeHeap* lir =
-        new(alloc()) LAsmJSCompareExchangeHeap(useRegister(base),
-                                               useRegister(ins->oldValue()),
-                                               useRegister(ins->newValue()));
+    LWasmCompareExchangeHeap* lir =
+        new(alloc()) LWasmCompareExchangeHeap(useRegister(base),
+                                              useRegister(ins->oldValue()),
+                                              useRegister(ins->newValue()));
 
     define(lir, ins);
 }
 
 void
-LIRGeneratorARM::visitAsmJSAtomicExchangeHeap(MAsmJSAtomicExchangeHeap* ins)
+LIRGeneratorARM::visitWasmAtomicExchangeHeap(MWasmAtomicExchangeHeap* ins)
 {
     MOZ_ASSERT(ins->base()->type() == MIRType::Int32);
-    MOZ_ASSERT(ins->access().type() < Scalar::Float32);
-    MOZ_ASSERT(ins->access().offset() == 0);
 
-    if (byteSize(ins->access().type()) < 4 && !HasLDSTREXBHD()) {
-        // Call out on ARMv6.
-        defineReturn(new(alloc()) LAsmJSAtomicExchangeCallout(useFixedAtStart(ins->base(), IntArgReg2),
-                                                              useFixedAtStart(ins->value(), IntArgReg3),
-                                                              useFixedAtStart(ins->tls(), WasmTlsReg),
-                                                              tempFixed(IntArgReg0),
-                                                              tempFixed(IntArgReg1)), ins);
+    if (ins->access().type() == Scalar::Int64) {
+        auto* lir = new(alloc()) LWasmAtomicExchangeI64(useRegister(ins->base()),
+                                                        useInt64Fixed(ins->value(),
+                                                                      Register64(IntArgReg3,
+                                                                                 IntArgReg2)),
+                                                        ins->access());
+        defineInt64Fixed(lir, ins, LInt64Allocation(LAllocation(AnyRegister(IntArgReg1)),
+                                                    LAllocation(AnyRegister(IntArgReg0))));
         return;
     }
 
-    const LAllocation base = useRegisterAtStart(ins->base());
-    const LAllocation value = useRegisterAtStart(ins->value());
-    define(new(alloc()) LAsmJSAtomicExchangeHeap(base, value), ins);
+    MOZ_ASSERT(ins->access().type() < Scalar::Float32);
+
+    if (byteSize(ins->access().type()) < 4 && !HasLDSTREXBHD()) {
+        MOZ_ASSERT(ins->access().offset() == 0);
+        // Call out on ARMv6.
+        defineReturn(new(alloc()) LWasmAtomicExchangeCallout(useFixedAtStart(ins->base(), IntArgReg2),
+                                                             useFixedAtStart(ins->value(), IntArgReg3),
+                                                             useFixedAtStart(ins->tls(), WasmTlsReg),
+                                                             tempFixed(IntArgReg0),
+                                                             tempFixed(IntArgReg1)), ins);
+        return;
+    }
+
+    const LAllocation base = useRegister(ins->base());
+    const LAllocation value = useRegister(ins->value());
+    define(new(alloc()) LWasmAtomicExchangeHeap(base, value), ins);
 }
 
 void
-LIRGeneratorARM::visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap* ins)
+LIRGeneratorARM::visitWasmAtomicBinopHeap(MWasmAtomicBinopHeap* ins)
 {
+    if (ins->access().type() == Scalar::Int64) {
+        auto* lir = new(alloc()) LWasmAtomicBinopI64(useRegister(ins->base()),
+                                                     useInt64Register(ins->value()),
+                                                     tempFixed(IntArgReg2),
+                                                     tempFixed(IntArgReg3),
+                                                     ins->access(),
+                                                     ins->operation());
+        defineInt64Fixed(lir, ins, LInt64Allocation(LAllocation(AnyRegister(IntArgReg1)),
+                                                    LAllocation(AnyRegister(IntArgReg0))));
+        return;
+    }
+
     MOZ_ASSERT(ins->access().type() < Scalar::Float32);
-    MOZ_ASSERT(ins->access().offset() == 0);
 
     MDefinition* base = ins->base();
     MOZ_ASSERT(base->type() == MIRType::Int32);
 
     if (byteSize(ins->access().type()) != 4 && !HasLDSTREXBHD()) {
-        LAsmJSAtomicBinopCallout* lir =
-            new(alloc()) LAsmJSAtomicBinopCallout(useFixedAtStart(base, IntArgReg2),
-                                                  useFixedAtStart(ins->value(), IntArgReg3),
-                                                  useFixedAtStart(ins->tls(), WasmTlsReg),
-                                                  tempFixed(IntArgReg0),
-                                                  tempFixed(IntArgReg1));
+        MOZ_ASSERT(ins->access().offset() == 0);
+        LWasmAtomicBinopCallout* lir =
+            new(alloc()) LWasmAtomicBinopCallout(useFixedAtStart(base, IntArgReg2),
+                                                 useFixedAtStart(ins->value(), IntArgReg3),
+                                                 useFixedAtStart(ins->tls(), WasmTlsReg),
+                                                 tempFixed(IntArgReg0),
+                                                 tempFixed(IntArgReg1));
         defineReturn(lir, ins);
         return;
     }
 
     if (!ins->hasUses()) {
-        LAsmJSAtomicBinopHeapForEffect* lir =
-            new(alloc()) LAsmJSAtomicBinopHeapForEffect(useRegister(base),
-                                                        useRegister(ins->value()),
-                                                        /* flagTemp= */ temp());
+        LWasmAtomicBinopHeapForEffect* lir =
+            new(alloc()) LWasmAtomicBinopHeapForEffect(useRegister(base),
+                                                       useRegister(ins->value()),
+                                                       /* flagTemp= */ temp());
         add(lir, ins);
         return;
     }
 
-    LAsmJSAtomicBinopHeap* lir =
-        new(alloc()) LAsmJSAtomicBinopHeap(useRegister(base),
-                                           useRegister(ins->value()),
-                                           /* temp = */ LDefinition::BogusTemp(),
-                                           /* flagTemp= */ temp());
+    LWasmAtomicBinopHeap* lir =
+        new(alloc()) LWasmAtomicBinopHeap(useRegister(base),
+                                          useRegister(ins->value()),
+                                          /* temp = */ LDefinition::BogusTemp(),
+                                          /* flagTemp= */ temp());
     define(lir, ins);
 }
 
 void
 LIRGeneratorARM::visitSubstr(MSubstr* ins)
 {
     LSubstr* lir = new (alloc()) LSubstr(useRegister(ins->string()),
                                          useRegister(ins->begin()),
--- a/js/src/jit/arm/Lowering-arm.h
+++ b/js/src/jit/arm/Lowering-arm.h
@@ -104,19 +104,19 @@ class LIRGeneratorARM : public LIRGenera
     void visitGuardObjectGroup(MGuardObjectGroup* ins);
     void visitWasmSelect(MWasmSelect* ins);
     void visitWasmUnsignedToDouble(MWasmUnsignedToDouble* ins);
     void visitWasmUnsignedToFloat32(MWasmUnsignedToFloat32* ins);
     void visitWasmLoad(MWasmLoad* ins);
     void visitWasmStore(MWasmStore* ins);
     void visitAsmJSLoadHeap(MAsmJSLoadHeap* ins);
     void visitAsmJSStoreHeap(MAsmJSStoreHeap* ins);
-    void visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap* ins);
-    void visitAsmJSAtomicExchangeHeap(MAsmJSAtomicExchangeHeap* ins);
-    void visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap* ins);
+    void visitWasmCompareExchangeHeap(MWasmCompareExchangeHeap* ins);
+    void visitWasmAtomicExchangeHeap(MWasmAtomicExchangeHeap* ins);
+    void visitWasmAtomicBinopHeap(MWasmAtomicBinopHeap* ins);
     void visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins);
     void visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement* ins);
     void visitAtomicExchangeTypedArrayElement(MAtomicExchangeTypedArrayElement* ins);
     void visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins);
     void visitSubstr(MSubstr* ins);
     void visitRandom(MRandom* ins);
     void visitWasmTruncateToInt64(MWasmTruncateToInt64* ins);
     void visitInt64ToFloatingPoint(MInt64ToFloatingPoint* ins);
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -7,16 +7,17 @@
 #include "jit/arm/MacroAssembler-arm.h"
 
 #include "mozilla/Attributes.h"
 #include "mozilla/Casting.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/MathAlgorithms.h"
 
 #include "jit/arm/Simulator-arm.h"
+#include "jit/AtomicOperations.h"
 #include "jit/Bailouts.h"
 #include "jit/BaselineFrame.h"
 #include "jit/JitFrames.h"
 #include "jit/MacroAssembler.h"
 #include "jit/MoveEmitter.h"
 
 #include "jit/MacroAssembler-inl.h"
 
@@ -4231,17 +4232,18 @@ MacroAssemblerARMCompat::compareExchange
 
 template<typename T>
 void
 MacroAssemblerARMCompat::compareExchangeARMv7(int nbytes, bool signExtend, const T& mem,
                                               Register oldval, Register newval, Register output)
 {
     Label again;
     Label done;
-    ma_dmb(BarrierST);
+
+    asMasm().memoryBarrier(MembarFull);
 
     SecondScratchRegisterScope scratch2(asMasm());
     Register ptr = computePointer(mem, scratch2);
 
     ScratchRegisterScope scratch(asMasm());
 
     bind(&again);
     switch (nbytes) {
@@ -4282,17 +4284,18 @@ MacroAssemblerARMCompat::compareExchange
         break;
       case 4:
         as_strex(scratch, newval, ptr);
         break;
     }
     as_cmp(scratch, Imm8(1));
     as_b(&again, Equal);
     bind(&done);
-    ma_dmb();
+
+    asMasm().memoryBarrier(MembarFull);
 }
 
 template<typename T>
 void
 MacroAssemblerARMCompat::compareExchangeARMv6(int nbytes, bool signExtend, const T& mem,
                                               Register oldval, Register newval, Register output)
 {
     // Bug 1077318: Must use read-modify-write with LDREX / STREX.
@@ -4327,17 +4330,18 @@ MacroAssemblerARMCompat::atomicExchange(
 
 template<typename T>
 void
 MacroAssemblerARMCompat::atomicExchangeARMv7(int nbytes, bool signExtend, const T& mem,
                                              Register value, Register output)
 {
     Label again;
     Label done;
-    ma_dmb(BarrierST);
+
+    asMasm().memoryBarrier(MembarFull);
 
     SecondScratchRegisterScope scratch2(asMasm());
     Register ptr = computePointer(mem, scratch2);
 
     ScratchRegisterScope scratch(asMasm());
 
     bind(&again);
     switch (nbytes) {
@@ -4359,17 +4363,18 @@ MacroAssemblerARMCompat::atomicExchangeA
         as_strex(scratch, value, ptr);
         break;
       default:
         MOZ_CRASH();
     }
     as_cmp(scratch, Imm8(1));
     as_b(&again, Equal);
     bind(&done);
-    ma_dmb();
+
+    asMasm().memoryBarrier(MembarFull);
 }
 
 template<typename T>
 void
 MacroAssemblerARMCompat::atomicExchangeARMv6(int nbytes, bool signExtend, const T& mem,
                                              Register value, Register output)
 {
     // Bug 1077318: Must use read-modify-write with LDREX / STREX.
@@ -4435,23 +4440,24 @@ MacroAssemblerARMCompat::atomicFetchOp(i
 
 template<typename T>
 void
 MacroAssemblerARMCompat::atomicFetchOpARMv7(int nbytes, bool signExtend, AtomicOp op,
                                             const Register& value, const T& mem, Register flagTemp,
                                             Register output)
 {
     MOZ_ASSERT(flagTemp != InvalidReg);
+    MOZ_ASSERT(output != value);
 
     Label again;
 
     SecondScratchRegisterScope scratch2(asMasm());
     Register ptr = computePointer(mem, scratch2);
 
-    ma_dmb();
+    asMasm().memoryBarrier(MembarFull);
 
     ScratchRegisterScope scratch(asMasm());
 
     bind(&again);
     switch (nbytes) {
       case 1:
         as_ldrexb(output, ptr);
         if (signExtend)
@@ -4493,30 +4499,30 @@ MacroAssemblerARMCompat::atomicFetchOpAR
         as_strexh(flagTemp, scratch, ptr);
         break;
       case 4:
         as_strex(flagTemp, scratch, ptr);
         break;
     }
     as_cmp(flagTemp, Imm8(1));
     as_b(&again, Equal);
-    ma_dmb();
+
+    asMasm().memoryBarrier(MembarFull);
 }
 
 template<typename T>
 void
 MacroAssemblerARMCompat::atomicFetchOpARMv6(int nbytes, bool signExtend, AtomicOp op,
                                             const Register& value, const T& mem, Register flagTemp,
                                             Register output)
 {
     // Bug 1077318: Must use read-modify-write with LDREX / STREX.
     MOZ_ASSERT(nbytes == 1 || nbytes == 2);
     MOZ_CRASH("NYI");
 }
-
 template<typename T>
 void
 MacroAssemblerARMCompat::atomicEffectOp(int nbytes, AtomicOp op, const Register& value,
                                         const T& mem, Register flagTemp)
 {
     // Fork for non-word operations on ARMv6.
     //
     // Bug 1077321: We may further optimize for ARMv8 (AArch32) here.
@@ -4558,17 +4564,17 @@ MacroAssemblerARMCompat::atomicEffectOpA
 {
     MOZ_ASSERT(flagTemp != InvalidReg);
 
     Label again;
 
     SecondScratchRegisterScope scratch2(asMasm());
     Register ptr = computePointer(mem, scratch2);
 
-    ma_dmb();
+    asMasm().memoryBarrier(MembarFull);
 
     ScratchRegisterScope scratch(asMasm());
 
     bind(&again);
     switch (nbytes) {
       case 1:
         as_ldrexb(scratch, ptr);
         break;
@@ -4605,17 +4611,18 @@ MacroAssemblerARMCompat::atomicEffectOpA
         as_strexh(flagTemp, scratch, ptr);
         break;
       case 4:
         as_strex(flagTemp, scratch, ptr);
         break;
     }
     as_cmp(flagTemp, Imm8(1));
     as_b(&again, Equal);
-    ma_dmb();
+
+    asMasm().memoryBarrier(MembarFull);
 }
 
 template<typename T>
 void
 MacroAssemblerARMCompat::atomicEffectOpARMv6(int nbytes, AtomicOp op, const Register& value,
                                              const T& mem, Register flagTemp)
 {
     // Bug 1077318: Must use read-modify-write with LDREX / STREX.
@@ -4731,16 +4738,190 @@ MacroAssemblerARMCompat::atomicExchangeT
 
 template void
 MacroAssemblerARMCompat::atomicExchangeToTypedIntArray(Scalar::Type arrayType, const Address& mem,
                                                        Register value, Register temp, AnyRegister output);
 template void
 MacroAssemblerARMCompat::atomicExchangeToTypedIntArray(Scalar::Type arrayType, const BaseIndex& mem,
                                                        Register value, Register temp, AnyRegister output);
 
+template<typename T>
+void
+MacroAssemblerARMCompat::atomicLoad64(const T& mem, Register64 temp, Register64 output)
+{
+    MOZ_ASSERT(temp.low == InvalidReg && temp.high == InvalidReg);
+    MOZ_ASSERT((output.low.code() & 1) == 0);
+    MOZ_ASSERT(output.low.code() + 1 == output.high.code());
+
+    asMasm().memoryBarrier(MembarFull);
+
+    SecondScratchRegisterScope scratch2(asMasm());
+    Register ptr = computePointer(mem, scratch2);
+
+    as_ldrexd(output.low, output.high, ptr);
+    as_clrex();
+
+    asMasm().memoryBarrier(MembarFull);
+}
+
+template void
+MacroAssemblerARMCompat::atomicLoad64(const Address& mem, Register64 temp, Register64 output);
+template void
+MacroAssemblerARMCompat::atomicLoad64(const BaseIndex& mem, Register64 temp, Register64 output);
+
+template<typename T>
+void
+MacroAssemblerARMCompat::atomicFetchOp64(AtomicOp op, Register64 value, const T& mem,
+                                         Register64 temp, Register64 output)
+{
+    MOZ_ASSERT(temp.low != InvalidReg && temp.high != InvalidReg);
+    MOZ_ASSERT(output != value);
+
+    MOZ_ASSERT((temp.low.code() & 1) == 0);
+    MOZ_ASSERT(temp.low.code() + 1 == temp.high.code());
+
+    // We could avoid this pair requirement but in that case we would end up
+    // with two moves in the loop to preserve the loaded value in output.  The
+    // prize would be less register spilling around this op since the pair
+    // requirement will tend to force more spilling.
+
+    MOZ_ASSERT((output.low.code() & 1) == 0);
+    MOZ_ASSERT(output.low.code() + 1 == output.high.code());
+
+    Label again;
+
+    SecondScratchRegisterScope scratch2(asMasm());
+    Register ptr = computePointer(mem, scratch2);
+
+    asMasm().memoryBarrier(MembarFull);
+
+    bind(&again);
+    as_ldrexd(output.low, output.high, ptr);
+    switch (op) {
+      case AtomicFetchAddOp:
+        as_add(temp.low, output.low, O2Reg(value.low), SetCC);
+        as_adc(temp.high, output.high, O2Reg(value.high));
+        break;
+      case AtomicFetchSubOp:
+        as_sub(temp.low, output.low, O2Reg(value.low), SetCC);
+        as_sbc(temp.high, output.high, O2Reg(value.high));
+        break;
+      case AtomicFetchAndOp:
+        as_and(temp.low, output.low, O2Reg(value.low));
+        as_and(temp.high, output.high, O2Reg(value.high));
+        break;
+      case AtomicFetchOrOp:
+        as_orr(temp.low, output.low, O2Reg(value.low));
+        as_orr(temp.high, output.high, O2Reg(value.high));
+        break;
+      case AtomicFetchXorOp:
+        as_eor(temp.low, output.low, O2Reg(value.low));
+        as_eor(temp.high, output.high, O2Reg(value.high));
+        break;
+    }
+
+    ScratchRegisterScope scratch(asMasm());
+
+    // Rd (temp) must differ from the two other arguments to strex.
+    as_strexd(scratch, temp.low, temp.high, ptr);
+    as_cmp(scratch, Imm8(1));
+    as_b(&again, Equal);
+
+    asMasm().memoryBarrier(MembarFull);
+}
+
+template void
+MacroAssemblerARMCompat::atomicFetchOp64(AtomicOp op, Register64 value, const Address& mem,
+                                         Register64 temp, Register64 output);
+template void
+MacroAssemblerARMCompat::atomicFetchOp64(AtomicOp op, Register64 value, const BaseIndex& mem,
+                                         Register64 temp, Register64 output);
+
+template<typename T>
+void
+MacroAssemblerARMCompat::atomicExchange64(const T& mem, Register64 value, Register64 output)
+{
+    MOZ_ASSERT(output != value);
+
+    MOZ_ASSERT((value.low.code() & 1) == 0);
+    MOZ_ASSERT(value.low.code() + 1 == value.high.code());
+
+    MOZ_ASSERT((output.low.code() & 1) == 0);
+    MOZ_ASSERT(output.low.code() + 1 == output.high.code());
+
+    Label again;
+
+    SecondScratchRegisterScope scratch2(asMasm());
+    Register ptr = computePointer(mem, scratch2);
+
+    asMasm().memoryBarrier(MembarFull);
+
+    bind(&again);
+    as_ldrexd(output.low, output.high, ptr);
+
+    ScratchRegisterScope scratch(asMasm());
+
+    as_strexd(scratch, value.low, value.high, ptr);
+    as_cmp(scratch, Imm8(1));
+    as_b(&again, Equal);
+
+    asMasm().memoryBarrier(MembarFull);
+}
+
+template void
+MacroAssemblerARMCompat::atomicExchange64(const Address& mem, Register64 value, Register64 output);
+template void
+MacroAssemblerARMCompat::atomicExchange64(const BaseIndex& mem, Register64 value, Register64 output);
+
+template<typename T>
+void
+MacroAssemblerARMCompat::compareExchange64(const T& mem, Register64 expect,
+                                           Register64 replace, Register64 output)
+{
+    MOZ_ASSERT(expect != replace && replace != output && output != expect);
+
+    MOZ_ASSERT((replace.low.code() & 1) == 0);
+    MOZ_ASSERT(replace.low.code() + 1 == replace.high.code());
+
+    MOZ_ASSERT((output.low.code() & 1) == 0);
+    MOZ_ASSERT(output.low.code() + 1 == output.high.code());
+
+    Label again;
+    Label done;
+
+    SecondScratchRegisterScope scratch2(asMasm());
+    Register ptr = computePointer(mem, scratch2);
+
+    asMasm().memoryBarrier(MembarFull);
+
+    bind(&again);
+    as_ldrexd(output.low, output.high, ptr);
+
+    as_cmp(output.low, O2Reg(expect.low));
+    as_cmp(output.high, O2Reg(expect.high), Equal);
+    as_b(&done, NotEqual);
+
+    ScratchRegisterScope scratch(asMasm());
+
+    // Rd (temp) must differ from the two other arguments to strex.
+    as_strexd(scratch, replace.low, replace.high, ptr);
+    as_cmp(scratch, Imm8(1));
+    as_b(&again, Equal);
+    bind(&done);
+
+    asMasm().memoryBarrier(MembarFull);
+}
+
+template void
+MacroAssemblerARMCompat::compareExchange64(const Address& mem, Register64 expect,
+                                           Register64 replace, Register64 output);
+template void
+MacroAssemblerARMCompat::compareExchange64(const BaseIndex& mem, Register64 expect,
+                                           Register64 replace, Register64 output);
+
 void
 MacroAssemblerARMCompat::profilerEnterFrame(Register framePtr, Register scratch)
 {
     asMasm().loadJSContext(scratch);
     loadPtr(Address(scratch, offsetof(JSContext, profilingActivation_)), scratch);
     storePtr(framePtr, Address(scratch, JitActivation::offsetOfLastProfilingFrame()));
     storePtr(ImmPtr(nullptr), Address(scratch, JitActivation::offsetOfLastProfilingCallSite()));
 }
@@ -5518,30 +5699,32 @@ MacroAssembler::wasmLoad(const wasm::Mem
 {
     wasmLoadImpl(access, memoryBase, ptr, ptrScratch, output, Register64::Invalid());
 }
 
 void
 MacroAssembler::wasmLoadI64(const wasm::MemoryAccessDesc& access, Register memoryBase, Register ptr,
                             Register ptrScratch, Register64 output)
 {
+    MOZ_ASSERT_IF(access.isAtomic(), access.byteSize() <= 4);
     wasmLoadImpl(access, memoryBase, ptr, ptrScratch, AnyRegister(), output);
 }
 
 void
 MacroAssembler::wasmStore(const wasm::MemoryAccessDesc& access, AnyRegister value,
                           Register memoryBase, Register ptr, Register ptrScratch)
 {
     wasmStoreImpl(access, value, Register64::Invalid(), memoryBase, ptr, ptrScratch);
 }
 
 void
 MacroAssembler::wasmStoreI64(const wasm::MemoryAccessDesc& access, Register64 value,
                              Register memoryBase, Register ptr, Register ptrScratch)
 {
+    MOZ_ASSERT(!access.isAtomic());
     wasmStoreImpl(access, AnyRegister(), value, memoryBase, ptr, ptrScratch);
 }
 
 void
 MacroAssembler::wasmUnalignedLoad(const wasm::MemoryAccessDesc& access, Register memoryBase,
                                   Register ptr, Register ptrScratch, Register output, Register tmp)
 {
     wasmUnalignedLoadImpl(access, memoryBase, ptr, ptrScratch, AnyRegister(output),
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -1162,16 +1162,20 @@ class MacroAssemblerARMCompat : public M
     template<typename T>
     void atomicEffectOp(int nbytes, AtomicOp op, const Imm32& value, const T& address,
                              Register flagTemp);
 
     template<typename T>
     void atomicEffectOp(int nbytes, AtomicOp op, const Register& value, const T& address,
                              Register flagTemp);
 
+    template<typename T>
+    void atomicFetchOp64(AtomicOp op, Register64 value, const T& mem, Register64 temp,
+                         Register64 output);
+
   public:
     // T in {Address,BaseIndex}
     // S in {Imm32,Register}
 
     template<typename T>
     void compareExchange8SignExtend(const T& mem, Register oldval, Register newval, Register output)
     {
         compareExchange(1, true, mem, oldval, newval, output);
@@ -1381,16 +1385,58 @@ class MacroAssemblerARMCompat : public M
     void atomicXor16(const S& value, const T& mem, Register flagTemp) {
         atomicEffectOp(2, AtomicFetchXorOp, value, mem, flagTemp);
     }
     template <typename T, typename S>
     void atomicXor32(const S& value, const T& mem, Register flagTemp) {
         atomicEffectOp(4, AtomicFetchXorOp, value, mem, flagTemp);
     }
 
+    // Temp should be invalid; output must be (even,odd) pair.
+    template<typename T>
+    void atomicLoad64(const T& mem, Register64 temp, Register64 output);
+
+    // Registers must be distinct; temp and output must be (even,odd) pairs.
+    template <typename T>
+    void atomicFetchAdd64(Register64 value, const T& mem, Register64 temp, Register64 output) {
+        atomicFetchOp64(AtomicFetchAddOp, value, mem, temp, output);
+    }
+
+    // Registers must be distinct; temp and output must be (even,odd) pairs.
+    template <typename T>
+    void atomicFetchSub64(Register64 value, const T& mem, Register64 temp, Register64 output) {
+        atomicFetchOp64(AtomicFetchSubOp, value, mem, temp, output);
+    }
+
+    // Registers must be distinct; temp and output must be (even,odd) pairs.
+    template <typename T>
+    void atomicFetchAnd64(Register64 value, const T& mem, Register64 temp, Register64 output) {
+        atomicFetchOp64(AtomicFetchAndOp, value, mem, temp, output);
+    }
+
+    // Registers must be distinct; temp and output must be (even,odd) pairs.
+    template <typename T>
+    void atomicFetchOr64(Register64 value, const T& mem, Register64 temp, Register64 output) {
+        atomicFetchOp64(AtomicFetchOrOp, value, mem, temp, output);
+    }
+
+    // Registers must be distinct; temp and output must be (even,odd) pairs.
+    template <typename T>
+    void atomicFetchXor64(Register64 value, const T& mem, Register64 temp, Register64 output) {
+        atomicFetchOp64(AtomicFetchXorOp, value, mem, temp, output);
+    }
+
+    // Registers must be distinct; value and output must be (even,odd) pairs.
+    template <typename T>
+    void atomicExchange64(const T& mem, Register64 value, Register64 output);
+
+    // Registers must be distinct; replace and output must be (even,odd) pairs.
+    template <typename T>
+    void compareExchange64(const T& mem, Register64 expect, Register64 replace, Register64 output);
+
     template<typename T>
     void compareExchangeToTypedIntArray(Scalar::Type arrayType, const T& mem, Register oldval, Register newval,
                                         Register temp, AnyRegister output);
 
     template<typename T>
     void atomicExchangeToTypedIntArray(Scalar::Type arrayType, const T& mem, Register value,
                                        Register temp, AnyRegister output);
 
--- a/js/src/jit/arm/Simulator-arm.cpp
+++ b/js/src/jit/arm/Simulator-arm.cpp
@@ -2490,16 +2490,20 @@ typedef int64_t (*Prototype_General4)(in
 typedef int64_t (*Prototype_General5)(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3,
                                       int32_t arg4);
 typedef int64_t (*Prototype_General6)(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3,
                                       int32_t arg4, int32_t arg5);
 typedef int64_t (*Prototype_General7)(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3,
                                       int32_t arg4, int32_t arg5, int32_t arg6);
 typedef int64_t (*Prototype_General8)(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3,
                                       int32_t arg4, int32_t arg5, int32_t arg6, int32_t arg7);
+typedef int64_t (*Prototype_GeneralGeneralGeneralInt64)(int32_t arg0, int32_t arg1, int32_t arg2,
+                                                        int64_t arg3);
+typedef int64_t (*Prototype_GeneralGeneralInt64Int64)(int32_t arg0, int32_t arg1, int64_t arg2,
+                                                      int64_t arg3);
 
 typedef double (*Prototype_Double_None)();
 typedef double (*Prototype_Double_Double)(double arg0);
 typedef double (*Prototype_Double_Int)(int32_t arg0);
 typedef double (*Prototype_Double_IntInt)(int32_t arg0, int32_t arg1);
 typedef int32_t (*Prototype_Int_Double)(double arg0);
 typedef int64_t (*Prototype_Int64_Double)(double arg0);
 typedef int32_t (*Prototype_Int_DoubleIntInt)(double arg0, int32_t arg1, int32_t arg2);
@@ -2541,16 +2545,23 @@ Simulator::scratchVolatileRegisters(bool
         uint64_t scratch_value_d = 0x5a5a5a5a5a5a5a5aLU ^ uint64_t(icount_) ^ (uint64_t(icount_) << 30);
         for (uint32_t i = d0; i < d8; i++)
             set_d_register(i, &scratch_value_d);
         for (uint32_t i = d16; i < FloatRegisters::TotalPhys; i++)
             set_d_register(i, &scratch_value_d);
     }
 }
 
+static int64_t
+MakeInt64(int32_t first, int32_t second)
+{
+    // Little-endian order.
+    return ((int64_t)second << 32) | (uint32_t)first;
+}
+
 // Software interrupt instructions are used by the simulator to call into C++.
 void
 Simulator::softwareInterrupt(SimInstruction* instr)
 {
     int svc = instr->svcValue();
     switch (svc) {
       case kCallRtRedirected: {
         Redirection* redirection = Redirection::FromSwiInstruction(instr);
@@ -2641,16 +2652,33 @@ Simulator::softwareInterrupt(SimInstruct
             Prototype_General8 target = reinterpret_cast<Prototype_General8>(external);
             int32_t arg6 = stack_pointer[2];
             int32_t arg7 = stack_pointer[3];
             int64_t result = target(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
             scratchVolatileRegisters(/* scratchFloat = true */);
             setCallResult(result);
             break;
           }
+          case Args_Int_GeneralGeneralGeneralInt64: {
+            Prototype_GeneralGeneralGeneralInt64 target =
+                reinterpret_cast<Prototype_GeneralGeneralGeneralInt64>(external);
+            // The int64 arg is not split across register and stack
+            int64_t result = target(arg0, arg1, arg2, MakeInt64(arg4, arg5));
+            scratchVolatileRegisters(/* scratchFloat = true */);
+            setCallResult(result);
+            break;
+          }
+          case Args_Int_GeneralGeneralInt64Int64: {
+            Prototype_GeneralGeneralInt64Int64 target =
+                reinterpret_cast<Prototype_GeneralGeneralInt64Int64>(external);
+            int64_t result = target(arg0, arg1, MakeInt64(arg2, arg3), MakeInt64(arg4, arg5));
+            scratchVolatileRegisters(/* scratchFloat = true */);
+            setCallResult(result);
+            break;
+          }
           case Args_Int64_Double: {
             double dval0, dval1;
             int32_t ival;
             getFpArgs(&dval0, &dval1, &ival);
             Prototype_Int64_Double target = reinterpret_cast<Prototype_Int64_Double>(external);
             int64_t result = target(dval0);
             scratchVolatileRegisters(/* scratchFloat = true */);
             setCallResult(result);
--- a/js/src/jit/arm/Trampoline-arm.cpp
+++ b/js/src/jit/arm/Trampoline-arm.cpp
@@ -886,37 +886,58 @@ JitRuntime::generateVMWrapper(JSContext*
     return functionWrappers_->putNew(&f, wrapperOffset);
 }
 
 uint32_t
 JitRuntime::generatePreBarrier(JSContext* cx, MacroAssembler& masm, MIRType type)
 {
     uint32_t offset = startTrampolineCode(masm);
 
+    masm.pushReturnAddress();
+
+    MOZ_ASSERT(PreBarrierReg == r1);
+    Register temp1 = r2;
+    Register temp2 = r3;
+    Register temp3 = r4;
+    masm.push(temp1);
+    masm.push(temp2);
+    masm.push(temp3);
+
+    Label noBarrier;
+    masm.emitPreBarrierFastPath(cx->runtime(), type, temp1, temp2, temp3, &noBarrier);
+
+    // Call into C++ to mark this GC thing.
+    masm.pop(temp3);
+    masm.pop(temp2);
+    masm.pop(temp1);
+
     LiveRegisterSet save;
     if (cx->runtime()->jitSupportsFloatingPoint) {
         save.set() = RegisterSet(GeneralRegisterSet(Registers::VolatileMask),
                                  FloatRegisterSet(FloatRegisters::VolatileDoubleMask));
     } else {
         save.set() = RegisterSet(GeneralRegisterSet(Registers::VolatileMask),
                                  FloatRegisterSet());
     }
-    save.add(lr);
     masm.PushRegsInMask(save);
 
-    MOZ_ASSERT(PreBarrierReg == r1);
     masm.movePtr(ImmPtr(cx->runtime()), r0);
 
     masm.setupUnalignedABICall(r2);
     masm.passABIArg(r0);
     masm.passABIArg(r1);
-    masm.callWithABI(IonMarkFunction(type));
-    save.take(AnyRegister(lr));
-    save.add(pc);
+    masm.callWithABI(JitMarkFunction(type));
     masm.PopRegsInMask(save);
+    masm.ret();
+
+    masm.bind(&noBarrier);
+    masm.pop(temp3);
+    masm.pop(temp2);
+    masm.pop(temp1);
+    masm.ret();
 
     return offset;
 }
 
 typedef bool (*HandleDebugTrapFn)(JSContext*, BaselineFrame*, uint8_t*, bool*);
 static const VMFunction HandleDebugTrapInfo =
     FunctionInfo<HandleDebugTrapFn>(HandleDebugTrap, "HandleDebugTrap");
 
--- a/js/src/jit/arm64/CodeGenerator-arm64.cpp
+++ b/js/src/jit/arm64/CodeGenerator-arm64.cpp
@@ -644,25 +644,25 @@ CodeGeneratorARM64::visitAsmJSLoadHeap(L
 
 void
 CodeGeneratorARM64::visitAsmJSStoreHeap(LAsmJSStoreHeap* ins)
 {
     MOZ_CRASH("visitAsmJSStoreHeap");
 }
 
 void
-CodeGeneratorARM64::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins)
+CodeGeneratorARM64::visitWasmCompareExchangeHeap(LWasmCompareExchangeHeap* ins)
 {
-    MOZ_CRASH("visitAsmJSCompareExchangeHeap");
+    MOZ_CRASH("visitWasmCompareExchangeHeap");
 }
 
 void
-CodeGeneratorARM64::visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap* ins)
+CodeGeneratorARM64::visitWasmAtomicBinopHeap(LWasmAtomicBinopHeap* ins)
 {
-    MOZ_CRASH("visitAsmJSAtomicBinopHeap");
+    MOZ_CRASH("visitWasmAtomicBinopHeap");
 }
 
 void
 CodeGeneratorARM64::visitWasmStackArg(LWasmStackArg* ins)
 {
     MOZ_CRASH("visitWasmStackArg");
 }
 
--- a/js/src/jit/arm64/CodeGenerator-arm64.h
+++ b/js/src/jit/arm64/CodeGenerator-arm64.h
@@ -195,18 +195,18 @@ class CodeGeneratorARM64 : public CodeGe
     void visitNegD(LNegD* lir);
     void visitNegF(LNegF* lir);
     void visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic* ins);
     void visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic* ins);
     void visitCompareExchangeTypedArrayElement(LCompareExchangeTypedArrayElement* lir);
     void visitAtomicExchangeTypedArrayElement(LAtomicExchangeTypedArrayElement* lir);
     void visitAsmJSLoadHeap(LAsmJSLoadHeap* ins);
     void visitAsmJSStoreHeap(LAsmJSStoreHeap* ins);
-    void visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins);
-    void visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap* ins);
+    void visitWasmCompareExchangeHeap(LWasmCompareExchangeHeap* ins);
+    void visitWasmAtomicBinopHeap(LWasmAtomicBinopHeap* ins);
     void visitWasmStackArg(LWasmStackArg* ins);
 
     void generateInvalidateEpilogue();
 
     void setReturnDoubleRegs(LiveRegisterSet* regs);
 
   protected:
     void postWasmCall(LWasmCall* lir) {
--- a/js/src/jit/arm64/Lowering-arm64.cpp
+++ b/js/src/jit/arm64/Lowering-arm64.cpp
@@ -259,31 +259,31 @@ LIRGeneratorARM64::visitAsmJSLoadHeap(MA
 
 void
 LIRGeneratorARM64::visitAsmJSStoreHeap(MAsmJSStoreHeap* ins)
 {
     MOZ_CRASH("visitAsmJSStoreHeap");
 }
 
 void
-LIRGeneratorARM64::visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap* ins)
+LIRGeneratorARM64::visitWasmCompareExchangeHeap(MWasmCompareExchangeHeap* ins)
 {
-    MOZ_CRASH("visitAsmJSCompareExchangeHeap");
+    MOZ_CRASH("visitWasmCompareExchangeHeap");
 }
 
 void
-LIRGeneratorARM64::visitAsmJSAtomicExchangeHeap(MAsmJSAtomicExchangeHeap* ins)
+LIRGeneratorARM64::visitWasmAtomicExchangeHeap(MWasmAtomicExchangeHeap* ins)
 {
-    MOZ_CRASH("visitAsmJSAtomicExchangeHeap");
+    MOZ_CRASH("visitWasmAtomicExchangeHeap");
 }
 
 void
-LIRGeneratorARM64::visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap* ins)
+LIRGeneratorARM64::visitWasmAtomicBinopHeap(MWasmAtomicBinopHeap* ins)
 {
-    MOZ_CRASH("visitAsmJSAtomicBinopHeap");
+    MOZ_CRASH("visitWasmAtomicBinopHeap");
 }
 
 void
 LIRGeneratorARM64::lowerTruncateDToInt32(MTruncateToInt32* ins)
 {
     MOZ_CRASH("lowerTruncateDToInt32");
 }
 
--- a/js/src/jit/arm64/Lowering-arm64.h
+++ b/js/src/jit/arm64/Lowering-arm64.h
@@ -102,19 +102,19 @@ class LIRGeneratorARM64 : public LIRGene
     void visitReturn(MReturn* ret);
     void lowerPhi(MPhi* phi);
     void visitGuardShape(MGuardShape* ins);
     void visitGuardObjectGroup(MGuardObjectGroup* ins);
     void visitWasmUnsignedToDouble(MWasmUnsignedToDouble* ins);
     void visitWasmUnsignedToFloat32(MWasmUnsignedToFloat32* ins);
     void visitAsmJSLoadHeap(MAsmJSLoadHeap* ins);
     void visitAsmJSStoreHeap(MAsmJSStoreHeap* ins);
-    void visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap* ins);
-    void visitAsmJSAtomicExchangeHeap(MAsmJSAtomicExchangeHeap* ins);
-    void visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap* ins);
+    void visitWasmCompareExchangeHeap(MWasmCompareExchangeHeap* ins);
+    void visitWasmAtomicExchangeHeap(MWasmAtomicExchangeHeap* ins);
+    void visitWasmAtomicBinopHeap(MWasmAtomicBinopHeap* ins);
     void visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins);
     void visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement* ins);
     void visitAtomicExchangeTypedArrayElement(MAtomicExchangeTypedArrayElement* ins);
     void visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins);
     void visitSubstr(MSubstr* ins);
     void visitRandom(MRandom* ins);
     void visitWasmTruncateToInt64(MWasmTruncateToInt64* ins);
     void visitWasmLoad(MWasmLoad* ins);
--- a/js/src/jit/arm64/MacroAssembler-arm64.h
+++ b/js/src/jit/arm64/MacroAssembler-arm64.h
@@ -1901,16 +1901,23 @@ class MacroAssemblerCompat : public vixl
         MOZ_CRASH("atomicEffectOp");
     }
 
     template <typename T>
     void atomicEffectOp(int nbytes, AtomicOp op, const Imm32& value, const T& mem) {
         MOZ_CRASH("atomicEffectOp");
     }
 
+    template <typename T>
+    void atomicFetchOp64(AtomicOp op, Register64 value, const T& mem, Register64 temp,
+                         Register64 output)
+    {
+        MOZ_CRASH("AtomicFetchOp64");
+    }
+
   public:
     // T in {Address,BaseIndex}
     // S in {Imm32,Register}
 
     template <typename T>
     void compareExchange8SignExtend(const T& mem, Register oldval, Register newval, Register output)
     {
         compareExchange(1, true, mem, oldval, newval, output);
@@ -2122,16 +2129,53 @@ class MacroAssemblerCompat : public vixl
     void atomicXor16(const S& value, const T& mem) {
         atomicEffectOp(2, AtomicFetchXorOp, value, mem);
     }
     template <typename T, typename S>
     void atomicXor32(const S& value, const T& mem) {
         atomicEffectOp(4, AtomicFetchXorOp, value, mem);
     }
 
+    template <typename T>
+    void atomicFetchAdd64(Register64 value, const T& mem, Register64 temp, Register64 output) {
+        atomicFetchOp64(AtomicFetchAddOp, value, mem, temp, output);
+    }
+
+    template <typename T>
+    void atomicFetchSub64(Register64 value, const T& mem, Register64 temp, Register64 output) {
+        atomicFetchOp64(AtomicFetchSubOp, value, mem, temp, output);
+    }
+
+    template <typename T>
+    void atomicFetchAnd64(Register64 value, const T& mem, Register64 temp, Register64 output) {
+        atomicFetchOp64(AtomicFetchAndOp, value, mem, temp, output);
+    }
+
+    template <typename T>
+    void atomicFetchOr64(Register64 value, const T& mem, Register64 temp, Register64 output) {
+        atomicFetchOp64(AtomicFetchOrOp, value, mem, temp, output);
+    }
+
+    template <typename T>
+    void atomicFetchXor64(Register64 value, const T& mem, Register64 temp, Register64 output) {
+        atomicFetchOp64(AtomicFetchXorOp, value, mem, temp, output);
+    }
+
+    template <typename T>
+    void atomicExchange64(const T& mem, Register64 src, Register64 output) {
+        MOZ_CRASH("atomicExchange64");
+    }
+
+    template <typename T>
+    void compareExchange64(const T& mem, Register64 expected, Register64 replacement,
+                           Register64 output)
+    {
+        MOZ_CRASH("compareExchange64");
+    }
+
     template<typename T>
     void compareExchangeToTypedIntArray(Scalar::Type arrayType, const T& mem, Register oldval, Register newval,
                                         Register temp, AnyRegister output);
 
     template<typename T>
     void atomicExchangeToTypedIntArray(Scalar::Type arrayType, const T& mem, Register value,
                                        Register temp, AnyRegister output);
 
--- a/js/src/jit/arm64/Trampoline-arm64.cpp
+++ b/js/src/jit/arm64/Trampoline-arm64.cpp
@@ -709,35 +709,55 @@ JitRuntime::generateVMWrapper(JSContext*
     return functionWrappers_->putNew(&f, wrapperOffset);
 }
 
 uint32_t
 JitRuntime::generatePreBarrier(JSContext* cx, MacroAssembler& masm, MIRType type)
 {
     uint32_t offset = startTrampolineCode(masm);
 
+    MOZ_ASSERT(PreBarrierReg == r1);
+    Register temp1 = r2;
+    Register temp2 = r3;
+    Register temp3 = r4;
+    masm.push(temp1);
+    masm.push(temp2);
+    masm.push(temp3);
+
+    Label noBarrier;
+    masm.emitPreBarrierFastPath(cx->runtime(), type, temp1, temp2, temp3, &noBarrier);
+
+    // Call into C++ to mark this GC thing.
+    masm.pop(temp3);
+    masm.pop(temp2);
+    masm.pop(temp1);
+
     LiveRegisterSet regs = LiveRegisterSet(GeneralRegisterSet(Registers::VolatileMask),
                                            FloatRegisterSet(FloatRegisters::VolatileMask));
 
     // Also preserve the return address.
     regs.add(lr);
 
     masm.PushRegsInMask(regs);
 
-    MOZ_ASSERT(PreBarrierReg == r1);
     masm.movePtr(ImmPtr(cx->runtime()), r3);
 
     masm.setupUnalignedABICall(r0);
     masm.passABIArg(r3);
     masm.passABIArg(PreBarrierReg);
-    masm.callWithABI(IonMarkFunction(type));
+    masm.callWithABI(JitMarkFunction(type));
 
     // Pop the volatile regs and restore LR.
     masm.PopRegsInMask(regs);
+    masm.abiret();
 
+    masm.bind(&noBarrier);
+    masm.pop(temp3);
+    masm.pop(temp2);
+    masm.pop(temp1);
     masm.abiret();
 
     return offset;
 }
 
 typedef bool (*HandleDebugTrapFn)(JSContext*, BaselineFrame*, uint8_t*, bool*);
 static const VMFunction HandleDebugTrapInfo =
     FunctionInfo<HandleDebugTrapFn>(HandleDebugTrap, "HandleDebugTrap");
--- a/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp
+++ b/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp
@@ -2204,19 +2204,19 @@ CodeGeneratorMIPSShared::visitAsmJSStore
         masm.ma_store(ToRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne),
                       static_cast<LoadStoreSize>(size), isSigned ? SignExtend : ZeroExtend);
     }
 
     masm.bind(&outOfRange);
 }
 
 void
-CodeGeneratorMIPSShared::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins)
+CodeGeneratorMIPSShared::visitWasmCompareExchangeHeap(LWasmCompareExchangeHeap* ins)
 {
-    MAsmJSCompareExchangeHeap* mir = ins->mir();
+    MWasmCompareExchangeHeap* mir = ins->mir();
     Scalar::Type vt = mir->access().type();
     const LAllocation* ptr = ins->ptr();
     Register ptrReg = ToRegister(ptr);
     BaseIndex srcAddr(HeapReg, ptrReg, TimesOne);
     MOZ_ASSERT(ins->addrTemp()->isBogusTemp());
 
     Register oldval = ToRegister(ins->oldValue());
     Register newval = ToRegister(ins->newValue());
@@ -2226,41 +2226,41 @@ CodeGeneratorMIPSShared::visitAsmJSCompa
 
     masm.compareExchangeToTypedIntArray(vt == Scalar::Uint32 ? Scalar::Int32 : vt,
                                         srcAddr, oldval, newval, InvalidReg,
                                         valueTemp, offsetTemp, maskTemp,
                                         ToAnyRegister(ins->output()));
 }
 
 void
-CodeGeneratorMIPSShared::visitAsmJSAtomicExchangeHeap(LAsmJSAtomicExchangeHeap* ins)
+CodeGeneratorMIPSShared::visitWasmAtomicExchangeHeap(LWasmAtomicExchangeHeap* ins)
 {
-    MAsmJSAtomicExchangeHeap* mir = ins->mir();
+    MWasmAtomicExchangeHeap* mir = ins->mir();
     Scalar::Type vt = mir->access().type();
     Register ptrReg = ToRegister(ins->ptr());
     Register value = ToRegister(ins->value());
     BaseIndex srcAddr(HeapReg, ptrReg, TimesOne);
     MOZ_ASSERT(ins->addrTemp()->isBogusTemp());
 
     Register valueTemp = ToRegister(ins->valueTemp());
     Register offsetTemp = ToRegister(ins->offsetTemp());
     Register maskTemp = ToRegister(ins->maskTemp());
 
     masm.atomicExchangeToTypedIntArray(vt == Scalar::Uint32 ? Scalar::Int32 : vt,
                                        srcAddr, value, InvalidReg, valueTemp,
                                        offsetTemp, maskTemp, ToAnyRegister(ins->output()));
 }
 
 void
-CodeGeneratorMIPSShared::visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap* ins)
+CodeGeneratorMIPSShared::visitWasmAtomicBinopHeap(LWasmAtomicBinopHeap* ins)
 {
     MOZ_ASSERT(ins->mir()->hasUses());
     MOZ_ASSERT(ins->addrTemp()->isBogusTemp());
 
-    MAsmJSAtomicBinopHeap* mir = ins->mir();
+    MWasmAtomicBinopHeap* mir = ins->mir();
     Scalar::Type vt = mir->access().type();
     Register ptrReg = ToRegister(ins->ptr());
     Register flagTemp = ToRegister(ins->flagTemp());
     Register valueTemp = ToRegister(ins->valueTemp());
     Register offsetTemp = ToRegister(ins->offsetTemp());
     Register maskTemp = ToRegister(ins->maskTemp());
     const LAllocation* value = ins->value();
     AtomicOp op = mir->operation();
@@ -2275,22 +2275,22 @@ CodeGeneratorMIPSShared::visitAsmJSAtomi
     else
         atomicBinopToTypedIntArray(op, vt == Scalar::Uint32 ? Scalar::Int32 : vt,
                                    ToRegister(value), srcAddr, flagTemp, InvalidReg,
                                    valueTemp, offsetTemp, maskTemp,
                                    ToAnyRegister(ins->output()));
 }
 
 void
-CodeGeneratorMIPSShared::visitAsmJSAtomicBinopHeapForEffect(LAsmJSAtomicBinopHeapForEffect* ins)
+CodeGeneratorMIPSShared::visitWasmAtomicBinopHeapForEffect(LWasmAtomicBinopHeapForEffect* ins)
 {
     MOZ_ASSERT(!ins->mir()->hasUses());
     MOZ_ASSERT(ins->addrTemp()->isBogusTemp());
 
-    MAsmJSAtomicBinopHeap* mir = ins->mir();
+    MWasmAtomicBinopHeap* mir = ins->mir();
     Scalar::Type vt = mir->access().type();
     Register ptrReg = ToRegister(ins->ptr());
     Register flagTemp = ToRegister(ins->flagTemp());
     Register valueTemp = ToRegister(ins->valueTemp());
     Register offsetTemp = ToRegister(ins->offsetTemp());
     Register maskTemp = ToRegister(ins->maskTemp());
     const LAllocation* value = ins->value();
     AtomicOp op = mir->operation();
--- a/js/src/jit/mips-shared/CodeGenerator-mips-shared.h
+++ b/js/src/jit/mips-shared/CodeGenerator-mips-shared.h
@@ -215,20 +215,20 @@ class CodeGeneratorMIPSShared : public C
     void visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic* ins);
     void visitWasmLoad(LWasmLoad* ins);
     void visitWasmUnalignedLoad(LWasmUnalignedLoad* ins);
     void visitWasmStore(LWasmStore* ins);
     void visitWasmUnalignedStore(LWasmUnalignedStore* ins);
     void visitWasmAddOffset(LWasmAddOffset* ins);
     void visitAsmJSLoadHeap(LAsmJSLoadHeap* ins);
     void visitAsmJSStoreHeap(LAsmJSStoreHeap* ins);
-    void visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins);
-    void visitAsmJSAtomicExchangeHeap(LAsmJSAtomicExchangeHeap* ins);
-    void visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap* ins);
-    void visitAsmJSAtomicBinopHeapForEffect(LAsmJSAtomicBinopHeapForEffect* ins);
+    void visitWasmCompareExchangeHeap(LWasmCompareExchangeHeap* ins);
+    void visitWasmAtomicExchangeHeap(LWasmAtomicExchangeHeap* ins);
+    void visitWasmAtomicBinopHeap(LWasmAtomicBinopHeap* ins);
+    void visitWasmAtomicBinopHeapForEffect(LWasmAtomicBinopHeapForEffect* ins);
 
     void visitWasmStackArg(LWasmStackArg* ins);
     void visitWasmStackArgI64(LWasmStackArgI64* ins);
     void visitWasmSelect(LWasmSelect* ins);
     void visitWasmReinterpret(LWasmReinterpret* ins);
 
     void visitMemoryBarrier(LMemoryBarrier* ins);
     void visitAtomicTypedArrayElementBinop(LAtomicTypedArrayElementBinop* lir);
--- a/js/src/jit/mips-shared/Lowering-mips-shared.cpp
+++ b/js/src/jit/mips-shared/Lowering-mips-shared.cpp
@@ -615,85 +615,85 @@ LIRGeneratorMIPSShared::visitAtomicExcha
         new(alloc()) LAtomicExchangeTypedArrayElement(elements, index, value, uint32Temp,
                                                       /* valueTemp= */ temp(), /* offsetTemp= */ temp(),
                                                       /* maskTemp= */ temp());
 
     define(lir, ins);
 }
 
 void
-LIRGeneratorMIPSShared::visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap* ins)
+LIRGeneratorMIPSShared::visitWasmCompareExchangeHeap(MWasmCompareExchangeHeap* ins)
 {
     MOZ_ASSERT(ins->access().type() < Scalar::Float32);
     MOZ_ASSERT(ins->access().offset() == 0);
 
     MDefinition* base = ins->base();
     MOZ_ASSERT(base->type() == MIRType::Int32);
 
-    LAsmJSCompareExchangeHeap* lir =
-        new(alloc()) LAsmJSCompareExchangeHeap(useRegister(base),
-                                               useRegister(ins->oldValue()),
-                                               useRegister(ins->newValue()),
-                                               /* valueTemp= */ temp(),
-                                               /* offsetTemp= */ temp(),
-                                               /* maskTemp= */ temp());
+    LWasmCompareExchangeHeap* lir =
+        new(alloc()) LWasmCompareExchangeHeap(useRegister(base),
+                                              useRegister(ins->oldValue()),
+                                              useRegister(ins->newValue()),
+                                              /* valueTemp= */ temp(),
+                                              /* offsetTemp= */ temp(),
+                                              /* maskTemp= */ temp());
 
     define(lir, ins);
 }
 
 void
-LIRGeneratorMIPSShared::visitAsmJSAtomicExchangeHeap(MAsmJSAtomicExchangeHeap* ins)
+LIRGeneratorMIPSShared::visitWasmAtomicExchangeHeap(MWasmAtomicExchangeHeap* ins)
 {
     MOZ_ASSERT(ins->base()->type() == MIRType::Int32);
     MOZ_ASSERT(ins->access().offset() == 0);
 
     const LAllocation base = useRegister(ins->base());
     const LAllocation value = useRegister(ins->value());
 
     // The output may not be used but will be clobbered regardless,
     // so ignore the case where we're not using the value and just
     // use the output register as a temp.
 
-    LAsmJSAtomicExchangeHeap* lir =
-        new(alloc()) LAsmJSAtomicExchangeHeap(base, value,
-                                              /* valueTemp= */ temp(),
-                                              /* offsetTemp= */ temp(),
-                                              /* maskTemp= */ temp());
+    LWasmAtomicExchangeHeap* lir =
+        new(alloc()) LWasmAtomicExchangeHeap(base, value,
+                                             /* valueTemp= */ temp(),
+                                             /* offsetTemp= */ temp(),
+                                             /* maskTemp= */ temp());
     define(lir, ins);
 }
 
 void
-LIRGeneratorMIPSShared::visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap* ins)
+LIRGeneratorMIPSShared::visitWasmAtomicBinopHeap(MWasmAtomicBinopHeap* ins)
 {
     MOZ_ASSERT(ins->access().type() < Scalar::Float32);
     MOZ_ASSERT(ins->access().offset() == 0);
 
     MDefinition* base = ins->base();
     MOZ_ASSERT(base->type() == MIRType::Int32);
 
     if (!ins->hasUses()) {
-        LAsmJSAtomicBinopHeapForEffect* lir =
-            new(alloc()) LAsmJSAtomicBinopHeapForEffect(useRegister(base),
-                                                        useRegister(ins->value()),
-                                                        /* flagTemp= */ temp(),
-                                                        /* valueTemp= */ temp(),
-                                                        /* offsetTemp= */ temp(),
-                                                        /* maskTemp= */ temp());
+        LWasmAtomicBinopHeapForEffect* lir =
+            new(alloc()) LWasmAtomicBinopHeapForEffect(useRegister(base),
+                                                       useRegister(ins->value()),
+                                                       /* flagTemp= */ temp(),
+                                                       /* valueTemp= */ temp(),
+                                                       /* offsetTemp= */ temp(),
+                                                       /* maskTemp= */ temp());
         add(lir, ins);
         return;
     }
 
-    LAsmJSAtomicBinopHeap* lir =
-        new(alloc()) LAsmJSAtomicBinopHeap(useRegister(base),
-                                           useRegister(ins->value()),
-                                           /* temp= */ LDefinition::BogusTemp(),
-                                           /* flagTemp= */ temp(),
-                                           /* valueTemp= */ temp(),
-                                           /* offsetTemp= */ temp(),
-                                           /* maskTemp= */ temp());
+    LWasmAtomicBinopHeap* lir =
+        new(alloc()) LWasmAtomicBinopHeap(useRegister(base),
+                                          useRegister(ins->value()),
+                                          /* temp= */ LDefinition::BogusTemp(),
+                                          /* flagTemp= */ temp(),
+                                          /* valueTemp= */ temp(),
+                                          /* offsetTemp= */ temp(),
+                                          /* maskTemp= */ temp());
 
     define(lir, ins);
 }
 
 void
 LIRGeneratorMIPSShared::visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins)
 {
     MOZ_ASSERT(ins->arrayType() != Scalar::Uint8Clamped);
--- a/js/src/jit/mips-shared/Lowering-mips-shared.h
+++ b/js/src/jit/mips-shared/Lowering-mips-shared.h
@@ -82,19 +82,19 @@ class LIRGeneratorMIPSShared : public LI
   public:
     void lowerPhi(MPhi* phi);
     void visitGuardShape(MGuardShape* ins);
     void visitGuardObjectGroup(MGuardObjectGroup* ins);
     void visitWasmUnsignedToDouble(MWasmUnsignedToDouble* ins);
     void visitWasmUnsignedToFloat32(MWasmUnsignedToFloat32* ins);
     void visitAsmJSLoadHeap(MAsmJSLoadHeap* ins);
     void visitAsmJSStoreHeap(MAsmJSStoreHeap* ins);
-    void visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap* ins);
-    void visitAsmJSAtomicExchangeHeap(MAsmJSAtomicExchangeHeap* ins);
-    void visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap* ins);
+    void visitWasmCompareExchangeHeap(MWasmCompareExchangeHeap* ins);
+    void visitWasmAtomicExchangeHeap(MWasmAtomicExchangeHeap* ins);
+    void visitWasmAtomicBinopHeap(MWasmAtomicBinopHeap* ins);
     void visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins);
     void visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement* ins);
     void visitAtomicExchangeTypedArrayElement(MAtomicExchangeTypedArrayElement* ins);
     void visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins);
     void visitSubstr(MSubstr* ins);
     void visitCopySign(MCopySign* ins);
     void visitExtendInt32ToInt64(MExtendInt32ToInt64* ins);
     void visitSignExtendInt64(MSignExtendInt64* ins);
--- a/js/src/jit/mips32/MacroAssembler-mips32.h
+++ b/js/src/jit/mips32/MacroAssembler-mips32.h
@@ -305,16 +305,24 @@ class MacroAssemblerMIPSCompat : public 
     void jump(JitCode* code) {
         branch(code);
     }
 
     void jump(wasm::TrapDesc target) {
         ma_b(target);
     }
 
+    void jump(TrampolinePtr code)
+    {
+        auto target = ImmPtr(code.value);
+        BufferOffset bo = m_buffer.nextOffset();
+        addPendingJump(bo, target, Relocation::HARDCODED);
+        ma_jump(target);
+    }
+
     void negl(Register reg) {
         ma_negu(reg, reg);
     }
 
     // Returns the register containing the type tag.
     Register splitTagForTest(const ValueOperand& value) {
         return value.typeReg();
     }
--- a/js/src/jit/mips32/Trampoline-mips32.cpp
+++ b/js/src/jit/mips32/Trampoline-mips32.cpp
@@ -688,18 +688,19 @@ JitRuntime::generateVMWrapper(JSContext*
 
     // Save the base of the argument set stored on the stack.
     Register argsBase = InvalidReg;
     if (f.explicitArgs) {
         argsBase = t1; // Use temporary register.
         regs.take(argsBase);
         masm.ma_addu(argsBase, StackPointer, Imm32(ExitFrameLayout::SizeWithFooter()));
     }
-
+    uint32_t framePushedBeforeAlignStack = masm.framePushed();
     masm.alignStackPointer();
+    masm.setFramePushed(0);
 
     // Reserve space for the outparameter. Reserve sizeof(Value) for every
     // case so that stack stays aligned.
     uint32_t outParamSize = 0;
     switch (f.outParam) {
       case Type_Value:
         outParamSize = sizeof(Value);
         masm.reserveStack(outParamSize);
@@ -848,53 +849,75 @@ JitRuntime::generateVMWrapper(JSContext*
         break;
 
       default:
         MOZ_ASSERT(f.outParam == Type_Void);
         break;
     }
 
     masm.restoreStackPointer();
+    masm.setFramePushed(framePushedBeforeAlignStack);
 
     masm.leaveExitFrame();
     masm.retn(Imm32(sizeof(ExitFrameLayout) +
                     f.explicitStackSlots() * sizeof(uintptr_t) +
                     f.extraValuesToPop * sizeof(Value)));
 
     return functionWrappers_->putNew(&f, wrapperOffset);
 }
 
 uint32_t
 JitRuntime::generatePreBarrier(JSContext* cx, MacroAssembler& masm, MIRType type)
 {
     uint32_t offset = startTrampolineCode(masm);
 
+    MOZ_ASSERT(PreBarrierReg == a1);
+    Register temp1 = a0;
+    Register temp2 = a2;
+    Register temp3 = a3;
+    masm.push(temp1);
+    masm.push(temp2);
+    masm.push(temp3);
+
+    Label noBarrier;
+    masm.emitPreBarrierFastPath(cx->runtime(), type, temp1, temp2, temp3, &noBarrier);
+
+    // Call into C++ to mark this GC thing.
+    masm.pop(temp3);
+    masm.pop(temp2);
+    masm.pop(temp1);
+
     LiveRegisterSet save;
     if (cx->runtime()->jitSupportsFloatingPoint) {
         save.set() = RegisterSet(GeneralRegisterSet(Registers::VolatileMask),
                            FloatRegisterSet(FloatRegisters::VolatileMask));
     } else {
         save.set() = RegisterSet(GeneralRegisterSet(Registers::VolatileMask),
                            FloatRegisterSet());
     }
     save.add(ra);
     masm.PushRegsInMask(save);
 
-    MOZ_ASSERT(PreBarrierReg == a1);
     masm.movePtr(ImmPtr(cx->runtime()), a0);
 
     masm.setupUnalignedABICall(a2);
     masm.passABIArg(a0);
     masm.passABIArg(a1);
-    masm.callWithABI(IonMarkFunction(type));
+    masm.callWithABI(JitMarkFunction(type));
 
     save.take(AnyRegister(ra));
     masm.PopRegsInMask(save);
     masm.ret();
 
+    masm.bind(&noBarrier);
+    masm.pop(temp3);
+    masm.pop(temp2);
+    masm.pop(temp1);
+    masm.abiret();
+
     return offset;
 }
 
 typedef bool (*HandleDebugTrapFn)(JSContext*, BaselineFrame*, uint8_t*, bool*);
 static const VMFunction HandleDebugTrapInfo =
     FunctionInfo<HandleDebugTrapFn>(HandleDebugTrap, "HandleDebugTrap");
 
 JitCode*
--- a/js/src/jit/mips64/MacroAssembler-mips64.h
+++ b/js/src/jit/mips64/MacroAssembler-mips64.h
@@ -334,16 +334,24 @@ class MacroAssemblerMIPS64Compat : publi
     void jump(JitCode* code) {
         branch(code);
     }
 
     void jump(wasm::TrapDesc target) {
         ma_b(target);
     }
 
+    void jump(TrampolinePtr code)
+    {
+        auto target = ImmPtr(code.value);
+        BufferOffset bo = m_buffer.nextOffset();
+        addPendingJump(bo, target, Relocation::HARDCODED);
+        ma_jump(target);
+    }
+
     void splitTag(Register src, Register dest) {
         ma_dsrl(dest, src, Imm32(JSVAL_TAG_SHIFT));
     }
 
     void splitTag(const ValueOperand& operand, Register dest) {
         splitTag(operand.valueReg(), dest);
     }
 
--- a/js/src/jit/mips64/Trampoline-mips64.cpp
+++ b/js/src/jit/mips64/Trampoline-mips64.cpp
@@ -818,39 +818,60 @@ JitRuntime::generateVMWrapper(JSContext*
     return functionWrappers_->putNew(&f, wrapperOffset);
 }
 
 uint32_t
 JitRuntime::generatePreBarrier(JSContext* cx, MacroAssembler& masm, MIRType type)
 {
     uint32_t offset = startTrampolineCode(masm);
 
+    MOZ_ASSERT(PreBarrierReg == a1);
+    Register temp1 = a0;
+    Register temp2 = a2;
+    Register temp3 = a3;
+    masm.push(temp1);
+    masm.push(temp2);
+    masm.push(temp3);
+
+    Label noBarrier;
+    masm.emitPreBarrierFastPath(cx->runtime(), type, temp1, temp2, temp3, &noBarrier);
+
+    // Call into C++ to mark this GC thing.
+    masm.pop(temp3);
+    masm.pop(temp2);
+    masm.pop(temp1);
+
     LiveRegisterSet save;
     if (cx->runtime()->jitSupportsFloatingPoint) {
         save.set() = RegisterSet(GeneralRegisterSet(Registers::VolatileMask),
                                  FloatRegisterSet(FloatRegisters::VolatileMask));
     } else {
         save.set() = RegisterSet(GeneralRegisterSet(Registers::VolatileMask),
                                  FloatRegisterSet());
     }
     save.add(ra);
     masm.PushRegsInMask(save);
 
-    MOZ_ASSERT(PreBarrierReg == a1);
     masm.movePtr(ImmPtr(cx->runtime()), a0);
 
     masm.setupUnalignedABICall(a2);
     masm.passABIArg(a0);
     masm.passABIArg(a1);
-    masm.callWithABI(IonMarkFunction(type));
+    masm.callWithABI(JitMarkFunction(type));
 
     save.take(AnyRegister(ra));
     masm.PopRegsInMask(save);
     masm.ret();
 
+    masm.bind(&noBarrier);
+    masm.pop(temp3);
+    masm.pop(temp2);
+    masm.pop(temp1);
+    masm.abiret();
+
     return offset;
 }
 
 typedef bool (*HandleDebugTrapFn)(JSContext*, BaselineFrame*, uint8_t*, bool*);
 static const VMFunction HandleDebugTrapInfo =
     FunctionInfo<HandleDebugTrapFn>(HandleDebugTrap, "HandleDebugTrap");
 
 JitCode*
--- a/js/src/jit/none/Lowering-none.h
+++ b/js/src/jit/none/Lowering-none.h
@@ -80,19 +80,19 @@ class LIRGeneratorNone : public LIRGener
     void visitWasmUnsignedToDouble(MWasmUnsignedToDouble* ins) { MOZ_CRASH(); }
     void visitWasmUnsignedToFloat32(MWasmUnsignedToFloat32* ins) { MOZ_CRASH(); }
     void visitAsmJSLoadHeap(MAsmJSLoadHeap* ins) { MOZ_CRASH(); }
     void visitAsmJSStoreHeap(MAsmJSStoreHeap* ins) { MOZ_CRASH(); }
     void visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins) { MOZ_CRASH(); }
     void visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins) { MOZ_CRASH(); }
     void visitCompareExchan