Merge inbound to mozilla-central. a=merge
authorshindli <shindli@mozilla.com>
Wed, 22 May 2019 18:26:18 +0300
changeset 474943 aaae630f30291056f4f40bbd9e12a917309e401e
parent 474905 a5f7f53421ebce84b0dd4cb3535b49906fdf78ef (current diff)
parent 474942 a479292cb15ca5db48aa280cf3bec0e585ad8ed4 (diff)
child 474987 5bded5d71d405e86d28aed53fbfdea410b716acb
child 475007 bdd76368ddc15c25c10527bd1afd2b8e9c46dbee
push id36051
push usershindli@mozilla.com
push dateWed, 22 May 2019 15:28:21 +0000
treeherdermozilla-central@aaae630f3029 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone69.0a1
first release with
nightly linux32
aaae630f3029 / 69.0a1 / 20190522152821 / files
nightly linux64
aaae630f3029 / 69.0a1 / 20190522152821 / files
nightly mac
aaae630f3029 / 69.0a1 / 20190522152821 / files
nightly win32
aaae630f3029 / 69.0a1 / 20190522152821 / files
nightly win64
aaae630f3029 / 69.0a1 / 20190522152821 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
--- a/image/DecodePool.cpp
+++ b/image/DecodePool.cpp
@@ -21,16 +21,20 @@
 #include "nsIXULRuntime.h"
 
 #include "gfxPrefs.h"
 
 #include "Decoder.h"
 #include "IDecodingTask.h"
 #include "RasterImage.h"
 
+#if defined(XP_WIN)
+#  include <objbase.h>
+#endif
+
 using std::max;
 using std::min;
 
 namespace mozilla {
 namespace image {
 
 ///////////////////////////////////////////////////////////////////////////////
 // DecodePool implementation.
@@ -328,17 +332,32 @@ DecodePool* DecodePool::Singleton() {
   }
 
   return sSingleton;
 }
 
 /* static */
 uint32_t DecodePool::NumberOfCores() { return sNumCores; }
 
-DecodePool::DecodePool() : mMutex("image::DecodePool") {
+#if defined(XP_WIN)
+class IOThreadIniter final : public Runnable {
+ public:
+  explicit IOThreadIniter() : Runnable("image::IOThreadIniter") {}
+
+  NS_IMETHOD Run() override {
+    MOZ_ASSERT(!NS_IsMainThread());
+
+    CoInitialize(nullptr);
+
+    return NS_OK;
+  }
+};
+#endif
+
+DecodePool::DecodePool() : mMutex("image::IOThread") {
   // Determine the number of threads we want.
   int32_t prefLimit = gfxPrefs::ImageMTDecodingLimit();
   uint32_t limit;
   if (prefLimit <= 0) {
     int32_t numCores = NumberOfCores();
     if (numCores <= 1) {
       limit = 1;
     } else if (numCores == 2) {
@@ -374,17 +393,26 @@ DecodePool::DecodePool() : mMutex("image
     idleTimeout = TimeDuration::FromMilliseconds(prefIdleTimeout);
     idleLimit = (limit + 1) / 2;
   }
 
   // Initialize the thread pool.
   mImpl = new DecodePoolImpl(limit, idleLimit, idleTimeout);
 
   // Initialize the I/O thread.
+#if defined(XP_WIN)
+  // On Windows we use the io thread to get icons from the system. Any thread
+  // that makes system calls needs to call CoInitialize. And these system calls
+  // (SHGetFileInfo) should only be called from one thread at a time, in case
+  // we ever create more than on io thread.
+  nsCOMPtr<nsIRunnable> initer = new IOThreadIniter();
+  nsresult rv = NS_NewNamedThread("ImageIO", getter_AddRefs(mIOThread), initer);
+#else
   nsresult rv = NS_NewNamedThread("ImageIO", getter_AddRefs(mIOThread));
+#endif
   MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv) && mIOThread,
                      "Should successfully create image I/O thread");
 
   nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
   if (obsSvc) {
     obsSvc->AddObserver(this, "xpcom-shutdown-threads", false);
   }
 }
--- a/image/Downscaler.cpp
+++ b/image/Downscaler.cpp
@@ -281,17 +281,17 @@ void Downscaler::DownscaleInputLine() {
   mYFilter.GetFilterOffsetAndLength(mCurrentOutLine, &newFilterOffset,
                                     &newFilterLength);
 
   int diff = newFilterOffset - filterOffset;
   MOZ_ASSERT(diff >= 0, "Moving backwards in the filter?");
 
   // Shift the buffer. We're just moving pointers here, so this is cheap.
   mLinesInBuffer -= diff;
-  mLinesInBuffer = min(max(mLinesInBuffer, 0), mWindowCapacity);
+  mLinesInBuffer = std::min(std::max(mLinesInBuffer, 0), mWindowCapacity);
 
   // If we already have enough rows to satisfy the filter, there is no need
   // to swap as we won't be writing more before the next convolution.
   if (filterLength > mLinesInBuffer) {
     for (int32_t i = 0; i < mLinesInBuffer; ++i) {
       swap(mWindow[i], mWindow[filterLength - mLinesInBuffer + i]);
     }
   }
--- a/image/DynamicImage.cpp
+++ b/image/DynamicImage.cpp
@@ -214,24 +214,33 @@ DynamicImage::Draw(gfxContext* aContext,
 
   gfxUtils::DrawPixelSnapped(aContext, mDrawable, SizeDouble(drawableSize),
                              region, SurfaceFormat::B8G8R8A8, aSamplingFilter,
                              aOpacity);
   return ImgDrawResult::SUCCESS;
 }
 
 NS_IMETHODIMP
-DynamicImage::StartDecoding(uint32_t aFlags) { return NS_OK; }
+DynamicImage::StartDecoding(uint32_t aFlags, uint32_t aWhichFrame) {
+  return NS_OK;
+}
 
-bool DynamicImage::StartDecodingWithResult(uint32_t aFlags) { return true; }
+bool DynamicImage::StartDecodingWithResult(uint32_t aFlags,
+                                           uint32_t aWhichFrame) {
+  return true;
+}
 
-bool DynamicImage::RequestDecodeWithResult(uint32_t aFlags) { return true; }
+bool DynamicImage::RequestDecodeWithResult(uint32_t aFlags,
+                                           uint32_t aWhichFrame) {
+  return true;
+}
 
 NS_IMETHODIMP
-DynamicImage::RequestDecodeForSize(const nsIntSize& aSize, uint32_t aFlags) {
+DynamicImage::RequestDecodeForSize(const nsIntSize& aSize, uint32_t aFlags,
+                                   uint32_t aWhichFrame) {
   return NS_OK;
 }
 
 NS_IMETHODIMP
 DynamicImage::LockImage() { return NS_OK; }
 
 NS_IMETHODIMP
 DynamicImage::UnlockImage() { return NS_OK; }
--- a/image/FrozenImage.cpp
+++ b/image/FrozenImage.cpp
@@ -87,16 +87,37 @@ FrozenImage::Draw(gfxContext* aContext, 
                   uint32_t /* aWhichFrame - ignored */,
                   SamplingFilter aSamplingFilter,
                   const Maybe<SVGImageContext>& aSVGContext, uint32_t aFlags,
                   float aOpacity) {
   return InnerImage()->Draw(aContext, aSize, aRegion, FRAME_FIRST,
                             aSamplingFilter, aSVGContext, aFlags, aOpacity);
 }
 
+NS_IMETHODIMP
+FrozenImage::StartDecoding(uint32_t aFlags, uint32_t aWhichFrame) {
+  return InnerImage()->StartDecoding(aFlags, FRAME_FIRST);
+}
+
+bool FrozenImage::StartDecodingWithResult(uint32_t aFlags,
+                                          uint32_t aWhichFrame) {
+  return InnerImage()->StartDecodingWithResult(aFlags, FRAME_FIRST);
+}
+
+bool FrozenImage::RequestDecodeWithResult(uint32_t aFlags,
+                                          uint32_t aWhichFrame) {
+  return InnerImage()->RequestDecodeWithResult(aFlags, FRAME_FIRST);
+}
+
+NS_IMETHODIMP
+FrozenImage::RequestDecodeForSize(const nsIntSize& aSize, uint32_t aFlags,
+                                  uint32_t aWhichFrame) {
+  return InnerImage()->RequestDecodeForSize(aSize, aFlags, FRAME_FIRST);
+}
+
 NS_IMETHODIMP_(void)
 FrozenImage::RequestRefresh(const TimeStamp& aTime) {
   // Do nothing.
 }
 
 NS_IMETHODIMP
 FrozenImage::GetAnimationMode(uint16_t* aAnimationMode) {
   *aAnimationMode = kNormalAnimMode;
--- a/image/FrozenImage.h
+++ b/image/FrozenImage.h
@@ -54,16 +54,23 @@ class FrozenImage : public ImageWrapper 
                           const Maybe<SVGImageContext>& aSVGContext,
                           uint32_t aFlags,
                           layers::ImageContainer** aOutContainer) override;
   NS_IMETHOD_(ImgDrawResult)
   Draw(gfxContext* aContext, const nsIntSize& aSize, const ImageRegion& aRegion,
        uint32_t aWhichFrame, gfx::SamplingFilter aSamplingFilter,
        const Maybe<SVGImageContext>& aSVGContext, uint32_t aFlags,
        float aOpacity) override;
+  NS_IMETHOD StartDecoding(uint32_t aFlags, uint32_t aWhichFrame) override;
+  NS_IMETHOD_(bool)
+  StartDecodingWithResult(uint32_t aFlags, uint32_t aWhichFrame) override;
+  NS_IMETHOD_(bool)
+  RequestDecodeWithResult(uint32_t aFlags, uint32_t aWhichFrame) override;
+  NS_IMETHOD RequestDecodeForSize(const nsIntSize& aSize, uint32_t aFlags,
+                                  uint32_t aWhichFrame) override;
   NS_IMETHOD_(void) RequestRefresh(const TimeStamp& aTime) override;
   NS_IMETHOD GetAnimationMode(uint16_t* aAnimationMode) override;
   NS_IMETHOD SetAnimationMode(uint16_t aAnimationMode) override;
   NS_IMETHOD ResetAnimation() override;
   NS_IMETHOD_(float) GetFrameIndex(uint32_t aWhichFrame) override;
 
  protected:
   explicit FrozenImage(Image* aImage) : ImageWrapper(aImage) {}
--- a/image/ImageWrapper.cpp
+++ b/image/ImageWrapper.cpp
@@ -188,31 +188,34 @@ ImageWrapper::Draw(gfxContext* aContext,
                    SamplingFilter aSamplingFilter,
                    const Maybe<SVGImageContext>& aSVGContext, uint32_t aFlags,
                    float aOpacity) {
   return mInnerImage->Draw(aContext, aSize, aRegion, aWhichFrame,
                            aSamplingFilter, aSVGContext, aFlags, aOpacity);
 }
 
 NS_IMETHODIMP
-ImageWrapper::StartDecoding(uint32_t aFlags) {
-  return mInnerImage->StartDecoding(aFlags);
+ImageWrapper::StartDecoding(uint32_t aFlags, uint32_t aWhichFrame) {
+  return mInnerImage->StartDecoding(aFlags, aWhichFrame);
 }
 
-bool ImageWrapper::StartDecodingWithResult(uint32_t aFlags) {
-  return mInnerImage->StartDecodingWithResult(aFlags);
+bool ImageWrapper::StartDecodingWithResult(uint32_t aFlags,
+                                           uint32_t aWhichFrame) {
+  return mInnerImage->StartDecodingWithResult(aFlags, aWhichFrame);
 }
 
-bool ImageWrapper::RequestDecodeWithResult(uint32_t aFlags) {
-  return mInnerImage->RequestDecodeWithResult(aFlags);
+bool ImageWrapper::RequestDecodeWithResult(uint32_t aFlags,
+                                           uint32_t aWhichFrame) {
+  return mInnerImage->RequestDecodeWithResult(aFlags, aWhichFrame);
 }
 
 NS_IMETHODIMP
-ImageWrapper::RequestDecodeForSize(const nsIntSize& aSize, uint32_t aFlags) {
-  return mInnerImage->RequestDecodeForSize(aSize, aFlags);
+ImageWrapper::RequestDecodeForSize(const nsIntSize& aSize, uint32_t aFlags,
+                                   uint32_t aWhichFrame) {
+  return mInnerImage->RequestDecodeForSize(aSize, aFlags, aWhichFrame);
 }
 
 NS_IMETHODIMP
 ImageWrapper::LockImage() {
   MOZ_ASSERT(NS_IsMainThread(),
              "Main thread to encourage serialization with UnlockImage");
   return mInnerImage->LockImage();
 }
--- a/image/RasterImage.cpp
+++ b/image/RasterImage.cpp
@@ -1041,76 +1041,85 @@ void RasterImage::Discard() {
 
 bool RasterImage::CanDiscard() {
   return mAllSourceData &&
          // Can discard animated images if the pref is set
          (!mAnimationState || gfxPrefs::ImageMemAnimatedDiscardable());
 }
 
 NS_IMETHODIMP
-RasterImage::StartDecoding(uint32_t aFlags) {
+RasterImage::StartDecoding(uint32_t aFlags, uint32_t aWhichFrame) {
   if (mError) {
     return NS_ERROR_FAILURE;
   }
 
   if (!mHasSize) {
     mWantFullDecode = true;
     return NS_OK;
   }
 
   uint32_t flags = (aFlags & FLAG_ASYNC_NOTIFY) | FLAG_SYNC_DECODE_IF_FAST |
                    FLAG_HIGH_QUALITY_SCALING;
-  return RequestDecodeForSize(mSize, flags);
+  return RequestDecodeForSize(mSize, flags, aWhichFrame);
 }
 
-bool RasterImage::StartDecodingWithResult(uint32_t aFlags) {
+bool RasterImage::StartDecodingWithResult(uint32_t aFlags,
+                                          uint32_t aWhichFrame) {
   if (mError) {
     return false;
   }
 
   if (!mHasSize) {
     mWantFullDecode = true;
     return false;
   }
 
   uint32_t flags = (aFlags & FLAG_ASYNC_NOTIFY) | FLAG_SYNC_DECODE_IF_FAST |
                    FLAG_HIGH_QUALITY_SCALING;
-  DrawableSurface surface = RequestDecodeForSizeInternal(mSize, flags);
+  DrawableSurface surface =
+      RequestDecodeForSizeInternal(mSize, flags, aWhichFrame);
   return surface && surface->IsFinished();
 }
 
-bool RasterImage::RequestDecodeWithResult(uint32_t aFlags) {
+bool RasterImage::RequestDecodeWithResult(uint32_t aFlags,
+                                          uint32_t aWhichFrame) {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (mError) {
     return false;
   }
 
   uint32_t flags = aFlags | FLAG_ASYNC_NOTIFY;
-  DrawableSurface surface = RequestDecodeForSizeInternal(mSize, flags);
+  DrawableSurface surface =
+      RequestDecodeForSizeInternal(mSize, flags, aWhichFrame);
   return surface && surface->IsFinished();
 }
 
 NS_IMETHODIMP
-RasterImage::RequestDecodeForSize(const IntSize& aSize, uint32_t aFlags) {
+RasterImage::RequestDecodeForSize(const IntSize& aSize, uint32_t aFlags,
+                                  uint32_t aWhichFrame) {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (mError) {
     return NS_ERROR_FAILURE;
   }
 
-  RequestDecodeForSizeInternal(aSize, aFlags);
+  RequestDecodeForSizeInternal(aSize, aFlags, aWhichFrame);
 
   return NS_OK;
 }
 
-DrawableSurface RasterImage::RequestDecodeForSizeInternal(const IntSize& aSize,
-                                                          uint32_t aFlags) {
+DrawableSurface RasterImage::RequestDecodeForSizeInternal(
+    const IntSize& aSize, uint32_t aFlags, uint32_t aWhichFrame) {
   MOZ_ASSERT(NS_IsMainThread());
 
+  if (aWhichFrame > FRAME_MAX_VALUE) {
+    return DrawableSurface();
+  }
+
   if (mError) {
     return DrawableSurface();
   }
 
   if (!mHasSize) {
     mWantFullDecode = true;
     return DrawableSurface();
   }
@@ -1120,20 +1129,18 @@ DrawableSurface RasterImage::RequestDeco
   // redecoding an image (see bug 845147).
   bool shouldSyncDecodeIfFast =
       !mHasBeenDecoded && (aFlags & FLAG_SYNC_DECODE_IF_FAST);
 
   uint32_t flags =
       shouldSyncDecodeIfFast ? aFlags : aFlags & ~FLAG_SYNC_DECODE_IF_FAST;
 
   // Perform a frame lookup, which will implicitly start decoding if needed.
-  PlaybackType playbackType =
-      mAnimationState ? PlaybackType::eAnimated : PlaybackType::eStatic;
-  LookupResult result =
-      LookupFrame(aSize, flags, playbackType, /* aMarkUsed = */ false);
+  LookupResult result = LookupFrame(aSize, flags, ToPlaybackType(aWhichFrame),
+                                    /* aMarkUsed = */ false);
   return std::move(result.Surface());
 }
 
 static bool LaunchDecodingTask(IDecodingTask* aTask, RasterImage* aImage,
                                uint32_t aFlags, bool aHaveSourceData) {
   if (aHaveSourceData) {
     nsCString uri(aImage->GetURIString());
 
@@ -1692,17 +1699,18 @@ void RasterImage::NotifyDecodeComplete(
       NotifyForLoadEvent(*mLoadProgress);
       mLoadProgress = Nothing();
     }
 
     // If we were a metadata decode and a full decode was requested, do it.
     if (mWantFullDecode) {
       mWantFullDecode = false;
       RequestDecodeForSize(mSize,
-                           DECODE_FLAGS_DEFAULT | FLAG_HIGH_QUALITY_SCALING);
+                           DECODE_FLAGS_DEFAULT | FLAG_HIGH_QUALITY_SCALING,
+                           FRAME_CURRENT);
     }
   }
 }
 
 void RasterImage::ReportDecoderError() {
   nsCOMPtr<nsIConsoleService> consoleService =
       do_GetService(NS_CONSOLESERVICE_CONTRACTID);
   nsCOMPtr<nsIScriptError> errorObject =
--- a/image/RasterImage.h
+++ b/image/RasterImage.h
@@ -447,17 +447,18 @@ class RasterImage final : public ImageRe
   };
 
   // Helpers
   bool CanDiscard();
 
   bool IsOpaque();
 
   DrawableSurface RequestDecodeForSizeInternal(const gfx::IntSize& aSize,
-                                               uint32_t aFlags);
+                                               uint32_t aFlags,
+                                               uint32_t aWhichFrame);
 
  protected:
   explicit RasterImage(nsIURI* aURI = nullptr);
 
   bool ShouldAnimate() override;
 
   friend class ImageFactory;
 };
--- a/image/VectorImage.cpp
+++ b/image/VectorImage.cpp
@@ -1197,33 +1197,36 @@ void VectorImage::Show(gfxDrawable* aDra
 void VectorImage::RecoverFromLossOfSurfaces() {
   NS_WARNING("An imgFrame became invalid. Attempting to recover...");
 
   // Discard all existing frames, since they're probably all now invalid.
   SurfaceCache::RemoveImage(ImageKey(this));
 }
 
 NS_IMETHODIMP
-VectorImage::StartDecoding(uint32_t aFlags) {
+VectorImage::StartDecoding(uint32_t aFlags, uint32_t aWhichFrame) {
   // Nothing to do for SVG images
   return NS_OK;
 }
 
-bool VectorImage::StartDecodingWithResult(uint32_t aFlags) {
+bool VectorImage::StartDecodingWithResult(uint32_t aFlags,
+                                          uint32_t aWhichFrame) {
   // SVG images are ready to draw when they are loaded
   return mIsFullyLoaded;
 }
 
-bool VectorImage::RequestDecodeWithResult(uint32_t aFlags) {
+bool VectorImage::RequestDecodeWithResult(uint32_t aFlags,
+                                          uint32_t aWhichFrame) {
   // SVG images are ready to draw when they are loaded
   return mIsFullyLoaded;
 }
 
 NS_IMETHODIMP
-VectorImage::RequestDecodeForSize(const nsIntSize& aSize, uint32_t aFlags) {
+VectorImage::RequestDecodeForSize(const nsIntSize& aSize, uint32_t aFlags,
+                                  uint32_t aWhichFrame) {
   // Nothing to do for SVG images, though in theory we could rasterize to the
   // provided size ahead of time if we supported off-main-thread SVG
   // rasterization...
   return NS_OK;
 }
 
 //******************************************************************************
 
--- a/image/decoders/icon/win/moz.build
+++ b/image/decoders/icon/win/moz.build
@@ -3,9 +3,13 @@
 # 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/.
 
 SOURCES += [
     'nsIconChannel.cpp',
 ]
 
+LOCAL_INCLUDES += [
+    '/image',
+]
+
 FINAL_LIBRARY = 'xul'
--- a/image/decoders/icon/win/nsIconChannel.cpp
+++ b/image/decoders/icon/win/nsIconChannel.cpp
@@ -1,48 +1,53 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  *
  * 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 "mozilla/ArrayUtils.h"
+#include "mozilla/Monitor.h"
 
 #include "nsIconChannel.h"
 #include "nsIIconURI.h"
 #include "nsIServiceManager.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsString.h"
 #include "nsReadableUtils.h"
 #include "nsMimeTypes.h"
 #include "nsMemory.h"
 #include "nsIStringStream.h"
 #include "nsIURL.h"
-#include "nsIOutputStream.h"
 #include "nsIPipe.h"
 #include "nsNetCID.h"
 #include "nsIFile.h"
 #include "nsIFileURL.h"
 #include "nsIMIMEService.h"
 #include "nsCExternalHandlerService.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsProxyRelease.h"
 #include "nsContentSecurityManager.h"
 #include "nsContentUtils.h"
 #include "nsNetUtil.h"
+#include "nsThreadUtils.h"
+
+#include "Decoder.h"
+#include "DecodePool.h"
 
 // we need windows.h to read out registry information...
 #include <windows.h>
 #include <shellapi.h>
 #include <shlobj.h>
 #include <objbase.h>
 #include <wchar.h>
 
 using namespace mozilla;
+using namespace mozilla::image;
 
 struct ICONFILEHEADER {
   uint16_t ifhReserved;
   uint16_t ifhType;
   uint16_t ifhCount;
 };
 
 struct ICONENTRY {
@@ -56,16 +61,105 @@ struct ICONENTRY {
   uint32_t ieFileOffset;
 };
 
 // Match stock icons with names
 static SHSTOCKICONID GetStockIconIDForName(const nsACString& aStockName) {
   return aStockName.EqualsLiteral("uac-shield") ? SIID_SHIELD : SIID_INVALID;
 }
 
+class nsIconChannel::IconAsyncOpenTask final : public Runnable {
+ public:
+  IconAsyncOpenTask(nsIconChannel* aChannel, nsIEventTarget* aTarget,
+                    nsCOMPtr<nsIFile>&& aLocalFile, nsAutoString& aPath,
+                    UINT aInfoFlags)
+      : Runnable("IconAsyncOpenTask"),
+        mChannel(aChannel),
+
+        mTarget(aTarget),
+        mLocalFile(std::move(aLocalFile)),
+        mPath(aPath),
+        mInfoFlags(aInfoFlags) {}
+
+  NS_IMETHOD Run() override;
+
+ private:
+  RefPtr<nsIconChannel> mChannel;
+  nsCOMPtr<nsIEventTarget> mTarget;
+  nsCOMPtr<nsIFile> mLocalFile;
+  nsAutoString mPath;
+  UINT mInfoFlags;
+};
+
+NS_IMETHODIMP nsIconChannel::IconAsyncOpenTask::Run() {
+  HICON hIcon = nullptr;
+  nsresult rv =
+      mChannel->GetHIconFromFile(mLocalFile, mPath, mInfoFlags, &hIcon);
+  nsCOMPtr<nsIRunnable> task = NewRunnableMethod<HICON, nsresult>(
+      "nsIconChannel::FinishAsyncOpen", mChannel,
+      &nsIconChannel::FinishAsyncOpen, hIcon, rv);
+  mTarget->Dispatch(task.forget(), NS_DISPATCH_NORMAL);
+  return NS_OK;
+}
+
+class nsIconChannel::IconSyncOpenTask final : public Runnable {
+ public:
+  IconSyncOpenTask(nsIconChannel* aChannel, nsIEventTarget* aTarget,
+                   nsCOMPtr<nsIFile>&& aLocalFile, nsAutoString& aPath,
+                   UINT aInfoFlags)
+      : Runnable("IconSyncOpenTask"),
+        mMonitor("IconSyncOpenTask"),
+        mDone(false),
+        mChannel(aChannel),
+        mTarget(aTarget),
+        mLocalFile(std::move(aLocalFile)),
+        mPath(aPath),
+        mInfoFlags(aInfoFlags),
+        mHIcon(nullptr),
+        mRv(NS_OK) {}
+
+  NS_IMETHOD Run() override;
+
+  Monitor& GetMonitor() { return mMonitor; }
+  bool Done() const {
+    mMonitor.AssertCurrentThreadOwns();
+    return mDone;
+  }
+  HICON GetHIcon() const {
+    mMonitor.AssertCurrentThreadOwns();
+    return mHIcon;
+  }
+  nsresult GetRv() const {
+    mMonitor.AssertCurrentThreadOwns();
+    return mRv;
+  }
+
+ private:
+  Monitor mMonitor;
+  bool mDone;
+  // Parameters in
+  RefPtr<nsIconChannel> mChannel;
+  nsCOMPtr<nsIEventTarget> mTarget;
+  nsCOMPtr<nsIFile> mLocalFile;
+  nsAutoString mPath;
+  UINT mInfoFlags;
+  // Return values
+  HICON mHIcon;
+  nsresult mRv;
+};
+
+NS_IMETHODIMP
+nsIconChannel::IconSyncOpenTask::Run() {
+  MonitorAutoLock lock(mMonitor);
+  mRv = mChannel->GetHIconFromFile(mLocalFile, mPath, mInfoFlags, &mHIcon);
+  mDone = true;
+  mMonitor.NotifyAll();
+  return NS_OK;
+}
+
 // nsIconChannel methods
 nsIconChannel::nsIconChannel() {}
 
 nsIconChannel::~nsIconChannel() {
   if (mLoadInfo) {
     NS_ReleaseOnMainThreadSystemGroup("nsIconChannel::mLoadInfo",
                                       mLoadInfo.forget());
   }
@@ -157,17 +251,23 @@ nsIconChannel::GetURI(nsIURI** aURI) {
 
 NS_IMETHODIMP
 nsIconChannel::Open(nsIInputStream** aStream) {
   nsCOMPtr<nsIStreamListener> listener;
   nsresult rv =
       nsContentSecurityManager::doContentSecurityCheck(this, listener);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  return MakeInputStream(aStream, false);
+  HICON hIcon = nullptr;
+  rv = GetHIcon(/* aNonBlocking */ false, &hIcon);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  return MakeInputStream(aStream, /* aNonBlocking */ false, hIcon);
 }
 
 nsresult nsIconChannel::ExtractIconInfoFromUrl(nsIFile** aLocalFile,
                                                uint32_t* aDesiredImageSize,
                                                nsCString& aContentType,
                                                nsCString& aFileExtension) {
   nsresult rv = NS_OK;
   nsCOMPtr<nsIMozIconURI> iconURI(do_QueryInterface(mUrl, &rv));
@@ -186,16 +286,49 @@ nsresult nsIconChannel::ExtractIconInfoF
 
   nsCOMPtr<nsIFile> file;
   rv = fileURL->GetFile(getter_AddRefs(file));
   if (NS_FAILED(rv) || !file) return NS_OK;
 
   return file->Clone(aLocalFile);
 }
 
+void nsIconChannel::OnAsyncError(nsresult aStatus) {
+  OnStartRequest(this);
+  OnStopRequest(this, aStatus);
+}
+
+void nsIconChannel::FinishAsyncOpen(HICON aIcon, nsresult aStatus) {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (NS_FAILED(aStatus)) {
+    OnAsyncError(aStatus);
+    return;
+  }
+
+  nsCOMPtr<nsIInputStream> inStream;
+  nsresult rv = MakeInputStream(getter_AddRefs(inStream),
+                                /* aNonBlocking */ true, aIcon);
+  if (NS_FAILED(rv)) {
+    OnAsyncError(rv);
+    return;
+  }
+
+  rv = mPump->Init(inStream, 0, 0, false, mListenerTarget);
+  if (NS_FAILED(rv)) {
+    OnAsyncError(rv);
+    return;
+  }
+
+  rv = mPump->AsyncRead(this, nullptr);
+  if (NS_FAILED(rv)) {
+    OnAsyncError(rv);
+  }
+}
+
 NS_IMETHODIMP
 nsIconChannel::AsyncOpen(nsIStreamListener* aListener) {
   nsCOMPtr<nsIStreamListener> listener = aListener;
   nsresult rv =
       nsContentSecurityManager::doContentSecurityCheck(this, listener);
   if (NS_FAILED(rv)) {
     mCallbacks = nullptr;
     return rv;
@@ -205,44 +338,51 @@ nsIconChannel::AsyncOpen(nsIStreamListen
       !mLoadInfo || mLoadInfo->GetSecurityMode() == 0 ||
           mLoadInfo->GetInitialSecurityCheckDone() ||
           (mLoadInfo->GetSecurityMode() ==
                nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL &&
            nsContentUtils::IsSystemPrincipal(mLoadInfo->LoadingPrincipal())),
       "security flags in loadInfo but doContentSecurityCheck() not called");
 
   nsCOMPtr<nsIInputStream> inStream;
-  rv = MakeInputStream(getter_AddRefs(inStream), true);
+  rv = EnsurePipeCreated(/* aIconSize */ 0, /* aNonBlocking */ true);
   if (NS_FAILED(rv)) {
     mCallbacks = nullptr;
     return rv;
   }
 
-  // Init our streampump
-  nsCOMPtr<nsIEventTarget> target = nsContentUtils::GetEventTargetByLoadInfo(
+  mListenerTarget = nsContentUtils::GetEventTargetByLoadInfo(
       mLoadInfo, mozilla::TaskCategory::Other);
-  rv = mPump->Init(inStream, 0, 0, false, target);
+  if (!mListenerTarget) {
+    mListenerTarget = do_GetMainThread();
+  }
+
+  // If we pass aNonBlocking as true, GetHIcon will always have dispatched
+  // upon success.
+  HICON hIcon = nullptr;
+  rv = GetHIcon(/* aNonBlocking */ true, &hIcon);
   if (NS_FAILED(rv)) {
     mCallbacks = nullptr;
+    mInputStream = nullptr;
+    mOutputStream = nullptr;
+    mListenerTarget = nullptr;
     return rv;
   }
 
-  rv = mPump->AsyncRead(this, nullptr);
-  if (NS_SUCCEEDED(rv)) {
-    // Store our real listener
-    mListener = aListener;
-    // Add ourself to the load group, if available
-    if (mLoadGroup) {
-      mLoadGroup->AddRequest(this, nullptr);
-    }
-  } else {
-    mCallbacks = nullptr;
+  // We shouldn't have the icon yet if it is non-blocking.
+  MOZ_ASSERT(!hIcon);
+
+  // Add ourself to the load group, if available
+  if (mLoadGroup) {
+    mLoadGroup->AddRequest(this, nullptr);
   }
 
-  return rv;
+  // Store our real listener
+  mListener = aListener;
+  return NS_OK;
 }
 
 static DWORD GetSpecialFolderIcon(nsIFile* aFile, int aFolder,
                                   SHFILEINFOW* aSFI, UINT aInfoFlags) {
   DWORD shellResult = 0;
 
   if (!aFile) {
     return shellResult;
@@ -271,28 +411,27 @@ static DWORD GetSpecialFolderIcon(nsIFil
   CoTaskMemFree(idList);
   return shellResult;
 }
 
 static UINT GetSizeInfoFlag(uint32_t aDesiredImageSize) {
   return (UINT)(aDesiredImageSize > 16 ? SHGFI_SHELLICONSIZE : SHGFI_SMALLICON);
 }
 
-nsresult nsIconChannel::GetHIconFromFile(HICON* hIcon) {
+nsresult nsIconChannel::GetHIconFromFile(bool aNonBlocking, HICON* hIcon) {
   nsCString contentType;
   nsCString fileExt;
   nsCOMPtr<nsIFile> localFile;  // file we want an icon for
   uint32_t desiredImageSize;
   nsresult rv = ExtractIconInfoFromUrl(getter_AddRefs(localFile),
                                        &desiredImageSize, contentType, fileExt);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // if the file exists, we are going to use it's real attributes...
   // otherwise we only want to use it for it's extension...
-  SHFILEINFOW sfi;
   UINT infoFlags = SHGFI_ICON;
 
   bool fileExists = false;
 
   nsAutoString filePath;
   CopyASCIItoUTF16(fileExt, filePath);
   if (localFile) {
     rv = localFile->Normalize();
@@ -329,44 +468,71 @@ nsresult nsIconChannel::GetHIconFromFile
     nsAutoCString defFileExt;
     mimeService->GetPrimaryExtension(contentType, fileExt, defFileExt);
     // If the mime service does not know about this mime type, we show
     // the generic icon.
     // In any case, we need to insert a '.' before the extension.
     filePath = NS_LITERAL_STRING(".") + NS_ConvertUTF8toUTF16(defFileExt);
   }
 
+  if (aNonBlocking) {
+    RefPtr<nsIEventTarget> target = DecodePool::Singleton()->GetIOEventTarget();
+    RefPtr<IconAsyncOpenTask> task = new IconAsyncOpenTask(
+        this, mListenerTarget, std::move(localFile), filePath, infoFlags);
+    target->Dispatch(task.forget(), NS_DISPATCH_NORMAL);
+    return NS_OK;
+  }
+
+  // We cannot call SHGetFileInfo on more than one thread (at a time), so it
+  // must be called on the same thread every time, even now when we need sync
+  // behaviour. So we synchronously wait on the other thread to finish.
+  RefPtr<nsIEventTarget> target = DecodePool::Singleton()->GetIOEventTarget();
+  RefPtr<IconSyncOpenTask> task = new IconSyncOpenTask(
+      this, mListenerTarget, std::move(localFile), filePath, infoFlags);
+  MonitorAutoLock lock(task->GetMonitor());
+  target->Dispatch(task, NS_DISPATCH_NORMAL);
+  do {
+    task->GetMonitor().Wait();
+  } while (!task->Done());
+  *hIcon = task->GetHIcon();
+  return task->GetRv();
+}
+
+nsresult nsIconChannel::GetHIconFromFile(nsIFile* aLocalFile,
+                                         const nsAutoString& aPath,
+                                         UINT aInfoFlags, HICON* hIcon) {
+  SHFILEINFOW sfi;
+
   // Is this the "Desktop" folder?
   DWORD shellResult =
-      GetSpecialFolderIcon(localFile, CSIDL_DESKTOP, &sfi, infoFlags);
+      GetSpecialFolderIcon(aLocalFile, CSIDL_DESKTOP, &sfi, aInfoFlags);
   if (!shellResult) {
     // Is this the "My Documents" folder?
     shellResult =
-        GetSpecialFolderIcon(localFile, CSIDL_PERSONAL, &sfi, infoFlags);
+        GetSpecialFolderIcon(aLocalFile, CSIDL_PERSONAL, &sfi, aInfoFlags);
   }
 
   // There are other "Special Folders" and Namespace entities that we
   // are not fetching icons for, see:
   // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/
   //        shellcc/platform/shell/reference/enums/csidl.asp
   // If we ever need to get them, code to do so would be inserted here.
 
   // Not a special folder, or something else failed above.
   if (!shellResult) {
-    shellResult = ::SHGetFileInfoW(filePath.get(), FILE_ATTRIBUTE_ARCHIVE, &sfi,
-                                   sizeof(sfi), infoFlags);
+    shellResult = ::SHGetFileInfoW(aPath.get(), FILE_ATTRIBUTE_ARCHIVE, &sfi,
+                                   sizeof(sfi), aInfoFlags);
   }
 
-  if (shellResult && sfi.hIcon) {
-    *hIcon = sfi.hIcon;
-  } else {
-    rv = NS_ERROR_NOT_AVAILABLE;
+  if (!shellResult || !sfi.hIcon) {
+    return NS_ERROR_NOT_AVAILABLE;
   }
 
-  return rv;
+  *hIcon = sfi.hIcon;
+  return NS_OK;
 }
 
 nsresult nsIconChannel::GetStockHIcon(nsIMozIconURI* aIconURI, HICON* hIcon) {
   nsresult rv = NS_OK;
 
   uint32_t desiredImageSize;
   aIconURI->GetImageSize(&desiredImageSize);
   nsAutoCString stockIcon;
@@ -449,42 +615,53 @@ static BITMAPINFO* CreateBitmapInfo(BITM
       sizeof(BITMAPINFOHEADER) + aColorTableSize, mozilla::fallible);
   if (bmi) {
     memcpy(bmi, aHeader, sizeof(BITMAPINFOHEADER));
     memset(bmi->bmiColors, 0, aColorTableSize);
   }
   return bmi;
 }
 
-nsresult nsIconChannel::MakeInputStream(nsIInputStream** _retval,
-                                        bool aNonBlocking) {
+nsresult nsIconChannel::EnsurePipeCreated(uint32_t aIconSize,
+                                          bool aNonBlocking) {
+  if (mInputStream || mOutputStream) {
+    return NS_OK;
+  }
+
+  return NS_NewPipe(getter_AddRefs(mInputStream), getter_AddRefs(mOutputStream),
+                    aIconSize, aIconSize > 0 ? aIconSize : UINT32_MAX,
+                    aNonBlocking);
+}
+
+nsresult nsIconChannel::GetHIcon(bool aNonBlocking, HICON* aIcon) {
   // Check whether the icon requested's a file icon or a stock icon
   nsresult rv = NS_ERROR_NOT_AVAILABLE;
 
   // GetDIBits does not exist on windows mobile.
-  HICON hIcon = nullptr;
-
   nsCOMPtr<nsIMozIconURI> iconURI(do_QueryInterface(mUrl, &rv));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsAutoCString stockIcon;
   iconURI->GetStockIcon(stockIcon);
   if (!stockIcon.IsEmpty()) {
-    rv = GetStockHIcon(iconURI, &hIcon);
-  } else {
-    rv = GetHIconFromFile(&hIcon);
+    return GetStockHIcon(iconURI, aIcon);
   }
 
-  NS_ENSURE_SUCCESS(rv, rv);
+  return GetHIconFromFile(aNonBlocking, aIcon);
+}
 
-  if (hIcon) {
+nsresult nsIconChannel::MakeInputStream(nsIInputStream** _retval,
+                                        bool aNonBlocking, HICON aIcon) {
+  nsresult rv = NS_ERROR_FAILURE;
+
+  if (aIcon) {
     // we got a handle to an icon. Now we want to get a bitmap for the icon
     // using GetIconInfo....
     ICONINFO iconInfo;
-    if (GetIconInfo(hIcon, &iconInfo)) {
+    if (GetIconInfo(aIcon, &iconInfo)) {
       // we got the bitmaps, first find out their size
       HDC hDC = CreateCompatibleDC(nullptr);  // get a device context for
                                               // the screen.
       BITMAPINFOHEADER maskHeader = {sizeof(BITMAPINFOHEADER)};
       BITMAPINFOHEADER colorHeader = {sizeof(BITMAPINFOHEADER)};
       int colorTableSize, maskTableSize;
       if (GetDIBits(hDC, iconInfo.hbmMask, 0, 0, nullptr,
                     (BITMAPINFO*)&maskHeader, DIB_RGB_COLORS) &&
@@ -555,47 +732,46 @@ nsresult nsIconChannel::MakeInputStream(
             whereTo += colorHeader.biSizeImage;
 
             // and finally the AND bitmap data (maskHeader)
             BITMAPINFO* maskInfo = CreateBitmapInfo(&maskHeader, maskTableSize);
             if (maskInfo &&
                 GetDIBits(hDC, iconInfo.hbmMask, 0, maskHeader.biHeight,
                           whereTo, maskInfo, DIB_RGB_COLORS)) {
               // Now, create a pipe and stuff our data into it
-              nsCOMPtr<nsIInputStream> inStream;
-              nsCOMPtr<nsIOutputStream> outStream;
-              rv = NS_NewPipe(getter_AddRefs(inStream),
-                              getter_AddRefs(outStream), iconSize, iconSize,
-                              aNonBlocking);
+              rv = EnsurePipeCreated(iconSize, aNonBlocking);
               if (NS_SUCCEEDED(rv)) {
                 uint32_t written;
-                rv = outStream->Write(buffer.get(), iconSize, &written);
+                rv = mOutputStream->Write(buffer.get(), iconSize, &written);
                 if (NS_SUCCEEDED(rv)) {
-                  NS_ADDREF(*_retval = inStream);
+                  NS_ADDREF(*_retval = mInputStream);
                 }
               }
 
             }  // if we got bitmap bits
             delete maskInfo;
           }  // if we got mask bits
           delete colorInfo;
         }  // if we allocated the buffer
       }    // if we got mask size
 
       DeleteDC(hDC);
       DeleteObject(iconInfo.hbmColor);
       DeleteObject(iconInfo.hbmMask);
     }  // if we got icon info
-    DestroyIcon(hIcon);
+    DestroyIcon(aIcon);
   }  // if we got an hIcon
 
   // If we didn't make a stream, then fail.
   if (!*_retval && NS_SUCCEEDED(rv)) {
     rv = NS_ERROR_NOT_AVAILABLE;
   }
+
+  mInputStream = nullptr;
+  mOutputStream = nullptr;
   return rv;
 }
 
 NS_IMETHODIMP
 nsIconChannel::GetContentType(nsACString& aContentType) {
   aContentType.AssignLiteral(IMAGE_ICO);
   return NS_OK;
 }
@@ -723,16 +899,17 @@ nsIconChannel::OnStopRequest(nsIRequest*
 
   // Remove from load group
   if (mLoadGroup) {
     mLoadGroup->RemoveRequest(this, nullptr, aStatus);
   }
 
   // Drop notification callbacks to prevent cycles.
   mCallbacks = nullptr;
+  mListenerTarget = nullptr;
 
   return NS_OK;
 }
 
 // nsIStreamListener methods
 NS_IMETHODIMP
 nsIconChannel::OnDataAvailable(nsIRequest* aRequest, nsIInputStream* aStream,
                                uint64_t aOffset, uint32_t aCount) {
--- a/image/decoders/icon/win/nsIconChannel.h
+++ b/image/decoders/icon/win/nsIconChannel.h
@@ -13,16 +13,17 @@
 #include "nsString.h"
 #include "nsIChannel.h"
 #include "nsILoadGroup.h"
 #include "nsILoadInfo.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIURI.h"
 #include "nsIInputStreamPump.h"
+#include "nsIOutputStream.h"
 #include "nsIStreamListener.h"
 #include "nsIIconURI.h"
 
 #include <windows.h>
 
 class nsIFile;
 
 class nsIconChannel final : public nsIChannel, public nsIStreamListener {
@@ -35,31 +36,45 @@ class nsIconChannel final : public nsICh
   NS_DECL_NSIREQUESTOBSERVER
   NS_DECL_NSISTREAMLISTENER
 
   nsIconChannel();
 
   nsresult Init(nsIURI* uri);
 
  protected:
+  class IconAsyncOpenTask;
+  class IconSyncOpenTask;
+
+  void OnAsyncError(nsresult aStatus);
+  void FinishAsyncOpen(HICON aIcon, nsresult aStatus);
+  nsresult EnsurePipeCreated(uint32_t aIconSize, bool aNonBlocking);
+
   nsCOMPtr<nsIURI> mUrl;
   nsCOMPtr<nsIURI> mOriginalURI;
   nsCOMPtr<nsILoadGroup> mLoadGroup;
   nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
   nsCOMPtr<nsISupports> mOwner;
   nsCOMPtr<nsILoadInfo> mLoadInfo;
 
   nsCOMPtr<nsIInputStreamPump> mPump;
+  nsCOMPtr<nsIInputStream> mInputStream;
+  nsCOMPtr<nsIOutputStream> mOutputStream;
   nsCOMPtr<nsIStreamListener> mListener;
+  nsCOMPtr<nsIEventTarget> mListenerTarget;
 
   nsresult ExtractIconInfoFromUrl(nsIFile** aLocalFile,
                                   uint32_t* aDesiredImageSize,
                                   nsCString& aContentType,
                                   nsCString& aFileExtension);
-  nsresult GetHIconFromFile(HICON* hIcon);
-  nsresult MakeInputStream(nsIInputStream** _retval, bool nonBlocking);
+  nsresult GetHIcon(bool aNonBlocking, HICON* hIcon);
+  nsresult GetHIconFromFile(bool aNonBlocking, HICON* hIcon);
+  nsresult GetHIconFromFile(nsIFile* aLocalFile, const nsAutoString& aPath,
+                            UINT aInfoFlags, HICON* hIcon);
+  nsresult MakeInputStream(nsIInputStream** _retval, bool aNonBlocking,
+                           HICON aIcon);
 
   // Functions specific to Vista and above
  protected:
   nsresult GetStockHIcon(nsIMozIconURI* aIconURI, HICON* hIcon);
 };
 
 #endif  // mozilla_image_encoders_icon_win_nsIconChannel_h
--- a/image/imgIContainer.idl
+++ b/image/imgIContainer.idl
@@ -462,51 +462,80 @@ interface imgIContainer : nsISupports
   /*
    * Ensures that an image is decoding. Calling this function guarantees that
    * the image will at some point fire off decode notifications. Images that
    * can be decoded "quickly" according to some heuristic will be decoded
    * synchronously.
    *
    * @param aFlags Flags of the FLAG_* variety. Only FLAG_ASYNC_NOTIFY
    *               is accepted; all others are ignored.
+   * @param aWhichFrame Frame specifier of the FRAME_* variety.
    */
-  [noscript] void startDecoding(in uint32_t aFlags);
+  [noscript] void startDecoding(in uint32_t aFlags, in uint32_t aWhichFrame);
+
+%{C++
+  nsresult StartDecoding(uint32_t aFlags) {
+    return StartDecoding(aFlags, FRAME_CURRENT);
+  }
+%}
 
   /*
    * Exactly like startDecoding above except returns whether the current frame
    * of the image is complete or not.
    *
    * @param aFlags Flags of the FLAG_* variety. Only FLAG_ASYNC_NOTIFY
    *               is accepted; all others are ignored.
+   * @param aWhichFrame Frame specifier of the FRAME_* variety.
    */
-  [noscript, notxpcom] boolean startDecodingWithResult(in uint32_t aFlags);
+  [noscript, notxpcom] boolean startDecodingWithResult(in uint32_t aFlags, in uint32_t aWhichFrame);
+
+%{C++
+  bool StartDecodingWithResult(uint32_t aFlags) {
+    return StartDecodingWithResult(aFlags, FRAME_CURRENT);
+  }
+%}
 
   /*
    * This method triggers decoding for an image, but unlike startDecoding() it
    * enables the caller to provide more detailed information about the decode
    * request.
    *
    * @param aFlags Flags of the FLAG_* variety.
+   * @param aWhichFrame Frame specifier of the FRAME_* variety.
    * @return True there is a surface that satisfies the request and it is
    *         fully decoded, else false.
    */
-  [noscript, notxpcom] boolean requestDecodeWithResult(in uint32_t aFlags);
+  [noscript, notxpcom] boolean requestDecodeWithResult(in uint32_t aFlags, in uint32_t aWhichFrame);
+
+%{C++
+  bool RequestDecodeWithResult(uint32_t aFlags) {
+    return RequestDecodeWithResult(aFlags, FRAME_CURRENT);
+  }
+%}
 
   /*
    * This method triggers decoding for an image, but unlike startDecoding() it
    * enables the caller to provide more detailed information about the decode
    * request.
    *
    * @param aSize The size to which the image should be scaled while decoding,
    *              if possible. If the image cannot be scaled to this size while
    *              being decoded, it will be decoded at its intrinsic size.
    * @param aFlags Flags of the FLAG_* variety.
+   * @param aWhichFrame Frame specifier of the FRAME_* variety.
    */
   [noscript] void requestDecodeForSize([const] in nsIntSize aSize,
-                                       in uint32_t aFlags);
+                                       in uint32_t aFlags,
+                                       in uint32_t aWhichFrame);
+
+%{C++
+  nsresult RequestDecodeForSize(const nsIntSize& aSize, uint32_t aFlags) {
+    return RequestDecodeForSize(aSize, aFlags, FRAME_CURRENT);
+  }
+%}
 
   /**
     * Increments the lock count on the image. An image will not be discarded
     * as long as the lock count is nonzero. Note that it is still possible for
     * the image to be undecoded if decode-on-draw is enabled and the image
     * was never drawn.
     *
     * Upon instantiation images have a lock count of zero.
--- a/image/moz.build
+++ b/image/moz.build
@@ -63,17 +63,16 @@ EXPORTS.mozilla.image += [
     'RecyclingSourceSurface.h',
 ]
 
 UNIFIED_SOURCES += [
     'AnimationFrameBuffer.cpp',
     'AnimationSurfaceProvider.cpp',
     'ClippedImage.cpp',
     'DecodedSurfaceProvider.cpp',
-    'DecodePool.cpp',
     'Decoder.cpp',
     'DecoderFactory.cpp',
     'DynamicImage.cpp',
     'FrameAnimator.cpp',
     'FrozenImage.cpp',
     'IDecodingTask.cpp',
     'Image.cpp',
     'ImageCacheKey.cpp',
@@ -97,16 +96,21 @@ UNIFIED_SOURCES += [
     'SurfaceCacheUtils.cpp',
     'SurfacePipe.cpp',
     'SVGDocumentWrapper.cpp',
     'VectorImage.cpp',
 ]
 if CONFIG['MOZ_ENABLE_SKIA']:
     UNIFIED_SOURCES += [ 'Downscaler.cpp']
 
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
+    SOURCES += [ 'DecodePool.cpp']
+else:
+    UNIFIED_SOURCES += [ 'DecodePool.cpp']
+
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
     # Because SVGDocumentWrapper.cpp includes "mozilla/dom/SVGSVGElement.h"
     '/dom/base',
     '/dom/svg',
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -3528,22 +3528,17 @@ nsDocumentViewer::Print(nsIPrintSettings
     }
     mPrintJob = printJob;
   }
   if (printJob->HasPrintCallbackCanvas()) {
     // Postpone the 'afterprint' event until after the mozPrintCallback
     // callbacks have been called:
     mAutoBeforeAndAfterPrint = std::move(autoBeforeAndAfterPrint);
   }
-  dom::Element* root = mDocument->GetRootElement();
-  if (root &&
-      root->HasAttr(kNameSpaceID_None, nsGkAtoms::mozdisallowselectionprint)) {
-    printJob->SetDisallowSelectionPrint(true);
-  }
-  rv = printJob->Print(aPrintSettings, aWebProgressListener);
+  rv = printJob->Print(mDocument, aPrintSettings, aWebProgressListener);
   if (NS_FAILED(rv)) {
     OnDonePrinting();
   }
   return rv;
 }
 
 NS_IMETHODIMP
 nsDocumentViewer::PrintPreview(nsIPrintSettings* aPrintSettings,
@@ -3613,22 +3608,16 @@ nsDocumentViewer::PrintPreview(nsIPrintS
     }
     mPrintJob = printJob;
   }
   if (autoBeforeAndAfterPrint && printJob->HasPrintCallbackCanvas()) {
     // Postpone the 'afterprint' event until after the mozPrintCallback
     // callbacks have been called:
     mAutoBeforeAndAfterPrint = std::move(autoBeforeAndAfterPrint);
   }
-  dom::Element* root = doc->GetRootElement();
-  if (root &&
-      root->HasAttr(kNameSpaceID_None, nsGkAtoms::mozdisallowselectionprint)) {
-    PR_PL(("PrintPreview: found mozdisallowselectionprint"));
-    printJob->SetDisallowSelectionPrint(true);
-  }
   rv = printJob->PrintPreview(doc, aPrintSettings, aWebProgressListener);
   mPrintPreviewZoomed = false;
   if (NS_FAILED(rv)) {
     OnDonePrinting();
   }
   return rv;
 #  else
   return NS_ERROR_FAILURE;
--- a/layout/generic/ReflowInput.cpp
+++ b/layout/generic/ReflowInput.cpp
@@ -852,16 +852,27 @@ void ReflowInput::InitDynamicReflowRoot(
 
   // If we participate in a container's block reflow context, or margins
   // can collapse through us, we can't be a dynamic reflow root.
   if (canBeDynamicReflowRoot && mFrame->IsBlockFrameOrSubclass() &&
       !mFrame->HasAllStateBits(NS_BLOCK_FLOAT_MGR | NS_BLOCK_MARGIN_ROOT)) {
     canBeDynamicReflowRoot = false;
   }
 
+  // Subgrids are never reflow roots, but 'contain:layout/paint' prevents
+  // creating a subgrid in the first place.
+  if (canBeDynamicReflowRoot &&
+      (mStylePosition->GridTemplateColumns().mIsSubgrid ||
+       mStylePosition->GridTemplateRows().mIsSubgrid) &&
+      !(mStyleDisplay->IsContainLayout() || mStyleDisplay->IsContainPaint())) {
+    // NOTE: we could check that 'display' of our content's primary frame is
+    // '[inline-]grid' here but that's probably not worth it in practice.
+    canBeDynamicReflowRoot = false;
+  }
+
   if (canBeDynamicReflowRoot) {
     mFrame->AddStateBits(NS_FRAME_DYNAMIC_REFLOW_ROOT);
   } else {
     mFrame->RemoveStateBits(NS_FRAME_DYNAMIC_REFLOW_ROOT);
   }
 }
 
 nscoord ReflowInput::GetContainingBlockContentISize(
--- a/layout/generic/nsGridContainerFrame.cpp
+++ b/layout/generic/nsGridContainerFrame.cpp
@@ -41,16 +41,19 @@ typedef nsAbsoluteContainingBlock::AbsPo
 typedef nsGridContainerFrame::TrackSize TrackSize;
 const uint32_t nsGridContainerFrame::kTranslatedMaxLine =
     uint32_t(nsStyleGridLine::kMaxLine - nsStyleGridLine::kMinLine);
 const uint32_t nsGridContainerFrame::kAutoLine = kTranslatedMaxLine + 3457U;
 typedef nsTHashtable<nsPtrHashKey<nsIFrame>> FrameHashtable;
 typedef mozilla::CSSAlignUtils::AlignJustifyFlags AlignJustifyFlags;
 typedef nsLayoutUtils::IntrinsicISizeType IntrinsicISizeType;
 
+static const nsFrameState kIsSubgridBits = (NS_STATE_GRID_IS_COL_SUBGRID |
+                                            NS_STATE_GRID_IS_ROW_SUBGRID);
+
 // https://drafts.csswg.org/css-sizing/#constraints
 enum class SizingConstraint {
   MinContent,   // sizing under min-content constraint
   MaxContent,   // sizing under max-content constraint
   NoConstraint  // no constraint, used during Reflow
 };
 
 static void ReparentFrame(nsIFrame* aFrame, nsContainerFrame* aOldParent,
@@ -133,16 +136,50 @@ static nscoord SynthesizeBaselineFromBor
   }
   MOZ_ASSERT(aGroup == BaselineSharingGroup::Last);
   // Round up for central baseline offset, to be consistent with eFirst.
   return aWM.IsAlphabeticalBaseline()
              ? 0
              : (aBorderBoxSize / 2) + (aBorderBoxSize % 2);
 }
 
+// The input sizes for calculating the number of repeat(auto-fill/fit) tracks.
+// https://drafts.csswg.org/css-grid/#auto-repeat
+struct RepeatTrackSizingInput {
+  explicit RepeatTrackSizingInput(WritingMode aWM)
+      : mMin(aWM, 0, 0),
+        mSize(aWM, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE),
+        mMax(aWM, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE) {}
+  RepeatTrackSizingInput(const LogicalSize& aMin, const LogicalSize& aSize,
+                         const LogicalSize& aMax)
+      : mMin(aMin), mSize(aSize), mMax(aMax) {}
+
+  void SetDefiniteSizes(LogicalAxis aAxis, WritingMode aWM,
+                        const StyleSize& aMinCoord,
+                        const StyleSize& aSizeCoord,
+                        const StyleMaxSize& aMaxCoord) {
+    nscoord& min = mMin.Size(aAxis, aWM);
+    nscoord& size = mSize.Size(aAxis, aWM);
+    nscoord& max = mMax.Size(aAxis, aWM);
+    if (aMinCoord.ConvertsToLength()) {
+      min = aMinCoord.ToLength();
+    }
+    if (aMaxCoord.ConvertsToLength()) {
+      max = std::max(min, aMaxCoord.ToLength());
+    }
+    if (aSizeCoord.ConvertsToLength()) {
+      size = Clamp(aSizeCoord.ToLength(), min, max);
+    }
+  }
+
+  LogicalSize mMin;
+  LogicalSize mSize;
+  LogicalSize mMax;
+};
+
 enum class GridLineSide {
   BeforeGridGap,
   AfterGridGap,
 };
 
 struct nsGridContainerFrame::TrackSize {
   enum StateBits : uint16_t {
     // clang-format off
@@ -386,18 +423,20 @@ struct nsGridContainerFrame::LineRange {
    * Resolve this auto range to start at aStart, making it definite.
    * @param aClampMaxLine the maximum allowed line number (zero-based)
    * Precondition: this range IsAuto()
    */
   void ResolveAutoPosition(uint32_t aStart, uint32_t aClampMaxLine) {
     MOZ_ASSERT(IsAuto(), "Why call me?");
     mStart = aStart;
     mEnd += aStart;
-    // Clamping to where kMaxLine is in the explicit grid, per
-    // http://dev.w3.org/csswg/css-grid/#overlarge-grids :
+    // Clamp to aClampMaxLine, which is where kMaxLine is in the explicit
+    // grid in a non-subgrid axis; this implements clamping per
+    // http://dev.w3.org/csswg/css-grid/#overlarge-grids
+    // In a subgrid axis it's the end of the grid in that axis.
     if (MOZ_UNLIKELY(mStart >= aClampMaxLine)) {
       mEnd = aClampMaxLine;
       mStart = mEnd - 1;
     } else if (MOZ_UNLIKELY(mEnd > aClampMaxLine)) {
       mEnd = aClampMaxLine;
     }
   }
   /**
@@ -452,16 +491,31 @@ struct nsGridContainerFrame::LineRange {
    * Given an array of track sizes and a grid origin coordinate, adjust the
    * abs.pos. containing block along an axis given by aPos and aLength.
    * aPos and aLength should already be initialized to the grid container
    * containing block for this axis before calling this method.
    */
   void ToPositionAndLengthForAbsPos(const Tracks& aTracks, nscoord aGridOrigin,
                                     nscoord* aPos, nscoord* aLength) const;
 
+  void Translate(int32_t aOffset) {
+    MOZ_ASSERT(IsDefinite());
+    mStart += aOffset;
+    mEnd += aOffset;
+  }
+
+  /** Swap the start/end sides of this range. */
+  void ReverseDirection(uint32_t aGridEnd) {
+    MOZ_ASSERT(IsDefinite());
+    MOZ_ASSERT(aGridEnd >= mEnd);
+    uint32_t newStart = aGridEnd - mEnd;
+    mEnd = aGridEnd - mStart;
+    mStart = newStart;
+  }
+
   /**
    * @note We'll use the signed member while resolving definite positions
    * to line numbers (1-based), which may become negative for implicit lines
    * to the top/left of the explicit grid.  PlaceGridItems() then translates
    * the whole grid to a 0,0 origin and we'll use the unsigned member from
    * there on.
    */
   union {
@@ -494,25 +548,31 @@ struct nsGridContainerFrame::TranslatedL
  * The area is represented by two LineRanges, both of which can be auto
  * (@see LineRange) in intermediate steps while the item is being placed.
  * @see PlaceGridItems
  */
 struct nsGridContainerFrame::GridArea {
   GridArea(const LineRange& aCols, const LineRange& aRows)
       : mCols(aCols), mRows(aRows) {}
   bool IsDefinite() const { return mCols.IsDefinite() && mRows.IsDefinite(); }
+  LineRange& LineRangeForAxis(LogicalAxis aAxis) {
+    return aAxis == eLogicalAxisInline ? mCols : mRows;
+  }
+  const LineRange& LineRangeForAxis(LogicalAxis aAxis) const {
+    return aAxis == eLogicalAxisInline ? mCols : mRows;
+  }
   LineRange mCols;
   LineRange mRows;
 };
 
 struct nsGridContainerFrame::GridItemInfo {
   /**
    * Item state per axis.
    */
-  enum StateBits : uint8_t {
+  enum StateBits : uint16_t {
     // clang-format off
     eIsFlexing =              0x1, // does the item span a flex track?
     eFirstBaseline =          0x2, // participate in 'first baseline' alignment?
     // ditto 'last baseline', mutually exclusive w. eFirstBaseline
     eLastBaseline =           0x4,
     eIsBaselineAligned = eFirstBaseline | eLastBaseline,
     // One of e[Self|Content]Baseline is set when eIsBaselineAligned is true
     eSelfBaseline =           0x8, // is it *-self:[last ]baseline alignment?
@@ -520,16 +580,21 @@ struct nsGridContainerFrame::GridItemInf
     eContentBaseline =       0x10,
     eAllBaselineBits = eIsBaselineAligned | eSelfBaseline | eContentBaseline,
     // Should apply Automatic Minimum Size per:
     // https://drafts.csswg.org/css-grid/#min-size-auto
     eApplyAutoMinSize =      0x20,
     // Clamp per https://drafts.csswg.org/css-grid/#min-size-auto
     eClampMarginBoxMinSize = 0x40,
     eIsSubgrid =             0x80,
+    // set on subgrids and items in subgrids if they are adjacent to the grid
+    // start/end edge (excluding grid-aligned abs.pos. frames)
+    eStartEdge =            0x100,
+    eEndEdge =              0x200,
+    eEdgeBits = eStartEdge | eEndEdge,
     // clang-format on
   };
 
   explicit GridItemInfo(nsIFrame* aFrame, const GridArea& aArea)
       : mFrame(aFrame), mArea(aArea) {
     mState[eLogicalAxisBlock] = StateBits(0);
     mState[eLogicalAxisInline] = StateBits(0);
     nsGridContainerFrame* gridFrame = GetGridContainerFrame(mFrame);
@@ -544,26 +609,49 @@ struct nsGridContainerFrame::GridItemInf
         mState[isOrthogonal ? eLogicalAxisInline : eLogicalAxisBlock] =
             StateBits::eIsSubgrid;
       }
     }
     mBaselineOffset[eLogicalAxisBlock] = nscoord(0);
     mBaselineOffset[eLogicalAxisInline] = nscoord(0);
   }
 
+  /**
+   * Return a copy of this item with its row/column data swapped.
+   */
+  GridItemInfo Transpose() const {
+    GridItemInfo info(mFrame, GridArea(mArea.mRows, mArea.mCols));
+    info.mState[0] = mState[1];
+    info.mState[1] = mState[0];
+    info.mBaselineOffset[0] = mBaselineOffset[1];
+    info.mBaselineOffset[1] = mBaselineOffset[0];
+    return info;
+  }
+
+  /** Swap the start/end sides in aAxis. */
+  inline void ReverseDirection(LogicalAxis aAxis, uint32_t aGridEnd);
+
   // Is this item a subgrid in the given container axis?
   bool IsSubgrid(LogicalAxis aAxis) const {
     return mState[aAxis] & StateBits::eIsSubgrid;
   }
 
   // Is this item a subgrid in either axis?
   bool IsSubgrid() const {
     return IsSubgrid(eLogicalAxisInline) || IsSubgrid(eLogicalAxisBlock);
   }
 
+  // Return the (inner) grid container frame associated with this subgrid item.
+  nsGridContainerFrame* SubgridFrame() const {
+    MOZ_ASSERT(IsSubgrid());
+    nsGridContainerFrame* gridFrame = GetGridContainerFrame(mFrame);
+    MOZ_ASSERT(gridFrame && gridFrame->IsSubgrid());
+    return gridFrame;
+  }
+
   /**
    * If the item is [align|justify]-self:[last ]baseline aligned in the given
    * axis then set aBaselineOffset to the baseline offset and return aAlign.
    * Otherwise, return a fallback alignment.
    */
   uint8_t GetSelfBaseline(uint8_t aAlign, LogicalAxis aAxis,
                           nscoord* aBaselineOffset) const {
     MOZ_ASSERT(aAlign == NS_STYLE_ALIGN_BASELINE ||
@@ -636,24 +724,126 @@ struct nsGridContainerFrame::GridItemInf
   static_assert(mozilla::eLogicalAxisBlock == 0, "unexpected index value");
   static_assert(mozilla::eLogicalAxisInline == 1, "unexpected index value");
 };
 
 using GridItemInfo = nsGridContainerFrame::GridItemInfo;
 using ItemState = GridItemInfo::StateBits;
 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(ItemState)
 
+void GridItemInfo::ReverseDirection(LogicalAxis aAxis, uint32_t aGridEnd) {
+  mArea.LineRangeForAxis(aAxis).ReverseDirection(aGridEnd);
+  ItemState& state = mState[aAxis];
+  ItemState newState = state & ~ItemState::eEdgeBits;
+  if (state & ItemState::eStartEdge) {
+    newState |= ItemState::eEndEdge;
+  }
+  if (state & ItemState::eEndEdge) {
+    newState |= ItemState::eStartEdge;
+  }
+  state = newState;
+}
+
+// Each subgrid stores this data about its items etc on a frame property.
+struct nsGridContainerFrame::Subgrid {
+  Subgrid(const GridArea& aArea, bool aIsOrthogonal, WritingMode aCBWM)
+      : mArea(aArea),
+        mGridColEnd(0),
+        mGridRowEnd(0),
+        mMarginBorderPadding(aCBWM),
+        mIsOrthogonal(aIsOrthogonal) {}
+
+  // Return the relevant line range for the subgrid column axis.
+  const LineRange& SubgridCols() const {
+    return mIsOrthogonal ? mArea.mRows : mArea.mCols;
+  }
+  // Return the relevant line range for the subgrid row axis.
+  const LineRange& SubgridRows() const {
+    return mIsOrthogonal ? mArea.mCols : mArea.mRows;
+  }
+
+  // The subgrid's items.
+  nsTArray<GridItemInfo> mGridItems;
+  // The subgrid's abs.pos. items.
+  nsTArray<GridItemInfo> mAbsPosItems;
+  // The subgrid's area as a grid item, i.e. in its parent's grid space.
+  GridArea mArea;
+  // The (inner) grid size for the subgrid, zero-based.
+  uint32_t mGridColEnd;
+  uint32_t mGridRowEnd;
+  // The margin+border+padding for the subgrid box in its parent grid's WM.
+  // (This also includes the size of any scrollbars.)
+  LogicalMargin mMarginBorderPadding;
+  // Does the subgrid frame have orthogonal writing-mode to its parent grid container?
+  bool mIsOrthogonal;
+
+  NS_DECLARE_FRAME_PROPERTY_DELETABLE(Prop, Subgrid)
+};
+using Subgrid = nsGridContainerFrame::Subgrid;
+
+/**
+ * Track size data for use by subgrids (which don't do sizing of their own
+ * in a subgridded axis).  A non-subgrid container stores its resolved sizes,
+ * but only if it has any subgrid children.  A subgrid always stores one.
+ * In a subgridded axis, we copy the parent's sizes (see CopyUsedTrackSizes).
+ *
+ * This struct us stored on a frame property, which may be null before the track
+ * sizing step for the given container.  A null property is semantically
+ * equivalent to mCanResolveLineRangeSize being false in both axes.
+ * @note the axis used to access this data is in the grid container's own
+ * writing-mode, same as in other track-sizing functions.
+ */
+struct nsGridContainerFrame::UsedTrackSizes {
+  UsedTrackSizes() : mCanResolveLineRangeSize{false, false} {}
+
+  /**
+   * Setup mSizes by copying track sizes from aFrame's grid container
+   * parent when aAxis is subgridded (and recurse if the parent is a subgrid
+   * that doesn't have sizes yet), or by running the Track Sizing Algo when
+   * the axis is not subgridded (for a subgrid).
+   * Set mCanResolveLineRangeSize[aAxis] to true once we have obtained
+   * sizes for an axis (if it's already true then this method is a NOP).
+   */
+  void ResolveTrackSizesForAxis(nsGridContainerFrame* aFrame, LogicalAxis aAxis,
+                                gfxContext& aRC);
+
+  /** Helper function for the above method */
+  void ResolveSubgridTrackSizesForAxis(nsGridContainerFrame* aFrame,
+                                       LogicalAxis aAxis, Subgrid* aSubgrid,
+                                       gfxContext& aRC, nscoord aCBSize);
+
+  // This only has valid sizes when mCanResolveLineRangeSize is true in
+  // the same axis.  It may have zero tracks (a grid with only abs.pos.
+  // subgrids/items may have zero tracks).
+  PerLogicalAxis<nsTArray<TrackSize>> mSizes;
+  // True if mSizes can be used to resolve line range sizes in an axis.
+  PerLogicalAxis<bool> mCanResolveLineRangeSize;
+
+  NS_DECLARE_FRAME_PROPERTY_DELETABLE(Prop, UsedTrackSizes)
+};
+using UsedTrackSizes = nsGridContainerFrame::UsedTrackSizes;
+
 #ifdef DEBUG
 void nsGridContainerFrame::GridItemInfo::Dump() const {
   auto Dump1 = [this](const char* aMsg, LogicalAxis aAxis) {
     auto state = mState[aAxis];
     if (!state) {
       return;
     }
     printf("%s", aMsg);
+    if (state & ItemState::eEdgeBits) {
+      printf("subgrid-adjacent-edges(");
+      if (state & ItemState::eStartEdge) {
+        printf("start ");
+      }
+      if (state & ItemState::eEndEdge) {
+        printf("end");
+      }
+      printf(") ");
+    }
     if (state & ItemState::eIsSubgrid) {
       printf("subgrid ");
     }
     if (state & ItemState::eIsFlexing) {
       printf("flexing ");
     }
     if (state & ItemState::eApplyAutoMinSize) {
       printf("auto-min-size ");
@@ -686,133 +876,267 @@ void nsGridContainerFrame::GridItemInfo:
  * Utility class to find line names.  It provides an interface to lookup line
  * names with a dynamic number of repeat(auto-fill/fit) tracks taken into
  * account.
  */
 class MOZ_STACK_CLASS nsGridContainerFrame::LineNameMap {
  public:
   /**
    * Create a LineNameMap.
+   * @param aStylePosition the style for the grid container
+   * @param aImplicitNamedAreas the implicit areas for the grid container
    * @param aGridTemplate is the grid-template-rows/columns data for this axis
    * @param aNumRepeatTracks the number of actual tracks associated with
    *   a repeat(auto-fill/fit) track (zero or more), or zero if there is no
    *   specified repeat(auto-fill/fit) track
+   * @param aClampMinLine/aClampMaxLine in a non-subgrid axis it's kMin/MaxLine;
+   *   in a subgrid axis it's its explicit grid bounds (all 1-based)
+   * @param aParentLineNameMap the parent grid's map parallel to this map, or
+   *                           null if this map isn't for a subgrid
+   * @param aRange the subgrid's range in the parent grid, or null
+   * @param aIsSameDirection true if our axis progresses in the same direction
+   *                              in the subgrid and parent
    */
-  LineNameMap(const nsStyleGridTemplate& aGridTemplate,
-              uint32_t aNumRepeatTracks)
-      : mLineNameLists(aGridTemplate.mLineNameLists),
+  LineNameMap(const nsStylePosition* aStylePosition,
+              const ImplicitNamedAreas* aImplicitNamedAreas,
+              const nsStyleGridTemplate& aGridTemplate,
+              uint32_t aNumRepeatTracks, int32_t aClampMinLine,
+              int32_t aClampMaxLine, const LineNameMap* aParentLineNameMap,
+              const LineRange* aRange, bool aIsSameDirection)
+      : mClampMinLine(aClampMinLine),
+        mClampMaxLine(aClampMaxLine),
+        mStylePosition(aStylePosition),
+        mAreas(aImplicitNamedAreas),
+        mLineNameLists(aGridTemplate.mLineNameLists),
         mRepeatAutoLineNameListBefore(
             aGridTemplate.mRepeatAutoLineNameListBefore),
         mRepeatAutoLineNameListAfter(
             aGridTemplate.mRepeatAutoLineNameListAfter),
         mRepeatAutoStart(
             aGridTemplate.HasRepeatAuto() ? aGridTemplate.mRepeatAutoIndex : 0),
         mRepeatAutoEnd(mRepeatAutoStart + aNumRepeatTracks),
         mRepeatEndDelta(
             aGridTemplate.HasRepeatAuto() ? int32_t(aNumRepeatTracks) - 1 : 0),
         mTemplateLinesEnd(mLineNameLists.Length() + mRepeatEndDelta),
+        mParentLineNameMap(aParentLineNameMap),
+        mRange(aRange),
+        mIsSameDirection(aIsSameDirection),
         mHasRepeatAuto(aGridTemplate.HasRepeatAuto()) {
     MOZ_ASSERT(mHasRepeatAuto || aNumRepeatTracks == 0);
     MOZ_ASSERT(mRepeatAutoStart <= mLineNameLists.Length());
     MOZ_ASSERT(!mHasRepeatAuto || mLineNameLists.Length() >= 2);
   }
 
   /**
    * Find the aNth occurrence of aName, searching forward if aNth is positive,
    * and in reverse if aNth is negative (aNth == 0 is invalid), starting from
    * aFromIndex (not inclusive), and return a 1-based line number.
-   * Also take into account there is an unconditional match at aImplicitLine
-   * unless it's zero.
+   * Also take into account there is an unconditional match at the lines in
+   * aImplicitLines.
    * Return zero if aNth occurrences can't be found.  In that case, aNth has
    * been decremented with the number of occurrences that were found (if any).
    *
    * E.g. to search for "A 2" forward from the start of the grid: aName is "A"
    * aNth is 2 and aFromIndex is zero.  To search for "A -2", aNth is -2 and
    * aFromIndex is ExplicitGridEnd + 1 (which is the line "before" the last
    * line when we're searching in reverse).  For "span A 2", aNth is 2 when
    * used on a grid-[row|column]-end property and -2 for a *-start property,
    * and aFromIndex is the line (which we should skip) on the opposite property.
    */
   uint32_t FindNamedLine(const nsString& aName, int32_t* aNth,
-                         uint32_t aFromIndex, uint32_t aImplicitLine) const {
+                         uint32_t aFromIndex,
+                         const nsTArray<uint32_t>& aImplicitLines) const {
     MOZ_ASSERT(aNth && *aNth != 0);
     if (*aNth > 0) {
-      return FindLine(aName, aNth, aFromIndex, aImplicitLine);
+      return FindLine(aName, aNth, aFromIndex, aImplicitLines);
     }
     int32_t nth = -*aNth;
-    int32_t line = RFindLine(aName, &nth, aFromIndex, aImplicitLine);
+    int32_t line = RFindLine(aName, &nth, aFromIndex, aImplicitLines);
     *aNth = -nth;
     return line;
   }
 
+  /**
+   * Return a set of lines in aImplicitLines which matches the area name aName
+   * on aSide.  For example, for aName "a" and aSide being an end side, it
+   * returns the line numbers which would match "a-end" in the relevant axis.
+   * For subgrids it includes searching the relevant axis in all ancestor
+   * grids too (within this subgrid's spanned area).  If an ancestor has
+   * opposite direction, we switch aSide to the opposite logical side so we
+   * match on the same physical side as the original subgrid we're resolving
+   * the name for.
+   */
+  void FindNamedAreas(const nsAString& aName, LogicalSide aSide,
+                      nsTArray<uint32_t>& aImplicitLines) const {
+    // True if we're currently in a map that has the same direction as 'this'.
+    bool sameDirectionAsThis = true;
+    uint32_t min = !mParentLineNameMap ? 1 : mClampMinLine;
+    uint32_t max = mClampMaxLine;
+    for (auto* map = this; true;) {
+      uint32_t line = map->FindNamedArea(aName, aSide, min, max);
+      if (line > 0) {
+        if (MOZ_LIKELY(sameDirectionAsThis)) {
+          line -= min - 1;
+        } else {
+          line = max - line + 1;
+        }
+        aImplicitLines.AppendElement(line);
+      }
+      auto* parent = map->mParentLineNameMap;
+      if (!parent) {
+        if (MOZ_UNLIKELY(aImplicitLines.Length() > 1)) {
+          // Remove duplicates and sort in ascending order.
+          aImplicitLines.Sort();
+          for (size_t i = 0; i < aImplicitLines.Length(); ++i) {
+            uint32_t prev = aImplicitLines[i];
+            auto j = i + 1;
+            const auto start = j;
+            while (j < aImplicitLines.Length() && aImplicitLines[j] == prev) {
+              ++j;
+            }
+            if (j != start) {
+              aImplicitLines.RemoveElementsAt(start, j - start);
+            }
+          }
+        }
+        return;
+      }
+      if (MOZ_UNLIKELY(!map->mIsSameDirection)) {
+        aSide = GetOppositeSide(aSide);
+        sameDirectionAsThis = !sameDirectionAsThis;
+      }
+      min = map->TranslateToParentMap(min);
+      max = map->TranslateToParentMap(max);
+      if (min > max) {
+        MOZ_ASSERT(!map->mIsSameDirection);
+        mozilla::Swap(min, max);
+      }
+      map = parent;
+    }
+  }
+
+  /**
+   * Return true if any implicit named areas match aName, in this map or
+   * in any of our ancestor maps.
+   */
+  bool HasImplicitNamedArea(const nsString& aName) const {
+    const auto* map = this;
+    do {
+      if (map->mAreas && map->mAreas->Contains(aName)) {
+        return true;
+      }
+      map = map->mParentLineNameMap;
+    } while (map);
+    return false;
+  }
+
+  // The min/max line number (1-based) for clamping.
+  const int32_t mClampMinLine;
+  const int32_t mClampMaxLine;
+
  private:
+  // Return true if this map represents a subgridded axis.
+  bool IsSubgridded() const { return mParentLineNameMap != nullptr; }
+
   /**
    * @see FindNamedLine, this function searches forward.
    */
   uint32_t FindLine(const nsString& aName, int32_t* aNth, uint32_t aFromIndex,
-                    uint32_t aImplicitLine) const {
+                    const nsTArray<uint32_t>& aImplicitLines) const {
     MOZ_ASSERT(aNth && *aNth > 0);
     int32_t nth = *aNth;
-    const uint32_t end = mTemplateLinesEnd;
+    // For a subgrid we need to search to the end of the grid rather than
+    // the end of the local name list, since ancestors might match.
+    const uint32_t end = IsSubgridded() ? mClampMaxLine : mTemplateLinesEnd;
     uint32_t line;
     uint32_t i = aFromIndex;
     for (; i < end; i = line) {
       line = i + 1;
-      if (line == aImplicitLine || Contains(i, aName)) {
+      if (Contains(i, aName) || aImplicitLines.Contains(line)) {
         if (--nth == 0) {
           return line;
         }
       }
     }
-    if (aImplicitLine > i) {
-      // aImplicitLine is after the lines we searched above so it's last.
-      // (grid-template-areas has more tracks than grid-template-[rows|columns])
-      if (--nth == 0) {
-        return aImplicitLine;
+    for (auto implicitLine : aImplicitLines) {
+      if (implicitLine > i) {
+        // implicitLine is after the lines we searched above so it's last.
+        // (grid-template-areas has more tracks than
+        // grid-template-[rows|columns])
+        if (--nth == 0) {
+          return implicitLine;
+        }
       }
     }
     MOZ_ASSERT(nth > 0, "should have returned a valid line above already");
     *aNth = nth;
     return 0;
   }
 
   /**
    * @see FindNamedLine, this function searches in reverse.
    */
   uint32_t RFindLine(const nsString& aName, int32_t* aNth, uint32_t aFromIndex,
-                     uint32_t aImplicitLine) const {
+                     const nsTArray<uint32_t>& aImplicitLines) const {
     MOZ_ASSERT(aNth && *aNth > 0);
     if (MOZ_UNLIKELY(aFromIndex == 0)) {
       return 0;  // There are no named lines beyond the start of the explicit
                  // grid.
     }
     --aFromIndex;  // (shift aFromIndex so we can treat it as inclusive)
     int32_t nth = *aNth;
-    // The implicit line may be beyond the explicit grid so we match
-    // this line first if it's within the mTemplateLinesEnd..aFromIndex range.
-    const uint32_t end = mTemplateLinesEnd;
-    if (aImplicitLine > end && aImplicitLine < aFromIndex) {
-      if (--nth == 0) {
-        return aImplicitLine;
+    // Implicit lines may be beyond the explicit grid so we match those
+    // first if it's within the mTemplateLinesEnd..aFromIndex range.
+    // aImplicitLines is presumed sorted.
+    // For a subgrid we need to search to the end of the grid rather than
+    // the end of the local name list, since ancestors might match.
+    const uint32_t end = IsSubgridded() ? mClampMaxLine : mTemplateLinesEnd;
+    for (auto implicitLine : Reversed(aImplicitLines)) {
+      if (implicitLine <= end) {
+        break;
+      }
+      if (implicitLine < aFromIndex) {
+        if (--nth == 0) {
+          return implicitLine;
+        }
       }
     }
     for (uint32_t i = std::min(aFromIndex, end); i; --i) {
-      if (i == aImplicitLine || Contains(i - 1, aName)) {
+      if (Contains(i - 1, aName) || aImplicitLines.Contains(i)) {
         if (--nth == 0) {
           return i;
         }
       }
     }
     MOZ_ASSERT(nth > 0, "should have returned a valid line above already");
     *aNth = nth;
     return 0;
   }
 
-  // Return true if aName exists at aIndex.
+  // Return true if aName exists at aIndex in this map or any parent map.
   bool Contains(uint32_t aIndex, const nsString& aName) const {
+    const auto* map = this;
+    while (true) {
+      if (aIndex < map->mTemplateLinesEnd && map->HasNameAt(aIndex, aName)) {
+        return true;
+      }
+      auto* parent = map->mParentLineNameMap;
+      if (!parent) {
+        return false;
+      }
+      uint32_t line = map->TranslateToParentMap(aIndex + 1);
+      MOZ_ASSERT(line >= 1, "expected a 1-based line number");
+      aIndex = line - 1;
+      map = parent;
+    }
+    MOZ_ASSERT_UNREACHABLE("we always return from inside the loop above");
+  }
+
+  // Return true if aName exists at aIndex in this map.
+  bool HasNameAt(uint32_t aIndex, const nsString& aName) const {
     if (!mHasRepeatAuto) {
       return mLineNameLists[aIndex].Contains(aName);
     }
     if (aIndex < mRepeatAutoEnd && aIndex >= mRepeatAutoStart &&
         mRepeatAutoLineNameListBefore.Contains(aName)) {
       return true;
     }
     if (aIndex <= mRepeatAutoEnd && aIndex > mRepeatAutoStart &&
@@ -823,57 +1147,144 @@ class MOZ_STACK_CLASS nsGridContainerFra
       return mLineNameLists[aIndex].Contains(aName) ||
              (aIndex == mRepeatAutoEnd &&
               mLineNameLists[aIndex + 1].Contains(aName));
     }
     return aIndex >= mRepeatAutoEnd &&
            mLineNameLists[aIndex - mRepeatEndDelta].Contains(aName);
   }
 
+  // Translate a subgrid line (1-based) to a parent line (1-based).
+  uint32_t TranslateToParentMap(uint32_t aLine) const {
+    if (MOZ_LIKELY(mIsSameDirection)) {
+      return aLine + mRange->mStart;
+    }
+    MOZ_ASSERT(mRange->mEnd + 1 >= aLine);
+    return mRange->mEnd - (aLine - 1) + 1;
+  }
+
+  /**
+   * Return the 1-based line that match aName in 'grid-template-areas'
+   * on the side aSide.  Clamp the result to aMin..aMax but require
+   * that some part of the area is inside for it to match.
+   * Return zero if there is no match.
+   */
+  uint32_t FindNamedArea(const nsAString& aName, LogicalSide aSide,
+                         int32_t aMin, int32_t aMax) const {
+    const GridNamedArea* area = FindNamedArea(aName);
+    if (area) {
+      int32_t start = IsBlock(aSide) ? area->mRowStart : area->mColumnStart;
+      int32_t end = IsBlock(aSide) ? area->mRowEnd : area->mColumnEnd;
+      if (IsStart(aSide)) {
+        if (start >= aMin) {
+          if (start <= aMax) {
+            return start;
+          }
+        } else if (end >= aMin) {
+          return aMin;
+        }
+      } else {
+        if (end <= aMax) {
+          if (end >= aMin) {
+            return end;
+          }
+        } else if (start <= aMax) {
+          return aMax;
+        }
+      }
+    }
+    return 0;  // no match
+  }
+
+  /**
+   * A convenience method to lookup a name in 'grid-template-areas'.
+   * @param aStyle the StylePosition() for the grid container
+   * @return null if not found
+   */
+  const css::GridNamedArea* FindNamedArea(const nsAString& aName) const {
+    if (!mStylePosition->mGridTemplateAreas) {
+      return nullptr;
+    }
+    const nsTArray<css::GridNamedArea>& areas =
+        mStylePosition->mGridTemplateAreas->mNamedAreas;
+    size_t len = areas.Length();
+    for (size_t i = 0; i < len; ++i) {
+      const css::GridNamedArea& area = areas[i];
+      if (area.mName == aName) {
+        return &area;
+      }
+    }
+    return nullptr;
+  }
+
   // Some style data references, for easy access.
+  const nsStylePosition* mStylePosition;
+  const ImplicitNamedAreas* mAreas;
   const nsTArray<nsTArray<nsString>>& mLineNameLists;
   const nsTArray<nsString>& mRepeatAutoLineNameListBefore;
   const nsTArray<nsString>& mRepeatAutoLineNameListAfter;
   // The index of the repeat(auto-fill/fit) track, or zero if there is none.
   const uint32_t mRepeatAutoStart;
   // The (hypothetical) index of the last such repeat() track.
   const uint32_t mRepeatAutoEnd;
   // The difference between mTemplateLinesEnd and mLineNameLists.Length().
   const int32_t mRepeatEndDelta;
   // The end of the line name lists with repeat(auto-fill/fit) tracks accounted
   // for.  It is equal to mLineNameLists.Length() when a repeat() track
   // generates one track (making mRepeatEndDelta == 0).
   const uint32_t mTemplateLinesEnd;
+
+  // The parent line map, or null if this map isn't for a subgrid.
+  const LineNameMap* mParentLineNameMap;
+  // The subgrid's range, or null if this map isn't for a subgrid.
+  const LineRange* mRange;
+  // True if the subgrid/parent axes progresses in the same direction.
+  const bool mIsSameDirection;
+
   // True if there is a specified repeat(auto-fill/fit) track.
   const bool mHasRepeatAuto;
 };
 
 /**
  * Encapsulates CSS track-sizing functions.
  */
 struct nsGridContainerFrame::TrackSizingFunctions {
-  TrackSizingFunctions(const nsStyleGridTemplate& aGridTemplate,
+  TrackSizingFunctions(const nsTArray<nsStyleCoord>& aMinSizingFunctions,
+                       const nsTArray<nsStyleCoord>& aMaxSizingFunctions,
                        const nsStyleCoord& aAutoMinSizing,
-                       const nsStyleCoord& aAutoMaxSizing)
-      : mMinSizingFunctions(aGridTemplate.mMinTrackSizingFunctions),
-        mMaxSizingFunctions(aGridTemplate.mMaxTrackSizingFunctions),
+                       const nsStyleCoord& aAutoMaxSizing, bool aHasRepeatAuto,
+                       int32_t aRepeatAutoIndex)
+      : mMinSizingFunctions(aMinSizingFunctions),
+        mMaxSizingFunctions(aMaxSizingFunctions),
         mAutoMinSizing(aAutoMinSizing),
         mAutoMaxSizing(aAutoMaxSizing),
         mExplicitGridOffset(0),
-        mRepeatAutoStart(
-            aGridTemplate.HasRepeatAuto() ? aGridTemplate.mRepeatAutoIndex : 0),
+        mRepeatAutoStart(aHasRepeatAuto ? aRepeatAutoIndex : 0),
         mRepeatAutoEnd(mRepeatAutoStart),
         mRepeatEndDelta(0),
-        mHasRepeatAuto(aGridTemplate.HasRepeatAuto()) {
+        mHasRepeatAuto(aHasRepeatAuto) {
     MOZ_ASSERT(mMinSizingFunctions.Length() == mMaxSizingFunctions.Length());
     MOZ_ASSERT(!mHasRepeatAuto ||
                (mMinSizingFunctions.Length() >= 1 &&
                 mRepeatAutoStart < mMinSizingFunctions.Length()));
   }
 
+  TrackSizingFunctions(const nsStyleGridTemplate& aGridTemplate,
+                       const nsStyleCoord& aAutoMinSizing,
+                       const nsStyleCoord& aAutoMaxSizing)
+      // Note: if mIsSubgrid is true below then the HasRepeatAuto bit is for
+      // the mLineNameList, so we suppress that so that we can use this struct
+      // also when it's true.  This can happen when a specified 'subgrid' has
+      // no grid parent, which will behave as 'none'.
+      : TrackSizingFunctions(aGridTemplate.mMinTrackSizingFunctions,
+                             aGridTemplate.mMaxTrackSizingFunctions,
+                             aAutoMinSizing, aAutoMaxSizing,
+                             !aGridTemplate.mIsSubgrid && aGridTemplate.HasRepeatAuto(),
+                             aGridTemplate.mRepeatAutoIndex) {}
+
   /**
    * Initialize the number of auto-fill/fit tracks to use and return that.
    * (zero if no auto-fill/fit track was specified)
    */
   uint32_t InitRepeatTracks(const NonNegativeLengthPercentageOrNormal& aGridGap,
                             nscoord aMinSize, nscoord aSize, nscoord aMaxSize) {
     uint32_t repeatTracks =
         CalculateRepeatFillCount(aGridGap, aMinSize, aSize, aMaxSize);
@@ -1020,22 +1431,91 @@ struct nsGridContainerFrame::TrackSizing
   // True if there is a specified repeat(auto-fill/fit) track.
   const bool mHasRepeatAuto;
   // True if this track (relative to mRepeatAutoStart) is a removed auto-fit.
   // Indexed relative to mExplicitGridOffset + mRepeatAutoStart.
   nsTArray<bool> mRemovedRepeatTracks;
 };
 
 /**
+ * This is used in a subgridded axis to resolve sizes before its parent's
+ * sizes are known for intrinsic sizing purposes.  It copies the slice of
+ * the nearest non-subgridded axis' track sizing functions spanned by
+ * the subgrid.
+ *
+ * FIXME: this was written before there was a spec... the spec now says:
+ * "If calculating the layout of a grid item in this step depends on
+ *  the available space in the block axis, assume the available space
+ *  that it would have if any row with a definite max track sizing
+ *  function had that size and all other rows were infinite."
+ * https://drafts.csswg.org/css-grid-2/#subgrid-sizing
+ */
+struct MOZ_STACK_CLASS nsGridContainerFrame::SubgridFallbackTrackSizingFunctions {
+  SubgridFallbackTrackSizingFunctions(nsGridContainerFrame* aSubgridFrame,
+                                      const Subgrid* aSubgrid,
+                                      nsGridContainerFrame* aParentGridContainer,
+                                      LogicalAxis aParentAxis) {
+    MOZ_ASSERT(aSubgrid);
+    MOZ_ASSERT(aSubgridFrame->IsSubgrid(
+        aSubgrid->mIsOrthogonal ? GetOrthogonalAxis(aParentAxis) : aParentAxis));
+    nsGridContainerFrame* parent = aParentGridContainer;
+    auto parentAxis = aParentAxis;
+    LineRange range = aSubgrid->mArea.LineRangeForAxis(parentAxis);
+    // Find our nearest non-subgridded axis and use its track sizing functions.
+    while (parent->IsSubgrid(parentAxis)) {
+      const auto* parentSubgrid = parent->GetProperty(Subgrid::Prop());
+      auto* grandParent = parent->ParentGridContainerForSubgrid();
+      auto grandParentWM = grandParent->GetWritingMode();
+      bool isSameDirInAxis = parent->GetWritingMode().
+          ParallelAxisStartsOnSameSide(parentAxis, grandParentWM);
+      if (MOZ_UNLIKELY(!isSameDirInAxis)) {
+        auto end = parentAxis == eLogicalAxisBlock ? parentSubgrid->mGridRowEnd
+                                                   : parentSubgrid->mGridColEnd;
+        range.ReverseDirection(end);
+        // range is now in the same direction as the grand-parent's axis
+      }
+      auto grandParentAxis = parentSubgrid->mIsOrthogonal ?
+          GetOrthogonalAxis(parentAxis) : parentAxis;
+      const auto& parentRange = parentSubgrid->mArea.LineRangeForAxis(grandParentAxis);
+      range.Translate(parentRange.mStart);
+      // range is now in the grand-parent's coordinates
+      parentAxis = grandParentAxis;
+      parent = grandParent;
+    }
+    const auto* pos = parent->StylePosition();
+    const auto isInlineAxis = parentAxis == eLogicalAxisInline;
+    const auto& szf = isInlineAxis ? pos->GridTemplateColumns()
+                                   : pos->GridTemplateRows();
+    const auto& minAuto = isInlineAxis ? pos->mGridAutoColumnsMin
+                                       : pos->mGridAutoRowsMin;
+    const auto& maxAuto = isInlineAxis ? pos->mGridAutoColumnsMax
+                                       : pos->mGridAutoRowsMax;
+    TrackSizingFunctions tsf(szf, minAuto, maxAuto);
+    for (auto i : range.Range()) {
+      mMinSizingFunctions.AppendElement(tsf.MinSizingFor(i));
+      mMaxSizingFunctions.AppendElement(tsf.MaxSizingFor(i));
+    }
+    mAutoMinSizing = &minAuto;
+    mAutoMaxSizing = &maxAuto;
+  }
+  nsTArray<nsStyleCoord> mMinSizingFunctions;
+  nsTArray<nsStyleCoord> mMaxSizingFunctions;
+  const nsStyleCoord* mAutoMinSizing;
+  const nsStyleCoord* mAutoMaxSizing;
+  uint32_t mRepeatAutoIndex = 0;
+  bool mHasRepeatAuto = false;
+};
+
+/**
  * State for the tracks in one dimension.
  */
 struct nsGridContainerFrame::Tracks {
   explicit Tracks(LogicalAxis aAxis)
-      : mContentBoxSize(0),
-        mGridGap(0),
+      : mContentBoxSize(NS_UNCONSTRAINEDSIZE),
+        mGridGap(NS_UNCONSTRAINEDSIZE),
         mStateUnion(TrackSize::StateBits(0)),
         mAxis(aAxis),
         mCanResolveLineRangeSize(false) {
     mBaselineSubtreeAlign[BaselineSharingGroup::First] = NS_STYLE_ALIGN_AUTO;
     mBaselineSubtreeAlign[BaselineSharingGroup::Last] = NS_STYLE_ALIGN_AUTO;
     mBaseline[BaselineSharingGroup::First] = NS_INTRINSIC_ISIZE_UNKNOWN;
     mBaseline[BaselineSharingGroup::Last] = NS_INTRINSIC_ISIZE_UNKNOWN;
   }
@@ -1543,17 +2023,17 @@ struct nsGridContainerFrame::Tracks {
                       nscoord aContentSize, LineRange GridArea::*aRange,
                       SizingConstraint aConstraint);
 
   /**
    * Apply 'align/justify-content', whichever is relevant for this axis.
    * https://drafts.csswg.org/css-align-3/#propdef-align-content
    */
   void AlignJustifyContent(const nsStylePosition* aStyle, WritingMode aWM,
-                           nscoord aContentSize);
+                           nscoord aContentSize, bool aIsSubgridded);
 
   nscoord GridLineEdge(uint32_t aLine, GridLineSide aSide) const {
     if (MOZ_UNLIKELY(mSizes.IsEmpty())) {
       // https://drafts.csswg.org/css-grid/#grid-definition
       // "... the explicit grid still contains one grid line in each axis."
       MOZ_ASSERT(aLine == 0, "We should only resolve line 1 in an empty grid");
       return nscoord(0);
     }
@@ -1789,16 +2269,27 @@ struct MOZ_STACK_CLASS nsGridContainerFr
 
     // Copy the shared track state.
     // XXX consider temporarily swapping the array elements instead and swapping
     // XXX them back after we're done reflowing, for better performance.
     // XXX (bug 1252002)
     mCols = mSharedGridData->mCols;
     mRows = mSharedGridData->mRows;
 
+    if (firstInFlow->GetProperty(UsedTrackSizes::Prop())) {
+      auto* prop = aGridContainerFrame->GetProperty(UsedTrackSizes::Prop());
+      if (!prop) {
+        prop = new UsedTrackSizes();
+        aGridContainerFrame->SetProperty(UsedTrackSizes::Prop(), prop);
+      }
+      prop->mCanResolveLineRangeSize = { true, true };
+      prop->mSizes[eLogicalAxisInline] = mCols.mSizes;
+      prop->mSizes[eLogicalAxisBlock] = mRows.mSizes;
+    }
+
     // Copy item data from each child's first-in-flow data in mSharedGridData.
     // XXX NOTE: This is O(n^2) in the number of items. (bug 1252186)
     mIter.Reset();
     for (; !mIter.AtEnd(); mIter.Next()) {
       nsIFrame* child = *mIter;
       nsIFrame* childFirstInFlow = child->FirstInFlow();
       DebugOnly<size_t> len = mGridItems.Length();
       for (auto& itemInfo : mSharedGridData->mGridItems) {
@@ -1834,16 +2325,23 @@ struct MOZ_STACK_CLASS nsGridContainerFr
 
     // Copy in the computed grid info state bit
     if (mSharedGridData->mGenerateComputedGridInfo) {
       aGridContainerFrame->AddStateBits(NS_STATE_GRID_GENERATE_COMPUTED_VALUES);
     }
   }
 
   /**
+   * Calculate our track sizes in the given axis.
+   */
+  void CalculateTrackSizesForAxis(LogicalAxis aAxis, const Grid& aGrid,
+                                  nscoord aCBSize,
+                                  SizingConstraint aConstraint);
+
+  /**
    * Calculate our track sizes.
    */
   void CalculateTrackSizes(const Grid& aGrid, const LogicalSize& aContentBox,
                            SizingConstraint aConstraint);
 
   /**
    * Return the percentage basis for a grid item in its writing-mode.
    * If aAxis is eLogicalAxisInline then we return NS_UNCONSTRAINEDSIZE in
@@ -1867,16 +2365,80 @@ struct MOZ_STACK_CLASS nsGridContainerFr
    * containing block on that side.
    * @param aGridOrigin the origin of the grid
    * @param aGridCB the grid container containing block (its padding area)
    */
   LogicalRect ContainingBlockForAbsPos(const GridArea& aArea,
                                        const LogicalPoint& aGridOrigin,
                                        const LogicalRect& aGridCB) const;
 
+  // Helper for CollectSubgridItemsForAxis.
+  static void CollectSubgridForAxis(LogicalAxis aAxis, WritingMode aContainerWM,
+                                    const LineRange& aRangeInAxis,
+                                    const LineRange& aRangeInOppositeAxis,
+                                    const GridItemInfo& aItem,
+                                    const nsTArray<GridItemInfo>& aItems,
+                                    nsTArray<GridItemInfo>& aResult) {
+    const auto oppositeAxis = GetOrthogonalAxis(aAxis);
+    bool itemIsSubgridInOppositeAxis = aItem.IsSubgrid(oppositeAxis);
+    auto subgridWM = aItem.mFrame->GetWritingMode();
+    bool isOrthogonal = subgridWM.IsOrthogonalTo(aContainerWM);
+    bool isSameDirInAxis =
+        subgridWM.ParallelAxisStartsOnSameSide(aAxis, aContainerWM);
+    bool isSameDirInOppositeAxis =
+        subgridWM.ParallelAxisStartsOnSameSide(oppositeAxis, aContainerWM);
+    if (isOrthogonal) {
+      // We'll Transpose the area below so these needs to be transposed as well.
+      Swap(isSameDirInAxis, isSameDirInOppositeAxis);
+    }
+    uint32_t offsetInAxis = aRangeInAxis.mStart;
+    uint32_t gridEndInAxis = aRangeInAxis.Extent();
+    uint32_t offsetInOppositeAxis = aRangeInOppositeAxis.mStart;
+    uint32_t gridEndInOppositeAxis = aRangeInOppositeAxis.Extent();
+    for (const auto& subgridItem : aItems) {
+      auto* newItem = aResult.AppendElement(
+          isOrthogonal ? subgridItem.Transpose() : subgridItem);
+      if (MOZ_UNLIKELY(!isSameDirInAxis)) {
+        newItem->ReverseDirection(aAxis, gridEndInAxis);
+      }
+      newItem->mArea.LineRangeForAxis(aAxis).Translate(offsetInAxis);
+      if (itemIsSubgridInOppositeAxis) {
+        if (MOZ_UNLIKELY(!isSameDirInOppositeAxis)) {
+          newItem->ReverseDirection(oppositeAxis, gridEndInOppositeAxis);
+        }
+        LineRange& range = newItem->mArea.LineRangeForAxis(oppositeAxis);
+        range.Translate(offsetInOppositeAxis);
+      }
+      if (newItem->IsSubgrid(aAxis)) {
+        auto* subgrid =
+            subgridItem.SubgridFrame()->GetProperty(Subgrid::Prop());
+        CollectSubgridForAxis(aAxis, aContainerWM,
+                              newItem->mArea.LineRangeForAxis(aAxis),
+                              newItem->mArea.LineRangeForAxis(oppositeAxis),
+                              *newItem, subgrid->mGridItems, aResult);
+      }
+    }
+  }
+
+  // Copy all descendant items from all our subgrid children that are subgridded
+  // in aAxis recursively into aResult.  All item grid area's and state are
+  // translated to our coordinates.
+  void CollectSubgridItemsForAxis(LogicalAxis aAxis,
+                                  nsTArray<GridItemInfo>& aResult) const {
+    for (const auto& item : mGridItems) {
+      if (item.IsSubgrid(aAxis)) {
+        const auto oppositeAxis = GetOrthogonalAxis(aAxis);
+        auto* subgrid = item.SubgridFrame()->GetProperty(Subgrid::Prop());
+        CollectSubgridForAxis(aAxis, mWM, item.mArea.LineRangeForAxis(aAxis),
+                              item.mArea.LineRangeForAxis(oppositeAxis), item,
+                              subgrid->mGridItems, aResult);
+      }
+    }
+  }
+
   CSSOrderAwareFrameIterator mIter;
   const nsStylePosition* const mGridStyle;
   Tracks mCols;
   Tracks mRows;
   TrackSizingFunctions mColFunctions;
   TrackSizingFunctions mRowFunctions;
   /**
    * Info about each (normal flow) grid item.
@@ -1952,31 +2514,30 @@ struct MOZ_STACK_CLASS nsGridContainerFr
 
 using GridReflowInput = nsGridContainerFrame::GridReflowInput;
 
 /**
  * The Grid implements grid item placement and the state of the grid -
  * the size of the explicit/implicit grid, which cells are occupied etc.
  */
 struct MOZ_STACK_CLASS nsGridContainerFrame::Grid {
+  explicit Grid(const Grid* aParentGrid = nullptr) : mParentGrid(aParentGrid) {}
+
   /**
    * Place all child frames into the grid and expand the (implicit) grid as
    * needed.  The allocated GridAreas are stored in the GridAreaProperty
    * frame property on the child frame.
-   * @param aComputedMinSize the container's min-size - used to determine
-   *   the number of repeat(auto-fill/fit) tracks.
-   * @param aComputedSize the container's size - used to determine
-   *   the number of repeat(auto-fill/fit) tracks.
-   * @param aComputedMaxSize the container's max-size - used to determine
+   * @param aRepeatSizing the container's [min-|max-]*size - used to determine
    *   the number of repeat(auto-fill/fit) tracks.
    */
   void PlaceGridItems(GridReflowInput& aState,
-                      const LogicalSize& aComputedMinSize,
-                      const LogicalSize& aComputedSize,
-                      const LogicalSize& aComputedMaxSize);
+                      const RepeatTrackSizingInput& aRepeatSizing);
+
+  void SubgridPlaceGridItems(GridReflowInput& aParentState, Grid* aParentGrid,
+                             const GridItemInfo& aGridItem);
 
   /**
    * As above but for an abs.pos. child.  Any 'auto' lines will be represented
    * by kAutoLine in the LineRange result.
    * @param aGridStart the first line in the final, but untranslated grid
    * @param aGridEnd the last line in the final, but untranslated grid
    */
   LineRange ResolveAbsPosLineRange(const nsStyleGridLine& aStart,
@@ -2093,17 +2654,18 @@ struct MOZ_STACK_CLASS nsGridContainerFr
    *             a specified line name, it's permitted to pass in zero which
    *             will be treated as one.
    * @param aFromIndex the zero-based index to start counting from
    * @param aLineNameList the explicit named lines
    * @param aSide the axis+edge we're resolving names for (e.g. if we're
                   resolving a grid-row-start line, pass eLogicalSideBStart)
    * @param aExplicitGridEnd the last line in the explicit grid
    * @param aStyle the StylePosition() for the grid container
-   * @return a definite line (1-based), clamped to the kMinLine..kMaxLine range
+   * @return a definite line (1-based), clamped to
+   *   the mClampMinLine..mClampMaxLine range
    */
   int32_t ResolveLine(const nsStyleGridLine& aLine, int32_t aNth,
                       uint32_t aFromIndex, const LineNameMap& aNameMap,
                       LogicalSide aSide, uint32_t aExplicitGridEnd,
                       const nsStylePosition* aStyle);
 
   /**
    * Helper method for ResolveLineRange.
@@ -2147,38 +2709,16 @@ struct MOZ_STACK_CLASS nsGridContainerFr
   GridArea PlaceDefinite(nsIFrame* aChild, const LineNameMap& aColLineNameMap,
                          const LineNameMap& aRowLineNameMap,
                          const nsStylePosition* aStyle);
 
   bool HasImplicitNamedArea(const nsString& aName) const {
     return mAreas && mAreas->Contains(aName);
   }
 
-  /**
-   * A convenience method to lookup a name in 'grid-template-areas'.
-   * @param aStyle the StylePosition() for the grid container
-   * @return null if not found
-   */
-  static const css::GridNamedArea* FindNamedArea(
-      const nsAString& aName, const nsStylePosition* aStyle) {
-    if (!aStyle->mGridTemplateAreas) {
-      return nullptr;
-    }
-    const nsTArray<css::GridNamedArea>& areas =
-        aStyle->mGridTemplateAreas->mNamedAreas;
-    size_t len = areas.Length();
-    for (size_t i = 0; i < len; ++i) {
-      const css::GridNamedArea& area = areas[i];
-      if (area.mName == aName) {
-        return &area;
-      }
-    }
-    return nullptr;
-  }
-
   // Return true if aString ends in aSuffix and has at least one character
   // before the suffix. Assign aIndex to where the suffix starts.
   static bool IsNameWithSuffix(const nsString& aString, const nsString& aSuffix,
                                uint32_t* aIndex) {
     if (StringEndsWith(aString, aSuffix)) {
       *aIndex = aString.Length() - aSuffix.Length();
       return *aIndex != 0;
     }
@@ -2188,16 +2728,32 @@ struct MOZ_STACK_CLASS nsGridContainerFr
   static bool IsNameWithEndSuffix(const nsString& aString, uint32_t* aIndex) {
     return IsNameWithSuffix(aString, NS_LITERAL_STRING("-end"), aIndex);
   }
 
   static bool IsNameWithStartSuffix(const nsString& aString, uint32_t* aIndex) {
     return IsNameWithSuffix(aString, NS_LITERAL_STRING("-start"), aIndex);
   }
 
+  // Return the relevant parent LineNameMap for the given subgrid axis aAxis.
+  const LineNameMap* ParentLineMapForAxis(bool aIsOrthogonal,
+                                          LogicalAxis aAxis) const {
+    if (!mParentGrid) {
+      return nullptr;
+    }
+    bool isRows = aIsOrthogonal == (aAxis == eLogicalAxisInline);
+    return isRows ? mParentGrid->mRowNameMap : mParentGrid->mColNameMap;
+  }
+
+  void SetLineMaps(const LineNameMap* aColNameMap,
+                   const LineNameMap* aRowNameMap) {
+    mColNameMap = aColNameMap;
+    mRowNameMap = aRowNameMap;
+  }
+
   /**
    * A CellMap holds state for each cell in the grid.
    * It's row major.  It's sparse in the sense that it only has enough rows to
    * cover the last row that has a grid item.  Each row only has enough entries
    * to cover columns that are occupied *on that row*, i.e. it's not a full
    * matrix covering the entire implicit grid.  An absent Cell means that it's
    * unoccupied by any grid item.
    */
@@ -2287,34 +2843,292 @@ struct MOZ_STACK_CLASS nsGridContainerFr
    * Offsets from the start of the implicit grid to the start of the translated
    * explicit grid.  They are zero if there are no implicit lines before 1,1.
    * e.g. "grid-column: span 3 / 1" makes mExplicitGridOffsetCol = 3 and the
    * corresponding GridArea::mCols will be 0 / 3 in the zero-based translated
    * grid.
    */
   uint32_t mExplicitGridOffsetCol;
   uint32_t mExplicitGridOffsetRow;
+
+  /**
+   * Our parent grid if any.
+   */
+  const Grid* mParentGrid;
+
+  /**
+   * Our LineNameMaps.
+   */
+  const LineNameMap* mColNameMap;
+  const LineNameMap* mRowNameMap;
 };
 
+/**
+ * Compute margin+border+padding for aGridItem.mFrame (a subgrid) and store it
+ * on its Subgrid property (and return that property).
+ * aPercentageBasis is in the grid item's writing-mode.
+ */
+static Subgrid* SubgridComputeMarginBorderPadding(
+    const GridItemInfo& aGridItem, const LogicalSize& aPercentageBasis) {
+  auto* subgridFrame = aGridItem.SubgridFrame();
+  auto cbWM = aGridItem.mFrame->GetParent()->GetWritingMode();
+  nsMargin physicalMBP;
+  {
+    auto wm = subgridFrame->GetWritingMode();
+    auto pmPercentageBasis = cbWM.IsOrthogonalTo(wm)
+        ? aPercentageBasis.BSize(wm) : aPercentageBasis.ISize(wm);
+    SizeComputationInput sz(subgridFrame, nullptr, cbWM, pmPercentageBasis);
+    physicalMBP = sz.ComputedPhysicalMargin() +
+                  sz.ComputedPhysicalBorderPadding();
+  }
+  auto* subgrid = subgridFrame->GetProperty(Subgrid::Prop());
+  subgrid->mMarginBorderPadding = LogicalMargin(cbWM, physicalMBP);
+  if (aGridItem.mFrame != subgridFrame) {
+    nsIScrollableFrame* scrollFrame = aGridItem.mFrame->GetScrollTargetFrame();
+    if (scrollFrame) {
+      nsMargin ssz = scrollFrame->GetActualScrollbarSizes();
+      subgrid->mMarginBorderPadding += LogicalMargin(cbWM, ssz);
+    }
+
+    if (aGridItem.mFrame->IsFieldSetFrame()) {
+      const auto* f = static_cast<nsFieldSetFrame*>(aGridItem.mFrame);
+      const auto* inner = f->GetInner();
+      auto wm = inner->GetWritingMode();
+      LogicalPoint pos = inner->GetLogicalPosition(aGridItem.mFrame->GetSize());
+      // The legend is always on the BStart side and it inflates the fieldset's
+      // "border area" size.  The inner frame's b-start pos equals that size.
+      LogicalMargin offsets(wm, pos.B(wm), 0, 0, 0);
+      subgrid->mMarginBorderPadding += offsets.ConvertTo(cbWM, wm);
+    }
+  }
+  return subgrid;
+}
+
+static void CopyUsedTrackSizes(nsTArray<TrackSize>& aResult,
+                               const nsGridContainerFrame* aUsedTrackSizesFrame,
+                               const UsedTrackSizes* aUsedTrackSizes,
+                               const nsGridContainerFrame* aSubgridFrame,
+                               const Subgrid* aSubgrid,
+                               LogicalAxis aSubgridAxis) {
+  MOZ_ASSERT(aSubgridFrame->ParentGridContainerForSubgrid() ==
+             aUsedTrackSizesFrame);
+  aResult.SetLength(aSubgridAxis == eLogicalAxisInline ? aSubgrid->mGridColEnd
+                                                       : aSubgrid->mGridRowEnd);
+  auto parentAxis =
+      aSubgrid->mIsOrthogonal ? GetOrthogonalAxis(aSubgridAxis) : aSubgridAxis;
+  const auto& parentSizes = aUsedTrackSizes->mSizes[parentAxis];
+  MOZ_ASSERT(aUsedTrackSizes->mCanResolveLineRangeSize[parentAxis]);
+  if (parentSizes.IsEmpty()) {
+    return;
+  }
+  const auto& range = aSubgrid->mArea.LineRangeForAxis(parentAxis);
+  const auto cbwm = aUsedTrackSizesFrame->GetWritingMode();
+  const auto wm = aSubgridFrame->GetWritingMode();
+  // Recompute the MBP to resolve percentages against the resolved track sizes.
+  if (parentAxis == eLogicalAxisInline) {
+    // Find the subgrid's grid item frame in its parent grid container.  This
+    // is usually the same as aSubgridFrame but it may also have a ScrollFrame,
+    // FieldSetFrame etc.  We just loop until we see the first ancestor
+    // GridContainerFrame and pick the last frame we saw before that.
+    // Note that all subgrids are inside a parent (sub)grid container.
+    const nsIFrame* outerGridItemFrame = aSubgridFrame;
+    for (nsIFrame* parent = aSubgridFrame->GetParent();
+         parent != aUsedTrackSizesFrame;
+         parent = parent->GetParent()) {
+      MOZ_ASSERT(!parent->IsGridContainerFrame());
+      outerGridItemFrame = parent;
+    }
+    auto sizeInAxis = range.ToLength(aUsedTrackSizes->mSizes[parentAxis]);
+    LogicalSize pmPercentageBasis = aSubgrid->mIsOrthogonal
+        ? LogicalSize(wm, nscoord(0), sizeInAxis)
+        : LogicalSize(wm, sizeInAxis, nscoord(0));
+    GridItemInfo info(const_cast<nsIFrame*>(outerGridItemFrame),
+                      aSubgrid->mArea);
+    SubgridComputeMarginBorderPadding(info, pmPercentageBasis);
+  }
+  const LogicalMargin& mbp = aSubgrid->mMarginBorderPadding;
+  if (MOZ_LIKELY(cbwm.ParallelAxisStartsOnSameSide(parentAxis, wm))) {
+    uint32_t i = range.mStart;
+    nscoord startMBP = mbp.Start(parentAxis, cbwm);
+    nscoord startPos = parentSizes[i].mPosition + startMBP;
+    for (auto& sz : aResult) {
+      sz = parentSizes[i++];
+      sz.mPosition -= startPos;
+    }
+    aResult[0].mPosition = 0;
+    aResult[0].mBase -= startMBP;
+    aResult.LastElement().mBase -= mbp.End(parentAxis, cbwm);
+  } else {
+    const uint32_t first = range.mEnd - 1;
+    uint32_t i = first;
+    const auto& parentEnd = parentSizes[first];
+    nscoord startMBP = mbp.End(parentAxis, cbwm);
+    nscoord parentEndPos = parentEnd.mPosition + parentEnd.mBase - startMBP;
+    for (auto& sz : aResult) {
+      sz = parentSizes[i--];
+      sz.mPosition = parentEndPos - (sz.mPosition + sz.mBase);
+    }
+    aResult[0].mPosition = 0;
+    aResult[0].mBase -= startMBP;
+    aResult.LastElement().mBase -= mbp.Start(parentAxis, cbwm);
+  }
+}
+
+void nsGridContainerFrame::UsedTrackSizes::ResolveTrackSizesForAxis(
+  nsGridContainerFrame* aFrame, LogicalAxis aAxis, gfxContext& aRC) {
+  if (mCanResolveLineRangeSize[aAxis]) {
+    return;
+  }
+  if (!aFrame->IsSubgrid()) {
+    // We can't resolve sizes in this axis at this point. aFrame is the top grid
+    // container, which will store its final track sizes later once they're
+    // resolved in this axis (in GridReflowInput::CalculateTrackSizesForAxis).
+    // The single caller of this method only needs track sizes for
+    // calculating a CB size and it will treat it as indefinite when
+    // this happens.
+    return;
+  }
+  auto* parent = aFrame->ParentGridContainerForSubgrid();
+  auto* parentSizes = parent->GetUsedTrackSizes();
+  if (!parentSizes) {
+    parentSizes = new UsedTrackSizes();
+    parent->SetProperty(UsedTrackSizes::Prop(), parentSizes);
+  }
+  auto* subgrid = aFrame->GetProperty(Subgrid::Prop());
+  const auto parentAxis =
+    subgrid->mIsOrthogonal ? GetOrthogonalAxis(aAxis) : aAxis;
+  parentSizes->ResolveTrackSizesForAxis(parent, parentAxis, aRC);
+  if (!parentSizes->mCanResolveLineRangeSize[parentAxis]) {
+    if (aFrame->IsSubgrid(aAxis)) {
+      ResolveSubgridTrackSizesForAxis(aFrame, aAxis, subgrid, aRC,
+                                      NS_UNCONSTRAINEDSIZE);
+    }
+    return;
+  }
+  if (aFrame->IsSubgrid(aAxis)) {
+    CopyUsedTrackSizes(mSizes[aAxis], parent, parentSizes, aFrame, subgrid,
+                       aAxis);
+    mCanResolveLineRangeSize[aAxis] = true;
+  } else {
+    const auto& range = subgrid->mArea.LineRangeForAxis(parentAxis);
+    nscoord cbSize = range.ToLength(parentSizes->mSizes[parentAxis]);
+    ResolveSubgridTrackSizesForAxis(aFrame, aAxis, subgrid, aRC, cbSize);
+  }
+}
+
+void nsGridContainerFrame::UsedTrackSizes::ResolveSubgridTrackSizesForAxis(
+    nsGridContainerFrame* aFrame, LogicalAxis aAxis, Subgrid* aSubgrid,
+    gfxContext& aRC, nscoord aCBSize) {
+  GridReflowInput state(aFrame, aRC);
+  state.mGridItems = aSubgrid->mGridItems;
+  Grid grid;
+  grid.mGridColEnd = aSubgrid->mGridColEnd;
+  grid.mGridRowEnd = aSubgrid->mGridRowEnd;
+  state.CalculateTrackSizesForAxis(aAxis, grid, aCBSize,
+                                   SizingConstraint::NoConstraint);
+  const auto& tracks = aAxis == eLogicalAxisInline ? state.mCols : state.mRows;
+  mSizes[aAxis] = tracks.mSizes;
+  mCanResolveLineRangeSize[aAxis] = tracks.mCanResolveLineRangeSize;
+  MOZ_ASSERT(mCanResolveLineRangeSize[aAxis]);
+}
+
+void nsGridContainerFrame::GridReflowInput::CalculateTrackSizesForAxis(
+    LogicalAxis aAxis, const Grid& aGrid, nscoord aContentSize,
+    SizingConstraint aConstraint) {
+  auto& tracks = aAxis == eLogicalAxisInline ? mCols : mRows;
+  const auto& sizingFunctions =
+      aAxis == eLogicalAxisInline ? mColFunctions : mRowFunctions;
+  const auto& gapStyle = aAxis == eLogicalAxisInline ? mGridStyle->mColumnGap
+                                                     : mGridStyle->mRowGap;
+  uint32_t gridEnd =
+      aAxis == eLogicalAxisInline ? aGrid.mGridColEnd : aGrid.mGridRowEnd;
+  Maybe<SubgridFallbackTrackSizingFunctions> fallbackTrackSizing;
+
+  bool useParentGaps = false;
+  const bool isSubgriddedAxis = mFrame->IsSubgrid(aAxis);
+  if (MOZ_LIKELY(!isSubgriddedAxis)) {
+    tracks.Initialize(sizingFunctions, gapStyle, gridEnd, aContentSize);
+  } else {
+    tracks.mGridGap = nsLayoutUtils::ResolveGapToLength(gapStyle, aContentSize);
+    tracks.mContentBoxSize = aContentSize;
+    const auto* subgrid = mFrame->GetProperty(Subgrid::Prop());
+    tracks.mSizes.SetLength(gridEnd);
+    auto* parent = mFrame->ParentGridContainerForSubgrid();
+    auto parentAxis = subgrid->mIsOrthogonal ? GetOrthogonalAxis(aAxis) : aAxis;
+    const auto* parentSizes = parent->GetUsedTrackSizes();
+    if (parentSizes && parentSizes->mCanResolveLineRangeSize[parentAxis]) {
+      CopyUsedTrackSizes(tracks.mSizes, parent, parentSizes, mFrame, subgrid,
+                         aAxis);
+      useParentGaps = gapStyle.IsNormal();
+    } else {
+      fallbackTrackSizing.emplace(mFrame, subgrid, parent, parentAxis);
+      tracks.Initialize(
+          TrackSizingFunctions(fallbackTrackSizing->mMinSizingFunctions,
+                               fallbackTrackSizing->mMaxSizingFunctions,
+                               *fallbackTrackSizing->mAutoMinSizing,
+                               *fallbackTrackSizing->mAutoMaxSizing,
+                               fallbackTrackSizing->mHasRepeatAuto,
+                               fallbackTrackSizing->mRepeatAutoIndex),
+          gapStyle, gridEnd, aContentSize);
+    }
+  }
+
+  // We run the Track Sizing Algorithm in non-subgridded axes, and in some
+  // cases in a subgridded axis when our parent track sizes aren't resolved yet.
+  if (MOZ_LIKELY(!isSubgriddedAxis) || fallbackTrackSizing.isSome()) {
+    const size_t origGridItemCount = mGridItems.Length();
+    if (mFrame->HasSubgridItems(aAxis)) {
+      CollectSubgridItemsForAxis(aAxis, mGridItems);
+    }
+    tracks.CalculateSizes(
+        *this, mGridItems,
+        fallbackTrackSizing.isSome()
+            ? TrackSizingFunctions(fallbackTrackSizing->mMinSizingFunctions,
+                                   fallbackTrackSizing->mMaxSizingFunctions,
+                                   *fallbackTrackSizing->mAutoMinSizing,
+                                   *fallbackTrackSizing->mAutoMaxSizing,
+                                   fallbackTrackSizing->mHasRepeatAuto,
+                                   fallbackTrackSizing->mRepeatAutoIndex)
+            : sizingFunctions,
+        aContentSize,
+        aAxis == eLogicalAxisInline ? &GridArea::mCols : &GridArea::mRows,
+        aConstraint);
+    // XXXmats we're losing the baseline state of subgrid descendants that
+    // CollectSubgridItemsForAxis added here.  We need to propagate that
+    // state into the subgrid's Reflow somehow...
+    mGridItems.TruncateLength(origGridItemCount);
+  }
+
+  if (aContentSize != NS_UNCONSTRAINEDSIZE) {
+    tracks.AlignJustifyContent(mGridStyle, mWM, aContentSize, isSubgriddedAxis);
+  } else if (!useParentGaps) {
+    const nscoord gridGap = tracks.mGridGap;
+    nscoord pos = 0;
+    for (TrackSize& sz : tracks.mSizes) {
+      sz.mPosition = pos;
+      pos += sz.mBase + gridGap;
+    }
+  }
+
+  if (aConstraint == SizingConstraint::NoConstraint &&
+      (mFrame->HasSubgridItems() || mFrame->IsSubgrid())) {
+    mFrame->StoreUsedTrackSizes(aAxis, tracks.mSizes);
+  }
+
+  // positions and sizes are now final
+  tracks.mCanResolveLineRangeSize = true;
+}
+
 void nsGridContainerFrame::GridReflowInput::CalculateTrackSizes(
     const Grid& aGrid, const LogicalSize& aContentBox,
     SizingConstraint aConstraint) {
-  mCols.Initialize(mColFunctions, mGridStyle->mColumnGap, aGrid.mGridColEnd,
-                   aContentBox.ISize(mWM));
-  mRows.Initialize(mRowFunctions, mGridStyle->mRowGap, aGrid.mGridRowEnd,
-                   aContentBox.BSize(mWM));
-
-  mCols.CalculateSizes(*this, mGridItems, mColFunctions, aContentBox.ISize(mWM),
-                       &GridArea::mCols, aConstraint);
-  mCols.AlignJustifyContent(mGridStyle, mWM, aContentBox.ISize(mWM));
-  // Column positions and sizes are now final.
-  mCols.mCanResolveLineRangeSize = true;
-
-  mRows.CalculateSizes(*this, mGridItems, mRowFunctions, aContentBox.BSize(mWM),
-                       &GridArea::mRows, aConstraint);
+  CalculateTrackSizesForAxis(eLogicalAxisInline, aGrid,
+                             aContentBox.ISize(mWM), aConstraint);
+  CalculateTrackSizesForAxis(eLogicalAxisBlock, aGrid,
+                             aContentBox.BSize(mWM), aConstraint);
 }
 
 /**
  * (XXX share this utility function with nsFlexContainerFrame at some point)
  *
  * Helper for BuildDisplayList, to implement this special-case for grid
  * items from the spec:
  *   The painting order of grid items is exactly the same as inline blocks,
@@ -2577,59 +3391,47 @@ int32_t nsGridContainerFrame::Grid::Reso
     line = int32_t(aFromIndex) + aNth;
   } else {
     if (aNth == 0) {
       // <integer> was omitted; treat it as 1.
       aNth = 1;
     }
     bool isNameOnly = !aLine.mHasSpan && aLine.mInteger == 0;
     if (isNameOnly) {
-      const GridNamedArea* area = FindNamedArea(aLine.mLineName, aStyle);
-      if (area || HasImplicitNamedArea(aLine.mLineName)) {
-        // The given name is a named area - look for explicit lines named
+      AutoTArray<uint32_t, 16> implicitLines;
+      aNameMap.FindNamedAreas(aLine.mLineName, aSide, implicitLines);
+      if (!implicitLines.IsEmpty() ||
+          aNameMap.HasImplicitNamedArea(aLine.mLineName)) {
+        // aName is a named area - look for explicit lines named
         // <name>-start/-end depending on which side we're resolving.
         // http://dev.w3.org/csswg/css-grid/#grid-placement-slot
-        uint32_t implicitLine = 0;
         nsAutoString lineName(aLine.mLineName);
         if (IsStart(aSide)) {
           lineName.AppendLiteral("-start");
-          if (area) {
-            implicitLine =
-                IsBlock(aSide) ? area->mRowStart : area->mColumnStart;
-          }
         } else {
           lineName.AppendLiteral("-end");
-          if (area) {
-            implicitLine = IsBlock(aSide) ? area->mRowEnd : area->mColumnEnd;
-          }
         }
         line =
-            aNameMap.FindNamedLine(lineName, &aNth, aFromIndex, implicitLine);
+            aNameMap.FindNamedLine(lineName, &aNth, aFromIndex, implicitLines);
       }
     }
 
     if (line == 0) {
       // If mLineName ends in -start/-end, try the prefix as a named area.
-      uint32_t implicitLine = 0;
+      AutoTArray<uint32_t, 16> implicitLines;
       uint32_t index;
       bool useStart = IsNameWithStartSuffix(aLine.mLineName, &index);
       if (useStart || IsNameWithEndSuffix(aLine.mLineName, &index)) {
-        const GridNamedArea* area = FindNamedArea(
-            nsDependentSubstring(aLine.mLineName, 0, index), aStyle);
-        if (area) {
-          if (useStart) {
-            implicitLine =
-                IsBlock(aSide) ? area->mRowStart : area->mColumnStart;
-          } else {
-            implicitLine = IsBlock(aSide) ? area->mRowEnd : area->mColumnEnd;
-          }
-        }
+        auto side = MakeLogicalSide(
+            GetAxis(aSide), useStart ? eLogicalEdgeStart : eLogicalEdgeEnd);
+        aNameMap.FindNamedAreas(nsDependentSubstring(aLine.mLineName, 0, index),
+                                side, implicitLines);
       }
       line = aNameMap.FindNamedLine(aLine.mLineName, &aNth, aFromIndex,
-                                    implicitLine);
+                                    implicitLines);
     }
 
     if (line == 0) {
       MOZ_ASSERT(aNth != 0, "we found all N named lines but 'line' is zero!");
       int32_t edgeLine;
       if (aLine.mHasSpan) {
         // http://dev.w3.org/csswg/css-grid/#grid-placement-span-int
         // 'span <custom-ident> N'
@@ -2639,17 +3441,17 @@ int32_t nsGridContainerFrame::Grid::Reso
         // '<custom-ident> N'
         edgeLine = aNth < 0 ? 1 : aExplicitGridEnd;
       }
       // "If not enough lines with that name exist, all lines in the implicit
       // grid are assumed to have that name..."
       line = edgeLine + aNth;
     }
   }
-  return clamped(line, nsStyleGridLine::kMinLine, nsStyleGridLine::kMaxLine);
+  return clamped(line, aNameMap.mClampMinLine, aNameMap.mClampMaxLine);
 }
 
 nsGridContainerFrame::Grid::LinePair
 nsGridContainerFrame::Grid::ResolveLineRangeHelper(
     const nsStyleGridLine& aStart, const nsStyleGridLine& aEnd,
     const LineNameMap& aNameMap, LogicalAxis aAxis, uint32_t aExplicitGridEnd,
     const nsStylePosition* aStyle) {
   MOZ_ASSERT(int32_t(nsGridContainerFrame::kAutoLine) >
@@ -2671,17 +3473,17 @@ nsGridContainerFrame::Grid::ResolveLineR
     uint32_t from = aEnd.mInteger < 0 ? aExplicitGridEnd + 1 : 0;
     auto end = ResolveLine(aEnd, aEnd.mInteger, from, aNameMap,
                            MakeLogicalSide(aAxis, eLogicalEdgeEnd),
                            aExplicitGridEnd, aStyle);
     int32_t span = aStart.mInteger == 0 ? 1 : aStart.mInteger;
     if (end <= 1) {
       // The end is at or before the first explicit line, thus all lines before
       // it match <custom-ident> since they're implicit.
-      int32_t start = std::max(end - span, nsStyleGridLine::kMinLine);
+      int32_t start = std::max(end - span, aNameMap.mClampMinLine);
       return LinePair(start, end);
     }
     auto start = ResolveLine(aStart, -span, end, aNameMap,
                              MakeLogicalSide(aAxis, eLogicalEdgeStart),
                              aExplicitGridEnd, aStyle);
     return LinePair(start, end);
   }
 
@@ -2721,54 +3523,53 @@ nsGridContainerFrame::Grid::ResolveLineR
       if (aEnd.mLineName.IsEmpty()) {
         return LinePair(start, start + nth);
       }
       from = 0;
     } else {
       if (start >= int32_t(aExplicitGridEnd)) {
         // The start is at or after the last explicit line, thus all lines
         // after it match <custom-ident> since they're implicit.
-        return LinePair(start,
-                        std::min(start + nth, nsStyleGridLine::kMaxLine));
+        return LinePair(start, std::min(start + nth, aNameMap.mClampMaxLine));
       }
       from = start;
     }
   } else {
     from = aEnd.mInteger < 0 ? aExplicitGridEnd + 1 : 0;
   }
   auto end = ResolveLine(aEnd, nth, from, aNameMap,
                          MakeLogicalSide(aAxis, eLogicalEdgeEnd),
                          aExplicitGridEnd, aStyle);
   if (start == int32_t(kAutoLine)) {
     // auto / definite line
-    start = std::max(nsStyleGridLine::kMinLine, end - 1);
+    start = std::max(aNameMap.mClampMinLine, end - 1);
   }
   return LinePair(start, end);
 }
 
 nsGridContainerFrame::LineRange nsGridContainerFrame::Grid::ResolveLineRange(
     const nsStyleGridLine& aStart, const nsStyleGridLine& aEnd,
     const LineNameMap& aNameMap, LogicalAxis aAxis, uint32_t aExplicitGridEnd,
     const nsStylePosition* aStyle) {
   LinePair r = ResolveLineRangeHelper(aStart, aEnd, aNameMap, aAxis,
                                       aExplicitGridEnd, aStyle);
   MOZ_ASSERT(r.second != int32_t(kAutoLine));
 
   if (r.first == int32_t(kAutoLine)) {
-    // r.second is a span, clamp it to kMaxLine - 1 so that the returned
-    // range has a HypotheticalEnd <= kMaxLine.
+    // r.second is a span, clamp it to aNameMap.mClampMaxLine - 1 so that
+    // the returned range has a HypotheticalEnd <= aNameMap.mClampMaxLine.
     // http://dev.w3.org/csswg/css-grid/#overlarge-grids
-    r.second = std::min(r.second, nsStyleGridLine::kMaxLine - 1);
+    r.second = std::min(r.second, aNameMap.mClampMaxLine - 1);
   } else {
     // http://dev.w3.org/csswg/css-grid/#grid-placement-errors
     if (r.first > r.second) {
       Swap(r.first, r.second);
     } else if (r.first == r.second) {
-      if (MOZ_UNLIKELY(r.first == nsStyleGridLine::kMaxLine)) {
-        r.first = nsStyleGridLine::kMaxLine - 1;
+      if (MOZ_UNLIKELY(r.first == aNameMap.mClampMaxLine)) {
+        r.first = aNameMap.mClampMaxLine - 1;
       }
       r.second = r.first + 1;  // XXX subgrid explicit size instead of 1?
     }
   }
   return LineRange(r.first, r.second);
 }
 
 nsGridContainerFrame::GridArea nsGridContainerFrame::Grid::PlaceDefinite(
@@ -2970,52 +3771,214 @@ void nsGridContainerFrame::Grid::PlaceAu
   }
   MOZ_ASSERT(col < gridColEnd || row == 0,
              "expected row 0 for placing in a new column");
   aArea->mCols.ResolveAutoPosition(col, aClampMaxColLine);
   aArea->mRows.ResolveAutoPosition(row, aClampMaxRowLine);
   MOZ_ASSERT(aArea->IsDefinite());
 }
 
+void nsGridContainerFrame::Grid::SubgridPlaceGridItems(
+    GridReflowInput& aParentState, Grid* aParentGrid,
+    const GridItemInfo& aGridItem) {
+  MOZ_ASSERT(aGridItem.mArea.IsDefinite() ||
+                 aGridItem.mFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW),
+             "the subgrid's lines should be resolved by now");
+  if (aGridItem.IsSubgrid(eLogicalAxisInline)) {
+    aParentState.mFrame->AddStateBits(NS_STATE_GRID_HAS_COL_SUBGRID_ITEM);
+  }
+  if (aGridItem.IsSubgrid(eLogicalAxisBlock)) {
+    aParentState.mFrame->AddStateBits(NS_STATE_GRID_HAS_ROW_SUBGRID_ITEM);
+  }
+  auto* childGrid = aGridItem.SubgridFrame();
+  const auto* pos = childGrid->StylePosition();
+  childGrid->NormalizeChildLists();
+  GridReflowInput state(childGrid, aParentState.mRenderingContext);
+  childGrid->InitImplicitNamedAreas(pos);
+
+  const bool isOrthogonal = aParentState.mWM.IsOrthogonalTo(state.mWM);
+  // Record the subgrid's GridArea in a frame property.
+  auto* subgrid = childGrid->GetProperty(Subgrid::Prop());
+  if (!subgrid) {
+    subgrid = new Subgrid(aGridItem.mArea, isOrthogonal, aParentState.mWM);
+    childGrid->SetProperty(Subgrid::Prop(), subgrid);
+  } else {
+    subgrid->mArea = aGridItem.mArea;
+    subgrid->mIsOrthogonal = isOrthogonal;
+    subgrid->mGridItems.Clear();
+    subgrid->mAbsPosItems.Clear();
+  }
+
+  // Abs.pos. subgrids may have kAutoLine in their area.  Map those to the edge
+  // line in the parent's explicit grid (zero-based line numbers).  This is ok
+  // because it's only used for translating lines and such, not for layout.
+  if (MOZ_UNLIKELY(subgrid->mArea.mCols.mStart == kAutoLine)) {
+    subgrid->mArea.mCols.mStart = 0;
+  }
+  if (MOZ_UNLIKELY(subgrid->mArea.mCols.mEnd == kAutoLine)) {
+    subgrid->mArea.mCols.mEnd = aParentGrid->mExplicitGridOffsetCol +
+                                aParentGrid->mExplicitGridColEnd - 1;
+  }
+  if (MOZ_UNLIKELY(subgrid->mArea.mRows.mStart == kAutoLine)) {
+    subgrid->mArea.mRows.mStart = 0;
+  }
+  if (MOZ_UNLIKELY(subgrid->mArea.mRows.mEnd == kAutoLine)) {
+    subgrid->mArea.mRows.mEnd = aParentGrid->mExplicitGridOffsetRow +
+                                aParentGrid->mExplicitGridRowEnd - 1;
+  }
+
+  // The min/sz/max sizes are the input to the "repeat-to-fill" algorithm:
+  // https://drafts.csswg.org/css-grid/#auto-repeat
+  // They're only used for auto-repeat in a non-subgridded axis so we skip
+  // computing them otherwise.
+  RepeatTrackSizingInput repeatSizing(state.mWM);
+  if (!childGrid->IsColSubgrid() && state.mColFunctions.mHasRepeatAuto) {
+    repeatSizing.SetDefiniteSizes(eLogicalAxisInline, state.mWM,
+                                  state.mGridStyle->MinISize(state.mWM),
+                                  state.mGridStyle->ISize(state.mWM),
+                                  state.mGridStyle->MaxISize(state.mWM));
+  }
+  if (!childGrid->IsRowSubgrid() && state.mRowFunctions.mHasRepeatAuto) {
+    repeatSizing.SetDefiniteSizes(eLogicalAxisBlock, state.mWM,
+                                  state.mGridStyle->MinBSize(state.mWM),
+                                  state.mGridStyle->BSize(state.mWM),
+                                  state.mGridStyle->MaxBSize(state.mWM));
+  }
+
+  PlaceGridItems(state, repeatSizing);
+
+  subgrid->mGridItems = std::move(state.mGridItems);
+  subgrid->mAbsPosItems = std::move(state.mAbsPosItems);
+  subgrid->mGridColEnd = mGridColEnd;
+  subgrid->mGridRowEnd = mGridRowEnd;
+}
+
 void nsGridContainerFrame::Grid::PlaceGridItems(
-    GridReflowInput& aState, const LogicalSize& aComputedMinSize,
-    const LogicalSize& aComputedSize, const LogicalSize& aComputedMaxSize) {
+    GridReflowInput& aState, const RepeatTrackSizingInput& aSizes) {
+  MOZ_ASSERT(mCellMap.mCells.IsEmpty(), "unexpected entries in cell map");
+
   mAreas = aState.mFrame->GetImplicitNamedAreas();
-  const nsStylePosition* const gridStyle = aState.mGridStyle;
-  MOZ_ASSERT(mCellMap.mCells.IsEmpty(), "unexpected entries in cell map");
+
+  if (aState.mFrame->HasSubgridItems()) {
+    if (auto* uts = aState.mFrame->GetUsedTrackSizes()) {
+      uts->mCanResolveLineRangeSize = { false, false };
+      uts->mSizes[eLogicalAxisInline].ClearAndRetainStorage();
+      uts->mSizes[eLogicalAxisBlock].ClearAndRetainStorage();
+    }
+  }
 
   // SubgridPlaceGridItems will set these if we find any subgrid items.
   aState.mFrame->RemoveStateBits(NS_STATE_GRID_HAS_COL_SUBGRID_ITEM |
                                  NS_STATE_GRID_HAS_ROW_SUBGRID_ITEM);
 
   // http://dev.w3.org/csswg/css-grid/#grid-definition
   // Initialize the end lines of the Explicit Grid (mExplicitGridCol[Row]End).
   // This is determined by the larger of the number of rows/columns defined
   // by 'grid-template-areas' and the 'grid-template-rows'/'-columns', plus one.
   // Also initialize the Implicit Grid (mGridCol[Row]End) to the same values.
   // Note that this is for a grid with a 1,1 origin.  We'll change that
   // to a 0,0 based grid after placing definite lines.
+  const nsStylePosition* const gridStyle = aState.mGridStyle;
   auto areas = gridStyle->mGridTemplateAreas.get();
+  int32_t clampMinColLine = nsStyleGridLine::kMinLine;
   int32_t clampMaxColLine = nsStyleGridLine::kMaxLine;
-  uint32_t numRepeatCols = aState.mColFunctions.InitRepeatTracks(
-      gridStyle->mColumnGap, aComputedMinSize.ISize(aState.mWM),
-      aComputedSize.ISize(aState.mWM), aComputedMaxSize.ISize(aState.mWM));
-  mGridColEnd = mExplicitGridColEnd =
-      aState.mColFunctions.ComputeExplicitGridEnd(areas ? areas->mNColumns + 1
-                                                        : 1);
-  LineNameMap colLineNameMap(gridStyle->GridTemplateColumns(), numRepeatCols);
+  uint32_t numRepeatCols;
+  const LineNameMap* parentLineNameMap = nullptr;
+  const LineRange* subgridRange = nullptr;
+  bool subgridAxisIsSameDirection = true;
+  if (!aState.mFrame->IsColSubgrid()) {
+    numRepeatCols = aState.mColFunctions.InitRepeatTracks(
+        gridStyle->mColumnGap, aSizes.mMin.ISize(aState.mWM),
+        aSizes.mSize.ISize(aState.mWM), aSizes.mMax.ISize(aState.mWM));
+    uint32_t areaCols = areas ? areas->mNColumns + 1 : 1;
+    mExplicitGridColEnd = aState.mColFunctions.ComputeExplicitGridEnd(areaCols);
+  } else {
+    const auto* subgrid = aState.mFrame->GetProperty(Subgrid::Prop());
+    subgridRange = &subgrid->SubgridCols();
+    uint32_t extent = subgridRange->Extent();
+    mExplicitGridColEnd = extent + 1;  // the grid is 1-based at this point
+    clampMinColLine = 1;
+    clampMaxColLine = mExplicitGridColEnd;
+    const auto& cols = gridStyle->GridTemplateColumns();
+    numRepeatCols =
+        cols.HasRepeatAuto()
+            ? std::max<uint32_t>(extent - cols.mLineNameLists.Length(), 1)
+            : 0;
+    parentLineNameMap =
+        ParentLineMapForAxis(subgrid->mIsOrthogonal, eLogicalAxisInline);
+    auto parentWM =
+        aState.mFrame->ParentGridContainerForSubgrid()->GetWritingMode();
+    subgridAxisIsSameDirection =
+        aState.mWM.ParallelAxisStartsOnSameSide(eLogicalAxisInline, parentWM);
+  }
+  mGridColEnd = mExplicitGridColEnd;
+  LineNameMap colLineNameMap(
+      gridStyle, mAreas, gridStyle->GridTemplateColumns(), numRepeatCols,
+      clampMinColLine, clampMaxColLine, parentLineNameMap, subgridRange,
+      subgridAxisIsSameDirection);
+
+  int32_t clampMinRowLine = nsStyleGridLine::kMinLine;
 
   int32_t clampMaxRowLine = nsStyleGridLine::kMaxLine;
-  uint32_t numRepeatRows = aState.mRowFunctions.InitRepeatTracks(
-      gridStyle->mRowGap, aComputedMinSize.BSize(aState.mWM),
-      aComputedSize.BSize(aState.mWM), aComputedMaxSize.BSize(aState.mWM));
-  mGridRowEnd = mExplicitGridRowEnd =
-      aState.mRowFunctions.ComputeExplicitGridEnd(areas ? areas->NRows() + 1
-                                                        : 1);
-  LineNameMap rowLineNameMap(gridStyle->GridTemplateRows(), numRepeatRows);
+  uint32_t numRepeatRows;
+  if (!aState.mFrame->IsRowSubgrid()) {
+    numRepeatRows = aState.mRowFunctions.InitRepeatTracks(
+        gridStyle->mRowGap, aSizes.mMin.BSize(aState.mWM),
+        aSizes.mSize.BSize(aState.mWM), aSizes.mMax.BSize(aState.mWM));
+    uint32_t areaRows = areas ? areas->NRows() + 1 : 1;
+    mExplicitGridRowEnd = aState.mRowFunctions.ComputeExplicitGridEnd(areaRows);
+    parentLineNameMap = nullptr;
+    subgridRange = nullptr;
+  } else {
+    const auto* subgrid = aState.mFrame->GetProperty(Subgrid::Prop());
+    subgridRange = &subgrid->SubgridRows();
+    uint32_t extent = subgridRange->Extent();
+    mExplicitGridRowEnd = extent + 1;  // the grid is 1-based at this point
+    clampMinRowLine = 1;
+    clampMaxRowLine = mExplicitGridRowEnd;
+    const auto& rows = gridStyle->GridTemplateRows();
+    numRepeatRows =
+        rows.HasRepeatAuto()
+            ? std::max<uint32_t>(extent - rows.mLineNameLists.Length(), 1)
+            : 0;
+    parentLineNameMap =
+        ParentLineMapForAxis(subgrid->mIsOrthogonal, eLogicalAxisBlock);
+    auto parentWM =
+        aState.mFrame->ParentGridContainerForSubgrid()->GetWritingMode();
+    subgridAxisIsSameDirection =
+        aState.mWM.ParallelAxisStartsOnSameSide(eLogicalAxisBlock, parentWM);
+  }
+  mGridRowEnd = mExplicitGridRowEnd;
+  LineNameMap rowLineNameMap(gridStyle, mAreas, gridStyle->GridTemplateRows(),
+                             numRepeatRows, clampMinRowLine, clampMaxRowLine,
+                             parentLineNameMap, subgridRange,
+                             subgridAxisIsSameDirection);
+
+  const bool isSubgridOrItemInSubgrid =
+      aState.mFrame->IsSubgrid() || !!mParentGrid;
+  auto SetSubgridChildEdgeBits =
+      [this, isSubgridOrItemInSubgrid](GridItemInfo& aItem) -> void {
+    if (isSubgridOrItemInSubgrid) {
+      const auto& area = aItem.mArea;
+      if (area.mCols.mStart == 0) {
+        aItem.mState[eLogicalAxisInline] |= ItemState::eStartEdge;
+      }
+      if (area.mCols.mEnd == mGridColEnd) {
+        aItem.mState[eLogicalAxisInline] |= ItemState::eEndEdge;
+      }
+      if (area.mRows.mStart == 0) {
+        aItem.mState[eLogicalAxisBlock] |= ItemState::eStartEdge;
+      }
+      if (area.mRows.mEnd == mGridRowEnd) {
+        aItem.mState[eLogicalAxisBlock] |= ItemState::eEndEdge;
+      }
+    }
+  };
+
+  SetLineMaps(&colLineNameMap, &rowLineNameMap);
 
   // http://dev.w3.org/csswg/css-grid/#line-placement
   // Resolve definite positions per spec chap 9.2.
   int32_t minCol = 1;
   int32_t minRow = 1;
   aState.mGridItems.ClearAndRetainStorage();
   aState.mIter.Reset();
   for (; !aState.mIter.AtEnd(); aState.mIter.Next()) {
@@ -3042,28 +4005,34 @@ void nsGridContainerFrame::Grid::PlaceGr
   const int32_t offsetToColZero = int32_t(mExplicitGridOffsetCol) - 1;
   const int32_t offsetToRowZero = int32_t(mExplicitGridOffsetRow) - 1;
   mGridColEnd += offsetToColZero;
   mGridRowEnd += offsetToRowZero;
   clampMaxColLine += offsetToColZero;
   clampMaxRowLine += offsetToRowZero;
   aState.mIter.Reset();
   for (; !aState.mIter.AtEnd(); aState.mIter.Next()) {
-    GridArea& area = aState.mGridItems[aState.mIter.ItemIndex()].mArea;
+    auto& item = aState.mGridItems[aState.mIter.ItemIndex()];
+    GridArea& area = item.mArea;
     if (area.mCols.IsDefinite()) {
       area.mCols.mStart = area.mCols.mUntranslatedStart + offsetToColZero;
       area.mCols.mEnd = area.mCols.mUntranslatedEnd + offsetToColZero;
     }
     if (area.mRows.IsDefinite()) {
       area.mRows.mStart = area.mRows.mUntranslatedStart + offsetToRowZero;
       area.mRows.mEnd = area.mRows.mUntranslatedEnd + offsetToRowZero;
     }
     if (area.IsDefinite()) {
+      if (item.IsSubgrid()) {
+        Grid grid(this);
+        grid.SubgridPlaceGridItems(aState, this, item);
+      }
       mCellMap.Fill(area);
       InflateGridFor(area);
+      SetSubgridChildEdgeBits(item);
     }
   }
 
   // http://dev.w3.org/csswg/css-grid/#auto-placement-algo
   // Step 1, place 'auto' items that have one definite position -
   // definite row (column) for grid-auto-flow:row (column).
   auto flowStyle = gridStyle->mGridAutoFlow;
   const bool isRowOrder = (flowStyle & NS_STYLE_GRID_AUTO_FLOW_ROW);
@@ -3074,27 +4043,33 @@ void nsGridContainerFrame::Grid::PlaceGr
     if (isSparse) {
       cursors.emplace();
     }
     auto placeAutoMinorFunc =
         isRowOrder ? &Grid::PlaceAutoCol : &Grid::PlaceAutoRow;
     uint32_t clampMaxLine = isRowOrder ? clampMaxColLine : clampMaxRowLine;
     aState.mIter.Reset();
     for (; !aState.mIter.AtEnd(); aState.mIter.Next()) {
-      GridArea& area = aState.mGridItems[aState.mIter.ItemIndex()].mArea;
+      auto& item = aState.mGridItems[aState.mIter.ItemIndex()];
+      GridArea& area = item.mArea;
       LineRange& major = isRowOrder ? area.mRows : area.mCols;
       LineRange& minor = isRowOrder ? area.mCols : area.mRows;
       if (major.IsDefinite() && minor.IsAuto()) {
         // Items with 'auto' in the minor dimension only.
         uint32_t cursor = 0;
         if (isSparse) {
           cursors->Get(major.mStart, &cursor);
         }
         (this->*placeAutoMinorFunc)(cursor, &area, clampMaxLine);
+        if (item.IsSubgrid()) {
+          Grid grid(this);
+          grid.SubgridPlaceGridItems(aState, this, item);
+        }
         mCellMap.Fill(area);
+        SetSubgridChildEdgeBits(item);
         if (isSparse) {
           cursors->Put(major.mStart, minor.mEnd);
         }
       }
       InflateGridFor(area);  // Step 2, inflating for auto items too
     }
   }
 
@@ -3109,20 +4084,20 @@ void nsGridContainerFrame::Grid::PlaceGr
   // Step 3, place the remaining grid items
   uint32_t cursorMajor = 0;  // for 'dense' these two cursors will stay at 0,0
   uint32_t cursorMinor = 0;
   auto placeAutoMajorFunc =
       isRowOrder ? &Grid::PlaceAutoRow : &Grid::PlaceAutoCol;
   uint32_t clampMaxMajorLine = isRowOrder ? clampMaxRowLine : clampMaxColLine;
   aState.mIter.Reset();
   for (; !aState.mIter.AtEnd(); aState.mIter.Next()) {
-    GridArea& area = aState.mGridItems[aState.mIter.ItemIndex()].mArea;
-    MOZ_ASSERT(
-        *aState.mIter == aState.mGridItems[aState.mIter.ItemIndex()].mFrame,
-        "iterator out of sync with aState.mGridItems");
+    auto& item = aState.mGridItems[aState.mIter.ItemIndex()];
+    GridArea& area = item.mArea;
+    MOZ_ASSERT(*aState.mIter == item.mFrame,
+               "iterator out of sync with aState.mGridItems");
     LineRange& major = isRowOrder ? area.mRows : area.mCols;
     LineRange& minor = isRowOrder ? area.mCols : area.mRows;
     if (major.IsAuto()) {
       if (minor.IsDefinite()) {
         // Items with 'auto' in the major dimension only.
         if (isSparse) {
           if (minor.mStart < cursorMinor) {
             ++cursorMajor;
@@ -3151,18 +4126,23 @@ void nsGridContainerFrame::Grid::PlaceGr
           MOZ_ASSERT(cursorMajor <= gridMajorEnd,
                      "we shouldn't need to place items further than 1 track "
                      "past the current end of the grid, in major dimension");
           MOZ_ASSERT(cursorMinor <= gridMinorEnd,
                      "we shouldn't add implicit minor tracks for auto/auto");
 #endif
         }
       }
+      if (item.IsSubgrid()) {
+        Grid grid(this);
+        grid.SubgridPlaceGridItems(aState, this, item);
+      }
       mCellMap.Fill(area);
       InflateGridFor(area);
+      SetSubgridChildEdgeBits(item);
     }
   }
 
   if (aState.mFrame->IsAbsoluteContainer()) {
     // 9.4 Absolutely-positioned Grid Items
     // http://dev.w3.org/csswg/css-grid/#abspos-items
     // We only resolve definite lines here; we'll align auto positions to the
     // grid container later during reflow.
@@ -3190,16 +4170,20 @@ void nsGridContainerFrame::Grid::PlaceGr
         area.mCols.mEnd = area.mCols.mUntranslatedEnd + offsetToColZero;
       }
       if (area.mRows.mUntranslatedStart != int32_t(kAutoLine)) {
         area.mRows.mStart = area.mRows.mUntranslatedStart + offsetToRowZero;
       }
       if (area.mRows.mUntranslatedEnd != int32_t(kAutoLine)) {
         area.mRows.mEnd = area.mRows.mUntranslatedEnd + offsetToRowZero;
       }
+      if (info->IsSubgrid()) {
+        Grid grid(this);
+        grid.SubgridPlaceGridItems(aState, this, *info);
+      }
     }
   }
 
   // Count empty 'auto-fit' tracks in the repeat() range.
   // |colAdjust| will have a count for each line in the grid of how many
   // tracks were empty between the start of the grid and that line.
 
   // Since this loop is concerned with just the repeat tracks, we
@@ -3413,41 +4397,187 @@ static nscoord MeasuringReflow(nsIFrame*
                                       LogicalPoint(wm), nsSize(), flags);
 #ifdef DEBUG
   parent->DeleteProperty(nsContainerFrame::DebugReflowingWithInfiniteISize());
 #endif
   return childSize.BSize(wm);
 }
 
 /**
+ * Return the accumulated margin+border+padding in aAxis for aFrame (a subgrid)
+ * and its ancestor subgrids.
+ */
+static LogicalMargin SubgridAccumulatedMarginBorderPadding(
+    nsIFrame* aFrame, const Subgrid* aSubgrid, WritingMode aResultWM,
+    LogicalAxis aAxis) {
+  MOZ_ASSERT(aFrame->IsGridContainerFrame());
+  auto* subgridFrame = static_cast<nsGridContainerFrame*>(aFrame);
+  LogicalMargin result(aSubgrid->mMarginBorderPadding);
+  auto* parent = subgridFrame->ParentGridContainerForSubgrid();
+  auto subgridCBWM = parent->GetWritingMode();
+  auto childRange = aSubgrid->mArea.LineRangeForAxis(aAxis);
+  bool skipStartSide = false;
+  bool skipEndSide = false;
+  auto axis = aSubgrid->mIsOrthogonal ? GetOrthogonalAxis(aAxis) : aAxis;
+  // If aFrame's parent is also a subgrid, then add its MBP on the edges that
+  // are adjacent (i.e. start or end in the same track), recursively.
+  // ("parent" refers to the grid-frame we're currently adding MBP for,
+  // and "grandParent" its parent, as we walk up the chain.)
+  while (parent->IsSubgrid(axis)) {
+    auto* parentSubgrid = parent->GetProperty(Subgrid::Prop());
+    auto* grandParent = parent->ParentGridContainerForSubgrid();
+    auto parentCBWM = grandParent->GetWritingMode();
+    if (parentCBWM.IsOrthogonalTo(subgridCBWM)) {
+      axis = GetOrthogonalAxis(axis);
+    }
+    const auto& parentRange = parentSubgrid->mArea.LineRangeForAxis(axis);
+    bool sameDir = parentCBWM.ParallelAxisStartsOnSameSide(axis, subgridCBWM);
+    if (sameDir) {
+      skipStartSide |= childRange.mStart != 0;
+      skipEndSide |= childRange.mEnd != parentRange.Extent();
+    } else {
+      skipEndSide |= childRange.mStart != 0;
+      skipStartSide |= childRange.mEnd != parentRange.Extent();
+    }
+    if (skipStartSide && skipEndSide) {
+      break;
+    }
+    auto mbp =
+        parentSubgrid->mMarginBorderPadding.ConvertTo(subgridCBWM, parentCBWM);
+    if (skipStartSide) {
+      mbp.Start(aAxis, subgridCBWM) = nscoord(0);
+    }
+    if (skipEndSide) {
+      mbp.End(aAxis, subgridCBWM) = nscoord(0);
+    }
+    result += mbp;
+    parent = grandParent;
+    childRange = parentRange;
+  }
+  return result.ConvertTo(aResultWM, subgridCBWM);
+}
+
+/**
  * Return the [min|max]-content contribution of aChild to its parent (i.e.
  * the child's margin-box) in aAxis.
  */
 static nscoord ContentContribution(
     const GridItemInfo& aGridItem, const GridReflowInput& aState,
     gfxContext* aRC, WritingMode aCBWM, LogicalAxis aAxis,
     const Maybe<LogicalSize>& aPercentageBasis, IntrinsicISizeType aConstraint,
     nscoord aMinSizeClamp = NS_MAXSIZE, uint32_t aFlags = 0) {
   nsIFrame* child = aGridItem.mFrame;
+
+  nscoord extraMargin = 0;
+  nsGridContainerFrame::Subgrid* subgrid = nullptr;
+  if (child->GetParent() != aState.mFrame) {
+    // |child| is a subgrid descendant, so it contributes its subgrids'
+    // margin+border+padding for any edge tracks that it spans.
+    auto* subgridFrame = child->GetParent();
+    subgrid = subgridFrame->GetProperty(Subgrid::Prop());
+    const auto itemEdgeBits = aGridItem.mState[aAxis] & ItemState::eEdgeBits;
+    if (itemEdgeBits) {
+      LogicalMargin mbp = SubgridAccumulatedMarginBorderPadding(
+          subgridFrame, subgrid, aCBWM, aAxis);
+      if (itemEdgeBits & ItemState::eStartEdge) {
+        extraMargin += mbp.Start(aAxis, aCBWM);
+      }
+      if (itemEdgeBits & ItemState::eEndEdge) {
+        extraMargin += mbp.End(aAxis, aCBWM);
+      }
+    }
+    // It also contributes (half of) the subgrid's gap on its edges (if any)
+    // subtracted by the non-subgrid ancestor grid container's gap.
+    // Note that this can also be negative since it's considered a margin.
+    if (itemEdgeBits != ItemState::eEdgeBits) {
+      auto subgridAxis = aCBWM.IsOrthogonalTo(subgridFrame->GetWritingMode())
+                              ? GetOrthogonalAxis(aAxis) : aAxis;
+      auto& gapStyle = subgridAxis == eLogicalAxisBlock ?
+          subgridFrame->StylePosition()->mRowGap :
+          subgridFrame->StylePosition()->mColumnGap;
+      if (!gapStyle.IsNormal()) {
+        auto subgridExtent =
+            subgridAxis == eLogicalAxisBlock ? subgrid->mGridRowEnd
+                                             : subgrid->mGridColEnd;
+        if (subgridExtent > 1) {
+          nscoord subgridGap =
+              nsLayoutUtils::ResolveGapToLength(gapStyle, NS_UNCONSTRAINEDSIZE);
+          auto& tracks = aAxis == eLogicalAxisBlock ? aState.mRows : aState.mCols;
+          auto gapDelta = subgridGap - tracks.mGridGap;
+          if (!itemEdgeBits) {
+            extraMargin += gapDelta;
+          } else {
+            extraMargin += gapDelta / 2;
+          }
+        }
+      }
+    }
+  }
+
   PhysicalAxis axis(aCBWM.PhysicalAxis(aAxis));
   nscoord size = nsLayoutUtils::IntrinsicForAxis(
       axis, aRC, child, aConstraint, aPercentageBasis,
       aFlags | nsLayoutUtils::BAIL_IF_REFLOW_NEEDED, aMinSizeClamp);
-  if (size == NS_INTRINSIC_ISIZE_UNKNOWN) {
+  auto childWM = child->GetWritingMode();
+  const bool isOrthogonal = childWM.IsOrthogonalTo(aCBWM);
+  auto childAxis = isOrthogonal ? GetOrthogonalAxis(aAxis) : aAxis;
+  if (size == NS_INTRINSIC_ISIZE_UNKNOWN && childAxis == eLogicalAxisBlock) {
     // We need to reflow the child to find its BSize contribution.
     // XXX this will give mostly correct results for now (until bug 1174569).
     nscoord availISize = INFINITE_ISIZE_COORD;
     nscoord availBSize = NS_UNCONSTRAINEDSIZE;
-    auto childWM = child->GetWritingMode();
-    const bool isOrthogonal = childWM.IsOrthogonalTo(aCBWM);
     // The next two variables are MinSizeClamp values in the child's axes.
     nscoord iMinSizeClamp = NS_MAXSIZE;
     nscoord bMinSizeClamp = NS_MAXSIZE;
     LogicalSize cbSize(childWM, 0, NS_UNCONSTRAINEDSIZE);
-    if (aState.mCols.mCanResolveLineRangeSize) {
+    // Below, we try to resolve the child's grid-area size in its inline-axis
+    // to use as the CB/Available size in the MeasuringReflow that follows.
+    if (child->GetParent() != aState.mFrame) {
+      // This item is a child of a subgrid descendant.
+      auto* subgridFrame = static_cast<nsGridContainerFrame*>(child->GetParent());
+      MOZ_ASSERT(subgridFrame->IsGridContainerFrame());
+      auto* uts = subgridFrame->GetProperty(UsedTrackSizes::Prop());
+      if (!uts) {
+        uts = new UsedTrackSizes();
+        subgridFrame->SetProperty(UsedTrackSizes::Prop(), uts);
+      }
+      // The grid-item's inline-axis as expressed in the subgrid's WM.
+      auto subgridAxis = childWM.IsOrthogonalTo(subgridFrame->GetWritingMode())
+                            ? eLogicalAxisBlock
+                            : eLogicalAxisInline;
+      uts->ResolveTrackSizesForAxis(subgridFrame, subgridAxis, *aRC);
+      if (uts->mCanResolveLineRangeSize[subgridAxis]) {
+        auto* subgrid =
+            subgridFrame->GetProperty(nsGridContainerFrame::Subgrid::Prop());
+        const GridItemInfo* originalItem = nullptr;
+        for (const auto& item : subgrid->mGridItems) {
+          if (item.mFrame == child) {
+            originalItem = &item;
+            break;
+          }
+        }
+        MOZ_ASSERT(originalItem, "huh?");
+        const auto& range = originalItem->mArea.LineRangeForAxis(subgridAxis);
+        nscoord pos, sz;
+        range.ToPositionAndLength(uts->mSizes[subgridAxis], &pos, &sz);
+        if (childWM.IsOrthogonalTo(subgridFrame->GetWritingMode())) {
+          availBSize = sz;
+          cbSize.BSize(childWM) = sz;
+          if (aGridItem.mState[aAxis] & ItemState::eClampMarginBoxMinSize) {
+            bMinSizeClamp = sz;
+          }
+        } else {
+          availISize = sz;
+          cbSize.ISize(childWM) = sz;
+          if (aGridItem.mState[aAxis] & ItemState::eClampMarginBoxMinSize) {
+            iMinSizeClamp = sz;
+          }
+        }
+      }
+    } else if (aState.mCols.mCanResolveLineRangeSize) {
       nscoord sz = aState.mCols.ResolveSize(aGridItem.mArea.mCols);
       if (isOrthogonal) {
         availBSize = sz;
         cbSize.BSize(childWM) = sz;
         if (aGridItem.mState[aAxis] & ItemState::eClampMarginBoxMinSize) {
           bMinSizeClamp = sz;
         }
       } else {
@@ -3476,16 +4606,17 @@ static nscoord ContentContribution(
     }
   }
   MOZ_ASSERT(aGridItem.mBaselineOffset[aAxis] >= 0,
              "baseline offset should be non-negative at this point");
   MOZ_ASSERT((aGridItem.mState[aAxis] & ItemState::eIsBaselineAligned) ||
                  aGridItem.mBaselineOffset[aAxis] == nscoord(0),
              "baseline offset should be zero when not baseline-aligned");
   size += aGridItem.mBaselineOffset[aAxis];
+  size += extraMargin;
   return std::max(size, 0);
 }
 
 struct CachedIntrinsicSizes {
   Maybe<nscoord> mMinSize;
   Maybe<nscoord> mMinContentContribution;
   Maybe<nscoord> mMaxContentContribution;
 
@@ -3639,16 +4770,30 @@ TrackSize::StateBits nsGridContainerFram
   MOZ_ASSERT(!aRange.IsAuto(), "must have a definite range");
   TrackSize::StateBits state = TrackSize::StateBits(0);
   for (auto i : aRange.Range()) {
     state |= mSizes[i].mState;
   }
   return state;
 }
 
+static void AddSubgridContribution(TrackSize& aSize,
+                                   nscoord aMarginBorderPadding) {
+  if (aSize.mState & TrackSize::eIntrinsicMinSizing) {
+    aSize.mBase = std::max(aSize.mBase, aMarginBorderPadding);
+    aSize.mLimit = std::max(aSize.mLimit, aSize.mBase);
+  }
+  // XXX maybe eFlexMaxSizing too?
+  // (once we implement https://github.com/w3c/csswg-drafts/issues/2177)
+  if (aSize.mState &
+      (TrackSize::eIntrinsicMaxSizing | TrackSize::eFitContent)) {
+    aSize.mLimit = std::max(aSize.mLimit, aMarginBorderPadding);
+  }
+}
+
 bool nsGridContainerFrame::Tracks::ResolveIntrinsicSizeStep1(
     GridReflowInput& aState, const TrackSizingFunctions& aFunctions,
     nscoord aPercentageBasis, SizingConstraint aConstraint,
     const LineRange& aRange, const GridItemInfo& aGridItem) {
   CachedIntrinsicSizes cache;
   TrackSize& sz = mSizes[aRange.mStart];
   WritingMode wm = aState.mWM;
 
@@ -3768,21 +4913,29 @@ void nsGridContainerFrame::Tracks::Calcu
     trackStartIndex = i;
     maxBaseline = item.mBaseline;
     maxDescent = item.mSize - item.mBaseline;
   }
 }
 
 void nsGridContainerFrame::Tracks::InitializeItemBaselines(
     GridReflowInput& aState, nsTArray<GridItemInfo>& aGridItems) {
+  if (aState.mFrame->IsSubgrid(mAxis)) {
+    // A grid container's subgridded axis doesn't have a baseline.
+    return;
+  }
   nsTArray<ItemBaselineData> firstBaselineItems;
   nsTArray<ItemBaselineData> lastBaselineItems;
   WritingMode wm = aState.mWM;
   ComputedStyle* containerSC = aState.mFrame->Style();
   for (GridItemInfo& gridItem : aGridItems) {
+    if (gridItem.IsSubgrid(mAxis)) {
+      // A subgrid itself is never baseline-aligned.
+      continue;
+    }
     nsIFrame* child = gridItem.mFrame;
     uint32_t baselineTrack = kAutoLine;
     auto state = ItemState(0);
     auto childWM = child->GetWritingMode();
     const bool isOrthogonal = wm.IsOrthogonalTo(childWM);
     const bool isInlineAxis = mAxis == eLogicalAxisInline;  // i.e. columns
     // XXX update the line below to include orthogonal grid/table boxes
     // XXX since they have baselines in both dimensions. And flexbox with
@@ -3863,16 +5016,19 @@ void nsGridContainerFrame::Tracks::Initi
             baselineTrack =
                 (isInlineAxis ? area.mCols.mEnd : area.mRows.mEnd) - 1;
           }
         }
       }
     }
 
     if (state & ItemState::eIsBaselineAligned) {
+      // XXXmats if |child| is a descendant of a subgrid then the metrics
+      // below needs to account for the accumulated MPB somehow...
+
       // XXX available size issue
       LogicalSize avail(childWM, INFINITE_ISIZE_COORD, NS_UNCONSTRAINEDSIZE);
       auto* rc = &aState.mRenderingContext;
       // XXX figure out if we can avoid/merge this reflow with the main reflow.
       // XXX (after bug 1174569 is sorted out)
       //
       // XXX How should we handle percentage padding here? (bug 1330866)
       // XXX (see ::ContentContribution and how it deals with percentages)
@@ -4077,16 +5233,41 @@ void nsGridContainerFrame::Tracks::Resol
   for (auto& gridItem : aGridItems) {
     MOZ_ASSERT(!(gridItem.mState[mAxis] &
                  (ItemState::eApplyAutoMinSize | ItemState::eIsFlexing |
                   ItemState::eClampMarginBoxMinSize)),
                "Why are any of these bits set already?");
     const GridArea& area = gridItem.mArea;
     const LineRange& lineRange = area.*aRange;
     uint32_t span = lineRange.Extent();
+    if (MOZ_UNLIKELY(gridItem.mState[mAxis] & ItemState::eIsSubgrid)) {
+      auto itemWM = gridItem.mFrame->GetWritingMode();
+      auto percentageBasis = aState.PercentageBasisFor(mAxis, gridItem);
+      if (percentageBasis.ISize(itemWM) == NS_UNCONSTRAINEDSIZE) {
+        percentageBasis.ISize(itemWM) = nscoord(0);
+      }
+      if (percentageBasis.BSize(itemWM) == NS_UNCONSTRAINEDSIZE) {
+        percentageBasis.BSize(itemWM) = nscoord(0);
+      }
+      auto* subgrid =
+          SubgridComputeMarginBorderPadding(gridItem, percentageBasis);
+      LogicalMargin mbp = SubgridAccumulatedMarginBorderPadding(
+          gridItem.SubgridFrame(), subgrid, wm, mAxis);
+      if (span == 1) {
+        AddSubgridContribution(mSizes[lineRange.mStart],
+                               mbp.StartEnd(mAxis, wm));
+      } else {
+        AddSubgridContribution(mSizes[lineRange.mStart],
+                               mbp.Start(mAxis, wm));
+        AddSubgridContribution(mSizes[lineRange.mEnd - 1],
+                               mbp.End(mAxis, wm));
+      }
+      continue;
+    }
+
     if (span == 1) {
       // Step 1. Size tracks to fit non-spanning items.
       if (ResolveIntrinsicSizeStep1(aState, aFunctions, aPercentageBasis,
                                     aConstraint, lineRange, gridItem)) {
         gridItem.mState[mAxis] |= ItemState::eIsFlexing;
       }
     } else {
       TrackSize::StateBits state = StateBitsForRange(lineRange);
@@ -4425,22 +5606,79 @@ void nsGridContainerFrame::Tracks::Stret
         continue;
       }
     }
     break;
   }
 }
 
 void nsGridContainerFrame::Tracks::AlignJustifyContent(
-    const nsStylePosition* aStyle, WritingMode aWM, nscoord aContentSize) {
+    const nsStylePosition* aStyle, WritingMode aWM, nscoord aContentSize,
+    bool aIsSubgriddedAxis) {
+  const bool isAlign = mAxis == eLogicalAxisBlock;
+  // Align-/justify-content doesn't apply in a subgridded axis.
+  // Gap properties do apply though so we need to stretch/position the tracks
+  // to center-align the gaps with the parent's gaps.
+  if (MOZ_UNLIKELY(aIsSubgriddedAxis)) {
+    auto& gap = isAlign ? aStyle->mRowGap : aStyle->mColumnGap;
+    if (gap.IsNormal()) {
+      return;
+    }
+    auto len = mSizes.Length();
+    if (len <= 1) {
+      return;
+    }
+    // This stores the gap deltas between the subgrid gap and the gaps in
+    // the used track sizes (as encoded in its tracks' mPosition):
+    nsTArray<nscoord> gapDeltas;
+    const size_t numGaps = len - 1;
+    gapDeltas.SetLength(numGaps);
+    for (size_t i = 0; i < numGaps; ++i) {
+      TrackSize& sz1 = mSizes[i];
+      TrackSize& sz2 = mSizes[i + 1];
+      nscoord currentGap = sz2.mPosition - (sz1.mPosition + sz1.mBase);
+      gapDeltas[i] = mGridGap - currentGap;
+    }
+    // Recompute the tracks' size/position so that they end up with
+    // a subgrid-gap centered on the original track gap.
+    nscoord currentPos = mSizes[0].mPosition;
+    nscoord lastHalfDelta(0);
+    for (size_t i = 0; i < numGaps; ++i) {
+      TrackSize& sz = mSizes[i];
+      nscoord delta = gapDeltas[i];
+      nscoord halfDelta;
+      nscoord roundingError = NSCoordDivRem(delta, 2, &halfDelta);
+      auto newSize = sz.mBase - (halfDelta + roundingError) - lastHalfDelta;
+      lastHalfDelta = halfDelta;
+      if (newSize >= 0) {
+        sz.mBase = newSize;
+        sz.mPosition = currentPos;
+        currentPos += newSize + mGridGap;
+      } else {
+        sz.mBase = nscoord(0);
+        sz.mPosition = currentPos + newSize;
+        currentPos = sz.mPosition + mGridGap;
+      }
+    }
+    auto& lastTrack = mSizes.LastElement();
+    auto newSize = lastTrack.mBase - lastHalfDelta;
+    if (newSize >= 0) {
+      lastTrack.mBase = newSize;
+      lastTrack.mPosition = currentPos;
+    } else {
+      lastTrack.mBase = nscoord(0);
+      lastTrack.mPosition = currentPos + newSize;
+    }
+    return;
+  }
+
   if (mSizes.IsEmpty()) {
     return;
   }
 
-  const bool isAlign = mAxis == eLogicalAxisBlock;
   auto valueAndFallback =
       isAlign ? aStyle->mAlignContent : aStyle->mJustifyContent;
   bool overflowSafe;
   auto alignment =
       ::GetAlignJustifyValue(valueAndFallback, aWM, isAlign, &overflowSafe);
   if (alignment == NS_STYLE_ALIGN_NORMAL) {
     MOZ_ASSERT(valueAndFallback == NS_STYLE_ALIGN_NORMAL,
                "*-content:normal cannot be specified with explicit fallback");
@@ -4448,20 +5686,24 @@ void nsGridContainerFrame::Tracks::Align
     valueAndFallback = alignment;  // we may need a fallback for 'stretch' below
   }
 
   // Compute the free space and count auto-sized tracks.
   size_t numAutoTracks = 0;
   nscoord space;
   if (alignment != NS_STYLE_ALIGN_START) {
     nscoord trackSizeSum = 0;
-    for (const TrackSize& sz : mSizes) {
-      trackSizeSum += sz.mBase;
-      if (sz.mState & TrackSize::eAutoMaxSizing) {
-        ++numAutoTracks;
+    if (aIsSubgriddedAxis) {
+      numAutoTracks = mSizes.Length();
+    } else {
+      for (const TrackSize& sz : mSizes) {
+        trackSizeSum += sz.mBase;
+        if (sz.mState & TrackSize::eAutoMaxSizing) {
+          ++numAutoTracks;
+        }
       }
     }
     space = aContentSize - trackSizeSum - SumOfGridGaps();
     // Use the fallback value instead when applicable.
     if (space < 0 ||
         (alignment == NS_STYLE_ALIGN_SPACE_BETWEEN && mSizes.Length() == 1)) {
       auto fallback = ::GetAlignJustifyFallbackIfAny(valueAndFallback, aWM,
                                                      isAlign, &overflowSafe);
@@ -4618,22 +5860,57 @@ void nsGridContainerFrame::LineRange::To
       *aLength = nscoord(0);
     }
   }
 }
 
 LogicalSize nsGridContainerFrame::GridReflowInput::PercentageBasisFor(
     LogicalAxis aAxis, const GridItemInfo& aGridItem) const {
   auto wm = aGridItem.mFrame->GetWritingMode();
-  if (aAxis == eLogicalAxisInline) {
+  const auto* itemParent = aGridItem.mFrame->GetParent();
+  if (MOZ_UNLIKELY(itemParent != mFrame)) {
+    // The item comes from a descendant subgrid.  Use the subgrid's
+    // used track sizes to resolve the grid area size, if present.
+    MOZ_ASSERT(itemParent->IsGridContainerFrame());
+    auto* subgridFrame = static_cast<const nsGridContainerFrame*>(itemParent);
+    MOZ_ASSERT(subgridFrame->IsSubgrid());
+    if (auto* uts = subgridFrame->GetUsedTrackSizes()) {
+      auto subgridWM = subgridFrame->GetWritingMode();
+      LogicalSize cbSize(subgridWM, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
+      if (!subgridFrame->IsSubgrid(eLogicalAxisInline) &&
+          uts->mCanResolveLineRangeSize[eLogicalAxisInline]) {
+        // NOTE: At this point aGridItem.mArea is in this->mFrame coordinates
+        // and thus may have been transposed.  The range values in a non-
+        // subgridded axis still has its original values in subgridFrame's
+        // coordinates though.
+        auto rangeAxis = subgridWM.IsOrthogonalTo(mWM) ? eLogicalAxisBlock
+                                                       : eLogicalAxisInline;
+        const auto& range = aGridItem.mArea.LineRangeForAxis(rangeAxis);
+        cbSize.ISize(subgridWM) =
+            range.ToLength(uts->mSizes[eLogicalAxisInline]);
+      }
+      if (!subgridFrame->IsSubgrid(eLogicalAxisBlock) &&
+          uts->mCanResolveLineRangeSize[eLogicalAxisBlock]) {
+        auto rangeAxis = subgridWM.IsOrthogonalTo(mWM) ? eLogicalAxisInline
+                                                       : eLogicalAxisBlock;
+        const auto& range = aGridItem.mArea.LineRangeForAxis(rangeAxis);
+        cbSize.BSize(subgridWM) =
+            range.ToLength(uts->mSizes[eLogicalAxisBlock]);
+      }
+      return cbSize.ConvertTo(wm, subgridWM);
+    }
+
+    return LogicalSize(wm, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
+  }
+
+  if (aAxis == eLogicalAxisInline || !mCols.mCanResolveLineRangeSize) {
     return LogicalSize(wm, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
   }
   // Note: for now, we only resolve transferred percentages to row sizing.
   // We may need to adjust these assertions once we implement bug 1300366.
-  MOZ_ASSERT(mCols.mCanResolveLineRangeSize);
   MOZ_ASSERT(!mRows.mCanResolveLineRangeSize);
   nscoord colSize = aGridItem.mArea.mCols.ToLength(mCols.mSizes);
   nscoord rowSize = NS_UNCONSTRAINEDSIZE;
   return !wm.IsOrthogonalTo(mWM) ? LogicalSize(wm, colSize, rowSize)
                                  : LogicalSize(wm, rowSize, colSize);
 }
 
 LogicalRect nsGridContainerFrame::GridReflowInput::ContainingBlockFor(
@@ -5421,16 +6698,29 @@ nscoord nsGridContainerFrame::ReflowRows
     }
     if (!overflowIncompleteList.IsEmpty()) {
       MergeSortedExcessOverflowContainers(overflowIncompleteList);
     }
   }
   return aBSize;
 }
 
+nsGridContainerFrame* nsGridContainerFrame::ParentGridContainerForSubgrid()
+    const {
+  MOZ_ASSERT(IsSubgrid());
+  nsIFrame* p = GetParent();
+  while (p->GetContent() == GetContent()) {
+    p = p->GetParent();
+  }
+  MOZ_ASSERT(p->IsGridContainerFrame());
+  auto* parent = static_cast<nsGridContainerFrame*>(p);
+  MOZ_ASSERT(parent->HasSubgridItems());
+  return parent;
+}
+
 nscoord nsGridContainerFrame::ReflowChildren(GridReflowInput& aState,
                                              const LogicalRect& aContentArea,
                                              ReflowOutput& aDesiredSize,
                                              nsReflowStatus& aStatus) {
   MOZ_ASSERT(aState.mReflowInput);
   MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
 
   nsOverflowAreas ocBounds;
@@ -5510,30 +6800,18 @@ nscoord nsGridContainerFrame::ReflowChil
       GetAbsoluteContainingBlock()->Reflow(
           this, PresContext(), *aState.mReflowInput, aStatus, dummyRect, flags,
           &aDesiredSize.mOverflowAreas);
     }
   }
   return bSize;
 }
 
-void nsGridContainerFrame::Reflow(nsPresContext* aPresContext,
-                                  ReflowOutput& aDesiredSize,
-                                  const ReflowInput& aReflowInput,
-                                  nsReflowStatus& aStatus) {
-  MarkInReflow();
-  DO_GLOBAL_REFLOW_COUNT("nsGridContainerFrame");
-  DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
-  MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
-
-  if (IsFrameTreeTooDeep(aReflowInput, aDesiredSize, aStatus)) {
-    return;
-  }
-
-  // First we gather child frames we should include in our reflow,
+void nsGridContainerFrame::NormalizeChildLists() {
+  // First we gather child frames we should include in our reflow/placement,
   // i.e. overflowed children from our prev-in-flow, and pushed first-in-flow
   // children (that might now fit). It's important to note that these children
   // can be in arbitrary order vis-a-vis the current children in our lists.
   // E.g. grid items in the document order: A, B, C may be placed in the rows
   // 3, 2, 1.  Assume each row goes in a separate grid container fragment,
   // and we reflow the second fragment.  Now if C (in fragment 1) overflows,
   // we can't just prepend it to our mFrames like we usually do because that
   // would violate the document order invariant that other code depends on.
@@ -5544,17 +6822,17 @@ void nsGridContainerFrame::Reflow(nsPres
   //
   // The invariants that we maintain are that each grid container child list
   // is sorted in the normal document order at all times, but that children
   // in different grid container continuations may be in arbitrary order.
 
   auto prevInFlow = static_cast<nsGridContainerFrame*>(GetPrevInFlow());
   // Merge overflow frames from our prev-in-flow into our principal child list.
   if (prevInFlow) {
-    AutoFrameListPtr overflow(aPresContext, prevInFlow->StealOverflowFrames());
+    AutoFrameListPtr overflow(PresContext(), prevInFlow->StealOverflowFrames());
     if (overflow) {
       ReparentFrames(*overflow, prevInFlow, this);
       ::MergeSortedFrameLists(mFrames, *overflow, GetContent());
 
       // Move trailing next-in-flows into our overflow list.
       nsFrameList continuations;
       for (nsIFrame* f = mFrames.FirstChild(); f;) {
         nsIFrame* next = f->GetNextSibling();
@@ -5713,31 +6991,52 @@ void nsGridContainerFrame::Reflow(nsPres
       firstNIF->MergeSortedExcessOverflowContainers(childOCNIFs);
     }
 
     MOZ_ASSERT(
         foundOwnPushedChild || !items.IsEmpty() || mDidPushItemsBitMayLie,
         "NS_STATE_GRID_DID_PUSH_ITEMS lied");
     ::MergeSortedFrameLists(mFrames, items, GetContent());
   }
+}
+
+void nsGridContainerFrame::Reflow(nsPresContext* aPresContext,
+                                  ReflowOutput& aDesiredSize,
+                                  const ReflowInput& aReflowInput,
+                                  nsReflowStatus& aStatus) {
+  MarkInReflow();
+  DO_GLOBAL_REFLOW_COUNT("nsGridContainerFrame");
+  DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
+  MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
+
+  if (IsFrameTreeTooDeep(aReflowInput, aDesiredSize, aStatus)) {
+    return;
+  }
+
+  NormalizeChildLists();
 
 #ifdef DEBUG
   mDidPushItemsBitMayLie = false;
   SanityCheckGridItemsBeforeReflow();
 #endif  // DEBUG
 
   for (auto& perAxisBaseline : mBaseline) {
     for (auto& baseline : perAxisBaseline) {
       baseline = NS_INTRINSIC_ISIZE_UNKNOWN;
     }
   }
 
   const nsStylePosition* stylePos = aReflowInput.mStylePosition;
-  if (!prevInFlow) {
+  auto prevInFlow = static_cast<nsGridContainerFrame*>(GetPrevInFlow());
+  if (MOZ_LIKELY(!prevInFlow)) {
     InitImplicitNamedAreas(stylePos);
+  } else {
+    MOZ_ASSERT((prevInFlow->GetStateBits() & kIsSubgridBits) ==
+                   (GetStateBits() & kIsSubgridBits),
+               "continuations should have same kIsSubgridBits");
   }
   GridReflowInput gridReflowInput(this, aReflowInput);
   if (gridReflowInput.mIter.ItemsAreAlreadyInOrder()) {
     AddStateBits(NS_STATE_GRID_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER);
   } else {
     RemoveStateBits(NS_STATE_GRID_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER);
   }
   if (gridReflowInput.mIter.AtEnd()) {
@@ -5748,33 +7047,49 @@ void nsGridContainerFrame::Reflow(nsPres
   }
   const nscoord computedBSize = aReflowInput.ComputedBSize();
   const nscoord computedISize = aReflowInput.ComputedISize();
   const WritingMode& wm = gridReflowInput.mWM;
   const LogicalSize computedSize(wm, computedISize, computedBSize);
 
   nscoord consumedBSize = 0;
   nscoord bSize = 0;
-  if (!prevInFlow) {
+  if (MOZ_LIKELY(!prevInFlow)) {
     Grid grid;
-    grid.PlaceGridItems(gridReflowInput, aReflowInput.ComputedMinSize(),
-                        computedSize, aReflowInput.ComputedMaxSize());
-
+    if (MOZ_LIKELY(!IsSubgrid())) {
+      RepeatTrackSizingInput repeatSizing(aReflowInput.ComputedMinSize(),
+                                          computedSize,
+                                          aReflowInput.ComputedMaxSize());
+      grid.PlaceGridItems(gridReflowInput, repeatSizing);
+    } else {
+      auto* subgrid = GetProperty(Subgrid::Prop());
+      MOZ_ASSERT(subgrid, "an ancestor forgot to call PlaceGridItems?");
+      gridReflowInput.mGridItems = subgrid->mGridItems;
+      gridReflowInput.mAbsPosItems = subgrid->mAbsPosItems;
+      grid.mGridColEnd = subgrid->mGridColEnd;
+      grid.mGridRowEnd = subgrid->mGridRowEnd;
+    }
     gridReflowInput.CalculateTrackSizes(grid, computedSize,
                                         SizingConstraint::NoConstraint);
     // XXX Technically incorrect: We're ignoring our row sizes, when really
     // we should use them but *they* should be computed as if we had no
     // children. To be fixed in bug 1488878.
     if (!aReflowInput.mStyleDisplay->IsContainSize()) {
-      // Note: we can't use GridLineEdge here since we haven't calculated
-      // the rows' mPosition yet (happens in AlignJustifyContent below).
-      for (const auto& sz : gridReflowInput.mRows.mSizes) {
-        bSize += sz.mBase;
-      }
-      bSize += gridReflowInput.mRows.SumOfGridGaps();
+      const auto& rowSizes = gridReflowInput.mRows.mSizes;
+      if (MOZ_LIKELY(!IsSubgrid(eLogicalAxisBlock))) {
+        // Note: we can't use GridLineEdge here since we haven't calculated
+        // the rows' mPosition yet (happens in AlignJustifyContent below).
+        for (const auto& sz : rowSizes) {
+          bSize += sz.mBase;
+        }
+        bSize += gridReflowInput.mRows.SumOfGridGaps();
+      } else if (computedBSize == NS_AUTOHEIGHT) {
+        bSize = gridReflowInput.mRows.GridLineEdge(rowSizes.Length(),
+                                                   GridLineSide::BeforeGridGap);
+      }
     }
   } else {
     consumedBSize = ConsumedBSize(wm);
     gridReflowInput.InitializeForContinuation(this, consumedBSize);
     // XXX Technically incorrect: We're ignoring our row sizes, when really
     // we should use them but *they* should be computed as if we had no
     // children. To be fixed in bug 1488878.
     if (!aReflowInput.mStyleDisplay->IsContainSize()) {
@@ -5790,26 +7105,38 @@ void nsGridContainerFrame::Reflow(nsPres
     bSize = computedBSize;
   }
   bSize = std::max(bSize - consumedBSize, 0);
   auto& bp = gridReflowInput.mBorderPadding;
   LogicalRect contentArea(wm, bp.IStart(wm), bp.BStart(wm), computedISize,
                           bSize);
 
   if (!prevInFlow) {
-    if (computedBSize == NS_AUTOHEIGHT &&
-        stylePos->mRowGap.IsLengthPercentage() &&
-        stylePos->mRowGap.AsLengthPercentage().HasPercent()) {
-      // Re-resolve the row-gap now that we know our intrinsic block-size.
-      gridReflowInput.mRows.mGridGap =
+    const auto& rowSizes = gridReflowInput.mRows.mSizes;
+    if (!IsRowSubgrid()) {
+      // Apply 'align-content' to the grid.
+      if (computedBSize == NS_AUTOHEIGHT &&
+          stylePos->mRowGap.IsLengthPercentage() &&
+          stylePos->mRowGap.AsLengthPercentage().HasPercent()) {
+        // Re-resolve the row-gap now that we know our intrinsic block-size.
+        gridReflowInput.mRows.mGridGap =
           nsLayoutUtils::ResolveGapToLength(stylePos->mRowGap, bSize);
-    }
-    // Apply 'align/justify-content' to the grid.
-    // CalculateTrackSizes did the columns.
-    gridReflowInput.mRows.AlignJustifyContent(stylePos, wm, bSize);
+      }
+      gridReflowInput.mRows.AlignJustifyContent(stylePos, wm, bSize, false);
+    } else {
+      if (computedBSize == NS_AUTOHEIGHT) {
+        bSize = gridReflowInput.mRows.GridLineEdge(rowSizes.Length(),
+                                                   GridLineSide::BeforeGridGap);
+        contentArea.BSize(wm) = bSize;
+      }
+    }
+    // Save the final row sizes for use by subgrids, if needed.
+    if (HasSubgridItems() || IsSubgrid()) {
+      StoreUsedTrackSizes(eLogicalAxisBlock, rowSizes);
+    }
   }
 
   bSize = ReflowChildren(gridReflowInput, contentArea, aDesiredSize, aStatus);
   bSize = std::max(bSize - consumedBSize, 0);
 
   // Skip our block-end border if we're INCOMPLETE.
   if (!aStatus.IsComplete() && !gridReflowInput.mSkipSides.BEnd() &&
       StyleBorder()->mBoxDecorationBreak != StyleBoxDecorationBreak::Clone) {
@@ -5922,18 +7249,20 @@ void nsGridContainerFrame::Reflow(nsPres
       colTrackStates.AppendElement(
           isRepeat ? (uint32_t)mozilla::dom::GridTrackState::Repeat
                    : (uint32_t)mozilla::dom::GridTrackState::Static);
 
       col++;
     }
     ComputedGridTrackInfo* colInfo = new ComputedGridTrackInfo(
         gridReflowInput.mColFunctions.mExplicitGridOffset,
-        gridReflowInput.mColFunctions.NumExplicitTracks(), 0, col,
-        std::move(colTrackPositions), std::move(colTrackSizes),
+        IsSubgrid(eLogicalAxisInline)
+            ? colTrackSizes.Length()
+            : gridReflowInput.mColFunctions.NumExplicitTracks(),
+        0, col, std::move(colTrackPositions), std::move(colTrackSizes),
         std::move(colTrackStates), std::move(colRemovedRepeatTracks),
         gridReflowInput.mColFunctions.mRepeatAutoStart);
     SetProperty(GridColTrackInfo(), colInfo);
 
     uint32_t rowTrackCount = gridReflowInput.mRows.mSizes.Length();
     nsTArray<nscoord> rowTrackPositions(rowTrackCount);
     nsTArray<nscoord> rowTrackSizes(rowTrackCount);
     nsTArray<uint32_t> rowTrackStates(rowTrackCount);
@@ -5952,17 +7281,19 @@ void nsGridContainerFrame::Reflow(nsPres
 
       row++;
     }
     // Row info has to accomodate fragmentation of the grid, which may happen in
     // later calls to Reflow. For now, presume that no more fragmentation will
     // occur.
     ComputedGridTrackInfo* rowInfo = new ComputedGridTrackInfo(
         gridReflowInput.mRowFunctions.mExplicitGridOffset,
-        gridReflowInput.mRowFunctions.NumExplicitTracks(),
+        IsSubgrid(eLogicalAxisBlock)
+            ? rowTrackSizes.Length()
+            : gridReflowInput.mRowFunctions.NumExplicitTracks(),
         gridReflowInput.mStartRow, row, std::move(rowTrackPositions),
         std::move(rowTrackSizes), std::move(rowTrackStates),
         std::move(rowRemovedRepeatTracks),
         gridReflowInput.mRowFunctions.mRepeatAutoStart);
     SetProperty(GridRowTrackInfo(), rowInfo);
 
     if (prevInFlow) {
       // This frame is fragmenting rows from a previous frame, so patch up
@@ -6113,108 +7444,138 @@ void nsGridContainerFrame::Reflow(nsPres
       DeleteProperty(SharedGridData::Prop());
     }
   }
 
   FinishAndStoreOverflow(&aDesiredSize);
   NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
 }
 
+void nsGridContainerFrame::UpdateSubgridFrameState() {
+  nsFrameState oldBits = GetStateBits() & kIsSubgridBits;
+  nsFrameState newBits = ComputeSelfSubgridBits();
+  if (newBits != oldBits) {
+    RemoveStateBits(kIsSubgridBits);
+    if (!newBits) {
+      DeleteProperty(Subgrid::Prop());
+    } else {
+      AddStateBits(newBits);
+    }
+  }
+}
+
+nsFrameState nsGridContainerFrame::ComputeSelfSubgridBits() const {
+  // 'contain:layout/paint' makes us an "independent formatting context",
+  // which prevents us from being a subgrid in this case (but not always).
+  // https://drafts.csswg.org/css-display-3/#establish-an-independent-formatting-context
+  auto* display = StyleDisplay();
+  if (display->IsContainLayout() || display->IsContainPaint()) {
+    return nsFrameState(0);
+  }
+
+  // skip our scroll frame and such if we have it
+  auto* parent = GetParent();
+  while (parent && parent->GetContent() == GetContent()) {
+    parent = parent->GetParent();
+  }
+  nsFrameState bits = nsFrameState(0);
+  if (parent && parent->IsGridContainerFrame()) {
+    const auto* pos = StylePosition();
+    if (pos->GridTemplateColumns().mIsSubgrid) {
+      bits |= NS_STATE_GRID_IS_COL_SUBGRID;
+    }
+    if (pos->GridTemplateRows().mIsSubgrid) {
+      bits |= NS_STATE_GRID_IS_ROW_SUBGRID;
+    }
+  }
+  return bits;
+}
+
 void nsGridContainerFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
                                 nsIFrame* aPrevInFlow) {
   nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
 
   nsFrameState bits = nsFrameState(0);
   if (MOZ_LIKELY(!aPrevInFlow)) {
-    // skip our scroll frame and such if we have it
-    auto* parent = aParent;
-    while (parent && parent->GetContent() == aContent) {
-      parent = parent->GetParent();
-    }
-    if (parent && parent->IsGridContainerFrame()) {
-      const auto* pos = StylePosition();
-      if (pos->GridTemplateColumns().mIsSubgrid) {
-        bits |= NS_STATE_GRID_IS_COL_SUBGRID;
-      }
-      if (pos->GridTemplateRows().mIsSubgrid) {
-        bits |= NS_STATE_GRID_IS_ROW_SUBGRID;
-      }
-    }
+    bits = ComputeSelfSubgridBits();
   } else {
     bits = aPrevInFlow->GetStateBits() &
-           (NS_STATE_GRID_IS_COL_SUBGRID | NS_STATE_GRID_IS_ROW_SUBGRID |
+           (kIsSubgridBits |
             NS_STATE_GRID_HAS_COL_SUBGRID_ITEM |
             NS_STATE_GRID_HAS_ROW_SUBGRID_ITEM);
   }
   AddStateBits(bits);
 }
 
+void nsGridContainerFrame::DidSetComputedStyle(ComputedStyle* aOldStyle) {
+  nsContainerFrame::DidSetComputedStyle(aOldStyle);
+
+  if (!aOldStyle) {
+    return; // Init() already initialized the bits.
+  }
+  UpdateSubgridFrameState();
+}
+
 nscoord nsGridContainerFrame::IntrinsicISize(gfxContext* aRenderingContext,
                                              IntrinsicISizeType aType) {
   // Calculate the sum of column sizes under intrinsic sizing.
   // http://dev.w3.org/csswg/css-grid/#intrinsic-sizes
+  NormalizeChildLists();
   GridReflowInput state(this, *aRenderingContext);
   InitImplicitNamedAreas(state.mGridStyle);  // XXX optimize
 
-  auto GetDefiniteSizes = [](const StyleSize& aMinCoord,
-                             const StyleSize& aSizeCoord,
-                             const StyleMaxSize& aMaxCoord, nscoord* aMin,
-                             nscoord* aSize, nscoord* aMax) {
-    if (aMinCoord.ConvertsToLength()) {
-      *aMin = aMinCoord.ToLength();
-    }
-    if (aMaxCoord.ConvertsToLength()) {
-      *aMax = std::max(*aMin, aMaxCoord.ToLength());
-    }
-    if (aSizeCoord.ConvertsToLength()) {
-      *aSize = Clamp(aSizeCoord.ToLength(), *aMin, *aMax);
-    }
-  };
   // The min/sz/max sizes are the input to the "repeat-to-fill" algorithm:
   // https://drafts.csswg.org/css-grid/#auto-repeat
   // They're only used for auto-repeat so we skip computing them otherwise.
-  LogicalSize min(state.mWM, 0, 0);
-  LogicalSize sz(state.mWM, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
-  LogicalSize max(state.mWM, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
-  if (state.mColFunctions.mHasRepeatAuto) {
-    GetDefiniteSizes(state.mGridStyle->MinISize(state.mWM),
-                     state.mGridStyle->ISize(state.mWM),
-                     state.mGridStyle->MaxISize(state.mWM),
-                     &min.ISize(state.mWM), &sz.ISize(state.mWM),
-                     &max.ISize(state.mWM));
-  }
-  if (state.mRowFunctions.mHasRepeatAuto &&
+  RepeatTrackSizingInput repeatSizing(state.mWM);
+  if (!IsColSubgrid() && state.mColFunctions.mHasRepeatAuto) {
+    repeatSizing.SetDefiniteSizes(eLogicalAxisInline, state.mWM,
+                                  state.mGridStyle->MinISize(state.mWM),
+                                  state.mGridStyle->ISize(state.mWM),
+                                  state.mGridStyle->MaxISize(state.mWM));
+  }
+  if (!IsRowSubgrid() && state.mRowFunctions.mHasRepeatAuto &&
       !(state.mGridStyle->mGridAutoFlow & NS_STYLE_GRID_AUTO_FLOW_ROW)) {
     // Only 'grid-auto-flow:column' can create new implicit columns, so that's
     // the only case where our block-size can affect the number of columns.
-    GetDefiniteSizes(state.mGridStyle->MinBSize(state.mWM),
-                     state.mGridStyle->BSize(state.mWM),
-                     state.mGridStyle->MaxBSize(state.mWM),
-                     &min.BSize(state.mWM), &sz.BSize(state.mWM),
-                     &max.BSize(state.mWM));
+    repeatSizing.SetDefiniteSizes(eLogicalAxisBlock, state.mWM,
+                                  state.mGridStyle->MinBSize(state.mWM),
+                                  state.mGridStyle->BSize(state.mWM),
+                                  state.mGridStyle->MaxBSize(state.mWM));
   }
 
   Grid grid;
-  grid.PlaceGridItems(state, min, sz, max);  // XXX optimize
+  if (MOZ_LIKELY(!IsSubgrid())) {
+    grid.PlaceGridItems(state, repeatSizing);  // XXX optimize
+  } else {
+    auto* subgrid = GetProperty(Subgrid::Prop());
+    state.mGridItems = subgrid->mGridItems;
+    state.mAbsPosItems = subgrid->mAbsPosItems;
+    grid.mGridColEnd = subgrid->mGridColEnd;
+    grid.mGridRowEnd = subgrid->mGridRowEnd;
+  }
   if (grid.mGridColEnd == 0) {
-    return 0;
-  }
-  state.mCols.Initialize(state.mColFunctions, state.mGridStyle->mColumnGap,
-                         grid.mGridColEnd, NS_UNCONSTRAINEDSIZE);
+    return nscoord(0);
+  }
+
   auto constraint = aType == nsLayoutUtils::MIN_ISIZE
                         ? SizingConstraint::MinContent
                         : SizingConstraint::MaxContent;
-  state.mCols.CalculateSizes(state, state.mGridItems, state.mColFunctions,
-                             NS_UNCONSTRAINEDSIZE, &GridArea::mCols,
-                             constraint);
-  nscoord length = 0;
-  for (const TrackSize& sz : state.mCols.mSizes) {
-    length += sz.mBase;
-  }
-  return length + state.mCols.SumOfGridGaps();
+  state.CalculateTrackSizesForAxis(eLogicalAxisInline, grid,
+                                   NS_UNCONSTRAINEDSIZE, constraint);
+
+  if (MOZ_LIKELY(!IsSubgrid())) {
+    nscoord length = 0;
+    for (const TrackSize& sz : state.mCols.mSizes) {
+      length += sz.mBase;
+    }
+    return length + state.mCols.SumOfGridGaps();
+  }
+  const auto& last = state.mCols.mSizes.LastElement();
+  return last.mPosition + last.mBase;
 }
 
 nscoord nsGridContainerFrame::GetMinISize(gfxContext* aRC) {
   DISPLAY_MIN_INLINE_SIZE(this, mCachedMinISize);
   if (mCachedMinISize == NS_INTRINSIC_ISIZE_UNKNOWN) {
     mCachedMinISize = StyleDisplay()->IsContainSize()
                           ? 0
                           : IntrinsicISize(aRC, nsLayoutUtils::MIN_ISIZE);
@@ -6610,16 +7971,37 @@ nsGridContainerFrame::FindLastItemInGrid
       maxMinor = minor;
       result.mItem = &item;
       result.mIsInEdgeTrack = major == lastMajorTrack;
     }
   }
   return result;
 }
 
+nsGridContainerFrame::UsedTrackSizes* nsGridContainerFrame::GetUsedTrackSizes()
+    const {
+  return GetProperty(UsedTrackSizes::Prop());
+}
+
+void nsGridContainerFrame::StoreUsedTrackSizes(
+    LogicalAxis aAxis, const nsTArray<TrackSize>& aSizes) {
+  auto* uts = GetUsedTrackSizes();
+  if (!uts) {
+    uts = new UsedTrackSizes();
+    SetProperty(UsedTrackSizes::Prop(), uts);
+  }
+  uts->mSizes[aAxis] = aSizes;
+  uts->mCanResolveLineRangeSize[aAxis] = true;
+  // XXX is resetting these bits necessary?
+  for (auto& sz : uts->mSizes[aAxis]) {
+    sz.mState &= ~(TrackSize::eFrozen | TrackSize::eSkipGrowUnlimited |
+                   TrackSize::eInfinitelyGrowable);
+  }
+}
+
 #ifdef DEBUG
 void nsGridContainerFrame::SetInitialChildList(ChildListID aListID,
                                                nsFrameList& aChildList) {
 #  ifdef DEBUG
   ChildListIDs supportedLists = {kAbsoluteList, kFixedList, kPrincipalList};
   // We don't handle the kBackdropList frames in any way, but it only contains
   // a placeholder for ::backdrop which is OK to not reflow (for now anyway).
   supportedLists += kBackdropList;
--- a/layout/generic/nsGridContainerFrame.h
+++ b/layout/generic/nsGridContainerFrame.h
@@ -100,16 +100,17 @@ class nsGridContainerFrame final : publi
       mozilla::EnumeratedArray<LogicalAxis, LogicalAxis(2), T>;
 
   // nsIFrame overrides
   void Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize,
               const ReflowInput& aReflowInput,
               nsReflowStatus& aStatus) override;
   void Init(nsIContent* aContent, nsContainerFrame* aParent,
             nsIFrame* aPrevInFlow) override;
+  void DidSetComputedStyle(ComputedStyle* aOldStyle) override;
   nscoord GetMinISize(gfxContext* aRenderingContext) override;
   nscoord GetPrefISize(gfxContext* aRenderingContext) override;
   void MarkIntrinsicISizesDirty() override;
   bool IsFrameOfType(uint32_t aFlags) const override {
     return nsContainerFrame::IsFrameOfType(
         aFlags & ~nsIFrame::eCanContainOverflowContainers);
   }
 
@@ -146,19 +147,18 @@ class nsGridContainerFrame final : publi
 #endif
 
   // nsContainerFrame overrides
   bool DrainSelfOverflowList() override;
   void AppendFrames(ChildListID aListID, nsFrameList& aFrameList) override;
   void InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame,
                     nsFrameList& aFrameList) override;
   void RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame) override;
-  uint16_t CSSAlignmentForAbsPosChild(
-      const ReflowInput& aChildRI,
-      mozilla::LogicalAxis aLogicalAxis) const override;
+  uint16_t CSSAlignmentForAbsPosChild(const ReflowInput& aChildRI,
+                                      LogicalAxis aLogicalAxis) const override;
 
 #ifdef DEBUG
   void SetInitialChildList(ChildListID aListID,
                            nsFrameList& aChildList) override;
 #endif
 
   /**
    * Return the containing block for aChild which MUST be an abs.pos. child
@@ -213,31 +213,31 @@ class nsGridContainerFrame final : publi
   typedef nsTArray<mozilla::css::GridNamedArea> ExplicitNamedAreas;
   NS_DECLARE_FRAME_PROPERTY_DELETABLE(ExplicitNamedAreasProperty,
                                       ExplicitNamedAreas)
   ExplicitNamedAreas* GetExplicitNamedAreas() const {
     return GetProperty(ExplicitNamedAreasProperty());
   }
 
   /** Return true if this frame is subgridded in its aAxis. */
-  bool IsSubgrid(mozilla::LogicalAxis aAxis) const {
+  bool IsSubgrid(LogicalAxis aAxis) const {
     return HasAnyStateBits(aAxis == mozilla::eLogicalAxisBlock
                                ? NS_STATE_GRID_IS_ROW_SUBGRID
                                : NS_STATE_GRID_IS_COL_SUBGRID);
   }
   bool IsColSubgrid() const { return IsSubgrid(mozilla::eLogicalAxisInline); }
   bool IsRowSubgrid() const { return IsSubgrid(mozilla::eLogicalAxisBlock); }
   /** Return true if this frame is subgridded in any axis. */
   bool IsSubgrid() const {
     return HasAnyStateBits(NS_STATE_GRID_IS_ROW_SUBGRID |
                            NS_STATE_GRID_IS_COL_SUBGRID);
   }
 
   /** Return true if this frame has an item that is subgridded in our aAxis. */
-  bool HasSubgridItems(mozilla::LogicalAxis aAxis) const {
+  bool HasSubgridItems(LogicalAxis aAxis) const {
     return HasAnyStateBits(aAxis == mozilla::eLogicalAxisBlock
                                ? NS_STATE_GRID_HAS_ROW_SUBGRID_ITEM
                                : NS_STATE_GRID_HAS_COL_SUBGRID_ITEM);
   }
   /** Return true if this frame has any subgrid items. */
   bool HasSubgridItems() const {
     return HasAnyStateBits(NS_STATE_GRID_HAS_ROW_SUBGRID_ITEM |
                            NS_STATE_GRID_HAS_COL_SUBGRID_ITEM);
@@ -252,26 +252,31 @@ class nsGridContainerFrame final : publi
   /**
    * Return a container grid frame, and ensure it has computed grid info
    * @return nullptr if aFrame has no grid container, or frame was destroyed
    * @note this might destroy layout/style data since it may flush layout
    */
   MOZ_CAN_RUN_SCRIPT_BOUNDARY
   static nsGridContainerFrame* GetGridFrameWithComputedInfo(nsIFrame* aFrame);
 
+  struct Subgrid;
+  struct UsedTrackSizes;
   struct TrackSize;
   struct GridItemInfo;
   struct GridReflowInput;
   struct FindItemInGridOrderResult {
     // The first(last) item in (reverse) grid order.
     const GridItemInfo* mItem;
     // Does the above item span the first(last) track?
     bool mIsInEdgeTrack;
   };
 
+  /** Return our parent grid container; |this| MUST be a subgrid. */
+  nsGridContainerFrame* ParentGridContainerForSubgrid() const;
+
  protected:
   static const uint32_t kAutoLine;
   // The maximum line number, in the zero-based translated grid.
   static const uint32_t kTranslatedMaxLine;
   typedef mozilla::LogicalPoint LogicalPoint;
   typedef mozilla::LogicalRect LogicalRect;
   typedef mozilla::LogicalSize LogicalSize;
   typedef mozilla::CSSOrderAwareFrameIterator CSSOrderAwareFrameIterator;
@@ -281,16 +286,17 @@ class nsGridContainerFrame final : publi
   typedef mozilla::css::GridNamedArea GridNamedArea;
   typedef mozilla::layout::AutoFrameListPtr AutoFrameListPtr;
   typedef nsLayoutUtils::IntrinsicISizeType IntrinsicISizeType;
   struct Grid;
   struct GridArea;
   class LineNameMap;
   struct LineRange;
   struct SharedGridData;
+  struct SubgridFallbackTrackSizingFunctions;
   struct TrackSizingFunctions;
   struct Tracks;
   struct TranslatedLineRange;
   friend nsContainerFrame* NS_NewGridContainerFrame(
       mozilla::PresShell* aPresShell, ComputedStyle* aStyle);
   explicit nsGridContainerFrame(ComputedStyle* aStyle,
                                 nsPresContext* aPresContext)
       : nsContainerFrame(aStyle, aPresContext, kClassID),
@@ -308,16 +314,18 @@ class nsGridContainerFrame final : publi
    * The implicit area names that come from x-start .. x-end lines in
    * grid-template-columns / grid-template-rows are stored in this frame
    * property when needed, as a ImplicitNamedAreas* value.
    */
   void InitImplicitNamedAreas(const nsStylePosition* aStyle);
   void AddImplicitNamedAreas(
       const nsTArray<nsTArray<nsString>>& aLineNameLists);
 
+  void NormalizeChildLists();
+
   /**
    * Reflow and place our children.
    * @return the consumed size of all of this grid container's continuations
    *         so far including this frame
    */
   nscoord ReflowChildren(GridReflowInput& aState,
                          const LogicalRect& aContentArea,
                          ReflowOutput& aDesiredSize, nsReflowStatus& aStatus);
@@ -371,18 +379,17 @@ class nsGridContainerFrame final : publi
                           const nsSize& aCBPhysicalSize,
                           nscoord aCBBorderPaddingStart,
                           nscoord aCBBorderPaddingStartEnd, nscoord aCBSize);
 
   /**
    * Synthesize a Grid container baseline for aGroup.
    */
   nscoord SynthesizeBaseline(const FindItemInGridOrderResult& aItem,
-                             mozilla::LogicalAxis aAxis,
-                             BaselineSharingGroup aGroup,
+                             LogicalAxis aAxis, BaselineSharingGroup aGroup,
                              const nsSize& aCBPhysicalSize, nscoord aCBSize,
                              WritingMode aCBWM);
   /**
    * Find the first item in Grid Order in this fragment.
    * https://drafts.csswg.org/css-grid/#grid-order
    * @param aFragmentStartTrack is the first track in this fragment in the same
    * axis as aMajor.  Pass zero if that's not the axis we're fragmenting in.
    */
@@ -403,16 +410,29 @@ class nsGridContainerFrame final : publi
       const nsTArray<GridItemInfo>& aGridItems, LineRange GridArea::*aMajor,
       LineRange GridArea::*aMinor, uint32_t aFragmentStartTrack,
       uint32_t aFirstExcludedTrack);
 
 #ifdef DEBUG
   void SanityCheckGridItemsBeforeReflow() const;
 #endif  // DEBUG
 
+  /**
+   * Update our NS_STATE_GRID_IS_COL/ROW_SUBGRID bits and related subgrid state
+   * on our entire continuation chain based on the current style.
+   * This is needed because grid-template-columns/rows style changes only
+   * trigger a reflow so we need to update this dynamically.
+   */
+  void UpdateSubgridFrameState();
+
+  /**
+   * Return the NS_STATE_GRID_IS_COL/ROW_SUBGRID bits we ought to have.
+   */
+  nsFrameState ComputeSelfSubgridBits() const;
+
  private:
   // Helpers for ReflowChildren
   struct Fragmentainer {
     /**
      * The distance from the first grid container fragment's block-axis content
      * edge to the fragmentainer end.
      */
     nscoord mToFragmentainerEnd;
@@ -458,16 +478,23 @@ class nsGridContainerFrame final : publi
   void ReflowInFlowChild(nsIFrame* aChild, const GridItemInfo* aGridItemInfo,
                          nsSize aContainerSize,
                          const mozilla::Maybe<nscoord>& aStretchBSize,
                          const Fragmentainer* aFragmentainer,
                          const GridReflowInput& aState,
                          const LogicalRect& aContentArea,
                          ReflowOutput& aDesiredSize, nsReflowStatus& aStatus);
 
+  // Return the stored UsedTrackSizes, if any.
+  UsedTrackSizes* GetUsedTrackSizes() const;
+
+  // Store the given TrackSizes in aAxis on a UsedTrackSizes frame property.
+  void StoreUsedTrackSizes(LogicalAxis aAxis,
+                           const nsTArray<TrackSize>& aSizes);
+
   /**
    * Cached values to optimize GetMinISize/GetPrefISize.
    */
   nscoord mCachedMinISize;
   nscoord mCachedPrefISize;
 
   // Our baselines, one per BaselineSharingGroup per axis.
   PerLogicalAxis<PerBaseline<nscoord>> mBaseline;
--- a/layout/generic/test/test_dynamic_reflow_root_disallowal.html
+++ b/layout/generic/test/test_dynamic_reflow_root_disallowal.html
@@ -406,16 +406,62 @@ https://bugzilla.mozilla.org/show_bug.cg
     let tweakFunc = function() {
       cand.style.cssText += "; " + subtest.candStyle;
     };
     tweakAndCompareSnapshots(tweakFunc, subtest.desc);
 
     gridContainer.remove(); // clean up
   }
 
+  let gridSubgridSubtests = [
+    { desc: "subgrid",
+      candStyle: "grid: subgrid / subgrid",
+    },
+    { desc: "subgrid-rows",
+      candStyle: "grid: subgrid / 20px",
+    },
+    { desc: "subgrid-columns",
+      candStyle: "grid: 20px / subgrid",
+    },
+  ];
+
+  // Test that a subgrid is not a reflow root.
+  function runGridSubgridSubtest(subtest) {
+    // We create a 4x4 grid container a with one grid item:
+    //  - a reflow root display:grid that we'll style as a subgrid from
+    //    the list above. We place an item inside it that we'll tweak
+    //    the size of, which should affect the outer grid track sizes.
+    let gridContainer = createStyledDiv("display: grid; \
+                                         width: 100px; \
+                                         height: 100px; \
+                                         grid: 1fr auto / 1fr auto; \
+                                         border: 2px solid teal");
+    // The reflow root candidate
+    let cand = createStyledDiv(gReflowRootCandidateStyles +
+                               "display: grid;" +
+                               "grid-area: 2/2;" +
+                               "background: blue;" +
+                               "min-width: 10px; min-height: 10px;" +
+                               subtest.candStyle);
+
+    // Something whose size we can adjust, inside the subgrid:
+    let inner = createStyledDiv("height:20px; width:20px;");
+
+    cand.appendChild(inner);
+    gridContainer.appendChild(cand);
+    gFBody.appendChild(gridContainer);
+
+    let tweakFunc = function() {
+      inner.style.width = inner.style.height = "40px";
+    };
+    tweakAndCompareSnapshots(tweakFunc, subtest.desc);
+
+    gridContainer.remove(); // clean up
+  }
+
   let tableSubtests = [
     { desc: "table",
       /* Testing the default "display:table" styling that runTableTest uses: */
       candStyle: "",
     },
     { desc: "inline-table",
       candStyle: "display:inline-table;",
     },
@@ -706,16 +752,21 @@ https://bugzilla.mozilla.org/show_bug.cg
       runIntrinsicSizeSubtest(subtest);
     }
     for (let subtest of flexItemSubtests) {
       runFlexItemSubtest(subtest);
     }
     for (let subtest of gridContainerSubtests) {
       runGridContainerSubtest(subtest);
     }
+    if (SpecialPowers.getBoolPref("layout.css.grid-template-subgrid-value.enabled")) {
+      for (let subtest of gridSubgridSubtests) {
+        runGridSubgridSubtest(subtest);
+      }
+    }
     for (let subtest of gridItemSubtests) {
       runGridItemSubtest(subtest);
     }
     for (let subtest of tableSubtests) {
       runTableSubtest(subtest);
     }
     for (let subtest of inlineSubtests) {
       runInlineSubtest(subtest);
--- a/layout/printing/nsPrintJob.cpp
+++ b/layout/printing/nsPrintJob.cpp
@@ -493,16 +493,47 @@ static nsresult EnsureSettingsHasPrinter
   rv = printSettingsService->GetDefaultPrinterName(printerName);
   if (NS_SUCCEEDED(rv) && !printerName.IsEmpty()) {
     rv = aPrintSettings->SetPrinterName(printerName);
   }
   return rv;
 #endif
 }
 
+static bool DocHasPrintCallbackCanvas(Document* aDoc, void* aData) {
+  if (!aDoc) {
+    return true;
+  }
+  Element* root = aDoc->GetRootElement();
+  if (!root) {
+    return true;
+  }
+  RefPtr<nsContentList> canvases =
+      NS_GetContentList(root, kNameSpaceID_XHTML, NS_LITERAL_STRING("canvas"));
+  uint32_t canvasCount = canvases->Length(true);
+  for (uint32_t i = 0; i < canvasCount; ++i) {
+    HTMLCanvasElement* canvas =
+        HTMLCanvasElement::FromNodeOrNull(canvases->Item(i, false));
+    if (canvas && canvas->GetMozPrintCallback()) {
+      // This subdocument has a print callback. Set result and return false to
+      // stop iteration.
+      *static_cast<bool*>(aData) = true;
+      return false;
+    }
+  }
+  return true;
+}
+
+static bool AnySubdocHasPrintCallbackCanvas(Document* aDoc) {
+  bool result = false;
+  aDoc->EnumerateSubDocuments(&DocHasPrintCallbackCanvas,
+                              static_cast<void*>(&result));
+  return result;
+}
+
 //-------------------------------------------------------
 
 NS_IMPL_ISUPPORTS(nsPrintJob, nsIWebProgressListener, nsISupportsWeakReference,
                   nsIObserver)
 
 //-------------------------------------------------------
 nsPrintJob::~nsPrintJob() {
   Destroy();  // for insurance
@@ -545,16 +576,38 @@ nsresult nsPrintJob::Initialize(nsIDocum
   mScreenDPI = aScreenDPI;
 
   // XXX We should not be storing this.  The original document that the user
   // selected to print can be mutated while print preview is open.  Anything
   // we need to know about the original document should be checked and stored
   // here instead.
   mOriginalDoc = aOriginalDoc;
 
+  // Anything state that we need from aOriginalDoc must be fetched and stored
+  // here, since the document that the user selected to print may mutate
+  // across consecutive PrintPreview() calls.
+
+  Element* root = aOriginalDoc->GetRootElement();
+  mDisallowSelectionPrint =
+      root &&
+      root->HasAttr(kNameSpaceID_None, nsGkAtoms::mozdisallowselectionprint);
+
+  nsCOMPtr<nsIDocShellTreeOwner> owner;
+  aDocShell->GetTreeOwner(getter_AddRefs(owner));
+  nsCOMPtr<nsIWebBrowserChrome> browserChrome = do_GetInterface(owner);
+  if (browserChrome) {
+    browserChrome->IsWindowModal(&mIsForModalWindow);
+  }
+
+  bool hasMozPrintCallback = false;
+  DocHasPrintCallbackCanvas(aOriginalDoc,
+                            static_cast<void*>(&hasMozPrintCallback));
+  mHasMozPrintCallback =
+      hasMozPrintCallback || AnySubdocHasPrintCallbackCanvas(aOriginalDoc);
+
   return NS_OK;
 }
 
 //-------------------------------------------------------
 bool nsPrintJob::CheckBeforeDestroy() {
   if (mPrt && mPrt->mPreparingForPrint) {
     mPrt->mDocWasToBeDestroyed = true;
     return true;
@@ -1028,24 +1081,25 @@ nsresult nsPrintJob::DoCommonPrint(bool 
 
   // We will enable scripting later after printing has finished.
   scriptSuppressor.Disconnect();
 
   return NS_OK;
 }
 
 //---------------------------------------------------------------------------------
-nsresult nsPrintJob::Print(nsIPrintSettings* aPrintSettings,
+nsresult nsPrintJob::Print(Document* aSourceDoc,
+                           nsIPrintSettings* aPrintSettings,
                            nsIWebProgressListener* aWebProgressListener) {
   // If we have a print preview document, use that instead of the original
   // mDocument. That way animated images etc. get printed using the same state
   // as in print preview.
   Document* doc = mPrtPreview && mPrtPreview->mPrintObject
-                      ? mPrtPreview->mPrintObject->mDocument
-                      : mOriginalDoc;
+                      ? mPrtPreview->mPrintObject->mDocument.get()
+                      : aSourceDoc;
 
   return CommonPrint(false, aPrintSettings, aWebProgressListener, doc);
 }
 
 nsresult nsPrintJob::PrintPreview(
     Document* aSourceDoc, nsIPrintSettings* aPrintSettings,
     nsIWebProgressListener* aWebProgressListener) {
   // Get the DocShell and see if it is busy
@@ -1209,33 +1263,25 @@ void nsPrintJob::ShowPrintProgress(bool 
   }
 
   // Now open the service to get the progress dialog
   // If we don't get a service, that's ok, then just don't show progress
   if (showProgresssDialog) {
     nsCOMPtr<nsIPrintingPromptService> printPromptService(
         do_GetService(kPrintingPromptService));
     if (printPromptService) {
-      nsPIDOMWindowOuter* domWin = mOriginalDoc->GetWindow();
-      if (!domWin) return;
-
-      nsCOMPtr<nsIDocShell> docShell = domWin->GetDocShell();
-      if (!docShell) return;
-      nsCOMPtr<nsIDocShellTreeOwner> owner;
-      docShell->GetTreeOwner(getter_AddRefs(owner));
-      nsCOMPtr<nsIWebBrowserChrome> browserChrome = do_GetInterface(owner);
-      if (!browserChrome) return;
-      bool isModal = true;
-      browserChrome->IsWindowModal(&isModal);
-      if (isModal) {
+      if (mIsForModalWindow) {
         // Showing a print progress dialog when printing a modal window
         // isn't supported. See bug 301560.
         return;
       }
 
+      nsPIDOMWindowOuter* domWin = mOriginalDoc->GetWindow();
+      if (!domWin) return;
+
       nsCOMPtr<nsIWebProgressListener> printProgressListener;
 
       nsCOMPtr<nsIWebBrowserPrint> wbp(do_QueryInterface(mDocViewerPrint));
       nsresult rv = printPromptService->ShowPrintProgressDialog(
           domWin, wbp, printData->mPrintSettings, this, aIsForPrinting,
           getter_AddRefs(printProgressListener),
           getter_AddRefs(printData->mPrintProgressParams), &aDoNotify);
       if (NS_SUCCEEDED(rv)) {
@@ -2574,63 +2620,16 @@ void nsPrintJob::EllipseLongString(nsASt
       aStr = newStr;
     } else {
       aStr.SetLength(aLen - 3);
       aStr.AppendLiteral("...");
     }
   }
 }
 
-static bool DocHasPrintCallbackCanvas(Document* aDoc, void* aData) {
-  if (!aDoc) {
-    return true;
-  }
-  Element* root = aDoc->GetRootElement();
-  if (!root) {
-    return true;
-  }
-  RefPtr<nsContentList> canvases =
-      NS_GetContentList(root, kNameSpaceID_XHTML, NS_LITERAL_STRING("canvas"));
-  uint32_t canvasCount = canvases->Length(true);
-  for (uint32_t i = 0; i < canvasCount; ++i) {
-    HTMLCanvasElement* canvas =
-        HTMLCanvasElement::FromNodeOrNull(canvases->Item(i, false));
-    if (canvas && canvas->GetMozPrintCallback()) {
-      // This subdocument has a print callback. Set result and return false to
-      // stop iteration.
-      *static_cast<bool*>(aData) = true;
-      return false;
-    }
-  }
-  return true;
-}
-
-static bool DocHasPrintCallbackCanvas(Document* aDoc) {
-  bool result = false;
-  aDoc->EnumerateSubDocuments(&DocHasPrintCallbackCanvas,
-                              static_cast<void*>(&result));
-  return result;
-}
-
-/**
- * Checks to see if the document this print engine is associated with has any
- * canvases that have a mozPrintCallback.
- * https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement#Properties
- */
-bool nsPrintJob::HasPrintCallbackCanvas() {
-  if (!mOriginalDoc) {
-    return false;
-  }
-  // First check this mOriginalDoc.
-  bool result = false;
-  DocHasPrintCallbackCanvas(mOriginalDoc, static_cast<void*>(&result));
-  // Also check the sub documents.
-  return result || DocHasPrintCallbackCanvas(mOriginalDoc);
-}
-
 //-------------------------------------------------------
 bool nsPrintJob::PrePrintPage() {
   NS_ASSERTION(mPageSeqFrame.IsAlive(), "mPageSeqFrame is not alive!");
   NS_ASSERTION(mPrt, "mPrt is null!");
 
   // Although these should NEVER be nullptr
   // This is added insurance, to make sure we don't crash in optimized builds
   if (!mPrt || !mPageSeqFrame.IsAlive()) {
--- a/layout/printing/nsPrintJob.h
+++ b/layout/printing/nsPrintJob.h
@@ -88,17 +88,18 @@ class nsPrintJob final : public nsIObser
 
   // Our nsIWebBrowserPrint implementation (nsDocumentViewer) defers to the
   // following methods.
 
   /**
    * May be called immediately after initialization, or after one or more
    * PrintPreview calls.
    */
-  nsresult Print(nsIPrintSettings* aPrintSettings,
+  nsresult Print(mozilla::dom::Document* aSourceDoc,
+                 nsIPrintSettings* aPrintSettings,
                  nsIWebProgressListener* aWebProgressListener);
 
   /**
    * Generates a new print preview document and replaces our docViewer's
    * document with it.  (Note that this breaks the normal invariant that a
    * Document and its nsDocumentViewer have an unchanging 1:1 relationship.)
    *
    * This may be called multiple times on the same instance in order to
@@ -153,18 +154,24 @@ class nsPrintJob final : public nsIObser
 
   /**
    * Filters out certain user events while Print Preview is open to prevent
    * the user from interacting with the Print Preview document and breaking
    * printing invariants.
    */
   void SuppressPrintPreviewUserEvents();
 
-  // nsIDocumentViewerPrint Printing Methods
-  bool HasPrintCallbackCanvas();
+  // nsIDocumentViewerPrint Printing Methods:
+
+  /**
+   * Checks to see if the document this print engine is associated with has any
+   * canvases that have a mozPrintCallback.
+   * https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement#Properties
+   */
+  bool HasPrintCallbackCanvas() { return mHasMozPrintCallback; }
   bool PrePrintPage();
   bool PrintPage(nsPrintObject* aPOect, bool& aInRange);
   bool DonePrintingPages(nsPrintObject* aPO, nsresult aResult);
 
   //---------------------------------------------------------------------
   void BuildDocTree(nsIDocShell* aParentNode,
                     nsTArray<nsPrintObject*>* aDocList,
                     const mozilla::UniquePtr<nsPrintObject>& aPO);
@@ -221,20 +228,16 @@ class nsPrintJob final : public nsIObser
 
   // These calls also update the DocViewer
   void SetIsPrinting(bool aIsPrinting);
   bool GetIsPrinting() { return mIsDoingPrinting; }
   void SetIsPrintPreview(bool aIsPrintPreview);
   bool GetIsPrintPreview() { return mIsDoingPrintPreview; }
   bool GetIsCreatingPrintPreview() { return mIsCreatingPrintPreview; }
 
-  void SetDisallowSelectionPrint(bool aDisallowSelectionPrint) {
-    mDisallowSelectionPrint = aDisallowSelectionPrint;
-  }
-
  private:
   nsPrintJob& operator=(const nsPrintJob& aOther) = delete;
 
   ~nsPrintJob();
 
   nsresult CommonPrint(bool aIsPrintPreview, nsIPrintSettings* aPrintSettings,
                        nsIWebProgressListener* aWebProgressListener,
                        mozilla::dom::Document* aSourceDoc);
@@ -303,11 +306,13 @@ class nsPrintJob final : public nsIObser
 
   bool mIsCreatingPrintPreview = false;
   bool mIsDoingPrinting = false;
   bool mIsDoingPrintPreview = false;
   bool mProgressDialogIsShown = false;
   bool mDidLoadDataForPrinting = false;
   bool mIsDestroying = false;
   bool mDisallowSelectionPrint = false;
+  bool mIsForModalWindow = false;
+  bool mHasMozPrintCallback = false;
 };
 
 #endif  // nsPrintJob_h
--- a/modules/libpref/init/StaticPrefList.h
+++ b/modules/libpref/init/StaticPrefList.h
@@ -1186,21 +1186,27 @@ VARCACHE_PREF(
 // pages.
 VARCACHE_PREF(
   "layout.css.xul-tree-pseudos.content.enabled",
    layout_css_xul_tree_pseudos_content_enabled,
   bool, false
 )
 
 // Is support for CSS "grid-template-{columns,rows}: subgrid X" enabled?
+#ifdef NIGHTLY_BUILD
+# define PREF_VALUE  true
+#else
+# define PREF_VALUE  false
+#endif
 VARCACHE_PREF(
   "layout.css.grid-template-subgrid-value.enabled",
    layout_css_grid_template_subgrid_value_enabled,
-  bool, false
+  bool, PREF_VALUE
 )
+#undef PREF_VALUE
 
 // Pref to control whether line-height: -moz-block-height is exposed to content.
 VARCACHE_PREF(
   "layout.css.line-height-moz-block-height.content.enabled",
    layout_css_line_height_moz_block_height_content_enabled,
   bool, false
 )
 
--- a/remote/doc/index.rst
+++ b/remote/doc/index.rst
@@ -48,16 +48,14 @@ Bugs are tracked under the `Remote Proto
 
 
 Communication
 =============
 
 The mailing list for Firefox remote debugging discussion is
 `dev-remote@lists.mozilla.org`_ (`subscribe`_, `archive`_).
 
-If you prefer real-time chat, there is often someone in the *#remote*
-IRC channel on irc.mozilla.org.  Don’t ask if you may ask a
-question just go ahead and ask, and please wait for an answer as
-we might not be in your timezone.
+If you prefer real-time chat, there is often someone in the *#remote* channel
+on the <a href=https://devtools-html.slack.com/>DevTools Slack</a> instance.
 
 .. _dev-remote@lists.mozilla.org: mailto:dev-remote@lists.mozilla.org
 .. _subscribe: https://lists.mozilla.org/listinfo/dev-remote
 .. _archive: https://lists.mozilla.org/pipermail/dev-remote/
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/contain-subgrid-001.html
@@ -0,0 +1,45 @@
+<!doctype html>
+<html lang=en>
+  <meta charset=utf-8>
+  <title>CSS-contain test: layout/paint containment on subgrid</title>
+  <link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
+  <meta name=assert content="layout/paint containment inhibits subgrid">
+  <link rel="match" href="reference/contain-subgrid-001.html">
+  <link rel=help href="https://drafts.csswg.org/css-contain/#containment-layout">
+  <link rel=help href="https://drafts.csswg.org/css-contain/#containment-paint">
+  <link rel=help href="https://drafts.csswg.org/css-grid-2/">
+
+<style>
+.grid {
+  display: grid;
+  width: 40px;
+  height: 40px;
+  grid: [a] repeat(2,15px) [b] / [a] repeat(2,15px) [b];
+  gap: 10px;
+}
+
+.subgrid {
+  display: grid;
+  grid: subgrid;
+  background: lightgrey;
+  grid-area:1/1/3/3;
+}
+
+.layout { contain: layout; }
+.paint { contain: paint; }
+
+.inner {
+  background: blue;
+  grid-area:a/a/b/b;
+}
+.p {
+  place-self: start;
+  width:100%;
+  height:100%;
+}
+</style>
+
+<div class="grid"><div class="subgrid layout"><div class="inner"></div></div></div>
+<div class="grid"><div class="subgrid paint"><div class="inner"></div></div></div>
+<div class="grid"><div class="subgrid layout"><div class="inner p"></div></div></div>
+<div class="grid"><div class="subgrid paint"><div class="inner p"></div></div></div>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/reference/contain-subgrid-001.html
@@ -0,0 +1,35 @@
+<!doctype html>
+<html lang=en>
+  <meta charset=utf-8>
+  <title>CSS-contain reference: layout/paint containment on subgrid</title>
+  <link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
+
+<style>
+.grid {
+  display: grid;
+  width: 40px;
+  height: 40px;
+  grid: [a] repeat(2,15px) [b] / [a] repeat(2,15px) [b];
+  gap: 10px;
+}
+
+.subgrid {
+  display: grid;
+  grid: none;
+  background: lightgrey;
+  grid-area:1/1/3/3;
+}
+
+.layout { contain: layout; }
+.paint { contain: paint; }
+
+.inner {
+  background: blue;
+  grid-area:a/a/b/b;
+}
+</style>
+
+<div class="grid"><div class="subgrid layout"><div class="inner"></div></div></div>
+<div class="grid"><div class="subgrid paint"><div class="inner"></div></div></div>
+<div class="grid"><div class="subgrid layout"><div class="inner"></div></div></div>
+<div class="grid"><div class="subgrid paint"><div class="inner"></div></div></div>
--- a/toolkit/components/search/tests/xpcshell/searchconfigs/xpcshell-1.ini
+++ b/toolkit/components/search/tests/xpcshell/searchconfigs/xpcshell-1.ini
@@ -2,9 +2,9 @@
 firefox-appdir = browser
 head = head_searchconfig.js head_chunk1.js
 dupe-manifest =
 support-files =
   ../../../../../../browser/locales/all-locales
 tags=searchconfig searchconfig1
 
 [include:xpcshell-common.ini]
-skip-if = toolkit == 'android'
+skip-if = toolkit == 'android' || appname == "thunderbird"
--- a/toolkit/components/search/tests/xpcshell/searchconfigs/xpcshell-2.ini
+++ b/toolkit/components/search/tests/xpcshell/searchconfigs/xpcshell-2.ini
@@ -2,9 +2,9 @@
 firefox-appdir = browser
 head = head_searchconfig.js head_chunk2.js
 dupe-manifest =
 support-files =
   ../../../../../../browser/locales/all-locales
 tags=searchconfig searchconfig2
 
 [include:xpcshell-common.ini]
-skip-if = toolkit == 'android'
+skip-if = toolkit == 'android' || appname == "thunderbird"
--- a/toolkit/components/search/tests/xpcshell/searchconfigs/xpcshell-3.ini
+++ b/toolkit/components/search/tests/xpcshell/searchconfigs/xpcshell-3.ini
@@ -2,9 +2,9 @@
 firefox-appdir = browser
 head = head_searchconfig.js head_chunk3.js
 dupe-manifest =
 support-files =
   ../../../../../../browser/locales/all-locales
 tags=searchconfig searchconfig3
 
 [include:xpcshell-common.ini]
-skip-if = toolkit == 'android'
+skip-if = toolkit == 'android' || appname == "thunderbird"
--- a/toolkit/components/search/tests/xpcshell/searchconfigs/xpcshell-4.ini
+++ b/toolkit/components/search/tests/xpcshell/searchconfigs/xpcshell-4.ini
@@ -2,9 +2,9 @@
 firefox-appdir = browser
 head = head_searchconfig.js head_chunk4.js
 dupe-manifest =
 support-files =
   ../../../../../../browser/locales/all-locales
 tags=searchconfig searchconfig4
 
 [include:xpcshell-common.ini]
-skip-if = toolkit == 'android'
+skip-if = toolkit == 'android' || appname == "thunderbird"
--- a/toolkit/components/search/tests/xpcshell/searchconfigs/xpcshell-common.ini
+++ b/toolkit/components/search/tests/xpcshell/searchconfigs/xpcshell-common.ini
@@ -16,18 +16,17 @@ requesttimeoutfactor = 3
 [test_duckduckgo.js]
 # This is an extensive test and currently takes a long time. Therefore skip on
 # debug/asan and extend the timeout length otherwise.
 skip-if = debug || asan
 requesttimeoutfactor = 3
 [test_ebay.js]
 # This is an extensive test and currently takes a long time. Therefore skip on
 # debug/asan and extend the timeout length otherwise.
-# Thunderbird doesn't provide eBay search engines.
-skip-if = debug || asan || appname == "thunderbird"
+skip-if = debug || asan
 requesttimeoutfactor = 3
 [test_google.js]
 # This is an extensive test and currently takes a long time. Therefore skip on
 # debug/asan and extend the timeout length otherwise.
 skip-if = debug || asan
 requesttimeoutfactor = 3
 [test_yandex.js]
 # This is an extensive test and currently takes a long time. Therefore skip on
--- a/toolkit/content/tests/chrome/test_bug437844.xul
+++ b/toolkit/content/tests/chrome/test_bug437844.xul
@@ -32,17 +32,17 @@ https://bugzilla.mozilla.org/show_bug.cg
     </div>
     <pre id="test">
     </pre>
   </body>
 
   <script class="testbody" type="application/javascript">
     <![CDATA[
 
-      SimpleTest.expectAssertions(14, 38);
+      SimpleTest.expectAssertions(14, 52);
 
       /** Test for Bug 437844 and Bug 348233 **/
       SimpleTest.waitForExplicitFinish();
 
       let prefs = Cc["@mozilla.org/preferences-service;1"]
                     .getService(Ci.nsIPrefBranch);
       prefs.setIntPref("intl.uidirection", 1);