merge mozilla-central to autoland. r=merge a=merge
authorSebastian Hengst <archaeopteryx@coole-files.de>
Mon, 11 Sep 2017 11:20:16 +0200
changeset 429516 1ace2189941fdcf4926b08ffaf5521a418d04ee8
parent 429515 8c9d50f961f55d6c144cbbedfa94a6126ea0dab7 (current diff)
parent 429508 f9a5e9ed62103c84e4cde915f4d08f1ce71be83e (diff)
child 429517 064df3f608f52f4cd3287b1bf392b8a1b1403e04
push id7761
push userjlund@mozilla.com
push dateFri, 15 Sep 2017 00:19:52 +0000
treeherdermozilla-beta@c38455951db4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone57.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 mozilla-central to autoland. r=merge a=merge
--- a/docshell/base/crashtests/914521.html
+++ b/docshell/base/crashtests/914521.html
@@ -17,17 +17,20 @@ function f()
     window.addEventListener("popstate", spin);
     window.close();
     window.location = "#c";
     finish();
 }
 
 function init()
 {
-  SpecialPowers.pushPrefEnv({"set": [["security.data_uri.unique_opaque_origin", false]]}, start);
+  SpecialPowers.pushPrefEnv({"set": [
+    ["security.data_uri.unique_opaque_origin", false],
+    ["security.data_uri.block_toplevel_data_uri_navigations", false],
+  ]}, start);
 }
 
 function start()
 {
     var html = "<script>" + f + "<\/script><body onload=f()>";
     var win = window.open("data:text/html," + encodeURIComponent(html), null, "width=300,height=300");
     win.finish = function() {
       SpecialPowers.clearUserPref("security.data_uri.unique_opaque_origin");
--- a/docshell/test/chrome/test_bug364461.xul
+++ b/docshell/test/chrome/test_bug364461.xul
@@ -24,15 +24,21 @@ https://bugzilla.mozilla.org/show_bug.cg
 </body>
 
 <script class="testbody" type="application/javascript">
 <![CDATA[
 
 /** Test for Bug 364461 **/
 
 SimpleTest.waitForExplicitFinish();
-window.open("bug364461_window.xul", "bug364461",
-            "chrome,width=600,height=600");
+
+SpecialPowers.pushPrefEnv({
+  "set":[["security.data_uri.block_toplevel_data_uri_navigations", false]]
+}, runTests);
 
+function runTests() {
+  window.open("bug364461_window.xul", "bug364461",
+              "chrome,width=600,height=600");
+}
 ]]>
 </script>
 
 </window>
new file mode 100644
--- /dev/null
+++ b/docshell/test/file_bug598895_1.html
@@ -0,0 +1,1 @@
+<script>window.onload = function() { opener.postMessage('loaded', '*'); }</script><body>Should show</body>
new file mode 100644
--- /dev/null
+++ b/docshell/test/file_bug598895_2.html
@@ -0,0 +1,1 @@
+<script>window.onload = function() { opener.postMessage('loaded', '*'); }</script><body></body>
new file mode 100644
--- /dev/null
+++ b/docshell/test/file_bug637644_1.html
@@ -0,0 +1,1 @@
+<script>window.onload = function() { opener.postMessage('loaded', '*'); }</script><body>Should show</body>
new file mode 100644
--- /dev/null
+++ b/docshell/test/file_bug637644_2.html
@@ -0,0 +1,1 @@
+<script>window.onload = function() { opener.postMessage('loaded', '*'); }</script><body></body>
--- a/docshell/test/mochitest.ini
+++ b/docshell/test/mochitest.ini
@@ -20,19 +20,23 @@ support-files =
   file_bug385434_1.html
   file_bug385434_2.html
   file_bug385434_3.html
   file_bug475636.sjs
   file_bug509055.html
   file_bug540462.html
   file_bug580069_1.html
   file_bug580069_2.sjs
+  file_bug598895_1.html
+  file_bug598895_2.html
   file_bug590573_1.html
   file_bug590573_2.html
   file_bug634834.html
+  file_bug637644_1.html
+  file_bug637644_2.html
   file_bug640387.html
   file_bug653741.html
   file_bug660404
   file_bug660404^headers^
   file_bug660404-1.html
   file_bug662170.html
   file_bug669671.sjs
   file_bug680257.html
--- a/docshell/test/navigation/NavigationUtils.js
+++ b/docshell/test/navigation/NavigationUtils.js
@@ -4,20 +4,20 @@
 
 ///////////////////////////////////////////////////////////////////////////
 //
 // Utilities for navigation tests
 // 
 ///////////////////////////////////////////////////////////////////////////
 
 var body = "This frame was navigated.";
-var target_url = "data:text/html,<html><body>" + body + "</body></html>";
+var target_url = "navigation_target_url.html"
 
 var popup_body = "This is a popup";
-var target_popup_url = "data:text/html,<html><body>" + popup_body + "</body></html>";
+var target_popup_url = "navigation_target_popup_url.html";
 
 ///////////////////////////////////////////////////////////////////////////
 // Functions that navigate frames
 ///////////////////////////////////////////////////////////////////////////
 
 function navigateByLocation(wnd) {
   try {
     wnd.location = target_url;
@@ -53,27 +53,27 @@ function navigateByHyperlink(name) {
 
 ///////////////////////////////////////////////////////////////////////////
 // Functions that call into Mochitest framework
 ///////////////////////////////////////////////////////////////////////////
 
 function isNavigated(wnd, message) {
   var result = null;
   try {
-    result = SpecialPowers.wrap(wnd).document.body.innerHTML;
+    result = SpecialPowers.wrap(wnd).document.body.innerHTML.trim();
   } catch(ex) {
     result = ex;
   }
   is(result, body, message);
 }
 
 function isBlank(wnd, message) {
   var result = null;
   try {
-    result = wnd.document.body.innerHTML;
+    result = wnd.document.body.innerHTML.trim();
   } catch(ex) {
     result = ex;
   }
   is(result, "This is a blank document.", message);
 }
 
 function isAccessible(wnd, message) {
   try {
@@ -141,18 +141,21 @@ function xpcGetFramesByName(name) {
       results.push(win);
   });
 
   return results;
 }
 
 function xpcCleanupWindows() {
   xpcEnumerateContentWindows(function(win) {
-    if (win.location && win.location.protocol == "data:")
+    if (win.location &&
+        (win.location.href.endsWith(target_url) ||
+         win.location.href.endsWith(target_popup_url))) {
       win.close();
+    }
   });
 }
 
 function xpcWaitForFinishedFrames(callback, numFrames) {
   var finishedFrameCount = 0;
   function frameFinished() {
     finishedFrameCount++;
 
@@ -172,22 +175,22 @@ function xpcWaitForFinishedFrames(callba
     for (var i = 0; i < arr.length; i++) {
       if (obj === arr[i])
         return true;
     }
     return false;
   }
 
   function searchForFinishedFrames(win) {
-    if ((escape(unescape(win.location)) == escape(target_url) ||
-         escape(unescape(win.location)) == escape(target_popup_url)) && 
+    if ((win.location.href.endsWith(target_url) ||
+         win.location.href.endsWith(target_popup_url)) &&
         win.document && 
         win.document.body && 
-        (win.document.body.textContent == body ||
-         win.document.body.textContent == popup_body) && 
+        (win.document.body.textContent.trim() == body ||
+         win.document.body.textContent.trim() == popup_body) && 
         win.document.readyState == "complete") {
 
       var util = win.QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
                     .getInterface(SpecialPowers.Ci.nsIDOMWindowUtils);
       var windowId = util.outerWindowID;
       if (!contains(windowId, finishedWindows)) {
         finishedWindows.push(windowId);
         frameFinished();
--- a/docshell/test/navigation/file_scrollRestoration.html
+++ b/docshell/test/navigation/file_scrollRestoration.html
@@ -35,34 +35,34 @@
           }
           case 3: {
             opener.is(event.persisted, false, "Shouldn't have persisted session history entry.");
             opener.is(window.scrollY, 0, "Should not have restored scrolling.");
             opener.is(history.scrollRestoration, "manual", "Should have the same scrollRestoration as before reload.");
             document.getElementById("bottom").scrollIntoView();
             window.onunload = null; // Should get bfcache behavior.
             opener.setTimeout("SpecialPowers.wrap(testWindow).history.back();", 250);
-            window.location.href = 'data:text/html,';
+            window.location.href = 'about:blank';
             break;
           }
           case 4: {
             opener.is(event.persisted, true, "Should have persisted session history entry.");
             opener.isnot(Math.round(window.scrollY), 0, "Should have kept the old scroll position.");
             opener.is(history.scrollRestoration, "manual", "Should have the same scrollRestoration as before reload.");
             window.scrollTo(0, 0);
             window.location.hash = "hash";
             requestAnimationFrame(test);
             break;
           }
           case 5: {
             opener.isnot(Math.round(window.scrollY), 0, "Should have scrolled to #hash.");
             opener.is(history.scrollRestoration, "manual", "Should have the same scrollRestoration mode as before fragment navigation.");
             window.onunload = function() {} // Disable bfcache.
             opener.setTimeout("is(SpecialPowers.wrap(testWindow).history.scrollRestoration, 'auto'); SpecialPowers.wrap(testWindow).history.back();", 250);
-            window.location.href = 'data:text/html,';
+            window.location.href = 'about:blank';
             break;
           }
           case 6: {
             opener.is(event.persisted, false, "Shouldn't have persisted session history entry.");
             opener.is(window.scrollY, 0, "Shouldn't have kept the old scroll position.");
             opener.is(history.scrollRestoration, "manual", "Should have the same scrollRestoration mode as before fragment navigation.");
             history.scrollRestoration = "auto";
             document.getElementById("bottom").scrollIntoView();
--- a/docshell/test/navigation/mochitest.ini
+++ b/docshell/test/navigation/mochitest.ini
@@ -1,11 +1,13 @@
 [DEFAULT]
 support-files =
   NavigationUtils.js
+  navigation_target_url.html
+  navigation_target_popup_url.html
   blank.html
   file_bug386782_contenteditable.html
   file_bug386782_designmode.html
   redbox_bug430723.html
   bluebox_bug430723.html
   file_bug462076_1.html
   file_bug462076_2.html
   file_bug462076_3.html
new file mode 100644
--- /dev/null
+++ b/docshell/test/navigation/navigation_target_popup_url.html
@@ -0,0 +1,1 @@
+<html><body>This is a popup</body></html>
new file mode 100644
--- /dev/null
+++ b/docshell/test/navigation/navigation_target_url.html
@@ -0,0 +1,1 @@
+<html><body>This frame was navigated.</body></html>
--- a/docshell/test/navigation/open.html
+++ b/docshell/test/navigation/open.html
@@ -1,9 +1,9 @@
 <html>
 <body>
 <script>
 var target = window.location.hash.substring(1);
 document.write("target=" + target);
-window.open("data:text/html,<html><body>This is a popup</body></html>", target, "width=10,height=10");
+window.open("navigation_target_popup_url.html", target, "width=10,height=10");
 </script>
 </body>
 </html>
--- a/docshell/test/navigation/test_triggeringprincipal_window_open.html
+++ b/docshell/test/navigation/test_triggeringprincipal_window_open.html
@@ -8,26 +8,25 @@
 </head>
 <body>
 
 <script type="text/javascript">
 
 /* We call window.open() using different URIs and make sure the triggeringPrincipal
  * loadingPrincipal are correct.
  * Test1: window.open(http:)
- * Test2: window.open(data:)
- * Test3: window.open(javascript:)
+ * Test2: window.open(javascript:)
  */
 
 const TRIGGERING_PRINCIPAL_URI =
   "http://mochi.test:8888/tests/docshell/test/navigation/test_triggeringprincipal_window_open.html";
 
 SimpleTest.waitForExplicitFinish();
 
-const NUM_TESTS = 3;
+const NUM_TESTS = 2;
 var test_counter = 0;
 
 function checkFinish() {
   test_counter++;
   if (test_counter === NUM_TESTS) {
     SimpleTest.finish();
   }
 }
@@ -49,41 +48,17 @@ httpWin.onload = function() {
   is(httpLoadingPrincipal, null,
      "LoadingPrincipal for window.open(http:) should be null");
 
   httpWin.close();
   checkFinish();
 }
 
 // ----------------------------------------------------------------------------
-// Test 2: window.open(data:)
-var dataWin = window.open("data:text/html,<html><script>opener.postMessage('loaded','*');<\/script></html>", "_blank", "width=10,height=10");
-window.onmessage = function (evt) {
-  is(evt.data, "loaded", "message should be loaded");
-
-  var doc = SpecialPowers.wrap(dataWin).document;
-  var dataChannel = doc.docShell.currentDocumentChannel;
-  var dataTriggeringPrincipal = dataChannel.loadInfo.triggeringPrincipal.URI.asciiSpec;
-  var dataLoadingPrincipal = dataChannel.loadInfo.loadingPrincipal;
-
-  is(dataTriggeringPrincipal, TRIGGERING_PRINCIPAL_URI,
-     "TriggeringPrincipal for window.open(data:) should be the principal of the document");
-
-  is(doc.referrer, "",
-     "Referrer for window.open(data:) should be empty");
-
-  is(dataLoadingPrincipal, null,
-     "LoadingPrincipal for window.open(data:) should be null");
-
-  dataWin.close();
-  checkFinish();
-}
-
-// ----------------------------------------------------------------------------
-// Test 3: window.open(javascript:)
+// Test 2: window.open(javascript:)
 var jsWin = window.open("javascript:'<html><body>js</body></html>';", "_blank", "width=10,height=10");
 jsWin.onload = function() {
   var jsChannel = SpecialPowers.wrap(jsWin.document).docShell.currentDocumentChannel;
   var jsTriggeringPrincipal = jsChannel.loadInfo.triggeringPrincipal.URI.asciiSpec;
   var jsLoadingPrincipal = jsChannel.loadInfo.loadingPrincipal;
 
   is(jsTriggeringPrincipal, TRIGGERING_PRINCIPAL_URI,
      "TriggeringPrincipal for window.open(javascript:) should be the principal of the document");
--- a/docshell/test/test_bug598895.html
+++ b/docshell/test/test_bug598895.html
@@ -38,16 +38,15 @@ window.onmessage = function (ev) {
     win3.close();
     ok(compareSnapshots(one, two, true)[0], "Popups should look identical");
     ok(compareSnapshots(one, three, false)[0], "Popups should not look identical");
 
     SimpleTest.finish();
   }
 }
 
-var win2 = window.open("data:text/html,<script>window.onload = function() { opener.postMessage('loaded', '*'); }</" + "script><body>Should show</body>");
-
-var win3 = window.open("data:text/html,<script>window.onload = function() { opener.postMessage('loaded', '*'); }</" + "script><body></body>");
+var win2 = window.open("file_bug598895_1.html");
+var win3 = window.open("file_bug598895_2.html");
 });
 </script>
 </pre>
 </body>
 </html>
--- a/docshell/test/test_bug637644.html
+++ b/docshell/test/test_bug637644.html
@@ -38,16 +38,15 @@ window.onmessage = function (ev) {
     win3.close();
     ok(compareSnapshots(one, two, true)[0], "Popups should look identical");
     ok(compareSnapshots(one, three, false)[0], "Popups should not look identical");
 
     SimpleTest.finish();
   }
 }
 
-var win2 = window.open("data:text/html,<script>window.onload = function() { opener.postMessage('loaded', '*'); }</" + "script><body>Should show</body>", "", "height=500,width=500");
-
-var win3 = window.open("data:text/html,<script>window.onload = function() { opener.postMessage('loaded', '*'); }</" + "script><body></body>", "", "height=500,width=500");
+var win2 = window.open("file_bug637644_1.html", "", "height=500,width=500");
+var win3 = window.open("file_bug637644_2.html", "", "height=500,width=500");
 });
 </script>
 </pre>
 </body>
 </html>
--- a/js/src/jit/AtomicOperations.h
+++ b/js/src/jit/AtomicOperations.h
@@ -126,22 +126,24 @@ class AtomicOperations
     static inline T fetchXorSeqCst(T* addr, T val);
 
     // The SafeWhenRacy functions are to be used when C++ code has to access
     // memory without synchronization and can't guarantee that there won't be a
     // race on the access.  But they are access-atomic for integer data so long
     // as any racing writes are of the same size and to the same address.
 
     // Defined for all the integral types as well as for float32 and float64,
-    // but not access-atomic for floats.
+    // but not access-atomic for floats, nor for int64 and uint64 on 32-bit
+    // platforms.
     template<typename T>
     static inline T loadSafeWhenRacy(T* addr);
 
     // Defined for all the integral types as well as for float32 and float64,
-    // but not access-atomic for floats.
+    // but not access-atomic for floats, nor for int64 and uint64 on 32-bit
+    // platforms.
     template<typename T>
     static inline void storeSafeWhenRacy(T* addr, T val);
 
     // Replacement for memcpy().  No access-atomicity guarantees.
     static inline void memcpySafeWhenRacy(void* dest, const void* src, size_t nbytes);
 
     // Replacement for memmove().  No access-atomicity guarantees.
     static inline void memmoveSafeWhenRacy(void* dest, const void* src, size_t nbytes);
--- a/js/src/jit/arm/AtomicOperations-arm.h
+++ b/js/src/jit/arm/AtomicOperations-arm.h
@@ -151,44 +151,88 @@ js::jit::AtomicOperations::loadSafeWhenR
     MOZ_ASSERT(tier1Constraints(addr));
     T v;
     __atomic_load(addr, &v, __ATOMIC_RELAXED);
     return v;
 }
 
 namespace js { namespace jit {
 
+#define GCC_RACYLOADOP(T)                                       \
+    template<>                                                  \
+    inline T                                                    \
+    js::jit::AtomicOperations::loadSafeWhenRacy(T* addr) {      \
+        return *addr;                                           \
+    }
+
+// On 32-bit platforms, loadSafeWhenRacy need not be access-atomic for 64-bit
+// data, so just use regular accesses instead of the expensive __atomic_load
+// solution which must use LDREXD/CLREX.
+#ifndef JS_64BIT
+GCC_RACYLOADOP(int64_t)
+GCC_RACYLOADOP(uint64_t)
+#endif
+
+// Float and double accesses are not access-atomic.
+GCC_RACYLOADOP(float)
+GCC_RACYLOADOP(double)
+
+// Clang requires a specialization for uint8_clamped.
 template<>
 inline uint8_clamped
 js::jit::AtomicOperations::loadSafeWhenRacy(uint8_clamped* addr)
 {
     uint8_t v;
     __atomic_load(&addr->val, &v, __ATOMIC_RELAXED);
     return uint8_clamped(v);
 }
 
+#undef GCC_RACYLOADOP
+
 } }
 
 template<typename T>
 inline void
 js::jit::AtomicOperations::storeSafeWhenRacy(T* addr, T val)
 {
     MOZ_ASSERT(tier1Constraints(addr));
     __atomic_store(addr, &val, __ATOMIC_RELAXED);
 }
 
 namespace js { namespace jit {
 
+#define GCC_RACYSTOREOP(T)                                         \
+    template<>                                                     \
+    inline void                                                    \
+    js::jit::AtomicOperations::storeSafeWhenRacy(T* addr, T val) { \
+        *addr = val;                                               \
+    }
+
+// On 32-bit platforms, storeSafeWhenRacy need not be access-atomic for 64-bit
+// data, so just use regular accesses instead of the expensive __atomic_store
+// solution which must use LDREXD/STREXD.
+#ifndef JS_64BIT
+GCC_RACYSTOREOP(int64_t)
+GCC_RACYSTOREOP(uint64_t)
+#endif
+
+// Float and double accesses are not access-atomic.
+GCC_RACYSTOREOP(float)
+GCC_RACYSTOREOP(double)
+
+// Clang requires a specialization for uint8_clamped.
 template<>
 inline void
 js::jit::AtomicOperations::storeSafeWhenRacy(uint8_clamped* addr, uint8_clamped val)
 {
     __atomic_store(&addr->val, &val.val, __ATOMIC_RELAXED);
 }
 
+#undef GCC_RACYSTOREOP
+
 } }
 
 inline void
 js::jit::AtomicOperations::memcpySafeWhenRacy(void* dest, const void* src, size_t nbytes)
 {
     MOZ_ASSERT(!((char*)dest <= (char*)src && (char*)src < (char*)dest+nbytes));
     MOZ_ASSERT(!((char*)src <= (char*)dest && (char*)dest < (char*)src+nbytes));
     memcpy(dest, src, nbytes);
--- a/js/src/jit/none/AtomicOperations-feeling-lucky.h
+++ b/js/src/jit/none/AtomicOperations-feeling-lucky.h
@@ -368,63 +368,31 @@ AtomicOperations::fetchXorSeqCst(uint64_
 } }
 #endif
 
 template<typename T>
 inline T
 js::jit::AtomicOperations::loadSafeWhenRacy(T* addr)
 {
     static_assert(sizeof(T) <= 8, "atomics supported up to 8 bytes only");
+    // This is actually roughly right even on 32-bit platforms since in that
+    // case, double, int64, and uint64 loads need not be access-atomic.
     return *addr;
 }
 
-#ifndef HAS_64BIT_ATOMICS
-namespace js { namespace jit {
-
-template<>
-inline int64_t
-AtomicOperations::loadSafeWhenRacy(int64_t* addr) {
-    MOZ_CRASH("No 64-bit atomics");
-}
-
-template<>
-inline uint64_t
-AtomicOperations::loadSafeWhenRacy(uint64_t* addr) {
-    MOZ_CRASH("No 64-bit atomics");
-}
-
-} }
-#endif
-
 template<typename T>
 inline void
 js::jit::AtomicOperations::storeSafeWhenRacy(T* addr, T val)
 {
     static_assert(sizeof(T) <= 8, "atomics supported up to 8 bytes only");
+    // This is actually roughly right even on 32-bit platforms since in that
+    // case, double, int64, and uint64 loads need not be access-atomic.
     *addr = val;
 }
 
-#ifndef HAS_64BIT_ATOMICS
-namespace js { namespace jit {
-
-template<>
-inline void
-AtomicOperations::storeSafeWhenRacy(int64_t* addr, int64_t val) {
-    MOZ_CRASH("No 64-bit atomics");
-}
-
-template<>
-inline void
-AtomicOperations::storeSafeWhenRacy(uint64_t* addr, uint64_t val) {
-    MOZ_CRASH("No 64-bit atomics");
-}
-
-} }
-#endif
-
 inline void
 js::jit::AtomicOperations::memcpySafeWhenRacy(void* dest, const void* src, size_t nbytes)
 {
     MOZ_ASSERT(!((char*)dest <= (char*)src && (char*)src < (char*)dest+nbytes));
     MOZ_ASSERT(!((char*)src <= (char*)dest && (char*)dest < (char*)src+nbytes));
     ::memcpy(dest, src, nbytes);
 }
 
--- a/js/src/jit/x86-shared/AtomicOperations-x86-shared-gcc.h
+++ b/js/src/jit/x86-shared/AtomicOperations-x86-shared-gcc.h
@@ -167,44 +167,88 @@ js::jit::AtomicOperations::loadSafeWhenR
     MOZ_ASSERT(tier1Constraints(addr));
     T v;
     __atomic_load(addr, &v, __ATOMIC_RELAXED);
     return v;
 }
 
 namespace js { namespace jit {
 
+#define GCC_RACYLOADOP(T)                                       \
+    template<>                                                  \
+    inline T                                                    \
+    js::jit::AtomicOperations::loadSafeWhenRacy(T* addr) {      \
+        return *addr;                                           \
+    }
+
+// On 32-bit platforms, loadSafeWhenRacy need not be access-atomic for 64-bit
+// data, so just use regular accesses instead of the expensive __atomic_load
+// solution which must use CMPXCHG8B.
+#ifndef JS_64BIT
+GCC_RACYLOADOP(int64_t)
+GCC_RACYLOADOP(uint64_t)
+#endif
+
+// Float and double accesses are not access-atomic.
+GCC_RACYLOADOP(float)
+GCC_RACYLOADOP(double)
+
+// Clang requires a specialization for uint8_clamped.
 template<>
 inline uint8_clamped
 js::jit::AtomicOperations::loadSafeWhenRacy(uint8_clamped* addr)
 {
     uint8_t v;
     __atomic_load(&addr->val, &v, __ATOMIC_RELAXED);
     return uint8_clamped(v);
 }
 
+#undef GCC_RACYLOADOP
+
 } }
 
 template<typename T>
 inline void
 js::jit::AtomicOperations::storeSafeWhenRacy(T* addr, T val)
 {
     MOZ_ASSERT(tier1Constraints(addr));
     __atomic_store(addr, &val, __ATOMIC_RELAXED);
 }
 
 namespace js { namespace jit {
 
+#define GCC_RACYSTOREOP(T)                                         \
+    template<>                                                     \
+    inline void                                                    \
+    js::jit::AtomicOperations::storeSafeWhenRacy(T* addr, T val) { \
+        *addr = val;                                               \
+    }
+
+// On 32-bit platforms, storeSafeWhenRacy need not be access-atomic for 64-bit
+// data, so just use regular accesses instead of the expensive __atomic_store
+// solution which must use CMPXCHG8B.
+#ifndef JS_64BIT
+GCC_RACYSTOREOP(int64_t)
+GCC_RACYSTOREOP(uint64_t)
+#endif
+
+// Float and double accesses are not access-atomic.
+GCC_RACYSTOREOP(float)
+GCC_RACYSTOREOP(double)
+
+// Clang requires a specialization for uint8_clamped.
 template<>
 inline void
 js::jit::AtomicOperations::storeSafeWhenRacy(uint8_clamped* addr, uint8_clamped val)
 {
     __atomic_store(&addr->val, &val.val, __ATOMIC_RELAXED);
 }
 
+#undef GCC_RACYSTOREOP
+
 } }
 
 inline void
 js::jit::AtomicOperations::memcpySafeWhenRacy(void* dest, const void* src, size_t nbytes)
 {
     MOZ_ASSERT(!((char*)dest <= (char*)src && (char*)src < (char*)dest+nbytes));
     MOZ_ASSERT(!((char*)src <= (char*)dest && (char*)dest < (char*)src+nbytes));
     ::memcpy(dest, src, nbytes);
--- a/js/src/jit/x86-shared/AtomicOperations-x86-shared-msvc.h
+++ b/js/src/jit/x86-shared/AtomicOperations-x86-shared-msvc.h
@@ -335,77 +335,31 @@ MSC_FETCHBITOP(uint64_t, __int64, _Inter
 #undef MSC_FETCHBITOP_CAS
 #undef MSC_FETCHBITOP
 
 template<typename T>
 inline T
 js::jit::AtomicOperations::loadSafeWhenRacy(T* addr)
 {
     MOZ_ASSERT(tier1Constraints(addr));
+    // This is also appropriate for double, int64, and uint64 on 32-bit
+    // platforms since there are no guarantees of access-atomicity.
     return *addr;
 }
 
-#ifdef _M_IX86
-# define MSC_RACYLOADOP(T)                        \
-    template<>                                    \
-    inline T                                      \
-    AtomicOperations::loadSafeWhenRacy(T* addr) { \
-        MOZ_ASSERT(tier1Constraints(addr));       \
-        return (T)_InterlockedCompareExchange64((__int64 volatile*)addr, 0, 0); \
-    }
-
-namespace js { namespace jit {
-
-// For double and float there are no access-atomicity guarantees so go directly
-// to the default implementation.
-MSC_RACYLOADOP(int64_t)
-MSC_RACYLOADOP(uint64_t)
-
-} }
-
-# undef MSC_RACYLOADOP
-#endif // _M_IX86
-
 template<typename T>
 inline void
 js::jit::AtomicOperations::storeSafeWhenRacy(T* addr, T val)
 {
     MOZ_ASSERT(tier1Constraints(addr));
+    // This is also appropriate for double, int64, and uint64 on 32-bit
+    // platforms since there are no guarantees of access-atomicity.
     *addr = val;
 }
 
-#ifdef _M_IX86
-namespace js { namespace jit {
-
-# define MSC_RACYSTOREOP(T)                               \
-    template<>                                            \
-    inline void                                           \
-    AtomicOperations::storeSafeWhenRacy(T* addr, T val) { \
-        MOZ_ASSERT(tier1Constraints(addr));               \
-        T oldval = *addr;                                 \
-        for (;;) {                                        \
-            T nextval = (T)_InterlockedCompareExchange64((__int64 volatile*)addr, \
-                                                         (__int64)val,            \
-                                                         (__int64)oldval);        \
-            if (nextval == oldval)                        \
-                break;                                    \
-            oldval = nextval;                             \
-        }                                                 \
-    }
-
-// For double and float there are no access-atomicity guarantees so go directly
-// to the default implementation.
-MSC_RACYSTOREOP(int64_t)
-MSC_RACYSTOREOP(uint64_t)
-
-# undef MSC_STOREOP
-
-} }
-#endif // _M_IX86
-
 inline void
 js::jit::AtomicOperations::memcpySafeWhenRacy(void* dest, const void* src, size_t nbytes)
 {
     MOZ_ASSERT(!((char*)dest <= (char*)src && (char*)src < (char*)dest+nbytes));
     MOZ_ASSERT(!((char*)src <= (char*)dest && (char*)dest < (char*)src+nbytes));
     ::memcpy(dest, src, nbytes);
 }
 
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -8323,18 +8323,22 @@ main(int argc, char** argv, char** envp)
         || !op.addBoolOption('\0', "code-coverage", "Enable code coverage instrumentation.")
 #ifdef DEBUG
         || !op.addBoolOption('O', "print-alloc", "Print the number of allocations at exit")
 #endif
         || !op.addOptionalStringArg("script", "A script to execute (after all options)")
         || !op.addOptionalMultiStringArg("scriptArgs",
                                          "String arguments to bind as |scriptArgs| in the "
                                          "shell's global")
-        || !op.addIntOption('\0', "thread-count", "COUNT", "Use COUNT auxiliary threads "
-                            "(default: # of cores - 1)", -1)
+        || !op.addIntOption('\0', "cpu-count", "COUNT",
+                            "Set the number of CPUs (hardware threads) to COUNT, the "
+                            "default is the actual number of CPUs. The total number of "
+                            "background helper threads is the CPU count plus some constant.",
+                            -1)
+        || !op.addIntOption('\0', "thread-count", "COUNT", "Alias for --cpu-count.", -1)
         || !op.addBoolOption('\0', "ion", "Enable IonMonkey (default)")
         || !op.addBoolOption('\0', "no-ion", "Disable IonMonkey")
         || !op.addBoolOption('\0', "no-asmjs", "Disable asm.js compilation")
         || !op.addBoolOption('\0', "no-wasm", "Disable WebAssembly compilation")
         || !op.addBoolOption('\0', "no-wasm-baseline", "Disable wasm baseline compiler")
         || !op.addBoolOption('\0', "no-wasm-ion", "Disable wasm ion compiler")
         || !op.addBoolOption('\0', "no-native-regexp", "Disable native regexp compilation")
         || !op.addBoolOption('\0', "no-unboxed-objects", "Disable creating unboxed plain objects")
@@ -8511,21 +8515,23 @@ main(int argc, char** argv, char** envp)
 
     // Start the engine.
     if (!JS_Init())
         return 1;
 
     if (!InitSharedArrayBufferMailbox())
         return 1;
 
-    // The fake thread count must be set before initializing the Runtime,
+    // The fake CPU count must be set before initializing the Runtime,
     // which spins up the thread pool.
-    int32_t threadCount = op.getIntOption("thread-count");
-    if (threadCount >= 0)
-        SetFakeCPUCount(threadCount);
+    int32_t cpuCount = op.getIntOption("cpu-count"); // What we're really setting
+    if (cpuCount < 0)
+        cpuCount = op.getIntOption("thread-count");  // Legacy name
+    if (cpuCount >= 0)
+        SetFakeCPUCount(cpuCount);
 
     size_t nurseryBytes = JS::DefaultNurseryBytes;
     nurseryBytes = op.getIntOption("nursery-size") * 1024L * 1024L;
 
     /* Use the same parameters as the browser in xpcjsruntime.cpp. */
     JSContext* cx = JS_NewContext(JS::DefaultHeapMaxBytes, nurseryBytes);
     if (!cx)
         return 1;
--- a/js/xpconnect/src/XPCConvert.cpp
+++ b/js/xpconnect/src/XPCConvert.cpp
@@ -5,17 +5,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* Data conversion between native and JavaScript types. */
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Range.h"
 
 #include "xpcprivate.h"
-#include "nsIAtom.h"
 #include "nsIScriptError.h"
 #include "nsWrapperCache.h"
 #include "nsJSUtils.h"
 #include "nsQueryObject.h"
 #include "nsScriptError.h"
 #include "WrapperFactory.h"
 
 #include "nsWrapperCacheInlines.h"
@@ -686,30 +685,17 @@ XPCConvert::JSData2Native(void* d, Handl
 
         if (iid->Equals(NS_GET_IID(nsIVariant))) {
             nsCOMPtr<nsIVariant> variant = XPCVariant::newVariant(cx, s);
             if (!variant)
                 return false;
 
             variant.forget(static_cast<nsISupports**>(d));
             return true;
-        } else if (iid->Equals(NS_GET_IID(nsIAtom)) && s.isString()) {
-            // We're trying to pass a string as an nsIAtom.  Let's atomize!
-            JSString* str = s.toString();
-            nsAutoJSString autoStr;
-            if (!autoStr.init(cx, str)) {
-                if (pErr)
-                    *pErr = NS_ERROR_XPC_BAD_CONVERT_JS_NULL_REF;
-                return false;
-            }
-            nsCOMPtr<nsIAtom> atom = NS_Atomize(autoStr);
-            atom.forget((nsISupports**)d);
-            return true;
         }
-        //else ...
 
         if (s.isNullOrUndefined()) {
             *((nsISupports**)d) = nullptr;
             return true;
         }
 
         // only wrap JSObjects
         if (!s.isObject()) {
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -1604,16 +1604,18 @@ MoveChildrenTo(nsIFrame* aOldParent,
 nsCSSFrameConstructor::nsCSSFrameConstructor(nsIDocument* aDocument,
                                              nsIPresShell* aPresShell)
   : nsFrameManager(aPresShell)
   , mDocument(aDocument)
   , mRootElementFrame(nullptr)
   , mRootElementStyleFrame(nullptr)
   , mDocElementContainingBlock(nullptr)
   , mPageSequenceFrame(nullptr)
+  , mFirstFreeFCItem(nullptr)
+  , mFCItemsInUse(0)
   , mCurrentDepth(0)
 #ifdef DEBUG
   , mUpdateCount(0)
 #endif
   , mQuotesDirty(false)
   , mCountersDirty(false)
   , mIsDestroyingFrameTree(false)
   , mHasRootAbsPosContainingBlock(false)
@@ -2679,20 +2681,20 @@ nsCSSFrameConstructor::ConstructDocEleme
     // XXXbz on the other hand, if we converted this whole function to
     // FrameConstructionData/Item, then we'd need the right function
     // here... but would probably be able to get away with less code in this
     // function in general.
     // Use a null PendingBinding, since our binding is not in fact pending.
     static const FrameConstructionData rootSVGData = FCDATA_DECL(0, nullptr);
     already_AddRefed<nsStyleContext> extraRef =
       RefPtr<nsStyleContext>(styleContext).forget();
-    FrameConstructionItem item(&rootSVGData, aDocElement,
-                               aDocElement->NodeInfo()->NameAtom(),
-                               kNameSpaceID_SVG, nullptr, extraRef, true,
-                               nullptr);
+    AutoFrameConstructionItem item(this, &rootSVGData, aDocElement,
+                                   aDocElement->NodeInfo()->NameAtom(),
+                                   kNameSpaceID_SVG, nullptr, extraRef, true,
+                                   nullptr);
 
     nsFrameItems frameItems;
     contentFrame = static_cast<nsContainerFrame*>(
       ConstructOuterSVG(state, item, mDocElementContainingBlock,
                         styleContext->StyleDisplay(),
                         frameItems));
     newFrame = frameItems.FirstChild();
     NS_ASSERTION(frameItems.OnlyChild(), "multiple root element frames");
@@ -2729,20 +2731,20 @@ nsCSSFrameConstructor::ConstructDocEleme
     // XXXbz on the other hand, if we converted this whole function to
     // FrameConstructionData/Item, then we'd need the right function
     // here... but would probably be able to get away with less code in this
     // function in general.
     // Use a null PendingBinding, since our binding is not in fact pending.
     static const FrameConstructionData rootTableData = FCDATA_DECL(0, nullptr);
     already_AddRefed<nsStyleContext> extraRef =
       RefPtr<nsStyleContext>(styleContext).forget();
-    FrameConstructionItem item(&rootTableData, aDocElement,
-                               aDocElement->NodeInfo()->NameAtom(),
-                               kNameSpaceID_None, nullptr, extraRef, true,
-                               nullptr);
+    AutoFrameConstructionItem item(this, &rootTableData, aDocElement,
+                                   aDocElement->NodeInfo()->NameAtom(),
+                                   kNameSpaceID_None, nullptr, extraRef, true,
+                                   nullptr);
 
     nsFrameItems frameItems;
     // if the document is a table then just populate it.
     contentFrame = static_cast<nsContainerFrame*>(
       ConstructTable(state, item, mDocElementContainingBlock,
                      styleContext->StyleDisplay(),
                      frameItems));
     newFrame = frameItems.FirstChild();
@@ -3068,17 +3070,17 @@ nsCSSFrameConstructor::ConstructAnonymou
   NS_ASSERTION(aFrame->IsCanvasFrame(), "aFrame should be canvas frame!");
 
   AutoTArray<nsIAnonymousContentCreator::ContentInfo, 4> anonymousItems;
   GetAnonymousContent(aDocElement, aFrame, anonymousItems);
   if (anonymousItems.IsEmpty()) {
     return;
   }
 
-  FrameConstructionItemList itemsToConstruct;
+  AutoFrameConstructionItemList itemsToConstruct(this);
   nsContainerFrame* frameAsContainer = do_QueryFrame(aFrame);
   AddFCItemsForAnonymousContent(aState, frameAsContainer, anonymousItems, itemsToConstruct);
 
   nsFrameItems frameItems;
   ConstructFramesFromItemList(aState, itemsToConstruct, frameAsContainer,
                               /* aParentIsWrapperAnonBox = */ false,
                               frameItems);
   frameAsContainer->AppendFrames(kPrincipalList, frameItems);
@@ -3277,17 +3279,17 @@ nsCSSFrameConstructor::ConstructSelectFr
     MOZ_ASSERT(newAnonymousItems[0].mContent == comboboxFrame->GetDisplayNode());
     newAnonymousItems.RemoveElementAt(0);
     nsIFrame* customFrame = comboboxFrame->CreateFrameForDisplayNode();
     MOZ_ASSERT(customFrame);
     customFrame->AddStateBits(NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT);
     childItems.AddChild(customFrame);
 
     // The other piece of NAC can take the normal path.
-    FrameConstructionItemList fcItems;
+    AutoFrameConstructionItemList fcItems(this);
     AddFCItemsForAnonymousContent(aState, comboboxFrame, newAnonymousItems,
                                   fcItems);
     ConstructFramesFromItemList(aState, fcItems, comboboxFrame,
                                 /* aParentIsWrapperAnonBox = */ false,
                                 childItems);
 
     comboboxFrame->SetInitialChildList(kPrincipalList, childItems);
 
@@ -4718,17 +4720,17 @@ nsCSSFrameConstructor::BeginBuildingScro
   if (scrollNAC.Length() > 0) {
     TreeMatchContext::AutoAncestorPusher ancestorPusher(aState.mTreeMatchContext);
     if (aState.HasAncestorFilter()) {
       ancestorPusher.PushAncestorAndStyleScope(aContent->AsElement());
     } else {
       ancestorPusher.PushStyleScope(aContent->AsElement());
     }
 
-    FrameConstructionItemList items;
+    AutoFrameConstructionItemList items(this);
     AddFCItemsForAnonymousContent(aState, gfxScrollFrame, scrollNAC, items);
     ConstructFramesFromItemList(aState, items, gfxScrollFrame,
                                 /* aParentIsWrapperAnonBox = */ false,
                                 anonymousItems);
   }
 
   aNewFrame = gfxScrollFrame;
   gfxScrollFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
@@ -5745,17 +5747,17 @@ nsCSSFrameConstructor::AddPageBreakItem(
   MOZ_ASSERT(pseudoStyle->StyleDisplay()->mDisplay == StyleDisplay::Block,
              "Unexpected display");
 
   static const FrameConstructionData sPageBreakData =
     FCDATA_DECL(FCDATA_SKIP_FRAMESET, NS_NewPageBreakFrame);
 
   // Lie about the tag and namespace so we don't trigger anything
   // interesting during frame construction.
-  aItems.AppendItem(&sPageBreakData, aContent, nsCSSAnonBoxes::pageBreak,
+  aItems.AppendItem(this, &sPageBreakData, aContent, nsCSSAnonBoxes::pageBreak,
                     kNameSpaceID_None, nullptr, pseudoStyle.forget(),
                     true, nullptr);
 }
 
 bool
 nsCSSFrameConstructor::ShouldCreateItemsForChild(nsFrameConstructorState& aState,
                                                  nsIContent* aContent,
                                                  nsContainerFrame* aParentFrame)
@@ -6163,24 +6165,24 @@ nsCSSFrameConstructor::AddFrameConstruct
   }
 
   FrameConstructionItem* item = nullptr;
   if (details && details->Open()) {
     auto* summary = HTMLSummaryElement::FromContentOrNull(aContent);
     if (summary && summary->IsMainSummary()) {
       // If details is open, the main summary needs to be rendered as if it is
       // the first child, so add the item to the front of the item list.
-      item = aItems.PrependItem(data, aContent, aTag, aNameSpaceID,
+      item = aItems.PrependItem(this, data, aContent, aTag, aNameSpaceID,
                                 pendingBinding, styleContext.forget(),
                                 aSuppressWhiteSpaceOptimizations, aAnonChildren);
     }
   }
 
   if (!item) {
-    item = aItems.AppendItem(data, aContent, aTag, aNameSpaceID,
+    item = aItems.AppendItem(this, data, aContent, aTag, aNameSpaceID,
                              pendingBinding, styleContext.forget(),
                              aSuppressWhiteSpaceOptimizations, aAnonChildren);
   }
   item->mIsText = isText;
   item->mIsGeneratedContent = isGeneratedContent;
   item->mIsAnonymousContentCreatorContent =
     aFlags & ITEM_IS_ANONYMOUSCONTENTCREATOR_CONTENT;
   if (isGeneratedContent) {
@@ -7790,17 +7792,17 @@ nsCSSFrameConstructor::ContentAppended(n
                                 GetAbsoluteContainingBlock(parentFrame, FIXED_POS),
                                 GetAbsoluteContainingBlock(parentFrame, ABS_POS),
                                 containingBlock);
 
   LayoutFrameType frameType = parentFrame->Type();
 
   FlattenedChildIterator iter(aContainer);
   bool haveNoXBLChildren = (!iter.XBLInvolved() || !iter.GetNextChild());
-  FrameConstructionItemList items;
+  AutoFrameConstructionItemList items(this);
   if (aFirstNewContent->GetPreviousSibling() &&
       GetParentType(frameType) == eTypeBlock &&
       haveNoXBLChildren) {
     // If there's a text node in the normal content list just before the new
     // items, and it has no frame, make a frame construction item for it. If it
     // doesn't need a frame, ConstructFramesFromItemList below won't give it
     // one.  No need to do all this if our parent type is not block, though,
     // since WipeContainingBlock already handles that situation.
@@ -8361,17 +8363,17 @@ nsCSSFrameConstructor::ContentRangeInser
       // Insert the new frames after the last continuation of the :before
       prevSibling = firstChild->GetTailContinuation();
       insertion.mParentFrame = prevSibling->GetParent()->GetContentInsertionFrame();
       // Don't change isAppend here; we'll can call AppendFrames as needed, and
       // the change to our prevSibling doesn't affect that.
     }
   }
 
-  FrameConstructionItemList items;
+  AutoFrameConstructionItemList items(this);
   ParentType parentType = GetParentType(frameType);
   FlattenedChildIterator iter(aContainer);
   bool haveNoXBLChildren = (!iter.XBLInvolved() || !iter.GetNextChild());
   if (aStartChild->GetPreviousSibling() &&
       parentType == eTypeBlock && haveNoXBLChildren) {
     // If there's a text node in the normal content list just before the
     // new nodes, and it has no frame, make a frame construction item for
     // it, because it might need a frame now.  No need to do this if our
@@ -9497,17 +9499,17 @@ nsCSSFrameConstructor::ReplicateFixedFra
     nsIFrame* prevPlaceholder = fixed->GetPlaceholderFrame();
     if (prevPlaceholder &&
         nsLayoutUtils::IsProperAncestorFrame(prevCanvasFrame, prevPlaceholder)) {
       // We want to use the same style as the primary style frame for
       // our content
       nsIContent* content = fixed->GetContent();
       nsStyleContext* styleContext =
         nsLayoutUtils::GetStyleFrame(content)->StyleContext();
-      FrameConstructionItemList items;
+      AutoFrameConstructionItemList items(this);
       AddFrameConstructionItemsInternal(state, content, canvasFrame,
                                         content->NodeInfo()->NameAtom(),
                                         content->GetNameSpaceID(),
                                         true,
                                         styleContext,
                                         ITEM_ALLOW_XBL_BASE |
                                           ITEM_ALLOW_PAGE_BREAK,
                                         nullptr, items);
@@ -10297,17 +10299,17 @@ nsCSSFrameConstructor::CreateNeededAnonF
       bool hitEnd = afterWhitespaceIter.SkipWhitespace(aState);
       bool nextChildNeedsAnonItem =
         !hitEnd &&
         afterWhitespaceIter.item().NeedsAnonFlexOrGridItem(aState, isWebkitBox);
 
       if (!nextChildNeedsAnonItem) {
         // There's nothing after the whitespace that we need to wrap, so we
         // just drop this run of whitespace.
-        iter.DeleteItemsTo(afterWhitespaceIter);
+        iter.DeleteItemsTo(this, afterWhitespaceIter);
         if (hitEnd) {
           // Nothing left to do -- we're finished!
           return;
         }
         // else, we have a next child and it does not want to be wrapped.  So,
         // we jump back to the beginning of the loop to skip over that child
         // (and anything else non-wrappable after it)
         MOZ_ASSERT(!iter.IsDone() &&
@@ -10339,17 +10341,17 @@ nsCSSFrameConstructor::CreateNeededAnonF
 
     static const FrameConstructionData sBlockFormattingContextFCData =
       FCDATA_DECL(FCDATA_SKIP_FRAMESET |
                   FCDATA_USE_CHILD_ITEMS |
                   FCDATA_IS_WRAPPER_ANON_BOX,
                   NS_NewBlockFormattingContext);
 
     FrameConstructionItem* newItem =
-      new FrameConstructionItem(&sBlockFormattingContextFCData,
+      new (this) FrameConstructionItem(&sBlockFormattingContextFCData,
                                 // Use the content of our parent frame
                                 parentContent,
                                 // Lie about the tag; it doesn't matter anyway
                                 pseudoType,
                                 iter.item().mNameSpaceID,
                                 // no pending binding
                                 nullptr,
                                 wrapperStyle,
@@ -10370,17 +10372,17 @@ nsCSSFrameConstructor::CreateNeededAnonF
     newItem->mChildItems.SetLineBoundaryAtEnd(true);
     // The parent of the items in aItems is also the parent of the items
     // in mChildItems
     newItem->mChildItems.SetParentHasNoXBLChildren(
       aItems.ParentHasNoXBLChildren());
 
     // Eat up all items between |iter| and |endIter| and put them in our
     // wrapper. This advances |iter| to point to |endIter|.
-    iter.AppendItemsToList(endIter, newItem->mChildItems);
+    iter.AppendItemsToList(this, endIter, newItem->mChildItems);
 
     iter.InsertItem(newItem);
   } while (!iter.IsDone());
 }
 
 /* static */ nsCSSFrameConstructor::RubyWhitespaceType
 nsCSSFrameConstructor::ComputeRubyWhitespaceType(StyleDisplay aPrevDisplay,
                                                  StyleDisplay aNextDisplay)
@@ -10503,17 +10505,17 @@ nsCSSFrameConstructor::WrapItemsInPseudo
     // trailing whitespace described in the spec have been trimmed at
     // this point. With this precondition, it is safe not to check
     // whether contentEndIter has been done.
     RubyWhitespaceType whitespaceType =
       InterpretRubyWhitespace(aState, endIter, contentEndIter);
     if (whitespaceType == eRubyInterLevelWhitespace) {
       // Remove inter-level whitespace.
       bool atStart = (aIter == endIter);
-      endIter.DeleteItemsTo(contentEndIter);
+      endIter.DeleteItemsTo(this, contentEndIter);
       if (atStart) {
         aIter = endIter;
       }
     } else if (whitespaceType == eRubyInterSegmentWhitespace) {
       // If this level container starts with inter-segment whitespaces,
       // wrap them. Break at contentEndIter. Otherwise, leave it here.
       // Break at endIter. They will be wrapped when we are here again.
       if (aIter == endIter) {
@@ -10552,33 +10554,33 @@ nsCSSFrameConstructor::TrimLeadingAndTra
     nsFrameConstructorState& aState,
     FrameConstructionItemList& aItems)
 {
   FCItemIterator iter(aItems);
   if (!iter.IsDone() &&
       iter.item().IsWhitespace(aState)) {
     FCItemIterator spaceEndIter(iter);
     spaceEndIter.SkipWhitespace(aState);
-    iter.DeleteItemsTo(spaceEndIter);
+    iter.DeleteItemsTo(this, spaceEndIter);
   }
 
   iter.SetToEnd();
   if (!iter.AtStart()) {
     FCItemIterator spaceEndIter(iter);
     do {
       iter.Prev();
       if (iter.AtStart()) {
         // It's fine to not check the first item, because we
         // should have trimmed leading whitespaces above.
         break;
       }
     } while (iter.item().IsWhitespace(aState));
     iter.Next();
     if (iter != spaceEndIter) {
-      iter.DeleteItemsTo(spaceEndIter);
+      iter.DeleteItemsTo(this, spaceEndIter);
     }
   }
 }
 
 /**
  * This function walks through the child list (aItems) and creates
  * needed pseudo ruby boxes to wrap misparented children.
  */
@@ -10699,17 +10701,17 @@ nsCSSFrameConstructor::CreateNeededPseud
           //    tabular container according to rule 1.3 of CSS 2.1 Sec 17.2.1.
           //    (Being a tabular container pretty much means ourParentType is
           //    not eTypeBlock besides the eTypeColGroup case, which won't
           //    reach here.)
           if ((!trailingSpaces &&
                IsTableParentType(spaceEndIter.item().DesiredParentType())) ||
               (trailingSpaces && ourParentType != eTypeBlock)) {
             bool updateStart = (iter == endIter);
-            endIter.DeleteItemsTo(spaceEndIter);
+            endIter.DeleteItemsTo(this, spaceEndIter);
             NS_ASSERTION(trailingSpaces == endIter.IsDone(),
                          "These should match");
 
             if (updateStart) {
               iter = endIter;
             }
 
             if (trailingSpaces) {
@@ -10844,17 +10846,17 @@ nsCSSFrameConstructor::WrapItemsInPseudo
       mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(pseudoType,
                                                                  aParentStyle);
   } else {
     wrapperStyle =
       mPresShell->StyleSet()->ResolveNonInheritingAnonymousBoxStyle(pseudoType);
   }
 
   FrameConstructionItem* newItem =
-    new FrameConstructionItem(&pseudoData.mFCData,
+    new (this) FrameConstructionItem(&pseudoData.mFCData,
                               // Use the content of our parent frame
                               aParentContent,
                               // Lie about the tag; it doesn't matter anyway
                               pseudoType,
                               // The namespace does matter, however; it needs
                               // to match that of our first child item to
                               // match the old behavior
                               aIter.item().mNameSpaceID,
@@ -10885,17 +10887,17 @@ nsCSSFrameConstructor::WrapItemsInPseudo
   }
   // The parent of the items in aItems is also the parent of the items
   // in mChildItems
   newItem->mChildItems.SetParentHasNoXBLChildren(
       aIter.List()->ParentHasNoXBLChildren());
 
   // Eat up all items between |aIter| and |aEndIter| and put them in our
   // wrapper Advances |aIter| to point to |aEndIter|.
-  aIter.AppendItemsToList(aEndIter, newItem->mChildItems);
+  aIter.AppendItemsToList(this, aEndIter, newItem->mChildItems);
 
   aIter.InsertItem(newItem);
 }
 
 void
 nsCSSFrameConstructor::CreateNeededPseudoSiblings(
     nsFrameConstructorState& aState,
     FrameConstructionItemList& aItems,
@@ -10915,27 +10917,27 @@ nsCSSFrameConstructor::CreateNeededPseud
                "Child of ruby frame should either a rbc or a rtc");
 
   const PseudoParentData& pseudoData =
     sPseudoParentData[eTypeRubyBaseContainer];
   already_AddRefed<nsStyleContext> pseudoStyle = mPresShell->StyleSet()->
     ResolveInheritingAnonymousBoxStyle(*pseudoData.mPseudoType,
                                        aParentFrame->StyleContext());
   FrameConstructionItem* newItem =
-    new FrameConstructionItem(&pseudoData.mFCData,
-                              // Use the content of the parent frame
-                              aParentFrame->GetContent(),
-                              // Tag type
-                              *pseudoData.mPseudoType,
-                              // Use the namespace of the rtc frame
-                              iter.item().mNameSpaceID,
-                              // no pending binding
-                              nullptr,
-                              pseudoStyle,
-                              true, nullptr);
+    new (this) FrameConstructionItem(&pseudoData.mFCData,
+                                     // Use the content of the parent frame
+                                     aParentFrame->GetContent(),
+                                     // Tag type
+                                     *pseudoData.mPseudoType,
+                                     // Use the namespace of the rtc frame
+                                     iter.item().mNameSpaceID,
+                                     // no pending binding
+                                     nullptr,
+                                     pseudoStyle,
+                                     true, nullptr);
   newItem->mIsAllInline = true;
   newItem->mChildItems.SetParentHasNoXBLChildren(true);
   iter.InsertItem(newItem);
 }
 
 #ifdef DEBUG
 /**
  * Returns true iff aFrame should be wrapped in an anonymous flex/grid item,
@@ -11231,17 +11233,17 @@ nsCSSFrameConstructor::ProcessChildren(n
     aState.PushFloatContainingBlock(nullptr, floatSaveState);
   } else if (aFrame->IsFloatContainingBlock()) {
     aState.PushFloatContainingBlock(aFrame, floatSaveState);
   }
 
   nsFrameConstructorState::PendingBindingAutoPusher pusher(aState,
                                                            aPendingBinding);
 
-  FrameConstructionItemList itemsToConstruct;
+  AutoFrameConstructionItemList itemsToConstruct(this);
 
   // If we have first-letter or first-line style then frames can get
   // moved around so don't set these flags.
   if (aAllowBlockStyles && !haveFirstLetterStyle && !haveFirstLineStyle) {
     itemsToConstruct.SetLineBoundaryAtStart(true);
     itemsToConstruct.SetLineBoundaryAtEnd(true);
   }
 
@@ -12309,17 +12311,17 @@ nsCSSFrameConstructor::CreateListBoxCont
 
     if (StyleDisplay::None == display->mDisplay) {
       *aNewFrame = nullptr;
       return;
     }
 
     BeginUpdate();
 
-    FrameConstructionItemList items;
+    AutoFrameConstructionItemList items(this);
     AddFrameConstructionItemsInternal(state, aChild, aParentFrame,
                                       aChild->NodeInfo()->NameAtom(),
                                       aChild->GetNameSpaceID(),
                                       true, styleContext,
                                       ITEM_ALLOW_XBL_BASE, nullptr, items);
     ConstructFramesFromItemList(state, items, aParentFrame,
                                 /* aParentIsWrapperAnonBox = */ false,
                                 frameItems);
@@ -12984,17 +12986,17 @@ nsCSSFrameConstructor::WipeContainingBlo
             NS_ASSERTION(!IsTablePseudo(aFrame), "How did that happen?");
           }
 #endif
         } else {
           okToDrop = (spaceEndIter.item().DesiredParentType() == parentType);
         }
 
         if (okToDrop) {
-          iter.DeleteItemsTo(spaceEndIter);
+          iter.DeleteItemsTo(this, spaceEndIter);
         } else {
           // We're done: we don't want to drop the whitespace, and it has the
           // wrong parent type.
           break;
         }
 
         // Now loop, since |iter| points to item right after the whitespace we
         // removed.
@@ -13377,17 +13379,17 @@ Iterator::AppendItemToList(FrameConstruc
   aTargetList.mItems.insertBack(item);
 
   mList.AdjustCountsForItem(item, -1);
   aTargetList.AdjustCountsForItem(item, 1);
 }
 
 void
 nsCSSFrameConstructor::FrameConstructionItemList::
-Iterator::AppendItemsToList(const Iterator& aEnd,
+Iterator::AppendItemsToList(nsCSSFrameConstructor* aFCtor, const Iterator& aEnd,
                             FrameConstructionItemList& aTargetList)
 {
   NS_ASSERTION(&aTargetList != &mList, "Unexpected call");
   NS_PRECONDITION(&mList == &aEnd.mList, "End iterator for some other list?");
 
   // We can't just move our guts to the other list if it already has
   // some information or if we're not moving our entire list.
   if (!AtStart() || !aEnd.IsDone() || !aTargetList.IsEmpty() ||
@@ -13408,18 +13410,17 @@ Iterator::AppendItemsToList(const Iterat
   aTargetList.mItemCount = mList.mItemCount;
   memcpy(aTargetList.mDesiredParentCounts, mList.mDesiredParentCounts,
          sizeof(aTargetList.mDesiredParentCounts));
 
   // Swap out undisplayed item arrays, before we nuke the array on our end
   aTargetList.mUndisplayedItems.SwapElements(mList.mUndisplayedItems);
 
   // reset mList
-  mList.~FrameConstructionItemList();
-  new (&mList) FrameConstructionItemList();
+  mList.Reset(aFCtor);
 
   // Point ourselves to aEnd, as advertised
   SetToEnd();
   NS_POSTCONDITION(*this == aEnd, "How did that happen?");
 }
 
 void
 nsCSSFrameConstructor::FrameConstructionItemList::
@@ -13433,28 +13434,28 @@ Iterator::InsertItem(FrameConstructionIt
   }
   mList.AdjustCountsForItem(aItem, 1);
 
   NS_POSTCONDITION(aItem->getNext() == mCurrent, "How did that happen?");
 }
 
 void
 nsCSSFrameConstructor::FrameConstructionItemList::
-Iterator::DeleteItemsTo(const Iterator& aEnd)
+Iterator::DeleteItemsTo(nsCSSFrameConstructor* aFCtor, const Iterator& aEnd)
 {
   NS_PRECONDITION(&mList == &aEnd.mList, "End iterator for some other list?");
   NS_PRECONDITION(*this != aEnd, "Shouldn't be at aEnd yet");
 
   do {
     NS_ASSERTION(!IsDone(), "Ran off end of list?");
     FrameConstructionItem* item = mCurrent;
     Next();
     item->remove();
     mList.AdjustCountsForItem(item, -1);
-    delete item;
+    item->Delete(aFCtor);
   } while (*this != aEnd);
 }
 
 void
 nsCSSFrameConstructor::QuotesDirty()
 {
   NS_PRECONDITION(mUpdateCount != 0, "Instant quote updates are bad news");
   mQuotesDirty = true;
@@ -13463,8 +13464,38 @@ nsCSSFrameConstructor::QuotesDirty()
 
 void
 nsCSSFrameConstructor::CountersDirty()
 {
   NS_PRECONDITION(mUpdateCount != 0, "Instant counter updates are bad news");
   mCountersDirty = true;
   mPresShell->SetNeedLayoutFlush();
 }
+
+void*
+nsCSSFrameConstructor::AllocateFCItem()
+{
+  void* item;
+  if (mFirstFreeFCItem) {
+    item = mFirstFreeFCItem;
+    mFirstFreeFCItem = mFirstFreeFCItem->mNext;
+  } else {
+    item = mFCItemPool.Allocate(sizeof(FrameConstructionItem));
+  }
+  ++mFCItemsInUse;
+  return item;
+}
+
+void
+nsCSSFrameConstructor::FreeFCItem(FrameConstructionItem* aItem)
+{
+  MOZ_ASSERT(mFCItemsInUse != 0);
+  if (--mFCItemsInUse == 0) {
+    // The arena is now unused - clear it but retain one chunk.
+    mFirstFreeFCItem = nullptr;
+    mFCItemPool.Clear();
+  } else {
+    // Prepend it to the list of free items.
+    FreeFCItemLink* item = reinterpret_cast<FreeFCItemLink*>(aItem);
+    item->mNext = mFirstFreeFCItem;
+    mFirstFreeFCItem = item;
+  }
+}
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -6,16 +6,17 @@
 /*
  * construction of a frame tree that is nearly isomorphic to the content
  * tree and updating of that tree in response to dynamic changes
  */
 
 #ifndef nsCSSFrameConstructor_h___
 #define nsCSSFrameConstructor_h___
 
+#include "mozilla/ArenaAllocator.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/RestyleManager.h"
 #include "mozilla/RestyleManager.h"
 
 #include "nsCOMPtr.h"
 #include "nsILayoutHistoryState.h"
 #include "nsQuoteList.h"
@@ -57,16 +58,17 @@ public:
 
   friend class mozilla::RestyleManager;
   friend class mozilla::GeckoRestyleManager;
   friend class mozilla::ServoRestyleManager;
 
   nsCSSFrameConstructor(nsIDocument* aDocument, nsIPresShell* aPresShell);
   ~nsCSSFrameConstructor() {
     MOZ_ASSERT(mUpdateCount == 0, "Dying in the middle of our own update?");
+    MOZ_ASSERT(mFCItemsInUse == 0);
   }
 
   // get the alternate text for a content node
   static void GetAlternateTextFor(nsIContent* aContent,
                                   nsIAtom* aTag,  // content object's tag
                                   nsAString& aAltText);
 
 private:
@@ -833,50 +835,27 @@ private:
      match or if the matching tag has a FrameConstructionDataGetter that
      returns null. */
   static const FrameConstructionData*
     FindDataByTag(nsIAtom* aTag, Element* aElement,
                   nsStyleContext* aStyleContext,
                   const FrameConstructionDataByTag* aDataPtr,
                   uint32_t aDataLength);
 
-  /* A class representing a list of FrameConstructionItems */
-  class FrameConstructionItemList final {
+  /* A class representing a list of FrameConstructionItems.  Instances of this
+     class are only created as AutoFrameConstructionItemList, or as a member
+     of FrameConstructionItem. */
+  class FrameConstructionItemList
+  {
   public:
-    FrameConstructionItemList() :
-      mInlineCount(0),
-      mBlockCount(0),
-      mLineParticipantCount(0),
-      mItemCount(0),
-      mLineBoundaryAtStart(false),
-      mLineBoundaryAtEnd(false),
-      mParentHasNoXBLChildren(false),
-      mTriedConstructingFrames(false)
+    void Reset(nsCSSFrameConstructor* aFCtor)
     {
-      memset(mDesiredParentCounts, 0, sizeof(mDesiredParentCounts));
-    }
-
-    ~FrameConstructionItemList() {
-      while (FrameConstructionItem* item = mItems.popFirst()) {
-        delete item;
-      }
-
-      // Create the undisplayed entries for our mUndisplayedItems, if any, but
-      // only if we have tried constructing frames for this item list.  If we
-      // haven't, then we're just throwing it away and will probably try again.
-      if (!mUndisplayedItems.IsEmpty() && mTriedConstructingFrames) {
-        // We could store the frame manager in a member, but just
-        // getting it off the style context is not too bad.
-        nsFrameManager *mgr =
-          mUndisplayedItems[0].mStyleContext->PresContext()->FrameManager();
-        for (uint32_t i = 0; i < mUndisplayedItems.Length(); ++i) {
-          UndisplayedItem& item = mUndisplayedItems[i];
-          mgr->RegisterDisplayNoneStyleFor(item.mContent, item.mStyleContext);
-        }
-      }
+      Destroy(aFCtor);
+      this->~FrameConstructionItemList();
+      new (this) FrameConstructionItemList();
     }
 
     void SetLineBoundaryAtStart(bool aBoundary) { mLineBoundaryAtStart = aBoundary; }
     void SetLineBoundaryAtEnd(bool aBoundary) { mLineBoundaryAtEnd = aBoundary; }
     void SetParentHasNoXBLChildren(bool aHasNoXBLChildren) {
       mParentHasNoXBLChildren = aHasNoXBLChildren;
     }
     void SetTriedConstructingFrames() { mTriedConstructingFrames = true; }
@@ -890,51 +869,53 @@ private:
     bool AllWantParentType(ParentType aDesiredParentType) const {
       return mDesiredParentCounts[aDesiredParentType] == mItemCount;
     }
 
     // aSuppressWhiteSpaceOptimizations is true if optimizations that
     // skip constructing whitespace frames for this item or items
     // around it cannot be performed.
     // Also, the return value is always non-null, thanks to infallible 'new'.
-    FrameConstructionItem* AppendItem(const FrameConstructionData* aFCData,
+    FrameConstructionItem* AppendItem(nsCSSFrameConstructor* aFCtor,
+                                      const FrameConstructionData* aFCData,
                                       nsIContent* aContent,
                                       nsIAtom* aTag,
                                       int32_t aNameSpaceID,
                                       PendingBinding* aPendingBinding,
                                       already_AddRefed<nsStyleContext>&& aStyleContext,
                                       bool aSuppressWhiteSpaceOptimizations,
                                       nsTArray<nsIAnonymousContentCreator::ContentInfo>* aAnonChildren)
     {
       FrameConstructionItem* item =
-        new FrameConstructionItem(aFCData, aContent, aTag, aNameSpaceID,
-                                  aPendingBinding, aStyleContext,
-                                  aSuppressWhiteSpaceOptimizations,
-                                  aAnonChildren);
+        new (aFCtor) FrameConstructionItem(aFCData, aContent, aTag, aNameSpaceID,
+                                           aPendingBinding, aStyleContext,
+                                           aSuppressWhiteSpaceOptimizations,
+                                           aAnonChildren);
       mItems.insertBack(item);
       ++mItemCount;
       ++mDesiredParentCounts[item->DesiredParentType()];
       return item;
     }
 
     // Arguments are the same as AppendItem().
-    FrameConstructionItem* PrependItem(const FrameConstructionData* aFCData,
+    FrameConstructionItem* PrependItem(nsCSSFrameConstructor* aFCtor,
+                                       const FrameConstructionData* aFCData,
                                        nsIContent* aContent,
                                        nsIAtom* aTag,
                                        int32_t aNameSpaceID,
                                        PendingBinding* aPendingBinding,
                                        already_AddRefed<nsStyleContext>&& aStyleContext,
                                        bool aSuppressWhiteSpaceOptimizations,
                                        nsTArray<nsIAnonymousContentCreator::ContentInfo>* aAnonChildren)
     {
       FrameConstructionItem* item =
-        new FrameConstructionItem(aFCData, aContent, aTag, aNameSpaceID,
-                                  aPendingBinding, aStyleContext,
-                                  aSuppressWhiteSpaceOptimizations,
-                                  aAnonChildren);
+        new (aFCtor) FrameConstructionItem(aFCData, aContent, aTag, aNameSpaceID,
+                                           aPendingBinding, aStyleContext,
+                                           aSuppressWhiteSpaceOptimizations,
+                                           aAnonChildren);
       mItems.insertFront(item);
       ++mItemCount;
       ++mDesiredParentCounts[item->DesiredParentType()];
       return item;
     }
 
     void AppendUndisplayedItem(nsIContent* aContent,
                                nsStyleContext* aStyleContext) {
@@ -1036,78 +1017,144 @@ private:
       void AppendItemToList(FrameConstructionItemList& aTargetList);
 
       // As above, but moves all items starting with this iterator until we
       // get to aEnd; the item pointed to by aEnd is not stolen.  This method
       // might have optimizations over just looping and doing StealItem for
       // some special cases.  After this method returns, this iterator will
       // point to the item aEnd points to now; aEnd is not modified.
       // aTargetList must not be the list this iterator is iterating over.
-      void AppendItemsToList(const Iterator& aEnd,
+      void AppendItemsToList(nsCSSFrameConstructor*     aFCtor,
+                             const Iterator&            aEnd,
                              FrameConstructionItemList& aTargetList);
 
       // Insert aItem in this iterator's list right before the item pointed to
       // by this iterator.  After the insertion, this iterator will continue to
       // point to the item it now points to (the one just after the
       // newly-inserted item).  This iterator is allowed to be done; in that
       // case this call just appends the given item to the list.
       void InsertItem(FrameConstructionItem* aItem);
 
       // Delete the items between this iterator and aEnd, including the item
       // this iterator currently points to but not including the item pointed
       // to by aEnd.  When this returns, this iterator will point to the same
       // item as aEnd.  This iterator must not equal aEnd when this method is
       // called.
-      void DeleteItemsTo(const Iterator& aEnd);
+      void DeleteItemsTo(nsCSSFrameConstructor* aFCtor, const Iterator& aEnd);
 
     private:
       FrameConstructionItem* mCurrent;
       FrameConstructionItemList& mList;
     };
 
+  protected:
+    FrameConstructionItemList() :
+      mInlineCount(0),
+      mBlockCount(0),
+      mLineParticipantCount(0),
+      mItemCount(0),
+      mLineBoundaryAtStart(false),
+      mLineBoundaryAtEnd(false),
+      mParentHasNoXBLChildren(false),
+      mTriedConstructingFrames(false)
+    {
+      MOZ_COUNT_CTOR(FrameConstructionItemList);
+      memset(mDesiredParentCounts, 0, sizeof(mDesiredParentCounts));
+    }
+
+    void Destroy(nsCSSFrameConstructor* aFCtor)
+    {
+      while (FrameConstructionItem* item = mItems.popFirst()) {
+        item->Delete(aFCtor);
+      }
+
+      // Create the undisplayed entries for our mUndisplayedItems, if any, but
+      // only if we have tried constructing frames for this item list.  If we
+      // haven't, then we're just throwing it away and will probably try again.
+      if (!mUndisplayedItems.IsEmpty() && mTriedConstructingFrames) {
+        for (uint32_t i = 0; i < mUndisplayedItems.Length(); ++i) {
+          UndisplayedItem& item = mUndisplayedItems[i];
+          aFCtor->RegisterDisplayNoneStyleFor(item.mContent, item.mStyleContext);
+        }
+      }
+    }
+
+    // Prevent stack instances (except as AutoFrameConstructionItemList).
+    friend struct FrameConstructionItem;
+    ~FrameConstructionItemList()
+    {
+      MOZ_COUNT_DTOR(FrameConstructionItemList);
+      MOZ_ASSERT(mItems.isEmpty(), "leaking");
+    }
   private:
+    // Not allocated from the heap!
+    void* operator new(size_t) = delete;
+    void* operator new[](size_t) = delete;
+#ifdef _MSC_VER  /* Visual Studio */
+    void operator delete(void*) { MOZ_CRASH("FrameConstructionItemList::del"); }
+#else
+    void operator delete(void*) = delete;
+#endif
+    void operator delete[](void*) = delete;
+    // Placement new is used by Reset().
+    void* operator new(size_t, void* aPtr) { return aPtr; }
+
     struct UndisplayedItem {
       UndisplayedItem(nsIContent* aContent, nsStyleContext* aStyleContext) :
         mContent(aContent), mStyleContext(aStyleContext)
       {}
 
       nsIContent * const mContent;
       RefPtr<nsStyleContext> mStyleContext;
     };
 
     // Adjust our various counts for aItem being added or removed.  aDelta
     // should be either +1 or -1 depending on which is happening.
     void AdjustCountsForItem(FrameConstructionItem* aItem, int32_t aDelta);
 
+    nsTArray<UndisplayedItem> mUndisplayedItems;
     mozilla::LinkedList<FrameConstructionItem> mItems;
     uint32_t mInlineCount;
     uint32_t mBlockCount;
     uint32_t mLineParticipantCount;
     uint32_t mItemCount;
     uint32_t mDesiredParentCounts[eParentTypeCount];
     // True if there is guaranteed to be a line boundary before the
     // frames created by these items
     bool mLineBoundaryAtStart;
     // True if there is guaranteed to be a line boundary after the
     // frames created by these items
     bool mLineBoundaryAtEnd;
     // True if the parent is guaranteed to have no XBL anonymous children
     bool mParentHasNoXBLChildren;
     // True if we have tried constructing frames from this list
     bool mTriedConstructingFrames;
+  };
 
-    nsTArray<UndisplayedItem> mUndisplayedItems;
+  /* A struct representing a list of FrameConstructionItems on the stack. */
+  struct MOZ_RAII AutoFrameConstructionItemList final
+    : public FrameConstructionItemList
+  {
+    template<typename... Args>
+    explicit AutoFrameConstructionItemList(nsCSSFrameConstructor* aFCtor, Args&&... args)
+      : FrameConstructionItemList(std::forward<Args>(args)...)
+      , mFCtor(aFCtor)
+    { MOZ_ASSERT(mFCtor); }
+    ~AutoFrameConstructionItemList() { Destroy(mFCtor); }
+  private:
+    nsCSSFrameConstructor* const mFCtor;
   };
 
   typedef FrameConstructionItemList::Iterator FCItemIterator;
 
   /* A struct representing an item for which frames might need to be
    * constructed.  This contains all the information needed to construct the
    * frame other than the parent frame and whatever would be stored in the
-   * frame constructor state. */
+   * frame constructor state.  You probably want to use
+   * AutoFrameConstructionItem instead of this struct. */
   struct FrameConstructionItem final
     : public mozilla::LinkedListElement<FrameConstructionItem> {
     FrameConstructionItem(const FrameConstructionData* aFCData,
                           nsIContent* aContent,
                           nsIAtom* aTag,
                           int32_t aNameSpaceID,
                           PendingBinding* aPendingBinding,
                           already_AddRefed<nsStyleContext>& aStyleContext,
@@ -1118,33 +1165,42 @@ private:
       mNameSpaceID(aNameSpaceID),
       mSuppressWhiteSpaceOptimizations(aSuppressWhiteSpaceOptimizations),
       mIsText(false), mIsGeneratedContent(false),
       mIsAnonymousContentCreatorContent(false),
       mIsRootPopupgroup(false), mIsAllInline(false), mIsBlock(false),
       mHasInlineEnds(false), mIsPopup(false),
       mIsLineParticipant(false), mIsForSVGAElement(false)
     {
+      MOZ_COUNT_CTOR(FrameConstructionItem);
       if (aAnonChildren) {
         NS_ASSERTION(!(mFCData->mBits & FCDATA_FUNC_IS_FULL_CTOR) ||
                      mFCData->mFullConstructor ==
                        &nsCSSFrameConstructor::ConstructInline,
                      "This is going to fail");
         NS_ASSERTION(!(mFCData->mBits & FCDATA_USE_CHILD_ITEMS),
                      "nsIAnonymousContentCreator::CreateAnonymousContent "
                      "implementations should not output a list where the "
                      "items have children in this case");
         mAnonChildren.SwapElements(*aAnonChildren);
       }
     }
-    ~FrameConstructionItem() {
+
+    void* operator new(size_t, nsCSSFrameConstructor* aFCtor)
+    { return aFCtor->AllocateFCItem(); }
+
+    void Delete(nsCSSFrameConstructor* aFCtor)
+    {
+      mChildItems.Destroy(aFCtor);
       if (mIsGeneratedContent) {
         mContent->UnbindFromTree();
         NS_RELEASE(mContent);
       }
+      this->~FrameConstructionItem();
+      aFCtor->FreeFCItem(this);
     }
 
     ParentType DesiredParentType() {
       return FCDATA_DESIRED_PARENT_TYPE(mFCData->mBits);
     }
 
     // Indicates whether (when in a flex or grid container) this item needs
     // to be wrapped in an anonymous block.  (Note that we implement
@@ -1233,17 +1289,54 @@ private:
     // go into the global popup items.
     bool mIsPopup:1;
     // Whether this item should be treated as a line participant
     bool mIsLineParticipant:1;
     // Whether this item is for an SVG <a> element
     bool mIsForSVGAElement:1;
 
   private:
+    // Not allocated from the general heap - instead, use the new/Delete APIs
+    // that take a nsCSSFrameConstructor* (which manages our arena allocation).
+    void* operator new(size_t) = delete;
+    void* operator new[](size_t) = delete;
+#ifdef _MSC_VER  /* Visual Studio */
+    void operator delete(void*) { MOZ_CRASH("FrameConstructionItem::delete"); }
+#else
+    void operator delete(void*) = delete;
+#endif
+    void operator delete[](void*) = delete;
     FrameConstructionItem(const FrameConstructionItem& aOther) = delete; /* not implemented */
+    // Not allocated from the stack!
+    ~FrameConstructionItem()
+    {
+      MOZ_COUNT_DTOR(FrameConstructionItem);
+      MOZ_ASSERT(mChildItems.IsEmpty(), "leaking");
+    }
+  };
+
+  /**
+   * Convenience struct to assist in managing a temporary FrameConstructionItem
+   * using a local variable. Castable to FrameConstructionItem so that it can
+   * be passed transparently to functions that expect that type.
+   * (This struct exists because FrameConstructionItem is arena-allocated, and
+   * it's nice to abstract away its allocation/deallocation.)
+   */
+  struct MOZ_RAII AutoFrameConstructionItem final
+  {
+    template<typename... Args>
+    explicit AutoFrameConstructionItem(nsCSSFrameConstructor* aFCtor, Args&&... args)
+      : mFCtor(aFCtor)
+      , mItem(new (aFCtor) FrameConstructionItem(std::forward<Args>(args)...))
+    { MOZ_ASSERT(mFCtor); }
+    ~AutoFrameConstructionItem() { mItem->Delete(mFCtor); }
+    operator FrameConstructionItem&() { return *mItem; }
+  private:
+    nsCSSFrameConstructor* const mFCtor;
+    FrameConstructionItem* const mItem;
   };
 
   /**
    * Function to create the anonymous flex or grid items that we need.
    * If aParentFrame is not a nsFlexContainerFrame or nsGridContainerFrame then
    * this method is a NOP.
    * @param aItems the child frame construction items before pseudo creation
    * @param aParentFrame the parent frame
@@ -2138,30 +2231,41 @@ private:
                                           nsIFrame* aFrame,
                                           nsIContent* aDocElement);
 
 public:
 
   friend class nsFrameConstructorState;
 
 private:
+  // For allocating FrameConstructionItems from the mFCItemPool arena.
+  friend struct FrameConstructionItem;
+  void* AllocateFCItem();
+  void FreeFCItem(FrameConstructionItem*);
 
   nsIDocument*        mDocument;  // Weak ref
 
   // See the comment at the start of ConstructRootFrame for more details
   // about the following frames.
 
   // This is just the outermost frame for the root element.
   nsContainerFrame*   mRootElementFrame;
   // This is the frame for the root element that has no pseudo-element style.
   nsIFrame*           mRootElementStyleFrame;
   // This is the containing block that contains the root element ---
   // the real "initial containing block" according to CSS 2.1.
   nsContainerFrame*   mDocElementContainingBlock;
   nsIFrame*           mPageSequenceFrame;
+
+  // FrameConstructionItem arena + list of freed items available for re-use.
+  mozilla::ArenaAllocator<4096, 8> mFCItemPool;
+  struct FreeFCItemLink { FreeFCItemLink* mNext; };
+  FreeFCItemLink* mFirstFreeFCItem;
+  size_t mFCItemsInUse;
+
   nsQuoteList         mQuoteList;
   nsCounterManager    mCounterManager;
   // Current ProcessChildren depth.
   uint16_t            mCurrentDepth;
 #ifdef DEBUG
   uint16_t            mUpdateCount;
 #endif
   bool                mQuotesDirty : 1;
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -2186,189 +2186,189 @@
     "bug_numbers": [1272345, 1296287],
     "expires_in_version": "56",
     "kind": "enumerated",
     "n_values": 12,
     "description": "Whether the URL gets redirected?  (0=200, 1=301, 2=302, 3=304, 4=307, 5=308, 6=400, 7=401, 8=403, 9=404, 10=500, 11=other)"
   },
   "HTTP_NET_VS_CACHE_ONSTART_QSMALL_NORMALPRI_V2": {
     "record_in_processes": ["main", "content"],
-    "expires_in_version": "58",
+    "expires_in_version": "62",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1325322],
       "kind": "enumerated",
       "n_values": 80,
       "description": "Network vs cache time load (OnStartRequest) difference (ms) for requests with a normal priority and small queue. Cache wins: 41-50 for 1-100ms, 51-59 for 101-1000ms, 60-68 for 1-10s, 69-73 for 11-60s and 74 for > 1m. Network wins: 39-30 for 1-100ms, 29-21 for 101-1000ms, 20-12 for 1-10s, 11-7 for 11-60s and 6 for > 1m."
   },
   "HTTP_NET_VS_CACHE_ONSTART_QMED_NORMALPRI_V2": {
     "record_in_processes": ["main", "content"],
-    "expires_in_version": "58",
+    "expires_in_version": "62",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1325322],
       "kind": "enumerated",
       "n_values": 80,
       "description": "Network vs cache time load (OnStartRequest) difference (ms) for requests with a normal priority and medium queue. Cache wins: 41-50 for 1-100ms, 51-59 for 101-1000ms, 60-68 for 1-10s, 69-73 for 11-60s and 74 for > 1m. Network wins: 39-30 for 1-100ms, 29-21 for 101-1000ms, 20-12 for 1-10s, 11-7 for 11-60s and 6 for > 1m."
   },
   "HTTP_NET_VS_CACHE_ONSTART_QBIG_NORMALPRI_V2": {
     "record_in_processes": ["main", "content"],
-    "expires_in_version": "58",
+    "expires_in_version": "62",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1325322],
       "kind": "enumerated",
       "n_values": 80,
       "description": "Network vs cache time load (OnStartRequest) difference (ms) for requests with a normal priority and large queue. Cache wins: 41-50 for 1-100ms, 51-59 for 101-1000ms, 60-68 for 1-10s, 69-73 for 11-60s and 74 for > 1m. Network wins: 39-30 for 1-100ms, 29-21 for 101-1000ms, 20-12 for 1-10s, 11-7 for 11-60s and 6 for > 1m."
   },
   "HTTP_NET_VS_CACHE_ONSTART_QSMALL_HIGHPRI_V2": {
     "record_in_processes": ["main", "content"],
-    "expires_in_version": "58",
+    "expires_in_version": "62",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1325322],
       "kind": "enumerated",
       "n_values": 80,
       "description": "Network vs cache time load (OnStartRequest) difference (ms) for requests with a high priority and small queue. Cache wins: 41-50 for 1-100ms, 51-59 for 101-1000ms, 60-68 for 1-10s, 69-73 for 11-60s and 74 for > 1m. Network wins: 39-30 for 1-100ms, 29-21 for 101-1000ms, 20-12 for 1-10s, 11-7 for 11-60s and 6 for > 1m."
   },
   "HTTP_NET_VS_CACHE_ONSTART_QMED_HIGHPRI_V2": {
     "record_in_processes": ["main", "content"],
-    "expires_in_version": "58",
+    "expires_in_version": "62",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1325322],
       "kind": "enumerated",
       "n_values": 80,
       "description": "Network vs cache time load (OnStartRequest) difference (ms) for requests with a high priority and medium queue. Cache wins: 41-50 for 1-100ms, 51-59 for 101-1000ms, 60-68 for 1-10s, 69-73 for 11-60s and 74 for > 1m. Network wins: 39-30 for 1-100ms, 29-21 for 101-1000ms, 20-12 for 1-10s, 11-7 for 11-60s and 6 for > 1m."
   },
   "HTTP_NET_VS_CACHE_ONSTART_QBIG_HIGHPRI_V2": {
     "record_in_processes": ["main", "content"],
-    "expires_in_version": "58",
+    "expires_in_version": "62",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1325322],
       "kind": "enumerated",
       "n_values": 80,
       "description": "Network vs cache time load (OnStartRequest) difference (ms) for requests with a high priority and large queue. Cache wins: 41-50 for 1-100ms, 51-59 for 101-1000ms, 60-68 for 1-10s, 69-73 for 11-60s and 74 for > 1m. Network wins: 39-30 for 1-100ms, 29-21 for 101-1000ms, 20-12 for 1-10s, 11-7 for 11-60s and 6 for > 1m."
   },
   "HTTP_NET_VS_CACHE_ONSTOP_QSMALL_NORMALPRI_V2": {
     "record_in_processes": ["main", "content"],
-    "expires_in_version": "58",
+    "expires_in_version": "62",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1325322],
       "kind": "enumerated",
       "n_values": 80,
       "description": "Network vs cache time load (OnStopRequest) difference (ms) for requests with a normal priority and small queue. Cache wins: 41-50 for 1-100ms, 51-59 for 101-1000ms, 60-68 for 1-10s, 69-73 for 11-60s and 74 for > 1m. Network wins: 39-30 for 1-100ms, 29-21 for 101-1000ms, 20-12 for 1-10s, 11-7 for 11-60s and 6 for > 1m."
   },
   "HTTP_NET_VS_CACHE_ONSTOP_QMED_NORMALPRI_V2": {
     "record_in_processes": ["main", "content"],
-    "expires_in_version": "58",
+    "expires_in_version": "62",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1325322],
       "kind": "enumerated",
       "n_values": 80,
       "description": "Network vs cache time load (OnStopRequest) difference (ms) for requests with a normal priority and medium queue. Cache wins: 41-50 for 1-100ms, 51-59 for 101-1000ms, 60-68 for 1-10s, 69-73 for 11-60s and 74 for > 1m. Network wins: 39-30 for 1-100ms, 29-21 for 101-1000ms, 20-12 for 1-10s, 11-7 for 11-60s and 6 for > 1m."
   },
   "HTTP_NET_VS_CACHE_ONSTOP_QBIG_NORMALPRI_V2": {
     "record_in_processes": ["main", "content"],
-    "expires_in_version": "58",
+    "expires_in_version": "62",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1325322],
       "kind": "enumerated",
       "n_values": 80,
       "description": "Network vs cache time load (OnStopRequest) difference (ms) for requests with a normal priority and large queue. Cache wins: 41-50 for 1-100ms, 51-59 for 101-1000ms, 60-68 for 1-10s, 69-73 for 11-60s and 74 for > 1m. Network wins: 39-30 for 1-100ms, 29-21 for 101-1000ms, 20-12 for 1-10s, 11-7 for 11-60s and 6 for > 1m."
   },
   "HTTP_NET_VS_CACHE_ONSTOP_QSMALL_HIGHPRI_V2": {
     "record_in_processes": ["main", "content"],
-    "expires_in_version": "58",
+    "expires_in_version": "62",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1325322],
       "kind": "enumerated",
       "n_values": 80,
       "description": "Network vs cache time load (OnStopRequest) difference (ms) for requests with a high priority and small queue. Cache wins: 41-50 for 1-100ms, 51-59 for 101-1000ms, 60-68 for 1-10s, 69-73 for 11-60s and 74 for > 1m. Network wins: 39-30 for 1-100ms, 29-21 for 101-1000ms, 20-12 for 1-10s, 11-7 for 11-60s and 6 for > 1m."
   },
   "HTTP_NET_VS_CACHE_ONSTOP_QMED_HIGHPRI_V2": {
     "record_in_processes": ["main", "content"],
-    "expires_in_version": "58",
+    "expires_in_version": "62",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1325322],
       "kind": "enumerated",
       "n_values": 80,
       "description": "Network vs cache time load (OnStopRequest) difference (ms) for requests with a high priority and medium queue. Cache wins: 41-50 for 1-100ms, 51-59 for 101-1000ms, 60-68 for 1-10s, 69-73 for 11-60s and 74 for > 1m. Network wins: 39-30 for 1-100ms, 29-21 for 101-1000ms, 20-12 for 1-10s, 11-7 for 11-60s and 6 for > 1m."
   },
   "HTTP_NET_VS_CACHE_ONSTOP_QBIG_HIGHPRI_V2": {
     "record_in_processes": ["main", "content"],
-    "expires_in_version": "58",
+    "expires_in_version": "62",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1325322],
       "kind": "enumerated",
       "n_values": 80,
       "description": "Network vs cache time load (OnStopRequest) difference (ms) for requests with a high priority and large queue. Cache wins: 41-50 for 1-100ms, 51-59 for 101-1000ms, 60-68 for 1-10s, 69-73 for 11-60s and 74 for > 1m. Network wins: 39-30 for 1-100ms, 29-21 for 101-1000ms, 20-12 for 1-10s, 11-7 for 11-60s and 6 for > 1m."
   },
   "HTTP_NET_VS_CACHE_ONSTOP_SMALL_V2": {
     "record_in_processes": ["main", "content"],
-    "expires_in_version": "58",
+    "expires_in_version": "62",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1325322],
       "kind": "enumerated",
       "n_values": 80,
       "description": "Network vs cache time load (OnStopRequest) difference (ms) for cache files with a small size (<256K). Cache wins: 41-50 for 1-100ms, 51-59 for 101-1000ms, 60-68 for 1-10s, 69-73 for 11-60s and 74 for > 1m. Network wins: 39-30 for 1-100ms, 29-21 for 101-1000ms, 20-12 for 1-10s, 11-7 for 11-60s and 6 for > 1m."
   },
   "HTTP_NET_VS_CACHE_ONSTOP_LARGE_V2": {
     "record_in_processes": ["main", "content"],
-    "expires_in_version": "58",
+    "expires_in_version": "62",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1325322],
       "kind": "enumerated",
       "n_values": 80,
       "description": "Network vs cache time load (OnStopRequest) difference (ms) for cache files with a large size (>=256K). Cache wins: 41-50 for 1-100ms, 51-59 for 101-1000ms, 60-68 for 1-10s, 69-73 for 11-60s and 74 for > 1m. Network wins: 39-30 for 1-100ms, 29-21 for 101-1000ms, 20-12 for 1-10s, 11-7 for 11-60s and 6 for > 1m."
   },
   "HTTP_NET_VS_CACHE_ONSTART_REVALIDATED_V2": {
     "record_in_processes": ["main", "content"],
-    "expires_in_version": "58",
+    "expires_in_version": "62",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1325322],
       "kind": "enumerated",
       "n_values": 80,
       "description": "Network vs cache time load (OnStartRequest) difference revalidated cache entries. Cache wins: 41-50 for 1-100ms, 51-59 for 101-1000ms, 60-68 for 1-10s, 69-73 for 11-60s and 74 for > 1m. Network wins: 39-30 for 1-100ms, 29-21 for 101-1000ms, 20-12 for 1-10s, 11-7 for 11-60s and 6 for > 1m."
   },
   "HTTP_NET_VS_CACHE_ONSTART_NOTREVALIDATED_V2": {
     "record_in_processes": ["main", "content"],
-    "expires_in_version": "58",
+    "expires_in_version": "62",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1325322],
       "kind": "enumerated",
       "n_values": 80,
       "description": "Network vs cache time load (OnStartRequest) difference (ms) not revalidated cache entries. Cache wins: 41-50 for 1-100ms, 51-59 for 101-1000ms, 60-68 for 1-10s, 69-73 for 11-60s and 74 for > 1m. Network wins: 39-30 for 1-100ms, 29-21 for 101-1000ms, 20-12 for 1-10s, 11-7 for 11-60s and 6 for > 1m."
   },
   "HTTP_NET_VS_CACHE_ONSTOP_REVALIDATED_V2": {
     "record_in_processes": ["main", "content"],
-    "expires_in_version": "58",
+    "expires_in_version": "62",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1325322],
       "kind": "enumerated",
       "n_values": 80,
       "description": "Network vs cache time load (OnStopRequest) difference (ms) revalidated cache entries. Cache wins: 41-50 for 1-100ms, 51-59 for 101-1000ms, 60-68 for 1-10s, 69-73 for 11-60s and 74 for > 1m. Network wins: 39-30 for 1-100ms, 29-21 for 101-1000ms, 20-12 for 1-10s, 11-7 for 11-60s and 6 for > 1m."
   },
   "HTTP_NET_VS_CACHE_ONSTOP_NOTREVALIDATED_V2": {
     "record_in_processes": ["main", "content"],
-    "expires_in_version": "58",
+    "expires_in_version": "62",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1325322],
       "kind": "enumerated",
       "n_values": 80,
       "description": "Network vs cache time load (OnStopRequest) difference (ms) not revalidated cache entries. Cache wins: 41-50 for 1-100ms, 51-59 for 101-1000ms, 60-68 for 1-10s, 69-73 for 11-60s and 74 for > 1m. Network wins: 39-30 for 1-100ms, 29-21 for 101-1000ms, 20-12 for 1-10s, 11-7 for 11-60s and 6 for > 1m."
   },
   "HTTP_ONSTART_SUSPEND_TOTAL_TIME": {
     "record_in_processes": ["main", "content"],
-    "expires_in_version": "58",
+    "expires_in_version": "62",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1347948],
       "kind": "exponential",
       "high": 60000,
       "n_buckets": 100,
       "description": "Time in milliseconds that http channel spent suspended between AsyncOpen and OnStartRequest."
   },
   "NETWORK_RACE_CACHE_WITH_NETWORK_USAGE_2": {
     "record_in_processes": ["main", "content"],
-    "expires_in_version": "58",
+    "expires_in_version": "62",
     "alert_emails": ["necko@mozilla.com"],
     "bug_numbers": [1377340],
     "kind": "categorical",
     "labels": ["NetworkNoRace", "CacheNoRace", "NetworkRace", "CacheRace", "NetworkDelayedRace", "CacheDelayedRace"],
     "description": "Whether we raced network with the cache."
   },
   "NETWORK_RACE_CACHE_WITH_NETWORK_SAVED_TIME": {
     "record_in_processes": ["main", "content"],