Backed out 3 changesets (bug 1561738) for causing build bustages on nsGridContainerFrame.cpp CLOSED TREE
authorCosmin Sabou <csabou@mozilla.com>
Fri, 28 Jun 2019 13:27:09 +0300
changeset 480512 df2c3d2f218ecbe16b986705a4bccdf00c84af3a
parent 480511 9fb40bfdedf28eab8b91fde23c7a2f7867760875
child 480513 51dd2e169f3fdcb3a834e7e620f312ee8434ee16
push id36214
push usercsabou@mozilla.com
push dateFri, 28 Jun 2019 16:12:48 +0000
treeherdermozilla-central@8537d24d8aa8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1561738
milestone69.0a1
backs out98f100abc2ba9226fabcddda36647f32fd4cae23
9ed20d0fb9bafca54f7a935ee656c21ef4023199
483b1e134ace636bd745403be2017a1b3cd18993
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out 3 changesets (bug 1561738) for causing build bustages on nsGridContainerFrame.cpp CLOSED TREE Backed out changeset 98f100abc2ba (bug 1561738) Backed out changeset 9ed20d0fb9ba (bug 1561738) Backed out changeset 483b1e134ace (bug 1561738)
docshell/base/nsDocShellTreeOwner.cpp
dom/base/DOMIntersectionObserver.h
dom/base/nsFocusManager.cpp
dom/svg/SVGContentUtils.h
gfx/2d/Types.h
gfx/layers/FrameMetrics.h
layout/base/ScrollStyles.h
layout/base/ShapeUtils.cpp
layout/base/ShapeUtils.h
layout/base/nsLayoutUtils.h
layout/generic/ColumnUtils.h
layout/generic/ReflowInput.h
layout/generic/WritingModes.h
layout/generic/nsFlexContainerFrame.h
layout/generic/nsFrame.cpp
layout/generic/nsGridContainerFrame.cpp
layout/generic/nsImageFrame.cpp
layout/style/GeckoBindings.cpp
layout/style/GeckoBindings.h
layout/style/ServoBindings.toml
layout/style/ServoStyleConstsInlines.h
layout/style/StyleAnimationValue.h
layout/style/moz.build
layout/style/nsCSSValue.h
layout/style/nsComputedDOMStyle.cpp
layout/style/nsComputedDOMStyle.h
layout/style/nsStyleCoord.cpp
layout/style/nsStyleCoord.h
layout/style/nsStyleStruct.cpp
layout/style/nsStyleStruct.h
layout/style/nsStyleUtil.cpp
layout/style/nsStyleUtil.h
layout/svg/nsSVGUtils.cpp
layout/tables/nsTableFrame.h
servo/components/style/gecko/conversions.rs
servo/components/style/gecko/values.rs
servo/components/style/gecko_bindings/sugar/mod.rs
servo/components/style/gecko_bindings/sugar/ns_style_coord.rs
servo/components/style/properties/gecko.mako.rs
servo/components/style/values/computed/mod.rs
servo/components/style/values/generics/grid.rs
servo/components/style/values/specified/grid.rs
servo/ports/geckolib/cbindgen.toml
--- a/docshell/base/nsDocShellTreeOwner.cpp
+++ b/docshell/base/nsDocShellTreeOwner.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // Local Includes
 #include "nsDocShellTreeOwner.h"
 #include "nsWebBrowser.h"
 
 // Helper Classes
 #include "nsContentUtils.h"
+#include "nsStyleCoord.h"
 #include "nsSize.h"
 #include "mozilla/ReflowInput.h"
 #include "nsIServiceManager.h"
 #include "nsComponentManagerUtils.h"
 #include "nsString.h"
 #include "nsAtom.h"
 #include "nsReadableUtils.h"
 #include "nsUnicharUtils.h"
--- a/dom/base/DOMIntersectionObserver.h
+++ b/dom/base/DOMIntersectionObserver.h
@@ -4,17 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef DOMIntersectionObserver_h
 #define DOMIntersectionObserver_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/IntersectionObserverBinding.h"
-#include "mozilla/ServoStyleConsts.h"
+#include "nsStyleCoord.h"
 #include "nsTArray.h"
 
 using mozilla::dom::DOMRect;
 using mozilla::dom::Element;
 
 namespace mozilla {
 namespace dom {
 
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -31,16 +31,17 @@
 #include "mozilla/dom/Selection.h"
 #include "nsXULPopupManager.h"
 #include "nsMenuPopupFrame.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIPrincipal.h"
 #include "nsIObserverService.h"
 #include "nsIObjectFrame.h"
 #include "nsBindingManager.h"
+#include "nsStyleCoord.h"
 #include "BrowserChild.h"
 #include "nsFrameLoader.h"
 #include "nsHTMLDocument.h"
 #include "nsNumberControlFrame.h"
 #include "nsNetUtil.h"
 #include "nsRange.h"
 
 #include "mozilla/AccessibleCaretEventHub.h"
--- a/dom/svg/SVGContentUtils.h
+++ b/dom/svg/SVGContentUtils.h
@@ -8,17 +8,17 @@
 #define MOZILLA_SVGCONTENTUTILS_H
 
 // include math.h to pick up definition of M_ maths defines e.g. M_PI
 #include <math.h>
 
 #include "mozilla/gfx/2D.h"  // for StrokeOptions
 #include "mozilla/gfx/Matrix.h"
 #include "mozilla/RangedPtr.h"
-#include "mozilla/ServoStyleConsts.h"
+#include "nsStyleCoord.h"
 #include "nsError.h"
 #include "nsStringFwd.h"
 #include "gfx2DGlue.h"
 #include "nsDependentSubstring.h"
 
 class nsIContent;
 
 class nsIFrame;
@@ -313,17 +313,17 @@ class SVGContentUtils {
    * Parse an integer of the form:
    * integer ::= [+-]? [0-9]+
    * The returned number is clamped to an int32_t if outside that range.
    * Parsing fails if there is anything left over after the number.
    */
   static bool ParseInteger(const nsAString& aString, int32_t& aValue);
 
   /**
-   * Converts a LengthPercentage into a userspace value, resolving percentage
+   * Converts an nsStyleCoord into a userspace value, resolving percentage
    * values relative to aContent's SVG viewport.
    */
   static float CoordToFloat(dom::SVGElement* aContent, const LengthPercentage&);
   /**
    * Parse the SVG path string
    * Returns a path
    * string formatted as an SVG path
    */
--- a/gfx/2d/Types.h
+++ b/gfx/2d/Types.h
@@ -555,17 +555,17 @@ static inline HalfCorner operator++(Half
   MOZ_ASSERT(
       aHalfCorner >= eCornerTopLeftX && aHalfCorner <= eCornerBottomLeftY,
       "Out of range half corner!");
   aHalfCorner = HalfCorner(aHalfCorner + 1);
   return aHalfCorner;
 }
 
 // The result of these conversion functions are exhaustively checked in
-// nsFrame.cpp, which also serves as usage examples.
+// nsStyleCoord.cpp, which also serves as usage examples.
 
 constexpr bool HalfCornerIsX(HalfCorner aHalfCorner) {
   return !(aHalfCorner % 2);
 }
 
 constexpr Corner HalfToFullCorner(HalfCorner aHalfCorner) {
   return Corner(aHalfCorner / 2);
 }
--- a/gfx/layers/FrameMetrics.h
+++ b/gfx/layers/FrameMetrics.h
@@ -17,17 +17,17 @@
 #include "mozilla/gfx/Rect.h"                    // for RoundedIn
 #include "mozilla/gfx/ScaleFactor.h"             // for ScaleFactor
 #include "mozilla/gfx/Logging.h"                 // for Log
 #include "mozilla/layers/LayersTypes.h"          // for ScrollDirection
 #include "mozilla/layers/ScrollableLayerGuid.h"  // for ScrollableLayerGuid
 #include "mozilla/StaticPtr.h"                   // for StaticAutoPtr
 #include "mozilla/TimeStamp.h"                   // for TimeStamp
 #include "nsString.h"
-#include "mozilla/ServoStyleConsts.h"
+#include "nsStyleCoord.h"  // for nsStyleCoord
 #include "PLDHashTable.h"  // for PLDHashNumber
 
 struct nsStyleDisplay;
 namespace mozilla {
 class WritingMode;
 }  // namespace mozilla
 
 namespace IPC {
@@ -754,17 +754,17 @@ struct ScrollSnapInfo {
   mozilla::StyleScrollSnapStrictness mScrollSnapStrictnessY =
       mozilla::StyleScrollSnapStrictness::None;
 
   // The intervals derived from the scroll frame's scroll-snap-points.
   Maybe<nscoord> mScrollSnapIntervalX;
   Maybe<nscoord> mScrollSnapIntervalY;
 
   // The scroll frame's scroll-snap-destination, in cooked form (to avoid
-  // shipping the raw style value over IPC).
+  // shipping the raw nsStyleCoord::CalcValue over IPC).
   nsPoint mScrollSnapDestination;
 
   // The scroll-snap-coordinates of any descendant frames of the scroll frame,
   // relative to the origin of the scrolled frame.
   nsTArray<nsPoint> mScrollSnapCoordinates;
 
   // The scroll positions corresponding to scroll-snap-align values.
   nsTArray<nscoord> mSnapPositionX;
--- a/layout/base/ScrollStyles.h
+++ b/layout/base/ScrollStyles.h
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_ScrollStyles_h
 #define mozilla_ScrollStyles_h
 
 #include <stdint.h>
 #include "nsStyleConsts.h"
+#include "nsStyleCoord.h"  // for nsStyleCoord
 #include "mozilla/dom/WindowBinding.h"
 
 // Forward declarations
 struct nsStyleDisplay;
 
 namespace mozilla {
 
 struct ScrollStyles {
--- a/layout/base/ShapeUtils.cpp
+++ b/layout/base/ShapeUtils.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/ShapeUtils.h"
 
 #include <cstdlib>
 
 #include "nsCSSRendering.h"
 #include "nsMargin.h"
+#include "nsStyleCoord.h"
 #include "nsStyleStruct.h"
 #include "SVGContentUtils.h"
 
 namespace mozilla {
 
 nscoord ShapeUtils::ComputeShapeRadius(const StyleShapeRadius& aType,
                                        const nscoord aCenter,
                                        const nscoord aPosMin,
--- a/layout/base/ShapeUtils.h
+++ b/layout/base/ShapeUtils.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_ShapeUtils_h
 #define mozilla_ShapeUtils_h
 
 #include "nsCoord.h"
 #include "nsSize.h"
 #include "nsStyleConsts.h"
+#include "nsStyleCoord.h"
 #include "nsTArray.h"
 
 struct nsPoint;
 struct nsRect;
 
 namespace mozilla {
 // ShapeUtils is a namespace class containing utility functions related to
 // processing basic shapes in the CSS Shapes Module.
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -14,16 +14,17 @@
 #include "mozilla/Maybe.h"
 #include "mozilla/TypedEnumBits.h"
 #include "nsBoundingMetrics.h"
 #include "nsChangeHint.h"
 #include "mozilla/layout/FrameChildList.h"
 #include "mozilla/layers/ScrollableLayerGuid.h"
 #include "nsThreadUtils.h"
 #include "nsCSSPropertyIDSet.h"
+#include "nsStyleCoord.h"
 #include "nsStyleConsts.h"
 #include "nsGkAtoms.h"
 #include "mozilla/gfx/2D.h"
 #include "Units.h"
 #include "mozilla/ToString.h"
 #include "mozilla/ReflowOutput.h"
 #include "ImageContainer.h"  // for layers::Image
 #include "gfx2DGlue.h"
@@ -49,16 +50,17 @@ enum class nsDisplayListBuilderMode : ui
 class nsDisplayItem;
 class nsFontMetrics;
 class nsFontFaceList;
 class nsIImageLoadingContent;
 class nsBlockFrame;
 class nsContainerFrame;
 class nsView;
 class nsIFrame;
+class nsStyleCoord;
 class nsPIDOMWindowOuter;
 class imgIRequest;
 struct nsStyleFont;
 struct nsOverflowAreas;
 
 namespace mozilla {
 struct AspectRatio;
 class ComputedStyle;
--- a/layout/generic/ColumnUtils.h
+++ b/layout/generic/ColumnUtils.h
@@ -4,18 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* A namespace class for static muti-column utilities. */
 
 #ifndef mozilla_ColumnUtils_h
 #define mozilla_ColumnUtils_h
 
-#include "nsCoord.h"
-#include "nsStyleConsts.h"
+#include "nsStyleCoord.h"
 
 class nsContainerFrame;
 
 namespace mozilla {
 
 // ColumnUtils is a namespace class containing utility functions used by
 // multi-column containers like ColumnSetWrapperFrame and nsColumnSetFrame.
 //
--- a/layout/generic/ReflowInput.h
+++ b/layout/generic/ReflowInput.h
@@ -5,17 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* struct containing the input to nsIFrame::Reflow */
 
 #ifndef mozilla_ReflowInput_h
 #define mozilla_ReflowInput_h
 
 #include "nsMargin.h"
-#include "nsStyleConsts.h"
+#include "nsStyleCoord.h"
 #include "nsIFrame.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Maybe.h"
 #include <algorithm>
 
 class gfxContext;
 class nsFloatManager;
 struct nsHypotheticalPosition;
@@ -329,20 +329,20 @@ struct SizeComputationInput {
  protected:
   void InitOffsets(mozilla::WritingMode aWM, nscoord aPercentBasis,
                    mozilla::LayoutFrameType aFrameType, ReflowInputFlags aFlags,
                    const nsMargin* aBorder = nullptr,
                    const nsMargin* aPadding = nullptr,
                    const nsStyleDisplay* aDisplay = nullptr);
 
   /*
-   * Convert StyleSize or StyleMaxSize to nscoord when percentages depend on the
-   * inline size of the containing block, and enumerated values are for inline
-   * size, min-inline-size, or max-inline-size.  Does not handle auto inline
-   * sizes.
+   * Convert nsStyleCoord to nscoord when percentages depend on the
+   * inline size of the containing block, and enumerated values are for
+   * inline size, min-inline-size, or max-inline-size.  Does not handle
+   * auto inline sizes.
    */
   template <typename SizeOrMaxSize>
   inline nscoord ComputeISizeValue(nscoord aContainingBlockISize,
                                    nscoord aContentEdgeToBoxSizing,
                                    nscoord aBoxSizingToMarginEdge,
                                    const SizeOrMaxSize&) const;
   // same as previous, but using mComputedBorderPadding, mComputedPadding,
   // and mComputedMargin
--- a/layout/generic/WritingModes.h
+++ b/layout/generic/WritingModes.h
@@ -1914,16 +1914,69 @@ const T& StyleRect<T>::GetIEnd(mozilla::
 
 template <typename T>
 const T& StyleRect<T>::GetBEnd(mozilla::WritingMode aWM) const {
   return Get(aWM, mozilla::eLogicalSideBEnd);
 }
 
 }  // namespace mozilla
 
+// Definitions of inline methods for nsStyleSides, declared in nsStyleCoord.h
+// but not defined there because they need WritingMode.
+inline nsStyleUnit nsStyleSides::GetUnit(mozilla::WritingMode aWM,
+                                         mozilla::LogicalSide aSide) const {
+  return GetUnit(aWM.PhysicalSide(aSide));
+}
+
+inline nsStyleUnit nsStyleSides::GetIStartUnit(mozilla::WritingMode aWM) const {
+  return GetUnit(aWM, mozilla::eLogicalSideIStart);
+}
+
+inline nsStyleUnit nsStyleSides::GetBStartUnit(mozilla::WritingMode aWM) const {
+  return GetUnit(aWM, mozilla::eLogicalSideBStart);
+}
+
+inline nsStyleUnit nsStyleSides::GetIEndUnit(mozilla::WritingMode aWM) const {
+  return GetUnit(aWM, mozilla::eLogicalSideIEnd);
+}
+
+inline nsStyleUnit nsStyleSides::GetBEndUnit(mozilla::WritingMode aWM) const {
+  return GetUnit(aWM, mozilla::eLogicalSideBEnd);
+}
+
+inline bool nsStyleSides::HasBlockAxisAuto(mozilla::WritingMode aWM) const {
+  return GetBStartUnit(aWM) == eStyleUnit_Auto ||
+         GetBEndUnit(aWM) == eStyleUnit_Auto;
+}
+
+inline bool nsStyleSides::HasInlineAxisAuto(mozilla::WritingMode aWM) const {
+  return GetIStartUnit(aWM) == eStyleUnit_Auto ||
+         GetIEndUnit(aWM) == eStyleUnit_Auto;
+}
+
+inline nsStyleCoord nsStyleSides::Get(mozilla::WritingMode aWM,
+                                      mozilla::LogicalSide aSide) const {
+  return Get(aWM.PhysicalSide(aSide));
+}
+
+inline nsStyleCoord nsStyleSides::GetIStart(mozilla::WritingMode aWM) const {
+  return Get(aWM, mozilla::eLogicalSideIStart);
+}
+
+inline nsStyleCoord nsStyleSides::GetBStart(mozilla::WritingMode aWM) const {
+  return Get(aWM, mozilla::eLogicalSideBStart);
+}
+
+inline nsStyleCoord nsStyleSides::GetIEnd(mozilla::WritingMode aWM) const {
+  return Get(aWM, mozilla::eLogicalSideIEnd);
+}
+
+inline nsStyleCoord nsStyleSides::GetBEnd(mozilla::WritingMode aWM) const {
+  return Get(aWM, mozilla::eLogicalSideBEnd);
+}
 
 // Definitions of inline methods for nsStylePosition, declared in
 // nsStyleStruct.h but not defined there because they need WritingMode.
 inline const mozilla::StyleSize& nsStylePosition::ISize(WritingMode aWM) const {
   return aWM.IsVertical() ? mHeight : mWidth;
 }
 inline const mozilla::StyleSize& nsStylePosition::MinISize(
     WritingMode aWM) const {
--- a/layout/generic/nsFlexContainerFrame.h
+++ b/layout/generic/nsFlexContainerFrame.h
@@ -8,16 +8,18 @@
 
 #ifndef nsFlexContainerFrame_h___
 #define nsFlexContainerFrame_h___
 
 #include "nsContainerFrame.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/dom/FlexBinding.h"
 
+class nsStyleCoord;
+
 namespace mozilla {
 template <class T>
 class LinkedList;
 class LogicalPoint;
 class PresShell;
 }  // namespace mozilla
 
 nsContainerFrame* NS_NewFlexContainerFrame(mozilla::PresShell* aPresShell,
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -12316,102 +12316,9 @@ void ReflowInput::DisplayInitFrameTypeEx
     printf("%s%s\n", repNoBlock ? " +rep" : "", repBlock ? " +repBlk" : "");
   }
   DR_state->DeleteTreeNode(*treeNode);
 }
 
 #  endif
 // End Display Reflow
 
-// Validation of SideIsVertical.
-#define CASE(side, result) \
-  static_assert(SideIsVertical(side) == result, "SideIsVertical is wrong")
-CASE(eSideTop, false);
-CASE(eSideRight, true);
-CASE(eSideBottom, false);
-CASE(eSideLeft, true);
-#undef CASE
-
-// Validation of HalfCornerIsX.
-#define CASE(corner, result) \
-  static_assert(HalfCornerIsX(corner) == result, "HalfCornerIsX is wrong")
-CASE(eCornerTopLeftX, true);
-CASE(eCornerTopLeftY, false);
-CASE(eCornerTopRightX, true);
-CASE(eCornerTopRightY, false);
-CASE(eCornerBottomRightX, true);
-CASE(eCornerBottomRightY, false);
-CASE(eCornerBottomLeftX, true);
-CASE(eCornerBottomLeftY, false);
-#undef CASE
-
-// Validation of HalfToFullCorner.
-#define CASE(corner, result)                        \
-  static_assert(HalfToFullCorner(corner) == result, \
-                "HalfToFullCorner is "              \
-                "wrong")
-CASE(eCornerTopLeftX, eCornerTopLeft);
-CASE(eCornerTopLeftY, eCornerTopLeft);
-CASE(eCornerTopRightX, eCornerTopRight);
-CASE(eCornerTopRightY, eCornerTopRight);
-CASE(eCornerBottomRightX, eCornerBottomRight);
-CASE(eCornerBottomRightY, eCornerBottomRight);
-CASE(eCornerBottomLeftX, eCornerBottomLeft);
-CASE(eCornerBottomLeftY, eCornerBottomLeft);
-#undef CASE
-
-// Validation of FullToHalfCorner.
-#define CASE(corner, vert, result)                        \
-  static_assert(FullToHalfCorner(corner, vert) == result, \
-                "FullToHalfCorner is wrong")
-CASE(eCornerTopLeft, false, eCornerTopLeftX);
-CASE(eCornerTopLeft, true, eCornerTopLeftY);
-CASE(eCornerTopRight, false, eCornerTopRightX);
-CASE(eCornerTopRight, true, eCornerTopRightY);
-CASE(eCornerBottomRight, false, eCornerBottomRightX);
-CASE(eCornerBottomRight, true, eCornerBottomRightY);
-CASE(eCornerBottomLeft, false, eCornerBottomLeftX);
-CASE(eCornerBottomLeft, true, eCornerBottomLeftY);
-#undef CASE
-
-// Validation of SideToFullCorner.
-#define CASE(side, second, result)                        \
-  static_assert(SideToFullCorner(side, second) == result, \
-                "SideToFullCorner is wrong")
-CASE(eSideTop, false, eCornerTopLeft);
-CASE(eSideTop, true, eCornerTopRight);
-
-CASE(eSideRight, false, eCornerTopRight);
-CASE(eSideRight, true, eCornerBottomRight);
-
-CASE(eSideBottom, false, eCornerBottomRight);
-CASE(eSideBottom, true, eCornerBottomLeft);
-
-CASE(eSideLeft, false, eCornerBottomLeft);
-CASE(eSideLeft, true, eCornerTopLeft);
-#undef CASE
-
-// Validation of SideToHalfCorner.
-#define CASE(side, second, parallel, result)                        \
-  static_assert(SideToHalfCorner(side, second, parallel) == result, \
-                "SideToHalfCorner is wrong")
-CASE(eSideTop, false, true, eCornerTopLeftX);
-CASE(eSideTop, false, false, eCornerTopLeftY);
-CASE(eSideTop, true, true, eCornerTopRightX);
-CASE(eSideTop, true, false, eCornerTopRightY);
-
-CASE(eSideRight, false, false, eCornerTopRightX);
-CASE(eSideRight, false, true, eCornerTopRightY);
-CASE(eSideRight, true, false, eCornerBottomRightX);
-CASE(eSideRight, true, true, eCornerBottomRightY);
-
-CASE(eSideBottom, false, true, eCornerBottomRightX);
-CASE(eSideBottom, false, false, eCornerBottomRightY);
-CASE(eSideBottom, true, true, eCornerBottomLeftX);
-CASE(eSideBottom, true, false, eCornerBottomLeftY);
-
-CASE(eSideLeft, false, false, eCornerBottomLeftX);
-CASE(eSideLeft, false, true, eCornerBottomLeftY);
-CASE(eSideLeft, true, false, eCornerTopLeftX);
-CASE(eSideLeft, true, true, eCornerTopLeftY);
-#undef CASE
-
 #endif
--- a/layout/generic/nsGridContainerFrame.cpp
+++ b/layout/generic/nsGridContainerFrame.cpp
@@ -54,42 +54,16 @@ static const nsFrameState kIsSubgridBits
 
 // 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
 };
 
-template <>
-inline const StyleTrackBreadth& StyleTrackSize::GetMax() const {
-  if (IsBreadth()) {
-    return AsBreadth();
-  }
-  if (IsMinmax()) {
-    return AsMinmax()._1;
-  }
-  MOZ_ASSERT(IsFitContent());
-  return AsFitContent();
-}
-
-template <>
-inline const StyleTrackBreadth& StyleTrackSize::GetMin() const {
-  static const StyleTrackBreadth kAuto = StyleTrackBreadth::Auto();
-  if (IsBreadth()) {
-    // <flex> behaves like minmax(auto, <flex>)
-    return AsBreadth().IsFr() ? kAuto : AsBreadth();
-  }
-  if (IsMinmax()) {
-    return AsMinmax()._0;
-  }
-  MOZ_ASSERT(IsFitContent());
-  return kAuto;
-}
-
 static void ReparentFrame(nsIFrame* aFrame, nsContainerFrame* aOldParent,
                           nsContainerFrame* aNewParent) {
   NS_ASSERTION(aOldParent == aFrame->GetParent(),
                "Parent not consistent with expectations");
 
   aFrame->SetParent(aNewParent);
 
   // When pushing and pulling frames we need to check for whether any
@@ -137,23 +111,23 @@ static nscoord ClampToCSSMaxBSize(nscoor
 }
 
 template <typename Size>
 static bool IsPercentOfIndefiniteSize(const Size& aCoord,
                                       nscoord aPercentBasis) {
   return aPercentBasis == NS_UNCONSTRAINEDSIZE && aCoord.HasPercent();
 }
 
-static nscoord ResolveToDefiniteSize(const StyleTrackBreadth& aBreadth,
+static nscoord ResolveToDefiniteSize(const nsStyleCoord& aCoord,
                                      nscoord aPercentBasis) {
-  MOZ_ASSERT(aBreadth.IsBreadth());
-  if (::IsPercentOfIndefiniteSize(aBreadth.AsBreadth(), aPercentBasis)) {
+  MOZ_ASSERT(aCoord.IsCoordPercentCalcUnit());
+  if (::IsPercentOfIndefiniteSize(aCoord, aPercentBasis)) {
     return nscoord(0);
   }
-  return std::max(nscoord(0), aBreadth.AsBreadth().Resolve(aPercentBasis));
+  return std::max(nscoord(0), aCoord.ComputeCoordPercentCalc(aPercentBasis));
 }
 
 // Synthesize a baseline from a border box.  For an alphabetical baseline
 // this is the end edge of the border box.  For a central baseline it's
 // the center of the border box.
 // https://drafts.csswg.org/css-align-3/#synthesize-baselines
 // For a 'first baseline' the measure is from the border-box start edge and
 // for a 'last baseline' the measure is from the border-box end edge.
@@ -228,22 +202,28 @@ struct nsGridContainerFrame::TrackSize {
     eSkipGrowUnlimited2 =       0x400,
     eSkipGrowUnlimited = eSkipGrowUnlimited1 | eSkipGrowUnlimited2,
     eBreakBefore =              0x800,
     eFitContent =              0x1000,
     eInfinitelyGrowable =      0x2000,
     // clang-format on
   };
 
-  StateBits Initialize(nscoord aPercentageBasis, const StyleTrackSize&);
+  StateBits Initialize(nscoord aPercentageBasis, const nsStyleCoord& aMinCoord,
+                       const nsStyleCoord& aMaxCoord);
   bool IsFrozen() const { return mState & eFrozen; }
 #ifdef DEBUG
   void Dump() const;
 #endif
 
+  static bool IsMinContent(const nsStyleCoord& aCoord) {
+    return aCoord.GetUnit() == eStyleUnit_Enumerated &&
+           aCoord.GetEnumValue<StyleGridTrackBreadth>() ==
+               StyleGridTrackBreadth::MinContent;
+  }
   static bool IsDefiniteMaxSizing(StateBits aStateBits) {
     return (aStateBits & (eIntrinsicMaxSizing | eFlexMaxSizing)) == 0;
   }
 
   nscoord mBase;
   nscoord mLimit;
   nscoord mPosition;  // zero until we apply 'align/justify-content'
   // mBaselineSubtreeSize is the size of a baseline-aligned subtree within
@@ -255,82 +235,76 @@ struct nsGridContainerFrame::TrackSize {
 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(TrackSize::StateBits)
 
 namespace mozilla {
 template <>
 struct IsPod<nsGridContainerFrame::TrackSize> : TrueType {};
 }  // namespace mozilla
 
 TrackSize::StateBits nsGridContainerFrame::TrackSize::Initialize(
-    nscoord aPercentageBasis, const StyleTrackSize& aSize) {
-  using Tag = StyleTrackBreadth::Tag;
-
+    nscoord aPercentageBasis, const nsStyleCoord& aMinCoord,
+    const nsStyleCoord& aMaxCoord) {
   MOZ_ASSERT(mBase == 0 && mLimit == 0 && mState == 0,
              "track size data is expected to be initialized to zero");
-  mBaselineSubtreeSize[BaselineSharingGroup::First] = nscoord(0);
-  mBaselineSubtreeSize[BaselineSharingGroup::Last] = nscoord(0);
-
-  auto& min = aSize.GetMin();
-  auto& max = aSize.GetMax();
-
-  Tag minSizeTag = min.tag;
-  Tag maxSizeTag = max.tag;
-  if (aSize.IsFitContent()) {
-    // In layout, fit-content(size) behaves as minmax(auto, max-content), with
-    // 'size' as an additional upper-bound.
+  auto minSizeUnit = aMinCoord.GetUnit();
+  auto maxSizeUnit = aMaxCoord.GetUnit();
+  if (minSizeUnit == eStyleUnit_None) {
+    // This track is sized using fit-content(size) (represented in style system
+    // with minCoord=None,maxCoord=size).  In layout, fit-content(size) behaves
+    // as minmax(auto, max-content), with 'size' as an additional upper-bound.
     mState = eFitContent;
-    minSizeTag = Tag::Auto;
-    maxSizeTag = Tag::MaxContent;
-  }
-  if (::IsPercentOfIndefiniteSize(min, aPercentageBasis)) {
+    minSizeUnit = eStyleUnit_Auto;
+    maxSizeUnit = eStyleUnit_Enumerated;  // triggers max-content sizing below
+  }
+  if (::IsPercentOfIndefiniteSize(aMinCoord, aPercentageBasis)) {
     // https://drafts.csswg.org/css-grid/#valdef-grid-template-columns-percentage
     // "If the inline or block size of the grid container is indefinite,
     //  <percentage> values relative to that size are treated as 'auto'."
-    minSizeTag = Tag::Auto;
-  }
-  if (::IsPercentOfIndefiniteSize(max, aPercentageBasis)) {
-    maxSizeTag = Tag::Auto;
-  }
-
+    minSizeUnit = eStyleUnit_Auto;
+  }
+  if (::IsPercentOfIndefiniteSize(aMaxCoord, aPercentageBasis)) {
+    maxSizeUnit = eStyleUnit_Auto;
+  }
   // http://dev.w3.org/csswg/css-grid/#algo-init
-  switch (minSizeTag) {
-    case Tag::Auto:
+  switch (minSizeUnit) {
+    case eStyleUnit_Auto:
       mState |= eAutoMinSizing;
       break;
-    case Tag::MinContent:
-      mState |= eMinContentMinSizing;
-      break;
-    case Tag::MaxContent:
-      mState |= eMaxContentMinSizing;
+    case eStyleUnit_Enumerated:
+      mState |=
+          IsMinContent(aMinCoord) ? eMinContentMinSizing : eMaxContentMinSizing;
       break;
     default:
-      MOZ_ASSERT(!min.IsFr(), "<flex> min-sizing is invalid as a track size");
-      mBase = ::ResolveToDefiniteSize(min, aPercentageBasis);
-  }
-  switch (maxSizeTag) {
-    case Tag::Auto:
+      MOZ_ASSERT(minSizeUnit != eStyleUnit_FlexFraction,
+                 "<flex> min-sizing is invalid as a track size");
+      mBase = ::ResolveToDefiniteSize(aMinCoord, aPercentageBasis);
+  }
+  switch (maxSizeUnit) {
+    case eStyleUnit_Auto:
       mState |= eAutoMaxSizing;
       mLimit = NS_UNCONSTRAINEDSIZE;
       break;
-    case Tag::MinContent:
-    case Tag::MaxContent:
-      mState |= maxSizeTag == Tag::MinContent ? eMinContentMaxSizing
-                                              : eMaxContentMaxSizing;
+    case eStyleUnit_Enumerated:
+      mState |=
+          IsMinContent(aMaxCoord) ? eMinContentMaxSizing : eMaxContentMaxSizing;
       mLimit = NS_UNCONSTRAINEDSIZE;
       break;
-    case Tag::Fr:
+    case eStyleUnit_FlexFraction:
       mState |= eFlexMaxSizing;
       mLimit = mBase;
       break;
     default:
-      mLimit = ::ResolveToDefiniteSize(max, aPercentageBasis);
+      mLimit = ::ResolveToDefiniteSize(aMaxCoord, aPercentageBasis);
       if (mLimit < mBase) {
         mLimit = mBase;
       }
   }
+
+  mBaselineSubtreeSize[BaselineSharingGroup::First] = nscoord(0);
+  mBaselineSubtreeSize[BaselineSharingGroup::Last] = nscoord(0);
   return mState;
 }
 
 /**
  * Is aFrame1 a prev-continuation of aFrame2?
  */
 static bool IsPrevContinuationOf(nsIFrame* aFrame1, nsIFrame* aFrame2) {
   nsIFrame* prev = aFrame2;
@@ -1261,39 +1235,47 @@ class MOZ_STACK_CLASS nsGridContainerFra
   // True if there is a specified repeat(auto-fill/fit) track.
   const bool mHasRepeatAuto;
 };
 
 /**
  * Encapsulates CSS track-sizing functions.
  */
 struct nsGridContainerFrame::TrackSizingFunctions {
-  TrackSizingFunctions(const nsTArray<StyleTrackSize>& aSizingFunctions,
-                       const StyleTrackSize& aAutoSizing, bool aHasRepeatAuto,
+  TrackSizingFunctions(const nsTArray<nsStyleCoord>& aMinSizingFunctions,
+                       const nsTArray<nsStyleCoord>& aMaxSizingFunctions,
+                       const nsStyleCoord& aAutoMinSizing,
+                       const nsStyleCoord& aAutoMaxSizing, bool aHasRepeatAuto,
                        int32_t aRepeatAutoIndex)
-      : mSizingFunctions(aSizingFunctions),
-        mAutoSizing(aAutoSizing),
+      : mMinSizingFunctions(aMinSizingFunctions),
+        mMaxSizingFunctions(aMaxSizingFunctions),
+        mAutoMinSizing(aAutoMinSizing),
+        mAutoMaxSizing(aAutoMaxSizing),
         mExplicitGridOffset(0),
         mRepeatAutoStart(aHasRepeatAuto ? aRepeatAutoIndex : 0),
         mRepeatAutoEnd(mRepeatAutoStart),
         mRepeatEndDelta(0),
         mHasRepeatAuto(aHasRepeatAuto) {
+    MOZ_ASSERT(mMinSizingFunctions.Length() == mMaxSizingFunctions.Length());
     MOZ_ASSERT(!mHasRepeatAuto ||
-               (mSizingFunctions.Length() >= 1 &&
-                mRepeatAutoStart < mSizingFunctions.Length()));
+               (mMinSizingFunctions.Length() >= 1 &&
+                mRepeatAutoStart < mMinSizingFunctions.Length()));
   }
 
   TrackSizingFunctions(const nsStyleGridTemplate& aGridTemplate,
-                       const StyleTrackSize& aAutoSizing)
+                       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.mTrackSizingFunctions, aAutoSizing,
+            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,
@@ -1311,36 +1293,36 @@ struct nsGridContainerFrame::TrackSizing
 
   uint32_t CalculateRepeatFillCount(
       const NonNegativeLengthPercentageOrNormal& aGridGap, nscoord aMinSize,
       nscoord aSize, nscoord aMaxSize) const {
     if (!mHasRepeatAuto) {
       return 0;
     }
     // Spec quotes are from https://drafts.csswg.org/css-grid/#repeat-notation
-    const uint32_t numTracks = mSizingFunctions.Length();
+    const uint32_t numTracks = mMinSizingFunctions.Length();
     MOZ_ASSERT(numTracks >= 1, "expected at least the repeat() track");
     nscoord maxFill = aSize != NS_UNCONSTRAINEDSIZE ? aSize : aMaxSize;
     if (maxFill == NS_UNCONSTRAINEDSIZE && aMinSize == 0) {
       // "Otherwise, the specified track list repeats only once."
       return 1;
     }
     nscoord repeatTrackSize = 0;
     // Note that one repeat() track size is included in |sum| in this loop.
     nscoord sum = 0;
     const nscoord percentBasis = aSize;
     for (uint32_t i = 0; i < numTracks; ++i) {
       // "treating each track as its max track sizing function if that is
       // definite or as its minimum track sizing function otherwise"
       // https://drafts.csswg.org/css-grid/#valdef-repeat-auto-fill
-      const auto& maxCoord = mSizingFunctions[i].GetMax();
+      const auto& maxCoord = mMaxSizingFunctions[i];
       const auto* coord = &maxCoord;
-      if (!coord->IsBreadth()) {
-        coord = &mSizingFunctions[i].GetMin();
-        if (!coord->IsBreadth()) {
+      if (!coord->IsCoordPercentCalcUnit()) {
+        coord = &mMinSizingFunctions[i];
+        if (!coord->IsCoordPercentCalcUnit()) {
           return 1;
         }
       }
       nscoord trackSize = ::ResolveToDefiniteSize(*coord, percentBasis);
       if (i == mRepeatAutoStart) {
         // Use a minimum 1px for the repeat() track-size.
         if (trackSize < AppUnitsPerCSSPixel()) {
           trackSize = AppUnitsPerCSSPixel();
@@ -1383,57 +1365,67 @@ struct nsGridContainerFrame::TrackSizing
    */
   uint32_t ComputeExplicitGridEnd(uint32_t aGridTemplateAreasEnd) {
     uint32_t end = NumExplicitTracks() + 1;
     end = std::max(end, aGridTemplateAreasEnd);
     end = std::min(end, uint32_t(kMaxLine));
     return end;
   }
 
-  const StyleTrackSize& SizingFor(uint32_t aTrackIndex) const {
+  const nsStyleCoord& MinSizingFor(uint32_t aTrackIndex) const {
     if (MOZ_UNLIKELY(aTrackIndex < mExplicitGridOffset)) {
-      return mAutoSizing;
+      return mAutoMinSizing;
     }
     uint32_t index = aTrackIndex - mExplicitGridOffset;
     if (index >= mRepeatAutoStart) {
       if (index < mRepeatAutoEnd) {
-        return mSizingFunctions[mRepeatAutoStart];
+        return mMinSizingFunctions[mRepeatAutoStart];
       }
       index -= mRepeatEndDelta;
     }
-    return index < mSizingFunctions.Length() ? mSizingFunctions[index]
-                                             : mAutoSizing;
-  }
-  const StyleTrackBreadth& MaxSizingFor(uint32_t aTrackIndex) const {
-    return SizingFor(aTrackIndex).GetMax();
-  }
-  const StyleTrackBreadth& MinSizingFor(uint32_t aTrackIndex) const {
-    return SizingFor(aTrackIndex).GetMin();
+    return index < mMinSizingFunctions.Length() ? mMinSizingFunctions[index]
+                                                : mAutoMinSizing;
+  }
+  const nsStyleCoord& MaxSizingFor(uint32_t aTrackIndex) const {
+    if (MOZ_UNLIKELY(aTrackIndex < mExplicitGridOffset)) {
+      return mAutoMaxSizing;
+    }
+    uint32_t index = aTrackIndex - mExplicitGridOffset;
+    if (index >= mRepeatAutoStart) {
+      if (index < mRepeatAutoEnd) {
+        return mMaxSizingFunctions[mRepeatAutoStart];
+      }
+      index -= mRepeatEndDelta;
+    }
+    return index < mMaxSizingFunctions.Length() ? mMaxSizingFunctions[index]
+                                                : mAutoMaxSizing;
   }
   uint32_t NumExplicitTracks() const {
-    return mSizingFunctions.Length() + mRepeatEndDelta;
+    return mMinSizingFunctions.Length() + mRepeatEndDelta;
   }
   uint32_t NumRepeatTracks() const { return mRepeatAutoEnd - mRepeatAutoStart; }
   void SetNumRepeatTracks(uint32_t aNumRepeatTracks) {
     MOZ_ASSERT(mHasRepeatAuto || aNumRepeatTracks == 0);
     mRepeatAutoEnd = mRepeatAutoStart + aNumRepeatTracks;
     mRepeatEndDelta = mHasRepeatAuto ? int32_t(aNumRepeatTracks) - 1 : 0;
   }
 
   // Some style data references, for easy access.
-  const nsTArray<StyleTrackSize>& mSizingFunctions;
-  const StyleTrackSize& mAutoSizing;
+  const nsTArray<nsStyleCoord>& mMinSizingFunctions;
+  const nsTArray<nsStyleCoord>& mMaxSizingFunctions;
+  const nsStyleCoord& mAutoMinSizing;
+  const nsStyleCoord& mAutoMaxSizing;
   // Offset from the start of the implicit grid to the first explicit track.
   uint32_t mExplicitGridOffset;
   // The index of the repeat(auto-fill/fit) track, or zero if there is none.
   // Relative to mExplicitGridOffset (repeat tracks are explicit by definition).
   const uint32_t mRepeatAutoStart;
   // The (hypothetical) index of the last such repeat() track.
   uint32_t mRepeatAutoEnd;
-  // The difference between mExplicitGridEnd and mSizingFunctions.Length().
+  // The difference between mExplicitGridEnd and mMinSizingFunctions.Length().
   int32_t mRepeatEndDelta;
   // 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;
 };
 
@@ -1485,24 +1477,32 @@ struct MOZ_STACK_CLASS
       // 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();
-    mAutoSizing = isInlineAxis ? &pos->mGridAutoColumns : &pos->mGridAutoRows;
-    TrackSizingFunctions tsf(szf, *mAutoSizing);
+    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()) {
-      mSizingFunctions.AppendElement(tsf.SizingFor(i));
-    }
-  }
-  nsTArray<StyleTrackSize> mSizingFunctions;
-  const StyleTrackSize* mAutoSizing;
+      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 {
@@ -2481,19 +2481,21 @@ struct MOZ_STACK_CLASS nsGridContainerFr
   GridReflowInput(nsGridContainerFrame* aFrame, gfxContext& aRenderingContext,
                   const ReflowInput* aReflowInput,
                   const nsStylePosition* aGridStyle, const WritingMode& aWM)
       : mIter(aFrame, kPrincipalList),
         mGridStyle(aGridStyle),
         mCols(eLogicalAxisInline),
         mRows(eLogicalAxisBlock),
         mColFunctions(mGridStyle->GridTemplateColumns(),
-                      mGridStyle->mGridAutoColumns),
+                      mGridStyle->mGridAutoColumnsMin,
+                      mGridStyle->mGridAutoColumnsMax),
         mRowFunctions(mGridStyle->GridTemplateRows(),
-                      mGridStyle->mGridAutoRows),
+                      mGridStyle->mGridAutoRowsMin,
+                      mGridStyle->mGridAutoRowsMax),
         mReflowInput(aReflowInput),
         mRenderingContext(aRenderingContext),
         mFrame(aFrame),
         mSharedGridData(nullptr),
         mBorderPadding(aWM),
         mFragBStart(0),
         mStartRow(0),
         mNextFragmentStartRow(0),
@@ -3055,36 +3057,40 @@ void nsGridContainerFrame::GridReflowInp
     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->mSizingFunctions,
-                               *fallbackTrackSizing->mAutoSizing,
+          TrackSizingFunctions(fallbackTrackSizing->mMinSizingFunctions,
+                               fallbackTrackSizing->mMaxSizingFunctions,
+                               *fallbackTrackSizing->mAutoMinSizing,
+                               *fallbackTrackSizing->mAutoMaxSizing,
                                fallbackTrackSizing->mHasRepeatAuto,
                                fallbackTrackSizing->mRepeatAutoIndex),
           gapStyle, gridEnd, aContentBoxSize);
     }
   }
 
   // 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->mSizingFunctions,
-                                   *fallbackTrackSizing->mAutoSizing,
+            ? TrackSizingFunctions(fallbackTrackSizing->mMinSizingFunctions,
+                                   fallbackTrackSizing->mMaxSizingFunctions,
+                                   *fallbackTrackSizing->mAutoMinSizing,
+                                   *fallbackTrackSizing->mAutoMaxSizing,
                                    fallbackTrackSizing->mHasRepeatAuto,
                                    fallbackTrackSizing->mRepeatAutoIndex)
             : sizingFunctions,
         aContentBoxSize,
         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
@@ -4314,17 +4320,18 @@ void nsGridContainerFrame::Tracks::Initi
     const NonNegativeLengthPercentageOrNormal& aGridGap, uint32_t aNumTracks,
     nscoord aContentBoxSize) {
   MOZ_ASSERT(aNumTracks >=
              aFunctions.mExplicitGridOffset + aFunctions.NumExplicitTracks());
   mSizes.SetLength(aNumTracks);
   PodZero(mSizes.Elements(), mSizes.Length());
   for (uint32_t i = 0, len = mSizes.Length(); i < len; ++i) {
     mStateUnion |=
-        mSizes[i].Initialize(aContentBoxSize, aFunctions.SizingFor(i));
+        mSizes[i].Initialize(aContentBoxSize, aFunctions.MinSizingFor(i),
+                             aFunctions.MaxSizingFor(i));
   }
   mGridGap = nsLayoutUtils::ResolveGapToLength(aGridGap, aContentBoxSize);
   mContentBoxSize = aContentBoxSize;
 }
 
 /**
  * Reflow aChild in the given aAvailableSize.
  */
@@ -4794,19 +4801,19 @@ bool nsGridContainerFrame::Tracks::Resol
   gfxContext* rc = &aState.mRenderingContext;
   if (sz.mState & TrackSize::eAutoMinSizing) {
     nscoord s;
     // Check if we need to apply "Automatic Minimum Size" and cache it.
     if (aGridItem.ShouldApplyAutoMinSize(wm, mAxis, aPercentageBasis)) {
       aGridItem.mState[mAxis] |= ItemState::eApplyAutoMinSize;
       // Clamp it if it's spanning a definite track max-sizing function.
       if (TrackSize::IsDefiniteMaxSizing(sz.mState)) {
-        cache.mMinSizeClamp = aFunctions.MaxSizingFor(aRange.mStart)
-                                  .AsBreadth()
-                                  .Resolve(aPercentageBasis);
+        auto maxCoord = aFunctions.MaxSizingFor(aRange.mStart);
+        cache.mMinSizeClamp =
+            maxCoord.ComputeCoordPercentCalc(aPercentageBasis);
         aGridItem.mState[mAxis] |= ItemState::eClampMarginBoxMinSize;
       }
       if (aConstraint != SizingConstraint::MaxContent) {
         s = MinContentContribution(aGridItem, aState, rc, wm, mAxis, &cache);
       } else {
         s = MaxContentContribution(aGridItem, aState, rc, wm, mAxis, &cache);
       }
     } else {
@@ -4833,20 +4840,19 @@ bool nsGridContainerFrame::Tracks::Resol
     auto s = MaxContentContribution(aGridItem, aState, rc, wm, mAxis, &cache);
     if (sz.mLimit == NS_UNCONSTRAINEDSIZE) {
       sz.mLimit = s;
     } else {
       sz.mLimit = std::max(sz.mLimit, s);
     }
     if (MOZ_UNLIKELY(sz.mState & TrackSize::eFitContent)) {
       // Clamp mLimit to the fit-content() size, for ยง12.5.1.
-      nscoord fitContentClamp = aFunctions.SizingFor(aRange.mStart)
-                                    .AsFitContent()
-                                    .AsBreadth()
-                                    .Resolve(aPercentageBasis);
+      auto maxCoord = aFunctions.MaxSizingFor(aRange.mStart);
+      nscoord fitContentClamp =
+          maxCoord.ComputeCoordPercentCalc(aPercentageBasis);
       sz.mLimit = std::min(sz.mLimit, fitContentClamp);
     }
   }
   if (sz.mLimit < sz.mBase) {
     sz.mLimit = sz.mBase;
   }
   return sz.mState & TrackSize::eFlexMaxSizing;
 }
@@ -5282,18 +5288,18 @@ void nsGridContainerFrame::Tracks::Resol
         perSpanData[span].mItemCountWithSameSpan++;
         perSpanData[span].mStateBits |= state;
         CachedIntrinsicSizes cache;
         // Calculate data for "Automatic Minimum Size" clamping, if needed.
         if (TrackSize::IsDefiniteMaxSizing(state) &&
             (gridItem.mState[mAxis] & ItemState::eApplyAutoMinSize)) {
           nscoord minSizeClamp = 0;
           for (auto i : lineRange.Range()) {
-            minSizeClamp += aFunctions.MaxSizingFor(i).AsBreadth().Resolve(
-                aPercentageBasis);
+            auto maxCoord = aFunctions.MaxSizingFor(i);
+            minSizeClamp += maxCoord.ComputeCoordPercentCalc(aPercentageBasis);
           }
           minSizeClamp += mGridGap * (span - 1);
           cache.mMinSizeClamp = minSizeClamp;
           gridItem.mState[mAxis] |= ItemState::eClampMarginBoxMinSize;
         }
         // Collect the various grid item size contributions we need.
         nscoord minSize = 0;
         if (state & (TrackSize::eIntrinsicMinSizing |    // for 2.1
@@ -5422,17 +5428,17 @@ float nsGridContainerFrame::Tracks::Find
     const LineRange& aRange, const nsTArray<uint32_t>& aFlexTracks,
     const TrackSizingFunctions& aFunctions, nscoord aSpaceToFill) const {
   MOZ_ASSERT(aSpaceToFill > 0 && !aFlexTracks.IsEmpty());
   float flexFactorSum = 0.0f;
   nscoord leftOverSpace = aSpaceToFill;
   for (auto i : aRange.Range()) {
     const TrackSize& sz = mSizes[i];
     if (sz.mState & TrackSize::eFlexMaxSizing) {
-      flexFactorSum += aFunctions.MaxSizingFor(i).AsFr();
+      flexFactorSum += aFunctions.MaxSizingFor(i).GetFlexFractionValue();
     } else {
       leftOverSpace -= sz.mBase;
       if (leftOverSpace <= 0) {
         return 0.0f;
       }
     }
   }
   bool restart;
@@ -5442,17 +5448,17 @@ float nsGridContainerFrame::Tracks::Find
   do {
     restart = false;
     hypotheticalFrSize = leftOverSpace / std::max(flexFactorSum, 1.0f);
     for (uint32_t i = 0, len = flexTracks.Length(); i < len; ++i) {
       uint32_t track = flexTracks[i];
       if (track == kAutoLine) {
         continue;  // Track marked as inflexible in a prev. iter of this loop.
       }
-      float flexFactor = aFunctions.MaxSizingFor(track).AsFr();
+      float flexFactor = aFunctions.MaxSizingFor(track).GetFlexFractionValue();
       const nscoord base = mSizes[track].mBase;
       if (flexFactor * hypotheticalFrSize < base) {
         // 12.7.1.4: Treat this track as inflexible.
         flexTracks[i] = kAutoLine;
         flexFactorSum -= flexFactor;
         leftOverSpace -= base;
         --numFlexTracks;
         if (numFlexTracks == 0 || leftOverSpace <= 0) {
@@ -5476,17 +5482,17 @@ float nsGridContainerFrame::Tracks::Find
     return FindFrUnitSize(range, aFlexTracks, aFunctions, aAvailableSize);
   }
 
   // The used flex fraction is the maximum of:
   // ... each flexible track's base size divided by its flex factor (which is
   // floored at 1).
   float fr = 0.0f;
   for (uint32_t track : aFlexTracks) {
-    float flexFactor = aFunctions.MaxSizingFor(track).AsFr();
+    float flexFactor = aFunctions.MaxSizingFor(track).GetFlexFractionValue();
     float possiblyDividedBaseSize = (flexFactor > 1.0f)
                                         ? mSizes[track].mBase / flexFactor
                                         : mSizes[track].mBase;
     fr = std::max(fr, possiblyDividedBaseSize);
   }
   WritingMode wm = aState.mWM;
   gfxContext* rc = &aState.mRenderingContext;
   // ... the result of 'finding the size of an fr' for each item that spans
@@ -5551,17 +5557,17 @@ void nsGridContainerFrame::Tracks::Stret
                      aAvailableSize == NS_UNCONSTRAINEDSIZE;
   // We iterate twice at most.  The 2nd time if the grid size changed after
   // applying a min/max-size (can only occur if aAvailableSize is indefinite).
   while (true) {
     float fr = FindUsedFlexFraction(aState, aGridItems, flexTracks, aFunctions,
                                     aAvailableSize);
     if (fr != 0.0f) {
       for (uint32_t i : flexTracks) {
-        float flexFactor = aFunctions.MaxSizingFor(i).AsFr();
+        float flexFactor = aFunctions.MaxSizingFor(i).GetFlexFractionValue();
         nscoord flexLength = NSToCoordRound(flexFactor * fr);
         nscoord& base = mSizes[i].mBase;
         if (flexLength > base) {
           if (applyMinMax && origSizes.isNothing()) {
             origSizes.emplace(mSizes);
           }
           base = flexLength;
         }
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -38,16 +38,17 @@
 #include "nsString.h"
 #include "nsPrintfCString.h"
 #include "nsPresContext.h"
 #include "nsGkAtoms.h"
 #include "mozilla/dom/Document.h"
 #include "nsContentUtils.h"
 #include "nsCSSAnonBoxes.h"
 #include "nsStyleConsts.h"
+#include "nsStyleCoord.h"
 #include "nsStyleUtil.h"
 #include "nsTransform2D.h"
 #include "nsImageMap.h"
 #include "nsIIOService.h"
 #include "nsILoadGroup.h"
 #include "nsISupportsPriority.h"
 #include "nsNetUtil.h"
 #include "nsNetCID.h"
--- a/layout/style/GeckoBindings.cpp
+++ b/layout/style/GeckoBindings.cpp
@@ -1165,17 +1165,17 @@ void Gecko_CopyImageValueFrom(nsStyleIma
   MOZ_ASSERT(aImage);
   MOZ_ASSERT(aOther);
 
   *aImage = *aOther;
 }
 
 void Gecko_InitializeImageCropRect(nsStyleImage* aImage) {
   MOZ_ASSERT(aImage);
-  aImage->SetCropRect(MakeUnique<nsStyleImage::CropRect>());
+  aImage->SetCropRect(MakeUnique<nsStyleSides>());
 }
 
 void Gecko_SetCursorArrayLength(nsStyleUI* aStyleUI, size_t aLen) {
   aStyleUI->mCursorImages.Clear();
   aStyleUI->mCursorImages.SetLength(aLen);
 }
 
 void Gecko_SetCursorImageValue(nsCursorImage* aCursor,
@@ -1265,22 +1265,18 @@ void Gecko_ResizeAtomArray(nsTArray<RefP
 void Gecko_SetStyleGridTemplate(UniquePtr<nsStyleGridTemplate>* aGridTemplate,
                                 nsStyleGridTemplate* aValue) {
   aGridTemplate->reset(aValue);
 }
 
 nsStyleGridTemplate* Gecko_CreateStyleGridTemplate(uint32_t aTrackSizes,
                                                    uint32_t aNameSize) {
   nsStyleGridTemplate* result = new nsStyleGridTemplate;
-  result->mTrackSizingFunctions.SetCapacity(aTrackSizes);
-  auto auto_ = StyleTrackSize::Breadth(StyleTrackBreadth::Auto());
-  for (auto i : IntegerRange(aTrackSizes)) {
-    Unused << i;
-    result->mTrackSizingFunctions.AppendElement(auto_);
-  }
+  result->mMinTrackSizingFunctions.SetLength(aTrackSizes);
+  result->mMaxTrackSizingFunctions.SetLength(aTrackSizes);
   result->mLineNameLists.SetLength(aNameSize);
   return result;
 }
 
 void Gecko_CopyStyleGridTemplateValues(
     UniquePtr<nsStyleGridTemplate>* aGridTemplate,
     const nsStyleGridTemplate* aOther) {
   if (aOther) {
@@ -1468,16 +1464,33 @@ Keyframe* Gecko_GetOrCreateFinalKeyframe
 PropertyValuePair* Gecko_AppendPropertyValuePair(
     nsTArray<PropertyValuePair>* aProperties, nsCSSPropertyID aProperty) {
   MOZ_ASSERT(aProperties);
   MOZ_ASSERT(aProperty == eCSSPropertyExtra_variable ||
              !nsCSSProps::PropHasFlags(aProperty, CSSPropFlags::IsLogical));
   return aProperties->AppendElement(PropertyValuePair{aProperty});
 }
 
+void Gecko_ResetStyleCoord(nsStyleUnit* aUnit, nsStyleUnion* aValue) {
+  nsStyleCoord::Reset(*aUnit, *aValue);
+}
+
+void Gecko_SetStyleCoordCalcValue(nsStyleUnit* aUnit, nsStyleUnion* aValue,
+                                  nsStyleCoord::CalcValue aCalc) {
+  // Calc units should be cleaned up first
+  MOZ_ASSERT(*aUnit != nsStyleUnit::eStyleUnit_Calc);
+  nsStyleCoord::Calc* calcRef = new nsStyleCoord::Calc();
+  calcRef->mLength = aCalc.mLength;
+  calcRef->mPercent = aCalc.mPercent;
+  calcRef->mHasPercent = aCalc.mHasPercent;
+  *aUnit = nsStyleUnit::eStyleUnit_Calc;
+  aValue->mPointer = calcRef;
+  calcRef->AddRef();
+}
+
 void Gecko_CopyShapeSourceFrom(StyleShapeSource* aDst,
                                const StyleShapeSource* aSrc) {
   MOZ_ASSERT(aDst);
   MOZ_ASSERT(aSrc);
 
   *aDst = *aSrc;
 }
 
@@ -1587,16 +1600,18 @@ void Gecko_Element_DebugListAttributes(c
 
 void Gecko_Snapshot_DebugListAttributes(const ServoElementSnapshot* aSnapshot,
                                         nsCString* aOut) {
   DebugListAttributes(*aSnapshot, *aOut);
 }
 
 NS_IMPL_THREADSAFE_FFI_REFCOUNTING(URLExtraData, URLExtraData);
 
+NS_IMPL_THREADSAFE_FFI_REFCOUNTING(nsStyleCoord::Calc, Calc);
+
 void Gecko_nsStyleFont_SetLang(nsStyleFont* aFont, nsAtom* aAtom) {
   aFont->mLanguage = dont_AddRef(aAtom);
   aFont->mExplicitLanguage = true;
 }
 
 void Gecko_nsStyleFont_CopyLangFrom(nsStyleFont* aFont,
                                     const nsStyleFont* aSource) {
   aFont->mLanguage = aSource->mLanguage;
--- a/layout/style/GeckoBindings.h
+++ b/layout/style/GeckoBindings.h
@@ -499,16 +499,23 @@ mozilla::Keyframe* Gecko_GetOrCreateFina
     const nsTimingFunction* timingFunction);
 
 // Appends and returns a new PropertyValuePair to |aProperties| initialized with
 // its mProperty member set to |aProperty| and all other members initialized to
 // their default values.
 mozilla::PropertyValuePair* Gecko_AppendPropertyValuePair(
     nsTArray<mozilla::PropertyValuePair>*, nsCSSPropertyID aProperty);
 
+// Clean up pointer-based coordinates
+void Gecko_ResetStyleCoord(nsStyleUnit* unit, nsStyleUnion* value);
+
+// Set an nsStyleCoord to a computed `calc()` value
+void Gecko_SetStyleCoordCalcValue(nsStyleUnit* unit, nsStyleUnion* value,
+                                  nsStyleCoord::CalcValue calc);
+
 void Gecko_CopyShapeSourceFrom(mozilla::StyleShapeSource* dst,
                                const mozilla::StyleShapeSource* src);
 
 void Gecko_DestroyShapeSource(mozilla::StyleShapeSource* shape);
 
 void Gecko_NewShapeImage(mozilla::StyleShapeSource* shape);
 
 void Gecko_SetToSVGPath(
@@ -544,16 +551,18 @@ void Gecko_GetComputedImageURLSpec(const
                                    nsCString* spec);
 
 void Gecko_nsIURI_Debug(nsIURI*, nsCString* spec);
 
 NS_DECL_THREADSAFE_FFI_REFCOUNTING(mozilla::URLExtraData, URLExtraData);
 
 void Gecko_FillAllImageLayers(nsStyleImageLayers* layers, uint32_t max_len);
 
+NS_DECL_THREADSAFE_FFI_REFCOUNTING(nsStyleCoord::Calc, Calc);
+
 float Gecko_FontStretch_ToFloat(mozilla::FontStretch aStretch);
 
 void Gecko_FontStretch_SetFloat(mozilla::FontStretch* aStretch,
                                 float aFloatValue);
 
 void Gecko_LoadData_DeregisterLoad(const mozilla::StyleLoadData*);
 
 float Gecko_FontSlantStyle_ToFloat(mozilla::FontSlantStyle aStyle);
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -66,16 +66,17 @@ rusty-enums = [
     "mozilla::dom::CallerType",
     "mozilla::dom::IterationCompositeOperation",
     "mozilla::dom::CompositeOperation",
     "mozilla::InheritTarget",
     "mozilla::css::DocumentMatchingFunction",
     "mozilla::css::SheetParsingMode",
     "mozilla::StyleContentType",
     "nsStyleSVGOpacitySource",
+    "nsStyleUnit",
     "nsCSSKeyword",
     "mozilla::dom::Document_DocumentTheme",
     "mozilla::dom::Document_Type",
     "nsCSSUnit",
     "nsCSSFontDesc",
     "nsCSSPropertyID",
     "nsCSSCounterDesc",
     "nsMediaFeature_RangeType",
@@ -269,16 +270,17 @@ whitelist-types = [
     "nsSize",
     "nsStyleBackground",
     "nsStyleBorder",
     "nsStyleColor",
     "nsStyleColumn",
     "nsStyleContent",
     "nsStyleContentData",
     "ComputedStyle",
+    "nsStyleCoord",
     "nsStyleCounterData",
     "nsStyleDisplay",
     "nsStyleEffects",
     "nsStyleFilter",
     "nsStyleFont",
     "nsStyleGradient",
     "nsStyleGridTemplate",
     "nsStyleImage",
@@ -294,16 +296,17 @@ whitelist-types = [
     "nsStyleSVGPaint",
     "nsStyleSVGReset",
     "nsStyleTable",
     "nsStyleTableBorder",
     "nsStyleText",
     "nsStyleTextReset",
     "nsStyleUIReset",
     "nsStyleUnion",
+    "nsStyleUnit",
     "nsStyleUI",
     "nsStyleVisibility",
     "nsStyleXUL",
     "nsTArrayHeader",
     "Position",
     "PropertyValuePair",
     "Runnable",
     "ServoAttrSnapshot",
@@ -508,19 +511,16 @@ cbindgen-types = [
     { gecko = "StyleComputedUrl", servo = "gecko::url::ComputedUrl" },
     { gecko = "StyleComputedImageUrl", servo = "gecko::url::ComputedImageUrl" },
     { gecko = "StyleLoadData", servo = "gecko::url::LoadData" },
     { gecko = "StyleGenericFilter", servo = "values::generics::effects::Filter" },
     { gecko = "StyleGenericGradient", servo = "values::generics::image::Gradient" },
     { gecko = "StyleLineDirection", servo = "values::computed::image::LineDirection" },
     { gecko = "StyleGridTemplateAreas", servo = "values::computed::position::GridTemplateAreas" },
     { gecko = "StyleGenericGridLine", servo = "values::generics::grid::GridLine" },
-    { gecko = "StyleGenericTrackSize", servo = "values::generics::grid::TrackSize" },
-    { gecko = "StyleGenericTrackBreadth", servo = "values::generics::grid::TrackBreadth" },
-    { gecko = "StyleNumberOrPercentage", servo = "values::computed::NumberOrPercentage" },
 ]
 
 mapped-generic-types = [
     { generic = true, gecko = "mozilla::RustCell", servo = "::std::cell::Cell" },
     { generic = false, gecko = "ServoNodeData", servo = "AtomicRefCell<ElementData>" },
     { generic = false, gecko = "mozilla::ServoWritingMode", servo = "::logical_geometry::WritingMode" },
     { generic = false, gecko = "mozilla::ServoCustomPropertiesMap", servo = "Option<::servo_arc::Arc<::custom_properties::CustomPropertiesMap>>" },
     { generic = false, gecko = "mozilla::ServoRuleNode", servo = "Option<::rule_tree::StrongRuleNode>" },
--- a/layout/style/ServoStyleConstsInlines.h
+++ b/layout/style/ServoStyleConstsInlines.h
@@ -395,253 +395,11 @@ inline nsAtom* StyleGridLine::LineName()
   return ident.AsAtom();
 }
 
 template <>
 inline bool StyleGridLine::IsAuto() const {
   return LineName()->IsEmpty() && line_num == 0 && !is_span;
 }
 
-class WritingMode;
-
-// Logical axis, edge, side and corner constants for use in various places.
-enum LogicalAxis { eLogicalAxisBlock = 0x0, eLogicalAxisInline = 0x1 };
-enum LogicalEdge { eLogicalEdgeStart = 0x0, eLogicalEdgeEnd = 0x1 };
-enum LogicalSide : uint8_t {
-  eLogicalSideBStart = (eLogicalAxisBlock << 1) | eLogicalEdgeStart,   // 0x0
-  eLogicalSideBEnd = (eLogicalAxisBlock << 1) | eLogicalEdgeEnd,       // 0x1
-  eLogicalSideIStart = (eLogicalAxisInline << 1) | eLogicalEdgeStart,  // 0x2
-  eLogicalSideIEnd = (eLogicalAxisInline << 1) | eLogicalEdgeEnd       // 0x3
-};
-
-enum LogicalCorner {
-  eLogicalCornerBStartIStart = 0,
-  eLogicalCornerBStartIEnd = 1,
-  eLogicalCornerBEndIEnd = 2,
-  eLogicalCornerBEndIStart = 3
-};
-
-using LengthPercentage = StyleLengthPercentage;
-using LengthPercentageOrAuto = StyleLengthPercentageOrAuto;
-using NonNegativeLengthPercentage = StyleNonNegativeLengthPercentage;
-using NonNegativeLengthPercentageOrAuto =
-    StyleNonNegativeLengthPercentageOrAuto;
-using NonNegativeLengthPercentageOrNormal =
-    StyleNonNegativeLengthPercentageOrNormal;
-using Length = StyleLength;
-using LengthOrAuto = StyleLengthOrAuto;
-using NonNegativeLength = StyleNonNegativeLength;
-using NonNegativeLengthOrAuto = StyleNonNegativeLengthOrAuto;
-using BorderRadius = StyleBorderRadius;
-
-bool StyleCSSPixelLength::IsZero() const { return _0 == 0.0f; }
-
-nscoord StyleCSSPixelLength::ToAppUnits() const {
-  // We want to resolve the length part of the calc() expression rounding 0.5
-  // away from zero, instead of the default behavior of
-  // NSToCoordRound{,WithClamp} which do floor(x + 0.5).
-  //
-  // This is what the rust code in the app_units crate does, and not doing this
-  // would regress bug 1323735, for example.
-  //
-  // FIXME(emilio, bug 1528114): Probably we should do something smarter.
-  float length = _0 * float(mozilla::AppUnitsPerCSSPixel());
-  if (length >= nscoord_MAX) {
-    return nscoord_MAX;
-  }
-  if (length <= nscoord_MIN) {
-    return nscoord_MIN;
-  }
-  return NSToIntRound(length);
-}
-
-constexpr LengthPercentage LengthPercentage::Zero() {
-  return {{0.}, {0.}, StyleAllowedNumericType::All, false, false};
-}
-
-LengthPercentage LengthPercentage::FromPixels(CSSCoord aCoord) {
-  return {{aCoord}, {0.}, StyleAllowedNumericType::All, false, false};
-}
-
-LengthPercentage LengthPercentage::FromAppUnits(nscoord aCoord) {
-  return LengthPercentage::FromPixels(CSSPixel::FromAppUnits(aCoord));
-}
-
-LengthPercentage LengthPercentage::FromPercentage(float aPercentage) {
-  return {{0.}, {aPercentage}, StyleAllowedNumericType::All, true, false};
-}
-
-CSSCoord LengthPercentage::LengthInCSSPixels() const { return length._0; }
-
-float LengthPercentage::Percentage() const { return percentage._0; }
-
-bool LengthPercentage::HasPercent() const { return has_percentage; }
-
-bool LengthPercentage::ConvertsToLength() const { return !HasPercent(); }
-
-nscoord LengthPercentage::ToLength() const {
-  MOZ_ASSERT(ConvertsToLength());
-  return length.ToAppUnits();
-}
-
-bool LengthPercentage::ConvertsToPercentage() const {
-  return has_percentage && length.IsZero();
-}
-
-float LengthPercentage::ToPercentage() const {
-  MOZ_ASSERT(ConvertsToPercentage());
-  return Percentage();
-}
-
-bool LengthPercentage::HasLengthAndPercentage() const {
-  return !ConvertsToLength() && !ConvertsToPercentage();
-}
-
-bool LengthPercentage::IsDefinitelyZero() const {
-  return length.IsZero() && Percentage() == 0.0f;
-}
-
-CSSCoord LengthPercentage::ResolveToCSSPixels(CSSCoord aPercentageBasis) const {
-  return LengthInCSSPixels() + Percentage() * aPercentageBasis;
-}
-
-template <typename T>
-CSSCoord LengthPercentage::ResolveToCSSPixelsWith(T aPercentageGetter) const {
-  static_assert(std::is_same<decltype(aPercentageGetter()), CSSCoord>::value,
-                "Should return CSS pixels");
-  if (ConvertsToLength()) {
-    return LengthInCSSPixels();
-  }
-  return ResolveToCSSPixels(aPercentageGetter());
-}
-
-template <typename T, typename U>
-nscoord LengthPercentage::Resolve(T aPercentageGetter,
-                                  U aPercentageRounder) const {
-  static_assert(std::is_same<decltype(aPercentageGetter()), nscoord>::value,
-                "Should return app units");
-  static_assert(
-      std::is_same<decltype(aPercentageRounder(1.0f)), nscoord>::value,
-      "Should return app units");
-  if (ConvertsToLength()) {
-    return ToLength();
-  }
-  nscoord basis = aPercentageGetter();
-  return length.ToAppUnits() + aPercentageRounder(basis * Percentage());
-}
-
-nscoord LengthPercentage::Resolve(nscoord aPercentageBasis) const {
-  return Resolve([=] { return aPercentageBasis; }, NSToCoordFloorClamped);
-}
-
-template <typename T>
-nscoord LengthPercentage::Resolve(T aPercentageGetter) const {
-  static_assert(std::is_same<decltype(aPercentageGetter()), nscoord>::value,
-                "Should return app units");
-  return Resolve(aPercentageGetter, NSToCoordFloorClamped);
-}
-
-template <typename T>
-nscoord LengthPercentage::Resolve(nscoord aPercentageBasis,
-                                  T aPercentageRounder) const {
-  return Resolve([=] { return aPercentageBasis; }, aPercentageRounder);
-}
-
-#define IMPL_LENGTHPERCENTAGE_FORWARDS(ty_)                                 \
-  template <>                                                               \
-  inline bool ty_::HasPercent() const {                                     \
-    return IsLengthPercentage() && AsLengthPercentage().HasPercent();       \
-  }                                                                         \
-  template <>                                                               \
-  inline bool ty_::ConvertsToLength() const {                               \
-    return IsLengthPercentage() && AsLengthPercentage().ConvertsToLength(); \
-  }                                                                         \
-  template <>                                                               \
-  inline bool ty_::HasLengthAndPercentage() const {                         \
-    return IsLengthPercentage() &&                                          \
-           AsLengthPercentage().HasLengthAndPercentage();                   \
-  }                                                                         \
-  template <>                                                               \
-  inline nscoord ty_::ToLength() const {                                    \
-    MOZ_ASSERT(ConvertsToLength());                                         \
-    return AsLengthPercentage().ToLength();                                 \
-  }                                                                         \
-  template <>                                                               \
-  inline bool ty_::ConvertsToPercentage() const {                           \
-    return IsLengthPercentage() &&                                          \
-           AsLengthPercentage().ConvertsToPercentage();                     \
-  }                                                                         \
-  template <>                                                               \
-  inline float ty_::ToPercentage() const {                                  \
-    MOZ_ASSERT(ConvertsToPercentage());                                     \
-    return AsLengthPercentage().ToPercentage();                             \
-  }
-
-IMPL_LENGTHPERCENTAGE_FORWARDS(LengthPercentageOrAuto)
-IMPL_LENGTHPERCENTAGE_FORWARDS(StyleSize)
-IMPL_LENGTHPERCENTAGE_FORWARDS(StyleMaxSize)
-
-template <>
-inline bool LengthOrAuto::IsLength() const {
-  return IsLengthPercentage();
-}
-
-template <>
-inline const Length& LengthOrAuto::AsLength() const {
-  return AsLengthPercentage();
-}
-
-template <>
-inline bool StyleFlexBasis::IsAuto() const {
-  return IsSize() && AsSize().IsAuto();
-}
-
-template <>
-inline bool StyleSize::BehavesLikeInitialValueOnBlockAxis() const {
-  return IsAuto() || IsExtremumLength();
-}
-
-template <>
-inline bool StyleMaxSize::BehavesLikeInitialValueOnBlockAxis() const {
-  return IsNone() || IsExtremumLength();
-}
-
-template <>
-inline bool StyleBackgroundSize::IsInitialValue() const {
-  return IsExplicitSize() && explicit_size.width.IsAuto() &&
-         explicit_size.height.IsAuto();
-}
-
-template <typename T>
-const T& StyleRect<T>::Get(mozilla::Side aSide) const {
-  static_assert(sizeof(StyleRect<T>) == sizeof(T) * 4, "");
-  static_assert(alignof(StyleRect<T>) == alignof(T), "");
-  return reinterpret_cast<const T*>(this)[aSide];
-}
-
-template <typename T>
-template <typename Predicate>
-bool StyleRect<T>::All(Predicate aPredicate) const {
-  return aPredicate(_0) && aPredicate(_1) && aPredicate(_2) && aPredicate(_3);
-}
-
-template <typename T>
-template <typename Predicate>
-bool StyleRect<T>::Any(Predicate aPredicate) const {
-  return aPredicate(_0) || aPredicate(_1) || aPredicate(_2) || aPredicate(_3);
-}
-
-template <>
-inline const LengthPercentage& BorderRadius::Get(HalfCorner aCorner) const {
-  static_assert(sizeof(BorderRadius) == sizeof(LengthPercentage) * 8, "");
-  static_assert(alignof(BorderRadius) == alignof(LengthPercentage), "");
-  auto* self = reinterpret_cast<const LengthPercentage*>(this);
-  return self[aCorner];
-}
-
-template <>
-inline bool StyleTrackBreadth::HasPercent() const {
-  return IsBreadth() && AsBreadth().HasPercent();
-}
-
 }  // namespace mozilla
 
 #endif
--- a/layout/style/StyleAnimationValue.h
+++ b/layout/style/StyleAnimationValue.h
@@ -16,17 +16,17 @@
 #include "mozilla/ServoStyleConsts.h"  // Servo_AnimationValue_Dump
 #include "mozilla/DbgMacro.h"
 #include "nsStringFwd.h"
 #include "nsStringBuffer.h"
 #include "nsCoord.h"
 #include "nsColor.h"
 #include "nsCSSPropertyID.h"
 #include "nsCSSValue.h"
-#include "nsStyleConsts.h"
+#include "nsStyleCoord.h"
 #include "nsStyleTransformMatrix.h"
 
 class nsIFrame;
 class gfx3DMatrix;
 
 namespace mozilla {
 
 class ComputedStyle;
--- a/layout/style/moz.build
+++ b/layout/style/moz.build
@@ -55,16 +55,17 @@ EXPORTS += [
     'nsDOMCSSAttrDeclaration.h',
     'nsDOMCSSDeclaration.h',
     'nsICSSDeclaration.h',
     'nsICSSLoaderObserver.h',
     'nsLayoutStylesheetCache.h',
     'nsMediaFeatures.h',
     'nsStyleAutoArray.h',
     'nsStyleConsts.h',
+    'nsStyleCoord.h',
     'nsStyleStruct.h',
     'nsStyleStructFwd.h',
     'nsStyleStructInlines.h',
     'nsStyleStructList.h',
     'nsStyleTransformMatrix.h',
     'nsStyleUtil.h',
     'nsTimingFunction.h',
 ]
@@ -199,16 +200,17 @@ UNIFIED_SOURCES += [
     'nsFontFaceLoader.cpp',
     'nsFontFaceUtils.cpp',
     'nsHTMLCSSStyleSheet.cpp',
     'nsHTMLStyleSheet.cpp',
     'nsICSSDeclaration.cpp',
     'nsLayoutStylesheetCache.cpp',
     'nsMediaFeatures.cpp',
     'nsROCSSPrimitiveValue.cpp',
+    'nsStyleCoord.cpp',
     'nsStyleStruct.cpp',
     'nsStyleTransformMatrix.cpp',
     'nsStyleUtil.cpp',
     'nsTransitionManager.cpp',
     'PaintWorkletGlobalScope.cpp',
     'PaintWorkletImpl.cpp',
     'PostTraversalTask.cpp',
     'PreferenceSheet.cpp',
--- a/layout/style/nsCSSValue.h
+++ b/layout/style/nsCSSValue.h
@@ -6,32 +6,32 @@
 
 /* representation of simple property values within CSS declarations */
 
 #ifndef nsCSSValue_h___
 #define nsCSSValue_h___
 
 #include "mozilla/Attributes.h"
 #include "mozilla/CORSMode.h"
-#include "mozilla/EnumTypeTraits.h"
 #include "mozilla/FontPropertyTypes.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/ServoBindingTypes.h"
 #include "mozilla/URLExtraData.h"
 #include "mozilla/UniquePtr.h"
 
 #include "nsCSSKeywords.h"
 #include "nsCSSPropertyID.h"
 #include "nsCoord.h"
 #include "nsProxyRelease.h"
 #include "nsRefPtrHashtable.h"
 #include "nsString.h"
 #include "nsStringBuffer.h"
 #include "nsTArray.h"
 #include "nsStyleConsts.h"
+#include "nsStyleCoord.h"
 #include "gfxFontFamilyList.h"
 
 #include <type_traits>
 
 class imgRequestProxy;
 class nsAtom;
 class nsIContent;
 
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -41,16 +41,17 @@
 #include "nsCSSPseudoElements.h"
 #include "mozilla/EffectSet.h"
 #include "mozilla/IntegerRange.h"
 #include "mozilla/ServoStyleSet.h"
 #include "mozilla/RestyleManager.h"
 #include "imgIRequest.h"
 #include "nsLayoutUtils.h"
 #include "nsCSSKeywords.h"
+#include "nsStyleCoord.h"
 #include "nsDisplayList.h"
 #include "nsDOMCSSDeclaration.h"
 #include "nsStyleTransformMatrix.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/ElementInlines.h"
 #include "prtime.h"
 #include "nsWrapperCacheInlines.h"
 #include "mozilla/AppUnits.h"
@@ -1159,16 +1160,40 @@ already_AddRefed<CSSValue> nsComputedDOM
     return nullptr;
 
   RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
   val->SetIdent(nsCSSProps::ValueToKeywordEnum(
       StyleFont()->mFont.smoothing, nsCSSProps::kFontSmoothingKTable));
   return val.forget();
 }
 
+static void SetValueToCalc(const nsStyleCoord::CalcValue* aCalc,
+                           nsROCSSPrimitiveValue* aValue) {
+  RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
+  nsAutoString tmp, result;
+
+  result.AppendLiteral("calc(");
+
+  val->SetAppUnits(aCalc->mLength);
+  val->GetCssText(tmp);
+  result.Append(tmp);
+
+  if (aCalc->mHasPercent) {
+    result.AppendLiteral(" + ");
+
+    val->SetPercent(aCalc->mPercent);
+    val->GetCssText(tmp);
+    result.Append(tmp);
+  }
+
+  result.Append(')');
+
+  aValue->SetString(result);  // not really SetString
+}
+
 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetImageLayerPosition(
     const nsStyleImageLayers& aLayers) {
   if (aLayers.mPositionXCount != aLayers.mPositionYCount) {
     // No value to return.  We can't express this combination of
     // values as a shorthand.
     return nullptr;
   }
 
@@ -1268,112 +1293,77 @@ void nsComputedDOMStyle::AppendGridLineN
     }
     AppendGridLineNames(lineNamesString, aLineNames2);
   }
   lineNamesString.Append(']');
   val->SetString(lineNamesString);
   aValueList->AppendCSSValue(val.forget());
 }
 
-void nsComputedDOMStyle::SetValueToTrackBreadth(
-    nsROCSSPrimitiveValue* aValue,
-    const StyleTrackBreadth& aBreadth) {
-  using Tag = StyleTrackBreadth::Tag;
-  switch (aBreadth.tag) {
-    case Tag::MinContent:
-      return aValue->SetIdent(eCSSKeyword_min_content);
-    case Tag::MaxContent:
-      return aValue->SetIdent(eCSSKeyword_max_content);
-    case Tag::Auto:
-      return aValue->SetIdent(eCSSKeyword_auto);
-    case Tag::Breadth:
-      return SetValueToLengthPercentage(aValue, aBreadth.AsBreadth(), true);
-    case Tag::Fr: {
-      nsAutoString tmpStr;
-      nsStyleUtil::AppendCSSNumber(aBreadth.AsFr(), tmpStr);
-      tmpStr.AppendLiteral("fr");
-      return aValue->SetString(tmpStr);
-    }
-    default:
-      MOZ_ASSERT_UNREACHABLE("Unknown breadth value");
-      return;
-  }
-}
-
-already_AddRefed<nsROCSSPrimitiveValue> nsComputedDOMStyle::GetGridTrackBreadth(
-    const StyleTrackBreadth& aBreadth) {
-  RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
-  SetValueToTrackBreadth(val, aBreadth);
-  return val.forget();
-}
-
-already_AddRefed<nsROCSSPrimitiveValue> nsComputedDOMStyle::GetGridTrackSize(
-    const StyleTrackSize& aTrackSize) {
-  if (aTrackSize.IsFitContent()) {
+already_AddRefed<CSSValue> nsComputedDOMStyle::GetGridTrackSize(
+    const nsStyleCoord& aMinValue, const nsStyleCoord& aMaxValue) {
+  if (aMinValue.GetUnit() == eStyleUnit_None) {
     // A fit-content() function.
     RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
     nsAutoString argumentStr, fitContentStr;
     fitContentStr.AppendLiteral("fit-content(");
-    MOZ_ASSERT(aTrackSize.AsFitContent().IsBreadth(),
+    MOZ_ASSERT(aMaxValue.IsCoordPercentCalcUnit(),
                "unexpected unit for fit-content() argument value");
-    SetValueToLengthPercentage(val, aTrackSize.AsFitContent().AsBreadth(), true);
+    SetValueToCoord(val, aMaxValue, true);
     val->GetCssText(argumentStr);
     fitContentStr.Append(argumentStr);
     fitContentStr.Append(char16_t(')'));
     val->SetString(fitContentStr);
     return val.forget();
   }
 
-  if (aTrackSize.IsBreadth()) {
-    return GetGridTrackBreadth(aTrackSize.AsBreadth());
-  }
-
-  MOZ_ASSERT(aTrackSize.IsMinmax());
-  auto& min = aTrackSize.AsMinmax()._0;
-  auto& max = aTrackSize.AsMinmax()._1;
-  if (min == max) {
-    return GetGridTrackBreadth(min);
+  if (aMinValue == aMaxValue) {
+    RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
+    SetValueToCoord(val, aMinValue, true, nullptr,
+                    nsCSSProps::kGridTrackBreadthKTable);
+    return val.forget();
   }
 
   // minmax(auto, <flex>) is equivalent to (and is our internal representation
   // of) <flex>, and both compute to <flex>
-  if (min.IsAuto() && max.IsFr()) {
-    return GetGridTrackBreadth(max);
+  if (aMinValue.GetUnit() == eStyleUnit_Auto &&
+      aMaxValue.GetUnit() == eStyleUnit_FlexFraction) {
+    RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
+    SetValueToCoord(val, aMaxValue, true);
+    return val.forget();
   }
 
+  RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
   nsAutoString argumentStr, minmaxStr;
   minmaxStr.AppendLiteral("minmax(");
 
-  {
-    RefPtr<nsROCSSPrimitiveValue> argValue = GetGridTrackBreadth(min);
-    argValue->GetCssText(argumentStr);
-    minmaxStr.Append(argumentStr);
-    argumentStr.Truncate();
-  }
+  SetValueToCoord(val, aMinValue, true, nullptr,
+                  nsCSSProps::kGridTrackBreadthKTable);
+  val->GetCssText(argumentStr);
+  minmaxStr.Append(argumentStr);
 
   minmaxStr.AppendLiteral(", ");
 
-  {
-    RefPtr<nsROCSSPrimitiveValue> argValue = GetGridTrackBreadth(max);
-    argValue->GetCssText(argumentStr);
-    minmaxStr.Append(argumentStr);
-  }
+  SetValueToCoord(val, aMaxValue, true, nullptr,
+                  nsCSSProps::kGridTrackBreadthKTable);
+  val->GetCssText(argumentStr);
+  minmaxStr.Append(argumentStr);
 
   minmaxStr.Append(char16_t(')'));
-  RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
   val->SetString(minmaxStr);
   return val.forget();
 }
 
 already_AddRefed<CSSValue> nsComputedDOMStyle::GetGridTemplateColumnsRows(
     const nsStyleGridTemplate& aTrackList,
     const ComputedGridTrackInfo* aTrackInfo) {
   if (aTrackList.mIsSubgrid) {
     // XXX TODO: add support for repeat(auto-fill) for 'subgrid' (bug 1234311)
-    NS_ASSERTION(aTrackList.mTrackSizingFunctions.IsEmpty(),
+    NS_ASSERTION(aTrackList.mMinTrackSizingFunctions.IsEmpty() &&
+                     aTrackList.mMaxTrackSizingFunctions.IsEmpty(),
                  "Unexpected sizing functions with subgrid");
     RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
 
     RefPtr<nsROCSSPrimitiveValue> subgridKeyword = new nsROCSSPrimitiveValue;
     subgridKeyword->SetIdent(eCSSKeyword_subgrid);
     valueList->AppendCSSValue(subgridKeyword.forget());
 
     for (uint32_t i = 0, len = aTrackList.mLineNameLists.Length();; ++i) {
@@ -1394,17 +1384,19 @@ already_AddRefed<CSSValue> nsComputedDOM
         break;
       }
       AppendGridLineNames(valueList, aTrackList.mLineNameLists[i],
                           /*aSuppressEmptyList*/ false);
     }
     return valueList.forget();
   }
 
-  uint32_t numSizes = aTrackList.mTrackSizingFunctions.Length();
+  uint32_t numSizes = aTrackList.mMinTrackSizingFunctions.Length();
+  MOZ_ASSERT(aTrackList.mMaxTrackSizingFunctions.Length() == numSizes,
+             "Different number of min and max track sizing functions");
   if (aTrackInfo) {
     DebugOnly<bool> isAutoFill =
         aTrackList.HasRepeatAuto() && aTrackList.mIsAutoFill;
     DebugOnly<bool> isAutoFit =
         aTrackList.HasRepeatAuto() && !aTrackList.mIsAutoFill;
     DebugOnly<uint32_t> numExplicitTracks = aTrackInfo->mNumExplicitTracks;
     MOZ_ASSERT(numExplicitTracks == numSizes ||
                    (isAutoFill && numExplicitTracks >= numSizes) ||
@@ -1565,40 +1557,44 @@ already_AddRefed<CSSValue> nsComputedDOM
                              : NS_LITERAL_STRING("repeat(auto-fit,"));
         valueList->AppendCSSValue(start.forget());
         if (!aTrackList.mRepeatAutoLineNameListBefore.IsEmpty()) {
           AppendGridLineNames(valueList,
                               aTrackList.mRepeatAutoLineNameListBefore);
         }
 
         valueList->AppendCSSValue(
-            GetGridTrackSize(aTrackList.mTrackSizingFunctions[i]));
+            GetGridTrackSize(aTrackList.mMinTrackSizingFunctions[i],
+                             aTrackList.mMaxTrackSizingFunctions[i]));
         if (!aTrackList.mRepeatAutoLineNameListAfter.IsEmpty()) {
           AppendGridLineNames(valueList,
                               aTrackList.mRepeatAutoLineNameListAfter);
         }
         RefPtr<nsROCSSPrimitiveValue> end = new nsROCSSPrimitiveValue;
         end->SetString(NS_LITERAL_STRING(")"));
         valueList->AppendCSSValue(end.forget());
       } else {
         valueList->AppendCSSValue(
-            GetGridTrackSize(aTrackList.mTrackSizingFunctions[i]));
+            GetGridTrackSize(aTrackList.mMinTrackSizingFunctions[i],
+                             aTrackList.mMaxTrackSizingFunctions[i]));
       }
     }
   }
 
   return valueList.forget();
 }
 
 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetGridAutoColumns() {
-  return GetGridTrackSize(StylePosition()->mGridAutoColumns);
+  return GetGridTrackSize(StylePosition()->mGridAutoColumnsMin,
+                          StylePosition()->mGridAutoColumnsMax);
 }
 
 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetGridAutoRows() {
-  return GetGridTrackSize(StylePosition()->mGridAutoRows);
+  return GetGridTrackSize(StylePosition()->mGridAutoRowsMin,
+                          StylePosition()->mGridAutoRowsMax);
 }
 
 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetGridTemplateColumns() {
   const ComputedGridTrackInfo* info = nullptr;
 
   nsGridContainerFrame* gridFrame =
       nsGridContainerFrame::GetGridFrameWithComputedInfo(mInnerFrame);
 
@@ -1756,16 +1752,42 @@ already_AddRefed<CSSValue> nsComputedDOM
 
   val->SetIdent(
       nsCSSProps::ValueToKeywordEnum(StyleTextReset()->mTextDecorationStyle,
                                      nsCSSProps::kTextDecorationStyleKTable));
 
   return val.forget();
 }
 
+/* Border image properties */
+
+void nsComputedDOMStyle::AppendFourSideCoordValues(
+    nsDOMCSSValueList* aList, const nsStyleSides& aValues) {
+  const nsStyleCoord& top = aValues.Get(eSideTop);
+  const nsStyleCoord& right = aValues.Get(eSideRight);
+  const nsStyleCoord& bottom = aValues.Get(eSideBottom);
+  const nsStyleCoord& left = aValues.Get(eSideLeft);
+
+  auto appendValue = [this, aList](const nsStyleCoord& value) {
+    RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
+    SetValueToCoord(val, value, true);
+    aList->AppendCSSValue(val.forget());
+  };
+  appendValue(top);
+  if (top != right || top != bottom || top != left) {
+    appendValue(right);
+    if (top != bottom || right != left) {
+      appendValue(bottom);
+      if (right != left) {
+        appendValue(left);
+      }
+    }
+  }
+}
+
 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetHeight() {
   RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
 
   bool calcHeight = false;
 
   if (mInnerFrame) {
     calcHeight = true;
 
@@ -2212,16 +2234,107 @@ void nsComputedDOMStyle::SetValueToLengt
     val->SetPercent(aLength.Percentage());
     val->GetCssText(tmp);
     result.Append(tmp);
   }
   result.Append(')');
   aValue->SetString(result);
 }
 
+void nsComputedDOMStyle::SetValueToCoord(
+    nsROCSSPrimitiveValue* aValue, const nsStyleCoord& aCoord,
+    bool aClampNegativeCalc, PercentageBaseGetter aPercentageBaseGetter,
+    const KTableEntry aTable[]) {
+  MOZ_ASSERT(aValue, "Must have a value to work with");
+
+  switch (aCoord.GetUnit()) {
+    case eStyleUnit_Normal:
+      aValue->SetIdent(eCSSKeyword_normal);
+      break;
+
+    case eStyleUnit_Auto:
+      aValue->SetIdent(eCSSKeyword_auto);
+      break;
+
+    case eStyleUnit_Percent: {
+      nscoord percentageBase;
+      if (aPercentageBaseGetter &&
+          (this->*aPercentageBaseGetter)(percentageBase)) {
+        nscoord val =
+            NSCoordSaturatingMultiply(percentageBase, aCoord.GetPercentValue());
+        aValue->SetAppUnits(val);
+      } else {
+        aValue->SetPercent(aCoord.GetPercentValue());
+      }
+    } break;
+
+    case eStyleUnit_Factor:
+      aValue->SetNumber(aCoord.GetFactorValue());
+      break;
+
+    case eStyleUnit_Coord: {
+      nscoord val = aCoord.GetCoordValue();
+      aValue->SetAppUnits(val);
+    } break;
+
+    case eStyleUnit_Integer:
+      aValue->SetNumber(aCoord.GetIntValue());
+      break;
+
+    case eStyleUnit_Enumerated:
+      NS_ASSERTION(aTable, "Must have table to handle this case");
+      aValue->SetIdent(
+          nsCSSProps::ValueToKeywordEnum(aCoord.GetIntValue(), aTable));
+      break;
+
+    case eStyleUnit_None:
+      aValue->SetIdent(eCSSKeyword_none);
+      break;
+
+    case eStyleUnit_Calc:
+      nscoord percentageBase;
+      if (!aCoord.CalcHasPercent()) {
+        nscoord val = aCoord.ComputeCoordPercentCalc(0);
+        if (aClampNegativeCalc && val < 0) {
+          MOZ_ASSERT(aCoord.IsCalcUnit(), "parser should have rejected value");
+          val = 0;
+        }
+        aValue->SetAppUnits(val);
+      } else if (aPercentageBaseGetter &&
+                 (this->*aPercentageBaseGetter)(percentageBase)) {
+        nscoord val = aCoord.ComputeCoordPercentCalc(percentageBase);
+        if (aClampNegativeCalc && val < 0) {
+          MOZ_ASSERT(aCoord.IsCalcUnit(), "parser should have rejected value");
+          val = 0;
+        }
+        aValue->SetAppUnits(val);
+      } else {
+        nsStyleCoord::Calc* calc = aCoord.GetCalcValue();
+        SetValueToCalc(calc, aValue);
+      }
+      break;
+
+    case eStyleUnit_Degree:
+      aValue->SetDegree(aCoord.GetAngleValue());
+      break;
+
+    case eStyleUnit_FlexFraction: {
+      nsAutoString tmpStr;
+      nsStyleUtil::AppendCSSNumber(aCoord.GetFlexFractionValue(), tmpStr);
+      tmpStr.AppendLiteral("fr");
+      aValue->SetString(tmpStr);
+      break;
+    }
+
+    default:
+      NS_ERROR("Can't handle this unit");
+      break;
+  }
+}
+
 nscoord nsComputedDOMStyle::StyleCoordToNSCoord(
     const LengthPercentage& aCoord, PercentageBaseGetter aPercentageBaseGetter,
     nscoord aDefaultValue, bool aClampNegativeCalc) {
   MOZ_ASSERT(aPercentageBaseGetter, "Must have a percentage base getter");
   if (aCoord.ConvertsToLength()) {
     return aCoord.ToLength();
   }
   nscoord percentageBase;
@@ -2389,16 +2502,24 @@ already_AddRefed<CSSValue> nsComputedDOM
                                                        nsSize(0, 0));
 
   gfx::Matrix4x4 matrix = nsStyleTransformMatrix::ReadTransforms(
       aTransform, refBox, float(mozilla::AppUnitsPerCSSPixel()));
 
   return MatrixToCSSValue(matrix);
 }
 
+void nsComputedDOMStyle::SetCssTextToCoord(nsAString& aCssText,
+                                           const nsStyleCoord& aCoord,
+                                           bool aClampNegativeCalc) {
+  RefPtr<nsROCSSPrimitiveValue> value = new nsROCSSPrimitiveValue;
+  SetValueToCoord(value, aCoord, aClampNegativeCalc);
+  value->GetCssText(aCssText);
+}
+
 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMask() {
   const nsStyleSVGReset* svg = StyleSVGReset();
   const nsStyleImageLayers::Layer& firstLayer = svg->mMask.mLayers[0];
 
   // Mask is now a shorthand, but it used to be a longhand, so that we
   // need to support computed style for the cases where it used to be
   // a longhand.
   if (svg->mMask.mImageCount > 1 ||
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -35,18 +35,20 @@ struct ComputedGridTrackInfo;
 }  // namespace mozilla
 
 struct ComputedStyleMap;
 struct nsCSSKTableEntry;
 class nsIFrame;
 class nsDOMCSSValueList;
 struct nsMargin;
 class nsROCSSPrimitiveValue;
+class nsStyleCoord;
 class nsStyleGradient;
 struct nsStyleImage;
+class nsStyleSides;
 
 class nsComputedDOMStyle final : public nsDOMCSSDeclaration,
                                  public nsStubMutationObserver {
  private:
   // Convenience typedefs:
   using KTableEntry = nsCSSKTableEntry;
   using CSSValue = mozilla::dom::CSSValue;
   using StyleGeometryBox = mozilla::StyleGeometryBox;
@@ -194,19 +196,18 @@ class nsComputedDOMStyle final : public 
   // a value ("[]") is only appended if aSuppressEmptyList is false.
   void AppendGridLineNames(nsDOMCSSValueList* aValueList,
                            const nsTArray<RefPtr<nsAtom>>& aLineNames,
                            bool aSuppressEmptyList = true);
   // Appends aLineNames1/2 (if non-empty) as a CSSValue* to aValueList.
   void AppendGridLineNames(nsDOMCSSValueList* aValueList,
                            const nsTArray<RefPtr<nsAtom>>& aLineNames1,
                            const nsTArray<RefPtr<nsAtom>>& aLineNames2);
-  already_AddRefed<nsROCSSPrimitiveValue> GetGridTrackSize(const mozilla::StyleTrackSize&);
-  already_AddRefed<nsROCSSPrimitiveValue> GetGridTrackBreadth(const mozilla::StyleTrackBreadth&);
-  void SetValueToTrackBreadth(nsROCSSPrimitiveValue*, const mozilla::StyleTrackBreadth&);
+  already_AddRefed<CSSValue> GetGridTrackSize(const nsStyleCoord& aMinSize,
+                                              const nsStyleCoord& aMaxSize);
   already_AddRefed<CSSValue> GetGridTemplateColumnsRows(
       const nsStyleGridTemplate& aTrackList,
       const mozilla::ComputedGridTrackInfo* aTrackInfo);
 
   bool GetLineHeightCoord(nscoord& aCoord);
 
   bool ShouldHonorMinSizeAutoInAxis(mozilla::PhysicalAxis aAxis);
 
@@ -302,16 +303,35 @@ class nsComputedDOMStyle final : public 
                                   bool aClampNegativeCalc);
 
   void SetValueToMaxSize(nsROCSSPrimitiveValue* aValue, const StyleMaxSize&);
 
   void SetValueToExtremumLength(nsROCSSPrimitiveValue* aValue,
                                 StyleExtremumLength);
 
   /**
+   * Method to set aValue to aCoord.  If aCoord is a percentage value and
+   * aPercentageBaseGetter is not null, aPercentageBaseGetter is called.  If it
+   * returns true, the percentage base it outputs in its out param is used
+   * to compute an nscoord value.  If the getter is null or returns false,
+   * the percent value of aCoord is set as a percent value on aValue.  aTable,
+   * if not null, is the keyword table to handle eStyleUnit_Enumerated.  When
+   * calling SetAppUnits on aValue (for coord or percent values), the value
+   * passed in will be clamped to be no less than aMinAppUnits and no more than
+   * aMaxAppUnits.
+   *
+   * XXXbz should caller pass in some sort of bitfield indicating which units
+   * can be expected or something?
+   */
+  void SetValueToCoord(nsROCSSPrimitiveValue* aValue,
+                       const nsStyleCoord& aCoord, bool aClampNegativeCalc,
+                       PercentageBaseGetter aPercentageBaseGetter = nullptr,
+                       const KTableEntry aTable[] = nullptr);
+
+  /**
    * If aCoord is a eStyleUnit_Coord returns the nscoord.  If it's
    * eStyleUnit_Percent, attempts to resolve the percentage base and returns
    * the resulting nscoord.  If it's some other unit or a percentage base can't
    * be determined, returns aDefaultValue.
    */
   nscoord StyleCoordToNSCoord(const LengthPercentage& aCoord,
                               PercentageBaseGetter aPercentageBaseGetter,
                               nscoord aDefaultValue, bool aClampNegativeCalc);
@@ -322,25 +342,35 @@ class nsComputedDOMStyle final : public 
     if (aCoord.IsLengthPercentage()) {
       return StyleCoordToNSCoord(aCoord.AsLengthPercentage(),
                                  aPercentageBaseGetter, aDefaultValue,
                                  aClampNegativeCalc);
     }
     return aDefaultValue;
   }
 
+  /**
+   * Append coord values from four sides. It omits values when possible.
+   */
+  void AppendFourSideCoordValues(nsDOMCSSValueList* aList,
+                                 const nsStyleSides& aValues);
+
   bool GetCBContentWidth(nscoord& aWidth);
   bool GetCBContentHeight(nscoord& aHeight);
   bool GetCBPaddingRectWidth(nscoord& aWidth);
   bool GetCBPaddingRectHeight(nscoord& aHeight);
   bool GetScrollFrameContentWidth(nscoord& aWidth);
   bool GetScrollFrameContentHeight(nscoord& aHeight);
   bool GetFrameBorderRectWidth(nscoord& aWidth);
   bool GetFrameBorderRectHeight(nscoord& aHeight);
 
+  /* Helper functions for computing and serializing a nsStyleCoord. */
+  void SetCssTextToCoord(nsAString& aCssText, const nsStyleCoord& aCoord,
+                         bool aClampNegativeCalc);
+
   // Find out if we can safely skip flushing (i.e. pending restyles do not
   // affect mElement).
   bool NeedsToFlush() const;
 
   static ComputedStyleMap* GetComputedStyleMap();
 
   // We don't really have a good immutable representation of "presentation".
   // Given the way GetComputedStyle is currently used, we should just grab the
new file mode 100644
--- /dev/null
+++ b/layout/style/nsStyleCoord.cpp
@@ -0,0 +1,297 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* representation of length values in computed style data */
+
+#include "mozilla/HashFunctions.h"
+#include "mozilla/PodOperations.h"
+
+// nsStyleCoord.h must not be the first header in a unified source file,
+// otherwise it may not build with MSVC due to a bug in our STL wrapper.
+// See bug 1331102.
+#include "nsStyleCoord.h"
+
+using namespace mozilla;
+
+nsStyleCoord::nsStyleCoord(nsStyleUnit aUnit) : mUnit(aUnit) {
+  NS_ASSERTION(aUnit < eStyleUnit_Percent, "not a valueless unit");
+  if (aUnit >= eStyleUnit_Percent) {
+    mUnit = eStyleUnit_Null;
+  }
+  mValue.mInt = 0;
+}
+
+nsStyleCoord::nsStyleCoord(int32_t aValue, nsStyleUnit aUnit) : mUnit(aUnit) {
+  // if you want to pass in eStyleUnit_Coord, don't. instead, use the
+  // constructor just above this one... MMP
+  NS_ASSERTION(
+      (aUnit == eStyleUnit_Enumerated) || (aUnit == eStyleUnit_Integer),
+      "not an int value");
+  if ((aUnit == eStyleUnit_Enumerated) || (aUnit == eStyleUnit_Integer)) {
+    mValue.mInt = aValue;
+  } else {
+    mUnit = eStyleUnit_Null;
+    mValue.mInt = 0;
+  }
+}
+
+nsStyleCoord::nsStyleCoord(float aValue, nsStyleUnit aUnit) : mUnit(aUnit) {
+  if (aUnit < eStyleUnit_Percent || aUnit >= eStyleUnit_Coord) {
+    MOZ_ASSERT_UNREACHABLE("not a float value");
+    mUnit = eStyleUnit_Null;
+    mValue.mInt = 0;
+  } else {
+    mValue.mFloat = aValue;
+  }
+}
+
+bool nsStyleCoord::operator==(const nsStyleCoord& aOther) const {
+  if (mUnit != aOther.mUnit) {
+    return false;
+  }
+  switch (mUnit) {
+    case eStyleUnit_Null:
+    case eStyleUnit_Normal:
+    case eStyleUnit_Auto:
+    case eStyleUnit_None:
+      return true;
+    case eStyleUnit_Percent:
+    case eStyleUnit_Factor:
+    case eStyleUnit_Degree:
+    case eStyleUnit_FlexFraction:
+      return mValue.mFloat == aOther.mValue.mFloat;
+    case eStyleUnit_Coord:
+    case eStyleUnit_Integer:
+    case eStyleUnit_Enumerated:
+      return mValue.mInt == aOther.mValue.mInt;
+    case eStyleUnit_Calc:
+      return *this->GetCalcValue() == *aOther.GetCalcValue();
+  }
+  MOZ_ASSERT(false, "unexpected unit");
+  return false;
+}
+
+void nsStyleCoord::Reset() { Reset(mUnit, mValue); }
+
+void nsStyleCoord::SetCoordValue(nscoord aValue) {
+  Reset();
+  mUnit = eStyleUnit_Coord;
+  mValue.mInt = aValue;
+}
+
+void nsStyleCoord::SetIntValue(int32_t aValue, nsStyleUnit aUnit) {
+  NS_ASSERTION(
+      (aUnit == eStyleUnit_Enumerated) || (aUnit == eStyleUnit_Integer),
+      "not an int value");
+  Reset();
+  if ((aUnit == eStyleUnit_Enumerated) || (aUnit == eStyleUnit_Integer)) {
+    mUnit = aUnit;
+    mValue.mInt = aValue;
+  }
+}
+
+void nsStyleCoord::SetPercentValue(float aValue) {
+  Reset();
+  mUnit = eStyleUnit_Percent;
+  mValue.mFloat = aValue;
+}
+
+void nsStyleCoord::SetFactorValue(float aValue) {
+  Reset();
+  mUnit = eStyleUnit_Factor;
+  mValue.mFloat = aValue;
+}
+
+void nsStyleCoord::SetFlexFractionValue(float aValue) {
+  Reset();
+  mUnit = eStyleUnit_FlexFraction;
+  mValue.mFloat = aValue;
+}
+
+void nsStyleCoord::SetCalcValue(Calc* aValue) {
+  Reset();
+  mUnit = eStyleUnit_Calc;
+  mValue.mPointer = aValue;
+  aValue->AddRef();
+}
+
+void nsStyleCoord::SetNormalValue() {
+  Reset();
+  mUnit = eStyleUnit_Normal;
+  mValue.mInt = 0;
+}
+
+void nsStyleCoord::SetAutoValue() {
+  Reset();
+  mUnit = eStyleUnit_Auto;
+  mValue.mInt = 0;
+}
+
+void nsStyleCoord::SetNoneValue() {
+  Reset();
+  mUnit = eStyleUnit_None;
+  mValue.mInt = 0;
+}
+
+// accessors that are not inlined
+
+double nsStyleCoord::GetAngleValueInDegrees() const {
+  // Note that this extends the value from float to double.
+  return GetAngleValue();
+}
+
+double nsStyleCoord::GetAngleValueInRadians() const {
+  return GetAngleValueInDegrees() * M_PI / 180.0;
+}
+
+nscoord nsStyleCoord::ComputeComputedCalc(nscoord aPercentageBasis) const {
+  Calc* calc = GetCalcValue();
+  return calc->mLength +
+         NSToCoordFloorClamped(aPercentageBasis * calc->mPercent);
+}
+
+nscoord nsStyleCoord::ComputeCoordPercentCalc(nscoord aPercentageBasis) const {
+  switch (GetUnit()) {
+    case eStyleUnit_Coord:
+      return GetCoordValue();
+    case eStyleUnit_Percent:
+      return NSToCoordFloorClamped(aPercentageBasis * GetPercentValue());
+    case eStyleUnit_Calc:
+      return ComputeComputedCalc(aPercentageBasis);
+    default:
+      MOZ_ASSERT_UNREACHABLE("Unexpected unit!");
+      return 0;
+  }
+}
+
+nsStyleSides::nsStyleSides() {
+  NS_FOR_CSS_SIDES(i) { mUnits[i] = eStyleUnit_Null; }
+  mozilla::PodArrayZero(mValues);
+}
+
+nsStyleSides::nsStyleSides(const nsStyleSides& aOther) {
+  NS_FOR_CSS_SIDES(i) { mUnits[i] = eStyleUnit_Null; }
+  *this = aOther;
+}
+
+nsStyleSides::~nsStyleSides() { Reset(); }
+
+nsStyleSides& nsStyleSides::operator=(const nsStyleSides& aCopy) {
+  if (this != &aCopy) {
+    NS_FOR_CSS_SIDES(i) {
+      nsStyleCoord::SetValue(mUnits[i], mValues[i], aCopy.mUnits[i],
+                             aCopy.mValues[i]);
+    }
+  }
+  return *this;
+}
+
+bool nsStyleSides::operator==(const nsStyleSides& aOther) const {
+  NS_FOR_CSS_SIDES(i) {
+    if (nsStyleCoord(mValues[i], (nsStyleUnit)mUnits[i]) !=
+        nsStyleCoord(aOther.mValues[i], (nsStyleUnit)aOther.mUnits[i])) {
+      return false;
+    }
+  }
+  return true;
+}
+
+void nsStyleSides::Reset() {
+  NS_FOR_CSS_SIDES(i) { nsStyleCoord::Reset(mUnits[i], mValues[i]); }
+}
+
+// Validation of SideIsVertical.
+#define CASE(side, result) \
+  static_assert(SideIsVertical(side) == result, "SideIsVertical is wrong")
+CASE(eSideTop, false);
+CASE(eSideRight, true);
+CASE(eSideBottom, false);
+CASE(eSideLeft, true);
+#undef CASE
+
+// Validation of HalfCornerIsX.
+#define CASE(corner, result) \
+  static_assert(HalfCornerIsX(corner) == result, "HalfCornerIsX is wrong")
+CASE(eCornerTopLeftX, true);
+CASE(eCornerTopLeftY, false);
+CASE(eCornerTopRightX, true);
+CASE(eCornerTopRightY, false);
+CASE(eCornerBottomRightX, true);
+CASE(eCornerBottomRightY, false);
+CASE(eCornerBottomLeftX, true);
+CASE(eCornerBottomLeftY, false);
+#undef CASE
+
+// Validation of HalfToFullCorner.
+#define CASE(corner, result)                        \
+  static_assert(HalfToFullCorner(corner) == result, \
+                "HalfToFullCorner is "              \
+                "wrong")
+CASE(eCornerTopLeftX, eCornerTopLeft);
+CASE(eCornerTopLeftY, eCornerTopLeft);
+CASE(eCornerTopRightX, eCornerTopRight);
+CASE(eCornerTopRightY, eCornerTopRight);
+CASE(eCornerBottomRightX, eCornerBottomRight);
+CASE(eCornerBottomRightY, eCornerBottomRight);
+CASE(eCornerBottomLeftX, eCornerBottomLeft);
+CASE(eCornerBottomLeftY, eCornerBottomLeft);
+#undef CASE
+
+// Validation of FullToHalfCorner.
+#define CASE(corner, vert, result)                        \
+  static_assert(FullToHalfCorner(corner, vert) == result, \
+                "FullToHalfCorner is wrong")
+CASE(eCornerTopLeft, false, eCornerTopLeftX);
+CASE(eCornerTopLeft, true, eCornerTopLeftY);
+CASE(eCornerTopRight, false, eCornerTopRightX);
+CASE(eCornerTopRight, true, eCornerTopRightY);
+CASE(eCornerBottomRight, false, eCornerBottomRightX);
+CASE(eCornerBottomRight, true, eCornerBottomRightY);
+CASE(eCornerBottomLeft, false, eCornerBottomLeftX);
+CASE(eCornerBottomLeft, true, eCornerBottomLeftY);
+#undef CASE
+
+// Validation of SideToFullCorner.
+#define CASE(side, second, result)                        \
+  static_assert(SideToFullCorner(side, second) == result, \
+                "SideToFullCorner is wrong")
+CASE(eSideTop, false, eCornerTopLeft);
+CASE(eSideTop, true, eCornerTopRight);
+
+CASE(eSideRight, false, eCornerTopRight);
+CASE(eSideRight, true, eCornerBottomRight);
+
+CASE(eSideBottom, false, eCornerBottomRight);
+CASE(eSideBottom, true, eCornerBottomLeft);
+
+CASE(eSideLeft, false, eCornerBottomLeft);
+CASE(eSideLeft, true, eCornerTopLeft);
+#undef CASE
+
+// Validation of SideToHalfCorner.
+#define CASE(side, second, parallel, result)                        \
+  static_assert(SideToHalfCorner(side, second, parallel) == result, \
+                "SideToHalfCorner is wrong")
+CASE(eSideTop, false, true, eCornerTopLeftX);
+CASE(eSideTop, false, false, eCornerTopLeftY);
+CASE(eSideTop, true, true, eCornerTopRightX);
+CASE(eSideTop, true, false, eCornerTopRightY);
+
+CASE(eSideRight, false, false, eCornerTopRightX);
+CASE(eSideRight, false, true, eCornerTopRightY);
+CASE(eSideRight, true, false, eCornerBottomRightX);
+CASE(eSideRight, true, true, eCornerBottomRightY);
+
+CASE(eSideBottom, false, true, eCornerBottomRightX);
+CASE(eSideBottom, false, false, eCornerBottomRightY);
+CASE(eSideBottom, true, true, eCornerBottomLeftX);
+CASE(eSideBottom, true, false, eCornerBottomLeftY);
+
+CASE(eSideLeft, false, false, eCornerBottomLeftX);
+CASE(eSideLeft, false, true, eCornerBottomLeftY);
+CASE(eSideLeft, true, false, eCornerTopLeftX);
+CASE(eSideLeft, true, true, eCornerTopLeftY);
+#undef CASE
new file mode 100644
--- /dev/null
+++ b/layout/style/nsStyleCoord.h
@@ -0,0 +1,800 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* representation of length values in computed style data */
+
+#ifndef nsStyleCoord_h___
+#define nsStyleCoord_h___
+
+#include <type_traits>
+
+#include "mozilla/EnumTypeTraits.h"
+#include "mozilla/gfx/Types.h"
+#include "Units.h"
+#include "nsCoord.h"
+#include "nsISupportsImpl.h"
+#include "nsStyleConsts.h"
+
+namespace mozilla {
+
+class WritingMode;
+
+// Logical axis, edge, side and corner constants for use in various places.
+enum LogicalAxis { eLogicalAxisBlock = 0x0, eLogicalAxisInline = 0x1 };
+enum LogicalEdge { eLogicalEdgeStart = 0x0, eLogicalEdgeEnd = 0x1 };
+enum LogicalSide : uint8_t {
+  eLogicalSideBStart = (eLogicalAxisBlock << 1) | eLogicalEdgeStart,   // 0x0
+  eLogicalSideBEnd = (eLogicalAxisBlock << 1) | eLogicalEdgeEnd,       // 0x1
+  eLogicalSideIStart = (eLogicalAxisInline << 1) | eLogicalEdgeStart,  // 0x2
+  eLogicalSideIEnd = (eLogicalAxisInline << 1) | eLogicalEdgeEnd       // 0x3
+};
+
+enum LogicalCorner {
+  eLogicalCornerBStartIStart = 0,
+  eLogicalCornerBStartIEnd = 1,
+  eLogicalCornerBEndIEnd = 2,
+  eLogicalCornerBEndIStart = 3
+};
+
+using LengthPercentage = StyleLengthPercentage;
+using LengthPercentageOrAuto = StyleLengthPercentageOrAuto;
+using NonNegativeLengthPercentage = StyleNonNegativeLengthPercentage;
+using NonNegativeLengthPercentageOrAuto =
+    StyleNonNegativeLengthPercentageOrAuto;
+using NonNegativeLengthPercentageOrNormal =
+    StyleNonNegativeLengthPercentageOrNormal;
+using Length = StyleLength;
+using LengthOrAuto = StyleLengthOrAuto;
+using NonNegativeLength = StyleNonNegativeLength;
+using NonNegativeLengthOrAuto = StyleNonNegativeLengthOrAuto;
+using BorderRadius = StyleBorderRadius;
+
+bool StyleCSSPixelLength::IsZero() const { return _0 == 0.0f; }
+
+nscoord StyleCSSPixelLength::ToAppUnits() const {
+  // We want to resolve the length part of the calc() expression rounding 0.5
+  // away from zero, instead of the default behavior of
+  // NSToCoordRound{,WithClamp} which do floor(x + 0.5).
+  //
+  // This is what the rust code in the app_units crate does, and not doing this
+  // would regress bug 1323735, for example.
+  //
+  // FIXME(emilio, bug 1528114): Probably we should do something smarter.
+  float length = _0 * float(mozilla::AppUnitsPerCSSPixel());
+  if (length >= nscoord_MAX) {
+    return nscoord_MAX;
+  }
+  if (length <= nscoord_MIN) {
+    return nscoord_MIN;
+  }
+  return NSToIntRound(length);
+}
+
+constexpr LengthPercentage LengthPercentage::Zero() {
+  return {{0.}, {0.}, StyleAllowedNumericType::All, false, false};
+}
+
+LengthPercentage LengthPercentage::FromPixels(CSSCoord aCoord) {
+  return {{aCoord}, {0.}, StyleAllowedNumericType::All, false, false};
+}
+
+LengthPercentage LengthPercentage::FromAppUnits(nscoord aCoord) {
+  return LengthPercentage::FromPixels(CSSPixel::FromAppUnits(aCoord));
+}
+
+LengthPercentage LengthPercentage::FromPercentage(float aPercentage) {
+  return {{0.}, {aPercentage}, StyleAllowedNumericType::All, true, false};
+}
+
+CSSCoord LengthPercentage::LengthInCSSPixels() const { return length._0; }
+
+float LengthPercentage::Percentage() const { return percentage._0; }
+
+bool LengthPercentage::HasPercent() const { return has_percentage; }
+
+bool LengthPercentage::ConvertsToLength() const { return !HasPercent(); }
+
+nscoord LengthPercentage::ToLength() const {
+  MOZ_ASSERT(ConvertsToLength());
+  return length.ToAppUnits();
+}
+
+bool LengthPercentage::ConvertsToPercentage() const {
+  return has_percentage && length.IsZero();
+}
+
+float LengthPercentage::ToPercentage() const {
+  MOZ_ASSERT(ConvertsToPercentage());
+  return Percentage();
+}
+
+bool LengthPercentage::HasLengthAndPercentage() const {
+  return !ConvertsToLength() && !ConvertsToPercentage();
+}
+
+bool LengthPercentage::IsDefinitelyZero() const {
+  return length.IsZero() && Percentage() == 0.0f;
+}
+
+CSSCoord LengthPercentage::ResolveToCSSPixels(CSSCoord aPercentageBasis) const {
+  return LengthInCSSPixels() + Percentage() * aPercentageBasis;
+}
+
+template <typename T>
+CSSCoord LengthPercentage::ResolveToCSSPixelsWith(T aPercentageGetter) const {
+  static_assert(std::is_same<decltype(aPercentageGetter()), CSSCoord>::value,
+                "Should return CSS pixels");
+  if (ConvertsToLength()) {
+    return LengthInCSSPixels();
+  }
+  return ResolveToCSSPixels(aPercentageGetter());
+}
+
+template <typename T, typename U>
+nscoord LengthPercentage::Resolve(T aPercentageGetter,
+                                  U aPercentageRounder) const {
+  static_assert(std::is_same<decltype(aPercentageGetter()), nscoord>::value,
+                "Should return app units");
+  static_assert(
+      std::is_same<decltype(aPercentageRounder(1.0f)), nscoord>::value,
+      "Should return app units");
+  if (ConvertsToLength()) {
+    return ToLength();
+  }
+  nscoord basis = aPercentageGetter();
+  return length.ToAppUnits() + aPercentageRounder(basis * Percentage());
+}
+
+nscoord LengthPercentage::Resolve(nscoord aPercentageBasis) const {
+  return Resolve([=] { return aPercentageBasis; }, NSToCoordFloorClamped);
+}
+
+template <typename T>
+nscoord LengthPercentage::Resolve(T aPercentageGetter) const {
+  static_assert(std::is_same<decltype(aPercentageGetter()), nscoord>::value,
+                "Should return app units");
+  return Resolve(aPercentageGetter, NSToCoordFloorClamped);
+}
+
+template <typename T>
+nscoord LengthPercentage::Resolve(nscoord aPercentageBasis,
+                                  T aPercentageRounder) const {
+  return Resolve([=] { return aPercentageBasis; }, aPercentageRounder);
+}
+
+#define IMPL_LENGTHPERCENTAGE_FORWARDS(ty_)                                 \
+  template <>                                                               \
+  inline bool ty_::HasPercent() const {                                     \
+    return IsLengthPercentage() && AsLengthPercentage().HasPercent();       \
+  }                                                                         \
+  template <>                                                               \
+  inline bool ty_::ConvertsToLength() const {                               \
+    return IsLengthPercentage() && AsLengthPercentage().ConvertsToLength(); \
+  }                                                                         \
+  template <>                                                               \
+  inline bool ty_::HasLengthAndPercentage() const {                         \
+    return IsLengthPercentage() &&                                          \
+           AsLengthPercentage().HasLengthAndPercentage();                   \
+  }                                                                         \
+  template <>                                                               \
+  inline nscoord ty_::ToLength() const {                                    \
+    MOZ_ASSERT(ConvertsToLength());                                         \
+    return AsLengthPercentage().ToLength();                                 \
+  }                                                                         \
+  template <>                                                               \
+  inline bool ty_::ConvertsToPercentage() const {                           \
+    return IsLengthPercentage() &&                                          \
+           AsLengthPercentage().ConvertsToPercentage();                     \
+  }                                                                         \
+  template <>                                                               \
+  inline float ty_::ToPercentage() const {                                  \
+    MOZ_ASSERT(ConvertsToPercentage());                                     \
+    return AsLengthPercentage().ToPercentage();                             \
+  }
+
+IMPL_LENGTHPERCENTAGE_FORWARDS(LengthPercentageOrAuto)
+IMPL_LENGTHPERCENTAGE_FORWARDS(StyleSize)
+IMPL_LENGTHPERCENTAGE_FORWARDS(StyleMaxSize)
+
+template <>
+inline bool LengthOrAuto::IsLength() const {
+  return IsLengthPercentage();
+}
+
+template <>
+inline const Length& LengthOrAuto::AsLength() const {
+  return AsLengthPercentage();
+}
+
+template <>
+inline bool StyleFlexBasis::IsAuto() const {
+  return IsSize() && AsSize().IsAuto();
+}
+
+template <>
+inline bool StyleSize::BehavesLikeInitialValueOnBlockAxis() const {
+  return IsAuto() || IsExtremumLength();
+}
+
+template <>
+inline bool StyleMaxSize::BehavesLikeInitialValueOnBlockAxis() const {
+  return IsNone() || IsExtremumLength();
+}
+
+template <>
+inline bool StyleBackgroundSize::IsInitialValue() const {
+  return IsExplicitSize() && explicit_size.width.IsAuto() &&
+         explicit_size.height.IsAuto();
+}
+
+template <typename T>
+const T& StyleRect<T>::Get(mozilla::Side aSide) const {
+  static_assert(sizeof(StyleRect<T>) == sizeof(T) * 4, "");
+  static_assert(alignof(StyleRect<T>) == alignof(T), "");
+  return reinterpret_cast<const T*>(this)[aSide];
+}
+
+template <typename T>
+template <typename Predicate>
+bool StyleRect<T>::All(Predicate aPredicate) const {
+  return aPredicate(_0) && aPredicate(_1) && aPredicate(_2) && aPredicate(_3);
+}
+
+template <typename T>
+template <typename Predicate>
+bool StyleRect<T>::Any(Predicate aPredicate) const {
+  return aPredicate(_0) || aPredicate(_1) || aPredicate(_2) || aPredicate(_3);
+}
+
+template <>
+inline const LengthPercentage& BorderRadius::Get(HalfCorner aCorner) const {
+  static_assert(sizeof(BorderRadius) == sizeof(LengthPercentage) * 8, "");
+  static_assert(alignof(BorderRadius) == alignof(LengthPercentage), "");
+  auto* self = reinterpret_cast<const LengthPercentage*>(this);
+  return self[aCorner];
+}
+
+}  // namespace mozilla
+
+enum nsStyleUnit : uint8_t {
+  eStyleUnit_Null = 0,           // (no value) value is not specified
+  eStyleUnit_Normal = 1,         // (no value)
+  eStyleUnit_Auto = 2,           // (no value)
+  eStyleUnit_None = 3,           // (no value)
+  eStyleUnit_Percent = 10,       // (float) 1.0 == 100%
+  eStyleUnit_Factor = 11,        // (float) a multiplier
+  eStyleUnit_Degree = 12,        // (float) angle in degrees
+  eStyleUnit_FlexFraction = 16,  // (float) <flex> in fr units
+  eStyleUnit_Coord = 20,         // (nscoord) value is twips
+  eStyleUnit_Integer = 30,       // (int) value is simple integer
+  eStyleUnit_Enumerated = 32,    // (int) value has enumerated meaning
+
+  // The following are reference counted allocated types.
+  eStyleUnit_Calc = 40,  // (Calc*) calc() toplevel; always present
+                         // to distinguish 50% from calc(50%), etc.
+
+  eStyleUnit_MAX = 40  // highest valid nsStyleUnit value
+};
+
+typedef union {
+  int32_t mInt;  // nscoord is a int32_t for now
+  float mFloat;
+  // An mPointer is a reference counted pointer.  Currently this can only
+  // ever be an nsStyleCoord::Calc*.
+  void* mPointer;
+} nsStyleUnion;
+
+/**
+ * Class that hold a single size specification used by the style
+ * system.  The size specification consists of two parts -- a number
+ * and a unit.  The number is an integer, a floating point value, an
+ * nscoord, or undefined, and the unit is an nsStyleUnit.  Checking
+ * the unit is a must before asking for the value in any particular
+ * form.
+ */
+/** <div rustbindgen private accessor="unsafe"></div> */
+class nsStyleCoord {
+ public:
+  // Non-reference counted calc() value.  See nsStyleStruct.h for some uses
+  // of this.
+  struct CalcValue {
+    // Every calc() expression evaluates to a length plus a percentage.
+    nscoord mLength;
+    float mPercent;
+    bool mHasPercent;  // whether there was any % syntax, even if 0
+
+    bool operator==(const CalcValue& aOther) const {
+      return mLength == aOther.mLength && mPercent == aOther.mPercent &&
+             mHasPercent == aOther.mHasPercent;
+    }
+    bool operator!=(const CalcValue& aOther) const {
+      return !(*this == aOther);
+    }
+
+    nscoord ToLength() const {
+      MOZ_ASSERT(!mHasPercent);
+      return mLength;
+    }
+
+    // If this returns true the value is definitely zero. It it returns false
+    // it might be zero. So it's best used for conservative optimization.
+    bool IsDefinitelyZero() const { return mLength == 0 && mPercent == 0; }
+  };
+
+  // Reference counted calc() value.  This is the type that is used to store
+  // the calc() value in nsStyleCoord.
+  struct Calc final : public CalcValue {
+    NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Calc)
+    Calc() {}
+
+   private:
+    Calc(const Calc&) = delete;
+    ~Calc() {}
+    Calc& operator=(const Calc&) = delete;
+  };
+
+  explicit nsStyleCoord(nsStyleUnit aUnit = eStyleUnit_Null);
+  enum CoordConstructorType { CoordConstructor };
+  inline nsStyleCoord(nscoord aValue, CoordConstructorType);
+  nsStyleCoord(int32_t aValue, nsStyleUnit aUnit);
+  nsStyleCoord(float aValue, nsStyleUnit aUnit);
+  inline nsStyleCoord(const nsStyleCoord& aCopy);
+  inline nsStyleCoord(const nsStyleUnion& aValue, nsStyleUnit aUnit);
+  ~nsStyleCoord() { Reset(); }
+
+  nsStyleCoord& operator=(const nsStyleCoord& aOther) {
+    if (this != &aOther) {
+      SetValue(mUnit, mValue, aOther);
+    }
+    return *this;
+  }
+  bool operator==(const nsStyleCoord& aOther) const;
+  bool operator!=(const nsStyleCoord& aOther) const;
+
+  nsStyleUnit GetUnit() const {
+    NS_ASSERTION(mUnit != eStyleUnit_Null, "reading uninitialized value");
+    return mUnit;
+  }
+
+  // This is especially useful to check if it is the property's initial value
+  // or keyword for sizing properties.
+  bool IsAutoOrEnum() const {
+    // The initial value of width/height and min-width/min-height is `auto`.
+    // The initial value of max-width/max-height is `none`.
+    return mUnit == eStyleUnit_Auto || mUnit == eStyleUnit_None ||
+           mUnit == eStyleUnit_Enumerated;
+  }
+
+  bool IsAngleValue() const { return eStyleUnit_Degree == mUnit; }
+
+  static bool IsCalcUnit(nsStyleUnit aUnit) { return aUnit == eStyleUnit_Calc; }
+
+  static bool IsPointerUnit(nsStyleUnit aUnit) { return IsCalcUnit(aUnit); }
+
+  bool IsCalcUnit() const { return IsCalcUnit(mUnit); }
+
+  bool IsPointerValue() const { return IsPointerUnit(mUnit); }
+
+  bool IsCoordPercentCalcUnit() const {
+    return mUnit == eStyleUnit_Coord || mUnit == eStyleUnit_Percent ||
+           IsCalcUnit();
+  }
+
+  // Does this calc() expression have any percentages inside it?  Can be
+  // called only when IsCalcUnit() is true.
+  bool CalcHasPercent() const { return GetCalcValue()->mHasPercent; }
+
+  bool HasPercent() const {
+    return mUnit == eStyleUnit_Percent || (IsCalcUnit() && CalcHasPercent());
+  }
+
+  static bool ConvertsToPercent(const nsStyleUnit aUnit,
+                                const nsStyleUnion aValue) {
+    if (aUnit == eStyleUnit_Percent) {
+      return true;
+    }
+    if (!IsCalcUnit(aUnit)) {
+      return false;
+    }
+    auto* calc = AsCalcValue(aValue);
+    return calc->mLength == 0 && calc->mHasPercent;
+  }
+
+  bool ConvertsToPercent() const { return ConvertsToPercent(mUnit, mValue); }
+
+  float ToPercent() const {
+    MOZ_ASSERT(ConvertsToPercent());
+    if (IsCalcUnit()) {
+      MOZ_ASSERT(CalcHasPercent() && GetCalcValue()->mLength == 0);
+      return GetCalcValue()->mPercent;
+    }
+    return mValue.mFloat;
+  }
+
+  static bool ConvertsToLength(const nsStyleUnit aUnit,
+                               const nsStyleUnion aValue) {
+    return aUnit == eStyleUnit_Coord ||
+           (IsCalcUnit(aUnit) && !AsCalcValue(aValue)->mHasPercent);
+  }
+
+  bool ConvertsToLength() const { return ConvertsToLength(mUnit, mValue); }
+
+  static nscoord ToLength(nsStyleUnit aUnit, nsStyleUnion aValue) {
+    MOZ_ASSERT(ConvertsToLength(aUnit, aValue));
+    if (IsCalcUnit(aUnit)) {
+      // Note: ToLength asserts !mHasPercent
+      return AsCalcValue(aValue)->ToLength();
+    }
+    MOZ_ASSERT(aUnit == eStyleUnit_Coord);
+    return aValue.mInt;
+  }
+
+  nscoord ToLength() const { return ToLength(GetUnit(), mValue); }
+
+  // Callers must verify IsCalcUnit before calling this function.
+  static Calc* AsCalcValue(nsStyleUnion aValue) {
+    return static_cast<Calc*>(aValue.mPointer);
+  }
+
+  // Compute the value that IsCalcUnit().
+  // @note the caller is expected to handle percentage of an indefinite size
+  // and NOT call this method with aPercentageBasis == NS_UNCONSTRAINEDSIZE.
+  // @note the return value may be negative, e.g. for "calc(a - b%)"
+  nscoord ComputeComputedCalc(nscoord aPercentageBasis) const;
+
+  // Compute the value that is either a coord, a percent, or a calc expression.
+  // @note the caller is expected to handle percentage of an indefinite size
+  // and NOT call this method with aPercentageBasis == NS_UNCONSTRAINEDSIZE.
+  // @note the return value may be negative, e.g. for "calc(a - b%)"
+  nscoord ComputeCoordPercentCalc(nscoord aPercentageBasis) const;
+
+  nscoord GetCoordValue() const;
+  int32_t GetIntValue() const;
+  float GetPercentValue() const;
+  float GetFactorValue() const;
+  float GetFactorOrPercentValue() const;
+  float GetAngleValue() const;
+  double GetAngleValueInDegrees() const;
+  double GetAngleValueInRadians() const;
+  float GetFlexFractionValue() const;
+  Calc* GetCalcValue() const;
+  template <typename T,
+            typename = typename std::enable_if<std::is_enum<T>::value>::type>
+  T GetEnumValue() const {
+    MOZ_ASSERT(GetUnit() == eStyleUnit_Enumerated,
+               "The unit must be eStyleUnit_Enumerated!");
+    return static_cast<T>(GetIntValue());
+  }
+
+  // Sets to null and releases any refcounted objects.  Only use this if the
+  // object is initialized (i.e. don't use it in nsStyleCoord constructors).
+  void Reset();
+
+  void SetCoordValue(nscoord aValue);
+  void SetIntValue(int32_t aValue, nsStyleUnit aUnit);
+  void SetPercentValue(float aValue);
+  void SetFactorValue(float aValue);
+  void SetFlexFractionValue(float aValue);
+  void SetNormalValue();
+  void SetAutoValue();
+  void SetNoneValue();
+  void SetCalcValue(Calc* aValue);
+  template <typename T,
+            typename = typename std::enable_if<std::is_enum<T>::value>::type>
+  void SetEnumValue(T aValue) {
+    static_assert(mozilla::EnumTypeFitsWithin<T, int32_t>::value,
+                  "aValue must be an enum that fits within mValue.mInt!");
+    SetIntValue(static_cast<int32_t>(aValue), eStyleUnit_Enumerated);
+  }
+
+  // Resets a coord represented by a unit/value pair.
+  static inline void Reset(nsStyleUnit& aUnit, nsStyleUnion& aValue);
+
+  // Sets a coord represented by a unit/value pair from a second
+  // unit/value pair.
+  static inline void SetValue(nsStyleUnit& aUnit, nsStyleUnion& aValue,
+                              nsStyleUnit aOtherUnit,
+                              const nsStyleUnion& aOtherValue);
+
+  // Sets a coord represented by a unit/value pair from an nsStyleCoord.
+  static inline void SetValue(nsStyleUnit& aUnit, nsStyleUnion& aValue,
+                              const nsStyleCoord& aOther);
+
+  // Like the above, but do not reset before setting.
+  static inline void InitWithValue(nsStyleUnit& aUnit, nsStyleUnion& aValue,
+                                   nsStyleUnit aOtherUnit,
+                                   const nsStyleUnion& aOtherValue);
+
+  static inline void InitWithValue(nsStyleUnit& aUnit, nsStyleUnion& aValue,
+                                   const nsStyleCoord& aOther);
+
+ private:
+  nsStyleUnit mUnit;
+  nsStyleUnion mValue;
+};
+
+/**
+ * Class that represents a set of top/right/bottom/left nsStyleCoords.
+ * This is commonly used to hold the widths of the borders, margins,
+ * or paddings of a box.
+ */
+/** <div rustbindgen private accessor="unsafe"></div> */
+class nsStyleSides {
+ public:
+  nsStyleSides();
+  nsStyleSides(const nsStyleSides&);
+  ~nsStyleSides();
+
+  nsStyleSides& operator=(const nsStyleSides& aCopy);
+  bool operator==(const nsStyleSides& aOther) const;
+  bool operator!=(const nsStyleSides& aOther) const;
+
+  inline nsStyleUnit GetUnit(mozilla::Side aSide) const;
+  inline nsStyleUnit GetLeftUnit() const;
+  inline nsStyleUnit GetTopUnit() const;
+  inline nsStyleUnit GetRightUnit() const;
+  inline nsStyleUnit GetBottomUnit() const;
+
+  inline nsStyleCoord Get(mozilla::Side aSide) const;
+  inline nsStyleCoord GetLeft() const;
+  inline nsStyleCoord GetTop() const;
+  inline nsStyleCoord GetRight() const;
+  inline nsStyleCoord GetBottom() const;
+
+  // Methods to access the units and values in terms of logical sides
+  // for a given writing mode.
+  // NOTE: The definitions are in WritingModes.h (after we have the full
+  // declaration of WritingMode available).
+  inline nsStyleUnit GetUnit(mozilla::WritingMode aWritingMode,
+                             mozilla::LogicalSide aSide) const;
+  inline nsStyleUnit GetIStartUnit(mozilla::WritingMode aWritingMode) const;
+  inline nsStyleUnit GetBStartUnit(mozilla::WritingMode aWritingMode) const;
+  inline nsStyleUnit GetIEndUnit(mozilla::WritingMode aWritingMode) const;
+  inline nsStyleUnit GetBEndUnit(mozilla::WritingMode aWritingMode) const;
+
+  // Return true if either the start or end side in the axis is 'auto'.
+  inline bool HasBlockAxisAuto(mozilla::WritingMode aWritingMode) const;
+  inline bool HasInlineAxisAuto(mozilla::WritingMode aWritingMode) const;
+
+  inline nsStyleCoord Get(mozilla::WritingMode aWritingMode,
+                          mozilla::LogicalSide aSide) const;
+  inline nsStyleCoord GetIStart(mozilla::WritingMode aWritingMode) const;
+  inline nsStyleCoord GetBStart(mozilla::WritingMode aWritingMode) const;
+  inline nsStyleCoord GetIEnd(mozilla::WritingMode aWritingMode) const;
+  inline nsStyleCoord GetBEnd(mozilla::WritingMode aWritingMode) const;
+
+  // Sets each side to null and releases any refcounted objects.  Only use this
+  // if the object is initialized (i.e. don't use it in nsStyleSides
+  // constructors).
+  void Reset();
+
+  inline void Set(mozilla::Side aSide, const nsStyleCoord& aCoord);
+  inline void SetLeft(const nsStyleCoord& aCoord);
+  inline void SetTop(const nsStyleCoord& aCoord);
+  inline void SetRight(const nsStyleCoord& aCoord);
+  inline void SetBottom(const nsStyleCoord& aCoord);
+
+  nscoord ToLength(mozilla::Side aSide) const {
+    return nsStyleCoord::ToLength(mUnits[aSide], mValues[aSide]);
+  }
+
+  bool ConvertsToLength() const {
+    NS_FOR_CSS_SIDES(side) {
+      if (!nsStyleCoord::ConvertsToLength(mUnits[side], mValues[side])) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+ protected:
+  nsStyleUnit mUnits[4];
+  nsStyleUnion mValues[4];
+};
+
+// -------------------------
+// nsStyleCoord inlines
+//
+inline nsStyleCoord::nsStyleCoord(nscoord aValue, CoordConstructorType)
+    : mUnit(eStyleUnit_Coord) {
+  mValue.mInt = aValue;
+}
+
+inline nsStyleCoord::nsStyleCoord(const nsStyleCoord& aCopy)
+    : mUnit(eStyleUnit_Null) {
+  InitWithValue(mUnit, mValue, aCopy);
+}
+
+inline nsStyleCoord::nsStyleCoord(const nsStyleUnion& aValue, nsStyleUnit aUnit)
+    : mUnit(eStyleUnit_Null) {
+  InitWithValue(mUnit, mValue, aUnit, aValue);
+}
+
+inline bool nsStyleCoord::operator!=(const nsStyleCoord& aOther) const {
+  return !((*this) == aOther);
+}
+
+inline nscoord nsStyleCoord::GetCoordValue() const {
+  NS_ASSERTION((mUnit == eStyleUnit_Coord), "not a coord value");
+  if (mUnit == eStyleUnit_Coord) {
+    return mValue.mInt;
+  }
+  return 0;
+}
+
+inline int32_t nsStyleCoord::GetIntValue() const {
+  NS_ASSERTION(
+      (mUnit == eStyleUnit_Enumerated) || (mUnit == eStyleUnit_Integer),
+      "not an int value");
+  if ((mUnit == eStyleUnit_Enumerated) || (mUnit == eStyleUnit_Integer)) {
+    return mValue.mInt;
+  }
+  return 0;
+}
+
+inline float nsStyleCoord::GetPercentValue() const {
+  NS_ASSERTION(mUnit == eStyleUnit_Percent, "not a percent value");
+  if (mUnit == eStyleUnit_Percent) {
+    return mValue.mFloat;
+  }
+  return 0.0f;
+}
+
+inline float nsStyleCoord::GetFactorValue() const {
+  NS_ASSERTION(mUnit == eStyleUnit_Factor, "not a factor value");
+  if (mUnit == eStyleUnit_Factor) {
+    return mValue.mFloat;
+  }
+  return 0.0f;
+}
+
+inline float nsStyleCoord::GetFactorOrPercentValue() const {
+  NS_ASSERTION(mUnit == eStyleUnit_Factor || mUnit == eStyleUnit_Percent,
+               "not a percent or factor value");
+  if (mUnit == eStyleUnit_Factor || mUnit == eStyleUnit_Percent) {
+    return mValue.mFloat;
+  }
+  return 0.0f;
+}
+
+inline float nsStyleCoord::GetAngleValue() const {
+  MOZ_ASSERT(mUnit == eStyleUnit_Degree);
+  return mValue.mFloat;
+}
+
+inline float nsStyleCoord::GetFlexFractionValue() const {
+  NS_ASSERTION(mUnit == eStyleUnit_FlexFraction, "not a fr value");
+  if (mUnit == eStyleUnit_FlexFraction) {
+    return mValue.mFloat;
+  }
+  return 0.0f;
+}
+
+inline nsStyleCoord::Calc* nsStyleCoord::GetCalcValue() const {
+  NS_ASSERTION(IsCalcUnit(), "not a pointer value");
+  if (IsCalcUnit()) {
+    return AsCalcValue(mValue);
+  }
+  return nullptr;
+}
+
+/* static */ inline void nsStyleCoord::Reset(nsStyleUnit& aUnit,
+                                             nsStyleUnion& aValue) {
+  MOZ_ASSERT(aUnit <= eStyleUnit_MAX,
+             "calling Reset on uninitialized nsStyleCoord?");
+
+  switch (aUnit) {
+    case eStyleUnit_Calc:
+      static_cast<Calc*>(aValue.mPointer)->Release();
+      break;
+    default:
+      MOZ_ASSERT(!IsPointerUnit(aUnit), "check pointer refcounting logic");
+  }
+
+  aUnit = eStyleUnit_Null;
+  aValue.mInt = 0;
+}
+
+/* static */ inline void nsStyleCoord::SetValue(
+    nsStyleUnit& aUnit, nsStyleUnion& aValue, nsStyleUnit aOtherUnit,
+    const nsStyleUnion& aOtherValue) {
+  Reset(aUnit, aValue);
+  InitWithValue(aUnit, aValue, aOtherUnit, aOtherValue);
+}
+
+/* static */ inline void nsStyleCoord::InitWithValue(
+    nsStyleUnit& aUnit, nsStyleUnion& aValue, nsStyleUnit aOtherUnit,
+    const nsStyleUnion& aOtherValue) {
+  aUnit = aOtherUnit;
+  aValue = aOtherValue;
+
+  switch (aUnit) {
+    case eStyleUnit_Calc:
+      static_cast<Calc*>(aValue.mPointer)->AddRef();
+      break;
+    default:
+      MOZ_ASSERT(!IsPointerUnit(aUnit), "check pointer refcounting logic");
+  }
+}
+
+/* static */ inline void nsStyleCoord::SetValue(nsStyleUnit& aUnit,
+                                                nsStyleUnion& aValue,
+                                                const nsStyleCoord& aOther) {
+  SetValue(aUnit, aValue, aOther.mUnit, aOther.mValue);
+}
+
+/* static */ inline void nsStyleCoord::InitWithValue(
+    nsStyleUnit& aUnit, nsStyleUnion& aValue, const nsStyleCoord& aOther) {
+  InitWithValue(aUnit, aValue, aOther.mUnit, aOther.mValue);
+}
+
+// -------------------------
+// nsStyleSides inlines
+//
+inline bool nsStyleSides::operator!=(const nsStyleSides& aOther) const {
+  return !((*this) == aOther);
+}
+
+inline nsStyleUnit nsStyleSides::GetUnit(mozilla::Side aSide) const {
+  return (nsStyleUnit)mUnits[aSide];
+}
+
+inline nsStyleUnit nsStyleSides::GetLeftUnit() const {
+  return GetUnit(mozilla::eSideLeft);
+}
+
+inline nsStyleUnit nsStyleSides::GetTopUnit() const {
+  return GetUnit(mozilla::eSideTop);
+}
+
+inline nsStyleUnit nsStyleSides::GetRightUnit() const {
+  return GetUnit(mozilla::eSideRight);
+}
+
+inline nsStyleUnit nsStyleSides::GetBottomUnit() const {
+  return GetUnit(mozilla::eSideBottom);
+}
+
+inline nsStyleCoord nsStyleSides::Get(mozilla::Side aSide) const {
+  return nsStyleCoord(mValues[aSide], nsStyleUnit(mUnits[aSide]));
+}
+
+inline nsStyleCoord nsStyleSides::GetLeft() const {
+  return Get(mozilla::eSideLeft);
+}
+
+inline nsStyleCoord nsStyleSides::GetTop() const {
+  return Get(mozilla::eSideTop);
+}
+
+inline nsStyleCoord nsStyleSides::GetRight() const {
+  return Get(mozilla::eSideRight);
+}
+
+inline nsStyleCoord nsStyleSides::GetBottom() const {
+  return Get(mozilla::eSideBottom);
+}
+
+inline void nsStyleSides::Set(mozilla::Side aSide, const nsStyleCoord& aCoord) {
+  nsStyleCoord::SetValue(mUnits[aSide], mValues[aSide], aCoord);
+}
+
+inline void nsStyleSides::SetLeft(const nsStyleCoord& aCoord) {
+  Set(mozilla::eSideLeft, aCoord);
+}
+
+inline void nsStyleSides::SetTop(const nsStyleCoord& aCoord) {
+  Set(mozilla::eSideTop, aCoord);
+}
+
+inline void nsStyleSides::SetRight(const nsStyleCoord& aCoord) {
+  Set(mozilla::eSideRight, aCoord);
+}
+
+inline void nsStyleSides::SetBottom(const nsStyleCoord& aCoord) {
+  Set(mozilla::eSideBottom, aCoord);
+}
+#endif /* nsStyleCoord_h___ */
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -1268,18 +1268,20 @@ nsStylePosition::nsStylePosition(const D
       mOffset(StyleRectWithAllSides(LengthPercentageOrAuto::Auto())),
       mWidth(StyleSize::Auto()),
       mMinWidth(StyleSize::Auto()),
       mMaxWidth(StyleMaxSize::None()),
       mHeight(StyleSize::Auto()),
       mMinHeight(StyleSize::Auto()),
       mMaxHeight(StyleMaxSize::None()),
       mFlexBasis(StyleFlexBasis::Size(StyleSize::Auto())),
-      mGridAutoColumns(StyleTrackSize::Breadth(StyleTrackBreadth::Auto())),
-      mGridAutoRows(StyleTrackSize::Breadth(StyleTrackBreadth::Auto())),
+      mGridAutoColumnsMin(eStyleUnit_Auto),
+      mGridAutoColumnsMax(eStyleUnit_Auto),
+      mGridAutoRowsMin(eStyleUnit_Auto),
+      mGridAutoRowsMax(eStyleUnit_Auto),
       mAspectRatio(0.0f),
       mGridAutoFlow(NS_STYLE_GRID_AUTO_FLOW_ROW),
       mBoxSizing(StyleBoxSizing::Content),
       mAlignContent(NS_STYLE_ALIGN_NORMAL),
       mAlignItems(NS_STYLE_ALIGN_NORMAL),
       mAlignSelf(NS_STYLE_ALIGN_AUTO),
       mJustifyContent(NS_STYLE_JUSTIFY_NORMAL),
       mSpecifiedJustifyItems(NS_STYLE_JUSTIFY_LEGACY),
@@ -1313,18 +1315,20 @@ nsStylePosition::nsStylePosition(const n
       mOffset(aSource.mOffset),
       mWidth(aSource.mWidth),
       mMinWidth(aSource.mMinWidth),
       mMaxWidth(aSource.mMaxWidth),
       mHeight(aSource.mHeight),
       mMinHeight(aSource.mMinHeight),
       mMaxHeight(aSource.mMaxHeight),
       mFlexBasis(aSource.mFlexBasis),
-      mGridAutoColumns(aSource.mGridAutoColumns),
-      mGridAutoRows(aSource.mGridAutoRows),
+      mGridAutoColumnsMin(aSource.mGridAutoColumnsMin),
+      mGridAutoColumnsMax(aSource.mGridAutoColumnsMax),
+      mGridAutoRowsMin(aSource.mGridAutoRowsMin),
+      mGridAutoRowsMax(aSource.mGridAutoRowsMax),
       mAspectRatio(aSource.mAspectRatio),
       mGridAutoFlow(aSource.mGridAutoFlow),
       mBoxSizing(aSource.mBoxSizing),
       mAlignContent(aSource.mAlignContent),
       mAlignItems(aSource.mAlignItems),
       mAlignSelf(aSource.mAlignSelf),
       mJustifyContent(aSource.mJustifyContent),
       mSpecifiedJustifyItems(aSource.mSpecifiedJustifyItems),
@@ -1432,18 +1436,20 @@ nsChangeHint nsStylePosition::CalcDiffer
 
   // Properties that apply to grid containers:
   // FIXME: only for grid containers
   // (ie. 'display: grid' or 'display: inline-grid')
   if (!IsGridTemplateEqual(mGridTemplateColumns,
                            aNewData.mGridTemplateColumns) ||
       !IsGridTemplateEqual(mGridTemplateRows, aNewData.mGridTemplateRows) ||
       mGridTemplateAreas != aNewData.mGridTemplateAreas ||
-      mGridAutoColumns != aNewData.mGridAutoColumns ||
-      mGridAutoRows != aNewData.mGridAutoRows ||
+      mGridAutoColumnsMin != aNewData.mGridAutoColumnsMin ||
+      mGridAutoColumnsMax != aNewData.mGridAutoColumnsMax ||
+      mGridAutoRowsMin != aNewData.mGridAutoRowsMin ||
+      mGridAutoRowsMax != aNewData.mGridAutoRowsMax ||
       mGridAutoFlow != aNewData.mGridAutoFlow) {
     return hint | nsChangeHint_AllReflowHints;
   }
 
   // Properties that apply to grid items:
   // FIXME: only for grid items
   // (ie. parent frame is 'display: grid' or 'display: inline-grid')
   if (mGridColumnStart != aNewData.mGridColumnStart ||
@@ -1901,19 +1907,19 @@ void nsStyleImage::DoCopy(const nsStyleI
   if (aOther.mType == eStyleImageType_Image) {
     SetImageRequest(do_AddRef(aOther.mImage));
   } else if (aOther.mType == eStyleImageType_Gradient) {
     SetGradientData(MakeUnique<StyleGradient>(*aOther.mGradient));
   } else if (aOther.mType == eStyleImageType_Element) {
     SetElementId(do_AddRef(aOther.mElementId));
   }
 
-  UniquePtr<CropRect> cropRectCopy;
+  UniquePtr<nsStyleSides> cropRectCopy;
   if (aOther.mCropRect) {
-    cropRectCopy = MakeUnique<CropRect>(*aOther.mCropRect.get());
+    cropRectCopy = MakeUnique<nsStyleSides>(*aOther.mCropRect.get());
   }
   SetCropRect(std::move(cropRectCopy));
 }
 
 void nsStyleImage::SetNull() {
   if (mType == eStyleImageType_Gradient) {
     delete mGradient;
     mGradient = nullptr;
@@ -1961,28 +1967,33 @@ void nsStyleImage::SetElementId(already_
   }
 
   if (RefPtr<nsAtom> atom = aElementId) {
     mElementId = atom.forget().take();
     mType = eStyleImageType_Element;
   }
 }
 
-void nsStyleImage::SetCropRect(UniquePtr<CropRect> aCropRect) {
+void nsStyleImage::SetCropRect(UniquePtr<nsStyleSides> aCropRect) {
   mCropRect = std::move(aCropRect);
 }
 
-static int32_t ConvertToPixelCoord(const StyleNumberOrPercentage& aCoord,
+static int32_t ConvertToPixelCoord(const nsStyleCoord& aCoord,
                                    int32_t aPercentScale) {
   double pixelValue;
-  if (aCoord.IsNumber()) {
-    pixelValue = aCoord.AsNumber();
-  } else {
-    MOZ_ASSERT(aCoord.IsPercentage());
-    pixelValue = aCoord.AsPercentage()._0 * aPercentScale;
+  switch (aCoord.GetUnit()) {
+    case eStyleUnit_Percent:
+      pixelValue = aCoord.GetPercentValue() * aPercentScale;
+      break;
+    case eStyleUnit_Factor:
+      pixelValue = aCoord.GetFactorValue();
+      break;
+    default:
+      MOZ_ASSERT_UNREACHABLE("unexpected unit for image crop rect");
+      return 0;
   }
   MOZ_ASSERT(pixelValue >= 0, "we ensured non-negative while parsing");
   pixelValue = std::min(pixelValue, double(INT32_MAX));  // avoid overflow
   return NS_lround(pixelValue);
 }
 
 already_AddRefed<nsIURI> nsStyleImageRequest::GetImageURI() const {
   nsCOMPtr<nsIURI> uri;
@@ -2019,23 +2030,21 @@ bool nsStyleImage::ComputeActualCropRect
 
   nsIntSize imageSize;
   imageContainer->GetWidth(&imageSize.width);
   imageContainer->GetHeight(&imageSize.height);
   if (imageSize.width <= 0 || imageSize.height <= 0) {
     return false;
   }
 
-  int32_t left =
-      ConvertToPixelCoord(mCropRect->Get(eSideLeft), imageSize.width);
-  int32_t top = ConvertToPixelCoord(mCropRect->Get(eSideTop), imageSize.height);
-  int32_t right =
-      ConvertToPixelCoord(mCropRect->Get(eSideRight), imageSize.width);
+  int32_t left = ConvertToPixelCoord(mCropRect->GetLeft(), imageSize.width);
+  int32_t top = ConvertToPixelCoord(mCropRect->GetTop(), imageSize.height);
+  int32_t right = ConvertToPixelCoord(mCropRect->GetRight(), imageSize.width);
   int32_t bottom =
-      ConvertToPixelCoord(mCropRect->Get(eSideBottom), imageSize.height);
+      ConvertToPixelCoord(mCropRect->GetBottom(), imageSize.height);
 
   // IntersectRect() returns an empty rect if we get negative width or height
   nsIntRect cropRect(left, top, right - left, bottom - top);
   nsIntRect imageRect(nsIntPoint(0, 0), imageSize);
   aActualCropRect.IntersectRect(imageRect, cropRect);
 
   if (aIsEntireImage) {
     *aIsEntireImage = aActualCropRect.IsEqualInterior(imageRect);
@@ -2135,28 +2144,28 @@ bool nsStyleImage::IsLoaded() const {
              (status & imgIRequest::STATUS_LOAD_COMPLETE);
     }
     default:
       MOZ_ASSERT_UNREACHABLE("unexpected image type");
       return false;
   }
 }
 
-static inline bool EqualRects(const nsStyleImage::CropRect* aRect1,
-                              const nsStyleImage::CropRect* aRect2) {
+static inline bool EqualRects(const UniquePtr<nsStyleSides>& aRect1,
+                              const UniquePtr<nsStyleSides>& aRect2) {
   return aRect1 == aRect2 || /* handles null== null, and optimize */
          (aRect1 && aRect2 && *aRect1 == *aRect2);
 }
 
 bool nsStyleImage::operator==(const nsStyleImage& aOther) const {
   if (mType != aOther.mType) {
     return false;
   }
 
-  if (!EqualRects(mCropRect.get(), aOther.mCropRect.get())) {
+  if (!EqualRects(mCropRect, aOther.mCropRect)) {
     return false;
   }
 
   if (mType == eStyleImageType_Image) {
     return DefinitelyEqualImages(mImage, aOther.mImage);
   }
 
   if (mType == eStyleImageType_Gradient) {
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -17,16 +17,17 @@
 #include "mozilla/StaticPtr.h"
 #include "mozilla/StyleColorInlines.h"
 #include "mozilla/UniquePtr.h"
 #include "nsColor.h"
 #include "nsCoord.h"
 #include "nsMargin.h"
 #include "nsFont.h"
 #include "nsStyleAutoArray.h"
+#include "nsStyleCoord.h"
 #include "nsStyleConsts.h"
 #include "nsChangeHint.h"
 #include "nsTimingFunction.h"
 #include "nsCOMPtr.h"
 #include "nsCOMArray.h"
 #include "nsTArray.h"
 #include "nsCSSValue.h"
 #include "imgRequestProxy.h"
@@ -251,28 +252,26 @@ struct CachedBorderImageData {
  * (2) A CSS linear or radial gradient.
  * (3) An element within a document, or an <img>, <video>, or <canvas> element
  *     not in a document.
  * (*) Optionally a crop rect can be set to paint a partial (rectangular)
  * region of an image. (Currently, this feature is only supported with an
  * image of type (1)).
  */
 struct nsStyleImage {
-  using CropRect = mozilla::StyleRect<mozilla::StyleNumberOrPercentage>;
-
   nsStyleImage();
   ~nsStyleImage();
   nsStyleImage(const nsStyleImage& aOther);
   nsStyleImage& operator=(const nsStyleImage& aOther);
 
   void SetNull();
   void SetImageRequest(already_AddRefed<nsStyleImageRequest> aImage);
   void SetGradientData(mozilla::UniquePtr<mozilla::StyleGradient>);
   void SetElementId(already_AddRefed<nsAtom> aElementId);
-  void SetCropRect(mozilla::UniquePtr<CropRect> aCropRect);
+  void SetCropRect(mozilla::UniquePtr<nsStyleSides> aCropRect);
 
   void ResolveImage(mozilla::dom::Document& aDocument,
                     const nsStyleImage* aOldImage) {
     MOZ_ASSERT(mType != eStyleImageType_Image || mImage);
     if (mType == eStyleImageType_Image && !mImage->IsResolved()) {
       const nsStyleImageRequest* oldRequest =
           (aOldImage && aOldImage->GetType() == eStyleImageType_Image)
               ? aOldImage->ImageRequest()
@@ -294,20 +293,20 @@ struct nsStyleImage {
   }
   bool IsResolved() const {
     return mType != eStyleImageType_Image || ImageRequest()->IsResolved();
   }
   const nsAtom* GetElementId() const {
     NS_ASSERTION(mType == eStyleImageType_Element, "Data is not an element!");
     return mElementId;
   }
-  const CropRect* GetCropRect() const {
+  const mozilla::UniquePtr<nsStyleSides>& GetCropRect() const {
     NS_ASSERTION(mType == eStyleImageType_Image,
                  "Only image data can have a crop rect");
-    return mCropRect.get();
+    return mCropRect;
   }
 
   already_AddRefed<nsIURI> GetImageURI() const;
 
   const mozilla::StyleComputedImageUrl* GetURLValue() const;
 
   /**
    * Compute the actual crop rect in pixels, using the source image bounds.
@@ -390,17 +389,17 @@ struct nsStyleImage {
   nsStyleImageType mType;
   union {
     nsStyleImageRequest* mImage;
     mozilla::StyleGradient* mGradient;
     nsAtom* mElementId;
   };
 
   // This is _currently_ used only in conjunction with eStyleImageType_Image.
-  mozilla::UniquePtr<CropRect> mCropRect;
+  mozilla::UniquePtr<nsStyleSides> mCropRect;
 };
 
 struct nsStyleImageLayers {
   // Indices into kBackgroundLayerTable and kMaskLayerTable
   enum {
     shorthand = 0,
     color,
     image,
@@ -950,57 +949,65 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
 // (but *not* grid-template-areas.)
 // http://dev.w3.org/csswg/css-grid/#track-sizing
 //
 // This represents either:
 // * none:
 //   mIsSubgrid is false, all three arrays are empty
 // * <track-list>:
 //   mIsSubgrid is false,
-//   mTrackSizingFunctions is of non-zero size,
+//   mMinTrackSizingFunctions and mMaxTrackSizingFunctions
+//   are of identical non-zero size,
 //   and mLineNameLists is one element longer than that.
 //   (Delimiting N columns requires N+1 lines:
 //   one before each track, plus one at the very end.)
 //
 //   An omitted <line-names> is still represented in mLineNameLists,
 //   as an empty sub-array.
 //
 //   A <track-size> specified as a single <track-breadth> is represented
 //   as identical min and max sizing functions.
 //   A 'fit-content(size)' <track-size> is represented as eStyleUnit_None
 //   in the min sizing function and 'size' in the max sizing function.
 //
+//   The units for nsStyleCoord are:
+//   * eStyleUnit_Percent represents a <percentage>
+//   * eStyleUnit_FlexFraction represents a <flex> flexible fraction
+//   * eStyleUnit_Coord represents a <length>
+//   * eStyleUnit_Enumerated represents min-content or max-content
 // * subgrid <line-name-list>?:
 //   mIsSubgrid is true,
 //   mLineNameLists may or may not be empty,
-//   mTrackSizingFunctions is empty.
+//   mMinTrackSizingFunctions and mMaxTrackSizingFunctions are empty.
 //
 // If mRepeatAutoIndex != -1 then that index is an <auto-repeat> and
 // mIsAutoFill == true means it's an 'auto-fill', otherwise 'auto-fit'.
 // mRepeatAutoLineNameListBefore is the list of line names before the track
 // size, mRepeatAutoLineNameListAfter the names after.  (They are empty
 // when there is no <auto-repeat> track, i.e. when mRepeatAutoIndex == -1).
 // When mIsSubgrid is true, mRepeatAutoLineNameListBefore contains the line
 // names and mRepeatAutoLineNameListAfter is empty.
 struct nsStyleGridTemplate {
   nsTArray<nsTArray<RefPtr<nsAtom>>> mLineNameLists;
-  nsTArray<mozilla::StyleTrackSize> mTrackSizingFunctions;
+  nsTArray<nsStyleCoord> mMinTrackSizingFunctions;
+  nsTArray<nsStyleCoord> mMaxTrackSizingFunctions;
   nsTArray<RefPtr<nsAtom>> mRepeatAutoLineNameListBefore;
   nsTArray<RefPtr<nsAtom>> mRepeatAutoLineNameListAfter;
   int16_t mRepeatAutoIndex;  // -1 or the track index for an auto-fill/fit track
   bool mIsAutoFill : 1;
   bool mIsSubgrid : 1;
 
   nsStyleGridTemplate()
       : mRepeatAutoIndex(-1), mIsAutoFill(false), mIsSubgrid(false) {}
 
   inline bool operator==(const nsStyleGridTemplate& aOther) const {
     return mIsSubgrid == aOther.mIsSubgrid &&
            mLineNameLists == aOther.mLineNameLists &&
-           mTrackSizingFunctions == aOther.mTrackSizingFunctions &&
+           mMinTrackSizingFunctions == aOther.mMinTrackSizingFunctions &&
+           mMaxTrackSizingFunctions == aOther.mMaxTrackSizingFunctions &&
            mIsAutoFill == aOther.mIsAutoFill &&
            mRepeatAutoIndex == aOther.mRepeatAutoIndex &&
            mRepeatAutoLineNameListBefore ==
                aOther.mRepeatAutoLineNameListBefore &&
            mRepeatAutoLineNameListAfter == aOther.mRepeatAutoLineNameListAfter;
   }
 
   bool HasRepeatAuto() const { return mRepeatAutoIndex != -1; }
@@ -1015,17 +1022,16 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
   using LengthPercentageOrAuto = mozilla::LengthPercentageOrAuto;
   using Position = mozilla::Position;
   template <typename T>
   using StyleRect = mozilla::StyleRect<T>;
   using StyleSize = mozilla::StyleSize;
   using StyleMaxSize = mozilla::StyleMaxSize;
   using StyleFlexBasis = mozilla::StyleFlexBasis;
   using WritingMode = mozilla::WritingMode;
-  using StyleTrackSize = mozilla::StyleTrackSize;
 
   explicit nsStylePosition(const mozilla::dom::Document&);
   nsStylePosition(const nsStylePosition& aOther);
   ~nsStylePosition();
   void TriggerImageLoads(mozilla::dom::Document&, const nsStylePosition*) {}
   const static bool kHasTriggerImageLoads = false;
 
   nsChangeHint CalcDifference(
@@ -1057,18 +1063,20 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
   StyleRect<LengthPercentageOrAuto> mOffset;
   StyleSize mWidth;
   StyleSize mMinWidth;
   StyleMaxSize mMaxWidth;
   StyleSize mHeight;
   StyleSize mMinHeight;
   StyleMaxSize mMaxHeight;
   StyleFlexBasis mFlexBasis;
-  StyleTrackSize mGridAutoColumns;
-  StyleTrackSize mGridAutoRows;
+  nsStyleCoord mGridAutoColumnsMin;  // coord, percent, enum, calc, flex
+  nsStyleCoord mGridAutoColumnsMax;  // coord, percent, enum, calc, flex
+  nsStyleCoord mGridAutoRowsMin;     // coord, percent, enum, calc, flex
+  nsStyleCoord mGridAutoRowsMax;     // coord, percent, enum, calc, flex
   float mAspectRatio;
   uint8_t mGridAutoFlow;             // NS_STYLE_GRID_AUTO_FLOW_*
   mozilla::StyleBoxSizing mBoxSizing;
 
   // All align/justify properties here take NS_STYLE_ALIGN_* values.
   uint16_t mAlignContent;  // fallback value in the high byte
   uint8_t mAlignItems;
   uint8_t mAlignSelf;
--- a/layout/style/nsStyleUtil.cpp
+++ b/layout/style/nsStyleUtil.cpp
@@ -181,16 +181,34 @@ void nsStyleUtil::AppendBitmaskCSSValue(
         aResult.Append(char16_t(' '));
       }
     }
   }
   MOZ_ASSERT(aMaskedValue == 0, "unexpected bit remaining in bitfield");
 }
 
 /* static */
+void nsStyleUtil::AppendAngleValue(const nsStyleCoord& aAngle,
+                                   nsAString& aResult) {
+  MOZ_ASSERT(aAngle.IsAngleValue(), "Should have angle value");
+
+  // Append number.
+  AppendCSSNumber(aAngle.GetAngleValue(), aResult);
+
+  // Append unit.
+  switch (aAngle.GetUnit()) {
+    case eStyleUnit_Degree:
+      aResult.AppendLiteral("deg");
+      break;
+    default:
+      MOZ_ASSERT_UNREACHABLE("unrecognized angle unit");
+  }
+}
+
+/* static */
 void nsStyleUtil::AppendPaintOrderValue(uint8_t aValue, nsAString& aResult) {
   static_assert(
       NS_STYLE_PAINT_ORDER_BITWIDTH * NS_STYLE_PAINT_ORDER_LAST_VALUE <= 8,
       "SVGStyleStruct::mPaintOrder and local variables not big enough");
 
   if (aValue == NS_STYLE_PAINT_ORDER_NORMAL) {
     aResult.AppendLiteral("normal");
     return;
@@ -405,13 +423,12 @@ void nsStyleUtil::AppendFontSlantStyle(c
     aOut.AppendLiteral("normal");
   } else if (aStyle.IsItalic()) {
     aOut.AppendLiteral("italic");
   } else {
     aOut.AppendLiteral("oblique");
     auto angle = aStyle.ObliqueAngle();
     if (angle != FontSlantStyle::kDefaultAngle) {
       aOut.AppendLiteral(" ");
-      AppendCSSNumber(angle, aOut);
-      aOut.AppendLiteral("deg");
+      AppendAngleValue(nsStyleCoord(angle, eStyleUnit_Degree), aOut);
     }
   }
 }
--- a/layout/style/nsStyleUtil.h
+++ b/layout/style/nsStyleUtil.h
@@ -10,16 +10,17 @@
 #include "nsCSSPropertyID.h"
 #include "nsTArrayForwardDeclare.h"
 #include "gfxFontFamilyList.h"
 #include "nsStringFwd.h"
 #include "nsStyleStruct.h"
 #include "nsCRT.h"
 
 class nsCSSValue;
+class nsStyleCoord;
 class nsIContent;
 class nsIPrincipal;
 class nsIURI;
 struct gfxFontFeature;
 struct gfxAlternateValue;
 struct nsCSSKTableEntry;
 struct nsCSSValueList;
 
@@ -59,16 +60,18 @@ class nsStyleUtil {
                                    nsAString& aResult);
 
  public:
   // Append a bitmask-valued property's value(s) (space-separated) to aResult.
   static void AppendBitmaskCSSValue(const nsCSSKTableEntry aTable[],
                                     int32_t aMaskedValue, int32_t aFirstMask,
                                     int32_t aLastMask, nsAString& aResult);
 
+  static void AppendAngleValue(const nsStyleCoord& aValue, nsAString& aResult);
+
   static void AppendPaintOrderValue(uint8_t aValue, nsAString& aResult);
 
   static void AppendCSSNumber(float aNumber, nsAString& aResult) {
     aResult.AppendFloat(aNumber);
   }
 
   /*
    * Convert an author-provided floating point number to an integer (0
--- a/layout/svg/nsSVGUtils.cpp
+++ b/layout/svg/nsSVGUtils.cpp
@@ -21,16 +21,17 @@
 #include "nsDisplayList.h"
 #include "nsFilterInstance.h"
 #include "nsFrameList.h"
 #include "nsGkAtoms.h"
 #include "nsIContent.h"
 #include "nsIFrame.h"
 #include "nsLayoutUtils.h"
 #include "nsPresContext.h"
+#include "nsStyleCoord.h"
 #include "nsStyleStruct.h"
 #include "nsStyleTransformMatrix.h"
 #include "SVGAnimatedLength.h"
 #include "nsSVGClipPathFrame.h"
 #include "nsSVGContainerFrame.h"
 #include "SVGContentUtils.h"
 #include "nsSVGDisplayableFrame.h"
 #include "nsSVGFilterPaintCallback.h"
--- a/layout/tables/nsTableFrame.h
+++ b/layout/tables/nsTableFrame.h
@@ -5,16 +5,17 @@
 #ifndef nsTableFrame_h__
 #define nsTableFrame_h__
 
 #include "mozilla/Attributes.h"
 #include "celldata.h"
 #include "imgIContainer.h"
 #include "nscore.h"
 #include "nsContainerFrame.h"
+#include "nsStyleCoord.h"
 #include "nsStyleConsts.h"
 #include "nsCellMap.h"
 #include "nsGkAtoms.h"
 #include "nsDisplayList.h"
 #include "TableArea.h"
 
 struct BCPaintBorderAction;
 class nsTableCellFrame;
--- a/servo/components/style/gecko/conversions.rs
+++ b/servo/components/style/gecko/conversions.rs
@@ -5,42 +5,95 @@
 //! This module contains conversion helpers between Servo and Gecko types
 //! Ideally, it would be in geckolib itself, but coherence
 //! forces us to keep the traits and implementations here
 //!
 //! FIXME(emilio): This file should generally just die.
 
 #![allow(unsafe_code)]
 
+use crate::gecko::values::GeckoStyleCoordConvertible;
 use crate::gecko_bindings::bindings;
-use crate::gecko_bindings::structs::{self, Matrix4x4Components};
+use crate::gecko_bindings::structs::{self, nsStyleCoord_CalcValue, Matrix4x4Components};
 use crate::gecko_bindings::structs::{nsStyleImage, nsresult};
+use crate::gecko_bindings::sugar::ns_style_coord::{CoordData, CoordDataMut, CoordDataValue};
 use crate::stylesheets::RulesMutateError;
 use crate::values::computed::transform::Matrix3D;
 use crate::values::computed::url::ComputedImageUrl;
-use crate::values::computed::{Gradient, Image, TextAlign};
+use crate::values::computed::{Angle, Gradient, Image};
+use crate::values::computed::{Integer, LengthPercentage};
+use crate::values::computed::{Length, Percentage, TextAlign};
+use crate::values::generics::grid::{TrackListValue, TrackSize};
 use crate::values::generics::image::GenericImage;
 use crate::values::generics::rect::Rect;
+use crate::Zero;
+use app_units::Au;
+use style_traits::values::specified::AllowedNumericType;
+
+impl From<LengthPercentage> for nsStyleCoord_CalcValue {
+    fn from(other: LengthPercentage) -> nsStyleCoord_CalcValue {
+        debug_assert!(
+            other.was_calc || !other.has_percentage || other.unclamped_length() == Length::zero()
+        );
+        nsStyleCoord_CalcValue {
+            mLength: other.unclamped_length().to_i32_au(),
+            mPercent: other.percentage(),
+            mHasPercent: other.has_percentage,
+        }
+    }
+}
+
+impl From<nsStyleCoord_CalcValue> for LengthPercentage {
+    fn from(other: nsStyleCoord_CalcValue) -> LengthPercentage {
+        let percentage = if other.mHasPercent {
+            Some(Percentage(other.mPercent))
+        } else {
+            None
+        };
+        Self::with_clamping_mode(
+            Au(other.mLength).into(),
+            percentage,
+            AllowedNumericType::All,
+            /* was_calc = */ true,
+        )
+    }
+}
+impl From<Angle> for CoordDataValue {
+    fn from(reference: Angle) -> Self {
+        CoordDataValue::Degree(reference.degrees())
+    }
+}
 
 impl nsStyleImage {
     /// Set a given Servo `Image` value into this `nsStyleImage`.
     pub fn set(&mut self, image: Image) {
         match image {
             GenericImage::Gradient(boxed_gradient) => self.set_gradient(boxed_gradient),
             GenericImage::Url(ref url) => unsafe {
                 bindings::Gecko_SetLayerImageImageValue(self, url);
             },
             GenericImage::Rect(ref image_rect) => {
                 unsafe {
                     bindings::Gecko_SetLayerImageImageValue(self, &image_rect.url);
                     bindings::Gecko_InitializeImageCropRect(self);
 
                     // Set CropRect
                     let ref mut rect = *self.mCropRect.mPtr;
-                    *rect = Rect(image_rect.top, image_rect.right, image_rect.bottom, image_rect.left);
+                    image_rect
+                        .top
+                        .to_gecko_style_coord(&mut rect.data_at_mut(0));
+                    image_rect
+                        .right
+                        .to_gecko_style_coord(&mut rect.data_at_mut(1));
+                    image_rect
+                        .bottom
+                        .to_gecko_style_coord(&mut rect.data_at_mut(2));
+                    image_rect
+                        .left
+                        .to_gecko_style_coord(&mut rect.data_at_mut(3));
                 }
             },
             GenericImage::Element(ref element) => unsafe {
                 bindings::Gecko_SetImageElement(self, element.as_ptr());
             },
         }
     }
 
@@ -48,33 +101,49 @@ impl nsStyleImage {
         unsafe {
             bindings::Gecko_SetGradientImageValue(self, Box::into_raw(gradient));
         }
     }
 
     /// Converts into Image.
     pub unsafe fn into_image(self: &nsStyleImage) -> Option<Image> {
         use crate::gecko_bindings::structs::nsStyleImageType;
-        use crate::values::computed::MozImageRect;
+        use crate::values::computed::{MozImageRect, NumberOrPercentage};
 
         match self.mType {
             nsStyleImageType::eStyleImageType_Null => None,
             nsStyleImageType::eStyleImageType_Image => {
                 let url = self.get_image_url();
                 if self.mCropRect.mPtr.is_null() {
                     Some(GenericImage::Url(url))
                 } else {
-                    let rect = &*self.mCropRect.mPtr;
-                    Some(GenericImage::Rect(Box::new(MozImageRect {
-                        url,
-                        top: rect.0,
-                        right: rect.1,
-                        bottom: rect.2,
-                        left: rect.3,
-                    })))
+                    let ref rect = *self.mCropRect.mPtr;
+                    match (
+                        NumberOrPercentage::from_gecko_style_coord(&rect.data_at(0)),
+                        NumberOrPercentage::from_gecko_style_coord(&rect.data_at(1)),
+                        NumberOrPercentage::from_gecko_style_coord(&rect.data_at(2)),
+                        NumberOrPercentage::from_gecko_style_coord(&rect.data_at(3)),
+                    ) {
+                        (Some(top), Some(right), Some(bottom), Some(left)) => {
+                            Some(GenericImage::Rect(Box::new(MozImageRect {
+                                url,
+                                top,
+                                right,
+                                bottom,
+                                left,
+                            })))
+                        },
+                        _ => {
+                            debug_assert!(
+                                false,
+                                "mCropRect could not convert to NumberOrPercentage"
+                            );
+                            None
+                        },
+                    }
                 }
             },
             nsStyleImageType::eStyleImageType_Gradient => {
                 let gradient: &Gradient = &**self.__bindgen_anon_1.mGradient.as_ref();
                 Some(GenericImage::Gradient(Box::new(gradient.clone())))
             },
             nsStyleImageType::eStyleImageType_Element => {
                 use crate::gecko_string_cache::Atom;
@@ -257,16 +326,108 @@ impl From<RulesMutateError> for nsresult
             RulesMutateError::Syntax => nsresult::NS_ERROR_DOM_SYNTAX_ERR,
             RulesMutateError::IndexSize => nsresult::NS_ERROR_DOM_INDEX_SIZE_ERR,
             RulesMutateError::HierarchyRequest => nsresult::NS_ERROR_DOM_HIERARCHY_REQUEST_ERR,
             RulesMutateError::InvalidState => nsresult::NS_ERROR_DOM_INVALID_STATE_ERR,
         }
     }
 }
 
+impl TrackSize<LengthPercentage> {
+    /// Return TrackSize from given two nsStyleCoord
+    pub fn from_gecko_style_coords<T: CoordData>(gecko_min: &T, gecko_max: &T) -> Self {
+        use crate::gecko_bindings::structs::root::nsStyleUnit;
+        use crate::values::generics::grid::TrackBreadth;
+
+        if gecko_min.unit() == nsStyleUnit::eStyleUnit_None {
+            debug_assert!(
+                gecko_max.unit() == nsStyleUnit::eStyleUnit_Coord ||
+                    gecko_max.unit() == nsStyleUnit::eStyleUnit_Percent ||
+                    gecko_max.unit() == nsStyleUnit::eStyleUnit_Calc
+            );
+            return TrackSize::FitContent(
+                LengthPercentage::from_gecko_style_coord(gecko_max)
+                    .expect("gecko_max could not convert to LengthPercentage"),
+            );
+        }
+
+        let min = TrackBreadth::from_gecko_style_coord(gecko_min)
+            .expect("gecko_min could not convert to TrackBreadth");
+        let max = TrackBreadth::from_gecko_style_coord(gecko_max)
+            .expect("gecko_max could not convert to TrackBreadth");
+        if min == max {
+            TrackSize::Breadth(max)
+        } else {
+            TrackSize::Minmax(min, max)
+        }
+    }
+
+    /// Save TrackSize to given gecko fields.
+    pub fn to_gecko_style_coords<T: CoordDataMut>(&self, gecko_min: &mut T, gecko_max: &mut T) {
+        match *self {
+            TrackSize::FitContent(ref lop) => {
+                // Gecko sets min value to None and max value to the actual value in fit-content
+                // https://searchfox.org/mozilla-central/rev/c05d9d61188d32b8/layout/style/nsRuleNode.cpp#7910
+                gecko_min.set_value(CoordDataValue::None);
+                lop.to_gecko_style_coord(gecko_max);
+            },
+            TrackSize::Breadth(ref breadth) => {
+                // Set the value to both fields if there's one breadth value
+                // https://searchfox.org/mozilla-central/rev/c05d9d61188d32b8/layout/style/nsRuleNode.cpp#7919
+                breadth.to_gecko_style_coord(gecko_min);
+                breadth.to_gecko_style_coord(gecko_max);
+            },
+            TrackSize::Minmax(ref min, ref max) => {
+                min.to_gecko_style_coord(gecko_min);
+                max.to_gecko_style_coord(gecko_max);
+            },
+        }
+    }
+}
+
+impl TrackListValue<LengthPercentage, Integer> {
+    /// Return TrackSize from given two nsStyleCoord
+    pub fn from_gecko_style_coords<T: CoordData>(gecko_min: &T, gecko_max: &T) -> Self {
+        TrackListValue::TrackSize(TrackSize::from_gecko_style_coords(gecko_min, gecko_max))
+    }
+
+    /// Save TrackSize to given gecko fields.
+    pub fn to_gecko_style_coords<T: CoordDataMut>(&self, gecko_min: &mut T, gecko_max: &mut T) {
+        match *self {
+            TrackListValue::TrackSize(ref size) => size.to_gecko_style_coords(gecko_min, gecko_max),
+            _ => unreachable!("Should only transform from track-size computed values"),
+        }
+    }
+}
+
+impl<T> Rect<T>
+where
+    T: GeckoStyleCoordConvertible,
+{
+    /// Convert this generic Rect to given Gecko fields.
+    pub fn to_gecko_rect(&self, sides: &mut crate::gecko_bindings::structs::nsStyleSides) {
+        self.0.to_gecko_style_coord(&mut sides.data_at_mut(0));
+        self.1.to_gecko_style_coord(&mut sides.data_at_mut(1));
+        self.2.to_gecko_style_coord(&mut sides.data_at_mut(2));
+        self.3.to_gecko_style_coord(&mut sides.data_at_mut(3));
+    }
+
+    /// Convert from given Gecko data to generic Rect.
+    pub fn from_gecko_rect(
+        sides: &crate::gecko_bindings::structs::nsStyleSides,
+    ) -> Option<crate::values::generics::rect::Rect<T>> {
+        Some(Rect::new(
+            T::from_gecko_style_coord(&sides.data_at(0)).expect("coord[0] cound not convert"),
+            T::from_gecko_style_coord(&sides.data_at(1)).expect("coord[1] cound not convert"),
+            T::from_gecko_style_coord(&sides.data_at(2)).expect("coord[2] cound not convert"),
+            T::from_gecko_style_coord(&sides.data_at(3)).expect("coord[3] cound not convert"),
+        ))
+    }
+}
+
 impl TextAlign {
     /// Obtain a specified value from a Gecko keyword value
     ///
     /// Intended for use with presentation attributes, not style structs
     pub fn from_gecko_keyword(kw: u32) -> Self {
         match kw {
             structs::NS_STYLE_TEXT_ALIGN_LEFT => TextAlign::Left,
             structs::NS_STYLE_TEXT_ALIGN_RIGHT => TextAlign::Right,
--- a/servo/components/style/gecko/values.rs
+++ b/servo/components/style/gecko/values.rs
@@ -2,25 +2,225 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
 
 #![allow(unsafe_code)]
 
 //! Different kind of helpers to interact with Gecko values.
 
 use crate::counter_style::{Symbol, Symbols};
-use crate::gecko_bindings::structs::CounterStylePtr;
-use crate::values::generics::CounterStyleOrNone;
+use crate::gecko_bindings::structs::StyleGridTrackBreadth;
+use crate::gecko_bindings::structs::{nsStyleCoord, CounterStylePtr};
+use crate::gecko_bindings::sugar::ns_style_coord::{CoordData, CoordDataMut, CoordDataValue};
+use crate::values::computed::{Angle, Length, LengthPercentage};
+use crate::values::computed::{Number, NumberOrPercentage, Percentage};
+use crate::values::generics::grid::{TrackBreadth, TrackKeyword};
+use crate::values::generics::length::LengthPercentageOrAuto;
+use crate::values::generics::{CounterStyleOrNone, NonNegative};
 use crate::values::Either;
-use crate::Atom;
+use crate::{Atom, Zero};
 use app_units::Au;
 use cssparser::RGBA;
 use nsstring::{nsACString, nsCStr};
 use std::cmp::max;
 
+/// A trait that defines an interface to convert from and to `nsStyleCoord`s.
+///
+/// TODO(emilio): Almost everything that is in this file should be somehow
+/// switched to cbindgen.
+pub trait GeckoStyleCoordConvertible: Sized {
+    /// Convert this to a `nsStyleCoord`.
+    fn to_gecko_style_coord<T: CoordDataMut>(&self, coord: &mut T);
+    /// Given a `nsStyleCoord`, try to get a value of this type..
+    fn from_gecko_style_coord<T: CoordData>(coord: &T) -> Option<Self>;
+}
+
+impl nsStyleCoord {
+    #[inline]
+    /// Set this `nsStyleCoord` value to `val`.
+    pub fn set<T: GeckoStyleCoordConvertible>(&mut self, val: T) {
+        val.to_gecko_style_coord(self);
+    }
+}
+
+impl<Inner> GeckoStyleCoordConvertible for NonNegative<Inner>
+where
+    Inner: GeckoStyleCoordConvertible,
+{
+    fn to_gecko_style_coord<T: CoordDataMut>(&self, coord: &mut T) {
+        self.0.to_gecko_style_coord(coord)
+    }
+
+    fn from_gecko_style_coord<T: CoordData>(coord: &T) -> Option<Self> {
+        Some(NonNegative(Inner::from_gecko_style_coord(coord)?))
+    }
+}
+
+impl GeckoStyleCoordConvertible for Number {
+    fn to_gecko_style_coord<T: CoordDataMut>(&self, coord: &mut T) {
+        coord.set_value(CoordDataValue::Factor(*self));
+    }
+
+    fn from_gecko_style_coord<T: CoordData>(coord: &T) -> Option<Self> {
+        match coord.as_value() {
+            CoordDataValue::Factor(f) => Some(f),
+            _ => None,
+        }
+    }
+}
+
+impl GeckoStyleCoordConvertible for Percentage {
+    fn to_gecko_style_coord<T: CoordDataMut>(&self, coord: &mut T) {
+        coord.set_value(CoordDataValue::Percent(self.0));
+    }
+
+    fn from_gecko_style_coord<T: CoordData>(coord: &T) -> Option<Self> {
+        match coord.as_value() {
+            CoordDataValue::Percent(p) => Some(Percentage(p)),
+            _ => None,
+        }
+    }
+}
+
+impl GeckoStyleCoordConvertible for NumberOrPercentage {
+    fn to_gecko_style_coord<T: CoordDataMut>(&self, coord: &mut T) {
+        match *self {
+            NumberOrPercentage::Number(ref n) => n.to_gecko_style_coord(coord),
+            NumberOrPercentage::Percentage(ref p) => p.to_gecko_style_coord(coord),
+        }
+    }
+
+    fn from_gecko_style_coord<T: CoordData>(coord: &T) -> Option<Self> {
+        match coord.as_value() {
+            CoordDataValue::Factor(f) => Some(NumberOrPercentage::Number(f)),
+            CoordDataValue::Percent(p) => Some(NumberOrPercentage::Percentage(Percentage(p))),
+            _ => None,
+        }
+    }
+}
+
+impl GeckoStyleCoordConvertible for LengthPercentage {
+    fn to_gecko_style_coord<T: CoordDataMut>(&self, coord: &mut T) {
+        if self.was_calc {
+            return coord.set_value(CoordDataValue::Calc((*self).into()));
+        }
+        debug_assert!(!self.has_percentage || self.unclamped_length() == Length::zero());
+        if self.has_percentage {
+            return coord.set_value(CoordDataValue::Percent(self.percentage()));
+        }
+        coord.set_value(CoordDataValue::Coord(self.unclamped_length().to_i32_au()))
+    }
+
+    fn from_gecko_style_coord<T: CoordData>(coord: &T) -> Option<Self> {
+        match coord.as_value() {
+            CoordDataValue::Coord(coord) => Some(LengthPercentage::new(Au(coord).into(), None)),
+            CoordDataValue::Percent(p) => {
+                Some(LengthPercentage::new(Au(0).into(), Some(Percentage(p))))
+            },
+            CoordDataValue::Calc(calc) => Some(calc.into()),
+            _ => None,
+        }
+    }
+}
+
+impl GeckoStyleCoordConvertible for Length {
+    fn to_gecko_style_coord<T: CoordDataMut>(&self, coord: &mut T) {
+        coord.set_value(CoordDataValue::Coord(self.to_i32_au()));
+    }
+
+    fn from_gecko_style_coord<T: CoordData>(coord: &T) -> Option<Self> {
+        match coord.as_value() {
+            CoordDataValue::Coord(coord) => Some(Au(coord).into()),
+            _ => None,
+        }
+    }
+}
+
+impl<LengthPercentage> GeckoStyleCoordConvertible for LengthPercentageOrAuto<LengthPercentage>
+where
+    LengthPercentage: GeckoStyleCoordConvertible,
+{
+    fn to_gecko_style_coord<T: CoordDataMut>(&self, coord: &mut T) {
+        match *self {
+            LengthPercentageOrAuto::Auto => coord.set_value(CoordDataValue::Auto),
+            LengthPercentageOrAuto::LengthPercentage(ref lp) => lp.to_gecko_style_coord(coord),
+        }
+    }
+
+    fn from_gecko_style_coord<T: CoordData>(coord: &T) -> Option<Self> {
+        match coord.as_value() {
+            CoordDataValue::Auto => Some(LengthPercentageOrAuto::Auto),
+            _ => LengthPercentage::from_gecko_style_coord(coord)
+                .map(LengthPercentageOrAuto::LengthPercentage),
+        }
+    }
+}
+
+impl<L: GeckoStyleCoordConvertible> GeckoStyleCoordConvertible for TrackBreadth<L> {
+    fn to_gecko_style_coord<T: CoordDataMut>(&self, coord: &mut T) {
+        match *self {
+            TrackBreadth::Breadth(ref lp) => lp.to_gecko_style_coord(coord),
+            TrackBreadth::Fr(fr) => coord.set_value(CoordDataValue::FlexFraction(fr)),
+            TrackBreadth::Keyword(TrackKeyword::Auto) => coord.set_value(CoordDataValue::Auto),
+            TrackBreadth::Keyword(TrackKeyword::MinContent) => coord.set_value(
+                CoordDataValue::Enumerated(StyleGridTrackBreadth::MinContent as u32),
+            ),
+            TrackBreadth::Keyword(TrackKeyword::MaxContent) => coord.set_value(
+                CoordDataValue::Enumerated(StyleGridTrackBreadth::MaxContent as u32),
+            ),
+        }
+    }
+
+    fn from_gecko_style_coord<T: CoordData>(coord: &T) -> Option<Self> {
+        L::from_gecko_style_coord(coord)
+            .map(TrackBreadth::Breadth)
+            .or_else(|| match coord.as_value() {
+                CoordDataValue::Enumerated(v) => {
+                    if v == StyleGridTrackBreadth::MinContent as u32 {
+                        Some(TrackBreadth::Keyword(TrackKeyword::MinContent))
+                    } else if v == StyleGridTrackBreadth::MaxContent as u32 {
+                        Some(TrackBreadth::Keyword(TrackKeyword::MaxContent))
+                    } else {
+                        None
+                    }
+                },
+                CoordDataValue::FlexFraction(fr) => Some(TrackBreadth::Fr(fr)),
+                CoordDataValue::Auto => Some(TrackBreadth::Keyword(TrackKeyword::Auto)),
+                _ => L::from_gecko_style_coord(coord).map(TrackBreadth::Breadth),
+            })
+    }
+}
+
+impl<T: GeckoStyleCoordConvertible> GeckoStyleCoordConvertible for Option<T> {
+    fn to_gecko_style_coord<U: CoordDataMut>(&self, coord: &mut U) {
+        if let Some(ref me) = *self {
+            me.to_gecko_style_coord(coord);
+        } else {
+            coord.set_value(CoordDataValue::None);
+        }
+    }
+
+    fn from_gecko_style_coord<U: CoordData>(coord: &U) -> Option<Self> {
+        Some(T::from_gecko_style_coord(coord))
+    }
+}
+
+impl GeckoStyleCoordConvertible for Angle {
+    fn to_gecko_style_coord<T: CoordDataMut>(&self, coord: &mut T) {
+        coord.set_value(CoordDataValue::from(*self));
+    }
+
+    fn from_gecko_style_coord<T: CoordData>(coord: &T) -> Option<Self> {
+        match coord.as_value() {
+            CoordDataValue::Degree(val) => Some(Angle::from_degrees(val)),
+            _ => None,
+        }
+    }
+}
+
 /// Convert a given RGBA value to `nscolor`.
 pub fn convert_rgba_to_nscolor(rgba: &RGBA) -> u32 {
     ((rgba.alpha as u32) << 24) |
         ((rgba.blue as u32) << 16) |
         ((rgba.green as u32) << 8) |
         (rgba.red as u32)
 }
 
--- a/servo/components/style/gecko_bindings/sugar/mod.rs
+++ b/servo/components/style/gecko_bindings/sugar/mod.rs
@@ -2,12 +2,13 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
 
 //! Rust sugar and convenience methods for Gecko types.
 
 mod ns_com_ptr;
 mod ns_compatibility;
 mod ns_style_auto_array;
+pub mod ns_style_coord;
 mod ns_t_array;
 pub mod origin_flags;
 pub mod ownership;
 pub mod refptr;
new file mode 100644
--- /dev/null
+++ b/servo/components/style/gecko_bindings/sugar/ns_style_coord.rs
@@ -0,0 +1,369 @@
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+//! Rust helpers for Gecko's `nsStyleCoord`.
+
+use crate::gecko_bindings::bindings;
+use crate::gecko_bindings::structs::nsStyleSides;
+use crate::gecko_bindings::structs::{nsStyleCoord, nsStyleCoord_Calc, nsStyleCoord_CalcValue};
+use crate::gecko_bindings::structs::{nsStyleUnion, nsStyleUnit, nscoord};
+use std::mem;
+
+impl nsStyleCoord {
+    #[inline]
+    /// Get a `null` nsStyleCoord.
+    pub fn null() -> Self {
+        // Can't construct directly because it has private fields
+        let mut coord: Self = unsafe { mem::zeroed() };
+        coord.leaky_set_null();
+        coord
+    }
+}
+
+unsafe impl CoordData for nsStyleCoord {
+    #[inline]
+    fn unit(&self) -> nsStyleUnit {
+        unsafe { *self.get_mUnit() }
+    }
+    #[inline]
+    fn union(&self) -> nsStyleUnion {
+        unsafe { *self.get_mValue() }
+    }
+}
+
+unsafe impl CoordDataMut for nsStyleCoord {
+    unsafe fn values_mut(&mut self) -> (&mut nsStyleUnit, &mut nsStyleUnion) {
+        let unit = self.get_mUnit_mut() as *mut _;
+        let value = self.get_mValue_mut() as *mut _;
+        (&mut *unit, &mut *value)
+    }
+}
+
+impl nsStyleCoord_CalcValue {
+    /// Create an "empty" CalcValue (whose value is `0`).
+    pub fn new() -> Self {
+        nsStyleCoord_CalcValue {
+            mLength: 0,
+            mPercent: 0.0,
+            mHasPercent: false,
+        }
+    }
+}
+
+impl PartialEq for nsStyleCoord_CalcValue {
+    fn eq(&self, other: &Self) -> bool {
+        self.mLength == other.mLength &&
+            self.mPercent == other.mPercent &&
+            self.mHasPercent == other.mHasPercent
+    }
+}
+
+impl nsStyleSides {
+    /// Immutably get the `nsStyleCoord`-like object representing the side at
+    /// index `index`.
+    #[inline]
+    pub fn data_at(&self, index: usize) -> SidesData {
+        SidesData {
+            sides: self,
+            index: index,
+        }
+    }
+
+    /// Mutably get the `nsStyleCoord`-like object representing the side at
+    /// index `index`.
+    #[inline]
+    pub fn data_at_mut(&mut self, index: usize) -> SidesDataMut {
+        SidesDataMut {
+            sides: self,
+            index: index,
+        }
+    }
+}
+
+/// A `nsStyleCoord`-like object on top of an immutable reference to
+/// `nsStyleSides`.
+pub struct SidesData<'a> {
+    sides: &'a nsStyleSides,
+    index: usize,
+}
+
+/// A `nsStyleCoord`-like object on top of an mutable reference to
+/// `nsStyleSides`.
+pub struct SidesDataMut<'a> {
+    sides: &'a mut nsStyleSides,
+    index: usize,
+}
+
+unsafe impl<'a> CoordData for SidesData<'a> {
+    #[inline]
+    fn unit(&self) -> nsStyleUnit {
+        unsafe { self.sides.get_mUnits()[self.index] }
+    }
+    #[inline]
+    fn union(&self) -> nsStyleUnion {
+        unsafe { self.sides.get_mValues()[self.index] }
+    }
+}
+unsafe impl<'a> CoordData for SidesDataMut<'a> {
+    #[inline]
+    fn unit(&self) -> nsStyleUnit {
+        unsafe { self.sides.get_mUnits()[self.index] }
+    }
+    #[inline]
+    fn union(&self) -> nsStyleUnion {
+        unsafe { self.sides.get_mValues()[self.index] }
+    }
+}
+unsafe impl<'a> CoordDataMut for SidesDataMut<'a> {
+    unsafe fn values_mut(&mut self) -> (&mut nsStyleUnit, &mut nsStyleUnion) {
+        let unit = &mut self.sides.get_mUnits_mut()[self.index] as *mut _;
+        let value = &mut self.sides.get_mValues_mut()[self.index] as *mut _;
+        (&mut *unit, &mut *value)
+    }
+}
+
+/// Enum representing the tagged union that is CoordData.
+///
+/// In release mode this should never actually exist in the code, and will be
+/// optimized out by threading matches and inlining.
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum CoordDataValue {
+    /// eStyleUnit_Null
+    Null,
+    /// eStyleUnit_Normal
+    Normal,
+    /// eStyleUnit_Auto
+    Auto,
+    /// eStyleUnit_None
+    None,
+    /// eStyleUnit_Percent
+    Percent(f32),
+    /// eStyleUnit_Factor
+    Factor(f32),
+    /// eStyleUnit_Degree
+    Degree(f32),
+    /// eStyleUnit_FlexFraction
+    FlexFraction(f32),
+    /// eStyleUnit_Coord
+    Coord(nscoord),
+    /// eStyleUnit_Integer
+    Integer(i32),
+    /// eStyleUnit_Enumerated
+    Enumerated(u32),
+    /// eStyleUnit_Calc
+    Calc(nsStyleCoord_CalcValue),
+}
+
+/// A trait to abstract on top of a mutable `nsStyleCoord`-like object.
+pub unsafe trait CoordDataMut: CoordData {
+    /// Get mutably the unit and the union.
+    ///
+    /// This is unsafe since it's possible to modify the unit without changing
+    /// the union.
+    ///
+    /// NB: This can't be two methods since we can't mutably borrow twice
+    unsafe fn values_mut(&mut self) -> (&mut nsStyleUnit, &mut nsStyleUnion);
+
+    /// Clean up any resources used by the union.
+    ///
+    /// Currently, this only happens if the nsStyleUnit is a Calc.
+    #[inline]
+    fn reset(&mut self) {
+        unsafe {
+            if self.unit() == nsStyleUnit::eStyleUnit_Calc {
+                let (unit, union) = self.values_mut();
+                bindings::Gecko_ResetStyleCoord(unit, union);
+            }
+        }
+    }
+
+    #[inline]
+    /// Copies the unit and value from another `CoordData` type.
+    fn copy_from<T: CoordData>(&mut self, other: &T) {
+        unsafe {
+            self.reset();
+            self.copy_from_unchecked(other);
+            self.addref_if_calc();
+        }
+    }
+
+    #[inline]
+    /// Moves the unit and value from another `CoordData` type.
+    fn move_from<T: CoordData>(&mut self, other: T) {
+        unsafe {
+            self.reset();
+            self.copy_from_unchecked(&other);
+        }
+    }
+
+    #[inline]
+    /// Copies the unit and value from another `CoordData` type without checking
+    /// the type of the value (so refcounted values like calc may leak).
+    unsafe fn copy_from_unchecked<T: CoordData>(&mut self, other: &T) {
+        let (unit, union) = self.values_mut();
+        *unit = other.unit();
+        *union = other.union();
+    }
+
+    /// Useful for initializing uninits, given that `set_value` may segfault on
+    /// uninits.
+    fn leaky_set_null(&mut self) {
+        use crate::gecko_bindings::structs::nsStyleUnit::*;
+        unsafe {
+            let (unit, union) = self.values_mut();
+            *unit = eStyleUnit_Null;
+            *union.mInt.as_mut() = 0;
+        }
+    }
+
+    #[inline(always)]
+    /// Sets the inner value.
+    fn set_value(&mut self, value: CoordDataValue) {
+        use self::CoordDataValue::*;
+        use crate::gecko_bindings::structs::nsStyleUnit::*;
+        self.reset();
+        unsafe {
+            let (unit, union) = self.values_mut();
+            match value {
+                Null => {
+                    *unit = eStyleUnit_Null;
+                    *union.mInt.as_mut() = 0;
+                },
+                Normal => {
+                    *unit = eStyleUnit_Normal;
+                    *union.mInt.as_mut() = 0;
+                },
+                Auto => {
+                    *unit = eStyleUnit_Auto;
+                    *union.mInt.as_mut() = 0;
+                },
+                None => {
+                    *unit = eStyleUnit_None;
+                    *union.mInt.as_mut() = 0;
+                },
+                Percent(f) => {
+                    *unit = eStyleUnit_Percent;
+                    *union.mFloat.as_mut() = f;
+                },
+                Factor(f) => {
+                    *unit = eStyleUnit_Factor;
+                    *union.mFloat.as_mut() = f;
+                },
+                Degree(f) => {
+                    *unit = eStyleUnit_Degree;
+                    *union.mFloat.as_mut() = f;
+                },
+                FlexFraction(f) => {
+                    *unit = eStyleUnit_FlexFraction;
+                    *union.mFloat.as_mut() = f;
+                },
+                Coord(coord) => {
+                    *unit = eStyleUnit_Coord;
+                    *union.mInt.as_mut() = coord;
+                },
+                Integer(i) => {
+                    *unit = eStyleUnit_Integer;
+                    *union.mInt.as_mut() = i;
+                },
+                Enumerated(i) => {
+                    *unit = eStyleUnit_Enumerated;
+                    *union.mInt.as_mut() = i as i32;
+                },
+                Calc(calc) => {
+                    // Gecko_SetStyleCoordCalcValue changes the unit internally
+                    bindings::Gecko_SetStyleCoordCalcValue(unit, union, calc);
+                },
+            }
+        }
+    }
+
+    #[inline]
+    /// Gets the `Calc` value mutably, asserts in debug builds if the unit is
+    /// not `Calc`.
+    unsafe fn as_calc_mut(&mut self) -> &mut nsStyleCoord_Calc {
+        debug_assert_eq!(self.unit(), nsStyleUnit::eStyleUnit_Calc);
+        &mut *(*self.union().mPointer.as_mut() as *mut nsStyleCoord_Calc)
+    }
+
+    #[inline]
+    /// Does what it promises, if the unit is `calc`, it bumps the reference
+    /// count _of the calc expression_.
+    fn addref_if_calc(&mut self) {
+        unsafe {
+            if self.unit() == nsStyleUnit::eStyleUnit_Calc {
+                bindings::Gecko_AddRefCalcArbitraryThread(self.as_calc_mut());
+            }
+        }
+    }
+}
+/// A trait to abstract on top of a `nsStyleCoord`-like object.
+pub unsafe trait CoordData {
+    /// Get the unit of this object.
+    fn unit(&self) -> nsStyleUnit;
+    /// Get the `nsStyleUnion` for this object.
+    fn union(&self) -> nsStyleUnion;
+
+    #[inline(always)]
+    /// Get the appropriate value for this object.
+    fn as_value(&self) -> CoordDataValue {
+        use self::CoordDataValue::*;
+        use crate::gecko_bindings::structs::nsStyleUnit::*;
+        unsafe {
+            match self.unit() {
+                eStyleUnit_Null => Null,
+                eStyleUnit_Normal => Normal,
+                eStyleUnit_Auto => Auto,
+                eStyleUnit_None => None,
+                eStyleUnit_Percent => Percent(self.get_float()),
+                eStyleUnit_Factor => Factor(self.get_float()),
+                eStyleUnit_Degree => Degree(self.get_float()),
+                eStyleUnit_FlexFraction => FlexFraction(self.get_float()),
+                eStyleUnit_Coord => Coord(self.get_integer()),
+                eStyleUnit_Integer => Integer(self.get_integer()),
+                eStyleUnit_Enumerated => Enumerated(self.get_integer() as u32),
+                eStyleUnit_Calc => Calc(self.get_calc_value()),
+            }
+        }
+    }
+
+    #[inline]
+    /// Pretend inner value is a float; obtain it.
+    unsafe fn get_float(&self) -> f32 {
+        use crate::gecko_bindings::structs::nsStyleUnit::*;
+        debug_assert!(
+            self.unit() == eStyleUnit_Percent ||
+                self.unit() == eStyleUnit_Factor ||
+                self.unit() == eStyleUnit_Degree ||
+                self.unit() == eStyleUnit_FlexFraction
+        );
+        *self.union().mFloat.as_ref()
+    }
+
+    #[inline]
+    /// Pretend inner value is an int; obtain it.
+    unsafe fn get_integer(&self) -> i32 {
+        use crate::gecko_bindings::structs::nsStyleUnit::*;
+        debug_assert!(
+            self.unit() == eStyleUnit_Coord ||
+                self.unit() == eStyleUnit_Integer ||
+                self.unit() == eStyleUnit_Enumerated
+        );
+        *self.union().mInt.as_ref()
+    }
+
+    #[inline]
+    /// Pretend inner value is a calc; obtain it.
+    /// Ensure that the unit is Calc before calling this.
+    unsafe fn get_calc_value(&self) -> nsStyleCoord_CalcValue {
+        debug_assert_eq!(self.unit(), nsStyleUnit::eStyleUnit_Calc);
+        (*self.as_calc())._base
+    }
+
+    #[inline]
+    /// Pretend the inner value is a calc expression, and obtain it.
+    unsafe fn as_calc(&self) -> &nsStyleCoord_Calc {
+        debug_assert_eq!(self.unit(), nsStyleUnit::eStyleUnit_Calc);
+        &*(*self.union().mPointer.as_ref() as *const nsStyleCoord_Calc)
+    }
+}
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -31,16 +31,17 @@ use crate::gecko_bindings::bindings::Gec
 use crate::gecko_bindings::bindings::Gecko_nsStyleFont_SetLang;
 use crate::gecko_bindings::bindings::Gecko_nsStyleFont_CopyLangFrom;
 use crate::gecko_bindings::bindings::Gecko_SetListStyleImageNone;
 use crate::gecko_bindings::bindings::Gecko_SetListStyleImageImageValue;
 use crate::gecko_bindings::bindings::Gecko_SetNullImageValue;
 use crate::gecko_bindings::structs;
 use crate::gecko_bindings::structs::nsCSSPropertyID;
 use crate::gecko_bindings::structs::mozilla::PseudoStyleType;
+use crate::gecko_bindings::sugar::ns_style_coord::CoordDataMut;
 use crate::gecko_bindings::sugar::refptr::RefPtr;
 use crate::gecko::values::round_border_to_device_pixels;
 use crate::logical_geometry::WritingMode;
 use crate::media_queries::Device;
 use crate::properties::computed_value_flags::*;
 use crate::properties::longhands;
 use crate::rule_tree::StrongRuleNode;
 use crate::selector_parser::PseudoElement;
@@ -1069,16 +1070,17 @@ fn static_assert() {
     % endfor
 </%self:impl_trait>
 
 <% skip_position_longhands = " ".join(x.ident for x in SIDES) %>
 <%self:impl_trait style_struct_name="Position"
                   skip_longhands="${skip_position_longhands} order
                                   align-content justify-content align-self
                                   justify-self align-items justify-items
+                                  grid-auto-rows grid-auto-columns
                                   grid-auto-flow grid-template-rows
                                   grid-template-columns">
     % for side in SIDES:
     <% impl_split_style_coord(side.ident, "mOffset", side.index) %>
     % endfor
 
     % for kind in ["align", "justify"]:
     ${impl_simple_type_with_conversion(kind + "_content")}
@@ -1119,23 +1121,43 @@ fn static_assert() {
 
     pub fn clone_order(&self) -> longhands::order::computed_value::T {
         self.gecko.mOrder
     }
 
     ${impl_simple_copy('order', 'mOrder')}
 
     % for kind in ["rows", "columns"]:
+    pub fn set_grid_auto_${kind}(&mut self, v: longhands::grid_auto_${kind}::computed_value::T) {
+        let gecko = &mut *self.gecko;
+        v.to_gecko_style_coords(&mut gecko.mGridAuto${kind.title()}Min,
+                                &mut gecko.mGridAuto${kind.title()}Max)
+    }
+
+    pub fn copy_grid_auto_${kind}_from(&mut self, other: &Self) {
+        self.gecko.mGridAuto${kind.title()}Min.copy_from(&other.gecko.mGridAuto${kind.title()}Min);
+        self.gecko.mGridAuto${kind.title()}Max.copy_from(&other.gecko.mGridAuto${kind.title()}Max);
+    }
+
+    pub fn reset_grid_auto_${kind}(&mut self, other: &Self) {
+        self.copy_grid_auto_${kind}_from(other)
+    }
+
+    pub fn clone_grid_auto_${kind}(&self) -> longhands::grid_auto_${kind}::computed_value::T {
+        crate::values::generics::grid::TrackSize::from_gecko_style_coords(&self.gecko.mGridAuto${kind.title()}Min,
+                                                                     &self.gecko.mGridAuto${kind.title()}Max)
+    }
+
     pub fn set_grid_template_${kind}(&mut self, v: longhands::grid_template_${kind}::computed_value::T) {
         <% self_grid = "self.gecko.mGridTemplate%s" % kind.title() %>
         use crate::gecko_bindings::structs::nsTArray;
         use std::usize;
         use crate::values::CustomIdent;
         use crate::values::generics::grid::TrackListType::Auto;
-        use crate::values::generics::grid::{GridTemplateComponent, RepeatCount, TrackListValue, MAX_GRID_LINE};
+        use crate::values::generics::grid::{GridTemplateComponent, RepeatCount, MAX_GRID_LINE};
 
         #[inline]
         fn set_line_names(servo_names: &[CustomIdent], gecko_names: &mut nsTArray<structs::RefPtr<structs::nsAtom>>) {
             unsafe {
                 bindings::Gecko_ResizeAtomArray(gecko_names, servo_names.len() as u32);
             }
 
             for (servo_name, gecko_name) in servo_names.iter().zip(gecko_names.iter_mut()) {
@@ -1184,29 +1206,30 @@ fn static_assert() {
                         bindings::Gecko_ResizeAtomArray(
                             &mut value.mRepeatAutoLineNameListAfter, 0);
                     }
                 }
 
                 let mut line_names = track.line_names.into_iter();
                 let mut values_iter = track.values.into_iter();
                 {
-                    for (i, track_size) in value.mTrackSizingFunctions.iter_mut().enumerate().take(max_lines) {
+                    let min_max_iter = value.mMinTrackSizingFunctions.iter_mut()
+                                            .zip(value.mMaxTrackSizingFunctions.iter_mut());
+                    for (i, (gecko_min, gecko_max)) in min_max_iter.enumerate().take(max_lines) {
                         let name_list = line_names.next().expect("expected line-names");
                         set_line_names(&name_list, &mut value.mLineNameLists[i]);
-                        *track_size = if i == auto_idx {
-                            auto_track_size.take().expect("expected <track-size> for <auto-track-repeat>")
-                        } else {
-                            match values_iter.next().expect("expected <track-size> value") {
-                                TrackListValue::TrackSize(size) => size,
-                                // FIXME(emilio): This shouldn't be
-                                // representable in the first place.
-                                TrackListValue::TrackRepeat(..) => unreachable!("Shouldn't have track-repeats in computed track lists"),
-                            }
-                        };
+                        if i == auto_idx {
+                            let track_size = auto_track_size.take()
+                                .expect("expected <track-size> for <auto-track-repeat>");
+                            track_size.to_gecko_style_coords(gecko_min, gecko_max);
+                            continue
+                        }
+
+                        let track_size = values_iter.next().expect("expected <track-size> value");
+                        track_size.to_gecko_style_coords(gecko_min, gecko_max);
                     }
                 }
 
                 let final_names = line_names.next().unwrap();
                 set_line_names(&final_names, value.mLineNameLists.last_mut().unwrap());
 
                 value
             },
@@ -1251,17 +1274,17 @@ fn static_assert() {
         self.copy_grid_template_${kind}_from(other)
     }
 
     pub fn clone_grid_template_${kind}(&self) -> longhands::grid_template_${kind}::computed_value::T {
         <% self_grid = "self.gecko.mGridTemplate%s" % kind.title() %>
         use crate::gecko_bindings::structs::nsTArray;
         use crate::values::CustomIdent;
         use crate::values::generics::grid::{GridTemplateComponent, LineNameList, RepeatCount};
-        use crate::values::generics::grid::{TrackList, TrackListType, TrackListValue, TrackRepeat};
+        use crate::values::generics::grid::{TrackList, TrackListType, TrackListValue, TrackRepeat, TrackSize};
 
         let value = match unsafe { ${self_grid}.mPtr.as_ref() } {
             None => return GridTemplateComponent::None,
             Some(value) => value,
         };
 
         #[inline]
         fn to_boxed_customident_slice(gecko_names: &nsTArray<structs::RefPtr<structs::nsAtom>>) -> Box<[CustomIdent]> {
@@ -1293,18 +1316,23 @@ fn static_assert() {
             };
             let names = names_vec.into_boxed_slice();
 
             GridTemplateComponent::Subgrid(LineNameList{names, fill_idx})
         } else {
             let mut auto_repeat = None;
             let mut list_type = TrackListType::Normal;
             let line_names = to_line_names_vec(&value.mLineNameLists).into_boxed_slice();
-            let mut values = Vec::with_capacity(value.mTrackSizingFunctions.len());
-            for (i, track_size) in value.mTrackSizingFunctions.iter().enumerate() {
+            let mut values = Vec::with_capacity(value.mMinTrackSizingFunctions.len());
+
+            let min_max_iter = value.mMinTrackSizingFunctions.iter()
+                .zip(value.mMaxTrackSizingFunctions.iter());
+            for (i, (gecko_min, gecko_max)) in min_max_iter.enumerate() {
+                let track_size = TrackSize::from_gecko_style_coords(gecko_min, gecko_max);
+
                 if i == repeat_auto_index {
                     list_type = TrackListType::Auto(repeat_auto_index as u16);
 
                     let count = if value.mIsAutoFill() {
                         RepeatCount::AutoFill
                     } else {
                         RepeatCount::AutoFit
                     };
@@ -1313,21 +1341,21 @@ fn static_assert() {
                         let mut vec: Vec<Box<[CustomIdent]>> = Vec::with_capacity(2);
                         vec.push(to_boxed_customident_slice(
                             &value.mRepeatAutoLineNameListBefore));
                         vec.push(to_boxed_customident_slice(
                             &value.mRepeatAutoLineNameListAfter));
                         vec.into_boxed_slice()
                     };
 
-                    let track_sizes = vec!(track_size.clone());
+                    let track_sizes = vec!(track_size);
 
                     auto_repeat = Some(TrackRepeat{count, line_names, track_sizes});
                 } else {
-                    values.push(TrackListValue::TrackSize(track_size.clone()));
+                    values.push(TrackListValue::TrackSize(track_size));
                 }
             }
 
             GridTemplateComponent::TrackList(TrackList{list_type, values, line_names, auto_repeat})
         }
     }
     % endfor
 
--- a/servo/components/style/values/computed/mod.rs
+++ b/servo/components/style/values/computed/mod.rs
@@ -2,18 +2,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
 
 //! Computed values.
 
 use self::transform::DirectionVector;
 use super::animated::ToAnimatedValue;
 use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent;
-use super::generics::grid::{GenericGridLine, GenericTrackBreadth};
-use super::generics::grid::{TrackList as GenericTrackList, GenericTrackSize};
+use super::generics::grid::{GenericGridLine, TrackBreadth as GenericTrackBreadth};
+use super::generics::grid::{TrackList as GenericTrackList, TrackSize as GenericTrackSize};
 use super::generics::transform::IsParallelTo;
 use super::generics::{self, GreaterThanOrEqualToOne, NonNegative, ZeroToOne};
 use super::specified;
 use super::{CSSFloat, CSSInteger};
 use crate::context::QuirksMode;
 use crate::font_metrics::{get_metrics_provider_for_product, FontMetricsProvider};
 use crate::media_queries::Device;
 #[cfg(feature = "gecko")]
--- a/servo/components/style/values/generics/grid.rs
+++ b/servo/components/style/values/generics/grid.rs
@@ -2,16 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
 
 //! Generic types for the handling of
 //! [grids](https://drafts.csswg.org/css-grid/).
 
 use crate::{Atom, Zero};
 use crate::parser::{Parse, ParserContext};
+use crate::values::computed::{Context, ToComputedValue};
 use crate::values::specified;
 use crate::values::specified::grid::parse_line_names;
 use crate::values::{CSSFloat, CustomIdent};
 use cssparser::Parser;
 use std::fmt::{self, Write};
 use std::{cmp, mem, usize};
 use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
 
@@ -174,90 +175,96 @@ impl Parse for GridLine<specified::Integ
                 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
             }
         }
 
         Ok(grid_line)
     }
 }
 
+#[allow(missing_docs)]
+#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
+#[derive(
+    Animate,
+    Clone,
+    Copy,
+    Debug,
+    Eq,
+    MallocSizeOf,
+    Parse,
+    PartialEq,
+    SpecifiedValueInfo,
+    ToComputedValue,
+    ToCss,
+    ToResolvedValue,
+    ToShmem,
+)]
+pub enum TrackKeyword {
+    Auto,
+    MaxContent,
+    MinContent,
+}
+
 /// A track breadth for explicit grid track sizing. It's generic solely to
 /// avoid re-implementing it for the computed type.
 ///
 /// <https://drafts.csswg.org/css-grid/#typedef-track-breadth>
-///
-/// cbindgen:derive-tagged-enum-copy-constructor=true
 #[derive(
     Animate,
     Clone,
     Debug,
     MallocSizeOf,
     PartialEq,
     SpecifiedValueInfo,
     ToComputedValue,
     ToCss,
     ToResolvedValue,
     ToShmem,
 )]
-#[repr(C, u8)]
-pub enum GenericTrackBreadth<L> {
+pub enum TrackBreadth<L> {
     /// The generic type is almost always a non-negative `<length-percentage>`
     Breadth(L),
     /// A flex fraction specified in `fr` units.
     #[css(dimension)]
     Fr(CSSFloat),
-    /// `auto`
-    Auto,
-    /// `min-content`
-    MinContent,
-    /// `max-content`
-    MaxContent,
+    /// One of the track-sizing keywords (`auto`, `min-content`, `max-content`)
+    Keyword(TrackKeyword),
 }
 
-pub use self::GenericTrackBreadth as TrackBreadth;
-
 impl<L> TrackBreadth<L> {
     /// Check whether this is a `<fixed-breadth>` (i.e., it only has `<length-percentage>`)
     ///
     /// <https://drafts.csswg.org/css-grid/#typedef-fixed-breadth>
     #[inline]
     pub fn is_fixed(&self) -> bool {
         matches!(*self, TrackBreadth::Breadth(..))
     }
 }
 
 /// A `<track-size>` type for explicit grid track sizing. Like `<track-breadth>`, this is
 /// generic only to avoid code bloat. It only takes `<length-percentage>`
 ///
 /// <https://drafts.csswg.org/css-grid/#typedef-track-size>
-///
-/// cbindgen:derive-tagged-enum-copy-constructor=true
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem)]
-#[repr(C, u8)]
-pub enum GenericTrackSize<L> {
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToResolvedValue, ToShmem)]
+pub enum TrackSize<L> {
     /// A flexible `<track-breadth>`
-    Breadth(GenericTrackBreadth<L>),
+    Breadth(TrackBreadth<L>),
     /// A `minmax` function for a range over an inflexible `<track-breadth>`
     /// and a flexible `<track-breadth>`
     ///
     /// <https://drafts.csswg.org/css-grid/#valdef-grid-template-columns-minmax>
     #[css(function)]
-    Minmax(GenericTrackBreadth<L>, GenericTrackBreadth<L>),
+    Minmax(TrackBreadth<L>, TrackBreadth<L>),
     /// A `fit-content` function.
     ///
-    /// This stores a TrackBreadth<L> for convenience, but it can only be a
-    /// LengthPercentage.
-    ///
     /// <https://drafts.csswg.org/css-grid/#valdef-grid-template-columns-fit-content>
     #[css(function)]
-    FitContent(GenericTrackBreadth<L>),
+    FitContent(L),
 }
 
-pub use self::GenericTrackSize as TrackSize;
-
 impl<L> TrackSize<L> {
     /// Check whether this is a `<fixed-size>`
     ///
     /// <https://drafts.csswg.org/css-grid/#typedef-fixed-size>
     pub fn is_fixed(&self) -> bool {
         match *self {
             TrackSize::Breadth(ref breadth) => breadth.is_fixed(),
             // For minmax function, it could be either
@@ -276,17 +283,17 @@ impl<L> TrackSize<L> {
             },
             TrackSize::FitContent(_) => false,
         }
     }
 }
 
 impl<L> Default for TrackSize<L> {
     fn default() -> Self {
-        TrackSize::Breadth(TrackBreadth::Auto)
+        TrackSize::Breadth(TrackBreadth::Keyword(TrackKeyword::Auto))
     }
 }
 
 impl<L: PartialEq> TrackSize<L> {
     /// Returns true if current TrackSize is same as default.
     pub fn is_default(&self) -> bool {
         *self == TrackSize::default()
     }
@@ -297,17 +304,17 @@ impl<L: ToCss> ToCss for TrackSize<L> {
     where
         W: Write,
     {
         match *self {
             TrackSize::Breadth(ref breadth) => breadth.to_css(dest),
             TrackSize::Minmax(ref min, ref max) => {
                 // According to gecko minmax(auto, <flex>) is equivalent to <flex>,
                 // and both are serialized as <flex>.
-                if let TrackBreadth::Auto = *min {
+                if let TrackBreadth::Keyword(TrackKeyword::Auto) = *min {
                     if let TrackBreadth::Fr(_) = *max {
                         return max.to_css(dest);
                     }
                 }
 
                 dest.write_str("minmax(")?;
                 min.to_css(dest)?;
                 dest.write_str(", ")?;
@@ -318,16 +325,58 @@ impl<L: ToCss> ToCss for TrackSize<L> {
                 dest.write_str("fit-content(")?;
                 lp.to_css(dest)?;
                 dest.write_str(")")
             },
         }
     }
 }
 
+impl<L: ToComputedValue> ToComputedValue for TrackSize<L> {
+    type ComputedValue = TrackSize<L::ComputedValue>;
+
+    #[inline]
+    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
+        match *self {
+            TrackSize::Breadth(TrackBreadth::Fr(ref f)) => {
+                // <flex> outside `minmax()` expands to `mimmax(auto, <flex>)`
+                // https://drafts.csswg.org/css-grid/#valdef-grid-template-columns-flex
+                // FIXME(nox): This sounds false, the spec just says that <flex>
+                // implies `minmax(auto, <flex>)`, not that it should be changed
+                // into `minmax` at computed value time.
+                TrackSize::Minmax(
+                    TrackBreadth::Keyword(TrackKeyword::Auto),
+                    TrackBreadth::Fr(f.to_computed_value(context)),
+                )
+            },
+            TrackSize::Breadth(ref b) => TrackSize::Breadth(b.to_computed_value(context)),
+            TrackSize::Minmax(ref b1, ref b2) => {
+                TrackSize::Minmax(b1.to_computed_value(context), b2.to_computed_value(context))
+            },
+            TrackSize::FitContent(ref lp) => TrackSize::FitContent(lp.to_computed_value(context)),
+        }
+    }
+
+    #[inline]
+    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
+        match *computed {
+            TrackSize::Breadth(ref b) => {
+                TrackSize::Breadth(ToComputedValue::from_computed_value(b))
+            },
+            TrackSize::Minmax(ref b1, ref b2) => TrackSize::Minmax(
+                ToComputedValue::from_computed_value(b1),
+                ToComputedValue::from_computed_value(b2),
+            ),
+            TrackSize::FitContent(ref lp) => {
+                TrackSize::FitContent(ToComputedValue::from_computed_value(lp))
+            },
+        }
+    }
+}
+
 /// Helper function for serializing identifiers with a prefix and suffix, used
 /// for serializing <line-names> (in grid).
 pub fn concat_serialize_idents<W>(
     prefix: &str,
     suffix: &str,
     slice: &[CustomIdent],
     sep: &str,
     dest: &mut CssWriter<W>,
--- a/servo/components/style/values/specified/grid.rs
+++ b/servo/components/style/values/specified/grid.rs
@@ -3,17 +3,17 @@
  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
 
 //! CSS handling for the computed value of
 //! [grids](https://drafts.csswg.org/css-grid/)
 
 use crate::parser::{Parse, ParserContext};
 use crate::values::computed::{self, Context, ToComputedValue};
 use crate::values::generics::grid::{GridTemplateComponent, RepeatCount, TrackBreadth};
-use crate::values::generics::grid::{LineNameList, TrackRepeat, TrackSize};
+use crate::values::generics::grid::{LineNameList, TrackKeyword, TrackRepeat, TrackSize};
 use crate::values::generics::grid::{TrackList, TrackListType, TrackListValue};
 use crate::values::specified::{Integer, LengthPercentage};
 use crate::values::{CSSFloat, CustomIdent};
 use cssparser::{ParseError as CssParseError, Parser, Token};
 use std::mem;
 use style_traits::{ParseError, StyleParseErrorKind};
 
 /// Parse a single flexible length.
@@ -22,53 +22,30 @@ pub fn parse_flex<'i, 't>(input: &mut Pa
     match *input.next()? {
         Token::Dimension {
             value, ref unit, ..
         } if unit.eq_ignore_ascii_case("fr") && value.is_sign_positive() => Ok(value),
         ref t => Err(location.new_unexpected_token_error(t.clone())),
     }
 }
 
-impl<L> TrackBreadth<L> {
-    fn parse_keyword<'i, 't>(
-        input: &mut Parser<'i, 't>,
-    ) -> Result<Self, ParseError<'i>> {
-        #[derive(Parse)]
-        enum TrackKeyword {
-            Auto,
-            MaxContent,
-            MinContent,
-        }
-
-        Ok(match TrackKeyword::parse(input)? {
-            TrackKeyword::Auto => TrackBreadth::Auto,
-            TrackKeyword::MaxContent => TrackBreadth::MaxContent,
-            TrackKeyword::MinContent => TrackBreadth::MinContent,
-        })
-    }
-}
-
 impl Parse for TrackBreadth<LengthPercentage> {
     fn parse<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Self, ParseError<'i>> {
-        // FIXME: This and other callers in this file should use
-        // NonNegativeLengthPercentage instead.
-        //
-        // Though it seems these cannot be animated so it's ~ok.
         if let Ok(lp) = input.try(|i| LengthPercentage::parse_non_negative(context, i)) {
             return Ok(TrackBreadth::Breadth(lp));
         }
 
         if let Ok(f) = input.try(parse_flex) {
             return Ok(TrackBreadth::Fr(f));
         }
 
-        Self::parse_keyword(input)
+        TrackKeyword::parse(input).map(TrackBreadth::Keyword)
     }
 }
 
 impl Parse for TrackSize<LengthPercentage> {
     fn parse<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Self, ParseError<'i>> {
@@ -76,30 +53,33 @@ impl Parse for TrackSize<LengthPercentag
             return Ok(TrackSize::Breadth(b));
         }
 
         if input.try(|i| i.expect_function_matching("minmax")).is_ok() {
             return input.parse_nested_block(|input| {
                 let inflexible_breadth =
                     match input.try(|i| LengthPercentage::parse_non_negative(context, i)) {
                         Ok(lp) => TrackBreadth::Breadth(lp),
-                        Err(..) => TrackBreadth::parse_keyword(input)?,
+                        Err(..) => {
+                            let keyword = TrackKeyword::parse(input)?;
+                            TrackBreadth::Keyword(keyword)
+                        },
                     };
 
                 input.expect_comma()?;
                 Ok(TrackSize::Minmax(
                     inflexible_breadth,
                     TrackBreadth::parse(context, input)?,
                 ))
             });
         }
 
         input.expect_function_matching("fit-content")?;
         let lp = input.parse_nested_block(|i| LengthPercentage::parse_non_negative(context, i))?;
-        Ok(TrackSize::FitContent(TrackBreadth::Breadth(lp)))
+        Ok(TrackSize::FitContent(lp))
     }
 }
 
 /// Parse the grid line names into a vector of owned strings.
 ///
 /// <https://drafts.csswg.org/css-grid/#typedef-line-names>
 pub fn parse_line_names<'i, 't>(
     input: &mut Parser<'i, 't>,
--- a/servo/ports/geckolib/cbindgen.toml
+++ b/servo/ports/geckolib/cbindgen.toml
@@ -153,18 +153,16 @@ include = [
   "BorderImageWidth",
   "ComputedUrl",
   "ComputedImageUrl",
   "UrlOrNone",
   "Filter",
   "Gradient",
   "GridTemplateAreas",
   "GridLine",
-  "TrackSize",
-  "TrackBreadth",
 ]
 item_types = ["enums", "structs", "typedefs", "functions", "constants"]
 renaming_overrides_prefixing = true
 
 # Prevent some renaming for Gecko types that cbindgen doesn't otherwise understand.
 [export.rename]
 "nscolor" = "nscolor"
 "nsAtom" = "nsAtom"
@@ -216,16 +214,17 @@ renaming_overrides_prefixing = true
 [export.body]
 "CSSPixelLength" = """
   inline nscoord ToAppUnits() const;
   inline bool IsZero() const;
   float ToCSSPixels() const { return _0; }
 """
 
 "LengthPercentage" = """
+  // Defined in nsStyleCoord.h
   static constexpr inline StyleLengthPercentage Zero();
   static inline StyleLengthPercentage FromAppUnits(nscoord);
   static inline StyleLengthPercentage FromPixels(CSSCoord);
   static inline StyleLengthPercentage FromPercentage(float);
   inline CSSCoord LengthInCSSPixels() const;
   inline float Percentage() const;
   inline bool HasPercent() const;
   inline bool ConvertsToLength() const;
@@ -286,16 +285,17 @@ renaming_overrides_prefixing = true
   static inline StyleGenericPosition FromPercentage(float);
 """
 
 "GenericBackgroundSize" = """
   bool IsInitialValue() const;
 """
 
 "Rect" = """
+  // Defined in nsStyleCoord.h
   template<typename Predicate> inline bool All(Predicate) const;
   template<typename Predicate> inline bool Any(Predicate) const;
 
   // Defined in WritingModes.h
   inline const T& Get(mozilla::Side) const;
   inline const T& Get(mozilla::WritingMode, mozilla::LogicalSide) const;
   inline const T& GetIStart(mozilla::WritingMode) const;
   inline const T& GetBStart(mozilla::WritingMode) const;
@@ -602,29 +602,8 @@ renaming_overrides_prefixing = true
 
 "GenericGridLine" = """
   // Returns the `auto` value.
   inline StyleGenericGridLine();
   inline bool IsAuto() const;
   // The line name, or nsGkAtoms::_empty if not present.
   inline nsAtom* LineName() const;
 """
-
-"GenericTrackBreadth" = """
- private:
-  // Private default constructor without initialization so that the helper
-  // constructor functions still work as expected. They take care of
-  // initializing the fields properly.
-  StyleGenericTrackBreadth() {}
- public:
-  inline bool HasPercent() const;
-"""
-
-"GenericTrackSize" = """
- private:
-  // Private default constructor without initialization so that the helper
-  // constructor functions still work as expected. They take care of
-  // initializing the fields properly.
-  StyleGenericTrackSize() {}
- public:
-  inline const StyleGenericTrackBreadth<L>& GetMin() const;
-  inline const StyleGenericTrackBreadth<L>& GetMax() const;
-"""