merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 23 Nov 2016 16:39:02 +0100
changeset 323935 47f42f21541b9b98ad7db82edb996b29065debd0
parent 323884 2a47a071271f61d69019aed92da55383fdc05cef (current diff)
parent 323934 4772a0f4bed2e9f0cce209eec7560ede79097398 (diff)
child 323994 7838378b57e73777ca80b2319b8124c8d8bf615f
child 324099 1053366efde1bec1f6b826e620843cf1cf15c59a
push id24
push usermaklebus@msu.edu
push dateTue, 20 Dec 2016 03:11:33 +0000
reviewersmerge
milestone53.0a1
merge mozilla-inbound to mozilla-central a=merge
security/nss/fuzz/cert_target.cc
security/nss/fuzz/spki_target.cc
--- a/browser/components/extensions/test/browser/browser-common.ini
+++ b/browser/components/extensions/test/browser/browser-common.ini
@@ -36,16 +36,17 @@ support-files =
 [browser_ext_contextMenus_checkboxes.js]
 [browser_ext_contextMenus_icons.js]
 [browser_ext_contextMenus_onclick.js]
 [browser_ext_contextMenus_radioGroups.js]
 [browser_ext_contextMenus_uninstall.js]
 [browser_ext_contextMenus_urlPatterns.js]
 [browser_ext_currentWindow.js]
 [browser_ext_getViews.js]
+[browser_ext_incognito_views.js]
 [browser_ext_incognito_popup.js]
 [browser_ext_lastError.js]
 [browser_ext_optionsPage_privileges.js]
 [browser_ext_pageAction_context.js]
 [browser_ext_pageAction_popup.js]
 [browser_ext_pageAction_popup_resize.js]
 [browser_ext_pageAction_simple.js]
 [browser_ext_pageAction_title.js]
copy from browser/components/extensions/test/browser/browser_ext_incognito_popup.js
copy to browser/components/extensions/test/browser/browser_ext_incognito_views.js
--- a/browser/components/extensions/test/browser/browser_ext_incognito_popup.js
+++ b/browser/components/extensions/test/browser/browser_ext_incognito_views.js
@@ -1,25 +1,24 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
-add_task(function* testIncognitoPopup() {
+add_task(function* testIncognitoViews() {
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       "permissions": ["tabs"],
       "browser_action": {
         "default_popup": "popup.html",
       },
-      "page_action": {
-        "default_popup": "popup.html",
-      },
     },
 
     background: async function() {
+      window.isBackgroundPage = true;
+
       let resolveMessage;
       browser.runtime.onMessage.addListener(msg => {
         if (resolveMessage && msg.message == "popup-details") {
           resolveMessage(msg);
         }
       });
 
       let awaitPopup = windowId => {
@@ -27,27 +26,19 @@ add_task(function* testIncognitoPopup() 
           resolveMessage = resolve;
         }).then(msg => {
           browser.test.assertEq(windowId, msg.windowId, "Got popup message from correct window");
           return msg;
         });
       };
 
       let testWindow = async window => {
-        let [tab] = await browser.tabs.query({active: true, windowId: window.id});
-
-        await browser.pageAction.show(tab.id);
-        browser.test.sendMessage("click-pageAction");
+        browser.test.sendMessage("click-browserAction");
 
         let msg = await awaitPopup(window.id);
-        browser.test.assertEq(window.incognito, msg.incognito, "Correct incognito status in pageAction popup");
-
-        browser.test.sendMessage("click-browserAction");
-
-        msg = await awaitPopup(window.id);
         browser.test.assertEq(window.incognito, msg.incognito, "Correct incognito status in browserAction popup");
       };
 
       const URL = "http://example.com/incognito";
       let windowReady = new Promise(resolve => {
         browser.tabs.onUpdated.addListener(function listener(tabId, changed, tab) {
           if (changed.status == "complete" && tab.url == URL) {
             browser.tabs.onUpdated.removeListener(listener);
@@ -67,42 +58,64 @@ add_task(function* testIncognitoPopup() 
           let window = await browser.windows.create({incognito: true, url: URL});
           await windowReady;
 
           await testWindow(window);
 
           await browser.windows.remove(window.id);
         }
 
-        browser.test.notifyPass("incognito");
+        browser.test.notifyPass("incognito-views");
       } catch (error) {
         browser.test.fail(`Error: ${error} :: ${error.stack}`);
-        browser.test.notifyFail("incognito");
+        browser.test.notifyFail("incognito-views");
       }
     },
 
     files: {
       "popup.html": '<html><head><meta charset="utf-8"><script src="popup.js"></script></head></html>',
 
       "popup.js": async function() {
+        let views = browser.extension.getViews();
+
+        if (browser.extension.inIncognitoContext) {
+          let bgPage = browser.extension.getBackgroundPage();
+          browser.test.assertEq(null, bgPage, "Should not be able to access background page in incognito context");
+
+          bgPage = await browser.runtime.getBackgroundPage();
+          browser.test.assertEq(null, bgPage, "Should not be able to access background page in incognito context");
+
+          browser.test.assertEq(1, views.length, "Should only see one view in incognito popup");
+          browser.test.assertEq(window, views[0], "This window should be the only view");
+        } else {
+          let bgPage = browser.extension.getBackgroundPage();
+          browser.test.assertEq(true, bgPage.isBackgroundPage,
+                                "Should be able to access background page in non-incognito context");
+
+          bgPage = await browser.runtime.getBackgroundPage();
+          browser.test.assertEq(true, bgPage.isBackgroundPage,
+                                "Should be able to access background page in non-incognito context");
+
+          browser.test.assertEq(2, views.length, "Should only two views in non-incognito popup");
+          browser.test.assertEq(bgPage, views[0], "The background page should be the first view");
+          browser.test.assertEq(window, views[1], "This window should be the second view");
+        }
+
         let win = await browser.windows.getCurrent();
         browser.runtime.sendMessage({
           message: "popup-details",
           windowId: win.id,
           incognito: browser.extension.inIncognitoContext,
         });
+
         window.close();
       },
     },
   });
 
   extension.onMessage("click-browserAction", () => {
     clickBrowserAction(extension, Services.wm.getMostRecentWindow("navigator:browser"));
   });
 
-  extension.onMessage("click-pageAction", () => {
-    clickPageAction(extension, Services.wm.getMostRecentWindow("navigator:browser"));
-  });
-
   yield extension.startup();
-  yield extension.awaitFinish("incognito");
+  yield extension.awaitFinish("incognito-views");
   yield extension.unload();
 });
--- a/browser/config/tooltool-manifests/linux64/clang.manifest.centos6
+++ b/browser/config/tooltool-manifests/linux64/clang.manifest.centos6
@@ -9,10 +9,18 @@
 },
 {
 "size": 12072532,
 "digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
 "algorithm": "sha512",
 "filename": "gtk3.tar.xz",
 "setup": "setup.sh",
 "unpack": true
+},
+{
+"version": "sccache rev b21198a7183a2fe226ff49348b1c0b51bae9f4f8",
+"algorithm": "sha512",
+"filename": "sccache2.tar.xz",
+"unpack": true,
+"digest": "b89c40dbf28c2bd54fadf017c15a8789f6e7611252a623cc3a1507e3dd6fc9e5a50d746e81776ba856e33fdc99b4a6413ba7c3ac0aed5f4835705da2b758ef22",
+"size": 1020700
 }
 ]
--- a/build/mozconfig.cache
+++ b/build/mozconfig.cache
@@ -36,25 +36,30 @@ if test -z "$SCCACHE_DISABLE" -a -z "$no
             ;;
         *usw2.mozilla.com*)
             bucket=mozilla-releng-s3-cache-us-west-2-prod
             ;;
         esac
         ;;
     esac
 fi
+fi
 
-# builds without buildprops (eg: taskcluster or non-buildbot) and without ccache env config and without sccache disabled:
-elif test -z "$CCACHE_DIR" -a -z "$SCCACHE_DISABLE" -a -z "$no_sccache" -a -z "$MOZ_PGO_IS_SET" -a -z "$MOZ_PGO"; then
+# builds where buildprops didn't have the data (eg: taskcluster or non-buildbot) and without sccache disabled:
+if test -z "$bucket" -a -z "$SCCACHE_DISABLE" -a -z "$no_sccache" -a -z "$MOZ_PGO_IS_SET" -a -z "$MOZ_PGO"; then
 
     # prevent rerun if az is set, or wget is not available
     if test -z "$availability_zone" -a -x "$(command -v wget)"; then
-        # timeout after 1 second, and don't retry (failure indicates instance is not in ec2 or network issue)
-        # availability_zone is of the form <region><letter> where region is e.g. us-west-2, and az is us-west-2a
-        availability_zone=$(wget -T 1 -t 1 -q -O - http://169.254.169.254/latest/meta-data/placement/availability-zone || true)
+        if test -n "${TASKCLUSTER_WORKER_GROUP}"; then
+            availability_zone="${TASKCLUSTER_WORKER_GROUP}"
+        else
+            # timeout after 1 second, and don't retry (failure indicates instance is not in ec2 or network issue)
+            # availability_zone is of the form <region><letter> where region is e.g. us-west-2, and az is us-west-2a
+            availability_zone=$(wget -T 1 -t 1 -q -O - http://169.254.169.254/latest/meta-data/placement/availability-zone || true)
+        fi
         if test -z "$availability_zone" -o "$availability_zone" = "not-ec2"; then
             availability_zone=not-ec2
         else
             # region is az with last letter trimmed
             region=${availability_zone%?}
             # set S3 bucket according to tree (level)
             case "${GECKO_HEAD_REPOSITORY}" in
             *hg.mozilla.org/try*)
--- a/devtools/client/netmonitor/har/test/browser_net_har_throttle_upload.js
+++ b/devtools/client/netmonitor/har/test/browser_net_har_throttle_upload.js
@@ -19,18 +19,18 @@ function* throttleUploadTest(actuallyThr
   let { NetMonitorView } = monitor.panelWin;
   let { RequestsMenu } = NetMonitorView;
 
   const size = 4096;
   const uploadSize = actuallyThrottle ? size / 3 : 0;
 
   const request = {
     "NetworkMonitor.throttleData": {
-      roundTripTimeMean: 0,
-      roundTripTimeMax: 0,
+      latencyMean: 0,
+      latencyMax: 0,
       downloadBPSMean: 200000,
       downloadBPSMax: 200000,
       uploadBPSMean: uploadSize,
       uploadBPSMax: uploadSize,
     },
   };
   let client = monitor._controller.webConsoleClient;
 
--- a/devtools/client/netmonitor/test/browser_net_throttle.js
+++ b/devtools/client/netmonitor/test/browser_net_throttle.js
@@ -19,18 +19,18 @@ function* throttleTest(actuallyThrottle)
   info("Starting test... (actuallyThrottle = " + actuallyThrottle + ")");
 
   // When throttling, must be smaller than the length of the content
   // of SIMPLE_URL in bytes.
   const size = actuallyThrottle ? 200 : 0;
 
   const request = {
     "NetworkMonitor.throttleData": {
-      roundTripTimeMean: 0,
-      roundTripTimeMax: 0,
+      latencyMean: 0,
+      latencyMax: 0,
       downloadBPSMean: size,
       downloadBPSMax: size,
       uploadBPSMean: 10000,
       uploadBPSMax: 10000,
     },
   };
   let client = monitor._controller.webConsoleClient;
 
--- a/devtools/client/responsive.html/test/browser/browser_resize_cmd.js
+++ b/devtools/client/responsive.html/test/browser/browser_resize_cmd.js
@@ -1,19 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 /* global ResponsiveUIManager */
 /* eslint key-spacing: 0 */
 
-// This test is too slow for debug builds (bug 1297205)
-requestLongerTimeout(2);
-
 add_task(function* () {
   let manager = ResponsiveUIManager;
   let done;
 
   function isOpen() {
     return ResponsiveUIManager.isActiveForTab(gBrowser.selectedTab);
   }
 
--- a/devtools/client/responsive.html/test/browser/browser_toolbox_computed_view.js
+++ b/devtools/client/responsive.html/test/browser/browser_toolbox_computed_view.js
@@ -1,17 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-// On debug builds in particular, this test file seems to time out fairly
-// frequently.
-requestLongerTimeout(2);
-
 // Check that when the viewport is resized, the computed-view refreshes.
 
 const TEST_URI = "data:text/html;charset=utf-8,<html><style>" +
                  "div {" +
                  "  width: 500px;" +
                  "  height: 10px;" +
                  "  background: purple;" +
                  "} " +
--- a/devtools/client/responsive.html/test/browser/browser_toolbox_swap_browsers.js
+++ b/devtools/client/responsive.html/test/browser/browser_toolbox_swap_browsers.js
@@ -2,19 +2,16 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Verify that toolbox remains open when opening and closing RDM.
 
 const TEST_URL = "http://example.com/";
 
-// Bug 1297575: Too slow for debug runs
-requestLongerTimeout(2);
-
 function getServerConnections(browser) {
   ok(browser.isRemoteBrowser, "Content browser is remote");
   return ContentTask.spawn(browser, {}, function* () {
     const Cu = Components.utils;
     const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
     const { DebuggerServer } = require("devtools/server/main");
     if (!DebuggerServer._connections) {
       return 0;
--- a/devtools/client/responsive.html/test/browser/head.js
+++ b/devtools/client/responsive.html/test/browser/head.js
@@ -38,16 +38,21 @@ const OPEN_DEVICE_MODAL_VALUE = "OPEN_DE
 const { _loadPreferredDevices } = require("devtools/client/responsive.html/actions/devices");
 const { getOwnerWindow } = require("sdk/tabs/utils");
 const asyncStorage = require("devtools/shared/async-storage");
 const { addDevice, removeDevice } = require("devtools/client/shared/devices");
 
 SimpleTest.requestCompleteLog();
 SimpleTest.waitForExplicitFinish();
 
+// Toggling the RDM UI involves several docShell swap operations, which are somewhat slow
+// on debug builds. Usually we are just barely over the limit, so a blanket factor of 2
+// should be enough.
+requestLongerTimeout(2);
+
 flags.testing = true;
 Services.prefs.clearUserPref("devtools.responsive.html.displayedDeviceList");
 Services.prefs.setCharPref("devtools.devices.url",
   TEST_URI_ROOT + "devices.json");
 Services.prefs.setBoolPref("devtools.responsive.html.enabled", true);
 
 registerCleanupFunction(() => {
   flags.testing = false;
--- a/devtools/server/actors/emulation.js
+++ b/devtools/server/actors/emulation.js
@@ -89,18 +89,18 @@ let EmulationActor = protocol.ActorClass
 
   _previousNetworkThrottling: undefined,
 
   /**
    * Transform the RDP format into the internal format and then set network throttling.
    */
   setNetworkThrottling({ downloadThroughput, uploadThroughput, latency }) {
     let throttleData = {
-      roundTripTimeMean: latency,
-      roundTripTimeMax: latency,
+      latencyMean: latency,
+      latencyMax: latency,
       downloadBPSMean: downloadThroughput,
       downloadBPSMax: downloadThroughput,
       uploadBPSMean: uploadThroughput,
       uploadBPSMax: uploadThroughput,
     };
     return this._setNetworkThrottling(throttleData);
   },
 
@@ -140,21 +140,21 @@ let EmulationActor = protocol.ActorClass
   /**
    * Get network throttling and then transform the internal format into the RDP format.
    */
   getNetworkThrottling() {
     let throttleData = this._getNetworkThrottling();
     if (!throttleData) {
       return null;
     }
-    let { downloadBPSMax, uploadBPSMax, roundTripTimeMax } = throttleData;
+    let { downloadBPSMax, uploadBPSMax, latencyMax } = throttleData;
     return {
       downloadThroughput: downloadBPSMax,
       uploadThroughput: uploadBPSMax,
-      latency: roundTripTimeMax,
+      latency: latencyMax,
     };
   },
 
   _getNetworkThrottling() {
     let consoleActor = this._consoleActor;
     if (!consoleActor) {
       return null;
     }
--- a/devtools/shared/webconsole/test/unit/test_throttle.js
+++ b/devtools/shared/webconsole/test/unit/test_throttle.js
@@ -71,18 +71,18 @@ TestChannel.prototype = {
     this.listener = listener;
     this.state = "listener";
     return this.testListener;
   },
 };
 
 add_task(function*() {
   let throttler = new NetworkThrottleManager({
-    roundTripTimeMean: 1,
-    roundTripTimeMax: 1,
+    latencyMean: 1,
+    latencyMax: 1,
     downloadBPSMean: 500,
     downloadBPSMax: 500,
     uploadBPSMean: 500,
     uploadBPSMax: 500,
   });
 
   let uploadChannel = new TestChannel();
   throttler.manageUpload(uploadChannel);
--- a/devtools/shared/webconsole/throttle.js
+++ b/devtools/shared/webconsole/throttle.js
@@ -220,25 +220,24 @@ NetworkThrottleListener.prototype = {
 };
 
 /**
  * Construct a new queue that can be used to throttle the network for
  * a group of related network requests.
  *
  * meanBPS {Number} Mean bytes per second.
  * maxBPS {Number} Maximum bytes per second.
- * roundTripTimeMean {Number} Mean round trip time in milliseconds.
- * roundTripTimeMax {Number} Maximum round trip time in milliseconds.
+ * latencyMean {Number} Mean latency in milliseconds.
+ * latencyMax {Number} Maximum latency in milliseconds.
  */
-function NetworkThrottleQueue(meanBPS, maxBPS,
-                              roundTripTimeMean, roundTripTimeMax) {
+function NetworkThrottleQueue(meanBPS, maxBPS, latencyMean, latencyMax) {
   this.meanBPS = meanBPS;
   this.maxBPS = maxBPS;
-  this.roundTripTimeMean = roundTripTimeMean;
-  this.roundTripTimeMax = roundTripTimeMax;
+  this.latencyMean = latencyMean;
+  this.latencyMax = latencyMax;
 
   this.pendingRequests = new Set();
   this.downloadQueue = [];
   this.previousReads = [];
 
   this.pumping = false;
 }
 
@@ -271,17 +270,17 @@ NetworkThrottleQueue.prototype = {
    * NetworkThrottleListener when the request has started.  Initially
    * a new listener object is put into a "pending" state, until the
    * round-trip time has elapsed.  This is used to simulate latency.
    *
    * @param {NetworkThrottleListener} throttleListener the new listener
    */
   start: function (throttleListener) {
     this.pendingRequests.add(throttleListener);
-    let delay = this.random(this.roundTripTimeMean, this.roundTripTimeMax);
+    let delay = this.random(this.latencyMean, this.latencyMax);
     if (delay > 0) {
       setTimeout(() => this.allowDataFrom(throttleListener), delay);
     } else {
       this.allowDataFrom(throttleListener);
     }
   },
 
   /**
@@ -349,36 +348,36 @@ NetworkThrottleQueue.prototype = {
   },
 };
 
 /**
  * Construct a new object that can be used to throttle the network for
  * a group of related network requests.
  *
  * @param {Object} An object with the following attributes:
- * roundTripTimeMean {Number} Mean round trip time in milliseconds.
- * roundTripTimeMax {Number} Maximum round trip time in milliseconds.
+ * latencyMean {Number} Mean latency in milliseconds.
+ * latencyMax {Number} Maximum latency in milliseconds.
  * downloadBPSMean {Number} Mean bytes per second for downloads.
  * downloadBPSMax {Number} Maximum bytes per second for downloads.
  * uploadBPSMean {Number} Mean bytes per second for uploads.
  * uploadBPSMax {Number} Maximum bytes per second for uploads.
  *
  * Download throttling will not be done if downloadBPSMean and
  * downloadBPSMax are <= 0.  Upload throttling will not be done if
  * uploadBPSMean and uploadBPSMax are <= 0.
  */
-function NetworkThrottleManager({roundTripTimeMean, roundTripTimeMax,
+function NetworkThrottleManager({latencyMean, latencyMax,
                                  downloadBPSMean, downloadBPSMax,
                                  uploadBPSMean, uploadBPSMax}) {
   if (downloadBPSMax <= 0 && downloadBPSMean <= 0) {
     this.downloadQueue = null;
   } else {
     this.downloadQueue =
       new NetworkThrottleQueue(downloadBPSMean, downloadBPSMax,
-                               roundTripTimeMean, roundTripTimeMax);
+                               latencyMean, latencyMax);
   }
   if (uploadBPSMax <= 0 && uploadBPSMean <= 0) {
     this.uploadQueue = null;
   } else {
     this.uploadQueue = Cc["@mozilla.org/network/throttlequeue;1"]
       .createInstance(Ci.nsIInputChannelThrottleQueue);
     this.uploadQueue.init(uploadBPSMean, uploadBPSMax);
   }
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -5749,16 +5749,18 @@ nsDocShell::Create()
 }
 
 NS_IMETHODIMP
 nsDocShell::Destroy()
 {
   NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome,
                "Unexpected item type in docshell");
 
+  AssertOriginAttributesMatchPrivateBrowsing();
+
   if (!mIsBeingDestroyed) {
     nsCOMPtr<nsIObserverService> serv = services::GetObserverService();
     if (serv) {
       const char* msg = mItemType == typeContent ?
         NS_WEBNAVIGATION_DESTROY : NS_CHROME_WEBNAVIGATION_DESTROY;
       serv->NotifyObservers(GetAsSupports(this), msg, nullptr);
     }
   }
@@ -9506,16 +9508,18 @@ public:
                         nsIURI* aNewURI,
                         nsIPrincipal* aLoadingPrincipal,
                         bool aInPrivateBrowsing)
     : mSvc(aSvc)
     , mNewURI(aNewURI)
     , mLoadingPrincipal(aLoadingPrincipal)
     , mInPrivateBrowsing(aInPrivateBrowsing)
   {
+    MOZ_DIAGNOSTIC_ASSERT(
+      (BasePrincipal::Cast(aLoadingPrincipal)->OriginAttributesRef().mPrivateBrowsingId > 0) == aInPrivateBrowsing);
   }
 
   NS_IMETHOD
   OnComplete(nsIURI* aFaviconURI, uint32_t aDataLen,
              const uint8_t* aData, const nsACString& aMimeType) override
   {
     // Continue only if there is an associated favicon.
     if (!aFaviconURI) {
@@ -9548,16 +9552,19 @@ NS_IMPL_ISUPPORTS(nsCopyFaviconCallback,
 } // namespace
 
 void
 nsDocShell::CopyFavicon(nsIURI* aOldURI,
                         nsIURI* aNewURI,
                         nsIPrincipal* aLoadingPrincipal,
                         bool aInPrivateBrowsing)
 {
+  MOZ_DIAGNOSTIC_ASSERT(
+    (BasePrincipal::Cast(aLoadingPrincipal)->OriginAttributesRef().mPrivateBrowsingId > 0) == aInPrivateBrowsing);
+
   if (XRE_IsContentProcess()) {
     dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
     if (contentChild) {
       mozilla::ipc::URIParams oldURI, newURI;
       SerializeURI(aOldURI, oldURI);
       SerializeURI(aNewURI, newURI);
       contentChild->SendCopyFavicon(oldURI, newURI,
                                     IPC::Principal(aLoadingPrincipal),
@@ -14342,16 +14349,17 @@ nsDocShell::SetOriginAttributes(const Do
 
   bool isPrivate = mOriginAttributes.mPrivateBrowsingId > 0;
   // Chrome docshell can not contain OriginAttributes.mPrivateBrowsingId
   if (mItemType == typeChrome && isPrivate) {
     mOriginAttributes.mPrivateBrowsingId = 0;
   }
 
   SetPrivateBrowsing(isPrivate);
+  AssertOriginAttributesMatchPrivateBrowsing();
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::SetOriginAttributesBeforeLoading(JS::Handle<JS::Value> aOriginAttributes)
 {
   if (!aOriginAttributes.isObject()) {
--- a/dom/base/ChildIterator.cpp
+++ b/dom/base/ChildIterator.cpp
@@ -55,17 +55,17 @@ GetMatchedNodesForPoint(nsIContent* aCon
 {
   if (aContent->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) {
     // XBL case
     return MatchedNodes(static_cast<XBLChildrenElement*>(aContent));
   }
 
   // Web components case
   MOZ_ASSERT(aContent->IsHTMLElement(nsGkAtoms::content));
-  return MatchedNodes(static_cast<HTMLContentElement*>(aContent));
+  return MatchedNodes(HTMLContentElement::FromContent(aContent));
 }
 
 nsIContent*
 ExplicitChildIterator::GetNextChild()
 {
   // If we're already in the inserted-children array, look there first
   if (mIndexInInserted) {
     MOZ_ASSERT(mChild);
@@ -110,17 +110,17 @@ ExplicitChildIterator::GetNextChild()
   // Iterate until we find a non-insertion point, or an insertion point with
   // content.
   while (mChild) {
     // If the current child being iterated is a shadow insertion point then
     // the iterator needs to go into the projected ShadowRoot.
     if (ShadowRoot::IsShadowInsertionPoint(mChild)) {
       // Look for the next child in the projected ShadowRoot for the <shadow>
       // element.
-      HTMLShadowElement* shadowElem = static_cast<HTMLShadowElement*>(mChild);
+      HTMLShadowElement* shadowElem = HTMLShadowElement::FromContent(mChild);
       ShadowRoot* projectedShadow = shadowElem->GetOlderShadowRoot();
       if (projectedShadow) {
         mShadowIterator = new ExplicitChildIterator(projectedShadow);
         nsIContent* nextChild = mShadowIterator->GetNextChild();
         if (nextChild) {
           return nextChild;
         }
         mShadowIterator = nullptr;
@@ -265,17 +265,17 @@ ExplicitChildIterator::GetPreviousChild(
   }
 
   // Iterate until we find a non-insertion point, or an insertion point with
   // content.
   while (mChild) {
     if (ShadowRoot::IsShadowInsertionPoint(mChild)) {
       // If the current child being iterated is a shadow insertion point then
       // the iterator needs to go into the projected ShadowRoot.
-      HTMLShadowElement* shadowElem = static_cast<HTMLShadowElement*>(mChild);
+      HTMLShadowElement* shadowElem = HTMLShadowElement::FromContent(mChild);
       ShadowRoot* projectedShadow = shadowElem->GetOlderShadowRoot();
       if (projectedShadow) {
         // Create a ExplicitChildIterator that begins iterating from the end.
         mShadowIterator = new ExplicitChildIterator(projectedShadow, false);
         nsIContent* previousChild = mShadowIterator->GetPreviousChild();
         if (previousChild) {
           return previousChild;
         }
--- a/dom/base/ShadowRoot.cpp
+++ b/dom/base/ShadowRoot.cpp
@@ -557,21 +557,22 @@ ShadowRoot::ChangePoolHost(nsIContent* a
   if (mPoolHost) {
     mPoolHost->AddMutationObserver(this);
   }
 }
 
 bool
 ShadowRoot::IsShadowInsertionPoint(nsIContent* aContent)
 {
-  if (aContent && aContent->IsHTMLElement(nsGkAtoms::shadow)) {
-    HTMLShadowElement* shadowElem = static_cast<HTMLShadowElement*>(aContent);
-    return shadowElem->IsInsertionPoint();
+  if (!aContent) {
+    return false;
   }
-  return false;
+
+  HTMLShadowElement* shadowElem = HTMLShadowElement::FromContent(aContent);
+  return shadowElem && shadowElem->IsInsertionPoint();
 }
 
 /**
  * Returns whether the web components pool population algorithm
  * on the host would contain |aContent|. This function ignores
  * insertion points in the pool, thus should only be used to
  * test nodes that have not yet been distributed.
  */
@@ -589,20 +590,21 @@ ShadowRoot::IsPooledNode(nsIContent* aCo
       nsContentUtils::IsInSameAnonymousTree(aContainer, aContent)) {
     // Children of the host will end up in the pool. We check to ensure
     // that the content is in the same anonymous tree as the container
     // because anonymous content may report its container as the host
     // but it may not be in the host's child list.
     return true;
   }
 
-  if (aContainer && aContainer->IsHTMLElement(nsGkAtoms::content)) {
+  if (aContainer) {
     // Fallback content will end up in pool if its parent is a child of the host.
-    HTMLContentElement* content = static_cast<HTMLContentElement*>(aContainer);
-    return content->IsInsertionPoint() && content->MatchedNodes().IsEmpty() &&
+    HTMLContentElement* content = HTMLContentElement::FromContent(aContainer);
+    return content && content->IsInsertionPoint() &&
+           content->MatchedNodes().IsEmpty() &&
            aContainer->GetParentNode() == aHost;
   }
 
   return false;
 }
 
 void
 ShadowRoot::AttributeChanged(nsIDocument* aDocument,
@@ -634,17 +636,17 @@ ShadowRoot::ContentAppended(nsIDocument*
   }
 
   // Watch for new nodes added to the pool because the node
   // may need to be added to an insertion point.
   nsIContent* currentChild = aFirstNewContent;
   while (currentChild) {
     // Add insertion point to destination insertion points of fallback content.
     if (nsContentUtils::IsContentInsertionPoint(aContainer)) {
-      HTMLContentElement* content = static_cast<HTMLContentElement*>(aContainer);
+      HTMLContentElement* content = HTMLContentElement::FromContent(aContainer);
       if (content->MatchedNodes().IsEmpty()) {
         currentChild->DestInsertionPoints().AppendElement(aContainer);
       }
     }
 
     if (IsPooledNode(currentChild, aContainer, mPoolHost)) {
       DistributeSingleNode(currentChild);
     }
@@ -665,17 +667,17 @@ ShadowRoot::ContentInserted(nsIDocument*
     return;
   }
 
   // Watch for new nodes added to the pool because the node
   // may need to be added to an insertion point.
   if (IsPooledNode(aChild, aContainer, mPoolHost)) {
     // Add insertion point to destination insertion points of fallback content.
     if (nsContentUtils::IsContentInsertionPoint(aContainer)) {
-      HTMLContentElement* content = static_cast<HTMLContentElement*>(aContainer);
+      HTMLContentElement* content = HTMLContentElement::FromContent(aContainer);
       if (content->MatchedNodes().IsEmpty()) {
         aChild->DestInsertionPoints().AppendElement(aContainer);
       }
     }
 
     DistributeSingleNode(aChild);
   }
 }
@@ -691,17 +693,17 @@ ShadowRoot::ContentRemoved(nsIDocument* 
     DistributeAllNodes();
     mInsertionPointChanged = false;
     return;
   }
 
   // Clear destination insertion points for removed
   // fallback content.
   if (nsContentUtils::IsContentInsertionPoint(aContainer)) {
-    HTMLContentElement* content = static_cast<HTMLContentElement*>(aContainer);
+    HTMLContentElement* content = HTMLContentElement::FromContent(aContainer);
     if (content->MatchedNodes().IsEmpty()) {
       aChild->DestInsertionPoints().Clear();
     }
   }
 
   // Watch for node that is removed from the pool because
   // it may need to be removed from an insertion point.
   if (IsPooledNode(aChild, aContainer, mPoolHost)) {
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -7058,29 +7058,27 @@ nsContentUtils::GetHTMLEditor(nsPresCont
     return nullptr;
 
   nsCOMPtr<nsIEditor> editor;
   docShell->GetEditor(getter_AddRefs(editor));
   return editor;
 }
 
 bool
-nsContentUtils::IsContentInsertionPoint(const nsIContent* aContent)
+nsContentUtils::IsContentInsertionPoint(nsIContent* aContent)
 {
   // Check if the content is a XBL insertion point.
   if (aContent->IsActiveChildrenElement()) {
     return true;
   }
 
   // Check if the content is a web components content insertion point.
-  if (aContent->IsHTMLElement(nsGkAtoms::content)) {
-    return static_cast<const HTMLContentElement*>(aContent)->IsInsertionPoint();
-  }
-
-  return false;
+  HTMLContentElement* contentElement =
+    HTMLContentElement::FromContent(aContent);
+  return contentElement && contentElement->IsInsertionPoint();
 }
 
 // static
 bool
 nsContentUtils::HasDistributedChildren(nsIContent* aContent)
 {
   if (!aContent) {
     return false;
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -2407,17 +2407,17 @@ public:
    * Returns whether a content is an insertion point for XBL
    * bindings or web components ShadowRoot. In web components,
    * this corresponds to a <content> element that participates
    * in node distribution. In XBL this corresponds to an
    * <xbl:children> element in anonymous content.
    *
    * @param aContent The content to test for being an insertion point.
    */
-  static bool IsContentInsertionPoint(const nsIContent* aContent);
+  static bool IsContentInsertionPoint(nsIContent* aContent);
 
 
   /**
    * Returns whether the children of the provided content are
    * nodes that are distributed to Shadow DOM insertion points.
    */
   static bool HasDistributedChildren(nsIContent* aContent);
 
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -5726,25 +5726,46 @@ nsDocument::IsWebComponentsEnabled(JSCon
   }
 
   // Check for the webcomponents permission. See Bug 1181555.
   JSAutoCompartment ac(aCx, obj);
   JS::Rooted<JSObject*> global(aCx, JS_GetGlobalForObject(aCx, obj));
   nsCOMPtr<nsPIDOMWindowInner> window =
     do_QueryInterface(nsJSUtils::GetStaticScriptGlobal(global));
 
-  if (window) {
+  return IsWebComponentsEnabled(window);
+}
+
+bool
+nsDocument::IsWebComponentsEnabled(dom::NodeInfo* aNodeInfo)
+{
+  if (Preferences::GetBool("dom.webcomponents.enabled")) {
+    return true;
+  }
+
+  nsIDocument* doc = aNodeInfo->GetDocument();
+  // Use GetScopeObject() here so that data documents work the same way as the
+  // main document they're associated with.
+  nsCOMPtr<nsPIDOMWindowInner> window =
+    do_QueryInterface(doc->GetScopeObject());
+  return IsWebComponentsEnabled(window);
+}
+
+bool
+nsDocument::IsWebComponentsEnabled(nsPIDOMWindowInner* aWindow)
+{
+  if (aWindow) {
     nsresult rv;
     nsCOMPtr<nsIPermissionManager> permMgr =
       do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
     NS_ENSURE_SUCCESS(rv, false);
 
     uint32_t perm;
     rv = permMgr->TestPermissionFromWindow(
-      window, "moz-extremely-unstable-and-will-change-webcomponents", &perm);
+      aWindow, "moz-extremely-unstable-and-will-change-webcomponents", &perm);
     NS_ENSURE_SUCCESS(rv, false);
 
     return perm == nsIPermissionManager::ALLOW_ACTION;
   }
 
   return false;
 }
 
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -1377,17 +1377,23 @@ private:
     const nsAString& aLocalName,
     uint32_t aNamespaceID,
     ErrorResult& rv);
 
 public:
   virtual already_AddRefed<mozilla::dom::CustomElementRegistry>
     GetCustomElementRegistry() override;
 
+  // Check whether web components are enabled for the global of aObject.
   static bool IsWebComponentsEnabled(JSContext* aCx, JSObject* aObject);
+  // Check whether web components are enabled for the global of the document
+  // this nodeinfo comes from.
+  static bool IsWebComponentsEnabled(mozilla::dom::NodeInfo* aNodeInfo);
+  // Check whether web components are enabled for the given window.
+  static bool IsWebComponentsEnabled(nsPIDOMWindowInner* aWindow);
 
   RefPtr<mozilla::EventListenerManager> mListenerManager;
   RefPtr<mozilla::dom::StyleSheetList> mDOMStyleSheets;
   RefPtr<nsDOMStyleSheetSetList> mStyleSheetSetList;
   RefPtr<nsScriptLoader> mScriptLoader;
   nsDocHeaderData* mHeaderData;
   /* mIdentifierMap works as follows for IDs:
    * 1) Attribute changes affect the table immediately (removing and adding
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -1320,16 +1320,24 @@ public:
    * to let APZC be aware of it. It's used when the node may handle the apz
    * aware events and may do preventDefault to stop APZC to do default actions.
    *
    * For example, instead of scrolling page by APZ, we handle mouse wheel event
    * in HTMLInputElement with number type as increasing / decreasing its value.
    */
   virtual bool IsNodeApzAwareInternal() const;
 
+  // HTML elements named <shadow> may or may not be HTMLShadowElement.  This is
+  // a way to ask an element whether it's an HTMLShadowElement.
+  virtual bool IsHTMLShadowElement() const { return false; }
+
+  // Elements named <content> may or may not be HTMLContentElement.  This is a
+  // way to ask an element whether it's an HTMLContentElement.
+  virtual bool IsHTMLContentElement() const { return false; }
+
 protected:
   nsIURI* GetExplicitBaseURI() const {
     if (HasExplicitBaseURI()) {
       return static_cast<nsIURI*>(GetProperty(nsGkAtoms::baseURIProperty));
     }
     return nullptr;
   }
 
--- a/dom/canvas/WebGL2Context.h
+++ b/dom/canvas/WebGL2Context.h
@@ -197,19 +197,26 @@ public:
         const TexImageSourceAdapter src(anySrc, &out_error);
         TexSubImage3D(target, level, xOffset, yOffset, zOffset, width, height, depth,
                       unpackFormat, unpackType, src);
     }
 
     void TexSubImage3D(GLenum target, GLint level, GLint xOffset, GLint yOffset,
                        GLint zOffset, GLsizei width, GLsizei height, GLsizei depth,
                        GLenum unpackFormat, GLenum unpackType,
-                       const dom::ArrayBufferView& srcView, GLuint srcElemOffset,
-                       ErrorResult&)
+                       const dom::Nullable<dom::ArrayBufferView>& maybeSrcView,
+                       GLuint srcElemOffset, ErrorResult&)
     {
+        if (IsContextLost())
+            return;
+
+        if (!ValidateNonNull("texSubImage3D", maybeSrcView))
+            return;
+        const auto& srcView = maybeSrcView.Value();
+
         const TexImageSourceAdapter src(srcView, srcElemOffset);
         TexSubImage3D(target, level, xOffset, yOffset, zOffset, width, height, depth,
                       unpackFormat, unpackType, src);
     }
 
 protected:
     void TexSubImage3D(GLenum target, GLint level, GLint xOffset, GLint yOffset,
                        GLint zOffset, GLsizei width, GLsizei height, GLsizei depth,
@@ -219,17 +226,17 @@ protected:
         const uint8_t funcDims = 3;
         TexSubImage(funcName, funcDims, target, level, xOffset, yOffset, zOffset, width,
                     height, depth, unpackFormat, unpackType, src);
     }
 
 public:
     // -------------------------------------------------------------------------
     // Programs and shaders - WebGL2ContextPrograms.cpp
-    GLint GetFragDataLocation(WebGLProgram* program, const nsAString& name);
+    GLint GetFragDataLocation(const WebGLProgram& program, const nsAString& name);
 
 
     // -------------------------------------------------------------------------
     // Uniforms and attributes - WebGL2ContextUniforms.cpp
     void VertexAttribIPointer(GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset);
 
     ////////////////
 
@@ -304,78 +311,77 @@ public:
 
     // -------------------------------------------------------------------------
     // Sampler Objects - WebGL2ContextSamplers.cpp
 
     already_AddRefed<WebGLSampler> CreateSampler();
     void DeleteSampler(WebGLSampler* sampler);
     bool IsSampler(WebGLSampler* sampler);
     void BindSampler(GLuint unit, WebGLSampler* sampler);
-    void SamplerParameteri(WebGLSampler* sampler, GLenum pname, GLint param);
-    void SamplerParameteriv(WebGLSampler* sampler, GLenum pname, const dom::Int32Array& param);
-    void SamplerParameteriv(WebGLSampler* sampler, GLenum pname, const dom::Sequence<GLint>& param);
-    void SamplerParameterf(WebGLSampler* sampler, GLenum pname, GLfloat param);
-    void SamplerParameterfv(WebGLSampler* sampler, GLenum pname, const dom::Float32Array& param);
-    void SamplerParameterfv(WebGLSampler* sampler, GLenum pname, const dom::Sequence<GLfloat>& param);
-    void GetSamplerParameter(JSContext*, WebGLSampler* sampler, GLenum pname, JS::MutableHandleValue retval);
+    void SamplerParameteri(WebGLSampler& sampler, GLenum pname, GLint param);
+    void SamplerParameterf(WebGLSampler& sampler, GLenum pname, GLfloat param);
+    void GetSamplerParameter(JSContext*, const WebGLSampler& sampler, GLenum pname,
+                             JS::MutableHandleValue retval);
 
 
     // -------------------------------------------------------------------------
     // Sync objects - WebGL2ContextSync.cpp
 
     already_AddRefed<WebGLSync> FenceSync(GLenum condition, GLbitfield flags);
     bool IsSync(WebGLSync* sync);
     void DeleteSync(WebGLSync* sync);
-    GLenum ClientWaitSync(WebGLSync* sync, GLbitfield flags, GLuint64 timeout);
-    void WaitSync(WebGLSync* sync, GLbitfield flags, GLuint64 timeout);
-    void GetSyncParameter(JSContext*, WebGLSync* sync, GLenum pname, JS::MutableHandleValue retval);
+    GLenum ClientWaitSync(const WebGLSync& sync, GLbitfield flags, GLuint64 timeout);
+    void WaitSync(const WebGLSync& sync, GLbitfield flags, GLint64 timeout);
+    void GetSyncParameter(JSContext*, const WebGLSync& sync, GLenum pname,
+                          JS::MutableHandleValue retval);
 
 
     // -------------------------------------------------------------------------
     // Transform Feedback - WebGL2ContextTransformFeedback.cpp
 
     already_AddRefed<WebGLTransformFeedback> CreateTransformFeedback();
     void DeleteTransformFeedback(WebGLTransformFeedback* tf);
     bool IsTransformFeedback(WebGLTransformFeedback* tf);
     void BindTransformFeedback(GLenum target, WebGLTransformFeedback* tf);
     void BeginTransformFeedback(GLenum primitiveMode);
     void EndTransformFeedback();
     void PauseTransformFeedback();
     void ResumeTransformFeedback();
-    void TransformFeedbackVaryings(WebGLProgram* program, const dom::Sequence<nsString>& varyings, GLenum bufferMode);
-    already_AddRefed<WebGLActiveInfo> GetTransformFeedbackVarying(WebGLProgram* program, GLuint index);
+    void TransformFeedbackVaryings(WebGLProgram& program,
+                                   const dom::Sequence<nsString>& varyings,
+                                   GLenum bufferMode);
+    already_AddRefed<WebGLActiveInfo>
+    GetTransformFeedbackVarying(const WebGLProgram& program, GLuint index);
 
 
     // -------------------------------------------------------------------------
     // Uniform Buffer Objects and Transform Feedback Buffers - WebGL2ContextUniforms.cpp
     // TODO(djg): Implemented in WebGLContext
 /*
     void BindBufferBase(GLenum target, GLuint index, WebGLBuffer* buffer);
     void BindBufferRange(GLenum target, GLuint index, WebGLBuffer* buffer, GLintptr offset, GLsizeiptr size);
 */
     virtual JS::Value GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv) override;
     void GetIndexedParameter(JSContext* cx, GLenum target, GLuint index,
                              JS::MutableHandleValue retval, ErrorResult& rv);
-    void GetUniformIndices(WebGLProgram* program,
+    void GetUniformIndices(const WebGLProgram& program,
                            const dom::Sequence<nsString>& uniformNames,
                            dom::Nullable< nsTArray<GLuint> >& retval);
-    void GetActiveUniforms(JSContext* cx,
-                           WebGLProgram* program,
-                           const dom::Sequence<GLuint>& uniformIndices,
-                           GLenum pname,
+    void GetActiveUniforms(JSContext* cx, const WebGLProgram& program,
+                           const dom::Sequence<GLuint>& uniformIndices, GLenum pname,
                            JS::MutableHandleValue retval);
 
-    GLuint GetUniformBlockIndex(WebGLProgram* program, const nsAString& uniformBlockName);
-    void GetActiveUniformBlockParameter(JSContext*, WebGLProgram* program,
+    GLuint GetUniformBlockIndex(const WebGLProgram& program,
+                                const nsAString& uniformBlockName);
+    void GetActiveUniformBlockParameter(JSContext*, const WebGLProgram& program,
                                         GLuint uniformBlockIndex, GLenum pname,
-                                        JS::MutableHandleValue retval,
-                                        ErrorResult& rv);
-    void GetActiveUniformBlockName(WebGLProgram* program, GLuint uniformBlockIndex,
+                                        JS::MutableHandleValue retval, ErrorResult& rv);
+    void GetActiveUniformBlockName(const WebGLProgram& program, GLuint uniformBlockIndex,
                                    nsAString& retval);
-    void UniformBlockBinding(WebGLProgram* program, GLuint uniformBlockIndex,
+    void UniformBlockBinding(WebGLProgram& program, GLuint uniformBlockIndex,
                              GLuint uniformBlockBinding);
 
 
     // -------------------------------------------------------------------------
     // Vertex Array Object - WebGL2ContextVAOs.cpp
     // TODO(djg): Implemented in WebGLContext
 /*
     already_AddRefed<WebGLVertexArrayObject> CreateVertexArray();
--- a/dom/canvas/WebGL2ContextPrograms.cpp
+++ b/dom/canvas/WebGL2ContextPrograms.cpp
@@ -9,20 +9,20 @@
 #include "WebGLProgram.h"
 
 namespace mozilla {
 
 // -------------------------------------------------------------------------
 // Programs and shaders
 
 GLint
-WebGL2Context::GetFragDataLocation(WebGLProgram* prog, const nsAString& name)
+WebGL2Context::GetFragDataLocation(const WebGLProgram& prog, const nsAString& name)
 {
     if (IsContextLost())
         return -1;
 
-    if (!ValidateObject("getFragDataLocation: program", prog))
+    if (!ValidateObjectRef("getFragDataLocation: program", prog))
         return -1;
 
-    return prog->GetFragDataLocation(name);
+    return prog.GetFragDataLocation(name);
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGL2ContextQueries.cpp
+++ b/dom/canvas/WebGL2ContextQueries.cpp
@@ -105,41 +105,41 @@ WebGLContext::IsQuery(const WebGLQuery* 
 
     if (!ValidateObjectAllowDeleted("isQuery", query))
         return false;
 
     return query->IsQuery();
 }
 
 void
-WebGLContext::BeginQuery(GLenum target, WebGLQuery* query, const char* funcName)
+WebGLContext::BeginQuery(GLenum target, WebGLQuery& query, const char* funcName)
 {
     if (!funcName) {
         funcName = "beginQuery";
     }
 
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectAllowDeleted(funcName, query))
+    if (!ValidateObjectAllowDeleted(funcName, &query))
         return;
 
-    if (query->IsDeleted())
+    if (query.IsDeleted())
         return ErrorInvalidOperation("%s: Cannot begin a deleted query.", funcName);
 
     const auto& slot = ValidateQuerySlotByTarget(funcName, target);
     if (!slot)
         return;
 
     if (*slot)
         return ErrorInvalidOperation("%s: Query target already active.", funcName);
 
     ////
 
-    query->BeginQuery(target, *slot);
+    query.BeginQuery(target, *slot);
 }
 
 void
 WebGLContext::EndQuery(GLenum target, const char* funcName)
 {
     if (!funcName) {
         funcName = "endQuery";
     }
@@ -222,29 +222,29 @@ WebGLContext::GetQuery(JSContext* cx, GL
         break;
     }
 
     ErrorInvalidEnum("%s: Bad pname.", funcName);
     return;
 }
 
 void
-WebGLContext::GetQueryParameter(JSContext*, const WebGLQuery* query, GLenum pname,
+WebGLContext::GetQueryParameter(JSContext*, const WebGLQuery& query, GLenum pname,
                                 JS::MutableHandleValue retval, const char* funcName)
 {
     if (!funcName) {
         funcName = "getQueryParameter";
     }
 
     retval.setNull();
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectAllowDeleted(funcName, query))
+    if (!ValidateObjectAllowDeleted(funcName, &query))
         return;
 
-    if (query->IsDeleted())
+    if (query.IsDeleted())
         return ErrorInvalidOperation("%s: Query must not be deleted.", funcName);
 
-    query->GetQueryParameter(pname, retval);
+    query.GetQueryParameter(pname, retval);
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGL2ContextSamplers.cpp
+++ b/dom/canvas/WebGL2ContextSamplers.cpp
@@ -75,163 +75,91 @@ WebGL2Context::BindSampler(GLuint unit, 
         return;
 
     if (GLint(unit) >= mGLMaxTextureUnits)
         return ErrorInvalidValue("bindSampler: unit must be < %d", mGLMaxTextureUnits);
 
     if (sampler && sampler->IsDeleted())
         return ErrorInvalidOperation("bindSampler: binding deleted sampler");
 
-    WebGLContextUnchecked::BindSampler(unit, sampler);
+    ////
+
+    gl->MakeCurrent();
+    gl->fBindSampler(unit, sampler ? sampler->mGLName : 0);
+
     InvalidateResolveCacheForTextureWithTexUnit(unit);
-
     mBoundSamplers[unit] = sampler;
 }
 
 void
-WebGL2Context::SamplerParameteri(WebGLSampler* sampler, GLenum pname, GLint param)
+WebGL2Context::SamplerParameteri(WebGLSampler& sampler, GLenum pname, GLint paramInt)
 {
-    if (IsContextLost())
-        return;
-
-    if (!sampler || sampler->IsDeleted())
-        return ErrorInvalidOperation("samplerParameteri: invalid sampler");
-
-    if (!ValidateSamplerParameterParams(pname, WebGLIntOrFloat(param), "samplerParameteri"))
-        return;
-
-    sampler->SamplerParameter1i(pname, param);
-    WebGLContextUnchecked::SamplerParameteri(sampler, pname, param);
-}
-
-void
-WebGL2Context::SamplerParameteriv(WebGLSampler* sampler, GLenum pname, const dom::Int32Array& param)
-{
+    const char funcName[] = "samplerParameteri";
     if (IsContextLost())
         return;
 
-    if (!sampler || sampler->IsDeleted())
-        return ErrorInvalidOperation("samplerParameteriv: invalid sampler");
-
-    param.ComputeLengthAndData();
-    if (param.Length() < 1)
-        return /* TODO(djg): Error message */;
-
-    /* TODO(djg): All of these calls in ES3 only take 1 param */
-    if (!ValidateSamplerParameterParams(pname, WebGLIntOrFloat(param.Data()[0]), "samplerParameteriv"))
+    if (!ValidateObjectRef(funcName, sampler))
         return;
 
-    sampler->SamplerParameter1i(pname, param.Data()[0]);
-    WebGLContextUnchecked::SamplerParameteriv(sampler, pname, param.Data());
-}
-
-void
-WebGL2Context::SamplerParameteriv(WebGLSampler* sampler, GLenum pname, const dom::Sequence<GLint>& param)
-{
-    if (IsContextLost())
-        return;
-
-    if (!sampler || sampler->IsDeleted())
-        return ErrorInvalidOperation("samplerParameteriv: invalid sampler");
-
-    if (param.Length() < 1)
-        return /* TODO(djg): Error message */;
-
-    /* TODO(djg): All of these calls in ES3 only take 1 param */
-    if (!ValidateSamplerParameterParams(pname, WebGLIntOrFloat(param[0]), "samplerParameteriv"))
-        return;
-
-    sampler->SamplerParameter1i(pname, param[0]);
-    WebGLContextUnchecked::SamplerParameteriv(sampler, pname, param.Elements());
+    sampler.SamplerParameter(funcName, pname, paramInt);
 }
 
 void
-WebGL2Context::SamplerParameterf(WebGLSampler* sampler, GLenum pname, GLfloat param)
+WebGL2Context::SamplerParameterf(WebGLSampler& sampler, GLenum pname, GLfloat paramFloat)
 {
-    if (IsContextLost())
-        return;
-
-    if (!sampler || sampler->IsDeleted())
-        return ErrorInvalidOperation("samplerParameterf: invalid sampler");
-
-    if (!ValidateSamplerParameterParams(pname, WebGLIntOrFloat(param), "samplerParameterf"))
-        return;
-
-    sampler->SamplerParameter1f(pname, param);
-    WebGLContextUnchecked::SamplerParameterf(sampler, pname, param);
-}
-
-void
-WebGL2Context::SamplerParameterfv(WebGLSampler* sampler, GLenum pname, const dom::Float32Array& param)
-{
+    const char funcName[] = "samplerParameterf";
     if (IsContextLost())
         return;
 
-    if (!sampler || sampler->IsDeleted())
-        return ErrorInvalidOperation("samplerParameterfv: invalid sampler");
-
-    param.ComputeLengthAndData();
-    if (param.Length() < 1)
-        return /* TODO(djg): Error message */;
-
-    /* TODO(djg): All of these calls in ES3 only take 1 param */
-    if (!ValidateSamplerParameterParams(pname, WebGLIntOrFloat(param.Data()[0]), "samplerParameterfv"))
+    if (!ValidateObjectRef(funcName, sampler))
         return;
 
-    sampler->SamplerParameter1f(pname, param.Data()[0]);
-    WebGLContextUnchecked::SamplerParameterfv(sampler, pname, param.Data());
+    sampler.SamplerParameter(funcName, pname, WebGLIntOrFloat(paramFloat).AsInt());
 }
 
 void
-WebGL2Context::SamplerParameterfv(WebGLSampler* sampler, GLenum pname, const dom::Sequence<GLfloat>& param)
+WebGL2Context::GetSamplerParameter(JSContext*, const WebGLSampler& sampler, GLenum pname,
+                                   JS::MutableHandleValue retval)
 {
-    if (IsContextLost())
-        return;
-
-    if (!sampler || sampler->IsDeleted())
-        return ErrorInvalidOperation("samplerParameterfv: invalid sampler");
-
-    if (param.Length() < 1)
-        return /* TODO(djg): Error message */;
-
-    /* TODO(djg): All of these calls in ES3 only take 1 param */
-    if (!ValidateSamplerParameterParams(pname, WebGLIntOrFloat(param[0]), "samplerParameterfv"))
-        return;
-
-    sampler->SamplerParameter1f(pname, param[0]);
-    WebGLContextUnchecked::SamplerParameterfv(sampler, pname, param.Elements());
-}
-
-void
-WebGL2Context::GetSamplerParameter(JSContext*, WebGLSampler* sampler, GLenum pname, JS::MutableHandleValue retval)
-{
+    const char funcName[] = "getSamplerParameter";
     retval.setNull();
 
     if (IsContextLost())
         return;
 
-    if (!sampler || sampler->IsDeleted())
-        return ErrorInvalidOperation("getSamplerParameter: invalid sampler");
+    if (!ValidateObjectRef(funcName, sampler))
+        return;
 
-    if (!ValidateSamplerParameterName(pname, "getSamplerParameter"))
-        return;
+    ////
+
+    gl->MakeCurrent();
 
     switch (pname) {
     case LOCAL_GL_TEXTURE_MIN_FILTER:
     case LOCAL_GL_TEXTURE_MAG_FILTER:
     case LOCAL_GL_TEXTURE_WRAP_S:
     case LOCAL_GL_TEXTURE_WRAP_T:
     case LOCAL_GL_TEXTURE_WRAP_R:
     case LOCAL_GL_TEXTURE_COMPARE_MODE:
     case LOCAL_GL_TEXTURE_COMPARE_FUNC:
-        retval.set(JS::Int32Value(
-            WebGLContextUnchecked::GetSamplerParameteriv(sampler, pname)));
+        {
+            GLint param = 0;
+            gl->fGetSamplerParameteriv(sampler.mGLName, pname, &param);
+            retval.set(JS::Int32Value(param));
+        }
         return;
 
     case LOCAL_GL_TEXTURE_MIN_LOD:
     case LOCAL_GL_TEXTURE_MAX_LOD:
-        retval.set(JS::Float32Value(
-            WebGLContextUnchecked::GetSamplerParameterfv(sampler, pname)));
+        {
+            GLfloat param = 0;
+            gl->fGetSamplerParameterfv(sampler.mGLName, pname, &param);
+            retval.set(JS::Float32Value(param));
+        }
+        return;
+
+    default:
+        ErrorInvalidEnum("%s: invalid pname: %s", funcName, EnumName(pname));
         return;
     }
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGL2ContextSync.cpp
+++ b/dom/canvas/WebGL2ContextSync.cpp
@@ -54,81 +54,83 @@ WebGL2Context::DeleteSync(WebGLSync* syn
 
     if (!sync || sync->IsDeleted())
         return;
 
     sync->RequestDelete();
 }
 
 GLenum
-WebGL2Context::ClientWaitSync(WebGLSync* sync, GLbitfield flags, GLuint64 timeout)
+WebGL2Context::ClientWaitSync(const WebGLSync& sync, GLbitfield flags, GLuint64 timeout)
 {
+    const char funcName[] = "clientWaitSync";
     if (IsContextLost())
         return LOCAL_GL_WAIT_FAILED;
 
-    if (!sync || sync->IsDeleted()) {
-        ErrorInvalidValue("clientWaitSync: sync is not a sync object.");
+    if (!ValidateObjectRef(funcName, sync))
         return LOCAL_GL_WAIT_FAILED;
-    }
 
     if (flags != 0 && flags != LOCAL_GL_SYNC_FLUSH_COMMANDS_BIT) {
-        ErrorInvalidValue("clientWaitSync: flag must be SYNC_FLUSH_COMMANDS_BIT or 0");
+        ErrorInvalidValue("%s: `flags` must be SYNC_FLUSH_COMMANDS_BIT or 0.", funcName);
         return LOCAL_GL_WAIT_FAILED;
     }
 
     MakeContextCurrent();
-    return gl->fClientWaitSync(sync->mGLName, flags, timeout);
+    return gl->fClientWaitSync(sync.mGLName, flags, timeout);
 }
 
 void
-WebGL2Context::WaitSync(WebGLSync* sync, GLbitfield flags, GLuint64 timeout)
+WebGL2Context::WaitSync(const WebGLSync& sync, GLbitfield flags, GLint64 timeout)
 {
+    const char funcName[] = "waitSync";
     if (IsContextLost())
         return;
 
-    if (!sync || sync->IsDeleted()) {
-        ErrorInvalidValue("waitSync: sync is not a sync object.");
+    if (!ValidateObjectRef(funcName, sync))
+        return;
+
+    if (flags != 0) {
+        ErrorInvalidValue("%s: `flags` must be 0.", funcName);
         return;
     }
 
-    if (flags != 0) {
-        ErrorInvalidValue("waitSync: flags must be 0");
-        return;
-    }
-
-    if (timeout != LOCAL_GL_TIMEOUT_IGNORED) {
-        ErrorInvalidValue("waitSync: timeout must be TIMEOUT_IGNORED");
+    if (timeout != -1) {
+        ErrorInvalidValue("%s: `timeout` must be TIMEOUT_IGNORED.", funcName);
         return;
     }
 
     MakeContextCurrent();
-    gl->fWaitSync(sync->mGLName, flags, timeout);
+    gl->fWaitSync(sync.mGLName, flags, LOCAL_GL_TIMEOUT_IGNORED);
 }
 
 void
-WebGL2Context::GetSyncParameter(JSContext*, WebGLSync* sync, GLenum pname, JS::MutableHandleValue retval)
+WebGL2Context::GetSyncParameter(JSContext*, const WebGLSync& sync, GLenum pname,
+                                JS::MutableHandleValue retval)
 {
+    const char funcName[] = "getSyncParameter";
+    retval.setNull();
     if (IsContextLost())
         return;
 
-    if (!sync || sync->IsDeleted()) {
-        ErrorInvalidValue("getSyncParameter: sync is not a sync object.");
+    if (!ValidateObjectRef(funcName, sync))
         return;
-    }
 
-    retval.set(JS::NullValue());
+    ////
+
+    gl->MakeCurrent();
 
     GLint result = 0;
     switch (pname) {
     case LOCAL_GL_OBJECT_TYPE:
     case LOCAL_GL_SYNC_STATUS:
     case LOCAL_GL_SYNC_CONDITION:
     case LOCAL_GL_SYNC_FLAGS:
-        MakeContextCurrent();
-        gl->fGetSynciv(sync->mGLName, pname, 1, nullptr, &result);
+        gl->fGetSynciv(sync.mGLName, pname, 1, nullptr, &result);
         retval.set(JS::Int32Value(result));
         return;
+
+    default:
+        ErrorInvalidEnum("%s: Invalid pname 0x%04x", funcName, pname);
+        return;
     }
-
-    ErrorInvalidEnum("getSyncParameter: Invalid pname 0x%04x", pname);
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGL2ContextTransformFeedback.cpp
+++ b/dom/canvas/WebGL2ContextTransformFeedback.cpp
@@ -131,34 +131,34 @@ WebGL2Context::ResumeTransformFeedback()
 {
     if (IsContextLost())
         return;
 
     mBoundTransformFeedback->ResumeTransformFeedback();
 }
 
 void
-WebGL2Context::TransformFeedbackVaryings(WebGLProgram* program,
+WebGL2Context::TransformFeedbackVaryings(WebGLProgram& program,
                                          const dom::Sequence<nsString>& varyings,
                                          GLenum bufferMode)
 {
     if (IsContextLost())
         return;
 
-    if (!ValidateObject("transformFeedbackVaryings: program", program))
+    if (!ValidateObjectRef("transformFeedbackVaryings: program", program))
         return;
 
-    program->TransformFeedbackVaryings(varyings, bufferMode);
+    program.TransformFeedbackVaryings(varyings, bufferMode);
 }
 
 already_AddRefed<WebGLActiveInfo>
-WebGL2Context::GetTransformFeedbackVarying(WebGLProgram* program, GLuint index)
+WebGL2Context::GetTransformFeedbackVarying(const WebGLProgram& program, GLuint index)
 {
     if (IsContextLost())
         return nullptr;
 
-    if (!ValidateObject("getTransformFeedbackVarying: program", program))
+    if (!ValidateObjectRef("getTransformFeedbackVarying: program", program))
         return nullptr;
 
-    return program->GetTransformFeedbackVarying(index);
+    return program.GetTransformFeedbackVarying(index);
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGL2ContextUniforms.cpp
+++ b/dom/canvas/WebGL2ContextUniforms.cpp
@@ -125,31 +125,31 @@ WebGL2Context::GetIndexedParameter(JSCon
         ret = JS::NumberValue(binding.mRangeSize);
         break;
     }
 
     retval.set(ret);
 }
 
 void
-WebGL2Context::GetUniformIndices(WebGLProgram* program,
+WebGL2Context::GetUniformIndices(const WebGLProgram& program,
                                  const dom::Sequence<nsString>& uniformNames,
                                  dom::Nullable< nsTArray<GLuint> >& retval)
 {
     retval.SetNull();
     if (IsContextLost())
         return;
 
-    if (!ValidateObject("getUniformIndices: program", program))
+    if (!ValidateObjectRef("getUniformIndices: program", program))
         return;
 
     if (!uniformNames.Length())
         return;
 
-    program->GetUniformIndices(uniformNames, retval);
+    program.GetUniformIndices(uniformNames, retval);
 }
 
 static bool
 ValidateUniformEnum(WebGLContext* webgl, GLenum pname, const char* info)
 {
     switch (pname) {
     case LOCAL_GL_UNIFORM_TYPE:
     case LOCAL_GL_UNIFORM_SIZE:
@@ -162,149 +162,139 @@ ValidateUniformEnum(WebGLContext* webgl,
 
     default:
         webgl->ErrorInvalidEnum("%s: invalid pname: %s", info, webgl->EnumName(pname));
         return false;
     }
 }
 
 void
-WebGL2Context::GetActiveUniforms(JSContext* cx,
-                                 WebGLProgram* program,
+WebGL2Context::GetActiveUniforms(JSContext* cx, const WebGLProgram& program,
                                  const dom::Sequence<GLuint>& uniformIndices,
-                                 GLenum pname,
-                                 JS::MutableHandleValue retval)
+                                 GLenum pname, JS::MutableHandleValue retval)
 {
-    retval.set(JS::NullValue());
+    const char funcName[] = "getActiveUniforms";
+    retval.setNull();
     if (IsContextLost())
         return;
 
-    if (!ValidateUniformEnum(this, pname, "getActiveUniforms"))
+    if (!ValidateUniformEnum(this, pname, funcName))
         return;
 
-    if (!ValidateObject("getActiveUniforms: program", program))
-        return;
-
-    size_t count = uniformIndices.Length();
-    if (!count)
+    if (!ValidateObjectRef("getActiveUniforms: program", program))
         return;
 
-    GLuint progname = program->mGLName;
-    Vector<GLint> samples;
-    if (!samples.resize(count)) {
+    const auto& count = uniformIndices.Length();
+
+    JS::Rooted<JSObject*> array(cx, JS_NewArrayObject(cx, count));
+    UniquePtr<GLint[]> samples(new GLint[count]);
+    if (!array || !samples) {
+        ErrorOutOfMemory("%s: Failed to allocate buffers.", funcName);
         return;
     }
+    retval.setObject(*array);
 
     MakeContextCurrent();
-    gl->fGetActiveUniformsiv(progname, count, uniformIndices.Elements(), pname,
-                             samples.begin());
-
-    JS::Rooted<JSObject*> array(cx, JS_NewArrayObject(cx, count));
-    if (!array) {
-        return;
-    }
+    gl->fGetActiveUniformsiv(program.mGLName, count, uniformIndices.Elements(), pname,
+                             samples.get());
 
     switch (pname) {
     case LOCAL_GL_UNIFORM_TYPE:
     case LOCAL_GL_UNIFORM_SIZE:
     case LOCAL_GL_UNIFORM_BLOCK_INDEX:
     case LOCAL_GL_UNIFORM_OFFSET:
     case LOCAL_GL_UNIFORM_ARRAY_STRIDE:
     case LOCAL_GL_UNIFORM_MATRIX_STRIDE:
-        for (uint32_t i = 0; i < count; ++i) {
+        for (size_t i = 0; i < count; ++i) {
             JS::RootedValue value(cx);
             value = JS::Int32Value(samples[i]);
-            if (!JS_DefineElement(cx, array, i, value, JSPROP_ENUMERATE)) {
+            if (!JS_DefineElement(cx, array, i, value, JSPROP_ENUMERATE))
                 return;
-            }
         }
         break;
     case LOCAL_GL_UNIFORM_IS_ROW_MAJOR:
-        for (uint32_t i = 0; i < count; ++i) {
+        for (size_t i = 0; i < count; ++i) {
             JS::RootedValue value(cx);
             value = JS::BooleanValue(samples[i]);
-            if (!JS_DefineElement(cx, array, i, value, JSPROP_ENUMERATE)) {
+            if (!JS_DefineElement(cx, array, i, value, JSPROP_ENUMERATE))
                 return;
-            }
         }
         break;
 
     default:
-        return;
+        MOZ_CRASH("Invalid pname");
     }
-
-    retval.setObjectOrNull(array);
 }
 
 GLuint
-WebGL2Context::GetUniformBlockIndex(WebGLProgram* program,
+WebGL2Context::GetUniformBlockIndex(const WebGLProgram& program,
                                     const nsAString& uniformBlockName)
 {
     if (IsContextLost())
         return 0;
 
-    if (!ValidateObject("getUniformBlockIndex: program", program))
+    if (!ValidateObjectRef("getUniformBlockIndex: program", program))
         return 0;
 
-    return program->GetUniformBlockIndex(uniformBlockName);
+    return program.GetUniformBlockIndex(uniformBlockName);
 }
 
 void
-WebGL2Context::GetActiveUniformBlockParameter(JSContext* cx, WebGLProgram* program,
+WebGL2Context::GetActiveUniformBlockParameter(JSContext* cx, const WebGLProgram& program,
                                               GLuint uniformBlockIndex, GLenum pname,
                                               JS::MutableHandleValue out_retval,
                                               ErrorResult& out_error)
 {
-    out_retval.set(JS::NullValue());
+    out_retval.setNull();
     if (IsContextLost())
         return;
 
-    if (!ValidateObject("getActiveUniformBlockParameter: program", program))
+    if (!ValidateObjectRef("getActiveUniformBlockParameter: program", program))
         return;
 
     MakeContextCurrent();
 
     switch(pname) {
     case LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER:
     case LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER:
     case LOCAL_GL_UNIFORM_BLOCK_BINDING:
     case LOCAL_GL_UNIFORM_BLOCK_DATA_SIZE:
     case LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS:
-        out_retval.set(program->GetActiveUniformBlockParam(uniformBlockIndex, pname));
+        out_retval.set(program.GetActiveUniformBlockParam(uniformBlockIndex, pname));
         return;
 
     case LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES:
-        out_retval.set(program->GetActiveUniformBlockActiveUniforms(cx, uniformBlockIndex,
-                                                                    &out_error));
+        out_retval.set(program.GetActiveUniformBlockActiveUniforms(cx, uniformBlockIndex,
+                                                                   &out_error));
         return;
     }
 
     ErrorInvalidEnumInfo("getActiveUniformBlockParameter: parameter", pname);
 }
 
 void
-WebGL2Context::GetActiveUniformBlockName(WebGLProgram* program, GLuint uniformBlockIndex,
-                                         nsAString& retval)
+WebGL2Context::GetActiveUniformBlockName(const WebGLProgram& program,
+                                         GLuint uniformBlockIndex, nsAString& retval)
 {
     retval.SetIsVoid(true);
     if (IsContextLost())
         return;
 
-    if (!ValidateObject("getActiveUniformBlockName: program", program))
+    if (!ValidateObjectRef("getActiveUniformBlockName: program", program))
         return;
 
-    program->GetActiveUniformBlockName(uniformBlockIndex, retval);
+    program.GetActiveUniformBlockName(uniformBlockIndex, retval);
 }
 
 void
-WebGL2Context::UniformBlockBinding(WebGLProgram* program, GLuint uniformBlockIndex,
+WebGL2Context::UniformBlockBinding(WebGLProgram& program, GLuint uniformBlockIndex,
                                    GLuint uniformBlockBinding)
 {
     if (IsContextLost())
         return;
 
-    if (!ValidateObject("uniformBlockBinding: program", program))
+    if (!ValidateObjectRef("uniformBlockBinding: program", program))
         return;
 
-    program->UniformBlockBinding(uniformBlockIndex, uniformBlockBinding);
+    program.UniformBlockBinding(uniformBlockIndex, uniformBlockBinding);
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -159,29 +159,29 @@ struct WebGLContextOptions
     bool antialias;
     bool preserveDrawingBuffer;
     bool failIfMajorPerformanceCaveat;
 };
 
 // From WebGLContextUtils
 TexTarget TexImageTargetToTexTarget(TexImageTarget texImageTarget);
 
-class WebGLIntOrFloat {
-    enum {
+struct WebGLIntOrFloat {
+    const enum {
         Int,
         Float,
         Uint
     } mType;
+
     union {
         GLint i;
         GLfloat f;
         GLuint u;
     } mValue;
 
-public:
     explicit WebGLIntOrFloat(GLint i) : mType(Int) { mValue.i = i; }
     explicit WebGLIntOrFloat(GLfloat f) : mType(Float) { mValue.f = f; }
 
     GLint AsInt() const { return (mType == Int) ? mValue.i : NS_lroundf(mValue.f); }
     GLfloat AsFloat() const { return (mType == Float) ? mValue.f : GLfloat(mValue.i); }
 };
 
 struct IndexedBufferBinding
@@ -485,77 +485,77 @@ public:
     GetContextAttributes(dom::Nullable<dom::WebGLContextAttributes>& retval);
 
     bool IsContextLost() const { return mContextStatus != ContextNotLost; }
     void GetSupportedExtensions(dom::Nullable< nsTArray<nsString> >& retval,
                                 dom::CallerType callerType);
     void GetExtension(JSContext* cx, const nsAString& name,
                       JS::MutableHandle<JSObject*> retval,
                       dom::CallerType callerType, ErrorResult& rv);
-    void AttachShader(WebGLProgram* prog, WebGLShader* shader);
-    void BindAttribLocation(WebGLProgram* prog, GLuint location,
+    void AttachShader(WebGLProgram& prog, WebGLShader& shader);
+    void BindAttribLocation(WebGLProgram& prog, GLuint location,
                             const nsAString& name);
     void BindFramebuffer(GLenum target, WebGLFramebuffer* fb);
     void BindRenderbuffer(GLenum target, WebGLRenderbuffer* fb);
     void BindVertexArray(WebGLVertexArray* vao);
     void BlendColor(GLclampf r, GLclampf g, GLclampf b, GLclampf a);
     void BlendEquation(GLenum mode);
     void BlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha);
     void BlendFunc(GLenum sfactor, GLenum dfactor);
     void BlendFuncSeparate(GLenum srcRGB, GLenum dstRGB,
                            GLenum srcAlpha, GLenum dstAlpha);
     GLenum CheckFramebufferStatus(GLenum target);
     void Clear(GLbitfield mask);
     void ClearColor(GLclampf r, GLclampf g, GLclampf b, GLclampf a);
     void ClearDepth(GLclampf v);
     void ClearStencil(GLint v);
     void ColorMask(WebGLboolean r, WebGLboolean g, WebGLboolean b, WebGLboolean a);
-    void CompileShader(WebGLShader* shader);
+    void CompileShader(WebGLShader& shader);
     void CompileShaderANGLE(WebGLShader* shader);
     void CompileShaderBypass(WebGLShader* shader, const nsCString& shaderSource);
     already_AddRefed<WebGLFramebuffer> CreateFramebuffer();
     already_AddRefed<WebGLProgram> CreateProgram();
     already_AddRefed<WebGLRenderbuffer> CreateRenderbuffer();
     already_AddRefed<WebGLShader> CreateShader(GLenum type);
     already_AddRefed<WebGLVertexArray> CreateVertexArray();
     void CullFace(GLenum face);
     void DeleteFramebuffer(WebGLFramebuffer* fb);
     void DeleteProgram(WebGLProgram* prog);
     void DeleteRenderbuffer(WebGLRenderbuffer* rb);
     void DeleteShader(WebGLShader* shader);
     void DeleteVertexArray(WebGLVertexArray* vao);
     void DepthFunc(GLenum func);
     void DepthMask(WebGLboolean b);
     void DepthRange(GLclampf zNear, GLclampf zFar);
-    void DetachShader(WebGLProgram* prog, WebGLShader* shader);
+    void DetachShader(WebGLProgram& prog, const WebGLShader& shader);
     void DrawBuffers(const dom::Sequence<GLenum>& buffers);
     void Flush();
     void Finish();
     void FramebufferRenderbuffer(GLenum target, GLenum attachment,
                                  GLenum rbTarget, WebGLRenderbuffer* rb);
     void FramebufferTexture2D(GLenum target, GLenum attachment,
                               GLenum texImageTarget, WebGLTexture* tex,
                               GLint level);
 
     // Framebuffer validation
     bool ValidateFramebufferAttachment(const WebGLFramebuffer* fb, GLenum attachment,
                                        const char* funcName,
                                        bool badColorAttachmentIsInvalidOp = false);
 
     void FrontFace(GLenum mode);
-    already_AddRefed<WebGLActiveInfo> GetActiveAttrib(WebGLProgram* prog,
+    already_AddRefed<WebGLActiveInfo> GetActiveAttrib(const WebGLProgram& prog,
                                                       GLuint index);
-    already_AddRefed<WebGLActiveInfo> GetActiveUniform(WebGLProgram* prog,
+    already_AddRefed<WebGLActiveInfo> GetActiveUniform(const WebGLProgram& prog,
                                                        GLuint index);
 
     void
-    GetAttachedShaders(WebGLProgram* prog,
+    GetAttachedShaders(const WebGLProgram& prog,
                        dom::Nullable<nsTArray<RefPtr<WebGLShader>>>& retval);
 
-    GLint GetAttribLocation(WebGLProgram* prog, const nsAString& name);
+    GLint GetAttribLocation(const WebGLProgram& prog, const nsAString& name);
     JS::Value GetBufferParameter(GLenum target, GLenum pname);
 
     void GetBufferParameter(JSContext*, GLenum target, GLenum pname,
                             JS::MutableHandle<JS::Value> retval)
     {
         retval.set(GetBufferParameter(target, pname));
     }
 
@@ -568,71 +568,70 @@ public:
                                            GLenum attachment, GLenum pname,
                                            JS::MutableHandle<JS::Value> retval,
                                            ErrorResult& rv)
     {
         retval.set(GetFramebufferAttachmentParameter(cx, target, attachment,
                                                      pname, rv));
     }
 
-    JS::Value GetProgramParameter(WebGLProgram* prog, GLenum pname);
+    JS::Value GetProgramParameter(const WebGLProgram& prog, GLenum pname);
 
-    void  GetProgramParameter(JSContext*, WebGLProgram* prog, GLenum pname,
+    void  GetProgramParameter(JSContext*, const WebGLProgram& prog, GLenum pname,
                               JS::MutableHandle<JS::Value> retval)
     {
         retval.set(GetProgramParameter(prog, pname));
     }
 
-    void GetProgramInfoLog(WebGLProgram* prog, nsACString& retval);
-    void GetProgramInfoLog(WebGLProgram* prog, nsAString& retval);
+    void GetProgramInfoLog(const WebGLProgram& prog, nsACString& retval);
+    void GetProgramInfoLog(const WebGLProgram& prog, nsAString& retval);
     JS::Value GetRenderbufferParameter(GLenum target, GLenum pname);
 
     void GetRenderbufferParameter(JSContext*, GLenum target, GLenum pname,
                                   JS::MutableHandle<JS::Value> retval)
     {
         retval.set(GetRenderbufferParameter(target, pname));
     }
 
-    JS::Value GetShaderParameter(WebGLShader* shader, GLenum pname);
+    JS::Value GetShaderParameter(const WebGLShader& shader, GLenum pname);
 
-    void GetShaderParameter(JSContext*, WebGLShader* shader, GLenum pname,
+    void GetShaderParameter(JSContext*, const WebGLShader& shader, GLenum pname,
                             JS::MutableHandle<JS::Value> retval)
     {
         retval.set(GetShaderParameter(shader, pname));
     }
 
     already_AddRefed<WebGLShaderPrecisionFormat>
     GetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype);
 
-    void GetShaderInfoLog(WebGLShader* shader, nsACString& retval);
-    void GetShaderInfoLog(WebGLShader* shader, nsAString& retval);
-    void GetShaderSource(WebGLShader* shader, nsAString& retval);
-    void GetShaderTranslatedSource(WebGLShader* shader, nsAString& retval);
+    void GetShaderInfoLog(const WebGLShader& shader, nsACString& retval);
+    void GetShaderInfoLog(const WebGLShader& shader, nsAString& retval);
+    void GetShaderSource(const WebGLShader& shader, nsAString& retval);
 
-    JS::Value GetUniform(JSContext* cx, WebGLProgram* prog,
-                         WebGLUniformLocation* loc);
+    JS::Value GetUniform(JSContext* cx, const WebGLProgram& prog,
+                         const WebGLUniformLocation& loc);
 
-    void GetUniform(JSContext* cx, WebGLProgram* prog,
-                    WebGLUniformLocation* loc,
+    void GetUniform(JSContext* cx, const WebGLProgram& prog,
+                    const WebGLUniformLocation& loc,
                     JS::MutableHandle<JS::Value> retval)
     {
         retval.set(GetUniform(cx, prog, loc));
     }
 
     already_AddRefed<WebGLUniformLocation>
-    GetUniformLocation(WebGLProgram* prog, const nsAString& name);
+    GetUniformLocation(const WebGLProgram& prog, const nsAString& name);
 
     void Hint(GLenum target, GLenum mode);
     bool IsFramebuffer(WebGLFramebuffer* fb);
     bool IsProgram(WebGLProgram* prog);
     bool IsRenderbuffer(WebGLRenderbuffer* rb);
     bool IsShader(WebGLShader* shader);
     bool IsVertexArray(WebGLVertexArray* vao);
     void LineWidth(GLfloat width);
-    void LinkProgram(WebGLProgram* prog);
+    void LinkProgram(WebGLProgram& prog);
     void PixelStorei(GLenum pname, GLint param);
     void PolygonOffset(GLfloat factor, GLfloat units);
 
     already_AddRefed<layers::SharedSurfaceTextureClient> GetVRFrame();
     bool StartVRPresentation();
 protected:
     bool ReadPixels_SharedPrecheck(ErrorResult* const out_error);
     void ReadPixelsImpl(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format,
@@ -665,17 +664,17 @@ public:
                              GLsizei width, GLsizei height);
 protected:
     void RenderbufferStorage_base(const char* funcName, GLenum target,
                                   GLsizei samples, GLenum internalformat,
                                   GLsizei width, GLsizei height);
 public:
     void SampleCoverage(GLclampf value, WebGLboolean invert);
     void Scissor(GLint x, GLint y, GLsizei width, GLsizei height);
-    void ShaderSource(WebGLShader* shader, const nsAString& source);
+    void ShaderSource(WebGLShader& shader, const nsAString& source);
     void StencilFunc(GLenum func, GLint ref, GLuint mask);
     void StencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask);
     void StencilMask(GLuint mask);
     void StencilMaskSeparate(GLenum face, GLuint mask);
     void StencilOp(GLenum sfail, GLenum dpfail, GLenum dppass);
     void StencilOpSeparate(GLenum face, GLenum sfail, GLenum dpfail,
                            GLenum dppass);
 
@@ -866,17 +865,17 @@ public:
     bool ValidateUniformMatrixArraySetter(WebGLUniformLocation* loc,
                                           uint8_t setterCols,
                                           uint8_t setterRows,
                                           GLenum setterType,
                                           uint32_t setterArraySize,
                                           bool setterTranspose,
                                           const char* funcName,
                                           uint32_t* out_numElementsToUpload);
-    void ValidateProgram(WebGLProgram* prog);
+    void ValidateProgram(const WebGLProgram& prog);
     bool ValidateUniformLocation(const char* info, WebGLUniformLocation* loc);
     bool ValidateSamplerUniformSetter(const char* info,
                                       WebGLUniformLocation* loc, GLint value);
     void Viewport(GLint x, GLint y, GLsizei width, GLsizei height);
 // -----------------------------------------------------------------------------
 // WEBGL_lose_context
 public:
     void LoseContext();
@@ -889,32 +888,31 @@ public:
     void BindBufferRange(GLenum target, GLuint index, WebGLBuffer* buf,
                          WebGLintptr offset, WebGLsizeiptr size);
 
 private:
     void BufferDataImpl(GLenum target, size_t dataLen, const uint8_t* data, GLenum usage);
 
 public:
     void BufferData(GLenum target, WebGLsizeiptr size, GLenum usage);
+    void BufferData(GLenum target, const dom::Nullable<dom::ArrayBuffer>& maybeSrc,
+                    GLenum usage);
     void BufferData(GLenum target, const dom::ArrayBufferView& srcData, GLenum usage,
                     GLuint srcElemOffset = 0, GLuint srcElemCountOverride = 0);
-    void BufferData(GLenum target, const dom::Nullable<dom::ArrayBuffer>& maybeData,
-                    GLenum usage);
-    void BufferData(GLenum target, const dom::SharedArrayBuffer& data, GLenum usage);
 
 private:
     void BufferSubDataImpl(GLenum target, WebGLsizeiptr dstByteOffset,
                            size_t srcDataLen, const uint8_t* srcData);
 
 public:
     void BufferSubData(GLenum target, WebGLsizeiptr dstByteOffset,
                        const dom::ArrayBufferView& src, GLuint srcElemOffset = 0,
                        GLuint srcElemCountOverride = 0);
     void BufferSubData(GLenum target, WebGLsizeiptr dstByteOffset,
-                       const dom::Nullable<dom::ArrayBuffer>& maybeSrc);
+                       const dom::ArrayBuffer& src);
     void BufferSubData(GLenum target, WebGLsizeiptr dstByteOffset,
                        const dom::SharedArrayBuffer& src);
 
     already_AddRefed<WebGLBuffer> CreateBuffer();
     void DeleteBuffer(WebGLBuffer* buf);
     bool IsBuffer(WebGLBuffer* buf);
 
 protected:
@@ -941,21 +939,21 @@ protected:
 
     WebGLRefPtr<WebGLQuery>*
     ValidateQuerySlotByTarget(const char* funcName, GLenum target);
 
 public:
     already_AddRefed<WebGLQuery> CreateQuery(const char* funcName = nullptr);
     void DeleteQuery(WebGLQuery* query, const char* funcName = nullptr);
     bool IsQuery(const WebGLQuery* query, const char* funcName = nullptr);
-    void BeginQuery(GLenum target, WebGLQuery* query, const char* funcName = nullptr);
+    void BeginQuery(GLenum target, WebGLQuery& query, const char* funcName = nullptr);
     void EndQuery(GLenum target, const char* funcName = nullptr);
     void GetQuery(JSContext* cx, GLenum target, GLenum pname,
                   JS::MutableHandleValue retval, const char* funcName = nullptr);
-    void GetQueryParameter(JSContext* cx, const WebGLQuery* query, GLenum pname,
+    void GetQueryParameter(JSContext* cx, const WebGLQuery& query, GLenum pname,
                            JS::MutableHandleValue retval, const char* funcName = nullptr);
 
 
 // -----------------------------------------------------------------------------
 // State and State Requests (WebGLContextState.cpp)
 public:
     void Disable(GLenum cap);
     void Enable(GLenum cap);
@@ -1525,19 +1523,16 @@ protected:
     bool ValidateAttribPointer(bool integerMode, GLuint index, GLint size, GLenum type,
                                WebGLboolean normalized, GLsizei stride,
                                WebGLintptr byteOffset, const char* info);
     bool ValidateStencilParamsForDrawCall();
 
     bool ValidateCopyTexImage(TexInternalFormat srcFormat, TexInternalFormat dstformat,
                               WebGLTexImageFunc func, WebGLTexDimensions dims);
 
-    bool ValidateSamplerParameterName(GLenum pname, const char* info);
-    bool ValidateSamplerParameterParams(GLenum pname, const WebGLIntOrFloat& param, const char* info);
-
     bool ValidateTexImage(TexImageTarget texImageTarget,
                           GLint level, GLenum internalFormat,
                           GLint xoffset, GLint yoffset, GLint zoffset,
                           GLint width, GLint height, GLint depth,
                           GLint border, GLenum format, GLenum type,
                           WebGLTexImageFunc func, WebGLTexDimensions dims);
     bool ValidateTexImageFormat(GLenum internalFormat, WebGLTexImageFunc func,
                                 WebGLTexDimensions dims);
@@ -1597,16 +1592,25 @@ protected:
         if (MOZ_UNLIKELY(val < 0)) {
             ErrorInvalidValue("%s: `%s` must be non-negative.", funcName, argName);
             return false;
         }
         return true;
     }
 
 public:
+    template<typename T>
+    bool ValidateNonNull(const char* funcName, const dom::Nullable<T>& maybe) {
+        if (maybe.IsNull()) {
+            ErrorInvalidValue("%s: `null` is invalid.", funcName);
+            return false;
+        }
+        return true;
+    }
+
     bool ValidateArrayBufferView(const char* funcName, const dom::ArrayBufferView& view,
                                  GLuint elemOffset, GLuint elemCountOverride,
                                  uint8_t** const out_bytes, size_t* const out_byteLen);
 
 protected:
     ////
 
     void Invalidate();
@@ -1618,21 +1622,25 @@ protected:
 
     bool ConvertImage(size_t width, size_t height, size_t srcStride,
                       size_t dstStride, const uint8_t* src, uint8_t* dst,
                       WebGLTexelFormat srcFormat, bool srcPremultiplied,
                       WebGLTexelFormat dstFormat, bool dstPremultiplied,
                       size_t dstTexelSize);
 
     //////
-
+public:
     // Returns false if `object` is null or not valid.
     template<class ObjectType>
     bool ValidateObject(const char* info, const ObjectType* object);
 
+    // Returns false if `object` is not valid.
+    template<class ObjectType>
+    bool ValidateObjectRef(const char* info, const ObjectType& object);
+
     // Returns false if `object` is not valid.  Considers null to be valid.
     template<class ObjectType>
     bool ValidateObjectAllowNull(const char* info, const ObjectType* object);
 
     // Returns false if `object` is not valid, but considers deleted objects and
     // null objects valid.
     template<class ObjectType>
     bool ValidateObjectAllowDeletedOrNull(const char* info, const ObjectType* object);
@@ -1927,18 +1935,17 @@ ToSupports(WebGLContext* webgl)
 }
 
 /**
  ** Template implementations
  **/
 
 template<class ObjectType>
 inline bool
-WebGLContext::ValidateObjectAllowDeletedOrNull(const char* info,
-                                               const ObjectType* object)
+WebGLContext::ValidateObjectAllowDeletedOrNull(const char* info, const ObjectType* object)
 {
     if (object && !object->IsCompatibleWithContext(this)) {
         ErrorInvalidOperation("%s: object from different WebGL context "
                               "(or older generation of this one) "
                               "passed as argument", info);
         return false;
     }
 
@@ -1991,16 +1998,24 @@ WebGLContext::ValidateObject(const char*
     if (!object) {
         ErrorInvalidValue("%s: null object passed as argument", info);
         return false;
     }
 
     return ValidateObjectAssumeNonNull(info, object);
 }
 
+template<class ObjectType>
+inline bool
+WebGLContext::ValidateObjectRef(const char* info, const ObjectType& object)
+{
+    MOZ_ASSERT(bool(&object));
+    return ValidateObjectAssumeNonNull(info, &object);
+}
+
 // Returns `value` rounded to the next highest multiple of `multiple`.
 // AKA PadToAlignment, StrideForAlignment.
 template<typename V, typename M>
 V
 RoundUpToMultipleOf(const V& value, const M& multiple)
 {
     return ((value + multiple - 1) / multiple) * multiple;
 }
--- a/dom/canvas/WebGLContextBuffers.cpp
+++ b/dom/canvas/WebGLContextBuffers.cpp
@@ -330,35 +330,25 @@ WebGLContext::BufferData(GLenum target, 
     const UniqueBuffer zeroBuffer(calloc(size, 1));
     if (!zeroBuffer)
         return ErrorOutOfMemory("%s: Failed to allocate zeros.", funcName);
 
     BufferDataImpl(target, size_t(size), (const uint8_t*)zeroBuffer.get(), usage);
 }
 
 void
-WebGLContext::BufferData(GLenum target, const dom::SharedArrayBuffer& src, GLenum usage)
-{
-    if (IsContextLost())
-        return;
-
-    src.ComputeLengthAndData();
-    BufferDataImpl(target, src.LengthAllowShared(), src.DataAllowShared(), usage);
-}
-
-void
 WebGLContext::BufferData(GLenum target, const dom::Nullable<dom::ArrayBuffer>& maybeSrc,
                          GLenum usage)
 {
     if (IsContextLost())
         return;
 
-    if (maybeSrc.IsNull())
-        return ErrorInvalidValue("bufferData: null object passed");
-    auto& src = maybeSrc.Value();
+    if (!ValidateNonNull("bufferData", maybeSrc))
+        return;
+    const auto& src = maybeSrc.Value();
 
     src.ComputeLengthAndData();
     BufferDataImpl(target, src.LengthAllowShared(), src.DataAllowShared(), usage);
 }
 
 void
 WebGLContext::BufferData(GLenum target, const dom::ArrayBufferView& src, GLenum usage,
                          GLuint srcElemOffset, GLuint srcElemCountOverride)
@@ -418,33 +408,17 @@ WebGLContext::BufferSubDataImpl(GLenum t
     gl->fBufferSubData(target, dstByteOffset, glDataLen, data);
 
     // Warning: Possibly shared memory.  See bug 1225033.
     buffer->ElementArrayCacheBufferSubData(dstByteOffset, data, size_t(glDataLen));
 }
 
 void
 WebGLContext::BufferSubData(GLenum target, WebGLsizeiptr dstByteOffset,
-                            const dom::Nullable<dom::ArrayBuffer>& maybeSrc)
-{
-    if (IsContextLost())
-        return;
-
-    if (maybeSrc.IsNull())
-        return ErrorInvalidValue("BufferSubData: returnedData is null.");
-    auto& src = maybeSrc.Value();
-
-    src.ComputeLengthAndData();
-    BufferSubDataImpl(target, dstByteOffset, src.LengthAllowShared(),
-                      src.DataAllowShared());
-}
-
-void
-WebGLContext::BufferSubData(GLenum target, WebGLsizeiptr dstByteOffset,
-                            const dom::SharedArrayBuffer& src)
+                            const dom::ArrayBuffer& src)
 {
     if (IsContextLost())
         return;
 
     src.ComputeLengthAndData();
     BufferSubDataImpl(target, dstByteOffset, src.LengthAllowShared(),
                       src.DataAllowShared());
 }
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -82,41 +82,41 @@ WebGLContext::ActiveTexture(GLenum textu
     }
 
     MakeContextCurrent();
     mActiveTexture = texture - LOCAL_GL_TEXTURE0;
     gl->fActiveTexture(texture);
 }
 
 void
-WebGLContext::AttachShader(WebGLProgram* program, WebGLShader* shader)
+WebGLContext::AttachShader(WebGLProgram& program, WebGLShader& shader)
 {
     if (IsContextLost())
         return;
 
-    if (!ValidateObject("attachShader: program", program) ||
-        !ValidateObject("attachShader: shader", shader))
+    if (!ValidateObjectRef("attachShader: program", program) ||
+        !ValidateObjectRef("attachShader: shader", shader))
     {
         return;
     }
 
-    program->AttachShader(shader);
+    program.AttachShader(&shader);
 }
 
 void
-WebGLContext::BindAttribLocation(WebGLProgram* prog, GLuint location,
+WebGLContext::BindAttribLocation(WebGLProgram& prog, GLuint location,
                                  const nsAString& name)
 {
     if (IsContextLost())
         return;
 
-    if (!ValidateObject("bindAttribLocation: program", prog))
+    if (!ValidateObjectRef("bindAttribLocation: program", prog))
         return;
 
-    prog->BindAttribLocation(location, name);
+    prog.BindAttribLocation(location, name);
 }
 
 void
 WebGLContext::BindFramebuffer(GLenum target, WebGLFramebuffer* wfb)
 {
     if (IsContextLost())
         return;
 
@@ -431,30 +431,30 @@ WebGLContext::DeleteShader(WebGLShader* 
 
     if (!shader || shader->IsDeleted())
         return;
 
     shader->RequestDelete();
 }
 
 void
-WebGLContext::DetachShader(WebGLProgram* program, WebGLShader* shader)
+WebGLContext::DetachShader(WebGLProgram& program, const WebGLShader& shader)
 {
     if (IsContextLost())
         return;
 
     // It's valid to attempt to detach a deleted shader, since it's still a
     // shader.
-    if (!ValidateObject("detachShader: program", program) ||
-        !ValidateObjectAllowDeleted("detashShader: shader", shader))
+    if (!ValidateObjectRef("detachShader: program", program) ||
+        !ValidateObjectAllowDeleted("detachShader: shader", &shader))
     {
         return;
     }
 
-    program->DetachShader(shader);
+    program.DetachShader(&shader);
 }
 
 void
 WebGLContext::DepthFunc(GLenum func)
 {
     if (IsContextLost())
         return;
 
@@ -559,68 +559,63 @@ WebGLContext::FrontFace(GLenum mode)
             return ErrorInvalidEnumInfo("frontFace: mode", mode);
     }
 
     MakeContextCurrent();
     gl->fFrontFace(mode);
 }
 
 already_AddRefed<WebGLActiveInfo>
-WebGLContext::GetActiveAttrib(WebGLProgram* prog, GLuint index)
+WebGLContext::GetActiveAttrib(const WebGLProgram& prog, GLuint index)
 {
     if (IsContextLost())
         return nullptr;
 
-    if (!ValidateObject("getActiveAttrib: program", prog))
+    if (!ValidateObjectRef("getActiveAttrib: program", prog))
         return nullptr;
 
-    return prog->GetActiveAttrib(index);
+    return prog.GetActiveAttrib(index);
 }
 
 already_AddRefed<WebGLActiveInfo>
-WebGLContext::GetActiveUniform(WebGLProgram* prog, GLuint index)
+WebGLContext::GetActiveUniform(const WebGLProgram& prog, GLuint index)
 {
     if (IsContextLost())
         return nullptr;
 
-    if (!ValidateObject("getActiveUniform: program", prog))
+    if (!ValidateObjectRef("getActiveUniform: program", prog))
         return nullptr;
 
-    return prog->GetActiveUniform(index);
+    return prog.GetActiveUniform(index);
 }
 
 void
-WebGLContext::GetAttachedShaders(WebGLProgram* prog,
+WebGLContext::GetAttachedShaders(const WebGLProgram& prog,
                                  dom::Nullable<nsTArray<RefPtr<WebGLShader>>>& retval)
 {
     retval.SetNull();
     if (IsContextLost())
         return;
 
-    if (!prog) {
-        ErrorInvalidValue("getAttachedShaders: Invalid program.");
+    if (!ValidateObjectRef("getAttachedShaders", prog))
         return;
-    }
-
-    if (!ValidateObject("getAttachedShaders", prog))
-        return;
-
-    prog->GetAttachedShaders(&retval.SetValue());
+
+    prog.GetAttachedShaders(&retval.SetValue());
 }
 
 GLint
-WebGLContext::GetAttribLocation(WebGLProgram* prog, const nsAString& name)
+WebGLContext::GetAttribLocation(const WebGLProgram& prog, const nsAString& name)
 {
     if (IsContextLost())
         return -1;
 
-    if (!ValidateObject("getAttribLocation: program", prog))
+    if (!ValidateObjectRef("getAttribLocation: program", prog))
         return -1;
 
-    return prog->GetAttribLocation(name);
+    return prog.GetAttribLocation(name);
 }
 
 JS::Value
 WebGLContext::GetBufferParameter(GLenum target, GLenum pname)
 {
     if (IsContextLost())
         return JS::NullValue();
 
@@ -905,72 +900,70 @@ WebGLContext::GetError()
     MakeContextCurrent();
     GetAndFlushUnderlyingGLErrors();
 
     err = GetAndClearError(&mUnderlyingGLError);
     return err;
 }
 
 JS::Value
-WebGLContext::GetProgramParameter(WebGLProgram* prog, GLenum pname)
+WebGLContext::GetProgramParameter(const WebGLProgram& prog, GLenum pname)
 {
     if (IsContextLost())
         return JS::NullValue();
 
-    if (!ValidateObjectAllowDeleted("getProgramParameter: program", prog))
+    if (!ValidateObjectAllowDeleted("getProgramParameter: program", &prog))
         return JS::NullValue();
 
-    return prog->GetProgramParameter(pname);
+    return prog.GetProgramParameter(pname);
 }
 
 void
-WebGLContext::GetProgramInfoLog(WebGLProgram* prog, nsAString& retval)
+WebGLContext::GetProgramInfoLog(const WebGLProgram& prog, nsAString& retval)
 {
     retval.SetIsVoid(true);
 
     if (IsContextLost())
         return;
 
-    if (!ValidateObject("getProgramInfoLog: program", prog))
+    if (!ValidateObjectRef("getProgramInfoLog: program", prog))
         return;
 
-    prog->GetProgramInfoLog(&retval);
-
-    retval.SetIsVoid(false);
+    prog.GetProgramInfoLog(&retval);
 }
 
 JS::Value
-WebGLContext::GetUniform(JSContext* js, WebGLProgram* prog,
-                         WebGLUniformLocation* loc)
+WebGLContext::GetUniform(JSContext* js, const WebGLProgram& prog,
+                         const WebGLUniformLocation& loc)
 {
     if (IsContextLost())
         return JS::NullValue();
 
-    if (!ValidateObject("getUniform: `program`", prog))
+    if (!ValidateObjectRef("getUniform: `program`", prog))
         return JS::NullValue();
 
-    if (!ValidateObject("getUniform: `location`", loc))
+    if (!ValidateObjectRef("getUniform: `location`", loc))
         return JS::NullValue();
 
-    if (!loc->ValidateForProgram(prog, "getUniform"))
+    if (!loc.ValidateForProgram(&prog, "getUniform"))
         return JS::NullValue();
 
-    return loc->GetUniform(js);
+    return loc.GetUniform(js);
 }
 
 already_AddRefed<WebGLUniformLocation>
-WebGLContext::GetUniformLocation(WebGLProgram* prog, const nsAString& name)
+WebGLContext::GetUniformLocation(const WebGLProgram& prog, const nsAString& name)
 {
     if (IsContextLost())
         return nullptr;
 
-    if (!ValidateObject("getUniformLocation: program", prog))
+    if (!ValidateObjectRef("getUniformLocation: program", prog))
         return nullptr;
 
-    return prog->GetUniformLocation(name);
+    return prog.GetUniformLocation(name);
 }
 
 void
 WebGLContext::Hint(GLenum target, GLenum mode)
 {
     if (IsContextLost())
         return;
 
@@ -1055,39 +1048,39 @@ WebGLContext::IsShader(WebGLShader* shad
     if (IsContextLost())
         return false;
 
     return ValidateObjectAllowDeleted("isShader", shader) &&
         !shader->IsDeleted();
 }
 
 void
-WebGLContext::LinkProgram(WebGLProgram* prog)
+WebGLContext::LinkProgram(WebGLProgram& prog)
 {
     if (IsContextLost())
         return;
 
-    if (!ValidateObject("linkProgram", prog))
+    if (!ValidateObjectRef("linkProgram", prog))
         return;
 
-    prog->LinkProgram();
-
-    if (!prog->IsLinked()) {
+    prog.LinkProgram();
+
+    if (!prog.IsLinked()) {
         // If we failed to link, but `prog == mCurrentProgram`, we are *not* supposed to
         // null out mActiveProgramLinkInfo.
         return;
     }
 
-    if (prog == mCurrentProgram) {
-        mActiveProgramLinkInfo = prog->LinkInfo();
+    if (&prog == mCurrentProgram) {
+        mActiveProgramLinkInfo = prog.LinkInfo();
 
         if (gl->WorkAroundDriverBugs() &&
             gl->Vendor() == gl::GLVendor::NVIDIA)
         {
-            gl->fUseProgram(prog->mGLName);
+            gl->fUseProgram(prog.mGLName);
         }
     }
 }
 
 void
 WebGLContext::PixelStorei(GLenum pname, GLint param)
 {
     if (IsContextLost())
@@ -2207,25 +2200,25 @@ WebGLContext::UseProgram(WebGLProgram* p
 
     if (prog->UseProgram()) {
         mCurrentProgram = prog;
         mActiveProgramLinkInfo = mCurrentProgram->LinkInfo();
     }
 }
 
 void
-WebGLContext::ValidateProgram(WebGLProgram* prog)
+WebGLContext::ValidateProgram(const WebGLProgram& prog)
 {
     if (IsContextLost())
         return;
 
-    if (!ValidateObject("validateProgram", prog))
+    if (!ValidateObjectRef("validateProgram", prog))
         return;
 
-    prog->ValidateProgram();
+    prog.ValidateProgram();
 }
 
 already_AddRefed<WebGLFramebuffer>
 WebGLContext::CreateFramebuffer()
 {
     if (IsContextLost())
         return nullptr;
 
@@ -2262,53 +2255,51 @@ WebGLContext::Viewport(GLint x, GLint y,
 
     mViewportX = x;
     mViewportY = y;
     mViewportWidth = width;
     mViewportHeight = height;
 }
 
 void
-WebGLContext::CompileShader(WebGLShader* shader)
+WebGLContext::CompileShader(WebGLShader& shader)
 {
     if (IsContextLost())
         return;
 
-    if (!ValidateObject("compileShader", shader))
+    if (!ValidateObjectRef("compileShader", shader))
         return;
 
-    shader->CompileShader();
+    shader.CompileShader();
 }
 
 JS::Value
-WebGLContext::GetShaderParameter(WebGLShader* shader, GLenum pname)
+WebGLContext::GetShaderParameter(const WebGLShader& shader, GLenum pname)
 {
     if (IsContextLost())
         return JS::NullValue();
 
-    if (!ValidateObject("getShaderParameter: shader", shader))
+    if (!ValidateObjectRef("getShaderParameter: shader", shader))
         return JS::NullValue();
 
-    return shader->GetShaderParameter(pname);
+    return shader.GetShaderParameter(pname);
 }
 
 void
-WebGLContext::GetShaderInfoLog(WebGLShader* shader, nsAString& retval)
+WebGLContext::GetShaderInfoLog(const WebGLShader& shader, nsAString& retval)
 {
     retval.SetIsVoid(true);
 
     if (IsContextLost())
         return;
 
-    if (!ValidateObject("getShaderInfoLog: shader", shader))
+    if (!ValidateObjectRef("getShaderInfoLog: shader", shader))
         return;
 
-    shader->GetShaderInfoLog(&retval);
-
-    retval.SetIsVoid(false);
+    shader.GetShaderInfoLog(&retval);
 }
 
 already_AddRefed<WebGLShaderPrecisionFormat>
 WebGLContext::GetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype)
 {
     if (IsContextLost())
         return nullptr;
 
@@ -2350,53 +2341,39 @@ WebGLContext::GetShaderPrecisionFormat(G
     }
 
     RefPtr<WebGLShaderPrecisionFormat> retShaderPrecisionFormat
         = new WebGLShaderPrecisionFormat(this, range[0], range[1], precision);
     return retShaderPrecisionFormat.forget();
 }
 
 void
-WebGLContext::GetShaderSource(WebGLShader* shader, nsAString& retval)
+WebGLContext::GetShaderSource(const WebGLShader& shader, nsAString& retval)
 {
     retval.SetIsVoid(true);
 
     if (IsContextLost())
         return;
 
-    if (!ValidateObject("getShaderSource: shader", shader))
+    if (!ValidateObjectRef("getShaderSource: shader", shader))
         return;
 
-    shader->GetShaderSource(&retval);
+    shader.GetShaderSource(&retval);
 }
 
 void
-WebGLContext::ShaderSource(WebGLShader* shader, const nsAString& source)
+WebGLContext::ShaderSource(WebGLShader& shader, const nsAString& source)
 {
     if (IsContextLost())
         return;
 
-    if (!ValidateObject("shaderSource: shader", shader))
+    if (!ValidateObjectRef("shaderSource: shader", shader))
         return;
 
-    shader->ShaderSource(source);
-}
-
-void
-WebGLContext::GetShaderTranslatedSource(WebGLShader* shader, nsAString& retval)
-{
-    retval.SetIsVoid(true);
-
-    if (IsContextLost())
-        return;
-
-    if (!ValidateObject("getShaderTranslatedSource: shader", shader))
-        return;
-
-    shader->GetShaderTranslatedSource(&retval);
+    shader.ShaderSource(source);
 }
 
 void
 WebGLContext::LoseContext()
 {
     if (IsContextLost())
         return ErrorInvalidOperation("loseContext: Context is already lost.");
 
--- a/dom/canvas/WebGLContextUnchecked.cpp
+++ b/dom/canvas/WebGLContextUnchecked.cpp
@@ -11,84 +11,9 @@
 
 namespace mozilla {
 
 WebGLContextUnchecked::WebGLContextUnchecked(gl::GLContext* _gl)
     : mGL_OnlyClearInDestroyResourcesAndContext(_gl)
     , gl(mGL_OnlyClearInDestroyResourcesAndContext) // const reference
 { }
 
-// -----------------------------------------------------------------------------
-// Sampler Objects
-
-void
-WebGLContextUnchecked::BindSampler(GLuint unit, WebGLSampler* sampler)
-{
-    gl->MakeCurrent();
-    gl->fBindSampler(unit, sampler ? sampler->mGLName : 0);
-}
-
-GLint
-WebGLContextUnchecked::GetSamplerParameteriv(WebGLSampler* sampler,
-                                             GLenum pname)
-{
-    MOZ_ASSERT(sampler, "Did you validate?");
-
-    GLint param = 0;
-    gl->MakeCurrent();
-    gl->fGetSamplerParameteriv(sampler->mGLName, pname, &param);
-
-    return param;
-}
-
-GLfloat
-WebGLContextUnchecked::GetSamplerParameterfv(WebGLSampler* sampler,
-                                             GLenum pname)
-{
-    MOZ_ASSERT(sampler, "Did you validate?");
-
-    GLfloat param = 0.0f;
-    gl->MakeCurrent();
-    gl->fGetSamplerParameterfv(sampler->mGLName, pname, &param);
-    return param;
-}
-
-void
-WebGLContextUnchecked::SamplerParameteri(WebGLSampler* sampler,
-                                         GLenum pname,
-                                         GLint param)
-{
-    MOZ_ASSERT(sampler, "Did you validate?");
-    gl->MakeCurrent();
-    gl->fSamplerParameteri(sampler->mGLName, pname, param);
-}
-
-void
-WebGLContextUnchecked::SamplerParameteriv(WebGLSampler* sampler,
-                                          GLenum pname,
-                                          const GLint* param)
-{
-    MOZ_ASSERT(sampler, "Did you validate?");
-    gl->MakeCurrent();
-    gl->fSamplerParameteriv(sampler->mGLName, pname, param);
-}
-
-void
-WebGLContextUnchecked::SamplerParameterf(WebGLSampler* sampler,
-                                         GLenum pname,
-                                         GLfloat param)
-{
-    MOZ_ASSERT(sampler, "Did you validate?");
-    gl->MakeCurrent();
-    gl->fSamplerParameterf(sampler->mGLName, pname, param);
-}
-
-void
-WebGLContextUnchecked::SamplerParameterfv(WebGLSampler* sampler,
-                                          GLenum pname,
-                                          const GLfloat* param)
-{
-    MOZ_ASSERT(sampler, "Did you validate?");
-    gl->MakeCurrent();
-    gl->fSamplerParameterfv(sampler->mGLName, pname, param);
-}
-
 } // namespace mozilla
--- a/dom/canvas/WebGLContextUnchecked.h
+++ b/dom/canvas/WebGLContextUnchecked.h
@@ -15,28 +15,16 @@ namespace mozilla {
 class WebGLBuffer;
 class WebGLSampler;
 
 class WebGLContextUnchecked
 {
 public:
     explicit WebGLContextUnchecked(gl::GLContext* gl);
 
-    // -------------------------------------------------------------------------
-    // Sampler Objects
-    void BindSampler(GLuint unit, WebGLSampler* sampler);
-
-    GLint   GetSamplerParameteriv(WebGLSampler* sampler, GLenum pname);
-    GLfloat GetSamplerParameterfv(WebGLSampler* sampler, GLenum pname);
-
-    void SamplerParameteri(WebGLSampler* sampler, GLenum pname, GLint param);
-    void SamplerParameteriv(WebGLSampler* sampler, GLenum pname, const GLint* param);
-    void SamplerParameterf(WebGLSampler* sampler, GLenum pname, GLfloat param);
-    void SamplerParameterfv(WebGLSampler* sampler, GLenum pname, const GLfloat* param);
-
 protected:
     // We've had issues in the past with nulling `gl` without actually releasing
     // all of our resources. This construction ensures that we are aware that we
     // should only null `gl` in DestroyResourcesAndContext.
     RefPtr<gl::GLContext> mGL_OnlyClearInDestroyResourcesAndContext;
 public:
     // Grab a const reference so we can see changes, but can't make changes.
     const decltype(mGL_OnlyClearInDestroyResourcesAndContext)& gl;
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -235,127 +235,16 @@ WebGLContext::ValidateFramebufferAttachm
         ErrorInvalidOperation("%s: Bad color attachment: COLOR_ATTACHMENT%u. (0x%04x)",
                               funcName, offset, attachment);
     } else {
         ErrorInvalidEnum("%s: attachment: Bad attachment 0x%x.", funcName, attachment);
     }
     return false;
 }
 
-/**
- * Return true if pname is valid for GetSamplerParameter calls.
- */
-bool
-WebGLContext::ValidateSamplerParameterName(GLenum pname, const char* info)
-{
-    switch (pname) {
-    case LOCAL_GL_TEXTURE_MIN_FILTER:
-    case LOCAL_GL_TEXTURE_MAG_FILTER:
-    case LOCAL_GL_TEXTURE_WRAP_S:
-    case LOCAL_GL_TEXTURE_WRAP_T:
-    case LOCAL_GL_TEXTURE_WRAP_R:
-    case LOCAL_GL_TEXTURE_MIN_LOD:
-    case LOCAL_GL_TEXTURE_MAX_LOD:
-    case LOCAL_GL_TEXTURE_COMPARE_MODE:
-    case LOCAL_GL_TEXTURE_COMPARE_FUNC:
-        return true;
-
-    default:
-        ErrorInvalidEnum("%s: invalid pname: %s", info, EnumName(pname));
-        return false;
-    }
-}
-
-/**
- * Return true if pname and param are valid combination for SamplerParameter calls.
- */
-bool
-WebGLContext::ValidateSamplerParameterParams(GLenum pname, const WebGLIntOrFloat& param, const char* info)
-{
-    const GLenum p = param.AsInt();
-
-    switch (pname) {
-    case LOCAL_GL_TEXTURE_MIN_FILTER:
-        switch (p) {
-        case LOCAL_GL_NEAREST:
-        case LOCAL_GL_LINEAR:
-        case LOCAL_GL_NEAREST_MIPMAP_NEAREST:
-        case LOCAL_GL_NEAREST_MIPMAP_LINEAR:
-        case LOCAL_GL_LINEAR_MIPMAP_NEAREST:
-        case LOCAL_GL_LINEAR_MIPMAP_LINEAR:
-            return true;
-
-        default:
-            ErrorInvalidEnum("%s: invalid param: %s", info, EnumName(p));
-            return false;
-        }
-
-    case LOCAL_GL_TEXTURE_MAG_FILTER:
-        switch (p) {
-        case LOCAL_GL_NEAREST:
-        case LOCAL_GL_LINEAR:
-            return true;
-
-        default:
-            ErrorInvalidEnum("%s: invalid param: %s", info, EnumName(p));
-            return false;
-        }
-
-    case LOCAL_GL_TEXTURE_WRAP_S:
-    case LOCAL_GL_TEXTURE_WRAP_T:
-    case LOCAL_GL_TEXTURE_WRAP_R:
-        switch (p) {
-        case LOCAL_GL_CLAMP_TO_EDGE:
-        case LOCAL_GL_REPEAT:
-        case LOCAL_GL_MIRRORED_REPEAT:
-            return true;
-
-        default:
-            ErrorInvalidEnum("%s: invalid param: %s", info, EnumName(p));
-            return false;
-        }
-
-    case LOCAL_GL_TEXTURE_MIN_LOD:
-    case LOCAL_GL_TEXTURE_MAX_LOD:
-        return true;
-
-    case LOCAL_GL_TEXTURE_COMPARE_MODE:
-        switch (param.AsInt()) {
-        case LOCAL_GL_NONE:
-        case LOCAL_GL_COMPARE_REF_TO_TEXTURE:
-            return true;
-
-        default:
-            ErrorInvalidEnum("%s: invalid param: %s", info, EnumName(p));
-            return false;
-        }
-
-    case LOCAL_GL_TEXTURE_COMPARE_FUNC:
-        switch (p) {
-        case LOCAL_GL_LEQUAL:
-        case LOCAL_GL_GEQUAL:
-        case LOCAL_GL_LESS:
-        case LOCAL_GL_GREATER:
-        case LOCAL_GL_EQUAL:
-        case LOCAL_GL_NOTEQUAL:
-        case LOCAL_GL_ALWAYS:
-        case LOCAL_GL_NEVER:
-            return true;
-
-        default:
-            ErrorInvalidEnum("%s: invalid param: %s", info, EnumName(p));
-            return false;
-        }
-
-    default:
-        ErrorInvalidEnum("%s: invalid pname: %s", info, EnumName(pname));
-        return false;
-    }
-}
-
 bool
 WebGLContext::ValidateUniformLocation(WebGLUniformLocation* loc, const char* funcName)
 {
     /* GLES 2.0.25, p38:
      *   If the value of location is -1, the Uniform* commands will silently
      *   ignore the data passed in, and the current uniform values will not be
      *   changed.
      */
--- a/dom/canvas/WebGLExtensionDebugShaders.cpp
+++ b/dom/canvas/WebGLExtensionDebugShaders.cpp
@@ -2,42 +2,48 @@
 /* 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 "WebGLExtensions.h"
 
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "WebGLContext.h"
+#include "WebGLShader.h"
 
 namespace mozilla {
 
 WebGLExtensionDebugShaders::WebGLExtensionDebugShaders(WebGLContext* webgl)
     : WebGLExtensionBase(webgl)
 {
 }
 
 WebGLExtensionDebugShaders::~WebGLExtensionDebugShaders()
 {
 }
 
 // If no source has been defined, compileShader() has not been called, or the
 // translation has failed for shader, an empty string is returned; otherwise,
 // return the translated source.
 void
-WebGLExtensionDebugShaders::GetTranslatedShaderSource(WebGLShader* shader,
-                                                      nsAString& retval)
+WebGLExtensionDebugShaders::GetTranslatedShaderSource(const WebGLShader& shader,
+                                                      nsAString& retval) const
 {
     retval.SetIsVoid(true);
 
     if (mIsLost) {
         mContext->ErrorInvalidOperation("%s: Extension is lost.",
                                         "getTranslatedShaderSource");
         return;
     }
 
-    retval.SetIsVoid(false);
-    mContext->GetShaderTranslatedSource(shader, retval);
+    if (mContext->IsContextLost())
+        return;
+
+    if (!mContext->ValidateObjectRef("getShaderTranslatedSource: shader", shader))
+        return;
+
+    shader.GetShaderTranslatedSource(&retval);
 }
 
 IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionDebugShaders, WEBGL_debug_shaders)
 
 } // namespace mozilla
--- a/dom/canvas/WebGLExtensionDisjointTimerQuery.cpp
+++ b/dom/canvas/WebGLExtensionDisjointTimerQuery.cpp
@@ -52,17 +52,17 @@ WebGLExtensionDisjointTimerQuery::IsQuer
     const char funcName[] = "isQueryEXT";
     if (mIsLost)
         return false;
 
     return mContext->IsQuery(query, funcName);
 }
 
 void
-WebGLExtensionDisjointTimerQuery::BeginQueryEXT(GLenum target, WebGLQuery* query) const
+WebGLExtensionDisjointTimerQuery::BeginQueryEXT(GLenum target, WebGLQuery& query) const
 {
     const char funcName[] = "beginQueryEXT";
     if (mIsLost)
         return;
 
     mContext->BeginQuery(target, query, funcName);
 }
 
@@ -72,43 +72,43 @@ WebGLExtensionDisjointTimerQuery::EndQue
     const char funcName[] = "endQueryEXT";
     if (mIsLost)
         return;
 
     mContext->EndQuery(target, funcName);
 }
 
 void
-WebGLExtensionDisjointTimerQuery::QueryCounterEXT(WebGLQuery* query, GLenum target) const
+WebGLExtensionDisjointTimerQuery::QueryCounterEXT(WebGLQuery& query, GLenum target) const
 {
     const char funcName[] = "queryCounterEXT";
     if (mIsLost)
         return;
 
-    if (!mContext->ValidateObject(funcName, query))
+    if (!mContext->ValidateObjectRef(funcName, query))
         return;
 
-    query->QueryCounter(funcName, target);
+    query.QueryCounter(funcName, target);
 }
 
 void
 WebGLExtensionDisjointTimerQuery::GetQueryEXT(JSContext* cx, GLenum target, GLenum pname,
                                               JS::MutableHandleValue retval) const
 {
     const char funcName[] = "getQueryEXT";
     retval.setNull();
     if (mIsLost)
         return;
 
     mContext->GetQuery(cx, target, pname, retval, funcName);
 }
 
 void
 WebGLExtensionDisjointTimerQuery::GetQueryObjectEXT(JSContext* cx,
-                                                    const WebGLQuery* query, GLenum pname,
+                                                    const WebGLQuery& query, GLenum pname,
                                                     JS::MutableHandleValue retval) const
 {
     const char funcName[] = "getQueryObjectEXT";
     retval.setNull();
     if (mIsLost)
         return;
 
     mContext->GetQueryParameter(cx, query, pname, retval, funcName);
--- a/dom/canvas/WebGLExtensions.h
+++ b/dom/canvas/WebGLExtensions.h
@@ -122,17 +122,17 @@ public:
 
 class WebGLExtensionDebugShaders
     : public WebGLExtensionBase
 {
 public:
     explicit WebGLExtensionDebugShaders(WebGLContext*);
     virtual ~WebGLExtensionDebugShaders();
 
-    void GetTranslatedShaderSource(WebGLShader* shader, nsAString& retval);
+    void GetTranslatedShaderSource(const WebGLShader& shader, nsAString& retval) const;
 
     DECL_WEBGL_EXTENSION_GOOP
 };
 
 class WebGLExtensionDepthTexture
     : public WebGLExtensionBase
 {
 public:
@@ -367,22 +367,22 @@ class WebGLExtensionDisjointTimerQuery
 {
 public:
     explicit WebGLExtensionDisjointTimerQuery(WebGLContext* webgl);
     virtual ~WebGLExtensionDisjointTimerQuery();
 
     already_AddRefed<WebGLQuery> CreateQueryEXT() const;
     void DeleteQueryEXT(WebGLQuery* query) const;
     bool IsQueryEXT(const WebGLQuery* query) const;
-    void BeginQueryEXT(GLenum target, WebGLQuery* query) const;
+    void BeginQueryEXT(GLenum target, WebGLQuery& query) const;
     void EndQueryEXT(GLenum target) const;
-    void QueryCounterEXT(WebGLQuery* query, GLenum target) const;
+    void QueryCounterEXT(WebGLQuery& query, GLenum target) const;
     void GetQueryEXT(JSContext* cx, GLenum target, GLenum pname,
                      JS::MutableHandleValue retval) const;
-    void GetQueryObjectEXT(JSContext* cx, const WebGLQuery* query,
+    void GetQueryObjectEXT(JSContext* cx, const WebGLQuery& query,
                            GLenum pname, JS::MutableHandleValue retval) const;
 
     static bool IsSupported(const WebGLContext*);
 
     DECL_WEBGL_EXTENSION_GOOP
 };
 
 } // namespace mozilla
--- a/dom/canvas/WebGLProgram.cpp
+++ b/dom/canvas/WebGLProgram.cpp
@@ -532,17 +532,17 @@ WebGLProgram::BindAttribLocation(GLuint 
     const bool wasInserted = res.second;
     if (!wasInserted) {
         auto itr = res.first;
         itr->second = loc;
     }
 }
 
 void
-WebGLProgram::DetachShader(WebGLShader* shader)
+WebGLProgram::DetachShader(const WebGLShader* shader)
 {
     MOZ_ASSERT(shader);
 
     WebGLRefPtr<WebGLShader>* shaderSlot;
     switch (shader->mType) {
     case LOCAL_GL_VERTEX_SHADER:
         shaderSlot = &mVertShader;
         break;
--- a/dom/canvas/WebGLProgram.h
+++ b/dom/canvas/WebGLProgram.h
@@ -140,17 +140,17 @@ public:
 
     explicit WebGLProgram(WebGLContext* webgl);
 
     void Delete();
 
     // GL funcs
     void AttachShader(WebGLShader* shader);
     void BindAttribLocation(GLuint index, const nsAString& name);
-    void DetachShader(WebGLShader* shader);
+    void DetachShader(const WebGLShader* shader);
     already_AddRefed<WebGLActiveInfo> GetActiveAttrib(GLuint index) const;
     already_AddRefed<WebGLActiveInfo> GetActiveUniform(GLuint index) const;
     void GetAttachedShaders(nsTArray<RefPtr<WebGLShader>>* const out) const;
     GLint GetAttribLocation(const nsAString& name) const;
     GLint GetFragDataLocation(const nsAString& name) const;
     void GetProgramInfoLog(nsAString* const out) const;
     JS::Value GetProgramParameter(GLenum pname) const;
     GLuint GetUniformBlockIndex(const nsAString& name) const;
--- a/dom/canvas/WebGLSampler.cpp
+++ b/dom/canvas/WebGLSampler.cpp
@@ -48,116 +48,161 @@ WebGLSampler::GetParentObject() const
 }
 
 JSObject*
 WebGLSampler::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
 {
     return dom::WebGLSamplerBinding::Wrap(cx, this, givenProto);
 }
 
-void
-WebGLSampler::SamplerParameter1i(GLenum pname, GLint param)
+static bool
+ValidateSamplerParameterParams(WebGLContext* webgl, const char* funcName, GLenum pname,
+                               GLint paramInt)
 {
     switch (pname) {
     case LOCAL_GL_TEXTURE_MIN_FILTER:
-        mMinFilter = param;
+        switch (paramInt) {
+        case LOCAL_GL_NEAREST:
+        case LOCAL_GL_LINEAR:
+        case LOCAL_GL_NEAREST_MIPMAP_NEAREST:
+        case LOCAL_GL_NEAREST_MIPMAP_LINEAR:
+        case LOCAL_GL_LINEAR_MIPMAP_NEAREST:
+        case LOCAL_GL_LINEAR_MIPMAP_LINEAR:
+            return true;
+
+        default:
+            break;
+        }
         break;
 
     case LOCAL_GL_TEXTURE_MAG_FILTER:
-        mMagFilter = param;
+        switch (paramInt) {
+        case LOCAL_GL_NEAREST:
+        case LOCAL_GL_LINEAR:
+            return true;
+
+        default:
+            break;
+        }
         break;
 
     case LOCAL_GL_TEXTURE_WRAP_S:
-        mWrapS = param;
+    case LOCAL_GL_TEXTURE_WRAP_T:
+    case LOCAL_GL_TEXTURE_WRAP_R:
+        switch (paramInt) {
+        case LOCAL_GL_CLAMP_TO_EDGE:
+        case LOCAL_GL_REPEAT:
+        case LOCAL_GL_MIRRORED_REPEAT:
+            return true;
+
+        default:
+            break;
+        }
+        break;
+
+    case LOCAL_GL_TEXTURE_MIN_LOD:
+    case LOCAL_GL_TEXTURE_MAX_LOD:
+        return true;
+
+    case LOCAL_GL_TEXTURE_COMPARE_MODE:
+        switch (paramInt) {
+        case LOCAL_GL_NONE:
+        case LOCAL_GL_COMPARE_REF_TO_TEXTURE:
+            return true;
+
+        default:
+            break;
+        }
+        break;
+
+    case LOCAL_GL_TEXTURE_COMPARE_FUNC:
+        switch (paramInt) {
+        case LOCAL_GL_LEQUAL:
+        case LOCAL_GL_GEQUAL:
+        case LOCAL_GL_LESS:
+        case LOCAL_GL_GREATER:
+        case LOCAL_GL_EQUAL:
+        case LOCAL_GL_NOTEQUAL:
+        case LOCAL_GL_ALWAYS:
+        case LOCAL_GL_NEVER:
+            return true;
+
+        default:
+            break;
+        }
+        break;
+
+    default:
+        webgl->ErrorInvalidEnum("%s: invalid pname: %s", funcName,
+                                webgl->EnumName(pname));
+        return false;
+    }
+
+    webgl->ErrorInvalidEnum("%s: invalid param: %s", funcName, webgl->EnumName(paramInt));
+    return false;
+}
+
+void
+WebGLSampler::SamplerParameter(const char* funcName, GLenum pname, GLint paramInt)
+{
+    if (!ValidateSamplerParameterParams(mContext, funcName, pname, paramInt))
+        return;
+
+    switch (pname) {
+    case LOCAL_GL_TEXTURE_MIN_FILTER:
+        mMinFilter = paramInt;
+        break;
+
+    case LOCAL_GL_TEXTURE_MAG_FILTER:
+        mMagFilter = paramInt;
+        break;
+
+    case LOCAL_GL_TEXTURE_WRAP_S:
+        mWrapS = paramInt;
         break;
 
     case LOCAL_GL_TEXTURE_WRAP_T:
-        mWrapT = param;
+        mWrapT = paramInt;
         break;
 
     case LOCAL_GL_TEXTURE_WRAP_R:
-        mWrapR = param;
+        mWrapR = paramInt;
         break;
 
     case LOCAL_GL_TEXTURE_COMPARE_MODE:
-        mCompareMode = param;
+        mCompareMode = paramInt;
         break;
 
     case LOCAL_GL_TEXTURE_COMPARE_FUNC:
-        mCompareFunc = param;
+        mCompareFunc = paramInt;
         break;
 
     case LOCAL_GL_TEXTURE_MIN_LOD:
-        mMinLod = param;
+        mMinLod = paramInt;
         break;
 
     case LOCAL_GL_TEXTURE_MAX_LOD:
-        mMaxLod = param;
+        mMaxLod = paramInt;
         break;
 
     default:
         MOZ_CRASH("GFX: Unhandled pname");
         break;
     }
 
     for (uint32_t i = 0; i < mContext->mBoundSamplers.Length(); ++i) {
         if (this == mContext->mBoundSamplers[i])
             mContext->InvalidateResolveCacheForTextureWithTexUnit(i);
     }
+
+    ////
+
+    mContext->gl->MakeCurrent();
+    mContext->gl->fSamplerParameteri(mGLName, pname, paramInt);
 }
 
-void
-WebGLSampler::SamplerParameter1f(GLenum pname, GLfloat param)
-{
-    switch (pname) {
-    case LOCAL_GL_TEXTURE_MIN_LOD:
-        mMinLod = param;
-        break;
-
-    case LOCAL_GL_TEXTURE_MAX_LOD:
-        mMaxLod = param;
-        break;
-
-    case LOCAL_GL_TEXTURE_WRAP_S:
-        mWrapS = param;
-        break;
-
-    case LOCAL_GL_TEXTURE_WRAP_T:
-        mWrapT = param;
-        break;
-
-    case LOCAL_GL_TEXTURE_WRAP_R:
-        mWrapR = param;
-        break;
-
-    case LOCAL_GL_TEXTURE_MAG_FILTER:
-        mMagFilter = param;
-        break;
-
-    case LOCAL_GL_TEXTURE_MIN_FILTER:
-        mMinFilter = param;
-        break;
-
-    case LOCAL_GL_TEXTURE_COMPARE_MODE:
-        mCompareMode = param;
-        break;
-
-    case LOCAL_GL_TEXTURE_COMPARE_FUNC:
-        mCompareFunc = param;
-        break;
-
-    default:
-        MOZ_CRASH("GFX: Unhandled pname");
-        break;
-    }
-
-    for (uint32_t i = 0; i < mContext->mBoundSamplers.Length(); ++i) {
-        if (this == mContext->mBoundSamplers[i])
-            mContext->InvalidateResolveCacheForTextureWithTexUnit(i);
-    }
-}
-
+////
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLSampler)
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLSampler, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLSampler, Release)
 
 } // namespace mozilla
--- a/dom/canvas/WebGLSampler.h
+++ b/dom/canvas/WebGLSampler.h
@@ -27,21 +27,19 @@ public:
 
     const GLuint mGLName;
 
     void Delete();
     WebGLContext* GetParentObject() const;
 
     virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
 
-    void SamplerParameter1i(GLenum pname, GLint param);
-    void SamplerParameter1f(GLenum pname, GLfloat param);
+    void SamplerParameter(const char* funcName, GLenum pname, GLint paramInt);
 
 private:
-
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLSampler)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLSampler)
 
     TexMinFilter mMinFilter;
     TexMagFilter mMagFilter;
     TexWrap mWrapS;
     TexWrap mWrapT;
     TexWrap mWrapR;
--- a/dom/canvas/WebGLUniformLocation.cpp
+++ b/dom/canvas/WebGLUniformLocation.cpp
@@ -24,17 +24,18 @@ WebGLUniformLocation::WebGLUniformLocati
     , mLoc(loc)
     , mArrayIndex(arrayIndex)
 { }
 
 WebGLUniformLocation::~WebGLUniformLocation()
 { }
 
 bool
-WebGLUniformLocation::ValidateForProgram(WebGLProgram* prog, const char* funcName) const
+WebGLUniformLocation::ValidateForProgram(const WebGLProgram* prog,
+                                         const char* funcName) const
 {
     // Check the weak-pointer.
     if (!mLinkInfo) {
         mContext->ErrorInvalidOperation("%s: This uniform location is obsolete because"
                                         " its program has been successfully relinked.",
                                         funcName);
         return false;
     }
--- a/dom/canvas/WebGLUniformLocation.h
+++ b/dom/canvas/WebGLUniformLocation.h
@@ -47,17 +47,17 @@ public:
     const GLuint mLoc;
     const size_t mArrayIndex;
 
     //////
 
     WebGLUniformLocation(WebGLContext* webgl, const webgl::LinkedProgramInfo* linkInfo,
                          webgl::UniformInfo* info, GLuint loc, size_t arrayIndex);
 
-    bool ValidateForProgram(WebGLProgram* prog, const char* funcName) const;
+    bool ValidateForProgram(const WebGLProgram* prog, const char* funcName) const;
     bool ValidateSizeAndType(uint8_t setterElemSize, GLenum setterType,
                              const char* funcName) const;
     bool ValidateArrayLength(uint8_t setterElemSize, size_t setterArraySize,
                              const char* funcName) const;
 
     JS::Value GetUniform(JSContext* js) const;
 
     // Needed for certain helper functions like ValidateObject.
--- a/dom/canvas/test/webgl-conf/checkout/conformance/buffers/buffer-data-and-buffer-sub-data.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/buffers/buffer-data-and-buffer-sub-data.html
@@ -46,17 +46,17 @@ debug('Regression test for <a href="http
 var wtu = WebGLTestUtils;
 var gl = wtu.create3DContext();
 if (!gl) {
     testFailed("WebGL context does not exist");
 } else {
     testPassed("WebGL context exists");
 
     bufferDataTest();
-    bufferDataSizesTest();    
+    bufferDataSizesTest();
 
     bufferSubDataTest();
 }
 
 function bufferDataTest() {
     debug("");
     debug("Test bufferData without ArrayBuffer input");
 
@@ -158,32 +158,29 @@ function bufferSubDataTest() {
     gl.bufferSubData(gl.ARRAY_BUFFER, -10, new Float32Array(8));
     wtu.glErrorShouldBe(gl, gl.INVALID_VALUE,
         "calling bufferSubData with ArrayBufferView when offset is negative should generate INVALID_VALUE");
 
     gl.bufferSubData(gl.ARRAY_BUFFER, 10, array);
     wtu.glErrorShouldBe(gl, gl.NO_ERROR,
         "calling bufferSubData with ArrayBuffer should succeed");
 
-    gl.bufferSubData(gl.ARRAY_BUFFER, 10, null);
-    wtu.glErrorShouldBe(gl, gl.INVALID_VALUE,
-        "calling bufferSubData when BufferDataSource is null should generate INVALID_VALUE");
-
-    wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, "gl.bufferSubData(gl.ARRAY_BUFFER, 10, undefined)");
-
     gl.bufferSubData(gl.ARRAY_BUFFER, 10, new Float32Array(0));
     wtu.glErrorShouldBe(gl, gl.NO_ERROR,
         "calling bufferSubData with zero-sized ArrayBufferView should succeed");
 
     // Arguments that are not ArrayBuffers, null or undefined should throw a TypeError exception
     shouldThrow("gl.bufferSubData(gl.ARRAY_BUFFER, 0, 42);");
     shouldThrow("gl.bufferSubData(gl.ARRAY_BUFFER, 0, 5.5);");
     shouldThrow("gl.bufferSubData(gl.ARRAY_BUFFER, 0, \"5.5\");");
     shouldThrow("gl.bufferSubData(gl.ARRAY_BUFFER, 0, [4]);");
     shouldThrow("gl.bufferSubData(gl.ARRAY_BUFFER, 0, { mynumber: 42});");
+    shouldThrow("gl.bufferSubData(gl.ARRAY_BUFFER, 10, null)");
+    shouldThrow("gl.bufferSubData(gl.ARRAY_BUFFER, 10, undefined)");
+    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should generate no GL error");
 
     gl.bindBuffer(gl.ARRAY_BUFFER, null);
 }
 
 var successfullyParsed = true;
 </script>
 
 <script src="../../js/js-test-post.js"></script>
--- a/dom/canvas/test/webgl-conf/checkout/conformance/context/context-lost.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/context/context-lost.html
@@ -80,16 +80,19 @@ function init()
     }
     extension = gl.getExtension(extensionName);
 
     // need an extension that exposes new API methods.
     OES_vertex_array_object = wtu.getExtensionWithKnownPrefixes(gl, "OES_vertex_array_object");
 
     canvas.addEventListener("webglcontextlost", testLostContext, false);
 
+    // We need to initialize |uniformLocation| before losing context.
+    // Otherwise gl.getUniform() when context is lost will throw.
+    uniformLocation = gl.getUniformLocation(program, "tex");
     loseContext();
 }
 
 function loseContext()
 {
     debug("");
     debug("Lose context");
 
@@ -238,24 +241,24 @@ function testLostContext()
         "gl.stencilFuncSeparate(gl.FRONT, gl.NEVER, 0, 0)",
         "gl.stencilMask(0)",
         "gl.stencilMaskSeparate(gl.FRONT, 0)",
         "gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP)",
         "gl.stencilOpSeparate(gl.FRONT, gl.KEEP, gl.KEEP, gl.KEEP)",
         "gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, arrayBufferView)",
         "gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, imageData)",
         "gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image)",
-        "gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas)",
+        "gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas2d)",
         "gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, video)",
         "gl.texParameterf(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)",
         "gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)",
         "gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, arrayBufferView)",
         "gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, imageData)",
         "gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, image)",
-        "gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, canvas)",
+        "gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, canvas2d)",
         "gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, video)",
         "gl.uniform1f(uniformLocation, 0)",
         "gl.uniform1fv(uniformLocation, float32array)",
         "gl.uniform1fv(uniformLocation, [0])",
         "gl.uniform1i(uniformLocation, 0)",
         "gl.uniform1iv(uniformLocation, int32array)",
         "gl.uniform1iv(uniformLocation, [0])",
         "gl.uniform2f(uniformLocation, 0, 0)",
--- a/dom/canvas/test/webgl-conf/checkout/conformance/misc/error-reporting.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/misc/error-reporting.html
@@ -46,31 +46,29 @@ var program = wtu.loadStandardProgram(co
 // of many synthetic GL errors. This test verifies the raising of certain
 // known real GL errors, and contains a few regression tests for bugs
 // discovered in the synthetic error generation and in the WebGL
 // implementation itself.
 
 wtu.glErrorShouldBe(context, context.NO_ERROR);
 
 debug("Testing getActiveAttrib");
-// Synthetic OpenGL error
-shouldBeNull("context.getActiveAttrib(null, 2)");
-wtu.glErrorShouldBe(context, context.INVALID_VALUE);
+shouldThrow("context.getActiveAttrib(null, 2)");
+wtu.glErrorShouldBe(context, context.NO_ERROR);
 // Error state should be clear by this point
 wtu.glErrorShouldBe(context, context.NO_ERROR);
 // Real OpenGL error
 shouldBeNull("context.getActiveAttrib(program, 2)");
 wtu.glErrorShouldBe(context, context.INVALID_VALUE);
 // Error state should be clear by this point
 wtu.glErrorShouldBe(context, context.NO_ERROR);
 
 debug("Testing getActiveUniform");
-// Synthetic OpenGL error
-shouldBeNull("context.getActiveUniform(null, 0)");
-wtu.glErrorShouldBe(context, context.INVALID_VALUE);
+shouldThrow("context.getActiveUniform(null, 0)");
+wtu.glErrorShouldBe(context, context.NO_ERROR);
 // Error state should be clear by this point
 wtu.glErrorShouldBe(context, context.NO_ERROR);
 // Real OpenGL error
 shouldBeNull("context.getActiveUniform(program, 50)");
 wtu.glErrorShouldBe(context, context.INVALID_VALUE);
 // Error state should be clear by this point
 wtu.glErrorShouldBe(context, context.NO_ERROR);
 
--- a/dom/canvas/test/webgl-conf/checkout/conformance/misc/null-object-behaviour.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/misc/null-object-behaviour.html
@@ -44,47 +44,47 @@ description("Tests calling WebGL APIs wi
 
 var context = wtu.create3DContext();
 var program = wtu.loadStandardProgram(context);
 var shader = wtu.loadStandardVertexShader(context);
 var shouldGenerateGLError = wtu.shouldGenerateGLError;
 
 assertMsg(program != null, "Program Compiled");
 assertMsg(shader != null, "Shader Compiled");
-shouldGenerateGLError(context, context.INVALID_VALUE, "context.compileShader(undefined)");
-shouldGenerateGLError(context, context.INVALID_VALUE, "context.linkProgram(undefined)");
-shouldGenerateGLError(context, context.INVALID_VALUE, "context.attachShader(undefined, undefined)");
-shouldGenerateGLError(context, context.INVALID_VALUE, "context.attachShader(program, undefined)");
-shouldGenerateGLError(context, context.INVALID_VALUE, "context.attachShader(undefined, shader)");
-shouldGenerateGLError(context, context.INVALID_VALUE, "context.detachShader(program, undefined)");
-shouldGenerateGLError(context, context.INVALID_VALUE, "context.detachShader(undefined, shader)");
-shouldGenerateGLError(context, context.INVALID_VALUE, "context.shaderSource(undefined, undefined)");
-shouldGenerateGLError(context, context.INVALID_VALUE, "context.shaderSource(undefined, 'foo')");
-shouldGenerateGLError(context, context.INVALID_VALUE, "context.bindAttribLocation(undefined, 0, 'foo')");
+shouldThrow("context.compileShader(undefined)");
+shouldThrow("context.linkProgram(undefined)");
+shouldThrow("context.attachShader(undefined, undefined)");
+shouldThrow("context.attachShader(program, undefined)");
+shouldThrow("context.attachShader(undefined, shader)");
+shouldThrow("context.detachShader(program, undefined)");
+shouldThrow("context.detachShader(undefined, shader)");
+shouldThrow("context.shaderSource(undefined, undefined)");
+shouldThrow("context.shaderSource(undefined, 'foo')");
+shouldThrow("context.bindAttribLocation(undefined, 0, 'foo')");
 shouldThrow("context.bindBuffer(context.ARRAY_BUFFER, 0)");
 shouldThrow("context.bindFramebuffer(context.FRAMEBUFFER, 0)");
 shouldThrow("context.bindRenderbuffer(context.RENDERBUFFER, 0)");
 shouldThrow("context.bindTexture(context.TEXTURE_2D, 0)");
 shouldGenerateGLError(context, context.NO_ERROR, "context.bindBuffer(context.ARRAY_BUFFER, null)");
 shouldGenerateGLError(context, context.NO_ERROR, "context.bindFramebuffer(context.FRAMEBUFFER, null)");
 shouldGenerateGLError(context, context.NO_ERROR, "context.bindRenderbuffer(context.RENDERBUFFER, null)");
 shouldGenerateGLError(context, context.NO_ERROR, "context.bindTexture(context.TEXTURE_2D, null)");
 shouldGenerateGLError(context, context.NO_ERROR, "context.bindBuffer(context.ARRAY_BUFFER, undefined)");
 shouldGenerateGLError(context, context.NO_ERROR, "context.bindFramebuffer(context.FRAMEBUFFER, undefined)");
 shouldGenerateGLError(context, context.NO_ERROR, "context.bindRenderbuffer(context.RENDERBUFFER, undefined)");
 shouldGenerateGLError(context, context.NO_ERROR, "context.bindTexture(context.TEXTURE_2D, undefined)");
 shouldGenerateGLError(context, context.INVALID_OPERATION, "context.framebufferRenderbuffer(context.FRAMEBUFFER, context.DEPTH_ATTACHMENT, context.RENDERBUFFER, null)");
 shouldGenerateGLError(context, context.INVALID_OPERATION, "context.framebufferTexture2D(context.FRAMEBUFFER, context.COLOR_ATTACHMENT0, context.TEXTURE_2D, null, 0)");
-shouldGenerateGLError(context, context.INVALID_VALUE, "context.getProgramParameter(undefined, 0)");
-shouldGenerateGLError(context, context.INVALID_VALUE, "context.getProgramInfoLog(undefined, 0)");
-shouldGenerateGLError(context, context.INVALID_VALUE, "context.getShaderParameter(undefined, 0)");
-shouldGenerateGLError(context, context.INVALID_VALUE, "context.getShaderInfoLog(undefined, 0)");
-shouldGenerateGLError(context, context.INVALID_VALUE, "context.getShaderSource(undefined)");
-shouldGenerateGLError(context, context.INVALID_VALUE, "context.getUniform(undefined, null)");
-shouldGenerateGLError(context, context.INVALID_VALUE, "context.getUniformLocation(undefined, 'foo')");
+shouldThrow("context.getProgramParameter(undefined, 0)");
+shouldThrow("context.getProgramInfoLog(undefined, 0)");
+shouldThrow("context.getShaderParameter(undefined, 0)");
+shouldThrow("context.getShaderInfoLog(undefined, 0)");
+shouldThrow("context.getShaderSource(undefined)");
+shouldThrow("context.getUniform(undefined, null)");
+shouldThrow("context.getUniformLocation(undefined, 'foo')");
 
 debug("");
 debug("check with bindings");
 context.bindBuffer(context.ARRAY_BUFFER, context.createBuffer());
 context.bindTexture(context.TEXTURE_2D, context.createTexture());
 shouldGenerateGLError(context, context.NO_ERROR, "context.bufferData(context.ARRAY_BUFFER, 1, context.STATIC_DRAW)");
 shouldGenerateGLError(context, context.NO_ERROR, "context.getBufferParameter(context.ARRAY_BUFFER, context.BUFFER_SIZE)");
 shouldGenerateGLError(context, context.NO_ERROR, "context.texImage2D(context.TEXTURE_2D, 0, context.RGBA, 1, 1, 0, context.RGBA, context.UNSIGNED_BYTE, new Uint8Array([0,0,0,0]))");
--- a/dom/canvas/test/webgl-conf/checkout/conformance/programs/get-active-test.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/programs/get-active-test.html
@@ -52,18 +52,18 @@ var program2 = wtu.loadProgramFromFile(c
 wtu.glErrorShouldBe(context, context.NO_ERROR);
 shouldBe("context.getActiveUniform(program, 0).name", "'u_modelViewProjMatrix'");
 shouldBe("context.getActiveUniform(program, 0).type", "context.FLOAT_MAT4");
 shouldBe("context.getActiveUniform(program, 0).size", "1");
 shouldBeNull("context.getActiveUniform(program, 1)");
 wtu.glErrorShouldBe(context, context.INVALID_VALUE);
 shouldBeNull("context.getActiveUniform(program, -1)");
 wtu.glErrorShouldBe(context, context.INVALID_VALUE);
-shouldBeNull("context.getActiveUniform(null, 0)");
-wtu.glErrorShouldBe(context, context.INVALID_VALUE);
+shouldThrow("context.getActiveUniform(null, 0)");
+wtu.glErrorShouldBe(context, context.NO_ERROR);
 
 // we don't know the order the attribs will appear.
 var info = [
   context.getActiveAttrib(program, 0),
   context.getActiveAttrib(program, 1)
 ];
 for (var ii = 0; ii < info.length; ++ii)
     shouldBeNonNull("info[ii]");
@@ -109,18 +109,18 @@ for (var ii = 0; ii < info2.length; ++ii
     shouldBe("info2[ii].type", "expected2[ii].type");
     shouldBe("info2[ii].size", "expected2[ii].size");
 }
 
 shouldBeNull("context.getActiveAttrib(program, 2)");
 wtu.glErrorShouldBe(context, context.INVALID_VALUE);
 shouldBeNull("context.getActiveAttrib(program, -1)");
 wtu.glErrorShouldBe(context, context.INVALID_VALUE);
-shouldBeNull("context.getActiveAttrib(null, 0)");
-wtu.glErrorShouldBe(context, context.INVALID_VALUE);
+shouldThrow("context.getActiveAttrib(null, 0)");
+wtu.glErrorShouldBe(context, context.NO_ERROR);
 
 wtu.glErrorShouldBe(context2, context.NO_ERROR);
 
 debug("Check trying to get attribs from different context");
 shouldBeNull("context2.getActiveAttrib(program, 0)");
 wtu.glErrorShouldBe(context2, context2.INVALID_OPERATION);
 shouldBeNull("context2.getActiveUniform(program, 0)");
 wtu.glErrorShouldBe(context2, context2.INVALID_OPERATION);
--- a/dom/canvas/test/webgl-conf/checkout/conformance2/context/constants-and-properties-2.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/context/constants-and-properties-2.html
@@ -41,131 +41,131 @@
 "use strict";
 description("This test ensures that the WebGL context has all the constants and (non-function) properties in the specification.");
 
 var constants = {
     /* ClearBufferMask */
 DEPTH_BUFFER_BIT               : 0x00000100,
 STENCIL_BUFFER_BIT             : 0x00000400,
 COLOR_BUFFER_BIT               : 0x00004000,
-    
+
     /* BeginMode */
 POINTS                         : 0x0000,
 LINES                          : 0x0001,
 LINE_LOOP                      : 0x0002,
 LINE_STRIP                     : 0x0003,
 TRIANGLES                      : 0x0004,
 TRIANGLE_STRIP                 : 0x0005,
 TRIANGLE_FAN                   : 0x0006,
-    
+
     /* AlphaFunction (not supported in ES20) */
     /*      NEVER */
     /*      LESS */
     /*      EQUAL */
     /*      LEQUAL */
     /*      GREATER */
     /*      NOTEQUAL */
     /*      GEQUAL */
     /*      ALWAYS */
-    
+
     /* BlendingFactorDest */
 ZERO                           : 0,
 ONE                            : 1,
 SRC_COLOR                      : 0x0300,
 ONE_MINUS_SRC_COLOR            : 0x0301,
 SRC_ALPHA                      : 0x0302,
 ONE_MINUS_SRC_ALPHA            : 0x0303,
 DST_ALPHA                      : 0x0304,
 ONE_MINUS_DST_ALPHA            : 0x0305,
-    
+
     /* BlendingFactorSrc */
     /*      ZERO */
     /*      ONE */
 DST_COLOR                      : 0x0306,
 ONE_MINUS_DST_COLOR            : 0x0307,
 SRC_ALPHA_SATURATE             : 0x0308,
     /*      SRC_ALPHA */
     /*      ONE_MINUS_SRC_ALPHA */
     /*      DST_ALPHA */
     /*      ONE_MINUS_DST_ALPHA */
-    
+
     /* BlendEquationSeparate */
 FUNC_ADD                       : 0x8006,
 BLEND_EQUATION                 : 0x8009,
 BLEND_EQUATION_RGB             : 0x8009,   /* same as BLEND_EQUATION */
 BLEND_EQUATION_ALPHA           : 0x883D,
-    
+
     /* BlendSubtract */
 FUNC_SUBTRACT                  : 0x800A,
 FUNC_REVERSE_SUBTRACT          : 0x800B,
-    
+
     /* Separate Blend Functions */
 BLEND_DST_RGB                  : 0x80C8,
 BLEND_SRC_RGB                  : 0x80C9,
 BLEND_DST_ALPHA                : 0x80CA,
 BLEND_SRC_ALPHA                : 0x80CB,
 CONSTANT_COLOR                 : 0x8001,
 ONE_MINUS_CONSTANT_COLOR       : 0x8002,
 CONSTANT_ALPHA                 : 0x8003,
 ONE_MINUS_CONSTANT_ALPHA       : 0x8004,
 BLEND_COLOR                    : 0x8005,
-    
+
     /* Buffer Objects */
 ARRAY_BUFFER                   : 0x8892,
 ELEMENT_ARRAY_BUFFER           : 0x8893,
 ARRAY_BUFFER_BINDING           : 0x8894,
 ELEMENT_ARRAY_BUFFER_BINDING   : 0x8895,
-    
+
 STREAM_DRAW                    : 0x88E0,
 STATIC_DRAW                    : 0x88E4,
 DYNAMIC_DRAW                   : 0x88E8,
-    
+
 BUFFER_SIZE                    : 0x8764,
 BUFFER_USAGE                   : 0x8765,
-    
+
 CURRENT_VERTEX_ATTRIB          : 0x8626,
-    
+
     /* CullFaceMode */
 FRONT                          : 0x0404,
 BACK                           : 0x0405,
 FRONT_AND_BACK                 : 0x0408,
-    
+
     /* DepthFunction */
     /*      NEVER */
     /*      LESS */
     /*      EQUAL */
     /*      LEQUAL */
     /*      GREATER */
     /*      NOTEQUAL */
     /*      GEQUAL */
     /*      ALWAYS */
-    
+
     /* EnableCap */
     /* TEXTURE_2D */
 CULL_FACE                      : 0x0B44,
 BLEND                          : 0x0BE2,
 DITHER                         : 0x0BD0,
 STENCIL_TEST                   : 0x0B90,
 DEPTH_TEST                     : 0x0B71,
 SCISSOR_TEST                   : 0x0C11,
 POLYGON_OFFSET_FILL            : 0x8037,
 SAMPLE_ALPHA_TO_COVERAGE       : 0x809E,
 SAMPLE_COVERAGE                : 0x80A0,
-    
+
     /* ErrorCode */
 NO_ERROR                       : 0,
 INVALID_ENUM                   : 0x0500,
 INVALID_VALUE                  : 0x0501,
 INVALID_OPERATION              : 0x0502,
 OUT_OF_MEMORY                  : 0x0505,
-    
+
     /* FrontFaceDirection */
 CW                             : 0x0900,
 CCW                            : 0x0901,
-    
+
     /* GetPName */
 LINE_WIDTH                     : 0x0B21,
 ALIASED_POINT_SIZE_RANGE       : 0x846D,
 ALIASED_LINE_WIDTH_RANGE       : 0x846E,
 CULL_FACE_MODE                 : 0x0B45,
 FRONT_FACE                     : 0x0B46,
 DEPTH_RANGE                    : 0x0B70,
 DEPTH_WRITEMASK                : 0x0B72,
@@ -205,56 +205,56 @@ STENCIL_BITS                   : 0x0D57,
 POLYGON_OFFSET_UNITS           : 0x2A00,
     /*      POLYGON_OFFSET_FILL */
 POLYGON_OFFSET_FACTOR          : 0x8038,
 TEXTURE_BINDING_2D             : 0x8069,
 SAMPLE_BUFFERS                 : 0x80A8,
 SAMPLES                        : 0x80A9,
 SAMPLE_COVERAGE_VALUE          : 0x80AA,
 SAMPLE_COVERAGE_INVERT         : 0x80AB,
-    
+
     /* GetTextureParameter */
     /*      TEXTURE_MAG_FILTER */
     /*      TEXTURE_MIN_FILTER */
     /*      TEXTURE_WRAP_S */
     /*      TEXTURE_WRAP_T */
-    
+
 COMPRESSED_TEXTURE_FORMATS     : 0x86A3,
-    
+
     /* HintMode */
 DONT_CARE                      : 0x1100,
 FASTEST                        : 0x1101,
 NICEST                         : 0x1102,
-    
+
     /* HintTarget */
 GENERATE_MIPMAP_HINT            : 0x8192,
-    
+
     /* DataType */
 BYTE                           : 0x1400,
 UNSIGNED_BYTE                  : 0x1401,
 SHORT                          : 0x1402,
 UNSIGNED_SHORT                 : 0x1403,
 INT                            : 0x1404,
 UNSIGNED_INT                   : 0x1405,
 FLOAT                          : 0x1406,
-    
+
     /* PixelFormat */
 DEPTH_COMPONENT                : 0x1902,
 ALPHA                          : 0x1906,
 RGB                            : 0x1907,
 RGBA                           : 0x1908,
 LUMINANCE                      : 0x1909,
 LUMINANCE_ALPHA                : 0x190A,
-    
+
     /* PixelType */
     /*      UNSIGNED_BYTE */
 UNSIGNED_SHORT_4_4_4_4         : 0x8033,
 UNSIGNED_SHORT_5_5_5_1         : 0x8034,
 UNSIGNED_SHORT_5_6_5           : 0x8363,
-    
+
     /* Shaders */
 FRAGMENT_SHADER                  : 0x8B30,
 VERTEX_SHADER                    : 0x8B31,
 MAX_VERTEX_ATTRIBS               : 0x8869,
 MAX_VERTEX_UNIFORM_VECTORS       : 0x8DFB,
 MAX_VARYING_VECTORS              : 0x8DFC,
 MAX_COMBINED_TEXTURE_IMAGE_UNITS : 0x8B4D,
 MAX_VERTEX_TEXTURE_IMAGE_UNITS   : 0x8B4C,
@@ -264,74 +264,74 @@ SHADER_TYPE                      : 0x8B4
 DELETE_STATUS                    : 0x8B80,
 LINK_STATUS                      : 0x8B82,
 VALIDATE_STATUS                  : 0x8B83,
 ATTACHED_SHADERS                 : 0x8B85,
 ACTIVE_UNIFORMS                  : 0x8B86,
 ACTIVE_ATTRIBUTES                : 0x8B89,
 SHADING_LANGUAGE_VERSION         : 0x8B8C,
 CURRENT_PROGRAM                  : 0x8B8D,
-    
+
     /* StencilFunction */
 NEVER                          : 0x0200,
 LESS                           : 0x0201,
 EQUAL                          : 0x0202,
 LEQUAL                         : 0x0203,
 GREATER                        : 0x0204,
 NOTEQUAL                       : 0x0205,
 GEQUAL                         : 0x0206,
 ALWAYS                         : 0x0207,
-    
+
     /* StencilOp */
     /*      ZERO */
 KEEP                           : 0x1E00,
 REPLACE                        : 0x1E01,
 INCR                           : 0x1E02,
 DECR                           : 0x1E03,
 INVERT                         : 0x150A,
 INCR_WRAP                      : 0x8507,
 DECR_WRAP                      : 0x8508,
-    
+
     /* StringName */
 VENDOR                         : 0x1F00,
 RENDERER                       : 0x1F01,
 VERSION                        : 0x1F02,
-    
+
     /* TextureMagFilter */
 NEAREST                        : 0x2600,
 LINEAR                         : 0x2601,
-    
+
     /* TextureMinFilter */
     /*      NEAREST */
     /*      LINEAR */
 NEAREST_MIPMAP_NEAREST         : 0x2700,
 LINEAR_MIPMAP_NEAREST          : 0x2701,
 NEAREST_MIPMAP_LINEAR          : 0x2702,
 LINEAR_MIPMAP_LINEAR           : 0x2703,
-    
+
     /* TextureParameterName */
 TEXTURE_MAG_FILTER             : 0x2800,
 TEXTURE_MIN_FILTER             : 0x2801,
 TEXTURE_WRAP_S                 : 0x2802,
 TEXTURE_WRAP_T                 : 0x2803,
-    
+
     /* TextureTarget */
 TEXTURE_2D                     : 0x0DE1,
 TEXTURE                        : 0x1702,
-    
+
 TEXTURE_CUBE_MAP               : 0x8513,
 TEXTURE_BINDING_CUBE_MAP       : 0x8514,
 TEXTURE_CUBE_MAP_POSITIVE_X    : 0x8515,
 TEXTURE_CUBE_MAP_NEGATIVE_X    : 0x8516,
 TEXTURE_CUBE_MAP_POSITIVE_Y    : 0x8517,
 TEXTURE_CUBE_MAP_NEGATIVE_Y    : 0x8518,
 TEXTURE_CUBE_MAP_POSITIVE_Z    : 0x8519,
 TEXTURE_CUBE_MAP_NEGATIVE_Z    : 0x851A,
 MAX_CUBE_MAP_TEXTURE_SIZE      : 0x851C,
-    
+
     /* TextureUnit */
 TEXTURE0                       : 0x84C0,
 TEXTURE1                       : 0x84C1,
 TEXTURE2                       : 0x84C2,
 TEXTURE3                       : 0x84C3,
 TEXTURE4                       : 0x84C4,
 TEXTURE5                       : 0x84C5,
 TEXTURE6                       : 0x84C6,
@@ -356,103 +356,103 @@ TEXTURE24                      : 0x84D8,
 TEXTURE25                      : 0x84D9,
 TEXTURE26                      : 0x84DA,
 TEXTURE27                      : 0x84DB,
 TEXTURE28                      : 0x84DC,
 TEXTURE29                      : 0x84DD,
 TEXTURE30                      : 0x84DE,
 TEXTURE31                      : 0x84DF,
 ACTIVE_TEXTURE                 : 0x84E0,
-    
+
     /* TextureWrapMode */
 REPEAT                         : 0x2901,
 CLAMP_TO_EDGE                  : 0x812F,
 MIRRORED_REPEAT                : 0x8370,
-    
+
     /* Uniform Types */
 FLOAT_VEC2                     : 0x8B50,
 FLOAT_VEC3                     : 0x8B51,
 FLOAT_VEC4                     : 0x8B52,
 INT_VEC2                       : 0x8B53,
 INT_VEC3                       : 0x8B54,
 INT_VEC4                       : 0x8B55,
 BOOL                           : 0x8B56,
 BOOL_VEC2                      : 0x8B57,
 BOOL_VEC3                      : 0x8B58,
 BOOL_VEC4                      : 0x8B59,
 FLOAT_MAT2                     : 0x8B5A,
 FLOAT_MAT3                     : 0x8B5B,
 FLOAT_MAT4                     : 0x8B5C,
 SAMPLER_2D                     : 0x8B5E,
 SAMPLER_CUBE                   : 0x8B60,
-    
+
     /* Vertex Arrays */
 VERTEX_ATTRIB_ARRAY_ENABLED        : 0x8622,
 VERTEX_ATTRIB_ARRAY_SIZE           : 0x8623,
 VERTEX_ATTRIB_ARRAY_STRIDE         : 0x8624,
 VERTEX_ATTRIB_ARRAY_TYPE           : 0x8625,
 VERTEX_ATTRIB_ARRAY_NORMALIZED     : 0x886A,
 VERTEX_ATTRIB_ARRAY_POINTER        : 0x8645,
 VERTEX_ATTRIB_ARRAY_BUFFER_BINDING : 0x889F,
-    
+
     /* Shader Source */
 COMPILE_STATUS                 : 0x8B81,
-    
+
     /* Shader Precision-Specified Types */
 LOW_FLOAT                      : 0x8DF0,
 MEDIUM_FLOAT                   : 0x8DF1,
 HIGH_FLOAT                     : 0x8DF2,
 LOW_INT                        : 0x8DF3,
 MEDIUM_INT                     : 0x8DF4,
 HIGH_INT                       : 0x8DF5,
-    
+
     /* Framebuffer Object. */
 FRAMEBUFFER                    : 0x8D40,
 RENDERBUFFER                   : 0x8D41,
-    
+
 RGBA4                          : 0x8056,
 RGB5_A1                        : 0x8057,
 RGB565                         : 0x8D62,
 DEPTH_COMPONENT16              : 0x81A5,
 STENCIL_INDEX                  : 0x1901,
 STENCIL_INDEX8                 : 0x8D48,
 DEPTH_STENCIL                  : 0x84F9,
-    
+
 RENDERBUFFER_WIDTH             : 0x8D42,
 RENDERBUFFER_HEIGHT            : 0x8D43,
 RENDERBUFFER_INTERNAL_FORMAT   : 0x8D44,
 RENDERBUFFER_RED_SIZE          : 0x8D50,
 RENDERBUFFER_GREEN_SIZE        : 0x8D51,
 RENDERBUFFER_BLUE_SIZE         : 0x8D52,
 RENDERBUFFER_ALPHA_SIZE        : 0x8D53,
 RENDERBUFFER_DEPTH_SIZE        : 0x8D54,
 RENDERBUFFER_STENCIL_SIZE      : 0x8D55,
-    
+
 FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE           : 0x8CD0,
 FRAMEBUFFER_ATTACHMENT_OBJECT_NAME           : 0x8CD1,
 FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL         : 0x8CD2,
 FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE : 0x8CD3,
-    
+
 COLOR_ATTACHMENT0              : 0x8CE0,
 DEPTH_ATTACHMENT               : 0x8D00,
 STENCIL_ATTACHMENT             : 0x8D20,
 DEPTH_STENCIL_ATTACHMENT       : 0x821A,
-    
+
 NONE                           : 0,
-    
+
 FRAMEBUFFER_COMPLETE                      : 0x8CD5,
 FRAMEBUFFER_INCOMPLETE_ATTACHMENT         : 0x8CD6,
 FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT : 0x8CD7,
 FRAMEBUFFER_INCOMPLETE_DIMENSIONS         : 0x8CD9,
 FRAMEBUFFER_UNSUPPORTED                   : 0x8CDD,
-  
+
 FRAMEBUFFER_BINDING            : 0x8CA6,
 RENDERBUFFER_BINDING           : 0x8CA7,
 MAX_RENDERBUFFER_SIZE          : 0x84E8,
-    
+
 INVALID_FRAMEBUFFER_OPERATION  : 0x0506,
 
 IMPLEMENTATION_COLOR_READ_TYPE : 0x8B9A,
 IMPLEMENTATION_COLOR_READ_FORMAT : 0x8B9B,
 
 /* WebGL-specific enums */
 UNPACK_FLIP_Y_WEBGL                : 0x9240,
 UNPACK_PREMULTIPLY_ALPHA_WEBGL     : 0x9241,
@@ -710,36 +710,23 @@ CONDITION_SATISFIED : 0x911C,
 WAIT_FAILED : 0x911D,
 SYNC_FLUSH_COMMANDS_BIT : 0x00000001,
 TIMEOUT_IGNORED : -1,
 VERTEX_ATTRIB_ARRAY_DIVISOR : 0x88FE,
 ANY_SAMPLES_PASSED : 0x8C2F,
 ANY_SAMPLES_PASSED_CONSERVATIVE : 0x8D6A,
 SAMPLER_BINDING : 0x8919,
 RGB10_A2UI : 0x906F,
-GREEN : 0x1904,
-BLUE : 0x1905,
 INT_2_10_10_10_REV : 0x8D9F,
 TRANSFORM_FEEDBACK : 0x8E22,
 TRANSFORM_FEEDBACK_PAUSED : 0x8E23,
 TRANSFORM_FEEDBACK_ACTIVE : 0x8E24,
 TRANSFORM_FEEDBACK_BINDING : 0x8E25,
-COMPRESSED_R11_EAC : 0x9270,
-COMPRESSED_SIGNED_R11_EAC : 0x9271,
-COMPRESSED_RG11_EAC : 0x9272,
-COMPRESSED_SIGNED_RG11_EAC : 0x9273,
-COMPRESSED_RGB8_ETC2 : 0x9274,
-COMPRESSED_SRGB8_ETC2 : 0x9275,
-COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 : 0x9276,
-COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 : 0x9277,
-COMPRESSED_RGBA8_ETC2_EAC : 0x9278,
-COMPRESSED_SRGB8_ALPHA8_ETC2_EAC : 0x9279,
 TEXTURE_IMMUTABLE_FORMAT : 0x912F,
 MAX_ELEMENT_INDEX : 0x8D6B,
-NUM_SAMPLE_COUNTS : 0x9380,
 TEXTURE_IMMUTABLE_LEVELS : 0x82DF,
 
 /* WebGL-specific enums */
 MAX_CLIENT_WAIT_TIMEOUT_WEBGL: 0x9247
 };
 
 // Other non-function properties on the WebGL object
 var otherProperties = {
--- a/dom/canvas/test/webgl-conf/checkout/js/tests/gl-object-get-calls.js
+++ b/dom/canvas/test/webgl-conf/checkout/js/tests/gl-object-get-calls.js
@@ -310,18 +310,18 @@ var standardFrag = wtu.loadStandardFragm
 var standardProgram = gl.createProgram();
 gl.attachShader(standardProgram, standardVert);
 gl.attachShader(standardProgram, standardFrag);
 gl.linkProgram(standardProgram);
 var shaders = gl.getAttachedShaders(standardProgram);
 shouldBe('shaders.length', '2');
 shouldBeTrue('shaders[0] == standardVert && shaders[1] == standardFrag || shaders[1] == standardVert && shaders[0] == standardFrag');
 wtu.glErrorShouldBe(gl, gl.NO_ERROR);
-gl.getAttachedShaders(null);
-wtu.glErrorShouldBe(gl, gl.INVALID_VALUE);
+shouldThrow('gl.getAttachedShaders(null)');
+wtu.glErrorShouldBe(gl, gl.NO_ERROR);
 shouldThrow('gl.getAttachedShaders(standardVert)');
 wtu.glErrorShouldBe(gl, gl.NO_ERROR);
 
 debug("");
 debug("Test getProgramParameter");
 shouldBe('gl.getProgramParameter(standardProgram, gl.DELETE_STATUS)', 'false');
 shouldBe('gl.getProgramParameter(standardProgram, gl.LINK_STATUS)', 'true');
 shouldBe('typeof gl.getProgramParameter(standardProgram, gl.VALIDATE_STATUS)', '"boolean"');
--- a/dom/canvas/test/webgl-mochitest/test_sab_with_webgl.html
+++ b/dom/canvas/test/webgl-mochitest/test_sab_with_webgl.html
@@ -94,19 +94,19 @@ function TestScreenColor(gl, r, g, b, a)
   gl.useProgram(prog);
 
   // Test gl.bufferData(), gl.bufferSubData() and gl.readPixels() APIs with SAB as input.
   var arr = new SharedArrayBuffer(8*4);
   var view = new Float32Array(arr);
   view.set(new Float32Array([-1, -1, 1, -1, -1,  1, 1,  1]));
   var vb = gl.createBuffer();
   gl.bindBuffer(gl.ARRAY_BUFFER, vb);
-  gl.bufferData(gl.ARRAY_BUFFER, arr, gl.STATIC_DRAW);
+  gl.bufferData(gl.ARRAY_BUFFER, view, gl.STATIC_DRAW);
   ok(gl.getError() == 0, 'bufferData with SAB as input parameter works ok.');
-  gl.bufferSubData(gl.ARRAY_BUFFER, 0, arr);
+  gl.bufferSubData(gl.ARRAY_BUFFER, 0, view);
   ok(gl.getError() == 0, 'bufferSubData with SAB as input parameter works ok.');
   gl.enableVertexAttribArray(0);
   gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
   gl.clearColor(0, 0, 0, 1.0);
   gl.clear(gl.COLOR_BUFFER_BIT);
   gl.uniform4f(prog.uFragColor, 0.2, 0.4, 0.6, 1.0);
   gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
   var arr = new Uint8Array(4);
@@ -148,17 +148,17 @@ function TestScreenColor(gl, r, g, b, a)
     return;
   }
 
   var arr = new SharedArrayBuffer(8*4);
   var view = new Float32Array(arr);
   view.set(new Float32Array([-1, -1, 1, -1, -1,  1, 1,  1]));
   var vb = gl.createBuffer();
   gl.bindBuffer(gl.ARRAY_BUFFER, vb);
-  gl.bufferData(gl.ARRAY_BUFFER, arr, gl.STATIC_DRAW);
+  gl.bufferData(gl.ARRAY_BUFFER, view, gl.STATIC_DRAW);
 
   var arr2 = new SharedArrayBuffer(8*4);
   var view2 = new Float32Array(arr2);
   gl.getBufferSubData(gl.ARRAY_BUFFER, 0, view2);
   var equal = true;
   for(var i = 0; i < 8; ++i) {
     if (view[i] != view2[i]) equal = false;
   }
--- a/dom/html/HTMLContentElement.cpp
+++ b/dom/html/HTMLContentElement.cpp
@@ -1,29 +1,50 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/HTMLContentElement.h"
 #include "mozilla/dom/HTMLContentElementBinding.h"
+#include "mozilla/dom/HTMLUnknownElement.h"
 #include "mozilla/dom/NodeListBinding.h"
 #include "mozilla/dom/ShadowRoot.h"
 #include "mozilla/css/StyleRule.h"
 #include "nsGkAtoms.h"
 #include "nsStyleConsts.h"
 #include "nsIAtom.h"
 #include "nsCSSRuleProcessor.h"
 #include "nsRuleData.h"
 #include "nsRuleProcessorData.h"
 #include "nsRuleWalker.h"
 #include "nsCSSParser.h"
+#include "nsDocument.h"
 
-NS_IMPL_NS_NEW_HTML_ELEMENT(Content)
+// Expand NS_IMPL_NS_NEW_HTML_ELEMENT(Content) to add check for web components
+// being enabled.
+nsGenericHTMLElement*
+NS_NewHTMLContentElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
+                         mozilla::dom::FromParser aFromParser)
+{
+  // When this check is removed, remove the nsDocument.h and
+  // HTMLUnknownElement.h includes.  Also remove nsINode::IsHTMLContentElement.
+  //
+  // We have to jump through some hoops to be able to produce both NodeInfo* and
+  // already_AddRefed<NodeInfo>& for our callees.
+  RefPtr<mozilla::dom::NodeInfo> nodeInfo(aNodeInfo);
+  if (!nsDocument::IsWebComponentsEnabled(nodeInfo)) {
+    already_AddRefed<mozilla::dom::NodeInfo> nodeInfoArg(nodeInfo.forget());
+    return new mozilla::dom::HTMLUnknownElement(nodeInfoArg);
+  }
+
+  already_AddRefed<mozilla::dom::NodeInfo> nodeInfoArg(nodeInfo.forget());
+  return new mozilla::dom::HTMLContentElement(nodeInfoArg);
+}
 
 using namespace mozilla::dom;
 
 HTMLContentElement::HTMLContentElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
   : nsGenericHTMLElement(aNodeInfo), mValidSelector(true), mIsInsertionPoint(false)
 {
 }
 
@@ -61,17 +82,17 @@ HTMLContentElement::BindToTree(nsIDocume
                                                  aBindingParent,
                                                  aCompileEventHandlers);
   NS_ENSURE_SUCCESS(rv, rv);
 
   ShadowRoot* containingShadow = GetContainingShadow();
   if (containingShadow && !oldContainingShadow) {
     nsINode* parentNode = nsINode::GetParentNode();
     while (parentNode && parentNode != containingShadow) {
-      if (parentNode->IsHTMLElement(nsGkAtoms::content)) {
+      if (parentNode->IsHTMLContentElement()) {
         // Content element in fallback content is not an insertion point.
         return NS_OK;
       }
       parentNode = parentNode->GetParentNode();
     }
 
     // If the content element is being inserted into a ShadowRoot,
     // add this element to the list of insertion points.
--- a/dom/html/HTMLContentElement.h
+++ b/dom/html/HTMLContentElement.h
@@ -18,24 +18,33 @@ namespace dom {
 
 class DistributedContentList;
 
 class HTMLContentElement final : public nsGenericHTMLElement
 {
 public:
   explicit HTMLContentElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
 
-  NS_IMPL_FROMCONTENT_HTML_WITH_TAG(HTMLContentElement, content)
-
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLContentElement,
                                            nsGenericHTMLElement)
 
+  static HTMLContentElement* FromContent(nsIContent* aContent)
+  {
+    if (aContent->IsHTMLContentElement()) {
+      return static_cast<HTMLContentElement*>(aContent);
+    }
+
+    return nullptr;
+  }
+
+  virtual bool IsHTMLContentElement() const override { return true; }
+
   virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override;
 
   virtual nsIDOMNode* AsDOMNode() override { return this; }
 
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                               nsIContent* aBindingParent,
                               bool aCompileEventHandlers) override;
 
--- a/dom/html/HTMLShadowElement.cpp
+++ b/dom/html/HTMLShadowElement.cpp
@@ -3,20 +3,41 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/ShadowRoot.h"
 
 #include "ChildIterator.h"
 #include "nsContentUtils.h"
+#include "nsDocument.h"
 #include "mozilla/dom/HTMLShadowElement.h"
+#include "mozilla/dom/HTMLUnknownElement.h"
 #include "mozilla/dom/HTMLShadowElementBinding.h"
 
-NS_IMPL_NS_NEW_HTML_ELEMENT(Shadow)
+// Expand NS_IMPL_NS_NEW_HTML_ELEMENT(Shadow) to add check for web components
+// being enabled.
+nsGenericHTMLElement*
+NS_NewHTMLShadowElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
+                        mozilla::dom::FromParser aFromParser)
+{
+  // When this check is removed, remove the nsDocument.h and
+  // HTMLUnknownElement.h includes.  Also remove nsINode::IsHTMLShadowElement.
+  //
+  // We have to jump through some hoops to be able to produce both NodeInfo* and
+  // already_AddRefed<NodeInfo>& for our callees.
+  RefPtr<mozilla::dom::NodeInfo> nodeInfo(aNodeInfo);
+  if (!nsDocument::IsWebComponentsEnabled(nodeInfo)) {
+    already_AddRefed<mozilla::dom::NodeInfo> nodeInfoArg(nodeInfo.forget());
+    return new mozilla::dom::HTMLUnknownElement(nodeInfoArg);
+  }
+
+  already_AddRefed<mozilla::dom::NodeInfo> nodeInfoArg(nodeInfo.forget());
+  return new mozilla::dom::HTMLShadowElement(nodeInfoArg);
+}
 
 using namespace mozilla::dom;
 
 HTMLShadowElement::HTMLShadowElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
   : nsGenericHTMLElement(aNodeInfo), mIsInsertionPoint(false)
 {
 }
 
--- a/dom/html/HTMLShadowElement.h
+++ b/dom/html/HTMLShadowElement.h
@@ -13,28 +13,37 @@ namespace mozilla {
 namespace dom {
 
 class HTMLShadowElement final : public nsGenericHTMLElement,
                                 public nsStubMutationObserver
 {
 public:
   explicit HTMLShadowElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
 
-  NS_IMPL_FROMCONTENT_HTML_WITH_TAG(HTMLShadowElement, shadow)
-
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLShadowElement,
                                            nsGenericHTMLElement)
 
+  static HTMLShadowElement* FromContent(nsIContent* aContent)
+  {
+    if (aContent->IsHTMLShadowElement()) {
+      return static_cast<HTMLShadowElement*>(aContent);
+    }
+
+    return nullptr;
+  }
+
+  virtual bool IsHTMLShadowElement() const override { return true; }
+
   virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override;
 
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                               nsIContent* aBindingParent,
                               bool aCompileEventHandlers) override;
 
   virtual void UnbindFromTree(bool aDeep = true,
                               bool aNullParent = true) override;
--- a/dom/svg/SVGFEImageElement.cpp
+++ b/dom/svg/SVGFEImageElement.cpp
@@ -226,18 +226,18 @@ SVGFEImageElement::GetPrimitiveDescripti
 
   nsCOMPtr<imgIContainer> imageContainer;
   if (currentRequest) {
     currentRequest->GetImage(getter_AddRefs(imageContainer));
   }
 
   RefPtr<SourceSurface> image;
   if (imageContainer) {
-    image = imageContainer->GetFrame(imgIContainer::FRAME_CURRENT,
-                                     imgIContainer::FLAG_SYNC_DECODE);
+    uint32_t flags = imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY;
+    image = imageContainer->GetFrame(imgIContainer::FRAME_CURRENT, flags);
   }
 
   if (!image) {
     return FilterPrimitiveDescription(PrimitiveType::Empty);
   }
 
   IntSize nativeSize;
   imageContainer->GetWidth(&nativeSize.width);
--- a/dom/webidl/WebGL2RenderingContext.webidl
+++ b/dom/webidl/WebGL2RenderingContext.webidl
@@ -277,57 +277,42 @@ interface WebGL2RenderingContext : WebGL
     const GLenum CONDITION_SATISFIED                           = 0x911C;
     const GLenum WAIT_FAILED                                   = 0x911D;
     const GLenum SYNC_FLUSH_COMMANDS_BIT                       = 0x00000001;
     const GLenum VERTEX_ATTRIB_ARRAY_DIVISOR                   = 0x88FE;
     const GLenum ANY_SAMPLES_PASSED                            = 0x8C2F;
     const GLenum ANY_SAMPLES_PASSED_CONSERVATIVE               = 0x8D6A;
     const GLenum SAMPLER_BINDING                               = 0x8919;
     const GLenum RGB10_A2UI                                    = 0x906F;
-    const GLenum GREEN                                         = 0x1904;
-    const GLenum BLUE                                          = 0x1905;
     const GLenum INT_2_10_10_10_REV                            = 0x8D9F;
     const GLenum TRANSFORM_FEEDBACK                            = 0x8E22;
     const GLenum TRANSFORM_FEEDBACK_PAUSED                     = 0x8E23;
     const GLenum TRANSFORM_FEEDBACK_ACTIVE                     = 0x8E24;
     const GLenum TRANSFORM_FEEDBACK_BINDING                    = 0x8E25;
-    const GLenum COMPRESSED_R11_EAC                            = 0x9270;
-    const GLenum COMPRESSED_SIGNED_R11_EAC                     = 0x9271;
-    const GLenum COMPRESSED_RG11_EAC                           = 0x9272;
-    const GLenum COMPRESSED_SIGNED_RG11_EAC                    = 0x9273;
-    const GLenum COMPRESSED_RGB8_ETC2                          = 0x9274;
-    const GLenum COMPRESSED_SRGB8_ETC2                         = 0x9275;
-    const GLenum COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2      = 0x9276;
-    const GLenum COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2     = 0x9277;
-    const GLenum COMPRESSED_RGBA8_ETC2_EAC                     = 0x9278;
-    const GLenum COMPRESSED_SRGB8_ALPHA8_ETC2_EAC              = 0x9279;
     const GLenum TEXTURE_IMMUTABLE_FORMAT                      = 0x912F;
     const GLenum MAX_ELEMENT_INDEX                             = 0x8D6B;
-    const GLenum NUM_SAMPLE_COUNTS                             = 0x9380;
     const GLenum TEXTURE_IMMUTABLE_LEVELS                      = 0x82DF;
 
     const GLint64 TIMEOUT_IGNORED                              = -1;
 
     /* WebGL-specific enums */
     const GLenum MAX_CLIENT_WAIT_TIMEOUT_WEBGL                 = 0x9247;
 
     /* Buffer objects */
     // WebGL1:
     void bufferData(GLenum target, GLsizeiptr size, GLenum usage);
-    void bufferData(GLenum target, ArrayBufferView data, GLenum usage);
-    void bufferData(GLenum target, ArrayBuffer? data, GLenum usage);
-    void bufferData(GLenum target, SharedArrayBuffer data, GLenum usage);
-    void bufferSubData(GLenum target, GLintptr offset, ArrayBufferView data);
-    void bufferSubData(GLenum target, GLintptr offset, ArrayBuffer? data);
-    void bufferSubData(GLenum target, GLintptr offset, SharedArrayBuffer data);
+    void bufferData(GLenum target, ArrayBuffer? srcData, GLenum usage);
+    //void bufferData(GLenum target, ArrayBufferView srcData, GLenum usage);
+    void bufferSubData(GLenum target, GLintptr offset, ArrayBuffer srcData);
+    //void bufferSubData(GLenum target, GLintptr offset, ArrayBufferView srcData);
     // WebGL2:
-    void bufferData(GLenum target, ArrayBufferView srcData, GLenum usage, GLuint srcOffset,
-                    optional GLuint length = 0);
+    void bufferData(GLenum target, ArrayBufferView srcData, GLenum usage,
+                    optional GLuint srcOffset = 0, optional GLuint length = 0);
     void bufferSubData(GLenum target, GLintptr dstByteOffset, ArrayBufferView srcData,
-                       GLuint srcOffset, optional GLuint length = 0);
+                       optional GLuint srcOffset = 0, optional GLuint length = 0);
 
     void copyBufferSubData(GLenum readTarget, GLenum writeTarget, GLintptr readOffset,
                            GLintptr writeOffset, GLsizeiptr size);
     // MapBufferRange, in particular its read-only and write-only modes,
     // can not be exposed safely to JavaScript. GetBufferSubData
     // replaces it for the purpose of fetching data back from the GPU.
     void getBufferSubData(GLenum target, GLintptr srcByteOffset, ArrayBufferView dstData,
                           optional GLuint dstOffset = 0, optional GLuint length = 0);
@@ -360,28 +345,32 @@ interface WebGL2RenderingContext : WebGL
                       GLsizei height, GLsizei depth);
 
     // WebGL1 legacy entrypoints:
     [Throws] // Another overhead throws.
     void texImage2D(GLenum target, GLint level, GLint internalformat,
                     GLsizei width, GLsizei height, GLint border, GLenum format,
                     GLenum type, ArrayBufferView? pixels);
     [Throws]
-    void texImage2D(GLenum target, GLint level, GLenum internalformat,
+    void texImage2D(GLenum target, GLint level, GLint internalformat,
                     GLenum format, GLenum type, HTMLCanvasElement source); // May throw DOMException
     [Throws]
-    void texImage2D(GLenum target, GLint level, GLenum internalformat,
+    void texImage2D(GLenum target, GLint level, GLint internalformat,
                     GLenum format, GLenum type, HTMLImageElement source); // May throw DOMException
     [Throws]
-    void texImage2D(GLenum target, GLint level, GLenum internalformat,
+    void texImage2D(GLenum target, GLint level, GLint internalformat,
                     GLenum format, GLenum type, HTMLVideoElement source); // May throw DOMException
     [Throws] // Another overhead throws.
-    void texImage2D(GLenum target, GLint level, GLenum internalformat,
+    void texImage2D(GLenum target, GLint level, GLint internalformat,
                     GLenum format, GLenum type, ImageData source);
 
+    [Throws] // Another overhead throws.
+    void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
+                       GLsizei width, GLsizei height,
+                       GLenum format, GLenum type, ArrayBufferView? pixels);
     [Throws]
     void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
                        GLenum format, GLenum type, HTMLCanvasElement source); // May throw DOMException
     [Throws]
     void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
                        GLenum format, GLenum type, HTMLImageElement source); // May throw DOMException
     [Throws]
     void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
@@ -459,17 +448,17 @@ interface WebGL2RenderingContext : WebGL
                        HTMLVideoElement source); // May throw DOMException
     [Throws] // Another overhead throws.
     void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width,
                        GLsizei height, GLenum format, GLenum type,
                        ImageData source);
     [Throws] // Another overhead throws.
     void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width,
                        GLsizei height, GLenum format, GLenum type, ArrayBufferView srcData,
-                       optional GLuint srcOffset = 0);
+                       GLuint srcOffset);
 
     [Throws] // Another overhead throws.
     void texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset,
                        GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type,
                        GLintptr pboOffset);
     [Throws]
     void texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset,
                        GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type,
@@ -484,50 +473,52 @@ interface WebGL2RenderingContext : WebGL
                        HTMLVideoElement source); // May throw DOMException
     [Throws] // Another overhead throws.
     void texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset,
                        GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type,
                        ImageData source);
     [Throws] // Another overhead throws.
     void texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset,
                        GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type,
-                       ArrayBufferView srcData, optional GLuint srcOffset = 0);
+                       ArrayBufferView? srcData, optional GLuint srcOffset = 0);
 
     void copyTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset,
                            GLint x, GLint y, GLsizei width, GLsizei height);
 
     void compressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width,
                               GLsizei height, GLint border, GLintptr offset);
     void compressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width,
                               GLsizei height, GLint border, ArrayBufferView srcData,
-                              optional GLuint srcOffset = 0, optional GLuint srcLength = 0);
+                              optional GLuint srcOffset = 0, optional GLuint srcLengthOverride = 0);
 
     void compressedTexImage3D(GLenum target, GLint level, GLenum internalformat, GLsizei width,
                               GLsizei height, GLsizei depth, GLint border, GLintptr offset);
     void compressedTexImage3D(GLenum target, GLint level, GLenum internalformat, GLsizei width,
                               GLsizei height, GLsizei depth, GLint border, ArrayBufferView srcData,
-                              optional GLuint srcOffset = 0, optional GLuint srcLength = 0);
+                              optional GLuint srcOffset = 0, optional GLuint srcLengthOverride = 0);
 
     void compressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
                                  GLsizei width, GLsizei height, GLenum format, GLintptr offset);
     void compressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
                                  GLsizei width, GLsizei height, GLenum format,
-                                 ArrayBufferView srcData, optional GLuint srcOffset = 0,
-                                 optional GLuint srcLength = 0);
+                                 ArrayBufferView srcData,
+                                 optional GLuint srcOffset = 0,
+                                 optional GLuint srcLengthOverride = 0);
 
     void compressedTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
                                  GLint zoffset, GLsizei width, GLsizei height, GLsizei depth,
                                  GLenum format, GLintptr offset);
     void compressedTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
                                  GLint zoffset, GLsizei width, GLsizei height, GLsizei depth,
                                  GLenum format, ArrayBufferView srcData,
-                                 optional GLuint srcOffset = 0, optional GLuint srcLength = 0);
+                                 optional GLuint srcOffset = 0,
+                                 optional GLuint srcLengthOverride = 0);
 
     /* Programs and shaders */
-    [WebGLHandlesContextLoss] GLint getFragDataLocation(WebGLProgram? program, DOMString name);
+    [WebGLHandlesContextLoss] GLint getFragDataLocation(WebGLProgram program, DOMString name);
 
     /* Uniforms */
     void uniform1ui(WebGLUniformLocation? location, GLuint v0);
     void uniform2ui(WebGLUniformLocation? location, GLuint v0, GLuint v1);
     void uniform3ui(WebGLUniformLocation? location, GLuint v0, GLuint v1, GLuint v2);
     void uniform4ui(WebGLUniformLocation? location, GLuint v0, GLuint v1, GLuint v2, GLuint v3);
 
     void uniform1fv(WebGLUniformLocation? location, Float32List data, optional GLuint srcOffset = 0,
@@ -587,22 +578,27 @@ interface WebGL2RenderingContext : WebGL
 
     /* Writing to the drawing buffer */
     void vertexAttribDivisor(GLuint index, GLuint divisor);
     void drawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instanceCount);
     void drawElementsInstanced(GLenum mode, GLsizei count, GLenum type, GLintptr offset, GLsizei instanceCount);
     void drawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, GLintptr offset);
 
     /* Reading back pixels */
+    // WebGL1:
+    [Throws] // Throws on readback in a write-only context.
+    void readPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type,
+                    ArrayBufferView? dstData);
+    // WebGL2:
     [Throws] // Throws on readback in a write-only context.
     void readPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type,
                     GLintptr offset);
     [Throws] // Throws on readback in a write-only context.
     void readPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type,
-                    ArrayBufferView dstData, optional GLuint dstOffset = 0);
+                    ArrayBufferView dstData, GLuint dstOffset);
 
     /* Multiple Render Targets */
     void drawBuffers(sequence<GLenum> buffers);
 
     void clearBufferfv(GLenum buffer, GLint drawbuffer, Float32List values,
                        optional GLuint srcOffset = 0);
     void clearBufferiv(GLenum buffer, GLint drawbuffer, Int32List values,
                        optional GLuint srcOffset = 0);
@@ -610,62 +606,63 @@ interface WebGL2RenderingContext : WebGL
                         optional GLuint srcOffset = 0);
 
     void clearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil);
 
     /* Query Objects */
     WebGLQuery? createQuery();
     void deleteQuery(WebGLQuery? query);
     [WebGLHandlesContextLoss] GLboolean isQuery(WebGLQuery? query);
-    void beginQuery(GLenum target, WebGLQuery? query);
+    void beginQuery(GLenum target, WebGLQuery query);
     void endQuery(GLenum target);
     any getQuery(GLenum target, GLenum pname);
-    any getQueryParameter(WebGLQuery? query, GLenum pname);
+    any getQueryParameter(WebGLQuery query, GLenum pname);
 
     /* Sampler Objects */
     WebGLSampler? createSampler();
     void deleteSampler(WebGLSampler? sampler);
     [WebGLHandlesContextLoss] GLboolean isSampler(WebGLSampler? sampler);
     void bindSampler(GLuint unit, WebGLSampler? sampler);
-    void samplerParameteri(WebGLSampler? sampler, GLenum pname, GLint param);
-    void samplerParameterf(WebGLSampler? sampler, GLenum pname, GLfloat param);
-    any getSamplerParameter(WebGLSampler? sampler, GLenum pname);
+    void samplerParameteri(WebGLSampler sampler, GLenum pname, GLint param);
+    void samplerParameterf(WebGLSampler sampler, GLenum pname, GLfloat param);
+    any getSamplerParameter(WebGLSampler sampler, GLenum pname);
 
     /* Sync objects */
     WebGLSync? fenceSync(GLenum condition, GLbitfield flags);
     [WebGLHandlesContextLoss] GLboolean isSync(WebGLSync? sync);
     void deleteSync(WebGLSync? sync);
-    GLenum clientWaitSync(WebGLSync? sync, GLbitfield flags, GLint64 timeout);
-    void waitSync(WebGLSync? sync, GLbitfield flags, GLint64 timeout);
-    any getSyncParameter(WebGLSync? sync, GLenum pname);
+    GLenum clientWaitSync(WebGLSync sync, GLbitfield flags, GLuint64 timeout);
+    void waitSync(WebGLSync sync, GLbitfield flags, GLint64 timeout);
+    any getSyncParameter(WebGLSync sync, GLenum pname);
 
     /* Transform Feedback */
     WebGLTransformFeedback? createTransformFeedback();
     void deleteTransformFeedback(WebGLTransformFeedback? tf);
     [WebGLHandlesContextLoss] GLboolean isTransformFeedback(WebGLTransformFeedback? tf);
     void bindTransformFeedback(GLenum target, WebGLTransformFeedback? tf);
     void beginTransformFeedback(GLenum primitiveMode);
     void endTransformFeedback();
-    void transformFeedbackVaryings(WebGLProgram? program, sequence<DOMString> varyings, GLenum bufferMode);
-    [NewObject] WebGLActiveInfo? getTransformFeedbackVarying(WebGLProgram? program, GLuint index);
+    void transformFeedbackVaryings(WebGLProgram program, sequence<DOMString> varyings, GLenum bufferMode);
+    [NewObject]
+    WebGLActiveInfo? getTransformFeedbackVarying(WebGLProgram program, GLuint index);
     void pauseTransformFeedback();
     void resumeTransformFeedback();
 
     /* Uniform Buffer Objects and Transform Feedback Buffers */
     void bindBufferBase(GLenum target, GLuint index, WebGLBuffer? buffer);
     void bindBufferRange(GLenum target, GLuint index, WebGLBuffer? buffer, GLintptr offset, GLsizeiptr size);
     [Throws] // GetOrCreateDOMReflector can fail.
     any getIndexedParameter(GLenum target, GLuint index);
-    sequence<GLuint>? getUniformIndices(WebGLProgram? program, sequence<DOMString> uniformNames);
-    any getActiveUniforms(WebGLProgram? program, sequence<GLuint> uniformIndices, GLenum pname);
-    GLuint getUniformBlockIndex(WebGLProgram? program, DOMString uniformBlockName);
+    sequence<GLuint>? getUniformIndices(WebGLProgram program, sequence<DOMString> uniformNames);
+    any getActiveUniforms(WebGLProgram program, sequence<GLuint> uniformIndices, GLenum pname);
+    GLuint getUniformBlockIndex(WebGLProgram program, DOMString uniformBlockName);
     [Throws] // Creating a Uint32Array can fail.
-    any getActiveUniformBlockParameter(WebGLProgram? program, GLuint uniformBlockIndex, GLenum pname);
-    DOMString? getActiveUniformBlockName(WebGLProgram? program, GLuint uniformBlockIndex);
-    void uniformBlockBinding(WebGLProgram? program, GLuint uniformBlockIndex, GLuint uniformBlockBinding);
+    any getActiveUniformBlockParameter(WebGLProgram program, GLuint uniformBlockIndex, GLenum pname);
+    DOMString? getActiveUniformBlockName(WebGLProgram program, GLuint uniformBlockIndex);
+    void uniformBlockBinding(WebGLProgram program, GLuint uniformBlockIndex, GLuint uniformBlockBinding);
 
     /* Vertex Array Objects */
     WebGLVertexArrayObject? createVertexArray();
     void deleteVertexArray(WebGLVertexArrayObject? vertexArray);
     [WebGLHandlesContextLoss] GLboolean isVertexArray(WebGLVertexArrayObject? vertexArray);
     void bindVertexArray(WebGLVertexArrayObject? array);
 };
 
--- a/dom/webidl/WebGLRenderingContext.webidl
+++ b/dom/webidl/WebGLRenderingContext.webidl
@@ -31,23 +31,23 @@ typedef unsigned long  GLuint;
 typedef unrestricted float GLfloat;
 typedef unrestricted float GLclampf;
 typedef unsigned long long GLuint64EXT;
 
 dictionary WebGLContextAttributes {
     // boolean alpha = true;
     // We deviate from the spec here.
     // If alpha isn't specified, we rely on a pref ("webgl.default-no-alpha")
-    boolean alpha;
-    boolean depth = true;
-    boolean stencil = false;
-    boolean antialias = true;
-    boolean premultipliedAlpha = true;
-    boolean preserveDrawingBuffer = false;
-    boolean failIfMajorPerformanceCaveat = false;
+    GLboolean alpha;
+    GLboolean depth = true;
+    GLboolean stencil = false;
+    GLboolean antialias = true;
+    GLboolean premultipliedAlpha = true;
+    GLboolean preserveDrawingBuffer = false;
+    GLboolean failIfMajorPerformanceCaveat = false;
 };
 
 [Exposed=(Window,Worker),
  Func="mozilla::dom::OffscreenCanvas::PrefEnabledOnWorkerThread"]
 interface WebGLBuffer {
 };
 
 [Exposed=(Window,Worker),
@@ -535,44 +535,42 @@ interface WebGLRenderingContext {
 
     [NeedsCallerType]
     sequence<DOMString>? getSupportedExtensions();
 
     [Throws, NeedsCallerType]
     object? getExtension(DOMString name);
 
     void activeTexture(GLenum texture);
-    void attachShader(WebGLProgram? program, WebGLShader? shader);
-    void bindAttribLocation(WebGLProgram? program, GLuint index, DOMString name);
+    void attachShader(WebGLProgram program, WebGLShader shader);
+    void bindAttribLocation(WebGLProgram program, GLuint index, DOMString name);
     void bindBuffer(GLenum target, WebGLBuffer? buffer);
     void bindFramebuffer(GLenum target, WebGLFramebuffer? framebuffer);
     void bindRenderbuffer(GLenum target, WebGLRenderbuffer? renderbuffer);
     void bindTexture(GLenum target, WebGLTexture? texture);
     void blendColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
     void blendEquation(GLenum mode);
     void blendEquationSeparate(GLenum modeRGB, GLenum modeAlpha);
     void blendFunc(GLenum sfactor, GLenum dfactor);
     void blendFuncSeparate(GLenum srcRGB, GLenum dstRGB,
                            GLenum srcAlpha, GLenum dstAlpha);
 
     void bufferData(GLenum target, GLsizeiptr size, GLenum usage);
+    void bufferData(GLenum target, ArrayBuffer? data, GLenum usage);
     void bufferData(GLenum target, ArrayBufferView data, GLenum usage);
-    void bufferData(GLenum target, ArrayBuffer? data, GLenum usage);
-    void bufferData(GLenum target, SharedArrayBuffer data, GLenum usage);
+    void bufferSubData(GLenum target, GLintptr offset, ArrayBuffer data);
     void bufferSubData(GLenum target, GLintptr offset, ArrayBufferView data);
-    void bufferSubData(GLenum target, GLintptr offset, ArrayBuffer? data);
-    void bufferSubData(GLenum target, GLintptr offset, SharedArrayBuffer data);
 
     [WebGLHandlesContextLoss] GLenum checkFramebufferStatus(GLenum target);
     void clear(GLbitfield mask);
     void clearColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
     void clearDepth(GLclampf depth);
     void clearStencil(GLint s);
     void colorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha);
-    void compileShader(WebGLShader? shader);
+    void compileShader(WebGLShader shader);
 
     void compressedTexImage2D(GLenum target, GLint level, GLenum internalformat,
                               GLsizei width, GLsizei height, GLint border,
                               ArrayBufferView data);
     void compressedTexSubImage2D(GLenum target, GLint level,
                                  GLint xoffset, GLint yoffset,
                                  GLsizei width, GLsizei height, GLenum format,
                                  ArrayBufferView data);
@@ -597,17 +595,17 @@ interface WebGLRenderingContext {
     void deleteProgram(WebGLProgram? program);
     void deleteRenderbuffer(WebGLRenderbuffer? renderbuffer);
     void deleteShader(WebGLShader? shader);
     void deleteTexture(WebGLTexture? texture);
 
     void depthFunc(GLenum func);
     void depthMask(GLboolean flag);
     void depthRange(GLclampf zNear, GLclampf zFar);
-    void detachShader(WebGLProgram? program, WebGLShader? shader);
+    void detachShader(WebGLProgram program, WebGLShader shader);
     void disable(GLenum cap);
     void disableVertexAttribArray(GLuint index);
     void drawArrays(GLenum mode, GLint first, GLsizei count);
     void drawElements(GLenum mode, GLsizei count, GLenum type, GLintptr offset);
 
     void enable(GLenum cap);
     void enableVertexAttribArray(GLuint index);
     void finish();
@@ -617,114 +615,114 @@ interface WebGLRenderingContext {
                                  WebGLRenderbuffer? renderbuffer);
     void framebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget,
                               WebGLTexture? texture, GLint level);
     void frontFace(GLenum mode);
 
     void generateMipmap(GLenum target);
 
     [NewObject]
-    WebGLActiveInfo? getActiveAttrib(WebGLProgram? program, GLuint index);
+    WebGLActiveInfo? getActiveAttrib(WebGLProgram program, GLuint index);
     [NewObject]
-    WebGLActiveInfo? getActiveUniform(WebGLProgram? program, GLuint index);
+    WebGLActiveInfo? getActiveUniform(WebGLProgram program, GLuint index);
 
-    sequence<WebGLShader>? getAttachedShaders(WebGLProgram? program);
+    sequence<WebGLShader>? getAttachedShaders(WebGLProgram program);
 
-    [WebGLHandlesContextLoss] GLint getAttribLocation(WebGLProgram? program, DOMString name);
+    [WebGLHandlesContextLoss] GLint getAttribLocation(WebGLProgram program, DOMString name);
 
     any getBufferParameter(GLenum target, GLenum pname);
     [Throws]
     any getParameter(GLenum pname);
 
     [WebGLHandlesContextLoss] GLenum getError();
 
     [Throws]
     any getFramebufferAttachmentParameter(GLenum target, GLenum attachment,
                                           GLenum pname);
-    any getProgramParameter(WebGLProgram? program, GLenum pname);
-    DOMString? getProgramInfoLog(WebGLProgram? program);
+    any getProgramParameter(WebGLProgram program, GLenum pname);
+    DOMString? getProgramInfoLog(WebGLProgram program);
     any getRenderbufferParameter(GLenum target, GLenum pname);
-    any getShaderParameter(WebGLShader? shader, GLenum pname);
+    any getShaderParameter(WebGLShader shader, GLenum pname);
 
     [NewObject]
     WebGLShaderPrecisionFormat? getShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype);
 
-    DOMString? getShaderInfoLog(WebGLShader? shader);
+    DOMString? getShaderInfoLog(WebGLShader shader);
 
-    DOMString? getShaderSource(WebGLShader? shader);
+    DOMString? getShaderSource(WebGLShader shader);
 
     any getTexParameter(GLenum target, GLenum pname);
 
-    any getUniform(WebGLProgram? program, WebGLUniformLocation? location);
+    any getUniform(WebGLProgram program, WebGLUniformLocation location);
 
     [NewObject]
-    WebGLUniformLocation? getUniformLocation(WebGLProgram? program, DOMString name);
+    WebGLUniformLocation? getUniformLocation(WebGLProgram program, DOMString name);
 
     [Throws]
     any getVertexAttrib(GLuint index, GLenum pname);
 
-    [WebGLHandlesContextLoss] GLsizeiptr getVertexAttribOffset(GLuint index, GLenum pname);
+    [WebGLHandlesContextLoss] GLintptr getVertexAttribOffset(GLuint index, GLenum pname);
 
     void hint(GLenum target, GLenum mode);
     [WebGLHandlesContextLoss] GLboolean isBuffer(WebGLBuffer? buffer);
     [WebGLHandlesContextLoss] GLboolean isEnabled(GLenum cap);
     [WebGLHandlesContextLoss] GLboolean isFramebuffer(WebGLFramebuffer? framebuffer);
     [WebGLHandlesContextLoss] GLboolean isProgram(WebGLProgram? program);
     [WebGLHandlesContextLoss] GLboolean isRenderbuffer(WebGLRenderbuffer? renderbuffer);
     [WebGLHandlesContextLoss] GLboolean isShader(WebGLShader? shader);
     [WebGLHandlesContextLoss] GLboolean isTexture(WebGLTexture? texture);
     void lineWidth(GLfloat width);
-    void linkProgram(WebGLProgram? program);
+    void linkProgram(WebGLProgram program);
     void pixelStorei(GLenum pname, GLint param);
     void polygonOffset(GLfloat factor, GLfloat units);
 
     [Throws]
     void readPixels(GLint x, GLint y, GLsizei width, GLsizei height,
                     GLenum format, GLenum type, ArrayBufferView? pixels);
 
     void renderbufferStorage(GLenum target, GLenum internalformat,
                              GLsizei width, GLsizei height);
     void sampleCoverage(GLclampf value, GLboolean invert);
     void scissor(GLint x, GLint y, GLsizei width, GLsizei height);
 
-    void shaderSource(WebGLShader? shader, DOMString source);
+    void shaderSource(WebGLShader shader, DOMString source);
 
     void stencilFunc(GLenum func, GLint ref, GLuint mask);
     void stencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask);
     void stencilMask(GLuint mask);
     void stencilMaskSeparate(GLenum face, GLuint mask);
     void stencilOp(GLenum fail, GLenum zfail, GLenum zpass);
     void stencilOpSeparate(GLenum face, GLenum fail, GLenum zfail, GLenum zpass);
 
 
     // Overloads must share [Throws].
     [Throws] // Can't actually throw.
-    void texImage2D(GLenum target, GLint level, GLenum internalformat,
+    void texImage2D(GLenum target, GLint level, GLint internalformat,
                     GLsizei width, GLsizei height, GLint border, GLenum format,
                     GLenum type, ArrayBufferView? pixels);
     [Throws] // Can't actually throw.
-    void texImage2D(GLenum target, GLint level, GLenum internalformat,
+    void texImage2D(GLenum target, GLint level, GLint internalformat,
                     GLenum format, GLenum type, ImageData pixels);
     [Throws]
-    void texImage2D(GLenum target, GLint level, GLenum internalformat,
+    void texImage2D(GLenum target, GLint level, GLint internalformat,
                     GLenum format, GLenum type, HTMLImageElement image); // May throw DOMException
     [Throws]
-    void texImage2D(GLenum target, GLint level, GLenum internalformat,
+    void texImage2D(GLenum target, GLint level, GLint internalformat,
                     GLenum format, GLenum type, HTMLCanvasElement canvas); // May throw DOMException
     [Throws]
-    void texImage2D(GLenum target, GLint level, GLenum internalformat,
+    void texImage2D(GLenum target, GLint level, GLint internalformat,
                     GLenum format, GLenum type, HTMLVideoElement video); // May throw DOMException
 
     void texParameterf(GLenum target, GLenum pname, GLfloat param);
     void texParameteri(GLenum target, GLenum pname, GLint param);
 
     [Throws] // Can't actually throw.
     void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
-                       GLsizei width, GLsizei height, GLenum format, GLenum type,
-                       ArrayBufferView pixels);
+                       GLsizei width, GLsizei height,
+                       GLenum format, GLenum type, ArrayBufferView? pixels);
     [Throws] // Can't actually throw.
     void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
                        GLenum format, GLenum type, ImageData pixels);
     [Throws]
     void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
                        GLenum format, GLenum type, HTMLImageElement image); // May throw DOMException
     [Throws]
     void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
@@ -753,17 +751,17 @@ interface WebGLRenderingContext {
     void uniform3iv(WebGLUniformLocation? location, Int32List data);
     void uniform4iv(WebGLUniformLocation? location, Int32List data);
 
     void uniformMatrix2fv(WebGLUniformLocation? location, GLboolean transpose, Float32List data);
     void uniformMatrix3fv(WebGLUniformLocation? location, GLboolean transpose, Float32List data);
     void uniformMatrix4fv(WebGLUniformLocation? location, GLboolean transpose, Float32List data);
 
     void useProgram(WebGLProgram? program);
-    void validateProgram(WebGLProgram? program);
+    void validateProgram(WebGLProgram program);
 
     void vertexAttrib1f(GLuint indx, GLfloat x);
     void vertexAttrib1fv(GLuint indx, Float32Array values);
     void vertexAttrib1fv(GLuint indx, sequence<GLfloat> values);
     void vertexAttrib2f(GLuint indx, GLfloat x, GLfloat y);
     void vertexAttrib2fv(GLuint indx, Float32Array values);
     void vertexAttrib2fv(GLuint indx, sequence<GLfloat> values);
     void vertexAttrib3f(GLuint indx, GLfloat x, GLfloat y, GLfloat z);
@@ -841,17 +839,17 @@ interface WEBGL_debug_renderer_info
 {
     const GLenum UNMASKED_VENDOR_WEBGL        = 0x9245;
     const GLenum UNMASKED_RENDERER_WEBGL      = 0x9246;
 };
 
 [NoInterfaceObject]
 interface WEBGL_debug_shaders
 {
-    DOMString getTranslatedShaderSource(WebGLShader? shader);
+    DOMString getTranslatedShaderSource(WebGLShader shader);
 };
 
 [NoInterfaceObject]
 interface WEBGL_depth_texture
 {
     const GLenum UNSIGNED_INT_24_8_WEBGL = 0x84FA;
 };
 
@@ -1014,14 +1012,14 @@ interface EXT_disjoint_timer_query {
     const GLenum QUERY_RESULT_AVAILABLE_EXT = 0x8867;
     const GLenum TIME_ELAPSED_EXT = 0x88BF;
     const GLenum TIMESTAMP_EXT = 0x8E28;
     const GLenum GPU_DISJOINT_EXT = 0x8FBB;
 
     WebGLQuery? createQueryEXT();
     void deleteQueryEXT(WebGLQuery? query);
     [WebGLHandlesContextLoss] boolean isQueryEXT(WebGLQuery? query);
-    void beginQueryEXT(GLenum target, WebGLQuery? query);
+    void beginQueryEXT(GLenum target, WebGLQuery query);
     void endQueryEXT(GLenum target);
-    void queryCounterEXT(WebGLQuery? query, GLenum target);
+    void queryCounterEXT(WebGLQuery query, GLenum target);
     any getQueryEXT(GLenum target, GLenum pname);
-    any getQueryObjectEXT(WebGLQuery? query, GLenum pname);
+    any getQueryObjectEXT(WebGLQuery query, GLenum pname);
 };
--- a/dom/workers/ServiceWorkerRegistrationInfo.cpp
+++ b/dom/workers/ServiceWorkerRegistrationInfo.cpp
@@ -235,20 +235,18 @@ ServiceWorkerRegistrationInfo::Activate(
   if (!mWaitingWorker) {
     return;
   }
 
   TransitionWaitingToActive();
 
   // FIXME(nsm): Unlink appcache if there is one.
 
+  // "Queue a task to fire a simple event named controllerchange..."
   RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
-  swm->CheckPendingReadyPromises();
-
-  // "Queue a task to fire a simple event named controllerchange..."
   nsCOMPtr<nsIRunnable> controllerChangeRunnable =
     NewRunnableMethod<RefPtr<ServiceWorkerRegistrationInfo>>(
       swm, &ServiceWorkerManager::FireControllerChange, this);
   NS_DispatchToMainThread(controllerChangeRunnable);
 
   nsCOMPtr<nsIRunnable> failRunnable =
     NewRunnableMethod<bool>(this,
                             &ServiceWorkerRegistrationInfo::FinishActivate,
@@ -314,16 +312,20 @@ ServiceWorkerRegistrationInfo::AsyncUpda
 {
   AssertIsOnMainThread();
   RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
   if (aTransition == Invalidate) {
     swm->InvalidateServiceWorkerRegistrationWorker(this, aWorker);
   } else {
     MOZ_ASSERT(aTransition == TransitionToNextState);
     swm->TransitionServiceWorkerRegistrationWorker(this, aWorker);
+
+    if (aWorker == WhichServiceWorker::WAITING_WORKER) {
+      swm->CheckPendingReadyPromises();
+    }
   }
 }
 
 void
 ServiceWorkerRegistrationInfo::UpdateRegistrationStateProperties(WhichServiceWorker aWorker,
                                                                  TransitionType aTransition)
 {
   AssertIsOnMainThread();
--- a/dom/workers/test/serviceworkers/test_worker_reference_gc_timeout.html
+++ b/dom/workers/test/serviceworkers/test_worker_reference_gc_timeout.html
@@ -23,34 +23,55 @@ SimpleTest.requestFlakyTimeout("Forcing 
 
 add_task(function setupPrefs() {
   return SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
   ]});
 });
 
-//var worker;
 add_task(function* test_worker_ref_gc() {
   let registration = yield navigator.serviceWorker.register(
     "lazy_worker.js", { scope: "./lazy_worker_scope_timeout"} )
     .then(function(registration) {
       SpecialPowers.exactGC();
       var worker = registration.installing;
       return new Promise(function(resolve) {
         worker.addEventListener('statechange', function() {
           info("state is " + worker.state + "\n");
           SpecialPowers.exactGC();
           if (worker.state === 'activated') {
             resolve(registration);
           }
         });
       });
     });
-
   ok(true, "Got activated event!");
 
   yield registration.unregister();
+});
+
+add_task(function* test_worker_ref_gc_ready_promise() {
+  let wait_active = navigator.serviceWorker.ready.then(function(reg) {
+    SpecialPowers.exactGC();
+    ok(reg.active, "Got active worker.");
+    ok(reg.active.state === "activating", "Worker is in activating state");
+    return new Promise(function(res) {
+      reg.active.onstatechange = function(e) {
+        reg.active.onstatechange = null;
+        ok(reg.active.state === "activated", "Worker was activated");
+        res();
+      }
+    });
+  });
+
+  let registration = yield navigator.serviceWorker.register(
+    "lazy_worker.js", { scope: "."} );
+  yield wait_active;
+  yield registration.unregister();
+});
+
+add_task(function* cleanup() {
   yield SpecialPowers.popPrefEnv();
 });
 </script>
 </body>
 </html>
--- a/js/public/GCPolicyAPI.h
+++ b/js/public/GCPolicyAPI.h
@@ -114,16 +114,21 @@ template <> struct GCPolicy<uint64_t> : 
 template <typename T>
 struct GCPointerPolicy
 {
     static T initial() { return nullptr; }
     static void trace(JSTracer* trc, T* vp, const char* name) {
         if (*vp)
             js::UnsafeTraceManuallyBarrieredEdge(trc, vp, name);
     }
+    static bool needsSweep(T* vp) {
+        if (*vp)
+            return js::gc::IsAboutToBeFinalizedUnbarriered(vp);
+        return false;
+    }
 };
 template <> struct GCPolicy<JS::Symbol*> : public GCPointerPolicy<JS::Symbol*> {};
 template <> struct GCPolicy<JSAtom*> : public GCPointerPolicy<JSAtom*> {};
 template <> struct GCPolicy<JSFunction*> : public GCPointerPolicy<JSFunction*> {};
 template <> struct GCPolicy<JSObject*> : public GCPointerPolicy<JSObject*> {};
 template <> struct GCPolicy<JSScript*> : public GCPointerPolicy<JSScript*> {};
 template <> struct GCPolicy<JSString*> : public GCPointerPolicy<JSString*> {};
 
--- a/js/public/TracingAPI.h
+++ b/js/public/TracingAPI.h
@@ -380,16 +380,24 @@ namespace js {
 //
 // This method does not check if |*edgep| is non-null before tracing through
 // it, so callers must check any nullable pointer before calling this method.
 template <typename T>
 extern JS_PUBLIC_API(void)
 UnsafeTraceManuallyBarrieredEdge(JSTracer* trc, T* edgep, const char* name);
 
 namespace gc {
+
 // Return true if the given edge is not live and is about to be swept.
 template <typename T>
 extern JS_PUBLIC_API(bool)
 EdgeNeedsSweep(JS::Heap<T>* edgep);
+
+// Not part of the public API, but declared here so we can use it in GCPolicy
+// which is.
+template <typename T>
+bool
+IsAboutToBeFinalizedUnbarriered(T* thingp);
+
 } // namespace gc
 } // namespace js
 
 #endif /* js_TracingAPI_h */
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -647,17 +647,17 @@ ArrayMetaTypeDescr::create(JSContext* cx
     obj->initReservedSlot(JS_DESCR_SLOT_TYPROTO, ObjectValue(*prototypeObj));
 
     if (!LinkConstructorAndPrototype(cx, obj, prototypeObj))
         return nullptr;
 
     if (!CreateTraceList(cx, obj))
         return nullptr;
 
-    if (!cx->zone()->typeDescrObjects.put(obj)) {
+    if (!cx->zone()->addTypeDescrObject(cx, obj)) {
         ReportOutOfMemory(cx);
         return nullptr;
     }
 
     return obj;
 }
 
 bool
@@ -988,18 +988,18 @@ StructMetaTypeDescr::create(JSContext* c
     descr->initReservedSlot(JS_DESCR_SLOT_TYPROTO, ObjectValue(*prototypeObj));
 
     if (!LinkConstructorAndPrototype(cx, descr, prototypeObj))
         return nullptr;
 
     if (!CreateTraceList(cx, descr))
         return nullptr;
 
-    if (!cx->zone()->typeDescrObjects.put(descr) ||
-        !cx->zone()->typeDescrObjects.put(fieldTypeVec))
+    if (!cx->zone()->addTypeDescrObject(cx, descr) ||
+        !cx->zone()->addTypeDescrObject(cx, fieldTypeVec))
     {
         ReportOutOfMemory(cx);
         return nullptr;
     }
 
     return descr;
 }
 
@@ -1160,20 +1160,18 @@ DefineSimpleTypeDescr(JSContext* cx,
 
     RootedValue descrValue(cx, ObjectValue(*descr));
     if (!DefineProperty(cx, module, className, descrValue, nullptr, nullptr, 0))
         return false;
 
     if (!CreateTraceList(cx, descr))
         return false;
 
-    if (!cx->zone()->typeDescrObjects.put(descr)) {
-        ReportOutOfMemory(cx);
+    if (!cx->zone()->addTypeDescrObject(cx, descr))
         return false;
-    }
 
     return true;
 }
 
 ///////////////////////////////////////////////////////////////////////////
 
 template<typename T>
 static JSObject*
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -473,16 +473,17 @@ void
 js::gc::GCRuntime::bufferGrayRoots()
 {
     // Precondition: the state has been reset to "unused" after the last GC
     //               and the zone's buffers have been cleared.
     MOZ_ASSERT(grayBufferState == GrayBufferState::Unused);
     for (GCZonesIter zone(rt); !zone.done(); zone.next())
         MOZ_ASSERT(zone->gcGrayRoots.empty());
 
+    gcstats::AutoPhase ap(stats, gcstats::PHASE_BUFFER_GRAY_ROOTS);
 
     BufferGrayRootsTracer grayBufferer(rt);
     if (JSTraceDataOp op = grayRootTracer.op)
         (*op)(&grayBufferer, grayRootTracer.data);
 
     // Propagate the failure flag from the marker to the runtime.
     if (grayBufferer.failed()) {
       grayBufferState = GrayBufferState::Failed;
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -364,16 +364,31 @@ Zone::clearTables()
 }
 
 void
 Zone::fixupAfterMovingGC()
 {
     fixupInitialShapeTable();
 }
 
+bool
+Zone::addTypeDescrObject(JSContext* cx, HandleObject obj)
+{
+    // Type descriptor objects are always tenured so we don't need post barriers
+    // on the set.
+    MOZ_ASSERT(!IsInsideNursery(obj));
+
+    if (!typeDescrObjects.put(obj)) {
+        ReportOutOfMemory(cx);
+        return false;
+    }
+
+    return true;
+}
+
 ZoneList::ZoneList()
   : head(nullptr), tail(nullptr)
 {}
 
 ZoneList::ZoneList(Zone* zone)
   : head(zone), tail(zone)
 {
     MOZ_RELEASE_ASSERT(!zone->isOnList());
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -350,21 +350,26 @@ struct Zone : public JS::shadow::Zone,
     //
     // This is used during GC while calculating zone groups to record edges that
     // can't be determined by examining this zone by itself.
     ZoneSet gcZoneGroupEdges;
 
     // Keep track of all TypeDescr and related objects in this compartment.
     // This is used by the GC to trace them all first when compacting, since the
     // TypedObject trace hook may access these objects.
-    using TypeDescrObjectSet = js::GCHashSet<js::HeapPtr<JSObject*>,
-                                             js::MovableCellHasher<js::HeapPtr<JSObject*>>,
+    //
+    // There are no barriers here - the set contains only tenured objects so no
+    // post-barrier is required, and these are weak references so no pre-barrier
+    // is required.
+    using TypeDescrObjectSet = js::GCHashSet<JSObject*,
+                                             js::MovableCellHasher<JSObject*>,
                                              js::SystemAllocPolicy>;
     JS::WeakCache<TypeDescrObjectSet> typeDescrObjects;
 
+    bool addTypeDescrObject(JSContext* cx, HandleObject obj);
 
     // Malloc counter to measure memory pressure for GC scheduling. It runs from
     // gcMaxMallocBytes down to zero. This counter should be used only when it's
     // not possible to know the size of a free.
     mozilla::Atomic<ptrdiff_t, mozilla::ReleaseAcquire> gcMallocBytes;
 
     // GC trigger threshold for allocations on the C heap.
     size_t gcMaxMallocBytes;
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -3243,30 +3243,36 @@ JS_NewArrayObject(JSContext* cx, size_t 
 {
     MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
 
     return NewDenseFullyAllocatedArray(cx, length);
 }
 
-JS_PUBLIC_API(bool)
-JS_IsArrayObject(JSContext* cx, JS::HandleObject obj, bool* isArray)
+inline bool
+IsGivenTypeObject(JSContext* cx, JS::HandleObject obj, const ESClass& typeClass, bool* isType)
 {
     assertSameCompartment(cx, obj);
 
     ESClass cls;
     if (!GetBuiltinClass(cx, obj, &cls))
         return false;
 
-    *isArray = cls == ESClass::Array;
+    *isType = cls == typeClass;
     return true;
 }
 
 JS_PUBLIC_API(bool)
+JS_IsArrayObject(JSContext* cx, JS::HandleObject obj, bool* isArray)
+{
+    return IsGivenTypeObject(cx, obj, ESClass::Array, isArray);
+}
+
+JS_PUBLIC_API(bool)
 JS_IsArrayObject(JSContext* cx, JS::HandleValue value, bool* isArray)
 {
     if (!value.isObject()) {
         *isArray = false;
         return true;
     }
 
     RootedObject obj(cx, &value.toObject());
@@ -3286,16 +3292,28 @@ JS_PUBLIC_API(bool)
 JS_SetArrayLength(JSContext* cx, HandleObject obj, uint32_t length)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
     return SetLengthProperty(cx, obj, length);
 }
 
+JS_PUBLIC_API(bool)
+JS::IsMapObject(JSContext* cx, JS::HandleObject obj, bool* isMap)
+{
+    return IsGivenTypeObject(cx, obj, ESClass::Map, isMap);
+}
+
+JS_PUBLIC_API(bool)
+JS::IsSetObject(JSContext* cx, JS::HandleObject obj, bool* isSet)
+{
+    return IsGivenTypeObject(cx, obj, ESClass::Set, isSet);
+}
+
 JS_PUBLIC_API(void)
 JS_HoldPrincipals(JSPrincipals* principals)
 {
     ++principals->refcount;
 }
 
 JS_PUBLIC_API(void)
 JS_DropPrincipals(JSContext* cx, JSPrincipals* principals)
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -948,16 +948,23 @@ extern JS_PUBLIC_API(JSString*)
 JS_ValueToSource(JSContext* cx, JS::Handle<JS::Value> v);
 
 extern JS_PUBLIC_API(bool)
 JS_DoubleIsInt32(double d, int32_t* ip);
 
 extern JS_PUBLIC_API(JSType)
 JS_TypeOfValue(JSContext* cx, JS::Handle<JS::Value> v);
 
+namespace JS {
+
+extern JS_PUBLIC_API(const char*)
+InformalValueTypeName(const JS::Value& v);
+
+} /* namespace JS */
+
 extern JS_PUBLIC_API(bool)
 JS_StrictlyEqual(JSContext* cx, JS::Handle<JS::Value> v1, JS::Handle<JS::Value> v2, bool* equal);
 
 extern JS_PUBLIC_API(bool)
 JS_LooselyEqual(JSContext* cx, JS::Handle<JS::Value> v1, JS::Handle<JS::Value> v2, bool* equal);
 
 extern JS_PUBLIC_API(bool)
 JS_SameValue(JSContext* cx, JS::Handle<JS::Value> v1, JS::Handle<JS::Value> v2, bool* same);
@@ -3433,16 +3440,40 @@ extern JS_PUBLIC_API(bool)
 JS_IsArrayObject(JSContext* cx, JS::HandleObject obj, bool* isArray);
 
 extern JS_PUBLIC_API(bool)
 JS_GetArrayLength(JSContext* cx, JS::Handle<JSObject*> obj, uint32_t* lengthp);
 
 extern JS_PUBLIC_API(bool)
 JS_SetArrayLength(JSContext* cx, JS::Handle<JSObject*> obj, uint32_t length);
 
+namespace JS {
+
+/**
+ * Returns true and sets |*isMap| indicating whether |obj| is an Map object
+ * or a wrapper around one, otherwise returns false on failure.
+ *
+ * This method returns true with |*isMap == false| when passed a proxy whose
+ * target is an Map, or when passed a revoked proxy.
+ */
+extern JS_PUBLIC_API(bool)
+IsMapObject(JSContext* cx, JS::HandleObject obj, bool* isMap);
+
+/**
+ * Returns true and sets |*isSet| indicating whether |obj| is an Set object
+ * or a wrapper around one, otherwise returns false on failure.
+ *
+ * This method returns true with |*isSet == false| when passed a proxy whose
+ * target is an Set, or when passed a revoked proxy.
+ */
+extern JS_PUBLIC_API(bool)
+IsSetObject(JSContext* cx, JS::HandleObject obj, bool* isSet);
+
+} /* namespace JS */
+
 /**
  * Assign 'undefined' to all of the object's non-reserved slots. Note: this is
  * done for all slots, regardless of the associated property descriptor.
  */
 JS_PUBLIC_API(void)
 JS_SetAllNonReservedSlotsToUndefined(JSContext* cx, JSObject* objArg);
 
 /**
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -2401,17 +2401,17 @@ ForegroundUpdateKinds(AllocKinds kinds)
     return result;
 }
 
 void
 GCRuntime::updateTypeDescrObjects(MovingTracer* trc, Zone* zone)
 {
     zone->typeDescrObjects.sweep();
     for (auto r = zone->typeDescrObjects.all(); !r.empty(); r.popFront())
-        UpdateCellPointers(trc, r.front().get());
+        UpdateCellPointers(trc, r.front());
 }
 
 void
 GCRuntime::updateCellPointers(MovingTracer* trc, Zone* zone, AllocKinds kinds, size_t bgTaskCount)
 {
     AllocKinds fgKinds = bgTaskCount == 0 ? kinds : ForegroundUpdateKinds(kinds);
     AllocKinds bgKinds = kinds - fgKinds;
 
@@ -3801,17 +3801,17 @@ GCRuntime::beginMarkPhase(JS::gcreason::
         }
 
         zone->setPreservingCode(false);
     }
 
     for (CompartmentsIter c(rt, WithAtoms); !c.done(); c.next()) {
         c->marked = false;
         c->scheduledForDestruction = false;
-        c->maybeAlive = false;
+        c->maybeAlive = c->hasBeenEntered() || !c->zone()->isGCScheduled();
         if (shouldPreserveJITCode(c, currentTime, reason))
             c->zone()->setPreservingCode(true);
     }
 
     if (!rt->gc.cleanUpEverything) {
         if (JSCompartment* comp = jit::TopmostIonActivationCompartment(rt))
             comp->zone()->setPreservingCode(true);
     }
@@ -3925,37 +3925,41 @@ GCRuntime::beginMarkPhase(JS::gcreason::
         }
     }
 
     traceRuntimeForMajorGC(gcmarker, lock);
 
     gcstats::AutoPhase ap2(stats, gcstats::PHASE_MARK_ROOTS);
 
     if (isIncremental) {
-        gcstats::AutoPhase ap3(stats, gcstats::PHASE_BUFFER_GRAY_ROOTS);
         bufferGrayRoots();
-    }
-
-    markCompartments();
+        markCompartments();
+    }
 
     return true;
 }
 
 void
 GCRuntime::markCompartments()
 {
     gcstats::AutoPhase ap(stats, gcstats::PHASE_MARK_COMPARTMENTS);
 
     /*
      * This code ensures that if a compartment is "dead", then it will be
      * collected in this GC. A compartment is considered dead if its maybeAlive
      * flag is false. The maybeAlive flag is set if:
-     *   (1) the compartment has incoming cross-compartment edges, or
-     *   (2) an object in the compartment was marked during root marking, either
-     *       as a black root or a gray root.
+     *
+     *   (1) the compartment has been entered (set in beginMarkPhase() above)
+     *   (2) the compartment is not being collected (set in beginMarkPhase()
+     *       above)
+     *   (3) an object in the compartment was marked during root marking, either
+     *       as a black root or a gray root (set in RootMarking.cpp), or
+     *   (4) the compartment has incoming cross-compartment edges from another
+     *       compartment that has maybeAlive set (set by this method).
+     *
      * If the maybeAlive is false, then we set the scheduledForDestruction flag.
      * At the end of the GC, we look for compartments where
      * scheduledForDestruction is true. These are compartments that were somehow
      * "revived" during the incremental GC. If any are found, we do a special,
      * non-incremental GC of those compartments to try to collect them.
      *
      * Compartments can be revived for a variety of reasons. On reason is bug
      * 811587, where a reflector that was dead can be revived by DOM code that
@@ -3963,36 +3967,47 @@ GCRuntime::markCompartments()
      *
      * Read barriers and allocations can also cause revival. This might happen
      * during a function like JS_TransplantObject, which iterates over all
      * compartments, live or dead, and operates on their objects. See bug 803376
      * for details on this problem. To avoid the problem, we try to avoid
      * allocation and read barriers during JS_TransplantObject and the like.
      */
 
-    /* Set the maybeAlive flag based on cross-compartment edges. */
-    for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
-        for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) {
+    /* Propagate the maybeAlive flag via cross-compartment edges. */
+
+    Vector<JSCompartment*, 0, js::SystemAllocPolicy> workList;
+
+    for (CompartmentsIter comp(rt, SkipAtoms); !comp.done(); comp.next()) {
+        if (comp->maybeAlive) {
+            if (!workList.append(comp))
+                return;
+        }
+    }
+
+    while (!workList.empty()) {
+        JSCompartment* comp = workList.popCopy();
+        for (JSCompartment::WrapperEnum e(comp); !e.empty(); e.popFront()) {
             if (e.front().key().is<JSString*>())
                 continue;
             JSCompartment* dest = e.front().mutableKey().compartment();
-            if (dest)
+            if (dest && !dest->maybeAlive) {
                 dest->maybeAlive = true;
+                if (!workList.append(dest))
+                    return;
+            }
         }
     }
 
-    /*
-     * For black roots, code in gc/Marking.cpp will already have set maybeAlive
-     * during MarkRuntime.
-     */
-
-    /* Propogate maybeAlive to scheduleForDestruction. */
-    for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
-        if (!c->maybeAlive && !rt->isAtomsCompartment(c))
-            c->scheduledForDestruction = true;
+    /* Set scheduleForDestruction based on maybeAlive. */
+
+    for (GCCompartmentsIter comp(rt); !comp.done(); comp.next()) {
+        MOZ_ASSERT(!comp->scheduledForDestruction);
+        if (!comp->maybeAlive && !rt->isAtomsCompartment(comp))
+            comp->scheduledForDestruction = true;
     }
 }
 
 template <class ZoneIterT>
 void
 GCRuntime::markWeakReferences(gcstats::Phase phase)
 {
     MOZ_ASSERT(marker.isDrained());
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -82,18 +82,18 @@ js::ReportNotObject(JSContext* cx, const
 
     RootedValue value(cx, v);
     UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, value, nullptr);
     if (bytes)
         JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT,
                                    bytes.get());
 }
 
-const char*
-js::InformalValueTypeName(const Value& v)
+JS_PUBLIC_API(const char*)
+JS::InformalValueTypeName(const Value& v)
 {
     if (v.isObject())
         return v.toObject().getClass()->name;
     if (v.isString())
         return "string";
     if (v.isSymbol())
         return "symbol";
     if (v.isNumber())
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -1331,18 +1331,16 @@ inline JSObject*
 NonNullObject(JSContext* cx, const Value& v)
 {
     if (v.isObject())
         return &v.toObject();
     ReportNotObject(cx, v);
     return nullptr;
 }
 
-extern const char*
-InformalValueTypeName(const Value& v);
 
 extern bool
 GetFirstArgumentAsObject(JSContext* cx, const CallArgs& args, const char* method,
                          MutableHandleObject objp);
 
 /* Helpers for throwing. These always return false. */
 extern bool
 Throw(JSContext* cx, jsid id, unsigned errorNumber);
--- a/js/src/proxy/CrossCompartmentWrapper.cpp
+++ b/js/src/proxy/CrossCompartmentWrapper.cpp
@@ -472,17 +472,17 @@ js::IsCrossCompartmentWrapper(JSObject* 
 
 void
 js::NukeCrossCompartmentWrapper(JSContext* cx, JSObject* wrapper)
 {
     MOZ_ASSERT(wrapper->is<CrossCompartmentWrapperObject>());
 
     NotifyGCNukeWrapper(wrapper);
 
-    wrapper->as<ProxyObject>().nuke(&DeadObjectProxy::singleton);
+    wrapper->as<ProxyObject>().nuke();
 
     MOZ_ASSERT(IsDeadProxyObject(wrapper));
 }
 
 /*
  * NukeChromeCrossCompartmentWrappersForGlobal reaches into chrome and cuts
  * all of the cross-compartment wrappers that point to objects parented to
  * obj's global.  The snag here is that we need to avoid cutting wrappers that
--- a/js/src/vm/ProxyObject.cpp
+++ b/js/src/vm/ProxyObject.cpp
@@ -2,16 +2,19 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "vm/ProxyObject.h"
 
 #include "jscompartment.h"
+
+#include "proxy/DeadObjectProxy.h"
+
 #include "jsobjinlines.h"
 
 using namespace js;
 
 /* static */ ProxyObject*
 ProxyObject::New(JSContext* cx, const BaseProxyHandler* handler, HandleValue priv, TaggedProto proto_,
                  const ProxyOptions& options)
 {
@@ -107,24 +110,30 @@ ProxyObject::setCrossCompartmentPrivate(
 void
 ProxyObject::setSameCompartmentPrivate(const Value& priv)
 {
     MOZ_ASSERT(IsObjectValueInCompartment(priv, compartment()));
     *slotOfPrivate() = priv;
 }
 
 void
-ProxyObject::nuke(const BaseProxyHandler* handler)
+ProxyObject::nuke()
 {
+    // Clear the target reference.
     setSameCompartmentPrivate(NullValue());
-    for (size_t i = 0; i < detail::PROXY_EXTRA_SLOTS; i++)
-        SetProxyExtra(this, i, NullValue());
+
+    // Update the handler to make this a DeadObjectProxy.
+    setHandler(&DeadObjectProxy::singleton);
 
-    /* Restore the handler as requested after nuking. */
-    setHandler(handler);
+    // The proxy's extra slots are not cleared and will continue to be
+    // traced. This avoids the possibility of triggering write barriers while
+    // nuking proxies in dead compartments which could otherwise cause those
+    // compartments to be kept alive. Note that these are slots cannot hold
+    // cross compartment pointers, so this cannot cause the target compartment
+    // to leak.
 }
 
 JS_FRIEND_API(void)
 js::SetValueInProxy(Value* slot, const Value& value)
 {
     // Slots in proxies are not GCPtrValues, so do a cast whenever assigning
     // values to them which might trigger a barrier.
     *reinterpret_cast<GCPtrValue*>(slot) = value;
--- a/js/src/vm/ProxyObject.h
+++ b/js/src/vm/ProxyObject.h
@@ -103,17 +103,17 @@ class ProxyObject : public ShapedObject
 
   public:
     static unsigned grayLinkExtraSlot(JSObject* obj);
 
     void renew(JSContext* cx, const BaseProxyHandler* handler, const Value& priv);
 
     static void trace(JSTracer* trc, JSObject* obj);
 
-    void nuke(const BaseProxyHandler* handler);
+    void nuke();
 
     // There is no class_ member to force specialization of JSObject::is<T>().
     // The implementation in JSObject is incorrect for proxies since it doesn't
     // take account of the handler type.
     static const Class proxyClass;
 };
 
 inline bool
--- a/layout/inspector/inDOMUtils.cpp
+++ b/layout/inspector/inDOMUtils.cpp
@@ -328,17 +328,17 @@ inDOMUtils::GetRelativeRuleLine(nsIDOMCS
   NS_ENSURE_ARG_POINTER(aRule);
 
   Rule* rule = aRule->GetCSSRule();
   if (!rule) {
     return NS_ERROR_FAILURE;
   }
 
   uint32_t lineNumber = rule->GetLineNumber();
-  CSSStyleSheet* sheet = rule->GetStyleSheet();
+  StyleSheet* sheet = rule->GetStyleSheet();
   if (sheet && lineNumber != 0) {
     nsINode* owningNode = sheet->GetOwnerNode();
     if (owningNode) {
       nsCOMPtr<nsIStyleSheetLinkingElement> link =
         do_QueryInterface(owningNode);
       if (link) {
         lineNumber -= link->GetLineNumber() - 1;
       }
--- a/layout/style/CSSRuleList.h
+++ b/layout/style/CSSRuleList.h
@@ -23,17 +23,17 @@ class CSSRuleList : public nsIDOMCSSRule
                   , public nsWrapperCache
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_ICSSRULELIST_IID)
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CSSRuleList)
 
-  virtual CSSStyleSheet* GetParentObject() = 0;
+  virtual StyleSheet* GetParentObject() = 0;
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override final;
 
   NS_IMETHOD
   GetLength(uint32_t* aLength) override final
   {
     *aLength = Length();
     return NS_OK;
   }
--- a/layout/style/CSSStyleSheet.cpp
+++ b/layout/style/CSSStyleSheet.cpp
@@ -1381,19 +1381,20 @@ CSSStyleSheet::FindOwningWindowInnerID()
     windowID = mDocument->InnerWindowID();
   }
 
   if (windowID == 0 && mOwningNode) {
     windowID = mOwningNode->OwnerDoc()->InnerWindowID();
   }
 
   if (windowID == 0 && mOwnerRule) {
-    RefPtr<CSSStyleSheet> sheet = static_cast<css::Rule*>(mOwnerRule)->GetStyleSheet();
+    RefPtr<StyleSheet> sheet =
+      static_cast<css::Rule*>(mOwnerRule)->GetStyleSheet();
     if (sheet) {
-      windowID = sheet->FindOwningWindowInnerID();
+      windowID = sheet->AsGecko()->FindOwningWindowInnerID();
     }
   }
 
   if (windowID == 0 && mParent) {
     windowID = mParent->FindOwningWindowInnerID();
   }
 
   return windowID;
--- a/layout/style/FontFaceSet.cpp
+++ b/layout/style/FontFaceSet.cpp
@@ -1259,17 +1259,17 @@ FontFaceSet::LogMessage(gfxUserFontEntry
   nsString href;
   nsString text;
   nsresult rv;
   uint32_t line = 0;
   uint32_t column = 0;
   if (rule) {
     rv = rule->GetCssText(text);
     NS_ENSURE_SUCCESS(rv, rv);
-    CSSStyleSheet* sheet = rule->GetStyleSheet();
+    StyleSheet* sheet = rule->GetStyleSheet();
     // if the style sheet is removed while the font is loading can be null
     if (sheet) {
       nsCString spec = sheet->GetSheetURI()->GetSpecOrDefault();
       CopyUTF8toUTF16(spec, href);
     } else {
       NS_WARNING("null parent stylesheet for @font-face rule");
       href.AssignLiteral("unknown");
     }
--- a/layout/style/GroupRule.h
+++ b/layout/style/GroupRule.h
@@ -17,17 +17,17 @@
 #include "mozilla/css/Rule.h"
 #include "nsCycleCollectionParticipant.h"
 
 class nsPresContext;
 class nsMediaQueryResultCacheKey;
 
 namespace mozilla {
 
-class CSSStyleSheet;
+class StyleSheet;
 
 namespace css {
 
 class GroupRuleRuleList;
 
 // inherits from Rule so it can be shared between
 // MediaRule and DocumentRule
 class GroupRule : public Rule
@@ -41,17 +41,17 @@ public:
   NS_DECL_CYCLE_COLLECTION_CLASS(GroupRule)
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
 
   // implement part of Rule
   DECL_STYLE_RULE_INHERIT_NO_DOMRULE
 #ifdef DEBUG
   virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
 #endif
-  virtual void SetStyleSheet(CSSStyleSheet* aSheet) override;
+  virtual void SetStyleSheet(StyleSheet* aSheet) override;
 
 public:
   void AppendStyleRule(Rule* aRule);
 
   int32_t StyleRuleCount() const { return mRules.Count(); }
   Rule* GetStyleRuleAt(int32_t aIndex) const;
 
   typedef IncrementalClearCOMRuleArray::nsCOMArrayEnumFunc RuleEnumFunc;
--- a/layout/style/Rule.h
+++ b/layout/style/Rule.h
@@ -75,26 +75,26 @@ public:
     DOCUMENT_RULE,
     SUPPORTS_RULE,
     FONT_FEATURE_VALUES_RULE,
     COUNTER_STYLE_RULE
   };
 
   virtual int32_t GetType() const = 0;
 
-  CSSStyleSheet* GetStyleSheet() const { return mSheet; }
+  StyleSheet* GetStyleSheet() const { return mSheet; }
 
   // Return the document the rule lives in, if any
   nsIDocument* GetDocument() const
   {
-    CSSStyleSheet* sheet = GetStyleSheet();
+    StyleSheet* sheet = GetStyleSheet();
     return sheet ? sheet->GetDocument() : nullptr;
   }
 
-  virtual void SetStyleSheet(CSSStyleSheet* aSheet);
+  virtual void SetStyleSheet(StyleSheet* aSheet);
 
   void SetParentRule(GroupRule* aRule) {
     // We don't reference count this up reference. The group rule
     // will tell us when it's going away or when we're detached from
     // it.
     mParentRule = aRule;
   }
 
@@ -120,17 +120,17 @@ public:
 
   // This is pure virtual because all of Rule's data members are non-owning and
   // thus measured elsewhere.
   virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
     const MOZ_MUST_OVERRIDE = 0;
 
 protected:
   // This is sometimes null (e.g., for style attributes).
-  CSSStyleSheet*    mSheet;
+  StyleSheet* mSheet;
   // When the parent GroupRule is destroyed, it will call SetParentRule(nullptr)
   // on this object. (Through SetParentRuleReference);
   GroupRule* MOZ_NON_OWNING_REF mParentRule;
 
   // Keep the same type so that MSVC packs them.
   uint32_t          mLineNumber;
   uint32_t          mColumnNumber;
 };
--- a/layout/style/ServoBindingList.h
+++ b/layout/style/ServoBindingList.h
@@ -32,28 +32,56 @@ SERVO_BINDING_FUNC(Servo_StyleSheet_From
                    ThreadSafeURIHolder* referrer,
                    ThreadSafePrincipalHolder* principal)
 SERVO_BINDING_FUNC(Servo_StyleSheet_AddRef, void,
                    RawServoStyleSheetBorrowed sheet)
 SERVO_BINDING_FUNC(Servo_StyleSheet_Release, void,
                    RawServoStyleSheetBorrowed sheet)
 SERVO_BINDING_FUNC(Servo_StyleSheet_HasRules, bool,
                    RawServoStyleSheetBorrowed sheet)
+SERVO_BINDING_FUNC(Servo_StyleSheet_GetRules, ServoCssRulesStrong,
+                   RawServoStyleSheetBorrowed sheet)
 SERVO_BINDING_FUNC(Servo_StyleSet_Init, RawServoStyleSetOwned)
 SERVO_BINDING_FUNC(Servo_StyleSet_Drop, void, RawServoStyleSetOwned set)
 SERVO_BINDING_FUNC(Servo_StyleSet_AppendStyleSheet, void,
                    RawServoStyleSetBorrowed set, RawServoStyleSheetBorrowed sheet)
 SERVO_BINDING_FUNC(Servo_StyleSet_PrependStyleSheet, void,
                    RawServoStyleSetBorrowed set, RawServoStyleSheetBorrowed sheet)
 SERVO_BINDING_FUNC(Servo_StyleSet_RemoveStyleSheet, void,
                    RawServoStyleSetBorrowed set, RawServoStyleSheetBorrowed sheet)
 SERVO_BINDING_FUNC(Servo_StyleSet_InsertStyleSheetBefore, void,
                    RawServoStyleSetBorrowed set, RawServoStyleSheetBorrowed sheet,
                    RawServoStyleSheetBorrowed reference)
 
+// CSSRuleList
+SERVO_BINDING_FUNC(Servo_CssRules_AddRef, void, ServoCssRulesBorrowed rules)
+SERVO_BINDING_FUNC(Servo_CssRules_Release, void, ServoCssRulesBorrowed rules)
+SERVO_BINDING_FUNC(Servo_CssRules_ListTypes, void,
+                   ServoCssRulesBorrowed rules,
+                   nsTArrayBorrowed_uintptr_t result)
+SERVO_BINDING_FUNC(Servo_CssRules_GetStyleRuleAt, RawServoStyleRuleStrong,
+                   ServoCssRulesBorrowed rules, uint32_t index)
+
+// CSS Rules
+SERVO_BINDING_FUNC(Servo_StyleRule_AddRef, void,
+                   RawServoStyleRuleBorrowed rule)
+SERVO_BINDING_FUNC(Servo_StyleRule_Release, void,
+                   RawServoStyleRuleBorrowed rule)
+SERVO_BINDING_FUNC(Servo_StyleRule_Debug, void,
+                   RawServoStyleRuleBorrowed rule, nsACString* result)
+SERVO_BINDING_FUNC(Servo_StyleRule_GetStyle, RawServoDeclarationBlockStrong,
+                   RawServoStyleRuleBorrowed rule)
+SERVO_BINDING_FUNC(Servo_StyleRule_SetStyle, void,
+                   RawServoStyleRuleBorrowed rule,
+                   RawServoDeclarationBlockBorrowed declarations)
+SERVO_BINDING_FUNC(Servo_StyleRule_GetCssText, void,
+                   RawServoStyleRuleBorrowed rule, nsAString* result)
+SERVO_BINDING_FUNC(Servo_StyleRule_GetSelectorText, void,
+                   RawServoStyleRuleBorrowed rule, nsAString* result)
+
 // Animations API
 SERVO_BINDING_FUNC(Servo_ParseProperty,
                    RawServoDeclarationBlockStrong,
                    const nsACString* property, const nsACString* value,
                    const nsACString* base_url, ThreadSafeURIHolder* base,
                    ThreadSafeURIHolder* referrer,
                    ThreadSafePrincipalHolder* principal)
 SERVO_BINDING_FUNC(Servo_RestyleWithAddedDeclaration,
--- a/layout/style/ServoBindingTypes.h
+++ b/layout/style/ServoBindingTypes.h
@@ -6,19 +6,21 @@
 
 #ifndef mozilla_ServoBindingTypes_h
 #define mozilla_ServoBindingTypes_h
 
 #include "mozilla/RefPtr.h"
 #include "mozilla/UniquePtr.h"
 
 struct ServoComputedValues;
+struct ServoCssRules;
 struct RawServoStyleSheet;
 struct RawServoStyleSet;
 struct RawServoDeclarationBlock;
+struct RawServoStyleRule;
 
 namespace mozilla {
 namespace dom {
 class Element;
 class StyleChildrenIterator;
 } // namespace dom
 } // namespace mozilla
 
@@ -67,18 +69,20 @@ typedef nsIDocument RawGeckoDocument;
   DECL_BORROWED_MUT_REF_TYPE_FOR(type_)
 
 #define DECL_NULLABLE_OWNED_REF_TYPE_FOR(type_)    \
   typedef type_* type_##OwnedOrNull;               \
   DECL_NULLABLE_BORROWED_REF_TYPE_FOR(type_)       \
   DECL_NULLABLE_BORROWED_MUT_REF_TYPE_FOR(type_)
 
 DECL_ARC_REF_TYPE_FOR(ServoComputedValues)
+DECL_ARC_REF_TYPE_FOR(ServoCssRules)
 DECL_ARC_REF_TYPE_FOR(RawServoStyleSheet)
 DECL_ARC_REF_TYPE_FOR(RawServoDeclarationBlock)
+DECL_ARC_REF_TYPE_FOR(RawServoStyleRule)
 // This is a reference to a reference of RawServoDeclarationBlock, which
 // corresponds to Option<&Arc<RawServoDeclarationBlock>> in Servo side.
 DECL_NULLABLE_BORROWED_REF_TYPE_FOR(RawServoDeclarationBlockStrong)
 
 DECL_OWNED_REF_TYPE_FOR(RawServoStyleSet)
 DECL_NULLABLE_OWNED_REF_TYPE_FOR(StyleChildrenIterator)
 DECL_OWNED_REF_TYPE_FOR(StyleChildrenIterator)
 
@@ -116,19 +120,21 @@ DECL_BORROWED_MUT_REF_TYPE_FOR(nsCSSValu
       Servo_##name_##_AddRef(aPtr);                  \
     }                                                \
     static void Release(type_* aPtr) {               \
       Servo_##name_##_Release(aPtr);                 \
     }                                                \
   };                                                 \
   }
 
+DEFINE_REFPTR_TRAITS(CssRules, ServoCssRules)
 DEFINE_REFPTR_TRAITS(StyleSheet, RawServoStyleSheet)
 DEFINE_REFPTR_TRAITS(ComputedValues, ServoComputedValues)
 DEFINE_REFPTR_TRAITS(DeclarationBlock, RawServoDeclarationBlock)
+DEFINE_REFPTR_TRAITS(StyleRule, RawServoStyleRule)
 
 #undef DEFINE_REFPTR_TRAITS
 
 extern "C" void Servo_StyleSet_Drop(RawServoStyleSetOwned ptr);
 
 namespace mozilla {
 template<>
 class DefaultDelete<RawServoStyleSet>
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -40,18 +40,20 @@ using namespace mozilla::dom;
   already_AddRefed<type_>               \
   type_##Strong::Consume() {            \
     RefPtr<type_> result;               \
     result.swap(mPtr);                  \
     return result.forget();             \
   }
 
 IMPL_STRONG_REF_TYPE_FOR(ServoComputedValues)
+IMPL_STRONG_REF_TYPE_FOR(ServoCssRules)
 IMPL_STRONG_REF_TYPE_FOR(RawServoStyleSheet)
 IMPL_STRONG_REF_TYPE_FOR(RawServoDeclarationBlock)
+IMPL_STRONG_REF_TYPE_FOR(RawServoStyleRule)
 
 #undef IMPL_STRONG_REF_TYPE_FOR
 
 uint32_t
 Gecko_ChildrenCount(RawGeckoNodeBorrowed aNode)
 {
   return aNode->GetChildCount();
 }
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -59,16 +59,26 @@ struct nsStyleDisplay;
   void Gecko_AddRef##name_##ArbitraryThread(ThreadSafe##name_##Holder* aPtr); \
   void Gecko_Release##name_##ArbitraryThread(ThreadSafe##name_##Holder* aPtr);
 #define NS_IMPL_HOLDER_FFI_REFCOUNTING(class_, name_)                         \
   void Gecko_AddRef##name_##ArbitraryThread(ThreadSafe##name_##Holder* aPtr)  \
   { NS_ADDREF(aPtr); }                                                        \
   void Gecko_Release##name_##ArbitraryThread(ThreadSafe##name_##Holder* aPtr) \
   { NS_RELEASE(aPtr); }                                                       \
 
+
+#define DEFINE_ARRAY_TYPE_FOR(type_)                                \
+  struct nsTArrayBorrowed_##type_ {                                 \
+    nsTArray<type_>* mArray;                                        \
+    MOZ_IMPLICIT nsTArrayBorrowed_##type_(nsTArray<type_>* aArray)  \
+      : mArray(aArray) {}                                           \
+  }
+DEFINE_ARRAY_TYPE_FOR(uintptr_t);
+#undef DEFINE_ARRAY_TYPE_FOR
+
 extern "C" {
 
 // Object refcounting.
 NS_DECL_HOLDER_FFI_REFCOUNTING(nsIPrincipal, Principal)
 NS_DECL_HOLDER_FFI_REFCOUNTING(nsIURI, URI)
 
 // DOM Traversal.
 uint32_t Gecko_ChildrenCount(RawGeckoNodeBorrowed node);
new file mode 100644
--- /dev/null
+++ b/layout/style/ServoCSSRuleList.cpp
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* representation of CSSRuleList for stylo */
+
+#include "mozilla/ServoCSSRuleList.h"
+
+#include "mozilla/ServoBindings.h"
+#include "mozilla/ServoStyleRule.h"
+
+namespace mozilla {
+
+ServoCSSRuleList::ServoCSSRuleList(ServoStyleSheet* aStyleSheet,
+                                   already_AddRefed<ServoCssRules> aRawRules)
+  : mStyleSheet(aStyleSheet)
+  , mRawRules(aRawRules)
+{
+  Servo_CssRules_ListTypes(mRawRules, &mRules);
+  // XXX We may want to eagerly create object for import rule, so that
+  //     we don't lose the reference to child stylesheet when our own
+  //     stylesheet goes away.
+}
+
+nsIDOMCSSRule*
+ServoCSSRuleList::IndexedGetter(uint32_t aIndex, bool& aFound)
+{
+  if (aIndex >= mRules.Length()) {
+    aFound = false;
+    return nullptr;
+  }
+  aFound = true;
+  uintptr_t rule = mRules[aIndex];
+  if (rule <= kMaxRuleType) {
+    RefPtr<css::Rule> ruleObj = nullptr;
+    switch (rule) {
+      case nsIDOMCSSRule::STYLE_RULE: {
+        ruleObj = new ServoStyleRule(
+          Servo_CssRules_GetStyleRuleAt(mRawRules, aIndex).Consume());
+        break;
+      }
+      case nsIDOMCSSRule::MEDIA_RULE:
+      case nsIDOMCSSRule::FONT_FACE_RULE:
+      case nsIDOMCSSRule::KEYFRAMES_RULE:
+      case nsIDOMCSSRule::NAMESPACE_RULE:
+        // XXX create corresponding rules
+      default:
+        MOZ_CRASH("stylo: not implemented yet");
+    }
+    ruleObj->SetStyleSheet(mStyleSheet);
+    rule = CastToUint(ruleObj.forget().take());
+    mRules[aIndex] = rule;
+  }
+  return CastToPtr(rule)->GetDOMRule();
+}
+
+template<typename Func>
+void
+ServoCSSRuleList::EnumerateInstantiatedRules(Func aCallback)
+{
+  for (uintptr_t rule : mRules) {
+    if (rule > kMaxRuleType) {
+      aCallback(CastToPtr(rule));
+    }
+  }
+}
+
+void
+ServoCSSRuleList::DropReference()
+{
+  mStyleSheet = nullptr;
+  EnumerateInstantiatedRules([](css::Rule* rule) {
+    rule->SetStyleSheet(nullptr);
+  });
+}
+
+ServoCSSRuleList::~ServoCSSRuleList()
+{
+  EnumerateInstantiatedRules([](css::Rule* rule) { rule->Release(); });
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/layout/style/ServoCSSRuleList.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* representation of CSSRuleList for stylo */
+
+#ifndef mozilla_ServoCSSRuleList_h
+#define mozilla_ServoCSSRuleList_h
+
+#include "mozilla/ServoBindingTypes.h"
+#include "mozilla/dom/CSSRuleList.h"
+
+namespace mozilla {
+
+class ServoStyleSheet;
+namespace css {
+class Rule;
+} // namespace css
+
+class ServoCSSRuleList final : public CSSRuleList
+{
+public:
+  ServoCSSRuleList(ServoStyleSheet* aStyleSheet,
+                   already_AddRefed<ServoCssRules> aRawRules);
+
+  ServoStyleSheet* GetParentObject() final { return mStyleSheet; }
+
+  nsIDOMCSSRule* IndexedGetter(uint32_t aIndex, bool& aFound) final;
+  uint32_t Length() final { return mRules.Length(); }
+
+  void DropReference();
+
+private:
+  virtual ~ServoCSSRuleList();
+
+  // XXX Is it possible to have an address lower than or equal to 255?
+  //     Is it possible to have more than 255 CSS rule types?
+  static const uintptr_t kMaxRuleType = UINT8_MAX;
+
+  static uintptr_t CastToUint(css::Rule* aPtr) {
+    return reinterpret_cast<uintptr_t>(aPtr);
+  }
+  static css::Rule* CastToPtr(uintptr_t aInt) {
+    MOZ_ASSERT(aInt > kMaxRuleType);
+    return reinterpret_cast<css::Rule*>(aInt);
+  }
+
+  template<typename Func>
+  void EnumerateInstantiatedRules(Func aCallback);
+
+  // mStyleSheet may be nullptr when it drops the reference to us.
+  ServoStyleSheet* mStyleSheet;
+  RefPtr<ServoCssRules> mRawRules;
+  // Array stores either a number indicating rule type, or a pointer to
+  // css::Rule. If the value is less than kMaxRuleType, the given rule
+  // instance has not been constructed, and the value means the type
+  // of the rule. Otherwise, it is a pointer.
+  nsTArray<uintptr_t> mRules;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_ServoCSSRuleList_h
--- a/layout/style/ServoDeclarationBlock.h
+++ b/layout/style/ServoDeclarationBlock.h
@@ -9,16 +9,20 @@
 #include "mozilla/ServoBindings.h"
 #include "mozilla/DeclarationBlock.h"
 
 namespace mozilla {
 
 class ServoDeclarationBlock final : public DeclarationBlock
 {
 public:
+  explicit ServoDeclarationBlock(
+    already_AddRefed<RawServoDeclarationBlock> aRaw)
+    : DeclarationBlock(StyleBackendType::Servo), mRaw(aRaw) {}
+
   ServoDeclarationBlock()
     : ServoDeclarationBlock(Servo_DeclarationBlock_CreateEmpty().Consume()) {}
 
   ServoDeclarationBlock(const ServoDeclarationBlock& aCopy)
     : DeclarationBlock(aCopy)
     , mRaw(Servo_DeclarationBlock_Clone(aCopy.mRaw).Consume()) {}
 
   NS_INLINE_DECL_REFCOUNTING(ServoDeclarationBlock)
@@ -51,21 +55,16 @@ public:
   void GetAuthoredPropertyValue(const nsAString& aProperty,
                                 nsAString& aValue) const {
     GetPropertyValue(aProperty, aValue);
   }
   bool GetPropertyIsImportant(const nsAString& aProperty) const;
   void RemoveProperty(const nsAString& aProperty);
   void RemovePropertyByID(nsCSSPropertyID aPropID);
 
-protected:
-  explicit ServoDeclarationBlock(
-    already_AddRefed<RawServoDeclarationBlock> aRaw)
-    : DeclarationBlock(StyleBackendType::Servo), mRaw(aRaw) {}
-
 private:
   ~ServoDeclarationBlock() {}
 
   RefPtr<RawServoDeclarationBlock> mRaw;
 };
 
 } // namespace mozilla
 
new file mode 100644
--- /dev/null
+++ b/layout/style/ServoStyleRule.cpp
@@ -0,0 +1,238 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* representation of CSSStyleRule for stylo */
+
+#include "mozilla/ServoStyleRule.h"
+
+#include "mozilla/ServoBindings.h"
+#include "mozilla/ServoDeclarationBlock.h"
+
+#include "nsDOMClassInfoID.h"
+#include "mozAutoDocUpdate.h"
+
+namespace mozilla {
+
+// -- ServoStyleRuleDeclaration ---------------------------------------
+
+ServoStyleRuleDeclaration::ServoStyleRuleDeclaration(
+  already_AddRefed<RawServoDeclarationBlock> aDecls)
+  : mDecls(new ServoDeclarationBlock(Move(aDecls)))
+{
+}
+
+ServoStyleRuleDeclaration::~ServoStyleRuleDeclaration()
+{
+}
+
+// QueryInterface implementation for ServoStyleRuleDeclaration
+NS_INTERFACE_MAP_BEGIN(ServoStyleRuleDeclaration)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  // We forward the cycle collection interfaces to Rule(), which is
+  // never null (in fact, we're part of that object!)
+  if (aIID.Equals(NS_GET_IID(nsCycleCollectionISupports)) ||
+      aIID.Equals(NS_GET_IID(nsXPCOMCycleCollectionParticipant))) {
+    return Rule()->QueryInterface(aIID, aInstancePtr);
+  }
+  else
+NS_IMPL_QUERY_TAIL_INHERITING(nsDOMCSSDeclaration)
+
+NS_IMPL_ADDREF_USING_AGGREGATOR(ServoStyleRuleDeclaration, Rule())
+NS_IMPL_RELEASE_USING_AGGREGATOR(ServoStyleRuleDeclaration, Rule())
+
+/* nsDOMCSSDeclaration implementation */
+
+NS_IMETHODIMP
+ServoStyleRuleDeclaration::GetParentRule(nsIDOMCSSRule** aParent)
+{
+  *aParent = do_AddRef(Rule()).take();
+  return NS_OK;
+}
+
+nsINode*
+ServoStyleRuleDeclaration::GetParentObject()
+{
+  return Rule()->GetDocument();
+}
+
+DeclarationBlock*
+ServoStyleRuleDeclaration::GetCSSDeclaration(Operation aOperation)
+{
+  return mDecls;
+}
+
+nsresult
+ServoStyleRuleDeclaration::SetCSSDeclaration(DeclarationBlock* aDecl)
+{
+  ServoStyleRule* rule = Rule();
+  if (RefPtr<ServoStyleSheet> sheet = rule->GetStyleSheet()->AsServo()) {
+    nsCOMPtr<nsIDocument> owningDoc = sheet->GetOwningDocument();
+    mozAutoDocUpdate updateBatch(owningDoc, UPDATE_STYLE, true);
+    if (aDecl != mDecls) {
+      RefPtr<ServoDeclarationBlock> decls = aDecl->AsServo();
+      Servo_StyleRule_SetStyle(rule->Raw(), decls->Raw());
+      mDecls = decls.forget();
+    }
+    if (owningDoc) {
+      owningDoc->StyleRuleChanged(sheet, rule);
+    }
+  }
+  return NS_OK;
+}
+
+nsIDocument*
+ServoStyleRuleDeclaration::DocToUpdate()
+{
+  return nullptr;
+}
+
+void
+ServoStyleRuleDeclaration::GetCSSParsingEnvironment(
+  CSSParsingEnvironment& aCSSParseEnv)
+{
+  GetCSSParsingEnvironmentForRule(Rule(), aCSSParseEnv);
+}
+
+// -- ServoStyleRule --------------------------------------------------
+
+ServoStyleRule::ServoStyleRule(already_AddRefed<RawServoStyleRule> aRawRule)
+  : css::Rule(0, 0)
+  , mRawRule(aRawRule)
+  , mDecls(Servo_StyleRule_GetStyle(mRawRule).Consume())
+{
+}
+
+// QueryInterface implementation for ServoStyleRule
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServoStyleRule)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMCSSStyleRule)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, css::Rule)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSStyleRule)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(ServoStyleRule)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(ServoStyleRule)
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(ServoStyleRule)
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(ServoStyleRule)
+  // Trace the wrapper for our declaration.  This just expands out
+  // NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER which we can't use
+  // directly because the wrapper is on the declaration, not on us.
+  tmp->mDecls.TraceWrapper(aCallbacks, aClosure);
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ServoStyleRule)
+  // Unlink the wrapper for our declaraton.  This just expands out
+  // NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER which we can't use
+  // directly because the wrapper is on the declaration, not on us.
+  tmp->mDecls.ReleaseWrapper(static_cast<nsISupports*>(p));
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ServoStyleRule)
+  // Just NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS here: that will call
+  // into our Trace hook, where we do the right thing with declarations
+  // already.
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+already_AddRefed<css::Rule>
+ServoStyleRule::Clone() const
+{
+  // Rule::Clone is only used when CSSStyleSheetInner is cloned in
+  // preparation of being mutated. However, ServoStyleSheet never clones
+  // anything, so this method should never be called.
+  MOZ_ASSERT_UNREACHABLE("Shouldn't be cloning ServoStyleRule");
+  return nullptr;
+}
+
+size_t
+ServoStyleRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  // TODO Implement this!
+  return aMallocSizeOf(this);
+}
+
+#ifdef DEBUG
+void
+ServoStyleRule::List(FILE* out, int32_t aIndent) const
+{
+  nsAutoCString str;
+  for (int32_t i = 0; i < aIndent; i++) {
+    str.AppendLiteral("  ");
+  }
+  Servo_StyleRule_Debug(mRawRule, &str);
+  fprintf_stderr(out, "%s\n", str.get());
+}
+#endif
+
+/* CSSRule implementation */
+
+NS_IMETHODIMP
+ServoStyleRule::GetType(uint16_t* aType)
+{
+  *aType = nsIDOMCSSRule::STYLE_RULE;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ServoStyleRule::GetCssText(nsAString& aCssText)
+{
+  Servo_StyleRule_GetCssText(mRawRule, &aCssText);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ServoStyleRule::SetCssText(const nsAString& aCssText)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ServoStyleRule::GetParentStyleSheet(nsIDOMCSSStyleSheet** aSheet)
+{
+  return css::Rule::GetParentStyleSheet(aSheet);
+}
+
+NS_IMETHODIMP
+ServoStyleRule::GetParentRule(nsIDOMCSSRule** aParentRule)
+{
+  *aParentRule = nullptr;
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+css::Rule*
+ServoStyleRule::GetCSSRule()
+{
+  return this;
+}
+
+/* CSSStyleRule implementation */
+
+NS_IMETHODIMP
+ServoStyleRule::GetSelectorText(nsAString& aSelectorText)
+{
+  Servo_StyleRule_GetSelectorText(mRawRule, &aSelectorText);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ServoStyleRule::SetSelectorText(const nsAString& aSelectorText)
+{
+  // XXX We need to implement this... But Gecko doesn't have this either
+  //     so it's probably okay to leave it unimplemented currently?
+  //     See bug 37468 and mozilla::css::StyleRule::SetSelectorText.
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ServoStyleRule::GetStyle(nsIDOMCSSStyleDeclaration** aStyle)
+{
+  *aStyle = do_AddRef(&mDecls).take();
+  return NS_OK;
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/layout/style/ServoStyleRule.h
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* representation of CSSStyleRule for stylo */
+
+#ifndef mozilla_ServoStyleRule_h
+#define mozilla_ServoStyleRule_h
+
+#include "mozilla/css/Rule.h"
+#include "mozilla/ServoBindingTypes.h"
+
+#include "nsIDOMCSSStyleRule.h"
+#include "nsDOMCSSDeclaration.h"
+
+namespace mozilla {
+
+class ServoDeclarationBlock;
+class ServoStyleRule;
+
+class ServoStyleRuleDeclaration final : public nsDOMCSSDeclaration
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+
+  NS_IMETHOD GetParentRule(nsIDOMCSSRule** aParent) final;
+  nsINode* GetParentObject() final;
+
+protected:
+  DeclarationBlock* GetCSSDeclaration(Operation aOperation) final;
+  nsresult SetCSSDeclaration(DeclarationBlock* aDecl) final;
+  nsIDocument* DocToUpdate() final;
+  void GetCSSParsingEnvironment(CSSParsingEnvironment& aCSSParseEnv) final;
+
+private:
+  // For accessing the constructor.
+  friend class ServoStyleRule;
+
+  explicit ServoStyleRuleDeclaration(
+    already_AddRefed<RawServoDeclarationBlock> aDecls);
+  ~ServoStyleRuleDeclaration();
+
+  inline ServoStyleRule* Rule();
+
+  RefPtr<ServoDeclarationBlock> mDecls;
+};
+
+class ServoStyleRule final : public css::Rule
+                           , public nsIDOMCSSStyleRule
+{
+public:
+  explicit ServoStyleRule(already_AddRefed<RawServoStyleRule> aRawRule);
+
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(ServoStyleRule,
+                                                         css::Rule)
+  NS_DECL_NSIDOMCSSRULE
+  NS_DECL_NSIDOMCSSSTYLERULE
+
+  RawServoStyleRule* Raw() const { return mRawRule; }
+
+  // Methods of mozilla::css::Rule
+  int32_t GetType() const final { return css::Rule::STYLE_RULE; }
+  already_AddRefed<Rule> Clone() const final;
+  nsIDOMCSSRule* GetDOMRule() final { return this; }
+  nsIDOMCSSRule* GetExistingDOMRule() final { return this; }
+  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const final;
+#ifdef DEBUG
+  void List(FILE* out = stdout, int32_t aIndent = 0) const final;
+#endif
+
+private:
+  ~ServoStyleRule() {}
+
+  // For computing the offset of mDecls.
+  friend class ServoStyleRuleDeclaration;
+
+  RefPtr<RawServoStyleRule> mRawRule;
+  ServoStyleRuleDeclaration mDecls;
+};
+
+ServoStyleRule*
+ServoStyleRuleDeclaration::Rule()
+{
+  return reinterpret_cast<ServoStyleRule*>(reinterpret_cast<uint8_t*>(this) -
+                                           offsetof(ServoStyleRule, mDecls));
+}
+
+} // namespace mozilla
+
+#endif // mozilla_ServoStyleRule_h
--- a/layout/style/ServoStyleSheet.cpp
+++ b/layout/style/ServoStyleSheet.cpp
@@ -1,31 +1,37 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/ServoStyleSheet.h"
 #include "mozilla/StyleBackendType.h"
+#include "mozilla/ServoBindings.h"
+#include "mozilla/ServoCSSRuleList.h"
+#include "mozilla/dom/CSSRuleList.h"
 
 namespace mozilla {
 
 ServoStyleSheet::ServoStyleSheet(css::SheetParsingMode aParsingMode,
                                  CORSMode aCORSMode,
                                  net::ReferrerPolicy aReferrerPolicy,
                                  const dom::SRIMetadata& aIntegrity)
   : StyleSheet(StyleBackendType::Servo, aParsingMode)
   , mSheetInfo(aCORSMode, aReferrerPolicy, aIntegrity)
 {
 }
 
 ServoStyleSheet::~ServoStyleSheet()
 {
   DropSheet();
+  if (mRuleList) {
+    mRuleList->DropReference();
+  }
 }
 
 bool
 ServoStyleSheet::HasRules() const
 {
   return mSheet && Servo_StyleSheet_HasRules(mSheet);
 }
 
@@ -117,18 +123,21 @@ nsIDOMCSSRule*
 ServoStyleSheet::GetDOMOwnerRule() const
 {
   return nullptr;
 }
 
 CSSRuleList*
 ServoStyleSheet::GetCssRulesInternal(ErrorResult& aRv)
 {
-  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
-  return nullptr;
+  if (!mRuleList) {
+    RefPtr<ServoCssRules> rawRules = Servo_StyleSheet_GetRules(mSheet).Consume();
+    mRuleList = new ServoCSSRuleList(this, rawRules.forget());
+  }
+  return mRuleList;
 }
 
 uint32_t
 ServoStyleSheet::InsertRuleInternal(const nsAString& aRule,
                                     uint32_t aIndex, ErrorResult& aRv)
 {
   aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
   return 0;
--- a/layout/style/ServoStyleSheet.h
+++ b/layout/style/ServoStyleSheet.h
@@ -11,16 +11,18 @@
 #include "mozilla/RefPtr.h"
 #include "mozilla/ServoBindingTypes.h"
 #include "mozilla/StyleSheet.h"
 #include "mozilla/StyleSheetInfo.h"
 #include "nsStringFwd.h"
 
 namespace mozilla {
 
+class ServoCSSRuleList;
+
 /**
  * CSS style sheet object that is a wrapper for a Servo Stylesheet.
  */
 class ServoStyleSheet : public StyleSheet
 {
 public:
   ServoStyleSheet(css::SheetParsingMode aParsingMode,
                   CORSMode aCORSMode,
@@ -75,16 +77,17 @@ protected:
   uint32_t InsertRuleInternal(const nsAString& aRule,
                               uint32_t aIndex, ErrorResult& aRv);
   void DeleteRuleInternal(uint32_t aIndex, ErrorResult& aRv);
 
 private:
   void DropSheet();
 
   RefPtr<RawServoStyleSheet> mSheet;
+  RefPtr<ServoCSSRuleList> mRuleList;
   StyleSheetInfo mSheetInfo;
 
   friend class StyleSheet;
 };
 
 } // namespace mozilla
 
 #endif // mozilla_ServoStyleSheet_h
--- a/layout/style/StyleRule.h
+++ b/layout/style/StyleRule.h
@@ -332,16 +332,22 @@ public:
   // hooks for DOM rule
   void GetCssText(nsAString& aCssText);
   void SetCssText(const nsAString& aCssText);
   void GetSelectorText(nsAString& aSelectorText);
   void SetSelectorText(const nsAString& aSelectorText);
 
   virtual int32_t GetType() const override;
 
+  CSSStyleSheet* GetStyleSheet() const
+  {
+    StyleSheet* sheet = Rule::GetStyleSheet();
+    return sheet ? sheet->AsGecko() : nullptr;
+  }
+
   virtual already_AddRefed<Rule> Clone() const override;
 
   virtual nsIDOMCSSRule* GetDOMRule() override;
 
   virtual nsIDOMCSSRule* GetExistingDOMRule() override;
 
 #ifdef DEBUG
   virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
--- a/layout/style/StyleSheet.cpp
+++ b/layout/style/StyleSheet.cpp
@@ -7,16 +7,18 @@
 #include "mozilla/StyleSheet.h"
 
 #include "mozilla/dom/CSSRuleList.h"
 #include "mozilla/dom/ShadowRoot.h"
 #include "mozilla/ServoStyleSheet.h"
 #include "mozilla/StyleSheetInlines.h"
 #include "mozilla/CSSStyleSheet.h"
 
+#include "mozAutoDocUpdate.h"
+#include "nsIMediaList.h"
 #include "nsNullPrincipal.h"
 
 namespace mozilla {
 
 StyleSheet::StyleSheet(StyleBackendType aType, css::SheetParsingMode aParsingMode)
   : mDocument(nullptr)
   , mOwningNode(nullptr)
   , mParsingMode(aParsingMode)
@@ -318,12 +320,12 @@ StyleSheet::AreRulesAvailable(nsIPrincip
   return true;
 }
 
 // nsWrapperCache
 
 JSObject*
 StyleSheet::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
-  return CSSStyleSheetBinding::Wrap(aCx, this, aGivenProto);
+  return dom::CSSStyleSheetBinding::Wrap(aCx, this, aGivenProto);
 }
 
 } // namespace mozilla
--- a/layout/style/moz.build
+++ b/layout/style/moz.build
@@ -91,18 +91,20 @@ EXPORTS.mozilla += [
     'HandleRefPtr.h',
     'IncrementalClearCOMRuleArray.h',
     'LayerAnimationInfo.h',
     'RuleNodeCacheConditions.h',
     'RuleProcessorCache.h',
     'ServoBindingList.h',
     'ServoBindings.h',
     'ServoBindingTypes.h',
+    'ServoCSSRuleList.h',
     'ServoDeclarationBlock.h',
     'ServoElementSnapshot.h',
+    'ServoStyleRule.h',
     'ServoStyleSet.h',
     'ServoStyleSheet.h',
     'ServoTypes.h',
     'ServoUtils.h',
     'SheetType.h',
     'StyleAnimationValue.h',
     'StyleBackendType.h',
     'StyleComplexColor.h',
@@ -191,18 +193,20 @@ UNIFIED_SOURCES += [
     'nsStyleSet.cpp',
     'nsStyleStruct.cpp',
     'nsStyleTransformMatrix.cpp',
     'nsStyleUtil.cpp',
     'nsTransitionManager.cpp',
     'RuleNodeCacheConditions.cpp',
     'RuleProcessorCache.cpp',
     'ServoBindings.cpp',
+    'ServoCSSRuleList.cpp',
     'ServoDeclarationBlock.cpp',
     'ServoElementSnapshot.cpp',
+    'ServoStyleRule.cpp',
     'ServoStyleSet.cpp',
     'ServoStyleSheet.cpp',
     'StyleAnimationValue.cpp',
     'StyleRule.cpp',
     'StyleSheet.cpp',
     'SVGAttrAnimationRuleProcessor.cpp',
 ]
 
--- a/layout/style/nsAnimationManager.cpp
+++ b/layout/style/nsAnimationManager.cpp
@@ -323,17 +323,17 @@ UpdateOldAnimationPropertiesWithNew(
     bool aNewIsStylePaused,
     nsStyleContext* aStyleContext)
 {
   bool animationChanged = false;
 
   // Update the old from the new so we can keep the original object
   // identity (and any expando properties attached to it).
   if (aOld.GetEffect()) {
-    AnimationEffectReadOnly* oldEffect = aOld.GetEffect();
+    dom::AnimationEffectReadOnly* oldEffect = aOld.GetEffect();
     animationChanged = oldEffect->SpecifiedTiming() != aNewTiming;
     oldEffect->SetSpecifiedTiming(aNewTiming);
 
     KeyframeEffectReadOnly* oldKeyframeEffect = oldEffect->AsKeyframeEffect();
     if (oldKeyframeEffect) {
       oldKeyframeEffect->SetKeyframes(Move(aNewKeyframes), aStyleContext);
     }
   }
--- a/layout/style/nsCSSRules.cpp
+++ b/layout/style/nsCSSRules.cpp
@@ -51,17 +51,17 @@ using namespace mozilla::dom;
 IMPL_STYLE_RULE_INHERIT_GET_DOM_RULE_WEAK(class_, super_)
 
 // base class for all rule types in a CSS style sheet
 
 namespace mozilla {
 namespace css {
 
 /* virtual */ void
-Rule::SetStyleSheet(CSSStyleSheet* aSheet)
+Rule::SetStyleSheet(StyleSheet* aSheet)
 {
   // We don't reference count this up reference. The style sheet
   // will tell us when it's going away or when we're detached from
   // it.
   mSheet = aSheet;
 }
 
 nsresult
@@ -127,18 +127,18 @@ GroupRuleRuleList::~GroupRuleRuleList()
 }
 
 CSSStyleSheet*
 GroupRuleRuleList::GetParentObject()
 {
   if (!mGroupRule) {
     return nullptr;
   }
-
-  return mGroupRule->GetStyleSheet();
+  StyleSheet* sheet = mGroupRule->GetStyleSheet();
+  return sheet ? sheet->AsGecko() : nullptr;
 }
 
 uint32_t
 GroupRuleRuleList::Length()
 {
   if (!mGroupRule) {
     return 0;
   }
@@ -384,18 +384,17 @@ NS_IMPL_CYCLE_COLLECTING_ADDREF(GroupRul
 NS_IMPL_CYCLE_COLLECTING_RELEASE(GroupRule)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(GroupRule)
 NS_INTERFACE_MAP_END
 
 static bool
 SetStyleSheetReference(Rule* aRule, void* aSheet)
 {
-  CSSStyleSheet* sheet = (CSSStyleSheet*)aSheet;
-  aRule->SetStyleSheet(sheet);
+  aRule->SetStyleSheet(reinterpret_cast<StyleSheet*>(aSheet));
   return true;
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(GroupRule)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(GroupRule)
   tmp->mRules.EnumerateForwards(SetParentRuleReference, nullptr);
   // If tmp does not have a stylesheet, neither do its descendants.  In that
@@ -419,17 +418,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
   for (int32_t i = 0, count = rules.Count(); i < count; ++i) {
     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mRules[i]");
     cb.NoteXPCOMChild(rules[i]->GetExistingDOMRule());
   }
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRuleCollection)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 /* virtual */ void
-GroupRule::SetStyleSheet(CSSStyleSheet* aSheet)
+GroupRule::SetStyleSheet(StyleSheet* aSheet)
 {
   // Don't set the sheet on the kids if it's already the same as the sheet we
   // already have.  This is needed to avoid O(N^2) behavior in group nesting
   // depth when seting the sheet to null during unlink, if we happen to unlin in
   // order from most nested rule up to least nested rule.
   if (aSheet != GetStyleSheet()) {
     mRules.EnumerateForwards(SetStyleSheetReference, aSheet);
     Rule::SetStyleSheet(aSheet);
@@ -445,21 +444,21 @@ GroupRule::List(FILE* out, int32_t aInde
   }
 }
 #endif
 
 void
 GroupRule::AppendStyleRule(Rule* aRule)
 {
   mRules.AppendObject(aRule);
-  CSSStyleSheet* sheet = GetStyleSheet();
+  StyleSheet* sheet = GetStyleSheet();
   aRule->SetStyleSheet(sheet);
   aRule->SetParentRule(this);
   if (sheet) {
-    sheet->SetModifiedByChildRule();
+    sheet->AsGecko()->SetModifiedByChildRule();
   }
 }
 
 Rule*
 GroupRule::GetStyleRuleAt(int32_t aIndex) const
 {
   return mRules.SafeObjectAt(aIndex);
 }
@@ -530,41 +529,41 @@ GroupRule::GetCssRules(nsIDOMCSSRuleList
 
   NS_ADDREF(*aRuleList = mRuleCollection);
   return NS_OK;
 }
 
 nsresult
 GroupRule::InsertRule(const nsAString & aRule, uint32_t aIndex, uint32_t* _retval)
 {
-  CSSStyleSheet* sheet = GetStyleSheet();
+  StyleSheet* sheet = GetStyleSheet();
   NS_ENSURE_TRUE(sheet, NS_ERROR_FAILURE);
   
   if (aIndex > uint32_t(mRules.Count()))
     return NS_ERROR_DOM_INDEX_SIZE_ERR;
 
   NS_ASSERTION(uint32_t(mRules.Count()) <= INT32_MAX,
                "Too many style rules!");
 
-  return sheet->InsertRuleIntoGroup(aRule, this, aIndex, _retval);
+  return sheet->AsGecko()->InsertRuleIntoGroup(aRule, this, aIndex, _retval);
 }
 
 nsresult
 GroupRule::DeleteRule(uint32_t aIndex)
 {
-  CSSStyleSheet* sheet = GetStyleSheet();
+  StyleSheet* sheet = GetStyleSheet();
   NS_ENSURE_TRUE(sheet, NS_ERROR_FAILURE);
 
   if (aIndex >= uint32_t(mRules.Count()))
     return NS_ERROR_DOM_INDEX_SIZE_ERR;
 
   NS_ASSERTION(uint32_t(mRules.Count()) <= INT32_MAX,
                "Too many style rules!");
 
-  return sheet->DeleteRuleFromGroup(this, aIndex);
+  return sheet->AsGecko()->DeleteRuleFromGroup(this, aIndex);
 }
 
 /* virtual */ size_t
 GroupRule::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
 {
   size_t n = mRules.ShallowSizeOfExcludingThis(aMallocSizeOf);
   for (size_t i = 0; i < mRules.Length(); i++) {
     n += mRules[i]->SizeOfIncludingThis(aMallocSizeOf);
@@ -611,22 +610,24 @@ NS_INTERFACE_MAP_BEGIN(MediaRule)
   NS_INTERFACE_MAP_ENTRY(nsIDOMCSSGroupingRule)
   NS_INTERFACE_MAP_ENTRY(nsIDOMCSSConditionRule)
   NS_INTERFACE_MAP_ENTRY(nsIDOMCSSMediaRule)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozilla::css::Rule)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSMediaRule)
 NS_INTERFACE_MAP_END_INHERITING(GroupRule)
 
 /* virtual */ void
-MediaRule::SetStyleSheet(CSSStyleSheet* aSheet)
+MediaRule::SetStyleSheet(StyleSheet* aSheet)
 {
   if (mMedia) {
     // Set to null so it knows it's leaving one sheet and joining another.
     mMedia->SetStyleSheet(nullptr);
-    mMedia->SetStyleSheet(aSheet);
+    if (aSheet) {
+      mMedia->SetStyleSheet(aSheet->AsGecko());
+    }
   }
 
   GroupRule::SetStyleSheet(aSheet);
 }
 
 #ifdef DEBUG
 /* virtual */ void
 MediaRule::List(FILE* out, int32_t aIndent) const
@@ -2126,20 +2127,18 @@ nsCSSKeyframeRule::SetKeyText(const nsAS
     return NS_OK;
   }
 
   nsIDocument* doc = GetDocument();
   MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true);
 
   newSelectors.SwapElements(mKeys);
 
-  CSSStyleSheet* sheet = GetStyleSheet();
-  if (sheet) {
-    sheet->SetModifiedByChildRule();
-
+  if (StyleSheet* sheet = GetStyleSheet()) {
+    sheet->AsGecko()->SetModifiedByChildRule();
     if (doc) {
       doc->StyleRuleChanged(sheet, this);
     }
   }
 
   return NS_OK;
 }
 
@@ -2163,20 +2162,18 @@ nsCSSKeyframeRule::ChangeDeclaration(css
   MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true);
 
   if (aDeclaration != mDeclaration) {
     mDeclaration->SetOwningRule(nullptr);
     mDeclaration = aDeclaration;
     mDeclaration->SetOwningRule(this);
   }
 
-  CSSStyleSheet* sheet = GetStyleSheet();
-  if (sheet) {
-    sheet->SetModifiedByChildRule();
-
+  if (StyleSheet* sheet = GetStyleSheet()) {
+    sheet->AsGecko()->SetModifiedByChildRule();
     if (doc) {
       doc->StyleRuleChanged(sheet, this);
     }
   }
 }
 
 /* virtual */ size_t
 nsCSSKeyframeRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
@@ -2312,20 +2309,18 @@ nsCSSKeyframesRule::SetName(const nsAStr
     return NS_OK;
   }
 
   nsIDocument* doc = GetDocument();
   MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true);
 
   mName = aName;
 
-  CSSStyleSheet* sheet = GetStyleSheet();
-  if (sheet) {
-    sheet->SetModifiedByChildRule();
-
+  if (StyleSheet* sheet = GetStyleSheet()) {
+    sheet->AsGecko()->SetModifiedByChildRule();
     if (doc) {
       doc->StyleRuleChanged(sheet, this);
     }
   }
 
   return NS_OK;
 }
 
@@ -2347,20 +2342,18 @@ nsCSSKeyframesRule::AppendRule(const nsA
   RefPtr<nsCSSKeyframeRule> rule =
     parser.ParseKeyframeRule(aRule, nullptr, 0);
   if (rule) {
     nsIDocument* doc = GetDocument();
     MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true);
 
     AppendStyleRule(rule);
 
-    CSSStyleSheet* sheet = GetStyleSheet();
-    if (sheet) {
-      sheet->SetModifiedByChildRule();
-
+    if (StyleSheet* sheet = GetStyleSheet()) {
+      sheet->AsGecko()->SetModifiedByChildRule();
       if (doc) {
         doc->StyleRuleChanged(sheet, this);
       }
     }
   }
 
   return NS_OK;
 }
@@ -2395,19 +2388,18 @@ nsCSSKeyframesRule::DeleteRule(const nsA
 {
   uint32_t index = FindRuleIndexForKey(aKey);
   if (index != RULE_NOT_FOUND) {
     nsIDocument* doc = GetDocument();
     MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true);
 
     mRules.RemoveObjectAt(index);
 
-    CSSStyleSheet* sheet = GetStyleSheet();
-    if (sheet) {
-      sheet->SetModifiedByChildRule();
+    if (StyleSheet* sheet = GetStyleSheet()) {
+      sheet->AsGecko()->SetModifiedByChildRule();
 
       if (doc) {
         doc->StyleRuleChanged(sheet, this);
       }
     }
   }
   return NS_OK;
 }
@@ -2648,19 +2640,18 @@ void
 nsCSSPageRule::ChangeDeclaration(css::Declaration* aDeclaration)
 {
   if (aDeclaration != mDeclaration) {
     mDeclaration->SetOwningRule(nullptr);
     mDeclaration = aDeclaration;
     mDeclaration->SetOwningRule(this);
   }
 
-  CSSStyleSheet* sheet = GetStyleSheet();
-  if (sheet) {
-    sheet->SetModifiedByChildRule();
+  if (StyleSheet* sheet = GetStyleSheet()) {
+    sheet->AsGecko()->SetModifiedByChildRule();
   }
 }
 
 /* virtual */ size_t
 nsCSSPageRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
 {
   return aMallocSizeOf(this);
 }
@@ -2964,19 +2955,18 @@ nsCSSCounterStyleRule::SetName(const nsA
   nsCSSParser parser;
   nsAutoString name;
   if (parser.ParseCounterStyleName(aName, nullptr, name)) {
     nsIDocument* doc = GetDocument();
     MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true);
 
     mName = name;
 
-    CSSStyleSheet* sheet = GetStyleSheet();
-    if (sheet) {
-      sheet->SetModifiedByChildRule();
+    if (StyleSheet* sheet = GetStyleSheet()) {
+      sheet->AsGecko()->SetModifiedByChildRule();
       if (doc) {
         doc->StyleRuleChanged(sheet, this);
       }
     }
   }
   return NS_OK;
 }
 
@@ -3010,19 +3000,18 @@ nsCSSCounterStyleRule::SetDesc(nsCSSCoun
              "descriptor ID out of range");
 
   nsIDocument* doc = GetDocument();
   MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true);
 
   mValues[aDescID] = aValue;
   mGeneration++;
 
-  CSSStyleSheet* sheet = GetStyleSheet();
-  if (sheet) {
-    sheet->SetModifiedByChildRule();
+  if (StyleSheet* sheet = GetStyleSheet()) {
+    sheet->AsGecko()->SetModifiedByChildRule();
     if (doc) {
       doc->StyleRuleChanged(sheet, this);
     }
   }
 }
 
 NS_IMETHODIMP
 nsCSSCounterStyleRule::GetSystem(nsAString& aSystem)
@@ -3240,20 +3229,19 @@ nsCSSCounterStyleRule::CheckDescValue(in
 }
 
 nsresult
 nsCSSCounterStyleRule::SetDescriptor(nsCSSCounterDesc aDescID,
                                      const nsAString& aValue)
 {
   nsCSSParser parser;
   nsCSSValue value;
-  CSSStyleSheet* sheet = GetStyleSheet();
   nsIURI* baseURL = nullptr;
   nsIPrincipal* principal = nullptr;
-  if (sheet) {
+  if (StyleSheet* sheet = GetStyleSheet()) {
     baseURL = sheet->GetBaseURI();
     principal = sheet->Principal();
   }
   if (parser.ParseCounterDescriptor(aDescID, aValue, nullptr,
                                     baseURL, principal, value)) {
     if (CheckDescValue(GetSystem(), aDescID, value)) {
       SetDesc(aDescID, value);
     }
--- a/layout/style/nsCSSRules.h
+++ b/layout/style/nsCSSRules.h
@@ -54,17 +54,22 @@ private:
 public:
 
   NS_DECL_ISUPPORTS_INHERITED
 
   // Rule methods
 #ifdef DEBUG
   virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
 #endif
-  virtual void SetStyleSheet(mozilla::CSSStyleSheet* aSheet) override; //override GroupRule
+  virtual void SetStyleSheet(mozilla::StyleSheet* aSheet) override; //override GroupRule
+  mozilla::CSSStyleSheet* GetStyleSheet() const
+  {
+    mozilla::StyleSheet* sheet = GroupRule::GetStyleSheet();
+    return sheet ? sheet->AsGecko() : nullptr;
+  }
   virtual int32_t GetType() const override;
   virtual already_AddRefed<Rule> Clone() const override;
   virtual nsIDOMCSSRule* GetDOMRule() override
   {
     return this;
   }
   virtual nsIDOMCSSRule* GetExistingDOMRule() override
   {
--- a/layout/style/nsDOMCSSDeclaration.cpp
+++ b/layout/style/nsDOMCSSDeclaration.cpp
@@ -256,17 +256,17 @@ nsDOMCSSDeclaration::RemoveProperty(cons
   NS_ENSURE_SUCCESS(rv, rv);
   return RemovePropertyInternal(aPropertyName);
 }
 
 /* static */ void
 nsDOMCSSDeclaration::GetCSSParsingEnvironmentForRule(css::Rule* aRule,
                                                      CSSParsingEnvironment& aCSSParseEnv)
 {
-  CSSStyleSheet* sheet = aRule ? aRule->GetStyleSheet() : nullptr;
+  StyleSheet* sheet = aRule ? aRule->GetStyleSheet() : nullptr;
   if (!sheet) {
     aCSSParseEnv.mPrincipal = nullptr;
     return;
   }
 
   nsIDocument* document = sheet->GetOwningDocument();
   aCSSParseEnv.mSheetURI = sheet->GetSheetURI();
   aCSSParseEnv.mBaseURI = sheet->GetBaseURI();
--- a/layout/style/nsStyleUtil.cpp
+++ b/layout/style/nsStyleUtil.cpp
@@ -3,16 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsStyleUtil.h"
 #include "nsStyleConsts.h"
 
 #include "nsIContent.h"
 #include "nsCSSProps.h"
+#include "nsContentUtils.h"
 #include "nsRuleNode.h"
 #include "nsROCSSPrimitiveValue.h"
 #include "nsStyleStruct.h"
 #include "nsIContentPolicy.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsIURI.h"
 #include "nsPrintfCString.h"
 
--- a/layout/style/nsTransitionManager.cpp
+++ b/layout/style/nsTransitionManager.cpp
@@ -355,34 +355,34 @@ CSSTransition::HasLowerCompositeOrderTha
   }
 
   // 3. (Same transition generation): Sort by transition property
   return nsCSSProps::GetStringValue(TransitionProperty()) <
          nsCSSProps::GetStringValue(aOther.TransitionProperty());
 }
 
 /* static */ Nullable<TimeDuration>
-CSSTransition::GetCurrentTimeAt(const DocumentTimeline& aTimeline,
+CSSTransition::GetCurrentTimeAt(const dom::DocumentTimeline& aTimeline,
                                 const TimeStamp& aBaseTime,
                                 const TimeDuration& aStartTime,
                                 double aPlaybackRate)
 {
   Nullable<TimeDuration> result;
 
   Nullable<TimeDuration> timelineTime = aTimeline.ToTimelineTime(aBaseTime);
   if (!timelineTime.IsNull()) {
     result.SetValue((timelineTime.Value() - aStartTime)
                       .MultDouble(aPlaybackRate));
   }
 
   return result;
 }
 
 void
-CSSTransition::SetEffectFromStyle(AnimationEffectReadOnly* aEffect)
+CSSTransition::SetEffectFromStyle(dom::AnimationEffectReadOnly* aEffect)
 {
   Animation::SetEffectNoUpdate(aEffect);
 
   // Initialize transition property.
   ElementPropertyTransition* pt = aEffect ? aEffect->AsTransition() : nullptr;
   if (eCSSProperty_UNKNOWN == mTransitionProperty && pt) {
     mTransitionProperty = pt->TransitionProperty();
     mTransitionToValue = pt->ToValue();
--- a/security/nss/TAG-INFO
+++ b/security/nss/TAG-INFO
@@ -1,1 +1,1 @@
-86638bd12ab0
+6c26f0cd19ba
--- a/security/nss/automation/taskcluster/graph/src/extend.js
+++ b/security/nss/automation/taskcluster/graph/src/extend.js
@@ -327,16 +327,31 @@ async function scheduleFuzzing() {
         "cert nss/fuzz/corpus/cert -max_total_time=300"
     ],
     // Need a privileged docker container to remove this.
     env: {ASAN_OPTIONS: "detect_leaks=0"},
     symbol: "SCert",
     kind: "test"
   }));
 
+  queue.scheduleTask(merge(base, {
+    parent: task_build,
+    name: "SPKI",
+    command: [
+      "/bin/bash",
+      "-c",
+      "bin/checkout.sh && nss/automation/taskcluster/scripts/fuzz.sh " +
+        "spki nss/fuzz/corpus/spki -max_total_time=300"
+    ],
+    // Need a privileged docker container to remove this.
+    env: {ASAN_OPTIONS: "detect_leaks=0"},
+    symbol: "SPKI",
+    kind: "test"
+  }));
+
   return queue.submit();
 }
 
 /*****************************************************************************/
 
 async function scheduleTestBuilds() {
   let base = {
     platform: "linux64",
--- a/security/nss/automation/taskcluster/graph/src/queue.js
+++ b/security/nss/automation/taskcluster/graph/src/queue.js
@@ -10,17 +10,17 @@ import * as image_builder from "./image_
 
 let maps = [];
 let filters = [];
 
 let tasks = new Map();
 let image_tasks = new Map();
 
 let queue = new taskcluster.Queue({
-  baseUrl: "http://taskcluster/queue/v1/"
+  baseUrl: "http://taskcluster/queue/v1"
 });
 
 function fromNow(hours) {
   let d = new Date();
   d.setHours(d.getHours() + (hours|0));
   return d.toJSON();
 }
 
--- a/security/nss/automation/taskcluster/graph/src/try_syntax.js
+++ b/security/nss/automation/taskcluster/graph/src/try_syntax.js
@@ -30,17 +30,17 @@ function parseOptions(opts) {
   if (platforms.length == 0 && opts.platform != "none") {
     platforms = allPlatforms;
   }
 
   // Parse unit tests.
   let aliases = {"gtests": "gtest"};
   let allUnitTests = ["bogo", "crmf", "chains", "cipher", "db", "ec", "fips",
                       "gtest", "lowhash", "merge", "sdr", "smime", "tools",
-                      "ssl", "mpi", "scert"];
+                      "ssl", "mpi", "scert", "spki"];
   let unittests = intersect(opts.unittests.split(/\s*,\s*/).map(t => {
     return aliases[t] || t;
   }), allUnitTests);
 
   // If the given value is "all" run all tests.
   // If it's nonsense then don't run any tests.
   if (opts.unittests == "all") {
     unittests = allUnitTests;
--- a/security/nss/build.sh
+++ b/security/nss/build.sh
@@ -9,16 +9,17 @@ set -e
 source $(dirname $0)/coreconf/nspr.sh
 
 # Usage info
 show_help() {
 cat << EOF
 
 Usage: ${0##*/} [-hcgv] [-j <n>] [--test] [--fuzz] [--scan-build[=output]]
                 [-m32] [--opt|-o] [--asan] [--ubsan] [--sancov[=edge|bb|func]]
+                [--pprof] [--msan]
 
 This script builds NSS with gyp and ninja.
 
 This build system is still under development.  It does not yet support all
 the features or platforms that NSS supports.
 
 NSS build tool options:
 
@@ -30,35 +31,37 @@ NSS build tool options:
     -m32          do a 32-bit build on a 64-bit system
     --test        ignore map files and export everything we have
     --fuzz        enable fuzzing mode. this always enables test builds
     --scan-build  run the build with scan-build (scan-build has to be in the path)
                   --scan-build=/out/path sets the output path for scan-build
     --opt|-o      do an opt build
     --asan        do an asan build
     --ubsan       do an ubsan build
+    --msan        do an msan build
     --sancov      do sanitize coverage builds
                   --sancov=func sets coverage to function level for example
+    --pprof       build with gperftool support
 EOF
 }
 
 if [ -n "$CCC" ] && [ -z "$CXX" ]; then
     export CXX="$CCC"
 fi
 
 opt_build=0
 build_64=0
 clean=0
 rebuild_gyp=0
 target=Debug
 verbose=0
 fuzz=0
 
 # parse parameters to store in config
-params=$(echo "$*" | perl -pe 's/-c|-v|-g|-j [0-9]*|-h//g' | perl -pe 's/^[ \t]*//')
+params=$(echo "$*" | perl -pe 's/-c|-v|-g|-j [0-9]*|-h//g' | perl -pe 's/^\s*(.*?)\s*$/\1/')
 params=$(echo "$params $CC $CCC" | tr " " "\n" | perl -pe '/^\s*$/d')
 params=$(echo "${params[*]}" | sort)
 
 cwd=$(cd $(dirname $0); pwd -P)
 dist_dir="$cwd/../dist"
 
 # try to guess sensible defaults
 arch=$(python "$cwd/coreconf/detect_host_arch.py")
@@ -96,16 +99,18 @@ while [ $# -gt 0 ]; do
         --scan-build) scanbuild=(scan-build) ;;
         --scan-build=?*) scanbuild=(scan-build -o "${1#*=}") ;;
         --opt|-o) opt_build=1 ;;
         -m32|--m32) build_64=0 ;;
         --asan) gyp_params+=(-Duse_asan=1); nspr_sanitizer asan ;;
         --ubsan) gyp_params+=(-Duse_ubsan=1); nspr_sanitizer ubsan ;;
         --sancov) gyp_params+=(-Duse_sancov=edge); nspr_sanitizer sancov edge ;;
         --sancov=?*) gyp_params+=(-Duse_sancov="${1#*=}"); nspr_sanitizer sancov "${1#*=}" ;;
+        --pprof) gyp_params+=(-Duse_pprof=1) ;;
+        --msan) gyp_params+=(-Duse_msan=1); nspr_sanitizer msan ;;
         *) show_help; exit ;;
     esac
     shift
 done
 
 if [ "$opt_build" = "1" ]; then
     target=Release
     nspr_opt+=(--disable-debug --enable-optimize)
--- a/security/nss/cmd/selfserv/selfserv.c
+++ b/security/nss/cmd/selfserv/selfserv.c
@@ -1251,17 +1251,17 @@ handle_connection(
     int newln = 0; /* # of consecutive newlns */
     int firstTime = 1;
     int reqLen;
     int rv;
     int numIOVs;
     PRSocketOptionData opt;
     PRIOVec iovs[16];
     char msgBuf[160];
-    char buf[10240];
+    char buf[10240] = { 0 };
     char fileName[513];
     char proto[128];
     PRDescIdentity aboveLayer = PR_INVALID_IO_LAYER;
 
     pBuf = buf;
     bufRem = sizeof buf;
 
     VLOG(("selfserv: handle_connection: starting"));
--- a/security/nss/cmd/shlibsign/shlibsign.c
+++ b/security/nss/cmd/shlibsign/shlibsign.c
@@ -702,17 +702,17 @@ getSlotList(CK_FUNCTION_LIST_PTR pFuncti
 }
 
 int
 main(int argc, char **argv)
 {
     PLOptState *optstate;
     char *program_name;
     char *libname = NULL;
-    PRLibrary *lib;
+    PRLibrary *lib = NULL;
     PRFileDesc *fd;
     PRStatus rv = PR_SUCCESS;
     const char *input_file = NULL; /* read/create encrypted data from here */
     char *output_file = NULL;      /* write new encrypted data here */
     int bytesRead;
     int bytesWritten;
     unsigned char file_buf[512];
     int count = 0;
@@ -865,34 +865,50 @@ main(int argc, char **argv)
         return 1;
     }
 
     /* Get the platform-dependent library name of the
      * NSS cryptographic module.
      */
     libname = PR_GetLibraryName(NULL, "softokn3");
     assert(libname != NULL);
+    if (!libname) {
+        PR_fprintf(PR_STDERR, "getting softokn3 failed");
+        goto cleanup;
+    }
     lib = PR_LoadLibrary(libname);
     assert(lib != NULL);
+    if (!lib) {
+        PR_fprintf(PR_STDERR, "loading softokn3 failed");
+        goto cleanup;
+    }
     PR_FreeLibraryName(libname);
 
     if (FIPSMODE) {
         /* FIPSMODE == FC_GetFunctionList */
         /* library path must be set to an already signed softokn3/freebl */
         pC_GetFunctionList = (CK_C_GetFunctionList)
             PR_FindFunctionSymbol(lib, "FC_GetFunctionList");
     } else {
         /* NON FIPS mode  == C_GetFunctionList */
         pC_GetFunctionList = (CK_C_GetFunctionList)
             PR_FindFunctionSymbol(lib, "C_GetFunctionList");
     }
     assert(pC_GetFunctionList != NULL);
+    if (!pC_GetFunctionList) {
+        PR_fprintf(PR_STDERR, "getting function list failed");
+        goto cleanup;
+    }
 
     crv = (*pC_GetFunctionList)(&pFunctionList);
     assert(crv == CKR_OK);
+    if (crv != CKR_OK) {
+        PR_fprintf(PR_STDERR, "loading function list failed");
+        goto cleanup;
+    }
 
     if (configDir) {
         if (!dbPrefix) {
             dbPrefix = PL_strdup("");
         }
         crv = softokn_Init(pFunctionList, configDir, dbPrefix);
         if (crv != CKR_OK) {
             logIt("Failed to use provided database directory "
@@ -1299,17 +1315,17 @@ cleanup:
     }
 #ifdef USES_LINKS
     if (link_file) { /* allocated by mkoutput function */
         PL_strfree(link_file);
     }
 #endif
 
     disableUnload = PR_GetEnvSecure("NSS_DISABLE_UNLOAD");
-    if (!disableUnload) {
+    if (!disableUnload && lib) {
         PR_UnloadLibrary(lib);
     }
     PR_Cleanup();
 
     if (crv != CKR_OK)
         return crv;
 
     return (successful) ? 0 : 1;
--- a/security/nss/coreconf/config.gypi
+++ b/security/nss/coreconf/config.gypi
@@ -88,20 +88,22 @@
     'disable_libpkix%': 1,
     'disable_werror%': 0,
     'mozilla_client%': 0,
     'moz_fold_libs%': 0,
     'moz_folded_library_name%': '',
     'ssl_enable_zlib%': 1,
     'use_asan%': 0,
     'use_ubsan%': 0,
+    'use_msan%': 0,
     'use_sancov%': 0,
     'test_build%': 0,
     'fuzz%': 0,
     'sign_libs%': 1,
+    'use_pprof%': 0,
     'nss_public_dist_dir%': '<(nss_dist_dir)/public',
     'nss_private_dist_dir%': '<(nss_dist_dir)/private',
   },
   'target_defaults': {
     # Settings specific to targets should go here.
     # This is mostly for linking to libraries.
     'variables': {
       'mapfile%': '',
@@ -266,16 +268,19 @@
               [ 'target_arch=="ia32"', {
                 'cflags': ['-m32'],
                 'ldflags': ['-m32'],
               }],
               [ 'target_arch=="x64"', {
                 'cflags': ['-m64'],
                 'ldflags': ['-m64'],
               }],
+              [ 'use_pprof==1' , {
+                'ldflags': [ '-lprofiler' ],
+              }],
             ],
           }],
           [ 'disable_werror==0 and (OS=="linux" or OS=="mac")', {
             'cflags': [
               '<!@(<(python) <(DEPTH)/coreconf/werror.py)',
             ],
           }],
           [ 'fuzz==1', {
@@ -318,16 +323,31 @@
             'ldflags!': ['<@(no_ldflags)'],
             'xcode_settings': {
               'OTHER_CFLAGS': ['<@(ubsan_flags)'],
               'OTHER_LDFLAGS!': ['<@(no_ldflags)'],
               # See comment above.
               'LIBRARY_SEARCH_PATHS': ['/usr/lib <(ubsan_flags)'],
             },
           }],
+          [ 'use_msan==1', {
+            'variables': {
+              'msan_flags': '<!(<(python) <(DEPTH)/coreconf/sanitizers.py msan)',
+              'no_ldflags': '<!(<(python) <(DEPTH)/coreconf/sanitizers.py ld)',
+            },
+            'cflags': ['<@(msan_flags)'],
+            'ldflags': ['<@(msan_flags)'],
+            'ldflags!': ['<@(no_ldflags)'],
+            'xcode_settings': {
+              'OTHER_CFLAGS': ['<@(msan_flags)'],
+              'OTHER_LDFLAGS!': ['<@(no_ldflags)'],
+              # See comment above.
+              'LIBRARY_SEARCH_PATHS': ['/usr/lib <(msan_flags)'],
+            },
+          }],
           [ 'use_sancov!=0', {
             'variables': {
               'sancov_flags': '<!(<(python) <(DEPTH)/coreconf/sanitizers.py sancov <(use_sancov))',
               'no_ldflags': '<!(<(python) <(DEPTH)/coreconf/sanitizers.py ld)',
             },
             'cflags': ['<@(sancov_flags)'],
             'ldflags': ['<@(sancov_flags)'],
             'ldflags!': ['<@(no_ldflags)'],
--- a/security/nss/coreconf/coreconf.dep
+++ b/security/nss/coreconf/coreconf.dep
@@ -5,9 +5,8 @@
 
 /*
  * A dummy header file that is a dependency for all the object files.
  * Used to force a full recompilation of NSS in Mozilla's Tinderbox
  * depend builds.  See comments in rules.mk.
  */
 
 #error "Do not include this header file."
-
--- a/security/nss/coreconf/sanitizers.py
+++ b/security/nss/coreconf/sanitizers.py
@@ -1,32 +1,36 @@
 #!/usr/bin/env python2
 
 from __future__ import print_function
 import sys
 
 def main():
     if len(sys.argv) < 2:
-        raise Exception('Specify either "ld", asan", "sancov" or "ubsan" as argument.')
+        raise Exception('Specify either "ld", asan", "msan", "sancov" or "ubsan" as argument.')
 
     sanitizer = sys.argv[1]
     if sanitizer == "ubsan":
         print('-fsanitize=undefined -fno-sanitize-recover=undefined ', end='')
         return
     if sanitizer == "asan":
         print('-fsanitize=address ', end='')
         print('-fno-omit-frame-pointer -fno-optimize-sibling-calls ', end='')
         return
+    if sanitizer == "msan":
+        print('-fsanitize=memory -fsanitize-memory-track-origins ', end='')
+        print('-fno-omit-frame-pointer -fno-optimize-sibling-calls ', end='')
+        return
     if sanitizer == "sancov":
         if len(sys.argv) < 3:
             raise Exception('sancov requires another argument (edge|bb|func).')
         print('-fsanitize-coverage='+sys.argv[2]+' ', end='')
         return
 
     # We have to remove this from the ld flags when building asan.
     if sanitizer == "ld":
         print('-Wl,-z,defs ', end='')
         return
 
-    raise Exception('Specify either "ld", asan", "sancov" or "ubsan" as argument.')
+    raise Exception('Specify either "ld", asan", "msan", "sancov" or "ubsan" as argument.')
 
 if __name__ == '__main__':
     main()
deleted file mode 100644
--- a/security/nss/fuzz/cert_target.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include <stdint.h>
-
-#include "cert.h"
-
-#include "registry.h"
-
-extern "C" int cert_fuzzing_target(const uint8_t *Data, size_t Size) {
-  PORTCheapArenaPool pool;
-  PORT_InitCheapArena(&pool, DER_DEFAULT_CHUNKSIZE);
-
-  CERTCertificate cert;
-  SECItem data = {siBuffer, (unsigned char *)Data, (unsigned int)Size};
-  SEC_QuickDERDecodeItem(&pool.arena, &cert, SEC_SignedCertificateTemplate,
-                         &data);
-
-  PORT_DestroyCheapArena(&pool);
-  return 0;
-}
-
-REGISTER_FUZZING_TARGET("cert", cert_fuzzing_target, 3072, "Certificate Import")
--- a/security/nss/fuzz/fuzz.gyp
+++ b/security/nss/fuzz/fuzz.gyp
@@ -6,20 +6,19 @@
     '../coreconf/config.gypi',
     '../cmd/platlibs.gypi'
   ],
   'targets': [
     {
       'target_name': 'nssfuzz',
       'type': 'executable',
       'sources': [
-        'cert_target.cc',
+        'nssfuzz.cc',
         'pkcs8_target.cc',
-        'spki_target.cc',
-        'nssfuzz.cc',
+        'quickder_targets.cc',
       ],
       'dependencies': [
         '<(DEPTH)/exports.gyp:nss_exports',
         '<(DEPTH)/fuzz/libFuzzer/libFuzzer.gyp:libFuzzer'
       ]
     }
   ],
   'target_defaults': {
new file mode 100644
--- /dev/null
+++ b/security/nss/fuzz/quickder_targets.cc
@@ -0,0 +1,36 @@
+/* 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 <stdint.h>
+
+#include "cert.h"
+
+#include "registry.h"
+
+void QuickDERDecode(void *dst, const SEC_ASN1Template *tpl, const uint8_t *buf,
+                    size_t len) {
+  PORTCheapArenaPool pool;
+  SECItem data = {siBuffer, const_cast<unsigned char *>(buf),
+                  static_cast<unsigned int>(len)};
+
+  PORT_InitCheapArena(&pool, DER_DEFAULT_CHUNKSIZE);
+  (void)SEC_QuickDERDecodeItem(&pool.arena, dst, tpl, &data);
+  PORT_DestroyCheapArena(&pool);
+}
+
+extern "C" int cert_fuzzing_target(const uint8_t *Data, size_t Size) {
+  CERTCertificate cert;
+  QuickDERDecode(&cert, SEC_SignedCertificateTemplate, Data, Size);
+  return 0;
+}
+
+REGISTER_FUZZING_TARGET("cert", cert_fuzzing_target, 3072, "Certificate Import")
+
+extern "C" int spki_fuzzing_target(const uint8_t *Data, size_t Size) {
+  CERTSubjectPublicKeyInfo spki;
+  QuickDERDecode(&spki, CERT_SubjectPublicKeyInfoTemplate, Data, Size);
+  return 0;
+}
+
+REGISTER_FUZZING_TARGET("spki", spki_fuzzing_target, 1024, "SPKI Import")
--- a/security/nss/fuzz/registry.h
+++ b/security/nss/fuzz/registry.h
@@ -58,14 +58,14 @@ class Registry {
     return GetInstance().targets_[name];
   }
 
   Registry() {}
 
   std::map<std::string, TargetData> targets_;
 };
 
-#define REGISTER_FUZZING_TARGET(name, func, max_len, desc)           \
-  static void __attribute__((constructor)) RegisterFuzzingTarget() { \
-    Registry::Add(name, func, max_len, desc);                        \
+#define REGISTER_FUZZING_TARGET(name, func, max_len, desc)     \
+  static void __attribute__((constructor)) Register_##func() { \
+    Registry::Add(name, func, max_len, desc);                  \
   }
 
 #endif  // registry_h__
deleted file mode 100644
--- a/security/nss/fuzz/spki_target.cc
+++ /dev/null
@@ -1,35 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include <assert.h>
-#include <stdint.h>
-#include <memory>
-
-#include "keyhi.h"
-#include "pk11pub.h"
-
-#include "registry.h"
-#include "shared.h"
-
-extern "C" int spki_fuzzing_target(const uint8_t *Data, size_t Size) {
-  SECItem data = {siBuffer, (unsigned char *)Data, (unsigned int)Size};
-
-  static std::unique_ptr<NSSDatabase> db(new NSSDatabase());
-  assert(db != nullptr);
-
-  CERTSubjectPublicKeyInfo *spki = SECKEY_DecodeDERSubjectPublicKeyInfo(&data);
-
-  if (spki) {
-    SECKEYPublicKey *key = SECKEY_ExtractPublicKey(spki);
-    SECKEY_DestroyPublicKey(key);
-  }
-
-  SECKEY_DestroySubjectPublicKeyInfo(spki);
-
-  return 0;
-}
-
-REGISTER_FUZZING_TARGET("spki", spki_fuzzing_target, 1024, "SPKI Import")
--- a/security/nss/gtests/common/scoped_ptrs.h
+++ b/security/nss/gtests/common/scoped_ptrs.h
@@ -14,16 +14,17 @@
 
 namespace nss_test {
 
 struct ScopedDelete {
   void operator()(CERTCertificate* cert) { CERT_DestroyCertificate(cert); }
   void operator()(CERTCertificateList* list) {
     CERT_DestroyCertificateList(list);
   }
+  void operator()(CERTCertList* list) { CERT_DestroyCertList(list); }
   void operator()(CERTSubjectPublicKeyInfo* spki) {
     SECKEY_DestroySubjectPublicKeyInfo(spki);
   }
   void operator()(PK11SlotInfo* slot) { PK11_FreeSlot(slot); }
   void operator()(PK11SymKey* key) { PK11_FreeSymKey(key); }
   void operator()(SECAlgorithmID* id) { SECOID_DestroyAlgorithmID(id, true); }
   void operator()(SECItem* item) { SECITEM_FreeItem(item, true); }
   void operator()(SECKEYPublicKey* key) { SECKEY_DestroyPublicKey(key); }
@@ -39,16 +40,17 @@ struct ScopedMaybeDelete {
     }
   }
 };
 
 #define SCOPED(x) typedef std::unique_ptr<x, ScopedMaybeDelete<x> > Scoped##x
 
 SCOPED(CERTCertificate);
 SCOPED(CERTCertificateList);
+SCOPED(CERTCertList);
 SCOPED(CERTSubjectPublicKeyInfo);
 SCOPED(PK11SlotInfo);
 SCOPED(PK11SymKey);
 SCOPED(SECAlgorithmID);
 SCOPED(SECItem);
 SCOPED(SECKEYPublicKey);
 SCOPED(SECKEYPrivateKey);
 
--- a/security/nss/gtests/ssl_gtest/databuffer.h
+++ b/security/nss/gtests/ssl_gtest/databuffer.h
@@ -131,17 +131,17 @@ class DataBuffer {
     len_ = index + ins_len + tail_len;
     data_ = new uint8_t[len_ ? len_ : 1];
 
     // The head of the old.
     if (old_value) {
       Write(0, old_value, std::min(old_len, index));
     }
     // Maybe a gap.
-    if (index > old_len) {
+    if (old_value && index > old_len) {
       memset(old_value + index, 0, index - old_len);
     }
     // The new.
     Write(index, ins, ins_len);
     // The tail of the old.
     if (tail_len > 0) {
       Write(index + ins_len, old_value + index + remove, tail_len);
     }
--- a/security/nss/gtests/ssl_gtest/ssl_dhe_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_dhe_unittest.cc
@@ -541,9 +541,69 @@ TEST_P(TlsConnectTls13, ResumeFfdhe) {
   server_->SetPacketFilter(serverCapture);
   ExpectResumption(RESUME_TICKET);
   Connect();
   CheckKeys(ssl_kea_dh, ssl_grp_ffdhe_2048, ssl_auth_rsa_sign, ssl_sig_none);
   ASSERT_LT(0UL, clientCapture->extension().len());
   ASSERT_LT(0UL, serverCapture->extension().len());
 }
 
+class TlsDheSkeChangeSignature : public TlsHandshakeFilter {
+ public:
+  TlsDheSkeChangeSignature(uint16_t version, const uint8_t* data, size_t len)
+      : version_(version), data_(data), len_(len) {}
+
+ protected:
+  virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
+                                               const DataBuffer& input,
+                                               DataBuffer* output) {
+    if (header.handshake_type() != kTlsHandshakeServerKeyExchange) {
+      return KEEP;
+    }
+
+    TlsParser parser(input);
+    EXPECT_TRUE(parser.SkipVariable(2));  // dh_p
+    EXPECT_TRUE(parser.SkipVariable(2));  // dh_g
+    EXPECT_TRUE(parser.SkipVariable(2));  // dh_Ys
+
+    // Copy DH params to output.
+    size_t offset = output->Write(0, input.data(), parser.consumed());
+
+    if (version_ == SSL_LIBRARY_VERSION_TLS_1_2) {
+      // Write signature algorithm.
+      offset = output->Write(offset, ssl_sig_dsa_sha256, 2);
+    }
+
+    // Write new signature.
+    offset = output->Write(offset, len_, 2);
+    offset = output->Write(offset, data_, len_);
+
+    return CHANGE;
+  }
+
+ private:
+  uint16_t version_;
+  const uint8_t* data_;
+  size_t len_;
+};
+
+TEST_P(TlsConnectGenericPre13, InvalidDERSignatureFfdhe) {
+  const uint8_t kBogusDheSignature[] = {
+      0x30, 0x69, 0x3c, 0x02, 0x1c, 0x7d, 0x0b, 0x2f, 0x64, 0x00, 0x27,
+      0xae, 0xcf, 0x1e, 0x28, 0x08, 0x6a, 0x7f, 0xb1, 0xbd, 0x78, 0xb5,
+      0x3b, 0x8c, 0x8f, 0x59, 0xed, 0x8f, 0xee, 0x78, 0xeb, 0x2c, 0xe9,
+      0x02, 0x1c, 0x6d, 0x7f, 0x3c, 0x0f, 0xf4, 0x44, 0x35, 0x0b, 0xb2,
+      0x6d, 0xdc, 0xb8, 0x21, 0x87, 0xdd, 0x0d, 0xb9, 0x46, 0x09, 0x3e,
+      0xef, 0x81, 0x5b, 0x37, 0x09, 0x39, 0xeb};
+
+  Reset(TlsAgent::kServerDsa);
+
+  const std::vector<SSLNamedGroup> client_groups = {ssl_grp_ffdhe_2048};
+  client_->ConfigNamedGroups(client_groups);
+
+  server_->SetPacketFilter(new TlsDheSkeChangeSignature(
+      version_, kBogusDheSignature, sizeof(kBogusDheSignature)));
+
+  ConnectExpectFail();
+  client_->CheckErrorCode(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE);
+}
+
 }  // namespace nss_test
--- a/security/nss/gtests/ssl_gtest/tls_agent.cc
+++ b/security/nss/gtests/ssl_gtest/tls_agent.cc
@@ -145,16 +145,20 @@ bool TlsAgent::EnsureTlsSetup(PRFileDesc
   if (rv != SECSuccess) return false;
 
   if (role_ == SERVER) {
     EXPECT_TRUE(ConfigServerCert(name_, true));
 
     rv = SSL_SNISocketConfigHook(ssl_fd_, SniHook, this);
     EXPECT_EQ(SECSuccess, rv);
     if (rv != SECSuccess) return false;
+
+    ScopedCERTCertList anchors(CERT_NewCertList());
+    rv = SSL_SetTrustAnchors(ssl_fd_, anchors.get());
+    if (rv != SECSuccess) return false;
   } else {
     rv = SSL_SetURL(ssl_fd_, "server");
     EXPECT_EQ(SECSuccess, rv);
     if (rv != SECSuccess) return false;
   }
 
   rv = SSL_AuthCertificateHook(ssl_fd_, AuthCertificateHook, this);
   EXPECT_EQ(SECSuccess, rv);
--- a/security/nss/lib/freebl/freebl.gyp
+++ b/security/nss/lib/freebl/freebl.gyp
@@ -368,16 +368,20 @@
           }],
           [ 'target_arch=="x64"', {
             'defines': [
               'MP_IS_LITTLE_ENDIAN',
               'NSS_BEVAND_ARCFOUR',
               'MPI_AMD64',
               'MP_ASSEMBLY_MULTIPLY',
               'NSS_USE_COMBA',
+            ],
+          }],
+          [ 'target_arch=="x64" and use_msan==0', {
+            'defines': [
               'USE_HW_AES',
               'INTEL_GCM',
             ],
           }],
           [ 'target_arch=="ia32"', {
             'defines': [
               'MP_IS_LITTLE_ENDIAN',
               'MP_ASSEMBLY_MULTIPLY',
--- a/security/nss/lib/nss/nss.h
+++ b/security/nss/lib/nss/nss.h
@@ -17,19 +17,19 @@
 
 /*
  * NSS's major version, minor version, patch level, build number, and whether
  * this is a beta release.
  *
  * The format of the version string should be
  *     "<major version>.<minor version>[.<patch level>[.<build number>]][ <ECC>][ <Beta>]"
  */
-#define NSS_VERSION "3.29" _NSS_CUSTOMIZED " Beta"
+#define NSS_VERSION "3.28" _NSS_CUSTOMIZED " Beta"
 #define NSS_VMAJOR 3
-#define NSS_VMINOR 29
+#define NSS_VMINOR 28
 #define NSS_VPATCH 0
 #define NSS_VBUILD 0
 #define NSS_BETA PR_TRUE
 
 #ifndef RC_INVOKED
 
 #include "seccomon.h"
 
--- a/security/nss/lib/softoken/softkver.h
+++ b/security/nss/lib/softoken/softkver.h
@@ -16,16 +16,16 @@
 
 /*
  * Softoken's major version, minor version, patch level, build number,
  * and whether this is a beta release.
  *
  * The format of the version string should be
  *     "<major version>.<minor version>[.<patch level>[.<build number>]][ <ECC>][ <Beta>]"
  */
-#define SOFTOKEN_VERSION "3.29" SOFTOKEN_ECC_STRING " Beta"
+#define SOFTOKEN_VERSION "3.28" SOFTOKEN_ECC_STRING " Beta"
 #define SOFTOKEN_VMAJOR 3
-#define SOFTOKEN_VMINOR 29
+#define SOFTOKEN_VMINOR 28
 #define SOFTOKEN_VPATCH 0
 #define SOFTOKEN_VBUILD 0
 #define SOFTOKEN_BETA PR_TRUE
 
 #endif /* _SOFTKVER_H_ */
--- a/security/nss/lib/ssl/ssl3con.c
+++ b/security/nss/lib/ssl/ssl3con.c
@@ -1249,17 +1249,17 @@ done:
 
 /* Called from ssl3_HandleServerKeyExchange, ssl3_HandleCertificateVerify */
 SECStatus
 ssl3_VerifySignedHashes(sslSocket *ss, SSLSignatureScheme scheme, SSL3Hashes *hash,
                         SECItem *buf)
 {
     SECKEYPublicKey *key;
     SECItem *signature = NULL;
-    SECStatus rv;
+    SECStatus rv = SECFailure;
     SECItem hashItem;
     SECOidTag encAlg;
     SECOidTag hashAlg;
     void *pwArg = ss->pkcs11PinArg;
     PRBool isRsaPssScheme = ssl_IsRsaPssSignatureScheme(scheme);
 
     PRINT_BUF(60, (NULL, "check signed hashes",
                    buf->data, buf->len));
@@ -1292,17 +1292,17 @@ ssl3_VerifySignedHashes(sslSocket *ss, S
                 hashItem.len = hash->len;
             }
             /* Allow DER encoded DSA signatures in SSL 3.0 */
             if (ss->ssl3.prSpec->version > SSL_LIBRARY_VERSION_3_0 ||
                 buf->len != SECKEY_SignatureLen(key)) {
                 signature = DSAU_DecodeDerSigToLen(buf, SECKEY_SignatureLen(key));
                 if (!signature) {
                     PORT_SetError(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE);
-                    return SECFailure;
+                    goto loser;
                 }
                 buf = signature;
             }
             if (scheme == ssl_sig_none) {
                 scheme = ssl_sig_dsa_sha1;
             }
             break;
 
@@ -1323,19 +1323,18 @@ ssl3_VerifySignedHashes(sslSocket *ss, S
                 hashItem.len = hash->len;
             }
             if (scheme == ssl_sig_none) {
                 scheme = ssl_sig_ecdsa_sha1;
             }
             break;
 
         default:
-            SECKEY_DestroyPublicKey(key);
             PORT_SetError(SEC_ERROR_UNSUPPORTED_KEYALG);
-            return SECFailure;
+            goto loser;
     }
 
     PRINT_BUF(60, (NULL, "hash(es) to be verified",
                    hashItem.data, hashItem.len));
 
     if (isRsaPssScheme ||
         hashAlg == SEC_OID_UNKNOWN ||
         SECKEY_GetPublicKeyType(key) == dsaKey) {
@@ -1360,30 +1359,32 @@ ssl3_VerifySignedHashes(sslSocket *ss, S
             mech = CKM_RSA_PKCS_PSS;
         }
 
         rv = PK11_VerifyWithMechanism(key, mech, params, buf, &hashItem, pwArg);
     } else {
         rv = VFY_VerifyDigestDirect(&hashItem, key, buf, encAlg, hashAlg,
                                     pwArg);
     }
-    SECKEY_DestroyPublicKey(key);
     if (signature) {
         SECITEM_FreeItem(signature, PR_TRUE);
     }
-#ifdef UNSAFE_FUZZER_MODE
-    rv = SECSuccess;
-    PORT_SetError(0);
-#endif
     if (rv != SECSuccess) {
         ssl_MapLowLevelError(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE);
     }
     if (!ss->sec.isServer) {
         ss->sec.signatureScheme = scheme;
     }
+
+loser:
+    SECKEY_DestroyPublicKey(key);
+#ifdef UNSAFE_FUZZER_MODE
+    rv = SECSuccess;
+    PORT_SetError(0);
+#endif
     return rv;
 }
 
 /* Caller must set hiLevel error code. */
 /* Called from ssl3_ComputeDHKeyHash
  * which are called from ssl3_HandleServerKeyExchange.
  *
  * hashAlg: ssl_hash_none indicates the pre-1.2, MD5/SHA1 combination hash.
@@ -6499,24 +6500,23 @@ ssl3_SendCertificateVerify(sslSocket *ss
     }
     if (ss->ssl3.hs.hashType == handshake_hash_record &&
         hashAlg != ssl3_GetSuitePrfHash(ss)) {
         rv = ssl3_ComputeHandshakeHash(ss->ssl3.hs.messages.buf,
                                        ss->ssl3.hs.messages.len,
                                        hashAlg, &hashes);
         if (rv != SECSuccess) {
             ssl_MapLowLevelError(SSL_ERROR_DIGEST_FAILURE);
-            goto done;
         }
     } else {
         rv = ssl3_ComputeHandshakeHashes(ss, ss->ssl3.pwSpec, &hashes, 0);
     }
     ssl_ReleaseSpecReadLock(ss);
     if (rv != SECSuccess) {
-        goto done; /* err code was set by ssl3_ComputeHandshakeHashes */
+        goto done; /* err code was set by ssl3_ComputeHandshakeHash(es) */
     }
 
     isTLS12 = (PRBool)(ss->version == SSL_LIBRARY_VERSION_TLS_1_2);
     PORT_Assert(ss->version <= SSL_LIBRARY_VERSION_TLS_1_2);
 
     rv = ssl3_SignHashes(ss, &hashes, privKey, &buf);
     if (rv == SECSuccess && !ss->sec.isServer) {
         /* Remember the info about the slot that did the signing.
@@ -9448,20 +9448,17 @@ ssl3_SendDHServerKeyExchange(sslSocket *
         goto loser;
     }
 
     certPrivateKey = ss->sec.serverCert->serverKeyPair->privKey;
     rv = ssl3_SignHashes(ss, &hashes, certPrivateKey, &signed_hash);
     if (rv != SECSuccess) {
         goto loser; /* ssl3_SignHashes has set err. */
     }
-    if (signed_hash.data == NULL) {
-        PORT_SetError(SSL_ERROR_SERVER_KEY_EXCHANGE_FAILURE);
-        goto loser;
-    }
+
     length = 2 + pubKey->u.dh.prime.len +
              2 + pubKey->u.dh.base.len +
              2 + pubKey->u.dh.prime.len +
              2 + signed_hash.len;
 
     if (ss->ssl3.pwSpec->version >= SSL_LIBRARY_VERSION_TLS_1_2) {
         length += 2;
     }
@@ -13189,16 +13186,19 @@ ssl3_DestroySSL3Info(sslSocket *ss)
 
     if (ss->ssl3.peerCertArena != NULL)
         ssl3_CleanupPeerCerts(ss);
 
     if (ss->ssl3.clientCertChain != NULL) {
         CERT_DestroyCertificateList(ss->ssl3.clientCertChain);
         ss->ssl3.clientCertChain = NULL;
     }
+    if (ss->ssl3.ca_list) {
+        CERT_FreeDistNames(ss->ssl3.ca_list);
+    }
 
     /* clean up handshake */
     if (ss->ssl3.hs.md5) {
         PK11_DestroyContext(ss->ssl3.hs.md5, PR_TRUE);
     }
     if (ss->ssl3.hs.sha) {
         PK11_DestroyContext(ss->ssl3.hs.sha, PR_TRUE);
     }
--- a/security/nss/lib/ssl/ssl3ecc.c
+++ b/security/nss/lib/ssl/ssl3ecc.c
@@ -678,17 +678,17 @@ loser:
 
 SECStatus
 ssl3_SendECDHServerKeyExchange(sslSocket *ss)
 {
     SECStatus rv = SECFailure;
     int length;
     PRBool isTLS12;
     SECItem signed_hash = { siBuffer, NULL, 0 };
-    SSLHashType hashAlg = ssl_hash_none;
+    SSLHashType hashAlg;
     SSL3Hashes hashes;
 
     SECItem ec_params = { siBuffer, NULL, 0 };
     unsigned char paramBuf[3];
     const sslNamedGroupDef *ecGroup;
     sslEphemeralKeyPair *keyPair;
     SECKEYPublicKey *pubKey;
 
@@ -746,21 +746,16 @@ ssl3_SendECDHServerKeyExchange(sslSocket
 
     isTLS12 = (PRBool)(ss->ssl3.pwSpec->version >= SSL_LIBRARY_VERSION_TLS_1_2);
 
     rv = ssl3_SignHashes(ss, &hashes,
                          ss->sec.serverCert->serverKeyPair->privKey, &signed_hash);
     if (rv != SECSuccess) {
         goto loser; /* ssl3_SignHashes has set err. */
     }
-    if (signed_hash.data == NULL) {
-        /* how can this happen and rv == SECSuccess ?? */
-        PORT_SetError(SSL_ERROR_SERVER_KEY_EXCHANGE_FAILURE);
-        goto loser;
-    }
 
     length = ec_params.len +
              1 + pubKey->u.ec.publicValue.len +
              (isTLS12 ? 2 : 0) + 2 + signed_hash.len;
 
     rv = ssl3_AppendHandshakeHeader(ss, server_key_exchange, length);
     if (rv != SECSuccess) {
         goto loser; /* err set by AppendHandshake. */
--- a/security/nss/lib/ssl/tls13con.c
+++ b/security/nss/lib/ssl/tls13con.c
@@ -380,16 +380,17 @@ SSL_SendAdditionalKeyShares(PRFileDesc *
  * the requisite type and creates a share for that.
  *
  * Called from ssl3_SendClientHello.
  */
 SECStatus
 tls13_SetupClientHello(sslSocket *ss)
 {
     unsigned int i;
+    SSL3Statistics *ssl3stats = SSL_GetStatistics();
     NewSessionTicket *session_ticket = NULL;
     sslSessionID *sid = ss->sec.ci.sid;
     unsigned int numShares = 0;
 
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
     PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss));
     PORT_Assert(PR_CLIST_IS_EMPTY(&ss->ephemeralKeyPairs));
 
@@ -430,25 +431,32 @@ tls13_SetupClientHello(sslSocket *ss)
              session_ticket->received_timestamp >
          ssl_Time())) {
         ss->statelessResume = PR_TRUE;
     }
 
     if (ss->statelessResume) {
         SECStatus rv;
 
+        PORT_Assert(ss->sec.ci.sid);
         rv = tls13_RecoverWrappedSharedSecret(ss, ss->sec.ci.sid);
         if (rv != SECSuccess) {
             FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
+            SSL_AtomicIncrementLong(&ssl3stats->sch_sid_cache_not_ok);
+            ss->sec.uncache(ss->sec.ci.sid);
+            ssl_FreeSID(ss->sec.ci.sid);
+            ss->sec.ci.sid = NULL;
             return SECFailure;
         }
 
         rv = ssl3_SetCipherSuite(ss, ss->sec.ci.sid->u.ssl3.cipherSuite, PR_FALSE);
-        if (rv != SECSuccess)
+        if (rv != SECSuccess) {
+            FATAL_ERROR(ss, PORT_GetError(), internal_error);
             return SECFailure;
+        }
 
         rv = tls13_ComputeEarlySecrets(ss);
         if (rv != SECSuccess) {
             FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
             return SECFailure;
         }
     }
 
@@ -1329,16 +1337,17 @@ tls13_HandleClientHelloPart2(sslSocket *
                                 &ss->ssl3.hs.srvVirtName) != SECEqual) {
             FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO,
                         handshake_failure);
             goto loser;
         }
 
         rv = tls13_RecoverWrappedSharedSecret(ss, sid);
         if (rv != SECSuccess) {
+            SSL_AtomicIncrementLong(&ssl3stats->hch_sid_cache_not_ok);
             FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
             goto loser;
         }
         tls13_RestoreCipherInfo(ss, sid);
 
         ss->sec.serverCert = ssl_FindServerCert(ss, &sid->certType);
         PORT_Assert(ss->sec.serverCert);
         ss->sec.localCert = CERT_DupCertificate(ss->sec.serverCert->serverCert);
@@ -1994,29 +2003,17 @@ tls13_HandleServerHelloPart2(sslSocket *
     }
 
     /* Now create a synthetic kea_def that we can tweak. */
     ss->ssl3.hs.kea_def_mutable = *ss->ssl3.hs.kea_def;
     ss->ssl3.hs.kea_def = &ss->ssl3.hs.kea_def_mutable;
 
     if (ss->statelessResume) {
         /* PSK */
-        PRBool cacheOK = PR_FALSE;
-        do {
-            ss->ssl3.hs.kea_def_mutable.authKeyType = ssl_auth_psk;
-
-            cacheOK = PR_TRUE;
-        } while (0);
-
-        if (!cacheOK) {
-            SSL_AtomicIncrementLong(&ssl3stats->hsh_sid_cache_not_ok);
-            ss->sec.uncache(sid);
-            return SECFailure;
-        }
-
+        ss->ssl3.hs.kea_def_mutable.authKeyType = ssl_auth_psk;
         tls13_RestoreCipherInfo(ss, sid);
         if (sid->peerCert) {
             ss->sec.peerCert = CERT_DupCertificate(sid->peerCert);
         }
 
         SSL_AtomicIncrementLong(&ssl3stats->hsh_sid_cache_hits);
         SSL_AtomicIncrementLong(&ssl3stats->hsh_sid_stateless_resumes);
     } else {
--- a/security/nss/lib/ssl/tls13exthandle.c
+++ b/security/nss/lib/ssl/tls13exthandle.c
@@ -528,20 +528,18 @@ tls13_ClientSendPreSharedKeyXtn(const ss
         rv = ssl3_ExtAppendHandshakeVariable(ss,
                                              binder, binderLen, 1);
         if (rv != SECSuccess)
             goto loser;
 
         PRINT_BUF(50, (ss, "Sending PreSharedKey value",
                        session_ticket->ticket.data,
                        session_ticket->ticket.len));
+
         xtnData->sentSessionTicketInClientHello = PR_TRUE;
-        if (rv != SECSuccess)
-            goto loser;
-
         xtnData->advertised[xtnData->numAdvertised++] =
             ssl_tls13_pre_shared_key_xtn;
     }
     return extension_length;
 
 loser:
     xtnData->ticketTimestampVerified = PR_FALSE;
     return -1;
--- a/security/nss/lib/util/nssutil.h
+++ b/security/nss/lib/util/nssutil.h
@@ -14,19 +14,19 @@
 
 /*
  * NSS utilities's major version, minor version, patch level, build number,
  * and whether this is a beta release.
  *
  * The format of the version string should be
  *     "<major version>.<minor version>[.<patch level>[.<build number>]][ <Beta>]"
  */
-#define NSSUTIL_VERSION "3.29 Beta"
+#define NSSUTIL_VERSION "3.28 Beta"
 #define NSSUTIL_VMAJOR 3
-#define NSSUTIL_VMINOR 29
+#define NSSUTIL_VMINOR 28
 #define NSSUTIL_VPATCH 0
 #define NSSUTIL_VBUILD 0
 #define NSSUTIL_BETA PR_TRUE
 
 SEC_BEGIN_PROTOS
 
 /*
  * Returns a const string of the UTIL library version.
--- a/taskcluster/scripts/builder/build-linux.sh
+++ b/taskcluster/scripts/builder/build-linux.sh
@@ -45,16 +45,21 @@ export TINDERBOX_OUTPUT=1
 export MOZ_SIMPLE_PACKAGE_NAME=target
 
 # Do not try to upload symbols (see https://bugzilla.mozilla.org/show_bug.cgi?id=1164615)
 export MOZ_AUTOMATION_UPLOAD_SYMBOLS=0
 
 # Ensure that in tree libraries can be found
 export LIBRARY_PATH=$LIBRARY_PATH:$WORKSPACE/src/obj-firefox:$WORKSPACE/src/gcc/lib64
 
+if [[ -n ${USE_SCCACHE} ]]; then
+    # Point sccache at the Taskcluster proxy for AWS credentials.
+    export AWS_IAM_CREDENTIALS_URL="http://taskcluster/auth/v1/aws/s3/read-write/taskcluster-level-${MOZ_SCM_LEVEL}-sccache-${TASKCLUSTER_WORKER_GROUP%?}/?format=iam-role-compat"
+fi
+
 # test required parameters are supplied
 if [[ -z ${MOZHARNESS_SCRIPT} ]]; then fail "MOZHARNESS_SCRIPT is not set"; fi
 if [[ -z ${MOZHARNESS_CONFIG} ]]; then fail "MOZHARNESS_CONFIG is not set"; fi
 
 cleanup() {
     local rv=$?
     cleanup_xvfb
     exit $rv
--- a/taskcluster/taskgraph/transforms/build.py
+++ b/taskcluster/taskgraph/transforms/build.py
@@ -14,16 +14,17 @@ transforms = TransformSequence()
 
 
 @transforms.add
 def set_defaults(config, jobs):
     """Set defaults, including those that differ per worker implementation"""
     for job in jobs:
         job['treeherder'].setdefault('kind', 'build')
         job['treeherder'].setdefault('tier', 1)
+        job.setdefault('needs-sccache', True)
         if job['worker']['implementation'] in ('docker-worker', 'docker-engine'):
             job['worker'].setdefault('docker-image', {'in-tree': 'desktop-build'})
             job['worker']['chain-of-trust'] = True
             job.setdefault('extra', {})
             job['extra'].setdefault('chainOfTrust', {})
             job['extra']['chainOfTrust'].setdefault('inputs', {})
             job['extra']['chainOfTrust']['inputs']['docker-image'] = {
                 "task-reference": "<docker-image>"
--- a/taskcluster/taskgraph/transforms/job/__init__.py
+++ b/taskcluster/taskgraph/transforms/job/__init__.py
@@ -48,16 +48,17 @@ job_description_schema = Schema({
     Optional('routes'): task_description_schema['routes'],
     Optional('scopes'): task_description_schema['scopes'],
     Optional('extra'): task_description_schema['extra'],
     Optional('treeherder'): task_description_schema['treeherder'],
     Optional('index'): task_description_schema['index'],
     Optional('run-on-projects'): task_description_schema['run-on-projects'],
     Optional('coalesce-name'): task_description_schema['coalesce-name'],
     Optional('worker-type'): task_description_schema['worker-type'],
+    Optional('needs-sccache'): task_description_schema['needs-sccache'],
     Required('worker'): task_description_schema['worker'],
     Optional('when'): task_description_schema['when'],
 
     # A description of how to run this job.
     'run': {
         # The key to a job implementation in a peer module to this one
         'using': basestring,
 
--- a/taskcluster/taskgraph/transforms/job/mozharness.py
+++ b/taskcluster/taskgraph/transforms/job/mozharness.py
@@ -215,12 +215,25 @@ def mozharness_on_windows(config, job, t
     hg_command.append('robustcheckout')
     hg_command.extend(['--sharebase', 'y:\\hg-shared'])
     hg_command.append('--purge')
     hg_command.extend(['--upstream', 'https://hg.mozilla.org/mozilla-unified'])
     hg_command.extend(['--revision', env['GECKO_HEAD_REV']])
     hg_command.append(env['GECKO_HEAD_REPOSITORY'])
     hg_command.append('.\\build\\src')
 
-    worker['command'] = [
+    worker['command'] = []
+    # sccache currently uses the full compiler commandline as input to the
+    # cache hash key, so create a symlink to the task dir and build from
+    # the symlink dir to get consistent paths.
+    if taskdesc.get('needs-sccache'):
+        worker['command'].extend([
+            r'if exist z:\build rmdir z:\build',
+            r'mklink /d z:\build %cd%',
+            # Grant delete permission on the link to everyone.
+            r'icacls z:\build /grant *S-1-1-0:D /L',
+            r'cd /d z:\build',
+        ])
+
+    worker['command'].extend([
         ' '.join(hg_command),
         ' '.join(mh_command)
-    ]
+    ])
--- a/taskcluster/taskgraph/transforms/task.py
+++ b/taskcluster/taskgraph/transforms/task.py
@@ -135,16 +135,19 @@ task_description_schema = Schema({
     # tasks are never coalesced
     Optional('coalesce-name'): basestring,
 
     # the provisioner-id/worker-type for the task.  The following parameters will
     # be substituted in this string:
     #  {level} -- the scm level of this push
     'worker-type': basestring,
 
+    # Whether the job should use sccache compiler caching.
+    Required('needs-sccache', default=False): bool,
+
     # information specific to the worker implementation that will run this task
     'worker': Any({
         Required('implementation'): Any('docker-worker', 'docker-engine'),
 
         # For tasks that will run in docker-worker or docker-engine, this is the
         # name of the docker image or in-tree docker image to run the task in.  If
         # in-tree, then a dependency will be created automatically.  This is
         # generally `desktop-test`, or an image that acts an awful lot like it.
@@ -365,16 +368,24 @@ def build_docker_worker_payload(config, 
 
     if worker.get('allow-ptrace'):
         features['allowPtrace'] = True
         task_def['scopes'].append('docker-worker:feature:allowPtrace')
 
     if worker.get('chain-of-trust'):
         features['chainOfTrust'] = True
 
+    if task.get('needs-sccache'):
+        features['taskclusterProxy'] = True
+        task_def['scopes'].append(
+            'assume:project:taskcluster:level-{level}-sccache-buckets'.format(
+                level=config.params['level'])
+        )
+        worker['env']['USE_SCCACHE'] = '1'
+
     capabilities = {}
 
     for lo in 'audio', 'video':
         if worker.get('loopback-' + lo):
             capitalized = 'loopback' + lo.capitalize()
             devices = capabilities.setdefault('devices', {})
             devices[capitalized] = True
             task_def['scopes'].append('docker-worker:capability:device:' + capitalized)
@@ -446,16 +457,18 @@ def build_generic_worker_payload(config,
         'command': worker['command'],
         'artifacts': artifacts,
         'env': worker.get('env', {}),
         'mounts': mounts,
         'maxRunTime': worker['max-run-time'],
         'osGroups': worker.get('os-groups', []),
     }
 
+    # needs-sccache is handled in mozharness_on_windows
+
     if 'retry-exit-status' in worker:
         raise Exception("retry-exit-status not supported in generic-worker")
 
 
 @payload_builder('macosx-engine')
 def build_macosx_engine_payload(config, task, task_def):
     worker = task['worker']
     artifacts = map(lambda artifact: {
@@ -467,16 +480,19 @@ def build_macosx_engine_payload(config, 
 
     task_def['payload'] = {
         'link': worker['link'],
         'command': worker['command'],
         'env': worker['env'],
         'artifacts': artifacts,
     }
 
+    if task.get('needs-sccache'):
+        raise Exception('needs-sccache not supported in macosx-engine')
+
 
 @payload_builder('buildbot-bridge')
 def build_buildbot_bridge_payload(config, task, task_def):
     worker = task['worker']
     task_def['payload'] = {
         'buildername': worker['buildername'],
         'sourcestamp': worker['sourcestamp'],
         'properties': worker['properties'],
--- a/toolkit/components/extensions/ext-backgroundPage.js
+++ b/toolkit/components/extensions/ext-backgroundPage.js
@@ -2,16 +2,18 @@
 
 var {interfaces: Ci, utils: Cu} = Components;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
                                   "resource://gre/modules/AddonManager.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
+                                  "resource://gre/modules/PrivateBrowsingUtils.jsm");
 
 Cu.import("resource://gre/modules/ExtensionUtils.jsm");
 const {
   promiseDocumentLoaded,
   promiseEvent,
   promiseObserved,
 } = ExtensionUtils;
 
@@ -87,16 +89,22 @@ class BackgroundPageBase {
         }
       }
 
       this.extension.emit("startup");
     }.bind(this));
   }
 
   initParentWindow(chromeShell) {
+    if (PrivateBrowsingUtils.permanentPrivateBrowsing) {
+      let attrs = chromeShell.getOriginAttributes();
+      attrs.privateBrowsingId = 1;
+      chromeShell.setOriginAttributes(attrs);
+    }
+
     let system = Services.scriptSecurityManager.getSystemPrincipal();
     chromeShell.createAboutBlankContentViewer(system);
     chromeShell.useGlobalHistory = false;
     chromeShell.loadURI(XUL_URL, 0, null, null, null);
 
     return promiseObserved("chrome-document-global-created",
                            win => win.document == chromeShell.document);
   }
--- a/toolkit/components/extensions/ext-c-backgroundPage.js
+++ b/toolkit/components/extensions/ext-c-backgroundPage.js
@@ -20,20 +20,21 @@ global.initializeBackgroundPage = (conte
     contentWindow.console.log(text);
   };
   Cu.exportFunction(alertOverwrite, contentWindow, {defineAs: "alert"});
 };
 
 extensions.registerSchemaAPI("extension", "addon_child", context => {
   function getBackgroundPage() {
     for (let view of context.extension.views) {
-      if (view.viewType == "background") {
+      if (view.viewType == "background" && context.principal.subsumes(view.principal)) {
         return view.contentWindow;
       }
     }
+    return null;
   }
   return {
     extension: {
       getBackgroundPage,
     },
 
     runtime: {
       getBackgroundPage() {
--- a/toolkit/components/extensions/ext-c-extension.js
+++ b/toolkit/components/extensions/ext-c-extension.js
@@ -29,16 +29,20 @@ extensions.registerSchemaAPI("extension"
     extension: {
       getViews: function(fetchProperties) {
         let result = Cu.cloneInto([], context.cloneScope);
 
         for (let view of context.extension.views) {
           if (!view.active) {
             continue;
           }
+          if (!context.principal.subsumes(view.principal)) {
+            continue;
+          }
+
           if (fetchProperties !== null) {
             if (fetchProperties.type !== null && view.viewType != fetchProperties.type) {
               continue;
             }
 
             if (fetchProperties.windowId !== null && view.windowId != fetchProperties.windowId) {
               continue;
             }
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_background_private_browsing.js
@@ -0,0 +1,40 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+Cu.import("resource://gre/modules/Preferences.jsm");
+
+function* testBackgroundPage(expected) {
+  let extension = ExtensionTestUtils.loadExtension({
+    async background() {
+      browser.test.assertEq(window, browser.extension.getBackgroundPage(),
+                            "Caller should be able to access itself as a background page");
+      browser.test.assertEq(window, await browser.runtime.getBackgroundPage(),
+                            "Caller should be able to access itself as a background page");
+
+      browser.test.sendMessage("incognito", browser.extension.inIncognitoContext);
+    },
+  });
+
+  yield extension.startup();
+
+  let incognito = yield extension.awaitMessage("incognito");
+  equal(incognito, expected.incognito, "Expected incognito value");
+
+  yield extension.unload();
+}
+
+add_task(function* test_background_incognito() {
+  do_print("Test background page incognito value with permanent private browsing disabled");
+
+  yield testBackgroundPage({incognito: false});
+
+  do_print("Test background page incognito value with permanent private browsing enabled");
+
+  Preferences.set("browser.privatebrowsing.autostart", true);
+  do_register_cleanup(() => {
+    Preferences.reset("browser.privatebrowsing.autostart");
+  });
+
+  yield testBackgroundPage({incognito: true});
+});
--- a/toolkit/components/extensions/test/xpcshell/xpcshell.ini
+++ b/toolkit/components/extensions/test/xpcshell/xpcshell.ini
@@ -14,16 +14,17 @@ tags = webextensions
 [test_ext_alarms_periodic.js]
 [test_ext_alarms_replaces.js]
 [test_ext_apimanager.js]
 [test_ext_api_permissions.js]
 [test_ext_background_generated_load_events.js]
 [test_ext_background_generated_reload.js]
 [test_ext_background_global_history.js]
 skip-if = os == "android" # Android does not use Places for history.
+[test_ext_background_private_browsing.js]
 [test_ext_background_runtime_connect_params.js]
 [test_ext_background_sub_windows.js]
 [test_ext_background_window_properties.js]
 skip-if = os == "android"
 [test_ext_contexts.js]
 [test_ext_downloads.js]
 [test_ext_downloads_download.js]
 skip-if = os == "android"
--- a/toolkit/crashreporter/tools/symbolstore.py
+++ b/toolkit/crashreporter/tools/symbolstore.py
@@ -420,17 +420,17 @@ class Dumper:
         # popen likes absolute paths, at least on windows
         self.dump_syms = os.path.abspath(dump_syms)
         self.symbol_path = symbol_path
         if archs is None:
             # makes the loop logic simpler
             self.archs = ['']
         else:
             self.archs = ['-a %s' % a for a in archs.split()]
-        self.srcdirs = [os.path.normpath(a) for a in srcdirs]
+        self.srcdirs = [os.path.normpath(self.FixFilenameCase(a)) for a in srcdirs]
         self.copy_debug = copy_debug
         self.vcsinfo = vcsinfo
         self.srcsrv = srcsrv
         self.exclude = exclude[:]
         if repo_manifest:
             self.parse_repo_manifest(repo_manifest)
         self.file_mapping = file_mapping or {}
 
@@ -758,17 +758,20 @@ class Dumper_Win32(Dumper):
         handle = ctypes.windll.kernel32.CreateFileW(file,
                                                     # GENERIC_READ
                                                     0x80000000,
                                                     # FILE_SHARE_READ
                                                     1,
                                                     None,
                                                     # OPEN_EXISTING
                                                     3,
-                                                    0,
+                                                    # FILE_FLAG_BACKUP_SEMANTICS
+                                                    # This is necessary to open
+                                                    # directory handles.
+                                                    0x02000000,
                                                     None)
         if handle != -1:
             size = ctypes.windll.kernel32.GetFinalPathNameByHandleW(handle,
                                                                     None,
                                                                     0,
                                                                     0)
             buf = ctypes.create_unicode_buffer(size)
             if ctypes.windll.kernel32.GetFinalPathNameByHandleW(handle,
--- a/tools/profiler/core/GeckoSampler.cpp
+++ b/tools/profiler/core/GeckoSampler.cpp
@@ -439,16 +439,25 @@ void GeckoSampler::ToJSObjectAsync(doubl
 {
   if (NS_WARN_IF(!mGatherer)) {
     return;
   }
 
   mGatherer->Start(aSinceTime, aPromise);
 }
 
+void GeckoSampler::ToFileAsync(const nsACString& aFileName, double aSinceTime)
+{
+  if (NS_WARN_IF(!mGatherer)) {
+    return;
+  }
+
+  mGatherer->Start(aSinceTime, aFileName);
+}
+
 struct SubprocessClosure {
   explicit SubprocessClosure(SpliceableJSONWriter* aWriter)
     : mWriter(aWriter)
   {}
 
   SpliceableJSONWriter* mWriter;
 };
 
--- a/tools/profiler/core/GeckoSampler.h
+++ b/tools/profiler/core/GeckoSampler.h
@@ -107,16 +107,17 @@ class GeckoSampler: public Sampler {
 
   void ToStreamAsJSON(std::ostream& stream, double aSinceTime = 0);
 #ifndef SPS_STANDALONE
   virtual JSObject *ToJSObject(JSContext *aCx, double aSinceTime = 0);
   void GetGatherer(nsISupports** aRetVal);
 #endif
   mozilla::UniquePtr<char[]> ToJSON(double aSinceTime = 0);
   virtual void ToJSObjectAsync(double aSinceTime = 0, mozilla::dom::Promise* aPromise = 0);
+  void ToFileAsync(const nsACString& aFileName, double aSinceTime = 0);
   void StreamMetaJSCustomObject(SpliceableJSONWriter& aWriter);
   void StreamTaskTracer(SpliceableJSONWriter& aWriter);
   void FlushOnJSShutdown(JSContext* aContext);
   bool ProfileJS() const { return mProfileJS; }
   bool ProfileJava() const { return mProfileJava; }
   bool ProfileGPU() const { return mProfileGPU; }
   bool ProfilePower() const { return mProfilePower; }
   bool ProfileThreads() const override { return mProfileThreads; }
--- a/tools/profiler/core/platform.cpp
+++ b/tools/profiler/core/platform.cpp
@@ -619,16 +619,30 @@ void mozilla_sampler_get_profile_data_as
   GeckoSampler *t = tlsTicker.get();
   if (NS_WARN_IF(!t)) {
     return;
   }
 
   t->ToJSObjectAsync(aSinceTime, aPromise);
 }
 
+void mozilla_sampler_save_profile_to_file_async(double aSinceTime,
+						const char* aFileName)
+{
+  nsCString filename(aFileName);
+  NS_DispatchToMainThread(NS_NewRunnableFunction([=] () {
+	GeckoSampler *t = tlsTicker.get();
+	if (NS_WARN_IF(!t)) {
+	  return;
+	}
+
+	t->ToFileAsync(filename, aSinceTime);
+      }));
+}
+
 void mozilla_sampler_get_profiler_start_params(int* aEntrySize,
                                                double* aInterval,
                                                mozilla::Vector<const char*>* aFilters,
                                                mozilla::Vector<const char*>* aFeatures)
 {
   if (NS_WARN_IF(!aEntrySize) || NS_WARN_IF(!aInterval) ||
       NS_WARN_IF(!aFilters) || NS_WARN_IF(!aFeatures)) {
     return;
--- a/tools/profiler/gecko/ProfileGatherer.cpp
+++ b/tools/profiler/gecko/ProfileGatherer.cpp
@@ -1,16 +1,20 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
+
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/ProfileGatherer.h"
 #include "mozilla/Services.h"
 #include "nsIObserverService.h"
+#include "nsIProfileSaveEvent.h"
 #include "GeckoSampler.h"
+#include "nsLocalFile.h"
+#include "nsIFileStreams.h"
 
 using mozilla::dom::AutoJSAPI;
 using mozilla::dom::Promise;
 
 namespace mozilla {
 
 /**
  * When a subprocess exits before we've gathered profiles, we'll
@@ -37,17 +41,17 @@ ProfileGatherer::GatheredOOPProfile()
   MOZ_ASSERT(NS_IsMainThread());
   if (!mGathering) {
     // If we're not actively gathering, then we don't actually
     // care that we gathered a profile here. This can happen for
     // processes that exit while profiling.
     return;
   }
 
-  if (NS_WARN_IF(!mPromise)) {
+  if (NS_WARN_IF(!mPromise && !mFile)) {
     // If we're not holding on to a Promise, then someone is
     // calling us erroneously.
     return;
   }
 
   mPendingProfiles--;
 
   if (mPendingProfiles == 0) {
@@ -92,28 +96,79 @@ ProfileGatherer::Start(double aSinceTime
   }
 
   if (!mPendingProfiles) {
     Finish();
   }
 }
 
 void
+ProfileGatherer::Start(double aSinceTime,
+                       nsIFile* aFile)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  if (mGathering) {
+    return;
+  }
+
+  mSinceTime = aSinceTime;
+  mFile = aFile;
+  mGathering = true;
+  mPendingProfiles = 0;
+
+  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+  if (os) {
+    DebugOnly<nsresult> rv =
+      os->AddObserver(this, "profiler-subprocess", false);
+    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "AddObserver failed");
+    rv = os->NotifyObservers(this, "profiler-subprocess-gather", nullptr);
+    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NotifyObservers failed");
+  }
+
+  if (!mPendingProfiles) {
+    Finish();
+  }
+}
+
+void
+ProfileGatherer::Start(double aSinceTime,
+                       const nsACString& aFileName)
+{
+  nsCOMPtr<nsIFile> file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
+  nsresult rv = file->InitWithNativePath(aFileName);
+  if (NS_FAILED(rv)) {
+    MOZ_CRASH();
+  }
+  Start(aSinceTime, file);
+}
+
+void
 ProfileGatherer::Finish()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!mTicker) {
     // We somehow got called after we were cancelled! This shouldn't
     // be possible, but doing a belt-and-suspenders check to be sure.
     return;
   }
 
   UniquePtr<char[]> buf = mTicker->ToJSON(mSinceTime);
 
+  if (mFile) {
+    nsCOMPtr<nsIFileOutputStream> of =
+      do_CreateInstance("@mozilla.org/network/file-output-stream;1");
+    of->Init(mFile, -1, -1, 0);
+    uint32_t sz;
+    of->Write(buf.get(), strlen(buf.get()), &sz);
+    of->Close();
+    Reset();
+    return;
+  }
+
   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   if (os) {
     DebugOnly<nsresult> rv = os->RemoveObserver(this, "profiler-subprocess");
     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "RemoveObserver failed");
   }
 
   AutoJSAPI jsapi;
   if (NS_WARN_IF(!jsapi.Init(mPromise->GlobalJSObject()))) {
@@ -148,28 +203,31 @@ ProfileGatherer::Finish()
   Reset();
 }
 
 void
 ProfileGatherer::Reset()
 {
   mSinceTime = 0;
   mPromise = nullptr;
+  mFile = nullptr;
   mPendingProfiles = 0;
   mGathering = false;
 }
 
 void
 ProfileGatherer::Cancel()
 {
   // The GeckoSampler is going away. If we have a Promise in flight, we
   // should reject it.
   if (mPromise) {
     mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
   }
+  mPromise = nullptr;
+  mFile = nullptr;
 
   // Clear out the GeckoSampler reference, since it's being destroyed.
   mTicker = nullptr;
 }
 
 void
 ProfileGatherer::OOPExitProfile(const nsCString& aProfile)
 {
--- a/tools/profiler/moz.build
+++ b/tools/profiler/moz.build
@@ -34,21 +34,28 @@ if CONFIG['MOZ_ENABLE_PROFILER_SPS']:
         'core/ProfilerMarkers.cpp',
         'core/StackTop.cpp',
         'core/SyncProfile.cpp',
         'core/ThreadInfo.cpp',
         'core/ThreadProfile.cpp',
         'gecko/nsProfiler.cpp',
         'gecko/nsProfilerFactory.cpp',
         'gecko/nsProfilerStartParams.cpp',
-        'gecko/ProfileGatherer.cpp',
         'gecko/ProfilerIOInterposeObserver.cpp',
         'gecko/SaveProfileTask.cpp',
         'gecko/ThreadResponsiveness.cpp',
     ]
+    if CONFIG['OS_TARGET'] == 'Darwin':
+        SOURCES += [
+            'gecko/ProfileGatherer.cpp',
+        ]
+    else:
+        UNIFIED_SOURCES += [
+            'gecko/ProfileGatherer.cpp',
+        ]
 
     if CONFIG['OS_TARGET'] in ('Android', 'Linux'):
         UNIFIED_SOURCES += [
             'lul/AutoObjectMapper.cpp',
             'lul/LulCommon.cpp',
             'lul/LulDwarf.cpp',
             'lul/LulDwarfSummariser.cpp',
             'lul/LulElf.cpp',
@@ -123,16 +130,17 @@ include('/ipc/chromium/chromium-config.m
 EXPORTS += [
     'public/GeckoProfiler.h',
 ]
 
 if CONFIG['MOZ_TASK_TRACER']:
     EXPORTS += [
         'tasktracer/GeckoTaskTracer.h',
         'tasktracer/GeckoTaskTracerImpl.h',
+        'tasktracer/SourceEventTypeMap.h',
         'tasktracer/TracedTaskCommon.h',
     ]
     UNIFIED_SOURCES += [
         'tasktracer/GeckoTaskTracer.cpp',
         'tasktracer/TracedTaskCommon.cpp',
     ]
 
 XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell.ini']
--- a/tools/profiler/public/GeckoProfilerFunc.h
+++ b/tools/profiler/public/GeckoProfilerFunc.h
@@ -66,16 +66,19 @@ const double* mozilla_sampler_get_respon
 void mozilla_sampler_save();
 
 mozilla::UniquePtr<char[]> mozilla_sampler_get_profile(double aSinceTime);
 
 #ifndef SPS_STANDALONE
 JSObject *mozilla_sampler_get_profile_data(JSContext* aCx, double aSinceTime);
 void mozilla_sampler_get_profile_data_async(double aSinceTime,
                                             mozilla::dom::Promise* aPromise);
+MOZ_EXPORT
+void mozilla_sampler_save_profile_to_file_async(double aSinceTime,
+                                                const char* aFileName);
 void mozilla_sampler_get_profiler_start_params(int* aEntrySize,
                                                double* aInterval,
                                                mozilla::Vector<const char*>* aFilters,
                                                mozilla::Vector<const char*>* aFeatures);
 void mozilla_sampler_get_gatherer(nsISupports** aRetVal);
 #endif
 
 // Make this function easily callable from a debugger in a build without
--- a/tools/profiler/public/ProfileGatherer.h
+++ b/tools/profiler/public/ProfileGatherer.h
@@ -1,41 +1,45 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZ_PROFILE_GATHERER_H
 #define MOZ_PROFILE_GATHERER_H
 
 #include "mozilla/dom/Promise.h"
+#include "nsIFile.h"
 
 class GeckoSampler;
 
 namespace mozilla {
 
 class ProfileGatherer final : public nsIObserver
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
 
   explicit ProfileGatherer(GeckoSampler* aTicker);
   void WillGatherOOPProfile();
   void GatheredOOPProfile();
   void Start(double aSinceTime, mozilla::dom::Promise* aPromise);
+  void Start(double aSinceTime, nsIFile* aFile);
+  void Start(double aSinceTime, const nsACString& aFileName);
   void Cancel();
   void OOPExitProfile(const nsCString& aProfile);
 
 private:
   ~ProfileGatherer() {};
   void Finish();
   void Reset();
 
   nsTArray<nsCString> mExitProfiles;
   RefPtr<mozilla::dom::Promise> mPromise;
+  nsCOMPtr<nsIFile> mFile;
   GeckoSampler* mTicker;
   double mSinceTime;
   uint32_t mPendingProfiles;
   bool mGathering;
 };
 
 } // namespace mozilla
 
--- a/tools/profiler/tasktracer/GeckoTaskTracer.cpp
+++ b/tools/profiler/tasktracer/GeckoTaskTracer.cpp
@@ -15,16 +15,18 @@
 #include "mozilla/Unused.h"
 
 #include "nsString.h"
 #include "nsThreadUtils.h"
 #include "prtime.h"
 
 #include <stdarg.h>
 
+#define MAX_SIZE_LOG (1024 * 128)
+
 // NS_ENSURE_TRUE_VOID() without the warning on the debug build.
 #define ENSURE_TRUE_VOID(x)   \
   do {                        \
     if (MOZ_UNLIKELY(!(x))) { \
        return;                \
     }                         \
   } while(0)
 
@@ -69,162 +71,132 @@ AllocTraceInfo(int aTid)
   StaticMutexAutoLock lock(sMutex);
 
   auto* info = sTraceInfos->AppendElement(MakeUnique<TraceInfo>(aTid));
 
   return info->get();
 }
 
 static void
-SaveCurTraceInfo()
-{
-  TraceInfo* info = GetOrCreateTraceInfo();
-  ENSURE_TRUE_VOID(info);
-
-  info->mSavedCurTraceSourceId = info->mCurTraceSourceId;
-  info->mSavedCurTraceSourceType = info->mCurTraceSourceType;
-  info->mSavedCurTaskId = info->mCurTaskId;
-}
-
-static void
-RestoreCurTraceInfo()
-{
-  TraceInfo* info = GetOrCreateTraceInfo();
-  ENSURE_TRUE_VOID(info);
-
-  info->mCurTraceSourceId = info->mSavedCurTraceSourceId;
-  info->mCurTraceSourceType = info->mSavedCurTraceSourceType;
-  info->mCurTaskId = info->mSavedCurTaskId;
-}
-
-static void
 CreateSourceEvent(SourceEventType aType)
 {
-  // Save the currently traced source event info.
-  SaveCurTraceInfo();
-
   // Create a new unique task id.
   uint64_t newId = GenNewUniqueTaskId();
   TraceInfo* info = GetOrCreateTraceInfo();
   ENSURE_TRUE_VOID(info);
 
   info->mCurTraceSourceId = newId;
   info->mCurTraceSourceType = aType;
   info->mCurTaskId = newId;
 
-  uintptr_t namePtr;
+  uintptr_t* namePtr;
 #define SOURCE_EVENT_NAME(type)         \
   case SourceEventType::type:           \
   {                                     \
-    namePtr = (uintptr_t)&CreateSourceEvent##type; \
+    namePtr = (uintptr_t*)&CreateSourceEvent##type; \
     break;                              \
   }
 
   switch (aType) {
 #include "SourceEventTypeMap.h"
     default:
       MOZ_CRASH("Unknown SourceEvent.");
   }
 #undef SOURCE_EVENT_NAME
 
   // Log a fake dispatch and start for this source event.
   LogDispatch(newId, newId, newId, aType);
-  LogVirtualTablePtr(newId, newId, &namePtr);
+  LogVirtualTablePtr(newId, newId, namePtr);
   LogBegin(newId, newId);
 }
 
 static void
 DestroySourceEvent()
 {
   // Log a fake end for this source event.
   TraceInfo* info = GetOrCreateTraceInfo();
   ENSURE_TRUE_VOID(info);
 
   LogEnd(info->mCurTraceSourceId, info->mCurTraceSourceId);
-
-  // Restore the previously saved source event info.
-  RestoreCurTraceInfo();
 }
 
 inline static bool
 IsStartLogging()
 {
   return sStarted;
 }
 
 static void
 SetLogStarted(bool aIsStartLogging)
 {
-  MOZ_ASSERT(aIsStartLogging != IsStartLogging());
+  MOZ_ASSERT(aIsStartLogging != sStarted);
+  MOZ_ASSERT(sTraceInfos != nullptr);
   sStarted = aIsStartLogging;
 
   StaticMutexAutoLock lock(sMutex);
-  if (!aIsStartLogging) {
+  if (!aIsStartLogging && sTraceInfos) {
     for (uint32_t i = 0; i < sTraceInfos->Length(); ++i) {
       (*sTraceInfos)[i]->mObsolete = true;
     }
   }
 }
 
-static void
-CleanUp()
-{
-  MOZ_ASSERT(!IsStartLogging());
-  StaticMutexAutoLock lock(sMutex);
-
-  if (sTraceInfos) {
-    delete sTraceInfos;
-    sTraceInfos = nullptr;
-  }
-}
-
 inline static void
 ObsoleteCurrentTraceInfos()
 {
   // Note that we can't and don't need to acquire sMutex here because this
   // function is called before the other threads are recreated.
   for (uint32_t i = 0; i < sTraceInfos->Length(); ++i) {
     (*sTraceInfos)[i]->mObsolete = true;
   }
 }
 
 } // namespace anonymous
 
-nsCString*
+TraceInfoLogType*
 TraceInfo::AppendLog()
 {
-  MutexAutoLock lock(mLogsMutex);
-  return mLogs.AppendElement();
-}
-
-void
-TraceInfo::MoveLogsInto(TraceInfoLogsType& aResult)
-{
-  MutexAutoLock lock(mLogsMutex);
-  aResult.AppendElements(Move(mLogs));
+  if (mLogsSize >= MAX_SIZE_LOG) {
+    return nullptr;
+  }
+  TraceInfoLogNode* node = new TraceInfoLogNode;
+  node->mNext = nullptr;
+  if (mLogsTail) {
+    mLogsTail->mNext = node;
+    mLogsTail = node;
+  } else {
+    mLogsTail = mLogsHead = node;
+  }
+  mLogsSize++;
+  return &node->mLog;
 }
 
 void
 InitTaskTracer(uint32_t aFlags)
 {
+  StaticMutexAutoLock lock(sMutex);
+
   if (aFlags & FORKED_AFTER_NUWA) {
     ObsoleteCurrentTraceInfos();
     return;
   }
 
   MOZ_ASSERT(!sTraceInfos);
-  sTraceInfos = new nsTArray<UniquePtr<TraceInfo>>();
 
   sTraceInfoTLS.init();
+  // A memory barrier is necessary here.
+  sTraceInfos = new nsTArray<UniquePtr<TraceInfo>>();
 }
 
 void
 ShutdownTaskTracer()
 {
-  CleanUp();
+  if (IsStartLogging()) {
+    SetLogStarted(false);
+  }
 }
 
 static void
 FreeTraceInfo(TraceInfo* aTraceInfo)
 {
   StaticMutexAutoLock lock(sMutex);
   if (aTraceInfo) {
     UniquePtr<TraceInfo> traceinfo(aTraceInfo);
@@ -238,17 +210,16 @@ FreeTraceInfo(TraceInfo* aTraceInfo)
 void FreeTraceInfo()
 {
   FreeTraceInfo(sTraceInfoTLS.get());
 }
 
 TraceInfo*
 GetOrCreateTraceInfo()
 {
-  ENSURE_TRUE(sTraceInfoTLS.init(), nullptr);
   ENSURE_TRUE(IsStartLogging(), nullptr);
 
   TraceInfo* info = sTraceInfoTLS.get();
   if (info && info->mObsolete) {
     // TraceInfo is obsolete: remove it.
     FreeTraceInfo(info);
     info = nullptr;
   }
@@ -269,22 +240,22 @@ GenNewUniqueTaskId()
 
   pid_t tid = gettid();
   uint64_t taskid = ((uint64_t)tid << 32) | ++info->mLastUniqueTaskId;
   return taskid;
 }
 
 AutoSaveCurTraceInfo::AutoSaveCurTraceInfo()
 {
-  SaveCurTraceInfo();
+  GetCurTraceInfo(&mSavedSourceEventId, &mSavedTaskId, &mSavedSourceEventType);
 }
 
 AutoSaveCurTraceInfo::~AutoSaveCurTraceInfo()
 {
-  RestoreCurTraceInfo();
+  SetCurTraceInfo(mSavedSourceEventId, mSavedTaskId, mSavedSourceEventType);
 }
 
 void
 SetCurTraceInfo(uint64_t aSourceEventId, uint64_t aParentTaskId,
                 SourceEventType aSourceEventType)
 {
   TraceInfo* info = GetOrCreateTraceInfo();
   ENSURE_TRUE_VOID(info);
@@ -320,99 +291,117 @@ LogDispatch(uint64_t aTaskId, uint64_t a
   TraceInfo* info = GetOrCreateTraceInfo();
   ENSURE_TRUE_VOID(info);
 
   // aDelayTimeMs is the expected delay time in milliseconds, thus the dispatch
   // time calculated of it might be slightly off in the real world.
   uint64_t time = (aDelayTimeMs <= 0) ? GetTimestamp() :
                   GetTimestamp() + aDelayTimeMs;
 
+  MutexAutoLock lock(info->mLogsMutex);
   // Log format:
   // [0 taskId dispatchTime sourceEventId sourceEventType parentTaskId]
-  nsCString* log = info->AppendLog();
+  TraceInfoLogType* log = info->AppendLog();
   if (log) {
-    log->AppendPrintf("%d %lld %lld %lld %d %lld",
-                      ACTION_DISPATCH, aTaskId, time, aSourceEventId,
-                      aSourceEventType, aParentTaskId);
+    log->mDispatch.mType = ACTION_DISPATCH;
+    log->mDispatch.mTaskId = aTaskId;
+    log->mDispatch.mTime = time;
+    log->mDispatch.mSourceEventId = aSourceEventId;
+    log->mDispatch.mSourceEventType = aSourceEventType;
+    log->mDispatch.mParentTaskId = aParentTaskId;
   }
 }
 
 void
 LogBegin(uint64_t aTaskId, uint64_t aSourceEventId)
 {
   TraceInfo* info = GetOrCreateTraceInfo();
   ENSURE_TRUE_VOID(info);
 
+  MutexAutoLock lock(info->mLogsMutex);
   // Log format:
   // [1 taskId beginTime processId threadId]
-  nsCString* log = info->AppendLog();
+  TraceInfoLogType* log = info->AppendLog();
   if (log) {
-    log->AppendPrintf("%d %lld %lld %d %d",
-                      ACTION_BEGIN, aTaskId, GetTimestamp(), getpid(), gettid());
+    log->mBegin.mType = ACTION_BEGIN;
+    log->mBegin.mTaskId = aTaskId;
+    log->mBegin.mTime = GetTimestamp();
+    log->mBegin.mPid = getpid();
+    log->mBegin.mTid = gettid();
   }
 }
 
 void
 LogEnd(uint64_t aTaskId, uint64_t aSourceEventId)
 {
   TraceInfo* info = GetOrCreateTraceInfo();
   ENSURE_TRUE_VOID(info);
 
+  MutexAutoLock lock(info->mLogsMutex);
   // Log format:
   // [2 taskId endTime]
-  nsCString* log = info->AppendLog();
+  TraceInfoLogType* log = info->AppendLog();
   if (log) {
-    log->AppendPrintf("%d %lld %lld", ACTION_END, aTaskId, GetTimestamp());
+    log->mEnd.mType = ACTION_END;
+    log->mEnd.mTaskId = aTaskId;
+    log->mEnd.mTime = GetTimestamp();
   }
 }
 
 void
 LogVirtualTablePtr(uint64_t aTaskId, uint64_t aSourceEventId, uintptr_t* aVptr)
 {
   TraceInfo* info = GetOrCreateTraceInfo();
   ENSURE_TRUE_VOID(info);
 
+  MutexAutoLock lock(info->mLogsMutex);
   // Log format:
   // [4 taskId address]
-  nsCString* log = info->AppendLog();
+  TraceInfoLogType* log = info->AppendLog();
   if (log) {
     // Since addr2line used by SPS addon can not solve non-function
     // addresses, we use the first entry of vtable as the symbol to
     // solve.  We should find a better solution later.
-    log->AppendPrintf("%d %lld %p", ACTION_GET_VTABLE, aTaskId, *aVptr);
+    log->mVPtr.mType = ACTION_GET_VTABLE;
+    log->mVPtr.mTaskId = aTaskId;
+    log->mVPtr.mVPtr = reinterpret_cast<uintptr_t>(aVptr);
   }
 }
 
 AutoSourceEvent::AutoSourceEvent(SourceEventType aType)
+  : AutoSaveCurTraceInfo()
 {
   CreateSourceEvent(aType);
 }
 
 AutoSourceEvent::~AutoSourceEvent()
 {
   DestroySourceEvent();
 }
 
 void AddLabel(const char* aFormat, ...)
 {
   TraceInfo* info = GetOrCreateTraceInfo();
   ENSURE_TRUE_VOID(info);
 
-  va_list args;
-  va_start(args, aFormat);
-  nsAutoCString buffer;
-  buffer.AppendPrintf(aFormat, args);
-  va_end(args);
-
+  MutexAutoLock lock(info->mLogsMutex);
   // Log format:
   // [3 taskId "label"]
-  nsCString* log = info->AppendLog();
+  TraceInfoLogType* log = info->AppendLog();
   if (log) {
-    log->AppendPrintf("%d %lld %lld \"%s\"", ACTION_ADD_LABEL, info->mCurTaskId,
-                      GetTimestamp(), buffer.get());
+    va_list args;
+    va_start(args, aFormat);
+    nsCString &buffer = *info->mStrs.AppendElement();
+    buffer.AppendPrintf(aFormat, args);
+    va_end(args);
+
+    log->mLabel.mType = ACTION_ADD_LABEL;
+    log->mLabel.mTaskId = info->mCurTaskId;
+    log->mLabel.mTime = GetTimestamp();
+    log->mLabel.mStrIdx = info->mStrs.Length() - 1;
   }
 }
 
 // Functions used by GeckoProfiler.
 
 void
 StartLogging()
 {
@@ -421,27 +410,74 @@ StartLogging()
 }
 
 void
 StopLogging()
 {
   SetLogStarted(false);
 }
 
-UniquePtr<TraceInfoLogsType>
+UniquePtr<nsTArray<nsCString>>
 GetLoggedData(TimeStamp aTimeStamp)
 {
-  auto result = MakeUnique<TraceInfoLogsType>();
+  auto result = MakeUnique<nsTArray<nsCString>>();
 
   // TODO: This is called from a signal handler. Use semaphore instead.
   StaticMutexAutoLock lock(sMutex);
 
   for (uint32_t i = 0; i < sTraceInfos->Length(); ++i) {
-    if (!(*sTraceInfos)[i]->mObsolete) {
-      (*sTraceInfos)[i]->MoveLogsInto(*result);
+    TraceInfo* info = (*sTraceInfos)[i].get();
+    MutexAutoLock lockLogs(info->mLogsMutex);
+    if (info->mObsolete) {
+      continue;
+    }
+
+    nsTArray<nsCString> &strs = info->mStrs;
+    for (TraceInfoLogNode* node = info->mLogsHead; node; node = node->mNext) {
+      TraceInfoLogType &log = node->mLog;
+      nsCString &buffer = *result->AppendElement();
+
+      switch (log.mType) {
+      case ACTION_DISPATCH:
+        buffer.AppendPrintf("%d %lld %lld %lld %d %lld",
+                            ACTION_DISPATCH,
+                            log.mDispatch.mTaskId,
+                            log.mDispatch.mTime,
+                            log.mDispatch.mSourceEventId,
+                            log.mDispatch.mSourceEventType,
+                            log.mDispatch.mParentTaskId);
+        break;
+
+      case ACTION_BEGIN:
+        buffer.AppendPrintf("%d %lld %lld %d %d",
+                            ACTION_BEGIN, log.mBegin.mTaskId,
+                            log.mBegin.mTime, log.mBegin.mPid,
+                            log.mBegin.mTid);
+        break;
+
+      case ACTION_END:
+        buffer.AppendPrintf("%d %lld %lld",
+                            ACTION_END, log.mEnd.mTaskId, log.mEnd.mTime);
+        break;
+
+      case ACTION_GET_VTABLE:
+        buffer.AppendPrintf("%d %lld %p",
+                            ACTION_GET_VTABLE, log.mVPtr.mTaskId,
+                            log.mVPtr.mVPtr);
+        break;
+
+      case ACTION_ADD_LABEL:
+        buffer.AppendPrintf("%d %lld %lld \"%s\"",
+                            ACTION_ADD_LABEL, log.mLabel.mTaskId,
+                            log.mLabel.mTime, strs[log.mLabel.mStrIdx].get());
+        break;
+
+      default:
+        MOZ_CRASH("Unknow TaskTracer log type!");
+      }
     }
   }
 
   return result;
 }
 
 const PRTime
 GetStartTime()
--- a/tools/profiler/tasktracer/GeckoTaskTracer.h
+++ b/tools/profiler/tasktracer/GeckoTaskTracer.h
@@ -34,26 +34,32 @@ class TimeStamp;
 
 namespace tasktracer {
 
 enum {
   FORKED_AFTER_NUWA = 1 << 0
 };
 
 enum SourceEventType {
-  Unknown = 0,
-  Touch,
-  Mouse,
-  Key,
-  Bluetooth,
-  Unixsocket,
-  Wifi
+#define SOURCE_EVENT_NAME(x) x,
+#include "SourceEventTypeMap.h"
+#undef SOURCE_EVENT_NAME
 };
 
-class AutoSourceEvent
+class AutoSaveCurTraceInfo
+{
+  uint64_t mSavedTaskId;
+  uint64_t mSavedSourceEventId;
+  SourceEventType mSavedSourceEventType;
+public:
+  AutoSaveCurTraceInfo();
+  ~AutoSaveCurTraceInfo();
+};
+
+class AutoSourceEvent : public AutoSaveCurTraceInfo
 {
 public:
   AutoSourceEvent(SourceEventType aType);
   ~AutoSourceEvent();
 };
 
 void InitTaskTracer(uint32_t aFlags = 0);
 void ShutdownTaskTracer();
@@ -78,12 +84,15 @@ CreateTracedRunnable(already_AddRefed<ns
 
 // Free the TraceInfo allocated on a thread's TLS. Currently we are wrapping
 // tasks running on nsThreads and base::thread, so FreeTraceInfo is called at
 // where nsThread and base::thread release themselves.
 void FreeTraceInfo();
 
 const char* GetJSLabelPrefix();
 
+void GetCurTraceInfo(uint64_t* aOutSourceEventId, uint64_t* aOutParentTaskId,
+                     SourceEventType* aOutSourceEventType);
+
 } // namespace tasktracer
 } // namespace mozilla.
 
 #endif
--- a/tools/profiler/tasktracer/GeckoTaskTracerImpl.h
+++ b/tools/profiler/tasktracer/GeckoTaskTracerImpl.h
@@ -9,74 +9,117 @@
 
 #include "GeckoTaskTracer.h"
 #include "mozilla/Mutex.h"
 #include "nsTArray.h"
 
 namespace mozilla {
 namespace tasktracer {
 
-typedef nsTArray<nsCString> TraceInfoLogsType;
+struct LogRecDispatch {
+  uint32_t mType;
+  uint32_t mSourceEventType;
+  uint64_t mTaskId;
+  uint64_t mTime;
+  uint64_t mSourceEventId;
+  uint64_t mParentTaskId;
+};
+
+struct LogRecBegin {
+  uint32_t mType;
+  uint64_t mTaskId;
+  uint64_t mTime;
+  uint32_t mPid;
+  uint32_t mTid;
+};
+
+struct LogRecEnd {
+  uint32_t mType;
+  uint64_t mTaskId;
+  uint64_t mTime;
+};
+
+struct LogRecVPtr {
+  uint32_t mType;
+  uint64_t mTaskId;
+  uintptr_t mVPtr;
+};
+
+struct LogRecLabel {
+  uint32_t mType;
+  uint32_t mStrIdx;
+  uint64_t mTaskId;
+  uint64_t mTime;
+};
+
+union TraceInfoLogType {
+  uint32_t mType;
+  LogRecDispatch mDispatch;
+  LogRecBegin mBegin;
+  LogRecEnd mEnd;
+  LogRecVPtr mVPtr;
+  LogRecLabel mLabel;
+};
+
+struct TraceInfoLogNode {
+  TraceInfoLogType mLog;
+  TraceInfoLogNode* mNext;
+};
 
 struct TraceInfo
 {
   TraceInfo(uint32_t aThreadId)
     : mCurTraceSourceId(0)
     , mCurTaskId(0)
-    , mSavedCurTraceSourceId(0)
-    , mSavedCurTaskId(0)
     , mCurTraceSourceType(Unknown)
-    , mSavedCurTraceSourceType(Unknown)
     , mThreadId(aThreadId)
     , mLastUniqueTaskId(0)
     , mObsolete(false)
     , mLogsMutex("TraceInfoMutex")
+    , mLogsHead(nullptr)
+    , mLogsTail(nullptr)
+    , mLogsSize(0)
   {
     MOZ_COUNT_CTOR(TraceInfo);
   }
 
-  ~TraceInfo() { MOZ_COUNT_DTOR(TraceInfo); }
+  ~TraceInfo() {
+    MOZ_COUNT_DTOR(TraceInfo);
+    while (mLogsHead) {
+      auto node = mLogsHead;
+      mLogsHead = node->mNext;
+      delete node;
+    }
+  }
 
-  nsCString* AppendLog();
-  void MoveLogsInto(TraceInfoLogsType& aResult);
+  TraceInfoLogType* AppendLog();
 
   uint64_t mCurTraceSourceId;
   uint64_t mCurTaskId;
-  uint64_t mSavedCurTraceSourceId;
-  uint64_t mSavedCurTaskId;
   SourceEventType mCurTraceSourceType;
-  SourceEventType mSavedCurTraceSourceType;
   uint32_t mThreadId;
   uint32_t mLastUniqueTaskId;
   mozilla::Atomic<bool> mObsolete;
 
-  // This mutex protects the following log array because MoveLogsInto() might
-  // be called on another thread.
+  // This mutex protects the following log
   mozilla::Mutex mLogsMutex;
-  TraceInfoLogsType mLogs;
+  TraceInfoLogNode* mLogsHead;
+  TraceInfoLogNode* mLogsTail;
+  int mLogsSize;
+  nsTArray<nsCString> mStrs;
 };
 
 // Return the TraceInfo of current thread, allocate a new one if not exit.
 TraceInfo* GetOrCreateTraceInfo();
 
 uint64_t GenNewUniqueTaskId();
 
-class AutoSaveCurTraceInfo
-{
-public:
-  AutoSaveCurTraceInfo();
-  ~AutoSaveCurTraceInfo();
-};
-
 void SetCurTraceInfo(uint64_t aSourceEventId, uint64_t aParentTaskId,
                      SourceEventType aSourceEventType);
 
-void GetCurTraceInfo(uint64_t* aOutSourceEventId, uint64_t* aOutParentTaskId,
-                     SourceEventType* aOutSourceEventType);
-
 /**
  * Logging functions of different trace actions.
  */
 enum ActionType {
   ACTION_DISPATCH = 0,
   ACTION_BEGIN,
   ACTION_END,
   ACTION_ADD_LABEL,
--- a/tools/profiler/tasktracer/SourceEventTypeMap.h
+++ b/tools/profiler/tasktracer/SourceEventTypeMap.h
@@ -1,11 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#ifndef GECKO_TASK_TRACER_H
+#error "Don't include this file directly"
+#endif
+
 SOURCE_EVENT_NAME(Unknown)
 SOURCE_EVENT_NAME(Touch)
 SOURCE_EVENT_NAME(Mouse)
 SOURCE_EVENT_NAME(Key)
 SOURCE_EVENT_NAME(Bluetooth)
 SOURCE_EVENT_NAME(Unixsocket)
 SOURCE_EVENT_NAME(Wifi)
--- a/tools/profiler/tasktracer/TracedTaskCommon.cpp
+++ b/tools/profiler/tasktracer/TracedTaskCommon.cpp
@@ -120,10 +120,23 @@ TracedRunnable::Run()
  */
 already_AddRefed<nsIRunnable>
 CreateTracedRunnable(already_AddRefed<nsIRunnable>&& aRunnable)
 {
   nsCOMPtr<nsIRunnable> runnable = new TracedRunnable(Move(aRunnable));
   return runnable.forget();
 }
 
+VirtualTask::AutoRunTask::AutoRunTask(VirtualTask* aTask)
+  : AutoSaveCurTraceInfo()
+  , mTask(aTask)
+{
+  mTask->SetTLSTraceInfo();
+  LogBegin(mTask->mTaskId, mTask->mSourceEventId);
+}
+
+VirtualTask::AutoRunTask::~AutoRunTask()
+{
+  LogEnd(mTask->mTaskId, mTask->mSourceEventId);
+}
+
 } // namespace tasktracer
 } // namespace mozilla
--- a/tools/profiler/tasktracer/TracedTaskCommon.h
+++ b/tools/profiler/tasktracer/TracedTaskCommon.h
@@ -2,28 +2,33 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef TRACED_TASK_COMMON_H
 #define TRACED_TASK_COMMON_H
 
-#include "base/task.h"
 #include "GeckoTaskTracer.h"
 #include "nsCOMPtr.h"
 #include "nsThreadUtils.h"
 
 namespace mozilla {
 namespace tasktracer {
 
 class TracedTaskCommon
 {
 public:
   TracedTaskCommon();
+  TracedTaskCommon(const TracedTaskCommon& aSrc)
+    : mSourceEventType(aSrc.mSourceEventType)
+    , mSourceEventId(aSrc.mSourceEventId)
+    , mParentTaskId(aSrc.mParentTaskId)
+    , mTaskId(aSrc.mTaskId)
+    , mIsTraceInfoInit(aSrc.mIsTraceInfoInit) {}
   virtual ~TracedTaskCommon();
 
   void DispatchTask(int aDelayTimeMs = 0);
 
   void SetTLSTraceInfo();
   void GetTLSTraceInfo();
   void ClearTLSTraceInfo();
 
@@ -49,12 +54,53 @@ public:
   TracedRunnable(already_AddRefed<nsIRunnable>&& aOriginalObj);
 
 private:
   virtual ~TracedRunnable();
 
   nsCOMPtr<nsIRunnable> mOriginalObj;
 };
 
+/**
+ * This class is used to create a logical task, without a real
+ * runnable.
+ */
+class VirtualTask : public TracedTaskCommon {
+public:
+  VirtualTask() : TracedTaskCommon() {}
+
+  VirtualTask(const VirtualTask& aSrc) : TracedTaskCommon(aSrc) {}
+
+  /**
+   * Initialize the task to create an unique ID, and store other
+   * information.
+   *
+   * This method may be called for one or more times.
+   */
+  void Init(uintptr_t* aVPtr = nullptr) {
+    TracedTaskCommon::Init();
+    if (aVPtr) {
+      extern void LogVirtualTablePtr(uint64_t aTaskId, uint64_t aSourceEventId, uintptr_t* aVptr);
+      LogVirtualTablePtr(mTaskId, mSourceEventId, aVPtr);
+    }
+    DispatchTask();
+  }
+
+  /**
+   * Define the life-span of a VirtualTask.
+   *
+   * VirtualTask is not a real task, goes without a runnable, it's
+   * instances are never dispatched and ran by event loops.  This
+   * class used to define running time as the life-span of it's
+   * instance.
+   */
+  class AutoRunTask : public AutoSaveCurTraceInfo {
+    VirtualTask* mTask;
+  public:
+    AutoRunTask(VirtualTask *aTask);
+    ~AutoRunTask();
+  };
+};
+
 } // namespace tasktracer
 } // namespace mozilla
 
 #endif