Merge mozilla-central to autoland a=merge on a CLOSED TREE
authorCoroiu Cristina <ccoroiu@mozilla.com>
Fri, 22 Feb 2019 18:39:27 +0200
changeset 518487 d9d6968b7c2f682ee72cd0b4df5c8ada53a788f8
parent 518486 176ff7c8871e7df2977659d1f7c9107241bb6318 (current diff)
parent 518442 826b59e57fe4274954088e7a9ed9bab092203e1c (diff)
child 518488 f79d064a028c5f7898da39a72e5573ec7c1b2fd2
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
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to autoland a=merge on a CLOSED TREE
devtools/client/preferences/devtools-client.js
js/src/jsapi-tests/testExternalArrayBuffer.cpp
js/src/jsfriendapi.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/devtools/server/actors/addon/webextension-inspected-window.js
+++ b/devtools/server/actors/addon/webextension-inspected-window.js
@@ -133,17 +133,17 @@ CustomizedReload.prototype = {
         if (this.ignoreCache) {
           reloadFlags |= Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE;
         }
 
         try {
           if (this.injectedScript) {
             // Listen to the newly created document elements only if there is an
             // injectedScript to evaluate.
-            Services.obs.addObserver(this, "initial-document-element-inserted");
+            Services.obs.addObserver(this, "document-element-inserted");
           }
 
           // Watch the loading progress and clear the current CustomizedReload once the
           // page has been reloaded (or if its reloading has been interrupted).
           this.docShell.addProgressListener(this,
                                             Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT);
 
           this.webNavigation.reload(reloadFlags);
@@ -154,17 +154,17 @@ CustomizedReload.prototype = {
         }
       });
     }
 
     return this.waitForReloadCompleted;
   },
 
   observe(subject, topic, data) {
-    if (topic !== "initial-document-element-inserted") {
+    if (topic !== "document-element-inserted") {
       return;
     }
 
     const document = subject;
     const window = document && document.defaultView;
 
     // Filter out non interesting documents.
     if (!document || !document.location || !window) {
@@ -227,17 +227,17 @@ CustomizedReload.prototype = {
   stop(error) {
     if (this.stopped) {
       return;
     }
 
     this.docShell.removeProgressListener(this);
 
     if (this.injectedScript) {
-      Services.obs.removeObserver(this, "initial-document-element-inserted");
+      Services.obs.removeObserver(this, "document-element-inserted");
     }
 
     // Reset the customized user agent.
     if (this.userAgent && this.docShell.customUserAgent == this.userAgent) {
       this.docShell.customUserAgent = null;
     }
 
     if (error) {
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -9362,17 +9362,16 @@ class UnblockParsingPromiseHandler final
       : mPromise(aPromise) {
     nsCOMPtr<nsIParser> parser = aDocument->CreatorParserOrNull();
     if (parser &&
         (aOptions.mBlockScriptCreated || !parser->IsScriptCreated())) {
       parser->BlockParser();
       mParser = do_GetWeakReference(parser);
       mDocument = aDocument;
       mDocument->BlockOnload();
-      mDocument->BlockDOMContentLoaded();
     }
   }
 
   void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override {
     MaybeUnblockParser();
 
     mPromise->MaybeResolve(aCx, aValue);
   }
@@ -9396,26 +9395,18 @@ class UnblockParsingPromiseHandler final
   void MaybeUnblockParser() {
     nsCOMPtr<nsIParser> parser = do_QueryReferent(mParser);
     if (parser) {
       MOZ_DIAGNOSTIC_ASSERT(mDocument);
       nsCOMPtr<nsIParser> docParser = mDocument->CreatorParserOrNull();
       if (parser == docParser) {
         parser->UnblockParser();
         parser->ContinueInterruptedParsingAsync();
-      }
-    }
-    if (mDocument) {
-      // We blocked DOMContentLoaded and load events on this document.  Unblock
-      // them.  Note that we want to do that no matter what's going on with the
-      // parser state for this document.  Maybe someone caused it to stop being
-      // parsed, so CreatorParserOrNull() is returning null, but we still want
-      // to unblock these.
-      mDocument->UnblockDOMContentLoaded();
-      mDocument->UnblockOnload(false);
+        mDocument->UnblockOnload(false);
+      }
     }
     mParser = nullptr;
     mDocument = nullptr;
   }
 
   nsWeakPtr mParser;
   RefPtr<Promise> mPromise;
   RefPtr<Document> mDocument;
--- a/dom/base/nsContentSink.cpp
+++ b/dom/base/nsContentSink.cpp
@@ -24,17 +24,16 @@
 #include "nsIProtocolHandler.h"
 #include "nsIHttpChannel.h"
 #include "nsIContent.h"
 #include "nsIPresShell.h"
 #include "nsPresContext.h"
 #include "nsViewManager.h"
 #include "nsAtom.h"
 #include "nsGkAtoms.h"
-#include "nsGlobalWindowInner.h"
 #include "nsNetCID.h"
 #include "nsIOfflineCacheUpdate.h"
 #include "nsIApplicationCache.h"
 #include "nsIApplicationCacheContainer.h"
 #include "nsIApplicationCacheChannel.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsICookieService.h"
 #include "nsContentUtils.h"
@@ -1549,30 +1548,20 @@ void nsContentSink::WillBuildModelImpl()
 }
 
 /* static */
 void nsContentSink::NotifyDocElementCreated(Document* aDoc) {
   MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
 
   nsCOMPtr<nsIObserverService> observerService =
       mozilla::services::GetObserverService();
-  MOZ_ASSERT(observerService);
-
-  auto* win = nsGlobalWindowInner::Cast(aDoc->GetInnerWindow());
-  bool fireInitialInsertion = !win || !win->DidFireDocElemInserted();
-  if (win) {
-    win->SetDidFireDocElemInserted();
+  if (observerService) {
+    observerService->NotifyObservers(
+        ToSupports(aDoc), "document-element-inserted", EmptyString().get());
   }
-  if (fireInitialInsertion) {
-    observerService->NotifyObservers(ToSupports(aDoc),
-                                     "initial-document-element-inserted",
-                                     EmptyString().get());
-  }
-  observerService->NotifyObservers(
-      ToSupports(aDoc), "document-element-inserted", EmptyString().get());
 
   nsContentUtils::DispatchChromeEvent(
       aDoc, ToSupports(aDoc), NS_LITERAL_STRING("DOMDocElementInserted"),
       CanBubble::eYes, Cancelable::eNo);
 }
 
 NS_IMETHODIMP
 nsContentSink::GetName(nsACString& aName) {
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -851,17 +851,16 @@ nsGlobalWindowInner::nsGlobalWindowInner
       mNotifyIdleObserversIdleOnThaw(false),
       mNotifyIdleObserversActiveOnThaw(false),
       mIsChrome(false),
       mCleanMessageManager(false),
       mNeedsFocus(true),
       mHasFocus(false),
       mShowFocusRingForContent(false),
       mFocusByKeyOccurred(false),
-      mDidFireDocElemInserted(false),
       mHasGamepad(false),
       mHasVREvents(false),
       mHasVRDisplayActivateEvents(false),
       mHasSeenGamepadInput(false),
       mSuspendDepth(0),
       mFreezeDepth(0),
 #ifdef DEBUG
       mSerial(0),
--- a/dom/base/nsGlobalWindowInner.h
+++ b/dom/base/nsGlobalWindowInner.h
@@ -858,19 +858,16 @@ class nsGlobalWindowInner final : public
   void SetFullScreen(bool aFullscreen, mozilla::ErrorResult& aError);
   bool Find(const nsAString& aString, bool aCaseSensitive, bool aBackwards,
             bool aWrapAround, bool aWholeWord, bool aSearchInFrames,
             bool aShowDialog, mozilla::ErrorResult& aError);
   uint64_t GetMozPaintCount(mozilla::ErrorResult& aError);
 
   bool ShouldResistFingerprinting();
 
-  bool DidFireDocElemInserted() const { return mDidFireDocElemInserted; }
-  void SetDidFireDocElemInserted() { mDidFireDocElemInserted = true; }
-
   mozilla::dom::Nullable<mozilla::dom::WindowProxyHolder> OpenDialog(
       JSContext* aCx, const nsAString& aUrl, const nsAString& aName,
       const nsAString& aOptions,
       const mozilla::dom::Sequence<JS::Value>& aExtraArgument,
       mozilla::ErrorResult& aError);
   void UpdateCommands(const nsAString& anAction, mozilla::dom::Selection* aSel,
                       int16_t aReason);
 
@@ -1315,20 +1312,16 @@ class nsGlobalWindowInner final : public
   // when true, show focus rings for the current focused content only.
   // This will be reset when another element is focused
   bool mShowFocusRingForContent : 1;
 
   // true if tab navigation has occurred for this window. Focus rings
   // should be displayed.
   bool mFocusByKeyOccurred : 1;
 
-  // True if we have notified document-element-inserted observers for this
-  // document.
-  bool mDidFireDocElemInserted : 1;
-
   // Indicates whether this window wants gamepad input events
   bool mHasGamepad : 1;
 
   // Indicates whether this window wants VR events
   bool mHasVREvents : 1;
 
   // Indicates whether this window wants VRDisplayActivate events
   bool mHasVRDisplayActivateEvents : 1;
--- a/dom/html/nsHTMLContentSink.cpp
+++ b/dom/html/nsHTMLContentSink.cpp
@@ -36,17 +36,16 @@
 
 #include "nsGenericHTMLElement.h"
 
 #include "nsIScriptElement.h"
 
 #include "nsIComponentManager.h"
 #include "nsIServiceManager.h"
 
-#include "nsDocElementCreatedNotificationRunner.h"
 #include "nsGkAtoms.h"
 #include "nsContentUtils.h"
 #include "nsIChannel.h"
 #include "nsIHttpChannel.h"
 #include "nsIDocShell.h"
 #include "mozilla/dom/Document.h"
 #include "nsStubDocumentObserver.h"
 #include "nsIHTMLDocument.h"
@@ -901,19 +900,16 @@ void HTMLContentSink::NotifyRootInsertio
   // tag.
   mNotifiedRootInsertion = true;
   NotifyInsert(nullptr, mRoot);
 
   // Now update the notification information in all our
   // contexts, since we just inserted the root and notified on
   // our whole tree
   UpdateChildCounts();
-
-  nsContentUtils::AddScriptRunner(
-      new nsDocElementCreatedNotificationRunner(mDocument));
 }
 
 void HTMLContentSink::UpdateChildCounts() {
   uint32_t numContexts = mContextStack.Length();
   for (uint32_t i = 0; i < numContexts; i++) {
     SinkContext* sc = mContextStack.ElementAt(i);
 
     sc->UpdateChildCounts();
--- 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
@@ -595,17 +595,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));
@@ -1793,29 +1793,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);
 
 /**
@@ -1848,17 +1848,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/components/extensions/ExtensionPageChild.jsm
+++ b/toolkit/components/extensions/ExtensionPageChild.jsm
@@ -368,17 +368,17 @@ ExtensionPageChild = {
       let context = this.extensionContexts.get(windowId);
 
       global.sendAsyncMessage("Extension:ExtensionViewLoaded",
                               {childId: context && context.childManager.id});
     });
   },
 
   /**
-   * Create a privileged context at initial-document-element-inserted.
+   * Create a privileged context at document-element-inserted.
    *
    * @param {BrowserExtensionContent} extension
    *     The extension for which the context should be created.
    * @param {nsIDOMWindow} contentWindow The global of the page.
    */
   initExtensionContext(extension, contentWindow) {
     this._init();
 
--- a/toolkit/components/extensions/ExtensionPolicyService.cpp
+++ b/toolkit/components/extensions/ExtensionPolicyService.cpp
@@ -50,18 +50,16 @@ using dom::Promise;
   "'unsafe-eval' 'unsafe-inline'; "                               \
   "object-src 'self' https://* moz-extension: blob: filesystem:;"
 
 #define DEFAULT_DEFAULT_CSP "script-src 'self'; object-src 'self';"
 
 #define OBS_TOPIC_PRELOAD_SCRIPT "web-extension-preload-content-script"
 #define OBS_TOPIC_LOAD_SCRIPT "web-extension-load-content-script"
 
-static const char kDocElementInserted[] = "initial-document-element-inserted";
-
 static mozIExtensionProcessScript& ProcessScript() {
   static nsCOMPtr<mozIExtensionProcessScript> sProcessScript;
 
   if (MOZ_UNLIKELY(!sProcessScript)) {
     nsCOMPtr<mozIExtensionProcessScriptJSM> jsm =
         do_ImportModule("resource://gre/modules/ExtensionProcessScript.jsm");
     MOZ_RELEASE_ASSERT(jsm);
 
@@ -232,35 +230,42 @@ ExtensionPolicyService::CollectReports(n
   return NS_OK;
 }
 
 /*****************************************************************************
  * Content script management
  *****************************************************************************/
 
 void ExtensionPolicyService::RegisterObservers() {
-  mObs->AddObserver(this, kDocElementInserted, false);
+  mObs->AddObserver(this, "content-document-global-created", false);
+  mObs->AddObserver(this, "document-element-inserted", false);
   mObs->AddObserver(this, "tab-content-frameloader-created", false);
   if (XRE_IsContentProcess()) {
     mObs->AddObserver(this, "http-on-opening-request", false);
   }
 }
 
 void ExtensionPolicyService::UnregisterObservers() {
-  mObs->RemoveObserver(this, kDocElementInserted);
+  mObs->RemoveObserver(this, "content-document-global-created");
+  mObs->RemoveObserver(this, "document-element-inserted");
   mObs->RemoveObserver(this, "tab-content-frameloader-created");
   if (XRE_IsContentProcess()) {
     mObs->RemoveObserver(this, "http-on-opening-request");
   }
 }
 
 nsresult ExtensionPolicyService::Observe(nsISupports* aSubject,
                                          const char* aTopic,
                                          const char16_t* aData) {
-  if (!strcmp(aTopic, kDocElementInserted)) {
+  if (!strcmp(aTopic, "content-document-global-created")) {
+    nsCOMPtr<nsPIDOMWindowOuter> win = do_QueryInterface(aSubject);
+    if (win) {
+      CheckWindow(win);
+    }
+  } else if (!strcmp(aTopic, "document-element-inserted")) {
     nsCOMPtr<Document> doc = do_QueryInterface(aSubject);
     if (doc) {
       CheckDocument(doc);
     }
   } else if (!strcmp(aTopic, "http-on-opening-request")) {
     nsCOMPtr<nsIChannel> chan = do_QueryInterface(aSubject);
     if (chan) {
       CheckRequest(chan);
@@ -464,16 +469,44 @@ void ExtensionPolicyService::CheckDocume
     if (policy) {
       bool privileged = IsExtensionProcess() && CheckParentFrames(win, *policy);
 
       ProcessScript().InitExtensionDocument(policy, aDocument, privileged);
     }
   }
 }
 
+// Checks for loads of about:blank into new window globals, and loads any
+// matching content scripts. about:blank loads do not trigger document element
+// inserted events, so they're the only load type that are special cased this
+// way.
+void ExtensionPolicyService::CheckWindow(nsPIDOMWindowOuter* aWindow) {
+  // We only care about non-initial document loads here. The initial
+  // about:blank document will usually be re-used to load another document.
+  RefPtr<Document> doc = aWindow->GetExtantDoc();
+  if (!doc || doc->IsInitialDocument() ||
+      doc->GetReadyStateEnum() == Document::READYSTATE_UNINITIALIZED) {
+    return;
+  }
+
+  nsCOMPtr<nsIURI> docUri = doc->GetDocumentURI();
+  nsCOMPtr<nsIURI> uri;
+  if (!docUri || NS_FAILED(NS_GetURIWithoutRef(docUri, getter_AddRefs(uri))) ||
+      !NS_IsAboutBlank(uri)) {
+    return;
+  }
+
+  nsIDocShell* docShell = aWindow->GetDocShell();
+  if (RefPtr<ContentFrameMessageManager> mm = docShell->GetMessageManager()) {
+    if (mMessageManagers.Contains(mm)) {
+      CheckContentScripts(aWindow, false);
+    }
+  }
+}
+
 void ExtensionPolicyService::CheckContentScripts(const DocInfo& aDocInfo,
                                                  bool aIsPreload) {
   nsCOMPtr<nsPIDOMWindowInner> win;
   if (!aIsPreload) {
     win = aDocInfo.GetWindow()->GetCurrentInnerWindow();
   }
 
   for (auto iter = mExtensions.Iter(); !iter.Done(); iter.Next()) {
--- a/toolkit/components/extensions/ExtensionPolicyService.h
+++ b/toolkit/components/extensions/ExtensionPolicyService.h
@@ -98,16 +98,17 @@ class ExtensionPolicyService final : pub
  private:
   ExtensionPolicyService();
 
   void RegisterObservers();
   void UnregisterObservers();
 
   void CheckRequest(nsIChannel* aChannel);
   void CheckDocument(dom::Document* aDocument);
+  void CheckWindow(nsPIDOMWindowOuter* aWindow);
 
   void CheckContentScripts(const DocInfo& aDocInfo, bool aIsPreload);
 
   already_AddRefed<dom::Promise> ExecuteContentScript(
       nsPIDOMWindowInner* aWindow,
       extensions::WebExtensionContentScript& aScript);
 
   RefPtr<dom::Promise> ExecuteContentScripts(
--- 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());