Merge m-i to m-c, a=merge
authorPhil Ringnalda <philringnalda@gmail.com>
Fri, 20 Jan 2017 19:00:04 -0800
changeset 464601 487a4e43eb9d1f04a5d8e3dd183fe38dbe105e1f
parent 464600 41d8ef56d03b9b5b382d0575def6712d9d51b1df (current diff)
parent 464496 24e88a2d883fff700725490665bce10aeb303551 (diff)
child 464602 31a1dec740d5f164bc3edc0bba46974cdfa422f5
child 464634 09223e98b27936eeb51a489849d7f9f34d980702
child 464635 6ffe8384c6fcc107f3ad35efa5fb861fdf02558e
child 464656 1072a014088adc1353f19c4c659fc98a2e6dbe9c
child 464678 cf9efdba5e2401f1bde0654579bb63a30b958b72
child 464733 cfb667d165ed96952b7c421770fffe7a072e1db9
child 464745 6b1643e1a93d9f4e3e348e70938777eaf298a4fc
child 464763 dbf1eddbe8b324f522906c6994671480bbaf74db
child 464765 15f73eaddbfa5622953ddbb5d921c8f9c31a78a2
child 464773 0a7ff313f23c34b5e78130c195df81ede529576a
child 464774 50bb656cbb852784d9726cc3bb5d9f54606d1a22
child 464811 87f47f3fdcbb14d4f85571f00630ac3847738e46
child 465165 3bfb3958d661e0bd244c0572696924154e86aac3
child 465736 c3b4591dcc5d295aa2319192341999dee8a16e16
push id42370
push usermwein@mozilla.com
push dateSat, 21 Jan 2017 03:10:58 +0000
reviewersmerge
milestone53.0a1
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;
+  }
+
+  return !mDOMDeclaration || !mDOMDeclaration->PreservingWrapper();
+}
 
 /* virtual */ int32_t
 StyleRule::GetType() const
 {
   return Rule::STYLE_RULE;
 }
 
 /* virtual */ already_AddRefed<Rule>
 StyleRule::Clone() const
 {
   RefPtr<Rule> clone = new StyleRule(*this);
   return clone.forget();
 }
 
-/* virtual */ nsIDOMCSSRule*
-StyleRule::GetDOMRule()
-{
-  if (!mDOMRule) {
-    if (!GetStyleSheet()) {
-      // Inline style rules aren't supposed to have a DOM rule object, only
-      // a declaration.  But if we do have one already, from a style sheet
-      // rule that used to be in a document, we still want to return it.
-      return nullptr;
-    }
-    mDOMRule = new DOMCSSStyleRule(this);
-  }
-  return mDOMRule;
-}
-
-/* virtual */ nsIDOMCSSRule*
-StyleRule::GetExistingDOMRule()
-{
-  return mDOMRule;
-}
-
 void
 StyleRule::SetDeclaration(Declaration* aDecl)
 {
   if (aDecl == mDeclaration) {
     return;
   }
   mDeclaration->SetOwningRule(nullptr);
   mDeclaration = aDecl;
@@ -1505,17 +1344,17 @@ StyleRule::List(FILE* out, int32_t aInde
     str.AppendLiteral("{ null declaration }");
   }
   str.Append('\n');
   fprintf_stderr(out, "%s", str.get());
 }
 #endif
 
 void
-StyleRule::GetCssText(nsAString& aCssText)