Merge m-i to m-c, a=merge
authorPhil Ringnalda <philringnalda@gmail.com>
Fri, 30 Sep 2016 23:24:52 -0700
changeset 316107 fcc62bbf09eecabf27c8d198d5d3719e16b296ea
parent 316030 b0706e5d7ae3ca450ce1017dcc17cefdf0b0a08e (current diff)
parent 316106 c8b0d217e5a4a903975eb2a104a3fb5e56899e6f (diff)
child 316119 87cd291d2db621da6b3eb1057027cc0725b6eb1d
push id30759
push userphilringnalda@gmail.com
push dateSat, 01 Oct 2016 06:25:09 +0000
treeherdermozilla-central@fcc62bbf09ee [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone52.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-i to m-c, a=merge
CLOBBER
dom/apps/tests/addons/application.zip
dom/apps/tests/addons/index.html
dom/apps/tests/addons/invalid.webapp
dom/apps/tests/addons/invalid.webapp^headers^
dom/apps/tests/addons/manifest.json
dom/apps/tests/addons/script.js
dom/apps/tests/addons/script2.js
dom/apps/tests/addons/style.css
dom/apps/tests/addons/style2.css
dom/apps/tests/addons/update.webapp
dom/apps/tests/addons/update.webapp^headers^
dom/apps/tests/test_app_addons.html
dom/apps/tests/test_app_blocklist.html
mobile/android/base/moz.build
taskcluster/ci/spidermonkey/kind.yml
widget/nsBaseWidget.cpp
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Merge day clobber
\ No newline at end of file
+Bug 1280590 - xpcshell failure in windows7
--- a/accessible/base/EventTree.cpp
+++ b/accessible/base/EventTree.cpp
@@ -479,16 +479,69 @@ EventTree::Mutated(AccMutationEvent* aEv
 {
   // If shown or hidden node is a root of previously mutated subtree, then
   // discard those subtree mutations as we are no longer interested in them.
   UniquePtr<EventTree>* node = &mFirst;
   while (*node) {
     Accessible* cntr = (*node)->mContainer;
     while (cntr != mContainer) {
       if (cntr == aEv->mAccessible) {
+#ifdef A11Y_LOG
+        if (logging::IsEnabled(logging::eEventTree)) {
+          logging::MsgBegin("EVENTS_TREE", "Trim subtree");
+          logging::AccessibleInfo("Show/hide container", aEv->mAccessible);
+          logging::AccessibleInfo("Trimmed subtree root", (*node)->mContainer);
+          logging::MsgEnd();
+        }
+#endif
+
+        // If the new hide is part of a move and it contains existing child
+        // shows, then move preceding events from the child shows to the buffer,
+        // so the ongoing show event will pick them up.
+        if (aEv->IsHide()) {
+          AccHideEvent* hideEv = downcast_accEvent(aEv);
+          if (!hideEv->mNeedsShutdown) {
+            for (uint32_t i = 0; i < (*node)->mDependentEvents.Length(); i++) {
+              AccMutationEvent* childEv = (*node)->mDependentEvents[i];
+              if (childEv->IsShow()) {
+                AccShowEvent* childShowEv = downcast_accEvent(childEv);
+                if (childShowEv->mPrecedingEvents.Length() > 0) {
+                  Controller(mContainer)->StorePrecedingEvents(
+                    mozilla::Move(childShowEv->mPrecedingEvents));
+                }
+              }
+            }
+          }
+        }
+        // If the new show contains existing child shows, then move preceding
+        // events from the child shows to the new show.
+        else if (aEv->IsShow()) {
+          AccShowEvent* showEv = downcast_accEvent(aEv);
+          for (uint32_t i = 0; (*node)->mDependentEvents.Length(); i++) {
+            AccMutationEvent* childEv = (*node)->mDependentEvents[i];
+            if (childEv->IsShow()) {
+              AccShowEvent* showChildEv = downcast_accEvent(childEv);
+              if (showChildEv->mPrecedingEvents.Length() > 0) {
+#ifdef A11Y_LOG
+                if (logging::IsEnabled(logging::eEventTree)) {
+                  logging::MsgBegin("EVENTS_TREE", "Adopt preceding events");
+                  logging::AccessibleInfo("Parent", aEv->mAccessible);
+                  for (uint32_t j = 0; j < showChildEv->mPrecedingEvents.Length(); j++) {
+                    logging::AccessibleInfo("Adoptee",
+                      showChildEv->mPrecedingEvents[i]->mAccessible);
+                  }
+                  logging::MsgEnd();
+                }
+#endif
+                showEv->mPrecedingEvents.AppendElements(showChildEv->mPrecedingEvents);
+              }
+            }
+          }
+        }
+
         *node = Move((*node)->mNext);
         break;
       }
       cntr = cntr->Parent();
     }
     if (cntr == aEv->mAccessible) {
       continue;
     }
--- a/accessible/tests/mochitest/events/test_coalescence.html
+++ b/accessible/tests/mochitest/events/test_coalescence.html
@@ -553,16 +553,62 @@
         getNode('t8_c1').setAttribute('aria-owns', 't8_c2_moved');
       };
 
       this.getID = function test8_getID() {
         return "Move a node by aria-owns to left within the tree";
       };
     }
 
+    /**
+     * Move 't9_c2_moved' node by aria-owns, and then move 't9_c3_moved' node
+     * under 't9_c2_moved' (same as test9 but has different aria-owns
+     * ordering), the eventing looks same way as in test9:
+     * reorder for 't9_c1'
+     *   hide for 't9_c1_child'
+     *   show for 't9_c2_moved'
+     * reorder for 't9_c2'
+     *   hide for 't9_c2_moved'
+     * reorder for 't9_c3'
+     *   hide for 't9_c3_moved'
+     *
+     * The hide events for 't9_c2_moved' and 't9_c3_moved' should be delivered
+     * before the show event for 't9_c2_moved'.
+     */
+    function test9()
+    {
+      this.eventSeq = [
+        new invokerChecker(EVENT_HIDE, getNode('t9_c1_child')),
+        new invokerChecker(EVENT_HIDE, 't9_c3_moved'),
+        new invokerChecker(EVENT_HIDE, 't9_c2_moved'),
+        new invokerChecker(EVENT_SHOW, 't9_c2_moved'),
+        new invokerChecker(EVENT_REORDER, 't9_c1'),
+        new invokerChecker(EVENT_HIDE, getNode('t9_c2_child')),
+        new invokerChecker(EVENT_REORDER, 't9_c2'),
+        new invokerChecker(EVENT_REORDER, 't9_c3'),
+        new unexpectedInvokerChecker(EVENT_SHOW, 't9_c3_moved')
+      ];
+
+      this.invoke = function test9_invoke()
+      {
+        // Remove child nodes from 't9_c1' and 't9_c2' containers to give
+        // the event tree a needed structure ('t9_c1' and 't9_c2' nodes go
+        // first in the event tree),
+        getNode('t9_c1_child').remove();
+        getNode('t9_c2_child').remove();
+        // then do aria-owns magic.
+        getNode('t9_c2_moved').setAttribute('aria-owns', 't9_c3_moved');
+        getNode('t9_c1').setAttribute('aria-owns', 't9_c2_moved');
+      };
+
+      this.getID = function test9_getID() {
+        return "Move node #1 by aria-owns and then move node #2 into node #1";
+      };
+    }
+
     ////////////////////////////////////////////////////////////////////////////
     // Do tests.
 
     //gA11yEventDumpToConsole = true; // debug stuff
     //enableLogging("eventTree");
 
     var gQueue = null;
     function doTests()
@@ -587,16 +633,17 @@
 
       gQueue.push(new removeGrandChildrenNHideParent("t1_child1", "t1_child2", "t1_parent"));
       gQueue.push(new test3());
       gQueue.push(new test4());
       gQueue.push(new test5());
       gQueue.push(new test6());
       gQueue.push(new test7());
       gQueue.push(new test8());
+      gQueue.push(new test9());
 
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTests);
   </script>
 </head>
@@ -694,10 +741,21 @@
   </div>
 
   <div id="t8">
     <div id="t8_c1"><div id="t8_c1_child"></div></div>
     <div id="t8_c2">
       <div id="t8_c2_moved"></div>
     </div>
   </div>
+
+  <div id="t9">
+    <div id="t9_c1"><div id="t9_c1_child"></div></div>
+    <div id="t9_c2">
+      <div id="t9_c2_child"></div>
+      <div id="t9_c2_moved"></div>
+    </div>
+    <div id="t9_c3">
+      <div id="t9_c3_moved"></div>
+    </div>
+  </div>
 </body>
 </html>
--- a/addon-sdk/source/lib/sdk/panel/utils.js
+++ b/addon-sdk/source/lib/sdk/panel/utils.js
@@ -101,18 +101,20 @@ function close(panel) {
   return panel.hidePopup && panel.hidePopup();
 }
 exports.close = close
 
 
 function resize(panel, width, height) {
   // Resize the iframe instead of using panel.sizeTo
   // because sizeTo doesn't work with arrow panels
-  panel.firstChild.style.width = width + "px";
-  panel.firstChild.style.height = height + "px";
+  if (panel.firstChild) {
+    panel.firstChild.style.width = width + "px";
+    panel.firstChild.style.height = height + "px";
+  }
 }
 exports.resize = resize
 
 function display(panel, options, anchor) {
   let document = panel.ownerDocument;
 
   let x, y;
   let { width, height, defaultWidth, defaultHeight } = options;
@@ -182,22 +184,24 @@ function display(panel, options, anchor)
     popupPosition = vertical + "center " + verticalInverse + horizontal;
 
     // Allow panel to flip itself if the panel can't be displayed at the
     // specified position (useful if we compute a bad position or if the
     // user moves the window and panel remains visible)
     panel.setAttribute("flip", "both");
   }
 
-  panel.viewFrame = document.importNode(panel.backgroundFrame, false);
-  panel.appendChild(panel.viewFrame);
+  if (!panel.viewFrame) {
+    panel.viewFrame = document.importNode(panel.backgroundFrame, false);
+    panel.appendChild(panel.viewFrame);
 
-  let {privateBrowsingId} = getDocShell(panel.viewFrame).getOriginAttributes();
-  let principal = Services.scriptSecurityManager.createNullPrincipal({privateBrowsingId});
-  getDocShell(panel.viewFrame).createAboutBlankContentViewer(principal);
+    let {privateBrowsingId} = getDocShell(panel.viewFrame).getOriginAttributes();
+    let principal = Services.scriptSecurityManager.createNullPrincipal({privateBrowsingId});
+    getDocShell(panel.viewFrame).createAboutBlankContentViewer(principal);
+  }
 
   // Resize the iframe instead of using panel.sizeTo
   // because sizeTo doesn't work with arrow panels
   panel.firstChild.style.width = width + "px";
   panel.firstChild.style.height = height + "px";
 
   panel.openPopup(anchor, popupPosition, x, y);
 }
--- a/addon-sdk/source/test/test-panel.js
+++ b/addon-sdk/source/test/test-panel.js
@@ -249,16 +249,20 @@ exports["test Resize Panel"] = function(
 
     let panel = Panel({
       contentScript: "self.postMessage('')",
       contentScriptWhen: "end",
       contentURL: "data:text/html;charset=utf-8,",
       height: 10,
       width: 10,
       onMessage: function (message) {
+        // Make sure that attempting to resize a panel while it isn't
+        // visible doesn't cause an error.
+        panel.resize(1, 1);
+
         panel.show();
       },
       onShow: function () {
         panel.resize(100,100);
         panel.hide();
       },
       onHide: function () {
         assert.ok((panel.width == 100) && (panel.height == 100),
@@ -330,17 +334,17 @@ exports["test Several Show Hides"] = fun
     }
   });
   panel.on('error', function(e) {
     assert.fail('error was emitted:' + e.message + '\n' + e.stack);
   });
   panel.show();
 };
 
-exports["test Anchor And Arrow"] = function(assert, done) {
+exports["test Anchor And Arrow"] = function*(assert, done) {
   let { loader } = LoaderWithHookedConsole(module, ignorePassingDOMNodeWarning);
   let { Panel } = loader.require('sdk/panel');
 
   let count = 0;
   let url = 'data:text/html;charset=utf-8,' +
     '<html><head><title>foo</title></head><body>' +
     '</body></html>';
 
--- a/browser/components/customizableui/CustomizeMode.jsm
+++ b/browser/components/customizableui/CustomizeMode.jsm
@@ -148,20 +148,18 @@ CustomizeMode.prototype = {
       this.enter();
     }
   },
 
   swatchForTheme: function(aDocument) {
    let lwthemeButton = aDocument.getElementById("customization-lwtheme-button");
    let lwthemeIcon = aDocument.getAnonymousElementByAttribute(lwthemeButton,
           "class", "button-icon");
-   let imageURL = LightweightThemeManager.currentTheme === null ?
-          "chrome://browser/skin/theme-switcher-icon.png" :
-          LightweightThemeManager.currentTheme.iconURL;
-    lwthemeIcon.style.backgroundImage = "url(" + imageURL + ")";
+   lwthemeIcon.style.backgroundImage = LightweightThemeManager.currentTheme ?
+     "url(" + LightweightThemeManager.currentTheme.iconURL + ")" : "";
   },
 
   setTab: function(aTab) {
     if (gTab == aTab) {
       return;
     }
 
     if (gTab) {
--- a/browser/components/sessionstore/SessionCookies.jsm
+++ b/browser/components/sessionstore/SessionCookies.jsm
@@ -110,24 +110,25 @@ var SessionCookiesInternal = {
 
     return hosts;
   },
 
   /**
    * Restores a given list of session cookies.
    */
   restore(cookies) {
+
     for (let cookie of cookies) {
       let expiry = "expiry" in cookie ? cookie.expiry : MAX_EXPIRY;
       let cookieObj = {
         host: cookie.host,
         path: cookie.path || "",
         name: cookie.name || ""
       };
-      if (!Services.cookies.cookieExists(cookieObj)) {
+      if (!Services.cookies.cookieExists(cookieObj, cookie.originAttributes || {})) {
         Services.cookies.add(cookie.host, cookie.path || "", cookie.name || "",
                              cookie.value, !!cookie.secure, !!cookie.httponly,
                              /* isSession = */ true, expiry, cookie.originAttributes || {});
       }
     }
   },
 
   /**
--- a/browser/extensions/e10srollout/bootstrap.js
+++ b/browser/extensions/e10srollout/bootstrap.js
@@ -4,16 +4,19 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/UpdateUtils.jsm");
+Cu.import("resource://gre/modules/Task.jsm");
+Cu.import("resource://gre/modules/TelemetryArchive.jsm");
+Cu.import("resource://gre/modules/TelemetryController.jsm");
 
  // The amount of people to be part of e10s
 const TEST_THRESHOLD = {
   "beta"    : 0.5,  // 50%
   "release" : 1.0,  // 100%
 };
 
 const ADDON_ROLLOUT_POLICY = {
@@ -25,27 +28,32 @@ const PREF_COHORT_SAMPLE       = "e10s.r
 const PREF_COHORT_NAME         = "e10s.rollout.cohort";
 const PREF_E10S_OPTED_IN       = "browser.tabs.remote.autostart";
 const PREF_E10S_FORCE_ENABLED  = "browser.tabs.remote.force-enable";
 const PREF_E10S_FORCE_DISABLED = "browser.tabs.remote.force-disable";
 const PREF_TOGGLE_E10S         = "browser.tabs.remote.autostart.2";
 const PREF_E10S_ADDON_POLICY   = "extensions.e10s.rollout.policy";
 const PREF_E10S_ADDON_BLOCKLIST = "extensions.e10s.rollout.blocklist";
 const PREF_E10S_HAS_NONEXEMPT_ADDON = "extensions.e10s.rollout.hasAddon";
+const PREF_DISABLED_FOR_SPINNERS = "e10s.rollout.disabledByLongSpinners";
+
+const LONG_SPINNER_HISTOGRAM   = "FX_TAB_SWITCH_SPINNER_VISIBLE_LONG_MS";
 
 function startup() {
   // In theory we only need to run this once (on install()), but
   // it's better to also run it on every startup. If the user has
   // made manual changes to the prefs, this will keep the data
   // reported more accurate.
   // It's also fine (and preferred) to just do it here on startup
   // (instead of observing prefs), because e10s takes a restart
   // to take effect, so we keep the data based on how it was when
   // the session started.
   defineCohort();
+
+  setUpSpinnerCheck();
 }
 
 function install() {
   defineCohort();
 }
 
 let cohortDefinedOnThisSession = false;
 
@@ -165,20 +173,105 @@ function optedOut() {
 
 /* If this function returns a non-empty string, it
  * means that this particular user should be temporarily
  * disqualified due to some particular reason.
  * If a user shouldn't be disqualified, then an empty
  * string must be returned.
  */
 function getTemporaryDisqualification() {
+  if (Preferences.isSet(PREF_DISABLED_FOR_SPINNERS)) {
+    return "longspinner";
+  }
+
   let applicationLanguage =
     Cc["@mozilla.org/chrome/chrome-registry;1"]
       .getService(Ci.nsIXULChromeRegistry)
       .getSelectedLocale("global")
       .split("-")[0];
 
   if (applicationLanguage == "ru") {
     return "ru";
   }
 
   return "";
 }
+
+let performLongSpinnerCheck = Task.async(function*() {
+  if (!Services.appinfo.browserTabsRemoteAutostart) {
+    return;
+  }
+
+  const DAYS_OLD = 3;
+  let thresholdDate = new Date(Date.now() - (1000 * 60 * 60 * 24 * DAYS_OLD));
+
+  let allPingsInfo = yield TelemetryArchive.promiseArchivedPingList();
+
+  let recentPingsInfo = allPingsInfo.filter(ping => {
+    let pingDate = new Date(ping.timestampCreated);
+    return pingDate > thresholdDate;
+  });
+
+  let pingList = [];
+
+  for (let pingInfo of recentPingsInfo) {
+    pingList.push(yield TelemetryArchive.promiseArchivedPingById(pingInfo.id));
+  }
+
+  pingList.push(TelemetryController.getCurrentPingData(/* subsession = */ true));
+
+  let totalSessionTime = 0;
+  let totalSpinnerTime = 0;
+
+  for (let ping of pingList) {
+    try {
+      if (ping.type != "main") {
+        continue;
+      }
+
+      if (!ping.environment.settings.e10sEnabled) {
+        continue;
+      }
+
+      totalSessionTime = ping.payload.info.subsessionLength;
+
+      if (!(LONG_SPINNER_HISTOGRAM in ping.payload.histograms)) {
+        // The Histogram might not be defined in this ping if no data was recorded for it.
+        // In this case, we still add the session length because that was a valid session
+        // without a long spinner.
+        continue;
+      }
+
+      let histogram = ping.payload.histograms[LONG_SPINNER_HISTOGRAM];
+
+      for (spinnerTime of Object.keys(histogram.values)) {
+        // Only consider spinners that took more than 2 seconds.
+        // Note: the first bucket size that fits this criteria is
+        // 2297ms. And the largest bucket is 64000ms, meaning that
+        // any pause larger than that is only counted as a 64s pause.
+        // For reference, the bucket sizes are:
+        // 0, 1000, 2297, 5277, 12124, 27856, 64000
+        if (spinnerTime >= 2000) {
+          totalSpinnerTime += spinnerTime * histogram.values[spinnerTime];
+        }
+      }
+    } catch (e) { /* just in case there's a malformed ping, ignore it silently */ }
+  }
+
+  totalSpinnerTime /= 1000; // session time is in seconds, but spinner time in ms
+
+  const ACCEPTABLE_THRESHOLD = 20 / 3600; // 20 seconds per hour, per bug 1301131
+
+  if ((totalSpinnerTime / totalSessionTime) > ACCEPTABLE_THRESHOLD) {
+    Preferences.set(PREF_DISABLED_FOR_SPINNERS, true);
+  } else {
+    Preferences.reset(PREF_DISABLED_FOR_SPINNERS);
+  }
+});
+
+function setUpSpinnerCheck() {
+  let {setTimeout, setInterval} = Cu.import("resource://gre/modules/Timer.jsm");
+
+  // Perform an initial check after 5min (which should give good clearance from
+  // the startup process), and then a subsequent check every hour.
+  setTimeout(performLongSpinnerCheck, 1000 * 60 * 5);
+  setInterval(performLongSpinnerCheck, 1000 * 60 * 60);
+}
--- a/browser/extensions/e10srollout/install.rdf.in
+++ b/browser/extensions/e10srollout/install.rdf.in
@@ -5,17 +5,17 @@
 
 #filter substitution
 
 <RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
      xmlns:em="http://www.mozilla.org/2004/em-rdf#">
 
   <Description about="urn:mozilla:install-manifest">
     <em:id>e10srollout@mozilla.org</em:id>
-    <em:version>1.3</em:version>
+    <em:version>1.4</em:version>
     <em:type>2</em:type>
     <em:bootstrap>true</em:bootstrap>
     <em:multiprocessCompatible>true</em:multiprocessCompatible>
 
     <!-- Target Application this theme can install into,
         with minimum and maximum supported versions. -->
     <em:targetApplication>
       <Description>
--- a/browser/themes/shared/customizableui/customizeMode.inc.css
+++ b/browser/themes/shared/customizableui/customizeMode.inc.css
@@ -189,16 +189,17 @@
   /* Sadly, button.css thinks its margins are perfect for everyone. */
   margin-inline-start: 6px !important;
 }
 
 #customization-lwtheme-button > .box-inherit > .box-inherit > .button-icon {
   width: 20px;
   height: 20px;
   border-radius: 2px;
+  background-image: url("chrome://browser/skin/theme-switcher-icon.png");
   background-size: contain;
 }
 
 %ifdef CAN_DRAW_IN_TITLEBAR
 #customization-titlebar-visibility-button {
   list-style-image: url("chrome://browser/skin/customizableui/customize-titleBar-toggle.png");
   -moz-image-region: rect(0, 24px, 24px, 0);
 }
deleted file mode 100644
index 4f299ea4b9ec7e384d1eaa696179f8ee43f0d158..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
--- a/dom/apps/tests/addons/index.html
+++ /dev/null
@@ -1,26 +0,0 @@
-<!DOCTYPE html>
-<html>
-  <head>
-    <title>This page will be modified by the add-on</title>
-    <script>
-function sendAlertsForNode(node) {
-  alert(node.textContent);
-  var color = window.getComputedStyle(node).getPropertyValue("color");
-  alert(color);
-}
-
-function run() {
-  // We need to wait for the next tick because add-ons are injected in the
-  // onload event too.
-  window.setTimeout(function() {
-    sendAlertsForNode(document.getElementById("header"));
-    sendAlertsForNode(document.getElementById("header2"));
-  }, 0);
-}
-    </script>
-  </head>
-  <body onload="run()">
-    <h1 id="header">Lorem ipsum</h1>
-    <h2 id="header2">Uncustomized content</h2>
-  </body>
-</html>
\ No newline at end of file
deleted file mode 100644
--- a/dom/apps/tests/addons/invalid.webapp
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-  "name": "Addon app with an invalid name",
-  "description": "Let me inject script and css!",
-  "developer": { "name": "The Mozilla Community" },
-  "package_path" : "application.zip"
-}
deleted file mode 100644
--- a/dom/apps/tests/addons/invalid.webapp^headers^
+++ /dev/null
@@ -1,1 +0,0 @@
-Content-Type: application/manifest+json
\ No newline at end of file
deleted file mode 100644
--- a/dom/apps/tests/addons/manifest.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
-  "name": "Addon app",
-  "version": "1.0",
-  "manifest_version": 2,
-  "permissions": ["tabs"],
-  "description": "Let me inject script and css!",
-  "author": "The Mozilla Community",
-  "content_scripts": [
-    {"matches": ["http://mochi.test/tests/dom/apps/tests/addons/index.html"],
-     "js": ["script.js", "script2.js", "invalid.js", "script.js"],
-     "css": ["style.css", "style2.css"]}
-  ]
-}
deleted file mode 100644
--- a/dom/apps/tests/addons/script.js
+++ /dev/null
@@ -1,5 +0,0 @@
-// Simple script that changes an element's content.
-
-var head = document.getElementById("header");
-head.innerHTML = "Hello World!";
-
deleted file mode 100644
--- a/dom/apps/tests/addons/script2.js
+++ /dev/null
@@ -1,6 +0,0 @@
-// Simple script that changes an element's content.
-
-var head = document.getElementById("header2");
-head.innerHTML = "Customized content";
-
-
deleted file mode 100644
--- a/dom/apps/tests/addons/style.css
+++ /dev/null
@@ -1,3 +0,0 @@
-#header {
-  color: red;
-}
\ No newline at end of file
deleted file mode 100644
--- a/dom/apps/tests/addons/style2.css
+++ /dev/null
@@ -1,3 +0,0 @@
-#header2 {
-  color: blue;
-}
\ No newline at end of file
deleted file mode 100644
--- a/dom/apps/tests/addons/update.webapp
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-  "name": "Addon app",
-  "description": "Let me inject script and css!",
-  "developer": { "name": "The Mozilla Community" },
-  "package_path" : "application.zip",
-  "id": "webextension@mochitest"
-}
deleted file mode 100644
--- a/dom/apps/tests/addons/update.webapp^headers^
+++ /dev/null
@@ -1,1 +0,0 @@
-Content-Type: application/manifest+json
\ No newline at end of file
--- a/dom/apps/tests/mochitest.ini
+++ b/dom/apps/tests/mochitest.ini
@@ -1,17 +1,11 @@
 [DEFAULT]
 skip-if = true ### Bug 1255339: blacklist because no more mozApps
 support-files =
-  addons/application.zip
-  addons/invalid.webapp
-  addons/invalid.webapp^headers^
-  addons/update.webapp
-  addons/update.webapp^headers^
-  addons/index.html
   chromeAddCert.js
   common.js
   file_app.sjs
   file_app.template.html
   file_bug_779982.html
   file_bug_779982.js
   file_script.template.js
   file_cached_app.template.appcache
@@ -34,20 +28,16 @@ support-files =
   signed/*
   test_packaged_app_common.js
   marketplace/*
   pkg_install_iframe.html
   icon15.png
   icon15alternate.png
   icon48.png
 
-[test_app_addons.html]
-skip-if = os == "android" || toolkit == "gonk" || e10s # embed-apps doesn't work in mochitest app
-[test_app_blocklist.html]
-skip-if = buildapp != 'mulet' # we need MOZ_B2G defined and the test to run in the parent process.
 [test_app_enabled.html]
 [test_app_update.html]
 skip-if = os == "android" || toolkit == "gonk" || e10s # embed-apps doesn't work in mochitest app
 [test_bug_779982.html]
 skip-if = e10s || buildapp == 'b2g' || toolkit == 'android' #Bug 793211
 [test_bug_795164.html]
 [test_bug_1168300.html]
 skip-if = toolkit == "gonk" || e10s # see bug 1175784
deleted file mode 100644
--- a/dom/apps/tests/test_app_addons.html
+++ /dev/null
@@ -1,216 +0,0 @@
-<!DOCTYPE HTML><!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1042881
--->
-<html>
-  <head>
-  <meta charset="utf-8">
-  <title>Test for Bug 923897 - Test apps as addons</title>
-  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="text/javascript" src="common.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-  <script type="application/javascript;version=1.7">
-/**
-  * Test for Bug 923897
-  * This file covers testing addons.
-  *
-  * The setup is as follows:
-  * - app is installed and offers both script and css to inject in
-  *   http://mochi.test:8888/tests/dom/apps/tests/addons/index.html
-  */
-
-SimpleTest.waitForExplicitFinish();
-
-const baseURL = "http://mochi.test:8888/tests/dom/apps/tests/addons/";
-
-const appManifestURL        = baseURL + "update.webapp";
-const invalidAppManifestURL = baseURL + "invalid.webapp";
-
-let gGenerator = runTest();
-
-function go() {
-  gGenerator.next();
-}
-
-function continueTest() {
-  try {
-    gGenerator.next();
-  } catch (e if e instanceof StopIteration) {
-    SpecialPowers.debugUserCustomizations(false);
-    SimpleTest.finish();
-  }
-}
-
-function mozAppsError() {
-  ok(false, "mozApps error: " + this.error.name);
-  SpecialPowers.debugUserCustomizations(false);
-  SimpleTest.finish();
-}
-
-// Triggers one navigation test to the given page.
-// Waits for alert() messages before tearing down the iframe.
-function openPage(pageURL, messages) {
-  info("Navigating to " + pageURL);
-  let ifr = document.createElement("iframe");
-  let listener = function(event) {
-    let message = messages.shift();
-    is(event.detail.message, message, "Checking alert message for " + pageURL);
-    if (messages.length == 0) {
-      ifr.removeEventListener("mozbrowsershowmodalprompt", listener);
-      ifr.parentNode.removeChild(ifr);
-      continueTest();
-    }
-  }
-
-  ifr.addEventListener("mozbrowsershowmodalprompt", listener, false);
-
-  // Open an the app url in an iframe.
-  ifr.setAttribute("mozbrowser", "true");
-  ifr.setAttribute("src", pageURL);
-  document.getElementById("container").appendChild(ifr);
-}
-
-let apps = [];
-
-function installApp(manifestURL, expectedError) {
-  info("About to install app at " + manifestURL);
-  let req = navigator.mozApps.installPackage(manifestURL);
-  req.onsuccess = function() {
-    apps.push(req.result);
-    is(req.result.manifestURL, manifestURL, "app installed");
-    if (req.result.installState == "installed") {
-      is(req.result.manifest.version, "1.0", "correct version");
-      is(req.result.installState, "installed", "app downloaded");
-      continueTest();
-    } else {
-      req.result.ondownloadapplied = function() {
-        is(req.result.manifest.version, "1.0", "correct version");
-        is(req.result.installState, "installed", "app downloaded");
-        continueTest();
-      }
-
-      req.result.ondownloaderror = function() {
-        if (expectedError) {
-          is(req.result.downloadError.name, "MANIFEST_MISMATCH");
-        } else {
-          ok(false, "unexpected installation error: " + req.result.downloadError.name);
-        }
-        continueTest();
-      }
-    }
-  }
-  req.onerror = mozAppsError;
-}
-
-function runTest() {
-  // Set up.
-  SpecialPowers.allowUnsignedAddons();
-  SpecialPowers.debugUserCustomizations(true);
-  SpecialPowers.pushPrefEnv({'set': [
-    ["dom.mozBrowserFramesEnabled", true],
-    ["dom.apps.customization.enabled", true],
-    ]},continueTest);
-  yield undefined;
-
-  SpecialPowers.pushPermissions(
-    [{ "type": "webapps-manage", "allow": 1, "context": document },
-     { "type": "browser", "allow": 1, "context": document } ],
-    continueTest);
-  yield undefined;
-
-  SpecialPowers.autoConfirmAppInstall(continueTest);
-  yield undefined;
-
-  SpecialPowers.autoConfirmAppUninstall(continueTest);
-  yield undefined;
-
-  // Install addon app with an invalid manifest.
-  installApp(invalidAppManifestURL, true);
-  yield undefined;
-
-  // Uninstall the invalid app.
-  while (apps.length) {
-    let app = apps.pop();
-    req = navigator.mozApps.mgmt.uninstall(app);
-    req.onsuccess = continueTest;
-    req.onerror = mozAppsError;
-    yield undefined;
-  }
-
-  // Opens the iframe to the test page, initial state.
-  openPage("http://mochi.test:8888/tests/dom/apps/tests/addons/index.html",
-    ["Lorem ipsum", "rgb(0, 0, 0)",
-     "Uncustomized content", "rgb(0, 0, 0)"]);
-  yield undefined;
-
-  // Install valid addon app.
-  installApp(appManifestURL, false);
-  yield undefined;
-
-  // Opens the iframe to the test page, customized.
-  openPage("http://mochi.test:8888/tests/dom/apps/tests/addons/index.html",
-    ["Hello World!", "rgb(255, 0, 0)",
-     "Customized content", "rgb(0, 0, 255)"]);
-  yield undefined;
-
-  // Disable the app.
-  navigator.mozApps.mgmt.onenabledstatechange = function(event) {
-    ok(true, "onenabledstatechange received");
-    is(event.application.enabled, false, "Application is disabled");
-    is(apps[0].enabled, false, "Application is disabled");
-    continueTest();
-  }
-
-  navigator.mozApps.mgmt.setEnabled(apps[0], false);
-  yield undefined;
-
-  // Opens the iframe to the test page, back to initial state.
-  openPage("http://mochi.test:8888/tests/dom/apps/tests/addons/index.html",
-    ["Lorem ipsum", "rgb(0, 0, 0)",
-     "Uncustomized content", "rgb(0, 0, 0)"]);
-  yield undefined;
-
-  // Re-enable the app.
-  navigator.mozApps.mgmt.onenabledstatechange = function(event) {
-    ok(true, "onenabledstatechange received");
-    is(event.application.enabled, true, "Application is enabled");
-    is(apps[0].enabled, true, "Application is enabled");
-    continueTest();
-  }
-
-  navigator.mozApps.mgmt.setEnabled(apps[0], true);
-  yield undefined;
-
-  // Opens the iframe to the test page, customized.
-  openPage("http://mochi.test:8888/tests/dom/apps/tests/addons/index.html",
-    ["Hello World!", "rgb(255, 0, 0)",
-     "Customized content", "rgb(0, 0, 255)"]);
-  yield undefined;
-
-  // Clean up after ourselves by uninstalling apps.
-  while (apps.length) {
-    let app = apps.pop();
-    req = navigator.mozApps.mgmt.uninstall(app);
-    req.onsuccess = continueTest;
-    req.onerror = mozAppsError;
-    yield undefined;
-  }
-
-  // Opens the iframe to the test page, back to initial state.
-  openPage("http://mochi.test:8888/tests/dom/apps/tests/addons/index.html",
-    ["Lorem ipsum", "rgb(0, 0, 0)",
-     "Uncustomized content", "rgb(0, 0, 0)"]);
-  yield undefined;
-}
-
-  </script>
-  </head>
-<body onload="prepareEnv(go)">
-<p id="display"></p>
-<div id="content" style="display: none">
-</div>
-<pre id="test">
-</pre>
-<div id="container"></div>
-</body>
-</html>
deleted file mode 100644
--- a/dom/apps/tests/test_app_blocklist.html
+++ /dev/null
@@ -1,191 +0,0 @@
-<!DOCTYPE html>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id={1208242}
--->
-<head>
-  <title>Test for Bug {1208242}</title>
-  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="text/javascript" src="common.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-</head>
-<body>
-
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id={1208242}">Mozilla Bug {1208242}</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-
-</div>
-<pre id="test">
-<script class="testbody" type="application/javascript;version=1.7">
-
-var baseURL = "http://mochi.test:8888/tests/dom/apps/tests/addons/";
-var appManifestURL = baseURL + "update.webapp";
-
-SimpleTest.waitForExplicitFinish();
-
-var gGenerator = runTest();
-
-// Utilities to turn off auto update of the blocklist.
-// Copied from toolkit/mozapps/extensions/test/browser/head.js
-var gCatMan = SpecialPowers.Cc["@mozilla.org/categorymanager;1"]
-                           .getService(SpecialPowers.Ci.nsICategoryManager);
-
-var UTIMER = "update-timer";
-var BLOCKLIST = "nsBlocklistService";
-
-var blocklistUpdateConfig = "@mozilla.org/extensions/blocklist;1,getService,blocklist-background-update-timer,extensions.blocklist.interval,86400";
-
-function disableBlocklistUpdateTimer() {
-  info("Disabling " + UTIMER + " " + BLOCKLIST);
-  blocklistUpdateConfig = gCatMan.getCategoryEntry(UTIMER, BLOCKLIST);
-  gCatMan.deleteCategoryEntry(UTIMER, BLOCKLIST, true);
-}
-
-function enableBlocklistUpdateTimer() {
-  info("Enabling " + UTIMER + " " + BLOCKLIST);
-  gCatMan.addCategoryEntry(UTIMER, BLOCKLIST, blocklistUpdateConfig, false, true);
-}
-
-// End of utilities
-
-function go() {
-  disableBlocklistUpdateTimer();
-  SpecialPowers.allowUnsignedAddons();
-  SpecialPowers.pushPermissions(
-    [{ "type": "webapps-manage", "allow": 1, "context": document }],
-    function() { gGenerator.next() });
-}
-
-function continueTest() {
-  try {
-    gGenerator.next();
-  } catch (e if e instanceof StopIteration) {
-    finish();
-  }
-}
-
-function finish() {
-  enableBlocklistUpdateTimer();
-  SimpleTest.finish();
-}
-
-function cbError(aEvent) {
-  ok(false, "Error callback invoked " +
-            aEvent.target.error.name + " " + aEvent.target.error.message);
-  finish();
-}
-
-function mozAppsError() {
-  ok(false, "mozApps error: " + this.error.name);
-  SpecialPowers.debugUserCustomizations(false);
-  SimpleTest.finish();
-}
-
-function installApp(manifestURL) {
-  info("About to install app at " + manifestURL);
-  let req = navigator.mozApps.installPackage(manifestURL);
-  req.onsuccess = function() {
-    is(req.result.manifestURL, manifestURL, "app installed");
-    if (req.result.installState == "installed") {
-      is(req.result.manifest.version, "1.0", "correct version");
-      is(req.result.installState, "installed", "app downloaded");
-      continueTest();
-    } else {
-      req.result.ondownloadapplied = function() {
-        is(req.result.manifest.version, "1.0", "correct version");
-        is(req.result.installState, "installed", "app downloaded");
-        continueTest();
-      }
-
-      req.result.ondownloaderror = function() {
-        ok(false, "unexpected installation error: " + req.result.downloadError.name);
-        continueTest();
-      }
-    }
-  }
-  req.onerror = mozAppsError;
-  return req;
-}
-
-/**
-  * Test blocking of an add-on.
-  */
-function runTest() {
-  SpecialPowers.autoConfirmAppInstall(continueTest);
-  yield undefined;
-
-  SpecialPowers.autoConfirmAppUninstall(continueTest);
-  yield undefined;
-
-  request = navigator.mozApps.mgmt.getAll();
-  request.onerror = cbError;
-  request.onsuccess = continueTest;
-  yield undefined;
-  var initialAppsCount = request.result.length;
-  info("Starting with " + initialAppsCount + " apps installed.");
-
-  // Install valid addon app.
-  var req = installApp(appManifestURL, false);
-  yield undefined;
-
-  var app = req.result;
-
-  ok(app, "App  is non-null");
-  is(app.manifestURL, appManifestURL, "App manifest url is correct.");
-  is(app.enabled, true, "App is enabled by default after install.");
-
-  // Check that the app is disabled
-  navigator.mozApps.mgmt.onenabledstatechange = function(event) {
-    ok(true, "onenabledstatechange received");
-    is(event.application.enabled, false, "Application is disabled");
-    continueTest();
-  }
-
-  // Trigger the blocklist by passing an XML blocklist string.
-  var blocklist =
-    `<?xml version="1.0"?>
-       <blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1443544016000">
-         <emItems>
-           <emItem blockID="i00" id="webextension@mochitest">
-             <versionRange minVersion="0" maxVersion="*" severity="1"> </versionRange>
-           </emItem>
-         </emItems>
-       </blocklist>`;
-
-  var bls = SpecialPowers.Cc["@mozilla.org/extensions/blocklist;1"]
-                         .getService(SpecialPowers.Ci.nsIBlocklistService).wrappedJSObject;
-  bls._loadBlocklistFromString(blocklist);
-
-  //navigator.mozApps.mgmt.setEnabled(app, true);
-  yield undefined;
-
-  // Cleaning up after ourselves.
-  navigator.mozApps.mgmt.onuninstall = function(event) {
-    var app = event.application;
-    is(app.manifestURL, appManifestURL, "App uninstall event ok.");
-    is(app.manifest.name, "Addon app", "App uninstall manifest ok.");
-    continueTest();
-  }
-  request = navigator.mozApps.mgmt.uninstall(app);
-  request.onerror = cbError;
-  request.onsuccess = continueTest;
-  yield undefined;
-  yield undefined;
-  is(request.result, appManifestURL, "App uninstalled.");
-  navigator.mozApps.mgmt.onuninstall = null;
-
-  request = navigator.mozApps.mgmt.getAll();
-  request.onerror = cbError;
-  request.onsuccess = continueTest;
-  yield undefined;
-  is(request.result.length, initialAppsCount, "All apps are uninstalled.");
-}
-
-addLoadEvent(() => prepareEnv(go));
-
-</script>
-</pre>
-</body>
-</html>
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -662,17 +662,17 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(DialogV
 // nsOuterWindowProxy: Outer Window Proxy
 //*****************************************************************************
 
 class nsOuterWindowProxy : public js::Wrapper
 {
 public:
   constexpr nsOuterWindowProxy() : js::Wrapper(0) { }
 
-  virtual bool finalizeInBackground(JS::Value priv) const override {
+  virtual bool finalizeInBackground(const JS::Value& priv) const override {
     return false;
   }
 
   // Standard internal methods
   virtual bool getOwnPropertyDescriptor(JSContext* cx,
                                         JS::Handle<JSObject*> proxy,
                                         JS::Handle<jsid> id,
                                         JS::MutableHandle<JS::PropertyDescriptor> desc)
--- a/dom/base/nsJSTimeoutHandler.cpp
+++ b/dom/base/nsJSTimeoutHandler.cpp
@@ -97,18 +97,18 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSScrip
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSScriptTimeoutHandler)
   tmp->ReleaseJSObjects();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsJSScriptTimeoutHandler)
   if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
     nsAutoCString name("nsJSScriptTimeoutHandler");
     if (tmp->mFunction) {
-      JSFunction* fun =
-        JS_GetObjectFunction(js::UncheckedUnwrap(tmp->mFunction->Callable()));
+      JSObject* obj = tmp->mFunction->CallablePreserveColor();
+      JSFunction* fun = JS_GetObjectFunction(js::UncheckedUnwrap(obj));
       if (fun && JS_GetFunctionId(fun)) {
         JSFlatString *funId = JS_ASSERT_STRING_IS_FLAT(JS_GetFunctionId(fun));
         size_t size = 1 + JS_PutEscapedFlatString(nullptr, 0, funId, 0);
         char *funIdName = new char[size];
         if (funIdName) {
           JS_PutEscapedFlatString(funIdName, size, funId, 0);
           name.AppendLiteral(" [");
           name.Append(funIdName);
--- a/dom/bindings/BindingDeclarations.h
+++ b/dom/bindings/BindingDeclarations.h
@@ -21,16 +21,17 @@
 #include "mozilla/RootedRefPtr.h"
 
 #include "mozilla/dom/DOMString.h"
 
 #include "nsCOMPtr.h"
 #include "nsStringGlue.h"
 #include "nsTArray.h"
 
+class nsIPrincipal;
 class nsWrapperCache;
 
 namespace mozilla {
 namespace dom {
 
 // Struct that serves as a base class for all dictionaries.  Particularly useful
 // so we can use IsBaseOf to detect dictionary template arguments.
 struct DictionaryBase
@@ -103,16 +104,20 @@ public:
     return mCx;
   }
 
   bool Failed() const
   {
     return !Get();
   }
 
+  // It returns the subjectPrincipal if called on the main-thread, otherwise
+  // a nullptr is returned.
+  nsIPrincipal* GetSubjectPrincipal() const;
+
 protected:
   JS::Rooted<JSObject*> mGlobalJSObject;
   JSContext* mCx;
   mutable nsISupports* MOZ_UNSAFE_REF("Valid because GlobalObject is a stack "
                                       "class, and mGlobalObject points to the "
                                       "global, so it won't be destroyed as long "
                                       "as GlobalObject lives on the stack") mGlobalObject;
 };
@@ -269,17 +274,17 @@ public:
 
 // A specialization of Optional for JS::Value to make sure no one ever uses it.
 template<>
 class Optional<JS::Value>
 {
 private:
   Optional() = delete;
 
-  explicit Optional(JS::Value aValue) = delete;
+  explicit Optional(const JS::Value& aValue) = delete;
 };
 
 // A specialization of Optional for NonNull that lets us get a T& from Value()
 template<typename U> class NonNull;
 template<typename T>
 class Optional<NonNull<T> > : public Optional_base<T, NonNull<T> >
 {
 public:
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -2249,16 +2249,29 @@ GlobalObject::GetAsSupports() const
   }
 
   MOZ_ASSERT(!mGlobalObject);
 
   Throw(mCx, NS_ERROR_XPC_BAD_CONVERT_JS);
   return nullptr;
 }
 
+nsIPrincipal*
+GlobalObject::GetSubjectPrincipal() const
+{
+  if (!NS_IsMainThread()) {
+    return nullptr;
+  }
+
+  JSCompartment* compartment = js::GetContextCompartment(mCx);
+  MOZ_ASSERT(compartment);
+  JSPrincipals* principals = JS_GetCompartmentPrincipals(compartment);
+  return nsJSPrincipals::get(principals);
+}
+
 static bool
 CallOrdinaryHasInstance(JSContext* cx, JS::CallArgs& args)
 {
     JS::Rooted<JSObject*> thisObj(cx, &args.thisv().toObject());
     bool isInstance;
     if (!JS::OrdinaryHasInstance(cx, thisObj, args.get(0), &isInstance)) {
       return false;
     }
--- a/dom/bindings/CallbackFunction.h
+++ b/dom/bindings/CallbackFunction.h
@@ -40,16 +40,21 @@ public:
   {
   }
 
   JS::Handle<JSObject*> Callable() const
   {
     return Callback();
   }
 
+  JS::Handle<JSObject*> CallablePreserveColor() const
+  {
+    return CallbackPreserveColor();
+  }
+
   bool HasGrayCallable() const
   {
     // Play it safe in case this gets called after unlink.
     return mCallback && JS::ObjectIsMarkedGray(mCallback);
   }
 
 protected:
   explicit CallbackFunction(CallbackFunction* aCallbackFunction)
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -10448,17 +10448,17 @@ class CGClass(CGThing):
         if self.disallowCopyConstruction:
             class DisallowedCopyConstructor(object):
                 def __init__(self):
                     self.visibility = "private"
 
                 def declare(self, cgClass):
                     name = cgClass.getNameString()
                     return ("%s(const %s&) = delete;\n"
-                            "void operator=(const %s) = delete;\n" % (name, name, name))
+                            "void operator=(const %s&) = delete;\n" % (name, name, name))
 
             disallowedCopyConstructors = [DisallowedCopyConstructor()]
         else:
             disallowedCopyConstructors = []
 
         order = [self.enums, self.unions,
                  self.typedefs, self.members,
                  self.constructors + disallowedCopyConstructors,
@@ -11628,17 +11628,17 @@ class CGDOMJSProxyHandler_className(Clas
         self.descriptor = descriptor
 
     def getBody(self):
         return 'return "%s";\n' % self.descriptor.name
 
 
 class CGDOMJSProxyHandler_finalizeInBackground(ClassMethod):
     def __init__(self, descriptor):
-        args = [Argument('JS::Value', 'priv')]
+        args = [Argument('const JS::Value&', 'priv')]
         ClassMethod.__init__(self, "finalizeInBackground", "bool", args,
                              virtual=True, override=True, const=True)
         self.descriptor = descriptor
 
     def getBody(self):
         return "return false;\n"
 
 
@@ -13838,17 +13838,17 @@ class CGNativeMember(ClassMethod):
         # And the ErrorResult
         if 'infallible' not in self.extendedAttrs:
             # Use aRv so it won't conflict with local vars named "rv"
             args.append(Argument("ErrorResult&", "aRv"))
         # The legacycaller thisval
         if self.member.isMethod() and self.member.isLegacycaller():
             # If it has an identifier, we can't deal with it yet
             assert self.member.isIdentifierLess()
-            args.insert(0, Argument("JS::Value", "aThisVal"))
+            args.insert(0, Argument("const JS::Value&", "aThisVal"))
         # And jscontext bits.
         if needCx(returnType, argList, self.extendedAttrs,
                   self.passJSBitsAsNeeded, self.member.isStatic()):
             args.insert(0, Argument("JSContext*", "cx"))
             if needScopeObject(returnType, argList, self.extendedAttrs,
                                self.descriptorProvider.wrapperCache,
                                self.passJSBitsAsNeeded,
                                self.member.getExtendedAttribute("StoreInSlot")):
--- a/dom/bindings/DOMJSProxyHandler.h
+++ b/dom/bindings/DOMJSProxyHandler.h
@@ -239,26 +239,26 @@ FillPropertyDescriptor(JS::MutableHandle
   desc.setAttributes((readonly ? JSPROP_READONLY : 0) |
                      (enumerable ? JSPROP_ENUMERATE : 0));
   desc.setGetter(nullptr);
   desc.setSetter(nullptr);
 }
 
 inline void
 FillPropertyDescriptor(JS::MutableHandle<JS::PropertyDescriptor> desc,
-                       JSObject* obj, JS::Value v,
+                       JSObject* obj, const JS::Value& v,
                        bool readonly, bool enumerable = true)
 {
   desc.value().set(v);
   FillPropertyDescriptor(desc, obj, readonly, enumerable);
 }
 
 inline void
 FillPropertyDescriptor(JS::MutableHandle<JS::PropertyDescriptor> desc,
-                       JSObject* obj, unsigned attributes, JS::Value v)
+                       JSObject* obj, unsigned attributes, const JS::Value& v)
 {
   desc.object().set(obj);
   desc.value().set(v);
   desc.setAttributes(attributes);
   desc.setGetter(nullptr);
   desc.setSetter(nullptr);
 }
 
--- a/dom/bindings/test/TestBindingHeader.h
+++ b/dom/bindings/test/TestBindingHeader.h
@@ -800,30 +800,30 @@ public:
   void ExerciseTypedefInterfaces1(TestInterface&);
   already_AddRefed<TestInterface> ExerciseTypedefInterfaces2(TestInterface*);
   void ExerciseTypedefInterfaces3(TestInterface&);
 
   // Deprecated methods and attributes
   int8_t DeprecatedAttribute();
   int8_t SetDeprecatedAttribute(int8_t);
   int8_t DeprecatedMethod();
-  int8_t DeprecatedMethodWithContext(JSContext*, JS::Value);
+  int8_t DeprecatedMethodWithContext(JSContext*, const JS::Value&);
 
   // Static methods and attributes
   static void StaticMethod(const GlobalObject&, bool);
-  static void StaticMethodWithContext(const GlobalObject&, JS::Value);
+  static void StaticMethodWithContext(const GlobalObject&, const JS::Value&);
   static bool StaticAttribute(const GlobalObject&);
   static void SetStaticAttribute(const GlobalObject&, bool);
   static void Assert(const GlobalObject&, bool);
 
   // Deprecated static methods and attributes
   static int8_t StaticDeprecatedAttribute(const GlobalObject&);
   static int8_t SetStaticDeprecatedAttribute(const GlobalObject&, int8_t);
   static int8_t StaticDeprecatedMethod(const GlobalObject&);
-  static int8_t StaticDeprecatedMethodWithContext(const GlobalObject&, JS::Value);
+  static int8_t StaticDeprecatedMethodWithContext(const GlobalObject&, const JS::Value&);
 
   // Overload resolution tests
   bool Overload1(TestInterface&);
   TestInterface* Overload1(const nsAString&, TestInterface&);
   void Overload2(TestInterface&);
   void Overload2(JSContext*, const Dict&);
   void Overload2(bool);
   void Overload2(const nsAString&);
@@ -928,17 +928,17 @@ public:
   void SetJsonifierShouldSkipThis3(TestCallbackInterface&);
   void ThrowingMethod(ErrorResult& aRv);
   bool GetThrowingAttr(ErrorResult& aRv) const;
   void SetThrowingAttr(bool arg, ErrorResult& aRv);
   bool GetThrowingGetterAttr(ErrorResult& aRv) const;
   void SetThrowingGetterAttr(bool arg);
   bool ThrowingSetterAttr() const;
   void SetThrowingSetterAttr(bool arg, ErrorResult& aRv);
-  int16_t LegacyCall(JS::Value, uint32_t, TestInterface&);
+  int16_t LegacyCall(const JS::Value&, uint32_t, TestInterface&);
   void PassArgsWithDefaults(JSContext*, const Optional<int32_t>&,
                             TestInterface*, const Dict&, double,
                             const Optional<float>&);
 
   void SetDashed_attribute(int8_t);
   int8_t Dashed_attribute();
   void Dashed_method();
 
--- a/dom/canvas/CanvasUtils.cpp
+++ b/dom/canvas/CanvasUtils.cpp
@@ -107,17 +107,17 @@ DoDrawImageSecurityCheck(dom::HTMLCanvas
         // This canvas has access to that image anyway
         return;
     }
 
     aCanvasElement->SetWriteOnly();
 }
 
 bool
-CoerceDouble(JS::Value v, double* d)
+CoerceDouble(const JS::Value& v, double* d)
 {
     if (v.isDouble()) {
         *d = v.toDouble();
     } else if (v.isInt32()) {
         *d = double(v.toInt32());
     } else if (v.isUndefined()) {
         *d = 0.0;
     } else {
--- a/dom/canvas/CanvasUtils.h
+++ b/dom/canvas/CanvasUtils.h
@@ -44,17 +44,17 @@ inline bool CheckSaneSubrectSize(int32_t
 void DoDrawImageSecurityCheck(dom::HTMLCanvasElement *aCanvasElement,
                               nsIPrincipal *aPrincipal,
                               bool forceWriteOnly,
                               bool CORSUsed);
 
 // Make a double out of |v|, treating undefined values as 0.0 (for
 // the sake of sparse arrays).  Return true iff coercion
 // succeeded.
-bool CoerceDouble(JS::Value v, double* d);
+bool CoerceDouble(const JS::Value& v, double* d);
 
     /* Float validation stuff */
 #define VALIDATE(_f)  if (!IsFinite(_f)) return false
 
 inline bool FloatValidate (double f1) {
     VALIDATE(f1);
     return true;
 }
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -823,18 +823,21 @@ WebGLContext::ResizeBackbuffer(uint32_t 
     }
     return true;
 }
 
 void
 WebGLContext::ThrowEvent_WebGLContextCreationError(const nsACString& text)
 {
     RefPtr<EventTarget> target = mCanvasElement;
-    if (!target) {
+    if (!target && mOffscreenCanvas) {
         target = mOffscreenCanvas;
+    } else if (!target) {
+        GenerateWarning("Failed to create WebGL context: %s", text.BeginReading());
+        return;
     }
 
     const auto kEventName = NS_LITERAL_STRING("webglcontextcreationerror");
 
     WebGLContextEventInit eventInit;
     // eventInit.mCancelable = true; // The spec says this, but it's silly.
     eventInit.mStatusMessage = NS_ConvertASCIItoUTF16(text);
 
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -865,29 +865,21 @@ WebGLContext::InitAndValidateGL(FailureR
                 mGLMaxVaryingVectors = 16;
                 // 16 = 64/4, and 64 is the min value for
                 // maxVertexOutputComponents in the OpenGL 3.2 spec.
             }
         }
     }
 
     if (gl->IsCompatibilityProfile()) {
-        // gl_PointSize is always available in ES2 GLSL, but has to be
-        // specifically enabled on desktop GLSL.
-        gl->fEnable(LOCAL_GL_VERTEX_PROGRAM_POINT_SIZE);
+        gl->fEnable(LOCAL_GL_POINT_SPRITE);
+    }
 
-        /* gl_PointCoord is always available in ES2 GLSL and in newer desktop
-         * GLSL versions, but apparently not in OpenGL 2 and apparently not (due
-         * to a driver bug) on certain NVIDIA setups. See:
-         *   http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=261472
-         *
-         * Note that this used to cause crashes on old ATI drivers... Hopefully
-         * not a significant anymore. See bug 602183.
-         */
-        gl->fEnable(LOCAL_GL_POINT_SPRITE);
+    if (!gl->IsGLES()) {
+        gl->fEnable(LOCAL_GL_PROGRAM_POINT_SIZE);
     }
 
 #ifdef XP_MACOSX
     if (gl->WorkAroundDriverBugs() &&
         gl->Vendor() == gl::GLVendor::ATI &&
         !nsCocoaFeatures::IsAtLeastVersion(10,9))
     {
         // The Mac ATI driver, in all known OSX version up to and including
--- a/dom/canvas/test/webgl-conf/generated-mochitest.ini
+++ b/dom/canvas/test/webgl-conf/generated-mochitest.ini
@@ -5270,17 +5270,16 @@ skip-if = (os == 'android' || os == 'lin
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance__glsl__variables__gl-fragcoord.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance__glsl__variables__gl-fragdata-and-fragcolor.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance__glsl__variables__gl-frontfacing.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance__glsl__variables__gl-pointcoord.html]
-fail-if = (os == 'mac')
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance__glsl__variables__glsl-built-ins.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance__limits__gl-line-width.html]
 skip-if = (os == 'mac') || (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance__limits__gl-max-texture-dimensions.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance__limits__gl-min-attribs.html]
@@ -5776,22 +5775,20 @@ skip-if = (os == 'android' || os == 'lin
 [generated/test_2_conformance__rendering__multisample-corruption.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance__rendering__negative-one-index.html]
 fail-if = (os == 'mac')
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance__rendering__point-no-attributes.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance__rendering__point-size.html]
-fail-if = (os == 'mac')
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance__rendering__point-specific-shader-variables.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance__rendering__point-with-gl-pointcoord-in-fragment-shader.html]
-fail-if = (os == 'mac')
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance__rendering__polygon-offset.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance__rendering__simple.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance__rendering__triangle.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance__state__gl-enable-enum-test.html]
--- a/dom/canvas/test/webgl-conf/mochitest-errata.ini
+++ b/dom/canvas/test/webgl-conf/mochitest-errata.ini
@@ -529,26 +529,20 @@ fail-if = (os == 'mac' && os_version == 
 fail-if = (os == 'mac' && os_version == '10.8')
 [generated/test_conformance__glsl__variables__gl-pointcoord.html]
 fail-if = (os == 'mac' && os_version == '10.8')
 [generated/test_conformance__limits__gl-max-texture-dimensions.html]
 fail-if = (os == 'mac' && os_version == '10.8')
 
 ####################
 # failure on OSX
-[generated/test_2_conformance__rendering__point-with-gl-pointcoord-in-fragment-shader.html]
-fail-if = (os == 'mac')
-[generated/test_2_conformance__glsl__variables__gl-pointcoord.html]
-fail-if = (os == 'mac')
 [generated/test_2_conformance2__state__gl-get-calls.html]
 fail-if = (os == 'mac')
 [generated/test_conformance__extensions__angle-instanced-arrays.html]
 fail-if = (os == 'mac')
-[generated/test_2_conformance__rendering__point-size.html]
-fail-if = (os == 'mac')
 [generated/test_conformance__glsl__misc__shaders-with-invariance.html]
 fail-if = (os == 'mac')
 
 [generated/test_2_conformance2__extensions__ext-color-buffer-float.html]
 skip-if = (os == 'mac' && debug)
 [generated/test_2_conformance__limits__gl-line-width.html]
 skip-if = (os == 'mac')
 [generated/test_2_conformance__misc__type-conversion-test.html]
--- a/dom/events/ClipboardEvent.cpp
+++ b/dom/events/ClipboardEvent.cpp
@@ -70,17 +70,18 @@ ClipboardEvent::Constructor(const Global
   RefPtr<DataTransfer> clipboardData;
   if (e->mEventIsInternal) {
     InternalClipboardEvent* event = e->mEvent->AsClipboardEvent();
     if (event) {
       // Always create a clipboardData for the copy event. If this is changed to
       // support other types of events, make sure that read/write privileges are
       // checked properly within DataTransfer.
       clipboardData = new DataTransfer(ToSupports(e), eCopy, false, -1);
-      clipboardData->SetData(aParam.mDataType, aParam.mData, aRv);
+      clipboardData->SetData(aParam.mDataType, aParam.mData,
+                             Some(aGlobal.GetSubjectPrincipal()), aRv);
       NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
     }
   }
 
   e->InitClipboardEvent(aType, aParam.mBubbles, aParam.mCancelable,
                         clipboardData);
   e->SetTrusted(trusted);
   return e.forget();
--- a/dom/events/DataTransfer.cpp
+++ b/dom/events/DataTransfer.cpp
@@ -279,19 +279,21 @@ DataTransfer::SetEffectAllowedInt(uint32
 NS_IMETHODIMP
 DataTransfer::GetMozUserCancelled(bool* aUserCancelled)
 {
   *aUserCancelled = MozUserCancelled();
   return NS_OK;
 }
 
 already_AddRefed<FileList>
-DataTransfer::GetFiles(ErrorResult& aRv)
+DataTransfer::GetFiles(const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                       ErrorResult& aRv)
 {
-  return mItems->Files(nsContentUtils::SubjectPrincipal());
+  MOZ_ASSERT(aSubjectPrincipal.isSome());
+  return mItems->Files(aSubjectPrincipal.value());
 }
 
 NS_IMETHODIMP
 DataTransfer::GetFiles(nsIDOMFileList** aFileList)
 {
   if (!aFileList) {
     return NS_ERROR_FAILURE;
   }
@@ -369,24 +371,27 @@ DataTransfer::GetTypes(nsISupports** aTy
   }
 
   types.forget(aTypes);
   return NS_OK;
 }
 
 void
 DataTransfer::GetData(const nsAString& aFormat, nsAString& aData,
+                      const Maybe<nsIPrincipal*>& aSubjectPrincipal,
                       ErrorResult& aRv)
 {
+  MOZ_ASSERT(aSubjectPrincipal.isSome());
+
   // return an empty string if data for the format was not found
   aData.Truncate();
 
   nsCOMPtr<nsIVariant> data;
   nsresult rv =
-    GetDataAtInternal(aFormat, 0, nsContentUtils::SubjectPrincipal(),
+    GetDataAtInternal(aFormat, 0, aSubjectPrincipal.value(),
                       getter_AddRefs(data));
   if (NS_FAILED(rv)) {
     if (rv != NS_ERROR_DOM_INDEX_SIZE_ERR) {
       aRv.Throw(rv);
     }
     return;
   }
 
@@ -428,29 +433,31 @@ DataTransfer::GetData(const nsAString& a
     }
   }
 }
 
 NS_IMETHODIMP
 DataTransfer::GetData(const nsAString& aFormat, nsAString& aData)
 {
   ErrorResult rv;
-  GetData(aFormat, aData, rv);
+  GetData(aFormat, aData, Some(nsContentUtils::SubjectPrincipal()), rv);
   return rv.StealNSResult();
 }
 
 void
 DataTransfer::SetData(const nsAString& aFormat, const nsAString& aData,
+                      const Maybe<nsIPrincipal*>& aSubjectPrincipal,
                       ErrorResult& aRv)
 {
+  MOZ_ASSERT(aSubjectPrincipal.isSome());
+
   RefPtr<nsVariantCC> variant = new nsVariantCC();
   variant->SetAsAString(aData);
 
-  aRv = SetDataAtInternal(aFormat, variant, 0,
-                          nsContentUtils::SubjectPrincipal());
+  aRv = SetDataAtInternal(aFormat, variant, 0, aSubjectPrincipal.value());
 }
 
 void
 DataTransfer::ClearData(const Optional<nsAString>& aFormat,
                         const Maybe<nsIPrincipal*>& aSubjectPrincipal,
                         ErrorResult& aRv)
 {
   MOZ_ASSERT(aSubjectPrincipal.isSome());
@@ -636,20 +643,23 @@ DataTransfer::GetDataAtInternal(const ns
   data.forget(aData);
   return NS_OK;
 }
 
 void
 DataTransfer::MozGetDataAt(JSContext* aCx, const nsAString& aFormat,
                            uint32_t aIndex,
                            JS::MutableHandle<JS::Value> aRetval,
+                           const Maybe<nsIPrincipal*>& aSubjectPrincipal,
                            mozilla::ErrorResult& aRv)
 {
+  MOZ_ASSERT(aSubjectPrincipal.isSome());
+
   nsCOMPtr<nsIVariant> data;
-  aRv = GetDataAtInternal(aFormat, aIndex, nsContentUtils::SubjectPrincipal(),
+  aRv = GetDataAtInternal(aFormat, aIndex, aSubjectPrincipal.value(),
                           getter_AddRefs(data));
   if (aRv.Failed()) {
     return;
   }
 
   if (!data) {
     aRetval.setNull();
     return;
@@ -718,25 +728,27 @@ DataTransfer::SetDataAtInternal(const ns
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   return SetDataWithPrincipal(aFormat, aData, aIndex, aSubjectPrincipal);
 }
 
 void
 DataTransfer::MozSetDataAt(JSContext* aCx, const nsAString& aFormat,
-                           JS::Handle<JS::Value> aData,
-                           uint32_t aIndex, ErrorResult& aRv)
+                           JS::Handle<JS::Value> aData, uint32_t aIndex,
+                           const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                           ErrorResult& aRv)
 {
+  MOZ_ASSERT(aSubjectPrincipal.isSome());
+
   nsCOMPtr<nsIVariant> data;
   aRv = nsContentUtils::XPConnect()->JSValToVariant(aCx, aData,
                                                     getter_AddRefs(data));
   if (!aRv.Failed()) {
-    aRv = SetDataAtInternal(aFormat, data, aIndex,
-                            nsContentUtils::SubjectPrincipal());
+    aRv = SetDataAtInternal(aFormat, data, aIndex, aSubjectPrincipal.value());
   }
 }
 
 void
 DataTransfer::MozClearDataAt(const nsAString& aFormat, uint32_t aIndex,
                              const Maybe<nsIPrincipal*>& aSubjectPrincipal,
                              ErrorResult& aRv)
 {
@@ -813,18 +825,21 @@ DataTransfer::SetDragImage(nsIDOMElement
   nsCOMPtr<Element> image = do_QueryInterface(aImage);
   if (image) {
     SetDragImage(*image, aX, aY, rv);
   }
   return rv.StealNSResult();
 }
 
 already_AddRefed<Promise>
-DataTransfer::GetFilesAndDirectories(ErrorResult& aRv)
+DataTransfer::GetFilesAndDirectories(const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                                     ErrorResult& aRv)
 {
+  MOZ_ASSERT(aSubjectPrincipal.isSome());
+
   nsCOMPtr<nsINode> parentNode = do_QueryInterface(mParent);
   if (!parentNode) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   nsCOMPtr<nsIGlobalObject> global = parentNode->OwnerDoc()->GetScopeObject();
   MOZ_ASSERT(global);
@@ -833,37 +848,39 @@ DataTransfer::GetFilesAndDirectories(Err
     return nullptr;
   }
 
   RefPtr<Promise> p = Promise::Create(global, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
-  RefPtr<FileList> files = mItems->Files(nsContentUtils::SubjectPrincipal());
+  RefPtr<FileList> files = mItems->Files(aSubjectPrincipal.value());
   if (NS_WARN_IF(!files)) {
     return nullptr;
   }
 
   Sequence<RefPtr<File>> filesSeq;
   files->ToSequence(filesSeq, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   p->MaybeResolve(filesSeq);
 
   return p.forget();
 }
 
 already_AddRefed<Promise>
-DataTransfer::GetFiles(bool aRecursiveFlag, ErrorResult& aRv)
+DataTransfer::GetFiles(bool aRecursiveFlag,
+                       const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                       ErrorResult& aRv)
 {
   // Currently we don't support directories.
-  return GetFilesAndDirectories(aRv);
+  return GetFilesAndDirectories(aSubjectPrincipal, aRv);
 }
 
 void
 DataTransfer::AddElement(Element& aElement, ErrorResult& aRv)
 {
   if (mReadOnly) {
     aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
     return;
--- a/dom/events/DataTransfer.h
+++ b/dom/events/DataTransfer.h
@@ -133,30 +133,40 @@ public:
     }
   }
 
   void SetDragImage(Element& aElement, int32_t aX, int32_t aY,
                     ErrorResult& aRv);
 
   already_AddRefed<DOMStringList> GetTypes(ErrorResult& rv) const;
 
-  void GetData(const nsAString& aFormat, nsAString& aData, ErrorResult& aRv);
+  void GetData(const nsAString& aFormat, nsAString& aData,
+               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+               ErrorResult& aRv);
 
   void SetData(const nsAString& aFormat, const nsAString& aData,
+               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
                ErrorResult& aRv);
 
   void ClearData(const mozilla::dom::Optional<nsAString>& aFormat,
                  const Maybe<nsIPrincipal*>& aSubjectPrincipal,
                  mozilla::ErrorResult& aRv);
 
-  already_AddRefed<FileList> GetFiles(mozilla::ErrorResult& aRv);
+  already_AddRefed<FileList>
+  GetFiles(const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+           mozilla::ErrorResult& aRv);
 
-  already_AddRefed<Promise> GetFilesAndDirectories(ErrorResult& aRv);
+  already_AddRefed<Promise>
+  GetFilesAndDirectories(const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                         mozilla::ErrorResult& aRv);
 
-  already_AddRefed<Promise> GetFiles(bool aRecursiveFlag, ErrorResult& aRv);
+  already_AddRefed<Promise>
+  GetFiles(bool aRecursiveFlag,
+           const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+           ErrorResult& aRv);
 
 
   void AddElement(Element& aElement, mozilla::ErrorResult& aRv);
 
   uint32_t MozItemCount() const;
 
   void GetMozCursor(nsString& aCursor)
   {
@@ -171,20 +181,22 @@ public:
                                              mozilla::ErrorResult& aRv) const;
 
   void MozClearDataAt(const nsAString& aFormat, uint32_t aIndex,
                       const Maybe<nsIPrincipal*>& aSubjectPrincipal,
                       mozilla::ErrorResult& aRv);
 
   void MozSetDataAt(JSContext* aCx, const nsAString& aFormat,
                     JS::Handle<JS::Value> aData, uint32_t aIndex,
+                    const Maybe<nsIPrincipal*>& aSubjectPrincipal,
                     mozilla::ErrorResult& aRv);
 
   void MozGetDataAt(JSContext* aCx, const nsAString& aFormat,
                     uint32_t aIndex, JS::MutableHandle<JS::Value> aRetval,
+                    const Maybe<nsIPrincipal*>& aSubjectPrincipal,
                     mozilla::ErrorResult& aRv);
 
   bool MozUserCancelled() const
   {
     return mUserCancelled;
   }
 
   already_AddRefed<nsINode> GetMozSourceNode();
@@ -358,9 +370,8 @@ protected:
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(DataTransfer, NS_DATATRANSFER_IID)
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_DataTransfer_h */
-
new file mode 100644
--- /dev/null
+++ b/dom/events/test/pointerevents/bug1293174_implicit_pointer_capture_for_touch_1.html
@@ -0,0 +1,64 @@
+<!doctype html>
+<html>
+    <head>
+        <title>Pointer Events properties tests</title>
+        <meta name="viewport" content="width=device-width">
+        <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+        <script src="/resources/testharness.js"></script>
+        <!--script src="/resources/testharnessreport.js"></script-->
+        <!-- Additional helper script for common checks across event types -->
+        <script type="text/javascript" src="pointerevent_support.js"></script>
+        <script type="text/javascript" src="mochitest_support_internal.js"></script>
+        <script>
+          var detected_pointertypes = {};
+          var test_pointerEvent = async_test("implicit pointer capture for touch");
+          // showPointerTypes is defined in pointerevent_support.js
+          // Requirements: the callback function will reference the test_pointerEvent object and
+          // will fail unless the async_test is created with the var name "test_pointerEvent".
+          add_completion_callback(showPointerTypes);
+
+          function run() {
+            let target0 = window.document.getElementById("target0");
+            let target1 = window.document.getElementById("target1");
+
+            on_event(target0, "pointerdown", function (event) {
+              pointerdown_event = event;
+              detected_pointertypes[event.pointerType] = true;
+              assert_true(true, "target0 receives pointerdown");
+            });
+
+            on_event(target0, "pointermove", function (event) {
+              assert_true(true, "target0 receives pointermove");
+              assert_true(target0.hasPointerCapture(event.pointerId), "target0.hasPointerCapture should be true");
+            });
+
+            on_event(target0, "gotpointercapture", function (event) {
+              assert_true(true, "target0 should receive gotpointercapture");
+            });
+
+            on_event(target0, "lostpointercapture", function (event) {
+              assert_true(true, "target0 should receive lostpointercapture");
+            });
+
+            on_event(target1, "pointermove", function (event) {
+              assert_true(false, "target1 should not receive pointermove");
+            });
+
+            on_event(target0, "pointerup", function (event) {
+              assert_true(true, "target0 receives pointerup");
+              test_pointerEvent.done();
+            });
+          }
+        </script>
+    </head>
+    <body onload="run()">
+        <h1>Pointer Events tests</h1>
+        <div id="target0" style="width: 200px; height: 200px; background: green" touch-action:none></div>
+        <div id="target1" style="width: 200px; height: 200px; background: green" touch-action:none></div>
+        <div id="complete-notice">
+            <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+            <p>Refresh the page to run the tests again with a different pointer type.</p>
+        </div>
+        <div id="log"></div>
+    </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/events/test/pointerevents/bug1293174_implicit_pointer_capture_for_touch_2.html
@@ -0,0 +1,65 @@
+<!doctype html>
+<html>
+    <head>
+        <title>Pointer Events properties tests</title>
+        <meta name="viewport" content="width=device-width">
+        <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+        <script src="/resources/testharness.js"></script>
+        <!--script src="/resources/testharnessreport.js"></script-->
+        <!-- Additional helper script for common checks across event types -->
+        <script type="text/javascript" src="pointerevent_support.js"></script>
+        <script type="text/javascript" src="mochitest_support_internal.js"></script>
+        <script>
+          var detected_pointertypes = {};
+          var test_pointerEvent = async_test("implicit pointer capture for touch");
+          // showPointerTypes is defined in pointerevent_support.js
+          // Requirements: the callback function will reference the test_pointerEvent object and
+          // will fail unless the async_test is created with the var name "test_pointerEvent".
+          add_completion_callback(showPointerTypes);
+
+          function run() {
+            let target0 = window.document.getElementById("target0");
+            let target1 = window.document.getElementById("target1");
+
+            on_event(target0, "pointerdown", function (event) {
+              pointerdown_event = event;
+              detected_pointertypes[event.pointerType] = true;
+              assert_true(true, "target0 receives pointerdown");
+            });
+
+            on_event(target0, "pointermove", function (event) {
+              assert_true(true, "target0 receives pointermove");
+              assert_false(target0.hasPointerCapture(event.pointerId), "target0.hasPointerCapture should be false");
+            });
+
+            on_event(target0, "gotpointercapture", function (event) {
+              assert_unreached("target0 should not receive gotpointercapture");
+            });
+
+            on_event(target0, "lostpointercapture", function (event) {
+              assert_unreached("target0 should not receive lostpointercapture");
+            });
+
+            on_event(target1, "pointermove", function (event) {
+              assert_true(true, "target1 receives pointermove");
+              assert_false(target1.hasPointerCapture(), "target1.hasPointerCapture should be false");
+            });
+
+            on_event(target0, "pointerup", function (event) {
+              assert_true(true, "target0 receives pointerup");
+              test_pointerEvent.done();
+            });
+          }
+        </script>
+    </head>
+    <body onload="run()">
+        <h1>Pointer Events tests</h1>
+        <div id="target0" style="width: 200px; height: 200px; background: green" touch-action:none></div>
+        <div id="target1" style="width: 200px; height: 200px; background: green" touch-action:none></div>
+        <div id="complete-notice">
+            <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+            <p>Refresh the page to run the tests again with a different pointer type.</p>
+        </div>
+        <div id="log"></div>
+    </body>
+</html>
--- a/dom/events/test/pointerevents/mochitest.ini
+++ b/dom/events/test/pointerevents/mochitest.ini
@@ -144,10 +144,14 @@ support-files =
     pointerevent_touch-action-pan-x-css_touch-manual.html
     pointerevent_touch-action-pan-x-pan-y-pan-y_touch-manual.html
     pointerevent_touch-action-pan-x-pan-y_touch-manual.html
     pointerevent_touch-action-pan-y-css_touch-manual.html
     pointerevent_touch-action-span-test_touch-manual.html
     pointerevent_touch-action-svg-test_touch-manual.html
     pointerevent_touch-action-table-test_touch-manual.html
 [test_bug1285128.html]
+[test_bug1293174_implicit_pointer_capture_for_touch_1.html]
+  support-files = bug1293174_implicit_pointer_capture_for_touch_1.html
+[test_bug1293174_implicit_pointer_capture_for_touch_2.html]
+  support-files = bug1293174_implicit_pointer_capture_for_touch_2.html
 [test_empty_file.html]
   disabled = disabled # Bug 1150091 - Issue with support-files
--- a/dom/events/test/pointerevents/mochitest_support_external.js
+++ b/dom/events/test/pointerevents/mochitest_support_external.js
@@ -10,16 +10,25 @@ addEventListener("load", function(event)
 
 // Function allows to initialize prerequisites before testing
 function prepareTest() {
   SimpleTest.waitForExplicitFinish();
   SimpleTest.requestCompleteLog();
   turnOnPointerEvents(startTest);
 }
 
+function setImplicitPointerCapture(capture, callback) {
+  console.log("SET dom.w3c_pointer_events.implicit_capture as " + capture);
+  SpecialPowers.pushPrefEnv({
+    "set": [
+      ["dom.w3c_pointer_events.implicit_capture", capture]
+    ]
+  }, callback);
+}
+
 function turnOnPointerEvents(callback) {
   console.log("SET dom.w3c_pointer_events.enabled as TRUE");
   console.log("SET layout.css.touch_action.enabled as TRUE");
   SpecialPowers.pushPrefEnv({
     "set": [
       ["dom.w3c_pointer_events.enabled", true],
       ["layout.css.touch_action.enabled", true]
     ]
new file mode 100644
--- /dev/null
+++ b/dom/events/test/pointerevents/test_bug1293174_implicit_pointer_capture_for_touch_1.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>Test for Bug 1293174</title>
+    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+    <script type="text/javascript" src="mochitest_support_external.js"></script>
+    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+    <script type="text/javascript">
+      SimpleTest.waitForExplicitFinish();
+      function startTest() {
+        setImplicitPointerCapture(true, loadSubFrame);
+      }
+      function loadSubFrame() {
+        var iframe = document.getElementById("testFrame");
+        iframe.src = "bug1293174_implicit_pointer_capture_for_touch_1.html";
+      }
+      function executeTest(int_win) {
+        sendTouchEvent(int_win, "target0", "touchstart");
+        sendTouchEvent(int_win, "target0", "touchmove");
+        sendTouchEvent(int_win, "target1", "touchmove");
+        sendTouchEvent(int_win, "target0", "touchmove");
+        sendTouchEvent(int_win, "target0", "touchend");
+      }
+    </script>
+  </head>
+  <body>
+    <iframe id="testFrame" height="800" width="1000"></iframe>
+  </body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/dom/events/test/pointerevents/test_bug1293174_implicit_pointer_capture_for_touch_2.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>Test for Bug 1293174</title>
+    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+    <script type="text/javascript" src="mochitest_support_external.js"></script>
+    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+    <script type="text/javascript">
+      SimpleTest.waitForExplicitFinish();
+      function startTest() {
+        setImplicitPointerCapture(false, loadSubFrame);
+      }
+      function loadSubFrame() {
+        var iframe = document.getElementById("testFrame");
+        iframe.src = "bug1293174_implicit_pointer_capture_for_touch_2.html";
+      }
+      function executeTest(int_win) {
+        sendTouchEvent(int_win, "target0", "touchstart");
+        sendTouchEvent(int_win, "target0", "touchmove");
+        sendTouchEvent(int_win, "target1", "touchmove");
+        sendTouchEvent(int_win, "target0", "touchmove");
+        sendTouchEvent(int_win, "target0", "touchend");
+      }
+    </script>
+  </head>
+  <body>
+    <iframe id="testFrame" height="800" width="1000"></iframe>
+  </body>
+</html>
+
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -1773,30 +1773,24 @@ TabChild::RecvHandleTap(const GeckoConte
   if (!presShell->GetPresContext()) {
     return true;
   }
   CSSToLayoutDeviceScale scale(presShell->GetPresContext()->CSSToDevPixelScale());
   CSSPoint point = APZCCallbackHelper::ApplyCallbackTransform(aPoint / scale, aGuid);
 
   switch (aType) {
   case GeckoContentController::TapType::eSingleTap:
-    if (mRemoteFrame) {
-      mRemoteFrame->SendTakeFocusForClickFromTap();
-    }
     if (mGlobal && mTabChildGlobal) {
       mAPZEventState->ProcessSingleTap(point, scale, aModifiers, aGuid, 1);
     }
     break;
   case GeckoContentController::TapType::eDoubleTap:
     HandleDoubleTap(point, aModifiers, aGuid);
     break;
   case GeckoContentController::TapType::eSecondTap:
-    if (mRemoteFrame) {
-      mRemoteFrame->SendTakeFocusForClickFromTap();
-    }
     if (mGlobal && mTabChildGlobal) {
       mAPZEventState->ProcessSingleTap(point, scale, aModifiers, aGuid, 2);
     }
     break;
   case GeckoContentController::TapType::eLongTap:
     if (mGlobal && mTabChildGlobal) {
       mAPZEventState->ProcessLongTap(presShell, point, scale, aModifiers, aGuid,
           aInputBlockId);
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -1691,16 +1691,20 @@ TabParent::SendHandleTap(TapType aType,
                          const LayoutDevicePoint& aPoint,
                          Modifiers aModifiers,
                          const ScrollableLayerGuid& aGuid,
                          uint64_t aInputBlockId)
 {
   if (mIsDestroyed) {
     return false;
   }
+  if ((aType == TapType::eSingleTap || aType == TapType::eSecondTap) &&
+      GetRenderFrame()) {
+    GetRenderFrame()->TakeFocusForClickFromTap();
+  }
   LayoutDeviceIntPoint offset = GetChildProcessOffset();
   return PBrowserParent::SendHandleTap(aType, aPoint + offset, aModifiers, aGuid,
       aInputBlockId);
 }
 
 bool
 TabParent::RecvSyncMessage(const nsString& aMessage,
                            const ClonedMessageData& aData,
--- a/dom/media/systemservices/CamerasChild.cpp
+++ b/dom/media/systemservices/CamerasChild.cpp
@@ -29,17 +29,18 @@ mozilla::LazyLogModule gCamerasChildLog(
 #define FAKE_ONDEVICECHANGE_EVENT_REPEAT_COUNT 30
 
 namespace mozilla {
 namespace camera {
 
 CamerasSingleton::CamerasSingleton()
   : mCamerasMutex("CamerasSingleton::mCamerasMutex"),
     mCameras(nullptr),
-    mCamerasChildThread(nullptr) {
+    mCamerasChildThread(nullptr),
+    mFakeDeviceChangeEventThread(nullptr) {
   LOG(("CamerasSingleton: %p", this));
 }
 
 CamerasSingleton::~CamerasSingleton() {
   LOG(("~CamerasSingleton: %p", this));
 }
 
 class FakeOnDeviceChangeEventRunnable : public Runnable
@@ -53,17 +54,17 @@ public:
     OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
 
     CamerasChild* child = CamerasSingleton::Child();
     if (child) {
       child->OnDeviceChange();
 
       if (mCounter++ < FAKE_ONDEVICECHANGE_EVENT_REPEAT_COUNT) {
         RefPtr<FakeOnDeviceChangeEventRunnable> evt = new FakeOnDeviceChangeEventRunnable(mCounter);
-        CamerasSingleton::Thread()->DelayedDispatch(evt.forget(),
+        CamerasSingleton::FakeDeviceChangeEventThread()->DelayedDispatch(evt.forget(),
           FAKE_ONDEVICECHANGE_EVENT_PERIOD_IN_MS);
       }
     }
 
     return NS_OK;
   }
 
 private:
@@ -585,16 +586,24 @@ CamerasChild::ShutdownChild()
                                              &nsIThread::Shutdown));
     CamerasSingleton::Thread()->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
   } else {
     LOG(("Shutdown called without PBackground thread"));
   }
   LOG(("Erasing sCameras & thread refs (original thread)"));
   CamerasSingleton::Child() = nullptr;
   CamerasSingleton::Thread() = nullptr;
+
+  if (CamerasSingleton::FakeDeviceChangeEventThread()) {
+    RefPtr<ShutdownRunnable> runnable =
+      new ShutdownRunnable(NewRunnableMethod(CamerasSingleton::FakeDeviceChangeEventThread(),
+                                             &nsIThread::Shutdown));
+    CamerasSingleton::FakeDeviceChangeEventThread()->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
+  }
+  CamerasSingleton::FakeDeviceChangeEventThread() = nullptr;
 }
 
 bool
 CamerasChild::RecvDeliverFrame(const CaptureEngine& capEngine,
                                const int& capId,
                                mozilla::ipc::Shmem&& shmem,
                                const size_t& size,
                                const uint32_t& time_stamp,
@@ -622,20 +631,29 @@ CamerasChild::RecvDeviceChange()
   return true;
 }
 
 int
 CamerasChild::SetFakeDeviceChangeEvents()
 {
   CamerasSingleton::Mutex().AssertCurrentThreadOwns();
 
+  if(!CamerasSingleton::FakeDeviceChangeEventThread()) {
+    nsresult rv = NS_NewNamedThread("Fake DC Event",
+                                    getter_AddRefs(CamerasSingleton::FakeDeviceChangeEventThread()));
+    if (NS_FAILED(rv)) {
+      LOG(("Error launching Fake OnDeviceChange Event Thread"));
+      return -1;
+    }
+  }
+
   // To simulate the devicechange event in mochitest,
   // we fire a fake devicechange event in Camera IPC thread periodically
   RefPtr<FakeOnDeviceChangeEventRunnable> evt = new FakeOnDeviceChangeEventRunnable(0);
-  CamerasSingleton::Thread()->Dispatch(evt.forget(), NS_DISPATCH_NORMAL);
+  CamerasSingleton::FakeDeviceChangeEventThread()->Dispatch(evt.forget(), NS_DISPATCH_NORMAL);
 
   return 0;
 }
 
 bool
 CamerasChild::RecvFrameSizeChange(const CaptureEngine& capEngine,
                                   const int& capId,
                                   const int& w, const int& h)
--- a/dom/media/systemservices/CamerasChild.h
+++ b/dom/media/systemservices/CamerasChild.h
@@ -79,32 +79,38 @@ public:
     return gTheInstance.get()->mCameras;
   }
 
   static nsCOMPtr<nsIThread>& Thread() {
     Mutex().AssertCurrentThreadOwns();
     return gTheInstance.get()->mCamerasChildThread;
   }
 
+  static nsCOMPtr<nsIThread>& FakeDeviceChangeEventThread() {
+    Mutex().AssertCurrentThreadOwns();
+    return gTheInstance.get()->mFakeDeviceChangeEventThread;
+  }
+
 private:
   static Singleton<CamerasSingleton> gTheInstance;
 
   // Reinitializing CamerasChild will change the pointers below.
   // We don't want this to happen in the middle of preparing IPC.
   // We will be alive on destruction, so this needs to be off the books.
   mozilla::OffTheBooksMutex mCamerasMutex;
 
   // This is owned by the IPC code, and the same code controls the lifetime.
   // It will set and clear this pointer as appropriate in setup/teardown.
   // We'd normally make this a WeakPtr but unfortunately the IPC code already
   // uses the WeakPtr mixin in a protected base class of CamerasChild, and in
   // any case the object becomes unusable as soon as IPC is tearing down, which
   // will be before actual destruction.
   CamerasChild* mCameras;
   nsCOMPtr<nsIThread> mCamerasChildThread;
+  nsCOMPtr<nsIThread> mFakeDeviceChangeEventThread;
 };
 
 // Get a pointer to a CamerasChild object we can use to do IPC with.
 // This does everything needed to set up, including starting the IPC
 // channel with PBackground, blocking until thats done, and starting the
 // thread to do IPC on. This will fail if we're in shutdown. On success
 // it will set up the CamerasSingleton.
 CamerasChild* GetCamerasChild();
--- a/dom/plugins/base/nsJSNPRuntime.cpp
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -534,17 +534,17 @@ NPVariantToJSVal(NPP npp, JSContext *cx,
   }
 
   NS_ERROR("Unable to convert NPVariant to jsval!");
 
   return JS::UndefinedValue();
 }
 
 bool
-JSValToNPVariant(NPP npp, JSContext *cx, JS::Value val, NPVariant *variant)
+JSValToNPVariant(NPP npp, JSContext *cx, const JS::Value& val, NPVariant *variant)
 {
   NS_ASSERTION(npp, "Must have an NPP to wrap a jsval!");
 
   if (val.isPrimitive()) {
     if (val.isUndefined()) {
       VOID_TO_NPVARIANT(*variant);
     } else if (val.isNull()) {
       NULL_TO_NPVARIANT(*variant);
--- a/dom/plugins/base/nsJSNPRuntime.h
+++ b/dom/plugins/base/nsJSNPRuntime.h
@@ -97,12 +97,12 @@ class nsNPObjWrapper
 {
 public:
   static bool IsWrapper(JSObject *obj);
   static void OnDestroy(NPObject *npobj);
   static JSObject *GetNewOrUsed(NPP npp, JSContext *cx, NPObject *npobj);
 };
 
 bool
-JSValToNPVariant(NPP npp, JSContext *cx, JS::Value val, NPVariant *variant);
+JSValToNPVariant(NPP npp, JSContext *cx, const JS::Value& val, NPVariant *variant);
 
 
 #endif // nsJSNPRuntime_h_
--- a/dom/webidl/DataTransfer.webidl
+++ b/dom/webidl/DataTransfer.webidl
@@ -14,31 +14,31 @@ interface DataTransfer {
 
   readonly attribute DataTransferItemList items;
 
   [Throws]
   void setDragImage(Element image, long x, long y);
 
   [Throws]
   readonly attribute DOMStringList types;
-  [Throws]
+  [Throws, NeedsSubjectPrincipal]
   DOMString getData(DOMString format);
-  [Throws]
+  [Throws, NeedsSubjectPrincipal]
   void setData(DOMString format, DOMString data);
   [Throws, NeedsSubjectPrincipal]
   void clearData(optional DOMString format);
-  [Throws]
+  [Throws, NeedsSubjectPrincipal]
   readonly attribute FileList? files;
 };
 
 partial interface DataTransfer {
-  [Throws, Pref="dom.input.dirpicker"]
+  [Throws, Pref="dom.input.dirpicker", NeedsSubjectPrincipal]
   Promise<sequence<(File or Directory)>> getFilesAndDirectories();
 
-  [Throws, Pref="dom.input.dirpicker"]
+  [Throws, Pref="dom.input.dirpicker", NeedsSubjectPrincipal]
   Promise<sequence<File>>                getFiles(optional boolean recursiveFlag = false);
 };
 
 // Mozilla specific stuff
 partial interface DataTransfer {
   /*
    * Set the drag source. Usually you would not change this, but it will
    * affect which node the drag and dragend events are fired at. The
@@ -110,29 +110,29 @@ partial interface DataTransfer {
    * (which will be converted into a string) or an nsISupports.
    *
    * @param format the format to add
    * @param data the data to add
    * @throws NS_ERROR_NULL_POINTER if the data is null
    * @throws NS_ERROR_DOM_INDEX_SIZE_ERR if index is greater than itemCount
    * @throws NO_MODIFICATION_ALLOWED_ERR if the item cannot be modified
    */
-  [Throws]
+  [Throws, NeedsSubjectPrincipal]
   void mozSetDataAt(DOMString format, any data, unsigned long index);
 
   /**
    * Retrieve the data associated with the given format for an item at the
    * specified index, or null if it does not exist. The index should be in the
    * range from zero to itemCount - 1.
    *
    * @param format the format of the data to look up
    * @returns the data of the given format, or null if it doesn't exist.
    * @throws NS_ERROR_DOM_INDEX_SIZE_ERR if index is greater or equal than itemCount
    */
-  [Throws]
+  [Throws, NeedsSubjectPrincipal]
   any mozGetDataAt(DOMString format, unsigned long index);
 
   /**
    * Will be true when the user has cancelled the drag (typically by pressing
    * Escape) and when the drag has been cancelled unexpectedly.  This will be
    * false otherwise, including when the drop has been rejected by its target.
    * This property is only relevant for the dragend event.
    */
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -6650,17 +6650,17 @@ EventTarget::IsOnCurrentThread(bool* aIs
   }
 
   return NS_OK;
 }
 
 BEGIN_WORKERS_NAMESPACE
 
 WorkerCrossThreadDispatcher*
-GetWorkerCrossThreadDispatcher(JSContext* aCx, JS::Value aWorker)
+GetWorkerCrossThreadDispatcher(JSContext* aCx, const JS::Value& aWorker)
 {
   if (!aWorker.isObject()) {
     return nullptr;
   }
 
   WorkerPrivate* w = nullptr;
   UNWRAP_OBJECT(Worker, &aWorker.toObject(), w);
   MOZ_ASSERT(w);
--- a/dom/workers/Workers.h
+++ b/dom/workers/Workers.h
@@ -343,17 +343,17 @@ public:
 
   // Generically useful function for running a bit of C++ code on the worker
   // thread.
   bool
   PostTask(WorkerTask* aTask);
 };
 
 WorkerCrossThreadDispatcher*
-GetWorkerCrossThreadDispatcher(JSContext* aCx, JS::Value aWorker);
+GetWorkerCrossThreadDispatcher(JSContext* aCx, const JS::Value& aWorker);
 
 // Random unique constant to facilitate JSPrincipal debugging
 const uint32_t kJSPrincipalsDebugToken = 0x7e2df9d2;
 
 namespace exceptions {
 
 // Implemented in Exceptions.cpp
 void
--- a/dom/xbl/nsXBLMaybeCompiled.h
+++ b/dom/xbl/nsXBLMaybeCompiled.h
@@ -109,16 +109,21 @@ struct BarrierMethods<nsXBLMaybeCompiled
                         prev.IsCompiled() ? prev.UnsafeGetJSFunction() : nullptr,
                         next.UnsafeGetJSFunction());
     } else if (prev.IsCompiled()) {
       Base::postBarrier(&prev.UnsafeGetJSFunction(),
                         prev.UnsafeGetJSFunction(),
                         nullptr);
     }
   }
+  static void exposeToJS(nsXBLMaybeCompiled<UncompiledT> fun) {
+    if (fun.IsCompiled()) {
+      JS::ExposeObjectToActiveJS(fun.UnsafeGetJSFunction());
+    }
+  }
 };
 
 template <class T>
 struct IsHeapConstructibleType<nsXBLMaybeCompiled<T>>
 { // Yes, this is the exception to the rule. Sorry.
   static constexpr bool value = true;
 };
 
--- a/dom/xbl/nsXBLProtoImplMethod.h
+++ b/dom/xbl/nsXBLProtoImplMethod.h
@@ -82,17 +82,17 @@ class nsXBLProtoImplMethod: public nsXBL
 public:
   explicit nsXBLProtoImplMethod(const char16_t* aName);
   virtual ~nsXBLProtoImplMethod();
 
   void AppendBodyText(const nsAString& aBody);
   void AddParameter(const nsAString& aName);
 
   void SetLineNumber(uint32_t aLineNumber);
-  
+
   virtual nsresult InstallMember(JSContext* aCx,
                                  JS::Handle<JSObject*> aTargetClassObject) override;
   virtual nsresult CompileMember(mozilla::dom::AutoJSAPI& jsapi, const nsString& aClassStr,
                                  JS::Handle<JSObject*> aClassObject) override;
 
   virtual void Trace(const TraceCallbacks& aCallbacks, void *aClosure) override;
 
   nsresult Read(nsIObjectInputStream* aStream);
@@ -132,17 +132,17 @@ protected:
   JS::Heap<nsXBLMaybeCompiled<nsXBLUncompiledMethod> > mMethod;
 };
 
 class nsXBLProtoImplAnonymousMethod : public nsXBLProtoImplMethod {
 public:
   explicit nsXBLProtoImplAnonymousMethod(const char16_t* aName) :
     nsXBLProtoImplMethod(aName)
   {}
-  
+
   nsresult Execute(nsIContent* aBoundElement, JSAddonId* aAddonId);
 
   // Override InstallMember; these methods never get installed as members on
   // binding instantiations (though they may hang out in mMembers on the
   // prototype implementation).
   virtual nsresult InstallMember(JSContext* aCx,
                                  JS::Handle<JSObject*> aTargetClassObject) override {
     return NS_OK;
--- a/gfx/2d/DrawTargetD2D1.cpp
+++ b/gfx/2d/DrawTargetD2D1.cpp
@@ -1415,16 +1415,17 @@ IntersectRect(const D2D1_RECT_F& aRect1,
 
 bool
 DrawTargetD2D1::GetDeviceSpaceClipRect(D2D1_RECT_F& aClipRect, bool& aIsPixelAligned)
 {
   if (!CurrentLayer().mPushedClips.size()) {
     return false;
   }
 
+  aIsPixelAligned = true;
   aClipRect = D2D1::RectF(0, 0, mSize.width, mSize.height);
   for (auto iter = CurrentLayer().mPushedClips.begin();iter != CurrentLayer().mPushedClips.end(); iter++) {
     if (iter->mGeometry) {
       return false;
     }
     aClipRect = IntersectRect(aClipRect, iter->mBounds);
     if (!iter->mIsPixelAligned) {
       aIsPixelAligned = false;
--- a/gfx/cairo/libpixman/src/pixman-image.c
+++ b/gfx/cairo/libpixman/src/pixman-image.c
@@ -28,16 +28,35 @@
 #include <stdio.h>
 #include <string.h>
 #include <assert.h>
 
 #include "pixman-private.h"
 
 static const pixman_color_t transparent_black = { 0, 0, 0, 0 };
 
+/**
+ ** bug 1293598 - clean up every pointer after free to avoid
+ ** "dereferencing freed memory" problem
+ **/
+#define PIXMAN_POSION
+
+static void
+free_memory (void** p)
+{
+#ifdef PIXMAN_POISON
+    if (*p) {
+#endif
+        free (*p);
+#ifdef PIXMAN_POISON
+        *p = NULL;
+    }
+#endif
+}
+
 static void
 gradient_property_changed (pixman_image_t *image)
 {
     gradient_t *gradient = &image->gradient;
     int n = gradient->n_stops;
     pixman_gradient_stop_t *stops = gradient->stops;
     pixman_gradient_stop_t *begin = &(gradient->stops[-1]);
     pixman_gradient_stop_t *end = &(gradient->stops[n]);
@@ -140,42 +159,46 @@ pixman_bool_t
 
     if (common->ref_count == 0)
     {
 	if (image->common.destroy_func)
 	    image->common.destroy_func (image, image->common.destroy_data);
 
 	pixman_region32_fini (&common->clip_region);
 
-	free (common->transform);
-	free (common->filter_params);
+	free_memory (&common->transform);
+	free_memory (&common->filter_params);
 
 	if (common->alpha_map)
 	    pixman_image_unref ((pixman_image_t *)common->alpha_map);
 
 	if (image->type == LINEAR ||
 	    image->type == RADIAL ||
 	    image->type == CONICAL)
 	{
 	    if (image->gradient.stops)
 	    {
 		/* See _pixman_init_gradient() for an explanation of the - 1 */
-		free (image->gradient.stops - 1);
+		void *addr = image->gradient.stops - 1;
+		free_memory (&addr);
 	    }
 
 	    /* This will trigger if someone adds a property_changed
 	     * method to the linear/radial/conical gradient overwriting
 	     * the general one.
 	     */
 	    assert (
 		image->common.property_changed == gradient_property_changed);
 	}
 
-	if (image->type == BITS && image->bits.free_me)
-	    free (image->bits.free_me);
+	if (image->type == BITS && image->bits.free_me) {
+	    free_memory (&image->bits.free_me);
+	    image->bits.bits = NULL;
+        }
+
 
 	return TRUE;
     }
 
     return FALSE;
 }
 
 pixman_image_t *
@@ -205,17 +228,17 @@ pixman_image_ref (pixman_image_t *image)
 }
 
 /* returns TRUE when the image is freed */
 PIXMAN_EXPORT pixman_bool_t
 pixman_image_unref (pixman_image_t *image)
 {
     if (_pixman_image_fini (image))
     {
-	free (image);
+	free_memory (&image);
 	return TRUE;
     }
 
     return FALSE;
 }
 
 PIXMAN_EXPORT void
 pixman_image_set_destroy_function (pixman_image_t *            image,
--- a/gfx/ipc/GPUProcessManager.cpp
+++ b/gfx/ipc/GPUProcessManager.cpp
@@ -54,16 +54,17 @@ void
 GPUProcessManager::Shutdown()
 {
   sSingleton = nullptr;
 }
 
 GPUProcessManager::GPUProcessManager()
  : mTaskFactory(this),
    mNextLayerTreeId(0),
+   mNumProcessAttempts(0),
    mProcess(nullptr),
    mGPUChild(nullptr)
 {
   mObserver = new Observer(this);
   nsContentUtils::RegisterShutdownObserver(mObserver);
 
   LayerTreeOwnerTracker::Initialize();
 }
@@ -102,36 +103,42 @@ GPUProcessManager::OnXPCOMShutdown()
     nsContentUtils::UnregisterShutdownObserver(mObserver);
     mObserver = nullptr;
   }
 
   CleanShutdown();
 }
 
 void
-GPUProcessManager::EnableGPUProcess()
+GPUProcessManager::LaunchGPUProcess()
 {
   if (mProcess) {
     return;
   }
 
   // Start the Vsync I/O thread so can use it as soon as the process launches.
   EnsureVsyncIOThread();
 
+  mNumProcessAttempts++;
+
   // The subprocess is launched asynchronously, so we wait for a callback to
   // acquire the IPDL actor.
   mProcess = new GPUProcessHost(this);
   if (!mProcess->Launch()) {
     DisableGPUProcess("Failed to launch GPU process");
   }
 }
 
 void
 GPUProcessManager::DisableGPUProcess(const char* aMessage)
 {
+  if (!gfxConfig::IsEnabled(Feature::GPU_PROCESS)) {
+    return;
+  }
+
   gfxConfig::SetFailed(Feature::GPU_PROCESS, FeatureStatus::Failed, aMessage);
   gfxCriticalNote << aMessage;
 
   DestroyProcess();
   ShutdownVsyncIOThread();
 }
 
 void
@@ -242,16 +249,23 @@ GPUProcessManager::OnProcessLaunchComple
 
 void
 GPUProcessManager::OnProcessUnexpectedShutdown(GPUProcessHost* aHost)
 {
   MOZ_ASSERT(mProcess && mProcess == aHost);
 
   DestroyProcess();
 
+  if (mNumProcessAttempts > uint32_t(gfxPrefs::GPUProcessDevMaxRestarts())) {
+    DisableGPUProcess("GPU processed crashed too many times");
+  }
+  if (gfxConfig::IsEnabled(Feature::GPU_PROCESS)) {
+    LaunchGPUProcess();
+  }
+
   // The shutdown and restart sequence for the GPU process is as follows:
   //
   //  (1) The GPU process dies. IPDL will enqueue an ActorDestroy message on
   //      each channel owning a bridge to the GPU process, on the thread
   //      owning that channel.
   //
   //  (2) The first channel to process its ActorDestroy message will post a
   //      message to the main thread to call NotifyRemoteActorDestroyed on
--- a/gfx/ipc/GPUProcessManager.h
+++ b/gfx/ipc/GPUProcessManager.h
@@ -68,17 +68,17 @@ class GPUProcessManager final : public G
 public:
   static void Initialize();
   static void Shutdown();
   static GPUProcessManager* Get();
 
   ~GPUProcessManager();
 
   // If not using a GPU process, launch a new GPU process asynchronously.
-  void EnableGPUProcess();
+  void LaunchGPUProcess();
 
   // Ensure that GPU-bound methods can be used. If no GPU process is being
   // used, or one is launched and ready, this function returns immediately.
   // Otherwise it blocks until the GPU process has finished launching.
   void EnsureGPUReady();
 
   RefPtr<CompositorSession> CreateTopLevelCompositor(
     nsBaseWidget* aWidget,
@@ -189,16 +189,17 @@ private:
   };
   friend class Observer;
 
 private:
   RefPtr<Observer> mObserver;
   ipc::TaskFactory<GPUProcessManager> mTaskFactory;
   RefPtr<VsyncIOThreadHolder> mVsyncIOThread;
   uint64_t mNextLayerTreeId;
+  uint32_t mNumProcessAttempts;
 
   nsTArray<RefPtr<RemoteCompositorSession>> mRemoteSessions;
   nsTArray<GPUProcessListener*> mListeners;
 
   // Fields that are associated with the current GPU process.
   GPUProcessHost* mProcess;
   MOZ_INIT_OUTSIDE_CTOR uint64_t mProcessToken;
   GPUChild* mGPUChild;
--- a/gfx/ipc/RemoteCompositorSession.cpp
+++ b/gfx/ipc/RemoteCompositorSession.cpp
@@ -1,15 +1,16 @@
 /* -*- 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 "RemoteCompositorSession.h"
+#include "mozilla/VsyncDispatcher.h"
 #include "mozilla/layers/APZChild.h"
 #include "mozilla/layers/APZCTreeManagerChild.h"
 #include "nsBaseWidget.h"
 
 namespace mozilla {
 namespace layers {
 
 using namespace gfx;
--- a/gfx/layers/ipc/CompositorBridgeChild.cpp
+++ b/gfx/layers/ipc/CompositorBridgeChild.cpp
@@ -838,16 +838,25 @@ CompositorBridgeChild::SendNotifyApproxi
                                                             const CSSIntRegion& aRegion)
 {
   if (!mCanSend) {
     return false;
   }
   return PCompositorBridgeChild::SendNotifyApproximatelyVisibleRegion(aGuid, aRegion);
 }
 
+bool
+CompositorBridgeChild::SendAllPluginsCaptured()
+{
+  if (!mCanSend) {
+    return false;
+  }
+  return PCompositorBridgeChild::SendAllPluginsCaptured();
+}
+
 PTextureChild*
 CompositorBridgeChild::AllocPTextureChild(const SurfaceDescriptor&,
                                           const LayersBackend&,
                                           const TextureFlags&,
                                           const uint64_t&,
                                           const uint64_t& aSerial)
 {
   return TextureClient::CreateIPDLActor();
--- a/gfx/layers/ipc/CompositorBridgeChild.h
+++ b/gfx/layers/ipc/CompositorBridgeChild.h
@@ -160,16 +160,17 @@ public:
   bool SendGetTileSize(int32_t* tileWidth, int32_t* tileHeight);
   bool SendStartFrameTimeRecording(const int32_t& bufferSize, uint32_t* startIndex);
   bool SendStopFrameTimeRecording(const uint32_t& startIndex, nsTArray<float>* intervals);
   bool SendNotifyRegionInvalidated(const nsIntRegion& region);
   bool SendRequestNotifyAfterRemotePaint();
   bool SendClearApproximatelyVisibleRegions(uint64_t aLayersId, uint32_t aPresShellId);
   bool SendNotifyApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid,
                                             const mozilla::CSSIntRegion& aRegion);
+  bool SendAllPluginsCaptured();
   bool IsSameProcess() const override;
 
   virtual bool IPCOpen() const override { return mCanSend; }
 
   static void ShutDown();
 
   void UpdateFwdTransactionId() { ++mFwdTransactionId; }
   uint64_t GetFwdTransactionId() { return mFwdTransactionId; }
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -2173,17 +2173,17 @@ gfxPlatform::InitAcceleration()
           FeatureStatus::Unavailable,
           "Multi-process mode is not enabled",
           NS_LITERAL_CSTRING("FEATURE_FAILURE_NO_E10S"));
       }
     }
 
     if (gpuProc.IsEnabled()) {
       GPUProcessManager* gpu = GPUProcessManager::Get();
-      gpu->EnableGPUProcess();
+      gpu->LaunchGPUProcess();
     }
   }
 }
 
 void
 gfxPlatform::InitCompositorAccelerationPrefs()
 {
   const char *acceleratedEnv = PR_GetEnv("MOZ_ACCELERATED");
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -474,16 +474,17 @@ private:
   DECL_GFX_PREF(Live, "layers.effect.grayscale",               LayersEffectGrayscale, bool, false);
   DECL_GFX_PREF(Live, "layers.effect.invert",                  LayersEffectInvert, bool, false);
   DECL_GFX_PREF(Once, "layers.enable-tiles",                   LayersTilesEnabled, bool, false);
   DECL_GFX_PREF(Live, "layers.flash-borders",                  FlashLayerBorders, bool, false);
   DECL_GFX_PREF(Once, "layers.force-shmem-tiles",              ForceShmemTiles, bool, false);
   DECL_GFX_PREF(Live, "layers.frame-counter",                  DrawFrameCounter, bool, false);
   DECL_GFX_PREF(Once, "layers.gpu-process.dev.enabled",        GPUProcessDevEnabled, bool, false);
   DECL_GFX_PREF(Once, "layers.gpu-process.dev.timeout_ms",     GPUProcessDevTimeoutMs, int32_t, 5000);
+  DECL_GFX_PREF(Live, "layers.gpu-process.dev.max_restarts",   GPUProcessDevMaxRestarts, int32_t, 0);
   DECL_GFX_PREF(Once, "layers.gralloc.disable",                DisableGralloc, bool, false);
   DECL_GFX_PREF(Live, "layers.low-precision-buffer",           UseLowPrecisionBuffer, bool, false);
   DECL_GFX_PREF(Live, "layers.low-precision-opacity",          LowPrecisionOpacity, float, 1.0f);
   DECL_GFX_PREF(Live, "layers.low-precision-resolution",       LowPrecisionResolution, float, 0.25f);
   DECL_GFX_PREF(Live, "layers.max-active",                     MaxActiveLayers, int32_t, -1);
   DECL_GFX_PREF(Once, "layers.offmainthreadcomposition.force-disabled", LayersOffMainThreadCompositionForceDisabled, bool, false);
   DECL_GFX_PREF(Live, "layers.offmainthreadcomposition.frame-rate", LayersCompositionFrameRate, int32_t,-1);
   DECL_GFX_PREF(Live, "layers.orientation.sync.timeout",       OrientationSyncMillis, uint32_t, (uint32_t)0);
--- a/image/imgFrame.cpp
+++ b/image/imgFrame.cpp
@@ -496,17 +496,17 @@ imgFrame::SurfaceForDrawing(bool        
                             ImageRegion&       aRegion,
                             SourceSurface*     aSurface)
 {
   MOZ_ASSERT(NS_IsMainThread());
   mMonitor.AssertCurrentThreadOwns();
 
   if (!aDoPartialDecode) {
     return SurfaceWithFormat(new gfxSurfaceDrawable(aSurface, mImageSize),
-                                                    mFormat);
+                             mFormat);
   }
 
   gfxRect available = gfxRect(mDecoded.x, mDecoded.y, mDecoded.width,
                               mDecoded.height);
 
   if (aDoTile) {
     // Create a temporary surface.
     // Give this surface an alpha channel because there are
--- a/ipc/testshell/TestShellParent.cpp
+++ b/ipc/testshell/TestShellParent.cpp
@@ -46,17 +46,17 @@ TestShellParent::CommandDone(TestShellCo
   /*bool ok = */command->RunCallback(aResponse);
   command->ReleaseCallback();
 
   return true;
 }
 
 bool
 TestShellCommandParent::SetCallback(JSContext* aCx,
-                                    JS::Value aCallback)
+                                    const JS::Value& aCallback)
 {
   if (!mCallback.initialized()) {
     mCallback.init(aCx, aCallback);
     return true;
   }
 
   mCallback = aCallback;
 
--- a/ipc/testshell/TestShellParent.h
+++ b/ipc/testshell/TestShellParent.h
@@ -37,17 +37,17 @@ public:
 };
 
 
 class TestShellCommandParent : public PTestShellCommandParent
 {
 public:
   TestShellCommandParent() {}
 
-  bool SetCallback(JSContext* aCx, JS::Value aCallback);
+  bool SetCallback(JSContext* aCx, const JS::Value& aCallback);
 
   bool RunCallback(const nsString& aResponse);
 
   void ReleaseCallback();
 
 protected:
   bool ExecuteCallback(const nsString& aResponse);
 
--- a/js/ipc/JavaScriptShared.cpp
+++ b/js/ipc/JavaScriptShared.cpp
@@ -78,16 +78,27 @@ IdToObjectMap::clear()
 }
 
 bool
 IdToObjectMap::empty() const
 {
     return table_.empty();
 }
 
+#ifdef DEBUG
+bool
+IdToObjectMap::has(const ObjectId& id, const JSObject* obj) const
+{
+    auto p = table_.lookup(id);
+    if (!p)
+        return false;
+    return p->value().unbarrieredGet() == obj;
+}
+#endif
+
 bool
 ObjectToIdMap::init()
 {
     return table_.initialized() || table_.init(32);
 }
 
 void
 ObjectToIdMap::trace(JSTracer* trc)
--- a/js/ipc/JavaScriptShared.h
+++ b/js/ipc/JavaScriptShared.h
@@ -96,16 +96,20 @@ class IdToObjectMap
 
     bool add(ObjectId id, JSObject* obj);
     JSObject* find(ObjectId id);
     void remove(ObjectId id);
 
     void clear();
     bool empty() const;
 
+#ifdef DEBUG
+    bool has(const ObjectId& id, const JSObject* obj) const;
+#endif
+
   private:
     Table table_;
 };
 
 // Map JSObjects -> ids
 class ObjectToIdMap
 {
     using Hasher = js::MovableCellHasher<JS::Heap<JSObject*>>;
@@ -168,16 +172,22 @@ class JavaScriptShared : public CPOWMana
     static void ConvertID(const nsID& from, JSIID* to);
     static void ConvertID(const JSIID& from, nsID* to);
 
     JSObject* findCPOWById(const ObjectId& objId) {
         return cpows_.find(objId);
     }
     JSObject* findObjectById(JSContext* cx, const ObjectId& objId);
 
+#ifdef DEBUG
+    bool hasCPOW(const ObjectId& objId, const JSObject* obj) {
+        return cpows_.has(objId, obj);
+    }
+#endif
+
     static bool LoggingEnabled() { return sLoggingEnabled; }
     static bool StackLoggingEnabled() { return sStackLoggingEnabled; }
 
     friend class Logging;
 
     virtual bool isParent() = 0;
 
     virtual JSObject* scopeForTargetObjects() = 0;
--- a/js/ipc/WrapperOwner.cpp
+++ b/js/ipc/WrapperOwner.cpp
@@ -78,27 +78,27 @@ WrapperOwner::idOfUnchecked(JSObject* ob
     MOZ_ASSERT(!aux->id.isNull());
     return aux->id;
 }
 
 ObjectId
 WrapperOwner::idOf(JSObject* obj)
 {
     ObjectId objId = idOfUnchecked(obj);
-    MOZ_ASSERT(findCPOWById(objId) == obj);
+    MOZ_ASSERT(hasCPOW(objId, obj));
     return objId;
 }
 
 class CPOWProxyHandler : public BaseProxyHandler
 {
   public:
     constexpr CPOWProxyHandler()
       : BaseProxyHandler(&family) {}
 
-    virtual bool finalizeInBackground(Value priv) const override {
+    virtual bool finalizeInBackground(const Value& priv) const override {
         return false;
     }
 
     virtual bool getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
                                           MutableHandle<PropertyDescriptor> desc) const override;
     virtual bool defineProperty(JSContext* cx, HandleObject proxy, HandleId id,
                                 Handle<PropertyDescriptor> desc,
                                 ObjectOpResult& result) const override;
@@ -925,17 +925,17 @@ WrapperOwner::drop(JSObject* obj)
         Unused << SendDropObject(objId);
     decref();
 }
 
 void
 WrapperOwner::updatePointer(JSObject* obj, const JSObject* old)
 {
     ObjectId objId = idOfUnchecked(obj);
-    MOZ_ASSERT(findCPOWById(objId) == old);
+    MOZ_ASSERT(hasCPOW(objId, old));
     cpows_.add(objId, obj);
 }
 
 bool
 WrapperOwner::init()
 {
     if (!JavaScriptShared::init())
         return false;
--- a/js/public/CallArgs.h
+++ b/js/public/CallArgs.h
@@ -87,17 +87,17 @@ namespace detail {
  * Compute |this| for the |vp| inside a JSNative, either boxing primitives or
  * replacing with the global object as necessary.
  */
 extern JS_PUBLIC_API(Value)
 ComputeThis(JSContext* cx, JS::Value* vp);
 
 #ifdef JS_DEBUG
 extern JS_PUBLIC_API(void)
-CheckIsValidConstructible(Value v);
+CheckIsValidConstructible(const Value& v);
 #endif
 
 class MOZ_STACK_CLASS IncludeUsedRval
 {
   protected:
 #ifdef JS_DEBUG
     mutable bool usedRval_;
     void setUsedRval() const { usedRval_ = true; }
@@ -236,22 +236,22 @@ class MOZ_STACK_CLASS CallArgsBase : pub
         return MutableHandleValue::fromMarkedLocation(&argv_[-2]);
     }
 
   public:
     // These methods are publicly exposed, but they are *not* to be used when
     // implementing a JSNative method and encapsulating access to |vp| within
     // it.  You probably don't want to use these!
 
-    void setCallee(Value aCalleev) const {
+    void setCallee(const Value& aCalleev) const {
         this->clearUsedRval();
         argv_[-2] = aCalleev;
     }
 
-    void setThis(Value aThisv) const {
+    void setThis(const Value& aThisv) const {
         argv_[-1] = aThisv;
     }
 
     MutableHandleValue mutableThisv() const {
         return MutableHandleValue::fromMarkedLocation(&argv_[-1]);
     }
 
   public:
--- a/js/public/Conversions.h
+++ b/js/public/Conversions.h
@@ -25,17 +25,17 @@ struct JSContext;
 namespace js {
 
 /* DO NOT CALL THIS. Use JS::ToBoolean. */
 extern JS_PUBLIC_API(bool)
 ToBooleanSlow(JS::HandleValue v);
 
 /* DO NOT CALL THIS.  Use JS::ToNumber. */
 extern JS_PUBLIC_API(bool)
-ToNumberSlow(JSContext* cx, JS::Value v, double* dp);
+ToNumberSlow(JSContext* cx, const JS::Value& v, double* dp);
 
 /* DO NOT CALL THIS. Use JS::ToInt8. */
 extern JS_PUBLIC_API(bool)
 ToInt8Slow(JSContext *cx, JS::HandleValue v, int8_t *out);
 
 /* DO NOT CALL THIS. Use JS::ToUint8. */
 extern JS_PUBLIC_API(bool)
 ToUint8Slow(JSContext *cx, JS::HandleValue v, uint8_t *out);
--- a/js/public/HeapAPI.h
+++ b/js/public/HeapAPI.h
@@ -357,41 +357,16 @@ GetStringZone(JSString* str)
 {
     return js::gc::detail::GetGCThingZone(uintptr_t(str));
 }
 
 extern JS_PUBLIC_API(Zone*)
 GetObjectZone(JSObject* obj);
 
 static MOZ_ALWAYS_INLINE bool
-ObjectIsTenured(JSObject* obj)
-{
-    return !js::gc::IsInsideNursery(reinterpret_cast<js::gc::Cell*>(obj));
-}
-
-static MOZ_ALWAYS_INLINE bool
-ObjectIsMarkedGray(JSObject* obj)
-{
-    /*
-     * GC things residing in the nursery cannot be gray: they have no mark bits.
-     * All live objects in the nursery are moved to tenured at the beginning of
-     * each GC slice, so the gray marker never sees nursery things.
-     */
-    if (js::gc::IsInsideNursery(reinterpret_cast<js::gc::Cell*>(obj)))
-        return false;
-    return js::gc::detail::CellIsMarkedGray(reinterpret_cast<js::gc::Cell*>(obj));
-}
-
-static MOZ_ALWAYS_INLINE bool
-ScriptIsMarkedGray(JSScript* script)
-{
-    return js::gc::detail::CellIsMarkedGray(reinterpret_cast<js::gc::Cell*>(script));
-}
-
-static MOZ_ALWAYS_INLINE bool
 GCThingIsMarkedGray(GCCellPtr thing)
 {
     if (js::gc::IsInsideNursery(thing.asCell()))
         return false;
     if (thing.mayBeOwnedByOtherRuntime())
         return false;
     return js::gc::detail::CellIsMarkedGray(thing.asCell());
 }
--- a/js/public/Id.h
+++ b/js/public/Id.h
@@ -186,23 +186,27 @@ struct DefaultHasher<jsid>
         return id1 == id2;
     }
 };
 
 template <>
 struct BarrierMethods<jsid>
 {
     static void postBarrier(jsid* idp, jsid prev, jsid next) {}
+    static void exposeToJS(jsid id) {
+        if (JSID_IS_GCTHING(id))
+            js::gc::ExposeGCThingToActiveJS(JSID_TO_GCTHING(id));
+    }
 };
 
 // If the jsid is a GC pointer type, convert to that type and call |f| with
 // the pointer. If the jsid is not a GC type, calls F::defaultValue.
 template <typename F, typename... Args>
 auto
-DispatchTyped(F f, jsid& id, Args&&... args)
+DispatchTyped(F f, const jsid& id, Args&&... args)
   -> decltype(f(static_cast<JSString*>(nullptr), mozilla::Forward<Args>(args)...))
 {
     if (JSID_IS_STRING(id))
         return f(JSID_TO_STRING(id), mozilla::Forward<Args>(args)...);
     if (JSID_IS_SYMBOL(id))
         return f(JSID_TO_SYMBOL(id), mozilla::Forward<Args>(args)...);
     MOZ_ASSERT(!JSID_IS_GCTHING(id));
     return F::defaultValue(id);
--- a/js/public/Proxy.h
+++ b/js/public/Proxy.h
@@ -206,17 +206,17 @@ class JS_FRIEND_API(BaseProxyHandler)
 
     inline const void* family() const {
         return mFamily;
     }
     static size_t offsetOfFamily() {
         return offsetof(BaseProxyHandler, mFamily);
     }
 
-    virtual bool finalizeInBackground(Value priv) const {
+    virtual bool finalizeInBackground(const Value& priv) const {
         /*
          * Called on creation of a proxy to determine whether its finalize
          * method can be finalized on the background thread.
          */
         return true;
     }
 
     virtual bool canNurseryAllocate() const {
@@ -529,17 +529,17 @@ class MOZ_STACK_CLASS ProxyOptions {
     const Class* clasp_;
 };
 
 JS_FRIEND_API(JSObject*)
 NewProxyObject(JSContext* cx, const BaseProxyHandler* handler, HandleValue priv,
                JSObject* proto, const ProxyOptions& options = ProxyOptions());
 
 JSObject*
-RenewProxyObject(JSContext* cx, JSObject* obj, BaseProxyHandler* handler, Value priv);
+RenewProxyObject(JSContext* cx, JSObject* obj, BaseProxyHandler* handler, const Value& priv);
 
 class JS_FRIEND_API(AutoEnterPolicy)
 {
   public:
     typedef BaseProxyHandler::Action Action;
     AutoEnterPolicy(JSContext* cx, const BaseProxyHandler* handler,
                     HandleObject wrapper, HandleId id, Action act, bool mayThrow)
 #ifdef JS_DEBUG
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -233,71 +233,129 @@ class Heap : public js::HeapBase<T>
     static_assert(js::IsHeapConstructibleType<T>::value,
                   "Type T must be a public GC pointer type");
   public:
     Heap() {
         static_assert(sizeof(T) == sizeof(Heap<T>),
                       "Heap<T> must be binary compatible with T.");
         init(GCPolicy<T>::initial());
     }
-    explicit Heap(T p) { init(p); }
+    explicit Heap(const T& p) { init(p); }
 
     /*
      * For Heap, move semantics are equivalent to copy semantics. In C++, a
      * copy constructor taking const-ref is the way to get a single function
      * that will be used for both lvalue and rvalue copies, so we can simply
      * omit the rvalue variant.
      */
     explicit Heap(const Heap<T>& p) { init(p.ptr); }
 
     ~Heap() {
         post(ptr, GCPolicy<T>::initial());
     }
 
     DECLARE_POINTER_CONSTREF_OPS(T);
     DECLARE_POINTER_ASSIGN_OPS(Heap, T);
-    DECLARE_NONPOINTER_ACCESSOR_METHODS(ptr);
+
+    const T* address() const { return &ptr; }
+    const T& get() const {
+        js::BarrierMethods<T>::exposeToJS(ptr);
+        return ptr;
+    }
+    const T& unbarrieredGet() const {
+        return ptr;
+    }
 
     T* unsafeGet() { return &ptr; }
 
+    explicit operator bool() const {
+        return bool(js::BarrierMethods<T>::asGCThingOrNull(ptr));
+    }
+    explicit operator bool() {
+        return bool(js::BarrierMethods<T>::asGCThingOrNull(ptr));
+    }
+
     /*
      * Set the pointer to a value which will cause a crash if it is
      * dereferenced.
      */
     void setToCrashOnTouch() {
         ptr = reinterpret_cast<T>(crashOnTouchPointer);
     }
 
     bool isSetToCrashOnTouch() {
         return ptr == crashOnTouchPointer;
     }
 
   private:
-    void init(T newPtr) {
+    void init(const T& newPtr) {
         ptr = newPtr;
         post(GCPolicy<T>::initial(), ptr);
     }
 
-    void set(T newPtr) {
+    void set(const T& newPtr) {
         T tmp = ptr;
         ptr = newPtr;
         post(tmp, ptr);
     }
 
     void post(const T& prev, const T& next) {
         js::BarrierMethods<T>::postBarrier(&ptr, prev, next);
     }
 
     enum {
         crashOnTouchPointer = 1
     };
 
     T ptr;
 };
 
+static MOZ_ALWAYS_INLINE bool
+ObjectIsTenured(JSObject* obj)
+{
+    return !js::gc::IsInsideNursery(reinterpret_cast<js::gc::Cell*>(obj));
+}
+
+static MOZ_ALWAYS_INLINE bool
+ObjectIsTenured(const Heap<JSObject*>& obj)
+{
+    return ObjectIsTenured(obj.unbarrieredGet());
+}
+
+static MOZ_ALWAYS_INLINE bool
+ObjectIsMarkedGray(JSObject* obj)
+{
+    /*
+     * GC things residing in the nursery cannot be gray: they have no mark bits.
+     * All live objects in the nursery are moved to tenured at the beginning of
+     * each GC slice, so the gray marker never sees nursery things.
+     */
+    if (js::gc::IsInsideNursery(reinterpret_cast<js::gc::Cell*>(obj)))
+        return false;
+    return js::gc::detail::CellIsMarkedGray(reinterpret_cast<js::gc::Cell*>(obj));
+}
+
+static MOZ_ALWAYS_INLINE bool
+ObjectIsMarkedGray(const JS::Heap<JSObject*>& obj)
+{
+    return ObjectIsMarkedGray(obj.unbarrieredGet());
+}
+
+static MOZ_ALWAYS_INLINE bool
+ScriptIsMarkedGray(JSScript* script)
+{
+    return js::gc::detail::CellIsMarkedGray(reinterpret_cast<js::gc::Cell*>(script));
+}
+
+static MOZ_ALWAYS_INLINE bool
+ScriptIsMarkedGray(const Heap<JSScript*>& script)
+{
+    return ScriptIsMarkedGray(script.unbarrieredGet());
+}
+
 /**
  * The TenuredHeap<T> class is similar to the Heap<T> class above in that it
  * encapsulates the GC concerns of an on-heap reference to a JS object. However,
  * it has two important differences:
  *
  *  1) Pointers which are statically known to only reference "tenured" objects
  *     can avoid the extra overhead of SpiderMonkey's write barriers.
  *
@@ -487,17 +545,17 @@ class MOZ_STACK_CLASS MutableHandle : pu
     inline MOZ_IMPLICIT MutableHandle(Rooted<T>* root);
     inline MOZ_IMPLICIT MutableHandle(PersistentRooted<T>* root);
 
   private:
     // Disallow nullptr for overloading purposes.
     MutableHandle(decltype(nullptr)) = delete;
 
   public:
-    void set(T v) {
+    void set(const T& v) {
         *ptr = v;
     }
 
     /*
      * This may be called only if the location of the T is guaranteed
      * to be marked (for some reason other than being a Rooted),
      * e.g., if it is guaranteed to be reachable from an implicit root.
      *
@@ -523,47 +581,70 @@ class MOZ_STACK_CLASS MutableHandle : pu
 } /* namespace JS */
 
 namespace js {
 
 template <typename T>
 struct BarrierMethods<T*>
 {
     static T* initial() { return nullptr; }
+    static gc::Cell* asGCThingOrNull(T* v) {
+        if (!v)
+            return nullptr;
+        MOZ_ASSERT(uintptr_t(v) > 32);
+        return reinterpret_cast<gc::Cell*>(v);
+    }
     static void postBarrier(T** vp, T* prev, T* next) {
         if (next)
             JS::AssertGCThingIsNotAnObjectSubclass(reinterpret_cast<js::gc::Cell*>(next));
     }
-    static void relocate(T** vp) {}
+    static void exposeToJS(T* t) {
+        if (t)
+            js::gc::ExposeGCThingToActiveJS(JS::GCCellPtr(t));
+    }
 };
 
 template <>
 struct BarrierMethods<JSObject*>
 {
     static JSObject* initial() { return nullptr; }
     static gc::Cell* asGCThingOrNull(JSObject* v) {
         if (!v)
             return nullptr;
         MOZ_ASSERT(uintptr_t(v) > 32);
         return reinterpret_cast<gc::Cell*>(v);
     }
     static void postBarrier(JSObject** vp, JSObject* prev, JSObject* next) {
         JS::HeapObjectPostBarrier(vp, prev, next);
     }
+    static void exposeToJS(JSObject* obj) {
+        if (obj)
+            JS::ExposeObjectToActiveJS(obj);
+    }
 };
 
 template <>
 struct BarrierMethods<JSFunction*>
 {
     static JSFunction* initial() { return nullptr; }
+    static gc::Cell* asGCThingOrNull(JSFunction* v) {
+        if (!v)
+            return nullptr;
+        MOZ_ASSERT(uintptr_t(v) > 32);
+        return reinterpret_cast<gc::Cell*>(v);
+    }
     static void postBarrier(JSFunction** vp, JSFunction* prev, JSFunction* next) {
         JS::HeapObjectPostBarrier(reinterpret_cast<JSObject**>(vp),
                                   reinterpret_cast<JSObject*>(prev),
                                   reinterpret_cast<JSObject*>(next));
     }
+    static void exposeToJS(JSFunction* fun) {
+        if (fun)
+            JS::ExposeObjectToActiveJS(reinterpret_cast<JSObject*>(fun));
+    }
 };
 
 // Provide hash codes for Cell kinds that may be relocated and, thus, not have
 // a stable address to use as the base for a hash code. Instead of the address,
 // this hasher uses Cell::getUniqueId to provide exact matches and as a base
 // for generating hash codes.
 //
 // Note: this hasher, like PointerHasher can "hash" a nullptr. While a nullptr
@@ -589,17 +670,19 @@ template <typename T>
 struct JS_PUBLIC_API(MovableCellHasher<JS::Heap<T>>)
 {
     using Key = JS::Heap<T>;
     using Lookup = T;
 
     static bool hasHash(const Lookup& l) { return MovableCellHasher<T>::hasHash(l); }
     static bool ensureHash(const Lookup& l) { return MovableCellHasher<T>::ensureHash(l); }
     static HashNumber hash(const Lookup& l) { return MovableCellHasher<T>::hash(l); }
-    static bool match(const Key& k, const Lookup& l) { return MovableCellHasher<T>::match(k, l); }
+    static bool match(const Key& k, const Lookup& l) {
+        return MovableCellHasher<T>::match(k.unbarrieredGet(), l);
+    }
     static void rekey(Key& k, const Key& newKey) { k.unsafeSet(newKey); }
 };
 
 template <typename T>
 struct FallibleHashMethods<MovableCellHasher<T>>
 {
     template <typename Lookup> static bool hasHash(Lookup&& l) {
         return MovableCellHasher<T>::hasHash(mozilla::Forward<Lookup>(l));
@@ -708,17 +791,17 @@ class MOZ_RAII Rooted : public js::Roote
     }
 
     Rooted<T>* previous() { return reinterpret_cast<Rooted<T>*>(prev); }
 
     /*
      * This method is public for Rooted so that Codegen.py can use a Rooted
      * interchangeably with a MutableHandleValue.
      */
-    void set(T value) {
+    void set(const T& value) {
         ptr = value;
     }
 
     DECLARE_POINTER_COMPARISON_OPS(T);
     DECLARE_POINTER_CONSTREF_OPS(T);
     DECLARE_POINTER_ASSIGN_OPS(Rooted, T);
     DECLARE_NONPOINTER_ACCESSOR_METHODS(ptr);
     DECLARE_NONPOINTER_MUTABLE_ACCESSOR_METHODS(ptr);
@@ -824,17 +907,17 @@ class FakeMutableHandle : public js::Mut
     MOZ_IMPLICIT FakeMutableHandle(T* t) {
         ptr = t;
     }
 
     MOZ_IMPLICIT FakeMutableHandle(FakeRooted<T>* root) {
         ptr = root->address();
     }
 
-    void set(T v) {
+    void set(const T& v) {
         *ptr = v;
     }
 
     DECLARE_POINTER_CONSTREF_OPS(T);
     DECLARE_NONPOINTER_ACCESSOR_METHODS(*ptr);
     DECLARE_NONPOINTER_MUTABLE_ACCESSOR_METHODS(*ptr);
 
   private:
@@ -881,17 +964,17 @@ template <typename T> class MaybeRooted<
     static inline JS::Handle<T2*> downcastHandle(HandleType v) {
         return v.template as<T2>();
     }
 };
 
 template <typename T> class MaybeRooted<T, NoGC>
 {
   public:
-    typedef T HandleType;
+    typedef const T& HandleType;
     typedef FakeRooted<T> RootType;
     typedef FakeMutableHandle<T> MutableHandleType;
 
     static JS::Handle<T> toHandle(HandleType v) {
         MOZ_CRASH("Bad conversion");
     }
 
     static JS::MutableHandle<T> toMutableHandle(MutableHandleType v) {
@@ -1109,16 +1192,17 @@ class JS_PUBLIC_API(ObjectPtr)
     ~ObjectPtr() { MOZ_ASSERT(!value); }
 
     void finalize(JSRuntime* rt);
     void finalize(JSContext* cx);
 
     void init(JSObject* obj) { value = obj; }
 
     JSObject* get() const { return value; }
+    JSObject* unbarrieredGet() const { return value.unbarrieredGet(); }
 
     void writeBarrierPre(JSContext* cx) {
         IncrementalObjectBarrier(value);
     }
 
     void updateWeakPointerAfterGC();
 
     ObjectPtr& operator=(JSObject* obj) {
@@ -1127,16 +1211,19 @@ class JS_PUBLIC_API(ObjectPtr)
         return *this;
     }
 
     void trace(JSTracer* trc, const char* name);
 
     JSObject& operator*() const { return *value; }
     JSObject* operator->() const { return value; }
     operator JSObject*() const { return value; }
+
+    explicit operator bool() const { return value.unbarrieredGet(); }
+    explicit operator bool() { return value.unbarrieredGet(); }
 };
 
 } /* namespace JS */
 
 namespace js {
 
 template <typename Outer, typename T, typename D>
 class UniquePtrOperations
--- a/js/public/Value.h
+++ b/js/public/Value.h
@@ -388,38 +388,38 @@ JS_STATIC_ASSERT(sizeof(jsval_layout) ==
 
 static inline JS_VALUE_CONSTEXPR jsval_layout
 BUILD_JSVAL(JSValueTag tag, uint32_t payload)
 {
     JS_RETURN_LAYOUT_FROM_BITS((((uint64_t)(uint32_t)tag) << 32) | payload);
 }
 
 static inline bool
-JSVAL_IS_DOUBLE_IMPL(jsval_layout l)
+JSVAL_IS_DOUBLE_IMPL(const jsval_layout& l)
 {
     return (uint32_t)l.s.tag <= (uint32_t)JSVAL_TAG_CLEAR;
 }
 
 static inline jsval_layout
 DOUBLE_TO_JSVAL_IMPL(double d)
 {
     jsval_layout l;
     l.asDouble = d;
     MOZ_ASSERT(JSVAL_IS_DOUBLE_IMPL(l));
     return l;
 }
 
 static inline bool
-JSVAL_IS_INT32_IMPL(jsval_layout l)
+JSVAL_IS_INT32_IMPL(const jsval_layout& l)
 {
     return l.s.tag == JSVAL_TAG_INT32;
 }
 
 static inline int32_t
-JSVAL_TO_INT32_IMPL(jsval_layout l)
+JSVAL_TO_INT32_IMPL(const jsval_layout& l)
 {
     return l.s.payload.i32;
 }
 
 static inline JS_VALUE_CONSTEXPR jsval_layout
 INT32_TO_JSVAL_IMPL(int32_t i)
 {
 #if defined(JS_VALUE_IS_CONSTEXPR)
@@ -428,154 +428,154 @@ INT32_TO_JSVAL_IMPL(int32_t i)
     jsval_layout l;
     l.s.tag = JSVAL_TAG_INT32;
     l.s.payload.i32 = i;
     return l;
 #endif
 }
 
 static inline bool
-JSVAL_IS_NUMBER_IMPL(jsval_layout l)
+JSVAL_IS_NUMBER_IMPL(const jsval_layout& l)
 {
     JSValueTag tag = l.s.tag;
     MOZ_ASSERT(tag != JSVAL_TAG_CLEAR);
     return (uint32_t)tag <= (uint32_t)JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET;
 }
 
 static inline bool
-JSVAL_IS_UNDEFINED_IMPL(jsval_layout l)
+JSVAL_IS_UNDEFINED_IMPL(const jsval_layout& l)
 {
     return l.s.tag == JSVAL_TAG_UNDEFINED;
 }
 
 static inline bool
-JSVAL_IS_STRING_IMPL(jsval_layout l)
+JSVAL_IS_STRING_IMPL(const jsval_layout& l)
 {
     return l.s.tag == JSVAL_TAG_STRING;
 }
 
 static inline jsval_layout
 STRING_TO_JSVAL_IMPL(JSString* str)
 {
     jsval_layout l;
     MOZ_ASSERT(uintptr_t(str) > 0x1000);
     l.s.tag = JSVAL_TAG_STRING;
     l.s.payload.str = str;
     return l;
 }
 
 static inline JSString*
-JSVAL_TO_STRING_IMPL(jsval_layout l)
+JSVAL_TO_STRING_IMPL(const jsval_layout& l)
 {
     return l.s.payload.str;
 }
 
 static inline bool
-JSVAL_IS_SYMBOL_IMPL(jsval_layout l)
+JSVAL_IS_SYMBOL_IMPL(const jsval_layout& l)
 {
     return l.s.tag == JSVAL_TAG_SYMBOL;
 }
 
 static inline jsval_layout
 SYMBOL_TO_JSVAL_IMPL(JS::Symbol* sym)
 {
     jsval_layout l;
     MOZ_ASSERT(uintptr_t(sym) > 0x1000);
     l.s.tag = JSVAL_TAG_SYMBOL;
     l.s.payload.sym = sym;
     return l;
 }
 
 static inline JS::Symbol*
-JSVAL_TO_SYMBOL_IMPL(jsval_layout l)
+JSVAL_TO_SYMBOL_IMPL(const jsval_layout& l)
 {
     return l.s.payload.sym;
 }
 
 static inline bool
-JSVAL_IS_BOOLEAN_IMPL(jsval_layout l)
+JSVAL_IS_BOOLEAN_IMPL(const jsval_layout& l)
 {
     return l.s.tag == JSVAL_TAG_BOOLEAN;
 }
 
 static inline bool
-JSVAL_TO_BOOLEAN_IMPL(jsval_layout l)
+JSVAL_TO_BOOLEAN_IMPL(const jsval_layout& l)
 {
     return bool(l.s.payload.boo);
 }
 
 static inline jsval_layout
 BOOLEAN_TO_JSVAL_IMPL(bool b)
 {
     jsval_layout l;
     l.s.tag = JSVAL_TAG_BOOLEAN;
     l.s.payload.boo = uint32_t(b);
     return l;
 }
 
 static inline bool
-JSVAL_IS_MAGIC_IMPL(jsval_layout l)
+JSVAL_IS_MAGIC_IMPL(const jsval_layout& l)
 {
     return l.s.tag == JSVAL_TAG_MAGIC;
 }
 
 static inline bool
-JSVAL_IS_OBJECT_IMPL(jsval_layout l)
+JSVAL_IS_OBJECT_IMPL(const jsval_layout& l)
 {
     return l.s.tag == JSVAL_TAG_OBJECT;
 }
 
 static inline bool
-JSVAL_IS_PRIMITIVE_IMPL(jsval_layout l)
+JSVAL_IS_PRIMITIVE_IMPL(const jsval_layout& l)
 {
     return (uint32_t)l.s.tag < (uint32_t)JSVAL_UPPER_EXCL_TAG_OF_PRIMITIVE_SET;
 }
 
 static inline bool
-JSVAL_IS_OBJECT_OR_NULL_IMPL(jsval_layout l)
+JSVAL_IS_OBJECT_OR_NULL_IMPL(const jsval_layout& l)
 {
     MOZ_ASSERT((uint32_t)l.s.tag <= (uint32_t)JSVAL_TAG_OBJECT);
     return (uint32_t)l.s.tag >= (uint32_t)JSVAL_LOWER_INCL_TAG_OF_OBJ_OR_NULL_SET;
 }
 
 static inline JSObject*
-JSVAL_TO_OBJECT_IMPL(jsval_layout l)
+JSVAL_TO_OBJECT_IMPL(const jsval_layout& l)
 {
     return l.s.payload.obj;
 }
 
 static inline jsval_layout
 OBJECT_TO_JSVAL_IMPL(JSObject* obj)
 {
     jsval_layout l;
     MOZ_ASSERT(uintptr_t(obj) > 0x1000 || uintptr_t(obj) == 0x48);
     l.s.tag = JSVAL_TAG_OBJECT;
     l.s.payload.obj = obj;
     return l;
 }
 
 static inline bool
-JSVAL_IS_NULL_IMPL(jsval_layout l)
+JSVAL_IS_NULL_IMPL(const jsval_layout& l)
 {
     return l.s.tag == JSVAL_TAG_NULL;
 }
 
 static inline jsval_layout
 PRIVATE_PTR_TO_JSVAL_IMPL(void* ptr)
 {
     jsval_layout l;
     MOZ_ASSERT((uintptr_t(ptr) & 1) == 0);
     l.s.tag = (JSValueTag)0;
     l.s.payload.ptr = ptr;
     MOZ_ASSERT(JSVAL_IS_DOUBLE_IMPL(l));
     return l;
 }
 
 static inline void*
-JSVAL_TO_PRIVATE_PTR_IMPL(jsval_layout l)
+JSVAL_TO_PRIVATE_PTR_IMPL(const jsval_layout& l)
 {
     return l.s.payload.ptr;
 }
 
 static inline jsval_layout
 PRIVATE_GCTHING_TO_JSVAL_IMPL(js::gc::Cell* cell)
 {
     MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::String,
@@ -588,56 +588,56 @@ PRIVATE_GCTHING_TO_JSVAL_IMPL(js::gc::Ce
     jsval_layout l;
     MOZ_ASSERT(uintptr_t(cell) > 0x1000);
     l.s.tag = JSVAL_TAG_PRIVATE_GCTHING;
     l.s.payload.cell = cell;
     return l;
 }
 
 static inline bool
-JSVAL_IS_PRIVATE_GCTHING_IMPL(jsval_layout l)
+JSVAL_IS_PRIVATE_GCTHING_IMPL(const jsval_layout& l)
 {
     return l.s.tag == JSVAL_TAG_PRIVATE_GCTHING;
 }
 
 static inline bool
-JSVAL_IS_GCTHING_IMPL(jsval_layout l)
+JSVAL_IS_GCTHING_IMPL(const jsval_layout& l)
 {
     /* gcc sometimes generates signed < without explicit casts. */
     return (uint32_t)l.s.tag >= (uint32_t)JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET;
 }
 
 static inline js::gc::Cell*
-JSVAL_TO_GCTHING_IMPL(jsval_layout l)
+JSVAL_TO_GCTHING_IMPL(const jsval_layout& l)
 {
     return l.s.payload.cell;
 }
 
 static inline uint32_t
-JSVAL_TRACE_KIND_IMPL(jsval_layout l)
+JSVAL_TRACE_KIND_IMPL(const jsval_layout& l)
 {
     static_assert((JSVAL_TAG_STRING & 0x03) == size_t(JS::TraceKind::String),
                   "Value type tags must correspond with JS::TraceKinds.");
     static_assert((JSVAL_TAG_SYMBOL & 0x03) == size_t(JS::TraceKind::Symbol),
                   "Value type tags must correspond with JS::TraceKinds.");
     static_assert((JSVAL_TAG_OBJECT & 0x03) == size_t(JS::TraceKind::Object),
                   "Value type tags must correspond with JS::TraceKinds.");
     if (MOZ_UNLIKELY(JSVAL_IS_PRIVATE_GCTHING_IMPL(l)))
         return (uint32_t)JS::GCThingTraceKind(JSVAL_TO_GCTHING_IMPL(l));
     return l.s.tag & 0x03;
 }
 
 static inline bool
-JSVAL_IS_SPECIFIC_INT32_IMPL(jsval_layout l, int32_t i32)
+JSVAL_IS_SPECIFIC_INT32_IMPL(const jsval_layout& l, int32_t i32)
 {
     return l.s.tag == JSVAL_TAG_INT32 && l.s.payload.i32 == i32;
 }
 
 static inline bool
-JSVAL_IS_SPECIFIC_BOOLEAN_IMPL(jsval_layout l, bool b)
+JSVAL_IS_SPECIFIC_BOOLEAN_IMPL(const jsval_layout& l, bool b)
 {
     return (l.s.tag == JSVAL_TAG_BOOLEAN) && (l.s.payload.boo == uint32_t(b));
 }
 
 static inline jsval_layout
 MAGIC_TO_JSVAL_IMPL(JSWhyMagic why)
 {
     jsval_layout l;
@@ -651,177 +651,177 @@ MAGIC_UINT32_TO_JSVAL_IMPL(uint32_t payl
 {
     jsval_layout l;
     l.s.tag = JSVAL_TAG_MAGIC;
     l.s.payload.u32 = payload;
     return l;
 }
 
 static inline bool
-JSVAL_SAME_TYPE_IMPL(jsval_layout lhs, jsval_layout rhs)
+JSVAL_SAME_TYPE_IMPL(const jsval_layout& lhs, const jsval_layout& rhs)
 {
     JSValueTag ltag = lhs.s.tag, rtag = rhs.s.tag;
     return ltag == rtag || (ltag < JSVAL_TAG_CLEAR && rtag < JSVAL_TAG_CLEAR);
 }
 
 static inline JSValueType
-JSVAL_EXTRACT_NON_DOUBLE_TYPE_IMPL(jsval_layout l)
+JSVAL_EXTRACT_NON_DOUBLE_TYPE_IMPL(const jsval_layout& l)
 {
     uint32_t type = l.s.tag & 0xF;
     MOZ_ASSERT(type > JSVAL_TYPE_DOUBLE);
     return (JSValueType)type;
 }
 
 #elif defined(JS_PUNBOX64)
 
 static inline JS_VALUE_CONSTEXPR jsval_layout
 BUILD_JSVAL(JSValueTag tag, uint64_t payload)
 {
     JS_RETURN_LAYOUT_FROM_BITS((((uint64_t)(uint32_t)tag) << JSVAL_TAG_SHIFT) | payload);
 }
 
 static inline bool
-JSVAL_IS_DOUBLE_IMPL(jsval_layout l)
+JSVAL_IS_DOUBLE_IMPL(const jsval_layout& l)
 {
     return (l.asBits | mozilla::DoubleTypeTraits::kSignBit) <= JSVAL_SHIFTED_TAG_MAX_DOUBLE;
 }
 
 static inline jsval_layout
 DOUBLE_TO_JSVAL_IMPL(double d)
 {
     jsval_layout l;
     l.asDouble = d;
     MOZ_ASSERT(JSVAL_IS_DOUBLE_IMPL(l));
     return l;
 }
 
 static inline bool
-JSVAL_IS_INT32_IMPL(jsval_layout l)
+JSVAL_IS_INT32_IMPL(const jsval_layout& l)
 {
     return (uint32_t)(l.asBits >> JSVAL_TAG_SHIFT) == JSVAL_TAG_INT32;
 }
 
 static inline int32_t
-JSVAL_TO_INT32_IMPL(jsval_layout l)
+JSVAL_TO_INT32_IMPL(const jsval_layout& l)
 {
     return (int32_t)l.asBits;
 }
 
 static inline JS_VALUE_CONSTEXPR jsval_layout
 INT32_TO_JSVAL_IMPL(int32_t i32)
 {
     JS_RETURN_LAYOUT_FROM_BITS(((uint64_t)(uint32_t)i32) | JSVAL_SHIFTED_TAG_INT32);
 }
 
 static inline bool
-JSVAL_IS_NUMBER_IMPL(jsval_layout l)
+JSVAL_IS_NUMBER_IMPL(const jsval_layout& l)
 {
     return l.asBits < JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_NUMBER_SET;
 }
 
 static inline bool
-JSVAL_IS_UNDEFINED_IMPL(jsval_layout l)
+JSVAL_IS_UNDEFINED_IMPL(const jsval_layout& l)
 {
     return l.asBits == JSVAL_SHIFTED_TAG_UNDEFINED;
 }
 
 static inline bool
-JSVAL_IS_STRING_IMPL(jsval_layout l)
+JSVAL_IS_STRING_IMPL(const jsval_layout& l)
 {
     return (uint32_t)(l.asBits >> JSVAL_TAG_SHIFT) == JSVAL_TAG_STRING;
 }
 
 static inline jsval_layout
 STRING_TO_JSVAL_IMPL(JSString* str)
 {
     jsval_layout l;
     uint64_t strBits = (uint64_t)str;
     MOZ_ASSERT(uintptr_t(str) > 0x1000);
     MOZ_ASSERT((strBits >> JSVAL_TAG_SHIFT) == 0);
     l.asBits = strBits | JSVAL_SHIFTED_TAG_STRING;
     return l;
 }
 
 static inline JSString*
-JSVAL_TO_STRING_IMPL(jsval_layout l)
+JSVAL_TO_STRING_IMPL(const jsval_layout& l)
 {
     return (JSString*)(l.asBits & JSVAL_PAYLOAD_MASK);
 }
 
 static inline bool
-JSVAL_IS_SYMBOL_IMPL(jsval_layout l)
+JSVAL_IS_SYMBOL_IMPL(const jsval_layout& l)
 {
     return (uint32_t)(l.asBits >> JSVAL_TAG_SHIFT) == JSVAL_TAG_SYMBOL;
 }
 
 static inline jsval_layout
 SYMBOL_TO_JSVAL_IMPL(JS::Symbol* sym)
 {
     jsval_layout l;
     uint64_t symBits = (uint64_t)sym;
     MOZ_ASSERT(uintptr_t(sym) > 0x1000);
     MOZ_ASSERT((symBits >> JSVAL_TAG_SHIFT) == 0);
     l.asBits = symBits | JSVAL_SHIFTED_TAG_SYMBOL;
     return l;
 }
 
 static inline JS::Symbol*
-JSVAL_TO_SYMBOL_IMPL(jsval_layout l)
+JSVAL_TO_SYMBOL_IMPL(const jsval_layout& l)
 {
     return (JS::Symbol*)(l.asBits & JSVAL_PAYLOAD_MASK);
 }
 
 static inline bool
-JSVAL_IS_BOOLEAN_IMPL(jsval_layout l)
+JSVAL_IS_BOOLEAN_IMPL(const jsval_layout& l)
 {
     return (uint32_t)(l.asBits >> JSVAL_TAG_SHIFT) == JSVAL_TAG_BOOLEAN;
 }
 
 static inline bool
-JSVAL_TO_BOOLEAN_IMPL(jsval_layout l)
+JSVAL_TO_BOOLEAN_IMPL(const jsval_layout& l)
 {
     return (bool)(l.asBits & JSVAL_PAYLOAD_MASK);
 }
 
 static inline jsval_layout
 BOOLEAN_TO_JSVAL_IMPL(bool b)
 {
     jsval_layout l;
     l.asBits = ((uint64_t)(uint32_t)b) | JSVAL_SHIFTED_TAG_BOOLEAN;
     return l;
 }
 
 static inline bool
-JSVAL_IS_MAGIC_IMPL(jsval_layout l)
+JSVAL_IS_MAGIC_IMPL(const jsval_layout& l)
 {
     return (l.asBits >> JSVAL_TAG_SHIFT) == JSVAL_TAG_MAGIC;
 }
 
 static inline bool
-JSVAL_IS_PRIMITIVE_IMPL(jsval_layout l)
+JSVAL_IS_PRIMITIVE_IMPL(const jsval_layout& l)
 {
     return l.asBits < JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_PRIMITIVE_SET;
 }
 
 static inline bool
-JSVAL_IS_OBJECT_IMPL(jsval_layout l)
+JSVAL_IS_OBJECT_IMPL(const jsval_layout& l)
 {
     MOZ_ASSERT((l.asBits >> JSVAL_TAG_SHIFT) <= JSVAL_TAG_OBJECT);
     return l.asBits >= JSVAL_SHIFTED_TAG_OBJECT;
 }
 
 static inline bool
-JSVAL_IS_OBJECT_OR_NULL_IMPL(jsval_layout l)
+JSVAL_IS_OBJECT_OR_NULL_IMPL(const jsval_layout& l)
 {
     MOZ_ASSERT((l.asBits >> JSVAL_TAG_SHIFT) <= JSVAL_TAG_OBJECT);
     return l.asBits >= JSVAL_LOWER_INCL_SHIFTED_TAG_OF_OBJ_OR_NULL_SET;
 }
 
 static inline JSObject*
-JSVAL_TO_OBJECT_IMPL(jsval_layout l)
+JSVAL_TO_OBJECT_IMPL(const jsval_layout& l)
 {
     uint64_t ptrBits = l.asBits & JSVAL_PAYLOAD_MASK;
     MOZ_ASSERT((ptrBits & 0x7) == 0);
     return (JSObject*)ptrBits;
 }
 
 static inline jsval_layout
 OBJECT_TO_JSVAL_IMPL(JSObject* obj)
@@ -830,43 +830,43 @@ OBJECT_TO_JSVAL_IMPL(JSObject* obj)
     uint64_t objBits = (uint64_t)obj;
     MOZ_ASSERT(uintptr_t(obj) > 0x1000 || uintptr_t(obj) == 0x48);
     MOZ_ASSERT((objBits >> JSVAL_TAG_SHIFT) == 0);
     l.asBits = objBits | JSVAL_SHIFTED_TAG_OBJECT;
     return l;
 }
 
 static inline bool
-JSVAL_IS_NULL_IMPL(jsval_layout l)
+JSVAL_IS_NULL_IMPL(const jsval_layout& l)
 {
     return l.asBits == JSVAL_SHIFTED_TAG_NULL;
 }
 
 static inline bool
-JSVAL_IS_PRIVATE_GCTHING_IMPL(jsval_layout l)
+JSVAL_IS_PRIVATE_GCTHING_IMPL(const jsval_layout& l)
 {
     return (l.asBits >> JSVAL_TAG_SHIFT) == JSVAL_TAG_PRIVATE_GCTHING;
 }
 
 static inline bool
-JSVAL_IS_GCTHING_IMPL(jsval_layout l)
+JSVAL_IS_GCTHING_IMPL(const jsval_layout& l)
 {
     return l.asBits >= JSVAL_LOWER_INCL_SHIFTED_TAG_OF_GCTHING_SET;
 }
 
 static inline js::gc::Cell*
-JSVAL_TO_GCTHING_IMPL(jsval_layout l)
+JSVAL_TO_GCTHING_IMPL(const jsval_layout& l)
 {
     uint64_t ptrBits = l.asBits & JSVAL_PAYLOAD_MASK;
     MOZ_ASSERT((ptrBits & 0x7) == 0);
     return reinterpret_cast<js::gc::Cell*>(ptrBits);
 }
 
 static inline uint32_t
-JSVAL_TRACE_KIND_IMPL(jsval_layout l)
+JSVAL_TRACE_KIND_IMPL(const jsval_layout& l)
 {
     static_assert((JSVAL_TAG_STRING & 0x03) == size_t(JS::TraceKind::String),
                   "Value type tags must correspond with JS::TraceKinds.");
     static_assert((JSVAL_TAG_SYMBOL & 0x03) == size_t(JS::TraceKind::Symbol),
                   "Value type tags must correspond with JS::TraceKinds.");
     static_assert((JSVAL_TAG_OBJECT & 0x03) == size_t(JS::TraceKind::Object),
                   "Value type tags must correspond with JS::TraceKinds.");
     if (MOZ_UNLIKELY(JSVAL_IS_PRIVATE_GCTHING_IMPL(l)))
@@ -881,17 +881,17 @@ PRIVATE_PTR_TO_JSVAL_IMPL(void* ptr)
     uintptr_t ptrBits = uintptr_t(ptr);
     MOZ_ASSERT((ptrBits & 1) == 0);
     l.asBits = ptrBits >> 1;
     MOZ_ASSERT(JSVAL_IS_DOUBLE_IMPL(l));
     return l;
 }
 
 static inline void*
-JSVAL_TO_PRIVATE_PTR_IMPL(jsval_layout l)
+JSVAL_TO_PRIVATE_PTR_IMPL(const jsval_layout& l)
 {
     MOZ_ASSERT((l.asBits & 0x8000000000000000LL) == 0);
     return (void*)(l.asBits << 1);
 }
 
 static inline jsval_layout
 PRIVATE_GCTHING_TO_JSVAL_IMPL(js::gc::Cell* cell)
 {
@@ -906,23 +906,23 @@ PRIVATE_GCTHING_TO_JSVAL_IMPL(js::gc::Ce
     uint64_t cellBits = (uint64_t)cell;
     MOZ_ASSERT(uintptr_t(cellBits) > 0x1000);
     MOZ_ASSERT((cellBits >> JSVAL_TAG_SHIFT) == 0);
     l.asBits = cellBits | JSVAL_SHIFTED_TAG_PRIVATE_GCTHING;
     return l;
 }
 
 static inline bool
-JSVAL_IS_SPECIFIC_INT32_IMPL(jsval_layout l, int32_t i32)
+JSVAL_IS_SPECIFIC_INT32_IMPL(const jsval_layout& l, int32_t i32)
 {
     return l.asBits == (((uint64_t)(uint32_t)i32) | JSVAL_SHIFTED_TAG_INT32);
 }
 
 static inline bool
-JSVAL_IS_SPECIFIC_BOOLEAN_IMPL(jsval_layout l, bool b)
+JSVAL_IS_SPECIFIC_BOOLEAN_IMPL(const jsval_layout& l, bool b)
 {
     return l.asBits == (((uint64_t)(uint32_t)b) | JSVAL_SHIFTED_TAG_BOOLEAN);
 }
 
 static inline jsval_layout
 MAGIC_TO_JSVAL_IMPL(JSWhyMagic why)
 {
     jsval_layout l;
@@ -934,40 +934,40 @@ static inline jsval_layout
 MAGIC_UINT32_TO_JSVAL_IMPL(uint32_t payload)
 {
     jsval_layout l;
     l.asBits = ((uint64_t)payload) | JSVAL_SHIFTED_TAG_MAGIC;
     return l;
 }
 
 static inline bool
-JSVAL_SAME_TYPE_IMPL(jsval_layout lhs, jsval_layout rhs)
+JSVAL_SAME_TYPE_IMPL(const jsval_layout& lhs, const jsval_layout& rhs)
 {
     return (JSVAL_IS_DOUBLE_IMPL(lhs) && JSVAL_IS_DOUBLE_IMPL(rhs)) ||
            (((lhs.asBits ^ rhs.asBits) & 0xFFFF800000000000LL) == 0);
 }
 
 static inline JSValueType
-JSVAL_EXTRACT_NON_DOUBLE_TYPE_IMPL(jsval_layout l)
+JSVAL_EXTRACT_NON_DOUBLE_TYPE_IMPL(const jsval_layout& l)
 {
    uint64_t type = (l.asBits >> JSVAL_TAG_SHIFT) & 0xF;
    MOZ_ASSERT(type > JSVAL_TYPE_DOUBLE);
    return (JSValueType)type;
 }
 
 #endif  /* JS_PUNBOX64 */
 
 static inline bool
-JSVAL_IS_TRACEABLE_IMPL(jsval_layout l)
+JSVAL_IS_TRACEABLE_IMPL(const jsval_layout& l)
 {
     return JSVAL_IS_GCTHING_IMPL(l) && !JSVAL_IS_NULL_IMPL(l);
 }
 
-static inline jsval_layout JSVAL_TO_IMPL(JS::Value v);
-static inline JS_VALUE_CONSTEXPR JS::Value IMPL_TO_JSVAL(jsval_layout l);
+static inline jsval_layout JSVAL_TO_IMPL(const JS::Value& v);
+static inline JS_VALUE_CONSTEXPR JS::Value IMPL_TO_JSVAL(const jsval_layout& l);
 
 namespace JS {
 
 static inline JS_VALUE_CONSTEXPR JS::Value UndefinedValue();
 
 /**
  * Returns a generic quiet NaN value, with all payload bits set to zero.
  *
@@ -1387,28 +1387,28 @@ class Value
   // precisely, we don't want Value return values compiled as out params.
   private:
 #endif
 
     jsval_layout data;
 
   private:
 #if defined(JS_VALUE_IS_CONSTEXPR)
-    MOZ_IMPLICIT JS_VALUE_CONSTEXPR Value(jsval_layout layout) : data(layout) {}
+    MOZ_IMPLICIT JS_VALUE_CONSTEXPR Value(const jsval_layout& layout) : data(layout) {}
 #endif
 
     void staticAssertions() {
         JS_STATIC_ASSERT(sizeof(JSValueType) == 1);
         JS_STATIC_ASSERT(sizeof(JSValueTag) == 4);
         JS_STATIC_ASSERT(sizeof(JSWhyMagic) <= 4);
         JS_STATIC_ASSERT(sizeof(Value) == 8);
     }
 
-    friend jsval_layout (::JSVAL_TO_IMPL)(Value);
-    friend Value JS_VALUE_CONSTEXPR (::IMPL_TO_JSVAL)(jsval_layout l);
+    friend jsval_layout (::JSVAL_TO_IMPL)(const Value&);
+    friend Value JS_VALUE_CONSTEXPR (::IMPL_TO_JSVAL)(const jsval_layout& l);
     friend Value JS_VALUE_CONSTEXPR (JS::UndefinedValue)();
 } JS_HAZ_GC_POINTER;
 
 inline bool
 IsOptimizedPlaceholderMagicValue(const Value& v)
 {
     if (v.isMagic()) {
         MOZ_ASSERT(v.whyMagic() == JS_OPTIMIZED_ARGUMENTS || v.whyMagic() == JS_OPTIMIZED_OUT);
@@ -1734,16 +1734,19 @@ template <>
 struct BarrierMethods<JS::Value>
 {
     static gc::Cell* asGCThingOrNull(const JS::Value& v) {
         return v.isMarkable() ? v.toGCThing() : nullptr;
     }
     static void postBarrier(JS::Value* v, const JS::Value& prev, const JS::Value& next) {
         JS::HeapValuePostBarrier(v, prev, next);
     }
+    static void exposeToJS(JS::Value v) {
+        JS::ExposeValueToActiveJS(v);
+    }
 };
 
 template <class Outer> class MutableValueOperations;
 
 /**
  * A class designed for CRTP use in implementing the non-mutating parts of the
  * Value interface in Value-like classes.  Outer must be a class inheriting
  * ValueOperations<Outer> with a visible get() method returning a const
@@ -1917,30 +1920,30 @@ DispatchTyped(F f, const JS::Value& val,
     if (val.isSymbol())
         return f(val.toSymbol(), mozilla::Forward<Args>(args)...);
     if (MOZ_UNLIKELY(val.isPrivateGCThing()))
         return DispatchTyped(f, val.toGCCellPtr(), mozilla::Forward<Args>(args)...);
     MOZ_ASSERT(!val.isMarkable());
     return F::defaultValue(val);
 }
 
-template <class S> struct VoidDefaultAdaptor { static void defaultValue(S) {} };
+template <class S> struct VoidDefaultAdaptor { static void defaultValue(const S&) {} };
 template <class S> struct IdentityDefaultAdaptor { static S defaultValue(const S& v) {return v;} };
-template <class S, bool v> struct BoolDefaultAdaptor { static bool defaultValue(S) { return v; } };
+template <class S, bool v> struct BoolDefaultAdaptor { static bool defaultValue(const S&) { return v; } };
 
 } // namespace js
 
 inline jsval_layout
-JSVAL_TO_IMPL(JS::Value v)
+JSVAL_TO_IMPL(const JS::Value& v)
 {
     return v.data;
 }
 
 inline JS_VALUE_CONSTEXPR JS::Value
-IMPL_TO_JSVAL(jsval_layout l)
+IMPL_TO_JSVAL(const jsval_layout& l)
 {
 #if defined(JS_VALUE_IS_CONSTEXPR)
     return JS::Value(l);
 #else
     JS::Value v;
     v.data = l;
     return v;
 #endif
--- a/js/src/asmjs/AsmJS.cpp
+++ b/js/src/asmjs/AsmJS.cpp
@@ -853,17 +853,17 @@ class NumLit
     union {
         Value scalar_;
         SimdConstant simd_;
     } u;
 
   public:
     NumLit() = default;
 
-    NumLit(Which w, Value v) : which_(w) {
+    NumLit(Which w, const Value& v) : which_(w) {
         u.scalar_ = v;
         MOZ_ASSERT(!isSimd());
     }
 
     NumLit(Which w, SimdConstant c) : which_(w) {
         u.simd_ = c;
         MOZ_ASSERT(isSimd());
     }
@@ -1291,16 +1291,56 @@ class Type
         }
     }
 
     // Convert this canonical type to a wasm::ValType.
     ValType canonicalToValType() const {
         return NonVoidToValType(canonicalToExprType());
     }
 
+    // Convert this type to a wasm::ExprType for use in a wasm
+    // block signature. This works for all types, including non-canonical
+    // ones. Consequently, the type isn't valid for subsequent asm.js
+    // validation; it's only valid for use in producing wasm.
+    ExprType toWasmBlockSignatureType() const {
+        switch (which()) {
+          case Fixnum:
+          case Signed:
+          case Unsigned:
+          case Int:
+          case Intish:
+            return ExprType::I32;
+
+          case Float:
+          case MaybeFloat:
+          case Floatish:
+            return ExprType::F32;
+
+          case DoubleLit:
+          case Double:
+          case MaybeDouble:
+            return ExprType::F64;
+
+          case Void:
+            return ExprType::Void;
+
+          case Uint8x16:
+          case Int8x16:   return ExprType::I8x16;
+          case Uint16x8:
+          case Int16x8:   return ExprType::I16x8;
+          case Uint32x4:
+          case Int32x4:   return ExprType::I32x4;
+          case Float32x4: return ExprType::F32x4;
+          case Bool8x16:  return ExprType::B8x16;
+          case Bool16x8:  return ExprType::B16x8;
+          case Bool32x4:  return ExprType::B32x4;
+        }
+        MOZ_CRASH("Invalid Type");
+    }
+
     const char* toChars() const {
         switch (which_) {
           case Double:      return "double";
           case DoubleLit:   return "doublelit";
           case MaybeDouble: return "double?";
           case Float:       return "float";
           case Floatish:    return "floatish";
           case MaybeFloat:  return "float?";
@@ -2643,17 +2683,17 @@ ExtractNumericLiteral(ModuleValidator& m
         MOZ_ASSERT(i64 <= UINT32_MAX);
         return NumLit(NumLit::BigUnsigned, Int32Value(uint32_t(i64)));
     }
     MOZ_ASSERT(i64 >= INT32_MIN);
     return NumLit(NumLit::NegativeInt, Int32Value(i64));
 }
 
 static inline bool
-IsLiteralInt(NumLit lit, uint32_t* u32)
+IsLiteralInt(const NumLit& lit, uint32_t* u32)
 {
     switch (lit.which()) {
       case NumLit::Fixnum:
       case NumLit::BigUnsigned:
       case NumLit::NegativeInt:
         *u32 = lit.toUint32();
         return true;
       case NumLit::Double:
@@ -3102,17 +3142,17 @@ class MOZ_STACK_CLASS FunctionValidator
     /**************************************************** Encoding interface */
 
     Encoder& encoder() { return *encoder_; }
 
     MOZ_MUST_USE bool writeInt32Lit(int32_t i32) {
         return encoder().writeExpr(Expr::I32Const) &&
                encoder().writeVarS32(i32);
     }
-    MOZ_MUST_USE bool writeConstExpr(NumLit lit) {
+    MOZ_MUST_USE bool writeConstExpr(const NumLit& lit) {
         switch (lit.which()) {
           case NumLit::Fixnum:
           case NumLit::NegativeInt:
           case NumLit::BigUnsigned:
             return writeInt32Lit(lit.toInt32());
           case NumLit::Float:
             return encoder().writeExpr(Expr::F32Const) &&
                    encoder().writeFixedF32(lit.toFloat());
@@ -5944,17 +5984,17 @@ CheckComma(FunctionValidator& f, ParseNo
     for (; NextNode(pn); pn = NextNode(pn)) {
         if (!CheckAsExprStatement(f, pn))
             return false;
     }
 
     if (!CheckExpr(f, pn, type))
         return false;
 
-    f.encoder().patchFixedU7(typeAt, uint8_t(Type::canonicalize(*type).canonicalToExprType()));
+    f.encoder().patchFixedU7(typeAt, uint8_t(type->toWasmBlockSignatureType()));
 
     return f.encoder().writeExpr(Expr::End);
 }
 
 static bool
 CheckConditional(FunctionValidator& f, ParseNode* ternary, Type* type)
 {
     MOZ_ASSERT(ternary->isKind(PNK_CONDITIONAL));
@@ -5994,17 +6034,17 @@ CheckConditional(FunctionValidator& f, P
     } else if (thenType.isSimd() && elseType == thenType) {
         *type = thenType;
     } else {
         return f.failf(ternary, "then/else branches of conditional must both produce int, float, "
                        "double or SIMD types, current types are %s and %s",
                        thenType.toChars(), elseType.toChars());
     }
 
-    if (!f.popIf(typeAt, Type::canonicalize(*type).canonicalToExprType()))
+    if (!f.popIf(typeAt, type->toWasmBlockSignatureType()))
         return false;
 
     return true;
 }
 
 static bool
 IsValidIntMultiplyConstant(ModuleValidator& m, ParseNode* expr)
 {
--- a/js/src/asmjs/WasmBinaryIterator.h
+++ b/js/src/asmjs/WasmBinaryIterator.h
@@ -1107,37 +1107,41 @@ template <typename Policy>
 inline bool
 ExprIter<Policy>::readBrTableEntry(ExprType* type, Value* value, uint32_t* depth)
 {
     MOZ_ASSERT(Classify(expr_) == ExprKind::BrTable);
 
     if (!readVarU32(depth))
         return false;
 
+    ExprType knownType = *type;
+
     if (MOZ_LIKELY(reachable_)) {
         ControlStackEntry<ControlItem>* controlItem = nullptr;
         if (!getControl(*depth, &controlItem))
             return false;
 
         if (controlItem->kind() != LabelKind::Loop) {
             controlItem->setReachable();
 
             // If we've already seen one label, we know the type and can check
             // that the type for the current label matches it.
-            ExprType knownType = *type;
             if (knownType != ExprType::Limit)
                 return checkType(knownType, controlItem->type());
 
             // This is the first label; record the type and the value now.
             ExprType expectedType = controlItem->type();
             if (!IsVoid(expectedType)) {
                 *type = expectedType;
                 return popWithType(NonVoidToValType(expectedType), value);
             }
         }
+
+        if (knownType != ExprType::Limit && knownType != ExprType::Void)
+            return typeMismatch(knownType, ExprType::Void);
     }
 
     *type = ExprType::Void;
     if (Output)
         *value = Value();
     return true;
 }
 
--- a/js/src/asmjs/WasmIonCompile.cpp
+++ b/js/src/asmjs/WasmIonCompile.cpp
@@ -307,17 +307,17 @@ class FunctionCompiler
         if (inDeadCode())
             return nullptr;
         MInstruction* constant;
         constant = MSimdConstant::New(alloc(), v, type);
         curBlock_->add(constant);
         return constant;
     }
 
-    MDefinition* constant(Value v, MIRType type)
+    MDefinition* constant(const Value& v, MIRType type)
     {
         if (inDeadCode())
             return nullptr;
         MConstant* constant = MConstant::NewAsmJS(alloc(), v, type);
         curBlock_->add(constant);
         return constant;
     }
 
@@ -1660,45 +1660,45 @@ EmitBlock(FunctionCompiler& f)
 }
 
 static bool
 EmitLoop(FunctionCompiler& f)
 {
     if (!f.iter().readLoop())
         return false;
 
-    MBasicBlock *loopHeader;
+    MBasicBlock* loopHeader;
     if (!f.startLoop(&loopHeader))
         return false;
 
     f.addInterruptCheck();
 
     f.iter().controlItem() = loopHeader;
     return true;
 }
 
 static bool
 EmitIf(FunctionCompiler& f)
 {
-    MDefinition* condition;
+    MDefinition* condition = nullptr;
     if (!f.iter().readIf(&condition))
         return false;
 
     MBasicBlock* elseBlock;
     if (!f.branchAndStartThen(condition, &elseBlock))
         return false;
 
     f.iter().controlItem() = elseBlock;
     return true;
 }
 
 static bool
 EmitElse(FunctionCompiler& f)
 {
-    MBasicBlock *block = f.iter().controlItem();
+    MBasicBlock* block = f.iter().controlItem();
 
     ExprType thenType;
     MDefinition* thenValue;
     if (!f.iter().readElse(&thenType, &thenValue))
         return false;
 
     if (!IsVoid(thenType))
         f.pushDef(thenValue);
@@ -1707,28 +1707,28 @@ EmitElse(FunctionCompiler& f)
         return false;
 
     return true;
 }
 
 static bool
 EmitEnd(FunctionCompiler& f)
 {
-    MBasicBlock *block = f.iter().controlItem();
+    MBasicBlock* block = f.iter().controlItem();
 
     LabelKind kind;
     ExprType type;
     MDefinition* value;
     if (!f.iter().readEnd(&kind, &type, &value))
         return false;
 
     if (!IsVoid(type))
         f.pushDef(value);
 
-    MDefinition* def;
+    MDefinition* def = nullptr;
     switch (kind) {
       case LabelKind::Block:
         if (!f.finishBlock(&def))
             return false;
         break;
       case LabelKind::Loop:
         if (!f.closeLoop(block, &def))
             return false;
@@ -1744,18 +1744,20 @@ EmitEnd(FunctionCompiler& f)
             return false;
         break;
       case LabelKind::Else:
         if (!f.joinIfElse(block, &def))
             return false;
         break;
     }
 
-    if (!IsVoid(type))
+    if (!IsVoid(type)) {
+        MOZ_ASSERT_IF(!f.inDeadCode(), def);
         f.iter().setResult(def);
+    }
 
     return true;
 }
 
 static bool
 EmitBr(FunctionCompiler& f)
 {
     uint32_t relativeDepth;
--- a/js/src/asmjs/WasmTextToBinary.cpp
+++ b/js/src/asmjs/WasmTextToBinary.cpp
@@ -2348,17 +2348,17 @@ ParseBranchTable(WasmParseContext& c, Wa
 
     AstRef target;
     while (c.ts.getIfRef(&target)) {
         if (!table.append(target))
             return nullptr;
     }
 
     if (table.empty()) {
-        c.ts.generateError(brTable, c.error);
+        c.ts.generateError(c.ts.get(), c.error);
         return nullptr;
     }
 
     AstRef def = table.popCopy();
 
     AstExpr* index = ParseExpr(c, inParens);
     if (!index)
         return nullptr;
--- a/js/src/builtin/ModuleObject.cpp
+++ b/js/src/builtin/ModuleObject.cpp
@@ -825,17 +825,17 @@ ModuleObject::state() const
 
 Value
 ModuleObject::hostDefinedField() const
 {
     return getReservedSlot(HostDefinedSlot);
 }
 
 void
-ModuleObject::setHostDefinedField(JS::Value value)
+ModuleObject::setHostDefinedField(const JS::Value& value)
 {
     setReservedSlot(HostDefinedSlot, value);
 }
 
 ModuleEnvironmentObject&
 ModuleObject::initialEnvironment() const
 {
     return getReservedSlot(InitialEnvironmentSlot).toObject().as<ModuleEnvironmentObject>();
--- a/js/src/builtin/ModuleObject.h
+++ b/js/src/builtin/ModuleObject.h
@@ -260,17 +260,17 @@ class ModuleObject : public NativeObject
     ArrayObject& starExportEntries() const;
     IndirectBindingMap& importBindings();
     JSObject* namespaceExports();
     IndirectBindingMap* namespaceBindings();
 
     static bool DeclarationInstantiation(JSContext* cx, HandleModuleObject self);
     static bool Evaluation(JSContext* cx, HandleModuleObject self);
 
-    void setHostDefinedField(JS::Value value);
+    void setHostDefinedField(const JS::Value& value);
 
     // For intrinsic_CreateModuleEnvironment.
     void createEnvironment();
 
     // For BytecodeEmitter.
     bool noteFunctionDeclaration(ExclusiveContext* cx, HandleAtom name, HandleFunction fun);
 
     // For intrinsic_InstantiateModuleFunctionDeclarations.
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -472,17 +472,17 @@ obj_setPrototypeOf(JSContext* cx, unsign
     /* Step 8. */
     args.rval().set(args[0]);
     return true;
 }
 
 #if JS_HAS_OBJ_WATCHPOINT
 
 bool
-js::WatchHandler(JSContext* cx, JSObject* obj_, jsid id_, JS::Value old,
+js::WatchHandler(JSContext* cx, JSObject* obj_, jsid id_, const JS::Value& old,
                  JS::Value* nvp, void* closure)
 {
     RootedObject obj(cx, obj_);
     RootedId id(cx, id_);
 
     /* Avoid recursion on (obj, id) already being watched on cx. */
     AutoResolving resolving(cx, obj, id, AutoResolving::WATCH);
     if (resolving.alreadyStarted())
--- a/js/src/builtin/Object.h
+++ b/js/src/builtin/Object.h
@@ -74,14 +74,14 @@ IdToStringOrSymbol(JSContext* cx, JS::Ha
 
 #if JS_HAS_TOSOURCE
 // Object.prototype.toSource. Function.prototype.toSource and uneval use this.
 JSString*
 ObjectToSource(JSContext* cx, JS::HandleObject obj);
 #endif // JS_HAS_TOSOURCE
 
 extern MOZ_MUST_USE bool
-WatchHandler(JSContext* cx, JSObject* obj, jsid id, JS::Value old,
+WatchHandler(JSContext* cx, JSObject* obj, jsid id, const JS::Value& old,
              JS::Value* nvp, void* closure);
 
 } /* namespace js */
 
 #endif /* builtin_Object_h */
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -1174,17 +1174,17 @@ js::regexp_test_no_statics(JSContext* cx
     size_t ignored = 0;
     RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, 0,
                                            nullptr, &ignored, DontUpdateRegExpStatics);
     args.rval().setBoolean(status == RegExpRunStatus_Success);
     return status != RegExpRunStatus_Error;
 }
 
 static void
-GetParen(JSLinearString* matched, JS::Value capture, JSSubString* out)
+GetParen(JSLinearString* matched, const JS::Value& capture, JSSubString* out)
 {
     if (capture.isUndefined()) {
         out->initEmpty(matched);
         return;
     }
     JSLinearString& captureLinear = capture.toString()->asLinear();
     out->init(&captureLinear, 0, captureLinear.length());
 }
--- a/js/src/builtin/SIMD.cpp
+++ b/js/src/builtin/SIMD.cpp
@@ -955,29 +955,29 @@ ReplaceLane(JSContext* cx, unsigned argc
 {
     typedef typename V::Elem Elem;
 
     CallArgs args = CallArgsFromVp(argc, vp);
     // Only the first and second arguments are mandatory
     if (args.length() < 2 || !IsVectorObject<V>(args[0]))
         return ErrorBadArgs(cx);
 
-    Elem* vec = TypedObjectMemory<Elem*>(args[0]);
-    Elem result[V::lanes];
-
     unsigned lane;
     if (!ArgumentToLaneIndex(cx, args[1], V::lanes, &lane))
         return false;
 
     Elem value;
     if (!V::Cast(cx, args.get(2), &value))
         return false;
 
+    Elem* vec = TypedObjectMemory<Elem*>(args[0]);
+    Elem result[V::lanes];
     for (unsigned i = 0; i < V::lanes; i++)
         result[i] = i == lane ? value : vec[i];
+
     return StoreResult<V>(cx, args, result);
 }
 
 template<typename V>
 static bool
 Swizzle(JSContext* cx, unsigned argc, Value* vp)
 {
     typedef typename V::Elem Elem;
@@ -1034,27 +1034,28 @@ static bool
 BinaryScalar(JSContext* cx, unsigned argc, Value* vp)
 {
     typedef typename V::Elem Elem;
 
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() != 2)
         return ErrorBadArgs(cx);
 
-    Elem result[V::lanes];
     if (!IsVectorObject<V>(args[0]))
         return ErrorBadArgs(cx);
 
-    Elem* val = TypedObjectMemory<Elem*>(args[0]);
     int32_t bits;
     if (!ToInt32(cx, args[1], &bits))
         return false;
 
+    Elem result[V::lanes];
+    Elem* val = TypedObjectMemory<Elem*>(args[0]);
     for (unsigned i = 0; i < V::lanes; i++)
         result[i] = Op<Elem>::apply(val[i], bits);
+
     return StoreResult<V>(cx, args, result);
 }
 
 template<typename In, template<typename C> class Op, typename Out>
 static bool
 CompareFunc(JSContext* cx, unsigned argc, Value* vp)
 {
     typedef typename In::Elem InElem;
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -318,17 +318,17 @@ namespace StructType {
   static bool FieldSetter(JSContext* cx, unsigned argc, Value* vp);
   static bool AddressOfField(JSContext* cx, unsigned argc, Value* vp);
   static bool Define(JSContext* cx, unsigned argc, Value* vp);
 } // namespace StructType
 
 namespace FunctionType {
   static bool Create(JSContext* cx, unsigned argc, Value* vp);
   static bool ConstructData(JSContext* cx, HandleObject typeObj,
-    HandleObject dataObj, HandleObject fnObj, HandleObject thisObj, Value errVal);
+    HandleObject dataObj, HandleObject fnObj, HandleObject thisObj, HandleValue errVal);
 
   static bool Call(JSContext* cx, unsigned argc, Value* vp);
 
   bool IsFunctionType(HandleValue v);
 
   bool ArgTypesGetter(JSContext* cx, const JS::CallArgs& args);
   bool ReturnTypeGetter(JSContext* cx, const JS::CallArgs& args);
   bool ABIGetter(JSContext* cx, const JS::CallArgs& args);
@@ -1455,19 +1455,18 @@ FieldCountMismatch(JSContext* cx,
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_FIELD_MISMATCH,
                              valStr, structStr, expectedCountStr, actualCountStr,
                              posStr);
   return false;
 }
 
 static bool
-FieldDescriptorCountError(JSContext* cx, Value val, size_t length)
-{
-  RootedValue typeVal(cx, val);
+FieldDescriptorCountError(JSContext* cx, HandleValue typeVal, size_t length)
+{
   JSAutoByteString valBytes;
   const char* valStr = CTypesToSourceForError(cx, typeVal, valBytes);
   if (!valStr)
     return false;
 
   char lengthStr[16];
   SprintfLiteral(lengthStr, "%" PRIuSIZE, length);
 
@@ -1506,19 +1505,18 @@ FieldDescriptorSizeError(JSContext* cx, 
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_FIELD_DESC_SIZE, typeStr, propStr);
   return false;
 }
 
 static bool
-FieldDescriptorNameTypeError(JSContext* cx, Value val)
-{
-  RootedValue typeVal(cx, val);
+FieldDescriptorNameTypeError(JSContext* cx, HandleValue typeVal)
+{
   JSAutoByteString valBytes;
   const char* valStr = CTypesToSourceForError(cx, typeVal, valBytes);
   if (!valStr)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_FIELD_DESC_NAMETYPE, valStr);
   return false;
@@ -1614,39 +1612,37 @@ FunctionArgumentLengthMismatch(JSContext
                              CTYPESMSG_ARG_COUNT_MISMATCH,
                              funStr, expectedCountStr, variadicStr,
                              actualCountStr);
   return false;
 }
 
 static bool
 FunctionArgumentTypeError(JSContext* cx,
-                          uint32_t index, Value type, const char* reason)
-{
-  RootedValue val(cx, type);
+                          uint32_t index, HandleValue typeVal, const char* reason)
+{
   JSAutoByteString valBytes;
-  const char* valStr = CTypesToSourceForError(cx, val, valBytes);
+  const char* valStr = CTypesToSourceForError(cx, typeVal, valBytes);
   if (!valStr)
     return false;
 
   char indexStr[16];
   SprintfLiteral(indexStr, "%u", index + 1);
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_ARG_TYPE_ERROR,
                              indexStr, reason, valStr);
   return false;
 }
 
 static bool
-FunctionReturnTypeError(JSContext* cx, Value type, const char* reason)
-{
-  RootedValue val(cx, type);
+FunctionReturnTypeError(JSContext* cx, HandleValue type, const char* reason)
+{
   JSAutoByteString valBytes;
-  const char* valStr = CTypesToSourceForError(cx, val, valBytes);
+  const char* valStr = CTypesToSourceForError(cx, type, valBytes);
   if (!valStr)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_RET_TYPE_ERROR, reason, valStr);
   return false;
 }
 
@@ -2313,22 +2309,26 @@ InitTypeClasses(JSContext* cx, HandleObj
   //   * __proto__ === ctypes.CType.prototype
   //   * A constructor which creates and returns a CData object, containing
   //     binary data of the given type.
   //   * 'prototype' property:
   //     * [[Class]] "CDataProto"
   //     * __proto__ === ctypes.CData.prototype
   //     * 'constructor' property === 't'
 #define DEFINE_TYPE(name, type, ffiType)                                       \
-  RootedObject typeObj_##name(cx,                                              \
-    CType::DefineBuiltin(cx, ctypesObj, #name, CTypeProto, CDataProto, #name,  \
-      TYPE_##name, Int32Value(sizeof(type)),                                   \
-      Int32Value(ffiType.alignment), &ffiType));                               \
-  if (!typeObj_##name)                                                         \
-    return false;
+  RootedObject typeObj_##name(cx);                                             \
+  {                                                                            \
+    RootedValue typeVal(cx, Int32Value(sizeof(type)));                         \
+    RootedValue alignVal(cx, Int32Value(ffiType.alignment));                   \
+    typeObj_##name = CType::DefineBuiltin(cx, ctypesObj, #name, CTypeProto,    \
+                                          CDataProto, #name, TYPE_##name,      \
+                                          typeVal, alignVal, &ffiType);        \
+    if (!typeObj_##name)                                                       \
+      return false;                                                            \
+  }
   CTYPES_FOR_EACH_TYPE(DEFINE_TYPE)
 #undef DEFINE_TYPE
 
   // Alias 'ctypes.unsigned' as 'ctypes.unsigned_int', since they represent
   // the same type in C.
   if (!JS_DefineProperty(cx, ctypesObj, "unsigned", typeObj_unsigned_int,
                          JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
     return false;
@@ -2337,17 +2337,17 @@ InitTypeClasses(JSContext* cx, HandleObj
   // that are still using jschar (bug 1064935).
   if (!JS_DefineProperty(cx, ctypesObj, "jschar", typeObj_char16_t,
                          JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
     return false;
 
   // Create objects representing the special types void_t and voidptr_t.
   RootedObject typeObj(cx,
     CType::DefineBuiltin(cx, ctypesObj, "void_t", CTypeProto, CDataProto, "void",
-                         TYPE_void_t, JS::UndefinedValue(), JS::UndefinedValue(),
+                         TYPE_void_t, JS::UndefinedHandleValue, JS::UndefinedHandleValue,
                          &ffi_type_void));
   if (!typeObj)
     return false;
 
   typeObj = PointerType::CreateInternal(cx, typeObj);
   if (!typeObj)
     return false;
   if (!JS_DefineProperty(cx, ctypesObj, "voidptr_t", typeObj,
@@ -2660,17 +2660,17 @@ template<class Type>
 static MOZ_ALWAYS_INLINE bool IsNegative(Type i)
 {
   return IsNegativeImpl<Type, numeric_limits<Type>::is_signed>::Test(i);
 }
 
 // Implicitly convert val to bool, allowing bool, int, and double
 // arguments numerically equal to 0 or 1.
 static bool
-jsvalToBool(JSContext* cx, Value val, bool* result)
+jsvalToBool(JSContext* cx, HandleValue val, bool* result)
 {
   if (val.isBoolean()) {
     *result = val.toBoolean();
     return true;
   }
   if (val.isInt32()) {
     int32_t i = val.toInt32();
     *result = i != 0;
@@ -2686,17 +2686,17 @@ jsvalToBool(JSContext* cx, Value val, bo
   return false;
 }
 
 // Implicitly convert val to IntegerType, allowing bool, int, double,
 // Int64, UInt64, and CData integer types 't' where all values of 't' are
 // representable by IntegerType.
 template<class IntegerType>
 static bool
-jsvalToInteger(JSContext* cx, Value val, IntegerType* result)
+jsvalToInteger(JSContext* cx, HandleValue val, IntegerType* result)
 {
   JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact);
 
   if (val.isInt32()) {
     // Make sure the integer fits in the alotted precision, and has the right
     // sign.
     int32_t i = val.toInt32();
     return ConvertExact(i, result);
@@ -2776,17 +2776,17 @@ jsvalToInteger(JSContext* cx, Value val,
   return false;
 }
 
 // Implicitly convert val to FloatType, allowing int, double,
 // Int64, UInt64, and CData numeric types 't' where all values of 't' are
 // representable by FloatType.
 template<class FloatType>
 static bool
-jsvalToFloat(JSContext* cx, Value val, FloatType* result)
+jsvalToFloat(JSContext* cx, HandleValue val, FloatType* result)
 {
   JS_STATIC_ASSERT(!numeric_limits<FloatType>::is_exact);
 
   // The following casts may silently throw away some bits, but there's
   // no good way around it. Sternly requiring that the 64-bit double
   // argument be exactly representable as a 32-bit float is
   // unrealistic: it would allow 1/2 to pass but not 1/3.
   if (val.isInt32()) {
@@ -2908,17 +2908,17 @@ StringToInteger(JSContext* cx, JSString*
 }
 
 // Implicitly convert val to IntegerType, allowing int, double,
 // Int64, UInt64, and optionally a decimal or hexadecimal string argument.
 // (This is common code shared by jsvalToSize and the Int64/UInt64 constructors.)
 template<class IntegerType>
 static bool
 jsvalToBigInteger(JSContext* cx,
-                  Value val,
+                  HandleValue val,
                   bool allowString,
                   IntegerType* result,
                   bool* overflow)
 {
   JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact);
 
   if (val.isInt32()) {
     // Make sure the integer fits in the alotted precision, and has the right
@@ -2965,17 +2965,17 @@ jsvalToBigInteger(JSContext* cx,
 
   }
   return false;
 }
 
 // Implicitly convert val to a size value, where the size value is represented
 // by size_t but must also fit in a double.
 static bool
-jsvalToSize(JSContext* cx, Value val, bool allowString, size_t* result)
+jsvalToSize(JSContext* cx, HandleValue val, bool allowString, size_t* result)
 {
   bool dummy;
   if (!jsvalToBigInteger(cx, val, allowString, result, &dummy))
     return false;
 
   // Also check that the result fits in a double.
   return Convert<size_t>(double(*result)) == *result;
 }
@@ -3032,17 +3032,17 @@ SizeTojsval(JSContext* cx, size_t size, 
 
   result.setNumber(double(size));
   return true;
 }
 
 // Forcefully convert val to IntegerType when explicitly requested.
 template<class IntegerType>
 static bool
-jsvalToIntegerExplicit(Value val, IntegerType* result)
+jsvalToIntegerExplicit(HandleValue val, IntegerType* result)
 {
   JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact);
 
   if (val.isDouble()) {
     // Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
     double d = val.toDouble();
     *result = mozilla::IsFinite(d) ? IntegerType(d) : 0;
     return true;
@@ -3061,17 +3061,17 @@ jsvalToIntegerExplicit(Value val, Intege
       return true;
     }
   }
   return false;
 }
 
 // Forcefully convert val to a pointer value when explicitly requested.
 static bool
-jsvalToPtrExplicit(JSContext* cx, Value val, uintptr_t* result)
+jsvalToPtrExplicit(JSContext* cx, HandleValue val, uintptr_t* result)
 {
   if (val.isInt32()) {
     // int32_t always fits in intptr_t. If the integer is negative, cast through
     // an intptr_t intermediate to sign-extend.
     int32_t i = val.toInt32();
     *result = i < 0 ? uintptr_t(intptr_t(i)) : uintptr_t(i);
     return true;
   }
@@ -4394,23 +4394,21 @@ CType::ConstructBasic(JSContext* cx,
 }
 
 JSObject*
 CType::Create(JSContext* cx,
               HandleObject typeProto,
               HandleObject dataProto,
               TypeCode type,
               JSString* name_,
-              Value size_,
-              Value align_,
+              HandleValue size,
+              HandleValue align,
               ffi_type* ffiType)
 {
   RootedString name(cx, name_);
-  RootedValue size(cx, size_);
-  RootedValue align(cx, align_);
 
   // Create a CType object with the properties and slots common to all CTypes.
   // Each type object 't' has:
   //   * [[Class]] "CType"
   //   * __proto__ === 'typeProto'; one of ctypes.{CType,PointerType,ArrayType,
   //     StructType}.prototype
   //   * A constructor which creates and returns a CData object, containing
   //     binary data of the given type.
@@ -4466,24 +4464,22 @@ CType::Create(JSContext* cx,
 JSObject*
 CType::DefineBuiltin(JSContext* cx,
                      HandleObject ctypesObj,
                      const char* propName,
                      JSObject* typeProto_,
                      JSObject* dataProto_,
                      const char* name,
                      TypeCode type,
-                     Value size_,
-                     Value align_,
+                     HandleValue size,
+                     HandleValue align,
                      ffi_type* ffiType)
 {
   RootedObject typeProto(cx, typeProto_);
   RootedObject dataProto(cx, dataProto_);
-  RootedValue size(cx, size_);
-  RootedValue align(cx, align_);
 
   RootedString nameStr(cx, JS_NewStringCopyZ(cx, name));
   if (!nameStr)
     return nullptr;
 
   // Create a new CType object with the common properties and slots.
   RootedObject typeObj(cx, Create(cx, typeProto, dataProto, type, nameStr, size, align, ffiType));
   if (!typeObj)
@@ -5108,20 +5104,21 @@ PointerType::CreateInternal(JSContext* c
   RootedObject dataProto(cx, CType::GetProtoFromType(cx, baseType, slotId));
   if (!dataProto)
     return nullptr;
   RootedObject typeProto(cx, CType::GetProtoFromType(cx, baseType, SLOT_POINTERPROTO));
   if (!typeProto)
     return nullptr;
 
   // Create a new CType object with the common properties and slots.
+  RootedValue sizeVal(cx, Int32Value(sizeof(void*)));
+  RootedValue alignVal(cx, Int32Value(ffi_type_pointer.alignment));
   JSObject* typeObj = CType::Create(cx, typeProto, dataProto, TYPE_pointer,
-                        nullptr, Int32Value(sizeof(void*)),
-                        Int32Value(ffi_type_pointer.alignment),
-                        &ffi_type_pointer);
+                                    nullptr, sizeVal, alignVal,
+                                    &ffi_type_pointer);
   if (!typeObj)
     return nullptr;
 
   // Set the target type. (This will be 'null' for an opaque pointer type.)
   JS_SetReservedSlot(typeObj, SLOT_TARGET_T, ObjectValue(*baseType));
 
   // Finally, cache our newly-created PointerType on our pointed-to CType.
   JS_SetReservedSlot(baseType, SLOT_PTR, ObjectValue(*typeObj));
@@ -5196,17 +5193,17 @@ PointerType::ConstructData(JSContext* cx
     } else if (!JS_ValueToObject(cx, args[1], &thisObj)) {
       return false;
     }
   }
 
   // The third argument is an optional error sentinel that js-ctypes will return
   // if an exception is raised while executing the closure. The type must match
   // the return type of the callback.
-  Value errVal = JS::UndefinedValue();
+  RootedValue errVal(cx);
   if (args.length() == 3)
     errVal = args[2];
 
   RootedObject fnObj(cx, &args[0].toObject());
   return FunctionType::ConstructData(cx, baseObj, result, fnObj, thisObj, errVal);
 }
 
 JSObject*
@@ -5420,18 +5417,18 @@ ArrayType::CreateInternal(JSContext* cx,
   // The size of the base type must be defined.
   // If our length is undefined, both our size and length will be undefined.
   size_t baseSize;
   if (!CType::GetSafeSize(baseType, &baseSize)) {
     JS_ReportErrorASCII(cx, "base size must be defined");
     return nullptr;
   }
 
-  RootedValue sizeVal(cx, JS::UndefinedValue());
-  RootedValue lengthVal(cx, JS::UndefinedValue());
+  RootedValue sizeVal(cx);
+  RootedValue lengthVal(cx);
   if (lengthDefined) {
     // Check for overflow, and convert to an int or double as required.
     size_t size = length * baseSize;
     if (length > 0 && size / length != baseSize) {
       SizeOverflow(cx, "array size", "size_t");
       return nullptr;
     }
     if (!SizeTojsval(cx, size, &sizeVal)) {
@@ -5439,21 +5436,21 @@ ArrayType::CreateInternal(JSContext* cx,
       return nullptr;
     }
     if (!SizeTojsval(cx, length, &lengthVal)) {
       SizeOverflow(cx, "array length", "JavaScript number");
       return nullptr;
     }
   }
 
-  size_t align = CType::GetAlignment(baseType);
+  RootedValue alignVal(cx, Int32Value(CType::GetAlignment(baseType)));
 
   // Create a new CType object with the common properties and slots.
   JSObject* typeObj = CType::Create(cx, typeProto, dataProto, TYPE_array, nullptr,
-                        sizeVal, Int32Value(align), nullptr);
+                                    sizeVal, alignVal, nullptr);
   if (!typeObj)
     return nullptr;
 
   // Set the element type.
   JS_SetReservedSlot(typeObj, SLOT_ELEMENT_T, ObjectValue(*baseType));
 
   // Set the length.
   JS_SetReservedSlot(typeObj, SLOT_LENGTH, lengthVal);
@@ -5947,18 +5944,19 @@ StructType::Create(JSContext* cx, unsign
 
   // Get ctypes.StructType.prototype from the ctypes.StructType constructor.
   RootedObject typeProto(cx, CType::GetProtoFromCtor(&args.callee(), SLOT_STRUCTPROTO));
 
   // Create a simple StructType with no defined fields. The result will be
   // non-instantiable as CData, will have no 'prototype' property, and will
   // have undefined size and alignment and no ffi_type.
   RootedObject result(cx, CType::Create(cx, typeProto, nullptr, TYPE_struct,
-                                        name.toString(), JS::UndefinedValue(),
-                                        JS::UndefinedValue(), nullptr));
+                                        name.toString(),
+                                        JS::UndefinedHandleValue,
+                                        JS::UndefinedHandleValue, nullptr));
   if (!result)
     return false;
 
   if (args.length() == 2) {
     RootedObject arr(cx, args[1].isObject() ? &args[1].toObject() : nullptr);
     bool isArray;
     if (!arr) {
         isArray = false;
@@ -6567,17 +6565,17 @@ struct AutoValue
       memset(mData, 0, size);
     return mData != nullptr;
   }
 
   void* mData;
 };
 
 static bool
-GetABI(JSContext* cx, Value abiType, ffi_abi* result)
+GetABI(JSContext* cx, HandleValue abiType, ffi_abi* result)
 {
   if (abiType.isPrimitive())
     return false;
 
   ABICode abi = GetABICode(abiType.toObjectOrNull());
 
   // determine the ABI from the subset of those available on the
   // given platform. ABI_DEFAULT specifies the default
@@ -6645,17 +6643,17 @@ PrepareType(JSContext* cx, uint32_t inde
 
   // libffi cannot pass types of zero size by value.
   MOZ_ASSERT(CType::GetSize(result) != 0);
 
   return result;
 }
 
 static JSObject*
-PrepareReturnType(JSContext* cx, Value type)
+PrepareReturnType(JSContext* cx, HandleValue type)
 {
   if (type.isPrimitive() || !CType::IsCType(type.toObjectOrNull())) {
     FunctionReturnTypeError(cx, type, "is not a ctypes type");
     return nullptr;
   }
 
   JSObject* result = type.toObjectOrNull();
   TypeCode typeCode = CType::GetTypeCode(result);
@@ -6673,17 +6671,17 @@ PrepareReturnType(JSContext* cx, Value t
 
   // libffi cannot pass types of zero size by value.
   MOZ_ASSERT(typeCode == TYPE_void_t || CType::GetSize(result) != 0);
 
   return result;
 }
 
 static MOZ_ALWAYS_INLINE bool
-IsEllipsis(JSContext* cx, Value v, bool* isEllipsis)
+IsEllipsis(JSContext* cx, HandleValue v, bool* isEllipsis)
 {
   *isEllipsis = false;
   if (!v.isString())
     return true;
   JSString* str = v.toString();
   if (str->length() != 3)
     return true;
   JSLinearString* linear = str->ensureLinear(cx);
@@ -6696,17 +6694,18 @@ IsEllipsis(JSContext* cx, Value v, bool*
   return true;
 }
 
 static bool
 PrepareCIF(JSContext* cx,
            FunctionInfo* fninfo)
 {
   ffi_abi abi;
-  if (!GetABI(cx, ObjectOrNullValue(fninfo->mABI), &abi)) {
+  RootedValue abiType(cx, ObjectOrNullValue(fninfo->mABI));
+  if (!GetABI(cx, abiType, &abi)) {
     JS_ReportErrorASCII(cx, "Invalid ABI specification");
     return false;
   }
 
   ffi_type* rtype = CType::GetFFIType(cx, fninfo->mReturnType);
   if (!rtype)
     return false;
 
@@ -6927,18 +6926,18 @@ FunctionType::CreateInternal(JSContext* 
   if (!typeProto)
     return nullptr;
   RootedObject dataProto(cx, CType::GetProtoFromType(cx, returnType, SLOT_FUNCTIONDATAPROTO));
   if (!dataProto)
     return nullptr;
 
   // Create a new CType object with the common properties and slots.
   RootedObject typeObj(cx, CType::Create(cx, typeProto, dataProto, TYPE_function,
-                                         nullptr, JS::UndefinedValue(),
-                                         JS::UndefinedValue(), nullptr));
+                                         nullptr, JS::UndefinedHandleValue,
+                                         JS::UndefinedHandleValue, nullptr));
   if (!typeObj)
     return nullptr;
 
   // Determine and check the types, and prepare the function CIF.
   if (!CreateFunctionInfo(cx, typeObj, abi, returnType, args))
       return nullptr;
 
   return typeObj;
@@ -6948,17 +6947,17 @@ FunctionType::CreateInternal(JSContext* 
 // Regular function pointers are constructed directly in
 // PointerType::ConstructData().
 bool
 FunctionType::ConstructData(JSContext* cx,
                             HandleObject typeObj,
                             HandleObject dataObj,
                             HandleObject fnObj,
                             HandleObject thisObj,
-                            Value errVal)
+                            HandleValue errVal)
 {
   MOZ_ASSERT(CType::GetTypeCode(typeObj) == TYPE_function);
 
   PRFuncPtr* data = static_cast<PRFuncPtr*>(CData::GetData(dataObj));
 
   FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj);
   if (fninfo->mIsVariadic) {
     JS_ReportErrorASCII(cx, "Can't declare a variadic callback function");
@@ -7279,20 +7278,19 @@ FunctionType::IsVariadicGetter(JSContext
 ** CClosure implementation
 *******************************************************************************/
 
 JSObject*
 CClosure::Create(JSContext* cx,
                  HandleObject typeObj,
                  HandleObject fnObj,
                  HandleObject thisObj,
-                 Value errVal_,
+                 HandleValue errVal,
                  PRFuncPtr* fnptr)
 {
-  RootedValue errVal(cx, errVal_);
   MOZ_ASSERT(fnObj);
 
   RootedObject result(cx, JS_NewObject(cx, &sCClosureClass));
   if (!result)
     return nullptr;
 
   // Get the FunctionInfo from the FunctionType.
   FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj);
@@ -8304,18 +8302,19 @@ CDataFinalizer::Construct(JSContext* cx,
                      SLOT_DATAFINALIZER_VALTYPE,
                      ObjectOrNullValue(objBestArgType));
 
   // Used by ToSource
   JS_SetReservedSlot(objResult,
                      SLOT_DATAFINALIZER_CODETYPE,
                      ObjectValue(*objCodePtrType));
 
+  RootedValue abiType(cx, ObjectOrNullValue(funInfoFinalizer->mABI));
   ffi_abi abi;
-  if (!GetABI(cx, ObjectOrNullValue(funInfoFinalizer->mABI), &abi)) {
+  if (!GetABI(cx, abiType, &abi)) {
     JS_ReportErrorASCII(cx, "Internal Error: "
                         "Invalid ABI specification in CDataFinalizer");
     return false;
   }
 
   ffi_type* rtype = CType::GetFFIType(cx, funInfoFinalizer->mReturnType);
   if (!rtype) {
     JS_ReportErrorASCII(cx, "Internal Error: "
@@ -8474,17 +8473,17 @@ CDataFinalizer::Methods::Dispose(JSConte
   MOZ_ASSERT(valCodePtrType.isObject());
   JSObject* objCodePtrType = &valCodePtrType.toObject();
 
   JSObject* objCodeType = PointerType::GetBaseType(objCodePtrType);
   MOZ_ASSERT(objCodeType);
   MOZ_ASSERT(CType::GetTypeCode(objCodeType) == TYPE_function);
 
   RootedObject resultType(cx, FunctionType::GetFunctionInfo(objCodeType)->mReturnType);
-  RootedValue result(cx, JS::UndefinedValue());
+  RootedValue result(cx);
 
   int errnoStatus;
 #if defined(XP_WIN)
   int32_t lastErrorStatus;
   CDataFinalizer::CallFinalizer(p, &errnoStatus, &lastErrorStatus);
 #else
   CDataFinalizer::CallFinalizer(p, &errnoStatus, nullptr);
 #endif // defined(XP_WIN)
--- a/js/src/ctypes/CTypes.h
+++ b/js/src/ctypes/CTypes.h
@@ -439,21 +439,22 @@ enum Int64FunctionSlot {
 };
 
 /*******************************************************************************
 ** Object API definitions
 *******************************************************************************/
 
 namespace CType {
   JSObject* Create(JSContext* cx, HandleObject typeProto, HandleObject dataProto,
-    TypeCode type, JSString* name, Value size, Value align, ffi_type* ffiType);
+    TypeCode type, JSString* name, HandleValue size, HandleValue align,
+                   ffi_type* ffiType);
 
   JSObject* DefineBuiltin(JSContext* cx, HandleObject ctypesObj, const char* propName,
     JSObject* typeProto, JSObject* dataProto, const char* name, TypeCode type,
-    Value size, Value align, ffi_type* ffiType);
+    HandleValue size, HandleValue align, ffi_type* ffiType);
 
   bool IsCType(JSObject* obj);
   bool IsCTypeProto(JSObject* obj);
   TypeCode GetTypeCode(JSObject* typeObj);
   bool TypesEqual(JSObject* t1, JSObject* t2);
   size_t GetSize(JSObject* obj);
   MOZ_MUST_USE bool GetSafeSize(JSObject* obj, size_t* result);
   bool IsSizeDefined(JSObject* obj);
@@ -501,17 +502,17 @@ namespace FunctionType {
 
   FunctionInfo* GetFunctionInfo(JSObject* obj);
   void BuildSymbolName(JSString* name, JSObject* typeObj,
     AutoCString& result);
 } // namespace FunctionType
 
 namespace CClosure {
   JSObject* Create(JSContext* cx, HandleObject typeObj, HandleObject fnObj,
-    HandleObject thisObj, Value errVal, PRFuncPtr* fnptr);
+    HandleObject thisObj, HandleValue errVal, PRFuncPtr* fnptr);
 } // namespace CClosure
 
 namespace CData {
   JSObject* Create(JSContext* cx, HandleObject typeObj, HandleObject refObj,
     void* data, bool ownResult);
 
   JSObject* GetCType(JSObject* dataObj);
   void* GetData(JSObject* dataObj);
--- a/js/src/ctypes/Library.cpp
+++ b/js/src/ctypes/Library.cpp
@@ -81,19 +81,18 @@ Library::Name(JSContext* cx, unsigned ar
   if (!result)
     return false;
 
   args.rval().setString(result);
   return true;
 }
 
 JSObject*
-Library::Create(JSContext* cx, Value path_, const JSCTypesCallbacks* callbacks)
+Library::Create(JSContext* cx, HandleValue path, const JSCTypesCallbacks* callbacks)
 {
-  RootedValue path(cx, path_);
   RootedObject libraryObj(cx, JS_NewObject(cx, &sLibraryClass));
   if (!libraryObj)
     return nullptr;
 
   // initialize the library
   JS_SetReservedSlot(libraryObj, SLOT_LIBRARY, PrivateValue(nullptr));
 
   // attach API functions
--- a/js/src/ctypes/Library.h
+++ b/js/src/ctypes/Library.h
@@ -20,17 +20,17 @@ enum LibrarySlot {
   SLOT_LIBRARY = 0,
   LIBRARY_SLOTS
 };
 
 namespace Library
 {
   MOZ_MUST_USE bool Name(JSContext* cx, unsigned argc, JS::Value* vp);
 
-  JSObject* Create(JSContext* cx, JS::Value path, const JSCTypesCallbacks* callbacks);
+  JSObject* Create(JSContext* cx, JS::HandleValue path, const JSCTypesCallbacks* callbacks);
 
   bool IsLibrary(JSObject* obj);
   PRLibrary* GetLibrary(JSObject* obj);
 
   MOZ_MUST_USE bool Open(JSContext* cx, unsigned argc, JS::Value* vp);
 } // namespace Library
 
 } // namespace ctypes
--- a/js/src/ds/IdValuePair.h
+++ b/js/src/ds/IdValuePair.h
@@ -22,17 +22,17 @@ struct IdValuePair
     jsid id;
 
     IdValuePair()
       : value(UndefinedValue()), id(JSID_EMPTY)
     {}
     explicit IdValuePair(jsid idArg)
       : value(UndefinedValue()), id(idArg)
     {}
-    IdValuePair(jsid idArg, Value valueArg)
+    IdValuePair(jsid idArg, const Value& valueArg)
       : value(valueArg), id(idArg)
     {}
 
     void trace(JSTracer* trc) {
         TraceRoot(trc, &value, "IdValuePair::value");
         TraceRoot(trc, &id, "IdValuePair::id");
     }
 };
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -28,17 +28,17 @@ class ParseNode;
 template <typename ParseHandler> class Parser;
 class SharedContext;
 class TokenStream;
 
 class CGConstList {
     Vector<Value> list;
   public:
     explicit CGConstList(ExclusiveContext* cx) : list(cx) {}
-    MOZ_MUST_USE bool append(Value v) {
+    MOZ_MUST_USE bool append(const Value& v) {
         MOZ_ASSERT_IF(v.isString(), v.toString()->isAtom());
         return list.append(v);
     }
     size_t length() const { return list.length(); }
     void finish(ConstArray* array);
 };
 
 struct CGObjectList {
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -5541,43 +5541,42 @@ Parser<ParseHandler>::continueStatement(
     // Labeled 'continue' statements target the nearest labeled loop
     // statements with the same label. Unlabeled 'continue' statements target
     // the innermost loop statement.
     auto isLoop = [](ParseContext::Statement* stmt) {
         return StatementKindIsLoop(stmt->kind());
     };
 
     if (label) {
-        bool foundTarget = false;
         ParseContext::Statement* stmt = pc->innermostStatement();
+        bool foundLoop = false;
 
         for (;;) {
             stmt = ParseContext::Statement::findNearest(stmt, isLoop);
             if (!stmt) {
-                report(ParseError, false, null(), JSMSG_BAD_CONTINUE);
+                report(ParseError, false, null(),
+                       foundLoop ? JSMSG_LABEL_NOT_FOUND : JSMSG_BAD_CONTINUE);
                 return null();
             }
 
+            foundLoop = true;
+
             // Is it labeled by our label?
+            bool foundTarget = false;
             stmt = stmt->enclosing();
             while (stmt && stmt->is<ParseContext::LabelStatement>()) {
                 if (stmt->as<ParseContext::LabelStatement>().label() == label) {
                     foundTarget = true;
                     break;
                 }
                 stmt = stmt->enclosing();
             }
             if (foundTarget)
                 break;
         }
-
-        if (!foundTarget) {
-            report(ParseError, false, null(), JSMSG_LABEL_NOT_FOUND);
-            return null();
-        }
     } else if (!pc->findInnermostStatement(isLoop)) {
         report(ParseError, false, null(), JSMSG_BAD_CONTINUE);
         return null();
     }
 
     if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream))
         return null();
 
--- a/js/src/gc/Allocator.cpp
+++ b/js/src/gc/Allocator.cpp
@@ -305,17 +305,17 @@ GCRuntime::refillFreeListOffMainThread(E
     JSRuntime* rt = zone->runtimeFromAnyThread();
 
     AutoMaybeStartBackgroundAllocation maybeStartBGAlloc;
 
     // If we're off the main thread, we try to allocate once and return
     // whatever value we get. We need to first ensure the main thread is not in
     // a GC session.
     AutoLockHelperThreadState lock;
-    while (rt->isHeapBusy()) {
+    while (rt->isHeapCollecting()) {
         HelperThreadState().wait(lock, GlobalHelperThreadState::PRODUCER);
         HelperThreadState().notifyOne(GlobalHelperThreadState::PRODUCER, lock);
     }
 
     return arenas->allocateFromArena(zone, thingKind, maybeStartBGAlloc);
 }
 
 /* static */ TenuredCell*
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -274,20 +274,20 @@ template <typename S> struct PreBarrierF
 
 template <typename S> struct ReadBarrierFunctor : public VoidDefaultAdaptor<S> {
     template <typename T> void operator()(T* t);
 };
 
 template <>
 struct InternalBarrierMethods<Value>
 {
-    static bool isMarkable(Value v) { return v.isMarkable(); }
-    static bool isMarkableTaggedPointer(Value v) { return isMarkable(v); }
+    static bool isMarkable(const Value& v) { return v.isMarkable(); }
+    static bool isMarkableTaggedPointer(const Value& v) { return isMarkable(v); }
 
-    static void preBarrier(Value v) {
+    static void preBarrier(const Value& v) {
         DispatchTyped(PreBarrierFunctor<Value>(), v);
     }
 
     static void postBarrier(Value* vp, const Value& prev, const Value& next) {
         MOZ_ASSERT(!CurrentThreadIsIonCompiling());
         MOZ_ASSERT(vp);
 
         // If the target needs an entry, add it.
@@ -329,17 +329,17 @@ template <typename T>
 class BarrieredBaseMixins {};
 
 // Base class of all barrier types.
 template <typename T>
 class BarrieredBase : public BarrieredBaseMixins<T>
 {
   protected:
     // BarrieredBase is not directly instantiable.
-    explicit BarrieredBase(T v) : value(v) {}
+    explicit BarrieredBase(const T& v) : value(v) {}
 
     // Storage for all barrier classes. |value| must be a GC thing reference
     // type: either a direct pointer to a GC thing or a supported tagged
     // pointer that can reference GC things, such as JS::Value or jsid. Nested
     // barrier types are NOT supported. See assertTypeConstraints.
     T value;
 
   public:
@@ -350,35 +350,37 @@ class BarrieredBase : public BarrieredBa
 };
 
 // Base class for barriered pointer types that intercept only writes.
 template <class T>
 class WriteBarrieredBase : public BarrieredBase<T>
 {
   protected:
     // WriteBarrieredBase is not directly instantiable.
-    explicit WriteBarrieredBase(T v) : BarrieredBase<T>(v) {}
+    explicit WriteBarrieredBase(const T& v) : BarrieredBase<T>(v) {}
 
   public:
     DECLARE_POINTER_COMPARISON_OPS(T);
     DECLARE_POINTER_CONSTREF_OPS(T);
 
     // Use this if the automatic coercion to T isn't working.
     const T& get() const { return this->value; }
 
     // Use this if you want to change the value without invoking barriers.
     // Obviously this is dangerous unless you know the barrier is not needed.
-    void unsafeSet(T v) { this->value = v; }
+    void unsafeSet(const T& v) { this->value = v; }
 
     // For users who need to manually barrier the raw types.
     static void writeBarrierPre(const T& v) { InternalBarrierMethods<T>::preBarrier(v); }
 
   protected:
     void pre() { InternalBarrierMethods<T>::preBarrier(this->value); }
-    void post(T prev, T next) { InternalBarrierMethods<T>::postBarrier(&this->value, prev, next); }
+    void post(const T& prev, const T& next) {
+        InternalBarrierMethods<T>::postBarrier(&this->value, prev, next);
+    }
 };
 
 /*
  * PreBarriered only automatically handles pre-barriers. Post-barriers must be
  * manually implemented when using this class. GCPtr and HeapPtr should be used
  * in all cases that do not require explicit low-level control of moving
  * behavior, e.g. for HashMap keys.
  */
@@ -386,21 +388,21 @@ template <class T>
 class PreBarriered : public WriteBarrieredBase<T>
 {
   public:
     PreBarriered() : WriteBarrieredBase<T>(JS::GCPolicy<T>::initial()) {}
     /*
      * Allow implicit construction for use in generic contexts, such as
      * DebuggerWeakMap::markKeys.
      */
-    MOZ_IMPLICIT PreBarriered(T v) : WriteBarrieredBase<T>(v) {}
+    MOZ_IMPLICIT PreBarriered(const T& v) : WriteBarrieredBase<T>(v) {}
     explicit PreBarriered(const PreBarriered<T>& v) : WriteBarrieredBase<T>(v.value) {}
     ~PreBarriered() { this->pre(); }
 
-    void init(T v) {
+    void init(const T& v) {
         this->value = v;
     }
 
     /* Use to set the pointer to nullptr. */
     void clear() {
         this->pre();
         this->value = nullptr;
     }
@@ -425,33 +427,33 @@ class PreBarriered : public WriteBarrier
  * implemented by js::HeapPtr<T> or JS::Heap<T> at the cost of not
  * automatically handling deletion or movement.
  */
 template <class T>
 class GCPtr : public WriteBarrieredBase<T>
 {
   public:
     GCPtr() : WriteBarrieredBase<T>(JS::GCPolicy<T>::initial()) {}
-    explicit GCPtr(T v) : WriteBarrieredBase<T>(v) {
+    explicit GCPtr(const T& v) : WriteBarrieredBase<T>(v) {
         this->post(JS::GCPolicy<T>::initial(), v);
     }
     explicit GCPtr(const GCPtr<T>& v) : WriteBarrieredBase<T>(v) {
         this->post(JS::GCPolicy<T>::initial(), v);
     }
 #ifdef DEBUG
     ~GCPtr() {
         // No prebarrier necessary as this only happens when we are sweeping or
         // after we have just collected the nursery.  Note that the wrapped
         // pointer may already have been freed by this point.
         MOZ_ASSERT(CurrentThreadIsGCSweeping());
         Poison(this, JS_FREED_HEAP_PTR_PATTERN, sizeof(*this));
     }
 #endif
 
-    void init(T v) {
+    void init(const T& v) {
         this->value = v;
         this->post(JS::GCPolicy<T>::initial(), v);
     }
 
     DECLARE_POINTER_ASSIGN_OPS(GCPtr, T);
 
     T unbarrieredGet() const {
         return this->value;
@@ -518,17 +520,17 @@ class HeapPtr : public WriteBarrieredBas
         this->post(JS::GCPolicy<T>::initial(), this->value);
     }
 
     ~HeapPtr() {
         this->pre();
         this->post(this->value, JS::GCPolicy<T>::initial());
     }
 
-    void init(T v) {
+    void init(const T& v) {
         this->value = v;
         this->post(JS::GCPolicy<T>::initial(), this->value);
     }
 
     DECLARE_POINTER_ASSIGN_OPS(HeapPtr, T);
 
     /* Make this friend so it can access pre() and post(). */
     template <class T1, class T2>
@@ -551,21 +553,23 @@ class HeapPtr : public WriteBarrieredBas
 };
 
 // Base class for barriered pointer types that intercept reads and writes.
 template <typename T>
 class ReadBarrieredBase : public BarrieredBase<T>
 {
   protected:
     // ReadBarrieredBase is not directly instantiable.
-    explicit ReadBarrieredBase(T v) : BarrieredBase<T>(v) {}
+    explicit ReadBarrieredBase(const T& v) : BarrieredBase<T>(v) {}
 
   protected:
     void read() const { InternalBarrierMethods<T>::readBarrier(this->value); }
-    void post(T prev, T next) { InternalBarrierMethods<T>::postBarrier(&this->value, prev, next); }
+    void post(const T& prev, const T& next) {
+        InternalBarrierMethods<T>::postBarrier(&this->value, prev, next);
+    }
 };
 
 // Incremental GC requires that weak pointers have read barriers. See the block
 // comment at the top of Barrier.h for a complete discussion of why.
 //
 // Note that this class also has post-barriers, so is safe to use with nursery
 // pointers. However, when used as a hashtable key, care must still be taken to
 // insert manual post-barriers on the table for rekeying if the key is based in
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -310,17 +310,17 @@ ShouldMarkCrossCompartment(JSTracer* trc
                 DelayCrossCompartmentGrayMarking(src);
             return false;
         }
         return zone->isGCMarkingGray();
     }
 }
 
 static bool
-ShouldMarkCrossCompartment(JSTracer* trc, JSObject* src, Value val)
+ShouldMarkCrossCompartment(JSTracer* trc, JSObject* src, const Value& val)
 {
     return val.isMarkable() && ShouldMarkCrossCompartment(trc, src, (Cell*)val.toGCThing());
 }
 
 static void
 AssertZoneIsMarking(Cell* thing)
 {
     MOZ_ASSERT(TenuredCell::fromPointer(thing)->zone()->isGCMarking());
@@ -395,17 +395,17 @@ typename PtrBaseGCType<T>::type*
 ConvertToBase(T* thingp)
 {
     return reinterpret_cast<typename PtrBaseGCType<T>::type*>(thingp);
 }
 
 template <typename T> void DispatchToTracer(JSTracer* trc, T* thingp, const char* name);
 template <typename T> T DoCallback(JS::CallbackTracer* trc, T* thingp, const char* name);
 template <typename T> void DoMarking(GCMarker* gcmarker, T* thing);
-template <typename T> void DoMarking(GCMarker* gcmarker, T thing);
+template <typename T> void DoMarking(GCMarker* gcmarker, const T& thing);
 template <typename T> void NoteWeakEdge(GCMarker* gcmarker, T** thingp);
 template <typename T> void NoteWeakEdge(GCMarker* gcmarker, T* thingp);
 
 template <typename T>
 void
 js::TraceEdge(JSTracer* trc, WriteBarrieredBase<T>* thingp, const char* name)
 {
     DispatchToTracer(trc, ConvertToBase(thingp->unsafeUnbarrieredForTracing()), name);
@@ -426,17 +426,17 @@ js::TraceNullableEdge(JSTracer* trc, Wri
         DispatchToTracer(trc, ConvertToBase(thingp->unsafeUnbarrieredForTracing()), name);
 }
 
 template <typename T>
 JS_PUBLIC_API(void)
 JS::TraceEdge(JSTracer* trc, JS::Heap<T>* thingp, const char* name)
 {
     MOZ_ASSERT(thingp);
-    if (InternalBarrierMethods<T>::isMarkable(thingp->get()))
+    if (InternalBarrierMethods<T>::isMarkable(*thingp->unsafeGet()))
         DispatchToTracer(trc, ConvertToBase(thingp->unsafeGet()), name);
 }
 
 JS_PUBLIC_API(void)
 JS::TraceEdge(JSTracer* trc, JS::TenuredHeap<JSObject*>* thingp, const char* name)
 {
     MOZ_ASSERT(thingp);
     if (JSObject* ptr = thingp->getPtr()) {
@@ -802,17 +802,17 @@ DoMarking(GCMarker* gcmarker, T* thing)
 
 template <typename S>
 struct DoMarkingFunctor : public VoidDefaultAdaptor<S> {
     template <typename T> void operator()(T* t, GCMarker* gcmarker) { DoMarking(gcmarker, t); }
 };
 
 template <typename T>
 void
-DoMarking(GCMarker* gcmarker, T thing)
+DoMarking(GCMarker* gcmarker, const T& thing)
 {
     DispatchTyped(DoMarkingFunctor<T>(), thing, gcmarker);
 }
 
 template <typename T>
 void
 NoteWeakEdge(GCMarker* gcmarker, T** thingp)
 {
@@ -951,17 +951,17 @@ js::GCMarker::traverseEdge(S source, T* 
 template <typename V, typename S> struct TraverseEdgeFunctor : public VoidDefaultAdaptor<V> {
     template <typename T> void operator()(T t, GCMarker* gcmarker, S s) {
         return gcmarker->traverseEdge(s, t);
     }
 };
 
 template <typename S, typename T>
 void
-js::GCMarker::traverseEdge(S source, T thing)
+js::GCMarker::traverseEdge(S source, const T& thing)
 {
     DispatchTyped(TraverseEdgeFunctor<T, S>(), thing, this, source);
 }
 
 template <typename T>
 bool
 js::GCMarker::mark(T* thing)
 {
@@ -2944,16 +2944,17 @@ UnmarkGrayTracer::onChild(const JS::GCCe
 template <typename T>
 static bool
 TypedUnmarkGrayCellRecursively(T* t)
 {
     MOZ_ASSERT(t);
 
     JSRuntime* rt = t->runtimeFromMainThread();
     MOZ_ASSERT(!rt->isHeapCollecting());
+    MOZ_ASSERT(!rt->isCycleCollecting());
 
     bool unmarkedArg = false;
     if (t->isTenured()) {
         if (!t->asTenured().isMarked(GRAY))
             return false;
 
         t->asTenured().unmark(GRAY);
         unmarkedArg = true;
--- a/js/src/gc/Marking.h
+++ b/js/src/gc/Marking.h
@@ -177,17 +177,17 @@ class GCMarker : public JSTracer
     void stop();
     void reset();
 
     // Mark the given GC thing and traverse its children at some point.
     template <typename T> void traverse(T thing);
 
     // Calls traverse on target after making additional assertions.
     template <typename S, typename T> void traverseEdge(S source, T* target);
-    template <typename S, typename T> void traverseEdge(S source, T target);
+    template <typename S, typename T> void traverseEdge(S source, const T& target);
 
     // Notes a weak graph edge for later sweeping.
     template <typename T> void noteWeakEdge(T* edge);
 
     /*
      * Care must be taken changing the mark color from gray to black. The cycle
      * collector depends on the invariant that there are no black to gray edges
      * in the GC heap. This invariant lets the CC not trace through black
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/SIMD/bug1296640-gc-args.js
@@ -0,0 +1,9 @@
+if (typeof gczeal === 'undefined' || typeof SIMD === 'undefined') {
+    quit();
+}
+
+gczeal(9, 2);
+var Int8x16 = SIMD.Int8x16;
+var v = Int8x16();
+var good = { valueOf: () => 21 };
+Int8x16.shiftLeftByScalar(v, good);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/SIMD/bug1303780-gc-args.js
@@ -0,0 +1,12 @@
+if (typeof gczeal === 'undefined' || typeof SIMD === 'undefined') {
+    quit();
+}
+
+gczeal(14,2);
+var Float32x4 = SIMD.Float32x4;
+function test() {
+    var v = Float32x4(1,2,3,4);
+    var good = {valueOf: () => 42};
+    Float32x4.replaceLane(v, 0, good);
+}
+test();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/asm.js/bug1306506.js
@@ -0,0 +1,7 @@
+f = (function(stdlib, foreign, heap) {
+    "use asm";
+    function f() {
+        return (1, 1 / 0)|0;
+    }
+    return f;
+});
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/parser/break-continue-errors.js
@@ -0,0 +1,22 @@
+load(libdir + "asserts.js");
+
+function test(s, expected) {
+    assertErrorMessage(() => Function(s), SyntaxError, expected);
+}
+
+test("A: continue;", "continue must be inside loop");
+test("A: continue B;", "continue must be inside loop");
+test("A: if (false) { continue; }", "continue must be inside loop");
+test("A: if (false) { continue B; }", "continue must be inside loop");
+test("while (false) { (() => { continue B; })(); }", "continue must be inside loop");
+test("B: while (false) { (() => { continue B; })(); }", "continue must be inside loop");
+
+test("do { continue B; } while (false);", "label not found");
+test("A: for (;;) { continue B; }", "label not found");
+test("A: while (false) { if (x) { continue B; } }", "label not found");
+test("A: while (false) { B: if (x) { continue B; } }", "label not found");
+
+test("A: if (false) { break B; }", "label not found");
+test("A: while (false) { break B; }", "label not found");
+test("A: while (true) { if (x) { break B; } }", "label not found");
+test("B: while (false) { (() => { break B; })(); }", "label not found");
--- a/js/src/jit-test/tests/wasm/regress/misc-control-flow.js
+++ b/js/src/jit-test/tests/wasm/regress/misc-control-flow.js
@@ -190,8 +190,38 @@ wasmEvalText(`
     ))
     (i32.const 0)
    )
    (i32.const 13)
   )
  )
 )
 `);
+
+wasmFailValidateText(`
+(module
+    (func (result i32)
+      (loop
+        (i32.const 0)
+        (br_table 1 0 (i32.const 15))
+      )
+    )
+)`, mismatchError("i32", "void"));
+
+wasmFailValidateText(`
+(module
+  (func (result i32)
+    (loop i32
+      (i32.const 0)
+      (br_table 1 0 (i32.const 15))
+    )
+  )
+)`, mismatchError("i32", "void"));
+
+wasmValidateText(`
+(module
+    (func
+        (loop
+          (i32.const 0)
+          (br_table 1 0 (i32.const 15))
+        )
+    )
+)`);
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -227,17 +227,17 @@ struct BaselineStackBuilder
                 JitSpew(JitSpew_BaselineBailouts,
                         "      WRITE_WRD %p/%p %-15s %016llx",
                         header_->copyStackBottom, virtualPointerAtStackOffset(0), info, w);
             }
         }
         return true;
     }
 
-    bool writeValue(Value val, const char* info) {
+    bool writeValue(const Value& val, const char* info) {
         if (!write<Value>(val))
             return false;
         if (info) {
             JitSpew(JitSpew_BaselineBailouts,
                     "      WRITE_VAL %p/%p %-15s %016llx",
                     header_->copyStackBottom, virtualPointerAtStackOffset(0), info,
                     *((uint64_t*) &val));
         }
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -5528,18 +5528,20 @@ GetTemplateObjectForNative(JSContext* cx
     }
 
     if (args.length() == 1) {
         size_t len = 0;
 
         if (args[0].isInt32() && args[0].toInt32() >= 0)
             len = args[0].toInt32();
 
-        if (TypedArrayObject::GetTemplateObjectForNative(cx, native, len, res))
-            return !!res;
+        if (!TypedArrayObject::GetTemplateObjectForNative(cx, native, len, res))
+            return false;
+        if (res)
+            return true;
     }
 
     if (native == js::array_slice) {
         if (args.thisv().isObject()) {
             JSObject* obj = &args.thisv().toObject();
             if (!obj->isSingleton()) {
                 if (obj->group()->maybePreliminaryObjects()) {
                     *skipAttach = true;
@@ -5606,17 +5608,17 @@ GetTemplateObjectForClassHook(JSContext*
         templateObject.set(cx->compartment()->jitCompartment()->getSimdTemplateObjectFor(cx, descr));
         return !!templateObject;
     }
 
     return true;
 }
 
 static bool
-IsOptimizableCallStringSplit(Value callee, int argc, Value* args)
+IsOptimizableCallStringSplit(const Value& callee, int argc, Value* args)
 {
     if (argc != 2 || !args[0].isString() || !args[1].isString())
         return false;
 
     if (!args[0].toString()->isAtom() || !args[1].toString()->isAtom())
         return false;
 
     if (!callee.isObject() || !callee.toObject().is<JSFunction>())
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -8615,17 +8615,17 @@ CodeGenerator::visitArrayPopShiftT(LArra
 }
 
 typedef bool (*ArrayPushDenseFn)(JSContext*, HandleObject, HandleValue, uint32_t*);
 static const VMFunction ArrayPushDenseInfo =
     FunctionInfo<ArrayPushDenseFn>(jit::ArrayPushDense, "ArrayPushDense");
 
 void
 CodeGenerator::emitArrayPush(LInstruction* lir, const MArrayPush* mir, Register obj,
-                             ConstantOrRegister value, Register elementsTemp, Register length)
+                             const ConstantOrRegister& value, Register elementsTemp, Register length)
 {
     OutOfLineCode* ool = oolCallVM(ArrayPushDenseInfo, lir, ArgList(obj, value), StoreRegisterTo(length));
 
     RegisterOrInt32Constant key = RegisterOrInt32Constant(length);
     if (mir->unboxedType() == JSVAL_TYPE_MAGIC) {
         // Load elements and length.
         masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), elementsTemp);
         masm.load32(Address(elementsTemp, ObjectElements::offsetOfLength()), length);
@@ -9907,29 +9907,30 @@ CodeGenerator::visitNameIC(OutOfLineUpda
     StoreValueTo(ic->outputReg()).generate(this);
     restoreLiveIgnore(lir, StoreValueTo(ic->outputReg()).clobbered());
 
     masm.jump(ool->rejoin());
 }
 
 void
 CodeGenerator::addGetPropertyCache(LInstruction* ins, LiveRegisterSet liveRegs, Register objReg,
-                                   ConstantOrRegister id, TypedOrValueRegister output,
+                                   const ConstantOrRegister& id, TypedOrValueRegister output,
                                    bool monitoredResult, bool allowDoubleResult,
                                    jsbytecode* profilerLeavePc)
 {
     GetPropertyIC cache(liveRegs, objReg, id, output, monitoredResult, allowDoubleResult);
     cache.setProfilerLeavePC(profilerLeavePc);
     addCache(ins, allocateCache(cache));
 }
 
 void
 CodeGenerator::addSetPropertyCache(LInstruction* ins, LiveRegisterSet liveRegs, Register objReg,
                                    Register temp, Register tempUnbox, FloatRegister tempDouble,
-                                   FloatRegister tempF32, ConstantOrRegister id, ConstantOrRegister value,
+                                   FloatRegister tempF32, const ConstantOrRegister& id,
+                                   const ConstantOrRegister& value,
                                    bool strict, bool needsTypeBarrier, bool guardHoles,
                                    jsbytecode* profilerLeavePc)
 {
     SetPropertyIC cache(liveRegs, objReg, temp, tempUnbox, tempDouble, tempF32, id, value, strict,
                         needsTypeBarrier, guardHoles);
     cache.setProfilerLeavePC(profilerLeavePc);
     addCache(ins, allocateCache(cache));
 }
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -306,17 +306,17 @@ class CodeGenerator final : public CodeG
     void visitFallibleStoreElementT(LFallibleStoreElementT* lir);
     void visitStoreUnboxedPointer(LStoreUnboxedPointer* lir);
     void visitConvertUnboxedObjectToNative(LConvertUnboxedObjectToNative* lir);
     void emitArrayPopShift(LInstruction* lir, const MArrayPopShift* mir, Register obj,
                            Register elementsTemp, Register lengthTemp, TypedOrValueRegister out);
     void visitArrayPopShiftV(LArrayPopShiftV* lir);
     void visitArrayPopShiftT(LArrayPopShiftT* lir);
     void emitArrayPush(LInstruction* lir, const MArrayPush* mir, Register obj,
-                       ConstantOrRegister value, Register elementsTemp, Register length);
+                       const ConstantOrRegister& value, Register elementsTemp, Register length);
     void visitArrayPushV(LArrayPushV* lir);
     void visitArrayPushT(LArrayPushT* lir);
     void visitArraySlice(LArraySlice* lir);
     void visitArrayJoin(LArrayJoin* lir);
     void visitLoadUnboxedScalar(LLoadUnboxedScalar* lir);
     void visitLoadTypedArrayElementHole(LLoadTypedArrayElementHole* lir);
     void visitStoreUnboxedScalar(LStoreUnboxedScalar* lir);
     void visitStoreTypedArrayElementHole(LStoreTypedArrayElementHole* lir);
@@ -434,22 +434,23 @@ class CodeGenerator final : public CodeG
     IonScriptCounts* extractScriptCounts() {
         IonScriptCounts* counts = scriptCounts_;
         scriptCounts_ = nullptr;  // prevent delete in dtor
         return counts;
     }
 
   private:
     void addGetPropertyCache(LInstruction* ins, LiveRegisterSet liveRegs, Register objReg,
-                             ConstantOrRegister id, TypedOrValueRegister output,
+                             const ConstantOrRegister& id, TypedOrValueRegister output,
                              bool monitoredResult, bool allowDoubleResult,
                              jsbytecode* profilerLeavePc);
     void addSetPropertyCache(LInstruction* ins, LiveRegisterSet liveRegs, Register objReg,
                              Register temp, Register tempUnbox, FloatRegister tempDouble,
-                             FloatRegister tempF32, ConstantOrRegister id, ConstantOrRegister value,
+                             FloatRegister tempF32, const ConstantOrRegister& id,
+                             const ConstantOrRegister& value,
                              bool strict, bool needsTypeBarrier, bool guardHoles,
                              jsbytecode* profilerLeavePc);
 
     MOZ_MUST_USE bool generateBranchV(const ValueOperand& value, Label* ifTrue, Label* ifFalse,
                                       FloatRegister fr);
 
     void emitLambdaInit(Register resultReg, Register envChainReg,
                         const LambdaFunctionInfo& info);
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -527,17 +527,17 @@ IsOptimizableArgumentsObjectForLength(JS
 
     if (obj->as<ArgumentsObject>().hasOverriddenLength())
         return false;
 
     return true;
 }
 
 static bool
-IsOptimizableArgumentsObjectForGetElem(JSObject* obj, Value idval)
+IsOptimizableArgumentsObjectForGetElem(JSObject* obj, const Value& idval)
 {
     if (!IsOptimizableArgumentsObjectForLength(obj))
         return false;
 
     ArgumentsObject& argsObj = obj->as<ArgumentsObject>();
 
     if (argsObj.isAnyElementDeleted())
         return false;
@@ -2346,17 +2346,17 @@ IonCache::reset(ReprotectCode reprotect)
     this->stubCount_ = 0;
     PatchJump(initialJump_, fallbackLabel_, reprotect);
     lastJump_ = initialJump_;
 }
 
 // Jump to failure if a value being written is not a property for obj/id.
 static void
 CheckTypeSetForWrite(MacroAssembler& masm, JSObject* obj, jsid id,
-                     Register scratch, ConstantOrRegister value, Label* failure)
+                     Register scratch, const ConstantOrRegister& value, Label* failure)
 {
     TypedOrValueRegister valReg = value.reg();
     ObjectGroup* group = obj->group();
     MOZ_ASSERT(!group->unknownProperties());
 
     HeapTypeSet* propTypes = group->maybeGetProperty(id);
     MOZ_ASSERT(propTypes);
 
@@ -2364,17 +2364,17 @@ CheckTypeSetForWrite(MacroAssembler& mas
     TypeSet::readBarrier(propTypes);
 
     masm.guardTypeSet(valReg, propTypes, BarrierKind::TypeSet, scratch, failure);
 }
 
 static void
 GenerateSetSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& attacher,
                 JSObject* obj, Shape* shape, Register object, Register tempReg,
-                ConstantOrRegister value, bool needsTypeBarrier, bool checkTypeset,
+                const ConstantOrRegister& value, bool needsTypeBarrier, bool checkTypeset,
                 Label* failures)
 {
     TestMatchingReceiver(masm, attacher, object, obj, failures, needsTypeBarrier);
 
     // Guard that the incoming value is in the type set for the property
     // if a type barrier is required.
     if (checkTypeset) {
         MOZ_ASSERT(needsTypeBarrier);
@@ -2537,17 +2537,17 @@ ProxySetProperty(JSContext* cx, HandleOb
     ObjectOpResult result;
     return Proxy::set(cx, proxy, id, v, receiver, result)
            && result.checkStrictErrorOrWarning(cx, proxy, id, strict);
 }
 
 static bool
 EmitCallProxySet(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& attacher,
                  HandleId propId, LiveRegisterSet liveRegs, Register object,
-                 ConstantOrRegister value, void* returnAddr, bool strict)
+                 const ConstantOrRegister& value, void* returnAddr, bool strict)
 {
     MacroAssembler::AfterICSaveLive aic = masm.icSaveLive(liveRegs);
 
     // Remaining registers should be free, but we still need to use |object| so
     // leave it alone.
     //
     // WARNING: We do not take() the register used by |value|, if any, so
     // regSet is going to re-allocate it. Hence the emitted code must not touch
@@ -2683,17 +2683,17 @@ SetPropertyIC::attachDOMProxyShadowed(JS
     return linkAndAttachStub(cx, masm, attacher, ion, "DOM proxy shadowed set",
                              JS::TrackedOutcome::ICSetPropStub_DOMProxyShadowed);
 }
 
 static bool
 GenerateCallSetter(JSContext* cx, IonScript* ion, MacroAssembler& masm,
                    IonCache::StubAttacher& attacher, HandleObject obj, HandleObject holder,
                    HandleShape shape, bool strict, Register object, Register tempReg,
-                   ConstantOrRegister value, Label* failure, LiveRegisterSet liveRegs,
+                   const ConstantOrRegister& value, Label* failure, LiveRegisterSet liveRegs,
                    void* returnAddr)
 {
     // Generate prototype guards if needed.
     {
         // Generate prototype/shape guards.
         if (obj != holder)
             GeneratePrototypeGuards(cx, ion, masm, obj, holder, object, tempReg, failure);
 
@@ -3000,17 +3000,17 @@ SetPropertyIC::attachCallSetter(JSContex
 
     return linkAndAttachStub(cx, masm, attacher, ion, "setter call",
                              JS::TrackedOutcome::ICSetPropStub_CallSetter);
 }
 
 static void
 GenerateAddSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& attacher,
                 JSObject* obj, Shape* oldShape, ObjectGroup* oldGroup,
-                Register object, Register tempReg, ConstantOrRegister value,
+                Register object, Register tempReg, const ConstantOrRegister& value,
                 bool checkTypeset, Label* failures)
 {
     // Use a modified version of TestMatchingReceiver that uses the old shape and group.
     masm.branchTestObjGroup(Assembler::NotEqual, object, oldGroup, failures);
     if (obj->maybeShape()) {
         masm.branchTestObjShape(Assembler::NotEqual, object, oldShape, failures);
     } else {
         MOZ_ASSERT(obj->is<UnboxedPlainObject>());
@@ -3173,17 +3173,18 @@ SetPropertyIC::attachAddSlot(JSContext* 
 
     GenerateAddSlot(cx, masm, attacher, obj, oldShape, oldGroup, object(), temp(), value(),
                     checkTypeset, &failures);
     return linkAndAttachStub(cx, masm, attacher, ion, "adding",
                              JS::TrackedOutcome::ICSetPropStub_AddSlot);
 }
 
 static bool
-CanInlineSetPropTypeCheck(JSObject* obj, jsid id, ConstantOrRegister val, bool* checkTypeset)
+CanInlineSetPropTypeCheck(JSObject* obj, jsid id, const ConstantOrRegister& val,
+                          bool* checkTypeset)
 {
     bool shouldCheck = false;
     ObjectGroup* group = obj->group();
     if (!group->unknownProperties()) {
         HeapTypeSet* propTypes = group->maybeGetProperty(id);
         if (!propTypes)
             return false;
         if (!propTypes->unknown()) {
@@ -3212,17 +3213,17 @@ CanInlineSetPropTypeCheck(JSObject* obj,
     }
 
     *checkTypeset = shouldCheck;
     return true;
 }
 
 static bool
 IsPropertySetInlineable(NativeObject* obj, HandleId id, MutableHandleShape pshape,
-                        ConstantOrRegister val, bool needsTypeBarrier, bool* checkTypeset)
+                        const ConstantOrRegister& val, bool needsTypeBarrier, bool* checkTypeset)
 {
     // CanInlineSetPropTypeCheck assumes obj has a non-lazy group.
     MOZ_ASSERT(!obj->hasLazyGroup());
 
     // Do a pure non-proto chain climbing lookup. See note in
     // CanAttachNativeGetProp.
     pshape.set(obj->lookupPure(id));
 
@@ -3267,17 +3268,18 @@ PrototypeChainShadowsPropertyAdd(JSConte
         if (ClassMayResolveId(cx->names(), proto->getClass(), id, proto))
              return true;
     }
 
     return false;
 }
 
 static bool
-IsPropertyAddInlineable(JSContext* cx, NativeObject* obj, HandleId id, ConstantOrRegister val,
+IsPropertyAddInlineable(JSContext* cx, NativeObject* obj, HandleId id,
+                        const ConstantOrRegister& val,
                         HandleShape oldShape, bool needsTypeBarrier, bool* checkTypeset)
 {
     // If the shape of the object did not change, then this was not an add.
     if (obj->lastProperty() == oldShape)
         return false;
 
     Shape* shape = obj->lookupPure(id);
     if (!shape || shape->inDictionary() || !shape->hasSlot() || !shape->hasDefaultSetter())
@@ -3310,17 +3312,17 @@ IsPropertyAddInlineable(JSContext* cx, N
     *checkTypeset = false;
     if (needsTypeBarrier && !CanInlineSetPropTypeCheck(obj, id, val, checkTypeset))
         return false;
 
     return true;
 }
 
 static SetPropertyIC::NativeSetPropCacheability
-CanAttachNativeSetProp(JSContext* cx, HandleObject obj, HandleId id, ConstantOrRegister val,
+CanAttachNativeSetProp(JSContext* cx, HandleObject obj, HandleId id, const ConstantOrRegister& val,
                        bool needsTypeBarrier, MutableHandleObject holder,
                        MutableHandleShape shape, bool* checkTypeset)
 {
     // See if the property exists on the object.
     if (obj->isNative() && IsPropertySetInlineable(&obj->as<NativeObject>(), id, shape, val,
                                                    needsTypeBarrier, checkTypeset))
     {
         return SetPropertyIC::CanAttachSetSlot;
@@ -3352,18 +3354,18 @@ CanAttachNativeSetProp(JSContext* cx, Ha
     }
 
     return SetPropertyIC::CanAttachNone;
 }
 
 static void
 GenerateSetUnboxed(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& attacher,
                    JSObject* obj, jsid id, uint32_t unboxedOffset, JSValueType unboxedType,
-                   Register object, Register tempReg, ConstantOrRegister value, bool checkTypeset,
-                   Label* failures)
+                   Register object, Register tempReg, const ConstantOrRegister& value,
+                   bool checkTypeset, Label* failures)
 {
     // Guard on the type of the object.
     masm.branchPtr(Assembler::NotEqual,
                    Address(object, JSObject::offsetOfGroup()),
                    ImmGCPtr(obj->group()), failures);
 
     if (checkTypeset)
         CheckTypeSetForWrite(masm, obj, id, tempReg, value, failures);
@@ -3383,17 +3385,17 @@ GenerateSetUnboxed(JSContext* cx, MacroA
 
     attacher.jumpRejoin(masm);
 
     masm.bind(failures);
     attacher.jumpNextStub(masm);
 }
 
 static bool
-CanAttachSetUnboxed(JSContext* cx, HandleObject obj, HandleId id, ConstantOrRegister val,
+CanAttachSetUnboxed(JSContext* cx, HandleObject obj, HandleId id, const ConstantOrRegister& val,
                     bool needsTypeBarrier, bool* checkTypeset,
                     uint32_t* unboxedOffset, JSValueType* unboxedType)
 {
     if (!obj->is<UnboxedPlainObject>())
         return false;
 
     const UnboxedLayout::Property* property = obj->as<UnboxedPlainObject>().layout().lookup(id);
     if (property) {
@@ -3404,17 +3406,18 @@ CanAttachSetUnboxed(JSContext* cx, Handl
         *unboxedType = property->type;
         return true;
     }
 
     return false;
 }
 
 static bool
-CanAttachSetUnboxedExpando(JSContext* cx, HandleObject obj, HandleId id, ConstantOrRegister val,
+CanAttachSetUnboxedExpando(JSContext* cx, HandleObject obj, HandleId id,
+                           const ConstantOrRegister& val,
                            bool needsTypeBarrier, bool* checkTypeset, Shape** pshape)
 {
     if (!obj->is<UnboxedPlainObject>())
         return false;
 
     Rooted<UnboxedExpandoObject*> expando(cx, obj->as<UnboxedPlainObject>().maybeExpando());
     if (!expando)
         return false;
@@ -3428,17 +3431,17 @@ CanAttachSetUnboxedExpando(JSContext* cx
         return false;
 
     *pshape = shape;
     return true;
 }
 
 static bool
 CanAttachAddUnboxedExpando(JSContext* cx, HandleObject obj, HandleShape oldShape,
-                           HandleId id, ConstantOrRegister val,
+                           HandleId id, const ConstantOrRegister& val,
                            bool needsTypeBarrier, bool* checkTypeset)
 {
     if (!obj->is<UnboxedPlainObject>())
         return false;
 
     Rooted<UnboxedExpandoObject*> expando(cx, obj->as<UnboxedPlainObject>().maybeExpando());
     if (!expando || expando->inDictionaryMode())
         return false;
@@ -4045,17 +4048,17 @@ GetPropertyIC::canAttachTypedOrUnboxedAr
 
     return output.hasValue() || !output.typedReg().isFloat();
 }
 
 static void
 GenerateGetTypedOrUnboxedArrayElement(JSContext* cx, MacroAssembler& masm,
                                       IonCache::StubAttacher& attacher,
                                       HandleObject array, const Value& idval, Register object,
-                                      ConstantOrRegister index, TypedOrValueRegister output,
+                                      const ConstantOrRegister& index, TypedOrValueRegister output,
                                       bool allowDoubleResult)
 {
     MOZ_ASSERT(GetPropertyIC::canAttachTypedOrUnboxedArrayElement(array, idval, output));
 
     Label failures;
 
     TestMatchingReceiver(masm, attacher, object, array, &failures);
 
@@ -4294,17 +4297,17 @@ GetPropertyIC::tryAttachArgumentsElement
 
     MOZ_ASSERT(!hasMappedArgumentsElementStub_);
     hasMappedArgumentsElementStub_ = true;
     return linkAndAttachStub(cx, masm, attacher, ion, "ArgsObj element (mapped)",
                              JS::TrackedOutcome::ICGetElemStub_ArgsElementMapped);
 }
 
 static bool
-IsDenseElementSetInlineable(JSObject* obj, const Value& idval, ConstantOrRegister val,
+IsDenseElementSetInlineable(JSObject* obj, const Value& idval, const ConstantOrRegister& val,
                             bool needsTypeBarrier, bool* checkTypeset)
 {
     if (!obj->is<ArrayObject>())
         return false;
 
     if (obj->watched())
         return false;
 
@@ -4339,17 +4342,17 @@ static bool
 IsTypedArrayElementSetInlineable(JSObject* obj, const Value& idval, const Value& value)
 {
     // Don't bother attaching stubs for assigning strings, objects or symbols.
     return obj->is<TypedArrayObject>() && idval.isInt32() &&
            !value.isString() && !value.isObject() && !value.isSymbol();
 }
 
 static void
-StoreDenseElement(MacroAssembler& masm, ConstantOrRegister value, Register elements,
+StoreDenseElement(MacroAssembler& masm, const ConstantOrRegister& value, Register elements,
                   BaseObjectElementIndex target)
 {
     // If the ObjectElements::CONVERT_DOUBLE_ELEMENTS flag is set, int32 values
     // have to be converted to double first. If the value is not int32, it can
     // always be stored directly.
 
     Address elementsFlags(elements, ObjectElements::offsetOfFlags());
     if (value.constant()) {
@@ -4395,18 +4398,19 @@ StoreDenseElement(MacroAssembler& masm, 
     }
 
     masm.bind(&done);
 }
 
 static bool
 GenerateSetDenseElement(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& attacher,
                         JSObject* obj, const Value& idval, bool guardHoles, Register object,
-                        TypedOrValueRegister index, ConstantOrRegister value, Register tempToUnboxIndex,
-                        Register temp, bool needsTypeBarrier, bool checkTypeset)
+                        TypedOrValueRegister index, const ConstantOrRegister& value,
+                        Register tempToUnboxIndex, Register temp,
+                        bool needsTypeBarrier, bool checkTypeset)
 {
     MOZ_ASSERT(obj->isNative());
     MOZ_ASSERT(idval.isInt32());
 
     Label failures;
 
     // Guard object is a dense array.
     Shape* shape = obj->as<NativeObject>().lastProperty();
@@ -4532,17 +4536,17 @@ SetPropertyIC::tryAttachDenseElement(JSC
     const char* message = guardHoles() ?  "dense array (holes)" : "dense array";
     return linkAndAttachStub(cx, masm, attacher, ion, message,
                              JS::TrackedOutcome::ICSetElemStub_Dense);
 }
 
 static bool
 GenerateSetTypedArrayElement(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& attacher,
                              HandleObject tarr, Register object, TypedOrValueRegister index,
-                             ConstantOrRegister value, Register tempUnbox, Register temp,
+                             const ConstantOrRegister& value, Register tempUnbox, Register temp,
                              FloatRegister tempDouble, FloatRegister tempFloat32)
 {
     Label failures, done, popObjectAndFail;
 
     // Guard on the shape.
     Shape* shape = tarr->as<TypedArrayObject>().lastProperty();
     if (!shape)
         return false;
--- a/js/src/jit/IonCaches.h
+++ b/js/src/jit/IonCaches.h
@@ -406,17 +406,17 @@ class GetPropertyIC : public IonCache
     bool hasUnmappedArgumentsElementStub_ : 1;
     bool hasGenericProxyStub_ : 1;
     bool hasDenseStub_ : 1;
 
     void emitIdGuard(MacroAssembler& masm, jsid id, Label* fail);
 
   public:
     GetPropertyIC(LiveRegisterSet liveRegs,
-                  Register object, ConstantOrRegister id,
+                  Register object, const ConstantOrRegister& id,
                   TypedOrValueRegister output,
                   bool monitoredResult, bool allowDoubleResult)
       : liveRegs_(liveRegs),
         object_(object),
         id_(id),
         output_(output),
         locationsIndex_(0),
         numLocations_(0),
@@ -597,18 +597,19 @@ class SetPropertyIC : public IonCache
 
     bool hasGenericProxyStub_ : 1;
     bool hasDenseStub_ : 1;
 
     void emitIdGuard(MacroAssembler& masm, jsid id, Label* fail);
 
   public:
     SetPropertyIC(LiveRegisterSet liveRegs, Register object, Register temp, Register tempToUnboxIndex,
-                  FloatRegister tempDouble, FloatRegister tempFloat32, ConstantOrRegister id,
-                  ConstantOrRegister value, bool strict, bool needsTypeBarrier, bool guardHoles)
+                  FloatRegister tempDouble, FloatRegister tempFloat32,
+                  const ConstantOrRegister& id, const ConstantOrRegister& value,
+                  bool strict, bool needsTypeBarrier, bool guardHoles)
       : liveRegs_(liveRegs),
         object_(object),
         temp_(temp),
         tempToUnboxIndex_(tempToUnboxIndex),
         tempDouble_(tempDouble),
         tempFloat32_(tempFloat32),
         id_(id),
         value_(value),
--- a/js/src/jit/JitFrameIterator.h
+++ b/js/src/jit/JitFrameIterator.h
@@ -373,17 +373,17 @@ struct MaybeReadFallback
     bool canRecoverResults() { return maybeCx; }
 
     Value unreadablePlaceholder() const {
         if (unreadablePlaceholder_ == NoGC_MagicOptimizedOut)
             return MagicValue(JS_OPTIMIZED_OUT);
         return UndefinedValue();
     }
 
-    NoGCValue noGCPlaceholder(Value v) const {
+    NoGCValue noGCPlaceholder(const Value& v) const {
         if (v.isMagic(JS_OPTIMIZED_OUT))
             return NoGC_MagicOptimizedOut;
         return NoGC_UndefinedValue;
     }
 };
 
 
 class RResumePoint;
@@ -440,17 +440,17 @@ class SnapshotIterator
     }
     bool hasInstructionResults() const {
         return instructionResults_;
     }
     Value fromInstructionResult(uint32_t index) const;
 
     Value allocationValue(const RValueAllocation& a, ReadMethod rm = RM_Normal);
     MOZ_MUST_USE bool allocationReadable(const RValueAllocation& a, ReadMethod rm = RM_Normal);
-    void writeAllocationValuePayload(const RValueAllocation& a, Value v);
+    void writeAllocationValuePayload(const RValueAllocation& a, const Value& v);
     void warnUnreadableAllocation();
 
   private:
     friend class RSimdBox;
     const FloatRegisters::RegisterContent* floatAllocationPointer(const RValueAllocation& a) const;
 
   public:
     // Handle iterating over RValueAllocations of the snapshots.
@@ -472,17 +472,17 @@ class SnapshotIterator
     inline bool moreAllocations() const {
         return snapshot_.numAllocationsRead() < numAllocations();
     }
 
     int32_t readOuterNumActualArgs() const;
 
     // Used by recover instruction to store the value back into the instruction
     // results array.
-    void storeInstructionResult(Value v);
+    void storeInstructionResult(const Value& v);
 
   public:
     // Exhibits frame properties contained in the snapshot.
     uint32_t pcOffset() const;
     inline MOZ_MUST_USE bool resumeAfter() const {
         // Inline frames are inlined on calls, which are considered as being
         // resumed on the Call as baseline will push the pc once we return from
         // the call.
@@ -646,17 +646,17 @@ class InlineFrameIterator
     MachineState machine_;
 
     struct Nop {
         void operator()(const Value& v) { }
     };
 
   private:
     void findNextFrame();
-    JSObject* computeEnvironmentChain(Value envChainValue, MaybeReadFallback& fallback,
+    JSObject* computeEnvironmentChain(const Value& envChainValue, MaybeReadFallback& fallback,
                                       bool* hasInitialEnv = nullptr) const;
 
   public:
     InlineFrameIterator(JSContext* cx, const JitFrameIterator* iter);
     InlineFrameIterator(JSRuntime* rt, const JitFrameIterator* iter);
     InlineFrameIterator(JSContext* cx, const InlineFrameIterator* iter);
 
     bool more() const {
--- a/js/src/jit/JitFrames.cpp
+++ b/js/src/jit/JitFrames.cpp
@@ -1921,17 +1921,17 @@ SnapshotIterator::maybeRead(const RValue
 
         MOZ_ASSERT_UNREACHABLE("All allocations should be readable.");
     }
 
     return fallback.unreadablePlaceholder();
 }
 
 void
-SnapshotIterator::writeAllocationValuePayload(const RValueAllocation& alloc, Value v)
+SnapshotIterator::writeAllocationValuePayload(const RValueAllocation& alloc, const Value& v)
 {
     uintptr_t payload = *v.payloadUIntPtr();
 #if defined(JS_PUNBOX64)
     // Do not write back the tag, as this will trigger an assertion when we will
     // reconstruct the JS Value while marking again or when bailing out.
     payload &= JSVAL_PAYLOAD_MASK;
 #endif
 
@@ -2139,17 +2139,17 @@ SnapshotIterator::computeInstructionResu
         }
     }
 
     MOZ_ASSERT(results->isInitialized());
     return true;
 }
 
 void
-SnapshotIterator::storeInstructionResult(Value v)
+SnapshotIterator::storeInstructionResult(const Value& v)
 {
     uint32_t currIns = recover_.numInstructionsRead() - 1;
     MOZ_ASSERT((*instructionResults_)[currIns].isMagic(JS_ION_BAILOUT));
     (*instructionResults_)[currIns] = v;
 }
 
 Value
 SnapshotIterator::fromInstructionResult(uint32_t index) const
@@ -2400,17 +2400,18 @@ InlineFrameIterator::callee(MaybeReadFal
 
     SnapshotIterator s(si_);
     // :TODO: Handle allocation failures from recover instruction.
     Value funval = s.maybeRead(calleeRVA_, fallback);
     return &funval.toObject().as<JSFunction>();
 }
 
 JSObject*
-InlineFrameIterator::computeEnvironmentChain(Value envChainValue, MaybeReadFallback& fallback,
+InlineFrameIterator::computeEnvironmentChain(const Value& envChainValue,
+                                             MaybeReadFallback& fallback,
                                              bool* hasInitialEnv) const
 {
     if (envChainValue.isObject()) {
         if (hasInitialEnv) {
             if (fallback.canRecoverResults()) {
                 RootedObject obj(fallback.maybeCx, &envChainValue.toObject());
                 *hasInitialEnv = isFunctionFrame() &&
                                  callee(fallback)->needsFunctionEnvironmentObjects();
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -571,17 +571,17 @@ StoreUnboxedFailure(MacroAssembler& masm
         masm.jump(failure);
     else
         masm.assumeUnreachable("Incompatible write to unboxed property");
 }
 
 template <typename T>
 void
 MacroAssembler::storeUnboxedProperty(T address, JSValueType type,
-                                     ConstantOrRegister value, Label* failure)
+                                     const ConstantOrRegister& value, Label* failure)
 {
     switch (type) {
       case JSVAL_TYPE_BOOLEAN:
         if (value.constant()) {
             if (value.value().isBoolean())
                 store8(Imm32(value.value().toBoolean()), address);
             else
                 StoreUnboxedFailure(*this, failure);
@@ -690,21 +690,21 @@ MacroAssembler::storeUnboxedProperty(T a
 
       default:
         MOZ_CRASH();
     }
 }
 
 template void
 MacroAssembler::storeUnboxedProperty(Address address, JSValueType type,
-                                     ConstantOrRegister value, Label* failure);
+                                     const ConstantOrRegister& value, Label* failure);
 
 template void
 MacroAssembler::storeUnboxedProperty(BaseIndex address, JSValueType type,
-                                     ConstantOrRegister value, Label* failure);
+                                     const ConstantOrRegister& value, Label* failure);
 
 void
 MacroAssembler::checkUnboxedArrayCapacity(Register obj, const RegisterOrInt32Constant& index,
                                           Register temp, Label* failure)
 {
     Address initLengthAddr(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength());
     Address lengthAddr(obj, UnboxedArrayObject::offsetOfLength());
 
@@ -1874,17 +1874,18 @@ MacroAssembler::convertValueToFloatingPo
     }
 
     MOZ_ASSERT(v.isObject() || v.isSymbol());
     jump(fail);
     return true;
 }
 
 bool
-MacroAssembler::convertConstantOrRegisterToFloatingPoint(JSContext* cx, ConstantOrRegister src,
+MacroAssembler::convertConstantOrRegisterToFloatingPoint(JSContext* cx,
+                                                         const ConstantOrRegister& src,
                                                          FloatRegister output, Label* fail,
                                                          MIRType outputType)
 {
     if (src.constant())
         return convertValueToFloatingPoint(cx, src.value(), output, fail, outputType);
 
     convertTypedOrValueToFloatingPoint(src.reg(), output, fail, outputType);
     return true;
@@ -2143,17 +2144,18 @@ MacroAssembler::convertValueToInt(JSCont
 
     MOZ_ASSERT(v.isObject() || v.isSymbol());
 
     jump(fail);
     return true;
 }
 
 bool
-MacroAssembler::convertConstantOrRegisterToInt(JSContext* cx, ConstantOrRegister src,
+MacroAssembler::convertConstantOrRegisterToInt(JSContext* cx,
+                                               const ConstantOrRegister& src,
                                                FloatRegister temp, Register output,
                                                Label* fail, IntConversionBehavior behavior)
 {
     if (src.constant())
         return convertValueToInt(cx, src.value(), output, fail, behavior);
 
     convertTypedOrValueToInt(src.reg(), temp, output, fail, behavior);
     return true;
@@ -2447,17 +2449,17 @@ MacroAssembler::Push(TypedOrValueRegiste
         }
         Push(reg);
     } else {
         Push(ValueTypeFromMIRType(v.type()), v.typedReg().gpr());
     }
 }
 
 void
-MacroAssembler::Push(ConstantOrRegister v)
+MacroAssembler::Push(const ConstantOrRegister& v)
 {
     if (v.constant())
         Push(v.value());
     else
         Push(v.reg());
 }
 
 void
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -433,17 +433,17 @@ class MacroAssembler : public MacroAssem
     void Push(Register reg1, Register reg2, Register reg3, Register reg4) DEFINED_ON(arm64);
     void Push(const Imm32 imm) PER_SHARED_ARCH;
     void Push(const ImmWord imm) PER_SHARED_ARCH;
     void Push(const ImmPtr imm) PER_SHARED_ARCH;
     void Push(const ImmGCPtr ptr) PER_SHARED_ARCH;
     void Push(FloatRegister reg) PER_SHARED_ARCH;
     void Push(jsid id, Register scratchReg);
     void Push(TypedOrValueRegister v);
-    void Push(ConstantOrRegister v);
+    void Push(const ConstantOrRegister& v);
     void Push(const ValueOperand& val);
     void Push(const Value& val);
     void Push(JSValueType type, Register reg);
     void PushValue(const Address& addr);
     void PushEmptyRooted(VMFunction::RootType rootType);
     inline CodeOffset PushWithPatch(ImmWord word);
     inline CodeOffset PushWithPatch(ImmPtr imm);
 
@@ -1289,17 +1289,17 @@ class MacroAssembler : public MacroAssem
 
     template<class T>
     inline void storeFloat32(FloatRegister src, const T& dest);
 
     inline void storeFloat32x3(FloatRegister src, const Address& dest) PER_SHARED_ARCH;
     inline void storeFloat32x3(FloatRegister src, const BaseIndex& dest) PER_SHARED_ARCH;
 
     template <typename T>
-    void storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const T& dest,
+    void storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType, const T& dest,
                            MIRType slotType) PER_ARCH;
 
   public:
     // ========================================================================
     // Truncate floating point.
 
     // Undefined behaviour when truncation is outside Int64 range.
     // Needs a temp register if SSE3 is not present.
@@ -1463,17 +1463,17 @@ class MacroAssembler : public MacroAssem
             storeValue(ValueTypeFromMIRType(src.type()), src.typedReg().gpr(), dest);
         }
     }
 
     template <typename T>
     inline void storeObjectOrNull(Register src, const T& dest);
 
     template <typename T>
-    void storeConstantOrRegister(ConstantOrRegister src, const T& dest) {
+    void storeConstantOrRegister(const ConstantOrRegister& src, const T& dest) {
         if (src.constant())
             storeValue(src.value(), dest);
         else
             storeTypedOrValue(src.reg(), dest);
     }
 
     void storeCallResult(Register reg) {
         if (reg != ReturnReg)
@@ -1603,17 +1603,17 @@ class MacroAssembler : public MacroAssem
     template <typename T>
     void loadUnboxedProperty(T address, JSValueType type, TypedOrValueRegister output);
 
     // Store a property to an UnboxedPlainObject, without triggering barriers.
     // If failure is null, the value definitely has a type suitable for storing
     // in the property.
     template <typename T>
     void storeUnboxedProperty(T address, JSValueType type,
-                              ConstantOrRegister value, Label* failure);
+                              const ConstantOrRegister& value, Label* failure);
 
     void checkUnboxedArrayCapacity(Register obj, const RegisterOrInt32Constant& index,
                                    Register temp, Label* failure);
 
     Register extractString(const Address& address, Register scratch) {
         return extractObject(address, scratch);
     }
     Register extractString(const ValueOperand& value, Register scratch) {
@@ -1848,50 +1848,52 @@ class MacroAssembler : public MacroAssem
 #undef DISPATCH_FLOATING_POINT_OP
 
     void convertValueToFloatingPoint(ValueOperand value, FloatRegister output, Label* fail,
                                      MIRType outputType);
     MOZ_MUST_USE bool convertValueToFloatingPoint(JSContext* cx, const Value& v,
                                                   FloatRegister output, Label* fail,
                                                   MIRType outputType);
     MOZ_MUST_USE bool convertConstantOrRegisterToFloatingPoint(JSContext* cx,
-                                                               ConstantOrRegister src,
+                                                               const ConstantOrRegister& src,
                                                                FloatRegister output, Label* fail,
                                                                MIRType outputType);
     void convertTypedOrValueToFloatingPoint(TypedOrValueRegister src, FloatRegister output,
                                             Label* fail, MIRType outputType);
 
     void outOfLineTruncateSlow(FloatRegister src, Register dest, bool widenFloatToDouble,
                                bool compilingAsmJS);
 
     void convertInt32ValueToDouble(const Address& address, Register scratch, Label* done);
     void convertValueToDouble(ValueOperand value, FloatRegister output, Label* fail) {
         convertValueToFloatingPoint(value, output, fail, MIRType::Double);
     }
     MOZ_MUST_USE bool convertValueToDouble(JSContext* cx, const Value& v, FloatRegister output,
                                            Label* fail) {
         return convertValueToFloatingPoint(cx, v, output, fail, MIRType::Double);
     }
-    MOZ_MUST_USE bool convertConstantOrRegisterToDouble(JSContext* cx, ConstantOrRegister src,
+    MOZ_MUST_USE bool convertConstantOrRegisterToDouble(JSContext* cx,
+                                                        const ConstantOrRegister& src,
                                                         FloatRegister output, Label* fail)
     {
         return convertConstantOrRegisterToFloatingPoint(cx, src, output, fail, MIRType::Double);
     }
     void convertTypedOrValueToDouble(TypedOrValueRegister src, FloatRegister output, Label* fail) {
         convertTypedOrValueToFloatingPoint(src, output, fail, MIRType::Double);
     }
 
     void convertValueToFloat(ValueOperand value, FloatRegister output, Label* fail) {
         convertValueToFloatingPoint(value, output, fail, MIRType::Float32);
     }
     MOZ_MUST_USE bool convertValueToFloat(JSContext* cx, const Value& v, FloatRegister output,
                                           Label* fail) {
         return convertValueToFloatingPoint(cx, v, output, fail, MIRType::Float32);
     }
-    MOZ_MUST_USE bool convertConstantOrRegisterToFloat(JSContext* cx, ConstantOrRegister src,
+    MOZ_MUST_USE bool convertConstantOrRegisterToFloat(JSContext* cx,
+                                                       const ConstantOrRegister& src,
                                                        FloatRegister output, Label* fail)
     {
         return convertConstantOrRegisterToFloatingPoint(cx, src, output, fail, MIRType::Float32);
     }
     void convertTypedOrValueToFloat(TypedOrValueRegister src, FloatRegister output, Label* fail) {
         convertTypedOrValueToFloatingPoint(src, output, fail, MIRType::Float32);
     }
 
@@ -1927,17 +1929,18 @@ class MacroAssembler : public MacroAssem
     void convertValueToInt(ValueOperand value, FloatRegister temp, Register output, Label* fail,
                            IntConversionBehavior behavior)
     {
         convertValueToInt(value, nullptr, nullptr, nullptr, nullptr, InvalidReg, temp, output,
                           fail, behavior);
     }
     MOZ_MUST_USE bool convertValueToInt(JSContext* cx, const Value& v, Register output, Label* fail,
                                         IntConversionBehavior behavior);
-    MOZ_MUST_USE bool convertConstantOrRegisterToInt(JSContext* cx, ConstantOrRegister src,
+    MOZ_MUST_USE bool convertConstantOrRegisterToInt(JSContext* cx,
+                                                     const ConstantOrRegister& src,
                                                      FloatRegister temp, Register output,
                                                      Label* fail, IntConversionBehavior behavior);
     void convertTypedOrValueToInt(TypedOrValueRegister src, FloatRegister temp, Register output,
                                   Label* fail, IntConversionBehavior behavior);
 
     //
     // Convenience functions for converting values to int32.
     //
@@ -1960,17 +1963,18 @@ class MacroAssembler : public MacroAssem
     }
     MOZ_MUST_USE bool convertValueToInt32(JSContext* cx, const Value& v, Register output,
                                           Label* fail, bool negativeZeroCheck)
     {
         return convertValueToInt(cx, v, output, fail, negativeZeroCheck
                                  ? IntConversion_NegativeZeroCheck
                                  : IntConversion_Normal);
     }
-    MOZ_MUST_USE bool convertConstantOrRegisterToInt32(JSContext* cx, ConstantOrRegister src,
+    MOZ_MUST_USE bool convertConstantOrRegisterToInt32(JSContext* cx,
+                                                       const ConstantOrRegister& src,
                                                        FloatRegister temp, Register output,
                                                        Label* fail, bool negativeZeroCheck)
     {
         return convertConstantOrRegisterToInt(cx, src, temp, output, fail, negativeZeroCheck
                                               ? IntConversion_NegativeZeroCheck
                                               : IntConversion_Normal);
     }
     void convertTypedOrValueToInt32(TypedOrValueRegister src, FloatRegister temp, Register output,
@@ -2000,17 +2004,18 @@ class MacroAssembler : public MacroAssem
     {
         convertValueToInt(value, input, nullptr, nullptr, nullptr, InvalidReg, temp, output, fail,
                           IntConversion_Truncate);
     }
     MOZ_MUST_USE bool truncateValueToInt32(JSContext* cx, const Value& v, Register output,
                                            Label* fail) {
         return convertValueToInt(cx, v, output, fail, IntConversion_Truncate);
     }
-    MOZ_MUST_USE bool truncateConstantOrRegisterToInt32(JSContext* cx, ConstantOrRegister src,
+    MOZ_MUST_USE bool truncateConstantOrRegisterToInt32(JSContext* cx,
+                                                        const ConstantOrRegister& src,
                                                         FloatRegister temp, Register output,
                                                         Label* fail)
     {
         return convertConstantOrRegisterToInt(cx, src, temp, output, fail, IntConversion_Truncate);
     }
     void truncateTypedOrValueToInt32(TypedOrValueRegister src, FloatRegister temp, Register output,
                                      Label* fail)
     {
@@ -2033,17 +2038,18 @@ class MacroAssembler : public MacroAssem
     {
         convertValueToInt(value, input, nullptr, nullptr, nullptr, InvalidReg, temp, output, fail,
                           IntConversion_ClampToUint8);
     }
     MOZ_MUST_USE bool clampValueToUint8(JSContext* cx, const Value& v, Register output,
                                         Label* fail) {
         return convertValueToInt(cx, v, output, fail, IntConversion_ClampToUint8);
     }
-    MOZ_MUST_USE bool clampConstantOrRegisterToUint8(JSContext* cx, ConstantOrRegister src,
+    MOZ_MUST_USE bool clampConstantOrRegisterToUint8(JSContext* cx,
+                                                     const ConstantOrRegister& src,
                                                      FloatRegister temp, Register output,
                                                      Label* fail)
     {
         return convertConstantOrRegisterToInt(cx, src, temp, output, fail,
                                               IntConversion_ClampToUint8);
     }
     void clampTypedOrValueToUint8(TypedOrValueRegister src, FloatRegister temp, Register output,
                                   Label* fail)
--- a/js/src/jit/RegisterSets.h
+++ b/js/src/jit/RegisterSets.h
@@ -225,51 +225,59 @@ class ConstantOrRegister
     bool constant_;
 
     // Space to hold either a Value or a TypedOrValueRegister.
     union U {
         Value constant;
         TypedOrValueRegister reg;
     } data;
 
-    Value& dataValue() {
+    const Value& dataValue() const {
         MOZ_ASSERT(constant());
         return data.constant;
     }
-    TypedOrValueRegister& dataReg() {
+    void setDataValue(const Value& value) {
+        MOZ_ASSERT(constant());
+        data.constant = value;
+    }
+    const TypedOrValueRegister& dataReg() const {
         MOZ_ASSERT(!constant());
         return data.reg;
     }
+    void setDataReg(const TypedOrValueRegister& reg) {
+        MOZ_ASSERT(!constant());
+        data.reg = reg;
+    }
 
   public:
 
     ConstantOrRegister()
     {}
 
-    MOZ_IMPLICIT ConstantOrRegister(Value value)
+    MOZ_IMPLICIT ConstantOrRegister(const Value& value)
       : constant_(true)
     {
-        dataValue() = value;
+        setDataValue(value);
     }
 
     MOZ_IMPLICIT ConstantOrRegister(TypedOrValueRegister reg)
       : constant_(false)
     {
-        dataReg() = reg;
+        setDataReg(reg);
     }
 
-    bool constant() {
+    bool constant() const {
         return constant_;
     }
 
-    Value value() {
+    const Value& value() const {
         return dataValue();
     }
 
-    TypedOrValueRegister reg() {
+    const TypedOrValueRegister& reg() const {
         return dataReg();
     }
 };
 
 struct RegisterOrInt32Constant {
     bool isRegister_;
     union {
         Register reg_;
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -5203,18 +5203,18 @@ MacroAssembler::branchTestValue(Conditio
     ma_cmp(lhs.typeReg(), Imm32(jv.s.tag), Equal);
     ma_b(label, cond);
 }
 
 // ========================================================================
 // Memory access primitives.
 template <typename T>
 void
-MacroAssembler::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const T& dest,
-                                  MIRType slotType)
+MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType,
+                                  const T& dest, MIRType slotType)
 {
     if (valueType == MIRType::Double) {
         storeDouble(value.reg().typedReg().fpu(), dest);
         return;
     }
 
     // Store the type tag if needed.
     if (valueType != slotType)
@@ -5223,20 +5223,20 @@ MacroAssembler::storeUnboxedValue(Consta
     // Store the payload.
     if (value.constant())
         storePayload(value.value(), dest);
     else
         storePayload(value.reg().typedReg().gpr(), dest);
 }
 
 template void
-MacroAssembler::storeUnboxedValue(ConstantOrRegister value, MIRType valueType,
+MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType,
                                   const Address& dest, MIRType slotType);
 template void
-MacroAssembler::storeUnboxedValue(ConstantOrRegister value, MIRType valueType,
+MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType,
                                   const BaseIndex& dest, MIRType slotType);
 
 //}}} check_macroassembler_style
 
 void
 MacroAssemblerARM::emitUnalignedLoad(bool isSigned, unsigned byteSize, Register ptr, Register tmp,
                                      Register dest, unsigned offset)
 {
--- a/js/src/jit/arm64/MacroAssembler-arm64.cpp
+++ b/js/src/jit/arm64/MacroAssembler-arm64.cpp
@@ -784,18 +784,18 @@ MacroAssembler::branchTestValue(Conditio
     Cmp(ARMRegister(lhs.valueReg(), 64), scratch64);
     B(label, cond);
 }
 
 // ========================================================================
 // Memory access primitives.
 template <typename T>
 void
-MacroAssembler::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const T& dest,
-                                  MIRType slotType)
+MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType,
+                                  const T& dest, MIRType slotType)
 {
     if (valueType == MIRType::Double) {
         storeDouble(value.reg().typedReg().fpu(), dest);
         return;
     }
 
     // For known integers and booleans, we can just store the unboxed value if
     // the slot has the same type.
@@ -815,20 +815,20 @@ MacroAssembler::storeUnboxedValue(Consta
     if (value.constant())
         storeValue(value.value(), dest);
     else
         storeValue(ValueTypeFromMIRType(valueType), value.reg().typedReg().gpr(), dest);
 
 }
 
 template void
-MacroAssembler::storeUnboxedValue(ConstantOrRegister value, MIRType valueType,
+MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType,
                                   const Address& dest, MIRType slotType);
 template void
-MacroAssembler::storeUnboxedValue(ConstantOrRegister value, MIRType valueType,
+MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType,
                                   const BaseIndex& dest, MIRType slotType);
 
 void
 MacroAssembler::comment(const char* msg)
 {
     Assembler::comment(msg);
 }
 
--- a/js/src/jit/mips32/MacroAssembler-mips32.cpp
+++ b/js/src/jit/mips32/MacroAssembler-mips32.cpp
@@ -2213,18 +2213,18 @@ MacroAssembler::branchTestValue(Conditio
         ma_b(lhs.typeReg(), Imm32(getType(rhs)), label, NotEqual);
     }
 }
 
 // ========================================================================
 // Memory access primitives.
 template <typename T>
 void
-MacroAssembler::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const T& dest,
-                                  MIRType slotType)
+MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType,
+                                  const T& dest, MIRType slotType)
 {
     if (valueType == MIRType::Double) {
         storeDouble(value.reg().typedReg().fpu(), dest);
         return;
     }
 
     // Store the type tag if needed.
     if (valueType != slotType)
@@ -2233,15 +2233,15 @@ MacroAssembler::storeUnboxedValue(Consta
     // Store the payload.
     if (value.constant())
         storePayload(value.value(), dest);
     else
         storePayload(value.reg().typedReg().gpr(), dest);
 }
 
 template void
-MacroAssembler::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const Address& dest,
-                                  MIRType slotType);
+MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType,
+                                  const Address& dest, MIRType slotType);
 template void
-MacroAssembler::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const BaseIndex& dest,
-                                  MIRType slotType);
+MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType,
+                                  const BaseIndex& dest, MIRType slotType);
 
 //}}} check_macroassembler_style
--- a/js/src/jit/mips64/MacroAssembler-mips64.cpp
+++ b/js/src/jit/mips64/MacroAssembler-mips64.cpp
@@ -2315,18 +2315,18 @@ MacroAssembler::branchTestValue(Conditio
     moveValue(rhs, scratch);
     ma_b(lhs.valueReg(), scratch, label, cond);
 }
 
 // ========================================================================
 // Memory access primitives.
 template <typename T>
 void
-MacroAssembler::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const T& dest,
-                                  MIRType slotType)
+MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType,
+                                  const T& dest, MIRType slotType)
 {
     if (valueType == MIRType::Double) {
         storeDouble(value.reg().typedReg().fpu(), dest);
         return;
     }
 
     // For known integers and booleans, we can just store the unboxed value if
     // the slot has the same type.
@@ -2345,15 +2345,15 @@ MacroAssembler::storeUnboxedValue(Consta
 
     if (value.constant())
         storeValue(value.value(), dest);
     else
         storeValue(ValueTypeFromMIRType(valueType), value.reg().typedReg().gpr(), dest);
 }
 
 template void
-MacroAssembler::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const Address& dest,
-                                  MIRType slotType);
+MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType,
+                                  const Address& dest, MIRType slotType);
 template void
-MacroAssembler::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const BaseIndex& dest,
-                                  MIRType slotType);
+MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType,
+                                  const BaseIndex& dest, MIRType slotType);
 
 //}}} check_macroassembler_style
--- a/js/src/jit/none/MacroAssembler-none.h
+++ b/js/src/jit/none/MacroAssembler-none.h
@@ -391,17 +391,17 @@ class MacroAssemblerNone : public Assemb
     void loadConstantDouble(double, FloatRegister) { MOZ_CRASH(); }
     void loadConstantFloat32(float, FloatRegister) { MOZ_CRASH(); }
     void loadConstantDouble(wasm::RawF64, FloatRegister) { MOZ_CRASH(); }
     void loadConstantFloat32(wasm::RawF32, FloatRegister) { MOZ_CRASH(); }
     Condition testInt32Truthy(bool, ValueOperand) { MOZ_CRASH(); }
     Condition testStringTruthy(bool, ValueOperand) { MOZ_CRASH(); }
 
     template <typename T> void loadUnboxedValue(T, MIRType, AnyRegister) { MOZ_CRASH(); }
-    template <typename T> void storeUnboxedValue(ConstantOrRegister, MIRType, T, MIRType) { MOZ_CRASH(); }
+    template <typename T> void storeUnboxedValue(const ConstantOrRegister&, MIRType, T, MIRType) { MOZ_CRASH(); }
     template <typename T> void storeUnboxedPayload(ValueOperand value, T, size_t) { MOZ_CRASH(); }
 
     void convertUInt32ToDouble(Register, FloatRegister) { MOZ_CRASH(); }
     void convertUInt32ToFloat32(Register, FloatRegister) { MOZ_CRASH(); }
     void incrementInt32Value(Address) { MOZ_CRASH(); }
     void ensureDouble(ValueOperand, FloatRegister, Label*) { MOZ_CRASH(); }
     void handleFailureWithHandlerTail(void*) { MOZ_CRASH(); }
 
--- a/js/src/jit/shared/CodeGenerator-shared.h
+++ b/js/src/jit/shared/CodeGenerator-shared.h
@@ -644,37 +644,39 @@ class ArgSeq<>
     inline void generate(CodeGeneratorShared* codegen) const {
     }
 };
 
 template <typename HeadType, typename... TailTypes>
 class ArgSeq<HeadType, TailTypes...> : public ArgSeq<TailTypes...>
 {
   private:
-    HeadType head_;
+    using RawHeadType = typename mozilla::RemoveReference<HeadType>::Type;
+    RawHeadType head_;
 
   public:
-    explicit ArgSeq(HeadType&& head, TailTypes&&... tail)
-      : ArgSeq<TailTypes...>(mozilla::Move(tail)...),
-        head_(mozilla::Move(head))
+    template <typename ProvidedHead, typename... ProvidedTail>
+    explicit ArgSeq(ProvidedHead&& head, ProvidedTail&&... tail)
+      : ArgSeq<TailTypes...>(mozilla::Forward<ProvidedTail>(tail)...),
+        head_(mozilla::Forward<ProvidedHead>(head))
     { }
 
     // Arguments are pushed in reverse order, from last argument to first
     // argument.
     inline void generate(CodeGeneratorShared* codegen) const {
         this->ArgSeq<TailTypes...>::generate(codegen);
         codegen->pushArg(head_);
     }
 };
 
 template <typename... ArgTypes>
 inline ArgSeq<ArgTypes...>
-ArgList(ArgTypes... args)
+ArgList(ArgTypes&&... args)
 {
-    return ArgSeq<ArgTypes...>(mozilla::Move(args)...);
+    return ArgSeq<ArgTypes...>(mozilla::Forward<ArgTypes>(args)...);
 }
 
 // Store wrappers, to generate the right move of data after the VM call.
 
 struct StoreNothing
 {
     inline void generate(CodeGeneratorShared* codegen) const {
     }
--- a/js/src/jit/x64/MacroAssembler-x64.cpp
+++ b/js/src/jit/x64/MacroAssembler-x64.cpp
@@ -616,18 +616,18 @@ MacroAssembler::branchTestValue(Conditio
     cmpPtr(lhs.valueReg(), scratch);
     j(cond, label);
 }
 
 // ========================================================================
 // Memory access primitives.
 template <typename T>
 void
-MacroAssembler::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const T& dest,
-                                  MIRType slotType)
+MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType,
+                                  const T& dest, MIRType slotType)
 {
     if (valueType == MIRType::Double) {
         storeDouble(value.reg().typedReg().fpu(), dest);
         return;
     }
 
     // For known integers and booleans, we can just store the unboxed value if
     // the slot has the same type.
@@ -646,21 +646,21 @@ MacroAssembler::storeUnboxedValue(Consta
 
     if (value.constant())
         storeValue(value.value(), dest);
     else
         storeValue(ValueTypeFromMIRType(valueType), value.reg().typedReg().gpr(), dest);
 }
 
 template void
-MacroAssembler::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const Address& dest,
-                                  MIRType slotType);
+MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType,
+                                  const Address& dest, MIRType slotType);
 template void
-MacroAssembler::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const BaseIndex& dest,
-                                  MIRType slotType);
+MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType,
+                                  const BaseIndex& dest, MIRType slotType);
 
 // ========================================================================
 // wasm support
 
 void
 MacroAssembler::wasmLoad(Scalar::Type type, unsigned numSimdElems, Operand srcAddr, AnyRegister out)
 {
     switch (type) {
--- a/js/src/jit/x86/MacroAssembler-x86.cpp
+++ b/js/src/jit/x86/MacroAssembler-x86.cpp
@@ -524,18 +524,18 @@ MacroAssembler::branchTestValue(Conditio
         j(NotEqual, label);
     }
 }
 
 // ========================================================================
 // Memory access primitives.
 template <typename T>
 void
-MacroAssembler::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const T& dest,
-                                     MIRType slotType)
+MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType,
+                                  const T& dest, MIRType slotType)
 {
     if (valueType == MIRType::Double) {
         storeDouble(value.reg().typedReg().fpu(), dest);
         return;
     }
 
     // Store the type tag if needed.
     if (valueType != slotType)
@@ -544,21 +544,21 @@ MacroAssembler::storeUnboxedValue(Consta
     // Store the payload.
     if (value.constant())
         storePayload(value.value(), Operand(dest));
     else
         storePayload(value.reg().typedReg().gpr(), Operand(dest));
 }
 
 template void
-MacroAssembler::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const Address& dest,
-                                     MIRType slotType);
+MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType,
+                                  const Address& dest, MIRType slotType);
 template void
-MacroAssembler::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const BaseIndex& dest,
-                                     MIRType slotType);
+MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType,
+                                  const BaseIndex& dest, MIRType slotType);
 
 // wasm specific methods, used in both the wasm baseline compiler and ion.
 
 void
 MacroAssembler::wasmLoad(Scalar::Type type, unsigned numSimdElems, Operand srcAddr, AnyRegister out)
 {
     switch (type) {
       case Scalar::Int8:
--- a/js/src/jsapi-tests/testArrayBuffer.cpp
+++ b/js/src/jsapi-tests/testArrayBuffer.cpp
@@ -153,8 +153,75 @@ static void GC(JSContext* cx)
 }
 
 bool hasDetachedBuffer(JS::HandleObject obj) {
     JS::RootedValue v(cx);
     return JS_GetProperty(cx, obj, "byteLength", &v) && v.toInt32() == 0;
 }
 
 END_TEST(testArrayBuffer_bug720949_viewList)
+
+BEGIN_TEST(testArrayBuffer_externalize)
+{
+    if (!testWithSize(cx, 2))    // ArrayBuffer data stored inline in the object.
+        return false;
+    if (!testWithSize(cx, 2000)) // ArrayBuffer data stored out-of-line in a separate heap allocation.
+        return false;
+
+    return true;
+}
+
+bool testWithSize(JSContext* cx, size_t n)
+{
+    JS::RootedObject buffer(cx, JS_NewArrayBuffer(cx, n));
+    CHECK(buffer != nullptr);
+
+    JS::RootedObject view(cx, JS_NewUint8ArrayWithBuffer(cx, buffer, 0, -1));
+    CHECK(view != nullptr);
+
+    void* contents = JS_ExternalizeArrayBufferContents(cx, buffer);
+    CHECK(contents != nullptr);
+    uint32_t actualLength;
+    CHECK(hasExpectedLength(cx, view, &actualLength));
+    CHECK(actualLength == n);
+    CHECK(!JS_IsDetachedArrayBufferObject(buffer));
+    CHECK(JS_GetArrayBufferByteLength(buffer) == uint32_t(n));
+
+    uint8_t* uint8Contents = static_cast<uint8_t*>(contents);
+    CHECK(*uint8Contents == 0);
+    uint8_t randomByte(rand() % UINT8_MAX);
+    *uint8Contents = randomByte;
+
+    JS::RootedValue v(cx);
+    CHECK(JS_GetElement(cx, view, 0, &v));
+    CHECK(v.toInt32() == randomByte);
+
+    view = nullptr;
+    GC(cx);
+
+    CHECK(JS_DetachArrayBuffer(cx, buffer));
+    GC(cx);
+    CHECK(*uint8Contents == randomByte);
+    JS_free(cx, contents);
+    GC(cx);
+    buffer = nullptr;
+    GC(cx);
+
+    return true;
+}
+
+static void GC(JSContext* cx)
+{
+    JS_GC(cx);
+    JS_GC(cx); // Trigger another to wait for background finalization to end
+}
+
+static bool
+hasExpectedLength(JSContext* cx, JS::HandleObject obj, uint32_t* len)
+{
+    JS::RootedValue v(cx);
+    if (!JS_GetProperty(cx, obj, "byteLength", &v))
+        return false;
+    *len = v.toInt32();
+    return true;
+}
+
+END_TEST(testArrayBuffer_externalize)
--- a/js/src/jsapi-tests/tests.h
+++ b/js/src/jsapi-tests/tests.h
@@ -178,17 +178,17 @@ class JSAPITest
     }
 
 #define CHECK_NULL(actual) \
     do { \
         if (!checkNull(actual, #actual, __FILE__, __LINE__)) \
             return false; \
     } while (false)
 
-    bool checkSame(JS::Value actualArg, JS::Value expectedArg,
+    bool checkSame(const JS::Value& actualArg, const JS::Value& expectedArg,
                    const char* actualExpr, const char* expectedExpr,
                    const char* filename, int lineno) {
         bool same;
         JS::RootedValue actual(cx, actualArg), expected(cx, expectedArg);
         return (JS_SameValue(cx, actual, expected, &same) && same) ||
                fail(JSAPITestString("CHECK_SAME failed: expected JS_SameValue(cx, ") +
                     actualExpr + ", " + expectedExpr + "), got !JS_SameValue(cx, " +
                     jsvalToSource(actual) + ", " + jsvalToSource(expected) + ")", filename, lineno);
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -3206,17 +3206,17 @@ JS_SetAllNonReservedSlotsToUndefined(JSC
 
 JS_PUBLIC_API(Value)
 JS_GetReservedSlot(JSObject* obj, uint32_t index)
 {
     return obj->as<NativeObject>().getReservedSlot(index);
 }
 
 JS_PUBLIC_API(void)
-JS_SetReservedSlot(JSObject* obj, uint32_t index, Value value)
+JS_SetReservedSlot(JSObject* obj, uint32_t index, const Value& value)
 {
     obj->as<NativeObject>().setReservedSlot(index, value);
 }
 
 JS_PUBLIC_API(JSObject*)
 JS_NewArrayObject(JSContext* cx, const JS::HandleValueArray& contents)
 {
     MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
@@ -4539,17 +4539,17 @@ JS::CompileModule(JSContext* cx, const R
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
 
     module.set(frontend::CompileModule(cx, options, srcBuf));
     return !!module;
 }
 
 JS_PUBLIC_API(void)
-JS::SetModuleHostDefinedField(JSObject* module, JS::Value value)
+JS::SetModuleHostDefinedField(JSObject* module, const JS::Value& value)
 {
     module->as<ModuleObject>().setHostDefinedField(value);
 }
 
 JS_PUBLIC_API(JS::Value)
 JS::GetModuleHostDefinedField(JSObject* module)
 {
     return module->as<ModuleObject>().hostDefinedField();
@@ -6154,17 +6154,17 @@ JSErrorReport::initLinebuf(const char16_
 JS_PUBLIC_API(bool)
 JS_ThrowStopIteration(JSContext* cx)
 {
     AssertHeapIsIdle(cx);
     return ThrowStopIteration(cx);
 }
 
 JS_PUBLIC_API(bool)
-JS_IsStopIteration(Value v)
+JS_IsStopIteration(const Value& v)
 {
     return v.isObject() && v.toObject().is<StopIterationObject>();
 }
 
 extern MOZ_NEVER_INLINE JS_PUBLIC_API(void)
 JS_AbortIfWrongThread(JSContext* cx)
 {
     if (!CurrentThreadCanAccessRuntime(cx))
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -3442,16 +3442,35 @@ JS_NewArrayBufferWithExternalContents(JS
  * length set to 0 and its contents array cleared. The caller takes ownership
  * of the return value and must free it or transfer ownership via
  * JS_NewArrayBufferWithContents when done using it.
  */
 extern JS_PUBLIC_API(void*)
 JS_StealArrayBufferContents(JSContext* cx, JS::HandleObject obj);
 
 /**
+ * Returns a pointer to the ArrayBuffer |obj|'s data.  |obj| and its views will store and expose
+ * the data in the returned pointer: assigning into the returned pointer will affect values exposed
+ * by views of |obj| and vice versa.
+ *
+ * The caller must ultimately deallocate the returned pointer to avoid leaking.  The memory is
+ * *not* garbage-collected with |obj|.  These steps must be followed to deallocate:
+ *
+ * 1. The ArrayBuffer |obj| must be detached using JS_DetachArrayBuffer.
+ * 2. The returned pointer must be freed using JS_free.
+ *
+ * To perform step 1, callers *must* hold a reference to |obj| until they finish using the returned
+ * pointer.  They *must not* attempt to let |obj| be GC'd, then JS_free the pointer.
+ *
+ * If |obj| isn't an ArrayBuffer, this function returns null and reports an error.
+ */
+extern JS_PUBLIC_API(void*)
+JS_ExternalizeArrayBufferContents(JSContext* cx, JS::HandleObject obj);
+
+/**
  * Create a new mapped array buffer with the given memory mapped contents. It
  * must be legal to free the contents pointer by unmapping it. On success,
  * ownership is transferred to the new mapped array buffer.
  */
 extern JS_PUBLIC_API(JSObject*)
 JS_NewMappedArrayBufferWithContents(JSContext* cx, size_t nbytes, void* contents);
 
 /**
@@ -3470,17 +3489,17 @@ JS_CreateMappedArrayBufferContents(int f
  */
 extern JS_PUBLIC_API(void)
 JS_ReleaseMappedArrayBufferContents(void* contents, size_t length);
 
 extern JS_PUBLIC_API(JS::Value)
 JS_GetReservedSlot(JSObject* obj, uint32_t index);
 
 extern JS_PUBLIC_API(void)
-JS_SetReservedSlot(JSObject* obj, uint32_t index, JS::Value v);
+JS_SetReservedSlot(JSObject* obj, uint32_t index, const JS::Value& v);
 
 
 /************************************************************************/
 
 /*
  * Functions and scripts.
  */
 extern JS_PUBLIC_API(JSFunction*)
@@ -4257,17 +4276,17 @@ extern JS_PUBLIC_API(bool)
 CompileModule(JSContext* cx, const ReadOnlyCompileOptions& options,
               SourceBufferHolder& srcBuf, JS::MutableHandleObject moduleRecord);
 
 /**
  * Set the [[HostDefined]] field of a source text module record to the given
  * value.
  */
 extern JS_PUBLIC_API(void)
-SetModuleHostDefinedField(JSObject* module, JS::Value value);
+SetModuleHostDefinedField(JSObject* module, const JS::Value& value);
 
 /**
  * Get the [[HostDefined]] field of a source text module record.
  */
 extern JS_PUBLIC_API(JS::Value)
 GetModuleHostDefinedField(JSObject* module);
 
 /*
@@ -5621,17 +5640,17 @@ ExceptionStackOrNull(JS::HandleObject ob
 
 /*
  * Throws a StopIteration exception on cx.
  */
 extern JS_PUBLIC_API(bool)
 JS_ThrowStopIteration(JSContext* cx);
 
 extern JS_PUBLIC_API(bool)
-JS_IsStopIteration(JS::Value v);
+JS_IsStopIteration(const JS::Value& v);
 
 /**
  * A JS context always has an "owner thread". The owner thread is set when the
  * context is created (to the current thread) and practically all entry points
  * into the JS engine check that a context (or anything contained in the
  * context: runtime, compartment, object, etc) is only touched by its owner
  * thread. Embeddings may check this invariant outside the JS engine by calling
  * JS_AbortIfWrongThread (which will abort if not on the owner thread, even for
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -530,17 +530,17 @@ js::ToAtom(ExclusiveContext* cx, typenam
     }
     return atom;
 }
 
 template JSAtom*
 js::ToAtom<CanGC>(ExclusiveContext* cx, HandleValue v);
 
 template JSAtom*
-js::ToAtom<NoGC>(ExclusiveContext* cx, Value v);
+js::ToAtom<NoGC>(ExclusiveContext* cx, const Value& v);
 
 template<XDRMode mode>
 bool
 js::XDRAtom(XDRState<mode>* xdr, MutableHandleAtom atomp)
 {
     if (mode == XDR_ENCODE) {
         static_assert(JSString::MAX_LENGTH <= INT32_MAX, "String length must fit in 31 bits");
         uint32_t length = atomp->length();
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -495,17 +495,17 @@ struct JSContext : public js::ExclusiveC
 
     MOZ_MUST_USE
     bool getPendingException(JS::MutableHandleValue rval);
 
     bool isThrowingOutOfMemory();
     bool isThrowingDebuggeeWouldRun();
     bool isClosingGenerator();
 
-    void setPendingException(js::Value v);
+    void setPendingException(const js::Value& v);
 
     void clearPendingException() {
         throwing = false;
         overRecursed_ = false;
         unwrappedException_.setUndefined();
     }
 
     bool isThrowingOverRecursed() const { return throwing && overRecursed_; }
--- a/js/src/jscntxtinlines.h
+++ b/js/src/jscntxtinlines.h
@@ -364,17 +364,17 @@ inline LifoAlloc&
 ExclusiveContext::typeLifoAlloc()
 {
     return zone()->types.typeLifoAlloc;
 }
 
 }  /* namespace js */
 
 inline void
-JSContext::setPendingException(js::Value v)
+JSContext::setPendingException(const js::Value& v)
 {
     // overRecursed_ is set after the fact by ReportOverRecursed.
     this->overRecursed_ = false;
     this->throwing = true;
     this->unwrappedException_ = v;
     // We don't use assertSameCompartment here to allow
     // js::SetPendingExceptionCrossContext to work.
     MOZ_ASSERT_IF(v.isObject(), v.toObject().compartment() == compartment());
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -79,17 +79,17 @@ class CrossCompartmentKey
     using WrappedType = mozilla::Variant<
         JSObject*,
         JSString*,
         DebuggerAndScript,
         DebuggerAndObject>;
 
     explicit CrossCompartmentKey(JSObject* obj) : wrapped(obj) { MOZ_RELEASE_ASSERT(obj); }
     explicit CrossCompartmentKey(JSString* str) : wrapped(str) { MOZ_RELEASE_ASSERT(str); }
-    explicit CrossCompartmentKey(JS::Value v)
+    explicit CrossCompartmentKey(const JS::Value& v)
       : wrapped(v.isString() ? WrappedType(v.toString()) : WrappedType(&v.toObject()))
     {}
     explicit CrossCompartmentKey(NativeObject* debugger, JSObject* obj, DebuggerObjectKind kind)
       : wrapped(DebuggerAndObject(debugger, obj, kind))
     {
         MOZ_RELEASE_ASSERT(debugger);
         MOZ_RELEASE_ASSERT(obj);
     }
@@ -1036,17 +1036,17 @@ class MOZ_RAII AutoWrapperVector : publi
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     }
 
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 class MOZ_RAII AutoWrapperRooter : private JS::AutoGCRooter {
   public:
-    AutoWrapperRooter(JSContext* cx, WrapperValue v
+    AutoWrapperRooter(JSContext* cx, const WrapperValue& v
                       MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : JS::AutoGCRooter(cx, WRAPPER), value(v)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     }
 
     operator JSObject*() const {
         return value.get().toObjectOrNull();
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -2298,17 +2298,17 @@ js::ReportIncompatible(JSContext* cx, co
         }
     }
 }
 
 namespace JS {
 namespace detail {
 
 JS_PUBLIC_API(void)
-CheckIsValidConstructible(Value calleev)
+CheckIsValidConstructible(const Value& calleev)
 {
     JSObject* callee = &calleev.toObject();
     if (callee->is<JSFunction>())
         MOZ_ASSERT(callee->as<JSFunction>().isConstructor());
     else
         MOZ_ASSERT(callee->constructHook() != nullptr);
 }
 
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -810,15 +810,15 @@ fun_call(JSContext* cx, unsigned argc, V
 
 } /* namespace js */
 
 #ifdef DEBUG
 namespace JS {
 namespace detail {
 
 JS_PUBLIC_API(void)
-CheckIsValidConstructible(Value calleev);
+CheckIsValidConstructible(const Value& calleev);
 
 } // namespace detail
 } // namespace JS
 #endif
 
 #endif /* jsfun_h */
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -5544,17 +5544,18 @@ HeapStateToLabel(JS::HeapState heapState
     switch (heapState) {
       case JS::HeapState::MinorCollecting:
         return "js::Nursery::collect";
       case JS::HeapState::MajorCollecting:
         return "js::GCRuntime::collect";
       case JS::HeapState::Tracing:
         return "JS_IterateCompartments";
       case JS::HeapState::Idle:
-        MOZ_CRASH("Should never have an Idle heap state when pushing GC pseudo frames!");
+      case JS::HeapState::CycleCollecting:
+        MOZ_CRASH("Should never have an Idle or CC heap state when pushing GC pseudo frames!");
     }
     MOZ_ASSERT_UNREACHABLE("Should have exhausted every JS::HeapState variant!");
     return nullptr;
 }
 
 /* Start a new heap session. */
 AutoTraceSession::AutoTraceSession(JSRuntime* rt, JS::HeapState heapState)
   : lock(rt),
@@ -7044,16 +7045,37 @@ AutoAssertNoNurseryAlloc::AutoAssertNoNu
 {
     gc.disallowNurseryAlloc();
 }
 
 AutoAssertNoNurseryAlloc::~AutoAssertNoNurseryAlloc()
 {
     gc.allowNurseryAlloc();
 }
+
+JS::AutoEnterCycleCollection::AutoEnterCycleCollection(JSContext* cx)
+  : runtime(shadow::Runtime::asShadowRuntime(cx->runtime()))
+{
+    if (runtime) {
+        // Lock the helper thread state when changing the heap state in the
+        // presence of exclusive threads, to avoid racing with refillFreeList.
+        AutoLockHelperThreadState lock;
+        MOZ_ASSERT(runtime->heapState_ == HeapState::Idle);
+        runtime->heapState_ = HeapState::CycleCollecting;
+    }
+}
+
+JS::AutoEnterCycleCollection::~AutoEnterCycleCollection()
+{
+    if (runtime) {
+        AutoLockHelperThreadState lock;
+        MOZ_ASSERT(runtime->heapState_ == HeapState::CycleCollecting);
+        runtime->heapState_ = HeapState::Idle;
+    }
+}
 #endif
 
 JS::AutoAssertGCCallback::AutoAssertGCCallback(JSObject* obj)
   : AutoSuppressGCAnalysis()
 {
     MOZ_ASSERT(obj->runtimeFromMainThread()->isHeapCollecting());
 }
 
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -1569,18 +1569,19 @@ js::StringToNumber(ExclusiveContext* cx,
         return false;
 
     return linearStr->hasLatin1Chars()
            ? CharsToNumber(cx, linearStr->latin1Chars(nogc), str->length(), result)
            : CharsToNumber(cx, linearStr->twoByteChars(nogc), str->length(), result);
 }
 
 bool
-js::ToNumberSlow(ExclusiveContext* cx, Value v, double* out)
+js::ToNumberSlow(ExclusiveContext* cx, const Value& v_, double* out)
 {
+    RootedValue v(cx, v_);
     MOZ_ASSERT(!v.isNumber());
     goto skip_int_double;
     for (;;) {
         if (v.isNumber()) {
             *out = v.toNumber();
             return true;
         }
 
@@ -1607,30 +1608,28 @@ js::ToNumberSlow(ExclusiveContext* cx, V
             MOZ_ASSERT(v.isUndefined());
             *out = GenericNaN();
             return true;
         }
 
         if (!cx->isJSContext())
             return false;
 
-        RootedValue v2(cx, v);
-        if (!ToPrimitive(cx->asJSContext(), JSTYPE_NUMBER, &v2))
+        if (!ToPrimitive(cx->asJSContext(), JSTYPE_NUMBER, &v))
             return false;
-        v = v2;
         if (v.isObject())
             break;
     }
 
     *out = GenericNaN();
     return true;
 }
 
 JS_PUBLIC_API(bool)
-js::ToNumberSlow(JSContext* cx, Value v, double* out)
+js::ToNumberSlow(JSContext* cx, const Value& v, double* out)
 {
     return ToNumberSlow(static_cast<ExclusiveContext*>(cx), v, out);
 }
 
 /*
  * Convert a value to an int8_t, according to the WebIDL rules for byte
  * conversion. Return converted value in *out on success, false on failure.
  */
--- a/js/src/jsnum.h
+++ b/js/src/jsnum.h
@@ -168,17 +168,17 @@ StringToNumber(ExclusiveContext* cx, JSS
 
 /* ES5 9.3 ToNumber, overwriting *vp with the appropriate number value. */
 MOZ_ALWAYS_INLINE MOZ_MUST_USE bool
 ToNumber(JSContext* cx, JS::MutableHandleValue vp)
 {
     if (vp.isNumber())
         return true;
     double d;
-    extern JS_PUBLIC_API(bool) ToNumberSlow(JSContext* cx, Value v, double* dp);
+    extern JS_PUBLIC_API(bool) ToNumberSlow(JSContext* cx, const Value& v, double* dp);
     if (!ToNumberSlow(cx, vp, &d))
         return false;
 
     vp.setNumber(d);
     return true;
 }
 
 MOZ_MUST_USE bool
@@ -253,17 +253,17 @@ ToInteger(JSContext* cx, HandleValue v, 
 {
     if (v.isInt32()) {
         *dp = v.toInt32();
         return true;
     }
     if (v.isDouble()) {
         *dp = v.toDouble();
     } else {
-        extern JS_PUBLIC_API(bool) ToNumberSlow(JSContext* cx, Value v, double* dp);
+        extern JS_PUBLIC_API(bool) ToNumberSlow(JSContext* cx, const Value& v, double* dp);
         if (!ToNumberSlow(cx, v, dp))
             return false;
     }
     *dp = JS::ToInteger(*dp);
     return true;
 }
 
 /* ES6 7.1.15 ToLength, but clamped to the [0,2^32-2] range.  If the
@@ -330,17 +330,17 @@ SafeMul(int32_t one, int32_t two, int32_
 #else
     *res = uint32_t(one) * uint32_t(two);
     int64_t ores = (int64_t)one * (int64_t)two;
     return ores == (int64_t)*res;
 #endif
 }
 
 extern MOZ_MUST_USE bool
-ToNumberSlow(ExclusiveContext* cx, Value v, double* dp);
+ToNumberSlow(ExclusiveContext* cx, const Value& v, double* dp);
 
 // Variant of ToNumber which takes an ExclusiveContext instead of a JSContext.
 // ToNumber is part of the API and can't use ExclusiveContext directly.
 MOZ_ALWAYS_INLINE MOZ_MUST_USE bool
 ToNumber(ExclusiveContext* cx, const Value& v, double* out)
 {
     if (v.isNumber()) {
         *out = v.toNumber();
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -615,17 +615,17 @@ HasObjectValueOf(JSObject* obj, JSContex
             return false;
     }
 
     return IsNativeFunction(v, obj_valueOf);
 }
 
 /* ES6 draft rev 28 (2014 Oct 14) 7.1.14 */
 inline bool
-ToPropertyKey(JSContext* cx, Value argument, MutableHandleId result)
+ToPropertyKey(JSContext* cx, const Value& argument, MutableHandleId result)
 {
     // Steps 1-2.
     RootedValue key(cx, argument);
     if (!ToPrimitive(cx, JSTYPE_STRING, &key))
         return false;
 
     // Steps 3-4.
     return ValueToId<CanGC>(cx, key, result);
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -588,17 +588,17 @@ Str(JSContext* cx, const Value& v, Strin
     if (!IsArray(cx, obj, &isArray))
         return false;
 
     return isArray ? JA(cx, obj, scx) : JO(cx, obj, scx);
 }
 
 /* ES6 24.3.2. */
 bool
-js::Stringify(JSContext* cx, MutableHandleValue vp, JSObject* replacer_, Value space_,
+js::Stringify(JSContext* cx, MutableHandleValue vp, JSObject* replacer_, const Value& space_,
               StringBuffer& sb, StringifyBehavior stringifyBehavior)
 {
     RootedObject replacer(cx, replacer_);
     RootedValue space(cx, space_);
 
     MOZ_ASSERT_IF(stringifyBehavior == StringifyBehavior::RestrictedSafe, space.isNull());
     MOZ_ASSERT_IF(stringifyBehavior == StringifyBehavior::RestrictedSafe, vp.isObject());
     /**
--- a/js/src/json.h
+++ b/js/src/json.h
@@ -26,17 +26,17 @@ enum class StringifyBehavior {
 
 /**
  * If maybeSafely is true, Stringify will attempt to assert the API requirements
  * of JS::ToJSONMaybeSafely as it traverses the graph, and will not try to
  * invoke .toJSON on things as it goes.
  */
 extern bool
 Stringify(JSContext* cx, js::MutableHandleValue vp, JSObject* replacer,
-          Value space, StringBuffer& sb, StringifyBehavior stringifyBehavior);
+          const Value& space, StringBuffer& sb, StringifyBehavior stringifyBehavior);
 
 template <typename CharT>
 extern bool
 ParseJSONWithReviver(JSContext* cx, const mozilla::Range<const CharT> chars,
                      HandleValue reviver, MutableHandleValue vp);
 
 } // namespace js
 
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -1405,17 +1405,17 @@ ExpressionDecompiler::getOutput(char** r
     js_memcpy(*res, sprinter.stringAt(0), len);
     (*res)[len] = 0;
     return true;
 }
 
 }  // anonymous namespace
 
 static bool
-FindStartPC(JSContext* cx, const FrameIter& iter, int spindex, int skipStackHits, Value v,
+FindStartPC(JSContext* cx, const FrameIter& iter, int spindex, int skipStackHits, const Value& v,
             jsbytecode** valuepc)
 {
     jsbytecode* current = *valuepc;
 
     if (spindex == JSDVG_IGNORE_STACK)
         return true;
 
     /*
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -125,63 +125,86 @@ namespace js {
 namespace gc {
 class AutoTraceSession;
 class StoreBuffer;
 } // namespace gc
 } // namespace js
 
 namespace JS {
 
+class JS_PUBLIC_API(AutoEnterCycleCollection);
 struct PropertyDescriptor;
 
 typedef void (*OffThreadCompileCallback)(void* token, void* callbackData);
 
 enum class HeapState {
     Idle,             // doing nothing with the GC heap
     Tracing,          // tracing the GC heap without collecting, e.g. IterateCompartments()
     MajorCollecting,  // doing a GC of the major heap
-    MinorCollecting   // doing a GC of the minor heap (nursery)
+    MinorCollecting,  // doing a GC of the minor heap (nursery)
+    CycleCollecting   // in the "Unlink" phase of cycle collection
 };
 
 namespace shadow {
 
 struct Runtime
 {
   protected:
     // Allow inlining of heapState checks.
     friend class js::gc::AutoTraceSession;
+    friend class JS::AutoEnterCycleCollection;
     JS::HeapState heapState_;
 
     js::gc::StoreBuffer* gcStoreBufferPtr_;
 
   public:
     Runtime()
       : heapState_(JS::HeapState::Idle)
       , gcStoreBufferPtr_(nullptr)
     {}
 
     bool isHeapBusy() const { return heapState_ != JS::HeapState::Idle; }
     bool isHeapMajorCollecting() const { return heapState_ == JS::HeapState::MajorCollecting; }
     bool isHeapMinorCollecting() const { return heapState_ == JS::HeapState::MinorCollecting; }
     bool isHeapCollecting() const { return isHeapMinorCollecting() || isHeapMajorCollecting(); }
+    bool isCycleCollecting() const {
+        return heapState_ == JS::HeapState::CycleCollecting;
+    }
 
     js::gc::StoreBuffer* gcStoreBufferPtr() { return gcStoreBufferPtr_; }
 
     static JS::shadow::Runtime* asShadowRuntime(JSRuntime* rt) {
         return reinterpret_cast<JS::shadow::Runtime*>(rt);
     }
 
   protected:
     void setGCStoreBufferPtr(js::gc::StoreBuffer* storeBuffer) {
         gcStoreBufferPtr_ = storeBuffer;
     }
 };
 
 } /* namespace shadow */
 
+// Decorates the Unlinking phase of CycleCollection so that accidental use
+// of barriered accessors results in assertions instead of leaks.
+class MOZ_STACK_CLASS JS_PUBLIC_API(AutoEnterCycleCollection)
+{
+#ifdef DEBUG
+    shadow::Runtime* runtime;
+
+  public:
+    explicit AutoEnterCycleCollection(JSContext* cx);
+    ~AutoEnterCycleCollection();
+#else
+  public:
+    explicit AutoEnterCycleCollection(JSContext* cx) {}
+    ~AutoEnterCycleCollection() {}
+#endif
+};
+
 class JS_PUBLIC_API(AutoGCRooter)
 {
   public:
     AutoGCRooter(JSContext* cx, ptrdiff_t tag);
     AutoGCRooter(JS::RootingContext* cx, ptrdiff_t tag);
 
     ~AutoGCRooter() {
         MOZ_ASSERT(this == *stackTop);
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -3051,17 +3051,17 @@ js::ToStringSlow(ExclusiveContext* cx, t
     }
     return str;
 }
 
 template JSString*
 js::ToStringSlow<CanGC>(ExclusiveContext* cx, HandleValue arg);
 
 template JSString*
-js::ToStringSlow<NoGC>(ExclusiveContext* cx, Value arg);
+js::ToStringSlow<NoGC>(ExclusiveContext* cx, const Value& arg);
 
 JS_PUBLIC_API(JSString*)
 js::ToStringSlow(JSContext* cx, HandleValue v)
 {
     return ToStringSlow<CanGC>(cx, v);
 }
 
 static JSString*
--- a/js/src/jsutil.h
+++ b/js/src/jsutil.h
@@ -301,17 +301,17 @@ ClearAllBitArrayElements(size_t* array, 
 
 namespace mozilla {
 
 /**
  * Set the first |aNElem| T elements in |aDst| to |aSrc|.
  */
 template<typename T>
 static MOZ_ALWAYS_INLINE void
-PodSet(T* aDst, T aSrc, size_t aNElem)
+PodSet(T* aDst, const T& aSrc, size_t aNElem)
 {
     for (const T* dstend = aDst + aNElem; aDst < dstend; ++aDst)
         *aDst = aSrc;
 }
 
 } /* namespace mozilla */
 
 /*
--- a/js/src/jswatchpoint.h
+++ b/js/src/jswatchpoint.h
@@ -27,17 +27,17 @@ struct WatchKey {
     PreBarrieredId id;
 
     bool operator!=(const WatchKey& other) const {
         return object != other.object || id != other.id;
     }
 };
 
 typedef bool
-(* JSWatchPointHandler)(JSContext* cx, JSObject* obj, jsid id, JS::Value old,
+(* JSWatchPointHandler)(JSContext* cx, JSObject* obj, jsid id, const JS::Value& old,
                         JS::Value* newp, void* closure);
 
 struct Watchpoint {
     JSWatchPointHandler handler;
     PreBarrieredObject closure;  /* This is always marked in minor GCs and so doesn't require a postbarrier. */
     bool held;  /* true if currently running handler */
     Watchpoint(JSWatchPointHandler handler, JSObject* closure, bool held)
       : handler(handler), closure(closure), held(held) {}
--- a/js/src/jswrapper.h
+++ b/js/src/jswrapper.h
@@ -61,17 +61,17 @@ class JS_FRIEND_API(Wrapper) : public Ba
 
   public:
     explicit constexpr Wrapper(unsigned aFlags, bool aHasPrototype = false,
                                    bool aHasSecurityPolicy = false)
       : BaseProxyHandler(&family, aHasPrototype, aHasSecurityPolicy),
         mFlags(aFlags)
     { }
 
-    virtual bool finalizeInBackground(Value priv) const override;
+    virtual bool finalizeInBackground(const Value& priv) const override;
 
     /* Standard internal methods. */
     virtual bool getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
                                           MutableHandle<PropertyDescriptor> desc) const override;
     virtual bool defineProperty(JSContext* cx, HandleObject proxy, HandleId id,
                                 Handle<PropertyDescriptor> desc,
                                 ObjectOpResult& result) const override;
     virtual bool ownPropertyKeys(JSContext* cx, HandleObject proxy,
--- a/js/src/perf/jsperf.cpp
+++ b/js/src/perf/jsperf.cpp
@@ -271,17 +271,17 @@ RegisterPerfMeasurement(JSContext* cx, H
         !JS_FreezeObject(cx, ctor)) {
         return 0;
     }
 
     return prototype;
 }
 
 PerfMeasurement*
-ExtractPerfMeasurement(Value wrapper)
+ExtractPerfMeasurement(const Value& wrapper)
 {
     if (wrapper.isPrimitive())
         return 0;
 
     // This is what JS_GetInstancePrivate does internally.  We can't
     // call JS_anything from here, because we don't have a JSContext.
     JSObject* obj = wrapper.toObjectOrNull();
     if (obj->getClass() != js::Valueify(&pm_class))
--- a/js/src/perf/jsperf.h
+++ b/js/src/perf/jsperf.h
@@ -121,13 +121,13 @@ extern JS_FRIEND_API(JSObject*)
     RegisterPerfMeasurement(JSContext* cx, JS::HandleObject global);
 
 /*
  * Given a Value which contains an instance of the aforementioned
  * wrapper class, extract the C++ object.  Returns nullptr if the
  * Value is not an instance of the wrapper.
  */
 extern JS_FRIEND_API(PerfMeasurement*)
-    ExtractPerfMeasurement(Value wrapper);
+    ExtractPerfMeasurement(const Value& wrapper);
 
 } // namespace JS
 
 #endif /* perf_jsperf_h */
--- a/js/src/proxy/Proxy.cpp
+++ b/js/src/proxy/Proxy.cpp
@@ -265,17 +265,17 @@ Proxy::hasOwn(JSContext* cx, HandleObjec
     *bp = false; // default result if we refuse to perform this action
     AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true);
     if (!policy.allowed())
         return policy.returnValue();
     return handler->hasOwn(cx, proxy, id, bp);
 }
 
 static Value
-ValueToWindowProxyIfWindow(Value v)
+ValueToWindowProxyIfWindow(const Value& v)
 {
     if (v.isObject())
         return ObjectValue(*ToWindowProxyIfWindow(&v.toObject()));
     return v;
 }
 
 bool
 Proxy::get(JSContext* cx, HandleObject proxy, HandleValue receiver_, HandleId id,
@@ -769,17 +769,17 @@ js::NewProxyObject(JSContext* cx, const 
         MOZ_ASSERT(!proto_);
         proto_ = TaggedProto::LazyProto;
     }
 
     return ProxyObject::New(cx, handler, priv, TaggedProto(proto_), options);
 }
 
 void
-ProxyObject::renew(JSContext* cx, const BaseProxyHandler* handler, Value priv)
+ProxyObject::renew(JSContext* cx, const BaseProxyHandler* handler, const Value& priv)
 {
     MOZ_ASSERT(!IsInsideNursery(this));
     MOZ_ASSERT_IF(IsCrossCompartmentWrapper(this), IsDeadProxyObject(this));
     MOZ_ASSERT(getClass() == &ProxyObject::proxyClass);
     MOZ_ASSERT(!IsWindowProxy(this));
     MOZ_ASSERT(hasDynamicPrototype());
 
     setHandler(handler);
--- a/js/src/proxy/Wrapper.cpp
+++ b/js/src/proxy/Wrapper.cpp
@@ -16,17 +16,17 @@
 
 #include "jsobjinlines.h"
 
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 
 bool
-Wrapper::finalizeInBackground(Value priv) const
+Wrapper::finalizeInBackground(const Value& priv) const
 {
     if (!priv.isObject())
         return true;
 
     /*
      * Make the 'background-finalized-ness' of the wrapper the same as the
      * wrapped object, to allow transplanting between them.
      *
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -2206,20 +2206,18 @@ AssertEq(JSContext* cx, unsigned argc, V
         }
         return false;
     }
     args.rval().setUndefined();
     return true;
 }
 
 static JSScript*
-ValueToScript(JSContext* cx, Value vArg, JSFunction** funp = nullptr)
-{
-    RootedValue v(cx, vArg);
-
+ValueToScript(JSContext* cx, HandleValue v, JSFunction** funp = nullptr)
+{
     if (v.isString()) {
         // To convert a string to a script, compile it. Parse it as an ES6 Program.
         RootedLinearString linearStr(cx, StringToLinearString(cx, v.toString()));
         if (!linearStr)
             return nullptr;
         size_t len = GetLinearStringLength(linearStr);
         AutoStableStringChars linearChars(cx);
         if (!linearChars.initTwoByte(cx, linearStr))
@@ -2270,17 +2268,17 @@ GetTopScript(JSContext* cx)
 
 static bool
 GetScriptAndPCArgs(JSContext* cx, unsigned argc, Value* argv, MutableHandleScript scriptp,
                    int32_t* ip)
 {
     RootedScript script(cx, GetTopScript(cx));
     *ip = 0;
     if (argc != 0) {
-        Value v = argv[0];
+        RootedValue v(cx, argv[0]);
         unsigned intarg = 0;
         if (v.isObject() &&
             JS_GetClass(&v.toObject()) == Jsvalify(&JSFunction::class_)) {
             script = ValueToScript(cx, v);
             if (!script)
                 return false;
             intarg++;
         }
@@ -6147,17 +6145,17 @@ DefineConsole(JSContext* cx, HandleObjec
 #endif
 
 #undef PROFILING_FUNCTION_COUNT
 #undef CALLGRIND_FUNCTION_COUNT
 #undef VTUNE_FUNCTION_COUNT
 #undef EXTERNAL_FUNCTION_COUNT
 
 static bool
-PrintHelpString(JSContext* cx, Value v)
+PrintHelpString(JSContext* cx, HandleValue v)
 {
     JSString* str = v.toString();
     MOZ_ASSERT(gOutFile->isOpen());
 
     JSLinearString* linear = str->ensureLinear(cx);
     if (!linear)
         return false;
 
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -311,31 +311,31 @@ ArrayBufferObject::detach(JSContext* cx,
             MOZ_ASSERT(buffer->firstView()->is<InlineTransparentTypedObject>());
         } else {
             NoteViewBufferWasDetached(buffer->firstView(), newContents, cx);
             buffer->setFirstView(nullptr);
         }
     }
 
     if (newContents.data() != buffer->dataPointer())
-        buffer->setNewOwnedData(cx->runtime()->defaultFreeOp(), newContents);
+        buffer->setNewData(cx->runtime()->defaultFreeOp(), newContents, OwnsData);
 
     buffer->setByteLength(0);
     buffer->setIsDetached();
 }
 
 void
-ArrayBufferObject::setNewOwnedData(FreeOp* fop, BufferContents newContents)
+ArrayBufferObject::setNewData(FreeOp* fop, BufferContents newContents, OwnsState ownsState)
 {
     if (ownsData()) {
         MOZ_ASSERT(newContents.data() != dataPointer());
         releaseData(fop);
     }
 
-    setDataPointer(newContents, OwnsData);
+    setDataPointer(newContents, ownsState);
 }
 
 // This is called *only* from changeContents(), below.
 // By construction, every view parameter will be mapping unshared memory (an ArrayBuffer).
 // Hence no reason to worry about shared memory here.
 
 void
 ArrayBufferObject::changeViewContents(JSContext* cx, ArrayBufferViewObject* view,
@@ -356,24 +356,25 @@ ArrayBufferObject::changeViewContents(JS
 
     // Notify compiled jit code that the base pointer has moved.
     MarkObjectStateChange(cx, view);
 }
 
 // BufferContents is specific to ArrayBuffer, hence it will not represent shared memory.
 
 void
-ArrayBufferObject::changeContents(JSContext* cx, BufferContents newContents)
+ArrayBufferObject::changeContents(JSContext* cx, BufferContents newContents,
+                                  OwnsState ownsState)
 {
     MOZ_RELEASE_ASSERT(!isWasm());
     MOZ_ASSERT(!forInlineTypedObject());
 
     // Change buffer contents.
     uint8_t* oldDataPointer = dataPointer();
-    setNewOwnedData(cx->runtime()->defaultFreeOp(), newContents);
+    setNewData(cx->runtime()->defaultFreeOp(), newContents, ownsState);
 
     // Update all views.
     auto& innerViews = cx->compartment()->innerViews;
     if (InnerViewTable::ViewVector* views = innerViews.maybeViewsUnbarriered(this)) {
         for (size_t i = 0; i < views->length(); i++)
             changeViewContents(cx, (*views)[i], oldDataPointer, newContents);
     }
     if (firstView())
@@ -736,17 +737,17 @@ ArrayBufferObject::prepareForAsmJS(JSCon
             return false;
         }
 
         void* data = wasmBuf->dataPointer();
         memcpy(data, buffer->dataPointer(), length);
 
         // Swap the new elements into the ArrayBufferObject. Mark the
         // ArrayBufferObject so we don't do this again.
-        buffer->changeContents(cx, BufferContents::create<WASM>(data));
+        buffer->changeContents(cx, BufferContents::create<WASM>(data), OwnsData);
         buffer->setIsPreparedForAsmJS();
         MOZ_ASSERT(data == buffer->dataPointer());
         cx->zone()->updateMallocCounter(wasmBuf->mappedSize());
         return true;
     }
 
     if (!buffer->isWasm() && buffer->isPreparedForAsmJS())
         return true;
@@ -755,17 +756,17 @@ ArrayBufferObject::prepareForAsmJS(JSCon
     if (buffer->isWasm())
         return false;
 
     if (!buffer->ownsData()) {
         BufferContents contents = AllocateArrayBufferContents(cx, buffer->byteLength());
         if (!contents)
             return false;
         memcpy(contents.data(), buffer->dataPointer(), buffer->byteLength());
-        buffer->changeContents(cx, contents);
+        buffer->changeContents(cx, contents, OwnsData);
     }
 
     buffer->setIsPreparedForAsmJS();
     return true;
 }
 
 ArrayBufferObject::BufferContents
 ArrayBufferObject::createMappedContents(int fd, size_t offset, size_t length)
@@ -1092,16 +1093,42 @@ ArrayBufferObject::createDataViewForThis
 bool
 ArrayBufferObject::createDataViewForThis(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<IsArrayBuffer, createDataViewForThisImpl>(cx, args);
 }
 
 /* static */ ArrayBufferObject::BufferContents
+ArrayBufferObject::externalizeContents(JSContext* cx, Handle<ArrayBufferObject*> buffer,
+                                       bool hasStealableContents)
+{
+    MOZ_ASSERT(buffer->isPlain(), "Only support doing this on plain ABOs");
+    MOZ_ASSERT(!buffer->isDetached(), "must have contents to externalize");
+    MOZ_ASSERT_IF(hasStealableContents, buffer->hasStealableContents());
+
+    BufferContents contents(buffer->dataPointer(), buffer->bufferKind());
+
+    if (hasStealableContents) {
+        buffer->setOwnsData(DoesntOwnData);
+        return contents;
+    }
+
+    // Create a new chunk of memory to return since we cannot steal the
+    // existing contents away from the buffer.
+    BufferContents newContents = AllocateArrayBufferContents(cx, buffer->byteLength());
+    if (!newContents)
+        return BufferContents::createPlain(nullptr);
+    memcpy(newContents.data(), contents.data(), buffer->byteLength());
+    buffer->changeContents(cx, newContents, DoesntOwnData);
+
+    return newContents;
+}
+
+/* static */ ArrayBufferObject::BufferContents
 ArrayBufferObject::stealContents(JSContext* cx, Handle<ArrayBufferObject*> buffer,
                                  bool hasStealableContents)
 {
     // While wasm buffers cannot generally be transferred by content, the
     // stealContents() is used internally by the impl of memory growth.
     MOZ_ASSERT_IF(hasStealableContents, buffer->hasStealableContents() ||
                                         (buffer->isWasm() && !buffer->isPreparedForAsmJS()));
     assertSameCompartment(cx, buffer);
@@ -1651,16 +1678,48 @@ JS_FRIEND_API(JSObject*)
 js::UnwrapSharedArrayBuffer(JSObject* obj)
 {
     if (JSObject* unwrapped = CheckedUnwrap(obj))
         return unwrapped->is<SharedArrayBufferObject>() ? unwrapped : nullptr;
     return nullptr;
 }
 
 JS_PUBLIC_API(void*)
+JS_ExternalizeArrayBufferContents(JSContext* cx, HandleObject obj)
+{
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+    assertSameCompartment(cx, obj);
+
+    if (!obj->is<ArrayBufferObject>()) {
+        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
+        return nullptr;
+    }
+
+    Handle<ArrayBufferObject*> buffer = obj.as<ArrayBufferObject>();
+    if (!buffer->isPlain()) {
+        // This operation isn't supported on mapped or wsm ArrayBufferObjects.
+        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
+        return nullptr;
+    }
+    if (buffer->isDetached()) {
+        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
+        return nullptr;
+    }
+
+    // The caller assumes that a plain malloc'd buffer is returned.
+    // hasStealableContents is true for mapped buffers, so we must additionally
+    // require that the buffer is plain. In the future, we could consider
+    // returning something that handles releasing the memory.
+    bool hasStealableContents = buffer->hasStealableContents();
+
+    return ArrayBufferObject::externalizeContents(cx, buffer, hasStealableContents).data();
+}
+
+JS_PUBLIC_API(void*)
 JS_StealArrayBufferContents(JSContext* cx, HandleObject objArg)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, objArg);
 
     JSObject* obj = CheckedUnwrap(objArg);
     if (!obj)
--- a/js/src/vm/ArrayBufferObject.h
+++ b/js/src/vm/ArrayBufferObject.h
@@ -271,16 +271,19 @@ class ArrayBufferObject : public ArrayBu
 
     static void copyData(Handle<ArrayBufferObject*> toBuffer,
                          Handle<ArrayBufferObject*> fromBuffer,
                          uint32_t fromIndex, uint32_t count);
 
     static void trace(JSTracer* trc, JSObject* obj);
     static void objectMoved(JSObject* obj, const JSObject* old);
 
+    static BufferContents externalizeContents(JSContext* cx,
+                                              Handle<ArrayBufferObject*> buffer,
+                                              bool hasStealableContents);
     static BufferContents stealContents(JSContext* cx,
                                         Handle<ArrayBufferObject*> buffer,
                                         bool hasStealableContents);
 
     bool hasStealableContents() const {
         // Inline elements strictly adhere to the corresponding buffer.
         return ownsData() && !isPreparedForAsmJS() && !isWasm();
     }
@@ -292,18 +295,18 @@ class ArrayBufferObject : public ArrayBu
     // later views are (weakly) stored in the compartment's InnerViewTable
     // below. Buffers usually only have one view, so this slot optimizes for
     // the common case. Avoiding entries in the InnerViewTable saves memory and
     // non-incrementalized sweep time.
     ArrayBufferViewObject* firstView();
 
     bool addView(JSContext* cx, JSObject* view);
 
-    void setNewOwnedData(FreeOp* fop, BufferContents newContents);
-    void changeContents(JSContext* cx, BufferContents newContents);
+    void setNewData(FreeOp* fop, BufferContents newContents, OwnsState ownsState);
+    void changeContents(JSContext* cx, BufferContents newContents, OwnsState ownsState);
 
     // Detach this buffer from its original memory.  (This necessarily makes
     // views of this buffer unusable for modifying that original memory.)
     static void
     detach(JSContext* cx, Handle<ArrayBufferObject*> buffer, BufferContents newContents);
 
   private:
     void changeViewContents(JSContext* cx, ArrayBufferViewObject* view,
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -1439,17 +1439,17 @@ Debugger::resultToCompletion(JSContext* 
         cx->clearPendingException();
     } else {
         *status = JSTRAP_ERROR;
         value.setUndefined();
     }
 }
 
 bool
-Debugger::newCompletionValue(JSContext* cx, JSTrapStatus status, Value value_,
+Debugger::newCompletionValue(JSContext* cx, JSTrapStatus status, const Value& value_,
                              MutableHandleValue result)
 {
     /*
      * We must be in the debugger's compartment, since that's where we want
      * to construct the completion value.
      */
     assertSameCompartment(cx, object.get());
     assertSameCompartment(cx, value_);
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -1014,17 +1014,17 @@ class Debugger : private mozilla::Linked
     static void resultToCompletion(JSContext* cx, bool ok, const Value& rv,
                                    JSTrapStatus* status, MutableHandleValue value);
 
     /*
      * Set |*result| to a JavaScript completion value corresponding to |status|
      * and |value|. |value| should be the return value or exception value, not
      * wrapped as a debuggee value. |cx| must be in the debugger compartment.
      */
-    MOZ_MUST_USE bool newCompletionValue(JSContext* cx, JSTrapStatus status, Value value,
+    MOZ_MUST_USE bool newCompletionValue(JSContext* cx, JSTrapStatus status, const Value& value,
                                          MutableHandleValue result);
 
     /*
      * Precondition: we are in the debuggee compartment (ac is entered) and ok
      * is true if the operation in the debuggee compartment succeeded, false on
      * error or exception.
      *
      * Postcondition: we are in the debugger compartment, having called
--- a/js/src/vm/EnvironmentObject.cpp
+++ b/js/src/vm/EnvironmentObject.cpp
@@ -1711,17 +1711,17 @@ class DebugEnvironmentProxyHandler : pub
      * initialized by JSOP_FUNCTIONTHIS, the this-binding will be |undefined|.
      * In that case, we have to call createMissingThis to initialize the
      * this-binding.
      *
      * Note that an |undefined| this-binding is perfectly valid in strict-mode
      * code, but that's fine: createMissingThis will do the right thing in that
      * case.
      */
-    static bool isMaybeUninitializedThisValue(JSContext* cx, jsid id, Value v)
+    static bool isMaybeUninitializedThisValue(JSContext* cx, jsid id, const Value& v)
     {
         return isThis(cx, id) && v.isUndefined();
     }
 
     /*
      * Create a missing arguments object. If the function returns true but
      * argsObj is null, it means the env is dead.
      */
--- a/js/src/vm/GeneratorObject.h
+++ b/js/src/vm/GeneratorObject.h
@@ -111,17 +111,17 @@ class GeneratorObject : public NativeObj
     }
 
     bool isConstructing() const {
         return getFixedSlot(NEWTARGET_SLOT).isObject();
     }
     const Value& newTarget() const {
         return getFixedSlot(NEWTARGET_SLOT);
     }
-    void setNewTarget(Value newTarget) {
+    void setNewTarget(const Value& newTarget) {
         setFixedSlot(NEWTARGET_SLOT, newTarget);
     }
 
 
     // The yield index slot is abused for a few purposes.  It's undefined if
     // it hasn't been set yet (before the initial yield), and null if the
     // generator is closed. If the generator is running, the yield index is
     // YIELD_INDEX_RUNNING. If the generator is in that bizarre "closing"
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -404,17 +404,17 @@ GlobalObject::getOrCreateEval(JSContext*
 {
     if (!global->getOrCreateObjectPrototype(cx))
         return false;
     eval.set(&global->getSlot(EVAL).toObject());
     return true;
 }
 
 bool
-GlobalObject::valueIsEval(Value val)
+GlobalObject::valueIsEval(const Value& val)
 {
     Value eval = getSlot(EVAL);
     return eval.isObject() && eval == val;
 }
 
 /* static */ bool
 GlobalObject::initStandardClasses(JSContext* cx, Handle<GlobalObject*> global)
 {
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -700,17 +700,17 @@ class GlobalObject : public NativeObject
         //return warnOnceAbout(cx, obj, WARN_WATCH_DEPRECATED, JSMSG_OBJECT_WATCH_DEPRECATED);
         return true;
     }
 
     static bool getOrCreateEval(JSContext* cx, Handle<GlobalObject*> global,
                                 MutableHandleObject eval);
 
     // Infallibly test whether the given value is the eval function for this global.
-    bool valueIsEval(Value val);
+    bool valueIsEval(const Value& val);
 
     // Implemented in jsiter.cpp.
     static bool initIteratorProto(JSContext* cx, Handle<GlobalObject*> global);
     static bool initArrayIteratorProto(JSContext* cx, Handle<GlobalObject*> global);
     static bool initStringIteratorProto(JSContext* cx, Handle<GlobalObject*> global);
 
     // Implemented in vm/GeneratorObject.cpp.
     static bool initLegacyGeneratorProto(JSContext* cx, Handle<GlobalObject*> global);
--- a/js/src/vm/Interpreter.h
+++ b/js/src/vm/Interpreter.h
@@ -228,17 +228,17 @@ class RunState
     InvokeState* asInvoke() const {
         MOZ_ASSERT(isInvoke());
         return (InvokeState*)this;
     }
 
     JS::HandleScript script() const { return script_; }
 
     virtual InterpreterFrame* pushInterpreterFrame(JSContext* cx) = 0;
-    virtual void setReturnValue(Value v) = 0;
+    virtual void setReturnValue(const Value& v) = 0;
 
     bool maybeCreateThisForConstructor(JSContext* cx);
 
   private:
     RunState(const RunState& other) = delete;
     RunState(const ExecuteState& other) = delete;
     RunState(const InvokeState& other) = delete;
     void operator=(const RunState& other) = delete;
@@ -264,17 +264,17 @@ class ExecuteState : public RunState
     { }
 
     Value newTarget() { return newTargetValue_; }
     JSObject* environmentChain() const { return envChain_; }
     bool isDebuggerEval() const { return !!evalInFrame_; }
 
     virtual InterpreterFrame* pushInterpreterFrame(JSContext* cx);
 
-    virtual void setReturnValue(Value v) {
+    virtual void setReturnValue(const Value& v) {
         if (result_)
             *result_ = v;
     }
 };
 
 // Data to invoke a function.
 class InvokeState final : public RunState
 {
@@ -293,17 +293,17 @@ class InvokeState final : public RunStat
     bool createSingleton() const { return createSingleton_; }
     void setCreateSingleton() { createSingleton_ = true; }
 
     bool constructing() const { return construct_; }
     const CallArgs& args() const { return args_; }
 
     virtual InterpreterFrame* pushInterpreterFrame(JSContext* cx);
 
-    virtual void setReturnValue(Value v) {
+    virtual void setReturnValue(const Value& v) {
         args_.rval().set(v);
     }
 };
 
 extern bool
 RunScript(JSContext* cx, RunState& state);
 
 extern bool
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -1004,17 +1004,17 @@ js::NativeLookupOwnProperty(ExclusiveCon
     return LookupOwnPropertyInline<allowGC>(cx, obj, id, propp, &done);
 }
 
 template bool
 js::NativeLookupOwnProperty<CanGC>(ExclusiveContext* cx, HandleNativeObject obj, HandleId id,
                                    MutableHandleShape propp);
 
 template bool
-js::NativeLookupOwnProperty<NoGC>(ExclusiveContext* cx, NativeObject* obj, jsid id,
+js::NativeLookupOwnProperty<NoGC>(ExclusiveContext* cx, NativeObject* const& obj, const jsid& id,
                                   FakeMutableHandle<Shape*> propp);
 
 /*** [[DefineOwnProperty]] ***********************************************************************/
 
 static inline bool
 CallAddPropertyHook(ExclusiveContext* cx, HandleNativeObject obj, HandleShape shape,
                     HandleValue value)
 {
@@ -1956,17 +1956,17 @@ GetNonexistentProperty(JSContext* cx, Ha
     // Ok, bad undefined property reference: whine about it.
     RootedValue val(cx, IdToValue(id));
     return ReportValueErrorFlags(cx, flags, JSMSG_UNDEFINED_PROP, JSDVG_IGNORE_STACK, val,
                                     nullptr, nullptr, nullptr);
 }
 
 /* The NoGC version of GetNonexistentProperty, present only to make types line up. */
 bool
-GetNonexistentProperty(JSContext* cx, NativeObject* obj, jsid id, Value& receiver,
+GetNonexistentProperty(JSContext* cx, NativeObject* const& obj, const jsid& id, const Value& receiver,
                        IsNameLookup nameLookup, FakeMutableHandle<Value> vp)
 {
     return false;
 }
 
 static inline bool
 GeneralizedGetProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue receiver,
                        IsNameLookup nameLookup, MutableHandleValue vp)
--- a/js/src/vm/NativeObject.h
+++ b/js/src/vm/NativeObject.h
@@ -324,17 +324,17 @@ extern HeapSlot* const emptyObjectElemen
 struct Class;
 class GCMarker;
 class Shape;
 
 class NewObjectCache;
 
 #ifdef DEBUG
 static inline bool
-IsObjectValueInCompartment(Value v, JSCompartment* comp);
+IsObjectValueInCompartment(const Value& v, JSCompartment* comp);
 #endif
 
 // Operations which change an object's dense elements can either succeed, fail,
 // or be unable to complete. For native objects, the latter is used when the
 // object's elements must become sparse instead. The enum below is used for
 // such operations, and for similar operations on unboxed arrays and methods
 // that work on both kinds of objects.
 enum class DenseElementResult {
@@ -1303,17 +1303,17 @@ NativeObject::privateWriteBarrierPre(voi
 {
     JS::shadow::Zone* shadowZone = this->shadowZoneFromAnyThread();
     if (shadowZone->needsIncrementalBarrier() && *oldval && getClass()->hasTrace())
         getClass()->doTrace(shadowZone->barrierTracer(), this);
 }
 
 #ifdef DEBUG
 static inline bool
-IsObjectValueInCompartment(Value v, JSCompartment* comp)
+IsObjectValueInCompartment(const Value& v, JSCompartment* comp)
 {
     if (!v.isObject())
         return true;
     return v.toObject().compartment() == comp;
 }
 #endif
 
 
--- a/js/src/vm/ProxyObject.h
+++ b/js/src/vm/ProxyObject.h
@@ -99,17 +99,17 @@ class ProxyObject : public ShapedObject
         return clasp->isProxy() &&
                clasp->isTrace(proxy_Trace) &&
                !clasp->getCall() && !clasp->getConstruct();
     }
 
   public:
     static unsigned grayLinkExtraSlot(JSObject* obj);
 
-    void renew(JSContext* cx, const BaseProxyHandler* handler, Value priv);
+    void renew(JSContext* cx, const BaseProxyHandler* handler, const Value& priv);
 
     static void trace(JSTracer* trc, JSObject* obj);
 
     void nuke(const BaseProxyHandler* handler);
 
     // There is no class_ member to force specialization of JSObject::is<T>().
     // The implementation in JSObject is incorrect for proxies since it doesn't
     // take account of the handler type.
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -924,18 +924,18 @@ class AnyInvokeArgs : public JS::CallArg
 {
 };
 
 /** Base class for all function construction args. */
 class AnyConstructArgs : public JS::CallArgs
 {
     // Only js::Construct (or internal methods that call the qualified CallArgs
     // versions) should do these things!
-    void setCallee(Value v) = delete;
-    void setThis(Value v) = delete;
+    void setCallee(const Value& v) = delete;
+    void setThis(const Value& v) = delete;
     MutableHandleValue newTarget() const = delete;
     MutableHandleValue rval() const = delete;
 };
 
 namespace detail {
 
 /** Function call/construct args of statically-unknown count. */
 template <MaybeConstruct Construct>
--- a/js/src/vm/String.cpp
+++ b/js/src/vm/String.cpp
@@ -654,17 +654,17 @@ js::ConcatStrings(ExclusiveContext* cx,
 
     return JSRope::new_<allowGC>(cx, left, right, wholeLength);
 }
 
 template JSString*
 js::ConcatStrings<CanGC>(ExclusiveContext* cx, HandleString left, HandleString right);
 
 template JSString*
-js::ConcatStrings<NoGC>(ExclusiveContext* cx, JSString* left, JSString* right);
+js::ConcatStrings<NoGC>(ExclusiveContext* cx, JSString* const& left, JSString* const& right);
 
 template <typename CharT>
 JSFlatString*
 JSDependentString::undependInternal(ExclusiveContext* cx)
 {
     size_t n = length();
     CharT* s = cx->pod_malloc<CharT>(n + 1);
     if (!s)
--- a/js/src/vm/StructuredClone.cpp
+++ b/js/src/vm/StructuredClone.cpp
@@ -332,17 +332,17 @@ struct JSStructuredCloneReader {
 };
 
 struct JSStructuredCloneWriter {
   public:
     explicit JSStructuredCloneWriter(JSContext* cx,
                                      JS::StructuredCloneScope scope,
                                      const JSStructuredCloneCallbacks* cb,
                                      void* cbClosure,
-                                     Value tVal)
+                                     const Value& tVal)
         : out(cx), scope(scope), objs(out.context()),
           counts(out.context()), entries(out.context()),
           memory(out.context()), callbacks(cb),
           closure(cbClosure), transferable(out.context(), tVal),
           transferableObjects(out.context(), GCHashSet<JSObject*>(cx))
     {}
 
     ~JSStructuredCloneWriter();
@@ -482,17 +482,17 @@ ReportDataCloneError(JSContext* cx,
         break;
     }
 }
 
 bool
 WriteStructuredClone(JSContext* cx, HandleValue v, JSStructuredCloneData* bufp,
                      JS::StructuredCloneScope scope,
                      const JSStructuredCloneCallbacks* cb, void* cbClosure,
-                     Value transferable)
+                     const Value& transferable)
 {
     JSStructuredCloneWriter w(cx, scope, cb, cbClosure, transferable);
     return w.init() && w.write(v) && w.extractBuffer(bufp);
 }
 
 bool
 ReadStructuredClone(JSContext* cx, JSStructuredCloneData& data,
                     JS::StructuredCloneScope scope, MutableHandleValue vp,
--- a/js/src/vm/Symbol.cpp
+++ b/js/src/vm/Symbol.cpp
@@ -124,23 +124,23 @@ js::SymbolDescriptiveString(JSContext* c
     str = sb.finishString();
     if (!str)
         return false;
     result.setString(str);
     return true;
 }
 
 bool
-js::IsSymbolOrSymbolWrapper(Value v)
+js::IsSymbolOrSymbolWrapper(const Value& v)
 {
     return v.isSymbol() || (v.isObject() && v.toObject().is<SymbolObject>());
 }
 
 JS::Symbol*
-js::ToSymbolPrimitive(Value v)
+js::ToSymbolPrimitive(const Value& v)
 {
     MOZ_ASSERT(IsSymbolOrSymbolWrapper(v));
     return v.isSymbol() ? v.toSymbol() : v.toObject().as<SymbolObject>().unbox();
 }
 
 
 JS::ubi::Node::Size
 JS::ubi::Concrete<JS::Symbol>::size(mozilla::MallocSizeOf mallocSizeOf) const
--- a/js/src/vm/Symbol.h
+++ b/js/src/vm/Symbol.h
@@ -127,16 +127,16 @@ class SymbolRegistry : public GCHashSet<
 
 namespace js {
 
 // ES6 rev 27 (2014 Aug 24) 19.4.3.3
 bool
 SymbolDescriptiveString(JSContext* cx, JS::Symbol* sym, JS::MutableHandleValue result);
 
 bool
-IsSymbolOrSymbolWrapper(JS::Value v);
+IsSymbolOrSymbolWrapper(const JS::Value& v);
 
 JS::Symbol*
-ToSymbolPrimitive(JS::Value v);
+ToSymbolPrimitive(const JS::Value& v);
 
 } /* namespace js */
 
 #endif /* vm_Symbol_h */
--- a/js/src/vm/TaggedProto.h
+++ b/js/src/vm/TaggedProto.h
@@ -106,17 +106,17 @@ class RootedBase<TaggedProto> : public T
 template <>
 class BarrieredBaseMixins<TaggedProto> : public TaggedProtoOperations<GCPtr<TaggedProto>>
 {};
 
 // If the TaggedProto is a JSObject pointer, convert to that type and call |f|
 // with the pointer. If the TaggedProto is lazy, calls F::defaultValue.
 template <typename F, typename... Args>
 auto
-DispatchTyped(F f, TaggedProto& proto, Args&&... args)
+DispatchTyped(F f, const TaggedProto& proto, Args&&... args)
   -> decltype(f(static_cast<JSObject*>(nullptr), mozilla::Forward<Args>(args)...))
 {
     if (proto.isObject())
         return f(proto.toObject(), mozilla::Forward<Args>(args)...);
     return F::defaultValue(proto);
 }
 
 // Since JSObject pointers are either nullptr or a valid object and since the
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -1276,22 +1276,22 @@ TypedArrayObject::GetTemplateObjectForNa
 #define CHECK_TYPED_ARRAY_CONSTRUCTOR(T, N) \
     if (native == &TypedArrayObjectTemplate<T>::class_constructor) { \
         size_t nbytes; \
         if (!js::CalculateAllocSize<T>(len, &nbytes)) \
             return true; \
         \
         if (nbytes < TypedArrayObject::SINGLETON_BYTE_LENGTH) { \
             res.set(TypedArrayObjectTemplate<T>::makeTemplateObject(cx, len)); \
-            return true; \
+            return !!res; \
         } \
     }
 JS_FOR_EACH_TYPED_ARRAY(CHECK_TYPED_ARRAY_CONSTRUCTOR)
 #undef CHECK_TYPED_ARRAY_CONSTRUCTOR
-    return false;
+    return true;
 }
 
 /*
  * These next 3 functions are brought to you by the buggy GCC we use to build
  * B2G ICS. Older GCC versions have a bug in which they fail to compile
  * reinterpret_casts of templated functions with the message: "insufficient
  * contextual information to determine type". JS_PSG needs to
  * reinterpret_cast<JSGetterOp>, so this causes problems for us here.
--- a/js/xpconnect/src/XPCConvert.cpp
+++ b/js/xpconnect/src/XPCConvert.cpp
@@ -1026,17 +1026,17 @@ XPCConvert::ConstructException(nsresult 
     return NS_OK;
 }
 
 /********************************/
 
 class MOZ_STACK_CLASS AutoExceptionRestorer
 {
 public:
-    AutoExceptionRestorer(JSContext* cx, Value v)
+    AutoExceptionRestorer(JSContext* cx, const Value& v)
         : mContext(cx), tvr(cx, v)
     {
         JS_ClearPendingException(mContext);
     }
 
     ~AutoExceptionRestorer()
     {
         JS_SetPendingException(mContext, tvr);
--- a/js/xpconnect/src/XPCInlines.h
+++ b/js/xpconnect/src/XPCInlines.h
@@ -170,17 +170,17 @@ XPCCallContext::GetArgv() const
 inline JS::Value*
 XPCCallContext::GetRetVal() const
 {
     CHECK_STATE(READY_TO_CALL);
     return mRetVal;
 }
 
 inline void
-XPCCallContext::SetRetVal(JS::Value val)
+XPCCallContext::SetRetVal(const JS::Value& val)
 {
     CHECK_STATE(HAVE_ARGS);
     if (mRetVal)
         *mRetVal = val;
 }
 
 inline jsid
 XPCCallContext::GetResolveName() const
--- a/js/xpconnect/src/XPCMaps.cpp
+++ b/js/xpconnect/src/XPCMaps.cpp
@@ -71,17 +71,17 @@ JSObject2WrappedJSMap::UpdateWeakPointer
                 wrapper->UpdateObjectPointerAfterGC();
                 if (!wrapper->GetJSObjectPreserveColor())
                     dying.AppendElement(dont_AddRef(wrapper));
             }
             wrapper = wrapper->GetNextWrapper();
         }
 
         // Remove or update the JSObject key in the table if necessary.
-        JSObject* obj = e.front().key();
+        JSObject* obj = e.front().key().unbarrieredGet();
         JS_UpdateWeakPointerAfterGCUnbarriered(&obj);
         if (!obj)
             e.removeFront();
         else
             e.front().mutableKey() = obj;
     }
 }
 
--- a/js/xpconnect/src/XPCVariant.cpp
+++ b/js/xpconnect/src/XPCVariant.cpp
@@ -24,17 +24,17 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(nsISupports)
   NS_IMPL_QUERY_CLASSINFO(XPCVariant)
 NS_INTERFACE_MAP_END
 NS_IMPL_CI_INTERFACE_GETTER(XPCVariant, XPCVariant, nsIVariant)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(XPCVariant)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(XPCVariant)
 
-XPCVariant::XPCVariant(JSContext* cx, Value aJSVal)
+XPCVariant::XPCVariant(JSContext* cx, const Value& aJSVal)
     : mJSVal(aJSVal), mCCGeneration(0)
 {
     if (!mJSVal.isPrimitive()) {
         // XXXbholley - The innerization here was from bug 638026. Blake says
         // the basic problem was that we were storing the C++ inner but the JS
         // outer, which meant that, after navigation, the JS inner could be
         // collected, which would cause us to try to recreate the JS inner at
         // some later point after teardown, which would crash. This is shouldn't
@@ -60,17 +60,17 @@ XPCTraceableVariant::~XPCTraceableVarian
     mData.Cleanup();
 
     if (!val.isNull())
         RemoveFromRootSet();
 }
 
 void XPCTraceableVariant::TraceJS(JSTracer* trc)
 {
-    MOZ_ASSERT(mJSVal.isMarkable());
+    MOZ_ASSERT(GetJSValPreserveColor().isMarkable());
     JS::TraceEdge(trc, &mJSVal, "XPCTraceableVariant::mJSVal");
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(XPCVariant)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(XPCVariant)
     JS::Value val = tmp->GetJSValPreserveColor();
     if (val.isObject()) {
@@ -90,17 +90,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(XP
         XPCTraceableVariant* v = static_cast<XPCTraceableVariant*>(tmp);
         v->RemoveFromRootSet();
     }
     tmp->mJSVal = JS::NullValue();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 // static
 already_AddRefed<XPCVariant>
-XPCVariant::newVariant(JSContext* cx, Value aJSVal)
+XPCVariant::newVariant(JSContext* cx, const Value& aJSVal)
 {
     RefPtr<XPCVariant> variant;
 
     if (!aJSVal.isMarkable())
         variant = new XPCVariant(cx, aJSVal);
     else
         variant = new XPCTraceableVariant(cx, aJSVal);
 
--- a/js/xpconnect/src/XPCWrappedJS.cpp
+++ b/js/xpconnect/src/XPCWrappedJS.cpp
@@ -538,20 +538,20 @@ nsXPCWrappedJS::Unlink()
         }
     }
 }
 
 bool
 nsXPCWrappedJS::IsMultiCompartment() const
 {
     MOZ_ASSERT(IsRootWrapper());
-    JSCompartment* compartment = js::GetObjectCompartment(mJSObj);
+    JSCompartment* compartment = Compartment();
     nsXPCWrappedJS* next = mNext;
     while (next) {
-        if (js::GetObjectCompartment(next->mJSObj) != compartment)
+        if (next->Compartment() != compartment)
             return true;
         next = next->mNext;
     }
     return false;
 }
 
 nsXPCWrappedJS*
 nsXPCWrappedJS::Find(REFNSIID aIID)
--- a/js/xpconnect/src/XPCWrappedNativeProto.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeProto.cpp
@@ -125,17 +125,17 @@ XPCWrappedNativeProto::JSProtoObjectFina
     GetContext()->GetDyingWrappedNativeProtoMap()->Add(this);
 
     mJSProtoObject.finalize(js::CastToJSFreeOp(fop)->runtime());
 }
 
 void
 XPCWrappedNativeProto::JSProtoObjectMoved(JSObject* obj, const JSObject* old)
 {
-    MOZ_ASSERT(mJSProtoObject == old);
+    MOZ_ASSERT(mJSProtoObject.unbarrieredGet() == old);
     mJSProtoObject.init(obj); // Update without triggering barriers.
 }
 
 void
 XPCWrappedNativeProto::SystemIsBeingShutDown()
 {
     // Note that the instance might receive this call multiple times
     // as we walk to here from various places.
--- a/js/xpconnect/src/XPCWrappedNativeScope.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeScope.cpp
@@ -519,17 +519,17 @@ XPCWrappedNativeScope::SuspectAllWrapper
 {
     for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext) {
         for (auto i = cur->mWrappedNativeMap->Iter(); !i.Done(); i.Next()) {
             static_cast<Native2WrappedNativeMap::Entry*>(i.Get())->value->Suspect(cb);
         }
 
         if (cur->mDOMExpandoSet) {
             for (DOMExpandoSet::Range r = cur->mDOMExpandoSet->all(); !r.empty(); r.popFront())
-                SuspectDOMExpandos(r.front(), cb);
+                SuspectDOMExpandos(r.front().unbarrieredGet(), cb);
         }
     }
 }
 
 // static
 void
 XPCWrappedNativeScope::UpdateWeakPointersAfterGC(XPCJSContext* cx)
 {
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -733,17 +733,17 @@ public:
     inline void                         SetMethodIndex(uint16_t index) ;
 
     inline jsid GetResolveName() const;
     inline jsid SetResolveName(JS::HandleId name);
 
     inline XPCWrappedNative* GetResolvingWrapper() const;
     inline XPCWrappedNative* SetResolvingWrapper(XPCWrappedNative* w);
 
-    inline void SetRetVal(JS::Value val);
+    inline void SetRetVal(const JS::Value& val);
 
     void SetName(jsid name);
     void SetArgsAndResultPtr(unsigned argc, JS::Value* argv, JS::Value* rval);
     void SetCallInfo(XPCNativeInterface* iface, XPCNativeMember* member,
                      bool isSetter);
 
     nsresult  CanCallNow();
 
@@ -882,22 +882,21 @@ public:
     bool AttachComponentsObject(JSContext* aCx);
 
     // Returns the JS object reflection of the Components object.
     bool
     GetComponentsJSObject(JS::MutableHandleObject obj);
 
     JSObject*
     GetGlobalJSObject() const {
-        JS::ExposeObjectToActiveJS(mGlobalJSObject);
         return mGlobalJSObject;
     }
 
     JSObject*
-    GetGlobalJSObjectPreserveColor() const {return mGlobalJSObject;}
+    GetGlobalJSObjectPreserveColor() const {return mGlobalJSObject.unbarrieredGet();}
 
     nsIPrincipal*
     GetPrincipal() const {
         JSCompartment* c = js::GetObjectCompartment(mGlobalJSObject);
         return nsJSPrincipals::get(JS_GetCompartmentPrincipals(c));
     }
 
     JSObject*
@@ -2154,17 +2153,17 @@ public:
     /**
      * This getter does not change the color of the JSObject meaning that the
      * object returned is not guaranteed to be kept alive past the next CC.
      *
      * This should only be called if you are certain that the return value won't
      * be passed into a JS API function and that it won't be stored without
      * being rooted (or otherwise signaling the stored value to the CC).
      */
-    JSObject* GetJSObjectPreserveColor() const {return mJSObj;}
+    JSObject* GetJSObjectPreserveColor() const { return mJSObj.unbarrieredGet(); }
 
     // Returns true if the wrapper chain contains references to multiple
     // compartments. If the wrapper chain contains references to multiple
     // compartments, then it must be registered on the XPCJSContext. Otherwise,
     // it should be registered in the CompartmentPrivate for the compartment of
     // the root's JS object. This will only return correct results when called
     // on the root wrapper and will assert if not called on a root wrapper.
     bool IsMultiCompartment() const;
@@ -2178,18 +2177,18 @@ public:
     nsXPCWrappedJS* FindInherited(REFNSIID aIID);
     nsXPCWrappedJS* FindOrFindInherited(REFNSIID aIID) {
         nsXPCWrappedJS* wrapper = Find(aIID);
         if (wrapper)
             return wrapper;
         return FindInherited(aIID);
     }
 
-    bool IsRootWrapper() const {return mRoot == this;}
-    bool IsValid() const {return mJSObj != nullptr;}
+    bool IsRootWrapper() const { return mRoot == this; }
+    bool IsValid() const { return bool(mJSObj); }
     void SystemIsBeingShutDown();
 
     // These two methods are used by JSObject2WrappedJSMap::FindDyingJSObjects
     // to find non-rooting wrappers for dying JS objects. See the top of
     // XPCWrappedJS.cpp for more details.
     bool IsSubjectToFinalization() const {return IsValid() && mRefCnt == 1;}
     void UpdateObjectPointerAfterGC() {JS_UpdateWeakPointerAfterGC(&mJSObj);}
 
@@ -2218,16 +2217,20 @@ protected:
                    nsXPCWrappedJS* root,
                    nsresult* rv);
 
     bool CanSkip();
     void Destroy();
     void Unlink();
 
 private:
+    JSCompartment* Compartment() const {
+        return js::GetObjectCompartment(mJSObj.unbarrieredGet());
+    }
+
     JS::Heap<JSObject*> mJSObj;
     RefPtr<nsXPCWrappedJSClass> mClass;
     nsXPCWrappedJS* mRoot;    // If mRoot != this, it is an owning pointer.
     nsXPCWrappedJS* mNext;
     nsCOMPtr<nsISupports> mOuter;    // only set in root
 };
 
 /***************************************************************************/
@@ -2868,41 +2871,39 @@ public:
     // If this class ever implements nsIWritableVariant, take special care with
     // the case when mJSVal is JSVAL_STRING, since we don't own the data in
     // that case.
 
     // We #define and iid so that out module local code can use QI to detect
     // if a given nsIVariant is in fact an XPCVariant.
     NS_DECLARE_STATIC_IID_ACCESSOR(XPCVARIANT_IID)
 
-    static already_AddRefed<XPCVariant> newVariant(JSContext* cx, JS::Value aJSVal);
+    static already_AddRefed<XPCVariant> newVariant(JSContext* cx, const JS::Value& aJSVal);
 
     /**
      * This getter clears the gray bit before handing out the Value if the Value
      * represents a JSObject. That means that the object is guaranteed to be
      * kept alive past the next CC.
      */
     JS::Value GetJSVal() const {
-        if (!mJSVal.isPrimitive())
-            JS::ExposeObjectToActiveJS(&mJSVal.toObject());
         return mJSVal;
     }
 
     /**
      * This getter does not change the color of the Value (if it represents a
      * JSObject) meaning that the value returned is not guaranteed to be kept
      * alive past the next CC.
      *
      * This should only be called if you are certain that the return value won't
      * be passed into a JS API function and that it won't be stored without
      * being rooted (or otherwise signaling the stored value to the CC).
      */
-    JS::Value GetJSValPreserveColor() const {return mJSVal;}
-
-    XPCVariant(JSContext* cx, JS::Value aJSVal);
+    JS::Value GetJSValPreserveColor() const { return mJSVal.unbarrieredGet(); }
+
+    XPCVariant(JSContext* cx, const JS::Value& aJSVal);
 
     /**
      * Convert a variant into a JS::Value.
      *
      * @param ccx the context for the whole procedure
      * @param variant the variant to convert
      * @param scope the default scope to put on the new JSObject's parent chain
      * @param pErr [out] relevant error code, if any.
@@ -2940,17 +2941,17 @@ protected:
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(XPCVariant, XPCVARIANT_IID)
 
 class XPCTraceableVariant: public XPCVariant,
                            public XPCRootSetElem
 {
 public:
-    XPCTraceableVariant(JSContext* cx, JS::Value aJSVal)
+    XPCTraceableVariant(JSContext* cx, const JS::Value& aJSVal)
         : XPCVariant(cx, aJSVal)
     {
          nsXPConnect::GetContextInstance()->AddVariantRoot(this);
     }
 
     virtual ~XPCTraceableVariant();
 
     void TraceJS(JSTracer* trc);
--- a/layout/base/AccessibleCaretManager.cpp
+++ b/layout/base/AccessibleCaretManager.cpp
@@ -531,16 +531,26 @@ AccessibleCaretManager::TapCaret(const n
 nsresult
 AccessibleCaretManager::SelectWordOrShortcut(const nsPoint& aPoint)
 {
   auto UpdateCaretsWithHapticFeedback = [this] {
     UpdateCarets();
     ProvideHapticFeedback();
   };
 
+  // If the long-tap is landing on a pre-existing selection, don't replace
+  // it with a new one. Instead just return and let the context menu pop up
+  // on the pre-existing selection.
+  if (GetCaretMode() == CaretMode::Selection &&
+      GetSelection()->ContainsPoint(aPoint)) {
+    AC_LOG("%s: UpdateCarets() for current selection", __FUNCTION__);
+    UpdateCaretsWithHapticFeedback();
+    return NS_OK;
+  }
+
   if (!mPresShell) {
     return NS_ERROR_UNEXPECTED;
   }
 
   nsIFrame* rootFrame = mPresShell->GetRootFrame();
   if (!rootFrame) {
     return NS_ERROR_NOT_AVAILABLE;
   }
@@ -608,26 +618,16 @@ AccessibleCaretManager::SelectWordOrShor
 
   // ptFrame is selectable. Now change the focus.
   ChangeFocusToOrClearOldFocus(focusableFrame);
   if (!ptFrame.IsAlive()) {
     // Cannot continue because ptFrame died.
     return NS_ERROR_FAILURE;
   }
 
-  if (GetCaretMode() == CaretMode::Selection &&
-      !mFirstCaret->IsLogicallyVisible() && !mSecondCaret->IsLogicallyVisible()) {
-    // We have a selection while both carets have Appearance::None because of
-    // previous operations like blur event. Just update carets on the selection
-    // without selecting a new word.
-    AC_LOG("%s: UpdateCarets() for current selection", __FUNCTION__);
-    UpdateCaretsWithHapticFeedback();
-    return NS_OK;
-  }
-
   // Then try select a word under point.
   nsresult rv = SelectWord(ptFrame, ptInFrame);
   UpdateCaretsWithHapticFeedback();
 
   return rv;
 }
 
 void
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -5526,18 +5526,18 @@ nsLayoutUtils::ComputeSizeWithIntrinsicD
   } else if (MOZ_UNLIKELY(isGridItem)) {
     MOZ_ASSERT(!IS_TRUE_OVERFLOW_CONTAINER(aFrame));
     // 'auto' block-size for grid-level box - apply 'stretch' as needed:
     auto cbSize = aCBSize.BSize(aWM);
     if (cbSize != NS_AUTOHEIGHT &&
         !aFrame->StyleMargin()->HasBlockAxisAuto(aWM)) {
       auto blockAxisAlignment =
         !aWM.IsOrthogonalTo(aFrame->GetParent()->GetWritingMode()) ?
-          stylePos->ComputedAlignSelf(aFrame->StyleContext()->GetParent()) :
-          stylePos->ComputedJustifySelf(aFrame->StyleContext()->GetParent());
+          stylePos->UsedAlignSelf(aFrame->StyleContext()->GetParent()) :
+          stylePos->UsedJustifySelf(aFrame->StyleContext()->GetParent());
       if (blockAxisAlignment == NS_STYLE_ALIGN_NORMAL ||
           blockAxisAlignment == NS_STYLE_ALIGN_STRETCH) {
         bSize = std::max(nscoord(0), cbSize -
                                      aPadding.BSize(aWM) -
                                      aBorder.BSize(aWM) -
                                      aMargin.BSize(aWM));
         isAutoBSize = false;
       }
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -726,16 +726,17 @@ nsIPresShell::FrameSelection()
   return ret.forget();
 }
 
 //----------------------------------------------------------------------
 
 static bool sSynthMouseMove = true;
 static uint32_t sNextPresShellId;
 static bool sPointerEventEnabled = true;
+static bool sPointerEventImplicitCapture = false;
 static bool sAccessibleCaretEnabled = false;
 static bool sAccessibleCaretOnTouch = false;
 static bool sBeforeAfterKeyboardEventEnabled = false;
 
 /* static */ bool
 PresShell::AccessibleCaretEnabled(nsIDocShell* aDocShell)
 {
   static bool initialized = false;
@@ -803,17 +804,23 @@ PresShell::PresShell()
     addedSynthMouseMove = true;
   }
   static bool addedPointerEventEnabled = false;
   if (!addedPointerEventEnabled) {
     Preferences::AddBoolVarCache(&sPointerEventEnabled,
                                  "dom.w3c_pointer_events.enabled", true);
     addedPointerEventEnabled = true;
   }
-
+  static bool addedPointerEventImplicitCapture = false;
+  if (!addedPointerEventImplicitCapture) {
+    Preferences::AddBoolVarCache(&sPointerEventImplicitCapture,
+                                 "dom.w3c_pointer_events.implicit_capture",
+                                 true);
+    addedPointerEventImplicitCapture = true;
+  }
   mPaintingIsFrozen = false;
   mHasCSSBackgroundColor = true;
   mIsLastChromeOnlyEscapeKeyConsumed = false;
   mHasReceivedPaintMessage = false;
 }
 
 NS_IMPL_ISUPPORTS(PresShell, nsIPresShell, nsIDocumentObserver,
                   nsISelectionController,
@@ -7686,16 +7693,29 @@ PresShell::HandleEvent(nsIFrame* aFrame,
         // gotpointercapture / lostpointercapture.
         CheckPointerCaptureState(pointerEvent->pointerId,
                                  pointerEvent->inputSource,
                                  pointerEvent->mIsPrimary);
         // Prevent application crashes, in case damaged frame.
         if (!frameKeeper.IsAlive()) {
           frame = nullptr;
         }
+        // Implicit pointer capture for touch
+        if (sPointerEventImplicitCapture &&
+            pointerEvent->mMessage == ePointerDown &&
+            pointerEvent->inputSource == nsIDOMMouseEvent::MOZ_SOURCE_TOUCH) {
+          nsCOMPtr<nsIContent> targetContent;
+          frame->GetContentForEvent(aEvent, getter_AddRefs(targetContent));
+          while (targetContent && !targetContent->IsElement()) {
+            targetContent = targetContent->GetParent();
+          }
+          if (targetContent) {
+            SetPointerCapturingContent(pointerEvent->pointerId, targetContent);
+          }
+        }
       }
     }
 
     if (aEvent->mClass == ePointerEventClass &&
         aEvent->mMessage != ePointerDown) {
       if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) {
         uint32_t pointerId = pointerEvent->pointerId;
         nsIContent* pointerCapturingContent = GetPointerCapturingContent(pointerId);
--- a/layout/generic/ReflowInput.cpp
+++ b/layout/generic/ReflowInput.cpp
@@ -2313,18 +2313,18 @@ ReflowInput::InitConstraints(nsPresConte
       }
 
       nsIFrame* parent = mFrame->GetParent();
       nsIAtom* parentFrameType = parent ? parent->GetType() : nullptr;
       if (parentFrameType == nsGkAtoms::gridContainerFrame) {
         // Shrink-wrap grid items that will be aligned (rather than stretched)
         // in its inline axis.
         auto inlineAxisAlignment = wm.IsOrthogonalTo(cbwm) ?
-          mStylePosition->ComputedAlignSelf(mFrame->StyleContext()->GetParent()) :
-          mStylePosition->ComputedJustifySelf(mFrame->StyleContext()->GetParent());
+          mStylePosition->UsedAlignSelf(mFrame->StyleContext()->GetParent()) :
+          mStylePosition->UsedJustifySelf(mFrame->StyleContext()->GetParent());
         if ((inlineAxisAlignment != NS_STYLE_ALIGN_STRETCH &&
              inlineAxisAlignment != NS_STYLE_ALIGN_NORMAL) ||
             mStyleMargin->mMargin.GetIStartUnit(wm) == eStyleUnit_Auto ||
             mStyleMargin->mMargin.GetIEndUnit(wm) == eStyleUnit_Auto) {
           computeSizeFlags =
             ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eShrinkWrap);
         }
       } else {
--- a/layout/generic/Selection.h
+++ b/layout/generic/Selection.h
@@ -115,17 +115,17 @@ public:
    * to this Selection.
    */
   nsresult      AddItem(nsRange* aRange, int32_t* aOutIndex, bool aNoStartSelect = false);
   nsresult      RemoveItem(nsRange* aRange);
   nsresult      RemoveCollapsedRanges();
   nsresult      Clear(nsPresContext* aPresContext);
   nsresult      Collapse(nsINode* aParentNode, int32_t aOffset);
   nsresult      Extend(nsINode* aParentNode, int32_t aOffset);
-  nsRange*      GetRangeAt(int32_t aIndex);
+  nsRange*      GetRangeAt(int32_t aIndex) const;
 
   // Get the anchor-to-focus range if we don't care which end is
   // anchor and which end is focus.
   const nsRange* GetAnchorFocusRange() const {
     return mAnchorFocusRange;
   }
 
   nsDirection  GetDirection(){return mDirection;}
@@ -155,17 +155,17 @@ public:
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   // WebIDL methods
   nsINode*     GetAnchorNode();
   uint32_t     AnchorOffset();
   nsINode*     GetFocusNode();
   uint32_t     FocusOffset();
 
-  bool IsCollapsed();
+  bool IsCollapsed() const;
   void Collapse(nsINode& aNode, uint32_t aOffset, mozilla::ErrorResult& aRv);
   void CollapseToStart(mozilla::ErrorResult& aRv);
   void CollapseToEnd(mozilla::ErrorResult& aRv);
 
   void Extend(nsINode& aNode, uint32_t aOffset, mozilla::ErrorResult& aRv);
 
   void SelectAllChildren(nsINode& aNode, mozilla::ErrorResult& aRv);
   void DeleteFromDocument(mozilla::ErrorResult& aRv);
@@ -178,16 +178,25 @@ public:
   void AddRange(nsRange& aRange, mozilla::ErrorResult& aRv);
   void RemoveRange(nsRange& aRange, mozilla::ErrorResult& aRv);
   void RemoveAllRanges(mozilla::ErrorResult& aRv);
 
   void Stringify(nsAString& aResult);
 
   bool ContainsNode(nsINode& aNode, bool aPartlyContained, mozilla::ErrorResult& aRv);
 
+  /**
+   * Check to see if the given point is contained within the selection area. In
+   * particular, this iterates through all the rects that make up the selection,
+   * not just the bounding box, and checks to see if the given point is contained
+   * in any one of them.
+   * @param aPoint The point to check, relative to the root frame.
+   */
+  bool ContainsPoint(const nsPoint& aPoint);
+
   void Modify(const nsAString& aAlter, const nsAString& aDirection,
               const nsAString& aGranularity, mozilla::ErrorResult& aRv);
 
   bool GetInterlinePosition(mozilla::ErrorResult& aRv);
   void SetInterlinePosition(bool aValue, mozilla::ErrorResult& aRv);
 
   Nullable<int16_t> GetCaretBidiLevel(mozilla::ErrorResult& aRv) const;
   void SetCaretBidiLevel(const Nullable<int16_t>& aCaretBidiLevel, mozilla::ErrorResult& aRv);
--- a/layout/generic/nsFlexContainerFrame.cpp
+++ b/layout/generic/nsFlexContainerFrame.cpp
@@ -1712,17 +1712,17 @@ FlexItem::FlexItem(ReflowInput& aFlexIte
     //     container's cross-axis alignment behavior.
     // (2) Suppress the ability for flex items to override that with their own
     //     cross-axis alignment. (The legacy box model doesn't support this.)
     // So, each FlexItem simply copies the container's converted "align-items"
     // value and disregards their own "align-self" property.
     const nsStyleXUL* containerStyleXUL = containerRS->mFrame->StyleXUL();
     mAlignSelf = ConvertLegacyStyleToAlignItems(containerStyleXUL);
   } else {
-    mAlignSelf = aFlexItemReflowInput.mStylePosition->ComputedAlignSelf(
+    mAlignSelf = aFlexItemReflowInput.mStylePosition->UsedAlignSelf(
                    containerRS->mFrame->StyleContext());
     if (MOZ_LIKELY(mAlignSelf == NS_STYLE_ALIGN_NORMAL)) {
       mAlignSelf = NS_STYLE_ALIGN_STRETCH;
     }
 
     // XXX strip off the <overflow-position> bit until we implement that
     mAlignSelf &= ~NS_STYLE_ALIGN_FLAG_BITS;
   }
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -4765,18 +4765,18 @@ nsFrame::ComputeSize(nsRenderingContext 
                blockStyleCoord->GetUnit() == eStyleUnit_Auto &&
                !IS_TRUE_OVERFLOW_CONTAINER(this)) {
       // 'auto' block-size for grid-level box - apply 'stretch' as needed:
       auto cbSize = aCBSize.BSize(aWM);
       if (cbSize != NS_AUTOHEIGHT &&
           !StyleMargin()->HasBlockAxisAuto(aWM)) {
         auto blockAxisAlignment =
           !aWM.IsOrthogonalTo(GetParent()->GetWritingMode()) ?
-            StylePosition()->ComputedAlignSelf(StyleContext()->GetParent()) :
-            StylePosition()->ComputedJustifySelf(StyleContext()->GetParent());
+            StylePosition()->UsedAlignSelf(StyleContext()->GetParent()) :
+            StylePosition()->UsedJustifySelf(StyleContext()->GetParent());
         if (blockAxisAlignment == NS_STYLE_ALIGN_NORMAL ||
             blockAxisAlignment == NS_STYLE_ALIGN_STRETCH) {
           result.BSize(aWM) = std::max(nscoord(0), cbSize -
                                                    aPadding.BSize(aWM) -
                                                    aBorder.BSize(aWM) -
                                                    aMargin.BSize(aWM));
         }
       }
--- a/layout/generic/nsGridContainerFrame.cpp
+++ b/layout/generic/nsGridContainerFrame.cpp
@@ -8,47 +8,44 @@
 
 #include "nsGridContainerFrame.h"
 
 #include <algorithm> // for std::stable_sort
 #include <limits>
 #include "mozilla/Function.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/PodOperations.h" // for PodZero
+#include "mozilla/Poison.h"
 #include "nsAbsoluteContainingBlock.h"
 #include "nsAlgorithm.h" // for clamped()
 #include "nsCSSAnonBoxes.h"
 #include "nsCSSFrameConstructor.h"
 #include "nsDataHashtable.h"
 #include "nsDisplayList.h"
 #include "nsHashKeys.h"
 #include "nsIFrameInlines.h"
 #include "nsPresContext.h"
 #include "nsRenderingContext.h"
 #include "nsReadableUtils.h"
 #include "nsRuleNode.h"
 #include "nsStyleContext.h"
 #include "mozilla/dom/GridBinding.h"
 
+#if defined(__clang__) && __clang_major__ == 3 && __clang_minor__  == 6
+#define CLANG_CRASH_BUG 1
+#endif
+
 using namespace mozilla;
 typedef nsAbsoluteContainingBlock::AbsPosReflowFlags AbsPosReflowFlags;
 typedef nsGridContainerFrame::TrackSize TrackSize;
 const uint32_t nsGridContainerFrame::kTranslatedMaxLine =
   uint32_t(nsStyleGridLine::kMaxLine - nsStyleGridLine::kMinLine);
 const uint32_t nsGridContainerFrame::kAutoLine = kTranslatedMaxLine + 3457U;
 typedef nsTHashtable< nsPtrHashKey<nsIFrame> > FrameHashtable;
 
-// https://drafts.csswg.org/css-align-3/#baseline-sharing-group
-enum BaselineSharingGroup
-{
-  // NOTE Used as an array index so must be 0 and 1.
-  eFirst = 0,
-  eLast = 1,
-};
-
 // https://drafts.csswg.org/css-sizing/#constraints
 enum class SizingConstraint
 {
   eMinContent,  // sizing under min-content constraint
   eMaxContent,  // sizing under max-content constraint
   eNoConstraint // no constraint, used during Reflow
 };
 
@@ -120,16 +117,36 @@ ResolveToDefiniteSize(const nsStyleCoord
   MOZ_ASSERT(aCoord.IsCoordPercentCalcUnit());
   if (::IsPercentOfIndefiniteSize(aCoord, aPercentBasis)) {
     return nscoord(0);
   }
   return std::max(nscoord(0),
                   nsRuleNode::ComputeCoordPercentCalc(aCoord, aPercentBasis));
 }
 
+// Synthesize a baseline from a border box.  For an alphabetical baseline
+// this is the end edge of the border box.  For a central baseline it's
+// the center of the border box.
+// https://drafts.csswg.org/css-align-3/#synthesize-baselines
+// For a first-baseline the measure is from the border-box start edge and
+// for a last-baseline the measure is from the border-box end edge.
+static nscoord
+SynthesizeBaselineFromBorderBox(BaselineSharingGroup aGroup,
+                                WritingMode aWM,
+                                nscoord aBorderBoxSize)
+{
+  if (aGroup == BaselineSharingGroup::eFirst) {
+    return aWM.IsAlphabeticalBaseline() ? aBorderBoxSize : aBorderBoxSize / 2;
+  }
+  MOZ_ASSERT(aGroup == BaselineSharingGroup::eLast);
+  // Round up for central baseline offset, to be consistent with eFirst.
+  return aWM.IsAlphabeticalBaseline() ? 0 :
+    (aBorderBoxSize / 2) + (aBorderBoxSize % 2);
+}
+
 enum class GridLineSide
 {
   eBeforeGridGap,
   eAfterGridGap,
 };
 
 struct nsGridContainerFrame::TrackSize
 {
@@ -314,174 +331,255 @@ MergeSortedFrameLists(nsFrameList& aDest
 
 static void
 MergeSortedFrameListsFor(nsFrameList& aDest, nsFrameList& aSrc,
                          nsContainerFrame* aParent)
 {
   MergeSortedFrameLists(aDest, aSrc, aParent->GetContent());
 }
 
-class nsGridContainerFrame::GridItemCSSOrderIterator
+template<typename Iterator>
+class nsGridContainerFrame::GridItemCSSOrderIteratorT
 {
 public:
   enum OrderState { eUnknownOrder, eKnownOrdered, eKnownUnordered };
   enum ChildFilter { eSkipPlaceholders, eIncludeAll };
-  GridItemCSSOrderIterator(nsIFrame* aGridContainer,
-                           nsIFrame::ChildListID aListID,
-                           ChildFilter aFilter = eSkipPlaceholders,
-                           OrderState aState = eUnknownOrder)
+  GridItemCSSOrderIteratorT(nsIFrame* aGridContainer,
+                            nsIFrame::ChildListID aListID,
+                            ChildFilter aFilter = eSkipPlaceholders,
+                            OrderState aState = eUnknownOrder)
     : mChildren(aGridContainer->GetChildList(aListID))
     , mArrayIndex(0)
     , mGridItemIndex(0)
     , mSkipPlaceholders(aFilter == eSkipPlaceholders)
 #ifdef DEBUG
     , mGridContainer(aGridContainer)
     , mListID(aListID)
 #endif
   {
     size_t count = 0;
     bool isOrdered = aState != eKnownUnordered;
     if (aState == eUnknownOrder) {
       auto maxOrder = std::numeric_limits<int32_t>::min();
-      for (nsFrameList::Enumerator e(mChildren); !e.AtEnd(); e.Next()) {
+      for (auto child : mChildren) {
         ++count;
-        int32_t order = e.get()->StylePosition()->mOrder;
+        int32_t order = child->StylePosition()->mOrder;
         if (order < maxOrder) {
           isOrdered = false;
           break;
         }
         maxOrder = order;
       }
     }
     if (isOrdered) {
-      mEnumerator.emplace(mChildren);
+      mIter.emplace(begin(mChildren));
+      mIterEnd.emplace(end(mChildren));
     } else {
       count *= 2; // XXX somewhat arbitrary estimate for now...
       mArray.emplace(count);
-      for (nsFrameList::Enumerator e(mChildren); !e.AtEnd(); e.Next()) {
-        mArray->AppendElement(e.get());
+      for (Iterator i(begin(mChildren)), iEnd(end(mChildren)); i != iEnd; ++i) {
+        mArray->AppendElement(*i);
       }
       // XXX replace this with nsTArray::StableSort when bug 1147091 is fixed.
-      std::stable_sort(mArray->begin(), mArray->end(), IsCSSOrderLessThan);
+      std::stable_sort(mArray->begin(), mArray->end(), CSSOrderComparator);
     }
 
     if (mSkipPlaceholders) {
       SkipPlaceholders();
     }
   }
+  ~GridItemCSSOrderIteratorT()
+  {
+    MOZ_ASSERT(IsForward() == mGridItemCount.isNothing());
+  }
+
+  bool IsForward() const;
+  Iterator begin(const nsFrameList& aList);
+  Iterator end(const nsFrameList& aList);
 
   nsIFrame* operator*() const
   {
     MOZ_ASSERT(!AtEnd());
-    if (mEnumerator) {
-      return mEnumerator->get();
+    if (mIter.isSome()) {
+      return **mIter;
     }
     return (*mArray)[mArrayIndex];
   }
 
   /**
    * Return the child index of the current item, placeholders not counted.
    * It's forbidden to call this method when the current frame is placeholder.
    */
   size_t GridItemIndex() const
   {
     MOZ_ASSERT(!AtEnd());
     MOZ_ASSERT((**this)->GetType() != nsGkAtoms::placeholderFrame,
                "MUST not call this when at a placeholder");
+    MOZ_ASSERT(IsForward() || mGridItemIndex < *mGridItemCount,
+               "Returning an out-of-range mGridItemIndex...");
     return mGridItemIndex;
   }
 
+  void SetGridItemCount(size_t aGridItemCount)
+  {
+    MOZ_ASSERT(mIter.isSome() || mArray->Length() == aGridItemCount,
+               "grid item count mismatch");
+    mGridItemCount.emplace(aGridItemCount);
+    // Note: it's OK if mGridItemIndex underflows -- GridItemIndex()
+    // will not be called unless there is at least one item.
+    mGridItemIndex = IsForward() ? 0 : *mGridItemCount - 1;
+  }
+
   /**
    * Skip over placeholder children.
    */
   void SkipPlaceholders()
   {
-    if (mEnumerator) {
-      for (; !mEnumerator->AtEnd(); mEnumerator->Next()) {
-        nsIFrame* child = mEnumerator->get();
+    if (mIter.isSome()) {
+      for (; *mIter != *mIterEnd; ++*mIter) {
+        nsIFrame* child = **mIter;
         if (child->GetType() != nsGkAtoms::placeholderFrame) {
           return;
         }
       }
     } else {
       for (; mArrayIndex < mArray->Length(); ++mArrayIndex) {
         nsIFrame* child = (*mArray)[mArrayIndex];
         if (child->GetType() != nsGkAtoms::placeholderFrame) {
           return;
         }
       }
     }
   }
 
   bool AtEnd() const
   {
-    MOZ_ASSERT(mEnumerator || mArrayIndex <= mArray->Length());
-    return mEnumerator ? mEnumerator->AtEnd() : mArrayIndex >= mArray->Length();
+#ifndef CLANG_CRASH_BUG
+    // Clang 3.6.2 crashes when compiling this assertion:
+    MOZ_ASSERT(mIter.isSome() || mArrayIndex <= mArray->Length());
+#endif
+    return mIter ? (*mIter == *mIterEnd) : mArrayIndex >= mArray->Length();
   }
 
   void Next()
   {
 #ifdef DEBUG
     MOZ_ASSERT(!AtEnd());
     nsFrameList list = mGridContainer->GetChildList(mListID);
     MOZ_ASSERT(list.FirstChild() == mChildren.FirstChild() &&
                list.LastChild() == mChildren.LastChild(),
                "the list of child frames must not change while iterating!");
 #endif
     if (mSkipPlaceholders ||
         (**this)->GetType() != nsGkAtoms::placeholderFrame) {
-      ++mGridItemIndex;
-    }
-    if (mEnumerator) {
-      mEnumerator->Next();
+      IsForward() ? ++mGridItemIndex : --mGridItemIndex;
+    }
+    if (mIter.isSome()) {
+      ++*mIter;
     } else {
       ++mArrayIndex;
     }
     if (mSkipPlaceholders) {
       SkipPlaceholders();
     }
   }
 
   void Reset(ChildFilter aFilter = eSkipPlaceholders)
   {
-    if (mEnumerator) {
-      mEnumerator.reset();
-      mEnumerator.emplace(mChildren);
+    if (mIter.isSome()) {
+      mIter.reset();
+      mIter.emplace(begin(mChildren));
+      mIterEnd.reset();
+      mIterEnd.emplace(end(mChildren));
     } else {
       mArrayIndex = 0;
     }
-    mGridItemIndex = 0;
+    mGridItemIndex = IsForward() ? 0 : *mGridItemCount - 1;
     mSkipPlaceholders = aFilter == eSkipPlaceholders;
     if (mSkipPlaceholders) {
       SkipPlaceholders();
     }
   }
 
-  bool ItemsAreAlreadyInOrder() const { return mEnumerator.isSome(); }
-
+  bool IsValid() const { return mIter.isSome() || mArray.isSome(); }
+
+  void Invalidate()
+  {
+    mIter.reset();
+    mArray.reset();
+    mozWritePoison(&mChildren, sizeof(mChildren));
+  }
+
+  bool ItemsAreAlreadyInOrder() const { return mIter.isSome(); }
+
+  static bool CSSOrderComparator(nsIFrame* const& a, nsIFrame* const& b);
 private:
-  static bool IsCSSOrderLessThan(nsIFrame* const& a, nsIFrame* const& b)
-    { return a->StylePosition()->mOrder < b->StylePosition()->mOrder; }
-
   nsFrameList mChildren;
   // Used if child list is already in ascending 'order'.
-  Maybe<nsFrameList::Enumerator> mEnumerator;
+  Maybe<Iterator> mIter;
+  Maybe<Iterator> mIterEnd;
   // Used if child list is *not* in ascending 'order'.
+  // This array is pre-sorted in reverse order for a reverse iterator.
   Maybe<nsTArray<nsIFrame*>> mArray;
   size_t mArrayIndex;
   // The index of the current grid item (placeholders excluded).
   size_t mGridItemIndex;
+  // The number of grid items (placeholders excluded).
+  // It's only initialized and used in a reverse iterator.
+  Maybe<size_t> mGridItemCount;
   // Skip placeholder children in the iteration?
   bool mSkipPlaceholders;
 #ifdef DEBUG
   nsIFrame* mGridContainer;
   nsIFrame::ChildListID mListID;
 #endif
 };
 
+using GridItemCSSOrderIterator = nsGridContainerFrame::GridItemCSSOrderIterator;
+using ReverseGridItemCSSOrderIterator = nsGridContainerFrame::ReverseGridItemCSSOrderIterator;
+
+template<>
+bool
+GridItemCSSOrderIterator::CSSOrderComparator(nsIFrame* const& a,
+                                             nsIFrame* const& b)
+{ return a->StylePosition()->mOrder < b->StylePosition()->mOrder; }
+
+template<>
+bool
+GridItemCSSOrderIterator::IsForward() const { return true; }
+
+template<>
+nsFrameList::iterator
+GridItemCSSOrderIterator::begin(const nsFrameList& aList)
+{ return aList.begin(); }
+
+template<>
+nsFrameList::iterator GridItemCSSOrderIterator::end(const nsFrameList& aList)
+{ return aList.end(); }
+
+template<>
+bool
+ReverseGridItemCSSOrderIterator::CSSOrderComparator(nsIFrame* const& a,
+                                                    nsIFrame* const& b)
+{ return a->StylePosition()->mOrder > b->StylePosition()->mOrder; }
+
+template<>
+bool
+ReverseGridItemCSSOrderIterator::IsForward() const
+{ return false; }
+
+template<>
+nsFrameList::reverse_iterator
+ReverseGridItemCSSOrderIterator::begin(const nsFrameList& aList)
+{ return aList.rbegin(); }
+
+template<>
+nsFrameList::reverse_iterator
+ReverseGridItemCSSOrderIterator::end(const nsFrameList& aList)
+{ return aList.rend(); }
 
 /**
  * A LineRange can be definite or auto - when it's definite it represents
  * a consecutive set of tracks between a starting line and an ending line.
  * Before it's definite it can also represent an auto position with a span,
  * where mStart == kAutoLine and mEnd is the (non-zero positive) span.
  * For normal-flow items, the invariant mStart < mEnd holds when both
  * lines are definite.
@@ -1119,16 +1217,18 @@ struct nsGridContainerFrame::TrackSizing
 struct nsGridContainerFrame::Tracks
 {
   explicit Tracks(LogicalAxis aAxis)
     : mAxis(aAxis)
     , mCanResolveLineRangeSize(false)
   {
     mBaselineSubtreeAlign[BaselineSharingGroup::eFirst] = NS_STYLE_ALIGN_AUTO;
     mBaselineSubtreeAlign[BaselineSharingGroup::eLast] = NS_STYLE_ALIGN_AUTO;
+    mBaseline[BaselineSharingGroup::eFirst] = NS_INTRINSIC_WIDTH_UNKNOWN;
+    mBaseline[BaselineSharingGroup::eLast] = NS_INTRINSIC_WIDTH_UNKNOWN;
   }
 
   void Initialize(const TrackSizingFunctions& aFunctions,
                   const nsStyleCoord&         aGridGap,
                   uint32_t                    aNumTracks,
                   nscoord                     aContentBoxSize);
 
   /**
@@ -1697,16 +1797,18 @@ struct nsGridContainerFrame::Tracks
       printf("\n");
     }
   }
 #endif
 
   AutoTArray<TrackSize, 32> mSizes;
   nscoord mContentBoxSize;
   nscoord mGridGap;
+  // The first(last)-baseline for the first(last) track in this axis.
+  nscoord mBaseline[2]; // index by BaselineSharingGroup
   LogicalAxis mAxis;
   // Used for aligning a baseline-aligned subtree of items.  The only possible
   // values are NS_STYLE_ALIGN_{START,END,CENTER,AUTO}.  AUTO means there are
   // no baseline-aligned items in any track in that axis.
   // There is one alignment value for each BaselineSharingGroup.
   uint8_t mBaselineSubtreeAlign[2];
   // True if track positions and sizes are final in this axis.
   bool mCanResolveLineRangeSize;
@@ -1917,19 +2019,26 @@ struct MOZ_STACK_CLASS nsGridContainerFr
   /**
    * BStart of this fragment in "grid space" (i.e. the concatenation of content
    * areas of all fragments).  Equal to mRows.mSizes[mStartRow].mPosition,
    * or, if this fragment starts after the last row, the GetConsumedBSize().
    */
   nscoord mFragBStart;
   /** The start row for this fragment. */
   uint32_t mStartRow;
+  /**
+   * The start row for the next fragment, if any.  If mNextFragmentStartRow ==
+   * mStartRow then there are no rows in this fragment.
+   */
+  uint32_t mNextFragmentStartRow;
   /** Our tentative ApplySkipSides bits. */
   LogicalSides mSkipSides;
   const WritingMode mWM;
+  /** Initialized lazily, when we find the fragmentainer. */
+  bool mInFragmentainer;
 
 private:
   GridReflowInput(nsGridContainerFrame*    aFrame,
                   nsRenderingContext&      aRenderingContext,
                   const ReflowInput* aReflowInput,
                   const nsStylePosition*   aGridStyle,
                   const WritingMode&       aWM)
     : mIter(aFrame, kPrincipalList)
@@ -1944,17 +2053,19 @@ private:
                     mGridStyle->mGridAutoRowsMax)
     , mReflowInput(aReflowInput)
     , mRenderingContext(aRenderingContext)
     , mFrame(aFrame)
     , mSharedGridData(nullptr)
     , mBorderPadding(aWM)
     , mFragBStart(0)
     , mStartRow(0)
+    , mNextFragmentStartRow(0)
     , mWM(aWM)
+    , mInFragmentainer(false)
   {
     MOZ_ASSERT(!aReflowInput || aReflowInput->mFrame == mFrame);
     if (aReflowInput) {
       mBorderPadding = aReflowInput->ComputedLogicalBorderPadding();
       mSkipSides = aFrame->PreReflowBlockLevelLogicalSkipSides();
       mBorderPadding.ApplySkipSides(mSkipSides);
     }
   }
@@ -3862,16 +3973,18 @@ nsGridContainerFrame::Tracks::CalculateI
   if (aBaselineItems.IsEmpty()) {
     return;
   }
 
   // Sort the collected items on their baseline track.
   std::sort(aBaselineItems.begin(), aBaselineItems.end(),
             ItemBaselineData::IsBaselineTrackLessThan);
 
+  MOZ_ASSERT(mSizes.Length() > 0, "having an item implies at least one track");
+  const uint32_t lastTrack = mSizes.Length() - 1;
   nscoord maxBaseline = 0;
   nscoord maxDescent = 0;
   uint32_t currentTrack = kAutoLine; // guaranteed to not match any item
   uint32_t trackStartIndex = 0;
   for (uint32_t i = 0, len = aBaselineItems.Length(); true ; ++i) {
     // Find the maximum baseline and descent in the current track.
     if (i != len) {
       const ItemBaselineData& item = aBaselineItems[i];
@@ -3883,20 +3996,28 @@ nsGridContainerFrame::Tracks::CalculateI
     }
     // Iterate the current track again and update the baseline offsets making
     // all items baseline-aligned within this group in this track.
     for (uint32_t j = trackStartIndex; j < i; ++j) {
       const ItemBaselineData& item = aBaselineItems[j];
       item.mGridItem->mBaselineOffset[mAxis] = maxBaseline - item.mBaseline;
       MOZ_ASSERT(item.mGridItem->mBaselineOffset[mAxis] >= 0);
     }
-    // Store the size of this baseline-aligned subtree.
     if (i != 0) {
+      // Store the size of this baseline-aligned subtree.
       mSizes[currentTrack].mBaselineSubtreeSize[aBaselineGroup] =
         maxBaseline + maxDescent;
+      // Record the first(last) baseline for the first(last) track.
+      if (currentTrack == 0 && aBaselineGroup == BaselineSharingGroup::eFirst) {
+        mBaseline[aBaselineGroup] = maxBaseline;
+      }
+      if (currentTrack == lastTrack &&
+          aBaselineGroup == BaselineSharingGroup::eLast) {
+        mBaseline[aBaselineGroup] = maxBaseline;
+      }
     }
     if (i == len) {
       break;
     }
     // Initialize data for the next track with baseline-aligned items.
     const ItemBaselineData& item = aBaselineItems[i];
     currentTrack = item.mBaselineTrack;
     trackStartIndex = i;
@@ -3927,18 +4048,18 @@ nsGridContainerFrame::Tracks::Initialize
     const bool isInlineAxis = mAxis == eLogicalAxisInline; // i.e. columns
     // XXX update the line below to include orthogonal grid/table boxes
     // XXX since they have baselines in both dimensions. And flexbox with
     // XXX reversed main/cross axis?
     const bool itemHasBaselineParallelToTrack = isInlineAxis == isOrthogonal;
     if (itemHasBaselineParallelToTrack) {
       // [align|justify]-self:[last-]baseline.
       auto selfAlignment = isOrthogonal ?
-        child->StylePosition()->ComputedJustifySelf(containerSC) :
-        child->StylePosition()->ComputedAlignSelf(containerSC);
+        child->StylePosition()->UsedJustifySelf(containerSC) :
+        child->StylePosition()->UsedAlignSelf(containerSC);
       selfAlignment &= ~NS_STYLE_ALIGN_FLAG_BITS;
       if (selfAlignment == NS_STYLE_ALIGN_BASELINE) {
         state |= ItemState::eFirstBaseline | ItemState::eSelfBaseline;
         const GridArea& area = gridItem.mArea;
         baselineTrack = isOrthogonal ? area.mCols.mStart : area.mRows.mStart;
       } else if (selfAlignment == NS_STYLE_ALIGN_LAST_BASELINE) {
         state |= ItemState::eLastBaseline | ItemState::eSelfBaseline;
         const GridArea& area = gridItem.mArea;
@@ -4012,34 +4133,58 @@ nsGridContainerFrame::Tracks::Initialize
     if (state & ItemState::eIsBaselineAligned) {
       // XXX available size issue
       LogicalSize avail(childWM, INFINITE_ISIZE_COORD, NS_UNCONSTRAINEDSIZE);
       auto* rc = &aState.mRenderingContext;
       // XXX figure out if we can avoid/merge this reflow with the main reflow.
       // XXX (after bug 1174569 is sorted out)
       ::MeasuringReflow(child, aState.mReflowInput, rc, avail);
       nscoord baseline;
+      nsGridContainerFrame* grid = do_QueryFrame(child);
       if (state & ItemState::eFirstBaseline) {
-        if (nsLayoutUtils::GetFirstLineBaseline(wm, child, &baseline)) {
+        if (grid) {
+          if (isOrthogonal == isInlineAxis) {
+            grid->GetBBaseline(BaselineSharingGroup::eFirst, &baseline);
+          } else {
+            grid->GetIBaseline(BaselineSharingGroup::eFirst, &baseline);
+          }
+        }
+        if (grid ||
+            nsLayoutUtils::GetFirstLineBaseline(wm, child, &baseline)) {
+          NS_ASSERTION(baseline != NS_INTRINSIC_WIDTH_UNKNOWN,
+                       "about to use an unknown baseline");
           auto frameSize = isInlineAxis ? child->ISize(wm) : child->BSize(wm);
           auto m = child->GetLogicalUsedMargin(wm);
           baseline += isInlineAxis ? m.IStart(wm) : m.BStart(wm);
           auto alignSize = frameSize + (isInlineAxis ? m.IStartEnd(wm)
                                                      : m.BStartEnd(wm));
           firstBaselineItems.AppendElement(ItemBaselineData(
             { baselineTrack, baseline, alignSize, &gridItem }));
         } else {
           state &= ~ItemState::eAllBaselineBits;
         }
       } else {
-        if (nsLayoutUtils::GetLastLineBaseline(wm, child, &baseline)) {
+        if (grid) {
+          if (isOrthogonal == isInlineAxis) {
+            grid->GetBBaseline(BaselineSharingGroup::eLast, &baseline);
+          } else {
+            grid->GetIBaseline(BaselineSharingGroup::eLast, &baseline);
+          }
+        }
+        if (grid ||
+            nsLayoutUtils::GetLastLineBaseline(wm, child, &baseline)) {
+          NS_ASSERTION(baseline != NS_INTRINSIC_WIDTH_UNKNOWN,
+                       "about to use an unknown baseline");
           auto frameSize = isInlineAxis ? child->ISize(wm) : child->BSize(wm);
           auto m = child->GetLogicalUsedMargin(wm);
-          auto descent = frameSize - baseline + (isInlineAxis ? m.IEnd(wm)
-                                                              : m.BEnd(wm));
+          if (!grid) {
+            // Convert to distance from border-box end.
+            baseline = frameSize - baseline;
+          }
+          auto descent = baseline + (isInlineAxis ? m.IEnd(wm) : m.BEnd(wm));
           auto alignSize = frameSize + (isInlineAxis ? m.IStartEnd(wm)
                                                      : m.BStartEnd(wm));
           lastBaselineItems.AppendElement(ItemBaselineData(
             { baselineTrack, descent, alignSize, &gridItem }));
         } else {
           state &= ~ItemState::eAllBaselineBits;
         }
       }
@@ -4892,17 +5037,17 @@ nsGridContainerFrame::ReflowInFlowChild(
   // If the child is stretching in its block axis, and we might be fragmenting
   // it in that axis, then setup a frame property to tell
   // nsBlockFrame::ComputeFinalSize the size.
   if (isConstrainedBSize && !wm.IsOrthogonalTo(childWM)) {
     bool stretch = false;
     if (!childRI.mStyleMargin->HasBlockAxisAuto(childWM) &&
         childRI.mStylePosition->BSize(childWM).GetUnit() == eStyleUnit_Auto) {
       auto blockAxisAlignment =
-        childRI.mStylePosition->ComputedAlignSelf(StyleContext());
+        childRI.mStylePosition->UsedAlignSelf(StyleContext());
       if (blockAxisAlignment == NS_STYLE_ALIGN_NORMAL ||
           blockAxisAlignment == NS_STYLE_ALIGN_STRETCH) {
         stretch = true;
       }
     }
     if (stretch) {
       aChild->Properties().Set(FragStretchBSizeProperty(), *aStretchBSize);
     } else {
@@ -4919,26 +5064,26 @@ nsGridContainerFrame::ReflowInFlowChild(
               dummyContainerSize, 0, aStatus);
   LogicalPoint childPos =
     cb.Origin(wm).ConvertTo(childWM, wm,
                             aContainerSize - childSize.PhysicalSize());
   // Apply align/justify-self and reflow again if that affects the size.
   if (MOZ_LIKELY(isGridItem)) {
     LogicalSize size = childSize.Size(childWM); // from the ReflowChild()
     if (NS_FRAME_IS_COMPLETE(aStatus)) {
-      auto align = childRI.mStylePosition->ComputedAlignSelf(containerSC);
+      auto align = childRI.mStylePosition->UsedAlignSelf(containerSC);
       auto state = aGridItemInfo->mState[eLogicalAxisBlock];
       if (state & ItemState::eContentBaseline) {
         align = (state & ItemState::eFirstBaseline) ? NS_STYLE_ALIGN_SELF_START
                                                     : NS_STYLE_ALIGN_SELF_END;
       }
       nscoord cbsz = cb.BSize(wm) - consumedGridAreaBSize;
       AlignSelf(*aGridItemInfo, align, cbsz, wm, childRI, size, &childPos);
     }
-    auto justify = childRI.mStylePosition->ComputedJustifySelf(containerSC);
+    auto justify = childRI.mStylePosition->UsedJustifySelf(containerSC);
     auto state = aGridItemInfo->mState[eLogicalAxisInline];
     if (state & ItemState::eContentBaseline) {
       justify = (state & ItemState::eFirstBaseline) ? NS_STYLE_JUSTIFY_SELF_START
                                                     : NS_STYLE_JUSTIFY_SELF_END;
     }
     nscoord cbsz = cb.ISize(wm);
     JustifySelf(*aGridItemInfo, justify, cbsz, wm, childRI, size, &childPos);
   } else {
@@ -5324,16 +5469,17 @@ nsGridContainerFrame::ReflowRowsInFragme
     if (NS_FRAME_IS_NOT_COMPLETE(childStatus)) {
       incompleteItems.PutEntry(child);
     } else if (!NS_FRAME_IS_FULLY_COMPLETE(childStatus)) {
       overflowIncompleteItems.PutEntry(child);
     }
   }
 
   // Record a break before |aEndRow|.
+  aState.mNextFragmentStartRow = aEndRow;
   if (aEndRow < aState.mRows.mSizes.Length()) {
     aState.mRows.BreakBeforeRow(aEndRow);
     if (aState.mSharedGridData) {
       aState.mSharedGridData->mRows.BreakBeforeRow(aEndRow);
     }
   }
 
   if (!pushedItems.IsEmpty() ||
@@ -5431,19 +5577,25 @@ nsGridContainerFrame::ReflowRowsInFragme
       }
       child = next;
     }
 
     // Merge the results into our respective overflow child lists.
     if (!pushedList.IsEmpty()) {
       MergeSortedOverflow(pushedList);
       AddStateBits(NS_STATE_GRID_DID_PUSH_ITEMS);
+      // NOTE since we messed with our child list here, we intentionally
+      // make aState.mIter invalid to avoid any use of it after this point.
+      aState.mIter.Invalidate();
     }
     if (!incompleteList.IsEmpty()) {
       MergeSortedOverflow(incompleteList);
+      // NOTE since we messed with our child list here, we intentionally
+      // make aState.mIter invalid to avoid any use of it after this point.
+      aState.mIter.Invalidate();
     }
     if (!overflowIncompleteList.IsEmpty()) {
       MergeSortedExcessOverflowContainers(overflowIncompleteList);
     }
   }
   return aBSize;
 }
 
@@ -5466,16 +5618,17 @@ nsGridContainerFrame::ReflowChildren(Gri
 
   WritingMode wm = aState.mReflowInput->GetWritingMode();
   const nsSize containerSize =
     (aContentArea.Size(wm) + aState.mBorderPadding.Size(wm)).GetPhysicalSize(wm);
 
   nscoord bSize = aContentArea.BSize(wm);
   Maybe<Fragmentainer> fragmentainer = GetNearestFragmentainer(aState);
   if (MOZ_UNLIKELY(fragmentainer.isSome())) {
+    aState.mInFragmentainer = true;
     bSize = ReflowInFragmentainer(aState, aContentArea, aDesiredSize, aStatus,
                                   *fragmentainer, containerSize);
   } else {
     aState.mIter.Reset(GridItemCSSOrderIterator::eIncludeAll);
     for (; !aState.mIter.AtEnd(); aState.mIter.Next()) {
       nsIFrame* child = *aState.mIter;
       const GridItemInfo* info = nullptr;
       if (child->GetType() != nsGkAtoms::placeholderFrame) {
@@ -5717,16 +5870,21 @@ nsGridContainerFrame::Reflow(nsPresConte
 
   RenumberList();
 
 #ifdef DEBUG
   mDidPushItemsBitMayLie = false;
   SanityCheckGridItemsBeforeReflow();
 #endif // DEBUG
 
+  mBaseline[0][0] = NS_INTRINSIC_WIDTH_UNKNOWN;
+  mBaseline[0][1] = NS_INTRINSIC_WIDTH_UNKNOWN;
+  mBaseline[1][0] = NS_INTRINSIC_WIDTH_UNKNOWN;
+  mBaseline[1][1] = NS_INTRINSIC_WIDTH_UNKNOWN;
+
   const nsStylePosition* stylePos = aReflowInput.mStylePosition;
   if (!prevInFlow) {
     InitImplicitNamedAreas(stylePos);
   }
   GridReflowInput gridReflowInput(this, aReflowInput);
   if (gridReflowInput.mIter.ItemsAreAlreadyInOrder()) {
     AddStateBits(NS_STATE_GRID_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER);
   } else {
@@ -5790,31 +5948,95 @@ nsGridContainerFrame::Reflow(nsPresConte
       StyleBorder()->mBoxDecorationBreak !=
         StyleBoxDecorationBreak::Clone) {
     bp.BEnd(wm) = nscoord(0);
   }
 
   LogicalSize desiredSize(wm, computedISize + bp.IStartEnd(wm),
                               bSize         + bp.BStartEnd(wm));
   aDesiredSize.SetSize(wm, desiredSize);
-  aDesiredSize.mOverflowAreas.UnionAllWith(nsRect(0, 0,
-                                                  aDesiredSize.Width(),
-                                                  aDesiredSize.Height()));
+  nsRect frameRect(0, 0, aDesiredSize.Width(), aDesiredSize.Height());
+  aDesiredSize.mOverflowAreas.UnionAllWith(frameRect);
 
   // Convert INCOMPLETE -> OVERFLOW_INCOMPLETE and zero bsize if we're an OC.
   if (HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) {
     if (!NS_FRAME_IS_COMPLETE(aStatus)) {
       NS_FRAME_SET_OVERFLOW_INCOMPLETE(aStatus);
       aStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
     }
     bSize = 0;
     desiredSize.BSize(wm) = bSize + bp.BStartEnd(wm);
     aDesiredSize.SetSize(wm, desiredSize);
   }
 
+  if (!gridReflowInput.mInFragmentainer) {
+    MOZ_ASSERT(gridReflowInput.mIter.IsValid());
+    auto sz = frameRect.Size();
+    CalculateBaselines(BaselineSet::eBoth, &gridReflowInput.mIter,
+                       &gridReflowInput.mGridItems, gridReflowInput.mCols,
+                       0, gridReflowInput.mCols.mSizes.Length(),
+                       wm, sz, bp.IStart(wm),
+                       bp.IEnd(wm), desiredSize.ISize(wm));
+    CalculateBaselines(BaselineSet::eBoth, &gridReflowInput.mIter,
+                       &gridReflowInput.mGridItems, gridReflowInput.mRows,
+                       0, gridReflowInput.mRows.mSizes.Length(),
+                       wm, sz, bp.BStart(wm),
+                       bp.BEnd(wm), desiredSize.BSize(wm));
+  } else {
+    // Only compute first-baseline if this fragment contains the first track.
+    // XXXmats maybe remove this condition? bug 1306499
+    BaselineSet baselines = BaselineSet::eNone;
+    if (gridReflowInput.mStartRow == 0 &&
+        gridReflowInput.mStartRow != gridReflowInput.mNextFragmentStartRow) {
+      baselines = BaselineSet::eFirst;
+    }
+    // Only compute last-baseline if this fragment contains the last track.
+    // XXXmats maybe remove this condition? bug 1306499
+    uint32_t len = gridReflowInput.mRows.mSizes.Length();
+    if (gridReflowInput.mStartRow != len &&
+        gridReflowInput.mNextFragmentStartRow == len) {
+      baselines = BaselineSet(baselines | BaselineSet::eLast);
+    }
+    Maybe<GridItemCSSOrderIterator> iter;
+    Maybe<nsTArray<GridItemInfo>> gridItems;
+    if (baselines != BaselineSet::eNone) {
+      // We need to create a new iterator and GridItemInfo array because we
+      // might have pushed some children at this point.
+      // Even if the gridReflowInput iterator is invalid we can reuse its
+      // state about order to optimize initialization of the new iterator.
+      // An ordered child list can't become unordered by pushing frames.
+      // An unordered list can become ordered in a number of cases, but we
+      // ignore that here and guess that the child list is still unordered.
+      // XXX this is O(n^2) in the number of items in this fragment: bug 1306705
+      using Filter = GridItemCSSOrderIterator::ChildFilter;
+      using Order = GridItemCSSOrderIterator::OrderState;
+      bool ordered = gridReflowInput.mIter.ItemsAreAlreadyInOrder();
+      auto orderState = ordered ? Order::eKnownOrdered : Order::eKnownUnordered;
+      iter.emplace(this, kPrincipalList, Filter::eSkipPlaceholders, orderState);
+      gridItems.emplace();
+      for (; !iter->AtEnd(); iter->Next()) {
+        auto child = **iter;
+        for (const auto& info : gridReflowInput.mGridItems) {
+          if (info.mFrame == child) {
+            gridItems->AppendElement(info);
+          }
+        }
+      }
+    }
+    auto sz = frameRect.Size();
+    CalculateBaselines(baselines, iter.ptrOr(nullptr), gridItems.ptrOr(nullptr),
+                       gridReflowInput.mCols, 0,
+                       gridReflowInput.mCols.mSizes.Length(), wm, sz,
+                       bp.IStart(wm), bp.IEnd(wm), desiredSize.ISize(wm));
+    CalculateBaselines(baselines, iter.ptrOr(nullptr), gridItems.ptrOr(nullptr),
+                       gridReflowInput.mRows, gridReflowInput.mStartRow,
+                       gridReflowInput.mNextFragmentStartRow, wm, sz,
+                       bp.BStart(wm), bp.BEnd(wm), desiredSize.BSize(wm));
+  }
+
   if (HasAnyStateBits(NS_STATE_GRID_GENERATE_COMPUTED_VALUES)) {
     // This state bit will never be cleared, since reflow can be called
     // multiple times in fragmented grids, and it's challenging to scope
     // the bit to only that sequence of calls. This is relatively harmless
     // since this bit is only set by accessing a ChromeOnly property, and
     // therefore can't unduly slow down normal web browsing.
 
     // Now that we know column and row sizes and positions, set
@@ -6117,16 +6339,20 @@ nsGridContainerFrame::GetPrefISize(nsRen
   return mCachedPrefISize;
 }
 
 void
 nsGridContainerFrame::MarkIntrinsicISizesDirty()
 {
   mCachedMinISize = NS_INTRINSIC_WIDTH_UNKNOWN;
   mCachedPrefISize = NS_INTRINSIC_WIDTH_UNKNOWN;
+  mBaseline[0][0] = NS_INTRINSIC_WIDTH_UNKNOWN;
+  mBaseline[0][1] = NS_INTRINSIC_WIDTH_UNKNOWN;
+  mBaseline[1][0] = NS_INTRINSIC_WIDTH_UNKNOWN;
+  mBaseline[1][1] = NS_INTRINSIC_WIDTH_UNKNOWN;
   nsContainerFrame::MarkIntrinsicISizesDirty();
 }
 
 nsIAtom*
 nsGridContainerFrame::GetType() const
 {
   return nsGkAtoms::gridContainerFrame;
 }
@@ -6207,16 +6433,151 @@ nsGridContainerFrame::RemoveFrame(ChildL
         frameThatMayLie->GetPrevInFlow());
     } while (frameThatMayLie);
   }
 #endif
 
   nsContainerFrame::RemoveFrame(aListID, aOldFrame);
 }
 
+nscoord
+nsGridContainerFrame::SynthesizeBaseline(
+  const FindItemInGridOrderResult& aGridOrderItem,
+  LogicalAxis          aAxis,
+  BaselineSharingGroup aGroup,
+  const nsSize&        aCBPhysicalSize,
+  nscoord              aCBSize,
+  WritingMode          aCBWM)
+{
+  if (MOZ_UNLIKELY(!aGridOrderItem.mItem)) {
+    // No item in this fragment - synthesize a baseline from our border-box.
+    return ::SynthesizeBaselineFromBorderBox(aGroup, aCBWM, aCBSize);
+  }
+  nsIFrame* child = aGridOrderItem.mItem->mFrame;
+  nsGridContainerFrame* grid = do_QueryFrame(child);
+  auto childWM = child->GetWritingMode();
+  bool isOrthogonal = aCBWM.IsOrthogonalTo(childWM);
+  nscoord baseline;
+  nscoord start;
+  nscoord size;
+  if (aAxis == eLogicalAxisBlock) {
+    start = child->GetLogicalNormalPosition(aCBWM, aCBPhysicalSize).B(aCBWM);
+    size = child->BSize(aCBWM);
+    if (grid && aGridOrderItem.mIsInEdgeTrack) {
+      isOrthogonal ? grid->GetIBaseline(aGroup, &baseline) :
+                     grid->GetBBaseline(aGroup, &baseline);
+    } else if (!isOrthogonal && aGridOrderItem.mIsInEdgeTrack &&
+               nsLayoutUtils::GetLastLineBaseline(childWM, child, &baseline)) {
+      if (aGroup == BaselineSharingGroup::eLast) {
+        baseline = size - baseline; // convert to distance from border-box end
+      }
+    } else {
+      baseline = ::SynthesizeBaselineFromBorderBox(aGroup, childWM, size);
+    }
+  } else {
+    start = child->GetLogicalNormalPosition(aCBWM, aCBPhysicalSize).I(aCBWM);
+    size = child->ISize(aCBWM);
+    if (grid && aGridOrderItem.mIsInEdgeTrack) {
+      isOrthogonal ? grid->GetBBaseline(aGroup, &baseline) :
+                     grid->GetIBaseline(aGroup, &baseline);
+    } else if (isOrthogonal && aGridOrderItem.mIsInEdgeTrack &&
+               nsLayoutUtils::GetLastLineBaseline(childWM, child, &baseline)) {
+      if (aGroup == BaselineSharingGroup::eLast) {
+        baseline = size - baseline; // convert to distance from border-box end
+      }
+    } else {
+      baseline = ::SynthesizeBaselineFromBorderBox(aGroup, childWM, size);
+    }
+  }
+  return aGroup == BaselineSharingGroup::eFirst ? start + baseline :
+    aCBSize - start - size + baseline;
+}
+
+void
+nsGridContainerFrame::CalculateBaselines(
+  BaselineSet                   aBaselineSet,
+  GridItemCSSOrderIterator*     aIter,
+  const nsTArray<GridItemInfo>* aGridItems,
+  const Tracks&    aTracks,
+  uint32_t         aFragmentStartTrack,
+  uint32_t         aFirstExcludedTrack,
+  WritingMode      aWM,
+  const nsSize&    aCBPhysicalSize,
+  nscoord          aCBBorderPaddingStart,
+  nscoord          aCBBorderPaddingEnd,
+  nscoord          aCBSize)
+{
+  const auto axis = aTracks.mAxis;
+  auto firstBaseline = aTracks.mBaseline[BaselineSharingGroup::eFirst];
+  if (!(aBaselineSet & BaselineSet::eFirst)) {
+    mBaseline[axis][BaselineSharingGroup::eFirst] =
+      ::SynthesizeBaselineFromBorderBox(BaselineSharingGroup::eFirst, aWM,
+                                        aCBSize);
+  } else if (firstBaseline == NS_INTRINSIC_WIDTH_UNKNOWN) {
+    FindItemInGridOrderResult gridOrderFirstItem =
+      FindFirstItemInGridOrder(*aIter, *aGridItems,
+        axis == eLogicalAxisBlock ? &GridArea::mRows : &GridArea::mCols,
+        axis == eLogicalAxisBlock ? &GridArea::mCols : &GridArea::mRows,
+        aFragmentStartTrack);
+    mBaseline[axis][BaselineSharingGroup::eFirst] =
+      SynthesizeBaseline(gridOrderFirstItem,
+                         axis,
+                         BaselineSharingGroup::eFirst,
+                         aCBPhysicalSize,
+                         aCBSize,
+                         aWM);
+  } else {
+    // We have a first-baseline group in the start track in this fragment.
+    // Convert it from track to grid container border-box coordinates.
+    MOZ_ASSERT(!aGridItems->IsEmpty());
+    nscoord gapBeforeStartTrack = aFragmentStartTrack == 0 ?
+      aTracks.GridLineEdge(aFragmentStartTrack, GridLineSide::eAfterGridGap) :
+      nscoord(0); // no content gap at start of fragment
+    mBaseline[axis][BaselineSharingGroup::eFirst] =
+      aCBBorderPaddingStart + gapBeforeStartTrack + firstBaseline;
+  }
+
+  auto lastBaseline = aTracks.mBaseline[BaselineSharingGroup::eLast];
+  if (!(aBaselineSet & BaselineSet::eLast)) {
+    mBaseline[axis][BaselineSharingGroup::eLast] =
+      ::SynthesizeBaselineFromBorderBox(BaselineSharingGroup::eLas