Merge m-i to m-c, a=merge
authorPhil Ringnalda <philringnalda@gmail.com>
Fri, 20 Jan 2017 19:00:04 -0800
changeset 377732 487a4e43eb9d1f04a5d8e3dd183fe38dbe105e1f
parent 377619 41d8ef56d03b9b5b382d0575def6712d9d51b1df (current diff)
parent 377731 24e88a2d883fff700725490665bce10aeb303551 (diff)
child 377762 cfb667d165ed96952b7c421770fffe7a072e1db9
child 377811 1072a014088adc1353f19c4c659fc98a2e6dbe9c
push id1419
push userjlund@mozilla.com
push dateMon, 10 Apr 2017 20:44:07 +0000
treeherdermozilla-release@5e6801b73ef6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone53.0a1
first release with
nightly mac
487a4e43eb9d / 53.0a1 / 20170121030206 / files
nightly win32
487a4e43eb9d / 53.0a1 / 20170121030206 / files
nightly win64
487a4e43eb9d / 53.0a1 / 20170121030206 / files
nightly linux32
nightly linux64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly mac
nightly win32
nightly win64
Merge m-i to m-c, a=merge
browser/base/content/test/general/browser_fxa_oauth.html
browser/base/content/test/general/browser_fxa_oauth.js
browser/base/content/test/general/browser_fxa_oauth_with_keys.html
modules/libpref/init/all.js
services/fxaccounts/FxAccountsOAuthClient.jsm
services/fxaccounts/FxAccountsPush.js
services/fxaccounts/tests/xpcshell/test_oauth_client.js
--- a/accessible/generic/DocAccessible.h
+++ b/accessible/generic/DocAccessible.h
@@ -347,17 +347,17 @@ public:
   void ContentRemoved(Accessible* aContainer, nsIContent* aChildNode)
   {
     // Update the whole tree of this document accessible when the container is
     // null (document element is removed).
     UpdateTreeOnRemoval((aContainer ? aContainer : this), aChildNode);
   }
   void ContentRemoved(nsIContent* aContainerNode, nsIContent* aChildNode)
   {
-    ContentRemoved(GetAccessibleOrContainer(aContainerNode), aChildNode);
+    ContentRemoved(AccessibleOrTrueContainer(aContainerNode), aChildNode);
   }
 
   /**
    * Updates accessible tree when rendered text is changed.
    */
   void UpdateText(nsIContent* aTextNode);
 
   /**
--- a/accessible/tests/mochitest/treeupdate/test_select.html
+++ b/accessible/tests/mochitest/treeupdate/test_select.html
@@ -1,12 +1,12 @@
 <!DOCTYPE html>
 <html>
 <head>
-  <title>Add select options test</title>
+  <title>HTML select options test</title>
   <link rel="stylesheet" type="text/css"
         href="chrome://mochikit/content/tests/SimpleTest/test.css" />
 
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
 
   <script type="application/javascript"
           src="../common.js"></script>
@@ -86,45 +86,65 @@
       }
 
       this.getID = function removeptions_getID()
       {
         return "test elements removal from a select";
       }
     }
 
-    //gA11yEventDumpID = "debug";
+    /**
+     * Setting @href on option makes the accessible to recreate.
+     */
+    function setHrefOnOption()
+    {
+      this.eventSeq = [
+        new invokerChecker(EVENT_HIDE, 's2_o'),
+        new invokerChecker(EVENT_SHOW, 's2_o'),
+      ];
+
+      this.invoke = function setHrefOnOption_setHref()
+      {
+        getNode('s2_o').setAttribute('href', '1');
+      }
+
+      this.finalCheck = function() {
+        var tree =
+          { COMBOBOX: [
+            { COMBOBOX_LIST: [
+              { COMBOBOX_OPTION: [ ] }
+            ] }
+          ] };
+        testAccessibleTree('s2', tree);
+      }
+
+      this.getID = function removeptions_getID()
+      {
+        return "setting @href on select option";
+      }
+    }
 
     function doTest()
     {
       gQueue = new eventQueue();
 
       gQueue.push(new addOptions("select"));
       gQueue.push(new removeOptions("select"));
+      gQueue.push(new setHrefOnOption());
 
       gQueue.invoke(); // Will call SimpleTest.finish();
 
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
 <body>
-
-  <a target="_blank"
-     href="https://bugzilla.mozilla.org/show_bug.cgi?id=616452"
-     title="Bug 616452 - Dynamically inserted select options aren't reflected in accessible tree">
-    Mozilla Bug 616452</a>
-  <a target="_blank"
-     href="https://bugzilla.mozilla.org/show_bug.cgi?id=616940"
-     title="Removed select option accessibles aren't removed until hide event is fired">
-    Mozilla Bug 616940</a>
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <select id="select"></select>
-
-  <div id="debug"/>
+  <select id="s2"><option id="s2_o"></option></select>
 </body>
 </html>
--- a/browser/base/content/aboutNetError.xhtml
+++ b/browser/base/content/aboutNetError.xhtml
@@ -593,16 +593,20 @@
           <p id="errorShortDescText" />
         </div>
         <p id="badStsCertExplanation" hidden="true">&certerror.whatShouldIDo.badStsCertExplanation;</p>
 
         <div id="wrongSystemTimePanel" style="display: none;">
           &certerror.wrongSystemTime;
         </div>
 
+        <div id="wrongSystemTimeWithoutReferencePanel" style="display: none;">
+          &certerror.wrongSystemTimeWithoutReference;
+        </div>
+
         <!-- Long Description (Note: See netError.dtd for used XHTML tags) -->
         <div id="errorLongDesc" />
 
         <div id="learnMoreContainer">
           <p><a href="https://support.mozilla.org/kb/what-does-your-connection-is-not-secure-mean" id="learnMoreLink" target="new">&errorReporting.learnMore;</a></p>
         </div>
 
         <div id="prefChangeContainer" class="button-container">
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -1476,16 +1476,20 @@ var BookmarkingUI = {
 
     let options = PlacesUtils.history.getNewQueryOptions();
     options.excludeQueries = true;
     options.queryType = options.QUERY_TYPE_BOOKMARKS;
     options.sortingMode = options.SORT_BY_DATEADDED_DESCENDING;
     options.maxResults = kMaxResults;
     let query = PlacesUtils.history.getNewQuery();
 
+    let sh = Cc["@mozilla.org/network/serialization-helper;1"]
+               .getService(Ci.nsISerializationHelper);
+    let loadingPrincipal = sh.serializeToString(document.nodePrincipal);
+
     let fragment = document.createDocumentFragment();
     let root = PlacesUtils.history.executeQuery(query, options).root;
     root.containerOpen = true;
     for (let i = 0; i < root.childCount; i++) {
       let node = root.getChild(i);
       let uri = node.uri;
       let title = node.title;
       let icon = node.icon;
@@ -1495,16 +1499,17 @@ var BookmarkingUI = {
                                  "menuitem");
       item.setAttribute("label", title || uri);
       item.setAttribute("targetURI", uri);
       item.setAttribute("simulated-places-node", true);
       item.setAttribute("class", "menuitem-iconic menuitem-with-favicon bookmark-item " +
                                  aExtraCSSClass);
       if (icon) {
         item.setAttribute("image", icon);
+        item.setAttribute("loadingprincipal", loadingPrincipal);
       }
       item._placesNode = node;
       fragment.appendChild(item);
     }
     root.containerOpen = false;
     aHeaderItem.parentNode.insertBefore(fragment, aHeaderItem.nextSibling);
   },
 
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -300,46 +300,74 @@ var AboutNetAndCertErrorListener = {
     let learnMoreLink = content.document.getElementById("learnMoreLink");
     let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL");
 
     switch (msg.data.code) {
       case SEC_ERROR_UNKNOWN_ISSUER:
         learnMoreLink.href = baseURL + "security-error";
         break;
 
-      // in case the certificate expired we make sure the system clock
-      // matches settings server (kinto) time
+      // In case the certificate expired we make sure the system clock
+      // matches the blocklist ping (Kinto) time and is not before the build date.
       case SEC_ERROR_EXPIRED_CERTIFICATE:
       case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
       case SEC_ERROR_OCSP_FUTURE_RESPONSE:
       case SEC_ERROR_OCSP_OLD_RESPONSE:
       case MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE:
       case MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE:
 
-        // use blocklist stats if available
+        // We check against Kinto time first if available, because that allows us
+        // to give the user an approximation of what the correct time is.
+        let difference = 0;
         if (Services.prefs.getPrefType(PREF_BLOCKLIST_CLOCK_SKEW_SECONDS)) {
-          let difference = Services.prefs.getIntPref(PREF_BLOCKLIST_CLOCK_SKEW_SECONDS);
+          difference = Services.prefs.getIntPref(PREF_BLOCKLIST_CLOCK_SKEW_SECONDS);
+        }
+
+        // If the difference is more than a day.
+        if (Math.abs(difference) > 60 * 60 * 24) {
+          let formatter = new Intl.DateTimeFormat();
+          let systemDate = formatter.format(new Date());
+          // negative difference means local time is behind server time
+          let actualDate = formatter.format(new Date(Date.now() - difference * 1000));
+
+          content.document.getElementById("wrongSystemTime_URL")
+            .textContent = content.document.location.hostname;
+          content.document.getElementById("wrongSystemTime_systemDate")
+            .textContent = systemDate;
+          content.document.getElementById("wrongSystemTime_actualDate")
+            .textContent = actualDate;
 
-          // if the difference is more than a day
-          if (Math.abs(difference) > 60 * 60 * 24) {
+          content.document.getElementById("errorShortDesc")
+            .style.display = "none";
+          content.document.getElementById("wrongSystemTimePanel")
+            .style.display = "block";
+
+        // If there is no clock skew with Kinto servers, check against the build date.
+        // (The Kinto ping could have happened when the time was still right, or not at all)
+        } else {
+          let appBuildID = Services.appinfo.appBuildID;
+
+          let year = parseInt(appBuildID.substr(0, 4), 10);
+          let month = parseInt(appBuildID.substr(4, 2), 10) - 1;
+          let day = parseInt(appBuildID.substr(6, 2), 10);
+
+          let buildDate = new Date(year, month, day);
+          let systemDate = new Date();
+
+          if (buildDate > systemDate) {
             let formatter = new Intl.DateTimeFormat();
-            let systemDate = formatter.format(new Date());
-            // negative difference means local time is behind server time
-            let actualDate = formatter.format(new Date(Date.now() - difference * 1000));
 
-            content.document.getElementById("wrongSystemTime_URL")
+            content.document.getElementById("wrongSystemTimeWithoutReference_URL")
               .textContent = content.document.location.hostname;
-            content.document.getElementById("wrongSystemTime_systemDate")
-              .textContent = systemDate;
-            content.document.getElementById("wrongSystemTime_actualDate")
-              .textContent = actualDate;
+            content.document.getElementById("wrongSystemTimeWithoutReference_systemDate")
+              .textContent = formatter.format(systemDate);
 
             content.document.getElementById("errorShortDesc")
               .style.display = "none";
-            content.document.getElementById("wrongSystemTimePanel")
+            content.document.getElementById("wrongSystemTimeWithoutReferencePanel")
               .style.display = "block";
           }
         }
         learnMoreLink.href = baseURL + "time-errors";
         break;
     }
   },
 
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -6,18 +6,16 @@ support-files =
   app_bug575561.html
   app_subframe_bug575561.html
   aboutHome_content_script.js
   audio.ogg
   browser_bug479408_sample.html
   browser_bug678392-1.html
   browser_bug678392-2.html
   browser_bug970746.xhtml
-  browser_fxa_oauth.html
-  browser_fxa_oauth_with_keys.html
   browser_fxa_web_channel.html
   browser_registerProtocolHandler_notification.html
   browser_star_hsts.sjs
   browser_tab_dragdrop2_frame1.xul
   browser_web_channel.html
   browser_web_channel_iframe.html
   bug1262648_string_with_newlines.dtd
   bug592338.html
@@ -315,17 +313,16 @@ skip-if = true # browser_drag.js is disa
 [browser_findbarClose.js]
 [browser_focusonkeydown.js]
 [browser_fullscreen-window-open.js]
 tags = fullscreen
 skip-if = os == "linux" # Linux: Intermittent failures - bug 941575.
 [browser_fxaccounts.js]
 support-files = fxa_profile_handler.sjs
 [browser_fxa_migrate.js]
-[browser_fxa_oauth.js]
 [browser_fxa_web_channel.js]
 [browser_gestureSupport.js]
 skip-if = e10s # Bug 863514 - no gesture support.
 [browser_getshortcutoruri.js]
 [browser_hide_removing.js]
 [browser_homeDrop.js]
 [browser_identity_UI.js]
 [browser_insecureLoginForms.js]
--- a/browser/base/content/test/general/browser_aboutCertError.js
+++ b/browser/base/content/test/general/browser_aboutCertError.js
@@ -100,16 +100,29 @@ add_task(function* checkBadStsCert() {
     let exceptionButton = doc.getElementById("exceptionDialogButton");
     return exceptionButton.hidden;
   });
   ok(exceptionButtonHidden, "Exception button is hidden");
 
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
 
+// This checks that the appinfo.appBuildID starts with a date string,
+// which is required for the misconfigured system time check.
+add_task(function* checkAppBuildIDIsDate() {
+  let appBuildID = Services.appinfo.appBuildID;
+  let year = parseInt(appBuildID.substr(0, 4), 10);
+  let month = parseInt(appBuildID.substr(4, 2), 10);
+  let day = parseInt(appBuildID.substr(6, 2), 10);
+
+  ok(year >= 2016 && year <= 2100, "appBuildID contains a valid year");
+  ok(month >= 1 && month <= 12, "appBuildID contains a valid month");
+  ok(day >= 1 && day <= 31, "appBuildID contains a valid day");
+});
+
 const PREF_BLOCKLIST_CLOCK_SKEW_SECONDS = "services.blocklist.clock_skew_seconds";
 
 add_task(function* checkWrongSystemTimeWarning() {
   function* setUpPage() {
     let browser;
     let certErrorLoaded;
     yield BrowserTestUtils.openNewForegroundTab(gBrowser, () => {
       gBrowser.selectedTab = gBrowser.addTab(BAD_CERT);
@@ -146,17 +159,17 @@ add_task(function* checkWrongSystemTimeW
 
   let skew = Math.floor((Date.now() - serverDate.getTime()) / 1000);
   yield SpecialPowers.pushPrefEnv({set: [[PREF_BLOCKLIST_CLOCK_SKEW_SECONDS, skew]]});
 
   info("Loading a bad cert page with a skewed clock");
   let message = yield Task.spawn(setUpPage);
 
   isnot(message.divDisplay, "none", "Wrong time message information is visible");
-  ok(message.text.includes("because your clock appears to show the wrong time"),
+  ok(message.text.includes("clock appears to show the wrong time"),
      "Correct error message found");
   ok(message.text.includes("expired.example.com"), "URL found in error message");
   ok(message.systemDate.includes(localDateFmt), "correct local date displayed");
   ok(message.actualDate.includes(serverDateFmt), "correct server date displayed");
   ok(message.learnMoreLink.includes("time-errors"), "time-errors in the Learn More URL");
 
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 
@@ -167,17 +180,17 @@ add_task(function* checkWrongSystemTimeW
 
   skew = Math.floor((Date.now() - serverDate.getTime()) / 1000);
   yield SpecialPowers.pushPrefEnv({set: [[PREF_BLOCKLIST_CLOCK_SKEW_SECONDS, skew]]});
 
   info("Loading a bad cert page with a skewed clock");
   message = yield Task.spawn(setUpPage);
 
   isnot(message.divDisplay, "none", "Wrong time message information is visible");
-  ok(message.text.includes("because your clock appears to show the wrong time"),
+  ok(message.text.includes("clock appears to show the wrong time"),
      "Correct error message found");
   ok(message.text.includes("expired.example.com"), "URL found in error message");
   ok(message.systemDate.includes(localDateFmt), "correct local date displayed");
   ok(message.actualDate.includes(serverDateFmt), "correct server date displayed");
 
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 
   // pretend we only have a slightly skewed system time, four hours
deleted file mode 100644
--- a/browser/base/content/test/general/browser_fxa_oauth.html
+++ /dev/null
@@ -1,30 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-  <meta charset="utf-8">
-  <title>fxa_oauth_test</title>
-</head>
-<body>
-<script>
-  window.onload = function() {
-    var event = new window.CustomEvent("WebChannelMessageToChrome", {
-      // Note: This intentionally sends an object instead of a string, to ensure both work
-      // (see browser_fxa_oauth_with_keys.html for the other test)
-      detail: {
-        id: "oauth_client_id",
-        message: {
-          command: "oauth_complete",
-          data: {
-            state: "state",
-            code: "code1",
-            closeWindow: "signin",
-          },
-        },
-      },
-    });
-
-    window.dispatchEvent(event);
-  };
-</script>
-</body>
-</html>
deleted file mode 100644
--- a/browser/base/content/test/general/browser_fxa_oauth.js
+++ /dev/null
@@ -1,327 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-//
-// Whitelisting this test.
-// As part of bug 1077403, the leaking uncaught rejection should be fixed.
-//
-thisTestLeaksUncaughtRejectionsAndShouldBeFixed("TypeError: this.docShell is null");
-
-Cu.import("resource://gre/modules/Promise.jsm");
-Cu.import("resource://gre/modules/Task.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "FxAccountsOAuthClient",
-  "resource://gre/modules/FxAccountsOAuthClient.jsm");
-
-const HTTP_PATH = "http://example.com";
-const HTTP_ENDPOINT = "/browser/browser/base/content/test/general/browser_fxa_oauth.html";
-const HTTP_ENDPOINT_WITH_KEYS = "/browser/browser/base/content/test/general/browser_fxa_oauth_with_keys.html";
-
-var gTests = [
-  {
-    desc: "FxA OAuth - should open a new tab, complete OAuth flow",
-    run() {
-      return new Promise(function(resolve, reject) {
-        let tabOpened = false;
-        let properURL = "http://example.com/browser/browser/base/content/test/general/browser_fxa_oauth.html";
-        let queryStrings = [
-          "action=signin",
-          "client_id=client_id",
-          "scope=",
-          "state=state",
-          "webChannelId=oauth_client_id",
-        ];
-        queryStrings.sort();
-
-        waitForTab(function(tab) {
-          Assert.ok("Tab successfully opened");
-          Assert.ok(gBrowser.currentURI.spec.split("?")[0], properURL, "Check URL without params");
-          let actualURL = new URL(gBrowser.currentURI.spec);
-          let actualQueryStrings = actualURL.search.substring(1).split("&");
-          actualQueryStrings.sort();
-          Assert.equal(actualQueryStrings.length, queryStrings.length, "Check number of params");
-
-          for (let i = 0; i < queryStrings.length; i++) {
-            Assert.equal(actualQueryStrings[i], queryStrings[i], "Check parameter " + i);
-          }
-
-          tabOpened = true;
-        });
-
-        let client = new FxAccountsOAuthClient({
-          parameters: {
-            state: "state",
-            client_id: "client_id",
-            oauth_uri: HTTP_PATH,
-            content_uri: HTTP_PATH,
-          },
-          authorizationEndpoint: HTTP_ENDPOINT
-        });
-
-        client.onComplete = function(tokenData) {
-          Assert.ok(tabOpened);
-          Assert.equal(tokenData.code, "code1");
-          Assert.equal(tokenData.state, "state");
-          resolve();
-        };
-
-        client.onError = reject;
-
-        client.launchWebFlow();
-      });
-    }
-  },
-  {
-    desc: "FxA OAuth - should open a new tab, complete OAuth flow when forcing auth",
-    run() {
-      return new Promise(function(resolve, reject) {
-        let tabOpened = false;
-        let properURL = "http://example.com/browser/browser/base/content/test/general/browser_fxa_oauth.html";
-        let queryStrings = [
-          "action=force_auth",
-          "client_id=client_id",
-          "scope=",
-          "state=state",
-          "webChannelId=oauth_client_id",
-          "email=test%40invalid.com",
-        ];
-        queryStrings.sort();
-
-        waitForTab(function(tab) {
-          Assert.ok("Tab successfully opened");
-          Assert.ok(gBrowser.currentURI.spec.split("?")[0], properURL, "Check URL without params");
-
-          let actualURL = new URL(gBrowser.currentURI.spec);
-          let actualQueryStrings = actualURL.search.substring(1).split("&");
-          actualQueryStrings.sort();
-          Assert.equal(actualQueryStrings.length, queryStrings.length, "Check number of params");
-
-          for (let i = 0; i < queryStrings.length; i++) {
-            Assert.equal(actualQueryStrings[i], queryStrings[i], "Check parameter " + i);
-          }
-
-          tabOpened = true;
-        });
-
-        let client = new FxAccountsOAuthClient({
-          parameters: {
-            state: "state",
-            client_id: "client_id",
-            oauth_uri: HTTP_PATH,
-            content_uri: HTTP_PATH,
-            action: "force_auth",
-            email: "test@invalid.com"
-          },
-          authorizationEndpoint: HTTP_ENDPOINT
-        });
-
-        client.onComplete = function(tokenData) {
-          Assert.ok(tabOpened);
-          Assert.equal(tokenData.code, "code1");
-          Assert.equal(tokenData.state, "state");
-          resolve();
-        };
-
-        client.onError = reject;
-
-        client.launchWebFlow();
-      });
-    }
-  },
-  {
-    desc: "FxA OAuth - should receive an error when there's a state mismatch",
-    run() {
-      return new Promise(function(resolve, reject) {
-        let tabOpened = false;
-
-        waitForTab(function(tab) {
-          Assert.ok("Tab successfully opened");
-
-          // It should have passed in the expected non-matching state value.
-          let queryString = gBrowser.currentURI.spec.split("?")[1];
-          Assert.ok(queryString.indexOf("state=different-state") >= 0);
-
-          tabOpened = true;
-        });
-
-        let client = new FxAccountsOAuthClient({
-          parameters: {
-            state: "different-state",
-            client_id: "client_id",
-            oauth_uri: HTTP_PATH,
-            content_uri: HTTP_PATH,
-          },
-          authorizationEndpoint: HTTP_ENDPOINT
-        });
-
-        client.onComplete = reject;
-
-        client.onError = function(err) {
-          Assert.ok(tabOpened);
-          Assert.equal(err.message, "OAuth flow failed. State doesn't match");
-          resolve();
-        };
-
-        client.launchWebFlow();
-      });
-    }
-  },
-  {
-    desc: "FxA OAuth - should be able to request keys during OAuth flow",
-    run() {
-      return new Promise(function(resolve, reject) {
-        let tabOpened = false;
-
-        waitForTab(function(tab) {
-          Assert.ok("Tab successfully opened");
-
-          // It should have asked for keys.
-          let queryString = gBrowser.currentURI.spec.split("?")[1];
-          Assert.ok(queryString.indexOf("keys=true") >= 0);
-
-          tabOpened = true;
-        });
-
-        let client = new FxAccountsOAuthClient({
-          parameters: {
-            state: "state",
-            client_id: "client_id",
-            oauth_uri: HTTP_PATH,
-            content_uri: HTTP_PATH,
-            keys: true,
-          },
-          authorizationEndpoint: HTTP_ENDPOINT_WITH_KEYS
-        });
-
-        client.onComplete = function(tokenData, keys) {
-          Assert.ok(tabOpened);
-          Assert.equal(tokenData.code, "code1");
-          Assert.equal(tokenData.state, "state");
-          Assert.deepEqual(keys.kAr, {k: "kAr"});
-          Assert.deepEqual(keys.kBr, {k: "kBr"});
-          resolve();
-        };
-
-        client.onError = reject;
-
-        client.launchWebFlow();
-      });
-    }
-  },
-  {
-    desc: "FxA OAuth - should not receive keys if not explicitly requested",
-    run() {
-      return new Promise(function(resolve, reject) {
-        let tabOpened = false;
-
-        waitForTab(function(tab) {
-          Assert.ok("Tab successfully opened");
-
-          // It should not have asked for keys.
-          let queryString = gBrowser.currentURI.spec.split("?")[1];
-          Assert.ok(queryString.indexOf("keys=true") == -1);
-
-          tabOpened = true;
-        });
-
-        let client = new FxAccountsOAuthClient({
-          parameters: {
-            state: "state",
-            client_id: "client_id",
-            oauth_uri: HTTP_PATH,
-            content_uri: HTTP_PATH
-          },
-          // This endpoint will cause the completion message to contain keys.
-          authorizationEndpoint: HTTP_ENDPOINT_WITH_KEYS
-        });
-
-        client.onComplete = function(tokenData, keys) {
-          Assert.ok(tabOpened);
-          Assert.equal(tokenData.code, "code1");
-          Assert.equal(tokenData.state, "state");
-          Assert.strictEqual(keys, undefined);
-          resolve();
-        };
-
-        client.onError = reject;
-
-        client.launchWebFlow();
-      });
-    }
-  },
-  {
-    desc: "FxA OAuth - should receive an error if keys could not be obtained",
-    run() {
-      return new Promise(function(resolve, reject) {
-        let tabOpened = false;
-
-        waitForTab(function(tab) {
-          Assert.ok("Tab successfully opened");
-
-          // It should have asked for keys.
-          let queryString = gBrowser.currentURI.spec.split("?")[1];
-          Assert.ok(queryString.indexOf("keys=true") >= 0);
-
-          tabOpened = true;
-        });
-
-        let client = new FxAccountsOAuthClient({
-          parameters: {
-            state: "state",
-            client_id: "client_id",
-            oauth_uri: HTTP_PATH,
-            content_uri: HTTP_PATH,
-            keys: true,
-          },
-          // This endpoint will cause the completion message not to contain keys.
-          authorizationEndpoint: HTTP_ENDPOINT
-        });
-
-        client.onComplete = reject;
-
-        client.onError = function(err) {
-          Assert.ok(tabOpened);
-          Assert.equal(err.message, "OAuth flow failed. Keys were not returned");
-          resolve();
-        };
-
-        client.launchWebFlow();
-      });
-    }
-  }
-]; // gTests
-
-function waitForTab(aCallback) {
-  let container = gBrowser.tabContainer;
-  container.addEventListener("TabOpen", function tabOpener(event) {
-    container.removeEventListener("TabOpen", tabOpener);
-    gBrowser.addEventListener("load", function listener() {
-      gBrowser.removeEventListener("load", listener, true);
-      let tab = event.target;
-      aCallback(tab);
-    }, true);
-  });
-}
-
-function test() {
-  waitForExplicitFinish();
-
-  Task.spawn(function* () {
-    const webchannelWhitelistPref = "webchannel.allowObject.urlWhitelist";
-    let origWhitelist = Services.prefs.getCharPref(webchannelWhitelistPref);
-    let newWhitelist = origWhitelist + " http://example.com";
-    Services.prefs.setCharPref(webchannelWhitelistPref, newWhitelist);
-    try {
-      for (let testCase of gTests) {
-        info("Running: " + testCase.desc);
-        yield testCase.run();
-      }
-    } finally {
-      Services.prefs.clearUserPref(webchannelWhitelistPref);
-    }
-  }).then(finish, ex => {
-    Assert.ok(false, "Unexpected Exception: " + ex);
-    finish();
-  });
-}
deleted file mode 100644
--- a/browser/base/content/test/general/browser_fxa_oauth_with_keys.html
+++ /dev/null
@@ -1,33 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-  <meta charset="utf-8">
-  <title>fxa_oauth_test</title>
-</head>
-<body>
-<script>
-  window.onload = function() {
-    var event = new window.CustomEvent("WebChannelMessageToChrome", {
-      // Note: This intentionally sends a string instead of an object, to ensure both work
-      // (see browser_fxa_oauth.html for the other test)
-      detail: JSON.stringify({
-        id: "oauth_client_id",
-        message: {
-          command: "oauth_complete",
-          data: {
-            state: "state",
-            code: "code1",
-            closeWindow: "signin",
-            // Keys normally contain more information, but this is enough
-            // to keep Loop's tests happy.
-            keys: { kAr: { k: "kAr" }, kBr: { k: "kBr" }},
-          },
-        },
-      }),
-    });
-
-    window.dispatchEvent(event);
-  };
-</script>
-</body>
-</html>
--- a/browser/base/content/test/general/browser_misused_characters_in_strings.js
+++ b/browser/base/content/test/general/browser_misused_characters_in_strings.js
@@ -28,16 +28,20 @@ let gWhitelist = [{
     file: "netError.dtd",
     key: "inadequateSecurityError.longDesc",
     type: "single-quote"
   }, {
     file: "netError.dtd",
     key: "certerror.wrongSystemTime",
     type: "single-quote"
   }, {
+    file: "netError.dtd",
+    key: "certerror.wrongSystemTimeWithoutReference",
+    type: "single-quote"
+  }, {
     file: "phishing-afterload-warning-message.dtd",
     key: "safeb.blocked.malwarePage.shortDesc",
     type: "single-quote"
   }, {
     file: "phishing-afterload-warning-message.dtd",
     key: "safeb.blocked.unwantedPage.shortDesc",
     type: "single-quote"
   }, {
--- a/browser/components/downloads/content/allDownloadsViewOverlay.js
+++ b/browser/components/downloads/content/allDownloadsViewOverlay.js
@@ -1156,17 +1156,19 @@ DownloadsPlacesView.prototype = {
     return aCommand == "downloadsCmd_clearDownloads" ||
            document.activeElement == this._richlistbox;
   },
 
   // nsIController
   isCommandEnabled(aCommand) {
     switch (aCommand) {
       case "cmd_copy":
-        return this._richlistbox.selectedItems.length > 0;
+      case "downloadsCmd_openReferrer":
+      case "downloadShowMenuItem":
+        return this._richlistbox.selectedItems.length == 1;
       case "cmd_selectAll":
         return true;
       case "cmd_paste":
         return this._canDownloadClipboardURL();
       case "downloadsCmd_clearDownloads":
         return this._canClearDownloads();
       default:
         return Array.every(this._richlistbox.selectedItems,
--- a/browser/components/sessionstore/SessionStorage.jsm
+++ b/browser/components/sessionstore/SessionStorage.jsm
@@ -117,17 +117,17 @@ var SessionStorageInternal = {
       }
 
       let storageManager = aDocShell.QueryInterface(Ci.nsIDOMStorageManager);
       let window = aDocShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
 
       // There is no need to pass documentURI, it's only used to fill documentURI property of
       // domstorage event, which in this case has no consumer. Prevention of events in case
       // of missing documentURI will be solved in a followup bug to bug 600307.
-      let storage = storageManager.createStorage(window, principal, "");
+      let storage = storageManager.createStorage(window, principal, "", aDocShell.usePrivateBrowsing);
 
       for (let key of Object.keys(data)) {
         try {
           storage.setItem(key, data[key]);
         } catch (e) {
           // throws e.g. for URIs that can't have sessionStorage
           console.error(e);
         }
--- a/browser/config/mozconfigs/linux64/stylo
+++ b/browser/config/mozconfigs/linux64/stylo
@@ -1,3 +1,5 @@
 . "$topsrcdir/browser/config/mozconfigs/linux64/nightly"
 
+export LLVM_CONFIG="${TOOLTOOL_DIR}/clang/bin/llvm-config"
+
 ac_add_options --enable-stylo
--- a/browser/config/mozconfigs/linux64/stylo-debug
+++ b/browser/config/mozconfigs/linux64/stylo-debug
@@ -1,3 +1,5 @@
 . "$topsrcdir/browser/config/mozconfigs/linux64/debug"
 
+export LLVM_CONFIG="${TOOLTOOL_DIR}/clang/bin/llvm-config"
+
 ac_add_options --enable-stylo
--- a/browser/config/tooltool-manifests/linux64/releng.manifest
+++ b/browser/config/tooltool-manifests/linux64/releng.manifest
@@ -26,10 +26,18 @@
 {
 "version": "sccache rev b21198a7183a2fe226ff49348b1c0b51bae9f4f8",
 "algorithm": "sha512",
 "visibility": "public",
 "filename": "sccache2.tar.xz",
 "unpack": true,
 "digest": "b89c40dbf28c2bd54fadf017c15a8789f6e7611252a623cc3a1507e3dd6fc9e5a50d746e81776ba856e33fdc99b4a6413ba7c3ac0aed5f4835705da2b758ef22",
 "size": 1020700
+},
+{
+"version": "clang + llvm 3.9.0, built from SVN r290136",
+"size": 151724092,
+"digest": "4ab5ff2131e4ce4888d38c17feb192c19bc6ede83abef55af7d2f29e2446f6335dc860377fa25cbb0283b3958c0a3d377a3cfdc7705a85d4843e3ab357ddca7f",
+"algorithm": "sha512",
+"filename": "clang.tar.xz",
+"unpack": true
 }
 ]
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -439,16 +439,18 @@
 @RESPATH@/components/nsLivemarkService.js
 @RESPATH@/components/nsTaggingService.js
 @RESPATH@/components/UnifiedComplete.js
 @RESPATH@/components/nsPlacesExpiration.js
 @RESPATH@/components/PageIconProtocolHandler.js
 @RESPATH@/components/PlacesCategoriesStarter.js
 @RESPATH@/components/ColorAnalyzer.js
 @RESPATH@/components/PageThumbsProtocol.js
+@RESPATH@/components/mozProtocolHandler.js
+@RESPATH@/components/mozProtocolHandler.manifest
 @RESPATH@/components/nsDefaultCLH.manifest
 @RESPATH@/components/nsDefaultCLH.js
 @RESPATH@/components/nsContentPrefService.manifest
 @RESPATH@/components/nsContentPrefService.js
 @RESPATH@/components/nsContentDispatchChooser.manifest
 @RESPATH@/components/nsContentDispatchChooser.js
 @RESPATH@/components/nsHandlerService.manifest
 @RESPATH@/components/nsHandlerService.js
--- a/browser/locales/en-US/chrome/overrides/netError.dtd
+++ b/browser/locales/en-US/chrome/overrides/netError.dtd
@@ -192,19 +192,21 @@ was trying to connect. -->
 <!ENTITY weakCryptoUsed.title "Your connection is not secure">
 <!-- LOCALIZATION NOTE (weakCryptoUsed.longDesc2) - Do not translate
      "SSL_ERROR_NO_CYPHER_OVERLAP". -->
 <!ENTITY weakCryptoUsed.longDesc2 "Advanced info: SSL_ERROR_NO_CYPHER_OVERLAP">
 <!ENTITY weakCryptoAdvanced.title "Advanced">
 <!ENTITY weakCryptoAdvanced.longDesc "<span class='hostname'></span> uses security technology that is outdated and vulnerable to attack. An attacker could easily reveal information which you thought to be safe.">
 <!ENTITY weakCryptoAdvanced.override "(Not secure) Try loading <span class='hostname'></span> using outdated security">
 
-<!-- LOCALIZATION NOTE (certerror.wrongSystemTime) - The <span id='..' /> tags will be injected with actual values,
-     please leave them unchanged. -->
-<!ENTITY certerror.wrongSystemTime "<p>A secure connection to <span id='wrongSystemTime_URL'/> isn’t possible because your clock appears to show the wrong time.</p> <p>Your computer thinks it is <span id='wrongSystemTime_systemDate'/>, when it should be <span id='wrongSystemTime_actualDate'/>. To fix this problem, change your date and time settings to match the correct time.</p>">
+<!-- LOCALIZATION NOTE (certerror.wrongSystemTime,
+                        certerror.wrongSystemTimeWithoutReference) - The <span id='..' />
+     tags will be injected with actual values, please leave them unchanged. -->
+<!ENTITY certerror.wrongSystemTime "<p> &brandShortName; did not connect to <span id='wrongSystemTime_URL'/> because your computer’s clock appears to show the wrong time and this is preventing a secure connection.</p> <p>Your computer is set to <span id='wrongSystemTime_systemDate'/>, when it should be <span id='wrongSystemTime_actualDate'/>. To fix this problem, change your date and time settings to match the correct time.</p>">
+<!ENTITY certerror.wrongSystemTimeWithoutReference "<p>&brandShortName; did not connect to <span id='wrongSystemTimeWithoutReference_URL'/> because your computer’s clock appears to show the wrong time and this is preventing a secure connection.</p> <p>Your computer is set to <span id='wrongSystemTimeWithoutReference_systemDate'/>. To fix this problem, change your date and time settings to match the correct time.</p>">
 
 <!ENTITY certerror.pagetitle1  "Insecure Connection">
 <!ENTITY certerror.whatShouldIDo.badStsCertExplanation "This site uses HTTP
 Strict Transport Security (HSTS) to specify that &brandShortName; may only connect
 to it securely. As a result, it is not possible to add an exception for this
 certificate.">
 <!ENTITY certerror.copyToClipboard.label "Copy text to clipboard">
 
--- a/browser/modules/webrtcUI.jsm
+++ b/browser/modules/webrtcUI.jsm
@@ -549,16 +549,21 @@ function prompt(aBrowser, aRequest) {
           this.remove();
           return true;
         }
       }
 
       function listDevices(menupopup, devices) {
         while (menupopup.lastChild)
           menupopup.removeChild(menupopup.lastChild);
+        // Removing the child nodes of the menupopup doesn't clear the value
+        // attribute of the menulist. This can have unfortunate side effects
+        // when the list is rebuilt with a different content, so we remove
+        // the value attribute explicitly.
+        menupopup.parentNode.removeAttribute("value");
 
         for (let device of devices)
           addDeviceToList(menupopup, device.name, device.deviceIndex);
       }
 
       function listScreenShareDevices(menupopup, devices) {
         while (menupopup.lastChild)
           menupopup.removeChild(menupopup.lastChild);
--- a/build/build-clang/build-clang.py
+++ b/build/build-clang/build-clang.py
@@ -465,42 +465,31 @@ if __name__ == "__main__":
     cc = get_tool(config, "cc")
     cxx = get_tool(config, "cxx")
     ld = get_tool(config, "link" if is_windows() else "ld")
     ar = get_tool(config, "lib" if is_windows() else "ar")
     ranlib = None if is_windows() else get_tool(config, "ranlib")
 
     if not os.path.exists(source_dir):
         os.makedirs(source_dir)
-    if os.path.exists(llvm_source_dir):
-        svn_update(llvm_source_dir, llvm_revision)
-    else:
-        svn_co(source_dir, llvm_repo, llvm_source_dir, llvm_revision)
-    if os.path.exists(clang_source_dir):
-        svn_update(clang_source_dir, llvm_revision)
-    else:
-        svn_co(source_dir, clang_repo, clang_source_dir, llvm_revision)
-    if os.path.exists(compiler_rt_source_dir):
-        svn_update(compiler_rt_source_dir, llvm_revision)
-    else:
-        svn_co(source_dir, compiler_repo, compiler_rt_source_dir, llvm_revision)
-    if os.path.exists(libcxx_source_dir):
-        svn_update(libcxx_source_dir, llvm_revision)
-    else:
-        svn_co(source_dir, libcxx_repo, libcxx_source_dir, llvm_revision)
+
+    def checkout_or_update(repo, checkout_dir):
+        if os.path.exists(checkout_dir):
+            svn_update(checkout_dir, llvm_revision)
+        else:
+            svn_co(source_dir, repo, checkout_dir, llvm_revision)
+
+    checkout_or_update(llvm_repo, llvm_source_dir)
+    checkout_or_update(clang_repo, clang_source_dir)
+    checkout_or_update(compiler_repo, compiler_rt_source_dir)
+    checkout_or_update(libcxx_repo, libcxx_source_dir)
     if libcxxabi_repo:
-        if os.path.exists(libcxxabi_source_dir):
-            svn_update(libcxxabi_source_dir, llvm_revision)
-        else:
-            svn_co(source_dir, libcxxabi_repo, libcxxabi_source_dir, llvm_revision)
+        checkout_or_update(libcxxabi_repo, libcxxabi_source_dir)
     if extra_repo:
-        if os.path.exists(extra_source_dir):
-            svn_update(extra_source_dir, llvm_revision)
-        else:
-            svn_co(source_dir, extra_repo, extra_source_dir, llvm_revision)
+        checkout_or_update(extra_repo, extra_source_dir)
     for p in config.get("patches", {}).get(get_platform(), []):
         patch(p, source_dir)
 
     symlinks = [(source_dir + "/clang",
                  llvm_source_dir + "/tools/clang"),
                 (source_dir + "/extra",
                  llvm_source_dir + "/tools/clang/tools/extra"),
                 (source_dir + "/compiler-rt",
--- a/build/build-clang/clang-static-analysis-linux64.json
+++ b/build/build-clang/clang-static-analysis-linux64.json
@@ -1,30 +1,27 @@
 {
-    "llvm_revision": "262557",
+    "llvm_revision": "290136",
     "stages": "3",
     "build_libcxx": true,
     "build_type": "Release",
     "assertions": false,
-    "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/tags/RELEASE_380/final",
-    "clang_repo": "https://llvm.org/svn/llvm-project/cfe/tags/RELEASE_380/final",
-    "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/tags/RELEASE_380/final",
-    "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/tags/RELEASE_380/final",
-    "libcxxabi_repo": "https://llvm.org/svn/llvm-project/libcxxabi/tags/RELEASE_380/final",
+    "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/tags/RELEASE_390/final",
+    "clang_repo": "https://llvm.org/svn/llvm-project/cfe/tags/RELEASE_390/final",
+    "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/tags/RELEASE_390/final",
+    "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/tags/RELEASE_390/final",
+    "libcxxabi_repo": "https://llvm.org/svn/llvm-project/libcxxabi/tags/RELEASE_390/final",
     "python_path": "/usr/bin/python2.7",
     "gcc_dir": "/home/worker/workspace/build/src/gcc",
     "cc": "/home/worker/workspace/build/src/gcc/bin/gcc",
     "cxx": "/home/worker/workspace/build/src/gcc/bin/g++",
     "patches": {
         "macosx64": [
-          "llvm-debug-frame.patch",
-          "return-empty-string-non-mangled.patch"
+          "llvm-debug-frame.patch"
         ],
         "linux64": [
-          "llvm-debug-frame.patch",
-          "return-empty-string-non-mangled.patch"
+          "llvm-debug-frame.patch"
         ],
         "linux32": [
-          "llvm-debug-frame.patch",
-          "return-empty-string-non-mangled.patch"
+          "llvm-debug-frame.patch"
         ]
     }
 }
--- a/build/gyp.mozbuild
+++ b/build/gyp.mozbuild
@@ -104,8 +104,11 @@ if CONFIG['ARM_ARCH']:
 
 # Don't try to compile ssse3/sse4.1 code if toolchain doesn't support
 if CONFIG['INTEL_ARCHITECTURE']:
     if not CONFIG['HAVE_TOOLCHAIN_SUPPORT_MSSSE3'] or not CONFIG['HAVE_TOOLCHAIN_SUPPORT_MSSE4_1']:
         gyp_vars['yuv_disable_asm'] = 1
 
 if CONFIG['MACOS_SDK_DIR']:
     gyp_vars['mac_sdk_path'] = CONFIG['MACOS_SDK_DIR']
+
+if not CONFIG['MOZ_SYSTEM_LIBVPX']:
+    gyp_vars['libvpx_dir'] =  '/media/libvpx/libvpx'
--- a/caps/tests/mochitest/browser_checkloaduri.js
+++ b/caps/tests/mochitest/browser_checkloaduri.js
@@ -16,58 +16,62 @@ const URLs = new Map([
     ["feed:chrome://foo/content/bar.xul", false, false, false],
     ["view-source:http://www.example2.com", false, false, true],
     ["view-source:https://www.example2.com", false, false, true],
     ["view-source:feed:http://www.example2.com", false, false, true],
     ["feed:view-source:http://www.example2.com", false, false, false],
     ["data:text/html,Hi", true, false, true],
     ["view-source:data:text/html,Hi", false, false, true],
     ["javascript:alert('hi')", true, false, true],
+    ["moz://a", false, false, true],
   ]],
   ["feed:http://www.example.com", [
     ["http://www.example2.com", true, true, true],
     ["feed:http://www.example2.com", true, true, true],
     ["https://www.example2.com", true, true, true],
     ["feed:https://www.example2.com", true, true, true],
     ["chrome://foo/content/bar.xul", false, false, true],
     ["feed:chrome://foo/content/bar.xul", false, false, false],
     ["view-source:http://www.example2.com", false, false, true],
     ["view-source:https://www.example2.com", false, false, true],
     ["view-source:feed:http://www.example2.com", false, false, true],
     ["feed:view-source:http://www.example2.com", false, false, false],
     ["data:text/html,Hi", true, false, true],
     ["view-source:data:text/html,Hi", false, false, true],
     ["javascript:alert('hi')", true, false, true],
+    ["moz://a", false, false, true],
   ]],
   ["view-source:http://www.example.com", [
     ["http://www.example2.com", true, true, true],
     ["feed:http://www.example2.com", false, false, true],
     ["https://www.example2.com", true, true, true],
     ["feed:https://www.example2.com", false, false, true],
     ["chrome://foo/content/bar.xul", false, false, true],
     ["feed:chrome://foo/content/bar.xul", false, false, false],
     ["view-source:http://www.example2.com", true, true, true],
     ["view-source:https://www.example2.com", true, true, true],
     ["view-source:feed:http://www.example2.com", false, false, true],
     ["feed:view-source:http://www.example2.com", false, false, false],
     ["data:text/html,Hi", true, false, true],
     ["view-source:data:text/html,Hi", true, false, true],
     ["javascript:alert('hi')", true, false, true],
+    ["moz://a", false, false, true],
   ]],
   ["about:foo", [
     ["about:foo?", true, true, true],
     ["about:foo?bar", true, true, true],
     ["about:foo#", true, true, true],
     ["about:foo#bar", true, true, true],
     ["about:foo?#", true, true, true],
     ["about:foo?bar#baz", true, true, true],
     ["about:bar", false, false, true],
     ["about:bar?foo#baz", false, false, true],
     ["about:bar?foo", false, false, true],
     ["http://www.example.com/", true, true, true],
+    ["moz://a", false, false, true],
   ]],
 ]);
 
 function testURL(source, target, canLoad, canLoadWithoutInherit, canCreate, flags) {
   let threw = false;
   let targetURI;
   try {
     targetURI = makeURI(target);
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -937,17 +937,23 @@ endif
 # choices, and Cargo only supports two, we choose to enable various
 # optimization levels in our Cargo.toml files all the time, and override the
 # optimization level here, if necessary.  (The Cargo.toml files already
 # specify debug-assertions appropriately for --{disable,enable}-debug.)
 ifndef MOZ_OPTIMIZE
 rustflags_override = RUSTFLAGS='-C opt-level=0'
 endif
 
-CARGO_BUILD = env $(rustflags_override) CARGO_TARGET_DIR=. RUSTC=$(RUSTC) MOZ_DIST=$(ABS_DIST) $(CARGO) build $(cargo_build_flags)
+CARGO_BUILD = env $(rustflags_override) \
+	CARGO_TARGET_DIR=. \
+	RUSTC=$(RUSTC) \
+	MOZ_DIST=$(ABS_DIST) \
+	LIBCLANG_PATH=$(MOZ_LIBCLANG_PATH) \
+	CLANG_PATH=$(MOZ_CLANG_PATH) \
+	$(CARGO) build $(cargo_build_flags)
 
 ifdef RUST_LIBRARY_FILE
 
 ifdef RUST_LIBRARY_FEATURES
 rust_features_flag := --features "$(RUST_LIBRARY_FEATURES)"
 endif
 
 # Assume any system libraries rustc links against are already in the target's LIBS.
--- a/devtools/client/themes/toolbox.css
+++ b/devtools/client/themes/toolbox.css
@@ -69,22 +69,20 @@
   left: 0;
   right: 0;
   bottom: 0;
   display: flex;
 }
 
 /* Set flex attribute to Toolbox buttons and Picker container so,
    they don't overlap with the tab bar */
+#toolbox-buttons-start,
 #toolbox-buttons-end {
   display: flex;
-}
-
-#toolbox-picker-container {
-  display: flex;
+  align-items: stretch;
 }
 
 #toolbox-buttons-start {
   border: solid 0 var(--theme-splitter-color);
   border-inline-end-width: 1px;
 }
 
 /* Toolbox tabs */
@@ -313,18 +311,19 @@
 
 #toolbox-controls-separator {
   margin: 0;
 }
 
 /* Command buttons */
 
 .command-button {
-  padding: 0;
-  margin: 0;
+  /* !important is needed to override .devtools-button rules in common.css */
+  padding: 0 !important;
+  margin: 0 !important;
   position: relative;
 }
 
 .command-button::before {
   opacity: 0.7;
 }
 
 .command-button:hover {
--- a/dom/base/TimeoutManager.cpp
+++ b/dom/base/TimeoutManager.cpp
@@ -127,17 +127,17 @@ TimeoutManager::Initialize()
   Preferences::AddIntVarCache(&gMinBackgroundTimeoutValue,
                               "dom.min_background_timeout_value",
                               DEFAULT_MIN_BACKGROUND_TIMEOUT_VALUE);
   Preferences::AddIntVarCache(&gMinTrackingTimeoutValue,
                               "dom.min_tracking_timeout_value",
                               DEFAULT_MIN_TRACKING_TIMEOUT_VALUE);
   Preferences::AddIntVarCache(&gMinTrackingBackgroundTimeoutValue,
                               "dom.min_tracking_background_timeout_value",
-                              DEFAULT_MIN_BACKGROUND_TIMEOUT_VALUE);
+                              DEFAULT_MIN_TRACKING_BACKGROUND_TIMEOUT_VALUE);
   Preferences::AddIntVarCache(&gTimeoutBucketingStrategy,
                               "dom.timeout_bucketing_strategy",
                               TRACKING_SEPARATE_TIMEOUT_BUCKETING_STRATEGY);
 }
 
 uint32_t
 TimeoutManager::GetTimeoutId(Timeout::Reason aReason)
 {
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -184,16 +184,17 @@
 #include "nsParserCIID.h"
 #include "nsParserConstants.h"
 #include "nsPIDOMWindow.h"
 #include "nsPresContext.h"
 #include "nsPrintfCString.h"
 #include "nsReferencedElement.h"
 #include "nsSandboxFlags.h"
 #include "nsScriptSecurityManager.h"
+#include "nsSerializationHelper.h"
 #include "nsStreamUtils.h"
 #include "nsTextEditorState.h"
 #include "nsTextFragment.h"
 #include "nsTextNode.h"
 #include "nsThreadUtils.h"
 #include "nsUnicharUtilCIID.h"
 #include "nsUnicodeProperties.h"
 #include "nsViewManager.h"
@@ -9774,8 +9775,38 @@ nsContentUtils::AppendDocumentLevelNativ
     if (nsIFrame* scrollFrame = presShell->GetRootScrollFrame()) {
       nsIAnonymousContentCreator* creator = do_QueryFrame(scrollFrame);
       MOZ_ASSERT(creator,
                  "scroll frame should always implement nsIAnonymousContentCreator");
       creator->AppendAnonymousContentTo(aElements, 0);
     }
   }
 }
+
+/* static */ void
+nsContentUtils::GetContentPolicyTypeForUIImageLoading(nsIContent* aLoadingNode,
+                                                      nsIPrincipal** aLoadingPrincipal,
+                                                      nsContentPolicyType& aContentPolicyType)
+{
+  // Use the serialized loadingPrincipal from the image element. Fall back
+  // to mContent's principal (SystemPrincipal) if not available.
+  aContentPolicyType = nsIContentPolicy::TYPE_INTERNAL_IMAGE;
+  nsCOMPtr<nsIPrincipal> loadingPrincipal = aLoadingNode->NodePrincipal();
+  nsAutoString imageLoadingPrincipal;
+  aLoadingNode->GetAttr(kNameSpaceID_None, nsGkAtoms::loadingprincipal,
+                        imageLoadingPrincipal);
+  if (!imageLoadingPrincipal.IsEmpty()) {
+    nsCOMPtr<nsISupports> serializedPrincipal;
+    NS_DeserializeObject(NS_ConvertUTF16toUTF8(imageLoadingPrincipal),
+                         getter_AddRefs(serializedPrincipal));
+    loadingPrincipal = do_QueryInterface(serializedPrincipal);
+
+    if (loadingPrincipal) {
+      // Set the content policy type to TYPE_INTERNAL_IMAGE_FAVICON for
+      // indicating it's a favicon loading.
+      aContentPolicyType = nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON;
+    } else {
+      // Fallback if the deserialization is failed.
+      loadingPrincipal = aLoadingNode->NodePrincipal();
+    }
+  }
+  loadingPrincipal.forget(aLoadingPrincipal);
+}
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -2738,16 +2738,26 @@ public:
    * aDocument to aElements.  Document level NAC subtrees are those created
    * by ancestor frames of the document element's primary frame, such as
    * the scrollbar elements created by the root scroll frame.
    */
   static void AppendDocumentLevelNativeAnonymousContentTo(
       nsIDocument* aDocument,
       nsTArray<nsIContent*>& aElements);
 
+  /**
+   * Returns the content policy type that should be used for loading images
+   * for displaying in the UI.  The sources of such images can be <xul:image>,
+   * <xul:menuitem> on OSX where we load the image through nsMenuItemIconX, etc.
+   */
+  static void
+  GetContentPolicyTypeForUIImageLoading(nsIContent* aLoadingNode,
+                                        nsIPrincipal** aLoadingPrincipal,
+                                        nsContentPolicyType& aContentPolicyType);
+
 private:
   static bool InitializeEventTable();
 
   static nsresult EnsureStringBundle(PropertiesFile aFile);
 
   static bool CanCallerAccess(nsIPrincipal* aSubjectPrincipal,
                                 nsIPrincipal* aPrincipal);
 
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -64,31 +64,20 @@
 #include "nsIDOMXULButtonElement.h"
 #include "nsIDOMXULCheckboxElement.h"
 #include "nsIDOMXULPopupElement.h"
 
 // Event related includes
 #include "nsIDOMEventTarget.h"
 
 // CSS related includes
-#include "nsCSSRules.h"
 #include "nsIDOMCSSRule.h"
 #include "nsMemory.h"
 
 // includes needed for the prototype chain interfaces
-#include "nsIDOMCSSKeyframeRule.h"
-#include "nsIDOMCSSKeyframesRule.h"
-#include "nsIDOMCSSImportRule.h"
-#include "nsIDOMCSSMediaRule.h"
-#include "nsIDOMCSSFontFaceRule.h"
-#include "nsIDOMCSSMozDocumentRule.h"
-#include "nsIDOMCSSSupportsRule.h"
-#include "nsIDOMCSSCounterStyleRule.h"
-#include "nsIDOMCSSPageRule.h"
-#include "nsIDOMCSSStyleRule.h"
 #include "nsIDOMXULCommandDispatcher.h"
 #include "nsIControllers.h"
 #ifdef MOZ_XUL
 #include "nsITreeSelection.h"
 #include "nsITreeContentView.h"
 #include "nsITreeView.h"
 #include "nsIXULTemplateBuilder.h"
 #endif
@@ -184,26 +173,16 @@ static nsDOMClassInfoData sClassInfoData
                            nsIXPCScriptable::WANT_RESOLVE |
                            nsIXPCScriptable::WANT_HASINSTANCE |
                            nsIXPCScriptable::WANT_CALL |
                            nsIXPCScriptable::WANT_CONSTRUCT |
                            nsIXPCScriptable::DONT_ENUM_QUERY_INTERFACE)
 
   // Misc Core related classes
 
-  // CSS classes
-  NS_DEFINE_CLASSINFO_DATA(CSSStyleRule, nsDOMGenericSH,
-                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
-  NS_DEFINE_CLASSINFO_DATA(CSSImportRule, nsDOMGenericSH,
-                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
-  NS_DEFINE_CLASSINFO_DATA(CSSMediaRule, nsDOMGenericSH,
-                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
-  NS_DEFINE_CLASSINFO_DATA(CSSNameSpaceRule, nsDOMGenericSH,
-                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
-
   // XUL classes
 #ifdef MOZ_XUL
   NS_DEFINE_CHROME_XBL_CLASSINFO_DATA(XULCommandDispatcher, nsDOMGenericSH,
                                       DOM_DEFAULT_SCRIPTABLE_FLAGS)
 #endif
   NS_DEFINE_CHROME_XBL_CLASSINFO_DATA(XULControllers, nsNonDOMObjectSH,
                                       DEFAULT_SCRIPTABLE_FLAGS)
 #ifdef MOZ_XUL
@@ -216,55 +195,32 @@ static nsDOMClassInfoData sClassInfoData
 #ifdef MOZ_XUL
   NS_DEFINE_CHROME_XBL_CLASSINFO_DATA(XULTemplateBuilder, nsDOMGenericSH,
                                       DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CHROME_XBL_CLASSINFO_DATA(XULTreeBuilder, nsDOMGenericSH,
                                       DEFAULT_SCRIPTABLE_FLAGS)
 #endif
 
-  NS_DEFINE_CLASSINFO_DATA(CSSMozDocumentRule, nsDOMGenericSH,
-                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
-
-  NS_DEFINE_CLASSINFO_DATA(CSSSupportsRule, nsDOMGenericSH,
-                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
-
-  NS_DEFINE_CLASSINFO_DATA(CSSFontFaceRule, nsDOMGenericSH,
-                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
-
   NS_DEFINE_CHROME_ONLY_CLASSINFO_DATA(ContentFrameMessageManager,
                                        nsMessageManagerSH<nsEventTargetSH>,
                                        DOM_DEFAULT_SCRIPTABLE_FLAGS |
                                        nsIXPCScriptable::WANT_ENUMERATE |
                                        nsIXPCScriptable::IS_GLOBAL_OBJECT)
   NS_DEFINE_CHROME_ONLY_CLASSINFO_DATA(ContentProcessMessageManager,
                                        nsMessageManagerSH<nsDOMGenericSH>,
                                        DOM_DEFAULT_SCRIPTABLE_FLAGS |
                                        nsIXPCScriptable::WANT_ENUMERATE |
                                        nsIXPCScriptable::IS_GLOBAL_OBJECT)
   NS_DEFINE_CHROME_ONLY_CLASSINFO_DATA(ChromeMessageBroadcaster, nsDOMGenericSH,
                                        DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CHROME_ONLY_CLASSINFO_DATA(ChromeMessageSender, nsDOMGenericSH,
                                        DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
 
-  NS_DEFINE_CLASSINFO_DATA(CSSKeyframeRule, nsDOMGenericSH,
-                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
-  NS_DEFINE_CLASSINFO_DATA(CSSKeyframesRule, nsDOMGenericSH,
-                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
-
-  NS_DEFINE_CLASSINFO_DATA(CSSCounterStyleRule, nsDOMGenericSH,
-                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
-
-  NS_DEFINE_CLASSINFO_DATA(CSSPageRule, nsDOMGenericSH,
-                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
-
-  NS_DEFINE_CLASSINFO_DATA(CSSFontFeatureValuesRule, nsDOMGenericSH,
-                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
-
   NS_DEFINE_CHROME_XBL_CLASSINFO_DATA(XULControlElement, nsDOMGenericSH,
                                       DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CHROME_XBL_CLASSINFO_DATA(XULLabeledControlElement, nsDOMGenericSH,
                                       DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CHROME_XBL_CLASSINFO_DATA(XULButtonElement, nsDOMGenericSH,
                                       DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CHROME_XBL_CLASSINFO_DATA(XULCheckboxElement, nsDOMGenericSH,
                                       DOM_DEFAULT_SCRIPTABLE_FLAGS)
@@ -507,32 +463,16 @@ nsDOMClassInfo::Init()
   DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(DOMPrototype, nsIDOMDOMConstructor)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMDOMConstructor)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(DOMConstructor, nsIDOMDOMConstructor)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMDOMConstructor)
   DOM_CLASSINFO_MAP_END
 
-  DOM_CLASSINFO_MAP_BEGIN(CSSStyleRule, nsIDOMCSSStyleRule)
-    DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSStyleRule)
-  DOM_CLASSINFO_MAP_END
-
-  DOM_CLASSINFO_MAP_BEGIN(CSSImportRule, nsIDOMCSSImportRule)
-    DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSImportRule)
-  DOM_CLASSINFO_MAP_END
-
-  DOM_CLASSINFO_MAP_BEGIN(CSSMediaRule, nsIDOMCSSMediaRule)
-    DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSMediaRule)
-  DOM_CLASSINFO_MAP_END
-
-  DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(CSSNameSpaceRule, nsIDOMCSSRule)
-    DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSRule)
-  DOM_CLASSINFO_MAP_END
-
 #ifdef MOZ_XUL
   DOM_CLASSINFO_MAP_BEGIN(XULCommandDispatcher, nsIDOMXULCommandDispatcher)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMXULCommandDispatcher)
   DOM_CLASSINFO_MAP_END
 #endif
 
   DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(XULControllers, nsIControllers)
     DOM_CLASSINFO_MAP_ENTRY(nsIControllers)
@@ -556,28 +496,16 @@ nsDOMClassInfo::Init()
 
   DOM_CLASSINFO_MAP_BEGIN(XULTreeBuilder, nsIXULTreeBuilder)
     DOM_CLASSINFO_MAP_ENTRY(nsIXULTreeBuilder)
     DOM_CLASSINFO_MAP_ENTRY(nsIXULTemplateBuilder)
     DOM_CLASSINFO_MAP_ENTRY(nsITreeView)
   DOM_CLASSINFO_MAP_END
 #endif
 
-  DOM_CLASSINFO_MAP_BEGIN(CSSMozDocumentRule, nsIDOMCSSMozDocumentRule)
-    DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSMozDocumentRule)
-  DOM_CLASSINFO_MAP_END
-
-  DOM_CLASSINFO_MAP_BEGIN(CSSSupportsRule, nsIDOMCSSSupportsRule)
-    DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSSupportsRule)
-  DOM_CLASSINFO_MAP_END
-
-  DOM_CLASSINFO_MAP_BEGIN(CSSFontFaceRule, nsIDOMCSSFontFaceRule)
-    DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSFontFaceRule)
-  DOM_CLASSINFO_MAP_END
-
   DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(ContentFrameMessageManager, nsISupports)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
     DOM_CLASSINFO_MAP_ENTRY(nsIMessageListenerManager)
     DOM_CLASSINFO_MAP_ENTRY(nsIMessageSender)
     DOM_CLASSINFO_MAP_ENTRY(nsISyncMessageSender)
     DOM_CLASSINFO_MAP_ENTRY(nsIContentFrameMessageManager)
   DOM_CLASSINFO_MAP_END
 
@@ -598,36 +526,16 @@ nsDOMClassInfo::Init()
 
   DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(ChromeMessageSender, nsISupports)
     DOM_CLASSINFO_MAP_ENTRY(nsIFrameScriptLoader)
     DOM_CLASSINFO_MAP_ENTRY(nsIProcessScriptLoader)
     DOM_CLASSINFO_MAP_ENTRY(nsIMessageListenerManager)
     DOM_CLASSINFO_MAP_ENTRY(nsIMessageSender)
   DOM_CLASSINFO_MAP_END
 
-  DOM_CLASSINFO_MAP_BEGIN(CSSKeyframeRule, nsIDOMCSSKeyframeRule)
-    DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSKeyframeRule)
-  DOM_CLASSINFO_MAP_END
-
-  DOM_CLASSINFO_MAP_BEGIN(CSSKeyframesRule, nsIDOMCSSKeyframesRule)
-    DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSKeyframesRule)
-  DOM_CLASSINFO_MAP_END
-
-  DOM_CLASSINFO_MAP_BEGIN(CSSCounterStyleRule, nsIDOMCSSCounterStyleRule)
-    DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSCounterStyleRule)
-  DOM_CLASSINFO_MAP_END
-
-  DOM_CLASSINFO_MAP_BEGIN(CSSPageRule, nsIDOMCSSPageRule)
-    DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSPageRule)
-  DOM_CLASSINFO_MAP_END
-
-  DOM_CLASSINFO_MAP_BEGIN(CSSFontFeatureValuesRule, nsIDOMCSSFontFeatureValuesRule)
-    DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSFontFeatureValuesRule)
-  DOM_CLASSINFO_MAP_END
-
   DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(XULControlElement, nsIDOMXULControlElement)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMXULControlElement)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(XULLabeledControlElement, nsIDOMXULLabeledControlElement)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMXULLabeledControlElement)
   DOM_CLASSINFO_MAP_END
 
--- a/dom/base/nsDOMClassInfoID.h
+++ b/dom/base/nsDOMClassInfoID.h
@@ -14,58 +14,36 @@
 
 #include "nsIXPCScriptable.h"
 
 enum nsDOMClassInfoID
 {
   eDOMClassInfo_DOMPrototype_id,
   eDOMClassInfo_DOMConstructor_id,
 
-  // CSS classes
-  eDOMClassInfo_CSSStyleRule_id,
-  eDOMClassInfo_CSSImportRule_id,
-  eDOMClassInfo_CSSMediaRule_id,
-  eDOMClassInfo_CSSNameSpaceRule_id,
-
   // XUL classes
 #ifdef MOZ_XUL
   eDOMClassInfo_XULCommandDispatcher_id,
 #endif
   eDOMClassInfo_XULControllers_id,
 #ifdef MOZ_XUL
   eDOMClassInfo_TreeSelection_id,
   eDOMClassInfo_TreeContentView_id,
 #endif
 
 #ifdef MOZ_XUL
   eDOMClassInfo_XULTemplateBuilder_id,
   eDOMClassInfo_XULTreeBuilder_id,
 #endif
 
-  eDOMClassInfo_CSSMozDocumentRule_id,
-  eDOMClassInfo_CSSSupportsRule_id,
-
-  // @font-face in CSS
-  eDOMClassInfo_CSSFontFaceRule_id,
-
   eDOMClassInfo_ContentFrameMessageManager_id,
   eDOMClassInfo_ContentProcessMessageManager_id,
   eDOMClassInfo_ChromeMessageBroadcaster_id,
   eDOMClassInfo_ChromeMessageSender_id,
 
-  eDOMClassInfo_CSSKeyframeRule_id,
-  eDOMClassInfo_CSSKeyframesRule_id,
-
-  // @counter-style in CSS
-  eDOMClassInfo_CSSCounterStyleRule_id,
-
-  eDOMClassInfo_CSSPageRule_id,
-
-  eDOMClassInfo_CSSFontFeatureValuesRule_id,
-
   eDOMClassInfo_XULControlElement_id,
   eDOMClassInfo_XULLabeledControlElement_id,
   eDOMClassInfo_XULButtonElement_id,
   eDOMClassInfo_XULCheckboxElement_id,
   eDOMClassInfo_XULPopupElement_id,
 
   // This one better be the last one in this list
   eDOMClassInfoIDCount
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -5193,47 +5193,45 @@ nsDocument::StyleRuleChanged(StyleSheet*
                              css::Rule* aStyleRule)
 {
   NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleChanged, (aSheet));
 
   if (StyleSheetChangeEventsEnabled()) {
     DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent,
                                "StyleRuleChanged",
                                mRule,
-                               aStyleRule ? aStyleRule->GetDOMRule() : nullptr);
+                               aStyleRule);
   }
 }
 
 void
 nsDocument::StyleRuleAdded(StyleSheet* aSheet,
                            css::Rule* aStyleRule)
 {
   NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleAdded, (aSheet));
 
   if (StyleSheetChangeEventsEnabled()) {
     DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent,
                                "StyleRuleAdded",
                                mRule,
-                               aStyleRule ? aStyleRule->GetDOMRule()
-                                          : nullptr);
+                               aStyleRule);
   }
 }
 
 void
 nsDocument::StyleRuleRemoved(StyleSheet* aSheet,
                              css::Rule* aStyleRule)
 {
   NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleRemoved, (aSheet));
 
   if (StyleSheetChangeEventsEnabled()) {
     DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent,
                                "StyleRuleRemoved",
                                mRule,
-                               aStyleRule ? aStyleRule->GetDOMRule()
-                                          : nullptr);
+                               aStyleRule);
   }
 }
 
 #undef DO_STYLESHEET_NOTIFICATION
 
 already_AddRefed<AnonymousContent>
 nsIDocument::InsertAnonymousContent(Element& aElement, ErrorResult& aRv)
 {
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -10670,20 +10670,19 @@ nsGlobalWindow::GetSessionStorage(ErrorR
     nsresult rv;
 
     nsCOMPtr<nsIDOMStorageManager> storageManager = do_QueryInterface(docShell, &rv);
     if (NS_FAILED(rv)) {
       aError.Throw(rv);
       return nullptr;
     }
 
-    MOZ_ASSERT_IF(!mIsChrome, (principal->GetPrivateBrowsingId() > 0) == IsPrivateBrowsing());
-
     nsCOMPtr<nsIDOMStorage> storage;
     aError = storageManager->CreateStorage(AsInner(), principal, documentURI,
+                                           IsPrivateBrowsing(),
                                            getter_AddRefs(storage));
     if (aError.Failed()) {
       return nullptr;
     }
 
     mSessionStorage = static_cast<Storage*>(storage.get());
     MOZ_ASSERT(mSessionStorage);
 
@@ -10736,22 +10735,19 @@ nsGlobalWindow::GetLocalStorage(ErrorRes
     nsString documentURI;
     if (mDoc) {
       aError = mDoc->GetDocumentURI(documentURI);
       if (NS_WARN_IF(aError.Failed())) {
         return nullptr;
       }
     }
 
-    //XXX huseby -- was causing crashes see bug 1321969. Bug 1322760 covers
-    //investigating and fixing the following assert.
-    //MOZ_DIAGNOSTIC_ASSERT((principal->GetPrivateBrowsingId() > 0) == IsPrivateBrowsing());
-
     nsCOMPtr<nsIDOMStorage> storage;
     aError = storageManager->CreateStorage(AsInner(), principal, documentURI,
+                                           IsPrivateBrowsing(),
                                            getter_AddRefs(storage));
     if (aError.Failed()) {
       return nullptr;
     }
 
     mLocalStorage = static_cast<Storage*>(storage.get());
     MOZ_ASSERT(mLocalStorage);
   }
@@ -11594,24 +11590,19 @@ nsGlobalWindow::Observe(nsISupports* aSu
     bool fireMozStorageChanged = false;
     nsAutoString eventType;
     eventType.AssignLiteral("storage");
     principal = GetPrincipal();
     if (!principal) {
       return NS_OK;
     }
 
-    uint32_t privateBrowsingId = 0;
-    nsIPrincipal *storagePrincipal = changingStorage->GetPrincipal();
-    rv = storagePrincipal->GetPrivateBrowsingId(&privateBrowsingId);
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
-
-    MOZ_ASSERT((privateBrowsingId > 0) == isPrivateBrowsing);
+    if (changingStorage->IsPrivate() != IsPrivateBrowsing()) {
+      return NS_OK;
+    }
 
     switch (changingStorage->GetType())
     {
     case Storage::SessionStorage:
     {
       bool check = false;
 
       nsCOMPtr<nsIDOMStorageManager> storageManager = do_QueryInterface(GetDocShell());
--- a/dom/base/nsWrapperCache.h
+++ b/dom/base/nsWrapperCache.h
@@ -140,17 +140,17 @@ public:
   void UpdateWrapper(JSObject* aNewObject, const JSObject* aOldObject)
   {
     if (mWrapper) {
       MOZ_ASSERT(mWrapper == aOldObject);
       mWrapper = aNewObject;
     }
   }
 
-  bool PreservingWrapper()
+  bool PreservingWrapper() const
   {
     return HasWrapperFlag(WRAPPER_BIT_PRESERVED);
   }
 
   bool IsDOMBinding() const
   {
     return !HasWrapperFlag(WRAPPER_IS_NOT_DOM_BINDING);
   }
@@ -159,17 +159,17 @@ public:
    * Wrap the object corresponding to this wrapper cache. If non-null is
    * returned, the object has already been stored in the wrapper cache.
    */
   virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) = 0;
 
   /**
    * Returns true if the object has a non-gray wrapper.
    */
-  bool IsBlack();
+  bool IsBlack() const;
 
   /**
    * Returns true if the object has a black wrapper,
    * and all the GC things it is keeping alive are black too.
    */
   bool IsBlackAndDoesNotNeedTracing(nsISupports* aThis);
 
   bool HasNothingToTrace(nsISupports* aThis);
@@ -267,16 +267,19 @@ protected:
     if (mWrapper) {
       // Set the pointer to a value that will cause a crash if it is
       // dereferenced.
       mWrapper = reinterpret_cast<JSObject*>(1);
     }
   }
 
 private:
+  // Friend declarations for things that need to be able to call
+  // SetIsNotDOMBinding().  The goal is to get rid of all of these, and
+  // SetIsNotDOMBinding() too.
   friend class mozilla::dom::TabChildGlobal;
   friend class mozilla::dom::ProcessGlobal;
   friend class SandboxPrivate;
   friend class nsInProcessTabChildGlobal;
   friend class nsWindowRoot;
   void SetIsNotDOMBinding()
   {
     MOZ_ASSERT(!mWrapper && !(GetWrapperFlags() & ~WRAPPER_IS_NOT_DOM_BINDING),
--- a/dom/base/nsWrapperCacheInlines.h
+++ b/dom/base/nsWrapperCacheInlines.h
@@ -17,17 +17,17 @@ nsWrapperCache::GetWrapper() const
     JSObject* obj = GetWrapperPreserveColor();
     if (obj) {
       JS::ExposeObjectToActiveJS(obj);
     }
     return obj;
 }
 
 inline bool
-nsWrapperCache::IsBlack()
+nsWrapperCache::IsBlack() const
 {
   JSObject* o = GetWrapperPreserveColor();
   return o && !JS::ObjectIsMarkedGray(o);
 }
 
 static void
 SearchGray(JS::GCCellPtr aGCThing, const char* aName, void* aClosure)
 {
--- a/dom/base/test/test_bug1198095.html
+++ b/dom/base/test/test_bug1198095.html
@@ -37,21 +37,16 @@ function onLoadEnd1(e) {
   is(view.length, fileData1.length, "File data length matches");
   for (var i = 0; i < fileData1.length; ++i) {
     is(String.fromCharCode(view[i]), fileData1[i], "Byte matches");
   }
 
   opener.sendAsyncMessage("file.modify", fileData2);
 }
 
-function onLoadEnd2(e) {
-  e.target.removeEventListener('loadend', onLoadEnd2);
-  ok(false, "This method should not be called - loadEnd2!");
-}
-
 function onError1(e) {
   ok(false, "This method should not be called - error1!");
 }
 
 function onError2(e) {
   e.target.removeEventListener('error', onError2);
   SimpleTest.finish();
 }
@@ -60,17 +55,16 @@ function onFileOpened(blob) {
   firstBlob = blob;
   r = new FileReader();
   r.addEventListener("loadend", onLoadEnd1);
   r.addEventListener("error", onError1);
   r.readAsArrayBuffer(firstBlob);
 }
 
 function onFileModified(blob) {
-  r.addEventListener("loadend", onLoadEnd2);
   r.removeEventListener('error', onError1);
   r.addEventListener("error", onError2);
   r.readAsArrayBuffer(firstBlob);
 }
 
 SimpleTest.waitForExplicitFinish();
 </script>
 </pre>
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -191,33 +191,106 @@ DOMInterfaces = {
 'CSS': {
     'concrete': False,
 },
 
 'CSS2Properties': {
     'nativeType': 'nsDOMCSSDeclaration'
 },
 
+'CSSConditionRule': {
+    'concrete': False,
+    'nativeType': 'mozilla::css::ConditionRule',
+    'headerFile': 'mozilla/css/GroupRule.h',
+},
+
+'CSSCounterStyleRule': {
+    'nativeType': 'nsCSSCounterStyleRule',
+    'headerFile': 'nsCSSRules.h',
+},
+
+'CSSFontFaceRule': {
+    'nativeType': 'nsCSSFontFaceRule',
+    'headerFile': 'nsCSSRules.h',
+},
+
+'CSSFontFeatureValuesRule': {
+    'nativeType': 'nsCSSFontFeatureValuesRule',
+    'headerFile': 'nsCSSRules.h',
+},
+
+'CSSGroupingRule': {
+    'concrete': False,
+    'nativeType': 'mozilla::css::GroupRule',
+},
+
+'CSSImportRule': {
+    'nativeType': 'mozilla::css::ImportRule',
+},
+
+'CSSKeyframeRule': {
+    'nativeType': 'nsCSSKeyframeRule',
+    'headerFile': 'nsCSSRules.h',
+},
+
+'CSSKeyframesRule': {
+    'nativeType': 'nsCSSKeyframesRule',
+    'headerFile': 'nsCSSRules.h',
+},
+
 'CSSLexer': {
     'wrapperCache': False
 },
 
+'CSSMediaRule': {
+    'nativeType': 'mozilla::css::MediaRule',
+    'headerFile': 'nsCSSRules.h',
+},
+
+'CSSMozDocumentRule': {
+    'nativeType': 'mozilla::css::DocumentRule',
+    'headerFile': 'nsCSSRules.h',
+},
+
+'CSSNamespaceRule': {
+    'nativeType': 'mozilla::css::NameSpaceRule',
+},
+
+'CSSPageRule': {
+    'nativeType': 'nsCSSPageRule',
+    'headerFile': 'nsCSSRules.h',
+},
+
 'CSSPrimitiveValue': {
     'nativeType': 'nsROCSSPrimitiveValue',
 },
 
+'CSSRule': {
+    'concrete': False,
+    'nativeType': 'mozilla::css::Rule'
+},
+
 'CSSStyleDeclaration': {
     'nativeType': 'nsICSSDeclaration'
 },
 
+'CSSStyleRule': {
+    'nativeType': 'mozilla::BindingStyleRule',
+},
+
 'CSSStyleSheet': {
     'nativeType': 'mozilla::StyleSheet',
     'binaryNames': { 'ownerRule': 'DOMOwnerRule' },
 },
 
+'CSSSupportsRule': {
+    'nativeType': 'mozilla::CSSSupportsRule',
+    'headerFile': 'nsCSSRules.h',
+},
+
 'CSSValue': {
     'concrete': False
 },
 
 'CSSValueList': {
     'nativeType': 'nsDOMCSSValueList'
 },
 
@@ -1655,17 +1728,16 @@ def addExternalIface(iface, nativeType=N
         domInterface['nativeType'] = nativeType
     if not headerFile is None:
         domInterface['headerFile'] = headerFile
     domInterface['notflattened'] = notflattened
     DOMInterfaces[iface] = domInterface
 
 addExternalIface('ApplicationCache', nativeType='nsIDOMOfflineResourceList')
 addExternalIface('Counter')
-addExternalIface('CSSRule')
 addExternalIface('RTCDataChannel', nativeType='nsIDOMDataChannel')
 addExternalIface('HitRegionOptions', nativeType='nsISupports')
 addExternalIface('imgINotificationObserver', nativeType='imgINotificationObserver')
 addExternalIface('imgIRequest', nativeType='imgIRequest', notflattened=True)
 addExternalIface('MenuBuilder', nativeType='nsIMenuBuilder', notflattened=True)
 addExternalIface('MozControllers', nativeType='nsIControllers')
 addExternalIface('MozFrameLoader', nativeType='nsIFrameLoader', notflattened=True)
 addExternalIface('MozObserver', nativeType='nsIObserver', notflattened=True)
--- a/dom/file/FileReader.cpp
+++ b/dom/file/FileReader.cpp
@@ -198,19 +198,17 @@ ReadFuncBinaryString(nsIInputStream* in,
     ++source;
   }
   *writeCount = count;
 
   return NS_OK;
 }
 
 nsresult
-FileReader::DoOnLoadEnd(nsresult aStatus,
-                        nsAString& aSuccessEvent,
-                        nsAString& aTerminationEvent)
+FileReader::DoOnLoadEnd(nsresult aStatus)
 {
   // Make sure we drop all the objects that could hold files open now.
   nsCOMPtr<nsIAsyncInputStream> stream;
   mAsyncStream.swap(stream);
 
   RefPtr<Blob> blob;
   mBlob.swap(blob);
 
@@ -218,35 +216,32 @@ FileReader::DoOnLoadEnd(nsresult aStatus
   if (NS_FAILED(aStatus)) {
     FreeFileData();
     return NS_OK;
   }
 
   // In case we read a different number of bytes, we can assume that the
   // underlying storage has changed. We should not continue.
   if (mDataLen != mTotal) {
-    DispatchError(NS_ERROR_FAILURE, aTerminationEvent);
     FreeFileData();
     return NS_ERROR_FAILURE;
   }
 
-  aSuccessEvent = NS_LITERAL_STRING(LOAD_STR);
-  aTerminationEvent = NS_LITERAL_STRING(LOADEND_STR);
-
   nsresult rv = NS_OK;
   switch (mDataFormat) {
     case FILE_AS_ARRAYBUFFER: {
       AutoJSAPI jsapi;
       if (!jsapi.Init(GetParentObject())) {
         FreeFileData();
         return NS_ERROR_FAILURE;
       }
 
       RootResultArrayBuffer();
-      mResultArrayBuffer = JS_NewArrayBufferWithContents(jsapi.cx(), mDataLen, mFileData);
+      mResultArrayBuffer =
+        JS_NewArrayBufferWithContents(jsapi.cx(), mDataLen, mFileData);
       if (!mResultArrayBuffer) {
         JS_ClearPendingException(jsapi.cx());
         rv = NS_ERROR_OUT_OF_MEMORY;
       } else {
         mFileData = nullptr; // Transfer ownership
       }
       break;
     }
@@ -530,34 +525,34 @@ FileReader::ClearProgressEventTimer()
   mProgressEventWasDelayed = false;
   mTimerIsActive = false;
   if (mProgressNotifier) {
     mProgressNotifier->Cancel();
   }
 }
 
 void
-FileReader::DispatchError(nsresult rv, nsAString& finalEvent)
+FileReader::DispatchError(nsresult aRv)
 {
   // Set the status attribute, and dispatch the error event
-  switch (rv) {
+  switch (aRv) {
   case NS_ERROR_FILE_NOT_FOUND:
     mError = new DOMError(GetOwner(), NS_LITERAL_STRING("NotFoundError"));
     break;
   case NS_ERROR_FILE_ACCESS_DENIED:
     mError = new DOMError(GetOwner(), NS_LITERAL_STRING("SecurityError"));
     break;
   default:
     mError = new DOMError(GetOwner(), NS_LITERAL_STRING("NotReadableError"));
     break;
   }
 
   // Dispatch error event to signify load failure
   DispatchProgressEvent(NS_LITERAL_STRING(ERROR_STR));
-  DispatchProgressEvent(finalEvent);
+  DispatchProgressEvent(NS_LITERAL_STRING(LOADEND_STR));
 }
 
 nsresult
 FileReader::DispatchProgressEvent(const nsAString& aType)
 {
   ProgressEventInit init;
   init.mBubbles = false;
   init.mCancelable = false;
@@ -644,29 +639,31 @@ nsresult
 FileReader::OnLoadEnd(nsresult aStatus)
 {
   // Cancel the progress event timer
   ClearProgressEventTimer();
 
   // FileReader must be in DONE stage after an operation
   mReadyState = DONE;
 
-  nsAutoString successEvent, termEvent;
-  nsresult rv = DoOnLoadEnd(aStatus, successEvent, termEvent);
-  NS_ENSURE_SUCCESS(rv, rv);
+  nsresult rv = DoOnLoadEnd(aStatus);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    DispatchError(rv);
+    return NS_OK;
+  }
 
   // Set the status field as appropriate
   if (NS_FAILED(aStatus)) {
-    DispatchError(aStatus, termEvent);
+    DispatchError(aStatus);
     return NS_OK;
   }
 
   // Dispatch event to signify end of a successful operation
-  DispatchProgressEvent(successEvent);
-  DispatchProgressEvent(termEvent);
+  DispatchProgressEvent(NS_LITERAL_STRING(LOAD_STR));
+  DispatchProgressEvent(NS_LITERAL_STRING(LOADEND_STR));
 
   return NS_OK;
 }
 
 void
 FileReader::Abort()
 {
   if (mReadyState == EMPTY || mReadyState == DONE) {
--- a/dom/file/FileReader.h
+++ b/dom/file/FileReader.h
@@ -136,24 +136,23 @@ private:
                      nsAString &aResult);
   nsresult GetAsDataURL(Blob *aBlob, const char *aFileData,
                         uint32_t aDataLen, nsAString &aResult);
 
   nsresult OnLoadEnd(nsresult aStatus);
 
   void StartProgressEventTimer();
   void ClearProgressEventTimer();
-  void DispatchError(nsresult rv, nsAString& finalEvent);
+  void DispatchError(nsresult aRv);
   nsresult DispatchProgressEvent(const nsAString& aType);
 
   nsresult DoAsyncWait();
   nsresult DoReadData(uint64_t aCount);
 
-  nsresult DoOnLoadEnd(nsresult aStatus, nsAString& aSuccessEvent,
-                       nsAString& aTerminationEvent);
+  nsresult DoOnLoadEnd(nsresult aStatus);
 
   void FreeFileData()
   {
     free(mFileData);
     mFileData = nullptr;
     mDataLen = 0;
   }
 
--- a/dom/interfaces/css/nsIDOMCSSCounterStyleRule.idl
+++ b/dom/interfaces/css/nsIDOMCSSCounterStyleRule.idl
@@ -1,18 +1,18 @@
 /* -*- Mode: IDL; tab-width: 2; 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 "nsIDOMCSSRule.idl"
+#include "nsISupports.idl"
 
 [scriptable, uuid(9b5e48ce-d84c-4e31-aff5-34e9f4141313)]
-interface nsIDOMCSSCounterStyleRule : nsIDOMCSSRule
+interface nsIDOMCSSCounterStyleRule : nsISupports
 {
   attribute DOMString name;
   attribute DOMString system;
   attribute DOMString symbols;
   attribute DOMString additiveSymbols;
   attribute DOMString negative;
   attribute DOMString prefix;
   attribute DOMString suffix;
--- a/dom/interfaces/css/nsIDOMCSSFontFaceRule.idl
+++ b/dom/interfaces/css/nsIDOMCSSFontFaceRule.idl
@@ -1,12 +1,14 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "nsIDOMCSSRule.idl"
+#include "nsISupports.idl"
+
+interface nsIDOMCSSStyleDeclaration;
 
 [scriptable, uuid(db971017-fe0c-4529-972c-8217f2fee217)]
-interface nsIDOMCSSFontFaceRule : nsIDOMCSSRule
+interface nsIDOMCSSFontFaceRule : nsISupports
 {
   readonly attribute nsIDOMCSSStyleDeclaration  style;
 };
--- a/dom/interfaces/css/nsIDOMCSSFontFeatureValuesRule.idl
+++ b/dom/interfaces/css/nsIDOMCSSFontFeatureValuesRule.idl
@@ -1,16 +1,16 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "nsIDOMCSSRule.idl"
+#include "nsISupports.idl"
 
 [scriptable, uuid(a343d27f-1da6-4fc3-9355-d4ca434f958e)]
-interface nsIDOMCSSFontFeatureValuesRule : nsIDOMCSSRule
+interface nsIDOMCSSFontFeatureValuesRule : nsISupports
 {
   attribute DOMString fontFamily;
                       // raises(DOMException) on setting
 
   attribute DOMString valueText;
                       // raises(DOMException) on setting
 };
--- a/dom/interfaces/css/nsIDOMCSSGroupingRule.idl
+++ b/dom/interfaces/css/nsIDOMCSSGroupingRule.idl
@@ -1,20 +1,22 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "nsIDOMCSSRule.idl"
+#include "nsISupports.idl"
+
+interface nsIDOMCSSRuleList;
 
 /**
  * Interface for at-rules that have child rules in the CSS OM.
  */
 [scriptable, uuid(a0e3324a-f911-4baf-9591-5322c76cbb0d)]
-interface nsIDOMCSSGroupingRule : nsIDOMCSSRule
+interface nsIDOMCSSGroupingRule : nsISupports
 {
   readonly attribute nsIDOMCSSRuleList cssRules;
 
   unsigned long      insertRule(in DOMString rule,
                                 in unsigned long index)
                                         raises(DOMException);
   void               deleteRule(in unsigned long index)
                                         raises(DOMException);
--- a/dom/interfaces/css/nsIDOMCSSImportRule.idl
+++ b/dom/interfaces/css/nsIDOMCSSImportRule.idl
@@ -1,14 +1,17 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "nsIDOMCSSRule.idl"
+#include "nsISupports.idl"
+
+interface nsIDOMMediaList;
+interface nsIDOMCSSStyleSheet;
 
 [scriptable, uuid(d3b2b914-01ef-4663-beda-a6475a26f491)]
-interface nsIDOMCSSImportRule : nsIDOMCSSRule
+interface nsIDOMCSSImportRule : nsISupports
 {
   readonly attribute DOMString           href;
   readonly attribute nsIDOMMediaList     media;
   readonly attribute nsIDOMCSSStyleSheet styleSheet;
 };
--- a/dom/interfaces/css/nsIDOMCSSKeyframeRule.idl
+++ b/dom/interfaces/css/nsIDOMCSSKeyframeRule.idl
@@ -1,13 +1,15 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "nsIDOMCSSRule.idl"
+#include "nsISupports.idl"
+
+interface nsIDOMCSSStyleDeclaration;
 
 [scriptable, uuid(a281a8b4-eaa2-49a8-8b97-acc2814a57c9)]
-interface nsIDOMCSSKeyframeRule : nsIDOMCSSRule
+interface nsIDOMCSSKeyframeRule : nsISupports
 {
            attribute DOMString                 keyText;
   readonly attribute nsIDOMCSSStyleDeclaration style;
 };
--- a/dom/interfaces/css/nsIDOMCSSKeyframesRule.idl
+++ b/dom/interfaces/css/nsIDOMCSSKeyframesRule.idl
@@ -1,17 +1,20 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "nsIDOMCSSRule.idl"
+#include "nsISupports.idl"
+
+interface nsIDOMCSSRuleList;
+interface nsIDOMCSSKeyframeRule;
 
 [scriptable, uuid(400f4b70-ad0a-4047-aba4-ee8019f6b907)]
-interface nsIDOMCSSKeyframesRule : nsIDOMCSSRule
+interface nsIDOMCSSKeyframesRule : nsISupports
 {
            attribute DOMString         name;
   readonly attribute nsIDOMCSSRuleList cssRules;
 
   void                     appendRule(in DOMString rule);
   void                     deleteRule(in DOMString key);
   nsIDOMCSSKeyframeRule    findRule(in DOMString key);
 };
--- a/dom/interfaces/css/nsIDOMCSSMediaRule.idl
+++ b/dom/interfaces/css/nsIDOMCSSMediaRule.idl
@@ -1,15 +1,17 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "nsIDOMCSSConditionRule.idl"
 
+interface nsIDOMMediaList;
+
 /**
  * Interface for @media rules in the CSS OM.
  */
 [scriptable, uuid(6cf9c5b2-fa0f-43c0-aa50-ef85b4756e3a)]
 interface nsIDOMCSSMediaRule : nsIDOMCSSConditionRule
 {
   readonly attribute nsIDOMMediaList   media;
 };
--- a/dom/interfaces/css/nsIDOMCSSPageRule.idl
+++ b/dom/interfaces/css/nsIDOMCSSPageRule.idl
@@ -1,15 +1,17 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "nsIDOMCSSRule.idl"
+#include "nsISupports.idl"
+
+interface nsIDOMCSSStyleDeclaration;
 
 [scriptable, uuid(c119072b-7d2f-4aeb-a90d-e2d6b606c32a)]
-interface nsIDOMCSSPageRule : nsIDOMCSSRule
+interface nsIDOMCSSPageRule : nsISupports
 {
            //attribute DOMString        selectorText;
                                         // raises(DOMException) on setting
 
   readonly attribute nsIDOMCSSStyleDeclaration  style;
 };
--- a/dom/interfaces/css/nsIDOMCSSStyleRule.idl
+++ b/dom/interfaces/css/nsIDOMCSSStyleRule.idl
@@ -1,15 +1,17 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "nsIDOMCSSRule.idl"
+#include "nsISupports.idl"
+
+interface nsIDOMCSSStyleDeclaration;
 
 [scriptable, uuid(b5e9af48-a7c2-4f88-aae3-58307af4b5a5)]
-interface nsIDOMCSSStyleRule : nsIDOMCSSRule
+interface nsIDOMCSSStyleRule : nsISupports
 {
            attribute DOMString        selectorText;
                                         // raises(DOMException) on setting
 
   readonly attribute nsIDOMCSSStyleDeclaration  style;
 };
--- a/dom/interfaces/css/nsIDOMCSSUnknownRule.idl
+++ b/dom/interfaces/css/nsIDOMCSSUnknownRule.idl
@@ -1,11 +1,11 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "nsIDOMCSSRule.idl"
+#include "nsISupports.idl"
 
 [scriptable, uuid(98f4c27b-fb35-4355-8fd9-546c4697d71a)]
-interface nsIDOMCSSUnknownRule : nsIDOMCSSRule
+interface nsIDOMCSSUnknownRule : nsISupports
 {
 };
--- a/dom/interfaces/storage/nsIDOMStorageManager.idl
+++ b/dom/interfaces/storage/nsIDOMStorageManager.idl
@@ -34,32 +34,34 @@ interface nsIDOMStorageManager : nsISupp
    *    Principal to bound storage to.
    * @param aDocumentURI
    *    URL of the demanding document, used for DOM storage event only.
    * @param aPrivate
    *    Whether the demanding document is running in Private Browsing mode or not.
    */
   nsIDOMStorage createStorage(in mozIDOMWindow aWindow,
                               in nsIPrincipal aPrincipal,
-                              in DOMString aDocumentURI);
+                              in DOMString aDocumentURI,
+                              [optional] in bool aPrivate);
   /**
    * Returns instance of DOM storage object for given principal.
    * If there is no storage managed for the scope, then null is returned and
    * no object is created.  Otherwise, an object (new) for the existing storage
    * scope is returned.
    *
    * @param aWindow
    *    The parent window.
    * @param aPrincipal
    *    Principal to bound storage to.
    * @param aPrivate
    *    Whether the demanding document is running in Private Browsing mode or not.
    */
   nsIDOMStorage getStorage(in mozIDOMWindow aWindow,
-                           in nsIPrincipal aPrincipal);
+                           in nsIPrincipal aPrincipal,
+                           [optional] in bool aPrivate);
 
   /**
    * Clones given storage into this storage manager.
    *
    * @param aStorageToCloneFrom
    *    The storage to copy all items from into this manager.  Manager will then
    *    return a new and independent object that contains snapshot of data from
    *    the moment this method was called.  Modification to this new object will
@@ -93,10 +95,11 @@ interface nsIDOMStorageManager : nsISupp
    *
    * Currently just forwards to the createStorage method of this
    * interface.
    *
    * Extension developers are strongly encouraged to use getStorage
    * or createStorage method instead.
    */
   nsIDOMStorage getLocalStorageForPrincipal(in nsIPrincipal aPrincipal,
-                                            in DOMString aDocumentURI);
+                                            in DOMString aDocumentURI,
+                                            [optional] in bool aPrivate);
 };
--- a/dom/media/CubebUtils.cpp
+++ b/dom/media/CubebUtils.cpp
@@ -17,16 +17,17 @@
 #include "nsThreadUtils.h"
 #include "CubebUtils.h"
 #include "nsAutoRef.h"
 #include "prdtoa.h"
 
 #define PREF_VOLUME_SCALE "media.volume_scale"
 #define PREF_CUBEB_LATENCY_PLAYBACK "media.cubeb_latency_playback_ms"
 #define PREF_CUBEB_LATENCY_MSG "media.cubeb_latency_msg_frames"
+#define PREF_CUBEB_LOG_LEVEL "media.cubeb.log_level"
 
 namespace mozilla {
 
 namespace {
 
 LazyLogModule gCubebLog("cubeb");
 
 void CubebLogCallback(const char* aFmt, ...)
@@ -126,16 +127,30 @@ void PrefChanged(const char* aPref, void
     sCubebMSGLatencyPrefSet = Preferences::HasUserValue(aPref);
     uint32_t value = Preferences::GetUint(aPref, CUBEB_NORMAL_LATENCY_FRAMES);
     StaticMutexAutoLock lock(sMutex);
     // 128 is the block size for the Web Audio API, which limits how low the
     // latency can be here.
     // We don't want to limit the upper limit too much, so that people can
     // experiment.
     sCubebMSGLatencyInFrames = std::min<uint32_t>(std::max<uint32_t>(value, 128), 1e6);
+  } else if (strcmp(aPref, PREF_CUBEB_LOG_LEVEL) == 0) {
+    nsAdoptingString value = Preferences::GetString(aPref);
+    NS_ConvertUTF16toUTF8 utf8(value);
+    LogModule* cubebLog = LogModule::Get("cubeb");
+    if (strcmp(utf8.get(), "verbose") == 0) {
+      cubeb_set_log_callback(CUBEB_LOG_VERBOSE, CubebLogCallback);
+      cubebLog->SetLevel(LogLevel::Verbose);
+    } else if (strcmp(utf8.get(), "normal") == 0) {
+      cubeb_set_log_callback(CUBEB_LOG_NORMAL, CubebLogCallback);
+      cubebLog->SetLevel(LogLevel::Error);
+    } else if (utf8.IsEmpty()) {
+      cubeb_set_log_callback(CUBEB_LOG_DISABLED, nullptr);
+      cubebLog->SetLevel(LogLevel::Disabled);
+    }
   }
 }
 
 bool GetFirstStream()
 {
   static bool sFirstStream = true;
 
   StaticMutexAutoLock lock(sMutex);
@@ -303,26 +318,28 @@ Maybe<uint32_t> GetCubebMSGLatencyInFram
 void InitLibrary()
 {
   PrefChanged(PREF_VOLUME_SCALE, nullptr);
   Preferences::RegisterCallback(PrefChanged, PREF_VOLUME_SCALE);
   PrefChanged(PREF_CUBEB_LATENCY_PLAYBACK, nullptr);
   PrefChanged(PREF_CUBEB_LATENCY_MSG, nullptr);
   Preferences::RegisterCallback(PrefChanged, PREF_CUBEB_LATENCY_PLAYBACK);
   Preferences::RegisterCallback(PrefChanged, PREF_CUBEB_LATENCY_MSG);
+  Preferences::RegisterCallback(PrefChanged, PREF_CUBEB_LOG_LEVEL);
 #ifndef MOZ_WIDGET_ANDROID
   NS_DispatchToMainThread(NS_NewRunnableFunction(&InitBrandName));
 #endif
 }
 
 void ShutdownLibrary()
 {
   Preferences::UnregisterCallback(PrefChanged, PREF_VOLUME_SCALE);
   Preferences::UnregisterCallback(PrefChanged, PREF_CUBEB_LATENCY_PLAYBACK);
   Preferences::UnregisterCallback(PrefChanged, PREF_CUBEB_LATENCY_MSG);
+  Preferences::UnregisterCallback(PrefChanged, PREF_CUBEB_LOG_LEVEL);
 
   StaticMutexAutoLock lock(sMutex);
   if (sCubebContext) {
     cubeb_destroy(sCubebContext);
     sCubebContext = nullptr;
   }
   sBrandName = nullptr;
   // This will ensure we don't try to re-create a context.
--- a/dom/media/webaudio/AudioBuffer.h
+++ b/dom/media/webaudio/AudioBuffer.h
@@ -10,16 +10,17 @@
 #include "nsWrapperCache.h"
 #include "nsCycleCollectionParticipant.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/StaticMutex.h"
 #include "nsTArray.h"
 #include "js/TypeDecls.h"
 #include "mozilla/MemoryReporting.h"
+#include "mozilla/dom/TypedArray.h"
 
 namespace mozilla {
 
 class ErrorResult;
 class ThreadSharedFloatArrayBufferList;
 
 namespace dom {
 
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -2731,27 +2731,16 @@ PluginModuleParent::NPP_NewInternal(NPMI
                if (!values[wmodeAttributeIndex].EqualsLiteral("transparent")) {
                    values[wmodeAttributeIndex].Assign(opaqueAttributeValue);
                }
            } else {
                names.AppendElement(wmodeAttributeName);
                values.AppendElement(opaqueAttributeValue);
            }
         }
-
-      // Update the flashvar bgcolor if it's not set, fixes a rendering problem with
-      // async plugin painting and transparent flash.
-      if (supportsAsyncRender) {
-        NS_NAMED_LITERAL_CSTRING(bgcolorAttributeName, "bgcolor");
-        NS_NAMED_LITERAL_CSTRING(bgcolorAttributeDefault, "#FFFFFF");
-        if (!names.Contains(bgcolorAttributeName)) {
-          names.AppendElement(bgcolorAttributeName);
-          values.AppendElement(bgcolorAttributeDefault);
-        }
-      }
 #endif
     }
 
     // Release the surrogate reference that was in pdata
     RefPtr<PluginAsyncSurrogate> surrogate(
         dont_AddRef(PluginAsyncSurrogate::Cast(instance)));
     // Now replace it with the instance
     instance->pdata = static_cast<PluginDataResolver*>(parentInstance);
--- a/dom/storage/Storage.cpp
+++ b/dom/storage/Storage.cpp
@@ -38,22 +38,24 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(nsIDOMStorage)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
 NS_INTERFACE_MAP_END
 
 Storage::Storage(nsPIDOMWindowInner* aWindow,
                  StorageManagerBase* aManager,
                  StorageCache* aCache,
                  const nsAString& aDocumentURI,
-                 nsIPrincipal* aPrincipal)
+                 nsIPrincipal* aPrincipal,
+                 bool aIsPrivate)
   : mWindow(aWindow)
   , mManager(aManager)
   , mCache(aCache)
   , mDocumentURI(aDocumentURI)
   , mPrincipal(aPrincipal)
+  , mIsPrivate(aIsPrivate)
   , mIsSessionOnly(false)
 {
   mCache->Preload();
 }
 
 Storage::~Storage()
 {
   mCache->KeepAlive();
@@ -236,16 +238,18 @@ Storage::BroadcastChangeNotification(con
 
 static const char kPermissionType[] = "cookie";
 static const char kStorageEnabled[] = "dom.storage.enabled";
 
 bool
 Storage::CanUseStorage(nsIPrincipal& aSubjectPrincipal)
 {
   // This method is responsible for correct setting of mIsSessionOnly.
+  // It doesn't work with mIsPrivate flag at all, since it is checked
+  // regardless mIsSessionOnly flag in DOMStorageCache code.
 
   if (!mozilla::Preferences::GetBool(kStorageEnabled)) {
     return false;
   }
 
   nsContentUtils::StorageAccess access =
     nsContentUtils::StorageAllowedForPrincipal(mPrincipal);
 
@@ -276,27 +280,16 @@ PrincipalsEqual(nsIPrincipal* aObjectPri
 
 bool
 Storage::PrincipalEquals(nsIPrincipal* aPrincipal)
 {
   return PrincipalsEqual(mPrincipal, aPrincipal);
 }
 
 bool
-Storage::IsPrivate() const
-{
-  uint32_t privateBrowsingId = 0;
-  nsresult rv = mPrincipal->GetPrivateBrowsingId(&privateBrowsingId);
-  if (NS_FAILED(rv)) {
-    return false;
-  }
-  return privateBrowsingId > 0;
-}
-
-bool
 Storage::CanAccess(nsIPrincipal* aPrincipal)
 {
   return !aPrincipal || aPrincipal->Subsumes(mPrincipal);
 }
 
 void
 Storage::GetSupportedNames(nsTArray<nsString>& aKeys)
 {
--- a/dom/storage/Storage.h
+++ b/dom/storage/Storage.h
@@ -55,17 +55,18 @@ public:
   nsIPrincipal* GetPrincipal();
   bool PrincipalEquals(nsIPrincipal* aPrincipal);
   bool CanAccess(nsIPrincipal* aPrincipal);
 
   Storage(nsPIDOMWindowInner* aWindow,
           StorageManagerBase* aManager,
           StorageCache* aCache,
           const nsAString& aDocumentURI,
-          nsIPrincipal* aPrincipal);
+          nsIPrincipal* aPrincipal,
+          bool aIsPrivate);
 
   // WebIDL
   JSObject* WrapObject(JSContext* aCx,
                        JS::Handle<JSObject*> aGivenProto) override;
 
   nsPIDOMWindowInner* GetParentObject() const
   {
     return mWindow;
@@ -114,17 +115,17 @@ public:
     RemoveItem(aKey, aSubjectPrincipal, aRv);
 
     aFound = !aRv.ErrorCodeIs(NS_SUCCESS_DOM_NO_OPERATION);
   }
 
   void Clear(nsIPrincipal& aSubjectPrincipal,
              ErrorResult& aRv);
 
-  bool IsPrivate() const;
+  bool IsPrivate() const { return mIsPrivate; }
   bool IsSessionOnly() const { return mIsSessionOnly; }
 
   bool IsForkOf(const Storage* aOther) const
   {
     MOZ_ASSERT(aOther);
     return mCache == aOther->mCache;
   }
 
@@ -148,16 +149,19 @@ private:
   RefPtr<StorageManagerBase> mManager;
   RefPtr<StorageCache> mCache;
   nsString mDocumentURI;
 
   // Principal this Storage (i.e. localStorage or sessionStorage) has
   // been created for
   nsCOMPtr<nsIPrincipal> mPrincipal;
 
+  // Whether this storage is running in private-browsing window.
+  bool mIsPrivate : 1;
+
   // Whether storage is set to persist data only per session, may change
   // dynamically and is set by CanUseStorage function that is called
   // before any operation on the storage.
   bool mIsSessionOnly : 1;
 
   void BroadcastChangeNotification(const nsSubstring& aKey,
                                    const nsSubstring& aOldValue,
                                    const nsSubstring& aNewValue);
--- a/dom/storage/StorageCache.h
+++ b/dom/storage/StorageCache.h
@@ -97,17 +97,17 @@ public:
   void Preload();
 
   // Keeps the cache alive (i.e. present in the manager's hash table) for a
   // time.
   void KeepAlive();
 
   // The set of methods that are invoked by DOM storage web API.
   // We are passing the Storage object just to let the cache
-  // read properties like mPrincipal and mSessionOnly.
+  // read properties like mPrivate, mPrincipal and mSessionOnly.
   // Get* methods return error when load from the database has failed.
   nsresult GetLength(const Storage* aStorage, uint32_t* aRetval);
   nsresult GetKey(const Storage* aStorage, uint32_t index, nsAString& aRetval);
   nsresult GetItem(const Storage* aStorage, const nsAString& aKey,
                    nsAString& aRetval);
   nsresult SetItem(const Storage* aStorage, const nsAString& aKey,
                    const nsString& aValue, nsString& aOld);
   nsresult RemoveItem(const Storage* aStorage, const nsAString& aKey,
--- a/dom/storage/StorageManager.cpp
+++ b/dom/storage/StorageManager.cpp
@@ -308,16 +308,17 @@ StorageManagerBase::DropCache(StorageCac
   table->RemoveEntry(aCache->OriginNoSuffix());
 }
 
 nsresult
 StorageManagerBase::GetStorageInternal(bool aCreate,
                                        mozIDOMWindow* aWindow,
                                        nsIPrincipal* aPrincipal,
                                        const nsAString& aDocumentURI,
+                                       bool aPrivate,
                                        nsIDOMStorage** aRetval)
 {
   nsresult rv;
 
   nsAutoCString originAttrSuffix;
   aPrincipal->OriginAttributesRef().CreateSuffix(originAttrSuffix);
 
   nsAutoCString originKey;
@@ -358,44 +359,49 @@ StorageManagerBase::GetStorageInternal(b
       return NS_ERROR_DOM_SECURITY_ERR;
     }
   }
 
   if (aRetval) {
     nsCOMPtr<nsPIDOMWindowInner> inner = nsPIDOMWindowInner::From(aWindow);
 
     nsCOMPtr<nsIDOMStorage> storage = new Storage(
-      inner, this, cache, aDocumentURI, aPrincipal);
+      inner, this, cache, aDocumentURI, aPrincipal, aPrivate);
     storage.forget(aRetval);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 StorageManagerBase::PrecacheStorage(nsIPrincipal* aPrincipal)
 {
-  return GetStorageInternal(true, nullptr, aPrincipal, EmptyString(), nullptr);
+  return GetStorageInternal(true, nullptr, aPrincipal, EmptyString(), false,
+                            nullptr);
 }
 
 NS_IMETHODIMP
 StorageManagerBase::CreateStorage(mozIDOMWindow* aWindow,
                                   nsIPrincipal* aPrincipal,
                                   const nsAString& aDocumentURI,
+                                  bool aPrivate,
                                   nsIDOMStorage** aRetval)
 {
-  return GetStorageInternal(true, aWindow, aPrincipal, aDocumentURI, aRetval);
+  return GetStorageInternal(true, aWindow, aPrincipal, aDocumentURI, aPrivate,
+                            aRetval);
 }
 
 NS_IMETHODIMP
 StorageManagerBase::GetStorage(mozIDOMWindow* aWindow,
                                nsIPrincipal* aPrincipal,
+                               bool aPrivate,
                                nsIDOMStorage** aRetval)
 {
-  return GetStorageInternal(false, aWindow, aPrincipal, EmptyString(), aRetval);
+  return GetStorageInternal(false, aWindow, aPrincipal, EmptyString(), aPrivate,
+                            aRetval);
 }
 
 NS_IMETHODIMP
 StorageManagerBase::CloneStorage(nsIDOMStorage* aStorage)
 {
   if (mType != SessionStorage) {
     // Cloning is supported only for sessionStorage
     return NS_ERROR_NOT_IMPLEMENTED;
@@ -465,23 +471,24 @@ StorageManagerBase::CheckStorage(nsIPrin
   return NS_OK;
 }
 
 // Obsolete nsIDOMStorageManager methods
 
 NS_IMETHODIMP
 StorageManagerBase::GetLocalStorageForPrincipal(nsIPrincipal* aPrincipal,
                                                 const nsAString& aDocumentURI,
+                                                bool aPrivate,
                                                 nsIDOMStorage** aRetval)
 {
   if (mType != LocalStorage) {
     return NS_ERROR_UNEXPECTED;
   }
 
-  return CreateStorage(nullptr, aPrincipal, aDocumentURI, aRetval);
+  return CreateStorage(nullptr, aPrincipal, aDocumentURI, aPrivate, aRetval);
 }
 
 void
 StorageManagerBase::ClearCaches(uint32_t aUnloadFlags,
                                 const OriginAttributesPattern& aPattern,
                                 const nsACString& aOriginScope)
 {
   for (auto iter1 = mCaches.Iter(); !iter1.Done(); iter1.Next()) {
--- a/dom/storage/StorageManager.h
+++ b/dom/storage/StorageManager.h
@@ -90,16 +90,17 @@ private:
                                           const nsACString& aOriginNoSuffix,
                                           nsIPrincipal* aPrincipal);
 
   // Helper for creation of DOM storage objects
   nsresult GetStorageInternal(bool aCreate,
                               mozIDOMWindow* aWindow,
                               nsIPrincipal* aPrincipal,
                               const nsAString& aDocumentURI,
+                              bool aPrivate,
                               nsIDOMStorage** aRetval);
 
   // Suffix->origin->cache map
   typedef nsTHashtable<StorageCacheHashKey> CacheOriginHashtable;
   nsClassHashtable<nsCStringHashKey, CacheOriginHashtable> mCaches;
 
   const Storage::StorageType mType;
 
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -244,17 +244,17 @@ var interfaceNamesInGlobalScope =
     "CSSKeyframeRule",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "CSSKeyframesRule",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "CSSMediaRule",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "CSSMozDocumentRule",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    "CSSNameSpaceRule",
+    "CSSNamespaceRule",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "CSSPageRule",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "CSSPrimitiveValue",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "CSSPseudoElement", release: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "CSSRule",
new file mode 100644
--- /dev/null
+++ b/dom/webidl/CSSConditionRule.webidl
@@ -0,0 +1,14 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ *
+ * The origin of this IDL file is
+ * https://drafts.csswg.org/css-conditional/#the-cssconditionrule-interface
+ */
+
+// https://drafts.csswg.org/css-conditional/#the-cssconditionrule-interface
+interface CSSConditionRule : CSSGroupingRule {
+  [SetterThrows]
+  attribute DOMString conditionText;
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/CSSCounterStyleRule.webidl
@@ -0,0 +1,23 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ *
+ * The origin of this IDL file is
+ * https://drafts.csswg.org/css-counter-styles-3/#the-csscounterstylerule-interface
+ */
+
+// https://drafts.csswg.org/css-counter-styles-3/#the-csscounterstylerule-interface
+interface CSSCounterStyleRule : CSSRule {
+  attribute DOMString name;
+  attribute DOMString system;
+  attribute DOMString symbols;
+  attribute DOMString additiveSymbols;
+  attribute DOMString negative;
+  attribute DOMString prefix;
+  attribute DOMString suffix;
+  attribute DOMString range;
+  attribute DOMString pad;
+  attribute DOMString speakAs;
+  attribute DOMString fallback;
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/CSSFontFaceRule.webidl
@@ -0,0 +1,15 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ *
+ * The origin of this IDL file is
+ * https://drafts.csswg.org/css-fonts/#om-fontface
+ */
+
+// https://drafts.csswg.org/css-fonts/#om-fontface
+// But we implement a very old draft, apparently....
+// See bug 1058408 for implementing the current spec.
+interface CSSFontFaceRule : CSSRule {
+  [SameObject] readonly attribute CSSStyleDeclaration style;
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/CSSFontFeatureValuesRule.webidl
@@ -0,0 +1,29 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ *
+ * The origin of this IDL file is
+ * https://drafts.csswg.org/css-fonts/#om-fontfeaturevalues
+ */
+
+// https://drafts.csswg.org/css-fonts/#om-fontfeaturevalues
+// but we don't implement anything remotely resembling the spec.
+interface CSSFontFeatureValuesRule : CSSRule {
+  [SetterThrows]
+  attribute DOMString fontFamily;
+
+  // Not yet implemented
+  //  readonly attribute CSSFontFeatureValuesMap annotation;
+  //  readonly attribute CSSFontFeatureValuesMap ornaments;
+  //  readonly attribute CSSFontFeatureValuesMap stylistic;
+  //  readonly attribute CSSFontFeatureValuesMap swash;
+  //  readonly attribute CSSFontFeatureValuesMap characterVariant;
+  //  readonly attribute CSSFontFeatureValuesMap styleset;
+};
+
+partial interface CSSFontFeatureValuesRule {
+  // Gecko addition?
+  [SetterThrows]
+  attribute DOMString valueText;
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/CSSGroupingRule.webidl
@@ -0,0 +1,17 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ *
+ * The origin of this IDL file is
+ * https://drafts.csswg.org/cssom/#cssgroupingrule
+ */
+
+// https://drafts.csswg.org/cssom/#cssgroupingrule
+interface CSSGroupingRule : CSSRule {
+  [SameObject] readonly attribute CSSRuleList cssRules;
+  [Throws]
+  unsigned long insertRule(DOMString rule, unsigned long index);
+  [Throws]
+  void deleteRule(unsigned long index);
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/CSSImportRule.webidl
@@ -0,0 +1,17 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ *
+ * The origin of this IDL file is
+ * https://drafts.csswg.org/cssom/#cssimportrule
+ */
+
+// https://drafts.csswg.org/cssom/#cssimportrule
+interface CSSImportRule : CSSRule {
+  readonly attribute DOMString href;
+  [SameObject, PutForwards=mediaText] readonly attribute MediaList media;
+  // Per spec, the .styleSheet is never null, but in our implementation it can
+  // be.  See <https://bugzilla.mozilla.org/show_bug.cgi?id=1326509>.
+  [SameObject] readonly attribute CSSStyleSheet? styleSheet;
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/CSSKeyframeRule.webidl
@@ -0,0 +1,14 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ *
+ * The origin of this IDL file is
+ * https://drafts.csswg.org/css-animations/#interface-csskeyframerule
+ */
+
+// https://drafts.csswg.org/css-animations/#interface-csskeyframerule
+interface CSSKeyframeRule : CSSRule {
+           attribute DOMString           keyText;
+  readonly attribute CSSStyleDeclaration style;
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/CSSKeyframesRule.webidl
@@ -0,0 +1,18 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ *
+ * The origin of this IDL file is
+ * https://drafts.csswg.org/css-animations/#interface-csskeyframesrule
+ */
+
+// https://drafts.csswg.org/css-animations/#interface-csskeyframesrule
+interface CSSKeyframesRule : CSSRule {
+           attribute DOMString   name;
+  readonly attribute CSSRuleList cssRules;
+
+  void            appendRule(DOMString rule);
+  void            deleteRule(DOMString select);
+  CSSKeyframeRule? findRule(DOMString select);
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/CSSMediaRule.webidl
@@ -0,0 +1,17 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ *
+ * The origin of this IDL file is
+ * https://drafts.csswg.org/cssom/#the-cssmediarule-interface
+ * https://drafts.csswg.org/css-conditional/#the-cssmediarule-interface
+ */
+
+// https://drafts.csswg.org/cssom/#the-cssmediarule-interface and
+// https://drafts.csswg.org/css-conditional/#the-cssmediarule-interface
+// except they disagree with each other.  We're taking the inheritance from
+// css-conditional and the PutForwards behavior from cssom.
+interface CSSMediaRule : CSSConditionRule {
+  [SameObject, PutForwards=mediaText] readonly attribute MediaList media;
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/CSSMozDocumentRule.webidl
@@ -0,0 +1,10 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ */
+
+// This is a non-standard interface for @-moz-document rules
+interface CSSMozDocumentRule : CSSConditionRule {
+  // XXX Add access to the URL list.
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/CSSNamespaceRule.webidl
@@ -0,0 +1,16 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ *
+ * The origin of this IDL file is
+ * https://drafts.csswg.org/cssom/#cssnamespacerule
+ */
+
+// https://drafts.csswg.org/cssom/#cssnamespacerule
+interface CSSNamespaceRule : CSSRule {
+  // Not implemented yet.  <See
+  // https://bugzilla.mozilla.org/show_bug.cgi?id=1326514>.
+  //  readonly attribute DOMString namespaceURI;
+  //  readonly attribute DOMString prefix;
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/CSSPageRule.webidl
@@ -0,0 +1,17 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ *
+ * The origin of this IDL file is
+ * https://drafts.csswg.org/cssom/#the-csspagerule-interface
+ */
+
+// https://drafts.csswg.org/cssom/#the-csspagerule-interface
+// Per spec, this should inherit from CSSGroupingRule, but we don't
+// implement this yet.
+interface CSSPageRule : CSSRule {
+  // selectorText not implemented yet
+  //         attribute DOMString selectorText;
+  [SameObject, PutForwards=cssText] readonly attribute CSSStyleDeclaration style;
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/CSSRule.webidl
@@ -0,0 +1,52 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ *
+ * The origin of this IDL file is
+ * https://drafts.csswg.org/cssom/#the-cssrule-interface
+ * https://drafts.csswg.org/css-animations/#interface-cssrule
+ * https://drafts.csswg.org/css-counter-styles-3/#extentions-to-cssrule-interface
+ * https://drafts.csswg.org/css-conditional-3/#extentions-to-cssrule-interface
+ * https://drafts.csswg.org/css-fonts-3/#om-fontfeaturevalues
+ */
+
+// https://drafts.csswg.org/cssom/#the-cssrule-interface
+interface CSSRule {
+
+  const unsigned short STYLE_RULE = 1;
+  const unsigned short CHARSET_RULE = 2; // historical
+  const unsigned short IMPORT_RULE = 3;
+  const unsigned short MEDIA_RULE = 4;
+  const unsigned short FONT_FACE_RULE = 5;
+  const unsigned short PAGE_RULE = 6;
+  // FIXME: We don't support MARGIN_RULE yet.
+  // XXXbz Should we expose the constant anyway?
+  // const unsigned short MARGIN_RULE = 9;
+  const unsigned short NAMESPACE_RULE = 10;
+  readonly attribute unsigned short type;
+  attribute DOMString cssText;
+  readonly attribute CSSRule? parentRule;
+  readonly attribute CSSStyleSheet? parentStyleSheet;
+};
+
+// https://drafts.csswg.org/css-animations/#interface-cssrule
+partial interface CSSRule {
+    const unsigned short KEYFRAMES_RULE = 7;
+    const unsigned short KEYFRAME_RULE = 8;
+};
+
+// https://drafts.csswg.org/css-counter-styles-3/#extentions-to-cssrule-interface
+partial interface CSSRule {
+    const unsigned short COUNTER_STYLE_RULE = 11;
+};
+
+// https://drafts.csswg.org/css-conditional-3/#extentions-to-cssrule-interface
+partial interface CSSRule {
+    const unsigned short SUPPORTS_RULE = 12;
+};
+
+// https://drafts.csswg.org/css-fonts-3/#om-fontfeaturevalues
+partial interface CSSRule {
+  const unsigned short FONT_FEATURE_VALUES_RULE = 14;
+};
--- a/dom/webidl/CSSStyleDeclaration.webidl
+++ b/dom/webidl/CSSStyleDeclaration.webidl
@@ -2,18 +2,16 @@
 /* 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/.
  *
  * The origin of this IDL file is
  * http://dev.w3.org/csswg/cssom/
  */
 
-interface CSSRule;
-
 interface CSSStyleDeclaration {
   [SetterThrows]
   attribute DOMString cssText;
 
   readonly attribute unsigned long length;
   getter DOMString item(unsigned long index);
 
   [Throws]
new file mode 100644
--- /dev/null
+++ b/dom/webidl/CSSStyleRule.webidl
@@ -0,0 +1,14 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ *
+ * The origin of this IDL file is
+ * https://drafts.csswg.org/cssom/#the-cssstylerule-interface
+ */
+
+// https://drafts.csswg.org/cssom/#the-cssstylerule-interface
+interface CSSStyleRule : CSSRule {
+  attribute DOMString selectorText;
+  [SameObject, PutForwards=cssText] readonly attribute CSSStyleDeclaration style;
+};
--- a/dom/webidl/CSSStyleSheet.webidl
+++ b/dom/webidl/CSSStyleSheet.webidl
@@ -2,18 +2,16 @@
 /* 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/.
  *
  * The origin of this IDL file is
  * http://dev.w3.org/csswg/cssom/
  */
 
-interface CSSRule;
-
 enum CSSStyleSheetParsingMode {
   "author",
   "user",
   "agent"
 };
 
 interface CSSStyleSheet : StyleSheet {
   [Pure]
new file mode 100644
--- /dev/null
+++ b/dom/webidl/CSSSupportsRule.webidl
@@ -0,0 +1,12 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ *
+ * The origin of this IDL file is
+ * https://drafts.csswg.org/css-conditional/#the-csssupportsrule-interface
+ */
+
+// https://drafts.csswg.org/css-conditional/#the-csssupportsrule-interface
+interface CSSSupportsRule : CSSConditionRule {
+};
--- a/dom/webidl/LegacyQueryInterface.webidl
+++ b/dom/webidl/LegacyQueryInterface.webidl
@@ -19,18 +19,20 @@ interface LegacyQueryInterface {
 };
 
 Attr implements LegacyQueryInterface;
 BarProp implements LegacyQueryInterface;
 BoxObject implements LegacyQueryInterface;
 CaretPosition implements LegacyQueryInterface;
 Comment implements LegacyQueryInterface;
 Crypto implements LegacyQueryInterface;
+CSSMozDocumentRule implements LegacyQueryInterface;
 CSSPrimitiveValue implements LegacyQueryInterface;
 CSSStyleDeclaration implements LegacyQueryInterface;
+CSSStyleRule implements LegacyQueryInterface;
 CSSValueList implements LegacyQueryInterface;
 DOMImplementation implements LegacyQueryInterface;
 DOMParser implements LegacyQueryInterface;
 DOMStringMap implements LegacyQueryInterface;
 DOMTokenList implements LegacyQueryInterface;
 Document implements LegacyQueryInterface;
 DocumentFragment implements LegacyQueryInterface;
 DocumentType implements LegacyQueryInterface;
--- a/dom/webidl/StyleRuleChangeEvent.webidl
+++ b/dom/webidl/StyleRuleChangeEvent.webidl
@@ -1,15 +1,13 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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/.
  */
-interface CSSRule;
-
 [ChromeOnly, Constructor(DOMString type, optional StyleRuleChangeEventInit eventInitDict)]
 interface StyleRuleChangeEvent : Event
 {
   readonly attribute CSSStyleSheet? stylesheet;
   readonly attribute CSSRule? rule;
 };
 
 dictionary StyleRuleChangeEventInit : EventInit
--- a/dom/webidl/StyleSheet.webidl
+++ b/dom/webidl/StyleSheet.webidl
@@ -2,18 +2,16 @@
 /* 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/.
  *
  * The origin of this IDL file is
  * http://dev.w3.org/csswg/cssom/
  */
 
-interface CSSRule;
-
 interface StyleSheet {
   [Constant]
   readonly attribute DOMString type;
   [Constant]
   readonly attribute DOMString? href;
   // Spec says "Node", but it can go null when the node gets a new
   // sheet.  That's also why it's not [Constant]
   [Pure]
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -84,22 +84,37 @@ WEBIDL_FILES = [
     'ConvolverNode.webidl',
     'Coordinates.webidl',
     'CreateOfferRequest.webidl',
     'Crypto.webidl',
     'CSPDictionaries.webidl',
     'CSPReport.webidl',
     'CSS.webidl',
     'CSSAnimation.webidl',
+    'CSSConditionRule.webidl',
+    'CSSCounterStyleRule.webidl',
+    'CSSFontFaceRule.webidl',
+    'CSSFontFeatureValuesRule.webidl',
+    'CSSGroupingRule.webidl',
+    'CSSImportRule.webidl',
+    'CSSKeyframeRule.webidl',
+    'CSSKeyframesRule.webidl',
     'CSSLexer.webidl',
+    'CSSMediaRule.webidl',
+    'CSSMozDocumentRule.webidl',
+    'CSSNamespaceRule.webidl',
+    'CSSPageRule.webidl',
     'CSSPrimitiveValue.webidl',
     'CSSPseudoElement.webidl',
+    'CSSRule.webidl',
     'CSSRuleList.webidl',
     'CSSStyleDeclaration.webidl',
+    'CSSStyleRule.webidl',
     'CSSStyleSheet.webidl',
+    'CSSSupportsRule.webidl',
     'CSSTransition.webidl',
     'CSSValue.webidl',
     'CSSValueList.webidl',
     'CustomElementRegistry.webidl',
     'DataContainerEvent.webidl',
     'DataTransfer.webidl',
     'DataTransferItem.webidl',
     'DataTransferItemList.webidl',
--- a/dom/worklet/Worklet.cpp
+++ b/dom/worklet/Worklet.cpp
@@ -203,16 +203,18 @@ public:
     compileOptions.setFileAndLine(NS_ConvertUTF16toUTF8(mURL).get(), 0);
     compileOptions.setVersion(JSVERSION_DEFAULT);
     compileOptions.setIsRunOnce(true);
 
     // We only need the setNoScriptRval bit when compiling off-thread here,
     // since otherwise nsJSUtils::EvaluateString will set it up for us.
     compileOptions.setNoScriptRval(true);
 
+    JSAutoCompartment comp(cx, globalObj);
+
     JS::Rooted<JS::Value> unused(cx);
     if (!JS::Evaluate(cx, compileOptions, buffer, &unused)) {
       ErrorResult error;
       error.MightThrowJSException();
       error.StealExceptionFromJSContext(cx);
       RejectPromises(error.StealNSResult());
       return NS_OK;
     }
--- a/gfx/ipc/GPUParent.cpp
+++ b/gfx/ipc/GPUParent.cpp
@@ -21,16 +21,17 @@
 #include "mozilla/layers/APZThreadUtils.h"
 #include "mozilla/layers/APZCTreeManager.h"
 #include "mozilla/layers/CompositorBridgeParent.h"
 #include "mozilla/dom/VideoDecoderManagerParent.h"
 #include "mozilla/layers/CompositorThread.h"
 #include "mozilla/layers/ImageBridgeParent.h"
 #include "mozilla/dom/VideoDecoderManagerChild.h"
 #include "mozilla/layers/LayerTreeOwnerTracker.h"
+#include "mozilla/layers/UiCompositorControllerParent.h"
 #include "nsDebugImpl.h"
 #include "nsExceptionHandler.h"
 #include "nsThreadManager.h"
 #include "prenv.h"
 #include "ProcessUtils.h"
 #include "VRManager.h"
 #include "VRManagerParent.h"
 #include "VsyncBridgeParent.h"
@@ -215,16 +216,23 @@ GPUParent::RecvInitImageBridge(Endpoint<
 mozilla::ipc::IPCResult
 GPUParent::RecvInitVRManager(Endpoint<PVRManagerParent>&& aEndpoint)
 {
   VRManagerParent::CreateForGPUProcess(Move(aEndpoint));
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
+GPUParent::RecvInitUiCompositorController(Endpoint<PUiCompositorControllerParent>&& aEndpoint)
+{
+  UiCompositorControllerParent::Start(Move(aEndpoint));
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
 GPUParent::RecvUpdatePref(const GfxPrefSetting& setting)
 {
   gfxPrefs::Pref* pref = gfxPrefs::all()[setting.index()];
   pref->SetCachedValue(setting.value());
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
--- a/gfx/ipc/GPUParent.h
+++ b/gfx/ipc/GPUParent.h
@@ -31,16 +31,17 @@ public:
   void NotifyDeviceReset();
 
   mozilla::ipc::IPCResult RecvInit(nsTArray<GfxPrefSetting>&& prefs,
                                    nsTArray<GfxVarUpdate>&& vars,
                                    const DevicePrefs& devicePrefs) override;
   mozilla::ipc::IPCResult RecvInitVsyncBridge(Endpoint<PVsyncBridgeParent>&& aVsyncEndpoint) override;
   mozilla::ipc::IPCResult RecvInitImageBridge(Endpoint<PImageBridgeParent>&& aEndpoint) override;
   mozilla::ipc::IPCResult RecvInitVRManager(Endpoint<PVRManagerParent>&& aEndpoint) override;
+  mozilla::ipc::IPCResult RecvInitUiCompositorController(Endpoint<PUiCompositorControllerParent>&& aEndpoint) override;
   mozilla::ipc::IPCResult RecvUpdatePref(const GfxPrefSetting& pref) override;
   mozilla::ipc::IPCResult RecvUpdateVar(const GfxVarUpdate& pref) override;
   mozilla::ipc::IPCResult RecvNewWidgetCompositor(
       Endpoint<PCompositorBridgeParent>&& aEndpoint,
       const CSSToLayoutDeviceScale& aScale,
       const TimeDuration& aVsyncRate,
       const CompositorOptions& aOptions,
       const bool& aUseExternalSurface,
--- a/gfx/ipc/GPUProcessManager.cpp
+++ b/gfx/ipc/GPUProcessManager.cpp
@@ -28,16 +28,21 @@
 #include "VRManagerParent.h"
 #include "VsyncBridgeChild.h"
 #include "VsyncIOThreadHolder.h"
 #include "VsyncSource.h"
 #include "mozilla/dom/VideoDecoderManagerChild.h"
 #include "mozilla/dom/VideoDecoderManagerParent.h"
 #include "MediaPrefs.h"
 
+#if defined(MOZ_WIDGET_ANDROID)
+#include "mozilla/widget/AndroidUiThread.h"
+#include "mozilla/layers/UiCompositorControllerChild.h"
+#endif // defined(MOZ_WIDGET_ANDROID)
+
 namespace mozilla {
 namespace gfx {
 
 using namespace mozilla::layers;
 
 static StaticAutoPtr<GPUProcessManager> sSingleton;
 
 GPUProcessManager*
@@ -224,16 +229,54 @@ GPUProcessManager::EnsureVRManager()
     DisableGPUProcess("Failed to create PVRManager endpoints");
     return;
   }
 
   mGPUChild->SendInitVRManager(Move(parentPipe));
   VRManagerChild::InitWithGPUProcess(Move(childPipe));
 }
 
+#if defined(MOZ_WIDGET_ANDROID)
+void
+GPUProcessManager::EnsureUiCompositorController()
+{
+  if (UiCompositorControllerChild::IsInitialized()) {
+    return;
+  }
+
+  EnsureGPUReady();
+
+  RefPtr<nsThread> uiThread;
+
+  uiThread = GetAndroidUiThread();
+
+  MOZ_ASSERT(uiThread);
+
+  if (!mGPUChild) {
+    UiCompositorControllerChild::InitSameProcess(uiThread);
+    return;
+  }
+
+  ipc::Endpoint<PUiCompositorControllerParent> parentPipe;
+  ipc::Endpoint<PUiCompositorControllerChild> childPipe;
+  nsresult rv = PUiCompositorController::CreateEndpoints(
+    mGPUChild->OtherPid(),
+    base::GetCurrentProcId(),
+    &parentPipe,
+    &childPipe);
+  if (NS_FAILED(rv)) {
+    DisableGPUProcess("Failed to create PUiCompositorController endpoints");
+    return;
+  }
+
+  mGPUChild->SendInitUiCompositorController(Move(parentPipe));
+  UiCompositorControllerChild::InitWithGPUProcess(uiThread, mProcessToken, Move(childPipe));
+}
+#endif // defined(MOZ_WIDGET_ANDROID)
+
 void
 GPUProcessManager::OnProcessLaunchComplete(GPUProcessHost* aHost)
 {
   MOZ_ASSERT(mProcess && mProcess == aHost);
 
   if (!mProcess->IsConnected()) {
     DisableGPUProcess("Failed to connect GPU process");
     return;
@@ -477,31 +520,37 @@ GPUProcessManager::DestroyProcess()
   mProcess->Shutdown();
   mProcessToken = 0;
   mProcess = nullptr;
   mGPUChild = nullptr;
   if (mVsyncBridge) {
     mVsyncBridge->Close();
     mVsyncBridge = nullptr;
   }
+#if defined(MOZ_WIDGET_ANDROID)
+  UiCompositorControllerChild::Shutdown();
+#endif // defined(MOZ_WIDGET_ANDROID)
 }
 
 RefPtr<CompositorSession>
 GPUProcessManager::CreateTopLevelCompositor(nsBaseWidget* aWidget,
                                             LayerManager* aLayerManager,
                                             CSSToLayoutDeviceScale aScale,
                                             const CompositorOptions& aOptions,
                                             bool aUseExternalSurfaceSize,
                                             const gfx::IntSize& aSurfaceSize)
 {
   uint64_t layerTreeId = AllocateLayerTreeId();
 
   EnsureGPUReady();
   EnsureImageBridgeChild();
   EnsureVRManager();
+#if defined(MOZ_WIDGET_ANDROID)
+  EnsureUiCompositorController();
+#endif // defined(MOZ_WIDGET_ANDROID)
 
   if (mGPUChild) {
     RefPtr<CompositorSession> session = CreateRemoteSession(
       aWidget,
       aLayerManager,
       layerTreeId,
       aScale,
       aOptions,
--- a/gfx/ipc/GPUProcessManager.h
+++ b/gfx/ipc/GPUProcessManager.h
@@ -188,16 +188,17 @@ private:
 
   void HandleProcessLost();
 
   void EnsureVsyncIOThread();
   void ShutdownVsyncIOThread();
 
   void EnsureImageBridgeChild();
   void EnsureVRManager();
+  void EnsureUiCompositorController();
 
   RefPtr<CompositorSession> CreateRemoteSession(
     nsBaseWidget* aWidget,
     LayerManager* aLayerManager,
     const uint64_t& aRootLayerTreeId,
     CSSToLayoutDeviceScale aScale,
     const CompositorOptions& aOptions,
     bool aUseExternalSurfaceSize,
--- a/gfx/ipc/PGPU.ipdl
+++ b/gfx/ipc/PGPU.ipdl
@@ -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 GraphicsMessages;
 include protocol PCompositorBridge;
 include protocol PImageBridge;
 include protocol PVRManager;
 include protocol PVsyncBridge;
+include protocol PUiCompositorController;
 include protocol PVideoDecoderManager;
 
 using base::ProcessId from "base/process.h";
 using mozilla::TimeDuration from "mozilla/TimeStamp.h";
 using mozilla::CSSToLayoutDeviceScale from "Units.h";
 using mozilla::gfx::IntSize from "mozilla/gfx/2D.h";
 using mozilla::layers::CompositorOptions from "mozilla/layers/CompositorOptions.h";
 using mozilla::Telemetry::Accumulation from "mozilla/TelemetryComms.h";
@@ -47,16 +48,17 @@ parent:
   // Sent by the UI process to initiate core settings.
   async Init(GfxPrefSetting[] prefs,
              GfxVarUpdate[] vars,
              DevicePrefs devicePrefs);
 
   async InitVsyncBridge(Endpoint<PVsyncBridgeParent> endpoint);
   async InitImageBridge(Endpoint<PImageBridgeParent> endpoint);
   async InitVRManager(Endpoint<PVRManagerParent> endpoint);
+  async InitUiCompositorController(Endpoint<PUiCompositorControllerParent> endpoint);
 
   // Called to update a gfx preference or variable.
   async UpdatePref(GfxPrefSetting pref);
   async UpdateVar(GfxVarUpdate var);
 
   // Create a new top-level compositor.
   async NewWidgetCompositor(Endpoint<PCompositorBridgeParent> endpoint,
                             CSSToLayoutDeviceScale scale,
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -416,20 +416,21 @@ CompositorBridgeParent::~CompositorBridg
   // We expect all textures to be destroyed by now.
   MOZ_DIAGNOSTIC_ASSERT(textures.Length() == 0);
   for (unsigned int i = 0; i < textures.Length(); ++i) {
     RefPtr<TextureHost> tex = TextureHost::AsTextureHost(textures[i]);
     tex->DeallocateDeviceData();
   }
 }
 
-void
-CompositorBridgeParent::ForceIsFirstPaint()
+mozilla::ipc::IPCResult
+CompositorBridgeParent::RecvForceIsFirstPaint()
 {
   mCompositionManager->ForceIsFirstPaint();
+  return IPC_OK();
 }
 
 void
 CompositorBridgeParent::StopAndClearResources()
 {
   if (mForceCompositionTask) {
     mForceCompositionTask->Cancel();
     mForceCompositionTask = nullptr;
@@ -1112,16 +1113,23 @@ CompositorBridgeParent::RecvGetComposito
 }
 
 RefPtr<APZCTreeManager>
 CompositorBridgeParent::GetAPZCTreeManager()
 {
   return mApzcTreeManager;
 }
 
+CompositorBridgeParent*
+CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(const uint64_t& aLayersId)
+{
+  MonitorAutoLock lock(*sIndirectLayerTreesLock);
+  return sIndirectLayerTrees[aLayersId].mParent;
+}
+
 bool
 CompositorBridgeParent::CanComposite()
 {
   return mLayerManager &&
          mLayerManager->GetRoot() &&
          !mPaused;
 }
 
--- a/gfx/layers/ipc/CompositorBridgeParent.h
+++ b/gfx/layers/ipc/CompositorBridgeParent.h
@@ -260,17 +260,17 @@ public:
 
   /**
    * This forces the is-first-paint flag to true. This is intended to
    * be called by the widget code when it loses its viewport information
    * (or for whatever reason wants to refresh the viewport information).
    * The information refresh happens because the compositor will call
    * SetFirstPaintViewport on the next frame of composition.
    */
-  void ForceIsFirstPaint();
+  mozilla::ipc::IPCResult RecvForceIsFirstPaint() override;
 
   static void SetShadowProperties(Layer* aLayer);
 
   void NotifyChildCreated(uint64_t aChild);
 
   void AsyncRender();
 
   // Can be called from any thread
@@ -436,16 +436,17 @@ public:
                                                    CompositorOptions* aOptions) override;
 
   RefPtr<APZCTreeManager> GetAPZCTreeManager();
 
   CompositorOptions GetOptions() const {
     return mOptions;
   }
 
+  static CompositorBridgeParent* GetCompositorBridgeParentFromLayersId(const uint64_t& aLayersId);
 private:
 
   void Initialize();
 
   /**
    * Called during destruction in order to release resources as early as possible.
    */
   void StopAndClearResources();
@@ -475,22 +476,26 @@ protected:
                                  TextureFactoryIdentifier* aTextureFactoryIdentifier,
                                  bool* aSuccess) override;
   virtual bool DeallocPLayerTransactionParent(PLayerTransactionParent* aLayers) override;
   virtual void ScheduleTask(already_AddRefed<CancelableRunnable>, int);
 
   void SetEGLSurfaceSize(int width, int height);
 
   void InitializeLayerManager(const nsTArray<LayersBackend>& aBackendHints);
+
+public:
   void PauseComposition();
   void ResumeComposition();
   void ResumeCompositionAndResize(int width, int height);
+  void Invalidate();
+
+protected:
   void ForceComposition();
   void CancelCurrentCompositeTask();
-  void Invalidate();
 
   // CompositorVsyncSchedulerOwner
   bool IsPendingComposite() override;
   void FinishPendingComposite() override;
   void CompositeToTarget(gfx::DrawTarget* aTarget, const gfx::IntRect* aRect = nullptr) override;
 
   RefPtr<Compositor> NewCompositor(const nsTArray<LayersBackend>& aBackendHints);
   void ResetCompositorTask(const nsTArray<LayersBackend>& aBackendHints,
--- a/gfx/layers/ipc/CrossProcessCompositorBridgeParent.h
+++ b/gfx/layers/ipc/CrossProcessCompositorBridgeParent.h
@@ -49,16 +49,17 @@ public:
                          const uint64_t& aSeqNo,
                          bool* aResult,
                          TextureFactoryIdentifier* aOutIdentifier) override
   { return IPC_FAIL_NO_REASON(this); }
   virtual mozilla::ipc::IPCResult RecvRequestOverfill() override { return IPC_OK(); }
   virtual mozilla::ipc::IPCResult RecvWillClose() override { return IPC_OK(); }
   virtual mozilla::ipc::IPCResult RecvPause() override { return IPC_OK(); }
   virtual mozilla::ipc::IPCResult RecvResume() override { return IPC_OK(); }
+  virtual mozilla::ipc::IPCResult RecvForceIsFirstPaint() override { return IPC_OK(); }
   virtual mozilla::ipc::IPCResult RecvNotifyChildCreated(const uint64_t& child) override;
   virtual mozilla::ipc::IPCResult RecvNotifyChildRecreated(const uint64_t& child) override { return IPC_FAIL_NO_REASON(this); }
   virtual mozilla::ipc::IPCResult RecvAdoptChild(const uint64_t& child) override { return IPC_FAIL_NO_REASON(this); }
   virtual mozilla::ipc::IPCResult RecvMakeSnapshot(const SurfaceDescriptor& aInSnapshot,
                                 const gfx::IntRect& aRect) override
   { return IPC_OK(); }
   virtual mozilla::ipc::IPCResult RecvFlushRendering() override { return IPC_OK(); }
   virtual mozilla::ipc::IPCResult RecvForcePresent() override { return IPC_OK(); }
--- a/gfx/layers/ipc/PCompositorBridge.ipdl
+++ b/gfx/layers/ipc/PCompositorBridge.ipdl
@@ -172,16 +172,17 @@ parent:
 
   // The child is about to be destroyed, so perform any necessary cleanup.
   sync WillClose();
 
   // Pause/resume the compositor. These are intended to be used on mobile, when
   // the compositor needs to pause/resume in lockstep with the application.
   sync Pause();
   sync Resume();
+  async ForceIsFirstPaint();
 
   // See bug 1316632 comment #33 for why this has to be sync. Otherwise,
   // there are ordering issues with SendPLayerTransactionConstructor.
   sync NotifyChildCreated(uint64_t id);
 
   async AdoptChild(uint64_t id);
 
   // Same as NotifyChildCreated, but used when child processes need to
new file mode 100644
--- /dev/null
+++ b/gfx/layers/ipc/PUiCompositorController.ipdl
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * The PUiCompositorController protocol is used to pause and resume the
+ * compositor from the UI thread. Primarily used on Android to coordinate registering and
+ * releasing the surface with the compositor.
+ */
+sync protocol PUiCompositorController
+{
+
+parent:
+  // Pause/resume the compositor. These are intended to be used on mobile, when
+  // the compositor needs to pause/resume in lockstep with the application.
+  sync Pause(uint64_t layersId);
+  sync Resume(uint64_t layersId);
+  sync ResumeAndResize(uint64_t layersId, int32_t height, int32_t width);
+  async InvalidateAndRender(uint64_t layersId);
+};
+
+} // layers
+} // mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/ipc/UiCompositorControllerChild.cpp
@@ -0,0 +1,184 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=2 et sw=2 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 "UiCompositorControllerChild.h"
+#include "UiCompositorControllerParent.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/StaticPtr.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+namespace layers {
+
+static bool sInitialized = false;
+static StaticRefPtr<UiCompositorControllerChild> sChild;
+static StaticRefPtr<UiCompositorControllerParent> sParent;
+
+UiCompositorControllerChild::UiCompositorControllerChild(RefPtr<nsThread> aThread, const uint64_t& aProcessToken)
+ : mUiThread(aThread),
+   mProcessToken(aProcessToken)
+{
+}
+
+UiCompositorControllerChild::~UiCompositorControllerChild()
+{
+}
+
+/* static */ UiCompositorControllerChild*
+UiCompositorControllerChild::Get()
+{
+  return sChild;
+}
+
+/* static */ bool
+UiCompositorControllerChild::IsInitialized()
+{
+  return sInitialized;
+}
+
+/* static */ void
+UiCompositorControllerChild::Shutdown()
+{
+  RefPtr<UiCompositorControllerChild> child = sChild;
+  if (child) {
+    child->Close();
+    sInitialized = false;
+  }
+}
+
+/* static */ void
+UiCompositorControllerChild::InitSameProcess(RefPtr<nsThread> aThread)
+{
+  MOZ_ASSERT(!sChild);
+  MOZ_ASSERT(!sParent);
+  MOZ_ASSERT(aThread);
+  MOZ_ASSERT(!sInitialized);
+
+  sInitialized = true;
+  RefPtr<UiCompositorControllerChild> child = new UiCompositorControllerChild(aThread, 0);
+  sParent = new UiCompositorControllerParent();
+  aThread->Dispatch(NewRunnableMethod(child, &UiCompositorControllerChild::OpenForSameProcess), nsIThread::DISPATCH_NORMAL);
+}
+
+/* static */ void
+UiCompositorControllerChild::InitWithGPUProcess(RefPtr<nsThread> aThread,
+                                                const uint64_t& aProcessToken,
+                                                Endpoint<PUiCompositorControllerChild>&& aEndpoint)
+{
+  MOZ_ASSERT(!sChild);
+  MOZ_ASSERT(!sParent);
+  MOZ_ASSERT(aThread);
+  MOZ_ASSERT(!sInitialized);
+
+  sInitialized = true;
+  RefPtr<UiCompositorControllerChild> child = new UiCompositorControllerChild(aThread, aProcessToken);
+
+  RefPtr<nsIRunnable> task = NewRunnableMethod<Endpoint<PUiCompositorControllerChild>&&>(
+    child, &UiCompositorControllerChild::OpenForGPUProcess, Move(aEndpoint));
+
+  aThread->Dispatch(task.forget(), nsIThread::DISPATCH_NORMAL);
+}
+
+void
+UiCompositorControllerChild::OpenForSameProcess()
+{
+  MOZ_ASSERT(sParent);
+  MOZ_ASSERT(!sChild);
+  MOZ_ASSERT(IsOnUiThread());
+
+  if (!Open(sParent->GetIPCChannel(),
+           mozilla::layers::CompositorThreadHolder::Loop(),
+           mozilla::ipc::ChildSide)) {
+    sParent = nullptr;
+    return;
+  }
+
+  AddRef();
+  sChild = this;
+}
+
+void
+UiCompositorControllerChild::OpenForGPUProcess(Endpoint<PUiCompositorControllerChild>&& aEndpoint)
+{
+  MOZ_ASSERT(!sChild);
+  MOZ_ASSERT(IsOnUiThread());
+
+  if (!aEndpoint.Bind(this)) {
+    // The GPU Process Manager might be gone if we receive ActorDestroy very
+    // late in shutdown.
+    if (GPUProcessManager* gpm = GPUProcessManager::Get()) {
+      gpm->NotifyRemoteActorDestroyed(mProcessToken);
+    }
+    return;
+  }
+
+  AddRef();
+  sChild = this;
+}
+
+void
+UiCompositorControllerChild::Close()
+{
+  if (!IsOnUiThread()) {
+    mUiThread->Dispatch(NewRunnableMethod(this, &UiCompositorControllerChild::Close), nsIThread::DISPATCH_NORMAL);
+    return;
+  }
+
+  // We clear mProcessToken when the channel is closed.
+  if (!mProcessToken) {
+    return;
+  }
+
+  // Clear the process token so we don't notify the GPUProcessManager. It already
+  // knows we're closed since it manually called Close, and in fact the GPM could
+  // have already been destroyed during shutdown.
+  mProcessToken = 0;
+  if (this == sChild) {
+    sChild = nullptr;
+  }
+
+  // Close the underlying IPC channel.
+  PUiCompositorControllerChild::Close();
+}
+
+void
+UiCompositorControllerChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+  if (mProcessToken) {
+    GPUProcessManager::Get()->NotifyRemoteActorDestroyed(mProcessToken);
+    mProcessToken = 0;
+    sParent = nullptr;
+  }
+}
+
+void
+UiCompositorControllerChild::DeallocPUiCompositorControllerChild()
+{
+  Release();
+  sInitialized = false;
+}
+
+void
+UiCompositorControllerChild::ProcessingError(Result aCode, const char* aReason)
+{
+  MOZ_RELEASE_ASSERT(aCode == MsgDropped, "Processing error in UiCompositorControllerChild");
+}
+
+void
+UiCompositorControllerChild::HandleFatalError(const char* aName, const char* aMsg) const
+{
+  dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aName, aMsg, OtherPid());
+}
+
+bool
+UiCompositorControllerChild::IsOnUiThread() const
+{
+  return NS_GetCurrentThread() == mUiThread;
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/ipc/UiCompositorControllerChild.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=2 et sw=2 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/. */
+#ifndef include_gfx_ipc_UiCompositorControllerChild_h
+#define include_gfx_ipc_UiCompositorControllerChild_h
+
+#include "mozilla/layers/PUiCompositorControllerChild.h"
+#include "mozilla/RefPtr.h"
+#include <nsThread.h>
+
+namespace mozilla {
+namespace layers {
+
+class UiCompositorControllerChild final : public PUiCompositorControllerChild
+{
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(UiCompositorControllerChild)
+
+  static bool IsInitialized();
+  static void Shutdown();
+  static UiCompositorControllerChild* Get();
+  static void InitSameProcess(RefPtr<nsThread> aThread);
+  static void InitWithGPUProcess(RefPtr<nsThread> aThread,
+                                 const uint64_t& aProcessToken,
+                                 Endpoint<PUiCompositorControllerChild>&& aEndpoint);
+
+  void Close();
+
+  void ActorDestroy(ActorDestroyReason aWhy) override;
+  void DeallocPUiCompositorControllerChild() override;
+  void ProcessingError(Result aCode, const char* aReason) override;
+
+  virtual void HandleFatalError(const char* aName, const char* aMsg) const override;
+
+private:
+  UiCompositorControllerChild(RefPtr<nsThread> aThread, const uint64_t& aProcessToken);
+  ~UiCompositorControllerChild();
+  void OpenForSameProcess();
+  void OpenForGPUProcess(Endpoint<PUiCompositorControllerChild>&& aEndpoint);
+  bool IsOnUiThread() const;
+
+  RefPtr<nsThread> mUiThread;
+  uint64_t mProcessToken;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // include_gfx_ipc_UiCompositorControllerChild_h
new file mode 100644
--- /dev/null
+++ b/gfx/layers/ipc/UiCompositorControllerParent.cpp
@@ -0,0 +1,118 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=2 et sw=2 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 "UiCompositorControllerParent.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/CompositorThread.h"
+
+namespace mozilla {
+namespace layers {
+
+RefPtr<UiCompositorControllerParent>
+UiCompositorControllerParent::Start(Endpoint<PUiCompositorControllerParent>&& aEndpoint)
+{
+  RefPtr<UiCompositorControllerParent> parent = new UiCompositorControllerParent();
+
+  RefPtr<Runnable> task = NewRunnableMethod<Endpoint<PUiCompositorControllerParent>&&>(
+    parent, &UiCompositorControllerParent::Open, Move(aEndpoint));
+  CompositorThreadHolder::Loop()->PostTask(task.forget());
+
+  return parent;
+}
+
+UiCompositorControllerParent::UiCompositorControllerParent()
+{
+  MOZ_COUNT_CTOR(UiCompositorControllerParent);
+}
+
+UiCompositorControllerParent::~UiCompositorControllerParent()
+{
+  MOZ_COUNT_DTOR(UiCompositorControllerParent);
+}
+
+void
+UiCompositorControllerParent::Open(Endpoint<PUiCompositorControllerParent>&& aEndpoint)
+{
+  if (!aEndpoint.Bind(this)) {
+    // We can't recover from this.
+    MOZ_CRASH("Failed to bind UiCompositorControllerParent to endpoint");
+  }
+  AddRef();
+}
+
+mozilla::ipc::IPCResult
+UiCompositorControllerParent::RecvPause(const uint64_t& aLayersId)
+{
+  CompositorBridgeParent* parent = CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(aLayersId);
+  if (parent) {
+    parent->PauseComposition();
+  }
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+UiCompositorControllerParent::RecvResume(const uint64_t& aLayersId)
+{
+  CompositorBridgeParent* parent = CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(aLayersId);
+  if (parent) {
+    parent->ResumeComposition();
+  }
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+UiCompositorControllerParent::RecvResumeAndResize(const uint64_t& aLayersId,
+                                                  const int32_t& aHeight,
+                                                  const int32_t& aWidth)
+{
+  CompositorBridgeParent* parent = CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(aLayersId);
+  if (parent) {
+    parent->ResumeCompositionAndResize(aHeight, aWidth);
+  }
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+UiCompositorControllerParent::RecvInvalidateAndRender(const uint64_t& aLayersId)
+{
+  CompositorBridgeParent* parent = CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(aLayersId);
+  if (parent) {
+    parent->Invalidate();
+    parent->ScheduleComposition();
+  }
+  return IPC_OK();
+}
+
+void
+UiCompositorControllerParent::Shutdown()
+{
+  MessageLoop* ccloop = CompositorThreadHolder::Loop();
+  if (MessageLoop::current() != ccloop) {
+    ccloop->PostTask(NewRunnableMethod(this, &UiCompositorControllerParent::ShutdownImpl));
+    return;
+  }
+
+  ShutdownImpl();
+}
+
+void
+UiCompositorControllerParent::ShutdownImpl()
+{
+  Close();
+}
+
+void
+UiCompositorControllerParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+}
+
+void
+UiCompositorControllerParent::DeallocPUiCompositorControllerParent()
+{
+  Release();
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/ipc/UiCompositorControllerParent.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=2 et sw=2 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/. */
+#ifndef include_gfx_ipc_UiCompositorControllerParent_h
+#define include_gfx_ipc_UiCompositorControllerParent_h
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/layers/PUiCompositorControllerParent.h"
+
+namespace mozilla {
+namespace layers {
+
+class UiCompositorControllerParent final : public PUiCompositorControllerParent
+{
+public:
+  UiCompositorControllerParent();
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(UiCompositorControllerParent)
+
+  static RefPtr<UiCompositorControllerParent> Start(Endpoint<PUiCompositorControllerParent>&& aEndpoint);
+
+  mozilla::ipc::IPCResult RecvPause(const uint64_t& aLayersId) override;
+  mozilla::ipc::IPCResult RecvResume(const uint64_t& aLayersId) override;
+  mozilla::ipc::IPCResult RecvResumeAndResize(const uint64_t& aLayersId,
+                                              const int32_t& aHeight,
+                                              const int32_t& aWidth) override;
+  mozilla::ipc::IPCResult RecvInvalidateAndRender(const uint64_t& aLayersId) override;
+
+  void ActorDestroy(ActorDestroyReason aWhy) override;
+  void DeallocPUiCompositorControllerParent() override;
+
+  void Shutdown();
+
+private:
+  ~UiCompositorControllerParent();
+
+  void Open(Endpoint<PUiCompositorControllerParent>&& aEndpoint);
+  void ShutdownImpl();
+
+private:
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // include_gfx_ipc_UiCompositorControllerParent_h
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -178,16 +178,18 @@ EXPORTS.mozilla.layers += [
     'ipc/LayerTransactionParent.h',
     'ipc/LayerTreeOwnerTracker.h',
     'ipc/RemoteContentController.h',
     'ipc/ShadowLayers.h',
     'ipc/SharedPlanarYCbCrImage.h',
     'ipc/SharedRGBImage.h',
     'ipc/SynchronousTask.h',
     'ipc/TextureForwarder.h',
+    'ipc/UiCompositorControllerChild.h',
+    'ipc/UiCompositorControllerParent.h',
     'ipc/VideoBridgeChild.h',
     'ipc/VideoBridgeParent.h',
     'LayerMetricsWrapper.h',
     'LayersTypes.h',
     'opengl/CompositingRenderTargetOGL.h',
     'opengl/CompositorOGL.h',
     'opengl/MacIOSurfaceTextureClientOGL.h',
     'opengl/MacIOSurfaceTextureHostOGL.h',
@@ -342,16 +344,18 @@ UNIFIED_SOURCES += [
     'ipc/LayerAnimationUtils.cpp',
     'ipc/LayerTransactionChild.cpp',
     'ipc/LayerTransactionParent.cpp',
     'ipc/LayerTreeOwnerTracker.cpp',
     'ipc/RemoteContentController.cpp',
     'ipc/ShadowLayers.cpp',
     'ipc/SharedPlanarYCbCrImage.cpp',
     'ipc/SharedRGBImage.cpp',
+    'ipc/UiCompositorControllerChild.cpp',
+    'ipc/UiCompositorControllerParent.cpp',
     'ipc/VideoBridgeChild.cpp',
     'ipc/VideoBridgeParent.cpp',
     'LayerScope.cpp',
     'LayersLogging.cpp',
     'LayerSorter.cpp',
     'LayersTypes.cpp',
     'opengl/CompositingRenderTargetOGL.cpp',
     'opengl/CompositorOGL.cpp',
@@ -389,25 +393,26 @@ if CONFIG['_MSC_VER'] and CONFIG['CPU_AR
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
     SOURCES += [
         'basic/MacIOSurfaceTextureHostBasic.cpp',
         'opengl/MacIOSurfaceTextureClientOGL.cpp',
         'opengl/MacIOSurfaceTextureHostOGL.cpp',
     ]
 
-IPDL_SOURCES = [
+IPDL_SOURCES += [
     'ipc/LayersMessages.ipdlh',
     'ipc/LayersSurfaces.ipdlh',
     'ipc/PAPZ.ipdl',
     'ipc/PAPZCTreeManager.ipdl',
     'ipc/PCompositorBridge.ipdl',
     'ipc/PImageBridge.ipdl',
     'ipc/PLayerTransaction.ipdl',
     'ipc/PTexture.ipdl',
+    'ipc/PUiCompositorController.ipdl',
     'ipc/PVideoBridge.ipdl',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 LOCAL_INCLUDES += [
     '/docshell/base',  # for nsDocShell.h
     '/layout/base',    # for TouchManager.h
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -89,16 +89,17 @@
 #include "mozilla/gfx/Logging.h"
 
 #if defined(MOZ_WIDGET_GTK)
 #include "gfxPlatformGtk.h" // xxx - for UseFcFontList
 #endif
 
 #ifdef MOZ_WIDGET_ANDROID
 #include "TexturePoolOGL.h"
+#include "mozilla/layers/UiCompositorControllerChild.h"
 #endif
 
 #ifdef USE_SKIA
 # ifdef __GNUC__
 #  pragma GCC diagnostic push
 #  pragma GCC diagnostic ignored "-Wshadow"
 # endif
 # include "skia/include/core/SkGraphics.h"
@@ -957,17 +958,19 @@ gfxPlatform::ShutdownLayersIPC()
         if (gfxPrefs::ChildProcessShutdown()) {
           layers::CompositorBridgeChild::ShutDown();
           layers::ImageBridgeChild::ShutDown();
         }
     } else if (XRE_IsParentProcess()) {
         gfx::VRManagerChild::ShutDown();
         layers::CompositorBridgeChild::ShutDown();
         layers::ImageBridgeChild::ShutDown();
-
+#if defined(MOZ_WIDGET_ANDROID)
+        layers::UiCompositorControllerChild::Shutdown();
+#endif // defined(MOZ_WIDGET_ANDROID)
         // This has to happen after shutting down the child protocols.
         layers::CompositorThreadHolder::Shutdown();
     } else {
       // TODO: There are other kind of processes and we should make sure gfx
       // stuff is either not created there or shut down properly.
     }
 }
 
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -243,18 +243,129 @@ class ObjectOpResult
      * Convenience method. The same as checkStrict(cx, id), except the
      * operation is not associated with a particular property id.
      */
     bool checkStrict(JSContext* cx, HandleObject obj) {
         return checkStrictErrorOrWarning(cx, obj, true);
     }
 };
 
+class PropertyResult
+{
+    union {
+        js::Shape* shape_;
+        uintptr_t bits_;
+    };
+
+    static const uintptr_t NotFound = 0;
+    static const uintptr_t NonNativeProperty = 1;
+    static const uintptr_t DenseOrTypedArrayElement = 1;
+
+  public:
+    PropertyResult() : bits_(NotFound) {}
+
+    explicit PropertyResult(js::Shape* propertyShape)
+      : shape_(propertyShape)
+    {
+        MOZ_ASSERT(!isFound() || isNativeProperty());
+    }
+
+    explicit operator bool() const {
+        return isFound();
+    }
+
+    bool isFound() const {
+        return bits_ != NotFound;
+    }
+
+    bool isNonNativeProperty() const {
+        return bits_ == NonNativeProperty;
+    }
+
+    bool isDenseOrTypedArrayElement() const {
+        return bits_ == DenseOrTypedArrayElement;
+    }
+
+    bool isNativeProperty() const {
+        return isFound() && !isNonNativeProperty();
+    }
+
+    js::Shape* maybeShape() const {
+        MOZ_ASSERT(!isNonNativeProperty());
+        return isFound() ? shape_ : nullptr;
+    }
+
+    js::Shape* shape() const {
+        MOZ_ASSERT(isNativeProperty());
+        return shape_;
+    }
+
+    void setNotFound() {
+        bits_ = NotFound;
+    }
+
+    void setNativeProperty(js::Shape* propertyShape) {
+        shape_ = propertyShape;
+        MOZ_ASSERT(isNativeProperty());
+    }
+
+    void setNonNativeProperty() {
+        bits_ = NonNativeProperty;
+    }
+
+    void setDenseOrTypedArrayElement() {
+        bits_ = DenseOrTypedArrayElement;
+    }
+
+    void trace(JSTracer* trc);
+};
+
 } // namespace JS
 
+namespace js {
+
+template <class Wrapper>
+class WrappedPtrOperations<JS::PropertyResult, Wrapper>
+{
+    const JS::PropertyResult& value() const { return static_cast<const Wrapper*>(this)->get(); }
+
+  public:
+    bool isFound() const { return value().isFound(); }
+    explicit operator bool() const { return bool(value()); }
+    js::Shape* maybeShape() const { return value().maybeShape(); }
+    js::Shape* shape() const { return value().shape(); }
+    bool isNativeProperty() const { return value().isNativeProperty(); }
+    bool isNonNativeProperty() const { return value().isNonNativeProperty(); }
+    bool isDenseOrTypedArrayElement() const { return value().isDenseOrTypedArrayElement(); }
+    js::Shape* asTaggedShape() const { return value().asTaggedShape(); }
+};
+
+template <class Wrapper>
+class MutableWrappedPtrOperations<JS::PropertyResult, Wrapper>
+  : public WrappedPtrOperations<JS::PropertyResult, Wrapper>
+{
+    JS::PropertyResult& value() { return static_cast<Wrapper*>(this)->get(); }
+
+  public:
+    void setNotFound() {
+        value().setNotFound();
+    }
+    void setNativeProperty(js::Shape* shape) {
+        value().setNativeProperty(shape);
+    }
+    void setNonNativeProperty() {
+        value().setNonNativeProperty();
+    }
+    void setDenseOrTypedArrayElement() {
+        value().setDenseOrTypedArrayElement();
+    }
+};
+
+} // namespace js
+
 // JSClass operation signatures.
 
 /**
  * Get a property named by id in obj.  Note the jsid id type -- id may
  * be a string (Unicode property identifier) or an int (element index).  The
  * *vp out parameter, on success, is the new property value after the action.
  */
 typedef bool
@@ -400,17 +511,17 @@ typedef void
 (* JSObjectMovedOp)(JSObject* obj, const JSObject* old);
 
 /* js::Class operation signatures. */
 
 namespace js {
 
 typedef bool
 (* LookupPropertyOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
-                     JS::MutableHandleObject objp, JS::MutableHandle<Shape*> propp);
+                     JS::MutableHandleObject objp, JS::MutableHandle<JS::PropertyResult> propp);
 typedef bool
 (* DefinePropertyOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
                      JS::Handle<JS::PropertyDescriptor> desc,
                      JS::ObjectOpResult& result);
 typedef bool
 (* HasPropertyOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* foundp);
 typedef bool
 (* GetPropertyOp)(JSContext* cx, JS::HandleObject obj, JS::HandleValue receiver, JS::HandleId id,
--- a/js/src/NamespaceImports.h
+++ b/js/src/NamespaceImports.h
@@ -41,16 +41,17 @@ using ScriptVector = JS::GCVector<JSScri
 template<typename K, typename V> class AutoHashMapRooter;
 template<typename T> class AutoHashSetRooter;
 
 class MOZ_STACK_CLASS SourceBufferHolder;
 
 class HandleValueArray;
 
 class ObjectOpResult;
+class PropertyResult;
 
 class Symbol;
 enum class SymbolCode: uint32_t;
 
 } // namespace JS
 
 // Do the importing.
 namespace js {
@@ -150,16 +151,17 @@ using JS::MutableHandleValue;
 using JS::NullHandleValue;
 using JS::UndefinedHandleValue;
 using JS::TrueHandleValue;
 using JS::FalseHandleValue;
 
 using JS::HandleValueArray;
 
 using JS::ObjectOpResult;
+using JS::PropertyResult;
 
 using JS::Zone;
 
 using JS::Symbol;
 using JS::SymbolCode;
 
 } /* namespace js */
 
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -63,28 +63,28 @@ js::obj_propertyIsEnumerable(JSContext* 
     // we can safely retrieve the attributes from the object's shape.
 
     /* Steps 1-2. */
     jsid id;
     if (args.thisv().isObject() && ValueToId<NoGC>(cx, idValue, &id)) {
         JSObject* obj = &args.thisv().toObject();
 
         /* Step 3. */
-        Shape* shape;
+        PropertyResult prop;
         if (obj->isNative() &&
-            NativeLookupOwnProperty<NoGC>(cx, &obj->as<NativeObject>(), id, &shape))
+            NativeLookupOwnProperty<NoGC>(cx, &obj->as<NativeObject>(), id, &prop))
         {
             /* Step 4. */
-            if (!shape) {
+            if (!prop) {
                 args.rval().setBoolean(false);
                 return true;
             }
 
             /* Step 5. */
-            unsigned attrs = GetShapeAttributes(obj, shape);
+            unsigned attrs = GetPropertyAttributes(obj, prop);
             args.rval().setBoolean((attrs & JSPROP_ENUMERATE) != 0);
             return true;
         }
     }
 
     /* Step 1. */
     RootedId idRoot(cx);
     if (!ToPropertyKey(cx, idValue, &idRoot))
@@ -575,21 +575,21 @@ js::obj_hasOwnProperty(JSContext* cx, un
 
     // As an optimization, provide a fast path when rooting is not necessary and
     // we can safely retrieve the object's shape.
 
     /* Step 1, 2. */
     jsid id;
     if (args.thisv().isObject() && ValueToId<NoGC>(cx, idValue, &id)) {
         JSObject* obj = &args.thisv().toObject();
-        Shape* prop;
+        PropertyResult prop;
         if (obj->isNative() &&
             NativeLookupOwnProperty<NoGC>(cx, &obj->as<NativeObject>(), id, &prop))
         {
-            args.rval().setBoolean(!!prop);
+            args.rval().setBoolean(prop.isFound());
             return true;
         }
     }
 
     /* Step 1. */
     RootedId idRoot(cx);
     if (!ToPropertyKey(cx, idValue, &idRoot))
         return false;
@@ -834,17 +834,17 @@ EnumerableOwnProperties(JSContext* cx, c
         }
 
         // Step 4.a.i.
         if (nobj) {
             if (JSID_IS_INT(id) && nobj->containsDenseElement(JSID_TO_INT(id))) {
                 value = nobj->getDenseOrTypedArrayElement(JSID_TO_INT(id));
             } else {
                 shape = nobj->lookup(cx, id);
-                if (!shape || !(GetShapeAttributes(nobj, shape) & JSPROP_ENUMERATE))
+                if (!shape || !(shape->attributes() & JSPROP_ENUMERATE))
                     continue;
                 if (!shape->isAccessorShape()) {
                     if (!NativeGetExistingProperty(cx, nobj, nobj, shape, &value))
                         return false;
                 } else if (!GetProperty(cx, obj, obj, id, &value)) {
                     return false;
                 }
             }
--- a/js/src/builtin/ReflectParse.cpp
+++ b/js/src/builtin/ReflectParse.cpp
@@ -3039,17 +3039,22 @@ ASTSerializer::expression(ParseNode* pn,
         NodeVector cooked(cx);
         if (!cooked.reserve(pn->pn_count - 1))
             return false;
 
         for (ParseNode* next = pn->pn_head->pn_next; next; next = next->pn_next) {
             MOZ_ASSERT(pn->pn_pos.encloses(next->pn_pos));
 
             RootedValue expr(cx);
-            expr.setString(next->pn_atom);
+            if (next->isKind(PNK_RAW_UNDEFINED)) {
+                expr.setUndefined();
+            } else {
+                MOZ_ASSERT(next->isKind(PNK_TEMPLATE_STRING));
+                expr.setString(next->pn_atom);
+            }
             cooked.infallibleAppend(expr);
         }
 
         return builder.callSiteObj(raw, cooked, &pn->pn_pos, dst);
       }
 
       case PNK_ARRAY:
       {
@@ -3131,16 +3136,17 @@ ASTSerializer::expression(ParseNode* pn,
 
       case PNK_TEMPLATE_STRING:
       case PNK_STRING:
       case PNK_REGEXP:
       case PNK_NUMBER:
       case PNK_TRUE:
       case PNK_FALSE:
       case PNK_NULL:
+      case PNK_RAW_UNDEFINED:
         return literal(pn, dst);
 
       case PNK_YIELD_STAR:
       {
         MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos));
 
         RootedValue arg(cx);
         return expression(pn->pn_left, &arg) &&
@@ -3271,16 +3277,20 @@ ASTSerializer::literal(ParseNode* pn, Mu
       case PNK_NUMBER:
         val.setNumber(pn->pn_dval);
         break;
 
       case PNK_NULL:
         val.setNull();
         break;
 
+      case PNK_RAW_UNDEFINED:
+        val.setUndefined();
+        break;
+
       case PNK_TRUE:
         val.setBoolean(true);
         break;
 
       case PNK_FALSE:
         val.setBoolean(false);
         break;
 
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -1668,28 +1668,28 @@ TypeDescr::hasProperty(const JSAtomState
       }
     }
 
     MOZ_CRASH("Unexpected kind");
 }
 
 /* static */ bool
 TypedObject::obj_lookupProperty(JSContext* cx, HandleObject obj, HandleId id,
-                                MutableHandleObject objp, MutableHandleShape propp)
+                                MutableHandleObject objp, MutableHandle<PropertyResult> propp)
 {
     if (obj->as<TypedObject>().typeDescr().hasProperty(cx->names(), id)) {
-        MarkNonNativePropertyFound<CanGC>(propp);
+        propp.setNonNativeProperty();
         objp.set(obj);
         return true;
     }
 
     RootedObject proto(cx, obj->staticPrototype());
     if (!proto) {
         objp.set(nullptr);
-        propp.set(nullptr);
+        propp.setNotFound();
         return true;
     }
 
     return LookupProperty(cx, proto, id, objp, propp);
 }
 
 static bool
 ReportPropertyError(JSContext* cx,
--- a/js/src/builtin/TypedObject.h
+++ b/js/src/builtin/TypedObject.h
@@ -505,17 +505,17 @@ class TypedObject : public ShapedObject
                                                  uint32_t index,
                                                  MutableHandleValue vp);
 
   protected:
     static const ObjectOps objectOps_;
 
     static MOZ_MUST_USE bool obj_lookupProperty(JSContext* cx, HandleObject obj,
                                                 HandleId id, MutableHandleObject objp,
-                                                MutableHandleShape propp);
+                                                MutableHandle<PropertyResult> propp);
 
     static MOZ_MUST_USE bool obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id,
                                                 Handle<PropertyDescriptor> desc,
                                                 ObjectOpResult& result);
 
     static MOZ_MUST_USE bool obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id,
                                              bool* foundp);
 
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -2655,16 +2655,17 @@ BytecodeEmitter::checkSideEffects(ParseN
       // Trivial cases with no side effects.
       case PNK_NOP:
       case PNK_STRING:
       case PNK_TEMPLATE_STRING:
       case PNK_REGEXP:
       case PNK_TRUE:
       case PNK_FALSE:
       case PNK_NULL:
+      case PNK_RAW_UNDEFINED:
       case PNK_ELISION:
       case PNK_GENERATOR:
       case PNK_NUMBER:
       case PNK_OBJECT_PROPERTY_NAME:
         MOZ_ASSERT(pn->isArity(PN_NULLARY));
         *answer = false;
         return true;
 
@@ -5820,16 +5821,19 @@ ParseNode::getConstantValue(ExclusiveCon
         vp.setBoolean(true);
         return true;
       case PNK_FALSE:
         vp.setBoolean(false);
         return true;
       case PNK_NULL:
         vp.setNull();
         return true;
+      case PNK_RAW_UNDEFINED:
+        vp.setUndefined();
+        return true;
       case PNK_CALLSITEOBJ:
       case PNK_ARRAY: {
         unsigned count;
         ParseNode* pn;
 
         if (allowObjects == DontAllowObjects) {
             vp.setMagic(JS_GENERIC_MAGIC);
             return true;
@@ -10205,16 +10209,17 @@ BytecodeEmitter::emitTree(ParseNode* pn,
       case PNK_REGEXP:
         if (!emitRegExp(objectList.add(pn->as<RegExpLiteral>().objbox())))
             return false;
         break;
 
       case PNK_TRUE:
       case PNK_FALSE:
       case PNK_NULL:
+      case PNK_RAW_UNDEFINED:
         if (!emit1(pn->getOp()))
             return false;
         break;
 
       case PNK_THIS:
         if (!emitThisLiteral(pn))
             return false;
         break;
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -373,16 +373,17 @@ ContainsHoistedDeclaration(ExclusiveCont
       case PNK_TEMPLATE_STRING_LIST:
       case PNK_TAGGED_TEMPLATE:
       case PNK_CALLSITEOBJ:
       case PNK_STRING:
       case PNK_REGEXP:
       case PNK_TRUE:
       case PNK_FALSE:
       case PNK_NULL:
+      case PNK_RAW_UNDEFINED:
       case PNK_THIS:
       case PNK_ELISION:
       case PNK_NUMBER:
       case PNK_NEW:
       case PNK_GENERATOR:
       case PNK_GENEXP:
       case PNK_ARRAYCOMP:
       case PNK_PARAMSBODY:
@@ -463,16 +464,17 @@ static bool
 IsEffectless(ParseNode* node)
 {
     return node->isKind(PNK_TRUE) ||
            node->isKind(PNK_FALSE) ||
            node->isKind(PNK_STRING) ||
            node->isKind(PNK_TEMPLATE_STRING) ||
            node->isKind(PNK_NUMBER) ||
            node->isKind(PNK_NULL) ||
+           node->isKind(PNK_RAW_UNDEFINED) ||
            node->isKind(PNK_FUNCTION) ||
            node->isKind(PNK_GENEXP);
 }
 
 enum Truthiness { Truthy, Falsy, Unknown };
 
 static Truthiness
 Boolish(ParseNode* pn)
@@ -487,16 +489,17 @@ Boolish(ParseNode* pn)
 
       case PNK_TRUE:
       case PNK_FUNCTION:
       case PNK_GENEXP:
         return Truthy;
 
       case PNK_FALSE:
       case PNK_NULL:
+      case PNK_RAW_UNDEFINED:
         return Falsy;
 
       case PNK_VOID: {
         // |void <foo>| evaluates to |undefined| which isn't truthy.  But the
         // sense of this method requires that the expression be literally
         // replaceable with true/false: not the case if the nested expression
         // is effectful, might throw, &c.  Walk past the |void| (and nested
         // |void| expressions, for good measure) and check that the nested
@@ -1638,16 +1641,17 @@ Fold(ExclusiveContext* cx, ParseNode** p
 
     switch (pn->getKind()) {
       case PNK_NOP:
       case PNK_REGEXP:
       case PNK_STRING:
       case PNK_TRUE:
       case PNK_FALSE:
       case PNK_NULL:
+      case PNK_RAW_UNDEFINED:
       case PNK_ELISION:
       case PNK_NUMBER:
       case PNK_DEBUGGER:
       case PNK_BREAK:
       case PNK_CONTINUE:
       case PNK_TEMPLATE_STRING:
       case PNK_GENERATOR:
       case PNK_EXPORT_BATCH_SPEC:
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -178,16 +178,20 @@ class FullParseHandler
     ParseNode* newThisLiteral(const TokenPos& pos, ParseNode* thisName) {
         return new_<ThisLiteral>(pos, thisName);
     }
 
     ParseNode* newNullLiteral(const TokenPos& pos) {
         return new_<NullLiteral>(pos);
     }
 
+    ParseNode* newRawUndefinedLiteral(const TokenPos& pos) {
+        return new_<RawUndefinedLiteral>(pos);
+    }
+
     // The Boxer object here is any object that can allocate ObjectBoxes.
     // Specifically, a Boxer has a .newObjectBox(T) method that accepts a
     // Rooted<RegExpObject*> argument and returns an ObjectBox*.
     template <class Boxer>
     ParseNode* newRegExp(RegExpObject* reobj, const TokenPos& pos, Boxer& boxer) {
         ObjectBox* objbox = boxer.newObjectBox(reobj);
         if (!objbox)
             return null();
--- a/js/src/frontend/NameFunctions.cpp
+++ b/js/src/frontend/NameFunctions.cpp
@@ -311,27 +311,28 @@ class NameResolver
         ParseNode* element = node->pn_head;
 
         // The list head is a leading expression, e.g. |tag| in |tag`foo`|,
         // that might contain functions.
         if (!resolve(element, prefix))
             return false;
 
         // Next is the callsite object node.  This node only contains
-        // internal strings and an array -- no user-controlled expressions.
+        // internal strings or undefined and an array -- no user-controlled
+        // expressions.
         element = element->pn_next;
 #ifdef DEBUG
         {
             MOZ_ASSERT(element->isKind(PNK_CALLSITEOBJ));
             ParseNode* array = element->pn_head;
             MOZ_ASSERT(array->isKind(PNK_ARRAY));
             for (ParseNode* kid = array->pn_head; kid; kid = kid->pn_next)
                 MOZ_ASSERT(kid->isKind(PNK_TEMPLATE_STRING));
             for (ParseNode* next = array->pn_next; next; next = next->pn_next)
-                MOZ_ASSERT(next->isKind(PNK_TEMPLATE_STRING));
+                MOZ_ASSERT(next->isKind(PNK_TEMPLATE_STRING) || next->isKind(PNK_RAW_UNDEFINED));
         }
 #endif
 
         // Next come any interpolated expressions in the tagged template.
         ParseNode* interpolated = element->pn_next;
         for (; interpolated; interpolated = interpolated->pn_next) {
             if (!resolve(interpolated, prefix))
                 return false;
@@ -377,16 +378,17 @@ class NameResolver
           // further work.
           case PNK_NOP:
           case PNK_STRING:
           case PNK_TEMPLATE_STRING:
           case PNK_REGEXP:
           case PNK_TRUE:
           case PNK_FALSE:
           case PNK_NULL:
+          case PNK_RAW_UNDEFINED:
           case PNK_ELISION:
           case PNK_GENERATOR:
           case PNK_NUMBER:
           case PNK_BREAK:
           case PNK_CONTINUE:
           case PNK_DEBUGGER:
           case PNK_EXPORT_BATCH_SPEC:
           case PNK_OBJECT_PROPERTY_NAME:
--- a/js/src/frontend/ParseNode.cpp
+++ b/js/src/frontend/ParseNode.cpp
@@ -185,16 +185,17 @@ PushNodeChildren(ParseNode* pn, NodeStac
       // but their parents, are never used, and are never a definition.
       case PNK_NOP:
       case PNK_STRING:
       case PNK_TEMPLATE_STRING:
       case PNK_REGEXP:
       case PNK_TRUE:
       case PNK_FALSE:
       case PNK_NULL:
+      case PNK_RAW_UNDEFINED:
       case PNK_ELISION:
       case PNK_GENERATOR:
       case PNK_NUMBER:
       case PNK_BREAK:
       case PNK_CONTINUE:
       case PNK_DEBUGGER:
       case PNK_EXPORT_BATCH_SPEC:
       case PNK_OBJECT_PROPERTY_NAME:
@@ -680,16 +681,17 @@ ParseNode::dump(int indent)
 
 void
 NullaryNode::dump()
 {
     switch (getKind()) {
       case PNK_TRUE:  fprintf(stderr, "#true");  break;
       case PNK_FALSE: fprintf(stderr, "#false"); break;
       case PNK_NULL:  fprintf(stderr, "#null");  break;
+      case PNK_RAW_UNDEFINED: fprintf(stderr, "#undefined"); break;
 
       case PNK_NUMBER: {
         ToCStringBuf cbuf;
         const char* cstr = NumberToCString(nullptr, &cbuf, pn_dval);
         if (!IsFinite(pn_dval))
             fputc('#', stderr);
         if (cstr)
             fprintf(stderr, "%s", cstr);
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -49,16 +49,17 @@ class ObjectBox;
     F(TEMPLATE_STRING_LIST) \
     F(TEMPLATE_STRING) \
     F(TAGGED_TEMPLATE) \
     F(CALLSITEOBJ) \
     F(REGEXP) \
     F(TRUE) \
     F(FALSE) \
     F(NULL) \
+    F(RAW_UNDEFINED) \
     F(THIS) \
     F(FUNCTION) \
     F(MODULE) \
     F(IF) \
     F(SWITCH) \
     F(CASE) \
     F(WHILE) \
     F(DOWHILE) \
@@ -401,17 +402,18 @@ IsTypeofKind(ParseNodeKind kind)
  * PNK_TAGGED_TEMPLATE      pn_head: list of call, call site object, arg1, arg2, ... argN
  *              list        pn_count: 2 + N (N is the number of substitutions)
  * PNK_CALLSITEOBJ list     pn_head: a PNK_ARRAY node followed by
  *                          list of pn_count - 1 PNK_TEMPLATE_STRING nodes
  * PNK_REGEXP   nullary     pn_objbox: RegExp model object
  * PNK_NUMBER   dval        pn_dval: double value of numeric literal
  * PNK_TRUE,    nullary     pn_op: JSOp bytecode
  * PNK_FALSE,
- * PNK_NULL
+ * PNK_NULL,
+ * PNK_RAW_UNDEFINED
  *
  * PNK_THIS,        unary   pn_kid: '.this' Name if function `this`, else nullptr
  * PNK_SUPERBASE    unary   pn_kid: '.this' Name
  *
  * PNK_SETTHIS      binary  pn_left: '.this' Name, pn_right: SuperCall
  *
  * PNK_LEXICALSCOPE scope   pn_u.scope.bindings: scope bindings
  *                          pn_u.scope.body: scope body
@@ -681,17 +683,18 @@ class ParseNode
     }
 
     /* True if pn is a parsenode representing a literal constant. */
     bool isLiteral() const {
         return isKind(PNK_NUMBER) ||
                isKind(PNK_STRING) ||
                isKind(PNK_TRUE) ||
                isKind(PNK_FALSE) ||
-               isKind(PNK_NULL);
+               isKind(PNK_NULL) ||
+               isKind(PNK_RAW_UNDEFINED);
     }
 
     /* Return true if this node appears in a Directive Prologue. */
     bool isDirectivePrologueMember() const { return pn_prologue; }
 
     // True iff this is a for-in/of loop variable declaration (var/let/const).
     bool isForLoopDeclaration() const {
         if (isKind(PNK_VAR) || isKind(PNK_LET) || isKind(PNK_CONST)) {
@@ -1136,16 +1139,26 @@ class ThisLiteral : public UnaryNode
 };
 
 class NullLiteral : public ParseNode
 {
   public:
     explicit NullLiteral(const TokenPos& pos) : ParseNode(PNK_NULL, JSOP_NULL, PN_NULLARY, pos) { }
 };
 
+// This is only used internally, currently just for tagged templates.
+// It represents the value 'undefined' (aka `void 0`), like NullLiteral
+// represents the value 'null'.
+class RawUndefinedLiteral : public ParseNode
+{
+  public:
+    explicit RawUndefinedLiteral(const TokenPos& pos)
+      : ParseNode(PNK_RAW_UNDEFINED, JSOP_UNDEFINED, PN_NULLARY, pos) { }
+};
+
 class BooleanLiteral : public ParseNode
 {
   public:
     BooleanLiteral(bool b, const TokenPos& pos)
       : ParseNode(b ? PNK_TRUE : PNK_FALSE, b ? JSOP_TRUE : JSOP_FALSE, PN_NULLARY, pos)
     { }
 };
 
@@ -1356,16 +1369,17 @@ class ParseNodeAllocator
 inline bool
 ParseNode::isConstant()
 {
     switch (pn_type) {
       case PNK_NUMBER:
       case PNK_STRING:
       case PNK_TEMPLATE_STRING:
       case PNK_NULL:
+      case PNK_RAW_UNDEFINED:
       case PNK_FALSE:
       case PNK_TRUE:
         return true;
       case PNK_ARRAY:
       case PNK_OBJECT:
         MOZ_ASSERT(isOp(JSOP_NEWINIT));
         return !(pn_xflags & PNX_NONCONST);
       default:
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -3080,30 +3080,30 @@ Parser<ParseHandler>::taggedTemplate(Yie
     handler.setEndPosition(nodeList, callSiteObjNode);
     return true;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::templateLiteral(YieldHandling yieldHandling)
 {
-    Node pn = noSubstitutionTemplate();
+    Node pn = noSubstitutionUntaggedTemplate();
     if (!pn)
         return null();
 
     Node nodeList = handler.newList(PNK_TEMPLATE_STRING_LIST, pn);
     if (!nodeList)
         return null();
 
     TokenKind tt;
     do {
         if (!addExprAndGetNextTemplStrToken(yieldHandling, nodeList, &tt))
             return null();
 
-        pn = noSubstitutionTemplate();
+        pn = noSubstitutionUntaggedTemplate();
         if (!pn)
             return null();
 
         handler.addList(nodeList, pn);
     } while (tt == TOK_TEMPLATE_HEAD);
     return nodeList;
 }
 
@@ -3316,17 +3316,17 @@ Parser<ParseHandler>::innerFunction(Node
     return innerFunction(pn, outerpc, funbox, inHandling, yieldHandling, kind, inheritedDirectives,
                          newDirectives);
 }
 
 template <typename ParseHandler>
 bool
 Parser<ParseHandler>::appendToCallSiteObj(Node callSiteObj)
 {
-    Node cookedNode = noSubstitutionTemplate();
+    Node cookedNode = noSubstitutionTaggedTemplate();
     if (!cookedNode)
         return false;
 
     JSAtom* atom = tokenStream.getRawTemplateStringAtom();
     if (!atom)
         return false;
     Node rawNode = handler.newTemplateStringLiteral(atom, pos());
     if (!rawNode)
@@ -8706,18 +8706,33 @@ template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::stringLiteral()
 {
     return handler.newStringLiteral(stopStringCompression(), pos());
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
-Parser<ParseHandler>::noSubstitutionTemplate()
-{
+Parser<ParseHandler>::noSubstitutionTaggedTemplate()
+{
+    if (tokenStream.hasInvalidTemplateEscape()) {
+        tokenStream.clearInvalidTemplateEscape();
+        return handler.newRawUndefinedLiteral(pos());
+    }
+
+    return handler.newTemplateStringLiteral(stopStringCompression(), pos());
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::noSubstitutionUntaggedTemplate()
+{
+    if (!tokenStream.checkForInvalidTemplateEscapeError())
+        return null();
+
     return handler.newTemplateStringLiteral(stopStringCompression(), pos());
 }
 
 template <typename ParseHandler>
 JSAtom * Parser<ParseHandler>::stopStringCompression() {
     JSAtom* atom = tokenStream.currentToken().atom();
 
     // Large strings are fast to parse but slow to compress. Stop compression on
@@ -9420,17 +9435,17 @@ Parser<ParseHandler>::primaryExpr(YieldH
         handler.setEndPosition(expr, pos().end);
         return handler.parenthesize(expr);
       }
 
       case TOK_TEMPLATE_HEAD:
         return templateLiteral(yieldHandling);
 
       case TOK_NO_SUBS_TEMPLATE:
-        return noSubstitutionTemplate();
+        return noSubstitutionUntaggedTemplate();
 
       case TOK_STRING:
         return stringLiteral();
 
       case TOK_YIELD:
       case TOK_NAME: {
         if (tokenStream.currentName() == context->names().async &&
             !tokenStream.currentToken().nameContainsEscape())
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -1048,17 +1048,18 @@ class Parser final : public ParserBase, 
     bool checkUnescapedName();
 
   private:
     Parser* thisForCtor() { return this; }
 
     JSAtom* stopStringCompression();
 
     Node stringLiteral();
-    Node noSubstitutionTemplate();
+    Node noSubstitutionTaggedTemplate();
+    Node noSubstitutionUntaggedTemplate();
     Node templateLiteral(YieldHandling yieldHandling);
     bool taggedTemplate(YieldHandling yieldHandling, Node nodeList, TokenKind tt);
     bool appendToCallSiteObj(Node callSiteObj);
     bool addExprAndGetNextTemplStrToken(YieldHandling yieldHandling, Node nodeList,
                                         TokenKind* ttp);
     bool checkStatementsEOF();
 
     inline Node newName(PropertyName* name);
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -219,16 +219,17 @@ class SyntaxParseHandler
     Node newCallSiteObject(uint32_t begin) {
         return NodeGeneric;
     }
 
     void addToCallSiteObject(Node callSiteObj, Node rawNode, Node cookedNode) {}
 
     Node newThisLiteral(const TokenPos& pos, Node thisName) { return NodeGeneric; }
     Node newNullLiteral(const TokenPos& pos) { return NodeGeneric; }
+    Node newRawUndefinedLiteral(const TokenPos& pos) { return NodeGeneric; }
 
     template <class Boxer>
     Node newRegExp(RegExpObject* reobj, const TokenPos& pos, Boxer& boxer) { return NodeGeneric; }
 
     Node newConditional(Node cond, Node thenExpr, Node elseExpr) { return NodeGeneric; }
 
     Node newElision() { return NodeGeneric; }
 
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -1904,66 +1904,16 @@ TokenStream::getTokenInternal(TokenKind*
     // immediately.
     userbuf.poison();
 #endif
     MOZ_MAKE_MEM_UNDEFINED(ttp, sizeof(*ttp));
     return false;
 }
 
 bool
-TokenStream::matchBracedUnicode(bool* matched, uint32_t* cp)
-{
-    int32_t c;
-    if (!peekChar(&c))
-        return false;
-    if (c != '{') {
-        *matched = false;
-        return true;
-    }
-
-    consumeKnownChar('{');
-
-    uint32_t start = userbuf.offset();
-
-    bool first = true;
-    uint32_t code = 0;
-    do {
-        int32_t c = getCharIgnoreEOL();
-        if (c == EOF) {
-            error(JSMSG_MALFORMED_ESCAPE, "Unicode");
-            return false;
-        }
-        if (c == '}') {
-            if (first) {
-                error(JSMSG_MALFORMED_ESCAPE, "Unicode");
-                return false;
-            }
-            break;
-        }
-
-        if (!JS7_ISHEX(c)) {
-            error(JSMSG_MALFORMED_ESCAPE, "Unicode");
-            return false;
-        }
-
-        code = (code << 4) | JS7_UNHEX(c);
-        if (code > unicode::NonBMPMax) {
-            errorAt(start, JSMSG_UNICODE_OVERFLOW, "escape sequence");
-            return false;
-        }
-
-        first = false;
-    } while (true);
-
-    *matched = true;
-    *cp = code;
-    return true;
-}
-
-bool
 TokenStream::getStringOrTemplateToken(int untilChar, Token** tp)
 {
     int c;
     int nc = -1;
 
     bool parsingTemplate = (untilChar == '`');
 
     *tp = newToken(-1);
@@ -1975,36 +1925,103 @@ TokenStream::getStringOrTemplateToken(in
     while ((c = getCharIgnoreEOL()) != untilChar) {
         if (c == EOF) {
             ungetCharIgnoreEOL(c);
             error(JSMSG_UNTERMINATED_STRING);
             return false;
         }
 
         if (c == '\\') {
+            // When parsing templates, we don't immediately report errors for
+            // invalid escapes; these are handled by the parser.
+            // In those cases we don't append to tokenbuf, since it won't be
+            // read.
             switch (c = getChar()) {
               case 'b': c = '\b'; break;
               case 'f': c = '\f'; break;
               case 'n': c = '\n'; break;
               case 'r': c = '\r'; break;
               case 't': c = '\t'; break;
               case 'v': c = '\v'; break;
 
               case '\n':
                 // ES5 7.8.4: an escaped line terminator represents
                 // no character.
                 continue;
 
               // Unicode character specification.
               case 'u': {
-                bool matched;
-                uint32_t code;
-                if (!matchBracedUnicode(&matched, &code))
+                uint32_t code = 0;
+
+                int32_t c2;
+                if (!peekChar(&c2))
                     return false;
-                if (matched) {
+
+                uint32_t start = userbuf.offset() - 2;
+
+                if (c2 == '{') {
+                    consumeKnownChar('{');
+
+                    bool first = true;
+                    bool valid = true;
+                    do {
+                        int32_t c = getCharIgnoreEOL();
+                        if (c == EOF) {
+                            if (parsingTemplate) {
+                                setInvalidTemplateEscape(start, InvalidEscapeType::Unicode);
+                                valid = false;
+                                break;
+                            }
+                            reportInvalidEscapeError(start, InvalidEscapeType::Unicode);
+                            return false;
+                        }
+                        if (c == '}') {
+                            if (first) {
+                                if (parsingTemplate) {
+                                    setInvalidTemplateEscape(start, InvalidEscapeType::Unicode);
+                                    valid = false;
+                                    break;
+                                }
+                                reportInvalidEscapeError(start, InvalidEscapeType::Unicode);
+                                return false;
+                            }
+                            break;
+                        }
+
+                        if (!JS7_ISHEX(c)) {
+                            if (parsingTemplate) {
+                                // We put the character back so that we read
+                                // it on the next pass, which matters if it
+                                // was '`' or '\'.
+                                ungetCharIgnoreEOL(c);
+                                setInvalidTemplateEscape(start, InvalidEscapeType::Unicode);
+                                valid = false;
+                                break;
+                            }
+                            reportInvalidEscapeError(start, InvalidEscapeType::Unicode);
+                            return false;
+                        }
+
+                        code = (code << 4) | JS7_UNHEX(c);
+                        if (code > unicode::NonBMPMax) {
+                            if (parsingTemplate) {
+                                setInvalidTemplateEscape(start + 3, InvalidEscapeType::UnicodeOverflow);
+                                valid = false;
+                                break;
+                            }
+                            reportInvalidEscapeError(start + 3, InvalidEscapeType::UnicodeOverflow);
+                            return false;
+                        }
+
+                        first = false;
+                    } while (true);
+
+                    if (!valid)
+                        continue;
+
                     MOZ_ASSERT(code <= unicode::NonBMPMax);
                     if (code < unicode::NonBMPMin) {
                         c = code;
                     } else {
                         if (!tokenbuf.append(unicode::LeadSurrogate(code)))
                             return false;
                         c = unicode::TrailSurrogate(code);
                     }
@@ -2016,48 +2033,57 @@ TokenStream::getStringOrTemplateToken(in
                     JS7_ISHEX(cp[0]) && JS7_ISHEX(cp[1]) && JS7_ISHEX(cp[2]) && JS7_ISHEX(cp[3]))
                 {
                     c = JS7_UNHEX(cp[0]);
                     c = (c << 4) + JS7_UNHEX(cp[1]);
                     c = (c << 4) + JS7_UNHEX(cp[2]);
                     c = (c << 4) + JS7_UNHEX(cp[3]);
                     skipChars(4);
                 } else {
-                    error(JSMSG_MALFORMED_ESCAPE, "Unicode");
+                    if (parsingTemplate) {
+                        setInvalidTemplateEscape(start, InvalidEscapeType::Unicode);
+                        continue;
+                    }
+                    reportInvalidEscapeError(start, InvalidEscapeType::Unicode);
                     return false;
                 }
                 break;
               }
 
               // Hexadecimal character specification.
               case 'x': {
                 char16_t cp[2];
                 if (peekChars(2, cp) && JS7_ISHEX(cp[0]) && JS7_ISHEX(cp[1])) {
                     c = (JS7_UNHEX(cp[0]) << 4) + JS7_UNHEX(cp[1]);
                     skipChars(2);
                 } else {
-                    error(JSMSG_MALFORMED_ESCAPE, "hexadecimal");
+                    uint32_t start = userbuf.offset() - 2;
+                    if (parsingTemplate) {
+                        setInvalidTemplateEscape(start, InvalidEscapeType::Hexadecimal);
+                        continue;
+                    }
+                    reportInvalidEscapeError(start, InvalidEscapeType::Hexadecimal);
                     return false;
                 }
                 break;
               }
 
               default:
                 // Octal character specification.
                 if (JS7_ISOCT(c)) {
                     int32_t val = JS7_UNOCT(c);
 
                     if (!peekChar(&c))
                         return false;
 
                     // Strict mode code allows only \0, then a non-digit.
                     if (val != 0 || JS7_ISDEC(c)) {
                         if (parsingTemplate) {
-                            error(JSMSG_DEPRECATED_OCTAL);
-                            return false;
+                            setInvalidTemplateEscape(userbuf.offset() - 2, InvalidEscapeType::Octal);
+                            continue;
                         }
                         if (!reportStrictModeError(JSMSG_DEPRECATED_OCTAL))
                             return false;
                         flags.sawOctalEscape = true;
                     }
 
                     if (JS7_ISOCT(c)) {
                         val = 8 * val + JS7_UNOCT(c);
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -75,16 +75,30 @@ struct TokenPos {
 
     bool encloses(const TokenPos& pos) const {
         return begin <= pos.begin && pos.end <= end;
     }
 };
 
 enum DecimalPoint { NoDecimal = false, HasDecimal = true };
 
+enum class InvalidEscapeType {
+    // No invalid character escapes.
+    None,
+    // A malformed \x escape.
+    Hexadecimal,
+    // A malformed \u escape.
+    Unicode,
+    // An otherwise well-formed \u escape which represents a
+    // codepoint > 10FFFF.
+    UnicodeOverflow,
+    // An octal escape in a template token.
+    Octal
+};
+
 class TokenStream;
 
 struct Token
 {
   private:
     // Sometimes the parser needs to inform the tokenizer to interpret
     // subsequent text in a particular manner: for example, to tokenize a
     // keyword as an identifier, not as the actual keyword, on the right-hand
@@ -356,16 +370,33 @@ class MOZ_STACK_CLASS TokenStream
     }
 
     // Flag methods.
     bool isEOF() const { return flags.isEOF; }
     bool sawOctalEscape() const { return flags.sawOctalEscape; }
     bool hadError() const { return flags.hadError; }
     void clearSawOctalEscape() { flags.sawOctalEscape = false; }
 
+    bool hasInvalidTemplateEscape() const {
+        return invalidTemplateEscapeType != InvalidEscapeType::None;
+    }
+    void clearInvalidTemplateEscape() {
+        invalidTemplateEscapeType = InvalidEscapeType::None;
+    }
+
+    // If there is an invalid escape in a template, report it and return false,
+    // otherwise return true.
+    bool checkForInvalidTemplateEscapeError() {
+        if (invalidTemplateEscapeType == InvalidEscapeType::None)
+            return true;
+
+        reportInvalidEscapeError(invalidTemplateEscapeOffset, invalidTemplateEscapeType);
+        return false;
+    }
+
     // TokenStream-specific error reporters.
     bool reportError(unsigned errorNumber, ...);
     bool reportErrorNoOffset(unsigned errorNumber, ...);
 
     // Report the given error at the current offset.
     void error(unsigned errorNumber, ...);
 
     // Report the given error at the given offset.
@@ -417,16 +448,43 @@ class MOZ_STACK_CLASS TokenStream
     }
 
   private:
     // These are private because they should only be called by the tokenizer
     // while tokenizing not by, for example, BytecodeEmitter.
     bool reportStrictModeError(unsigned errorNumber, ...);
     bool strictMode() const { return strictModeGetter && strictModeGetter->strictMode(); }
 
+    void setInvalidTemplateEscape(uint32_t offset, InvalidEscapeType type) {
+        MOZ_ASSERT(type != InvalidEscapeType::None);
+        if (invalidTemplateEscapeType != InvalidEscapeType::None)
+            return;
+        invalidTemplateEscapeOffset = offset;
+        invalidTemplateEscapeType = type;
+    }
+    void reportInvalidEscapeError(uint32_t offset, InvalidEscapeType type) {
+        switch (type) {
+            case InvalidEscapeType::None:
+                MOZ_ASSERT_UNREACHABLE("unexpected InvalidEscapeType");
+                return;
+            case InvalidEscapeType::Hexadecimal:
+                errorAt(offset, JSMSG_MALFORMED_ESCAPE, "hexadecimal");
+                return;
+            case InvalidEscapeType::Unicode:
+                errorAt(offset, JSMSG_MALFORMED_ESCAPE, "Unicode");
+                return;
+            case InvalidEscapeType::UnicodeOverflow:
+                errorAt(offset, JSMSG_UNICODE_OVERFLOW, "escape sequence");
+                return;
+            case InvalidEscapeType::Octal:
+                errorAt(offset, JSMSG_DEPRECATED_OCTAL);
+                return;
+        }
+    }
+
     static JSAtom* atomize(ExclusiveContext* cx, CharBuffer& cb);
     MOZ_MUST_USE bool putIdentInTokenbuf(const char16_t* identStart);
 
     struct Flags
     {
         bool isEOF:1;           // Hit end of file.
         bool isDirtyLine:1;     // Non-whitespace since start of line.
         bool sawOctalEscape:1;  // Saw an octal character escape.
@@ -437,16 +495,19 @@ class MOZ_STACK_CLASS TokenStream
         Flags()
           : isEOF(), isDirtyLine(), sawOctalEscape(), hadError(), hitOOM()
         {}
     };
 
     bool awaitIsKeyword = false;
     friend class AutoAwaitIsKeyword;
 
+    uint32_t invalidTemplateEscapeOffset = 0;
+    InvalidEscapeType invalidTemplateEscapeType = InvalidEscapeType::None;
+
   public:
     typedef Token::Modifier Modifier;
     static constexpr Modifier None = Token::None;
     static constexpr Modifier Operand = Token::Operand;
     static constexpr Modifier KeywordIsName = Token::KeywordIsName;
     static constexpr Modifier TemplateTail = Token::TemplateTail;
 
     typedef Token::ModifierException ModifierException;
@@ -950,17 +1011,16 @@ class MOZ_STACK_CLASS TokenStream
         const char16_t* base_;          // base of buffer
         uint32_t startOffset_;          // offset of base_[0]
         const char16_t* limit_;         // limit for quick bounds check
         const char16_t* ptr;            // next char to get
     };
 
     MOZ_MUST_USE bool getTokenInternal(TokenKind* ttp, Modifier modifier);
 
-    MOZ_MUST_USE bool matchBracedUnicode(bool* matched, uint32_t* code);
     MOZ_MUST_USE bool getStringOrTemplateToken(int untilChar, Token** tp);
 
     int32_t getChar();
     int32_t getCharIgnoreEOL();
     void ungetChar(int32_t c);
     void ungetCharIgnoreEOL(int32_t c);
     Token* newToken(ptrdiff_t adjust);
     uint32_t peekUnicodeEscape(uint32_t* codePoint);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/negative-zero-index.js
@@ -0,0 +1,20 @@
+function test() {
+    const array = [1];
+    for (let i = 0; i < 10; i++) {
+        assertEq(array[0], 1);
+        assertEq(array[0.0], 1);
+        assertEq(array[-0.0], 1);
+        // ToPropertyKey(-0.0) is "0", but "-0" is distinct!
+        assertEq(array["-0"], undefined);
+    }
+
+    const string = "a";
+    for (let i = 0; i < 10; i++) {
+        assertEq(string[0], "a");
+        assertEq(string[0.0], "a");
+        assertEq(string[-0.0], "a");
+        assertEq(string["-0"], undefined);
+    }
+}
+
+test();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1321597.js
@@ -0,0 +1,6 @@
+gczeal(9,3);
+function test(s, okLine) {  };
+var dbg = new Debugger;
+dbg.onNewGlobalObject = function(global) {};
+x = evalcx(test());
+shortestPaths(this, ["\$4"], 5);
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -2054,21 +2054,27 @@ TryAttachNativeInStub(JSContext* cx, Han
 {
     MOZ_ASSERT(!*attached);
 
     RootedId id(cx);
     if (!IsOptimizableElementPropertyName(cx, key, &id))
         return true;
 
     RootedPropertyName name(cx, JSID_TO_ATOM(id)->asPropertyName());
-    RootedShape shape(cx);
+    Rooted<PropertyResult> prop(cx);
     RootedObject holder(cx);
-    if (!EffectlesslyLookupProperty(cx, obj, id, &holder, &shape))
+    if (!EffectlesslyLookupProperty(cx, obj, id, &holder, &prop))
         return false;
 
+    if (prop.isNonNativeProperty()) {
+        MOZ_ASSERT(!IsCacheableProtoChain(obj, holder, false));
+        return true;
+    }
+
+    RootedShape shape(cx, prop.maybeShape());
     if (IsCacheableGetPropReadSlot(obj, holder, shape)) {
         ICStub::Kind kind = (obj == holder) ? ICStub::In_Native
                                             : ICStub::In_NativePrototype;
         JitSpew(JitSpew_BaselineIC, "  Generating In(Native %s) stub",
                     (obj == holder) ? "direct" : "prototype");
         ICInNativeCompiler compiler(cx, kind, obj, holder, name);
         ICStub* newStub = compiler.getStub(compiler.getStubSpace(outerScript));
         if (!newStub)
@@ -2577,24 +2583,27 @@ TryAttachSetValuePropStub(JSContext* cx,
                           HandleObject obj, HandleShape oldShape, HandleObjectGroup oldGroup,
                           HandlePropertyName name, HandleId id, HandleValue rhs, bool* attached)
 {
     MOZ_ASSERT(!*attached);
 
     if (obj->watched())
         return true;
 
-    RootedShape shape(cx);
+    Rooted<PropertyResult> prop(cx);
     RootedObject holder(cx);
-    if (!EffectlesslyLookupProperty(cx, obj, id, &holder, &shape))
+    if (!EffectlesslyLookupProperty(cx, obj, id, &holder, &prop))
         return false;
     if (obj != holder)
         return true;
 
-    if (!obj->isNative()) {
+    RootedShape shape(cx);
+    if (obj->isNative()) {
+        shape = prop.shape();
+    } else {
         if (obj->is<UnboxedPlainObject>()) {
             UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
             if (expando) {
                 shape = expando->lookup(cx, name);
                 if (!shape)
                     return true;
             } else {
                 return true;
@@ -2699,21 +2708,27 @@ TryAttachSetAccessorPropStub(JSContext* 
                              bool* isTemporarilyUnoptimizable)
 {
     MOZ_ASSERT(!*attached);
     MOZ_ASSERT(!*isTemporarilyUnoptimizable);
 
     if (obj->watched())
         return true;
 
-    RootedShape shape(cx);
+    Rooted<PropertyResult> prop(cx);
     RootedObject holder(cx);
-    if (!EffectlesslyLookupProperty(cx, obj, id, &holder, &shape))
+    if (!EffectlesslyLookupProperty(cx, obj, id, &holder, &prop))
         return false;
 
+    if (prop.isNonNativeProperty()) {
+        MOZ_ASSERT(!IsCacheableProtoChain(obj, holder));
+        return true;
+    }
+
+    RootedShape shape(cx, prop.maybeShape());
     bool isScripted = false;
     bool cacheableCall = IsCacheableSetPropCall(cx, obj, holder, shape,
                                                 &isScripted, isTemporarilyUnoptimizable);
 
     // Try handling scripted setters.
     if (cacheableCall && isScripted) {
         RootedFunction callee(cx, &shape->setterObject()->as<JSFunction>());
         MOZ_ASSERT(callee->hasScript());
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -262,27 +262,29 @@ CanAttachNativeGetProp(JSContext* cx, Ha
 {
     MOZ_ASSERT(JSID_IS_STRING(id) || JSID_IS_SYMBOL(id));
 
     // The lookup needs to be universally pure, otherwise we risk calling hooks out
     // of turn. We don't mind doing this even when purity isn't required, because we
     // only miss out on shape hashification, which is only a temporary perf cost.
     // The limits were arbitrarily set, anyways.
     JSObject* baseHolder = nullptr;
-    if (!LookupPropertyPure(cx, obj, id, &baseHolder, shape.address()))
+    PropertyResult prop;
+    if (!LookupPropertyPure(cx, obj, id, &baseHolder, &prop))
         return CanAttachNone;
 
     MOZ_ASSERT(!holder);
     if (baseHolder) {
         if (!baseHolder->isNative())
             return CanAttachNone;
         holder.set(&baseHolder->as<NativeObject>());
     }
+    shape.set(prop.maybeShape());
 
-    if (IsCacheableGetPropReadSlotForIonOrCacheIR(obj, holder, shape))
+    if (IsCacheableGetPropReadSlotForIonOrCacheIR(obj, holder, prop))
         return CanAttachReadSlot;
 
     // Idempotent ICs only support plain data properties, see
     // tryAttachIdempotentStub.
     if (!pc)
         return CanAttachNone;
 
     if (IsCacheableNoProperty(cx, obj, holder, shape, id, pc))
@@ -1329,17 +1331,16 @@ bool
 GetNameIRGenerator::tryAttachGlobalNameValue(ObjOperandId objId, HandleId id)
 {
     if (!IsGlobalOp(JSOp(*pc_)) || script_->hasNonSyntacticScope())
         return false;
 
     Handle<LexicalEnvironmentObject*> globalLexical = env_.as<LexicalEnvironmentObject>();
     MOZ_ASSERT(globalLexical->isGlobal());
 
-
     RootedNativeObject holder(cx_);
     RootedShape shape(cx_);
     if (!CanAttachGlobalName(cx_, globalLexical, id, &holder, &shape))
         return false;
 
     // The property must be found, and it must be found as a normal data property.
     if (!shape->hasDefaultGetter() || !shape->hasSlot())
         return false;
@@ -1357,17 +1358,18 @@ GetNameIRGenerator::tryAttachGlobalNameV
         // non-configurable, and this stub cannot be shared across globals.
         size_t dynamicSlotOffset = holder->dynamicSlotIndex(shape->slot()) * sizeof(Value);
         writer.loadDynamicSlotResult(objId, dynamicSlotOffset);
     } else {
         // Check the prototype chain from the global to the holder
         // prototype. Ignore the global lexical scope as it doesn't figure
         // into the prototype chain. We guard on the global lexical
         // scope's shape independently.
-        if (!IsCacheableGetPropReadSlotForIonOrCacheIR(&globalLexical->global(), holder, shape))
+        if (!IsCacheableGetPropReadSlotForIonOrCacheIR(&globalLexical->global(), holder,
+                                                       PropertyResult(shape)))
             return false;
 
         // Shape guard for global lexical.
         writer.guardShape(objId, globalLexical->lastProperty());
 
         // Guard on the shape of the GlobalObject.
         ObjOperandId globalId = writer.loadEnclosingEnvironment(objId);
         writer.guardShape(globalId, globalLexical->global().lastProperty());
@@ -1455,17 +1457,17 @@ GetNameIRGenerator::tryAttachEnvironment
         shape = env->as<NativeObject>().lookup(cx_, id);
         if (shape)
             break;
 
         env = env->enclosingEnvironment();
     }
 
     holder = &env->as<NativeObject>();
-    if (!IsCacheableGetPropReadSlotForIonOrCacheIR(holder, holder, shape))
+    if (!IsCacheableGetPropReadSlotForIonOrCacheIR(holder, holder, PropertyResult(shape)))
         return false;
     if (holder->getSlot(shape->slot()).isMagic())
         return false;
 
     ObjOperandId lastObjId = objId;
     env = env_;
     while (env) {
         if (env == holder) {
@@ -1493,17 +1495,18 @@ bool
 IRGenerator::maybeGuardInt32Index(const Value& index, ValOperandId indexId,
                                   uint32_t* int32Index, Int32OperandId* int32IndexId)
 {
     if (index.isNumber()) {
         int32_t indexSigned;
         if (index.isInt32()) {
             indexSigned = index.toInt32();
         } else {
-            if (!mozilla::NumberIsInt32(index.toDouble(), &indexSigned))
+            // We allow negative zero here.
+            if (!mozilla::NumberEqualsInt32(index.toDouble(), &indexSigned))
                 return false;
             if (!cx_->runtime()->jitSupportsFloatingPoint)
                 return false;
         }
 
         if (indexSigned < 0)
             return false;
 
--- a/js/src/jit/CacheIRCompiler.cpp
+++ b/js/src/jit/CacheIRCompiler.cpp
@@ -1135,18 +1135,20 @@ CacheIRCompiler::emitGuardIsInt32Index()
         masm.branchTestDouble(Assembler::NotEqual, input, failure->label());
 
         // If we're compiling a Baseline IC, FloatReg0 is always available.
         Label failurePopReg;
         if (mode_ != Mode::Baseline)
             masm.push(FloatReg0);
 
         masm.unboxDouble(input, FloatReg0);
+        // ToPropertyKey(-0.0) is "0", so we can truncate -0.0 to 0 here.
         masm.convertDoubleToInt32(FloatReg0, output,
-                                  (mode_ == Mode::Baseline) ? failure->label() : &failurePopReg);
+                                  (mode_ == Mode::Baseline) ? failure->label() : &failurePopReg,
+                                  false);
         if (mode_ != Mode::Baseline) {
             masm.pop(FloatReg0);
             masm.jump(&done);
 
             masm.bind(&failurePopReg);
             masm.pop(FloatReg0);
             masm.jump(failure->label());
         }
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -465,32 +465,33 @@ jit::IsCacheableProtoChainForIonOrCacheI
         if (!proto || !proto->isNative())
             return false;
         obj = proto;
     }
     return true;
 }
 
 bool
-jit::IsCacheableGetPropReadSlotForIonOrCacheIR(JSObject* obj, JSObject* holder, Shape* shape)
+jit::IsCacheableGetPropReadSlotForIonOrCacheIR(JSObject* obj, JSObject* holder, PropertyResult prop)
 {
-    if (!shape || !IsCacheableProtoChainForIonOrCacheIR(obj, holder))
+    if (!prop || !IsCacheableProtoChainForIonOrCacheIR(obj, holder))
         return false;
 
+    Shape* shape = prop.shape();
     if (!shape->hasSlot() || !shape->hasDefaultGetter())
         return false;
 
     return true;
 }
 
 static bool
-IsCacheableNoProperty(JSObject* obj, JSObject* holder, Shape* shape, jsbytecode* pc,
+IsCacheableNoProperty(JSObject* obj, JSObject* holder, PropertyResult prop, jsbytecode* pc,
                       const TypedOrValueRegister& output)
 {
-    if (shape)
+    if (prop)
         return false;
 
     MOZ_ASSERT(!holder);
 
     // Just because we didn't find the property on the object doesn't mean it
     // won't magically appear through various engine hacks.
     if (obj->getClass()->getGetProperty())
         return false;
@@ -758,17 +759,17 @@ CheckDOMProxyExpandoDoesNotShadow(JSCont
     // Success case: restore the tempval and proceed.
     masm.bind(&domProxyOk);
     masm.popValue(tempVal);
 }
 
 static void
 GenerateReadSlot(JSContext* cx, IonScript* ion, MacroAssembler& masm,
                  IonCache::StubAttacher& attacher, MaybeCheckTDZ checkTDZ,
-                 JSObject* obj, JSObject* holder, Shape* shape, Register object,
+                 JSObject* obj, JSObject* holder, PropertyResult prop, Register object,
                  TypedOrValueRegister output, Label* failures = nullptr)
 {
     // If there's a single jump to |failures|, we can patch the shape guard
     // jump directly. Otherwise, jump to the end of the stub, so there's a
     // common point to patch.
     bool multipleFailureJumps = (obj != holder)
                              || obj->is<UnboxedPlainObject>()
                              || (checkTDZ && output.hasValue())
@@ -785,32 +786,32 @@ GenerateReadSlot(JSContext* cx, IonScrip
     // If we need a scratch register, use either an output register or the
     // object register. After this point, we cannot jump directly to
     // |failures| since we may still have to pop the object register.
     bool restoreScratch = false;
     Register scratchReg = Register::FromCode(0); // Quell compiler warning.
 
     if (obj != holder ||
         obj->is<UnboxedPlainObject>() ||
-        !holder->as<NativeObject>().isFixedSlot(shape->slot()))
+        !holder->as<NativeObject>().isFixedSlot(prop.shape()->slot()))
     {
         if (output.hasValue()) {
             scratchReg = output.valueReg().scratchReg();
         } else if (output.type() == MIRType::Double) {
             scratchReg = object;
             masm.push(scratchReg);
             restoreScratch = true;
         } else {
             scratchReg = output.typedReg().gpr();
         }
     }
 
     // Fast path: single failure jump, no prototype guards.
     if (!multipleFailureJumps) {
-        EmitLoadSlot(masm, &holder->as<NativeObject>(), shape, object, output, scratchReg);
+        EmitLoadSlot(masm, &holder->as<NativeObject>(), prop.shape(), object, output, scratchReg);
         if (restoreScratch)
             masm.pop(scratchReg);
         attacher.jumpRejoin(masm);
         return;
     }
 
     // Slow path: multiple jumps; generate prototype guards.
     Label prototypeFailures;
@@ -855,17 +856,18 @@ GenerateReadSlot(JSContext* cx, IonScrip
         holderReg = scratchReg;
         masm.loadPtr(Address(object, UnboxedPlainObject::offsetOfExpando()), holderReg);
     } else {
         holderReg = object;
     }
 
     // Slot access.
     if (holder) {
-        EmitLoadSlot(masm, &holder->as<NativeObject>(), shape, holderReg, output, scratchReg);
+        EmitLoadSlot(masm, &holder->as<NativeObject>(), prop.shape(), holderReg, output,
+                     scratchReg);
         if (checkTDZ && output.hasValue())
             masm.branchTestMagic(Assembler::Equal, output.valueReg(), failures);
     } else {
         masm.moveValue(UndefinedValue(), output.valueReg());
     }
 
     // Restore scratch on success.
     if (restoreScratch)
@@ -1301,31 +1303,33 @@ CanAttachNativeGetProp(JSContext* cx, co
     if (!obj)
         return GetPropertyIC::CanAttachNone;
 
     // The lookup needs to be universally pure, otherwise we risk calling hooks out
     // of turn. We don't mind doing this even when purity isn't required, because we
     // only miss out on shape hashification, which is only a temporary perf cost.
     // The limits were arbitrarily set, anyways.
     JSObject* baseHolder = nullptr;
-    if (!LookupPropertyPure(cx, obj, id, &baseHolder, shape.address()))
+    PropertyResult prop;
+    if (!LookupPropertyPure(cx, obj, id, &baseHolder, &prop))
         return GetPropertyIC::CanAttachNone;
 
     MOZ_ASSERT(!holder);
     if (baseHolder) {
         if (!baseHolder->isNative())
             return GetPropertyIC::CanAttachNone;
         holder.set(&baseHolder->as<NativeObject>());
     }
+    shape.set(prop.maybeShape());
 
     RootedScript script(cx);
     jsbytecode* pc;
     cache.getScriptedLocation(&script, &pc);
-    if (IsCacheableGetPropReadSlotForIonOrCacheIR(obj, holder, shape) ||
-        IsCacheableNoProperty(obj, holder, shape, pc, cache.output()))
+    if (IsCacheableGetPropReadSlotForIonOrCacheIR(obj, holder, prop) ||
+        IsCacheableNoProperty(obj, holder, prop, pc, cache.output()))
     {
         return GetPropertyIC::CanAttachReadSlot;
     }
 
     // |length| is a non-configurable getter property on ArrayObjects. Any time this
     // check would have passed, we can install a getter stub instead. Allow people to
     // make that decision themselves with skipArrayLen
     if (!skipArrayLen && JSID_IS_ATOM(id, cx->names().length) && cache.allowArrayLength(cx) &&
@@ -1498,17 +1502,17 @@ GetPropertyIC::tryAttachNative(JSContext
 
     Label failures;
     emitIdGuard(masm, id, &failures);
     Label* maybeFailures = failures.used() ? &failures : nullptr;
 
     switch (type) {
       case CanAttachReadSlot:
         GenerateReadSlot(cx, ion, masm, attacher, DontCheckTDZ, obj, holder,
-                         shape, object(), output(), maybeFailures);
+                         PropertyResult(shape), object(), output(), maybeFailures);
         attachKind = idempotent() ? "idempotent reading"
                                     : "non idempotent reading";
         outcome = JS::TrackedOutcome::ICGetPropStub_ReadSlot;
         break;
       case CanAttachCallGetter:
         if (!GenerateCallGetter(cx, ion, masm, attacher, obj, holder, shape,
                                 liveRegs_, object(), output(), returnAddr, maybeFailures))
         {
@@ -1581,17 +1585,17 @@ GetPropertyIC::tryAttachUnboxedExpando(J
     MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
 
     Label failures;
     emitIdGuard(masm, id, &failures);
     Label* maybeFailures = failures.used() ? &failures : nullptr;
 
     StubAttacher attacher(*this);
     GenerateReadSlot(cx, ion, masm, attacher, DontCheckTDZ, obj, obj,
-                     shape, object(), output(), maybeFailures);
+                     PropertyResult(shape), object(), output(), maybeFailures);
     return linkAndAttachStub(cx, masm, attacher, ion, "read unboxed expando",
                              JS::TrackedOutcome::ICGetPropStub_UnboxedReadExpando);
 }
 
 bool
 GetPropertyIC::tryAttachUnboxedArrayLength(JSContext* cx, HandleScript outerScript, IonScript* ion,
                                            HandleObject obj, HandleId id, void* returnAddr,
                                            bool* emitted)
@@ -2914,22 +2918,24 @@ IsCacheableDOMProxyUnshadowedSetterCall(
                                         MutableHandleObject holder, MutableHandleShape shape)
 {
     MOZ_ASSERT(IsCacheableDOMProxy(obj));
 
     RootedObject checkObj(cx, obj->staticPrototype());
     if (!checkObj)
         return false;
 
-    if (!LookupPropertyPure(cx, obj, id, holder.address(), shape.address()))
+    PropertyResult prop;
+    if (!LookupPropertyPure(cx, obj, id, holder.address(), &prop))
         return false;
 
-    if (!holder)
+    if (!holder || !holder->isNative())
         return false;
 
+    shape.set(prop.shape());
     return IsCacheableSetPropCallNative(checkObj, holder, shape) ||
            IsCacheableSetPropCallPropertyOp(checkObj, holder, shape) ||
            IsCacheableSetPropCallScripted(checkObj, holder, shape);
 }
 
 bool
 SetPropertyIC::attachDOMProxyUnshadowed(JSContext* cx, HandleScript outerScript, IonScript* ion,
                                         HandleObject obj, HandleId id, void* returnAddr)
@@ -3331,32 +3337,36 @@ CanAttachNativeSetProp(JSContext* cx, Ha
     if (obj->isNative() && IsPropertySetInlineable(&obj->as<NativeObject>(), id, shape, val,
                                                    needsTypeBarrier, checkTypeset))
     {
         return SetPropertyIC::CanAttachSetSlot;
     }
 
     // If we couldn't find the property on the object itself, do a full, but
     // still pure lookup for setters.
-    if (!LookupPropertyPure(cx, obj, id, holder.address(), shape.address()))
+    Rooted<PropertyResult> prop(cx);
+    if (!LookupPropertyPure(cx, obj, id, holder.address(), prop.address()))
         return SetPropertyIC::CanAttachNone;
 
     // If the object doesn't have the property, we don't know if we can attach
     // a stub to add the property until we do the VM call to add. If the
     // property exists as a data property on the prototype, we should add
     // a new, shadowing property.
-    if (obj->isNative() && (!shape || (obj != holder && holder->isNative() &&
-                                       shape->hasDefaultSetter() && shape->hasSlot())))
+    if (obj->isNative() &&
+        (!prop || (obj != holder && holder->isNative() &&
+                   prop.shape()->hasDefaultSetter() && prop.shape()->hasSlot())))
     {
+        shape.set(prop.maybeShape());
         return SetPropertyIC::MaybeCanAttachAddSlot;
     }
 
-    if (IsImplicitNonNativeProperty(shape))
+    if (prop.isNonNativeProperty())
         return SetPropertyIC::CanAttachNone;
 
+    shape.set(prop.maybeShape());
     if (IsCacheableSetPropCallPropertyOp(obj, holder, shape) ||
         IsCacheableSetPropCallNative(obj, holder, shape) ||
         IsCacheableSetPropCallScripted(obj, holder, shape))
     {
         return SetPropertyIC::CanAttachCallSetter;
     }
 
     return SetPropertyIC::CanAttachNone;
@@ -4827,17 +4837,17 @@ BindNameIC::update(JSContext* cx, Handle
     }
 
     return holder;
 }
 
 bool
 NameIC::attachReadSlot(JSContext* cx, HandleScript outerScript, IonScript* ion,
                        HandleObject envChain, HandleObject holderBase,
-                       HandleNativeObject holder, HandleShape shape)
+                       HandleNativeObject holder, Handle<PropertyResult> prop)
 {
     MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
     Label failures;
     StubAttacher attacher(*this);
 
     Register scratchReg = outputReg().valueReg().scratchReg();
 
     // Don't guard the base of the proto chain the name was found on. It will be guarded
@@ -4845,17 +4855,17 @@ NameIC::attachReadSlot(JSContext* cx, Ha
     masm.mov(environmentChainReg(), scratchReg);
     GenerateEnvironmentChainGuards(masm, envChain, holderBase, scratchReg, &failures,
                              /* skipLastGuard = */true);
 
     // GenerateEnvironmentChain leaves the last env chain in scratchReg, even though it
     // doesn't generate the extra guard.
     //
     // NAME ops must do their own TDZ checks.
-    GenerateReadSlot(cx, ion, masm, attacher, CheckTDZ, holderBase, holder, shape, scratchReg,
+    GenerateReadSlot(cx, ion, masm, attacher, CheckTDZ, holderBase, holder, prop, scratchReg,
                      outputReg(), failures.used() ? &failures : nullptr);
 
     return linkAndAttachStub(cx, masm, attacher, ion, "generic",
                              JS::TrackedOutcome::ICNameStub_ReadSlot);
 }
 
 static bool
 IsCacheableEnvironmentChain(JSObject* envChain, JSObject* obj)
@@ -4871,36 +4881,36 @@ IsCacheableEnvironmentChain(JSObject* en
 
         obj2 = obj2->enclosingEnvironment();
     }
 
     return obj == obj2;
 }
 
 static bool
-IsCacheableNameReadSlot(HandleObject envChain, HandleObject obj,
-                        HandleObject holder, HandleShape shape, jsbytecode* pc,
+IsCacheableNameReadSlot(JSContext* cx, HandleObject envChain, HandleObject obj,
+                        HandleObject holder, Handle<PropertyResult> prop, jsbytecode* pc,
                         const TypedOrValueRegister& output)
 {
-    if (!shape)
+    if (!prop)
         return false;
     if (!obj->isNative())
         return false;
 
     if (obj->is<GlobalObject>()) {
         // Support only simple property lookups.
-        if (!IsCacheableGetPropReadSlotForIonOrCacheIR(obj, holder, shape) &&
-            !IsCacheableNoProperty(obj, holder, shape, pc, output))
+        if (!IsCacheableGetPropReadSlotForIonOrCacheIR(obj, holder, prop) &&
+            !IsCacheableNoProperty(obj, holder, prop, pc, output))
             return false;
     } else if (obj->is<ModuleEnvironmentObject>()) {
         // We don't yet support lookups in a module environment.
         return false;
     } else if (obj->is<CallObject>()) {
         MOZ_ASSERT(obj == holder);
-        if (!shape->hasDefaultGetter())
+        if (!prop.shape()->hasDefaultGetter())
             return false;
     } else {
         // We don't yet support lookups on Block or DeclEnv objects.
         return false;
     }
 
     return IsCacheableEnvironmentChain(envChain, obj);
 }
@@ -4933,26 +4943,30 @@ NameIC::attachCallGetter(JSContext* cx, 
 
     const char* attachKind = "name getter";
     return linkAndAttachStub(cx, masm, attacher, ion, attachKind,
                              JS::TrackedOutcome::ICNameStub_CallGetter);
 }
 
 static bool
 IsCacheableNameCallGetter(HandleObject envChain, HandleObject obj, HandleObject holder,
-                          HandleShape shape)
+                          Handle<PropertyResult> prop)
 {
-    if (!shape)
+    if (!prop)
         return false;
     if (!obj->is<GlobalObject>())
         return false;
 
     if (!IsCacheableEnvironmentChain(envChain, obj))
         return false;
 
+    if (!prop || !IsCacheableProtoChainForIonOrCacheIR(obj, holder))
+        return false;
+
+    Shape* shape = prop.shape();
     return IsCacheableGetPropCallNative(obj, holder, shape) ||
         IsCacheableGetPropCallPropertyOp(obj, holder, shape) ||
         IsCacheableGetPropCallScripted(obj, holder, shape);
 }
 
 bool
 NameIC::attachTypeOfNoProperty(JSContext* cx, HandleScript outerScript, IonScript* ion,
                                HandleObject envChain)
@@ -4987,20 +5001,20 @@ NameIC::attachTypeOfNoProperty(JSContext
     attacher.jumpNextStub(masm);
 
     return linkAndAttachStub(cx, masm, attacher, ion, "generic",
                              JS::TrackedOutcome::ICNameStub_TypeOfNoProperty);
 }
 
 static bool
 IsCacheableNameNoProperty(HandleObject envChain, HandleObject obj,
-                          HandleObject holder, HandleShape shape, jsbytecode* pc,
+                          HandleObject holder, Handle<PropertyResult> prop, jsbytecode* pc,
                           NameIC& cache)
 {
-    if (cache.isTypeOf() && !shape) {
+    if (cache.isTypeOf() && !prop) {
         MOZ_ASSERT(!obj);
         MOZ_ASSERT(!holder);
         MOZ_ASSERT(envChain);
 
         // Assert those extra things checked by IsCacheableNoProperty().
         MOZ_ASSERT(cache.outputReg().hasValue());
         MOZ_ASSERT(pc != nullptr);
 
@@ -5020,44 +5034,45 @@ NameIC::update(JSContext* cx, HandleScri
     RootedPropertyName name(cx, cache.name());
 
     RootedScript script(cx);
     jsbytecode* pc;
     cache.getScriptedLocation(&script, &pc);
 
     RootedObject obj(cx);
     RootedObject holder(cx);
-    RootedShape shape(cx);
-    if (!LookupName(cx, name, envChain, &obj, &holder, &shape))
+    Rooted<PropertyResult> prop(cx);
+    if (!LookupName(cx, name, envChain, &obj, &holder, &prop))
         return false;
 
     // Look first. Don't generate cache entries if the lookup fails.
     if (cache.isTypeOf()) {
-        if (!FetchName<true>(cx, obj, holder, name, shape, vp))
+        if (!FetchName<true>(cx, obj, holder, name, prop, vp))
             return false;
     } else {
-        if (!FetchName<false>(cx, obj, holder, name, shape, vp))
+        if (!FetchName<false>(cx, obj, holder, name, prop, vp))
             return false;
     }
 
     if (cache.canAttachStub()) {
-        if (IsCacheableNameReadSlot(envChain, obj, holder, shape, pc, cache.outputReg())) {
+        if (IsCacheableNameReadSlot(cx, envChain, obj, holder, prop, pc, cache.outputReg())) {
             if (!cache.attachReadSlot(cx, outerScript, ion, envChain, obj,
-                                      holder.as<NativeObject>(), shape))
+                                      holder.as<NativeObject>(), prop))
             {
                 return false;
             }
-        } else if (IsCacheableNameCallGetter(envChain, obj, holder, shape)) {
+        } else if (IsCacheableNameCallGetter(envChain, obj, holder, prop)) {
             void* returnAddr = GetReturnAddressToIonCode(cx);
+            RootedShape shape(cx, prop.shape());
             if (!cache.attachCallGetter(cx, outerScript, ion, envChain, obj, holder, shape,
                                         returnAddr))
             {
                 return false;
             }
-        } else if (IsCacheableNameNoProperty(envChain, obj, holder, shape, pc, cache)) {
+        } else if (IsCacheableNameNoProperty(envChain, obj, holder, prop, pc, cache)) {
             if (!cache.attachTypeOfNoProperty(cx, outerScript, ion, envChain))
                 return false;
         }
     }
 
     // Monitor changes to cache entry.
     TypeScript::Monitor(cx, script, pc, vp);
 
--- a/js/src/jit/IonCaches.h
+++ b/js/src/jit/IonCaches.h
@@ -802,17 +802,17 @@ class NameIC : public IonCache
         return output_;
     }
     bool isTypeOf() const {
         return typeOf_;
     }
 
     MOZ_MUST_USE bool attachReadSlot(JSContext* cx, HandleScript outerScript, IonScript* ion,
                                      HandleObject envChain, HandleObject holderBase,
-                                     HandleNativeObject holder, HandleShape shape);
+                                     HandleNativeObject holder, Handle<PropertyResult> prop);
 
     MOZ_MUST_USE bool attachCallGetter(JSContext* cx, HandleScript outerScript, IonScript* ion,
                                        HandleObject envChain, HandleObject obj,
                                        HandleObject holder, HandleShape shape,
                                        void* returnAddr);
 
     MOZ_MUST_USE bool attachTypeOfNoProperty(JSContext* cx, HandleScript outerScript,
                                              IonScript* ion, HandleObject envChain);
@@ -835,17 +835,18 @@ class NameIC : public IonCache
     {                                                                   \
         MOZ_ASSERT(is##ickind());                                       \
         return *static_cast<const ickind##IC*>(this);                  \
     }
 IONCACHE_KIND_LIST(CACHE_CASTS)
 #undef OPCODE_CASTS
 
 bool IsCacheableProtoChainForIonOrCacheIR(JSObject* obj, JSObject* holder);
-bool IsCacheableGetPropReadSlotForIonOrCacheIR(JSObject* obj, JSObject* holder, Shape* shape);
+bool IsCacheableGetPropReadSlotForIonOrCacheIR(JSObject* obj, JSObject* holder,
+                                               PropertyResult prop);
 
 bool IsCacheableGetPropCallScripted(JSObject* obj, JSObject* holder, Shape* shape,
                                     bool* isTemporarilyUnoptimizable = nullptr);
 bool IsCacheableGetPropCallNative(JSObject* obj, JSObject* holder, Shape* shape);
 
 bool ValueToNameOrSymbolId(JSContext* cx, HandleValue idval, MutableHandleId id,
                            bool* nameOrSymbol);
 
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -1982,26 +1982,26 @@ GetDOMProxyProto(JSObject* obj)
     return obj->staticPrototype();
 }
 
 // Look up a property's shape on an object, being careful never to do any effectful
 // operations.  This procedure not yielding a shape should not be taken as a lack of
 // existence of the property on the object.
 bool
 EffectlesslyLookupProperty(JSContext* cx, HandleObject obj, HandleId id,
-                           MutableHandleObject holder, MutableHandleShape shape)
+                           MutableHandleObject holder, MutableHandle<PropertyResult> prop)
 {
-    shape.set(nullptr);
+    prop.setNotFound();
     holder.set(nullptr);
 
-    if (LookupPropertyPure(cx, obj, id, holder.address(), shape.address()))
+    if (LookupPropertyPure(cx, obj, id, holder.address(), prop.address()))
         return true;
 
     holder.set(nullptr);
-    shape.set(nullptr);
+    prop.setNotFound();
     return true;
 }
 
 bool
 IsCacheableProtoChain(JSObject* obj, JSObject* holder, bool isDOMProxy)
 {
     MOZ_ASSERT_IF(isDOMProxy, IsCacheableDOMProxy(obj));
 
--- a/js/src/jit/SharedIC.h
+++ b/js/src/jit/SharedIC.h
@@ -2245,16 +2245,20 @@ IsPreliminaryObject(JSObject* obj);
 
 void
 StripPreliminaryObjectStubs(JSContext* cx, ICFallbackStub* stub);
 
 MOZ_MUST_USE bool
 EffectlesslyLookupProperty(JSContext* cx, HandleObject obj, HandleId name,
                            MutableHandleObject holder, MutableHandleShape shape);
 
+MOZ_MUST_USE bool
+EffectlesslyLookupProperty(JSContext* cx, HandleObject obj, HandleId name,
+                           MutableHandleObject holder, MutableHandle<PropertyResult> prop);
+
 JSObject*
 GetDOMProxyProto(JSObject* obj);
 
 bool
 IsCacheableProtoChain(JSObject* obj, JSObject* holder, bool isDOMProxy=false);
 
 bool
 IsCacheableGetPropReadSlot(JSObject* obj, JSObject* holder, Shape* shape, bool isDOMProxy=false);
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -595,21 +595,21 @@ GetDynamicName(JSContext* cx, JSObject* 
         }
     }
 
     if (!frontend::IsIdentifier(atom) || frontend::IsKeyword(atom)) {
         vp->setUndefined();
         return;
     }
 
-    Shape* shape = nullptr;
+    PropertyResult prop;
     JSObject* scope = nullptr;
     JSObject* pobj = nullptr;
-    if (LookupNameNoGC(cx, atom->asPropertyName(), envChain, &scope, &pobj, &shape)) {
-        if (FetchNameNoGC(pobj, shape, MutableHandleValue::fromMarkedLocation(vp)))
+    if (LookupNameNoGC(cx, atom->asPropertyName(), envChain, &scope, &pobj, &prop)) {
+        if (FetchNameNoGC(pobj, prop, MutableHandleValue::fromMarkedLocation(vp)))
             return;
     }
 
     vp->setUndefined();
 }
 
 void
 PostWriteBarrier(JSRuntime* rt, JSObject* obj)
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -2901,19 +2901,19 @@ JS_AlreadyHasOwnPropertyById(JSContext* 
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, id);
 
     if (!obj->isNative())
         return js::HasOwnProperty(cx, obj, id, foundp);
 
     RootedNativeObject nativeObj(cx, &obj->as<NativeObject>());
-    RootedShape prop(cx);
+    Rooted<PropertyResult> prop(cx);
     NativeLookupOwnPropertyNoResolve(cx, nativeObj, id, &prop);
-    *foundp = !!prop;
+    *foundp = prop.isFound();
     return true;
 }
 
 JS_PUBLIC_API(bool)
 JS_AlreadyHasOwnProperty(JSContext* cx, HandleObject obj, const char* name, bool* foundp)
 {
     JSAtom* atom = Atomize(cx, name, strlen(name));
     if (!atom)
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -16,16 +16,17 @@
 #include "jsobj.h"
 #include "jsprf.h"
 #include "jswatchpoint.h"
 #include "jsweakmap.h"
 #include "jswrapper.h"
 
 #include "builtin/Promise.h"
 #include "builtin/TestingFunctions.h"
+#include "gc/GCInternals.h"
 #include "js/Proxy.h"
 #include "proxy/DeadObjectProxy.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/Time.h"
 #include "vm/WrapperObject.h"
 
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
@@ -1158,18 +1159,24 @@ DumpHeapTracer::onChild(const JS::GCCell
 
 void
 js::DumpHeap(JSContext* cx, FILE* fp, js::DumpHeapNurseryBehaviour nurseryBehaviour)
 {
     if (nurseryBehaviour == js::CollectNurseryBeforeDump)
         cx->gc.evictNursery(JS::gcreason::API);
 
     DumpHeapTracer dtrc(fp, cx);
+
     fprintf(dtrc.output, "# Roots.\n");
-    TraceRuntime(&dtrc);
+    {
+        JSRuntime* rt = cx->runtime();
+        js::gc::AutoPrepareForTracing prep(cx, WithAtoms);
+        gcstats::AutoPhase ap(rt->gc.stats, gcstats::PHASE_TRACE_HEAP);
+        rt->gc.traceRuntime(&dtrc, prep.session().lock);
+    }
 
     fprintf(dtrc.output, "# Weak maps.\n");
     WeakMapBase::traceAllMappings(&dtrc);
 
     fprintf(dtrc.output, "==========\n");
 
     dtrc.prefix = "> ";
     IterateZonesCompartmentsArenasCells(cx, &dtrc,
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -379,18 +379,19 @@ class GrayObjectIter : public ZoneCellIt
 
 class GCZonesIter
 {
   private:
     ZonesIter zone;
 
   public:
     explicit GCZonesIter(JSRuntime* rt, ZoneSelector selector = WithAtoms) : zone(rt, selector) {
-        MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt) && rt->isHeapBusy());
-        if (!zone->isCollecting())
+        MOZ_ASSERT((CurrentThreadCanAccessRuntime(rt) && rt->isHeapBusy()) ||
+                   CurrentThreadIsPerformingGC());
+        if (!zone->isCollectingFromAnyThread())
             next();
     }
 
     bool done() const { return zone.done(); }
 
     void next() {
         MOZ_ASSERT(!done());
         do {
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -2107,51 +2107,51 @@ JSObject::constructHook() const
         if (p.handler()->isConstructor(const_cast<JSObject*>(this)))
             return js::proxy_Construct;
     }
     return nullptr;
 }
 
 bool
 js::LookupProperty(JSContext* cx, HandleObject obj, js::HandleId id,
-                   MutableHandleObject objp, MutableHandleShape propp)
+                   MutableHandleObject objp, MutableHandle<PropertyResult> propp)
 {
     /* NB: The logic of lookupProperty is implicitly reflected in
      *     BaselineIC.cpp's |EffectlesslyLookupProperty| logic.
      *     If this changes, please remember to update the logic there as well.
      */
     if (LookupPropertyOp op = obj->getOpsLookupProperty())
         return op(cx, obj, id, objp, propp);
     return LookupPropertyInline<CanGC>(cx, obj.as<NativeObject>(), id, objp, propp);
 }
 
 bool
 js::LookupName(JSContext* cx, HandlePropertyName name, HandleObject envChain,
-               MutableHandleObject objp, MutableHandleObject pobjp, MutableHandleShape propp)
+               MutableHandleObject objp, MutableHandleObject pobjp, MutableHandle<PropertyResult> propp)
 {
     RootedId id(cx, NameToId(name));
 
     for (RootedObject env(cx, envChain); env; env = env->enclosingEnvironment()) {
         if (!LookupProperty(cx, env, id, pobjp, propp))
             return false;
         if (propp) {
             objp.set(env);
             return true;
         }
     }
 
     objp.set(nullptr);
     pobjp.set(nullptr);
-    propp.set(nullptr);
+    propp.setNotFound();
     return true;
 }
 
 bool
 js::LookupNameNoGC(JSContext* cx, PropertyName* name, JSObject* envChain,
-                   JSObject** objp, JSObject** pobjp, Shape** propp)
+                   JSObject** objp, JSObject** pobjp, PropertyResult* propp)
 {
     AutoAssertNoException nogc(cx);
 
     MOZ_ASSERT(!*objp && !*pobjp && !*propp);
 
     for (JSObject* env = envChain; env; env = env->enclosingEnvironment()) {
         if (env->getOpsLookupProperty())
             return false;
@@ -2168,69 +2168,69 @@ js::LookupNameNoGC(JSContext* cx, Proper
 
 bool
 js::LookupNameWithGlobalDefault(JSContext* cx, HandlePropertyName name, HandleObject envChain,
                                 MutableHandleObject objp)
 {
     RootedId id(cx, NameToId(name));
 
     RootedObject pobj(cx);
-    RootedShape shape(cx);
+    Rooted<PropertyResult> prop(cx);
 
     RootedObject env(cx, envChain);
     for (; !env->is<GlobalObject>(); env = env->enclosingEnvironment()) {
-        if (!LookupProperty(cx, env, id, &pobj, &shape))
+        if (!LookupProperty(cx, env, id, &pobj, &prop))
             return false;
-        if (shape)
+        if (prop)
             break;
     }
 
     objp.set(env);
     return true;
 }
 
 bool
 js::LookupNameUnqualified(JSContext* cx, HandlePropertyName name, HandleObject envChain,
                           MutableHandleObject objp)
 {
     RootedId id(cx, NameToId(name));
 
     RootedObject pobj(cx);
-    RootedShape shape(cx);
+    Rooted<PropertyResult> prop(cx);
 
     RootedObject env(cx, envChain);
     for (; !env->isUnqualifiedVarObj(); env = env->enclosingEnvironment()) {
-        if (!LookupProperty(cx, env, id, &pobj, &shape))
+        if (!LookupProperty(cx, env, id, &pobj, &prop))
             return false;
-        if (shape)
+        if (prop)
             break;
     }
 
     // See note above RuntimeLexicalErrorObject.
     if (pobj == env) {
         bool isTDZ = false;
-        if (shape && name != cx->names().dotThis) {
+        if (prop && name != cx->names().dotThis) {
             // Treat Debugger environments specially for TDZ checks, as they
             // look like non-native environments but in fact wrap native
             // environments.
             if (env->is<DebugEnvironmentProxy>()) {
                 RootedValue v(cx);
                 if (!env->as<DebugEnvironmentProxy>().getMaybeSentinelValue(cx, id, &v))
                     return false;
                 isTDZ = IsUninitializedLexical(v);
             } else {
-                isTDZ = IsUninitializedLexicalSlot(env, shape);
+                isTDZ = IsUninitializedLexicalSlot(env, prop);
             }
         }
 
         if (isTDZ) {
             env = RuntimeLexicalErrorObject::create(cx, env, JSMSG_UNINITIALIZED_LEXICAL);
             if (!env)
                 return false;
-        } else if (env->is<LexicalEnvironmentObject>() && !shape->writable()) {
+        } else if (env->is<LexicalEnvironmentObject>() && !prop.shape()->writable()) {
             // Assigning to a named lambda callee name is a no-op in sloppy mode.
             Rooted<LexicalEnvironmentObject*> lexicalEnv(cx, &env->as<LexicalEnvironmentObject>());
             if (lexicalEnv->isExtensible() ||
                 lexicalEnv->scope().kind() != ScopeKind::NamedLambda)
             {
                 MOZ_ASSERT(name != cx->names().dotThis);
                 env = RuntimeLexicalErrorObject::create(cx, env, JSMSG_BAD_CONST_ASSIGN);
                 if (!env)
@@ -2252,26 +2252,26 @@ js::HasOwnProperty(JSContext* cx, Handle
     if (GetOwnPropertyOp op = obj->getOpsGetOwnPropertyDescriptor()) {
         Rooted<PropertyDescriptor> desc(cx);
         if (!op(cx, obj, id, &desc))
             return false;
         *result = !!desc.object();
         return true;
     }
 
-    RootedShape shape(cx);
-    if (!NativeLookupOwnProperty<CanGC>(cx, obj.as<NativeObject>(), id, &shape))
+    Rooted<PropertyResult> prop(cx);
+    if (!NativeLookupOwnProperty<CanGC>(cx, obj.as<NativeObject>(), id, &prop))
         return false;
-    *result = (shape != nullptr);
+    *result = prop.isFound();
     return true;
 }
 
 bool
 js::LookupPropertyPure(ExclusiveContext* cx, JSObject* obj, jsid id, JSObject** objp,
-                       Shape** propp)
+                       PropertyResult* propp)
 {
     bool isTypedArrayOutOfRange = false;
     do {
         if (!LookupOwnPropertyPure(cx, obj, id, propp, &isTypedArrayOutOfRange))
             return false;
 
         if (*propp) {
             *objp = obj;
@@ -2282,95 +2282,96 @@ js::LookupPropertyPure(ExclusiveContext*
             *objp = nullptr;
             return true;
         }
 
         obj = obj->staticPrototype();
     } while (obj);
 
     *objp = nullptr;
-    *propp = nullptr;
+    propp->setNotFound();
     return true;
 }
 
 bool
-js::LookupOwnPropertyPure(ExclusiveContext* cx, JSObject* obj, jsid id, Shape** propp,
+js::LookupOwnPropertyPure(ExclusiveContext* cx, JSObject* obj, jsid id, PropertyResult* propp,
                           bool* isTypedArrayOutOfRange /* = nullptr */)
 {
     JS::AutoCheckCannotGC nogc;
     if (isTypedArrayOutOfRange)
         *isTypedArrayOutOfRange = false;
 
     if (obj->isNative()) {
         // Search for a native dense element, typed array element, or property.
 
         if (JSID_IS_INT(id) && obj->as<NativeObject>().containsDenseElement(JSID_TO_INT(id))) {
-            MarkDenseOrTypedArrayElementFound<NoGC>(propp);
+            propp->setDenseOrTypedArrayElement();
             return true;
         }
 
         if (obj->is<TypedArrayObject>()) {
             uint64_t index;
             if (IsTypedArrayIndex(id, &index)) {
                 if (index < obj->as<TypedArrayObject>().length()) {
-                    MarkDenseOrTypedArrayElementFound<NoGC>(propp);
+                    propp->setDenseOrTypedArrayElement();
                 } else {
-                    *propp = nullptr;
+                    propp->setNotFound();
                     if (isTypedArrayOutOfRange)
                         *isTypedArrayOutOfRange = true;
                 }
                 return true;
             }
         }
 
         if (Shape* shape = obj->as<NativeObject>().lookupPure(id)) {
-            *propp = shape;
+            propp->setNativeProperty(shape);
             return true;
         }
 
         // Fail if there's a resolve hook, unless the mayResolve hook tells
         // us the resolve hook won't define a property with this id.
         if (ClassMayResolveId(cx->names(), obj->getClass(), id, obj))
             return false;
     } else if (obj->is<UnboxedPlainObject>()) {
         if (obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id)) {
-            MarkNonNativePropertyFound<NoGC>(propp);
+            propp->setNonNativeProperty();
             return true;
         }
     } else if (obj->is<UnboxedArrayObject>()) {
         if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
-            MarkNonNativePropertyFound<NoGC>(propp);
+            propp->setNonNativeProperty();
             return true;
         }
     } else if (obj->is<TypedObject>()) {
         if (obj->as<TypedObject>().typeDescr().hasProperty(cx->names(), id)) {
-            MarkNonNativePropertyFound<NoGC>(propp);
+            propp->setNonNativeProperty();
             return true;
         }
     } else {
         return false;
     }
 
-    *propp = nullptr;
+    propp->setNotFound();
     return true;
 }
 
 static inline bool
-NativeGetPureInline(NativeObject* pobj, jsid id, Shape* shape, Value* vp)
+NativeGetPureInline(NativeObject* pobj, jsid id, PropertyResult prop, Value* vp)
 {
-    if (IsImplicitDenseOrTypedArrayElement(shape)) {
+    if (prop.isDenseOrTypedArrayElement()) {
         // For simplicity we ignore the TypedArray with string index case.
         if (!JSID_IS_INT(id))
             return false;
 
         *vp = pobj->getDenseOrTypedArrayElement(JSID_TO_INT(id));
         return true;
     }
 
     // Fail if we have a custom getter.
+    Shape* shape = prop.shape();
     if (!shape->hasDefaultGetter())
         return false;
 
     if (shape->hasSlot()) {
         *vp = pobj->getSlot(shape->slot());
         MOZ_ASSERT(!vp->isMagic());
     } else {
         vp->setUndefined();
@@ -2378,109 +2379,110 @@ NativeGetPureInline(NativeObject* pobj, 
 
     return true;
 }
 
 bool
 js::GetPropertyPure(ExclusiveContext* cx, JSObject* obj, jsid id, Value* vp)
 {
     JSObject* pobj;
-    Shape* shape;
-    if (!LookupPropertyPure(cx, obj, id, &pobj, &shape))
+    PropertyResult prop;
+    if (!LookupPropertyPure(cx, obj, id, &pobj, &prop))
         return false;
 
-    if (!shape) {
+    if (!prop) {
         vp->setUndefined();
         return true;
     }
 
-    return pobj->isNative() && NativeGetPureInline(&pobj->as<NativeObject>(), id, shape, vp);
+    return pobj->isNative() && NativeGetPureInline(&pobj->as<NativeObject>(), id, prop, vp);
 }
 
 static inline bool
-NativeGetGetterPureInline(Shape* shape, JSFunction** fp)
+NativeGetGetterPureInline(PropertyResult prop, JSFunction** fp)
 {
-    if (!IsImplicitDenseOrTypedArrayElement(shape) && shape->hasGetterObject()) {
+    if (!prop.isDenseOrTypedArrayElement() && prop.shape()->hasGetterObject()) {
+        Shape* shape = prop.shape();
         if (shape->getterObject()->is<JSFunction>()) {
             *fp = &shape->getterObject()->as<JSFunction>();
             return true;
         }
     }
 
     *fp = nullptr;
     return true;
 }
 
 bool
 js::GetGetterPure(ExclusiveContext* cx, JSObject* obj, jsid id, JSFunction** fp)
 {
     /* Just like GetPropertyPure, but get getter function, without invoking
      * it. */
     JSObject* pobj;
-    Shape* shape;
-    if (!LookupPropertyPure(cx, obj, id, &pobj, &shape))
+    PropertyResult prop;
+    if (!LookupPropertyPure(cx, obj, id, &pobj, &prop))
         return false;
 
-    if (!shape) {
+    if (!prop) {
         *fp = nullptr;
         return true;
     }
 
-    return pobj->isNative() && NativeGetGetterPureInline(shape, fp);
+    return prop.isNativeProperty() && NativeGetGetterPureInline(prop, fp);
 }
 
 bool
 js::GetOwnGetterPure(ExclusiveContext* cx, JSObject* obj, jsid id, JSFunction** fp)
 {
     JS::AutoCheckCannotGC nogc;
-    Shape* shape;
-    if (!LookupOwnPropertyPure(cx, obj, id, &shape))
+    PropertyResult prop;
+    if (!LookupOwnPropertyPure(cx, obj, id, &prop))
         return false;
 
-    if (!shape) {
+    if (!prop) {
         *fp = nullptr;
         return true;
     }
 
-    return NativeGetGetterPureInline(shape, fp);
+    return prop.isNativeProperty() && NativeGetGetterPureInline(prop, fp);
 }
 
 bool
 js::GetOwnNativeGetterPure(JSContext* cx, JSObject* obj, jsid id, JSNative* native)
 {
     JS::AutoCheckCannotGC nogc;
     *native = nullptr;
-    Shape* shape;
-    if (!LookupOwnPropertyPure(cx, obj, id, &shape))
+    PropertyResult prop;
+    if (!LookupOwnPropertyPure(cx, obj, id, &prop))
         return false;
 
-    if (!shape || IsImplicitDenseOrTypedArrayElement(shape) || !shape->hasGetterObject())
+    if (!prop || prop.isDenseOrTypedArrayElement() || !prop.shape()->hasGetterObject())
         return true;
 
-    JSObject* getterObj = shape->getterObject();
+    JSObject* getterObj = prop.shape()->getterObject();
     if (!getterObj->is<JSFunction>())
         return true;
 
     JSFunction* getter = &getterObj->as<JSFunction>();
     if (!getter->isNative())
         return true;
 
     *native = getter->native();
     return true;
 }
 
 bool
 js::HasOwnDataPropertyPure(JSContext* cx, JSObject* obj, jsid id, bool* result)
 {
-    Shape* shape = nullptr;
-    if (!LookupOwnPropertyPure(cx, obj, id, &shape))
+    PropertyResult prop;
+    if (!LookupOwnPropertyPure(cx, obj, id, &prop))
         return false;
 
-    *result = shape && !IsImplicitDenseOrTypedArrayElement(shape) && shape->hasDefaultGetter() &&
-              shape->hasSlot();
+    *result = prop && !prop.isDenseOrTypedArrayElement() && prop.shape()->hasDefaultGetter() &&
+              prop.shape()->hasSlot();
     return true;
 }
 
 bool
 JSObject::reportReadOnly(JSContext* cx, jsid id, unsigned report)
 {
     RootedValue val(cx, IdToValue(id));
     return ReportValueErrorFlags(cx, report, JSMSG_READ_ONLY,
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -999,21 +999,21 @@ GetPropertyDescriptor(JSContext* cx, Han
 
 /*
  * Deprecated. A version of HasProperty that also returns the object on which
  * the property was found (but that information is unreliable for proxies), and
  * the Shape of the property, if native.
  */
 extern bool
 LookupProperty(JSContext* cx, HandleObject obj, HandleId id,
-               MutableHandleObject objp, MutableHandleShape propp);
+               MutableHandleObject objp, MutableHandle<PropertyResult> propp);
 
 inline bool
 LookupProperty(JSContext* cx, HandleObject obj, PropertyName* name,
-               MutableHandleObject objp, MutableHandleShape propp)
+               MutableHandleObject objp, MutableHandle<PropertyResult> propp)
 {
     RootedId id(cx, NameToId(name));
     return LookupProperty(cx, obj, id, objp, propp);
 }
 
 /* Set *result to tell whether obj has an own property with the given id. */
 extern bool
 HasOwnProperty(JSContext* cx, HandleObject obj, HandleId id, bool* result);
@@ -1210,21 +1210,21 @@ CompletePropertyDescriptor(MutableHandle
  */
 extern bool
 ReadPropertyDescriptors(JSContext* cx, HandleObject props, bool checkAccessors,
                         AutoIdVector* ids, MutableHandle<PropertyDescriptorVector> descs);
 
 /* Read the name using a dynamic lookup on the scopeChain. */
 extern bool
 LookupName(JSContext* cx, HandlePropertyName name, HandleObject scopeChain,
-           MutableHandleObject objp, MutableHandleObject pobjp, MutableHandleShape propp);
+           MutableHandleObject objp, MutableHandleObject pobjp, MutableHandle<PropertyResult> propp);
 
 extern bool
 LookupNameNoGC(JSContext* cx, PropertyName* name, JSObject* scopeChain,
-               JSObject** objp, JSObject** pobjp, Shape** propp);
+               JSObject** objp, JSObject** pobjp, PropertyResult* propp);
 
 /*
  * Like LookupName except returns the global object if 'name' is not found in
  * any preceding scope.
  *
  * Additionally, pobjp and propp are not needed by callers so they are not
  * returned.
  */
@@ -1248,20 +1248,20 @@ LookupNameUnqualified(JSContext* cx, Han
 
 namespace js {
 
 extern JSObject*
 FindVariableScope(JSContext* cx, JSFunction** funp);
 
 bool
 LookupPropertyPure(ExclusiveContext* cx, JSObject* obj, jsid id, JSObject** objp,
-                   Shape** propp);
+                   PropertyResult* propp);
 
 bool
-LookupOwnPropertyPure(ExclusiveContext* cx, JSObject* obj, jsid id, Shape** propp,
+LookupOwnPropertyPure(ExclusiveContext* cx, JSObject* obj, jsid id, PropertyResult* propp,
                       bool* isTypedArrayOutOfRange = nullptr);
 
 bool
 GetPropertyPure(ExclusiveContext* cx, JSObject* obj, jsid id, Value* vp);
 
 bool
 GetGetterPure(ExclusiveContext* cx, JSObject* obj, jsid id, JSFunction** fp);
 
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -588,21 +588,21 @@ HasNativeMethodPure(JSObject* obj, Prope
 }
 
 // Return whether 'obj' definitely has no @@toPrimitive method.
 static MOZ_ALWAYS_INLINE bool
 HasNoToPrimitiveMethodPure(JSObject* obj, JSContext* cx)
 {
     jsid id = SYMBOL_TO_JSID(cx->wellKnownSymbols().toPrimitive);
     JSObject* pobj;
-    Shape* shape;
-    if (!LookupPropertyPure(cx, obj, id, &pobj, &shape))
+    PropertyResult prop;
+    if (!LookupPropertyPure(cx, obj, id, &pobj, &prop))
         return false;
 
-    return !shape;
+    return !prop;
 }
 
 /* ES6 draft rev 28 (2014 Oct 14) 7.1.14 */
 inline bool
 ToPropertyKey(JSContext* cx, HandleValue argument, MutableHandleId result)
 {
     // Steps 1-2.
     RootedValue key(cx, argument);
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -387,19 +387,19 @@ JO(JSContext* cx, HandleObject obj, Stri
          * values which process to |undefined|, and 4) stringifying all values
          * which pass the filter.
          */
         id = propertyList[i];
         RootedValue outputValue(cx);
 #ifdef DEBUG
         if (scx->maybeSafely) {
             RootedNativeObject nativeObj(cx, &obj->as<NativeObject>());
-            RootedShape prop(cx);
+            Rooted<PropertyResult> prop(cx);
             NativeLookupOwnPropertyNoResolve(cx, nativeObj, id, &prop);
-            MOZ_ASSERT(prop && prop->isDataDescriptor());
+            MOZ_ASSERT(prop && prop.isNativeProperty() && prop.shape()->isDataDescriptor());
         }
 #endif // DEBUG
         if (!GetProperty(cx, obj, obj, id, &outputValue))
             return false;
         if (!PreprocessValue(cx, obj, HandleId(id), &outputValue, scx))
             return false;
         if (IsFilteredValue(outputValue))
             continue;
--- a/js/src/proxy/Proxy.cpp
+++ b/js/src/proxy/Proxy.cpp
@@ -561,28 +561,28 @@ Proxy::getElements(JSContext* cx, Handle
 Proxy::trace(JSTracer* trc, JSObject* proxy)
 {
     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
     handler->trace(trc, proxy);
 }
 
 static bool
 proxy_LookupProperty(JSContext* cx, HandleObject obj, HandleId id,
-                     MutableHandleObject objp, MutableHandleShape propp)
+                     MutableHandleObject objp, MutableHandle<JS::PropertyResult> propp)
 {
     bool found;
     if (!Proxy::has(cx, obj, id, &found))
         return false;
 
     if (found) {
-        MarkNonNativePropertyFound<CanGC>(propp);
+        propp.setNonNativeProperty();
         objp.set(obj);
     } else {
+        propp.setNotFound();
         objp.set(nullptr);
-        propp.set(nullptr);
     }
     return true;
 }
 
 static bool
 proxy_DeleteProperty(JSContext* cx, HandleObject obj, HandleId id, ObjectOpResult& result)
 {
     if (!Proxy::delete_(cx, obj, id, result))
--- a/js/src/tests/ecma_6/TemplateStrings/tagTempl.js
+++ b/js/src/tests/ecma_6/TemplateStrings/tagTempl.js
@@ -282,10 +282,182 @@ assertEq(func`hey``there``amine`, "was n
 assertEq(func`hey``tshere``amine`, "was not there");
 assertEq(func`heys``there``mine`, "was not hey");
 
 // String.raw
 assertEq(String.raw`h\r\ney${4}there\n`, "h\\r\\ney4there\\n");
 assertEq(String.raw`hey`, "hey");
 assertEq(String.raw``, "");
 
+// Invalid escape sequences
+check(raw`\01`, ["\\01"]);
+check(raw`\01${0}right`, ["\\01","right"]);
+check(raw`left${0}\01`, ["left","\\01"]);
+check(raw`left${0}\01${1}right`, ["left","\\01","right"]);
+check(raw`\1`, ["\\1"]);
+check(raw`\1${0}right`, ["\\1","right"]);
+check(raw`left${0}\1`, ["left","\\1"]);
+check(raw`left${0}\1${1}right`, ["left","\\1","right"]);
+check(raw`\xg`, ["\\xg"]);
+check(raw`\xg${0}right`, ["\\xg","right"]);
+check(raw`left${0}\xg`, ["left","\\xg"]);
+check(raw`left${0}\xg${1}right`, ["left","\\xg","right"]);
+check(raw`\xAg`, ["\\xAg"]);
+check(raw`\xAg${0}right`, ["\\xAg","right"]);
+check(raw`left${0}\xAg`, ["left","\\xAg"]);
+check(raw`left${0}\xAg${1}right`, ["left","\\xAg","right"]);
+check(raw`\u0`, ["\\u0"]);
+check(raw`\u0${0}right`, ["\\u0","right"]);
+check(raw`left${0}\u0`, ["left","\\u0"]);
+check(raw`left${0}\u0${1}right`, ["left","\\u0","right"]);
+check(raw`\u0g`, ["\\u0g"]);
+check(raw`\u0g${0}right`, ["\\u0g","right"]);
+check(raw`left${0}\u0g`, ["left","\\u0g"]);
+check(raw`left${0}\u0g${1}right`, ["left","\\u0g","right"]);
+check(raw`\u00g`, ["\\u00g"]);
+check(raw`\u00g${0}right`, ["\\u00g","right"]);
+check(raw`left${0}\u00g`, ["left","\\u00g"]);
+check(raw`left${0}\u00g${1}right`, ["left","\\u00g","right"]);
+check(raw`\u000g`, ["\\u000g"]);
+check(raw`\u000g${0}right`, ["\\u000g","right"]);
+check(raw`left${0}\u000g`, ["left","\\u000g"]);
+check(raw`left${0}\u000g${1}right`, ["left","\\u000g","right"]);
+check(raw`\u{}`, ["\\u{}"]);
+check(raw`\u{}${0}right`, ["\\u{}","right"]);
+check(raw`left${0}\u{}`, ["left","\\u{}"]);
+check(raw`left${0}\u{}${1}right`, ["left","\\u{}","right"]);
+check(raw`\u{-0}`, ["\\u{-0}"]);
+check(raw`\u{-0}${0}right`, ["\\u{-0}","right"]);
+check(raw`left${0}\u{-0}`, ["left","\\u{-0}"]);
+check(raw`left${0}\u{-0}${1}right`, ["left","\\u{-0}","right"]);
+check(raw`\u{g}`, ["\\u{g}"]);
+check(raw`\u{g}${0}right`, ["\\u{g}","right"]);
+check(raw`left${0}\u{g}`, ["left","\\u{g}"]);
+check(raw`left${0}\u{g}${1}right`, ["left","\\u{g}","right"]);
+check(raw`\u{0`, ["\\u{0"]);
+check(raw`\u{0${0}right`, ["\\u{0","right"]);
+check(raw`left${0}\u{0`, ["left","\\u{0"]);
+check(raw`left${0}\u{0${1}right`, ["left","\\u{0","right"]);
+check(raw`\u{\u{0}`, ["\\u{\\u{0}"]);
+check(raw`\u{\u{0}${0}right`, ["\\u{\\u{0}","right"]);
+check(raw`left${0}\u{\u{0}`, ["left","\\u{\\u{0}"]);
+check(raw`left${0}\u{\u{0}${1}right`, ["left","\\u{\\u{0}","right"]);
+check(raw`\u{110000}`, ["\\u{110000}"]);
+check(raw`\u{110000}${0}right`, ["\\u{110000}","right"]);
+check(raw`left${0}\u{110000}`, ["left","\\u{110000}"]);
+check(raw`left${0}\u{110000}${1}right`, ["left","\\u{110000}","right"]);
+
+check(cooked`\01`, [void 0]);
+check(cooked`\01${0}right`, [void 0,"right"]);
+check(cooked`left${0}\01`, ["left",void 0]);
+check(cooked`left${0}\01${1}right`, ["left",void 0,"right"]);
+check(cooked`\1`, [void 0]);
+check(cooked`\1${0}right`, [void 0,"right"]);
+check(cooked`left${0}\1`, ["left",void 0]);
+check(cooked`left${0}\1${1}right`, ["left",void 0,"right"]);
+check(cooked`\xg`, [void 0]);
+check(cooked`\xg${0}right`, [void 0,"right"]);
+check(cooked`left${0}\xg`, ["left",void 0]);
+check(cooked`left${0}\xg${1}right`, ["left",void 0,"right"]);
+check(cooked`\xAg`, [void 0]);
+check(cooked`\xAg${0}right`, [void 0,"right"]);
+check(cooked`left${0}\xAg`, ["left",void 0]);
+check(cooked`left${0}\xAg${1}right`, ["left",void 0,"right"]);
+check(cooked`\u0`, [void 0]);
+check(cooked`\u0${0}right`, [void 0,"right"]);
+check(cooked`left${0}\u0`, ["left",void 0]);
+check(cooked`left${0}\u0${1}right`, ["left",void 0,"right"]);
+check(cooked`\u0g`, [void 0]);
+check(cooked`\u0g${0}right`, [void 0,"right"]);
+check(cooked`left${0}\u0g`, ["left",void 0]);
+check(cooked`left${0}\u0g${1}right`, ["left",void 0,"right"]);
+check(cooked`\u00g`, [void 0]);
+check(cooked`\u00g${0}right`, [void 0,"right"]);
+check(cooked`left${0}\u00g`, ["left",void 0]);
+check(cooked`left${0}\u00g${1}right`, ["left",void 0,"right"]);
+check(cooked`\u000g`, [void 0]);
+check(cooked`\u000g${0}right`, [void 0,"right"]);
+check(cooked`left${0}\u000g`, ["left",void 0]);
+check(cooked`left${0}\u000g${1}right`, ["left",void 0,"right"]);
+check(cooked`\u{}`, [void 0]);
+check(cooked`\u{}${0}right`, [void 0,"right"]);
+check(cooked`left${0}\u{}`, ["left",void 0]);
+check(cooked`left${0}\u{}${1}right`, ["left",void 0,"right"]);
+check(cooked`\u{-0}`, [void 0]);
+check(cooked`\u{-0}${0}right`, [void 0,"right"]);
+check(cooked`left${0}\u{-0}`, ["left",void 0]);
+check(cooked`left${0}\u{-0}${1}right`, ["left",void 0,"right"]);
+check(cooked`\u{g}`, [void 0]);
+check(cooked`\u{g}${0}right`, [void 0,"right"]);
+check(cooked`left${0}\u{g}`, ["left",void 0]);
+check(cooked`left${0}\u{g}${1}right`, ["left",void 0,"right"]);
+check(cooked`\u{0`, [void 0]);
+check(cooked`\u{0${0}right`, [void 0,"right"]);
+check(cooked`left${0}\u{0`, ["left",void 0]);
+check(cooked`left${0}\u{0${1}right`, ["left",void 0,"right"]);
+check(cooked`\u{\u{0}`, [void 0]);
+check(cooked`\u{\u{0}${0}right`, [void 0,"right"]);
+check(cooked`left${0}\u{\u{0}`, ["left",void 0]);
+check(cooked`left${0}\u{\u{0}${1}right`, ["left",void 0,"right"]);
+check(cooked`\u{110000}`, [void 0]);
+check(cooked`\u{110000}${0}right`, [void 0,"right"]);
+check(cooked`left${0}\u{110000}`, ["left",void 0]);
+check(cooked`left${0}\u{110000}${1}right`, ["left",void 0,"right"]);
+
+syntaxError("`\\01`");
+syntaxError("`\\01${0}right`");
+syntaxError("`left${0}\\01`");
+syntaxError("`left${0}\\01${1}right`");
+syntaxError("`\\1`");
+syntaxError("`\\1${0}right`");
+syntaxError("`left${0}\\1`");
+syntaxError("`left${0}\\1${1}right`");
+syntaxError("`\\xg`");
+syntaxError("`\\xg${0}right`");
+syntaxError("`left${0}\\xg`");
+syntaxError("`left${0}\\xg${1}right`");
+syntaxError("`\\xAg`");
+syntaxError("`\\xAg${0}right`");
+syntaxError("`left${0}\\xAg`");
+syntaxError("`left${0}\\xAg${1}right`");
+syntaxError("`\\u0`");
+syntaxError("`\\u0${0}right`");
+syntaxError("`left${0}\\u0`");
+syntaxError("`left${0}\\u0${1}right`");
+syntaxError("`\\u0g`");
+syntaxError("`\\u0g${0}right`");
+syntaxError("`left${0}\\u0g`");
+syntaxError("`left${0}\\u0g${1}right`");
+syntaxError("`\\u00g`");
+syntaxError("`\\u00g${0}right`");
+syntaxError("`left${0}\\u00g`");
+syntaxError("`left${0}\\u00g${1}right`");
+syntaxError("`\\u000g`");
+syntaxError("`\\u000g${0}right`");
+syntaxError("`left${0}\\u000g`");
+syntaxError("`left${0}\\u000g${1}right`");
+syntaxError("`\\u{}`");
+syntaxError("`\\u{}${0}right`");
+syntaxError("`left${0}\\u{}`");
+syntaxError("`left${0}\\u{}${1}right`");
+syntaxError("`\\u{-0}`");
+syntaxError("`\\u{-0}${0}right`");
+syntaxError("`left${0}\\u{-0}`");
+syntaxError("`left${0}\\u{-0}${1}right`");
+syntaxError("`\\u{g}`");
+syntaxError("`\\u{g}${0}right`");
+syntaxError("`left${0}\\u{g}`");
+syntaxError("`left${0}\\u{g}${1}right`");
+syntaxError("`\\u{0`");
+syntaxError("`\\u{0${0}right`");
+syntaxError("`left${0}\\u{0`");
+syntaxError("`left${0}\\u{0${1}right`");
+syntaxError("`\\u{\\u{0}`");
+syntaxError("`\\u{\\u{0}${0}right`");
+syntaxError("`left${0}\\u{\\u{0}`");
+syntaxError("`left${0}\\u{\\u{0}${1}right`");
+syntaxError("`\\u{110000}`");
+syntaxError("`\\u{110000}${0}right`");
+syntaxError("`left${0}\\u{110000}`");
+syntaxError("`left${0}\\u{110000}${1}right`");
+
 
 reportCompare(0, 0, "ok");
--- a/js/src/tests/js1_8_5/extensions/sharedtypedarray.js
+++ b/js/src/tests/js1_8_5/extensions/sharedtypedarray.js
@@ -171,27 +171,29 @@ function testSharedTypedArrayMethods() {
     assertEq(v[8], -4);
     assertEq(v[9], -5);
 }
 
 function testClone1() {
     var sab1 = b;
     var blob = serialize(sab1, []);
     var sab2 = deserialize(blob);
-    assertEq(sharedAddress(sab1), sharedAddress(sab2));
+    if (typeof sharedAddress != "undefined")
+	assertEq(sharedAddress(sab1), sharedAddress(sab2));
 }
 
 function testClone2() {
     var sab = b;
     var ia1 = new Int32Array(sab);
     var blob = serialize(ia1, []);
     var ia2 = deserialize(blob);
     assertEq(ia1.length, ia2.length);
     assertEq(ia1.buffer instanceof SharedArrayBuffer, true);
-    assertEq(sharedAddress(ia1.buffer), sharedAddress(ia2.buffer));
+    if (typeof sharedAddress != "undefined")
+	assertEq(sharedAddress(ia1.buffer), sharedAddress(ia2.buffer));
     ia1[10] = 37;
     assertEq(ia2[10], 37);
 }
 
 // Serializing a SharedArrayBuffer should fail if we've set its flag to 'deny' or if
 // the flag is bogus.
 
 function testNoClone() {
@@ -207,17 +209,18 @@ function testNoClone() {
 
 // Eventually, this will be prohibited, but for now, allow the SAB to
 // appear in the transfer list.  See bug 1302036 and bug 1302037.
 
 function testRedundantTransfer() {
     var sab1 = b;
     var blob = serialize(sab1, [sab1]);
     var sab2 = deserialize(blob);
-    assertEq(sharedAddress(sab1), sharedAddress(sab2));
+    if (typeof sharedAddress != "undefined")
+	assertEq(sharedAddress(sab1), sharedAddress(sab2));
 }
 
 function testApplicable() {
     var sab = b;
     var x;
 
     // Just make sure we can create all the view types on shared memory.
 
--- a/js/src/tests/js1_8_5/reflect-parse/templateStrings.js
+++ b/js/src/tests/js1_8_5/reflect-parse/templateStrings.js
@@ -2,16 +2,18 @@
 function test() {
 
 // template strings
 assertStringExpr("`hey there`", literal("hey there"));
 assertStringExpr("`hey\nthere`", literal("hey\nthere"));
 assertExpr("`hey${\"there\"}`", templateLit([lit("hey"), lit("there"), lit("")]));
 assertExpr("`hey${\"there\"}mine`", templateLit([lit("hey"), lit("there"), lit("mine")]));
 assertExpr("`hey${a == 5}mine`", templateLit([lit("hey"), binExpr("==", ident("a"), lit(5)), lit("mine")]));
+assertExpr("func`hey\\x`", taggedTemplate(ident("func"), template(["hey\\x"], [void 0])));
+assertExpr("func`hey${4}\\x`", taggedTemplate(ident("func"), template(["hey","\\x"], ["hey",void 0], lit(4))));
 assertExpr("`hey${`there${\"how\"}`}mine`", templateLit([lit("hey"),
            templateLit([lit("there"), lit("how"), lit("")]), lit("mine")]));
 assertExpr("func`hey`", taggedTemplate(ident("func"), template(["hey"], ["hey"])));
 assertExpr("func`hey${\"4\"}there`", taggedTemplate(ident("func"),
            template(["hey", "there"], ["hey", "there"], lit("4"))));
 assertExpr("func`hey${\"4\"}there${5}`", taggedTemplate(ident("func"),
            template(["hey", "there", ""], ["hey", "there", ""],
                   lit("4"), lit(5))));
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -1032,16 +1032,23 @@ ArrayBufferObject::setFlags(uint32_t fla
 ArrayBufferObject*
 ArrayBufferObject::create(JSContext* cx, uint32_t nbytes, BufferContents contents,
                           OwnsState ownsState /* = OwnsData */,
                           HandleObject proto /* = nullptr */,
                           NewObjectKind newKind /* = GenericObject */)
 {
     MOZ_ASSERT_IF(contents.kind() == MAPPED, contents);
 
+    // 24.1.1.1, step 3 (Inlined 6.2.6.1 CreateByteDataBlock, step 2).
+    // Refuse to allocate too large buffers, currently limited to ~2 GiB.
+    if (nbytes > INT32_MAX) {
+        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
+        return nullptr;
+    }
+
     // If we need to allocate data, try to use a larger object size class so
     // that the array buffer's data can be allocated inline with the object.
     // The extra space will be left unused by the object's fixed slots and
     // available for the buffer's data, see NewObject().
     size_t reservedSlots = JSCLASS_RESERVED_SLOTS(&class_);
 
     size_t nslots = reservedSlots;
     bool allocated = false;
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -10336,22 +10336,24 @@ DebuggerObject::forceLexicalInitializati
     }
 
     MOZ_ASSERT(object->isGlobal());
 
     Rooted<GlobalObject*> referent(cx, &object->referent()->as<GlobalObject>());
 
     RootedObject globalLexical(cx, &referent->lexicalEnvironment());
     RootedObject pobj(cx);
-    RootedShape shape(cx);
-    if (!LookupProperty(cx, globalLexical, id, &pobj, &shape))
+    Rooted<PropertyResult> prop(cx);
+    if (!LookupProperty(cx, globalLexical, id, &pobj, &prop))
         return false;
 
     result = false;
-    if (shape) {
+    if (prop) {
+        MOZ_ASSERT(prop.isNativeProperty());
+        Shape* shape = prop.shape();
         Value v = globalLexical->as<NativeObject>().getSlot(shape->slot());
         if (shape->hasSlot() && v.isMagic() && v.whyMagic() == JS_UNINITIALIZED_LEXICAL) {
             globalLexical->as<NativeObject>().setSlot(shape->slot(), UndefinedValue());
             result = true;
         }
     }
 
     return true;
--- a/js/src/vm/EnvironmentObject.cpp
+++ b/js/src/vm/EnvironmentObject.cpp
@@ -512,24 +512,24 @@ ModuleEnvironmentObject::lookupImport(js
 void
 ModuleEnvironmentObject::fixEnclosingEnvironmentAfterCompartmentMerge(GlobalObject& global)
 {
     setEnclosingEnvironment(&global.lexicalEnvironment());
 }
 
 /* static */ bool
 ModuleEnvironmentObject::lookupProperty(JSContext* cx, HandleObject obj, HandleId id,
-                                        MutableHandleObject objp, MutableHandleShape propp)
+                                        MutableHandleObject objp, MutableHandle<PropertyResult> propp)
 {
     const IndirectBindingMap& bindings = obj->as<ModuleEnvironmentObject>().importBindings();
     Shape* shape;
     ModuleEnvironmentObject* env;
     if (bindings.lookup(id, &env, &shape)) {
         objp.set(env);
-        propp.set(shape);
+        propp.setNativeProperty(shape);
         return true;
     }
 
     RootedNativeObject target(cx, &obj->as<NativeObject>());
     if (!NativeLookupOwnProperty<CanGC>(cx, target, id, propp))
         return false;
 
     objp.set(obj);
@@ -714,37 +714,37 @@ CheckUnscopables(JSContext *cx, HandleOb
     } else {
         *scopable = true;
     }
     return true;
 }
 
 static bool
 with_LookupProperty(JSContext* cx, HandleObject obj, HandleId id,
-                    MutableHandleObject objp, MutableHandleShape propp)
+                    MutableHandleObject objp, MutableHandle<PropertyResult> propp)
 {
     // SpiderMonkey-specific: consider internal '.generator' and '.this' names
     // to be unscopable.
     if (IsUnscopableDotName(cx, id)) {
         objp.set(nullptr);
-        propp.set(nullptr);
+        propp.setNotFound();
         return true;
     }
 
     RootedObject actual(cx, &obj->as<WithEnvironmentObject>().object());
     if (!LookupProperty(cx, actual, id, objp, propp))
         return false;
 
     if (propp) {
         bool scopable;
         if (!CheckUnscopables(cx, actual, id, &scopable))
             return false;
         if (!scopable) {
             objp.set(nullptr);
-            propp.set(nullptr);
+            propp.setNotFound();
         }
     }
     return true;
 }
 
 static bool
 with_DefineProperty(JSContext* cx, HandleObject obj, HandleId id, Handle<PropertyDescriptor> desc,
                     ObjectOpResult& result)
@@ -1130,17 +1130,17 @@ ReportRuntimeLexicalErrorId(JSContext* c
         ReportRuntimeLexicalError(cx, errorNumber, name);
         return;
     }
     MOZ_CRASH("RuntimeLexicalErrorObject should only be used with property names");
 }
 
 static bool
 lexicalError_LookupProperty(JSContext* cx, HandleObject obj, HandleId id,
-                            MutableHandleObject objp, MutableHandleShape propp)
+                            MutableHandleObject objp, MutableHandle<PropertyResult> propp)
 {
     ReportRuntimeLexicalErrorId(cx, obj->as<RuntimeLexicalErrorObject>().errorNumber(), id);
     return false;
 }
 
 static bool
 lexicalError_HasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp)
 {
--- a/js/src/vm/EnvironmentObject.h
+++ b/js/src/vm/EnvironmentObject.h
@@ -403,17 +403,17 @@ class ModuleEnvironmentObject : public E
     bool hasImportBinding(HandlePropertyName name);
 
     bool lookupImport(jsid name, ModuleEnvironmentObject** envOut, Shape** shapeOut);
 
     void fixEnclosingEnvironmentAfterCompartmentMerge(GlobalObject& global);
 
   private:
     static bool lookupProperty(JSContext* cx, HandleObject obj, HandleId id,
-                               MutableHandleObject objp, MutableHandleShape propp);
+                               MutableHandleObject objp, MutableHandle<PropertyResult> propp);
     static bool hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp);
     static bool getProperty(JSContext* cx, HandleObject obj, HandleValue receiver, HandleId id,
                             MutableHandleValue vp);
     static bool setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
                             HandleValue receiver, JS::ObjectOpResult& result);
     static bool getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
                                          MutableHandle<PropertyDescriptor> desc);
     static bool deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
--- a/js/src/vm/GeneratorObject.cpp
+++ b/js/src/vm/GeneratorObject.cpp
@@ -354,18 +354,20 @@ js::CheckStarGeneratorResumptionValue(JS
     Value doneVal;
     if (!GetPropertyPure(cx, obj, NameToId(cx->names().done), &doneVal))
         return false;
     if (!doneVal.isBoolean())
         return false;
 
     // It should have `value` data property, but the type doesn't matter
     JSObject* ignored;
-    Shape* shape;
-    if (!LookupPropertyPure(cx, obj, NameToId(cx->names().value), &ignored, &shape))
+    PropertyResult prop;
+    if (!LookupPropertyPure(cx, obj, NameToId(cx->names().value), &ignored, &prop))
         return false;
-    if (!shape)
+    if (!prop)
         return false;
-    if (!shape->hasDefaultGetter())
+    if (!prop.isNativeProperty())
+        return false;
+    if (!prop.shape()->hasDefaultGetter())
         return false;
 
     return true;
 }
--- a/js/src/vm/Interpreter-inl.h
+++ b/js/src/vm/Interpreter-inl.h
@@ -74,27 +74,28 @@ GuardFunApplyArgumentsOptimization(JSCon
 static inline bool
 IsUninitializedLexical(const Value& val)
 {
     // Use whyMagic here because JS_OPTIMIZED_ARGUMENTS could flow into here.
     return val.isMagic() && val.whyMagic() == JS_UNINITIALIZED_LEXICAL;
 }
 
 static inline bool
-IsUninitializedLexicalSlot(HandleObject obj, HandleShape shape)
+IsUninitializedLexicalSlot(HandleObject obj, Handle<PropertyResult> prop)
 {
-    MOZ_ASSERT(shape);
+    MOZ_ASSERT(prop);
     if (obj->is<WithEnvironmentObject>())
         return false;
-    // We check for IsImplicitDenseOrTypedArrayElement even though the shape
-    // is always a non-indexed property because proxy hooks may return a
-    // "non-native property found" shape, which happens to be encoded in the
-    // same way as the "dense element" shape. See MarkNonNativePropertyFound.
-    if (IsImplicitDenseOrTypedArrayElement(shape) ||
-        !shape->hasSlot() ||
+
+    // Proxy hooks may return a non-native property.
+    if (prop.isNonNativeProperty())
+        return false;
+
+    Shape* shape = prop.shape();
+    if (!shape->hasSlot() ||
         !shape->hasDefaultGetter() ||
         !shape->hasDefaultSetter())
     {
         return false;
     }
     MOZ_ASSERT(obj->as<NativeObject>().containsPure(shape));
     return IsUninitializedLexical(obj->as<NativeObject>().getSlot(shape->slot()));
 }
@@ -170,32 +171,33 @@ GetLengthProperty(const Value& lval, Mut
         }
     }
 
     return false;
 }
 
 template <bool TypeOf> inline bool
 FetchName(JSContext* cx, HandleObject obj, HandleObject obj2, HandlePropertyName name,
-          HandleShape shape, MutableHandleValue vp)
+          Handle<PropertyResult> prop, MutableHandleValue vp)
 {
-    if (!shape) {
+    if (!prop) {
         if (TypeOf) {
             vp.setUndefined();
             return true;
         }
         return ReportIsNotDefined(cx, name);
     }
 
     /* Take the slow path if shape was not found in a native object. */
     if (!obj->isNative() || !obj2->isNative()) {
         Rooted<jsid> id(cx, NameToId(name));
         if (!GetProperty(cx, obj, obj, id, vp))
             return false;
     } else {
+        RootedShape shape(cx, prop.shape());
         RootedObject normalized(cx, obj);
         if (normalized->is<WithEnvironmentObject>() && !shape->hasDefaultGetter())
             normalized = &normalized->as<WithEnvironmentObject>().object();
         if (shape->isDataDescriptor() && shape->hasDefaultGetter()) {
             /* Fast path for Object instance properties. */
             MOZ_ASSERT(shape->hasSlot());
             vp.set(obj2->as<NativeObject>().getSlot(shape->slot()));
         } else {
@@ -209,19 +211,23 @@ FetchName(JSContext* cx, HandleObject ob
         return true;
 
     // NAME operations are the slow paths already, so unconditionally check
     // for uninitialized lets.
     return CheckUninitializedLexical(cx, name, vp);
 }
 
 inline bool
-FetchNameNoGC(JSObject* pobj, Shape* shape, MutableHandleValue vp)
+FetchNameNoGC(JSObject* pobj, PropertyResult prop, MutableHandleValue vp)
 {
-    if (!shape || !pobj->isNative() || !shape->isDataDescriptor() || !shape->hasDefaultGetter())
+    if (!prop || !pobj->isNative())
+        return false;
+
+    Shape* shape = prop.shape();
+    if (!shape->isDataDescriptor() || !shape->hasDefaultGetter())
         return false;
 
     vp.set(pobj->as<NativeObject>().getSlot(shape->slot()));
     return !IsUninitializedLexical(vp);
 }
 
 inline bool
 GetIntrinsicOperation(JSContext* cx, jsbytecode* pc, MutableHandleValue vp)
@@ -357,17 +363,17 @@ DefVarOperation(JSContext* cx, HandleObj
     // have already been checked.
     if (JS_HasExtensibleLexicalEnvironment(varobj)) {
         Rooted<LexicalEnvironmentObject*> lexicalEnv(cx);
         lexicalEnv = &JS_ExtensibleLexicalEnvironment(varobj)->as<LexicalEnvironmentObject>();
         MOZ_ASSERT(CheckVarNameConflict(cx, lexicalEnv, dn));
     }
 #endif
 
-    RootedShape prop(cx);
+    Rooted<PropertyResult> prop(cx);
     RootedObject obj2(cx);
     if (!LookupProperty(cx, varobj, dn, &obj2, &prop))
         return false;
 
     /* Steps 8c, 8d. */
     if (!prop || (obj2 != varobj && varobj->is<GlobalObject>())) {
         if (!DefineProperty(cx, varobj, dn, UndefinedHandleValue, nullptr, nullptr, attrs))
             return false;
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -205,50 +205,50 @@ GetNameOperation(JSContext* cx, Interpre
      * in the emitter: type inference will assume that GNAME opcodes are
      * accessing the global object, and the inferred behavior should match
      * the actual behavior even if the id could be found on the env chain
      * before the global object.
      */
     if (IsGlobalOp(JSOp(*pc)) && !fp->script()->hasNonSyntacticScope())
         obj = &obj->global().lexicalEnvironment();
 
-    Shape* shape = nullptr;
+    PropertyResult prop;
     JSObject* env = nullptr;
     JSObject* pobj = nullptr;
-    if (LookupNameNoGC(cx, name, obj, &env, &pobj, &shape)) {
-        if (FetchNameNoGC(pobj, shape, vp))
+    if (LookupNameNoGC(cx, name, obj, &env, &pobj, &prop)) {
+        if (FetchNameNoGC(pobj, prop, vp))
             return true;
     }
 
     RootedObject objRoot(cx, obj), envRoot(cx), pobjRoot(cx);
     RootedPropertyName nameRoot(cx, name);
-    RootedShape shapeRoot(cx);
-
-    if (!LookupName(cx, nameRoot, objRoot, &envRoot, &pobjRoot, &shapeRoot))
+    Rooted<PropertyResult> propRoot(cx);
+
+    if (!LookupName(cx, nameRoot, objRoot, &envRoot, &pobjRoot, &propRoot))
         return false;
 
     /* Kludge to allow (typeof foo == "undefined") tests. */
     JSOp op2 = JSOp(pc[JSOP_GETNAME_LENGTH]);
     if (op2 == JSOP_TYPEOF)
-        return FetchName<true>(cx, envRoot, pobjRoot, nameRoot, shapeRoot, vp);
-
-    return FetchName<false>(cx, envRoot, pobjRoot, nameRoot, shapeRoot, vp);
+        return FetchName<true>(cx, envRoot, pobjRoot, nameRoot, propRoot, vp);
+
+    return FetchName<false>(cx, envRoot, pobjRoot, nameRoot, propRoot, vp);
 }
 
 static inline bool
 GetImportOperation(JSContext* cx, InterpreterFrame* fp, jsbytecode* pc, MutableHandleValue vp)
 {
     RootedObject obj(cx, fp->environmentChain()), env(cx), pobj(cx);
     RootedPropertyName name(cx, fp->script()->getName(pc));
-    RootedShape shape(cx);
-
-    MOZ_ALWAYS_TRUE(LookupName(cx, name, obj, &env, &pobj, &shape));
+    Rooted<PropertyResult> prop(cx);
+
+    MOZ_ALWAYS_TRUE(LookupName(cx, name, obj, &env, &pobj, &prop));
     MOZ_ASSERT(env && env->is<ModuleEnvironmentObject>());
     MOZ_ASSERT(env->as<ModuleEnvironmentObject>().hasImportBinding(name));
-    return FetchName<false>(cx, env, pobj, name, shape, vp);
+    return FetchName<false>(cx, env, pobj, name, prop, vp);
 }
 
 static bool
 SetPropertyOperation(JSContext* cx, JSOp op, HandleValue lval, HandleId id, HandleValue rval)
 {
     MOZ_ASSERT(op == JSOP_SETPROP || op == JSOP_STRICTSETPROP);
 
     RootedObject obj(cx, ToObjectFromStack(cx, lval));
@@ -4309,22 +4309,22 @@ js::GetProperty(JSContext* cx, HandleVal
 
     return GetProperty(cx, obj, receiver, name, vp);
 }
 
 bool
 js::GetEnvironmentName(JSContext* cx, HandleObject envChain, HandlePropertyName name,
                        MutableHandleValue vp)
 {
-    RootedShape shape(cx);
+    Rooted<PropertyResult> prop(cx);
     RootedObject obj(cx), pobj(cx);
-    if (!LookupName(cx, name, envChain, &obj, &pobj, &shape))
+    if (!LookupName(cx, name, envChain, &obj, &pobj, &prop))
         return false;
 
-    if (!shape)
+    if (!prop)
         return ReportIsNotDefined(cx, name);
 
     if (!GetProperty(cx, obj, obj, name, vp))
         return false;
 
     // We do our own explicit checking for |this|
     if (name == cx->names().dotThis)
         return true;
@@ -4336,22 +4336,22 @@ js::GetEnvironmentName(JSContext* cx, Ha
 /*
  * Alternate form for NAME opcodes followed immediately by a TYPEOF,
  * which do not report an exception on (typeof foo == "undefined") tests.
  */
 bool
 js::GetEnvironmentNameForTypeOf(JSContext* cx, HandleObject envChain, HandlePropertyName name,
                                 MutableHandleValue vp)
 {
-    RootedShape shape(cx);
+    Rooted<PropertyResult> prop(cx);
     RootedObject obj(cx), pobj(cx);
-    if (!LookupName(cx, name, envChain, &obj, &pobj, &shape))
+    if (!LookupName(cx, name, envChain, &obj, &pobj, &prop))
         return false;
 
-    if (!shape) {
+    if (!prop) {
         vp.set(UndefinedValue());
         return true;
     }
 
     if (!GetProperty(cx, obj, obj, name, vp))
         return false;
 
     // See note in FetchName.
@@ -4399,33 +4399,33 @@ js::DefFunOperation(JSContext* cx, Handl
      */
     RootedObject parent(cx, envChain);
     while (!parent->isQualifiedVarObj())
         parent = parent->enclosingEnvironment();
 
     /* ES5 10.5 (NB: with subsequent errata). */
     RootedPropertyName name(cx, fun->explicitName()->asPropertyName());
 
-    RootedShape shape(cx);
+    Rooted<PropertyResult> prop(cx);
     RootedObject pobj(cx);
-    if (!LookupProperty(cx, parent, name, &pobj, &shape))
+    if (!LookupProperty(cx, parent, name, &pobj, &prop))
         return false;
 
     RootedValue rval(cx, ObjectValue(*fun));
 
     /*
      * ECMA requires functions defined when entering Eval code to be
      * impermanent.
      */
     unsigned attrs = script->isActiveEval()
                      ? JSPROP_ENUMERATE
                      : JSPROP_ENUMERATE | JSPROP_PERMANENT;
 
     /* Steps 5d, 5f. */
-    if (!shape || pobj != parent) {
+    if (!prop || pobj != parent) {
         if (!DefineProperty(cx, parent, name, rval, nullptr, nullptr, attrs))
             return false;
 
         return parent->is<GlobalObject>() ? parent->compartment()->addToVarNames(cx, name) : true;
     }
 
     /*
      * Step 5e.
@@ -4433,16 +4433,17 @@ js::DefFunOperation(JSContext* cx, Handl
      * A DebugEnvironmentProxy is okay here, and sometimes necessary. If
      * Debugger.Frame.prototype.eval defines a function with the same name as an
      * extant variable in the frame, the DebugEnvironmentProxy takes care of storing
      * the function in the stack frame (for non-aliased variables) or on the
      * scope object (for aliased).
      */
     MOZ_ASSERT(parent->isNative() || parent->is<DebugEnvironmentProxy>());
     if (parent->is<GlobalObject>()) {
+        Shape* shape = prop.shape();
         if (shape->configurable()) {
             if (!DefineProperty(cx, parent, name, rval, nullptr, nullptr, attrs))
                 return false;
         } else {
             MOZ_ASSERT(shape->isDataDescriptor());
             MOZ_ASSERT(shape->writable());
             MOZ_ASSERT(shape->enumerable());
         }
@@ -4637,18 +4638,18 @@ js::AtomicIsLockFree(JSContext* cx, Hand
     return true;
 }
 
 bool
 js::DeleteNameOperation(JSContext* cx, HandlePropertyName name, HandleObject scopeObj,
                         MutableHandleValue res)
 {
     RootedObject scope(cx), pobj(cx);
-    RootedShape shape(cx);
-    if (!LookupName(cx, name, scopeObj, &scope, &pobj, &shape))
+    Rooted<PropertyResult> prop(cx);
+    if (!LookupName(cx, name, scopeObj, &scope, &pobj, &prop))
         return false;
 
     if (!scope) {
         // Return true for non-existent names.
         res.setBoolean(true);
         return true;
     }
 
--- a/js/src/vm/NativeObject-inl.h
+++ b/js/src/vm/NativeObject-inl.h
@@ -378,18 +378,18 @@ NewNativeObjectWithClassProto(ExclusiveC
  *
  *   - If the resolve hook finds or defines the sought property, set propp
  *      appropriately, set *recursedp = false, and return true.
  *
  *   - Otherwise no property was resolved. Set propp to nullptr and
  *     *recursedp = false and return true.
  */
 static MOZ_ALWAYS_INLINE bool
-CallResolveOp(JSContext* cx, HandleNativeObject obj, HandleId id, MutableHandleShape propp,
-              bool* recursedp)
+CallResolveOp(JSContext* cx, HandleNativeObject obj, HandleId id,
+              MutableHandle<PropertyResult> propp, bool* recursedp)
 {
     // Avoid recursion on (obj, id) already being resolved on cx.
     AutoResolving resolving(cx, obj, id);
     if (resolving.alreadyStarted()) {
         // Already resolving id in obj, suppress recursion.
         *recursedp = true;
         return true;
     }
@@ -403,23 +403,28 @@ CallResolveOp(JSContext* cx, HandleNativ
         return true;
 
     // Assert the mayResolve hook, if there is one, returns true for this
     // property.
     MOZ_ASSERT_IF(obj->getClass()->getMayResolve(),
                   obj->getClass()->getMayResolve()(cx->names(), id, obj));
 
     if (JSID_IS_INT(id) && obj->containsDenseElement(JSID_TO_INT(id))) {
-        MarkDenseOrTypedArrayElementFound<CanGC>(propp);
+        propp.setDenseOrTypedArrayElement();
         return true;
     }
 
     MOZ_ASSERT(!obj->is<TypedArrayObject>());
 
-    propp.set(obj->lookup(cx, id));
+    RootedShape shape(cx, obj->lookup(cx, id));
+    if (shape)
+        propp.setNativeProperty(shape);
+    else
+        propp.setNotFound();
+
     return true;
 }
 
 static MOZ_ALWAYS_INLINE bool
 ClassMayResolveId(const JSAtomState& names, const Class* clasp, jsid id, JSObject* maybeObj)
 {
     MOZ_ASSERT_IF(maybeObj, maybeObj->getClass() == clasp);
 
@@ -440,118 +445,120 @@ ClassMayResolveId(const JSAtomState& nam
     return true;
 }
 
 template <AllowGC allowGC>
 static MOZ_ALWAYS_INLINE bool
 LookupOwnPropertyInline(ExclusiveContext* cx,
                         typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
                         typename MaybeRooted<jsid, allowGC>::HandleType id,
-                        typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp,
+                        typename MaybeRooted<PropertyResult, allowGC>::MutableHandleType propp,
                         bool* donep)
 {
     // Check for a native dense element.
     if (JSID_IS_INT(id) && obj->containsDenseElement(JSID_TO_INT(id))) {
-        MarkDenseOrTypedArrayElementFound<allowGC>(propp);
+        propp.setDenseOrTypedArrayElement();
         *donep = true;
         return true;
     }
 
     // Check for a typed array element. Integer lookups always finish here
     // so that integer properties on the prototype are ignored even for out
     // of bounds accesses.
     if (obj->template is<TypedArrayObject>()) {
         uint64_t index;
         if (IsTypedArrayIndex(id, &index)) {
-            if (index < obj->template as<TypedArrayObject>().length()) {
-                MarkDenseOrTypedArrayElementFound<allowGC>(propp);
-            } else {
-                propp.set(nullptr);
-            }
+            if (index < obj->template as<TypedArrayObject>().length())
+                propp.setDenseOrTypedArrayElement();
+            else
+                propp.setNotFound();
             *donep = true;
             return true;
         }
     }
 
     // Check for a native property.
     if (Shape* shape = obj->lookup(cx, id)) {
-        propp.set(shape);
+        propp.setNativeProperty(shape);
         *donep = true;
         return true;
     }
 
     // id was not found in obj. Try obj's resolve hook, if any.
     if (obj->getClass()->getResolve()) {
         if (!cx->shouldBeJSContext() || !allowGC)
             return false;
 
         bool recursed;
         if (!CallResolveOp(cx->asJSContext(),
                            MaybeRooted<NativeObject*, allowGC>::toHandle(obj),
                            MaybeRooted<jsid, allowGC>::toHandle(id),
-                           MaybeRooted<Shape*, allowGC>::toMutableHandle(propp),
+                           MaybeRooted<PropertyResult, allowGC>::toMutableHandle(propp),
                            &recursed))
         {
             return false;
         }
 
         if (recursed) {
-            propp.set(nullptr);
+            propp.setNotFound();
             *donep = true;
             return true;
         }
 
         if (propp) {
             *donep = true;
             return true;
         }
     }
 
-    propp.set(nullptr);
+    propp.setNotFound();
     *donep = false;
     return true;
 }
 
 /*
  * Simplified version of LookupOwnPropertyInline that doesn't call resolve
  * hooks.
  */
 static inline void
 NativeLookupOwnPropertyNoResolve(ExclusiveContext* cx, HandleNativeObject obj, HandleId id,
-                                 MutableHandleShape result)
+                                 MutableHandle<PropertyResult> result)
 {
     // Check for a native dense element.
     if (JSID_IS_INT(id) && obj->containsDenseElement(JSID_TO_INT(id))) {
-        MarkDenseOrTypedArrayElementFound<CanGC>(result);
+        result.setDenseOrTypedArrayElement();
         return;
     }
 
     // Check for a typed array element.
     if (obj->is<TypedArrayObject>()) {
         uint64_t index;
         if (IsTypedArrayIndex(id, &index)) {
             if (index < obj->as<TypedArrayObject>().length())
-                MarkDenseOrTypedArrayElementFound<CanGC>(result);
+                result.setDenseOrTypedArrayElement();
             else
-                result.set(nullptr);
+                result.setNotFound();
             return;
         }
     }
 
     // Check for a native property.
-    result.set(obj->lookup(cx, id));
+    if (Shape* shape = obj->lookup(cx, id))
+        result.setNativeProperty(shape);
+    else
+        result.setNotFound();
 }
 
 template <AllowGC allowGC>
 static MOZ_ALWAYS_INLINE bool
 LookupPropertyInline(ExclusiveContext* cx,
                      typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
                      typename MaybeRooted<jsid, allowGC>::HandleType id,
                      typename MaybeRooted<JSObject*, allowGC>::MutableHandleType objp,
-                     typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp)
+                     typename MaybeRooted<PropertyResult, allowGC>::MutableHandleType propp)
 {
     /* NB: The logic of this procedure is implicitly reflected in
      *     BaselineIC.cpp's |EffectlesslyLookupProperty| logic.
      *     If this changes, please remember to update the logic there as well.
      */
 
     /* Search scopes starting with obj and following the prototype link. */
     typename MaybeRooted<NativeObject*, allowGC>::RootType current(cx, obj);
@@ -574,24 +581,24 @@ LookupPropertyInline(ExclusiveContext* c
             break;
         if (!proto->isNative()) {
             if (!cx->shouldBeJSContext() || !allowGC)
                 return false;
             return LookupProperty(cx->asJSContext(),
                                   MaybeRooted<JSObject*, allowGC>::toHandle(proto),
                                   MaybeRooted<jsid, allowGC>::toHandle(id),
                                   MaybeRooted<JSObject*, allowGC>::toMutableHandle(objp),
-                                  MaybeRooted<Shape*, allowGC>::toMutableHandle(propp));
+                                  MaybeRooted<PropertyResult, allowGC>::toMutableHandle(propp));
         }
 
         current = &proto->template as<NativeObject>();
     }
 
     objp.set(nullptr);
-    propp.set(nullptr);
+    propp.setNotFound();
     return true;
 }
 
 inline bool
 ThrowIfNotConstructing(JSContext *cx, const CallArgs &args, const char *builtinName)
 {
     if (args.isConstructing())
         return true;
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -1042,29 +1042,29 @@ NativeObject::addDataProperty(ExclusiveC
     return addProperty(cx, self, id, nullptr, nullptr, slot, attrs, 0);
 }
 
 template <AllowGC allowGC>
 bool
 js::NativeLookupOwnProperty(ExclusiveContext* cx,
                             typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
                             typename MaybeRooted<jsid, allowGC>::HandleType id,
-                            typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp)
+                            typename MaybeRooted<PropertyResult, allowGC>::MutableHandleType propp)
 {
     bool done;
     return LookupOwnPropertyInline<allowGC>(cx, obj, id, propp, &done);
 }
 
 template bool
 js::NativeLookupOwnProperty<CanGC>(ExclusiveContext* cx, HandleNativeObject obj, HandleId id,
-                                   MutableHandleShape propp);
+                                   MutableHandle<PropertyResult> propp);
 
 template bool
 js::NativeLookupOwnProperty<NoGC>(ExclusiveContext* cx, NativeObject* const& obj, const jsid& id,
-                                  FakeMutableHandle<Shape*> propp);
+                                  FakeMutableHandle<PropertyResult> propp);
 
 /*** [[DefineOwnProperty]] ***********************************************************************/
 
 static inline bool
 CallAddPropertyHook(ExclusiveContext* cx, HandleNativeObject obj, HandleShape shape,
                     HandleValue value)
 {
     if (JSAddPropertyOp addProperty = obj->getClass()->getAddProperty()) {
@@ -1277,92 +1277,95 @@ static MOZ_ALWAYS_INLINE bool
 GetExistingProperty(JSContext* cx,
                     typename MaybeRooted<Value, allowGC>::HandleType receiver,
                     typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
                     typename MaybeRooted<Shape*, allowGC>::HandleType shape,
                     typename MaybeRooted<Value, allowGC>::MutableHandleType vp);
 
 static bool
 GetExistingPropertyValue(ExclusiveContext* cx, HandleNativeObject obj, HandleId id,
-                         HandleShape shape, MutableHandleValue vp)
+                         Handle<PropertyResult> prop, MutableHandleValue vp)
 {
-    if (IsImplicitDenseOrTypedArrayElement(shape)) {
+    if (prop.isDenseOrTypedArrayElement()) {
         vp.set(obj->getDenseOrTypedArrayElement(JSID_TO_INT(id)));
         return true;
     }
     if (!cx->shouldBeJSContext())
         return false;
 
-    MOZ_ASSERT(shape->propid() == id);
-    MOZ_ASSERT(obj->contains(cx, shape));
+    MOZ_ASSERT(prop.shape()->propid() == id);
+    MOZ_ASSERT(obj->contains(cx, prop.shape()));
 
     RootedValue receiver(cx, ObjectValue(*obj));
+    RootedShape shape(cx, prop.shape());
     return GetExistingProperty<CanGC>(cx->asJSContext(), receiver, obj, shape, vp);
 }
 
 /*
  * If ES6 draft rev 37 9.1.6.3 ValidateAndApplyPropertyDescriptor step 4 would
  * return early, because desc is redundant with an existing own property obj[id],
  * then set *redundant = true and return true.
  */
 static bool
 DefinePropertyIsRedundant(ExclusiveContext* cx, HandleNativeObject obj, HandleId id,
-                          HandleShape shape, unsigned shapeAttrs,
+                          Handle<PropertyResult> prop, unsigned shapeAttrs,
                           Handle<PropertyDescriptor> desc, bool *redundant)
 {
     *redundant = false;
 
     if (desc.hasConfigurable() && desc.configurable() != ((shapeAttrs & JSPROP_PERMANENT) == 0))
         return true;
     if (desc.hasEnumerable() && desc.enumerable() != ((shapeAttrs & JSPROP_ENUMERATE) != 0))
         return true;
     if (desc.isDataDescriptor()) {
         if ((shapeAttrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0)
             return true;
         if (desc.hasWritable() && desc.writable() != ((shapeAttrs & JSPROP_READONLY) == 0))
             return true;
         if (desc.hasValue()) {
             // Get the current value of the existing property.
             RootedValue currentValue(cx);
-            if (!IsImplicitDenseOrTypedArrayElement(shape) &&
-                shape->hasSlot() &&
-                shape->hasDefaultGetter())
+            if (!prop.isDenseOrTypedArrayElement() &&
+                prop.shape()->hasSlot() &&
+                prop.shape()->hasDefaultGetter())
             {
                 // Inline GetExistingPropertyValue in order to omit a type
                 // correctness assertion that's too strict for this particular
                 // call site. For details, see bug 1125624 comments 13-16.
-                currentValue.set(obj->getSlot(shape->slot()));
+                currentValue.set(obj->getSlot(prop.shape()->slot()));
             } else {
-                if (!GetExistingPropertyValue(cx, obj, id, shape, &currentValue))
+                if (!GetExistingPropertyValue(cx, obj, id, prop, &currentValue))
                     return false;
             }
 
             // The specification calls for SameValue here, but it seems to be a
             // bug. See <https://bugs.ecmascript.org/show_bug.cgi?id=3508>.
             if (desc.value() != currentValue)
                 return true;
         }
 
         GetterOp existingGetterOp =
-            IsImplicitDenseOrTypedArrayElement(shape) ? nullptr : shape->getter();
+            prop.isDenseOrTypedArrayElement() ? nullptr : prop.shape()->getter();
         if (desc.getter() != existingGetterOp)
             return true;
 
         SetterOp existingSetterOp =
-            IsImplicitDenseOrTypedArrayElement(shape) ? nullptr : shape->setter();
+            prop.isDenseOrTypedArrayElement() ? nullptr : prop.shape()->setter();
         if (desc.setter() != existingSetterOp)
             return true;
     } else {
-        if (desc.hasGetterObject()) {
-            if (!(shapeAttrs & JSPROP_GETTER) || desc.getterObject() != shape->getterObject())
-                return true;
+        if (desc.hasGetterObject() &&
+            (!(shapeAttrs & JSPROP_GETTER) || desc.getterObject() != prop.shape()->getterObject()))
+        {
+            return true;
         }
-        if (desc.hasSetterObject()) {
-            if (!(shapeAttrs & JSPROP_SETTER) || desc.setterObject() != shape->setterObject())
-                return true;
+        if (desc.hasSetterObject() &&
+            (!(shapeAttrs & JSPROP_SETTER) || desc.setterObject() != prop.shape()->setterObject()))
+        {
+            return true;
         }
     }
 
     *redundant = true;
     return true;
 }
 
 bool
@@ -1422,65 +1425,64 @@ js::NativeDefineProperty(ExclusiveContex
                 obj->as<ArgumentsObject>().markIteratorOverridden();
         } else if (JSID_IS_INT(id)) {
             if ((desc_.attributes() & JSPROP_RESOLVING) == 0)
                 obj->as<ArgumentsObject>().markElementOverridden();
         }
     }
 
     // 9.1.6.1 OrdinaryDefineOwnProperty step 1.
-    RootedShape shape(cx);
+    Rooted<PropertyResult> prop(cx);
     if (desc_.attributes() & JSPROP_RESOLVING) {
         // We are being called from a resolve or enumerate hook to reify a
         // lazily-resolved property. To avoid reentering the resolve hook and
         // recursing forever, skip the resolve hook when doing this lookup.
-        NativeLookupOwnPropertyNoResolve(cx, obj, id, &shape);
+        NativeLookupOwnPropertyNoResolve(cx, obj, id, &prop);
     } else {
-        if (!NativeLookupOwnProperty<CanGC>(cx, obj, id, &shape))
+        if (!NativeLookupOwnProperty<CanGC>(cx, obj, id, &prop))
             return false;
     }
 
     // From this point, the step numbers refer to
     // 9.1.6.3, ValidateAndApplyPropertyDescriptor.
     // Step 1 is a redundant assertion.
 
     // Filling in desc: Here we make a copy of the desc_ argument. We will turn
     // it into a complete descriptor before updating obj. The spec algorithm
     // does not explicitly do this, but the end result is the same. Search for
     // "fill in" below for places where the filling-in actually occurs.
     Rooted<PropertyDescriptor> desc(cx, desc_);
 
     // Step 2.
-    if (!shape) {
+    if (!prop) {
         if (!obj->nonProxyIsExtensible())
             return result.fail(JSMSG_CANT_DEFINE_PROP_OBJECT_NOT_EXTENSIBLE);
 
         // Fill in missing desc fields with defaults.
         CompletePropertyDescriptor(&desc);
 
         if (!AddOrChangeProperty(cx, obj, id, desc))
             return false;
         return result.succeed();
     }
 
-    MOZ_ASSERT(shape);
-
     // Steps 3-4. (Step 3 is a special case of step 4.) We use shapeAttrs as a
     // stand-in for shape in many places below, since shape might not be a
     // pointer to a real Shape (see IsImplicitDenseOrTypedArrayElement).
-    unsigned shapeAttrs = GetShapeAttributes(obj, shape);
+    unsigned shapeAttrs = GetPropertyAttributes(obj, prop);
     bool redundant;
-    if (!DefinePropertyIsRedundant(cx, obj, id, shape, shapeAttrs, desc, &redundant))
+    if (!DefinePropertyIsRedundant(cx, obj, id, prop, shapeAttrs, desc, &redundant))
         return false;
     if (redundant) {
         // In cases involving JSOP_NEWOBJECT and JSOP_INITPROP, obj can have a
         // type for this property that doesn't match the value in the slot.
         // Update the type here, even though this DefineProperty call is
         // otherwise a no-op. (See bug 1125624 comment 13.)
-        if (!IsImplicitDenseOrTypedArrayElement(shape) && desc.hasValue()) {
+        if (!prop.isDenseOrTypedArrayElement() && desc.hasValue()) {
+            RootedShape shape(cx, prop.shape());
             if (!UpdateShapeTypeAndValue(cx, obj, shape, desc.value()))
                 return false;
         }
         return result.succeed();
     }
 
     // Non-standard hack: Allow redefining non-configurable properties if
     // JSPROP_REDEFINE_NONCONFIGURABLE is set _and_ the object is a non-DOM
@@ -1513,54 +1515,54 @@ js::NativeDefineProperty(ExclusiveContex
         // Fill in desc. A generic descriptor has none of these fields, so copy
         // everything from shape.
         MOZ_ASSERT(!desc.hasValue());
         MOZ_ASSERT(!desc.hasWritable());
         MOZ_ASSERT(!desc.hasGetterObject());
         MOZ_ASSERT(!desc.hasSetterObject());
         if (IsDataDescriptor(shapeAttrs)) {
             RootedValue currentValue(cx);
-            if (!GetExistingPropertyValue(cx, obj, id, shape, &currentValue))
+            if (!GetExistingPropertyValue(cx, obj, id, prop, &currentValue))
                 return false;
             desc.setValue(currentValue);
             desc.setWritable(IsWritable(shapeAttrs));
         } else {
-            desc.setGetterObject(shape->getterObject());
-            desc.setSetterObject(shape->setterObject());
+            desc.setGetterObject(prop.shape()->getterObject());
+            desc.setSetterObject(prop.shape()->setterObject());
         }
     } else if (desc.isDataDescriptor() != IsDataDescriptor(shapeAttrs)) {
         // Step 7.
         if (!IsConfigurable(shapeAttrs) && !skipRedefineChecks)
             return result.fail(JSMSG_CANT_REDEFINE_PROP);
 
-        if (IsImplicitDenseOrTypedArrayElement(shape)) {
+        if (prop.isDenseOrTypedArrayElement()) {
             MOZ_ASSERT(!obj->is<TypedArrayObject>());
             if (!NativeObject::sparsifyDenseElement(cx, obj, JSID_TO_INT(id)))
                 return false;
-            shape = obj->lookup(cx, id);
+            prop.setNativeProperty(obj->lookup(cx, id));
         }
 
         // Fill in desc fields with default values (steps 7.b.i and 7.c.i).
         CompletePropertyDescriptor(&desc);
     } else if (desc.isDataDescriptor()) {
         // Step 8.
         bool frozen = !IsConfigurable(shapeAttrs) && !IsWritable(shapeAttrs);
         if (frozen && desc.hasWritable() && desc.writable() && !skipRedefineChecks)
             return result.fail(JSMSG_CANT_REDEFINE_PROP);
 
         if (frozen || !desc.hasValue()) {
-            if (IsImplicitDenseOrTypedArrayElement(shape)) {
+            if (prop.isDenseOrTypedArrayElement()) {
                 MOZ_ASSERT(!obj->is<TypedArrayObject>());
                 if (!NativeObject::sparsifyDenseElement(cx, obj, JSID_TO_INT(id)))
                     return false;
-                shape = obj->lookup(cx, id);
+                prop.setNativeProperty(obj->lookup(cx, id));
             }
 
             RootedValue currentValue(cx);
-            if (!GetExistingPropertyValue(cx, obj, id, shape, &currentValue))
+            if (!GetExistingPropertyValue(cx, obj, id, prop, &currentValue))
                 return false;
 
             if (!desc.hasValue()) {
                 // Fill in desc.[[Value]].
                 desc.setValue(currentValue);
             } else {
                 // Step 8.a.ii.1.
                 bool same;
@@ -1572,42 +1574,42 @@ js::NativeDefineProperty(ExclusiveContex
                     return result.fail(JSMSG_CANT_REDEFINE_PROP);
             }
         }
 
         if (!desc.hasWritable())
             desc.setWritable(IsWritable(shapeAttrs));
     } else {
         // Step 9.
-        MOZ_ASSERT(shape->isAccessorDescriptor());
+        MOZ_ASSERT(prop.shape()->isAccessorDescriptor());
         MOZ_ASSERT(desc.isAccessorDescriptor());
 
         // The spec says to use SameValue, but since the values in
         // question are objects, we can just compare pointers.
         if (desc.hasSetterObject()) {
             if (!IsConfigurable(shapeAttrs) &&
-                desc.setterObject() != shape->setterObject() &&
+                desc.setterObject() != prop.shape()->setterObject() &&
                 !skipRedefineChecks)
             {
                 return result.fail(JSMSG_CANT_REDEFINE_PROP);
             }
         } else {
             // Fill in desc.[[Set]] from shape.
-            desc.setSetterObject(shape->setterObject());
+            desc.setSetterObject(prop.shape()->setterObject());
         }
         if (desc.hasGetterObject()) {
             if (!IsConfigurable(shapeAttrs) &&
-                desc.getterObject() != shape->getterObject() &&
+                desc.getterObject() != prop.shape()->getterObject() &&
                 !skipRedefineChecks)
             {
                 return result.fail(JSMSG_CANT_REDEFINE_PROP);
             }
         } else {
             // Fill in desc.[[Get]] from shape.
-            desc.setGetterObject(shape->getterObject());
+            desc.setGetterObject(prop.shape()->getterObject());
         }
     }
 
     // Step 10.
     if (!AddOrChangeProperty(cx, obj, id, desc))
         return false;
     return result.succeed();
 }
@@ -1682,28 +1684,28 @@ js::NativeDefineProperty(ExclusiveContex
 
 /*** [[HasProperty]] *****************************************************************************/
 
 // ES6 draft rev31 9.1.7.1 OrdinaryHasProperty
 bool
 js::NativeHasProperty(JSContext* cx, HandleNativeObject obj, HandleId id, bool* foundp)
 {
     RootedNativeObject pobj(cx, obj);
-    RootedShape shape(cx);
+    Rooted<PropertyResult> prop(cx);
 
     // This loop isn't explicit in the spec algorithm. See the comment on step
     // 7.a. below.
     for (;;) {
         // Steps 2-3. ('done' is a SpiderMonkey-specific thing, used below.)
         bool done;
-        if (!LookupOwnPropertyInline<CanGC>(cx, pobj, id, &shape, &done))
+        if (!LookupOwnPropertyInline<CanGC>(cx, pobj, id, &prop, &done))
             return false;
 
         // Step 4.
-        if (shape) {
+        if (prop) {
             *foundp = true;
             return true;
         }
 
         // Step 5-6. The check for 'done' on this next line is tricky.
         // done can be true in exactly these unlikely-sounding cases:
         // - We're looking up an element, and pobj is a TypedArray that
         //   doesn't have that many elements.
@@ -1734,62 +1736,63 @@ js::NativeHasProperty(JSContext* cx, Han
 
 
 /*** [[GetOwnPropertyDescriptor]] ****************************************************************/
 
 bool
 js::NativeGetOwnPropertyDescriptor(JSContext* cx, HandleNativeObject obj, HandleId id,
                                    MutableHandle<PropertyDescriptor> desc)
 {
-    RootedShape shape(cx);
-    if (!NativeLookupOwnProperty<CanGC>(cx, obj, id, &shape))
+    Rooted<PropertyResult> prop(cx);
+    if (!NativeLookupOwnProperty<CanGC>(cx, obj, id, &prop))
         return false;
-    if (!shape) {
+    if (!prop) {
         desc.object().set(nullptr);
         return true;
     }
 
-    desc.setAttributes(GetShapeAttributes(obj, shape));
+    desc.setAttributes(GetPropertyAttributes(obj, prop));
     if (desc.isAccessorDescriptor()) {
         MOZ_ASSERT(desc.isShared());
 
         // The result of GetOwnPropertyDescriptor() must be either undefined or
         // a complete property descriptor (per ES6 draft rev 32 (2015 Feb 2)
         // 6.1.7.3, Invariants of the Essential Internal Methods).
         //
         // It is an unfortunate fact that in SM, properties can exist that have
         // JSPROP_GETTER or JSPROP_SETTER but not both. In these cases, rather
         // than return true with desc incomplete, we fill out the missing
         // getter or setter with a null, following CompletePropertyDescriptor.
         if (desc.hasGetterObject()) {
-            desc.setGetterObject(shape->getterObject());
+            desc.setGetterObject(prop.shape()->getterObject());
         } else {
             desc.setGetterObject(nullptr);
             desc.attributesRef() |= JSPROP_GETTER;
         }
         if (desc.hasSetterObject()) {
-            desc.setSetterObject(shape->setterObject());
+            desc.setSetterObject(prop.shape()->setterObject());
         } else {
             desc.setSetterObject(nullptr);
             desc.attributesRef() |= JSPROP_SETTER;
         }
 
         desc.value().setUndefined();
     } else {
         // This is either a straight-up data property or (rarely) a
         // property with a JSGetterOp/JSSetterOp. The latter must be
         // reported to the caller as a plain data property, so clear
         // desc.getter/setter, and mask away the SHARED bit.
         desc.setGetter(nullptr);
         desc.setSetter(nullptr);
         desc.attributesRef() &= ~JSPROP_SHARED;
 
-        if (IsImplicitDenseOrTypedArrayElement(shape)) {
+        if (prop.isDenseOrTypedArrayElement()) {
             desc.value().set(obj->getDenseOrTypedArrayElement(JSID_TO_INT(id)));
         } else {
+            RootedShape shape(cx, prop.shape());
             if (!NativeGetExistingProperty(cx, obj, obj, shape, desc.value()))
                 return false;
         }
     }
 
     desc.object().set(obj);
     desc.assertComplete();
     return true;
@@ -2059,33 +2062,35 @@ static MOZ_ALWAYS_INLINE bool
 NativeGetPropertyInline(JSContext* cx,
                         typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
                         typename MaybeRooted<Value, allowGC>::HandleType receiver,
                         typename MaybeRooted<jsid, allowGC>::HandleType id,
                         IsNameLookup nameLookup,
                         typename MaybeRooted<Value, allowGC>::MutableHandleType vp)
 {
     typename MaybeRooted<NativeObject*, allowGC>::RootType pobj(cx, obj);
-    typename MaybeRooted<Shape*, allowGC>::RootType shape(cx);
+    typename MaybeRooted<PropertyResult, allowGC>::RootType prop(cx);
 
     // This loop isn't explicit in the spec algorithm. See the comment on step
     // 4.d below.
     for (;;) {
         // Steps 2-3. ('done' is a SpiderMonkey-specific thing, used below.)
         bool done;
-        if (!LookupOwnPropertyInline<allowGC>(cx, pobj, id, &shape, &done))
+        if (!LookupOwnPropertyInline<allowGC>(cx, pobj, id, &prop, &done))
             return false;
 
-        if (shape) {
+        if (prop) {
             // Steps 5-8. Special case for dense elements because
             // GetExistingProperty doesn't support those.
-            if (IsImplicitDenseOrTypedArrayElement(shape)) {
+            if (prop.isDenseOrTypedArrayElement()) {
                 vp.set(pobj->getDenseOrTypedArrayElement(JSID_TO_INT(id)));
                 return true;
             }
+
+            typename MaybeRooted<Shape*, allowGC>::RootType shape(cx, prop.shape());
             return GetExistingProperty<allowGC>(cx, receiver, pobj, shape, vp);
         }
 
         // Steps 4.a-b. The check for 'done' on this next line is tricky.
         // done can be true in exactly these unlikely-sounding cases:
         // - We're looking up an element, and pobj is a TypedArray that
         //   doesn't have that many elements.
         // - We're being called from a resolve hook to assign to the property
@@ -2367,34 +2372,35 @@ SetDenseOrTypedArrayElement(JSContext* c
  * has been found on a native object (pobj). This implements ES6 draft rev 32
  * (2015 Feb 2) 9.1.9 steps 5 and 6.
  *
  * It is necessary to pass both id and shape because shape could be an implicit
  * dense or typed array element (i.e. not actually a pointer to a Shape).
  */
 static bool
 SetExistingProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleValue v,
-                    HandleValue receiver, HandleNativeObject pobj, HandleShape shape,
+                    HandleValue receiver, HandleNativeObject pobj, Handle<PropertyResult> prop,
                     ObjectOpResult& result)
 {
     // Step 5 for dense elements.
-    if (IsImplicitDenseOrTypedArrayElement(shape)) {
+    if (prop.isDenseOrTypedArrayElement()) {
         // Step 5.a.
         if (obj->getElementsHeader()->isFrozen())
             return result.fail(JSMSG_READ_ONLY);
 
         // Pure optimization for the common case:
         if (receiver.isObject() && pobj == &receiver.toObject())
             return SetDenseOrTypedArrayElement(cx, pobj, JSID_TO_INT(id), v, result);
 
         // Steps 5.b-f.
         return SetPropertyByDefining(cx, id, v, receiver, result);
     }
 
     // Step 5 for all other properties.
+    RootedShape shape(cx, prop.shape());
     if (shape->isDataDescriptor()) {
         // Step 5.a.
         if (!shape->writable())
             return result.fail(JSMSG_READ_ONLY);
 
         // steps 5.c-f.
         if (receiver.isObject() && pobj == &receiver.toObject()) {
             // Pure optimization for the common case. There's no point performing
@@ -2450,31 +2456,31 @@ js::NativeSetProperty(JSContext* cx, Han
         WatchpointMap* wpmap = cx->compartment()->watchpointMap;
         if (wpmap && !wpmap->triggerWatchpoint(cx, obj, id, &v))
             return false;
     }
 
     // Step numbers below reference ES6 rev 27 9.1.9, the [[Set]] internal
     // method for ordinary objects. We substitute our own names for these names
     // used in the spec: O -> pobj, P -> id, ownDesc -> shape.
-    RootedShape shape(cx);
+    Rooted<PropertyResult> prop(cx);
     RootedNativeObject pobj(cx, obj);
 
     // This loop isn't explicit in the spec algorithm. See the comment on step
     // 4.c.i below. (There's a very similar loop in the NativeGetProperty
     // implementation, but unfortunately not similar enough to common up.)
     for (;;) {
         // Steps 2-3. ('done' is a SpiderMonkey-specific thing, used below.)
         bool done;
-        if (!LookupOwnPropertyInline<CanGC>(cx, pobj, id, &shape, &done))
+        if (!LookupOwnPropertyInline<CanGC>(cx, pobj, id, &prop, &done))
             return false;
 
-        if (shape) {
+        if (prop) {
             // Steps 5-6.
-            return SetExistingProperty(cx, obj, id, v, receiver, pobj, shape, result);
+            return SetExistingProperty(cx, obj, id, v, receiver, pobj, prop, result);
         }
 
         // Steps 4.a-b. The check for 'done' on this next line is tricky.
         // done can be true in exactly these unlikely-sounding cases:
         // - We're looking up an element, and pobj is a TypedArray that
         //   doesn't have that many elements.
         // - We're being called from a resolve hook to assign to the property
         //   being resolved.
@@ -2522,40 +2528,40 @@ js::NativeSetElement(JSContext* cx, Hand
 /*** [[Delete]] **********************************************************************************/
 
 // ES6 draft rev31 9.1.10 [[Delete]]
 bool
 js::NativeDeleteProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
                          ObjectOpResult& result)
 {
     // Steps 2-3.
-    RootedShape shape(cx);
-    if (!NativeLookupOwnProperty<CanGC>(cx, obj, id, &shape))
+    Rooted<PropertyResult> prop(cx);
+    if (!NativeLookupOwnProperty<CanGC>(cx, obj, id, &prop))
         return false;
 
     // Step 4.
-    if (!shape) {
+    if (!prop) {
         // If no property call the class's delProperty hook, passing succeeded
         // as the result parameter. This always succeeds when there is no hook.
         return CallJSDeletePropertyOp(cx, obj->getClass()->getDelProperty(), obj, id, result);
     }
 
     cx->runtime()->gc.poke();
 
     // Step 6. Non-configurable property.
-    if (GetShapeAttributes(obj, shape) & JSPROP_PERMANENT)
+    if (GetPropertyAttributes(obj, prop) & JSPROP_PERMANENT)
         return result.failCantDelete();
 
     if (!CallJSDeletePropertyOp(cx, obj->getClass()->getDelProperty(), obj, id, result))
         return false;
     if (!result)
         return true;
 
     // Step 5.
-    if (IsImplicitDenseOrTypedArrayElement(shape)) {
+    if (prop.isDenseOrTypedArrayElement()) {
         // Typed array elements are non-configurable.
         MOZ_ASSERT(!obj->is<TypedArrayObject>());
 
         if (!obj->maybeCopyElementsForWrite(cx))
             return false;
 
         obj->setDenseElementHole(cx, JSID_TO_INT(id));
     } else {
--- a/js/src/vm/NativeObject.h
+++ b/js/src/vm/NativeObject.h
@@ -1447,17 +1447,17 @@ NativeDeleteProperty(JSContext* cx, Hand
 
 /*** SpiderMonkey nonstandard internal methods ***************************************************/
 
 template <AllowGC allowGC>
 extern bool
 NativeLookupOwnProperty(ExclusiveContext* cx,
                         typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
                         typename MaybeRooted<jsid, allowGC>::HandleType id,
-                        typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp);
+                        typename MaybeRooted<PropertyResult, allowGC>::MutableHandleType propp);
 
 /*
  * Get a property from `receiver`, after having already done a lookup and found
  * the property on a native object `obj`.
  *
  * `shape` must not be null and must not be an implicit dense property. It must
  * be present in obj's shape chain.
  */
--- a/js/src/vm/Shape-inl.h
+++ b/js/src/vm/Shape-inl.h
@@ -187,24 +187,24 @@ AutoRooterGetterSetter::AutoRooterGetter
     if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
         inner.emplace(cx, attrs, reinterpret_cast<GetterOp*>(pgetter),
                       reinterpret_cast<SetterOp*>(psetter));
     }
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
 }
 
 static inline uint8_t
-GetShapeAttributes(JSObject* obj, Shape* shape)
+GetPropertyAttributes(JSObject* obj, PropertyResult prop)
 {
     MOZ_ASSERT(obj->isNative());
 
-    if (IsImplicitDenseOrTypedArrayElement(shape)) {
+    if (prop.isDenseOrTypedArrayElement()) {
         if (obj->is<TypedArrayObject>())
             return JSPROP_ENUMERATE | JSPROP_PERMANENT;
         return obj->as<NativeObject>().getElementsHeader()->elementAttributes();
     }
 
-    return shape->attributes();
+    return prop.shape()->attributes();
 }
 
 } /* namespace js */
 
 #endif /* vm_Shape_inl_h */
--- a/js/src/vm/Shape.cpp
+++ b/js/src/vm/Shape.cpp
@@ -1775,8 +1775,15 @@ JS::ubi::Concrete<js::Shape>::size(mozil
     return size;
 }
 
 JS::ubi::Node::Size
 JS::ubi::Concrete<js::BaseShape>::size(mozilla::MallocSizeOf mallocSizeOf) const
 {
     return js::gc::Arena::thingSize(get().asTenured().getAllocKind());
 }
+
+void
+PropertyResult::trace(JSTracer* trc)
+{
+    if (isNativeProperty())
+        TraceRoot(trc, &shape_, "PropertyResult::shape_");
+}
--- a/js/src/vm/Shape.h
+++ b/js/src/vm/Shape.h
@@ -1540,48 +1540,16 @@ Shape::searchNoHashify(Shape* start, jsi
 inline bool
 Shape::matches(const StackShape& other) const
 {
     return propid_.get() == other.propid &&
            matchesParamsAfterId(other.base, other.slot_, other.attrs, other.flags,
                                 other.rawGetter, other.rawSetter);
 }
 
-// Property lookup hooks on objects are required to return a non-nullptr shape
-// to signify that the property has been found. For cases where the property is
-// not actually represented by a Shape, use a dummy value. This includes all
-// properties of non-native objects, and dense elements for native objects.
-// Use separate APIs for these two cases.
-
-template <AllowGC allowGC>
-static inline void
-MarkNonNativePropertyFound(typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp)
-{
-    propp.set(reinterpret_cast<Shape*>(1));
-}
-
-template <AllowGC allowGC>
-static inline void
-MarkDenseOrTypedArrayElementFound(typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp)
-{
-    propp.set(reinterpret_cast<Shape*>(1));
-}
-
-static inline bool
-IsImplicitDenseOrTypedArrayElement(Shape* prop)
-{
-    return prop == reinterpret_cast<Shape*>(1);
-}
-
-static inline bool
-IsImplicitNonNativeProperty(Shape* prop)
-{
-    return prop == reinterpret_cast<Shape*>(1);
-}
-
 Shape*
 ReshapeForAllocKind(JSContext* cx, Shape* shape, TaggedProto proto,
                     gc::AllocKind allocKind);
 
 } // namespace js
 
 #ifdef _MSC_VER
 #pragma warning(pop)
--- a/js/src/vm/TypeInference-inl.h
+++ b/js/src/vm/TypeInference-inl.h
@@ -288,36 +288,36 @@ struct AutoEnterAnalysis
     // For use when initializing an UnboxedLayout.  The UniquePtr's destructor
     // must run when GC is not suppressed.
     UniquePtr<UnboxedLayout> unboxedLayoutToCleanUp;
 
     // Prevent GC activity in the middle of analysis.
     gc::AutoSuppressGC suppressGC;
 
     // Allow clearing inference info on OOM during incremental sweeping.
-    AutoClearTypeInferenceStateOnOOM oom;
+    mozilla::Maybe<AutoClearTypeInferenceStateOnOOM> oom;
 
     // Pending recompilations to perform before execution of JIT code can resume.
     RecompileInfoVector pendingRecompiles;
 
     // Prevent us from calling the objectMetadataCallback.
     js::AutoSuppressAllocationMetadataBuilder suppressMetadata;
 
     FreeOp* freeOp;
     Zone* zone;
 
     explicit AutoEnterAnalysis(ExclusiveContext* cx)
-      : suppressGC(cx), oom(cx->zone()), suppressMetadata(cx)
+      : suppressGC(cx), suppressMetadata(cx)
     {
         init(cx->defaultFreeOp(), cx->zone());
     }
 
     AutoEnterAnalysis(FreeOp* fop, Zone* zone)
       : suppressGC(zone->runtimeFromMainThread()->contextFromMainThread()),
-        oom(zone), suppressMetadata(zone)
+        suppressMetadata(zone)
     {
         init(fop, zone);
     }
 
     ~AutoEnterAnalysis()
     {
         if (this != zone->types.activeAnalysis)
             return;
@@ -328,18 +328,20 @@ struct AutoEnterAnalysis
             zone->types.processPendingRecompiles(freeOp, pendingRecompiles);
     }
 
   private:
     void init(FreeOp* fop, Zone* zone) {
         this->freeOp = fop;
         this->zone = zone;
 
-        if (!zone->types.activeAnalysis)
+        if (!zone->types.activeAnalysis) {
+            MOZ_RELEASE_ASSERT(!zone->types.sweepingTypes);
             zone->types.activeAnalysis = this;
+        }
     }
 };
 
 /////////////////////////////////////////////////////////////////////
 // Interface functions
 /////////////////////////////////////////////////////////////////////
 
 void MarkIteratorUnknownSlow(JSContext* cx);
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -4245,18 +4245,20 @@ ObjectGroup::clearProperties()
     propertySet = nullptr;
 }
 
 static void
 EnsureHasAutoClearTypeInferenceStateOnOOM(AutoClearTypeInferenceStateOnOOM*& oom, Zone* zone,
                                           Maybe<AutoClearTypeInferenceStateOnOOM>& fallback)
 {
     if (!oom) {
-        if (zone->types.activeAnalysis) {
-            oom = &zone->types.activeAnalysis->oom;
+        if (AutoEnterAnalysis* analysis = zone->types.activeAnalysis) {
+            if (analysis->oom.isNothing())
+                analysis->oom.emplace(zone);
+            oom = analysis->oom.ptr();
         } else {
             fallback.emplace(zone);
             oom = &fallback.ref();
         }
     }
 }
 
 /*
@@ -4439,24 +4441,26 @@ Zone::addSizeOfIncludingThis(mozilla::Ma
 TypeZone::TypeZone(Zone* zone)
   : zone_(zone),
     typeLifoAlloc(TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     generation(0),
     compilerOutputs(nullptr),
     sweepTypeLifoAlloc(TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     sweepCompilerOutputs(nullptr),
     sweepReleaseTypes(false),
+    sweepingTypes(false),
     activeAnalysis(nullptr)
 {
 }
 
 TypeZone::~TypeZone()
 {
     js_delete(compilerOutputs);
     js_delete(sweepCompilerOutputs);
+    MOZ_RELEASE_ASSERT(!sweepingTypes);
 }
 
 void
 TypeZone::beginSweep(FreeOp* fop, bool releaseTypes, AutoClearTypeInferenceStateOnOOM& oom)
 {
     MOZ_ASSERT(zone()->isGCSweepingOrCompacting());
     MOZ_ASSERT(!sweepCompilerOutputs);
     MOZ_ASSERT(!sweepReleaseTypes);
@@ -4520,18 +4524,27 @@ TypeZone::clearAllNewScriptsOnOOM()
 {
     for (auto iter = zone()->cellIter<ObjectGroup>(); !iter.done(); iter.next()) {
         ObjectGroup* group = iter;
         if (!IsAboutToBeFinalizedUnbarriered(&group))
             group->maybeClearNewScriptOnOOM();
     }
 }
 
+AutoClearTypeInferenceStateOnOOM::AutoClearTypeInferenceStateOnOOM(Zone* zone)
+  : zone(zone), oom(false)
+{
+    MOZ_RELEASE_ASSERT(CurrentThreadCanAccessZone(zone));
+    zone->types.setSweepingTypes(true);
+}
+
 AutoClearTypeInferenceStateOnOOM::~AutoClearTypeInferenceStateOnOOM()
 {
+    zone->types.setSweepingTypes(false);
+
     if (oom) {
         JSRuntime* rt = zone->runtimeFromMainThread();
         js::CancelOffThreadIonCompile(rt);
         zone->setPreservingCode(false);
         zone->discardJitCode(rt->defaultFreeOp(), /* discardBaselineCode = */ false);
         zone->types.clearAllNewScriptsOnOOM();
     }
 }
--- a/js/src/vm/TypeInference.h
+++ b/js/src/vm/TypeInference.h
@@ -583,21 +583,21 @@ public:
 // or even copied over completely. In this case, destroy all JIT code and new
 // script information in the zone, the only things whose correctness depends on
 // the type constraints.
 class AutoClearTypeInferenceStateOnOOM
 {
     Zone* zone;
     bool oom;
 
+    AutoClearTypeInferenceStateOnOOM(const AutoClearTypeInferenceStateOnOOM&) = delete;
+    void operator=(const AutoClearTypeInferenceStateOnOOM&) = delete;
+
   public:
-    explicit AutoClearTypeInferenceStateOnOOM(Zone* zone)
-      : zone(zone), oom(false)
-    {}
-
+    explicit AutoClearTypeInferenceStateOnOOM(Zone* zone);
     ~AutoClearTypeInferenceStateOnOOM();
 
     void setOOM() {
         oom = true;
     }
     bool hadOOM() const {
         return oom;
     }
@@ -1261,16 +1261,18 @@ struct TypeZone
     // During incremental sweeping, the old compiler outputs for use by
     // recompile indexes with a stale generation.
     CompilerOutputVector* sweepCompilerOutputs;
 
     // During incremental sweeping, whether to try to destroy all type
     // information attached to scripts.
     bool sweepReleaseTypes;
 
+    bool sweepingTypes;
+
     // The topmost AutoEnterAnalysis on the stack, if there is one.
     AutoEnterAnalysis* activeAnalysis;
 
     explicit TypeZone(JS::Zone* zone);
     ~TypeZone();
 
     JS::Zone* zone() const { return zone_; }
 
@@ -1278,16 +1280,21 @@ struct TypeZone
     void endSweep(JSRuntime* rt);
     void clearAllNewScriptsOnOOM();
 
     /* Mark a script as needing recompilation once inference has finished. */
     void addPendingRecompile(JSContext* cx, const RecompileInfo& info);
     void addPendingRecompile(JSContext* cx, JSScript* script);
 
     void processPendingRecompiles(FreeOp* fop, RecompileInfoVector& recompiles);
+
+    void setSweepingTypes(bool sweeping) {
+        MOZ_RELEASE_ASSERT(sweepingTypes != sweeping);
+        sweepingTypes = sweeping;
+    }
 };
 
 enum SpewChannel {
     ISpewOps,      /* ops: New constraints and types. */
     ISpewResult,   /* result: Final type sets. */
     SPEW_COUNT
 };
 
--- a/js/src/vm/UbiNode.cpp
+++ b/js/src/vm/UbiNode.cpp
@@ -202,16 +202,18 @@ Node::exposeToJS() const
     } else if (is<JSString>()) {
         v.setString(as<JSString>());
     } else if (is<JS::Symbol>()) {
         v.setSymbol(as<JS::Symbol>());
     } else {
         v.setUndefined();
     }
 
+    ExposeValueToActiveJS(v);
+
     return v;
 }
 
 
 // A JS::CallbackTracer subclass that adds a Edge to a Vector for each
 // edge on which it is invoked.
 class EdgeVectorTracer : public JS::CallbackTracer {
     // The vector to which we add Edges.
--- a/js/src/vm/UnboxedObject.cpp
+++ b/js/src/vm/UnboxedObject.cpp
@@ -716,28 +716,28 @@ UnboxedPlainObject::createWithProperties
 #endif
 
     return obj;
 }
 
 /* static */ bool
 UnboxedPlainObject::obj_lookupProperty(JSContext* cx, HandleObject obj,
                                        HandleId id, MutableHandleObject objp,
-                                       MutableHandleShape propp)
+                                       MutableHandle<PropertyResult> propp)
 {
     if (obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id)) {
-        MarkNonNativePropertyFound<CanGC>(propp);
+        propp.setNonNativeProperty();
         objp.set(obj);
         return true;
     }
 
     RootedObject proto(cx, obj->staticPrototype());
     if (!proto) {
         objp.set(nullptr);
-        propp.set(nullptr);
+        propp.setNotFound();
         return true;
     }
 
     return LookupProperty(cx, proto, id, objp, propp);
 }
 
 /* static */ bool
 UnboxedPlainObject::obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id,
@@ -1417,28 +1417,28 @@ UnboxedArrayObject::containsProperty(Exc
     if (JSID_IS_ATOM(id) && JSID_TO_ATOM(id) == cx->names().length)
         return true;
     return false;
 }
 
 /* static */ bool
 UnboxedArrayObject::obj_lookupProperty(JSContext* cx, HandleObject obj,
                                        HandleId id, MutableHandleObject objp,
-                                       MutableHandleShape propp)
+                                       MutableHandle<PropertyResult> propp)
 {
     if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
-        MarkNonNativePropertyFound<CanGC>(propp);
+        propp.setNonNativeProperty();
         objp.set(obj);
         return true;
     }
 
     RootedObject proto(cx, obj->staticPrototype());
     if (!proto) {
         objp.set(nullptr);
-        propp.set(nullptr);
+        propp.setNotFound();
         return true;
     }
 
     return LookupProperty(cx, proto, id, objp, propp);
 }
 
 /* static */ bool
 UnboxedArrayObject::obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id,
--- a/js/src/vm/UnboxedObject.h
+++ b/js/src/vm/UnboxedObject.h
@@ -238,17 +238,17 @@ class UnboxedPlainObject : public JSObje
     // Start of the inline data, which immediately follows the group and extra properties.
     uint8_t data_[1];
 
   public:
     static const Class class_;
 
     static bool obj_lookupProperty(JSContext* cx, HandleObject obj,
                                    HandleId id, MutableHandleObject objp,
-                                   MutableHandleShape propp);
+                                   MutableHandle<PropertyResult> propp);
 
     static bool obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id,
                                    Handle<PropertyDescriptor> desc,
                                    ObjectOpResult& result);
 
     static bool obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp);
 
     static bool obj_getProperty(JSContext* cx, HandleObject obj, HandleValue receiver,
@@ -374,17 +374,17 @@ class UnboxedArrayObject : public JSObje
     static uint32_t chooseCapacityIndex(uint32_t capacity, uint32_t length);
     static uint32_t exactCapacityIndex(uint32_t capacity);
 
   public:
     static const Class class_;
 
     static bool obj_lookupProperty(JSContext* cx, HandleObject obj,
                                    HandleId id, MutableHandleObject objp,
-                                   MutableHandleShape propp);
+                                   MutableHandle<PropertyResult> propp);
 
     static bool obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id,
                                    Handle<PropertyDescriptor> desc,
                                    ObjectOpResult& result);
 
     static bool obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp);
 
     static bool obj_getProperty(JSContext* cx, HandleObject obj, HandleValue receiver,
--- a/js/xpconnect/tests/chrome/test_weakmaps.xul
+++ b/js/xpconnect/tests/chrome/test_weakmaps.xul
@@ -209,38 +209,18 @@ https://bugzilla.mozilla.org/show_bug.cg
   let make_live_map = function () {
     let live = get_live_dom();
     wn_live_map.set(live, {});
     ok(wn_live_map.has(get_live_dom()), "Live map should have live DOM node before GC.");
   }
 
   make_live_map();
 
-  let unpreservable_native_key = function () {
-    // We only allow natives that support wrapper preservation to be used as weak
-    // map keys.  We should be able to try to add unpreservable natives as keys without
-    // crashing (bug 711616), but we should throw an error (bug 761620).
-
-    let dummy_test_map = new WeakMap;
-
-    let rule_fail = false;
-    let got_rule = false;
-    try {
-      var rule = document.styleSheets[0].cssRules[0];
-      got_rule = true;
-      dummy_test_map.set(rule, 1);
-    } catch (e) {
-      rule_fail = true;
-    }
-    ok(got_rule, "Got the CSS rule");
-    ok(rule_fail, "Using a CSS rule as a weak map key should produce an exception because it can't be wrapper preserved.");
-
-  }
-
-  unpreservable_native_key();
+  // We're out of ideas for unpreservable natives, now that just about
+  // everything is on webidl, so just don't test those.
 
   /* set up for running precise GC/CC then checking the results */
 
   SimpleTest.waitForExplicitFinish();
 
   Cu.schedulePreciseGC(function () {
     SpecialPowers.DOMWindowUtils.cycleCollect();
     SpecialPowers.DOMWindowUtils.garbageCollect();
--- a/js/xpconnect/tests/mochitest/file_crosscompartment_weakmap.html
+++ b/js/xpconnect/tests/mochitest/file_crosscompartment_weakmap.html
@@ -1,9 +1,8 @@
 <!DOCTYPE HTML>
 <html>
 <head>
-  <link rel="stylesheet" href="data:text/css,div {}">
   <title>Test Cross-Compartment DOM WeakMaps</title>
 </head>
 <body>
 </body>
 </html>
--- a/js/xpconnect/tests/mochitest/test_crosscompartment_weakmap.html
+++ b/js/xpconnect/tests/mochitest/test_crosscompartment_weakmap.html
@@ -10,24 +10,16 @@
 <script type="application/javascript">
 
 var my_map = new WeakMap();
 
 function setup() {
   var item = window.frames[0].document.querySelector("body");
 
   my_map.set(item, "success_string");
-
-  var rule_fail = false;
-  try {
-    my_map.set(window.frames[0].document.styleSheets[0].cssRules[0], 1);
-  } catch (e) {
-    rule_fail = true;
-  }
-  ok(rule_fail, "Using rule as a weak map key across compartments should produce an exception because it can't be wrapper preserved.");
 }
 
 function runTest() {
   setup();
   SpecialPowers.forceGC();
   SpecialPowers.forceCC();
   SpecialPowers.forceGC();
   SpecialPowers.forceCC();
--- a/layout/inspector/inCSSValueSearch.cpp
+++ b/layout/inspector/inCSSValueSearch.cpp
@@ -13,16 +13,17 @@
 #include "nsIDOMStyleSheetList.h"
 #include "nsIDOMCSSStyleSheet.h"
 #include "nsIDOMCSSRuleList.h"
 #include "nsIDOMCSSStyleRule.h"
 #include "nsIDOMCSSStyleDeclaration.h"
 #include "nsIDOMCSSImportRule.h"
 #include "nsIDOMCSSMediaRule.h"
 #include "nsIDOMCSSSupportsRule.h"
+#include "nsIDOMCSSRule.h"
 #include "nsIURI.h"
 #include "nsIDocument.h"
 #include "nsNetUtil.h"
 
 using namespace mozilla;
 
 ///////////////////////////////////////////////////////////////////////////////
 inCSSValueSearch::inCSSValueSearch()
--- a/layout/inspector/inDOMUtils.cpp
+++ b/layout/inspector/inDOMUtils.cpp
@@ -248,23 +248,19 @@ inDOMUtils::GetCSSStyleRules(nsIDOMEleme
     ruleNodes.AppendElement(ruleNode);
     ruleNode = ruleNode->GetParent();
   }
 
   nsCOMPtr<nsIMutableArray> rules = nsArray::Create();
   for (nsRuleNode* ruleNode : Reversed(ruleNodes)) {
     RefPtr<Declaration> decl = do_QueryObject(ruleNode->GetRule());
     if (decl) {
-      RefPtr<mozilla::css::StyleRule> styleRule =
-        do_QueryObject(decl->GetOwningRule());
-      if (styleRule) {
-        nsCOMPtr<nsIDOMCSSRule> domRule = styleRule->GetDOMRule();
-        if (domRule) {
-          rules->AppendElement(domRule, /*weak =*/ false);
-        }
+      css::Rule* owningRule = decl->GetOwningRule();
+      if (owningRule) {
+        rules->AppendElement(owningRule, /*weak =*/ false);
       }
     }
   }
 
   rules.forget(_retval);
 
   return NS_OK;
 }
new file mode 100644
--- /dev/null
+++ b/layout/style/BindingStyleRule.cpp
@@ -0,0 +1,18 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/BindingStyleRule.h"
+#include "mozilla/dom/CSSStyleRuleBinding.h"
+
+namespace mozilla {
+
+/* virtual */ JSObject*
+BindingStyleRule::WrapObject(JSContext* aCx,
+			     JS::Handle<JSObject*> aGivenProto)
+{
+  return dom::CSSStyleRuleBinding::Wrap(aCx, this, aGivenProto);
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/layout/style/BindingStyleRule.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_BindingStyleRule_h__
+#define mozilla_BindingStyleRule_h__
+
+#include "nscore.h"
+#include "nsStringGlue.h"
+#include "mozilla/css/Rule.h"
+
+/**
+ * Shared superclass for mozilla::css::StyleRule and mozilla::ServoStyleRule,
+ * for use from bindings code.
+ */
+
+class nsICSSDeclaration;
+
+namespace mozilla {
+
+class BindingStyleRule : public css::Rule
+{
+protected:
+  BindingStyleRule(uint32_t aLineNumber, uint32_t aColumnNumber)
+    : css::Rule(aLineNumber, aColumnNumber)
+  {
+  }
+  BindingStyleRule(const BindingStyleRule& aCopy)
+    : css::Rule(aCopy)
+  {
+  }
+  virtual ~BindingStyleRule() {}
+
+public:
+  // This is pure virtual because we have no members, and are an abstract class
+  // to start with.  The fact that we have to have this declaration at all is
+  // kinda dumb.  :(
+  virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
+    const override MOZ_MUST_OVERRIDE = 0;
+
+  // Likewise for this one.  We have to override our superclass, but don't
+  // really need to do anything in this method.
+  virtual bool IsCCLeaf() const override MOZ_MUST_OVERRIDE = 0;
+
+  // WebIDL API
+  // For GetSelectorText/SetSelectorText, we purposefully use a signature that
+  // matches the nsIDOMCSSStyleRule one for now, so subclasses can just
+  // implement both at once.  The actual implementations must never return
+  // anything other than NS_OK;
+  NS_IMETHOD GetSelectorText(nsAString& aSelectorText) = 0;
+  NS_IMETHOD SetSelectorText(const nsAString& aSelectorText) = 0;
+  virtual nsICSSDeclaration* Style() = 0;
+
+  virtual JSObject* WrapObject(JSContext* aCx,
+                               JS::Handle<JSObject*> aGivenProto) override;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_BindingStyleRule_h__
--- a/layout/style/CSSRuleList.h
+++ b/layout/style/CSSRuleList.h
@@ -2,16 +2,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_CSSRuleList_h
 #define mozilla_dom_CSSRuleList_h
 
 #include "mozilla/StyleSheetInlines.h"
+#include "mozilla/css/Rule.h"
 #include "nsIDOMCSSRule.h"
 #include "nsIDOMCSSRuleList.h"
 #include "nsWrapperCache.h"
 
 namespace mozilla {
 namespace dom {
 
 // IID for the CSSRuleList interface
@@ -40,23 +41,23 @@ public:
   NS_IMETHOD
   Item(uint32_t aIndex, nsIDOMCSSRule** aReturn) override final
   {
     NS_IF_ADDREF(*aReturn = Item(aIndex));
     return NS_OK;
   }
 
   // WebIDL API
-  nsIDOMCSSRule* Item(uint32_t aIndex)
+  css::Rule* Item(uint32_t aIndex)
   {
     bool unused;
     return IndexedGetter(aIndex, unused);
   }
 
-  virtual nsIDOMCSSRule* IndexedGetter(uint32_t aIndex, bool& aFound) = 0;
+  virtual css::Rule* IndexedGetter(uint32_t aIndex, bool& aFound) = 0;
   virtual uint32_t Length() = 0;
 
 protected:
   virtual ~CSSRuleList() {}
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(CSSRuleList, NS_ICSSRULELIST_IID)
 
--- a/layout/style/CSSStyleSheet.cpp
+++ b/layout/style/CSSStyleSheet.cpp
@@ -56,17 +56,17 @@ using namespace mozilla::dom;
 //
 class CSSRuleListImpl final : public CSSRuleList
 {
 public:
   explicit CSSRuleListImpl(CSSStyleSheet *aStyleSheet);
 
   virtual CSSStyleSheet* GetParentObject() override;
 
-  virtual nsIDOMCSSRule*
+  virtual css::Rule*
   IndexedGetter(uint32_t aIndex, bool& aFound) override;
   virtual uint32_t
   Length() override;
 
   void DropReference() { mStyleSheet = nullptr; }
 
 protected:
   virtual ~CSSRuleListImpl();
@@ -96,28 +96,28 @@ CSSRuleListImpl::Length()
 {
   if (!mStyleSheet) {
     return 0;
   }
 
   return AssertedCast<uint32_t>(mStyleSheet->StyleRuleCount());
 }
 
-nsIDOMCSSRule*    
+css::Rule*
 CSSRuleListImpl::IndexedGetter(uint32_t aIndex, bool& aFound)
 {
   aFound = false;
 
   if (mStyleSheet) {
     // ensure rules have correct parent
     mStyleSheet->EnsureUniqueInner();
     css::Rule* rule = mStyleSheet->GetStyleRuleAt(aIndex);
     if (rule) {
       aFound = true;
-      return rule->GetDOMRule();
+      return rule;
     }
   }
 
   // Per spec: "Return Value ... null if ... not a valid index."
   return nullptr;
 }
 
 namespace mozilla {
@@ -504,18 +504,20 @@ CSSStyleSheet::TraverseInner(nsCycleColl
   while (*childSheetSlot) {
     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "child sheet");
     cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIDOMCSSStyleSheet*, childSheetSlot->get()));
     childSheetSlot = &(*childSheetSlot)->mNext;
   }
 
   const nsCOMArray<css::Rule>& rules = mInner->mOrderedRules;
   for (int32_t i = 0, count = rules.Count(); i < count; ++i) {
-    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mOrderedRules[i]");
-    cb.NoteXPCOMChild(rules[i]->GetExistingDOMRule());
+    if (!rules[i]->IsCCLeaf()) {
+      NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mOrderedRules[i]");
+      cb.NoteXPCOMChild(rules[i]);
+    }
   }
 }
 
 // QueryInterface implementation for CSSStyleSheet
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(CSSStyleSheet)
   NS_INTERFACE_MAP_ENTRY(nsICSSLoaderObserver)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, StyleSheet)
   if (aIID.Equals(NS_GET_IID(CSSStyleSheet)))
@@ -868,20 +870,20 @@ CSSStyleSheet::RegisterNamespaceRule(css
     nsresult rv = mInner->CreateNamespaceMap();
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   AddNamespaceRuleToMap(aRule, mInner->mNameSpaceMap);
   return NS_OK;
 }
 
-nsIDOMCSSRule*
+css::Rule*
 CSSStyleSheet::GetDOMOwnerRule() const
 {
-  return mOwnerRule ? mOwnerRule->GetDOMRule() : nullptr;
+  return mOwnerRule;
 }
 
 CSSRuleList*
 CSSStyleSheet::GetCssRulesInternal(ErrorResult& aRv)
 {
   if (!mRuleCollection) {
     mRuleCollection = new CSSRuleListImpl(this);
   }
@@ -1034,21 +1036,16 @@ CSSStyleSheet::DeleteRuleInternal(uint32
 
   NS_ASSERTION(uint32_t(mInner->mOrderedRules.Count()) <= INT32_MAX,
                "Too many style rules!");
 
   // Hold a strong ref to the rule so it doesn't die when we RemoveObjectAt
   RefPtr<css::Rule> rule = mInner->mOrderedRules.ObjectAt(aIndex);
   if (rule) {
     mInner->mOrderedRules.RemoveObjectAt(aIndex);
-    if (mDocument && mDocument->StyleSheetChangeEventsEnabled()) {
-      // Force creation of the DOM rule, so that it can be put on the
-      // StyleRuleRemoved event object.
-      rule->GetDOMRule();
-    }
     rule->SetStyleSheet(nullptr);
     DidDirty();
 
     if (mDocument) {
       mDocument->StyleRuleRemoved(this, rule);
     }
   }
 }
--- a/layout/style/CSSStyleSheet.h
+++ b/layout/style/CSSStyleSheet.h
@@ -190,17 +190,17 @@ public:
   {
     mScopeElement = aScopeElement;
   }
 
   // WebIDL CSSStyleSheet API
   // Can't be inline because we can't include ImportRule here.  And can't be
   // called GetOwnerRule because that would be ambiguous with the ImportRule
   // version.
-  nsIDOMCSSRule* GetDOMOwnerRule() const final;
+  css::Rule* GetDOMOwnerRule() const final;
 
   void WillDirty();
   void DidDirty();
 
 private:
   CSSStyleSheet(const CSSStyleSheet& aCopy,
                 CSSStyleSheet* aParentToUse,
                 css::ImportRule* aOwnerRuleToUse,
--- a/layout/style/Declaration.h
+++ b/layout/style/Declaration.h
@@ -93,16 +93,18 @@ public:
    * |InitializeEmpty| method is called.
    */
   Declaration() : DeclarationBlock(StyleBackendType::Gecko) {}
 
   Declaration(const Declaration& aCopy);
 
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_CSS_DECLARATION_IMPL_CID)
 
+  // If this ever becomes cycle-collected, please change the CC implementation
+  // for StyleRule to traverse it.
   NS_DECL_ISUPPORTS
 
 private:
   ~Declaration();
 
 public:
 
   // nsIStyleRule implementation
--- a/layout/style/GroupRule.h
+++ b/layout/style/GroupRule.h
@@ -7,47 +7,51 @@
  * internal interface representing CSS style rules that contain other
  * rules, such as @media rules
  */
 
 #ifndef mozilla_css_GroupRule_h__
 #define mozilla_css_GroupRule_h__
 
 #include "mozilla/Attributes.h"
+#include "mozilla/ErrorResult.h"
 #include "mozilla/IncrementalClearCOMRuleArray.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/css/Rule.h"
 #include "nsCycleCollectionParticipant.h"
 
 class nsPresContext;
 class nsMediaQueryResultCacheKey;
 
 namespace mozilla {
 
 class StyleSheet;
 
+namespace dom {
+class CSSRuleList;
+} // namespace dom
+
 namespace css {
 
 class GroupRuleRuleList;
 
 // inherits from Rule so it can be shared between
 // MediaRule and DocumentRule
 class GroupRule : public Rule
 {
 protected:
   GroupRule(uint32_t aLineNumber, uint32_t aColumnNumber);
   GroupRule(const GroupRule& aCopy);
   virtual ~GroupRule();
 public:
 
-  NS_DECL_CYCLE_COLLECTION_CLASS(GroupRule)
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(GroupRule, Rule)
+  NS_DECL_ISUPPORTS_INHERITED
+  virtual bool IsCCLeaf() const override;
 
-  // 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(StyleSheet* aSheet) override;
 
 public:
   void AppendStyleRule(Rule* aRule);
 
@@ -75,27 +79,51 @@ public:
   static bool
   CloneRuleInto(Rule* aRule, void* aArray)
   {
     RefPtr<Rule> clone = aRule->Clone();
     static_cast<IncrementalClearCOMRuleArray*>(aArray)->AppendObject(clone);
     return true;
   }
 
+  // WebIDL API
+  dom::CSSRuleList* CssRules();
+  uint32_t InsertRule(const nsAString& aRule, uint32_t aIndex,
+                      ErrorResult& aRv);
+  void DeleteRule(uint32_t aIndex, ErrorResult& aRv);
+
 protected:
   // to help implement nsIDOMCSSRule
-  void AppendRulesToCssText(nsAString& aCssText);
+  void AppendRulesToCssText(nsAString& aCssText) const;
 
   // to implement common methods on nsIDOMCSSMediaRule and
   // nsIDOMCSSMozDocumentRule
   nsresult GetCssRules(nsIDOMCSSRuleList* *aRuleList);
   nsresult InsertRule(const nsAString & aRule, uint32_t aIndex,
                       uint32_t* _retval);
   nsresult DeleteRule(uint32_t aIndex);
 
   IncrementalClearCOMRuleArray mRules;
   RefPtr<GroupRuleRuleList> mRuleCollection; // lazily constructed
 };
 
+// Implementation of WebIDL CSSConditionRule.
+class ConditionRule : public GroupRule
+{
+protected:
+  ConditionRule(uint32_t aLineNumber, uint32_t aColumnNumber);
+  ConditionRule(const ConditionRule& aCopy);
+  virtual ~ConditionRule();
+
+public:
+
+  // GetConditionText signature matches nsIDOMCSSConditionRule, so subclasses
+  // can implement this easily.  The implementations should never return
+  // anything other than NS_OK.
+  NS_IMETHOD GetConditionText(nsAString& aConditionText) = 0;
+  virtual void SetConditionText(const nsAString& aConditionText,
+                                ErrorResult& aRv) = 0;
+};
+
 } // namespace css
 } // namespace mozilla
 
 #endif /* mozilla_css_GroupRule_h__ */
--- a/layout/style/ImportRule.h
+++ b/layout/style/ImportRule.h
@@ -15,54 +15,62 @@
 #include "nsIDOMCSSImportRule.h"
 
 class nsMediaList;
 class nsString;
 
 namespace mozilla {
 
 class CSSStyleSheet;
+class StyleSheet;
 
 namespace css {
 
 class ImportRule final : public Rule,
                          public nsIDOMCSSImportRule
 {
 public:
   ImportRule(nsMediaList* aMedia, const nsString& aURLSpec,
              uint32_t aLineNumber, uint32_t aColumnNumber);
 private:
   // for |Clone|
   ImportRule(const ImportRule& aCopy);
   ~ImportRule();
 public:
-  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(ImportRule, mozilla::css::Rule)
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-
-  DECL_STYLE_RULE_INHERIT
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ImportRule, Rule)
+  NS_DECL_ISUPPORTS_INHERITED
+  virtual bool IsCCLeaf() const override;
 
   using Rule::GetStyleSheet; // unhide since nsIDOMCSSImportRule has its own GetStyleSheet
 
   // Rule methods
 #ifdef DEBUG
   virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
 #endif
   virtual int32_t GetType() const override;
+  using Rule::GetType;
   virtual already_AddRefed<Rule> Clone() const override;
 
   void SetSheet(CSSStyleSheet*);
 
   virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override;
 
-  // nsIDOMCSSRule interface
-  NS_DECL_NSIDOMCSSRULE
+  virtual JSObject* WrapObject(JSContext* aCx,
+                               JS::Handle<JSObject*> aGivenProto) override;
 
   // nsIDOMCSSImportRule interface
   NS_DECL_NSIDOMCSSIMPORTRULE
 
+  // WebIDL interface
+  uint16_t Type() const override;
+  void GetCssTextImpl(nsAString& aCssText) const override;
+  // The XPCOM GetHref is fine, since it never fails.
+  nsMediaList* Media() const { return mMedia; }
+  StyleSheet* GetStyleSheet() const;
+
 private:
   nsString  mURLSpec;
   RefPtr<nsMediaList> mMedia;
   RefPtr<CSSStyleSheet> mChildSheet;
 };
 
 } // namespace css
 } // namespace mozilla
--- a/layout/style/NameSpaceRule.h
+++ b/layout/style/NameSpaceRule.h
@@ -19,48 +19,51 @@ class nsIAtom;
 // IID for the NameSpaceRule class {f0b0dbe1-5031-4a21-b06a-dc141ef2af98}
 #define NS_CSS_NAMESPACE_RULE_IMPL_CID     \
 {0xf0b0dbe1, 0x5031, 0x4a21, {0xb0, 0x6a, 0xdc, 0x14, 0x1e, 0xf2, 0xaf, 0x98}}
 
 
 namespace mozilla {
 namespace css {
 
-class NameSpaceRule final : public Rule,
-                            public nsIDOMCSSRule
+class NameSpaceRule final : public Rule
 {
 public:
   NameSpaceRule(nsIAtom* aPrefix, const nsString& aURLSpec,
                 uint32_t aLineNumber, uint32_t aColumnNumber);
 private:
   // for |Clone|
   NameSpaceRule(const NameSpaceRule& aCopy);
   ~NameSpaceRule();
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_CSS_NAMESPACE_RULE_IMPL_CID)
 
-  NS_DECL_ISUPPORTS
+  NS_DECL_ISUPPORTS_INHERITED
+  virtual bool IsCCLeaf() const override;
 
-  // Rule methods
-  DECL_STYLE_RULE_INHERIT
 #ifdef DEBUG
   virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
 #endif
   virtual int32_t GetType() const override;
+  using Rule::GetType;
   virtual already_AddRefed<Rule> Clone() const override;
 
   nsIAtom* GetPrefix() const { return mPrefix; }
 
   void GetURLSpec(nsString& aURLSpec) const { aURLSpec = mURLSpec; }
 
+  // WebIDL interface
+  uint16_t Type() const override;
+  void GetCssTextImpl(nsAString& aCssText) const override;
+
   virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
     override MOZ_MUST_OVERRIDE;
 
-  // nsIDOMCSSRule interface
-  NS_DECL_NSIDOMCSSRULE
+  virtual JSObject* WrapObject(JSContext* aCx,
+                               JS::Handle<JSObject*> aGivenProto) override;
 
 private:
   nsCOMPtr<nsIAtom> mPrefix;
   nsString          mURLSpec;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(NameSpaceRule, NS_CSS_NAMESPACE_RULE_IMPL_CID)
 
--- a/layout/style/Rule.h
+++ b/layout/style/Rule.h
@@ -7,35 +7,30 @@
 
 #ifndef mozilla_css_Rule_h___
 #define mozilla_css_Rule_h___
 
 #include "mozilla/StyleSheetInlines.h"
 #include "mozilla/MemoryReporting.h"
 #include "nsISupports.h"
 #include "nsIDOMCSSRule.h"
+#include "nsWrapperCache.h"
 
 class nsIDocument;
 struct nsRuleData;
 template<class T> struct already_AddRefed;
 class nsHTMLCSSStyleSheet;
 
 namespace mozilla {
 namespace css {
 class GroupRule;
 
-#define DECL_STYLE_RULE_INHERIT_NO_DOMRULE  \
- /* nothing */
-
-#define DECL_STYLE_RULE_INHERIT                            \
-  DECL_STYLE_RULE_INHERIT_NO_DOMRULE                       \
-  virtual nsIDOMCSSRule* GetDOMRule() override;        \
-  virtual nsIDOMCSSRule* GetExistingDOMRule() override;
-
-class Rule : public nsISupports {
+class Rule : public nsIDOMCSSRule
+           , public nsWrapperCache
+{
 protected:
   Rule(uint32_t aLineNumber, uint32_t aColumnNumber)
     : mSheet(nullptr),
       mParentRule(nullptr),
       mLineNumber(aLineNumber),
       mColumnNumber(aColumnNumber)
   {
   }
@@ -47,16 +42,25 @@ protected:
       mColumnNumber(aCopy.mColumnNumber)
   {
   }
 
   virtual ~Rule() {}
 
 public:
 
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS(Rule)
+  // Return true if this rule is known to be a cycle collection leaf, in the
+  // sense that it doesn't have any outgoing owning edges.
+  virtual bool IsCCLeaf() const MOZ_MUST_OVERRIDE;
+
+  // nsIDOMCSSRule interface
+  NS_DECL_NSIDOMCSSRULE
+
 #ifdef DEBUG
   virtual void List(FILE* out = stdout, int32_t aIndent = 0) const = 0;
 #endif
 
   // The constants in this list must maintain the following invariants:
   //   If a rule of type N must appear before a rule of type M in stylesheets
   //   then N < M
   // Note that CSSStyleSheet::RebuildChildList assumes that no other kinds of
@@ -101,34 +105,34 @@ public:
   uint32_t GetLineNumber() const { return mLineNumber; }
   uint32_t GetColumnNumber() const { return mColumnNumber; }
 
   /**
    * Clones |this|. Never returns nullptr.
    */
   virtual already_AddRefed<Rule> Clone() const = 0;
 
-  // Note that this returns null for inline style rules since they aren't
-  // supposed to have a DOM rule representation (and our code wouldn't work).
-  virtual nsIDOMCSSRule* GetDOMRule() = 0;
-
-  // Like GetDOMRule(), but won't create one if we don't have one yet
-  virtual nsIDOMCSSRule* GetExistingDOMRule() = 0;
-
-  // to implement methods on nsIDOMCSSRule
-  nsresult GetParentRule(nsIDOMCSSRule** aParentRule);
-  nsresult GetParentStyleSheet(nsIDOMCSSStyleSheet** aSheet);
-  Rule* GetCSSRule();
-
   // 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;
 
+  // WebIDL interface, aka helpers for nsIDOMCSSRule implementation.
+  virtual uint16_t Type() const = 0;
+  virtual void GetCssTextImpl(nsAString& aCssText) const = 0;
+  // XPCOM GetCssText is OK, since it never throws.
+  // XPCOM SetCssText is OK, since it never throws.
+  Rule* GetParentRule() const;
+  StyleSheet* GetParentStyleSheet() const { return GetStyleSheet(); }
+  nsIDocument* GetParentObject() const { return GetDocument(); }
+
 protected:
+  // True if we're known-live for cycle collection purposes.
+  bool IsKnownLive() const;
+
   // This is sometimes null (e.g., for style attributes).
   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;
--- a/layout/style/ServoCSSRuleList.cpp
+++ b/layout/style/ServoCSSRuleList.cpp
@@ -41,18 +41,20 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Se
       // during their own unlinking process.
       rule = 0;
     }
   }
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(dom::CSSRuleList)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ServoCSSRuleList,
                                                   dom::CSSRuleList)
   tmp->EnumerateInstantiatedRules([&](css::Rule* aRule) {
-    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mRules[i]");
-    cb.NoteXPCOMChild(aRule->GetExistingDOMRule());
+    if (!aRule->IsCCLeaf()) {
+      NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mRules[i]");
+      cb.NoteXPCOMChild(aRule);
+    }
   });
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 css::Rule*
 ServoCSSRuleList::GetRule(uint32_t aIndex)
 {
   uintptr_t rule = mRules[aIndex];
   if (rule <= kMaxRuleType) {
@@ -74,28 +76,25 @@ ServoCSSRuleList::GetRule(uint32_t aInde
     }
     ruleObj->SetStyleSheet(mStyleSheet);
     rule = CastToUint(ruleObj.forget().take());
     mRules[aIndex] = rule;
   }
   return CastToPtr(rule);
 }
 
-nsIDOMCSSRule*
+css::Rule*
 ServoCSSRuleList::IndexedGetter(uint32_t aIndex, bool& aFound)
 {
   if (aIndex >= mRules.Length()) {
     aFound = false;
     return nullptr;
   }
   aFound = true;
-  if (css::Rule* rule = GetRule(aIndex)) {
-    return rule->GetDOMRule();
-  }
-  return nullptr;
+  return GetRule(aIndex);
 }
 
 template<typename Func>
 void
 ServoCSSRuleList::EnumerateInstantiatedRules(Func aCallback)
 {
   for (uintptr_t rule : mRules) {
     if (rule > kMaxRuleType) {
--- a/layout/style/ServoCSSRuleList.h
+++ b/layout/style/ServoCSSRuleList.h
@@ -25,17 +25,17 @@ public:
   ServoCSSRuleList(ServoStyleSheet* aStyleSheet,
                    already_AddRefed<ServoCssRules> aRawRules);
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServoCSSRuleList, dom::CSSRuleList)
 
   ServoStyleSheet* GetParentObject() final { return mStyleSheet; }
 
-  nsIDOMCSSRule* IndexedGetter(uint32_t aIndex, bool& aFound) final;
+  css::Rule* IndexedGetter(uint32_t aIndex, bool& aFound) final;
   uint32_t Length() final { return mRules.Length(); }
 
   void DropReference();
 
   css::Rule* GetRule(uint32_t aIndex);
   nsresult InsertRule(const nsAString& aRule, uint32_t aIndex);
   nsresult DeleteRule(uint32_t aIndex);
 
--- a/layout/style/ServoStyleRule.cpp
+++ b/layout/style/ServoStyleRule.cpp
@@ -6,18 +6,18 @@
 
 /* representation of CSSStyleRule for stylo */
 
 #include "mozilla/ServoStyleRule.h"
 
 #include "mozilla/DeclarationBlockInlines.h"
 #include "mozilla/ServoBindings.h"
 #include "mozilla/ServoDeclarationBlock.h"
+#include "mozilla/dom/CSSStyleRuleBinding.h"
 
-#include "nsDOMClassInfoID.h"
 #include "mozAutoDocUpdate.h"
 
 namespace mozilla {
 
 // -- ServoStyleRuleDeclaration ---------------------------------------
 
 ServoStyleRuleDeclaration::ServoStyleRuleDeclaration(
   already_AddRefed<RawServoDeclarationBlock> aDecls)
@@ -95,52 +95,64 @@ ServoStyleRuleDeclaration::GetCSSParsing
   CSSParsingEnvironment& aCSSParseEnv)
 {
   GetCSSParsingEnvironmentForRule(Rule(), aCSSParseEnv);
 }
 
 // -- ServoStyleRule --------------------------------------------------
 
 ServoStyleRule::ServoStyleRule(already_AddRefed<RawServoStyleRule> aRawRule)
-  : css::Rule(0, 0)
+  : BindingStyleRule(0, 0)
   , mRawRule(aRawRule)
   , mDecls(Servo_StyleRule_GetStyle(mRawRule).Consume())
 {
 }
 
 // QueryInterface implementation for ServoStyleRule
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServoStyleRule)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(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_INTERFACE_MAP_END_INHERITING(css::Rule)
 
-NS_IMPL_CYCLE_COLLECTING_ADDREF(ServoStyleRule)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(ServoStyleRule)
+NS_IMPL_ADDREF_INHERITED(ServoStyleRule, css::Rule)
+NS_IMPL_RELEASE_INHERITED(ServoStyleRule, css::Rule)
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(ServoStyleRule)
 
-NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(ServoStyleRule)
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(ServoStyleRule, css::Rule)
+  // Keep this in sync with IsCCLeaf.
+
   // 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)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ServoStyleRule, css::Rule)
+  // Keep this in sync with IsCCLeaf.
+
   // 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)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ServoStyleRule, css::Rule)
+  // Keep this in sync with IsCCLeaf.
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
+bool
+ServoStyleRule::IsCCLeaf() const
+{
+  if (!Rule::IsCCLeaf()) {
+    return false;
+  }
+
+  return !mDecls.PreservingWrapper();
+}
+
 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;
@@ -163,53 +175,32 @@ ServoStyleRule::List(FILE* out, int32_t 
   }
   Servo_StyleRule_Debug(mRawRule, &str);
   fprintf_stderr(out, "%s\n", str.get());
 }
 #endif
 
 /* CSSRule implementation */
 
-NS_IMETHODIMP
-ServoStyleRule::GetType(uint16_t* aType)
+uint16_t
+ServoStyleRule::Type() const
 {
-  *aType = nsIDOMCSSRule::STYLE_RULE;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-ServoStyleRule::GetCssText(nsAString& aCssText)
-{
-  Servo_StyleRule_GetCssText(mRawRule, &aCssText);
-  return NS_OK;
+  return nsIDOMCSSRule::STYLE_RULE;
 }
 
-NS_IMETHODIMP
-ServoStyleRule::SetCssText(const nsAString& aCssText)
+void
+ServoStyleRule::GetCssTextImpl(nsAString& aCssText) const
 {
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-ServoStyleRule::GetParentStyleSheet(nsIDOMCSSStyleSheet** aSheet)
-{
-  return css::Rule::GetParentStyleSheet(aSheet);
+  Servo_StyleRule_GetCssText(mRawRule, &aCssText);
 }
 
-NS_IMETHODIMP
-ServoStyleRule::GetParentRule(nsIDOMCSSRule** aParentRule)
+nsICSSDeclaration*
+ServoStyleRule::Style()
 {
-  *aParentRule = nullptr;
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-css::Rule*
-ServoStyleRule::GetCSSRule()
-{
-  return this;
+  return &mDecls;
 }
 
 /* CSSStyleRule implementation */
 
 NS_IMETHODIMP
 ServoStyleRule::GetSelectorText(nsAString& aSelectorText)
 {
   Servo_StyleRule_GetSelectorText(mRawRule, &aSelectorText);
--- a/layout/style/ServoStyleRule.h
+++ b/layout/style/ServoStyleRule.h
@@ -4,17 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* representation of CSSStyleRule for stylo */
 
 #ifndef mozilla_ServoStyleRule_h
 #define mozilla_ServoStyleRule_h
 
-#include "mozilla/css/Rule.h"
+#include "mozilla/BindingStyleRule.h"
 #include "mozilla/ServoBindingTypes.h"
 
 #include "nsIDOMCSSStyleRule.h"
 #include "nsDOMCSSDeclaration.h"
 
 namespace mozilla {
 
 class ServoDeclarationBlock;
@@ -42,35 +42,39 @@ private:
     already_AddRefed<RawServoDeclarationBlock> aDecls);
   ~ServoStyleRuleDeclaration();
 
   inline ServoStyleRule* Rule();
 
   RefPtr<ServoDeclarationBlock> mDecls;
 };
 
-class ServoStyleRule final : public css::Rule
+class ServoStyleRule final : public BindingStyleRule
                            , public nsIDOMCSSStyleRule
 {
 public:
   explicit ServoStyleRule(already_AddRefed<RawServoStyleRule> aRawRule);
 
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(ServoStyleRule,
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(ServoStyleRule,
                                                          css::Rule)
-  NS_DECL_NSIDOMCSSRULE
+  virtual bool IsCCLeaf() const override MOZ_MUST_OVERRIDE;
   NS_DECL_NSIDOMCSSSTYLERULE
 
+  // WebIDL interface
+  uint16_t Type() const override;
+  void GetCssTextImpl(nsAString& aCssText) const override;
+  virtual nsICSSDeclaration* Style() override;
+
   RawServoStyleRule* Raw() const { return mRawRule; }
 
   // Methods of mozilla::css::Rule
   int32_t GetType() const final { return css::Rule::STYLE_RULE; }
+  using Rule::GetType;
   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() {}
 
--- a/layout/style/ServoStyleSheet.cpp
+++ b/layout/style/ServoStyleSheet.cpp
@@ -144,19 +144,20 @@ ServoStyleSheet::SizeOfIncludingThis(Mal
 #ifdef DEBUG
 void
 ServoStyleSheet::List(FILE* aOut, int32_t aIndex) const
 {
   MOZ_CRASH("stylo: not implemented");
 }
 #endif
 
-nsIDOMCSSRule*
+css::Rule*
 ServoStyleSheet::GetDOMOwnerRule() const
 {
+  NS_ERROR("stylo: Don't know how to get DOM owner rule for ServoStyleSheet");
   return nullptr;
 }
 
 CSSRuleList*
 ServoStyleSheet::GetCssRulesInternal(ErrorResult& aRv)
 {
   if (!mRuleList) {
     RefPtr<ServoCssRules> rawRules = Servo_StyleSheet_GetRules(mSheet).Consume();
--- a/layout/style/ServoStyleSheet.h
+++ b/layout/style/ServoStyleSheet.h
@@ -15,16 +15,17 @@
 #include "nsStringFwd.h"
 
 namespace mozilla {
 
 class ServoCSSRuleList;
 
 namespace css {
 class Loader;
+class Rule;
 }
 
 /**
  * CSS style sheet object that is a wrapper for a Servo Stylesheet.
  */
 class ServoStyleSheet : public StyleSheet
 {
 public:
@@ -69,17 +70,17 @@ public:
     MOZ_ASSERT(!mSheet);
     mSheet = aSheet;
   }
 
   // WebIDL CSSStyleSheet API
   // Can't be inline because we can't include ImportRule here.  And can't be
   // called GetOwnerRule because that would be ambiguous with the ImportRule
   // version.
-  nsIDOMCSSRule* GetDOMOwnerRule() const final;
+  css::Rule* GetDOMOwnerRule() const final;
 
   void WillDirty() {}
   void DidDirty() {}
 
 protected:
   virtual ~ServoStyleSheet();
 
   // Internal methods which do not have security check and completeness check.
--- a/layout/style/StyleRule.cpp
+++ b/layout/style/StyleRule.cpp
@@ -11,28 +11,27 @@
 
 #include "mozilla/css/StyleRule.h"
 
 #include "mozilla/DeclarationBlockInlines.h"
 #include "mozilla/StyleSheetInlines.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/css/GroupRule.h"
 #include "mozilla/css/Declaration.h"
+#include "mozilla/dom/CSSStyleRuleBinding.h"
 #include "nsIDocument.h"
 #include "nsIAtom.h"
 #include "nsString.h"
 #include "nsStyleUtil.h"
-#include "nsICSSStyleRuleDOMWrapper.h"
 #include "nsDOMCSSDeclaration.h"
 #include "nsNameSpaceManager.h"
 #include "nsXMLNameSpaceMap.h"
 #include "nsCSSPseudoClasses.h"
 #include "nsCSSAnonBoxes.h"
 #include "nsTArray.h"
-#include "nsDOMClassInfoID.h"
 #include "nsContentUtils.h"
 #include "nsError.h"
 #include "mozAutoDocUpdate.h"
 
 class nsIDOMCSSStyleDeclaration;
 class nsIDOMCSSStyleSheet;
 
 using namespace mozilla;
@@ -1042,166 +1041,96 @@ nsCSSSelectorList::SizeOfIncludingThis(m
     n += s->mSelectors ? s->mSelectors->SizeOfIncludingThis(aMallocSizeOf) : 0;
     s = s->mNext;
   }
   return n;
 }
 
 // --------------------------------------------------------
 
-namespace mozilla {
-namespace css {
-class DOMCSSStyleRule;
-} // namespace css
-} // namespace mozilla
-
 class DOMCSSDeclarationImpl : public nsDOMCSSDeclaration
 {
 protected:
+  // Needs to be protected so we can use NS_IMPL_ADDREF_USING_AGGREGATOR.
   virtual ~DOMCSSDeclarationImpl(void);
 
+  // But we need to allow UniquePtr to delete us.
+  friend class mozilla::DefaultDelete<DOMCSSDeclarationImpl>;
+
 public:
   explicit DOMCSSDeclarationImpl(css::StyleRule *aRule);
 
   NS_IMETHOD GetParentRule(nsIDOMCSSRule **aParent) override;
-  void DropReference(void);
   virtual DeclarationBlock* GetCSSDeclaration(Operation aOperation) override;
   virtual nsresult SetCSSDeclaration(DeclarationBlock* aDecl) override;
   virtual void GetCSSParsingEnvironment(CSSParsingEnvironment& aCSSParseEnv) override;
   virtual nsIDocument* DocToUpdate() override;
 
-  // Override |AddRef| and |Release| for being a member of
-  // |DOMCSSStyleRule|.  Also, we need to forward QI for cycle
-  // collection things to DOMCSSStyleRule.
+  // Override |AddRef| and |Release| for being owned by StyleRule.  Also, we
+  // need to forward QI for cycle collection things to StyleRule.
   NS_DECL_ISUPPORTS_INHERITED
 
   virtual nsINode *GetParentObject() override
   {
     return mRule ? mRule->GetDocument() : nullptr;
   }
 
-  friend class css::DOMCSSStyleRule;
-
 protected:
-  // This reference is not reference-counted. The rule object tells us
-  // when it's about to go away.
+  // This reference is not reference-counted. The rule object owns us and we go
+  // away when it does.
   css::StyleRule *mRule;
-
-  inline css::DOMCSSStyleRule* DomRule();
-
-private:
-  // NOT TO BE IMPLEMENTED
-  // This object cannot be allocated on its own.  It must be a member of
-  // DOMCSSStyleRule.
-  void* operator new(size_t size) CPP_THROW_NEW;
 };
 
-namespace mozilla {
-namespace css {
-
-class DOMCSSStyleRule : public nsICSSStyleRuleDOMWrapper
-{
-public:
-  explicit DOMCSSStyleRule(StyleRule *aRule);
-
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DOMCSSStyleRule)
-  NS_DECL_NSIDOMCSSRULE
-  NS_DECL_NSIDOMCSSSTYLERULE
-
-  // nsICSSStyleRuleDOMWrapper
-  NS_IMETHOD GetCSSStyleRule(StyleRule **aResult) override;
-
-  DOMCSSDeclarationImpl* DOMDeclaration() { return &mDOMDeclaration; }
-
-  friend class ::DOMCSSDeclarationImpl;
-
-protected:
-  virtual ~DOMCSSStyleRule();
-
-  DOMCSSDeclarationImpl mDOMDeclaration;
-
-  StyleRule* Rule() {
-    return mDOMDeclaration.mRule;
-  }
-};
-
-} // namespace css
-} // namespace mozilla
-
 DOMCSSDeclarationImpl::DOMCSSDeclarationImpl(css::StyleRule *aRule)
   : mRule(aRule)
 {
 }
 
 DOMCSSDeclarationImpl::~DOMCSSDeclarationImpl(void)
 {
-  NS_ASSERTION(!mRule, "DropReference not called.");
 }
 
-inline css::DOMCSSStyleRule* DOMCSSDeclarationImpl::DomRule()
-{
-  return reinterpret_cast<css::DOMCSSStyleRule*>
-                         (reinterpret_cast<char*>(this) -
-           offsetof(css::DOMCSSStyleRule, mDOMDeclaration));
-}
-
-NS_IMPL_ADDREF_USING_AGGREGATOR(DOMCSSDeclarationImpl, DomRule())
-NS_IMPL_RELEASE_USING_AGGREGATOR(DOMCSSDeclarationImpl, DomRule())
+NS_IMPL_ADDREF_USING_AGGREGATOR(DOMCSSDeclarationImpl, mRule)
+NS_IMPL_RELEASE_USING_AGGREGATOR(DOMCSSDeclarationImpl, mRule)
 
 NS_INTERFACE_MAP_BEGIN(DOMCSSDeclarationImpl)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
-  // We forward the cycle collection interfaces to DomRule(), which is
-  // never null (in fact, we're part of that object!)
+  // We forward the cycle collection interfaces to mRule, which is
+  // never null.
   if (aIID.Equals(NS_GET_IID(nsCycleCollectionISupports)) ||
       aIID.Equals(NS_GET_IID(nsXPCOMCycleCollectionParticipant))) {
-    return DomRule()->QueryInterface(aIID, aInstancePtr);
+    return mRule->QueryInterface(aIID, aInstancePtr);
   }
   else
 NS_IMPL_QUERY_TAIL_INHERITING(nsDOMCSSDeclaration)
 
-void
-DOMCSSDeclarationImpl::DropReference(void)
-{
-  mRule = nullptr;
-}
-
 DeclarationBlock*
 DOMCSSDeclarationImpl::GetCSSDeclaration(Operation aOperation)
 {
-  if (mRule) {
-    if (aOperation != eOperation_Read) {
-      RefPtr<CSSStyleSheet> sheet = mRule->GetStyleSheet();
-      if (sheet) {
-        sheet->WillDirty();
-      }
+  if (aOperation != eOperation_Read) {
+    RefPtr<CSSStyleSheet> sheet = mRule->GetStyleSheet();
+    if (sheet) {
+      sheet->WillDirty();
     }
-    return mRule->GetDeclaration();
-  } else {
-    return nullptr;
   }
+  return mRule->GetDeclaration();
 }
 
 void
 DOMCSSDeclarationImpl::GetCSSParsingEnvironment(CSSParsingEnvironment& aCSSParseEnv)
 {
   GetCSSParsingEnvironmentForRule(mRule, aCSSParseEnv);
 }
 
 NS_IMETHODIMP
 DOMCSSDeclarationImpl::GetParentRule(nsIDOMCSSRule **aParent)
 {
   NS_ENSURE_ARG_POINTER(aParent);
 
-  if (!mRule) {
-    *aParent = nullptr;
-    return NS_OK;
-  }
-
-  NS_IF_ADDREF(*aParent = mRule->GetDOMRule());
+  NS_IF_ADDREF(*aParent = mRule);
   return NS_OK;
 }
 
 nsresult
 DOMCSSDeclarationImpl::SetCSSDeclaration(DeclarationBlock* aDecl)
 {
   NS_PRECONDITION(mRule,
          "can only be called when |GetCSSDeclaration| returned a declaration");
@@ -1227,239 +1156,149 @@ DOMCSSDeclarationImpl::SetCSSDeclaration
 }
 
 nsIDocument*
 DOMCSSDeclarationImpl::DocToUpdate()
 {
   return nullptr;
 }
 
-namespace mozilla {
-namespace css {
-
-DOMCSSStyleRule::DOMCSSStyleRule(StyleRule* aRule)
-  : mDOMDeclaration(aRule)
-{
-}
-
-DOMCSSStyleRule::~DOMCSSStyleRule()
-{
-}
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMCSSStyleRule)
-  NS_INTERFACE_MAP_ENTRY(nsICSSStyleRuleDOMWrapper)
-  NS_INTERFACE_MAP_ENTRY(nsIDOMCSSStyleRule)
-  NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule)
-  NS_INTERFACE_MAP_ENTRY(nsISupports)
-  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSStyleRule)
-NS_INTERFACE_MAP_END
-
-NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMCSSStyleRule)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMCSSStyleRule)
-
-NS_IMPL_CYCLE_COLLECTION_CLASS(DOMCSSStyleRule)
-
-NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(DOMCSSStyleRule)
-  // 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->DOMDeclaration()->TraceWrapper(aCallbacks, aClosure);
-NS_IMPL_CYCLE_COLLECTION_TRACE_END
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMCSSStyleRule)
-  // 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->DOMDeclaration()->ReleaseWrapper(static_cast<nsISupports*>(p));
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMCSSStyleRule)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-
-NS_IMETHODIMP
-DOMCSSStyleRule::GetType(uint16_t* aType)
-{
-  *aType = nsIDOMCSSRule::STYLE_RULE;
-  
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-DOMCSSStyleRule::GetCssText(nsAString& aCssText)
-{
-  if (!Rule()) {
-    aCssText.Truncate();
-  } else {
-    Rule()->GetCssText(aCssText);
-  }
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-DOMCSSStyleRule::SetCssText(const nsAString& aCssText)
-{
-  if (Rule()) {
-    Rule()->SetCssText(aCssText);
-  }
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-DOMCSSStyleRule::GetParentStyleSheet(nsIDOMCSSStyleSheet** aSheet)
-{
-  if (!Rule()) {
-    *aSheet = nullptr;
-    return NS_OK;
-  }
-  return Rule()->GetParentStyleSheet(aSheet);
-}
-
-NS_IMETHODIMP
-DOMCSSStyleRule::GetParentRule(nsIDOMCSSRule** aParentRule)
-{
-  if (!Rule()) {
-    *aParentRule = nullptr;
-    return NS_OK;
-  }
-  return Rule()->GetParentRule(aParentRule);
-}
-
-css::Rule*
-DOMCSSStyleRule::GetCSSRule()
-{
-  return Rule();
-}
-
-NS_IMETHODIMP
-DOMCSSStyleRule::GetSelectorText(nsAString& aSelectorText)
-{
-  if (!Rule()) {
-    aSelectorText.Truncate();
-  } else {
-    Rule()->GetSelectorText(aSelectorText);
-  }
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-DOMCSSStyleRule::SetSelectorText(const nsAString& aSelectorText)
-{
-  if (Rule()) {
-    Rule()->SetSelectorText(aSelectorText);
-  }
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-DOMCSSStyleRule::GetStyle(nsIDOMCSSStyleDeclaration** aStyle)
-{
-  *aStyle = &mDOMDeclaration;
-  NS_ADDREF(*aStyle);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-DOMCSSStyleRule::GetCSSStyleRule(StyleRule **aResult)
-{
-  *aResult = Rule();
-  NS_IF_ADDREF(*aResult);
-  return NS_OK;
-}
-
-} // namespace css
-} // namespace mozilla
-
 // -- StyleRule ------------------------------------
 
 namespace mozilla {
 namespace css {
 
+uint16_t
+StyleRule::Type() const
+{
+  return nsIDOMCSSRule::STYLE_RULE;
+}
+
+NS_IMETHODIMP
+StyleRule::GetStyle(nsIDOMCSSStyleDeclaration** aStyle)
+{
+  NS_ADDREF(*aStyle = Style());
+  return NS_OK;
+}
+
+nsICSSDeclaration*
+StyleRule::Style()
+{
+  if (!mDOMDeclaration) {
+    mDOMDeclaration.reset(new DOMCSSDeclarationImpl(this));
+  }
+  return mDOMDeclaration.get();
+}
+
+NS_IMETHODIMP
+StyleRule::GetCSSStyleRule(StyleRule **aResult)
+{
+  *aResult = this;
+  NS_ADDREF(*aResult);
+  return NS_OK;
+}
+
 StyleRule::StyleRule(nsCSSSelectorList* aSelector,
                      Declaration* aDeclaration,
                      uint32_t aLineNumber,
                      uint32_t aColumnNumber)
-  : Rule(aLineNumber, aColumnNumber),
+  : BindingStyleRule(aLineNumber, aColumnNumber),
     mSelector(aSelector),
     mDeclaration(aDeclaration)
 {
   NS_PRECONDITION(aDeclaration, "must have a declaration");
 
   mDeclaration->SetOwningRule(this);
 }
 
 // for |Clone|
 StyleRule::StyleRule(const StyleRule& aCopy)
-  : Rule(aCopy),
+  : BindingStyleRule(aCopy),
     mSelector(aCopy.mSelector ? aCopy.mSelector->Clone() : nullptr),
     mDeclaration(new Declaration(*aCopy.mDeclaration))
 {
   mDeclaration->SetOwningRule(this);
   // rest is constructed lazily on existing data
 }
 
 StyleRule::~StyleRule()
 {
   delete mSelector;
-  if (mDOMRule) {
-    mDOMRule->DOMDeclaration()->DropReference();
-  }
+  DropReferences();
+}
 
+void
+StyleRule::DropReferences()
+{
   if (mDeclaration) {
     mDeclaration->SetOwningRule(nullptr);
   }
 }
 
 // QueryInterface implementation for StyleRule
-NS_INTERFACE_MAP_BEGIN(StyleRule)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(StyleRule)
   if (aIID.Equals(NS_GET_IID(mozilla::css::StyleRule))) {
     *aInstancePtr = this;
     NS_ADDREF_THIS();
     return NS_OK;
   }
   else
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozilla::css::Rule)
-NS_INTERFACE_MAP_END
+  NS_INTERFACE_MAP_ENTRY(nsICSSStyleRuleDOMWrapper)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMCSSStyleRule)
+NS_INTERFACE_MAP_END_INHERITING(Rule)
+
+NS_IMPL_ADDREF_INHERITED(StyleRule, Rule)
+NS_IMPL_RELEASE_INHERITED(StyleRule, Rule)
 
-NS_IMPL_ADDREF(StyleRule)
-NS_IMPL_RELEASE(StyleRule)
+NS_IMPL_CYCLE_COLLECTION_CLASS(StyleRule)
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(StyleRule, Rule)
+  // Keep this in sync with IsCCLeaf.
+  // 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.
+  if (tmp->mDOMDeclaration) {
+    tmp->mDOMDeclaration->TraceWrapper(aCallbacks, aClosure);
+  }
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(StyleRule, Rule)
+  // 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.
+  if (tmp->mDOMDeclaration) {
+    tmp->mDOMDeclaration->ReleaseWrapper(static_cast<nsISupports*>(p));
+  }
+  tmp->DropReferences();
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(StyleRule, Rule)
+  // Keep this in sync with IsCCLeaf.
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+bool
+StyleRule::IsCCLeaf() const
+{
+  if (!Rule::IsCCLeaf()) {
+    return false;