Merge inbound to mozilla-central a=merge
authorCoroiu Cristina <ccoroiu@mozilla.com>
Fri, 22 Feb 2019 18:30:44 +0200
changeset 518442 826b59e57fe4274954088e7a9ed9bab092203e1c
parent 518400 a1d118e856dd41925677327b05aebe4e249f817e (current diff)
parent 518441 31404c0c060e9ee2c46b56a199369a3e42d668e4 (diff)
child 518443 ca0ea512f874390477cb4595d9c36c8106296820
child 518487 d9d6968b7c2f682ee72cd0b4df5c8ada53a788f8
child 518596 fa8d864ab068910637097f34f75c8d8ae5ef8dce
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone67.0a1
first release with
nightly linux32
826b59e57fe4 / 67.0a1 / 20190222163114 / files
nightly linux64
826b59e57fe4 / 67.0a1 / 20190222163114 / files
nightly mac
826b59e57fe4 / 67.0a1 / 20190222163114 / files
nightly win32
826b59e57fe4 / 67.0a1 / 20190222163114 / files
nightly win64
826b59e57fe4 / 67.0a1 / 20190222163114 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central a=merge
js/src/jsapi-tests/testExternalArrayBuffer.cpp
js/src/jsapi.h
testing/web-platform/meta/css/css-sizing/range-percent-intrinsic-size-2.html.ini
--- a/browser/components/preferences/in-content/preferences.xul
+++ b/browser/components/preferences/in-content/preferences.xul
@@ -127,21 +127,21 @@
           <label class="category-name" flex="1" data-l10n-id="pane-privacy-title"></label>
         </richlistitem>
 
         <richlistitem id="category-sync"
                       class="category"
                       hidden="true"
                       value="paneSync"
                       helpTopic="prefs-weave"
-                      data-l10n-id="category-sync"
+                      data-l10n-id="category-sync2"
                       data-l10n-attrs="tooltiptext"
                       align="center">
           <image class="category-icon"/>
-          <label class="category-name" flex="1" data-l10n-id="pane-sync-title"></label>
+          <label class="category-name" flex="1" data-l10n-id="pane-sync-title2"></label>
         </richlistitem>
       </richlistbox>
 
       <spacer flex="1"/>
 
       <hbox class="sidebar-footer-button" pack="center">
         <label id="addonsButton" is="text-link">
           <hbox align="center">
--- a/browser/components/preferences/in-content/sync.xul
+++ b/browser/components/preferences/in-content/sync.xul
@@ -6,17 +6,17 @@
 
 <script type="application/javascript"
         src="chrome://browser/content/preferences/in-content/sync.js"/>
 <box id="template-paneSync" hidden="true"><![CDATA[
 <hbox id="firefoxAccountCategory"
       class="subcategory"
       hidden="true"
       data-category="paneSync">
-  <html:h1 data-l10n-id="pane-sync-title"/>
+  <html:h1 data-l10n-id="pane-sync-title2"/>
 </hbox>
 
 <deck id="weavePrefsDeck" data-category="paneSync" hidden="true"
       data-hidden-from-search="true">
   <groupbox id="noFxaAccount">
     <hbox>
       <vbox flex="1">
         <label id="noFxaCaption"><html:h2 data-l10n-id="sync-signedout-caption"/></label>
@@ -65,17 +65,17 @@
         class="text-link" target="_blank"/>
     </label>
   </groupbox>
 
   <vbox id="hasFxaAccount">
     <hbox>
       <vbox id="fxaContentWrapper" flex="1">
         <groupbox id="fxaGroup">
-          <label class="search-header" hidden="true"><html:h2 data-l10n-id="pane-sync-title"/></label>
+          <label class="search-header" hidden="true"><html:h2 data-l10n-id="pane-sync-title2"/></label>
 
           <deck id="fxaLoginStatus" flex="1">
 
             <!-- logged in and verified and all is good -->
             <hbox id="fxaLoginVerified" align="center" flex="1">
               <image class="fxaProfileImage actionable"
                      role="button"
                      onclick="gSyncPane.openChangeProfileImage(event);"
--- a/browser/locales/en-US/browser/preferences/preferences.ftl
+++ b/browser/locales/en-US/browser/preferences/preferences.ftl
@@ -49,20 +49,19 @@ category-home =
 pane-search-title = Search
 category-search =
     .tooltiptext = { pane-search-title }
 
 pane-privacy-title = Privacy & Security
 category-privacy =
     .tooltiptext = { pane-privacy-title }
 
-# The word "account" can be translated, do not translate or transliterate "Firefox".
-pane-sync-title = Firefox Account
-category-sync =
-    .tooltiptext = { pane-sync-title }
+pane-sync-title2 = { -sync-brand-short-name }
+category-sync2 =
+    .tooltiptext = { pane-sync-title2 }
 
 help-button-label = { -brand-short-name } Support
 addons-button-label = Extensions & Themes
 
 focus-search =
     .key = f
 
 close-button =
--- a/devtools/client/inspector/changes/test/head.js
+++ b/devtools/client/inspector/changes/test/head.js
@@ -15,23 +15,20 @@ Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/inspector/rules/test/head.js",
   this);
 
 // Load the shared Redux helpers.
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/shared/test/shared-redux-head.js",
   this);
 
-// Ensure the Changes panel is enabled before running the tests.
-Services.prefs.setBoolPref("devtools.inspector.changes.enabled", true);
 // Ensure the three-pane mode is enabled before running the tests.
 Services.prefs.setBoolPref("devtools.inspector.three-pane-enabled", true);
 
 registerCleanupFunction(() => {
-  Services.prefs.clearUserPref("devtools.inspector.changes.enabled");
   Services.prefs.clearUserPref("devtools.inspector.three-pane-enabled");
 });
 
 /**
  * Get an array of objects with property/value pairs of the CSS declarations rendered
  * in the Changes panel.
  *
  * @param  {Document} panelDoc
--- a/devtools/client/inspector/fonts/components/FontName.js
+++ b/devtools/client/inspector/fonts/components/FontName.js
@@ -1,23 +1,20 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const Services = require("Services");
 const { PureComponent } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 
 const Types = require("../types");
 
-const FONT_HIGHLIGHTER_PREF = "devtools.inspector.fonthighlighter.enabled";
-
 class FontName extends PureComponent {
   static get propTypes() {
     return {
       font: PropTypes.shape(Types.font).isRequired,
       onToggleFontHighlight: PropTypes.func.isRequired,
     };
   }
 
@@ -41,22 +38,20 @@ class FontName extends PureComponent {
       font,
       onToggleFontHighlight,
     } = this.props;
 
     onToggleFontHighlight(font, false);
   }
 
   render() {
-    const options = {
-      className: "font-name",
-    };
-
-    if (Services.prefs.getBoolPref(FONT_HIGHLIGHTER_PREF)) {
-      options.onMouseOver = this.onNameMouseOver;
-      options.onMouseOut = this.onNameMouseOut;
-    }
-
-    return dom.span(options, this.props.font.name);
+    return dom.span(
+      {
+        className: "font-name",
+        onMouseOver: this.onNameMouseOver,
+        onMouseOut: this.onNameMouseOut,
+      },
+      this.props.font.name
+    );
   }
 }
 
 module.exports = FontName;
--- a/devtools/client/inspector/inspector.js
+++ b/devtools/client/inspector/inspector.js
@@ -50,17 +50,16 @@ const PORTRAIT_MODE_WIDTH_THRESHOLD = 70
 // the sidebar automatically switches from 'landscape/horizontal' to 'portrait/vertical'
 // mode.
 const SIDE_PORTAIT_MODE_WIDTH_THRESHOLD = 1000;
 
 const THREE_PANE_ENABLED_PREF = "devtools.inspector.three-pane-enabled";
 const THREE_PANE_ENABLED_SCALAR = "devtools.inspector.three_pane_enabled";
 const THREE_PANE_CHROME_ENABLED_PREF = "devtools.inspector.chrome.three-pane-enabled";
 const TELEMETRY_EYEDROPPER_OPENED = "devtools.toolbar.eyedropper.opened";
-const TRACK_CHANGES_PREF = "devtools.inspector.changes.enabled";
 
 /**
  * Represents an open instance of the Inspector for a tab.
  * The inspector controls the breadcrumbs, the markup view, and the sidebar
  * (computed view, rule view, font view and animation inspector).
  *
  * Events:
  * - ready
@@ -240,18 +239,17 @@ Inspector.prototype = {
 
   /**
    * Check if the changes panel is enabled and supported by the server.
    */
   _supportsChangesPanel() {
     // The changes actor was introduced in Fx65, we are checking this for backward
     // compatibility when connecting to an older server. Can be removed once Fx65 hit the
     // release channel.
-    return this._target.hasActor("changes") &&
-      Services.prefs.getBoolPref(TRACK_CHANGES_PREF);
+    return this._target.hasActor("changes");
   },
 
   /**
    * Handle promise rejections for various asynchronous actions, and only log errors if
    * the inspector panel still exists.
    * This is useful to silence useless errors that happen when the inspector is closed
    * while still initializing (and making protocol requests).
    */
--- a/devtools/client/preferences/devtools-client.js
+++ b/devtools/client/preferences/devtools-client.js
@@ -45,20 +45,16 @@ pref("devtools.inspector.show_pseudo_ele
 // The default size for image preview tooltips in the rule-view/computed-view/markup-view
 pref("devtools.inspector.imagePreviewTooltipSize", 300);
 // Enable user agent style inspection in rule-view
 pref("devtools.inspector.showUserAgentStyles", false);
 // Show all native anonymous content
 pref("devtools.inspector.showAllAnonymousContent", false);
 // Show user agent shadow roots
 pref("devtools.inspector.showUserAgentShadowRoots", false);
-// Enable the font highlight-on-hover feature
-pref("devtools.inspector.fonthighlighter.enabled", true);
-// Enable tracking of style changes and the Changes panel in the Inspector
-pref("devtools.inspector.changes.enabled", true);
 // Enable the new Rules View
 pref("devtools.inspector.new-rulesview.enabled", false);
 
 // Flexbox preferences
 // Whether or not to show the combined flexbox and box model highlighter.
 #if defined(NIGHTLY_BUILD) || defined(MOZ_DEV_EDITION)
 pref("devtools.inspector.flexboxHighlighter.combine", true);
 #else
--- a/devtools/client/themes/webconsole.css
+++ b/devtools/client/themes/webconsole.css
@@ -977,16 +977,17 @@ body {
   /* Let the parent component handle the border. This is needed otherwise there is a visual
      glitch between the input and the sidebar borders */
   background-color: transparent;
 }
 
 .split-box.vert.sidebar {
   /* Set to prevent the sidebar from extending past the right edge of the page */
   width: unset;
+  height: 100vh;
 }
 
 .sidebar-wrapper {
   display: grid;
   grid-template-rows: auto 1fr;
   width: 100%;
   overflow: hidden;
 }
@@ -997,17 +998,16 @@ body {
   display: flex;
   justify-content: end;
   padding: 0;
 }
 
 .sidebar-contents {
   grid-row: 2 / 3;
   overflow: auto;
-  max-height: 100%;
 }
 
 .webconsole-sidebar-toolbar .sidebar-close-button {
   padding: 4px 0;
   margin: 0;
 }
 
 .sidebar-close-button::before {
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -3038,17 +3038,17 @@ static mozilla::Maybe<JS::StructuredClon
     scope.emplace(JS::StructuredCloneScope::DifferentProcess);
   } else if (StringEqualsAscii(scopeStr, "DifferentProcessForIndexedDB")) {
     scope.emplace(JS::StructuredCloneScope::DifferentProcessForIndexedDB);
   }
 
   return scope;
 }
 
-static bool Serialize(JSContext* cx, unsigned argc, Value* vp) {
+bool js::testingFunc_serialize(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
   mozilla::Maybe<JSAutoStructuredCloneBuffer> clonebuf;
   JS::CloneDataPolicy policy;
 
   if (!args.get(2).isUndefined()) {
     RootedObject opts(cx, ToObject(cx, args.get(2)));
     if (!opts) {
@@ -6089,17 +6089,17 @@ gc::ZealModeHelpText),
 "  Iterates the Jit stack and check that stack invariants hold."),
 
     JS_FN_HELP("setIonCheckGraphCoherency", SetIonCheckGraphCoherency, 1, 0,
 "setIonCheckGraphCoherency(bool)",
 "  Set whether Ion should perform graph consistency (DEBUG-only) assertions. These assertions\n"
 "  are valuable and should be generally enabled, however they can be very expensive for large\n"
 "  (wasm) programs."),
 
-    JS_FN_HELP("serialize", Serialize, 1, 0,
+    JS_FN_HELP("serialize", testingFunc_serialize, 1, 0,
 "serialize(data, [transferables, [policy]])",
 "  Serialize 'data' using JS_WriteStructuredClone. Returns a structured\n"
 "  clone buffer object. 'policy' may be an options hash. Valid keys:\n"
 "    'SharedArrayBuffer' - either 'allow' (the default) or 'deny'\n"
 "      to specify whether SharedArrayBuffers may be serialized.\n"
 "    'scope' - SameProcessSameThread, SameProcessDifferentThread,\n"
 "      DifferentProcess, or DifferentProcessForIndexedDB. Determines how some\n"
 "      values will be serialized. Clone buffers may only be deserialized with a\n"
--- a/js/src/builtin/TestingFunctions.h
+++ b/js/src/builtin/TestingFunctions.h
@@ -17,14 +17,18 @@ MOZ_MUST_USE bool DefineTestingFunctions
 
 MOZ_MUST_USE bool testingFunc_assertFloat32(JSContext* cx, unsigned argc,
                                             Value* vp);
 
 MOZ_MUST_USE bool testingFunc_assertRecoveredOnBailout(JSContext* cx,
                                                        unsigned argc,
                                                        Value* vp);
 
+MOZ_MUST_USE bool testingFunc_serialize(JSContext* cx,
+                                        unsigned argc,
+                                        Value* vp);
+
 extern JSScript* TestingFunctionArgumentToScript(JSContext* cx, HandleValue v,
                                                  JSFunction** funp = nullptr);
 
 } /* namespace js */
 
 #endif /* builtin_TestingFunctions_h */
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -1646,17 +1646,17 @@ void OutlineTypedObject::attach(JSContex
       cx, OutlineTypedObject::createUnattached(cx, descr, heap));
   if (!obj) {
     return nullptr;
   }
 
   // Allocate and initialize the memory for this instance.
   size_t totalSize = descr->size();
   Rooted<ArrayBufferObject*> buffer(cx);
-  buffer = ArrayBufferObject::create(cx, totalSize);
+  buffer = ArrayBufferObject::createZeroed(cx, totalSize);
   if (!buffer) {
     return nullptr;
   }
   descr->initInstances(cx->runtime(), buffer->dataPointer(), 1);
   obj->attach(cx, *buffer, 0);
   return obj;
 }
 
--- a/js/src/jsapi-tests/moz.build
+++ b/js/src/jsapi-tests/moz.build
@@ -9,16 +9,17 @@ GeckoProgram('jsapi-tests', linkage=None
 include('../js-cxxflags.mozbuild')
 
 UNIFIED_SOURCES += [
     'selfTest.cpp',
     'testAddPropertyPropcache.cpp',
     'testArgumentsObject.cpp',
     'testArrayBuffer.cpp',
     'testArrayBufferView.cpp',
+    'testArrayBufferWithUserOwnedContents.cpp',
     'testAtomicOperations.cpp',
     'testBoundFunction.cpp',
     'testBug604087.cpp',
     'testCallArgs.cpp',
     'testCallNonGenericMethodOnProxy.cpp',
     'testChromeBuffer.cpp',
     'testCloneScript.cpp',
     'testCompileNonSyntactic.cpp',
@@ -31,17 +32,16 @@ UNIFIED_SOURCES += [
     'testDefinePropertyIgnoredAttributes.cpp',
     'testDeflateStringToUTF8Buffer.cpp',
     'testDifferentNewTargetInvokeConstructor.cpp',
     'testEmptyWindowIsOmitted.cpp',
     'testErrorCopying.cpp',
     'testErrorLineOfContext.cpp',
     'testException.cpp',
     'testExecuteInJSMEnvironment.cpp',
-    'testExternalArrayBuffer.cpp',
     'testExternalStrings.cpp',
     'testFindSCCs.cpp',
     'testForceLexicalInitialization.cpp',
     'testForOfIterator.cpp',
     'testForwardSetProperty.cpp',
     'testFreshGlobalEvalRedefinition.cpp',
     'testFunctionBinding.cpp',
     'testFunctionProperties.cpp',
--- a/js/src/jsapi-tests/testArrayBuffer.cpp
+++ b/js/src/jsapi-tests/testArrayBuffer.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * vim: set ts=8 sts=2 et sw=2 tw=80:
  */
 
 #include "jsfriendapi.h"
+
+#include "builtin/TestingFunctions.h"
 #include "js/MemoryFunctions.h"
-
 #include "jsapi-tests/tests.h"
 
 BEGIN_TEST(testArrayBuffer_bug720949_steal) {
   static const unsigned NUM_TEST_BUFFERS = 2;
   static const unsigned MAGIC_VALUE_1 = 3;
   static const unsigned MAGIC_VALUE_2 = 17;
 
   JS::RootedObject buf_len1(cx), buf_len200(cx);
@@ -153,83 +154,16 @@ 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)
-
 BEGIN_TEST(testArrayBuffer_customFreeFunc) {
   ExternalData data("One two three four");
 
   // The buffer takes ownership of the data.
   JS::RootedObject buffer(
       cx, JS_NewExternalArrayBuffer(cx, data.len(), data.contents(),
                                     &ExternalData::freeCallback, &data));
   CHECK(buffer);
@@ -301,8 +235,66 @@ BEGIN_TEST(testArrayBuffer_stealDetachEx
   buffer = nullptr;
   JS_GC(cx);
   JS_GC(cx);
   CHECK(data.wasFreed());
 
   return true;
 }
 END_TEST(testArrayBuffer_stealDetachExternal)
+
+BEGIN_TEST(testArrayBuffer_serializeExternal) {
+  JS::RootedValue serializeValue(cx);
+
+  {
+    JS::RootedFunction serialize(cx);
+    serialize = JS_NewFunction(cx, js::testingFunc_serialize, 1, 0, "serialize");
+    CHECK(serialize);
+
+    serializeValue.setObject(*JS_GetFunctionObject(serialize));
+  }
+
+  ExternalData data("One two three four");
+  JS::RootedObject externalBuffer(
+      cx, JS_NewExternalArrayBuffer(cx, data.len(), data.contents(),
+                                    &ExternalData::freeCallback, &data));
+  CHECK(externalBuffer);
+  CHECK(!data.wasFreed());
+
+  JS::RootedValue v(cx, JS::ObjectValue(*externalBuffer));
+  JS::RootedObject transferMap(cx, JS_NewArrayObject(cx, JS::HandleValueArray(v)));
+  CHECK(transferMap);
+
+  JS::AutoValueArray<2> args(cx);
+  args[0].setObject(*externalBuffer);
+  args[1].setObject(*transferMap);
+
+  // serialize(externalBuffer, [externalBuffer]) should throw for an unhandled
+  // BufferContents kind.
+  CHECK(!JS::Call(cx, JS::UndefinedHandleValue, serializeValue,
+                  JS::HandleValueArray(args), &v));
+
+  JS::RootedValue exn(cx);
+  CHECK(JS_GetPendingException(cx, &exn));
+  JS_ClearPendingException(cx);
+
+  js::ErrorReport report(cx);
+  CHECK(report.init(cx, exn, js::ErrorReport::NoSideEffects));
+
+  CHECK_EQUAL(report.report()->errorNumber,
+              static_cast<unsigned int>(JSMSG_SC_NOT_TRANSFERABLE));
+
+  // Data should have been left alone.
+  CHECK(!data.wasFreed());
+
+  v.setNull();
+  transferMap = nullptr;
+  args[0].setNull();
+  args[1].setNull();
+  externalBuffer = nullptr;
+
+  JS_GC(cx);
+  JS_GC(cx);
+  CHECK(data.wasFreed());
+
+  return true;
+}
+END_TEST(testArrayBuffer_serializeExternal)
rename from js/src/jsapi-tests/testExternalArrayBuffer.cpp
rename to js/src/jsapi-tests/testArrayBufferWithUserOwnedContents.cpp
--- a/js/src/jsapi-tests/testExternalArrayBuffer.cpp
+++ b/js/src/jsapi-tests/testArrayBufferWithUserOwnedContents.cpp
@@ -10,20 +10,20 @@ char test_data[] =
     "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
 
 static void GC(JSContext* cx) {
   JS_GC(cx);
   // Trigger another to wait for background finalization to end.
   JS_GC(cx);
 }
 
-BEGIN_TEST(testExternalArrayBuffer) {
+BEGIN_TEST(testArrayBufferWithUserOwnedContents) {
   size_t length = sizeof(test_data);
   JS::RootedObject obj(
-      cx, JS_NewArrayBufferWithExternalContents(cx, length, test_data));
+      cx, JS_NewArrayBufferWithUserOwnedContents(cx, length, test_data));
   GC(cx);
   CHECK(VerifyObject(obj, length));
   GC(cx);
   JS_DetachArrayBuffer(cx, obj);
   GC(cx);
   CHECK(VerifyObject(obj, 0));
 
   return true;
@@ -39,9 +39,9 @@ bool VerifyObject(JS::HandleObject obj, 
   const char* data = reinterpret_cast<const char*>(
       JS_GetArrayBufferData(obj, &sharedDummy, nogc));
   CHECK(data);
   CHECK(test_data == data);
 
   return true;
 }
 
-END_TEST(testExternalArrayBuffer)
+END_TEST(testArrayBufferWithUserOwnedContents)
--- a/js/src/jsapi-tests/testAtomicOperations.cpp
+++ b/js/src/jsapi-tests/testAtomicOperations.cpp
@@ -5,16 +5,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/Alignment.h"
 #include "mozilla/Assertions.h"
 
 #include "jit/AtomicOperations.h"
 #include "jsapi-tests/tests.h"
+#include "vm/ArrayBufferObject.h"
 #include "vm/SharedMem.h"
 #include "wasm/WasmJS.h"
 
 using namespace js;
 
 // Machinery to disguise pointer addresses to the C++ compiler -- quite possibly
 // not thread-safe.
 
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2016,117 +2016,111 @@ extern JS_PUBLIC_API bool IsSetObject(JS
 /**
  * Assign 'undefined' to all of the object's non-reserved slots. Note: this is
  * done for all slots, regardless of the associated property descriptor.
  */
 JS_PUBLIC_API void JS_SetAllNonReservedSlotsToUndefined(JSContext* cx,
                                                         JSObject* objArg);
 
 /**
- * Create a new array buffer with the given contents. It must be legal to pass
- * these contents to JS_free(). On success, the ownership is transferred to the
- * new array buffer.
+ * Create a new ArrayBuffer with the given |contents|, which may be null only
+ * if |nbytes == 0|.  |contents| must be allocated compatible with deallocation
+ * by |JS_free|.
+ *
+ * If and only if an ArrayBuffer is successfully created and returned,
+ * ownership of |contents| is transferred to the new ArrayBuffer.
  */
 extern JS_PUBLIC_API JSObject* JS_NewArrayBufferWithContents(JSContext* cx,
                                                              size_t nbytes,
                                                              void* contents);
 
 namespace JS {
 
 using BufferContentsFreeFunc = void (*)(void* contents, void* userData);
 
 } /* namespace JS */
 
 /**
- * Create a new array buffer with the given contents. The contents must not be
+ * Create a new ArrayBuffer with the given contents. The contents must not be
  * modified by any other code, internal or external.
  *
- * When the array buffer is ready to be disposed of, `freeFunc(contents,
- * freeUserData)` will be called to release the array buffer's reference on the
+ * When the ArrayBuffer is ready to be disposed of, `freeFunc(contents,
+ * freeUserData)` will be called to release the ArrayBuffer's reference on the
  * contents.
  *
  * `freeFunc()` must not call any JSAPI functions that could cause a garbage
  * collection.
  *
  * The caller must keep the buffer alive until `freeFunc()` is called, or, if
  * `freeFunc` is null, until the JSRuntime is destroyed.
  *
  * The caller must not access the buffer on other threads. The JS engine will
  * not allow the buffer to be transferred to other threads. If you try to
  * transfer an external ArrayBuffer to another thread, the data is copied to a
  * new malloc buffer. `freeFunc()` must be threadsafe, and may be called from
  * any thread.
  *
- * This allows array buffers to be used with embedder objects that use reference
+ * This allows ArrayBuffers to be used with embedder objects that use reference
  * counting, for example. In that case the caller is responsible
  * for incrementing the reference count before passing the contents to this
  * function. This also allows using non-reference-counted contents that must be
  * freed with some function other than free().
  */
 extern JS_PUBLIC_API JSObject* JS_NewExternalArrayBuffer(
     JSContext* cx, size_t nbytes, void* contents,
     JS::BufferContentsFreeFunc freeFunc, void* freeUserData = nullptr);
 
 /**
- * Create a new array buffer with the given contents.  The array buffer does not
- * take ownership of contents.  JS_DetachArrayBuffer must be called before
- * the contents are disposed of by the user; this call will always succeed.
+ * Create a new ArrayBuffer with the given non-null |contents|.
+ *
+ * Ownership of |contents| remains with the caller: it isn't transferred to the
+ * returned ArrayBuffer.  Callers of this function *must* ensure that they
+ * perform these two steps, in this order, to properly relinquish ownership of
+ * |contents|:
+ *
+ *   1. Call |JS_DetachArrayBuffer| on the buffer returned by this function.
+ *      (|JS_DetachArrayBuffer| is generally fallible, but a call under these
+ *      circumstances is guaranteed to succeed.)
+ *   2. |contents| may be deallocated or discarded consistent with the manner
+ *      in which it was allocated.
+ *
+ * Do not simply allow the returned buffer to be garbage-collected before
+ * deallocating |contents|, because in general there is no way to know *when*
+ * an object is fully garbage-collected to the point where this would be safe.
  */
-extern JS_PUBLIC_API JSObject* JS_NewArrayBufferWithExternalContents(
+extern JS_PUBLIC_API JSObject* JS_NewArrayBufferWithUserOwnedContents(
     JSContext* cx, size_t nbytes, void* contents);
 
 /**
- * Steal the contents of the given array buffer. The array buffer has its
- * 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
+ * Steal the contents of the given ArrayBuffer. The ArrayBuffer has its 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
+ * Create a new mapped ArrayBuffer 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.
+ * ownership is transferred to the new mapped ArrayBuffer.
  */
 extern JS_PUBLIC_API JSObject* JS_NewMappedArrayBufferWithContents(
     JSContext* cx, size_t nbytes, void* contents);
 
 /**
- * Create memory mapped array buffer contents.
+ * Create memory mapped ArrayBuffer contents.
  * Caller must take care of closing fd after calling this function.
  */
 extern JS_PUBLIC_API void* JS_CreateMappedArrayBufferContents(int fd,
                                                               size_t offset,
                                                               size_t length);
 
 /**
- * Release the allocated resource of mapped array buffer contents before the
+ * Release the allocated resource of mapped ArrayBuffer contents before the
  * object is created.
  * If a new object has been created by JS_NewMappedArrayBufferWithContents()
  * with this content, then JS_DetachArrayBuffer() should be used instead to
  * release the resource used by the object.
  */
 extern JS_PUBLIC_API void JS_ReleaseMappedArrayBufferContents(void* contents,
                                                               size_t length);
 
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -591,17 +591,17 @@ class Shape {
  * depending on the object's specific layout.
  */
 struct Object {
   shadow::ObjectGroup* group;
   shadow::Shape* shape;
   JS::Value* slots;
   void* _1;
 
-  static const size_t MAX_FIXED_SLOTS = 16;
+  static constexpr size_t MAX_FIXED_SLOTS = 16;
 
   size_t numFixedSlots() const {
     return (shape->immutableFlags & Shape::FIXED_SLOTS_MASK) >>
            Shape::FIXED_SLOTS_SHIFT;
   }
 
   JS::Value* fixedSlots() const {
     return (JS::Value*)(uintptr_t(this) + sizeof(shadow::Object));
@@ -1789,29 +1789,29 @@ extern JS_FRIEND_API js::Scalar::Type JS
  * unwrapping. If this test succeeds, then it is safe to call the various
  * accessor JSAPI calls defined below.
  */
 extern JS_FRIEND_API bool JS_IsArrayBufferObject(JSObject* obj);
 
 extern JS_FRIEND_API bool JS_IsSharedArrayBufferObject(JSObject* obj);
 
 /**
- * Return the available byte length of an array buffer.
+ * Return the available byte length of an ArrayBuffer.
  *
  * |obj| must have passed a JS_IsArrayBufferObject test, or somehow be known
  * that it would pass such a test: it is an ArrayBuffer or a wrapper of an
  * ArrayBuffer, and the unwrapping will succeed.
  */
 extern JS_FRIEND_API uint32_t JS_GetArrayBufferByteLength(JSObject* obj);
 
 extern JS_FRIEND_API uint32_t JS_GetSharedArrayBufferByteLength(JSObject* obj);
 
 /**
- * Return true if the arrayBuffer contains any data. This will return false for
- * ArrayBuffer.prototype and detached ArrayBuffers.
+ * Return true if the ArrayBuffer |obj| contains any data, i.e. it is not a
+ * detached ArrayBuffer.  (ArrayBuffer.prototype is not an ArrayBuffer.)
  *
  * |obj| must have passed a JS_IsArrayBufferObject test, or somehow be known
  * that it would pass such a test: it is an ArrayBuffer or a wrapper of an
  * ArrayBuffer, and the unwrapping will succeed.
  */
 extern JS_FRIEND_API bool JS_ArrayBufferHasData(JSObject* obj);
 
 /**
@@ -1844,17 +1844,17 @@ extern JS_FRIEND_API bool JS_IsMappedArr
  *
  * |obj| must have passed a JS_IsTypedArrayObject/JS_Is*Array test, or somehow
  * be known that it would pass such a test: it is a typed array or a wrapper of
  * a typed array, and the unwrapping will succeed.
  */
 extern JS_FRIEND_API uint32_t JS_GetTypedArrayLength(JSObject* obj);
 
 /**
- * Return the byte offset from the start of an array buffer to the start of a
+ * Return the byte offset from the start of an ArrayBuffer to the start of a
  * typed array view.
  *
  * |obj| must have passed a JS_IsTypedArrayObject/JS_Is*Array test, or somehow
  * be known that it would pass such a test: it is a typed array or a wrapper of
  * a typed array, and the unwrapping will succeed.
  */
 extern JS_FRIEND_API uint32_t JS_GetTypedArrayByteOffset(JSObject* obj);
 
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -1913,19 +1913,19 @@ static uint8_t* CacheEntry_getBytecode(J
 }
 
 static bool CacheEntry_setBytecode(JSContext* cx, HandleObject cache,
                                    uint8_t* buffer, uint32_t length) {
   MOZ_ASSERT(CacheEntry_isCacheEntry(cache));
 
   using BufferContents = ArrayBufferObject::BufferContents;
 
-  BufferContents contents = BufferContents::createPlainData(buffer);
+  BufferContents contents = BufferContents::createMalloced(buffer);
   Rooted<ArrayBufferObject*> arrayBuffer(
-      cx, ArrayBufferObject::create(cx, length, contents));
+      cx, ArrayBufferObject::createForContents(cx, length, contents));
   if (!arrayBuffer) {
     return false;
   }
 
   SetReservedSlot(cache, CacheEntry_BYTECODE, ObjectValue(*arrayBuffer));
   return true;
 }
 
@@ -6976,17 +6976,17 @@ class StreamCacheEntryObject : public Na
     if (!args.thisv().isObject() ||
         !args.thisv().toObject().is<StreamCacheEntryObject>()) {
       return false;
     }
 
     auto& bytes =
         args.thisv().toObject().as<StreamCacheEntryObject>().cache().bytes();
     RootedArrayBufferObject buffer(
-        cx, ArrayBufferObject::create(cx, bytes.length()));
+        cx, ArrayBufferObject::createZeroed(cx, bytes.length()));
     if (!buffer) {
       return false;
     }
 
     memcpy(buffer->dataPointer(), bytes.begin(), bytes.length());
 
     args.rval().setObject(*buffer);
     return true;
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "vm/ArrayBufferObject-inl.h"
 #include "vm/ArrayBufferObject.h"
 
 #include "mozilla/Alignment.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/FloatingPoint.h"
+#include "mozilla/Likely.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/TaggedAnonymousMemory.h"
 
 #include <string.h>
 #ifndef XP_WIN
 #  include <sys/mman.h>
 #endif
@@ -428,29 +429,30 @@ bool ArrayBufferObject::class_constructo
   // Refuse to allocate too large buffers, currently limited to ~2 GiB.
   if (byteLength > INT32_MAX) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_BAD_ARRAY_LENGTH);
     return false;
   }
 
   // 24.1.1.1, steps 1 and 4-6.
-  JSObject* bufobj = create(cx, uint32_t(byteLength), proto);
+  JSObject* bufobj = createZeroed(cx, uint32_t(byteLength), proto);
   if (!bufobj) {
     return false;
   }
   args.rval().setObject(*bufobj);
   return true;
 }
 
-static ArrayBufferObject::BufferContents AllocateArrayBufferContents(
-    JSContext* cx, uint32_t nbytes) {
-  uint8_t* p =
-      cx->pod_callocCanGC<uint8_t>(nbytes, js::ArrayBufferContentsArena);
-  return ArrayBufferObject::BufferContents::createPlainData(p);
+static uint8_t* AllocateArrayBufferContents(JSContext* cx, uint32_t nbytes) {
+  auto* p = cx->pod_callocCanGC<uint8_t>(nbytes, js::ArrayBufferContentsArena);
+  if (!p) {
+    ReportOutOfMemory(cx);
+  }
+  return p;
 }
 
 static void NoteViewBufferWasDetached(
     ArrayBufferViewObject* view, ArrayBufferObject::BufferContents newContents,
     JSContext* cx) {
   MOZ_ASSERT(!view->isSharedMemory());
 
   view->notifyBufferDetached(newContents.data());
@@ -917,17 +919,28 @@ bool js::CreateWasmBuffer(JSContext* cx,
 }
 
 // Note this function can return false with or without an exception pending. The
 // asm.js caller checks cx->isExceptionPending before propagating failure.
 // Returning false without throwing means that asm.js linking will fail which
 // will recompile as non-asm.js.
 /* static */ bool ArrayBufferObject::prepareForAsmJS(
     JSContext* cx, Handle<ArrayBufferObject*> buffer) {
-  MOZ_ASSERT(buffer->byteLength() % wasm::PageSize == 0);
+  MOZ_ASSERT(buffer->byteLength() % wasm::PageSize == 0,
+             "prior size checking should have guaranteed page-size multiple");
+  MOZ_ASSERT(buffer->byteLength() > 0,
+             "prior size checking should have excluded empty buffers");
+  MOZ_ASSERT(!buffer->isNoData(),
+             "size checking should have excluded detached or empty buffers");
+
+  static_assert(wasm::PageSize > MaxInlineBytes,
+                "inline data must be too small to be a page size multiple");
+  MOZ_ASSERT(!buffer->isInlineData(),
+             "inline-data buffers are implicitly excluded by size checks");
+
   // Don't assert cx->wasmHaveSignalHandlers because (1) they aren't needed
   // for asm.js, (2) they are only installed for WebAssembly, not asm.js.
 
   // wasm buffers can be detached at any time.
   if (buffer->isWasm()) {
     MOZ_ASSERT(!buffer->isPreparedForAsmJS());
     return false;
   }
@@ -938,43 +951,42 @@ bool js::CreateWasmBuffer(JSContext* cx,
   // this edge case by not allowing buffers with user-provided content to be
   // used with asm.js, as no callers exist that want to use such buffer with
   // asm.js.
   if (buffer->hasUserOwnedData()) {
     MOZ_ASSERT(!buffer->isPreparedForAsmJS());
     return false;
   }
 
-  MOZ_ASSERT(buffer->isPlainData() || buffer->isMapped() ||
+  MOZ_ASSERT(buffer->isMalloced() || buffer->isMapped() ||
              buffer->isExternal());
 
   // Buffers already prepared for asm.js need no further work.
   if (buffer->isPreparedForAsmJS()) {
     return true;
   }
 
   if (!buffer->ownsData()) {
-    BufferContents contents =
-        AllocateArrayBufferContents(cx, buffer->byteLength());
-    if (!contents) {
+    uint8_t* data = AllocateArrayBufferContents(cx, buffer->byteLength());
+    if (!data) {
       return false;
     }
-    memcpy(contents.data(), buffer->dataPointer(), buffer->byteLength());
-    buffer->changeContents(cx, contents, OwnsData);
+    memcpy(data, buffer->dataPointer(), buffer->byteLength());
+    buffer->changeContents(cx, BufferContents::createMalloced(data), OwnsData);
   }
 
   buffer->setIsPreparedForAsmJS();
   return true;
 }
 
 ArrayBufferObject::BufferContents ArrayBufferObject::createMappedContents(
     int fd, size_t offset, size_t length) {
   void* data =
       gc::AllocateMappedContent(fd, offset, length, ARRAY_BUFFER_ALIGNMENT);
-  return BufferContents::create<MAPPED>(data);
+  return BufferContents::createMapped(data);
 }
 
 uint8_t* ArrayBufferObject::inlineDataPointer() const {
   return static_cast<uint8_t*>(fixedData(JSCLASS_RESERVED_SLOTS(&class_)));
 }
 
 uint8_t* ArrayBufferObject::dataPointer() const {
   return static_cast<uint8_t*>(getFixedSlot(DATA_SLOT).toPrivate());
@@ -988,19 +1000,25 @@ ArrayBufferObject::FreeInfo* ArrayBuffer
   MOZ_ASSERT(isExternal());
   return reinterpret_cast<FreeInfo*>(inlineDataPointer());
 }
 
 void ArrayBufferObject::releaseData(FreeOp* fop) {
   MOZ_ASSERT(ownsData());
 
   switch (bufferKind()) {
-    case PLAIN_DATA:
+    case INLINE_DATA:
+      MOZ_ASSERT_UNREACHABLE("inline data should never be owned");
+      break;
+    case MALLOCED:
       fop->free_(dataPointer());
       break;
+    case NO_DATA:
+      MOZ_ASSERT(dataPointer() == nullptr);
+      break;
     case USER_OWNED:
       MOZ_ASSERT_UNREACHABLE("user-owned data should never be owned by this");
       break;
     case MAPPED:
       gc::DeallocateMappedContent(dataPointer(), byteLength());
       break;
     case WASM:
       WasmArrayRawBuffer::Release(dataPointer());
@@ -1011,18 +1029,16 @@ void ArrayBufferObject::releaseData(Free
         // free function will GC. We give the analyzer a hint here.
         // (Doing a GC in the free function is considered a programmer
         // error.)
         JS::AutoSuppressGCAnalysis nogc;
         freeInfo()->freeFunc(dataPointer(), freeInfo()->freeUserData);
       }
       break;
     case BAD1:
-    case BAD2:
-    case BAD3:
       MOZ_CRASH("invalid BufferKind encountered");
       break;
   }
 }
 
 void ArrayBufferObject::setDataPointer(BufferContents contents,
                                        OwnsState ownsData) {
   setFixedSlot(DATA_SLOT, PrivateValue(contents.data()));
@@ -1129,22 +1145,21 @@ Maybe<uint32_t> js::WasmArrayBufferMaxSi
   }
 
   WasmArrayRawBuffer* newRawBuf =
       WasmArrayRawBuffer::Allocate(newSize, Nothing());
   if (!newRawBuf) {
     return false;
   }
   BufferContents contents =
-      BufferContents::create<WASM>(newRawBuf->dataPointer());
+      BufferContents::createWasm(newRawBuf->dataPointer());
   newBuf->initialize(newSize, contents, OwnsData);
 
   memcpy(newBuf->dataPointer(), oldBuf->dataPointer(), oldBuf->byteLength());
-  ArrayBufferObject::detach(cx, oldBuf,
-                            BufferContents::createPlainData(nullptr));
+  ArrayBufferObject::detach(cx, oldBuf, BufferContents::createNoData());
   return true;
 }
 
 uint32_t ArrayBufferObject::wasmBoundsCheckLimit() const {
   if (isWasm()) {
     return contents().wasmBuffer()->boundsCheckLimit();
   }
   return byteLength();
@@ -1169,230 +1184,256 @@ uint32_t ArrayBufferObjectMaybeShared::w
 uint32_t ArrayBufferObject::flags() const {
   return uint32_t(getFixedSlot(FLAGS_SLOT).toInt32());
 }
 
 void ArrayBufferObject::setFlags(uint32_t flags) {
   setFixedSlot(FLAGS_SLOT, Int32Value(flags));
 }
 
-ArrayBufferObject* ArrayBufferObject::create(
+static MOZ_MUST_USE bool CheckArrayBufferTooLarge(JSContext* cx,
+                                                  uint32_t nbytes) {
+  // Refuse to allocate too large buffers, currently limited to ~2 GiB.
+  if (MOZ_UNLIKELY(nbytes > ArrayBufferObject::MaxBufferByteLength)) {
+    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+                              JSMSG_BAD_ARRAY_LENGTH);
+    return false;
+  }
+
+  return true;
+}
+
+ArrayBufferObject* ArrayBufferObject::createForContents(
     JSContext* cx, uint32_t nbytes, BufferContents contents,
-    OwnsState ownsState /* = OwnsData */, HandleObject proto /* = nullptr */,
-    NewObjectKind newKind /* = GenericObject */) {
-  MOZ_ASSERT_IF(contents.kind() == MAPPED, contents);
+    OwnsState ownsState /* = OwnsData */) {
+  MOZ_ASSERT(contents);
+  MOZ_ASSERT(contents.kind() != INLINE_DATA);
+  MOZ_ASSERT(contents.kind() != NO_DATA);
+  MOZ_ASSERT(contents.kind() != WASM);
 
   // 24.1.1.1, step 3 (Inlined 6.2.6.1 CreateByteDataBlock, step 2).
-  // Refuse to allocate too large buffers, currently limited to ~2 GiB.
-  if (nbytes > INT32_MAX) {
-    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
-                              JSMSG_BAD_ARRAY_LENGTH);
+  if (!CheckArrayBufferTooLarge(cx, nbytes)) {
     return nullptr;
   }
 
-  // If we need to allocate data, try to use a larger object size class so
-  // that the array buffer's data can be allocated inline with the object.
-  // The extra space will be left unused by the object's fixed slots and
-  // available for the buffer's data, see NewObject().
+  // Some |contents| kinds need to store extra data in the ArrayBuffer beyond a
+  // data pointer.  If needed for the particular kind, add extra fixed slots to
+  // the ArrayBuffer for use as raw storage to store such information.
   size_t reservedSlots = JSCLASS_RESERVED_SLOTS(&class_);
 
   size_t nslots = reservedSlots;
-  bool allocated = false;
-  if (contents) {
-    if (ownsState == OwnsData) {
-      if (contents.kind() == EXTERNAL) {
-        // Store the FreeInfo in the inline data slots so that we
-        // don't use up slots for it in non-refcounted array buffers.
-        size_t freeInfoSlots = JS_HOWMANY(sizeof(FreeInfo), sizeof(Value));
-        MOZ_ASSERT(
-            reservedSlots + freeInfoSlots <= NativeObject::MAX_FIXED_SLOTS,
-            "FreeInfo must fit in inline slots");
-        nslots += freeInfoSlots;
+  if (ownsState == OwnsData) {
+    if (contents.kind() == EXTERNAL) {
+      // Store the FreeInfo in the inline data slots so that we
+      // don't use up slots for it in non-refcounted array buffers.
+      size_t freeInfoSlots = JS_HOWMANY(sizeof(FreeInfo), sizeof(Value));
+      MOZ_ASSERT(reservedSlots + freeInfoSlots <= NativeObject::MAX_FIXED_SLOTS,
+                 "FreeInfo must fit in inline slots");
+      nslots += freeInfoSlots;
+    } else {
+      // The ABO is taking ownership, so account the bytes against the zone.
+      size_t nAllocated = nbytes;
+      if (contents.kind() == MAPPED) {
+        nAllocated = JS_ROUNDUP(nbytes, js::gc::SystemPageSize());
       } else {
-        // The ABO is taking ownership, so account the bytes against
-        // the zone.
-        size_t nAllocated = nbytes;
-        if (contents.kind() == MAPPED) {
-          nAllocated = JS_ROUNDUP(nbytes, js::gc::SystemPageSize());
-        }
-        cx->updateMallocCounter(nAllocated);
+        MOZ_ASSERT(contents.kind() == MALLOCED,
+                   "should have handled all possible callers' kinds");
       }
-    }
-  } else {
-    MOZ_ASSERT(ownsState == OwnsData);
-    size_t usableSlots = NativeObject::MAX_FIXED_SLOTS - reservedSlots;
-    if (nbytes <= usableSlots * sizeof(Value)) {
-      int newSlots = JS_HOWMANY(nbytes, sizeof(Value));
-      MOZ_ASSERT(int(nbytes) <= newSlots * int(sizeof(Value)));
-      nslots = reservedSlots + newSlots;
-      contents = BufferContents::createPlainData(nullptr);
-    } else {
-      contents = AllocateArrayBufferContents(cx, nbytes);
-      if (!contents) {
-        ReportOutOfMemory(cx);
-        return nullptr;
-      }
-      allocated = true;
+
+      // "mapped" bytes are fed into a "malloc" counter because (bug 1037358)
+      // this counter constitutes an input to the "when do we GC?" subsystem.
+      // Arguably it deserves renaming to something that doesn't narrowly cabin
+      // it to just "malloc" stuff, if we're going to use it this way.
+      cx->updateMallocCounter(nAllocated);
     }
   }
 
   MOZ_ASSERT(!(class_.flags & JSCLASS_HAS_PRIVATE));
   gc::AllocKind allocKind = gc::GetGCObjectKind(nslots);
 
   AutoSetNewObjectMetadata metadata(cx);
-  Rooted<ArrayBufferObject*> obj(cx, NewObjectWithClassProto<ArrayBufferObject>(
-                                         cx, proto, allocKind, newKind));
-  if (!obj) {
-    if (allocated) {
-      js_free(contents.data());
+  Rooted<ArrayBufferObject*> buffer(
+      cx, NewObjectWithClassProto<ArrayBufferObject>(cx, nullptr, allocKind,
+                                                     TenuredObject));
+  if (!buffer) {
+    return nullptr;
+  }
+
+  MOZ_ASSERT(!gc::IsInsideNursery(buffer),
+             "ArrayBufferObject has a finalizer that must be called to not "
+             "leak in some cases, so it can't be nursery-allocated");
+
+  buffer->initialize(nbytes, contents, ownsState);
+
+  return buffer;
+}
+
+ArrayBufferObject* ArrayBufferObject::createZeroed(
+    JSContext* cx, uint32_t nbytes, HandleObject proto /* = nullptr */) {
+  // 24.1.1.1, step 3 (Inlined 6.2.6.1 CreateByteDataBlock, step 2).
+  if (!CheckArrayBufferTooLarge(cx, nbytes)) {
+    return nullptr;
+  }
+
+  // Try fitting the data inline with the object by repurposing fixed-slot
+  // storage.  Add extra fixed slots if necessary to accomplish this, but don't
+  // exceed the maximum number of fixed slots!
+  size_t nslots = JSCLASS_RESERVED_SLOTS(&class_);
+  uint8_t* data;
+  if (nbytes <= MaxInlineBytes) {
+    int newSlots = JS_HOWMANY(nbytes, sizeof(Value));
+    MOZ_ASSERT(int(nbytes) <= newSlots * int(sizeof(Value)));
+
+    nslots += newSlots;
+    data = nullptr;
+  } else {
+    data = AllocateArrayBufferContents(cx, nbytes);
+    if (!data) {
+      return nullptr;
+    }
+  }
+
+  MOZ_ASSERT(!(class_.flags & JSCLASS_HAS_PRIVATE));
+  gc::AllocKind allocKind = gc::GetGCObjectKind(nslots);
+
+  AutoSetNewObjectMetadata metadata(cx);
+  Rooted<ArrayBufferObject*> buffer(
+      cx, NewObjectWithClassProto<ArrayBufferObject>(cx, proto, allocKind,
+                                                     GenericObject));
+  if (!buffer) {
+    if (data) {
+      js_free(data);
     }
     return nullptr;
   }
 
-  MOZ_ASSERT(obj->getClass() == &class_);
-  MOZ_ASSERT(!gc::IsInsideNursery(obj));
+  MOZ_ASSERT(!gc::IsInsideNursery(buffer),
+             "ArrayBufferObject has a finalizer that must be called to not "
+             "leak in some cases, so it can't be nursery-allocated");
 
-  if (!contents) {
-    void* data = obj->inlineDataPointer();
-    memset(data, 0, nbytes);
-    obj->initialize(nbytes, BufferContents::createPlainData(data),
-                    DoesntOwnData);
+  if (data) {
+    buffer->initialize(nbytes, BufferContents::createMalloced(data), OwnsData);
   } else {
-    obj->initialize(nbytes, contents, ownsState);
+    void* inlineData = buffer->initializeToInlineData(nbytes);
+    memset(inlineData, 0, nbytes);
   }
 
-  return obj;
-}
-
-ArrayBufferObject* ArrayBufferObject::create(
-    JSContext* cx, uint32_t nbytes, HandleObject proto /* = nullptr */) {
-  return create(cx, nbytes, BufferContents::createPlainData(nullptr),
-                OwnsState::OwnsData, proto);
+  return buffer;
 }
 
 ArrayBufferObject* ArrayBufferObject::createEmpty(JSContext* cx) {
   AutoSetNewObjectMetadata metadata(cx);
   ArrayBufferObject* obj = NewBuiltinClassInstance<ArrayBufferObject>(cx);
   if (!obj) {
     return nullptr;
   }
 
-  obj->setByteLength(0);
-  obj->setFlags(0);
-  obj->setFirstView(nullptr);
-  obj->setDataPointer(BufferContents::createPlainData(nullptr), DoesntOwnData);
-
+  obj->initialize(0, BufferContents::createNoData(), OwnsData);
   return obj;
 }
 
 ArrayBufferObject* ArrayBufferObject::createFromNewRawBuffer(
-    JSContext* cx, WasmArrayRawBuffer* buffer, uint32_t initialSize) {
+    JSContext* cx, WasmArrayRawBuffer* rawBuffer, uint32_t initialSize) {
   AutoSetNewObjectMetadata metadata(cx);
-  ArrayBufferObject* obj = NewBuiltinClassInstance<ArrayBufferObject>(cx);
-  if (!obj) {
-    WasmArrayRawBuffer::Release(buffer->dataPointer());
+  ArrayBufferObject* buffer = NewBuiltinClassInstance<ArrayBufferObject>(cx);
+  if (!buffer) {
+    WasmArrayRawBuffer::Release(rawBuffer->dataPointer());
     return nullptr;
   }
 
-  obj->setByteLength(initialSize);
-  obj->setFlags(0);
-  obj->setFirstView(nullptr);
+  buffer->setByteLength(initialSize);
+  buffer->setFlags(0);
+  buffer->setFirstView(nullptr);
 
-  auto contents = BufferContents::create<WASM>(buffer->dataPointer());
-  obj->setDataPointer(contents, OwnsData);
+  auto contents = BufferContents::createWasm(rawBuffer->dataPointer());
+  buffer->setDataPointer(contents, OwnsData);
 
   cx->updateMallocCounter(initialSize);
 
-  return obj;
-}
-
-/* static */ ArrayBufferObject::BufferContents
-ArrayBufferObject::externalizeContents(JSContext* cx,
-                                       Handle<ArrayBufferObject*> buffer,
-                                       bool hasStealableContents) {
-  MOZ_ASSERT(buffer->isPlainData(),
-             "only support doing this on ABOs containing plain data");
-  MOZ_ASSERT(!buffer->isDetached(), "must have contents to externalize");
-  MOZ_ASSERT_IF(hasStealableContents, buffer->hasStealableContents());
-
-  BufferContents contents = buffer->contents();
-
-  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::createFailed();
-  }
-  memcpy(newContents.data(), contents.data(), buffer->byteLength());
-  buffer->changeContents(cx, newContents, DoesntOwnData);
-
-  return newContents;
+  return buffer;
 }
 
 /* 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()));
   cx->check(buffer);
 
+  MOZ_ASSERT(!buffer->isPreparedForAsmJS(),
+             "asm.js-prepared buffers don't have detachable/stealable data");
+
+#ifdef DEBUG
+  if (hasStealableContents) {
+    MOZ_ASSERT(!buffer->isInlineData(),
+               "inline data is DoesntOwnData and isn't malloc-allocated");
+    MOZ_ASSERT(!buffer->isNoData(),
+               "null |dataPointer()| for the no-data case isn't stealable "
+               "because it would be confused with failure");
+    MOZ_ASSERT(!buffer->hasUserOwnedData(),
+               "user-owned data isn't stealable or necessarily malloc'd");
+
+    // wasm buffers can't be stolen by |JS_StealArrayBufferContents|, but
+    // but *this* function is used internally by the impl of memory growth, so
+    // we can't assert |!buffer->isWasm()|.
+
+    // Mapped buffers can't be stolen by |JS_StealArrayBufferContents| which
+    // only can steal malloc'd data.  But they *can* be stolen when structured
+    // clone code calls this function, so they can appear here.
+
+    MOZ_ASSERT(!buffer->isExternal(),
+               "external data isn't necessarily malloc-allocated");
+  }
+#endif
+
   BufferContents oldContents = buffer->contents();
 
   if (hasStealableContents) {
-    // Return the old contents and reset the detached buffer's data
-    // pointer. This pointer should never be accessed.
-    auto newContents = BufferContents::createPlainData(nullptr);
     buffer->setOwnsData(DoesntOwnData);  // Do not free the stolen data.
-    ArrayBufferObject::detach(cx, buffer, newContents);
-    buffer->setOwnsData(DoesntOwnData);  // Do not free the nullptr.
+    ArrayBufferObject::detach(cx, buffer, BufferContents::createNoData());
     return oldContents;
   }
 
   // Create a new chunk of memory to return since we cannot steal the
   // existing contents away from the buffer.
-  BufferContents contentsCopy =
-      AllocateArrayBufferContents(cx, buffer->byteLength());
-  if (!contentsCopy) {
+  uint8_t* dataCopy = AllocateArrayBufferContents(cx, buffer->byteLength());
+  if (!dataCopy) {
     return BufferContents::createFailed();
   }
 
   if (buffer->byteLength() > 0) {
-    memcpy(contentsCopy.data(), oldContents.data(), buffer->byteLength());
+    memcpy(dataCopy, oldContents.data(), buffer->byteLength());
   }
   ArrayBufferObject::detach(cx, buffer, oldContents);
-  return contentsCopy;
+  return BufferContents::createMalloced(dataCopy);
 }
 
 /* static */ void ArrayBufferObject::addSizeOfExcludingThis(
     JSObject* obj, mozilla::MallocSizeOf mallocSizeOf, JS::ClassInfo* info) {
   ArrayBufferObject& buffer = AsArrayBuffer(obj);
 
   if (!buffer.ownsData()) {
     return;
   }
 
   switch (buffer.bufferKind()) {
-    case PLAIN_DATA:
+    case INLINE_DATA:
+      MOZ_ASSERT_UNREACHABLE("inline data should never be owned and should be "
+                             "accounted for in |this|'s memory");
+      break;
+    case MALLOCED:
       if (buffer.isPreparedForAsmJS()) {
         info->objectsMallocHeapElementsAsmJS +=
             mallocSizeOf(buffer.dataPointer());
       } else {
         info->objectsMallocHeapElementsNormal +=
             mallocSizeOf(buffer.dataPointer());
       }
       break;
+    case NO_DATA:
+      MOZ_ASSERT(buffer.dataPointer() == nullptr);
+      break;
     case USER_OWNED:
       MOZ_ASSERT_UNREACHABLE(
           "user-owned data should never be owned by this, and such memory "
           "should be accounted for by the code that provided it");
       break;
     case MAPPED:
       info->objectsNonHeapElementsNormal += buffer.byteLength();
       break;
@@ -1400,18 +1441,16 @@ ArrayBufferObject::externalizeContents(J
       info->objectsNonHeapElementsWasm += buffer.byteLength();
       MOZ_ASSERT(buffer.wasmMappedSize() >= buffer.byteLength());
       info->wasmGuardPages += buffer.wasmMappedSize() - buffer.byteLength();
       break;
     case EXTERNAL:
       MOZ_CRASH("external buffers not currently supported");
       break;
     case BAD1:
-    case BAD2:
-    case BAD3:
       MOZ_CRASH("bad bufferKind()");
   }
 }
 
 /* static */ void ArrayBufferObject::finalize(FreeOp* fop, JSObject* obj) {
   ArrayBufferObject& buffer = obj->as<ArrayBufferObject>();
 
   if (buffer.ownsData()) {
@@ -1636,19 +1675,21 @@ JS_FRIEND_API bool JS_DetachArrayBuffer(
   Rooted<ArrayBufferObject*> buffer(cx, &obj->as<ArrayBufferObject>());
 
   if (buffer->isWasm() || buffer->isPreparedForAsmJS()) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_WASM_NO_TRANSFER);
     return false;
   }
 
-  ArrayBufferObject::BufferContents newContents =
+  using BufferContents = ArrayBufferObject::BufferContents;
+
+  BufferContents newContents =
       buffer->hasStealableContents()
-          ? ArrayBufferObject::BufferContents::createPlainData(nullptr)
+          ? BufferContents::createNoData()
           : buffer->contents();
 
   ArrayBufferObject::detach(cx, buffer, newContents);
 
   return true;
 }
 
 JS_FRIEND_API bool JS_IsDetachedArrayBufferObject(JSObject* obj) {
@@ -1658,120 +1699,87 @@ JS_FRIEND_API bool JS_IsDetachedArrayBuf
   }
 
   return aobj->isDetached();
 }
 
 JS_FRIEND_API JSObject* JS_NewArrayBuffer(JSContext* cx, uint32_t nbytes) {
   AssertHeapIsIdle();
   CHECK_THREAD(cx);
-  MOZ_ASSERT(nbytes <= INT32_MAX);
-  return ArrayBufferObject::create(cx, nbytes);
+
+  return ArrayBufferObject::createZeroed(cx, nbytes);
 }
 
 JS_PUBLIC_API JSObject* JS_NewArrayBufferWithContents(JSContext* cx,
                                                       size_t nbytes,
                                                       void* data) {
   AssertHeapIsIdle();
   CHECK_THREAD(cx);
   MOZ_ASSERT_IF(!data, nbytes == 0);
 
+  if (!data) {
+    // Don't pass nulled contents to |createForContents|.
+    return ArrayBufferObject::createZeroed(cx, 0);
+  }
+
   using BufferContents = ArrayBufferObject::BufferContents;
 
-  BufferContents contents = BufferContents::createPlainData(data);
-  return ArrayBufferObject::create(cx, nbytes, contents,
-                                   ArrayBufferObject::OwnsData,
-                                   /* proto = */ nullptr, TenuredObject);
+  BufferContents contents = BufferContents::createMalloced(data);
+  return ArrayBufferObject::createForContents(cx, nbytes, contents,
+                                              ArrayBufferObject::OwnsData);
 }
 
 JS_PUBLIC_API JSObject* JS_NewExternalArrayBuffer(
     JSContext* cx, size_t nbytes, void* data,
     JS::BufferContentsFreeFunc freeFunc, void* freeUserData) {
   AssertHeapIsIdle();
   CHECK_THREAD(cx);
 
   MOZ_ASSERT(data);
   MOZ_ASSERT(nbytes > 0);
 
-  ArrayBufferObject::BufferContents contents =
-      ArrayBufferObject::BufferContents::createExternal(data, freeFunc,
-                                                        freeUserData);
-  return ArrayBufferObject::create(cx, nbytes, contents,
-                                   ArrayBufferObject::OwnsData,
-                                   /* proto = */ nullptr, TenuredObject);
+  using BufferContents = ArrayBufferObject::BufferContents;
+
+  BufferContents contents =
+      BufferContents::createExternal(data, freeFunc, freeUserData);
+  return ArrayBufferObject::createForContents(cx, nbytes, contents,
+                                              ArrayBufferObject::OwnsData);
 }
 
-JS_PUBLIC_API JSObject* JS_NewArrayBufferWithExternalContents(JSContext* cx,
-                                                              size_t nbytes,
-                                                              void* data) {
+JS_PUBLIC_API JSObject* JS_NewArrayBufferWithUserOwnedContents(JSContext* cx,
+                                                               size_t nbytes,
+                                                               void* data) {
   AssertHeapIsIdle();
   CHECK_THREAD(cx);
-  MOZ_ASSERT_IF(!data, nbytes == 0);
+
+  MOZ_ASSERT(data);
 
   using BufferContents = ArrayBufferObject::BufferContents;
 
   BufferContents contents = BufferContents::createUserOwned(data);
-  return ArrayBufferObject::create(cx, nbytes, contents,
-                                   ArrayBufferObject::DoesntOwnData,
-                                   /* proto = */ nullptr, TenuredObject);
+  return ArrayBufferObject::createForContents(cx, nbytes, contents,
+                                              ArrayBufferObject::DoesntOwnData);
 }
 
 JS_FRIEND_API bool JS_IsArrayBufferObject(JSObject* obj) {
   return obj->canUnwrapAs<ArrayBufferObject>();
 }
 
 JS_FRIEND_API bool JS_ArrayBufferHasData(JSObject* obj) {
-  return obj->unwrapAs<ArrayBufferObject>().hasData();
+  return !obj->unwrapAs<ArrayBufferObject>().isDetached();
 }
 
 JS_FRIEND_API JSObject* js::UnwrapArrayBuffer(JSObject* obj) {
   return obj->maybeUnwrapIf<ArrayBufferObject>();
 }
 
 JS_FRIEND_API JSObject* js::UnwrapSharedArrayBuffer(JSObject* obj) {
   return obj->maybeUnwrapIf<SharedArrayBufferObject>();
 }
 
-JS_PUBLIC_API void* JS_ExternalizeArrayBufferContents(JSContext* cx,
-                                                      HandleObject obj) {
-  AssertHeapIsIdle();
-  CHECK_THREAD(cx);
-  cx->check(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->isPlainData()) {
-    // This operation isn't supported on mapped or wasm ArrayBufferObjects, or
-    // on ArrayBufferObjects with user-provided data.
-    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();
   CHECK_THREAD(cx);
   cx->check(objArg);
 
   JSObject* obj = CheckedUnwrapStatic(objArg);
   if (!obj) {
@@ -1795,43 +1803,47 @@ JS_PUBLIC_API void* JS_StealArrayBufferC
   if (buffer->isWasm() || buffer->isPreparedForAsmJS()) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_WASM_NO_TRANSFER);
     return nullptr;
   }
 
   // The caller assumes that a plain malloc'd buffer is returned.  To steal
   // actual contents, then, we must have |hasStealableContents()| *and* the
-  // contents must be |isPlainData()|.  (Mapped data would not be malloc'd;
-  // user-provided data we flat-out know nothing about at all -- although it
-  // *should* have not passed the |hasStealableContents()| check anyway.)
+  // contents must be |isMalloced()|.  (Inline data would not be malloc'd and
+  // shouldn't pass |hasStealableContents()| anyway; no-data would be nullptr
+  // and would signal failure if we returned it, plus |hasStealableContents()|
+  // specifically excludes it; user-provided data we know nothing about at all
+  // -- although it *should* have not passed the |hasStealableContents()| check
+  // anyway because it's not owned; mapped data wouldn't be malloc'd; external
+  // data has to be freed using a provided function.)
   //
   // In the future, we could consider returning something that handles
   // releasing the memory, in the mapped-data case.
   bool hasStealableContents =
-      buffer->hasStealableContents() && buffer->isPlainData();
+      buffer->hasStealableContents() && buffer->isMalloced();
 
   AutoRealm ar(cx, buffer);
   return ArrayBufferObject::stealContents(cx, buffer, hasStealableContents)
       .data();
 }
 
 JS_PUBLIC_API JSObject* JS_NewMappedArrayBufferWithContents(JSContext* cx,
                                                             size_t nbytes,
                                                             void* data) {
   AssertHeapIsIdle();
   CHECK_THREAD(cx);
 
   MOZ_ASSERT(data);
-  ArrayBufferObject::BufferContents contents =
-      ArrayBufferObject::BufferContents::create<ArrayBufferObject::MAPPED>(
-          data);
-  return ArrayBufferObject::create(cx, nbytes, contents,
-                                   ArrayBufferObject::OwnsData,
-                                   /* proto = */ nullptr, TenuredObject);
+
+  using BufferContents = ArrayBufferObject::BufferContents;
+
+  BufferContents contents = BufferContents::createMapped(data);
+  return ArrayBufferObject::createForContents(cx, nbytes, contents,
+                                              ArrayBufferObject::OwnsData);
 }
 
 JS_PUBLIC_API void* JS_CreateMappedArrayBufferContents(int fd, size_t offset,
                                                        size_t length) {
   return ArrayBufferObject::createMappedContents(fd, offset, length).data();
 }
 
 JS_PUBLIC_API void JS_ReleaseMappedArrayBufferContents(void* contents,
--- a/js/src/vm/ArrayBufferObject.h
+++ b/js/src/vm/ArrayBufferObject.h
@@ -160,45 +160,57 @@ class ArrayBufferObject : public ArrayBu
   static const size_t ARRAY_BUFFER_ALIGNMENT = 8;
 
   static_assert(FLAGS_SLOT == JS_ARRAYBUFFER_FLAGS_SLOT,
                 "self-hosted code with burned-in constants must get the "
                 "right flags slot");
 
   // The length of an ArrayBuffer or SharedArrayBuffer can be at most
   // INT32_MAX, and much code must change if this changes.
+  static constexpr size_t MaxBufferByteLength = INT32_MAX;
 
-  static const size_t MaxBufferByteLength = INT32_MAX;
+  /** The largest number of bytes that can be stored inline. */
+  static constexpr size_t MaxInlineBytes =
+    (NativeObject::MAX_FIXED_SLOTS - RESERVED_SLOTS) * sizeof(JS::Value);
 
  public:
   enum OwnsState {
     DoesntOwnData = 0,
     OwnsData = 1,
   };
 
   enum BufferKind {
-    /** Malloced or inline data. */
-    PLAIN_DATA = 0b000,
+    /** Inline data kept in the repurposed slots of this ArrayBufferObject. */
+    INLINE_DATA = 0b000,
+
+    /* Data allocated using the SpiderMonkey allocator. */
+    MALLOCED = 0b001,
+
+    /**
+     * No bytes are associated with this buffer.  (This could be because the
+     * buffer is detached, because it's an internal, newborn buffer not yet
+     * overwritten with user-exposable semantics, or some other reason.  The
+     * point is, don't read precise language semantics into this kind.)
+     */
+    NO_DATA = 0b010,
 
     /**
      * User-owned memory.  The associated buffer must be manually detached
      * before the user invalidates (deallocates, reuses the storage of, &c.)
      * the user-owned memory.
      */
-    USER_OWNED = 0b001,
+    USER_OWNED = 0b011,
 
-    WASM = 0b010,
-    MAPPED = 0b011,
-    EXTERNAL = 0b100,
+    WASM = 0b100,
+    MAPPED = 0b101,
+    EXTERNAL = 0b110,
 
     // These kind-values are currently invalid.  We intend to expand valid
     // BufferKinds in the future to either partly or fully use these values.
-    BAD1 = 0b101,
-    BAD2 = 0b110,
-    BAD3 = 0b111,
+    BAD1 = 0b111,
 
     KIND_MASK = 0b111
   };
 
  protected:
   enum ArrayBufferFlags {
     // The flags also store the BufferKind
     BUFFER_KIND_MASK = BufferKind::KIND_MASK,
@@ -214,19 +226,21 @@ class ArrayBufferObject : public ArrayBu
     // allocate their data inline, and buffers that are created lazily for
     // typed objects with inline storage, in which case the buffer points
     // directly to the typed object's storage.
     OWNS_DATA = 0b1'0000,
 
     // Views of this buffer might include typed objects.
     TYPED_OBJECT_VIEWS = 0b10'0000,
 
-    // This PLAIN_DATA, MAPPED, or EXTERNAL buffer (only WASM and USER_OWNED
-    // are excluded) has been prepared for asm.js and cannot henceforth be
-    // transferred/detached.
+    // This MALLOCED, MAPPED, or EXTERNAL buffer has been prepared for asm.js
+    // and cannot henceforth be transferred/detached.  (WASM, USER_OWNED, and
+    // INLINE_DATA buffers can't be prepared for asm.js -- although if an
+    // INLINE_DATA buffer is used with asm.js, it's silently rewritten into a
+    // MALLOCED buffer which *can* be prepared.)
     FOR_ASMJS = 0b100'0000,
   };
 
   static_assert(JS_ARRAYBUFFER_DETACHED_FLAG == DETACHED,
                 "self-hosted code with burned-in constants must use the "
                 "correct DETACHED bit value");
 
  public:
@@ -248,38 +262,52 @@ class ArrayBufferObject : public ArrayBu
       MOZ_ASSERT((kind_ & ~KIND_MASK) == 0);
       MOZ_ASSERT_IF(free_ || freeUserData_, kind_ == EXTERNAL);
 
       // It is the caller's responsibility to ensure that the
       // BufferContents does not outlive the data.
     }
 
    public:
-    template <BufferKind Kind>
-    static BufferContents create(void* data) {
-      return BufferContents(static_cast<uint8_t*>(data), Kind);
+    static BufferContents createInlineData(void* data) {
+      return BufferContents(static_cast<uint8_t*>(data), INLINE_DATA);
     }
 
-    static BufferContents createPlainData(void* data) {
-      return BufferContents(static_cast<uint8_t*>(data), PLAIN_DATA);
+    static BufferContents createMalloced(void* data) {
+      return BufferContents(static_cast<uint8_t*>(data), MALLOCED);
+    }
+
+    static BufferContents createNoData() {
+      return BufferContents(nullptr, NO_DATA);
     }
 
     static BufferContents createUserOwned(void* data) {
       return BufferContents(static_cast<uint8_t*>(data), USER_OWNED);
     }
 
+    static BufferContents createWasm(void* data) {
+      return BufferContents(static_cast<uint8_t*>(data), WASM);
+    }
+
+    static BufferContents createMapped(void* data) {
+      return BufferContents(static_cast<uint8_t*>(data), MAPPED);
+    }
+
     static BufferContents createExternal(void* data,
                                          JS::BufferContentsFreeFunc freeFunc,
                                          void* freeUserData = nullptr) {
       return BufferContents(static_cast<uint8_t*>(data), EXTERNAL, freeFunc,
                             freeUserData);
     }
 
     static BufferContents createFailed() {
-      return BufferContents(nullptr, PLAIN_DATA);
+      // There's no harm in tagging this as MALLOCED, even tho obviously it
+      // isn't.  And adding an extra tag purely for this case is a complication
+      // that presently appears avoidable.
+      return BufferContents(nullptr, MALLOCED);
     }
 
     uint8_t* data() const { return data_; }
     BufferKind kind() const { return kind_; }
     JS::BufferContentsFreeFunc freeFunc() const { return free_; }
     void* freeUserData() const { return freeUserData_; }
 
     explicit operator bool() const { return data_ != nullptr; }
@@ -294,23 +322,22 @@ class ArrayBufferObject : public ArrayBu
   static bool fun_slice(JSContext* cx, unsigned argc, Value* vp);
 
   static bool fun_isView(JSContext* cx, unsigned argc, Value* vp);
 
   static bool fun_species(JSContext* cx, unsigned argc, Value* vp);
 
   static bool class_constructor(JSContext* cx, unsigned argc, Value* vp);
 
-  static ArrayBufferObject* create(JSContext* cx, uint32_t nbytes,
-                                   BufferContents contents,
-                                   OwnsState ownsState = OwnsData,
-                                   HandleObject proto = nullptr,
-                                   NewObjectKind newKind = GenericObject);
-  static ArrayBufferObject* create(JSContext* cx, uint32_t nbytes,
-                                   HandleObject proto = nullptr);
+  static ArrayBufferObject* createForContents(JSContext* cx, uint32_t nbytes,
+                                              BufferContents contents,
+                                              OwnsState ownsState = OwnsData);
+
+  static ArrayBufferObject* createZeroed(JSContext* cx, uint32_t nbytes,
+                                         HandleObject proto = nullptr);
 
   // Create an ArrayBufferObject that is safely finalizable and can later be
   // initialize()d to become a real, content-visible ArrayBufferObject.
   static ArrayBufferObject* createEmpty(JSContext* cx);
 
   // Create an ArrayBufferObject using the provided buffer and size.  Assumes
   // ownership of |buffer| even in case of failure, i.e. on failure |buffer|
   // is deallocated.
@@ -319,26 +346,31 @@ class ArrayBufferObject : public ArrayBu
                                                    uint32_t initialSize);
 
   static void copyData(Handle<ArrayBufferObject*> toBuffer, uint32_t toIndex,
                        Handle<ArrayBufferObject*> fromBuffer,
                        uint32_t fromIndex, uint32_t count);
 
   static size_t objectMoved(JSObject* obj, 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();
+    // Inline data is always DoesntOwnData and so will fail the first test.
+    if (ownsData()) {
+      MOZ_ASSERT(!isInlineData(), "inline data is always DoesntOwnData");
+
+      // Making no data stealable is tricky, because it'd be null and usually
+      // that signals failure, so directly exclude it here.
+      return !isPreparedForAsmJS() && !isNoData() && !isWasm();
+    }
+
+    return false;
   }
 
   static void addSizeOfExcludingThis(JSObject* obj,
                                      mozilla::MallocSizeOf mallocSizeOf,
                                      JS::ClassInfo* info);
 
   // ArrayBufferObjects (strongly) store the first view added to them, while
   // later views are (weakly) stored in the compartment's InnerViewTable
@@ -382,32 +414,29 @@ class ArrayBufferObject : public ArrayBu
                             freeInfo()->freeUserData);
     }
     return BufferContents(dataPointer(), bufferKind());
   }
   bool hasInlineData() const { return dataPointer() == inlineDataPointer(); }
 
   void releaseData(FreeOp* fop);
 
-  /*
-   * Check if the arrayBuffer contains any data. This will return false for
-   * ArrayBuffer.prototype and detached ArrayBuffers.
-   */
-  bool hasData() const { return getClass() == &class_; }
-
   BufferKind bufferKind() const {
     return BufferKind(flags() & BUFFER_KIND_MASK);
   }
 
-  bool isPlainData() const { return bufferKind() == PLAIN_DATA; }
+  bool isInlineData() const { return bufferKind() == INLINE_DATA; }
+  bool isMalloced() const { return bufferKind() == MALLOCED; }
+  bool isNoData() const { return bufferKind() == NO_DATA; }
   bool hasUserOwnedData() const { return bufferKind() == USER_OWNED; }
 
   bool isWasm() const { return bufferKind() == WASM; }
   bool isMapped() const { return bufferKind() == MAPPED; }
   bool isExternal() const { return bufferKind() == EXTERNAL; }
+
   bool isDetached() const { return flags() & DETACHED; }
   bool isPreparedForAsmJS() const { return flags() & FOR_ASMJS; }
 
   // WebAssembly support:
   static MOZ_MUST_USE bool prepareForAsmJS(JSContext* cx,
                                            Handle<ArrayBufferObject*> buffer);
   size_t wasmMappedSize() const;
   mozilla::Maybe<uint32_t> wasmMaxSize() const;
@@ -421,17 +450,16 @@ class ArrayBufferObject : public ArrayBu
 #endif
   uint32_t wasmBoundsCheckLimit() const;
 
   static void finalize(FreeOp* fop, JSObject* obj);
 
   static BufferContents createMappedContents(int fd, size_t offset,
                                              size_t length);
 
-  static size_t offsetOfFlagsSlot() { return getFixedSlotOffset(FLAGS_SLOT); }
   static size_t offsetOfDataSlot() { return getFixedSlotOffset(DATA_SLOT); }
 
   void setHasTypedObjectViews() { setFlags(flags() | TYPED_OBJECT_VIEWS); }
 
  protected:
   void setDataPointer(BufferContents contents, OwnsState ownsState);
   void setByteLength(uint32_t length);
 
@@ -444,27 +472,35 @@ class ArrayBufferObject : public ArrayBu
   }
 
   bool hasTypedObjectViews() const { return flags() & TYPED_OBJECT_VIEWS; }
 
   void setIsDetached() { setFlags(flags() | DETACHED); }
   void setIsPreparedForAsmJS() {
     MOZ_ASSERT(!isWasm());
     MOZ_ASSERT(!hasUserOwnedData());
-    MOZ_ASSERT(isPlainData() || isMapped() || isExternal());
+    MOZ_ASSERT(!isInlineData());
+    MOZ_ASSERT(isMalloced() || isMapped() || isExternal());
     setFlags(flags() | FOR_ASMJS);
   }
 
   void initialize(size_t byteLength, BufferContents contents,
                   OwnsState ownsState) {
     setByteLength(byteLength);
     setFlags(0);
     setFirstView(nullptr);
     setDataPointer(contents, ownsState);
   }
+
+  void* initializeToInlineData(size_t byteLength) {
+    void* data = inlineDataPointer();
+    initialize(byteLength, BufferContents::createInlineData(data),
+               DoesntOwnData);
+    return data;
+  }
 };
 
 typedef Rooted<ArrayBufferObject*> RootedArrayBufferObject;
 typedef Handle<ArrayBufferObject*> HandleArrayBufferObject;
 typedef MutableHandle<ArrayBufferObject*> MutableHandleArrayBufferObject;
 
 bool CreateWasmBuffer(JSContext* cx, const wasm::Limits& memory,
                       MutableHandleArrayBufferObjectMaybeShared buffer);
--- a/js/src/vm/NativeObject.h
+++ b/js/src/vm/NativeObject.h
@@ -1028,17 +1028,17 @@ class NativeObject : public ShapedObject
   }
 
   MOZ_ALWAYS_INLINE void initSlotUnchecked(uint32_t slot, const Value& value) {
     getSlotAddressUnchecked(slot)->init(this, HeapSlot::Slot, slot, value);
   }
 
   // MAX_FIXED_SLOTS is the biggest number of fixed slots our GC
   // size classes will give an object.
-  static const uint32_t MAX_FIXED_SLOTS = shadow::Object::MAX_FIXED_SLOTS;
+  static constexpr uint32_t MAX_FIXED_SLOTS = shadow::Object::MAX_FIXED_SLOTS;
 
  protected:
   MOZ_ALWAYS_INLINE bool updateSlotsForSpan(JSContext* cx, size_t oldSpan,
                                             size_t newSpan);
 
  private:
   void prepareElementRangeForOverwrite(size_t start, size_t end) {
     MOZ_ASSERT(end <= getDenseInitializedLength());
--- a/js/src/vm/StructuredClone.cpp
+++ b/js/src/vm/StructuredClone.cpp
@@ -1882,16 +1882,18 @@ bool JSStructuredCloneWriter::transferOw
         if (!bufContents) {
           return false;  // out of memory
         }
 
         content = bufContents.data();
         if (bufContents.kind() == ArrayBufferObject::MAPPED) {
           ownership = JS::SCTAG_TMO_MAPPED_DATA;
         } else {
+          MOZ_ASSERT(bufContents.kind() == ArrayBufferObject::MALLOCED,
+                     "failing to handle new ArrayBuffer kind?");
           ownership = JS::SCTAG_TMO_ALLOC_DATA;
         }
         extraData = nbytes;
       }
     } else {
       if (!out.buf.callbacks_ || !out.buf.callbacks_->writeTransfer) {
         return reportDataCloneError(JS_SCERR_TRANSFERABLE);
       }
@@ -2197,17 +2199,17 @@ bool JSStructuredCloneReader::readDataVi
 
   allObjs[placeholderIndex].set(vp);
 
   return true;
 }
 
 bool JSStructuredCloneReader::readArrayBuffer(uint32_t nbytes,
                                               MutableHandleValue vp) {
-  JSObject* obj = ArrayBufferObject::create(context(), nbytes);
+  JSObject* obj = ArrayBufferObject::createZeroed(context(), nbytes);
   if (!obj) {
     return false;
   }
   vp.setObject(*obj);
   ArrayBufferObject& buffer = obj->as<ArrayBufferObject>();
   MOZ_ASSERT(buffer.byteLength() == nbytes);
   return in.readArray(buffer.dataPointer(), nbytes);
 }
@@ -2315,17 +2317,17 @@ bool JSStructuredCloneReader::readV1Arra
       TypedArrayElemSize(static_cast<Scalar::Type>(arrayType));
   if (!nbytes.isValid() || nbytes.value() > UINT32_MAX) {
     JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
                               JSMSG_SC_BAD_SERIALIZED_DATA,
                               "invalid typed array size");
     return false;
   }
 
-  JSObject* obj = ArrayBufferObject::create(context(), nbytes.value());
+  JSObject* obj = ArrayBufferObject::createZeroed(context(), nbytes.value());
   if (!obj) {
     return false;
   }
   vp.setObject(*obj);
   ArrayBufferObject& buffer = obj->as<ArrayBufferObject>();
   MOZ_ASSERT(buffer.byteLength() == nbytes);
 
   switch (arrayType) {
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -74,17 +74,17 @@ using mozilla::IsAsciiDigit;
 /* static */ bool TypedArrayObject::ensureHasBuffer(
     JSContext* cx, Handle<TypedArrayObject*> tarray) {
   if (tarray->hasBuffer()) {
     return true;
   }
 
   AutoRealm ar(cx, tarray);
   Rooted<ArrayBufferObject*> buffer(
-      cx, ArrayBufferObject::create(cx, tarray->byteLength()));
+      cx, ArrayBufferObject::createZeroed(cx, tarray->byteLength()));
   if (!buffer) {
     return false;
   }
 
   // Attaching the first view to an array buffer is infallible.
   MOZ_ALWAYS_TRUE(buffer->addView(cx, tarray));
 
   // tarray is not shared, because if it were it would have a buffer.
@@ -902,17 +902,17 @@ class TypedArrayObjectTemplate : public 
                   "ArrayBuffer inline storage shouldn't waste any space");
 
     if (!nonDefaultProto && byteLength <= INLINE_BUFFER_LIMIT) {
       // The array's data can be inline, and the buffer created lazily.
       return true;
     }
 
     ArrayBufferObject* buf =
-        ArrayBufferObject::create(cx, byteLength, nonDefaultProto);
+        ArrayBufferObject::createZeroed(cx, byteLength, nonDefaultProto);
     if (!buf) {
       return false;
     }
 
     buffer.set(buf);
     return true;
   }
 
--- a/js/src/wasm/WasmJS.cpp
+++ b/js/src/wasm/WasmJS.cpp
@@ -988,17 +988,17 @@ static JSString* UTF8CharsToString(JSCon
   for (const CustomSection& cs : module->customSections()) {
     if (name.length() != cs.name.length()) {
       continue;
     }
     if (memcmp(name.begin(), cs.name.begin(), name.length())) {
       continue;
     }
 
-    buf = ArrayBufferObject::create(cx, cs.payload->length());
+    buf = ArrayBufferObject::createZeroed(cx, cs.payload->length());
     if (!buf) {
       return false;
     }
 
     memcpy(buf->dataPointer(), cs.payload->begin(), cs.payload->length());
     if (!elems.append(ObjectValue(*buf))) {
       return false;
     }
--- a/layout/generic/ReflowInput.cpp
+++ b/layout/generic/ReflowInput.cpp
@@ -2926,16 +2926,19 @@ bool SizeComputationInput::ComputeMargin
   // If style style can provide us the margin directly, then use it.
   const nsStyleMargin* styleMargin = mFrame->StyleMargin();
 
   bool isCBDependent = !styleMargin->GetMargin(ComputedPhysicalMargin());
   if (isCBDependent) {
     // We have to compute the value. Note that this calculation is
     // performed according to the writing mode of the containing block
     // (http://dev.w3.org/csswg/css-writing-modes-3/#orthogonal-flows)
+    if (aPercentBasis == NS_UNCONSTRAINEDSIZE) {
+      aPercentBasis = 0;
+    }
     LogicalMargin m(aWM);
     m.IStart(aWM) = nsLayoutUtils::ComputeCBDependentValue(
         aPercentBasis, styleMargin->mMargin.GetIStart(aWM));
     m.IEnd(aWM) = nsLayoutUtils::ComputeCBDependentValue(
         aPercentBasis, styleMargin->mMargin.GetIEnd(aWM));
 
     m.BStart(aWM) = nsLayoutUtils::ComputeCBDependentValue(
         aPercentBasis, styleMargin->mMargin.GetBStart(aWM));
@@ -2971,16 +2974,19 @@ bool SizeComputationInput::ComputePaddin
       LayoutFrameType::TableRow == aFrameType ||
       LayoutFrameType::TableCol == aFrameType) {
     ComputedPhysicalPadding().SizeTo(0, 0, 0, 0);
   } else if (isCBDependent) {
     // We have to compute the value. This calculation is performed
     // according to the writing mode of the containing block
     // (http://dev.w3.org/csswg/css-writing-modes-3/#orthogonal-flows)
     // clamp negative calc() results to 0
+    if (aPercentBasis == NS_UNCONSTRAINEDSIZE) {
+      aPercentBasis = 0;
+    }
     LogicalMargin p(aWM);
     p.IStart(aWM) =
         std::max(0, nsLayoutUtils::ComputeCBDependentValue(
                         aPercentBasis, stylePadding->mPadding.GetIStart(aWM)));
     p.IEnd(aWM) =
         std::max(0, nsLayoutUtils::ComputeCBDependentValue(
                         aPercentBasis, stylePadding->mPadding.GetIEnd(aWM)));
 
--- a/layout/generic/nsGridContainerFrame.cpp
+++ b/layout/generic/nsGridContainerFrame.cpp
@@ -3434,17 +3434,17 @@ static nscoord ContentContribution(
     // XXX this will give mostly correct results for now (until bug 1174569).
     nscoord availISize = INFINITE_ISIZE_COORD;
     nscoord availBSize = NS_UNCONSTRAINEDSIZE;
     auto childWM = child->GetWritingMode();
     const bool isOrthogonal = childWM.IsOrthogonalTo(aCBWM);
     // The next two variables are MinSizeClamp values in the child's axes.
     nscoord iMinSizeClamp = NS_MAXSIZE;
     nscoord bMinSizeClamp = NS_MAXSIZE;
-    LogicalSize cbSize(childWM, 0, 0);
+    LogicalSize cbSize(childWM, 0, NS_UNCONSTRAINEDSIZE);
     if (aState.mCols.mCanResolveLineRangeSize) {
       nscoord sz = aState.mCols.ResolveSize(aGridItem.mArea.mCols);
       if (isOrthogonal) {
         availBSize = sz;
         cbSize.BSize(childWM) = sz;
         if (aGridItem.mState[aAxis] & ItemState::eClampMarginBoxMinSize) {
           bMinSizeClamp = sz;
         }
--- a/layout/reftests/css-grid/reftest.list
+++ b/layout/reftests/css-grid/reftest.list
@@ -118,17 +118,17 @@ random-if(/^Windows\x20NT\x206\.1/.test(
 == grid-item-overflow-stretch-002.html grid-item-overflow-stretch-002-ref.html
 == grid-item-overflow-stretch-003.html grid-item-overflow-stretch-003-ref.html
 == grid-item-overflow-stretch-004.html grid-item-overflow-stretch-004-ref.html
 == grid-item-overflow-stretch-005.html grid-item-overflow-stretch-005-ref.html
 == grid-item-overflow-stretch-006.html grid-item-overflow-stretch-006-ref.html
 == grid-item-canvas-001.html grid-item-canvas-001-ref.html
 skip-if(Android) == grid-item-button-001.html grid-item-button-001-ref.html
 == grid-item-table-stretch-001.html grid-item-table-stretch-001-ref.html
-== grid-item-table-stretch-002.html grid-item-table-stretch-002-ref.html
+fails-if(Android) == grid-item-table-stretch-002.html grid-item-table-stretch-002-ref.html # Bug 1527734
 == grid-item-table-stretch-003.html grid-item-table-stretch-003-ref.html
 == grid-item-table-stretch-004.html grid-item-table-stretch-004-ref.html
 == grid-item-fieldset-stretch-001.html grid-item-fieldset-stretch-001-ref.html
 skip-if(Android) == grid-item-video-stretch-001.html grid-item-video-stretch-001-ref.html # Huh, Android doesn't have webm support?
 skip-if(Android) == grid-item-video-stretch-002.html grid-item-video-stretch-002-ref.html # Huh, Android doesn't have webm support?
 == grid-item-input-stretch-001.html grid-item-input-stretch-001-ref.html
 == grid-item-self-baseline-001.html grid-item-self-baseline-001-ref.html
 random-if(http.oscpu!="Linux\u0020i686") == grid-item-content-baseline-001.html grid-item-content-baseline-001-ref.html # depends on exact Ahem baseline font metrics which seems to differ between platforms: bug 1310792
--- a/layout/reftests/forms/input/range/range-percent-intrinsic-size-2b-ref.html
+++ b/layout/reftests/forms/input/range/range-percent-intrinsic-size-2b-ref.html
@@ -70,18 +70,19 @@ input[orient="vertical"] {
 
 <br>
 <br>
 
 <div style="height:30px"><div>
   <input type="range" class="mb n" orient="vertical">
 </div></div>
 
-<div class="grid" style="grid: 30px / auto">
-  <input type="range" class="b" orient="vertical" style="height:15px">
+<div class="grid" style="grid: min-content / auto">
+  <input type="range" class="b" orient="vertical" style="height:50%; grid-area:1/1">
+  <input type="range" class="b" orient="vertical" style="visibility:hidden; grid-area:1/1">
 </div>
 
 <div class="grid" style="grid: 30px / auto">
   <input type="range" class="b" orient="vertical" style="height:15px">
 </div>
 
 <div class="grid" style="grid: 30px / auto">
   <input type="range" class="b" orient="vertical" style="height:15px">
--- a/layout/svg/SVGTextFrame.cpp
+++ b/layout/svg/SVGTextFrame.cpp
@@ -3515,17 +3515,18 @@ void SVGTextFrame::ReflowSVG() {
     mRect.SetEmpty();
   } else {
     mRect = nsLayoutUtils::RoundGfxRectToAppRect(r.ToThebesRect(),
                                                  AppUnitsPerCSSPixel());
 
     // Due to rounding issues when we have a transform applied, we sometimes
     // don't include an additional row of pixels.  For now, just inflate our
     // covered region.
-    mRect.Inflate(ceil(presContext->AppUnitsPerDevPixel() / mLastContextScale));
+    double contextScale = GetContextScale(GetCanvasTM());
+    mRect.Inflate(ceil(presContext->AppUnitsPerDevPixel() / contextScale));
   }
 
   if (mState & NS_FRAME_FIRST_REFLOW) {
     // Make sure we have our filter property (if any) before calling
     // FinishAndStoreOverflow (subsequent filter changes are handled off
     // nsChangeHint_UpdateEffects):
     SVGObserverUtils::UpdateEffects(this);
   }
--- a/netwerk/base/Predictor.cpp
+++ b/netwerk/base/Predictor.cpp
@@ -602,16 +602,18 @@ nsresult Predictor::Create(nsISupports *
   nsresult rv;
 
   if (aOuter != nullptr) {
     return NS_ERROR_NO_AGGREGATION;
   }
 
   RefPtr<Predictor> svc = new Predictor();
   if (IsNeckoChild()) {
+    NeckoChild::InitNeckoChild();
+
     // Child threads only need to be call into the public interface methods
     // so we don't bother with initialization
     return svc->QueryInterface(aIID, aResult);
   }
 
   rv = svc->Init();
   if (NS_FAILED(rv)) {
     PREDICTOR_LOG(("Failed to initialize predictor, predictor will be a noop"));
deleted file mode 100644
--- a/testing/web-platform/meta/css/css-sizing/range-percent-intrinsic-size-2.html.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[range-percent-intrinsic-size-2.html]
-  expected:
-    if (os == "win"): FAIL
-    if (os == "android"): "FAIL"
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-grid/grid-items/grid-item-overflow-auto-max-height-percentage-ref.html
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+  <meta charset="utf-8">
+  <title>Reference: Testcase for bug 1526567</title>
+  <link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
+  <style>
+html,body {
+  color:black; background-color:white; font:16px/1 monospace; padding:0; margin:0;
+}
+
+.webconsole-app {
+  display: grid;
+}
+
+.sidebar {
+  display: flex;
+  flex: 1;
+  flex-direction: row;
+  background: blue;
+}
+
+.controlled {
+  display: flex;
+  overflow: auto;
+}
+
+.sidebar-wrapper {
+  display: grid;
+  grid-template-columns: 100px;
+  grid-template-rows: 50px 1fr;
+  overflow: hidden;
+}
+
+.sidebar-contents {
+  grid-row: 2 / 3;
+  overflow: auto;
+}
+
+  </style>
+</head>
+<body>
+
+<div class="webconsole-app">
+  <div class="sidebar">
+    <div class="controlled">
+      <div class="sidebar-wrapper">
+        <div></div>
+        <div class="sidebar-contents">
+          <div style="height:400px; background: grey"></div>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
+
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-grid/grid-items/grid-item-overflow-auto-max-height-percentage.html
@@ -0,0 +1,69 @@
+<!DOCTYPE HTML>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+  <meta charset="utf-8">
+  <title>Testcase for bug 1526567</title>
+  <link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
+  <link rel="help" href="https://drafts.csswg.org/css-sizing-3/#behave-auto">
+  <link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1526567">
+  <link rel="match" href="grid-item-overflow-auto-max-height-percentage-ref.html">
+  <meta name="assert" content="Check that max-height:100% has no effect on a grid item's max-content contribution, since the percentage basis is indefinite in this case.">
+  <style>
+html,body {
+  color:black; background-color:white; font:16px/1 monospace; padding:0; margin:0;
+}
+
+.webconsole-app {
+  display: grid;
+  height: 200px;
+}
+
+.sidebar {
+  display: flex;
+  flex: 1;
+  flex-direction: row;
+  height: 100%;
+  background: blue;
+}
+
+.controlled {
+  display: flex;
+  overflow: auto;
+}
+
+.sidebar-wrapper {
+  display: grid;
+  grid-template-columns: 100px;
+  grid-template-rows: 50px 1fr;
+  overflow: hidden;
+}
+
+.sidebar-contents {
+  grid-row: 2 / 3;
+  overflow: auto;
+  max-height: 100%;
+}
+
+  </style>
+</head>
+<body>
+
+<div class="webconsole-app">
+  <div class="sidebar">
+    <div class="controlled">
+      <div class="sidebar-wrapper">
+        <div></div>
+        <div class="sidebar-contents">
+          <div style="height:400px; background: grey"></div>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
+
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-grid/grid-items/grid-item-percentage-sizes-001-ref.html
@@ -0,0 +1,312 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Reference: item [min-|max-]*[width|height] percentages</title>
+<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
+<style>
+html,body {
+  color:black; background-color:white; font:16px/1 monospace; padding:0; margin:0;
+}
+
+.grid {
+  display: inline-grid;
+  height: 10px;
+  width: 10px;
+  grid: 1px auto 2px / 3px auto 4px;
+  border: solid 1px;
+  margin: 0 20px 20px 0;
+  place-items: start;
+}
+.item {
+  background: cyan;
+  grid-area: 2/2;
+  height: 30px;
+  width: 30px;
+}
+.content {
+  height: 30px;
+  width: 30px;
+}
+.min {
+  height: 7px;
+  width: 3px;
+}
+.min:not(.max):not(.size) {
+  height: 30px;
+  width: 30px;
+}
+
+.hl  .item { writing-mode: horizontal-tb; direction:ltr; }
+.hr  .item { writing-mode: horizontal-tb; direction:rtl; }
+.vrl .item { writing-mode: vertical-rl; direction:ltr; }
+.vrr .item { writing-mode: vertical-rl; direction:rtl; }
+.vll .item { writing-mode: vertical-lr; direction:ltr; }
+.vlr .item { writing-mode: vertical-lr; direction:rtl; }
+.sll .item { writing-mode: sideways-lr; direction:ltr; }
+.slr .item { writing-mode: sideways-lr; direction:rtl; }
+
+</style>
+<div class="hl">
+<div class="grid">
+  <div class="item max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item max size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max size">
+    <div class="content"></div>
+  </div>
+</div>
+</div>
+
+<div class="vrl">
+<div class="grid">
+  <div class="item max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item max size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max size">
+    <div class="content"></div>
+  </div>
+</div>
+</div>
+
+<div class="vrr">
+<div class="grid">
+  <div class="item max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item max size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max size">
+    <div class="content"></div>
+  </div>
+</div>
+</div>
+
+<div class="vll">
+<div class="grid">
+  <div class="item max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item max size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max size">
+    <div class="content"></div>
+  </div>
+</div>
+</div>
+
+<div class="vlr">
+<div class="grid">
+  <div class="item max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item max size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max size">
+    <div class="content"></div>
+  </div>
+</div>
+</div>
+
+<div class="slr">
+<div class="grid">
+  <div class="item max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item max size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max size">
+    <div class="content"></div>
+  </div>
+</div>
+</div>
+
+<div class="srl">
+<div class="grid">
+  <div class="item max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item max size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max size">
+    <div class="content"></div>
+  </div>
+</div>
+</div>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-grid/grid-items/grid-item-percentage-sizes-001.html
@@ -0,0 +1,318 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: item [min-|max-]*[width|height] percentages</title>
+<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
+<link rel="help" href="http://www.w3.org/TR/css-grid-1">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1526567">
+<link rel="match" href="grid-item-percentage-sizes-001-ref.html">
+<meta name="assert" content="Checks that item [min-|max-]*[width|height] percentages are resolved correctly in span=1 auto tracks.">
+<style>
+html,body {
+  color:black; background-color:white; font:16px/1 monospace; padding:0; margin:0;
+}
+
+.grid {
+  display: inline-grid;
+  height: 10px;
+  width: 10px;
+  grid: 1px auto 2px / 3px auto 4px;
+  border: solid 1px;
+  margin: 0 20px 20px 0;
+  place-items: start;
+}
+.item {
+  background: cyan;
+  grid-area: 2/2;
+}
+.content {
+  height: 30px;
+  width: 30px;
+}
+.min {
+  min-height: 100%;
+  min-width: 100%;
+}
+.max {
+  max-height: 100%;
+  max-width: 100%;
+}
+.size {
+  height: 100%;
+  width: 100%;
+}
+
+.hl  .item { writing-mode: horizontal-tb; direction:ltr; }
+.hr  .item { writing-mode: horizontal-tb; direction:rtl; }
+.vrl .item { writing-mode: vertical-rl; direction:ltr; }
+.vrr .item { writing-mode: vertical-rl; direction:rtl; }
+.vll .item { writing-mode: vertical-lr; direction:ltr; }
+.vlr .item { writing-mode: vertical-lr; direction:rtl; }
+.sll .item { writing-mode: sideways-lr; direction:ltr; }
+.slr .item { writing-mode: sideways-lr; direction:rtl; }
+
+</style>
+<div class="hl">
+<div class="grid">
+  <div class="item max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item max size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max size">
+    <div class="content"></div>
+  </div>
+</div>
+</div>
+
+<div class="vrl">
+<div class="grid">
+  <div class="item max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item max size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max size">
+    <div class="content"></div>
+  </div>
+</div>
+</div>
+
+<div class="vrr">
+<div class="grid">
+  <div class="item max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item max size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max size">
+    <div class="content"></div>
+  </div>
+</div>
+</div>
+
+<div class="vll">
+<div class="grid">
+  <div class="item max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item max size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max size">
+    <div class="content"></div>
+  </div>
+</div>
+</div>
+
+<div class="vlr">
+<div class="grid">
+  <div class="item max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item max size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max size">
+    <div class="content"></div>
+  </div>
+</div>
+</div>
+
+<div class="slr">
+<div class="grid">
+  <div class="item max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item max size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max size">
+    <div class="content"></div>
+  </div>
+</div>
+</div>
+
+<div class="srl">
+<div class="grid">
+  <div class="item max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item max size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max size">
+    <div class="content"></div>
+  </div>
+</div>
+</div>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-grid/grid-items/grid-item-percentage-sizes-002-ref.html
@@ -0,0 +1,312 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Reference: item [min-|max-]*[width|height] percentages</title>
+<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
+<style>
+html,body {
+  color:black; background-color:white; font:16px/1 monospace; padding:0; margin:0;
+}
+
+.grid {
+  display: inline-grid;
+  height: 10px;
+  width: 10px;
+  grid: 1px auto 2px 3px / 3px auto 4px 2px;
+  border: solid 1px;
+  margin: 0 20px 20px 0;
+  place-items: start;
+}
+.item {
+  background: cyan;
+  grid-area: 2/2/span 2/span 2;
+  height: 30px;
+  width: 30px;
+}
+.content {
+  height: 30px;
+  width: 30px;
+}
+.min {
+  height: 6px;
+  width: 5px;
+}
+.min:not(.max):not(.size) {
+  height: 30px;
+  width: 30px;
+}
+
+.hl  .item { writing-mode: horizontal-tb; direction:ltr; }
+.hr  .item { writing-mode: horizontal-tb; direction:rtl; }
+.vrl .item { writing-mode: vertical-rl; direction:ltr; }
+.vrr .item { writing-mode: vertical-rl; direction:rtl; }
+.vll .item { writing-mode: vertical-lr; direction:ltr; }
+.vlr .item { writing-mode: vertical-lr; direction:rtl; }
+.sll .item { writing-mode: sideways-lr; direction:ltr; }
+.slr .item { writing-mode: sideways-lr; direction:rtl; }
+
+</style>
+<div class="hl">
+<div class="grid">
+  <div class="item max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item max size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max size">
+    <div class="content"></div>
+  </div>
+</div>
+</div>
+
+<div class="vrl">
+<div class="grid">
+  <div class="item max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item max size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max size">
+    <div class="content"></div>
+  </div>
+</div>
+</div>
+
+<div class="vrr">
+<div class="grid">
+  <div class="item max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item max size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max size">
+    <div class="content"></div>
+  </div>
+</div>
+</div>
+
+<div class="vll">
+<div class="grid">
+  <div class="item max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item max size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max size">
+    <div class="content"></div>
+  </div>
+</div>
+</div>
+
+<div class="vlr">
+<div class="grid">
+  <div class="item max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item max size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max size">
+    <div class="content"></div>
+  </div>
+</div>
+</div>
+
+<div class="slr">
+<div class="grid">
+  <div class="item max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item max size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max size">
+    <div class="content"></div>
+  </div>
+</div>
+</div>
+
+<div class="srl">
+<div class="grid">
+  <div class="item max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item max size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max size">
+    <div class="content"></div>
+  </div>
+</div>
+</div>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-grid/grid-items/grid-item-percentage-sizes-002.html
@@ -0,0 +1,318 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: item [min-|max-]*[width|height] percentages</title>
+<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
+<link rel="help" href="http://www.w3.org/TR/css-grid-1">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1526567">
+<link rel="match" href="grid-item-percentage-sizes-002-ref.html">
+<meta name="assert" content="Checks that item [min-|max-]*[width|height] percentages are resolved correctly in span=2 auto tracks.">
+<style>
+html,body {
+  color:black; background-color:white; font:16px/1 monospace; padding:0; margin:0;
+}
+
+.grid {
+  display: inline-grid;
+  height: 10px;
+  width: 10px;
+  grid: 1px auto 2px 3px / 3px auto 4px 2px;
+  border: solid 1px;
+  margin: 0 20px 20px 0;
+  place-items: start;
+}
+.item {
+  background: cyan;
+  grid-area: 2/2/span 2/span 2;
+}
+.content {
+  height: 30px;
+  width: 30px;
+}
+.min {
+  min-height: 100%;
+  min-width: 100%;
+}
+.max {
+  max-height: 100%;
+  max-width: 100%;
+}
+.size {
+  height: 100%;
+  width: 100%;
+}
+
+.hl  .item { writing-mode: horizontal-tb; direction:ltr; }
+.hr  .item { writing-mode: horizontal-tb; direction:rtl; }
+.vrl .item { writing-mode: vertical-rl; direction:ltr; }
+.vrr .item { writing-mode: vertical-rl; direction:rtl; }
+.vll .item { writing-mode: vertical-lr; direction:ltr; }
+.vlr .item { writing-mode: vertical-lr; direction:rtl; }
+.sll .item { writing-mode: sideways-lr; direction:ltr; }
+.slr .item { writing-mode: sideways-lr; direction:rtl; }
+
+</style>
+<div class="hl">
+<div class="grid">
+  <div class="item max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item max size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max size">
+    <div class="content"></div>
+  </div>
+</div>
+</div>
+
+<div class="vrl">
+<div class="grid">
+  <div class="item max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item max size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max size">
+    <div class="content"></div>
+  </div>
+</div>
+</div>
+
+<div class="vrr">
+<div class="grid">
+  <div class="item max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item max size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max size">
+    <div class="content"></div>
+  </div>
+</div>
+</div>
+
+<div class="vll">
+<div class="grid">
+  <div class="item max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item max size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max size">
+    <div class="content"></div>
+  </div>
+</div>
+</div>
+
+<div class="vlr">
+<div class="grid">
+  <div class="item max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item max size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max size">
+    <div class="content"></div>
+  </div>
+</div>
+</div>
+
+<div class="slr">
+<div class="grid">
+  <div class="item max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item max size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max size">
+    <div class="content"></div>
+  </div>
+</div>
+</div>
+
+<div class="srl">
+<div class="grid">
+  <div class="item max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item max size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max size">
+    <div class="content"></div>
+  </div>
+</div>
+</div>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-grid/grid-items/grid-item-percentage-sizes-003-ref.html
@@ -0,0 +1,316 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Reference: item [min-|max-]*[width|height] calc() percentages</title>
+<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
+<style>
+html,body {
+  color:black; background-color:white; font:16px/1 monospace; padding:0; margin:0;
+}
+
+.grid {
+  display: inline-grid;
+  height: 10px;
+  width: 10px;
+  grid: 1px auto 2px / 3px auto 4px;
+  border: solid 1px;
+  margin: 0 20px 20px 0;
+  place-items: start;
+}
+.item {
+  background: cyan;
+  grid-area: 2/2;
+  height: 30px;
+  width: 30px;
+}
+.content {
+  height: 30px;
+  width: 30px;
+}
+.min {
+  height: 9px;
+  width: 5px;
+}
+.min:not(.max):not(.size) {
+  height: 30px;
+  width: 30px;
+}
+.size:not(.min) {
+  height: 32px;
+  width: 32px;
+}
+
+.hl  .item { writing-mode: horizontal-tb; direction:ltr; }
+.hr  .item { writing-mode: horizontal-tb; direction:rtl; }
+.vrl .item { writing-mode: vertical-rl; direction:ltr; }
+.vrr .item { writing-mode: vertical-rl; direction:rtl; }
+.vll .item { writing-mode: vertical-lr; direction:ltr; }
+.vlr .item { writing-mode: vertical-lr; direction:rtl; }
+.sll .item { writing-mode: sideways-lr; direction:ltr; }
+.slr .item { writing-mode: sideways-lr; direction:rtl; }
+
+</style>
+<div class="hl">
+<div class="grid">
+  <div class="item max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item max size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max size">
+    <div class="content"></div>
+  </div>
+</div>
+</div>
+
+<div class="vrl">
+<div class="grid">
+  <div class="item max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item max size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max size">
+    <div class="content"></div>
+  </div>
+</div>
+</div>
+
+<div class="vrr">
+<div class="grid">
+  <div class="item max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item max size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max size">
+    <div class="content"></div>
+  </div>
+</div>
+</div>
+
+<div class="vll">
+<div class="grid">
+  <div class="item max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item max size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max size">
+    <div class="content"></div>
+  </div>
+</div>
+</div>
+
+<div class="vlr">
+<div class="grid">
+  <div class="item max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item max size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max size">
+    <div class="content"></div>
+  </div>
+</div>
+</div>
+
+<div class="slr">
+<div class="grid">
+  <div class="item max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item max size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max size">
+    <div class="content"></div>
+  </div>
+</div>
+</div>
+
+<div class="srl">
+<div class="grid">
+  <div class="item max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item max size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max size">
+    <div class="content"></div>
+  </div>
+</div>
+</div>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-grid/grid-items/grid-item-percentage-sizes-003.html
@@ -0,0 +1,318 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: item [min-|max-]*[width|height] calc() percentages</title>
+<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
+<link rel="help" href="http://www.w3.org/TR/css-grid-1">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1526567">
+<link rel="match" href="grid-item-percentage-sizes-003-ref.html">
+<meta name="assert" content="Checks that item [min-|max-]*[width|height] percentages are resolved correctly in span=1 auto tracks.">
+<style>
+html,body {
+  color:black; background-color:white; font:16px/1 monospace; padding:0; margin:0;
+}
+
+.grid {
+  display: inline-grid;
+  height: 10px;
+  width: 10px;
+  grid: 1px auto 2px / 3px auto 4px;
+  border: solid 1px;
+  margin: 0 20px 20px 0;
+  place-items: start;
+}
+.item {
+  background: cyan;
+  grid-area: 2/2;
+}
+.content {
+  height: 30px;
+  width: 30px;
+}
+.min {
+  min-height: calc(2px + 100%);
+  min-width: calc(2px + 100%);
+}
+.max {
+  max-height: calc(2px + 100%);
+  max-width: calc(2px + 100%);
+}
+.size {
+  height: calc(2px + 100%);
+  width: calc(2px + 100%);
+}
+
+.hl  .item { writing-mode: horizontal-tb; direction:ltr; }
+.hr  .item { writing-mode: horizontal-tb; direction:rtl; }
+.vrl .item { writing-mode: vertical-rl; direction:ltr; }
+.vrr .item { writing-mode: vertical-rl; direction:rtl; }
+.vll .item { writing-mode: vertical-lr; direction:ltr; }
+.vlr .item { writing-mode: vertical-lr; direction:rtl; }
+.sll .item { writing-mode: sideways-lr; direction:ltr; }
+.slr .item { writing-mode: sideways-lr; direction:rtl; }
+
+</style>
+<div class="hl">
+<div class="grid">
+  <div class="item max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item max size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max size">
+    <div class="content"></div>
+  </div>
+</div>
+</div>
+
+<div class="vrl">
+<div class="grid">
+  <div class="item max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item max size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max size">
+    <div class="content"></div>
+  </div>
+</div>
+</div>
+
+<div class="vrr">
+<div class="grid">
+  <div class="item max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item max size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max size">
+    <div class="content"></div>
+  </div>
+</div>
+</div>
+
+<div class="vll">
+<div class="grid">
+  <div class="item max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item max size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max size">
+    <div class="content"></div>
+  </div>
+</div>
+</div>
+
+<div class="vlr">
+<div class="grid">
+  <div class="item max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item max size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max size">
+    <div class="content"></div>
+  </div>
+</div>
+</div>
+
+<div class="slr">
+<div class="grid">
+  <div class="item max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item max size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max size">
+    <div class="content"></div>
+  </div>
+</div>
+</div>
+
+<div class="srl">
+<div class="grid">
+  <div class="item max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item max size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min size">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max">
+    <div class="content"></div>
+  </div>
+</div>
+<div class="grid">
+  <div class="item min max size">
+    <div class="content"></div>
+  </div>
+</div>
+</div>
--- a/testing/web-platform/tests/css/css-sizing/range-percent-intrinsic-size-2-ref.html
+++ b/testing/web-platform/tests/css/css-sizing/range-percent-intrinsic-size-2-ref.html
@@ -92,42 +92,46 @@ input[orient="vertical"] {
 
 <br>
 <br>
 
 <div style="height:30px"><div>
   <input type="range" class="mb" orient="vertical">
 </div></div>
 
-<div class="grid" style="grid: 4px / auto">
-  <input type="range" class="mb" orient="vertical" style="height:2px">
+<div class="grid" style="grid: min-content / auto">
+  <input type="range" class="b" orient="vertical" style="height:50%; grid-area:1/1">
+  <input type="range" class="b" orient="vertical" style="visibility:hidden; grid-area:1/1">
 </div>
 
-<div class="grid" style="grid: 30px / auto">
-  <input type="range" class="b" orient="vertical" style="height:15px">
+<div class="grid" style="grid: min-content / auto">
+  <input type="range" class="b" orient="vertical" style="height:50%; grid-area:1/1">
+  <input type="range" class="b" orient="vertical" style="visibility:hidden; grid-area:1/1">
 </div>
 
 <div class="grid" style="grid: 30px / auto">
   <input type="range" class="b" orient="vertical" style="height:15px">
 </div>
 
 <div class="grid" style="grid: 30px / auto">
   <input type="range" class="b" orient="vertical" style="height:15px">
 </div>
 
 <div style="height:30px"><div>
   <input type="range" class="mb n" orient="vertical">
 </div></div>
 
-<div class="grid" style="grid: 4px / auto">
-  <input type="range" class="mb n" orient="vertical" style="height:2px">
+<div class="grid" style="grid: min-content / auto">
+  <input type="range" class="b" orient="vertical" style="height:50%; grid-area:1/1">
+  <input type="range" class="b" orient="vertical" style="visibility:hidden; grid-area:1/1">
 </div>
 
-<div class="grid" style="grid: 30px / auto">
-  <input type="range" class="b" orient="vertical" style="height:15px">
+<div class="grid" style="grid: min-content / auto">
+  <input type="range" class="b" orient="vertical" style="height:50%; grid-area:1/1">
+  <input type="range" class="b" orient="vertical" style="visibility:hidden; grid-area:1/1">
 </div>
 
 <div class="grid" style="grid: 30px / auto">
   <input type="range" class="b" orient="vertical" style="height:15px">
 </div>
 
 <div class="grid" style="grid: 30px / auto">
   <input type="range" class="b" orient="vertical" style="height:15px">
--- a/testing/web-platform/tests/css/css-sizing/range-percent-intrinsic-size-2a-ref.html
+++ b/testing/web-platform/tests/css/css-sizing/range-percent-intrinsic-size-2a-ref.html
@@ -66,18 +66,19 @@ input[orient="vertical"] {
 
 <br>
 <br>
 
 <div style="height:30px"><div>
   <input type="range" class="mb" orient="vertical">
 </div></div>
 
-<div class="grid" style="grid: 30px / auto">
-  <input type="range" class="b" orient="vertical" style="height:15px">
+<div class="grid" style="grid: min-content / auto">
+  <input type="range" class="b" orient="vertical" style="height:50%; grid-area:1/1">
+  <input type="range" class="b" orient="vertical" style="visibility:hidden; grid-area:1/1">
 </div>
 
 <div class="grid" style="grid: 30px / auto">
   <input type="range" class="b" orient="vertical" style="height:15px">
 </div>
 
 <div class="grid" style="grid: 30px / auto">
   <input type="range" class="b" orient="vertical" style="height:15px">
--- a/toolkit/recordreplay/ipc/ParentGraphics.cpp
+++ b/toolkit/recordreplay/ipc/ParentGraphics.cpp
@@ -162,28 +162,28 @@ void UpdateGraphicsInUIProcess(const Pai
   }
 
   AutoSafeJSContext cx;
   JSAutoRealm ar(cx, xpc::PrivilegedJunkScope());
 
   // Create an ArrayBuffer whose contents are the externally-provided |memory|.
   JS::RootedObject bufferObject(cx);
   bufferObject =
-      JS_NewArrayBufferWithExternalContents(cx, width * height * 4, memory);
+      JS_NewArrayBufferWithUserOwnedContents(cx, width * height * 4, memory);
   MOZ_RELEASE_ASSERT(bufferObject);
 
   JS::RootedValue buffer(cx, ObjectValue(*bufferObject));
 
   // Call into the graphics module to update the canvas it manages.
   if (NS_FAILED(gGraphics->UpdateCanvas(buffer, width, height, hadFailure))) {
     MOZ_CRASH("UpdateGraphicsInUIProcess");
   }
 
   // Manually detach this ArrayBuffer once this update completes, as the
-  // JS_NewArrayBufferWithExternalContents API mandates.  (The API also
+  // JS_NewArrayBufferWithUserOwnedContents API mandates.  (The API also
   // guarantees that this call always succeeds.)
   MOZ_ALWAYS_TRUE(JS_DetachArrayBuffer(cx, bufferObject));
 }
 
 static void MaybeTriggerExplicitPaint() {
   if (gLastExplicitPaint &&
       gLastExplicitPaint->mCheckpointId == gLastCheckpoint) {
     UpdateGraphicsInUIProcess(gLastExplicitPaint.get());