Bug 1509717 - Use cbindgen for border-style and outline-style. r=heycam
authorEmilio Cobos Álvarez <emilio@crisal.io>
Fri, 30 Nov 2018 05:27:28 +0000
changeset 505372 c5b713000513a2cdc1fdbc70aeb8f7d78bd687b2
parent 505371 70030bab1073c89c41311368a224178257cb9068
child 505381 58a0412e15574f063cd380517a0369bfb48b22e0
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersheycam
bugs1509717
milestone65.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 1509717 - Use cbindgen for border-style and outline-style. r=heycam I'm pretty sure the FIXME I left in the outline-style code is a bug, but I want to clean this up further and I didn't want to fix it without adding a test. Differential Revision: https://phabricator.services.mozilla.com/D12859
layout/generic/nsFrame.cpp
layout/painting/nsCSSRendering.cpp
layout/painting/nsDisplayList.cpp
layout/style/ServoBindings.toml
layout/style/ServoCSSPropList.mako.py
layout/style/nsCSSProps.cpp
layout/style/nsCSSProps.h
layout/style/nsComputedDOMStyle.cpp
layout/style/nsComputedDOMStyle.h
layout/style/nsStyleConsts.h
layout/style/nsStyleStruct.cpp
layout/style/nsStyleStruct.h
servo/components/style/cbindgen.toml
servo/components/style/properties/gecko.mako.rs
servo/components/style/properties/longhands/column.mako.rs
servo/components/style/values/specified/border.rs
servo/components/style/values/specified/outline.rs
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -9461,17 +9461,17 @@ ComputeAndIncludeOutlineArea(nsIFrame* a
 
   // Keep this code in sync with GetOutlineInnerRect in nsCSSRendering.cpp.
   aFrame->SetProperty(nsIFrame::OutlineInnerRectProperty(),
                            new nsRect(innerRect));
   const nscoord offset = outline->mOutlineOffset;
   nsRect outerRect(innerRect);
   bool useOutlineAuto = false;
   if (nsLayoutUtils::IsOutlineStyleAutoEnabled()) {
-    useOutlineAuto = outline->mOutlineStyle == StyleBorderStyle::Auto;
+    useOutlineAuto = outline->mOutlineStyle.IsAuto();
     if (MOZ_UNLIKELY(useOutlineAuto)) {
       nsPresContext* presContext = aFrame->PresContext();
       nsITheme* theme = presContext->GetTheme();
       if (theme && theme->ThemeSupportsWidget(presContext, aFrame,
                                               StyleAppearance::FocusOutline)) {
         outerRect.Inflate(offset);
         theme->GetWidgetOverflow(presContext->DeviceContext(), aFrame,
                                  StyleAppearance::FocusOutline, &outerRect);
--- a/layout/painting/nsCSSRendering.cpp
+++ b/layout/painting/nsCSSRendering.cpp
@@ -1148,18 +1148,18 @@ nsCSSRendering::CreateBorderRendererForO
   // get the outer rectangles
   Rect oRect(NSRectToRect(outerRect, oneDevPixel));
 
   // convert the radii
   nsMargin outlineMargin(width, width, width, width);
   RectCornerRadii outlineRadii;
   ComputePixelRadii(twipsRadii, oneDevPixel, &outlineRadii);
 
-  StyleBorderStyle outlineStyle = ourOutline->mOutlineStyle;
-  if (outlineStyle == StyleBorderStyle::Auto) {
+  StyleBorderStyle outlineStyle;
+  if (ourOutline->mOutlineStyle.IsAuto()) {
     if (nsLayoutUtils::IsOutlineStyleAutoEnabled()) {
       nsITheme* theme = aPresContext->GetTheme();
       if (theme && theme->ThemeSupportsWidget(
                      aPresContext, aForFrame, StyleAppearance::FocusOutline)) {
         theme->DrawWidgetBackground(aRenderingContext,
                                     aForFrame,
                                     StyleAppearance::FocusOutline,
                                     innerRect,
@@ -1168,16 +1168,19 @@ nsCSSRendering::CreateBorderRendererForO
       }
     }
     if (width == 0) {
       return Nothing(); // empty outline
     }
     // http://dev.w3.org/csswg/css-ui/#outline
     // "User agents may treat 'auto' as 'solid'."
     outlineStyle = StyleBorderStyle::Solid;
+  } else {
+    MOZ_ASSERT(ourOutline->mOutlineStyle.IsBorderStyle());
+    outlineStyle = ourOutline->mOutlineStyle.border_style._0;
   }
 
   StyleBorderStyle outlineStyles[4] = {
     outlineStyle, outlineStyle, outlineStyle, outlineStyle
   };
 
   // This handles treating the initial color as 'currentColor'; if we
   // ever want 'invert' back we'll need to do a bit of work here too.
@@ -4093,19 +4096,16 @@ nsCSSRendering::GetTableBorderSolidSegme
       { aStartBevelSide, aStartBevelOffset },
       { aEndBevelSide, aEndBevelOffset }
     });
     break;
   case StyleBorderStyle::Outset:
   case StyleBorderStyle::Inset:
     MOZ_ASSERT_UNREACHABLE("inset, outset should have been converted to groove, ridge");
     break;
-  case StyleBorderStyle::Auto:
-    MOZ_ASSERT_UNREACHABLE("Unexpected 'auto' table border");
-    break;
   }
 }
 
 // End table border-collapsing section
 
 Rect
 nsCSSRendering::ExpandPaintingRectForDecorationLine(nsIFrame* aFrame,
                                                     const uint8_t aStyle,
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -5338,18 +5338,18 @@ nsDisplayOutline::CreateWebRenderCommand
   mozilla::wr::DisplayListBuilder& aBuilder,
   mozilla::wr::IpcResourceUpdateQueue& aResources,
   const StackingContextHelper& aSc,
   mozilla::layers::WebRenderLayerManager* aManager,
   nsDisplayListBuilder* aDisplayListBuilder)
 {
   ContainerLayerParameters parameter;
 
-  StyleBorderStyle outlineStyle = mFrame->Style()->StyleOutline()->mOutlineStyle;
-  if (outlineStyle == StyleBorderStyle::Auto &&
+  const auto& outlineStyle = mFrame->StyleOutline()->mOutlineStyle;
+  if (outlineStyle.IsAuto() &&
       nsLayoutUtils::IsOutlineStyleAutoEnabled()) {
     nsITheme* theme = mFrame->PresContext()->GetTheme();
     if (theme && theme->ThemeSupportsWidget(mFrame->PresContext(),
                                             mFrame,
                                             StyleAppearance::FocusOutline)) {
       return false;
     }
   }
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -396,16 +396,18 @@ cbindgen-types = [
     { gecko = "StyleFontFaceSourceListComponent", servo = "font_face::FontFaceSourceListComponent" },
     { gecko = "StyleFontLanguageOverride", servo = "values::computed::font::FontLanguageOverride" },
     { gecko = "StylePathCommand", servo = "values::specified::svg_path::PathCommand" },
     { gecko = "StyleUnicodeRange", servo = "cssparser::UnicodeRange" },
     { gecko = "StyleOverflowWrap", servo = "values::computed::OverflowWrap" },
     { gecko = "StyleUserSelect", servo = "values::computed::UserSelect" },
     { gecko = "StyleBreakBetween", servo = "values::computed::BreakBetween" },
     { gecko = "StyleBreakWithin", servo = "values::computed::BreakWithin" },
+    { gecko = "StyleBorderStyle", servo = "values::computed::BorderStyle" },
+    { gecko = "StyleOutlineStyle", servo = "values::computed::OutlineStyle" },
 ]
 
 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/ServoCSSPropList.mako.py
+++ b/layout/style/ServoCSSPropList.mako.py
@@ -65,16 +65,17 @@ def method(prop):
 # TODO(emilio): This will go away once the rest of the longhands have been
 # moved or perhaps using a blacklist for the ones with non-layout-dependence
 # but other non-trivial dependence like scrollbar colors.
 SERIALIZED_PREDEFINED_TYPES = [
     "Appearance",
     "BackgroundRepeat",
     "BackgroundSize",
     "BorderImageRepeat",
+    "BorderStyle",
     "BreakBetween",
     "BreakWithin",
     "Clear",
     "ClipRectOrAuto",
     "Color",
     "Content",
     "CounterIncrement",
     "CounterReset",
@@ -99,16 +100,17 @@ SERIALIZED_PREDEFINED_TYPES = [
     "ImageLayer",
     "Length",
     "LengthOrPercentage",
     "NonNegativeLength",
     "NonNegativeLengthOrPercentage",
     "ListStyleType",
     "OffsetPath",
     "Opacity",
+    "OutlineStyle",
     "OverflowWrap",
     "Position",
     "Quotes",
     "Resize",
     "Rotate",
     "Scale",
     "TextAlign",
     "Translate",
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -183,30 +183,16 @@ nsCSSProps::GetStringValue(nsCSSCounterD
   } else {
     static nsDependentCString sNullStr("");
     return sNullStr;
   }
 }
 
 /***************************************************************************/
 
-const KTableEntry nsCSSProps::kBorderStyleKTable[] = {
-  { eCSSKeyword_none,   StyleBorderStyle::None },
-  { eCSSKeyword_hidden, StyleBorderStyle::Hidden },
-  { eCSSKeyword_dotted, StyleBorderStyle::Dotted },
-  { eCSSKeyword_dashed, StyleBorderStyle::Dashed },
-  { eCSSKeyword_solid,  StyleBorderStyle::Solid },
-  { eCSSKeyword_double, StyleBorderStyle::Double },
-  { eCSSKeyword_groove, StyleBorderStyle::Groove },
-  { eCSSKeyword_ridge,  StyleBorderStyle::Ridge },
-  { eCSSKeyword_inset,  StyleBorderStyle::Inset },
-  { eCSSKeyword_outset, StyleBorderStyle::Outset },
-  { eCSSKeyword_UNKNOWN, -1 }
-};
-
 const KTableEntry nsCSSProps::kBoxShadowTypeKTable[] = {
   { eCSSKeyword_inset, uint8_t(StyleBoxShadowType::Inset) },
   { eCSSKeyword_UNKNOWN, -1 }
 };
 
 const KTableEntry nsCSSProps::kCursorKTable[] = {
   // CSS 2.0
   { eCSSKeyword_auto, NS_STYLE_CURSOR_AUTO },
@@ -491,44 +477,16 @@ const KTableEntry nsCSSProps::kContainKT
   { eCSSKeyword_content, NS_STYLE_CONTAIN_CONTENT },
   { eCSSKeyword_layout,  NS_STYLE_CONTAIN_LAYOUT },
   { eCSSKeyword_style,   NS_STYLE_CONTAIN_STYLE },
   { eCSSKeyword_paint,   NS_STYLE_CONTAIN_PAINT },
   { eCSSKeyword_size,    NS_STYLE_CONTAIN_SIZE },
   { eCSSKeyword_UNKNOWN, -1 }
 };
 
-// Same as kBorderStyleKTable except 'hidden'.
-const KTableEntry nsCSSProps::kOutlineStyleKTable[] = {
-  { eCSSKeyword_none,   StyleBorderStyle::None },
-  { eCSSKeyword_auto,   StyleBorderStyle::Auto },
-  { eCSSKeyword_dotted, StyleBorderStyle::Dotted },
-  { eCSSKeyword_dashed, StyleBorderStyle::Dashed },
-  { eCSSKeyword_solid,  StyleBorderStyle::Solid },
-  { eCSSKeyword_double, StyleBorderStyle::Double },
-  { eCSSKeyword_groove, StyleBorderStyle::Groove },
-  { eCSSKeyword_ridge,  StyleBorderStyle::Ridge },
-  { eCSSKeyword_inset,  StyleBorderStyle::Inset },
-  { eCSSKeyword_outset, StyleBorderStyle::Outset },
-  { eCSSKeyword_UNKNOWN, -1 }
-};
-
-const KTableEntry nsCSSProps::kOverflowKTable[] = {
-  { eCSSKeyword_auto, NS_STYLE_OVERFLOW_AUTO },
-  { eCSSKeyword_visible, NS_STYLE_OVERFLOW_VISIBLE },
-  { eCSSKeyword_hidden, NS_STYLE_OVERFLOW_HIDDEN },
-  { eCSSKeyword_scroll, NS_STYLE_OVERFLOW_SCROLL },
-  // Deprecated:
-  { eCSSKeyword__moz_scrollbars_none, NS_STYLE_OVERFLOW_HIDDEN },
-  { eCSSKeyword__moz_scrollbars_horizontal, NS_STYLE_OVERFLOW_SCROLLBARS_HORIZONTAL },
-  { eCSSKeyword__moz_scrollbars_vertical, NS_STYLE_OVERFLOW_SCROLLBARS_VERTICAL },
-  { eCSSKeyword__moz_hidden_unscrollable, NS_STYLE_OVERFLOW_CLIP },
-  { eCSSKeyword_UNKNOWN, -1 }
-};
-
 const KTableEntry nsCSSProps::kOverflowClipBoxKTable[] = {
   { eCSSKeyword_padding_box, NS_STYLE_OVERFLOW_CLIP_BOX_PADDING_BOX },
   { eCSSKeyword_content_box, NS_STYLE_OVERFLOW_CLIP_BOX_CONTENT_BOX },
   { eCSSKeyword_UNKNOWN, -1 }
 };
 
 const KTableEntry nsCSSProps::kOverflowSubKTable[] = {
   { eCSSKeyword_auto, NS_STYLE_OVERFLOW_AUTO },
--- a/layout/style/nsCSSProps.h
+++ b/layout/style/nsCSSProps.h
@@ -304,17 +304,16 @@ public:
                             es_ = (nsCSSPropertyID)((enabledstate_) |       \
                                                   CSSEnabledState(0));    \
        *it_ != eCSSProperty_UNKNOWN; ++it_)                               \
     if (nsCSSProps::IsEnabled(*it_, (mozilla::CSSEnabledState) es_))
 
   // Keyword/Enum value tables
   // Not const because we modify its entries when the pref
   // "layout.css.background-clip.text" changes:
-  static const KTableEntry kBorderStyleKTable[];
   static const KTableEntry kShapeRadiusKTable[];
   static const KTableEntry kFilterFunctionKTable[];
   static const KTableEntry kBoxShadowTypeKTable[];
   static const KTableEntry kCursorKTable[];
   // Not const because we modify its entries when various
   // "layout.css.*.enabled" prefs changes:
   static KTableEntry kDisplayKTable[];
   // -- tables for parsing the {align,justify}-{content,items,self} properties --
@@ -333,18 +332,16 @@ public:
   static const KTableEntry kAutoCompletionAlignItems[];
   static const KTableEntry kAutoCompletionAlignJustifyContent[];
   // ------------------------------------------------------------------
   static const KTableEntry kFontSmoothingKTable[];
   static const KTableEntry kGridAutoFlowKTable[];
   static const KTableEntry kGridTrackBreadthKTable[];
   static const KTableEntry kLineHeightKTable[];
   static const KTableEntry kContainKTable[];
-  static const KTableEntry kOutlineStyleKTable[];
-  static const KTableEntry kOverflowKTable[];
   static const KTableEntry kOverflowSubKTable[];
   static const KTableEntry kOverflowClipBoxKTable[];
   static const KTableEntry kOverscrollBehaviorKTable[];
   static const KTableEntry kScrollSnapTypeKTable[];
   static const KTableEntry kTextAlignKTable[];
   static const KTableEntry kTextDecorationLineKTable[];
   static const KTableEntry kTextDecorationStyleKTable[];
   static const KTableEntry kTextEmphasisStyleShapeKTable[];
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -1998,40 +1998,16 @@ nsComputedDOMStyle::DoGetBorderSpacing()
 
   valueList->AppendCSSValue(xSpacing.forget());
   valueList->AppendCSSValue(ySpacing.forget());
 
   return valueList.forget();
 }
 
 already_AddRefed<CSSValue>
-nsComputedDOMStyle::DoGetBorderTopStyle()
-{
-  return GetBorderStyleFor(eSideTop);
-}
-
-already_AddRefed<CSSValue>
-nsComputedDOMStyle::DoGetBorderBottomStyle()
-{
-  return GetBorderStyleFor(eSideBottom);
-}
-
-already_AddRefed<CSSValue>
-nsComputedDOMStyle::DoGetBorderLeftStyle()
-{
-  return GetBorderStyleFor(eSideLeft);
-}
-
-already_AddRefed<CSSValue>
-nsComputedDOMStyle::DoGetBorderRightStyle()
-{
-  return GetBorderStyleFor(eSideRight);
-}
-
-already_AddRefed<CSSValue>
 nsComputedDOMStyle::DoGetBorderBottomLeftRadius()
 {
   return GetEllipseRadii(StyleBorder()->mBorderRadius,
                          eCornerBottomLeft);
 }
 
 already_AddRefed<CSSValue>
 nsComputedDOMStyle::DoGetBorderBottomRightRadius()
@@ -2196,38 +2172,17 @@ nsComputedDOMStyle::DoGetScrollbarColor(
   put(ui->mScrollbarTrackColor);
   return list.forget();
 }
 
 already_AddRefed<CSSValue>
 nsComputedDOMStyle::DoGetOutlineWidth()
 {
   RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
-
-  const nsStyleOutline* outline = StyleOutline();
-
-  nscoord width;
-  if (outline->mOutlineStyle == StyleBorderStyle::None) {
-    NS_ASSERTION(outline->GetOutlineWidth() == 0, "unexpected width");
-    width = 0;
-  } else {
-    width = outline->GetOutlineWidth();
-  }
-  val->SetAppUnits(width);
-
-  return val.forget();
-}
-
-already_AddRefed<CSSValue>
-nsComputedDOMStyle::DoGetOutlineStyle()
-{
-  RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
-  val->SetIdent(
-    nsCSSProps::ValueToKeywordEnum(StyleOutline()->mOutlineStyle,
-                                   nsCSSProps::kOutlineStyleKTable));
+  val->SetAppUnits(StyleOutline()->GetOutlineWidth());
   return val.forget();
 }
 
 already_AddRefed<CSSValue>
 nsComputedDOMStyle::DoGetOutlineRadiusBottomLeft()
 {
   return GetEllipseRadii(StyleOutline()->mOutlineRadius,
                          eCornerBottomLeft);
@@ -3402,26 +3357,16 @@ nsComputedDOMStyle::GetMarginWidthFor(mo
     NS_ASSERTION(mOuterFrame == mInnerFrame ||
                  mInnerFrame->GetUsedMargin() == nsMargin(0, 0, 0, 0),
                  "Inner tables must have zero margins");
   }
 
   return val.forget();
 }
 
-already_AddRefed<CSSValue>
-nsComputedDOMStyle::GetBorderStyleFor(mozilla::Side aSide)
-{
-  RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
-  val->SetIdent(
-    nsCSSProps::ValueToKeywordEnum(StyleBorder()->GetBorderStyle(aSide),
-                                   nsCSSProps::kBorderStyleKTable));
-  return val.forget();
-}
-
 void
 nsComputedDOMStyle::SetValueToCoord(nsROCSSPrimitiveValue* aValue,
                                     const nsStyleCoord& aCoord,
                                     bool aClampNegativeCalc,
                                     PercentageBaseGetter aPercentageBaseGetter,
                                     const KTableEntry aTable[],
                                     nscoord aMinAppUnits,
                                     nscoord aMaxAppUnits)
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -284,20 +284,16 @@ private:
   already_AddRefed<CSSValue> DoGetPaddingLeft();
   already_AddRefed<CSSValue> DoGetPaddingRight();
 
   /* Table Properties */
   already_AddRefed<CSSValue> DoGetBorderSpacing();
   already_AddRefed<CSSValue> DoGetVerticalAlign();
 
   /* Border Properties */
-  already_AddRefed<CSSValue> DoGetBorderTopStyle();
-  already_AddRefed<CSSValue> DoGetBorderBottomStyle();
-  already_AddRefed<CSSValue> DoGetBorderLeftStyle();
-  already_AddRefed<CSSValue> DoGetBorderRightStyle();
   already_AddRefed<CSSValue> DoGetBorderTopWidth();
   already_AddRefed<CSSValue> DoGetBorderBottomWidth();
   already_AddRefed<CSSValue> DoGetBorderLeftWidth();
   already_AddRefed<CSSValue> DoGetBorderRightWidth();
   already_AddRefed<CSSValue> DoGetBorderBottomLeftRadius();
   already_AddRefed<CSSValue> DoGetBorderBottomRightRadius();
   already_AddRefed<CSSValue> DoGetBorderTopLeftRadius();
   already_AddRefed<CSSValue> DoGetBorderTopRightRadius();
@@ -313,17 +309,16 @@ private:
   /* Margin Properties */
   already_AddRefed<CSSValue> DoGetMarginTopWidth();
   already_AddRefed<CSSValue> DoGetMarginBottomWidth();
   already_AddRefed<CSSValue> DoGetMarginLeftWidth();
   already_AddRefed<CSSValue> DoGetMarginRightWidth();
 
   /* Outline Properties */
   already_AddRefed<CSSValue> DoGetOutlineWidth();
-  already_AddRefed<CSSValue> DoGetOutlineStyle();
   already_AddRefed<CSSValue> DoGetOutlineRadiusBottomLeft();
   already_AddRefed<CSSValue> DoGetOutlineRadiusBottomRight();
   already_AddRefed<CSSValue> DoGetOutlineRadiusTopLeft();
   already_AddRefed<CSSValue> DoGetOutlineRadiusTopRight();
 
   /* z-index */
   already_AddRefed<CSSValue> DoGetZIndex();
 
--- a/layout/style/nsStyleConsts.h
+++ b/layout/style/nsStyleConsts.h
@@ -295,30 +295,16 @@ enum class StyleImageLayerRepeat : uint8
 #define NS_STYLE_MASK_MODE_ALPHA                0
 #define NS_STYLE_MASK_MODE_LUMINANCE            1
 #define NS_STYLE_MASK_MODE_MATCH_SOURCE         2
 
 // See nsStyleTable
 #define NS_STYLE_BORDER_COLLAPSE                0
 #define NS_STYLE_BORDER_SEPARATE                1
 
-enum class StyleBorderStyle : uint8_t {
-  None,
-  Groove,
-  Ridge,
-  Dotted,
-  Dashed,
-  Solid,
-  Double,
-  Inset,
-  Outset,
-  Hidden,
-  Auto,     // for outline-style only
-};
-
 // border-image-repeat
 enum class StyleBorderImageRepeat : uint8_t {
   Stretch,
   Repeat,
   Round,
   Space
 };
 
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -444,17 +444,17 @@ nsStyleBorder::CalcDifference(const nsSt
 
   return nsChangeHint(0);
 }
 
 nsStyleOutline::nsStyleOutline(const nsPresContext* aContext)
   : mOutlineWidth(kMediumBorderWidth)
   , mOutlineOffset(0)
   , mOutlineColor(StyleComplexColor::CurrentColor())
-  , mOutlineStyle(StyleBorderStyle::None)
+  , mOutlineStyle(StyleOutlineStyle::BorderStyle(StyleBorderStyle::None))
   , mActualOutlineWidth(0)
   , mTwipsPerPixel(aContext->DevPixelsToAppUnits(1))
 {
   MOZ_COUNT_CTOR(nsStyleOutline);
   // spacing values not inherited
   nsStyleCoord zero(0, nsStyleCoord::CoordConstructor);
   NS_FOR_CSS_HALF_CORNERS(corner) {
     mOutlineRadius.Set(corner, zero);
@@ -468,27 +468,16 @@ nsStyleOutline::nsStyleOutline(const nsS
   , mOutlineColor(aSrc.mOutlineColor)
   , mOutlineStyle(aSrc.mOutlineStyle)
   , mActualOutlineWidth(aSrc.mActualOutlineWidth)
   , mTwipsPerPixel(aSrc.mTwipsPerPixel)
 {
   MOZ_COUNT_CTOR(nsStyleOutline);
 }
 
-void
-nsStyleOutline::RecalcData()
-{
-  if (StyleBorderStyle::None == mOutlineStyle) {
-    mActualOutlineWidth = 0;
-  } else {
-    mActualOutlineWidth =
-      NS_ROUND_BORDER_TO_PIXELS(mOutlineWidth, mTwipsPerPixel);
-  }
-}
-
 nsChangeHint
 nsStyleOutline::CalcDifference(const nsStyleOutline& aNewData) const
 {
   if (mActualOutlineWidth != aNewData.mActualOutlineWidth ||
       (mActualOutlineWidth > 0 &&
        mOutlineOffset != aNewData.mOutlineOffset)) {
     return nsChangeHint_UpdateOverflow |
            nsChangeHint_SchedulePaint |
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -1154,41 +1154,46 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
   explicit nsStyleOutline(const nsPresContext* aContext);
   nsStyleOutline(const nsStyleOutline& aOutline);
   ~nsStyleOutline() {
     MOZ_COUNT_DTOR(nsStyleOutline);
   }
   void FinishStyle(nsPresContext*, const nsStyleOutline*) {}
   const static bool kHasFinishStyle = false;
 
-  void RecalcData();
   nsChangeHint CalcDifference(const nsStyleOutline& aNewData) const;
 
   nsStyleCorners  mOutlineRadius; // coord, percent, calc
 
   // This is the specified value of outline-width, but with length values
   // computed to absolute.  mActualOutlineWidth stores the outline-width
   // value used by layout.  (We must store mOutlineWidth for the same
   // style struct resolution reasons that we do nsStyleBorder::mBorder;
   // see that field's comment.)
   nscoord       mOutlineWidth;
   nscoord       mOutlineOffset;
   mozilla::StyleComplexColor mOutlineColor;
-  mozilla::StyleBorderStyle  mOutlineStyle;  // StyleBorderStyle::*
+  mozilla::StyleOutlineStyle mOutlineStyle;
 
   nscoord GetOutlineWidth() const
   {
     return mActualOutlineWidth;
   }
 
   bool ShouldPaintOutline() const
   {
-    return mOutlineStyle == mozilla::StyleBorderStyle::Auto ||
-           (GetOutlineWidth() > 0 &&
-            mOutlineStyle != mozilla::StyleBorderStyle::None);
+    if (mOutlineStyle.IsAuto()) {
+      return true;
+    }
+    if (GetOutlineWidth() > 0) {
+      MOZ_ASSERT(mOutlineStyle.border_style._0 != mozilla::StyleBorderStyle::None,
+                 "outline-style: none implies outline-width of zero");
+      return true;
+    }
+    return false;
   }
 
 protected:
   // The actual value of outline-width is the computed value (an absolute
   // length, forced to zero when outline-style is none) rounded to device
   // pixels.  This is the value used by layout.
   nscoord       mActualOutlineWidth;
   nscoord       mTwipsPerPixel;
--- a/servo/components/style/cbindgen.toml
+++ b/servo/components/style/cbindgen.toml
@@ -26,26 +26,29 @@ language = "C++"
 namespaces = ["mozilla"]
 
 [parse]
 parse_deps = true
 include = ["cssparser"]
 
 [struct]
 derive_eq = true
+derive_neq = true
 
 [enum]
 derive_helper_methods = true
 
 [export]
 prefix = "Style"
 include = [
   "Appearance",
   "BreakBetween",
   "BreakWithin",
+  "BorderStyle",
+  "OutlineStyle",
   "ComputedFontStretchRange",
   "ComputedFontStyleDescriptor",
   "ComputedFontWeightRange",
   "ComputedTimingFunction",
   "Display",
   "DisplayMode",
   "FillRule",
   "FontDisplay",
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -53,25 +53,24 @@ use crate::properties::longhands;
 use crate::rule_tree::StrongRuleNode;
 use crate::selector_parser::PseudoElement;
 use servo_arc::{Arc, RawOffsetArc};
 use std::marker::PhantomData;
 use std::mem::{forget, uninitialized, transmute, zeroed};
 use std::{cmp, ops, ptr};
 use crate::values::{self, CustomIdent, Either, KeyframesName, None_};
 use crate::values::computed::{NonNegativeLength, Percentage, TransitionProperty};
+use crate::values::computed::BorderStyle;
 use crate::values::computed::font::FontSize;
 use crate::values::computed::effects::{BoxShadow, Filter, SimpleShadow};
-use crate::values::computed::outline::OutlineStyle;
 use crate::values::generics::column::ColumnCount;
 use crate::values::generics::position::ZIndex;
 use crate::values::generics::text::MozTabSize;
 use crate::values::generics::transform::TransformStyle;
 use crate::values::generics::url::UrlOrNone;
-use crate::computed_values::border_style;
 
 pub mod style_structs {
     % for style_struct in data.style_structs:
     pub use super::${style_struct.gecko_struct_name} as ${style_struct.name};
     % endfor
 }
 
 /// FIXME(emilio): This is completely duplicated with the other properties code.
@@ -328,23 +327,20 @@ impl ${style_struct.gecko_struct_name} {
 
 <%def name="impl_simple_clone(ident, gecko_ffi_name)">
     #[allow(non_snake_case)]
     pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
         From::from(self.gecko.${gecko_ffi_name})
     }
 </%def>
 
-<%def name="impl_simple_copy(ident, gecko_ffi_name, on_set=None, *kwargs)">
+<%def name="impl_simple_copy(ident, gecko_ffi_name, *kwargs)">
     #[allow(non_snake_case)]
     pub fn copy_${ident}_from(&mut self, other: &Self) {
         self.gecko.${gecko_ffi_name} = other.gecko.${gecko_ffi_name};
-        % if on_set:
-        self.${on_set}();
-        % endif
     }
 
     #[allow(non_snake_case)]
     pub fn reset_${ident}(&mut self, other: &Self) {
         self.copy_${ident}_from(other)
     }
 </%def>
 
@@ -363,31 +359,28 @@ impl ${style_struct.gecko_struct_name} {
 <%!
 def get_gecko_property(ffi_name, self_param = "self"):
     return "%s.gecko.%s" % (self_param, ffi_name)
 
 def set_gecko_property(ffi_name, expr):
     return "self.gecko.%s = %s;" % (ffi_name, expr)
 %>
 
-<%def name="impl_keyword_setter(ident, gecko_ffi_name, keyword, cast_type='u8', on_set=None)">
+<%def name="impl_keyword_setter(ident, gecko_ffi_name, keyword, cast_type='u8')">
     #[allow(non_snake_case)]
     pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
         use crate::properties::longhands::${ident}::computed_value::T as Keyword;
         // FIXME(bholley): Align binary representations and ditch |match| for cast + static_asserts
         let result = match v {
             % for value in keyword.values_for('gecko'):
                 Keyword::${to_camel_case(value)} =>
                     structs::${keyword.gecko_constant(value)} ${keyword.maybe_cast(cast_type)},
             % endfor
         };
         ${set_gecko_property(gecko_ffi_name, "result")}
-        % if on_set:
-        self.${on_set}();
-        % endif
     }
 </%def>
 
 <%def name="impl_keyword_clone(ident, gecko_ffi_name, keyword, cast_type='u8')">
     // FIXME: We introduced non_upper_case_globals for -moz-appearance only
     //        since the prefix of Gecko value starts with ThemeWidgetType_NS_THEME.
     //        We should remove this after fix bug 1371809.
     #[allow(non_snake_case, non_upper_case_globals)]
@@ -1510,66 +1503,75 @@ fn static_assert() {
     }
     // Note: using the above technique with an enum hits a rust bug when |structs| is in a different crate.
     % for side in SIDES:
     { const DETAIL: u32 = [0][(structs::Side::eSide${side.name} as usize != ${side.index}) as usize]; let _ = DETAIL; }
     % endfor
 }
 
 
-<% border_style_keyword = Keyword("border-style",
-                                  "none solid double dotted dashed hidden groove ridge inset outset",
-                                  gecko_enum_prefix="StyleBorderStyle",
-                                  gecko_inexhaustive=True) %>
-
 <% skip_border_longhands = " ".join(["border-{0}-{1}".format(x.ident, y)
                                      for x in SIDES
                                      for y in ["color", "style", "width"]] +
                                     ["border-{0}-radius".format(x.ident.replace("_", "-"))
                                      for x in CORNERS]) %>
 
 <%self:impl_trait style_struct_name="Border"
                   skip_longhands="${skip_border_longhands} border-image-source border-image-outset
                                   border-image-repeat border-image-width border-image-slice">
     % for side in SIDES:
-    <% impl_keyword("border_%s_style" % side.ident,
-                    "mBorderStyle[%s]" % side.index,
-                    border_style_keyword,
-                    on_set="update_border_%s" % side.ident) %>
-
-    // This is needed because the initial mComputedBorder value is set to zero.
-    //
-    // In order to compute stuff, we start from the initial struct, and keep
-    // going down the tree applying properties.
-    //
-    // That means, effectively, that when we set border-style to something
-    // non-hidden, we should use the initial border instead.
-    //
-    // Servo stores the initial border-width in the initial struct, and then
-    // adjusts as needed in the fixup phase. This means that the initial struct
-    // is technically not valid without fixups, and that you lose pretty much
-    // any sharing of the initial struct, which is kind of unfortunate.
-    //
-    // Gecko has two fields for this, one that stores the "specified" border,
-    // and other that stores the actual computed one. That means that when we
-    // set border-style, border-width may change and we need to sync back to the
-    // specified one. This is what this function does.
-    //
-    // Note that this doesn't impose any dependency in the order of computation
-    // of the properties. This is only relevant if border-style is specified,
-    // but border-width isn't. If border-width is specified at some point, the
-    // two mBorder and mComputedBorder fields would be the same already.
-    //
-    // Once we're here, we know that we'll run style fixups, so it's fine to
-    // just copy the specified border here, we'll adjust it if it's incorrect
-    // later.
-    fn update_border_${side.ident}(&mut self) {
+    pub fn set_border_${side.ident}_style(&mut self, v: BorderStyle) {
+        self.gecko.mBorderStyle[${side.index}] = v;
+
+        // This is needed because the initial mComputedBorder value is set to
+        // zero.
+        //
+        // In order to compute stuff, we start from the initial struct, and keep
+        // going down the tree applying properties.
+        //
+        // That means, effectively, that when we set border-style to something
+        // non-hidden, we should use the initial border instead.
+        //
+        // Servo stores the initial border-width in the initial struct, and then
+        // adjusts as needed in the fixup phase. This means that the initial
+        // struct is technically not valid without fixups, and that you lose
+        // pretty much any sharing of the initial struct, which is kind of
+        // unfortunate.
+        //
+        // Gecko has two fields for this, one that stores the "specified"
+        // border, and other that stores the actual computed one. That means
+        // that when we set border-style, border-width may change and we need to
+        // sync back to the specified one. This is what this function does.
+        //
+        // Note that this doesn't impose any dependency in the order of
+        // computation of the properties. This is only relevant if border-style
+        // is specified, but border-width isn't. If border-width is specified at
+        // some point, the two mBorder and mComputedBorder fields would be the
+        // same already.
+        //
+        // Once we're here, we know that we'll run style fixups, so it's fine to
+        // just copy the specified border here, we'll adjust it if it's
+        // incorrect later.
         self.gecko.mComputedBorder.${side.ident} = self.gecko.mBorder.${side.ident};
     }
 
+    pub fn copy_border_${side.ident}_style_from(&mut self, other: &Self) {
+        self.gecko.mBorderStyle[${side.index}] = other.gecko.mBorderStyle[${side.index}];
+        self.gecko.mComputedBorder.${side.ident} = self.gecko.mBorder.${side.ident};
+    }
+
+    pub fn reset_border_${side.ident}_style(&mut self, other: &Self) {
+        self.copy_border_${side.ident}_style_from(other);
+    }
+
+    #[inline]
+    pub fn clone_border_${side.ident}_style(&self) -> BorderStyle {
+        self.gecko.mBorderStyle[${side.index}]
+    }
+
     <% impl_color("border_%s_color" % side.ident, "mBorder%sColor" % side.name) %>
 
     <% impl_non_negative_length("border_%s_width" % side.ident,
                                 "mComputedBorder.%s" % side.ident,
                                 inherit_from="mBorder.%s" % side.ident,
                                 round_to_pixels=True) %>
 
     pub fn border_${side.ident}_has_nonzero_width(&self) -> bool {
@@ -2167,60 +2169,36 @@ fn static_assert() {
 </%self:impl_trait>
 
 <% skip_outline_longhands = " ".join("outline-style outline-width".split() +
                                      ["-moz-outline-radius-{0}".format(x.ident.replace("_", ""))
                                       for x in CORNERS]) %>
 <%self:impl_trait style_struct_name="Outline"
                   skip_longhands="${skip_outline_longhands}">
 
-    #[allow(non_snake_case)]
     pub fn set_outline_style(&mut self, v: longhands::outline_style::computed_value::T) {
-        // FIXME(bholley): Align binary representations and ditch |match| for
-        // cast + static_asserts
-        let result = match v {
-            % for value in border_style_keyword.values_for('gecko'):
-                OutlineStyle::Other(border_style::T::${to_camel_case(value)}) =>
-                    structs::${border_style_keyword.gecko_constant(value)} ${border_style_keyword.maybe_cast("u8")},
-            % endfor
-                OutlineStyle::Auto =>
-                    structs::${border_style_keyword.gecko_constant('auto')} ${border_style_keyword.maybe_cast("u8")},
-        };
-        ${set_gecko_property("mOutlineStyle", "result")}
-
+        self.gecko.mOutlineStyle = v;
         // NB: This is needed to correctly handling the initial value of
         // outline-width when outline-style changes, see the
         // update_border_${side.ident} comment for more details.
         self.gecko.mActualOutlineWidth = self.gecko.mOutlineWidth;
     }
 
-    #[allow(non_snake_case)]
     pub fn copy_outline_style_from(&mut self, other: &Self) {
+        // FIXME(emilio): Why doesn't this need to reset mActualOutlineWidth?
+        // Looks fishy.
         self.gecko.mOutlineStyle = other.gecko.mOutlineStyle;
     }
 
-    #[allow(non_snake_case)]
     pub fn reset_outline_style(&mut self, other: &Self) {
         self.copy_outline_style_from(other)
     }
 
-    #[allow(non_snake_case)]
     pub fn clone_outline_style(&self) -> longhands::outline_style::computed_value::T {
-        // FIXME(bholley): Align binary representations and ditch |match| for cast + static_asserts
-        match ${get_gecko_property("mOutlineStyle")} ${border_style_keyword.maybe_cast("u32")} {
-            % for value in border_style_keyword.values_for('gecko'):
-            structs::${border_style_keyword.gecko_constant(value)} => {
-                OutlineStyle::Other(border_style::T::${to_camel_case(value)})
-            },
-            % endfor
-            structs::${border_style_keyword.gecko_constant('auto')} => OutlineStyle::Auto,
-            % if border_style_keyword.gecko_inexhaustive:
-            _ => panic!("Found unexpected value in style struct for outline_style property"),
-            % endif
-        }
+        self.gecko.mOutlineStyle.clone()
     }
 
     <% impl_non_negative_length("outline_width", "mActualOutlineWidth",
                                 inherit_from="mOutlineWidth",
                                 round_to_pixels=True) %>
 
     % for corner in CORNERS:
     <% impl_corner_style_coord("_moz_outline_radius_%s" % corner.ident.replace("_", ""),
@@ -5407,17 +5385,17 @@ clip-path
                 thumb: self.gecko.mScrollbarFaceColor.into(),
                 track: self.gecko.mScrollbarTrackColor.into(),
             }
         }
     }
 </%self:impl_trait>
 
 <%self:impl_trait style_struct_name="Column"
-                  skip_longhands="column-count column-rule-width">
+                  skip_longhands="column-count column-rule-width column-rule-style">
 
     #[allow(unused_unsafe)]
     pub fn set_column_count(&mut self, v: longhands::column_count::computed_value::T) {
         use crate::gecko_bindings::structs::{nsStyleColumn_kColumnCountAuto, nsStyleColumn_kMaxColumnCount};
 
         self.gecko.mColumnCount = match v {
             ColumnCount::Integer(integer) => {
                 cmp::min(integer.0 as u32, unsafe { nsStyleColumn_kMaxColumnCount })
@@ -5436,16 +5414,17 @@ clip-path
             ColumnCount::Integer((self.gecko.mColumnCount as i32).into())
         } else {
             ColumnCount::Auto
         }
     }
 
     <% impl_non_negative_length("column_rule_width", "mColumnRuleWidth",
                                 round_to_pixels=True) %>
+    ${impl_simple('column_rule_style', 'mColumnRuleStyle')}
 </%self:impl_trait>
 
 <%self:impl_trait style_struct_name="Counters"
                   skip_longhands="content counter-increment counter-reset">
     pub fn ineffective_content_property(&self) -> bool {
         self.gecko.mContents.is_empty()
     }
 
--- a/servo/components/style/properties/longhands/column.mako.rs
+++ b/servo/components/style/properties/longhands/column.mako.rs
@@ -73,18 +73,19 @@
     animation_value_type="discrete",
     gecko_enum_prefix="StyleColumnSpan",
     gecko_pref="layout.css.column-span.enabled",
     enabled_in="ua",
     spec="https://drafts.csswg.org/css-multicol/#propdef-column-span",
     extra_prefixes="moz:layout.css.column-span.enabled",
 )}
 
-${helpers.single_keyword(
+${helpers.predefined_type(
     "column-rule-style",
-    "none hidden dotted dashed solid double groove ridge inset outset",
+    "BorderStyle",
+    "computed::BorderStyle::None",
+    needs_context=False,
+    initial_specified_value="specified::BorderStyle::None",
     products="gecko",
     extra_prefixes="moz",
-    gecko_enum_prefix="StyleBorderStyle",
-    gecko_inexhaustive=True,
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-style",
 )}
--- a/servo/components/style/values/specified/border.rs
+++ b/servo/components/style/values/specified/border.rs
@@ -16,45 +16,46 @@ use crate::values::generics::size::Size;
 use crate::values::specified::length::{Length, LengthOrPercentage, NonNegativeLength};
 use crate::values::specified::{AllowQuirks, Number, NumberOrPercentage};
 use cssparser::Parser;
 use std::fmt::{self, Write};
 use style_traits::{CssWriter, ParseError, ToCss};
 
 /// A specified value for a single side of a `border-style` property.
 ///
-/// The integer values here correspond to the border conflict resolution rules
-/// in CSS 2.1 § 17.6.2.1. Higher values override lower values.
+/// The order here corresponds to the integer values from the border conflict
+/// resolution rules in CSS 2.1 § 17.6.2.1. Higher values override lower values.
 #[allow(missing_docs)]
 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
 #[derive(
     Clone,
     Copy,
     Debug,
     Eq,
     MallocSizeOf,
     Ord,
     Parse,
     PartialEq,
     PartialOrd,
     SpecifiedValueInfo,
     ToComputedValue,
     ToCss,
 )]
+#[repr(u8)]
 pub enum BorderStyle {
-    Hidden = -2,
-    None = -1,
-    Inset = 0,
-    Groove = 1,
-    Outset = 2,
-    Ridge = 3,
-    Dotted = 4,
-    Dashed = 5,
-    Solid = 6,
-    Double = 7,
+    Hidden,
+    None,
+    Inset,
+    Groove,
+    Outset,
+    Ridge,
+    Dotted,
+    Dashed,
+    Solid,
+    Double,
 }
 
 impl BorderStyle {
     /// Whether this border style is either none or hidden.
     #[inline]
     pub fn none_or_hidden(&self) -> bool {
         matches!(*self, BorderStyle::None | BorderStyle::Hidden)
     }
--- a/servo/components/style/values/specified/outline.rs
+++ b/servo/components/style/values/specified/outline.rs
@@ -18,51 +18,52 @@ use style_traits::ParseError;
     MallocSizeOf,
     Ord,
     PartialEq,
     PartialOrd,
     SpecifiedValueInfo,
     ToComputedValue,
     ToCss,
 )]
+#[repr(C, u8)]
 /// <https://drafts.csswg.org/css-ui/#propdef-outline-style>
 pub enum OutlineStyle {
     /// auto
     Auto,
     /// <border-style>
-    Other(BorderStyle),
+    BorderStyle(BorderStyle),
 }
 
 impl OutlineStyle {
     #[inline]
     /// Get default value as None
     pub fn none() -> OutlineStyle {
-        OutlineStyle::Other(BorderStyle::None)
+        OutlineStyle::BorderStyle(BorderStyle::None)
     }
 
     #[inline]
     /// Get value for None or Hidden
     pub fn none_or_hidden(&self) -> bool {
         match *self {
             OutlineStyle::Auto => false,
-            OutlineStyle::Other(ref border_style) => border_style.none_or_hidden(),
+            OutlineStyle::BorderStyle(ref style) => style.none_or_hidden(),
         }
     }
 }
 
 impl Parse for OutlineStyle {
     fn parse<'i, 't>(
         _context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<OutlineStyle, ParseError<'i>> {
         if let Ok(border_style) = input.try(BorderStyle::parse) {
             if let BorderStyle::Hidden = border_style {
                 return Err(input
                     .new_custom_error(SelectorParseErrorKind::UnexpectedIdent("hidden".into())));
             }
 
-            return Ok(OutlineStyle::Other(border_style));
+            return Ok(OutlineStyle::BorderStyle(border_style));
         }
 
         input.expect_ident_matching("auto")?;
         Ok(OutlineStyle::Auto)
     }
 }