merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 13 Apr 2016 11:53:35 +0200
changeset 330824 564b225d553547fe4aa9a1039278f695c9507db9
parent 330766 ea707a9243907d2e89337d01ecf7c66f5f543a86 (current diff)
parent 330823 47162e3a9e0262b29af5dbb43ce1fa51dc3a31a5 (diff)
child 330829 0b2c8e57a6ebe93e58bb598ece22faaa7da9f591
child 330875 03be6d926fac7a1eb2828d6168edcea83a187912
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone48.0a1
first release with
nightly linux32
564b225d5535 / 48.0a1 / 20160413030239 / files
nightly linux64
564b225d5535 / 48.0a1 / 20160413030239 / files
nightly mac
564b225d5535 / 48.0a1 / 20160413030239 / files
nightly win32
564b225d5535 / 48.0a1 / 20160413030239 / files
nightly win64
564b225d5535 / 48.0a1 / 20160413030239 / 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
build/build-clang/clang-static-analysis-linux64-centos6.json
dom/broadcastchannel/tests/chrome.ini
dom/broadcastchannel/tests/test_broadcastchannel_private_browsing.html
testing/web-platform/meta/html/browsers/offline/application-cache-api/api_status_idle.html.ini
testing/web-platform/meta/resource-timing/test_resource_timing.html.ini
xpcom/threads/nsICancelableRunnable.idl
--- a/accessible/base/EventTree.cpp
+++ b/accessible/base/EventTree.cpp
@@ -223,16 +223,18 @@ EventTree::Process()
         }
       }
     }
 
     // Fire reorder event at last.
     if (mFireReorder) {
       nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_REORDER, mContainer);
     }
+
+    mDependentEvents.Clear();
   }
 }
 
 EventTree*
 EventTree::FindOrInsert(Accessible* aContainer)
 {
   if (!mFirst) {
     return mFirst = new EventTree(aContainer);
@@ -379,16 +381,33 @@ EventTree::FindOrInsert(Accessible* aCon
 
     prevNode = node;
   } while ((node = node->mNext));
 
   MOZ_ASSERT(prevNode, "Nowhere to insert");
   return prevNode->mNext = new EventTree(aContainer);
 }
 
+void
+EventTree::Clear()
+{
+  mFirst = nullptr;
+  mNext = nullptr;
+  mContainer = nullptr;
+
+  uint32_t eventsCount = mDependentEvents.Length();
+  for (uint32_t jdx = 0; jdx < eventsCount; jdx++) {
+    AccHideEvent* ev = downcast_accEvent(mDependentEvents[jdx]);
+    if (ev && ev->NeedsShutdown()) {
+      ev->GetDocAccessible()->ShutdownChildrenInSubtree(ev->mAccessible);
+    }
+  }
+  mDependentEvents.Clear();
+}
+
 const EventTree*
 EventTree::Find(const Accessible* aContainer) const
 {
   const EventTree* et = this;
   while (et) {
     if (et->mContainer == aContainer) {
       return et;
     }
@@ -453,23 +472,23 @@ EventTree::Log(uint32_t aLevel) const
 }
 #endif
 
 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.
-  EventTree* node = mFirst;
-  while (node) {
-    if (node->mContainer == aEv->mAccessible) {
-      node->Clear();
+  nsAutoPtr<EventTree>* node = &mFirst;
+  while (*node) {
+    if ((*node)->mContainer == aEv->mAccessible) {
+      *node = Move((*node)->mNext);
       break;
     }
-    node = node->mNext;
+    node = &(*node)->mNext;
   }
 
   AccMutationEvent* prevEvent = mDependentEvents.SafeLastElement(nullptr);
   mDependentEvents.AppendElement(aEv);
 
   // Coalesce text change events from this hide/show event and the previous one.
   if (prevEvent && aEv->mEventType == prevEvent->mEventType) {
     if (aEv->IsHide()) {
--- a/accessible/base/EventTree.h
+++ b/accessible/base/EventTree.h
@@ -51,17 +51,17 @@ private:
 
 
 /**
  * A mutation events coalescence structure.
  */
 class EventTree final {
 public:
   EventTree() :
-    mFirst(nullptr), mNext(nullptr), mContainer(nullptr), mFireReorder(true) { }
+    mFirst(nullptr), mNext(nullptr), mContainer(nullptr), mFireReorder(false) { }
   explicit EventTree(Accessible* aContainer) :
     mFirst(nullptr), mNext(nullptr), mContainer(aContainer), mFireReorder(true) { }
   ~EventTree() { Clear(); }
 
   void Shown(Accessible* aChild)
   {
     RefPtr<AccShowEvent> ev = new AccShowEvent(aChild);
     Mutated(ev);
@@ -89,17 +89,17 @@ private:
   void Process();
 
   /**
    * Return an event subtree for the given accessible.
    */
   EventTree* FindOrInsert(Accessible* aContainer);
 
   void Mutated(AccMutationEvent* aEv);
-  void Clear() { mFirst = nullptr; mNext = nullptr; mContainer = nullptr; }
+  void Clear();
 
   nsAutoPtr<EventTree> mFirst;
   nsAutoPtr<EventTree> mNext;
 
   Accessible* mContainer;
   nsTArray<RefPtr<AccMutationEvent>> mDependentEvents;
   bool mFireReorder;
 
--- a/accessible/tests/mochitest/events/test_coalescence.html
+++ b/accessible/tests/mochitest/events/test_coalescence.html
@@ -9,16 +9,18 @@
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
 
   <script type="application/javascript"
           src="../common.js"></script>
   <script type="application/javascript"
+          src="../states.js"></script>
+  <script type="application/javascript"
           src="../events.js"></script>
 
   <script type="application/javascript">
 
     ////////////////////////////////////////////////////////////////////////////
     // Invoker base classes
 
     const kRemoveElm = 1;
@@ -344,21 +346,91 @@
         this.parent.hidden = true;
       }
 
       this.getID = function removeGrandChildrenNHideParent_getID() {
         return "remove grand children of different parents and then hide their grand parent";
       }
     }
 
+    /**
+     * Remove a child, and then its parent.
+     */
+    function test3()
+    {
+      this.o = getAccessible("t3_o");
+      this.ofc = getAccessible("t3_o").firstChild;
+
+      this.eventSeq = [
+        new invokerChecker(EVENT_HIDE, this.o),
+        new invokerChecker(EVENT_REORDER, "t3_lb"),
+        new unexpectedInvokerChecker(EVENT_HIDE, this.ofc),
+        new unexpectedInvokerChecker(EVENT_REORDER, this.o)
+      ];
+
+      this.invoke = function test3_invoke()
+      {
+        getNode("t3_o").textContent = "";
+        getNode("t3_lb").removeChild(getNode("t3_o"));
+      }
+
+      this.finalCheck = function test3_finalCheck()
+      {
+        testIsDefunct(this.o);
+        testIsDefunct(this.ofc);
+      }
+
+      this.getID = function test3_getID() {
+        return "remove a child, and then its parent";
+      }
+    }
+
+    /**
+     * Remove children, and then a parent of 2nd child.
+     */
+    function test4()
+    {
+      this.o1 = getAccessible("t4_o1");
+      this.o1fc = this.o1.firstChild;
+      this.o2 = getAccessible("t4_o2");
+      this.o2fc = this.o2.firstChild;
+
+      this.eventSeq = [
+        new invokerChecker(EVENT_HIDE, this.o1fc),
+        new invokerChecker(EVENT_HIDE, this.o2),
+        new invokerChecker(EVENT_REORDER, "t4_lb"),
+        new unexpectedInvokerChecker(EVENT_HIDE, this.o2fc),
+        new unexpectedInvokerChecker(EVENT_REORDER, this.o1),
+        new unexpectedInvokerChecker(EVENT_REORDER, this.o2)
+      ];
+
+      this.invoke = function test4_invoke()
+      {
+        getNode("t4_o1").textContent = "";
+        getNode("t4_o2").textContent = "";
+        getNode("t4_lb").removeChild(getNode("t4_o2"));
+      }
+
+      this.finalCheck = function test4_finalCheck()
+      {
+        testIsDefunct(this.o1fc);
+        testIsDefunct(this.o2);
+        testIsDefunct(this.o2fc);
+      }
+
+      this.getID = function test4_getID() {
+        return "remove children, and then a parent of 2nd child";
+      }
+    }
+
     ////////////////////////////////////////////////////////////////////////////
     // Do tests.
 
     //gA11yEventDumpToConsole = true; // debug stuff
-    //enableLogging("events,tree,eventTree,verbose");
+    //enableLogging("tree,eventTree,verbose");
 
     var gQueue = null;
     function doTests()
     {
       gQueue = new eventQueue();
 
       gQueue.push(new removeChildNParent("option1", "select1"));
       gQueue.push(new removeParentNChild("option2", "select2"));
@@ -372,16 +444,18 @@
       gQueue.push(new addParentNChild("testContainer", false));
       gQueue.push(new addParentNChild("testContainer", true));
       gQueue.push(new showParentNChild("select9", "option9", false));
       gQueue.push(new showParentNChild("select10", "option10", true));
       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.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTests);
   </script>
 </head>
@@ -442,10 +516,23 @@
   </div>
 
   <div id="testContainer2">
     <div id="t1_parent">
       <div id="t1_mid1"><div id="t1_child1"></div></div>
       <div id="t1_mid2"><div id="t1_child2"></div></div>
     </div>
   </div>
+
+  <div id="t3">
+    <div role="listbox" id="t3_lb">
+      <div role="option" id="t3_o">opt</div>
+    </div>
+  </div>
+
+  <div id="t4">
+    <div role="listbox" id="t4_lb">
+      <div role="option" id="t4_o1">opt1</div>
+      <div role="option" id="t4_o2">opt2</div>
+    </div>
+  </div>
 </body>
 </html>
--- a/b2g/config/tooltool-manifests/macosx64/releng.manifest
+++ b/b2g/config/tooltool-manifests/macosx64/releng.manifest
@@ -1,13 +1,13 @@
 [
 {
-"version": "clang 3.8.0/r247539",
-"size": 121389802,
-"digest": "2be6b42cfa1e92de4b49a57123f54043fec2d3cf8385276516dc6aaed99c88768ac4aebd7ce2e007ab074163523da29223436a4d1aef82f0f750f08f1b14cd71",
+"version": "clang 3.8.0",
+"size": 133060926,
+"digest": "aff5ad3ac2d41db19d1ba0df5f97b189a7d7e1b6af8c56e22c2b0cced84d75fa98394ded6a4ba5713652e6684a0a46f47aeccf87991f9e849bf8d7d82e564f6f",
 "algorithm": "sha512",
 "filename": "clang.tar.bz2",
 "unpack": true
 },
 {
 "size": 167175,
 "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
 "algorithm": "sha512",
--- a/b2g/dev/config/tooltool-manifests/macosx64/releng.manifest
+++ b/b2g/dev/config/tooltool-manifests/macosx64/releng.manifest
@@ -1,13 +1,13 @@
 [
 {
-"version": "clang 3.8.0/r247539",
-"size": 121389802,
-"digest": "2be6b42cfa1e92de4b49a57123f54043fec2d3cf8385276516dc6aaed99c88768ac4aebd7ce2e007ab074163523da29223436a4d1aef82f0f750f08f1b14cd71",
+"version": "clang 3.8.0",
+"size": 133060926,
+"digest": "aff5ad3ac2d41db19d1ba0df5f97b189a7d7e1b6af8c56e22c2b0cced84d75fa98394ded6a4ba5713652e6684a0a46f47aeccf87991f9e849bf8d7d82e564f6f",
 "algorithm": "sha512",
 "filename": "clang.tar.bz2",
 "unpack": true
 },
 {
 "size": 167175,
 "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
 "algorithm": "sha512",
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -122,17 +122,16 @@ support-files =
   file_bug1045809_1.html
   file_bug1045809_2.html
   file_csp_block_all_mixedcontent.html
   file_csp_block_all_mixedcontent.js
   !/toolkit/components/passwordmgr/test/browser/form_basic.html
   !/toolkit/components/passwordmgr/test/browser/insecure_test.html
   !/toolkit/components/passwordmgr/test/browser/insecure_test_subframe.html
   !/toolkit/content/tests/browser/common/mockTransfer.js
-  !/toolkit/crashreporter/test/browser/crashreport.sjs
   !/toolkit/modules/tests/browser/metadata_*.html
   !/toolkit/mozapps/extensions/test/xpinstall/amosigned.xpi
   !/toolkit/mozapps/extensions/test/xpinstall/corrupt.xpi
   !/toolkit/mozapps/extensions/test/xpinstall/incompatible.xpi
   !/toolkit/mozapps/extensions/test/xpinstall/installtrigger.html
   !/toolkit/mozapps/extensions/test/xpinstall/redirect.sjs
   !/toolkit/mozapps/extensions/test/xpinstall/restartless-unsigned.xpi
   !/toolkit/mozapps/extensions/test/xpinstall/restartless.xpi
--- a/browser/base/content/test/general/browser_aboutCertError.js
+++ b/browser/base/content/test/general/browser_aboutCertError.js
@@ -1,11 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
+"use strict";
+
 // This is testing the aboutCertError page (Bug 1207107).
 
 const GOOD_PAGE = "https://example.com/";
 const BAD_CERT = "https://expired.example.com/";
 const BAD_STS_CERT = "https://badchain.include-subdomains.pinning.example.com:443";
 const {TabStateFlusher} = Cu.import("resource:///modules/sessionstore/TabStateFlusher.jsm", {});
 const ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
 
@@ -144,25 +146,25 @@ add_task(function* checkAdvancedDetails(
     let serializedSecurityInfo = serhelper.serializeToString(serializable);
     return {
       divDisplay: div.style.display,
       text: text.textContent,
       securityInfoAsString: serializedSecurityInfo
     };
   });
   is(message.divDisplay, "block", "Debug information is visible");
-  ok(message.text.contains(BAD_CERT), "Correct URL found");
-  ok(message.text.contains("Certificate has expired"),
+  ok(message.text.includes(BAD_CERT), "Correct URL found");
+  ok(message.text.includes("Certificate has expired"),
      "Correct error message found");
-  ok(message.text.contains("HTTP Strict Transport Security: false"),
+  ok(message.text.includes("HTTP Strict Transport Security: false"),
      "Correct HSTS value found");
-  ok(message.text.contains("HTTP Public Key Pinning: false"),
+  ok(message.text.includes("HTTP Public Key Pinning: false"),
      "Correct HPKP value found");
   let certChain = getCertChain(message.securityInfoAsString);
-  ok(message.text.contains(certChain), "Found certificate chain");
+  ok(message.text.includes(certChain), "Found certificate chain");
 
   gBrowser.removeCurrentTab();
 });
 
 add_task(function* checkAdvancedDetailsForHSTS() {
   info("Loading a bad STS cert page and verifying the advanced details section");
   let browser;
   let certErrorLoaded;
@@ -216,25 +218,25 @@ add_task(function* checkAdvancedDetailsF
     let serializedSecurityInfo = serhelper.serializeToString(serializable);
     return {
       divDisplay: div.style.display,
       text: text.textContent,
       securityInfoAsString: serializedSecurityInfo
     };
   });
   is(message.divDisplay, "block", "Debug information is visible");
-  ok(message.text.contains(badStsUri.spec), "Correct URL found");
-  ok(message.text.contains("requested domain name does not match the server's certificate"),
+  ok(message.text.includes(badStsUri.spec), "Correct URL found");
+  ok(message.text.includes("requested domain name does not match the server's certificate"),
      "Correct error message found");
-  ok(message.text.contains("HTTP Strict Transport Security: false"),
+  ok(message.text.includes("HTTP Strict Transport Security: false"),
      "Correct HSTS value found");
-  ok(message.text.contains("HTTP Public Key Pinning: true"),
+  ok(message.text.includes("HTTP Public Key Pinning: true"),
      "Correct HPKP value found");
   let certChain = getCertChain(message.securityInfoAsString);
-  ok(message.text.contains(certChain), "Found certificate chain");
+  ok(message.text.includes(certChain), "Found certificate chain");
 
   gBrowser.removeCurrentTab();
 });
 
 function waitForCertErrorLoad(browser) {
   return new Promise(resolve => {
     info("Waiting for DOMContentLoaded event");
     browser.addEventListener("DOMContentLoaded", function load() {
--- a/browser/components/sessionstore/test/browser_581937.js
+++ b/browser/components/sessionstore/test/browser_581937.js
@@ -10,10 +10,10 @@ add_task(function* () {
   is(tab.linkedBrowser.currentURI.spec, "about:blank",
      "we will be removing an about:blank tab");
 
   let r = `rand-${Math.random()}`;
   ss.setTabValue(tab, "foobar", r);
 
   yield promiseRemoveTab(tab);
   let closedTabData = ss.getClosedTabData(window);
-  ok(!closedTabData.contains(r), "tab not stored in _closedTabs");
+  ok(!closedTabData.includes(r), "tab not stored in _closedTabs");
 });
--- a/browser/components/sessionstore/test/browser_aboutPrivateBrowsing.js
+++ b/browser/components/sessionstore/test/browser_aboutPrivateBrowsing.js
@@ -12,10 +12,10 @@ add_task(function* () {
   is(gBrowser.browsers[1].currentURI.spec, "about:privatebrowsing",
      "we will be removing an about:privatebrowsing tab");
 
   let r = `rand-${Math.random()}`;
   ss.setTabValue(tab, "foobar", r);
 
   yield promiseRemoveTab(tab);
   let closedTabData = ss.getClosedTabData(window);
-  ok(!closedTabData.contains(r), "tab not stored in _closedTabs");
+  ok(!closedTabData.includes(r), "tab not stored in _closedTabs");
 });
--- a/browser/config/tooltool-manifests/linux32/clang.manifest
+++ b/browser/config/tooltool-manifests/linux32/clang.manifest
@@ -1,10 +1,10 @@
 [
 {
-"version": "clang 3.8.0/r247539",
-"size": 106877168,
-"digest": "1c50c6348eaf429ed59bb603cff63bcc1f870f59216dd3c234db5b1156cfd351d5ee7b820ec31be4d2661eb4213b2e0030e2ba2782b42905d1ec19c7f8bd322a",
+"version": "clang 3.8.0, libgcc 4.8.5",
+"size": 118876936,
+"digest": "f021d7b23cbbcc4086514b4bf20c86c9f94dbe5b4e0f1ef3aaf2bea337430f7c0d0965c55fe8be0e944a46c3b1555b9245c7af8b8b141eac4b47deea977b4852",
 "algorithm": "sha512", 
 "filename": "clang.tar.xz",
 "unpack": true,
 }
 ]
--- a/browser/config/tooltool-manifests/linux64/clang.manifest
+++ b/browser/config/tooltool-manifests/linux64/clang.manifest
@@ -1,13 +1,13 @@
 [
 {
-"version": "clang 3.8.0/r247539",
-"size": 106877168,
-"digest": "1c50c6348eaf429ed59bb603cff63bcc1f870f59216dd3c234db5b1156cfd351d5ee7b820ec31be4d2661eb4213b2e0030e2ba2782b42905d1ec19c7f8bd322a",
+"version": "clang 3.8.0, libgcc 4.8.5",
+"size": 118876936,
+"digest": "f021d7b23cbbcc4086514b4bf20c86c9f94dbe5b4e0f1ef3aaf2bea337430f7c0d0965c55fe8be0e944a46c3b1555b9245c7af8b8b141eac4b47deea977b4852",
 "algorithm": "sha512",
 "filename": "clang.tar.xz",
 "unpack": true
 },
 {
 "size": 12072532,
 "digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
 "algorithm": "sha512",
--- a/browser/config/tooltool-manifests/linux64/clang.manifest.centos6
+++ b/browser/config/tooltool-manifests/linux64/clang.manifest.centos6
@@ -1,13 +1,13 @@
 [
 {
-"version": "clang 3.8.0/r247539",
-"size": 106877168,
-"digest": "1c50c6348eaf429ed59bb603cff63bcc1f870f59216dd3c234db5b1156cfd351d5ee7b820ec31be4d2661eb4213b2e0030e2ba2782b42905d1ec19c7f8bd322a",
+"version": "clang 3.8.0, libgcc 4.8.5",
+"size": 118876936,
+"digest": "f021d7b23cbbcc4086514b4bf20c86c9f94dbe5b4e0f1ef3aaf2bea337430f7c0d0965c55fe8be0e944a46c3b1555b9245c7af8b8b141eac4b47deea977b4852",
 "algorithm": "sha512",
 "filename": "clang.tar.xz",
 "unpack": true
 },
 {
 "size": 12072532,
 "digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
 "algorithm": "sha512",
--- a/browser/config/tooltool-manifests/macosx64/clang.manifest
+++ b/browser/config/tooltool-manifests/macosx64/clang.manifest
@@ -1,13 +1,13 @@
 [
 {
-"version": "clang 3.8.0/r247539",
-"size": 121389802,
-"digest": "2be6b42cfa1e92de4b49a57123f54043fec2d3cf8385276516dc6aaed99c88768ac4aebd7ce2e007ab074163523da29223436a4d1aef82f0f750f08f1b14cd71",
+"version": "clang 3.8.0",
+"size": 133060926,
+"digest": "aff5ad3ac2d41db19d1ba0df5f97b189a7d7e1b6af8c56e22c2b0cced84d75fa98394ded6a4ba5713652e6684a0a46f47aeccf87991f9e849bf8d7d82e564f6f",
 "algorithm": "sha512",
 "filename": "clang.tar.bz2",
 "unpack": true
 },
 {
 "size": 167175,
 "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
 "algorithm": "sha512",
--- a/browser/config/tooltool-manifests/macosx64/cross-releng.manifest
+++ b/browser/config/tooltool-manifests/macosx64/cross-releng.manifest
@@ -1,13 +1,13 @@
 [
 {
-"version": "clang 3.8.0/r247539",
-"size": 106877168,
-"digest": "1c50c6348eaf429ed59bb603cff63bcc1f870f59216dd3c234db5b1156cfd351d5ee7b820ec31be4d2661eb4213b2e0030e2ba2782b42905d1ec19c7f8bd322a",
+"version": "clang 3.8.0, libgcc 4.8.5",
+"size": 118876936,
+"digest": "f021d7b23cbbcc4086514b4bf20c86c9f94dbe5b4e0f1ef3aaf2bea337430f7c0d0965c55fe8be0e944a46c3b1555b9245c7af8b8b141eac4b47deea977b4852",
 "algorithm": "sha512",
 "filename": "clang.tar.xz",
 "unpack": true
 },
 {
 "size": 3008804, 
 "visibility": "public", 
 "digest": "ba6937f14f3d8b26dcb2d39490dee6b0a8afb60f672f5debb71d7b62c1ec52103201b4b1a3d258f945567de531384b36ddb2ce4aa73dc63d72305b11c146847c", 
--- a/browser/config/tooltool-manifests/macosx64/releng.manifest
+++ b/browser/config/tooltool-manifests/macosx64/releng.manifest
@@ -1,13 +1,13 @@
 [
 {
-"version": "clang 3.8.0/r247539",
-"size": 121389802,
-"digest": "2be6b42cfa1e92de4b49a57123f54043fec2d3cf8385276516dc6aaed99c88768ac4aebd7ce2e007ab074163523da29223436a4d1aef82f0f750f08f1b14cd71",
+"version": "clang 3.8.0",
+"size": 133060926,
+"digest": "aff5ad3ac2d41db19d1ba0df5f97b189a7d7e1b6af8c56e22c2b0cced84d75fa98394ded6a4ba5713652e6684a0a46f47aeccf87991f9e849bf8d7d82e564f6f",
 "algorithm": "sha512",
 "filename": "clang.tar.bz2",
 "unpack": true
 },
 {
 "size": 93295855,
 "digest": "2b8fd0c1ba337a7035090c420305a7892e663ce6781569b100b36fa21cc26146e67f44a34c7715f0004f48bbe46c232bbbf2928c9d0595243d2584530770b504",
 "algorithm": "sha512",
--- a/browser/extensions/loop/chrome/test/xpcshell/test_looprooms.js
+++ b/browser/extensions/loop/chrome/test/xpcshell/test_looprooms.js
@@ -398,17 +398,17 @@ add_task(function* test_createRoom() {
   var expectedRoom = extend({}, kCreateRoomProps);
   expectedRoom.roomToken = kCreateRoomData.roomToken;
 
   gExpectedAdds.push(expectedRoom);
   let room = yield LoopRooms.promise("create", kCreateRoomProps);
 
   // We can't check the value of the key, but check we've got a # which indicates
   // there should be one.
-  Assert.ok(room.roomUrl.contains("#"), "Created room url should have a key");
+  Assert.ok(room.roomUrl.includes("#"), "Created room url should have a key");
   var key = room.roomUrl.split("#")[1];
   Assert.ok(key.length, "Created room url should have non-zero length key");
 
   compareRooms(room, expectedRoom);
 });
 
 // Test if opening a new room window works correctly.
 add_task(function* test_openRoom() {
--- a/browser/extensions/pocket/content/pktApi.jsm
+++ b/browser/extensions/pocket/content/pktApi.jsm
@@ -607,17 +607,17 @@ var pktApi = (function() {
         });
     }
 
     /**
      * Helper function to get current signup AB group the user is in
      */
     function getSignupAB() {
         var setting = getSetting('signupAB');
-        if (!setting || setting.contains('hero'))
+        if (!setting || setting.includes('hero'))
         {
             var rand = (Math.floor(Math.random()*100+1));
             if (rand > 90)
             {
                 setting = 'storyboard_nlm';
             }
             else
             {
--- a/build/autoconf/clang-plugin.m4
+++ b/build/autoconf/clang-plugin.m4
@@ -90,27 +90,30 @@ if test -n "$ENABLE_CLANG_PLUGIN"; then
             CLANG_REPLACE_LDFLAGS="$CLANG_REPLACE_LDFLAGS $arg"
         done
         CLANG_LDFLAGS="$CLANG_REPLACE_LDFLAGS"
     fi
 
     dnl Check for the new ASTMatcher API names.  Since this happened in the
     dnl middle of the 3.8 cycle, our CLANG_VERSION_FULL is impossible to use
     dnl correctly, so we have to detect this at configure time.
-    AC_CACHE_CHECK(for new ASTMatcher names,
-                   ac_cv_have_new_ASTMatcher_names,
+    AC_CACHE_CHECK(for new ASTMatcher API,
+                   ac_cv_have_new_ASTMatcher_api,
         [
             AC_LANG_SAVE
             AC_LANG_CPLUSPLUS
             _SAVE_CXXFLAGS="$CXXFLAGS"
+            _SAVE_CXX="$CXX"
             CXXFLAGS="${LLVM_CXXFLAGS}"
+            CXX="${HOST_CXX}"
             AC_TRY_COMPILE([#include "clang/ASTMatchers/ASTMatchers.h"],
                            [clang::ast_matchers::cxxConstructExpr();],
                            ac_cv_have_new_ASTMatcher_names="yes",
                            ac_cv_have_new_ASTMatcher_names="no")
+            CXX="$_SAVE_CXX"
             CXXFLAGS="$_SAVE_CXXFLAGS"
             AC_LANG_RESTORE
         ])
     if test "$ac_cv_have_new_ASTMatcher_names" = "yes"; then
       LLVM_CXXFLAGS="$LLVM_CXXFLAGS -DHAVE_NEW_ASTMATCHER_NAMES"
     fi
 
     AC_DEFINE(MOZ_CLANG_PLUGIN)
--- a/build/build-clang/build-clang.py
+++ b/build/build-clang/build-clang.py
@@ -109,55 +109,41 @@ def copy_dir_contents(src, dest):
 def mkdir_p(path):
     try:
         os.makedirs(path)
     except OSError as e:
         if e.errno != errno.EEXIST or not os.path.isdir(path):
             raise
 
 
-def build_and_use_libgcc(env, clang_dir):
-    with updated_env(env):
-        tempdir = tempfile.mkdtemp()
-        gcc_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)),
-                               "..", "build-gcc")
-        run_in(gcc_dir, ["./build-gcc.sh", tempdir, "libgcc"])
-        run_in(tempdir, ["tar", "-xf", "gcc.tar.xz"])
-        libgcc_dir = glob.glob(os.path.join(tempdir,
-                                            "gcc", "lib", "gcc",
-                                            "x86_64-unknown-linux-gnu",
+def install_libgcc(gcc_dir, clang_dir):
+        libgcc_dir = glob.glob(os.path.join(gcc_dir, "lib", "gcc",
+                                            "x86_64-*linux-gnu",
                                             "[0-9]*"))[0]
         clang_lib_dir = os.path.join(clang_dir, "lib", "gcc",
                                      "x86_64-unknown-linux-gnu",
                                      os.path.basename(libgcc_dir))
         mkdir_p(clang_lib_dir)
         copy_dir_contents(libgcc_dir, clang_lib_dir)
-        libgcc_dir = os.path.join(tempdir, "gcc", "lib64")
+        libgcc_dir = os.path.join(gcc_dir, "lib64")
         clang_lib_dir = os.path.join(clang_dir, "lib")
         copy_dir_contents(libgcc_dir, clang_lib_dir)
-        include_dir = os.path.join(tempdir, "gcc", "include")
+        include_dir = os.path.join(gcc_dir, "include")
         clang_include_dir = os.path.join(clang_dir, "include")
         copy_dir_contents(include_dir, clang_include_dir)
-        shutil.rmtree(tempdir)
 
 
 def svn_co(source_dir, url, directory, revision):
     run_in(source_dir, ["svn", "co", "-r", revision, url, directory])
 
 
 def svn_update(directory, revision):
     run_in(directory, ["svn", "update", "-r", revision])
 
 
-def build_one_stage(cc, cxx, src_dir, stage_dir, build_libcxx,
-                    build_type, assertions, python_path):
-    build_one_stage_aux(cc, cxx, src_dir, stage_dir, build_libcxx,
-                        build_type, assertions, python_path)
-
-
 def get_platform():
     p = platform.system()
     if p == "Darwin":
         return "macosx64"
     elif p == "Linux":
         if platform.architecture() == "AMD64":
             return "linux64"
         else:
@@ -178,41 +164,47 @@ def is_darwin():
 def is_linux():
     return platform.system() == "Linux"
 
 
 def is_windows():
     return platform.system() == "Windows"
 
 
-def build_one_stage_aux(cc, cxx, src_dir, stage_dir, build_libcxx,
-                        build_type, assertions, python_path):
+def build_one_stage(cc, cxx, src_dir, stage_dir, build_libcxx,
+                    build_type, assertions, python_path, gcc_dir):
     if not os.path.exists(stage_dir):
         os.mkdir(stage_dir)
 
     build_dir = stage_dir + "/build"
     inst_dir = stage_dir + "/clang"
 
     run_cmake = True
     if os.path.exists(build_dir + "/build.ninja"):
         run_cmake = False
 
     cmake_args = ["-GNinja",
-                  "-DCMAKE_C_COMPILER=%s" % cc,
-                  "-DCMAKE_CXX_COMPILER=%s" % cxx,
+                  "-DCMAKE_C_COMPILER=%s" % cc[0],
+                  "-DCMAKE_CXX_COMPILER=%s" % cxx[0],
+                  "-DCMAKE_C_FLAGS=%s" % ' '.join(cc[1:]),
+                  "-DCMAKE_CXX_FLAGS=%s" % ' '.join(cxx[1:]),
                   "-DCMAKE_BUILD_TYPE=%s" % build_type,
                   "-DLLVM_TARGETS_TO_BUILD=X86;ARM",
                   "-DLLVM_ENABLE_ASSERTIONS=%s" % ("ON" if assertions else "OFF"),
                   "-DPYTHON_EXECUTABLE=%s" % python_path,
                   "-DCMAKE_INSTALL_PREFIX=%s" % inst_dir,
                   "-DLLVM_TOOL_LIBCXX_BUILD=%s" % ("ON" if build_libcxx else "OFF"),
                   "-DLIBCXX_LIBCPPABI_VERSION=\"\"",
                   src_dir];
     build_package(build_dir, run_cmake, cmake_args)
 
+    if is_linux():
+        install_libgcc(gcc_dir, inst_dir)
+
+
 if __name__ == "__main__":
     # The directories end up in the debug info, so the easy way of getting
     # a reproducible build is to run it in a know absolute directory.
     # We use a directory in /builds/slave because the mozilla infrastructure
     # cleans it up automatically.
     base_dir = "/builds/slave/moz-toolchain"
     if is_windows():
         base_dir = "c:%s" % base_dir
@@ -338,68 +330,61 @@ if __name__ == "__main__":
         os.makedirs(build_dir)
 
     stage1_dir = build_dir + '/stage1'
     stage1_inst_dir = stage1_dir + '/clang'
 
     final_stage_dir = stage1_dir
 
     if is_darwin():
-        extra_cflags = ""
-        extra_cxxflags = "-stdlib=libc++"
-        extra_cflags2 = ""
-        extra_cxxflags2 = "-stdlib=libc++"
+        extra_cflags = []
+        extra_cxxflags = ["-stdlib=libc++"]
+        extra_cflags2 = []
+        extra_cxxflags2 = ["-stdlib=libc++"]
     elif is_linux():
-        extra_cflags = "-static-libgcc"
-        extra_cxxflags = "-static-libgcc -static-libstdc++"
-        extra_cflags2 = "-fPIC --gcc-toolchain=%s" % gcc_dir
-        extra_cxxflags2 = "-fPIC --gcc-toolchain=%s" % gcc_dir
+        extra_cflags = ["-static-libgcc"]
+        extra_cxxflags = ["-static-libgcc", "-static-libstdc++"]
+        extra_cflags2 = ["-fPIC"]
+        extra_cxxflags2 = ["-fPIC"]
 
         if os.environ.has_key('LD_LIBRARY_PATH'):
             os.environ['LD_LIBRARY_PATH'] = '%s/lib64/:%s' % (gcc_dir, os.environ['LD_LIBRARY_PATH']);
         else:
             os.environ['LD_LIBRARY_PATH'] = '%s/lib64/' % gcc_dir
     elif is_windows():
-        extra_cflags = ""
-        extra_cxxflags = ""
-        extra_cflags2 = ""
-        extra_cxxflags2 = ""
+        extra_cflags = []
+        extra_cxxflags = []
+        extra_cflags2 = []
+        extra_cxxflags2 = []
 
     build_one_stage(
-        cc + " %s" % extra_cflags,
-        cxx + " %s" % extra_cxxflags,
+        [cc] + extra_cflags,
+        [cxx] + extra_cxxflags,
         llvm_source_dir, stage1_dir, build_libcxx,
-        build_type, assertions, python_path)
+        build_type, assertions, python_path, gcc_dir)
 
     if stages > 1:
         stage2_dir = build_dir + '/stage2'
         stage2_inst_dir = stage2_dir + '/clang'
         final_stage_dir = stage2_dir
         build_one_stage(
-            stage1_inst_dir + "/bin/%s%s %s" %
-                (cc_name, exe_ext, extra_cflags2),
-            stage1_inst_dir + "/bin/%s%s %s" %
-                (cxx_name, exe_ext, extra_cxxflags2),
+            [stage1_inst_dir + "/bin/%s%s" %
+                (cc_name, exe_ext)] + extra_cflags2,
+            [stage1_inst_dir + "/bin/%s%s" %
+                (cxx_name, exe_ext)] + extra_cxxflags2,
             llvm_source_dir, stage2_dir, build_libcxx,
-            build_type, assertions, python_path)
+            build_type, assertions, python_path, gcc_dir)
 
-        if stages > 2:
-            stage3_dir = build_dir + '/stage3'
-            final_stage_dir = stage3_dir
-            build_one_stage(
-                stage2_inst_dir + "/bin/%s%s %s" %
-                    (cc_name, exe_ext, extra_cflags2),
-                stage2_inst_dir + "/bin/%s%s %s" %
-                    (cxx_name, exe_ext, extra_cxxflags2),
-                llvm_source_dir, stage3_dir, build_libcxx,
-                build_type, assertions, python_path)
-
-    if is_linux():
-        final_stage_inst_dir = final_stage_dir + '/clang'
-        build_and_use_libgcc(
-            {"CC": cc + " %s" % extra_cflags,
-             "CXX": cxx + " %s" % extra_cxxflags},
-            final_stage_inst_dir)
+    if stages > 2:
+        stage3_dir = build_dir + '/stage3'
+        final_stage_dir = stage3_dir
+        build_one_stage(
+            [stage2_inst_dir + "/bin/%s%s" %
+                (cc_name, exe_ext)] + extra_cflags2,
+            [stage2_inst_dir + "/bin/%s%s" %
+                (cxx_name, exe_ext)] + extra_cxxflags2,
+            llvm_source_dir, stage3_dir, build_libcxx,
+            build_type, assertions, python_path, gcc_dir)
 
     if is_darwin() or is_windows():
         build_tar_package("tar", "clang.tar.bz2", final_stage_dir, "clang")
     else:
         build_tar_package("tar", "clang.tar.xz", final_stage_dir, "clang")
deleted file mode 100644
--- a/build/build-clang/clang-static-analysis-linux64-centos6.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
-    "llvm_revision": "247539",
-    "stages": "3",
-    "build_libcxx": false,
-    "build_type": "Release",
-    "assertions": false,
-    "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/trunk",
-    "clang_repo": "https://llvm.org/svn/llvm-project/cfe/trunk",
-    "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/trunk",
-    "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/trunk",
-    "python_path": "/usr/bin/python2.7",
-    "gcc_dir": "/home/worker/workspace/build/src/gcc",
-    "cc": "/home/worker/workspace/build/src/gcc/bin/gcc",
-    "cxx": "/home/worker/workspace/build/src/gcc/bin/g++",
-    "patches": {
-        "macosx64": [
-          "llvm-debug-frame.patch",
-          "query-selector-visibility.patch",
-          "return-empty-string-non-mangled.patch"
-        ],
-        "linux64": [
-          "llvm-debug-frame.patch",
-          "query-selector-visibility.patch",
-          "return-empty-string-non-mangled.patch"
-        ],
-        "linux32": [
-          "llvm-debug-frame.patch",
-          "query-selector-visibility.patch",
-          "return-empty-string-non-mangled.patch"
-        ]
-    }
-}
--- a/build/build-clang/clang-static-analysis-linux64.json
+++ b/build/build-clang/clang-static-analysis-linux64.json
@@ -1,32 +1,29 @@
 {
-    "llvm_revision": "247539",
+    "llvm_revision": "262557",
     "stages": "3",
     "build_libcxx": false,
     "build_type": "Release",
     "assertions": false,
-    "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/trunk",
-    "clang_repo": "https://llvm.org/svn/llvm-project/cfe/trunk",
-    "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/trunk",
-    "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/trunk",
-    "python_path": "/usr/local/bin/python2.7",
-    "gcc_dir": "/tools/gcc-4.7.3-0moz1",
+    "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/tags/RELEASE_380/final",
+    "clang_repo": "https://llvm.org/svn/llvm-project/cfe/tags/RELEASE_380/final",
+    "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/tags/RELEASE_380/final",
+    "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/tags/RELEASE_380/final",
+    "python_path": "/usr/bin/python2.7",
+    "gcc_dir": "/home/worker/workspace/build/src/gcc",
     "cc": "/home/worker/workspace/build/src/gcc/bin/gcc",
     "cxx": "/home/worker/workspace/build/src/gcc/bin/g++",
     "patches": {
         "macosx64": [
           "llvm-debug-frame.patch",
-          "query-selector-visibility.patch",
           "return-empty-string-non-mangled.patch"
         ],
         "linux64": [
           "llvm-debug-frame.patch",
-          "query-selector-visibility.patch",
           "return-empty-string-non-mangled.patch"
         ],
         "linux32": [
           "llvm-debug-frame.patch",
-          "query-selector-visibility.patch",
           "return-empty-string-non-mangled.patch"
         ]
     }
 }
--- a/build/build-clang/clang-static-analysis-macosx64.json
+++ b/build/build-clang/clang-static-analysis-macosx64.json
@@ -1,31 +1,29 @@
 {
-    "llvm_revision": "247539",
+    "llvm_revision": "262557",
     "stages": "3",
     "build_libcxx": true,
     "build_type": "Release",
     "assertions": false,
-    "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/trunk",
-    "clang_repo": "https://llvm.org/svn/llvm-project/cfe/trunk",
-    "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/trunk",
-    "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/trunk",
+    "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/tags/RELEASE_380/final",
+    "clang_repo": "https://llvm.org/svn/llvm-project/cfe/tags/RELEASE_380/final",
+    "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/tags/RELEASE_380/final",
+    "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/tags/RELEASE_380/final",
     "python_path": "/usr/local/bin/python2.7",
-    "cc": "/usr/bin/clang",
-    "cxx": "/usr/bin/clang++",
+    "cc": "/Users/cltbld/clang/bin/clang",
+    "cxx": "/Users/cltbld/clang/bin/clang++",
     "patches": {
         "macosx64": [
+          "disable-mac-tsan.patch",
           "llvm-debug-frame.patch",
-          "query-selector-visibility.patch",
           "return-empty-string-non-mangled.patch"
         ],
         "linux64": [
           "llvm-debug-frame.patch",
-          "query-selector-visibility.patch",
           "return-empty-string-non-mangled.patch"
         ],
         "linux32": [
           "llvm-debug-frame.patch",
-          "query-selector-visibility.patch",
           "return-empty-string-non-mangled.patch"
         ]
     }
 }
new file mode 100644
--- /dev/null
+++ b/build/build-clang/disable-mac-tsan.patch
@@ -0,0 +1,11 @@
+--- a/compiler-rt/cmake/config-ix.cmake
++++ b/compiler-rt/cmake/config-ix.cmake
+@@ -617,7 +617,7 @@
+ endif()
+ 
+ if (COMPILER_RT_HAS_SANITIZER_COMMON AND TSAN_SUPPORTED_ARCH AND
+-    OS_NAME MATCHES "Darwin|Linux|FreeBSD")
++    OS_NAME MATCHES "Linux|FreeBSD")
+   set(COMPILER_RT_HAS_TSAN TRUE)
+ else()
+   set(COMPILER_RT_HAS_TSAN FALSE)
--- a/build/build-clang/query-selector-visibility.patch
+++ b/build/build-clang/query-selector-visibility.patch
@@ -1,18 +1,18 @@
 commit 865b9340996f9f9d04b73b187248737dc6fd845e
 Author: Michael Wu <mwu@mozilla.com>
 Date:   Mon Sep 14 17:47:21 2015 -0400
 
     Add support for querying the visibility of a cursor
 
-diff --git a/llvm/tools/clang/include/clang-c/Index.h b/llvm/tools/clang/include/clang-c/Index.h
+diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h
 index fad9cfa..311bfcb 100644
---- a/llvm/tools/clang/include/clang-c/Index.h
-+++ b/llvm/tools/clang/include/clang-c/Index.h
+--- a/clang/include/clang-c/Index.h
++++ b/clang/include/clang-c/Index.h
 @@ -2440,6 +2440,24 @@ enum CXLinkageKind {
  CINDEX_LINKAGE enum CXLinkageKind clang_getCursorLinkage(CXCursor cursor);
  
  /**
 + * \brief Describe the visibility of the entity referred to by a cursor.
 + */
 +enum CXVisibilityKind {
 +  /** \brief This value indicates that no visibility information is available
@@ -28,20 +28,20 @@ index fad9cfa..311bfcb 100644
 +};
 +
 +CINDEX_LINKAGE enum CXVisibilityKind clang_getCursorVisibility(CXCursor cursor);
 +
 +/**
   * \brief Determine the availability of the entity that this cursor refers to,
   * taking the current target platform into account.
   *
-diff --git a/llvm/tools/clang/tools/libclang/CIndex.cpp b/llvm/tools/clang/tools/libclang/CIndex.cpp
+diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
 index 8225a6c..9fa18d3 100644
---- a/llvm/tools/clang/tools/libclang/CIndex.cpp
-+++ b/llvm/tools/clang/tools/libclang/CIndex.cpp
+--- a/clang/tools/libclang/CIndex.cpp
++++ b/clang/tools/libclang/CIndex.cpp
 @@ -6361,6 +6361,27 @@ CXLinkageKind clang_getCursorLinkage(CXCursor cursor) {
  } // end: extern "C"
  
  //===----------------------------------------------------------------------===//
 +// Operations for querying visibility of a cursor.
 +//===----------------------------------------------------------------------===//
 +
 +extern "C" {
@@ -60,20 +60,20 @@ index 8225a6c..9fa18d3 100644
 +  return CXVisibility_Invalid;
 +}
 +} // end: extern "C"
 +
 +//===----------------------------------------------------------------------===//
  // Operations for querying language of a cursor.
  //===----------------------------------------------------------------------===//
  
-diff --git a/llvm/tools/clang/tools/libclang/libclang.exports b/llvm/tools/clang/tools/libclang/libclang.exports
+diff --git a/clang/tools/libclang/libclang.exports b/clang/tools/libclang/libclang.exports
 index f6a7175..a919a8e 100644
---- a/llvm/tools/clang/tools/libclang/libclang.exports
-+++ b/llvm/tools/clang/tools/libclang/libclang.exports
+--- a/clang/tools/libclang/libclang.exports
++++ b/clang/tools/libclang/libclang.exports
 @@ -173,6 +173,7 @@ clang_getCursorSemanticParent
  clang_getCursorSpelling
  clang_getCursorType
  clang_getCursorUSR
 +clang_getCursorVisibility
  clang_getDeclObjCTypeEncoding
  clang_getDefinitionSpellingAndExtent
  clang_getDiagnostic
--- a/build/build-clang/return-empty-string-non-mangled.patch
+++ b/build/build-clang/return-empty-string-non-mangled.patch
@@ -1,21 +1,19 @@
-commit 009de5ea7a1913f0b4619cf514787bd52af38c28
 Author: Michael Wu <mwu@mozilla.com>
 Date:   Thu Sep 24 11:36:08 2015 -0400
 
     Return an empty string when a symbol isn't mangled
 
-diff --git a/llvm/tools/clang/tools/libclang/CIndex.cpp b/llvm/tools/clang/tools/libclang/CIndex.cpp
-index 9fa18d3..1253832 100644
---- a/llvm/tools/clang/tools/libclang/CIndex.cpp
-+++ b/llvm/tools/clang/tools/libclang/CIndex.cpp
-@@ -3891,6 +3891,10 @@ CXString clang_Cursor_getMangling(CXCursor C) {
+diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
+--- a/clang/tools/libclang/CIndex.cpp
++++ b/clang/tools/libclang/CIndex.cpp
+@@ -3990,6 +3990,10 @@
    ASTContext &Ctx = ND->getASTContext();
    std::unique_ptr<MangleContext> MC(Ctx.createMangleContext());
  
 +  // Don't mangle if we don't need to.
 +  if (!MC->shouldMangleCXXName(ND))
 +    return cxstring::createEmpty();
 +
    std::string FrontendBuf;
    llvm::raw_string_ostream FrontendBufOS(FrontendBuf);
-   MC->mangleName(ND, FrontendBufOS);
+   if (MC->shouldMangleDeclName(ND)) {
--- a/build/moz.configure/init.configure
+++ b/build/moz.configure/init.configure
@@ -333,24 +333,25 @@ def mozconfig_options(mozconfig, wanted_
 # Mozilla-Build
 # ==============================================================
 option(env='MOZILLABUILD', nargs=1,
        help='Path to Mozilla Build (Windows-only)')
 
 # It feels dirty replicating this from python/mozbuild/mozbuild/mozconfig.py,
 # but the end goal being that the configure script would go away...
 @depends('MOZILLABUILD')
+@checking('for a shell')
 @imports('sys')
 def shell(mozillabuild):
     shell = 'sh'
     if mozillabuild:
         shell = mozillabuild[0] + '/msys/bin/sh'
     if sys.platform == 'win32':
         shell = shell + '.exe'
-    return shell
+    return find_program(shell)
 
 
 # Host and target systems
 # ==============================================================
 option('--host', nargs=1, help='Define the system type performing the build')
 
 option('--target', nargs=1,
        help='Define the system type where the resulting executables will be '
--- a/build/moz.configure/old.configure
+++ b/build/moz.configure/old.configure
@@ -9,16 +9,17 @@
 def encoded_open(path, mode):
     encoding = 'mbcs' if sys.platform == 'win32' else 'utf-8'
     return codecs.open(path, mode, encoding)
 
 
 option(env='AUTOCONF', nargs=1, help='Path to autoconf 2.13')
 
 @depends(mozconfig, 'AUTOCONF')
+@checking('for autoconf')
 @imports('re')
 def autoconf(mozconfig, autoconf):
     mozconfig_autoconf = None
     if mozconfig['path']:
         make_extra = mozconfig['make_extra']
         if make_extra:
             for assignment in make_extra:
                 m = re.match('(?:export\s+)?AUTOCONF\s*:?=\s*(.+)$',
--- a/build/win32/Makefile.in
+++ b/build/win32/Makefile.in
@@ -1,35 +1,14 @@
 # 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 $(topsrcdir)/config/rules.mk
 
-REDIST_FILES =
-
-ifdef WIN32_REDIST_DIR
-REDIST_FILES += '$(WIN32_REDIST_DIR)'/$(MSVC_C_RUNTIME_DLL)
-REDIST_FILES += '$(WIN32_REDIST_DIR)'/$(MSVC_CXX_RUNTIME_DLL)
-endif
-
-ifdef WIN_UCRT_REDIST_DIR
-REDIST_FILES += $(wildcard $(WIN_UCRT_REDIST_DIR)/api-ms-win-*.dll)
-REDIST_FILES += '$(WIN_UCRT_REDIST_DIR)'/ucrtbase.dll
-endif
-
-ifdef REDIST_FILES
-libs-preqs = \
-	$(call mkdir_deps,$(FINAL_TARGET)) \
-	$(NULL)
-
-libs:: $(libs-preqs)
-	install --preserve-timestamps $(REDIST_FILES) $(FINAL_TARGET)
-endif
-
 # run the binscope tool to make sure the binary and all libraries
 # are using all available Windows OS-level security mechanisms
 # Don't do this in clang-cl since it doesn't support debug information yet.
 ifndef CLANG_CL
 check::
 	$(PYTHON) $(srcdir)/autobinscope.py $(DIST)/bin/$(MOZ_APP_NAME)$(BIN_SUFFIX) $(DIST)/crashreporter-symbols/
 	$(PYTHON) $(srcdir)/autobinscope.py $(DIST)/bin/plugin-container.exe $(DIST)/crashreporter-symbols/
 endif
--- a/build/win32/moz.build
+++ b/build/win32/moz.build
@@ -9,8 +9,20 @@ TEST_DIRS += ['crashinjectdll']
 if CONFIG['ENABLE_TESTS']:
     Program('crashinject')
     SOURCES += [
         'crashinject.cpp',
     ]
     USE_STATIC_LIBS = True
 
 NO_PGO = True
+
+if CONFIG['WIN32_REDIST_DIR']:
+    for f in ['MSVC_C_RUNTIME_DLL', 'MSVC_CXX_RUNTIME_DLL']:
+        FINAL_TARGET_FILES += [
+            '%%%s/%s' % (CONFIG['WIN32_REDIST_DIR'], CONFIG[f])
+        ]
+
+if CONFIG['WIN_UCRT_REDIST_DIR']:
+    for f in ['api-ms-win-*.dll', 'ucrtbase.dll']:
+        FINAL_TARGET_FILES += [
+            '%%%s/%s' % (CONFIG['WIN_UCRT_REDIST_DIR'], f)
+        ]
--- a/client.mk
+++ b/client.mk
@@ -60,29 +60,16 @@ endif
 ifndef TOPSRCDIR
 ifeq (,$(wildcard client.mk))
 TOPSRCDIR := $(patsubst %/,%,$(dir $(MAKEFILE_LIST)))
 else
 TOPSRCDIR := $(CWD)
 endif
 endif
 
-# try to find autoconf 2.13 - discard errors from 'which'
-# MacOS X 10.4 sends "no autoconf*" errors to stdout, discard those via grep
-AUTOCONF ?= $(shell which autoconf-2.13 autoconf2.13 autoconf213 2>/dev/null | grep -v '^no autoconf' | head -1)
-
-# See if the autoconf package was installed through fink
-ifeq (,$(strip $(AUTOCONF)))
-AUTOCONF = $(shell which fink >/dev/null 2>&1 && echo `which fink`/../../lib/autoconf2.13/bin/autoconf)
-endif
-
-ifeq (,$(strip $(AUTOCONF)))
-AUTOCONF=$(error Could not find autoconf 2.13)
-endif
-
 SH := /bin/sh
 PERL ?= perl
 PYTHON ?= $(shell which python2.7 > /dev/null 2>&1 && echo python2.7 || echo python)
 
 CONFIG_GUESS_SCRIPT := $(wildcard $(TOPSRCDIR)/build/autoconf/config.guess)
 ifdef CONFIG_GUESS_SCRIPT
   CONFIG_GUESS := $(shell $(CONFIG_GUESS_SCRIPT))
 endif
@@ -314,18 +301,19 @@ EXTRA_CONFIG_DEPS := \
   $(TOPSRCDIR)/aclocal.m4 \
   $(TOPSRCDIR)/old-configure.in \
   $(wildcard $(TOPSRCDIR)/build/autoconf/*.m4) \
   $(TOPSRCDIR)/js/src/aclocal.m4 \
   $(TOPSRCDIR)/js/src/old-configure.in \
   $(NULL)
 
 $(CONFIGURES): %: %.in $(EXTRA_CONFIG_DEPS)
-	@echo Generating $@ using autoconf
-	cd $(@D); $(AUTOCONF)
+	@echo Generating $@
+	sed '1,/^divert/d' $< > $@
+	chmod +x $@
 
 CONFIG_STATUS_DEPS := \
   $(wildcard $(TOPSRCDIR)/*/confvars.sh) \
   $(CONFIGURES) \
   $(TOPSRCDIR)/CLOBBER \
   $(TOPSRCDIR)/nsprpub/configure \
   $(TOPSRCDIR)/config/milestone.txt \
   $(TOPSRCDIR)/browser/config/version.txt \
--- a/devtools/client/debugger/test/mochitest/browser_dbg_worker-window.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_worker-window.js
@@ -1,11 +1,13 @@
 // Check to make sure that a worker can be attached to a toolbox
 // directly, and that the toolbox has expected properties.
 
+"use strict";
+
 // Whitelisting this test.
 // As part of bug 1077403, the leaking uncaught rejections should be fixed.
 thisTestLeaksUncaughtRejectionsAndShouldBeFixed("[object Object]");
 
 var TAB_URL = EXAMPLE_URL + "doc_WorkerActor.attachThread-tab.html";
 var WORKER_URL = "code_WorkerActor.attachThread-worker.js";
 
 add_task(function* () {
@@ -28,17 +30,17 @@ add_task(function* () {
   let [, workerClient] = yield attachWorker(tabClient,
                                              findWorker(workers, WORKER_URL));
 
   let toolbox = yield gDevTools.showToolbox(TargetFactory.forWorker(workerClient),
                                             "jsdebugger",
                                             Toolbox.HostType.WINDOW);
 
   is(toolbox._host.type, "window", "correct host");
-  ok(toolbox._host._window.document.title.contains(WORKER_URL),
+  ok(toolbox._host._window.document.title.includes(WORKER_URL),
      "worker URL in host title");
 
   let toolTabs = toolbox.doc.querySelectorAll(".devtools-tab");
   let activeTools = [...toolTabs].map(tab=>tab.getAttribute("toolid"));
 
   is(activeTools.join(","), "webconsole,jsdebugger,scratchpad,options",
     "Correct set of tools supported by worker");
 
--- a/devtools/client/responsive.html/test/browser/browser.ini
+++ b/devtools/client/responsive.html/test/browser/browser.ini
@@ -5,13 +5,12 @@ skip-if = (!e10s && debug) # Bug 1262416
 support-files =
   devices.json
   head.js
   !/devtools/client/commandline/test/helpers.js
   !/devtools/client/framework/test/shared-head.js
   !/devtools/client/framework/test/shared-redux-head.js
 
 [browser_device_width.js]
-skip-if = (e10s && debug) # Bug 1262432 - crashes at nsLayoutUtils::HasDisplayPort(content)
 [browser_exit_button.js]
 [browser_resize_cmd.js]
 [browser_screenshot_button.js]
 [browser_viewport_basics.js]
--- a/devtools/server/tests/unit/test_promises_actor_onnewpromise.js
+++ b/devtools/server/tests/unit/test_promises_actor_onnewpromise.js
@@ -11,17 +11,17 @@
 const { PromisesFront } = require("devtools/server/actors/promises");
 
 var events = require("sdk/event/core");
 
 add_task(function*() {
   let client = yield startTestDebuggerServer("promises-actor-test");
   let chromeActors = yield getChromeActors(client);
 
-  ok(Promise.toString().contains("native code"), "Expect native DOM Promise");
+  ok(Promise.toString().includes("native code"), "Expect native DOM Promise");
 
   // We have to attach the chrome TabActor before playing with the PromiseActor
   yield attachTab(client, chromeActors);
   yield testNewPromisesEvent(client, chromeActors,
     v => new Promise(resolve => resolve(v)));
 
   let response = yield listTabs(client);
   let targetTab = findTab(response.tabs, "promises-actor-test");
--- a/devtools/server/tests/unit/test_promises_actor_onpromisesettled.js
+++ b/devtools/server/tests/unit/test_promises_actor_onpromisesettled.js
@@ -13,17 +13,17 @@ Cu.import("resource://testing-common/Pro
 const { PromisesFront } = require("devtools/server/actors/promises");
 
 var events = require("sdk/event/core");
 
 add_task(function*() {
   let client = yield startTestDebuggerServer("promises-actor-test");
   let chromeActors = yield getChromeActors(client);
 
-  ok(Promise.toString().contains("native code"), "Expect native DOM Promise");
+  ok(Promise.toString().includes("native code"), "Expect native DOM Promise");
 
   // We have to attach the chrome TabActor before playing with the PromiseActor
   yield attachTab(client, chromeActors);
   yield testPromisesSettled(client, chromeActors,
     v => new Promise(resolve => resolve(v)),
     v => new Promise((resolve, reject) => reject(v)));
 
   let response = yield listTabs(client);
--- a/devtools/server/tests/unit/test_promises_object_creationtimestamp.js
+++ b/devtools/server/tests/unit/test_promises_object_creationtimestamp.js
@@ -10,17 +10,17 @@
 const { PromisesFront } = require("devtools/server/actors/promises");
 
 var events = require("sdk/event/core");
 
 add_task(function*() {
   let client = yield startTestDebuggerServer("promises-object-test");
   let chromeActors = yield getChromeActors(client);
 
-  ok(Promise.toString().contains("native code"), "Expect native DOM Promise.");
+  ok(Promise.toString().includes("native code"), "Expect native DOM Promise.");
 
   // We have to attach the chrome TabActor before playing with the PromiseActor
   yield attachTab(client, chromeActors);
   yield testPromiseCreationTimestamp(client, chromeActors, v => {
     return new Promise(resolve => resolve(v));
   });
 
   let response = yield listTabs(client);
--- a/devtools/server/tests/unit/test_promises_object_timetosettle-01.js
+++ b/devtools/server/tests/unit/test_promises_object_timetosettle-01.js
@@ -11,17 +11,17 @@
 const { PromisesFront } = require("devtools/server/actors/promises");
 
 var events = require("sdk/event/core");
 
 add_task(function*() {
   let client = yield startTestDebuggerServer("test-promises-timetosettle");
   let chromeActors = yield getChromeActors(client);
 
-  ok(Promise.toString().contains("native code"), "Expect native DOM Promise.");
+  ok(Promise.toString().includes("native code"), "Expect native DOM Promise.");
 
   // We have to attach the chrome TabActor before playing with the PromiseActor
   yield attachTab(client, chromeActors);
   yield testGetTimeToSettle(client, chromeActors, () => {
     let p = new Promise(() => {});
     p.name = "p";
     let q = p.then();
     q.name = "q";
--- a/devtools/server/tests/unit/test_promises_object_timetosettle-02.js
+++ b/devtools/server/tests/unit/test_promises_object_timetosettle-02.js
@@ -12,17 +12,17 @@ const { setTimeout } = require("sdk/time
 
 var events = require("sdk/event/core");
 
 add_task(function*() {
   let client = yield startTestDebuggerServer("test-promises-timetosettle");
   let chromeActors = yield getChromeActors(client);
   yield attachTab(client, chromeActors);
 
-  ok(Promise.toString().contains("native code"), "Expect native DOM Promise.");
+  ok(Promise.toString().includes("native code"), "Expect native DOM Promise.");
 
   // We have to attach the chrome TabActor before playing with the PromiseActor
   yield attachTab(client, chromeActors);
   yield testGetTimeToSettle(client, chromeActors,
     v => new Promise(resolve => setTimeout(() => resolve(v), 100)));
 
   let response = yield listTabs(client);
   let targetTab = findTab(response.tabs, "test-promises-timetosettle");
--- a/devtools/shared/heapsnapshot/census-tree-node.js
+++ b/devtools/shared/heapsnapshot/census-tree-node.js
@@ -659,22 +659,22 @@ function filter(tree, predicate) {
  */
 function makeFilterPredicate(filterString) {
   return function (node) {
     if (!node.name) {
       return false;
     }
 
     if (isSavedFrame(node.name)) {
-      return node.name.source.contains(filterString)
-        || (node.name.functionDisplayName || "").contains(filterString)
-        || (node.name.asyncCause || "").contains(filterString);
+      return node.name.source.includes(filterString)
+        || (node.name.functionDisplayName || "").includes(filterString)
+        || (node.name.asyncCause || "").includes(filterString);
     }
 
-    return String(node.name).contains(filterString);
+    return String(node.name).includes(filterString);
   };
 }
 
 /**
  * Takes a report from a census (`dbg.memory.takeCensus()`) and the breakdown
  * used to generate the census and returns a structure used to render
  * a tree to display the data.
  *
--- a/devtools/shared/heapsnapshot/tests/gtest/DevTools.h
+++ b/devtools/shared/heapsnapshot/tests/gtest/DevTools.h
@@ -93,23 +93,26 @@ struct DevTools : public ::testing::Test
             message);
   }
 
   JSContext* createContext() {
     return JS_NewContext(rt, 8192);
   }
 
   static const JSClass* getGlobalClass() {
-    static const JSClass globalClass = {
-      "global", JSCLASS_GLOBAL_FLAGS,
+    static const JSClassOps globalClassOps = {
       nullptr, nullptr, nullptr, nullptr,
       nullptr, nullptr, nullptr, nullptr,
       nullptr, nullptr, nullptr,
       JS_GlobalObjectTraceHook
     };
+    static const JSClass globalClass = {
+      "global", JSCLASS_GLOBAL_FLAGS,
+      &globalClassOps
+    };
     return &globalClass;
   }
 
   JSObject* createGlobal()
   {
     /* Create the global object. */
     JS::RootedObject newGlobal(cx);
     JS::CompartmentOptions options;
--- a/devtools/shared/tests/unit/test_fetch-http.js
+++ b/devtools/shared/tests/unit/test_fetch-http.js
@@ -28,17 +28,17 @@ function cacheRequestHandler(request, re
 }
 
 do_register_cleanup(() => {
   return new Promise(resolve => server.stop(resolve));
 });
 
 add_task(function* test_normal() {
   yield DevToolsUtils.fetch(NORMAL_URL).then(({content}) => {
-    ok(content.contains("The content looks correct."),
+    ok(content.includes("The content looks correct."),
       "The content looks correct.");
   });
 });
 
 add_task(function* test_caching() {
   let initialContent = null;
 
   do_print("Performing the first request.");
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -10943,18 +10943,23 @@ nsDocShell::DoURILoad(nsIURI* aURI,
       props->SetPropertyAsBool(NS_LITERAL_STRING("docshell.newWindowTarget"),
                                true);
     }
   }
 
   nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(channel));
   if (timedChannel) {
     timedChannel->SetTimingEnabled(true);
-    if (IsFrame()) {
-      timedChannel->SetInitiatorType(NS_LITERAL_STRING("subdocument"));
+
+    nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
+    if (IsFrame() && win) {
+      nsCOMPtr<Element> frameElement = win->GetFrameElementInternal();
+      if (frameElement) {
+        timedChannel->SetInitiatorType(frameElement->LocalName());
+      }
     }
   }
 
   rv = DoChannelLoad(channel, uriLoader, aBypassClassifier);
 
   //
   // If the channel load failed, we failed and nsIWebProgress just ain't
   // gonna happen.
--- a/dom/base/Console.cpp
+++ b/dom/base/Console.cpp
@@ -404,17 +404,17 @@ private:
         : MainThreadWorkerControlRunnable(aWorkerPrivate)
         , mRunnable(aRunnable)
       {
         MOZ_ASSERT(aRunnable);
       }
 
       // If something goes wrong, we still need to release the ConsoleCallData
       // object. For this reason we have a custom Cancel method.
-      NS_IMETHOD
+      nsresult
       Cancel() override
       {
         mRunnable->ReleaseData();
         mRunnable->mConsole = nullptr;
         return NS_OK;
       }
 
       virtual bool
--- a/dom/base/ImageEncoder.cpp
+++ b/dom/base/ImageEncoder.cpp
@@ -67,17 +67,17 @@ private:
 // main thread.
 already_AddRefed<DataSourceSurface>
 GetBRGADataSourceSurfaceSync(already_AddRefed<layers::Image> aImage)
 {
   RefPtr<SurfaceHelper> helper = new SurfaceHelper(Move(aImage));
   return helper->GetDataSurfaceSafe();
 }
 
-class EncodingCompleteEvent : public nsCancelableRunnable
+class EncodingCompleteEvent : public CancelableRunnable
 {
   virtual ~EncodingCompleteEvent() {}
 
 public:
   explicit EncodingCompleteEvent(EncodeCompleteCallback* aEncodeCompleteCallback)
     : mImgSize(0)
     , mType()
     , mImgData(nullptr)
--- a/dom/base/SameProcessMessageQueue.h
+++ b/dom/base/SameProcessMessageQueue.h
@@ -9,18 +9,16 @@
 
 #include "nsIRunnable.h"
 #include "mozilla/RefPtr.h"
 #include "nsTArray.h"
 
 namespace mozilla {
 namespace dom {
 
-class CancelableRunnable;
-
 class SameProcessMessageQueue
 {
 public:
   SameProcessMessageQueue();
   virtual ~SameProcessMessageQueue();
 
   class Runnable : public nsIRunnable
   {
@@ -40,18 +38,16 @@ public:
   };
 
   void Push(Runnable* aRunnable);
   void Flush();
 
   static SameProcessMessageQueue* Get();
 
 private:
-  friend class CancelableRunnable;
-
   nsTArray<RefPtr<Runnable>> mQueue;
   static SameProcessMessageQueue* sSingleton;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_SameProcessMessageQueue_h
--- a/dom/base/WebSocket.cpp
+++ b/dom/base/WebSocket.cpp
@@ -248,17 +248,17 @@ private:
 NS_IMPL_ISUPPORTS(WebSocketImpl,
                   nsIInterfaceRequestor,
                   nsIWebSocketListener,
                   nsIObserver,
                   nsISupportsWeakReference,
                   nsIRequest,
                   nsIEventTarget)
 
-class CallDispatchConnectionCloseEvents final : public nsCancelableRunnable
+class CallDispatchConnectionCloseEvents final : public CancelableRunnable
 {
 public:
   explicit CallDispatchConnectionCloseEvents(WebSocketImpl* aWebSocketImpl)
     : mWebSocketImpl(aWebSocketImpl)
   {
     aWebSocketImpl->AssertIsOnTargetThread();
   }
 
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -7450,27 +7450,42 @@ nsContentUtils::TransferableToIPCTransfe
           } else {
             if (aInSyncMessage) {
               // Can't do anything.
               continue;
             }
             blobImpl = do_QueryInterface(data);
           }
           if (blobImpl) {
+            IPCDataTransferData data;
+
+            // If we failed to create the blob actor, then this blob probably
+            // can't get the file size for the underlying file, ignore it for
+            // now. TODO pass this through anyway.
+            if (aChild) {
+              auto* child = mozilla::dom::BlobChild::GetOrCreate(aChild,
+                              static_cast<BlobImpl*>(blobImpl.get()));
+              if (!child) {
+                continue;
+              }
+
+              data = child;
+            } else if (aParent) {
+              auto* parent = mozilla::dom::BlobParent::GetOrCreate(aParent,
+                               static_cast<BlobImpl*>(blobImpl.get()));
+              if (!parent) {
+                continue;
+              }
+
+              data = parent;
+            }
+
             IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
             item->flavor() = nsCString(flavorStr);
-            if (aChild) {
-              item->data() =
-                mozilla::dom::BlobChild::GetOrCreate(aChild,
-                  static_cast<BlobImpl*>(blobImpl.get()));
-            } else if (aParent) {
-              item->data() =
-                mozilla::dom::BlobParent::GetOrCreate(aParent,
-                  static_cast<BlobImpl*>(blobImpl.get()));
-            }
+            item->data() = data;
           } else {
             // This is a hack to support kFilePromiseMime.
             // On Windows there just needs to be an entry for it, 
             // and for OSX we need to create
             // nsContentAreaDragDropDataProvider as nsIFlavorDataProvider.
             if (flavorStr.EqualsLiteral(kFilePromiseMime)) {
               IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
               item->flavor() = nsCString(flavorStr);
--- a/dom/base/nsPerformance.cpp
+++ b/dom/base/nsPerformance.cpp
@@ -1131,33 +1131,33 @@ PerformanceBase::NotifyObservers()
 }
 
 void
 PerformanceBase::CancelNotificationObservers()
 {
   mPendingNotificationObserversTask = false;
 }
 
-class NotifyObserversTask final : public nsCancelableRunnable
+class NotifyObserversTask final : public CancelableRunnable
 {
 public:
   explicit NotifyObserversTask(PerformanceBase* aPerformance)
     : mPerformance(aPerformance)
   {
     MOZ_ASSERT(mPerformance);
   }
 
   NS_IMETHOD Run() override
   {
     MOZ_ASSERT(mPerformance);
     mPerformance->NotifyObservers();
     return NS_OK;
   }
 
-  NS_IMETHOD Cancel() override
+  nsresult Cancel() override
   {
     mPerformance->CancelNotificationObservers();
     mPerformance = nullptr;
     return NS_OK;
   }
 
 private:
   ~NotifyObserversTask()
new file mode 100644
--- /dev/null
+++ b/dom/base/test/file_bug769117.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML>
+<html>
+  <!--
+       https://bugzilla.mozilla.org/show_bug.cgi?id=769117
+     -->
+  <head>
+  </head>
+  <body>
+    <embed id="testembed"
+           src="https://mochitest.youtube.com/v/Xm5i5kbIXzc"
+           type="application/x-shockwave-flash"
+           allowscriptaccess="always"></embed>
+    <object id="testobject"
+            data="https://mochitest.youtube.com/v/Xm5i5kbIXzc"></embed>
+  </body>
+</html>
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -142,16 +142,17 @@ support-files =
   file_bug704320_preload_reuse.html
   file_bug704320_preload_noreuse.html
   file_bug704320_redirect.html
   file_bug707142_baseline.json
   file_bug707142_bom.json
   file_bug707142_utf-16.json
   file_bug708620-2.html
   file_bug708620.html
+  file_bug769117.html
   file_bug782342.txt
   file_bug787778.sjs
   file_bug804395.jar
   file_bug869432.eventsource
   file_bug869432.eventsource^headers^
   file_bug902350.html
   file_bug902350_frame.html
   file_bug907892.html
--- a/dom/base/test/test_bug769117.html
+++ b/dom/base/test/test_bug769117.html
@@ -8,44 +8,48 @@
     <title>Test for Bug 769117</title>
     <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
     <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
     <script type="application/javascript">
      SimpleTest.waitForExplicitFinish();
      /** Test for Bug 769117 **/
      "use strict";
      function onLoad () {
-       let youtube_url = "https://mochitest.youtube.com/v/Xm5i5kbIXzc";
-       let youtube_changed_url = "https://mochitest.youtube.com/embed/Xm5i5kbIXzc";
+       SpecialPowers.pushPrefEnv({"set": [["plugins.rewrite_youtube_embeds", true]]}, function() {
+
+         let youtube_url = "https://mochitest.youtube.com/v/Xm5i5kbIXzc";
+         let youtube_changed_url = "https://mochitest.youtube.com/embed/Xm5i5kbIXzc";
+         let static_iframe = document.getElementById("staticiframe");
 
-       function testEmbed(embed) {
-         ok (embed, "Embed node exists");
-         embed = SpecialPowers.wrap(embed);
-         is (embed.srcURI.spec, youtube_changed_url, "Should have src uri of " + youtube_changed_url);
-       }
-       info("Running static embed youtube rewrite test");
-       testEmbed(document.getElementById("testembed"));
-       testEmbed(document.getElementById("testobject"));
+         function testEmbed(embed) {
+           ok (embed, "Embed node exists");
+           embed = SpecialPowers.wrap(embed);
+           is (embed.srcURI.spec, youtube_changed_url, "Should have src uri of " + youtube_changed_url);
+         }
 
-       info("Running dynamic embed youtube rewrite test");
-       let embed_dynamic = document.createElement("embed");
-       embed_dynamic.src = "https://mochitest.youtube.com/v/Xm5i5kbIXzc";
-       embed_dynamic.type = "application/x-shockwave-flash";
+         function testStatic() {
+           info("Running static embed youtube rewrite test");
+           iframe_doc = static_iframe.contentWindow.document;
+           testEmbed(iframe_doc.getElementById("testembed"));
+           testEmbed(iframe_doc.getElementById("testobject"));
+           SimpleTest.executeSoon(() => {
+             testEmbed(embed_dynamic);
+             SimpleTest.finish();
+           });
+         }
 
-       document.body.appendChild(embed_dynamic);
+         info("Running dynamic embed youtube rewrite test");
+         let embed_dynamic = document.createElement("embed");
+         embed_dynamic.src = "https://mochitest.youtube.com/v/Xm5i5kbIXzc";
+         embed_dynamic.type = "application/x-shockwave-flash";
+         document.body.appendChild(embed_dynamic);
 
-       SimpleTest.executeSoon(() =>
-         {
-           testEmbed(embed_dynamic);
-           SimpleTest.finish();
-         });
+         static_iframe.onload = testStatic;
+         static_iframe.src = "file_bug769117.html"
+
+       });
      }
     </script>
   </head>
   <body onload="onLoad()">
-    <embed id="testembed"
-           src="https://mochitest.youtube.com/v/Xm5i5kbIXzc"
-           type="application/x-shockwave-flash"
-           allowscriptaccess="always"></embed>
-    <object id="testobject"
-            data="https://mochitest.youtube.com/v/Xm5i5kbIXzc"></embed>
+    <iframe id="staticiframe"></iframe>
   </body>
 </html>
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -1801,16 +1801,31 @@ NativePropertyHooks sEmptyNativeProperty
     nullptr,
     nullptr
   },
   prototypes::id::_ID_Count,
   constructors::id::_ID_Count,
   nullptr
 };
 
+const js::ClassOps sBoringInterfaceObjectClassClassOps = {
+    nullptr,               /* addProperty */
+    nullptr,               /* delProperty */
+    nullptr,               /* getProperty */
+    nullptr,               /* setProperty */
+    nullptr,               /* enumerate */
+    nullptr,               /* resolve */
+    nullptr,               /* mayResolve */
+    nullptr,               /* finalize */
+    ThrowingConstructor,   /* call */
+    InterfaceHasInstance,  /* hasInstance */
+    ThrowingConstructor,   /* construct */
+    nullptr,               /* trace */
+};
+
 const js::ObjectOps sInterfaceObjectClassObjectOps = {
   nullptr, /* lookupProperty */
   nullptr, /* defineProperty */
   nullptr, /* hasProperty */
   nullptr, /* getProperty */
   nullptr, /* setProperty */
   nullptr, /* getOwnPropertyDescriptor */
   nullptr, /* deleteProperty */
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -2503,16 +2503,18 @@ XrayGetNativeProto(JSContext* cx, JS::Ha
     }
   }
 
   return JS_WrapObject(cx, protop);
 }
 
 extern NativePropertyHooks sEmptyNativePropertyHooks;
 
+extern const js::ClassOps sBoringInterfaceObjectClassClassOps;
+
 extern const js::ObjectOps sInterfaceObjectClassObjectOps;
 
 // We use one constructor JSNative to represent all DOM interface objects (so
 // we can easily detect when we need to wrap them in an Xray wrapper). We store
 // the real JSNative in the mNative member of a JSNativeHolder in the
 // CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT slot of the JSFunction object for a
 // specific interface object. We also store the NativeProperties in the
 // JSNativeHolder.
@@ -2535,17 +2537,17 @@ UseDOMXray(JSObject* obj)
          IsDOMIfaceAndProtoClass(clasp);
 }
 
 #ifdef DEBUG
 inline bool
 HasConstructor(JSObject* obj)
 {
   return JS_IsNativeFunction(obj, Constructor) ||
-         js::GetObjectClass(obj)->construct;
+         js::GetObjectClass(obj)->getConstruct();
 }
  #endif
 
 // Helpers for creating a const version of a type.
 template<typename T>
 const T& Constify(T& arg)
 {
   return arg;
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -435,36 +435,40 @@ class CGDOMJSClass(CGThing):
             enumerateHook = "mozilla::dom::EnumerateGlobal"
         else:
             resolveHook = "nullptr"
             mayResolveHook = "nullptr"
             enumerateHook = "nullptr"
 
         return fill(
             """
+            static const js::ClassOps sClassOps = {
+              ${addProperty}, /* addProperty */
+              nullptr,               /* delProperty */
+              nullptr,               /* getProperty */
+              nullptr,               /* setProperty */
+              ${enumerate}, /* enumerate */
+              ${resolve}, /* resolve */
+              ${mayResolve}, /* mayResolve */
+              ${finalize}, /* finalize */
+              ${call}, /* call */
+              nullptr,               /* hasInstance */
+              nullptr,               /* construct */
+              ${trace}, /* trace */
+            };
+
             static const js::ClassExtension sClassExtension = {
               nullptr, /* weakmapKeyDelegateOp */
               ${objectMoved} /* objectMovedOp */
             };
 
             static const DOMJSClass sClass = {
               { "${name}",
                 ${flags},
-                ${addProperty}, /* addProperty */
-                nullptr,               /* delProperty */
-                nullptr,               /* getProperty */
-                nullptr,               /* setProperty */
-                ${enumerate}, /* enumerate */
-                ${resolve}, /* resolve */
-                ${mayResolve}, /* mayResolve */
-                ${finalize}, /* finalize */
-                ${call}, /* call */
-                nullptr,               /* hasInstance */
-                nullptr,               /* construct */
-                ${trace}, /* trace */
+                &sClassOps,
                 JS_NULL_CLASS_SPEC,
                 &sClassExtension,
                 JS_NULL_OBJECT_OPS
               },
               $*{descriptor}
             };
             static_assert(${instanceReservedSlots} == DOM_INSTANCE_RESERVED_SLOTS,
                           "Must have the right minimal number of reserved slots.");
@@ -589,28 +593,17 @@ class CGPrototypeJSClass(CGThing):
         (protoGetter, _) = InterfacePrototypeObjectProtoGetter(self.descriptor)
         type = "eGlobalInterfacePrototype" if self.descriptor.isGlobal() else "eInterfacePrototype"
         return fill(
             """
             static const DOMIfaceAndProtoJSClass sPrototypeClass = {
               {
                 "${name}Prototype",
                 JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(${slotCount}),
-                nullptr,               /* addProperty */
-                nullptr,               /* delProperty */
-                nullptr,               /* getProperty */
-                nullptr,               /* setProperty */
-                nullptr,               /* enumerate */
-                nullptr,               /* resolve */
-                nullptr,               /* mayResolve */
-                nullptr,               /* finalize */
-                nullptr,               /* call */
-                nullptr,               /* hasInstance */
-                nullptr,               /* construct */
-                nullptr,               /* trace */
+                JS_NULL_CLASS_OPS,
                 JS_NULL_CLASS_SPEC,
                 JS_NULL_CLASS_EXT,
                 JS_NULL_OBJECT_OPS
               },
               ${type},
               ${prototypeID},
               ${depth},
               ${hooks},
@@ -679,55 +672,71 @@ class CGInterfaceObjectJSClass(CGThing):
             hasinstance = "nullptr"
         prototypeID, depth = PrototypeIDAndDepth(self.descriptor)
         slotCount = "DOM_INTERFACE_SLOTS_BASE"
         if len(self.descriptor.interface.namedConstructors) > 0:
             slotCount += (" + %i /* slots for the named constructors */" %
                           len(self.descriptor.interface.namedConstructors))
         (protoGetter, _) = InterfaceObjectProtoGetter(self.descriptor)
 
-        return fill(
+        if ctorname == "ThrowingConstructor" and hasinstance == "InterfaceHasInstance":
+            ret = ""
+            classOpsPtr = "&sBoringInterfaceObjectClassClassOps"
+        else:
+            ret = fill(
+                """
+                static const js::ClassOps sInterfaceObjectClassOps = {
+                    nullptr,               /* addProperty */
+                    nullptr,               /* delProperty */
+                    nullptr,               /* getProperty */
+                    nullptr,               /* setProperty */
+                    nullptr,               /* enumerate */
+                    nullptr,               /* resolve */
+                    nullptr,               /* mayResolve */
+                    nullptr,               /* finalize */
+                    ${ctorname}, /* call */
+                    ${hasInstance}, /* hasInstance */
+                    ${ctorname}, /* construct */
+                    nullptr,               /* trace */
+                };
+
+                """,
+                ctorname=ctorname,
+                hasInstance=hasinstance)
+            classOpsPtr = "&sInterfaceObjectClassOps"
+
+        ret = ret + fill(
             """
             static const DOMIfaceAndProtoJSClass sInterfaceObjectClass = {
               {
                 "Function",
                 JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(${slotCount}),
-                nullptr,               /* addProperty */
-                nullptr,               /* delProperty */
-                nullptr,               /* getProperty */
-                nullptr,               /* setProperty */
-                nullptr,               /* enumerate */
-                nullptr,               /* resolve */
-                nullptr,               /* mayResolve */
-                nullptr,               /* finalize */
-                ${ctorname}, /* call */
-                ${hasInstance}, /* hasInstance */
-                ${ctorname}, /* construct */
-                nullptr,               /* trace */
+                ${classOpsPtr},
                 JS_NULL_CLASS_SPEC,
                 JS_NULL_CLASS_EXT,
                 &sInterfaceObjectClassObjectOps
               },
               eInterface,
               ${prototypeID},
               ${depth},
               ${hooks},
               "function ${name}() {\\n    [native code]\\n}",
               ${protoGetter}
             };
             """,
             slotCount=slotCount,
             ctorname=ctorname,
             hasInstance=hasinstance,
+            classOpsPtr=classOpsPtr,
             hooks=NativePropertyHooks(self.descriptor),
             name=self.descriptor.interface.identifier.name,
             prototypeID=prototypeID,
             depth=depth,
             protoGetter=protoGetter)
-
+        return ret
 
 class CGList(CGThing):
     """
     Generate code for a list of GCThings.  Just concatenates them together, with
     an optional joiner string.  "\n" is a common joiner.
     """
     def __init__(self, children, joiner=""):
         CGThing.__init__(self)
--- a/dom/bindings/SimpleGlobalObject.cpp
+++ b/dom/bindings/SimpleGlobalObject.cpp
@@ -65,36 +65,40 @@ SimpleGlobal_finalize(js::FreeOp *fop, J
 static void
 SimpleGlobal_moved(JSObject *obj, const JSObject *old)
 {
   SimpleGlobalObject* globalObject =
     static_cast<SimpleGlobalObject*>(JS_GetPrivate(obj));
   globalObject->UpdateWrapper(obj, old);
 }
 
-static const js::ClassExtension SimpleGlobalClassExtension = {
-  nullptr,
-  SimpleGlobal_moved
-};
-
-const js::Class SimpleGlobalClass = {
-    "",
-    JSCLASS_GLOBAL_FLAGS | JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS,
+static const js::ClassOps SimpleGlobalClassOps = {
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     SimpleGlobal_enumerate,
     SimpleGlobal_resolve,
     nullptr,
     SimpleGlobal_finalize,
     nullptr,
     nullptr,
     nullptr,
     JS_GlobalObjectTraceHook,
+};
+
+static const js::ClassExtension SimpleGlobalClassExtension = {
+  nullptr,
+  SimpleGlobal_moved
+};
+
+const js::Class SimpleGlobalClass = {
+    "",
+    JSCLASS_GLOBAL_FLAGS | JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS,
+    &SimpleGlobalClassOps,
     JS_NULL_CLASS_SPEC,
     &SimpleGlobalClassExtension,
     JS_NULL_OBJECT_OPS
 };
 
 // static
 JSObject*
 SimpleGlobalObject::Create(GlobalType globalType, JS::Handle<JS::Value> proto)
--- a/dom/broadcastchannel/BroadcastChannel.cpp
+++ b/dom/broadcastchannel/BroadcastChannel.cpp
@@ -137,17 +137,18 @@ public:
 private:
   WorkerPrivate* mWorkerPrivate;
   nsACString& mOrigin;
   PrincipalInfo& mPrincipalInfo;
   bool& mPrivateBrowsing;
   ErrorResult& mRv;
 };
 
-class BCPostMessageRunnable final : public nsICancelableRunnable
+class BCPostMessageRunnable final : public nsIRunnable,
+                                    public nsICancelableRunnable
 {
 public:
   NS_DECL_ISUPPORTS
 
   BCPostMessageRunnable(BroadcastChannelChild* aActor,
                         BroadcastChannelMessage* aData)
     : mActor(aActor)
     , mData(aData)
@@ -185,63 +186,65 @@ public:
         message.blobsChild().AppendElement(blobChild);
       }
     }
 
     mActor->SendPostMessage(message);
     return NS_OK;
   }
 
-  NS_IMETHODIMP Cancel() override
+  nsresult Cancel() override
   {
     mActor = nullptr;
     return NS_OK;
   }
 
 private:
   ~BCPostMessageRunnable() {}
 
   RefPtr<BroadcastChannelChild> mActor;
   RefPtr<BroadcastChannelMessage> mData;
 };
 
 NS_IMPL_ISUPPORTS(BCPostMessageRunnable, nsICancelableRunnable, nsIRunnable)
 
-class CloseRunnable final : public nsICancelableRunnable
+class CloseRunnable final : public nsIRunnable,
+                            public nsICancelableRunnable
 {
 public:
   NS_DECL_ISUPPORTS
 
   explicit CloseRunnable(BroadcastChannel* aBC)
     : mBC(aBC)
   {
     MOZ_ASSERT(mBC);
   }
 
   NS_IMETHODIMP Run() override
   {
     mBC->Shutdown();
     return NS_OK;
   }
 
-  NS_IMETHODIMP Cancel() override
+  nsresult Cancel() override
   {
     mBC = nullptr;
     return NS_OK;
   }
 
 private:
   ~CloseRunnable() {}
 
   RefPtr<BroadcastChannel> mBC;
 };
 
 NS_IMPL_ISUPPORTS(CloseRunnable, nsICancelableRunnable, nsIRunnable)
 
-class TeardownRunnable final : public nsICancelableRunnable
+class TeardownRunnable final : public nsIRunnable,
+                               public nsICancelableRunnable
 {
 public:
   NS_DECL_ISUPPORTS
 
   explicit TeardownRunnable(BroadcastChannelChild* aActor)
     : mActor(aActor)
   {
     MOZ_ASSERT(mActor);
@@ -251,17 +254,17 @@ public:
   {
     MOZ_ASSERT(mActor);
     if (!mActor->IsActorDestroyed()) {
       mActor->SendClose();
     }
     return NS_OK;
   }
 
-  NS_IMETHODIMP Cancel() override
+  nsresult Cancel() override
   {
     mActor = nullptr;
     return NS_OK;
   }
 
 private:
   ~TeardownRunnable() {}
 
--- a/dom/broadcastchannel/moz.build
+++ b/dom/broadcastchannel/moz.build
@@ -19,16 +19,16 @@ IPDL_SOURCES += [
     'PBroadcastChannel.ipdl',
 ]
 
 LOCAL_INCLUDES += [
     '../workers',
 ]
 
 MOCHITEST_MANIFESTS += ['tests/mochitest.ini']
-MOCHITEST_CHROME_MANIFESTS += ['tests/chrome.ini']
+BROWSER_CHROME_MANIFESTS += ['tests/browser.ini']
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
 if CONFIG['GNU_CXX']:
     CXXFLAGS += ['-Wshadow']
new file mode 100644
--- /dev/null
+++ b/dom/broadcastchannel/tests/browser.ini
@@ -0,0 +1,6 @@
+[DEFAULT]
+skip-if = buildapp == 'b2g' || os == 'android'
+support-files =
+  blank.html
+
+[browser_private_browsing.js]
new file mode 100644
--- /dev/null
+++ b/dom/broadcastchannel/tests/browser_private_browsing.js
@@ -0,0 +1,74 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const URL = "http://mochi.test:8888/browser/dom/broadcastchannel/tests/blank.html";
+
+add_task(function*() {
+  var win1 = OpenBrowserWindow({private: true});
+  var win1Promise = new win1.Promise(resolve => {
+    win1.addEventListener("load", function onLoad() {
+      win1.removeEventListener("load", onLoad, false);
+      resolve();
+    });
+  });
+  yield win1Promise;
+
+  var win2 = OpenBrowserWindow({private: false});
+  var win2Promise = new win2.Promise(resolve => {
+    win2.addEventListener("load", function onLoad() {
+      win2.removeEventListener("load", onLoad, false);
+      resolve();
+    });
+  });
+  yield win2Promise;
+
+  var tab1 = win1.gBrowser.addTab(URL);
+  yield BrowserTestUtils.browserLoaded(win1.gBrowser.getBrowserForTab(tab1));
+  var browser1 = gBrowser.getBrowserForTab(tab1);
+
+  var tab2 = win2.gBrowser.addTab(URL);
+  yield BrowserTestUtils.browserLoaded(win2.gBrowser.getBrowserForTab(tab2));
+  var browser2 = gBrowser.getBrowserForTab(tab2);
+
+  var p1 = ContentTask.spawn(browser1, null, function(opts) {
+    return new content.window.Promise(resolve => {
+      content.window.bc = new content.window.BroadcastChannel('foobar');
+      content.window.bc.onmessage = function(e) { resolve(e.data); }
+    });
+  });
+
+  var p2 = ContentTask.spawn(browser2, null, function(opts) {
+    return new content.window.Promise(resolve => {
+      content.window.bc = new content.window.BroadcastChannel('foobar');
+      content.window.bc.onmessage = function(e) { resolve(e.data); }
+    });
+  });
+
+  yield ContentTask.spawn(browser1, null, function(opts) {
+    return new content.window.Promise(resolve => {
+      var bc = new content.window.BroadcastChannel('foobar');
+      bc.postMessage('hello world from private browsing');
+      resolve();
+    });
+  });
+
+  yield ContentTask.spawn(browser2, null, function(opts) {
+    return new content.window.Promise(resolve => {
+      var bc = new content.window.BroadcastChannel('foobar');
+      bc.postMessage('hello world from non private browsing');
+      resolve();
+    });
+  });
+
+  var what1 = yield p1;
+  ok(what1, 'hello world from private browsing', 'No messages received from the other window.');
+
+  var what2 = yield p2;
+  ok(what1, 'hello world from non private browsing', 'No messages received from the other window.');
+
+  yield BrowserTestUtils.removeTab(tab1);
+  yield BrowserTestUtils.closeWindow(win1);
+
+  yield BrowserTestUtils.removeTab(tab2);
+  yield BrowserTestUtils.closeWindow(win2);
+});
deleted file mode 100644
--- a/dom/broadcastchannel/tests/chrome.ini
+++ /dev/null
@@ -1,6 +0,0 @@
-[DEFAULT]
-skip-if = e10s || buildapp == 'b2g' || os == 'android'
-support-files =
-  blank.html
-
-[test_broadcastchannel_private_browsing.html]
deleted file mode 100644
--- a/dom/broadcastchannel/tests/test_broadcastchannel_private_browsing.html
+++ /dev/null
@@ -1,118 +0,0 @@
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head>
-  <title>Test for BroadcastChannel - Private Browsing</title>
-  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
-</head>
-<body>
-
-<script type="application/javascript">
-
-const Ci = Components.interfaces;
-var mainWindow;
-
-var prefBranch = Components.classes["@mozilla.org/preferences-service;1"]
-                           .getService(Components.interfaces.nsIPrefBranch);
-prefBranch.setIntPref("browser.startup.page", 0);
-prefBranch.setCharPref("browser.startup.homepage_override.mstone", "ignore");
-
-var contentPage = "http://mochi.test:8888/chrome/dom/broadcastchannel/tests/blank.html";
-
-function testOnWindow(aIsPrivate, aCallback) {
-  var win = mainWindow.OpenBrowserWindow({private: aIsPrivate});
-  win.addEventListener("load", function onLoad() {
-    win.removeEventListener("load", onLoad, false);
-    win.addEventListener("DOMContentLoaded", function onInnerLoad() {
-      if (win.content.location.href != contentPage) {
-        win.gBrowser.loadURI(contentPage);
-        return;
-      }
-
-      win.removeEventListener("DOMContentLoaded", onInnerLoad, true);
-      SimpleTest.executeSoon(function() { aCallback(win); });
-    }, true);
-
-    if (!aIsPrivate) {
-      win.gBrowser.loadURI(contentPage);
-    }
-  }, true);
-}
-
-function setupWindow() {
-  mainWindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                     .getInterface(Ci.nsIWebNavigation)
-                     .QueryInterface(Ci.nsIDocShellTreeItem)
-                     .rootTreeItem
-                     .QueryInterface(Ci.nsIInterfaceRequestor)
-                     .getInterface(Ci.nsIDOMWindow);
-  runTest();
-}
-
-var gCounter = 0;
-
-function check(msg, private) {
-  is(msg, private ? "private" : "public", "Correct context!");
-  gCounter++;
-
-  if (gCounter > 1) {
-    runTest();
-  }
-}
-
-var wN;
-var wP;
-
-function doTests() {
-  testOnWindow(false, function(aWin) {
-    wN = aWin;
-
-    testOnWindow(true, function(aWin) {
-      wP = aWin;
-
-      var bcP = new wP.content.BroadcastChannel('foobar');
-      bcP.onmessage = function(e) { ok(false, "This should not be called!"); }
-
-      var bc = new wP.content.BroadcastChannel('foobar');
-      bc.onmessage = function(e) { check(e.data, true); }
-
-      var bcN = new wN.content.BroadcastChannel('foobar');
-      bcN.onmessage = function(e) { ok(false, "This should not be called!"); }
-
-      var bc = new wN.content.BroadcastChannel('foobar');
-      bc.onmessage = function(e) { check(e.data, false); }
-
-      bcP.postMessage('private');
-      bcN.postMessage('public');
-    });
-  });
-}
-
-var steps = [
-  setupWindow,
-  doTests
-];
-
-function runTest() {
-  if (!steps.length) {
-    wN.close();
-    wP.close();
-
-    prefBranch.clearUserPref("browser.startup.page")
-    prefBranch.clearUserPref("browser.startup.homepage_override.mstone");
-
-    SimpleTest.finish();
-    return;
-  }
-
-  var step = steps.shift();
-  step();
-}
-
-SimpleTest.waitForExplicitFinish();
-runTest();
-
-</script>
-</body>
-</html>
-
-
--- a/dom/cache/CachePushStreamChild.cpp
+++ b/dom/cache/CachePushStreamChild.cpp
@@ -12,17 +12,17 @@
 #include "nsIThread.h"
 #include "nsStreamUtils.h"
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
 class CachePushStreamChild::Callback final : public nsIInputStreamCallback
-                                           , public nsICancelableRunnable
+                                           , public CancelableRunnable
 {
 public:
   explicit Callback(CachePushStreamChild* aActor)
     : mActor(aActor)
     , mOwningThread(NS_GetCurrentThread())
   {
     MOZ_ASSERT(mActor);
   }
@@ -51,17 +51,17 @@ public:
   {
     MOZ_ASSERT(mOwningThread == NS_GetCurrentThread());
     if (mActor) {
       mActor->OnStreamReady(this);
     }
     return NS_OK;
   }
 
-  NS_IMETHOD
+  nsresult
   Cancel() override
   {
     // Cancel() gets called when the Worker thread is being shutdown.  We have
     // nothing to do here because CachePushStreamChild handles this case via
     // the Feature.
     return NS_OK;
   }
 
@@ -80,22 +80,22 @@ private:
 
     // ClearActor() should be called before the Callback is destroyed
     MOZ_ASSERT(!mActor);
   }
 
   CachePushStreamChild* mActor;
   nsCOMPtr<nsIThread> mOwningThread;
 
-  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_ISUPPORTS_INHERITED
 };
 
-NS_IMPL_ISUPPORTS(CachePushStreamChild::Callback, nsIInputStreamCallback,
-                                                  nsIRunnable,
-                                                  nsICancelableRunnable);
+NS_IMPL_ISUPPORTS_INHERITED(CachePushStreamChild::Callback,
+                            CancelableRunnable,
+                            nsIInputStreamCallback);
 
 CachePushStreamChild::CachePushStreamChild(Feature* aFeature,
                                            nsISupports* aParent,
                                            nsIAsyncInputStream* aStream)
   : mParent(aParent)
   , mStream(aStream)
   , mClosed(false)
 {
--- a/dom/cache/ReadStream.cpp
+++ b/dom/cache/ReadStream.cpp
@@ -112,33 +112,33 @@ private:
 };
 
 // ----------------------------------------------------------------------------
 
 // Runnable to notify actors that the ReadStream has closed.  This must
 // be done on the thread associated with the PBackground actor.  Must be
 // cancelable to execute on Worker threads (which can occur when the
 // ReadStream is constructed on a child process Worker thread).
-class ReadStream::Inner::NoteClosedRunnable final : public nsCancelableRunnable
+class ReadStream::Inner::NoteClosedRunnable final : public CancelableRunnable
 {
 public:
   explicit NoteClosedRunnable(ReadStream::Inner* aStream)
     : mStream(aStream)
   { }
 
   NS_IMETHOD Run()
   {
     mStream->NoteClosedOnOwningThread();
     mStream = nullptr;
     return NS_OK;
   }
 
   // Note, we must proceed with the Run() method since our actor will not
   // clean itself up until we note that the stream is closed.
-  NS_IMETHOD Cancel()
+  nsresult Cancel()
   {
     Run();
     return NS_OK;
   }
 
 private:
   ~NoteClosedRunnable() { }
 
@@ -147,33 +147,33 @@ private:
 
 // ----------------------------------------------------------------------------
 
 // Runnable to clear actors without reporting that the ReadStream has
 // closed.  Since this can trigger actor destruction, we need to do
 // it on the thread associated with the PBackground actor.  Must be
 // cancelable to execute on Worker threads (which can occur when the
 // ReadStream is constructed on a child process Worker thread).
-class ReadStream::Inner::ForgetRunnable final : public nsCancelableRunnable
+class ReadStream::Inner::ForgetRunnable final : public CancelableRunnable
 {
 public:
   explicit ForgetRunnable(ReadStream::Inner* aStream)
     : mStream(aStream)
   { }
 
   NS_IMETHOD Run()
   {
     mStream->ForgetOnOwningThread();
     mStream = nullptr;
     return NS_OK;
   }
 
   // Note, we must proceed with the Run() method so that we properly
   // call RemoveListener on the actor.
-  NS_IMETHOD Cancel()
+  nsresult Cancel()
   {
     Run();
     return NS_OK;
   }
 
 private:
   ~ForgetRunnable() { }
 
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -4746,17 +4746,21 @@ CanvasRenderingContext2D::DrawDirectlyTo
   nsIntSize scaledImageSize(std::ceil(aImgSize.width * scale.width),
                             std::ceil(aImgSize.height * scale.height));
   aSrc.Scale(scale.width, scale.height);
 
   // We're wrapping tempTarget's (our) DrawTarget here, so we need to restore
   // the matrix even though this is a temp gfxContext.
   AutoRestoreTransform autoRestoreTransform(mTarget);
 
-  RefPtr<gfxContext> context = new gfxContext(tempTarget);
+  RefPtr<gfxContext> context = gfxContext::ForDrawTarget(tempTarget);
+  if (!context) {
+    gfxDevCrash(LogReason::InvalidContext) << "Canvas context problem";
+    return;
+  }
   context->SetMatrix(contextMatrix.
                        Scale(1.0 / contextScale.width,
                              1.0 / contextScale.height).
                        Translate(aDest.x - aSrc.x, aDest.y - aSrc.y));
 
   // FLAG_CLAMP is added for increased performance, since we never tile here.
   uint32_t modifiedFlags = aImage.mDrawingFlags | imgIContainer::FLAG_CLAMP;
 
@@ -4945,29 +4949,31 @@ CanvasRenderingContext2D::DrawWindow(nsG
   RefPtr<gfxContext> thebes;
   RefPtr<DrawTarget> drawDT;
   // Rendering directly is faster and can be done if mTarget supports Azure
   // and does not need alpha blending.
   if (gfxPlatform::GetPlatform()->SupportsAzureContentForDrawTarget(mTarget) &&
       GlobalAlpha() == 1.0f &&
       UsedOperation() == CompositionOp::OP_OVER)
   {
-    thebes = new gfxContext(mTarget);
+    thebes = gfxContext::ForDrawTarget(mTarget);
+    MOZ_ASSERT(thebes); // alrady checked the draw target above
     thebes->SetMatrix(gfxMatrix(matrix._11, matrix._12, matrix._21,
                                 matrix._22, matrix._31, matrix._32));
   } else {
     drawDT =
       gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(IntSize(ceil(sw), ceil(sh)),
                                                                    SurfaceFormat::B8G8R8A8);
-    if (!drawDT) {
+    if (!drawDT || !drawDT->IsValid()) {
       aError.Throw(NS_ERROR_FAILURE);
       return;
     }
 
-    thebes = new gfxContext(drawDT);
+    thebes = gfxContext::ForDrawTarget(drawDT);
+    MOZ_ASSERT(thebes); // alrady checked the draw target above
     thebes->SetMatrix(gfxMatrix::Scaling(matrix._11, matrix._22));
   }
 
   nsCOMPtr<nsIPresShell> shell = presContext->PresShell();
   Unused << shell->RenderDocument(r, renderDocFlags, backgroundColor, thebes);
   // If this canvas was contained in the drawn window, the pre-transaction callback
   // may have returned its DT. If so, we must reacquire it here.
   EnsureTarget();
--- a/dom/canvas/DocumentRendererChild.cpp
+++ b/dom/canvas/DocumentRendererChild.cpp
@@ -74,20 +74,21 @@ DocumentRendererChild::RenderDocument(ns
     data.SetLength(renderSize.width * renderSize.height * 4);
 
     RefPtr<DrawTarget> dt =
         Factory::CreateDrawTargetForData(BackendType::CAIRO,
                                          reinterpret_cast<uint8_t*>(data.BeginWriting()),
                                          IntSize(renderSize.width, renderSize.height),
                                          4 * renderSize.width,
                                          SurfaceFormat::B8G8R8A8);
-    if (!dt) {
+    if (!dt || !dt->IsValid()) {
         gfxWarning() << "DocumentRendererChild::RenderDocument failed to Factory::CreateDrawTargetForData";
         return false;
     }
-    RefPtr<gfxContext> ctx = new gfxContext(dt);
+    RefPtr<gfxContext> ctx = gfxContext::ForDrawTarget(dt);
+    MOZ_ASSERT(ctx); // already checked the draw target above
     ctx->SetMatrix(mozilla::gfx::ThebesMatrix(transform));
 
     nsCOMPtr<nsIPresShell> shell = presContext->PresShell();
     shell->RenderDocument(documentRect, renderFlags, bgColor, ctx);
 
     return true;
 }
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -1533,34 +1533,34 @@ WebGLContext::TryToRestoreContext()
 }
 
 void
 WebGLContext::RunContextLossTimer()
 {
     mContextLossHandler->RunTimer();
 }
 
-class UpdateContextLossStatusTask : public nsCancelableRunnable
+class UpdateContextLossStatusTask : public CancelableRunnable
 {
     RefPtr<WebGLContext> mWebGL;
 
 public:
     explicit UpdateContextLossStatusTask(WebGLContext* webgl)
         : mWebGL(webgl)
     {
     }
 
-    NS_IMETHOD Run() {
+    NS_IMETHOD Run() override {
         if (mWebGL)
             mWebGL->UpdateContextLossStatus();
 
         return NS_OK;
     }
 
-    NS_IMETHOD Cancel() {
+    nsresult Cancel() override {
         mWebGL = nullptr;
         return NS_OK;
     }
 };
 
 void
 WebGLContext::EnqueueUpdateContextLossStatus()
 {
--- a/dom/canvas/WebGLContextLossHandler.cpp
+++ b/dom/canvas/WebGLContextLossHandler.cpp
@@ -33,26 +33,25 @@ public:
 
 protected:
     ~ContextLossWorkerEventTarget() {}
 
 private:
     nsCOMPtr<nsIEventTarget> mEventTarget;
 };
 
-class ContextLossWorkerRunnable final : public nsICancelableRunnable
+class ContextLossWorkerRunnable final : public CancelableRunnable
 {
 public:
     explicit ContextLossWorkerRunnable(nsIRunnable* aRunnable)
         : mRunnable(aRunnable)
     {
     }
 
-    NS_DECL_NSICANCELABLERUNNABLE
-    NS_DECL_THREADSAFE_ISUPPORTS
+    nsresult Cancel() override;
 
     NS_FORWARD_NSIRUNNABLE(mRunnable->)
 
 protected:
     ~ContextLossWorkerRunnable() {}
 
 private:
     nsCOMPtr<nsIRunnable> mRunnable;
@@ -79,20 +78,17 @@ ContextLossWorkerEventTarget::Dispatch(a
 }
 
 NS_IMETHODIMP
 ContextLossWorkerEventTarget::IsOnCurrentThread(bool* aResult)
 {
     return mEventTarget->IsOnCurrentThread(aResult);
 }
 
-NS_IMPL_ISUPPORTS(ContextLossWorkerRunnable, nsICancelableRunnable,
-                  nsIRunnable)
-
-NS_IMETHODIMP
+nsresult
 ContextLossWorkerRunnable::Cancel()
 {
     mRunnable = nullptr;
     return NS_OK;
 }
 
 // -------------------------------------------------------------------
 // End worker-specific code
--- a/dom/crypto/WebCryptoTask.h
+++ b/dom/crypto/WebCryptoTask.h
@@ -52,17 +52,17 @@ Cleanup should execute regardless of wha
 
 #define MAYBE_EARLY_FAIL(rv) \
 if (NS_FAILED(rv)) { \
   FailWithError(rv); \
   Skip(); \
   return; \
 }
 
-class WebCryptoTask : public nsCancelableRunnable,
+class WebCryptoTask : public CancelableRunnable,
                       public nsNSSShutDownObject
 {
 public:
   virtual void DispatchWithPromise(Promise* aResultPromise);
 
   void Skip()
   {
     virtualDestroyNSSReference();
--- a/dom/events/AsyncEventDispatcher.cpp
+++ b/dom/events/AsyncEventDispatcher.cpp
@@ -49,17 +49,17 @@ AsyncEventDispatcher::Run()
     MOZ_ASSERT(event->IsTrusted());
     event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
   }
   bool dummy;
   mTarget->DispatchEvent(event, &dummy);
   return NS_OK;
 }
 
-NS_IMETHODIMP
+nsresult
 AsyncEventDispatcher::Cancel()
 {
   mCanceled = true;
   return NS_OK;
 }
 
 nsresult
 AsyncEventDispatcher::PostDOMEvent()
--- a/dom/events/AsyncEventDispatcher.h
+++ b/dom/events/AsyncEventDispatcher.h
@@ -20,17 +20,17 @@ namespace mozilla {
 
 /**
  * Use AsyncEventDispatcher to fire a DOM event that requires safe a stable DOM.
  * For example, you may need to fire an event from within layout, but
  * want to ensure that the event handler doesn't mutate the DOM at
  * the wrong time, in order to avoid resulting instability.
  */
 
-class AsyncEventDispatcher : public nsCancelableRunnable
+class AsyncEventDispatcher : public CancelableRunnable
 {
 public:
   /**
    * If aOnlyChromeDispatch is true, the event is dispatched to only
    * chrome node. In that case, if aTarget is already a chrome node,
    * the event is dispatched to it, otherwise the dispatch path starts
    * at the first chrome ancestor of that target.
    */
@@ -55,17 +55,17 @@ public:
     : mTarget(aTarget)
     , mEvent(aEvent)
   {
   }
 
   AsyncEventDispatcher(dom::EventTarget* aTarget, WidgetEvent& aEvent);
 
   NS_IMETHOD Run() override;
-  NS_IMETHOD Cancel() override;
+  nsresult Cancel() override;
   nsresult PostDOMEvent();
   void RunDOMEventWhenSafe();
 
   nsCOMPtr<dom::EventTarget> mTarget;
   nsCOMPtr<nsIDOMEvent> mEvent;
   nsString              mEventType;
   bool                  mBubbles = false;
   bool                  mOnlyChromeDispatch = false;
--- a/dom/filesystem/FileSystemPermissionRequest.cpp
+++ b/dom/filesystem/FileSystemPermissionRequest.cpp
@@ -72,17 +72,17 @@ void
 PBackgroundInitializer::ActorCreated(mozilla::ipc::PBackgroundChild* aActor)
 {
   mTask->Start();
 }
 
 // This must be a CancelableRunnable because it can be dispatched to a worker
 // thread. But we don't care about the Cancel() because in that case, Run() is
 // not called and the task is deleted by the DTOR.
-class AsyncStartRunnable final : public nsCancelableRunnable
+class AsyncStartRunnable final : public CancelableRunnable
 {
 public:
   explicit AsyncStartRunnable(FileSystemTaskChildBase* aTask)
     : mTask(aTask)
   {
     MOZ_ASSERT(aTask);
   }
 
--- a/dom/filesystem/FileSystemTaskBase.cpp
+++ b/dom/filesystem/FileSystemTaskBase.cpp
@@ -73,17 +73,17 @@ DispatchToIOThread(nsIRunnable* aRunnabl
   MOZ_ASSERT(target);
 
   return target->Dispatch(aRunnable, NS_DISPATCH_NORMAL);
 }
 
 // This runnable is used when an error value is set before doing any real
 // operation on the I/O thread. In this case we skip all and we directly
 // communicate the error.
-class ErrorRunnable final : public nsCancelableRunnable
+class ErrorRunnable final : public CancelableRunnable
 {
 public:
   explicit ErrorRunnable(FileSystemTaskChildBase* aTask)
     : mTask(aTask)
   {
     MOZ_ASSERT(aTask);
   }
 
--- a/dom/html/HTMLCanvasElement.cpp
+++ b/dom/html/HTMLCanvasElement.cpp
@@ -1258,17 +1258,17 @@ HTMLCanvasElement::GetCompositorBackendT
 void
 HTMLCanvasElement::OnVisibilityChange()
 {
   if (OwnerDoc()->Hidden()) {
     return;
   }
 
   if (mOffscreenCanvas) {
-    class Runnable final : public nsCancelableRunnable
+    class Runnable final : public CancelableRunnable
     {
     public:
       explicit Runnable(AsyncCanvasRenderer* aRenderer)
         : mRenderer(aRenderer)
       {}
 
       NS_IMETHOD Run()
       {
@@ -1300,17 +1300,17 @@ HTMLCanvasElement::OnVisibilityChange()
     mCurrentContext->OnVisibilityChange();
   }
 }
 
 void
 HTMLCanvasElement::OnMemoryPressure()
 {
   if (mOffscreenCanvas) {
-    class Runnable final : public nsCancelableRunnable
+    class Runnable final : public CancelableRunnable
     {
     public:
       explicit Runnable(AsyncCanvasRenderer* aRenderer)
         : mRenderer(aRenderer)
       {}
 
       NS_IMETHOD Run()
       {
--- a/dom/indexedDB/ActorsChild.cpp
+++ b/dom/indexedDB/ActorsChild.cpp
@@ -2525,18 +2525,20 @@ BackgroundRequestChild::Recv__delete__(c
 
   return true;
 }
 
 /*******************************************************************************
  * BackgroundCursorChild
  ******************************************************************************/
 
+// Does not need to be threadsafe since this only runs on one thread, but
+// inheriting from CancelableRunnable is easy.
 class BackgroundCursorChild::DelayedActionRunnable final
-  : public nsICancelableRunnable
+  : public CancelableRunnable
 {
   using ActionFunc = void (BackgroundCursorChild::*)();
 
   BackgroundCursorChild* mActor;
   RefPtr<IDBRequest> mRequest;
   ActionFunc mActionFunc;
 
 public:
@@ -2547,25 +2549,22 @@ public:
     , mActionFunc(aActionFunc)
   {
     MOZ_ASSERT(aActor);
     aActor->AssertIsOnOwningThread();
     MOZ_ASSERT(mRequest);
     MOZ_ASSERT(mActionFunc);
   }
 
-  // Does not need to be threadsafe since this only runs on one thread.
-  NS_DECL_ISUPPORTS
-
 private:
   ~DelayedActionRunnable()
   { }
 
   NS_DECL_NSIRUNNABLE
-  NS_DECL_NSICANCELABLERUNNABLE
+  nsresult Cancel() override;
 };
 
 BackgroundCursorChild::BackgroundCursorChild(IDBRequest* aRequest,
                                              IDBObjectStore* aObjectStore,
                                              Direction aDirection)
   : mRequest(aRequest)
   , mTransaction(aRequest->GetTransaction())
   , mObjectStore(aObjectStore)
@@ -2992,20 +2991,16 @@ BackgroundCursorChild::RecvResponse(cons
       MOZ_CRASH("Should never get here!");
   }
 
   mTransaction->OnRequestFinished(/* aActorDestroyedNormally */ true);
 
   return true;
 }
 
-NS_IMPL_ISUPPORTS(BackgroundCursorChild::DelayedActionRunnable,
-                  nsIRunnable,
-                  nsICancelableRunnable)
-
 NS_IMETHODIMP
 BackgroundCursorChild::
 DelayedActionRunnable::Run()
 {
   MOZ_ASSERT(mActor);
   mActor->AssertIsOnOwningThread();
   MOZ_ASSERT(mRequest);
   MOZ_ASSERT(mActionFunc);
@@ -3013,17 +3008,17 @@ DelayedActionRunnable::Run()
   (mActor->*mActionFunc)();
 
   mActor = nullptr;
   mRequest = nullptr;
 
   return NS_OK;
 }
 
-NS_IMETHODIMP
+nsresult
 BackgroundCursorChild::
 DelayedActionRunnable::Cancel()
 {
   if (NS_WARN_IF(!mActor)) {
     return NS_ERROR_UNEXPECTED;
   }
 
   // This must always run to clean up our state.
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -7787,17 +7787,17 @@ private:
   virtual nsresult
   DoDatabaseWork(DatabaseConnection* aConnection) override;
 };
 
 class NormalJSRuntime
 {
   friend class nsAutoPtr<NormalJSRuntime>;
 
-  static const JSClass kGlobalClass;
+  static const JSClass sGlobalClass;
   static const uint32_t kRuntimeHeapSize = 768 * 1024;
 
   JSRuntime* mRuntime;
   JSContext* mContext;
   JSObject* mGlobal;
 
 public:
   static NormalJSRuntime*
@@ -23756,33 +23756,37 @@ CreateIndexOp::DoDatabaseWork(DatabaseCo
   rv = autoSave.Commit();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
-const JSClass NormalJSRuntime::kGlobalClass = {
-  "IndexedDBTransactionThreadGlobal",
-  JSCLASS_GLOBAL_FLAGS,
+static const JSClassOps sNormalJSRuntimeGlobalClassOps = {
   /* addProperty */ nullptr,
   /* delProperty */ nullptr,
   /* getProperty */ nullptr,
   /* setProperty */ nullptr,
   /* enumerate */ nullptr,
   /* resolve */ nullptr,
   /* mayResolve */ nullptr,
   /* finalize */ nullptr,
   /* call */ nullptr,
   /* hasInstance */ nullptr,
   /* construct */ nullptr,
   /* trace */ JS_GlobalObjectTraceHook
 };
 
+const JSClass NormalJSRuntime::sGlobalClass = {
+  "IndexedDBTransactionThreadGlobal",
+  JSCLASS_GLOBAL_FLAGS,
+  &sNormalJSRuntimeGlobalClassOps
+};
+
 bool
 NormalJSRuntime::Init()
 {
   MOZ_ASSERT(!IsOnBackgroundThread());
 
   mRuntime = JS_NewRuntime(kRuntimeHeapSize);
   if (NS_WARN_IF(!mRuntime)) {
     return false;
@@ -23794,17 +23798,17 @@ NormalJSRuntime::Init()
   mContext = JS_NewContext(mRuntime, 0);
   if (NS_WARN_IF(!mContext)) {
     return false;
   }
 
   JSAutoRequest ar(mContext);
 
   JS::CompartmentOptions options;
-  mGlobal = JS_NewGlobalObject(mContext, &kGlobalClass, nullptr,
+  mGlobal = JS_NewGlobalObject(mContext, &sGlobalClass, nullptr,
                                JS::FireOnNewGlobalHook, options);
   if (NS_WARN_IF(!mGlobal)) {
     return false;
   }
 
   return true;
 }
 
--- a/dom/indexedDB/IDBDatabase.cpp
+++ b/dom/indexedDB/IDBDatabase.cpp
@@ -62,36 +62,34 @@ using namespace mozilla::services;
 
 namespace {
 
 const char kCycleCollectionObserverTopic[] = "cycle-collector-end";
 const char kMemoryPressureObserverTopic[] = "memory-pressure";
 const char kWindowObserverTopic[] = "inner-window-destroyed";
 
 class CancelableRunnableWrapper final
-  : public nsICancelableRunnable
+  : public CancelableRunnable
 {
   nsCOMPtr<nsIRunnable> mRunnable;
 
 public:
   explicit
   CancelableRunnableWrapper(nsIRunnable* aRunnable)
     : mRunnable(aRunnable)
   {
     MOZ_ASSERT(aRunnable);
   }
 
-  NS_DECL_ISUPPORTS
-
 private:
   ~CancelableRunnableWrapper()
   { }
 
   NS_DECL_NSIRUNNABLE
-  NS_DECL_NSICANCELABLERUNNABLE
+  nsresult Cancel() override;
 };
 
 class DatabaseFile final
   : public PBackgroundIDBDatabaseFileChild
 {
   IDBDatabase* mDatabase;
 
 public:
@@ -1270,32 +1268,30 @@ IDBDatabase::PostHandleEvent(EventChainP
 }
 
 JSObject*
 IDBDatabase::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return IDBDatabaseBinding::Wrap(aCx, this, aGivenProto);
 }
 
-NS_IMPL_ISUPPORTS(CancelableRunnableWrapper, nsIRunnable, nsICancelableRunnable)
-
 NS_IMETHODIMP
 CancelableRunnableWrapper::Run()
 {
   nsCOMPtr<nsIRunnable> runnable;
   mRunnable.swap(runnable);
 
   if (runnable) {
     return runnable->Run();
   }
 
   return NS_OK;
 }
 
-NS_IMETHODIMP
+nsresult
 CancelableRunnableWrapper::Cancel()
 {
   if (mRunnable) {
     mRunnable = nullptr;
     return NS_OK;
   }
 
   return NS_ERROR_UNEXPECTED;
--- a/dom/ipc/Blob.cpp
+++ b/dom/ipc/Blob.cpp
@@ -207,17 +207,17 @@ EventTargetIsOnCurrentThread(nsIEventTar
 
   bool current;
   MOZ_ALWAYS_SUCCEEDS(aEventTarget->IsOnCurrentThread(&current));
 
   return current;
 }
 
 class CancelableRunnableWrapper final
-  : public nsCancelableRunnable
+  : public CancelableRunnable
 {
   nsCOMPtr<nsIRunnable> mRunnable;
 #ifdef DEBUG
   nsCOMPtr<nsIEventTarget> mDEBUGEventTarget;
 #endif
 
 public:
   CancelableRunnableWrapper(nsIRunnable* aRunnable,
@@ -233,20 +233,20 @@ public:
 
   NS_DECL_ISUPPORTS_INHERITED
 
 private:
   ~CancelableRunnableWrapper()
   { }
 
   NS_DECL_NSIRUNNABLE
-  NS_DECL_NSICANCELABLERUNNABLE
+  nsresult Cancel() override;
 };
 
-NS_IMPL_ISUPPORTS_INHERITED0(CancelableRunnableWrapper, nsCancelableRunnable)
+NS_IMPL_ISUPPORTS_INHERITED0(CancelableRunnableWrapper, CancelableRunnable)
 
 NS_IMETHODIMP
 CancelableRunnableWrapper::Run()
 {
   DebugOnly<bool> onTarget;
   MOZ_ASSERT(mDEBUGEventTarget);
   MOZ_ASSERT(NS_SUCCEEDED(mDEBUGEventTarget->IsOnCurrentThread(&onTarget)));
   MOZ_ASSERT(onTarget);
@@ -256,17 +256,17 @@ CancelableRunnableWrapper::Run()
 
   if (runnable) {
     return runnable->Run();
   }
 
   return NS_OK;
 }
 
-NS_IMETHODIMP
+nsresult
 CancelableRunnableWrapper::Cancel()
 {
   DebugOnly<bool> onTarget;
   MOZ_ASSERT(mDEBUGEventTarget);
   MOZ_ASSERT(NS_SUCCEEDED(mDEBUGEventTarget->IsOnCurrentThread(&onTarget)));
   MOZ_ASSERT(onTarget);
 
   if (NS_WARN_IF(!mRunnable)) {
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -37,16 +37,17 @@
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/nsIContentChild.h"
 #include "mozilla/psm/PSMContentListener.h"
 #include "mozilla/hal_sandbox/PHalChild.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/FileDescriptorSetChild.h"
 #include "mozilla/ipc/FileDescriptorUtils.h"
 #include "mozilla/ipc/GeckoChildProcessHost.h"
+#include "mozilla/ipc/ProcessChild.h"
 #include "mozilla/ipc/TestShellChild.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
 #include "mozilla/layers/APZChild.h"
 #include "mozilla/layers/CompositorBridgeChild.h"
 #include "mozilla/layers/ImageBridgeChild.h"
 #include "mozilla/layers/SharedBufferManagerChild.h"
 #include "mozilla/layout/RenderFrameChild.h"
 #include "mozilla/net/NeckoChild.h"
@@ -2224,24 +2225,24 @@ ContentChild::RecvSetConnectivity(const 
   return true;
 }
 
 void
 ContentChild::ActorDestroy(ActorDestroyReason why)
 {
   if (AbnormalShutdown == why) {
     NS_WARNING("shutting down early because of crash!");
-    QuickExit();
+    ProcessChild::QuickExit();
   }
 
 #ifndef NS_FREE_PERMANENT_DATA
   // In release builds, there's no point in the content process
   // going through the full XPCOM shutdown path, because it doesn't
   // keep persistent state.
-  QuickExit();
+  ProcessChild::QuickExit();
 #else
   if (sFirstIdleTask) {
     sFirstIdleTask->Cancel();
   }
 
   mAlertObservers.Clear();
 
   mIdleObservers.Clear();
@@ -2252,17 +2253,17 @@ ContentChild::ActorDestroy(ActorDestroyR
     mConsoleListener->mChild = nullptr;
   }
   mIsAlive = false;
 
 #ifdef MOZ_NUWA_PROCESS
   if (IsNuwaProcess()) {
     // The Nuwa cannot go through the full XPCOM shutdown path or deadlock
     // will result.
-    QuickExit();
+    ProcessChild::QuickExit();
   }
 #endif
 
   XRE_ShutdownChildProcess();
 #endif // NS_FREE_PERMANENT_DATA
 }
 
 void
@@ -2293,31 +2294,16 @@ ContentChild::ProcessingError(Result aCo
     crashReporter->SendAnnotateCrashReport(
         NS_LITERAL_CSTRING("ipc_channel_error"),
         reason);
   }
 #endif
   NS_RUNTIMEABORT("Content child abort due to IPC error");
 }
 
-void
-ContentChild::QuickExit()
-{
-  NS_WARNING("content process _exit()ing");
-
-#ifdef XP_WIN
-  // In bug 1254829, the destructor got called when dll got detached on windows,
-  // switch to TerminateProcess to bypass dll detach handler during the process
-  // termination.
-  TerminateProcess(GetCurrentProcess(), 0);
-#else
-  _exit(0);
-#endif
-}
-
 nsresult
 ContentChild::AddRemoteAlertObserver(const nsString& aData,
                                      nsIObserver* aObserver)
 {
   NS_ASSERTION(aObserver, "Adding a null observer?");
   mAlertObservers.AppendElement(new AlertObserver(aObserver, aData));
   return NS_OK;
 }
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -610,22 +610,16 @@ public:
                           const nsString& aDisplayName,
                           const nsString& aIconPath) override;
 
 private:
   virtual void ActorDestroy(ActorDestroyReason why) override;
 
   virtual void ProcessingError(Result aCode, const char* aReason) override;
 
-  /**
-   * Exit *now*.  Do not shut down XPCOM, do not pass Go, do not run
-   * static destructors, do not collect $200.
-   */
-  MOZ_NORETURN void QuickExit();
-
   InfallibleTArray<nsAutoPtr<AlertObserver> > mAlertObservers;
   RefPtr<ConsoleListener> mConsoleListener;
 
   nsTHashtable<nsPtrHashKey<nsIObserver>> mIdleObservers;
 
   InfallibleTArray<nsString> mAvailableDictionaries;
 
   /**
--- a/dom/media/gmp/GMPChild.cpp
+++ b/dom/media/gmp/GMPChild.cpp
@@ -14,22 +14,24 @@
 #include "GMPVideoHost.h"
 #include "nsDebugImpl.h"
 #include "nsIFile.h"
 #include "nsXULAppAPI.h"
 #include "gmp-video-decode.h"
 #include "gmp-video-encode.h"
 #include "GMPPlatform.h"
 #include "mozilla/dom/CrashReporterChild.h"
+#include "mozilla/ipc/ProcessChild.h"
 #include "GMPUtils.h"
 #include "prio.h"
 #ifdef MOZ_WIDEVINE_EME
 #include "widevine-adapter/WidevineAdapter.h"
 #endif
 
+using namespace mozilla::ipc;
 using mozilla::dom::CrashReporterChild;
 
 static const int MAX_VOUCHER_LENGTH = 500000;
 
 #ifdef XP_WIN
 #include <stdlib.h> // for _exit()
 #else
 #include <unistd.h> // for _exit()
@@ -425,17 +427,17 @@ GMPChild::ActorDestroy(ActorDestroyReaso
     mGMPContentChildren[i - 1]->Close();
   }
 
   if (mGMPLoader) {
     mGMPLoader->Shutdown();
   }
   if (AbnormalShutdown == aWhy) {
     NS_WARNING("Abnormal shutdown of GMP process!");
-    _exit(0);
+    ProcessChild::QuickExit();
   }
 
   XRE_ShutdownChildProcess();
 }
 
 void
 GMPChild::ProcessingError(Result aCode, const char* aReason)
 {
--- a/dom/media/platforms/android/AndroidDecoderModule.cpp
+++ b/dom/media/platforms/android/AndroidDecoderModule.cpp
@@ -373,20 +373,21 @@ MediaCodecDataDecoder::InitDecoder(Surfa
 
   nsresult rv;
   NS_ENSURE_SUCCESS(rv = mDecoder->Configure(mFormat, aSurface, nullptr, 0), rv);
   NS_ENSURE_SUCCESS(rv = mDecoder->Start(), rv);
 
   NS_ENSURE_SUCCESS(rv = ResetInputBuffers(), rv);
   NS_ENSURE_SUCCESS(rv = ResetOutputBuffers(), rv);
 
-  NS_NewNamedThread("MC Decoder", getter_AddRefs(mThread),
-                    NS_NewRunnableMethod(this, &MediaCodecDataDecoder::DecoderLoop));
+  rv = NS_NewNamedThread(
+      "MC Decoder", getter_AddRefs(mThread),
+      NS_NewRunnableMethod(this, &MediaCodecDataDecoder::DecoderLoop));
 
-  return NS_OK;
+  return rv;
 }
 
 // This is in usec, so that's 10ms.
 static const int64_t kDecoderTimeout = 10000;
 
 #define BREAK_ON_DECODER_ERROR() \
   if (NS_FAILED(res)) { \
     NS_WARNING("Exiting decoder loop due to exception"); \
@@ -666,28 +667,37 @@ MediaCodecDataDecoder::ModuleStateStr(Mo
 }
 
 MediaCodecDataDecoder::ModuleState
 MediaCodecDataDecoder::State() const
 {
   return mState;
 }
 
-void
+bool
 MediaCodecDataDecoder::State(ModuleState aState)
 {
-  LOG("%s -> %s", ModuleStateStr(mState), ModuleStateStr(aState));
+  bool ok = true;
 
-  if (aState == kDrainDecoder) {
-    MOZ_ASSERT(mState == kDrainQueue);
+  if (mState == kShutdown) {
+    ok = false;
+  } else if (mState == kStopping) {
+    ok = aState == kShutdown;
+  } else if (aState == kDrainDecoder) {
+    ok = mState == kDrainQueue;
   } else if (aState == kDrainWaitEOS) {
-    MOZ_ASSERT(mState == kDrainDecoder);
+    ok = mState == kDrainDecoder;
   }
 
-  mState = aState;
+  if (ok) {
+    LOG("%s -> %s", ModuleStateStr(mState), ModuleStateStr(aState));
+    mState = aState;
+  }
+
+  return ok;
 }
 
 template<typename T>
 void
 Clear(T& aCont)
 {
   T aEmpty = T();
   swap(aCont, aEmpty);
@@ -723,17 +733,19 @@ MediaCodecDataDecoder::ResetOutputBuffer
 {
   return mDecoder->GetOutputBuffers(ReturnTo(&mOutputBuffers));
 }
 
 nsresult
 MediaCodecDataDecoder::Flush()
 {
   MonitorAutoLock lock(mMonitor);
-  State(kFlushing);
+  if (!State(kFlushing)) {
+    return NS_OK;
+  }
   lock.Notify();
 
   while (State() == kFlushing) {
     lock.Wait();
   }
 
   return NS_OK;
 }
@@ -753,25 +765,20 @@ MediaCodecDataDecoder::Drain()
 }
 
 
 nsresult
 MediaCodecDataDecoder::Shutdown()
 {
   MonitorAutoLock lock(mMonitor);
 
-  if (State() == kStopping) {
-    // Already shutdown or in the process of doing so
-    return NS_OK;
-  }
-
   State(kStopping);
   lock.Notify();
 
-  while (mThread && State() == kStopping) {
+  while (mThread && State() != kShutdown) {
     lock.Wait();
   }
 
   if (mThread) {
     mThread->Shutdown();
     mThread = nullptr;
   }
 
--- a/dom/media/platforms/android/AndroidDecoderModule.h
+++ b/dom/media/platforms/android/AndroidDecoderModule.h
@@ -102,17 +102,18 @@ protected:
   nsresult QueueSample(const MediaRawData* aSample);
   nsresult QueueEOS();
   void HandleEOS(int32_t aOutputStatus);
   media::TimeUnit GetOutputDuration();
   nsresult ProcessOutput(widget::sdk::BufferInfo::Param aInfo,
                          widget::sdk::MediaFormat::Param aFormat,
                          int32_t aStatus);
   ModuleState State() const;
-  void State(ModuleState aState);
+  // Sets decoder state and returns whether the new state has become effective.
+  bool State(ModuleState aState);
   void DecoderLoop();
 
   virtual void ClearQueue();
 
   MediaData::Type mType;
 
   nsAutoCString mMimeType;
   widget::sdk::MediaFormat::GlobalRef mFormat;
--- a/dom/media/webrtc/MediaEngineTabVideoSource.cpp
+++ b/dom/media/webrtc/MediaEngineTabVideoSource.cpp
@@ -290,20 +290,21 @@ MediaEngineTabVideoSource::Draw() {
 
   RefPtr<layers::ImageContainer> container = layers::LayerManager::CreateImageContainer();
   RefPtr<DrawTarget> dt =
     Factory::CreateDrawTargetForData(BackendType::CAIRO,
                                      mData.get(),
                                      size,
                                      stride,
                                      SurfaceFormat::B8G8R8X8);
-  if (!dt) {
+  if (!dt || !dt->IsValid()) {
     return;
   }
-  RefPtr<gfxContext> context = new gfxContext(dt);
+  RefPtr<gfxContext> context = gfxContext::ForDrawTarget(dt);
+  MOZ_ASSERT(context); // already checked the draw target above
   context->SetMatrix(context->CurrentMatrix().Scale((((float) size.width)/mViewportWidth),
                                                     (((float) size.height)/mViewportHeight)));
 
   if (mWindow) {
     nscolor bgColor = NS_RGB(255, 255, 255);
     uint32_t renderDocFlags = mScrollWithPage? 0 :
       (nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING |
        nsIPresShell::RENDER_DOCUMENT_RELATIVE);
--- a/dom/messagechannel/MessagePort.cpp
+++ b/dom/messagechannel/MessagePort.cpp
@@ -43,23 +43,21 @@
 #undef PostMessage
 #endif
 
 using namespace mozilla::dom::workers;
 
 namespace mozilla {
 namespace dom {
 
-class PostMessageRunnable final : public nsICancelableRunnable
+class PostMessageRunnable final : public CancelableRunnable
 {
   friend class MessagePort;
 
 public:
-  NS_DECL_ISUPPORTS
-
   PostMessageRunnable(MessagePort* aPort, SharedMessagePortMessage* aData)
     : mPort(aPort)
     , mData(aData)
   {
     MOZ_ASSERT(aPort);
     MOZ_ASSERT(aData);
   }
 
@@ -76,17 +74,17 @@ public:
     mPort->UpdateMustKeepAlive();
 
     mPort->mPostMessageRunnable = nullptr;
     mPort->Dispatch();
 
     return rv;
   }
 
-  NS_IMETHOD
+  nsresult
   Cancel() override
   {
     mPort = nullptr;
     mData = nullptr;
     return NS_OK;
   }
 
 private:
@@ -160,18 +158,16 @@ private:
 private:
   ~PostMessageRunnable()
   {}
 
   RefPtr<MessagePort> mPort;
   RefPtr<SharedMessagePortMessage> mData;
 };
 
-NS_IMPL_ISUPPORTS(PostMessageRunnable, nsICancelableRunnable, nsIRunnable)
-
 NS_IMPL_CYCLE_COLLECTION_CLASS(MessagePort)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MessagePort,
                                                 DOMEventTargetHelper)
   if (tmp->mPostMessageRunnable) {
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mPostMessageRunnable->mPort);
   }
 
--- a/dom/plugins/base/nsJSNPRuntime.cpp
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -213,18 +213,33 @@ NPObjWrapper_Construct(JSContext *cx, un
 static bool
 NPObjWrapper_toPrimitive(JSContext *cx, unsigned argc, JS::Value *vp);
 
 static bool
 CreateNPObjectMember(NPP npp, JSContext *cx, JSObject *obj, NPObject* npobj,
                      JS::Handle<jsid> id,  NPVariant* getPropertyResult,
                      JS::MutableHandle<JS::Value> vp);
 
+const static js::ClassOps sNPObjectJSWrapperClassOps = {
+    NPObjWrapper_AddProperty,
+    NPObjWrapper_DelProperty,
+    NPObjWrapper_GetProperty,
+    NPObjWrapper_SetProperty,
+    nullptr,
+    NPObjWrapper_Resolve,
+    nullptr,                    /* mayResolve */
+    NPObjWrapper_Finalize,
+    NPObjWrapper_Call,
+    nullptr,                    /* hasInstance */
+    NPObjWrapper_Construct,
+    nullptr,                    /* trace */
+};
+
 const static js::ClassExtension sNPObjectJSWrapperClassExtension = {
-    nullptr,                                              /* weakmapKeyDelegateOp */
+    nullptr,                    /* weakmapKeyDelegateOp */
     NPObjWrapper_ObjectMoved
 };
 
 const static js::ObjectOps sNPObjectJSWrapperObjectOps = {
     nullptr, // lookupProperty
     nullptr, // defineProperty
     nullptr, // hasProperty
     nullptr, // getProperty
@@ -232,36 +247,24 @@ const static js::ObjectOps sNPObjectJSWr
     nullptr, // getOwnPropertyDescriptor
     nullptr, // deleteProperty
     nullptr, nullptr, // watch/unwatch
     nullptr, // getElements
     NPObjWrapper_Enumerate,
     nullptr,
 };
 
-const static js::Class sNPObjectJSWrapperClass =
-  {
+const static js::Class sNPObjectJSWrapperClass = {
     NPRUNTIME_JSCLASS_NAME,
     JSCLASS_HAS_PRIVATE,
-    NPObjWrapper_AddProperty,
-    NPObjWrapper_DelProperty,
-    NPObjWrapper_GetProperty,
-    NPObjWrapper_SetProperty,
-    nullptr,
-    NPObjWrapper_Resolve,
-    nullptr,                                                /* mayResolve */
-    NPObjWrapper_Finalize,
-    NPObjWrapper_Call,
-    nullptr,                                                /* hasInstance */
-    NPObjWrapper_Construct,
-    nullptr,                                                /* trace */
+    &sNPObjectJSWrapperClassOps,
     JS_NULL_CLASS_SPEC,
     &sNPObjectJSWrapperClassExtension,
     &sNPObjectJSWrapperObjectOps
-  };
+};
 
 typedef struct NPObjectMemberPrivate {
     JS::Heap<JSObject *> npobjWrapper;
     JS::Heap<JS::Value> fieldValue;
     JS::Heap<jsid> methodName;
     NPP   npp;
 } NPObjectMemberPrivate;
 
@@ -276,24 +279,27 @@ static bool
 NPObjectMember_Call(JSContext *cx, unsigned argc, JS::Value *vp);
 
 static void
 NPObjectMember_Trace(JSTracer *trc, JSObject *obj);
 
 static bool
 NPObjectMember_toPrimitive(JSContext *cx, unsigned argc, JS::Value *vp);
 
-static const JSClass sNPObjectMemberClass =
-  {
-    "NPObject Ambiguous Member class", JSCLASS_HAS_PRIVATE,
-    nullptr, nullptr, NPObjectMember_GetProperty, nullptr,
-    nullptr, nullptr, nullptr,
-    NPObjectMember_Finalize, NPObjectMember_Call,
-    nullptr, nullptr, NPObjectMember_Trace
-  };
+static const JSClassOps sNPObjectMemberClassOps = {
+  nullptr, nullptr, NPObjectMember_GetProperty, nullptr,
+  nullptr, nullptr, nullptr,
+  NPObjectMember_Finalize, NPObjectMember_Call,
+  nullptr, nullptr, NPObjectMember_Trace
+};
+
+static const JSClass sNPObjectMemberClass = {
+  "NPObject Ambiguous Member class", JSCLASS_HAS_PRIVATE,
+  &sNPObjectMemberClassOps
+};
 
 static void
 OnWrapperDestroyed();
 
 static void
 TraceJSObjWrappers(JSTracer *trc, void *data)
 {
   if (sJSObjWrappers.initialized()) {
--- a/dom/plugins/ipc/PluginModuleChild.cpp
+++ b/dom/plugins/ipc/PluginModuleChild.cpp
@@ -1,16 +1,15 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* vim: set sw=4 ts=4 et : */
 /* 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/. */
 
 #ifdef MOZ_WIDGET_QT
-#include <unistd.h> // for _exit()
 #include <QtCore/QTimer>
 #include "nsQAppInstance.h"
 #include "NestedLoopTimer.h"
 #endif
 
 #include "mozilla/plugins/PluginModuleChild.h"
 
 /* This must occur *after* plugins/PluginModuleChild.h to avoid typedefs conflicts. */
@@ -28,16 +27,17 @@
 #include "nsDebug.h"
 #include "nsCOMPtr.h"
 #include "nsPluginsDir.h"
 #include "nsXULAppAPI.h"
 
 #ifdef MOZ_X11
 # include "mozilla/X11Util.h"
 #endif
+#include "mozilla/ipc/ProcessChild.h"
 #include "mozilla/plugins/PluginInstanceChild.h"
 #include "mozilla/plugins/StreamNotifyChild.h"
 #include "mozilla/plugins/BrowserStreamChild.h"
 #include "mozilla/plugins/PluginStreamChild.h"
 #include "mozilla/dom/CrashReporterChild.h"
 #include "mozilla/unused.h"
 
 #include "nsNPAPIPlugin.h"
@@ -52,16 +52,17 @@
 #ifdef MOZ_WIDGET_COCOA
 #include "PluginInterposeOSX.h"
 #include "PluginUtilsOSX.h"
 #endif
 
 #include "GeckoProfiler.h"
 
 using namespace mozilla;
+using namespace mozilla::ipc;
 using namespace mozilla::plugins;
 using namespace mozilla::widget;
 using mozilla::dom::CrashReporterChild;
 using mozilla::dom::PCrashReporterChild;
 
 #if defined(XP_WIN)
 const wchar_t * kFlashFullscreenClass = L"ShockwaveFlashFullScreen";
 const wchar_t * kMozillaWindowClass = L"MozillaWindowClass";
@@ -781,23 +782,16 @@ PluginModuleChild::RecvSetAudioSessionDa
     NS_ENSURE_SUCCESS(rv, true); // Bail early if this fails
 
     // Ignore failures here; we can't really do anything about them
     mozilla::widget::StartAudioSession();
     return true;
 #endif
 }
 
-void
-PluginModuleChild::QuickExit()
-{
-    NS_WARNING("plugin process _exit()ing");
-    _exit(0);
-}
-
 PPluginModuleChild*
 PluginModuleChild::AllocPPluginModuleChild(mozilla::ipc::Transport* aTransport,
                                            base::ProcessId aOtherPid)
 {
     return PluginModuleChild::CreateForContentProcess(aTransport, aOtherPid);
 }
 
 PCrashReporterChild*
@@ -838,17 +832,17 @@ PluginModuleChild::ActorDestroy(ActorDes
 
         // Destroy ourselves once we finish other teardown activities.
         MessageLoop::current()->PostTask(FROM_HERE, new DeleteTask<PluginModuleChild>(this));
         return;
     }
 
     if (AbnormalShutdown == why) {
         NS_WARNING("shutting down early because of crash!");
-        QuickExit();
+        ProcessChild::QuickExit();
     }
 
     if (!mHasShutdown) {
         MOZ_ASSERT(gChromeInstance == this);
         NP_Shutdown();
     }
 
     // doesn't matter why we're being destroyed; it's up to us to
--- a/dom/plugins/ipc/PluginModuleChild.h
+++ b/dom/plugins/ipc/PluginModuleChild.h
@@ -137,18 +137,16 @@ protected:
     virtual bool
     AnswerPCrashReporterConstructor(PCrashReporterChild* actor,
                                     mozilla::dom::NativeThreadId* id,
                                     uint32_t* processType) override;
 
     virtual void
     ActorDestroy(ActorDestroyReason why) override;
 
-    MOZ_NORETURN void QuickExit();
-
     virtual bool
     RecvProcessNativeEventsInInterruptCall() override;
 
     virtual bool RecvStartProfiler(const ProfilerInitParams& params) override;
     virtual bool RecvStopProfiler() override;
     virtual bool RecvGatherProfile() override;
 
 public:
--- a/dom/plugins/test/mochitest/chrome.ini
+++ b/dom/plugins/test/mochitest/chrome.ini
@@ -1,15 +1,14 @@
 [DEFAULT]
 skip-if = (buildapp == 'b2g' || buildapp == 'mulet')
 support-files =
   hang_test.js
   privatemode_perwindowpb.xul
   plugin-utils.js
-  !/toolkit/crashreporter/test/browser/crashreport.sjs
 
 [test_bug479979.xul]
 [test_bug751809.html]
 [test_busy_hang.xul]
 skip-if = (!crashreporter) || (os != "win")
 [test_clear_site_data.html]
 [test_convertpoint.xul]
 skip-if = toolkit != "cocoa"
--- a/dom/promise/PromiseDebugging.cpp
+++ b/dom/promise/PromiseDebugging.cpp
@@ -16,17 +16,17 @@
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseBinding.h"
 #include "mozilla/dom/PromiseDebugging.h"
 #include "mozilla/dom/PromiseDebuggingBinding.h"
 
 namespace mozilla {
 namespace dom {
 
-class FlushRejections: public nsCancelableRunnable
+class FlushRejections: public CancelableRunnable
 {
 public:
   static void Init() {
     if (!sDispatched.init()) {
       MOZ_CRASH("Could not initialize FlushRejections::sDispatched");
     }
     sDispatched.set(false);
   }
--- a/dom/tests/mochitest/general/mochitest.ini
+++ b/dom/tests/mochitest/general/mochitest.ini
@@ -46,16 +46,17 @@ support-files =
   storagePermissionsUtils.js
   frameSelectEvents.html
   !/image/test/mochitest/big.png
   !/image/test/mochitest/blue.png
   !/image/test/mochitest/clear.png
   !/image/test/mochitest/damon.jpg
   !/image/test/mochitest/over.png
   !/image/test/mochitest/red.png
+  !/dom/base/test/file_empty.html
 
 [test_497898.html]
 skip-if = ((buildapp == 'mulet' || buildapp == 'b2g') && toolkit != 'gonk') || toolkit == 'android' #Bug 931116, b2g desktop specific, initial triage
 [test_bug504220.html]
 [test_bug628069_1.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_bug628069_2.html]
 [test_bug631440.html]
@@ -130,8 +131,9 @@ skip-if = buildapp == 'b2g' # Bug 118442
 [test_storagePermissionsReject.html]
 skip-if = buildapp == 'b2g' # Bug 1184427 - no SSL certs on b2g
 [test_storagePermissionsLimitForeign.html]
 skip-if = buildapp == 'b2g' # Bug 1184427 - no SSL certs on b2g
 [test_selectevents.html]
 skip-if = buildapp == 'b2g' || buildapp == 'mulet' || toolkit == 'android' # Mouse doesn't select in the same way
 # Disabled on Android, see bug 1230232
 [test_WebKitCSSMatrix.html]
+[test_resource_timing_frameset.html]
--- a/dom/tests/mochitest/general/resource_timing_iframe.html
+++ b/dom/tests/mochitest/general/resource_timing_iframe.html
@@ -30,17 +30,17 @@ function doTest() {
   window.parent.ok(!window.performance.getEntriesByName(
     "http://mochi.test:8888/tests/dom/tests/mochitest/general/resource_timing_iframe.html").length,
     "This iframe should NOT contain itself as an entry");
 
   // Check that there are no iframes added as entries
   var resources = window.performance.getEntriesByType("resource");
   for (var i = 0 ; i < resources.length; i++) {
     var entry = resources[i];
-    if (entry.initiatorType === "subdocument") {
+    if (entry.initiatorType === "iframe") {
       ok(false, "unexpected iframe " + entry.name);
     }
   }
 
   window.parent.iframeTestsCompleted();
 }
 </script>
 <body onLoad="doTest()">
--- a/dom/tests/mochitest/general/resource_timing_main_test.html
+++ b/dom/tests/mochitest/general/resource_timing_main_test.html
@@ -183,17 +183,17 @@ function checkEntries(anEntryList) {
 
   // Check that the entries have the expected initiator type. We can't check
   // the order (the order might depend on the platform the tests are running).
   allResources = {
     "http://mochi.test:8888/tests/SimpleTest/test.css" : "link",
     "http://mochi.test:8888/tests/image/test/mochitest/blue.png" : "img",
     "http://mochi.test:8888/tests/image/test/mochitest/red.png" : "object",
     "http://mochi.test:8888/tests/image/test/mochitest/big.png" : "embed",
-    "http://mochi.test:8888/tests/dom/tests/mochitest/general/resource_timing_iframe.html" : "subdocument"};
+    "http://mochi.test:8888/tests/dom/tests/mochitest/general/resource_timing_iframe.html" : "iframe"};
 
   for (resourceName in allResources) {
     // Check that we have a resource with the specific name.
     namedEntries = window.performance.getEntriesByName(resourceName);
     ok (!!namedEntries && (namedEntries.length == 1),
       "An entry with the name '" + resourceName + "' should be available");
 
     // Double check for the entry name.
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/general/test_resource_timing_frameset.html
@@ -0,0 +1,29 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN">
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <title>browser_frametree_sample_frameset.html</title>
+    <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+    <script type="text/javascript">
+      SimpleTest.waitForExplicitFinish();
+      window.addEventListener("load", function() {
+      	var frameEntries = performance.getEntriesByName("http://mochi.test:8888/tests/dom/base/test/file_empty.html");
+
+      	is(frameEntries.length, 2, "correct number");
+      	is(frameEntries[0].initiatorType, "frame", "correct type");
+      	SimpleTest.finish();
+      });
+    </script>
+  </head>
+  <frameset id="frames" rows="50%, 50%">
+    <frame src="http://mochi.test:8888/tests/dom/base/test/file_empty.html">
+    <frame src="http://mochi.test:8888/tests/dom/base/test/file_empty.html">
+  </frameset>
+</html>
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -313,17 +313,18 @@ private:
 
   virtual bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override;
 
   virtual void
   PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult)
           override;
 
-  NS_DECL_NSICANCELABLERUNNABLE
+  nsresult
+  Cancel() override;
 
   void
   ShutdownScriptLoader(JSContext* aCx,
                        WorkerPrivate* aWorkerPrivate,
                        bool aResult,
                        bool aMutedError);
 
   void LogExceptionToConsole(JSContext* aCx,
@@ -1887,17 +1888,17 @@ ScriptExecutorRunnable::PostRun(JSContex
     // script and GetOrCreateGlobalScope() fails.  In that case we would have
     // returned false from WorkerRun, so assert that.
     MOZ_ASSERT_IF(!result && !mScriptLoader.mRv.Failed(),
                   !aRunResult);
     ShutdownScriptLoader(aCx, aWorkerPrivate, result, mutedError);
   }
 }
 
-NS_IMETHODIMP
+nsresult
 ScriptExecutorRunnable::Cancel()
 {
   if (mLastIndex == mScriptLoader.mLoadInfos.Length() - 1) {
     ShutdownScriptLoader(mWorkerPrivate->GetJSContext(), mWorkerPrivate,
                          false, false);
   }
   return MainThreadWorkerSyncRunnable::Cancel();
 }
--- a/dom/workers/ServiceWorkerPrivate.cpp
+++ b/dom/workers/ServiceWorkerPrivate.cpp
@@ -156,17 +156,17 @@ public:
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
   {
     aWorkerPrivate->AssertIsOnWorkerThread();
     Done(aWorkerPrivate->WorkerScriptExecutedSuccessfully());
 
     return true;
   }
 
-  NS_IMETHOD
+  nsresult
   Cancel() override
   {
     Done(false);
     return WorkerRunnable::Cancel();
   }
 
 private:
   void
@@ -373,17 +373,17 @@ public:
 
   bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
   {
     MOZ_ASSERT(aWorkerPrivate);
     return DispatchLifecycleEvent(aCx, aWorkerPrivate);
   }
 
-  NS_IMETHOD
+  nsresult
   Cancel() override
   {
     mCallback->SetResult(false);
     MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(mCallback));
 
     return WorkerRunnable::Cancel();
   }
 
@@ -840,17 +840,17 @@ private:
   PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
   {
     // Silence bad assertions.
   }
 
   bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override;
 
-  NS_IMETHOD
+  nsresult
   Cancel() override
   {
     // Always ensure the handler is released on the worker thread, even if we
     // are cancelled.
     mHandler = nullptr;
     return WorkerRunnable::Cancel();
   }
 
@@ -1259,17 +1259,17 @@ public:
 
   bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
   {
     MOZ_ASSERT(aWorkerPrivate);
     return DispatchFetchEvent(aCx, aWorkerPrivate);
   }
 
-  NS_IMETHOD
+  nsresult
   Cancel() override
   {
     nsCOMPtr<nsIRunnable> runnable = new ResumeRequest(mInterceptedChannel);
     if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
       NS_WARNING("Failed to resume channel on FetchEventRunnable::Cancel()!\n");
     }
     WorkerRunnable::Cancel();
     return NS_OK;
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -208,21 +208,21 @@ SwapToISupportsArray(SmartPtr<T>& aSrc,
   dest->swap(rawSupports);
 }
 
 // This class is used to wrap any runnables that the worker receives via the
 // nsIEventTarget::Dispatch() method (either from NS_DispatchToCurrentThread or
 // from the worker's EventTarget).
 class ExternalRunnableWrapper final : public WorkerRunnable
 {
-  nsCOMPtr<nsICancelableRunnable> mWrappedRunnable;
+  nsCOMPtr<nsIRunnable> mWrappedRunnable;
 
 public:
   ExternalRunnableWrapper(WorkerPrivate* aWorkerPrivate,
-                          nsICancelableRunnable* aWrappedRunnable)
+                          nsIRunnable* aWrappedRunnable)
   : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
     mWrappedRunnable(aWrappedRunnable)
   {
     MOZ_ASSERT(aWorkerPrivate);
     MOZ_ASSERT(aWrappedRunnable);
   }
 
   NS_DECL_ISUPPORTS_INHERITED
@@ -239,20 +239,24 @@ private:
       if (!JS_IsExceptionPending(aCx)) {
         Throw(aCx, rv);
       }
       return false;
     }
     return true;
   }
 
-  NS_IMETHOD
+  nsresult
   Cancel() override
   {
-    nsresult rv = mWrappedRunnable->Cancel();
+    nsresult rv;
+    nsCOMPtr<nsICancelableRunnable> cancelable =
+      do_QueryInterface(mWrappedRunnable);
+    MOZ_ASSERT(cancelable); // We checked this earlier!
+    rv = cancelable->Cancel();
     nsresult rv2 = WorkerRunnable::Cancel();
     return NS_FAILED(rv) ? rv : rv2;
   }
 };
 
 struct WindowAction
 {
   nsPIDOMWindowInner* mWindow;
@@ -619,17 +623,17 @@ private:
     event->InitEvent(NS_LITERAL_STRING("close"), false, false);
     event->SetTrusted(true);
 
     globalScope->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
 
     return true;
   }
 
-  NS_IMETHOD Cancel() override
+  nsresult Cancel() override
   {
     // We need to run regardless.
     Run();
     return WorkerRunnable::Cancel();
   }
 
   virtual void
   PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult)
@@ -1340,17 +1344,17 @@ private:
     if (mTimer) {
       mTimer->Cancel();
       mTimer = nullptr;
     }
 
     return true;
   }
 
-  NS_IMETHOD Cancel() override
+  nsresult Cancel() override
   {
     // We need to run regardless.
     Run();
     return WorkerRunnable::Cancel();
   }
 };
 
 class UpdateRuntimeOptionsRunnable final : public WorkerControlRunnable
@@ -1650,17 +1654,17 @@ private:
   { }
 
   virtual bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
   {
     return aWorkerPrivate->ConnectMessagePort(aCx, mPortIdentifier);
   }
 
-  NS_IMETHOD
+  nsresult
   Cancel() override
   {
     MessagePort::ForceClose(mPortIdentifier);
     return WorkerRunnable::Cancel();
   }
 };
 
 class DummyRunnable final
@@ -2467,17 +2471,17 @@ WorkerPrivateParent<Derived>::MaybeWrapA
   }
 
   nsCOMPtr<nsICancelableRunnable> cancelable = do_QueryInterface(runnable);
   if (!cancelable) {
     MOZ_CRASH("All runnables destined for a worker thread must be cancelable!");
   }
 
   workerRunnable =
-    new ExternalRunnableWrapper(ParentAsWorkerPrivate(), cancelable);
+    new ExternalRunnableWrapper(ParentAsWorkerPrivate(), runnable);
   return workerRunnable.forget();
 }
 
 template <class Derived>
 already_AddRefed<nsIEventTarget>
 WorkerPrivateParent<Derived>::GetEventTarget()
 {
   WorkerPrivate* self = ParentAsWorkerPrivate();
--- a/dom/workers/WorkerRunnable.cpp
+++ b/dom/workers/WorkerRunnable.cpp
@@ -210,17 +210,17 @@ WorkerRunnable::FromRunnable(nsIRunnable
 }
 
 NS_IMPL_ADDREF(WorkerRunnable)
 NS_IMPL_RELEASE(WorkerRunnable)
 
 NS_INTERFACE_MAP_BEGIN(WorkerRunnable)
   NS_INTERFACE_MAP_ENTRY(nsIRunnable)
   NS_INTERFACE_MAP_ENTRY(nsICancelableRunnable)
-  NS_INTERFACE_MAP_ENTRY(nsISupports)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRunnable)
   // kWorkerRunnableIID is special in that it does not AddRef its result.
   if (aIID.Equals(kWorkerRunnableIID)) {
     *aInstancePtr = this;
     return NS_OK;
   }
   else
 NS_INTERFACE_MAP_END
 
@@ -397,17 +397,17 @@ WorkerRunnable::Run()
   // (CompileScriptRunnable) it actually doesn't matter which compartment we're
   // in for PostRun.
   PostRun(cx, mWorkerPrivate, result);
   MOZ_ASSERT(!jsapi->HasException());
 
   return result ? NS_OK : NS_ERROR_FAILURE;
 }
 
-NS_IMETHODIMP
+nsresult
 WorkerRunnable::Cancel()
 {
   uint32_t canceledCount = ++mCanceled;
 
   MOZ_ASSERT(canceledCount, "Cancel() overflow!");
 
   // The docs say that Cancel() should not be called more than once and that we
   // should throw NS_ERROR_UNEXPECTED if it is.
@@ -472,17 +472,17 @@ StopSyncLoopRunnable::StopSyncLoopRunnab
                                bool aResult)
 : WorkerSyncRunnable(aWorkerPrivate, Move(aSyncLoopTarget)), mResult(aResult)
 {
 #ifdef DEBUG
   mWorkerPrivate->AssertValidSyncLoop(mSyncLoopTarget);
 #endif
 }
 
-NS_IMETHODIMP
+nsresult
 StopSyncLoopRunnable::Cancel()
 {
   nsresult rv = Run();
   NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Run() failed");
 
   nsresult rv2 = WorkerSyncRunnable::Cancel();
   NS_WARN_IF_FALSE(NS_SUCCEEDED(rv2), "Cancel() failed");
 
@@ -529,17 +529,17 @@ WorkerControlRunnable::WorkerControlRunn
 {
   MOZ_ASSERT(aWorkerPrivate);
   MOZ_ASSERT(aBehavior == ParentThreadUnchangedBusyCount ||
              aBehavior == WorkerThreadUnchangedBusyCount,
              "WorkerControlRunnables should not modify the busy count");
 }
 #endif
 
-NS_IMETHODIMP
+nsresult
 WorkerControlRunnable::Cancel()
 {
   if (NS_FAILED(Run())) {
     NS_WARNING("WorkerControlRunnable::Run() failed.");
   }
 
   return WorkerRunnable::Cancel();
 }
--- a/dom/workers/WorkerRunnable.h
+++ b/dom/workers/WorkerRunnable.h
@@ -24,17 +24,18 @@ class ErrorResult;
 
 BEGIN_WORKERS_NAMESPACE
 
 class WorkerPrivate;
 
 // Use this runnable to communicate from the worker to its parent or vice-versa.
 // The busy count must be taken into consideration and declared at construction
 // time.
-class WorkerRunnable : public nsICancelableRunnable
+class WorkerRunnable : public nsIRunnable,
+                       public nsICancelableRunnable
 {
 public:
   enum TargetAndBusyBehavior {
     // Target the main thread for top-level workers, otherwise target the
     // WorkerThread of the worker's parent. No change to the busy count.
     ParentThreadUnchangedBusyCount,
 
     // Target the thread where the worker event loop runs. The busy count will
@@ -66,17 +67,18 @@ private:
   bool mCallingCancelWithinRun;
 
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
 
   // If you override Cancel() then you'll need to either call the base class
   // Cancel() method or override IsCanceled() so that the Run() method bails out
   // appropriately.
-  NS_DECL_NSICANCELABLERUNNABLE
+  nsresult
+  Cancel() override;
 
   // The return value is true if and only if both PreDispatch and
   // DispatchInternal return true.
   bool
   Dispatch();
 
   // See above note about Cancel().
   virtual bool
@@ -272,17 +274,18 @@ class StopSyncLoopRunnable : public Work
 public:
   // Passing null for aSyncLoopTarget is not allowed.
   StopSyncLoopRunnable(WorkerPrivate* aWorkerPrivate,
                        already_AddRefed<nsIEventTarget>&& aSyncLoopTarget,
                        bool aResult);
 
   // By default StopSyncLoopRunnables cannot be canceled since they could leave
   // a sync loop spinning forever.
-  NS_DECL_NSICANCELABLERUNNABLE
+  nsresult
+  Cancel() override;
 
 protected:
   virtual ~StopSyncLoopRunnable()
   { }
 
   // Called on the worker thread, in WorkerRun, right before stopping the
   // syncloop to set an exception (however subclasses want to handle that) if
   // mResult is false.  Note that overrides of this method must NOT set an
@@ -349,17 +352,17 @@ protected:
 #else
   : WorkerRunnable(aWorkerPrivate, aBehavior)
   { }
 #endif
 
   virtual ~WorkerControlRunnable()
   { }
 
-  NS_IMETHOD
+  nsresult
   Cancel() override;
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
 private:
   virtual bool
   DispatchInternal() override;
--- a/dom/workers/XMLHttpRequest.cpp
+++ b/dom/workers/XMLHttpRequest.cpp
@@ -494,17 +494,17 @@ class LoadStartDetectionRunnable final :
 
       if (mXMLHttpRequestPrivate->SendInProgress()) {
         mXMLHttpRequestPrivate->Unpin();
       }
 
       return true;
     }
 
-    NS_IMETHOD
+    nsresult
     Cancel() override
     {
       // This must run!
       nsresult rv = MainThreadProxyRunnable::Cancel();
       nsresult rv2 = Run();
       return NS_FAILED(rv) ? rv : rv2;
     }
   };
--- a/dom/workers/test/sharedWorker_sharedWorker.js
+++ b/dom/workers/test/sharedWorker_sharedWorker.js
@@ -67,20 +67,20 @@ onconnect = function(event) {
   }
   if (!(event.ports[0] == event.source)) {
     throw new Error("'connect' event source property is incorrect!");
   }
   if (event.data) {
     throw new Error("'connect' event has data: " + event.data);
   }
 
-  // "".contains("") should trigger a warning in debug builds, but NOT fire
-  // error events at us.  If we ever actually remove contains() we'll need
-  // something else to test this case.
-  "".contains("");
+  // The expression closures should trigger a warning in debug builds, but NOT
+  // fire error events at us. If we ever actually remove expression closures
+  // (in bug 1083458), we'll need something else to test this case.
+  (function() "Expected console warning: expression closures are deprecated");
 
   event.ports[0].onmessage = function(event) {
     if (!(event instanceof MessageEvent)) {
       throw new Error("'message' event is not a MessageEvent!");
     }
     if (!("ports" in event)) {
       throw new Error("'message' event doesn't have a 'ports' property!");
     }
--- a/dom/xbl/nsXBLBinding.cpp
+++ b/dom/xbl/nsXBLBinding.cpp
@@ -81,25 +81,29 @@ XBLEnumerate(JSContext *cx, JS::Handle<J
 {
   nsXBLPrototypeBinding* protoBinding =
     static_cast<nsXBLPrototypeBinding*>(::JS_GetReservedSlot(obj, 0).toPrivate());
   MOZ_ASSERT(protoBinding);
 
   return protoBinding->ResolveAllFields(cx, obj);
 }
 
+static const JSClassOps gPrototypeJSClassOps = {
+    nullptr, nullptr, nullptr, nullptr,
+    XBLEnumerate, nullptr,
+    nullptr, XBLFinalize,
+    nullptr, nullptr, nullptr, nullptr
+};
+
 static const JSClass gPrototypeJSClass = {
     "XBL prototype JSClass",
     JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS |
     // Our one reserved slot holds the relevant nsXBLPrototypeBinding
     JSCLASS_HAS_RESERVED_SLOTS(1),
-    nullptr, nullptr, nullptr, nullptr,
-    XBLEnumerate, nullptr,
-    nullptr, XBLFinalize,
-    nullptr, nullptr, nullptr, nullptr
+    &gPrototypeJSClassOps
 };
 
 // Implementation /////////////////////////////////////////////////////////////////
 
 // Constructors/Destructors
 nsXBLBinding::nsXBLBinding(nsXBLPrototypeBinding* aBinding)
   : mMarkedForDeath(false)
   , mUsingContentXBLScope(false)
--- a/gfx/2d/Logging.h
+++ b/gfx/2d/Logging.h
@@ -131,16 +131,17 @@ enum class LogReason : int {
   InvalidRect,
   CannotDraw3D, // 20
   IncompatibleBasicTexturedEffect,
   InvalidFont,
   PAllocTextureBackendMismatch,
   GetFontFileDataFailed,
   MessageChannelCloseFailure,
   TextureAliveAfterShutdown,
+  InvalidContext,
   // End
   MustBeLessThanThis = 101,
 };
 
 struct BasicLogger
 {
   // For efficiency, this method exists and copies the logic of the
   // OutputMessage below.  If making any changes here, also make it
--- a/gfx/layers/apz/src/InputBlockState.cpp
+++ b/gfx/layers/apz/src/InputBlockState.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "InputBlockState.h"
 #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"
 
 #define TBS_LOG(...)
 // #define TBS_LOG(...) printf_stderr("TBS: " __VA_ARGS__)
 
 namespace mozilla {
 namespace layers {
@@ -148,16 +149,23 @@ CancelableBlockState::SetContentResponse
   }
   TBS_LOG("%p got content response %d with timer expired %d\n",
     this, aPreventDefault, mContentResponseTimerExpired);
   mPreventDefault = aPreventDefault;
   mContentResponded = true;
   return true;
 }
 
+void
+CancelableBlockState::StartContentResponseTimer()
+{
+  MOZ_ASSERT(mContentResponseTimer.IsNull());
+  mContentResponseTimer = TimeStamp::Now();
+}
+
 bool
 CancelableBlockState::TimeoutContentResponse()
 {
   if (mContentResponseTimerExpired) {
     return false;
   }
   TBS_LOG("%p got content timer expired with response received %d\n",
     this, mContentResponded);
@@ -177,16 +185,22 @@ CancelableBlockState::IsContentResponseT
 bool
 CancelableBlockState::IsDefaultPrevented() const
 {
   MOZ_ASSERT(mContentResponded || mContentResponseTimerExpired);
   return mPreventDefault;
 }
 
 bool
+CancelableBlockState::HasReceivedAllContentNotifications() const
+{
+  return IsTargetConfirmed() && mContentResponded;
+}
+
+bool
 CancelableBlockState::IsReadyForHandling() const
 {
   if (!IsTargetConfirmed()) {
     return false;
   }
   return mContentResponded || mContentResponseTimerExpired;
 }
 
@@ -199,16 +213,36 @@ CancelableBlockState::DispatchImmediate(
 }
 
 void
 CancelableBlockState::DispatchEvent(const InputData& aEvent) const
 {
   GetTargetApzc()->HandleInputEvent(aEvent, mTransformToApzc);
 }
 
+void
+CancelableBlockState::RecordContentResponseTime()
+{
+  if (!mContentResponseTimer) {
+    // We might get responses from content even though we didn't wait for them.
+    // In that case, ignore the time on them, because they're not relevant for
+    // tuning our timeout value. Also this function might get called multiple
+    // times on the same input block, so we should only record the time from the
+    // first successful call.
+    return;
+  }
+  if (!HasReceivedAllContentNotifications()) {
+    // Not done yet, we'll get called again
+    return;
+  }
+  mozilla::Telemetry::Accumulate(mozilla::Telemetry::CONTENT_RESPONSE_DURATION,
+    (uint32_t)(TimeStamp::Now() - mContentResponseTimer).ToMilliseconds());
+  mContentResponseTimer = TimeStamp();
+}
+
 DragBlockState::DragBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
                                bool aTargetConfirmed,
                                const MouseInput& aInitialEvent)
   : CancelableBlockState(aTargetApzc, aTargetConfirmed)
   , mReceivedMouseUp(false)
 {
 }
 
@@ -381,25 +415,16 @@ WheelBlockState::Update(ScrollWheelInput
 
 void
 WheelBlockState::AddEvent(const ScrollWheelInput& aEvent)
 {
   mEvents.AppendElement(aEvent);
 }
 
 bool
-WheelBlockState::IsReadyForHandling() const
-{
-  if (!CancelableBlockState::IsReadyForHandling()) {
-    return false;
-  }
-  return true;
-}
-
-bool
 WheelBlockState::HasEvents() const
 {
   return !mEvents.IsEmpty();
 }
 
 void
 WheelBlockState::DropEvents()
 {
@@ -650,16 +675,23 @@ PanGestureBlockState::SetContentResponse
   if (mWaitingForContentResponse) {
     mWaitingForContentResponse = false;
     stateChanged = true;
   }
   return stateChanged;
 }
 
 bool
+PanGestureBlockState::HasReceivedAllContentNotifications() const
+{
+  return CancelableBlockState::HasReceivedAllContentNotifications()
+      && !mWaitingForContentResponse;
+}
+
+bool
 PanGestureBlockState::IsReadyForHandling() const
 {
   if (!CancelableBlockState::IsReadyForHandling()) {
     return false;
   }
   return !mWaitingForContentResponse ||
          IsContentResponseTimerExpired();
 }
@@ -717,16 +749,23 @@ TouchBlockState::CopyPropertiesFrom(cons
   if (gfxPrefs::TouchActionEnabled()) {
     MOZ_ASSERT(aOther.mAllowedTouchBehaviorSet || aOther.IsContentResponseTimerExpired());
     SetAllowedTouchBehaviors(aOther.mAllowedTouchBehaviors);
   }
   mTransformToApzc = aOther.mTransformToApzc;
 }
 
 bool
+TouchBlockState::HasReceivedAllContentNotifications() const
+{
+  return CancelableBlockState::HasReceivedAllContentNotifications()
+      && (!gfxPrefs::TouchActionEnabled() || mAllowedTouchBehaviorSet);
+}
+
+bool
 TouchBlockState::IsReadyForHandling() const
 {
   if (!CancelableBlockState::IsReadyForHandling()) {
     return false;
   }
 
   if (!gfxPrefs::TouchActionEnabled()) {
     return true;
--- a/gfx/layers/apz/src/InputBlockState.h
+++ b/gfx/layers/apz/src/InputBlockState.h
@@ -6,16 +6,17 @@
 
 #ifndef mozilla_layers_InputBlockState_h
 #define mozilla_layers_InputBlockState_h
 
 #include "InputData.h"                      // for MultiTouchInput
 #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 "nsAutoPtr.h"                      // for nsRefPtr
 #include "nsTArray.h"                       // for nsTArray
 #include "TouchCounter.h"
 
 namespace mozilla {
 namespace layers {
 
 class AsyncPanZoomController;
@@ -117,16 +118,30 @@ public:
    * Record whether or not content cancelled this block of events.
    * @param aPreventDefault true iff the block is cancelled.
    * @return false if this block has already received a response from
    *         web content, true if not.
    */
   virtual bool SetContentResponse(bool aPreventDefault);
 
   /**
+   * This should be called when this block is starting to wait for the
+   * necessary content response notifications. It is used to gather data
+   * on how long the content response notifications take.
+   */
+  void StartContentResponseTimer();
+
+  /**
+   * This should be called when a content response notification has been
+   * delivered to this block. If all the notifications have arrived, this
+   * will report the total time take to telemetry.
+   */
+  void RecordContentResponseTime();
+
+  /**
    * Record that content didn't respond in time.
    * @return false if this block already timed out, true if not.
    */
   bool TimeoutContentResponse();
 
   /**
    * Checks if the content response timer has already expired.
    */
@@ -146,16 +161,22 @@ public:
 
   /**
    * 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.
+   */
+  virtual bool HasReceivedAllContentNotifications() const;
+
+  /**
    * @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.
    */
@@ -179,33 +200,33 @@ public:
   virtual bool MustStayActive() = 0;
 
   /**
    * Return a descriptive name for the block kind.
    */
   virtual const char* Type() = 0;
 
 private:
+  TimeStamp mContentResponseTimer;
   bool mPreventDefault;
   bool mContentResponded;
   bool mContentResponseTimerExpired;
 };
 
 /**
  * A single block of wheel events.
  */
 class WheelBlockState : public CancelableBlockState
 {
 public:
   WheelBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
                   bool aTargetConfirmed,
                   const ScrollWheelInput& aEvent);
 
   bool SetContentResponse(bool aPreventDefault) 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) override;
 
   void AddEvent(const ScrollWheelInput& aEvent);
@@ -315,16 +336,17 @@ private:
 class PanGestureBlockState : public CancelableBlockState
 {
 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) override;
 
@@ -393,16 +415,22 @@ public:
    */
   bool GetAllowedTouchBehaviors(nsTArray<TouchBehaviorFlags>& aOutBehaviors) const;
 
   /**
    * Copy various properties from another block.
    */
   void CopyPropertiesFrom(const TouchBlockState& aOther);
 
+  /*
+   * @return true iff this block has received all the information it could
+   *         have gotten from the content thread.
+   */
+  bool HasReceivedAllContentNotifications() const override;
+
   /**
    * @return true iff this block has received all the information needed
    *         to properly dispatch the events in the block.
    */
   bool IsReadyForHandling() const override;
 
   /**
    * Sets a flag that indicates this input block occurred while the APZ was
--- a/gfx/layers/apz/src/InputQueue.cpp
+++ b/gfx/layers/apz/src/InputQueue.cpp
@@ -421,29 +421,29 @@ InputQueue::MaybeRequestContentResponse(
     // block.
     waitForMainThread = true;
   }
   if (waitForMainThread) {
     // We either don't know for sure if aTarget is the right APZC, or we may
     // need to wait to give content the opportunity to prevent-default the
     // touch events. Either way we schedule a timeout so the main thread stuff
     // can run.
-    ScheduleMainThreadTimeout(aTarget, aBlock->GetBlockId());
+    ScheduleMainThreadTimeout(aTarget, aBlock);
   }
 }
 
 uint64_t
 InputQueue::InjectNewTouchBlock(AsyncPanZoomController* aTarget)
 {
   TouchBlockState* block = StartNewTouchBlock(aTarget,
     /* 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->GetBlockId());
+  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.
@@ -560,20 +560,22 @@ InputQueue::IsDragOnScrollbar(bool aHitS
   // Now that we know we are in a drag, get the info from the drag tracker.
   // We keep it in the tracker rather than the block because the block can get
   // interrupted by something else (like a wheel event) and then a new block
   // will get created without the info we want. The tracker will persist though.
   return mDragTracker.IsOnScrollbar(aHitScrollbar);
 }
 
 void
-InputQueue::ScheduleMainThreadTimeout(const RefPtr<AsyncPanZoomController>& aTarget, uint64_t aInputBlockId) {
+InputQueue::ScheduleMainThreadTimeout(const RefPtr<AsyncPanZoomController>& aTarget,
+                                      CancelableBlockState* aBlock) {
   INPQ_LOG("scheduling main thread timeout for target %p\n", aTarget.get());
+  aBlock->StartContentResponseTimer();
   aTarget->PostDelayedTask(
-    NewRunnableMethod(this, &InputQueue::MainThreadTimeout, aInputBlockId),
+    NewRunnableMethod(this, &InputQueue::MainThreadTimeout, aBlock->GetBlockId()),
     gfxPrefs::APZContentResponseTimeout());
 }
 
 void
 InputQueue::MainThreadTimeout(const uint64_t& aInputBlockId) {
   APZThreadUtils::AssertOnControllerThread();
 
   INPQ_LOG("got a main thread timeout; block=%" PRIu64 "\n", aInputBlockId);
@@ -595,37 +597,40 @@ InputQueue::MainThreadTimeout(const uint
 
 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++) {
-    if (mInputBlockQueue[i]->GetBlockId() == aInputBlockId) {
-      CancelableBlockState* block = mInputBlockQueue[i].get();
+    CancelableBlockState* block = mInputBlockQueue[i].get();
+    if (block->GetBlockId() == aInputBlockId) {
       success = block->SetContentResponse(aPreventDefault);
+      block->RecordContentResponseTime();
       break;
     }
   }
   if (success) {
     ProcessInputBlocks();
   }
 }
 
 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++) {
-    if (mInputBlockQueue[i]->GetBlockId() == aInputBlockId) {
-      success = mInputBlockQueue[i]->SetConfirmedTargetApzc(aTargetApzc);
+    CancelableBlockState* block = mInputBlockQueue[i].get();
+    if (block->GetBlockId() == aInputBlockId) {
+      success = block->SetConfirmedTargetApzc(aTargetApzc);
+      block->RecordContentResponseTime();
       break;
     }
   }
   if (success) {
     ProcessInputBlocks();
   }
 }
 
@@ -638,16 +643,17 @@ InputQueue::ConfirmDragBlock(uint64_t aI
   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);
+      block->RecordContentResponseTime();
       break;
     }
   }
   if (success) {
     ProcessInputBlocks();
   }
 }
 
@@ -657,16 +663,17 @@ InputQueue::SetAllowedTouchBehavior(uint
 
   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;
     }
   }
   if (success) {
     ProcessInputBlocks();
--- a/gfx/layers/apz/src/InputQueue.h
+++ b/gfx/layers/apz/src/InputQueue.h
@@ -170,17 +170,18 @@ private:
 
   /**
    * Processes the current block if it's ready for handling, using the block's
    * target APZC.
    */
   bool MaybeHandleCurrentBlock(CancelableBlockState* block,
                                const InputData& aEvent);
 
-  void ScheduleMainThreadTimeout(const RefPtr<AsyncPanZoomController>& aTarget, uint64_t aInputBlockId);
+  void ScheduleMainThreadTimeout(const RefPtr<AsyncPanZoomController>& aTarget,
+                                 CancelableBlockState* aBlock);
   void MainThreadTimeout(const uint64_t& aInputBlockId);
   void ProcessInputBlocks();
   void UpdateActiveApzc(const RefPtr<AsyncPanZoomController>& aNewActive);
 
 private:
   // The queue of input blocks that have not yet been fully processed.
   // This member must only be accessed on the controller/UI thread.
   nsTArray<UniquePtr<CancelableBlockState>> mInputBlockQueue;
--- a/gfx/layers/apz/util/APZCCallbackHelper.cpp
+++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp
@@ -251,17 +251,16 @@ APZCCallbackHelper::UpdateRootFrame(Fram
                         * aMetrics.GetAsyncZoom().scale;
     shell->SetResolutionAndScaleTo(presShellResolution);
   }
 
   // Do this as late as possible since scrolling can flush layout. It also
   // adjusts the display port margins, so do it before we set those.
   ScrollFrame(content, aMetrics);
 
-  MOZ_ASSERT(nsLayoutUtils::HasDisplayPort(content));
   SetDisplayPortMargins(shell, content, aMetrics);
   SetPaintRequestTime(content, aMetrics.GetPaintRequestTime());
 }
 
 void
 APZCCallbackHelper::UpdateSubFrame(FrameMetrics& aMetrics)
 {
   if (aMetrics.GetScrollId() == FrameMetrics::NULL_SCROLL_ID) {
--- a/gfx/layers/basic/BasicLayerManager.cpp
+++ b/gfx/layers/basic/BasicLayerManager.cpp
@@ -102,17 +102,21 @@ BasicLayerManager::PushGroupForLayer(gfx
     aContext->SetMatrix(oldMat);
     rect.RoundOut();
     IntRect surfRect;
     ToRect(rect).ToIntRect(&surfRect);
 
     if (!surfRect.IsEmpty()) {
       RefPtr<DrawTarget> dt = aContext->GetDrawTarget()->CreateSimilarDrawTarget(surfRect.Size(), SurfaceFormat::B8G8R8A8);
 
-      RefPtr<gfxContext> ctx = new gfxContext(dt, ToRect(rect).TopLeft());
+      RefPtr<gfxContext> ctx = gfxContext::ForDrawTarget(dt, ToRect(rect).TopLeft());
+      if (!ctx) {
+        gfxDevCrash(LogReason::InvalidContext) << "BasicLayerManager context problem " << gfx::hexa(dt);
+        return group;
+      }
       ctx->SetMatrix(oldMat);
 
       group.mGroupOffset = surfRect.TopLeft();
       group.mGroupTarget = ctx;
 
       group.mMaskSurface = GetMaskForLayer(aLayer, &group.mMaskTransform);
       return group;
     }
@@ -892,36 +896,39 @@ BasicLayerManager::PaintLayer(gfxContext
       PaintSelfOrChildren(paintLayerContext, aTarget);
       return;
     }
 
     const IntRect& bounds = visibleRegion.GetBounds();
     RefPtr<DrawTarget> untransformedDT =
       gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(IntSize(bounds.width, bounds.height),
                                                                    SurfaceFormat::B8G8R8A8);
-    if (!untransformedDT) {
+    if (!untransformedDT || !untransformedDT->IsValid()) {
       return;
     }
 
-    RefPtr<gfxContext> groupTarget = new gfxContext(untransformedDT,
-                                                      Point(bounds.x, bounds.y));
+    RefPtr<gfxContext> groupTarget = gfxContext::ForDrawTarget(untransformedDT,
+                                                               Point(bounds.x, bounds.y));
+    MOZ_ASSERT(groupTarget); // already checked the target above
 
     PaintSelfOrChildren(paintLayerContext, groupTarget);
 
     // Temporary fast fix for bug 725886
     // Revert these changes when 725886 is ready
-    MOZ_ASSERT(untransformedDT,
+    MOZ_ASSERT(untransformedDT && untransformedDT->IsValid(),
                "We should always allocate an untransformed surface with 3d transforms!");
 #ifdef DEBUG
     if (aLayer->GetDebugColorIndex() != 0) {
       Color color((aLayer->GetDebugColorIndex() & 1) ? 1.f : 0.f,
                   (aLayer->GetDebugColorIndex() & 2) ? 1.f : 0.f,
                   (aLayer->GetDebugColorIndex() & 4) ? 1.f : 0.f);
 
-      RefPtr<gfxContext> temp = new gfxContext(untransformedDT, Point(bounds.x, bounds.y));
+      RefPtr<gfxContext> temp = gfxContext::ForDrawTarget(untransformedDT,
+                                                          Point(bounds.x, bounds.y));
+      MOZ_ASSERT(temp); // already checked for target above
       temp->SetColor(color);
       temp->Paint();
     }
 #endif
     Matrix4x4 effectiveTransform = aLayer->GetEffectiveTransform();
     Rect xformBounds =
       effectiveTransform.TransformAndClipBounds(Rect(bounds),
                                                 ToRect(aTarget->GetClipExtents()));
--- a/gfx/layers/basic/BasicPaintedLayer.cpp
+++ b/gfx/layers/basic/BasicPaintedLayer.cpp
@@ -169,17 +169,19 @@ BasicPaintedLayer::Validate(LayerManager
     // from RGB to RGBA, because we might need to repaint with
     // subpixel AA)
     state.mRegionToInvalidate.And(state.mRegionToInvalidate,
                                   GetLocalVisibleRegion().ToUnknownRegion());
     SetAntialiasingFlags(this, target);
 
     RenderTraceInvalidateStart(this, "FFFF00", state.mRegionToDraw.GetBounds());
 
-    RefPtr<gfxContext> ctx = gfxContext::ContextForDrawTarget(target);
+    RefPtr<gfxContext> ctx = gfxContext::ForDrawTargetWithTransform(target);
+    MOZ_ASSERT(ctx); // already checked the target above
+
     PaintBuffer(ctx,
                 state.mRegionToDraw, state.mRegionToDraw, state.mRegionToInvalidate,
                 state.mDidSelfCopy,
                 state.mClip,
                 aCallback, aCallbackData);
     MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) PaintThebes", this));
     Mutated();
     ctx = nullptr;
--- a/gfx/layers/client/ClientPaintedLayer.cpp
+++ b/gfx/layers/client/ClientPaintedLayer.cpp
@@ -84,17 +84,18 @@ ClientPaintedLayer::PaintThebes()
       if (target) {
         mContentClient->ReturnDrawTargetToBuffer(target);
       }
       continue;
     }
     
     SetAntialiasingFlags(this, target);
 
-    RefPtr<gfxContext> ctx = gfxContext::ContextForDrawTarget(target);
+    RefPtr<gfxContext> ctx = gfxContext::ForDrawTargetWithTransform(target);
+    MOZ_ASSERT(ctx); // already checked the target above
 
     ClientManager()->GetPaintedLayerCallback()(this,
                                               ctx,
                                               iter.mDrawRegion,
                                               iter.mDrawRegion,
                                               state.mClip,
                                               state.mRegionToInvalidate,
                                               ClientManager()->GetPaintedLayerCallbackData());
--- a/gfx/layers/client/SingleTiledContentClient.cpp
+++ b/gfx/layers/client/SingleTiledContentClient.cpp
@@ -180,17 +180,21 @@ ClientSingleTiledLayerBuffer::PaintThebe
   }
 
   if (dtOnWhite) {
     dt = gfx::Factory::CreateDualDrawTarget(dt, dtOnWhite);
     dtOnWhite = nullptr;
   }
 
   {
-    RefPtr<gfxContext> ctx = new gfxContext(dt);
+    RefPtr<gfxContext> ctx = gfxContext::ForDrawTarget(dt);
+    if (!ctx) {
+      gfxDevCrash(LogReason::InvalidContext) << "SingleTiledContextClient context problem " << gfx::hexa(dt);
+      return;
+    }
     ctx->SetMatrix(ctx->CurrentMatrix().Translate(-mTilingOrigin.x, -mTilingOrigin.y));
 
     aCallback(mPaintedLayer, ctx, paintRegion, paintRegion, DrawRegionClip::DRAW, nsIntRegion(), aCallbackData);
   }
 
   // Mark the area we just drew into the back buffer as invalid in the front buffer as they're
   // now out of sync.
   mTile.mInvalidFront.OrWith(tileDirtyRegion);
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -928,21 +928,22 @@ ClientMultiTiledLayerBuffer::PaintThebes
 
         mSinglePaintDrawTarget =
           gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
             gfx::IntSize(ceilf(bounds.width * mResolution),
                          ceilf(bounds.height * mResolution)),
             gfxPlatform::GetPlatform()->Optimal2DFormatForContent(
               GetContentType()));
 
-        if (!mSinglePaintDrawTarget) {
+        if (!mSinglePaintDrawTarget || !mSinglePaintDrawTarget->IsValid()) {
           return;
         }
 
-        ctxt = new gfxContext(mSinglePaintDrawTarget);
+        ctxt = gfxContext::ForDrawTarget(mSinglePaintDrawTarget);
+        MOZ_ASSERT(ctxt); // already checked draw target above
 
         mSinglePaintBufferOffset = nsIntPoint(bounds.x, bounds.y);
       }
       ctxt->NewPath();
       ctxt->SetMatrix(
         ctxt->CurrentMatrix().Scale(mResolution, mResolution).
                               Translate(-bounds.x, -bounds.y));
 #ifdef GFX_TILEDLAYER_PREF_WARNINGS
@@ -1153,19 +1154,24 @@ void ClientMultiTiledLayerBuffer::Update
     if (gfxPrefs::TiledDrawTargetEnabled() && mMoz2DTiles.size() > 0) {
       gfx::TileSet tileset;
       for (size_t i = 0; i < mMoz2DTiles.size(); ++i) {
         mMoz2DTiles[i].mTileOrigin -= mTilingOrigin;
       }
       tileset.mTiles = &mMoz2DTiles[0];
       tileset.mTileCount = mMoz2DTiles.size();
       RefPtr<DrawTarget> drawTarget = gfx::Factory::CreateTiledDrawTarget(tileset);
+      if (!drawTarget || !drawTarget->IsValid()) {
+        gfxDevCrash(LogReason::InvalidContext) << "Invalid tiled draw target";
+        return;
+      }
       drawTarget->SetTransform(Matrix());
 
-      RefPtr<gfxContext> ctx = new gfxContext(drawTarget);
+      RefPtr<gfxContext> ctx = gfxContext::ForDrawTarget(drawTarget);
+      MOZ_ASSERT(ctx); // already checked the draw target above
       ctx->SetMatrix(
         ctx->CurrentMatrix().Scale(mResolution, mResolution).Translate(ThebesPoint(-mTilingOrigin)));
 
       mCallback(mPaintedLayer, ctx, aPaintRegion, aDirtyRegion,
                 DrawRegionClip::DRAW, nsIntRegion(), mCallbackData);
       mMoz2DTiles.clear();
       // Reset:
       mTilingOrigin = IntPoint(std::numeric_limits<int32_t>::max(),
--- a/gfx/layers/ipc/ImageBridgeChild.cpp
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -620,41 +620,59 @@ void ImageBridgeChild::UpdateAsyncCanvas
 {
   MOZ_ASSERT(aWrapper);
   sImageBridgeChildSingleton->BeginTransaction();
   aWrapper->GetCanvasClient()->Updated();
   sImageBridgeChildSingleton->EndTransaction();
 }
 
 static void FlushAllImagesSync(ImageClient* aClient, ImageContainer* aContainer,
-                               RefPtr<AsyncTransactionWaiter>&& aWaiter)
+                               RefPtr<AsyncTransactionWaiter>&& aWaiter,
+                               ReentrantMonitor* aBarrier,
+                               bool* const outDone)
 {
+#ifdef MOZ_WIDGET_GONK
+  MOZ_ASSERT(aWaiter);
+#else
+  MOZ_ASSERT(!aWaiter);
+#endif
+  ReentrantMonitorAutoEnter autoMon(*aBarrier);
+
   if (!ImageBridgeChild::IsCreated() || ImageBridgeChild::IsShutDown()) {
     // How sad. If we get into this branch it means that the ImageBridge
     // got destroyed between the time we ImageBridgeChild::FlushAllImage
     // was called on some thread, and the time this function was proxied
     // to the ImageBridge thread. ImageBridge gets destroyed way to late
     // in the shutdown of gecko for this to be happening for a good reason.
     NS_WARNING("Something is holding on to graphics resources after the shutdown"
                "of the graphics subsystem!");
+#ifdef MOZ_WIDGET_GONK
     aWaiter->DecrementWaitCount();
+#endif
+
+    *outDone = true;
+    aBarrier->NotifyAll();
     return;
   }
   MOZ_ASSERT(aClient);
   sImageBridgeChildSingleton->BeginTransaction();
   if (aContainer) {
     aContainer->ClearImagesFromImageBridge();
   }
   aClient->FlushAllImages(aWaiter);
   sImageBridgeChildSingleton->EndTransaction();
   // This decrement is balanced by the increment in FlushAllImages.
   // If any AsyncTransactionTrackers were created by FlushAllImages and attached
   // to aWaiter, aWaiter will not complete until those trackers all complete.
   // Otherwise, aWaiter will be ready to complete now.
+#ifdef MOZ_WIDGET_GONK
   aWaiter->DecrementWaitCount();
+#endif
+  *outDone = true;
+  aBarrier->NotifyAll();
 }
 
 // static
 void ImageBridgeChild::FlushAllImages(ImageClient* aClient,
                                       ImageContainer* aContainer)
 {
   if (!IsCreated() || IsShutDown()) {
     return;
@@ -662,25 +680,37 @@ void ImageBridgeChild::FlushAllImages(Im
   MOZ_ASSERT(aClient);
   MOZ_ASSERT(!sImageBridgeChildSingleton->mShuttingDown);
   MOZ_ASSERT(!InImageBridgeChildThread());
   if (InImageBridgeChildThread()) {
     NS_ERROR("ImageBridgeChild::FlushAllImages() is called on ImageBridge thread.");
     return;
   }
 
-  RefPtr<AsyncTransactionWaiter> waiter = new AsyncTransactionWaiter();
+  ReentrantMonitor barrier("FlushAllImages Lock");
+  ReentrantMonitorAutoEnter autoMon(barrier);
+  bool done = false;
+
+  RefPtr<AsyncTransactionWaiter> waiter;
+#ifdef MOZ_WIDGET_GONK
+  waiter = new AsyncTransactionWaiter();
   // This increment is balanced by the decrement in FlushAllImagesSync
   waiter->IncrementWaitCount();
-
+#endif
   sImageBridgeChildSingleton->GetMessageLoop()->PostTask(
     FROM_HERE,
-    NewRunnableFunction(&FlushAllImagesSync, aClient, aContainer, waiter));
+    NewRunnableFunction(&FlushAllImagesSync, aClient, aContainer, waiter, &barrier, &done));
 
+  while (!done) {
+    barrier.Wait();
+  }
+
+#ifdef MOZ_WIDGET_GONK
   waiter->WaitComplete();
+#endif
 }
 
 void
 ImageBridgeChild::BeginTransaction()
 {
   MOZ_ASSERT(!mShuttingDown);
   MOZ_ASSERT(mTxn->Finished(), "uncommitted txn?");
   mTxn->Begin();
--- a/gfx/src/gfxCrashReporterUtils.cpp
+++ b/gfx/src/gfxCrashReporterUtils.cpp
@@ -78,17 +78,17 @@ public:
     if (!observerService)
       return NS_OK;
     RefPtr<ObserverToDestroyFeaturesAlreadyReported> observer = new ObserverToDestroyFeaturesAlreadyReported;
     observerService->AddObserver(observer, "xpcom-shutdown", false);
     return NS_OK;
   }
 };
 
-class AppendAppNotesRunnable : public nsCancelableRunnable {
+class AppendAppNotesRunnable : public CancelableRunnable {
 public:
   explicit AppendAppNotesRunnable(const nsACString& aFeatureStr)
     : mFeatureString(aFeatureStr)
   {
   }
 
   NS_IMETHOD Run() override {
     CrashReporter::AppendAppNotesToCrashReport(mFeatureString);
--- a/gfx/src/nsDeviceContext.cpp
+++ b/gfx/src/nsDeviceContext.cpp
@@ -343,33 +343,38 @@ nsDeviceContext::CreateRenderingContext(
 #endif
 
     RefPtr<gfx::DrawTarget> dt =
       gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(printingSurface,
                                                              gfx::IntSize(mWidth, mHeight));
 
     // This can legitimately happen - CreateDrawTargetForSurface will fail
     // to create a draw target if the size is too large, for instance.
-    if (!dt) {
+    if (!dt || !dt->IsValid()) {
         gfxCriticalNote << "Failed to create draw target in device context sized " << mWidth << "x" << mHeight << " and pointers " << hexa(mPrintingSurface) << " and " << hexa(printingSurface);
         return nullptr;
     }
 
     RefPtr<DrawEventRecorder> recorder;
     nsresult rv = mDeviceContextSpec->GetDrawEventRecorder(getter_AddRefs(recorder));
     if (NS_SUCCEEDED(rv) && recorder) {
       dt = gfx::Factory::CreateRecordingDrawTarget(recorder, dt);
+      if (!dt || !dt->IsValid()) {
+          gfxCriticalNote << "Failed to create a recording draw target";
+          return nullptr;
+      }
     }
 
 #ifdef XP_MACOSX
     dt->AddUserData(&gfxContext::sDontUseAsSourceKey, dt, nullptr);
 #endif
     dt->AddUserData(&sDisablePixelSnapping, (void*)0x1, nullptr);
 
-    RefPtr<gfxContext> pContext = new gfxContext(dt);
+    RefPtr<gfxContext> pContext = gfxContext::ForDrawTarget(dt);
+    MOZ_ASSERT(pContext); // already checked draw target above
 
     gfxMatrix transform;
     if (printingSurface->GetRotateForLandscape()) {
       // Rotate page 90 degrees to draw landscape page on portrait paper
       IntSize size = printingSurface->GetSize();
       transform.Translate(gfxPoint(0, size.width));
       gfxMatrix rotate(0, -1,
                        1,  0,
--- a/gfx/tests/gtest/gfxFontSelectionTest.cpp
+++ b/gfx/tests/gtest/gfxFontSelectionTest.cpp
@@ -186,17 +186,17 @@ struct TestEntry {
 static already_AddRefed<gfxContext>
 MakeContext ()
 {
     const int size = 200;
 
     RefPtr<DrawTarget> drawTarget = gfxPlatform::GetPlatform()->
         CreateOffscreenContentDrawTarget(IntSize(size, size),
                                          SurfaceFormat::B8G8R8X8);
-    RefPtr<gfxContext> ctx = new gfxContext(drawTarget);
+    RefPtr<gfxContext> ctx = gfxContext::ForDrawTarget(drawTarget);
 
     return ctx.forget();
 }
 
 TestEntry*
 AddTest (nsTArray<TestEntry>& testList,
          const char *utf8FamilyString,
          const gfxFontStyle& fontStyle,
--- a/gfx/tests/gtest/gfxTextRunPerfTest.cpp
+++ b/gfx/tests/gtest/gfxTextRunPerfTest.cpp
@@ -38,17 +38,17 @@ TestEntry testList[] = {
 static already_AddRefed<gfxContext>
 MakeContext ()
 {
     const int size = 200;
 
     RefPtr<DrawTarget> drawTarget = gfxPlatform::GetPlatform()->
         CreateOffscreenContentDrawTarget(IntSize(size, size),
                                          SurfaceFormat::B8G8R8X8);
-    RefPtr<gfxContext> ctx = new gfxContext(drawTarget);
+    RefPtr<gfxContext> ctx = gfxContext::ForDrawTarget(drawTarget);
 
     return ctx.forget();
 }
 
 const char* lastFamilies = nullptr;
 
 static void
 RunTest (TestEntry *test, gfxContext *ctx) {
--- a/gfx/thebes/gfxBlur.cpp
+++ b/gfx/thebes/gfxBlur.cpp
@@ -69,24 +69,25 @@ gfxAlphaBoxBlur::Init(const gfxRect& aRe
         return nullptr;
     }
     memset(mData.get(), 0, blurDataSize);
 
     RefPtr<DrawTarget> dt =
         gfxPlatform::GetPlatform()->CreateDrawTargetForData(mData.get(), size,
                                                             mBlur->GetStride(),
                                                             SurfaceFormat::A8);
-    if (!dt) {
+    if (!dt || !dt->IsValid()) {
         return nullptr;
     }
 
     IntRect irect = mBlur->GetRect();
     gfxPoint topleft(irect.TopLeft().x, irect.TopLeft().y);
 
-    mContext = new gfxContext(dt);
+    mContext = gfxContext::ForDrawTarget(dt);
+    MOZ_ASSERT(mContext); // already checked for target above
     mContext->SetMatrix(gfxMatrix::Translation(-topleft));
 
     return mContext;
 }
 
 void
 DrawBlur(gfxContext* aDestinationCtx,
          SourceSurface* aBlur,
--- a/gfx/thebes/gfxContext.cpp
+++ b/gfx/thebes/gfxContext.cpp
@@ -67,31 +67,46 @@ PatternFromState::operator mozilla::gfx:
 }
 
 
 gfxContext::gfxContext(DrawTarget *aTarget, const Point& aDeviceOffset)
   : mPathIsRect(false)
   , mTransformChanged(false)
   , mDT(aTarget)
 {
-  MOZ_ASSERT(aTarget, "Don't create a gfxContext without a DrawTarget");
+  if (!aTarget) {
+    gfxCriticalError() << "Don't create a gfxContext without a DrawTarget";
+  }
 
   MOZ_COUNT_CTOR(gfxContext);
 
   mStateStack.SetLength(1);
   CurrentState().drawTarget = mDT;
   CurrentState().deviceOffset = aDeviceOffset;
   mDT->SetTransform(GetDTTransform());
 }
 
 /* static */ already_AddRefed<gfxContext>
-gfxContext::ContextForDrawTarget(DrawTarget* aTarget)
+gfxContext::ForDrawTarget(DrawTarget* aTarget,
+                          const mozilla::gfx::Point& aDeviceOffset)
 {
   if (!aTarget || !aTarget->IsValid()) {
-    gfxWarning() << "Invalid target in gfxContext::ContextForDrawTarget";
+    gfxCriticalNote << "Invalid target in gfxContext::ForDrawTarget " << hexa(aTarget);
+    return nullptr;
+  }
+
+  RefPtr<gfxContext> result = new gfxContext(aTarget, aDeviceOffset);
+  return result.forget();
+}
+
+/* static */ already_AddRefed<gfxContext>
+gfxContext::ForDrawTargetWithTransform(DrawTarget* aTarget)
+{
+  if (!aTarget || !aTarget->IsValid()) {
+    gfxCriticalNote << "Invalid target in gfxContext::ForDrawTargetWithTransform " << hexa(aTarget);
     return nullptr;
   }
 
   Matrix transform = aTarget->GetTransform();
   RefPtr<gfxContext> result = new gfxContext(aTarget);
   result->SetMatrix(ThebesMatrix(transform));
   return result.forget();
 }
--- a/gfx/thebes/gfxContext.h
+++ b/gfx/thebes/gfxContext.h
@@ -53,31 +53,36 @@ class gfxContext final {
     typedef mozilla::gfx::Pattern Pattern;
     typedef mozilla::gfx::Rect Rect;
     typedef mozilla::gfx::RectCornerRadii RectCornerRadii;
     typedef mozilla::gfx::Size Size;
 
     NS_INLINE_DECL_REFCOUNTING(gfxContext)
 
 public:
-
     /**
      * Initialize this context from a DrawTarget.
      * Strips any transform from aTarget.
      * aTarget will be flushed in the gfxContext's destructor.
+     * If aTarget is null or invalid, nullptr is returned.  The caller
+     * is responsible for handling this scenario as appropriate.
      */
-    explicit gfxContext(mozilla::gfx::DrawTarget *aTarget,
-                        const mozilla::gfx::Point& aDeviceOffset = mozilla::gfx::Point());
-
+    static already_AddRefed<gfxContext>
+        ForDrawTarget(mozilla::gfx::DrawTarget* aTarget,
+                      const mozilla::gfx::Point& aDeviceOffset = mozilla::gfx::Point());
+    
     /**
      * Create a new gfxContext wrapping aTarget and preserving aTarget's
      * transform. Note that the transform is moved from aTarget to the resulting
      * gfxContext, aTarget will no longer have its transform.
+     * If aTarget is null or invalid, nullptr is returned.  The caller
+     * is responsible for handling this scenario as appropriate.
      */
-    static already_AddRefed<gfxContext> ContextForDrawTarget(mozilla::gfx::DrawTarget* aTarget);
+    static already_AddRefed<gfxContext>
+        ForDrawTargetWithTransform(mozilla::gfx::DrawTarget* aTarget);
 
     /**
      * Return the current transparency group target, if any. If no group is
      * active, returns the surface the gfxContext was created with.
      */
     already_AddRefed<gfxASurface> CurrentSurface();
 
     mozilla::gfx::DrawTarget *GetDrawTarget() { return mDT; }
@@ -453,16 +458,26 @@ public:
      * Copy a PNG encoded Data URL to the clipboard.
      */
     void CopyAsDataURI();
 #endif
 
     static mozilla::gfx::UserDataKey sDontUseAsSourceKey;
 
 private:
+
+    /**
+     * Initialize this context from a DrawTarget.
+     * Strips any transform from aTarget.
+     * aTarget will be flushed in the gfxContext's destructor.  Use the static
+     * ContextForDrawTargetNoTransform() when you want this behavior, as that
+     * version deals with null DrawTarget better.
+     */
+    explicit gfxContext(mozilla::gfx::DrawTarget *aTarget,
+                        const mozilla::gfx::Point& aDeviceOffset = mozilla::gfx::Point());
     ~gfxContext();
 
   friend class PatternFromState;
   friend class GlyphBufferAzure;
 
   typedef mozilla::gfx::Matrix Matrix;
   typedef mozilla::gfx::DrawTarget DrawTarget;
   typedef mozilla::gfx::Color Color;
--- a/gfx/thebes/gfxDrawable.cpp
+++ b/gfx/thebes/gfxDrawable.cpp
@@ -117,20 +117,21 @@ gfxCallbackDrawable::gfxCallbackDrawable
 already_AddRefed<gfxSurfaceDrawable>
 gfxCallbackDrawable::MakeSurfaceDrawable(const Filter aFilter)
 {
     SurfaceFormat format =
         gfxPlatform::GetPlatform()->Optimal2DFormatForContent(gfxContentType::COLOR_ALPHA);
     RefPtr<DrawTarget> dt =
         gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(mSize,
                                                                      format);
-    if (!dt)
+    if (!dt || !dt->IsValid())
         return nullptr;
 
-    RefPtr<gfxContext> ctx = new gfxContext(dt);
+    RefPtr<gfxContext> ctx = gfxContext::ForDrawTarget(dt);
+    MOZ_ASSERT(ctx); // already checked for target above
     Draw(ctx, gfxRect(0, 0, mSize.width, mSize.height), ExtendMode::CLAMP, aFilter);
 
     RefPtr<SourceSurface> surface = dt->Snapshot();
     if (surface) {
         RefPtr<gfxSurfaceDrawable> drawable = new gfxSurfaceDrawable(surface, mSize);
         return drawable.forget();
     }
     return nullptr;
--- a/gfx/thebes/gfxUtils.cpp
+++ b/gfx/thebes/gfxUtils.cpp
@@ -441,21 +441,23 @@ CreateSamplingRestrictedDrawable(gfxDraw
     // create a zero-size surface.
     if (needed.IsEmpty())
         return nullptr;
 
     IntSize size(int32_t(needed.Width()), int32_t(needed.Height()));
 
     RefPtr<DrawTarget> target =
       gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(size, aFormat);
-    if (!target) {
+    if (!target || !target->IsValid()) {
       return nullptr;
     }
 
-    RefPtr<gfxContext> tmpCtx = new gfxContext(target);
+    RefPtr<gfxContext> tmpCtx = gfxContext::ForDrawTarget(target);
+    MOZ_ASSERT(tmpCtx); // already checked the target above
+
     tmpCtx->SetOp(OptimalFillOp());
     aDrawable->Draw(tmpCtx, needed - needed.TopLeft(), ExtendMode::REPEAT, Filter::LINEAR,
                     1.0, gfxMatrix::Translation(needed.TopLeft()));
     RefPtr<SourceSurface> surface = target->Snapshot();
 
     RefPtr<gfxDrawable> drawable = new gfxSurfaceDrawable(surface, size, gfxMatrix::Translation(-needed.TopLeft()));
     return drawable.forget();
 }
@@ -587,21 +589,23 @@ PrescaleAndTileDrawable(gfxDrawable* aDr
       scaledImageSize.height != scaledImageRect.height) {
     // If the scaled image isn't pixel aligned, we'll get artifacts
     // so we have to take the slow path.
     return false;
   }
 
   RefPtr<DrawTarget> scaledDT =
     gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(scaledImageSize, aFormat);
-  if (!scaledDT) {
+  if (!scaledDT || !scaledDT->IsValid()) {
     return false;
   }
 
-  RefPtr<gfxContext> tmpCtx = new gfxContext(scaledDT);
+  RefPtr<gfxContext> tmpCtx = gfxContext::ForDrawTarget(scaledDT);
+  MOZ_ASSERT(tmpCtx); // already checked the target above
+
   scaledDT->SetTransform(ToMatrix(scaleMatrix));
   gfxRect gfxImageRect(aImageRect.x, aImageRect.y, aImageRect.width, aImageRect.height);
   aDrawable->Draw(tmpCtx, gfxImageRect, ExtendMode::REPEAT, aFilter, 1.0, gfxMatrix());
 
   RefPtr<SourceSurface> scaledImage = scaledDT->Snapshot();
 
   {
     gfxContextMatrixAutoSaveRestore autoSR(aContext);
@@ -1337,19 +1341,20 @@ gfxUtils::WriteAsPNG(nsIPresShell* aShel
 {
   int32_t width = 1000, height = 1000;
   nsRect r(0, 0, aShell->GetPresContext()->DevPixelsToAppUnits(width),
            aShell->GetPresContext()->DevPixelsToAppUnits(height));
 
   RefPtr<mozilla::gfx::DrawTarget> dt = gfxPlatform::GetPlatform()->
     CreateOffscreenContentDrawTarget(IntSize(width, height),
                                      SurfaceFormat::B8G8R8A8);
-  NS_ENSURE_TRUE(dt, /*void*/);
+  NS_ENSURE_TRUE(dt && dt->IsValid(), /*void*/);
 
-  RefPtr<gfxContext> context = new gfxContext(dt);
+  RefPtr<gfxContext> context = gfxContext::ForDrawTarget(dt);
+  MOZ_ASSERT(context); // already checked the draw target above
   aShell->RenderDocument(r, 0, NS_RGB(255, 255, 0), context);
   WriteAsPNG(dt.get(), aFile);
 }
 
 /* static */ void
 gfxUtils::DumpAsDataURI(SourceSurface* aSurface, FILE* aFile)
 {
   EncodeSourceSurface(aSurface, NS_LITERAL_CSTRING("image/png"),
--- a/image/ClippedImage.cpp
+++ b/image/ClippedImage.cpp
@@ -285,22 +285,23 @@ ClippedImage::GetFrameInternal(const nsI
   float frameToDraw = InnerImage()->GetFrameIndex(aWhichFrame);
   if (!mCachedSurface ||
       !mCachedSurface->Matches(aSize, aSVGContext, frameToDraw, aFlags) ||
       mCachedSurface->NeedsRedraw()) {
     // Create a surface to draw into.
     RefPtr<DrawTarget> target = gfxPlatform::GetPlatform()->
       CreateOffscreenContentDrawTarget(IntSize(aSize.width, aSize.height),
                                        SurfaceFormat::B8G8R8A8);
-    if (!target) {
+    if (!target || !target->IsValid()) {
       NS_ERROR("Could not create a DrawTarget");
       return MakePair(DrawResult::TEMPORARY_ERROR, RefPtr<SourceSurface>());
     }
 
-    RefPtr<gfxContext> ctx = new gfxContext(target);
+    RefPtr<gfxContext> ctx = gfxContext::ForDrawTarget(target);
+    MOZ_ASSERT(ctx); // already checked the draw target above
 
     // Create our callback.
     RefPtr<DrawSingleTileCallback> drawTileCallback =
       new DrawSingleTileCallback(this, aSize, aSVGContext, aWhichFrame, aFlags);
     RefPtr<gfxDrawable> drawable =
       new gfxCallbackDrawable(drawTileCallback, aSize);
 
     // Actually draw. The callback will end up invoking DrawSingleTile.
--- a/image/DynamicImage.cpp
+++ b/image/DynamicImage.cpp
@@ -175,22 +175,23 @@ DynamicImage::GetFrame(uint32_t aWhichFr
 
 NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
 DynamicImage::GetFrameAtSize(const IntSize& aSize,
                              uint32_t aWhichFrame,
                              uint32_t aFlags)
 {
   RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()->
     CreateOffscreenContentDrawTarget(aSize, SurfaceFormat::B8G8R8A8);
-  if (!dt) {
+  if (!dt || !dt->IsValid()) {
     gfxWarning() <<
       "DynamicImage::GetFrame failed in CreateOffscreenContentDrawTarget";
     return nullptr;
   }
-  RefPtr<gfxContext> context = new gfxContext(dt);
+  RefPtr<gfxContext> context = gfxContext::ForDrawTarget(dt);
+  MOZ_ASSERT(context); // already checked the draw target above
 
   auto result = Draw(context, aSize, ImageRegion::Create(aSize),
                      aWhichFrame, Filter::POINT, Nothing(), aFlags);
 
   return result == DrawResult::SUCCESS ? dt->Snapshot() : nullptr;
 }
 
 NS_IMETHODIMP_(bool)
--- a/image/OrientedImage.cpp
+++ b/image/OrientedImage.cpp
@@ -94,31 +94,32 @@ OrientedImage::GetFrame(uint32_t aWhichF
   } else {
     surfaceFormat = gfx::SurfaceFormat::B8G8R8A8;
   }
 
   // Create a surface to draw into.
   RefPtr<DrawTarget> target =
     gfxPlatform::GetPlatform()->
       CreateOffscreenContentDrawTarget(size, surfaceFormat);
-  if (!target) {
+  if (!target || !target->IsValid()) {
     NS_ERROR("Could not create a DrawTarget");
     return nullptr;
   }
 
 
   // Create our drawable.
   RefPtr<SourceSurface> innerSurface =
     InnerImage()->GetFrame(aWhichFrame, aFlags);
   NS_ENSURE_TRUE(innerSurface, nullptr);
   RefPtr<gfxDrawable> drawable =
     new gfxSurfaceDrawable(innerSurface, size);
 
   // Draw.
-  RefPtr<gfxContext> ctx = new gfxContext(target);
+  RefPtr<gfxContext> ctx = gfxContext::ForDrawTarget(target);
+  MOZ_ASSERT(ctx); // already checked the draw target above
   ctx->Multiply(OrientationMatrix(size));
   gfxUtils::DrawPixelSnapped(ctx, drawable, size, ImageRegion::Create(size),
                              surfaceFormat, Filter::LINEAR);
 
   return target->Snapshot();
 }
 
 NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
--- a/image/VectorImage.cpp
+++ b/image/VectorImage.cpp
@@ -721,22 +721,23 @@ VectorImage::GetFrameAtSize(const IntSiz
   if (mError || !mIsFullyLoaded) {
     return nullptr;
   }
 
   // Make our surface the size of what will ultimately be drawn to it.
   // (either the full image size, or the restricted region)
   RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()->
     CreateOffscreenContentDrawTarget(aSize, SurfaceFormat::B8G8R8A8);
-  if (!dt) {
+  if (!dt || !dt->IsValid()) {
     NS_ERROR("Could not create a DrawTarget");
     return nullptr;
   }
 
-  RefPtr<gfxContext> context = new gfxContext(dt);
+  RefPtr<gfxContext> context = gfxContext::ForDrawTarget(dt);
+  MOZ_ASSERT(context); // already checked the draw target above
 
   auto result = Draw(context, aSize, ImageRegion::Create(aSize),
                      aWhichFrame, Filter::POINT, Nothing(), aFlags);
 
   return result == DrawResult::SUCCESS ? dt->Snapshot() : nullptr;
 }
 
 NS_IMETHODIMP_(bool)
--- a/image/imgFrame.cpp
+++ b/image/imgFrame.cpp
@@ -292,24 +292,25 @@ imgFrame::InitWithDrawable(gfxDrawable* 
     // may have to do an expensive readback, but we warned callers about that in
     // the documentation for this method.
     MOZ_ASSERT(!mOptSurface, "Called imgFrame::InitWithDrawable() twice?");
 
     target = gfxPlatform::GetPlatform()->
       CreateOffscreenContentDrawTarget(mSize, mFormat);
   }
 
-  if (!target) {
+  if (!target || !target->IsValid()) {
     mAborted = true;
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   // Draw using the drawable the caller provided.
   nsIntRect imageRect(0, 0, mSize.width, mSize.height);
-  RefPtr<gfxContext> ctx = new gfxContext(target);
+  RefPtr<gfxContext> ctx = gfxContext::ForDrawTarget(target);
+  MOZ_ASSERT(ctx); // already checked the draw target above
   gfxUtils::DrawPixelSnapped(ctx, aDrawable, mSize,
                              ImageRegion::Create(ThebesRect(imageRect)),
                              mFormat, aFilter, aImageFlags);
 
   if (canUseDataSurface && !mImageSurface) {
     NS_WARNING("Failed to create VolatileDataSourceSurface");
     mAborted = true;
     return NS_ERROR_OUT_OF_MEMORY;
--- a/ipc/glue/BackgroundImpl.cpp
+++ b/ipc/glue/BackgroundImpl.cpp
@@ -695,32 +695,30 @@ private:
           override;
 
   virtual void
   Failure() override;
 };
 
 // Must be cancelable in order to dispatch on active worker threads
 class ChildImpl::AlreadyCreatedCallbackRunnable final :
-  public nsCancelableRunnable
+  public CancelableRunnable
 {
 public:
   AlreadyCreatedCallbackRunnable()
   {
     // May be created on any thread!
   }
 
-  NS_DECL_ISUPPORTS_INHERITED
-
 protected:
   virtual ~AlreadyCreatedCallbackRunnable()
   { }
 
   NS_DECL_NSIRUNNABLE
-  NS_DECL_NSICANCELABLERUNNABLE
+  nsresult Cancel() override;
 };
 
 class ChildImpl::FailedCreateCallbackRunnable final : public nsRunnable
 {
 public:
   FailedCreateCallbackRunnable()
   {
     // May be created on any thread!
@@ -1809,19 +1807,16 @@ ChildImpl::GetNextCallback()
   nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback;
   threadLocalInfo->mCallbacks[0].swap(callback);
 
   threadLocalInfo->mCallbacks.RemoveElementAt(0);
 
   return callback.forget();
 }
 
-NS_IMPL_ISUPPORTS_INHERITED0(ChildImpl::AlreadyCreatedCallbackRunnable,
-                             nsCancelableRunnable)
-
 NS_IMETHODIMP
 ChildImpl::AlreadyCreatedCallbackRunnable::Run()
 {
   // May run on any thread!
 
   // Report the current actor back in the callback.
   PBackgroundChild* actor = ChildImpl::GetForCurrentThread();
 
@@ -1839,17 +1834,17 @@ ChildImpl::AlreadyCreatedCallbackRunnabl
   while (callback) {
     callback->ActorCreated(actor);
     callback = ChildImpl::GetNextCallback();
   }
 
   return NS_OK;
 }
 
-NS_IMETHODIMP
+nsresult
 ChildImpl::AlreadyCreatedCallbackRunnable::Cancel()
 {
   // These are IPC infrastructure objects and need to run unconditionally.
   Run();
   return NS_OK;
 }
 
 NS_IMPL_ISUPPORTS_INHERITED0(ChildImpl::FailedCreateCallbackRunnable,
--- a/ipc/glue/MessagePump.cpp
+++ b/ipc/glue/MessagePump.cpp
@@ -34,30 +34,30 @@ NS_DEFINE_NAMED_CID(NS_TIMER_CID);
 
 #ifdef DEBUG
 static MessagePump::Delegate* gFirstDelegate;
 #endif
 
 namespace mozilla {
 namespace ipc {
 
-class DoWorkRunnable final : public nsICancelableRunnable,
+class DoWorkRunnable final : public CancelableRunnable,
                              public nsITimerCallback
 {
 public:
   explicit DoWorkRunnable(MessagePump* aPump)
   : mPump(aPump)
   {
     MOZ_ASSERT(aPump);
   }
 
-  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIRUNNABLE
   NS_DECL_NSITIMERCALLBACK
-  NS_DECL_NSICANCELABLERUNNABLE
+  nsresult Cancel() override;
 
 private:
   ~DoWorkRunnable()
   { }
 
   MessagePump* mPump;
   // DoWorkRunnable is designed as a stateless singleton.  Do not add stateful
   // members here!
@@ -204,18 +204,18 @@ void
 MessagePump::DoDelayedWork(base::MessagePump::Delegate* aDelegate)
 {
   aDelegate->DoDelayedWork(&delayed_work_time_);
   if (!delayed_work_time_.is_null()) {
     ScheduleDelayedWork(delayed_work_time_);
   }
 }
 
-NS_IMPL_ISUPPORTS(DoWorkRunnable, nsIRunnable, nsITimerCallback,
-                                  nsICancelableRunnable)
+NS_IMPL_ISUPPORTS_INHERITED(DoWorkRunnable, CancelableRunnable,
+			    nsITimerCallback)
 
 NS_IMETHODIMP
 DoWorkRunnable::Run()
 {
   MessageLoop* loop = MessageLoop::current();
   MOZ_ASSERT(loop);
 
   bool nestableTasksAllowed = loop->NestableTasksAllowed();
@@ -239,17 +239,17 @@ DoWorkRunnable::Notify(nsITimer* aTimer)
   bool nestableTasksAllowed = loop->NestableTasksAllowed();
   loop->SetNestableTasksAllowed(true);
   mPump->DoDelayedWork(loop);
   loop->SetNestableTasksAllowed(nestableTasksAllowed);
 
   return NS_OK;
 }
 
-NS_IMETHODIMP
+nsresult
 DoWorkRunnable::Cancel()
 {
   // Workers require cancelable runnables, but we can't really cancel cleanly
   // here.  If we don't process this runnable then we will leave something
   // unprocessed in the message_loop.  Therefore, eagerly complete our work
   // instead by immediately calling Run().  Run() should be called separately
   // after this.  Unfortunately we cannot use flags to verify this because
   // DoWorkRunnable is a stateless singleton that can be in the event queue
--- a/ipc/glue/ProcessChild.cpp
+++ b/ipc/glue/ProcessChild.cpp
@@ -2,16 +2,22 @@
  * vim: sw=2 ts=8 et :
  */
 /* 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 "nsDebug.h"
 
+#ifdef XP_WIN
+#include <stdlib.h> // for _exit()
+#else
+#include <unistd.h> // for _exit()
+#endif
+
 #include "mozilla/ipc/IOThreadChild.h"
 #include "mozilla/ipc/ProcessChild.h"
 
 namespace mozilla {
 namespace ipc {
 
 ProcessChild* ProcessChild::gProcessChild;
 
@@ -25,10 +31,23 @@ ProcessChild::ProcessChild(ProcessId aPa
   gProcessChild = this;
 }
 
 ProcessChild::~ProcessChild()
 {
   gProcessChild = nullptr;
 }
 
+/* static */ void
+ProcessChild::QuickExit()
+{
+#ifdef XP_WIN
+  // In bug 1254829, the destructor got called when dll got detached on windows,
+  // switch to TerminateProcess to bypass dll detach handler during the process
+  // termination.
+  TerminateProcess(GetCurrentProcess(), 0);
+#else
+  _exit(0);
+#endif
+}
+
 } // namespace ipc
 } // namespace mozilla
--- a/ipc/glue/ProcessChild.h
+++ b/ipc/glue/ProcessChild.h
@@ -31,16 +31,22 @@ public:
   virtual bool Init() = 0;
   virtual void CleanUp()
   { }
 
   static MessageLoop* message_loop() {
     return gProcessChild->mUILoop;
   }
 
+    /**
+   * Exit *now*.  Do not shut down XPCOM, do not pass Go, do not run
+   * static destructors, do not collect $200.
+   */
+  static void QuickExit();
+
 protected:
   static ProcessChild* current() {
     return gProcessChild;
   }
 
   ProcessId ParentPid() {
     return mParentPid;
   }
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -472,33 +472,65 @@ class JS_FRIEND_API(ElementAdder)
 
 typedef bool
 (* GetElementsOp)(JSContext* cx, JS::HandleObject obj, uint32_t begin, uint32_t end,
                   ElementAdder* adder);
 
 typedef void
 (* FinalizeOp)(FreeOp* fop, JSObject* obj);
 
-#define JS_CLASS_MEMBERS(FinalizeOpType)                                      \
-    const char*         name;                                                \
-    uint32_t            flags;                                                \
-                                                                              \
-    /* Function pointer members (may be null). */                             \
-    JSAddPropertyOp     addProperty;                                          \
-    JSDeletePropertyOp  delProperty;                                          \
-    JSGetterOp          getProperty;                                          \
-    JSSetterOp          setProperty;                                          \
-    JSEnumerateOp       enumerate;                                            \
-    JSResolveOp         resolve;                                              \
-    JSMayResolveOp      mayResolve;                                           \
-    FinalizeOpType      finalize;                                             \
-    JSNative            call;                                                 \
-    JSHasInstanceOp     hasInstance;                                          \
-    JSNative            construct;                                            \
-    JSTraceOp           trace
+// The special treatment of |finalize| and |trace| is necessary because if we
+// assign either of those hooks to a local variable and then call it -- as is
+// done with the other hooks -- the GC hazard analysis gets confused.
+#define JS_CLASS_MEMBERS(ClassOpsType, FinalizeOpType, FreeOpType) \
+    const char* name; \
+    uint32_t flags; \
+    const ClassOpsType* cOps; \
+    \
+    JSAddPropertyOp    getAddProperty() const { return cOps ? cOps->addProperty : nullptr; } \
+    JSDeletePropertyOp getDelProperty() const { return cOps ? cOps->delProperty : nullptr; } \
+    JSGetterOp         getGetProperty() const { return cOps ? cOps->getProperty : nullptr; } \
+    JSSetterOp         getSetProperty() const { return cOps ? cOps->setProperty : nullptr; } \
+    JSEnumerateOp      getEnumerate()   const { return cOps ? cOps->enumerate   : nullptr; } \
+    JSResolveOp        getResolve()     const { return cOps ? cOps->resolve     : nullptr; } \
+    JSMayResolveOp     getMayResolve()  const { return cOps ? cOps->mayResolve  : nullptr; } \
+    JSNative           getCall()        const { return cOps ? cOps->call        : nullptr; } \
+    JSHasInstanceOp    getHasInstance() const { return cOps ? cOps->hasInstance : nullptr; } \
+    JSNative           getConstruct()   const { return cOps ? cOps->construct   : nullptr; } \
+    \
+    bool hasFinalize() const { return cOps && cOps->finalize; } \
+    bool hasTrace()    const { return cOps && cOps->trace;    } \
+    \
+    bool isTrace(JSTraceOp trace) const { return cOps && cOps->trace == trace; } \
+    \
+    void doFinalize(FreeOpType* fop, JSObject* obj) const { \
+        MOZ_ASSERT(cOps && cOps->finalize); \
+        cOps->finalize(fop, obj); \
+    } \
+    void doTrace(JSTracer* trc, JSObject* obj) const { \
+        MOZ_ASSERT(cOps && cOps->trace); \
+        cOps->trace(trc, obj); \
+    }
+
+struct ClassOps
+{
+    /* Function pointer members (may be null). */
+    JSAddPropertyOp     addProperty;
+    JSDeletePropertyOp  delProperty;
+    JSGetterOp          getProperty;
+    JSSetterOp          setProperty;
+    JSEnumerateOp       enumerate;
+    JSResolveOp         resolve;
+    JSMayResolveOp      mayResolve;
+    FinalizeOp          finalize;
+    JSNative            call;
+    JSHasInstanceOp     hasInstance;
+    JSNative            construct;
+    JSTraceOp           trace;
+};
 
 /** Callback for the creation of constructor and prototype objects. */
 typedef JSObject* (*ClassObjectCreationOp)(JSContext* cx, JSProtoKey key);
 
 /** Callback for custom post-processing after class initialization via ClassSpec. */
 typedef bool (*FinishClassInitOp)(JSContext* cx, JS::HandleObject ctor,
                                   JS::HandleObject proto);
 
@@ -640,18 +672,37 @@ struct ObjectOps
 #define JS_NULL_OBJECT_OPS nullptr
 
 } // namespace js
 
 // Classes, objects, and properties.
 
 typedef void (*JSClassInternal)();
 
+struct JSClassOps
+{
+    /* Function pointer members (may be null). */
+    JSAddPropertyOp     addProperty;
+    JSDeletePropertyOp  delProperty;
+    JSGetterOp          getProperty;
+    JSSetterOp          setProperty;
+    JSEnumerateOp       enumerate;
+    JSResolveOp         resolve;
+    JSMayResolveOp      mayResolve;
+    JSFinalizeOp        finalize;
+    JSNative            call;
+    JSHasInstanceOp     hasInstance;
+    JSNative            construct;
+    JSTraceOp           trace;
+};
+
+#define JS_NULL_CLASS_OPS nullptr
+
 struct JSClass {
-    JS_CLASS_MEMBERS(JSFinalizeOp);
+    JS_CLASS_MEMBERS(JSClassOps, JSFinalizeOp, JSFreeOp);
 
     void* reserved[3];
 };
 
 #define JSCLASS_HAS_PRIVATE             (1<<0)  // objects have private slot
 #define JSCLASS_DELAY_METADATA_BUILDER  (1<<1)  // class's initialization code
                                                 // will call
                                                 // SetNewObjectMetadata itself
@@ -748,20 +799,20 @@ struct JSClass {
 // Initializer for unused members of statically initialized JSClass structs.
 #define JSCLASS_NO_INTERNAL_MEMBERS     {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
 #define JSCLASS_NO_OPTIONAL_MEMBERS     0,0,0,0,0,JSCLASS_NO_INTERNAL_MEMBERS
 
 namespace js {
 
 struct Class
 {
-    JS_CLASS_MEMBERS(FinalizeOp);
+    JS_CLASS_MEMBERS(js::ClassOps, FinalizeOp, FreeOp);
     const ClassSpec* spec;
     const ClassExtension* ext;
-    const ObjectOps* ops;
+    const ObjectOps* oOps;
 
     /*
      * Objects of this class aren't native objects. They don't have Shapes that
      * describe their properties and layout. Classes using this flag must
      * provide their own property behavior, either by being proxy classes (do
      * this) or by overriding all the ObjectOps except getElements, watch and
      * unwatch (don't do this).
      */
@@ -780,17 +831,17 @@ struct Class
     }
 
     bool isJSFunction() const {
         return this == js::FunctionClassPtr;
     }
 
     bool nonProxyCallable() const {
         MOZ_ASSERT(!isProxy());
-        return isJSFunction() || call;
+        return isJSFunction() || getCall();
     }
 
     bool isProxy() const {
         return flags & JSCLASS_IS_PROXY;
     }
 
     bool isDOMClass() const {
         return flags & JSCLASS_IS_DOMJSCLASS;
@@ -826,59 +877,64 @@ struct Class
     FinishClassInitOp specFinishInitHook()
                                const { return spec ? spec->finishInitHook()          : nullptr; }
 
     JSWeakmapKeyDelegateOp extWeakmapKeyDelegateOp()
                                const { return ext ? ext->weakmapKeyDelegateOp        : nullptr; }
     JSObjectMovedOp extObjectMovedOp()
                                const { return ext ? ext->objectMovedOp               : nullptr; }
 
-    LookupPropertyOp getOpsLookupProperty() const { return ops ? ops->lookupProperty : nullptr; }
-    DefinePropertyOp getOpsDefineProperty() const { return ops ? ops->defineProperty : nullptr; }
-    HasPropertyOp    getOpsHasProperty()    const { return ops ? ops->hasProperty    : nullptr; }
-    GetPropertyOp    getOpsGetProperty()    const { return ops ? ops->getProperty    : nullptr; }
-    SetPropertyOp    getOpsSetProperty()    const { return ops ? ops->setProperty    : nullptr; }
+    LookupPropertyOp getOpsLookupProperty() const { return oOps ? oOps->lookupProperty : nullptr; }
+    DefinePropertyOp getOpsDefineProperty() const { return oOps ? oOps->defineProperty : nullptr; }
+    HasPropertyOp    getOpsHasProperty()    const { return oOps ? oOps->hasProperty    : nullptr; }
+    GetPropertyOp    getOpsGetProperty()    const { return oOps ? oOps->getProperty    : nullptr; }
+    SetPropertyOp    getOpsSetProperty()    const { return oOps ? oOps->setProperty    : nullptr; }
     GetOwnPropertyOp getOpsGetOwnPropertyDescriptor()
-                                            const { return ops ? ops->getOwnPropertyDescriptor
+                                            const { return oOps ? oOps->getOwnPropertyDescriptor
                                                                                      : nullptr; }
-    DeletePropertyOp getOpsDeleteProperty() const { return ops ? ops->deleteProperty : nullptr; }
-    WatchOp          getOpsWatch()          const { return ops ? ops->watch          : nullptr; }
-    UnwatchOp        getOpsUnwatch()        const { return ops ? ops->unwatch        : nullptr; }
-    GetElementsOp    getOpsGetElements()    const { return ops ? ops->getElements    : nullptr; }
-    JSNewEnumerateOp getOpsEnumerate()      const { return ops ? ops->enumerate      : nullptr; }
-    JSFunToStringOp  getOpsFunToString()    const { return ops ? ops->funToString    : nullptr; }
+    DeletePropertyOp getOpsDeleteProperty() const { return oOps ? oOps->deleteProperty : nullptr; }
+    WatchOp          getOpsWatch()          const { return oOps ? oOps->watch          : nullptr; }
+    UnwatchOp        getOpsUnwatch()        const { return oOps ? oOps->unwatch        : nullptr; }
+    GetElementsOp    getOpsGetElements()    const { return oOps ? oOps->getElements    : nullptr; }
+    JSNewEnumerateOp getOpsEnumerate()      const { return oOps ? oOps->enumerate      : nullptr; }
+    JSFunToStringOp  getOpsFunToString()    const { return oOps ? oOps->funToString    : nullptr; }
 };
 
+static_assert(offsetof(JSClassOps, addProperty) == offsetof(ClassOps, addProperty),
+              "ClassOps and JSClassOps must be consistent");
+static_assert(offsetof(JSClassOps, delProperty) == offsetof(ClassOps, delProperty),
+              "ClassOps and JSClassOps must be consistent");
+static_assert(offsetof(JSClassOps, getProperty) == offsetof(ClassOps, getProperty),
+              "ClassOps and JSClassOps must be consistent");
+static_assert(offsetof(JSClassOps, setProperty) == offsetof(ClassOps, setProperty),
+              "ClassOps and JSClassOps must be consistent");
+static_assert(offsetof(JSClassOps, enumerate) == offsetof(ClassOps, enumerate),
+              "ClassOps and JSClassOps must be consistent");
+static_assert(offsetof(JSClassOps, resolve) == offsetof(ClassOps, resolve),
+              "ClassOps and JSClassOps must be consistent");
+static_assert(offsetof(JSClassOps, mayResolve) == offsetof(ClassOps, mayResolve),
+              "ClassOps and JSClassOps must be consistent");
+static_assert(offsetof(JSClassOps, finalize) == offsetof(ClassOps, finalize),
+              "ClassOps and JSClassOps must be consistent");
+static_assert(offsetof(JSClassOps, call) == offsetof(ClassOps, call),
+              "ClassOps and JSClassOps must be consistent");
+static_assert(offsetof(JSClassOps, construct) == offsetof(ClassOps, construct),
+              "ClassOps and JSClassOps must be consistent");
+static_assert(offsetof(JSClassOps, hasInstance) == offsetof(ClassOps, hasInstance),
+              "ClassOps and JSClassOps must be consistent");
+static_assert(offsetof(JSClassOps, trace) == offsetof(ClassOps, trace),
+              "ClassOps and JSClassOps must be consistent");
+static_assert(sizeof(JSClassOps) == sizeof(ClassOps),
+              "ClassOps and JSClassOps must be consistent");
+
 static_assert(offsetof(JSClass, name) == offsetof(Class, name),
               "Class and JSClass must be consistent");
 static_assert(offsetof(JSClass, flags) == offsetof(Class, flags),
               "Class and JSClass must be consistent");
-static_assert(offsetof(JSClass, addProperty) == offsetof(Class, addProperty),
-              "Class and JSClass must be consistent");
-static_assert(offsetof(JSClass, delProperty) == offsetof(Class, delProperty),
-              "Class and JSClass must be consistent");
-static_assert(offsetof(JSClass, getProperty) == offsetof(Class, getProperty),
-              "Class and JSClass must be consistent");
-static_assert(offsetof(JSClass, setProperty) == offsetof(Class, setProperty),
-              "Class and JSClass must be consistent");
-static_assert(offsetof(JSClass, enumerate) == offsetof(Class, enumerate),
-              "Class and JSClass must be consistent");
-static_assert(offsetof(JSClass, resolve) == offsetof(Class, resolve),
-              "Class and JSClass must be consistent");
-static_assert(offsetof(JSClass, mayResolve) == offsetof(Class, mayResolve),
-              "Class and JSClass must be consistent");
-static_assert(offsetof(JSClass, finalize) == offsetof(Class, finalize),
-              "Class and JSClass must be consistent");
-static_assert(offsetof(JSClass, call) == offsetof(Class, call),
-              "Class and JSClass must be consistent");
-static_assert(offsetof(JSClass, construct) == offsetof(Class, construct),
-              "Class and JSClass must be consistent");
-static_assert(offsetof(JSClass, hasInstance) == offsetof(Class, hasInstance),
-              "Class and JSClass must be consistent");
-static_assert(offsetof(JSClass, trace) == offsetof(Class, trace),
+static_assert(offsetof(JSClass, cOps) == offsetof(Class, cOps),
               "Class and JSClass must be consistent");
 static_assert(sizeof(JSClass) == sizeof(Class),
               "Class and JSClass must be consistent");
 
 static MOZ_ALWAYS_INLINE const JSClass*
 Jsvalify(const Class* c)
 {
     return (const JSClass*)c;
--- a/js/src/asmjs/WasmModule.cpp
+++ b/js/src/asmjs/WasmModule.cpp
@@ -1558,34 +1558,38 @@ Module::createText(JSContext* cx)
 const char*
 Module::profilingLabel(uint32_t funcIndex) const
 {
     MOZ_ASSERT(dynamicallyLinked_);
     MOZ_ASSERT(profilingEnabled_);
     return funcLabels_[funcIndex].get();
 }
 
-const Class WasmModuleObject::class_ = {
-    "WasmModuleObject",
-    JSCLASS_IS_ANONYMOUS | JSCLASS_DELAY_METADATA_BUILDER |
-    JSCLASS_HAS_RESERVED_SLOTS(WasmModuleObject::RESERVED_SLOTS),
+const ClassOps WasmModuleObject::classOps_ = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     WasmModuleObject::finalize,
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     WasmModuleObject::trace
 };
 
+const Class WasmModuleObject::class_ = {
+    "WasmModuleObject",
+    JSCLASS_IS_ANONYMOUS | JSCLASS_DELAY_METADATA_BUILDER |
+    JSCLASS_HAS_RESERVED_SLOTS(WasmModuleObject::RESERVED_SLOTS),
+    &WasmModuleObject::classOps_,
+};
+
 bool
 WasmModuleObject::hasModule() const
 {
     MOZ_ASSERT(is<WasmModuleObject>());
     return !getReservedSlot(MODULE_SLOT).isUndefined();
 }
 
 /* static */ void
--- a/js/src/asmjs/WasmModule.h
+++ b/js/src/asmjs/WasmModule.h
@@ -652,16 +652,18 @@ ExportedFunctionToIndex(JSFunction* fun)
 // An WasmModuleObject is an internal object (i.e., not exposed directly to user
 // code) which traces and owns a wasm::Module. The WasmModuleObject is
 // referenced by the extended slots of exported JSFunctions and serves to keep
 // the wasm::Module alive until its last GC reference is dead.
 
 class WasmModuleObject : public NativeObject
 {
     static const unsigned MODULE_SLOT = 0;
+    static const ClassOps classOps_;
+
     bool hasModule() const;
     static void finalize(FreeOp* fop, JSObject* obj);
     static void trace(JSTracer* trc, JSObject* obj);
   public:
     static const unsigned RESERVED_SLOTS = 1;
     static WasmModuleObject* create(ExclusiveContext* cx);
     bool init(wasm::Module* module);
     wasm::Module& module() const;
--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -623,29 +623,33 @@ static const size_t INITIAL_CHAR_BUFFER_
 
 /******************** Collator ********************/
 
 static void collator_finalize(FreeOp* fop, JSObject* obj);
 
 static const uint32_t UCOLLATOR_SLOT = 0;
 static const uint32_t COLLATOR_SLOTS_COUNT = 1;
 
-static const Class CollatorClass = {
-    js_Object_str,
-    JSCLASS_HAS_RESERVED_SLOTS(COLLATOR_SLOTS_COUNT),
+static const ClassOps CollatorClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     collator_finalize
 };
 
+static const Class CollatorClass = {
+    js_Object_str,
+    JSCLASS_HAS_RESERVED_SLOTS(COLLATOR_SLOTS_COUNT),
+    &CollatorClassOps
+};
+
 #if JS_HAS_TOSOURCE
 static bool
 collator_toSource(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().setString(cx->names().Collator);
     return true;
 }
@@ -1117,29 +1121,33 @@ js::intl_CompareStrings(JSContext* cx, u
 
 /******************** NumberFormat ********************/
 
 static void numberFormat_finalize(FreeOp* fop, JSObject* obj);
 
 static const uint32_t UNUMBER_FORMAT_SLOT = 0;
 static const uint32_t NUMBER_FORMAT_SLOTS_COUNT = 1;
 
-static const Class NumberFormatClass = {
-    js_Object_str,
-    JSCLASS_HAS_RESERVED_SLOTS(NUMBER_FORMAT_SLOTS_COUNT),
+static const ClassOps NumberFormatClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     numberFormat_finalize
 };
 
+static const Class NumberFormatClass = {
+    js_Object_str,
+    JSCLASS_HAS_RESERVED_SLOTS(NUMBER_FORMAT_SLOTS_COUNT),
+    &NumberFormatClassOps
+};
+
 #if JS_HAS_TOSOURCE
 static bool
 numberFormat_toSource(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().setString(cx->names().NumberFormat);
     return true;
 }
@@ -1586,29 +1594,33 @@ js::intl_FormatNumber(JSContext* cx, uns
 
 /******************** DateTimeFormat ********************/
 
 static void dateTimeFormat_finalize(FreeOp* fop, JSObject* obj);
 
 static const uint32_t UDATE_FORMAT_SLOT = 0;
 static const uint32_t DATE_TIME_FORMAT_SLOTS_COUNT = 1;
 
-static const Class DateTimeFormatClass = {
-    js_Object_str,
-    JSCLASS_HAS_RESERVED_SLOTS(DATE_TIME_FORMAT_SLOTS_COUNT),
+static const ClassOps DateTimeFormatClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     dateTimeFormat_finalize
 };
 
+static const Class DateTimeFormatClass = {
+    js_Object_str,
+    JSCLASS_HAS_RESERVED_SLOTS(DATE_TIME_FORMAT_SLOTS_COUNT),
+    &DateTimeFormatClassOps
+};
+
 #if JS_HAS_TOSOURCE
 static bool
 dateTimeFormat_toSource(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().setString(cx->names().DateTimeFormat);
     return true;
 }
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -99,29 +99,33 @@ HashableValue::mark(JSTracer* trc) const
 
 
 /*** MapIterator *********************************************************************************/
 
 namespace {
 
 } /* anonymous namespace */
 
-const Class MapIteratorObject::class_ = {
-    "Map Iterator",
-    JSCLASS_HAS_RESERVED_SLOTS(MapIteratorObject::SlotCount),
+static const ClassOps MapIteratorObjectClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     MapIteratorObject::finalize
 };
 
+const Class MapIteratorObject::class_ = {
+    "Map Iterator",
+    JSCLASS_HAS_RESERVED_SLOTS(MapIteratorObject::SlotCount),
+    &MapIteratorObjectClassOps
+};
+
 const JSFunctionSpec MapIteratorObject::methods[] = {
     JS_SELF_HOSTED_FN("next", "MapIteratorNext", 0, 0),
     JS_FS_END
 };
 
 static inline ValueMap::Range*
 MapIteratorObjectRange(NativeObject* obj)
 {
@@ -254,34 +258,38 @@ MapIteratorObject::createResultPair(JSCo
     AddTypePropertyId(cx, resultPairObj, JSID_VOID, TypeSet::UnknownType());
 
     return resultPairObj;
 }
 
 
 /*** Map *****************************************************************************************/
 
-const Class MapObject::class_ = {
-    "Map",
-    JSCLASS_HAS_PRIVATE |
-    JSCLASS_HAS_CACHED_PROTO(JSProto_Map),
+const ClassOps MapObject::classOps_ = {
     nullptr, // addProperty
     nullptr, // delProperty
     nullptr, // getProperty
     nullptr, // setProperty
     nullptr, // enumerate
     nullptr, // resolve
     nullptr, // mayResolve
     finalize,
     nullptr, // call
     nullptr, // hasInstance
     nullptr, // construct
     mark
 };
 
+const Class MapObject::class_ = {
+    "Map",
+    JSCLASS_HAS_PRIVATE |
+    JSCLASS_HAS_CACHED_PROTO(JSProto_Map),
+    &MapObject::classOps_
+};
+
 const JSPropertySpec MapObject::properties[] = {
     JS_PSG("size", size, 0),
     JS_PS_END
 };
 
 const JSFunctionSpec MapObject::methods[] = {
     JS_FN("get", get, 1, 0),
     JS_FN("has", has, 1, 0),
@@ -865,29 +873,33 @@ class SetIteratorObject : public NativeO
     static inline bool is(HandleValue v);
     inline ValueSet::Range* range();
     inline SetObject::IteratorKind kind() const;
     static bool next_impl(JSContext* cx, const CallArgs& args);
 };
 
 } /* anonymous namespace */
 
-const Class SetIteratorObject::class_ = {
-    "Set Iterator",
-    JSCLASS_HAS_RESERVED_SLOTS(SetIteratorObject::SlotCount),
+static const ClassOps SetIteratorObjectClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     SetIteratorObject::finalize
 };
 
+const Class SetIteratorObject::class_ = {
+    "Set Iterator",
+    JSCLASS_HAS_RESERVED_SLOTS(SetIteratorObject::SlotCount),
+    &SetIteratorObjectClassOps
+};
+
 const JSFunctionSpec SetIteratorObject::methods[] = {
     JS_FN("next", next, 0, 0),
     JS_FS_END
 };
 
 inline ValueSet::Range*
 SetIteratorObject::range()
 {
@@ -1001,34 +1013,38 @@ SetIteratorObject::next(JSContext* cx, u
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod(cx, is, next_impl, args);
 }
 
 
 /*** Set *****************************************************************************************/
 
-const Class SetObject::class_ = {
-    "Set",
-    JSCLASS_HAS_PRIVATE |
-    JSCLASS_HAS_CACHED_PROTO(JSProto_Set),
+const ClassOps SetObject::classOps_ = {
     nullptr, // addProperty
     nullptr, // delProperty
     nullptr, // getProperty
     nullptr, // setProperty
     nullptr, // enumerate
     nullptr, // resolve
     nullptr, // mayResolve
     finalize,
     nullptr, // call
     nullptr, // hasInstance
     nullptr, // construct
     mark
 };
 
+const Class SetObject::class_ = {
+    "Set",
+    JSCLASS_HAS_PRIVATE |
+    JSCLASS_HAS_CACHED_PROTO(JSProto_Set),
+    &SetObject::classOps_
+};
+
 const JSPropertySpec SetObject::properties[] = {
     JS_PSG("size", size, 0),
     JS_PS_END
 };
 
 const JSFunctionSpec SetObject::methods[] = {
     JS_FN("has", has, 1, 0),
     JS_FN("add", add, 1, 0),
--- a/js/src/builtin/MapObject.h
+++ b/js/src/builtin/MapObject.h
@@ -104,16 +104,18 @@ class MapObject : public NativeObject {
     // Set call for public JSAPI exposure. Does not actually return map object
     // as stated in spec, expects caller to return a value. for instance, with
     // webidl maplike/setlike, should return interface object.
     static bool set(JSContext *cx, HandleObject obj, HandleValue key, HandleValue val);
     static bool clear(JSContext *cx, HandleObject obj);
     static bool iterator(JSContext *cx, IteratorKind kind, HandleObject obj, MutableHandleValue iter);
 
   private:
+    static const ClassOps classOps_;
+
     static const JSPropertySpec properties[];
     static const JSFunctionSpec methods[];
     static const JSPropertySpec staticProperties[];
     ValueMap* getData() { return static_cast<ValueMap*>(getPrivate()); }
     static ValueMap & extract(HandleObject o);
     static ValueMap & extract(CallReceiver call);
     static void mark(JSTracer* trc, JSObject* obj);
     static void finalize(FreeOp* fop, JSObject* obj);
@@ -186,19 +188,22 @@ class SetObject : public NativeObject {
     static SetObject* create(JSContext *cx, HandleObject proto = nullptr);
     static uint32_t size(JSContext *cx, HandleObject obj);
     static bool has(JSContext *cx, HandleObject obj, HandleValue key, bool* rval);
     static bool clear(JSContext *cx, HandleObject obj);
     static bool iterator(JSContext *cx, IteratorKind kind, HandleObject obj, MutableHandleValue iter);
     static bool delete_(JSContext *cx, HandleObject obj, HandleValue key, bool *rval);
 
   private:
+    static const ClassOps classOps_;
+
     static const JSPropertySpec properties[];
     static const JSFunctionSpec methods[];
     static const JSPropertySpec staticProperties[];
+
     ValueSet* getData() { return static_cast<ValueSet*>(getPrivate()); }
     static ValueSet & extract(HandleObject o);
     static ValueSet & extract(CallReceiver call);
     static void mark(JSTracer* trc, JSObject* obj);
     static void finalize(FreeOp* fop, JSObject* obj);
     static bool construct(JSContext* cx, unsigned argc, Value* vp);
 
     static bool is(HandleValue v);
--- a/js/src/builtin/ModuleObject.cpp
+++ b/js/src/builtin/ModuleObject.cpp
@@ -524,35 +524,40 @@ void FunctionDeclaration::trace(JSTracer
 {
     TraceEdge(trc, &name, "FunctionDeclaration name");
     TraceEdge(trc, &fun, "FunctionDeclaration fun");
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // ModuleObject
 
-/* static */ const Class
-ModuleObject::class_ = {
-    "Module",
-    JSCLASS_HAS_RESERVED_SLOTS(ModuleObject::SlotCount) |
-    JSCLASS_IS_ANONYMOUS,
+/* static */ const ClassOps
+ModuleObject::classOps_ = {
     nullptr,        /* addProperty */
     nullptr,        /* delProperty */
     nullptr,        /* getProperty */
     nullptr,        /* setProperty */
     nullptr,        /* enumerate   */
     nullptr,        /* resolve     */
     nullptr,        /* mayResolve  */
     ModuleObject::finalize,
     nullptr,        /* call        */
     nullptr,        /* hasInstance */
     nullptr,        /* construct   */
     ModuleObject::trace
 };
 
+/* static */ const Class
+ModuleObject::class_ = {
+    "Module",
+    JSCLASS_HAS_RESERVED_SLOTS(ModuleObject::SlotCount) |
+    JSCLASS_IS_ANONYMOUS,
+    &ModuleObject::classOps_
+};
+
 #define DEFINE_ARRAY_SLOT_ACCESSOR(cls, name, slot)                           \
     ArrayObject&                                                              \
     cls::name() const                                                         \
     {                                                                         \
         return getFixedSlot(cls::slot).toObject().as<ArrayObject>();          \
     }
 
 DEFINE_ARRAY_SLOT_ACCESSOR(ModuleObject, requestedModules, RequestedModulesSlot)
--- a/js/src/builtin/ModuleObject.h
+++ b/js/src/builtin/ModuleObject.h
@@ -259,16 +259,18 @@ class ModuleObject : public NativeObject
 
     void setEvaluated();
     static bool evaluate(JSContext* cx, HandleModuleObject self, MutableHandleValue rval);
 
     static ModuleNamespaceObject* createNamespace(JSContext* cx, HandleModuleObject self,
                                                   HandleObject exports);
 
   private:
+    static const ClassOps classOps_;
+
     static void trace(JSTracer* trc, JSObject* obj);
     static void finalize(js::FreeOp* fop, JSObject* obj);
 
     bool hasScript() const;
     bool hasImportBindings() const;
     FunctionDeclarationVector* functionDeclarations();
 };
 
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -1219,24 +1219,13 @@ static const ClassSpec PlainObjectClassS
     object_methods,
     object_properties,
     FinishObjectClassInit
 };
 
 const Class PlainObject::class_ = {
     js_Object_str,
     JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
-    nullptr,  /* addProperty */
-    nullptr,  /* delProperty */
-    nullptr,  /* getProperty */
-    nullptr,  /* setProperty */
-    nullptr,  /* enumerate */
-    nullptr,  /* resolve */
-    nullptr,  /* mayResolve */
-    nullptr,  /* finalize */
-    nullptr,  /* call */
-    nullptr,  /* hasInstance */
-    nullptr,  /* construct */
-    nullptr,  /* trace */
+    JS_NULL_CLASS_OPS,
     &PlainObjectClassSpec
 };
 
 const Class* const js::ObjectClassPtr = &PlainObject::class_;
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -404,28 +404,17 @@ static const ClassSpec PromiseObjectClas
     promise_static_properties,
     promise_methods
 };
 
 const Class PromiseObject::class_ = {
     "Promise",
     JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) | JSCLASS_HAS_CACHED_PROTO(JSProto_Promise) |
     JSCLASS_HAS_XRAYED_CONSTRUCTOR,
-    nullptr, /* addProperty */
-    nullptr, /* delProperty */
-    nullptr, /* getProperty */
-    nullptr, /* setProperty */
-    nullptr, /* enumerate */
-    nullptr, /* resolve */
-    nullptr, /* mayResolve */
-    nullptr, /* finalize */
-    nullptr, /* call */
-    nullptr, /* hasInstance */
-    nullptr, /* construct */
-    nullptr, /* trace */
+    JS_NULL_CLASS_OPS,
     &PromiseObjectClassSpec
 };
 
 static const ClassSpec PromiseObjectProtoClassSpec = {
     DELEGATED_CLASSSPEC(PromiseObject::class_.spec),
     nullptr,
     nullptr,
     nullptr,
@@ -433,22 +422,11 @@ static const ClassSpec PromiseObjectProt
     nullptr,
     nullptr,
     ClassSpec::IsDelegated
 };
 
 const Class PromiseObject::protoClass_ = {
     "PromiseProto",
     JSCLASS_HAS_CACHED_PROTO(JSProto_Promise),
-    nullptr, /* addProperty */
-    nullptr, /* delProperty */
-    nullptr, /* getProperty */
-    nullptr, /* setProperty */
-    nullptr, /* enumerate */
-    nullptr, /* resolve */
-    nullptr, /* mayResolve */
-    nullptr, /* finalize */
-    nullptr, /* call */
-    nullptr, /* hasInstance */
-    nullptr, /* construct */
-    nullptr, /* trace  */
+    JS_NULL_CLASS_OPS,
     &PromiseObjectProtoClassSpec
 };
--- a/js/src/builtin/RegExp.js
+++ b/js/src/builtin/RegExp.js
@@ -742,17 +742,20 @@ function RegExpSplit(string, limit) {
                 return A;
         }
 
         // Step 19.d.iv.11.
         q = p;
     }
 
     // Steps 20-22.
-    _DefineDataProperty(A, lengthA, Substring(S, p, size - p));
+    if (p >= size)
+        _DefineDataProperty(A, lengthA, "");
+    else
+        _DefineDataProperty(A, lengthA, Substring(S, p, size - p));
 
     // Step 23.
     return A;
 }
 
 // ES6 21.2.5.2.
 // NOTE: This is not RegExpExec (21.2.5.2.1).
 function RegExp_prototype_Exec(string) {
--- a/js/src/builtin/SIMD.cpp
+++ b/js/src/builtin/SIMD.cpp
@@ -184,28 +184,32 @@ template bool js::ToSimdConstant<Bool32x
 template<typename Elem>
 static Elem
 TypedObjectMemory(HandleValue v)
 {
     TypedObject& obj = v.toObject().as<TypedObject>();
     return reinterpret_cast<Elem>(obj.typedMem());
 }
 
-const Class SimdTypeDescr::class_ = {
-    "SIMD",
-    JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
+static const ClassOps SimdTypeDescrClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     TypeDescr::finalize,
-    call
+    SimdTypeDescr::call
+};
+
+const Class SimdTypeDescr::class_ = {
+    "SIMD",
+    JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
+    &SimdTypeDescrClassOps
 };
 
 namespace {
 
 // Define classes (Int8x16Defn, Int16x8Defn, etc.) to group together various
 // properties and so on.
 #define DEFINE_DEFN_(TypeName)                                       \
 class TypeName##Defn {                                               \
@@ -419,25 +423,29 @@ SimdTypeDescr::call(JSContext* cx, unsig
 #undef CASE_CALL_
     MOZ_CRASH("unexpected SIMD descriptor");
     return false;
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // SIMD class
 
-const Class SimdObject::class_ = {
-    "SIMD",
-    JSCLASS_HAS_RESERVED_SLOTS(uint32_t(SimdType::Count)),
+static const ClassOps SimdObjectClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
-    resolve  /* resolve */
+    SimdObject::resolve
+};
+
+const Class SimdObject::class_ = {
+    "SIMD",
+    JSCLASS_HAS_RESERVED_SLOTS(uint32_t(SimdType::Count)),
+    &SimdObjectClassOps
 };
 
 bool
 GlobalObject::initSimdObject(JSContext* cx, Handle<GlobalObject*> global)
 {
     // SIMD relies on the TypedObject module being initialized.
     // In particular, the self-hosted code for array() wants
     // to be able to call GetTypedObjectModule(). It is NOT necessary
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -1405,28 +1405,32 @@ SettleFakePromise(JSContext* cx, unsigne
 static unsigned finalizeCount = 0;
 
 static void
 finalize_counter_finalize(JSFreeOp* fop, JSObject* obj)
 {
     ++finalizeCount;
 }
 
-static const JSClass FinalizeCounterClass = {
-    "FinalizeCounter", JSCLASS_IS_ANONYMOUS,
+static const JSClassOps FinalizeCounterClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     finalize_counter_finalize
 };
 
+static const JSClass FinalizeCounterClass = {
+    "FinalizeCounter", JSCLASS_IS_ANONYMOUS,
+    &FinalizeCounterClassOps
+};
+
 static bool
 MakeFinalizeObserver(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     JSObject* obj = JS_NewObjectWithGivenProto(cx, &FinalizeCounterClass, nullptr);
     if (!obj)
         return false;
@@ -2108,26 +2112,30 @@ class CloneBufferObject : public NativeO
         return CallNonGenericMethod<is, getCloneBuffer_impl>(cx, args);
     }
 
     static void Finalize(FreeOp* fop, JSObject* obj) {
         obj->as<CloneBufferObject>().discard();
     }
 };
 
-const Class CloneBufferObject::class_ = {
-    "CloneBuffer", JSCLASS_HAS_RESERVED_SLOTS(CloneBufferObject::NUM_SLOTS),
+static const ClassOps CloneBufferObjectClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
-    Finalize
+    CloneBufferObject::Finalize
+};
+
+const Class CloneBufferObject::class_ = {
+    "CloneBuffer", JSCLASS_HAS_RESERVED_SLOTS(CloneBufferObject::NUM_SLOTS),
+    &CloneBufferObjectClassOps
 };
 
 const JSPropertySpec CloneBufferObject::props_[] = {
     JS_PSGS("clonebuffer", getCloneBuffer, setCloneBuffer, 0),
     JS_PS_END
 };
 
 static bool
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -210,30 +210,34 @@ const Class js::TypedProto::class_ = {
  * Scalar type objects
  *
  * Scalar type objects like `uint8`, `uint16`, are all instances of
  * the ScalarTypeDescr class. Like all type objects, they have a reserved
  * slot pointing to a TypeRepresentation object, which is used to
  * distinguish which scalar type object this actually is.
  */
 
-const Class js::ScalarTypeDescr::class_ = {
-    "Scalar",
-    JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
+static const ClassOps ScalarTypeDescrClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     TypeDescr::finalize,
     ScalarTypeDescr::call
 };
 
+const Class js::ScalarTypeDescr::class_ = {
+    "Scalar",
+    JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
+    &ScalarTypeDescrClassOps
+};
+
 const JSFunctionSpec js::ScalarTypeDescr::typeObjectMethods[] = {
     JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
     JS_SELF_HOSTED_FN("array", "ArrayShorthand", 1, JSFUN_HAS_REST),
     JS_SELF_HOSTED_FN("equivalent", "TypeDescrEquivalent", 1, 0),
     JS_FS_END
 };
 
 int32_t
@@ -307,30 +311,34 @@ ScalarTypeDescr::call(JSContext* cx, uns
  *
  * Reference type objects like `Any` or `Object` basically work the
  * same way that the scalar type objects do. There is one class with
  * many instances, and each instance has a reserved slot with a
  * TypeRepresentation object, which is used to distinguish which
  * reference type object this actually is.
  */
 
-const Class js::ReferenceTypeDescr::class_ = {
-    "Reference",
-    JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
+static const ClassOps ReferenceTypeDescrClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     TypeDescr::finalize,
     ReferenceTypeDescr::call
 };
 
+const Class js::ReferenceTypeDescr::class_ = {
+    "Reference",
+    JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
+    &ReferenceTypeDescrClassOps
+};
+
 const JSFunctionSpec js::ReferenceTypeDescr::typeObjectMethods[] = {
     JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
     {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"},
     {"equivalent", {nullptr, nullptr}, 1, 0, "TypeDescrEquivalent"},
     JS_FS_END
 };
 
 static const int32_t ReferenceSizes[] = {
@@ -492,32 +500,36 @@ CreatePrototypeObjectForComplexTypeInsta
 {
     RootedObject ctorPrototypePrototype(cx, GetPrototype(cx, ctorPrototype));
     if (!ctorPrototypePrototype)
         return nullptr;
 
     return NewObjectWithGivenProto<TypedProto>(cx, ctorPrototypePrototype, SingletonObject);
 }
 
-const Class ArrayTypeDescr::class_ = {
-    "ArrayType",
-    JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
+static const ClassOps ArrayTypeDescrClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     TypeDescr::finalize,
     nullptr, /* call */
     nullptr, /* hasInstance */
     TypedObject::construct
 };
 
+const Class ArrayTypeDescr::class_ = {
+    "ArrayType",
+    JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
+    &ArrayTypeDescrClassOps
+};
+
 const JSPropertySpec ArrayMetaTypeDescr::typeObjectProperties[] = {
     JS_PS_END
 };
 
 const JSFunctionSpec ArrayMetaTypeDescr::typeObjectMethods[] = {
     {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"},
     JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
     {"equivalent", {nullptr, nullptr}, 1, 0, "TypeDescrEquivalent"},
@@ -717,32 +729,36 @@ js::IsTypedObjectArray(JSObject& obj)
     TypeDescr& d = obj.as<TypedObject>().typeDescr();
     return d.is<ArrayTypeDescr>();
 }
 
 /*********************************
  * StructType class
  */
 
-const Class StructTypeDescr::class_ = {
-    "StructType",
-    JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
+static const ClassOps StructTypeDescrClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     TypeDescr::finalize,
     nullptr, /* call */
     nullptr, /* hasInstance */
     TypedObject::construct
 };
 
+const Class StructTypeDescr::class_ = {
+    "StructType",
+    JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
+    &StructTypeDescrClassOps
+};
+
 const JSPropertySpec StructMetaTypeDescr::typeObjectProperties[] = {
     JS_PS_END
 };
 
 const JSFunctionSpec StructMetaTypeDescr::typeObjectMethods[] = {
     {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"},
     JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
     {"equivalent", {nullptr, nullptr}, 1, 0, "TypeDescrEquivalent"},
@@ -2256,31 +2272,34 @@ const ObjectOps TypedObject::objectOps_ 
     TypedObject::obj_deleteProperty,
     nullptr, nullptr, /* watch/unwatch */
     nullptr,   /* getElements */
     TypedObject::obj_enumerate,
     nullptr, /* thisValue */
 };
 
 #define DEFINE_TYPEDOBJ_CLASS(Name, Trace, flag)         \
-    const Class Name::class_ = {                         \
-        # Name,                                          \
-        Class::NON_NATIVE | flag,                        \
+    static const ClassOps Name##ClassOps = {             \
         nullptr,        /* addProperty */                \
         nullptr,        /* delProperty */                \
         nullptr,        /* getProperty */                \
         nullptr,        /* setProperty */                \
         nullptr,        /* enumerate   */                \
         nullptr,        /* resolve     */                \
         nullptr,        /* mayResolve  */                \
         nullptr,        /* finalize    */                \
         nullptr,        /* call        */                \
         nullptr,        /* hasInstance */                \
         nullptr,        /* construct   */                \
         Trace,                                           \
+    };                                                   \
+    const Class Name::class_ = {                         \
+        # Name,                                          \
+        Class::NON_NATIVE | flag,                        \
+        &Name##ClassOps,                                 \
         JS_NULL_CLASS_SPEC,                              \
         JS_NULL_CLASS_EXT,                               \
         &TypedObject::objectOps_                         \
     }
 
 DEFINE_TYPEDOBJ_CLASS(OutlineTransparentTypedObject, OutlineTypedObject::obj_trace, 0);
 DEFINE_TYPEDOBJ_CLASS(OutlineOpaqueTypedObject,      OutlineTypedObject::obj_trace, 0);
 DEFINE_TYPEDOBJ_CLASS(InlineTransparentTypedObject,  InlineTypedObject::obj_trace,
--- a/js/src/builtin/WeakMapObject.cpp
+++ b/js/src/builtin/WeakMapObject.cpp
@@ -378,34 +378,38 @@ WeakMap_construct(JSContext* cx, unsigne
             }
         }
     }
 
     args.rval().setObject(*obj);
     return true;
 }
 
-const Class WeakMapObject::class_ = {
-    "WeakMap",
-    JSCLASS_HAS_PRIVATE |
-    JSCLASS_HAS_CACHED_PROTO(JSProto_WeakMap),
+static const ClassOps WeakMapObjectClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     WeakMap_finalize,
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     WeakMap_mark
 };
 
+const Class WeakMapObject::class_ = {
+    "WeakMap",
+    JSCLASS_HAS_PRIVATE |
+    JSCLASS_HAS_CACHED_PROTO(JSProto_WeakMap),
+    &WeakMapObjectClassOps
+};
+
 static const JSFunctionSpec weak_map_methods[] = {
     JS_FN("has",    WeakMap_has, 1, 0),
     JS_FN("get",    WeakMap_get, 1, 0),
     JS_FN("delete", WeakMap_delete, 1, 0),
     JS_FN("set",    WeakMap_set, 2, 0),
     JS_FS_END
 };
 
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -541,75 +541,90 @@ static const JSClass sCTypesGlobalClass 
 static const JSClass sCABIClass = {
   "CABI",
   JSCLASS_HAS_RESERVED_SLOTS(CABI_SLOTS)
 };
 
 // Class representing ctypes.{C,Pointer,Array,Struct,Function}Type.prototype.
 // This exists to give said prototypes a class of "CType", and to provide
 // reserved slots for stashing various other prototype objects.
+static const JSClassOps sCTypeProtoClassOps = {
+  nullptr, nullptr, nullptr, nullptr,
+  nullptr, nullptr, nullptr, nullptr,
+  ConstructAbstract, nullptr, ConstructAbstract
+};
 static const JSClass sCTypeProtoClass = {
   "CType",
   JSCLASS_HAS_RESERVED_SLOTS(CTYPEPROTO_SLOTS),
-  nullptr, nullptr, nullptr, nullptr,
-  nullptr, nullptr, nullptr, nullptr,
-  ConstructAbstract, nullptr, ConstructAbstract
+  &sCTypeProtoClassOps
 };
 
 // Class representing ctypes.CData.prototype and the 'prototype' properties
 // of CTypes. This exists to give said prototypes a class of "CData".
 static const JSClass sCDataProtoClass = {
   "CData",
   0
 };
 
-static const JSClass sCTypeClass = {
-  "CType",
-  JSCLASS_HAS_RESERVED_SLOTS(CTYPE_SLOTS),
+static const JSClassOps sCTypeClassOps = {
   nullptr, nullptr, nullptr, nullptr,
   nullptr, nullptr, nullptr, CType::Finalize,
   CType::ConstructData, CType::HasInstance, CType::ConstructData,
   CType::Trace
 };
-
-static const JSClass sCDataClass = {
-  "CData",
-  JSCLASS_HAS_RESERVED_SLOTS(CDATA_SLOTS),
+static const JSClass sCTypeClass = {
+  "CType",
+  JSCLASS_HAS_RESERVED_SLOTS(CTYPE_SLOTS),
+  &sCTypeClassOps
+};
+
+static const JSClassOps sCDataClassOps = {
   nullptr, nullptr, ArrayType::Getter, ArrayType::Setter,
   nullptr, nullptr, nullptr, CData::Finalize,
   FunctionType::Call, nullptr, FunctionType::Call
 };
-
+static const JSClass sCDataClass = {
+  "CData",
+  JSCLASS_HAS_RESERVED_SLOTS(CDATA_SLOTS),
+  &sCDataClassOps
+};
+
+static const JSClassOps sCClosureClassOps = {
+  nullptr, nullptr, nullptr, nullptr,
+  nullptr, nullptr, nullptr, CClosure::Finalize,
+  nullptr, nullptr, nullptr, CClosure::Trace
+};
 static const JSClass sCClosureClass = {
   "CClosure",
   JSCLASS_HAS_RESERVED_SLOTS(CCLOSURE_SLOTS),
-  nullptr, nullptr, nullptr, nullptr,
-  nullptr, nullptr, nullptr, CClosure::Finalize,
-  nullptr, nullptr, nullptr, CClosure::Trace
+  &sCClosureClassOps
 };
 
 /*
  * Class representing the prototype of CDataFinalizer.
  */
 static const JSClass sCDataFinalizerProtoClass = {
   "CDataFinalizer",
   0
 };
 
 /*
  * Class representing instances of CDataFinalizer.
  *
  * Instances of CDataFinalizer have both private data (with type
  * |CDataFinalizer::Private|) and slots (see |CDataFinalizerSlots|).
  */
+static const JSClassOps sCDataFinalizerClassOps = {
+  nullptr, nullptr, nullptr, nullptr,
+  nullptr, nullptr, nullptr, CDataFinalizer::Finalize
+};
 static const JSClass sCDataFinalizerClass = {
   "CDataFinalizer",
   JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(CDATAFINALIZER_SLOTS),
-  nullptr, nullptr, nullptr, nullptr,
-  nullptr, nullptr, nullptr, CDataFinalizer::Finalize
+  &sCDataFinalizerClassOps
 };
 
 
 #define CTYPESFN_FLAGS \
   (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
 
 #define CTYPESCTOR_FLAGS \
   (CTYPESFN_FLAGS | JSFUN_CONSTRUCTOR)
@@ -781,28 +796,31 @@ static const JSClass sInt64ProtoClass = 
   0
 };
 
 static const JSClass sUInt64ProtoClass = {
   "UInt64",
   0
 };
 
+static const JSClassOps sInt64ClassOps = {
+  nullptr, nullptr, nullptr, nullptr,
+  nullptr, nullptr, nullptr, Int64Base::Finalize
+};
+
 static const JSClass sInt64Class = {
   "Int64",
   JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS),
-  nullptr, nullptr, nullptr, nullptr,
-  nullptr, nullptr, nullptr, Int64Base::Finalize
+  &sInt64ClassOps
 };
 
 static const JSClass sUInt64Class = {
   "UInt64",
   JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS),
-  nullptr, nullptr, nullptr, nullptr,
-  nullptr, nullptr, nullptr, Int64Base::Finalize
+  &sInt64ClassOps
 };
 
 static const JSFunctionSpec sInt64StaticFunctions[] = {
   JS_FN("compare", Int64::Compare, 2, CTYPESFN_FLAGS),
   JS_FN("lo", Int64::Lo, 1, CTYPESFN_FLAGS),
   JS_FN("hi", Int64::Hi, 1, CTYPESFN_FLAGS),
   // "join" is defined specially; see InitInt64Class.
   JS_FS_END
--- a/js/src/ctypes/Library.cpp
+++ b/js/src/ctypes/Library.cpp
@@ -27,21 +27,25 @@ namespace Library
 } // namespace Library
 
 /*******************************************************************************
 ** JSObject implementation
 *******************************************************************************/
 
 typedef Rooted<JSFlatString*>    RootedFlatString;
 
+static const JSClassOps sLibraryClassOps = {
+  nullptr, nullptr, nullptr, nullptr,
+  nullptr, nullptr, nullptr, Library::Finalize
+};
+
 static const JSClass sLibraryClass = {
   "Library",
   JSCLASS_HAS_RESERVED_SLOTS(LIBRARY_SLOTS),
-  nullptr, nullptr, nullptr, nullptr,
-  nullptr, nullptr, nullptr, Library::Finalize
+  &sLibraryClassOps
 };
 
 #define CTYPESFN_FLAGS \
   (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
 
 static const JSFunctionSpec sLibraryFunctions[] = {
   JS_FN("close",   Library::Close,   0, CTYPESFN_FLAGS),
   JS_FN("declare", Library::Declare, 0, CTYPESFN_FLAGS),
--- a/js/src/devtools/rootAnalysis/annotations.js
+++ b/js/src/devtools/rootAnalysis/annotations.js
@@ -63,18 +63,18 @@ var ignoreClasses = {
     "_MD_IOVector" : true,
     "malloc_table_t": true, // replace_malloc
     "malloc_hook_table_t": true, // replace_malloc
 };
 
 // Ignore calls through TYPE.FIELD, where TYPE is the class or struct name containing
 // a function pointer field named FIELD.
 var ignoreCallees = {
-    "js::Class.trace" : true,
-    "js::Class.finalize" : true,
+    "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.
     "PLDHashTableOps.hashKey" : true,
     "z_stream_s.zfree" : true,
     "z_stream_s.zalloc" : true,
     "GrGLInterface.fCallback" : true,
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -1263,20 +1263,20 @@ enum class CheckGeneration { DoChecks, N
 template <typename Functor, typename... Args>
 static inline NativeObject*
 CallTraceHook(Functor f, JSTracer* trc, JSObject* obj, CheckGeneration check, Args&&... args)
 {
     const Class* clasp = obj->getClass();
     MOZ_ASSERT(clasp);
     MOZ_ASSERT(obj->isNative() == clasp->isNative());
 
-    if (!clasp->trace)
+    if (!clasp->hasTrace())
         return &obj->as<NativeObject>();
 
-    if (clasp->trace == InlineTypedObject::obj_trace) {
+    if (clasp->isTrace(InlineTypedObject::obj_trace)) {
         Shape** pshape = obj->as<InlineTypedObject>().addressOfShapeFromGC();
         f(pshape, mozilla::Forward<Args>(args)...);
 
         InlineTypedObject& tobj = obj->as<InlineTypedObject>();
         if (tobj.typeDescr().hasTraceList()) {
             VisitTraceList(f, tobj.typeDescr().traceList(), tobj.inlineTypedMem(),
                            mozilla::Forward<Args>(args)...);
         }
@@ -1296,17 +1296,17 @@ CallTraceHook(Functor f, JSTracer* trc, 
         if (layout.traceList()) {
             VisitTraceList(f, layout.traceList(), unboxed.data(),
                            mozilla::Forward<Args>(args)...);
         }
 
         return nullptr;
     }
 
-    clasp->trace(trc, obj);
+    clasp->doTrace(trc, obj);
 
     if (!clasp->isNative())
         return nullptr;
     return &obj->as<NativeObject>();
 }
 
 template <typename F, typename... Args>
 static void
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -170,17 +170,17 @@ js::Nursery::allocateObject(JSContext* c
     MOZ_ASSERT(size >= sizeof(RelocationOverlay));
 
     /*
      * Classes with JSCLASS_SKIP_NURSERY_FINALIZE will not have their finalizer
      * called if they are nursery allocated and not promoted to the tenured
      * heap. The finalizers for these classes must do nothing except free data
      * which was allocated via Nursery::allocateBuffer.
      */
-    MOZ_ASSERT_IF(clasp->finalize, clasp->flags & JSCLASS_SKIP_NURSERY_FINALIZE);
+    MOZ_ASSERT_IF(clasp->hasFinalize(), clasp->flags & JSCLASS_SKIP_NURSERY_FINALIZE);
 
     /* Make the object allocation. */
     JSObject* obj = static_cast<JSObject*>(allocate(size));
     if (!obj)
         return nullptr;
 
     /* If we want external slots, add them. */
     HeapSlot* slots = nullptr;
--- a/js/src/gdb/gdb-tests.cpp
+++ b/js/src/gdb/gdb-tests.cpp
@@ -8,25 +8,29 @@
 
 #include "gdb-tests.h"
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "js/Initialization.h"
 
 using namespace JS;
 
-/* The class of the global object. */
-const JSClass global_class = {
-    "global", JSCLASS_GLOBAL_FLAGS,
+static const JSClassOps global_classOps = {
     nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr,
     JS_GlobalObjectTraceHook
 };
 
+/* The class of the global object. */
+static const JSClass global_class = {
+    "global", JSCLASS_GLOBAL_FLAGS,
+    &global_classOps
+};
+
 template<typename T>
 static inline T*
 checkPtr(T* ptr)
 {
   if (! ptr)
     abort();
   return ptr;
 }
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/auto-regress/bug1263532.js
@@ -0,0 +1,13 @@
+setJitCompilerOption("ion.warmup.trigger", 1);
+function f(g) {
+    for (var i = 0; i < 99; i++) {
+        "abc".match("b.");
+        g();
+    }
+}
+f(function() {});
+f(function() {
+    Object.defineProperty(RegExp.prototype, "sticky", {
+        get: function() {}
+    });
+});
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -922,17 +922,17 @@ IsCacheableSetPropAddSlot(JSContext* cx,
         !propertyShape->hasDefaultSetter() ||
         !propertyShape->writable())
     {
         return false;
     }
 
     // Watch out for resolve or addProperty hooks.
     if (ClassMayResolveId(cx->names(), obj->getClass(), id, obj) ||
-        obj->getClass()->addProperty)
+        obj->getClass()->getAddProperty())
     {
         return false;
     }
 
     size_t chainDepth = 0;
     // Walk up the object prototype chain and ensure that all prototypes are
     // native, and that all prototypes have no setter defined on the property.
     for (JSObject* proto = obj->getProto(); proto; proto = proto->getProto()) {
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -2145,19 +2145,19 @@ CodeGenerator::visitRegExpPrototypeOptim
     Register output = ToRegister(ins->output());
     Register temp = ToRegister(ins->temp());
 
     OutOfLineRegExpPrototypeOptimizable* ool = new(alloc()) OutOfLineRegExpPrototypeOptimizable(ins);
     addOutOfLineCode(ool, ins->mir());
 
     masm.loadJSContext(temp);
     masm.loadPtr(Address(temp, JSContext::offsetOfCompartment()), temp);
-    masm.loadPtr(Address(temp, JSCompartment::offsetOfRegExps()), temp);
-    masm.loadPtr(Address(temp, RegExpCompartment::offsetOfOptimizableRegExpPrototypeShape()),
-                 temp);
+    size_t offset = JSCompartment::offsetOfRegExps() +
+                    RegExpCompartment::offsetOfOptimizableRegExpPrototypeShape();
+    masm.loadPtr(Address(temp, offset), temp);
 
     masm.loadPtr(Address(object, JSObject::offsetOfShape()), output);
     masm.branchPtr(Assembler::NotEqual, output, temp, ool->entry());
     masm.move32(Imm32(0x1), output);
 
     masm.bind(ool->rejoin());
 }
 
@@ -2214,19 +2214,19 @@ CodeGenerator::visitRegExpInstanceOptimi
     Register output = ToRegister(ins->output());
     Register temp = ToRegister(ins->temp());
 
     OutOfLineRegExpInstanceOptimizable* ool = new(alloc()) OutOfLineRegExpInstanceOptimizable(ins);
     addOutOfLineCode(ool, ins->mir());
 
     masm.loadJSContext(temp);
     masm.loadPtr(Address(temp, JSContext::offsetOfCompartment()), temp);
-    masm.loadPtr(Address(temp, JSCompartment::offsetOfRegExps()), temp);
-    masm.loadPtr(Address(temp, RegExpCompartment::offsetOfOptimizableRegExpInstanceShape()),
-                 temp);
+    size_t offset = JSCompartment::offsetOfRegExps() +
+                    RegExpCompartment::offsetOfOptimizableRegExpInstanceShape();
+    masm.loadPtr(Address(temp, offset), temp);
 
     masm.loadPtr(Address(object, JSObject::offsetOfShape()), output);
     masm.branchPtr(Assembler::NotEqual, output, temp, ool->entry());
     masm.move32(Imm32(0x1), output);
 
     masm.bind(ool->rejoin());
 }
 
@@ -10694,29 +10694,38 @@ void
 CodeGenerator::visitIsCallable(LIsCallable* ins)
 {
     Register object = ToRegister(ins->object());
     Register output = ToRegister(ins->output());
 
     OutOfLineIsCallable* ool = new(alloc()) OutOfLineIsCallable(ins);
     addOutOfLineCode(ool, ins->mir());
 
-    Label notFunction, done;
+    Label notFunction, hasCOps, done;
     masm.loadObjClass(object, output);
 
     // Just skim proxies off. Their notion of isCallable() is more complicated.
     masm.branchTestClassIsProxy(true, output, ool->entry());
 
-    // An object is callable iff (is<JSFunction>() || getClass()->call.
+    // An object is callable iff:
+    //   is<JSFunction>() || (getClass()->cOps && getClass()->cOps->call).
     masm.branchPtr(Assembler::NotEqual, output, ImmPtr(&JSFunction::class_), &notFunction);
     masm.move32(Imm32(1), output);
     masm.jump(&done);
 
     masm.bind(&notFunction);
-    masm.cmpPtrSet(Assembler::NonZero, Address(output, offsetof(js::Class, call)), ImmPtr(nullptr), output);
+    masm.branchPtr(Assembler::NonZero, Address(output, offsetof(js::Class, cOps)),
+                   ImmPtr(nullptr), &hasCOps);
+    masm.move32(Imm32(0), output);
+    masm.jump(&done);
+
+    masm.bind(&hasCOps);
+    masm.cmpPtrSet(Assembler::NonZero, Address(output, offsetof(js::ClassOps, call)),
+                   ImmPtr(nullptr), output);
+
     masm.bind(&done);
     masm.bind(ool->rejoin());
 }
 
 void
 CodeGenerator::visitOutOfLineIsCallable(OutOfLineIsCallable* ool)
 {
     LIsCallable* ins = ool->ins();
@@ -10756,37 +10765,45 @@ void
 CodeGenerator::visitIsConstructor(LIsConstructor* ins)
 {
     Register object = ToRegister(ins->object());
     Register output = ToRegister(ins->output());
 
     OutOfLineIsConstructor* ool = new(alloc()) OutOfLineIsConstructor(ins);
     addOutOfLineCode(ool, ins->mir());
 
-    Label notFunction, notConstructor, done;
+    Label notFunction, notConstructor, hasCOps, done;
     masm.loadObjClass(object, output);
 
     // Just skim proxies off. Their notion of isConstructor() is more complicated.
     masm.branchTestClassIsProxy(true, output, ool->entry());
 
     // An object is constructor iff
     //  ((is<JSFunction>() && as<JSFunction>().isConstructor) ||
-    //   getClass()->construct).
+    //   (getClass()->cOps && getClass()->cOps->construct)).
     masm.branchPtr(Assembler::NotEqual, output, ImmPtr(&JSFunction::class_), &notFunction);
     masm.load16ZeroExtend(Address(object, JSFunction::offsetOfFlags()), output);
     masm.and32(Imm32(JSFunction::CONSTRUCTOR), output);
     masm.branchTest32(Assembler::Zero, output, output, &notConstructor);
     masm.move32(Imm32(1), output);
     masm.jump(&done);
     masm.bind(&notConstructor);
     masm.move32(Imm32(0), output);
     masm.jump(&done);
 
     masm.bind(&notFunction);
-    masm.cmpPtrSet(Assembler::NonZero, Address(output, offsetof(js::Class, construct)), ImmPtr(nullptr), output);
+    masm.branchPtr(Assembler::NonZero, Address(output, offsetof(js::Class, cOps)),
+                   ImmPtr(nullptr), &hasCOps);
+    masm.move32(Imm32(0), output);
+    masm.jump(&done);
+
+    masm.bind(&hasCOps);
+    masm.cmpPtrSet(Assembler::NonZero, Address(output, offsetof(js::ClassOps, construct)),
+                   ImmPtr(nullptr), output);
+
     masm.bind(&done);
     masm.bind(ool->rejoin());
 }
 
 void
 CodeGenerator::visitOutOfLineIsConstructor(OutOfLineIsConstructor* ool)
 {
     LIsConstructor* ins = ool->ins();
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -479,17 +479,17 @@ IsCacheableNoProperty(JSObject* obj, JSO
 {
     if (shape)
         return false;
 
     MOZ_ASSERT(!holder);
 
     // Just because we didn't find the property on the object doesn't mean it
     // won't magically appear through various engine hacks.
-    if (obj->getClass()->getProperty)
+    if (obj->getClass()->getGetProperty())
         return false;
 
     // Don't generate missing property ICs if we skipped a non-native object, as
     // lookups may extend beyond the prototype chain (e.g. for DOMProxy
     // proxies).
     JSObject* obj2 = obj;
     while (obj2) {
         if (!obj2->isNative())
@@ -3275,17 +3275,17 @@ IsPropertyAddInlineable(JSContext* cx, N
     // the shape must be the one we just added.
     MOZ_ASSERT(shape == obj->lastProperty());
 
     // Watch out for resolve hooks.
     if (ClassMayResolveId(cx->names(), obj->getClass(), id, obj))
         return false;
 
     // Likewise for an addProperty hook, since we'll need to invoke it.
-    if (obj->getClass()->addProperty)
+    if (obj->getClass()->getAddProperty())
         return false;
 
     if (!obj->nonProxyIsExtensible() || !shape->writable())
         return false;
 
     if (PrototypeChainShadowsPropertyAdd(cx, obj, id))
         return false;
 
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -8054,17 +8054,16 @@ class MRegExpTester
 class MRegExpPrototypeOptimizable
   : public MUnaryInstruction,
     public SingleObjectPolicy::Data
 {
     explicit MRegExpPrototypeOptimizable(MDefinition* object)
       : MUnaryInstruction(object)
     {
         setResultType(MIRType_Boolean);
-        setMovable();
     }
 
   public:
     INSTRUCTION_HEADER(RegExpPrototypeOptimizable)
 
     static MRegExpPrototypeOptimizable* New(TempAllocator& alloc, MDefinition* obj) {
         return new(alloc) MRegExpPrototypeOptimizable(obj);
     }
@@ -8079,17 +8078,16 @@ class MRegExpPrototypeOptimizable
 class MRegExpInstanceOptimizable
   : public MBinaryInstruction,
     public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1> >::Data
 {
     explicit MRegExpInstanceOptimizable(MDefinition* object, MDefinition* proto)
       : MBinaryInstruction(object, proto)
     {
         setResultType(MIRType_Boolean);
-        setMovable();
     }
 
   public:
     INSTRUCTION_HEADER(RegExpInstanceOptimizable)
 
     static MRegExpInstanceOptimizable* New(TempAllocator& alloc, MDefinition* obj,
                                            MDefinition* proto) {
         return new(alloc) MRegExpInstanceOptimizable(obj, proto);
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -2739,17 +2739,17 @@ CheckHasNoSuchProperty(JSContext* cx, JS
     JSObject* curObj = obj;
     while (curObj) {
         if (curObj->isNative()) {
             // Don't handle proto chains with resolve hooks.
             if (ClassMayResolveId(cx->names(), curObj->getClass(), NameToId(name), curObj))
                 return false;
             if (curObj->as<NativeObject>().contains(cx, NameToId(name)))
                 return false;
-            if (curObj->getClass()->getProperty)
+            if (curObj->getClass()->getGetProperty())
                 return false;
         } else if (curObj != obj) {
             // Non-native objects are only handled as the original receiver.
             return false;
         } else if (curObj->is<UnboxedPlainObject>()) {
             if (curObj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, NameToId(name)))
                 return false;
         } else if (curObj->is<UnboxedArrayObject>()) {
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -124,17 +124,16 @@ MSG_DEF(JSMSG_INVALID_DATE,            0
 MSG_DEF(JSMSG_BAD_TOISOSTRING_PROP,    0, JSEXN_TYPEERR, "toISOString property is not callable")
 
 // String
 MSG_DEF(JSMSG_BAD_URI,                 0, JSEXN_URIERR, "malformed URI sequence")
 MSG_DEF(JSMSG_INVALID_NORMALIZE_FORM,  0, JSEXN_RANGEERR, "form must be one of 'NFC', 'NFD', 'NFKC', or 'NFKD'")
 MSG_DEF(JSMSG_NEGATIVE_REPETITION_COUNT, 0, JSEXN_RANGEERR, "repeat count must be non-negative")
 MSG_DEF(JSMSG_NOT_A_CODEPOINT,         1, JSEXN_RANGEERR, "{0} is not a valid code point")
 MSG_DEF(JSMSG_RESULTING_STRING_TOO_LARGE, 0, JSEXN_RANGEERR, "repeat count must be less than infinity and not overflow maximum string size")
-MSG_DEF(JSMSG_DEPRECATED_STRING_CONTAINS, 0, JSEXN_NONE, "String.prototype.contains() is deprecated and will be removed in a future release; use String.prototype.includes() instead")
 
 // Number
 MSG_DEF(JSMSG_BAD_RADIX,               0, JSEXN_RANGEERR, "radix must be an integer at least 2 and no greater than 36")
 MSG_DEF(JSMSG_PRECISION_RANGE,         1, JSEXN_RANGEERR, "precision {0} out of range")
 
 // Function
 MSG_DEF(JSMSG_BAD_APPLY_ARGS,          1, JSEXN_TYPEERR, "second argument to Function.prototype.{0} must be an array")
 MSG_DEF(JSMSG_BAD_FORMAL,              0, JSEXN_SYNTAXERR, "malformed formal parameter")
--- a/js/src/jsapi-tests/testAddPropertyPropcache.cpp
+++ b/js/src/jsapi-tests/testAddPropertyPropcache.cpp
@@ -11,20 +11,24 @@ static int callCount = 0;
 
 static bool
 AddProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue v)
 {
     callCount++;
     return true;
 }
 
+static const JSClassOps AddPropertyClassOps = {
+    AddProperty
+};
+
 static const JSClass AddPropertyClass = {
     "AddPropertyTester",
     0,
-    AddProperty
+    &AddPropertyClassOps
 };
 
 BEGIN_TEST(testAddPropertyHook)
 {
     /*
      * Do the test a bunch of times, because sometimes we seem to randomly
      * miss the propcache.
      */
--- a/js/src/jsapi-tests/testChromeBuffer.cpp
+++ b/js/src/jsapi-tests/testChromeBuffer.cpp
@@ -3,33 +3,37 @@
  * 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 "jsapi-tests/tests.h"
 
 static TestJSPrincipals system_principals(1);
 
-static const JSClass global_class = {
-    "global",
-    JSCLASS_IS_GLOBAL | JSCLASS_GLOBAL_FLAGS,
+static const JSClassOps global_classOps = {
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     JS_GlobalObjectTraceHook
 };
 
+static const JSClass global_class = {
+    "global",
+    JSCLASS_IS_GLOBAL | JSCLASS_GLOBAL_FLAGS,
+    &global_classOps
+};
+
 static JS::PersistentRootedObject trusted_glob;
 static JS::PersistentRootedObject trusted_fun;
 
 static bool
 CallTrusted(JSContext* cx, unsigned argc, JS::Value* vp)
 {
     JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
 
--- a/js/src/jsapi-tests/testClassGetter.cpp
+++ b/js/src/jsapi-tests/testClassGetter.cpp
@@ -16,25 +16,29 @@ static bool test_prop_get( JSContext* cx
 {
     called_test_prop_get++;
     return true;
 }
 
 static bool
 PTest(JSContext* cx, unsigned argc, JS::Value* vp);
 
-static const JSClass ptestClass = {
-    "PTest",
-    JSCLASS_HAS_PRIVATE,
+static const JSClassOps ptestClassOps = {
     nullptr, // addProperty
     nullptr, // delProperty
     test_prop_get,
     nullptr // setProperty
 };
 
+static const JSClass ptestClass = {
+    "PTest",
+    JSCLASS_HAS_PRIVATE,
+    &ptestClassOps
+};
+
 static bool
 PTest(JSContext* cx, unsigned argc, JS::Value* vp)
 {
     JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
     JSObject* obj = JS_NewObjectForConstructor(cx, &ptestClass, args);
     if (!obj)
         return false;
     args.rval().setObject(*obj);
--- a/js/src/jsapi-tests/testFreshGlobalEvalRedefinition.cpp
+++ b/js/src/jsapi-tests/testFreshGlobalEvalRedefinition.cpp
@@ -16,24 +16,28 @@ GlobalEnumerate(JSContext* cx, JS::Handl
 static bool
 GlobalResolve(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* resolvedp)
 {
     return JS_ResolveStandardClass(cx, obj, id, resolvedp);
 }
 
 BEGIN_TEST(testRedefineGlobalEval)
 {
-    static const JSClass cls = {
-        "global", JSCLASS_GLOBAL_FLAGS,
+    static const JSClassOps clsOps = {
         nullptr, nullptr, nullptr, nullptr,
         GlobalEnumerate, GlobalResolve, nullptr, nullptr,
         nullptr, nullptr, nullptr,
         JS_GlobalObjectTraceHook
     };
 
+    static const JSClass cls = {
+        "global", JSCLASS_GLOBAL_FLAGS,
+        &clsOps
+    };
+
     /* Create the global object. */
     JS::CompartmentOptions options;
     JS::Rooted<JSObject*> g(cx, JS_NewGlobalObject(cx, &cls, nullptr, JS::FireOnNewGlobalHook, options));
     if (!g)
         return false;
 
     JSAutoCompartment ac(cx, g);
     JS::Rooted<JS::Value> v(cx);
--- a/js/src/jsapi-tests/testLookup.cpp
+++ b/js/src/jsapi-tests/testLookup.cpp
@@ -68,20 +68,24 @@ document_resolve(JSContext* cx, JS::Hand
             return true;
         }
     }
 
     *resolvedp = false;
     return true;
 }
 
+static const JSClassOps document_classOps = {
+    nullptr, nullptr, nullptr, nullptr,
+    nullptr, document_resolve, nullptr
+};
+
 static const JSClass document_class = {
     "document", 0,
-    nullptr, nullptr, nullptr, nullptr,
-    nullptr, document_resolve, nullptr
+    &document_classOps
 };
 
 BEGIN_TEST(testLookup_bug570195)
 {
     JS::RootedObject obj(cx, JS_NewObject(cx, &document_class));
     CHECK(obj);
     CHECK(JS_DefineProperty(cx, global, "document", obj, 0));
     JS::RootedValue v(cx);
--- a/js/src/jsapi-tests/testNewObject.cpp
+++ b/js/src/jsapi-tests/testNewObject.cpp
@@ -93,22 +93,25 @@ BEGIN_TEST(testNewObject_1)
     CHECK(JS_IsArrayObject(cx, obj, &isArray));
     CHECK(isArray);
     CHECK(JS_GetArrayLength(cx, obj, &len));
     CHECK_EQUAL(len, N);
     CHECK(JS_GetElement(cx, obj, N - 1, &v));
     CHECK(v.isInt32(N - 1));
 
     // With JSClass.construct.
+    static const JSClassOps clsOps = {
+        nullptr, nullptr, nullptr, nullptr,
+        nullptr, nullptr, nullptr, nullptr,
+        nullptr, nullptr, constructHook
+    };
     static const JSClass cls = {
         "testNewObject_1",
         0,
-        nullptr, nullptr, nullptr, nullptr,
-        nullptr, nullptr, nullptr, nullptr,
-        nullptr, nullptr, constructHook
+        &clsOps
     };
     JS::RootedObject ctor(cx, JS_NewObject(cx, &cls));
     CHECK(ctor);
     JS::RootedValue rt2(cx, JS::ObjectValue(*ctor));
     obj = JS_New(cx, ctor, JS::HandleValueArray::subarray(argv, 0, 3));
     CHECK(obj);
     CHECK(JS_GetElement(cx, ctor, 0, &v));
     CHECK(v.isInt32(0));
--- a/js/src/jsapi-tests/testPersistentRooted.cpp
+++ b/js/src/jsapi-tests/testPersistentRooted.cpp
@@ -15,31 +15,35 @@ struct BarkWhenTracedClass {
     static void finalize(JSFreeOp* fop, JSObject* obj) { finalizeCount++; }
     static void trace(JSTracer* trc, JSObject* obj) { traceCount++; }
     static void reset() { finalizeCount = 0; traceCount = 0; }
 };
 
 int BarkWhenTracedClass::finalizeCount;
 int BarkWhenTracedClass::traceCount;
 
-const JSClass BarkWhenTracedClass::class_ = {
-    "BarkWhenTracedClass",
-    0,
+static const JSClassOps BarkWhenTracedClassClassOps = {
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
-    finalize,
+    BarkWhenTracedClass::finalize,
     nullptr,
     nullptr,
     nullptr,
-    trace
+    BarkWhenTracedClass::trace
+};
+
+const JSClass BarkWhenTracedClass::class_ = {
+    "BarkWhenTracedClass",
+    0,
+    &BarkWhenTracedClassClassOps
 };
 
 struct Kennel {
     PersistentRootedObject obj;
     Kennel() { }
     explicit Kennel(JSContext* cx) : obj(cx) { }
     Kennel(JSContext* cx, const HandleObject& woof) : obj(cx, woof) { }
     void init(JSContext* cx, const HandleObject& woof) {
--- a/js/src/jsapi-tests/testPropCache.cpp
+++ b/js/src/jsapi-tests/testPropCache.cpp
@@ -11,20 +11,24 @@ static int g_counter;
 
 static bool
 CounterAdd(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue v)
 {
     g_counter++;
     return true;
 }
 
+static const JSClassOps CounterClassOps = {
+    CounterAdd
+};
+
 static const JSClass CounterClass = {
     "Counter",  /* name */
     0,  /* flags */
-    CounterAdd
+    &CounterClassOps
 };
 
 BEGIN_TEST(testPropCache_bug505798)
 {
     g_counter = 0;
     EXEC("var x = {};");
     CHECK(JS_DefineObject(cx, global, "y", &CounterClass, JSPROP_ENUMERATE));
     EXEC("var arr = [x, y];\n"
--- a/js/src/jsapi-tests/testResolveRecursion.cpp
+++ b/js/src/jsapi-tests/testResolveRecursion.cpp
@@ -8,27 +8,31 @@
 #include "jsapi-tests/tests.h"
 
 /*
  * Test that resolve hook recursion for the same object and property is
  * prevented.
  */
 BEGIN_TEST(testResolveRecursion)
 {
-    static const JSClass my_resolve_class = {
-        "MyResolve",
-        JSCLASS_HAS_PRIVATE,
+    static const JSClassOps my_resolve_classOps = {
         nullptr, // add
         nullptr, // delete
         nullptr, // get
         nullptr, // set
         nullptr, // enumerate
         my_resolve
     };
 
+    static const JSClass my_resolve_class = {
+        "MyResolve",
+        JSCLASS_HAS_PRIVATE,
+        &my_resolve_classOps
+    };
+
     obj1.init(cx, JS_NewObject(cx, &my_resolve_class));
     CHECK(obj1);
     obj2.init(cx, JS_NewObject(cx, &my_resolve_class));
     CHECK(obj2);
     JS_SetPrivate(obj1, this);
     JS_SetPrivate(obj2, this);
 
     JS::RootedValue obj1Val(cx, JS::ObjectValue(*obj1));
@@ -145,33 +149,37 @@ END_TEST(testResolveRecursion)
  */
 BEGIN_TEST(testResolveRecursion_InitStandardClasses)
 {
     CHECK(JS_InitStandardClasses(cx, global));
     return true;
 }
 
 const JSClass* getGlobalClass() override {
-    static const JSClass myGlobalClass = {
-        "testResolveRecursion_InitStandardClasses_myGlobalClass",
-        JSCLASS_GLOBAL_FLAGS,
+    static const JSClassOps myGlobalClassOps = {
         nullptr, // add
         nullptr, // delete
         nullptr, // get
         nullptr, // set
         nullptr, // enumerate
         my_resolve,
         nullptr, // mayResolve
         nullptr, // finalize
         nullptr, // call
         nullptr, // hasInstance
         nullptr, // construct
         JS_GlobalObjectTraceHook
     };
 
+    static const JSClass myGlobalClass = {
+        "testResolveRecursion_InitStandardClasses_myGlobalClass",
+        JSCLASS_GLOBAL_FLAGS,
+        &myGlobalClassOps
+    };
+
     return &myGlobalClass;
 }
 
 static bool
 my_resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* resolvedp)
 {
     MOZ_ASSERT_UNREACHABLE("resolve hook should not be called from InitStandardClasses");
     JS_ReportError(cx, "FAIL");
--- a/js/src/jsapi-tests/testSetProperty.cpp
+++ b/js/src/jsapi-tests/testSetProperty.cpp
@@ -63,17 +63,17 @@ NativeGet(JSContext* cx, JS::HandleObjec
 }
 END_TEST(testSetProperty_NativeGetterStubSetter)
 
 BEGIN_TEST(testSetProperty_InheritedGlobalSetter)
 {
     // This is a JSAPI test because jsapi-test globals do not have a resolve
     // hook and therefore can use the property cache in some cases where the
     // shell can't.
-    MOZ_RELEASE_ASSERT(!JS_GetClass(global)->resolve);
+    MOZ_RELEASE_ASSERT(!JS_GetClass(global)->getResolve());
 
     CHECK(JS_DefineProperty(cx, global, "HOTLOOP", 8, 0));
     EXEC("var n = 0;\n"
          "var global = this;\n"
          "function f() { n++; }\n"
          "Object.defineProperty(Object.prototype, 'x', {set: f});\n"
          "for (var i = 0; i < HOTLOOP; i++)\n"
          "    global.x = i;\n");
--- a/js/src/jsapi-tests/testWeakMap.cpp
+++ b/js/src/jsapi-tests/testWeakMap.cpp
@@ -154,28 +154,17 @@ JSObject* newKey()
 {
     static const js::ClassExtension keyClassExtension = {
         GetKeyDelegate
     };
 
     static const js::Class keyClass = {
         "keyWithDelegate",
         JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1),
-        nullptr, /* addProperty */
-        nullptr, /* delProperty */
-        nullptr, /* getProperty */
-        nullptr, /* setProperty */
-        nullptr, /* enumerate */
-        nullptr, /* resolve */
-        nullptr, /* mayResolve */
-        nullptr, /* finalize */
-        nullptr, /* call */
-        nullptr, /* hasInstance */
-        nullptr, /* construct */
-        nullptr, /* trace */
+        JS_NULL_CLASS_OPS,
         JS_NULL_CLASS_SPEC,
         &keyClassExtension,
         JS_NULL_OBJECT_OPS
     };
 
     JS::RootedObject key(cx, JS_NewObject(cx, Jsvalify(&keyClass)));
     if (!key)
         return nullptr;
@@ -202,36 +191,40 @@ JSObject* newCCW(JS::HandleObject source
         if (!JS_WrapObject(cx, &object))
             return nullptr;
     }
     return object;
 }
 
 JSObject* newDelegate()
 {
-    static const js::ClassExtension delegateClassExtension = {
-        nullptr,
-        DelegateObjectMoved
-    };
-
-    static const js::Class delegateClass = {
-        "delegate",
-        JSCLASS_GLOBAL_FLAGS | JSCLASS_HAS_RESERVED_SLOTS(1),
+    static const js::ClassOps delegateClassOps = {
         nullptr, /* addProperty */
         nullptr, /* delProperty */
         nullptr, /* getProperty */
         nullptr, /* setProperty */
         nullptr, /* enumerate */
         nullptr, /* resolve */
         nullptr, /* mayResolve */
         nullptr, /* finalize */
         nullptr, /* call */
         nullptr, /* hasInstance */
         nullptr, /* construct */
         JS_GlobalObjectTraceHook,
+    };
+
+    static const js::ClassExtension delegateClassExtension = {
+        nullptr,
+        DelegateObjectMoved
+    };
+
+    static const js::Class delegateClass = {
+        "delegate",
+        JSCLASS_GLOBAL_FLAGS | JSCLASS_HAS_RESERVED_SLOTS(1),
+        &delegateClassOps,
         JS_NULL_CLASS_SPEC,
         &delegateClassExtension,
         JS_NULL_OBJECT_OPS
     };
 
     /* Create the global object. */
     JS::CompartmentOptions options;
     options.behaviors().setVersion(JSVERSION_LATEST);
--- a/js/src/jsapi-tests/tests.h
+++ b/js/src/jsapi-tests/tests.h
@@ -221,23 +221,26 @@ class JSAPITest
         fprintf(stderr, "%s:%d:%.*s\n", filename, lineno, (int) msg.length(), msg.begin());
         msgs += msg;
         return false;
     }
 
     JSAPITestString messages() const { return msgs; }
 
     static const JSClass * basicGlobalClass() {
-        static const JSClass c = {
-            "global", JSCLASS_GLOBAL_FLAGS,
+        static const JSClassOps cOps = {
             nullptr, nullptr, nullptr, nullptr,
             nullptr, nullptr, nullptr, nullptr,
             nullptr, nullptr, nullptr,
             JS_GlobalObjectTraceHook
         };
+        static const JSClass c = {
+            "global", JSCLASS_GLOBAL_FLAGS,
+            &cOps
+        };
         return &c;
     }
 
   protected:
     static bool
     print(JSContext* cx, unsigned argc, JS::Value* vp)
     {
         JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -2191,19 +2191,19 @@ DefinePropertyById(JSContext* cx, Handle
 
     // In most places throughout the engine, a property with null getter and
     // not JSPROP_GETTER/SETTER/SHARED has no getter, and the same for setters:
     // it's just a plain old data property. However the JS_Define* APIs use
     // null getter and setter to mean "default to the Class getProperty and
     // setProperty ops".
     if (!(attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
         if (!getter)
-            getter = obj->getClass()->getProperty;
+            getter = obj->getClass()->getGetProperty();
         if (!setter)
-            setter = obj->getClass()->setProperty;
+            setter = obj->getClass()->getSetProperty();
     }
     if (getter == JS_PropertyStub)
         getter = nullptr;
     if (setter == JS_StrictPropertyStub)
         setter = nullptr;
     return DefineProperty(cx, obj, id, value, getter, setter, attrs);
 }
 
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -493,17 +493,18 @@ array_length_setter(JSContext* cx, Handl
                     ObjectOpResult& result)
 {
     if (!obj->is<ArrayObject>()) {
         // This array .length property was found on the prototype
         // chain. Ideally the setter should not have been called, but since
         // we're here, do an impression of SetPropertyByDefining.
         const Class* clasp = obj->getClass();
         return DefineProperty(cx, obj, cx->names().length, vp,
-                              clasp->getProperty, clasp->setProperty, JSPROP_ENUMERATE, result);
+                              clasp->getGetProperty(), clasp->getSetProperty(),
+                              JSPROP_ENUMERATE, result);
     }
 
     Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
     MOZ_ASSERT(arr->lengthIsWritable(),
                "setter shouldn't be called if property is non-writable");
 
     return ArraySetLength(cx, arr, id, JSPROP_PERMANENT, vp, result);
 }
@@ -2695,17 +2696,17 @@ GetIndexedPropertiesInRange(JSContext* c
                             Vector<uint32_t>& indexes, bool* success)
 {
     *success = false;
 
     // First, look for proxies or class hooks that can introduce extra
     // properties.
     JSObject* pobj = obj;
     do {
-        if (!pobj->isNative() || pobj->getClass()->resolve || pobj->getOpsLookupProperty())
+        if (!pobj->isNative() || pobj->getClass()->getResolve() || pobj->getOpsLookupProperty())
             return true;
     } while ((pobj = pobj->getProto()));
 
     // Collect indexed property names.
     pobj = obj;
     do {
         // Append dense elements.
         NativeObject* nativeObj = &pobj->as<NativeObject>();
@@ -3280,41 +3281,45 @@ array_proto_finish(JSContext* cx, JS::Ha
         return false;
     }
 
     RootedId id(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().get(JS::SymbolCode::unscopables)));
     value.setObject(*unscopables);
     return DefineProperty(cx, proto, id, value, nullptr, nullptr, JSPROP_READONLY);
 }
 
+static const ClassOps ArrayObjectClassOps = {
+    array_addProperty,
+    nullptr, /* delProperty */
+    nullptr, /* getProperty */
+    nullptr, /* setProperty */
+    nullptr, /* enumerate */
+    nullptr, /* resolve */
+    nullptr, /* mayResolve */
+    nullptr, /* finalize */
+    nullptr, /* call */
+    nullptr, /* hasInstance */
+    nullptr, /* construct */
+    nullptr, /* trace */
+};
+
 static const ClassSpec ArrayObjectClassSpec = {
     GenericCreateConstructor<ArrayConstructor, 1, AllocKind::FUNCTION, &jit::JitInfo_Array>,
     CreateArrayPrototype,
     array_static_methods,
     array_static_props,
     array_methods,
     nullptr,
     array_proto_finish
 };
 
 const Class ArrayObject::class_ = {
     "Array",
     JSCLASS_HAS_CACHED_PROTO(JSProto_Array) | JSCLASS_DELAY_METADATA_BUILDER,
-    array_addProperty,
-    nullptr, /* delProperty */
-    nullptr, /* getProperty */
-    nullptr, /* setProperty */
-    nullptr, /* enumerate */
-    nullptr, /* resolve */
-    nullptr, /* mayResolve */
-    nullptr, /* finalize */
-    nullptr, /* call */
-    nullptr, /* hasInstance */
-    nullptr, /* construct */
-    nullptr, /* trace */
+    &ArrayObjectClassOps,
     &ArrayObjectClassSpec
 };
 
 /*
  * Array allocation functions.
  */
 
 static inline bool
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -3281,28 +3281,17 @@ static const ClassSpec DateObjectClassSp
     nullptr,
     FinishDateClassInit
 };
 
 const Class DateObject::class_ = {
     js_Date_str,
     JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Date),
-    nullptr, /* addProperty */
-    nullptr, /* delProperty */
-    nullptr, /* getProperty */
-    nullptr, /* setProperty */
-    nullptr, /* enumerate */
-    nullptr, /* resolve */
-    nullptr, /* mayResolve */
-    nullptr, /* finalize */
-    nullptr, /* call */
-    nullptr, /* hasInstance */
-    nullptr, /* construct */
-    nullptr, /* trace */
+    JS_NULL_CLASS_OPS,
     &DateObjectClassSpec
 };
 
 static const ClassSpec DateObjectProtoClassSpec = {
     DELEGATED_CLASSSPEC(DateObject::class_.spec),
     nullptr,
     nullptr,
     nullptr,
@@ -3310,28 +3299,17 @@ static const ClassSpec DateObjectProtoCl
     nullptr,
     nullptr,
     ClassSpec::IsDelegated
 };
 
 const Class DateObject::protoClass_ = {
     js_Object_str,
     JSCLASS_HAS_CACHED_PROTO(JSProto_Date),
-    nullptr, /* addProperty */
-    nullptr, /* delProperty */
-    nullptr, /* getProperty */
-    nullptr, /* setProperty */
-    nullptr, /* enumerate */
-    nullptr, /* resolve */
-    nullptr, /* mayResolve */
-    nullptr, /* finalize */
-    nullptr, /* call */
-    nullptr, /* hasInstance */
-    nullptr, /* construct */
-    nullptr, /* trace  */
+    JS_NULL_CLASS_OPS,
     &DateObjectProtoClassSpec
 };
 
 JSObject*
 js::NewDateObjectMsec(JSContext* cx, ClippedTime t, HandleObject proto /* = nullptr */)
 {
     JSObject* obj = NewObjectWithClassProto(cx, &DateObject::class_, proto);
     if (!obj)
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -60,33 +60,37 @@ static const JSPropertySpec exception_pr
 static const JSFunctionSpec exception_methods[] = {
 #if JS_HAS_TOSOURCE
     JS_FN(js_toSource_str, exn_toSource, 0, 0),
 #endif
     JS_SELF_HOSTED_FN(js_toString_str, "ErrorToString", 0,0),
     JS_FS_END
 };
 
+static const ClassOps ErrorObjectClassOps = {
+    nullptr,                 /* addProperty */
+    nullptr,                 /* delProperty */
+    nullptr,                 /* getProperty */
+    nullptr,                 /* setProperty */
+    nullptr,                 /* enumerate */
+    nullptr,                 /* resolve */
+    nullptr,                 /* mayResolve */
+    exn_finalize,
+    nullptr,                 /* call        */
+    nullptr,                 /* hasInstance */
+    nullptr,                 /* construct   */
+    nullptr,                 /* trace       */
+};
+
 #define IMPLEMENT_ERROR_CLASS(name, classSpecPtr) \
     { \
         js_Error_str, /* yes, really */ \
         JSCLASS_HAS_CACHED_PROTO(JSProto_##name) | \
         JSCLASS_HAS_RESERVED_SLOTS(ErrorObject::RESERVED_SLOTS), \
-        nullptr,                 /* addProperty */ \
-        nullptr,                 /* delProperty */ \
-        nullptr,                 /* getProperty */ \
-        nullptr,                 /* setProperty */ \
-        nullptr,                 /* enumerate */ \
-        nullptr,                 /* resolve */ \
-        nullptr,                 /* mayResolve */ \
-        exn_finalize, \
-        nullptr,                 /* call        */ \
-        nullptr,                 /* hasInstance */ \
-        nullptr,                 /* construct   */ \
-        nullptr,                 /* trace       */ \
+        &ErrorObjectClassOps, \
         classSpecPtr \
     }
 
 const ClassSpec
 ErrorObject::errorClassSpec_ = {
     ErrorObject::createConstructor,
     ErrorObject::createProto,
     nullptr,
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -309,16 +309,17 @@ struct JSFunctionSpecWithHelp {
 #define JS_FS_HELP_END                                                        \
     {nullptr, nullptr, 0, 0, nullptr, nullptr}
 
 extern JS_FRIEND_API(bool)
 JS_DefineFunctionsWithHelp(JSContext* cx, JS::HandleObject obj, const JSFunctionSpecWithHelp* fs);
 
 namespace js {
 
+extern JS_FRIEND_DATA(const js::ClassOps) ProxyClassOps;
 extern JS_FRIEND_DATA(const js::ClassExtension) ProxyClassExtension;
 extern JS_FRIEND_DATA(const js::ObjectOps) ProxyObjectOps;
 
 /*
  * Helper Macros for creating JSClasses that function as proxies.
  *
  * NB: The macro invocation must be surrounded by braces, so as to
  *     allow for potential JSClass extensions.
@@ -331,28 +332,17 @@ extern JS_FRIEND_DATA(const js::ObjectOp
 
 #define PROXY_CLASS_WITH_EXT(name, flags, extPtr)                                       \
     {                                                                                   \
         name,                                                                           \
         js::Class::NON_NATIVE |                                                         \
             JSCLASS_IS_PROXY |                                                          \
             JSCLASS_DELAY_METADATA_BUILDER |                                            \
             flags,                                                                      \
-        nullptr,                 /* addProperty */                                      \
-        nullptr,                 /* delProperty */                                      \
-        nullptr,                 /* getProperty */                                      \
-        nullptr,                 /* setProperty */                                      \
-        nullptr,                 /* enumerate */                                        \
-        nullptr,                 /* resolve */                                          \
-        nullptr,                 /* mayResolve */                                       \
-        js::proxy_Finalize,      /* finalize    */                                      \
-        nullptr,                 /* call        */                                      \
-        js::proxy_HasInstance,   /* hasInstance */                                      \
-        nullptr,                 /* construct   */                                      \
-        js::proxy_Trace,         /* trace       */                                      \
+        &js::ProxyClassOps,                                                             \
         JS_NULL_CLASS_SPEC,                                                             \
         extPtr,                                                                         \
         &js::ProxyObjectOps                                                             \
     }
 
 #define PROXY_CLASS_DEF(name, flags) \
   PROXY_CLASS_WITH_EXT(name, flags, &js::ProxyClassExtension)
 
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -829,40 +829,44 @@ CreateFunctionPrototype(JSContext* cx, J
     if (!throwTypeError || !PreventExtensions(cx, throwTypeError))
         return nullptr;
 
     self->setThrowTypeError(throwTypeError);
 
     return functionProto;
 }
 
+static const ClassOps JSFunctionClassOps = {
+    nullptr,                 /* addProperty */
+    nullptr,                 /* delProperty */
+    nullptr,                 /* getProperty */
+    nullptr,                 /* setProperty */
+    fun_enumerate,
+    fun_resolve,
+    fun_mayResolve,
+    nullptr,                 /* finalize    */
+    nullptr,                 /* call        */
+    fun_hasInstance,
+    nullptr,                 /* construct   */
+    fun_trace,
+};
+
 static const ClassSpec JSFunctionClassSpec = {
     CreateFunctionConstructor,
     CreateFunctionPrototype,
     nullptr,
     nullptr,
     function_methods,
     function_properties
 };
 
 const Class JSFunction::class_ = {
     js_Function_str,
     JSCLASS_HAS_CACHED_PROTO(JSProto_Function),
-    nullptr,                 /* addProperty */
-    nullptr,                 /* delProperty */
-    nullptr,                 /* getProperty */
-    nullptr,                 /* setProperty */
-    fun_enumerate,
-    fun_resolve,
-    fun_mayResolve,
-    nullptr,                 /* finalize    */
-    nullptr,                 /* call        */
-    fun_hasInstance,
-    nullptr,                 /* construct   */
-    fun_trace,
+    &JSFunctionClassOps,
     &JSFunctionClassSpec
 };
 
 const Class* const js::FunctionClassPtr = &JSFunction::class_;
 
 /* Find the body of a function (not including braces). */
 bool
 js::FindBody(JSContext* cx, HandleFunction fun, HandleLinearString src, size_t* bodyStart,
@@ -2105,18 +2109,18 @@ js::DefineFunction(JSContext* cx, Handle
          * the defined property's attributes. This allows us to encode another,
          * internal flag using the same bit, JSFUN_EXPR_CLOSURE -- see jsfun.h
          * for more on this.
          */
         flags &= ~JSFUN_STUB_GSOPS;
         gop = nullptr;
         sop = nullptr;
     } else {
-        gop = obj->getClass()->getProperty;
-        sop = obj->getClass()->setProperty;
+        gop = obj->getClass()->getGetProperty();
+        sop = obj->getClass()->getSetProperty();
         MOZ_ASSERT(gop != JS_PropertyStub);
         MOZ_ASSERT(sop != JS_StrictPropertyStub);
     }
 
     RootedAtom atom(cx, IdToFunctionName(cx, id));
     if (!atom)
         return nullptr;
 
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -7195,17 +7195,18 @@ AutoDisableProxyCheck::~AutoDisableProxy
 {
     gc.enableStrictProxyChecking();
 }
 
 JS_FRIEND_API(void)
 JS::AssertGCThingMustBeTenured(JSObject* obj)
 {
     MOZ_ASSERT(obj->isTenured() &&
-               (!IsNurseryAllocable(obj->asTenured().getAllocKind()) || obj->getClass()->finalize));
+               (!IsNurseryAllocable(obj->asTenured().getAllocKind()) ||
+                obj->getClass()->hasFinalize()));
 }
 
 JS_FRIEND_API(void)
 JS::AssertGCThingIsNotAnObjectSubclass(Cell* cell)
 {
     MOZ_ASSERT(cell);
     MOZ_ASSERT(cell->getTraceKind() != JS::TraceKind::Object);
 }
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -150,17 +150,17 @@ CanBeFinalizedInBackground(AllocKind kin
     /* If the class has no finalizer or a finalizer that is safe to call on
      * a different thread, we change the alloc kind. For example,
      * AllocKind::OBJECT0 calls the finalizer on the main thread,
      * AllocKind::OBJECT0_BACKGROUND calls the finalizer on the gcHelperThread.
      * IsBackgroundFinalized is called to prevent recursively incrementing
      * the alloc kind; kind may already be a background finalize kind.
      */
     return (!IsBackgroundFinalized(kind) &&
-            (!clasp->finalize || (clasp->flags & JSCLASS_BACKGROUND_FINALIZE)));
+            (!clasp->hasFinalize() || (clasp->flags & JSCLASS_BACKGROUND_FINALIZE)));
 }
 
 /* Capacity for slotsToThingKind */
 const size_t SLOTS_TO_THING_KIND_LIMIT = 17;
 
 extern const AllocKind slotsToThingKind[];
 
 /* Get the best kind to use when making an object with the given slot count. */
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -376,17 +376,17 @@ Snapshot(JSContext* cx, HandleObject pob
 
                 if (pobj->isNative()) {
                     if (!EnumerateNativeProperties(cx, pobj.as<NativeObject>(), flags, ht, props))
                         return false;
                 }
             }
         } else if (pobj->isNative()) {
             // Give the object a chance to resolve all lazy properties
-            if (JSEnumerateOp enumerate = pobj->getClass()->enumerate) {
+            if (JSEnumerateOp enumerate = pobj->getClass()->getEnumerate()) {
                 if (!enumerate(cx, pobj.as<NativeObject>()))
                     return false;
             }
             if (!EnumerateNativeProperties(cx, pobj.as<NativeObject>(), flags, ht, props))
                 return false;
         } else if (pobj->is<ProxyObject>()) {
             AutoIdVector proxyProps(cx);
             if (flags & JSITER_HIDDEN || flags & JSITER_SYMBOLS) {
@@ -793,17 +793,17 @@ static inline bool
 CanCacheIterableObject(JSContext* cx, JSObject* obj)
 {
     if (!CanCompareIterableObjectToCache(obj))
         return false;
     if (obj->isNative()) {
         if (obj->is<TypedArrayObject>() ||
             obj->hasUncacheableProto() ||
             obj->getOpsEnumerate() ||
-            obj->getClass()->enumerate ||
+            obj->getClass()->getEnumerate() ||
             obj->as<NativeObject>().containsPure(cx->names().iteratorIntrinsic))
         {
             return false;
         }
     }
     return true;
 }
 
@@ -1079,35 +1079,39 @@ PropertyIteratorObject::trace(JSTracer* 
 
 void
 PropertyIteratorObject::finalize(FreeOp* fop, JSObject* obj)
 {
     if (NativeIterator* ni = obj->as<PropertyIteratorObject>().getNativeIterator())
         fop->free_(ni);
 }
 
-const Class PropertyIteratorObject::class_ = {
-    "Iterator",
-    JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator) |
-    JSCLASS_HAS_PRIVATE |
-    JSCLASS_BACKGROUND_FINALIZE,
+const ClassOps PropertyIteratorObject::classOps_ = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     finalize,
     nullptr, /* call        */
     nullptr, /* hasInstance */
     nullptr, /* construct   */
     trace
 };
 
+const Class PropertyIteratorObject::class_ = {
+    "Iterator",
+    JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator) |
+    JSCLASS_HAS_PRIVATE |
+    JSCLASS_BACKGROUND_FINALIZE,
+    &PropertyIteratorObject::classOps_
+};
+
 static const Class ArrayIteratorPrototypeClass = {
     "Array Iterator",
     0
 };
 
 enum {
     ArrayIteratorSlotIteratedObject,
     ArrayIteratorSlotNextIndex,
@@ -1420,31 +1424,35 @@ js::IteratorMore(JSContext* cx, HandleOb
 
 static bool
 stopiter_hasInstance(JSContext* cx, HandleObject obj, MutableHandleValue v, bool* bp)
 {
     *bp = JS_IsStopIteration(v);
     return true;
 }
 
-const Class StopIterationObject::class_ = {
-    "StopIteration",
-    JSCLASS_HAS_CACHED_PROTO(JSProto_StopIteration),
+static const ClassOps StopIterationObjectClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     nullptr, /* finalize */
     nullptr, /* call */
     stopiter_hasInstance
 };
 
+const Class StopIterationObject::class_ = {
+    "StopIteration",
+    JSCLASS_HAS_CACHED_PROTO(JSProto_StopIteration),
+    &StopIterationObjectClassOps
+};
+
 static const JSFunctionSpec iterator_proto_methods[] = {
     JS_SELF_HOSTED_SYM_FN(iterator, "IteratorIdentity", 0, 0),
     JS_FS_END
 };
 
 /* static */ bool
 GlobalObject::initIteratorProto(JSContext* cx, Handle<GlobalObject*> global)
 {
--- a/js/src/jsiter.h
+++ b/js/src/jsiter.h
@@ -115,16 +115,18 @@ struct NativeIterator
 
     static void destroy(NativeIterator* iter) {
         js_free(iter);
     }
 };
 
 class PropertyIteratorObject : public NativeObject
 {
+    static const ClassOps classOps_;
+
   public:
     static const Class class_;
 
     NativeIterator* getNativeIterator() const {
         return static_cast<js::NativeIterator*>(getPrivate());
     }
     void setNativeIterator(js::NativeIterator* ni) {
         setPrivate(ni);
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -1850,18 +1850,18 @@ js::InitClass(JSContext* cx, HandleObjec
               const Class* clasp, Native constructor, unsigned nargs,
               const JSPropertySpec* ps, const JSFunctionSpec* fs,
               const JSPropertySpec* static_ps, const JSFunctionSpec* static_fs,
               NativeObject** ctorp, AllocKind ctorKind)
 {
     RootedObject protoProto(cx, protoProto_);
 
     /* Check function pointer members. */
-    MOZ_ASSERT(clasp->getProperty != JS_PropertyStub);
-    MOZ_ASSERT(clasp->setProperty != JS_StrictPropertyStub);
+    MOZ_ASSERT(clasp->getGetProperty() != JS_PropertyStub);
+    MOZ_ASSERT(clasp->getSetProperty() != JS_StrictPropertyStub);
 
     RootedAtom atom(cx, Atomize(cx, clasp->name, strlen(clasp->name)));
     if (!atom)
         return nullptr;
 
     /*
      * All instances of the class will inherit properties from the prototype
      * object we are about to create (in DefineConstructorAndPrototype), which
@@ -2107,34 +2107,34 @@ JSObject::isConstructor() const
     return constructHook() != nullptr;
 }
 
 JSNative
 JSObject::callHook() const
 {
     const js::Class* clasp = getClass();
 
-    if (clasp->call)
-        return clasp->call;
+    if (JSNative call = clasp->getCall())
+        return call;
 
     if (is<js::ProxyObject>()) {
         const js::ProxyObject& p = as<js::ProxyObject>();
         if (p.handler()->isCallable(const_cast<JSObject*>(this)))
             return js::proxy_Call;
     }
     return nullptr;
 }
 
 JSNative
 JSObject::constructHook() const
 {
     const js::Class* clasp = getClass();
 
-    if (clasp->construct)
-        return clasp->construct;
+    if (JSNative construct = clasp->getConstruct())
+        return construct;
 
     if (is<js::ProxyObject>()) {
         const js::ProxyObject& p = as<js::ProxyObject>();
         if (p.handler()->isConstructor(const_cast<JSObject*>(this)))
             return js::proxy_Construct;
     }
     return nullptr;
 }
@@ -2939,18 +2939,18 @@ DefineFunctionFromSpec(JSContext* cx, Ha
     SetterOp sop;
     if (flags & JSFUN_STUB_GSOPS) {
         // JSFUN_STUB_GSOPS is a request flag only, not stored in fun->flags or
         // the defined property's attributes.
         flags &= ~JSFUN_STUB_GSOPS;
         gop = nullptr;
         sop = nullptr;
     } else {
-        gop = obj->getClass()->getProperty;
-        sop = obj->getClass()->setProperty;
+        gop = obj->getClass()->getGetProperty();
+        sop = obj->getClass()->getSetProperty();
         MOZ_ASSERT(gop != JS_PropertyStub);
         MOZ_ASSERT(sop != JS_StrictPropertyStub);
     }
 
     RootedId id(cx);
     if (!PropertySpecNameToId(cx, fs->name, &id))
         return false;
 
@@ -3904,18 +3904,18 @@ JSObject::traceChildren(JSTracer* trc)
                        nobj->getDenseInitializedLength(),
                        static_cast<HeapSlot*>(nobj->getDenseElementsAllowCopyOnWrite()),
                        "objectElements");
         } while (false);
     }
 
     // Call the trace hook at the end so that during a moving GC the trace hook
     // will see updated fields and slots.
-    if (clasp->trace)
-        clasp->trace(trc, this);
+    if (clasp->hasTrace())
+        clasp->doTrace(trc, this);
 }
 
 static JSAtom*
 displayAtomFromObjectGroup(ObjectGroup& group)
 {
     TypeNewScript* script = group.newScript();
     if (!script)
         return nullptr;
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -1093,17 +1093,17 @@ extern const char js_lookupSetter_str[];
 
 namespace js {
 
 inline gc::InitialHeap
 GetInitialHeap(NewObjectKind newKind, const Class* clasp)
 {
     if (newKind != GenericObject)
         return gc::TenuredHeap;
-    if (clasp->finalize && !(clasp->flags & JSCLASS_SKIP_NURSERY_FINALIZE))
+    if (clasp->hasFinalize() && !(clasp->flags & JSCLASS_SKIP_NURSERY_FINALIZE))
         return gc::TenuredHeap;
     return gc::DefaultHeap;
 }
 
 bool
 NewObjectWithTaggedProtoIsCachable(ExclusiveContext* cxArg, Handle<TaggedProto> proto,
                                    NewObjectKind newKind, const Class* clasp);
 
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -73,18 +73,18 @@ JSObject::finalize(js::FreeOp* fop)
     MOZ_ASSERT(isTenured());
     if (!IsBackgroundFinalized(asTenured().getAllocKind())) {
         /* Assert we're on the main thread. */
         MOZ_ASSERT(CurrentThreadCanAccessRuntime(fop->runtime()));
     }
 #endif
 
     const js::Class* clasp = getClass();
-    if (clasp->finalize)
-        clasp->finalize(fop, this);
+    if (clasp->hasFinalize())
+        clasp->doFinalize(fop, this);
 
     if (!clasp->isNative())
         return;
 
     js::NativeObject* nobj = &as<js::NativeObject>();
 
     if (nobj->hasDynamicSlots())
         fop->free_(nobj->slots_);
@@ -316,17 +316,17 @@ JSObject::create(js::ExclusiveContext* c
 {
     MOZ_ASSERT(shape && group);
     MOZ_ASSERT(group->clasp() == shape->getObjectClass());
     MOZ_ASSERT(group->clasp() != &js::ArrayObject::class_);
     MOZ_ASSERT_IF(!js::ClassCanHaveFixedData(group->clasp()),
                   js::gc::GetGCKindSlots(kind, group->clasp()) == shape->numFixedSlots());
     MOZ_ASSERT_IF(group->clasp()->flags & JSCLASS_BACKGROUND_FINALIZE,
                   IsBackgroundFinalized(kind));
-    MOZ_ASSERT_IF(group->clasp()->finalize,
+    MOZ_ASSERT_IF(group->clasp()->hasFinalize(),
                   heap == js::gc::TenuredHeap ||
                   (group->clasp()->flags & JSCLASS_SKIP_NURSERY_FINALIZE));
     MOZ_ASSERT_IF(group->hasUnanalyzedPreliminaryObjects(),
                   heap == js::gc::TenuredHeap);
     MOZ_ASSERT(!cx->compartment()->hasObjectPendingMetadata());
 
     // Non-native classes cannot have reserved slots or private data, and the
     // objects can't have any fixed slots, for compatibility with
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1697,32 +1697,36 @@ ScriptSourceObject::finalize(FreeOp* fop
     // source object.
     if (fop->runtime()->lcovOutput.isEnabled())
         sso->compartment()->lcovOutput.collectSourceFile(sso->compartment(), sso);
 
     sso->source()->decref();
     sso->setReservedSlot(SOURCE_SLOT, PrivateValue(nullptr));
 }
 
-const Class ScriptSourceObject::class_ = {
-    "ScriptSource",
-    JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) |
-    JSCLASS_IS_ANONYMOUS,
+static const ClassOps ScriptSourceObjectClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
-    finalize,
+    ScriptSourceObject::finalize,
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
-    trace
+    ScriptSourceObject::trace
+};
+
+const Class ScriptSourceObject::class_ = {
+    "ScriptSource",
+    JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) |
+    JSCLASS_IS_ANONYMOUS,
+    &ScriptSourceObjectClassOps
 };
 
 ScriptSourceObject*
 ScriptSourceObject::create(ExclusiveContext* cx, ScriptSource* source)
 {
     RootedObject object(cx, NewObjectWithGivenProto(cx, &class_, nullptr));
     if (!object)
         return nullptr;
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -875,16 +875,18 @@ struct CompressedSourceHasher
                !memcmp(a->compressedData(), b->compressedData(), a->compressedBytes());
     }
 };
 
 typedef HashSet<ScriptSource*, CompressedSourceHasher, SystemAllocPolicy> CompressedSourceSet;
 
 class ScriptSourceObject : public NativeObject
 {
+    static const ClassOps classOps_;
+
   public:
     static const Class class_;
 
     static void trace(JSTracer* trc, JSObject* obj);
     static void finalize(FreeOp* fop, JSObject* obj);
     static ScriptSourceObject* create(ExclusiveContext* cx, ScriptSource* source);
 
     // Initialize those properties of this ScriptSourceObject whose values
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -425,29 +425,33 @@ str_resolve(JSContext* cx, HandleObject 
         {
             return false;
         }
         *resolvedp = true;
     }
     return true;
 }
 
-const Class StringObject::class_ = {
-    js_String_str,
-    JSCLASS_HAS_RESERVED_SLOTS(StringObject::RESERVED_SLOTS) |
-    JSCLASS_HAS_CACHED_PROTO(JSProto_String),
+static const ClassOps StringObjectClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     str_enumerate,
     str_resolve,
     str_mayResolve
 };
 
+const Class StringObject::class_ = {
+    js_String_str,
+    JSCLASS_HAS_RESERVED_SLOTS(StringObject::RESERVED_SLOTS) |
+    JSCLASS_HAS_CACHED_PROTO(JSProto_String),
+    &StringObjectClassOps
+};
+
 /*
  * Returns a JSString * for the |this| value associated with 'call', or throws
  * a TypeError if |this| is null or undefined.  This algorithm is the same as
  * calling CheckObjectCoercible(this), then returning ToString(this), as all
  * String.prototype.* methods do (other than toString and valueOf).
  */
 static MOZ_ALWAYS_INLINE JSString*
 ThisToStringForStringProto(JSContext* cx, CallReceiver call)
@@ -1577,29 +1581,16 @@ js::str_includes(JSContext* cx, unsigned
     JSLinearString* text = str->ensureLinear(cx);
     if (!text)
         return false;
 
     args.rval().setBoolean(StringMatch(text, searchStr, start) != -1);
     return true;
 }
 
-/* TODO: remove String.prototype.contains (bug 1103588) */
-static bool
-str_contains(JSContext *cx, unsigned argc, Value *vp)
-{
-#ifndef RELEASE_BUILD
-    CallArgs args = CallArgsFromVp(argc, vp);
-    RootedObject callee(cx, &args.callee());
-    if (!GlobalObject::warnOnceAboutStringContains(cx, callee))
-        return false;
-#endif
-    return str_includes(cx, argc, vp);
-}
-
 /* ES6 20120927 draft 15.5.4.7. */
 bool
 js::str_indexOf(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     // Steps 1, 2, and 3
     RootedString str(cx, ThisToStringForStringProto(cx, args));
@@ -2537,17 +2528,16 @@ static const JSFunctionSpec string_metho
     JS_FN("toUpperCase",       str_toUpperCase,       0,JSFUN_GENERIC_NATIVE),
     JS_INLINABLE_FN("charAt",  str_charAt,            1,JSFUN_GENERIC_NATIVE, StringCharAt),
     JS_INLINABLE_FN("charCodeAt", str_charCodeAt,     1,JSFUN_GENERIC_NATIVE, StringCharCodeAt),
     JS_SELF_HOSTED_FN("substring", "String_substring", 2,0),
     JS_SELF_HOSTED_FN("padStart", "String_pad_start", 2,0),
     JS_SELF_HOSTED_FN("padEnd", "String_pad_end", 2,0),
     JS_SELF_HOSTED_FN("codePointAt", "String_codePointAt", 1,0),
     JS_FN("includes",          str_includes,          1,JSFUN_GENERIC_NATIVE),
-    JS_FN("contains",          str_contains,          1,JSFUN_GENERIC_NATIVE),
     JS_FN("indexOf",           str_indexOf,           1,JSFUN_GENERIC_NATIVE),
     JS_FN("lastIndexOf",       str_lastIndexOf,       1,JSFUN_GENERIC_NATIVE),
     JS_FN("startsWith",        str_startsWith,        1,JSFUN_GENERIC_NATIVE),
     JS_FN("endsWith",          str_endsWith,          1,JSFUN_GENERIC_NATIVE),
     JS_FN("trim",              str_trim,              0,JSFUN_GENERIC_NATIVE),
     JS_FN("trimLeft",          str_trimLeft,          0,JSFUN_GENERIC_NATIVE),
     JS_FN("trimRight",         str_trimRight,         0,JSFUN_GENERIC_NATIVE),
     JS_FN("toLocaleLowerCase", str_toLocaleLowerCase, 0,JSFUN_GENERIC_NATIVE),
--- a/js/src/old-configure.in
+++ b/js/src/old-configure.in
@@ -1,17 +1,16 @@
 dnl -*- Mode: Autoconf; tab-width: 4; indent-tabs-mode: nil; -*-
 dnl vi: set tabstop=4 shiftwidth=4 expandtab syntax=m4:
 dnl This Source Code Form is subject to the terms of the Mozilla Public
 dnl License, v. 2.0. If a copy of the MPL was not distributed with this
 dnl file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 dnl Process this file with autoconf to produce a configure script.
 dnl ========================================================
-
 AC_PREREQ(2.13)
 AC_INIT(js/src/jsapi.h)
 AC_CONFIG_AUX_DIR(${srcdir}/build/autoconf)
 AC_CANONICAL_SYSTEM
 
 dnl ========================================================
 dnl =
 dnl = Don't change the following two lines.  Doing so breaks:
--- a/js/src/perf/jsperf.cpp
+++ b/js/src/perf/jsperf.cpp
@@ -154,20 +154,31 @@ static const struct pm_const {
     { 0, PerfMeasurement::EventMask(0) }
 };
 
 #undef CONSTANT
 
 static bool pm_construct(JSContext* cx, unsigned argc, Value* vp);
 static void pm_finalize(JSFreeOp* fop, JSObject* obj);
 
+static const JSClassOps pm_classOps = {
+    nullptr,
+    nullptr,
+    nullptr,
+    nullptr,
+    nullptr,
+    nullptr,
+    nullptr,
+    pm_finalize
+};
+
 static const JSClass pm_class = {
-    "PerfMeasurement", JSCLASS_HAS_PRIVATE,
-    nullptr, nullptr, nullptr, nullptr,
-    nullptr, nullptr, nullptr, pm_finalize
+    "PerfMeasurement",
+    JSCLASS_HAS_PRIVATE,
+    &pm_classOps
 };
 
 // Constructor and destructor
 
 static bool
 pm_construct(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
--- a/js/src/proxy/BaseProxyHandler.cpp
+++ b/js/src/proxy/BaseProxyHandler.cpp
@@ -223,20 +223,20 @@ js::SetPropertyIgnoringNamedGetter(JSCon
         unsigned attrs =
             existingDescriptor.object()
             ? JSPROP_IGNORE_ENUMERATE | JSPROP_IGNORE_READONLY | JSPROP_IGNORE_PERMANENT
             : JSPROP_ENUMERATE;
 
         // A very old nonstandard SpiderMonkey extension: default to the Class
         // getter and setter ops.
         const Class* clasp = receiverObj->getClass();
-        MOZ_ASSERT(clasp->getProperty != JS_PropertyStub);
-        MOZ_ASSERT(clasp->setProperty != JS_StrictPropertyStub);
-        return DefineProperty(cx, receiverObj, id, v, clasp->getProperty, clasp->setProperty,
-                              attrs, result);
+        MOZ_ASSERT(clasp->getGetProperty() != JS_PropertyStub);
+        MOZ_ASSERT(clasp->getSetProperty() != JS_StrictPropertyStub);
+        return DefineProperty(cx, receiverObj, id, v,
+                              clasp->getGetProperty(), clasp->getSetProperty(), attrs, result);
     }
 
     // Step 6.
     MOZ_ASSERT(ownDesc.isAccessorDescriptor());
     RootedObject setter(cx);
     if (ownDesc.hasSetterObject())
         setter = ownDesc.setterObject();
     if (!setter)
--- a/js/src/proxy/Proxy.cpp
+++ b/js/src/proxy/Proxy.cpp
@@ -711,16 +711,31 @@ js::proxy_GetElements(JSContext* cx, Han
 }
 
 JSString*
 js::proxy_FunToString(JSContext* cx, HandleObject proxy, unsigned indent)
 {
     return Proxy::fun_toString(cx, proxy, indent);
 }
 
+const ClassOps js::ProxyClassOps = {
+    nullptr,                 /* addProperty */
+    nullptr,                 /* delProperty */
+    nullptr,                 /* getProperty */
+    nullptr,                 /* setProperty */
+    nullptr,                 /* enumerate   */
+    nullptr,                 /* resolve     */
+    nullptr,                 /* mayResolve  */
+    js::proxy_Finalize,      /* finalize    */
+    nullptr,                 /* call        */
+    js::proxy_HasInstance,   /* hasInstance */
+    nullptr,                 /* construct   */
+    js::proxy_Trace,         /* trace       */
+};
+
 const ClassExtension js::ProxyClassExtension = PROXY_MAKE_EXT(
     js::proxy_ObjectMoved
 );
 
 const ObjectOps js::ProxyObjectOps = {
     js::proxy_LookupProperty,
     js::proxy_DefineProperty,
     js::proxy_HasProperty,
--- a/js/src/shell/OSObject.cpp
+++ b/js/src/shell/OSObject.cpp
@@ -390,33 +390,37 @@ class FileObject : public JSObject {
 
   private:
 
     void setRCFile(RCFile* file) {
         js::SetReservedSlot(this, FILE_SLOT, PrivateValue(file));
     }
 };
 
-const js::Class FileObject::class_ = {
-    "File",
-    JSCLASS_HAS_RESERVED_SLOTS(FileObject::NUM_SLOTS),
+static const js::ClassOps FileObjectClassOps = {
     nullptr,               /* addProperty */
     nullptr,               /* delProperty */
     nullptr,               /* getProperty */
     nullptr,               /* setProperty */
     nullptr,               /* enumerate */
     nullptr,               /* resolve */
     nullptr,               /* mayResolve */
     FileObject::finalize,  /* finalize */
     nullptr,               /* call */
     nullptr,               /* hasInstance */
     nullptr,               /* construct */
     nullptr                /* trace */
 };
 
+const js::Class FileObject::class_ = {
+    "File",
+    JSCLASS_HAS_RESERVED_SLOTS(FileObject::NUM_SLOTS),
+    &FileObjectClassOps
+};
+
 static FileObject*
 redirect(JSContext* cx, HandleString relFilename, RCFile** globalFile)
 {
     RootedString filename(cx, ResolvePath(cx, relFilename, RootRelative));
     if (!filename)
         return nullptr;
     JSAutoByteString filenameABS(cx, filename);
     if (!filenameABS)
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -2741,26 +2741,30 @@ sandbox_resolve(JSContext* cx, HandleObj
     if (!JS_GetProperty(cx, obj, "lazy", &v))
         return false;
 
     if (ToBoolean(v))
         return JS_ResolveStandardClass(cx, obj, id, resolvedp);
     return true;
 }
 
-static const JSClass sandbox_class = {
-    "sandbox",
-    JSCLASS_GLOBAL_FLAGS,
+static const JSClassOps sandbox_classOps = {
     nullptr, nullptr, nullptr, nullptr,
     sandbox_enumerate, sandbox_resolve,
     nullptr, nullptr,
     nullptr, nullptr, nullptr,
     JS_GlobalObjectTraceHook
 };
 
+static const JSClass sandbox_class = {
+    "sandbox",
+    JSCLASS_GLOBAL_FLAGS,
+    &sandbox_classOps
+};
+
 static void
 SetStandardCompartmentOptions(JS::CompartmentOptions& options)
 {
     options.behaviors().setVersion(JSVERSION_DEFAULT);
     options.creationOptions().setSharedMemoryAndAtomicsEnabled(enableSharedMemory);
 }
 
 static JSObject*
@@ -6021,25 +6025,29 @@ global_resolve(JSContext* cx, HandleObje
 }
 
 static bool
 global_mayResolve(const JSAtomState& names, jsid id, JSObject* maybeObj)
 {
     return JS_MayResolveStandardClass(names, id, maybeObj);
 }
 
-static const JSClass global_class = {
-    "global", JSCLASS_GLOBAL_FLAGS,
+static const JSClassOps global_classOps = {
     nullptr, nullptr, nullptr, nullptr,
     global_enumerate, global_resolve, global_mayResolve,
     nullptr,
     nullptr, nullptr, nullptr,
     JS_GlobalObjectTraceHook
 };
 
+static const JSClass global_class = {
+    "global", JSCLASS_GLOBAL_FLAGS,
+    &global_classOps
+};
+
 /*
  * Define a FakeDOMObject constructor. It returns an object with a getter,
  * setter and method with attached JitInfo. This object can be used to test
  * IonMonkey DOM optimizations in the shell.
  */
 static const uint32_t DOM_OBJECT_SLOT = 0;
 
 static bool
--- a/js/src/tests/ecma_6/Comprehensions/sudoku.js
+++ b/js/src/tests/ecma_6/Comprehensions/sudoku.js
@@ -6,24 +6,17 @@
 
 function copy(obj) {
   var o = {};
   for (var i in obj)
     o[i] = obj[i];
   return o;
 }
 
-Array.prototype.contains = String.prototype.contains = function (e) {
-  for (var elt of this)
-    if (elt == e)
-      return true;
-  return false;
-}
-
-Array.prototype.repeat = String.prototype.repeat = function (n) {
+Array.prototype.repeat = function (n) {
   var s = this.constructor();
   for (var i = 0; i < n; i++)
     s = s.concat(this);
   return s;
 }
 
 String.prototype.center = function (w) {
   var n = this.length;
@@ -59,17 +52,17 @@ function dict(A) {
   for (var e of A)
     d[e[0]] = e[1];
   return d;
 }
 
 function set(A) {
   var s = [];
   for (var e of A)
-    if (!s.contains(e))
+    if (!s.includes(e))
       s.push(e);
   return s;
 }
 
 function zip(A, B) {
   var z = [];
   var n = Math.min(A.length, B.length);
   for (var i = 0; i < n; i++)
@@ -80,75 +73,75 @@ function zip(A, B) {
 rows = 'ABCDEFGHI';
 cols = '123456789';
 digits   = '123456789';
 squares  = cross(rows, cols);
 unitlist = [for (c of cols) cross(rows, c)]
   .concat([for (r of rows) cross(r, cols)])
   .concat([for (rs of ['ABC','DEF','GHI']) for (cs of ['123','456','789']) cross(rs, cs)]);
 units = dict((for (s of squares)
-                [s, [for (u of unitlist) if (u.contains(s)) u]]));
+                [s, [for (u of unitlist) if (u.includes(s)) u]]));
 
 peers = dict((for (s of squares)
                 [s, set([for (u of units[s]) for (s2 of u) if (s2 != s) s2])]));
 
 // Given a string of 81 digits (or . or 0 or -), return a dict of {cell:values}.
 function parse_grid(grid) {
-  grid = [for (c of grid) if ('0.-123456789'.contains(c)) c];
+  grid = [for (c of grid) if ('0.-123456789'.includes(c)) c];
   var values = dict((for (s of squares) [s, digits]));
 
   for (var pair of zip(squares, grid)) {
     var s = pair[0], d = pair[1];
-    if (digits.contains(d) && !assign(values, s, d))
+    if (digits.includes(d) && !assign(values, s, d))
       return false;
   }
   return values;
 }
 
 // Eliminate all the other values (except d) from values[s] and propagate.
 function assign(values, s, d) {
   if (all((for (d2 of values[s]) if (d2 != d) eliminate(values, s, d2))))
     return values;
   return false;
 }
 
 // Eliminate d from values[s]; propagate when values or places <= 2.
 function eliminate(values, s, d) {
-  if (!values[s].contains(d))
+  if (!values[s].includes(d))
     return values; // Already eliminated
   values[s] = values[s].replace(d, '');
   if (values[s].length == 0)
     return false;  // Contradiction: removed last value
   if (values[s].length == 1) {
     // If there is only one value (d2) left in square, remove it from peers
     var d2 = values[s][0];
     if (!all((for (s2 of peers[s]) eliminate(values, s2, d2))))
       return false;
   }
   // Now check the places where d appears in the units of s
   for (var u of units[s]) {
-    var dplaces = [for (s of u) if (values[s].contains(d)) s];
+    var dplaces = [for (s of u) if (values[s].includes(d)) s];
     if (dplaces.length == 0)
       return false;
     if (dplaces.length == 1)
 	    // d can only be in one place in unit; assign it there
       if (!assign(values, dplaces[0], d))
         return false;
   }
   return values;
 }
 
 // Used for debugging.
 function print_board(values) {
   var width = 1 + Math.max.apply(Math, [for (s of squares) values[s].length]);
   var line = '\n' + ['-'.repeat(width*3)].repeat(3).join('+');
   for (var r of rows)
     print([for (c of cols)
-              values[r+c].center(width) + ('36'.contains(c) && '|' || '')]
-          .join('') + ('CF'.contains(r) && line || ''));
+              values[r+c].center(width) + ('36'.includes(c) && '|' || '')]
+          .join('') + ('CF'.includes(r) && line || ''));
   print('\n');
 }
 
 easy = "..3.2.6..9..3.5..1..18.64....81.29..7.......8..67.82....26.95..8..2.3..9..5.1.3..";
 
 print_board(parse_grid(easy));
 
 // Using depth-first search and constraint propagation, try all possible values.
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/RegExp/split-invalid-lastIndex.js
@@ -0,0 +1,31 @@
+var BUGNUMBER = 1263851;
+var summary = "RegExp.prototype[@@split] should handle if lastIndex is out of bound.";
+
+print(BUGNUMBER + ": " + summary);
+
+var myRegExp = {
+    get constructor() {
+        return {
+            get [Symbol.species]() {
+                return function() {
+                    return {
+                        get lastIndex() {
+                            return 9;
+                        },
+                        set lastIndex(v) {},
+                        exec() {
+                            return [];
+                        }
+                    };
+                };
+            }
+        };
+    }
+};
+var result = RegExp.prototype[Symbol.split].call(myRegExp, "abcde");;
+assertEq(result.length, 2);
+assertEq(result[0], "");
+assertEq(result[1], "");
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
--- a/js/src/tests/shell/warning.js
+++ b/js/src/tests/shell/warning.js
@@ -4,25 +4,24 @@ var BUGNUMBER = 1170716;
 var summary = 'Add js shell functions to get last warning';
 
 print(BUGNUMBER + ": " + summary);
 
 // Warning with JSEXN_NONE.
 
 enableLastWarning();
 
-let line0 = new Error().lineNumber;
-assertEq("foo".contains("bar"), false);
+eval(`(function() "This is an expression closure.")`);
 
 var warning = getLastWarning();
 assertEq(warning !== null, true);
 assertEq(warning.name, "None");
-assertEq(warning.message.includes("deprecated"), true);
-assertEq(warning.lineNumber, line0 + 1);
-assertEq(warning.columnNumber, 10);
+assertEq(warning.message.includes("expression closures are deprecated"), true);
+assertEq(warning.lineNumber, 1);
+assertEq(warning.columnNumber, 12);
 
 // Clear last warning.
 
 clearLastWarning();
 warning = getLastWarning();
 assertEq(warning, null);
 
 // Warning with JSEXN_SYNTAXERR.
--- a/js/src/vm/ArgumentsObject.cpp
+++ b/js/src/vm/ArgumentsObject.cpp
@@ -670,53 +670,61 @@ ArgumentsObject::objectMovedDuringMinorG
 }
 
 /*
  * The classes below collaborate to lazily reflect and synchronize actual
  * argument values, argument count, and callee function object stored in a
  * stack frame with their corresponding property values in the frame's
  * arguments object.
  */
-const Class MappedArgumentsObject::class_ = {
-    "Arguments",
-    JSCLASS_DELAY_METADATA_BUILDER |
-    JSCLASS_HAS_RESERVED_SLOTS(MappedArgumentsObject::RESERVED_SLOTS) |
-    JSCLASS_HAS_CACHED_PROTO(JSProto_Object) |
-    JSCLASS_SKIP_NURSERY_FINALIZE |
-    JSCLASS_BACKGROUND_FINALIZE,
+const ClassOps MappedArgumentsObject::classOps_ = {
     nullptr,                 /* addProperty */
     ArgumentsObject::obj_delProperty,
     nullptr,                 /* getProperty */
     nullptr,                 /* setProperty */
     MappedArgumentsObject::obj_enumerate,
     MappedArgumentsObject::obj_resolve,
     nullptr,                 /* mayResolve  */
     ArgumentsObject::finalize,
     nullptr,                 /* call        */
     nullptr,                 /* hasInstance */
     nullptr,                 /* construct   */
     ArgumentsObject::trace
 };
 
+const Class MappedArgumentsObject::class_ = {
+    "Arguments",
+    JSCLASS_DELAY_METADATA_BUILDER |
+    JSCLASS_HAS_RESERVED_SLOTS(MappedArgumentsObject::RESERVED_SLOTS) |
+    JSCLASS_HAS_CACHED_PROTO(JSProto_Object) |
+    JSCLASS_SKIP_NURSERY_FINALIZE |
+    JSCLASS_BACKGROUND_FINALIZE,
+    &MappedArgumentsObject::classOps_
+};
+
 /*
  * Unmapped arguments is significantly less magical than mapped arguments, so
  * it is represented by a different class while sharing some functionality.
  */
-const Class UnmappedArgumentsObject::class_ = {
-    "Arguments",
-    JSCLASS_DELAY_METADATA_BUILDER |
-    JSCLASS_HAS_RESERVED_SLOTS(UnmappedArgumentsObject::RESERVED_SLOTS) |
-    JSCLASS_HAS_CACHED_PROTO(JSProto_Object) |
-    JSCLASS_SKIP_NURSERY_FINALIZE |
-    JSCLASS_BACKGROUND_FINALIZE,
+const ClassOps UnmappedArgumentsObject::classOps_ = {
     nullptr,                 /* addProperty */
     ArgumentsObject::obj_delProperty,
     nullptr,                 /* getProperty */
     nullptr,                 /* setProperty */
     UnmappedArgumentsObject::obj_enumerate,
     UnmappedArgumentsObject::obj_resolve,
     nullptr,                 /* mayResolve  */
     ArgumentsObject::finalize,
     nullptr,                 /* call        */
     nullptr,                 /* hasInstance */
     nullptr,                 /* construct   */
     ArgumentsObject::trace
 };
+
+const Class UnmappedArgumentsObject::class_ = {
+    "Arguments",
+    JSCLASS_DELAY_METADATA_BUILDER |
+    JSCLASS_HAS_RESERVED_SLOTS(UnmappedArgumentsObject::RESERVED_SLOTS) |
+    JSCLASS_HAS_CACHED_PROTO(JSProto_Object) |
+    JSCLASS_SKIP_NURSERY_FINALIZE |
+    JSCLASS_BACKGROUND_FINALIZE,
+    &UnmappedArgumentsObject::classOps_
+};
--- a/js/src/vm/ArgumentsObject.h
+++ b/js/src/vm/ArgumentsObject.h
@@ -324,16 +324,18 @@ class ArgumentsObject : public NativeObj
     static void MaybeForwardToCallObject(AbstractFramePtr frame, ArgumentsObject* obj,
                                          ArgumentsData* data);
     static void MaybeForwardToCallObject(jit::JitFrameLayout* frame, HandleObject callObj,
                                          ArgumentsObject* obj, ArgumentsData* data);
 };
 
 class MappedArgumentsObject : public ArgumentsObject
 {
+    static const ClassOps classOps_;
+
   public:
     static const Class class_;
 
     /*
      * Stores arguments.callee, or MagicValue(JS_ARGS_HOLE) if the callee has
      * been cleared.
      */
     const js::Value& callee() const {
@@ -347,16 +349,18 @@ class MappedArgumentsObject : public Arg
 
   private:
     static bool obj_enumerate(JSContext* cx, HandleObject obj);
     static bool obj_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp);
 };
 
 class UnmappedArgumentsObject : public ArgumentsObject
 {
+    static const ClassOps classOps_;
+
   public:
     static const Class class_;
 
   private:
     static bool obj_enumerate(JSContext* cx, HandleObject obj);
     static bool obj_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp);
 };
 
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -91,39 +91,43 @@ js::ToClampedIndex(JSContext* cx, Handle
  * ArrayBufferObject (base)
  */
 
 const Class ArrayBufferObject::protoClass = {
     "ArrayBufferPrototype",
     JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer)
 };
 
+const ClassOps ArrayBufferObject::classOps_ = {
+    nullptr,        /* addProperty */
+    nullptr,        /* delProperty */
+    nullptr,        /* getProperty */
+    nullptr,        /* setProperty */
+    nullptr,        /* enumerate */
+    nullptr,        /* resolve */
+    nullptr,        /* mayResolve */
+    ArrayBufferObject::finalize,
+    nullptr,        /* call        */
+    nullptr,        /* hasInstance */
+    nullptr,        /* construct   */
+    ArrayBufferObject::trace,
+};
+
 static const ClassExtension ArrayBufferObjectClassExtension = {
     nullptr,    /* weakmapKeyDelegateOp */
     ArrayBufferObject::objectMoved
 };
 
 const Class ArrayBufferObject::class_ = {
     "ArrayBuffer",
     JSCLASS_DELAY_METADATA_BUILDER |
     JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer) |
     JSCLASS_BACKGROUND_FINALIZE,
-    nullptr,                 /* addProperty */
-    nullptr,                 /* delProperty */
-    nullptr,                 /* getProperty */
-    nullptr,                 /* setProperty */
-    nullptr,                 /* enumerate */
-    nullptr,                 /* resolve */
-    nullptr,                 /* mayResolve */
-    ArrayBufferObject::finalize,
-    nullptr,        /* call        */
-    nullptr,        /* hasInstance */
-    nullptr,        /* construct   */
-    ArrayBufferObject::trace,
+    &ArrayBufferObject::classOps_,
     JS_NULL_CLASS_SPEC,
     &ArrayBufferObjectClassExtension
 };
 
 const JSFunctionSpec ArrayBufferObject::jsfuncs[] = {
     JS_SELF_HOSTED_FN("slice", "ArrayBufferSlice", 2,0),
     JS_FS_END
 };
--- a/js/src/vm/ArrayBufferObject.h
+++ b/js/src/vm/ArrayBufferObject.h
@@ -97,16 +97,18 @@ class ArrayBufferObjectMaybeShared : pub
  * ArrayBufferObject (or really the underlying memory) /is not racy/: the
  * memory is private to a single worker.
  */
 class ArrayBufferObject : public ArrayBufferObjectMaybeShared
 {
     static bool byteLengthGetterImpl(JSContext* cx, const CallArgs& args);
     static bool fun_slice_impl(JSContext* cx, const CallArgs& args);
 
+    static const ClassOps classOps_;
+
   public:
     static const uint8_t DATA_SLOT = 0;
     static const uint8_t BYTE_LENGTH_SLOT = 1;
     static const uint8_t FIRST_VIEW_SLOT = 2;
     static const uint8_t FLAGS_SLOT = 3;
 
     static const uint8_t RESERVED_SLOTS = 4;
 
--- a/js/src/vm/ArrayObject-inl.h
+++ b/js/src/vm/ArrayObject-inl.h
@@ -36,17 +36,17 @@ ArrayObject::setLength(ExclusiveContext*
 ArrayObject::createArrayInternal(ExclusiveContext* cx, gc::AllocKind kind, gc::InitialHeap heap,
                                  HandleShape shape, HandleObjectGroup group,
                                  AutoSetNewObjectMetadata&)
 {
     // Create a new array and initialize everything except for its elements.
     MOZ_ASSERT(shape && group);
     MOZ_ASSERT(group->clasp() == shape->getObjectClass());
     MOZ_ASSERT(group->clasp() == &ArrayObject::class_);
-    MOZ_ASSERT_IF(group->clasp()->finalize, heap == gc::TenuredHeap);
+    MOZ_ASSERT_IF(group->clasp()->hasFinalize(), heap == gc::TenuredHeap);
     MOZ_ASSERT_IF(group->hasUnanalyzedPreliminaryObjects(),
                   heap == js::gc::TenuredHeap);
     MOZ_ASSERT(group->clasp()->shouldDelayMetadataBuilder());
 
     // Arrays can use their fixed slots to store elements, so can't have shapes
     // which allow named properties to be stored in the fixed slots.
     MOZ_ASSERT(shape->numFixedSlots() == 0);
 
--- a/js/src/vm/Debugger-inl.h
+++ b/js/src/vm/Debugger-inl.h
@@ -24,17 +24,17 @@ js::Debugger::onLeaveFrame(JSContext* cx
         ok = slowPathOnLeaveFrame(cx, frame, pc, ok);
     MOZ_ASSERT(!inFrameMaps(frame));
     return ok;
 }
 
 /* static */ inline js::Debugger*
 js::Debugger::fromJSObject(const JSObject* obj)
 {
-    MOZ_ASSERT(js::GetObjectClass(obj) == &jsclass);
+    MOZ_ASSERT(js::GetObjectClass(obj) == &class_);
     return (Debugger*) obj->as<NativeObject>().getPrivate();
 }
 
 /* static */ inline bool
 js::Debugger::checkNoExecute(JSContext* cx, HandleScript script)
 {
     if (!cx->compartment()->isDebuggee() || !cx->runtime()->noExecuteDebuggerTop)
         return true;
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -54,68 +54,171 @@ using mozilla::DebugOnly;
 using mozilla::MakeScopeExit;
 using mozilla::Maybe;
 using mozilla::Some;
 using mozilla::Nothing;
 using mozilla::Variant;
 using mozilla::AsVariant;
 
 
-/*** Forward declarations ************************************************************************/
-
-extern const Class DebuggerFrame_class;
+/*** Forward declarations, ClassOps and Classes **************************************************/
+
+static void DebuggerFrame_finalize(FreeOp* fop, JSObject* obj);
+static void DebuggerEnv_trace(JSTracer* trc, JSObject* obj);
+static void DebuggerObject_trace(JSTracer* trc, JSObject* obj);
+static void DebuggerScript_trace(JSTracer* trc, JSObject* obj);
+static void DebuggerSource_trace(JSTracer* trc, JSObject* obj);
 
 enum {
     JSSLOT_DEBUGFRAME_OWNER,
     JSSLOT_DEBUGFRAME_ARGUMENTS,
     JSSLOT_DEBUGFRAME_ONSTEP_HANDLER,
     JSSLOT_DEBUGFRAME_ONPOP_HANDLER,
     JSSLOT_DEBUGFRAME_COUNT
 };
 
-extern const Class DebuggerArguments_class;
+static const ClassOps DebuggerFrame_classOps = {
+    nullptr,    /* addProperty */
+    nullptr,    /* delProperty */
+    nullptr,    /* getProperty */
+    nullptr,    /* setProperty */
+    nullptr,    /* enumerate   */
+    nullptr,    /* resolve     */
+    nullptr,    /* mayResolve  */
+    DebuggerFrame_finalize,
+    nullptr,    /* call        */
+    nullptr,    /* hasInstance */
+    nullptr,    /* construct   */
+    nullptr,    /* trace   */
+};
+
+static const Class DebuggerFrame_class = {
+    "Frame",
+    JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGFRAME_COUNT),
+    &DebuggerFrame_classOps
+};
 
 enum {
     JSSLOT_DEBUGARGUMENTS_FRAME,
     JSSLOT_DEBUGARGUMENTS_COUNT
 };
 
-extern const Class DebuggerEnv_class;
+static const Class DebuggerArguments_class = {
+    "Arguments",
+    JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGARGUMENTS_COUNT)
+};
 
 enum {
     JSSLOT_DEBUGENV_OWNER,
     JSSLOT_DEBUGENV_COUNT
 };
 
-extern const Class DebuggerObject_class;
+static const ClassOps DebuggerEnv_classOps = {
+    nullptr,    /* addProperty */
+    nullptr,    /* delProperty */
+    nullptr,    /* getProperty */
+    nullptr,    /* setProperty */
+    nullptr,    /* enumerate   */
+    nullptr,    /* resolve     */
+    nullptr,    /* mayResolve  */
+    nullptr,    /* finalize    */
+    nullptr,    /* call        */
+    nullptr,    /* hasInstance */
+    nullptr,    /* construct   */
+    DebuggerEnv_trace
+};
+
+static const Class DebuggerEnv_class = {
+    "Environment",
+    JSCLASS_HAS_PRIVATE |
+    JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGENV_COUNT),
+    &DebuggerEnv_classOps
+};
 
 enum {
     JSSLOT_DEBUGOBJECT_OWNER,
     JSSLOT_DEBUGOBJECT_COUNT
 };
 
-extern const Class DebuggerScript_class;
+static const ClassOps DebuggerObject_classOps = {
+    nullptr,    /* addProperty */
+    nullptr,    /* delProperty */
+    nullptr,    /* getProperty */
+    nullptr,    /* setProperty */
+    nullptr,    /* enumerate   */
+    nullptr,    /* resolve     */
+    nullptr,    /* mayResolve  */
+    nullptr,    /* finalize    */
+    nullptr,    /* call        */
+    nullptr,    /* hasInstance */
+    nullptr,    /* construct   */
+    DebuggerObject_trace
+};
+
+static const Class DebuggerObject_class = {
+    "Object",
+    JSCLASS_HAS_PRIVATE |
+    JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGOBJECT_COUNT),
+    &DebuggerObject_classOps
+};
 
 enum {
     JSSLOT_DEBUGSCRIPT_OWNER,
     JSSLOT_DEBUGSCRIPT_COUNT
 };
 
-extern const Class DebuggerSource_class;
+static const ClassOps DebuggerScript_classOps = {
+    nullptr,    /* addProperty */
+    nullptr,    /* delProperty */
+    nullptr,    /* getProperty */
+    nullptr,    /* setProperty */
+    nullptr,    /* enumerate   */
+    nullptr,    /* resolve     */
+    nullptr,    /* mayResolve  */
+    nullptr,    /* finalize    */
+    nullptr,    /* call        */
+    nullptr,    /* hasInstance */
+    nullptr,    /* construct   */
+    DebuggerScript_trace
+};
+
+static const Class DebuggerScript_class = {
+    "Script",
+    JSCLASS_HAS_PRIVATE |
+    JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGSCRIPT_COUNT),
+    &DebuggerScript_classOps
+};
 
 enum {
     JSSLOT_DEBUGSOURCE_OWNER,
     JSSLOT_DEBUGSOURCE_TEXT,
     JSSLOT_DEBUGSOURCE_COUNT
 };
 
-void DebuggerObject_trace(JSTracer* trc, JSObject* obj);
-void DebuggerEnv_trace(JSTracer* trc, JSObject* obj);
-void DebuggerScript_trace(JSTracer* trc, JSObject* obj);
-void DebuggerSource_trace(JSTracer* trc, JSObject* obj);
+static const ClassOps DebuggerSource_classOps = {
+    nullptr,    /* addProperty */
+    nullptr,    /* delProperty */
+    nullptr,    /* getProperty */
+    nullptr,    /* setProperty */
+    nullptr,    /* enumerate   */
+    nullptr,    /* resolve     */
+    nullptr,    /* mayResolve  */
+    nullptr,    /* finalize    */
+    nullptr,    /* call        */
+    nullptr,    /* hasInstance */
+    nullptr,    /* construct   */
+    DebuggerSource_trace
+};
+
+static const Class DebuggerSource_class = {
+    "Source",
+    JSCLASS_HAS_PRIVATE |
+    JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGSOURCE_COUNT),
+    &DebuggerSource_classOps
+};
 
 
 /*** Utils ***************************************************************************************/
 
 static inline bool
 EnsureFunctionHasScript(JSContext* cx, HandleFunction fun)
 {
     if (fun->isInterpretedLazy()) {
@@ -2785,35 +2888,45 @@ Debugger::findZoneEdges(Zone* zone, js::
 Debugger::finalize(FreeOp* fop, JSObject* obj)
 {
     Debugger* dbg = fromJSObject(obj);
     if (!dbg)
         return;
     fop->delete_(dbg);
 }
 
-const Class Debugger::jsclass = {
+const ClassOps Debugger::classOps_ = {
+    nullptr,    /* addProperty */
+    nullptr,    /* delProperty */
+    nullptr,    /* getProperty */
+    nullptr,    /* setProperty */
+    nullptr,    /* enumerate   */
+    nullptr,    /* resolve     */
+    nullptr,    /* mayResolve  */
+    Debugger::finalize,
+    nullptr,    /* call        */
+    nullptr,    /* hasInstance */
+    nullptr,    /* construct   */
+    Debugger::traceObject
+};
+
+const Class Debugger::class_ = {
     "Debugger",
     JSCLASS_HAS_PRIVATE |
     JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUG_COUNT),
-    nullptr, nullptr, nullptr, nullptr,
-    nullptr, nullptr, nullptr, Debugger::finalize,
-    nullptr,              /* call        */
-    nullptr,              /* hasInstance */
-    nullptr,              /* construct   */
-    Debugger::traceObject
+    &Debugger::classOps_
 };
 
 /* static */ Debugger*
 Debugger::fromThisValue(JSContext* cx, const CallArgs& args, const char* fnname)
 {
     JSObject* thisobj = NonNullObject(cx, args.thisv());
     if (!thisobj)
         return nullptr;
-    if (thisobj->getClass() != &Debugger::jsclass) {
+    if (thisobj->getClass() != &Debugger::class_) {
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
                              "Debugger", fnname, thisobj->getClass()->name);
         return nullptr;
     }
 
     /*
      * Forbid Debugger.prototype, which is of the Debugger JSClass but isn't
      * really a Debugger object. The prototype object is distinguished by
@@ -3390,23 +3503,23 @@ Debugger::construct(JSContext* cx, unsig
     }
 
     /* Get Debugger.prototype. */
     RootedValue v(cx);
     RootedObject callee(cx, &args.callee());
     if (!GetProperty(cx, callee, callee, cx->names().prototype, &v))
         return false;
     RootedNativeObject proto(cx, &v.toObject().as<NativeObject>());
-    MOZ_ASSERT(proto->getClass() == &Debugger::jsclass);
+    MOZ_ASSERT(proto->getClass() == &Debugger::class_);
     /*
      * Make the new Debugger object. Each one has a reference to
      * Debugger.{Frame,Object,Script,Memory}.prototype in reserved slots. The
      * rest of the reserved slots are for hooks; they default to undefined.
      */
-    RootedNativeObject obj(cx, NewNativeObjectWithGivenProto(cx, &Debugger::jsclass, proto));
+    RootedNativeObject obj(cx, NewNativeObjectWithGivenProto(cx, &Debugger::class_, proto));
     if (!obj)
         return false;
     for (unsigned slot = JSSLOT_DEBUG_PROTO_START; slot < JSSLOT_DEBUG_PROTO_STOP; slot++)
         obj->setReservedSlot(slot, proto->getReservedSlot(slot));
     obj->setReservedSlot(JSSLOT_DEBUG_MEMORY_INSTANCE, NullValue());
 
     Debugger* debugger;
     {
@@ -4879,28 +4992,16 @@ DebuggerScript_trace(JSTracer* trc, JSOb
             TraceManuallyBarrieredCrossCompartmentEdge(trc, obj, &wasm,
                                                        "Debugger.Script wasm referent");
             MOZ_ASSERT(wasm->is<WasmModuleObject>());
             obj->as<NativeObject>().setPrivateUnbarriered(wasm);
         }
     }
 }
 
-const Class DebuggerScript_class = {
-    "Script",
-    JSCLASS_HAS_PRIVATE |
-    JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGSCRIPT_COUNT),
-    nullptr, nullptr, nullptr, nullptr,
-    nullptr, nullptr, nullptr, nullptr,
-    nullptr,              /* call        */
-    nullptr,              /* hasInstance */
-    nullptr,              /* construct   */
-    DebuggerScript_trace
-};
-
 class DebuggerScriptSetPrivateMatcher
 {
     NativeObject* obj_;
   public:
     explicit DebuggerScriptSetPrivateMatcher(NativeObject* obj) : obj_(obj) { }
     using ReturnType = void;
     ReturnType match(HandleScript script) { obj_->setPrivateGCThing(script); }
     ReturnType match(Handle<WasmModuleObject*> module) { obj_->setPrivateGCThing(module); }
@@ -6152,28 +6253,16 @@ DebuggerSource_trace(JSTracer* trc, JSOb
      */
     if (JSObject *referent = GetSourceReferentRawObject(obj)) {
         TraceManuallyBarrieredCrossCompartmentEdge(trc, obj, &referent,
                                                    "Debugger.Source referent");
         obj->as<NativeObject>().setPrivateUnbarriered(referent);
     }
 }
 
-const Class DebuggerSource_class = {
-    "Source",
-    JSCLASS_HAS_PRIVATE |
-    JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGSOURCE_COUNT),
-    nullptr, nullptr, nullptr, nullptr,
-    nullptr, nullptr, nullptr, nullptr,
-    nullptr,              /* call        */
-    nullptr,              /* hasInstance */
-    nullptr,              /* construct   */
-    DebuggerSource_trace
-};
-
 class SetDebuggerSourcePrivateMatcher
 {
     NativeObject* obj_;
   public:
     explicit SetDebuggerSourcePrivateMatcher(NativeObject* obj) : obj_(obj) { }
     using ReturnType = void;
     ReturnType match(HandleScriptSource source) { obj_->setPrivateGCThing(source); }
     ReturnType match(Handle<WasmModuleObject*> module) { obj_->setPrivateGCThing(module); }
@@ -6715,22 +6804,16 @@ DebuggerFrame_maybeDecrementFrameScriptS
 }
 
 static void
 DebuggerFrame_finalize(FreeOp* fop, JSObject* obj)
 {
     DebuggerFrame_freeScriptFrameIterData(fop, obj);
 }
 
-const Class DebuggerFrame_class = {
-    "Frame", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGFRAME_COUNT),
-    nullptr, nullptr, nullptr, nullptr,
-    nullptr, nullptr, nullptr, DebuggerFrame_finalize
-};
-
 static NativeObject*
 CheckThisFrame(JSContext* cx, const CallArgs& args, const char* fnname, bool checkLive)
 {
     JSObject* thisobj = NonNullObject(cx, args.thisv());
     if (!thisobj)
         return nullptr;
     if (thisobj->getClass() != &DebuggerFrame_class) {
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
@@ -6944,20 +7027,16 @@ DebuggerFrame_getOlder(JSContext* cx, un
                 return false;
             return dbg->getScriptFrame(cx, iter, args.rval());
         }
     }
     args.rval().setNull();
     return true;
 }
 
-const Class DebuggerArguments_class = {
-    "Arguments", JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGARGUMENTS_COUNT)
-};
-
 /* The getter used for each element of frame.arguments. See DebuggerFrame_getArguments. */
 static bool
 DebuggerArguments_getArg(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     int32_t i = args.callee().as<JSFunction>().getExtendedSlot(0).toInt32();
 
     /* Check that the this value is an Arguments object. */
@@ -7484,28 +7563,16 @@ DebuggerObject_trace(JSTracer* trc, JSOb
      */
     if (JSObject* referent = (JSObject*) obj->as<NativeObject>().getPrivate()) {
         TraceManuallyBarrieredCrossCompartmentEdge(trc, obj, &referent,
                                                    "Debugger.Object referent");
         obj->as<NativeObject>().setPrivateUnbarriered(referent);
     }
 }
 
-const Class DebuggerObject_class = {
-    "Object",
-    JSCLASS_HAS_PRIVATE |
-    JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGOBJECT_COUNT),
-    nullptr, nullptr, nullptr, nullptr,
-    nullptr, nullptr, nullptr, nullptr,
-    nullptr,              /* call        */
-    nullptr,              /* hasInstance */
-    nullptr,              /* construct   */
-    DebuggerObject_trace
-};
-
 static NativeObject*
 DebuggerObject_checkThis(JSContext* cx, const CallArgs& args, const char* fnname)
 {
     JSObject* thisobj = NonNullObject(cx, args.thisv());
     if (!thisobj)
         return nullptr;
     if (thisobj->getClass() != &DebuggerObject_class) {
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
@@ -8705,28 +8772,16 @@ DebuggerEnv_trace(JSTracer* trc, JSObjec
      */
     if (Env* referent = (JSObject*) obj->as<NativeObject>().getPrivate()) {
         TraceManuallyBarrieredCrossCompartmentEdge(trc, obj, &referent,
                                                    "Debugger.Environment referent");
         obj->as<NativeObject>().setPrivateUnbarriered(referent);
     }
 }
 
-const Class DebuggerEnv_class = {
-    "Environment",
-    JSCLASS_HAS_PRIVATE |
-    JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGENV_COUNT),
-    nullptr, nullptr, nullptr, nullptr,
-    nullptr, nullptr, nullptr, nullptr,
-    nullptr,              /* call        */
-    nullptr,              /* hasInstance */
-    nullptr,              /* construct   */
-    DebuggerEnv_trace
-};
-
 static NativeObject*
 DebuggerEnv_checkThis(JSContext* cx, const CallArgs& args, const char* fnname,
                       bool requireDebuggee = true)
 {
     JSObject* thisobj = NonNullObject(cx, args.thisv());
     if (!thisobj)
         return nullptr;
     if (thisobj->getClass() != &DebuggerEnv_class) {
@@ -9201,17 +9256,17 @@ JS_DefineDebuggerObject(JSContext* cx, H
     RootedObject debuggeeWouldRunProto(cx);
     RootedValue debuggeeWouldRunCtor(cx);
     Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
 
     objProto = global->getOrCreateObjectPrototype(cx);
     if (!objProto)
         return false;
     debugProto = InitClass(cx, obj,
-                           objProto, &Debugger::jsclass, Debugger::construct,
+                           objProto, &Debugger::class_, Debugger::construct,
                            1, Debugger::properties, Debugger::methods, nullptr,
                            Debugger::static_methods, debugCtor.address());
     if (!debugProto)
         return false;
 
     frameProto = InitClass(cx, debugCtor, objProto, &DebuggerFrame_class,
                            DebuggerFrame_construct, 0,
                            DebuggerFrame_properties, DebuggerFrame_methods,
@@ -9305,17 +9360,17 @@ JS::dbg::onPromiseSettled(JSContext* cx,
     Debugger::slowPathPromiseHook(cx, Debugger::OnPromiseSettled, promise);
 }
 
 JS_PUBLIC_API(bool)
 JS::dbg::IsDebugger(JSObject& obj)
 {
     JSObject* unwrapped = CheckedUnwrap(&obj);
     return unwrapped &&
-           js::GetObjectClass(unwrapped) == &Debugger::jsclass &&
+           js::GetObjectClass(unwrapped) == &Debugger::class_ &&
            js::Debugger::fromJSObject(unwrapped) != nullptr;
 }
 
 JS_PUBLIC_API(bool)
 JS::dbg::GetDebuggeeGlobals(JSContext* cx, JSObject& dbgObj, AutoObjectVector& vector)
 {
     MOZ_ASSERT(IsDebugger(dbgObj));
     js::Debugger* dbg = js::Debugger::fromJSObject(CheckedUnwrap(&dbgObj));
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -554,17 +554,18 @@ class Debugger : private mozilla::Linked
 
     GlobalObject* unwrapDebuggeeArgument(JSContext* cx, const Value& v);
 
     static void traceObject(JSTracer* trc, JSObject* obj);
     void trace(JSTracer* trc);
     static void finalize(FreeOp* fop, JSObject* obj);
     void markCrossCompartmentEdges(JSTracer* tracer);
 
-    static const Class jsclass;
+    static const ClassOps classOps_;
+    static const Class class_;
 
     static bool getHookImpl(JSContext* cx, CallArgs& args, Debugger& dbg, Hook which);
     static bool setHookImpl(JSContext* cx, CallArgs& args, Debugger& dbg, Hook which);
 
     static Debugger* fromThisValue(JSContext* cx, const CallArgs& ca, const char* fnname);
     static bool getEnabled(JSContext* cx, unsigned argc, Value* vp);
     static bool setEnabled(JSContext* cx, unsigned argc, Value* vp);
     static bool getOnDebuggerStatement(JSContext* cx, unsigned argc, Value* vp);
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -288,17 +288,17 @@ GlobalObject::initBuiltinConstructor(JSC
     AddTypePropertyId(cx, global, id, ObjectValue(*ctor));
     return true;
 }
 
 GlobalObject*
 GlobalObject::createInternal(JSContext* cx, const Class* clasp)
 {
     MOZ_ASSERT(clasp->flags & JSCLASS_IS_GLOBAL);
-    MOZ_ASSERT(clasp->trace == JS_GlobalObjectTraceHook);
+    MOZ_ASSERT(clasp->isTrace(JS_GlobalObjectTraceHook));
 
     JSObject* obj = NewObjectWithGivenProto(cx, clasp, nullptr, SingletonObject);
     if (!obj)
         return nullptr;
 
     Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
     MOZ_ASSERT(global->isUnqualifiedVarObj());
 
@@ -615,21 +615,32 @@ js::DefinePropertiesAndFunctions(JSConte
 }
 
 static void
 GlobalDebuggees_finalize(FreeOp* fop, JSObject* obj)
 {
     fop->delete_((GlobalObject::DebuggerVector*) obj->as<NativeObject>().getPrivate());
 }
 
+static const ClassOps
+GlobalDebuggees_classOps = {
+    nullptr,
+    nullptr,
+    nullptr,
+    nullptr,
+    nullptr,
+    nullptr,
+    nullptr,
+    GlobalDebuggees_finalize
+};
+
 static const Class
 GlobalDebuggees_class = {
     "GlobalDebuggee", JSCLASS_HAS_PRIVATE,
-    nullptr, nullptr, nullptr, nullptr,
-    nullptr, nullptr, nullptr, GlobalDebuggees_finalize
+    &GlobalDebuggees_classOps
 };
 
 GlobalObject::DebuggerVector*
 GlobalObject::getDebuggers() const
 {
     Value debuggers = getReservedSlot(DEBUGGERS);
     if (debuggers.isUndefined())
         return nullptr;
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -130,17 +130,16 @@ class GlobalObject : public NativeObject
      * we won't expose GlobalObject, so just assert that the two values are
      * synchronized.
      */
     static_assert(JSCLASS_GLOBAL_SLOT_COUNT == RESERVED_SLOTS,
                   "global object slot counts are inconsistent");
 
     enum WarnOnceFlag : int32_t {
         WARN_WATCH_DEPRECATED                   = 1 << 0,
-        WARN_STRING_CONTAINS_DEPRECATED         = 1 << 1,
     };
 
     // Emit the specified warning if the given slot in |obj|'s global isn't
     // true, then set the slot to true.  Thus calling this method warns once
     // for each global object it's called on, and every other call does
     // nothing.
     static bool
     warnOnceAbout(JSContext* cx, HandleObject obj, WarnOnceFlag flag, unsigned errorNumber);
@@ -706,22 +705,16 @@ class GlobalObject : public NativeObject
     // in which |obj| was created, if no prior warning was given.
     static bool warnOnceAboutWatch(JSContext* cx, HandleObject obj) {
         // Temporarily disabled until we've provided a watch/unwatch workaround for
         // debuggers like Firebug (bug 934669).
         //return warnOnceAbout(cx, obj, WARN_WATCH_DEPRECATED, JSMSG_OBJECT_WATCH_DEPRECATED);
         return true;
     }
 
-    // Warn about use of the deprecated String.prototype.contains method
-    static bool warnOnceAboutStringContains(JSContext* cx, HandleObject strContains) {
-        return warnOnceAbout(cx, strContains, WARN_STRING_CONTAINS_DEPRECATED,
-                             JSMSG_DEPRECATED_STRING_CONTAINS);
-    }
-
     static bool getOrCreateEval(JSContext* cx, Handle<GlobalObject*> global,
                                 MutableHandleObject eval);
 
     // Infallibly test whether the given value is the eval function for this global.
     bool valueIsEval(Value val);
 
     // Implemented in jsiter.cpp.
     static bool initIteratorProto(JSContext* cx, Handle<GlobalObject*> global);
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -182,24 +182,28 @@ js::CancelOffThreadIonCompile(JSCompartm
         if (CompiledScriptMatches(compartment, script, builder->script())) {
             builder->script()->baselineScript()->removePendingIonBuilder(builder->script());
             jit::FinishOffThreadBuilder(nullptr, builder);
         }
         builder = next;
     }
 }
 
-static const JSClass parseTaskGlobalClass = {
-    "internal-parse-task-global", JSCLASS_GLOBAL_FLAGS,
+static const JSClassOps parseTaskGlobalClassOps = {
     nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr,
     JS_GlobalObjectTraceHook
 };
 
+static const JSClass parseTaskGlobalClass = {
+    "internal-parse-task-global", JSCLASS_GLOBAL_FLAGS,
+    &parseTaskGlobalClassOps
+};
+
 ParseTask::ParseTask(ParseTaskKind kind, ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
                      JSContext* initCx, const char16_t* chars, size_t length,
                      JS::OffThreadCompileCallback callback, void* callbackData)
   : kind(kind), cx(cx), options(initCx), chars(chars), length(length),
     alloc(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     exclusiveContextGlobal(initCx->runtime(), exclusiveContextGlobal),
     callback(callback), callbackData(callbackData),
     script(initCx->runtime()), sourceObject(initCx->runtime()),
--- a/js/src/vm/Interpreter-inl.h
+++ b/js/src/vm/Interpreter-inl.h
@@ -571,18 +571,18 @@ TypeOfObjectOperation(JSObject* obj, JSR
     JSType type = js::TypeOfObject(obj);
     return TypeName(type, *rt->commonNames);
 }
 
 static MOZ_ALWAYS_INLINE bool
 InitElemOperation(JSContext* cx, jsbytecode* pc, HandleObject obj, HandleValue idval, HandleValue val)
 {
     MOZ_ASSERT(!val.isMagic(JS_ELEMENTS_HOLE));
-    MOZ_ASSERT(!obj->getClass()->getProperty);
-    MOZ_ASSERT(!obj->getClass()->setProperty);
+    MOZ_ASSERT(!obj->getClass()->getGetProperty());
+    MOZ_ASSERT(!obj->getClass()->getSetProperty());
 
     RootedId id(cx);
     if (!ToPropertyKey(cx, idval, &id))
         return false;
 
     unsigned flags = JSOp(*pc) == JSOP_INITHIDDENELEM ? 0 : JSPROP_ENUMERATE;
     return DefineProperty(cx, obj, id, val, nullptr, nullptr, flags);
 }
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -532,17 +532,17 @@ js::Invoke(JSContext* cx, const Value& t
     return true;
 }
 
 static bool
 InternalConstruct(JSContext* cx, const AnyConstructArgs& args)
 {
     MOZ_ASSERT(args.array() + args.length() + 1 == args.end(),
                "must pass constructing arguments to a construction attempt");
-    MOZ_ASSERT(!JSFunction::class_.construct);
+    MOZ_ASSERT(!JSFunction::class_.getConstruct());
 
     // Callers are responsible for enforcing these preconditions.
     MOZ_ASSERT(IsConstructor(args.calleev()),
                "trying to construct a value that isn't a constructor");
     MOZ_ASSERT(IsConstructor(args.CallArgs::newTarget()),
                "provided new.target value must be a constructor");
 
     JSObject& callee = args.callee();
@@ -715,18 +715,18 @@ js::Execute(JSContext* cx, HandleScript 
                          NullFramePtr() /* evalInFrame */, rval);
 }
 
 bool
 js::HasInstance(JSContext* cx, HandleObject obj, HandleValue v, bool* bp)
 {
     const Class* clasp = obj->getClass();
     RootedValue local(cx, v);
-    if (clasp->hasInstance)
-        return clasp->hasInstance(cx, obj, &local, bp);
+    if (JSHasInstanceOp hasInstance = clasp->getHasInstance())
+        return hasInstance(cx, obj, &local, bp);
 
     RootedValue val(cx, ObjectValue(*obj));
     ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS,
                         JSDVG_SEARCH_STACK, val, nullptr);
     return false;
 }
 
 static inline bool
--- a/js/src/vm/NativeObject-inl.h
+++ b/js/src/vm/NativeObject-inl.h
@@ -389,26 +389,26 @@ CallResolveOp(JSContext* cx, HandleNativ
     if (resolving.alreadyStarted()) {
         // Already resolving id in obj, suppress recursion.
         *recursedp = true;
         return true;
     }
     *recursedp = false;
 
     bool resolved = false;
-    if (!obj->getClass()->resolve(cx, obj, id, &resolved))
+    if (!obj->getClass()->getResolve()(cx, obj, id, &resolved))
         return false;
 
     if (!resolved)
         return true;
 
     // Assert the mayResolve hook, if there is one, returns true for this
     // property.
-    MOZ_ASSERT_IF(obj->getClass()->mayResolve,
-                  obj->getClass()->mayResolve(cx->names(), id, obj));
+    MOZ_ASSERT_IF(obj->getClass()->getMayResolve(),
+                  obj->getClass()->getMayResolve()(cx->names(), id, obj));
 
     if (JSID_IS_INT(id) && obj->containsDenseElement(JSID_TO_INT(id))) {
         MarkDenseOrTypedArrayElementFound<CanGC>(propp);
         return true;
     }
 
     MOZ_ASSERT(!obj->is<TypedArrayObject>());
 
@@ -416,27 +416,27 @@ CallResolveOp(JSContext* cx, HandleNativ
     return true;
 }
 
 static MOZ_ALWAYS_INLINE bool
 ClassMayResolveId(const JSAtomState& names, const Class* clasp, jsid id, JSObject* maybeObj)
 {
     MOZ_ASSERT_IF(maybeObj, maybeObj->getClass() == clasp);
 
-    if (!clasp->resolve) {
+    if (!clasp->getResolve()) {
         // Sanity check: we should only have a mayResolve hook if we have a
         // resolve hook.
-        MOZ_ASSERT(!clasp->mayResolve, "Class with mayResolve hook but no resolve hook");
+        MOZ_ASSERT(!clasp->getMayResolve(), "Class with mayResolve hook but no resolve hook");
         return false;
     }
 
-    if (clasp->mayResolve) {
+    if (JSMayResolveOp mayResolve = clasp->getMayResolve()) {
         // Tell the analysis our mayResolve hooks won't trigger GC.
         JS::AutoSuppressGCAnalysis nogc;
-        if (!clasp->mayResolve(names, id, maybeObj))
+        if (!mayResolve(names, id, maybeObj))
             return false;
     }
 
     return true;
 }
 
 template <AllowGC allowGC>
 static MOZ_ALWAYS_INLINE bool
@@ -472,18 +472,17 @@ LookupOwnPropertyInline(ExclusiveContext
     // Check for a native property.
     if (Shape* shape = obj->lookup(cx, id)) {
         propp.set(shape);
         *donep = true;
         return true;
     }
 
     // id was not found in obj. Try obj's resolve hook, if any.
-    if (obj->getClass()->resolve)
-    {
+    if (obj->getClass()->getResolve()) {
         if (!cx->shouldBeJSContext() || !allowGC)
             return false;
 
         bool recursed;
         if (!CallResolveOp(cx->asJSContext(),
                            MaybeRooted<NativeObject*, allowGC>::toHandle(obj),
                            MaybeRooted<jsid, allowGC>::toHandle(id),
                            MaybeRooted<Shape*, allowGC>::toMutableHandle(propp),
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -995,17 +995,17 @@ js::NativeLookupOwnProperty<NoGC>(Exclus
                                   FakeMutableHandle<Shape*> propp);
 
 /*** [[DefineOwnProperty]] ***********************************************************************/
 
 static inline bool
 CallAddPropertyHook(ExclusiveContext* cx, HandleNativeObject obj, HandleShape shape,
                     HandleValue value)
 {
-    if (JSAddPropertyOp addProperty = obj->getClass()->addProperty) {
+    if (JSAddPropertyOp addProperty = obj->getClass()->getAddProperty()) {
         if (!cx->shouldBeJSContext())
             return false;
 
         RootedId id(cx, shape->propid());
         if (!CallJSAddPropertyOp(cx->asJSContext(), addProperty, obj, id, value)) {
             obj->removeProperty(cx, shape->propid());
             return false;
         }
@@ -1021,17 +1021,17 @@ CallAddPropertyHookDense(ExclusiveContex
     if (obj->is<ArrayObject>()) {
         ArrayObject* arr = &obj->as<ArrayObject>();
         uint32_t length = arr->length();
         if (index >= length)
             arr->setLength(cx, index + 1);
         return true;
     }
 
-    if (JSAddPropertyOp addProperty = obj->getClass()->addProperty) {
+    if (JSAddPropertyOp addProperty = obj->getClass()->getAddProperty()) {
         if (!cx->shouldBeJSContext())
             return false;
 
         if (!obj->maybeCopyElementsForWrite(cx))
             return false;
 
         RootedId id(cx, INT_TO_JSID(index));
         if (!CallJSAddPropertyOp(cx->asJSContext(), addProperty, obj, id, value)) {
@@ -1868,17 +1868,17 @@ static bool
 GetNonexistentProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
                        HandleValue receiver, IsNameLookup nameLookup, MutableHandleValue vp)
 {
     vp.setUndefined();
 
     // Non-standard extension: Call the getProperty hook. If it sets vp to a
     // value other than undefined, we're done. If not, fall through to the
     // warning/error checks below.
-    if (JSGetterOp getProperty = obj->getClass()->getProperty) {
+    if (JSGetterOp getProperty = obj->getClass()->getGetProperty()) {
         if (!CallJSGetterOp(cx, getProperty, obj, id, vp))
             return false;
 
         if (!vp.isUndefined())
             return true;
     }
 
     // If we are doing a name lookup, this is a ReferenceError.
@@ -2175,18 +2175,18 @@ js::SetPropertyByDefining(JSContext* cx,
     if (!PurgeScopeChain(cx, receiver, id))
         return false;
 
     // Steps 5.e.iii-iv. and 5.f.i. Define the new data property.
     unsigned attrs =
         existing
         ? JSPROP_IGNORE_ENUMERATE | JSPROP_IGNORE_READONLY | JSPROP_IGNORE_PERMANENT
         : JSPROP_ENUMERATE;
-    JSGetterOp getter = clasp->getProperty;
-    JSSetterOp setter = clasp->setProperty;
+    JSGetterOp getter = clasp->getGetProperty();
+    JSSetterOp setter = clasp->getSetProperty();
     MOZ_ASSERT(getter != JS_PropertyStub);
     MOZ_ASSERT(setter != JS_StrictPropertyStub);
     if (!DefineProperty(cx, receiver, id, v, getter, setter, attrs, result))
         return false;
 
     // If the receiver is native, there is one more legacy wrinkle: the class
     // JSSetterOp is called after defining the new property.
     if (setter && receiver->is<NativeObject>()) {
@@ -2446,26 +2446,26 @@ js::NativeDeleteProperty(JSContext* cx, 
     RootedShape shape(cx);
     if (!NativeLookupOwnProperty<CanGC>(cx, obj, id, &shape))
         return false;
 
     // Step 4.
     if (!shape) {
         // If no property call the class's delProperty hook, passing succeeded
         // as the result parameter. This always succeeds when there is no hook.
-        return CallJSDeletePropertyOp(cx, obj->getClass()->delProperty, obj, id, result);
+        return CallJSDeletePropertyOp(cx, obj->getClass()->getDelProperty(), obj, id, result);
     }
 
     cx->runtime()->gc.poke();
 
     // Step 6. Non-configurable property.
     if (GetShapeAttributes(obj, shape) & JSPROP_PERMANENT)
         return result.failCantDelete();
 
-    if (!CallJSDeletePropertyOp(cx, obj->getClass()->delProperty, obj, id, result))
+    if (!CallJSDeletePropertyOp(cx, obj->getClass()->getDelProperty(), obj, id, result))
         return false;
     if (!result)
         return true;
 
     // Step 5.
     if (IsImplicitDenseOrTypedArrayElement(shape)) {
         // Typed array elements are non-configurable.
         MOZ_ASSERT(!obj->is<TypedArrayObject>());
--- a/js/src/vm/NativeObject.h
+++ b/js/src/vm/NativeObject.h
@@ -1275,20 +1275,18 @@ class PlainObject : public NativeObject
   public:
     static const js::Class class_;
 };
 
 inline void
 NativeObject::privateWriteBarrierPre(void** oldval)
 {
     JS::shadow::Zone* shadowZone = this->shadowZoneFromAnyThread();
-    if (shadowZone->needsIncrementalBarrier()) {
-        if (*oldval && getClass()->trace)
-            getClass()->trace(shadowZone->barrierTracer(), this);
-    }
+    if (shadowZone->needsIncrementalBarrier() && *oldval && getClass()->hasTrace())
+        getClass()->doTrace(shadowZone->barrierTracer(), this);
 }
 
 #ifdef DEBUG
 static inline bool
 IsObjectValueInCompartment(Value v, JSCompartment* comp)
 {
     if (!v.isObject())
         return true;
--- a/js/src/vm/PIC.cpp
+++ b/js/src/vm/PIC.cpp
@@ -289,31 +289,35 @@ ForOfPIC_finalize(FreeOp* fop, JSObject*
 
 static void
 ForOfPIC_traceObject(JSTracer* trc, JSObject* obj)
 {
     if (ForOfPIC::Chain* chain = ForOfPIC::fromJSObject(&obj->as<NativeObject>()))
         chain->mark(trc);
 }
 
-const Class ForOfPIC::jsclass = {
-    "ForOfPIC", JSCLASS_HAS_PRIVATE,
+static const ClassOps ForOfPICClassOps = {
     nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, ForOfPIC_finalize,
     nullptr,              /* call        */
     nullptr,              /* hasInstance */
     nullptr,              /* construct   */
     ForOfPIC_traceObject
 };
 
+const Class ForOfPIC::class_ = {
+    "ForOfPIC", JSCLASS_HAS_PRIVATE,
+    &ForOfPICClassOps
+};
+
 /* static */ NativeObject*
 js::ForOfPIC::createForOfPICObject(JSContext* cx, Handle<GlobalObject*> global)
 {
     assertSameCompartment(cx, global);
-    NativeObject* obj = NewNativeObjectWithGivenProto(cx, &ForOfPIC::jsclass, nullptr);
+    NativeObject* obj = NewNativeObjectWithGivenProto(cx, &ForOfPIC::class_, nullptr);
     if (!obj)
         return nullptr;
     ForOfPIC::Chain* chain = cx->new_<ForOfPIC::Chain>();
     if (!chain)
         return nullptr;
     obj->setPrivate(chain);
     return obj;
 }
--- a/js/src/vm/PIC.h
+++ b/js/src/vm/PIC.h
@@ -248,22 +248,22 @@ struct ForOfPIC
         // Reset the PIC and all info associated with it.
         void reset(JSContext* cx);
 
         // Erase the stub chain.
         void eraseChain();
     };
 
     // Class for object that holds ForOfPIC chain.
-    static const Class jsclass;
+    static const Class class_;
 
     static NativeObject* createForOfPICObject(JSContext* cx, Handle<GlobalObject*> global);
 
     static inline Chain* fromJSObject(NativeObject* obj) {
-        MOZ_ASSERT(js::GetObjectClass(obj) == &ForOfPIC::jsclass);
+        MOZ_ASSERT(js::GetObjectClass(obj) == &ForOfPIC::class_);
         return (ForOfPIC::Chain*) obj->getPrivate();
     }
     static inline Chain* getOrCreate(JSContext* cx) {
         NativeObject* obj = cx->global()->getForOfPICObject();
         if (obj)
             return fromJSObject(obj);
         return create(cx);
     }
--- a/js/src/vm/ProxyObject.h
+++ b/js/src/vm/ProxyObject.h
@@ -86,18 +86,18 @@ class ProxyObject : public JSObject
         // they should be treated as such.
 
         // proxy_Trace is just a trivial wrapper around ProxyObject::trace for
         // friend api exposure.
 
         // Proxy classes are not allowed to have call or construct hooks directly. Their
         // callability is instead decided by handler()->isCallable().
         return clasp->isProxy() &&
-               clasp->trace == proxy_Trace &&
-               !clasp->call && !clasp->construct;
+               clasp->isTrace(proxy_Trace) &&
+               !clasp->getCall() && !clasp->getConstruct();
     }
 
   public:
     static unsigned grayLinkExtraSlot(JSObject* obj);
 
     void renew(JSContext* cx, const BaseProxyHandler* handler, Value priv);
 
     static void trace(JSTracer* trc, JSObject* obj);
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -180,42 +180,46 @@ RegExpObject::trace(JSTracer* trc, JSObj
         !obj->asTenured().zone()->isPreservingCode())
     {
         obj->as<RegExpObject>().NativeObject::setPrivate(nullptr);
     } else {
         shared->trace(trc);
     }
 }
 
+static const ClassOps RegExpObjectClassOps = {
+    nullptr, /* addProperty */
+    nullptr, /* delProperty */
+    nullptr, /* getProperty */
+    nullptr, /* setProperty */
+    nullptr, /* enumerate */
+    nullptr, /* resolve */
+    nullptr, /* mayResolve */
+    nullptr, /* finalize */
+    nullptr, /* call */
+    nullptr, /* hasInstance */
+    nullptr, /* construct */
+    RegExpObject::trace,
+};
+
 static const ClassSpec RegExpObjectClassSpec = {
     GenericCreateConstructor<js::regexp_construct, 2, gc::AllocKind::FUNCTION>,
     CreateRegExpPrototype,
     nullptr,
     js::regexp_static_props,
     js::regexp_methods,
     js::regexp_properties
 };
 
 const Class RegExpObject::class_ = {
     js_RegExp_str,
     JSCLASS_HAS_PRIVATE |
     JSCLASS_HAS_RESERVED_SLOTS(RegExpObject::RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_RegExp),
-    nullptr, /* addProperty */
-    nullptr, /* delProperty */
-    nullptr, /* getProperty */
-    nullptr, /* setProperty */
-    nullptr, /* enumerate */
-    nullptr, /* resolve */
-    nullptr, /* mayResolve */
-    nullptr, /* finalize */
-    nullptr, /* call */
-    nullptr, /* hasInstance */
-    nullptr, /* construct */
-    RegExpObject::trace,
+    &RegExpObjectClassOps,
     &RegExpObjectClassSpec
 };
 
 RegExpObject*
 RegExpObject::create(ExclusiveContext* cx, const char16_t* chars, size_t length, RegExpFlag flags,
                      TokenStream* tokenStream, LifoAlloc& alloc)
 {
     RootedAtom source(cx, AtomizeChars(cx, chars, length));
--- a/js/src/vm/RegExpStatics.cpp
+++ b/js/src/vm/RegExpStatics.cpp
@@ -29,33 +29,37 @@ resc_finalize(FreeOp* fop, JSObject* obj
 static void
 resc_trace(JSTracer* trc, JSObject* obj)
 {
     void* pdata = obj->as<RegExpStaticsObject>().getPrivate();
     if (pdata)
         static_cast<RegExpStatics*>(pdata)->mark(trc);
 }
 
-const Class RegExpStaticsObject::class_ = {
-    "RegExpStatics",
-    JSCLASS_HAS_PRIVATE,
+static const ClassOps RegExpStaticsObjectClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     resc_finalize,
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     resc_trace
 };
 
+const Class RegExpStaticsObject::class_ = {
+    "RegExpStatics",
+    JSCLASS_HAS_PRIVATE,
+    &RegExpStaticsObjectClassOps
+};
+
 RegExpStaticsObject*
 RegExpStatics::create(ExclusiveContext* cx, Handle<GlobalObject*> parent)
 {
     RegExpStaticsObject* obj = NewObjectWithGivenProto<RegExpStaticsObject>(cx, nullptr);
     if (!obj)
         return nullptr;
     RegExpStatics* res = cx->new_<RegExpStatics>();
     if (!res)
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -284,16 +284,31 @@ SavedFrame::finishSavedFrameInit(JSConte
 {
     // The only object with the SavedFrame::class_ that doesn't have a source
     // should be the prototype.
     proto->as<NativeObject>().setReservedSlot(SavedFrame::JSSLOT_SOURCE, NullValue());
 
     return FreezeObject(cx, proto);
 }
 
+static const ClassOps SavedFrameClassOps = {
+    nullptr,                    // addProperty
+    nullptr,                    // delProperty
+    nullptr,                    // getProperty
+    nullptr,                    // setProperty
+    nullptr,                    // enumerate
+    nullptr,                    // resolve
+    nullptr,                    // mayResolve
+    SavedFrame::finalize,       // finalize
+    nullptr,                    // call
+    nullptr,                    // hasInstance
+    nullptr,                    // construct
+    nullptr,                    // trace
+};
+
 const ClassSpec SavedFrame::classSpec_ = {
     GenericCreateConstructor<SavedFrame::construct, 0, gc::AllocKind::FUNCTION>,
     GenericCreatePrototype,
     SavedFrame::staticFunctions,
     nullptr,
     SavedFrame::protoFunctions,
     SavedFrame::protoAccessors,
     SavedFrame::finishSavedFrameInit,
@@ -301,28 +316,17 @@ const ClassSpec SavedFrame::classSpec_ =
 };
 
 /* static */ const Class SavedFrame::class_ = {
     "SavedFrame",
     JSCLASS_HAS_PRIVATE |
     JSCLASS_HAS_RESERVED_SLOTS(SavedFrame::JSSLOT_COUNT) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_SavedFrame) |
     JSCLASS_IS_ANONYMOUS,
-    nullptr,                    // addProperty
-    nullptr,                    // delProperty
-    nullptr,                    // getProperty
-    nullptr,                    // setProperty
-    nullptr,                    // enumerate
-    nullptr,                    // resolve
-    nullptr,                    // mayResolve
-    SavedFrame::finalize,       // finalize
-    nullptr,                    // call
-    nullptr,                    // hasInstance
-    nullptr,                    // construct
-    nullptr,                    // trace
+    &SavedFrameClassOps,
     &SavedFrame::classSpec_
 };
 
 /* static */ const JSFunctionSpec
 SavedFrame::staticFunctions[] = {
     JS_FS_END
 };
 
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -453,28 +453,17 @@ const ObjectOps ModuleEnvironmentObject:
     ModuleEnvironmentObject::enumerate,
     nullptr
 };
 
 const Class ModuleEnvironmentObject::class_ = {
     "ModuleEnvironmentObject",
     JSCLASS_HAS_RESERVED_SLOTS(ModuleEnvironmentObject::RESERVED_SLOTS) |
     JSCLASS_IS_ANONYMOUS,
-    nullptr,        /* addProperty */
-    nullptr,        /* delProperty */
-    nullptr,        /* getProperty */
-    nullptr,        /* setProperty */
-    nullptr,        /* enumerate   */
-    nullptr,        /* resolve     */
-    nullptr,        /* mayResolve  */
-    nullptr,        /* finalize    */
-    nullptr,        /* call        */
-    nullptr,        /* hasInstance */
-    nullptr,        /* construct   */
-    nullptr,        /* trace       */
+    JS_NULL_CLASS_OPS,
     JS_NULL_CLASS_SPEC,
     JS_NULL_CLASS_EXT,
     &ModuleEnvironmentObject::objectOps_
 };
 
 /* static */ ModuleEnvironmentObject*
 ModuleEnvironmentObject::create(ExclusiveContext* cx, HandleModuleObject module)
 {
@@ -693,18 +682,18 @@ DeclEnvObject::createTemplateObject(JSCo
     if (!obj)
         return nullptr;
 
     // Assign a fixed slot to a property with the same name as the lambda.
     Rooted<jsid> id(cx, AtomToId(fun->atom()));
     const Class*