merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 15 Sep 2016 11:59:50 +0200
changeset 313980 29af101880db7ce7f5f87f58e1ff20988c1c5fc3
parent 313920 dd3bc99fafd46295c50a87226c242cc60787b3b8 (current diff)
parent 313979 a387818ed08a57cc9d292c19dbd0a66f37ee94c3 (diff)
child 314002 667d8adc0bc3e70f39732b6651f289f36d77873c
child 314049 841aca60dc7363ae0fa5bd24d7fdf98097be2659
child 314127 814b84f92b6033a7e66da871aaccfee18b55cc42
push id30703
push usercbook@mozilla.com
push dateThu, 15 Sep 2016 10:00:06 +0000
treeherdermozilla-central@29af101880db [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone51.0a1
first release with
nightly linux32
29af101880db / 51.0a1 / 20160915030417 / files
nightly linux64
29af101880db / 51.0a1 / 20160915030417 / files
nightly mac
29af101880db / 51.0a1 / 20160915030417 / files
nightly win32
29af101880db / 51.0a1 / 20160915030417 / files
nightly win64
29af101880db / 51.0a1 / 20160915030417 / 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 mozilla-inbound to mozilla-central a=merge
js/xpconnect/src/XPCJSRuntime.cpp
media/libstagefright/binding/byteorder/Cargo.toml
media/libstagefright/binding/byteorder/src/lib.rs
media/libstagefright/binding/byteorder/src/new.rs
media/libstagefright/binding/mp4parse/Cargo.toml
mobile/android/base/java/org/mozilla/gecko/db/StubBrowserDB.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/ZoomConstraints.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/restrictions/DefaultConfiguration.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/restrictions/GuestProfileConfiguration.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/restrictions/Restrictable.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/restrictions/RestrictedProfileConfiguration.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/restrictions/RestrictionCache.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/restrictions/RestrictionConfiguration.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/restrictions/RestrictionProvider.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/restrictions/Restrictions.java
python/mozbuild/mozbuild/test/frontend/data/crate-build-dependency-absolute-path/Cargo.toml
python/mozbuild/mozbuild/test/frontend/data/crate-build-dependency-absolute-path/moz.build
python/mozbuild/mozbuild/test/frontend/data/crate-build-dependency-nonexistent-path/Cargo.toml
python/mozbuild/mozbuild/test/frontend/data/crate-build-dependency-nonexistent-path/moz.build
python/mozbuild/mozbuild/test/frontend/data/crate-build-dependency-recursive/Cargo.toml
python/mozbuild/mozbuild/test/frontend/data/crate-build-dependency-recursive/moz.build
python/mozbuild/mozbuild/test/frontend/data/crate-build-dependency-recursive/ok/Cargo.toml
python/mozbuild/mozbuild/test/frontend/data/crate-build-dependency-version-only/Cargo.toml
python/mozbuild/mozbuild/test/frontend/data/crate-build-dependency-version-only/moz.build
python/mozbuild/mozbuild/test/frontend/data/crate-build-dependency-with-dict/Cargo.toml
python/mozbuild/mozbuild/test/frontend/data/crate-build-dependency-with-dict/moz.build
python/mozbuild/mozbuild/test/frontend/data/crate-dependency-absolute-path/Cargo.toml
python/mozbuild/mozbuild/test/frontend/data/crate-dependency-absolute-path/moz.build
python/mozbuild/mozbuild/test/frontend/data/crate-dependency-nonexistent-path/Cargo.toml
python/mozbuild/mozbuild/test/frontend/data/crate-dependency-nonexistent-path/moz.build
python/mozbuild/mozbuild/test/frontend/data/crate-dependency-recursive/Cargo.toml
python/mozbuild/mozbuild/test/frontend/data/crate-dependency-recursive/moz.build
python/mozbuild/mozbuild/test/frontend/data/crate-dependency-recursive/ok/Cargo.toml
python/mozbuild/mozbuild/test/frontend/data/crate-dependency-version-only/Cargo.toml
python/mozbuild/mozbuild/test/frontend/data/crate-dependency-version-only/moz.build
python/mozbuild/mozbuild/test/frontend/data/crate-dependency-with-dict/Cargo.toml
python/mozbuild/mozbuild/test/frontend/data/crate-dependency-with-dict/moz.build
python/mozbuild/mozbuild/test/frontend/data/duplicated-crate-names/Cargo.toml
python/mozbuild/mozbuild/test/frontend/data/duplicated-crate-names/crate-one/Cargo.toml
python/mozbuild/mozbuild/test/frontend/data/duplicated-crate-names/crate-one/duplicate/Cargo.toml
python/mozbuild/mozbuild/test/frontend/data/duplicated-crate-names/crate-two/Cargo.toml
python/mozbuild/mozbuild/test/frontend/data/duplicated-crate-names/crate-two/duplicate/Cargo.toml
python/mozbuild/mozbuild/test/frontend/data/duplicated-crate-names/moz.build
taskcluster/ci/legacy/tasks/builds/android_l10n_api_15.yml
taskcluster/ci/legacy/tasks/builds/android_l10n_base.yml
toolkit/library/rust/Cargo.lock
xpcom/base/CycleCollectedJSRuntime.cpp
xpcom/base/CycleCollectedJSRuntime.h
new file mode 100644
--- /dev/null
+++ b/.cargo/config.in
@@ -0,0 +1,6 @@
+[source.crates-io]
+registry = 'https://github.com/rust-lang/crates.io-index'
+replace-with = 'vendored-sources'
+
+[source.vendored-sources]
+directory = '@top_srcdir@/third_party/rust'
--- a/accessible/base/EventTree.cpp
+++ b/accessible/base/EventTree.cpp
@@ -440,19 +440,26 @@ EventTree::Log(uint32_t aLevel) const
 
 void
 EventTree::Mutated(AccMutationEvent* aEv)
 {
   // If shown or hidden node is a root of previously mutated subtree, then
   // discard those subtree mutations as we are no longer interested in them.
   UniquePtr<EventTree>* node = &mFirst;
   while (*node) {
-    if ((*node)->mContainer == aEv->mAccessible) {
-      *node = Move((*node)->mNext);
-      break;
+    Accessible* cntr = (*node)->mContainer;
+    while (cntr != mContainer) {
+      if (cntr == aEv->mAccessible) {
+        *node = Move((*node)->mNext);
+        break;
+      }
+      cntr = cntr->Parent();
+    }
+    if (cntr == aEv->mAccessible) {
+      continue;
     }
     node = &(*node)->mNext;
   }
 
   AccMutationEvent* prevEvent = mDependentEvents.SafeLastElement(nullptr);
   mDependentEvents.AppendElement(aEv);
 
   // Coalesce text change events from this hide/show event and the previous one.
--- a/accessible/tests/mochitest/events.js
+++ b/accessible/tests/mochitest/events.js
@@ -332,35 +332,35 @@ function eventQueue(aEventType)
                   this.mScenarios[matchIdx].length > 0) {
                 ok(false,
                    "We have a matched scenario at index " + matchIdx + " already.");
               }
 
               if (matchIdx == -1 || eventSeq.length > 0)
                 matchIdx = scnIdx;
 
-              // Report everythign is ok.
+              // Report everything is ok.
               for (var idx = 0; idx < eventSeq.length; idx++) {
                 var checker = eventSeq[idx];
 
                 var typeStr = eventQueue.getEventTypeAsString(checker);
                 var msg = "Test with ID = '" + this.getEventID(checker) +
                   "' succeed. ";
 
                 if (checker.unexpected) {
+                  ok(true, msg + `There's no unexpected '${typeStr}' event.`);
+                }
+                else {
                   if (checker.todo) {
-                    todo(false, "Event " + typeStr + " event is still missing");
+                    todo(false, `Todo event '${typeStr}' was caught`);
                   }
                   else {
-                    ok(true, msg + "There's no unexpected " + typeStr + " event.");
+                    ok(true, `${msg} Event '${typeStr}' was handled.`);
                   }
                 }
-                else {
-                  ok(true, msg + "Event " + typeStr + " was handled.");
-                }
               }
             }
           }
         }
 
         // We don't have completely matched scenario. Report each failure/success
         // for every scenario.
         if (matchIdx == -1) {
@@ -373,25 +373,23 @@ function eventQueue(aEventType)
               var typeStr = eventQueue.getEventTypeAsString(checker);
               var msg = "Scenario #" + scnIdx + " of test with ID = '" +
                 this.getEventID(checker) + "' failed. ";
 
               if (checker.wasCaught > 1)
                 ok(false, msg + "Dupe " + typeStr + " event.");
 
               if (checker.unexpected) {
-                if (checker.todo) {
-                  todo(checker.wasCaught,
-                       "Event " + typeStr + " event is still missing");
-                }
-                else if (checker.wasCaught) {
+                if (checker.wasCaught) {
                   ok(false, msg + "There's unexpected " + typeStr + " event.");
                 }
-              } else if (!checker.wasCaught) {
-                ok(false, msg + typeStr + " event was missed.");
+              }
+              else if (!checker.wasCaught) {
+                var rf = checker.todo ? todo : ok;
+                rf(false, `${msg} '${typeStr} event is missed.`);
               }
             }
           }
         }
       }
     }
 
     this.clearEventHandler();
@@ -591,33 +589,35 @@ function eventQueue(aEventType)
   this.getNextExpectedEvent =
     function eventQueue_getNextExpectedEvent(aEventSeq)
   {
     if (!("idx" in aEventSeq))
       aEventSeq.idx = 0;
 
     while (aEventSeq.idx < aEventSeq.length &&
            (aEventSeq[aEventSeq.idx].unexpected ||
+            aEventSeq[aEventSeq.idx].todo ||
             aEventSeq[aEventSeq.idx].async ||
             aEventSeq[aEventSeq.idx].wasCaught > 0)) {
       aEventSeq.idx++;
     }
 
     return aEventSeq.idx != aEventSeq.length ? aEventSeq[aEventSeq.idx] : null;
   }
 
   this.areExpectedEventsLeft =
     function eventQueue_areExpectedEventsLeft(aScenario)
   {
     function scenarioHasUnhandledExpectedEvent(aEventSeq)
     {
       // Check if we have unhandled async (can be anywhere in the sequance) or
       // sync expcected events yet.
       for (var idx = 0; idx < aEventSeq.length; idx++) {
-        if (!aEventSeq[idx].unexpected && !aEventSeq[idx].wasCaught)
+        if (!aEventSeq[idx].unexpected && !aEventSeq[idx].todo &&
+            !aEventSeq[idx].wasCaught)
           return true;
       }
 
       return false;
     }
 
     if (aScenario)
       return scenarioHasUnhandledExpectedEvent(aScenario);
@@ -631,29 +631,29 @@ function eventQueue(aEventType)
   }
 
   this.areAllEventsExpected =
     function eventQueue_areAllEventsExpected()
   {
     for (var scnIdx = 0; scnIdx < this.mScenarios.length; scnIdx++) {
       var eventSeq = this.mScenarios[scnIdx];
       for (var idx = 0; idx < eventSeq.length; idx++) {
-        if (eventSeq[idx].unexpected)
+        if (eventSeq[idx].unexpected || eventSeq[idx].todo)
           return false;
       }
     }
 
     return true;
   }
 
   this.isUnexpectedEventScenario =
     function eventQueue_isUnexpectedEventsScenario(aScenario)
   {
     for (var idx = 0; idx < aScenario.length; idx++) {
-      if (!aScenario[idx].unexpected)
+      if (!aScenario[idx].unexpected && !aScenario[idx].todo)
         break;
     }
 
     return idx == aScenario.length;
   }
 
   this.hasUnexpectedEventsScenario =
     function eventQueue_hasUnexpectedEventsScenario()
@@ -1681,18 +1681,16 @@ function invokerChecker(aEventType, aTar
 
 /**
  * Generic invoker checker for todo events.
  */
 function todo_invokerChecker(aEventType, aTargetOrFunc, aTargetFuncArg)
 {
   this.__proto__ = new invokerChecker(aEventType, aTargetOrFunc,
                                       aTargetFuncArg, true);
-
-  this.unexpected = true;
   this.todo = true;
 }
 
 /**
  * Generic invoker checker for unexpected events.
  */
 function unexpectedInvokerChecker(aEventType, aTargetOrFunc, aTargetFuncArg)
 {
--- a/accessible/tests/mochitest/events/test_coalescence.html
+++ b/accessible/tests/mochitest/events/test_coalescence.html
@@ -482,16 +482,51 @@
         getNode("t6_sc").setAttribute("aria-owns", "t6_owns");
       };
 
       this.getID = function test6_getID() {
         return "Insert accessibles with a child node moved by aria-owns";
       };
     }
 
+    /**
+     * Insert text nodes under direct and grand children, and then hide
+     * their container by means of aria-owns.
+     *
+     * Markup:
+     * <div id="t7_moveplace" aria-owns="t7_c"></div>
+     * <div id="t7_c">
+     *   <div id="t7_c_directchild">ha</div>
+     *   <div><div id="t7_c_grandchild">ha</div></div>
+     * </div>
+     */
+    function test7()
+    {
+      this.eventSeq = [
+        new todo_invokerChecker(EVENT_HIDE, getNode('t7_c')),
+        new invokerChecker(EVENT_SHOW, getNode('t7_c')),
+        new invokerChecker(EVENT_REORDER, getNode('t7')),
+        new unexpectedInvokerChecker(EVENT_REORDER, getNode('t7_c_directchild')),
+        new unexpectedInvokerChecker(EVENT_REORDER, getNode('t7_c_grandchild')),
+        new unexpectedInvokerChecker(EVENT_SHOW, () => getNode('t7_c_directchild').firstChild),
+        new unexpectedInvokerChecker(EVENT_SHOW, () => getNode('t7_c_grandchild').firstChild)
+      ];
+
+      this.invoke = function test7_invoke()
+      {
+        getNode('t7_c_directchild').textContent = 'ha';
+        getNode('t7_c_grandchild').textContent = 'ha';
+        getNode('t7_moveplace').setAttribute('aria-owns', 't7_c');
+      };
+
+      this.getID = function test7_getID() {
+        return "Show child accessibles and then hide their container";
+      };
+    }
+
     ////////////////////////////////////////////////////////////////////////////
     // Do tests.
 
     //gA11yEventDumpToConsole = true; // debug stuff
     //enableLogging("tree,eventTree,verbose");
 
     var gQueue = null;
     function doTests()
@@ -514,16 +549,17 @@
       gQueue.push(new showParentNAddChild("select11", false));
       gQueue.push(new showParentNAddChild("select12", true));
 
       gQueue.push(new removeGrandChildrenNHideParent("t1_child1", "t1_child2", "t1_parent"));
       gQueue.push(new test3());
       gQueue.push(new test4());
       gQueue.push(new test5());
       gQueue.push(new test6());
+      gQueue.push(new test7());
 
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTests);
   </script>
 </head>
@@ -607,10 +643,18 @@
     <div role="button" id="t5_b">btn</div>
     <div role="listbox" id="t5_lb">
       <div role="option" id="t5_o">opt</div>
     </div>
   </div>
 
   <div id="t6">
   </div>
+
+  <div id="t7">
+    <div id="t7_moveplace"></div>
+    <div id="t7_c">
+      <div><div id="t7_c_grandchild"></div></div>
+      <div id="t7_c_directchild"></div>
+    </div>
+  </div>
 </body>
 </html>
--- a/browser/base/content/test/general/browser_contextmenu_childprocess.js
+++ b/browser/base/content/test/general/browser_contextmenu_childprocess.js
@@ -1,34 +1,31 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const gBaseURL = "https://example.com/browser/browser/base/content/test/general/";
 
 add_task(function *() {
-  let tab = gBrowser.addTab();
-  let browser = gBrowser.getBrowserForTab(tab);
+  let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, gBaseURL + "subtst_contextmenu.html");
 
-  gBrowser.selectedTab = tab;
-  yield promiseTabLoadEvent(tab, gBaseURL + "subtst_contextmenu.html");
-
-  let popupShownPromise = promiseWaitForEvent(window, "popupshown", true);
+  let contextMenu = document.getElementById("contentAreaContextMenu");
 
   // Get the point of the element with the page menu (test-pagemenu) and
   // synthesize a right mouse click there.
-  let eventDetails = { type : "contextmenu", button : 2 };
-  let rect = browser.contentWindow.document.getElementById("test-pagemenu").getBoundingClientRect();
-  EventUtils.synthesizeMouse(browser, rect.x + rect.width / 2, rect.y + rect.height / 2, eventDetails, window);
-
+  let popupShownPromise = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
+  yield BrowserTestUtils.synthesizeMouse("#test-pagemenu", 5, 5, { type : "contextmenu", button : 2 }, tab.linkedBrowser);
   let event = yield popupShownPromise;
 
-  let contextMenu = document.getElementById("contentAreaContextMenu");
   checkMenu(contextMenu);
+
+  let popupHiddenPromise = BrowserTestUtils.waitForEvent(contextMenu, "popuphidden");
   contextMenu.hidePopup();
-  gBrowser.removeCurrentTab();
+  yield popupHiddenPromise;
+
+  yield BrowserTestUtils.removeTab(tab);
 });
 
 function checkItems(menuitem, arr)
 {
   for (let i = 0; i < arr.length; i += 2) {
     let str = arr[i];
     let details = arr[i + 1];
     if (str == "---") {
--- a/devtools/shared/heapsnapshot/HeapSnapshot.cpp
+++ b/devtools/shared/heapsnapshot/HeapSnapshot.cpp
@@ -11,17 +11,17 @@
 
 #include "js/Debug.h"
 #include "js/TypeDecls.h"
 #include "js/UbiNodeBreadthFirst.h"
 #include "js/UbiNodeCensus.h"
 #include "js/UbiNodeDominatorTree.h"
 #include "js/UbiNodeShortestPaths.h"
 #include "mozilla/Attributes.h"
-#include "mozilla/CycleCollectedJSRuntime.h"
+#include "mozilla/CycleCollectedJSContext.h"
 #include "mozilla/devtools/AutoMemMap.h"
 #include "mozilla/devtools/CoreDump.pb.h"
 #include "mozilla/devtools/DeserializedNode.h"
 #include "mozilla/devtools/DominatorTree.h"
 #include "mozilla/devtools/FileDescriptorOutputStream.h"
 #include "mozilla/devtools/HeapSnapshotTempFileHelperChild.h"
 #include "mozilla/devtools/ZeroCopyNSIOutputStream.h"
 #include "mozilla/dom/ChromeUtils.h"
@@ -57,19 +57,19 @@ using ::google::protobuf::io::GzipInputS
 using ::google::protobuf::io::ZeroCopyInputStream;
 
 using JS::ubi::AtomOrTwoByteChars;
 using JS::ubi::ShortestPaths;
 
 MallocSizeOf
 GetCurrentThreadDebuggerMallocSizeOf()
 {
-  auto ccrt = CycleCollectedJSRuntime::Get();
-  MOZ_ASSERT(ccrt);
-  auto cx = ccrt->Context();
+  auto ccjscx = CycleCollectedJSContext::Get();
+  MOZ_ASSERT(ccjscx);
+  auto cx = ccjscx->Context();
   MOZ_ASSERT(cx);
   auto mallocSizeOf = JS::dbg::GetDebuggerMallocSizeOf(cx);
   MOZ_ASSERT(mallocSizeOf);
   return mallocSizeOf;
 }
 
 /*** Cycle Collection Boilerplate *****************************************************************/
 
@@ -550,19 +550,19 @@ HeapSnapshot::DescribeNode(JSContext* cx
 }
 
 
 already_AddRefed<DominatorTree>
 HeapSnapshot::ComputeDominatorTree(ErrorResult& rv)
 {
   Maybe<JS::ubi::DominatorTree> maybeTree;
   {
-    auto ccrt = CycleCollectedJSRuntime::Get();
-    MOZ_ASSERT(ccrt);
-    auto cx = ccrt->Context();
+    auto ccjscx = CycleCollectedJSContext::Get();
+    MOZ_ASSERT(ccjscx);
+    auto cx = ccjscx->Context();
     MOZ_ASSERT(cx);
     JS::AutoCheckCannotGC nogc(cx);
     maybeTree = JS::ubi::DominatorTree::Create(cx, nogc, getRoot());
   }
 
   if (NS_WARN_IF(maybeTree.isNothing())) {
     rv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return nullptr;
--- a/devtools/shared/heapsnapshot/tests/gtest/DevTools.h
+++ b/devtools/shared/heapsnapshot/tests/gtest/DevTools.h
@@ -10,17 +10,17 @@
 #include "jsapi.h"
 #include "jspubtd.h"
 #include "nsCRTGlue.h"
 
 #include "gtest/gtest.h"
 #include "gmock/gmock.h"
 #include "mozilla/devtools/HeapSnapshot.h"
 #include "mozilla/dom/ChromeUtils.h"
-#include "mozilla/CycleCollectedJSRuntime.h"
+#include "mozilla/CycleCollectedJSContext.h"
 #include "mozilla/Move.h"
 #include "js/Principals.h"
 #include "js/UbiNode.h"
 #include "js/UniquePtr.h"
 
 using namespace mozilla;
 using namespace mozilla::devtools;
 using namespace mozilla::dom;
@@ -55,17 +55,17 @@ struct DevTools : public ::testing::Test
 
     compartment = js::GetContextCompartment(cx);
     zone = js::GetContextZone(cx);
 
     _initialized = true;
   }
 
   JSContext* getContext() {
-    return CycleCollectedJSRuntime::Get()->Context();
+    return CycleCollectedJSContext::Get()->Context();
   }
 
   static void reportError(JSContext* cx, const char* message, JSErrorReport* report) {
     fprintf(stderr, "%s:%u:%s\n",
             report->filename ? report->filename : "<no filename>",
             (unsigned int) report->lineno,
             message);
   }
--- a/dom/animation/Animation.cpp
+++ b/dom/animation/Animation.cpp
@@ -1330,24 +1330,24 @@ Animation::GetRenderedDocument() const
   }
 
   return mEffect->AsKeyframeEffect()->GetRenderedDocument();
 }
 
 void
 Animation::DoFinishNotification(SyncNotifyFlag aSyncNotifyFlag)
 {
-  CycleCollectedJSRuntime* runtime = CycleCollectedJSRuntime::Get();
+  CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
 
   if (aSyncNotifyFlag == SyncNotifyFlag::Sync) {
     DoFinishNotificationImmediately();
   } else if (!mFinishNotificationTask.IsPending()) {
     RefPtr<nsRunnableMethod<Animation>> runnable =
       NewRunnableMethod(this, &Animation::DoFinishNotificationImmediately);
-    runtime->DispatchToMicroTask(do_AddRef(runnable));
+    context->DispatchToMicroTask(do_AddRef(runnable));
     mFinishNotificationTask = runnable.forget();
   }
 }
 
 void
 Animation::ResetFinishedPromise()
 {
   mFinishedIsResolved = false;
--- a/dom/asmjscache/AsmJSCache.cpp
+++ b/dom/asmjscache/AsmJSCache.cpp
@@ -7,17 +7,17 @@
 #include "AsmJSCache.h"
 
 #include <stdio.h>
 
 #include "js/RootingAPI.h"
 #include "jsfriendapi.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/CondVar.h"
-#include "mozilla/CycleCollectedJSRuntime.h"
+#include "mozilla/CycleCollectedJSContext.h"
 #include "mozilla/dom/asmjscache/PAsmJSCacheEntryChild.h"
 #include "mozilla/dom/asmjscache/PAsmJSCacheEntryParent.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
 #include "mozilla/dom/quota/Client.h"
 #include "mozilla/dom/quota/QuotaManager.h"
 #include "mozilla/dom/quota/QuotaObject.h"
 #include "mozilla/dom/quota/UsageInfo.h"
--- a/dom/base/ScriptSettings.cpp
+++ b/dom/base/ScriptSettings.cpp
@@ -2,17 +2,17 @@
 /* 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/ScriptSettings.h"
 #include "mozilla/ThreadLocal.h"
 #include "mozilla/Assertions.h"
-#include "mozilla/CycleCollectedJSRuntime.h"
+#include "mozilla/CycleCollectedJSContext.h"
 
 #include "jsapi.h"
 #include "xpcpublic.h"
 #include "nsIGlobalObject.h"
 #include "nsIDocShell.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIScriptContext.h"
 #include "nsContentUtils.h"
@@ -284,24 +284,24 @@ IsJSAPIActive()
   ScriptSettingsStackEntry* topEntry = ScriptSettingsStack::Top();
   return topEntry && !topEntry->NoJSAPI();
 }
 
 namespace danger {
 JSContext*
 GetJSContext()
 {
-  return CycleCollectedJSRuntime::Get()->Context();
+  return CycleCollectedJSContext::Get()->Context();
 }
 } // namespace danger
 
 JS::RootingContext*
 RootingCx()
 {
-  return CycleCollectedJSRuntime::Get()->RootingCx();
+  return CycleCollectedJSContext::Get()->RootingCx();
 }
 
 AutoJSAPI::AutoJSAPI()
   : ScriptSettingsStackEntry(nullptr, eJSAPI)
   , mCx(nullptr)
   , mIsMainThread(false) // For lack of anything better
 {
 }
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -5192,26 +5192,26 @@ nsContentUtils::AddScriptRunner(nsIRunna
   nsCOMPtr<nsIRunnable> runnable = aRunnable;
   AddScriptRunner(runnable.forget());
 }
 
 /* static */
 void
 nsContentUtils::RunInStableState(already_AddRefed<nsIRunnable> aRunnable)
 {
-  MOZ_ASSERT(CycleCollectedJSRuntime::Get(), "Must be on a script thread!");
-  CycleCollectedJSRuntime::Get()->RunInStableState(Move(aRunnable));
+  MOZ_ASSERT(CycleCollectedJSContext::Get(), "Must be on a script thread!");
+  CycleCollectedJSContext::Get()->RunInStableState(Move(aRunnable));
 }
 
 /* static */
 void
 nsContentUtils::RunInMetastableState(already_AddRefed<nsIRunnable> aRunnable)
 {
-  MOZ_ASSERT(CycleCollectedJSRuntime::Get(), "Must be on a script thread!");
-  CycleCollectedJSRuntime::Get()->RunInMetastableState(Move(aRunnable));
+  MOZ_ASSERT(CycleCollectedJSContext::Get(), "Must be on a script thread!");
+  CycleCollectedJSContext::Get()->RunInMetastableState(Move(aRunnable));
 }
 
 void
 nsContentUtils::EnterMicroTask()
 {
   MOZ_ASSERT(NS_IsMainThread());
   ++sMicroTaskLevel;
 }
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -1712,25 +1712,25 @@ static const JSClass ControllersShimClas
 #endif
 
 // static
 nsresult
 nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
                           JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
                           JS::MutableHandle<JS::PropertyDescriptor> desc)
 {
-  if (id == XPCJSRuntime::Get()->GetStringID(XPCJSRuntime::IDX_COMPONENTS)) {
+  if (id == XPCJSContext::Get()->GetStringID(XPCJSContext::IDX_COMPONENTS)) {
     return LookupComponentsShim(cx, obj, aWin->AsInner(), desc);
   }
 
 #ifdef USE_CONTROLLERS_SHIM
   // Note: We use |obj| rather than |aWin| to get the principal here, because
   // this is called during Window setup when the Document isn't necessarily
   // hooked up yet.
-  if (id == XPCJSRuntime::Get()->GetStringID(XPCJSRuntime::IDX_CONTROLLERS) &&
+  if (id == XPCJSContext::Get()->GetStringID(XPCJSContext::IDX_CONTROLLERS) &&
       !xpc::IsXrayWrapper(obj) &&
       !nsContentUtils::IsSystemPrincipal(nsContentUtils::ObjectPrincipal(obj)))
   {
     if (aWin->GetDoc()) {
       aWin->GetDoc()->WarnOnceAbout(nsIDocument::eWindow_Controllers);
     }
     MOZ_ASSERT(JS_IsGlobalObject(obj));
     JS::Rooted<JSObject*> shim(cx, JS_NewObject(cx, &ControllersShimClass));
--- a/dom/base/nsFrameMessageManager.cpp
+++ b/dom/base/nsFrameMessageManager.cpp
@@ -24,17 +24,17 @@
 #include "nsIXULRuntime.h"
 #include "nsIScriptError.h"
 #include "nsIConsoleService.h"
 #include "nsIMemoryReporter.h"
 #include "nsIProtocolHandler.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIDOMClassInfo.h"
 #include "xpcpublic.h"
-#include "mozilla/CycleCollectedJSRuntime.h"
+#include "mozilla/CycleCollectedJSContext.h"
 #include "mozilla/IntentionalCrash.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/MessagePort.h"
 #include "mozilla/dom/MessagePortList.h"
 #include "mozilla/dom/nsIContentParent.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -144,16 +144,17 @@
 #include "nsServiceManagerUtils.h"
 #ifdef MOZ_XUL
 #include "nsIDOMXULControlElement.h"
 #include "nsMenuPopupFrame.h"
 #endif
 #include "mozilla/dom/CustomEvent.h"
 #include "nsIJARChannel.h"
 #include "nsIScreenManager.h"
+#include "nsIEffectiveTLDService.h"
 
 #include "xpcprivate.h"
 
 #ifdef NS_PRINTING
 #include "nsIPrintSettings.h"
 #include "nsIPrintSettingsService.h"
 #include "nsIWebBrowserPrint.h"
 #endif
@@ -1219,17 +1220,19 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalW
 #endif
 #ifdef MOZ_B2G
     mNetworkUploadObserverEnabled(false),
     mNetworkDownloadObserverEnabled(false),
 #endif
     mCleanedUp(false),
     mDialogAbuseCount(0),
     mAreDialogsEnabled(true),
-    mCanSkipCCGeneration(0)
+    mCanSkipCCGeneration(0),
+    mStaticConstellation(0),
+    mConstellation(NullCString())
 {
   AssertIsOnMainThread();
 
   nsLayoutStatics::AddRef();
 
   // Initialize the PRCList (this).
   PR_INIT_CLIST(this);
 
@@ -1254,16 +1257,21 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalW
 
       Preferences::AddStrongObserver(mObserver, "intl.accept_languages");
     }
   } else {
     // |this| is an outer window. Outer windows start out frozen and
     // remain frozen until they get an inner window, so freeze this
     // outer window here.
     Freeze();
+
+    // As an outer window, we may be the root of a constellation. This initial
+    // static constellation may be overridden as this window is given a parent
+    // window or an opener.
+    mStaticConstellation = WindowID();
   }
 
   // We could have failed the first time through trying
   // to create the entropy collector, so we should
   // try to get one until we succeed.
 
   gRefCnt++;
 
@@ -3024,16 +3032,22 @@ nsGlobalWindow::SetDocShell(nsIDocShell*
   MOZ_ASSERT(aDocShell);
 
   if (aDocShell == mDocShell) {
     return;
   }
 
   mDocShell = aDocShell; // Weak Reference
 
+  // Copy over the static constellation from our new parent.
+  nsCOMPtr<nsPIDOMWindowOuter> parentWindow = GetParent();
+  if (parentWindow) {
+    mStaticConstellation = Cast(parentWindow)->mStaticConstellation;
+  }
+
   NS_ASSERTION(!mNavigator, "Non-null mNavigator in outer window!");
 
   if (mFrames) {
     mFrames->SetDocShell(aDocShell);
   }
 
   // Get our enclosing chrome shell and retrieve its global window impl, so
   // that we can do some forwarding to the chrome document.
@@ -3135,16 +3149,21 @@ nsGlobalWindow::SetOpenerWindow(nsPIDOMW
                "aOriginalOpener is true, but not first call to "
                "SetOpenerWindow!");
   NS_ASSERTION(aOpener || !aOriginalOpener,
                "Shouldn't set mHadOriginalOpener if aOpener is null");
 
   mOpener = do_GetWeakReference(aOpener);
   NS_ASSERTION(mOpener || !aOpener, "Opener must support weak references!");
 
+  // Copy over the static constellation from our new opener
+  if (aOpener) {
+    mStaticConstellation = Cast(aOpener)->mStaticConstellation;
+  }
+
   if (aOriginalOpener) {
     MOZ_ASSERT(!mHadOriginalOpener,
                "Probably too late to call ComputeIsSecureContext again");
     mHadOriginalOpener = true;
     mOriginalOpenerWasSecureContext =
       nsGlobalWindow::Cast(aOpener->GetCurrentInnerWindow())->IsSecureContext();
   }
 
@@ -4470,21 +4489,21 @@ bool
 nsGlobalWindow::MayResolve(jsid aId)
 {
   // Note: This function does not fail and may not have any side-effects.
   // Note: Keep this in sync with DoResolve.
   if (!JSID_IS_STRING(aId)) {
     return false;
   }
 
-  if (aId == XPCJSRuntime::Get()->GetStringID(XPCJSRuntime::IDX_COMPONENTS)) {
+  if (aId == XPCJSContext::Get()->GetStringID(XPCJSContext::IDX_COMPONENTS)) {
     return true;
   }
 
-  if (aId == XPCJSRuntime::Get()->GetStringID(XPCJSRuntime::IDX_CONTROLLERS)) {
+  if (aId == XPCJSContext::Get()->GetStringID(XPCJSContext::IDX_CONTROLLERS)) {
     // We only resolve .controllers in release builds and on non-chrome windows,
     // but let's not worry about any of that stuff.
     return true;
   }
 
   if (WebIDLGlobalNameHash::MayResolve(aId)) {
     return true;
   }
@@ -14379,16 +14398,56 @@ nsGlobalWindow::CheckForDPIChange()
     if (presContext) {
       if (presContext->DeviceContext()->CheckDPIChange()) {
         presContext->UIResolutionChanged();
       }
     }
   }
 }
 
+void
+nsGlobalWindow::GetConstellation(nsACString& aConstellation)
+{
+  FORWARD_TO_INNER_VOID(GetConstellation, (aConstellation));
+
+#ifdef DEBUG
+  RefPtr<nsGlobalWindow> outer = GetOuterWindowInternal();
+  MOZ_ASSERT(outer, "We should have an outer window");
+  RefPtr<nsGlobalWindow> top = outer->GetTopInternal();
+  RefPtr<nsPIDOMWindowOuter> opener = outer->GetOpener();
+  MOZ_ASSERT(!top || (top->mStaticConstellation ==
+                      outer->mStaticConstellation));
+  MOZ_ASSERT(!opener || (Cast(opener)->mStaticConstellation ==
+                         outer->mStaticConstellation));
+#endif
+
+  if (mConstellation.IsVoid()) {
+    mConstellation.Truncate();
+    // The dynamic constellation part comes from the eTLD+1 for the principal's URI.
+    nsCOMPtr<nsIPrincipal> principal = GetPrincipal();
+    nsCOMPtr<nsIURI> uri;
+    nsresult rv = principal->GetURI(getter_AddRefs(uri));
+    if (NS_SUCCEEDED(rv)) {
+      nsCOMPtr<nsIEffectiveTLDService> tldService =
+        do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
+      if (tldService) {
+        rv = tldService->GetBaseDomain(uri, 0, mConstellation);
+        if (NS_FAILED(rv)) {
+          mConstellation.Truncate();
+        }
+      }
+    }
+
+    // Get the static constellation from the outer window object.
+    mConstellation.AppendPrintf("^%llu", GetOuterWindowInternal()->mStaticConstellation);
+  }
+
+  aConstellation.Assign(mConstellation);
+}
+
 nsGlobalWindow::TemporarilyDisableDialogs::TemporarilyDisableDialogs(
   nsGlobalWindow* aWindow MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
 {
   MOZ_GUARD_OBJECT_NOTIFIER_INIT;
 
   MOZ_ASSERT(aWindow);
   nsGlobalWindow* topWindow = aWindow->GetScriptableTopInternal();
   if (!topWindow) {
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -1704,16 +1704,20 @@ private:
   void FireOnNewGlobalObject();
 
   void DisconnectEventTargetObjects();
 
   // Called only on outer windows to compute the value that will be returned by
   // IsSecureContext() for the inner window that corresponds to aDocument.
   bool ComputeIsSecureContext(nsIDocument* aDocument);
 
+public:
+
+  void GetConstellation(nsACString& aConstellation);
+
 protected:
   // This member is also used on both inner and outer windows, but
   // for slightly different purposes. On inner windows it means the
   // inner window is held onto by session history and should not
   // change. On outer windows it means that the window is in a state
   // where we don't want to force creation of a new inner window since
   // we're in the middle of doing just that.
   bool                          mIsFrozen : 1;
@@ -1921,16 +1925,19 @@ protected:
   // This is the CC generation the last time we called CanSkip.
   uint32_t mCanSkipCCGeneration;
 
   // The VR Displays for this window
   nsTArray<RefPtr<mozilla::dom::VRDisplay>> mVRDisplays;
 
   nsAutoPtr<mozilla::dom::VREventObserver> mVREventObserver;
 
+  uint64_t mStaticConstellation; // Only used on outer windows
+  nsCString mConstellation; // Only used on inner windows
+
   friend class nsDOMScriptableHelper;
   friend class nsDOMWindowUtils;
   friend class mozilla::dom::PostMessageEvent;
   friend class DesktopNotification;
 
   static WindowByIdTable* sWindowsById;
   static bool sWarnedAboutWindowInternal;
 };
--- a/dom/base/nsInProcessTabChildGlobal.cpp
+++ b/dom/base/nsInProcessTabChildGlobal.cpp
@@ -286,17 +286,17 @@ nsInProcessTabChildGlobal::PreHandleEven
   }
 
   return NS_OK;
 }
 
 nsresult
 nsInProcessTabChildGlobal::InitTabChildGlobal()
 {
-  // If you change this, please change GetCompartmentName() in XPCJSRuntime.cpp
+  // If you change this, please change GetCompartmentName() in XPCJSContext.cpp
   // accordingly.
   nsAutoCString id;
   id.AssignLiteral("inProcessTabChildGlobal");
   nsIURI* uri = mOwner->OwnerDoc()->GetDocumentURI();
   if (uri) {
     nsAutoCString u;
     nsresult rv = uri->GetSpec(u);
     NS_ENSURE_SUCCESS(rv, rv);
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -51,17 +51,17 @@
 #include "WrapperFactory.h"
 #include "nsGlobalWindow.h"
 #include "nsScriptNameSpaceManager.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/dom/DOMException.h"
 #include "mozilla/dom/DOMExceptionBinding.h"
 #include "mozilla/dom/ErrorEvent.h"
 #include "nsAXPCNativeCallContext.h"
-#include "mozilla/CycleCollectedJSRuntime.h"
+#include "mozilla/CycleCollectedJSContext.h"
 
 #include "nsJSPrincipals.h"
 
 #ifdef XP_MACOSX
 // AssertMacros.h defines 'check' and conflicts with AccessCheck.h
 #undef check
 #endif
 #include "AccessCheck.h"
@@ -70,17 +70,17 @@
 #include "prthread.h"
 
 #include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/asmjscache/AsmJSCache.h"
 #include "mozilla/dom/CanvasRenderingContext2DBinding.h"
-#include "mozilla/CycleCollectedJSRuntime.h"
+#include "mozilla/CycleCollectedJSContext.h"
 #include "mozilla/ContentEvents.h"
 
 #include "nsCycleCollectionNoteRootCallback.h"
 #include "GeckoProfiler.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
@@ -1203,17 +1203,17 @@ nsJSContext::GarbageCollectNow(JS::gcrea
   }
 
   JSGCInvocationKind gckind = aShrinking == ShrinkingGC ? GC_SHRINK : GC_NORMAL;
 
   if (sNeedsFullGC || aReason != JS::gcreason::CC_WAITING) {
     sNeedsFullGC = false;
     JS::PrepareForFullGC(sContext);
   } else {
-    CycleCollectedJSRuntime::Get()->PrepareWaitingZonesForGC();
+    CycleCollectedJSContext::Get()->PrepareWaitingZonesForGC();
   }
 
   if (aIncremental == IncrementalGC) {
     JS::StartIncrementalGC(sContext, gckind, aReason, aSliceMillis);
   } else {
     JS::GCForReason(sContext, gckind, aReason);
   }
 }
--- a/dom/base/nsWrapperCache.cpp
+++ b/dom/base/nsWrapperCache.cpp
@@ -4,17 +4,17 @@
  * 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 "nsWrapperCacheInlines.h"
 
 #include "js/Class.h"
 #include "js/Proxy.h"
 #include "mozilla/dom/DOMJSProxyHandler.h"
-#include "mozilla/CycleCollectedJSRuntime.h"
+#include "mozilla/CycleCollectedJSContext.h"
 #include "mozilla/HoldDropJSObjects.h"
 #include "nsCycleCollectionTraversalCallback.h"
 #include "nsCycleCollector.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 #ifdef DEBUG
@@ -26,28 +26,28 @@ nsWrapperCache::HasJSObjectMovedOp(JSObj
 #endif
 
 void
 nsWrapperCache::HoldJSObjects(void* aScriptObjectHolder,
                               nsScriptObjectTracer* aTracer)
 {
   cyclecollector::HoldJSObjectsImpl(aScriptObjectHolder, aTracer);
   if (mWrapper && !JS::ObjectIsTenured(mWrapper)) {
-    CycleCollectedJSRuntime::Get()->NurseryWrapperPreserved(mWrapper);
+    CycleCollectedJSContext::Get()->NurseryWrapperPreserved(mWrapper);
   }
 }
 
 void
 nsWrapperCache::SetWrapperJSObject(JSObject* aWrapper)
 {
   mWrapper = aWrapper;
   UnsetWrapperFlags(kWrapperFlagsMask & ~WRAPPER_IS_NOT_DOM_BINDING);
 
   if (aWrapper && !JS::ObjectIsTenured(aWrapper)) {
-    CycleCollectedJSRuntime::Get()->NurseryWrapperAdded(this);
+    CycleCollectedJSContext::Get()->NurseryWrapperAdded(this);
   }
 }
 
 void
 nsWrapperCache::ReleaseWrapper(void* aScriptObjectHolder)
 {
   if (PreservingWrapper()) {
     // PreserveWrapper puts new DOM bindings in the JS holders hash, but they
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -9,17 +9,17 @@
 
 #include "jsfriendapi.h"
 #include "jswrapper.h"
 #include "js/Conversions.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Alignment.h"
 #include "mozilla/Array.h"
 #include "mozilla/Assertions.h"
-#include "mozilla/CycleCollectedJSRuntime.h"
+#include "mozilla/CycleCollectedJSContext.h"
 #include "mozilla/DeferredFinalize.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/CallbackObject.h"
 #include "mozilla/dom/DOMJSClass.h"
 #include "mozilla/dom/DOMJSProxyHandler.h"
 #include "mozilla/dom/Exceptions.h"
 #include "mozilla/dom/NonRefcountedDOMObject.h"
 #include "mozilla/dom/Nullable.h"
--- a/dom/bindings/Exceptions.cpp
+++ b/dom/bindings/Exceptions.cpp
@@ -5,17 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/Exceptions.h"
 
 #include "js/GCAPI.h"
 #include "js/TypeDecls.h"
 #include "jsapi.h"
 #include "jsprf.h"
-#include "mozilla/CycleCollectedJSRuntime.h"
+#include "mozilla/CycleCollectedJSContext.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/DOMException.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "nsIProgrammingLanguage.h"
 #include "nsPIDOMWindow.h"
 #include "nsServiceManagerUtils.h"
 #include "nsThreadUtils.h"
 #include "XPCWrapper.h"
@@ -140,22 +140,22 @@ Throw(JSContext* aCx, nsresult aRv, cons
     return false;
   }
 
   if (JS_IsExceptionPending(aCx)) {
     // Don't clobber the existing exception.
     return false;
   }
 
-  CycleCollectedJSRuntime* runtime = CycleCollectedJSRuntime::Get();
-  nsCOMPtr<nsIException> existingException = runtime->GetPendingException();
+  CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
+  nsCOMPtr<nsIException> existingException = context->GetPendingException();
   // Make sure to clear the pending exception now.  Either we're going to reuse
   // it (and we already grabbed it), or we plan to throw something else and this
   // pending exception is no longer relevant.
-  runtime->SetPendingException(nullptr);
+  context->SetPendingException(nullptr);
 
   // Ignore the pending exception if we have a non-default message passed in.
   if (aMessage.IsEmpty() && existingException) {
     nsresult nr;
     if (NS_SUCCEEDED(existingException->GetResult(&nr)) &&
         aRv == nr) {
       // Reuse the existing exception.
       ThrowExceptionObject(aCx, existingException);
--- a/dom/events/EventListenerManager.cpp
+++ b/dom/events/EventListenerManager.cpp
@@ -4,17 +4,17 @@
  * 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/. */
 
 // Microsoft's API Name hackery sucks
 #undef CreateEvent
 
 #include "mozilla/AddonPathService.h"
 #include "mozilla/BasicEvents.h"
-#include "mozilla/CycleCollectedJSRuntime.h"
+#include "mozilla/CycleCollectedJSContext.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventListenerManager.h"
 #ifdef MOZ_B2G
 #include "mozilla/Hal.h"
 #endif // #ifdef MOZ_B2G
 #include "mozilla/HalSensor.h"
 #include "mozilla/InternalMutationEvent.h"
--- a/dom/events/JSEventHandler.cpp
+++ b/dom/events/JSEventHandler.cpp
@@ -14,17 +14,17 @@
 #include "nsVariant.h"
 #include "nsIDOMBeforeUnloadEvent.h"
 #include "nsGkAtoms.h"
 #include "xpcpublic.h"
 #include "nsJSEnvironment.h"
 #include "nsDOMJSUtils.h"
 #include "WorkerPrivate.h"
 #include "mozilla/ContentEvents.h"
-#include "mozilla/CycleCollectedJSRuntime.h"
+#include "mozilla/CycleCollectedJSContext.h"
 #include "mozilla/HoldDropJSObjects.h"
 #include "mozilla/JSEventHandler.h"
 #include "mozilla/Likely.h"
 #include "mozilla/dom/ErrorEvent.h"
 
 namespace mozilla {
 
 using namespace dom;
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -845,17 +845,17 @@ ContentParent::SendAsyncUpdate(nsIWidget
     return;
   }
   // Fire off an async request to the plugin to paint its window
   HWND hwnd = (HWND)aWidget->GetNativeData(NS_NATIVE_WINDOW);
   NS_ASSERTION(hwnd, "Expected valid hwnd value.");
   ContentParent* cp = reinterpret_cast<ContentParent*>(
     ::GetPropW(hwnd, kPluginWidgetContentParentProperty));
   if (cp && !cp->IsDestroyed()) {
-    cp->SendUpdateWindow((uintptr_t)hwnd);
+    Unused << cp->SendUpdateWindow((uintptr_t)hwnd);
   }
 }
 #endif // defined(XP_WIN)
 
 bool
 ContentParent::PreallocatedProcessReady()
 {
   return true;
--- a/dom/media/platforms/gonk/GonkVideoDecoderManager.cpp
+++ b/dom/media/platforms/gonk/GonkVideoDecoderManager.cpp
@@ -395,17 +395,18 @@ GonkVideoDecoderManager::CreateVideoData
                                                           gfx::IntRect& aPicture)
 {
   sp<GraphicBuffer> srcBuffer(aSource->graphicBuffer());
   RefPtr<TextureClient> textureClient;
 
   if (mNeedsCopyBuffer) {
     // Copy buffer contents for bug 1199809.
     if (!mCopyAllocator) {
-      mCopyAllocator = new TextureClientRecycleAllocator(ImageBridgeChild::GetSingleton());
+      RefPtr<layers::ImageBridgeChild> bridge = layers::ImageBridgeChild::GetSingleton();
+      mCopyAllocator = new TextureClientRecycleAllocator(bridge);
     }
     if (!mCopyAllocator) {
       GVDM_LOG("Create buffer allocator failed!");
       return nullptr;
     }
 
     gfx::IntSize size(srcBuffer->getWidth(), srcBuffer->getHeight());
     GonkTextureClientAllocationHelper helper(srcBuffer->getPixelFormat(), size);
--- a/dom/media/platforms/omx/GonkOmxPlatformLayer.cpp
+++ b/dom/media/platforms/omx/GonkOmxPlatformLayer.cpp
@@ -150,31 +150,33 @@ class GonkTextureClientRecycleHandler : 
 {
   typedef MozPromise<layers::TextureClient*, nsresult, /* IsExclusive = */ true> TextureClientRecyclePromise;
 
 public:
   GonkTextureClientRecycleHandler(OMX_VIDEO_PORTDEFINITIONTYPE& aDef)
     : ITextureClientRecycleAllocator()
     , mMonitor("GonkTextureClientRecycleHandler")
   {
+    RefPtr<layers::ImageBridgeChild> bridge = layers::ImageBridgeChild::GetSingleton();
+
     // Allocate Gralloc texture memory.
     layers::GrallocTextureData* textureData =
       layers::GrallocTextureData::Create(gfx::IntSize(aDef.nFrameWidth, aDef.nFrameHeight),
                                          aDef.eColorFormat,
                                          gfx::BackendType::NONE,
                                          GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_READ_OFTEN,
-                                         layers::ImageBridgeChild::GetSingleton());
+                                         bridge);
 
     mGraphBuffer = textureData->GetGraphicBuffer();
     MOZ_ASSERT(mGraphBuffer.get());
 
     mTextureClient =
       layers::TextureClient::CreateWithData(textureData,
                                             layers::TextureFlags::DEALLOCATE_CLIENT | layers::TextureFlags::RECYCLE,
-                                            layers::ImageBridgeChild::GetSingleton());
+                                            bridge);
     MOZ_ASSERT(mTextureClient);
 
     mPromise.SetMonitor(&mMonitor);
   }
 
   RefPtr<TextureClientRecyclePromise> WaitforRecycle()
   {
     MonitorAutoLock lock(mMonitor);
--- a/dom/media/webrtc/MediaEngineGonkVideoSource.cpp
+++ b/dom/media/webrtc/MediaEngineGonkVideoSource.cpp
@@ -408,18 +408,18 @@ MediaEngineGonkVideoSource::AllocImpl() 
 
   mCameraControl = ICameraControl::Create(mCaptureIndex);
   if (mCameraControl) {
     mState = kAllocated;
     // Add this as a listener for CameraControl events. We don't need
     // to explicitly remove this--destroying the CameraControl object
     // in DeallocImpl() will do that for us.
     mCameraControl->AddListener(this);
-    mTextureClientAllocator =
-      new layers::TextureClientRecycleAllocator(layers::ImageBridgeChild::GetSingleton());
+    RefPtr<layers::ImageBridgeChild> bridge = layers::ImageBridgeChild::GetSingleton();
+    mTextureClientAllocator = new layers::TextureClientRecycleAllocator(bridge);
     mTextureClientAllocator->SetMaxPoolSize(WEBRTC_GONK_VIDEO_SOURCE_POOL_BUFFERS);
   }
   mCallbackMonitor.Notify();
 }
 
 void
 MediaEngineGonkVideoSource::DeallocImpl() {
   MOZ_ASSERT(NS_IsMainThread());
--- a/dom/promise/Promise.cpp
+++ b/dom/promise/Promise.cpp
@@ -4,17 +4,17 @@
  * 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/Promise.h"
 
 #include "js/Debug.h"
 
 #include "mozilla/Atomics.h"
-#include "mozilla/CycleCollectedJSRuntime.h"
+#include "mozilla/CycleCollectedJSContext.h"
 #include "mozilla/OwningNonNull.h"
 #include "mozilla/Preferences.h"
 
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/DOMError.h"
 #include "mozilla/dom/DOMException.h"
 #include "mozilla/dom/DOMExceptionBinding.h"
 #include "mozilla/dom/MediaStreamError.h"
@@ -57,17 +57,17 @@ using namespace workers;
 class PromiseReactionJob final : public Runnable
 {
 public:
   PromiseReactionJob(Promise* aPromise,
                      PromiseCallback* aCallback,
                      const JS::Value& aValue)
     : mPromise(aPromise)
     , mCallback(aCallback)
-    , mValue(CycleCollectedJSRuntime::Get()->Runtime(), aValue)
+    , mValue(CycleCollectedJSContext::Get()->Context(), aValue)
   {
     MOZ_ASSERT(aPromise);
     MOZ_ASSERT(aCallback);
     MOZ_COUNT_CTOR(PromiseReactionJob);
   }
 
   virtual
   ~PromiseReactionJob()
@@ -182,17 +182,17 @@ GetPromise(JSContext* aCx, JS::Handle<JS
 // Equivalent to the specification's ResolvePromiseViaThenableTask.
 class PromiseResolveThenableJob final : public Runnable
 {
 public:
   PromiseResolveThenableJob(Promise* aPromise,
                             JS::Handle<JSObject*> aThenable,
                             PromiseInit* aThen)
     : mPromise(aPromise)
-    , mThenable(CycleCollectedJSRuntime::Get()->Runtime(), aThenable)
+    , mThenable(CycleCollectedJSContext::Get()->Context(), aThenable)
     , mThen(aThen)
   {
     MOZ_ASSERT(aPromise);
     MOZ_COUNT_CTOR(PromiseResolveThenableJob);
   }
 
   virtual
   ~PromiseResolveThenableJob()
@@ -1036,21 +1036,21 @@ Promise::ReportRejectedPromise(JSContext
 }
 #endif // defined(SPIDERMONKEY_PROMISE)
 
 bool
 Promise::PerformMicroTaskCheckpoint()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
 
-  CycleCollectedJSRuntime* runtime = CycleCollectedJSRuntime::Get();
+  CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
 
   // On the main thread, we always use the main promise micro task queue.
   std::queue<nsCOMPtr<nsIRunnable>>& microtaskQueue =
-    runtime->GetPromiseMicroTaskQueue();
+    context->GetPromiseMicroTaskQueue();
 
   if (microtaskQueue.empty()) {
     return false;
   }
 
   AutoSlowOperation aso;
 
   do {
@@ -1059,83 +1059,83 @@ Promise::PerformMicroTaskCheckpoint()
 
     // This function can re-enter, so we remove the element before calling.
     microtaskQueue.pop();
     nsresult rv = runnable->Run();
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return false;
     }
     aso.CheckForInterrupt();
-    runtime->AfterProcessMicrotask();
+    context->AfterProcessMicrotask();
   } while (!microtaskQueue.empty());
 
   return true;
 }
 
 void
 Promise::PerformWorkerMicroTaskCheckpoint()
 {
   MOZ_ASSERT(!NS_IsMainThread(), "Wrong thread!");
 
-  CycleCollectedJSRuntime* runtime = CycleCollectedJSRuntime::Get();
+  CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
 
   for (;;) {
     // For a normal microtask checkpoint, we try to use the debugger microtask
     // queue first. If the debugger queue is empty, we use the normal microtask
     // queue instead.
     std::queue<nsCOMPtr<nsIRunnable>>* microtaskQueue =
-      &runtime->GetDebuggerPromiseMicroTaskQueue();
+      &context->GetDebuggerPromiseMicroTaskQueue();
 
     if (microtaskQueue->empty()) {
-      microtaskQueue = &runtime->GetPromiseMicroTaskQueue();
+      microtaskQueue = &context->GetPromiseMicroTaskQueue();
       if (microtaskQueue->empty()) {
         break;
       }
     }
 
     nsCOMPtr<nsIRunnable> runnable = microtaskQueue->front().forget();
     MOZ_ASSERT(runnable);
 
     // This function can re-enter, so we remove the element before calling.
     microtaskQueue->pop();
     nsresult rv = runnable->Run();
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return;
     }
-    runtime->AfterProcessMicrotask();
+    context->AfterProcessMicrotask();
   }
 }
 
 void
 Promise::PerformWorkerDebuggerMicroTaskCheckpoint()
 {
   MOZ_ASSERT(!NS_IsMainThread(), "Wrong thread!");
 
-  CycleCollectedJSRuntime* runtime = CycleCollectedJSRuntime::Get();
+  CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
 
   for (;;) {
     // For a debugger microtask checkpoint, we always use the debugger microtask
     // queue.
     std::queue<nsCOMPtr<nsIRunnable>>* microtaskQueue =
-      &runtime->GetDebuggerPromiseMicroTaskQueue();
+      &context->GetDebuggerPromiseMicroTaskQueue();
 
     if (microtaskQueue->empty()) {
       break;
     }
 
     nsCOMPtr<nsIRunnable> runnable = microtaskQueue->front().forget();
     MOZ_ASSERT(runnable);
 
     // This function can re-enter, so we remove the element before calling.
     microtaskQueue->pop();
     nsresult rv = runnable->Run();
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return;
     }
-    runtime->AfterProcessMicrotask();
+    context->AfterProcessMicrotask();
   }
 }
 
 #ifndef SPIDERMONKEY_PROMISE
 
 /* static */ bool
 Promise::JSCallback(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
 {
@@ -2710,17 +2710,17 @@ Promise::HandleException(JSContext* aCx)
 }
 
 void
 Promise::ResolveInternal(JSContext* aCx,
                          JS::Handle<JS::Value> aValue)
 {
   NS_ASSERT_OWNINGTHREAD(Promise);
 
-  CycleCollectedJSRuntime* runtime = CycleCollectedJSRuntime::Get();
+  CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
 
   mResolvePending = true;
 
   if (aValue.isObject()) {
     JS::Rooted<JSObject*> valueObj(aCx, &aValue.toObject());
 
     // Thenables.
     JS::Rooted<JS::Value> then(aCx);
@@ -2752,17 +2752,17 @@ Promise::ResolveInternal(JSContext* aCx,
       //
       // Ensuring that stuff while not inside SpiderMonkey is painful, so let's
       // drop the fast path for now.
 
       RefPtr<PromiseInit> thenCallback =
         new PromiseInit(nullptr, thenObj, mozilla::dom::GetIncumbentGlobal());
       RefPtr<PromiseResolveThenableJob> task =
         new PromiseResolveThenableJob(this, valueObj, thenCallback);
-      runtime->DispatchToMicroTask(task.forget());
+      context->DispatchToMicroTask(task.forget());
       return;
     }
   }
 
   MaybeSettle(aValue, Resolved);
 }
 
 void
@@ -2856,17 +2856,17 @@ Promise::MaybeSettle(JS::Handle<JS::Valu
   Settle(aValue, aState);
 }
 
 void
 Promise::TriggerPromiseReactions()
 {
   NS_ASSERT_OWNINGTHREAD(Promise);
 
-  CycleCollectedJSRuntime* runtime = CycleCollectedJSRuntime::Get();
+  CycleCollectedJSContext* runtime = CycleCollectedJSContext::Get();
 
   nsTArray<RefPtr<PromiseCallback>> callbacks;
   callbacks.SwapElements(mState == Resolved ? mResolveCallbacks
                                             : mRejectCallbacks);
   mResolveCallbacks.Clear();
   mRejectCallbacks.Clear();
 
   for (uint32_t i = 0; i < callbacks.Length(); ++i) {
--- a/dom/promise/PromiseDebugging.cpp
+++ b/dom/promise/PromiseDebugging.cpp
@@ -2,17 +2,17 @@
 /* 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 "js/Value.h"
 #include "nsThreadUtils.h"
 
-#include "mozilla/CycleCollectedJSRuntime.h"
+#include "mozilla/CycleCollectedJSContext.h"
 #include "mozilla/ThreadLocal.h"
 #include "mozilla/TimeStamp.h"
 
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseBinding.h"
 #include "mozilla/dom/PromiseDebugging.h"
@@ -313,26 +313,26 @@ PromiseDebugging::GetTimeToSettle(Global
 }
 
 #endif // SPIDERMONKEY_PROMISE
 
 /* static */ void
 PromiseDebugging::AddUncaughtRejectionObserver(GlobalObject&,
                                                UncaughtRejectionObserver& aObserver)
 {
-  CycleCollectedJSRuntime* storage = CycleCollectedJSRuntime::Get();
+  CycleCollectedJSContext* storage = CycleCollectedJSContext::Get();
   nsTArray<nsCOMPtr<nsISupports>>& observers = storage->mUncaughtRejectionObservers;
   observers.AppendElement(&aObserver);
 }
 
 /* static */ bool
 PromiseDebugging::RemoveUncaughtRejectionObserver(GlobalObject&,
                                                   UncaughtRejectionObserver& aObserver)
 {
-  CycleCollectedJSRuntime* storage = CycleCollectedJSRuntime::Get();
+  CycleCollectedJSContext* storage = CycleCollectedJSContext::Get();
   nsTArray<nsCOMPtr<nsISupports>>& observers = storage->mUncaughtRejectionObservers;
   for (size_t i = 0; i < observers.Length(); ++i) {
     UncaughtRejectionObserver* observer = static_cast<UncaughtRejectionObserver*>(observers[i].get());
     if (*observer == aObserver) {
       observers.RemoveElementAt(i);
       return true;
     }
   }
@@ -340,46 +340,46 @@ PromiseDebugging::RemoveUncaughtRejectio
 }
 
 #ifdef SPIDERMONKEY_PROMISE
 
 /* static */ void
 PromiseDebugging::AddUncaughtRejection(JS::HandleObject aPromise)
 {
   // This might OOM, but won't set a pending exception, so we'll just ignore it.
-  if (CycleCollectedJSRuntime::Get()->mUncaughtRejections.append(aPromise)) {
+  if (CycleCollectedJSContext::Get()->mUncaughtRejections.append(aPromise)) {
     FlushRejections::DispatchNeeded();
   }
 }
 
 /* void */ void
 PromiseDebugging::AddConsumedRejection(JS::HandleObject aPromise)
 {
   // If the promise is in our list of uncaught rejections, we haven't yet
   // reported it as unhandled. In that case, just remove it from the list
   // and don't add it to the list of consumed rejections.
-  auto& uncaughtRejections = CycleCollectedJSRuntime::Get()->mUncaughtRejections;
+  auto& uncaughtRejections = CycleCollectedJSContext::Get()->mUncaughtRejections;
   for (size_t i = 0; i < uncaughtRejections.length(); i++) {
     if (uncaughtRejections[i] == aPromise) {
       // To avoid large amounts of memmoves, we don't shrink the vector here.
       // Instead, we filter out nullptrs when iterating over the vector later.
       uncaughtRejections[i].set(nullptr);
       return;
     }
   }
   // This might OOM, but won't set a pending exception, so we'll just ignore it.
-  if (CycleCollectedJSRuntime::Get()->mConsumedRejections.append(aPromise)) {
+  if (CycleCollectedJSContext::Get()->mConsumedRejections.append(aPromise)) {
     FlushRejections::DispatchNeeded();
   }
 }
 
 /* static */ void
 PromiseDebugging::FlushUncaughtRejectionsInternal()
 {
-  CycleCollectedJSRuntime* storage = CycleCollectedJSRuntime::Get();
+  CycleCollectedJSContext* storage = CycleCollectedJSContext::Get();
 
   auto& uncaught = storage->mUncaughtRejections;
   auto& consumed = storage->mConsumedRejections;
 
   AutoJSAPI jsapi;
   jsapi.Init();
   JSContext* cx = jsapi.cx();
 
@@ -422,24 +422,24 @@ PromiseDebugging::FlushUncaughtRejection
   storage->mConsumedRejections.clear();
 }
 
 #else
 
 /* static */ void
 PromiseDebugging::AddUncaughtRejection(Promise& aPromise)
 {
-  CycleCollectedJSRuntime::Get()->mUncaughtRejections.AppendElement(&aPromise);
+  CycleCollectedJSContext::Get()->mUncaughtRejections.AppendElement(&aPromise);
   FlushRejections::DispatchNeeded();
 }
 
 /* void */ void
 PromiseDebugging::AddConsumedRejection(Promise& aPromise)
 {
-  CycleCollectedJSRuntime::Get()->mConsumedRejections.AppendElement(&aPromise);
+  CycleCollectedJSContext::Get()->mConsumedRejections.AppendElement(&aPromise);
   FlushRejections::DispatchNeeded();
 }
 
 /* static */ void
 PromiseDebugging::GetPromiseID(GlobalObject&,
                                JS::Handle<JSObject*> aPromise,
                                nsString& aID,
                                ErrorResult& aRv)
@@ -451,17 +451,17 @@ PromiseDebugging::GetPromiseID(GlobalObj
   uint64_t promiseID = promise->GetID();
   aID = sIDPrefix;
   aID.AppendInt(promiseID);
 }
 
 /* static */ void
 PromiseDebugging::FlushUncaughtRejectionsInternal()
 {
-  CycleCollectedJSRuntime* storage = CycleCollectedJSRuntime::Get();
+  CycleCollectedJSContext* storage = CycleCollectedJSContext::Get();
 
   // The Promise that have been left uncaught (rejected and last in
   // their chain) since the last call to this function.
   nsTArray<nsCOMPtr<nsISupports>> uncaught;
   storage->mUncaughtRejections.SwapElements(uncaught);
 
   // The Promise that have been left uncaught at some point, but that
   // have eventually had their `then` method called.
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -23,17 +23,17 @@
 #include "nsPIDOMWindow.h"
 
 #include <algorithm>
 #include "BackgroundChild.h"
 #include "GeckoProfiler.h"
 #include "jsfriendapi.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Atomics.h"
-#include "mozilla/CycleCollectedJSRuntime.h"
+#include "mozilla/CycleCollectedJSContext.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/dom/asmjscache/AsmJSCache.h"
 #include "mozilla/dom/AtomList.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/ErrorEventBinding.h"
 #include "mozilla/dom/EventTargetBinding.h"
 #include "mozilla/dom/MessageChannel.h"
@@ -887,21 +887,21 @@ FinishAsyncTaskCallback(JS::AsyncTask* a
 
     MOZ_ALWAYS_TRUE(cr->Dispatch());
     return false;
   }
 
   return true;
 }
 
-class WorkerJSRuntime;
+class WorkerJSContext;
 
 class WorkerThreadContextPrivate : private PerThreadAtomCache
 {
-  friend class WorkerJSRuntime;
+  friend class WorkerJSContext;
 
   WorkerPrivate* mWorkerPrivate;
 
 public:
   // This can't return null, but we can't lose the "Get" prefix in the name or
   // it will be ambiguous with the WorkerPrivate class name.
   WorkerPrivate*
   GetWorkerPrivate() const
@@ -1038,28 +1038,28 @@ Wrap(JSContext *cx, JS::HandleObject exi
   return js::Wrapper::New(cx, obj, wrapper);
 }
 
 static const JSWrapObjectCallbacks WrapObjectCallbacks = {
   Wrap,
   nullptr,
 };
 
-class WorkerJSRuntime : public mozilla::CycleCollectedJSRuntime
+class MOZ_STACK_CLASS WorkerJSContext final : public mozilla::CycleCollectedJSContext
 {
 public:
   // The heap size passed here doesn't matter, we will change it later in the
   // call to JS_SetGCParameter inside InitJSContextForWorker.
-  explicit WorkerJSRuntime(WorkerPrivate* aWorkerPrivate)
+  explicit WorkerJSContext(WorkerPrivate* aWorkerPrivate)
     : mWorkerPrivate(aWorkerPrivate)
   {
     MOZ_ASSERT(aWorkerPrivate);
   }
 
-  ~WorkerJSRuntime()
+  ~WorkerJSContext()
   {
     JSContext* cx = MaybeContext();
     if (!cx) {
       return;   // Initialize() must have failed
     }
 
     delete static_cast<WorkerThreadContextPrivate*>(JS_GetContextPrivate(cx));
     JS_SetContextPrivate(cx, nullptr);
@@ -1073,17 +1073,17 @@ public:
     // The CC is shut down, and the superclass destructor will GC, so make sure
     // we don't try to CC again.
     mWorkerPrivate = nullptr;
   }
 
   nsresult Initialize(JSContext* aParentContext)
   {
     nsresult rv =
-      CycleCollectedJSRuntime::Initialize(aParentContext,
+      CycleCollectedJSContext::Initialize(aParentContext,
                                           WORKER_DEFAULT_RUNTIME_HEAPSIZE,
                                           WORKER_DEFAULT_NURSERY_SIZE);
      if (NS_WARN_IF(NS_FAILED(rv))) {
        return rv;
      }
 
     JSContext* cx = Context();
 
@@ -1137,20 +1137,20 @@ public:
     }
   }
 
   virtual void AfterProcessTask(uint32_t aRecursionDepth) override
   {
     // Only perform the Promise microtask checkpoint on the outermost event
     // loop.  Don't run it, for example, during sync XHR or importScripts.
     if (aRecursionDepth == 2) {
-      CycleCollectedJSRuntime::AfterProcessTask(aRecursionDepth);
+      CycleCollectedJSContext::AfterProcessTask(aRecursionDepth);
     } else if (aRecursionDepth > 2) {
       AutoDisableMicroTaskCheckpoint disableMicroTaskCheckpoint;
-      CycleCollectedJSRuntime::AfterProcessTask(aRecursionDepth);
+      CycleCollectedJSContext::AfterProcessTask(aRecursionDepth);
     }
   }
 
   virtual void DispatchToMicroTask(already_AddRefed<nsIRunnable> aRunnable) override
   {
     RefPtr<nsIRunnable> runnable(aRunnable);
 
     MOZ_ASSERT(!NS_IsMainThread());
@@ -1445,29 +1445,29 @@ GetWorkerPrivateFromContext(JSContext* a
     static_cast<WorkerThreadContextPrivate*>(cxPrivate)->GetWorkerPrivate();
 }
 
 WorkerPrivate*
 GetCurrentThreadWorkerPrivate()
 {
   MOZ_ASSERT(!NS_IsMainThread());
 
-  CycleCollectedJSRuntime* ccrt = CycleCollectedJSRuntime::Get();
-  if (!ccrt) {
+  CycleCollectedJSContext* ccjscx = CycleCollectedJSContext::Get();
+  if (!ccjscx) {
     return nullptr;
   }
 
-  JSContext* cx = ccrt->Context();
+  JSContext* cx = ccjscx->Context();
   MOZ_ASSERT(cx);
 
   void* cxPrivate = JS_GetContextPrivate(cx);
   if (!cxPrivate) {
-    // This can happen if the nsCycleCollector_shutdown() in ~WorkerJSRuntime()
+    // This can happen if the nsCycleCollector_shutdown() in ~WorkerJSContext()
     // triggers any calls to GetCurrentThreadWorkerPrivate().  At this stage
-    // CycleCollectedJSRuntime::Get() will still return a runtime, but
+    // CycleCollectedJSContext::Get() will still return a context, but
     // the context private has already been cleared.
     return nullptr;
   }
 
   return
     static_cast<WorkerThreadContextPrivate*>(cxPrivate)->GetWorkerPrivate();
 }
 
@@ -1878,17 +1878,17 @@ RuntimeService::ScheduleWorker(WorkerPri
   int32_t priority = aWorkerPrivate->IsChromeWorker() ?
                      nsISupportsPriority::PRIORITY_NORMAL :
                      nsISupportsPriority::PRIORITY_LOW;
 
   if (NS_FAILED(thread->SetPriority(priority))) {
     NS_WARNING("Could not set the thread's priority!");
   }
 
-  JSContext* cx = CycleCollectedJSRuntime::Get()->Context();
+  JSContext* cx = CycleCollectedJSContext::Get()->Context();
   nsCOMPtr<nsIRunnable> runnable =
     new WorkerThreadPrimaryRunnable(aWorkerPrivate, thread,
                                     JS_GetParentContext(cx));
   if (NS_FAILED(thread->DispatchPrimaryRunnable(friendKey, runnable.forget()))) {
     UnregisterWorker(aWorkerPrivate);
     return false;
   }
 
@@ -2827,27 +2827,27 @@ WorkerThreadPrimaryRunnable::Run()
 
   SetThreadHelper threadHelper(mWorkerPrivate, mThread);
 
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   {
     nsCycleCollector_startup();
 
-    WorkerJSRuntime runtime(mWorkerPrivate);
-    nsresult rv = runtime.Initialize(mParentContext);
+    WorkerJSContext context(mWorkerPrivate);
+    nsresult rv = context.Initialize(mParentContext);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
-    JSContext* cx = runtime.Context();
+    JSContext* cx = context.Context();
 
     if (!InitJSContextForWorker(mWorkerPrivate, cx)) {
       // XXX need to fire an error at parent.
-      NS_ERROR("Failed to create runtime and context!");
+      NS_ERROR("Failed to create context!");
       return NS_ERROR_FAILURE;
     }
 
     {
 #ifdef MOZ_ENABLE_PROFILER_SPS
       PseudoStack* stack = mozilla_get_pseudo_stack();
       if (stack) {
         stack->sampleContext(cx);
@@ -2884,17 +2884,17 @@ WorkerThreadPrimaryRunnable::Run()
     // which should break all cycles that touch JS.
     JS_GC(cx);
 
     // Before shutting down the cycle collector we need to do one more pass
     // through the event loop to clean up any C++ objects that need deferred
     // cleanup.
     mWorkerPrivate->ClearMainEventQueue(WorkerPrivate::WorkerRan);
 
-    // Now WorkerJSRuntime goes out of scope and its destructor will shut
+    // Now WorkerJSContext goes out of scope and its destructor will shut
     // down the cycle collector. This breaks any remaining cycles and collects
     // any remaining C++ objects.
   }
 
   threadHelper.Nullify();
 
   mWorkerPrivate->ScheduleDeletion(WorkerPrivate::WorkerRan);
 
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -1503,26 +1503,26 @@ public:
 #ifdef DEBUG
 static bool
 StartsWithExplicit(nsACString& s)
 {
     return StringBeginsWith(s, NS_LITERAL_CSTRING("explicit/"));
 }
 #endif
 
-class WorkerJSRuntimeStats : public JS::RuntimeStats
+class MOZ_STACK_CLASS WorkerJSContextStats final : public JS::RuntimeStats
 {
   const nsACString& mRtPath;
 
 public:
-  explicit WorkerJSRuntimeStats(const nsACString& aRtPath)
+  explicit WorkerJSContextStats(const nsACString& aRtPath)
   : JS::RuntimeStats(JsWorkerMallocSizeOf), mRtPath(aRtPath)
   { }
 
-  ~WorkerJSRuntimeStats()
+  ~WorkerJSContextStats()
   {
     for (size_t i = 0; i != zoneStatsVector.length(); i++) {
       delete static_cast<xpc::ZoneStatsExtras*>(zoneStatsVector[i].extra);
     }
 
     for (size_t i = 0; i != compartmentStatsVector.length(); i++) {
       delete static_cast<xpc::CompartmentStatsExtras*>(compartmentStatsVector[i].extra);
     }
@@ -2022,20 +2022,20 @@ public:
   }
 
   NS_IMETHOD
   CollectReports(nsIHandleReportCallback* aHandleReport,
                  nsISupports* aData, bool aAnonymize) override
   {
     AssertIsOnMainThread();
 
-    // Assumes that WorkerJSRuntimeStats will hold a reference to |path|, and
+    // Assumes that WorkerJSContextStats will hold a reference to |path|, and
     // not a copy, as TryToMapAddon() may later modify it.
     nsCString path;
-    WorkerJSRuntimeStats rtStats(path);
+    WorkerJSContextStats cxStats(path);
 
     {
       MutexAutoLock lock(mMutex);
 
       if (!mWorkerPrivate) {
         // Returning NS_OK here will effectively report 0 memory.
         return NS_OK;
       }
@@ -2055,23 +2055,23 @@ public:
         NS_ConvertUTF16toUTF8 escapedURL(mWorkerPrivate->ScriptURL());
         escapedURL.ReplaceChar('/', '\\');
         path.Append(escapedURL);
       }
       path.AppendPrintf(", 0x%p)/", static_cast<void*>(mWorkerPrivate));
 
       TryToMapAddon(path);
 
-      if (!mWorkerPrivate->BlockAndCollectRuntimeStats(&rtStats, aAnonymize)) {
+      if (!mWorkerPrivate->BlockAndCollectRuntimeStats(&cxStats, aAnonymize)) {
         // Returning NS_OK here will effectively report 0 memory.
         return NS_OK;
       }
     }
 
-    xpc::ReportJSRuntimeExplicitTreeStats(rtStats, path, aHandleReport, aData,
+    xpc::ReportJSRuntimeExplicitTreeStats(cxStats, path, aHandleReport, aData,
                                           aAnonymize);
     return NS_OK;
   }
 
 private:
   ~MemoryReporter()
   { }
 
@@ -4593,17 +4593,17 @@ WorkerPrivate::DoRunLoop(JSContext* aCx)
   MOZ_CRASH("Shouldn't get here!");
 }
 
 void
 WorkerPrivate::OnProcessNextEvent()
 {
   AssertIsOnWorkerThread();
 
-  uint32_t recursionDepth = CycleCollectedJSRuntime::Get()->RecursionDepth();
+  uint32_t recursionDepth = CycleCollectedJSContext::Get()->RecursionDepth();
   MOZ_ASSERT(recursionDepth);
 
   // Normally we process control runnables in DoRunLoop or RunCurrentSyncLoop.
   // However, it's possible that non-worker C++ could spin its own nested event
   // loop, and in that case we must ensure that we continue to process control
   // runnables here.
   if (recursionDepth > 1 &&
       mSyncLoopStack.Length() < recursionDepth - 1) {
@@ -4612,17 +4612,17 @@ WorkerPrivate::OnProcessNextEvent()
     // return value.
   }
 }
 
 void
 WorkerPrivate::AfterProcessNextEvent()
 {
   AssertIsOnWorkerThread();
-  MOZ_ASSERT(CycleCollectedJSRuntime::Get()->RecursionDepth());
+  MOZ_ASSERT(CycleCollectedJSContext::Get()->RecursionDepth());
 }
 
 void
 WorkerPrivate::MaybeDispatchLoadFailedRunnable()
 {
   AssertIsOnWorkerThread();
 
   nsCOMPtr<nsIRunnable> runnable = StealLoadFailedAsyncRunnable();
--- a/dom/workers/WorkerThread.cpp
+++ b/dom/workers/WorkerThread.cpp
@@ -326,17 +326,17 @@ WorkerThread::Observer::OnProcessNextEve
 
   // If the PBackground child is not created yet, then we must permit
   // blocking event processing to support
   // BackgroundChild::SynchronouslyCreateForCurrentThread(). If this occurs
   // then we are spinning on the event queue at the start of
   // PrimaryWorkerRunnable::Run() and don't want to process the event in
   // mWorkerPrivate yet.
   if (aMayWait) {
-    MOZ_ASSERT(CycleCollectedJSRuntime::Get()->RecursionDepth() == 2);
+    MOZ_ASSERT(CycleCollectedJSContext::Get()->RecursionDepth() == 2);
     MOZ_ASSERT(!BackgroundChild::GetForCurrentThread());
     return NS_OK;
   }
 
   mWorkerPrivate->OnProcessNextEvent();
   return NS_OK;
 }
 
--- a/gfx/doc/AsyncPanZoom.md
+++ b/gfx/doc/AsyncPanZoom.md
@@ -158,57 +158,57 @@ Another problem that we need to deal wit
 This ability is defined in web standards and is non-negotiable.
 Touch event listeners in web content are allowed call preventDefault() on the touchstart or first touchmove event for a touch point; doing this is supposed to "consume" the event and prevent touch-based panning.
 As we saw in a previous section, the input event needs to be untransformed by the APZ code before it can be delivered to content.
 But, because of the preventDefault problem, we cannot fully process the touch event in the APZ code until content has had a chance to handle it.
 Web browsers in general solve this problem by inserting a delay of up to 300ms before processing the input - that is, web content is allowed up to 300ms to process the event and call preventDefault on it.
 If web content takes longer than 300ms, or if it completes handling of the event without calling preventDefault, then the browser immediately starts processing the events.
 
 The way the APZ implementation deals with this is that upon receiving a touch event, it immediately returns an untransformed version that can be dispatched to content.
-It also schedules a 300ms timeout during which content is allowed to prevent scrolling.
+It also schedules a 400ms timeout (600ms on Android) during which content is allowed to prevent scrolling.
 There is an API that allows the main-thread event dispatching code to notify the APZ as to whether or not the default action should be prevented.
 If the APZ content response timeout expires, or if the main-thread event dispatching code notifies the APZ of the preventDefault status, then the APZ continues with the processing of the events (which may involve discarding the events).
 
-The touch-action CSS property from the pointer-events spec is intended to allow eliminating this 300ms delay in many cases (although for backwards compatibility it will still be needed for a while).
+The touch-action CSS property from the pointer-events spec is intended to allow eliminating this 400ms delay in many cases (although for backwards compatibility it will still be needed for a while).
 Note that even with touch-action implemented, there may be cases where the APZ code does not know the touch-action behaviour of the point the user touched.
-In such cases, the APZ code will still wait up to 300ms for the main thread to provide it with the touch-action behaviour information.
+In such cases, the APZ code will still wait up to 400ms for the main thread to provide it with the touch-action behaviour information.
 
 ## Technical details
 
 This section describes various pieces of the APZ code, and goes into more specific detail on APIs and code than the previous sections.
 The primary purpose of this section is to help people who plan on making changes to the code, while also not going into so much detail that it needs to be updated with every patch.
 
 ### Overall flow of input events
 
 This section describes how input events flow through the APZ code.
 <ol>
 <li value="1">
 Input events arrive from the hardware/widget code into the APZ via APZCTreeManager::ReceiveInputEvent.
 The thread that invokes this is called the input thread, and may or may not be the same as the Gecko main thread.
 </li>
 <li value="2">
-Conceptually the first thing that the APZCTreeManager does is to group these events into "input blocks".
-An input block is a contiguous set of events that get handled together.
+Conceptually the first thing that the APZCTreeManager does is to associate these events with "input blocks".
+An input block is a set of events that share certain properties, and generally are intended to represent a single gesture.
 For example with touch events, all events following a touchstart up to but not including the next touchstart are in the same block.
 All of the events in a given block will go to the same APZC instance and will either all be processed or all be dropped.
 </li>
 <li value="3">
 Using the first event in the input block, the APZCTreeManager does a hit-test to see which APZC it hits.
 This hit-test uses the event regions populated on the layers, which may be larger than the true hit area of the layer.
 If no APZC is hit, the events are discarded and we jump to step 6.
 Otherwise, the input block is tagged with the hit APZC as a tentative target and put into a global APZ input queue.
 </li>
 <li value="4">
  <ol>
   <li value="i">
    If the input events landed outside the dispatch-to-content event region for the layer, any available events in the input block are processed.
    These may trigger behaviours like scrolling or tap gestures.
   </li>
   <li value="ii">
-   If the input events landed inside the dispatch-to-content event region for the layer, the events are left in the queue and a 300ms timeout is initiated.
+   If the input events landed inside the dispatch-to-content event region for the layer, the events are left in the queue and a 400ms timeout is initiated.
    If the timeout expires before step 9 is completed, the APZ assumes the input block was not cancelled and the tentative target is correct, and processes them as part of step 10.
   </li>
  </ol>
 </li>
 <li value="5">
 The call stack unwinds back to APZCTreeManager::ReceiveInputEvent, which does an in-place modification of the input event so that any async transforms are removed.
 </li>
 <li value="6">
@@ -218,60 +218,61 @@ This code now has the event in the coord
 <li value="7">
 Gecko performs its own usual hit-testing and event dispatching for the event.
 As part of this, it records whether any touch listeners cancelled the input block by calling preventDefault().
 It also activates inactive scrollframes that were hit by the input events.
 </li>
 <li value="8">
 The call stack unwinds back to the widget code, which sends two notifications to the APZ code on the input thread.
 The first notification is via APZCTreeManager::ContentReceivedInputBlock, and informs the APZ whether the input block was cancelled.
-The second notification is via APZCTreeManager::SetTargetAPZC, and informs the APZ the results of the Gecko hit-test during event dispatch.
+The second notification is via APZCTreeManager::SetTargetAPZC, and informs the APZ of the results of the Gecko hit-test during event dispatch.
 Note that Gecko may report that the input event did not hit any scrollable frame at all.
-These notifications happen only once per input block.
+The SetTargetAPZC notification happens only once per input block, while the ContentReceivedInputBlock notification may happen once per block, or multiple times per block, depending on the input type.
 </li>
 <li value="9">
  <ol>
   <li value="i">
    If the events were processed as part of step 4(i), the notifications from step 8 are ignored and step 10 is skipped.
   </li>
   <li value="ii">
-   If events were queued as part of step 4(ii), and steps 5-8 take less than 300ms, the arrival of both notifications from step 8 will mark the input block ready for processing.
+   If events were queued as part of step 4(ii), and steps 5-8 take less than 400ms, the arrival of both notifications from step 8 will mark the input block ready for processing.
   </li>
   <li value="iii">
-   If events were queued as part of step 4(ii), but steps 5-8 take longer than 300ms, the notifications from step 8 will be ignored and step 10 will already have happened.
+   If events were queued as part of step 4(ii), but steps 5-8 take longer than 400ms, the notifications from step 8 will be ignored and step 10 will already have happened.
   </li>
  </ol>
 </li>
 <li value="10">
 If events were queued as part of step 4(ii) they are now either processed (if the input block was not cancelled and Gecko detected a scrollframe under the input event, or if the timeout expired) or dropped (all other cases).
 Note that the APZC that processes the events may be different at this step than the tentative target from step 3, depending on the SetTargetAPZC notification.
 Processing the events may trigger behaviours like scrolling or tap gestures.
 </li>
 </ol>
 
 If the CSS touch-action property is enabled, the above steps are modified as follows:
 <ul>
 <li>
- In step 4, the APZC also requires the allowed touch-action behaviours for the input event. This is not available yet, so the events are always queued.
+ In step 4, the APZC also requires the allowed touch-action behaviours for the input event.
+ This might have been determined as part of the hit-test in APZCTreeManager; if not, the events are queued.
 </li>
 <li>
  In step 6, the widget code determines the content element at the point under the input element, and notifies the APZ code of the allowed touch-action behaviours.
  This notification is sent via a call to APZCTreeManager::SetAllowedTouchBehavior on the input thread.
 </li>
 <li>
  In step 9(ii), the input block will only be marked ready for processing once all three notifications arrive.
 </li>
 </ul>
 
 #### Threading considerations
 
 The bulk of the input processing in the APZ code happens on what we call "the input thread".
 In practice the input thread could be the Gecko main thread, the compositor thread, or some other thread.
 There are obvious downsides to using the Gecko main thread - that is, "asynchronous" panning and zooming is not really asynchronous as input events can only be processed while Gecko is idle.
-However, this is the current state of things on B2G.
+In an e10s environment, using the Gecko main thread of the chrome process is acceptable, because the code running in that process is more controllable and well-behaved than arbitrary web content.
 Using the compositor thread as the input thread could work on some platforms, but may be inefficient on others.
 For example, on Android (Fennec) we receive input events from the system on a dedicated UI thread.
 We would have to redispatch the input events to the compositor thread if we wanted to the input thread to be the same as the compositor thread.
 This introduces a potential for higher latency, particularly if the compositor does any blocking operations - blocking SwapBuffers operations, for example.
 As a result, the APZ code itself does not assume that the input thread will be the same as the Gecko main thread or the compositor thread.
 
 #### Active vs. inactive scrollframes
 
@@ -284,15 +285,15 @@ Consider a page with a scrollframe that 
 When layout generates the layers for this page, the content of the scrollframe will be flattened into some other PaintedLayer (call it P).
 The layout code also adds the area (or bounding region in case of weird shapes) of the scrollframe to the dispatch-to-content region of P.
 
 When the user starts interacting with that content, the hit-test in the APZ code finds the dispatch-to-content region of P.
 The input block therefore has a tentative target of P when it goes into step 4(ii) in the flow above.
 When gecko processes the input event, it must detect the inactive scrollframe and activate it, as part of step 7.
 Finally, the widget code sends the SetTargetAPZC notification in step 8 to notify the APZ that the input block should really apply to this new layer.
 The issue here is that the layer transaction containing the new layer must reach the compositor and APZ before the SetTargetAPZC notification.
-If this does not occur within the 300ms timeout, the APZ code will be unable to update the tentative target, and will continue to use P for that input block.
+If this does not occur within the 400ms timeout, the APZ code will be unable to update the tentative target, and will continue to use P for that input block.
 Input blocks that start after the layer transaction will get correctly routed to the new layer as there will now be a layer and APZC instance for the active scrollframe.
 
 This model implies that when the user initially attempts to scroll an inactive scrollframe, it may end up scrolling an ancestor scrollframe.
 (This is because in the absence of the SetTargetAPZC notification, the input events will get applied to the closest ancestor scrollframe's APZC.)
 Only after the round-trip to the gecko thread is complete is there a layer for async scrolling to actually occur on the scrollframe itself.
 At that point the scrollframe will start receiving new input blocks and will scroll normally.
--- a/gfx/layers/GrallocImages.cpp
+++ b/gfx/layers/GrallocImages.cpp
@@ -68,17 +68,17 @@ GrallocImage::SetData(const Data& aData)
   mData = aData;
   mSize = aData.mPicSize;
 
   if (gfxPlatform::GetPlatform()->IsInGonkEmulator()) {
     // Emulator does not support HAL_PIXEL_FORMAT_YV12.
     return false;
   }
 
-  ClientIPCAllocator* allocator = ImageBridgeChild::GetSingleton();
+  RefPtr<ClientIPCAllocator> allocator = ImageBridgeChild::GetSingleton();
   GrallocTextureData* texData = GrallocTextureData::Create(mData.mYSize, HAL_PIXEL_FORMAT_YV12,
                                                            gfx::BackendType::NONE,
                                                            GraphicBuffer::USAGE_SW_READ_OFTEN |
                                                              GraphicBuffer::USAGE_SW_WRITE_OFTEN |
                                                              GraphicBuffer::USAGE_HW_TEXTURE,
                                                            allocator
   );
 
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -1054,17 +1054,17 @@ APZCTreeManager::ProcessTouchInput(Multi
 
   return result;
 }
 
 void
 APZCTreeManager::UpdateWheelTransaction(LayoutDeviceIntPoint aRefPoint,
                                         EventMessage aEventMessage)
 {
-  WheelBlockState* txn = mInputQueue->GetCurrentWheelTransaction();
+  WheelBlockState* txn = mInputQueue->GetActiveWheelTransaction();
   if (!txn) {
     return;
   }
 
   // If the transaction has simply timed out, we don't need to do anything
   // else.
   if (txn->MaybeTimeout(TimeStamp::Now())) {
     return;
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -1082,25 +1082,27 @@ nsEventStatus AsyncPanZoomController::On
 
   switch (mState) {
     case FLING:
     case ANIMATING_ZOOM:
     case SMOOTH_SCROLL:
     case OVERSCROLL_ANIMATION:
     case WHEEL_SCROLL:
     case PAN_MOMENTUM:
-      CurrentTouchBlock()->GetOverscrollHandoffChain()->CancelAnimations(ExcludeOverscroll);
+      MOZ_ASSERT(GetCurrentTouchBlock());
+      GetCurrentTouchBlock()->GetOverscrollHandoffChain()->CancelAnimations(ExcludeOverscroll);
       MOZ_FALLTHROUGH;
     case NOTHING: {
       mX.StartTouch(point.x, aEvent.mTime);
       mY.StartTouch(point.y, aEvent.mTime);
       if (RefPtr<GeckoContentController> controller = GetGeckoContentController()) {
+        MOZ_ASSERT(GetCurrentTouchBlock());
         controller->NotifyAPZStateChange(
             GetGuid(), APZStateChange::eStartTouch,
-            CurrentTouchBlock()->GetOverscrollHandoffChain()->CanBePanned(this));
+            GetCurrentTouchBlock()->GetOverscrollHandoffChain()->CanBePanned(this));
       }
       SetState(TOUCHING);
       break;
     }
     case TOUCHING:
     case PANNING:
     case PANNING_LOCKED_X:
     case PANNING_LOCKED_Y:
@@ -1129,17 +1131,18 @@ nsEventStatus AsyncPanZoomController::On
     case TOUCHING: {
       ScreenCoord panThreshold = GetTouchStartTolerance();
       UpdateWithTouchAtDevicePoint(aEvent);
 
       if (PanDistance() < panThreshold) {
         return nsEventStatus_eIgnore;
       }
 
-      if (gfxPrefs::TouchActionEnabled() && CurrentTouchBlock()->TouchActionAllowsPanningXY()) {
+      MOZ_ASSERT(GetCurrentTouchBlock());
+      if (gfxPrefs::TouchActionEnabled() && GetCurrentTouchBlock()->TouchActionAllowsPanningXY()) {
         // User tries to trigger a touch behavior. If allowed touch behavior is vertical pan
         // + horizontal pan (touch-action value is equal to AUTO) we can return ConsumeNoDefault
         // status immediately to trigger cancel event further. It should happen independent of
         // the parent type (whether it is scrolling or not).
         StartPanning(aEvent);
         return nsEventStatus_eConsumeNoDefault;
       }
 
@@ -1201,43 +1204,45 @@ nsEventStatus AsyncPanZoomController::On
     // second tap. Ignore if this happens.
     return nsEventStatus_eIgnore;
 
   case TOUCHING:
     // We may have some velocity stored on the axis from move events
     // that were not big enough to trigger scrolling. Clear that out.
     mX.SetVelocity(0);
     mY.SetVelocity(0);
+    MOZ_ASSERT(GetCurrentTouchBlock());
     APZC_LOG("%p still has %u touch points active\n", this,
-        CurrentTouchBlock()->GetActiveTouchCount());
+        GetCurrentTouchBlock()->GetActiveTouchCount());
     // In cases where the user is panning, then taps the second finger without
     // entering a pinch, we will arrive here when the second finger is lifted.
     // However the first finger is still down so we want to remain in state
     // TOUCHING.
-    if (CurrentTouchBlock()->GetActiveTouchCount() == 0) {
+    if (GetCurrentTouchBlock()->GetActiveTouchCount() == 0) {
       // It's possible we may be overscrolled if the user tapped during a
       // previous overscroll pan. Make sure to snap back in this situation.
       // An ancestor APZC could be overscrolled instead of this APZC, so
       // walk the handoff chain as well.
-      CurrentTouchBlock()->GetOverscrollHandoffChain()->SnapBackOverscrolledApzc(this);
+      GetCurrentTouchBlock()->GetOverscrollHandoffChain()->SnapBackOverscrolledApzc(this);
       // SnapBackOverscrolledApzc() will put any APZC it causes to snap back
       // into the OVERSCROLL_ANIMATION state. If that's not us, since we're
       // done TOUCHING enter the NOTHING state.
       if (mState != OVERSCROLL_ANIMATION) {
         SetState(NOTHING);
       }
     }
     return nsEventStatus_eIgnore;
 
   case PANNING:
   case PANNING_LOCKED_X:
   case PANNING_LOCKED_Y:
   case PAN_MOMENTUM:
   {
-    CurrentTouchBlock()->GetOverscrollHandoffChain()->FlushRepaints();
+    MOZ_ASSERT(GetCurrentTouchBlock());
+    GetCurrentTouchBlock()->GetOverscrollHandoffChain()->FlushRepaints();
     mX.EndTouch(aEvent.mTime);
     mY.EndTouch(aEvent.mTime);
     ParentLayerPoint flingVelocity = GetVelocityVector();
     // Clear our velocities; if DispatchFling() gives the fling to us,
     // the fling velocity gets *added* to our existing velocity in
     // AcceptFling().
     mX.SetVelocity(0);
     mY.SetVelocity(0);
@@ -1255,19 +1260,19 @@ nsEventStatus AsyncPanZoomController::On
       return nsEventStatus_eConsumeNoDefault;
     }
 
     // Make a local copy of the tree manager pointer and check that it's not
     // null before calling DispatchFling(). This is necessary because Destroy(),
     // which nulls out mTreeManager, could be called concurrently.
     if (APZCTreeManager* treeManagerLocal = GetApzcTreeManager()) {
       FlingHandoffState handoffState{flingVelocity,
-                                     CurrentTouchBlock()->GetOverscrollHandoffChain(),
+                                     GetCurrentTouchBlock()->GetOverscrollHandoffChain(),
                                      false /* not handoff */,
-                                     CurrentTouchBlock()->GetScrolledApzc()};
+                                     GetCurrentTouchBlock()->GetScrolledApzc()};
       treeManagerLocal->DispatchFling(this, handoffState);
     }
     return nsEventStatus_eConsumeNoDefault;
   }
   case PINCHING:
     SetState(NOTHING);
     // Scale gesture listener should have handled this.
     NS_WARNING("Gesture listener should have handled pinching in OnTouchEnd.");
@@ -1293,32 +1298,32 @@ nsEventStatus AsyncPanZoomController::On
 }
 
 nsEventStatus AsyncPanZoomController::OnScaleBegin(const PinchGestureInput& aEvent) {
   APZC_LOG("%p got a scale-begin in state %d\n", this, mState);
 
   mPinchPaintTimerSet = false;
   // Note that there may not be a touch block at this point, if we received the
   // PinchGestureEvent directly from widget code without any touch events.
-  if (HasReadyTouchBlock() && !CurrentTouchBlock()->TouchActionAllowsPinchZoom()) {
+  if (HasReadyTouchBlock() && !GetCurrentTouchBlock()->TouchActionAllowsPinchZoom()) {
     return nsEventStatus_eIgnore;
   }
 
   SetState(PINCHING);
   mX.SetVelocity(0);
   mY.SetVelocity(0);
   mLastZoomFocus = aEvent.mLocalFocusPoint - mFrameMetrics.GetCompositionBounds().TopLeft();
 
   return nsEventStatus_eConsumeNoDefault;
 }
 
 nsEventStatus AsyncPanZoomController::OnScale(const PinchGestureInput& aEvent) {
   APZC_LOG("%p got a scale in state %d\n", this, mState);
-
-  if (HasReadyTouchBlock() && !CurrentTouchBlock()->TouchActionAllowsPinchZoom()) {
+ 
+  if (HasReadyTouchBlock() && !GetCurrentTouchBlock()->TouchActionAllowsPinchZoom()) {
     return nsEventStatus_eIgnore;
   }
 
   if (mState != PINCHING) {
     return nsEventStatus_eConsumeNoDefault;
   }
 
   // Only the root APZC is zoomable, and the root APZC is not allowed to have
@@ -1414,17 +1419,17 @@ nsEventStatus AsyncPanZoomController::On
   return nsEventStatus_eConsumeNoDefault;
 }
 
 nsEventStatus AsyncPanZoomController::OnScaleEnd(const PinchGestureInput& aEvent) {
   APZC_LOG("%p got a scale-end in state %d\n", this, mState);
 
   mPinchPaintTimerSet = false;
 
-  if (HasReadyTouchBlock() && !CurrentTouchBlock()->TouchActionAllowsPinchZoom()) {
+  if (HasReadyTouchBlock() && !GetCurrentTouchBlock()->TouchActionAllowsPinchZoom()) {
     return nsEventStatus_eIgnore;
   }
 
   SetState(NOTHING);
 
   {
     ReentrantMonitorAutoEnter lock(mMonitor);
     ScheduleComposite();
@@ -1446,17 +1451,17 @@ nsEventStatus AsyncPanZoomController::On
     // pinch if we go into overscroll with a two-finger pan, and then turn
     // that into a pinch by increasing the span sufficiently. In such a case,
     // there is no snap-back animation to get us out of overscroll, so we need
     // to get out of it somehow.
     // Moreover, in cases of scroll handoff, the overscroll can be on an APZC
     // further up in the handoff chain rather than on the current APZC, so
     // we need to clear overscroll along the entire handoff chain.
     if (HasReadyTouchBlock()) {
-      CurrentTouchBlock()->GetOverscrollHandoffChain()->ClearOverscroll();
+      GetCurrentTouchBlock()->GetOverscrollHandoffChain()->ClearOverscroll();
     } else {
       ClearOverscroll();
     }
     // Along with clearing the overscroll, we also want to snap to the nearest
     // snap point as appropriate.
     ScrollSnap();
   }
 
@@ -1620,17 +1625,17 @@ AsyncPanZoomController::CanScroll(Layer:
   }
 }
 
 bool
 AsyncPanZoomController::AllowScrollHandoffInCurrentBlock() const
 {
   bool result = mInputQueue->AllowScrollHandoff();
   if (!gfxPrefs::APZAllowImmediateHandoff()) {
-    if (InputBlockState* currentBlock = CurrentInputBlock()) {
+    if (InputBlockState* currentBlock = GetCurrentInputBlock()) {
       // Do not allow handoff beyond the first APZC to scroll.
       if (currentBlock->GetScrolledApzc() == this) {
         result = false;
       }
     }
   }
   return result;
 }
@@ -1665,17 +1670,17 @@ ScrollInputMethodForWheelDeltaType(Scrol
 
 nsEventStatus AsyncPanZoomController::OnScrollWheel(const ScrollWheelInput& aEvent)
 {
   ParentLayerPoint delta = GetScrollWheelDelta(aEvent);
   APZC_LOG("%p got a scroll-wheel with delta %s\n", this, Stringify(delta).c_str());
 
   if ((delta.x || delta.y) && !CanScrollWithWheel(delta)) {
     // We can't scroll this apz anymore, so we simply drop the event.
-    if (mInputQueue->GetCurrentWheelTransaction() &&
+    if (mInputQueue->GetActiveWheelTransaction() &&
         gfxPrefs::MouseScrollTestingEnabled()) {
       if (RefPtr<GeckoContentController> controller = GetGeckoContentController()) {
         controller->NotifyMozMouseScrollEvent(
           mFrameMetrics.GetScrollId(),
           NS_LITERAL_STRING("MozMouseScrollFailed"));
       }
     }
     return nsEventStatus_eConsumeNoDefault;
@@ -1699,18 +1704,19 @@ nsEventStatus AsyncPanZoomController::On
       CSSPoint startPosition = mFrameMetrics.GetScrollOffset();
       MaybeAdjustDeltaForScrollSnapping(aEvent, delta, startPosition);
 
       ScreenPoint distance = ToScreenCoordinates(
         ParentLayerPoint(fabs(delta.x), fabs(delta.y)), aEvent.mLocalOrigin);
 
       CancelAnimation();
 
+      MOZ_ASSERT(mInputQueue->GetCurrentWheelBlock());
       OverscrollHandoffState handoffState(
-          *mInputQueue->CurrentWheelBlock()->GetOverscrollHandoffChain(),
+          *mInputQueue->GetCurrentWheelBlock()->GetOverscrollHandoffChain(),
           distance,
           ScrollSource::Wheel);
       ParentLayerPoint startPoint = aEvent.mLocalOrigin;
       ParentLayerPoint endPoint = aEvent.mLocalOrigin - delta;
       CallDispatchScroll(startPoint, endPoint, handoffState);
 
       SetState(NOTHING);
 
@@ -1789,17 +1795,18 @@ AsyncPanZoomController::NotifyMozMouseSc
   controller->NotifyMozMouseScrollEvent(mFrameMetrics.GetScrollId(), aString);
 }
 
 nsEventStatus AsyncPanZoomController::OnPanMayBegin(const PanGestureInput& aEvent) {
   APZC_LOG("%p got a pan-maybegin in state %d\n", this, mState);
 
   mX.StartTouch(aEvent.mLocalPanStartPoint.x, aEvent.mTime);
   mY.StartTouch(aEvent.mLocalPanStartPoint.y, aEvent.mTime);
-  CurrentPanGestureBlock()->GetOverscrollHandoffChain()->CancelAnimations();
+  MOZ_ASSERT(GetCurrentPanGestureBlock());
+  GetCurrentPanGestureBlock()->GetOverscrollHandoffChain()->CancelAnimations();
 
   return nsEventStatus_eConsumeNoDefault;
 }
 
 nsEventStatus AsyncPanZoomController::OnPanCancelled(const PanGestureInput& aEvent) {
   APZC_LOG("%p got a pan-cancelled in state %d\n", this, mState);
 
   mX.CancelGesture();
@@ -1886,18 +1893,19 @@ nsEventStatus AsyncPanZoomController::On
   mY.UpdateWithTouchAtDevicePoint(aEvent.mLocalPanStartPoint.y, logicalPanDisplacement.y, aEvent.mTime);
 
   HandlePanningUpdate(physicalPanDisplacement);
 
   mozilla::Telemetry::Accumulate(mozilla::Telemetry::SCROLL_INPUT_METHODS,
       (uint32_t) ScrollInputMethod::ApzPanGesture);
 
   ScreenPoint panDistance(fabs(physicalPanDisplacement.x), fabs(physicalPanDisplacement.y));
+  MOZ_ASSERT(GetCurrentPanGestureBlock());
   OverscrollHandoffState handoffState(
-      *CurrentPanGestureBlock()->GetOverscrollHandoffChain(),
+      *GetCurrentPanGestureBlock()->GetOverscrollHandoffChain(),
       panDistance,
       ScrollSource::Wheel);
 
   // Create fake "touch" positions that will result in the desired scroll motion.
   // Note that the pan displacement describes the change in scroll position:
   // positive displacement values mean that the scroll position increases.
   // However, an increase in scroll position means that the scrolled contents
   // are moved to the left / upwards. Since our simulated "touches" determine
@@ -1917,18 +1925,19 @@ nsEventStatus AsyncPanZoomController::On
   OnPan(aEvent, true);
 
   mX.EndTouch(aEvent.mTime);
   mY.EndTouch(aEvent.mTime);
 
   // Drop any velocity on axes where we don't have room to scroll anyways
   // (in this APZC, or an APZC further in the handoff chain).
   // This ensures that we don't enlarge the display port unnecessarily.
+  MOZ_ASSERT(GetCurrentPanGestureBlock());
   RefPtr<const OverscrollHandoffChain> overscrollHandoffChain =
-    CurrentPanGestureBlock()->GetOverscrollHandoffChain();
+    GetCurrentPanGestureBlock()->GetOverscrollHandoffChain();
   if (!overscrollHandoffChain->CanScrollInDirection(this, Layer::HORIZONTAL)) {
     mX.SetVelocity(0);
   }
   if (!overscrollHandoffChain->CanScrollInDirection(this, Layer::VERTICAL)) {
     mY.SetVelocity(0);
   }
 
   SetState(NOTHING);
@@ -1977,23 +1986,22 @@ nsEventStatus AsyncPanZoomController::On
 }
 
 nsEventStatus AsyncPanZoomController::OnLongPress(const TapGestureInput& aEvent) {
   APZC_LOG("%p got a long-press in state %d\n", this, mState);
   RefPtr<GeckoContentController> controller = GetGeckoContentController();
   if (controller) {
     LayoutDevicePoint geckoScreenPoint;
     if (ConvertToGecko(aEvent.mPoint, &geckoScreenPoint)) {
-      CancelableBlockState* block = CurrentInputBlock();
-      MOZ_ASSERT(block);
-      if (!block->AsTouchBlock()) {
+      TouchBlockState* touch = GetCurrentTouchBlock();
+      if (!touch) {
         APZC_LOG("%p dropping long-press because some non-touch block interrupted it\n", this);
         return nsEventStatus_eIgnore;
       }
-      if (block->AsTouchBlock()->IsDuringFastFling()) {
+      if (touch->IsDuringFastFling()) {
         APZC_LOG("%p dropping long-press because of fast fling\n", this);
         return nsEventStatus_eIgnore;
       }
       uint64_t blockId = GetInputQueue()->InjectNewTouchBlock(this);
       controller->HandleTap(TapType::eLongTap, geckoScreenPoint, aEvent.modifiers, GetGuid(), blockId);
       return nsEventStatus_eConsumeNoDefault;
     }
   }
@@ -2006,20 +2014,18 @@ nsEventStatus AsyncPanZoomController::On
 }
 
 nsEventStatus AsyncPanZoomController::GenerateSingleTap(TapType aType,
       const ScreenIntPoint& aPoint, mozilla::Modifiers aModifiers) {
   RefPtr<GeckoContentController> controller = GetGeckoContentController();
   if (controller) {
     LayoutDevicePoint geckoScreenPoint;
     if (ConvertToGecko(aPoint, &geckoScreenPoint)) {
-      CancelableBlockState* block = CurrentInputBlock();
-      MOZ_ASSERT(block);
-      TouchBlockState* touch = block->AsTouchBlock();
-      // |block| may be a non-touch block in the case where this function is
+      TouchBlockState* touch = GetCurrentTouchBlock();
+      // |touch| may be null in the case where this function is
       // invoked by GestureEventListener on a timeout. In that case we already
       // verified that the single tap is allowed so we let it through.
       // XXX there is a bug here that in such a case the touch block that
       // generated this tap will not get its mSingleTapOccurred flag set.
       // See https://bugzilla.mozilla.org/show_bug.cgi?id=1256344#c6
       if (touch) {
         if (touch->IsDuringFastFling()) {
           APZC_LOG("%p dropping single-tap because it was during a fast-fling\n", this);
@@ -2044,45 +2050,48 @@ nsEventStatus AsyncPanZoomController::Ge
       return nsEventStatus_eConsumeNoDefault;
     }
   }
   return nsEventStatus_eIgnore;
 }
 
 void AsyncPanZoomController::OnTouchEndOrCancel() {
   if (RefPtr<GeckoContentController> controller = GetGeckoContentController()) {
+    MOZ_ASSERT(GetCurrentTouchBlock());
     controller->NotifyAPZStateChange(
-        GetGuid(), APZStateChange::eEndTouch, CurrentTouchBlock()->SingleTapOccurred());
+        GetGuid(), APZStateChange::eEndTouch, GetCurrentTouchBlock()->SingleTapOccurred());
   }
 }
 
 nsEventStatus AsyncPanZoomController::OnSingleTapUp(const TapGestureInput& aEvent) {
   APZC_LOG("%p got a single-tap-up in state %d\n", this, mState);
   // If mZoomConstraints.mAllowDoubleTapZoom is true we wait for a call to OnSingleTapConfirmed before
   // sending event to content
-  if (!(mZoomConstraints.mAllowDoubleTapZoom && CurrentTouchBlock()->TouchActionAllowsDoubleTapZoom())) {
+  MOZ_ASSERT(GetCurrentTouchBlock());
+  if (!(mZoomConstraints.mAllowDoubleTapZoom && GetCurrentTouchBlock()->TouchActionAllowsDoubleTapZoom())) {
     return GenerateSingleTap(TapType::eSingleTap, aEvent.mPoint, aEvent.modifiers);
   }
   return nsEventStatus_eIgnore;
 }
 
 nsEventStatus AsyncPanZoomController::OnSingleTapConfirmed(const TapGestureInput& aEvent) {
   APZC_LOG("%p got a single-tap-confirmed in state %d\n", this, mState);
   return GenerateSingleTap(TapType::eSingleTap, aEvent.mPoint, aEvent.modifiers);
 }
 
 nsEventStatus AsyncPanZoomController::OnDoubleTap(const TapGestureInput& aEvent) {
   APZC_LOG("%p got a double-tap in state %d\n", this, mState);
   RefPtr<GeckoContentController> controller = GetGeckoContentController();
   if (controller) {
-    if (mZoomConstraints.mAllowDoubleTapZoom && CurrentTouchBlock()->TouchActionAllowsDoubleTapZoom()) {
+    MOZ_ASSERT(GetCurrentTouchBlock());
+    if (mZoomConstraints.mAllowDoubleTapZoom && GetCurrentTouchBlock()->TouchActionAllowsDoubleTapZoom()) {
       LayoutDevicePoint geckoScreenPoint;
       if (ConvertToGecko(aEvent.mPoint, &geckoScreenPoint)) {
         controller->HandleTap(TapType::eDoubleTap, geckoScreenPoint,
-            aEvent.modifiers, GetGuid(), CurrentTouchBlock()->GetBlockId());
+            aEvent.modifiers, GetGuid(), GetCurrentTouchBlock()->GetBlockId());
       }
     }
     return nsEventStatus_eConsumeNoDefault;
   }
   return nsEventStatus_eIgnore;
 }
 
 nsEventStatus AsyncPanZoomController::OnCancelTap(const TapGestureInput& aEvent) {
@@ -2148,45 +2157,46 @@ const ParentLayerPoint AsyncPanZoomContr
 void AsyncPanZoomController::SetVelocityVector(const ParentLayerPoint& aVelocityVector) {
   mX.SetVelocity(aVelocityVector.x);
   mY.SetVelocity(aVelocityVector.y);
 }
 
 void AsyncPanZoomController::HandlePanningWithTouchAction(double aAngle) {
   // Handling of cross sliding will need to be added in this method after touch-action released
   // enabled by default.
-  if (CurrentTouchBlock()->TouchActionAllowsPanningXY()) {
+  MOZ_ASSERT(GetCurrentTouchBlock());
+  if (GetCurrentTouchBlock()->TouchActionAllowsPanningXY()) {
     if (mX.CanScrollNow() && mY.CanScrollNow()) {
       if (IsCloseToHorizontal(aAngle, gfxPrefs::APZAxisLockAngle())) {
         mY.SetAxisLocked(true);
         SetState(PANNING_LOCKED_X);
       } else if (IsCloseToVertical(aAngle, gfxPrefs::APZAxisLockAngle())) {
         mX.SetAxisLocked(true);
         SetState(PANNING_LOCKED_Y);
       } else {
         SetState(PANNING);
       }
     } else if (mX.CanScrollNow() || mY.CanScrollNow()) {
       SetState(PANNING);
     } else {
       SetState(NOTHING);
     }
-  } else if (CurrentTouchBlock()->TouchActionAllowsPanningX()) {
+  } else if (GetCurrentTouchBlock()->TouchActionAllowsPanningX()) {
     // Using bigger angle for panning to keep behavior consistent
     // with IE.
     if (IsCloseToHorizontal(aAngle, gfxPrefs::APZAllowedDirectPanAngle())) {
       mY.SetAxisLocked(true);
       SetState(PANNING_LOCKED_X);
       mPanDirRestricted = true;
     } else {
       // Don't treat these touches as pan/zoom movements since 'touch-action' value
       // requires it.
       SetState(NOTHING);
     }
-  } else if (CurrentTouchBlock()->TouchActionAllowsPanningY()) {
+  } else if (GetCurrentTouchBlock()->TouchActionAllowsPanningY()) {
     if (IsCloseToVertical(aAngle, gfxPrefs::APZAllowedDirectPanAngle())) {
       mX.SetAxisLocked(true);
       SetState(PANNING_LOCKED_Y);
       mPanDirRestricted = true;
     } else {
       SetState(NOTHING);
     }
   } else {
@@ -2198,18 +2208,19 @@ void AsyncPanZoomController::HandlePanni
     // touchmoves.
     mX.SetVelocity(0);
     mY.SetVelocity(0);
   }
 }
 
 void AsyncPanZoomController::HandlePanning(double aAngle) {
   ReentrantMonitorAutoEnter lock(mMonitor);
+  MOZ_ASSERT(GetCurrentInputBlock());
   RefPtr<const OverscrollHandoffChain> overscrollHandoffChain =
-    CurrentInputBlock()->GetOverscrollHandoffChain();
+    GetCurrentInputBlock()->GetOverscrollHandoffChain();
   bool canScrollHorizontal = !mX.IsAxisLocked() &&
     overscrollHandoffChain->CanScrollInDirection(this, Layer::HORIZONTAL);
   bool canScrollVertical = !mY.IsAxisLocked() &&
     overscrollHandoffChain->CanScrollInDirection(this, Layer::VERTICAL);
 
   if (!canScrollHorizontal || !canScrollVertical) {
     SetState(PANNING);
   } else if (IsCloseToHorizontal(aAngle, gfxPrefs::APZAxisLockAngle())) {
@@ -2300,17 +2311,17 @@ bool AsyncPanZoomController::AttemptScro
   ParentLayerPoint overscroll;  // will be used outside monitor block
 
   // If the direction of panning is reversed within the same input block,
   // a later event in the block could potentially scroll an APZC earlier
   // in the handoff chain, than an earlier event in the block (because
   // the earlier APZC was scrolled to its extent in the original direction).
   // We want to disallow this.
   bool scrollThisApzc = false;
-  if (InputBlockState* block = CurrentInputBlock()) {
+  if (InputBlockState* block = GetCurrentInputBlock()) {
     scrollThisApzc = !block->GetScrolledApzc() || block->IsDownchainOfScrolledApzc(this);
   }
 
   if (scrollThisApzc) {
     ReentrantMonitorAutoEnter lock(mMonitor);
 
     ParentLayerPoint adjustedDisplacement;
     bool forceVerticalOverscroll =
@@ -2321,17 +2332,17 @@ bool AsyncPanZoomController::AttemptScro
     bool xChanged = mX.AdjustDisplacement(displacement.x, adjustedDisplacement.x, overscroll.x);
 
     if (xChanged || yChanged) {
       ScheduleComposite();
     }
 
     if (!IsZero(adjustedDisplacement)) {
       ScrollBy(adjustedDisplacement / mFrameMetrics.GetZoom());
-      if (CancelableBlockState* block = CurrentInputBlock()) {
+      if (CancelableBlockState* block = GetCurrentInputBlock()) {
         if (block->AsTouchBlock() && (block->GetScrolledApzc() != this)) {
           RefPtr<GeckoContentController> controller = GetGeckoContentController();
           if (controller) {
             controller->SetScrollingRootContent(IsRootContent());
           }
         }
         block->SetScrolledApzc(this);
       }
@@ -2540,18 +2551,19 @@ void AsyncPanZoomController::TrackTouch(
       PanStart());
   HandlePanningUpdate(panDistance);
 
   UpdateWithTouchAtDevicePoint(aEvent);
 
   if (prevTouchPoint != touchPoint) {
     mozilla::Telemetry::Accumulate(mozilla::Telemetry::SCROLL_INPUT_METHODS,
         (uint32_t) ScrollInputMethod::ApzTouch);
+    MOZ_ASSERT(GetCurrentTouchBlock());
     OverscrollHandoffState handoffState(
-        *CurrentTouchBlock()->GetOverscrollHandoffChain(),
+        *GetCurrentTouchBlock()->GetOverscrollHandoffChain(),
         panDistance,
         ScrollSource::Touch);
     CallDispatchScroll(prevTouchPoint, touchPoint, handoffState);
   }
 }
 
 ParentLayerPoint AsyncPanZoomController::GetFirstTouchPoint(const MultiTouchInput& aEvent) {
   return ((SingleTouchData&)aEvent.mTouches[0]).mLocalScreenPoint;
@@ -3606,45 +3618,45 @@ void AsyncPanZoomController::ZoomToRect(
       controller->DispatchToRepaintThread(
           NewRunnableMethod<FrameMetrics, ParentLayerPoint>(
               this, func, endZoomToMetrics, velocity));
     }
   }
 }
 
 CancelableBlockState*
-AsyncPanZoomController::CurrentInputBlock() const
+AsyncPanZoomController::GetCurrentInputBlock() const
 {
-  return GetInputQueue()->CurrentBlock();
+  return GetInputQueue()->GetCurrentBlock();
 }
 
 TouchBlockState*
-AsyncPanZoomController::CurrentTouchBlock() const
+AsyncPanZoomController::GetCurrentTouchBlock() const
 {
-  return GetInputQueue()->CurrentTouchBlock();
+  return GetInputQueue()->GetCurrentTouchBlock();
 }
 
 PanGestureBlockState*
-AsyncPanZoomController::CurrentPanGestureBlock() const
+AsyncPanZoomController::GetCurrentPanGestureBlock() const
 {
-  return GetInputQueue()->CurrentPanGestureBlock();
+  return GetInputQueue()->GetCurrentPanGestureBlock();
 }
 
 void
 AsyncPanZoomController::ResetTouchInputState()
 {
   MultiTouchInput cancel(MultiTouchInput::MULTITOUCH_CANCEL, 0, TimeStamp::Now(), 0);
   RefPtr<GestureEventListener> listener = GetGestureEventListener();
   if (listener) {
     listener->HandleInputEvent(cancel);
   }
   CancelAnimationAndGestureState();
   // Clear overscroll along the entire handoff chain, in case an APZC
   // later in the chain is overscrolled.
-  if (TouchBlockState* block = CurrentInputBlock()->AsTouchBlock()) {
+  if (TouchBlockState* block = GetCurrentTouchBlock()) {
     block->GetOverscrollHandoffChain()->ClearOverscroll();
   }
 }
 
 void
 AsyncPanZoomController::CancelAnimationAndGestureState()
 {
   mX.CancelGesture();
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -856,21 +856,21 @@ public:
    * Gets a ref to the input queue that is shared across the entire tree manager.
    */
   const RefPtr<InputQueue>& GetInputQueue() const;
 
 private:
   void CancelAnimationAndGestureState();
 
   RefPtr<InputQueue> mInputQueue;
-  CancelableBlockState* CurrentInputBlock() const;
-  TouchBlockState* CurrentTouchBlock() const;
+  CancelableBlockState* GetCurrentInputBlock() const;
+  TouchBlockState* GetCurrentTouchBlock() const;
   bool HasReadyTouchBlock() const;
 
-  PanGestureBlockState* CurrentPanGestureBlock() const;
+  PanGestureBlockState* GetCurrentPanGestureBlock() const;
 
 private:
   /* ===================================================================
    * The functions and members in this section are used to manage
    * fling animations, smooth scroll animations, and overscroll
    * during a fling or smooth scroll.
    */
 public:
--- a/gfx/layers/apz/src/GestureEventListener.cpp
+++ b/gfx/layers/apz/src/GestureEventListener.cpp
@@ -521,17 +521,18 @@ void GestureEventListener::CancelMaxTapT
     mMaxTapTimeoutTask = nullptr;
   }
 }
 
 void GestureEventListener::CreateMaxTapTimeoutTask()
 {
   mLastTapInput = mLastTouchInput;
 
-  TouchBlockState* block = mAsyncPanZoomController->GetInputQueue()->CurrentTouchBlock();
+  TouchBlockState* block = mAsyncPanZoomController->GetInputQueue()->GetCurrentTouchBlock();
+  MOZ_ASSERT(block);
   RefPtr<CancelableRunnable> task =
     NewCancelableRunnableMethod<bool>(this,
                                       &GestureEventListener::HandleInputTimeoutMaxTap,
                                       block->IsDuringFastFling());
 
   mMaxTapTimeoutTask = task;
   mAsyncPanZoomController->PostDelayedTask(
     task.forget(),
--- a/gfx/layers/apz/src/InputBlockState.cpp
+++ b/gfx/layers/apz/src/InputBlockState.cpp
@@ -8,16 +8,17 @@
 #include "AsyncPanZoomController.h"         // for AsyncPanZoomController
 #include "AsyncScrollBase.h"                // for kScrollSeriesTimeoutMs
 #include "gfxPrefs.h"                       // for gfxPrefs
 #include "mozilla/MouseEvents.h"
 #include "mozilla/SizePrintfMacros.h"       // for PRIuSIZE
 #include "mozilla/Telemetry.h"              // for Telemetry
 #include "mozilla/layers/APZCTreeManager.h" // for AllowedTouchBehavior
 #include "OverscrollHandoffState.h"
+#include "QueuedInput.h"
 
 #define TBS_LOG(...)
 // #define TBS_LOG(...) printf_stderr("TBS: " __VA_ARGS__)
 
 namespace mozilla {
 namespace layers {
 
 static uint64_t sBlockCounter = InputBlockState::NO_BLOCK_ID + 1;
@@ -32,17 +33,18 @@ InputBlockState::InputBlockState(const R
 {
   // We should never be constructed with a nullptr target.
   MOZ_ASSERT(mTargetApzc);
   mOverscrollHandoffChain = mTargetApzc->BuildOverscrollHandoffChain();
 }
 
 bool
 InputBlockState::SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc,
-                                        TargetConfirmationState aState)
+                                        TargetConfirmationState aState,
+                                        InputData* aFirstInput)
 {
   MOZ_ASSERT(aState == TargetConfirmationState::eConfirmed
           || aState == TargetConfirmationState::eTimedOut);
 
   if (mTargetConfirmed == TargetConfirmationState::eTimedOut &&
       aState == TargetConfirmationState::eConfirmed) {
     // The main thread finally responded. We had already timed out the
     // confirmation, but we want to update the state internally so that we
@@ -219,24 +221,16 @@ CancelableBlockState::IsReadyForHandling
 {
   if (!IsTargetConfirmed()) {
     return false;
   }
   return mContentResponded || mContentResponseTimerExpired;
 }
 
 void
-CancelableBlockState::DispatchImmediate(const InputData& aEvent) const
-{
-  MOZ_ASSERT(!HasEvents());
-  MOZ_ASSERT(GetTargetApzc());
-  DispatchEvent(aEvent);
-}
-
-void
 CancelableBlockState::DispatchEvent(const InputData& aEvent) const
 {
   GetTargetApzc()->HandleInputEvent(aEvent, mTransformToApzc);
 }
 
 void
 CancelableBlockState::RecordContentResponseTime()
 {
@@ -289,46 +283,16 @@ DragBlockState::DispatchEvent(const Inpu
   MouseInput mouseInput = aEvent.AsMouseInput();
   if (!mouseInput.TransformToLocal(mTransformToApzc)) {
     return;
   }
 
   GetTargetApzc()->HandleDragEvent(mouseInput, mDragMetrics);
 }
 
-void
-DragBlockState::AddEvent(const MouseInput& aEvent)
-{
-  mEvents.AppendElement(aEvent);
-}
-
-bool
-DragBlockState::HasEvents() const
-{
-  return !mEvents.IsEmpty();
-}
-
-void
-DragBlockState::DropEvents()
-{
-  TBS_LOG("%p dropping %" PRIuSIZE " events\n", this, mEvents.Length());
-  mEvents.Clear();
-}
-
-void
-DragBlockState::HandleEvents()
-{
-  while (HasEvents()) {
-    TBS_LOG("%p returning first of %" PRIuSIZE " events\n", this, mEvents.Length());
-    MouseInput event = mEvents[0];
-    mEvents.RemoveElementAt(0);
-    DispatchEvent(event);
-  }
-}
-
 bool
 DragBlockState::MustStayActive()
 {
   return !mReceivedMouseUp;
 }
 
 const char*
 DragBlockState::Type()
@@ -374,28 +338,28 @@ WheelBlockState::SetContentResponse(bool
   if (aPreventDefault) {
     EndTransaction();
   }
   return CancelableBlockState::SetContentResponse(aPreventDefault);
 }
 
 bool
 WheelBlockState::SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc,
-                                        TargetConfirmationState aState)
+                                        TargetConfirmationState aState,
+                                        InputData* aFirstInput)
 {
   // The APZC that we find via APZCCallbackHelpers may not be the same APZC
   // ESM or OverscrollHandoff would have computed. Make sure we get the right
   // one by looking for the first apzc the next pending event can scroll.
   RefPtr<AsyncPanZoomController> apzc = aTargetApzc;
-  if (apzc && mEvents.Length() > 0) {
-    const ScrollWheelInput& event = mEvents.ElementAt(0);
-    apzc = apzc->BuildOverscrollHandoffChain()->FindFirstScrollable(event);
+  if (apzc && aFirstInput) {
+    apzc = apzc->BuildOverscrollHandoffChain()->FindFirstScrollable(*aFirstInput);
   }
 
-  InputBlockState::SetConfirmedTargetApzc(apzc, aState);
+  InputBlockState::SetConfirmedTargetApzc(apzc, aState, aFirstInput);
   return true;
 }
 
 void
 WheelBlockState::Update(ScrollWheelInput& aEvent)
 {
   // We might not be in a transaction if the block never started in a
   // transaction - for example, if nothing was scrollable.
@@ -428,46 +392,16 @@ WheelBlockState::Update(ScrollWheelInput
 
   // Update the time of the last known good event, and reset the mouse move
   // time to null. This will reset the delays on both the general transaction
   // timeout and the mouse-move-in-frame timeout.
   mLastEventTime = aEvent.mTimeStamp;
   mLastMouseMove = TimeStamp();
 }
 
-void
-WheelBlockState::AddEvent(const ScrollWheelInput& aEvent)
-{
-  mEvents.AppendElement(aEvent);
-}
-
-bool
-WheelBlockState::HasEvents() const
-{
-  return !mEvents.IsEmpty();
-}
-
-void
-WheelBlockState::DropEvents()
-{
-  TBS_LOG("%p dropping %" PRIuSIZE " events\n", this, mEvents.Length());
-  mEvents.Clear();
-}
-
-void
-WheelBlockState::HandleEvents()
-{
-  while (HasEvents()) {
-    TBS_LOG("%p returning first of %" PRIuSIZE " events\n", this, mEvents.Length());
-    ScrollWheelInput event = mEvents[0];
-    mEvents.RemoveElementAt(0);
-    DispatchEvent(event);
-  }
-}
-
 bool
 WheelBlockState::MustStayActive()
 {
   return !mTransactionEnded;
 }
 
 const char*
 WheelBlockState::Type()
@@ -619,65 +553,35 @@ PanGestureBlockState::PanGestureBlockSta
     if (apzc && apzc != GetTargetApzc()) {
       UpdateTargetApzc(apzc);
     }
   }
 }
 
 bool
 PanGestureBlockState::SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc,
-                                             TargetConfirmationState aState)
+                                             TargetConfirmationState aState,
+                                             InputData* aFirstInput)
 {
   // The APZC that we find via APZCCallbackHelpers may not be the same APZC
   // ESM or OverscrollHandoff would have computed. Make sure we get the right
   // one by looking for the first apzc the next pending event can scroll.
   RefPtr<AsyncPanZoomController> apzc = aTargetApzc;
-  if (apzc && mEvents.Length() > 0) {
-    const PanGestureInput& event = mEvents.ElementAt(0);
+  if (apzc && aFirstInput) {
     RefPtr<AsyncPanZoomController> scrollableApzc =
-      apzc->BuildOverscrollHandoffChain()->FindFirstScrollable(event);
+      apzc->BuildOverscrollHandoffChain()->FindFirstScrollable(*aFirstInput);
     if (scrollableApzc) {
       apzc = scrollableApzc;
     }
   }
 
-  InputBlockState::SetConfirmedTargetApzc(apzc, aState);
+  InputBlockState::SetConfirmedTargetApzc(apzc, aState, aFirstInput);
   return true;
 }
 
-void
-PanGestureBlockState::AddEvent(const PanGestureInput& aEvent)
-{
-  mEvents.AppendElement(aEvent);
-}
-
-bool
-PanGestureBlockState::HasEvents() const
-{
-  return !mEvents.IsEmpty();
-}
-
-void
-PanGestureBlockState::DropEvents()
-{
-  TBS_LOG("%p dropping %" PRIuSIZE " events\n", this, mEvents.Length());
-  mEvents.Clear();
-}
-
-void
-PanGestureBlockState::HandleEvents()
-{
-  while (HasEvents()) {
-    TBS_LOG("%p returning first of %" PRIuSIZE " events\n", this, mEvents.Length());
-    PanGestureInput event = mEvents[0];
-    mEvents.RemoveElementAt(0);
-    DispatchEvent(event);
-  }
-}
-
 bool
 PanGestureBlockState::MustStayActive()
 {
   return !mInterrupted;
 }
 
 const char*
 PanGestureBlockState::Type()
@@ -827,59 +731,28 @@ TouchBlockState::SetSingleTapOccurred()
 
 bool
 TouchBlockState::SingleTapOccurred() const
 {
   return mSingleTapOccurred;
 }
 
 bool
-TouchBlockState::HasEvents() const
-{
-  return !mEvents.IsEmpty();
-}
-
-void
-TouchBlockState::AddEvent(const MultiTouchInput& aEvent)
-{
-  TBS_LOG("%p adding event of type %d\n", this, aEvent.mType);
-  mEvents.AppendElement(aEvent);
-}
-
-bool
 TouchBlockState::MustStayActive()
 {
   return true;
 }
 
 const char*
 TouchBlockState::Type()
 {
   return "touch";
 }
 
 void
-TouchBlockState::DropEvents()
-{
-  TBS_LOG("%p dropping %" PRIuSIZE " events\n", this, mEvents.Length());
-  mEvents.Clear();
-}
-
-void
-TouchBlockState::HandleEvents()
-{
-  while (HasEvents()) {
-    TBS_LOG("%p returning first of %" PRIuSIZE " events\n", this, mEvents.Length());
-    MultiTouchInput event = mEvents[0];
-    mEvents.RemoveElementAt(0);
-    DispatchEvent(event);
-  }
-}
-
-void
 TouchBlockState::DispatchEvent(const InputData& aEvent) const
 {
   MOZ_ASSERT(aEvent.mInputType == MULTITOUCH_INPUT);
   mTouchCounter.Update(aEvent.AsMultiTouchInput());
   CancelableBlockState::DispatchEvent(aEvent);
 }
 
 bool
--- a/gfx/layers/apz/src/InputBlockState.h
+++ b/gfx/layers/apz/src/InputBlockState.h
@@ -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/. */
 
 #ifndef mozilla_layers_InputBlockState_h
 #define mozilla_layers_InputBlockState_h
 
 #include "InputData.h"                      // for MultiTouchInput
+#include "mozilla/RefCounted.h"             // for RefCounted
 #include "mozilla/RefPtr.h"                 // for RefPtr
 #include "mozilla/gfx/Matrix.h"             // for Matrix4x4
 #include "mozilla/layers/APZUtils.h"        // for TouchBehaviorFlags
 #include "mozilla/layers/AsyncDragMetrics.h"
 #include "mozilla/TimeStamp.h"              // for TimeStamp
 #include "nsTArray.h"                       // for nsTArray
 #include "TouchCounter.h"
 
@@ -24,40 +25,42 @@ class OverscrollHandoffChain;
 class CancelableBlockState;
 class TouchBlockState;
 class WheelBlockState;
 class DragBlockState;
 class PanGestureBlockState;
 
 /**
  * A base class that stores state common to various input blocks.
- * Currently, it just stores the overscroll handoff chain.
  * Note that the InputBlockState constructor acquires the tree lock, so callers
  * from inside AsyncPanZoomController should ensure that the APZC lock is not
  * held.
  */
-class InputBlockState
+class InputBlockState : public RefCounted<InputBlockState>
 {
 public:
+  MOZ_DECLARE_REFCOUNTED_TYPENAME(InputBlockState)
+
   static const uint64_t NO_BLOCK_ID = 0;
 
   enum class TargetConfirmationState {
     eUnconfirmed,
     eTimedOut,
     eTimedOutAndMainThreadResponded,
     eConfirmed
   };
 
   explicit InputBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
                            bool aTargetConfirmed);
   virtual ~InputBlockState()
   {}
 
   virtual bool SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc,
-                                      TargetConfirmationState aState);
+                                      TargetConfirmationState aState,
+                                      InputData* aFirstInput);
   const RefPtr<AsyncPanZoomController>& GetTargetApzc() const;
   const RefPtr<const OverscrollHandoffChain>& GetOverscrollHandoffChain() const;
   uint64_t GetBlockId() const;
 
   bool IsTargetConfirmed() const;
   bool HasReceivedRealConfirmedTarget() const;
 
   void SetScrolledApzc(AsyncPanZoomController* aApzc);
@@ -157,23 +160,16 @@ public:
   bool IsContentResponseTimerExpired() const;
 
   /**
    * @return true iff web content cancelled this block of events.
    */
   bool IsDefaultPrevented() const;
 
   /**
-   * Process the given event using this input block's target apzc.
-   * This input block must not have pending events, and its apzc must not be
-   * nullptr.
-   */
-  void DispatchImmediate(const InputData& aEvent) const;
-
-  /**
    * Dispatch the event to the target APZC. Mostly this is a hook for
    * subclasses to do any per-event processing they need to.
    */
   virtual void DispatchEvent(const InputData& aEvent) const;
 
   /**
    * @return true iff this block has received all the information it could
    *         have gotten from the content thread.
@@ -182,32 +178,16 @@ public:
 
   /**
    * @return true iff this block has received all the information needed
    *         to properly dispatch the events in the block.
    */
   virtual bool IsReadyForHandling() const;
 
   /**
-   * Returns whether or not this block has pending events.
-   */
-  virtual bool HasEvents() const = 0;
-
-  /**
-   * Throw away all the events in this input block.
-   */
-  virtual void DropEvents() = 0;
-
-  /**
-   * Process all events using this input block's target apzc, leaving this
-   * block depleted. This input block's apzc must not be nullptr.
-   */
-  virtual void HandleEvents() = 0;
-
-  /**
    * Return true if this input block must stay active if it would otherwise
    * be removed as the last item in the pending queue.
    */
   virtual bool MustStayActive() = 0;
 
   /**
    * Return a descriptive name for the block kind.
    */
@@ -226,25 +206,21 @@ private:
 class WheelBlockState : public CancelableBlockState
 {
 public:
   WheelBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
                   bool aTargetConfirmed,
                   const ScrollWheelInput& aEvent);
 
   bool SetContentResponse(bool aPreventDefault) override;
-  bool HasEvents() const override;
-  void DropEvents() override;
-  void HandleEvents() override;
   bool MustStayActive() override;
   const char* Type() override;
   bool SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc,
-                              TargetConfirmationState aState) override;
-
-  void AddEvent(const ScrollWheelInput& aEvent);
+                              TargetConfirmationState aState,
+                              InputData* aFirstInput) override;
 
   WheelBlockState *AsWheelBlock() override {
     return this;
   }
 
   /**
    * Determine whether this wheel block is accepting new events.
    */
@@ -294,53 +270,46 @@ public:
    * Update the wheel transaction state for a new event.
    */
   void Update(ScrollWheelInput& aEvent);
 
 protected:
   void UpdateTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc) override;
 
 private:
-  nsTArray<ScrollWheelInput> mEvents;
   TimeStamp mLastEventTime;
   TimeStamp mLastMouseMove;
   uint32_t mScrollSeriesCounter;
   bool mTransactionEnded;
 };
 
 /**
  * A block of mouse events that are part of a drag
  */
 class DragBlockState : public CancelableBlockState
 {
 public:
   DragBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
                  bool aTargetConfirmed,
                  const MouseInput& aEvent);
 
-  bool HasEvents() const override;
-  void DropEvents() override;
-  void HandleEvents() override;
   bool MustStayActive() override;
   const char* Type() override;
 
   bool HasReceivedMouseUp();
   void MarkMouseUpReceived();
 
-  void AddEvent(const MouseInput& aEvent);
-
   DragBlockState *AsDragBlock() override {
     return this;
   }
 
   void SetDragMetrics(const AsyncDragMetrics& aDragMetrics);
 
   void DispatchEvent(const InputData& aEvent) const override;
 private:
-  nsTArray<MouseInput> mEvents;
   AsyncDragMetrics mDragMetrics;
   bool mReceivedMouseUp;
 };
 
 /**
  * A single block of pan gesture events.
  */
 class PanGestureBlockState : public CancelableBlockState
@@ -348,41 +317,36 @@ class PanGestureBlockState : public Canc
 public:
   PanGestureBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
                        bool aTargetConfirmed,
                        const PanGestureInput& aEvent);
 
   bool SetContentResponse(bool aPreventDefault) override;
   bool HasReceivedAllContentNotifications() const override;
   bool IsReadyForHandling() const override;
-  bool HasEvents() const override;
-  void DropEvents() override;
-  void HandleEvents() override;
   bool MustStayActive() override;
   const char* Type() override;
   bool SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc,
-                              TargetConfirmationState aState) override;
-
-  void AddEvent(const PanGestureInput& aEvent);
+                              TargetConfirmationState aState,
+                              InputData* aFirstInput) override;
 
   PanGestureBlockState *AsPanGestureBlock() override {
     return this;
   }
 
   /**
    * @return Whether or not overscrolling is prevented for this block.
    */
   bool AllowScrollHandoff() const;
 
   bool WasInterrupted() const { return mInterrupted; }
 
   void SetNeedsToWaitForContentResponse(bool aWaitForContentResponse);
 
 private:
-  nsTArray<PanGestureInput> mEvents;
   bool mInterrupted;
   bool mWaitingForContentResponse;
 };
 
 /**
  * This class represents a single touch block. A touch block is
  * a set of touch events that can be cancelled by web content via
  * touch event listeners.
@@ -459,21 +423,16 @@ public:
    */
   void SetSingleTapOccurred();
   /**
    * @return true iff the single-tap-occurred flag is set on this block.
    */
   bool SingleTapOccurred() const;
 
   /**
-   * Add a new touch event to the queue of events in this input block.
-   */
-  void AddEvent(const MultiTouchInput& aEvent);
-
-  /**
    * @return false iff touch-action is enabled and the allowed touch behaviors for
    *         this touch block do not allow pinch-zooming.
    */
   bool TouchActionAllowsPinchZoom() const;
   /**
    * @return false iff touch-action is enabled and the allowed touch behaviors for
    *         this touch block do not allow double-tap zooming.
    */
@@ -498,31 +457,27 @@ public:
   bool UpdateSlopState(const MultiTouchInput& aInput,
                        bool aApzcCanConsumeEvents);
 
   /**
    * Returns the number of touch points currently active.
    */
   uint32_t GetActiveTouchCount() const;
 
-  bool HasEvents() const override;
-  void DropEvents() override;
-  void HandleEvents() override;
   void DispatchEvent(const InputData& aEvent) const override;
   bool MustStayActive() override;
   const char* Type() override;
 
 private:
   nsTArray<TouchBehaviorFlags> mAllowedTouchBehaviors;
   bool mAllowedTouchBehaviorSet;
   bool mDuringFastFling;
   bool mSingleTapOccurred;
   bool mInSlop;
   ScreenIntPoint mSlopOrigin;
-  nsTArray<MultiTouchInput> mEvents;
   // A reference to the InputQueue's touch counter
   TouchCounter& mTouchCounter;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // mozilla_layers_InputBlockState_h
--- a/gfx/layers/apz/src/InputQueue.cpp
+++ b/gfx/layers/apz/src/InputQueue.cpp
@@ -7,29 +7,30 @@
 #include "InputQueue.h"
 
 #include "AsyncPanZoomController.h"
 #include "gfxPrefs.h"
 #include "InputBlockState.h"
 #include "LayersLogging.h"
 #include "mozilla/layers/APZThreadUtils.h"
 #include "OverscrollHandoffState.h"
+#include "QueuedInput.h"
 
 #define INPQ_LOG(...)
 // #define INPQ_LOG(...) printf_stderr("INPQ: " __VA_ARGS__)
 
 namespace mozilla {
 namespace layers {
 
 InputQueue::InputQueue()
 {
 }
 
 InputQueue::~InputQueue() {
-  mInputBlockQueue.Clear();
+  mQueuedInputs.Clear();
 }
 
 nsEventStatus
 InputQueue::ReceiveInputEvent(const RefPtr<AsyncPanZoomController>& aTarget,
                               bool aTargetConfirmed,
                               const InputData& aEvent,
                               uint64_t* aOutInputBlockId) {
   APZThreadUtils::AssertOnControllerThread();
@@ -59,86 +60,67 @@ InputQueue::ReceiveInputEvent(const RefP
       // The return value for non-touch input is only used by tests, so just pass
       // through the return value for now. This can be changed later if needed.
       // TODO (bug 1098430): we will eventually need to have smarter handling for
       // non-touch events as well.
       return aTarget->HandleInputEvent(aEvent, aTarget->GetTransformToThis());
   }
 }
 
-bool
-InputQueue::MaybeHandleCurrentBlock(CancelableBlockState *block,
-                                    const InputData& aEvent) {
-  if (block == CurrentBlock() && block->IsReadyForHandling()) {
-    const RefPtr<AsyncPanZoomController>& target = block->GetTargetApzc();
-    INPQ_LOG("current block is ready with target %p preventdefault %d\n",
-        target.get(), block->IsDefaultPrevented());
-    if (!target || block->IsDefaultPrevented()) {
-      return true;
-    }
-    UpdateActiveApzc(block->GetTargetApzc());
-    block->DispatchImmediate(aEvent);
-    return true;
-  }
-  return false;
-}
-
 nsEventStatus
 InputQueue::ReceiveTouchInput(const RefPtr<AsyncPanZoomController>& aTarget,
                               bool aTargetConfirmed,
                               const MultiTouchInput& aEvent,
                               uint64_t* aOutInputBlockId) {
   TouchBlockState* block = nullptr;
   if (aEvent.mType == MultiTouchInput::MULTITOUCH_START) {
     nsTArray<TouchBehaviorFlags> currentBehaviors;
     bool haveBehaviors = false;
     if (!gfxPrefs::TouchActionEnabled()) {
       haveBehaviors = true;
-    } else if (!mInputBlockQueue.IsEmpty() && CurrentBlock()->AsTouchBlock()) {
-      haveBehaviors = CurrentTouchBlock()->GetAllowedTouchBehaviors(currentBehaviors);
+    } else if (mActiveTouchBlock) {
+      haveBehaviors = mActiveTouchBlock->GetAllowedTouchBehaviors(currentBehaviors);
       // If the behaviours aren't set, but the main-thread response timer on
       // the block is expired we still treat it as though it has behaviors,
       // because in that case we still want to interrupt the fast-fling and
       // use the default behaviours.
-      haveBehaviors |= CurrentTouchBlock()->IsContentResponseTimerExpired();
+      haveBehaviors |= mActiveTouchBlock->IsContentResponseTimerExpired();
     }
 
     block = StartNewTouchBlock(aTarget, aTargetConfirmed, false);
     INPQ_LOG("started new touch block %p id %" PRIu64 " for target %p\n",
         block, block->GetBlockId(), aTarget.get());
 
     // XXX using the chain from |block| here may be wrong in cases where the
     // target isn't confirmed and the real target turns out to be something
     // else. For now assume this is rare enough that it's not an issue.
-    if (block == CurrentBlock() &&
+    if (mQueuedInputs.IsEmpty() &&
         aEvent.mTouches.Length() == 1 &&
         block->GetOverscrollHandoffChain()->HasFastFlungApzc() &&
         haveBehaviors) {
       // If we're already in a fast fling, and a single finger goes down, then
       // we want special handling for the touch event, because it shouldn't get
       // delivered to content. Note that we don't set this flag when going
       // from a fast fling to a pinch state (i.e. second finger goes down while
       // the first finger is moving).
       block->SetDuringFastFling();
       block->SetConfirmedTargetApzc(aTarget,
-          InputBlockState::TargetConfirmationState::eConfirmed);
+          InputBlockState::TargetConfirmationState::eConfirmed,
+          nullptr /* the block was just created so it has no events */);
       if (gfxPrefs::TouchActionEnabled()) {
         block->SetAllowedTouchBehaviors(currentBehaviors);
       }
       INPQ_LOG("block %p tagged as fast-motion\n", block);
     }
 
     CancelAnimationsForNewBlock(block);
 
     MaybeRequestContentResponse(aTarget, block);
   } else {
-    if (!mInputBlockQueue.IsEmpty()) {
-      block = mInputBlockQueue.LastElement().get()->AsTouchBlock();
-    }
-
+    block = mActiveTouchBlock.get();
     if (!block) {
       NS_WARNING("Received a non-start touch event while no touch blocks active!");
       return nsEventStatus_eIgnore;
     }
 
     INPQ_LOG("received new event in block %p\n", block);
   }
 
@@ -155,47 +137,42 @@ InputQueue::ReceiveTouchInput(const RefP
   nsEventStatus result = nsEventStatus_eIgnore;
 
   // XXX calling ArePointerEventsConsumable on |target| may be wrong here if
   // the target isn't confirmed and the real target turns out to be something
   // else. For now assume this is rare enough that it's not an issue.
   if (block->IsDuringFastFling()) {
     INPQ_LOG("dropping event due to block %p being in fast motion\n", block);
     result = nsEventStatus_eConsumeNoDefault;
-  } else if (target && target->ArePointerEventsConsumable(block, aEvent.AsMultiTouchInput().mTouches.Length())) {
-    if (block->UpdateSlopState(aEvent.AsMultiTouchInput(), true)) {
+  } else if (target && target->ArePointerEventsConsumable(block, aEvent.mTouches.Length())) {
+    if (block->UpdateSlopState(aEvent, true)) {
       INPQ_LOG("dropping event due to block %p being in slop\n", block);
       result = nsEventStatus_eConsumeNoDefault;
     } else {
       result = nsEventStatus_eConsumeDoDefault;
     }
-  } else if (block->UpdateSlopState(aEvent.AsMultiTouchInput(), false)) {
+  } else if (block->UpdateSlopState(aEvent, false)) {
     INPQ_LOG("dropping event due to block %p being in mini-slop\n", block);
     result = nsEventStatus_eConsumeNoDefault;
   }
-  if (!MaybeHandleCurrentBlock(block, aEvent)) {
-    block->AddEvent(aEvent.AsMultiTouchInput());
-  }
+  mQueuedInputs.AppendElement(MakeUnique<QueuedInput>(aEvent, *block));
+  ProcessQueue();
   return result;
 }
 
 nsEventStatus
 InputQueue::ReceiveMouseInput(const RefPtr<AsyncPanZoomController>& aTarget,
                               bool aTargetConfirmed,
                               const MouseInput& aEvent,
                               uint64_t* aOutInputBlockId) {
   // On a new mouse down we can have a new target so we must force a new block
   // with a new target.
   bool newBlock = DragTracker::StartsDrag(aEvent);
 
-  DragBlockState* block = nullptr;
-  if (!newBlock && !mInputBlockQueue.IsEmpty()) {
-    block = mInputBlockQueue.LastElement()->AsDragBlock();
-  }
-
+  DragBlockState* block = newBlock ? nullptr : mActiveDragBlock.get();
   if (block && block->HasReceivedMouseUp()) {
     block = nullptr;
   }
 
   if (!block && mDragTracker.InDrag()) {
     // If there's no current drag block, but we're getting a move with a button
     // down, we need to start a new drag block because we're obviously already
     // in the middle of a drag (it probably got interrupted by something else).
@@ -213,91 +190,85 @@ InputQueue::ReceiveMouseInput(const RefP
 
   if (!block) {
     MOZ_ASSERT(newBlock);
     block = new DragBlockState(aTarget, aTargetConfirmed, aEvent);
 
     INPQ_LOG("started new drag block %p id %" PRIu64 " for %sconfirmed target %p\n",
         block, block->GetBlockId(), aTargetConfirmed ? "" : "un", aTarget.get());
 
-    SweepDepletedBlocks();
-    mInputBlockQueue.AppendElement(block);
+    mActiveDragBlock = block;
 
     CancelAnimationsForNewBlock(block);
     MaybeRequestContentResponse(aTarget, block);
   }
 
   if (aOutInputBlockId) {
     *aOutInputBlockId = block->GetBlockId();
   }
 
-  if (!MaybeHandleCurrentBlock(block, aEvent)) {
-    block->AddEvent(aEvent.AsMouseInput());
-  }
+  mQueuedInputs.AppendElement(MakeUnique<QueuedInput>(aEvent, *block));
+  ProcessQueue();
 
   if (DragTracker::EndsDrag(aEvent)) {
     block->MarkMouseUpReceived();
   }
 
   // The event is part of a drag block and could potentially cause
   // scrolling, so return DoDefault.
   return nsEventStatus_eConsumeDoDefault;
 }
 
 nsEventStatus
 InputQueue::ReceiveScrollWheelInput(const RefPtr<AsyncPanZoomController>& aTarget,
                                     bool aTargetConfirmed,
                                     const ScrollWheelInput& aEvent,
                                     uint64_t* aOutInputBlockId) {
-  WheelBlockState* block = nullptr;
-  if (!mInputBlockQueue.IsEmpty()) {
-    block = mInputBlockQueue.LastElement()->AsWheelBlock();
-
-    // If the block is not accepting new events we'll create a new input block
-    // (and therefore a new wheel transaction).
-    if (block &&
-        (!block->ShouldAcceptNewEvent() ||
-         block->MaybeTimeout(aEvent)))
-    {
-      block = nullptr;
-    }
+  WheelBlockState* block = mActiveWheelBlock.get();
+  // If the block is not accepting new events we'll create a new input block
+  // (and therefore a new wheel transaction).
+  if (block &&
+      (!block->ShouldAcceptNewEvent() ||
+       block->MaybeTimeout(aEvent)))
+  {
+    block = nullptr;
   }
 
   MOZ_ASSERT(!block || block->InTransaction());
 
   if (!block) {
     block = new WheelBlockState(aTarget, aTargetConfirmed, aEvent);
     INPQ_LOG("started new scroll wheel block %p id %" PRIu64 " for target %p\n",
         block, block->GetBlockId(), aTarget.get());
 
-    SweepDepletedBlocks();
-    mInputBlockQueue.AppendElement(block);
+    mActiveWheelBlock = block;
 
     CancelAnimationsForNewBlock(block);
     MaybeRequestContentResponse(aTarget, block);
   } else {
     INPQ_LOG("received new event in block %p\n", block);
   }
 
   if (aOutInputBlockId) {
     *aOutInputBlockId = block->GetBlockId();
   }
 
-  // Copy the event, since WheelBlockState needs to affix a counter.
-  ScrollWheelInput event(aEvent);
-  block->Update(event);
-
   // Note that the |aTarget| the APZCTM sent us may contradict the confirmed
   // target set on the block. In this case the confirmed target (which may be
   // null) should take priority. This is equivalent to just always using the
   // target (confirmed or not) from the block, which is what
-  // MaybeHandleCurrentBlock() does.
-  if (!MaybeHandleCurrentBlock(block, event)) {
-    block->AddEvent(event);
-  }
+  // ProcessQueue() does.
+  mQueuedInputs.AppendElement(MakeUnique<QueuedInput>(aEvent, *block));
+
+  // The WheelBlockState needs to affix a counter to the event before we process
+  // it. Note that the counter is affixed to the copy in the queue rather than
+  // |aEvent|.
+  block->Update(mQueuedInputs.LastElement()->Input()->AsScrollWheelInput());
+
+  ProcessQueue();
 
   return nsEventStatus_eConsumeDoDefault;
 }
 
 static bool
 CanScrollTargetHorizontally(const PanGestureInput& aInitialEvent,
                             PanGestureBlockState* aBlock)
 {
@@ -315,19 +286,18 @@ InputQueue::ReceivePanGestureInput(const
                                    uint64_t* aOutInputBlockId) {
   if (aEvent.mType == PanGestureInput::PANGESTURE_MAYSTART ||
       aEvent.mType == PanGestureInput::PANGESTURE_CANCELLED) {
     // Ignore these events for now.
     return nsEventStatus_eConsumeDoDefault;
   }
 
   PanGestureBlockState* block = nullptr;
-  if (!mInputBlockQueue.IsEmpty() &&
-      aEvent.mType != PanGestureInput::PANGESTURE_START) {
-    block = mInputBlockQueue.LastElement()->AsPanGestureBlock();
+  if (aEvent.mType != PanGestureInput::PANGESTURE_START) {
+    block = mActivePanGestureBlock.get();
   }
 
   PanGestureInput event = aEvent;
   nsEventStatus result = nsEventStatus_eConsumeDoDefault;
 
   if (!block || block->WasInterrupted()) {
     if (event.mType != PanGestureInput::PANGESTURE_START) {
       // Only PANGESTURE_START events are allowed to start a new pan gesture
@@ -350,51 +320,49 @@ InputQueue::ReceivePanGestureInput(const
       // block.
       block->SetNeedsToWaitForContentResponse(true);
 
       // Inform our caller that we haven't scrolled in response to the event
       // and that a swipe can be started from this event if desired.
       result = nsEventStatus_eIgnore;
     }
 
-    SweepDepletedBlocks();
-    mInputBlockQueue.AppendElement(block);
+    mActivePanGestureBlock = block;
 
     CancelAnimationsForNewBlock(block);
     MaybeRequestContentResponse(aTarget, block);
   } else {
     INPQ_LOG("received new event in block %p\n", block);
   }
 
   if (aOutInputBlockId) {
     *aOutInputBlockId = block->GetBlockId();
   }
 
   // Note that the |aTarget| the APZCTM sent us may contradict the confirmed
   // target set on the block. In this case the confirmed target (which may be
   // null) should take priority. This is equivalent to just always using the
   // target (confirmed or not) from the block, which is what
-  // MaybeHandleCurrentBlock() does.
-  if (!MaybeHandleCurrentBlock(block, event)) {
-    block->AddEvent(event.AsPanGestureInput());
-  }
+  // ProcessQueue() does.
+  mQueuedInputs.AppendElement(MakeUnique<QueuedInput>(event, *block));
+  ProcessQueue();
 
   return result;
 }
 
 void
 InputQueue::CancelAnimationsForNewBlock(CancelableBlockState* aBlock)
 {
   // We want to cancel animations here as soon as possible (i.e. without waiting for
   // content responses) because a finger has gone down and we don't want to keep moving
   // the content under the finger. However, to prevent "future" touchstart events from
   // interfering with "past" animations (i.e. from a previous touch block that is still
   // being processed) we only do this animation-cancellation if there are no older
   // touch blocks still in the queue.
-  if (aBlock == CurrentBlock()) {
+  if (mQueuedInputs.IsEmpty()) {
     aBlock->GetOverscrollHandoffChain()->CancelAnimations(ExcludeOverscroll | ScrollSnap);
   }
 }
 
 void
 InputQueue::MaybeRequestContentResponse(const RefPtr<AsyncPanZoomController>& aTarget,
                                         CancelableBlockState* aBlock)
 {
@@ -431,121 +399,97 @@ InputQueue::InjectNewTouchBlock(AsyncPan
     /* aTargetConfirmed = */ true,
     /* aCopyPropertiesFromCurrent = */ true);
   INPQ_LOG("injecting new touch block %p with id %" PRIu64 " and target %p\n",
     block, block->GetBlockId(), aTarget);
   ScheduleMainThreadTimeout(aTarget, block);
   return block->GetBlockId();
 }
 
-void
-InputQueue::SweepDepletedBlocks()
-{
-  // We're going to start a new block, so clear out any depleted blocks at the head of the queue.
-  // See corresponding comment in ProcessInputBlocks.
-  while (!mInputBlockQueue.IsEmpty()) {
-    CancelableBlockState* block = mInputBlockQueue[0].get();
-    if (!block->IsReadyForHandling() || block->HasEvents()) {
-      break;
-    }
-
-    INPQ_LOG("discarding depleted %s block %p\n", block->Type(), block);
-    mInputBlockQueue.RemoveElementAt(0);
-  }
-}
-
 TouchBlockState*
 InputQueue::StartNewTouchBlock(const RefPtr<AsyncPanZoomController>& aTarget,
                                bool aTargetConfirmed,
                                bool aCopyPropertiesFromCurrent)
 {
   TouchBlockState* newBlock = new TouchBlockState(aTarget, aTargetConfirmed,
       mTouchCounter);
   if (aCopyPropertiesFromCurrent) {
-    newBlock->CopyPropertiesFrom(*CurrentTouchBlock());
+    // We should never enter here without a current touch block, because this
+    // codepath is invoked from the OnLongPress handler in
+    // AsyncPanZoomController, which should bail out if there is no current
+    // touch block.
+    MOZ_ASSERT(GetCurrentTouchBlock());
+    newBlock->CopyPropertiesFrom(*GetCurrentTouchBlock());
   }
 
-  SweepDepletedBlocks();
-
-  // Add the new block to the queue.
-  mInputBlockQueue.AppendElement(newBlock);
+  mActiveTouchBlock = newBlock;
   return newBlock;
 }
 
 CancelableBlockState*
-InputQueue::CurrentBlock() const
+InputQueue::GetCurrentBlock() const
 {
   APZThreadUtils::AssertOnControllerThread();
-
-  MOZ_ASSERT(!mInputBlockQueue.IsEmpty());
-  return mInputBlockQueue[0].get();
+  return mQueuedInputs.IsEmpty() ? nullptr : mQueuedInputs[0]->Block();
 }
 
 TouchBlockState*
-InputQueue::CurrentTouchBlock() const
+InputQueue::GetCurrentTouchBlock() const
 {
-  TouchBlockState* block = CurrentBlock()->AsTouchBlock();
-  MOZ_ASSERT(block);
-  return block;
+  CancelableBlockState* block = GetCurrentBlock();
+  return block ? block->AsTouchBlock() : mActiveTouchBlock.get();
 }
 
 WheelBlockState*
-InputQueue::CurrentWheelBlock() const
+InputQueue::GetCurrentWheelBlock() const
 {
-  WheelBlockState* block = CurrentBlock()->AsWheelBlock();
-  MOZ_ASSERT(block);
-  return block;
+  CancelableBlockState* block = GetCurrentBlock();
+  return block ? block->AsWheelBlock() : mActiveWheelBlock.get();
 }
 
 DragBlockState*
-InputQueue::CurrentDragBlock() const
+InputQueue::GetCurrentDragBlock() const
 {
-  DragBlockState* block = CurrentBlock()->AsDragBlock();
-  MOZ_ASSERT(block);
-  return block;
+  CancelableBlockState* block = GetCurrentBlock();
+  return block ? block->AsDragBlock() : mActiveDragBlock.get();
 }
 
 PanGestureBlockState*
-InputQueue::CurrentPanGestureBlock() const
+InputQueue::GetCurrentPanGestureBlock() const
 {
-  PanGestureBlockState* block = CurrentBlock()->AsPanGestureBlock();
-  MOZ_ASSERT(block);
-  return block;
+  CancelableBlockState* block = GetCurrentBlock();
+  return block ? block->AsPanGestureBlock() : mActivePanGestureBlock.get();
 }
 
 WheelBlockState*
-InputQueue::GetCurrentWheelTransaction() const
+InputQueue::GetActiveWheelTransaction() const
 {
-  if (mInputBlockQueue.IsEmpty()) {
-    return nullptr;
-  }
-  WheelBlockState* block = CurrentBlock()->AsWheelBlock();
+  WheelBlockState* block = mActiveWheelBlock.get();
   if (!block || !block->InTransaction()) {
     return nullptr;
   }
   return block;
 }
 
 bool
 InputQueue::HasReadyTouchBlock() const
 {
-  return !mInputBlockQueue.IsEmpty() &&
-         mInputBlockQueue[0]->AsTouchBlock() &&
-         mInputBlockQueue[0]->IsReadyForHandling();
+  return !mQueuedInputs.IsEmpty() &&
+      mQueuedInputs[0]->Block()->AsTouchBlock() &&
+      mQueuedInputs[0]->Block()->IsReadyForHandling();
 }
 
 bool
 InputQueue::AllowScrollHandoff() const
 {
-  MOZ_ASSERT(CurrentBlock());
-  if (CurrentBlock()->AsWheelBlock()) {
-    return CurrentBlock()->AsWheelBlock()->AllowScrollHandoff();
+  if (GetCurrentWheelBlock()) {
+    return GetCurrentWheelBlock()->AllowScrollHandoff();
   }
-  if (CurrentBlock()->AsPanGestureBlock()) {
-    return CurrentBlock()->AsPanGestureBlock()->AllowScrollHandoff();
+  if (GetCurrentPanGestureBlock()) {
+    return GetCurrentPanGestureBlock()->AllowScrollHandoff();
   }
   return true;
 }
 
 bool
 InputQueue::IsDragOnScrollbar(bool aHitScrollbar)
 {
   if (!mDragTracker.InDrag()) {
@@ -564,182 +508,224 @@ InputQueue::ScheduleMainThreadTimeout(co
   INPQ_LOG("scheduling main thread timeout for target %p\n", aTarget.get());
   aBlock->StartContentResponseTimer();
   aTarget->PostDelayedTask(NewRunnableMethod<uint64_t>(this,
                                                        &InputQueue::MainThreadTimeout,
                                                        aBlock->GetBlockId()),
                            gfxPrefs::APZContentResponseTimeout());
 }
 
+CancelableBlockState*
+InputQueue::FindBlockForId(uint64_t aInputBlockId,
+                           InputData** aOutFirstInput)
+{
+  for (const auto& queuedInput : mQueuedInputs) {
+    if (queuedInput->Block()->GetBlockId() == aInputBlockId) {
+      if (aOutFirstInput) {
+        *aOutFirstInput = queuedInput->Input();
+      }
+      return queuedInput->Block();
+    }
+  }
+
+  CancelableBlockState* block = nullptr;
+  if (mActiveTouchBlock && mActiveTouchBlock->GetBlockId() == aInputBlockId) {
+    block = mActiveTouchBlock.get();
+  } else if (mActiveWheelBlock && mActiveWheelBlock->GetBlockId() == aInputBlockId) {
+    block = mActiveWheelBlock.get();
+  } else if (mActiveDragBlock && mActiveDragBlock->GetBlockId() == aInputBlockId) {
+    block = mActiveDragBlock.get();
+  } else if (mActivePanGestureBlock && mActivePanGestureBlock->GetBlockId() == aInputBlockId) {
+    block = mActivePanGestureBlock.get();
+  }
+  // Since we didn't encounter this block while iterating through mQueuedInputs,
+  // it must have no events associated with it at the moment.
+  if (aOutFirstInput) {
+    *aOutFirstInput = nullptr;
+  }
+  return block;
+}
+
 void
-InputQueue::MainThreadTimeout(const uint64_t& aInputBlockId) {
+InputQueue::MainThreadTimeout(uint64_t aInputBlockId) {
   APZThreadUtils::AssertOnControllerThread();
 
   INPQ_LOG("got a main thread timeout; block=%" PRIu64 "\n", aInputBlockId);
   bool success = false;
-  for (size_t i = 0; i < mInputBlockQueue.Length(); i++) {
-    if (mInputBlockQueue[i]->GetBlockId() == aInputBlockId) {
-      // time out the touch-listener response and also confirm the existing
-      // target apzc in the case where the main thread doesn't get back to us
-      // fast enough.
-      success = mInputBlockQueue[i]->TimeoutContentResponse();
-      success |= mInputBlockQueue[i]->SetConfirmedTargetApzc(
-          mInputBlockQueue[i]->GetTargetApzc(),
-          InputBlockState::TargetConfirmationState::eTimedOut);
-      break;
-    }
+  InputData* firstInput = nullptr;
+  CancelableBlockState* block = FindBlockForId(aInputBlockId, &firstInput);
+  if (block) {
+    // time out the touch-listener response and also confirm the existing
+    // target apzc in the case where the main thread doesn't get back to us
+    // fast enough.
+    success = block->TimeoutContentResponse();
+    success |= block->SetConfirmedTargetApzc(
+        block->GetTargetApzc(),
+        InputBlockState::TargetConfirmationState::eTimedOut,
+        firstInput);
   }
   if (success) {
-    ProcessInputBlocks();
+    ProcessQueue();
   }
 }
 
 void
 InputQueue::ContentReceivedInputBlock(uint64_t aInputBlockId, bool aPreventDefault) {
   APZThreadUtils::AssertOnControllerThread();
 
   INPQ_LOG("got a content response; block=%" PRIu64 "\n", aInputBlockId);
   bool success = false;
-  for (size_t i = 0; i < mInputBlockQueue.Length(); i++) {
-    CancelableBlockState* block = mInputBlockQueue[i].get();
-    if (block->GetBlockId() == aInputBlockId) {
-      success = block->SetContentResponse(aPreventDefault);
-      block->RecordContentResponseTime();
-      break;
-    }
+  CancelableBlockState* block = FindBlockForId(aInputBlockId, nullptr);
+  if (block) {
+    success = block->SetContentResponse(aPreventDefault);
+    block->RecordContentResponseTime();
   }
   if (success) {
-    ProcessInputBlocks();
+    ProcessQueue();
   }
 }
 
 void
 InputQueue::SetConfirmedTargetApzc(uint64_t aInputBlockId, const RefPtr<AsyncPanZoomController>& aTargetApzc) {
   APZThreadUtils::AssertOnControllerThread();
 
   INPQ_LOG("got a target apzc; block=%" PRIu64 " guid=%s\n",
     aInputBlockId, aTargetApzc ? Stringify(aTargetApzc->GetGuid()).c_str() : "");
   bool success = false;
-  for (size_t i = 0; i < mInputBlockQueue.Length(); i++) {
-    CancelableBlockState* block = mInputBlockQueue[i].get();
-    if (block->GetBlockId() == aInputBlockId) {
-      success = block->SetConfirmedTargetApzc(aTargetApzc,
-          InputBlockState::TargetConfirmationState::eConfirmed);
-      block->RecordContentResponseTime();
-      break;
-    }
+  InputData* firstInput = nullptr;
+  CancelableBlockState* block = FindBlockForId(aInputBlockId, &firstInput);
+  if (block) {
+    success = block->SetConfirmedTargetApzc(aTargetApzc,
+        InputBlockState::TargetConfirmationState::eConfirmed,
+        firstInput);
+    block->RecordContentResponseTime();
   }
   if (success) {
-    ProcessInputBlocks();
+    ProcessQueue();
   }
 }
 
 void
 InputQueue::ConfirmDragBlock(uint64_t aInputBlockId, const RefPtr<AsyncPanZoomController>& aTargetApzc,
                                    const AsyncDragMetrics& aDragMetrics)
 {
   APZThreadUtils::AssertOnControllerThread();
 
   INPQ_LOG("got a target apzc; block=%" PRIu64 " guid=%s\n",
     aInputBlockId, aTargetApzc ? Stringify(aTargetApzc->GetGuid()).c_str() : "");
   bool success = false;
-  for (size_t i = 0; i < mInputBlockQueue.Length(); i++) {
-    DragBlockState* block = mInputBlockQueue[i]->AsDragBlock();
-    if (block && block->GetBlockId() == aInputBlockId) {
-      block->SetDragMetrics(aDragMetrics);
-      success = block->SetConfirmedTargetApzc(aTargetApzc,
-          InputBlockState::TargetConfirmationState::eConfirmed);
-      block->RecordContentResponseTime();
-      break;
-    }
+  InputData* firstInput = nullptr;
+  CancelableBlockState* block = FindBlockForId(aInputBlockId, &firstInput);
+  if (block && block->AsDragBlock()) {
+    block->AsDragBlock()->SetDragMetrics(aDragMetrics);
+    success = block->SetConfirmedTargetApzc(aTargetApzc,
+        InputBlockState::TargetConfirmationState::eConfirmed,
+        firstInput);
+    block->RecordContentResponseTime();
   }
   if (success) {
-    ProcessInputBlocks();
+    ProcessQueue();
   }
 }
 
 void
 InputQueue::SetAllowedTouchBehavior(uint64_t aInputBlockId, const nsTArray<TouchBehaviorFlags>& aBehaviors) {
   APZThreadUtils::AssertOnControllerThread();
 
   INPQ_LOG("got allowed touch behaviours; block=%" PRIu64 "\n", aInputBlockId);
   bool success = false;
-  for (size_t i = 0; i < mInputBlockQueue.Length(); i++) {
-    if (mInputBlockQueue[i]->GetBlockId() == aInputBlockId) {
-      TouchBlockState *block = mInputBlockQueue[i]->AsTouchBlock();
-      if (block) {
-        success = block->SetAllowedTouchBehaviors(aBehaviors);
-        block->RecordContentResponseTime();
-      } else {
-        NS_WARNING("input block is not a touch block");
-      }
-      break;
-    }
+  CancelableBlockState* block = FindBlockForId(aInputBlockId, nullptr);
+  if (block && block->AsTouchBlock()) {
+    success = block->AsTouchBlock()->SetAllowedTouchBehaviors(aBehaviors);
+    block->RecordContentResponseTime();
+  } else if (block) {
+    NS_WARNING("input block is not a touch block");
   }
   if (success) {
-    ProcessInputBlocks();
+    ProcessQueue();
   }
 }
 
 void
-InputQueue::ProcessInputBlocks() {
+InputQueue::ProcessQueue() {
   APZThreadUtils::AssertOnControllerThread();
 
-  do {
-    CancelableBlockState* curBlock = CurrentBlock();
+  while (!mQueuedInputs.IsEmpty()) {
+    CancelableBlockState* curBlock = mQueuedInputs[0]->Block();
     if (!curBlock->IsReadyForHandling()) {
       break;
     }
 
-    INPQ_LOG("processing input block %p; preventDefault %d target %p\n",
+    INPQ_LOG("processing input from block %p; preventDefault %d target %p\n",
         curBlock, curBlock->IsDefaultPrevented(),
         curBlock->GetTargetApzc().get());
     RefPtr<AsyncPanZoomController> target = curBlock->GetTargetApzc();
     // target may be null here if the initial target was unconfirmed and then
     // we later got a confirmed null target. in that case drop the events.
-    if (!target) {
-      curBlock->DropEvents();
-    } else if (curBlock->IsDefaultPrevented()) {
-      curBlock->DropEvents();
-      if (curBlock->AsTouchBlock()) {
-        target->ResetTouchInputState();
+    if (target) {
+      if (curBlock->IsDefaultPrevented()) {
+        if (curBlock->AsTouchBlock()) {
+          target->ResetTouchInputState();
+        }
+      } else {
+        UpdateActiveApzc(target);
+        curBlock->DispatchEvent(*(mQueuedInputs[0]->Input()));
       }
-    } else {
-      UpdateActiveApzc(curBlock->GetTargetApzc());
-      curBlock->HandleEvents();
     }
-    MOZ_ASSERT(!curBlock->HasEvents());
+    mQueuedInputs.RemoveElementAt(0);
+  }
 
-    if (mInputBlockQueue.Length() == 1 && curBlock->MustStayActive()) {
-      // Some types of blocks (e.g. touch blocks) accumulate events until the
-      // next input block is started. Therefore we cannot remove the block from
-      // the queue until we have started another block. This block will be
-      // removed by SweepDeletedBlocks() whenever a new block is added.
-      break;
-    }
+  if (CanDiscardBlock(mActiveTouchBlock)) {
+    mActiveTouchBlock = nullptr;
+  }
+  if (CanDiscardBlock(mActiveWheelBlock)) {
+    mActiveWheelBlock = nullptr;
+  }
+  if (CanDiscardBlock(mActiveDragBlock)) {
+    mActiveDragBlock = nullptr;
+  }
+  if (CanDiscardBlock(mActivePanGestureBlock)) {
+    mActivePanGestureBlock = nullptr;
+  }
+}
 
-    // If we get here, we know there are more touch blocks in the queue after
-    // |curBlock|, so we can remove |curBlock| and try to process the next one.
-    INPQ_LOG("discarding processed %s block %p\n", curBlock->Type(), curBlock);
-    mInputBlockQueue.RemoveElementAt(0);
-  } while (!mInputBlockQueue.IsEmpty());
+bool
+InputQueue::CanDiscardBlock(CancelableBlockState* aBlock)
+{
+  if (!aBlock ||
+      !aBlock->IsReadyForHandling() ||
+      aBlock->MustStayActive()) {
+    return false;
+  }
+  InputData* firstInput = nullptr;
+  FindBlockForId(aBlock->GetBlockId(), &firstInput);
+  if (firstInput) {
+    // The block has at least one input event still in the queue, so it's
+    // not depleted
+    return false;
+  }
+  return true;
 }
 
 void
 InputQueue::UpdateActiveApzc(const RefPtr<AsyncPanZoomController>& aNewActive) {
   if (mLastActiveApzc && mLastActiveApzc != aNewActive
       && mTouchCounter.GetActiveTouchCount() > 0) {
     mLastActiveApzc->ResetTouchInputState();
   }
   mLastActiveApzc = aNewActive;
 }
 
 void
 InputQueue::Clear()
 {
   APZThreadUtils::AssertOnControllerThread();
 
-  mInputBlockQueue.Clear();
+  mQueuedInputs.Clear();
+  mActiveTouchBlock = nullptr;
+  mActiveWheelBlock = nullptr;
+  mActiveDragBlock = nullptr;
+  mActivePanGestureBlock = nullptr;
   mLastActiveApzc = nullptr;
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/apz/src/InputQueue.h
+++ b/gfx/layers/apz/src/InputQueue.h
@@ -25,21 +25,21 @@ namespace layers {
 
 class AsyncPanZoomController;
 class CancelableBlockState;
 class TouchBlockState;
 class WheelBlockState;
 class DragBlockState;
 class PanGestureBlockState;
 class AsyncDragMetrics;
+class QueuedInput;
 
 /**
- * This class stores incoming input events, separated into "input blocks", until
- * they are ready for handling. Currently input blocks are only created from
- * touch input.
+ * This class stores incoming input events, associated with "input blocks", until
+ * they are ready for handling.
  */
 class InputQueue {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(InputQueue)
 
 public:
   InputQueue();
 
   /**
@@ -85,38 +85,43 @@ public:
    * Adds a new touch block at the end of the input queue that has the same
    * allowed touch behaviour flags as the the touch block currently being
    * processed. This should only be called when processing of a touch block
    * triggers the creation of a new touch block. Returns the input block id
    * of the the newly-created block.
    */
   uint64_t InjectNewTouchBlock(AsyncPanZoomController* aTarget);
   /**
-   * Returns the pending input block at the head of the queue.
-   */
-  CancelableBlockState* CurrentBlock() const;
-  /**
-   * Returns the current pending input block as a specific kind of block.
-   * These methods must only be called if the current pending block is of the
-   * requested type.
+   * Returns the pending input block at the head of the queue, if there is one.
+   * This may return null if there all input events have been processed.
    */
-  TouchBlockState* CurrentTouchBlock() const;
-  WheelBlockState* CurrentWheelBlock() const;
-  DragBlockState* CurrentDragBlock() const;
-  PanGestureBlockState* CurrentPanGestureBlock() const;
+  CancelableBlockState* GetCurrentBlock() const;
+  /*
+   * Returns the current pending input block as a specific kind of block. If
+   * GetCurrentBlock() returns null, these functions additionally check the
+   * mActiveXXXBlock field of the corresponding input type to see if there is
+   * a depleted but still active input block, and returns that if found. These
+   * functions may return null if no block is found.
+   */
+  TouchBlockState* GetCurrentTouchBlock() const;
+  WheelBlockState* GetCurrentWheelBlock() const;
+  DragBlockState* GetCurrentDragBlock() const;
+  PanGestureBlockState* GetCurrentPanGestureBlock() const;
   /**
-   * Returns true iff the pending block at the head of the queue is ready for
-   * handling.
+   * Returns true iff the pending block at the head of the queue is a touch
+   * block and is ready for handling.
    */
   bool HasReadyTouchBlock() const;
   /**
-   * If there is a wheel transaction, returns the WheelBlockState representing
-   * the transaction. Otherwise, returns null.
+   * If there is an active wheel transaction, returns the WheelBlockState
+   * representing the transaction. Otherwise, returns null. "Active" in this
+   * function name is the same kind of "active" as in mActiveWheelBlock - that
+   * is, new incoming wheel events will go into the "active" block.
    */
-  WheelBlockState* GetCurrentWheelTransaction() const;
+  WheelBlockState* GetActiveWheelTransaction() const;
   /**
    * Remove all input blocks from the input queue.
    */
   void Clear();
   /**
    * Whether the current pending block allows scroll handoff.
    */
   bool AllowScrollHandoff() const;
@@ -159,37 +164,47 @@ private:
                                         const ScrollWheelInput& aEvent,
                                         uint64_t* aOutInputBlockId);
   nsEventStatus ReceivePanGestureInput(const RefPtr<AsyncPanZoomController>& aTarget,
                                         bool aTargetConfirmed,
                                         const PanGestureInput& aEvent,
                                         uint64_t* aOutInputBlockId);
 
   /**
-   * Remove any blocks that are inactive - not ready, and having no events.
+   * Helper function that searches mQueuedInputs for the first block matching
+   * the given id, and returns it. If |aOutFirstInput| is non-null, it is
+   * populated with a pointer to the first input in mQueuedInputs that
+   * corresponds to the block, or null if no such input was found. Note that
+   * even if there are no inputs in mQueuedInputs, this function can return
+   * non-null if the block id provided matches one of the depleted-but-still-
+   * active blocks (mActiveTouchBlock, mActiveWheelBlock, etc.).
    */
-  void SweepDepletedBlocks();
-
-  /**
-   * Processes the current block if it's ready for handling, using the block's
-   * target APZC.
-   */
-  bool MaybeHandleCurrentBlock(CancelableBlockState* block,
-                               const InputData& aEvent);
-
+  CancelableBlockState* FindBlockForId(uint64_t aInputBlockId,
+                                       InputData** aOutFirstInput);
   void ScheduleMainThreadTimeout(const RefPtr<AsyncPanZoomController>& aTarget,
                                  CancelableBlockState* aBlock);
-  void MainThreadTimeout(const uint64_t& aInputBlockId);
-  void ProcessInputBlocks();
+  void MainThreadTimeout(uint64_t aInputBlockId);
+  void ProcessQueue();
+  bool CanDiscardBlock(CancelableBlockState* aBlock);
   void UpdateActiveApzc(const RefPtr<AsyncPanZoomController>& aNewActive);
 
 private:
-  // The queue of input blocks that have not yet been fully processed.
+  // The queue of input events that have not yet been fully processed.
   // This member must only be accessed on the controller/UI thread.
-  nsTArray<UniquePtr<CancelableBlockState>> mInputBlockQueue;
+  nsTArray<UniquePtr<QueuedInput>> mQueuedInputs;
+
+  // These are the most recently created blocks of each input type. They are
+  // "active" in the sense that new inputs of that type are associated with
+  // them. Note that these pointers may be null if no inputs of the type have
+  // arrived, or if the inputs for the type formed a complete block that was
+  // then discarded.
+  RefPtr<TouchBlockState> mActiveTouchBlock;
+  RefPtr<WheelBlockState> mActiveWheelBlock;
+  RefPtr<DragBlockState> mActiveDragBlock;
+  RefPtr<PanGestureBlockState> mActivePanGestureBlock;
 
   // The APZC to which the last event was delivered
   RefPtr<AsyncPanZoomController> mLastActiveApzc;
 
   // Track touches so we know when to clear mLastActiveApzc
   TouchCounter mTouchCounter;
 
   // Track mouse inputs so we know if we're in a drag or not
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/src/QueuedInput.cpp
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et 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 "QueuedInput.h"
+
+#include "AsyncPanZoomController.h"
+#include "InputBlockState.h"
+#include "InputData.h"
+#include "OverscrollHandoffState.h"
+
+namespace mozilla {
+namespace layers {
+
+QueuedInput::QueuedInput(const MultiTouchInput& aInput, TouchBlockState& aBlock)
+  : mInput(MakeUnique<MultiTouchInput>(aInput))
+  , mBlock(&aBlock)
+{
+}
+
+QueuedInput::QueuedInput(const ScrollWheelInput& aInput, WheelBlockState& aBlock)
+  : mInput(MakeUnique<ScrollWheelInput>(aInput))
+  , mBlock(&aBlock)
+{
+}
+
+QueuedInput::QueuedInput(const MouseInput& aInput, DragBlockState& aBlock)
+  : mInput(MakeUnique<MouseInput>(aInput))
+  , mBlock(&aBlock)
+{
+}
+
+QueuedInput::QueuedInput(const PanGestureInput& aInput, PanGestureBlockState& aBlock)
+  : mInput(MakeUnique<PanGestureInput>(aInput))
+  , mBlock(&aBlock)
+{
+}
+
+InputData*
+QueuedInput::Input()
+{
+  return mInput.get();
+}
+
+CancelableBlockState*
+QueuedInput::Block()
+{
+  return mBlock.get();
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/src/QueuedInput.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et 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_layers_QueuedInput_h
+#define mozilla_layers_QueuedInput_h
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+
+namespace mozilla {
+
+class InputData;
+class MultiTouchInput;
+class ScrollWheelInput;
+class MouseInput;
+class PanGestureInput;
+
+namespace layers {
+
+class CancelableBlockState;
+class TouchBlockState;
+class WheelBlockState;
+class DragBlockState;
+class PanGestureBlockState;
+
+/**
+ * This lightweight class holds a pointer to an input event that has not yet
+ * been completely processed, along with the input block that the input event
+ * is associated with.
+ */
+class QueuedInput
+{
+public:
+  QueuedInput(const MultiTouchInput& aInput, TouchBlockState& aBlock);
+  QueuedInput(const ScrollWheelInput& aInput, WheelBlockState& aBlock);
+  QueuedInput(const MouseInput& aInput, DragBlockState& aBlock);
+  QueuedInput(const PanGestureInput& aInput, PanGestureBlockState& aBlock);
+
+  InputData* Input();
+  CancelableBlockState* Block();
+
+private:
+  // A copy of the input event that is provided to the constructor. This must
+  // be non-null, and is owned by this QueuedInput instance (hence the
+  // UniquePtr).
+  UniquePtr<InputData> mInput;
+  // A pointer to the block that the input event is associated with. This must
+  // be non-null.
+  RefPtr<CancelableBlockState> mBlock;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_QueuedInput_h
--- a/gfx/layers/apz/test/gtest/InputUtils.h
+++ b/gfx/layers/apz/test/gtest/InputUtils.h
@@ -257,9 +257,40 @@ SmoothWheel(const RefPtr<InputReceiver>&
             const ScreenPoint& aDelta, TimeStamp aTime, uint64_t* aOutInputBlockId = nullptr)
 {
   ScrollWheelInput input(MillisecondsSinceStartup(aTime), aTime, 0,
       ScrollWheelInput::SCROLLMODE_SMOOTH, ScrollWheelInput::SCROLLDELTA_LINE,
       aPoint, aDelta.x, aDelta.y, false);
   return aTarget->ReceiveInputEvent(input, nullptr, aOutInputBlockId);
 }
 
+template<class InputReceiver>
+nsEventStatus
+MouseDown(const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aPoint,
+          TimeStamp aTime, uint64_t* aOutInputBlockId = nullptr)
+{
+  MouseInput input(MouseInput::MOUSE_DOWN, MouseInput::ButtonType::LEFT_BUTTON,
+        0, 0, aPoint, MillisecondsSinceStartup(aTime), aTime, 0);
+  return aTarget->ReceiveInputEvent(input, nullptr, aOutInputBlockId);
+}
+
+template<class InputReceiver>
+nsEventStatus
+MouseMove(const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aPoint,
+          TimeStamp aTime, uint64_t* aOutInputBlockId = nullptr)
+{
+  MouseInput input(MouseInput::MOUSE_MOVE, MouseInput::ButtonType::LEFT_BUTTON,
+        0, 0, aPoint, MillisecondsSinceStartup(aTime), aTime, 0);
+  return aTarget->ReceiveInputEvent(input, nullptr, aOutInputBlockId);
+}
+
+template<class InputReceiver>
+nsEventStatus
+MouseUp(const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aPoint,
+        TimeStamp aTime, uint64_t* aOutInputBlockId = nullptr)
+{
+  MouseInput input(MouseInput::MOUSE_UP, MouseInput::ButtonType::LEFT_BUTTON,
+        0, 0, aPoint, MillisecondsSinceStartup(aTime), aTime, 0);
+  return aTarget->ReceiveInputEvent(input, nullptr, aOutInputBlockId);
+}
+
+
 #endif // mozilla_layers_InputUtils_h
--- a/gfx/layers/apz/test/gtest/TestGestureDetector.cpp
+++ b/gfx/layers/apz/test/gtest/TestGestureDetector.cpp
@@ -592,20 +592,21 @@ TEST_F(APZCGestureDetectorTester, TapFol
   mti.mTouches.AppendElement(SingleTouchData(inputId, ParentLayerPoint(20, 20), ScreenSize(0, 0), 0, 0));
   mti.mTouches.AppendElement(SingleTouchData(inputId + 1, ParentLayerPoint(10, 10), ScreenSize(0, 0), 0, 0));
   apzc->ReceiveInputEvent(mti, nullptr);
 
   apzc->AssertStateIsReset();
 }
 
 TEST_F(APZCGestureDetectorTester, LongPressInterruptedByWheel) {
-  // Since the wheel block interrupted the long-press, we don't expect
-  // any long-press notifications. However, this also shouldn't crash, which
-  // is what it used to do.
-  EXPECT_CALL(*mcc, HandleTap(TapType::eLongTap, _, _, _, _)).Times(0);
+  // Since we try to allow concurrent input blocks of different types to
+  // co-exist, the wheel block shouldn't interrupt the long-press detection.
+  // But more importantly, this shouldn't crash, which is what it did at one
+  // point in time.
+  EXPECT_CALL(*mcc, HandleTap(TapType::eLongTap, _, _, _, _)).Times(1);
 
   uint64_t touchBlockId = 0;
   uint64_t wheelBlockId = 0;
   nsEventStatus status = TouchDown(apzc, ScreenIntPoint(10, 10), mcc->Time(), &touchBlockId);
   if (gfxPrefs::TouchActionEnabled() && status != nsEventStatus_eConsumeNoDefault) {
     SetDefaultAllowedTouchBehavior(apzc, touchBlockId);
   }
   mcc->AdvanceByMillis(10);
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/test/gtest/TestInputQueue.cpp
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et 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 "APZCTreeManagerTester.h"
+#include "APZTestCommon.h"
+#include "InputUtils.h"
+
+// Test of scenario described in bug 1269067 - that a continuing mouse drag
+// doesn't interrupt a wheel scrolling animation
+TEST_F(APZCTreeManagerTester, WheelInterruptedByMouseDrag) {
+  // Set up a scrollable layer
+  CreateSimpleScrollingLayer();
+  ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
+  manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
+  RefPtr<TestAsyncPanZoomController> apzc = ApzcOf(root);
+
+  uint64_t dragBlockId = 0;
+  uint64_t wheelBlockId = 0;
+  uint64_t tmpBlockId = 0;
+
+  // First start the mouse drag
+  MouseDown(apzc, ScreenIntPoint(5, 5), mcc->Time(), &dragBlockId);
+  MouseMove(apzc, ScreenIntPoint(6, 6), mcc->Time(), &tmpBlockId);
+  EXPECT_EQ(dragBlockId, tmpBlockId);
+
+  // Insert the wheel event, check that it has a new block id
+  SmoothWheel(apzc, ScreenIntPoint(6, 6), ScreenPoint(0, 1), mcc->Time(), &wheelBlockId);
+  EXPECT_NE(dragBlockId, wheelBlockId);
+
+  // Continue the drag, check that the block id is the same as before
+  MouseMove(apzc, ScreenIntPoint(7, 5), mcc->Time(), &tmpBlockId);
+  EXPECT_EQ(dragBlockId, tmpBlockId);
+
+  // Finish the wheel animation
+  apzc->AdvanceAnimationsUntilEnd();
+
+  // Check that it scrolled
+  ParentLayerPoint scroll = apzc->GetCurrentAsyncScrollOffset(AsyncPanZoomController::NORMAL);
+  EXPECT_EQ(scroll.x, 0);
+  EXPECT_EQ(scroll.y, 10); // We scrolled 1 "line" or 10 pixels
+}
--- a/gfx/layers/apz/test/gtest/moz.build
+++ b/gfx/layers/apz/test/gtest/moz.build
@@ -4,16 +4,17 @@
 # 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/.
 
 UNIFIED_SOURCES += [
     'TestBasic.cpp',
     'TestEventRegions.cpp',
     'TestGestureDetector.cpp',
     'TestHitTesting.cpp',
+    'TestInputQueue.cpp',
     'TestPanning.cpp',
     'TestPinching.cpp',
     'TestScrollHandoff.cpp',
     'TestSnapping.cpp',
     'TestTreeManager.cpp',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -276,16 +276,17 @@ UNIFIED_SOURCES += [
     'apz/src/CheckerboardEvent.cpp',
     'apz/src/DragTracker.cpp',
     'apz/src/GestureEventListener.cpp',
     'apz/src/HitTestingTreeNode.cpp',
     'apz/src/InputBlockState.cpp',
     'apz/src/InputQueue.cpp',
     'apz/src/OverscrollHandoffState.cpp',
     'apz/src/PotentialCheckerboardDurationTracker.cpp',
+    'apz/src/QueuedInput.cpp',
     'apz/src/TouchCounter.cpp',
     'apz/src/WheelScrollAnimation.cpp',
     'apz/testutil/APZTestData.cpp',
     'apz/util/ActiveElementManager.cpp',
     'apz/util/APZCCallbackHelper.cpp',
     'apz/util/APZEventState.cpp',
     'apz/util/APZThreadUtils.cpp',
     'apz/util/CheckerboardReportService.cpp',
--- a/gfx/vr/gfxVROculus.cpp
+++ b/gfx/vr/gfxVROculus.cpp
@@ -315,21 +315,21 @@ FromFovPort(const ovrFovPort& aFOV)
 } // namespace
 
 VRDisplayOculus::VRDisplayOculus(ovrSession aSession)
   : VRDisplayHost(VRDisplayType::Oculus)
   , mSession(aSession)
   , mTextureSet(nullptr)
   , mQuadVS(nullptr)
   , mQuadPS(nullptr)
+  , mLinearSamplerState(nullptr)
   , mVSConstantBuffer(nullptr)
   , mPSConstantBuffer(nullptr)
   , mVertexBuffer(nullptr)
   , mInputLayout(nullptr)
-  , mLinearSamplerState(nullptr)
   , mIsPresenting(false)
 {
   MOZ_COUNT_CTOR_INHERITED(VRDisplayOculus, VRDisplayHost);
 
   mDisplayInfo.mDisplayName.AssignLiteral("Oculus VR HMD");
   mDisplayInfo.mIsConnected = true;
 
   mDesc = ovr_GetHmdDesc(aSession);
@@ -347,19 +347,16 @@ VRDisplayOculus::VRDisplayOculus(ovrSess
   mDisplayInfo.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_Present;
 
   mFOVPort[VRDisplayInfo::Eye_Left] = mDesc.DefaultEyeFov[ovrEye_Left];
   mFOVPort[VRDisplayInfo::Eye_Right] = mDesc.DefaultEyeFov[ovrEye_Right];
 
   mDisplayInfo.mEyeFOV[VRDisplayInfo::Eye_Left] = FromFovPort(mFOVPort[VRDisplayInfo::Eye_Left]);
   mDisplayInfo.mEyeFOV[VRDisplayInfo::Eye_Right] = FromFovPort(mFOVPort[VRDisplayInfo::Eye_Right]);
 
-  uint32_t w = mDesc.Resolution.w;
-  uint32_t h = mDesc.Resolution.h;
-
   float pixelsPerDisplayPixel = 1.0;
   ovrSizei texSize[2];
 
   // get eye parameters and create the mesh
   for (uint32_t eye = 0; eye < VRDisplayInfo::NumEyes; eye++) {
 
     ovrEyeRenderDesc renderDesc = ovr_GetRenderDesc(mSession, (ovrEyeType)eye, mFOVPort[eye]);
 
--- a/js/src/asmjs/WasmBaselineCompile.cpp
+++ b/js/src/asmjs/WasmBaselineCompile.cpp
@@ -2141,18 +2141,18 @@ class BaseCompiler
 
         const SigWithId& sig = mg_.sigs[sigIndex];
 
         CalleeDesc callee;
         if (isCompilingAsmJS()) {
             MOZ_ASSERT(sig.id.kind() == SigIdDesc::Kind::None);
             const TableDesc& table = mg_.tables[mg_.asmJSSigToTableIndex[sigIndex]];
 
-            MOZ_ASSERT(IsPowerOfTwo(table.initial));
-            masm.andPtr(Imm32((table.initial - 1)), WasmTableCallIndexReg);
+            MOZ_ASSERT(IsPowerOfTwo(table.limits.initial));
+            masm.andPtr(Imm32((table.limits.initial - 1)), WasmTableCallIndexReg);
 
             callee = CalleeDesc::asmJSTable(table);
         } else {
             MOZ_ASSERT(sig.id.kind() != SigIdDesc::Kind::None);
             MOZ_ASSERT(mg_.tables.length() == 1);
             const TableDesc& table = mg_.tables[0];
 
             callee = CalleeDesc::wasmTable(table, sig.id);
--- a/js/src/asmjs/WasmCode.cpp
+++ b/js/src/asmjs/WasmCode.cpp
@@ -31,16 +31,17 @@
 #ifdef JS_ION_PERF
 # include "jit/PerfSpewer.h"
 #endif
 #include "vm/StringBuffer.h"
 #ifdef MOZ_VTUNE
 # include "vtune/VTuneWrapper.h"
 #endif
 
+#include "jit/MacroAssembler-inl.h"
 #include "vm/ArrayBufferObject-inl.h"
 
 using namespace js;
 using namespace js::jit;
 using namespace js::wasm;
 using mozilla::Atomic;
 using mozilla::BinarySearch;
 using mozilla::MakeEnumeratedRange;
--- a/js/src/asmjs/WasmCompile.cpp
+++ b/js/src/asmjs/WasmCompile.cpp
@@ -628,73 +628,67 @@ DecodeName(Decoder& d)
         return nullptr;
 
     memcpy(name.get(), bytes, numBytes);
     name[numBytes] = '\0';
 
     return name;
 }
 
-struct Resizable
-{
-    uint32_t initial;
-    Maybe<uint32_t> maximum;
-};
-
 static bool
-DecodeResizable(Decoder& d, Resizable* resizable)
+DecodeResizable(Decoder& d, ResizableLimits* limits)
 {
     uint32_t flags;
     if (!d.readVarU32(&flags))
         return Fail(d, "expected flags");
 
     if (flags & ~uint32_t(ResizableFlags::AllowedMask))
         return Fail(d, "unexpected bits set in flags");
 
     if (!(flags & uint32_t(ResizableFlags::Default)))
         return Fail(d, "currently, every memory/table must be declared default");
 
-    if (!d.readVarU32(&resizable->initial))
+    if (!d.readVarU32(&limits->initial))
         return Fail(d, "expected initial length");
 
     if (flags & uint32_t(ResizableFlags::HasMaximum)) {
         uint32_t maximum;
         if (!d.readVarU32(&maximum))
             return Fail(d, "expected maximum length");
 
-        if (resizable->initial > maximum)
+        if (limits->initial > maximum)
             return Fail(d, "maximum length less than initial length");
 
-        resizable->maximum.emplace(maximum);
+        limits->maximum.emplace(maximum);
     }
 
     return true;
 }
 
 static bool
 DecodeResizableMemory(Decoder& d, ModuleGeneratorData* init)
 {
     if (UsesMemory(init->memoryUsage))
         return Fail(d, "already have default memory");
 
-    Resizable resizable;
-    if (!DecodeResizable(d, &resizable))
+    ResizableLimits limits;
+    if (!DecodeResizable(d, &limits))
         return false;
 
     init->memoryUsage = MemoryUsage::Unshared;
 
-    CheckedInt<uint32_t> initialBytes = resizable.initial;
+    CheckedInt<uint32_t> initialBytes = limits.initial;
     initialBytes *= PageSize;
     if (!initialBytes.isValid() || initialBytes.value() > uint32_t(INT32_MAX))
         return Fail(d, "initial memory size too big");
 
     init->minMemoryLength = initialBytes.value();
 
-    if (resizable.maximum) {
-        CheckedInt<uint32_t> maximumBytes = *resizable.maximum;
+    if (limits.maximum) {
+        CheckedInt<uint32_t> maximumBytes = *limits.maximum;
         maximumBytes *= PageSize;
         if (!maximumBytes.isValid())
             return Fail(d, "maximum memory size too big");
 
         init->maxMemoryLength = Some(maximumBytes.value());
     }
 
     return true;
@@ -705,28 +699,24 @@ DecodeResizableTable(Decoder& d, ModuleG
 {
     uint32_t elementType;
     if (!d.readVarU32(&elementType))
         return Fail(d, "expected table element type");
 
     if (elementType != uint32_t(TypeConstructor::AnyFunc))
         return Fail(d, "expected 'anyfunc' element type");
 
-    Resizable resizable;
-    if (!DecodeResizable(d, &resizable))
+    ResizableLimits limits;
+    if (!DecodeResizable(d, &limits))
         return false;
 
     if (!init->tables.empty())
         return Fail(d, "already have default table");
 
-    TableDesc table;
-    table.kind = TableKind::AnyFunction;
-    table.initial = resizable.initial;
-    table.maximum = resizable.maximum ? *resizable.maximum : UINT32_MAX;
-    return init->tables.append(table);
+    return init->tables.emplaceBack(TableKind::AnyFunction, limits);
 }
 
 static bool
 DecodeGlobalType(Decoder& d, ValType* type, bool* isMutable)
 {
     if (!d.readValType(type))
         return Fail(d, "bad global type");
 
@@ -869,42 +859,41 @@ DecodeTableSection(Decoder& d, bool newF
         return Fail(d, "failed to start section");
     if (sectionStart == Decoder::NotStarted)
         return true;
 
     if (newFormat) {
         if (!DecodeResizableTable(d, init))
             return false;
     } else {
-        TableDesc table;
-        table.kind = TableKind::AnyFunction;
-        table.maximum = UINT32_MAX;
-
-        if (!d.readVarU32(&table.initial))
+        ResizableLimits limits;
+        if (!d.readVarU32(&limits.initial))
             return Fail(d, "expected number of table elems");
 
-        if (table.initial > MaxTableElems)
+        if (limits.initial > MaxTableElems)
             return Fail(d, "too many table elements");
 
-        if (!oldElems->resize(table.initial))
+        limits.maximum = Some(limits.initial);
+
+        if (!oldElems->resize(limits.initial))
             return false;
 
-        for (uint32_t i = 0; i < table.initial; i++) {
+        for (uint32_t i = 0; i < limits.initial; i++) {
             uint32_t funcDefIndex;
             if (!d.readVarU32(&funcDefIndex))
                 return Fail(d, "expected table element");
 
             if (funcDefIndex >= init->funcDefSigs.length())
                 return Fail(d, "table element out of range");
 
             (*oldElems)[i] = init->funcImports.length() + funcDefIndex;
         }
 
         MOZ_ASSERT(init->tables.empty());
-        if (!init->tables.append(table))
+        if (!init->tables.emplaceBack(TableKind::AnyFunction, limits))
             return false;
     }
 
     if (!d.finishSection(sectionStart, sectionSize))
         return Fail(d, "table section byte size mismatch");
 
     return true;
 }
@@ -1359,17 +1348,17 @@ DecodeElemSection(Decoder& d, bool newFo
         InitExpr offset;
         if (!DecodeInitializerExpression(d, mg.globals(), ValType::I32, &offset))
             return false;
 
         uint32_t numElems;
         if (!d.readVarU32(&numElems))
             return Fail(d, "expected segment size");
 
-        uint32_t tableLength = mg.tables()[tableIndex].initial;
+        uint32_t tableLength = mg.tables()[tableIndex].limits.initial;
         if (offset.isVal()) {
             uint32_t off = offset.val().i32();
             if (off > tableLength || tableLength - off < numElems)
                 return Fail(d, "element segment does not fit");
         }
 
         Uint32Vector elemFuncIndices;
         if (!elemFuncIndices.resize(numElems))
--- a/js/src/asmjs/WasmGenerator.cpp
+++ b/js/src/asmjs/WasmGenerator.cpp
@@ -152,17 +152,17 @@ ModuleGenerator::init(UniqueModuleGenera
             if (import.kind == DefinitionKind::Table) {
                 MOZ_ASSERT(shared_->tables.length() == 1);
                 shared_->tables[0].external = true;
                 break;
             }
         }
 
         for (TableDesc& table : shared_->tables) {
-            if (!allocateGlobalBytes(sizeof(void*), sizeof(void*), &table.globalDataOffset))
+            if (!allocateGlobalBytes(sizeof(TableTls), sizeof(void*), &table.globalDataOffset))
                 return false;
         }
 
         for (uint32_t i = 0; i < numSigs_; i++) {
             SigWithId& sig = shared_->sigs[i];
             if (SigIdDesc::isGlobal(sig)) {
                 uint32_t globalDataOffset;
                 if (!allocateGlobalBytes(sizeof(void*), sizeof(void*), &globalDataOffset))
@@ -1011,32 +1011,30 @@ ModuleGenerator::initSigTableLength(uint
     MOZ_ASSERT(isAsmJS());
     MOZ_ASSERT(length != 0);
     MOZ_ASSERT(length <= MaxTableElems);
 
     MOZ_ASSERT(shared_->asmJSSigToTableIndex[sigIndex] == 0);
     shared_->asmJSSigToTableIndex[sigIndex] = numTables_;
 
     TableDesc& table = shared_->tables[numTables_++];
-    MOZ_ASSERT(table.globalDataOffset == 0);
-    MOZ_ASSERT(table.initial == 0);
     table.kind = TableKind::TypedFunction;
-    table.initial = length;
-    table.maximum = UINT32_MAX;
-    return allocateGlobalBytes(sizeof(void*), sizeof(void*), &table.globalDataOffset);
+    table.limits.initial = length;
+    table.limits.maximum = Some(length);
+    return allocateGlobalBytes(sizeof(TableTls), sizeof(void*), &table.globalDataOffset);
 }
 
 bool
 ModuleGenerator::initSigTableElems(uint32_t sigIndex, Uint32Vector&& elemFuncDefIndices)
 {
     MOZ_ASSERT(isAsmJS());
     MOZ_ASSERT(finishedFuncDefs_);
 
     uint32_t tableIndex = shared_->asmJSSigToTableIndex[sigIndex];
-    MOZ_ASSERT(shared_->tables[tableIndex].initial == elemFuncDefIndices.length());
+    MOZ_ASSERT(shared_->tables[tableIndex].limits.initial == elemFuncDefIndices.length());
 
     Uint32Vector codeRangeIndices;
     if (!codeRangeIndices.resize(elemFuncDefIndices.length()))
         return false;
     for (size_t i = 0; i < elemFuncDefIndices.length(); i++) {
         codeRangeIndices[i] = funcDefIndexToCodeRange_[elemFuncDefIndices[i]];
         elemFuncDefIndices[i] += numFuncImports();
     }
--- a/js/src/asmjs/WasmInstance.cpp
+++ b/js/src/asmjs/WasmInstance.cpp
@@ -83,37 +83,37 @@ class SigIdSet
             js_delete(p->key());
             map_.remove(p);
         }
     }
 };
 
 ExclusiveData<SigIdSet> sigIdSet;
 
-void**
-Instance::addressOfTableBase(size_t tableIndex) const
-{
-    MOZ_ASSERT(metadata().tables[tableIndex].globalDataOffset >= InitialGlobalDataBytes);
-    return (void**)(codeSegment().globalData() + metadata().tables[tableIndex].globalDataOffset);
-}
-
 const void**
 Instance::addressOfSigId(const SigIdDesc& sigId) const
 {
     MOZ_ASSERT(sigId.globalDataOffset() >= InitialGlobalDataBytes);
     return (const void**)(codeSegment().globalData() + sigId.globalDataOffset());
 }
 
 FuncImportTls&
 Instance::funcImportTls(const FuncImport& fi)
 {
     MOZ_ASSERT(fi.tlsDataOffset() >= InitialGlobalDataBytes);
     return *(FuncImportTls*)(codeSegment().globalData() + fi.tlsDataOffset());
 }
 
+TableTls&
+Instance::tableTls(const TableDesc& td) const
+{
+    MOZ_ASSERT(td.globalDataOffset >= InitialGlobalDataBytes);
+    return *(TableTls*)(codeSegment().globalData() + td.globalDataOffset);
+}
+
 bool
 Instance::callImport(JSContext* cx, uint32_t funcImportIndex, unsigned argc, const uint64_t* argv,
                      MutableHandleValue rval)
 {
     const FuncImport& fi = metadata().funcImports[funcImportIndex];
 
     InvokeArgs args(cx);
     if (!args.init(argc))
@@ -342,16 +342,23 @@ Instance::Instance(JSContext* cx,
         } else {
             import.tls = &tlsData_;
             import.code = codeBase() + fi.interpExitCodeOffset();
             import.baselineScript = nullptr;
             import.obj = f;
         }
     }
 
+    for (size_t i = 0; i < tables_.length(); i++) {
+        const TableDesc& td = metadata().tables[i];
+        TableTls& table = tableTls(td);
+        table.length = tables_[i]->length();
+        table.base = tables_[i]->base();
+    }
+
     uint8_t* globalData = code_->segment().globalData();
 
     for (size_t i = 0; i < metadata().globals.length(); i++) {
         const GlobalDesc& global = metadata().globals[i];
         if (global.isConstant())
             continue;
 
         uint8_t* globalAddr = globalData + global.offset();
@@ -375,27 +382,29 @@ Instance::Instance(JSContext* cx,
             }
             break;
           }
           case GlobalKind::Constant: {
             MOZ_CRASH("skipped at the top");
           }
         }
     }
-
-    for (size_t i = 0; i < tables_.length(); i++)
-        *addressOfTableBase(i) = tables_[i]->base();
 }
 
 bool
 Instance::init(JSContext* cx)
 {
     if (memory_ && memory_->movingGrowable() && !memory_->addMovingGrowObserver(cx, object_))
         return false;
 
+    for (const SharedTable& table : tables_) {
+        if (table->movingGrowable() && !table->addMovingGrowObserver(cx, object_))
+            return false;
+    }
+
     if (!metadata().sigIds.empty()) {
         ExclusiveData<SigIdSet>::Guard lockedSigIdSet = sigIdSet.lock();
 
         if (!lockedSigIdSet->ensureInitialized(cx))
             return false;
 
         for (const SigWithId& sig : metadata().sigIds) {
             const void* sigId;
@@ -791,25 +800,35 @@ Instance::callExport(JSContext* cx, uint
 
     if (retObj)
         args.rval().set(ObjectValue(*retObj));
 
     return true;
 }
 
 void
-Instance::onMovingGrow(uint8_t* prevMemoryBase)
+Instance::onMovingGrowMemory(uint8_t* prevMemoryBase)
 {
     MOZ_ASSERT(!isAsmJS());
     ArrayBufferObject& buffer = memory_->buffer().as<ArrayBufferObject>();
     tlsData_.memoryBase = buffer.dataPointer();
     code_->segment().onMovingGrow(prevMemoryBase, metadata(), buffer);
 }
 
 void
+Instance::onMovingGrowTable()
+{
+    MOZ_ASSERT(!isAsmJS());
+    MOZ_ASSERT(tables_.length() == 1);
+    TableTls& table = tableTls(metadata().tables[0]);
+    table.length = tables_[0]->length();
+    table.base = tables_[0]->base();
+}
+
+void
 Instance::deoptimizeImportExit(uint32_t funcImportIndex)
 {
     const FuncImport& fi = metadata().funcImports[funcImportIndex];
     FuncImportTls& import = funcImportTls(fi);
     import.code = codeBase() + fi.interpExitCodeOffset();
     import.baselineScript = nullptr;
 }
 
--- a/js/src/asmjs/WasmInstance.h
+++ b/js/src/asmjs/WasmInstance.h
@@ -41,43 +41,35 @@ class Instance
     JSCompartment* const                 compartment_;
     ReadBarrieredWasmInstanceObject      object_;
     const UniqueCode                     code_;
     GCPtrWasmMemoryObject                memory_;
     SharedTableVector                    tables_;
     TlsData                              tlsData_;
 
     // Internal helpers:
-    void** addressOfTableBase(size_t tableIndex) const;
     const void** addressOfSigId(const SigIdDesc& sigId) const;
     FuncImportTls& funcImportTls(const FuncImport& fi);
+    TableTls& tableTls(const TableDesc& td) const;
 
     // Import call slow paths which are called directly from wasm code.
     friend void* AddressOf(SymbolicAddress, ExclusiveContext*);
     static int32_t callImport_void(Instance*, int32_t, int32_t, uint64_t*);
     static int32_t callImport_i32(Instance*, int32_t, int32_t, uint64_t*);
     static int32_t callImport_i64(Instance*, int32_t, int32_t, uint64_t*);
     static int32_t callImport_f64(Instance*, int32_t, int32_t, uint64_t*);
     static uint32_t growMemory_i32(Instance* instance, uint32_t delta);
     static uint32_t currentMemory_i32(Instance* instance);
     bool callImport(JSContext* cx, uint32_t funcImportIndex, unsigned argc, const uint64_t* argv,
                     MutableHandleValue rval);
 
     // Only WasmInstanceObject can call the private trace function.
     friend class js::WasmInstanceObject;
     void tracePrivate(JSTracer* trc);
 
-    // Only WasmMemoryObject can call the private onMovingGrow notification.
-    friend class js::WasmMemoryObject;
-    void onMovingGrow(uint8_t* prevMemoryBase);
-
-    // Called by WasmTableObject to barrier table writes.
-    friend class Table;
-    WasmInstanceObject* objectUnbarriered() const;
-
   public:
     Instance(JSContext* cx,
              HandleWasmInstanceObject object,
              UniqueCode code,
              HandleWasmMemoryObject memory,
              SharedTableVector&& tables,
              Handle<FunctionVector> funcImports,
              const ValVector& globalImports);
@@ -97,19 +89,21 @@ class Instance
     SharedMem<uint8_t*> memoryBase() const;
     size_t memoryLength() const;
     size_t memoryMappedSize() const;
     bool memoryAccessInGuardRegion(uint8_t* addr, unsigned numBytes) const;
     TlsData& tlsData() { return tlsData_; }
 
     // This method returns a pointer to the GC object that owns this Instance.
     // Instances may be reached via weak edges (e.g., Compartment::instances_)
-    // so this perform a read-barrier on the returned object.
+    // so this perform a read-barrier on the returned object unless the barrier
+    // is explicitly waived.
 
     WasmInstanceObject* object() const;
+    WasmInstanceObject* objectUnbarriered() const;
 
     // Execute the given export given the JS call arguments, storing the return
     // value in args.rval.
 
     MOZ_MUST_USE bool callExport(JSContext* cx, uint32_t funcDefIndex, CallArgs args);
 
     // Initially, calls to imports in wasm code call out through the generic
     // callImport method. If the imported callee gets JIT compiled and the types
@@ -119,16 +113,21 @@ class Instance
 
     void deoptimizeImportExit(uint32_t funcImportIndex);
 
     // Called by simulators to check whether accessing 'numBytes' starting at
     // 'addr' would trigger a fault and be safely handled by signal handlers.
 
     bool memoryAccessWouldFault(uint8_t* addr, unsigned numBytes);
 
+    // Called by Wasm(Memory|Table)Object when a moving resize occurs:
+
+    void onMovingGrowMemory(uint8_t* prevMemoryBase);
+    void onMovingGrowTable();
+
     // See Code::ensureProfilingState comment.
 
     MOZ_MUST_USE bool ensureProfilingState(JSContext* cx, bool enabled);
 
     // about:memory reporting:
 
     void addSizeOfMisc(MallocSizeOf mallocSizeOf,
                        Metadata::SeenSet* seenMetadata,
--- a/js/src/asmjs/WasmIonCompile.cpp
+++ b/js/src/asmjs/WasmIonCompile.cpp
@@ -1005,21 +1005,21 @@ class FunctionCompiler
         }
 
         const SigWithId& sig = mg_.sigs[sigIndex];
 
         CalleeDesc callee;
         if (mg_.isAsmJS()) {
             MOZ_ASSERT(sig.id.kind() == SigIdDesc::Kind::None);
             const TableDesc& table = mg_.tables[mg_.asmJSSigToTableIndex[sigIndex]];
-            MOZ_ASSERT(IsPowerOfTwo(table.initial));
+            MOZ_ASSERT(IsPowerOfTwo(table.limits.initial));
             MOZ_ASSERT(!table.external);
             MOZ_ASSERT(call.tlsStackOffset_ == MWasmCall::DontSaveTls);
 
-            MConstant* mask = MConstant::New(alloc(), Int32Value(table.initial - 1));
+            MConstant* mask = MConstant::New(alloc(), Int32Value(table.limits.initial - 1));
             curBlock_->add(mask);
             MBitAnd* maskedIndex = MBitAnd::NewAsmJS(alloc(), index, mask, MIRType::Int32);
             curBlock_->add(maskedIndex);
 
             index = maskedIndex;
             callee = CalleeDesc::asmJSTable(table);
         } else {
             MOZ_ASSERT(sig.id.kind() != SigIdDesc::Kind::None);
--- a/js/src/asmjs/WasmJS.cpp
+++ b/js/src/asmjs/WasmJS.cpp
@@ -322,16 +322,78 @@ js::InitWasmClass(JSContext* cx, HandleO
     if (!JS_DefineFunctions(cx, Wasm, wasm_static_methods))
         return nullptr;
 
     global->as<GlobalObject>().setConstructor(JSProto_Wasm, ObjectValue(*Wasm));
     return Wasm;
 }
 
 // ============================================================================
+// Common functions
+
+static bool
+ToNonWrappingUint32(JSContext* cx, HandleValue v, uint32_t max, const char* kind, const char* noun,
+                    uint32_t* u32)
+{
+    double dbl;
+    if (!ToInteger(cx, v, &dbl))
+        return false;
+
+    if (dbl < 0 || dbl > max) {
+        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_UINT32, kind, noun);
+        return false;
+    }
+
+    *u32 = uint32_t(dbl);
+    MOZ_ASSERT(double(*u32) == dbl);
+    return true;
+}
+
+static bool
+GetResizableLimits(JSContext* cx, HandleObject obj, uint32_t max, const char* kind,
+                   ResizableLimits* limits)
+{
+    JSAtom* initialAtom = Atomize(cx, "initial", strlen("initial"));
+    if (!initialAtom)
+        return false;
+    RootedId initialId(cx, AtomToId(initialAtom));
+
+    RootedValue initialVal(cx);
+    if (!GetProperty(cx, obj, obj, initialId, &initialVal))
+        return false;
+
+    if (!ToNonWrappingUint32(cx, initialVal, max, kind, "initial size", &limits->initial))
+        return false;
+
+    JSAtom* maximumAtom = Atomize(cx, "maximum", strlen("maximum"));
+    if (!maximumAtom)
+        return false;
+    RootedId maximumId(cx, AtomToId(maximumAtom));
+
+    bool found;
+    if (HasProperty(cx, obj, maximumId, &found) && found) {
+        RootedValue maxVal(cx);
+        if (!GetProperty(cx, obj, obj, maximumId, &maxVal))
+            return false;
+
+        limits->maximum.emplace();
+        if (!ToNonWrappingUint32(cx, maxVal, max, kind, "maximum size", limits->maximum.ptr()))
+            return false;
+
+        if (limits->initial > *limits->maximum) {
+            JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_UINT32,
+                                 kind, "maximum size");
+            return false;
+        }
+    }
+
+    return true;
+}
+
+// ============================================================================
 // WebAssembly.Module class and methods
 
 const ClassOps WasmModuleObject::classOps_ =
 {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
@@ -768,63 +830,27 @@ WasmMemoryObject::construct(JSContext* c
     if (!args.requireAtLeast(cx, "WebAssembly.Memory", 1))
         return false;
 
     if (!args.get(0).isObject()) {
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_DESC_ARG, "memory");
         return false;
     }
 
-    JSAtom* initialAtom = Atomize(cx, "initial", strlen("initial"));
-    if (!initialAtom)
-        return false;
-    RootedId initialId(cx, AtomToId(initialAtom));
-
-    JSAtom* maximumAtom = Atomize(cx, "maximum", strlen("maximum"));
-    if (!maximumAtom)
-        return false;
-    RootedId maximumId(cx, AtomToId(maximumAtom));
-
     RootedObject obj(cx, &args[0].toObject());
-    RootedValue initialVal(cx);
-    if (!GetProperty(cx, obj, obj, initialId, &initialVal))
-        return false;
-
-    double initialDbl;
-    if (!ToInteger(cx, initialVal, &initialDbl))
+    ResizableLimits limits;
+    if (!GetResizableLimits(cx, obj, UINT32_MAX / PageSize, "Memory", &limits))
         return false;
 
-    if (initialDbl < 0 || initialDbl > INT32_MAX / PageSize) {
-        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_SIZE, "Memory", "initial");
-        return false;
-    }
-
-    Maybe<uint32_t> maxSize;
-
-    bool found;
-    if (HasProperty(cx, obj, maximumId, &found) && found) {
-        RootedValue maxVal(cx);
-        if (!GetProperty(cx, obj, obj, maximumId, &maxVal))
-            return false;
+    limits.initial *= PageSize;
+    if (limits.maximum)
+        limits.maximum = Some(*limits.maximum * PageSize);
 
-        double maxDbl;
-        if (!ToInteger(cx, maxVal, &maxDbl))
-            return false;
-
-        if (maxDbl < initialDbl || maxDbl > UINT32_MAX / PageSize) {
-            JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_SIZE, "Memory",
-                                 "maximum");
-            return false;
-        }
-
-        maxSize = Some<uint32_t>(uint32_t(maxDbl) * PageSize);
-    }
-
-    uint32_t initialSize = uint32_t(initialDbl) * PageSize;
-    RootedArrayBufferObject buffer(cx, ArrayBufferObject::createForWasm(cx, initialSize, maxSize));
+    RootedArrayBufferObject buffer(cx,
+        ArrayBufferObject::createForWasm(cx, limits.initial, limits.maximum));
     if (!buffer)
         return false;
 
     RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmMemory).toObject());
     RootedWasmMemoryObject memoryObj(cx, WasmMemoryObject::create(cx, buffer, proto));
     if (!memoryObj)
         return false;
 
@@ -858,29 +884,24 @@ const JSPropertySpec WasmMemoryObject::p
     JS_PS_END
 };
 
 /* static */ bool
 WasmMemoryObject::growImpl(JSContext* cx, const CallArgs& args)
 {
     RootedWasmMemoryObject memory(cx, &args.thisv().toObject().as<WasmMemoryObject>());
 
-    double deltaDbl;
-    if (!ToInteger(cx, args.get(0), &deltaDbl))
+    uint32_t delta;
+    if (!ToNonWrappingUint32(cx, args.get(0), UINT32_MAX, "Memory", "grow delta", &delta))
         return false;
 
-    if (deltaDbl < 0 || deltaDbl > UINT32_MAX) {
-        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_GROW);
-        return false;
-    }
-
-    uint32_t ret = grow(memory, uint32_t(deltaDbl), cx);
+    uint32_t ret = grow(memory, delta, cx);
 
     if (ret == uint32_t(-1)) {
-        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_GROW);
+        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_GROW, "memory");
         return false;
     }
 
     args.rval().setInt32(ret);
     return true;
 }
 
 /* static */ bool
@@ -995,17 +1016,17 @@ WasmMemoryObject::grow(HandleWasmMemoryO
 
     memory->setReservedSlot(BUFFER_SLOT, ObjectValue(*newBuf));
 
     // Only notify moving-grow-observers after the BUFFER_SLOT has been updated
     // since observers will call buffer().
     if (memory->hasObservers()) {
         MOZ_ASSERT(prevMemoryBase);
         for (InstanceSet::Range r = memory->observers().all(); !r.empty(); r.popFront())
-            r.front()->instance().onMovingGrow(prevMemoryBase);
+            r.front()->instance().onMovingGrowMemory(prevMemoryBase);
     }
 
     return oldNumPages;
 }
 
 // ============================================================================
 // WebAssembly.Table class and methods
 
@@ -1053,34 +1074,31 @@ WasmTableObject::finalize(FreeOp* fop, J
 WasmTableObject::trace(JSTracer* trc, JSObject* obj)
 {
     WasmTableObject& tableObj = obj->as<WasmTableObject>();
     if (!tableObj.isNewborn())
         tableObj.table().tracePrivate(trc);
 }
 
 /* static */ WasmTableObject*
-WasmTableObject::create(JSContext* cx, uint32_t length)
+WasmTableObject::create(JSContext* cx, ResizableLimits limits)
 {
     RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmTable).toObject());
 
     AutoSetNewObjectMetadata metadata(cx);
     RootedWasmTableObject obj(cx, NewObjectWithGivenProto<WasmTableObject>(cx, proto));
     if (!obj)
         return nullptr;
 
     MOZ_ASSERT(obj->isNewborn());
 
-    TableDesc desc;
-    desc.kind = TableKind::AnyFunction;
-    desc.external = true;
-    desc.initial = length;
-    desc.maximum = length;
+    TableDesc td(TableKind::AnyFunction, limits);
+    td.external = true;
 
-    SharedTable table = Table::create(cx, desc, obj);
+    SharedTable table = Table::create(cx, td, obj);
     if (!table)
         return nullptr;
 
     obj->initReservedSlot(TABLE_SLOT, PrivateValue(table.forget().take()));
 
     MOZ_ASSERT(!obj->isNewborn());
     return obj;
 }
@@ -1097,65 +1115,45 @@ WasmTableObject::construct(JSContext* cx
         return false;
 
     if (!args.get(0).isObject()) {
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_DESC_ARG, "table");
         return false;
     }
 
     RootedObject obj(cx, &args[0].toObject());
-    RootedId id(cx);
-    RootedValue val(cx);
 
     JSAtom* elementAtom = Atomize(cx, "element", strlen("element"));
     if (!elementAtom)
         return false;
-    id = AtomToId(elementAtom);
-    if (!GetProperty(cx, obj, obj, id, &val))
+    RootedId elementId(cx, AtomToId(elementAtom));
+
+    RootedValue elementVal(cx);
+    if (!GetProperty(cx, obj, obj, elementId, &elementVal))
         return false;
 
-    if (!val.isString()) {
-        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_ELEMENT);
-        return false;
-    }
-
-    JSLinearString* str = val.toString()->ensureLinear(cx);
-    if (!str)
-        return false;
-
-    if (!StringEqualsAscii(str, "anyfunc")) {
+    if (!elementVal.isString()) {
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_ELEMENT);
         return false;
     }
 
-    JSAtom* initialAtom = Atomize(cx, "initial", strlen("initial"));
-    if (!initialAtom)
-        return false;
-    id = AtomToId(initialAtom);
-    if (!GetProperty(cx, obj, obj, id, &val))
+    JSLinearString* elementStr = elementVal.toString()->ensureLinear(cx);
+    if (!elementStr)
         return false;
 
-    double initialDbl;
-    if (!ToInteger(cx, val, &initialDbl))
-        return false;
-
-    if (initialDbl < 0 || initialDbl > INT32_MAX) {
-        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_SIZE, "Table", "initial");
+    if (!StringEqualsAscii(elementStr, "anyfunc")) {
+        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_ELEMENT);
         return false;
     }
 
-    uint32_t initial = uint32_t(initialDbl);
-    MOZ_ASSERT(double(initial) == initialDbl);
+    ResizableLimits limits;
+    if (!GetResizableLimits(cx, obj, UINT32_MAX, "Table", &limits))
+        return false;
 
-    if (initial > MaxTableElems) {
-        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_SIZE, "Table", "initial");
-        return false;
-    }
-
-    RootedWasmTableObject table(cx, WasmTableObject::create(cx, initial));
+    RootedWasmTableObject table(cx, WasmTableObject::create(cx, limits));
     if (!table)
         return false;
 
     args.rval().setObject(*table);
     return true;
 }
 
 static bool
@@ -1185,28 +1183,20 @@ const JSPropertySpec WasmTableObject::pr
 };
 
 /* static */ bool
 WasmTableObject::getImpl(JSContext* cx, const CallArgs& args)
 {
     RootedWasmTableObject tableObj(cx, &args.thisv().toObject().as<WasmTableObject>());
     const Table& table = tableObj->table();
 
-    double indexDbl;
-    if (!ToInteger(cx, args.get(0), &indexDbl))
+    uint32_t index;
+    if (!ToNonWrappingUint32(cx, args.get(0), table.length() - 1, "Table", "get index", &index))
         return false;
 
-    if (indexDbl < 0 || indexDbl >= table.length()) {
-        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
-        return false;
-    }
-
-    uint32_t index = uint32_t(indexDbl);
-    MOZ_ASSERT(double(index) == indexDbl);
-
     ExternalTableElem& elem = table.externalArray()[index];
     if (!elem.code) {
         args.rval().setNull();
         return true;
     }
 
     Instance& instance = *elem.tls->instance;
     const CodeRange& codeRange = *instance.code().lookupRange(elem.code);
@@ -1232,28 +1222,20 @@ WasmTableObject::get(JSContext* cx, unsi
 WasmTableObject::setImpl(JSContext* cx, const CallArgs& args)
 {
     RootedWasmTableObject tableObj(cx, &args.thisv().toObject().as<WasmTableObject>());
     Table& table = tableObj->table();
 
     if (!args.requireAtLeast(cx, "set", 2))
         return false;
 
-    double indexDbl;
-    if (!ToInteger(cx, args[0], &indexDbl))
+    uint32_t index;
+    if (!ToNonWrappingUint32(cx, args.get(0), table.length() - 1, "Table", "set index", &index))
         return false;
 
-    if (indexDbl < 0 || indexDbl >= table.length()) {
-        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
-        return false;
-    }
-
-    uint32_t index = uint32_t(indexDbl);
-    MOZ_ASSERT(double(index) == indexDbl);
-
     RootedFunction value(cx);
     if (!IsExportedFunction(args[1], &value) && !args[1].isNull()) {
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_TABLE_VALUE);
         return false;
     }
 
     if (value) {
         RootedWasmInstanceObject instanceObj(cx, ExportedFunctionToInstanceObject(value));
@@ -1280,20 +1262,48 @@ WasmTableObject::setImpl(JSContext* cx, 
 
 /* static */ bool
 WasmTableObject::set(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<IsTable, setImpl>(cx, args);
 }
 
+/* static */ bool
+WasmTableObject::growImpl(JSContext* cx, const CallArgs& args)
+{
+    RootedWasmTableObject table(cx, &args.thisv().toObject().as<WasmTableObject>());
+
+    uint32_t delta;
+    if (!ToNonWrappingUint32(cx, args.get(0), UINT32_MAX, "Table", "grow delta", &delta))
+        return false;
+
+    uint32_t ret = table->table().grow(delta, cx);
+
+    if (ret == uint32_t(-1)) {
+        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_GROW, "table");
+        return false;
+    }
+
+    args.rval().setInt32(ret);
+    return true;
+}
+
+/* static */ bool
+WasmTableObject::grow(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    return CallNonGenericMethod<IsTable, growImpl>(cx, args);
+}
+
 const JSFunctionSpec WasmTableObject::methods[] =
 {
     JS_FN("get", WasmTableObject::get, 1, 0),
     JS_FN("set", WasmTableObject::set, 2, 0),
+    JS_FN("grow", WasmTableObject::grow, 1, 0),
     JS_FS_END
 };
 
 Table&
 WasmTableObject::table() const
 {
     return *(Table*)getReservedSlot(TABLE_SLOT).toPrivate();
 }
--- a/js/src/asmjs/WasmJS.h
+++ b/js/src/asmjs/WasmJS.h
@@ -205,26 +205,28 @@ class WasmTableObject : public NativeObj
     static void finalize(FreeOp* fop, JSObject* obj);
     static void trace(JSTracer* trc, JSObject* obj);
     static bool lengthGetterImpl(JSContext* cx, const CallArgs& args);
     static bool lengthGetter(JSContext* cx, unsigned argc, Value* vp);
     static bool getImpl(JSContext* cx, const CallArgs& args);
     static bool get(JSContext* cx, unsigned argc, Value* vp);
     static bool setImpl(JSContext* cx, const CallArgs& args);
     static bool set(JSContext* cx, unsigned argc, Value* vp);
+    static bool growImpl(JSContext* cx, const CallArgs& args);
+    static bool grow(JSContext* cx, unsigned argc, Value* vp);
 
   public:
     static const unsigned RESERVED_SLOTS = 1;
     static const Class class_;
     static const JSPropertySpec properties[];
     static const JSFunctionSpec methods[];
     static bool construct(JSContext*, unsigned, Value*);
 
     // Note that, after creation, a WasmTableObject's table() is not initialized
     // and must be initialized before use.
 
-    static WasmTableObject* create(JSContext* cx, uint32_t length);
+    static WasmTableObject* create(JSContext* cx, wasm::ResizableLimits limits);
     wasm::Table& table() const;
 };
 
 } // namespace js
 
 #endif // wasm_js_h
--- a/js/src/asmjs/WasmModule.cpp
+++ b/js/src/asmjs/WasmModule.cpp
@@ -538,16 +538,41 @@ Module::instantiateFunctions(JSContext* 
             JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_IMPORT_SIG);
             return false;
         }
     }
 
     return true;
 }
 
+static bool
+CheckResizableLimits(JSContext* cx, uint32_t declaredMin, Maybe<uint32_t> declaredMax,
+                     uint32_t actualLength, Maybe<uint32_t> actualMax,
+                     bool isAsmJS, const char* kind)
+{
+    if (isAsmJS) {
+        MOZ_ASSERT(actualLength >= declaredMin);
+        MOZ_ASSERT(!declaredMax);
+        MOZ_ASSERT(actualLength == actualMax.value());
+        return true;
+    }
+
+    if (actualLength < declaredMin || actualLength > declaredMax.valueOr(UINT32_MAX)) {
+        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_IMP_SIZE, kind);
+        return false;
+    }
+
+    if ((actualMax && (!declaredMax || *actualMax > *declaredMax)) || (!actualMax && declaredMax)) {
+        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_IMP_MAX, kind);
+        return false;
+    }
+
+    return true;
+}
+
 // asm.js module instantiation supplies its own buffer, but for wasm, create and
 // initialize the buffer if one is requested. Either way, the buffer is wrapped
 // in a WebAssembly.Memory object which is what the Instance stores.
 bool
 Module::instantiateMemory(JSContext* cx, MutableHandleWasmMemoryObject memory) const
 {
     if (!metadata_->usesMemory()) {
         MOZ_ASSERT(!memory);
@@ -558,32 +583,21 @@ Module::instantiateMemory(JSContext* cx,
     uint32_t declaredMin = metadata_->minMemoryLength;
     Maybe<uint32_t> declaredMax = metadata_->maxMemoryLength;
 
     if (memory) {
         ArrayBufferObjectMaybeShared& buffer = memory->buffer();
         MOZ_ASSERT_IF(metadata_->isAsmJS(), buffer.isPreparedForAsmJS());
         MOZ_ASSERT_IF(!metadata_->isAsmJS(), buffer.as<ArrayBufferObject>().isWasm());
 
-        uint32_t actualLength = buffer.byteLength();
-        if (actualLength < declaredMin || actualLength > declaredMax.valueOr(UINT32_MAX)) {
-            JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_IMP_SIZE, "Memory");
+        if (!CheckResizableLimits(cx, declaredMin, declaredMax,
+                                  buffer.byteLength(), buffer.wasmMaxSize(),
+                                  metadata_->isAsmJS(), "Memory")) {
             return false;
         }
-
-        if (metadata_->isAsmJS()) {
-            MOZ_ASSERT(IsValidAsmJSHeapLength(actualLength));
-            MOZ_ASSERT(actualLength == buffer.wasmMaxSize().value());
-        } else {
-            Maybe<uint32_t> actualMax = buffer.as<ArrayBufferObject>().wasmMaxSize();
-            if (declaredMax.isSome() != actualMax.isSome() || declaredMax < actualMax) {
-                JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_IMP_SIZE, "Memory");
-                return false;
-            }
-        }
     } else {
         MOZ_ASSERT(!metadata_->isAsmJS());
         MOZ_ASSERT(metadata_->memoryUsage == MemoryUsage::Unshared);
 
         RootedArrayBufferObjectMaybeShared buffer(cx,
             ArrayBufferObject::createForWasm(cx, declaredMin, declaredMax));
         if (!buffer)
             return false;
@@ -603,43 +617,44 @@ Module::instantiateMemory(JSContext* cx,
 bool
 Module::instantiateTable(JSContext* cx, MutableHandleWasmTableObject tableObj,
                          SharedTableVector* tables) const
 {
     if (tableObj) {
         MOZ_ASSERT(!metadata_->isAsmJS());
 
         MOZ_ASSERT(metadata_->tables.length() == 1);
-        const TableDesc& tableDesc = metadata_->tables[0];
-        MOZ_ASSERT(tableDesc.external);
+        const TableDesc& td = metadata_->tables[0];
+        MOZ_ASSERT(td.external);
 
         Table& table = tableObj->table();
-        if (table.length() < tableDesc.initial || table.length() > tableDesc.maximum) {
-            JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_IMP_SIZE, "Table");
+        if (!CheckResizableLimits(cx, td.limits.initial, td.limits.maximum,
+                                  table.length(), table.maximum(),
+                                  metadata_->isAsmJS(), "Table")) {
             return false;
         }
 
         if (!tables->append(&table)) {
             ReportOutOfMemory(cx);
             return false;
         }
     } else {
-        for (const TableDesc& tableDesc : metadata_->tables) {
+        for (const TableDesc& td : metadata_->tables) {
             SharedTable table;
-            if (tableDesc.external) {
+            if (td.external) {
                 MOZ_ASSERT(!tableObj);
-                MOZ_ASSERT(tableDesc.kind == TableKind::AnyFunction);
+                MOZ_ASSERT(td.kind == TableKind::AnyFunction);
 
-                tableObj.set(WasmTableObject::create(cx, tableDesc.initial));
+                tableObj.set(WasmTableObject::create(cx, td.limits));
                 if (!tableObj)
                     return false;
 
                 table = &tableObj->table();
             } else {
-                table = Table::create(cx, tableDesc, /* HandleWasmTableObject = */ nullptr);
+                table = Table::create(cx, td, /* HandleWasmTableObject = */ nullptr);
                 if (!table)
                     return false;
             }
 
             if (!tables->emplaceBack(table)) {
                 ReportOutOfMemory(cx);
                 return false;
             }
--- a/js/src/asmjs/WasmSignalHandlers.cpp
+++ b/js/src/asmjs/WasmSignalHandlers.cpp
@@ -1106,21 +1106,16 @@ HandleFault(int signum, siginfo_t* info,
     // The signals we're expecting come from access violations, accessing
     // mprotected memory. If the signal originates anywhere else, don't try
     // to handle it.
     if (signal == Signal::SegFault)
         MOZ_RELEASE_ASSERT(signum == SIGSEGV);
     else
         MOZ_RELEASE_ASSERT(signum == SIGBUS);
 
-    if (signal == Signal::SegFault && info->si_code != SEGV_ACCERR)
-        return false;
-    if (signal == Signal::BusError && info->si_code != BUS_ADRALN)
-        return false;
-
     CONTEXT* context = (CONTEXT*)ctx;
     uint8_t** ppc = ContextToPC(context);
     uint8_t* pc = *ppc;
 
     // Don't allow recursive handling of signals, see AutoSetHandlingSegFault.
     JSRuntime* rt = RuntimeForCurrentThread();
     if (!rt || rt->handlingSegFault)
         return false;
@@ -1131,20 +1126,34 @@ HandleFault(int signum, siginfo_t* info,
         return false;
 
     const Instance* instance = activation->compartment()->wasm.lookupInstanceDeprecated(pc);
     if (!instance || !instance->codeSegment().containsFunctionPC(pc))
         return false;
 
     uint8_t* faultingAddress = reinterpret_cast<uint8_t*>(info->si_addr);
 
-    // This check isn't necessary, but, since we can, check anyway to make
-    // sure we aren't covering up a real bug.
-    if (!IsHeapAccessAddress(*instance, faultingAddress))
+    // Although it's not strictly necessary, to make sure we're not covering up
+    // any real bugs, check that the faulting address is indeed in the
+    // instance's memory.
+    if (!faultingAddress) {
+        // On some Linux systems, the kernel apparently sometimes "gives up" and
+        // passes a null faultingAddress with si_code set to SI_KERNEL.
+        // This is observed on some automation machines for some out-of-bounds
+        // atomic accesses on x86/64.
+#ifdef SI_KERNEL
+        if (info->si_code != SI_KERNEL)
+            return false;
+#else
         return false;
+#endif
+    } else {
+        if (!IsHeapAccessAddress(*instance, faultingAddress))
+            return false;
+    }
 
 #ifdef WASM_HUGE_MEMORY
     return HugeMemoryAccess(context, pc, faultingAddress, *instance, ppc);
 #elif defined(JS_CODEGEN_ARM)
     MOZ_RELEASE_ASSERT(signal == Signal::BusError || signal == Signal::SegFault);
     if (signal == Signal::BusError)
         *ppc = instance->codeSegment().unalignedAccessCode();
     else
--- a/js/src/asmjs/WasmTable.cpp
+++ b/js/src/asmjs/WasmTable.cpp
@@ -13,48 +13,53 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include "asmjs/WasmTable.h"
 
+#include "mozilla/CheckedInt.h"
+
 #include "jscntxt.h"
 
 #include "asmjs/WasmInstance.h"
 #include "asmjs/WasmJS.h"
 
 using namespace js;
 using namespace js::wasm;
+using mozilla::CheckedInt;
+
+Table::Table(JSContext* cx, const TableDesc& desc, HandleWasmTableObject maybeObject,
+             UniqueByteArray array)
+  : maybeObject_(maybeObject),
+    observers_(cx->zone(), InstanceSet()),
+    array_(Move(array)),
+    kind_(desc.kind),
+    length_(desc.limits.initial),
+    maximum_(desc.limits.maximum),
+    external_(desc.external)
+{}
 
 /* static */ SharedTable
 Table::create(JSContext* cx, const TableDesc& desc, HandleWasmTableObject maybeObject)
 {
-    SharedTable table = cx->new_<Table>();
-    if (!table)
-        return nullptr;
-
     // The raw element type of a Table depends on whether it is external: an
     // external table can contain functions from multiple instances and thus
     // must store an additional instance pointer in each element.
-    void* array;
+    UniqueByteArray array;
     if (desc.external)
-        array = cx->pod_calloc<ExternalTableElem>(desc.initial);
+        array.reset((uint8_t*)cx->pod_calloc<ExternalTableElem>(desc.limits.initial));
     else
-        array = cx->pod_calloc<void*>(desc.initial);
+        array.reset((uint8_t*)cx->pod_calloc<void*>(desc.limits.initial));
     if (!array)
         return nullptr;
 
-    table->maybeObject_.set(maybeObject);
-    table->array_.reset((uint8_t*)array);
-    table->kind_ = desc.kind;
-    table->length_ = desc.initial;
-    table->external_ = desc.external;
-    return table;
+    return SharedTable(cx->new_<Table>(cx, desc, maybeObject, Move(array)));
 }
 
 void
 Table::tracePrivate(JSTracer* trc)
 {
     // If this table has a WasmTableObject, then this method is only called by
     // WasmTableObject's trace hook so maybeObject_ must already be marked.
     // TraceEdge is called so that the pointer can be updated during a moving
@@ -128,13 +133,79 @@ Table::setNull(uint32_t index)
     ExternalTableElem& elem = externalArray()[index];
     if (elem.tls)
         JSObject::writeBarrierPre(elem.tls->instance->objectUnbarriered());
 
     elem.code = nullptr;
     elem.tls = nullptr;
 }
 
+uint32_t
+Table::grow(uint32_t delta, JSContext* cx)
+{
+    // This isn't just an optimization: movingGrowable() assumes that
+    // onMovingGrowTable does not fire when length == maximum.
+    if (!delta)
+        return length_;
+
+    uint32_t oldLength = length_;
+
+    CheckedInt<uint32_t> newLength = oldLength;
+    newLength += delta;
+    if (!newLength.isValid())
+        return -1;
+
+    if (maximum_ && newLength.value() > maximum_.value())
+        return -1;
+
+    MOZ_ASSERT(movingGrowable());
+
+    JSRuntime* rt = cx;  // Use JSRuntime's MallocProvider to avoid throwing.
+
+    // Note that realloc does not release array_'s pointee (which is returned by
+    // externalArray()) on failure which is exactly what we need here.
+    ExternalTableElem* newArray = rt->pod_realloc(externalArray(), length_, newLength.value());
+    if (!newArray)
+        return -1;
+    Unused << array_.release();
+    array_.reset((uint8_t*)newArray);
+
+    // Realloc does not zero the delta for us.
+    PodZero(newArray + length_, delta);
+    length_ = newLength.value();
+
+    if (observers_.initialized()) {
+        for (InstanceSet::Range r = observers_.all(); !r.empty(); r.popFront())
+            r.front()->instance().onMovingGrowTable();
+    }
+
+    return oldLength;
+}
+
+bool
+Table::movingGrowable() const
+{
+    return !maximum_ || length_ < maximum_.value();
+}
+
+bool
+Table::addMovingGrowObserver(JSContext* cx, WasmInstanceObject* instance)
+{
+    MOZ_ASSERT(movingGrowable());
+
+    if (!observers_.initialized() && !observers_.init()) {
+        ReportOutOfMemory(cx);
+        return false;
+    }
+
+    if (!observers_.putNew(instance)) {
+        ReportOutOfMemory(cx);
+        return false;
+    }
+
+    return true;
+}
+
 size_t
 Table::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
 {
     return mallocSizeOf(array_.get());
 }
--- a/js/src/asmjs/WasmTable.h
+++ b/js/src/asmjs/WasmTable.h
@@ -15,56 +15,71 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef wasm_table_h
 #define wasm_table_h
 
 #include "asmjs/WasmCode.h"
+#include "gc/Policy.h"
 
 namespace js {
 namespace wasm {
 
 // A Table is an indexable array of opaque values. Tables are first-class
 // stateful objects exposed to WebAssembly. asm.js also uses Tables to represent
 // its homogeneous function-pointer tables.
 
 class Table : public ShareableBase<Table>
 {
+    using InstanceSet = GCHashSet<ReadBarrieredWasmInstanceObject,
+                                  MovableCellHasher<ReadBarrieredWasmInstanceObject>,
+                                  SystemAllocPolicy>;
     typedef UniquePtr<uint8_t[], JS::FreePolicy> UniqueByteArray;
 
     ReadBarrieredWasmTableObject maybeObject_;
+    JS::WeakCache<InstanceSet>   observers_;
     UniqueByteArray              array_;
-    TableKind                    kind_;
+    const TableKind              kind_;
     uint32_t                     length_;
-    bool                         external_;
+    const Maybe<uint32_t>        maximum_;
+    const bool                   external_;
+
+    template <class> friend struct js::MallocProvider;
+    Table(JSContext* cx, const TableDesc& td, HandleWasmTableObject maybeObject,
+          UniqueByteArray array);
 
     void tracePrivate(JSTracer* trc);
     friend class js::WasmTableObject;
 
   public:
     static RefPtr<Table> create(JSContext* cx, const TableDesc& desc,
                                 HandleWasmTableObject maybeObject);
     void trace(JSTracer* trc);
 
     bool external() const { return external_; }
     bool isTypedFunction() const { return kind_ == TableKind::TypedFunction; }
     uint32_t length() const { return length_; }
+    Maybe<uint32_t> maximum() const { return maximum_; }
     uint8_t* base() const { return array_.get(); }
 
     // All updates must go through a set() function with the exception of
     // (profiling) updates to the callee pointer that do not change which
     // logical function is being called.
 
     void** internalArray() const;
     ExternalTableElem* externalArray() const;
     void set(uint32_t index, void* code, Instance& instance);
     void setNull(uint32_t index);
 
+    uint32_t grow(uint32_t delta, JSContext* cx);
+    bool movingGrowable() const;
+    bool addMovingGrowObserver(JSContext* cx, WasmInstanceObject* instance);
+
     // about:memory reporting:
 
     size_t sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const;
 };
 
 typedef RefPtr<Table> SharedTable;
 typedef Vector<SharedTable, 0, SystemAllocPolicy> SharedTableVector;
 
--- a/js/src/asmjs/WasmTypes.h
+++ b/js/src/asmjs/WasmTypes.h
@@ -940,156 +940,51 @@ struct Assumptions
 // A Module can either be asm.js or wasm.
 
 enum ModuleKind
 {
     Wasm,
     AsmJS
 };
 
+// Represents the resizable limits of memories and tables.
+
+struct ResizableLimits
+{
+    uint32_t initial;
+    Maybe<uint32_t> maximum;
+};
+
 // TableDesc describes a table as well as the offset of the table's base pointer
 // in global memory. Currently, wasm only has "any function" and asm.js only
 // "typed function".
 
 enum class TableKind
 {
     AnyFunction,
     TypedFunction
 };
 
 struct TableDesc
 {
     TableKind kind;
     bool external;
     uint32_t globalDataOffset;
-    uint32_t initial;
-    uint32_t maximum;
+    ResizableLimits limits;
 
-    TableDesc() { PodZero(this); }
+    TableDesc() = default;
+    TableDesc(TableKind kind, ResizableLimits limits)
+     : kind(kind),
+       external(false),
+       globalDataOffset(UINT32_MAX),
+       limits(limits)
+    {}
 };
 
-WASM_DECLARE_POD_VECTOR(TableDesc, TableDescVector)
-
-// CalleeDesc describes how to compile one of the variety of asm.js/wasm calls.
-// This is hoisted into WasmTypes.h for sharing between Ion and Baseline.
-
-class CalleeDesc
-{
-  public:
-    enum Which {
-        // Calls a function defined in the same module by its index.
-        Definition,
-
-        // Calls the import identified by the offset of its FuncImportTls in
-        // thread-local data.
-        Import,
-
-        // Calls a WebAssembly table (heterogeneous, index must be bounds
-        // checked, callee instance depends on TableDesc).
-        WasmTable,
-
-        // Calls an asm.js table (homogeneous, masked index, same-instance).
-        AsmJSTable,
-
-        // Call a C++ function identified by SymbolicAddress.
-        Builtin,
-
-        // Like Builtin, but automatically passes Instance* as first argument.
-        BuiltinInstanceMethod
-    };
-
-  private:
-    Which which_;
-    union U {
-        U() {}
-        uint32_t funcDefIndex_;
-        struct {
-            uint32_t globalDataOffset_;
-        } import;
-        struct {
-            TableDesc desc_;
-            SigIdDesc sigId_;
-        } table;
-        SymbolicAddress builtin_;
-    } u;
-
-  public:
-    CalleeDesc() {}
-    static CalleeDesc definition(uint32_t funcDefIndex) {
-        CalleeDesc c;
-        c.which_ = Definition;
-        c.u.funcDefIndex_ = funcDefIndex;
-        return c;
-    }
-    static CalleeDesc import(uint32_t globalDataOffset) {
-        CalleeDesc c;
-        c.which_ = Import;
-        c.u.import.globalDataOffset_ = globalDataOffset;
-        return c;
-    }
-    static CalleeDesc wasmTable(const TableDesc& desc, SigIdDesc sigId) {
-        CalleeDesc c;
-        c.which_ = WasmTable;
-        c.u.table.desc_ = desc;
-        c.u.table.sigId_ = sigId;
-        return c;
-    }
-    static CalleeDesc asmJSTable(const TableDesc& desc) {
-        CalleeDesc c;
-        c.which_ = AsmJSTable;
-        c.u.table.desc_ = desc;
-        return c;
-    }
-    static CalleeDesc builtin(SymbolicAddress callee) {
-        CalleeDesc c;
-        c.which_ = Builtin;
-        c.u.builtin_ = callee;
-        return c;
-    }
-    static CalleeDesc builtinInstanceMethod(SymbolicAddress callee) {
-        CalleeDesc c;
-        c.which_ = BuiltinInstanceMethod;
-        c.u.builtin_ = callee;
-        return c;
-    }
-    Which which() const {
-        return which_;
-    }
-    uint32_t funcDefIndex() const {
-        MOZ_ASSERT(which_ == Definition);
-        return u.funcDefIndex_;
-    }
-    uint32_t importGlobalDataOffset() const {
-        MOZ_ASSERT(which_ == Import);
-        return u.import.globalDataOffset_;
-    }
-    bool isTable() const {
-        return which_ == WasmTable || which_ == AsmJSTable;
-    }
-    uint32_t tableGlobalDataOffset() const {
-        MOZ_ASSERT(isTable());
-        return u.table.desc_.globalDataOffset;
-    }
-    uint32_t wasmTableLength() const {
-        MOZ_ASSERT(which_ == WasmTable);
-        return u.table.desc_.initial;
-    }
-    bool wasmTableIsExternal() const {
-        MOZ_ASSERT(which_ == WasmTable);
-        return u.table.desc_.external;
-    }
-    SigIdDesc wasmTableSigId() const {
-        MOZ_ASSERT(which_ == WasmTable);
-        return u.table.sigId_;
-    }
-    SymbolicAddress builtin() const {
-        MOZ_ASSERT(which_ == Builtin || which_ == BuiltinInstanceMethod);
-        return u.builtin_;
-    }
-};
+typedef Vector<TableDesc, 0, SystemAllocPolicy> TableDescVector;
 
 // ExportArg holds the unboxed operands to the wasm entry trampoline which can
 // be called through an ExportFuncPtr.
 
 struct ExportArg
 {
     uint64_t lo;
     uint64_t hi;
@@ -1150,34 +1045,168 @@ struct FuncImportTls
 
     // A GC pointer which keeps the callee alive. For imported wasm functions,
     // this points to the wasm function's WasmInstanceObject. For all other
     // imported functions, 'obj' points to the JSFunction.
     GCPtrObject obj;
     static_assert(sizeof(GCPtrObject) == sizeof(void*), "for JIT access");
 };
 
-// When a table can be shared between instances (it is "external"), the internal
-// representation is an array of ExternalTableElem instead of just an array of
-// code pointers.
+// TableTls describes the region of wasm global memory allocated in the
+// instance's thread-local storage which is accessed directly from JIT code
+// to bounds-check and index the table.
+
+struct TableTls
+{
+    // Length of the table in number of elements (not bytes).
+    uint32_t length;
+
+    // Pointer to the array of elements (of type either ExternalTableElem or
+    // void*).
+    void* base;
+};
+
+// When a table can contain functions from other instances (it is "external"),
+// the internal representation is an array of ExternalTableElem instead of just
+// an array of code pointers.
 
 struct ExternalTableElem
 {
     // The code to call when calling this element. The table ABI is the system
     // ABI with the additional ABI requirements that:
     //  - WasmTlsReg and any pinned registers have been loaded appropriately
     //  - if this is a heterogeneous table that requires a signature check,
     //    WasmTableCallSigReg holds the signature id.
     void* code;
 
     // The pointer to the callee's instance's TlsData. This must be loaded into
     // WasmTlsReg before calling 'code'.
     TlsData* tls;
 };
 
+// CalleeDesc describes how to compile one of the variety of asm.js/wasm calls.
+// This is hoisted into WasmTypes.h for sharing between Ion and Baseline.
+
+class CalleeDesc
+{
+  public:
+    enum Which {
+        // Calls a function defined in the same module by its index.
+        Definition,
+
+        // Calls the import identified by the offset of its FuncImportTls in
+        // thread-local data.
+        Import,
+
+        // Calls a WebAssembly table (heterogeneous, index must be bounds
+        // checked, callee instance depends on TableDesc).
+        WasmTable,
+
+        // Calls an asm.js table (homogeneous, masked index, same-instance).
+        AsmJSTable,
+
+        // Call a C++ function identified by SymbolicAddress.
+        Builtin,
+
+        // Like Builtin, but automatically passes Instance* as first argument.
+        BuiltinInstanceMethod
+    };
+
+  private:
+    Which which_;
+    union U {
+        U() {}
+        uint32_t funcDefIndex_;
+        struct {
+            uint32_t globalDataOffset_;
+        } import;
+        struct {
+            uint32_t globalDataOffset_;
+            bool external_;
+            SigIdDesc sigId_;
+        } table;
+        SymbolicAddress builtin_;
+    } u;
+
+  public:
+    CalleeDesc() {}
+    static CalleeDesc definition(uint32_t funcDefIndex) {
+        CalleeDesc c;
+        c.which_ = Definition;
+        c.u.funcDefIndex_ = funcDefIndex;
+        return c;
+    }
+    static CalleeDesc import(uint32_t globalDataOffset) {
+        CalleeDesc c;
+        c.which_ = Import;
+        c.u.import.globalDataOffset_ = globalDataOffset;
+        return c;
+    }
+    static CalleeDesc wasmTable(const TableDesc& desc, SigIdDesc sigId) {
+        CalleeDesc c;
+        c.which_ = WasmTable;
+        c.u.table.globalDataOffset_ = desc.globalDataOffset;
+        c.u.table.external_ = desc.external;
+        c.u.table.sigId_ = sigId;
+        return c;
+    }
+    static CalleeDesc asmJSTable(const TableDesc& desc) {
+        CalleeDesc c;
+        c.which_ = AsmJSTable;
+        c.u.table.globalDataOffset_ = desc.globalDataOffset;
+        return c;
+    }
+    static CalleeDesc builtin(SymbolicAddress callee) {
+        CalleeDesc c;
+        c.which_ = Builtin;
+        c.u.builtin_ = callee;
+        return c;
+    }
+    static CalleeDesc builtinInstanceMethod(SymbolicAddress callee) {
+        CalleeDesc c;
+        c.which_ = BuiltinInstanceMethod;
+        c.u.builtin_ = callee;
+        return c;
+    }
+    Which which() const {
+        return which_;
+    }
+    uint32_t funcDefIndex() const {
+        MOZ_ASSERT(which_ == Definition);
+        return u.funcDefIndex_;
+    }
+    uint32_t importGlobalDataOffset() const {
+        MOZ_ASSERT(which_ == Import);
+        return u.import.globalDataOffset_;
+    }
+    bool isTable() const {
+        return which_ == WasmTable || which_ == AsmJSTable;
+    }
+    uint32_t tableLengthGlobalDataOffset() const {
+        MOZ_ASSERT(isTable());
+        return u.table.globalDataOffset_ + offsetof(TableTls, length);
+    }
+    uint32_t tableBaseGlobalDataOffset() const {
+        MOZ_ASSERT(isTable());
+        return u.table.globalDataOffset_ + offsetof(TableTls, base);
+    }
+    bool wasmTableIsExternal() const {
+        MOZ_ASSERT(which_ == WasmTable);
+        return u.table.external_;
+    }
+    SigIdDesc wasmTableSigId() const {
+        MOZ_ASSERT(which_ == WasmTable);
+        return u.table.sigId_;
+    }
+    SymbolicAddress builtin() const {
+        MOZ_ASSERT(which_ == Builtin || which_ == BuiltinInstanceMethod);
+        return u.builtin_;
+    }
+};
+
 // Because ARM has a fixed-width instruction encoding, ARM can only express a
 // limited subset of immediates (in a single instruction).
 
 extern bool
 IsValidARMImmediate(uint32_t i);
 
 extern uint32_t
 RoundUpToNextValidARMImmediate(uint32_t i);
--- a/js/src/devtools/rootAnalysis/annotations.js
+++ b/js/src/devtools/rootAnalysis/annotations.js
@@ -71,18 +71,18 @@ var ignoreClasses = {
 
 // Ignore calls through TYPE.FIELD, where TYPE is the class or struct name containing
 // a function pointer field named FIELD.
 var ignoreCallees = {
     "js::ClassOps.trace" : true,
     "js::ClassOps.finalize" : true,
     "JSRuntime.destroyPrincipals" : true,
     "icu_50::UObject.__deleting_dtor" : true, // destructors in ICU code can't cause GC
-    "mozilla::CycleCollectedJSRuntime.DescribeCustomObjects" : true, // During tracing, cannot GC.
-    "mozilla::CycleCollectedJSRuntime.NoteCustomGCThingXPCOMChildren" : true, // During tracing, cannot GC.
+    "mozilla::CycleCollectedJSContext.DescribeCustomObjects" : true, // During tracing, cannot GC.
+    "mozilla::CycleCollectedJSContext.NoteCustomGCThingXPCOMChildren" : true, // During tracing, cannot GC.
     "PLDHashTableOps.hashKey" : true,
     "z_stream_s.zfree" : true,
     "z_stream_s.zalloc" : true,
     "GrGLInterface.fCallback" : true,
     "std::strstreambuf._M_alloc_fun" : true,
     "std::strstreambuf._M_free_fun" : true,
     "struct js::gc::Callback<void (*)(JSContext*, void*)>.op" : true,
     "mozilla::ThreadSharedFloatArrayBufferList::Storage.mFree" : true,
--- a/js/src/jit-test/tests/asm.js/testAtomics.js
+++ b/js/src/jit-test/tests/asm.js/testAtomics.js
@@ -303,19 +303,23 @@ function test_int32(heap) {
     assertErrorMessage(() => i32m.and_i(oob), RangeError, /out-of-range index/);
     assertErrorMessage(() => i32m.add_i(oob), RangeError, /out-of-range index/);
     assertErrorMessage(() => i32m.sub_i(oob), RangeError, /out-of-range index/);
     assertErrorMessage(() => i32m.load_i(oob), RangeError, /out-of-range index/);
     assertErrorMessage(() => i32m.store_i(oob), RangeError, /out-of-range index/);
     assertErrorMessage(() => i32m.xchg_i(oob), RangeError, /out-of-range index/);
 
     // Edge cases
-    assertErrorMessage(() => i32m.load_i(i32a.length*4), RangeError, /out-of-range index/);
-    assertErrorMessage(() => i32m.store_i(i32a.length*4), RangeError, /out-of-range index/);
-    assertErrorMessage(() => i32m.add_i(i32a.length*4), RangeError, /out-of-range index/);
+    const INT32_MAX = Math.pow(2, 31);
+    const UINT32_MAX = Math.pow(2, 32);
+    for (var i of [i32a.length*4, INT32_MAX - 4, INT32_MAX, UINT32_MAX - 4]) {
+        assertErrorMessage(() => i32m.load_i(i), RangeError, /out-of-range index/);
+        assertErrorMessage(() => i32m.store_i(i), RangeError, /out-of-range index/);
+        assertErrorMessage(() => i32m.add_i(i), RangeError, /out-of-range index/);
+    }
 
     i32a[i32a.length-1] = 88;
     assertEq(i32m.load_i((i32a.length-1)*4), 88);
     assertEq(i32m.store_i((i32a.length-1)*4), 37);
     assertEq(i32m.add_i((i32a.length-1)*4), 37);
     assertEq(i32m.load_i((i32a.length-1)*4), 37+37);
     i32a[i32a.length-1] = 0;
 }
@@ -583,19 +587,23 @@ function test_uint32(heap) {
     assertErrorMessage(() => i32m.and_i(oob), RangeError, /out-of-range index/);
     assertErrorMessage(() => i32m.add_i(oob), RangeError, /out-of-range index/);
     assertErrorMessage(() => i32m.sub_i(oob), RangeError, /out-of-range index/);
     assertErrorMessage(() => i32m.load_i(oob), RangeError, /out-of-range index/);
     assertErrorMessage(() => i32m.store_i(oob), RangeError, /out-of-range index/);
     assertErrorMessage(() => i32m.xchg_i(oob), RangeError, /out-of-range index/);
 
     // Edge cases
-    assertErrorMessage(() => i32m.load_i(i32a.length*4), RangeError, /out-of-range index/);
-    assertErrorMessage(() => i32m.store_i(i32a.length*4), RangeError, /out-of-range index/);
-    assertErrorMessage(() => i32m.add_i(i32a.length*4), RangeError, /out-of-range index/);
+    const INT32_MAX = Math.pow(2, 31);
+    const UINT32_MAX = Math.pow(2, 32);
+    for (var i of [i32a.length*4, INT32_MAX - 4, INT32_MAX, UINT32_MAX - 4]) {
+        assertErrorMessage(() => i32m.load_i(i), RangeError, /out-of-range index/);
+        assertErrorMessage(() => i32m.store_i(i), RangeError, /out-of-range index/);
+        assertErrorMessage(() => i32m.add_i(i), RangeError, /out-of-range index/);
+    }
 
     i32a[i32a.length-1] = 88;
     assertEq(i32m.load_i((i32a.length-1)*4), 88);
     assertEq(i32m.store_i((i32a.length-1)*4), 37);
     assertEq(i32m.add_i((i32a.length-1)*4), 37);
     assertEq(i32m.load_i((i32a.length-1)*4), 37+37);
     i32a[i32a.length-1] = 0;
 }
@@ -871,19 +879,23 @@ function test_int16(heap) {
     assertErrorMessage(() => i16m.and_i(oob), RangeError, /out-of-range index/);
     assertErrorMessage(() => i16m.add_i(oob), RangeError, /out-of-range index/);
     assertErrorMessage(() => i16m.sub_i(oob), RangeError, /out-of-range index/);
     assertErrorMessage(() => i16m.load_i(oob), RangeError, /out-of-range index/);
     assertErrorMessage(() => i16m.store_i(oob), RangeError, /out-of-range index/);
     assertErrorMessage(() => i16m.xchg_i(oob), RangeError, /out-of-range index/);
 
     // Edge cases
-    assertErrorMessage(() => i16m.load_i(i16a.length*2), RangeError, /out-of-range index/);
-    assertErrorMessage(() => i16m.store_i(i16a.length*2), RangeError, /out-of-range index/);
-    assertErrorMessage(() => i16m.add_i(i16a.length*2), RangeError, /out-of-range index/);
+    const INT32_MAX = Math.pow(2, 31);
+    const UINT32_MAX = Math.pow(2, 32);
+    for (var i of [i16a.length*2, INT32_MAX - 2, INT32_MAX, UINT32_MAX - 2]) {
+        assertErrorMessage(() => i16m.load_i(i), RangeError, /out-of-range index/);
+        assertErrorMessage(() => i16m.store_i(i), RangeError, /out-of-range index/);
+        assertErrorMessage(() => i16m.add_i(i), RangeError, /out-of-range index/);
+    }
 
     i16a[i16a.length-1] = 88;
     assertEq(i16m.load_i((i16a.length-1)*2), 88);
     assertEq(i16m.store_i((i16a.length-1)*2), 37);
     assertEq(i16m.add_i((i16a.length-1)*2), 37);
     assertEq(i16m.load_i((i16a.length-1)*2), 37+37);
     i16a[i16a.length-1] = 0;
 }
@@ -1159,19 +1171,23 @@ function test_uint16(heap) {
     assertErrorMessage(() => i16m.and_i(oob), RangeError, /out-of-range index/);
     assertErrorMessage(() => i16m.add_i(oob), RangeError, /out-of-range index/);
     assertErrorMessage(() => i16m.sub_i(oob), RangeError, /out-of-range index/);
     assertErrorMessage(() => i16m.load_i(oob), RangeError, /out-of-range index/);
     assertErrorMessage(() => i16m.store_i(oob), RangeError, /out-of-range index/);
     assertErrorMessage(() => i16m.xchg_i(oob), RangeError, /out-of-range index/);
 
     // Edge cases
-    assertErrorMessage(() => i16m.load_i(i16a.length*2), RangeError, /out-of-range index/);
-    assertErrorMessage(() => i16m.store_i(i16a.length*2), RangeError, /out-of-range index/);
-    assertErrorMessage(() => i16m.add_i(i16a.length*2), RangeError, /out-of-range index/);
+    const INT32_MAX = Math.pow(2, 31);
+    const UINT32_MAX = Math.pow(2, 32);
+    for (var i of [i16a.length*2, INT32_MAX - 2, INT32_MAX, UINT32_MAX - 2]) {
+        assertErrorMessage(() => i16m.load_i(i), RangeError, /out-of-range index/);
+        assertErrorMessage(() => i16m.store_i(i), RangeError, /out-of-range index/);
+        assertErrorMessage(() => i16m.add_i(i), RangeError, /out-of-range index/);
+    }
 
     i16a[i16a.length-1] = 88;
     assertEq(i16m.load_i((i16a.length-1)*2), 88);
     assertEq(i16m.store_i((i16a.length-1)*2), 37);
     assertEq(i16m.add_i((i16a.length-1)*2), 37);
     assertEq(i16m.load_i((i16a.length-1)*2), 37+37);
     i16a[i16a.length-1] = 0;
 }
@@ -1440,19 +1456,23 @@ function test_int8(heap) {
     assertErrorMessage(() => i8m.and_i(oob), RangeError, /out-of-range index/);
     assertErrorMessage(() => i8m.add_i(oob), RangeError, /out-of-range index/);
     assertErrorMessage(() => i8m.sub_i(oob), RangeError, /out-of-range index/);
     assertErrorMessage(() => i8m.load_i(oob), RangeError, /out-of-range index/);
     assertErrorMessage(() => i8m.store_i(oob), RangeError, /out-of-range index/);
     assertErrorMessage(() => i8m.xchg_i(oob), RangeError, /out-of-range index/);
 
     // Edge cases
-    assertErrorMessage(() => i8m.load_i(i8a.length), RangeError, /out-of-range index/);
-    assertErrorMessage(() => i8m.store_i(i8a.length), RangeError, /out-of-range index/);
-    assertErrorMessage(() => i8m.add_i(i8a.length), RangeError, /out-of-range index/);
+    const INT32_MAX = Math.pow(2, 31);
+    const UINT32_MAX = Math.pow(2, 32);
+    for (var i of [i8a.length, INT32_MAX - 1, INT32_MAX, UINT32_MAX - 1]) {
+        assertErrorMessage(() => i8m.load_i(i), RangeError, /out-of-range index/);
+        assertErrorMessage(() => i8m.store_i(i), RangeError, /out-of-range index/);
+        assertErrorMessage(() => i8m.add_i(i), RangeError, /out-of-range index/);
+    }
 
     i8a[i8a.length-1] = 88;
     assertEq(i8m.load_i(i8a.length-1), 88);
     assertEq(i8m.store_i(i8a.length-1), 37);
     assertEq(i8m.add_i(i8a.length-1), 37);
     assertEq(i8m.load_i(i8a.length-1), 37+37);
     i8a[i8a.length-1] = 0;
 }
@@ -1731,19 +1751,23 @@ function test_uint8(heap) {
     assertErrorMessage(() => i8m.and_i(oob), RangeError, /out-of-range index/);
     assertErrorMessage(() => i8m.add_i(oob), RangeError, /out-of-range index/);
     assertErrorMessage(() => i8m.sub_i(oob), RangeError, /out-of-range index/);
     assertErrorMessage(() => i8m.load_i(oob), RangeError, /out-of-range index/);
     assertErrorMessage(() => i8m.store_i(oob), RangeError, /out-of-range index/);
     assertErrorMessage(() => i8m.xchg_i(oob), RangeError, /out-of-range index/);
 
     // Edge cases
-    assertErrorMessage(() => i8m.load_i(i8a.length), RangeError, /out-of-range index/);
-    assertErrorMessage(() => i8m.store_i(i8a.length), RangeError, /out-of-range index/);
-    assertErrorMessage(() => i8m.add_i(i8a.length), RangeError, /out-of-range index/);
+    const INT32_MAX = Math.pow(2, 31);
+    const UINT32_MAX = Math.pow(2, 32);
+    for (var i of [i8a.length, INT32_MAX - 1, INT32_MAX, UINT32_MAX - 1]) {
+        assertErrorMessage(() => i8m.load_i(i), RangeError, /out-of-range index/);
+        assertErrorMessage(() => i8m.store_i(i), RangeError, /out-of-range index/);
+        assertErrorMessage(() => i8m.add_i(i), RangeError, /out-of-range index/);
+    }
 
     i8a[i8a.length-1] = 88;
     assertEq(i8m.load_i(i8a.length-1), 88);
     assertEq(i8m.store_i(i8a.length-1), 37);
     assertEq(i8m.add_i(i8a.length-1), 37);
     assertEq(i8m.load_i(i8a.length-1), 37+37);
     i8a[i8a.length-1] = 0;
 }
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/asm.js/testBug1302407.js
@@ -0,0 +1,6 @@
+if (!this['SharedArrayBuffer'])
+    quit();
+
+setJitCompilerOption('wasm.test-mode', 1);
+new SharedArrayBuffer(65536);
+setJitCompilerOption('wasm.test-mode', 0)
--- a/js/src/jit-test/tests/wasm/import-export.js
+++ b/js/src/jit-test/tests/wasm/import-export.js
@@ -17,17 +17,17 @@ const mem3Page = new Memory({initial:3})
 const mem3PageMax3 = new Memory({initial:3, maximum: 3});
 const mem4Page = new Memory({initial:4});
 const mem4PageMax4 = new Memory({initial:4, maximum: 4});
 const tab1Elem = new Table({initial:1, element:"anyfunc"});
 const tab2Elem = new Table({initial:2, element:"anyfunc"});
 const tab3Elem = new Table({initial:3, element:"anyfunc"});
 const tab4Elem = new Table({initial:4, element:"anyfunc"});
 
-assertErrorMessage(() => new Memory({initial:2, maximum:1}), TypeError, /bad Memory maximum size/);
+assertErrorMessage(() => new Memory({initial:2, maximum:1}), RangeError, /bad Memory maximum size/);
 
 const m1 = new Module(textToBinary('(module (import "foo" "bar") (import "baz" "quux"))'));
 assertErrorMessage(() => new Instance(m1), TypeError, /no import object given/);
 assertErrorMessage(() => new Instance(m1, {foo:null}), TypeError, /import object field is not an Object/);
 assertErrorMessage(() => new Instance(m1, {foo:{bar:{}}}), TypeError, /import object field is not a Function/);
 assertErrorMessage(() => new Instance(m1, {foo:{bar:()=>{}}, baz:null}), TypeError, /import object field is not an Object/);
 assertErrorMessage(() => new Instance(m1, {foo:{bar:()=>{}}, baz:{}}), TypeError, /import object field is not a Function/);
 assertEq(new Instance(m1, {foo:{bar:()=>{}}, baz:{quux:()=>{}}}) instanceof Instance, true);
@@ -35,57 +35,57 @@ assertEq(new Instance(m1, {foo:{bar:()=>
 const m2 = new Module(textToBinary('(module (import "x" "y" (memory 2 3)))'));
 assertErrorMessage(() => new Instance(m2), TypeError, /no import object given/);
 assertErrorMessage(() => new Instance(m2, {x:null}), TypeError, /import object field is not an Object/);
 assertErrorMessage(() => new Instance(m2, {x:{y:{}}}), TypeError, /import object field is not a Memory/);
 assertErrorMessage(() => new Instance(m2, {x:{y:mem1Page}}), TypeError, /imported Memory with incompatible size/);
 assertErrorMessage(() => new Instance(m2, {x:{y:mem1PageMax1}}), TypeError, /imported Memory with incompatible size/);
 assertErrorMessage(() => new Instance(m2, {x:{y:mem4Page}}), TypeError, /imported Memory with incompatible size/);
 assertErrorMessage(() => new Instance(m2, {x:{y:mem4PageMax4}}), TypeError, /imported Memory with incompatible size/);
-assertErrorMessage(() => new Instance(m2, {x:{y:mem2Page}}), TypeError, /imported Memory with incompatible size/);
+assertErrorMessage(() => new Instance(m2, {x:{y:mem2Page}}), TypeError, /imported Memory with incompatible maximum size/);
 assertEq(new Instance(m2, {x:{y:mem2PageMax2}}) instanceof Instance, true);
-assertErrorMessage(() => new Instance(m2, {x:{y:mem3Page}}), TypeError, /imported Memory with incompatible size/);
+assertErrorMessage(() => new Instance(m2, {x:{y:mem3Page}}), TypeError, /imported Memory with incompatible maximum size/);
 assertEq(new Instance(m2, {x:{y:mem3PageMax3}}) instanceof Instance, true);
 assertEq(new Instance(m2, {x:{y:mem2PageMax3}}) instanceof Instance, true);
-assertErrorMessage(() => new Instance(m2, {x:{y:mem2PageMax4}}), TypeError, /imported Memory with incompatible size/);
+assertErrorMessage(() => new Instance(m2, {x:{y:mem2PageMax4}}), TypeError, /imported Memory with incompatible maximum size/);
 
 const m3 = new Module(textToBinary('(module (import "foo" "bar" (memory 1 1)) (import "baz" "quux"))'));
 assertErrorMessage(() => new Instance(m3), TypeError, /no import object given/);
 assertErrorMessage(() => new Instance(m3, {foo:null}), TypeError, /import object field is not an Object/);
 assertErrorMessage(() => new Instance(m3, {foo:{bar:{}}}), TypeError, /import object field is not a Memory/);
 assertErrorMessage(() => new Instance(m3, {foo:{bar:mem1Page}, baz:null}), TypeError, /import object field is not an Object/);
 assertErrorMessage(() => new Instance(m3, {foo:{bar:mem1Page}, baz:{quux:mem1Page}}), TypeError, /import object field is not a Function/);
-assertErrorMessage(() => new Instance(m3, {foo:{bar:mem1Page}, baz:{quux:()=>{}}}), TypeError, /imported Memory with incompatible size/);
+assertErrorMessage(() => new Instance(m3, {foo:{bar:mem1Page}, baz:{quux:()=>{}}}), TypeError, /imported Memory with incompatible maximum size/);
 assertEq(new Instance(m3, {foo:{bar:mem1PageMax1}, baz:{quux:()=>{}}}) instanceof Instance, true);
 
 const m4 = new Module(textToBinary('(module (import "baz" "quux") (import "foo" "bar" (memory 1 1)))'));
 assertErrorMessage(() => new Instance(m4), TypeError, /no import object given/);
 assertErrorMessage(() => new Instance(m4, {baz:null}), TypeError, /import object field is not an Object/);
 assertErrorMessage(() => new Instance(m4, {baz:{quux:{}}}), TypeError, /import object field is not a Function/);
 assertErrorMessage(() => new Instance(m4, {baz:{quux:()=>{}}, foo:null}), TypeError, /import object field is not an Object/);
 assertErrorMessage(() => new Instance(m4, {baz:{quux:()=>{}}, foo:{bar:()=>{}}}), TypeError, /import object field is not a Memory/);
-assertErrorMessage(() => new Instance(m4, {baz:{quux:()=>{}}, foo:{bar:mem1Page}}), TypeError, /imported Memory with incompatible size/);
+assertErrorMessage(() => new Instance(m4, {baz:{quux:()=>{}}, foo:{bar:mem1Page}}), TypeError, /imported Memory with incompatible maximum size/);
 assertEq(new Instance(m3, {baz:{quux:()=>{}}, foo:{bar:mem1PageMax1}}) instanceof Instance, true);
 
 const m5 = new Module(textToBinary('(module (import "a" "b" (memory 2)))'));
 assertErrorMessage(() => new Instance(m5, {a:{b:mem1Page}}), TypeError, /imported Memory with incompatible size/);
 assertEq(new Instance(m5, {a:{b:mem2Page}}) instanceof Instance, true);
 assertEq(new Instance(m5, {a:{b:mem3Page}}) instanceof Instance, true);
 assertEq(new Instance(m5, {a:{b:mem4Page}}) instanceof Instance, true);
 
 const m6 = new Module(textToBinary('(module (import "a" "b" (table 2)))'));
 assertErrorMessage(() => new Instance(m6, {a:{b:tab1Elem}}), TypeError, /imported Table with incompatible size/);
 assertEq(new Instance(m6, {a:{b:tab2Elem}}) instanceof Instance, true);
 assertEq(new Instance(m6, {a:{b:tab3Elem}}) instanceof Instance, true);
 assertEq(new Instance(m6, {a:{b:tab4Elem}}) instanceof Instance, true);
 
 const m7 = new Module(textToBinary('(module (import "a" "b" (table 2 3)))'));
 assertErrorMessage(() => new Instance(m7, {a:{b:tab1Elem}}), TypeError, /imported Table with incompatible size/);
-assertEq(new Instance(m7, {a:{b:tab2Elem}}) instanceof Instance, true);
-assertEq(new Instance(m7, {a:{b:tab3Elem}}) instanceof Instance, true);
+assertErrorMessage(() => new Instance(m7, {a:{b:tab2Elem}}), TypeError, /imported Table with incompatible maximum size/);
+assertErrorMessage(() => new Instance(m7, {a:{b:tab3Elem}}), TypeError, /imported Table with incompatible maximum size/);
 assertErrorMessage(() => new Instance(m7, {a:{b:tab4Elem}}), TypeError, /imported Table with incompatible size/);
 
 assertErrorMessage(() => new Module(textToBinary('(module (memory 2 1))')), TypeError, /maximum length less than initial length/);
 assertErrorMessage(() => new Module(textToBinary('(module (import "a" "b" (memory 2 1)))')), TypeError, /maximum length less than initial length/);
 assertErrorMessage(() => new Module(textToBinary('(module (table (resizable 2 1)))')), TypeError, /maximum length less than initial length/);
 assertErrorMessage(() => new Module(textToBinary('(module (import "a" "b" (table 2 1)))')), TypeError, /maximum length less than initial length/);
 
 // Import wasm-wasm type mismatch
@@ -259,17 +259,17 @@ assertEq(e.f3(), 3);
 assertEq(e.tbl1, e.tbl2);
 assertEq(e.tbl1.get(0), e.f1);
 assertEq(e.tbl1.get(0), e.tbl1.get(0));
 assertEq(e.tbl1.get(0)(), 1);
 assertEq(e.tbl1.get(1), null);
 assertEq(e.tbl1.get(2), e.tbl1.get(2));
 assertEq(e.tbl1.get(2)(), 2);
 assertEq(e.tbl1.get(3), null);
-assertErrorMessage(() => e.tbl1.get(4), RangeError, /out-of-range index/);
+assertErrorMessage(() => e.tbl1.get(4), RangeError, /bad Table get index/);
 assertEq(e.tbl1.get(1), null);
 e.tbl1.set(1, e.f3);
 assertEq(e.tbl1.get(1), e.f3);
 e.tbl1.set(1, null);
 assertEq(e.tbl1.get(1), null);
 e.tbl1.set(3, e.f1);
 assertEq(e.tbl1.get(0), e.tbl1.get(3));
 
@@ -277,23 +277,23 @@ assertEq(e.tbl1.get(0), e.tbl1.get(3));
 
 var code = textToBinary('(module (import "a" "b" (memory 1 1)) (export "foo" memory) (export "bar" memory))');
 var mem = new Memory({initial:1, maximum:1});
 var e = new Instance(new Module(code), {a:{b:mem}}).exports;
 assertEq(mem, e.foo);
 assertEq(mem, e.bar);
 
 var code = textToBinary('(module (import "a" "b" (table 1 1)) (export "foo" table) (export "bar" table))');
-var tbl = new Table({initial:1, element:"anyfunc"});
+var tbl = new Table({initial:1, maximum:1, element:"anyfunc"});
 var e = new Instance(new Module(code), {a:{b:tbl}}).exports;
 assertEq(tbl, e.foo);
 assertEq(tbl, e.bar);
 
 var code = textToBinary('(module (import "a" "b" (table 2 2)) (func $foo) (elem (i32.const 0) $foo) (export "foo" $foo))');
-var tbl = new Table({initial:2, element:"anyfunc"});
+var tbl = new Table({initial:2, maximum:2, element:"anyfunc"});
 var e1 = new Instance(new Module(code), {a:{b:tbl}}).exports;
 assertEq(e1.foo, tbl.get(0));
 tbl.set(1, e1.foo);
 assertEq(e1.foo, tbl.get(1));
 var e2 = new Instance(new Module(code), {a:{b:tbl}}).exports;
 assertEq(e2.foo, tbl.get(0));
 assertEq(e1.foo, tbl.get(1));
 assertEq(tbl.get(0) === e1.foo, false);
--- a/js/src/jit-test/tests/wasm/jsapi.js
+++ b/js/src/jit-test/tests/wasm/jsapi.js
@@ -112,21 +112,21 @@ assertEq(memoryDesc.configurable, true);
 // 'WebAssembly.Memory' constructor function
 const Memory = WebAssembly.Memory;
 assertEq(Memory, memoryDesc.value);
 assertEq(Memory.length, 1);
 assertEq(Memory.name, "Memory");
 assertErrorMessage(() => Memory(), TypeError, /constructor without new is forbidden/);
 assertErrorMessage(() => new Memory(1), TypeError, "first argument must be a memory descriptor");
 assertErrorMessage(() => new Memory({initial:{valueOf() { throw new Error("here")}}}), Error, "here");
-assertErrorMessage(() => new Memory({initial:-1}), TypeError, /bad Memory initial size/);
-assertErrorMessage(() => new Memory({initial:Math.pow(2,32)}), TypeError, /bad Memory initial size/);
-assertErrorMessage(() => new Memory({initial:1, maximum: Math.pow(2,32)/Math.pow(2,14) }), TypeError, /bad Memory maximum size/);
-assertErrorMessage(() => new Memory({initial:2, maximum: 1 }), TypeError, /bad Memory maximum size/);
-assertErrorMessage(() => new Memory({maximum: -1 }), TypeError, /bad Memory maximum size/);
+assertErrorMessage(() => new Memory({initial:-1}), RangeError, /bad Memory initial size/);
+assertErrorMessage(() => new Memory({initial:Math.pow(2,32)}), RangeError, /bad Memory initial size/);
+assertErrorMessage(() => new Memory({initial:1, maximum: Math.pow(2,32)/Math.pow(2,14) }), RangeError, /bad Memory maximum size/);
+assertErrorMessage(() => new Memory({initial:2, maximum:1 }), RangeError, /bad Memory maximum size/);
+assertErrorMessage(() => new Memory({maximum: -1 }), RangeError, /bad Memory maximum size/);
 assertEq(new Memory({initial:1}) instanceof Memory, true);
 assertEq(new Memory({initial:1.5}).buffer.byteLength, WasmPage);
 
 // 'WebAssembly.Memory.prototype' data property
 const memoryProtoDesc = Object.getOwnPropertyDescriptor(Memory, 'prototype');
 assertEq(typeof memoryProtoDesc.value, "object");
 assertEq(memoryProtoDesc.writable, false);
 assertEq(memoryProtoDesc.enumerable, false);
@@ -154,28 +154,28 @@ assertEq(bufferDesc.configurable, true);
 // 'WebAssembly.Memory.prototype.buffer' getter
 const bufferGetter = bufferDesc.get;
 assertErrorMessage(() => bufferGetter.call(), TypeError, /called on incompatible undefined/);
 assertErrorMessage(() => bufferGetter.call({}), TypeError, /called on incompatible Object/);
 assertEq(bufferGetter.call(mem1) instanceof ArrayBuffer, true);
 assertEq(bufferGetter.call(mem1).byteLength, WasmPage);
 
 // 'WebAssembly.Memory.prototype.grow' data property
-const growDesc = Object.getOwnPropertyDescriptor(memoryProto, 'grow');
-assertEq(typeof growDesc.value, "function");
-assertEq(growDesc.enumerable, false);
-assertEq(growDesc.configurable, true);
+const memGrowDesc = Object.getOwnPropertyDescriptor(memoryProto, 'grow');
+assertEq(typeof memGrowDesc.value, "function");
+assertEq(memGrowDesc.enumerable, false);
+assertEq(memGrowDesc.configurable, true);
 
 // 'WebAssembly.Memory.prototype.grow' method
-const grow = growDesc.value;
-assertEq(grow.length, 1);
-assertErrorMessage(() => grow.call(), TypeError, /called on incompatible undefined/);
-assertErrorMessage(() => grow.call({}), TypeError, /called on incompatible Object/);
-assertErrorMessage(() => grow.call(mem1, -1), Error, /failed to grow memory/);
-assertErrorMessage(() => grow.call(mem1, Math.pow(2,32)), Error, /failed to grow memory/);
+const memGrow = memGrowDesc.value;
+assertEq(memGrow.length, 1);
+assertErrorMessage(() => memGrow.call(), TypeError, /called on incompatible undefined/);
+assertErrorMessage(() => memGrow.call({}), TypeError, /called on incompatible Object/);
+assertErrorMessage(() => memGrow.call(mem1, -1), RangeError, /bad Memory grow delta/);
+assertErrorMessage(() => memGrow.call(mem1, Math.pow(2,32)), RangeError, /bad Memory grow delta/);
 var mem = new Memory({initial:1, maximum:2});
 var buf = mem.buffer;
 assertEq(buf.byteLength, WasmPage);
 assertEq(mem.grow(0), 1);
 assertEq(buf !== mem.buffer, true);
 assertEq(buf.byteLength, 0);
 buf = mem.buffer;
 assertEq(buf.byteLength, WasmPage);
@@ -200,20 +200,24 @@ assertEq(Table, tableDesc.value);
 assertEq(Table.length, 1);
 assertEq(Table.name, "Table");
 assertErrorMessage(() => Table(), TypeError, /constructor without new is forbidden/);
 assertErrorMessage(() => new Table(1), TypeError, "first argument must be a table descriptor");
 assertErrorMessage(() => new Table({initial:1, element:1}), TypeError, /must be "anyfunc"/);
 assertErrorMessage(() => new Table({initial:1, element:"any"}), TypeError, /must be "anyfunc"/);
 assertErrorMessage(() => new Table({initial:1, element:{valueOf() { return "anyfunc" }}}), TypeError, /must be "anyfunc"/);
 assertErrorMessage(() => new Table({initial:{valueOf() { throw new Error("here")}}, element:"anyfunc"}), Error, "here");
-assertErrorMessage(() => new Table({initial:-1, element:"anyfunc"}), TypeError, /bad Table initial size/);
-assertErrorMessage(() => new Table({initial:Math.pow(2,32), element:"anyfunc"}), TypeError, /bad Table initial size/);
+assertErrorMessage(() => new Table({initial:-1, element:"anyfunc"}), RangeError, /bad Table initial size/);
+assertErrorMessage(() => new Table({initial:Math.pow(2,32), element:"anyfunc"}), RangeError, /bad Table initial size/);
+assertErrorMessage(() => new Table({initial:2, maximum:1, element:"anyfunc"}), RangeError, /bad Table maximum size/);
+assertErrorMessage(() => new Table({initial:2, maximum:Math.pow(2,32), element:"anyfunc"}), RangeError, /bad Table maximum size/);
 assertEq(new Table({initial:1, element:"anyfunc"}) instanceof Table, true);
 assertEq(new Table({initial:1.5, element:"anyfunc"}) instanceof Table, true);
+assertEq(new Table({initial:1, maximum:1.5, element:"anyfunc"}) instanceof Table, true);
+assertEq(new Table({initial:1, maximum:Math.pow(2,32)-1, element:"anyfunc"}) instanceof Table, true);
 
 // 'WebAssembly.Table.prototype' data property
 const tableProtoDesc = Object.getOwnPropertyDescriptor(Table, 'prototype');
 assertEq(typeof tableProtoDesc.value, "object");
 assertEq(tableProtoDesc.writable, false);
 assertEq(tableProtoDesc.enumerable, false);
 assertEq(tableProtoDesc.configurable, false);
 
@@ -253,45 +257,66 @@ assertEq(getDesc.configurable, true);
 // 'WebAssembly.Table.prototype.get' method
 const get = getDesc.value;
 assertEq(get.length, 1);
 assertErrorMessage(() => get.call(), TypeError, /called on incompatible undefined/);
 assertErrorMessage(() => get.call({}), TypeError, /called on incompatible Object/);
 assertEq(get.call(tbl1, 0), null);
 assertEq(get.call(tbl1, 1), null);
 assertEq(get.call(tbl1, 1.5), null);
-assertErrorMessage(() => get.call(tbl1, 2), RangeError, /out-of-range index/);
-assertErrorMessage(() => get.call(tbl1, 2.5), RangeError, /out-of-range index/);
-assertErrorMessage(() => get.call(tbl1, -1), RangeError, /out-of-range index/);
-assertErrorMessage(() => get.call(tbl1, Math.pow(2,33)), RangeError, /out-of-range index/);
+assertErrorMessage(() => get.call(tbl1, 2), RangeError, /bad Table get index/);
+assertErrorMessage(() => get.call(tbl1, 2.5), RangeError, /bad Table get index/);
+assertErrorMessage(() => get.call(tbl1, -1), RangeError, /bad Table get index/);
+assertErrorMessage(() => get.call(tbl1, Math.pow(2,33)), RangeError, /bad Table get index/);
 assertErrorMessage(() => get.call(tbl1, {valueOf() { throw new Error("hi") }}), Error, "hi");
 
 // 'WebAssembly.Table.prototype.set' data property
 const setDesc = Object.getOwnPropertyDescriptor(tableProto, 'set');
 assertEq(typeof setDesc.value, "function");
 assertEq(setDesc.enumerable, false);
 assertEq(setDesc.configurable, true);
 
 // 'WebAssembly.Table.prototype.set' method
 const set = setDesc.value;
 assertEq(set.length, 2);
 assertErrorMessage(() => set.call(), TypeError, /called on incompatible undefined/);
 assertErrorMessage(() => set.call({}), TypeError, /called on incompatible Object/);
 assertErrorMessage(() => set.call(tbl1, 0), TypeError, /requires more than 1 argument/);
-assertErrorMessage(() => set.call(tbl1, 2, null), RangeError, /out-of-range index/);
-assertErrorMessage(() => set.call(tbl1, -1, null), RangeError, /out-of-range index/);
-assertErrorMessage(() => set.call(tbl1, Math.pow(2,33), null), RangeError, /out-of-range index/);
+assertErrorMessage(() => set.call(tbl1, 2, null), RangeError, /bad Table set index/);
+assertErrorMessage(() => set.call(tbl1, -1, null), RangeError, /bad Table set index/);
+assertErrorMessage(() => set.call(tbl1, Math.pow(2,33), null), RangeError, /bad Table set index/);
 assertErrorMessage(() => set.call(tbl1, 0, undefined), TypeError, /can only assign WebAssembly exported functions to Table/);
 assertErrorMessage(() => set.call(tbl1, 0, {}), TypeError, /can only assign WebAssembly exported functions to Table/);
 assertErrorMessage(() => set.call(tbl1, 0, function() {}), TypeError, /can only assign WebAssembly exported functions to Table/);
 assertErrorMessage(() => set.call(tbl1, 0, Math.sin), TypeError, /can only assign WebAssembly exported functions to Table/);
 assertErrorMessage(() => set.call(tbl1, {valueOf() { throw Error("hai") }}, null), Error, "hai");
 assertEq(set.call(tbl1, 0, null), undefined);
 assertEq(set.call(tbl1, 1, null), undefined);
 
+// 'WebAssembly.Table.prototype.grow' data property
+const tblGrowDesc = Object.getOwnPropertyDescriptor(tableProto, 'grow');
+assertEq(typeof tblGrowDesc.value, "function");
+assertEq(tblGrowDesc.enumerable, false);
+assertEq(tblGrowDesc.configurable, true);
+
+// 'WebAssembly.Table.prototype.grow' method
+const tblGrow = tblGrowDesc.value;
+assertEq(tblGrow.length, 1);
+assertErrorMessage(() => tblGrow.call(), TypeError, /called on incompatible undefined/);
+assertErrorMessage(() => tblGrow.call({}), TypeError, /called on incompatible Object/);
+assertErrorMessage(() => tblGrow.call(tbl1, -1), RangeError, /bad Table grow delta/);
+assertErrorMessage(() => tblGrow.call(tbl1, Math.pow(2,32)), RangeError, /bad Table grow delta/);
+var tbl = new Table({element:"anyfunc", initial:1, maximum:2});
+assertEq(tbl.length, 1);
+assertEq(tbl.grow(0), 1);
+assertEq(tbl.length, 1);
+assertEq(tbl.grow(1), 1);
+assertEq(tbl.length, 2);
+assertErrorMessage(() => tbl.grow(1), Error, /failed to grow table/);
+
 // 'WebAssembly.compile' data property
 const compileDesc = Object.getOwnPropertyDescriptor(WebAssembly, 'compile');
 assertEq(typeof compileDesc.value, "function");
 assertEq(compileDesc.writable, true);
 assertEq(compileDesc.enumerable, false);
 assertEq(compileDesc.configurable, true);
 
 // 'WebAssembly.compile' function
--- a/js/src/jit-test/tests/wasm/resizing.js
+++ b/js/src/jit-test/tests/wasm/resizing.js
@@ -1,16 +1,20 @@
 // |jit-test| test-also-wasm-baseline
 load(libdir + "wasm.js");
 
 const Module = WebAssembly.Module;
 const Instance = WebAssembly.Instance;
 const Table = WebAssembly.Table;
 const Memory = WebAssembly.Memory;
 
+// ======
+// MEMORY
+// ======
+
 // Test for stale heap pointers after resize
 
 // Grow directly from builtin call:
 assertEq(evalText(`(module
     (memory 1)
     (func $test (result i32)
         (i32.store (i32.const 0) (i32.const 1))
         (i32.store (i32.const 65532) (i32.const 10))
@@ -21,63 +25,63 @@ assertEq(evalText(`(module
             (i32.add
                 (i32.load (i32.const 65532))
                 (i32.load (i32.const 6553596)))))
     (export "test" $test)
 )`).exports.test(), 111);
 
 // Grow during call_import:
 var exports = evalText(`(module
-    (import $imp "a" "imp")
+    (import $imp "" "imp")
     (memory 1)
     (func $grow (grow_memory (i32.const 99)))
     (export "grow" $grow)
     (func $test (result i32)
         (i32.store (i32.const 0) (i32.const 1))
         (i32.store (i32.const 65532) (i32.const 10))
         (call $imp)
         (i32.store (i32.const 6553596) (i32.const 100))
         (i32.add
             (i32.load (i32.const 0))
             (i32.add
                 (i32.load (i32.const 65532))
                 (i32.load (i32.const 6553596)))))
     (export "test" $test)
-)`, {a:{imp() { exports.grow() }}}).exports;
+)`, {"":{imp() { exports.grow() }}}).exports;
 
 setJitCompilerOption("baseline.warmup.trigger", 2);
 setJitCompilerOption("ion.warmup.trigger", 4);
 for (var i = 0; i < 10; i++)
     assertEq(exports.test(), 111);
 
 // Grow during call_indirect:
 var mem = new Memory({initial:1});
 var tbl = new Table({initial:1, element:"anyfunc"});
 var exports1 = evalText(`(module
-    (import "a" "mem" (memory 1))
+    (import "" "mem" (memory 1))
     (func $grow
         (i32.store (i32.const 65532) (i32.const 10))
         (grow_memory (i32.const 99))
         (i32.store (i32.const 6553596) (i32.const 100)))
     (export "grow" $grow)
-)`, {a:{mem}}).exports;
+)`, {"":{mem}}).exports;
 var exports2 = evalText(`(module
-    (import "a" "tbl" (table 1))
-    (import "a" "mem" (memory 1))
+    (import "" "tbl" (table 1))
+    (import "" "mem" (memory 1))
     (type $v2v (func))
     (func $test (result i32)
         (i32.store (i32.const 0) (i32.const 1))
         (call_indirect $v2v (i32.const 0))
         (i32.add
             (i32.load (i32.const 0))
             (i32.add
                 (i32.load (i32.const 65532))
                 (i32.load (i32.const 6553596)))))
     (export "test" $test)
-)`, {a:{tbl, mem}}).exports;
+)`, {"":{tbl, mem}}).exports;
 tbl.set(0, exports1.grow);
 assertEq(exports2.test(), 111);
 
 // Test for coherent length/contents
 
 var mem = new Memory({initial:1});
 new Int32Array(mem.buffer)[0] = 42;
 var mod = new Module(textToBinary(`(module
@@ -108,8 +112,105 @@ assertEq(exp2.load(0), 42);
 assertEq(exp2.load(64*1024), 13);
 exp1.grow_memory(2);
 assertEq(exp1.current_memory(), 4);
 exp1.store(3*64*1024, 99);
 assertEq(exp2.current_memory(), 4);
 assertEq(exp2.load(3*64*1024), 99);
 assertEq(mem.buffer.byteLength, 4*64*1024);
 assertEq(new Int32Array(mem.buffer)[3*64*1024/4], 99);
+
+// ======
+// TABLE
+// ======
+
+// Test for stale table base pointers after resize
+
+// Grow during call_import:
+var exports = evalText(`(module
+    (type $v2i (func (result i32)))
+    (import $grow "" "grow")
+    (table (resizable 1))
+    (func $test (result i32)
+        (i32.add
+            (call_indirect $v2i (i32.const 0))
+            (block
+                (call $grow)
+                (call_indirect $v2i (i32.const 1)))))
+    (func $one (result i32) (i32.const 1))
+    (elem (i32.const 0) $one)
+    (func $two (result i32) (i32.const 2))
+    (export "tbl" table)
+    (export "test" $test)
+    (export "two" $two)
+)`, {"":{grow() { exports.tbl.grow(1); exports.tbl.set(1, exports.two) }}}).exports;
+
+setJitCompilerOption("baseline.warmup.trigger", 2);
+setJitCompilerOption("ion.warmup.trigger", 4);
+for (var i = 0; i < 10; i++)
+    assertEq(exports.test(), 3);
+assertEq(exports.tbl.length, 11);
+
+// Grow during call_indirect:
+var exports1 = evalText(`(module
+    (import $grow "" "grow")
+    (func $exp (call $grow))
+    (export "exp" $exp)
+)`, {"":{grow() { exports2.tbl.grow(1); exports2.tbl.set(2, exports2.eleven) }}}).exports;
+var exports2 = evalText(`(module
+    (type $v2v (func))
+    (type $v2i (func (result i32)))
+    (import $imp "" "imp")
+    (elem (i32.const 0) $imp)
+    (table (resizable 2))
+    (func $test (result i32)
+        (i32.add
+            (call_indirect $v2i (i32.const 1))
+            (block
+                (call_indirect $v2v (i32.const 0))
+                (call_indirect $v2i (i32.const 2)))))
+    (func $ten (result i32) (i32.const 10))
+    (elem (i32.const 1) $ten)
+    (func $eleven (result i32) (i32.const 11))
+    (export "tbl" table)
+    (export "test" $test)
+    (export "eleven" $eleven)
+)`, {"":{imp:exports1.exp}}).exports;
+assertEq(exports2.test(), 21);
+
+// Test for coherent length/contents
+
+var src = evalText(`(module
+    (func $one (result i32) (i32.const 1))
+    (export "one" $one)
+    (func $two (result i32) (i32.const 2))
+    (export "two" $two)
+    (func $three (result i32) (i32.const 3))
+    (export "three" $three)
+)`).exports;
+var tbl = new Table({element:"anyfunc", initial:1});
+tbl.set(0, src.one);
+
+var mod = new Module(textToBinary(`(module
+    (type $v2i (func (result i32)))
+    (import "" "tbl" (table 1))
+    (func $ci (param i32) (result i32) (call_indirect $v2i (get_local 0)))
+    (export "call_indirect" $ci)
+)`));
+var exp1 = new Instance(mod, {"":{tbl}}).exports;
+var exp2 = new Instance(mod, {"":{tbl}}).exports;
+assertEq(exp1.call_indirect(0), 1);
+assertErrorMessage(() => exp1.call_indirect(1), Error, /out-of-range/);
+assertEq(exp2.call_indirect(0), 1);
+assertErrorMessage(() => exp2.call_indirect(1), Error, /out-of-range/);
+assertEq(tbl.grow(1), 1);
+assertEq(tbl.length, 2);
+assertEq(exp1.call_indirect(0), 1);
+assertErrorMessage(() => exp1.call_indirect(1), Error, /indirect call to null/);
+tbl.set(1, src.two);
+assertEq(exp1.call_indirect(1), 2);
+assertErrorMessage(() => exp1.call_indirect(2), Error, /out-of-range/);
+assertEq(tbl.grow(2), 2);
+assertEq(tbl.length, 4);
+assertEq(exp2.call_indirect(0), 1);
+assertEq(exp2.call_indirect(1), 2);
+assertErrorMessage(() => exp2.call_indirect(2), Error, /indirect call to null/);
+assertErrorMessage(() => exp2.call_indirect(3), Error, /indirect call to null/);
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -2772,17 +2772,17 @@ void
 MacroAssembler::wasmCallIndirect(const wasm::CallSiteDesc& desc, const wasm::CalleeDesc& callee)
 {
     Register scratch = WasmTableCallScratchReg;
     Register index = WasmTableCallIndexReg;
 
     if (callee.which() == wasm::CalleeDesc::AsmJSTable) {
         // asm.js tables require no signature check, have had their index masked
         // into range and thus need no bounds check and cannot be external.
-        loadWasmGlobalPtr(callee.tableGlobalDataOffset(), scratch);
+        loadWasmGlobalPtr(callee.tableBaseGlobalDataOffset(), scratch);
         loadPtr(BaseIndex(scratch, index, ScalePointer), scratch);
         call(desc, scratch);
         return;
     }
 
     MOZ_ASSERT(callee.which() == wasm::CalleeDesc::WasmTable);
 
     // Write the sig-id into the ABI sig-id register.
@@ -2794,22 +2794,21 @@ MacroAssembler::wasmCallIndirect(const w
       case wasm::SigIdDesc::Kind::Immediate:
         move32(Imm32(sigId.immediate()), WasmTableCallSigReg);
         break;
       case wasm::SigIdDesc::Kind::None:
         break;
     }
 
     // WebAssembly throws if the index is out-of-bounds.
-    branch32(Assembler::Condition::AboveOrEqual,
-             index, Imm32(callee.wasmTableLength()),
-             wasm::JumpTarget::OutOfBounds);
+    loadWasmGlobalPtr(callee.tableLengthGlobalDataOffset(), scratch);
+    branch32(Assembler::Condition::AboveOrEqual, index, scratch, wasm::JumpTarget::OutOfBounds);
 
     // Load the base pointer of the table.
-    loadWasmGlobalPtr(callee.tableGlobalDataOffset(), scratch);
+    loadWasmGlobalPtr(callee.tableBaseGlobalDataOffset(), scratch);
 
     // Load the callee from the table.
     if (callee.wasmTableIsExternal()) {
         static_assert(sizeof(wasm::ExternalTableElem) == 8 || sizeof(wasm::ExternalTableElem) == 16,
                       "elements of external tables are two words");
         if (sizeof(wasm::ExternalTableElem) == 8) {
             computeEffectiveAddress(BaseIndex(scratch, index, TimesEight), scratch);
         } else {
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -345,22 +345,23 @@ MSG_DEF(JSMSG_USE_ASM_LINK_FAIL,       1
 MSG_DEF(JSMSG_USE_ASM_TYPE_OK,         1, JSEXN_WARN,    "Successfully compiled asm.js code ({0})")
 
 // wasm
 MSG_DEF(JSMSG_WASM_FAIL,               1, JSEXN_TYPEERR,     "wasm error: {0}")
 MSG_DEF(JSMSG_WASM_DECODE_FAIL,        2, JSEXN_TYPEERR,     "wasm validation error at offset {0}: {1}")
 MSG_DEF(JSMSG_WASM_TEXT_FAIL,          1, JSEXN_SYNTAXERR,   "wasm text error: {0}")
 MSG_DEF(JSMSG_WASM_IND_CALL_TO_NULL,   0, JSEXN_ERR,         "indirect call to null")
 MSG_DEF(JSMSG_WASM_IND_CALL_BAD_SIG,   0, JSEXN_ERR,         "indirect call signature mismatch")
-MSG_DEF(JSMSG_WASM_BAD_GROW,           0, JSEXN_ERR,         "failed to grow memory")
+MSG_DEF(JSMSG_WASM_BAD_GROW,           1, JSEXN_ERR,         "failed to grow {0}")
 MSG_DEF(JSMSG_WASM_BAD_BUF_ARG,        0, JSEXN_TYPEERR,     "first argument must be an ArrayBuffer or typed array object")
 MSG_DEF(JSMSG_WASM_BAD_MOD_ARG,        0, JSEXN_TYPEERR,     "first argument must be a WebAssembly.Module")
 MSG_DEF(JSMSG_WASM_BAD_DESC_ARG,       1, JSEXN_TYPEERR,     "first argument must be a {0} descriptor")
 MSG_DEF(JSMSG_WASM_BAD_IMP_SIZE,       1, JSEXN_TYPEERR,     "imported {0} with incompatible size")
-MSG_DEF(JSMSG_WASM_BAD_SIZE,           2, JSEXN_TYPEERR,     "bad {0} {1} size")
+MSG_DEF(JSMSG_WASM_BAD_UINT32,         2, JSEXN_RANGEERR,    "bad {0} {1}")
+MSG_DEF(JSMSG_WASM_BAD_IMP_MAX,        1, JSEXN_TYPEERR,     "imported {0} with incompatible maximum size")
 MSG_DEF(JSMSG_WASM_BAD_ELEMENT,        0, JSEXN_TYPEERR,     "\"element\" property of table descriptor must be \"anyfunc\"")
 MSG_DEF(JSMSG_WASM_BAD_IMPORT_ARG,     0, JSEXN_TYPEERR,     "second argument, if present, must be an object")
 MSG_DEF(JSMSG_WASM_BAD_IMPORT_FIELD,   1, JSEXN_TYPEERR,     "import object field is not {0}")
 MSG_DEF(JSMSG_WASM_BAD_IMPORT_SIG,     0, JSEXN_TYPEERR,     "imported function signature mismatch")
 MSG_DEF(JSMSG_WASM_BAD_TABLE_VALUE,    0, JSEXN_TYPEERR,     "can only assign WebAssembly exported functions to Table")
 MSG_DEF(JSMSG_WASM_BAD_I64,            0, JSEXN_TYPEERR,     "cannot pass i64 to or from JS")
 MSG_DEF(JSMSG_WASM_BAD_FIT,            2, JSEXN_RANGEERR,    "{0} segment does not fit in {1}")
 MSG_DEF(JSMSG_WASM_UNREACHABLE,        0, JSEXN_ERR,         "unreachable executed")
--- a/js/src/make-source-package.sh
+++ b/js/src/make-source-package.sh
@@ -87,16 +87,20 @@ case $cmd in
     cp -pPR ${SRCDIR}/../../nsprpub ${tgtpath}
 
     # copy top-level build and config files.
     cp -p ${TOPSRCDIR}/configure.py ${TOPSRCDIR}/moz.configure ${tgtpath}
 
     # copy build and config directory.
     cp -pPR ${TOPSRCDIR}/build ${TOPSRCDIR}/config ${tgtpath}
 
+    # copy cargo config
+    ${MKDIR} -p ${tgtpath}/.cargo
+    cp -pPR ${TOPSRCDIR}/.cargo/config.in ${tgtpath}/.cargo
+
     # put in js itself
     cp -pPR ${TOPSRCDIR}/mfbt ${tgtpath}
     cp -p ${SRCDIR}/../moz.configure ${tgtpath}/js
     cp -pPR ${SRCDIR}/../public ${tgtpath}/js
     cp -pPR ${SRCDIR}/../examples ${tgtpath}/js
     find ${SRCDIR} -mindepth 1 -maxdepth 1 -not -path ${STAGING} -a -not -name ${pkg} \
         -exec cp -pPR {} ${tgtpath}/js/src \;
 
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -122,17 +122,17 @@ enum JSShellExitCode {
     EXITCODE_OUT_OF_MEMORY      = 5,
     EXITCODE_TIMEOUT            = 6
 };
 
 static const size_t gStackChunkSize = 8192;
 
 /*
  * Note: This limit should match the stack limit set by the browser in
- *       js/xpconnect/src/XPCJSRuntime.cpp
+ *       js/xpconnect/src/XPCJSContext.cpp
  */
 #if defined(MOZ_ASAN) || (defined(DEBUG) && !defined(XP_WIN))
 static const size_t gMaxStackSize = 2 * 128 * sizeof(size_t) * 1024;
 #else
 static const size_t gMaxStackSize = 128 * sizeof(size_t) * 1024;
 #endif
 
 /*
--- a/js/src/vm/ArrayBufferObject-inl.h
+++ b/js/src/vm/ArrayBufferObject-inl.h
@@ -38,36 +38,16 @@ ArrayBufferObjectMaybeShared::isDetached
 inline uint32_t
 AnyArrayBufferByteLength(const ArrayBufferObjectMaybeShared* buf)
 {
     if (buf->is<ArrayBufferObject>())
         return buf->as<ArrayBufferObject>().byteLength();
     return buf->as<SharedArrayBufferObject>().byteLength();
 }
 
-inline size_t
-WasmArrayBufferMappedSize(const ArrayBufferObjectMaybeShared* buf)
-{
-    if (buf->is<ArrayBufferObject>())
-        return buf->as<ArrayBufferObject>().wasmMappedSize();
-#ifdef WASM_HUGE_MEMORY
-    return wasm::HugeMappedSize;
-#else
-    return buf->as<SharedArrayBufferObject>().byteLength();
-#endif
-}
-
-inline mozilla::Maybe<uint32_t>
-WasmArrayBufferMaxSize(const ArrayBufferObjectMaybeShared* buf)
-{
-    if (buf->is<ArrayBufferObject>())
-        return buf->as<ArrayBufferObject>().wasmMaxSize();
-    return mozilla::Some(buf->as<SharedArrayBufferObject>().byteLength());
-}
-
 inline bool
 AnyArrayBufferIsPreparedForAsmJS(const ArrayBufferObjectMaybeShared* buf)
 {
     if (buf->is<ArrayBufferObject>())
         return buf->as<ArrayBufferObject>().isPreparedForAsmJS();
     return buf->as<SharedArrayBufferObject>().isPreparedForAsmJS();
 }
 
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -838,25 +838,46 @@ ArrayBufferObject::setByteLength(uint32_
 size_t
 ArrayBufferObject::wasmMappedSize() const
 {
     if (isWasm())
         return contents().wasmBuffer()->mappedSize();
     return byteLength();
 }
 
+size_t
+js::WasmArrayBufferMappedSize(const ArrayBufferObjectMaybeShared* buf)
+{
+    if (buf->is<ArrayBufferObject>())
+        return buf->as<ArrayBufferObject>().wasmMappedSize();
+#ifdef WASM_HUGE_MEMORY
+    return wasm::HugeMappedSize;
+#else
+    return buf->as<SharedArrayBufferObject>().byteLength();
+#endif
+}
+
 Maybe<uint32_t>
 ArrayBufferObject::wasmMaxSize() const
 {
     if (isWasm())
         return contents().wasmBuffer()->maxSize();
     else
         return Some<uint32_t>(byteLength());
 }
 
+Maybe<uint32_t>
+js::WasmArrayBufferMaxSize(const ArrayBufferObjectMaybeShared* buf)
+{
+    if (buf->is<ArrayBufferObject>())
+        return buf->as<ArrayBufferObject>().wasmMaxSize();
+
+    return Some(buf->as<SharedArrayBufferObject>().byteLength());
+}
+
 /* static */ bool
 ArrayBufferObject::wasmGrowToSizeInPlace(uint32_t newSize,
                                          HandleArrayBufferObject oldBuf,
                                          MutableHandleArrayBufferObject newBuf,
                                          JSContext* cx)
 {
     // On failure, do not throw and ensure that the original buffer is
     // unmodified and valid. After WasmArrayRawBuffer::growToSizeInPlace(), the
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -833,17 +833,17 @@ Debugger::slowPathOnEnterFrame(JSContext
 {
     RootedValue rval(cx);
     JSTrapStatus status = dispatchHook(
         cx,
         [frame](Debugger* dbg) -> bool {
             return dbg->observesFrame(frame) && dbg->observesEnterFrame();
         },
         [&](Debugger* dbg) -> JSTrapStatus {
-            return dbg->fireEnterFrame(cx, frame, &rval);
+            return dbg->fireEnterFrame(cx, &rval);
         });
 
     switch (status) {
       case JSTRAP_CONTINUE:
         break;
 
       case JSTRAP_THROW:
         cx->setPendingException(rval);
@@ -1569,16 +1569,37 @@ CheckResumptionValue(JSContext* cx, Abst
                 ReportValueError(cx, JSMSG_BAD_DERIVED_RETURN, JSDVG_IGNORE_STACK, vp, nullptr);
                 return false;
             }
         }
     }
     return true;
 }
 
+static bool
+GetThisValueForCheck(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc,
+                     MutableHandleValue thisv, Maybe<HandleValue>& maybeThisv)
+{
+    if (frame.debuggerNeedsCheckPrimitiveReturn()) {
+        {
+            AutoCompartment ac(cx, frame.environmentChain());
+            if (!GetThisValueForDebuggerMaybeOptimizedOut(cx, frame, pc, thisv))
+                return false;
+        }
+
+        if (!cx->compartment()->wrap(cx, thisv))
+            return false;
+
+        MOZ_ASSERT_IF(thisv.isMagic(), thisv.isMagic(JS_UNINITIALIZED_LEXICAL));
+        maybeThisv.emplace(HandleValue(thisv));
+    }
+
+    return true;
+}
+
 bool
 Debugger::processResumptionValue(Maybe<AutoCompartment>& ac, AbstractFramePtr frame,
                                  const Maybe<HandleValue>& maybeThisv, HandleValue rval,
                                  JSTrapStatus& statusp, MutableHandleValue vp)
 {
     JSContext* cx = ac->context()->asJSContext();
 
     if (!ParseResumptionValue(cx, rval, statusp, vp) ||
@@ -1593,67 +1614,78 @@ Debugger::processResumptionValue(Maybe<A
         statusp = JSTRAP_ERROR;
         vp.setUndefined();
     }
 
     return true;
 }
 
 JSTrapStatus
-Debugger::processHandlerResultHelper(Maybe<AutoCompartment>& ac, bool ok, const Value& rv,
-                                     const Maybe<HandleValue>& thisVForCheck, AbstractFramePtr frame,
-                                     MutableHandleValue vp)
-{
-    if (!ok)
-        return handleUncaughtException(ac, vp, thisVForCheck, frame);
+Debugger::processParsedHandlerResultHelper(Maybe<AutoCompartment>& ac, AbstractFramePtr frame,
+                                           const Maybe<HandleValue>& maybeThisv, bool success,
+                                           JSTrapStatus status, MutableHandleValue vp)
+{
+    if (!success)
+        return handleUncaughtException(ac, vp, maybeThisv, frame);
 
     JSContext* cx = ac->context()->asJSContext();
-    RootedValue rvRoot(cx, rv);
-    JSTrapStatus status = JSTRAP_CONTINUE;
-    RootedValue v(cx);
-    if (!processResumptionValue(ac, frame, thisVForCheck, rvRoot, status, &v))
-        return handleUncaughtException(ac, vp, thisVForCheck, frame);
-    vp.set(v);
+
+    if (!unwrapDebuggeeValue(cx, vp) ||
+        !CheckResumptionValue(cx, frame, maybeThisv, status, vp))
+    {
+        return handleUncaughtException(ac, vp, maybeThisv, frame);
+    }
+
+    ac.reset();
+    if (!cx->compartment()->wrap(cx, vp)) {
+        status = JSTRAP_ERROR;
+        vp.setUndefined();
+    }
+
     return status;
 }
 
 JSTrapStatus
-Debugger::processHandlerResult(Maybe<AutoCompartment>& ac, bool ok, const Value& rv,
+Debugger::processParsedHandlerResult(Maybe<AutoCompartment>& ac, AbstractFramePtr frame,
+                                     jsbytecode* pc, bool success, JSTrapStatus status,
+                                     MutableHandleValue vp)
+{
+    JSContext* cx = ac->context()->asJSContext();
+
+    RootedValue thisv(cx);
+    Maybe<HandleValue> maybeThisv;
+    if (!GetThisValueForCheck(cx, frame, pc, &thisv, maybeThisv)) {
+        ac.reset();
+        return JSTRAP_ERROR;
+    }
+
+    return processParsedHandlerResultHelper(ac, frame, maybeThisv, success, status, vp);
+}
+
+JSTrapStatus
+Debugger::processHandlerResult(Maybe<AutoCompartment>& ac, bool success, const Value& rv,
                                AbstractFramePtr frame, jsbytecode* pc, MutableHandleValue vp)
 {
     JSContext* cx = ac->context()->asJSContext();
-    RootedValue rootThis(cx);
-    Maybe<HandleValue> thisArg;
-    if (frame.debuggerNeedsCheckPrimitiveReturn()) {
-        bool success;
-        {
-            AutoCompartment ac2(cx, frame.environmentChain());
-            success = GetThisValueForDebuggerMaybeOptimizedOut(cx, frame, pc, &rootThis);
-        }
-        if (!success || !cx->compartment()->wrap(cx, &rootThis)) {
-            ac.reset();
-            return JSTRAP_ERROR;
-        }
-        MOZ_ASSERT_IF(rootThis.isMagic(), rootThis.isMagic(JS_UNINITIALIZED_LEXICAL));
-        thisArg.emplace(HandleValue(rootThis));
-    }
-    return processHandlerResultHelper(ac, ok, rv, thisArg, frame, vp);
-}
-
-JSTrapStatus
-Debugger::processHandlerResult(Maybe<AutoCompartment>& ac, bool ok, const Value& rv,
-                               const Value& thisV, AbstractFramePtr frame, MutableHandleValue vp)
-{
-    JSContext* cx = ac->context()->asJSContext();
-    RootedValue rootThis(cx, thisV);
-    Maybe<HandleValue> thisArg;
-    if (frame.debuggerNeedsCheckPrimitiveReturn())
-        thisArg.emplace(rootThis);
-
-    return processHandlerResultHelper(ac, ok, rv, thisArg, frame, vp);
+
+    RootedValue thisv(cx);
+    Maybe<HandleValue> maybeThisv;
+    if (!GetThisValueForCheck(cx, frame, pc, &thisv, maybeThisv)) {
+        ac.reset();
+        return JSTRAP_ERROR;
+    }
+
+    if (!success)
+        return handleUncaughtException(ac, vp, maybeThisv, frame);
+
+    RootedValue rootRv(cx, rv);
+    JSTrapStatus status = JSTRAP_CONTINUE;
+    success = ParseResumptionValue(cx, rootRv, status, vp);
+
+    return processParsedHandlerResultHelper(ac, frame, maybeThisv, success, status, vp);
 }
 
 static bool
 CallMethodIfPresent(JSContext* cx, HandleObject obj, const char* name, size_t argc, Value* argv,
                     MutableHandleValue rval)
 {
     rval.setUndefined();
     JSAtom* atom = Atomize(cx, name, strlen(name));
@@ -1727,34 +1759,36 @@ Debugger::fireExceptionUnwind(JSContext*
     bool ok = js::Call(cx, fval, object, scriptFrame, wrappedExc, &rv);
     JSTrapStatus st = processHandlerResult(ac, ok, rv, iter.abstractFramePtr(), iter.pc(), vp);
     if (st == JSTRAP_CONTINUE)
         cx->setPendingException(exc);
     return st;
 }
 
 JSTrapStatus
-Debugger::fireEnterFrame(JSContext* cx, AbstractFramePtr frame, MutableHandleValue vp)
+Debugger::fireEnterFrame(JSContext* cx, MutableHandleValue vp)
 {
     RootedObject hook(cx, getHook(OnEnterFrame));
     MOZ_ASSERT(hook);
     MOZ_ASSERT(hook->isCallable());
 
     Maybe<AutoCompartment> ac;
     ac.emplace(cx, object);
 
     RootedValue scriptFrame(cx);
-    if (!getScriptFrame(cx, frame, &scriptFrame))
+
+    ScriptFrameIter iter(cx);
+    if (!getScriptFrame(cx, iter, &scriptFrame))
         return reportUncaughtException(ac);
 
     RootedValue fval(cx, ObjectValue(*hook));
     RootedValue rv(cx);
     bool ok = js::Call(cx, fval, object, scriptFrame, &rv);
 
-    return processHandlerResult(ac, ok, rv, MagicValue(JS_UNINITIALIZED_LEXICAL), frame, vp);
+    return processHandlerResult(ac, ok, rv, iter.abstractFramePtr(), iter.pc(), vp);
 }
 
 void
 Debugger::fireNewScript(JSContext* cx, Handle<DebuggerScriptReferent> scriptReferent)
 {
     RootedObject hook(cx, getHook(OnNewScript));
     MOZ_ASSERT(hook);
     MOZ_ASSERT(hook->isCallable());
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -554,30 +554,26 @@ class Debugger : private mozilla::Linked
      *     null - Return JSTRAP_ERROR to terminate the debuggee with an
      *         uncatchable error.
      *     anything else - Make a new TypeError the pending exception and
      *         return handleUncaughtException(ac, vp, callHook).
      */
     JSTrapStatus processHandlerResult(mozilla::Maybe<AutoCompartment>& ac, bool OK, const Value& rv,
                                       AbstractFramePtr frame, jsbytecode* pc, MutableHandleValue vp);
 
-    /*
-     * When we run the onEnterFrame hook, the |this| slot hasn't been fully
-     * initialized, because the initialzation happens in the function's
-     * prologue. To combat this, we pass the this for the primitive return
-     * check directly. When bug 1249193 is fixed, this overload should be
-     * removed.
-     */
-    JSTrapStatus processHandlerResult(mozilla::Maybe<AutoCompartment>& ac, bool OK, const Value& rv,
-                                      const Value& thisVForCheck, AbstractFramePtr frame,
-                                      MutableHandleValue vp);
+    JSTrapStatus processParsedHandlerResult(mozilla::Maybe<AutoCompartment>& ac,
+                                            AbstractFramePtr frame, jsbytecode* pc,
+                                            bool success, JSTrapStatus status,
+                                            MutableHandleValue vp);
 
-    JSTrapStatus processHandlerResultHelper(mozilla::Maybe<AutoCompartment>& ac, bool ok, const Value& rv,
-                                            const mozilla::Maybe<HandleValue>& thisVForCheck, AbstractFramePtr frame,
-                                            MutableHandleValue vp);
+    JSTrapStatus processParsedHandlerResultHelper(mozilla::Maybe<AutoCompartment>& ac,
+                                                  AbstractFramePtr frame,
+                                                  const mozilla::Maybe<HandleValue>& maybeThisv,
+                                                  bool success, JSTrapStatus status,
+                                                  MutableHandleValue vp);
 
     bool processResumptionValue(mozilla::Maybe<AutoCompartment>& ac, AbstractFramePtr frame,
                                 const mozilla::Maybe<HandleValue>& maybeThis, HandleValue rval,
                                 JSTrapStatus& statusp, MutableHandleValue vp);
 
     GlobalObject* unwrapDebuggeeArgument(JSContext* cx, const Value& v);
 
     static void traceObject(JSTracer* trc, JSObject* obj);
@@ -712,17 +708,17 @@ class Debugger : private mozilla::Linked
 
     template <typename HookIsEnabledFun /* bool (Debugger*) */,
               typename FireHookFun /* JSTrapStatus (Debugger*) */>
     static JSTrapStatus dispatchHook(JSContext* cx, HookIsEnabledFun hookIsEnabled,
                                      FireHookFun fireHook);
 
     JSTrapStatus fireDebuggerStatement(JSContext* cx, MutableHandleValue vp);
     JSTrapStatus fireExceptionUnwind(JSContext* cx, MutableHandleValue vp);
-    JSTrapStatus fireEnterFrame(JSContext* cx, AbstractFramePtr frame, MutableHandleValue vp);
+    JSTrapStatus fireEnterFrame(JSContext* cx, MutableHandleValue vp);
     JSTrapStatus fireNewGlobalObject(JSContext* cx, Handle<GlobalObject*> global, MutableHandleValue vp);
     JSTrapStatus firePromiseHook(JSContext* cx, Hook hook, HandleObject promise, MutableHandleValue vp);
 
     NativeObject* newVariantWrapper(JSContext* cx, Handle<DebuggerScriptReferent> referent) {
         return newDebuggerScript(cx, referent);
     }
     NativeObject* newVariantWrapper(JSContext* cx, Handle<DebuggerSourceReferent> referent) {
         return newDebuggerSource(cx, referent);
--- a/js/src/vm/SharedArrayObject.cpp
+++ b/js/src/vm/SharedArrayObject.cpp
@@ -75,17 +75,16 @@ MarkValidRegion(void* addr, size_t len)
 
 // Since this SharedArrayBuffer will likely be used for asm.js code, prepare it
 // for asm.js by mapping the 4gb protected zone described in WasmTypes.h.
 // Since we want to put the SharedArrayBuffer header immediately before the
 // heap but keep the heap page-aligned, allocate an extra page before the heap.
 static uint64_t
 SharedArrayMappedSize(uint32_t allocSize)
 {
-    MOZ_RELEASE_ASSERT(jit::JitOptions.wasmTestMode);
     MOZ_RELEASE_ASSERT(sizeof(SharedArrayRawBuffer) < gc::SystemPageSize());
 #ifdef WASM_HUGE_MEMORY
     return wasm::HugeMappedSize + gc::SystemPageSize();
 #else
     return allocSize + wasm::GuardSize;
 #endif
 }
 
--- a/js/xpconnect/idl/xpccomponents.idl
+++ b/js/xpconnect/idl/xpccomponents.idl
@@ -607,17 +607,17 @@ interface nsIXPCComponents_Utils : nsISu
     [implicit_jscontext]
     nsISupports generateXPCWrappedJS(in jsval obj, [optional] in jsval scope);
 
     /**
       * Retrieve the last time, in microseconds since epoch, that a given
       * watchdog-related event occured.
       *
       * Valid categories:
-      *   "RuntimeStateChange"      - Runtime switching between active and inactive states
+      *   "ContextStateChange"      - Context switching between active and inactive states
       *   "WatchdogWakeup"          - Watchdog waking up from sleeping
       *   "WatchdogHibernateStart"  - Watchdog begins hibernating
       *   "WatchdogHibernateStop"   - Watchdog stops hibernating
       */
     PRTime getWatchdogTimestamp(in AString aCategory);
 
     [implicit_jscontext]
     jsval getJSEngineTelemetryValue();
--- a/js/xpconnect/loader/mozJSSubScriptLoader.cpp
+++ b/js/xpconnect/loader/mozJSSubScriptLoader.cpp
@@ -771,17 +771,17 @@ NS_IMETHODIMP
 NotifyPrecompilationCompleteRunnable::Run(void)
 {
     MOZ_ASSERT(NS_IsMainThread());
     MOZ_ASSERT(mPrecompiler);
 
     AutoSendObserverNotification notifier(mPrecompiler);
 
     if (mToken) {
-        JSContext* cx = XPCJSRuntime::Get()->Context();
+        JSContext* cx = XPCJSContext::Get()->Context();
         NS_ENSURE_TRUE(cx, NS_ERROR_FAILURE);
         JS::CancelOffThreadScript(cx, mToken);
     }
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/js/xpconnect/src/ExportHelpers.cpp
+++ b/js/xpconnect/src/ExportHelpers.cpp
@@ -360,17 +360,17 @@ FunctionForwarder(JSContext* cx, unsigne
 }
 
 bool
 NewFunctionForwarder(JSContext* cx, HandleId idArg, HandleObject callable,
                      FunctionForwarderOptions& options, MutableHandleValue vp)
 {
     RootedId id(cx, idArg);
     if (id == JSID_VOIDHANDLE)
-        id = GetRTIdByIndex(cx, XPCJSRuntime::IDX_EMPTYSTRING);
+        id = GetJSIDByIndex(cx, XPCJSContext::IDX_EMPTYSTRING);
 
     // We have no way of knowing whether the underlying function wants to be a
     // constructor or not, so we just mark all forwarders as constructors, and
     // let the underlying function throw for construct calls if it wants.
     JSFunction* fun = js::NewFunctionByIdWithReserved(cx, FunctionForwarder,
                                                       0, JSFUN_CONSTRUCTOR, id);
     if (!fun)
         return false;
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -477,17 +477,17 @@ static bool
 sandbox_addProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v)
 {
     CompartmentPrivate* priv = CompartmentPrivate::Get(obj);
     MOZ_ASSERT(priv->writeToGlobalPrototype);
 
     // Whenever JS_EnumerateStandardClasses is called, it defines the
     // "undefined" property, even if it's already defined. We don't want to do
     // anything in that case.
-    if (id == XPCJSRuntime::Get()->GetStringID(XPCJSRuntime::IDX_UNDEFINED))
+    if (id == XPCJSContext::Get()->GetStringID(XPCJSContext::IDX_UNDEFINED))
         return true;
 
     // Avoid recursively triggering sandbox_addProperty in the
     // JS_DefinePropertyById call below.
     if (priv->skipWriteToGlobalPrototype)
         return true;
 
     AutoSkipPropertyMirroring askip(priv);
--- a/js/xpconnect/src/XPCCallContext.cpp
+++ b/js/xpconnect/src/XPCCallContext.cpp
@@ -22,34 +22,34 @@ XPCCallContext::XPCCallContext(JSContext
                                HandleObject funobj /* = nullptr               */,
                                HandleId name       /* = JSID_VOID             */,
                                unsigned argc       /* = NO_ARGS               */,
                                Value* argv         /* = nullptr               */,
                                Value* rval         /* = nullptr               */)
     :   mAr(cx),
         mState(INIT_FAILED),
         mXPC(nsXPConnect::XPConnect()),
-        mXPCJSRuntime(nullptr),
+        mXPCJSContext(nullptr),
         mJSContext(cx),
         mWrapper(nullptr),
         mTearOff(nullptr),
         mName(cx)
 {
     MOZ_ASSERT(cx);
     MOZ_ASSERT(cx == nsContentUtils::GetCurrentJSContext());
 
     if (!mXPC)
         return;
 
-    mXPCJSRuntime = XPCJSRuntime::Get();
+    mXPCJSContext = XPCJSContext::Get();
 
     // hook into call context chain.
-    mPrevCallContext = mXPCJSRuntime->SetCallContext(this);
+    mPrevCallContext = mXPCJSContext->SetCallContext(this);
 
-    mState = HAVE_RUNTIME;
+    mState = HAVE_CONTEXT;
 
     if (!obj)
         return;
 
     mMethodIndex = 0xDEAD;
 
     mState = HAVE_OBJECT;
 
@@ -120,17 +120,17 @@ XPCCallContext::SetName(jsid name)
 
     mState = HAVE_NAME;
 }
 
 void
 XPCCallContext::SetCallInfo(XPCNativeInterface* iface, XPCNativeMember* member,
                             bool isSetter)
 {
-    CHECK_STATE(HAVE_RUNTIME);
+    CHECK_STATE(HAVE_CONTEXT);
 
     // We are going straight to the method info and need not do a lookup
     // by id.
 
     // don't be tricked if method is called with wrong 'this'
     if (mTearOff && mTearOff->GetInterface() != iface)
         mTearOff = nullptr;
 
@@ -192,28 +192,28 @@ XPCCallContext::CanCallNow()
 
 void
 XPCCallContext::SystemIsBeingShutDown()
 {
     // XXX This is pretty questionable since the per thread cleanup stuff
     // can be making this call on one thread for call contexts on another
     // thread.
     NS_WARNING("Shutting Down XPConnect even through there is a live XPCCallContext");
-    mXPCJSRuntime = nullptr;
+    mXPCJSContext = nullptr;
     mState = SYSTEM_SHUTDOWN;
     mInterface = nullptr;
 
     if (mPrevCallContext)
         mPrevCallContext->SystemIsBeingShutDown();
 }
 
 XPCCallContext::~XPCCallContext()
 {
-    if (mXPCJSRuntime) {
-        DebugOnly<XPCCallContext*> old = mXPCJSRuntime->SetCallContext(mPrevCallContext);
+    if (mXPCJSContext) {
+        DebugOnly<XPCCallContext*> old = mXPCJSContext->SetCallContext(mPrevCallContext);
         MOZ_ASSERT(old == this, "bad pop from per thread data");
     }
 }
 
 NS_IMETHODIMP
 XPCCallContext::GetCallee(nsISupports * *aCallee)
 {
     nsCOMPtr<nsISupports> rval = mWrapper ? mWrapper->GetIdentityObject() : nullptr;
--- a/js/xpconnect/src/XPCComponents.cpp
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -2564,17 +2564,17 @@ nsXPCComponents_Utils::GetWeakReference(
     NS_ENSURE_SUCCESS(rv, rv);
     ref.forget(_retval);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsXPCComponents_Utils::ForceGC()
 {
-    JSContext* cx = nsXPConnect::GetRuntimeInstance()->Context();
+    JSContext* cx = nsXPConnect::GetContextInstance()->Context();
     PrepareForFullGC(cx);
     GCForReason(cx, GC_NORMAL, gcreason::COMPONENT_UTILS);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsXPCComponents_Utils::ForceCC(nsICycleCollectorListener* listener)
 {
@@ -3182,27 +3182,27 @@ nsXPCComponents_Utils::GenerateXPCWrappe
     holder.forget(aOut);
     return rv;
 }
 
 NS_IMETHODIMP
 nsXPCComponents_Utils::GetWatchdogTimestamp(const nsAString& aCategory, PRTime* aOut)
 {
     WatchdogTimestampCategory category;
-    if (aCategory.EqualsLiteral("RuntimeStateChange"))
-        category = TimestampRuntimeStateChange;
+    if (aCategory.EqualsLiteral("ContextStateChange"))
+        category = TimestampContextStateChange;
     else if (aCategory.EqualsLiteral("WatchdogWakeup"))
         category = TimestampWatchdogWakeup;
     else if (aCategory.EqualsLiteral("WatchdogHibernateStart"))
         category = TimestampWatchdogHibernateStart;
     else if (aCategory.EqualsLiteral("WatchdogHibernateStop"))
         category = TimestampWatchdogHibernateStop;
     else
         return NS_ERROR_INVALID_ARG;
-    *aOut = XPCJSRuntime::Get()->GetWatchdogTimestamp(category);
+    *aOut = XPCJSContext::Get()->GetWatchdogTimestamp(category);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsXPCComponents_Utils::GetJSEngineTelemetryValue(JSContext* cx, MutableHandleValue rval)
 {
     RootedObject obj(cx, JS_NewPlainObject(cx));
     if (!obj)
@@ -3453,28 +3453,28 @@ nsXPCComponents::GetManager(nsIComponent
 {
     MOZ_ASSERT(aManager, "bad param");
     return NS_GetComponentManager(aManager);
 }
 
 NS_IMETHODIMP
 nsXPCComponents::GetReturnCode(JSContext* aCx, MutableHandleValue aOut)
 {
-    nsresult res = XPCJSRuntime::Get()->GetPendingResult();
+    nsresult res = XPCJSContext::Get()->GetPendingResult();
     aOut.setNumber(static_cast<uint32_t>(res));
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsXPCComponents::SetReturnCode(JSContext* aCx, HandleValue aCode)
 {
     nsresult rv;
     if (!ToUint32(aCx, aCode, (uint32_t*)&rv))
         return NS_ERROR_FAILURE;
-    XPCJSRuntime::Get()->SetPendingResult(rv);
+    XPCJSContext::Get()->SetPendingResult(rv);
     return NS_OK;
 }
 
 // static
 NS_IMETHODIMP nsXPCComponents::ReportError(HandleValue error, JSContext* cx)
 {
     NS_WARNING("Components.reportError deprecated, use Components.utils.reportError");
 
--- a/js/xpconnect/src/XPCForwards.h
+++ b/js/xpconnect/src/XPCForwards.h
@@ -7,17 +7,17 @@
 /* Private forward declarations. */
 
 #ifndef xpcforwards_h___
 #define xpcforwards_h___
 
 // forward declarations of interally used classes...
 
 class nsXPConnect;
-class XPCJSRuntime;
+class XPCJSContext;
 class XPCContext;
 class XPCCallContext;
 
 class XPCJSThrower;
 
 class nsXPCWrappedJS;
 class nsXPCWrappedJSClass;
 
--- a/js/xpconnect/src/XPCInlines.h
+++ b/js/xpconnect/src/XPCInlines.h
@@ -9,59 +9,59 @@
 #ifndef xpcinlines_h___
 #define xpcinlines_h___
 
 #include <algorithm>
 
 /***************************************************************************/
 
 inline void
-XPCJSRuntime::AddVariantRoot(XPCTraceableVariant* variant)
+XPCJSContext::AddVariantRoot(XPCTraceableVariant* variant)
 {
     variant->AddToRootSet(&mVariantRoots);
 }
 
 inline void
-XPCJSRuntime::AddWrappedJSRoot(nsXPCWrappedJS* wrappedJS)
+XPCJSContext::AddWrappedJSRoot(nsXPCWrappedJS* wrappedJS)
 {
     wrappedJS->AddToRootSet(&mWrappedJSRoots);
 }
 
 inline void
-XPCJSRuntime::AddObjectHolderRoot(XPCJSObjectHolder* holder)
+XPCJSContext::AddObjectHolderRoot(XPCJSObjectHolder* holder)
 {
     holder->AddToRootSet(&mObjectHolderRoots);
 }
 
 /***************************************************************************/
 
 inline bool
 XPCCallContext::IsValid() const
 {
     return mState != INIT_FAILED;
 }
 
-inline XPCJSRuntime*
-XPCCallContext::GetRuntime() const
+inline XPCJSContext*
+XPCCallContext::GetContext() const
 {
-    CHECK_STATE(HAVE_RUNTIME);
-    return mXPCJSRuntime;
+    CHECK_STATE(HAVE_CONTEXT);
+    return mXPCJSContext;
 }
 
 inline JSContext*
 XPCCallContext::GetJSContext() const
 {
-    CHECK_STATE(HAVE_RUNTIME);
+    CHECK_STATE(HAVE_CONTEXT);
     return mJSContext;
 }
 
 inline XPCCallContext*
 XPCCallContext::GetPrevCallContext() const
 {
-    CHECK_STATE(HAVE_RUNTIME);
+    CHECK_STATE(HAVE_CONTEXT);
     return mPrevCallContext;
 }
 
 inline nsISupports*
 XPCCallContext::GetIdentityObject() const
 {
     CHECK_STATE(HAVE_OBJECT);
     if (mWrapper)
@@ -180,39 +180,39 @@ XPCCallContext::SetRetVal(JS::Value val)
     CHECK_STATE(HAVE_ARGS);
     if (mRetVal)
         *mRetVal = val;
 }
 
 inline jsid
 XPCCallContext::GetResolveName() const
 {
-    CHECK_STATE(HAVE_RUNTIME);
-    return XPCJSRuntime::Get()->GetResolveName();
+    CHECK_STATE(HAVE_CONTEXT);
+    return XPCJSContext::Get()->GetResolveName();
 }
 
 inline jsid
 XPCCallContext::SetResolveName(JS::HandleId name)
 {
-    CHECK_STATE(HAVE_RUNTIME);
-    return XPCJSRuntime::Get()->SetResolveName(name);
+    CHECK_STATE(HAVE_CONTEXT);
+    return XPCJSContext::Get()->SetResolveName(name);
 }
 
 inline XPCWrappedNative*
 XPCCallContext::GetResolvingWrapper() const
 {
     CHECK_STATE(HAVE_OBJECT);
-    return XPCJSRuntime::Get()->GetResolvingWrapper();
+    return XPCJSContext::Get()->GetResolvingWrapper();
 }
 
 inline XPCWrappedNative*
 XPCCallContext::SetResolvingWrapper(XPCWrappedNative* w)
 {
     CHECK_STATE(HAVE_OBJECT);
-    return XPCJSRuntime::Get()->SetResolvingWrapper(w);
+    return XPCJSContext::Get()->SetResolvingWrapper(w);
 }
 
 inline uint16_t
 XPCCallContext::GetMethodIndex() const
 {
     CHECK_STATE(HAVE_OBJECT);
     return mMethodIndex;
 }
@@ -531,20 +531,20 @@ inline bool
 xpc_ForcePropertyResolve(JSContext* cx, JS::HandleObject obj, jsid idArg)
 {
     JS::RootedId id(cx, idArg);
     bool dummy;
     return JS_HasPropertyById(cx, obj, id, &dummy);
 }
 
 inline jsid
-GetRTIdByIndex(JSContext* cx, unsigned index)
+GetJSIDByIndex(JSContext* cx, unsigned index)
 {
-  XPCJSRuntime* rt = nsXPConnect::XPConnect()->GetRuntime();
-  return rt->GetStringID(index);
+  XPCJSContext* xpcx = nsXPConnect::XPConnect()->GetContext();
+  return xpcx->GetStringID(index);
 }
 
 inline
 bool ThrowBadParam(nsresult rv, unsigned paramNum, XPCCallContext& ccx)
 {
     XPCThrower::ThrowBadParam(rv, paramNum, ccx);
     return false;
 }
rename from js/xpconnect/src/XPCJSRuntime.cpp
rename to js/xpconnect/src/XPCJSContext.cpp
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSContext.cpp
@@ -1,15 +1,15 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* vim: set ts=8 sts=4 et sw=4 tw=99: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-/* Per JSRuntime object */
+/* Per JSContext object */
 
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/UniquePtr.h"
 
 #include "xpcprivate.h"
 #include "xpcpublic.h"
 #include "XPCWrapper.h"
 #include "XPCJSMemoryReporter.h"
@@ -72,17 +72,17 @@
 using namespace mozilla;
 using namespace xpc;
 using namespace JS;
 using mozilla::dom::PerThreadAtomCache;
 using mozilla::dom::AutoEntryScript;
 
 /***************************************************************************/
 
-const char* const XPCJSRuntime::mStrings[] = {
+const char* const XPCJSContext::mStrings[] = {
     "constructor",          // IDX_CONSTRUCTOR
     "toString",             // IDX_TO_STRING
     "toSource",             // IDX_TO_SOURCE
     "lastResult",           // IDX_LAST_RESULT
     "returnCode",           // IDX_RETURN_CODE
     "value",                // IDX_VALUE
     "QueryInterface",       // IDX_QUERY_INTERFACE
     "Components",           // IDX_COMPONENTS
@@ -496,29 +496,29 @@ EnableUniversalXPConnect(JSContext* cx)
         return true;
     scope->ForcePrivilegedComponents();
     return scope->AttachComponentsObject(cx);
 }
 
 JSObject*
 UnprivilegedJunkScope()
 {
-    return XPCJSRuntime::Get()->UnprivilegedJunkScope();
+    return XPCJSContext::Get()->UnprivilegedJunkScope();
 }
 
 JSObject*
 PrivilegedJunkScope()
 {
-    return XPCJSRuntime::Get()->PrivilegedJunkScope();
+    return XPCJSContext::Get()->PrivilegedJunkScope();
 }
 
 JSObject*
 CompilationScope()
 {
-    return XPCJSRuntime::Get()->CompilationScope();
+    return XPCJSContext::Get()->CompilationScope();
 }
 
 nsGlobalWindow*
 WindowOrNull(JSObject* aObj)
 {
     MOZ_ASSERT(aObj);
     MOZ_ASSERT(!js::IsWrapper(aObj));
 
@@ -563,18 +563,18 @@ CurrentWindowOrNull(JSContext* cx)
     return glob ? WindowOrNull(glob) : nullptr;
 }
 
 } // namespace xpc
 
 static void
 CompartmentDestroyedCallback(JSFreeOp* fop, JSCompartment* compartment)
 {
-    // NB - This callback may be called in JS_DestroyRuntime, which happens
-    // after the XPCJSRuntime has been torn down.
+    // NB - This callback may be called in JS_DestroyContext, which happens
+    // after the XPCJSContext has been torn down.
 
     // Get the current compartment private into an AutoPtr (which will do the
     // cleanup for us), and null out the private (which may already be null).
     nsAutoPtr<CompartmentPrivate> priv(CompartmentPrivate::Get(compartment));
     JS_SetCompartmentPrivate(compartment, nullptr);
 }
 
 static size_t
@@ -584,28 +584,28 @@ CompartmentSizeOfIncludingThisCallback(M
     return priv ? priv->SizeOfIncludingThis(mallocSizeOf) : 0;
 }
 
 /*
  * Return true if there exists a non-system inner window which is a current
  * inner window and whose reflector is gray.  We don't merge system
  * compartments, so we don't use them to trigger merging CCs.
  */
-bool XPCJSRuntime::UsefulToMergeZones() const
+bool XPCJSContext::UsefulToMergeZones() const
 {
     MOZ_ASSERT(NS_IsMainThread());
 
     // Turns out, actually making this return true often enough makes Windows
     // mochitest-gl OOM a lot.  Need to figure out what's going on there; see
     // bug 1277036.
 
     return false;
 }
 
-void XPCJSRuntime::TraceNativeBlackRoots(JSTracer* trc)
+void XPCJSContext::TraceNativeBlackRoots(JSTracer* trc)
 {
     // Skip this part if XPConnect is shutting down. We get into
     // bad locking problems with the thread iteration otherwise.
     if (!nsXPConnect::XPConnect()->IsShuttingDown()) {
         // Trace those AutoMarkingPtr lists!
         if (AutoMarkingPtr* roots = Get()->mAutoRoots)
             roots->TraceJSAll(trc);
     }
@@ -615,29 +615,29 @@ void XPCJSRuntime::TraceNativeBlackRoots
     XPCRootSetElem* e;
     for (e = mObjectHolderRoots; e; e = e->GetNextRoot())
         static_cast<XPCJSObjectHolder*>(e)->TraceJS(trc);
 
     dom::TraceBlackJS(trc, JS_GetGCParameter(Context(), JSGC_NUMBER),
                       nsXPConnect::XPConnect()->IsShuttingDown());
 }
 
-void XPCJSRuntime::TraceAdditionalNativeGrayRoots(JSTracer* trc)
+void XPCJSContext::TraceAdditionalNativeGrayRoots(JSTracer* trc)
 {
     XPCWrappedNativeScope::TraceWrappedNativesInAllScopes(trc, this);
 
     for (XPCRootSetElem* e = mVariantRoots; e ; e = e->GetNextRoot())
         static_cast<XPCTraceableVariant*>(e)->TraceJS(trc);
 
     for (XPCRootSetElem* e = mWrappedJSRoots; e ; e = e->GetNextRoot())
         static_cast<nsXPCWrappedJS*>(e)->TraceJS(trc);
 }
 
 void
-XPCJSRuntime::TraverseAdditionalNativeRoots(nsCycleCollectionNoteRootCallback& cb)
+XPCJSContext::TraverseAdditionalNativeRoots(nsCycleCollectionNoteRootCallback& cb)
 {
     XPCWrappedNativeScope::SuspectAllWrappers(this, cb);
 
     for (XPCRootSetElem* e = mVariantRoots; e ; e = e->GetNextRoot()) {
         XPCTraceableVariant* v = static_cast<XPCTraceableVariant*>(e);
         if (nsCCUncollectableMarker::InGeneration(cb,
                                                   v->CCGeneration())) {
            JS::Value val = v->GetJSValPreserveColor();
@@ -648,115 +648,115 @@ XPCJSRuntime::TraverseAdditionalNativeRo
     }
 
     for (XPCRootSetElem* e = mWrappedJSRoots; e ; e = e->GetNextRoot()) {
         cb.NoteXPCOMRoot(ToSupports(static_cast<nsXPCWrappedJS*>(e)));
     }
 }
 
 void
-XPCJSRuntime::UnmarkSkippableJSHolders()
+XPCJSContext::UnmarkSkippableJSHolders()
 {
-    CycleCollectedJSRuntime::UnmarkSkippableJSHolders();
+    CycleCollectedJSContext::UnmarkSkippableJSHolders();
 }
 
 void
-XPCJSRuntime::PrepareForForgetSkippable()
+XPCJSContext::PrepareForForgetSkippable()
 {
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     if (obs) {
         obs->NotifyObservers(nullptr, "cycle-collector-forget-skippable", nullptr);
     }
 }
 
 void
-XPCJSRuntime::BeginCycleCollectionCallback()
+XPCJSContext::BeginCycleCollectionCallback()
 {
     nsJSContext::BeginCycleCollectionCallback();
 
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     if (obs) {
         obs->NotifyObservers(nullptr, "cycle-collector-begin", nullptr);
     }
 }
 
 void
-XPCJSRuntime::EndCycleCollectionCallback(CycleCollectorResults& aResults)
+XPCJSContext::EndCycleCollectionCallback(CycleCollectorResults& aResults)
 {
     nsJSContext::EndCycleCollectionCallback(aResults);
 
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     if (obs) {
         obs->NotifyObservers(nullptr, "cycle-collector-end", nullptr);
     }
 }
 
 void
-XPCJSRuntime::DispatchDeferredDeletion(bool aContinuation, bool aPurge)
+XPCJSContext::DispatchDeferredDeletion(bool aContinuation, bool aPurge)
 {
     mAsyncSnowWhiteFreer->Dispatch(aContinuation, aPurge);
 }
 
 void
 xpc_UnmarkSkippableJSHolders()
 {
-    if (nsXPConnect::XPConnect()->GetRuntime()) {
-        nsXPConnect::XPConnect()->GetRuntime()->UnmarkSkippableJSHolders();
+    if (nsXPConnect::XPConnect()->GetContext()) {
+        nsXPConnect::XPConnect()->GetContext()->UnmarkSkippableJSHolders();
     }
 }
 
 /* static */ void
-XPCJSRuntime::GCSliceCallback(JSContext* cx,
+XPCJSContext::GCSliceCallback(JSContext* cx,
                               JS::GCProgress progress,
                               const JS::GCDescription& desc)
 {
-    XPCJSRuntime* self = nsXPConnect::GetRuntimeInstance();
+    XPCJSContext* self = nsXPConnect::GetContextInstance();
     if (!self)
         return;
 
 #ifdef MOZ_CRASHREPORTER
     CrashReporter::SetGarbageCollecting(progress == JS::GC_CYCLE_BEGIN ||
                                         progress == JS::GC_SLICE_BEGIN);
 #endif
 
     if (self->mPrevGCSliceCallback)
         (*self->mPrevGCSliceCallback)(cx, progress, desc);
 }
 
 /* static */ void
-XPCJSRuntime::DoCycleCollectionCallback(JSContext* cx)
+XPCJSContext::DoCycleCollectionCallback(JSContext* cx)
 {
     // The GC has detected that a CC at this point would collect a tremendous
     // amount of garbage that is being revivified unnecessarily.
     NS_DispatchToCurrentThread(
             NS_NewRunnableFunction([](){nsJSContext::CycleCollectNow(nullptr);}));
 
-    XPCJSRuntime* self = nsXPConnect::GetRuntimeInstance();
+    XPCJSContext* self = nsXPConnect::GetContextInstance();
     if (!self)
         return;
 
     if (self->mPrevDoCycleCollectionCallback)
         (*self->mPrevDoCycleCollectionCallback)(cx);
 }
 
 void
-XPCJSRuntime::CustomGCCallback(JSGCStatus status)
+XPCJSContext::CustomGCCallback(JSGCStatus status)
 {
     nsTArray<xpcGCCallback> callbacks(extraGCCallbacks);
     for (uint32_t i = 0; i < callbacks.Length(); ++i)
         callbacks[i](status);
 }
 
 /* static */ void
-XPCJSRuntime::FinalizeCallback(JSFreeOp* fop,
+XPCJSContext::FinalizeCallback(JSFreeOp* fop,
                                JSFinalizeStatus status,
                                bool isZoneGC,
                                void* data)
 {
-    XPCJSRuntime* self = nsXPConnect::GetRuntimeInstance();
+    XPCJSContext* self = nsXPConnect::GetContextInstance();
     if (!self)
         return;
 
     switch (status) {
         case JSFINALIZE_GROUP_START:
         {
             MOZ_ASSERT(!self->mDoingFinalization, "bad state");
 
@@ -805,17 +805,17 @@ XPCJSRuntime::FinalizeCallback(JSFreeOp*
             // Skip this part if XPConnect is shutting down. We get into
             // bad locking problems with the thread iteration otherwise.
             if (!nsXPConnect::XPConnect()->IsShuttingDown()) {
 
                 // Mark those AutoMarkingPtr lists!
                 if (AutoMarkingPtr* roots = Get()->mAutoRoots)
                     roots->MarkAfterJSFinalizeAll();
 
-                XPCCallContext* ccxp = XPCJSRuntime::Get()->GetCallContext();
+                XPCCallContext* ccxp = XPCJSContext::Get()->GetCallContext();
                 while (ccxp) {
                     // Deal with the strictness of callcontext that
                     // complains if you ask for a set when
                     // it is in a state where the set could not
                     // possibly be valid.
                     if (ccxp->CanGetSet()) {
                         XPCNativeSet* set = ccxp->GetSet();
                         if (set)
@@ -872,17 +872,17 @@ XPCJSRuntime::FinalizeCallback(JSFreeOp*
             //
             // XXX We may decide to not do this on *every* gc cycle.
 
             // Skip this part if XPConnect is shutting down. We get into
             // bad locking problems with the thread iteration otherwise.
             if (!nsXPConnect::XPConnect()->IsShuttingDown()) {
                 // Do the marking...
 
-                XPCCallContext* ccxp = XPCJSRuntime::Get()->GetCallContext();
+                XPCCallContext* ccxp = XPCJSContext::Get()->GetCallContext();
                 while (ccxp) {
                     // Deal with the strictness of callcontext that
                     // complains if you ask for a tearoff when
                     // it is in a state where the tearoff could not
                     // possibly be valid.
                     if (ccxp->CanGetTearOff()) {
                         XPCWrappedNativeTearOff* to =
                             ccxp->GetTearOff();
@@ -920,43 +920,43 @@ XPCJSRuntime::FinalizeCallback(JSFreeOp*
             self->mGCIsRunning = false;
 
             break;
         }
     }
 }
 
 /* static */ void
-XPCJSRuntime::WeakPointerZoneGroupCallback(JSContext* cx, void* data)
+XPCJSContext::WeakPointerZoneGroupCallback(JSContext* cx, void* data)
 {
     // Called before each sweeping slice -- after processing any final marking
     // triggered by barriers -- to clear out any references to things that are
     // about to be finalized and update any pointers to moved GC things.
-    XPCJSRuntime* self = static_cast<XPCJSRuntime*>(data);
+    XPCJSContext* self = static_cast<XPCJSContext*>(data);
 
     self->mWrappedJSMap->UpdateWeakPointersAfterGC(self);
 
     XPCWrappedNativeScope::UpdateWeakPointersAfterGC(self);
 }
 
 /* static */ void
-XPCJSRuntime::WeakPointerCompartmentCallback(JSContext* cx, JSCompartment* comp, void* data)
+XPCJSContext::WeakPointerCompartmentCallback(JSContext* cx, JSCompartment* comp, void* data)
 {
     // Called immediately after the ZoneGroup weak pointer callback, but only
     // once for each compartment that is being swept.
-    XPCJSRuntime* self = static_cast<XPCJSRuntime*>(data);
+    XPCJSContext* self = static_cast<XPCJSContext*>(data);
     CompartmentPrivate* xpcComp = CompartmentPrivate::Get(comp);
     if (xpcComp)
         xpcComp->UpdateWeakPointersAfterGC(self);
 }
 
 void
-CompartmentPrivate::UpdateWeakPointersAfterGC(XPCJSRuntime* runtime)
+CompartmentPrivate::UpdateWeakPointersAfterGC(XPCJSContext* context)
 {
-    mWrappedJSMap->UpdateWeakPointersAfterGC(runtime);
+    mWrappedJSMap->UpdateWeakPointersAfterGC(context);
 }
 
 static void WatchdogMain(void* arg);
 class Watchdog;
 class WatchdogManager;
 class AutoLockWatchdog {
     Watchdog* const mWatchdog;
   public:
@@ -1099,22 +1099,22 @@ class Watchdog
 #define PREF_MAX_SCRIPT_RUN_TIME_CONTENT "dom.max_script_run_time"
 #define PREF_MAX_SCRIPT_RUN_TIME_CHROME "dom.max_chrome_script_run_time"
 
 class WatchdogManager : public nsIObserver
 {
   public:
 
     NS_DECL_ISUPPORTS
-    explicit WatchdogManager(XPCJSRuntime* aRuntime) : mRuntime(aRuntime)
-                                                     , mRuntimeState(RUNTIME_INACTIVE)
+    explicit WatchdogManager(XPCJSContext* aContext) : mContext(aContext)
+                                                     , mContextState(CONTEXT_INACTIVE)
     {
-        // All the timestamps start at zero except for runtime state change.
+        // All the timestamps start at zero except for context state change.
         PodArrayZero(mTimestamps);
-        mTimestamps[TimestampRuntimeStateChange] = PR_Now();
+        mTimestamps[TimestampContextStateChange] = PR_Now();
 
         // Enable the watchdog, if appropriate.
         RefreshWatchdog();
 
         // Register ourselves as an observer to get updates on the pref.
         mozilla::Preferences::AddStrongObserver(this, "dom.use_watchdog");
         mozilla::Preferences::AddStrongObserver(this, PREF_MAX_SCRIPT_RUN_TIME_CONTENT);
         mozilla::Preferences::AddStrongObserver(this, PREF_MAX_SCRIPT_RUN_TIME_CHROME);
@@ -1137,44 +1137,44 @@ class WatchdogManager : public nsIObserv
 
     NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic,
                        const char16_t* aData) override
     {
         RefreshWatchdog();
         return NS_OK;
     }
 
-    // Runtime statistics. These live on the watchdog manager, are written
+    // Context statistics. These live on the watchdog manager, are written
     // from the main thread, and are read from the watchdog thread (holding
     // the lock in each case).
     void
-    RecordRuntimeActivity(bool active)
+    RecordContextActivity(bool active)
     {
         // The watchdog reads this state, so acquire the lock before writing it.
         MOZ_ASSERT(NS_IsMainThread());
         Maybe<AutoLockWatchdog> lock;
         if (mWatchdog)
             lock.emplace(mWatchdog);
 
         // Write state.
-        mTimestamps[TimestampRuntimeStateChange] = PR_Now();
-        mRuntimeState = active ? RUNTIME_ACTIVE : RUNTIME_INACTIVE;
-
-        // The watchdog may be hibernating, waiting for the runtime to go
+        mTimestamps[TimestampContextStateChange] = PR_Now();
+        mContextState = active ? CONTEXT_ACTIVE : CONTEXT_INACTIVE;
+
+        // The watchdog may be hibernating, waiting for the context to go
         // active. Wake it up if necessary.
         if (active && mWatchdog && mWatchdog->Hibernating())
             mWatchdog->WakeUp();
     }
-    bool IsRuntimeActive() { return mRuntimeState == RUNTIME_ACTIVE; }
-    PRTime TimeSinceLastRuntimeStateChange()
+    bool IsContextActive() { return mContextState == CONTEXT_ACTIVE; }
+    PRTime TimeSinceLastContextStateChange()
     {
-        return PR_Now() - GetTimestamp(TimestampRuntimeStateChange);
+        return PR_Now() - GetTimestamp(TimestampContextStateChange);
     }
 
-    // Note - Because of the runtime activity timestamp, these are read and
+    // Note - Because of the context activity timestamp, these are read and
     // written from both threads.
     void RecordTimestamp(WatchdogTimestampCategory aCategory)
     {
         // The watchdog thread always holds the lock when it runs.
         Maybe<AutoLockWatchdog> maybeLock;
         if (NS_IsMainThread() && mWatchdog)
             maybeLock.emplace(mWatchdog);
         mTimestamps[aCategory] = PR_Now();
@@ -1183,17 +1183,17 @@ class WatchdogManager : public nsIObserv
     {
         // The watchdog thread always holds the lock when it runs.
         Maybe<AutoLockWatchdog> maybeLock;
         if (NS_IsMainThread() && mWatchdog)
             maybeLock.emplace(mWatchdog);
         return mTimestamps[aCategory];
     }
 
-    XPCJSRuntime* Runtime() { return mRuntime; }
+    XPCJSContext* Context() { return mContext; }
     Watchdog* GetWatchdog() { return mWatchdog; }
 
     void RefreshWatchdog()
     {
         bool wantWatchdog = Preferences::GetBool("dom.use_watchdog", true);
         if (wantWatchdog != !!mWatchdog) {
             if (wantWatchdog)
                 StartWatchdog();
@@ -1222,20 +1222,20 @@ class WatchdogManager : public nsIObserv
     void StopWatchdog()
     {
         MOZ_ASSERT(mWatchdog);
         mWatchdog->Shutdown();
         mWatchdog = nullptr;
     }
 
   private:
-    XPCJSRuntime* mRuntime;
+    XPCJSContext* mContext;
     nsAutoPtr<Watchdog> mWatchdog;
 
-    enum { RUNTIME_ACTIVE, RUNTIME_INACTIVE } mRuntimeState;
+    enum { CONTEXT_ACTIVE, CONTEXT_INACTIVE } mContextState;
     PRTime mTimestamps[TimestampCount];
 };
 
 NS_IMPL_ISUPPORTS(WatchdogManager, nsIObserver)
 
 AutoLockWatchdog::AutoLockWatchdog(Watchdog* aWatchdog) : mWatchdog(aWatchdog)
 {
     PR_Lock(mWatchdog->GetLock());
@@ -1256,18 +1256,18 @@ WatchdogMain(void* arg)
 
     // Lock lasts until we return
     AutoLockWatchdog lock(self);
 
     MOZ_ASSERT(self->Initialized());
     MOZ_ASSERT(!self->ShuttingDown());
     while (!self->ShuttingDown()) {
         // Sleep only 1 second if recently (or currently) active; otherwise, hibernate
-        if (manager->IsRuntimeActive() ||
-            manager->TimeSinceLastRuntimeStateChange() <= PRTime(2*PR_USEC_PER_SEC))
+        if (manager->IsContextActive() ||
+            manager->TimeSinceLastContextStateChange() <= PRTime(2*PR_USEC_PER_SEC))
         {
             self->Sleep(PR_TicksPerSecond());
         } else {
             manager->RecordTimestamp(TimestampWatchdogHibernateStart);
             self->Hibernate();
             manager->RecordTimestamp(TimestampWatchdogHibernateStop);
         }
 
@@ -1284,61 +1284,61 @@ WatchdogMain(void* arg)
         // elapsed. The callback simply records the fact that it was called in
         // the mSlowScriptSecondHalf flag. Then we wait another (timeout/2)
         // seconds and invoke the callback again. This time around it sees
         // mSlowScriptSecondHalf is set and so it shows the slow script
         // dialog. If the computer is put to sleep during one of the (timeout/2)
         // periods, the script still has the other (timeout/2) seconds to
         // finish.
         PRTime usecs = self->MinScriptRunTimeSeconds() * PR_USEC_PER_SEC / 2;
-        if (manager->IsRuntimeActive() &&
-            manager->TimeSinceLastRuntimeStateChange() >= usecs)
+        if (manager->IsContextActive() &&
+            manager->TimeSinceLastContextStateChange() >= usecs)
         {
             bool debuggerAttached = false;
             nsCOMPtr<nsIDebug2> dbg = do_GetService("@mozilla.org/xpcom/debug;1");
             if (dbg)
                 dbg->GetIsDebuggerAttached(&debuggerAttached);
             if (!debuggerAttached)
-                JS_RequestInterruptCallback(manager->Runtime()->Context());
+                JS_RequestInterruptCallback(manager->Context()->Context());
         }
     }
 
     // Tell the manager that we've shut down.
     self->Finished();
 }
 
 PRTime
-XPCJSRuntime::GetWatchdogTimestamp(WatchdogTimestampCategory aCategory)
+XPCJSContext::GetWatchdogTimestamp(WatchdogTimestampCategory aCategory)
 {
     return mWatchdogManager->GetTimestamp(aCategory);
 }
 
 void
 xpc::SimulateActivityCallback(bool aActive)
 {
-    XPCJSRuntime::ActivityCallback(XPCJSRuntime::Get(), aActive);
+    XPCJSContext::ActivityCallback(XPCJSContext::Get(), aActive);
 }
 
 // static
 void
-XPCJSRuntime::ActivityCallback(void* arg, bool active)
+XPCJSContext::ActivityCallback(void* arg, bool active)
 {
     if (!active) {
         ProcessHangMonitor::ClearHang();
     }
 
-    XPCJSRuntime* self = static_cast<XPCJSRuntime*>(arg);
-    self->mWatchdogManager->RecordRuntimeActivity(active);
+    XPCJSContext* self = static_cast<XPCJSContext*>(arg);
+    self->mWatchdogManager->RecordContextActivity(active);
 }
 
 // static
 bool
-XPCJSRuntime::InterruptCallback(JSContext* cx)
+XPCJSContext::InterruptCallback(JSContext* cx)
 {
-    XPCJSRuntime* self = XPCJSRuntime::Get();
+    XPCJSContext* self = XPCJSContext::Get();
 
     // Normally we record mSlowScriptCheckpoint when we start to process an
     // event. However, we can run JS outside of event handlers. This code takes
     // care of that case.
     if (self->mSlowScriptCheckpoint.IsNull()) {
         self->mSlowScriptCheckpoint = TimeStamp::NowLoRes();
         self->mSlowScriptSecondHalf = false;
         self->mSlowScriptActualWait = mozilla::TimeDuration();
@@ -1433,17 +1433,17 @@ XPCJSRuntime::InterruptCallback(JSContex
 
     if (response == nsGlobalWindow::AlwaysContinueSlowScript)
         Preferences::SetInt(prefName, 0);
 
     return true;
 }
 
 void
-XPCJSRuntime::CustomOutOfMemoryCallback()
+XPCJSContext::CustomOutOfMemoryCallback()
 {
     if (!Preferences::GetBool("memory.dump_reports_on_oom")) {
         return;
     }
 
     nsCOMPtr<nsIMemoryInfoDumper> dumper =
         do_GetService("@mozilla.org/memory-info-dumper;1");
     if (!dumper) {
@@ -1452,37 +1452,37 @@ XPCJSRuntime::CustomOutOfMemoryCallback(
 
     // If this fails, it fails silently.
     dumper->DumpMemoryInfoToTempDir(NS_LITERAL_STRING("due-to-JS-OOM"),
                                     /* anonymize = */ false,
                                     /* minimizeMemoryUsage = */ false);
 }
 
 void
-XPCJSRuntime::CustomLargeAllocationFailureCallback()
+XPCJSContext::CustomLargeAllocationFailureCallback()
 {
     nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
     if (os) {
         os->NotifyObservers(nullptr, "memory-pressure", u"heap-minimize");
     }
 }
 
 size_t
-XPCJSRuntime::SizeOfIncludingThis(MallocSizeOf mallocSizeOf)
+XPCJSContext::SizeOfIncludingThis(MallocSizeOf mallocSizeOf)
 {
     size_t n = 0;
     n += mallocSizeOf(this);
     n += mWrappedJSMap->SizeOfIncludingThis(mallocSizeOf);
     n += mIID2NativeInterfaceMap->SizeOfIncludingThis(mallocSizeOf);
     n += mClassInfo2NativeSetMap->ShallowSizeOfIncludingThis(mallocSizeOf);
     n += mNativeSetMap->SizeOfIncludingThis(mallocSizeOf);
 
-    n += CycleCollectedJSRuntime::SizeOfExcludingThis(mallocSizeOf);
-
-    // There are other XPCJSRuntime members that could be measured; the above
+    n += CycleCollectedJSContext::SizeOfExcludingThis(mallocSizeOf);
+
+    // There are other XPCJSContext members that could be measured; the above
     // ones have been seen by DMD to be worth measuring.  More stuff may be
     // added later.
 
     return n;
 }
 
 size_t
 CompartmentPrivate::SizeOfIncludingThis(MallocSizeOf mallocSizeOf)
@@ -1490,32 +1490,32 @@ CompartmentPrivate::SizeOfIncludingThis(
     size_t n = mallocSizeOf(this);
     n += mWrappedJSMap->SizeOfIncludingThis(mallocSizeOf);
     n += mWrappedJSMap->SizeOfWrappedJS(mallocSizeOf);
     return n;
 }
 
 /***************************************************************************/
 
-void XPCJSRuntime::SystemIsBeingShutDown()
+void XPCJSContext::SystemIsBeingShutDown()
 {
     for (auto i = mDetachedWrappedNativeProtoMap->Iter(); !i.Done(); i.Next()) {
         auto entry = static_cast<XPCWrappedNativeProtoMap::Entry*>(i.Get());
         auto proto = const_cast<XPCWrappedNativeProto*>(static_cast<const XPCWrappedNativeProto*>(entry->key));
         proto->SystemIsBeingShutDown();
     }
 }
 
 #define JS_OPTIONS_DOT_STR "javascript.options."
 
 static void
 ReloadPrefsCallback(const char* pref, void* data)
 {
-    XPCJSRuntime* runtime = reinterpret_cast<XPCJSRuntime*>(data);
-    JSContext* cx = runtime->Context();
+    XPCJSContext* xpccx = reinterpret_cast<XPCJSContext*>(data);
+    JSContext* cx = xpccx->Context();
 
     bool safeMode = false;
     nsCOMPtr<nsIXULRuntime> xr = do_GetService("@mozilla.org/xre/runtime;1");
     if (xr) {
         xr->GetInSafeMode(&safeMode);
     }
 
     bool useBaseline = Preferences::GetBool(JS_OPTIONS_DOT_STR "baselinejit") && !safeMode;
@@ -1580,26 +1580,26 @@ ReloadPrefsCallback(const char* pref, vo
     JS_SetParallelParsingEnabled(cx, parallelParsing);
     JS_SetOffthreadIonCompilationEnabled(cx, offthreadIonCompilation);
     JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_BASELINE_WARMUP_TRIGGER,
                                   useBaselineEager ? 0 : -1);
     JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_ION_WARMUP_TRIGGER,
                                   useIonEager ? 0 : -1);
 }
 
-XPCJSRuntime::~XPCJSRuntime()
+XPCJSContext::~XPCJSContext()
 {
-    // Elsewhere we abort immediately if XPCJSRuntime initialization fails.
+    // Elsewhere we abort immediately if XPCJSContext initialization fails.
     // Therefore the context must be non-null.
     MOZ_ASSERT(MaybeContext());
 
-    // This destructor runs before ~CycleCollectedJSRuntime, which does the
-    // actual JS_DestroyRuntime() call. But destroying the runtime triggers
-    // one final GC, which can call back into the runtime with various
-    // callback if we aren't careful. Null out the relevant callbacks.
+    // This destructor runs before ~CycleCollectedJSContext, which does the
+    // actual JS_DestroyContext() call. But destroying the context triggers
+    // one final GC, which can call back into the context with various
+    // callbacks if we aren't careful. Null out the relevant callbacks.
     js::SetActivityCallback(Context(), nullptr, nullptr);
     JS_RemoveFinalizeCallback(Context(), FinalizeCallback);
     JS_RemoveWeakPointerZoneGroupCallback(Context(), WeakPointerZoneGroupCallback);
     JS_RemoveWeakPointerCompartmentCallback(Context(), WeakPointerCompartmentCallback);
 
     // Clear any pending exception.  It might be an XPCWrappedJS, and if we try
     // to destroy it later we will crash.
     SetPendingException(nullptr);
@@ -1643,17 +1643,17 @@ XPCJSRuntime::~XPCJSRuntime()
 
     delete mDyingWrappedNativeProtoMap;
     mDyingWrappedNativeProtoMap = nullptr;
 
     delete mDetachedWrappedNativeProtoMap;
     mDetachedWrappedNativeProtoMap = nullptr;
 
 #ifdef MOZ_ENABLE_PROFILER_SPS
-    // Tell the profiler that the runtime is gone
+    // Tell the profiler that the context is gone
     if (PseudoStack* stack = mozilla_get_pseudo_stack())
         stack->sampleContext(nullptr);
 #endif
 
     Preferences::UnregisterCallback(ReloadPrefsCallback, JS_OPTIONS_DOT_STR, this);
 }
 
 // If |*anonymizeID| is non-zero and this is a user compartment, the name will
@@ -1751,23 +1751,23 @@ xpc::GetCurrentCompartmentName(JSContext
     JSCompartment* compartment = GetObjectCompartment(global);
     int anonymizeID = 0;
     GetCompartmentName(compartment, name, &anonymizeID, false);
 }
 
 void
 xpc::AddGCCallback(xpcGCCallback cb)
 {
-    XPCJSRuntime::Get()->AddGCCallback(cb);
+    XPCJSContext::Get()->AddGCCallback(cb);
 }
 
 void
 xpc::RemoveGCCallback(xpcGCCallback cb)
 {
-    XPCJSRuntime::Get()->RemoveGCCallback(cb);
+    XPCJSContext::Get()->RemoveGCCallback(cb);
 }
 
 static int64_t
 JSMainRuntimeGCHeapDistinguishedAmount()
 {
     JSContext* cx = danger::GetJSContext();
     return int64_t(JS_GetGCParameter(cx, JSGC_TOTAL_CHUNKS)) *
            js::gc::ChunkSize;
@@ -1785,17 +1785,17 @@ JSMainRuntimeCompartmentsSystemDistingui
 {
     JSContext* cx = danger::GetJSContext();
     return JS::SystemCompartmentCount(cx);
 }
 
 static int64_t
 JSMainRuntimeCompartmentsUserDistinguishedAmount()
 {
-    JSContext* cx = nsXPConnect::GetRuntimeInstance()->Context();
+    JSContext* cx = nsXPConnect::GetContextInstance()->Context();
     return JS::UserCompartmentCount(cx);
 }
 
 class JSMainRuntimeTemporaryPeakReporter final : public nsIMemoryReporter
 {
     ~JSMainRuntimeTemporaryPeakReporter() {}
 
   public:
@@ -2640,17 +2640,17 @@ class JSMainRuntimeCompartmentsReporter 
     {
         // First we collect the compartment paths.  Then we report them.  Doing
         // the two steps interleaved is a bad idea, because calling
         // |handleReport| from within CompartmentCallback() leads to all manner
         // of assertions.
 
         Data d;
         d.anonymizeID = anonymize ? 1 : 0;
-        JS_IterateCompartments(nsXPConnect::GetRuntimeInstance()->Context(),
+        JS_IterateCompartments(nsXPConnect::GetContextInstance()->Context(),
                                &d, CompartmentCallback);
 
         for (size_t i = 0; i < d.paths.length(); i++)
             REPORT(nsCString(d.paths[i]), KIND_OTHER, UNITS_COUNT, 1,
                 "A live compartment in the main JSRuntime.");
 
         return NS_OK;
     }
@@ -2713,34 +2713,34 @@ class OrphanReporter : public JS::Object
 #ifdef DEBUG
 static bool
 StartsWithExplicit(nsACString& s)
 {
     return StringBeginsWith(s, NS_LITERAL_CSTRING("explicit/"));
 }
 #endif
 
-class XPCJSRuntimeStats : public JS::RuntimeStats
+class XPCJSContextStats : public JS::RuntimeStats
 {
     WindowPaths* mWindowPaths;
     WindowPaths* mTopWindowPaths;
     bool mGetLocations;
     int mAnonymizeID;
 
   public:
-    XPCJSRuntimeStats(WindowPaths* windowPaths, WindowPaths* topWindowPaths,
+    XPCJSContextStats(WindowPaths* windowPaths, WindowPaths* topWindowPaths,
                       bool getLocations, bool anonymize)
       : JS::RuntimeStats(JSMallocSizeOf),
         mWindowPaths(windowPaths),
         mTopWindowPaths(topWindowPaths),
         mGetLocations(getLocations),
         mAnonymizeID(anonymize ? 1 : 0)
     {}
 
-    ~XPCJSRuntimeStats() {
+    ~XPCJSContextStats() {
         for (size_t i = 0; i != compartmentStatsVector.length(); ++i)
             delete static_cast<xpc::CompartmentStatsExtras*>(compartmentStatsVector[i].extra);
 
 
         for (size_t i = 0; i != zoneStatsVector.length(); ++i)
             delete static_cast<xpc::ZoneStatsExtras*>(zoneStatsVector[i].extra);
     }
 
@@ -2848,42 +2848,42 @@ class XPCJSRuntimeStats : public JS::Run
 
 void
 JSReporter::CollectReports(WindowPaths* windowPaths,
                            WindowPaths* topWindowPaths,
                            nsIHandleReportCallback* handleReport,
                            nsISupports* data,
                            bool anonymize)
 {
-    XPCJSRuntime* xpcrt = nsXPConnect::GetRuntimeInstance();
+    XPCJSContext* xpccx = nsXPConnect::GetContextInstance();
 
     // In the first step we get all the stats and stash them in a local
     // data structure.  In the second step we pass all the stashed stats to
     // the callback.  Separating these steps is important because the
     // callback may be a JS function, and executing JS while getting these
     // stats seems like a bad idea.
 
     nsCOMPtr<amIAddonManager> addonManager;
     if (XRE_IsParentProcess()) {
         // Only try to access the service from the main process.
         addonManager = do_GetService("@mozilla.org/addons/integration;1");
     }
     bool getLocations = !!addonManager;
-    XPCJSRuntimeStats rtStats(windowPaths, topWindowPaths, getLocations,
+    XPCJSContextStats rtStats(windowPaths, topWindowPaths, getLocations,
                               anonymize);
     OrphanReporter orphanReporter(XPCConvert::GetISupportsFromJSObject);
-    if (!JS::CollectRuntimeStats(xpcrt->Context(), &rtStats, &orphanReporter,
+    if (!JS::CollectRuntimeStats(xpccx->Context(), &rtStats, &orphanReporter,
                                  anonymize))
     {
         return;
     }
 
-    size_t xpcJSRuntimeSize = xpcrt->SizeOfIncludingThis(JSMallocSizeOf);
-
-    size_t wrappedJSSize = xpcrt->GetMultiCompartmentWrappedJSMap()->SizeOfWrappedJS(JSMallocSizeOf);
+    size_t xpcJSRuntimeSize = xpccx->SizeOfIncludingThis(JSMallocSizeOf);
+
+    size_t wrappedJSSize = xpccx->GetMultiCompartmentWrappedJSMap()->SizeOfWrappedJS(JSMallocSizeOf);
 
     XPCWrappedNativeScope::ScopeSizeInfo sizeInfo(JSMallocSizeOf);
     XPCWrappedNativeScope::AddSizeOfAllScopesIncludingThis(&sizeInfo);
 
     mozJSComponentLoader* loader = mozJSComponentLoader::Get();
     size_t jsComponentLoaderSize = loader ? loader->SizeOfIncludingThis(JSMallocSizeOf) : 0;
 
     // This is the second step (see above).  First we report stuff in the
@@ -3051,17 +3051,17 @@ JSReporter::CollectReports(WindowPaths* 
         KIND_HEAP, jsComponentLoaderSize,
         "XPConnect's JS component loader.");
 }
 
 static nsresult
 JSSizeOfTab(JSObject* objArg, size_t* jsObjectsSize, size_t* jsStringsSize,
             size_t* jsPrivateSize, size_t* jsOtherSize)
 {
-    JSContext* cx = nsXPConnect::GetRuntimeInstance()->Context();
+    JSContext* cx = nsXPConnect::GetContextInstance()->Context();
     JS::RootedObject obj(cx, objArg);
 
     TabSizes sizes;
     OrphanReporter orphanReporter(XPCConvert::GetISupportsFromJSObject);
     NS_ENSURE_TRUE(JS::AddSizeOfTab(cx, obj, moz_malloc_size_of,
                                     &orphanReporter, &sizes),
                    NS_ERROR_OUT_OF_MEMORY);
 
@@ -3272,17 +3272,17 @@ ReadSourceFromFilename(JSContext* cx, co
     // accounting.  Since ConvertToUTF16() now uses js_malloc() instead we
     // update the accounting manually after the fact.
     JS_updateMallocCounter(cx, *len);
 
     return NS_OK;
 }
 
 // The JS engine calls this object's 'load' member function when it needs
-// the source for a chrome JS function. See the comment in the XPCJSRuntime
+// the source for a chrome JS function. See the comment in the XPCJSContext
 // constructor.
 class XPCJSSourceHook: public js::SourceHook {
     bool load(JSContext* cx, const char* filename, char16_t** src, size_t* length) {
         *src = nullptr;
         *length = 0;
 
         if (!nsContentUtils::IsCallerChrome())
             return true;
@@ -3300,17 +3300,17 @@ class XPCJSSourceHook: public js::Source
     }
 };
 
 static const JSWrapObjectCallbacks WrapObjectCallbacks = {
     xpc::WrapperFactory::Rewrap,
     xpc::WrapperFactory::PrepareForWrapping
 };
 
-XPCJSRuntime::XPCJSRuntime()
+XPCJSContext::XPCJSContext()
  : mCallContext(nullptr),
    mAutoRoots(nullptr),
    mResolveName(JSID_VOID),
    mResolvingWrapper(nullptr),
    mWrappedJSMap(JSObject2WrappedJSMap::newMap(XPC_JS_MAP_LENGTH)),
    mWrappedJSClassMap(IID2WrappedJSClassMap::newMap(XPC_JS_CLASS_MAP_LENGTH)),
    mIID2NativeInterfaceMap(IID2NativeInterfaceMap::newMap(XPC_NATIVE_INTERFACE_MAP_LENGTH)),
    mClassInfo2NativeSetMap(ClassInfo2NativeSetMap::newMap(XPC_NATIVE_SET_MAP_LENGTH)),
@@ -3364,19 +3364,19 @@ GetWindowsStackSize()
 
     // Subtract 40 KB (Win32) or 80 KB (Win64) to account for things like
     // the guard page and large PGO stack frames.
     return stackSize - 10 * sizeof(uintptr_t) * 1024;
 }
 #endif
 
 nsresult
-XPCJSRuntime::Initialize()
+XPCJSContext::Initialize()
 {
-    nsresult rv = CycleCollectedJSRuntime::Initialize(nullptr,
+    nsresult rv = CycleCollectedJSContext::Initialize(nullptr,
                                                       JS::DefaultHeapMaxBytes,
                                                       JS::DefaultNurseryBytes);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     MOZ_ASSERT(Context());
     JSContext* cx = Context();
@@ -3534,23 +3534,23 @@ XPCJSRuntime::Initialize()
     // Watch for the JS boolean options.
     ReloadPrefsCallback(nullptr, this);
     Preferences::RegisterCallback(ReloadPrefsCallback, JS_OPTIONS_DOT_STR, this);
 
     return NS_OK;
 }
 
 // static
-XPCJSRuntime*
-XPCJSRuntime::newXPCJSRuntime()
+XPCJSContext*
+XPCJSContext::newXPCJSContext()
 {
-    XPCJSRuntime* self = new XPCJSRuntime();
+    XPCJSContext* self = new XPCJSContext();
     nsresult rv = self->Initialize();
     if (NS_FAILED(rv)) {
-        NS_RUNTIMEABORT("new XPCJSRuntime failed to initialize.");
+        NS_RUNTIMEABORT("new XPCJSContext failed to initialize.");
         delete self;
         return nullptr;
     }
 
     if (self->Context()                         &&
         self->GetMultiCompartmentWrappedJSMap() &&
         self->GetWrappedJSClassMap()            &&
         self->GetIID2NativeInterfaceMap()       &&
@@ -3558,24 +3558,24 @@ XPCJSRuntime::newXPCJSRuntime()
         self->GetNativeSetMap()                 &&
         self->GetThisTranslatorMap()            &&
         self->GetNativeScriptableSharedMap()    &&
         self->GetDyingWrappedNativeProtoMap()   &&
         self->mWatchdogManager) {
         return self;
     }
 
-    NS_RUNTIMEABORT("new XPCJSRuntime failed to initialize.");
+    NS_RUNTIMEABORT("new XPCJSContext failed to initialize.");
 
     delete self;
     return nullptr;
 }
 
 bool
-XPCJSRuntime::JSContextInitialized(JSContext* cx)
+XPCJSContext::JSContextInitialized(JSContext* cx)
 {
     JSAutoRequest ar(cx);
 
     // if it is our first context then we need to generate our string ids
     if (JSID_IS_VOID(mStrIDs[0])) {
         RootedString str(cx);
         for (unsigned i = 0; i < IDX_TOTAL_COUNT; i++) {
             str = JS_AtomizeAndPinString(cx, mStrings[i]);
@@ -3591,17 +3591,17 @@ XPCJSRuntime::JSContextInitialized(JSCon
             return false;
         }
     }
 
     return true;
 }
 
 bool
-XPCJSRuntime::DescribeCustomObjects(JSObject* obj, const js::Class* clasp,
+XPCJSContext::DescribeCustomObjects(JSObject* obj, const js::Class* clasp,
                                     char (&name)[72]) const
 {
     XPCNativeScriptableInfo* si = nullptr;
 
     if (!IS_PROTO_CLASS(clasp)) {
         return false;
     }
 
@@ -3613,17 +3613,17 @@ XPCJSRuntime::DescribeCustomObjects(JSOb
         return false;
     }
 
     snprintf(name, sizeof(name), "JS Object (%s - %s)", clasp->name, si->GetJSClass()->name);
     return true;
 }
 
 bool
-XPCJSRuntime::NoteCustomGCThingXPCOMChildren(const js::Class* clasp, JSObject* obj,
+XPCJSContext::NoteCustomGCThingXPCOMChildren(const js::Class* clasp, JSObject* obj,
                                              nsCycleCollectionTraversalCallback& cb) const
 {
     if (clasp != &XPC_WN_Tearoff_JSClass) {
         return false;
     }
 
     // A tearoff holds a strong reference to its native object
     // (see XPCWrappedNative::FlatJSObjectFinalized). Its XPCWrappedNative
@@ -3631,17 +3631,17 @@ XPCJSRuntime::NoteCustomGCThingXPCOMChil
     XPCWrappedNativeTearOff* to =
         static_cast<XPCWrappedNativeTearOff*>(xpc_GetJSPrivate(obj));
     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "xpc_GetJSPrivate(obj)->mNative");
     cb.NoteXPCOMChild(to->GetNative());
     return true;
 }
 
 void
-XPCJSRuntime::BeforeProcessTask(bool aMightBlock)
+XPCJSContext::BeforeProcessTask(bool aMightBlock)
 {
     MOZ_ASSERT(NS_IsMainThread());
 
     // If ProcessNextEvent was called during a Promise "then" callback, we
     // must process any pending microtasks before blocking in the event loop,
     // otherwise we may deadlock until an event enters the queue later.
     if (aMightBlock) {
         if (Promise::PerformMicroTaskCheckpoint()) {
@@ -3660,45 +3660,45 @@ XPCJSRuntime::BeforeProcessTask(bool aMi
     mSlowScriptSecondHalf = false;
     mSlowScriptActualWait = mozilla::TimeDuration();
     mTimeoutAccumulated = false;
 
     // As we may be entering a nested event loop, we need to
     // cancel any ongoing performance measurement.
     js::ResetPerformanceMonitoring(Get()->Context());
 
-    CycleCollectedJSRuntime::BeforeProcessTask(aMightBlock);
+    CycleCollectedJSContext::BeforeProcessTask(aMightBlock);
 }
 
 void
-XPCJSRuntime::AfterProcessTask(uint32_t aNewRecursionDepth)
+XPCJSContext::AfterProcessTask(uint32_t aNewRecursionDepth)
 {
     // Now that we're back to the event loop, reset the slow script checkpoint.
     mSlowScriptCheckpoint = mozilla::TimeStamp();
     mSlowScriptSecondHalf = false;
 
     // Call cycle collector occasionally.
     MOZ_ASSERT(NS_IsMainThread());
     nsJSContext::MaybePokeCC();
 
-    CycleCollectedJSRuntime::AfterProcessTask(aNewRecursionDepth);
+    CycleCollectedJSContext::AfterProcessTask(aNewRecursionDepth);
 
     // Now that we are certain that the event is complete,
     // we can flush any ongoing performance measurement.
     js::FlushPerformanceMonitoring(Get()->Context());
 }
 
 /***************************************************************************/
 
 void
-XPCJSRuntime::DebugDump(int16_t depth)
+XPCJSContext::DebugDump(int16_t depth)
 {
 #ifdef DEBUG
     depth--;
-    XPC_LOG_ALWAYS(("XPCJSRuntime @ %x", this));
+    XPC_LOG_ALWAYS(("XPCJSContext @ %x", this));
         XPC_LOG_INDENT();
         XPC_LOG_ALWAYS(("mJSContext @ %x", Context()));
 
         XPC_LOG_ALWAYS(("mWrappedJSClassMap @ %x with %d wrapperclasses(s)",
                         mWrappedJSClassMap, mWrappedJSClassMap->Count()));
         // iterate wrappersclasses...
         if (depth && mWrappedJSClassMap->Count()) {
             XPC_LOG_INDENT();
@@ -3763,49 +3763,49 @@ XPCRootSetElem::AddToRootSet(XPCRootSetE
     }
     *listHead = this;
 }
 
 void
 XPCRootSetElem::RemoveFromRootSet()
 {
     nsXPConnect* xpc = nsXPConnect::XPConnect();
-    JS::PokeGC(xpc->GetRuntime()->Context());
+    JS::PokeGC(xpc->GetContext()->Context());
 
     MOZ_ASSERT(mSelfp, "Must be linked");
 
     MOZ_ASSERT(*mSelfp == this, "Link invariant");
     *mSelfp = mNext;
     if (mNext)
         mNext->mSelfp = mSelfp;
 #ifdef DEBUG
     mSelfp = nullptr;
     mNext = nullptr;
 #endif
 }
 
 void
-XPCJSRuntime::AddGCCallback(xpcGCCallback cb)
+XPCJSContext::AddGCCallback(xpcGCCallback cb)
 {
     MOZ_ASSERT(cb, "null callback");
     extraGCCallbacks.AppendElement(cb);
 }
 
 void
-XPCJSRuntime::RemoveGCCallback(xpcGCCallback cb)
+XPCJSContext::RemoveGCCallback(xpcGCCallback cb)
 {
     MOZ_ASSERT(cb, "null callback");
     bool found = extraGCCallbacks.RemoveElement(cb);
     if (!found) {
         NS_ERROR("Removing a callback which was never added.");
     }
 }
 
 void
-XPCJSRuntime::InitSingletonScopes()
+XPCJSContext::InitSingletonScopes()
 {
     // This all happens very early, so we don't bother with cx pushing.
     JSContext* cx = Context();
     JSAutoRequest ar(cx);
     RootedValue v(cx);
     nsresult rv;
 
     // Create the Unprivileged Junk Scope.
@@ -3831,14 +3831,14 @@ XPCJSRuntime::InitSingletonScopes()
     compilationScopeOptions.invisibleToDebugger = true;
     compilationScopeOptions.discardSource = ShouldDiscardSystemSource();
     rv = CreateSandboxObject(cx, &v, /* principal = */ nullptr, compilationScopeOptions);
     MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
     mCompilationScope = js::UncheckedUnwrap(&v.toObject());
 }
 
 void
-XPCJSRuntime::DeleteSingletonScopes()
+XPCJSContext::DeleteSingletonScopes()
 {
     mUnprivilegedJunkScope = nullptr;
     mPrivilegedJunkScope = nullptr;
     mCompilationScope = nullptr;
 }
--- a/js/xpconnect/src/XPCJSID.cpp
+++ b/js/xpconnect/src/XPCJSID.cpp
@@ -705,22 +705,22 @@ nsJSCID::GetService(HandleValue iidval, 
 }
 
 NS_IMETHODIMP
 nsJSCID::Construct(nsIXPConnectWrappedNative* wrapper,
                    JSContext* cx, JSObject* objArg,
                    const CallArgs& args, bool* _retval)
 {
     RootedObject obj(cx, objArg);
-    XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance();
-    if (!rt)
+    XPCJSContext* xpccx = nsXPConnect::GetContextInstance();
+    if (!xpccx)
         return NS_ERROR_FAILURE;
 
     // 'push' a call context and call on it
-    RootedId name(cx, rt->GetStringID(XPCJSRuntime::IDX_CREATE_INSTANCE));
+    RootedId name(cx, xpccx->GetStringID(XPCJSContext::IDX_CREATE_INSTANCE));
     XPCCallContext ccx(cx, obj, nullptr, name, args.length(), args.array(),
                        args.rval().address());
 
     *_retval = XPCWrappedNative::CallMethod(ccx);
     return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/js/xpconnect/src/XPCMaps.cpp
+++ b/js/xpconnect/src/XPCMaps.cpp
@@ -38,17 +38,17 @@ HashNativeKey(const void* data)
 {
     return static_cast<const XPCNativeSetKey*>(data)->Hash();
 }
 
 /***************************************************************************/
 // implement JSObject2WrappedJSMap...
 
 void
-JSObject2WrappedJSMap::UpdateWeakPointersAfterGC(XPCJSRuntime* runtime)
+JSObject2WrappedJSMap::UpdateWeakPointersAfterGC(XPCJSContext* context)
 {
     // Check all wrappers and update their JSObject pointer if it has been
     // moved. Release any wrappers whose weakly held JSObject has died.
 
     nsTArray<RefPtr<nsXPCWrappedJS>> dying;
     for (Map::Enum e(mTable); !e.empty(); e.popFront()) {
         nsXPCWrappedJS* wrapper = e.front().value();
         MOZ_ASSERT(wrapper, "found a null JS wrapper!");
--- a/js/xpconnect/src/XPCMaps.h
+++ b/js/xpconnect/src/XPCMaps.h
@@ -78,17 +78,17 @@ public:
 
     inline uint32_t Count() {return mTable.count();}
 
     inline void Dump(int16_t depth) {
         for (Map::Range r = mTable.all(); !r.empty(); r.popFront())
             r.front().value()->DebugDump(depth);
     }
 
-    void UpdateWeakPointersAfterGC(XPCJSRuntime* runtime);
+    void UpdateWeakPointersAfterGC(XPCJSContext* context);
 
     void ShutdownMarker();
 
     size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
 
     // Report the sum of SizeOfIncludingThis() for all wrapped JS in the map.
     // Each wrapped JS is only in one map.
     size_t SizeOfWrappedJS(mozilla::MallocSizeOf mallocSizeOf) const;
@@ -309,17 +309,17 @@ public:
     }
 
     inline uint32_t Count() { return mTable.EntryCount(); }
 
     PLDHashTable::Iterator Iter() { return mTable.Iter(); }
 
     // ClassInfo2NativeSetMap holds pointers to *some* XPCNativeSets.
     // So we don't want to count those XPCNativeSets, because they are better
-    // counted elsewhere (i.e. in XPCJSRuntime::mNativeSetMap, which holds
+    // counted elsewhere (i.e. in XPCJSContext::mNativeSetMap, which holds
     // pointers to *all* XPCNativeSets).  Hence the "Shallow".
     size_t ShallowSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
 
 private:
     ClassInfo2NativeSetMap();    // no implementation
     explicit ClassInfo2NativeSetMap(int size);
 private:
     PLDHashTable mTable;
--- a/js/xpconnect/src/XPCThrower.cpp
+++ b/js/xpconnect/src/XPCThrower.cpp
@@ -45,20 +45,20 @@ Throw(JSContext* cx, nsresult rv)
  * If there has already been an exception thrown, see if we're throwing the
  * same sort of exception, and if we are, don't clobber the old one. ccx
  * should be the current call context.
  */
 // static
 bool
 XPCThrower::CheckForPendingException(nsresult result, JSContext* cx)
 {
-    nsCOMPtr<nsIException> e = XPCJSRuntime::Get()->GetPendingException();
+    nsCOMPtr<nsIException> e = XPCJSContext::Get()->GetPendingException();
     if (!e)
         return false;
-    XPCJSRuntime::Get()->SetPendingException(nullptr);
+    XPCJSContext::Get()->SetPendingException(nullptr);
 
     nsresult e_result;
     if (NS_FAILED(e->GetResult(&e_result)) || e_result != result)
         return false;
 
     ThrowExceptionObject(cx, e);
     return true;
 }
--- a/js/xpconnect/src/XPCWrappedJS.cpp
+++ b/js/xpconnect/src/XPCWrappedJS.cpp
@@ -36,17 +36,17 @@ using namespace mozilla;
 // When the refcount of a rooting wrapper drops to 1, if there is no weak reference
 // to the wrapper (which can only happen for the root wrapper), it is immediately
 // Destroy()'d. Otherwise, it becomes subject to finalization.
 //
 // When a wrapper is subject to finalization, the wrapper has a refcount of 1. It is
 // now owned exclusively by its JS object. Either a weak reference will be turned into
 // a strong ref which will bring its refcount up to 2 and change the wrapper back to
 // the rooting state, or it will stay alive until the JS object dies. If the JS object
-// dies, then when XPCJSRuntime::FinalizeCallback calls FindDyingJSObjects
+// dies, then when XPCJSContext::FinalizeCallback calls FindDyingJSObjects
 // it will find the wrapper and call Release() in it, destroying the wrapper.
 // Otherwise, the wrapper will stay alive, even if it no longer has a weak reference
 // to it.
 //
 // When the wrapper is subject to finalization, it is kept alive by an implicit reference
 // from the JS object which is invisible to the cycle collector, so the cycle collector
 // does not traverse any children of wrappers that are subject to finalization. This will
 // result in a leak if a wrapper in the non-rooting state has an aggregated native that
@@ -138,17 +138,17 @@ NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrapp
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXPCWrappedJS)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXPCWrappedJS)
     tmp->Unlink();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
-// XPCJSRuntime keeps a table of WJS, so we can remove them from
+// XPCJSContext keeps a table of WJS, so we can remove them from
 // the purple buffer in between CCs.
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsXPCWrappedJS)
     return true;
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsXPCWrappedJS)
     return tmp->CanSkip();
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
@@ -244,17 +244,17 @@ nsXPCWrappedJS::AddRef(void)
 
     MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt");
     nsISupports* base = NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Upcast(this);
     nsrefcnt cnt = mRefCnt.incr(base);
     NS_LOG_ADDREF(this, cnt, "nsXPCWrappedJS", sizeof(*this));
 
     if (2 == cnt && IsValid()) {
         GetJSObject(); // Unmark gray JSObject.
-        mClass->GetRuntime()->AddWrappedJSRoot(this);
+        mClass->GetContext()->AddWrappedJSRoot(this);
     }
 
     return cnt;
 }
 
 MozExternalRefCountType
 nsXPCWrappedJS::Release(void)
 {
@@ -345,20 +345,20 @@ nsXPCWrappedJS::GetNewOrUsed(JS::HandleO
     if (!rootJSObj)
         return NS_ERROR_FAILURE;
 
     xpc::CompartmentPrivate* rootComp = xpc::CompartmentPrivate::Get(rootJSObj);
     MOZ_ASSERT(rootComp);
 
     // Find any existing wrapper.
     RefPtr<nsXPCWrappedJS> root = rootComp->GetWrappedJSMap()->Find(rootJSObj);
-    MOZ_ASSERT_IF(root, !nsXPConnect::GetRuntimeInstance()->GetMultiCompartmentWrappedJSMap()->
+    MOZ_ASSERT_IF(root, !nsXPConnect::GetContextInstance()->GetMultiCompartmentWrappedJSMap()->
                         Find(rootJSObj));
     if (!root) {
-        root = nsXPConnect::GetRuntimeInstance()->GetMultiCompartmentWrappedJSMap()->
+        root = nsXPConnect::GetContextInstance()->GetMultiCompartmentWrappedJSMap()->
             Find(rootJSObj);
     }
 
     nsresult rv = NS_ERROR_FAILURE;
     if (root) {
         RefPtr<nsXPCWrappedJS> wrapper = root->FindOrFindInherited(aIID);
         if (wrapper) {
             wrapper.forget(wrapperResult);
@@ -412,32 +412,32 @@ nsXPCWrappedJS::nsXPCWrappedJS(JSContext
         xpc::CompartmentPrivate::Get(mJSObj)->GetWrappedJSMap()->Add(cx, this);
     } else {
         NS_ADDREF(mRoot);
         mNext = mRoot->mNext;
         mRoot->mNext = this;
 
         // We always start wrappers in the per-compartment table. If adding
         // this wrapper to the chain causes it to cross compartments, we need
-        // to migrate the chain to the global table on the XPCJSRuntime.
+        // to migrate the chain to the global table on the XPCJSContext.
         if (mRoot->IsMultiCompartment()) {
             xpc::CompartmentPrivate::Get(mRoot->mJSObj)->GetWrappedJSMap()->Remove(mRoot);
-            MOZ_RELEASE_ASSERT(nsXPConnect::GetRuntimeInstance()->
+            MOZ_RELEASE_ASSERT(nsXPConnect::GetContextInstance()->
                     GetMultiCompartmentWrappedJSMap()->Add(cx, mRoot));
         }
     }
 }
 
 nsXPCWrappedJS::~nsXPCWrappedJS()
 {
     Destroy();
 }
 
 void
-XPCJSRuntime::RemoveWrappedJS(nsXPCWrappedJS* wrapper)
+XPCJSContext::RemoveWrappedJS(nsXPCWrappedJS* wrapper)
 {
     AssertInvalidWrappedJSNotInTable(wrapper);
     if (!wrapper->IsValid())
         return;
 
     // It is possible for the same JS XPCOM implementation object to be wrapped
     // with a different interface in multiple JSCompartments. In this case, the
     // wrapper chain will contain references to multiple compartments. While we
@@ -460,47 +460,47 @@ NotHasWrapperAssertionCallback(JSContext
 {
     auto wrapper = static_cast<nsXPCWrappedJS*>(data);
     auto xpcComp = xpc::CompartmentPrivate::Get(comp);
     MOZ_ASSERT_IF(xpcComp, !xpcComp->GetWrappedJSMap()->HasWrapper(wrapper));
 }
 #endif
 
 void
-XPCJSRuntime::AssertInvalidWrappedJSNotInTable(nsXPCWrappedJS* wrapper) const
+XPCJSContext::AssertInvalidWrappedJSNotInTable(nsXPCWrappedJS* wrapper) const
 {
 #ifdef DEBUG
     if (!wrapper->IsValid()) {
         MOZ_ASSERT(!GetMultiCompartmentWrappedJSMap()->HasWrapper(wrapper));
         if (!mGCIsRunning)
             JS_IterateCompartments(Context(), wrapper, NotHasWrapperAssertionCallback);
     }
 #endif
 }
 
 void
 nsXPCWrappedJS::Destroy()
 {
     MOZ_ASSERT(1 == int32_t(mRefCnt), "should be stabilized for deletion");
 
     if (IsRootWrapper())
-        nsXPConnect::GetRuntimeInstance()->RemoveWrappedJS(this);
+        nsXPConnect::GetContextInstance()->RemoveWrappedJS(this);
     Unlink();
 }
 
 void
 nsXPCWrappedJS::Unlink()
 {
-    nsXPConnect::GetRuntimeInstance()->AssertInvalidWrappedJSNotInTable(this);
+    nsXPConnect::GetContextInstance()->AssertInvalidWrappedJSNotInTable(this);
 
     if (IsValid()) {
-        XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance();
-        if (rt) {
+        XPCJSContext* cx = nsXPConnect::GetContextInstance();
+        if (cx) {
             if (IsRootWrapper())
-                rt->RemoveWrappedJS(this);
+                cx->RemoveWrappedJS(this);
 
             if (mRefCnt > 1)
                 RemoveFromRootSet();
         }
 
         mJSObj = nullptr;
     }
 
@@ -525,18 +525,18 @@ nsXPCWrappedJS::Unlink()
         // cannot get a JSContext here.
 
         // let the root go
         NS_RELEASE(mRoot);
     }
 
     mClass = nullptr;
     if (mOuter) {
-        XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance();
-        if (rt->GCIsRunning()) {
+        XPCJSContext* cx = nsXPConnect::GetContextInstance();
+        if (cx->GCIsRunning()) {
             DeferredFinalize(mOuter.forget().take());
         } else {
             mOuter = nullptr;
         }
     }
 }
 
 bool
--- a/js/xpconnect/src/XPCWrappedJSClass.cpp
+++ b/js/xpconnect/src/XPCWrappedJSClass.cpp
@@ -77,41 +77,41 @@ bool xpc_IsReportableErrorCode(nsresult 
         case NS_ERROR_FACTORY_REGISTER_AGAIN:
         case NS_BASE_STREAM_WOULD_BLOCK:
             return false;
         default:
             return true;
     }
 }
 
-// A little stack-based RAII class to help management of the XPCJSRuntime
+// A little stack-based RAII class to help management of the XPCJSContext
 // PendingResult.
 class MOZ_STACK_CLASS AutoSavePendingResult {
 public:
-    explicit AutoSavePendingResult(XPCJSRuntime* xpcrt) :
-        mXPCRuntime(xpcrt)
+    explicit AutoSavePendingResult(XPCJSContext* xpccx) :
+        mXPCContext(xpccx)
     {
         // Save any existing pending result and reset to NS_OK for this invocation.
-        mSavedResult = xpcrt->GetPendingResult();
-        xpcrt->SetPendingResult(NS_OK);
+        mSavedResult = xpccx->GetPendingResult();
+        xpccx->SetPendingResult(NS_OK);
     }
     ~AutoSavePendingResult() {
-        mXPCRuntime->SetPendingResult(mSavedResult);
+        mXPCContext->SetPendingResult(mSavedResult);
     }
 private:
-    XPCJSRuntime* mXPCRuntime;
+    XPCJSContext* mXPCContext;
     nsresult mSavedResult;
 };
 
 // static
 already_AddRefed<nsXPCWrappedJSClass>
 nsXPCWrappedJSClass::GetNewOrUsed(JSContext* cx, REFNSIID aIID, bool allowNonScriptable)
 {
-    XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance();
-    IID2WrappedJSClassMap* map = rt->GetWrappedJSClassMap();
+    XPCJSContext* xpccx = nsXPConnect::GetContextInstance();
+    IID2WrappedJSClassMap* map = xpccx->GetWrappedJSClassMap();
     RefPtr<nsXPCWrappedJSClass> clasp = map->Find(aIID);
 
     if (!clasp) {
         nsCOMPtr<nsIInterfaceInfo> info;
         nsXPConnect::XPConnect()->GetInfoForIID(&aIID, getter_AddRefs(info));
         if (info) {
             bool canScript, isBuiltin;
             if (NS_SUCCEEDED(info->IsScriptable(&canScript)) && (canScript || allowNonScriptable) &&
@@ -124,23 +124,23 @@ nsXPCWrappedJSClass::GetNewOrUsed(JSCont
             }
         }
     }
     return clasp.forget();
 }
 
 nsXPCWrappedJSClass::nsXPCWrappedJSClass(JSContext* cx, REFNSIID aIID,
                                          nsIInterfaceInfo* aInfo)
-    : mRuntime(nsXPConnect::GetRuntimeInstance()),
+    : mContext(nsXPConnect::GetContextInstance()),
       mInfo(aInfo),
       mName(nullptr),
       mIID(aIID),
       mDescriptors(nullptr)
 {
-    mRuntime->GetWrappedJSClassMap()->Add(this);
+    mContext->GetWrappedJSClassMap()->Add(this);
 
     uint16_t methodCount;
     if (NS_SUCCEEDED(mInfo->GetMethodCount(&methodCount))) {
         if (methodCount) {
             int wordCount = (methodCount/32)+1;
             if (nullptr != (mDescriptors = new uint32_t[wordCount])) {
                 int i;
                 // init flags to 0;
@@ -163,18 +163,18 @@ nsXPCWrappedJSClass::nsXPCWrappedJSClass
         }
     }
 }
 
 nsXPCWrappedJSClass::~nsXPCWrappedJSClass()
 {
     if (mDescriptors && mDescriptors != &zero_methods_descriptor)
         delete [] mDescriptors;
-    if (mRuntime)
-        mRuntime->GetWrappedJSClassMap()->Remove(this);
+    if (mContext)
+        mContext->GetWrappedJSClassMap()->Remove(this);
 
     if (mName)
         free(mName);
 }
 
 JSObject*
 nsXPCWrappedJSClass::CallQueryInterfaceOnJSObject(JSContext* cx,
                                                   JSObject* jsobjArg,
@@ -204,17 +204,17 @@ nsXPCWrappedJSClass::CallQueryInterfaceO
     AutoScriptEvaluate scriptEval(cx);
 
     // XXX we should install an error reporter that will send reports to
     // the JS error console service.
     if (!scriptEval.StartEvaluating(jsobj))
         return nullptr;
 
     // check upfront for the existence of the function property
-    HandleId funid = mRuntime->GetStringID(XPCJSRuntime::IDX_QUERY_INTERFACE);
+    HandleId funid = mContext->GetStringID(XPCJSContext::IDX_QUERY_INTERFACE);
     if (!JS_GetPropertyById(cx, jsobj, funid, &fun) || fun.isPrimitive())
         return nullptr;
 
     // Ensure that we are asking for a scriptable interface.
     // NB:  It's important for security that this check is here rather
     // than later, since it prevents untrusted objects from implementing
     // some interfaces in JS and aggregating a trusted object to
     // implement intentionally (for security) unscriptable interfaces.
@@ -773,35 +773,35 @@ nsXPCWrappedJSClass::CheckForException(X
                                        const char * anInterfaceName,
                                        nsIException* aSyntheticException)
 {
     JSContext * cx = ccx.GetJSContext();
     MOZ_ASSERT(cx == aes.cx());
     nsCOMPtr<nsIException> xpc_exception = aSyntheticException;
     /* this one would be set by our error reporter */
 
-    XPCJSRuntime* xpcrt = XPCJSRuntime::Get();
+    XPCJSContext* xpccx = XPCJSContext::Get();
 
     // Get this right away in case we do something below to cause JS code
     // to run.
-    nsresult pending_result = xpcrt->GetPendingResult();
+    nsresult pending_result = xpccx->GetPendingResult();
 
     RootedValue js_exception(cx);
     bool is_js_exception = JS_GetPendingException(cx, &js_exception);
 
     /* JS might throw an expection whether the reporter was called or not */
     if (is_js_exception) {
         if (!xpc_exception)
             XPCConvert::JSValToXPCException(&js_exception, anInterfaceName,
                                             aPropertyName,
                                             getter_AddRefs(xpc_exception));
 
         /* cleanup and set failed even if we can't build an exception */
         if (!xpc_exception) {
-            xpcrt->SetPendingException(nullptr); // XXX necessary?
+            xpccx->SetPendingException(nullptr); // XXX necessary?
         }
     }
 
     // Clear the pending exception now, because xpc_exception might be JS-
     // implemented, so invoking methods on it might re-enter JS, which we can't
     // do with an exception on the stack.
     aes.ClearException();
 
@@ -910,17 +910,17 @@ nsXPCWrappedJSClass::CheckForException(X
                     }
                     if (nullptr != scriptError)
                         consoleService->LogMessage(scriptError);
                 }
             }
             // Whether or not it passes the 'reportable' test, it might
             // still be an error and we have to do the right thing here...
             if (NS_FAILED(e_result)) {
-                xpcrt->SetPendingException(xpc_exception);
+                xpccx->SetPendingException(xpc_exception);
                 return e_result;
             }
         }
     } else {
         // see if JS code signaled failure result without throwing exception
         if (NS_FAILED(pending_result)) {
             return pending_result;
         }
@@ -980,28 +980,28 @@ nsXPCWrappedJSClass::CallMethod(nsXPCWra
     RootedObject obj(cx, wrapper->GetJSObject());
     RootedObject thisObj(cx, obj);
 
     JSAutoCompartment ac(cx, obj);
 
     AutoValueVector args(cx);
     AutoScriptEvaluate scriptEval(cx);
 
-    XPCJSRuntime* xpcrt = XPCJSRuntime::Get();
-    AutoSavePendingResult apr(xpcrt);
+    XPCJSContext* xpccx = XPCJSContext::Get();
+    AutoSavePendingResult apr(xpccx);
 
     // XXX ASSUMES that retval is last arg. The xpidl compiler ensures this.
     uint8_t paramCount = info->num_args;
     uint8_t argc = paramCount -
         (paramCount && XPT_PD_IS_RETVAL(info->params[paramCount-1].flags) ? 1 : 0);
 
     if (!scriptEval.StartEvaluating(obj))
         goto pre_call_clean_up;
 
-    xpcrt->SetPendingException(nullptr);
+    xpccx->SetPendingException(nullptr);
 
     // We use js_Invoke so that the gcthings we use as args will be rooted by
     // the engine as we do conversions and prepare to do the function call.
 
     // setup stack
 
     // if this isn't a function call then we don't need to push extra stuff
     if (!(XPT_MD_IS_SETTER(info->flags) || XPT_MD_IS_GETTER(info->flags))) {
@@ -1037,17 +1037,17 @@ nsXPCWrappedJSClass::CallMethod(nsXPCWra
                 const nsXPTParamInfo& firstParam = info->params[0];
                 if (firstParam.IsIn()) {
                     const nsXPTType& firstType = firstParam.GetType();
 
                     if (firstType.IsInterfacePointer()) {
                         nsIXPCFunctionThisTranslator* translator;
 
                         IID2ThisTranslatorMap* map =
-                            mRuntime->GetThisTranslatorMap();
+                            mContext->GetThisTranslatorMap();
 
                         translator = map->Find(mIID);
 
                         if (translator) {
                             nsCOMPtr<nsISupports> newThis;
                             if (NS_FAILED(translator->
                                           TranslateThis((nsISupports*)nativeParams[0].val.p,
                                                         getter_AddRefs(newThis)))) {
@@ -1168,17 +1168,17 @@ nsXPCWrappedJSClass::CallMethod(nsXPCWra
             RootedObject out_obj(cx, NewOutObject(cx));
             if (!out_obj) {
                 retval = NS_ERROR_OUT_OF_MEMORY;
                 goto pre_call_clean_up;
             }
 
             if (param.IsIn()) {
                 if (!JS_SetPropertyById(cx, out_obj,
-                                        mRuntime->GetStringID(XPCJSRuntime::IDX_VALUE),
+                                        mContext->GetStringID(XPCJSContext::IDX_VALUE),
                                         val)) {
                     goto pre_call_clean_up;
                 }
             }
             *sp++ = JS::ObjectValue(*out_obj);
         } else
             *sp++ = val;
     }
@@ -1230,17 +1230,17 @@ pre_call_clean_up:
             success = false;
         }
     }
 
     if (!success)
         return CheckForException(ccx, aes, name, GetInterfaceName(),
                                  syntheticException);
 
-    XPCJSRuntime::Get()->SetPendingException(nullptr); // XXX necessary?
+    XPCJSContext::Get()->SetPendingException(nullptr); // XXX necessary?
 
     // convert out args and result
     // NOTE: this is the total number of native params, not just the args
     // Convert independent params only.
     // When we later convert the dependent params (if any) we will know that
     // the params upon which they depend will have already been converted -
     // regardless of ordering.
 
@@ -1268,17 +1268,17 @@ pre_call_clean_up:
 
         if (param.IsRetval())
             val = rval;
         else if (argv[i].isPrimitive())
             break;
         else {
             RootedObject obj(cx, &argv[i].toObject());
             if (!JS_GetPropertyById(cx, obj,
-                                    mRuntime->GetStringID(XPCJSRuntime::IDX_VALUE),
+                                    mContext->GetStringID(XPCJSContext::IDX_VALUE),
                                     &val))
                 break;
         }
 
         // setup allocator and/or iid
 
         if (type_tag == nsXPTType::T_INTERFACE) {
             if (NS_FAILED(GetInterfaceInfo()->
@@ -1315,17 +1315,17 @@ pre_call_clean_up:
 
             pv = (nsXPTCMiniVariant*) nativeParams[i].val.p;
 
             if (param.IsRetval())
                 val = rval;
             else {
                 RootedObject obj(cx, &argv[i].toObject());
                 if (!JS_GetPropertyById(cx, obj,
-                                        mRuntime->GetStringID(XPCJSRuntime::IDX_VALUE),
+                                        mContext->GetStringID(XPCJSContext::IDX_VALUE),
                                         &val))
                     break;
             }
 
             // setup allocator and/or iid
 
             if (isArray) {
                 if (NS_FAILED(mInfo->GetTypeForParam(methodIndex, &param, 1,
@@ -1368,17 +1368,17 @@ pre_call_clean_up:
     }
 
     if (i != paramCount) {
         // We didn't manage all the result conversions!
         // We have to cleanup any junk that *did* get converted.
         CleanupOutparams(cx, methodIndex, info, nativeParams, /* inOutOnly = */ false, i);
     } else {
         // set to whatever the JS code might have set as the result
-        retval = xpcrt->GetPendingResult();
+        retval = xpccx->GetPendingResult();
     }
 
     return retval;
 }
 
 const char*
 nsXPCWrappedJSClass::GetInterfaceName()
 {
@@ -1431,17 +1431,17 @@ nsXPCWrappedJSClass::DebugDump(int16_t d
             mInfo->GetParent(getter_AddRefs(parent));
             XPC_LOG_ALWAYS(("parent @ %x", parent.get()));
             mInfo->GetMethodCount(&methodCount);
             XPC_LOG_ALWAYS(("MethodCount = %d", methodCount));
             mInfo->GetConstantCount(&i);
             XPC_LOG_ALWAYS(("ConstantCount = %d", i));
             XPC_LOG_OUTDENT();
         }
-        XPC_LOG_ALWAYS(("mRuntime @ %x", mRuntime));
+        XPC_LOG_ALWAYS(("mContext @ %x", mContext));
         XPC_LOG_ALWAYS(("mDescriptors @ %x count = %d", mDescriptors, methodCount));
         if (depth && mDescriptors && methodCount) {
             depth--;
             XPC_LOG_INDENT();
             for (uint16_t i = 0; i < methodCount; i++) {
                 XPC_LOG_ALWAYS(("Method %d is %s%s", \
                                 i, IsReflectable(i) ? "":" NOT ","reflectable"));
             }
--- a/js/xpconnect/src/XPCWrappedNative.cpp
+++ b/js/xpconnect/src/XPCWrappedNative.cpp
@@ -295,17 +295,17 @@ XPCWrappedNative::GetNewOrUsed(xpcObject
     nsWrapperCache* cache = helper.GetWrapperCache();
 
     MOZ_ASSERT(!cache || !cache->GetWrapperPreserveColor(),
                "We assume the caller already checked if it could get the "
                "wrapper from the cache.");
 
     nsresult rv;
 
-    MOZ_ASSERT(!Scope->GetRuntime()->GCIsRunning(),
+    MOZ_ASSERT(!Scope->GetContext()->GCIsRunning(),
                "XPCWrappedNative::GetNewOrUsed called during GC");
 
     nsISupports* identity = helper.GetCanonical();
 
     if (!identity) {
         NS_ERROR("This XPCOM object fails in QueryInterface to nsISupports!");
         return NS_ERROR_FAILURE;
     }
@@ -603,18 +603,18 @@ XPCWrappedNative::Destroy()
         Native2WrappedNativeMap* map = scope->GetWrappedNativeMap();
 
         // Post-1.9 we should not remove this wrapper from the map if it is
         // uninitialized.
         map->Remove(this);
     }
 
     if (mIdentity) {
-        XPCJSRuntime* rt = GetRuntime();
-        if (rt && rt->GetDoingFinalization()) {
+        XPCJSContext* cx = GetContext();
+        if (cx && cx->GetDoingFinalization()) {
             DeferredFinalize(mIdentity.forget().take());
         } else {
             mIdentity = nullptr;
         }
     }
 
     mMaybeScope = nullptr;
 }
@@ -629,17 +629,17 @@ XPCWrappedNative::UpdateScriptableInfo(X
 void
 XPCWrappedNative::SetProto(XPCWrappedNativeProto* p)
 {
     MOZ_ASSERT(!IsWrapperExpired(), "bad ptr!");
 
     MOZ_ASSERT(HasProto());
 
     // Write barrier for incremental GC.
-    JSContext* cx = GetRuntime()->Context();
+    JSContext* cx = GetContext()->Context();
     GetProto()->WriteBarrierPre(cx);
 
     mMaybeProto = p;
 }
 
 // This is factored out so that it can be called publicly
 // static
 void
@@ -898,17 +898,17 @@ XPCWrappedNative::FlatJSObjectFinalized(
             JS_UpdateWeakPointerAfterGCUnbarriered(&jso);
             MOZ_ASSERT(!jso);
 #endif
             to->JSObjectFinalized();
         }
 
         // We also need to release any native pointers held...
         RefPtr<nsISupports> native = to->TakeNative();
-        if (native && GetRuntime()) {
+        if (native && GetContext()) {
             DeferredFinalize(native.forget().take());
         }
 
         to->SetInterface(nullptr);
     }
 
     nsWrapperCache* cache = nullptr;
     CallQueryInterface(mIdentity, &cache);
@@ -1316,17 +1316,17 @@ public:
 
     explicit CallMethodHelper(XPCCallContext& ccx)
         : mCallContext(ccx)
         , mInvokeResult(NS_ERROR_UNEXPECTED)
         , mIFaceInfo(ccx.GetInterface()->GetInterfaceInfo())
         , mMethodInfo(nullptr)
         , mCallee(ccx.GetTearOff()->GetNative())
         , mVTableIndex(ccx.GetMethodIndex())
-        , mIdxValueId(ccx.GetRuntime()->GetStringID(XPCJSRuntime::IDX_VALUE))
+        , mIdxValueId(ccx.GetContext()->GetStringID(XPCJSContext::IDX_VALUE))
         , mJSContextIndex(UINT8_MAX)
         , mOptArgcIndex(UINT8_MAX)
         , mArgv(ccx.GetArgv())
         , mArgc(ccx.GetArgc())
 
     {
         // Success checked later.
         mIFaceInfo->GetMethodInfo(mVTableIndex, &mMethodInfo);
@@ -1351,17 +1351,17 @@ XPCWrappedNative::CallMethod(XPCCallCont
     return CallMethodHelper(ccx).Call();
 }
 
 bool
 CallMethodHelper::Call()
 {
     mCallContext.SetRetVal(JS::UndefinedValue());
 
-    XPCJSRuntime::Get()->SetPendingException(nullptr);
+    XPCJSContext::Get()->SetPendingException(nullptr);
 
     if (mVTableIndex == 0) {
         return QueryInterfaceFastPath();
     }
 
     if (!mMethodInfo) {
         Throw(NS_ERROR_XPC_CANT_GET_METHOD_INFO, mCallContext);
         return false;
@@ -2005,21 +2005,21 @@ CallMethodHelper::CleanupParam(nsXPTCMin
             js::RemoveRawValueRoot(mCallContext, (Value*)&param.val);
             break;
         case nsXPTType::T_INTERFACE:
         case nsXPTType::T_INTERFACE_IS:
             ((nsISupports*)param.val.p)->Release();
             break;
         case nsXPTType::T_ASTRING:
         case nsXPTType::T_DOMSTRING:
-            nsXPConnect::GetRuntimeInstance()->mScratchStrings.Destroy((nsString*)param.val.p);
+            nsXPConnect::GetContextInstance()->mScratchStrings.Destroy((nsString*)param.val.p);
             break;
         case nsXPTType::T_UTF8STRING:
         case nsXPTType::T_CSTRING:
-            nsXPConnect::GetRuntimeInstance()->mScratchCStrings.Destroy((nsCString*)param.val.p);
+            nsXPConnect::GetContextInstance()->mScratchCStrings.Destroy((nsCString*)param.val.p);
             break;
         default:
             MOZ_ASSERT(!type.IsArithmetic(), "Cleanup requested on unexpected type.");
             free(param.val.p);
             break;
     }
 }
 
@@ -2035,19 +2035,19 @@ CallMethodHelper::AllocateStringClass(ns
                type_tag == nsXPTType::T_DOMSTRING ||
                type_tag == nsXPTType::T_UTF8STRING ||
                type_tag == nsXPTType::T_CSTRING,
                "Unexpected string class type!");
 
     // ASTRING and DOMSTRING are very similar, and both use nsString.
     // UTF8_STRING and CSTRING are also quite similar, and both use nsCString.
     if (type_tag == nsXPTType::T_ASTRING || type_tag == nsXPTType::T_DOMSTRING)
-        dp->val.p = nsXPConnect::GetRuntimeInstance()->mScratchStrings.Create();
+        dp->val.p = nsXPConnect::GetContextInstance()->mScratchStrings.Create();
     else
-        dp->val.p = nsXPConnect::GetRuntimeInstance()->mScratchCStrings.Create();
+        dp->val.p = nsXPConnect::GetContextInstance()->mScratchCStrings.Create();
 
     // Check for OOM, in either case.
     if (!dp->val.p) {
         JS_ReportOutOfMemory(mCallContext);
         return false;
     }
 
     // We allocated, so we need to deallocate after the method call completes.
@@ -2312,17 +2312,17 @@ XPCJSObjectHolder::GetJSObject()
     NS_PRECONDITION(mJSObj, "bad object state");
     return mJSObj;
 }
 
 XPCJSObjectHolder::XPCJSObjectHolder(JSObject* obj)
     : mJSObj(obj)
 {
     MOZ_ASSERT(obj);
-    XPCJSRuntime::Get()->AddObjectHolderRoot(this);
+    XPCJSContext::Get()->AddObjectHolderRoot(this);
 }
 
 XPCJSObjectHolder::~XPCJSObjectHolder()
 {
     RemoveFromRootSet();
 }
 
 void
--- a/js/xpconnect/src/XPCWrappedNativeInfo.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeInfo.cpp
@@ -104,27 +104,27 @@ XPCNativeMember::Resolve(XPCCallContext&
     return true;
 }
 
 /***************************************************************************/
 // XPCNativeInterface
 
 XPCNativeInterface::~XPCNativeInterface()
 {
-    XPCJSRuntime::Get()->GetIID2NativeInterfaceMap()->Remove(this);
+    XPCJSContext::Get()->GetIID2NativeInterfaceMap()->Remove(this);
 }
 
 // static
 already_AddRefed<XPCNativeInterface>
 XPCNativeInterface::GetNewOrUsed(const nsIID* iid)
 {
     RefPtr<XPCNativeInterface> iface;
-    XPCJSRuntime* rt = XPCJSRuntime::Get();
+    XPCJSContext* cx = XPCJSContext::Get();
 
-    IID2NativeInterfaceMap* map = rt->GetIID2NativeInterfaceMap();
+    IID2NativeInterfaceMap* map = cx->GetIID2NativeInterfaceMap();
     if (!map)
         return nullptr;
 
     iface = map->Find(*iid);
 
     if (iface)
         return iface.forget();
 
@@ -153,19 +153,19 @@ already_AddRefed<XPCNativeInterface>
 XPCNativeInterface::GetNewOrUsed(nsIInterfaceInfo* info)
 {
     RefPtr<XPCNativeInterface> iface;
 
     const nsIID* iid;
     if (NS_FAILED(info->GetIIDShared(&iid)) || !iid)
         return nullptr;
 
-    XPCJSRuntime* rt = XPCJSRuntime::Get();
+    XPCJSContext* cx = XPCJSContext::Get();
 
-    IID2NativeInterfaceMap* map = rt->GetIID2NativeInterfaceMap();
+    IID2NativeInterfaceMap* map = cx->GetIID2NativeInterfaceMap();
     if (!map)
         return nullptr;
 
     iface = map->Find(*iid);
 
     if (iface)
         return iface.forget();
 
@@ -471,18 +471,18 @@ XPCNativeSet::GetNewOrUsed(const nsIID* 
 
     RefPtr<XPCNativeInterface> iface =
         XPCNativeInterface::GetNewOrUsed(iid);
     if (!iface)
         return nullptr;
 
     XPCNativeSetKey key(iface);
 
-    XPCJSRuntime* rt = XPCJSRuntime::Get();
-    NativeSetMap* map = rt->GetNativeSetMap();
+    XPCJSContext* xpccx = XPCJSContext::Get();
+    NativeSetMap* map = xpccx->GetNativeSetMap();
     if (!map)
         return nullptr;
 
     set = map->Find(&key);
 
     if (set)
         return set;
 
@@ -500,19 +500,19 @@ XPCNativeSet::GetNewOrUsed(const nsIID* 
 }
 
 // static
 XPCNativeSet*
 XPCNativeSet::GetNewOrUsed(nsIClassInfo* classInfo)
 {
     AutoJSContext cx;
     AutoMarkingNativeSetPtr set(cx);
-    XPCJSRuntime* rt = XPCJSRuntime::Get();
+    XPCJSContext* xpccx = XPCJSContext::Get();
 
-    ClassInfo2NativeSetMap* map = rt->GetClassInfo2NativeSetMap();
+    ClassInfo2NativeSetMap* map = xpccx->GetClassInfo2NativeSetMap();
     if (!map)
         return nullptr;
 
     set = map->Find(classInfo);
 
     if (set)
         return set;
 
@@ -553,17 +553,17 @@ XPCNativeSet::GetNewOrUsed(nsIClassInfo*
             }
 
             interfaceArray.AppendElement(iface.forget());
         }
 
         if (interfaceArray.Length() > 0) {
             set = NewInstance(Move(interfaceArray));
             if (set) {
-                NativeSetMap* map2 = rt->GetNativeSetMap();
+                NativeSetMap* map2 = xpccx->GetNativeSetMap();
                 if (!map2)
                     goto out;
 
                 XPCNativeSetKey key(set);
 
                 XPCNativeSet* set2 = map2->Add(&key, set);
                 if (!set2) {
                     NS_ERROR("failed to add our set!");
@@ -598,30 +598,30 @@ out:
 
     return set;
 }
 
 // static
 void
 XPCNativeSet::ClearCacheEntryForClassInfo(nsIClassInfo* classInfo)
 {
-    XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance();
-    ClassInfo2NativeSetMap* map = rt->GetClassInfo2NativeSetMap();
+    XPCJSContext* xpccx = nsXPConnect::GetContextInstance();
+    ClassInfo2NativeSetMap* map = xpccx->GetClassInfo2NativeSetMap();
     if (map)
         map->Remove(classInfo);
 }
 
 // static
 XPCNativeSet*
 XPCNativeSet::GetNewOrUsed(XPCNativeSetKey* key)
 {
     AutoJSContext cx;
     AutoMarkingNativeSetPtr set(cx);
-    XPCJSRuntime* rt = XPCJSRuntime::Get();
-    NativeSetMap* map = rt->GetNativeSetMap();
+    XPCJSContext* xpccx = XPCJSContext::Get();
+    NativeSetMap* map = xpccx->GetNativeSetMap();
     if (!map)
         return nullptr;
 
     set = map->Find(key);
 
     if (set)
         return set;
 
--- a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
@@ -73,17 +73,17 @@ XPC_WN_Shared_ToString(JSContext* cx, un
     CallArgs args = CallArgsFromVp(argc, vp);
     RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
     if (!obj)
         return false;
 
     XPCCallContext ccx(cx, obj);
     if (!ccx.IsValid())
         return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);
-    ccx.SetName(ccx.GetRuntime()->GetStringID(XPCJSRuntime::IDX_TO_STRING));
+    ccx.SetName(ccx.GetContext()->GetStringID(XPCJSContext::IDX_TO_STRING));
     ccx.SetArgsAndResultPtr(args.length(), args.array(), vp);
     return ToStringGuts(ccx);
 }
 
 static bool
 XPC_WN_Shared_ToSource(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
@@ -113,17 +113,17 @@ XPC_WN_Shared_toPrimitive(JSContext* cx,
         return false;
 
     if (hint == JSTYPE_NUMBER) {
         args.rval().set(JS_GetNaNValue(cx));
         return true;
     }
 
     MOZ_ASSERT(hint == JSTYPE_STRING || hint == JSTYPE_VOID);
-    ccx.SetName(ccx.GetRuntime()->GetStringID(XPCJSRuntime::IDX_TO_STRING));
+    ccx.SetName(ccx.GetContext()->GetStringID(XPCJSContext::IDX_TO_STRING));
     ccx.SetArgsAndResultPtr(0, nullptr, args.rval().address());
 
     XPCNativeMember* member = ccx.GetMember();
     if (member && member->IsMethod()) {
         if (!XPCWrappedNative::CallMethod(ccx))
             return false;
 
         if (args.rval().isPrimitive())
@@ -150,18 +150,18 @@ static JSObject*
 GetDoubleWrappedJSObject(XPCCallContext& ccx, XPCWrappedNative* wrapper)
 {
     RootedObject obj(ccx);
     nsCOMPtr<nsIXPConnectWrappedJS>
         underware = do_QueryInterface(wrapper->GetIdentityObject());
     if (underware) {
         RootedObject mainObj(ccx, underware->GetJSObject());
         if (mainObj) {
-            RootedId id(ccx, ccx.GetRuntime()->
-                            GetStringID(XPCJSRuntime::IDX_WRAPPED_JSOBJECT));
+            RootedId id(ccx, ccx.GetContext()->
+                            GetStringID(XPCJSContext::IDX_WRAPPED_JSOBJECT));
 
             JSAutoCompartment ac(ccx, mainObj);
 
             RootedValue val(ccx);
             if (JS_GetPropertyById(ccx, mainObj, id, &val) &&
                 !val.isPrimitive()) {
                 obj = val.toObjectOrNull();
             }
@@ -229,17 +229,17 @@ DefinePropertyIfFound(XPCCallContext& cc
                       XPCWrappedNative* wrapperToReflectInterfaceNames,
                       XPCWrappedNative* wrapperToReflectDoubleWrap,
                       XPCNativeScriptableInfo* scriptableInfo,
                       unsigned propFlags,
                       bool* resolved)
 {
     RootedId id(ccx, idArg);
     RefPtr<XPCNativeInterface> iface = ifaceArg;
-    XPCJSRuntime* rt = ccx.GetRuntime();
+    XPCJSContext* xpccx = ccx.GetContext();
     bool found;
     const char* name;
 
     propFlags |= JSPROP_RESOLVING;
 
     if (set) {
         if (iface)
             found = true;
@@ -262,24 +262,24 @@ DefinePropertyIfFound(XPCCallContext& cc
                     if (NS_FAILED(rv))
                         return Throw(rv, ccx);
                 }
             }
 
             bool overwriteToString = !(flags & nsIClassInfo::DOM_OBJECT)
                 || Preferences::GetBool("dom.XPCToStringForDOMClasses", false);
 
-            if(id == rt->GetStringID(XPCJSRuntime::IDX_TO_STRING)
+            if(id == xpccx->GetStringID(XPCJSContext::IDX_TO_STRING)
                 && overwriteToString)
             {
                 call = XPC_WN_Shared_ToString;
-                name = rt->GetStringName(XPCJSRuntime::IDX_TO_STRING);
-            } else if (id == rt->GetStringID(XPCJSRuntime::IDX_TO_SOURCE)) {
+                name = xpccx->GetStringName(XPCJSContext::IDX_TO_STRING);
+            } else if (id == xpccx->GetStringID(XPCJSContext::IDX_TO_SOURCE)) {
                 call = XPC_WN_Shared_ToSource;
-                name = rt->GetStringName(XPCJSRuntime::IDX_TO_SOURCE);
+                name = xpccx->GetStringName(XPCJSContext::IDX_TO_SOURCE);
             } else if (id == SYMBOL_TO_JSID(
                                JS::GetWellKnownSymbol(ccx, JS::SymbolCode::toPrimitive)))
             {
                 call = XPC_WN_Shared_toPrimitive;
                 name = "[Symbol.toPrimitive]";
             } else {
                 call = nullptr;
             }
@@ -326,25 +326,25 @@ DefinePropertyIfFound(XPCCallContext& cc
                                              propFlags & ~JSPROP_ENUMERATE);
             } else if (NS_FAILED(rv) && rv != NS_ERROR_NO_INTERFACE) {
                 return Throw(rv, ccx);
             }
         }
 
         // This *might* be a double wrapped JSObject
         if (wrapperToReflectDoubleWrap &&
-            id == rt->GetStringID(XPCJSRuntime::IDX_WRAPPED_JSOBJECT) &&
+            id == xpccx->GetStringID(XPCJSContext::IDX_WRAPPED_JSOBJECT) &&
             GetDoubleWrappedJSObject(ccx, wrapperToReflectDoubleWrap)) {
             // We build and add a getter function.
             // A security check is done on a per-get basis.
 
             JSFunction* fun;
 
-            id = rt->GetStringID(XPCJSRuntime::IDX_WRAPPED_JSOBJECT);
-            name = rt->GetStringName(XPCJSRuntime::IDX_WRAPPED_JSOBJECT);
+            id = xpccx->GetStringID(XPCJSContext::IDX_WRAPPED_JSOBJECT);
+            name = xpccx->GetStringName(XPCJSContext::IDX_WRAPPED_JSOBJECT);
 
             fun = JS_NewFunction(ccx, XPC_WN_DoubleWrappedGetter,
                                  0, 0, name);
 
             if (!fun)
                 return false;
 
             RootedObject funobj(ccx, JS_GetFunctionObject(fun));
@@ -407,21 +407,21 @@ DefinePropertyIfFound(XPCCal