Merge inbound to central, a=merge
authorWes Kocher <wkocher@mozilla.com>
Thu, 16 Mar 2017 13:47:50 -0700
changeset 348046 39607304b774591fa6e32c4b06158d869483c312
parent 348045 cf7510940a4e71f3cbe6e343db6b7f5dba5e1c40 (current diff)
parent 347974 0c50d37c0cf4b9943a36d278ed712b25a15ae8e9 (diff)
child 348047 32d95add349fbf758e49cd3ad1e30f4065170288
child 348134 eb818ab6e5797c51bd7efabaa378ba54ae596529
push id88135
push userkwierso@gmail.com
push dateThu, 16 Mar 2017 21:07:17 +0000
treeherdermozilla-inbound@32d95add349f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone55.0a1
first release with
nightly mac
39607304b774 / 55.0a1 / 20170317030204 / files
nightly win32
39607304b774 / 55.0a1 / 20170317030204 / files
nightly win64
39607304b774 / 55.0a1 / 20170317030204 / files
nightly linux32
nightly linux64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly mac
nightly win32
nightly win64
Merge inbound to central, a=merge MozReview-Commit-ID: IAdEn2C01ye
devtools/client/aboutdebugging/aboutdebugging.css
devtools/client/aboutdebugging/components/workers/moz.build
devtools/client/aboutdebugging/components/workers/panel.js
devtools/client/aboutdebugging/components/workers/service-worker-target.js
devtools/client/aboutdebugging/test/browser.ini
devtools/client/aboutdebugging/test/browser_service_workers.js
devtools/client/aboutdebugging/test/browser_service_workers_fetch_flag.js
devtools/client/aboutdebugging/test/browser_service_workers_push.js
devtools/client/aboutdebugging/test/browser_service_workers_push_service.js
devtools/client/aboutdebugging/test/browser_service_workers_start.js
devtools/client/aboutdebugging/test/browser_service_workers_status.js
devtools/client/aboutdebugging/test/browser_service_workers_timeout.js
devtools/client/aboutdebugging/test/browser_service_workers_unregister.js
devtools/client/aboutdebugging/test/head.js
devtools/client/locales/en-US/aboutdebugging.properties
--- a/devtools/client/aboutdebugging/aboutdebugging.css
+++ b/devtools/client/aboutdebugging/aboutdebugging.css
@@ -146,40 +146,58 @@ button {
   text-overflow: ellipsis;
 }
 
 .addons-controls {
   display: flex;
   flex-direction: row;
 }
 
-.addons-install-error {
-  background-color: #f3b0b0;
+.addons-install-error,
+.service-worker-multi-process {
   padding: 5px 10px;
   margin-top: 5px;
   margin-inline-end: 4px;
 }
 
-.service-worker-disabled .warning,
-.addons-install-error .warning {
+.addons-install-error {
+  background-color: #f3b0b0;
+}
+
+.service-worker-multi-process {
+  background-color: #ffeebb;
+  line-height: 1.5em;
+}
+
+.service-worker-multi-process .update-button {
+  margin: 5px 0;
+}
+
+.warning {
   background-image: url(chrome://devtools/skin/images/alerticon-warning.png);
   background-size: 13px 12px;
-  margin-inline-end: 10px;
   display: inline-block;
   width: 13px;
   height: 12px;
+  margin-inline-end: 10px;
 }
 
 @media (min-resolution: 1.1dppx) {
-  .service-worker-disabled .warning,
-  .addons-install-error .warning {
+  .warning {
     background-image: url(chrome://devtools/skin/images/alerticon-warning@2x.png);
   }
 }
 
+.addons-install-error .warning,
+.service-worker-multi-process .warning {
+  /* The warning icon can be hard to see on red / yellow backgrounds, this turns the icon
+  to a black icon. */
+  filter: brightness(0%);
+}
+
 .addons-options {
   flex: 1;
 }
 
 .addons-debugging-label {
   display: inline-block;
   margin-inline-end: 1ch;
 }
--- a/devtools/client/aboutdebugging/components/workers/moz.build
+++ b/devtools/client/aboutdebugging/components/workers/moz.build
@@ -1,9 +1,10 @@
 # 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/.
 
 DevToolsModules(
+    'multi-e10s-warning.js',
     'panel.js',
     'service-worker-target.js',
     'target.js',
 )
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging/components/workers/multi-e10s-warning.js
@@ -0,0 +1,60 @@
+/* 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/. */
+
+/* eslint-env browser */
+
+"use strict";
+
+loader.lazyImporter(this, "PrivateBrowsingUtils",
+  "resource://gre/modules/PrivateBrowsingUtils.jsm");
+const { createClass, DOM: dom } =
+  require("devtools/client/shared/vendor/react");
+const Services = require("Services");
+const { Ci } = require("chrome");
+
+loader.lazyImporter(this, "PrivateBrowsingUtils",
+  "resource://gre/modules/PrivateBrowsingUtils.jsm");
+
+loader.lazyRequireGetter(this, "DebuggerClient",
+  "devtools/shared/client/main", true);
+
+const Strings = Services.strings.createBundle("chrome://devtools/locale/aboutdebugging.properties");
+const PROCESS_COUNT_PREF = "dom.ipc.processCount";
+
+module.exports = createClass({
+  displayName: "multiE10SWarning",
+
+  onUpdatePreferenceClick() {
+    let message = Strings.GetStringFromName("multiProcessWarningConfirmUpdate");
+    if (window.confirm(message)) {
+      Services.prefs.setIntPref(PROCESS_COUNT_PREF, 1);
+      // Restart the browser.
+      Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart);
+    }
+  },
+
+  render() {
+    return dom.div(
+      {
+        className: "service-worker-multi-process"
+      },
+      dom.div(
+        {},
+        dom.div({ className: "warning" }),
+        dom.b({}, Strings.GetStringFromName("multiProcessWarningTitle"))
+      ),
+      dom.div(
+        {},
+        Strings.GetStringFromName("multiProcessWarningMessage")
+      ),
+      dom.button(
+        {
+          className: "update-button",
+          onClick: this.onUpdatePreferenceClick,
+        },
+        Strings.GetStringFromName("multiProcessWarningUpdateLink")
+      )
+    );
+  },
+});
--- a/devtools/client/aboutdebugging/components/workers/panel.js
+++ b/devtools/client/aboutdebugging/components/workers/panel.js
@@ -11,67 +11,80 @@ const { Ci } = require("chrome");
 const { createClass, createFactory, DOM: dom, PropTypes } =
   require("devtools/client/shared/vendor/react");
 const { getWorkerForms } = require("../../modules/worker");
 const Services = require("Services");
 
 const PanelHeader = createFactory(require("../panel-header"));
 const TargetList = createFactory(require("../target-list"));
 const WorkerTarget = createFactory(require("./target"));
+const MultiE10SWarning = createFactory(require("./multi-e10s-warning"));
 const ServiceWorkerTarget = createFactory(require("./service-worker-target"));
 
 loader.lazyImporter(this, "PrivateBrowsingUtils",
   "resource://gre/modules/PrivateBrowsingUtils.jsm");
 
 loader.lazyRequireGetter(this, "DebuggerClient",
   "devtools/shared/client/main", true);
 
 const Strings = Services.strings.createBundle(
   "chrome://devtools/locale/aboutdebugging.properties");
 
 const WorkerIcon = "chrome://devtools/skin/images/debugging-workers.svg";
 const MORE_INFO_URL = "https://developer.mozilla.org/en-US/docs/Tools/about%3Adebugging";
+const PROCESS_COUNT_PREF = "dom.ipc.processCount";
 
 module.exports = createClass({
   displayName: "WorkersPanel",
 
   propTypes: {
     client: PropTypes.instanceOf(DebuggerClient).isRequired,
     id: PropTypes.string.isRequired
   },
 
   getInitialState() {
     return {
       workers: {
         service: [],
         shared: [],
         other: []
-      }
+      },
+      processCount: 1,
     };
   },
 
   componentDidMount() {
     let client = this.props.client;
-    client.addListener("workerListChanged", this.update);
-    client.addListener("serviceWorkerRegistrationListChanged", this.update);
-    client.addListener("processListChanged", this.update);
-    client.addListener("registration-changed", this.update);
+    client.addListener("workerListChanged", this.updateWorkers);
+    client.addListener("serviceWorkerRegistrationListChanged", this.updateWorkers);
+    client.addListener("processListChanged", this.updateWorkers);
+    client.addListener("registration-changed", this.updateWorkers);
 
-    this.update();
+    Services.prefs.addObserver(PROCESS_COUNT_PREF, this.updateMultiE10S, false);
+
+    this.updateMultiE10S();
+    this.updateWorkers();
   },
 
   componentWillUnmount() {
     let client = this.props.client;
-    client.removeListener("processListChanged", this.update);
-    client.removeListener("serviceWorkerRegistrationListChanged", this.update);
-    client.removeListener("workerListChanged", this.update);
-    client.removeListener("registration-changed", this.update);
+    client.removeListener("processListChanged", this.updateWorkers);
+    client.removeListener("serviceWorkerRegistrationListChanged", this.updateWorkers);
+    client.removeListener("workerListChanged", this.updateWorkers);
+    client.removeListener("registration-changed", this.updateWorkers);
+
+    Services.prefs.removeObserver(PROCESS_COUNT_PREF, this.updateMultiE10S);
   },
 
-  update() {
+  updateMultiE10S() {
+    let processCount = Services.prefs.getIntPref(PROCESS_COUNT_PREF);
+    this.setState({ processCount });
+  },
+
+  updateWorkers() {
     let workers = this.getInitialState().workers;
 
     getWorkerForms(this.props.client).then(forms => {
       forms.registrations.forEach(form => {
         workers.service.push({
           icon: WorkerIcon,
           name: form.url,
           url: form.url,
@@ -131,66 +144,94 @@ module.exports = createClass({
     for (let registration of registrations) {
       if (registration.scope === form.scope) {
         return registration;
       }
     }
     return null;
   },
 
-  render() {
-    let { client, id } = this.props;
-    let { workers } = this.state;
+  isE10S() {
+    return Services.appinfo.browserTabsRemoteAutostart;
+  },
 
+  renderServiceWorkersError() {
     let isWindowPrivate = PrivateBrowsingUtils.isContentWindowPrivate(window);
     let isPrivateBrowsingMode = PrivateBrowsingUtils.permanentPrivateBrowsing;
     let isServiceWorkerDisabled = !Services.prefs
                                     .getBoolPref("dom.serviceWorkers.enabled");
-    let errorMsg = isWindowPrivate || isPrivateBrowsingMode ||
-           isServiceWorkerDisabled ?
-      dom.p({ className: "service-worker-disabled" },
-        dom.div({ className: "warning" }),
-        Strings.GetStringFromName("configurationIsNotCompatible"),
-        " (",
-        dom.a({ href: MORE_INFO_URL, target: "_blank" },
-          Strings.GetStringFromName("moreInfo")),
-        ")"
-      ) : "";
+
+    let isDisabled = isWindowPrivate || isPrivateBrowsingMode || isServiceWorkerDisabled;
+    if (!isDisabled) {
+      return "";
+    }
+    return dom.p(
+      {
+        className: "service-worker-disabled"
+      },
+      dom.div({ className: "warning" }),
+      Strings.GetStringFromName("configurationIsNotCompatible"),
+      " (",
+      dom.a(
+        {
+          href: MORE_INFO_URL,
+          target: "_blank"
+        },
+        Strings.GetStringFromName("moreInfo")
+      ),
+      ")"
+    );
+  },
+
+  render() {
+    let { client, id } = this.props;
+    let { workers, processCount } = this.state;
+
+    let isE10S = Services.appinfo.browserTabsRemoteAutostart;
+    let isMultiE10S = isE10S && processCount > 1;
 
-    return dom.div({
-      id: id + "-panel",
-      className: "panel",
-      role: "tabpanel",
-      "aria-labelledby": id + "-header"
-    },
-    PanelHeader({
-      id: id + "-header",
-      name: Strings.GetStringFromName("workers")
-    }),
-    dom.div({ id: "workers", className: "inverted-icons" },
-      TargetList({
-        client,
-        error: errorMsg,
-        id: "service-workers",
-        name: Strings.GetStringFromName("serviceWorkers"),
-        sort: true,
-        targetClass: ServiceWorkerTarget,
-        targets: workers.service
+    return dom.div(
+      {
+        id: id + "-panel",
+        className: "panel",
+        role: "tabpanel",
+        "aria-labelledby": id + "-header"
+      },
+      PanelHeader({
+        id: id + "-header",
+        name: Strings.GetStringFromName("workers")
       }),
-      TargetList({
-        client,
-        id: "shared-workers",
-        name: Strings.GetStringFromName("sharedWorkers"),
-        sort: true,
-        targetClass: WorkerTarget,
-        targets: workers.shared
-      }),
-      TargetList({
-        client,
-        id: "other-workers",
-        name: Strings.GetStringFromName("otherWorkers"),
-        sort: true,
-        targetClass: WorkerTarget,
-        targets: workers.other
-      })
-    ));
+      isMultiE10S ? MultiE10SWarning() : "",
+      dom.div(
+        {
+          id: "workers",
+          className: "inverted-icons"
+        },
+        TargetList({
+          client,
+          debugDisabled: isMultiE10S,
+          error: this.renderServiceWorkersError(),
+          id: "service-workers",
+          name: Strings.GetStringFromName("serviceWorkers"),
+          sort: true,
+          targetClass: ServiceWorkerTarget,
+          targets: workers.service
+        }),
+        TargetList({
+          client,
+          id: "shared-workers",
+          name: Strings.GetStringFromName("sharedWorkers"),
+          sort: true,
+          targetClass: WorkerTarget,
+          targets: workers.shared
+        }),
+        TargetList({
+          client,
+          id: "other-workers",
+          name: Strings.GetStringFromName("otherWorkers"),
+          sort: true,
+          targetClass: WorkerTarget,
+          targets: workers.other
+        })
+      )
+    );
   }
 });
--- a/devtools/client/aboutdebugging/components/workers/service-worker-target.js
+++ b/devtools/client/aboutdebugging/components/workers/service-worker-target.js
@@ -150,28 +150,30 @@ module.exports = createClass({
     // ACTIVE state. Unable to know the actual state ("installing", "waiting"), we
     // display a custom state "registering" for now. See Bug 1153292.
     return "registering";
   },
 
   renderButtons() {
     let pushButton = dom.button({
       className: "push-button",
-      onClick: this.push
+      onClick: this.push,
+      disabled: this.props.debugDisabled
     }, Strings.GetStringFromName("push"));
 
     let debugButton = dom.button({
       className: "debug-button",
       onClick: this.debug,
       disabled: this.props.debugDisabled
     }, Strings.GetStringFromName("debug"));
 
     let startButton = dom.button({
       className: "start-button",
       onClick: this.start,
+      disabled: this.props.debugDisabled
     }, Strings.GetStringFromName("start"));
 
     if (this.isRunning()) {
       if (this.isActive()) {
         return [pushButton, debugButton];
       }
       // Only debug button is available if the service worker is not active.
       return debugButton;
@@ -182,17 +184,17 @@ module.exports = createClass({
   renderUnregisterLink() {
     if (!this.isActive()) {
       // If not active, there might be no registrationActor available.
       return null;
     }
 
     return dom.a({
       onClick: this.unregister,
-      className: "unregister-link"
+      className: "unregister-link",
     }, Strings.GetStringFromName("unregister"));
   },
 
   render() {
     let { target } = this.props;
     let { pushSubscription } = this.state;
     let status = this.getServiceWorkerStatus();
 
--- a/devtools/client/aboutdebugging/test/browser.ini
+++ b/devtools/client/aboutdebugging/test/browser.ini
@@ -30,16 +30,18 @@ tags = webextensions
 tags = webextensions
 [browser_addons_debugging_initial_state.js]
 [browser_addons_install.js]
 [browser_addons_reload.js]
 [browser_addons_toggle_debug.js]
 [browser_page_not_found.js]
 [browser_service_workers.js]
 [browser_service_workers_fetch_flag.js]
+[browser_service_workers_multi_content_process.js]
+skip-if = !e10s # This test is only valid in e10s
 [browser_service_workers_not_compatible.js]
 [browser_service_workers_push.js]
 [browser_service_workers_push_service.js]
 [browser_service_workers_start.js]
 [browser_service_workers_status.js]
 [browser_service_workers_timeout.js]
 skip-if = true # Bug 1232931
 [browser_service_workers_unregister.js]
--- a/devtools/client/aboutdebugging/test/browser_service_workers.js
+++ b/devtools/client/aboutdebugging/test/browser_service_workers.js
@@ -4,23 +4,17 @@
 "use strict";
 
 // Service workers can't be loaded from chrome://,
 // but http:// is ok with dom.serviceWorkers.testing.enabled turned on.
 const SERVICE_WORKER = URL_ROOT + "service-workers/empty-sw.js";
 const TAB_URL = URL_ROOT + "service-workers/empty-sw.html";
 
 add_task(function* () {
-  yield new Promise(done => {
-    let options = {"set": [
-      ["dom.serviceWorkers.enabled", true],
-      ["dom.serviceWorkers.testing.enabled", true],
-    ]};
-    SpecialPowers.pushPrefEnv(options, done);
-  });
+  yield enableServiceWorkerDebugging();
 
   let { tab, document } = yield openAboutDebugging("workers");
 
   let swTab = yield addTab(TAB_URL);
 
   let serviceWorkersElement = getServiceWorkerList(document);
 
   yield waitForMutation(serviceWorkersElement, { childList: true });
--- a/devtools/client/aboutdebugging/test/browser_service_workers_fetch_flag.js
+++ b/devtools/client/aboutdebugging/test/browser_service_workers_fetch_flag.js
@@ -4,24 +4,17 @@
 "use strict";
 
 // Service workers can't be loaded from chrome://,
 // but http:// is ok with dom.serviceWorkers.testing.enabled turned on.
 const EMPTY_SW_TAB_URL = URL_ROOT + "service-workers/empty-sw.html";
 const FETCH_SW_TAB_URL = URL_ROOT + "service-workers/fetch-sw.html";
 
 function* testBody(url, expecting) {
-  yield new Promise(done => {
-    let options = {"set": [
-      ["dom.serviceWorkers.enabled", true],
-      ["dom.serviceWorkers.testing.enabled", true],
-    ]};
-    SpecialPowers.pushPrefEnv(options, done);
-  });
-
+  yield enableServiceWorkerDebugging();
   let { tab, document } = yield openAboutDebugging("workers");
 
   let swTab = yield addTab(url);
 
   let serviceWorkersElement = getServiceWorkerList(document);
   yield waitForMutation(serviceWorkersElement, { childList: true });
 
   let fetchFlags =
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging/test/browser_service_workers_multi_content_process.js
@@ -0,0 +1,61 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Service worker debugging is unavailable when multi-e10s is enabled.
+// Check that the appropriate warning panel is displayed when there are more than 1
+// content process available.
+
+const SERVICE_WORKER = URL_ROOT + "service-workers/empty-sw.js";
+const TAB_URL = URL_ROOT + "service-workers/empty-sw.html";
+
+add_task(function* () {
+  yield enableServiceWorkerDebugging();
+  info("Force two content processes");
+  yield pushPref("dom.ipc.processCount", 2);
+
+  let { tab, document } = yield openAboutDebugging("workers");
+
+  let warningSection = document.querySelector(".service-worker-multi-process");
+  let img = warningSection.querySelector(".warning");
+  ok(img, "warning message is rendered");
+
+  let swTab = yield addTab(TAB_URL, { background: true });
+  let serviceWorkersElement = getServiceWorkerList(document);
+
+  yield waitForMutation(serviceWorkersElement, { childList: true });
+
+  info("Check that service worker buttons are disabled.");
+  // Check that the service worker appears in the UI
+  let serviceWorkerContainer = getServiceWorkerContainer(SERVICE_WORKER, document);
+  let debugButton = serviceWorkerContainer.querySelector(".debug-button");
+  ok(debugButton.disabled, "Start/Debug button is disabled");
+
+  info("Update the preference to 1");
+  let onWarningCleared = waitUntil(() => {
+    return document.querySelector(".service-worker-multi-process");
+  });
+  yield pushPref("dom.ipc.processCount", 1);
+  yield onWarningCleared;
+  ok(!debugButton.disabled, "Debug button is enabled.");
+
+  info("Update the preference back to 2");
+  let onWarningRestored = waitUntil(() => {
+    return document.querySelector(".service-worker-multi-process");
+  });
+  yield pushPref("dom.ipc.processCount", 2);
+  yield onWarningRestored;
+  ok(debugButton.disabled, "Debug button is disabled again.");
+
+  info("Unregister service worker");
+  try {
+    yield unregisterServiceWorker(swTab, serviceWorkersElement);
+    ok(true, "Service worker registration unregistered");
+  } catch (e) {
+    ok(false, "SW not unregistered; " + e);
+  }
+
+  yield removeTab(swTab);
+  yield closeAboutDebugging(tab);
+});
--- a/devtools/client/aboutdebugging/test/browser_service_workers_push.js
+++ b/devtools/client/aboutdebugging/test/browser_service_workers_push.js
@@ -10,25 +10,17 @@
 // It should trigger a "push" notification in the worker.
 
 // Service workers can't be loaded from chrome://, but http:// is ok with
 // dom.serviceWorkers.testing.enabled turned on.
 const SERVICE_WORKER = URL_ROOT + "service-workers/push-sw.js";
 const TAB_URL = URL_ROOT + "service-workers/push-sw.html";
 
 add_task(function* () {
-  info("Turn on workers via mochitest http.");
-  yield new Promise(done => {
-    let options = { "set": [
-      // Accept workers from mochitest's http.
-      ["dom.serviceWorkers.testing.enabled", true],
-    ]};
-    SpecialPowers.pushPrefEnv(options, done);
-  });
-
+  yield enableServiceWorkerDebugging();
   let { tab, document } = yield openAboutDebugging("workers");
 
   // Listen for mutations in the service-workers list.
   let serviceWorkersElement = getServiceWorkerList(document);
   let onMutation = waitForMutation(serviceWorkersElement, { childList: true });
 
   // Open a tab that registers a push service worker.
   let swTab = yield addTab(TAB_URL);
--- a/devtools/client/aboutdebugging/test/browser_service_workers_push_service.js
+++ b/devtools/client/aboutdebugging/test/browser_service_workers_push_service.js
@@ -13,24 +13,19 @@ const TAB_URL = URL_ROOT + "service-work
 
 const FAKE_ENDPOINT = "https://fake/endpoint";
 
 const PushService = Cc["@mozilla.org/push/Service;1"]
   .getService(Ci.nsIPushService).wrappedJSObject;
 
 add_task(function* () {
   info("Turn on workers via mochitest http.");
-  yield SpecialPowers.pushPrefEnv({
-    "set": [
-      // Accept workers from mochitest's http.
-      ["dom.serviceWorkers.testing.enabled", true],
-      // Enable the push service.
-      ["dom.push.connection.enabled", true],
-    ]
-  });
+  yield enableServiceWorkerDebugging();
+  // Enable the push service.
+  yield pushPref("dom.push.connection.enabled", true);
 
   info("Mock the push service");
   PushService.service = {
     _registrations: new Map(),
     _notify(scope) {
       Services.obs.notifyObservers(
         null,
         PushService.subscriptionModifiedTopic,
--- a/devtools/client/aboutdebugging/test/browser_service_workers_start.js
+++ b/devtools/client/aboutdebugging/test/browser_service_workers_start.js
@@ -10,27 +10,19 @@
 // Service workers can't be loaded from chrome://, but http:// is ok with
 // dom.serviceWorkers.testing.enabled turned on.
 const SERVICE_WORKER = URL_ROOT + "service-workers/empty-sw.js";
 const TAB_URL = URL_ROOT + "service-workers/empty-sw.html";
 
 const SW_TIMEOUT = 1000;
 
 add_task(function* () {
-  info("Turn on workers via mochitest http.");
-  yield new Promise(done => {
-    let options = { "set": [
-      // Accept workers from mochitest's http.
-      ["dom.serviceWorkers.testing.enabled", true],
-      // Reduce the timeout to accelerate service worker freezing
-      ["dom.serviceWorkers.idle_timeout", SW_TIMEOUT],
-      ["dom.serviceWorkers.idle_extended_timeout", SW_TIMEOUT],
-    ]};
-    SpecialPowers.pushPrefEnv(options, done);
-  });
+  yield enableServiceWorkerDebugging();
+  yield pushPref("dom.serviceWorkers.idle_timeout", SW_TIMEOUT);
+  yield pushPref("dom.serviceWorkers.idle_extended_timeout", SW_TIMEOUT);
 
   let { tab, document } = yield openAboutDebugging("workers");
 
   // Listen for mutations in the service-workers list.
   let serviceWorkersElement = getServiceWorkerList(document);
   let onMutation = waitForMutation(serviceWorkersElement, { childList: true });
 
   // Open a tab that registers an empty service worker.
--- a/devtools/client/aboutdebugging/test/browser_service_workers_status.js
+++ b/devtools/client/aboutdebugging/test/browser_service_workers_status.js
@@ -7,25 +7,19 @@
 // but http:// is ok with dom.serviceWorkers.testing.enabled turned on.
 const SERVICE_WORKER = URL_ROOT + "service-workers/delay-sw.js";
 const TAB_URL = URL_ROOT + "service-workers/delay-sw.html";
 const SW_TIMEOUT = 2000;
 
 requestLongerTimeout(2);
 
 add_task(function* () {
-  yield SpecialPowers.pushPrefEnv({
-    "set": [
-      // Accept workers from mochitest's http.
-      ["dom.serviceWorkers.testing.enabled", true],
-      ["dom.serviceWorkers.idle_timeout", SW_TIMEOUT],
-      ["dom.serviceWorkers.idle_extended_timeout", SW_TIMEOUT],
-      ["dom.ipc.processCount", 1],
-    ]
-  });
+  yield enableServiceWorkerDebugging();
+  yield pushPref("dom.serviceWorkers.idle_timeout", SW_TIMEOUT);
+  yield pushPref("dom.serviceWorkers.idle_extended_timeout", SW_TIMEOUT);
 
   let { tab, document } = yield openAboutDebugging("workers");
 
   // Listen for mutations in the service-workers list.
   let serviceWorkersElement = getServiceWorkerList(document);
   let onMutation = waitForMutation(serviceWorkersElement, { childList: true });
 
   let swTab = yield addTab(TAB_URL);
--- a/devtools/client/aboutdebugging/test/browser_service_workers_timeout.js
+++ b/devtools/client/aboutdebugging/test/browser_service_workers_timeout.js
@@ -6,27 +6,19 @@
 // Service workers can't be loaded from chrome://,
 // but http:// is ok with dom.serviceWorkers.testing.enabled turned on.
 const SERVICE_WORKER = URL_ROOT + "service-workers/empty-sw.js";
 const TAB_URL = URL_ROOT + "service-workers/empty-sw.html";
 
 const SW_TIMEOUT = 1000;
 
 add_task(function* () {
-  yield new Promise(done => {
-    let options = {"set": [
-      // Accept workers from mochitest's http
-      ["dom.serviceWorkers.testing.enabled", true],
-      // Reduce the timeout to expose issues when service worker
-      // freezing is broken
-      ["dom.serviceWorkers.idle_timeout", SW_TIMEOUT],
-      ["dom.serviceWorkers.idle_extended_timeout", SW_TIMEOUT],
-    ]};
-    SpecialPowers.pushPrefEnv(options, done);
-  });
+  yield enableServiceWorkerDebugging();
+  yield pushPref("dom.serviceWorkers.idle_timeout", SW_TIMEOUT);
+  yield pushPref("dom.serviceWorkers.idle_extended_timeout", SW_TIMEOUT);
 
   let { tab, document } = yield openAboutDebugging("workers");
 
   let swTab = yield addTab(TAB_URL);
 
   let serviceWorkersElement = getServiceWorkerList(document);
   yield waitForMutation(serviceWorkersElement, { childList: true });
 
--- a/devtools/client/aboutdebugging/test/browser_service_workers_unregister.js
+++ b/devtools/client/aboutdebugging/test/browser_service_workers_unregister.js
@@ -10,25 +10,17 @@
 
 // Service workers can't be loaded from chrome://, but http:// is ok with
 // dom.serviceWorkers.testing.enabled turned on.
 const SCOPE = URL_ROOT + "service-workers/";
 const SERVICE_WORKER = SCOPE + "empty-sw.js";
 const TAB_URL = SCOPE + "empty-sw.html";
 
 add_task(function* () {
-  info("Turn on workers via mochitest http.");
-  yield new Promise(done => {
-    let options = { "set": [
-      // Accept workers from mochitest's http.
-      ["dom.serviceWorkers.testing.enabled", true],
-      ["dom.ipc.processCount", 1],
-    ]};
-    SpecialPowers.pushPrefEnv(options, done);
-  });
+  yield enableServiceWorkerDebugging();
 
   let { tab, document } = yield openAboutDebugging("workers");
 
   // Listen for mutations in the service-workers list.
   let serviceWorkersElement = getServiceWorkerList(document);
   let onMutation = waitForMutation(serviceWorkersElement, { childList: true });
 
   // Open a tab that registers an empty service worker.
--- a/devtools/client/aboutdebugging/test/head.js
+++ b/devtools/client/aboutdebugging/test/head.js
@@ -2,17 +2,18 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /* eslint-env browser */
 /* exported openAboutDebugging, changeAboutDebuggingHash, closeAboutDebugging,
    installAddon, uninstallAddon, waitForMutation, waitForContentMutation, assertHasTarget,
    getServiceWorkerList, getTabList, openPanel, waitForInitialAddonList,
    waitForServiceWorkerRegistered, unregisterServiceWorker,
    waitForDelayedStartupFinished, setupTestAboutDebuggingWebExtension,
-   waitForServiceWorkerActivation */
+   waitForServiceWorkerActivation, enableServiceWorkerDebugging,
+   getServiceWorkerContainer */
 /* import-globals-from ../../framework/test/shared-head.js */
 
 "use strict";
 
 // Load the shared-head file first.
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js",
   this);
@@ -127,16 +128,36 @@ function getInstalledAddonNames(document
  * @return {DOMNode}                 target list or container element
  */
 function getServiceWorkerList(document) {
   return document.querySelector("#service-workers .target-list") ||
     document.querySelector("#service-workers.targets");
 }
 
 /**
+ * Retrieve the container element for the service worker corresponding to the provided
+ * name.
+ *
+ * @param  {String} name
+ *         expected service worker name
+ * @param  {DOMDocument} document
+ *         #service-workers section container document
+ * @return {DOMNode} container element
+ */
+function getServiceWorkerContainer(name, document) {
+  let nameElements = [...document.querySelectorAll("#service-workers .target-name")];
+  let nameElement = nameElements.filter(element => element.textContent === name)[0];
+  if (nameElement) {
+    return nameElement.closest(".target-container");
+  }
+
+  return null;
+}
+
+/**
  * Depending on whether there are tabs opened, return either a
  * target list element or its container.
  * @param  {DOMDocument}  document   #tabs section container document
  * @return {DOMNode}                 target list or container element
  */
 function getTabList(document) {
   return document.querySelector("#tabs .target-list") ||
     document.querySelector("#tabs.targets");
@@ -392,8 +413,25 @@ function* waitForServiceWorkerActivation
 
   let targetElement = name.parentNode.parentNode;
   let targetStatus = targetElement.querySelector(".target-status");
   while (targetStatus.textContent === "Registering") {
     // Wait for the status to leave the "registering" stage.
     yield waitForMutation(serviceWorkersElement, { childList: true, subtree: true });
   }
 }
+
+/**
+ * Set all preferences needed to enable service worker debugging and testing.
+ */
+function enableServiceWorkerDebugging() {
+  return new Promise(done => {
+    let options = { "set": [
+      // Enable service workers.
+      ["dom.serviceWorkers.enabled", true],
+      // Accept workers from mochitest's http.
+      ["dom.serviceWorkers.testing.enabled", true],
+      // Force single content process.
+      ["dom.ipc.processCount", 1],
+    ]};
+    SpecialPowers.pushPrefEnv(options, done);
+  });
+}
--- a/devtools/client/locales/en-US/aboutdebugging.properties
+++ b/devtools/client/locales/en-US/aboutdebugging.properties
@@ -114,8 +114,28 @@ pageNotFound = Page not found
 # %S will be replaced by the name of the page at run-time.
 doesNotExist = #%S does not exist!
 
 # LOCALIZATION NOTE (nothing):
 # This string is displayed when the list of workers is empty.
 nothing = Nothing yet.
 
 configurationIsNotCompatible = Your browser configuration is not compatible with Service Workers
+
+# LOCALIZATION NOTE (multiProcessWarningTitle):
+# This string is displayed as a warning message on top of the about:debugging#workers
+# page when multi-e10s is enabled
+multiProcessWarningTitle = Service Worker debugging is not compatible with multiple content processes at the moment.
+
+# LOCALIZATION NOTE (multiProcessWarningMessage):
+# This string is displayed in the warning section for multi-e10s in
+# about:debugging#workers
+multiProcessWarningMessage = The preference “dom.ipc.processCount” can be set to 1 to force a single content process.
+
+# LOCALIZATION NOTE (multiProcessWarningLink):
+# This string is the text content of a link in the warning section for multi-e10s in
+# about:debugging#workers. The link updates the pref and restarts the browser.
+multiProcessWarningUpdateLink = Set dom.ipc.processCount to 1
+
+# LOCALIZATION NOTE (multiProcessWarningConfirmUpdate):
+# This string is displayed as a confirmation message when the user clicks on
+# the multiProcessWarningUpdateLink in about:debugging#workers
+multiProcessWarningConfirmUpdate = Set “dom.ipc.processCount” to 1 and restart the browser?
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -37,16 +37,17 @@
 #include "mozilla/CheckedInt.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/LoadInfo.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/CustomElementRegistry.h"
 #include "mozilla/dom/DocumentFragment.h"
 #include "mozilla/dom/DOMTypes.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/FileSystemSecurity.h"
 #include "mozilla/dom/FileBlobImpl.h"
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "mozilla/dom/HTMLTemplateElement.h"
 #include "mozilla/dom/HTMLContentElement.h"
 #include "mozilla/dom/HTMLShadowElement.h"
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/dom/ipc/BlobParent.h"
 #include "mozilla/dom/Promise.h"
@@ -7880,16 +7881,29 @@ nsContentUtils::TransferableToIPCTransfe
 
                 Shmem dataAsShmem = ConvertToShmem(aChild, aParent, data);
                 item->data() = dataAsShmem;
               }
 
               continue;
             }
 
+            if (aParent) {
+              bool isDir = false;
+              if (NS_SUCCEEDED(file->IsDirectory(&isDir)) && isDir) {
+                nsAutoString path;
+                if (NS_WARN_IF(NS_FAILED(file->GetPath(path)))) {
+                  continue;
+                }
+
+                RefPtr<FileSystemSecurity> fss = FileSystemSecurity::GetOrCreate();
+                fss->GrantAccessToContentProcess(aParent->ChildID(), path);
+              }
+            }
+
             blobImpl = new FileBlobImpl(file);
 
             IgnoredErrorResult rv;
 
             // Ensure that file data is cached no that the content process
             // has this data available to it when passed over:
             blobImpl->GetSize(rv);
             if (NS_WARN_IF(rv.Failed())) {
--- a/dom/filesystem/FileSystemRequestParent.cpp
+++ b/dom/filesystem/FileSystemRequestParent.cpp
@@ -5,18 +5,22 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/FileSystemRequestParent.h"
 #include "mozilla/dom/PFileSystemParams.h"
 
 #include "GetDirectoryListingTask.h"
 #include "GetFileOrDirectoryTask.h"
 
+#include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/FileSystemBase.h"
+#include "mozilla/dom/FileSystemSecurity.h"
 #include "mozilla/ipc/BackgroundParent.h"
+#include "mozilla/Unused.h"
+#include "nsProxyRelease.h"
 
 using namespace mozilla::ipc;
 
 namespace mozilla {
 namespace dom {
 
 FileSystemRequestParent::FileSystemRequestParent()
   : mDestroyed(false)
@@ -64,24 +68,114 @@ FileSystemRequestParent::Initialize(cons
   if (NS_WARN_IF(!mTask || !mFileSystem)) {
     // Should never reach here.
     return false;
   }
 
   return true;
 }
 
+namespace {
+
+class CheckPermissionRunnable final : public Runnable
+{
+public:
+  CheckPermissionRunnable(already_AddRefed<ContentParent> aParent,
+                          FileSystemRequestParent* aActor,
+                          FileSystemTaskParentBase* aTask,
+                          const nsAString& aPath)
+    : mContentParent(aParent)
+    , mActor(aActor)
+    , mTask(aTask)
+    , mPath(aPath)
+    , mBackgroundEventTarget(NS_GetCurrentThread())
+  {
+    AssertIsInMainProcess();
+    AssertIsOnBackgroundThread();
+
+    MOZ_ASSERT(mContentParent);
+    MOZ_ASSERT(mActor);
+    MOZ_ASSERT(mTask);
+    MOZ_ASSERT(mBackgroundEventTarget);
+  }
+
+  NS_IMETHOD
+  Run() override
+  {
+    if (NS_IsMainThread()) {
+      auto raii = mozilla::MakeScopeExit([&] { mContentParent = nullptr; });
+
+
+      if (!mozilla::Preferences::GetBool("dom.filesystem.pathcheck.disabled", false)) {
+        RefPtr<FileSystemSecurity> fss = FileSystemSecurity::Get();
+        if (NS_WARN_IF(!fss ||
+                       !fss->ContentProcessHasAccessTo(mContentParent->ChildID(),
+                                                       mPath))) {
+          mContentParent->KillHard("This path is not allowed.");
+          return NS_OK;
+        }
+      }
+
+      return mBackgroundEventTarget->Dispatch(this, NS_DISPATCH_NORMAL);
+    }
+
+    AssertIsOnBackgroundThread();
+
+    // It can happen that this actor has been destroyed in the meantime we were
+    // on the main-thread.
+    if (!mActor->Destroyed()) {
+      mTask->Start();
+    }
+
+    return NS_OK;
+  }
+
+private:
+  ~CheckPermissionRunnable()
+  {
+     NS_ProxyRelease(mBackgroundEventTarget, mActor.forget());
+  }
+
+  RefPtr<ContentParent> mContentParent;
+  RefPtr<FileSystemRequestParent> mActor;
+  RefPtr<FileSystemTaskParentBase> mTask;
+  const nsString mPath;
+
+  nsCOMPtr<nsIEventTarget> mBackgroundEventTarget;
+};
+
+} // anonymous
+
 void
 FileSystemRequestParent::Start()
 {
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+
   MOZ_ASSERT(!mDestroyed);
   MOZ_ASSERT(mFileSystem);
   MOZ_ASSERT(mTask);
 
-  mTask->Start();
+  nsAutoString path;
+  if (NS_WARN_IF(NS_FAILED(mTask->GetTargetPath(path)))) {
+    Unused << Send__delete__(this, FileSystemErrorResponse(NS_ERROR_DOM_SECURITY_ERR));
+    return;
+  }
+
+  RefPtr<ContentParent> parent = BackgroundParent::GetContentParent(Manager());
+
+  // If the ContentParent is null we are dealing with a same-process actor.
+  if (!parent) {
+    mTask->Start();
+    return;
+  }
+
+  RefPtr<Runnable> runnable =
+    new CheckPermissionRunnable(parent.forget(), this, mTask, path);
+  NS_DispatchToMainThread(runnable);
 }
 
 void
 FileSystemRequestParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(!mDestroyed);
 
new file mode 100644
--- /dev/null
+++ b/dom/filesystem/FileSystemSecurity.cpp
@@ -0,0 +1,107 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "FileSystemSecurity.h"
+#include "FileSystemUtils.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/StaticPtr.h"
+
+namespace mozilla {
+namespace dom {
+
+namespace {
+
+StaticRefPtr<FileSystemSecurity> gFileSystemSecurity;
+
+} // anonymous
+
+/* static */ already_AddRefed<FileSystemSecurity>
+FileSystemSecurity::Get()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsInMainProcess();
+
+  RefPtr<FileSystemSecurity> service = gFileSystemSecurity.get();
+  return service.forget();
+}
+
+/* static */ already_AddRefed<FileSystemSecurity>
+FileSystemSecurity::GetOrCreate()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsInMainProcess();
+
+  if (!gFileSystemSecurity) {
+    gFileSystemSecurity = new FileSystemSecurity();
+    ClearOnShutdown(&gFileSystemSecurity);
+  }
+
+  RefPtr<FileSystemSecurity> service = gFileSystemSecurity.get();
+  return service.forget();
+}
+
+FileSystemSecurity::FileSystemSecurity()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsInMainProcess();
+}
+
+FileSystemSecurity::~FileSystemSecurity()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsInMainProcess();
+}
+
+void
+FileSystemSecurity::GrantAccessToContentProcess(ContentParentId aId,
+                                                const nsAString& aDirectoryPath)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsInMainProcess();
+
+  nsTArray<nsString>* paths;
+  if (!mPaths.Get(aId, &paths)) {
+    paths = new nsTArray<nsString>();
+    mPaths.Put(aId, paths);
+  } else if (paths->Contains(aDirectoryPath)) {
+    return;
+  }
+
+  paths->AppendElement(aDirectoryPath);
+}
+
+void
+FileSystemSecurity::Forget(ContentParentId aId)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsInMainProcess();
+
+  mPaths.Remove(aId);
+}
+
+bool
+FileSystemSecurity::ContentProcessHasAccessTo(ContentParentId aId,
+                                              const nsAString& aPath)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsInMainProcess();
+
+  nsTArray<nsString>* paths;
+  if (!mPaths.Get(aId, &paths)) {
+    return false;
+  }
+
+  for (uint32_t i = 0, len = paths->Length(); i < len; ++i) {
+    if (FileSystemUtils::IsDescendantPath(paths->ElementAt(i), aPath)) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+} // dom namespace
+} // mozilla namespace
new file mode 100644
--- /dev/null
+++ b/dom/filesystem/FileSystemSecurity.h
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_FileSystemSecurity_h
+#define mozilla_dom_FileSystemSecurity_h
+
+#include "mozilla/dom/ipc/IdType.h"
+#include "nsClassHashtable.h"
+#include "nsISupportsImpl.h"
+
+namespace mozilla {
+namespace dom {
+
+class FileSystemSecurity final
+{
+public:
+  NS_INLINE_DECL_REFCOUNTING(FileSystemSecurity)
+
+  static already_AddRefed<FileSystemSecurity>
+  Get();
+
+  static already_AddRefed<FileSystemSecurity>
+  GetOrCreate();
+
+  void
+  GrantAccessToContentProcess(ContentParentId aId,
+                              const nsAString& aDirectoryPath);
+
+  void
+  Forget(ContentParentId aId);
+
+  bool
+  ContentProcessHasAccessTo(ContentParentId aId, const nsAString& aPath);
+
+private:
+  FileSystemSecurity();
+  ~FileSystemSecurity();
+
+  nsClassHashtable<nsUint64HashKey, nsTArray<nsString>> mPaths;
+};
+
+} // dom namespace
+} // mozilla namespace
+
+#endif // mozilla_dom_FileSystemSecurity_h
--- a/dom/filesystem/FileSystemTaskBase.h
+++ b/dom/filesystem/FileSystemTaskBase.h
@@ -226,16 +226,19 @@ public:
   HandleResult();
 
   bool
   HasError() const { return NS_FAILED(mErrorValue); }
 
   NS_IMETHOD
   Run() override;
 
+  virtual nsresult
+  GetTargetPath(nsAString& aPath) const = 0;
+
 private:
   /*
    * Wrap the task result to FileSystemResponseValue for sending it through IPC.
    * It will be called when the task is completed and we need to
    * send the task result back to the content. This runs on the PBackground
    * thread.
    */
   FileSystemResponseValue
--- a/dom/filesystem/FileSystemUtils.cpp
+++ b/dom/filesystem/FileSystemUtils.cpp
@@ -15,34 +15,22 @@ bool
 TokenizerIgnoreNothing(char16_t /* aChar */)
 {
   return false;
 }
 
 } // anonymous namespace
 
 /* static */ bool
-FileSystemUtils::IsDescendantPath(nsIFile* aFile,
-                                  nsIFile* aDescendantFile)
+FileSystemUtils::IsDescendantPath(const nsAString& aPath,
+                                  const nsAString& aDescendantPath)
 {
-  nsAutoString path;
-  nsresult rv = aFile->GetPath(path);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return false;
-  }
-
-  nsAutoString descendantPath;
-  rv = aDescendantFile->GetPath(descendantPath);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return false;
-  }
-
   // Check the sub-directory path to see if it has the parent path as prefix.
-  if (descendantPath.Length() <= path.Length() ||
-      !StringBeginsWith(descendantPath, path)) {
+  if (!aDescendantPath.Equals(aPath) &&
+      !StringBeginsWith(aDescendantPath, aPath)) {
     return false;
   }
 
   return true;
 }
 
 /* static */ bool
 FileSystemUtils::IsValidRelativeDOMPath(const nsAString& aPath,
--- a/dom/filesystem/FileSystemUtils.h
+++ b/dom/filesystem/FileSystemUtils.h
@@ -21,17 +21,18 @@ namespace dom {
  */
 class FileSystemUtils
 {
 public:
   /*
    * Return true if aDescendantPath is a descendant of aPath.
    */
   static bool
-  IsDescendantPath(nsIFile* aPath, nsIFile* aDescendantPath);
+  IsDescendantPath(const nsAString& aPath,
+                   const nsAString& aDescendantPath);
 
   /**
    * Return true if this is valid DOMPath. It also splits the path in
    * subdirectories and stores them in aParts.
    */
   static bool
   IsValidRelativeDOMPath(const nsAString& aPath,
                          nsTArray<nsString>& aParts);
--- a/dom/filesystem/GetDirectoryListingTask.cpp
+++ b/dom/filesystem/GetDirectoryListingTask.cpp
@@ -375,10 +375,16 @@ GetDirectoryListingTaskParent::IOWork()
 
     if (!mTargetData.AppendElement(element, fallible)) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
   }
   return NS_OK;
 }
 
+nsresult
+GetDirectoryListingTaskParent::GetTargetPath(nsAString& aPath) const
+{
+  return mTargetPath->GetPath(aPath);
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/GetDirectoryListingTask.h
+++ b/dom/filesystem/GetDirectoryListingTask.h
@@ -64,16 +64,19 @@ class GetDirectoryListingTaskParent fina
 {
 public:
   static already_AddRefed<GetDirectoryListingTaskParent>
   Create(FileSystemBase* aFileSystem,
          const FileSystemGetDirectoryListingParams& aParam,
          FileSystemRequestParent* aParent,
          ErrorResult& aRv);
 
+  nsresult
+  GetTargetPath(nsAString& aPath) const override;
+
 private:
   GetDirectoryListingTaskParent(FileSystemBase* aFileSystem,
                                 const FileSystemGetDirectoryListingParams& aParam,
                                 FileSystemRequestParent* aParent);
 
   virtual FileSystemResponseValue
   GetSuccessRequestResult(ErrorResult& aRv) const override;
 
--- a/dom/filesystem/GetFileOrDirectoryTask.cpp
+++ b/dom/filesystem/GetFileOrDirectoryTask.cpp
@@ -265,10 +265,16 @@ GetFileOrDirectoryTaskParent::IOWork()
 
   if (!mFileSystem->IsSafeFile(mTargetPath)) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   return NS_OK;
 }
 
+nsresult
+GetFileOrDirectoryTaskParent::GetTargetPath(nsAString& aPath) const
+{
+  return mTargetPath->GetPath(aPath);
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/GetFileOrDirectoryTask.h
+++ b/dom/filesystem/GetFileOrDirectoryTask.h
@@ -57,16 +57,19 @@ class GetFileOrDirectoryTaskParent final
 {
 public:
   static already_AddRefed<GetFileOrDirectoryTaskParent>
   Create(FileSystemBase* aFileSystem,
          const FileSystemGetFileOrDirectoryParams& aParam,
          FileSystemRequestParent* aParent,
          ErrorResult& aRv);
 
+  nsresult
+  GetTargetPath(nsAString& aPath) const override;
+
 protected:
   virtual FileSystemResponseValue
   GetSuccessRequestResult(ErrorResult& aRv) const override;
 
   virtual nsresult
   IOWork() override;
 
 private:
--- a/dom/filesystem/GetFilesTask.cpp
+++ b/dom/filesystem/GetFilesTask.cpp
@@ -248,10 +248,16 @@ GetFilesTaskParent::IOWork()
   rv = ExploreDirectory(mDirectoryDOMPath, mTargetPath);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
+nsresult
+GetFilesTaskParent::GetTargetPath(nsAString& aPath) const
+{
+  return mTargetPath->GetPath(aPath);
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/GetFilesTask.h
+++ b/dom/filesystem/GetFilesTask.h
@@ -66,16 +66,19 @@ class GetFilesTaskParent final : public 
 {
 public:
   static already_AddRefed<GetFilesTaskParent>
   Create(FileSystemBase* aFileSystem,
          const FileSystemGetFilesParams& aParam,
          FileSystemRequestParent* aParent,
          ErrorResult& aRv);
 
+  nsresult
+  GetTargetPath(nsAString& aPath) const override;
+
 private:
   GetFilesTaskParent(FileSystemBase* aFileSystem,
                      const FileSystemGetFilesParams& aParam,
                      FileSystemRequestParent* aParent);
 
   virtual FileSystemResponseValue
   GetSuccessRequestResult(ErrorResult& aRv) const override;
 
--- a/dom/filesystem/compat/tests/test_basic.html
+++ b/dom/filesystem/compat/tests/test_basic.html
@@ -11,16 +11,17 @@
 <script type="application/javascript">
 
 var fileEntry;
 var directoryEntry;
 var script;
 
 function setup_tests() {
   SpecialPowers.pushPrefEnv({"set": [["dom.webkitBlink.dirPicker.enabled", true],
+                                     ["dom.filesystem.pathcheck.disabled", true],
                                      ["dom.webkitBlink.filesystem.enabled", true]]}, next);
 }
 
 function populate_entries() {
   var url = SimpleTest.getTestFileURL("script_entries.js");
   script = SpecialPowers.loadChromeScript(url);
 
   function onOpened(message) {
--- a/dom/filesystem/compat/tests/test_formSubmission.html
+++ b/dom/filesystem/compat/tests/test_formSubmission.html
@@ -27,16 +27,17 @@ function setup_tests() {
   iframe = document.getElementById("target_iframe");
   iframe.onload = function() {
     info("Frame loaded!");
     next();
   }
 
   SpecialPowers.pushPrefEnv({"set": [["dom.input.dirpicker", true],
                                      ["dom.webkitBlink.dirPicker.enabled", true],
+                                     ["dom.filesystem.pathcheck.disabled", true],
                                      ["dom.webkitBlink.filesystem.enabled", true]]}, next);
 }
 
 function populate_entries(webkitDirectory) {
   if (input) {
     form.removeChild(input);
   }
 
--- a/dom/filesystem/compat/tests/test_no_dnd.html
+++ b/dom/filesystem/compat/tests/test_no_dnd.html
@@ -11,16 +11,17 @@
 
 var fileEntry;
 var directoryEntry;
 var script;
 var entries;
 
 function setup_tests() {
   SpecialPowers.pushPrefEnv({"set": [["dom.webkitBlink.dirPicker.enabled", true],
+                                     ["dom.filesystem.pathcheck.disabled", true],
                                      ["dom.webkitBlink.filesystem.enabled", true]]}, next);
 }
 
 function populate_entries() {
   entries = document.createElement('input');
   entries.setAttribute('type', 'file');
   document.body.appendChild(entries);
 
--- a/dom/filesystem/moz.build
+++ b/dom/filesystem/moz.build
@@ -10,26 +10,28 @@ with Files("**"):
 DIRS += ['compat']
 
 TEST_DIRS += ['tests']
 
 EXPORTS.mozilla.dom += [
     'Directory.h',
     'FileSystemBase.h',
     'FileSystemRequestParent.h',
+    'FileSystemSecurity.h',
     'FileSystemTaskBase.h',
     'FileSystemUtils.h',
     'GetFilesHelper.h',
     'OSFileSystem.h',
 ]
 
 UNIFIED_SOURCES += [
     'Directory.cpp',
     'FileSystemBase.cpp',
     'FileSystemRequestParent.cpp',
+    'FileSystemSecurity.cpp',
     'FileSystemTaskBase.cpp',
     'FileSystemUtils.cpp',
     'GetDirectoryListingTask.cpp',
     'GetFileOrDirectoryTask.cpp',
     'GetFilesHelper.cpp',
     'GetFilesTask.cpp',
     'OSFileSystem.cpp',
 ]
--- a/dom/filesystem/tests/filesystem_commons.js
+++ b/dom/filesystem/tests/filesystem_commons.js
@@ -6,16 +6,17 @@ function createRelativePath(parentDir, d
   let path = createPath(parentDir, dirOrFile);
   is(path[0], "/", "The full path should start with '/'");
   return path.substring(1);
 }
 
 function setup_tests(aNext) {
   SimpleTest.requestLongerTimeout(2);
   SpecialPowers.pushPrefEnv({"set": [["dom.input.dirpicker", true],
+                                     ["dom.filesystem.pathcheck.disabled", true],
                                      ["dom.webkitBlink.dirPicker.enabled", true]]}, aNext);
 }
 
 function test_basic(aDirectory, aNext) {
   ok(aDirectory, "Directory exists.");
   ok(aDirectory instanceof Directory, "We have a directory.");
   is(aDirectory.path, '/' + aDirectory.name, "directory.path must be '/'+name");
   aNext();
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -36,16 +36,17 @@
 #include "mozilla/StyleSheetInlines.h"
 #include "mozilla/DataStorage.h"
 #include "mozilla/devtools/HeapSnapshotTempFileHelperParent.h"
 #include "mozilla/docshell/OfflineCacheUpdateParent.h"
 #include "mozilla/dom/DataTransfer.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/FileCreatorHelper.h"
+#include "mozilla/dom/FileSystemSecurity.h"
 #include "mozilla/dom/ExternalHelperAppParent.h"
 #include "mozilla/dom/GetFilesHelper.h"
 #include "mozilla/dom/GeolocationBinding.h"
 #include "mozilla/dom/MemoryReportRequest.h"
 #include "mozilla/dom/Notification.h"
 #include "mozilla/dom/PContentBridgeParent.h"
 #include "mozilla/dom/PContentPermissionRequestParent.h"
 #include "mozilla/dom/PCycleCollectWithLogsParent.h"
@@ -1675,16 +1676,21 @@ ContentParent::ActorDestroy(ActorDestroy
   // finish waiting in the xpcom-shutdown/profile-before-change observer.
   mIPCOpen = false;
 
   if (mHangMonitorActor) {
     ProcessHangMonitor::RemoveProcess(mHangMonitorActor);
     mHangMonitorActor = nullptr;
   }
 
+  RefPtr<FileSystemSecurity> fss = FileSystemSecurity::Get();
+  if (fss) {
+    fss->Forget(ChildID());
+  }
+
   if (why == NormalShutdown && !mCalledClose) {
     // If we shut down normally but haven't called Close, assume somebody
     // else called Close on us. In that case, we still need to call
     // ShutDownProcess below to perform other necessary clean up.
     mCalledClose = true;
   }
 
   // Make sure we always clean up.
--- a/dom/ipc/FilePickerParent.cpp
+++ b/dom/ipc/FilePickerParent.cpp
@@ -8,16 +8,17 @@
 #include "nsComponentManagerUtils.h"
 #include "nsNetCID.h"
 #include "nsIDocument.h"
 #include "nsIDOMWindow.h"
 #include "nsIFile.h"
 #include "nsISimpleEnumerator.h"
 #include "mozilla/Unused.h"
 #include "mozilla/dom/FileBlobImpl.h"
+#include "mozilla/dom/FileSystemSecurity.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/dom/ipc/BlobParent.h"
 
 using mozilla::Unused;
 using namespace mozilla::dom;
 
@@ -140,32 +141,39 @@ void
 FilePickerParent::IORunnable::Destroy()
 {
   mFilePickerParent = nullptr;
 }
 
 void
 FilePickerParent::SendFilesOrDirectories(const nsTArray<BlobImplOrString>& aData)
 {
+  nsIContentParent* parent = TabParent::GetFrom(Manager())->Manager();
+
   if (mMode == nsIFilePicker::modeGetFolder) {
     MOZ_ASSERT(aData.Length() <= 1);
     if (aData.IsEmpty()) {
       Unused << Send__delete__(this, void_t(), mResult);
       return;
     }
 
     MOZ_ASSERT(aData[0].mType == BlobImplOrString::eDirectoryPath);
 
+    // Let's inform the security singleton about the given access of this tab on
+    // this directory path.
+    RefPtr<FileSystemSecurity> fss = FileSystemSecurity::GetOrCreate();
+    fss->GrantAccessToContentProcess(parent->ChildID(),
+                                     aData[0].mDirectoryPath);
+
     InputDirectory input;
     input.directoryPath() = aData[0].mDirectoryPath;
     Unused << Send__delete__(this, input, mResult);
     return;
   }
 
-  nsIContentParent* parent = TabParent::GetFrom(Manager())->Manager();
   InfallibleTArray<PBlobParent*> blobs;
 
   for (unsigned i = 0; i < aData.Length(); i++) {
     MOZ_ASSERT(aData[i].mType == BlobImplOrString::eBlobImpl);
     BlobParent* blobParent = parent->GetOrCreateActorForBlobImpl(aData[i].mBlobImpl);
     if (blobParent) {
       blobs.AppendElement(blobParent);
     }
--- a/image/DynamicImage.cpp
+++ b/image/DynamicImage.cpp
@@ -75,17 +75,17 @@ DynamicImage::OnImageDataComplete(nsIReq
                                   nsISupports* aContext,
                                   nsresult aStatus,
                                   bool aLastPart)
 {
   return NS_OK;
 }
 
 void
-DynamicImage::OnSurfaceDiscarded()
+DynamicImage::OnSurfaceDiscarded(const SurfaceKey& aSurfaceKey)
 { }
 
 void
 DynamicImage::SetInnerWindowID(uint64_t aInnerWindowId)
 { }
 
 uint64_t
 DynamicImage::InnerWindowID() const
--- a/image/DynamicImage.h
+++ b/image/DynamicImage.h
@@ -48,17 +48,17 @@ public:
                                         nsIInputStream* aInStr,
                                         uint64_t aSourceOffset,
                                         uint32_t aCount) override;
   virtual nsresult OnImageDataComplete(nsIRequest* aRequest,
                                        nsISupports* aContext,
                                        nsresult aStatus,
                                        bool aLastPart) override;
 
-  virtual void OnSurfaceDiscarded() override;
+  virtual void OnSurfaceDiscarded(const SurfaceKey& aSurfaceKey) override;
 
   virtual void SetInnerWindowID(uint64_t aInnerWindowId) override;
   virtual uint64_t InnerWindowID() const override;
 
   virtual bool HasError() override;
   virtual void SetHasError() override;
 
   virtual ImageURL* GetURI() override;
--- a/image/FrameAnimator.cpp
+++ b/image/FrameAnimator.cpp
@@ -23,20 +23,36 @@ namespace image {
 
 ///////////////////////////////////////////////////////////////////////////////
 // AnimationState implementation.
 ///////////////////////////////////////////////////////////////////////////////
 
 void
 AnimationState::NotifyDecodeComplete()
 {
+  // If we weren't discarded before the decode finished then mark ourselves as
+  // currently decoded.
+  if (!mDiscarded) {
+    mIsCurrentlyDecoded = true;
+  }
   mHasBeenDecoded = true;
 }
 
 void
+AnimationState::SetDiscarded(bool aDiscarded)
+{
+  if (aDiscarded) {
+    MOZ_ASSERT(gfxPrefs::ImageMemAnimatedDiscardable());
+    mIsCurrentlyDecoded = false;
+    mCompositedFrameInvalid = true;
+  }
+  mDiscarded = aDiscarded;
+}
+
+void
 AnimationState::ResetAnimation()
 {
   mCurrentAnimationFrameIndex = 0;
 }
 
 void
 AnimationState::SetAnimationMode(uint16_t aAnimationMode)
 {
@@ -260,23 +276,27 @@ FrameAnimator::AdvanceFrame(AnimationSta
   ret.mFrameAdvanced = true;
 
   return ret;
 }
 
 RefreshResult
 FrameAnimator::RequestRefresh(AnimationState& aState, const TimeStamp& aTime)
 {
+  // By default, an empty RefreshResult.
+  RefreshResult ret;
+
+  if (aState.IsDiscarded()) {
+    return ret;
+  }
+
   // only advance the frame if the current time is greater than or
   // equal to the current frame's end time.
   TimeStamp currentFrameEndTime = GetCurrentImgFrameEndTime(aState);
 
-  // By default, an empty RefreshResult.
-  RefreshResult ret;
-
   while (currentFrameEndTime <= aTime) {
     TimeStamp oldFrameEndTime = currentFrameEndTime;
 
     RefreshResult frameRes = AdvanceFrame(aState, aTime);
 
     // Accumulate our result for returning to callers.
     ret.Accumulate(frameRes);
 
@@ -285,22 +305,36 @@ FrameAnimator::RequestRefresh(AnimationS
     // If we didn't advance a frame, and our frame end time didn't change,
     // then we need to break out of this loop & wait for the frame(s)
     // to finish downloading.
     if (!frameRes.mFrameAdvanced && (currentFrameEndTime == oldFrameEndTime)) {
       break;
     }
   }
 
+  // Advanced to the correct frame, the composited frame is now valid to be drawn.
+  if (currentFrameEndTime > aTime) {
+    aState.mCompositedFrameInvalid = false;
+  }
+
+  MOZ_ASSERT(!aState.mIsCurrentlyDecoded || !aState.mCompositedFrameInvalid);
+
   return ret;
 }
 
 LookupResult
 FrameAnimator::GetCompositedFrame(AnimationState& aState)
 {
+  if (aState.mCompositedFrameInvalid) {
+    MOZ_ASSERT(gfxPrefs::ImageMemAnimatedDiscardable());
+    MOZ_ASSERT(aState.GetHasBeenDecoded());
+    MOZ_ASSERT(!aState.GetIsCurrentlyDecoded());
+    return LookupResult(MatchType::NOT_FOUND);
+  }
+
   // If we have a composited version of this frame, return that.
   if (mLastCompositedFrameIndex >= 0 &&
       (uint32_t(mLastCompositedFrameIndex) == aState.mCurrentAnimationFrameIndex)) {
     return LookupResult(DrawableSurface(mCompositingFrame->DrawableRef()),
                         MatchType::EXACT);
   }
 
   // Otherwise return the raw frame. DoBlend is required to ensure that we only
--- a/image/FrameAnimator.h
+++ b/image/FrameAnimator.h
@@ -28,29 +28,66 @@ public:
   explicit AnimationState(uint16_t aAnimationMode)
     : mFrameCount(0)
     , mCurrentAnimationFrameIndex(0)
     , mLoopRemainingCount(-1)
     , mLoopCount(-1)
     , mFirstFrameTimeout(FrameTimeout::FromRawMilliseconds(0))
     , mAnimationMode(aAnimationMode)
     , mHasBeenDecoded(false)
+    , mIsCurrentlyDecoded(false)
+    , mCompositedFrameInvalid(false)
+    , mDiscarded(false)
   { }
 
   /**
    * Call when a decode of this image has been completed.
    */
   void NotifyDecodeComplete();
 
   /**
    * Returns true if this image has been fully decoded before.
    */
   bool GetHasBeenDecoded() { return mHasBeenDecoded; }
 
   /**
+   * Call this with true when this image is discarded. Call this with false
+   * when a decoder is created to decode the image.
+   */
+  void SetDiscarded(bool aDiscarded);
+
+  /**
+   * Returns true if this image has been discarded and a decoded has not yet
+   * been created to redecode it.
+   */
+  bool IsDiscarded() { return mDiscarded; }
+
+  /**
+   * Sets the composited frame as valid or invalid.
+   */
+  void SetCompositedFrameInvalid(bool aInvalid) {
+    MOZ_ASSERT(!aInvalid || gfxPrefs::ImageMemAnimatedDiscardable());
+    mCompositedFrameInvalid = aInvalid;
+  }
+
+  /**
+   * Returns whether the composited frame is valid to draw to the screen.
+   */
+  bool GetCompositedFrameInvalid() {
+    return mCompositedFrameInvalid;
+  }
+
+  /**
+   * Returns whether the image is currently full decoded..
+   */
+  bool GetIsCurrentlyDecoded() {
+    return mIsCurrentlyDecoded;
+  }
+
+  /**
    * Call when you need to re-start animating. Ensures we start from the first
    * frame.
    */
   void ResetAnimation();
 
   /**
    * The animation mode of the image.
    *
@@ -140,18 +177,54 @@ private:
   Maybe<FrameTimeout> mLoopLength;
 
   //! The timeout for the first frame of this image.
   FrameTimeout mFirstFrameTimeout;
 
   //! The animation mode of this image. Constants defined in imgIContainer.
   uint16_t mAnimationMode;
 
+  /**
+   * The following four bools (mHasBeenDecoded, mIsCurrentlyDecoded,
+   * mCompositedFrameInvalid, mDiscarded) track the state of the image with
+   * regards to decoding. They all start out false, including mDiscarded,
+   * because we want to treat being discarded differently from "not yet decoded
+   * for the first time".
+   *
+   * (When we are decoding the image for the first time we want to show the
+   * image at the speed of data coming in from the network or the speed
+   * specified in the image file, whichever is slower. But when redecoding we
+   * want to show nothing until the frame for the current time has been
+   * decoded. The prevents the user from seeing the image "fast forward"
+   * to the expected spot.)
+   *
+   * When the image is decoded for the first time mHasBeenDecoded and
+   * mIsCurrentlyDecoded get set to true. When the image is discarded
+   * mIsCurrentlyDecoded gets set to false, and mCompositedFrameInvalid
+   * & mDiscarded get set to true. When we create a decoder to redecode the
+   * image mDiscarded gets set to false. mCompositedFrameInvalid gets set to
+   * false when we are able to advance to the frame that should be showing
+   * for the current time. mIsCurrentlyDecoded gets set to true when the
+   * redecode finishes.
+   */
+
   //! Whether this image has been decoded at least once.
   bool mHasBeenDecoded;
+
+  //! Whether this image is currently fully decoded.
+  bool mIsCurrentlyDecoded;
+
+  //! Whether the composited frame is valid to draw to the screen, note that
+  //! the composited frame can exist and be filled with image data but not
+  //! valid to draw to the screen.
+  bool mCompositedFrameInvalid;
+
+  //! Whether this image is currently discarded. Only set to true after the
+  //! image has been decoded at least once.
+  bool mDiscarded;
 };
 
 /**
  * RefreshResult is used to let callers know how the state of the animation
  * changed during a call to FrameAnimator::RequestRefresh().
  */
 struct RefreshResult
 {
--- a/image/Image.h
+++ b/image/Image.h
@@ -211,17 +211,17 @@ public:
   virtual nsresult OnImageDataComplete(nsIRequest* aRequest,
                                        nsISupports* aContext,
                                        nsresult aStatus,
                                        bool aLastPart) = 0;
 
   /**
    * Called when the SurfaceCache discards a surface belonging to this image.
    */
-  virtual void OnSurfaceDiscarded() = 0;
+  virtual void OnSurfaceDiscarded(const SurfaceKey& aSurfaceKey) = 0;
 
   virtual void SetInnerWindowID(uint64_t aInnerWindowId) = 0;
   virtual uint64_t InnerWindowID() const = 0;
 
   virtual bool HasError() = 0;
   virtual void SetHasError() = 0;
 
   virtual ImageURL* GetURI() = 0;
@@ -251,17 +251,17 @@ public:
   virtual void DecrementAnimationConsumers() override;
 #ifdef DEBUG
   virtual uint32_t GetAnimationConsumers() override
   {
     return mAnimationConsumers;
   }
 #endif
 
-  virtual void OnSurfaceDiscarded() override { }
+  virtual void OnSurfaceDiscarded(const SurfaceKey& aSurfaceKey) override { }
 
   virtual void SetInnerWindowID(uint64_t aInnerWindowId) override
   {
     mInnerWindowId = aInnerWindowId;
   }
   virtual uint64_t InnerWindowID() const override { return mInnerWindowId; }
 
   virtual bool HasError() override    { return mError; }
--- a/image/ImageWrapper.cpp
+++ b/image/ImageWrapper.cpp
@@ -83,19 +83,19 @@ ImageWrapper::OnImageDataComplete(nsIReq
                                   nsresult aStatus,
                                   bool aLastPart)
 {
   return mInnerImage->OnImageDataComplete(aRequest, aContext, aStatus,
                                           aLastPart);
 }
 
 void
-ImageWrapper::OnSurfaceDiscarded()
+ImageWrapper::OnSurfaceDiscarded(const SurfaceKey& aSurfaceKey)
 {
-  return mInnerImage->OnSurfaceDiscarded();
+  return mInnerImage->OnSurfaceDiscarded(aSurfaceKey);
 }
 
 void
 ImageWrapper::SetInnerWindowID(uint64_t aInnerWindowId)
 {
   mInnerImage->SetInnerWindowID(aInnerWindowId);
 }
 
--- a/image/ImageWrapper.h
+++ b/image/ImageWrapper.h
@@ -40,17 +40,17 @@ public:
                                         nsIInputStream* aInStr,
                                         uint64_t aSourceOffset,
                                         uint32_t aCount) override;
   virtual nsresult OnImageDataComplete(nsIRequest* aRequest,
                                        nsISupports* aContext,
                                        nsresult aStatus,
                                        bool aLastPart) override;
 
-  virtual void OnSurfaceDiscarded() override;
+  virtual void OnSurfaceDiscarded(const SurfaceKey& aSurfaceKey) override;
 
   virtual void SetInnerWindowID(uint64_t aInnerWindowId) override;
   virtual uint64_t InnerWindowID() const override;
 
   virtual bool HasError() override;
   virtual void SetHasError() override;
 
   virtual ImageURL* GetURI() override;
--- a/image/RasterImage.cpp
+++ b/image/RasterImage.cpp
@@ -415,20 +415,25 @@ RasterImage::WillDrawOpaqueNow()
       !result.Surface()->IsFinished()) {
     return false;
   }
 
   return true;
 }
 
 void
-RasterImage::OnSurfaceDiscarded()
+RasterImage::OnSurfaceDiscarded(const SurfaceKey& aSurfaceKey)
 {
   MOZ_ASSERT(mProgressTracker);
 
+  if (mAnimationState && aSurfaceKey.Playback() == PlaybackType::eAnimated) {
+    MOZ_ASSERT(gfxPrefs::ImageMemAnimatedDiscardable());
+    mAnimationState->SetDiscarded(true);
+  }
+
   NS_DispatchToMainThread(NewRunnableMethod("ProgressTracker::OnDiscard",
                                             mProgressTracker, &ProgressTracker::OnDiscard));
 }
 
 //******************************************************************************
 NS_IMETHODIMP
 RasterImage::GetAnimated(bool* aAnimated)
 {
@@ -1023,16 +1028,20 @@ RasterImage::Discard()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(CanDiscard(), "Asked to discard but can't");
   MOZ_ASSERT(!mAnimationState, "Asked to discard for animated image");
 
   // Delete all the decoded frames.
   SurfaceCache::RemoveImage(ImageKey(this));
 
+  if (mAnimationState) {
+    mAnimationState->SetDiscarded(true);
+  }
+
   // Notify that we discarded.
   if (mProgressTracker) {
     mProgressTracker->OnDiscard();
   }
 }
 
 bool
 RasterImage::CanDiscard() {
@@ -1189,16 +1198,17 @@ RasterImage::Decode(const IntSize& aSize
     // If there's no transparency, it doesn't matter whether we premultiply
     // alpha or not.
     surfaceFlags &= ~SurfaceFlags::NO_PREMULTIPLY_ALPHA;
   }
 
   // Create a decoder.
   RefPtr<IDecodingTask> task;
   if (mAnimationState && aPlaybackType == PlaybackType::eAnimated) {
+    mAnimationState->SetDiscarded(false);
     task = DecoderFactory::CreateAnimationDecoder(mDecoderType, WrapNotNull(this),
                                                   mSourceBuffer, mSize,
                                                   decoderFlags, surfaceFlags);
   } else {
     task = DecoderFactory::CreateDecoder(mDecoderType, WrapNotNull(this),
                                          mSourceBuffer, mSize, aSize,
                                          decoderFlags, surfaceFlags);
   }
--- a/image/RasterImage.h
+++ b/image/RasterImage.h
@@ -159,17 +159,17 @@ public:
 #ifdef DEBUG
   NS_DECL_IMGICONTAINERDEBUG
 #endif
 
   virtual nsresult StartAnimation() override;
   virtual nsresult StopAnimation() override;
 
   // Methods inherited from Image
-  virtual void OnSurfaceDiscarded() override;
+  virtual void OnSurfaceDiscarded(const SurfaceKey& aSurfaceKey) override;
 
   virtual size_t SizeOfSourceWithComputedFallback(MallocSizeOf aMallocSizeOf)
     const override;
   virtual void CollectSizeOfSurfaces(nsTArray<SurfaceMemoryCounter>& aCounters,
                                      MallocSizeOf aMallocSizeOf) const override;
 
   /* Triggers discarding. */
   void Discard();
--- a/image/SurfaceCache.cpp
+++ b/image/SurfaceCache.cpp
@@ -497,17 +497,17 @@ public:
   {
     ImageKey imageKey = aSurface->GetImageKey();
 
     RefPtr<ImageSurfaceCache> cache = GetImageCache(imageKey);
     MOZ_ASSERT(cache, "Shouldn't try to remove a surface with no image cache");
 
     // If the surface was not a placeholder, tell its image that we discarded it.
     if (!aSurface->IsPlaceholder()) {
-      static_cast<Image*>(imageKey)->OnSurfaceDiscarded();
+      static_cast<Image*>(imageKey)->OnSurfaceDiscarded(aSurface->GetSurfaceKey());
     }
 
     StopTracking(aSurface);
     cache->Remove(aSurface);
 
     // Remove the per-image cache if it's unneeded now. (Keep it if the image is
     // locked, since the per-image cache is where we store that state.)
     if (cache->IsEmpty() && !cache->IsLocked()) {
--- a/image/VectorImage.cpp
+++ b/image/VectorImage.cpp
@@ -1118,17 +1118,17 @@ VectorImage::RequestDiscard()
     SurfaceCache::RemoveImage(ImageKey(this));
     mProgressTracker->OnDiscard();
   }
 
   return NS_OK;
 }
 
 void
-VectorImage::OnSurfaceDiscarded()
+VectorImage::OnSurfaceDiscarded(const SurfaceKey& aSurfaceKey)
 {
   MOZ_ASSERT(mProgressTracker);
 
   NS_DispatchToMainThread(NewRunnableMethod(mProgressTracker, &ProgressTracker::OnDiscard));
 }
 
 //******************************************************************************
 NS_IMETHODIMP
--- a/image/VectorImage.h
+++ b/image/VectorImage.h
@@ -44,17 +44,17 @@ public:
                                         nsIInputStream* aInStr,
                                         uint64_t aSourceOffset,
                                         uint32_t aCount) override;
   virtual nsresult OnImageDataComplete(nsIRequest* aRequest,
                                        nsISupports* aContext,
                                        nsresult aResult,
                                        bool aLastPart) override;
 
-  virtual void OnSurfaceDiscarded() override;
+  virtual void OnSurfaceDiscarded(const SurfaceKey& aSurfaceKey) override;
 
   /**
    * Callback for SVGRootRenderingObserver.
    *
    * This just sets a dirty flag that we check in VectorImage::RequestRefresh,
    * which is called under the ticks of the refresh driver of any observing
    * documents that we may have. Only then (after all animations in this image
    * have been updated) do we send out "frame changed" notifications,
--- a/ipc/glue/BackgroundParentImpl.cpp
+++ b/ipc/glue/BackgroundParentImpl.cpp
@@ -782,19 +782,26 @@ BackgroundParentImpl::AllocPFileSystemRe
   AssertIsOnBackgroundThread();
 
   RefPtr<FileSystemRequestParent> result = new FileSystemRequestParent();
 
   if (NS_WARN_IF(!result->Initialize(aParams))) {
     return nullptr;
   }
 
-  result->Start();
+  return result.forget().take();
+}
 
-  return result.forget().take();
+mozilla::ipc::IPCResult
+BackgroundParentImpl::RecvPFileSystemRequestConstructor(
+                                               PFileSystemRequestParent* aActor,
+                                               const FileSystemParams& params)
+{
+  static_cast<FileSystemRequestParent*>(aActor)->Start();
+  return IPC_OK();
 }
 
 bool
 BackgroundParentImpl::DeallocPFileSystemRequestParent(
                                               PFileSystemRequestParent* aDoomed)
 {
   AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
--- a/ipc/glue/BackgroundParentImpl.h
+++ b/ipc/glue/BackgroundParentImpl.h
@@ -188,16 +188,20 @@ protected:
   AllocPQuotaParent() override;
 
   virtual bool
   DeallocPQuotaParent(PQuotaParent* aActor) override;
 
   virtual PFileSystemRequestParent*
   AllocPFileSystemRequestParent(const FileSystemParams&) override;
 
+  virtual mozilla::ipc::IPCResult
+  RecvPFileSystemRequestConstructor(PFileSystemRequestParent* actor,
+                                    const FileSystemParams& params) override;
+
   virtual bool
   DeallocPFileSystemRequestParent(PFileSystemRequestParent*) override;
 
   // Gamepad API Background IPC
   virtual PGamepadEventChannelParent*
   AllocPGamepadEventChannelParent() override;
 
   virtual bool
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -5178,47 +5178,80 @@ nsFrame::ComputeSizeWithIntrinsicDimensi
   }
   nscoord boxSizingToMarginEdgeISize =
     aMargin.ISize(aWM) + aBorder.ISize(aWM) + aPadding.ISize(aWM) -
       boxSizingAdjust.ISize(aWM);
 
   nscoord iSize, minISize, maxISize, bSize, minBSize, maxBSize;
   enum class Stretch {
     // stretch to fill the CB (preserving intrinsic ratio) in the relevant axis
-    eStretchPreservingRatio,
+    eStretchPreservingRatio, // XXX not used yet
     // stretch to fill the CB in the relevant axis
     eStretch,
     // no stretching in the relevant axis
     eNoStretch,
   };
   // just to avoid having to type these out everywhere:
   const auto eStretchPreservingRatio = Stretch::eStretchPreservingRatio;
   const auto eStretch = Stretch::eStretch;
   const auto eNoStretch = Stretch::eNoStretch;
 
   Stretch stretchI = eNoStretch; // stretch behavior in the inline axis
   Stretch stretchB = eNoStretch; // stretch behavior in the block axis
 
+  const bool isVertical = aWM.IsVertical();
+  const nsStyleCoord& isizeCoord =
+    isVertical ? aIntrinsicSize.height : aIntrinsicSize.width;
+  const bool hasIntrinsicISize = isizeCoord.GetUnit() == eStyleUnit_Coord;
+  nscoord intrinsicISize;
+  if (hasIntrinsicISize) {
+    intrinsicISize = std::max(nscoord(0), isizeCoord.GetCoordValue());
+  } else {
+    NS_ASSERTION(isizeCoord.GetUnit() == eStyleUnit_None,
+                 "unexpected unit");
+    intrinsicISize = 0;
+  }
+
+  const nsStyleCoord& bsizeCoord =
+    isVertical ? aIntrinsicSize.width : aIntrinsicSize.height;
+  const bool hasIntrinsicBSize = bsizeCoord.GetUnit() == eStyleUnit_Coord;
+  nscoord intrinsicBSize;
+  if (hasIntrinsicBSize) {
+    intrinsicBSize = std::max(nscoord(0), bsizeCoord.GetCoordValue());
+  } else {
+    NS_ASSERTION(bsizeCoord.GetUnit() == eStyleUnit_None,
+                 "unexpected unit");
+    intrinsicBSize = 0;
+  }
+
+  NS_ASSERTION(aIntrinsicRatio.width >= 0 && aIntrinsicRatio.height >= 0,
+               "Intrinsic ratio has a negative component!");
+  LogicalSize logicalRatio(aWM, aIntrinsicRatio);
+
   if (!isAutoISize) {
     iSize = ComputeISizeValue(aRenderingContext,
               aCBSize.ISize(aWM), boxSizingAdjust.ISize(aWM),
               boxSizingToMarginEdgeISize, *inlineStyleCoord, aFlags);
   } else if (MOZ_UNLIKELY(isGridItem)) {
     MOZ_ASSERT(!IS_TRUE_OVERFLOW_CONTAINER(this));
     // 'auto' inline-size for grid-level box - apply 'stretch' as needed:
     auto cbSize = aCBSize.ISize(aWM);
     if (cbSize != NS_UNCONSTRAINEDSIZE) {
       if (!StyleMargin()->HasInlineAxisAuto(aWM)) {
         auto inlineAxisAlignment =
           aWM.IsOrthogonalTo(GetParent()->GetWritingMode()) ?
             stylePos->UsedAlignSelf(GetParent()->StyleContext()) :
             stylePos->UsedJustifySelf(GetParent()->StyleContext());
-        if (inlineAxisAlignment == NS_STYLE_ALIGN_NORMAL) {
-          stretchI = eStretchPreservingRatio;
-        } else if (inlineAxisAlignment == NS_STYLE_ALIGN_STRETCH) {
+        // Note: 'normal' means 'start' for elements with an intrinsic size
+        // or ratio in the relevant dimension, otherwise 'stretch'.
+        // https://drafts.csswg.org/css-grid/#grid-item-sizing
+        if ((inlineAxisAlignment == NS_STYLE_ALIGN_NORMAL &&
+             !hasIntrinsicISize &&
+             !(logicalRatio.ISize(aWM) > 0)) ||
+            inlineAxisAlignment == NS_STYLE_ALIGN_STRETCH) {
           stretchI = eStretch;
         }
       }
       if (stretchI != eNoStretch ||
           (aFlags & ComputeSizeFlags::eIClampMarginBoxMinSize)) {
         iSize = std::max(nscoord(0), cbSize -
                                      aPadding.ISize(aWM) -
                                      aBorder.ISize(aWM) -
@@ -5271,19 +5304,23 @@ nsFrame::ComputeSizeWithIntrinsicDimensi
     // 'auto' block-size for grid-level box - apply 'stretch' as needed:
     auto cbSize = aCBSize.BSize(aWM);
     if (cbSize != NS_AUTOHEIGHT) {
       if (!StyleMargin()->HasBlockAxisAuto(aWM)) {
         auto blockAxisAlignment =
           !aWM.IsOrthogonalTo(GetParent()->GetWritingMode()) ?
             stylePos->UsedAlignSelf(GetParent()->StyleContext()) :
             stylePos->UsedJustifySelf(GetParent()->StyleContext());
-        if (blockAxisAlignment == NS_STYLE_ALIGN_NORMAL) {
-          stretchB = eStretchPreservingRatio;
-        } else if (blockAxisAlignment == NS_STYLE_ALIGN_STRETCH) {
+        // Note: 'normal' means 'start' for elements with an intrinsic size
+        // or ratio in the relevant dimension, otherwise 'stretch'.
+        // https://drafts.csswg.org/css-grid/#grid-item-sizing
+        if ((blockAxisAlignment == NS_STYLE_ALIGN_NORMAL &&
+             !hasIntrinsicBSize &&
+             !(logicalRatio.BSize(aWM) > 0)) ||
+            blockAxisAlignment == NS_STYLE_ALIGN_STRETCH) {
           stretchB = eStretch;
         }
       }
       if (stretchB != eNoStretch ||
           (aFlags & ComputeSizeFlags::eBClampMarginBoxMinSize)) {
         bSize = std::max(nscoord(0), cbSize -
                                      aPadding.BSize(aWM) -
                                      aBorder.BSize(aWM) -
@@ -5311,58 +5348,19 @@ nsFrame::ComputeSizeWithIntrinsicDimensi
   if (!nsLayoutUtils::IsAutoBSize(minBSizeCoord, aCBSize.BSize(aWM)) &&
       !(isFlexItem && !isInlineFlexItem)) {
     minBSize = nsLayoutUtils::ComputeBSizeValue(aCBSize.BSize(aWM),
                   boxSizingAdjust.BSize(aWM), minBSizeCoord);
   } else {
     minBSize = 0;
   }
 
-  // Resolve percentage intrinsic iSize/bSize as necessary:
-
   NS_ASSERTION(aCBSize.ISize(aWM) != NS_UNCONSTRAINEDSIZE,
                "Our containing block must not have unconstrained inline-size!");
 
-  const bool isVertical = aWM.IsVertical();
-  const nsStyleCoord& isizeCoord =
-    isVertical ? aIntrinsicSize.height : aIntrinsicSize.width;
-  const nsStyleCoord& bsizeCoord =
-    isVertical ? aIntrinsicSize.width : aIntrinsicSize.height;
-
-  bool hasIntrinsicISize, hasIntrinsicBSize;
-  nscoord intrinsicISize, intrinsicBSize;
-
-  if (isizeCoord.GetUnit() == eStyleUnit_Coord) {
-    hasIntrinsicISize = true;
-    intrinsicISize = isizeCoord.GetCoordValue();
-    if (intrinsicISize < 0)
-      intrinsicISize = 0;
-  } else {
-    NS_ASSERTION(isizeCoord.GetUnit() == eStyleUnit_None,
-                 "unexpected unit");
-    hasIntrinsicISize = false;
-    intrinsicISize = 0;
-  }
-
-  if (bsizeCoord.GetUnit() == eStyleUnit_Coord) {
-    hasIntrinsicBSize = true;
-    intrinsicBSize = bsizeCoord.GetCoordValue();
-    if (intrinsicBSize < 0)
-      intrinsicBSize = 0;
-  } else {
-    NS_ASSERTION(bsizeCoord.GetUnit() == eStyleUnit_None,
-                 "unexpected unit");
-    hasIntrinsicBSize = false;
-    intrinsicBSize = 0;
-  }
-
-  NS_ASSERTION(aIntrinsicRatio.width >= 0 && aIntrinsicRatio.height >= 0,
-               "Intrinsic ratio has a negative component!");
-  LogicalSize logicalRatio(aWM, aIntrinsicRatio);
-
   // Now calculate the used values for iSize and bSize:
 
   if (isAutoISize) {
     if (isAutoBSize) {
 
       // 'auto' iSize, 'auto' bSize
 
       // Get tentative values - CSS 2.1 sections 10.3.2 and 10.6.2:
--- a/layout/generic/nsGridContainerFrame.cpp
+++ b/layout/generic/nsGridContainerFrame.cpp
@@ -4753,65 +4753,65 @@ nsGridContainerFrame::Tracks::StretchFle
   if (aState.mReflowInput) {
     auto* ri = aState.mReflowInput;
     minSize = mAxis == eLogicalAxisBlock ? ri->ComputedMinBSize()
                                          : ri->ComputedMinISize();
     maxSize = mAxis == eLogicalAxisBlock ? ri->ComputedMaxBSize()
                                          : ri->ComputedMaxISize();
   }
   Maybe<nsTArray<TrackSize>> origSizes;
+  bool applyMinMax = (minSize != 0 || maxSize != NS_UNCONSTRAINEDSIZE) &&
+                     aAvailableSize == NS_UNCONSTRAINEDSIZE;
   // We iterate twice at most.  The 2nd time if the grid size changed after
   // applying a min/max-size (can only occur if aAvailableSize is indefinite).
   while (true) {
     float fr = FindUsedFlexFraction(aState, aGridItems, flexTracks,
                                     aFunctions, aAvailableSize);
     if (fr != 0.0f) {
-      bool applyMinMax = (minSize != 0 || maxSize != NS_UNCONSTRAINEDSIZE) &&
-                         aAvailableSize == NS_UNCONSTRAINEDSIZE;
       for (uint32_t i : flexTracks) {
         float flexFactor = aFunctions.MaxSizingFor(i).GetFlexFractionValue();
         nscoord flexLength = NSToCoordRound(flexFactor * fr);
         nscoord& base = mSizes[i].mBase;
         if (flexLength > base) {
           if (applyMinMax && origSizes.isNothing()) {
             origSizes.emplace(mSizes);
           }
           base = flexLength;
         }
       }
-      if (applyMinMax && origSizes.isSome()) {
-        // https://drafts.csswg.org/css-grid/#algo-flex-tracks
-        // "If using this flex fraction would cause the grid to be smaller than
-        // the grid container’s min-width/height (or larger than the grid
-        // container’s max-width/height), then redo this step, treating the free
-        // space as definite [...]"
-        nscoord newSize = 0;
-        for (auto& sz : mSizes) {
-          newSize += sz.mBase;
-        }
-        const auto sumOfGridGaps = SumOfGridGaps();
-        newSize += sumOfGridGaps;
-        if (newSize > maxSize) {
-          aAvailableSize = maxSize;
-        } else if (newSize < minSize) {
-          aAvailableSize = minSize;
-        }
-        if (aAvailableSize != NS_UNCONSTRAINEDSIZE) {
-          // Reset min/max-size to ensure 'applyMinMax' becomes false next time.
-          minSize = 0;
-          maxSize = NS_UNCONSTRAINEDSIZE;
-          aAvailableSize = std::max(0, aAvailableSize - sumOfGridGaps);
-          // Restart with the original track sizes and definite aAvailableSize.
+    }
+    if (applyMinMax) {
+      applyMinMax = false;
+      // https://drafts.csswg.org/css-grid/#algo-flex-tracks
+      // "If using this flex fraction would cause the grid to be smaller than
+      // the grid container’s min-width/height (or larger than the grid
+      // container’s max-width/height), then redo this step, treating the free
+      // space as definite [...]"
+      nscoord newSize = 0;
+      for (auto& sz : mSizes) {
+        newSize += sz.mBase;
+      }
+      const auto sumOfGridGaps = SumOfGridGaps();
+      newSize += sumOfGridGaps;
+      if (newSize > maxSize) {
+        aAvailableSize = maxSize;
+      } else if (newSize < minSize) {
+        aAvailableSize = minSize;
+      }
+      if (aAvailableSize != NS_UNCONSTRAINEDSIZE) {
+        aAvailableSize = std::max(0, aAvailableSize - sumOfGridGaps);
+        // Restart with the original track sizes and definite aAvailableSize.
+        if (origSizes.isSome()) {
           mSizes = Move(*origSizes);
           origSizes.reset();
-          if (aAvailableSize == 0) {
-            break; // zero available size wouldn't change any sizes though...
-          }
-          continue;
+        } // else, no mSizes[].mBase were changed above so it's still correct
+        if (aAvailableSize == 0) {
+          break; // zero available size wouldn't change any sizes though...
         }
+        continue;
       }
     }
     break;
   }
 }
 
 void
 nsGridContainerFrame::Tracks::AlignJustifyContent(
--- a/layout/reftests/css-grid/grid-item-button-001-ref.html
+++ b/layout/reftests/css-grid/grid-item-button-001-ref.html
@@ -77,10 +77,13 @@ a30 {
 <button>AB</button><button>AB</button><button style="margin-left:15px">AB</button><button style="margin-left:20px">AB</button><button style="margin-left:5px">AB</button><button>AB</button><button style="margin-left:15px">AB</button><button style="margin-left:20px">AB</button><button style="margin-left:5px">AB</button><button style="margin-left:5px">AB</button><button style="margin-left:5px">AB</button><button style="margin-left:5px">AB</button></div>
 
 <div class="grid mw40" style="width:215px">
 <button>AB</button><button style="margin-left:-25px">AB</button><button style="margin-left:-35px">AB</button><button style="margin-left:-5px">AB</button><button style="margin-left:5px">AB</button><button style="margin-left:-25px">AB</button><button style="margin-left:-35px">AB</button><button style="margin-left:-5px">AB</button></div>
 
 <div class="grid rel" style="width:100px; height:100px; position:relative">
 <button style="top:0;width:40px;height:40px">AB</button><button style="top:0;right:0;height:40px">&nbsp;&nbsp;</button><button style="bottom:0;width:40px;">AB</button><button style="bottom:0;right:0">&nbsp;&nbsp;</button></div>
 
+<div class="grid rel" style="width:100px; height:100px; position:relative">
+<button style="top:0;width:40px;height:40px">AB</button><button style="top:0;right:0;height:40px">&nbsp;&nbsp;</button><button style="bottom:0;width:40px;">AB</button><button style="bottom:0;right:0">&nbsp;&nbsp;</button></div>
+
 </body>
 </html>
--- a/layout/reftests/css-grid/grid-item-button-001.html
+++ b/layout/reftests/css-grid/grid-item-button-001.html
@@ -144,10 +144,17 @@ button:nth-child(4n) { background: silve
 
 <div class="grid max40" style="grid:50px 50px/50px 50px">
 <button>AB</button>
 <button class="jend">&nbsp;&nbsp;</button>
 <button class="aend">AB</button>
 <button class="end">&nbsp;&nbsp;</button>
 </div>
 
+<div class="grid max40" style="grid:50px 50px/50px 50px; place-items:stretch">
+<button>AB</button>
+<button class="jend">&nbsp;&nbsp;</button>
+<button class="aend">AB</button>
+<button class="end">&nbsp;&nbsp;</button>
+</div>
+
 </body>
 </html>
--- a/layout/reftests/css-grid/grid-item-input-stretch-001-ref.html
+++ b/layout/reftests/css-grid/grid-item-input-stretch-001-ref.html
@@ -42,16 +42,17 @@ input {
 .mxw { width: 32px; }
 .mxh { height: 16px; }
 
 .vr { writing-mode: vertical-rl; }
   </style>
 </head>
 <body>
 
+<div class="grid"><div><input></div></div>
 <div class="grid"><input class="m" style="width:190px; height:20px"></div>
 <div class="grid"><input class="hma10 je" style="height:20px"></div>
 <div class="grid"><input class="hmaa jc" style="height:20px"></div>
 <div class="grid"><input class="vr hma10 je" style="height:20px"></div>
 <div class="grid"><input class="vr hmaa jc" style="height:20px"></div>
 <div class="grid"><input class="vr je" style="width:198px; height:28px"></div>
 
 <div class="grid"><input class="vma10 ae" style="width:190px"></div>
--- a/layout/reftests/css-grid/grid-item-input-stretch-001.html
+++ b/layout/reftests/css-grid/grid-item-input-stretch-001.html
@@ -11,16 +11,17 @@
   <link rel="match" href="grid-item-input-stretch-001-ref.html">
   <style type="text/css">
 * { color:black; background-color:white; font:10px/1 monospace; padding:0; margin:0; }
 * { vertical-align: top; }
 .grid {
   display: inline-grid;
   border: 1px solid;
   grid: 2px 30px 3px / 6px 200px 4px;
+  place-items: stretch;
 }
 
 input {
   border: 1px solid;
   padding: 0;
   margin: 0;
   background: lightgrey;
   grid-area: 2/2;
@@ -35,16 +36,17 @@ input {
 .mxw { max-width: 32px; }
 .mxh { max-height: 16px; }
 
 .vr { writing-mode: vertical-rl; }
   </style>
 </head>
 <body>
 
+<div class="grid" style="place-items:start"><input></div>
 <div class="grid"><input class="m"></div>
 <div class="grid"><input class="hma10"></div>
 <div class="grid"><input class="hmaa"></div>
 <div class="grid"><input class="vr hma10"></div>
 <div class="grid"><input class="vr hmaa"></div>
 <div class="grid"><input class="vr"></div>
 
 <div class="grid"><input class="vma10"></div>
--- a/layout/reftests/css-grid/grid-item-intrinsic-ratio-normal-001-ref.html
+++ b/layout/reftests/css-grid/grid-item-intrinsic-ratio-normal-001-ref.html
@@ -86,85 +86,85 @@ x { width:32px; height:2px; background:c
 
 <script>
 var url = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAEElEQVQoz2NgGAWjYBTAAAADEAABaJFtwwAAAABJRU5ErkJggg%3D%3D";
 var grids = document.querySelectorAll('.grid');
 var js = [ "normal", "start", "center", "stretch" ];
 var as = [ "normal", "start", "center", "stretch" ];
 var imgSizes =
 [
-  ['24px', '24px'],
-  ['32px', '32px'],
-  ['32px', '32px'],
-  ['24px', '24px'],
-  ['24px', '24px'],
+  ['16px', '16px'],
+  ['16px', '16px'],
+  ['16px', '16px'],
   ['16px', '24px'],
-  ['24px', '24px'],
+  ['16px', '16px'],
   ['16px', '24px'],
-  ['32px', '32px'],
+  ['16px', '16px'],
+  ['16px', '24px'],
+  ['32px', '16px'],
   ['32px', '16px'],
   ['32px', '16px'],
   ['32px', '24px'],
-  ['24px', '24px'],
-  ['24px', '24px'],
-  ['24px', '24px'],
-  ['32px', '32px'],
-  ['32px', '32px'],
+  ['16px', '16px'],
+  ['16px', '16px'],
+  ['16px', '16px'],
   ['16px', '32px'],
-  ['32px', '32px'],
+  ['16px', '16px'],
   ['16px', '32px'],
-  ['24px', '24px'],
+  ['16px', '16px'],
+  ['16px', '32px'],
+  ['24px', '16px'],
   ['24px', '16px'],
   ['24px', '16px'],
   ['24px', '32px'],
-  ['4px', '4px'],
-  ['4px', '4px'],
-  ['4px', '4px'],
-  ['8px', '8px'],
-  ['8px', '8px'],
+  ['16px', '16px'],
+  ['16px', '16px'],
+  ['16px', '16px'],
   ['16px', '8px'],
-  ['8px', '8px'],
+  ['16px', '16px'],
   ['16px', '8px'],
-  ['4px', '4px'],
+  ['16px', '16px'],
+  ['16px', '8px'],
+  ['4px', '16px'],
   ['4px', '16px'],
   ['4px', '16px'],
   ['4px', '8px'],
-  ['4px', '4px'],
-  ['4px', '4px'],
-  ['4px', '4px'],
-  ['32px', '32px'],
-  ['32px', '32px'],
+  ['16px', '16px'],
+  ['16px', '16px'],
+  ['16px', '16px'],
   ['16px', '32px'],
-  ['32px', '32px'],
+  ['16px', '16px'],
   ['16px', '32px'],
-  ['4px', '4px'],
+  ['16px', '16px'],
+  ['16px', '32px'],
+  ['4px', '16px'],
   ['4px', '16px'],
   ['4px', '16px'],
   ['4px', '32px'],
-  ['4px', '4px'],
-  ['8px', '8px'],
-  ['8px', '8px'],
-  ['4px', '4px'],
-  ['4px', '4px'],
+  ['16px', '16px'],
+  ['16px', '16px'],
+  ['16px', '16px'],
   ['16px', '4px'],
-  ['4px', '4px'],
+  ['16px', '16px'],
   ['16px', '4px'],
-  ['8px', '8px'],
+  ['16px', '16px'],
+  ['16px', '4px'],
+  ['8px', '16px'],
   ['8px', '16px'],
   ['8px', '16px'],
   ['8px', '4px'],
-  ['8px', '8px'],
-  ['32px', '32px'],
-  ['32px', '32px'],
-  ['8px', '8px'],
-  ['8px', '8px'],
+  ['16px', '16px'],
+  ['16px', '16px'],
+  ['16px', '16px'],
   ['16px', '8px'],
-  ['8px', '8px'],
+  ['16px', '16px'],
   ['16px', '8px'],
-  ['32px', '32px'],
+  ['16px', '16px'],
+  ['16px', '8px'],
+  ['32px', '16px'],
   ['32px', '16px'],
   ['32px', '16px'],
   ['32px', '8px'],
 ];
 var index = 0;  // imgSizes index
 for (var i = 0; i < grids.length; ++i) {
   for (var j = 0; j < js.length; j++) {
     for (var a = 0; a < as.length; a++) {
--- a/layout/reftests/css-grid/grid-item-intrinsic-ratio-normal-002-ref.html
+++ b/layout/reftests/css-grid/grid-item-intrinsic-ratio-normal-002-ref.html
@@ -82,37 +82,37 @@ x { width:32px; height:2px; background:c
 
 <script>
 var url = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAEElEQVQoz2NgGAWjYBTAAAADEAABaJFtwwAAAABJRU5ErkJggg%3D%3D";
 var grids = document.querySelectorAll('.grid');
 var js = [ "normal", "start", "center", "stretch" ];
 var as = [ "normal", "start", "center", "stretch" ];
 var imgSizes =
 [
-  ['24px', '24px'],
-  ['32px', '32px'],
-  ['32px', '32px'],
-  ['24px', '24px'],
-  ['24px', '24px'],
+  ['16px', '16px'],
+  ['16px', '16px'],
+  ['16px', '16px'],
   ['16px', '24px'],
-  ['24px', '24px'],
+  ['16px', '16px'],
   ['16px', '24px'],
-  ['32px', '32px'],
+  ['16px', '16px'],
+  ['16px', '24px'],
+  ['32px', '16px'],
   ['32px', '16px'],
   ['32px', '16px'],
   ['32px', '24px'],
-  ['24px', '24px'],
-  ['24px', '24px'],
-  ['24px', '24px'],
-  ['32px', '32px'],
-  ['32px', '32px'],
+  ['16px', '16px'],
+  ['16px', '16px'],
+  ['16px', '16px'],
   ['16px', '32px'],
-  ['32px', '32px'],
+  ['16px', '16px'],
   ['16px', '32px'],
-  ['24px', '24px'],
+  ['16px', '16px'],
+  ['16px', '32px'],
+  ['24px', '16px'],
   ['24px', '16px'],
   ['24px', '16px'],
   ['24px', '32px'],
   ['4px', '4px'],
   ['4px', '4px'],
   ['4px', '4px'],
   ['4px', '8px'],
   ['4px', '4px'],
@@ -126,17 +126,17 @@ var imgSizes =
   ['4px', '4px'],
   ['4px', '4px'],
   ['4px', '4px'],
   ['4px', '32px'],
   ['4px', '4px'],
   ['4px', '32px'],
   ['4px', '4px'],
   ['4px', '32px'],
-  ['4px', '4px'],
+  ['4px', '16px'],
   ['4px', '16px'],
   ['4px', '16px'],
   ['4px', '32px'],
   ['4px', '4px'],
   ['4px', '4px'],
   ['4px', '4px'],
   ['8px', '4px'],
   ['4px', '4px'],
@@ -145,17 +145,17 @@ var imgSizes =
   ['8px', '4px'],
   ['8px', '4px'],
   ['8px', '4px'],
   ['8px', '4px'],
   ['8px', '4px'],
   ['8px', '8px'],
   ['8px', '8px'],
   ['8px', '8px'],
-  ['8px', '8px'],
+  ['16px', '8px'],
   ['8px', '8px'],
   ['16px', '8px'],
   ['8px', '8px'],
   ['16px', '8px'],
   ['32px', '8px'],
   ['32px', '8px'],
   ['32px', '8px'],
   ['32px', '8px'],
--- a/layout/reftests/css-grid/grid-item-intrinsic-ratio-normal-003-ref.html
+++ b/layout/reftests/css-grid/grid-item-intrinsic-ratio-normal-003-ref.html
@@ -135,62 +135,62 @@
 <div class="grid"><img class="as h20 mxw2"></div>
 <div class="grid"><img class="an h20"></div>
 <div class="grid"><img class="an h20 mxw10"></div>
 
 <script>
 var url = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAEElEQVQoz2NgGAWjYBTAAAADEAABaJFtwwAAAABJRU5ErkJggg%3D%3D";
 var imgs = document.querySelectorAll('img');
 var imgSizes =
-  [
+[
   ['4px', '4px'],
   ['4px', '4px'],
   ['16px', '4px'],
   ['10px', '4px'],
   ['4px', '4px'],
   ['2px', '2px'],
   ['20px', '20px'],
   ['10px', '20px'],
   ['20px', '20px'],
   ['10px', '20px'],
   ['16px', '20px'],
   ['10px', '20px'],
   ['20px', '20px'],
   ['10px', '20px'],
-  ['4px', '4px'],
-  ['4px', '4px'],
+  ['16px', '16px'],
+  ['10px', '10px'],
   ['16px', '16px'],
   ['10px', '10px'],
   ['16px', '4px'],
   ['10px', '4px'],
-  ['4px', '4px'],
+  ['16px', '16px'],
   ['2px', '2px'],
   ['4px', '4px'],
   ['2px', '2px'],
   ['4px', '4px'],
   ['2px', '2px'],
   ['4px', '32px'],
   ['2px', '32px'],
   ['4px', '4px'],
   ['2px', '2px'],
   ['20px', '20px'],
   ['20px', '10px'],
   ['20px', '20px'],
   ['20px', '10px'],
   ['20px', '32px'],
   ['20px', '10px'],
-  ['32px', '32px'],
+  ['20px', '20px'],
   ['20px', '10px'],
-  ['4px', '4px'],
-  ['4px', '4px'],
+  ['16px', '16px'],
+  ['10px', '10px'],
   ['16px', '16px'],
   ['10px', '10px'],
   ['16px', '32px'],
   ['16px', '10px'],
-  ['32px', '32px'],
+  ['16px', '16px'],
   ['10px', '10px'],
   ['20px', '20px'],
   ['20px', '10px'],
   ['20px', '20px'],
   ['20px', '10px'],
   ['20px', '4px'],
   ['20px', '2px'],
   ['20px', '20px'],
--- a/layout/reftests/css-grid/grid-item-intrinsic-ratio-normal-003.html
+++ b/layout/reftests/css-grid/grid-item-intrinsic-ratio-normal-003.html
@@ -152,35 +152,23 @@ button {
 <script>
 var url = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAEElEQVQoz2NgGAWjYBTAAAADEAABaJFtwwAAAABJRU5ErkJggg%3D%3D";
 var imgs = document.querySelectorAll('img');
 for (var i = 0; i < imgs.length; ++i) {
   imgs[i].src = url;
 }
 </script>
 
+<!--  For generating button size results in -ref file
 <script>
 document.body.clientHeight;
 var imgs = document.querySelectorAll('img');
 var s = '  [\n';
 for (var i = 0; i < imgs.length; ++i) {
   s += "  ['"+ imgs[i].width + "px', '" + imgs[i].height + "px'],\n";
 }
 s += ']';
 console.log(s)
 </script>
-
-<!--  For generating button size results in -ref file
-<script>
-document.body.clientHeight;
-var buttons = document.querySelectorAll('button');
-var s = '  [\n';
-for (var i = 0; i < buttons.length; ++i) {
-  var cs = window.getComputedStyle(buttons[i]);
-  s += "  ['"+ cs['width'] + "', '" + cs['height'] + "'],\n";
-}
-s += ']';
-console.log(s)
-</script>
 -->
 
 </body>
 </html>
--- a/layout/reftests/css-grid/grid-item-intrinsic-ratio-stretch-001-ref.html
+++ b/layout/reftests/css-grid/grid-item-intrinsic-ratio-stretch-001-ref.html
@@ -1,143 +1,143 @@
 <!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: stretching intrinsic ratio item with min/max-size:0</title>
+  <title>Reference: stretching intrinsic ratio item with min-size:0</title>
   <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1176775">
   <style type="text/css">
 body,html { color:black; background:white; font:16px/1 monospace; padding:0; margin:0; }
 .grid {
   display: inline-grid;
   border: 1px solid;
   margin: 5px;
   align-items: start;
   justify-items: start;
 }
 .vertical-tests div { vertical-align:bottom }
   </style>
 </head>
 <body>
 
 <div class="grid" style="grid: 96px / 20px">
-<img src="support/lime-2x24.png" style="height:96px; width:8px">
+<img src="support/lime-2x24.png" style="height:96px; width:20px">
 </div>
 <div class="grid" style="grid: 96px / 4px">
-<img src="support/lime-2x24.png" style="height:48px; width:4px">
+<img src="support/lime-2x24.png" style="height:96px; width:4px">
 </div>
 
 <div class="grid" style="grid: 8px / 20px">
-<img src="support/lime-24x2.png" style="width:20px; height:calc(2px * (20 / 24))">
+<img src="support/lime-24x2.png" style="width:20px; height:8px">
 </div>
 <div class="grid" style="grid: 8px / 100px">
-<img src="support/lime-24x2.png" style="width:96px; height:8px">
+<img src="support/lime-24x2.png" style="width:100px; height:8px">
 </div>
 
 <div class="grid" style="grid: 96px / 10px">
-<img src="support/lime-2x24.png" style="align-self:start; height:120px; width:10px">
+<img src="support/lime-2x24.png" style="align-self:start; height:24px; width:10px">
 </div>
 <div class="grid" style="grid: 96px / 4px">
-<img src="support/lime-2x24.png" style="align-self:start; height:48px; width:4px">
+<img src="support/lime-2x24.png" style="align-self:start; height:24px; width:4px">
 </div>
 
 <div class="grid" style="grid: 8px / 100px">
-<img src="support/lime-24x2.png" style="justify-self:start; width:96px; height:8px">
+<img src="support/lime-24x2.png" style="justify-self:start; width:24px; height:8px">
 </div>
 <div class="grid" style="grid: 8px / 10px">
-<img src="support/lime-24x2.png" style="justify-self:start; width:96px; height:8px">
+<img src="support/lime-24x2.png" style="justify-self:start; width:24px; height:8px">
 </div>
 
 <br>
 
 <div class="grid" style="grid: 96px / 10px">
-<img src="support/lime-2x24.png" style="align-self:start unsafe; height:120px; width:10px">
+<img src="support/lime-2x24.png" style="align-self:start unsafe; height:24px; width:10px">
 </div>
 <div class="grid" style="grid: 96px / 10px">
-<img src="support/lime-2x24.png" style="align-self:start safe; height:120px; width:10px">
+<img src="support/lime-2x24.png" style="align-self:start safe; height:24px; width:10px">
 </div>
 <div class="grid" style="grid: 96px / 10px">
-<img src="support/lime-2x24.png" style="align-self:end unsafe; height:120px; width:10px; margin-bottom:-24px">
+<img src="support/lime-2x24.png" style="align-self:end unsafe; height:24px; width:10px">
 </div>
 <div class="grid" style="grid: 96px / 10px">
-<img src="support/lime-2x24.png" style="align-self:end safe; height:120px; width:10px; margin-top:-24px">
+<img src="support/lime-2x24.png" style="align-self:end safe; height:24px; width:10px">
 </div>
 
 <div class="grid" style="grid: 4px / 10px">
-<img src="support/lime-24x2.png" style="justify-self:start unsafe; width:48px; height:4px">
+<img src="support/lime-24x2.png" style="justify-self:start unsafe; width:24px; height:4px">
 </div>
 <div class="grid" style="grid: 4px / 10px; margin-left: 40px">
-<img src="support/lime-24x2.png" style="justify-self:start safe; width:48px; height:4px">
+<img src="support/lime-24x2.png" style="justify-self:start safe; width:24px; height:4px">
 </div>
 <div class="grid" style="grid: 4px / 10px; margin-left: 40px">
-<img src="support/lime-24x2.png" style="justify-self:end unsafe; width:48px; height:4px; margin-right:-38px">
+<img src="support/lime-24x2.png" style="justify-self:end unsafe; width:24px; height:4px; margin-right:-14px">
 </div>
 <div class="grid" style="grid: 4px / 10px; margin-left: 80px">
-<img src="support/lime-24x2.png" style="justify-self:end safe; width:48px; height:4px; margin-left:-38px">
+<img src="support/lime-24x2.png" style="justify-self:end safe; width:24px; height:4px; margin-left:-38px">
 </div>
 
 <br>
 
 <div class="vertical-tests">
 
 <div class="grid" style="grid: 96px / 20px">
-<img src="support/lime-2x24.png" style="height:96px; width:8px">
+<img src="support/lime-2x24.png" style="height:96px; width:20px">
 </div>
 <div class="grid" style="grid: 96px / 4px">
-<img src="support/lime-2x24.png" style="height:48px; width:4px">
+<img src="support/lime-2x24.png" style="height:96px; width:4px">
 </div>
 
 <div class="grid" style="grid: 8px / 20px">
-<img src="support/lime-24x2.png" style="width:20px; height:calc(2px * (20 / 24))">
+<img src="support/lime-24x2.png" style="width:20px; height:8px">
 </div>
 <div class="grid" style="grid: 8px / 100px">
-<img src="support/lime-24x2.png" style="width:96px; height:8px">
+<img src="support/lime-24x2.png" style="width:100px; height:8px">
 </div>
 
 <div class="grid" style="grid: 96px / 10px">
-<img src="support/lime-2x24.png" style="align-self:start; height:120px; width:10px">
+<img src="support/lime-2x24.png" style="align-self:start; height:24px; width:10px">
 </div>
 <div class="grid" style="grid: 96px / 4px">
-<img src="support/lime-2x24.png" style="align-self:start; height:48px; width:4px">
+<img src="support/lime-2x24.png" style="align-self:start; height:24px; width:4px">
 </div>
 
 <div class="grid" style="grid: 8px / 100px">
-<img src="support/lime-24x2.png" style="justify-self:start; width:96px; height:8px">
+<img src="support/lime-24x2.png" style="justify-self:start; width:24px; height:8px">
 </div>
 <div class="grid" style="grid: 8px / 10px">
-<img src="support/lime-24x2.png" style="justify-self:start; width:96px; height:8px">
+<img src="support/lime-24x2.png" style="justify-self:start; width:24px; height:8px">
 </div>
 
 <br>
 
 <div class="grid" style="grid: 96px / 10px">
-<img src="support/lime-2x24.png" style="align-self:start unsafe; height:120px; width:10px">
+<img src="support/lime-2x24.png" style="align-self:start unsafe; height:24px; width:10px">
 </div>
 <div class="grid" style="grid: 96px / 10px">
-<img src="support/lime-2x24.png" style="align-self:start safe; height:120px; width:10px">
+<img src="support/lime-2x24.png" style="align-self:start safe; height:24px; width:10px">
 </div>
 <div class="grid" style="grid: 96px / 10px">
-<img src="support/lime-2x24.png" style="align-self:end unsafe; height:120px; width:10px; margin-bottom:-24px">
+<img src="support/lime-2x24.png" style="align-self:end unsafe; height:24px; width:10px">
 </div>
 <div class="grid" style="grid: 96px / 10px">
-<img src="support/lime-2x24.png" style="align-self:end safe; height:120px; width:10px; margin-top:-24px">
+<img src="support/lime-2x24.png" style="align-self:end safe; height:24px; width:10px">
 </div>
 
 <div class="grid" style="grid: 4px / 10px">
-<img src="support/lime-24x2.png" style="justify-self:start unsafe; width:48px; height:4px">
+<img src="support/lime-24x2.png" style="justify-self:start unsafe; width:24px; height:4px">
 </div>
 <div class="grid" style="grid: 4px / 10px; margin-left: 40px">
-<img src="support/lime-24x2.png" style="justify-self:start safe; width:48px; height:4px">
+<img src="support/lime-24x2.png" style="justify-self:start safe; width:24px; height:4px">
 </div>
 <div class="grid" style="grid: 4px / 10px; margin-left: 40px">
-<img src="support/lime-24x2.png" style="justify-self:end unsafe; width:48px; height:4px; margin-right:-38px">
+<img src="support/lime-24x2.png" style="justify-self:end unsafe; width:24px; height:4px; margin-right:-14px">
 </div>
 <div class="grid" style="grid: 4px / 10px; margin-left: 80px">
-<img src="support/lime-24x2.png" style="justify-self:end safe; width:48px; height:4px; margin-left:-38px">
+<img src="support/lime-24x2.png" style="justify-self:end safe; width:24px; height:4px; margin-left:-38px">
 </div>
 
 </div>
 
 </body>
 </html>
--- a/layout/reftests/css-grid/grid-item-intrinsic-ratio-stretch-001.html
+++ b/layout/reftests/css-grid/grid-item-intrinsic-ratio-stretch-001.html
@@ -1,25 +1,26 @@
 <!DOCTYPE HTML>
 <!--
      Any copyright is dedicated to the Public Domain.
      http://creativecommons.org/publicdomain/zero/1.0/
 -->
 <html><head>
   <meta charset="utf-8">
-  <title>CSS Grid Test: stretching intrinsic ratio item with min/max-size:0</title>
+  <title>CSS Grid Test: stretching intrinsic ratio item with min-size:0</title>
   <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1176775">
   <link rel="help" href="https://drafts.csswg.org/css-align-3/#valdef-justify-self-stretch">
   <link rel="match" href="grid-item-intrinsic-ratio-stretch-001-ref.html">
   <style type="text/css">
 body,html { color:black; background:white; font:16px/1 monospace; padding:0; margin:0; }
 .grid {
   display: inline-grid;
   border: 1px solid;
   margin: 5px;
+  place-items: stretch;
 }
 .grid > * {
   min-width: 0;
   min-height: 0;
 }
 .vertical-tests img { writing-mode: vertical-rl; }
 .vertical-tests div { vertical-align:bottom }
   </style>
--- a/layout/reftests/css-grid/grid-item-intrinsic-ratio-stretch-002-ref.html
+++ b/layout/reftests/css-grid/grid-item-intrinsic-ratio-stretch-002-ref.html
@@ -17,131 +17,131 @@ body,html { color:black; background:whit
   place-items: start start;
 }
 .vertical-tests div { vertical-align:bottom }
   </style>
 </head>
 <body>
 
 <div class="grid" style="grid: 96px / 20px">
-<img src="support/lime-2x24.png" style="width:4px; height:48px">
+<img src="support/lime-2x24.png" style="width:4px; height:96px">
 </div>
 <div class="grid" style="grid: 96px / 4px">
-<img src="support/lime-2x24.png" style="height:12px; width:1px">
+<img src="support/lime-2x24.png" style="height:12px; width:4px">
 </div>
 
 <div class="grid" style="grid: 8px / 20px">
-<img src="support/lime-24x2.png" style="height:1px; width:12px">
+<img src="support/lime-24x2.png" style="height:8px; width:12px">
 </div>
 <div class="grid" style="grid: 8px / 100px">
-<img src="support/lime-24x2.png" style="height:6px; width:72px">
+<img src="support/lime-24x2.png" style="height:6px; width:100px">
 </div>
 
 <div class="grid" style="grid: 96px / 20px">
-<img src="support/lime-2x24.png" style="align-self:start; height:48px; width:4px">
+<img src="support/lime-2x24.png" style="align-self:start; height:24px; width:20px">
 </div>
 <div class="grid" style="grid: 96px / 4px">
-<img src="support/lime-2x24.png" style="align-self:start; height:12px; width:1px">
+<img src="support/lime-2x24.png" style="align-self:start; height:12px; width:4px">
 </div>
 
 <div class="grid" style="grid: 8px / 100px">
-<img src="support/lime-24x2.png" style="justify-self:start; width:48px; height:4px">
+<img src="support/lime-24x2.png" style="justify-self:start; width:24px; height:8px">
 </div>
 <div class="grid" style="grid: 8px / 10px">
-<img src="support/lime-24x2.png" style="justify-self:start; width:48px; height:4px">
+<img src="support/lime-24x2.png" style="justify-self:start; width:24px; height:8px">
 </div>
 
 <br>
 
 <div class="grid" style="grid: 96px / 20px">
-<img src="support/lime-2x24.png" style="width:10px; height:120px">
+<img src="support/lime-2x24.png" style="width:20px; height:96px">
 </div>
 <div class="grid" style="grid: 96px / 4px">
-<img src="support/lime-2x24.png" style="width:6px; height:72px">
+<img src="support/lime-2x24.png" style="width:6px; height:96px">
 </div>
 
 <div class="grid" style="grid: 8px / 20px">
-<img src="support/lime-24x2.png" style="width:24px; height:2px">
+<img src="support/lime-24x2.png" style="width:20px; height:8px">
 </div>
 <div class="grid" style="grid: 8px / 100px">
-<img src="support/lime-24x2.png" style="width:120px; height:10px">
+<img src="support/lime-24x2.png" style="width:100px; height:10px">
 </div>
 
 <div class="grid" style="grid: 48px / 6px">
-<img src="support/lime-2x24.png" style="align-self:start; width:calc(2px * (80 / 24)); height:80px">
+<img src="support/lime-2x24.png" style="align-self:start; width:6px; height:80px">
 </div>
 <div class="grid" style="grid: 96px / 4px">
-<img src="support/lime-2x24.png" style="align-self:start; width:6px; height:72px">
+<img src="support/lime-2x24.png" style="align-self:start; width:4px; height:72px">
 </div>
 
 <div class="grid" style="grid: 8px / 100px">
 <img src="support/lime-24x2.png" style="justify-self:start; width:98px; height:calc(2px * (98 / 24))">
 </div>
 <div class="grid" style="grid: 4px / 10px">
-<img src="support/lime-24x2.png" style="justify-self:start; width:72px; height:6px">
+<img src="support/lime-24x2.png" style="justify-self:start; width:72px; height:4px">
 </div>
 
 <br>
 
 <div class="vertical-tests">
 
 <div class="grid" style="grid: 96px / 20px">
-<img src="support/lime-2x24.png" style="width:4px; height:48px">
+<img src="support/lime-2x24.png" style="width:4px; height:96px">
 </div>
 <div class="grid" style="grid: 96px / 4px">
-<img src="support/lime-2x24.png" style="height:12px; width:1px">
+<img src="support/lime-2x24.png" style="height:12px; width:4px">
 </div>
 
 <div class="grid" style="grid: 8px / 20px">
-<img src="support/lime-24x2.png" style="height:1px; width:12px">
+<img src="support/lime-24x2.png" style="height:8px; width:12px">
 </div>
 <div class="grid" style="grid: 8px / 100px">
-<img src="support/lime-24x2.png" style="height:6px; width:72px">
+<img src="support/lime-24x2.png" style="height:6px; width:100px">
 </div>
 
 <div class="grid" style="grid: 96px / 20px">
-<img src="support/lime-2x24.png" style="align-self:start; height:48px; width:4px">
+<img src="support/lime-2x24.png" style="align-self:start; height:24px; width:20px">
 </div>
 <div class="grid" style="grid: 96px / 4px">
-<img src="support/lime-2x24.png" style="align-self:start; height:12px; width:1px">
+<img src="support/lime-2x24.png" style="align-self:start; height:12px; width:4px">
 </div>
 
 <div class="grid" style="grid: 8px / 100px">
-<img src="support/lime-24x2.png" style="justify-self:start; width:48px; height:4px">
+<img src="support/lime-24x2.png" style="justify-self:start; width:24px; height:8px">
 </div>
 <div class="grid" style="grid: 8px / 10px">
-<img src="support/lime-24x2.png" style="justify-self:start; width:48px; height:4px">
+<img src="support/lime-24x2.png" style="justify-self:start; width:24px; height:8px">
 </div>
 
 <br>
 
 <div class="grid" style="grid: 96px / 20px">
-<img src="support/lime-2x24.png" style="width:10px; height:120px">
+<img src="support/lime-2x24.png" style="width:20px; height:96px">
 </div>
 <div class="grid" style="grid: 96px / 4px">
-<img src="support/lime-2x24.png" style="width:6px; height:72px">
+<img src="support/lime-2x24.png" style="width:6px; height:96px">
 </div>
 
 <div class="grid" style="grid: 8px / 20px">
-<img src="support/lime-24x2.png" style="width:24px; height:2px">
+<img src="support/lime-24x2.png" style="width:20px; height:8px">
 </div>
 <div class="grid" style="grid: 8px / 100px">
-<img src="support/lime-24x2.png" style="width:120px; height:10px">
+<img src="support/lime-24x2.png" style="width:100px; height:10px">
 </div>
 
 <div class="grid" style="grid: 48px / 6px">
-<img src="support/lime-2x24.png" style="align-self:start; width:calc(2px * (80 / 24)); height:80px">
+<img src="support/lime-2x24.png" style="align-self:start; width:6px; height:80px">
 </div>
 <div class="grid" style="grid: 96px / 4px">
-<img src="support/lime-2x24.png" style="align-self:start; width:6px; height:72px">
+<img src="support/lime-2x24.png" style="align-self:start; width:4px; height:72px">
 </div>
 
 <div class="grid" style="grid: 8px / 100px">
 <img src="support/lime-24x2.png" style="justify-self:start; width:98px; height:calc(2px * (98 / 24))">
 </div>
 <div class="grid" style="grid: 4px / 10px">
-<img src="support/lime-24x2.png" style="justify-self:start; width:72px; height:6px">
+<img src="support/lime-24x2.png" style="justify-self:start; width:72px; height:4px">
 </div>
 
 </div>
 
 </body>
 </html>
--- a/layout/reftests/css-grid/grid-item-intrinsic-ratio-stretch-002.html
+++ b/layout/reftests/css-grid/grid-item-intrinsic-ratio-stretch-002.html
@@ -11,16 +11,17 @@
   <link rel="match" href="grid-item-intrinsic-ratio-stretch-002-ref.html">
   <style type="text/css">
 body,html { color:black; background:white; font:16px/1 monospace; padding:0; margin:0; }
 
 .grid {
   display: inline-grid;
   border: 1px solid;
   margin: 5px;
+  place-items: stretch;
 }
 .grid > * {
   min-width: 0;
   min-height: 0;
 }
 .vertical-tests img { writing-mode: vertical-rl; }
 .vertical-tests div { vertical-align:bottom }
   </style>
--- a/layout/reftests/css-grid/grid-item-intrinsic-ratio-stretch-004-ref.html
+++ b/layout/reftests/css-grid/grid-item-intrinsic-ratio-stretch-004-ref.html
@@ -17,127 +17,127 @@ body,html { color:black; background:whit
   justify-items: start;
 }
 .vertical-tests div { vertical-align:bottom }
   </style>
 </head>
 <body>
 
 <div class="grid" style="grid: 96px / 20px">
-<img src="support/lime-2x24.png" style="height:96px; width:8px">
+<img src="support/lime-2x24.png" style="height:96px; width:20px">
 </div>
 <div class="grid" style="grid: 96px / 4px">
-<img src="support/lime-2x24.png" style="height:48px; width:4px">
+<img src="support/lime-2x24.png" style="height:96px; width:4px">
 </div>
 
 <div class="grid" style="grid: 8px / 20px">
-<img src="support/lime-24x2.png" style="width:20px; height:calc(2px * (20 / 24))">
+<img src="support/lime-24x2.png" style="width:20px; height:8px">
 </div>
 <div class="grid" style="grid: 8px / 100px">
-<img src="support/lime-24x2.png" style="width:96px; height:8px">
+<img src="support/lime-24x2.png" style="width:100px; height:8px">
 </div>
 
 <div class="grid" style="grid: 96px / 10px">
-<img src="support/lime-2x24.png" style="align-self:start; height:120px; width:10px">
+<img src="support/lime-2x24.png" style="align-self:start; height:24px; width:10px">
 </div>
 <div class="grid" style="grid: 96px / 4px">
-<img src="support/lime-2x24.png" style="align-self:start; height:48px; width:4px">
+<img src="support/lime-2x24.png" style="align-self:start; height:24px; width:4px">
 </div>
 
 <div class="grid" style="grid: 8px / 100px">
-<img src="support/lime-24x2.png" style="justify-self:start; width:96px; height:8px">
+<img src="support/lime-24x2.png" style="justify-self:start; width:24px; height:8px">
 </div>
 <div class="grid" style="grid: 8px / 10px">
-<img src="support/lime-24x2.png" style="justify-self:start; width:10px; height:1px">
+<img src="support/lime-24x2.png" style="justify-self:start; width:10px; height:8px">
 </div>
 
 <br>
 
 <div class="grid" style="grid: 96px / 10px">
-<img src="support/lime-2x24.png" style="align-self:start unsafe; height:120px; width:10px">
+<img src="support/lime-2x24.png" style="align-self:start unsafe; height:24px; width:10px">
 </div>
 <div class="grid" style="grid: 96px / 10px">
-<img src="support/lime-2x24.png" style="align-self:start safe; height:120px; width:10px">
+<img src="support/lime-2x24.png" style="align-self:start safe; height:24px; width:10px">
 </div>
 <div class="grid" style="grid: 96px / 10px">
-<img src="support/lime-2x24.png" style="align-self:end unsafe; height:120px; width:10px; margin-bottom:-24px">
+<img src="support/lime-2x24.png" style="align-self:end unsafe; height:24px; width:10px">
 </div>
 <div class="grid" style="grid: 96px / 10px">
-<img src="support/lime-2x24.png" style="align-self:end safe; height:120px; width:10px; margin-top:-24px">
+<img src="support/lime-2x24.png" style="align-self:end safe; height:24px; width:10px">
 </div>
 
 <div class="grid" style="grid: 4px / 10px">
-<img src="support/lime-24x2.png" style="justify-self:start unsafe; width:10px; height:1px">
+<img src="support/lime-24x2.png" style="justify-self:start unsafe; width:10px; height:4px">
 </div>
 <div class="grid" style="grid: 4px / 10px; margin-left: 40px">
-<img src="support/lime-24x2.png" style="justify-self:start safe; width:10px; height:1px">
+<img src="support/lime-24x2.png" style="justify-self:start safe; width:10px; height:4px">
 </div>
 <div class="grid" style="grid: 4px / 10px; margin-left: 40px">
-<img src="support/lime-24x2.png" style="justify-self:end unsafe; width:10px; height:1px">
+<img src="support/lime-24x2.png" style="justify-self:end unsafe; width:10px; height:4px">
 </div>
 <div class="grid" style="grid: 4px / 10px; margin-left: 80px">
-<img src="support/lime-24x2.png" style="justify-self:end safe; width:10px; height:1px">
+<img src="support/lime-24x2.png" style="justify-self:end safe; width:10px; height:4px">
 </div>
 
 <br>
 
 <div class="vertical-tests">
 
 <div class="grid" style="grid: 96px / 20px">
-<img src="support/lime-2x24.png" style="height:96px; width:8px">
+<img src="support/lime-2x24.png" style="height:96px; width:20px">
 </div>
 <div class="grid" style="grid: 96px / 4px">
-<img src="support/lime-2x24.png" style="height:48px; width:4px">
+<img src="support/lime-2x24.png" style="height:96px; width:4px">
 </div>
 
 <div class="grid" style="grid: 8px / 20px">
-<img src="support/lime-24x2.png" style="width:20px; height:calc(2px * (20 / 24))">
+<img src="support/lime-24x2.png" style="width:20px; height:8px">
 </div>
 <div class="grid" style="grid: 8px / 100px">
-<img src="support/lime-24x2.png" style="width:96px; height:8px">
+<img src="support/lime-24x2.png" style="width:100px; height:8px">
 </div>
 
 <div class="grid" style="grid: 96px / 10px">
-<img src="support/lime-2x24.png" style="align-self:start; height:120px; width:10px">
+<img src="support/lime-2x24.png" style="align-self:start; height:24px; width:10px">
 </div>
 <div class="grid" style="grid: 96px / 4px">
-<img src="support/lime-2x24.png" style="align-self:start; height:48px; width:4px">
+<img src="support/lime-2x24.png" style="align-self:start; height:24px; width:4px">
 </div>
 
 <div class="grid" style="grid: 8px / 100px">
-<img src="support/lime-24x2.png" style="justify-self:start; width:96px; height:8px">
+<img src="support/lime-24x2.png" style="justify-self:start; width:24px; height:8px">
 </div>
 <div class="grid" style="grid: 8px / 10px">
-<img src="support/lime-24x2.png" style="justify-self:start; width:10px; height:1px">
+<img src="support/lime-24x2.png" style="justify-self:start; width:10px; height:8px">
 </div>
 
 <br>
 
 <div class="grid" style="grid: 96px / 10px">
-<img src="support/lime-2x24.png" style="align-self:start unsafe; height:120px; width:10px">
+<img src="support/lime-2x24.png" style="align-self:start unsafe; height:24px; width:10px">
 </div>
 <div class="grid" style="grid: 96px / 10px">
-<img src="support/lime-2x24.png" style="align-self:start safe; height:120px; width:10px">
+<img src="support/lime-2x24.png" style="align-self:start safe; height:24px; width:10px">
 </div>
 <div class="grid" style="grid: 96px / 10px">
-<img src="support/lime-2x24.png" style="align-self:end unsafe; height:120px; width:10px; margin-bottom:-24px">
+<img src="support/lime-2x24.png" style="align-self:end unsafe; height:24px; width:10px">
 </div>
 <div class="grid" style="grid: 96px / 10px">
-<img src="support/lime-2x24.png" style="align-self:end safe; height:120px; width:10px; margin-top:-24px">
+<img src="support/lime-2x24.png" style="align-self:end safe; height:24px; width:10px">
 </div>
 
 <div class="grid" style="grid: 4px / 10px">
-<img src="support/lime-24x2.png" style="justify-self:start unsafe; width:10px; height:1px">
+<img src="support/lime-24x2.png" style="justify-self:start unsafe; width:10px; height:4px">
 </div>
 <div class="grid" style="grid: 4px / 10px; margin-left: 40px">
-<img src="support/lime-24x2.png" style="justify-self:start safe; width:10px; height:1px">
+<img src="support/lime-24x2.png" style="justify-self:start safe; width:10px; height:4px">
 </div>
 <div class="grid" style="grid: 4px / 10px; margin-left: 40px">
-<img src="support/lime-24x2.png" style="justify-self:end unsafe; width:10px; height:1px">
+<img src="support/lime-24x2.png" style="justify-self:end unsafe; width:10px; height:4px">
 </div>
 <div class="grid" style="grid: 4px / 10px; margin-left: 80px">
-<img src="support/lime-24x2.png" style="justify-self:end safe; width:10px; height:1px">
+<img src="support/lime-24x2.png" style="justify-self:end safe; width:10px; height:4px">
 </div>
 
 </div>
 
 </body>
 </html>
--- a/layout/reftests/css-grid/grid-item-intrinsic-ratio-stretch-004.html
+++ b/layout/reftests/css-grid/grid-item-intrinsic-ratio-stretch-004.html
@@ -10,16 +10,17 @@
   <link rel="help" href="https://drafts.csswg.org/css-grid/#min-size-auto">
   <link rel="match" href="grid-item-intrinsic-ratio-stretch-004-ref.html">
   <style type="text/css">
 body,html { color:black; background:white; font:16px/1 monospace; padding:0; margin:0; }
 .grid {
   display: inline-grid;
   border: 1px solid;
   margin: 5px;
+  place-items: stretch stretch;
 }
 .vertical-tests img { writing-mode: vertical-rl; }
 .vertical-tests div { vertical-align:bottom }
   </style>
 </head>
 <body>
 
 <div class="grid" style="grid: 96px / 20px">
--- a/layout/reftests/css-grid/grid-item-intrinsic-ratio-stretch-005-ref.html
+++ b/layout/reftests/css-grid/grid-item-intrinsic-ratio-stretch-005-ref.html
@@ -18,131 +18,131 @@ body,html { color:black; background:whit
   justify-items: start;
 }
 .vertical-tests div { vertical-align:bottom }
   </style>
 </head>
 <body>
 
 <div class="grid" style="grid: 96px / 20px">
-<img src="support/lime-2x24.png" style="width:4px; height:48px">
+<img src="support/lime-2x24.png" style="width:4px; height:96px">
 </div>
 <div class="grid" style="grid: 96px / 4px">
-<img src="support/lime-2x24.png" style="height:12px; width:1px">
+<img src="support/lime-2x24.png" style="height:12px; width:4px">
 </div>
 
 <div class="grid" style="grid: 8px / 20px">
-<img src="support/lime-24x2.png" style="height:1px; width:12px">
+<img src="support/lime-24x2.png" style="height:8px; width:12px">
 </div>
 <div class="grid" style="grid: 8px / 100px">
-<img src="support/lime-24x2.png" style="height:6px; width:72px">
+<img src="support/lime-24x2.png" style="height:6px; width:100px">
 </div>
 
 <div class="grid" style="grid: 96px / 20px">
-<img src="support/lime-2x24.png" style="align-self:start; height:48px; width:4px">
+<img src="support/lime-2x24.png" style="align-self:start; height:24px; width:20px">
 </div>
 <div class="grid" style="grid: 96px / 4px">
-<img src="support/lime-2x24.png" style="align-self:start; height:12px; width:1px">
+<img src="support/lime-2x24.png" style="align-self:start; height:12px; width:4px">
 </div>
 
 <div class="grid" style="grid: 8px / 100px">
-<img src="support/lime-24x2.png" style="justify-self:start; width:48px; height:4px">
+<img src="support/lime-24x2.png" style="justify-self:start; width:24px; height:8px">
 </div>
 <div class="grid" style="grid: 8px / 10px">
-<img src="support/lime-24x2.png" style="justify-self:start; width:10px; height:1px">
+<img src="support/lime-24x2.png" style="justify-self:start; width:10px; height:8px">
 </div>
 
 <br>
 
 <div class="grid" style="grid: 96px / 20px">
-<img src="support/lime-2x24.png" style="width:10px; height:120px">
+<img src="support/lime-2x24.png" style="width:20px; height:96px">
 </div>
 <div class="grid" style="grid: 96px / 4px">
-<img src="support/lime-2x24.png" style="width:6px; height:72px">
+<img src="support/lime-2x24.png" style="width:6px; height:96px">
 </div>
 
 <div class="grid" style="grid: 8px / 20px">
-<img src="support/lime-24x2.png" style="width:24px; height:2px">
+<img src="support/lime-24x2.png" style="width:20px; height:8px">
 </div>
 <div class="grid" style="grid: 8px / 100px">
-<img src="support/lime-24x2.png" style="width:120px; height:10px">
+<img src="support/lime-24x2.png" style="width:100px; height:10px">
 </div>
 
 <div class="grid" style="grid: 48px / 6px">
-<img src="support/lime-2x24.png" style="align-self:start; width:calc(2px * (80 / 24)); height:80px">
+<img src="support/lime-2x24.png" style="align-self:start; width:6px; height:80px">
 </div>
 <div class="grid" style="grid: 96px / 4px">
-<img src="support/lime-2x24.png" style="align-self:start; width:6px; height:72px">
+<img src="support/lime-2x24.png" style="align-self:start; width:4px; height:72px">
 </div>
 
 <div class="grid" style="grid: 8px / 100px">
 <img src="support/lime-24x2.png" style="justify-self:start; width:98px; height:calc(2px * (98 / 24))">
 </div>
 <div class="grid" style="grid: 4px / 10px">
-<img src="support/lime-24x2.png" style="justify-self:start; width:72px; height:6px">
+<img src="support/lime-24x2.png" style="justify-self:start; width:72px; height:4px">
 </div>
 
 <br>
 
 <div class="vertical-tests">
 
 <div class="grid" style="grid: 96px / 20px">
-<img src="support/lime-2x24.png" style="width:4px; height:48px">
+<img src="support/lime-2x24.png" style="width:4px; height:96px">
 </div>
 <div class="grid" style="grid: 96px / 4px">
-<img src="support/lime-2x24.png" style="height:12px; width:1px">
+<img src="support/lime-2x24.png" style="height:12px; width:4px">
 </div>
 
 <div class="grid" style="grid: 8px / 20px">
-<img src="support/lime-24x2.png" style="height:1px; width:12px">
+<img src="support/lime-24x2.png" style="height:8px; width:12px">
 </div>
 <div class="grid" style="grid: 8px / 100px">
-<img src="support/lime-24x2.png" style="height:6px; width:72px">
+<img src="support/lime-24x2.png" style="height:6px; width:100px">
 </div>
 
 <div class="grid" style="grid: 96px / 20px">
-<img src="support/lime-2x24.png" style="align-self:start; height:48px; width:4px">
+<img src="support/lime-2x24.png" style="align-self:start; height:24px; width:20px">
 </div>
 <div class="grid" style="grid: 96px / 4px">
-<img src="support/lime-2x24.png" style="align-self:start; height:12px; width:1px">
+<img src="support/lime-2x24.png" style="align-self:start; height:12px; width:4px">
 </div>
 
 <div class="grid" style="grid: 8px / 100px">
-<img src="support/lime-24x2.png" style="justify-self:start; width:48px; height:4px">
+<img src="support/lime-24x2.png" style="justify-self:start; width:24px; height:8px">
 </div>
 <div class="grid" style="grid: 8px / 10px">
-<img src="support/lime-24x2.png" style="justify-self:start; width:10px; height:1px">
+<img src="support/lime-24x2.png" style="justify-self:start; width:10px; height:8px">
 </div>
 
 <br>
 
 <div class="grid" style="grid: 96px / 20px">
-<img src="support/lime-2x24.png" style="width:10px; height:120px">
+<img src="support/lime-2x24.png" style="width:20px; height:96px">
 </div>
 <div class="grid" style="grid: 96px / 4px">
-<img src="support/lime-2x24.png" style="width:6px; height:72px">
+<img src="support/lime-2x24.png" style="width:6px; height:96px">
 </div>
 
 <div class="grid" style="grid: 8px / 20px">
-<img src="support/lime-24x2.png" style="width:24px; height:2px">
+<img src="support/lime-24x2.png" style="width:20px; height:8px">
 </div>
 <div class="grid" style="grid: 8px / 100px">
-<img src="support/lime-24x2.png" style="width:120px; height:10px">
+<img src="support/lime-24x2.png" style="width:100px; height:10px">
 </div>
 
 <div class="grid" style="grid: 48px / 6px">
-<img src="support/lime-2x24.png" style="align-self:start; width:calc(2px * (80 / 24)); height:80px">
+<img src="support/lime-2x24.png" style="align-self:start; width:6px; height:80px">
 </div>
 <div class="grid" style="grid: 96px / 4px">
-<img src="support/lime-2x24.png" style="align-self:start; width:6px; height:72px">
+<img src="support/lime-2x24.png" style="align-self:start; width:4px; height:72px">
 </div>
 
 <div class="grid" style="grid: 8px / 100px">
 <img src="support/lime-24x2.png" style="justify-self:start; width:98px; height:calc(2px * (98 / 24))">
 </div>
 <div class="grid" style="grid: 4px / 10px">
-<img src="support/lime-24x2.png" style="justify-self:start; width:72px; height:6px">
+<img src="support/lime-24x2.png" style="justify-self:start; width:72px; height:4px">
 </div>
 
 </div>
 
 </body>
 </html>
--- a/layout/reftests/css-grid/grid-item-intrinsic-ratio-stretch-005.html
+++ b/layout/reftests/css-grid/grid-item-intrinsic-ratio-stretch-005.html
@@ -11,16 +11,17 @@
   <link rel="match" href="grid-item-intrinsic-ratio-stretch-005-ref.html">
   <style type="text/css">
 body,html { color:black; background:white; font:16px/1 monospace; padding:0; margin:0; }
 
 .grid {
   display: inline-grid;
   border: 1px solid;
   margin: 5px;
+  place-items: stretch;
 }
 .vertical-tests img { writing-mode: vertical-rl; }
 .vertical-tests div { vertical-align:bottom }
   </style>
 </head>
 <body>
 
 <div class="grid" style="grid: 96px / 20px">
--- a/layout/reftests/css-grid/grid-item-intrinsic-size-normal-001-ref.html
+++ b/layout/reftests/css-grid/grid-item-intrinsic-size-normal-001-ref.html
@@ -142,89 +142,89 @@ button {
 <div class="grid"><button class="as h20 mxw2"></button></div>
 <div class="grid"><button class="an h20"></button></div>
 <div class="grid"><button class="an h20 mxw10"></button></div>
 
 <script>
 var buttonSizes =
 [
   ['32px', '4px'],
-  ['0px', '0px'],
-  ['0px', '4px'],
-  ['0px', '4px'],
-  ['0px', '4px'],
-  ['0px', '4px'],
+  ['2px', '2px'],
+  ['2px', '4px'],
+  ['2px', '4px'],
+  ['2px', '4px'],
+  ['2px', '4px'],
   ['32px', '20px'],
   ['10px', '20px'],
-  ['0px', '20px'],
-  ['0px', '20px'],
-  ['0px', '20px'],
-  ['0px', '20px'],
-  ['0px', '20px'],
-  ['0px', '20px'],
+  ['2px', '20px'],
+  ['2px', '20px'],
+  ['2px', '20px'],
+  ['2px', '20px'],
+  ['2px', '20px'],
+  ['2px', '20px'],
   ['32px', '4px'],
   ['10px', '4px'],
-  ['0px', '0px'],
-  ['0px', '0px'],
-  ['0px', '4px'],
-  ['0px', '4px'],
-  ['0px', '4px'],
-  ['0px', '4px'],
+  ['2px', '2px'],
+  ['2px', '2px'],
+  ['2px', '4px'],
+  ['2px', '4px'],
+  ['2px', '4px'],
+  ['2px', '4px'],
   ['4px', '32px'],
   ['2px', '32px'],
-  ['0px', '0px'],
-  ['2px', '0px'],
-  ['0px', '32px'],
-  ['0px', '32px'],
-  ['0px', '32px'],
-  ['0px', '32px'],
+  ['2px', '2px'],
+  ['2px', '2px'],
+  ['2px', '32px'],
+  ['2px', '32px'],
+  ['2px', '32px'],
+  ['2px', '32px'],
   ['20px', '32px'],
   ['20px', '10px'],
-  ['20px', '0px'],
-  ['20px', '0px'],
+  ['20px', '2px'],
+  ['20px', '2px'],
   ['20px', '32px'],
   ['20px', '10px'],
   ['20px', '32px'],
   ['20px', '10px'],
   ['4px', '32px'],
   ['4px', '10px'],
-  ['0px', '0px'],
-  ['0px', '0px'],
-  ['0px', '32px'],
-  ['0px', '10px'],
-  ['0px', '32px'],
-  ['0px', '10px'],
+  ['2px', '2px'],
+  ['2px', '2px'],
+  ['2px', '32px'],
+  ['2px', '10px'],
+  ['2px', '32px'],
+  ['2px', '10px'],
   ['20px', '4px'],
   ['20px', '4px'],
-  ['20px', '0px'],
-  ['20px', '0px'],
+  ['20px', '2px'],
+  ['20px', '2px'],
   ['20px', '4px'],
   ['20px', '2px'],
   ['20px', '4px'],
   ['20px', '2px'],
-  ['20px', '0px'],
-  ['20px', '0px'],
-  ['20px', '0px'],
-  ['20px', '0px'],
+  ['20px', '2px'],
+  ['20px', '2px'],
+  ['20px', '2px'],
+  ['20px', '2px'],
   ['20px', '32px'],
   ['20px', '10px'],
   ['20px', '32px'],
   ['20px', '10px'],
   ['32px', '20px'],
   ['10px', '20px'],
-  ['0px', '20px'],
-  ['0px', '20px'],
+  ['2px', '20px'],
+  ['2px', '20px'],
   ['32px', '20px'],
   ['10px', '20px'],
   ['32px', '20px'],
   ['10px', '20px'],
   ['4px', '20px'],
   ['2px', '20px'],
-  ['0px', '20px'],
-  ['0px', '20px'],
+  ['2px', '20px'],
+  ['2px', '20px'],
   ['4px', '20px'],
   ['2px', '20px'],
   ['4px', '20px'],
   ['4px', '20px'],
 ];
 var buttons = document.querySelectorAll('button');
 for (var i = 0; i < buttons.length; ++i) {
   var button = buttons[i];
--- a/layout/reftests/css-grid/grid-item-video-stretch-001-ref.html
+++ b/layout/reftests/css-grid/grid-item-video-stretch-001-ref.html
@@ -1,67 +1,67 @@
 <!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: stretching video items with auto-margins and/or orthogonal writing-mode</title>
+  <title>Reference: video items with auto-margins and/or orthogonal writing-mode</title>
   <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1316051">
   <style type="text/css">
 * { vertical-align: top; }
 .grid {
   display: inline-grid;
   border: 3px solid grey;
   grid: 50px / 100px;
   padding: 10px 4px 3px 6px;
 }
 
 video {
   border: 1px solid;
   padding: 0;
   margin: 0;
   background: lightgrey;
+  width: 16px;
+  height: 8px;
 }
 
 .m { margin: 7px 3px 1px 5px; }
 .p { padding: 3px 1px 5px 7px; }
 .hma10 { margin: 7px 3px 1px 0; }
 .hmaa  { margin: 7px 0 1px 0; }
 .vma10 { margin: 0 7px 3px 1px; }
 .vmaa  { margin: 0 7px 0 1px; }
 
 .js { justify-self: start; }
 .je { justify-self: end; }
 .jc { justify-self: center; }
 .as { align-self: start; }
 .ae { align-self: end; }
 .ac { align-self: center; }
 
-.mxw, .mxh { width: 32px; height: 16px; }
-
 .vr { writing-mode: vertical-rl; }
   </style>
 </head>
 <body>
 
-<div class="grid"><video class="m" src="support/colors-16x8.webm" style="width:80px; height:40px"></div>
-<div class="grid"><video class="hma10 je" src="support/colors-16x8.webm" style="width:80px; height:40px"></div>
-<div class="grid"><video class="hmaa jc" src="support/colors-16x8.webm" style="width:80px; height:40px"></div>
-<div class="grid"><video class="vr hma10 je" src="support/colors-16x8.webm" style="width:80px; height:40px"></div>
-<div class="grid"><video class="vr hmaa jc" src="support/colors-16x8.webm" style="width:80px; height:40px"></div>
-<div class="grid"><video class="vr" src="support/colors-16x8.webm" style="width:96px; height:48px"></div>
+<div class="grid"><video class="m" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="hma10 je" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="hmaa jc" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="vr hma10 je" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="vr hmaa jc" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="vr" src="support/colors-16x8.webm"></div>
 
-<div class="grid"><video class="vma10 ae" src="support/colors-16x8.webm" style="width:90px; height:45px"></div>
-<div class="grid"><video class="vmaa ac" src="support/colors-16x8.webm" style="width:90px; height:45px"></div>
-<div class="grid"><video class="vr vma10 ae" src="support/colors-16x8.webm" style="width:90px; height:45px"></div>
-<div class="grid"><video class="vr vmaa ac" src="support/colors-16x8.webm" style="width:90px; height:45px"></div>
-<div class="grid"><video class="vr p vma10 as" src="support/colors-16x8.webm" style="width:82px; height:41px"></div>
-<div class="grid"><video class="vr p vmaa ac" src="support/colors-16x8.webm" style="width:82px; height:41px"></div>
+<div class="grid"><video class="vma10 ae" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="vmaa ac" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="vr vma10 ae" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="vr vmaa ac" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="vr p vma10 ae" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="vr p vmaa ac" src="support/colors-16x8.webm"></div>
 
 <div class="grid"><video class="mxw m" src="support/colors-16x8.webm"></div>
 <div class="grid"><video class="mxw hma10 je" src="support/colors-16x8.webm"></div>
 <div class="grid"><video class="mxw hmaa jc" src="support/colors-16x8.webm"></div>
 <div class="grid"><video class="mxw vr hma10 je" src="support/colors-16x8.webm"></div>
 <div class="grid"><video class="mxw vr hmaa jc" src="support/colors-16x8.webm"></div>
 <div class="grid"><video class="mxw vr" src="support/colors-16x8.webm"></div>
 
--- a/layout/reftests/css-grid/grid-item-video-stretch-001.html
+++ b/layout/reftests/css-grid/grid-item-video-stretch-001.html
@@ -1,16 +1,16 @@
 <!DOCTYPE HTML>
 <!--
      Any copyright is dedicated to the Public Domain.
      http://creativecommons.org/publicdomain/zero/1.0/
 -->
 <html><head>
   <meta charset="utf-8">
-  <title>CSS Grid Test: stretching video items with auto-margins and/or orthogonal writing-mode</title>
+  <title>CSS Grid Test: video items with auto-margins and/or orthogonal writing-mode</title>
   <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1316051">
   <link rel="help" href="https://drafts.csswg.org/css-align-3/#valdef-justify-self-normal">
   <link rel="match" href="grid-item-video-stretch-001-ref.html">
   <style type="text/css">
 * { vertical-align: top; }
 .grid {
   display: inline-grid;
   border: 3px solid grey;
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-grid/grid-item-video-stretch-002-ref.html
@@ -0,0 +1,113 @@
+<!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: stretching video items with auto-margins and/or orthogonal writing-mode</title>
+  <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1316051">
+  <style type="text/css">
+* { vertical-align: top; }
+.grid {
+  display: inline-grid;
+  border: 3px solid grey;
+  grid: 50px / 100px;
+  padding: 10px 4px 3px 6px;
+}
+
+video {
+  border: 1px solid;
+  padding: 0;
+  margin: 0;
+  background: lightgrey;
+  width: 16px;
+  height: 8px;
+}
+
+.m { margin: 7px 3px 1px 5px; }
+.p { padding: 3px 1px 5px 7px; }
+.hma10 { margin: 7px 3px 1px 0; }
+.hmaa  { margin: 7px 0 1px 0; }
+.vma10 { margin: 0 7px 3px 1px; }
+.vmaa  { margin: 0 7px 0 1px; }
+
+.js { justify-self: start; }
+.je { justify-self: end; }
+.jc { justify-self: center; }
+.as { align-self: start; }
+.ae { align-self: end; }
+.ac { align-self: center; }
+
+.vr { writing-mode: vertical-rl; }
+  </style>
+</head>
+<body>
+
+<div class="grid"><video class="m" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="hma10 je" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="hmaa jc" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="vr hma10 je" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="vr hmaa jc" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="vr" src="support/colors-16x8.webm"></div>
+
+<div class="grid"><video class="vma10 ae" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="vmaa ac" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="vr vma10 ae" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="vr vmaa ac" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="vr p vma10 ae" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="vr p vmaa ac" src="support/colors-16x8.webm"></div>
+
+<div class="grid"><video class="mxw m" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="mxw hma10 je" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="mxw hmaa jc" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="mxw vr hma10 je" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="mxw vr hmaa jc" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="mxw vr" src="support/colors-16x8.webm"></div>
+
+<div class="grid"><video class="mxh m" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="mxh hma10 je" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="mxh hmaa jc" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="mxh vr hma10 je" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="mxh vr hmaa jc" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="mxh vr" src="support/colors-16x8.webm"></div>
+
+<script>
+var sizes =
+[
+  ['90px', '40px'],
+  ['16px', '40px'],
+  ['16px', '40px'],
+  ['16px', '40px'],
+  ['16px', '40px'],
+  ['98px', '48px'],
+  ['90px', '8px'],
+  ['90px', '8px'],
+  ['90px', '8px'],
+  ['90px', '8px'],
+  ['82px', '8px'],
+  ['82px', '8px'],
+  ['32px', '40px'],
+  ['16px', '40px'],
+  ['16px', '40px'],
+  ['16px', '40px'],
+  ['16px', '40px'],
+  ['32px', '48px'],
+  ['90px', '16px'],
+  ['16px', '16px'],
+  ['16px', '16px'],
+  ['16px', '16px'],
+  ['16px', '16px'],
+  ['98px', '16px'],
+];
+var items = document.querySelectorAll('video')
+for (var i = 0; i < items.length; ++i) {
+  let item = items[i];
+  item.style.width = sizes[i][0];
+  item.style.height = sizes[i][1];
+}
+
+</script>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-grid/grid-item-video-stretch-002.html
@@ -0,0 +1,91 @@
+<!DOCTYPE HTML>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+  <meta charset="utf-8">
+  <title>CSS Grid Test: stretching video items with auto-margins and/or orthogonal writing-mode</title>
+  <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1316051">
+  <link rel="help" href="https://drafts.csswg.org/css-align-3/#valdef-justify-self-normal">
+  <link rel="match" href="grid-item-video-stretch-002-ref.html">
+  <style type="text/css">
+* { vertical-align: top; }
+.grid {
+  display: inline-grid;
+  border: 3px solid grey;
+  grid: 10px 50px 3px / 6px 100px 4px;
+  align-items: stretch;
+  justify-items: stretch;
+}
+
+video {
+  border: 1px solid;
+  padding: 0;
+  margin: 0;
+  background: lightgrey;
+  grid-area: 2/2;
+}
+
+.m { margin: 7px 3px 1px 5px; }
+.p { padding: 3px 1px 5px 7px; }
+.hma10 { margin: 7px 3px 1px auto; }
+.hmaa  { margin: 7px auto 1px auto; }
+.vma10 { margin: auto 7px 3px 1px; }
+.vmaa  { margin: auto 7px auto 1px; }
+.mxw { max-width: 32px; }
+.mxh { max-height: 16px; }
+
+.vr { writing-mode: vertical-rl; }
+  </style>
+</head>
+<body>
+
+<div class="grid"><video class="m" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="hma10" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="hmaa" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="vr hma10" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="vr hmaa" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="vr" src="support/colors-16x8.webm"></div>
+
+<div class="grid"><video class="vma10" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="vmaa" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="vr vma10" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="vr vmaa" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="vr p vma10" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="vr p vmaa" src="support/colors-16x8.webm"></div>
+
+<div class="grid"><video class="mxw m" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="mxw hma10" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="mxw hmaa" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="mxw vr hma10" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="mxw vr hmaa" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="mxw vr" src="support/colors-16x8.webm"></div>
+
+<div class="grid"><video class="mxh m" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="mxh hma10" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="mxh hmaa" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="mxh vr hma10" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="mxh vr hmaa" src="support/colors-16x8.webm"></div>
+<div class="grid"><video class="mxh vr" src="support/colors-16x8.webm"></div>
+
+<!--
+<script>
+function test() {
+document.body.clientHeight;
+var videos = document.querySelectorAll('video');
+var s = '  [\n';
+for (var i = 0; i < videos.length; ++i) {
+  let cs = window.getComputedStyle(videos[i])
+  s += "  ['"+ cs.width + "', '" + cs.height + "'],\n";
+}
+s += ']';
+console.log(s)
+}
+
+setTimeout(test,2000)
+</script>
+-->
+
+</body>
+</html>
--- a/layout/reftests/css-grid/grid-max-sizing-flex-007-ref.html
+++ b/layout/reftests/css-grid/grid-max-sizing-flex-007-ref.html
@@ -102,9 +102,33 @@
   <div class="item"></div>
   <div class="item"></div>
 </div>
 <div class="grid cols w90"  style="width:90px">
   <div class="item"></div>
   <div class="item"></div>
 </div>
 
+<pre>The first 6 grids should look the same:</pre>
+<div class="grid rows" style="grid: 1fr / 30px; height:83px">
+  <div class="item"></div>
+</div>
+<div class="grid rows" style="grid: 10px 1fr / 30px; height:83px">
+  <div class="item" style="grid-row:span 2"></div>
+</div>
+<div class="grid rows" style="grid: 1fr / 30px; height:83px">
+  <div class="item"></div>
+</div>
+<div class="grid rows" style="grid: 1fr 1fr / 30px; height:83px">
+  <div class="item" style="grid-row:span 2"><div style="height:90px"></div></div>
+</div>
+<div class="grid rows" style="grid: 1fr auto / 30px; height:83px">
+  <div class="item" style="grid-row:span 2"><div style="height:90px"></div></div>
+</div>
+<div class="grid rows" style="grid: 10px 1fr / 30px; height:83px">
+  <div class="item" style="grid-row:span 2"><div style="height:90px"></div></div>
+</div>
+<div class="grid rows" style="grid: 1fr 1fr / 30px; grid-row-gap:10px; height:83px">
+  <div class="item" style="grid-row:span 2"><div style="height:40px"></div></div>
+  <div class="item"><div style="height:40px"></div></div>
+</div>
+
 </body></html>
--- a/layout/reftests/css-grid/grid-max-sizing-flex-007.html
+++ b/layout/reftests/css-grid/grid-max-sizing-flex-007.html
@@ -100,9 +100,33 @@
   <div class="item"></div>
   <div class="item"></div>
 </div>
 <div class="grid cols"  style="width:90px">
   <div class="item"></div>
   <div class="item"></div>
 </div>
 
+<pre>The first 6 grids should look the same:</pre>
+<div class="grid rows" style="grid: 1fr / 30px; min-height:83px">
+  <div class="item"></div>
+</div>
+<div class="grid rows" style="grid: 10px 1fr / 30px; min-height:83px">
+  <div class="item" style="grid-row:span 2"></div>
+</div>
+<div class="grid rows" style="grid: 1fr / 30px; max-height:30px; min-height:83px">
+  <div class="item"></div>
+</div>
+<div class="grid rows" style="grid: 1fr 1fr / 30px; max-height:83px">
+  <div class="item" style="grid-row:span 2"><div style="height:90px"></div></div>
+</div>
+<div class="grid rows" style="grid: 1fr auto / 30px; max-height:83px">
+  <div class="item" style="grid-row:span 2"><div style="height:90px"></div></div>
+</div>
+<div class="grid rows" style="grid: 10px 1fr / 30px; max-height:83px">
+  <div class="item" style="grid-row:span 2"><div style="height:90px"></div></div>
+</div>
+<div class="grid rows" style="grid: 1fr 1fr / 30px; grid-row-gap:10px; max-height:83px">
+  <div class="item" style="grid-row:span 2"><div style="height:40px"></div></div>
+  <div class="item"><div style="height:40px"></div></div>
+</div>
+
 </body></html>
--- a/layout/reftests/css-grid/reftest.list
+++ b/layout/reftests/css-grid/reftest.list
@@ -119,16 +119,17 @@ skip-if(Android) == grid-auto-min-sizing
 == 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
 == 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
 random-if(http.oscpu!="Linux\u0020i686") == grid-item-content-baseline-002.html grid-item-content-baseline-002-ref.html # ditto
 random-if(http.oscpu!="Linux\u0020i686") == grid-item-mixed-baseline-001.html grid-item-mixed-baseline-001-ref.html # ditto
 random-if(http.oscpu!="Linux\u0020i686") == grid-item-mixed-baseline-002.html grid-item-mixed-baseline-002-ref.html # ditto
 random-if(http.oscpu!="Linux\u0020i686") == grid-item-mixed-baseline-003.html grid-item-mixed-baseline-003-ref.html # ditto
 skip-if(!gtkWidget) == grid-item-mixed-baseline-004.html grid-item-mixed-baseline-004-ref.html # ditto
--- a/nsprpub/TAG-INFO
+++ b/nsprpub/TAG-INFO
@@ -1,1 +1,1 @@
-NSPR_4_13_1_RTM
+NSPR_4_14_BETA2
--- a/nsprpub/config/prdepend.h
+++ b/nsprpub/config/prdepend.h
@@ -5,9 +5,8 @@
 
 /*
  * A dummy header file that is a dependency for all the object files.
  * Used to force a full recompilation of NSPR in Mozilla's Tinderbox
  * depend builds.  See comments in rules.mk.
  */
 
 #error "Do not include this header file."
-
--- a/nsprpub/configure
+++ b/nsprpub/configure
@@ -2483,18 +2483,18 @@ case $target_os in *\ *) target_os=`echo
 # The aliases save the names the user supplied, while $host etc.
 # will get canonicalized.
 test -n "$target_alias" &&
   test "$program_prefix$program_suffix$program_transform_name" = \
     NONENONEs,x,x, &&
   program_prefix=${target_alias}-
 
 MOD_MAJOR_VERSION=4
-MOD_MINOR_VERSION=13
-MOD_PATCH_VERSION=1
+MOD_MINOR_VERSION=14
+MOD_PATCH_VERSION=0
 NSPR_MODNAME=nspr20
 _HAVE_PTHREADS=
 USE_PTHREADS=
 USE_USER_PTHREADS=
 USE_NSPR_THREADS=
 USE_N32=
 USE_X32=
 USE_64=
--- a/nsprpub/configure.in
+++ b/nsprpub/configure.in
@@ -10,18 +10,18 @@ AC_CONFIG_SRCDIR([pr/include/nspr.h])
 
 AC_CONFIG_AUX_DIR(${srcdir}/build/autoconf)
 AC_CANONICAL_TARGET
 
 dnl ========================================================
 dnl = Defaults
 dnl ========================================================
 MOD_MAJOR_VERSION=4
-MOD_MINOR_VERSION=13
-MOD_PATCH_VERSION=1
+MOD_MINOR_VERSION=14
+MOD_PATCH_VERSION=0
 NSPR_MODNAME=nspr20
 _HAVE_PTHREADS=
 USE_PTHREADS=
 USE_USER_PTHREADS=
 USE_NSPR_THREADS=
 USE_N32=
 USE_X32=
 USE_64=
--- a/nsprpub/pr/include/prinit.h
+++ b/nsprpub/pr/include/prinit.h
@@ -26,21 +26,21 @@ PR_BEGIN_EXTERN_C
 /*
 ** NSPR's version is used to determine the likelihood that the version you
 ** used to build your component is anywhere close to being compatible with
 ** what is in the underlying library.
 **
 ** The format of the version string is
 **     "<major version>.<minor version>[.<patch level>] [<Beta>]"
 */
-#define PR_VERSION  "4.13.1"
+#define PR_VERSION  "4.14 Beta"
 #define PR_VMAJOR   4
-#define PR_VMINOR   13
-#define PR_VPATCH   1
-#define PR_BETA     PR_FALSE
+#define PR_VMINOR   14
+#define PR_VPATCH   0
+#define PR_BETA     PR_TRUE
 
 /*
 ** PRVersionCheck
 **
 ** The basic signature of the function that is called to provide version
 ** checking. The result will be a boolean that indicates the likelihood
 ** that the underling library will perform as the caller expects.
 **
--- a/nsprpub/pr/src/md/windows/w32poll.c
+++ b/nsprpub/pr/src/md/windows/w32poll.c
@@ -150,17 +150,17 @@ PRInt32 _PR_MD_PR_POLL(PRPollDesc *pds, 
                 ready += 1;
                 pd->out_flags = out_flags_read | out_flags_write;
             }
             else
             {
                 pd->out_flags = 0;  /* pre-condition */
                 /* make sure this is an NSPR supported stack */
                 bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER);
-                // ignore a socket without PR_NSPR_IO_LAYER available.
+                /* ignore a socket without PR_NSPR_IO_LAYER available */
 
                 if ((NULL != bottom)
                 && (_PR_FILEDESC_OPEN == bottom->secret->state))
                 {
                     if (0 == ready)
                     {
                         osfd = (SOCKET) bottom->secret->md.osfd;
                         if (in_flags_read & PR_POLL_READ)
--- a/nsprpub/pr/src/pthreads/ptio.c
+++ b/nsprpub/pr/src/pthreads/ptio.c
@@ -3842,17 +3842,18 @@ static PRInt32 _pr_poll_with_poll(
                     ready += 1;
                     pds[index].out_flags = out_flags_read | out_flags_write;
                 }
                 else
                 {
                     /* now locate the NSPR layer at the bottom of the stack */
                     PRFileDesc *bottom = PR_GetIdentitiesLayer(
                         pds[index].fd, PR_NSPR_IO_LAYER);
-                    PR_ASSERT(NULL != bottom);  /* what to do about that? */
+                    /* ignore a socket without PR_NSPR_IO_LAYER available */
+
                     pds[index].out_flags = 0;  /* pre-condition */
                     if ((NULL != bottom)
                     && (_PR_FILEDESC_OPEN == bottom->secret->state))
                     {
                         if (0 == ready)
                         {
                             syspoll[index].fd = bottom->secret->md.osfd;
                             syspoll[index].events = 0;
@@ -4100,17 +4101,18 @@ static PRInt32 _pr_poll_with_select(
                     ready += 1;
                     pds[index].out_flags = out_flags_read | out_flags_write;
                 }
                 else
                 {
                     /* now locate the NSPR layer at the bottom of the stack */
                     PRFileDesc *bottom = PR_GetIdentitiesLayer(
                         pds[index].fd, PR_NSPR_IO_LAYER);
-                    PR_ASSERT(NULL != bottom);  /* what to do about that? */
+                    /* ignore a socket without PR_NSPR_IO_LAYER available */
+
                     pds[index].out_flags = 0;  /* pre-condition */
                     if ((NULL != bottom)
                     && (_PR_FILEDESC_OPEN == bottom->secret->state))
                     {
                         if (0 == ready)
                         {
                             PRBool add_to_rd = PR_FALSE;
                             PRBool add_to_wr = PR_FALSE;
--- a/nsprpub/pr/tests/vercheck.c
+++ b/nsprpub/pr/tests/vercheck.c
@@ -50,18 +50,18 @@ static char *compatible_version[] = {
  * Any release is incompatible with future releases and
  * patches.
  */
 static char *incompatible_version[] = {
     "2.1 19980529",
     "3.0", "3.0.1",
     "3.1", "3.1.1", "3.1.2", "3.1.3",
     "3.5", "3.5.1",
-    "4.13.2",
-    "4.14", "4.14.1",
+    "4.14.1",
+    "4.15", "4.15.1",
     "10.0", "11.1", "12.14.20"
 };
 
 int main(int argc, char **argv)
 {
     int idx;
     int num_compatible = sizeof(compatible_version) / sizeof(char *);
     int num_incompatible = sizeof(incompatible_version) / sizeof(char *);
--- a/python/mozbuild/mozbuild/vendor_rust.py
+++ b/python/mozbuild/mozbuild/vendor_rust.py
@@ -1,15 +1,16 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, # You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 from distutils.version import LooseVersion
+import hashlib
 import logging
 from mozbuild.base import (
     BuildEnvironmentNotFoundException,
     MozbuildObject,
 )
 import mozfile
 import mozpack.path as mozpath
 import os
@@ -89,57 +90,201 @@ Please commit or stash these changes bef
             return {
                  'OPENSSL_INCLUDE_DIR': '/usr/local/opt/openssl/include',
                  'DEP_OPENSSL_INCLUDE': '/usr/local/opt/openssl/include',
             }
 
         self.log(logging.ERROR, 'openssl', {}, "OpenSSL not found!")
         return None
 
-    def vendor(self, ignore_modified=False,
-               build_peers_said_large_imports_were_ok=False):
-        self.populate_logger()
-        self.log_manager.enable_unstructured()
-        if not ignore_modified:
-            self.check_modified_files()
+    def _ensure_cargo(self):
+        '''
+        Ensures all the necessary cargo bits are installed.
+
+        Returns the path to cargo if successful, None otherwise.
+        '''
         cargo = self.get_cargo_path()
         if not self.check_cargo_version(cargo):
             self.log(logging.ERROR, 'cargo_version', {}, 'Cargo >= 0.13 required (install Rust 1.12 or newer)')
-            return
+            return None
         else:
             self.log(logging.DEBUG, 'cargo_version', {}, 'cargo is new enough')
         have_vendor = any(l.strip() == 'vendor' for l in subprocess.check_output([cargo, '--list']).splitlines())
         if not have_vendor:
             self.log(logging.INFO, 'installing', {}, 'Installing cargo-vendor (this may take a few minutes)...')
             env = self.check_openssl()
             self.run_process(args=[cargo, 'install', 'cargo-vendor'],
                              append_env=env)
         elif not self.check_cargo_vendor_version(cargo):
             self.log(logging.INFO, 'cargo_vendor', {}, 'cargo-vendor >= 0.1.3 required; force-reinstalling (this may take a few minutes)...')
             env = self.check_openssl()
             self.run_process(args=[cargo, 'install', '--force', 'cargo-vendor'],
                              append_env=env)
         else:
-            self.log(logging.DEBUG, 'cargo_vendor', {}, 'sufficiently new cargo-vendor is already intalled')
+            self.log(logging.DEBUG, 'cargo_vendor', {}, 'sufficiently new cargo-vendor is already installed')
+
+        return cargo
+
+    def _check_licenses(self, vendor_dir):
+        # A whitelist of acceptable license identifiers for the
+        # packages.license field from https://spdx.org/licenses/.  Cargo
+        # documentation claims that values are checked against the above
+        # list and that multiple entries can be separated by '/'.  We
+        # choose to list all combinations instead for the sake of
+        # completeness and because some entries below obviously do not
+        # conform to the format prescribed in the documentation.
+        #
+        # It is insufficient to have additions to this whitelist reviewed
+        # solely by a build peer; any additions must be checked by somebody
+        # competent to review licensing minutiae.
+        LICENSE_WHITELIST = [
+            'Apache-2.0',
+            'Apache-2.0 / MIT',
+            'Apache-2.0/MIT',
+            'Apache-2 / MIT',
+            'BSD-3-Clause', # bindgen (only used at build time)
+            'CC0-1.0',
+            'ISC',
+            'ISC/Apache-2.0',
+            'MIT',
+            'MIT / Apache-2.0',
+            'MIT/Apache-2.0',
+            'MIT OR Apache-2.0',
+            'MPL-2.0',
+            'Unlicense/MIT',
+        ]
+
+        # This whitelist should only be used for packages that use a
+        # license-file and for which the license-file entry has been
+        # reviewed.  The table is keyed by package names and maps to the
+        # sha256 hash of the license file that we reviewed.
+        #
+        # As above, it is insufficient to have additions to this whitelist
+        # reviewed solely by a build peer; any additions must be checked by
+        # somebody competent to review licensing minutiae.
+        LICENSE_FILE_PACKAGE_WHITELIST = {
+            # Google BSD-like license; some directories have separate licenses
+            'gamma-lut': '1f04103e3a61b91343b3f9d2ed2cc8543062917e2cc7d52a739ffe6429ccaf61',
+            # MIT
+            'deque': '6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb',
+        }
+
+        LICENSE_LINE_RE = re.compile(r'\s*license\s*=\s*"([^"]+)"')
+        LICENSE_FILE_LINE_RE = re.compile(r'\s*license[-_]file\s*=\s*"([^"]+)"')
+
+        def check_package(package):
+            self.log(logging.DEBUG, 'package_check', {},
+                     'Checking license for {}'.format(package))
+
+            toml_file = os.path.join(vendor_dir, package, 'Cargo.toml')
+
+            # pytoml is not sophisticated enough to parse Cargo.toml files
+            # with [target.'cfg(...)'.dependencies sections, so we resort
+            # to scanning individual lines.
+            with open(toml_file, 'r') as f:
+                license_lines = [l for l in f if l.strip().startswith(b'license')]
+                license_matches = list(filter(lambda x: x, [LICENSE_LINE_RE.match(l) for l in license_lines]))
+                license_file_matches = list(filter(lambda x: x, [LICENSE_FILE_LINE_RE.match(l) for l in license_lines]))
+
+                # License information is optional for crates to provide, but
+                # we require it.
+                if not license_matches and not license_file_matches:
+                    self.log(logging.ERROR, 'package_no_license', {},
+                             'package {} does not provide a license'.format(package))
+                    return False
+
+                # The Cargo.toml spec suggests that crates should either have
+                # `license` or `license-file`, but not both.  We might as well
+                # be defensive about that, though.
+                if len(license_matches) > 1 or len(license_file_matches) > 1 or \
+                   license_matches and license_file_matches:
+                    self.log(logging.ERROR, 'package_many_licenses', {},
+                             'package {} provides too many licenses'.format(package))
+                    return False
+
+                if license_matches:
+                    license = license_matches[0].group(1)
+                    self.log(logging.DEBUG, 'package_license', {},
+                             'has license {}'.format(license))
+
+                    if license not in LICENSE_WHITELIST:
+                        self.log(logging.ERROR, 'package_license_error', {},
+                                 '''Package {} has a non-approved license: {}.
+
+Please request license review on the package's license.  If the package's license
+is approved, please add it to the whitelist of suitable licenses.
+'''.format(package, license))
+                        return False
+                else:
+                    license_file = license_file_matches[0].group(1)
+                    self.log(logging.DEBUG, 'package_license_file', {},
+                             'has license-file {}'.format(license_file))
+
+                    if package not in LICENSE_FILE_PACKAGE_WHITELIST:
+                        self.log(logging.ERROR, 'package_license_file_unknown', {},
+                                 '''Package {} has an unreviewed license file: {}.
+
+Please request review on the provided license; if approved, the package can be added
+to the whitelist of packages whose licenses are suitable.
+'''.format(package, license_file))
+                        return False
+
+                    approved_hash = LICENSE_FILE_PACKAGE_WHITELIST[package]
+                    license_contents = open(os.path.join(vendor_dir, package, license_file), 'r').read()
+                    current_hash = hashlib.sha256(license_contents).hexdigest()
+                    if current_hash != approved_hash:
+                        self.log(logging.ERROR, 'package_license_file_mismatch', {},
+                                 '''Package {} has changed its license file: {} (hash {}).
+
+Please request review on the provided license; if approved, please update the
+license file's hash.
+'''.format(package, license_file, current_hash))
+                        return False
+
+                return True
+
+        # Force all of the packages to be checked for license information
+        # before reducing via `all`, so all license issues are found in a
+        # single `mach vendor rust` invocation.
+        results = [check_package(p) for p in os.listdir(vendor_dir)]
+        return all(results)
+
+    def vendor(self, ignore_modified=False,
+               build_peers_said_large_imports_were_ok=False):
+        self.populate_logger()
+        self.log_manager.enable_unstructured()
+        if not ignore_modified:
+            self.check_modified_files()
+
+        cargo = self._ensure_cargo()
+        if not cargo:
+            return
+
         relative_vendor_dir = 'third_party/rust'
         vendor_dir = mozpath.join(self.topsrcdir, relative_vendor_dir)
         self.log(logging.INFO, 'rm_vendor_dir', {}, 'rm -rf %s' % vendor_dir)
         mozfile.remove(vendor_dir)
         # Once we require a new enough cargo to switch to workspaces, we can
         # just do this once on the workspace root crate.
         crates_and_roots = (
             ('gkrust', 'toolkit/library/rust'),
             ('gkrust-gtest', 'toolkit/library/gtest/rust'),
             ('mozjs_sys', 'js/src'),
         )
         for (lib, crate_root) in crates_and_roots:
             path = mozpath.join(self.topsrcdir, crate_root)
             # We do an |update -p| here to regenerate the Cargo.lock file with minimal changes. See bug 1324462
             self._run_command_in_srcdir(args=[cargo, 'update', '--manifest-path', mozpath.join(path, 'Cargo.toml'), '-p', lib])
             self._run_command_in_srcdir(args=[cargo, 'vendor', '--sync', mozpath.join(path, 'Cargo.lock'), vendor_dir])
+
+        if not self._check_licenses(vendor_dir):
+            self.log(logging.ERROR, 'license_check_failed', {},
+                     '''The changes from `mach vendor rust` will NOT be added to version control.''')
+            sys.exit(1)
+
         self.repository.add_remove_files(vendor_dir)
 
         # 100k is a reasonable upper bound on source file size.
         FILESIZE_LIMIT = 100 * 1024
         large_files = set()
         cumulative_added_size = 0
         for f in self.repository.get_added_files():
             path = mozpath.join(self.topsrcdir, f)