Bug 1690706 - Move caption-side outside of mako. r=TYLin
authorEmilio Cobos Álvarez <emilio@crisal.io>
Sun, 07 Feb 2021 12:33:45 +0000
changeset 566301 fde83e4dd9dc9c9c4782fd9b05913125a2378f9a
parent 566300 7da855a143d6f6197082b2ecbf3e67282b8f58cb
child 566302 9929a911a9942318e7c0ce4147816d673d08ae65
push id135896
push userealvarez@mozilla.com
push dateSun, 07 Feb 2021 12:36:13 +0000
treeherderautoland@fde83e4dd9dc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersTYLin
bugs1690706
milestone87.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 1690706 - Move caption-side outside of mako. r=TYLin Make it an enum class, etc. Differential Revision: https://phabricator.services.mozilla.com/D103978
dom/html/HTMLTableCaptionElement.cpp
layout/generic/ReflowInput.cpp
layout/generic/nsContainerFrame.cpp
layout/style/ServoBindings.toml
layout/style/nsStyleConsts.h
layout/style/nsStyleStruct.cpp
layout/style/nsStyleStruct.h
layout/tables/nsTableWrapperFrame.cpp
layout/tables/nsTableWrapperFrame.h
servo/components/style/properties/data.py
servo/components/style/properties/longhands/inherited_table.mako.rs
servo/components/style/values/computed/mod.rs
servo/components/style/values/computed/table.rs
servo/components/style/values/specified/mod.rs
servo/components/style/values/specified/table.rs
servo/ports/geckolib/cbindgen.toml
servo/ports/geckolib/glue.rs
--- a/dom/html/HTMLTableCaptionElement.cpp
+++ b/dom/html/HTMLTableCaptionElement.cpp
@@ -21,20 +21,20 @@ HTMLTableCaptionElement::~HTMLTableCapti
 JSObject* HTMLTableCaptionElement::WrapNode(JSContext* aCx,
                                             JS::Handle<JSObject*> aGivenProto) {
   return HTMLTableCaptionElement_Binding::Wrap(aCx, this, aGivenProto);
 }
 
 NS_IMPL_ELEMENT_CLONE(HTMLTableCaptionElement)
 
 static const nsAttrValue::EnumTable kCaptionAlignTable[] = {
-    {"left", NS_STYLE_CAPTION_SIDE_LEFT},
-    {"right", NS_STYLE_CAPTION_SIDE_RIGHT},
-    {"top", NS_STYLE_CAPTION_SIDE_TOP},
-    {"bottom", NS_STYLE_CAPTION_SIDE_BOTTOM},
+    {"left", StyleCaptionSide::Left},
+    {"right", StyleCaptionSide::Right},
+    {"top", StyleCaptionSide::Top},
+    {"bottom", StyleCaptionSide::Bottom},
     {nullptr, 0}};
 
 bool HTMLTableCaptionElement::ParseAttribute(
     int32_t aNamespaceID, nsAtom* aAttribute, const nsAString& aValue,
     nsIPrincipal* aMaybeScriptedPrincipal, nsAttrValue& aResult) {
   if (aAttribute == nsGkAtoms::align && aNamespaceID == kNameSpaceID_None) {
     return aResult.ParseEnumValue(aValue, kCaptionAlignTable, false);
   }
--- a/layout/generic/ReflowInput.cpp
+++ b/layout/generic/ReflowInput.cpp
@@ -2058,19 +2058,19 @@ static eNormalLineHeightControl GetNorma
 }
 
 static inline bool IsSideCaption(nsIFrame* aFrame,
                                  const nsStyleDisplay* aStyleDisplay,
                                  WritingMode aWM) {
   if (aStyleDisplay->mDisplay != StyleDisplay::TableCaption) {
     return false;
   }
-  uint8_t captionSide = aFrame->StyleTableBorder()->mCaptionSide;
-  return captionSide == NS_STYLE_CAPTION_SIDE_LEFT ||
-         captionSide == NS_STYLE_CAPTION_SIDE_RIGHT;
+  auto captionSide = aFrame->StyleTableBorder()->mCaptionSide;
+  return captionSide == StyleCaptionSide::Left ||
+         captionSide == StyleCaptionSide::Right;
 }
 
 // XXX refactor this code to have methods for each set of properties
 // we are computing: width,height,line-height; margin; offsets
 
 void ReflowInput::InitConstraints(
     nsPresContext* aPresContext, const Maybe<LogicalSize>& aContainingBlockSize,
     const Maybe<LogicalMargin>& aBorder, const Maybe<LogicalMargin>& aPadding,
--- a/layout/generic/nsContainerFrame.cpp
+++ b/layout/generic/nsContainerFrame.cpp
@@ -998,43 +998,43 @@ LogicalSize nsContainerFrame::ComputeAut
   }
 
   if (IsTableCaption()) {
     // If we're a container for font size inflation, then shrink
     // wrapping inside of us should not apply font size inflation.
     AutoMaybeDisableFontInflation an(this);
 
     WritingMode tableWM = GetParent()->GetWritingMode();
-    uint8_t captionSide = StyleTableBorder()->mCaptionSide;
+    StyleCaptionSide captionSide = StyleTableBorder()->mCaptionSide;
 
     if (aWM.IsOrthogonalTo(tableWM)) {
-      if (captionSide == NS_STYLE_CAPTION_SIDE_TOP ||
-          captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE ||
-          captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM ||
-          captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE) {
+      if (captionSide == StyleCaptionSide::Top ||
+          captionSide == StyleCaptionSide::TopOutside ||
+          captionSide == StyleCaptionSide::Bottom ||
+          captionSide == StyleCaptionSide::BottomOutside) {
         // For an orthogonal caption on a block-dir side of the table,
         // shrink-wrap to min-isize.
         result.ISize(aWM) = GetMinISize(aRenderingContext);
       } else {
         // An orthogonal caption on an inline-dir side of the table
         // is constrained to the containing block.
         nscoord pref = GetPrefISize(aRenderingContext);
         if (pref > aCBSize.ISize(aWM)) {
           pref = aCBSize.ISize(aWM);
         }
         if (pref < result.ISize(aWM)) {
           result.ISize(aWM) = pref;
         }
       }
     } else {
-      if (captionSide == NS_STYLE_CAPTION_SIDE_LEFT ||
-          captionSide == NS_STYLE_CAPTION_SIDE_RIGHT) {
+      if (captionSide == StyleCaptionSide::Left ||
+          captionSide == StyleCaptionSide::Right) {
         result.ISize(aWM) = GetMinISize(aRenderingContext);
-      } else if (captionSide == NS_STYLE_CAPTION_SIDE_TOP ||
-                 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) {
+      } else if (captionSide == StyleCaptionSide::Top ||
+                 captionSide == StyleCaptionSide::Bottom) {
         // The outer frame constrains our available isize to the isize of
         // the table.  Grow if our min-isize is bigger than that, but not
         // larger than the containing block isize.  (It would really be nice
         // to transmit that information another way, so we could grow up to
         // the table's available isize, but that's harder.)
         nscoord min = GetMinISize(aRenderingContext);
         if (min > aCBSize.ISize(aWM)) {
           min = aCBSize.ISize(aWM);
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -576,16 +576,17 @@ cbindgen-types = [
     { gecko = "StyleGridAutoFlow", servo = "crate::values::computed::GridAutoFlow" },
     { gecko = "StyleCursor", servo = "crate::values::computed::Cursor" },
     { gecko = "StyleSVGStrokeDashArray", servo = "crate::values::computed::svg::SVGStrokeDashArray" },
     { gecko = "StyleSVGWidth", servo = "crate::values::computed::svg::SVGWidth" },
     { gecko = "StyleSVGOpacity", servo = "crate::values::computed::svg::SVGOpacity" },
     { gecko = "StyleSVGLength", servo = "crate::values::computed::svg::SVGLength" },
     { gecko = "StyleFontSizeKeyword", servo = "crate::values::specified::font::FontSizeKeyword" },
     { gecko = "StyleDefaultFontSizes", servo = "crate::gecko::wrapper::DefaultFontSizes" },
+    { gecko = "StyleCaptionSide", servo = "crate::values::computed::table::CaptionSide" },
 ]
 
 mapped-generic-types = [
     { generic = true, gecko = "mozilla::RustCell", servo = "::std::cell::Cell" },
     { generic = false, gecko = "ServoNodeData", servo = "atomic_refcell::AtomicRefCell<crate::data::ElementData>" },
     { generic = false, gecko = "mozilla::ServoWritingMode", servo = "crate::logical_geometry::WritingMode" },
     { generic = false, gecko = "mozilla::ServoCustomPropertiesMap", servo = "Option<servo_arc::Arc<crate::custom_properties::CustomPropertiesMap>>" },
     { generic = false, gecko = "mozilla::ServoRuleNode", servo = "Option<crate::rule_tree::StrongRuleNode>" },
--- a/layout/style/nsStyleConsts.h
+++ b/layout/style/nsStyleConsts.h
@@ -597,27 +597,16 @@ enum class StyleTableLayout : uint8_t {
   Fixed,
 };
 
 enum class StyleEmptyCells : uint8_t {
   Hide,
   Show,
 };
 
-// Constants for the caption-side property. Note that despite having "physical"
-// names, these are actually interpreted according to the table's writing-mode:
-// TOP and BOTTOM are treated as block-start and -end respectively, and LEFT
-// and RIGHT are treated as line-left and -right.
-#define NS_STYLE_CAPTION_SIDE_TOP 0
-#define NS_STYLE_CAPTION_SIDE_RIGHT 1
-#define NS_STYLE_CAPTION_SIDE_BOTTOM 2
-#define NS_STYLE_CAPTION_SIDE_LEFT 3
-#define NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE 4
-#define NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE 5
-
 // constants for cell "scope" attribute
 #define NS_STYLE_CELL_SCOPE_ROW 0
 #define NS_STYLE_CELL_SCOPE_COL 1
 #define NS_STYLE_CELL_SCOPE_ROWGROUP 2
 #define NS_STYLE_CELL_SCOPE_COLGROUP 3
 
 // See nsStylePage
 #define NS_STYLE_PAGE_MARKS_NONE 0x00
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -1350,17 +1350,17 @@ nsChangeHint nsStyleTable::CalcDifferenc
 
 // -----------------------
 // nsStyleTableBorder
 
 nsStyleTableBorder::nsStyleTableBorder(const Document& aDocument)
     : mBorderSpacingCol(0),
       mBorderSpacingRow(0),
       mBorderCollapse(StyleBorderCollapse::Separate),
-      mCaptionSide(NS_STYLE_CAPTION_SIDE_TOP),
+      mCaptionSide(StyleCaptionSide::Top),
       mEmptyCells(StyleEmptyCells::Show) {
   MOZ_COUNT_CTOR(nsStyleTableBorder);
 }
 
 nsStyleTableBorder::~nsStyleTableBorder() {
   MOZ_COUNT_DTOR(nsStyleTableBorder);
 }
 
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -1668,17 +1668,17 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
   ~nsStyleTableBorder();
   static constexpr bool kHasTriggerImageLoads = false;
 
   nsChangeHint CalcDifference(const nsStyleTableBorder& aNewData) const;
 
   nscoord mBorderSpacingCol;
   nscoord mBorderSpacingRow;
   mozilla::StyleBorderCollapse mBorderCollapse;
-  uint8_t mCaptionSide;
+  mozilla::StyleCaptionSide mCaptionSide;
   mozilla::StyleEmptyCells mEmptyCells;
 };
 
 struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleContent {
   using CounterPair = mozilla::StyleGenericCounterPair<int32_t>;
 
   explicit nsStyleContent(const mozilla::dom::Document&);
   nsStyleContent(const nsStyleContent& aContent);
--- a/layout/tables/nsTableWrapperFrame.cpp
+++ b/layout/tables/nsTableWrapperFrame.cpp
@@ -20,18 +20,16 @@
 #include "nsDisplayList.h"
 #include "nsLayoutUtils.h"
 #include "nsIFrameInlines.h"
 #include <algorithm>
 
 using namespace mozilla;
 using namespace mozilla::layout;
 
-#define NO_SIDE 100
-
 /* virtual */
 nscoord nsTableWrapperFrame::GetLogicalBaseline(
     WritingMode aWritingMode) const {
   if (StyleDisplay()->IsContainLayout()) {
     // We have no baseline. Fall back to the inherited impl which is
     // appropriate for this situation.
     return nsContainerFrame::GetLogicalBaseline(aWritingMode);
   }
@@ -263,37 +261,36 @@ nscoord nsTableWrapperFrame::GetMinISize
 
 /* virtual */
 nscoord nsTableWrapperFrame::GetPrefISize(gfxContext* aRenderingContext) {
   nscoord maxISize;
   DISPLAY_PREF_INLINE_SIZE(this, maxISize);
 
   maxISize = nsLayoutUtils::IntrinsicForContainer(
       aRenderingContext, InnerTableFrame(), IntrinsicISizeType::PrefISize);
-  if (mCaptionFrames.NotEmpty()) {
-    uint8_t captionSide = GetCaptionSide();
-    switch (captionSide) {
-      case NS_STYLE_CAPTION_SIDE_LEFT:
-      case NS_STYLE_CAPTION_SIDE_RIGHT: {
+  if (Maybe<StyleCaptionSide> captionSide = GetCaptionSide()) {
+    switch (*captionSide) {
+      case StyleCaptionSide::Left:
+      case StyleCaptionSide::Right: {
         nscoord capMin = nsLayoutUtils::IntrinsicForContainer(
             aRenderingContext, mCaptionFrames.FirstChild(),
             IntrinsicISizeType::MinISize);
         maxISize += capMin;
       } break;
       default: {
         IntrinsicISizeType iwt;
-        if (captionSide == NS_STYLE_CAPTION_SIDE_TOP ||
-            captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) {
+        if (*captionSide == StyleCaptionSide::Top ||
+            *captionSide == StyleCaptionSide::Bottom) {
           // Don't let the caption's pref isize expand the table's pref
           // isize.
           iwt = IntrinsicISizeType::MinISize;
         } else {
-          NS_ASSERTION(captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE ||
-                           captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE,
-                       "unexpected caption side");
+          MOZ_ASSERT(*captionSide == StyleCaptionSide::TopOutside ||
+                         *captionSide == StyleCaptionSide::BottomOutside,
+                     "unexpected caption side");
           iwt = IntrinsicISizeType::PrefISize;
         }
         nscoord capPref = nsLayoutUtils::IntrinsicForContainer(
             aRenderingContext, mCaptionFrames.FirstChild(), iwt);
         maxISize = std::max(maxISize, capPref);
       } break;
     }
   }
@@ -346,107 +343,104 @@ LogicalSize nsTableWrapperFrame::Compute
                "Table wrapper frames cannot have borders or paddings");
 
   // When we're shrink-wrapping, our auto size needs to wrap around the
   // actual size of the table, which (if it is specified as a percent)
   // could be something that is not reflected in our GetMinISize and
   // GetPrefISize.  See bug 349457 for an example.
 
   // Match the availableISize logic in Reflow.
-  uint8_t captionSide = GetCaptionSide();
+  Maybe<StyleCaptionSide> captionSide = GetCaptionSide();
   nscoord inlineSize;
-  if (captionSide == NO_SIDE) {
+  if (!captionSide) {
     inlineSize =
         ChildShrinkWrapISize(aRenderingContext, InnerTableFrame(), aWM, aCBSize,
                              kidAvailableISize, aSizeOverrides);
-  } else if (captionSide == NS_STYLE_CAPTION_SIDE_LEFT ||
-             captionSide == NS_STYLE_CAPTION_SIDE_RIGHT) {
+  } else if (*captionSide == StyleCaptionSide::Left ||
+             *captionSide == StyleCaptionSide::Right) {
     nscoord capISize =
         ChildShrinkWrapISize(aRenderingContext, mCaptionFrames.FirstChild(),
                              aWM, aCBSize, kidAvailableISize, aSizeOverrides);
     inlineSize =
         capISize +
         ChildShrinkWrapISize(aRenderingContext, InnerTableFrame(), aWM, aCBSize,
                              kidAvailableISize - capISize, aSizeOverrides);
-  } else if (captionSide == NS_STYLE_CAPTION_SIDE_TOP ||
-             captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) {
+  } else if (*captionSide == StyleCaptionSide::Top ||
+             *captionSide == StyleCaptionSide::Bottom) {
     nscoord margin;
     inlineSize =
         ChildShrinkWrapISize(aRenderingContext, InnerTableFrame(), aWM, aCBSize,
                              kidAvailableISize, aSizeOverrides, &margin);
     nscoord capISize =
         ChildShrinkWrapISize(aRenderingContext, mCaptionFrames.FirstChild(),
                              aWM, aCBSize, inlineSize - margin, aSizeOverrides);
     if (capISize > inlineSize) {
       inlineSize = capISize;
     }
   } else {
-    NS_ASSERTION(captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE ||
-                     captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE,
-                 "unexpected caption-side");
+    MOZ_ASSERT(*captionSide == StyleCaptionSide::TopOutside ||
+                   *captionSide == StyleCaptionSide::BottomOutside,
+               "unexpected caption-side");
     inlineSize =
         ChildShrinkWrapISize(aRenderingContext, InnerTableFrame(), aWM, aCBSize,
                              kidAvailableISize, aSizeOverrides);
     nscoord capISize =
         ChildShrinkWrapISize(aRenderingContext, mCaptionFrames.FirstChild(),
                              aWM, aCBSize, kidAvailableISize, aSizeOverrides);
     if (capISize > inlineSize) {
       inlineSize = capISize;
     }
   }
 
   return LogicalSize(aWM, inlineSize, NS_UNCONSTRAINEDSIZE);
 }
 
-uint8_t nsTableWrapperFrame::GetCaptionSide() const {
-  if (mCaptionFrames.NotEmpty()) {
-    return mCaptionFrames.FirstChild()->StyleTableBorder()->mCaptionSide;
-  } else {
-    return NO_SIDE;  // no caption
+Maybe<StyleCaptionSide> nsTableWrapperFrame::GetCaptionSide() const {
+  if (mCaptionFrames.IsEmpty()) {
+    return Nothing();
   }
+  return Some(mCaptionFrames.FirstChild()->StyleTableBorder()->mCaptionSide);
 }
 
 StyleVerticalAlignKeyword nsTableWrapperFrame::GetCaptionVerticalAlign() const {
   const auto& va = mCaptionFrames.FirstChild()->StyleDisplay()->mVerticalAlign;
   return va.IsKeyword() ? va.AsKeyword() : StyleVerticalAlignKeyword::Top;
 }
 
 nscoord nsTableWrapperFrame::ComputeFinalBSize(
-    uint8_t aCaptionSide, const LogicalSize& aInnerSize,
+    const MaybeCaptionSide& aCaptionSide, const LogicalSize& aInnerSize,
     const LogicalSize& aCaptionSize, const LogicalMargin& aCaptionMargin,
     const WritingMode aWM) const {
-  nscoord bSize = 0;
+  nscoord bSize = aInnerSize.BSize(aWM);
 
   // compute the overall block-size
-  switch (aCaptionSide) {
-    case NS_STYLE_CAPTION_SIDE_TOP:
-    case NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE:
-    case NS_STYLE_CAPTION_SIDE_BOTTOM:
-    case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE:
-      bSize =
-          aInnerSize.BSize(aWM) +
-          std::max(0, aCaptionSize.BSize(aWM) + aCaptionMargin.BStartEnd(aWM));
-      break;
-    case NS_STYLE_CAPTION_SIDE_LEFT:
-    case NS_STYLE_CAPTION_SIDE_RIGHT:
-      bSize = std::max(aInnerSize.BSize(aWM),
-                       aCaptionSize.BSize(aWM) + aCaptionMargin.BEnd(aWM));
-      break;
-    default:
-      NS_ASSERTION(aCaptionSide == NO_SIDE, "unexpected caption side");
-      bSize = aInnerSize.BSize(aWM);
-      break;
+  if (aCaptionSide) {
+    switch (*aCaptionSide) {
+      case StyleCaptionSide::Top:
+      case StyleCaptionSide::TopOutside:
+      case StyleCaptionSide::Bottom:
+      case StyleCaptionSide::BottomOutside:
+        bSize = aInnerSize.BSize(aWM) +
+                std::max(
+                    0, aCaptionSize.BSize(aWM) + aCaptionMargin.BStartEnd(aWM));
+        break;
+      case StyleCaptionSide::Left:
+      case StyleCaptionSide::Right:
+        bSize = std::max(aInnerSize.BSize(aWM),
+                         aCaptionSize.BSize(aWM) + aCaptionMargin.BEnd(aWM));
+        break;
+    }
   }
 
   // negative sizes can upset overflow-area code
   return std::max(bSize, 0);
 }
 
 nsresult nsTableWrapperFrame::GetCaptionOrigin(
-    uint32_t aCaptionSide, const LogicalSize& aContainBlockSize,
+    StyleCaptionSide aCaptionSide, const LogicalSize& aContainBlockSize,
     const LogicalSize& aInnerSize, const LogicalSize& aCaptionSize,
     LogicalMargin& aCaptionMargin, LogicalPoint& aOrigin, WritingMode aWM) {
   aOrigin.I(aWM) = aOrigin.B(aWM) = 0;
   if ((NS_UNCONSTRAINEDSIZE == aInnerSize.ISize(aWM)) ||
       (NS_UNCONSTRAINEDSIZE == aInnerSize.BSize(aWM)) ||
       (NS_UNCONSTRAINEDSIZE == aCaptionSize.ISize(aWM)) ||
       (NS_UNCONSTRAINEDSIZE == aCaptionSize.BSize(aWM))) {
     return NS_OK;
@@ -457,68 +451,62 @@ nsresult nsTableWrapperFrame::GetCaption
 
   NS_ASSERTION(NS_AUTOMARGIN != aCaptionMargin.IStart(aWM) &&
                    NS_AUTOMARGIN != aCaptionMargin.BStart(aWM) &&
                    NS_AUTOMARGIN != aCaptionMargin.BEnd(aWM),
                "The computed caption margin is auto?");
 
   // inline-dir computation
   switch (aCaptionSide) {
-    case NS_STYLE_CAPTION_SIDE_TOP:
-    case NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE:
-    case NS_STYLE_CAPTION_SIDE_BOTTOM:
-    case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE:
+    case StyleCaptionSide::Top:
+    case StyleCaptionSide::TopOutside:
+    case StyleCaptionSide::Bottom:
+    case StyleCaptionSide::BottomOutside:
       aOrigin.I(aWM) = aCaptionMargin.IStart(aWM);
       break;
-    case NS_STYLE_CAPTION_SIDE_LEFT:
-    case NS_STYLE_CAPTION_SIDE_RIGHT:
+    case StyleCaptionSide::Left:
+    case StyleCaptionSide::Right:
       aOrigin.I(aWM) = aCaptionMargin.IStart(aWM);
-      if (aWM.IsBidiLTR() == (aCaptionSide == NS_STYLE_CAPTION_SIDE_RIGHT)) {
+      if (aWM.IsBidiLTR() == (aCaptionSide == StyleCaptionSide::Right)) {
         aOrigin.I(aWM) += aInnerSize.ISize(aWM);
       }
       break;
-    default:
-      MOZ_ASSERT_UNREACHABLE("Unknown caption alignment type");
-      break;
   }
   // block-dir computation
   switch (aCaptionSide) {
-    case NS_STYLE_CAPTION_SIDE_RIGHT:
-    case NS_STYLE_CAPTION_SIDE_LEFT:
+    case StyleCaptionSide::Right:
+    case StyleCaptionSide::Left:
       aOrigin.B(aWM) = 0;
       switch (GetCaptionVerticalAlign()) {
         case StyleVerticalAlignKeyword::Middle:
           aOrigin.B(aWM) = std::max(
               0, (aInnerSize.BSize(aWM) - aCaptionSize.BSize(aWM)) / 2);
           break;
         case StyleVerticalAlignKeyword::Bottom:
           aOrigin.B(aWM) =
               std::max(0, aInnerSize.BSize(aWM) - aCaptionSize.BSize(aWM));
           break;
         default:
           break;
       }
       break;
-    case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE:
-    case NS_STYLE_CAPTION_SIDE_BOTTOM:
+    case StyleCaptionSide::BottomOutside:
+    case StyleCaptionSide::Bottom:
       aOrigin.B(aWM) = aInnerSize.BSize(aWM) + aCaptionMargin.BStart(aWM);
       break;
-    case NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE:
-    case NS_STYLE_CAPTION_SIDE_TOP:
+    case StyleCaptionSide::TopOutside:
+    case StyleCaptionSide::Top:
       aOrigin.B(aWM) = aCaptionMargin.BStart(aWM);
       break;
-    default:
-      MOZ_ASSERT_UNREACHABLE("Unknown caption alignment type");
-      break;
   }
   return NS_OK;
 }
 
 nsresult nsTableWrapperFrame::GetInnerOrigin(
-    uint32_t aCaptionSide, const LogicalSize& aContainBlockSize,
+    const MaybeCaptionSide& aCaptionSide, const LogicalSize& aContainBlockSize,
     const LogicalSize& aCaptionSize, const LogicalMargin& aCaptionMargin,
     const LogicalSize& aInnerSize, LogicalPoint& aOrigin, WritingMode aWM) {
   NS_ASSERTION(NS_AUTOMARGIN != aCaptionMargin.IStart(aWM) &&
                    NS_AUTOMARGIN != aCaptionMargin.IEnd(aWM),
                "The computed caption margin is auto?");
 
   aOrigin.I(aWM) = aOrigin.B(aWM) = 0;
   if ((NS_UNCONSTRAINEDSIZE == aInnerSize.ISize(aWM)) ||
@@ -526,65 +514,52 @@ nsresult nsTableWrapperFrame::GetInnerOr
       (NS_UNCONSTRAINEDSIZE == aCaptionSize.ISize(aWM)) ||
       (NS_UNCONSTRAINEDSIZE == aCaptionSize.BSize(aWM))) {
     return NS_OK;
   }
 
   nscoord minCapISize = aCaptionSize.ISize(aWM) + aCaptionMargin.IStartEnd(aWM);
 
   // inline-dir computation
-  switch (aCaptionSide) {
-    case NS_STYLE_CAPTION_SIDE_LEFT:
-    case NS_STYLE_CAPTION_SIDE_RIGHT:
-      aOrigin.I(aWM) =
-          (aWM.IsBidiLTR() == (aCaptionSide == NS_STYLE_CAPTION_SIDE_LEFT))
-              ? minCapISize
-              : 0;
-      break;
-    default:
-      NS_ASSERTION(aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP ||
-                       aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE ||
-                       aCaptionSide == NS_STYLE_CAPTION_SIDE_BOTTOM ||
-                       aCaptionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE ||
-                       aCaptionSide == NO_SIDE,
-                   "unexpected caption side");
-      aOrigin.I(aWM) = 0;
-      break;
+  if (aCaptionSide && IsSideCaption(*aCaptionSide)) {
+    aOrigin.I(aWM) =
+        (aWM.IsBidiLTR() == (*aCaptionSide == StyleCaptionSide::Left))
+            ? minCapISize
+            : 0;
   }
 
   // block-dir computation
-  switch (aCaptionSide) {
-    case NS_STYLE_CAPTION_SIDE_BOTTOM:
-    case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE:
-      aOrigin.B(aWM) = 0;
-      break;
-    case NS_STYLE_CAPTION_SIDE_LEFT:
-    case NS_STYLE_CAPTION_SIDE_RIGHT:
-      aOrigin.B(aWM) = 0;
-      switch (GetCaptionVerticalAlign()) {
-        case StyleVerticalAlignKeyword::Middle:
-          aOrigin.B(aWM) = std::max(
-              0, (aCaptionSize.BSize(aWM) - aInnerSize.BSize(aWM)) / 2);
-          break;
-        case StyleVerticalAlignKeyword::Bottom:
-          aOrigin.B(aWM) =
-              std::max(0, aCaptionSize.BSize(aWM) - aInnerSize.BSize(aWM));
-          break;
-        default:
-          break;
-      }
-      break;
-    case NO_SIDE:
-    case NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE:
-    case NS_STYLE_CAPTION_SIDE_TOP:
-      aOrigin.B(aWM) = aCaptionSize.BSize(aWM) + aCaptionMargin.BStartEnd(aWM);
-      break;
-    default:
-      MOZ_ASSERT_UNREACHABLE("Unknown caption alignment type");
-      break;
+  if (aCaptionSide) {
+    switch (*aCaptionSide) {
+      case StyleCaptionSide::Bottom:
+      case StyleCaptionSide::BottomOutside:
+        // Leave at zero.
+        break;
+      case StyleCaptionSide::Left:
+      case StyleCaptionSide::Right:
+        switch (GetCaptionVerticalAlign()) {
+          case StyleVerticalAlignKeyword::Middle:
+            aOrigin.B(aWM) = std::max(
+                0, (aCaptionSize.BSize(aWM) - aInnerSize.BSize(aWM)) / 2);
+            break;
+          case StyleVerticalAlignKeyword::Bottom:
+            aOrigin.B(aWM) =
+                std::max(0, aCaptionSize.BSize(aWM) - aInnerSize.BSize(aWM));
+            break;
+          default:
+            // Leave at zero.
+            break;
+        }
+        break;
+      case StyleCaptionSide::TopOutside:
+      case StyleCaptionSide::Top:
+        aOrigin.B(aWM) =
+            aCaptionSize.BSize(aWM) + aCaptionMargin.BStartEnd(aWM);
+        break;
+    }
   }
   return NS_OK;
 }
 
 void nsTableWrapperFrame::CreateReflowInputForInnerTable(
     nsPresContext* aPresContext, nsTableFrame* aTableFrame,
     const ReflowInput& aOuterRI, Maybe<ReflowInput>& aChildRI,
     const nscoord aAvailISize) const {
@@ -630,20 +605,21 @@ void nsTableWrapperFrame::CreateReflowIn
 
   // Use unconstrained available block-size so that the caption is always
   // fully-complete.
   const LogicalSize availSize(wm, aAvailISize, NS_UNCONSTRAINEDSIZE);
   aChildRI.emplace(aPresContext, aOuterRI, aCaptionFrame, availSize);
 
   // See if we need to reset mIsTopOfPage flag.
   if (aChildRI->mFlags.mIsTopOfPage) {
-    uint8_t captionSide = GetCaptionSide();
-    if (captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM ||
-        captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE) {
-      aChildRI->mFlags.mIsTopOfPage = false;
+    if (auto captionSide = GetCaptionSide()) {
+      if (*captionSide == StyleCaptionSide::Bottom ||
+          *captionSide == StyleCaptionSide::BottomOutside) {
+        aChildRI->mFlags.mIsTopOfPage = false;
+      }
     }
   }
 }
 
 void nsTableWrapperFrame::ReflowChild(nsPresContext* aPresContext,
                                       nsIFrame* aChildFrame,
                                       const ReflowInput& aChildRI,
                                       ReflowOutput& aMetrics,
@@ -708,38 +684,40 @@ void nsTableWrapperFrame::Reflow(nsPresC
     origCaptionRect = mCaptionFrames.FirstChild()->GetRect();
     origCaptionInkOverflow = mCaptionFrames.FirstChild()->InkOverflowRect();
     captionFirstReflow =
         mCaptionFrames.FirstChild()->HasAnyStateBits(NS_FRAME_FIRST_REFLOW);
   }
 
   // ComputeAutoSize has to match this logic.
   WritingMode wm = aOuterRI.GetWritingMode();
-  uint8_t captionSide = GetCaptionSide();
+  Maybe<StyleCaptionSide> captionSide = GetCaptionSide();
   WritingMode captionWM = wm;  // will be changed below if necessary
   const nscoord contentBoxISize = aOuterRI.ComputedSize(wm).ISize(wm);
 
-  if (captionSide == NO_SIDE) {
+  MOZ_ASSERT(mCaptionFrames.NotEmpty() == captionSide.isSome());
+
+  if (!captionSide) {
     // We don't have a caption.
     CreateReflowInputForInnerTable(aPresContext, InnerTableFrame(), aOuterRI,
                                    innerRI, contentBoxISize);
-  } else if (captionSide == NS_STYLE_CAPTION_SIDE_LEFT ||
-             captionSide == NS_STYLE_CAPTION_SIDE_RIGHT) {
+  } else if (*captionSide == StyleCaptionSide::Left ||
+             *captionSide == StyleCaptionSide::Right) {
     // ComputeAutoSize takes care of making side captions small. Compute
     // the caption's size first, and tell the table to fit in what's left.
     CreateReflowInputForCaption(aPresContext, mCaptionFrames.FirstChild(),
                                 aOuterRI, captionRI, contentBoxISize);
     captionWM = captionRI->GetWritingMode();
     nscoord innerAvailISize =
         contentBoxISize -
         captionRI->ComputedSizeWithMarginBorderPadding(wm).ISize(wm);
     CreateReflowInputForInnerTable(aPresContext, InnerTableFrame(), aOuterRI,
                                    innerRI, innerAvailISize);
-  } else if (captionSide == NS_STYLE_CAPTION_SIDE_TOP ||
-             captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) {
+  } else if (*captionSide == StyleCaptionSide::Top ||
+             *captionSide == StyleCaptionSide::Bottom) {
     // Compute the table's size first, and then prevent the caption from
     // being larger in the inline dir unless it has to be.
     //
     // Note that CSS 2.1 (but not 2.0) says:
     //   The width of the anonymous box is the border-edge width of the
     //   table box inside it
     // We don't actually make our anonymous box that isize (if we did,
     // it would break 'auto' margins), but this effectively does that.
@@ -751,33 +729,33 @@ void nsTableWrapperFrame::Reflow(nsPresC
     // neither are auto).  (We take advantage of that later when we call
     // GetCaptionOrigin, though.)
     nscoord innerBorderISize =
         innerRI->ComputedSizeWithBorderPadding(wm).ISize(wm);
     CreateReflowInputForCaption(aPresContext, mCaptionFrames.FirstChild(),
                                 aOuterRI, captionRI, innerBorderISize);
     captionWM = captionRI->GetWritingMode();
   } else {
-    NS_ASSERTION(captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE ||
-                     captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE,
-                 "unexpected caption-side");
+    MOZ_ASSERT(*captionSide == StyleCaptionSide::TopOutside ||
+                   *captionSide == StyleCaptionSide::BottomOutside,
+               "unexpected caption-side");
     // Size the table and the caption independently.
     captionWM = mCaptionFrames.FirstChild()->GetWritingMode();
     CreateReflowInputForCaption(
         aPresContext, mCaptionFrames.FirstChild(), aOuterRI, captionRI,
         aOuterRI.ComputedSize(captionWM).ISize(captionWM));
     CreateReflowInputForInnerTable(aPresContext, InnerTableFrame(), aOuterRI,
                                    innerRI, contentBoxISize);
   }
 
   // First reflow the caption.
   Maybe<ReflowOutput> captionMet;
   LogicalSize captionSize(wm);
   LogicalMargin captionMargin(wm);
-  if (mCaptionFrames.NotEmpty()) {
+  if (captionSide) {
     captionMet.emplace(wm);
     // We intentionally don't merge capStatus into aStatus, since we currently
     // can't handle caption continuations, but we probably should.
     nsReflowStatus capStatus;
     ReflowChild(aPresContext, mCaptionFrames.FirstChild(), *captionRI,
                 *captionMet, capStatus);
     captionSize.ISize(wm) = captionMet->ISize(wm);
     captionSize.BSize(wm) = captionMet->BSize(wm);
@@ -785,25 +763,25 @@ void nsTableWrapperFrame::Reflow(nsPresC
     // Now that we know the bsize of the caption, reduce the available bsize
     // for the table frame if we are bsize constrained and the caption is above
     // or below the inner table.  Also reduce the CB size that we store for
     // our children in case we're a grid item, by the same amount.
     LogicalSize* cbSize = GetProperty(GridItemCBSizeProperty());
     if (NS_UNCONSTRAINEDSIZE != aOuterRI.AvailableBSize() || cbSize) {
       nscoord captionBSize = 0;
       nscoord captionISize = 0;
-      switch (captionSide) {
-        case NS_STYLE_CAPTION_SIDE_TOP:
-        case NS_STYLE_CAPTION_SIDE_BOTTOM:
-        case NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE:
-        case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE:
+      switch (*captionSide) {
+        case StyleCaptionSide::Top:
+        case StyleCaptionSide::Bottom:
+        case StyleCaptionSide::TopOutside:
+        case StyleCaptionSide::BottomOutside:
           captionBSize = captionSize.BSize(wm) + captionMargin.BStartEnd(wm);
           break;
-        case NS_STYLE_CAPTION_SIDE_LEFT:
-        case NS_STYLE_CAPTION_SIDE_RIGHT:
+        case StyleCaptionSide::Left:
+        case StyleCaptionSide::Right:
           captionISize = captionSize.ISize(wm) + captionMargin.IStartEnd(wm);
           break;
       }
       if (NS_UNCONSTRAINEDSIZE != aOuterRI.AvailableBSize()) {
         innerRI->AvailableBSize() =
             std::max(0, innerRI->AvailableBSize() - captionBSize);
       }
       if (cbSize) {
@@ -845,19 +823,20 @@ void nsTableWrapperFrame::Reflow(nsPresC
   desiredSize.BSize(wm) =
       ComputeFinalBSize(captionSide, innerSize, captionSize, captionMargin, wm);
 
   aDesiredSize.SetSize(wm, desiredSize);
   nsSize containerSize = aDesiredSize.PhysicalSize();
   // XXX It's possible for this to be NS_UNCONSTRAINEDSIZE, which will result
   // in assertions from FinishReflowChild.
 
+  MOZ_ASSERT(mCaptionFrames.NotEmpty() == captionSide.isSome());
   if (mCaptionFrames.NotEmpty()) {
     LogicalPoint captionOrigin(wm);
-    GetCaptionOrigin(captionSide, containSize, innerSize, captionSize,
+    GetCaptionOrigin(*captionSide, containSize, innerSize, captionSize,
                      captionMargin, captionOrigin, wm);
     FinishReflowChild(mCaptionFrames.FirstChild(), aPresContext, *captionMet,
                       captionRI.ptr(), wm, captionOrigin, containerSize,
                       ReflowChildFlags::Default);
     captionRI.reset();
   }
   // XXX If the bsize is constrained then we need to check whether
   // everything still fits...
--- a/layout/tables/nsTableWrapperFrame.h
+++ b/layout/tables/nsTableWrapperFrame.h
@@ -186,44 +186,51 @@ class nsTableWrapperFrame : public nsCon
                                       mozilla::LogicalSize);
 
  protected:
   explicit nsTableWrapperFrame(ComputedStyle* aStyle,
                                nsPresContext* aPresContext,
                                ClassID aID = kClassID);
   virtual ~nsTableWrapperFrame();
 
-  // Get a NS_STYLE_CAPTION_SIDE_* value, or NO_SIDE if no caption is present.
+  using MaybeCaptionSide = Maybe<mozilla::StyleCaptionSide>;
+
+  // Get a StyleCaptionSide value, or Nothing if no caption is present.
+  //
   // (Remember that caption-side values are interpreted logically, despite
   // having "physical" names.)
-  uint8_t GetCaptionSide() const;
+  MaybeCaptionSide GetCaptionSide() const;
 
   bool HasSideCaption() const {
-    uint8_t captionSide = GetCaptionSide();
-    return captionSide == NS_STYLE_CAPTION_SIDE_LEFT ||
-           captionSide == NS_STYLE_CAPTION_SIDE_RIGHT;
+    auto captionSide = GetCaptionSide();
+    return captionSide && IsSideCaption(*captionSide);
+  }
+
+  static bool IsSideCaption(const mozilla::StyleCaptionSide aCaptionSide) {
+    return aCaptionSide == mozilla::StyleCaptionSide::Left ||
+           aCaptionSide == mozilla::StyleCaptionSide::Right;
   }
 
   mozilla::StyleVerticalAlignKeyword GetCaptionVerticalAlign() const;
 
-  nscoord ComputeFinalBSize(uint8_t aCaptionSide,
+  nscoord ComputeFinalBSize(const MaybeCaptionSide&,
                             const mozilla::LogicalSize& aInnerSize,
                             const mozilla::LogicalSize& aCaptionSize,
                             const mozilla::LogicalMargin& aCaptionMargin,
                             const mozilla::WritingMode aWM) const;
 
-  nsresult GetCaptionOrigin(uint32_t aCaptionSide,
+  nsresult GetCaptionOrigin(mozilla::StyleCaptionSide,
                             const mozilla::LogicalSize& aContainBlockSize,
                             const mozilla::LogicalSize& aInnerSize,
                             const mozilla::LogicalSize& aCaptionSize,
                             mozilla::LogicalMargin& aCaptionMargin,
                             mozilla::LogicalPoint& aOrigin,
                             mozilla::WritingMode aWM);
 
-  nsresult GetInnerOrigin(uint32_t aCaptionSide,
+  nsresult GetInnerOrigin(const MaybeCaptionSide&,
                           const mozilla::LogicalSize& aContainBlockSize,
                           const mozilla::LogicalSize& aCaptionSize,
                           const mozilla::LogicalMargin& aCaptionMargin,
                           const mozilla::LogicalSize& aInnerSize,
                           mozilla::LogicalPoint& aOrigin,
                           mozilla::WritingMode aWM);
 
   // Create and init the child reflow input, using passed-in aChildRI, so that
--- a/servo/components/style/properties/data.py
+++ b/servo/components/style/properties/data.py
@@ -398,16 +398,17 @@ class Longhand(object):
                 "AlignSelf",
                 "Appearance",
                 "AspectRatio",
                 "BreakBetween",
                 "BreakWithin",
                 "BackgroundRepeat",
                 "BorderImageRepeat",
                 "BorderStyle",
+                "table::CaptionSide",
                 "Clear",
                 "ColumnCount",
                 "Contain",
                 "Display",
                 "FillRule",
                 "Float",
                 "FontSizeAdjust",
                 "FontStretch",
--- a/servo/components/style/properties/longhands/inherited_table.mako.rs
+++ b/servo/components/style/properties/longhands/inherited_table.mako.rs
@@ -21,30 +21,30 @@
     "show hide",
     engines="gecko servo-2013",
     gecko_enum_prefix="StyleEmptyCells",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-tables/#propdef-empty-cells",
     servo_restyle_damage="rebuild_and_reflow",
 )}
 
-${helpers.single_keyword(
+${helpers.predefined_type(
     "caption-side",
-    "top bottom",
+    "table::CaptionSide",
+    "computed::table::CaptionSide::Top",
+    needs_context=False,
     engines="gecko servo-2013",
-    extra_gecko_values="right left top-outside bottom-outside",
-    needs_conversion="True",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-tables/#propdef-caption-side",
     servo_restyle_damage="rebuild_and_reflow",
 )}
 
 ${helpers.predefined_type(
     "border-spacing",
     "BorderSpacing",
     "computed::BorderSpacing::zero()",
     engines="gecko servo-2013 servo-2020",
     servo_2020_pref="layout.2020.unimplemented",
     animation_value_type="BorderSpacing",
     boxed=True,
     spec="https://drafts.csswg.org/css-tables/#propdef-border-spacing",
-    servo_restyle_damage = "reflow",
+    servo_restyle_damage="reflow",
 )}
--- a/servo/components/style/values/computed/mod.rs
+++ b/servo/components/style/values/computed/mod.rs
@@ -119,16 +119,17 @@ pub mod list;
 pub mod motion;
 pub mod outline;
 pub mod page;
 pub mod percentage;
 pub mod position;
 pub mod rect;
 pub mod resolution;
 pub mod svg;
+pub mod table;
 pub mod text;
 pub mod time;
 pub mod transform;
 pub mod ui;
 pub mod url;
 
 /// A `Context` is all the data a specified value could ever need to compute
 /// itself and be transformed to a computed value.
new file mode 100644
--- /dev/null
+++ b/servo/components/style/values/computed/table.rs
@@ -0,0 +1,7 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Computed types for CSS values related to tables.
+
+pub use super::specified::table::CaptionSide;
--- a/servo/components/style/values/specified/mod.rs
+++ b/servo/components/style/values/specified/mod.rs
@@ -124,16 +124,17 @@ pub mod outline;
 pub mod percentage;
 pub mod page;
 pub mod position;
 pub mod rect;
 pub mod resolution;
 pub mod source_size_list;
 pub mod svg;
 pub mod svg_path;
+pub mod table;
 pub mod text;
 pub mod time;
 pub mod transform;
 pub mod ui;
 pub mod url;
 
 /// <angle> | <percentage>
 /// https://drafts.csswg.org/css-values/#typedef-angle-percentage
new file mode 100644
--- /dev/null
+++ b/servo/components/style/values/specified/table.rs
@@ -0,0 +1,45 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Specified types for CSS values related to tables.
+
+/// Specified values for the `caption-side` property.
+///
+/// Note that despite having "physical" names, these are actually interpreted
+/// according to the table's writing-mode: Top and Bottom are treated as
+/// block-start and -end respectively, and Left and Right are treated as
+/// line-start and -end.
+///
+/// https://drafts.csswg.org/css-tables/#propdef-caption-side
+#[allow(missing_docs)]
+#[derive(
+    Clone,
+    Copy,
+    Debug,
+    Eq,
+    FromPrimitive,
+    MallocSizeOf,
+    Ord,
+    Parse,
+    PartialEq,
+    PartialOrd,
+    SpecifiedValueInfo,
+    ToComputedValue,
+    ToCss,
+    ToResolvedValue,
+    ToShmem,
+)]
+#[repr(u8)]
+pub enum CaptionSide {
+    Top,
+    Bottom,
+    #[cfg(feature = "gecko")]
+    Right,
+    #[cfg(feature = "gecko")]
+    Left,
+    #[cfg(feature = "gecko")]
+    TopOutside,
+    #[cfg(feature = "gecko")]
+    BottomOutside,
+}
--- a/servo/ports/geckolib/cbindgen.toml
+++ b/servo/ports/geckolib/cbindgen.toml
@@ -74,16 +74,17 @@ exclude = [
   "NS_LogDtor",
 ]
 include = [
   "Appearance",
   "BreakBetween",
   "BreakWithin",
   "BorderStyle",
   "OutlineStyle",
+  "CaptionSide",
   "ComputedFontStretchRange",
   "ComputedFontStyleDescriptor",
   "ComputedFontWeightRange",
   "ComputedTimingFunction",
   "ComputedValueFlags",
   "ContrastPref",
   "CursorKind",
   "DisplayOutside",
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -4861,17 +4861,17 @@ pub extern "C" fn Servo_DeclarationBlock
     property: nsCSSPropertyID,
     value: i32,
 ) {
     use num_traits::FromPrimitive;
     use style::properties::longhands;
     use style::properties::PropertyDeclaration;
     use style::values::generics::box_::{VerticalAlign, VerticalAlignKeyword};
     use style::values::generics::font::FontStyle;
-    use style::values::specified::{BorderStyle, Clear, Display, Float, TextAlign};
+    use style::values::specified::{BorderStyle, Clear, Display, Float, TextAlign, table::CaptionSide};
 
     fn get_from_computed<T>(value: u32) -> T
     where
         T: ToComputedValue,
         T::ComputedValue: FromPrimitive,
     {
         T::from_computed_value(&T::ComputedValue::from_u32(value).unwrap())
     }
@@ -4902,17 +4902,17 @@ pub extern "C" fn Servo_DeclarationBlock
 
             ToComputedValue::from_computed_value(&val)
         },
         FontWeight => longhands::font_weight::SpecifiedValue::from_gecko_keyword(value),
         ListStyleType => Box::new(longhands::list_style_type::SpecifiedValue::from_gecko_keyword(value)),
         MathStyle => longhands::math_style::SpecifiedValue::from_gecko_keyword(value),
         MozMathVariant => longhands::_moz_math_variant::SpecifiedValue::from_gecko_keyword(value),
         WhiteSpace => longhands::white_space::SpecifiedValue::from_gecko_keyword(value),
-        CaptionSide => longhands::caption_side::SpecifiedValue::from_gecko_keyword(value),
+        CaptionSide => get_from_computed::<CaptionSide>(value),
         BorderTopStyle => get_from_computed::<BorderStyle>(value),
         BorderRightStyle => get_from_computed::<BorderStyle>(value),
         BorderBottomStyle => get_from_computed::<BorderStyle>(value),
         BorderLeftStyle => get_from_computed::<BorderStyle>(value),
     };
     write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
         decls.push(prop, Importance::Normal);
     })