Bug 1527410 - Use Rust sizes for flex-basis, width, height, and their min/max properties. r=jwatt
authorEmilio Cobos Álvarez <emilio@crisal.io>
Sun, 10 Feb 2019 13:55:16 +0100
changeset 459525 bf8e35be76d809477626dd0ebd4e6b0b4c89bd36
parent 459524 7436c0f5b8b1583d20d5ea2d1d9d3b2c665bdf33
child 459526 795f2e303548acd4d239e681331daa224cdab497
push id35562
push usercsabou@mozilla.com
push dateFri, 15 Feb 2019 18:40:35 +0000
treeherdermozilla-central@8961019ee4c6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwatt
bugs1527410, 957915
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1527410 - Use Rust sizes for flex-basis, width, height, and their min/max properties. r=jwatt Really sorry for the size of the patch :( Only intentional behavior change is in the uses of HasLengthAndPercentage(), where it's easier to do the right thing. The checks that used to check for (IsCalcUnit() && CalcHasPercentage()) are wrong since bug 957915. Differential Revision: https://phabricator.services.mozilla.com/D19553
Cargo.lock
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
layout/forms/nsRangeFrame.cpp
layout/forms/nsTextControlFrame.cpp
layout/generic/ReflowInput.cpp
layout/generic/ReflowInput.h
layout/generic/WritingModes.h
layout/generic/nsAbsoluteContainingBlock.cpp
layout/generic/nsBlockFrame.cpp
layout/generic/nsContainerFrame.cpp
layout/generic/nsFlexContainerFrame.cpp
layout/generic/nsFlexContainerFrame.h
layout/generic/nsFrame.cpp
layout/generic/nsGridContainerFrame.cpp
layout/generic/nsIFrame.h
layout/generic/nsImageFrame.cpp
layout/generic/nsLineLayout.cpp
layout/style/ServoBindings.toml
layout/style/nsCSSProps.cpp
layout/style/nsCSSProps.h
layout/style/nsComputedDOMStyle.cpp
layout/style/nsComputedDOMStyle.h
layout/style/nsStyleConsts.h
layout/style/nsStyleCoord.h
layout/style/nsStyleStruct.cpp
layout/style/nsStyleStruct.h
layout/svg/nsSVGOuterSVGFrame.cpp
layout/tables/BasicTableLayoutStrategy.cpp
layout/tables/FixedTableLayoutStrategy.cpp
layout/tables/nsTableFrame.cpp
layout/tables/nsTableRowFrame.cpp
layout/tables/nsTableRowGroupFrame.cpp
layout/xul/nsBox.cpp
layout/xul/tree/nsTreeBodyFrame.cpp
servo/components/style/cbindgen.toml
servo/components/style/gecko/values.rs
servo/components/style/properties/gecko.mako.rs
servo/components/style/properties/longhands/position.mako.rs
servo/components/style/values/computed/flex.rs
servo/components/style/values/generics/flex.rs
servo/components/style/values/generics/grid.rs
servo/components/style/values/generics/length.rs
servo/components/style/values/specified/flex.rs
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1,9 +1,11 @@
-[[package]]
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+[[package]]
 name = "Inflector"
 version = "0.11.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -4606,164 +4606,138 @@ bool nsLayoutUtils::IsViewportScrollbarF
 
   if (!IsProperAncestorFrame(rootScrollFrame, aFrame)) return false;
 
   nsIFrame* rootScrolledFrame = rootScrollableFrame->GetScrolledFrame();
   return !(rootScrolledFrame == aFrame ||
            IsProperAncestorFrame(rootScrolledFrame, aFrame));
 }
 
-// Use only for paddings, since it clamps negative calc() to 0.
-static bool GetAbsoluteCoord(const LengthPercentage& aStyle, nscoord& aResult) {
+/**
+ * Use only for paddings / widths / heights, since it clamps negative calc() to
+ * 0.
+ */
+template <typename LengthPercentageLike>
+static bool GetAbsoluteCoord(const LengthPercentageLike& aStyle,
+                             nscoord& aResult) {
   if (!aStyle.ConvertsToLength()) {
     return false;
   }
   aResult = std::max(0, aStyle.ToLength());
   return true;
 }
 
-// Use only for widths/heights (or their min/max), since it clamps
-// negative calc() results to 0.
-static bool GetAbsoluteCoord(const nsStyleCoord& aStyle, nscoord& aResult) {
-  if (aStyle.IsCalcUnit()) {
-    if (aStyle.CalcHasPercent()) {
-      return false;
-    }
-    // If it has no percents, we can pass 0 for the percentage basis.
-    aResult = aStyle.ComputeComputedCalc(0);
-    if (aResult < 0) aResult = 0;
-    return true;
-  }
-
-  if (eStyleUnit_Coord != aStyle.GetUnit()) return false;
-
-  aResult = aStyle.GetCoordValue();
-  NS_ASSERTION(aResult >= 0, "negative widths not allowed");
-  return true;
-}
-
 static nscoord GetBSizeTakenByBoxSizing(StyleBoxSizing aBoxSizing,
                                         nsIFrame* aFrame, bool aHorizontalAxis,
                                         bool aIgnorePadding);
 
+static bool GetPercentBSize(const LengthPercentage& aStyle, nsIFrame* aFrame,
+                            bool aHorizontalAxis, nscoord& aResult);
+
 // Only call on style coords for which GetAbsoluteCoord returned false.
-static bool GetPercentBSize(const nsStyleCoord& aStyle, nsIFrame* aFrame,
+template <typename SizeOrMaxSize>
+static bool GetPercentBSize(const SizeOrMaxSize& aStyle, nsIFrame* aFrame,
                             bool aHorizontalAxis, nscoord& aResult) {
-  if (eStyleUnit_Percent != aStyle.GetUnit() && !aStyle.IsCalcUnit())
+  if (!aStyle.IsLengthPercentage()) {
     return false;
-
-  MOZ_ASSERT(!aStyle.IsCalcUnit() || aStyle.CalcHasPercent(),
+  }
+  return GetPercentBSize(aStyle.AsLengthPercentage(), aFrame, aHorizontalAxis,
+                         aResult);
+}
+
+static bool GetPercentBSize(const LengthPercentage& aStyle, nsIFrame* aFrame,
+                            bool aHorizontalAxis, nscoord& aResult) {
+  if (!aStyle.HasPercent()) {
+    return false;
+  }
+
+  MOZ_ASSERT(!aStyle.ConvertsToLength(),
              "GetAbsoluteCoord should have handled this");
 
   // During reflow, nsHTMLScrollFrame::ReflowScrolledFrame uses
   // SetComputedHeight on the reflow state for its child to propagate its
   // computed height to the scrolled content. So here we skip to the scroll
   // frame that contains this scrolled content in order to get the same
   // behavior as layout when computing percentage heights.
   nsIFrame* f = aFrame->GetContainingBlock(nsIFrame::SKIP_SCROLLED_FRAME);
   if (!f) {
     MOZ_ASSERT_UNREACHABLE("top of frame tree not a containing block");
     return false;
   }
 
   WritingMode wm = f->GetWritingMode();
 
   const nsStylePosition* pos = f->StylePosition();
-  const nsStyleCoord& bSizeCoord = pos->BSize(wm);
+  const auto& bSizeCoord = pos->BSize(wm);
   nscoord h;
   if (!GetAbsoluteCoord(bSizeCoord, h) &&
       !GetPercentBSize(bSizeCoord, f, aHorizontalAxis, h)) {
-    NS_ASSERTION(bSizeCoord.IsAutoOrEnum() || bSizeCoord.HasPercent(),
+    NS_ASSERTION(bSizeCoord.IsAuto() || bSizeCoord.IsExtremumLength() ||
+                     bSizeCoord.HasPercent(),
                  "unknown block-size unit");
     LayoutFrameType fType = f->Type();
     if (fType != LayoutFrameType::Viewport &&
         fType != LayoutFrameType::Canvas &&
         fType != LayoutFrameType::PageContent) {
       // There's no basis for the percentage height, so it acts like auto.
       // Should we consider a max-height < min-height pair a basis for
       // percentage heights?  The spec is somewhat unclear, and not doing
       // so is simpler and avoids troubling discontinuities in behavior,
       // so I'll choose not to. -LDB
       return false;
     }
 
     NS_ASSERTION(
-        bSizeCoord.IsAutoOrEnum(),
+        bSizeCoord.IsAuto() || bSizeCoord.IsExtremumLength(),
         "Unexpected block-size unit for viewport or canvas or page-content");
     // For the viewport, canvas, and page-content kids, the percentage
     // basis is just the parent block-size.
     h = f->BSize(wm);
     if (h == NS_UNCONSTRAINEDSIZE) {
       // We don't have a percentage basis after all
       return false;
     }
   }
 
-  const nsStyleCoord& maxBSizeCoord = pos->MaxBSize(wm);
+  const auto& maxBSizeCoord = pos->MaxBSize(wm);
 
   nscoord maxh;
   if (GetAbsoluteCoord(maxBSizeCoord, maxh) ||
       GetPercentBSize(maxBSizeCoord, f, aHorizontalAxis, maxh)) {
     if (maxh < h) h = maxh;
   } else {
-    NS_ASSERTION(maxBSizeCoord.IsAutoOrEnum() || maxBSizeCoord.HasPercent(),
+    NS_ASSERTION(maxBSizeCoord.IsNone() || maxBSizeCoord.IsExtremumLength() ||
+                     maxBSizeCoord.HasPercent(),
                  "unknown max block-size unit");
   }
 
-  const nsStyleCoord& minBSizeCoord = pos->MinBSize(wm);
+  const auto& minBSizeCoord = pos->MinBSize(wm);
 
   nscoord minh;
   if (GetAbsoluteCoord(minBSizeCoord, minh) ||
       GetPercentBSize(minBSizeCoord, f, aHorizontalAxis, minh)) {
     if (minh > h) h = minh;
   } else {
-    NS_ASSERTION(minBSizeCoord.IsAutoOrEnum() || minBSizeCoord.HasPercent(),
+    NS_ASSERTION(minBSizeCoord.IsAuto() || minBSizeCoord.IsExtremumLength() ||
+                     minBSizeCoord.HasPercent(),
                  "unknown min block-size unit");
   }
 
   // Now adjust h for box-sizing styles on the parent.  We never ignore padding
   // here.  That could conceivably cause some problems with fieldsets (which are
   // the one place that wants to ignore padding), but solving that here without
   // hardcoding a check for f being a fieldset-content frame is a bit of a pain.
   nscoord bSizeTakenByBoxSizing =
       GetBSizeTakenByBoxSizing(pos->mBoxSizing, f, aHorizontalAxis, false);
   h = std::max(0, h - bSizeTakenByBoxSizing);
 
-  if (aStyle.IsCalcUnit()) {
-    aResult = std::max(aStyle.ComputeComputedCalc(h), 0);
-    return true;
-  }
-
-  aResult = NSToCoordRound(aStyle.GetPercentValue() * h);
+  aResult = std::max(aStyle.Resolve(h), 0);
   return true;
 }
 
-static bool GetPercentBSize(const LengthPercentage& aStyle, nsIFrame* aFrame,
-                            bool aHorizontalAxis, nscoord& aResult) {
-  if (!aStyle.HasPercent()) {
-    return false;
-  }
-
-  // TODO(emilio): Temporary until I convert width / height to Servo too.
-  nsStyleCoord coord;
-  if (aStyle.was_calc) {
-    nscoord length = CSSPixel::ToAppUnits(aStyle.LengthInCSSPixels());
-    float percent = aStyle.Percentage();
-    auto* calc = new nsStyleCoord::Calc();
-    calc->mLength = length;
-    calc->mPercent = percent;
-    calc->mHasPercent = true;
-    coord.SetCalcValue(calc);  // Takes ownership.
-  } else {
-    coord.SetPercentValue(aStyle.Percentage());
-  }
-
-  return GetPercentBSize(coord, aFrame, aHorizontalAxis, aResult);
-}
-
 // Return true if aStyle can be resolved to a definite value and if so
 // return that value in aResult.
 static bool GetDefiniteSize(const LengthPercentage& aStyle, nsIFrame* aFrame,
                             bool aIsInlineAxis,
                             const Maybe<LogicalSize>& aPercentageBasis,
                             nscoord* aResult) {
   if (aStyle.ConvertsToLength()) {
     *aResult = aStyle.ToLength();
@@ -4781,60 +4755,26 @@ static bool GetDefiniteSize(const Length
     return false;
   }
   *aResult = std::max(0, aStyle.Resolve(pb));
   return true;
 }
 
 // Return true if aStyle can be resolved to a definite value and if so
 // return that value in aResult.
-static bool GetDefiniteSize(const nsStyleCoord& aStyle, nsIFrame* aFrame,
+template <typename SizeOrMaxSize>
+static bool GetDefiniteSize(const SizeOrMaxSize& aStyle, nsIFrame* aFrame,
                             bool aIsInlineAxis,
                             const Maybe<LogicalSize>& aPercentageBasis,
                             nscoord* aResult) {
-  switch (aStyle.GetUnit()) {
-    case eStyleUnit_Coord:
-      *aResult = aStyle.GetCoordValue();
-      return true;
-    case eStyleUnit_Percent: {
-      if (aPercentageBasis.isNothing()) {
-        return false;
-      }
-      auto wm = aFrame->GetWritingMode();
-      nscoord pb = aIsInlineAxis ? aPercentageBasis.value().ISize(wm)
-                                 : aPercentageBasis.value().BSize(wm);
-      if (pb != NS_UNCONSTRAINEDSIZE) {
-        nscoord p = NSToCoordFloorClamped(pb * aStyle.GetPercentValue());
-        *aResult = std::max(nscoord(0), p);
-        return true;
-      }
-      return false;
-    }
-    case eStyleUnit_Calc: {
-      nsStyleCoord::Calc* calc = aStyle.GetCalcValue();
-      if (calc->mPercent != 0.0f) {
-        if (aPercentageBasis.isNothing()) {
-          return false;
-        }
-        auto wm = aFrame->GetWritingMode();
-        nscoord pb = aIsInlineAxis ? aPercentageBasis.value().ISize(wm)
-                                   : aPercentageBasis.value().BSize(wm);
-        if (pb == NS_UNCONSTRAINEDSIZE) {
-          return false;
-        }
-        *aResult = std::max(
-            0, calc->mLength + NSToCoordFloorClamped(pb * calc->mPercent));
-      } else {
-        *aResult = std::max(0, calc->mLength);
-      }
-      return true;
-    }
-    default:
-      return false;
-  }
+  if (!aStyle.IsLengthPercentage()) {
+    return false;
+  }
+  return GetDefiniteSize(aStyle.AsLengthPercentage(), aFrame, aIsInlineAxis,
+                         aPercentageBasis, aResult);
 }
 
 //
 // NOTE: this function will be replaced by GetDefiniteSizeTakenByBoxSizing (bug
 // 1363918). Please do not add new uses of this function.
 //
 // Get the amount of vertical space taken out of aFrame's content area due to
 // its borders and paddings given the box-sizing value in aBoxSizing.  We don't
@@ -4922,55 +4862,61 @@ static nscoord GetDefiniteSizeTakenByBox
   return sizeTakenByBoxSizing;
 }
 
 // Handles only max-content and min-content, and
 // -moz-fit-content for min-width and max-width, since the others
 // (-moz-fit-content for width, and -moz-available) have no effect on
 // intrinsic widths.
 enum eWidthProperty { PROP_WIDTH, PROP_MAX_WIDTH, PROP_MIN_WIDTH };
-static bool GetIntrinsicCoord(const nsStyleCoord& aStyle,
+static bool GetIntrinsicCoord(StyleExtremumLength aStyle,
                               gfxContext* aRenderingContext, nsIFrame* aFrame,
                               eWidthProperty aProperty, nscoord& aResult) {
   MOZ_ASSERT(aProperty == PROP_WIDTH || aProperty == PROP_MAX_WIDTH ||
                  aProperty == PROP_MIN_WIDTH,
              "unexpected property");
 
-  if (aStyle.GetUnit() != eStyleUnit_Enumerated) return false;
-  int32_t val = aStyle.GetIntValue();
-  NS_ASSERTION(
-      val == NS_STYLE_WIDTH_MAX_CONTENT || val == NS_STYLE_WIDTH_MIN_CONTENT ||
-          val == NS_STYLE_WIDTH_FIT_CONTENT || val == NS_STYLE_WIDTH_AVAILABLE,
-      "unexpected enumerated value for width property");
-  if (val == NS_STYLE_WIDTH_AVAILABLE) return false;
-  if (val == NS_STYLE_WIDTH_FIT_CONTENT) {
+  if (aStyle == StyleExtremumLength::MozAvailable) return false;
+  if (aStyle == StyleExtremumLength::MozFitContent) {
     if (aProperty == PROP_WIDTH) return false;  // handle like 'width: auto'
     if (aProperty == PROP_MAX_WIDTH)
       // constrain large 'width' values down to max-content
-      val = NS_STYLE_WIDTH_MAX_CONTENT;
+      aStyle = StyleExtremumLength::MaxContent;
     else
       // constrain small 'width' or 'max-width' values up to min-content
-      val = NS_STYLE_WIDTH_MIN_CONTENT;
-  }
-
-  NS_ASSERTION(
-      val == NS_STYLE_WIDTH_MAX_CONTENT || val == NS_STYLE_WIDTH_MIN_CONTENT,
-      "should have reduced everything remaining to one of these");
+      aStyle = StyleExtremumLength::MinContent;
+  }
+
+  NS_ASSERTION(aStyle == StyleExtremumLength::MinContent ||
+                   aStyle == StyleExtremumLength::MaxContent,
+               "should have reduced everything remaining to one of these");
 
   // If aFrame is a container for font size inflation, then shrink
   // wrapping inside of it should not apply font size inflation.
   AutoMaybeDisableFontInflation an(aFrame);
 
-  if (val == NS_STYLE_WIDTH_MAX_CONTENT)
+  if (aStyle == StyleExtremumLength::MaxContent)
     aResult = aFrame->GetPrefISize(aRenderingContext);
   else
     aResult = aFrame->GetMinISize(aRenderingContext);
   return true;
 }
 
+template <typename SizeOrMaxSize>
+static bool GetIntrinsicCoord(const SizeOrMaxSize& aStyle,
+                              gfxContext* aRenderingContext, nsIFrame* aFrame,
+                              eWidthProperty aProperty, nscoord& aResult) {
+  if (!aStyle.IsExtremumLength()) {
+    return false;
+  }
+
+  return GetIntrinsicCoord(aStyle.AsExtremumLength(), aRenderingContext, aFrame,
+                           aProperty, aResult);
+}
+
 #undef DEBUG_INTRINSIC_WIDTH
 
 #ifdef DEBUG_INTRINSIC_WIDTH
 static int32_t gNoiseIndent = 0;
 #endif
 
 // Return true for form controls whose minimum intrinsic inline-size
 // shrinks to 0 when they have a percentage inline-size (but not
@@ -5007,18 +4953,18 @@ inline static bool FormControlShrinksFor
 
   return true;
 }
 
 // https://drafts.csswg.org/css-sizing-3/#percentage-sizing
 // Return true if the above spec's rule for replaced boxes applies.
 // XXX bug 1463700 will make this match the spec...
 static bool IsReplacedBoxResolvedAgainstZero(
-    nsIFrame* aFrame, const nsStyleCoord& aStyleSize,
-    const nsStyleCoord& aStyleMaxSize) {
+    nsIFrame* aFrame, const StyleSize& aStyleSize,
+    const StyleMaxSize& aStyleMaxSize) {
   const bool sizeHasPercent = aStyleSize.HasPercent();
   return ((sizeHasPercent || aStyleMaxSize.HasPercent()) &&
           aFrame->IsFrameOfType(nsIFrame::eReplacedSizing)) ||
          (sizeHasPercent && FormControlShrinksForPercentISize(aFrame));
 }
 
 /**
  * Add aOffsets which describes what to add on outside of the content box
@@ -5040,20 +4986,20 @@ static bool IsReplacedBoxResolvedAgainst
  * @param aStyleMaxSize a 'max-width' or 'max-height' property value
  * @param aFlags same as for IntrinsicForContainer
  * @param aContainerWM the container's WM
  */
 static nscoord AddIntrinsicSizeOffset(
     gfxContext* aRenderingContext, nsIFrame* aFrame,
     const nsIFrame::IntrinsicISizeOffsetData& aOffsets,
     nsLayoutUtils::IntrinsicISizeType aType, StyleBoxSizing aBoxSizing,
-    nscoord aContentSize, nscoord aContentMinSize,
-    const nsStyleCoord& aStyleSize, const nscoord* aFixedMinSize,
-    const nsStyleCoord& aStyleMinSize, const nscoord* aFixedMaxSize,
-    const nsStyleCoord& aStyleMaxSize, uint32_t aFlags, PhysicalAxis aAxis) {
+    nscoord aContentSize, nscoord aContentMinSize, const StyleSize& aStyleSize,
+    const nscoord* aFixedMinSize, const StyleSize& aStyleMinSize,
+    const nscoord* aFixedMaxSize, const StyleMaxSize& aStyleMaxSize,
+    uint32_t aFlags, PhysicalAxis aAxis) {
   nscoord result = aContentSize;
   nscoord min = aContentMinSize;
   nscoord coordOutsideSize = 0;
 
   if (!(aFlags & nsLayoutUtils::IGNORE_PADDING)) {
     coordOutsideSize += aOffsets.hPadding;
   }
 
@@ -5157,43 +5103,42 @@ static void AddStateBitToAncestors(nsIFr
   AutoMaybeDisableFontInflation an(aFrame);
 
   // We want the size this frame will contribute to the parent's inline-size,
   // so we work in the parent's writing mode; but if aFrame is orthogonal to
   // its parent, we'll need to look at its BSize instead of min/pref-ISize.
   const nsStylePosition* stylePos = aFrame->StylePosition();
   StyleBoxSizing boxSizing = stylePos->mBoxSizing;
 
-  nsStyleCoord styleMinISize =
+  StyleSize styleMinISize =
       horizontalAxis ? stylePos->mMinWidth : stylePos->mMinHeight;
-  nsStyleCoord styleISize =
+  StyleSize styleISize =
       (aFlags & MIN_INTRINSIC_ISIZE)
           ? styleMinISize
           : (horizontalAxis ? stylePos->mWidth : stylePos->mHeight);
-  MOZ_ASSERT(!(aFlags & MIN_INTRINSIC_ISIZE) ||
-                 styleISize.GetUnit() == eStyleUnit_Auto ||
-                 styleISize.GetUnit() == eStyleUnit_Enumerated,
+  MOZ_ASSERT(!(aFlags & MIN_INTRINSIC_ISIZE) || styleISize.IsAuto() ||
+                 styleISize.IsExtremumLength(),
              "should only use MIN_INTRINSIC_ISIZE for intrinsic values");
-  nsStyleCoord styleMaxISize =
+  StyleMaxSize styleMaxISize =
       horizontalAxis ? stylePos->mMaxWidth : stylePos->mMaxHeight;
 
   PhysicalAxis ourInlineAxis =
       aFrame->GetWritingMode().PhysicalAxis(eLogicalAxisInline);
   const bool isInlineAxis = aAxis == ourInlineAxis;
 
-  auto resetIfKeywords = [](nsStyleCoord& aSize, nsStyleCoord& aMinSize,
-                            nsStyleCoord& aMaxSize) {
-    if (aSize.GetUnit() == eStyleUnit_Enumerated) {
-      aSize.SetAutoValue();
-    }
-    if (aMinSize.GetUnit() == eStyleUnit_Enumerated) {
-      aMinSize.SetAutoValue();
-    }
-    if (aMaxSize.GetUnit() == eStyleUnit_Enumerated) {
-      aMaxSize.SetNoneValue();
+  auto resetIfKeywords = [](StyleSize& aSize, StyleSize& aMinSize,
+                            StyleMaxSize& aMaxSize) {
+    if (aSize.IsExtremumLength()) {
+      aSize = StyleSize::Auto();
+    }
+    if (aMinSize.IsExtremumLength()) {
+      aMinSize = StyleSize::Auto();
+    }
+    if (aMaxSize.IsExtremumLength()) {
+      aMaxSize = StyleMaxSize::None();
     }
   };
   // According to the spec, max-content and min-content should behave as the
   // property's initial values in block axis.
   // It also make senses to use the initial values for -moz-fit-content and
   // -moz-available for intrinsic size in block axis. Therefore, we reset them
   // if needed.
   if (!isInlineAxis) {
@@ -5213,35 +5158,35 @@ static void AddStateBitToAncestors(nsIFr
   nscoord result = 0, min = 0;
 
   nscoord maxISize;
   bool haveFixedMaxISize = GetAbsoluteCoord(styleMaxISize, maxISize);
   nscoord minISize;
 
   // Treat "min-width: auto" as 0.
   bool haveFixedMinISize;
-  if (eStyleUnit_Auto == styleMinISize.GetUnit()) {
+  if (styleMinISize.IsAuto()) {
     // NOTE: Technically, "auto" is supposed to behave like "min-content" on
     // flex items. However, we don't need to worry about that here, because
     // flex items' min-sizes are intentionally ignored until the flex
     // container explicitly considers them during space distribution.
     minISize = 0;
     haveFixedMinISize = true;
   } else {
     haveFixedMinISize = GetAbsoluteCoord(styleMinISize, minISize);
   }
 
   // If we have a specified width (or a specified 'min-width' greater
   // than the specified 'max-width', which works out to the same thing),
   // don't even bother getting the frame's intrinsic width, because in
   // this case GetAbsoluteCoord(styleISize, w) will always succeed, so
   // we'll never need the intrinsic dimensions.
-  if (styleISize.GetUnit() == eStyleUnit_Enumerated &&
-      (styleISize.GetIntValue() == NS_STYLE_WIDTH_MAX_CONTENT ||
-       styleISize.GetIntValue() == NS_STYLE_WIDTH_MIN_CONTENT)) {
+  if (styleISize.IsExtremumLength() &&
+      (styleISize.AsExtremumLength() == StyleExtremumLength::MaxContent ||
+       styleISize.AsExtremumLength() == StyleExtremumLength::MinContent)) {
     MOZ_ASSERT(isInlineAxis);
     // -moz-fit-content and -moz-available enumerated widths compute intrinsic
     // widths just like auto.
     // For max-content and min-content, we handle them like
     // specified widths, but ignore box-sizing.
     boxSizing = StyleBoxSizing::Content;
   } else if (!styleISize.ConvertsToLength() &&
              !(haveFixedMinISize && haveFixedMaxISize &&
@@ -5283,37 +5228,38 @@ static void AddStateBitToAncestors(nsIFr
 #endif
 
     // Handle elements with an intrinsic ratio (or size) and a specified
     // height, min-height, or max-height.
     // NOTE: We treat "min-height:auto" as "0" for the purpose of this code,
     // since that's what it means in all cases except for on flex items -- and
     // even there, we're supposed to ignore it (i.e. treat it as 0) until the
     // flex container explicitly considers it.
-    nsStyleCoord styleBSize =
+    StyleSize styleBSize =
         horizontalAxis ? stylePos->mHeight : stylePos->mWidth;
-    nsStyleCoord styleMinBSize =
+    StyleSize styleMinBSize =
         horizontalAxis ? stylePos->mMinHeight : stylePos->mMinWidth;
-    nsStyleCoord styleMaxBSize =
+    StyleMaxSize styleMaxBSize =
         horizontalAxis ? stylePos->mMaxHeight : stylePos->mMaxWidth;
 
     // According to the spec, max-content and min-content should behave as the
     // property's initial values in block axis.
     // It also make senses to use the initial values for -moz-fit-content and
     // -moz-available for intrinsic size in block axis. Therefore, we reset them
     // if needed.
     if (isInlineAxis) {
       resetIfKeywords(styleBSize, styleMinBSize, styleMaxBSize);
     }
 
-    if (styleBSize.GetUnit() != eStyleUnit_Auto ||
-        !(styleMinBSize.GetUnit() == eStyleUnit_Auto ||
-          (styleMinBSize.GetUnit() == eStyleUnit_Coord &&
-           styleMinBSize.GetCoordValue() == 0)) ||
-        styleMaxBSize.GetUnit() != eStyleUnit_None) {
+    // FIXME(emilio): Why the minBsize == 0 special-case? Also, shouldn't this
+    // use BehavesLikeInitialValueOnBlockAxis instead?
+    if (!styleBSize.IsAuto() ||
+        !(styleMinBSize.IsAuto() || (styleMinBSize.ConvertsToLength() &&
+                                     styleMinBSize.ToLength() == 0)) ||
+        !styleMaxBSize.IsNone()) {
       nsSize ratio(aFrame->GetIntrinsicRatio());
       nscoord ratioISize = (horizontalAxis ? ratio.width : ratio.height);
       nscoord ratioBSize = (horizontalAxis ? ratio.height : ratio.width);
       if (ratioBSize != 0) {
         AddStateBitToAncestors(
             aFrame, NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE);
 
         nscoord bSizeTakenByBoxSizing = GetDefiniteSizeTakenByBoxSizing(
@@ -5435,46 +5381,45 @@ static void AddStateBitToAncestors(nsIFr
   static_cast<nsFrame*>(aFrame)->ListTag(stderr);
   printf_stderr(" %s min-isize for %s WM:\n",
                 aType == MIN_ISIZE ? "min" : "pref",
                 aWM.IsVertical() ? "vertical" : "horizontal");
 #endif
 
   // Note: this method is only meant for grid/flex items.
   const nsStylePosition* const stylePos = aFrame->StylePosition();
-  nsStyleCoord size =
+  StyleSize size =
       aAxis == eAxisHorizontal ? stylePos->mMinWidth : stylePos->mMinHeight;
-  nsStyleCoord maxSize =
+  StyleMaxSize maxSize =
       aAxis == eAxisHorizontal ? stylePos->mMaxWidth : stylePos->mMaxHeight;
   auto childWM = aFrame->GetWritingMode();
   PhysicalAxis ourInlineAxis = childWM.PhysicalAxis(eLogicalAxisInline);
   // According to the spec, max-content and min-content should behave as the
   // property's initial values in block axis.
   // It also make senses to use the initial values for -moz-fit-content and
   // -moz-available for intrinsic size in block axis. Therefore, we reset them
   // if needed.
   if (aAxis != ourInlineAxis) {
-    if (size.GetUnit() == eStyleUnit_Enumerated) {
-      size.SetAutoValue();
-    }
-    if (maxSize.GetUnit() == eStyleUnit_Enumerated) {
-      maxSize.SetNoneValue();
+    if (size.IsExtremumLength()) {
+      size = StyleSize::Auto();
+    }
+    if (maxSize.IsExtremumLength()) {
+      maxSize = StyleMaxSize::None();
     }
   }
 
   nscoord minSize;
   nscoord* fixedMinSize = nullptr;
-  auto minSizeUnit = size.GetUnit();
-  if (minSizeUnit == eStyleUnit_Auto) {
+  if (size.IsAuto()) {
     if (aFrame->StyleDisplay()->mOverflowX == StyleOverflow::Visible) {
       size = aAxis == eAxisHorizontal ? stylePos->mWidth : stylePos->mHeight;
       // This is same as above: keywords should behaves as property's initial
       // values in block axis.
-      if (aAxis != ourInlineAxis && size.GetUnit() == eStyleUnit_Enumerated) {
-        size.SetAutoValue();
+      if (aAxis != ourInlineAxis && size.IsExtremumLength()) {
+        size = StyleSize::Auto();
       }
 
       if (GetAbsoluteCoord(size, minSize)) {
         // We have a definite width/height.  This is the "specified size" in:
         // https://drafts.csswg.org/css-grid/#min-size-auto
         fixedMinSize = &minSize;
       } else if (::IsReplacedBoxResolvedAgainstZero(aFrame, size, maxSize)) {
         // XXX bug 1463700: this doesn't handle calc() according to spec
@@ -5484,17 +5429,17 @@ static void AddStateBitToAncestors(nsIFr
       // fall through - the caller will have to deal with "transferred size"
     } else {
       // min-[width|height]:auto with overflow != visible computes to zero.
       minSize = 0;
       fixedMinSize = &minSize;
     }
   } else if (GetAbsoluteCoord(size, minSize)) {
     fixedMinSize = &minSize;
-  } else if (minSizeUnit != eStyleUnit_Enumerated) {
+  } else if (!size.IsExtremumLength()) {
     MOZ_ASSERT(size.HasPercent());
     minSize = 0;
     fixedMinSize = &minSize;
   }
 
   if (!fixedMinSize) {
     // Let the caller deal with the "content size" cases.
 #ifdef DEBUG_INTRINSIC_WIDTH
@@ -8004,20 +7949,20 @@ static nscoord MinimumFontSizeFor(nsPres
       // grandparent, which is the ruby frame contains the annotation.
       if (fType == LayoutFrameType::RubyText) {
         MOZ_ASSERT(parent && parent->IsRubyTextContainerFrame());
         nsIFrame* grandparent = parent->GetParent();
         MOZ_ASSERT(grandparent && grandparent->IsRubyFrame());
         return FontSizeInflationFor(grandparent);
       }
       WritingMode wm = f->GetWritingMode();
-      nsStyleCoord stylePosISize = f->StylePosition()->ISize(wm);
-      nsStyleCoord stylePosBSize = f->StylePosition()->BSize(wm);
-      if (stylePosISize.GetUnit() != eStyleUnit_Auto ||
-          !stylePosBSize.IsAutoOrEnum()) {
+      const auto& stylePosISize = f->StylePosition()->ISize(wm);
+      const auto& stylePosBSize = f->StylePosition()->BSize(wm);
+      if (!stylePosISize.IsAuto() ||
+          !stylePosBSize.BehavesLikeInitialValueOnBlockAxis()) {
         return 1.0;
       }
     }
   }
 
   int32_t interceptParam = nsLayoutUtils::FontSizeInflationMappingIntercept();
   float maxRatio = (float)nsLayoutUtils::FontSizeInflationMaxRatio() / 100.0f;
 
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -1484,40 +1484,39 @@ class nsLayoutUtils {
     return ComputeCBDependentValue(aPercentBasis, aCoord.AsLengthPercentage());
   }
 
   static nscoord ComputeBSizeDependentValue(nscoord aContainingBlockBSize,
                                             const LengthPercentageOrAuto&);
 
   static nscoord ComputeBSizeValue(nscoord aContainingBlockBSize,
                                    nscoord aContentEdgeToBoxSizingBoxEdge,
-                                   const nsStyleCoord& aCoord) {
+                                   const LengthPercentage& aCoord) {
     MOZ_ASSERT(aContainingBlockBSize != nscoord_MAX || !aCoord.HasPercent(),
                "caller must deal with %% of unconstrained block-size");
-    MOZ_ASSERT(aCoord.IsCoordPercentCalcUnit());
-
-    nscoord result = aCoord.ComputeCoordPercentCalc(aContainingBlockBSize);
+
+    nscoord result = aCoord.Resolve(aContainingBlockBSize);
     // Clamp calc(), and the subtraction for box-sizing.
     return std::max(0, result - aContentEdgeToBoxSizingBoxEdge);
   }
 
-  static bool IsAutoBSize(const nsStyleCoord& aCoord, nscoord aCBBSize) {
-    nsStyleUnit unit = aCoord.GetUnit();
-    return unit == eStyleUnit_Auto ||  // only for 'height'
-           unit == eStyleUnit_None ||  // only for 'max-height'
-           // The enumerated values were originally aimed at inline-size
-           // (or width, as it was before logicalization). For now, let them
-           // return true here, so that we don't call ComputeBSizeValue with
-           // value types that it doesn't understand. (See bug 1113216.)
-           //
-           // FIXME (bug 567039, bug 527285)
-           // This isn't correct for the 'fill' value or for the 'min-*' or
-           // 'max-*' properties, which need to be handled differently by
-           // the callers of IsAutoBSize().
-           unit == eStyleUnit_Enumerated ||
+  /**
+   * The "extremum length" values (see ExtremumLength) were originally aimed at
+   * inline-size (or width, as it was before logicalization). For now, we return
+   * true for those here, so that we don't call ComputeBSizeValue with value
+   * types that it doesn't understand. (See bug 1113216.)
+   *
+   * FIXME (bug 567039, bug 527285)
+   * This isn't correct for the 'fill' value or for the 'min-*' or 'max-*'
+   * properties, which need to be handled differently by the callers of
+   * IsAutoBSize().
+   */
+  template <typename SizeOrMaxSize>
+  static bool IsAutoBSize(const SizeOrMaxSize& aCoord, nscoord aCBBSize) {
+    return aCoord.BehavesLikeInitialValueOnBlockAxis() ||
            (aCBBSize == nscoord_MAX && aCoord.HasPercent());
   }
 
   static bool IsPaddingZero(const LengthPercentage& aLength) {
     // clamp negative calc() to 0
     return aLength.Resolve(nscoord_MAX) <= 0 && aLength.Resolve(0) <= 0;
   }
 
--- a/layout/forms/nsRangeFrame.cpp
+++ b/layout/forms/nsRangeFrame.cpp
@@ -756,17 +756,18 @@ LogicalSize nsRangeFrame::ComputeAutoSiz
 }
 
 nscoord nsRangeFrame::GetMinISize(gfxContext* aRenderingContext) {
   auto pos = StylePosition();
   auto wm = GetWritingMode();
   if (pos->ISize(wm).HasPercent()) {
     // https://drafts.csswg.org/css-sizing-3/#percentage-sizing
     // https://drafts.csswg.org/css-sizing-3/#min-content-zero
-    return nsLayoutUtils::ResolveToLength<true>(pos->ISize(wm), nscoord(0));
+    return nsLayoutUtils::ResolveToLength<true>(
+        pos->ISize(wm).AsLengthPercentage(), nscoord(0));
   }
   return GetPrefISize(aRenderingContext);
 }
 
 nscoord nsRangeFrame::GetPrefISize(gfxContext* aRenderingContext) {
   bool isInline = IsInlineOriented();
   auto em = StyleFont()->mFont.size * nsLayoutUtils::FontSizeInflationFor(this);
   return isInline ? NSToCoordRound(em * MAIN_AXIS_EM_SIZE)
--- a/layout/forms/nsTextControlFrame.cpp
+++ b/layout/forms/nsTextControlFrame.cpp
@@ -529,18 +529,18 @@ LogicalSize nsTextControlFrame::ComputeA
     nscoord aAvailableISize, const LogicalSize& aMargin,
     const LogicalSize& aBorder, const LogicalSize& aPadding,
     ComputeSizeFlags aFlags) {
   float inflation = nsLayoutUtils::FontSizeInflationFor(this);
   LogicalSize autoSize = CalcIntrinsicSize(aRenderingContext, aWM, inflation);
 
   // Note: nsContainerFrame::ComputeAutoSize only computes the inline-size (and
   // only for 'auto'), the block-size it returns is always NS_UNCONSTRAINEDSIZE.
-  const nsStyleCoord& iSizeCoord = StylePosition()->ISize(aWM);
-  if (iSizeCoord.GetUnit() == eStyleUnit_Auto) {
+  const auto& iSizeCoord = StylePosition()->ISize(aWM);
+  if (iSizeCoord.IsAuto()) {
     if (aFlags & ComputeSizeFlags::eIClampMarginBoxMinSize) {
       // CalcIntrinsicSize isn't aware of grid-item margin-box clamping, so we
       // fall back to nsContainerFrame's ComputeAutoSize to handle that.
       // XXX maybe a font-inflation issue here? (per the assertion below).
       autoSize.ISize(aWM) =
           nsContainerFrame::ComputeAutoSize(aRenderingContext, aWM, aCBSize,
                                             aAvailableISize, aMargin, aBorder,
                                             aPadding, aFlags)
--- a/layout/generic/ReflowInput.cpp
+++ b/layout/generic/ReflowInput.cpp
@@ -254,48 +254,49 @@ ReflowInput::ReflowInput(nsPresContext* 
     mFlags.mDummyParentReflowInput = true;
   }
 
   if (!(aFlags & CALLER_WILL_INIT)) {
     Init(aPresContext, aContainingBlockSize);
   }
 }
 
+template <typename SizeOrMaxSize>
 inline nscoord SizeComputationInput::ComputeISizeValue(
     nscoord aContainingBlockISize, nscoord aContentEdgeToBoxSizing,
-    nscoord aBoxSizingToMarginEdge, const nsStyleCoord& aCoord) const {
+    nscoord aBoxSizingToMarginEdge, const SizeOrMaxSize& aSize) const {
   return mFrame->ComputeISizeValue(mRenderingContext, aContainingBlockISize,
                                    aContentEdgeToBoxSizing,
-                                   aBoxSizingToMarginEdge, aCoord);
+                                   aBoxSizingToMarginEdge, aSize);
 }
 
+template <typename SizeOrMaxSize>
 nscoord SizeComputationInput::ComputeISizeValue(
     nscoord aContainingBlockISize, StyleBoxSizing aBoxSizing,
-    const nsStyleCoord& aCoord) const {
+    const SizeOrMaxSize& aSize) const {
   WritingMode wm = GetWritingMode();
   nscoord inside = 0, outside = ComputedLogicalBorderPadding().IStartEnd(wm) +
                                 ComputedLogicalMargin().IStartEnd(wm);
   if (aBoxSizing == StyleBoxSizing::Border) {
     inside = ComputedLogicalBorderPadding().IStartEnd(wm);
   }
   outside -= inside;
 
-  return ComputeISizeValue(aContainingBlockISize, inside, outside, aCoord);
+  return ComputeISizeValue(aContainingBlockISize, inside, outside, aSize);
 }
 
 nscoord SizeComputationInput::ComputeBSizeValue(
     nscoord aContainingBlockBSize, StyleBoxSizing aBoxSizing,
-    const nsStyleCoord& aCoord) const {
+    const LengthPercentage& aSize) const {
   WritingMode wm = GetWritingMode();
   nscoord inside = 0;
   if (aBoxSizing == StyleBoxSizing::Border) {
     inside = ComputedLogicalBorderPadding().BStartEnd(wm);
   }
-  return nsLayoutUtils::ComputeBSizeValue(aContainingBlockBSize, inside,
-                                          aCoord);
+  return nsLayoutUtils::ComputeBSizeValue(aContainingBlockBSize, inside, aSize);
 }
 
 void ReflowInput::SetComputedWidth(nscoord aComputedWidth) {
   NS_ASSERTION(mFrame, "Must have a frame!");
   // It'd be nice to assert that |frame| is not in reflow, but this fails for
   // two reasons:
   //
   // 1) Viewport frames reset the computed width on a copy of their reflow
@@ -421,39 +422,37 @@ void ReflowInput::Init(nsPresContext* aP
   if (parent && (parent->GetStateBits() & NS_FRAME_IN_CONSTRAINED_BSIZE) &&
       !(parent->IsScrollFrame() &&
         parent->StyleDisplay()->mOverflowY != StyleOverflow::Hidden)) {
     mFrame->AddStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE);
   } else if (type == LayoutFrameType::SVGForeignObject) {
     // An SVG foreignObject frame is inherently constrained block-size.
     mFrame->AddStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE);
   } else {
-    const nsStyleCoord& bSizeCoord = mStylePosition->BSize(mWritingMode);
-    const nsStyleCoord& maxBSizeCoord = mStylePosition->MaxBSize(mWritingMode);
-    if ((!bSizeCoord.IsAutoOrEnum() || !maxBSizeCoord.IsAutoOrEnum()) &&
+    const auto& bSizeCoord = mStylePosition->BSize(mWritingMode);
+    const auto& maxBSizeCoord = mStylePosition->MaxBSize(mWritingMode);
+    if ((!bSizeCoord.BehavesLikeInitialValueOnBlockAxis() ||
+         !maxBSizeCoord.BehavesLikeInitialValueOnBlockAxis()) &&
         // Don't set NS_FRAME_IN_CONSTRAINED_BSIZE on body or html elements.
         (mFrame->GetContent() && !(mFrame->GetContent()->IsAnyOfHTMLElements(
                                      nsGkAtoms::body, nsGkAtoms::html)))) {
       // If our block-size was specified as a percentage, then this could
       // actually resolve to 'auto', based on:
       // http://www.w3.org/TR/CSS21/visudet.html#the-height-property
       nsIFrame* containingBlk = mFrame;
       while (containingBlk) {
         const nsStylePosition* stylePos = containingBlk->StylePosition();
-        const nsStyleCoord& bSizeCoord = stylePos->BSize(mWritingMode);
-        const nsStyleCoord& maxBSizeCoord = stylePos->MaxBSize(mWritingMode);
-        if ((bSizeCoord.IsCoordPercentCalcUnit() && !bSizeCoord.HasPercent()) ||
-            (maxBSizeCoord.IsCoordPercentCalcUnit() &&
+        const auto& bSizeCoord = stylePos->BSize(mWritingMode);
+        const auto& maxBSizeCoord = stylePos->MaxBSize(mWritingMode);
+        if ((bSizeCoord.IsLengthPercentage() && !bSizeCoord.HasPercent()) ||
+            (maxBSizeCoord.IsLengthPercentage() &&
              !maxBSizeCoord.HasPercent())) {
           mFrame->AddStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE);
           break;
-        } else if ((bSizeCoord.IsCoordPercentCalcUnit() &&
-                    bSizeCoord.HasPercent()) ||
-                   (maxBSizeCoord.IsCoordPercentCalcUnit() &&
-                    maxBSizeCoord.HasPercent())) {
+        } else if (bSizeCoord.HasPercent() || maxBSizeCoord.HasPercent()) {
           if (!(containingBlk = containingBlk->GetContainingBlock())) {
             // If we've reached the top of the tree, then we don't have
             // a constrained block-size.
             mFrame->RemoveStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE);
             break;
           }
 
           continue;
@@ -470,17 +469,17 @@ void ReflowInput::Init(nsPresContext* aP
   if (mParentReflowInput &&
       mParentReflowInput->GetWritingMode().IsOrthogonalTo(mWritingMode)) {
     // Orthogonal frames are always reflowed with an unconstrained
     // dimension to avoid incomplete reflow across an orthogonal
     // boundary. Normally this is the block-size, but for column sets
     // with auto-height it's the inline-size, so that they can add
     // columns in the container's block direction
     if (type == LayoutFrameType::ColumnSet &&
-        eStyleUnit_Auto == mStylePosition->ISize(mWritingMode).GetUnit()) {
+        mStylePosition->ISize(mWritingMode).IsAuto()) {
       ComputedISize() = NS_UNCONSTRAINEDSIZE;
     } else {
       AvailableBSize() = NS_UNCONSTRAINEDSIZE;
     }
   }
 
   if (mStyleDisplay->IsContainSize()) {
     // In the case that a box is size contained, we want to ensure
@@ -723,17 +722,17 @@ void ReflowInput::InitResizeFlags(nsPres
     // not 'auto' block-size
     SetBResize(mFrame->BSize(wm) !=
                ComputedBSize() + ComputedLogicalBorderPadding().BStartEnd(wm));
   }
 
   bool dependsOnCBBSize =
       (mStylePosition->BSizeDependsOnContainer(wm) &&
        // FIXME: condition this on not-abspos?
-       mStylePosition->BSize(wm).GetUnit() != eStyleUnit_Auto) ||
+       !mStylePosition->BSize(wm).IsAuto()) ||
       mStylePosition->MinBSizeDependsOnContainer(wm) ||
       mStylePosition->MaxBSizeDependsOnContainer(wm) ||
       mStylePosition->OffsetHasPercent(wm.PhysicalSide(eLogicalSideBStart)) ||
       !mStylePosition->mOffset.GetBEnd(wm).IsAuto() || mFrame->IsXULBoxFrame();
 
   if (mStyleText->mLineHeight.GetUnit() == eStyleUnit_Enumerated) {
     NS_ASSERTION(mStyleText->mLineHeight.GetIntValue() ==
                      NS_STYLE_LINE_HEIGHT_BLOCK_HEIGHT,
@@ -802,23 +801,24 @@ void ReflowInput::InitResizeFlags(nsPres
   }
   if (mFrame->GetStateBits() & NS_FRAME_IS_DIRTY) {
     // If we're reflowing everything, then we'll find out if we need
     // to re-set this.
     mFrame->RemoveStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
   }
 }
 
-static inline bool IsIntrinsicKeyword(const nsStyleCoord& aCoord) {
-  if (aCoord.GetUnit() != eStyleUnit_Enumerated) {
+template <typename SizeOrMaxSize>
+static inline bool IsIntrinsicKeyword(const SizeOrMaxSize& aSize) {
+  if (!aSize.IsExtremumLength()) {
     return false;
   }
 
   // All of the keywords except for '-moz-available' depend on intrinsic sizes.
-  return aCoord.GetIntValue() != NS_STYLE_WIDTH_AVAILABLE;
+  return aSize.AsExtremumLength() != StyleExtremumLength::MozAvailable;
 }
 
 static bool AreDynamicReflowRootsEnabled() {
   static bool sAreDynamicReflowRootsEnabled;
   static bool sIsPrefCached = false;
 
   if (!sIsPrefCached) {
     sIsPrefCached = true;
@@ -844,40 +844,42 @@ void ReflowInput::InitDynamicReflowRoot(
 
   bool canBeDynamicReflowRoot = AreDynamicReflowRootsEnabled();
 
   // We can't do this if our used 'width' and 'height' might be influenced by
   // content.
   // FIXME: For display:block, we should probably optimize inline-size
   // being auto.
   // FIXME: Other flex and grid cases?
-  const nsStyleCoord& width = mStylePosition->mWidth;
-  const nsStyleCoord& height = mStylePosition->mHeight;
+  const auto& width = mStylePosition->mWidth;
+  const auto& height = mStylePosition->mHeight;
   if (canBeDynamicReflowRoot &&
-      (!width.IsCoordPercentCalcUnit() || width.HasPercent() ||
-       !height.IsCoordPercentCalcUnit() || height.HasPercent() ||
+      (!width.IsLengthPercentage() || width.HasPercent() ||
+       !height.IsLengthPercentage() || height.HasPercent() ||
        IsIntrinsicKeyword(mStylePosition->mMinWidth) ||
        IsIntrinsicKeyword(mStylePosition->mMaxWidth) ||
        IsIntrinsicKeyword(mStylePosition->mMinHeight) ||
        IsIntrinsicKeyword(mStylePosition->mMaxHeight) ||
-       ((mStylePosition->mMinWidth.GetUnit() == eStyleUnit_Auto ||
-         mStylePosition->mMinHeight.GetUnit() == eStyleUnit_Auto) &&
+       ((mStylePosition->mMinWidth.IsAuto() ||
+         mStylePosition->mMinHeight.IsAuto()) &&
         mFrame->IsFlexOrGridItem()))) {
     canBeDynamicReflowRoot = false;
   }
 
   if (canBeDynamicReflowRoot && mFrame->IsFlexItem()) {
     // If our flex-basis is 'auto', it'll defer to 'width' (or 'height') which
     // we've already checked. Otherwise, it preempts them, so we need to
     // perform the same "could-this-value-be-influenced-by-content" checks that
     // we performed for 'width' and 'height' above.
-    const nsStyleCoord& flexBasis = mStylePosition->mFlexBasis;
-    if (flexBasis.GetUnit() != eStyleUnit_Auto &&
-        (!flexBasis.IsCoordPercentCalcUnit() || flexBasis.HasPercent())) {
-      canBeDynamicReflowRoot = false;
+    const auto& flexBasis = mStylePosition->mFlexBasis;
+    if (!flexBasis.IsAuto()) {
+      if (!flexBasis.IsSize() || !flexBasis.AsSize().IsLengthPercentage() ||
+          flexBasis.AsSize().HasPercent()) {
+        canBeDynamicReflowRoot = false;
+      }
     }
   }
 
   if (canBeDynamicReflowRoot && !mFrame->IsFixedPosContainingBlock()) {
     // We can't treat this frame as a reflow root, since dynamic changes
     // to absolutely-positioned frames inside of it require that we
     // reflow the placeholder before we reflow the absolutely positioned
     // frame.
@@ -1366,18 +1368,18 @@ void ReflowInput::CalculateHypotheticalP
       aPlaceholderFrame, blockIStartContentEdge, blockContentSize);
   // Now blockContentSize is in containingBlock's writing mode.
 
   // If it's a replaced element and it has a 'auto' value for
   //'inline size', see if we can get the intrinsic size. This will allow
   // us to exactly determine both the inline edges
   WritingMode wm = containingBlock->GetWritingMode();
 
-  nsStyleCoord styleISize = mStylePosition->ISize(wm);
-  bool isAutoISize = styleISize.GetUnit() == eStyleUnit_Auto;
+  const auto& styleISize = mStylePosition->ISize(wm);
+  bool isAutoISize = styleISize.IsAuto();
   nsSize intrinsicSize;
   bool knowIntrinsicSize = false;
   if (NS_FRAME_IS_REPLACED(mFrameType) && isAutoISize) {
     // See if we can get the intrinsic size of the element
     knowIntrinsicSize = GetIntrinsicSizeFor(mFrame, intrinsicSize, aFrameType);
   }
 
   // See if we can calculate what the box inline size would have been if
@@ -1583,35 +1585,36 @@ void ReflowInput::CalculateHypotheticalP
     // border/padding/margin that the element would have had if it had
     // been in the flow. Note that we ignore any 'auto' and 'inherit'
     // values.
     nscoord insideBoxSizing, outsideBoxSizing;
     CalculateBorderPaddingMargin(eLogicalAxisBlock, blockContentSize.BSize(wm),
                                  &insideBoxSizing, &outsideBoxSizing);
 
     nscoord boxBSize;
-    nsStyleCoord styleBSize = mStylePosition->BSize(wm);
-    if (styleBSize.IsAutoOrEnum()) {
+    const auto& styleBSize = mStylePosition->BSize(wm);
+    if (styleBSize.BehavesLikeInitialValueOnBlockAxis()) {
       if (NS_FRAME_IS_REPLACED(mFrameType) && knowIntrinsicSize) {
         // It's a replaced element with an 'auto' block size so the box
         // block size is its intrinsic size plus any border/padding/margin
         boxBSize = LogicalSize(wm, intrinsicSize).BSize(wm) + outsideBoxSizing +
                    insideBoxSizing;
       } else {
         // XXX Bug 1191801
         // Figure out how to get the correct boxBSize here (need to reflow the
         // positioned frame?)
         boxBSize = 0;
       }
     } else {
       // We need to compute it. It's important we do this, because if it's
       // percentage-based this computed value may be different from the
       // computed value calculated using the absolute containing block height.
-      boxBSize = nsLayoutUtils::ComputeBSizeValue(blockContentSize.BSize(wm),
-                                                  insideBoxSizing, styleBSize) +
+      boxBSize = nsLayoutUtils::ComputeBSizeValue(
+                     blockContentSize.BSize(wm), insideBoxSizing,
+                     styleBSize.AsLengthPercentage()) +
                  insideBoxSizing + outsideBoxSizing;
     }
 
     LogicalSize boxSize(wm, knowBoxISize ? boxISize : 0, boxBSize);
 
     LogicalPoint origin(wm, aHypotheticalPos.mIStart, aHypotheticalPos.mBStart);
     origin =
         origin.ConvertTo(cbwm, wm, reflowSize - boxSize.GetPhysicalSize(wm));
@@ -1803,17 +1806,17 @@ void ReflowInput::InitAbsoluteConstraint
 
   // XXX Now that we have ComputeSize, can we condense many of the
   // branches off of widthIsAuto?
 
   LogicalMargin margin = ComputedLogicalMargin().ConvertTo(cbwm, wm);
   const LogicalMargin borderPadding =
       ComputedLogicalBorderPadding().ConvertTo(cbwm, wm);
 
-  bool iSizeIsAuto = eStyleUnit_Auto == mStylePosition->ISize(cbwm).GetUnit();
+  bool iSizeIsAuto = mStylePosition->ISize(cbwm).IsAuto();
   bool marginIStartIsAuto = false;
   bool marginIEndIsAuto = false;
   bool marginBStartIsAuto = false;
   bool marginBEndIsAuto = false;
   if (iStartIsAuto) {
     // We know 'right' is not 'auto' anymore thanks to the hypothetical
     // box code above.
     // Solve for 'left'.
@@ -1915,17 +1918,18 @@ void ReflowInput::InitAbsoluteConstraint
         // above, where the spec says to ignore
         // 'margin-left'/'margin-right'.
         // Ignore the specified value for 'right'.
         offsets.IEnd(cbwm) += availMarginSpace;
       }
     }
   }
 
-  bool bSizeIsAuto = mStylePosition->BSize(cbwm).IsAutoOrEnum();
+  bool bSizeIsAuto =
+      mStylePosition->BSize(cbwm).BehavesLikeInitialValueOnBlockAxis();
   if (bStartIsAuto) {
     // solve for block-start
     if (bSizeIsAuto) {
       offsets.BStart(cbwm) = NS_AUTOOFFSET;
     } else {
       offsets.BStart(cbwm) = cbSize.BSize(cbwm) - margin.BStartEnd(cbwm) -
                              borderPadding.BStartEnd(cbwm) -
                              computedSize.BSize(cbwm) - offsets.BEnd(cbwm);
@@ -2175,28 +2179,32 @@ LogicalSize ReflowInput::ComputeContaini
       // If the ancestor is block-level, the containing block is formed by the
       // padding edge of the ancestor
       cbSize.ISize(wm) +=
           aContainingBlockRI->ComputedLogicalPadding().IStartEnd(wm);
       cbSize.BSize(wm) +=
           aContainingBlockRI->ComputedLogicalPadding().BStartEnd(wm);
     }
   } else {
+    auto IsQuirky = [&](const StyleSize& aSize) -> bool {
+      return aSize.ConvertsToPercentage() &&
+             !aSize.AsLengthPercentage().was_calc;
+    };
     // an element in quirks mode gets a containing block based on looking for a
     // parent with a non-auto height if the element has a percent height
     // Note: We don't emulate this quirk for percents in calc() or in
     // vertical writing modes.
     if (!wm.IsVertical() && NS_AUTOHEIGHT == cbSize.BSize(wm)) {
       if (eCompatibility_NavQuirks == aPresContext->CompatibilityMode() &&
-          (mStylePosition->mHeight.GetUnit() == eStyleUnit_Percent ||
+          (IsQuirky(mStylePosition->mHeight) ||
            (mFrame->IsTableWrapperFrame() &&
-            mFrame->PrincipalChildList()
-                    .FirstChild()
-                    ->StylePosition()
-                    ->mHeight.GetUnit() == eStyleUnit_Percent))) {
+            IsQuirky(mFrame->PrincipalChildList()
+                         .FirstChild()
+                         ->StylePosition()
+                         ->mHeight)))) {
         cbSize.BSize(wm) = CalcQuirkContainingBlockHeight(aContainingBlockRI);
       }
     }
   }
 
   return cbSize.ConvertTo(GetWritingMode(), wm);
 }
 
@@ -2295,60 +2303,57 @@ void ReflowInput::InitConstraints(nsPres
 
     // For calculating positioning offsets, margins, borders and
     // padding, we use the writing mode of the containing block
     WritingMode cbwm = cbri->GetWritingMode();
     InitOffsets(cbwm, cbSize.ConvertTo(cbwm, wm).ISize(cbwm), aFrameType,
                 mFlags, aBorder, aPadding, mStyleDisplay);
 
     // For calculating the size of this box, we use its own writing mode
-    const nsStyleCoord& blockSize = mStylePosition->BSize(wm);
-    nsStyleUnit blockSizeUnit =
-        blockSize.IsAutoOrEnum() ? eStyleUnit_Auto : blockSize.GetUnit();
+    const auto& blockSize = mStylePosition->BSize(wm);
+    bool isAutoBSize = blockSize.BehavesLikeInitialValueOnBlockAxis();
 
     // Check for a percentage based block size and a containing block
     // block size that depends on the content block size
-    // XXX twiddling blockSizeUnit doesn't help anymore
-    // FIXME Shouldn't we fix that?
     if (blockSize.HasPercent()) {
       if (NS_AUTOHEIGHT == cbSize.BSize(wm)) {
         // this if clause enables %-blockSize on replaced inline frames,
         // such as images.  See bug 54119.  The else clause "blockSizeUnit =
         // eStyleUnit_Auto;" used to be called exclusively.
         if (NS_FRAME_REPLACED(NS_CSS_FRAME_TYPE_INLINE) == mFrameType ||
             NS_FRAME_REPLACED_CONTAINS_BLOCK(NS_CSS_FRAME_TYPE_INLINE) ==
                 mFrameType) {
           // Get the containing block reflow state
           NS_ASSERTION(nullptr != cbri, "no containing block");
           // in quirks mode, get the cb height using the special quirk method
           if (!wm.IsVertical() &&
               eCompatibility_NavQuirks == aPresContext->CompatibilityMode()) {
             if (!IsTableCell(cbri->mFrame->Type())) {
               cbSize.BSize(wm) = CalcQuirkContainingBlockHeight(cbri);
               if (cbSize.BSize(wm) == NS_AUTOHEIGHT) {
-                blockSizeUnit = eStyleUnit_Auto;
+                isAutoBSize = true;
               }
             } else {
-              blockSizeUnit = eStyleUnit_Auto;
+              isAutoBSize = true;
             }
           }
           // in standard mode, use the cb block size.  if it's "auto",
           // as will be the case by default in BODY, use auto block size
           // as per CSS2 spec.
           else {
             nscoord computedBSize = cbri->ComputedSize(wm).BSize(wm);
             if (NS_AUTOHEIGHT != computedBSize) {
               cbSize.BSize(wm) = computedBSize;
             } else {
-              blockSizeUnit = eStyleUnit_Auto;
+              isAutoBSize = true;
             }
           }
         } else {
           // default to interpreting the blockSize like 'auto'
-          blockSizeUnit = eStyleUnit_Auto;
+          isAutoBSize = true;
         }
       }
     }
 
     // Compute our offsets if the element is relatively positioned.  We
     // need the correct containing block inline-size and block-size
     // here, which is why we need to do it after all the quirks-n-such
     // above. (If the element is sticky positioned, we need to wait
@@ -2369,60 +2374,57 @@ void ReflowInput::InitConstraints(nsPres
 
     // Calculate the computed inlineSize and blockSize.
     // This varies by frame type.
 
     if (NS_CSS_FRAME_TYPE_INTERNAL_TABLE == mFrameType) {
       // Internal table elements. The rules vary depending on the type.
       // Calculate the computed isize
       bool rowOrRowGroup = false;
-      const nsStyleCoord& inlineSize = mStylePosition->ISize(wm);
-      nsStyleUnit inlineSizeUnit = inlineSize.GetUnit();
+      const auto& inlineSize = mStylePosition->ISize(wm);
+      bool isAutoISize = inlineSize.IsAuto();
       if ((StyleDisplay::TableRow == mStyleDisplay->mDisplay) ||
           (StyleDisplay::TableRowGroup == mStyleDisplay->mDisplay)) {
         // 'inlineSize' property doesn't apply to table rows and row groups
-        inlineSizeUnit = eStyleUnit_Auto;
+        isAutoISize = true;
         rowOrRowGroup = true;
       }
 
-      // calc() with percentages acts like auto on internal table elements
-      if (eStyleUnit_Auto == inlineSizeUnit ||
-          (inlineSize.IsCalcUnit() && inlineSize.CalcHasPercent())) {
+      // calc() with both percentages and lengths act like auto on internal
+      // table elements
+      if (isAutoISize || inlineSize.HasLengthAndPercentage()) {
         ComputedISize() = AvailableISize();
 
         if ((ComputedISize() != NS_UNCONSTRAINEDSIZE) && !rowOrRowGroup) {
           // Internal table elements don't have margins. Only tables and
           // cells have border and padding
           ComputedISize() -= ComputedLogicalBorderPadding().IStartEnd(wm);
           if (ComputedISize() < 0) ComputedISize() = 0;
         }
         NS_ASSERTION(ComputedISize() >= 0, "Bogus computed isize");
 
       } else {
-        NS_ASSERTION(inlineSizeUnit == inlineSize.GetUnit(),
-                     "unexpected inline size unit change");
         ComputedISize() = ComputeISizeValue(
             cbSize.ISize(wm), mStylePosition->mBoxSizing, inlineSize);
       }
 
       // Calculate the computed block size
       if ((StyleDisplay::TableColumn == mStyleDisplay->mDisplay) ||
           (StyleDisplay::TableColumnGroup == mStyleDisplay->mDisplay)) {
         // 'blockSize' property doesn't apply to table columns and column groups
-        blockSizeUnit = eStyleUnit_Auto;
+        isAutoBSize = true;
       }
-      // calc() with percentages acts like 'auto' on internal table elements
-      if (eStyleUnit_Auto == blockSizeUnit ||
-          (blockSize.IsCalcUnit() && blockSize.CalcHasPercent())) {
+      // calc() with both percentages and lengths acts like 'auto' on internal
+      // table elements
+      if (isAutoBSize || blockSize.HasLengthAndPercentage()) {
         ComputedBSize() = NS_AUTOHEIGHT;
       } else {
-        NS_ASSERTION(blockSizeUnit == blockSize.GetUnit(),
-                     "unexpected block size unit change");
-        ComputedBSize() = ComputeBSizeValue(
-            cbSize.BSize(wm), mStylePosition->mBoxSizing, blockSize);
+        ComputedBSize() =
+            ComputeBSizeValue(cbSize.BSize(wm), mStylePosition->mBoxSizing,
+                              blockSize.AsLengthPercentage());
       }
 
       // Doesn't apply to table elements
       ComputedMinWidth() = ComputedMinHeight() = 0;
       ComputedMaxWidth() = ComputedMaxHeight() = NS_UNCONSTRAINEDSIZE;
 
     } else if (NS_FRAME_GET_TYPE(mFrameType) == NS_CSS_FRAME_TYPE_ABSOLUTE) {
       // XXX not sure if this belongs here or somewhere else - cwk
@@ -2992,32 +2994,32 @@ bool SizeComputationInput::ComputePaddin
     SetComputedLogicalPadding(aWM, p);
   }
   return isCBDependent;
 }
 
 void ReflowInput::ComputeMinMaxValues(const LogicalSize& aCBSize) {
   WritingMode wm = GetWritingMode();
 
-  const nsStyleCoord& minISize = mStylePosition->MinISize(wm);
-  const nsStyleCoord& maxISize = mStylePosition->MaxISize(wm);
-  const nsStyleCoord& minBSize = mStylePosition->MinBSize(wm);
-  const nsStyleCoord& maxBSize = mStylePosition->MaxBSize(wm);
+  const auto& minISize = mStylePosition->MinISize(wm);
+  const auto& maxISize = mStylePosition->MaxISize(wm);
+  const auto& minBSize = mStylePosition->MinBSize(wm);
+  const auto& maxBSize = mStylePosition->MaxBSize(wm);
 
   // NOTE: min-width:auto resolves to 0, except on a flex item. (But
   // even there, it's supposed to be ignored (i.e. treated as 0) until
   // the flex container explicitly resolves & considers it.)
-  if (eStyleUnit_Auto == minISize.GetUnit()) {
+  if (minISize.IsAuto()) {
     ComputedMinISize() = 0;
   } else {
     ComputedMinISize() = ComputeISizeValue(
         aCBSize.ISize(wm), mStylePosition->mBoxSizing, minISize);
   }
 
-  if (eStyleUnit_None == maxISize.GetUnit()) {
+  if (maxISize.IsNone()) {
     // Specified value of 'none'
     ComputedMaxISize() = NS_UNCONSTRAINEDSIZE;  // no limit
   } else {
     ComputedMaxISize() = ComputeISizeValue(
         aCBSize.ISize(wm), mStylePosition->mBoxSizing, maxISize);
   }
 
   // If the computed value of 'min-width' is greater than the value of
@@ -3028,39 +3030,47 @@ void ReflowInput::ComputeMinMaxValues(co
 
   // Check for percentage based values and a containing block height that
   // depends on the content height. Treat them like the initial value.
   // Likewise, check for calc() with percentages on internal table elements;
   // that's treated as the initial value too.
   // Likewise, if we're a child of a flex container who's measuring our
   // intrinsic height, then we want to disregard our min-height/max-height.
   const nscoord& bPercentageBasis = aCBSize.BSize(wm);
-  auto BSizeBehavesAsInitialValue = [&](const nsStyleCoord& aBSize) {
-    return nsLayoutUtils::IsAutoBSize(aBSize, bPercentageBasis) ||
-           (mFrameType == NS_CSS_FRAME_TYPE_INTERNAL_TABLE &&
-            aBSize.IsCalcUnit() && aBSize.CalcHasPercent()) ||
-           mFlags.mIsFlexContainerMeasuringBSize;
+  auto BSizeBehavesAsInitialValue = [&](const auto& aBSize) {
+    if (nsLayoutUtils::IsAutoBSize(aBSize, bPercentageBasis)) {
+      return true;
+    }
+    if (mFlags.mIsFlexContainerMeasuringBSize) {
+      return true;
+    }
+    if (mFrameType == NS_CSS_FRAME_TYPE_INTERNAL_TABLE) {
+      return aBSize.HasLengthAndPercentage();
+    }
+    return false;
   };
 
   // NOTE: min-height:auto resolves to 0, except on a flex item. (But
   // even there, it's supposed to be ignored (i.e. treated as 0) until
   // the flex container explicitly resolves & considers it.)
   if (BSizeBehavesAsInitialValue(minBSize)) {
     ComputedMinBSize() = 0;
   } else {
-    ComputedMinBSize() = ComputeBSizeValue(
-        bPercentageBasis, mStylePosition->mBoxSizing, minBSize);
+    ComputedMinBSize() =
+        ComputeBSizeValue(bPercentageBasis, mStylePosition->mBoxSizing,
+                          minBSize.AsLengthPercentage());
   }
 
   if (BSizeBehavesAsInitialValue(maxBSize)) {
     // Specified value of 'none'
     ComputedMaxBSize() = NS_UNCONSTRAINEDSIZE;  // no limit
   } else {
-    ComputedMaxBSize() = ComputeBSizeValue(
-        bPercentageBasis, mStylePosition->mBoxSizing, maxBSize);
+    ComputedMaxBSize() =
+        ComputeBSizeValue(bPercentageBasis, mStylePosition->mBoxSizing,
+                          maxBSize.AsLengthPercentage());
   }
 
   // If the computed value of 'min-height' is greater than the value of
   // 'max-height', 'max-height' is set to the value of 'min-height'
   if (ComputedMinBSize() > ComputedMaxBSize()) {
     ComputedMaxBSize() = ComputedMinBSize();
   }
 }
--- a/layout/generic/ReflowInput.h
+++ b/layout/generic/ReflowInput.h
@@ -319,29 +319,31 @@ struct SizeComputationInput {
                    const nsStyleDisplay* aDisplay = nullptr);
 
   /*
    * 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 nsStyleCoord& aCoord) const;
+                                   const SizeOrMaxSize&) const;
   // same as previous, but using mComputedBorderPadding, mComputedPadding,
   // and mComputedMargin
-  nscoord ComputeISizeValue(nscoord aContainingBlockISize,
-                            mozilla::StyleBoxSizing aBoxSizing,
-                            const nsStyleCoord& aCoord) const;
+  template <typename SizeOrMaxSize>
+  inline nscoord ComputeISizeValue(nscoord aContainingBlockISize,
+                                   mozilla::StyleBoxSizing aBoxSizing,
+                                   const SizeOrMaxSize&) const;
 
   nscoord ComputeBSizeValue(nscoord aContainingBlockBSize,
                             mozilla::StyleBoxSizing aBoxSizing,
-                            const nsStyleCoord& aCoord) const;
+                            const mozilla::LengthPercentage& aCoord) const;
 };
 
 /**
  * State passed to a frame during reflow or intrinsic size calculation.
  *
  * XXX Refactor so only a base class (nsSizingState?) is used for intrinsic
  * size calculation.
  *
--- a/layout/generic/WritingModes.h
+++ b/layout/generic/WritingModes.h
@@ -1975,101 +1975,73 @@ inline nsStyleCoord nsStyleSides::GetIEn
 }
 
 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 nsStyleCoord& nsStylePosition::ISize(mozilla::WritingMode aWM) {
+inline const mozilla::StyleSize& nsStylePosition::ISize(WritingMode aWM) const {
   return aWM.IsVertical() ? mHeight : mWidth;
 }
-inline nsStyleCoord& nsStylePosition::MinISize(mozilla::WritingMode aWM) {
+inline const mozilla::StyleSize& nsStylePosition::MinISize(WritingMode aWM) const {
   return aWM.IsVertical() ? mMinHeight : mMinWidth;
 }
-inline nsStyleCoord& nsStylePosition::MaxISize(mozilla::WritingMode aWM) {
+inline const mozilla::StyleMaxSize& nsStylePosition::MaxISize(
+    WritingMode aWM) const {
   return aWM.IsVertical() ? mMaxHeight : mMaxWidth;
 }
-inline nsStyleCoord& nsStylePosition::BSize(mozilla::WritingMode aWM) {
+inline const mozilla::StyleSize& nsStylePosition::BSize(WritingMode aWM) const {
   return aWM.IsVertical() ? mWidth : mHeight;
 }
-inline nsStyleCoord& nsStylePosition::MinBSize(mozilla::WritingMode aWM) {
+inline const mozilla::StyleSize& nsStylePosition::MinBSize(
+    WritingMode aWM) const {
   return aWM.IsVertical() ? mMinWidth : mMinHeight;
 }
-inline nsStyleCoord& nsStylePosition::MaxBSize(mozilla::WritingMode aWM) {
+inline const mozilla::StyleMaxSize& nsStylePosition::MaxBSize(
+    WritingMode aWM) const {
   return aWM.IsVertical() ? mMaxWidth : mMaxHeight;
 }
 
-inline const nsStyleCoord& nsStylePosition::ISize(
-    mozilla::WritingMode aWM) const {
-  return aWM.IsVertical() ? mHeight : mWidth;
-}
-inline const nsStyleCoord& nsStylePosition::MinISize(
-    mozilla::WritingMode aWM) const {
-  return aWM.IsVertical() ? mMinHeight : mMinWidth;
-}
-inline const nsStyleCoord& nsStylePosition::MaxISize(
-    mozilla::WritingMode aWM) const {
-  return aWM.IsVertical() ? mMaxHeight : mMaxWidth;
-}
-inline const nsStyleCoord& nsStylePosition::BSize(
-    mozilla::WritingMode aWM) const {
-  return aWM.IsVertical() ? mWidth : mHeight;
+inline bool nsStylePosition::ISizeDependsOnContainer(WritingMode aWM) const {
+  const auto& iSize = ISize(aWM);
+  return iSize.IsAuto() || ISizeCoordDependsOnContainer(iSize);
 }
-inline const nsStyleCoord& nsStylePosition::MinBSize(
-    mozilla::WritingMode aWM) const {
-  return aWM.IsVertical() ? mMinWidth : mMinHeight;
-}
-inline const nsStyleCoord& nsStylePosition::MaxBSize(
-    mozilla::WritingMode aWM) const {
-  return aWM.IsVertical() ? mMaxWidth : mMaxHeight;
-}
-
-inline bool nsStylePosition::ISizeDependsOnContainer(
-    mozilla::WritingMode aWM) const {
-  const auto& iSize = aWM.IsVertical() ? mHeight : mWidth;
-  return iSize.GetUnit() == eStyleUnit_Auto ||
-         ISizeCoordDependsOnContainer(iSize);
-}
-inline bool nsStylePosition::MinISizeDependsOnContainer(
-    mozilla::WritingMode aWM) const {
+inline bool nsStylePosition::MinISizeDependsOnContainer(WritingMode aWM) const {
   // NOTE: For a flex item, "min-inline-size:auto" is supposed to behave like
   // "min-content", which does depend on the container, so you might think we'd
   // need a special case for "flex item && min-inline-size:auto" here. However,
   // we don't actually need that special-case code, because flex items are
   // explicitly supposed to *ignore* their min-inline-size (i.e. behave like
   // it's 0) until the flex container explicitly considers it. So -- since the
   // flex container doesn't rely on this method, we don't need to worry about
   // special behavior for flex items' "min-inline-size:auto" values here.
   return ISizeCoordDependsOnContainer(MinISize(aWM));
 }
-inline bool nsStylePosition::MaxISizeDependsOnContainer(
-    mozilla::WritingMode aWM) const {
+inline bool nsStylePosition::MaxISizeDependsOnContainer(WritingMode aWM) const {
   // NOTE: The comment above MinISizeDependsOnContainer about flex items
   // applies here, too.
   return ISizeCoordDependsOnContainer(MaxISize(aWM));
 }
 // Note that these functions count `auto` as depending on the container
 // since that's the case for absolutely positioned elements.
 // However, some callers do not care about this case and should check
 // for it, since it is the most common case.
 // FIXME: We should probably change the assumption to be the other way
 // around.
-inline bool nsStylePosition::BSizeDependsOnContainer(
-    mozilla::WritingMode aWM) const {
-  const auto& bSize = aWM.IsVertical() ? mWidth : mHeight;
-  return bSize.IsAutoOrEnum() || BSizeCoordDependsOnContainer(bSize);
+inline bool nsStylePosition::BSizeDependsOnContainer(WritingMode aWM) const {
+  const auto& bSize = BSize(aWM);
+  return bSize.BehavesLikeInitialValueOnBlockAxis() ||
+         BSizeCoordDependsOnContainer(bSize);
 }
-inline bool nsStylePosition::MinBSizeDependsOnContainer(
-    mozilla::WritingMode aWM) const {
+inline bool nsStylePosition::MinBSizeDependsOnContainer(WritingMode aWM) const {
   return BSizeCoordDependsOnContainer(MinBSize(aWM));
 }
-inline bool nsStylePosition::MaxBSizeDependsOnContainer(
-    mozilla::WritingMode aWM) const {
+inline bool nsStylePosition::MaxBSizeDependsOnContainer(WritingMode aWM) const {
   return BSizeCoordDependsOnContainer(MaxBSize(aWM));
 }
 
 inline bool nsStyleMargin::HasBlockAxisAuto(mozilla::WritingMode aWM) const {
   return mMargin.GetBStart(aWM).IsAuto() || mMargin.GetBEnd(aWM).IsAuto();
 }
 
 inline bool nsStyleMargin::HasInlineAxisAuto(mozilla::WritingMode aWM) const {
--- a/layout/generic/nsAbsoluteContainingBlock.cpp
+++ b/layout/generic/nsAbsoluteContainingBlock.cpp
@@ -281,19 +281,21 @@ bool nsAbsoluteContainingBlock::FrameDep
   }
   if (wm.IsVertical() ? aCBWidthChanged : aCBHeightChanged) {
     // See if f's block-size might have changed.
     // If margin-block-start/end, padding-block-start/end,
     // min-block-size, and max-block-size are all lengths or 'none',
     // and bsize is a length or bsize and bend are auto and bstart is not auto,
     // then our frame bsize does not depend on the parent bsize.
     // Note that borders never depend on the parent bsize.
+    //
+    // FIXME(emilio): Should the BSize(wm).IsAuto() check also for the extremum
+    // lengths?
     if ((pos->BSizeDependsOnContainer(wm) &&
-         !(pos->BSize(wm).GetUnit() == eStyleUnit_Auto &&
-           pos->mOffset.GetBEnd(wm).IsAuto() &&
+         !(pos->BSize(wm).IsAuto() && pos->mOffset.GetBEnd(wm).IsAuto() &&
            !pos->mOffset.GetBStart(wm).IsAuto())) ||
         pos->MinBSizeDependsOnContainer(wm) ||
         pos->MaxBSizeDependsOnContainer(wm) ||
         !IsFixedPaddingSize(padding->mPadding.GetBStart(wm)) ||
         !IsFixedPaddingSize(padding->mPadding.GetBEnd(wm))) {
       return true;
     }
 
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -2995,38 +2995,36 @@ nsresult nsBlockFrame::AttributeChanged(
                                         NS_FRAME_HAS_DIRTY_CHILDREN);
         }
       }
     }
   }
   return rv;
 }
 
-static inline bool IsNonAutoNonZeroBSize(const nsStyleCoord& aCoord) {
-  nsStyleUnit unit = aCoord.GetUnit();
-  if (unit == eStyleUnit_Auto ||
-      // The enumerated values were originally aimed at inline-size
-      // (or width, as it was before logicalization). For now, let them
-      // return false here, so we treat them like 'auto' pending a
-      // real implementation. (See bug 1126420.)
-      //
-      // FIXME (bug 567039, bug 527285)
-      // This isn't correct for the 'fill' value, which should more
-      // likely (but not necessarily, depending on the available space)
-      // be returning true.
-      unit == eStyleUnit_Enumerated) {
+static inline bool IsNonAutoNonZeroBSize(const StyleSize& aCoord) {
+  // The "extremum length" values (see ExtremumLength) were originally aimed at
+  // inline-size (or width, as it was before logicalization). For now, let them
+  // return false here, so we treat them like 'auto' pending a real
+  // implementation. (See bug 1126420.)
+  //
+  // FIXME (bug 567039, bug 527285)
+  // This isn't correct for the 'fill' value, which should more
+  // likely (but not necessarily, depending on the available space)
+  // be returning true.
+  if (aCoord.IsAuto() || aCoord.IsExtremumLength()) {
     return false;
   }
-  if (aCoord.IsCoordPercentCalcUnit()) {
+  if (aCoord.IsLengthPercentage()) {
     // If we evaluate the length/percent/calc at a percentage basis of
     // both nscoord_MAX and 0, and it's zero both ways, then it's a zero
     // length, percent, or combination thereof.  Test > 0 so we clamp
     // negative calc() results to 0.
-    return aCoord.ComputeCoordPercentCalc(nscoord_MAX) > 0 ||
-           aCoord.ComputeCoordPercentCalc(0) > 0;
+    return aCoord.AsLengthPercentage().Resolve(nscoord_MAX) > 0 ||
+           aCoord.AsLengthPercentage().Resolve(0) > 0;
   }
   MOZ_ASSERT(false, "unexpected unit for height or min-height");
   return true;
 }
 
 /* virtual */ bool nsBlockFrame::IsSelfEmpty() {
   // Blocks which are margin-roots (including inline-blocks) cannot be treated
   // as empty for margin-collapsing and other purposes. They're more like
--- a/layout/generic/nsContainerFrame.cpp
+++ b/layout/generic/nsContainerFrame.cpp
@@ -794,20 +794,18 @@ LogicalSize nsContainerFrame::ComputeAut
   if ((aFlags & ComputeSizeFlags::eShrinkWrap) || IsFrameOfType(eReplaced)) {
     // Only bother computing our 'auto' ISize if the result will be used.
     // It'll be used under two scenarios:
     // - If our ISize property is itself 'auto'.
     // - If we're using flex-basis in place of our ISize property (i.e. we're a
     // flex item with our inline axis being the main axis), AND we have
     // flex-basis:content.
     const nsStylePosition* pos = StylePosition();
-    if (pos->ISize(aWM).GetUnit() == eStyleUnit_Auto ||
-        (pos->mFlexBasis.GetUnit() == eStyleUnit_Enumerated &&
-         pos->mFlexBasis.GetIntValue() == NS_STYLE_FLEX_BASIS_CONTENT &&
-         IsFlexItem() &&
+    if (pos->ISize(aWM).IsAuto() ||
+        (pos->mFlexBasis.IsContent() && IsFlexItem() &&
          nsFlexContainerFrame::IsItemInlineAxisMainAxis(this))) {
       result.ISize(aWM) =
           ShrinkWidthToFit(aRenderingContext, availBased, aFlags);
     }
   } else {
     result.ISize(aWM) = availBased;
   }
 
--- a/layout/generic/nsFlexContainerFrame.cpp
+++ b/layout/generic/nsFlexContainerFrame.cpp
@@ -249,20 +249,18 @@ static nscoord AddChecked(nscoord aFirst
 
 // Check if the size is auto or it is a keyword in the block axis.
 // |aIsInline| should represent whether aSize is in the inline axis, from the
 // perspective of the writing mode of the flex item that the size comes from.
 //
 // max-content and min-content should behave as property's initial value.
 // Bug 567039: We treat -moz-fit-content and -moz-available as property's
 // initial value for now.
-static inline bool IsAutoOrEnumOnBSize(const nsStyleCoord& aSize,
-                                       bool aIsInline) {
-  return aSize.GetUnit() == eStyleUnit_Auto ||
-         (!aIsInline && aSize.GetUnit() == eStyleUnit_Enumerated);
+static inline bool IsAutoOrEnumOnBSize(const StyleSize& aSize, bool aIsInline) {
+  return aSize.IsAuto() || (!aIsInline && aSize.IsExtremumLength());
 }
 
 // Helper-macros to let us pick one of two expressions to evaluate
 // (an inline-axis expression vs. a block-axis expression), to get a
 // main-axis or cross-axis component.
 // For code that has e.g. a LogicalSize object, the methods
 // FlexboxAxisTracker::GetMainComponent and GetCrossComponent are cleaner
 // than these macros. But in cases where we simply have two separate
@@ -1398,17 +1396,17 @@ UniquePtr<FlexItem> nsFlexContainerFrame
 // in nsFrame::ComputeSizeWithIntrinsicDimensions().
 static bool IsCrossSizeDefinite(const ReflowInput& aItemReflowInput,
                                 const FlexboxAxisTracker& aAxisTracker) {
   const nsStylePosition* pos = aItemReflowInput.mStylePosition;
   const WritingMode containerWM = aAxisTracker.GetWritingMode();
 
   if (aAxisTracker.IsColumnOriented()) {
     // Column-oriented means cross axis is container's inline axis.
-    return pos->ISize(containerWM).GetUnit() != eStyleUnit_Auto;
+    return !pos->ISize(containerWM).IsAuto();
   }
   // Else, we're row-oriented, which means cross axis is container's block
   // axis. We need to use IsAutoBSize() to catch e.g. %-BSize applied to
   // indefinite container BSize, which counts as auto.
   nscoord cbBSize = aItemReflowInput.mCBReflowInput->ComputedBSize();
   return !nsLayoutUtils::IsAutoBSize(pos->BSize(containerWM), cbBSize);
 }
 
@@ -1481,18 +1479,17 @@ static nscoord PartiallyResolveAutoMinSi
              "only call for FlexItems that need min-size auto resolution");
 
   nscoord minMainSize = nscoord_MAX;  // Intentionally huge; we'll shrink it
                                       // from here, w/ std::min().
 
   // We need the smallest of:
   // * the used flex-basis, if the computed flex-basis was 'auto':
   // XXXdholbert ('auto' might be renamed to 'main-size'; see bug 1032922)
-  if (eStyleUnit_Auto ==
-          aItemReflowInput.mStylePosition->mFlexBasis.GetUnit() &&
+  if (aItemReflowInput.mStylePosition->mFlexBasis.IsAuto() &&
       aFlexItem.GetFlexBaseSize() != NS_AUTOHEIGHT) {
     // NOTE: We skip this if the flex base size depends on content & isn't yet
     // resolved. This is OK, because the caller is responsible for computing
     // the min-content height and min()'ing it with the value we return, which
     // is equivalent to what would happen if we min()'d that at this point.
     minMainSize = std::min(minMainSize, aFlexItem.GetFlexBaseSize());
   }
 
@@ -2020,20 +2017,19 @@ void FlexItem::CheckForMinSizeAuto(const
   const nsStylePosition* pos = aFlexItemReflowInput.mStylePosition;
   const nsStyleDisplay* disp = aFlexItemReflowInput.mStyleDisplay;
 
   // We'll need special behavior for "min-[width|height]:auto" (whichever is in
   // the flex container's main axis) iff:
   // (a) its computed value is "auto"
   // (b) the "overflow" sub-property in the same axis (the main axis) has a
   //     computed value of "visible"
-  const nsStyleCoord& mainMinSize =
-      aAxisTracker.IsRowOriented()
-          ? pos->MinISize(aAxisTracker.GetWritingMode())
-          : pos->MinBSize(aAxisTracker.GetWritingMode());
+  const auto& mainMinSize = aAxisTracker.IsRowOriented()
+                                ? pos->MinISize(aAxisTracker.GetWritingMode())
+                                : pos->MinBSize(aAxisTracker.GetWritingMode());
 
   // NOTE: Technically we should be checking the 'overflow' subproperty in the
   // main axis. But since we only care whether it's 'visible', we can check
   // either subproperty -- because they must be BOTH 'visible' or BOTH
   // non-'visible' due to the way the subproperties interact.
   mNeedsMinSizeAutoResolution =
       IsAutoOrEnumOnBSize(mainMinSize, IsInlineAxisMainAxis()) &&
       disp->mOverflowX == StyleOverflow::Visible;
@@ -2078,19 +2074,18 @@ nscoord FlexItem::GetBaselineOffsetFromO
   return GetOuterCrossSize(crossAxis) - marginTopToBaseline;
 }
 
 bool FlexItem::IsCrossSizeAuto() const {
   const nsStylePosition* stylePos = mFrame->StylePosition();
   // Check whichever component is in the flex container's cross axis.
   // (IsInlineAxisCrossAxis() tells us whether that's our ISize or BSize, in
   // terms of our own WritingMode, mWM.)
-  return eStyleUnit_Auto == (IsInlineAxisCrossAxis()
-                                 ? stylePos->ISize(mWM).GetUnit()
-                                 : stylePos->BSize(mWM).GetUnit());
+  return IsInlineAxisCrossAxis() ? stylePos->ISize(mWM).IsAuto()
+                                 : stylePos->BSize(mWM).IsAuto();
 }
 
 uint32_t FlexItem::GetNumAutoMarginsInAxis(AxisOrientationType aAxis) const {
   uint32_t numAutoMargins = 0;
   const auto& styleMargin = mFrame->StyleMargin()->mMargin;
   for (uint32_t i = 0; i < eNumAxisEdges; i++) {
     mozilla::Side side = kAxisOrientationToSidesMap[aAxis][i];
     if (styleMargin.Get(side).IsAuto()) {
@@ -4201,21 +4196,21 @@ void nsFlexContainerFrame::Reflow(nsPres
 
   // We (and our children) can only depend on our ancestor's bsize if we have
   // a percent-bsize, or if we're positioned and we have "block-start" and
   // "block-end" set and have block-size:auto.  (There are actually other cases,
   // too -- e.g. if our parent is itself a block-dir flex container and we're
   // flexible -- but we'll let our ancestors handle those sorts of cases.)
   WritingMode wm = aReflowInput.GetWritingMode();
   const nsStylePosition* stylePos = StylePosition();
-  const nsStyleCoord& bsize = stylePos->BSize(wm);
-  if (bsize.HasPercent() ||
-      (StyleDisplay()->IsAbsolutelyPositionedStyle() && bsize.IsAutoOrEnum() &&
-       !stylePos->mOffset.GetBStart(wm).IsAuto() &&
-       !stylePos->mOffset.GetBEnd(wm).IsAuto())) {
+  const auto& bsize = stylePos->BSize(wm);
+  if (bsize.HasPercent() || (StyleDisplay()->IsAbsolutelyPositionedStyle() &&
+                             (bsize.IsAuto() || bsize.IsExtremumLength()) &&
+                             !stylePos->mOffset.GetBStart(wm).IsAuto() &&
+                             !stylePos->mOffset.GetBEnd(wm).IsAuto())) {
     AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
   }
 
   RenumberList();
 
   const FlexboxAxisTracker axisTracker(this, aReflowInput.GetWritingMode());
 
   // Check to see if we need to create a computed info structure, to
@@ -4460,25 +4455,25 @@ bool nsFlexContainerFrame::IsItemInlineA
 
   // aFrame's inline axis is its flex container's main axis IFF the above
   // questions have the same answer.
   return flexContainerIsRowOriented == itemInlineAxisIsParallelToParent;
 }
 
 /* static */
 bool nsFlexContainerFrame::IsUsedFlexBasisContent(
-    const nsStyleCoord* aFlexBasis, const nsStyleCoord* aMainSize) {
+    const StyleFlexBasis& aFlexBasis, const StyleSize& aMainSize) {
   // We have a used flex-basis of 'content' if flex-basis explicitly has that
   // value, OR if flex-basis is 'auto' (deferring to the main-size property)
   // and the main-size property is also 'auto'.
   // See https://drafts.csswg.org/css-flexbox-1/#valdef-flex-basis-auto
-  return (aFlexBasis->GetUnit() == eStyleUnit_Enumerated &&
-          aFlexBasis->GetIntValue() == NS_STYLE_FLEX_BASIS_CONTENT) ||
-         (aFlexBasis->GetUnit() == eStyleUnit_Auto &&
-          aMainSize->GetUnit() == eStyleUnit_Auto);
+  if (aFlexBasis.IsContent()) {
+    return true;
+  }
+  return aFlexBasis.IsAuto() && aMainSize.IsAuto();
 }
 
 static mozilla::dom::FlexPhysicalDirection ConvertAxisOrientationTypeToAPIEnum(
     AxisOrientationType aAxisOrientation) {
   switch (aAxisOrientation) {
     case eAxis_LR:
       return mozilla::dom::FlexPhysicalDirection::Horizontal_lr;
     case eAxis_RL:
--- a/layout/generic/nsFlexContainerFrame.h
+++ b/layout/generic/nsFlexContainerFrame.h
@@ -217,18 +217,18 @@ class nsFlexContainerFrame final : publi
   /**
    * Returns true iff the given computed 'flex-basis' & main-size property
    * values collectively represent a used flex-basis of 'content'.
    * See https://drafts.csswg.org/css-flexbox-1/#valdef-flex-basis-auto
    *
    * @param aFlexBasis the computed 'flex-basis' for a flex item.
    * @param aMainSize the computed main-size property for a flex item.
    */
-  static bool IsUsedFlexBasisContent(const nsStyleCoord* aFlexBasis,
-                                     const nsStyleCoord* aMainSize);
+  static bool IsUsedFlexBasisContent(const StyleFlexBasis& aFlexBasis,
+                                     const StyleSize& aMainSize);
 
   /**
    * Callback for nsFrame::MarkIntrinsicISizesDirty() on a flex item.
    */
   static void MarkCachedFlexMeasurementsDirty(nsIFrame* aItemFrame);
 
  protected:
   // Protected constructor & destructor
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -5358,18 +5358,18 @@ LogicalSize nsFrame::ComputeSize(gfxCont
   LogicalSize boxSizingAdjust(aWM);
   if (stylePos->mBoxSizing == StyleBoxSizing::Border) {
     boxSizingAdjust = aBorder + aPadding;
   }
   nscoord boxSizingToMarginEdgeISize = aMargin.ISize(aWM) + aBorder.ISize(aWM) +
                                        aPadding.ISize(aWM) -
                                        boxSizingAdjust.ISize(aWM);
 
-  const nsStyleCoord* inlineStyleCoord = &stylePos->ISize(aWM);
-  const nsStyleCoord* blockStyleCoord = &stylePos->BSize(aWM);
+  const auto* inlineStyleCoord = &stylePos->ISize(aWM);
+  const auto* blockStyleCoord = &stylePos->BSize(aWM);
 
   auto parentFrame = GetParent();
   auto alignCB = parentFrame;
   bool isGridItem = parentFrame && parentFrame->IsGridContainerFrame() &&
                     !HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
   if (parentFrame && parentFrame->IsTableWrapperFrame() && IsTableFrame()) {
     // An inner table frame is sized as a grid item if its table wrapper is,
     // because they actually have the same CB (the wrapper's CB).
@@ -5399,45 +5399,45 @@ LogicalSize nsFrame::ComputeSize(gfxCont
     // which case they use their main-size property after all.
     flexMainAxis = nsFlexContainerFrame::IsItemInlineAxisMainAxis(this)
                        ? eLogicalAxisInline
                        : eLogicalAxisBlock;
 
     // NOTE: The logic here should match the similar chunk for updating
     // mainAxisCoord in nsFrame::ComputeSizeWithIntrinsicDimensions() (aside
     // from using a different dummy value in the IsUsedFlexBasisContent() case).
-    const nsStyleCoord* flexBasis = &(stylePos->mFlexBasis);
+    const auto* flexBasis = &stylePos->mFlexBasis;
     auto& mainAxisCoord =
         (flexMainAxis == eLogicalAxisInline ? inlineStyleCoord
                                             : blockStyleCoord);
 
     // NOTE: If we're a table-wrapper frame, we skip this clause and just stick
     // with 'main-size:auto' behavior (which -- unlike 'content'
     // i.e. 'max-content' -- will give us the ability to honor percent sizes on
     // our table-box child when resolving the flex base size). The flexbox spec
     // doesn't call for this special case, but webcompat & regression-avoidance
     // seems to require it, for the time being... Tables sure are special.
-    if (nsFlexContainerFrame::IsUsedFlexBasisContent(flexBasis,
-                                                     mainAxisCoord) &&
+    if (nsFlexContainerFrame::IsUsedFlexBasisContent(*flexBasis,
+                                                     *mainAxisCoord) &&
         MOZ_LIKELY(!IsTableWrapperFrame())) {
-      static const nsStyleCoord maxContStyleCoord(NS_STYLE_WIDTH_MAX_CONTENT,
-                                                  eStyleUnit_Enumerated);
+      static const StyleSize maxContStyleCoord(
+          StyleSize::ExtremumLength(StyleExtremumLength::MaxContent));
       mainAxisCoord = &maxContStyleCoord;
       // (Note: if our main axis is the block axis, then this 'max-content'
       // value will be treated like 'auto', via the IsAutoBSize() call below.)
-    } else if (flexBasis->GetUnit() != eStyleUnit_Auto) {
+    } else if (!flexBasis->IsAuto()) {
       // For all other non-'auto' flex-basis values, we just swap in the
       // flex-basis itself for the main-size property.
-      mainAxisCoord = flexBasis;
+      mainAxisCoord = &flexBasis->AsSize();
     }  // else: flex-basis is 'auto', which is deferring to some explicit value
        // in mainAxisCoord. So we proceed w/o touching mainAxisCoord.
   }
 
   // Compute inline-axis size
-  if (inlineStyleCoord->GetUnit() != eStyleUnit_Auto) {
+  if (!inlineStyleCoord->IsAuto()) {
     result.ISize(aWM) = ComputeISizeValue(
         aRenderingContext, aCBSize.ISize(aWM), boxSizingAdjust.ISize(aWM),
         boxSizingToMarginEdgeISize, *inlineStyleCoord, aFlags);
   } else if (MOZ_UNLIKELY(isGridItem) && !IS_TRUE_OVERFLOW_CONTAINER(this)) {
     // 'auto' inline-size for grid-level box - fill the CB for 'stretch' /
     // 'normal' and clamp it to the CB if requested:
     bool stretch = false;
     if (!(aFlags & nsIFrame::eShrinkWrap) &&
@@ -5457,38 +5457,38 @@ LogicalSize nsFrame::ComputeSize(gfxCont
         result.ISize(aWM) = iSizeToFillCB;
       }
     }
   }
 
   // Flex items ignore their min & max sizing properties in their
   // flex container's main-axis.  (Those properties get applied later in
   // the flexbox algorithm.)
-  const nsStyleCoord& maxISizeCoord = stylePos->MaxISize(aWM);
+  const auto& maxISizeCoord = stylePos->MaxISize(aWM);
   nscoord maxISize = NS_UNCONSTRAINEDSIZE;
-  if (maxISizeCoord.GetUnit() != eStyleUnit_None &&
+  if (!maxISizeCoord.IsNone() &&
       !(isFlexItem && flexMainAxis == eLogicalAxisInline)) {
     maxISize = ComputeISizeValue(
         aRenderingContext, aCBSize.ISize(aWM), boxSizingAdjust.ISize(aWM),
         boxSizingToMarginEdgeISize, maxISizeCoord, aFlags);
     result.ISize(aWM) = std::min(maxISize, result.ISize(aWM));
   }
 
-  const nsStyleCoord& minISizeCoord = stylePos->MinISize(aWM);
+  const auto& minISizeCoord = stylePos->MinISize(aWM);
   nscoord minISize;
-  if (minISizeCoord.GetUnit() != eStyleUnit_Auto &&
+  if (!minISizeCoord.IsAuto() &&
       !(isFlexItem && flexMainAxis == eLogicalAxisInline)) {
     minISize = ComputeISizeValue(
         aRenderingContext, aCBSize.ISize(aWM), boxSizingAdjust.ISize(aWM),
         boxSizingToMarginEdgeISize, minISizeCoord, aFlags);
   } else if (MOZ_UNLIKELY(aFlags & eIApplyAutoMinSize)) {
     // This implements "Implied Minimum Size of Grid Items".
     // https://drafts.csswg.org/css-grid/#min-size-auto
     minISize = std::min(maxISize, GetMinISize(aRenderingContext));
-    if (inlineStyleCoord->IsCoordPercentCalcUnit()) {
+    if (inlineStyleCoord->IsLengthPercentage()) {
       minISize = std::min(minISize, result.ISize(aWM));
     } else if (aFlags & eIClampMarginBoxMinSize) {
       // "if the grid item spans only grid tracks that have a fixed max track
       // sizing function, its automatic minimum size in that dimension is
       // further clamped to less than or equal to the size necessary to fit
       // its margin box within the resulting grid area (flooring at zero)"
       // https://drafts.csswg.org/css-grid/#min-size-auto
       auto maxMinISize =
@@ -5508,18 +5508,20 @@ LogicalSize nsFrame::ComputeSize(gfxCont
 
   // Compute block-axis size
   // (but not if we have auto bsize or if we received the "eUseAutoBSize"
   // flag -- then, we'll just stick with the bsize that we already calculated
   // in the initial ComputeAutoSize() call.)
   if (!(aFlags & nsIFrame::eUseAutoBSize)) {
     if (!nsLayoutUtils::IsAutoBSize(*blockStyleCoord, aCBSize.BSize(aWM))) {
       result.BSize(aWM) = nsLayoutUtils::ComputeBSizeValue(
-          aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM), *blockStyleCoord);
-    } else if (MOZ_UNLIKELY(isGridItem) && blockStyleCoord->IsAutoOrEnum() &&
+          aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM),
+          blockStyleCoord->AsLengthPercentage());
+    } else if (MOZ_UNLIKELY(isGridItem) &&
+               blockStyleCoord->BehavesLikeInitialValueOnBlockAxis() &&
                !IS_TRUE_OVERFLOW_CONTAINER(this)) {
       auto cbSize = aCBSize.BSize(aWM);
       if (cbSize != NS_AUTOHEIGHT) {
         // 'auto' block-size for grid-level box - fill the CB for 'stretch' /
         // 'normal' and clamp it to the CB if requested:
         bool stretch = false;
         if (!StyleMargin()->HasBlockAxisAuto(aWM)) {
           auto blockAxisAlignment =
@@ -5537,32 +5539,34 @@ LogicalSize nsFrame::ComputeSize(gfxCont
                           result.BSize(aWM) > bSizeToFillCB)) {
             result.BSize(aWM) = bSizeToFillCB;
           }
         }
       }
     }
   }
 
-  const nsStyleCoord& maxBSizeCoord = stylePos->MaxBSize(aWM);
+  const auto& maxBSizeCoord = stylePos->MaxBSize(aWM);
 
   if (result.BSize(aWM) != NS_UNCONSTRAINEDSIZE) {
     if (!nsLayoutUtils::IsAutoBSize(maxBSizeCoord, aCBSize.BSize(aWM)) &&
         !(isFlexItem && flexMainAxis == eLogicalAxisBlock)) {
       nscoord maxBSize = nsLayoutUtils::ComputeBSizeValue(
-          aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM), maxBSizeCoord);
+          aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM),
+          maxBSizeCoord.AsLengthPercentage());
       result.BSize(aWM) = std::min(maxBSize, result.BSize(aWM));
     }
 
-    const nsStyleCoord& minBSizeCoord = stylePos->MinBSize(aWM);
+    const auto& minBSizeCoord = stylePos->MinBSize(aWM);
 
     if (!nsLayoutUtils::IsAutoBSize(minBSizeCoord, aCBSize.BSize(aWM)) &&
         !(isFlexItem && flexMainAxis == eLogicalAxisBlock)) {
       nscoord minBSize = nsLayoutUtils::ComputeBSizeValue(
-          aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM), minBSizeCoord);
+          aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM),
+          minBSizeCoord.AsLengthPercentage());
       result.BSize(aWM) = std::max(minBSize, result.BSize(aWM));
     }
   }
 
   const nsStyleDisplay* disp = StyleDisplay();
   if (IsThemed(disp)) {
     LayoutDeviceIntSize widget;
     bool canOverride = true;
@@ -5595,31 +5599,31 @@ LogicalSize nsFrame::ComputeSize(gfxCont
 
 LogicalSize nsFrame::ComputeSizeWithIntrinsicDimensions(
     gfxContext* aRenderingContext, WritingMode aWM,
     const IntrinsicSize& aIntrinsicSize, nsSize aIntrinsicRatio,
     const LogicalSize& aCBSize, const LogicalSize& aMargin,
     const LogicalSize& aBorder, const LogicalSize& aPadding,
     ComputeSizeFlags aFlags) {
   const nsStylePosition* stylePos = StylePosition();
-  const nsStyleCoord* inlineStyleCoord = &stylePos->ISize(aWM);
-  const nsStyleCoord* blockStyleCoord = &stylePos->BSize(aWM);
+  const auto* inlineStyleCoord = &stylePos->ISize(aWM);
+  const auto* blockStyleCoord = &stylePos->BSize(aWM);
   auto* parentFrame = GetParent();
   const bool isGridItem = parentFrame && parentFrame->IsGridContainerFrame() &&
                           !HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
   const bool isFlexItem =
       parentFrame && parentFrame->IsFlexContainerFrame() &&
       !parentFrame->HasAnyStateBits(NS_STATE_FLEX_IS_EMULATING_LEGACY_BOX) &&
       !HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
   // This variable only gets set (and used) if isFlexItem is true.  It
   // indicates which axis (in this frame's own WM) corresponds to its
   // flex container's main axis.
   LogicalAxis flexMainAxis =
       eLogicalAxisInline;  // (init to make valgrind happy)
-  Maybe<nsStyleCoord> imposedMainSizeStyleCoord;
+  Maybe<StyleSize> imposedMainSizeStyleCoord;
 
   // If this is a flex item, and we're measuring its cross size after flexing
   // to resolve its main size, then we need to use the resolved main size
   // that the container provides to us *instead of* the main-size coordinate
   // from our style struct. (Otherwise, we'll be using an irrelevant value in
   // the aspect-ratio calculations below.)
   if (isFlexItem) {
     flexMainAxis = nsFlexContainerFrame::IsItemInlineAxisMainAxis(this)
@@ -5628,71 +5632,71 @@ LogicalSize nsFrame::ComputeSizeWithIntr
 
     // If FlexItemMainSizeOverride frame-property is set, then that means the
     // flex container is imposing a main-size on this flex item for it to use
     // as its size in the container's main axis.
     bool didImposeMainSize;
     nscoord imposedMainSize =
         GetProperty(nsIFrame::FlexItemMainSizeOverride(), &didImposeMainSize);
     if (didImposeMainSize) {
-      imposedMainSizeStyleCoord.emplace(imposedMainSize,
-                                        nsStyleCoord::CoordConstructor);
+      imposedMainSizeStyleCoord = Some(StyleSize::LengthPercentage(
+          LengthPercentage::FromAppUnits(imposedMainSize)));
       if (flexMainAxis == eLogicalAxisInline) {
         inlineStyleCoord = imposedMainSizeStyleCoord.ptr();
       } else {
         blockStyleCoord = imposedMainSizeStyleCoord.ptr();
       }
 
     } else {
       // Flex items use their "flex-basis" property in place of their main-size
       // property (e.g. "width") for sizing purposes, *unless* they have
       // "flex-basis:auto", in which case they use their main-size property
       // after all.
       // NOTE: The logic here should match the similar chunk for updating
       // mainAxisCoord in nsFrame::ComputeSize() (aside from using a different
       // dummy value in the IsUsedFlexBasisContent() case).
-      const nsStyleCoord* flexBasis = &(stylePos->mFlexBasis);
+      const auto* flexBasis = &stylePos->mFlexBasis;
       auto& mainAxisCoord =
           (flexMainAxis == eLogicalAxisInline ? inlineStyleCoord
                                               : blockStyleCoord);
 
-      if (nsFlexContainerFrame::IsUsedFlexBasisContent(flexBasis,
-                                                       mainAxisCoord)) {
+      if (nsFlexContainerFrame::IsUsedFlexBasisContent(*flexBasis,
+                                                       *mainAxisCoord)) {
         // If we get here, we're resolving the flex base size for a flex item,
         // and we fall into the flexbox spec section 9.2 step 3, substep C (if
         // we have a definite cross size) or E (if not). And specifically:
         //
         // * If we have a definite cross size, we're supposed to resolve our
         //   main-size based on that and our intrinsic ratio.
         // * Otherwise, we're supposed to produce our max-content size.
         //
         // Conveniently, we can handle both of those scenarios (regardless of
         // which substep we fall into) by using the 'auto' keyword for our
         // main-axis coordinate here. (This makes sense, because the spec is
         // effectively trying to produce the 'auto' sizing behavior).
-        static const nsStyleCoord autoStyleCoord(eStyleUnit_Auto);
-        mainAxisCoord = &autoStyleCoord;
-      } else if (flexBasis->GetUnit() != eStyleUnit_Auto) {
+        static const StyleSize autoSize(StyleSize::Auto());
+        mainAxisCoord = &autoSize;
+      } else if (!flexBasis->IsAuto()) {
         // For all other non-'auto' flex-basis values, we just swap in the
         // flex-basis itself for the main-size property.
-        mainAxisCoord = flexBasis;
+        mainAxisCoord = &flexBasis->AsSize();
       }  // else: flex-basis is 'auto', which is deferring to some explicit
          // value in mainAxisCoord. So we proceed w/o touching mainAxisCoord.
     }
   }
 
   // Handle intrinsic sizes and their interaction with
   // {min-,max-,}{width,height} according to the rules in
   // http://www.w3.org/TR/CSS21/visudet.html#min-max-widths
 
   // Note: throughout the following section of the function, I avoid
   // a * (b / c) because of its reduced accuracy relative to a * b / c
   // or (a * b) / c (which are equivalent).
 
-  const bool isAutoISize = inlineStyleCoord->GetUnit() == eStyleUnit_Auto;
+  const bool isAutoISize = inlineStyleCoord->IsAuto();
   const bool isAutoBSize =
       nsLayoutUtils::IsAutoBSize(*blockStyleCoord, aCBSize.BSize(aWM));
 
   LogicalSize boxSizingAdjust(aWM);
   if (stylePos->mBoxSizing == StyleBoxSizing::Border) {
     boxSizingAdjust = aBorder + aPadding;
   }
   nscoord boxSizingToMarginEdgeISize = aMargin.ISize(aWM) + aBorder.ISize(aWM) +
@@ -5774,50 +5778,51 @@ LogicalSize nsFrame::ComputeSizeWithIntr
       }
     } else {
       // Reset this flag to avoid applying the clamping below.
       aFlags =
           ComputeSizeFlags(aFlags & ~ComputeSizeFlags::eIClampMarginBoxMinSize);
     }
   }
 
-  const nsStyleCoord& maxISizeCoord = stylePos->MaxISize(aWM);
-
-  if (maxISizeCoord.GetUnit() != eStyleUnit_None &&
+  const auto& maxISizeCoord = stylePos->MaxISize(aWM);
+
+  if (!maxISizeCoord.IsNone() &&
       !(isFlexItem && flexMainAxis == eLogicalAxisInline)) {
     maxISize = ComputeISizeValue(
         aRenderingContext, aCBSize.ISize(aWM), boxSizingAdjust.ISize(aWM),
         boxSizingToMarginEdgeISize, maxISizeCoord, aFlags);
   } else {
     maxISize = nscoord_MAX;
   }
 
   // NOTE: Flex items ignore their min & max sizing properties in their
   // flex container's main-axis.  (Those properties get applied later in
   // the flexbox algorithm.)
 
-  const nsStyleCoord& minISizeCoord = stylePos->MinISize(aWM);
-
-  if (minISizeCoord.GetUnit() != eStyleUnit_Auto &&
+  const auto& minISizeCoord = stylePos->MinISize(aWM);
+
+  if (!minISizeCoord.IsAuto() &&
       !(isFlexItem && flexMainAxis == eLogicalAxisInline)) {
     minISize = ComputeISizeValue(
         aRenderingContext, aCBSize.ISize(aWM), boxSizingAdjust.ISize(aWM),
         boxSizingToMarginEdgeISize, minISizeCoord, aFlags);
   } else {
     // Treat "min-width: auto" as 0.
     // NOTE: Technically, "auto" is supposed to behave like "min-content" on
     // flex items. However, we don't need to worry about that here, because
     // flex items' min-sizes are intentionally ignored until the flex
     // container explicitly considers them during space distribution.
     minISize = 0;
   }
 
   if (!isAutoBSize) {
     bSize = nsLayoutUtils::ComputeBSizeValue(
-        aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM), *blockStyleCoord);
+        aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM),
+        blockStyleCoord->AsLengthPercentage());
   } else if (MOZ_UNLIKELY(isGridItem)) {
     MOZ_ASSERT(!IS_TRUE_OVERFLOW_CONTAINER(this));
     // 'auto' block-size for grid-level box - apply 'stretch' as needed:
     auto cbSize = aCBSize.BSize(aWM);
     if (cbSize != NS_AUTOHEIGHT) {
       if (!StyleMargin()->HasBlockAxisAuto(aWM)) {
         auto blockAxisAlignment =
             !aWM.IsOrthogonalTo(GetParent()->GetWritingMode())
@@ -5840,32 +5845,34 @@ LogicalSize nsFrame::ComputeSizeWithIntr
       }
     } else {
       // Reset this flag to avoid applying the clamping below.
       aFlags =
           ComputeSizeFlags(aFlags & ~ComputeSizeFlags::eBClampMarginBoxMinSize);
     }
   }
 
-  const nsStyleCoord& maxBSizeCoord = stylePos->MaxBSize(aWM);
+  const auto& maxBSizeCoord = stylePos->MaxBSize(aWM);
 
   if (!nsLayoutUtils::IsAutoBSize(maxBSizeCoord, aCBSize.BSize(aWM)) &&
       !(isFlexItem && flexMainAxis == eLogicalAxisBlock)) {
     maxBSize = nsLayoutUtils::ComputeBSizeValue(
-        aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM), maxBSizeCoord);
+        aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM),
+        maxBSizeCoord.AsLengthPercentage());
   } else {
     maxBSize = nscoord_MAX;
   }
 
-  const nsStyleCoord& minBSizeCoord = stylePos->MinBSize(aWM);
+  const auto& minBSizeCoord = stylePos->MinBSize(aWM);
 
   if (!nsLayoutUtils::IsAutoBSize(minBSizeCoord, aCBSize.BSize(aWM)) &&
       !(isFlexItem && flexMainAxis == eLogicalAxisBlock)) {
     minBSize = nsLayoutUtils::ComputeBSizeValue(
-        aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM), minBSizeCoord);
+        aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM),
+        minBSizeCoord.AsLengthPercentage());
   } else {
     minBSize = 0;
   }
 
   NS_ASSERTION(aCBSize.ISize(aWM) != NS_UNCONSTRAINEDSIZE,
                "Our containing block must not have unconstrained inline-size!");
 
   // Now calculate the used values for iSize and bSize:
@@ -6059,17 +6066,17 @@ LogicalSize nsFrame::ComputeAutoSize(
     gfxContext* aRenderingContext, WritingMode aWM,
     const mozilla::LogicalSize& aCBSize, nscoord aAvailableISize,
     const mozilla::LogicalSize& aMargin, const mozilla::LogicalSize& aBorder,
     const mozilla::LogicalSize& aPadding, ComputeSizeFlags aFlags) {
   // Use basic shrink-wrapping as a default implementation.
   LogicalSize result(aWM, 0xdeadbeef, NS_UNCONSTRAINEDSIZE);
 
   // don't bother setting it if the result won't be used
-  if (StylePosition()->ISize(aWM).GetUnit() == eStyleUnit_Auto) {
+  if (StylePosition()->ISize(aWM).IsAuto()) {
     nscoord availBased = aAvailableISize - aMargin.ISize(aWM) -
                          aBorder.ISize(aWM) - aPadding.ISize(aWM);
     result.ISize(aWM) = ShrinkWidthToFit(aRenderingContext, availBased, aFlags);
   }
   return result;
 }
 
 nscoord nsFrame::ShrinkWidthToFit(gfxContext* aRenderingContext,
@@ -6093,71 +6100,75 @@ nscoord nsFrame::ShrinkWidthToFit(gfxCon
   }
   return result;
 }
 
 nscoord nsIFrame::ComputeISizeValue(gfxContext* aRenderingContext,
                                     nscoord aContainingBlockISize,
                                     nscoord aContentEdgeToBoxSizing,
                                     nscoord aBoxSizingToMarginEdge,
-                                    const nsStyleCoord& aCoord,
+                                    StyleExtremumLength aSize,
+                                    ComputeSizeFlags aFlags) {
+  // If 'this' is a container for font size inflation, then shrink
+  // wrapping inside of it should not apply font size inflation.
+  AutoMaybeDisableFontInflation an(this);
+  nscoord result;
+  switch (aSize) {
+    case StyleExtremumLength::MaxContent:
+      result = GetPrefISize(aRenderingContext);
+      NS_ASSERTION(result >= 0, "inline-size less than zero");
+      return result;
+    case StyleExtremumLength::MinContent:
+      result = GetMinISize(aRenderingContext);
+      NS_ASSERTION(result >= 0, "inline-size less than zero");
+      if (MOZ_UNLIKELY(aFlags & ComputeSizeFlags::eIClampMarginBoxMinSize)) {
+        auto available = aContainingBlockISize -
+                         (aBoxSizingToMarginEdge + aContentEdgeToBoxSizing);
+        result = std::min(available, result);
+      }
+      return result;
+    case StyleExtremumLength::MozFitContent: {
+      nscoord pref = GetPrefISize(aRenderingContext),
+              min = GetMinISize(aRenderingContext),
+              fill = aContainingBlockISize -
+                     (aBoxSizingToMarginEdge + aContentEdgeToBoxSizing);
+      if (MOZ_UNLIKELY(aFlags & ComputeSizeFlags::eIClampMarginBoxMinSize)) {
+        min = std::min(min, fill);
+      }
+      result = std::max(min, std::min(pref, fill));
+      NS_ASSERTION(result >= 0, "inline-size less than zero");
+      return result;
+    }
+    case StyleExtremumLength::MozAvailable:
+      return aContainingBlockISize -
+             (aBoxSizingToMarginEdge + aContentEdgeToBoxSizing);
+  }
+  MOZ_ASSERT_UNREACHABLE("Unknown extremum length?");
+  return 0;
+}
+
+nscoord nsIFrame::ComputeISizeValue(gfxContext* aRenderingContext,
+                                    nscoord aContainingBlockISize,
+                                    nscoord aContentEdgeToBoxSizing,
+                                    nscoord aBoxSizingToMarginEdge,
+                                    const LengthPercentage& aCoord,
                                     ComputeSizeFlags aFlags) {
   MOZ_ASSERT(aRenderingContext, "non-null rendering context expected");
   LAYOUT_WARN_IF_FALSE(
       aContainingBlockISize != NS_UNCONSTRAINEDSIZE,
       "have unconstrained inline-size; this should only result from "
       "very large sizes, not attempts at intrinsic inline-size "
       "calculation");
   MOZ_ASSERT(aContainingBlockISize >= 0, "inline-size less than zero");
 
-  nscoord result;
-  if (aCoord.IsCoordPercentCalcUnit()) {
-    result = aCoord.ComputeCoordPercentCalc(aContainingBlockISize);
-    // The result of a calc() expression might be less than 0; we
-    // should clamp at runtime (below).  (Percentages and coords that
-    // are less than 0 have already been dropped by the parser.)
-    result -= aContentEdgeToBoxSizing;
-  } else {
-    MOZ_ASSERT(eStyleUnit_Enumerated == aCoord.GetUnit());
-    // If 'this' is a container for font size inflation, then shrink
-    // wrapping inside of it should not apply font size inflation.
-    AutoMaybeDisableFontInflation an(this);
-
-    int32_t val = aCoord.GetIntValue();
-    switch (val) {
-      case NS_STYLE_WIDTH_MAX_CONTENT:
-        result = GetPrefISize(aRenderingContext);
-        NS_ASSERTION(result >= 0, "inline-size less than zero");
-        break;
-      case NS_STYLE_WIDTH_MIN_CONTENT:
-        result = GetMinISize(aRenderingContext);
-        NS_ASSERTION(result >= 0, "inline-size less than zero");
-        if (MOZ_UNLIKELY(aFlags & ComputeSizeFlags::eIClampMarginBoxMinSize)) {
-          auto available = aContainingBlockISize -
-                           (aBoxSizingToMarginEdge + aContentEdgeToBoxSizing);
-          result = std::min(available, result);
-        }
-        break;
-      case NS_STYLE_WIDTH_FIT_CONTENT: {
-        nscoord pref = GetPrefISize(aRenderingContext),
-                min = GetMinISize(aRenderingContext),
-                fill = aContainingBlockISize -
-                       (aBoxSizingToMarginEdge + aContentEdgeToBoxSizing);
-        if (MOZ_UNLIKELY(aFlags & ComputeSizeFlags::eIClampMarginBoxMinSize)) {
-          min = std::min(min, fill);
-        }
-        result = std::max(min, std::min(pref, fill));
-        NS_ASSERTION(result >= 0, "inline-size less than zero");
-      } break;
-      case NS_STYLE_WIDTH_AVAILABLE:
-        result = aContainingBlockISize -
-                 (aBoxSizingToMarginEdge + aContentEdgeToBoxSizing);
-    }
-  }
-
+  nscoord result = aCoord.Resolve(aContainingBlockISize);
+  // The result of a calc() expression might be less than 0; we
+  // should clamp at runtime (below).  (Percentages and coords that
+  // are less than 0 have already been dropped by the parser.)
+  result -= aContentEdgeToBoxSizing;
   return std::max(0, result);
 }
 
 void nsFrame::DidReflow(nsPresContext* aPresContext,
                         const ReflowInput* aReflowInput) {
   NS_FRAME_TRACE_MSG(NS_FRAME_TRACE_CALLS, ("nsFrame::DidReflow"));
 
   SVGObserverUtils::InvalidateDirectRenderingObservers(
@@ -6166,17 +6177,17 @@ void nsFrame::DidReflow(nsPresContext* a
   RemoveStateBits(NS_FRAME_IN_REFLOW | NS_FRAME_FIRST_REFLOW |
                   NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN);
 
   // Notify the percent bsize observer if there is a percent bsize.
   // The observer may be able to initiate another reflow with a computed
   // bsize. This happens in the case where a table cell has no computed
   // bsize but can fabricate one when the cell bsize is known.
   if (aReflowInput && aReflowInput->mPercentBSizeObserver && !GetPrevInFlow()) {
-    const nsStyleCoord& bsize =
+    const auto& bsize =
         aReflowInput->mStylePosition->BSize(aReflowInput->GetWritingMode());
     if (bsize.HasPercent()) {
       aReflowInput->mPercentBSizeObserver->NotifyPercentBSize(*aReflowInput);
     }
   }
 
   aPresContext->ReflowedFrame();
 }
--- a/layout/generic/nsGridContainerFrame.cpp
+++ b/layout/generic/nsGridContainerFrame.cpp
@@ -98,17 +98,18 @@ static nscoord ClampToCSSMaxBSize(nscoor
       aSize = maxSize;
     }
   } else {
     aStatus->SetIncomplete();
   }
   return aSize;
 }
 
-static bool IsPercentOfIndefiniteSize(const nsStyleCoord& aCoord,
+template <typename Size>
+static bool IsPercentOfIndefiniteSize(const Size& aCoord,
                                       nscoord aPercentBasis) {
   return aPercentBasis == NS_UNCONSTRAINEDSIZE && aCoord.HasPercent();
 }
 
 static nscoord ResolveToDefiniteSize(const nsStyleCoord& aCoord,
                                      nscoord aPercentBasis) {
   MOZ_ASSERT(aCoord.IsCoordPercentCalcUnit());
   if (::IsPercentOfIndefiniteSize(aCoord, aPercentBasis)) {
@@ -587,36 +588,36 @@ struct nsGridContainerFrame::GridItemInf
             ? mFrame->PrincipalChildList().FirstChild()->StylePosition()
             : mFrame->StylePosition();
     const auto& size =
         isInlineAxis ? pos->ISize(aContainerWM) : pos->BSize(aContainerWM);
     // max-content and min-content should behave as initial value in block axis.
     // FIXME: Bug 567039: moz-fit-content and -moz-available are not supported
     // for block size dimension on sizing properties (e.g. height), so we
     // treat it as `auto`.
-    bool isAuto = size.GetUnit() == eStyleUnit_Auto ||
+    bool isAuto = size.IsAuto() ||
                   (isInlineAxis ==
                        aContainerWM.IsOrthogonalTo(mFrame->GetWritingMode()) &&
-                   size.GetUnit() == eStyleUnit_Enumerated);
+                   size.IsExtremumLength());
     // NOTE: if we have a definite size then our automatic minimum size
     // can't affect our size.  Excluding these simplifies applying
     // the clamping in the right cases later.
     if (!isAuto && !::IsPercentOfIndefiniteSize(size, aPercentageBasis)) {
       return false;
     }
     const auto& minSize = isInlineAxis ? pos->MinISize(aContainerWM)
                                        : pos->MinBSize(aContainerWM);
     // max-content and min-content should behave as initial value in block axis.
     // FIXME: Bug 567039: moz-fit-content and -moz-available are not supported
     // for block size dimension on sizing properties (e.g. height), so we
     // treat it as `auto`.
-    isAuto = minSize.GetUnit() == eStyleUnit_Auto ||
+    isAuto = minSize.IsAuto() ||
              (isInlineAxis ==
                   aContainerWM.IsOrthogonalTo(mFrame->GetWritingMode()) &&
-              minSize.GetUnit() == eStyleUnit_Enumerated);
+              minSize.IsExtremumLength());
     return isAuto &&
            mFrame->StyleDisplay()->mOverflowX == StyleOverflow::Visible;
   }
 
 #ifdef DEBUG
   void Dump() const;
 #endif
 
@@ -3543,29 +3544,29 @@ static nscoord MinSize(const GridItemInf
                        WritingMode aCBWM, LogicalAxis aAxis,
                        CachedIntrinsicSizes* aCache) {
   if (aCache->mMinSize.isSome()) {
     return aCache->mMinSize.value();
   }
   nsIFrame* child = aGridItem.mFrame;
   PhysicalAxis axis(aCBWM.PhysicalAxis(aAxis));
   const nsStylePosition* stylePos = child->StylePosition();
-  nsStyleCoord sizeStyle =
+  StyleSize sizeStyle =
       axis == eAxisHorizontal ? stylePos->mWidth : stylePos->mHeight;
 
   auto ourInlineAxis = child->GetWritingMode().PhysicalAxis(eLogicalAxisInline);
   // max-content and min-content should behave as initial value in block axis.
   // FIXME: Bug 567039: moz-fit-content and -moz-available are not supported
   // for block size dimension on sizing properties (e.g. height), so we
   // treat it as `auto`.
-  if (axis != ourInlineAxis && sizeStyle.GetUnit() == eStyleUnit_Enumerated) {
-    sizeStyle.SetAutoValue();
-  }
-
-  if (sizeStyle.GetUnit() != eStyleUnit_Auto && !sizeStyle.HasPercent()) {
+  if (axis != ourInlineAxis && sizeStyle.IsExtremumLength()) {
+    sizeStyle = StyleSize::Auto();
+  }
+
+  if (!sizeStyle.IsAuto() && !sizeStyle.HasPercent()) {
     nscoord s =
         MinContentContribution(aGridItem, aState, aRC, aCBWM, aAxis, aCache);
     aCache->mMinSize.emplace(s);
     return s;
   }
 
   if (aCache->mPercentageBasis.isNothing()) {
     aCache->mPercentageBasis.emplace(
@@ -3582,30 +3583,29 @@ static nscoord MinSize(const GridItemInf
              "baseline offset should be non-negative at this point");
   MOZ_ASSERT((aGridItem.mState[aAxis] & ItemState::eIsBaselineAligned) ||
                  aGridItem.mBaselineOffset[aAxis] == nscoord(0),
              "baseline offset should be zero when not baseline-aligned");
   nscoord sz = aGridItem.mBaselineOffset[aAxis] +
                nsLayoutUtils::MinSizeContributionForAxis(
                    axis, aRC, child, nsLayoutUtils::MIN_ISIZE,
                    *aCache->mPercentageBasis);
-  const nsStyleCoord& style =
+  const StyleSize& style =
       axis == eAxisHorizontal ? stylePos->mMinWidth : stylePos->mMinHeight;
   // max-content and min-content should behave as initial value in block axis.
   // FIXME: Bug 567039: moz-fit-content and -moz-available are not supported
   // for block size dimension on sizing properties (e.g. height), so we
   // treat it as `auto`.
-  auto unit = axis != ourInlineAxis && style.GetUnit() == eStyleUnit_Enumerated
-                  ? eStyleUnit_Auto
-                  : style.GetUnit();
-  if (unit == eStyleUnit_Enumerated ||
-      (unit == eStyleUnit_Auto &&
-       child->StyleDisplay()->mOverflowX == StyleOverflow::Visible)) {
+  const bool inInlineAxis = axis == ourInlineAxis;
+  const bool isAuto =
+      style.IsAuto() || (!inInlineAxis && style.IsExtremumLength());
+  if ((inInlineAxis && style.IsExtremumLength()) ||
+      (isAuto && child->StyleDisplay()->mOverflowX == StyleOverflow::Visible)) {
     // Now calculate the "content size" part and return whichever is smaller.
-    MOZ_ASSERT(unit != eStyleUnit_Enumerated || sz == NS_UNCONSTRAINEDSIZE);
+    MOZ_ASSERT(isAuto || sz == NS_UNCONSTRAINEDSIZE);
     sz = std::min(
         sz, ContentContribution(aGridItem, aState, aRC, aCBWM, aAxis,
                                 aCache->mPercentageBasis,
                                 nsLayoutUtils::MIN_ISIZE, aCache->mMinSizeClamp,
                                 nsLayoutUtils::MIN_INTRINSIC_ISIZE));
   }
   aCache->mMinSize.emplace(sz);
   return sz;
@@ -4862,17 +4862,18 @@ void nsGridContainerFrame::ReflowInFlowC
   }
 
   // If the child is stretching in its block axis, and we might be fragmenting
   // it in that axis, then setup a frame property to tell
   // nsBlockFrame::ComputeFinalSize the size.
   if (isConstrainedBSize && !wm.IsOrthogonalTo(childWM)) {
     bool stretch = false;
     if (!childRI.mStyleMargin->HasBlockAxisAuto(childWM) &&
-        childRI.mStylePosition->BSize(childWM).IsAutoOrEnum()) {
+        (childRI.mStylePosition->BSize(childWM).IsAuto() ||
+         childRI.mStylePosition->BSize(childWM).IsExtremumLength())) {
       auto blockAxisAlignment = childRI.mStylePosition->UsedAlignSelf(Style());
       if (blockAxisAlignment == NS_STYLE_ALIGN_NORMAL ||
           blockAxisAlignment == NS_STYLE_ALIGN_STRETCH) {
         stretch = true;
       }
     }
     if (stretch) {
       aChild->SetProperty(FragStretchBSizeProperty(), *aStretchBSize);
@@ -6152,19 +6153,19 @@ nscoord nsGridContainerFrame::IntrinsicI
                                              IntrinsicISizeType aType) {
   RenumberList();
 
   // Calculate the sum of column sizes under intrinsic sizing.
   // http://dev.w3.org/csswg/css-grid/#intrinsic-sizes
   GridReflowInput state(this, *aRenderingContext);
   InitImplicitNamedAreas(state.mGridStyle);  // XXX optimize
 
-  auto GetDefiniteSizes = [](const nsStyleCoord& aMinCoord,
-                             const nsStyleCoord& aSizeCoord,
-                             const nsStyleCoord& aMaxCoord, nscoord* aMin,
+  auto GetDefiniteSizes = [](const StyleSize& aMinCoord,
+                             const StyleSize& aSizeCoord,
+                             const StyleMaxSize& aMaxCoord, nscoord* aMin,
                              nscoord* aSize, nscoord* aMax) {
     if (aMinCoord.ConvertsToLength()) {
       *aMin = aMinCoord.ToLength();
     }
     if (aMaxCoord.ConvertsToLength()) {
       *aMax = std::max(*aMin, aMaxCoord.ToLength());
     }
     if (aSizeCoord.ConvertsToLength()) {
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -546,16 +546,20 @@ class nsIFrame : public nsQueryFrame {
   using Maybe = mozilla::Maybe<T>;
   using Nothing = mozilla::Nothing;
   using OnNonvisible = mozilla::OnNonvisible;
   template <typename T = void>
   using PropertyDescriptor = const mozilla::FramePropertyDescriptor<T>*;
   using ReflowInput = mozilla::ReflowInput;
   using ReflowOutput = mozilla::ReflowOutput;
   using Visibility = mozilla::Visibility;
+  using StyleFlexBasis = mozilla::StyleFlexBasis;
+  using StyleSize = mozilla::StyleSize;
+  using LengthPercentage = mozilla::LengthPercentage;
+  using StyleExtremumLength = mozilla::StyleExtremumLength;
 
   typedef mozilla::ComputedStyle ComputedStyle;
   typedef mozilla::FrameProperties FrameProperties;
   typedef mozilla::layers::Layer Layer;
   typedef mozilla::layers::LayerManager LayerManager;
   typedef mozilla::layout::FrameChildList ChildList;
   typedef mozilla::layout::FrameChildListID ChildListID;
   typedef mozilla::layout::FrameChildListIDs ChildListIDs;
@@ -4091,24 +4095,49 @@ class nsIFrame : public nsQueryFrame {
    */
   virtual bool RenumberFrameAndDescendants(int32_t* aOrdinal, int32_t aDepth,
                                            int32_t aIncrement,
                                            bool aForCounting) {
     return false;
   }
 
   /**
-   * Helper function - computes the content-box inline size for aCoord.
+   * Helper function - computes the content-box inline size for aSize.
    */
   nscoord ComputeISizeValue(gfxContext* aRenderingContext,
                             nscoord aContainingBlockISize,
                             nscoord aContentEdgeToBoxSizing,
                             nscoord aBoxSizingToMarginEdge,
-                            const nsStyleCoord& aCoord,
-                            ComputeSizeFlags aFlags = eDefault);
+                            StyleExtremumLength aSize, ComputeSizeFlags aFlags);
+
+  nscoord ComputeISizeValue(gfxContext* aRenderingContext,
+                            nscoord aContainingBlockISize,
+                            nscoord aContentEdgeToBoxSizing,
+                            nscoord aBoxSizingToMarginEdge,
+                            const LengthPercentage& aSize,
+                            ComputeSizeFlags aFlags);
+
+  template <typename SizeOrMaxSize>
+  nscoord ComputeISizeValue(gfxContext* aRenderingContext,
+                            nscoord aContainingBlockISize,
+                            nscoord aContentEdgeToBoxSizing,
+                            nscoord aBoxSizingToMarginEdge,
+                            const SizeOrMaxSize& aSize,
+                            ComputeSizeFlags aFlags = eDefault) {
+    MOZ_ASSERT(aSize.IsExtremumLength() || aSize.IsLengthPercentage(),
+               "This doesn't handle auto / none");
+    if (aSize.IsLengthPercentage()) {
+      return ComputeISizeValue(aRenderingContext, aContainingBlockISize,
+                               aContentEdgeToBoxSizing, aBoxSizingToMarginEdge,
+                               aSize.AsLengthPercentage(), aFlags);
+    }
+    return ComputeISizeValue(aRenderingContext, aContainingBlockISize,
+                             aContentEdgeToBoxSizing, aBoxSizingToMarginEdge,
+                             aSize.AsExtremumLength(), aFlags);
+  }
 
   DisplayItemDataArray& DisplayItemData() { return mDisplayItemData; }
   const DisplayItemDataArray& DisplayItemData() const {
     return mDisplayItemData;
   }
 
   void AddDisplayItem(nsDisplayItem* aItem);
   bool RemoveDisplayItem(nsDisplayItem* aItem);
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -110,18 +110,18 @@ nsIIOService* nsImageFrame::sIOService;
 
 // test if the width and height are fixed, looking at the style data
 // This is used by nsImageFrame::ShouldCreateImageFrameFor and should
 // not be used for layout decisions.
 static bool HaveSpecifiedSize(const nsStylePosition* aStylePosition) {
   // check the width and height values in the reflow state's style struct
   // - if width and height are specified as either coord or percentage, then
   //   the size of the image frame is constrained
-  return aStylePosition->mWidth.IsCoordPercentCalcUnit() &&
-         aStylePosition->mHeight.IsCoordPercentCalcUnit();
+  return aStylePosition->mWidth.IsLengthPercentage() &&
+         aStylePosition->mHeight.IsLengthPercentage();
 }
 
 // Decide whether we can optimize away reflows that result from the
 // image's intrinsic size changing.
 static bool HaveFixedSize(const ReflowInput& aReflowInput) {
   NS_ASSERTION(aReflowInput.mStylePosition,
                "crappy reflowInput - null stylePosition");
   // Don't try to make this optimization when an image has percentages
@@ -2615,18 +2615,17 @@ nsImageListener::Notify(imgIRequest* aRe
 static bool IsInAutoWidthTableCellForQuirk(nsIFrame* aFrame) {
   if (eCompatibility_NavQuirks != aFrame->PresContext()->CompatibilityMode())
     return false;
   // Check if the parent of the closest nsBlockFrame has auto width.
   nsBlockFrame* ancestor = nsLayoutUtils::FindNearestBlockAncestor(aFrame);
   if (ancestor->Style()->GetPseudo() == nsCSSAnonBoxes::cellContent()) {
     // Assume direct parent is a table cell frame.
     nsFrame* grandAncestor = static_cast<nsFrame*>(ancestor->GetParent());
-    return grandAncestor &&
-           grandAncestor->StylePosition()->mWidth.GetUnit() == eStyleUnit_Auto;
+    return grandAncestor && grandAncestor->StylePosition()->mWidth.IsAuto();
   }
   return false;
 }
 
 /* virtual */ void nsImageFrame::AddInlineMinISize(
     gfxContext* aRenderingContext, nsIFrame::InlineMinISizeData* aData) {
   nscoord isize = nsLayoutUtils::IntrinsicForContainer(
       aRenderingContext, this, nsLayoutUtils::MIN_ISIZE);
--- a/layout/generic/nsLineLayout.cpp
+++ b/layout/generic/nsLineLayout.cpp
@@ -694,26 +694,25 @@ static bool IsPercentageAware(const nsIF
   if (HasPercentageUnitSide(padding->mPadding)) {
     return true;
   }
 
   // Note that borders can't be aware of percentages
 
   const nsStylePosition* pos = aFrame->StylePosition();
 
-  if ((pos->ISizeDependsOnContainer(aWM) &&
-       pos->ISize(aWM).GetUnit() != eStyleUnit_Auto) ||
+  if ((pos->ISizeDependsOnContainer(aWM) && !pos->ISize(aWM).IsAuto()) ||
       pos->MaxISizeDependsOnContainer(aWM) ||
       pos->MinISizeDependsOnContainer(aWM) ||
       pos->OffsetHasPercent(aWM.IsVertical() ? eSideBottom : eSideRight) ||
       pos->OffsetHasPercent(aWM.IsVertical() ? eSideTop : eSideLeft)) {
     return true;
   }
 
-  if (eStyleUnit_Auto == pos->ISize(aWM).GetUnit()) {
+  if (pos->ISize(aWM).IsAuto()) {
     // We need to check for frames that shrink-wrap when they're auto
     // width.
     const nsStyleDisplay* disp = aFrame->StyleDisplay();
     if (disp->mDisplay == StyleDisplay::InlineBlock ||
         disp->mDisplay == StyleDisplay::InlineTable ||
         fType == LayoutFrameType::HTMLButtonControl ||
         fType == LayoutFrameType::GfxButtonControl ||
         fType == LayoutFrameType::FieldSet ||
@@ -726,17 +725,17 @@ static bool IsPercentageAware(const nsIF
     //   the element has an intrinsic ratio but no intrinsic height or
     //   width and the containing block's width does not itself depend
     //   on the replaced element's width, then the used value of 'width'
     //   is calculated from the constraint equation used for
     //   block-level, non-replaced elements in normal flow.
     nsIFrame* f = const_cast<nsIFrame*>(aFrame);
     if (f->GetIntrinsicRatio() != nsSize(0, 0) &&
         // Some percents are treated like 'auto', so check != coord
-        pos->BSize(aWM).GetUnit() != eStyleUnit_Coord) {
+        !pos->BSize(aWM).ConvertsToLength()) {
       const IntrinsicSize& intrinsicSize = f->GetIntrinsicSize();
       if (intrinsicSize.width.GetUnit() == eStyleUnit_None &&
           intrinsicSize.height.GetUnit() == eStyleUnit_None) {
         return true;
       }
     }
   }
 
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -412,16 +412,22 @@ cbindgen-types = [
     { gecko = "StyleOverflow", servo = "values::computed::Overflow" },
     { gecko = "StyleOverflowAnchor", servo = "values::computed::OverflowAnchor" },
     { gecko = "StyleLengthPercentage", servo = "values::computed::LengthPercentage" },
     { gecko = "StyleNonNegativeLengthPercentage", servo = "values::computed::NonNegativeLengthPercentage" },
     { gecko = "StyleGenericLengthPercentageOrAuto", servo = "values::generics::length::LengthPercentageOrAuto" },
     { gecko = "StyleLengthPercentageOrAuto", servo = "values::computed::LengthPercentageOrAuto" },
     { gecko = "StyleRect", servo = "values::generics::rect::Rect" },
     { gecko = "StyleIntersectionObserverRootMargin", servo = "values::specified::gecko::IntersectionObserverRootMargin" },
+    { gecko = "StyleGenericSize", servo = "values::generics::length::Size" },
+    { gecko = "StyleGenericMaxSize", servo = "values::generics::length::MaxSize" },
+    { gecko = "StyleGenericFlexBasis", servo = "values::generics::flex::FlexBasis" },
+    { gecko = "StyleSize", servo = "values::computed::Size" },
+    { gecko = "StyleMaxSize", servo = "values::computed::MaxSize" },
+    { gecko = "StyleFlexBasis", servo = "values::computed::FlexBasis" },
 ]
 
 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/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -414,38 +414,16 @@ const KTableEntry nsCSSProps::kVerticalA
     {eCSSKeyword_text_top, NS_STYLE_VERTICAL_ALIGN_TEXT_TOP},
     {eCSSKeyword_middle, NS_STYLE_VERTICAL_ALIGN_MIDDLE},
     {eCSSKeyword__moz_middle_with_baseline,
      NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE},
     {eCSSKeyword_bottom, NS_STYLE_VERTICAL_ALIGN_BOTTOM},
     {eCSSKeyword_text_bottom, NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM},
     {eCSSKeyword_UNKNOWN, -1}};
 
-const KTableEntry nsCSSProps::kWidthKTable[] = {
-    {eCSSKeyword_max_content, NS_STYLE_WIDTH_MAX_CONTENT},
-    {eCSSKeyword_min_content, NS_STYLE_WIDTH_MIN_CONTENT},
-    {eCSSKeyword__moz_fit_content, NS_STYLE_WIDTH_FIT_CONTENT},
-    {eCSSKeyword__moz_available, NS_STYLE_WIDTH_AVAILABLE},
-    {eCSSKeyword_UNKNOWN, -1}};
-
-// This must be the same as kWidthKTable, but just with 'content' added:
-const KTableEntry nsCSSProps::kFlexBasisKTable[] = {
-    {eCSSKeyword_max_content, NS_STYLE_WIDTH_MAX_CONTENT},
-    {eCSSKeyword_min_content, NS_STYLE_WIDTH_MIN_CONTENT},
-    {eCSSKeyword__moz_fit_content, NS_STYLE_WIDTH_FIT_CONTENT},
-    {eCSSKeyword__moz_available, NS_STYLE_WIDTH_AVAILABLE},
-    {eCSSKeyword_content, NS_STYLE_FLEX_BASIS_CONTENT},
-    {eCSSKeyword_UNKNOWN, -1}};
-static_assert(ArrayLength(nsCSSProps::kFlexBasisKTable) ==
-                  ArrayLength(nsCSSProps::kWidthKTable) + 1,
-              "kFlexBasisKTable should have the same entries as "
-              "kWidthKTable, plus one more for 'content'");
-
-// Specific keyword tables for XUL.properties
-
 // keyword tables for SVG properties
 
 const KTableEntry nsCSSProps::kShapeRadiusKTable[] = {
     {eCSSKeyword_closest_side, StyleShapeRadius::ClosestSide},
     {eCSSKeyword_farthest_side, StyleShapeRadius::FarthestSide},
     {eCSSKeyword_UNKNOWN, -1}};
 
 const KTableEntry nsCSSProps::kFilterFunctionKTable[] = {
--- a/layout/style/nsCSSProps.h
+++ b/layout/style/nsCSSProps.h
@@ -308,13 +308,11 @@ class nsCSSProps {
   static const KTableEntry kContainKTable[];
   static const KTableEntry kTextAlignKTable[];
   static const KTableEntry kTextDecorationLineKTable[];
   static const KTableEntry kTextDecorationStyleKTable[];
   static const KTableEntry kTextEmphasisStyleShapeKTable[];
   static const KTableEntry kTextOverflowKTable[];
   static const KTableEntry kTouchActionKTable[];
   static const KTableEntry kVerticalAlignKTable[];
-  static const KTableEntry kWidthKTable[];  // also min-width, max-width
-  static const KTableEntry kFlexBasisKTable[];
 };
 
 #endif /* nsCSSProps_h___ */
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -2333,18 +2333,22 @@ already_AddRefed<CSSValue> nsComputedDOM
   //   if (i'm a flex item) {
   //     if (my flex container is horizontal) {
   //       percentageBaseGetter = &nsComputedDOMStyle::GetCBContentWidth;
   //     } else {
   //       percentageBaseGetter = &nsComputedDOMStyle::GetCBContentHeight;
   //     }
   //   }
 
-  SetValueToCoord(val, StylePosition()->mFlexBasis, true, nullptr,
-                  nsCSSProps::kFlexBasisKTable);
+  const auto& basis = StylePosition()->mFlexBasis;
+  if (basis.IsContent()) {
+    val->SetIdent(eCSSKeyword_content);
+    return val.forget();
+  }
+  SetValueToSize(val, basis.AsSize());
   return val.forget();
 }
 
 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetFlexGrow() {
   RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
   val->SetNumber(StylePosition()->mFlexGrow);
   return val.forget();
 }
@@ -2459,18 +2463,17 @@ already_AddRefed<CSSValue> nsComputedDOM
     nscoord minHeight =
         StyleCoordToNSCoord(positionData->mMinHeight,
                             &nsComputedDOMStyle::GetCBContentHeight, 0, true);
 
     nscoord maxHeight = StyleCoordToNSCoord(
         positionData->mMaxHeight, &nsComputedDOMStyle::GetCBContentHeight,
         nscoord_MAX, true);
 
-    SetValueToCoord(val, positionData->mHeight, true, nullptr,
-                    nsCSSProps::kWidthKTable, minHeight, maxHeight);
+    SetValueToSize(val, positionData->mHeight, minHeight, maxHeight);
   }
 
   return val.forget();
 }
 
 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetWidth() {
   RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
 
@@ -2499,34 +2502,31 @@ already_AddRefed<CSSValue> nsComputedDOM
     nscoord minWidth =
         StyleCoordToNSCoord(positionData->mMinWidth,
                             &nsComputedDOMStyle::GetCBContentWidth, 0, true);
 
     nscoord maxWidth = StyleCoordToNSCoord(
         positionData->mMaxWidth, &nsComputedDOMStyle::GetCBContentWidth,
         nscoord_MAX, true);
 
-    SetValueToCoord(val, positionData->mWidth, true, nullptr,
-                    nsCSSProps::kWidthKTable, minWidth, maxWidth);
+    SetValueToSize(val, positionData->mWidth, minWidth, maxWidth);
   }
 
   return val.forget();
 }
 
 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMaxHeight() {
   RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
-  SetValueToCoord(val, StylePosition()->mMaxHeight, true, nullptr,
-                  nsCSSProps::kWidthKTable);
+  SetValueToMaxSize(val, StylePosition()->mMaxHeight);
   return val.forget();
 }
 
 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMaxWidth() {
   RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
-  SetValueToCoord(val, StylePosition()->mMaxWidth, true, nullptr,
-                  nsCSSProps::kWidthKTable);
+  SetValueToMaxSize(val, StylePosition()->mMaxWidth);
   return val.forget();
 }
 
 /**
  * This function indicates whether we should return "auto" as the
  * getComputedStyle() result for the (default) "min-width: auto" and
  * "min-height: auto" CSS values.
  *
@@ -2539,38 +2539,36 @@ already_AddRefed<CSSValue> nsComputedDOM
  * "auto" to 0.
  */
 bool nsComputedDOMStyle::ShouldHonorMinSizeAutoInAxis(PhysicalAxis aAxis) {
   return mOuterFrame && mOuterFrame->IsFlexOrGridItem();
 }
 
 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMinHeight() {
   RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
-  nsStyleCoord minHeight = StylePosition()->mMinHeight;
-
-  if (eStyleUnit_Auto == minHeight.GetUnit() &&
-      !ShouldHonorMinSizeAutoInAxis(eAxisVertical)) {
-    minHeight.SetCoordValue(0);
+  StyleSize minHeight = StylePosition()->mMinHeight;
+
+  if (minHeight.IsAuto() && !ShouldHonorMinSizeAutoInAxis(eAxisVertical)) {
+    minHeight = StyleSize::LengthPercentage(LengthPercentage::Zero());
   }
 
-  SetValueToCoord(val, minHeight, true, nullptr, nsCSSProps::kWidthKTable);
+  SetValueToSize(val, minHeight);
   return val.forget();
 }
 
 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMinWidth() {
   RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
 
-  nsStyleCoord minWidth = StylePosition()->mMinWidth;
-
-  if (eStyleUnit_Auto == minWidth.GetUnit() &&
-      !ShouldHonorMinSizeAutoInAxis(eAxisHorizontal)) {
-    minWidth.SetCoordValue(0);
+  StyleSize minWidth = StylePosition()->mMinWidth;
+
+  if (minWidth.IsAuto() && !ShouldHonorMinSizeAutoInAxis(eAxisHorizontal)) {
+    minWidth = StyleSize::LengthPercentage(LengthPercentage::Zero());
   }
 
-  SetValueToCoord(val, minWidth, true, nullptr, nsCSSProps::kWidthKTable);
+  SetValueToSize(val, minWidth);
   return val.forget();
 }
 
 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetLeft() {
   return GetOffsetWidthFor(eSideLeft);
 }
 
 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetRight() {
@@ -2609,42 +2607,16 @@ already_AddRefed<CSSValue> nsComputedDOM
       return GetNonStaticPositionOffset(
           aSide, true, &nsComputedDOMStyle::GetCBContentWidth,
           &nsComputedDOMStyle::GetCBContentHeight);
     default:
       MOZ_ASSERT_UNREACHABLE("Invalid position");
       return nullptr;
   }
 }
-// FIXME(emilio): Remove these when nsStyleCoord is gone.
-static nsStyleCoord ToCoord(const LengthPercentage& aLength) {
-  nsStyleCoord ret;
-  if (aLength.was_calc) {
-    auto* calc = new nsStyleCoord::Calc();
-    calc->mLength = CSSPixel::ToAppUnits(aLength.LengthInCSSPixels());
-    calc->mPercent = aLength.Percentage();
-    calc->mHasPercent = aLength.HasPercent();
-    ret.SetCalcValue(calc);  // takes ownership.
-    return ret;
-  }
-
-  if (aLength.HasPercent()) {
-    ret.SetPercentValue(aLength.Percentage());
-  } else {
-    ret.SetCoordValue(aLength.ToLength());
-  }
-  return ret;
-}
-
-static nsStyleCoord ToCoord(const LengthPercentageOrAuto& aLength) {
-  if (aLength.IsAuto()) {
-    return nsStyleCoord(eStyleUnit_Auto);
-  }
-  return ToCoord(aLength.AsLengthPercentage());
-}
 
 static_assert(eSideTop == 0 && eSideRight == 1 && eSideBottom == 2 &&
                   eSideLeft == 3,
               "box side constants not as expected for NS_OPPOSITE_SIDE");
 #define NS_OPPOSITE_SIDE(s_) mozilla::Side(((s_) + 2) & 3)
 
 already_AddRefed<CSSValue> nsComputedDOMStyle::GetNonStaticPositionOffset(
     mozilla::Side aSide, bool aResolveAuto, PercentageBaseGetter aWidthGetter,
@@ -2663,18 +2635,17 @@ already_AddRefed<CSSValue> nsComputedDOM
     coord = positionData->mOffset.Get(NS_OPPOSITE_SIDE(aSide));
     sign = -1;
   }
 
   PercentageBaseGetter baseGetter = (aSide == eSideLeft || aSide == eSideRight)
                                         ? aWidthGetter
                                         : aHeightGetter;
 
-  val->SetAppUnits(sign *
-                   StyleCoordToNSCoord(ToCoord(coord), baseGetter, 0, false));
+  val->SetAppUnits(sign * StyleCoordToNSCoord(coord, baseGetter, 0, false));
   return val.forget();
 }
 
 already_AddRefed<CSSValue> nsComputedDOMStyle::GetAbsoluteOffset(
     mozilla::Side aSide) {
   const auto& offset = StylePosition()->mOffset;
   const auto& coord = offset.Get(aSide);
   const auto& oppositeCoord = offset.Get(NS_OPPOSITE_SIDE(aSide));
@@ -2744,26 +2715,27 @@ nscoord nsComputedDOMStyle::GetUsedAbsol
   }
 
   return offset;
 }
 
 already_AddRefed<CSSValue> nsComputedDOMStyle::GetStaticOffset(
     mozilla::Side aSide) {
   RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
-  SetValueToCoord(val, ToCoord(StylePosition()->mOffset.Get(aSide)), false);
+  SetValueToLengthPercentageOrAuto(val, StylePosition()->mOffset.Get(aSide),
+                                   false);
   return val.forget();
 }
 
 already_AddRefed<CSSValue> nsComputedDOMStyle::GetPaddingWidthFor(
     mozilla::Side aSide) {
   RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
 
   if (!mInnerFrame) {
-    SetValueToCoord(val, ToCoord(StylePadding()->mPadding.Get(aSide)), true);
+    SetValueToLengthPercentage(val, StylePadding()->mPadding.Get(aSide), true);
   } else {
     AssertFlushedPendingReflows();
 
     val->SetAppUnits(mInnerFrame->GetUsedPadding().Side(aSide));
   }
 
   return val.forget();
 }
@@ -2828,31 +2800,120 @@ already_AddRefed<CSSValue> nsComputedDOM
   return val.forget();
 }
 
 already_AddRefed<CSSValue> nsComputedDOMStyle::GetMarginWidthFor(
     mozilla::Side aSide) {
   RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
 
   if (!mInnerFrame) {
-    SetValueToCoord(val, ToCoord(StyleMargin()->mMargin.Get(aSide)), false);
+    SetValueToLengthPercentageOrAuto(val, StyleMargin()->mMargin.Get(aSide),
+                                     false);
   } else {
     AssertFlushedPendingReflows();
 
     // For tables, GetUsedMargin always returns an empty margin, so we
     // should read the margin from the table wrapper frame instead.
     val->SetAppUnits(mOuterFrame->GetUsedMargin().Side(aSide));
     NS_ASSERTION(mOuterFrame == mInnerFrame ||
                      mInnerFrame->GetUsedMargin() == nsMargin(0, 0, 0, 0),
                  "Inner tables must have zero margins");
   }
 
   return val.forget();
 }
 
+void nsComputedDOMStyle::SetValueToExtremumLength(nsROCSSPrimitiveValue* aValue,
+                                                  StyleExtremumLength aSize) {
+  switch (aSize) {
+    case StyleExtremumLength::MaxContent:
+      return aValue->SetIdent(eCSSKeyword_max_content);
+    case StyleExtremumLength::MinContent:
+      return aValue->SetIdent(eCSSKeyword_min_content);
+    case StyleExtremumLength::MozAvailable:
+      return aValue->SetIdent(eCSSKeyword__moz_available);
+    case StyleExtremumLength::MozFitContent:
+      return aValue->SetIdent(eCSSKeyword__moz_fit_content);
+  }
+  MOZ_ASSERT_UNREACHABLE("Unknown extremum length?");
+}
+
+void nsComputedDOMStyle::SetValueToSize(nsROCSSPrimitiveValue* aValue,
+                                        const StyleSize& aSize, nscoord aMin,
+                                        nscoord aMax) {
+  if (aSize.IsAuto()) {
+    return aValue->SetIdent(eCSSKeyword_auto);
+  }
+  if (aSize.IsExtremumLength()) {
+    return SetValueToExtremumLength(aValue, aSize.AsExtremumLength());
+  }
+  MOZ_ASSERT(aSize.IsLengthPercentage());
+  SetValueToLengthPercentage(aValue, aSize.AsLengthPercentage(), true, aMin,
+                             aMax);
+}
+
+void nsComputedDOMStyle::SetValueToMaxSize(nsROCSSPrimitiveValue* aValue,
+                                           const StyleMaxSize& aSize) {
+  if (aSize.IsNone()) {
+    return aValue->SetIdent(eCSSKeyword_none);
+  }
+  if (aSize.IsExtremumLength()) {
+    return SetValueToExtremumLength(aValue, aSize.AsExtremumLength());
+  }
+  MOZ_ASSERT(aSize.IsLengthPercentage());
+  SetValueToLengthPercentage(aValue, aSize.AsLengthPercentage(), true);
+}
+
+void nsComputedDOMStyle::SetValueToLengthPercentageOrAuto(
+    nsROCSSPrimitiveValue* aValue, const LengthPercentageOrAuto& aSize,
+    bool aClampNegativeCalc) {
+  if (aSize.IsAuto()) {
+    return aValue->SetIdent(eCSSKeyword_auto);
+  }
+  SetValueToLengthPercentage(aValue, aSize.AsLengthPercentage(),
+                             aClampNegativeCalc);
+}
+
+void nsComputedDOMStyle::SetValueToLengthPercentage(
+    nsROCSSPrimitiveValue* aValue, const mozilla::LengthPercentage& aLength,
+    bool aClampNegativeCalc, nscoord aMin, nscoord aMax) {
+  if (aLength.ConvertsToLength()) {
+    nscoord result = aLength.ToLength();
+    if (aClampNegativeCalc) {
+      result = std::max(result, 0);
+    }
+    // TODO(emilio, bug 1527392): It's wrong to clamp here.
+    return aValue->SetAppUnits(std::max(aMin, std::min(result, aMax)));
+  }
+  if (aLength.ConvertsToPercentage()) {
+    float result = aLength.ToPercentage();
+    if (aClampNegativeCalc) {
+      result = std::max(result, 0.0f);
+    }
+    return aValue->SetPercent(result);
+  }
+
+  // TODO(emilio): This intentionally matches the serialization of
+  // SetValueToCalc, but goes against the spec. Update this when possible.
+  RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
+  nsAutoString tmp, result;
+  result.AppendLiteral("calc(");
+  val->SetAppUnits(CSSPixel::ToAppUnits(aLength.LengthInCSSPixels()));
+  val->GetCssText(tmp);
+  result.Append(tmp);
+  if (aLength.HasPercent()) {
+    result.AppendLiteral(" + ");
+    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[], nscoord aMinAppUnits, nscoord aMaxAppUnits) {
   MOZ_ASSERT(aValue, "Must have a value to work with");
 
   switch (aCoord.GetUnit()) {
     case eStyleUnit_Normal:
@@ -2938,43 +2999,39 @@ void nsComputedDOMStyle::SetValueToCoord
 
     default:
       NS_ERROR("Can't handle this unit");
       break;
   }
 }
 
 nscoord nsComputedDOMStyle::StyleCoordToNSCoord(
-    const nsStyleCoord& aCoord, PercentageBaseGetter aPercentageBaseGetter,
+    const LengthPercentage& aCoord, PercentageBaseGetter aPercentageBaseGetter,
     nscoord aDefaultValue, bool aClampNegativeCalc) {
   MOZ_ASSERT(aPercentageBaseGetter, "Must have a percentage base getter");
-  if (aCoord.GetUnit() == eStyleUnit_Coord) {
-    return aCoord.GetCoordValue();
+  if (aCoord.ConvertsToLength()) {
+    return aCoord.ToLength();
   }
-  if (aCoord.GetUnit() == eStyleUnit_Percent || aCoord.IsCalcUnit()) {
-    nscoord percentageBase;
-    if ((this->*aPercentageBaseGetter)(percentageBase)) {
-      nscoord result = aCoord.ComputeCoordPercentCalc(percentageBase);
-      if (aClampNegativeCalc && result < 0) {
-        // It's expected that we can get a negative value here with calc().
-        // We can also get a negative value with a percentage value if
-        // percentageBase is negative; this isn't expected, but can happen
-        // when large length values overflow.
-        NS_WARNING_ASSERTION(percentageBase >= 0,
-                             "percentage base value overflowed to become "
-                             "negative for a property "
-                             "that disallows negative values");
-        MOZ_ASSERT(
-            aCoord.IsCalcUnit() || (aCoord.HasPercent() && percentageBase < 0),
-            "parser should have rejected value");
-        result = 0;
-      }
-      return result;
+  nscoord percentageBase;
+  if ((this->*aPercentageBaseGetter)(percentageBase)) {
+    nscoord result = aCoord.Resolve(percentageBase);
+    if (aClampNegativeCalc && result < 0) {
+      // It's expected that we can get a negative value here with calc().
+      // We can also get a negative value with a percentage value if
+      // percentageBase is negative; this isn't expected, but can happen
+      // when large length values overflow.
+      NS_WARNING_ASSERTION(percentageBase >= 0,
+                           "percentage base value overflowed to become "
+                           "negative for a property "
+                           "that disallows negative values");
+      MOZ_ASSERT(aCoord.was_calc || (aCoord.HasPercent() && percentageBase < 0),
+                 "parser should have rejected value");
+      result = 0;
     }
-    // Fall through to returning aDefaultValue if we have no percentage base.
+    return result;
   }
 
   return aDefaultValue;
 }
 
 bool nsComputedDOMStyle::GetCBContentWidth(nscoord& aWidth) {
   if (!mOuterFrame) {
     return false;
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -46,19 +46,28 @@ struct nsStyleFilter;
 class nsStyleGradient;
 struct nsStyleImage;
 class nsStyleSides;
 
 class nsComputedDOMStyle final : public nsDOMCSSDeclaration,
                                  public nsStubMutationObserver {
  private:
   // Convenience typedefs:
-  typedef nsCSSKTableEntry KTableEntry;
-  typedef mozilla::dom::CSSValue CSSValue;
-  typedef mozilla::StyleGeometryBox StyleGeometryBox;
+  using KTableEntry = nsCSSKTableEntry;
+  using CSSValue = mozilla::dom::CSSValue;
+  using StyleGeometryBox = mozilla::StyleGeometryBox;
+  using Element = mozilla::dom::Element;
+  using Document = mozilla::dom::Document;
+  using StyleFlexBasis = mozilla::StyleFlexBasis;
+  using StyleSize = mozilla::StyleSize;
+  using StyleMaxSize = mozilla::StyleMaxSize;
+  using LengthPercentage = mozilla::LengthPercentage;
+  using LengthPercentageOrAuto = mozilla::LengthPercentageOrAuto;
+  using StyleExtremumLength = mozilla::StyleExtremumLength;
+  using ComputedStyle = mozilla::ComputedStyle;
 
  public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_AMBIGUOUS(
       nsComputedDOMStyle, nsICSSDeclaration)
 
   NS_DECL_NSIDOMCSSSTYLEDECLARATION_HELPER
   nsresult GetPropertyValue(const nsCSSPropertyID aPropID,
@@ -69,37 +78,33 @@ class nsComputedDOMStyle final : public 
 
   void IndexedGetter(uint32_t aIndex, bool& aFound, nsAString& aPropName) final;
 
   enum StyleType {
     eDefaultOnly,  // Only includes UA and user sheets
     eAll           // Includes all stylesheets
   };
 
-  nsComputedDOMStyle(mozilla::dom::Element* aElement,
-                     const nsAString& aPseudoElt,
-                     mozilla::dom::Document* aDocument, StyleType aStyleType);
+  nsComputedDOMStyle(Element* aElement, const nsAString& aPseudoElt,
+                     Document* aDocument, StyleType aStyleType);
 
   nsINode* GetParentObject() override { return mElement; }
 
-  static already_AddRefed<mozilla::ComputedStyle> GetComputedStyle(
-      mozilla::dom::Element* aElement, nsAtom* aPseudo,
-      StyleType aStyleType = eAll);
+  static already_AddRefed<ComputedStyle> GetComputedStyle(
+      Element* aElement, nsAtom* aPseudo, StyleType aStyleType = eAll);
 
-  static already_AddRefed<mozilla::ComputedStyle> GetComputedStyleNoFlush(
-      mozilla::dom::Element* aElement, nsAtom* aPseudo,
-      StyleType aStyleType = eAll) {
+  static already_AddRefed<ComputedStyle> GetComputedStyleNoFlush(
+      Element* aElement, nsAtom* aPseudo, StyleType aStyleType = eAll) {
     return DoGetComputedStyleNoFlush(
         aElement, aPseudo, nsContentUtils::GetPresShellForContent(aElement),
         aStyleType);
   }
 
-  static already_AddRefed<mozilla::ComputedStyle>
-  GetUnanimatedComputedStyleNoFlush(mozilla::dom::Element* aElement,
-                                    nsAtom* aPseudo);
+  static already_AddRefed<ComputedStyle> GetUnanimatedComputedStyleNoFlush(
+      Element* aElement, nsAtom* aPseudo);
 
   // Helper for nsDOMWindowUtils::GetVisitedDependentComputedStyle
   void SetExposeVisitedStyle(bool aExpose) {
     NS_ASSERTION(aExpose != mExposeVisitedStyle, "should always be changing");
     mExposeVisitedStyle = aExpose;
   }
 
   void GetCSSImageURLs(const nsAString& aPropertyName,
@@ -139,24 +144,23 @@ class nsComputedDOMStyle final : public 
   nsMargin GetAdjustedValuesForBoxSizing();
 
   // This indicates error by leaving mComputedStyle null.
   void UpdateCurrentStyleSources(bool aNeedsLayoutFlush);
   void ClearCurrentStyleSources();
 
   // Helper functions called by UpdateCurrentStyleSources.
   void ClearComputedStyle();
-  void SetResolvedComputedStyle(RefPtr<mozilla::ComputedStyle>&& aContext,
+  void SetResolvedComputedStyle(RefPtr<ComputedStyle>&& aContext,
                                 uint64_t aGeneration);
-  void SetFrameComputedStyle(mozilla::ComputedStyle* aStyle,
-                             uint64_t aGeneration);
+  void SetFrameComputedStyle(ComputedStyle* aStyle, uint64_t aGeneration);
 
-  static already_AddRefed<mozilla::ComputedStyle> DoGetComputedStyleNoFlush(
-      mozilla::dom::Element* aElement, nsAtom* aPseudo,
-      nsIPresShell* aPresShell, StyleType aStyleType);
+  static already_AddRefed<ComputedStyle> DoGetComputedStyleNoFlush(
+      Element* aElement, nsAtom* aPseudo, nsIPresShell* aPresShell,
+      StyleType aStyleType);
 
 #define STYLE_STRUCT(name_)                \
   const nsStyle##name_* Style##name_() {   \
     return mComputedStyle->Style##name_(); \
   }
 #include "nsStyleStructList.h"
 #undef STYLE_STRUCT
 
@@ -402,16 +406,35 @@ class nsComputedDOMStyle final : public 
                                 const mozilla::StyleComplexColor& aColor);
   void SetValueToPositionCoord(const mozilla::Position::Coord& aCoord,
                                nsROCSSPrimitiveValue* aValue);
   void SetValueToPosition(const mozilla::Position& aPosition,
                           nsDOMCSSValueList* aValueList);
   void SetValueToURLValue(const mozilla::css::URLValue* aURL,
                           nsROCSSPrimitiveValue* aValue);
 
+  void SetValueToSize(nsROCSSPrimitiveValue* aValue, const mozilla::StyleSize&,
+                      nscoord aMinAppUnits = nscoord_MIN,
+                      nscoord aMaxAppUnits = nscoord_MAX);
+
+  void SetValueToLengthPercentageOrAuto(nsROCSSPrimitiveValue* aValue,
+                                        const LengthPercentageOrAuto&,
+                                        bool aClampNegativeCalc);
+
+  void SetValueToLengthPercentage(nsROCSSPrimitiveValue* aValue,
+                                  const LengthPercentage&,
+                                  bool aClampNegativeCalc,
+                                  nscoord aMinAppUnits = nscoord_MIN,
+                                  nscoord aMaxAppUnits = nscoord_MAX);
+
+  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
@@ -426,22 +449,33 @@ class nsComputedDOMStyle final : public 
                        PercentageBaseGetter aPercentageBaseGetter = nullptr,
                        const KTableEntry aTable[] = nullptr,
                        nscoord aMinAppUnits = nscoord_MIN,
                        nscoord aMaxAppUnits = nscoord_MAX);
 
   /**
    * 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 percentge base can't
+   * the resulting nscoord.  If it's some other unit or a percentage base can't
    * be determined, returns aDefaultValue.
    */
-  nscoord StyleCoordToNSCoord(const nsStyleCoord& aCoord,
+  nscoord StyleCoordToNSCoord(const LengthPercentage& aCoord,
                               PercentageBaseGetter aPercentageBaseGetter,
                               nscoord aDefaultValue, bool aClampNegativeCalc);
+  template <typename LengthPercentageLike>
+  nscoord StyleCoordToNSCoord(const LengthPercentageLike& aCoord,
+                              PercentageBaseGetter aPercentageBaseGetter,
+                              nscoord aDefaultValue, bool aClampNegativeCalc) {
+    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);
@@ -472,40 +506,40 @@ class nsComputedDOMStyle final : public 
   void BoxValuesToString(nsAString& aString,
                          const nsTArray<nsStyleCoord>& aBoxValues,
                          bool aClampNegativeCalc);
   void BasicShapeRadiiToString(nsAString& aCssText,
                                const nsStyleCorners& aCorners);
 
   // Find out if we can safely skip flushing for aDocument (i.e. pending
   // restyles does not affect mContent).
-  bool NeedsToFlush(mozilla::dom::Document*) const;
+  bool NeedsToFlush(Document*) 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
   // presshell, if any, from the document.
   nsWeakPtr mDocumentWeak;
-  RefPtr<mozilla::dom::Element> mElement;
+  RefPtr<Element> mElement;
 
   /**
    * Strong reference to the ComputedStyle we access data from.  This can be
    * either a ComputedStyle we resolved ourselves or a ComputedStyle we got
    * from our frame.
    *
    * If we got the ComputedStyle from the frame, we clear out mComputedStyle
    * in ClearCurrentStyleSources.  If we resolved one ourselves, then
    * ClearCurrentStyleSources leaves it in mComputedStyle for use the next
    * time this nsComputedDOMStyle object is queried.  UpdateCurrentStyleSources
    * in this case will check that the ComputedStyle is still valid to be used,
    * by checking whether flush styles results in any restyles having been
    * processed.
    */
-  RefPtr<mozilla::ComputedStyle> mComputedStyle;
+  RefPtr<ComputedStyle> mComputedStyle;
   RefPtr<nsAtom> mPseudo;
 
   /*
    * While computing style data, the primary frame for mContent --- named
    * "outer" because we should use it to compute positioning data.  Null
    * otherwise.
    */
   nsIFrame* mOuterFrame;
--- a/layout/style/nsStyleConsts.h
+++ b/layout/style/nsStyleConsts.h
@@ -473,37 +473,16 @@ enum class StyleGridTrackBreadth : uint8
 #define NS_MATHML_MATHVARIANT_TAILED 16
 #define NS_MATHML_MATHVARIANT_LOOPED 17
 #define NS_MATHML_MATHVARIANT_STRETCHED 18
 
 // See nsStyleFont::mMathDisplay
 #define NS_MATHML_DISPLAYSTYLE_INLINE 0
 #define NS_MATHML_DISPLAYSTYLE_BLOCK 1
 
-// See nsStylePosition::mWidth, mMinWidth, mMaxWidth
-#define NS_STYLE_WIDTH_MAX_CONTENT \
-  ((uint8_t)mozilla::StyleExtremumLength::MaxContent)
-#define NS_STYLE_WIDTH_MIN_CONTENT \
-  ((uint8_t)mozilla::StyleExtremumLength::MinContent)
-#define NS_STYLE_WIDTH_FIT_CONTENT \
-  ((uint8_t)mozilla::StyleExtremumLength::MozFitContent)
-#define NS_STYLE_WIDTH_AVAILABLE \
-  ((uint8_t)mozilla::StyleExtremumLength::MozAvailable)
-// The 'content' keyword is only valid for 'flex-basis' (not for 'width').  But
-// aside from that, the 'flex-basis' property accepts exactly the same values
-// as 'width'. So I'm listing this one 'flex-basis'-specific enumerated value
-// alongside the 'width' ones, to be sure we don't accidentally overload this
-// numeric value with two different meanings if new 'width' keywords are added.
-#define NS_STYLE_FLEX_BASIS_CONTENT 4
-static_assert(NS_STYLE_FLEX_BASIS_CONTENT != NS_STYLE_WIDTH_MAX_CONTENT &&
-                  NS_STYLE_FLEX_BASIS_CONTENT != NS_STYLE_WIDTH_MIN_CONTENT &&
-                  NS_STYLE_FLEX_BASIS_CONTENT != NS_STYLE_WIDTH_FIT_CONTENT &&
-                  NS_STYLE_FLEX_BASIS_CONTENT != NS_STYLE_WIDTH_AVAILABLE,
-              "Should use different enum value for flex basis content");
-
 // See nsStyleDisplay.mPosition
 #define NS_STYLE_POSITION_STATIC 0
 #define NS_STYLE_POSITION_RELATIVE 1
 #define NS_STYLE_POSITION_ABSOLUTE 2
 #define NS_STYLE_POSITION_FIXED 3
 #define NS_STYLE_POSITION_STICKY 4
 
 // See nsStyleEffects.mClip, mClipFlags
--- a/layout/style/nsStyleCoord.h
+++ b/layout/style/nsStyleCoord.h
@@ -42,16 +42,24 @@ enum LogicalCorner {
 using LengthPercentage = StyleLengthPercentage;
 using LengthPercentageOrAuto = StyleLengthPercentageOrAuto;
 using NonNegativeLengthPercentage = StyleNonNegativeLengthPercentage;
 
 constexpr LengthPercentage LengthPercentage::Zero() {
   return {{0.}, {0.}, StyleAllowedNumericType::All, false, false};
 }
 
+LengthPercentage LengthPercentage::FromAppUnits(nscoord aCoord) {
+  return {{CSSPixel::FromAppUnits(aCoord)},
+          {0.},
+          StyleAllowedNumericType::All,
+          false,
+          false};
+}
+
 bool LengthPercentage::HasPercent() const { return has_percentage; }
 
 bool LengthPercentage::ConvertsToLength() const { return !HasPercent(); }
 
 nscoord LengthPercentage::ToLength() const {
   MOZ_ASSERT(ConvertsToLength());
   return CSSPixel::ToAppUnits(length._0);
 }
@@ -60,16 +68,20 @@ bool LengthPercentage::ConvertsToPercent
   return has_percentage && length._0 == 0.0f;
 }
 
 float LengthPercentage::ToPercentage() const {
   MOZ_ASSERT(ConvertsToPercentage());
   return Percentage();
 }
 
+bool LengthPercentage::HasLengthAndPercentage() const {
+  return !ConvertsToLength() && !ConvertsToPercentage();
+}
+
 CSSCoord LengthPercentage::LengthInCSSPixels() const { return length._0; }
 
 float LengthPercentage::Percentage() const { return percentage._0; }
 
 CSSCoord LengthPercentage::ResolveToCSSPixels(CSSCoord aPercentageBasis) const {
   return LengthInCSSPixels() + Percentage() * aPercentageBasis;
 }
 
@@ -94,31 +106,86 @@ nscoord LengthPercentage::ResolveWith(T 
   static_assert(std::is_same<decltype(aPercentageGetter()), nscoord>::value,
                 "Should return app units");
   if (ConvertsToLength()) {
     return ToLength();
   }
   return Resolve(aPercentageGetter());
 }
 
+#define IMPL_LENGTHPERCENTAGE_FORWARDS(ty_)                                 \
+  template <>                                                               \
+  inline const LengthPercentage& ty_::AsLengthPercentage() const {          \
+    MOZ_ASSERT(IsLengthPercentage());                                       \
+    return length_percentage._0;                                            \
+  }                                                                         \
+  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 const LengthPercentage& LengthPercentageOrAuto::AsLengthPercentage()
-    const {
-  MOZ_ASSERT(IsLengthPercentage());
-  return length_percentage._0;
+inline const StyleSize& StyleFlexBasis::AsSize() const {
+  MOZ_ASSERT(IsSize());
+  return size._0;
 }
 
 template <>
-inline bool LengthPercentageOrAuto::ConvertsToLength() const {
-  return IsLengthPercentage() && AsLengthPercentage().ConvertsToLength();
+inline bool StyleFlexBasis::IsAuto() const {
+  return IsSize() && AsSize().IsAuto();
+}
+
+template <>
+inline StyleExtremumLength StyleSize::AsExtremumLength() const {
+  MOZ_ASSERT(IsExtremumLength());
+  return extremum_length._0;
 }
 
 template <>
-inline bool LengthPercentageOrAuto::HasPercent() const {
-  return IsLengthPercentage() && AsLengthPercentage().HasPercent();
+inline bool StyleSize::BehavesLikeInitialValueOnBlockAxis() const {
+  return IsAuto() || IsExtremumLength();
+}
+
+template <>
+inline bool StyleMaxSize::BehavesLikeInitialValueOnBlockAxis() const {
+  return IsNone() || IsExtremumLength();
+}
+
+template <>
+inline StyleExtremumLength StyleMaxSize::AsExtremumLength() const {
+  MOZ_ASSERT(IsExtremumLength());
+  return extremum_length._0;
 }
 
 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];
 }
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -1291,23 +1291,23 @@ bool nsStyleSVGPaint::operator==(const n
   }
 }
 
 // --------------------
 // nsStylePosition
 //
 nsStylePosition::nsStylePosition(const Document& aDocument)
     : mOffset(StyleRectWithAllSides(LengthPercentageOrAuto::Auto())),
-      mWidth(eStyleUnit_Auto),
-      mMinWidth(eStyleUnit_Auto),
-      mMaxWidth(eStyleUnit_None),
-      mHeight(eStyleUnit_Auto),
-      mMinHeight(eStyleUnit_Auto),
-      mMaxHeight(eStyleUnit_None),
-      mFlexBasis(eStyleUnit_Auto),
+      mWidth(StyleSize::Auto()),
+      mMinWidth(StyleSize::Auto()),
+      mMaxWidth(StyleMaxSize::None()),
+      mHeight(StyleSize::Auto()),
+      mMinHeight(StyleSize::Auto()),
+      mMaxHeight(StyleMaxSize::None()),
+      mFlexBasis(StyleFlexBasis::Size(StyleSize::Auto())),
       mGridAutoColumnsMin(eStyleUnit_Auto),
       mGridAutoColumnsMax(eStyleUnit_Auto),
       mGridAutoRowsMin(eStyleUnit_Auto),
       mGridAutoRowsMax(eStyleUnit_Auto),
       mGridAutoFlow(NS_STYLE_GRID_AUTO_FLOW_ROW),
       mBoxSizing(StyleBoxSizing::Content),
       mAlignContent(NS_STYLE_ALIGN_NORMAL),
       mAlignItems(NS_STYLE_ALIGN_NORMAL),
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -1294,16 +1294,25 @@ struct nsStyleGridTemplate {
 
   bool IsRepeatAutoIndex(uint32_t aIndex) const {
     MOZ_ASSERT(aIndex < uint32_t(2 * nsStyleGridLine::kMaxLine));
     return int32_t(aIndex) == mRepeatAutoIndex;
   }
 };
 
 struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStylePosition {
+  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;
+
   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(
       const nsStylePosition& aNewData,
@@ -1316,25 +1325,25 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
   uint8_t UsedAlignSelf(mozilla::ComputedStyle* aParent) const;
 
   /**
    * Return the used value for 'justify-self' given our parent ComputedStyle
    * aParent (or null for the root).
    */
   uint8_t UsedJustifySelf(mozilla::ComputedStyle* aParent) const;
 
-  mozilla::Position mObjectPosition;
-  mozilla::StyleRect<mozilla::LengthPercentageOrAuto> mOffset;
-  nsStyleCoord mWidth;               // coord, percent, enum, calc, auto
-  nsStyleCoord mMinWidth;            // coord, percent, enum, calc
-  nsStyleCoord mMaxWidth;            // coord, percent, enum, calc, none
-  nsStyleCoord mHeight;              // coord, percent, calc, auto
-  nsStyleCoord mMinHeight;           // coord, percent, calc
-  nsStyleCoord mMaxHeight;           // coord, percent, calc, none
-  nsStyleCoord mFlexBasis;           // coord, percent, enum, calc, auto
+  Position mObjectPosition;
+  StyleRect<LengthPercentageOrAuto> mOffset;
+  StyleSize mWidth;
+  StyleSize mMinWidth;
+  StyleMaxSize mMaxWidth;
+  StyleSize mHeight;
+  StyleSize mMinHeight;
+  StyleMaxSize mMaxHeight;
+  StyleFlexBasis mFlexBasis;
   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
   uint8_t mGridAutoFlow;             // NS_STYLE_GRID_AUTO_FLOW_*
   mozilla::StyleBoxSizing mBoxSizing;
 
   // All align/justify properties here take NS_STYLE_ALIGN_* values.
@@ -1377,47 +1386,52 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
   bool OffsetHasPercent(mozilla::Side aSide) const {
     return mOffset.Get(aSide).HasPercent();
   }
 
   // Logical-coordinate accessors for width and height properties,
   // given a WritingMode value. The definitions of these methods are
   // found in WritingModes.h (after the WritingMode class is fully
   // declared).
-  inline nsStyleCoord& ISize(mozilla::WritingMode aWM);
-  inline nsStyleCoord& MinISize(mozilla::WritingMode aWM);
-  inline nsStyleCoord& MaxISize(mozilla::WritingMode aWM);
-  inline nsStyleCoord& BSize(mozilla::WritingMode aWM);
-  inline nsStyleCoord& MinBSize(mozilla::WritingMode aWM);
-  inline nsStyleCoord& MaxBSize(mozilla::WritingMode aWM);
-  inline const nsStyleCoord& ISize(mozilla::WritingMode aWM) const;
-  inline const nsStyleCoord& MinISize(mozilla::WritingMode aWM) const;
-  inline const nsStyleCoord& MaxISize(mozilla::WritingMode aWM) const;
-  inline const nsStyleCoord& BSize(mozilla::WritingMode aWM) const;
-  inline const nsStyleCoord& MinBSize(mozilla::WritingMode aWM) const;
-  inline const nsStyleCoord& MaxBSize(mozilla::WritingMode aWM) const;
-  inline bool ISizeDependsOnContainer(mozilla::WritingMode aWM) const;
-  inline bool MinISizeDependsOnContainer(mozilla::WritingMode aWM) const;
-  inline bool MaxISizeDependsOnContainer(mozilla::WritingMode aWM) const;
-  inline bool BSizeDependsOnContainer(mozilla::WritingMode aWM) const;
-  inline bool MinBSizeDependsOnContainer(mozilla::WritingMode aWM) const;
-  inline bool MaxBSizeDependsOnContainer(mozilla::WritingMode aWM) const;
+  inline const StyleSize& ISize(WritingMode) const;
+  inline const StyleSize& MinISize(WritingMode) const;
+  inline const StyleMaxSize& MaxISize(WritingMode) const;
+  inline const StyleSize& BSize(WritingMode) const;
+  inline const StyleSize& MinBSize(WritingMode) const;
+  inline const StyleMaxSize& MaxBSize(WritingMode) const;
+  inline bool ISizeDependsOnContainer(WritingMode) const;
+  inline bool MinISizeDependsOnContainer(WritingMode) const;
+  inline bool MaxISizeDependsOnContainer(WritingMode) const;
+  inline bool BSizeDependsOnContainer(WritingMode) const;
+  inline bool MinBSizeDependsOnContainer(WritingMode) const;
+  inline bool MaxBSizeDependsOnContainer(WritingMode) const;
 
   const nsStyleGridTemplate& GridTemplateColumns() const;
   const nsStyleGridTemplate& GridTemplateRows() const;
 
  private:
-  static bool ISizeCoordDependsOnContainer(const nsStyleCoord& aCoord) {
-    return aCoord.HasPercent() ||
-           (aCoord.GetUnit() == eStyleUnit_Enumerated &&
-            (aCoord.GetIntValue() == NS_STYLE_WIDTH_FIT_CONTENT ||
-             aCoord.GetIntValue() == NS_STYLE_WIDTH_AVAILABLE));
+  template <typename SizeOrMaxSize>
+  static bool ISizeCoordDependsOnContainer(const SizeOrMaxSize& aCoord) {
+    if (aCoord.IsLengthPercentage()) {
+      return aCoord.AsLengthPercentage().HasPercent();
+    }
+
+    if (!aCoord.IsExtremumLength()) {
+      return false;
+    }
+
+    auto keyword = aCoord.AsExtremumLength();
+    return keyword == mozilla::StyleExtremumLength::MozFitContent ||
+           keyword == mozilla::StyleExtremumLength::MozAvailable;
   }
-  static bool BSizeCoordDependsOnContainer(const nsStyleCoord& aCoord) {
-    return aCoord.HasPercent();
+
+  template <typename SizeOrMaxSize>
+  static bool BSizeCoordDependsOnContainer(const SizeOrMaxSize& aCoord) {
+    return aCoord.IsLengthPercentage() &&
+           aCoord.AsLengthPercentage().HasPercent();
   }
 };
 
 struct nsStyleTextOverflowSide {
   nsStyleTextOverflowSide() : mType(NS_STYLE_TEXT_OVERFLOW_CLIP) {}
 
   bool operator==(const nsStyleTextOverflowSide& aOther) const {
     return mType == aOther.mType && (mType != NS_STYLE_TEXT_OVERFLOW_STRING ||
--- a/layout/svg/nsSVGOuterSVGFrame.cpp
+++ b/layout/svg/nsSVGOuterSVGFrame.cpp
@@ -74,18 +74,18 @@ nsSVGOuterSVGFrame::nsSVGOuterSVGFrame(C
       mIsRootContent(false) {
   // Outer-<svg> has CSS layout, so remove this bit:
   RemoveStateBits(NS_FRAME_SVG_LAYOUT);
 }
 
 // helper
 static inline bool DependsOnIntrinsicSize(const nsIFrame* aEmbeddingFrame) {
   const nsStylePosition* pos = aEmbeddingFrame->StylePosition();
-  const nsStyleCoord& width = pos->mWidth;
-  const nsStyleCoord& height = pos->mHeight;
+  const auto& width = pos->mWidth;
+  const auto& height = pos->mHeight;
 
   // XXX it would be nice to know if the size of aEmbeddingFrame's containing
   // block depends on aEmbeddingFrame, then we'd know if we can return false
   // for eStyleUnit_Percent too.
   return !width.ConvertsToLength() || !height.ConvertsToLength();
 }
 
 void nsSVGOuterSVGFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
--- a/layout/tables/BasicTableLayoutStrategy.cpp
+++ b/layout/tables/BasicTableLayoutStrategy.cpp
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-// vim:cindent:ts=4:et:sw=4:
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// vim:cindent:ts=2:et:sw=2:
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*
  * Web-compatible algorithms that determine column and table widths,
  * used for CSS2's 'table-layout: auto'.
  */
@@ -116,18 +116,17 @@ static CellISizeInfo GetISizeInfo(gfxCon
     }
   } else {
     minCoord = 0;
     prefCoord = 0;
   }
   float prefPercent = 0.0f;
   bool hasSpecifiedISize = false;
 
-  const nsStyleCoord &iSize = stylePos->ISize(aWM);
-  nsStyleUnit unit = iSize.GetUnit();
+  const auto &iSize = stylePos->ISize(aWM);
   // NOTE: We're ignoring calc() units with both lengths and percentages here,
   // for lack of a sensible idea for what to do with them.  This means calc()
   // with percentages is basically handled like 'auto' for table cells and
   // columns.
   if (iSize.ConvertsToLength()) {
     hasSpecifiedISize = true;
     // Note: since ComputeISizeValue was designed to return content-box
     // isize, it will (in some cases) subtract the box-sizing edges.
@@ -140,75 +139,76 @@ static CellISizeInfo GetISizeInfo(gfxCon
     // This is kept up-to-date with dynamic changes to nowrap by code in
     // nsTableCellFrame::AttributeChanged
     if (aIsCell && c > minCoord && isQuirks &&
         aFrame->GetContent()->AsElement()->HasAttr(kNameSpaceID_None,
                                                    nsGkAtoms::nowrap)) {
       minCoord = c;
     }
     prefCoord = std::max(c, minCoord);
-  } else if (iSize.ConvertsToPercent()) {
-    prefPercent = iSize.ToPercent();
-  } else if (unit == eStyleUnit_Enumerated && aIsCell) {
-    switch (iSize.GetIntValue()) {
-      case NS_STYLE_WIDTH_MAX_CONTENT:
+  } else if (iSize.ConvertsToPercentage()) {
+    prefPercent = iSize.ToPercentage();
+  } else if (iSize.IsExtremumLength() && aIsCell) {
+    switch (iSize.AsExtremumLength()) {
+      case StyleExtremumLength::MaxContent:
         // 'inline-size' only affects pref isize, not min
         // isize, so don't change anything
         break;
-      case NS_STYLE_WIDTH_MIN_CONTENT:
+      case StyleExtremumLength::MinContent:
         prefCoord = minCoord;
         break;
-      case NS_STYLE_WIDTH_FIT_CONTENT:
-      case NS_STYLE_WIDTH_AVAILABLE:
-        // act just like 'inline-size: auto'
+      case StyleExtremumLength::MozFitContent:
+      case StyleExtremumLength::MozAvailable:
         break;
       default:
         MOZ_ASSERT_UNREACHABLE("unexpected enumerated value");
     }
   }
 
-  nsStyleCoord maxISize(stylePos->MaxISize(aWM));
-  if (maxISize.GetUnit() == eStyleUnit_Enumerated) {
-    if (!aIsCell || maxISize.GetIntValue() == NS_STYLE_WIDTH_AVAILABLE) {
-      maxISize.SetNoneValue();
-    } else if (maxISize.GetIntValue() == NS_STYLE_WIDTH_FIT_CONTENT) {
+  StyleMaxSize maxISize = stylePos->MaxISize(aWM);
+  if (maxISize.IsExtremumLength()) {
+    if (!aIsCell ||
+        maxISize.AsExtremumLength() == StyleExtremumLength::MozAvailable) {
+      maxISize = StyleMaxSize::None();
+    } else if (maxISize.AsExtremumLength() ==
+               StyleExtremumLength::MozFitContent) {
       // for 'max-inline-size', '-moz-fit-content' is like 'max-content'
-      maxISize.SetIntValue(NS_STYLE_WIDTH_MAX_CONTENT, eStyleUnit_Enumerated);
+      maxISize = StyleMaxSize::ExtremumLength(StyleExtremumLength::MaxContent);
     }
   }
-  unit = maxISize.GetUnit();
   // XXX To really implement 'max-inline-size' well, we'd need to store
   // it separately on the columns.
-  if (maxISize.ConvertsToLength() || unit == eStyleUnit_Enumerated) {
+  if (maxISize.ConvertsToLength() || maxISize.IsExtremumLength()) {
     nscoord c = aFrame->ComputeISizeValue(aRenderingContext, 0, 0, 0, maxISize);
     minCoord = std::min(c, minCoord);
     prefCoord = std::min(c, prefCoord);
-  } else if (maxISize.ConvertsToPercent()) {
-    float p = maxISize.ToPercent();
+  } else if (maxISize.ConvertsToPercentage()) {
+    float p = maxISize.ToPercentage();
     if (p < prefPercent) {
       prefPercent = p;
     }
   }
 
-  nsStyleCoord minISize(stylePos->MinISize(aWM));
-  if (minISize.GetUnit() == eStyleUnit_Enumerated) {
-    if (!aIsCell || minISize.GetIntValue() == NS_STYLE_WIDTH_AVAILABLE) {
-      minISize.SetCoordValue(0);
-    } else if (minISize.GetIntValue() == NS_STYLE_WIDTH_FIT_CONTENT) {
+  StyleSize minISize = stylePos->MinISize(aWM);
+  if (minISize.IsExtremumLength()) {
+    if (!aIsCell ||
+        minISize.AsExtremumLength() == StyleExtremumLength::MozAvailable) {
+      minISize = StyleSize::LengthPercentage(LengthPercentage::Zero());
+    } else if (minISize.AsExtremumLength() ==
+               StyleExtremumLength::MozFitContent) {
       // for 'min-inline-size', '-moz-fit-content' is like 'min-content'
-      minISize.SetIntValue(NS_STYLE_WIDTH_MIN_CONTENT, eStyleUnit_Enumerated);
+      minISize = StyleSize::ExtremumLength(StyleExtremumLength::MinContent);
     }
   }
-  unit = minISize.GetUnit();
-  if (minISize.ConvertsToLength() || unit == eStyleUnit_Enumerated) {
+  if (minISize.ConvertsToLength() || minISize.IsExtremumLength()) {
     nscoord c = aFrame->ComputeISizeValue(aRenderingContext, 0, 0, 0, minISize);
     minCoord = std::max(c, minCoord);
     prefCoord = std::max(c, prefCoord);
-  } else if (minISize.ConvertsToPercent()) {
-    float p = minISize.ToPercent();
+  } else if (minISize.ConvertsToPercentage()) {
+    float p = minISize.ToPercentage();
     if (p > prefPercent) {
       prefPercent = p;
     }
   }
 
   // XXX Should col frame have border/padding considered?
   if (aIsCell) {
     minCoord += boxSizingToBorderEdge;
--- a/layout/tables/FixedTableLayoutStrategy.cpp
+++ b/layout/tables/FixedTableLayoutStrategy.cpp
@@ -62,52 +62,52 @@ FixedTableLayoutStrategy::~FixedTableLay
   WritingMode wm = mTableFrame->GetWritingMode();
   for (int32_t col = 0; col < colCount; ++col) {
     nsTableColFrame *colFrame = mTableFrame->GetColFrame(col);
     if (!colFrame) {
       NS_ERROR("column frames out of sync with cell map");
       continue;
     }
     nscoord spacing = mTableFrame->GetColSpacing(col);
-    const nsStyleCoord *styleISize = &colFrame->StylePosition()->ISize(wm);
+    const auto *styleISize = &colFrame->StylePosition()->ISize(wm);
     if (styleISize->ConvertsToLength()) {
       result +=
           colFrame->ComputeISizeValue(aRenderingContext, 0, 0, 0, *styleISize);
-    } else if (styleISize->ConvertsToPercent()) {
+    } else if (styleISize->ConvertsToPercentage()) {
       // do nothing
     } else {
-      NS_ASSERTION(
-          styleISize->GetUnit() == eStyleUnit_Auto ||
-              styleISize->GetUnit() == eStyleUnit_Enumerated ||
-              (styleISize->IsCalcUnit() && !styleISize->ConvertsToPercent()),
-          "bad inline size");
+      NS_ASSERTION(styleISize->IsAuto() || styleISize->IsExtremumLength() ||
+                       styleISize->HasLengthAndPercentage(),
+                   "bad inline size");
 
       // The 'table-layout: fixed' algorithm considers only cells in the
       // first row.
       bool originates;
       int32_t colSpan;
       nsTableCellFrame *cellFrame =
           cellMap->GetCellInfoAt(0, col, &originates, &colSpan);
       if (cellFrame) {
         styleISize = &cellFrame->StylePosition()->ISize(wm);
         if (styleISize->ConvertsToLength() ||
-            (styleISize->GetUnit() == eStyleUnit_Enumerated &&
-             (styleISize->GetIntValue() == NS_STYLE_WIDTH_MAX_CONTENT ||
-              styleISize->GetIntValue() == NS_STYLE_WIDTH_MIN_CONTENT))) {
+            (styleISize->IsExtremumLength() &&
+             (styleISize->AsExtremumLength() ==
+                  StyleExtremumLength::MaxContent ||
+              styleISize->AsExtremumLength() ==
+                  StyleExtremumLength::MinContent))) {
           nscoord cellISize = nsLayoutUtils::IntrinsicForContainer(
               aRenderingContext, cellFrame, nsLayoutUtils::MIN_ISIZE);
           if (colSpan > 1) {
             // If a column-spanning cell is in the first row, split up
             // the space evenly.  (XXX This isn't quite right if some of
             // the columns it's in have specified inline sizes.  Should
             // we care?)
             cellISize = ((cellISize + spacing) / colSpan) - spacing;
           }
           result += cellISize;
-        } else if (styleISize->ConvertsToPercent()) {
+        } else if (styleISize->ConvertsToPercentage()) {
           if (colSpan > 1) {
             // XXX Can this force columns to negative inline sizes?
             result -= spacing * (colSpan - 1);
           }
         }
         // else, for 'auto', '-moz-available', '-moz-fit-content',
         // and 'calc()' with both lengths and percentages, do nothing
       }
@@ -198,58 +198,59 @@ static inline nscoord AllocateUnassigned
     nsTableColFrame *colFrame = mTableFrame->GetColFrame(col);
     if (!colFrame) {
       oldColISizes.AppendElement(0);
       NS_ERROR("column frames out of sync with cell map");
       continue;
     }
     oldColISizes.AppendElement(colFrame->GetFinalISize());
     colFrame->ResetPrefPercent();
-    const nsStyleCoord *styleISize = &colFrame->StylePosition()->ISize(wm);
+    const auto *styleISize = &colFrame->StylePosition()->ISize(wm);
     nscoord colISize;
     if (styleISize->ConvertsToLength()) {
       colISize = colFrame->ComputeISizeValue(aReflowInput.mRenderingContext, 0,
                                              0, 0, *styleISize);
       specTotal += colISize;
-    } else if (styleISize->ConvertsToPercent()) {
-      float pct = styleISize->ToPercent();
+    } else if (styleISize->ConvertsToPercentage()) {
+      float pct = styleISize->ToPercentage();
       colISize = NSToCoordFloor(pct * float(tableISize));
       colFrame->AddPrefPercent(pct);
       pctTotal += pct;
     } else {
-      NS_ASSERTION(
-          styleISize->GetUnit() == eStyleUnit_Auto ||
-              styleISize->GetUnit() == eStyleUnit_Enumerated ||
-              (styleISize->IsCalcUnit() && !styleISize->ConvertsToPercent()),
-          "bad inline size");
+      NS_ASSERTION(styleISize->IsAuto() || styleISize->IsExtremumLength() ||
+                       (styleISize->IsLengthPercentage() &&
+                        !styleISize->ConvertsToLength()),
+                   "bad inline size");
 
       // The 'table-layout: fixed' algorithm considers only cells in the
       // first row.
       bool originates;
       int32_t colSpan;
       nsTableCellFrame *cellFrame =
           cellMap->GetCellInfoAt(0, col, &originates, &colSpan);
       if (cellFrame) {
         const nsStylePosition *cellStylePos = cellFrame->StylePosition();
         styleISize = &cellStylePos->ISize(wm);
         if (styleISize->ConvertsToLength() ||
-            (styleISize->GetUnit() == eStyleUnit_Enumerated &&
-             (styleISize->GetIntValue() == NS_STYLE_WIDTH_MAX_CONTENT ||
-              styleISize->GetIntValue() == NS_STYLE_WIDTH_MIN_CONTENT))) {
+            (styleISize->IsExtremumLength() &&
+             (styleISize->AsExtremumLength() ==
+                  StyleExtremumLength::MaxContent ||
+              styleISize->AsExtremumLength() ==
+                  StyleExtremumLength::MinContent))) {
           // XXX This should use real percentage padding
           // Note that the difference between MIN_ISIZE and PREF_ISIZE
           // shouldn't matter for any of these values of styleISize; use
           // MIN_ISIZE for symmetry with GetMinISize above, just in case
           // there is a difference.
           colISize = nsLayoutUtils::IntrinsicForContainer(
               aReflowInput.mRenderingContext, cellFrame,
               nsLayoutUtils::MIN_ISIZE);
-        } else if (styleISize->ConvertsToPercent()) {
+        } else if (styleISize->ConvertsToPercentage()) {
           // XXX This should use real percentage padding
-          float pct = styleISize->ToPercent();
+          float pct = styleISize->ToPercentage();
           colISize = NSToCoordFloor(pct * float(tableISize));
 
           if (cellStylePos->mBoxSizing == StyleBoxSizing::Content) {
             nsIFrame::IntrinsicISizeOffsetData offsets =
                 cellFrame->IntrinsicISizeOffsets();
             colISize += offsets.hPadding + offsets.hBorder;
           }
 
@@ -268,17 +269,17 @@ static inline nscoord AllocateUnassigned
             // the columns it's in have specified iSizes.  Should we
             // care?)
             nscoord spacing = mTableFrame->GetColSpacing(col);
             colISize = ((colISize + spacing) / colSpan) - spacing;
             if (colISize < 0) {
               colISize = 0;
             }
           }
-          if (!styleISize->ConvertsToPercent()) {
+          if (!styleISize->ConvertsToPercentage()) {
             specTotal += colISize;
           }
         }
       } else {
         colISize = unassignedMarker;
       }
     }
 
--- a/layout/tables/nsTableFrame.cpp
+++ b/layout/tables/nsTableFrame.cpp
@@ -1756,25 +1756,25 @@ LogicalSize nsTableFrame::ComputeAutoSiz
 bool nsTableFrame::AncestorsHaveStyleBSize(
     const ReflowInput& aParentReflowInput) {
   WritingMode wm = aParentReflowInput.GetWritingMode();
   for (const ReflowInput* rs = &aParentReflowInput; rs && rs->mFrame;
        rs = rs->mParentReflowInput) {
     LayoutFrameType frameType = rs->mFrame->Type();
     if (IsTableCell(frameType) || (LayoutFrameType::TableRow == frameType) ||
         (LayoutFrameType::TableRowGroup == frameType)) {
-      const nsStyleCoord& bsize = rs->mStylePosition->BSize(wm);
-      // calc() with percentages treated like 'auto' on internal table elements
-      if (bsize.GetUnit() != eStyleUnit_Auto &&
-          (!bsize.IsCalcUnit() || !bsize.HasPercent())) {
+      const auto& bsize = rs->mStylePosition->BSize(wm);
+      // calc() with both lengths and percentages treated like 'auto' on
+      // internal table elements
+      if (!bsize.IsAuto() && !bsize.HasLengthAndPercentage()) {
         return true;
       }
     } else if (LayoutFrameType::Table == frameType) {
       // we reached the containing table, so always return
-      return rs->mStylePosition->BSize(wm).GetUnit() != eStyleUnit_Auto;
+      return !rs->mStylePosition->BSize(wm).IsAuto();
     }
   }
   return false;
 }
 
 // See if a special block-size reflow needs to occur and if so,
 // call RequestSpecialBSizeReflow
 void nsTableFrame::CheckRequestSpecialBSizeReflow(
@@ -1784,18 +1784,18 @@ void nsTableFrame::CheckRequestSpecialBS
                    aReflowInput.mFrame->IsTableRowGroupFrame() ||
                    aReflowInput.mFrame->IsTableFrame(),
                "unexpected frame type");
   WritingMode wm = aReflowInput.GetWritingMode();
   if (!aReflowInput.mFrame->GetPrevInFlow() &&  // 1st in flow
       (NS_UNCONSTRAINEDSIZE ==
            aReflowInput.ComputedBSize() ||  // no computed bsize
        0 == aReflowInput.ComputedBSize()) &&
-      eStyleUnit_Percent ==
-          aReflowInput.mStylePosition->BSize(wm).GetUnit() &&  // pct bsize
+      aReflowInput.mStylePosition->BSize(wm)
+          .ConvertsToPercentage() &&  // pct bsize
       nsTableFrame::AncestorsHaveStyleBSize(*aReflowInput.mParentReflowInput)) {
     nsTableFrame::RequestSpecialBSizeReflow(aReflowInput);
   }
 }
 
 // Notify the frame and its ancestors (up to the containing table) that a
 // special bsize reflow will occur. During a special bsize reflow, a table, row
 // group, row, or cell returns the last size it was reflowed at. However, the
@@ -3889,21 +3889,24 @@ nsTableFrame* nsTableFrame::GetTableFram
     }
   }
 
   MOZ_ASSERT(tableFrame, "Should have a table frame here");
   return tableFrame;
 }
 
 bool nsTableFrame::IsAutoBSize(WritingMode aWM) {
-  const nsStyleCoord& bsize = StylePosition()->BSize(aWM);
+  const auto& bsize = StylePosition()->BSize(aWM);
+  if (bsize.IsAuto()) {
+    return true;
+  }
   // Don't consider calc() here like this quirk for percent.
-  return bsize.GetUnit() == eStyleUnit_Auto ||
-         (bsize.GetUnit() == eStyleUnit_Percent &&
-          bsize.GetPercentValue() <= 0.0f);
+  // FIXME(emilio): calc() causing this behavior change seems odd.
+  return bsize.HasPercent() && !bsize.AsLengthPercentage().was_calc &&
+         bsize.ToPercentage() <= 0.0f;
 }
 
 nscoord nsTableFrame::CalcBorderBoxBSize(const ReflowInput& aReflowInput) {
   nscoord bSize = aReflowInput.ComputedBSize();
   if (NS_AUTOHEIGHT != bSize) {
     WritingMode wm = aReflowInput.GetWritingMode();
     LogicalMargin borderPadding = GetChildAreaOffset(wm, &aReflowInput);
     bSize += borderPadding.BStartEnd(wm);
@@ -3914,20 +3917,20 @@ nscoord nsTableFrame::CalcBorderBoxBSize
 }
 
 bool nsTableFrame::IsAutoLayout() {
   if (StyleTable()->mLayoutStrategy == NS_STYLE_TABLE_LAYOUT_AUTO) return true;
   // a fixed-layout inline-table must have a inline size
   // and tables with inline size set to 'max-content' must be
   // auto-layout (at least as long as
   // FixedTableLayoutStrategy::GetPrefISize returns nscoord_MAX)
-  const nsStyleCoord& iSize = StylePosition()->ISize(GetWritingMode());
-  return (iSize.GetUnit() == eStyleUnit_Auto) ||
-         (iSize.GetUnit() == eStyleUnit_Enumerated &&
-          iSize.GetIntValue() == NS_STYLE_WIDTH_MAX_CONTENT);
+  const auto& iSize = StylePosition()->ISize(GetWritingMode());
+  return iSize.IsAuto() ||
+         (iSize.IsExtremumLength() &&
+          iSize.AsExtremumLength() == StyleExtremumLength::MaxContent);
 }
 
 #ifdef DEBUG_FRAME_DUMP
 nsresult nsTableFrame::GetFrameName(nsAString& aResult) const {
   return MakeFrameName(NS_LITERAL_STRING("Table"), aResult);
 }
 #endif
 
--- a/layout/tables/nsTableRowFrame.cpp
+++ b/layout/tables/nsTableRowFrame.cpp
@@ -496,21 +496,21 @@ nscoord nsTableRowFrame::CalcBSize(const
   nsTableFrame* tableFrame = GetTableFrame();
   nscoord computedBSize = (NS_UNCONSTRAINEDSIZE == aReflowInput.ComputedBSize())
                               ? 0
                               : aReflowInput.ComputedBSize();
   ResetBSize(computedBSize);
 
   WritingMode wm = aReflowInput.GetWritingMode();
   const nsStylePosition* position = StylePosition();
-  const nsStyleCoord& bsizeStyleCoord = position->BSize(wm);
+  const auto& bsizeStyleCoord = position->BSize(wm);
   if (bsizeStyleCoord.ConvertsToLength()) {
-    SetFixedBSize(bsizeStyleCoord.ComputeCoordPercentCalc(0));
-  } else if (bsizeStyleCoord.ConvertsToPercent()) {
-    SetPctBSize(bsizeStyleCoord.ToPercent());
+    SetFixedBSize(bsizeStyleCoord.ToLength());
+  } else if (bsizeStyleCoord.ConvertsToPercentage()) {
+    SetPctBSize(bsizeStyleCoord.ToPercentage());
   }
 
   for (nsIFrame* kidFrame : mFrames) {
     nsTableCellFrame* cellFrame = do_QueryFrame(kidFrame);
     if (cellFrame) {
       MOZ_ASSERT(cellFrame->GetWritingMode() == wm);
       LogicalSize desSize = cellFrame->GetDesiredSize();
       if ((NS_UNCONSTRAINEDSIZE == aReflowInput.AvailableBSize()) &&
@@ -563,36 +563,36 @@ nsresult nsTableRowFrame::CalculateCellA
                                                    WritingMode aWM) {
   nscoord specifiedBSize = 0;
 
   // Get the bsize specified in the style information
   const nsStylePosition* position = aCellFrame->StylePosition();
 
   int32_t rowSpan = GetTableFrame()->GetEffectiveRowSpan(*aCellFrame);
 
-  const nsStyleCoord& bsizeStyleCoord = position->BSize(aWM);
+  const auto& bsizeStyleCoord = position->BSize(aWM);
   if (bsizeStyleCoord.ConvertsToLength()) {
     // In quirks mode, table cell isize should be content-box, but bsize
     // should be border-box.
     // Because of this historic anomaly, we do not use quirk.css
     // (since we can't specify one value of box-sizing for isize and another
     // for bsize)
     specifiedBSize = bsizeStyleCoord.ToLength();
     if (PresContext()->CompatibilityMode() != eCompatibility_NavQuirks &&
         position->mBoxSizing == StyleBoxSizing::Content) {
       specifiedBSize +=
           aCellFrame->GetLogicalUsedBorderAndPadding(aWM).BStartEnd(aWM);
     }
 
     if (1 == rowSpan) {
       SetFixedBSize(specifiedBSize);
     }
-  } else if (bsizeStyleCoord.ConvertsToPercent()) {
+  } else if (bsizeStyleCoord.ConvertsToPercentage()) {
     if (1 == rowSpan) {
-      SetPctBSize(bsizeStyleCoord.ToPercent());
+      SetPctBSize(bsizeStyleCoord.ToPercentage());
     }
   }
 
   // If the specified bsize is greater than the desired bsize,
   // then use the specified bsize
   if (specifiedBSize > aDesiredBSize) {
     aDesiredBSize = specifiedBSize;
   }
@@ -1331,21 +1331,21 @@ void nsTableRowFrame::InitHasCellWithSty
 
   for (nsIFrame* kidFrame : mFrames) {
     nsTableCellFrame* cellFrame = do_QueryFrame(kidFrame);
     if (!cellFrame) {
       MOZ_ASSERT_UNREACHABLE("Table row has a non-cell child.");
       continue;
     }
     // Ignore row-spanning cells
-    const nsStyleCoord& cellBSize = cellFrame->StylePosition()->BSize(wm);
+    const auto& cellBSize = cellFrame->StylePosition()->BSize(wm);
     if (aTableFrame->GetEffectiveRowSpan(*cellFrame) == 1 &&
-        cellBSize.GetUnit() != eStyleUnit_Auto &&
+        !cellBSize.IsAuto() &&
         /* calc() with both percentages and lengths treated like 'auto' */
-        (cellBSize.ConvertsToLength() || cellBSize.ConvertsToPercent())) {
+        (cellBSize.ConvertsToLength() || cellBSize.ConvertsToPercentage())) {
       AddStateBits(NS_ROW_HAS_CELL_WITH_STYLE_BSIZE);
       return;
     }
   }
   RemoveStateBits(NS_ROW_HAS_CELL_WITH_STYLE_BSIZE);
 }
 
 void nsTableRowFrame::InvalidateFrame(uint32_t aDisplayItemKey,
--- a/layout/tables/nsTableRowGroupFrame.cpp
+++ b/layout/tables/nsTableRowGroupFrame.cpp
@@ -400,19 +400,18 @@ void nsTableRowGroupFrame::ReflowChildre
       aReflowInput.bCoord += cellSpacingB;
 
       if (!reflowAllKids) {
         if (IsSimpleRowFrame(aReflowInput.tableFrame, rowFrame)) {
           // Inform the row of its new bsize.
           rowFrame->DidResize();
           // the overflow area may have changed inflate the overflow area
           const nsStylePosition* stylePos = StylePosition();
-          nsStyleUnit unit = stylePos->BSize(wm).GetUnit();
           if (aReflowInput.tableFrame->IsAutoBSize(wm) &&
-              unit != eStyleUnit_Coord) {
+              !stylePos->BSize(wm).ConvertsToLength()) {
             // Because other cells in the row may need to be aligned
             // differently, repaint the entire row
             InvalidateFrame();
           } else if (oldKidRect.BSize(wm) != desiredSize.BSize(wm)) {
             needToCalcRowBSizes = true;
           }
         } else {
           needToCalcRowBSizes = true;
--- a/layout/xul/nsBox.cpp
+++ b/layout/xul/nsBox.cpp
@@ -383,40 +383,26 @@ bool nsIFrame::AddXULPrefSize(nsIFrame* 
   // add in the css min, max, pref
   const nsStylePosition* position = aBox->StylePosition();
 
   // see if the width or height was specifically set
   // XXX Handle eStyleUnit_Enumerated?
   // (Handling the eStyleUnit_Enumerated types requires
   // GetXULPrefSize/GetXULMinSize methods that don't consider
   // (min-/max-/)(width/height) properties.)
-  const nsStyleCoord& width = position->mWidth;
-  if (width.GetUnit() == eStyleUnit_Coord) {
-    aSize.width = width.GetCoordValue();
+  const auto& width = position->mWidth;
+  if (width.ConvertsToLength()) {
+    aSize.width = std::max(0, width.ToLength());
     aWidthSet = true;
-  } else if (width.IsCalcUnit()) {
-    if (!width.CalcHasPercent()) {
-      // pass 0 for percentage basis since we know there are no %s
-      aSize.width = width.ComputeComputedCalc(0);
-      if (aSize.width < 0) aSize.width = 0;
-      aWidthSet = true;
-    }
   }
 
-  const nsStyleCoord& height = position->mHeight;
-  if (height.GetUnit() == eStyleUnit_Coord) {
-    aSize.height = height.GetCoordValue();
+  const auto& height = position->mHeight;
+  if (height.ConvertsToLength()) {
+    aSize.height = std::max(0, height.ToLength());
     aHeightSet = true;
-  } else if (height.IsCalcUnit()) {
-    if (!height.CalcHasPercent()) {
-      // pass 0 for percentage basis since we know there are no %s
-      aSize.height = height.ComputeComputedCalc(0);
-      if (aSize.height < 0) aSize.height = 0;
-      aHeightSet = true;
-    }
   }
 
   nsIContent* content = aBox->GetContent();
   // ignore 'height' and 'width' attributes if the actual element is not XUL
   // For example, we might be magic XUL frames whose primary content is an HTML
   // <select>
   if (content && content->IsXULElement()) {
     nsAutoString value;
@@ -496,52 +482,44 @@ bool nsIFrame::AddXULMinSize(nsBoxLayout
         default:
           break;
       }
     }
   }
 
   // add in the css min, max, pref
   const nsStylePosition* position = aBox->StylePosition();
-
-  // same for min size. Unfortunately min size is always set to 0. So for now
-  // we will assume 0 (as a coord) means not set.
-  const nsStyleCoord& minWidth = position->mMinWidth;
-  if ((minWidth.GetUnit() == eStyleUnit_Coord &&
-       minWidth.GetCoordValue() != 0) ||
-      (minWidth.IsCalcUnit() && !minWidth.CalcHasPercent())) {
-    nscoord min = minWidth.ComputeCoordPercentCalc(0);
+  const auto& minWidth = position->mMinWidth;
+  if (minWidth.ConvertsToLength()) {
+    nscoord min = minWidth.ToLength();
     if (!aWidthSet || (min > aSize.width && canOverride)) {
       aSize.width = min;
       aWidthSet = true;
     }
-  } else if (minWidth.GetUnit() == eStyleUnit_Percent) {
-    NS_ASSERTION(minWidth.GetPercentValue() == 0.0f,
+  } else if (minWidth.ConvertsToPercentage()) {
+    NS_ASSERTION(minWidth.ToPercentage() == 0.0f,
                  "Non-zero percentage values not currently supported");
     aSize.width = 0;
     aWidthSet = true;  // FIXME: should we really do this for
                        // nonzero values?
   }
-  // XXX Handle eStyleUnit_Enumerated?
-  // (Handling the eStyleUnit_Enumerated types requires
-  // GetXULPrefSize/GetXULMinSize methods that don't consider
-  // (min-/max-/)(width/height) properties.
+  // XXX Handle ExtremumLength?
+  // (Handling them  requires GetXULPrefSize/GetXULMinSize methods that don't
+  // consider (min-/max-/)(width/height) properties.
   // calc() with percentage is treated like '0' (unset)
 
-  const nsStyleCoord& minHeight = position->mMinHeight;
-  if ((minHeight.GetUnit() == eStyleUnit_Coord &&
-       minHeight.GetCoordValue() != 0) ||
-      (minHeight.IsCalcUnit() && !minHeight.CalcHasPercent())) {
-    nscoord min = minHeight.ComputeCoordPercentCalc(0);
+  const auto& minHeight = position->mMinHeight;
+  if (minHeight.ConvertsToLength()) {
+    nscoord min = minHeight.ToLength();
     if (!aHeightSet || (min > aSize.height && canOverride)) {
       aSize.height = min;
       aHeightSet = true;
     }
-  } else if (minHeight.GetUnit() == eStyleUnit_Percent) {
-    NS_ASSERTION(position->mMinHeight.GetPercentValue() == 0.0f,
+  } else if (minHeight.ConvertsToPercentage()) {
+    NS_ASSERTION(position->mMinHeight.ToPercentage() == 0.0f,
                  "Non-zero percentage values not currently supported");
     aSize.height = 0;
     aHeightSet = true;  // FIXME: should we really do this for
                         // nonzero values?
   }
   // calc() with percentage is treated like '0' (unset)
 
   nsIContent* content = aBox->GetContent();
@@ -583,26 +561,26 @@ bool nsIFrame::AddXULMaxSize(nsIFrame* a
   const nsStylePosition* position = aBox->StylePosition();
 
   // and max
   // see if the width or height was specifically set
   // XXX Handle eStyleUnit_Enumerated?
   // (Handling the eStyleUnit_Enumerated types requires
   // GetXULPrefSize/GetXULMinSize methods that don't consider
   // (min-/max-/)(width/height) properties.)
-  const nsStyleCoord maxWidth = position->mMaxWidth;
+  const auto& maxWidth = position->mMaxWidth;
   if (maxWidth.ConvertsToLength()) {
-    aSize.width = maxWidth.ComputeCoordPercentCalc(0);
+    aSize.width = maxWidth.ToLength();
     aWidthSet = true;
   }
   // percentages and calc() with percentages are treated like 'none'
 
-  const nsStyleCoord& maxHeight = position->mMaxHeight;
+  const auto& maxHeight = position->mMaxHeight;
   if (maxHeight.ConvertsToLength()) {
-    aSize.height = maxHeight.ComputeCoordPercentCalc(0);
+    aSize.height = maxHeight.ToLength();
     aHeightSet = true;
   }
   // percentages and calc() with percentages are treated like 'none'
 
   nsIContent* content = aBox->GetContent();
   if (content && content->IsXULElement()) {
     nsAutoString value;
     nsresult error;
--- a/layout/xul/tree/nsTreeBodyFrame.cpp
+++ b/layout/xul/tree/nsTreeBodyFrame.cpp
@@ -1968,26 +1968,26 @@ nsRect nsTreeBodyFrame::GetImageSize(int
   const nsStylePosition* myPosition = aComputedStyle->StylePosition();
   const nsStyleList* myList = aComputedStyle->StyleList();
 
   if (useImageRegion) {
     r.x += myList->mImageRegion.x;
     r.y += myList->mImageRegion.y;
   }
 
-  if (myPosition->mWidth.GetUnit() == eStyleUnit_Coord) {
-    int32_t val = myPosition->mWidth.GetCoordValue();
+  if (myPosition->mWidth.ConvertsToLength()) {
+    int32_t val = myPosition->mWidth.ToLength();
     r.width += val;
   } else if (useImageRegion && myList->mImageRegion.width > 0)
     r.width += myList->mImageRegion.width;
   else
     needWidth = true;
 
-  if (myPosition->mHeight.GetUnit() == eStyleUnit_Coord) {
-    int32_t val = myPosition->mHeight.GetCoordValue();
+  if (myPosition->mHeight.ConvertsToLength()) {
+    int32_t val = myPosition->mHeight.ToLength();
     r.height += val;
   } else if (useImageRegion && myList->mImageRegion.height > 0)
     r.height += myList->mImageRegion.height;
   else
     needHeight = true;
 
   if (image) {
     if (needWidth || needHeight) {
@@ -2030,27 +2030,27 @@ nsSize nsTreeBodyFrame::GetImageDestSize
   // We need to get the width and height.
   bool needWidth = false;
   bool needHeight = false;
 
   // Get the style position to see if the CSS has specified the
   // destination width/height.
   const nsStylePosition* myPosition = aComputedStyle->StylePosition();
 
-  if (myPosition->mWidth.GetUnit() == eStyleUnit_Coord) {
+  if (myPosition->mWidth.ConvertsToLength()) {
     // CSS has specified the destination width.
-    size.width = myPosition->mWidth.GetCoordValue();
+    size.width = myPosition->mWidth.ToLength();
   } else {
     // We'll need to get the width of the image/region.
     needWidth = true;
   }
 
-  if (myPosition->mHeight.GetUnit() == eStyleUnit_Coord) {
+  if (myPosition->mHeight.ConvertsToLength()) {
     // CSS has specified the destination height.
-    size.height = myPosition->mHeight.GetCoordValue();
+    size.height = myPosition->mHeight.ToLength();
   } else {
     // We'll need to get the height of the image/region.
     needHeight = true;
   }
 
   if (needWidth || needHeight) {
     // We need to get the size of the image/region.
     nsSize imageSize(0, 0);
@@ -2138,22 +2138,24 @@ int32_t nsTreeBodyFrame::GetRowHeight() 
   // + the specified margins.
   mScratchArray.Clear();
   ComputedStyle* rowContext =
       GetPseudoComputedStyle(nsCSSAnonBoxes::mozTreeRow());
   if (rowContext) {
     const nsStylePosition* myPosition = rowContext->StylePosition();
 
     nscoord minHeight = 0;
-    if (myPosition->mMinHeight.GetUnit() == eStyleUnit_Coord)
-      minHeight = myPosition->mMinHeight.GetCoordValue();
+    if (myPosition->mMinHeight.ConvertsToLength()) {
+      minHeight = myPosition->mMinHeight.ToLength();
+    }
 
     nscoord height = 0;
-    if (myPosition->mHeight.GetUnit() == eStyleUnit_Coord)
-      height = myPosition->mHeight.GetCoordValue();
+    if (myPosition->mHeight.ConvertsToLength()) {
+      height = myPosition->mHeight.ToLength();
+    }
 
     if (height < minHeight) height = minHeight;
 
     if (height > 0) {
       height = nsPresContext::AppUnitsToIntCSSPixels(height);
       height += height % 2;
       height = nsPresContext::CSSPixelsToAppUnits(height);
 
@@ -2174,19 +2176,18 @@ int32_t nsTreeBodyFrame::GetRowHeight() 
 int32_t nsTreeBodyFrame::GetIndentation() {
   // Look up the correct indentation.  It is equal to the specified indentation
   // width.
   mScratchArray.Clear();
   ComputedStyle* indentContext =
       GetPseudoComputedStyle(nsCSSAnonBoxes::mozTreeIndentation());
   if (indentContext) {
     const nsStylePosition* myPosition = indentContext->StylePosition();
-    if (myPosition->mWidth.GetUnit() == eStyleUnit_Coord) {
-      nscoord val = myPosition->mWidth.GetCoordValue();
-      return val;
+    if (myPosition->mWidth.ConvertsToLength()) {
+      return myPosition->mWidth.ToLength();
     }
   }
 
   return nsPresContext::CSSPixelsToAppUnits(16);  // As good a default as any.
 }
 
 void nsTreeBodyFrame::CalcInnerBox() {
   mInnerBox.SetRect(0, 0, mRect.width, mRect.height);
@@ -2883,19 +2884,19 @@ ImgDrawResult nsTreeBodyFrame::PaintSepa
     theme->DrawWidgetBackground(&aRenderingContext, this,
                                 displayData->mAppearance, aSeparatorRect,
                                 dirty);
   } else {
     const nsStylePosition* stylePosition = separatorContext->StylePosition();
 
     // Obtain the height for the separator or use the default value.
     nscoord height;
-    if (stylePosition->mHeight.GetUnit() == eStyleUnit_Coord)
-      height = stylePosition->mHeight.GetCoordValue();
-    else {
+    if (stylePosition->mHeight.ConvertsToLength()) {
+      height = stylePosition->mHeight.ToLength();
+    } else {
       // Use default height 2px.
       height = nsPresContext::CSSPixelsToAppUnits(2);
     }
 
     // Obtain the margins for the separator and then deflate our rect by that
     // amount. The separator is assumed to be contained within the deflated
     // rect.
     nsRect separatorRect(aSeparatorRect.x, aSeparatorRect.y,
@@ -3628,28 +3629,28 @@ ImgDrawResult nsTreeBodyFrame::PaintDrop
       twistyRect.Inflate(twistyMargin);
       currX += twistyRect.width;
     }
 
     const nsStylePosition* stylePosition = feedbackContext->StylePosition();
 
     // Obtain the width for the drop feedback or use default value.
     nscoord width;
-    if (stylePosition->mWidth.GetUnit() == eStyleUnit_Coord)
-      width = stylePosition->mWidth.GetCoordValue();
-    else {
+    if (stylePosition->mWidth.ConvertsToLength()) {
+      width = stylePosition->mWidth.ToLength();
+    } else {
       // Use default width 50px.
       width = nsPresContext::CSSPixelsToAppUnits(50);
     }
 
     // Obtain the height for the drop feedback or use default value.
     nscoord height;
-    if (stylePosition->mHeight.GetUnit() == eStyleUnit_Coord)
-      height = stylePosition->mHeight.GetCoordValue();
-    else {
+    if (stylePosition->mHeight.ConvertsToLength()) {
+      height = stylePosition->mHeight.ToLength();
+    } else {
       // Use default height 2px.
       height = nsPresContext::CSSPixelsToAppUnits(2);
     }
 
     // Obtain the margins for the drop feedback and then deflate our rect
     // by that amount.
     nsRect feedbackRect(currX, aDropFeedbackRect.y, width, height);
     nsMargin margin;
--- a/servo/components/style/cbindgen.toml
+++ b/servo/components/style/cbindgen.toml
@@ -74,40 +74,78 @@ include = [
   "OverflowClipBox",
   "Resize",
   "Overflow",
   "LengthPercentage",
   "NonNegativeLengthPercentage",
   "LengthPercentageOrAuto",
   "Rect",
   "IntersectionObserverRootMargin",
+  "Size",
+  "MaxSize",
+  "FlexBasis",
 ]
 item_types = ["enums", "structs", "typedefs"]
 
 [export.body]
 "LengthPercentage" = """
   // Defined in nsStyleCoord.h
   static constexpr inline StyleLengthPercentage Zero();
+  static inline StyleLengthPercentage FromAppUnits(nscoord);
   inline bool HasPercent() const;
   inline bool ConvertsToLength() const;
   inline nscoord ToLength() const;
   inline bool ConvertsToPercentage() const;
+  inline bool HasLengthAndPercentage() const;
   inline float ToPercentage() const;
   inline CSSCoord LengthInCSSPixels() const;
   inline float Percentage() const;
   inline CSSCoord ResolveToCSSPixels(CSSCoord aPercentageBasisInCSSPixels) const;
   template<typename T> inline CSSCoord ResolveToCSSPixelsWith(T aPercentageGetter) const;
   inline nscoord Resolve(nscoord aPercentageBasis) const;
   template<typename T> inline nscoord ResolveWith(T aPercentageGetter) const;
 """
 
 "GenericLengthPercentageOrAuto" = """
   inline const StyleLengthPercentage& AsLengthPercentage() const;
+  inline bool ConvertsToLength() const;
+  inline nscoord ToLength() const;
+  inline bool ConvertsToPercentage() const;
+  inline float ToPercentage() const;
   inline bool HasPercent() const;
+  inline bool HasLengthAndPercentage() const;
+"""
+
+"GenericSize" = """
+  inline const StyleLengthPercentage& AsLengthPercentage() const;
+  inline StyleExtremumLength AsExtremumLength() const;
   inline bool ConvertsToLength() const;
+  inline nscoord ToLength() const;
+  inline bool ConvertsToPercentage() const;
+  inline float ToPercentage() const;
+  inline bool HasPercent() const;
+  inline bool HasLengthAndPercentage() const;
+  inline bool BehavesLikeInitialValueOnBlockAxis() const;
+"""
+
+"GenericFlexBasis" = """
+  inline bool IsAuto() const;
+  inline const StyleSize& AsSize() const;
+"""
+
+"GenericMaxSize" = """
+  inline const StyleLengthPercentage& AsLengthPercentage() const;
+  inline StyleExtremumLength AsExtremumLength() const;
+  inline bool ConvertsToLength() const;
+  inline nscoord ToLength() const;
+  inline bool ConvertsToPercentage() const;
+  inline float ToPercentage() const;
+  inline bool HasPercent() const;
+  inline bool HasLengthAndPercentage() const;
+  inline bool BehavesLikeInitialValueOnBlockAxis() 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
--- a/servo/components/style/gecko/values.rs
+++ b/servo/components/style/gecko/values.rs
@@ -2,32 +2,28 @@
  * 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::{self, nsStyleCoord, CounterStylePtr};
+use crate::gecko_bindings::structs::{nsStyleCoord, CounterStylePtr};
 use crate::gecko_bindings::structs::{StyleGridTrackBreadth, StyleShapeRadius};
 use crate::gecko_bindings::sugar::ns_style_coord::{CoordData, CoordDataMut, CoordDataValue};
 use crate::media_queries::Device;
 use crate::values::computed::basic_shape::ShapeRadius as ComputedShapeRadius;
-use crate::values::computed::FlexBasis as ComputedFlexBasis;
-use crate::values::computed::{Angle, ExtremumLength, Length, LengthPercentage};
-use crate::values::computed::{MaxSize as ComputedMaxSize, Size as ComputedSize};
-use crate::values::computed::{NonNegativeLengthPercentage, Percentage};
-use crate::values::computed::{Number, NumberOrPercentage};
+use crate::values::computed::{Angle, Length, LengthPercentage};
+use crate::values::computed::{Number, NumberOrPercentage, Percentage};
 use crate::values::generics::basic_shape::ShapeRadius;
 use crate::values::generics::box_::Perspective;
-use crate::values::generics::flex::FlexBasis;
 use crate::values::generics::gecko::ScrollSnapPoint;
 use crate::values::generics::grid::{TrackBreadth, TrackKeyword};
-use crate::values::generics::length::{LengthPercentageOrAuto, MaxSize, Size};
+use crate::values::generics::length::LengthPercentageOrAuto;
 use crate::values::generics::{CounterStyleOrNone, NonNegative};
 use crate::values::{Auto, Either, None_, Normal};
 use crate::Atom;
 use app_units::Au;
 use cssparser::RGBA;
 use nsstring::{nsACString, nsCStr};
 use std::cmp::max;
 
@@ -75,39 +71,16 @@ where
         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 ComputedFlexBasis {
-    fn to_gecko_style_coord<T: CoordDataMut>(&self, coord: &mut T) {
-        match *self {
-            FlexBasis::Content => coord.set_value(CoordDataValue::Enumerated(
-                structs::NS_STYLE_FLEX_BASIS_CONTENT,
-            )),
-            FlexBasis::Width(ref w) => w.to_gecko_style_coord(coord),
-        }
-    }
-
-    fn from_gecko_style_coord<T: CoordData>(coord: &T) -> Option<Self> {
-        if let Some(width) = ComputedSize::from_gecko_style_coord(coord) {
-            return Some(FlexBasis::Width(width));
-        }
-
-        if let CoordDataValue::Enumerated(structs::NS_STYLE_FLEX_BASIS_CONTENT) = coord.as_value() {
-            return Some(FlexBasis::Content);
-        }
-
-        None
-    }
-}
-
 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),
@@ -331,70 +304,16 @@ impl GeckoStyleCoordConvertible for Norm
         if let CoordDataValue::Normal = coord.as_value() {
             Some(Normal)
         } else {
             None
         }
     }
 }
 
-impl GeckoStyleCoordConvertible for ExtremumLength {
-    fn to_gecko_style_coord<T: CoordDataMut>(&self, coord: &mut T) {
-        coord.set_value(CoordDataValue::Enumerated(*self as u32));
-    }
-
-    fn from_gecko_style_coord<T: CoordData>(coord: &T) -> Option<Self> {
-        use num_traits::FromPrimitive;
-        match coord.as_value() {
-            CoordDataValue::Enumerated(v) => ExtremumLength::from_u32(v),
-            _ => None,
-        }
-    }
-}
-
-impl GeckoStyleCoordConvertible for ComputedSize {
-    fn to_gecko_style_coord<T: CoordDataMut>(&self, coord: &mut T) {
-        match *self {
-            Size::LengthPercentage(ref lpoa) => lpoa.to_gecko_style_coord(coord),
-            Size::Auto => coord.set_value(CoordDataValue::Auto),
-            Size::ExtremumLength(ref e) => e.to_gecko_style_coord(coord),
-        }
-    }
-
-    fn from_gecko_style_coord<T: CoordData>(coord: &T) -> Option<Self> {
-        if let CoordDataValue::Auto = coord.as_value() {
-            return Some(Size::Auto);
-        }
-        if let Some(lp) = NonNegativeLengthPercentage::from_gecko_style_coord(coord) {
-            return Some(Size::LengthPercentage(lp));
-        }
-        ExtremumLength::from_gecko_style_coord(coord).map(Size::ExtremumLength)
-    }
-}
-
-impl GeckoStyleCoordConvertible for ComputedMaxSize {
-    fn to_gecko_style_coord<T: CoordDataMut>(&self, coord: &mut T) {
-        match *self {
-            MaxSize::LengthPercentage(ref lpon) => lpon.to_gecko_style_coord(coord),
-            MaxSize::None => coord.set_value(CoordDataValue::None),
-            MaxSize::ExtremumLength(ref e) => e.to_gecko_style_coord(coord),
-        }
-    }
-
-    fn from_gecko_style_coord<T: CoordData>(coord: &T) -> Option<Self> {
-        if let CoordDataValue::None = coord.as_value() {
-            return Some(MaxSize::None);
-        }
-        if let Some(lp) = NonNegativeLengthPercentage::from_gecko_style_coord(coord) {
-            return Some(MaxSize::LengthPercentage(lp));
-        }
-        ExtremumLength::from_gecko_style_coord(coord).map(MaxSize::ExtremumLength)
-    }
-}
-
 impl GeckoStyleCoordConvertible for ScrollSnapPoint<LengthPercentage> {
     fn to_gecko_style_coord<T: CoordDataMut>(&self, coord: &mut T) {
         match self.repeated() {
             None => coord.set_value(CoordDataValue::None),
             Some(l) => l.to_gecko_style_coord(coord),
         };
     }
 
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -1380,23 +1380,23 @@ impl Clone for ${style_struct.gecko_stru
         "ColorOrAuto": impl_color,
         "GreaterThanOrEqualToOneNumber": impl_simple,
         "Integer": impl_simple,
         "length::LengthOrAuto": impl_style_coord,
         "length::LengthOrNormal": impl_style_coord,
         "length::NonNegativeLengthOrAuto": impl_style_coord,
         "length::NonNegativeLengthPercentageOrNormal": impl_style_coord,
         "FillRule": impl_simple,
-        "FlexBasis": impl_style_coord,
+        "FlexBasis": impl_simple,
         "Length": impl_absolute_length,
         "LengthOrNormal": impl_style_coord,
         "LengthPercentage": impl_simple,
         "LengthPercentageOrAuto": impl_style_coord,
-        "MaxSize": impl_style_coord,
-        "Size": impl_style_coord,
+        "MaxSize": impl_simple,
+        "Size": impl_simple,
         "MozScriptMinSize": impl_absolute_length,
         "MozScriptSizeMultiplier": impl_simple,
         "NonNegativeLengthPercentage": impl_simple,
         "NonNegativeNumber": impl_simple,
         "Number": impl_simple,
         "Opacity": impl_simple,
         "OverflowWrap": impl_simple,
         "OverflowAnchor": impl_simple,
--- a/servo/components/style/properties/longhands/position.mako.rs
+++ b/servo/components/style/properties/longhands/position.mako.rs
@@ -220,27 +220,28 @@ macro_rules! impl_align_conversions {
 // https://drafts.csswg.org/css-flexbox/#propdef-order
 ${helpers.predefined_type(
     "order",
     "Integer",
     "0",
     extra_prefixes="webkit",
     animation_value_type="ComputedValue",
     spec="https://drafts.csswg.org/css-flexbox/#order-property",
-    servo_restyle_damage = "reflow",
+    servo_restyle_damage="reflow",
 )}
 
 ${helpers.predefined_type(
     "flex-basis",
     "FlexBasis",
     "computed::FlexBasis::auto()",
     spec="https://drafts.csswg.org/css-flexbox/#flex-basis-property",
     extra_prefixes="webkit",
     animation_value_type="FlexBasis",
-    servo_restyle_damage = "reflow",
+    servo_restyle_damage="reflow",
+    boxed=True,
 )}
 
 % for (size, logical) in ALL_SIZES:
     <%
         spec = "https://drafts.csswg.org/css-box/#propdef-%s"
         if logical:
             spec = "https://drafts.csswg.org/css-logical-props/#propdef-%s"
     %>
--- a/servo/components/style/values/computed/flex.rs
+++ b/servo/components/style/values/computed/flex.rs
@@ -9,11 +9,11 @@ use crate::values::generics::flex::FlexB
 
 /// A computed value for the `flex-basis` property.
 pub type FlexBasis = GenericFlexBasis<Size>;
 
 impl FlexBasis {
     /// `auto`
     #[inline]
     pub fn auto() -> Self {
-        GenericFlexBasis::Width(Size::auto())
+        GenericFlexBasis::Size(Size::auto())
     }
 }
--- a/servo/components/style/values/generics/flex.rs
+++ b/servo/components/style/values/generics/flex.rs
@@ -14,14 +14,17 @@
     Debug,
     PartialEq,
     SpecifiedValueInfo,
     ToAnimatedValue,
     ToAnimatedZero,
     ToComputedValue,
     ToCss,
 )]
-pub enum FlexBasis<Width> {
+#[repr(C)]
+pub enum GenericFlexBasis<S> {
     /// `content`
     Content,
     /// `<width>`
-    Width(Width),
+    Size(S),
 }
+
+pub use self::GenericFlexBasis as FlexBasis;
--- a/servo/components/style/values/generics/grid.rs
+++ b/servo/components/style/values/generics/grid.rs
@@ -187,20 +187,17 @@ pub enum TrackBreadth<L> {
 }
 
 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 {
-        match *self {
-            TrackBreadth::Breadth(ref _lp) => true,
-            _ => false,
-        }
+        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>
 #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo)]
--- a/servo/components/style/values/generics/length.rs
+++ b/servo/components/style/values/generics/length.rs
@@ -91,24 +91,27 @@ impl<LengthPercentage: Parse> Parse for 
     MallocSizeOf,
     PartialEq,
     SpecifiedValueInfo,
     ToAnimatedValue,
     ToAnimatedZero,
     ToComputedValue,
     ToCss,
 )]
-pub enum Size<LengthPercentage> {
-    LengthPercentage(LengthPercentage),
+#[repr(C, u8)]
+pub enum GenericSize<LengthPercent> {
+    LengthPercentage(LengthPercent),
     Auto,
     #[cfg(feature = "gecko")]
     #[animation(error)]
     ExtremumLength(ExtremumLength),
 }
 
+pub use self::GenericSize as Size;
+
 impl<LengthPercentage> Size<LengthPercentage> {
     /// `auto` value.
     #[inline]
     pub fn auto() -> Self {
         Size::Auto
     }
 
     /// Returns whether we're the auto value.
@@ -129,23 +132,26 @@ impl<LengthPercentage> Size<LengthPercen
     Debug,
     PartialEq,
     SpecifiedValueInfo,
     ToAnimatedValue,
     ToAnimatedZero,
     ToComputedValue,
     ToCss,
 )]
-pub enum MaxSize<LengthPercentage> {
-    LengthPercentage(LengthPercentage),
+#[repr(C, u8)]
+pub enum GenericMaxSize<LengthPercent> {
+    LengthPercentage(LengthPercent),
     None,
     #[cfg(feature = "gecko")]
     #[animation(error)]
     ExtremumLength(ExtremumLength),
 }
 
+pub use self::GenericMaxSize as MaxSize;
+
 impl<LengthPercentage> MaxSize<LengthPercentage> {
     /// `none` value.
     #[inline]
     pub fn none() -> Self {
         MaxSize::None
     }
 }
--- a/servo/components/style/values/specified/flex.rs
+++ b/servo/components/style/values/specified/flex.rs
@@ -13,30 +13,30 @@ use style_traits::ParseError;
 /// A specified value for the `flex-basis` property.
 pub type FlexBasis = GenericFlexBasis<Size>;
 
 impl Parse for FlexBasis {
     fn parse<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Self, ParseError<'i>> {
-        if let Ok(width) = input.try(|i| Size::parse(context, i)) {
-            return Ok(GenericFlexBasis::Width(width));
+        if let Ok(size) = input.try(|i| Size::parse(context, i)) {
+            return Ok(GenericFlexBasis::Size(size));
         }
         try_match_ident_ignore_ascii_case! { input,
             "content" => Ok(GenericFlexBasis::Content),
         }
     }
 }
 
 impl FlexBasis {
     /// `auto`
     #[inline]
     pub fn auto() -> Self {
-        GenericFlexBasis::Width(Size::auto())
+        GenericFlexBasis::Size(Size::auto())
     }
 
     /// `0%`
     #[inline]
     pub fn zero_percent() -> Self {
-        GenericFlexBasis::Width(Size::zero_percent())
+        GenericFlexBasis::Size(Size::zero_percent())
     }
 }