Merge inbound to central, a=merge
authorWes Kocher <wkocher@mozilla.com>
Thu, 16 Mar 2017 13:47:50 -0700
changeset 500240 39607304b774591fa6e32c4b06158d869483c312
parent 500239 cf7510940a4e71f3cbe6e343db6b7f5dba5e1c40 (current diff)
parent 500192 0c50d37c0cf4b9943a36d278ed712b25a15ae8e9 (diff)
child 500241 9104268e5e4b2308f865cfae51492a3c2c0e5331
child 500244 6e973f16d3268ca76708e387914d4f5225993b67
child 500251 7e5ab33694bac07a5fd665cdbb00162a0f74b2bf
child 500270 ea325cddf24df6a053bc235fc892fd1823a22f5d
child 500288 5f9228d1776e98ac4f6e9027c11e9542acb21e56
child 500303 818e3b2706f3bbaa256fc47570e111b4571c9848
child 500306 ddf04a943b9c067ba35b94a347b11d69973ad307
child 500309 85c2939b020e7d4b401382c0d436f16c5af98b5e
child 500312 8ffe74684a7a525e797662fbe1669d6c9aa6c931
child 500313 693f7db68a7fb2b3177904f13116678854be9e50
child 500314 fecd9278c0e2bd00e2a38a26b7335a513c23d453
child 500342 592ff740f1d7a92b041981240d44f06eba85903f
child 500346 cd6d80e9cac9564d8cc60578f7e1b74bae84eb3b
child 500348 3f4f27c0a2bc9f0d200ee646d68666c0c8adda59
child 500350 8e49481a9c9aab28760b27dbf45c213c0875e4a7
child 500352 6edf5eef27d110c00369e7da2562fd39591d7a99
child 500357 a3ab1e637c90091e2deb7b91a9c3cc154750955c
child 500361 b285d25d1264819541c39ea914eabe3d91227dfc
child 500363 10b105c7bc018608bf2e5a32206c8de1039b4d69
child 500366 de17c5e7fce5161fa87c3bb44c120e9cd203e4b7
child 500368 8f82aaffd7857e1f6d77de692fa6ab4b964b6c3e
child 500375 9c7e74288a76acd8fa2d51d447dc78acbde66bed
child 500378 32b95c5a28f35167f64b3882fb0208544dadfa62
child 500383 3b1ea00780283829a17724d6bbdbaae0d311742a
child 500398 fbf98148c1a5ca9b453eb41a4d9e56ab9dda5549
child 500401 0726f65ba7630556e6039d328002c7e7b6759e0c
child 500404 2dc3f8f8a8e0a6d283344b0ff34550bc81e6d12a
child 500407 d3d7ce08e7bf75dea902d5ca31d61a2a42d7b6d5
child 500418 344846fef9bcfb353b18c68985a24971152db027
child 500420 04faaf57fd60f4624aa637d07efd0da6052d2f87
child 500423 269a2765805febfa8640583cf2b2b11049fbd441
child 500434 021b6cfbbc4f65cd3449c14a77a65c20df0a029d
child 500457 32d95add349fbf758e49cd3ad1e30f4065170288
child 500496 b3e7b811214dadb25b71219ff336c1fceeca15f4
child 500506 391c5d27e5304ea301370e73e66d17903011728a
child 500510 7a999d1124204e6a65982ab15af3cd902a486303
child 500513 b4791fea17a8e207cf929a2c976f7dfba344a511
child 500593 eb818ab6e5797c51bd7efabaa378ba54ae596529
child 500660 0ad5f2a610ceba290ccb0276333f6f55b94dc2f3
child 500661 1d8500ee0df6917745b5fe21f1b4135c6239dfec
child 500663 5fe753b43aeb0eabcf7908c23fc30d124168b185
child 500664 d8d7b5b752077035f90c12d90fcf167f47446a9d
child 500670 53f73916ccb2e4e4a5c4abcd7e7fe8aedb5799dc
child 500673 ea535c4ab060ec9f7fcafe070b2d189fcb41d5a7
child 500674 035720556a826ecfa9f4a942fb2d95b5321bd327
child 500675 3f72ce9e3758958a99dacb7b5a96f9c74f9859a4
child 500676 216cea3dc9162f70334a3abe3d169bf79dadf708
child 500677 f37a668c53aca83bfc4651b5eda5235fcc465dd4
child 500683 d1d275b020f4f33bd1b21d59487bf6bdda554f20
child 500684 2f65a5ee732444acb17e0f80a46e8207deb71458
child 500685 76d7e6bedabf97c47e2aa1b3bdbefffe50473c56
child 500687 46a68d26d6ccf384a0c1fdaa91d4c281ecb32c74
child 500713 b8de3d9ed27596383e7c5ccec9fc7f7418f647ab
child 500722 da4e72e6e58911498b5acbde08d535a6b80149a7
child 500765 c5c4fd78b4ccc75e15716e74ca9662e435f9d5a1
child 500816 035d4037bdf98ee9e94dc2b6318c1c6db80340da
child 500873 1a6991fde2ca31c747b6a77d94c6f74d9c99988c
child 500937 3873ea0f1882118ccf8f913e890855af43eaba35
child 500986 b9f152272d7bef1df03d71280a24a4af09ce3950
child 501105 98caf18f7f4ba16c601c24cc983e730c7109610c
child 501109 ee9c1e440bd3b40f9b4c3ee0b9166c4c2b93fb4f
child 501384 4e617a3b4795736ab7f9d0974e483072dab624be
child 501552 df0c529716ad8a70bf9f65397281a33ad74c7d26
child 501809 89f955f7ba207dac5f6e0bd2a28f379a4efd1005
child 502172 a6442a84fac0776586d3a1be437a059274f85995
child 502176 8b83f570fb787ca1d3bb09f30a8388b7792cbc03
child 502272 43ef6295699f14019a77a2e38247cb7da9e34f3b
child 502282 2de4dd986db1e98dc56e57ce682162c7a0304d20
child 503949 ffaf3bef8489eb5267c9ca175215c9a37756390a
child 504032 4a4d04e0f4e17b7f630191f9d43862f8e8d5686d
child 554842 ea169d0da92b9b0c7762e23cd6bdaf30488d7098
child 558478 c7f6241f1288a1422daf852f824a0ab570e18688
push id49652
push userkgupta@mozilla.com
push dateThu, 16 Mar 2017 20:59:23 +0000
reviewersmerge
milestone55.0a1
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)