Bug 1535165 - Use cbindgen for text-decoration-line. r=dholbert,boris
authorEmilio Cobos Álvarez <emilio@crisal.io>
Mon, 18 Mar 2019 17:58:16 +0000
changeset 464893 5797a6719d5b73d291d387b774b161187274c6ce
parent 464892 981af21f38b103ec8f8fcd0267cfc89d4e3cd07e
child 464894 8a5a706e25c81aec96eb4f6553c5d95ea25c0383
push id35727
push userdvarga@mozilla.com
push dateTue, 19 Mar 2019 09:48:59 +0000
treeherdermozilla-central@70baa37ae1eb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdholbert, boris
bugs1535165
milestone68.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 1535165 - Use cbindgen for text-decoration-line. r=dholbert,boris Differential Revision: https://phabricator.services.mozilla.com/D23412
accessible/base/TextAttrs.cpp
accessible/base/TextAttrs.h
dom/html/HTMLFontElement.cpp
layout/generic/nsTextFrame.cpp
layout/generic/nsTextFrame.h
layout/painting/nsCSSRendering.cpp
layout/painting/nsCSSRendering.h
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
layout/xul/nsTextBoxFrame.cpp
layout/xul/tree/nsTreeBodyFrame.cpp
servo/components/style/cbindgen.toml
servo/components/style/properties/gecko.mako.rs
servo/components/style/properties/longhands/text.mako.rs
servo/components/style/properties/properties.mako.rs
servo/components/style/values/computed/text.rs
servo/components/style/values/specified/text.rs
servo/ports/geckolib/glue.rs
--- a/accessible/base/TextAttrs.cpp
+++ b/accessible/base/TextAttrs.cpp
@@ -601,19 +601,19 @@ void TextAttrsMgr::AutoGeneratedTextAttr
 ////////////////////////////////////////////////////////////////////////////////
 // TextDecorTextAttr
 ////////////////////////////////////////////////////////////////////////////////
 
 TextAttrsMgr::TextDecorValue::TextDecorValue(nsIFrame* aFrame) {
   const nsStyleTextReset* textReset = aFrame->StyleTextReset();
   mStyle = textReset->mTextDecorationStyle;
   mColor = textReset->mTextDecorationColor.CalcColor(aFrame);
-  mLine = textReset->mTextDecorationLine &
-          (NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE |
-           NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH);
+  mLine =
+      textReset->mTextDecorationLine & (StyleTextDecorationLine_UNDERLINE |
+                                        StyleTextDecorationLine_LINE_THROUGH);
 }
 
 TextAttrsMgr::TextDecorTextAttr::TextDecorTextAttr(nsIFrame* aRootFrame,
                                                    nsIFrame* aFrame)
     : TTextAttr<TextDecorValue>(!aFrame) {
   mRootNativeValue = TextDecorValue(aRootFrame);
   mIsRootDefined = mRootNativeValue.IsDefined();
 
--- a/accessible/base/TextAttrs.h
+++ b/accessible/base/TextAttrs.h
@@ -353,40 +353,40 @@ class TextAttrsMgr {
    * "text-line-through-style", "text-line-through-color",
    * "text-underline-style" and "text-underline-color" text attributes.
    */
 
   class TextDecorValue {
    public:
     TextDecorValue()
         : mColor{0},
-          mLine{NS_STYLE_TEXT_DECORATION_LINE_NONE},
+          mLine{StyleTextDecorationLine_NONE},
           mStyle{NS_STYLE_TEXT_DECORATION_STYLE_NONE} {}
     explicit TextDecorValue(nsIFrame* aFrame);
 
     nscolor Color() const { return mColor; }
     uint8_t Style() const { return mStyle; }
 
     bool IsDefined() const { return IsUnderline() || IsLineThrough(); }
     bool IsUnderline() const {
-      return mLine & NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE;
+      return bool(mLine & mozilla::StyleTextDecorationLine_UNDERLINE);
     }
     bool IsLineThrough() const {
-      return mLine & NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH;
+      return bool(mLine & mozilla::StyleTextDecorationLine_LINE_THROUGH);
     }
 
     bool operator==(const TextDecorValue& aValue) {
       return mColor == aValue.mColor && mLine == aValue.mLine &&
              mStyle == aValue.mStyle;
     }
     bool operator!=(const TextDecorValue& aValue) { return !(*this == aValue); }
 
    private:
     nscolor mColor;
-    uint8_t mLine;
+    mozilla::StyleTextDecorationLine mLine;
     uint8_t mStyle;
   };
 
   class TextDecorTextAttr : public TTextAttr<TextDecorValue> {
    public:
     TextDecorTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame);
     virtual ~TextDecorTextAttr() {}
 
--- a/dom/html/HTMLFontElement.cpp
+++ b/dom/html/HTMLFontElement.cpp
@@ -68,17 +68,17 @@ void HTMLFontElement::MapAttributesIntoR
     const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::color);
     nscolor color;
     if (value && value->GetColorValue(color)) {
       aDecls.SetColorValue(eCSSProperty_color, color);
     }
   }
   if (aDecls.Document()->GetCompatibilityMode() == eCompatibility_NavQuirks) {
     // Make <a><font color="red">text</font></a> give the text a red underline
-    // in quirks mode.  The NS_STYLE_TEXT_DECORATION_LINE_OVERRIDE_ALL flag only
+    // in quirks mode.  The StyleTextDecorationLine_COLOR_OVERRIDE flag only
     // affects quirks mode rendering.
     const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::color);
     nscolor color;
     if (value && value->GetColorValue(color)) {
       aDecls.SetTextDecorationColorOverride();
     }
   }
 
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -5274,20 +5274,21 @@ void nsTextFrame::GetTextDecorations(
   for (nsIFrame *f = this, *fChild = nullptr; f;
        fChild = f, f = nsLayoutUtils::GetParentOrPlaceholderFor(f)) {
     ComputedStyle* const context = f->Style();
     if (!context->HasTextDecorationLines()) {
       break;
     }
 
     const nsStyleTextReset* const styleText = context->StyleTextReset();
-    const uint8_t textDecorations = styleText->mTextDecorationLine;
+    const StyleTextDecorationLine textDecorations =
+        styleText->mTextDecorationLine;
 
     if (!useOverride &&
-        (NS_STYLE_TEXT_DECORATION_LINE_OVERRIDE_ALL & textDecorations)) {
+        (StyleTextDecorationLine_COLOR_OVERRIDE & textDecorations)) {
       // This handles the <a href="blah.html"><font color="green">La
       // la la</font></a> case. The link underline should be green.
       useOverride = true;
       overrideColor =
           nsLayoutUtils::GetColor(f, &nsStyleTextReset::mTextDecorationColor);
     }
 
     nsBlockFrame* fBlock = do_QueryFrame(f);
@@ -5342,32 +5343,32 @@ void nsTextFrame::GetTextDecorations(
                     ? nsLayoutUtils::GetColor(f, &nsStyleSVG::mFill)
                     : NS_SAME_AS_FOREGROUND_COLOR;
       } else {
         color =
             nsLayoutUtils::GetColor(f, &nsStyleTextReset::mTextDecorationColor);
       }
 
       bool swapUnderlineAndOverline = vertical && IsUnderlineRight(f);
-      const uint8_t kUnderline = swapUnderlineAndOverline
-                                     ? NS_STYLE_TEXT_DECORATION_LINE_OVERLINE
-                                     : NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE;
-      const uint8_t kOverline = swapUnderlineAndOverline
-                                    ? NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE
-                                    : NS_STYLE_TEXT_DECORATION_LINE_OVERLINE;
+      const auto kUnderline = swapUnderlineAndOverline
+                                  ? StyleTextDecorationLine_OVERLINE
+                                  : StyleTextDecorationLine_UNDERLINE;
+      const auto kOverline = swapUnderlineAndOverline
+                                 ? StyleTextDecorationLine_UNDERLINE
+                                 : StyleTextDecorationLine_OVERLINE;
 
       if (textDecorations & kUnderline) {
         aDecorations.mUnderlines.AppendElement(
             nsTextFrame::LineDecoration(f, baselineOffset, color, style));
       }
       if (textDecorations & kOverline) {
         aDecorations.mOverlines.AppendElement(
             nsTextFrame::LineDecoration(f, baselineOffset, color, style));
       }
-      if (textDecorations & NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH) {
+      if (textDecorations & StyleTextDecorationLine_LINE_THROUGH) {
         aDecorations.mStrikes.AppendElement(
             nsTextFrame::LineDecoration(f, baselineOffset, color, style));
       }
     }
 
     // In all modes, if we're on an inline-block or inline-table (or
     // inline-stack, inline-box, inline-grid), we're done.
     // If we're on a ruby frame other than ruby text container, we
@@ -5546,21 +5547,21 @@ void nsTextFrame::UnionAdditionalOverflo
                      appUnitsPerDevUnit;
     params.lineSize = Size(gfxWidth, underlineSize / appUnitsPerDevUnit);
     params.ascent = gfxFloat(mAscent) / appUnitsPerDevUnit;
     params.style = decorationStyle;
     params.vertical = verticalRun;
     params.sidewaysLeft = mTextRun->IsSidewaysLeft();
 
     params.offset = underlineOffset / appUnitsPerDevUnit;
-    params.decoration = NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE;
+    params.decoration = StyleTextDecorationLine_UNDERLINE;
     nsRect underlineRect =
         nsCSSRendering::GetTextDecorationRect(aPresContext, params);
     params.offset = maxAscent / appUnitsPerDevUnit;
-    params.decoration = NS_STYLE_TEXT_DECORATION_LINE_OVERLINE;
+    params.decoration = StyleTextDecorationLine_OVERLINE;
     nsRect overlineRect =
         nsCSSRendering::GetTextDecorationRect(aPresContext, params);
 
     aVisualOverflowRect->UnionRect(*aVisualOverflowRect, underlineRect);
     aVisualOverflowRect->UnionRect(*aVisualOverflowRect, overlineRect);
 
     // XXX If strikeoutSize is much thicker than the underlineSize, it may
     //     cause overflowing from the overflow rect.  However, such case
@@ -5633,27 +5634,27 @@ void nsTextFrame::UnionAdditionalOverflo
         } else {
           topOrLeft = std::min(decorationRect.y, topOrLeft);
           bottomOrRight = std::max(decorationRect.YMost(), bottomOrRight);
         }
       };
 
       // Below we loop through all text decorations and compute the rectangle
       // containing all of them, in this frame's coordinate space
-      params.decoration = NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE;
+      params.decoration = StyleTextDecorationLine_UNDERLINE;
       for (const LineDecoration& dec : textDecs.mUnderlines) {
         accumulateDecorationRect(dec, &Metrics::underlineSize,
                                  &Metrics::underlineOffset);
       }
-      params.decoration = NS_STYLE_TEXT_DECORATION_LINE_OVERLINE;
+      params.decoration = StyleTextDecorationLine_OVERLINE;
       for (const LineDecoration& dec : textDecs.mOverlines) {
         accumulateDecorationRect(dec, &Metrics::underlineSize,
                                  &Metrics::maxAscent);
       }
-      params.decoration = NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH;
+      params.decoration = StyleTextDecorationLine_LINE_THROUGH;
       for (const LineDecoration& dec : textDecs.mStrikes) {
         accumulateDecorationRect(dec, &Metrics::strikeoutSize,
                                  &Metrics::strikeoutOffset);
       }
 
       aVisualOverflowRect->UnionRect(
           *aVisualOverflowRect,
           verticalDec
@@ -5784,24 +5785,24 @@ void nsTextFrame::PaintDecorationLine(
  * about drawing text decoration for selections.
  */
 void nsTextFrame::DrawSelectionDecorations(
     gfxContext* aContext, const LayoutDeviceRect& aDirtyRect,
     SelectionType aSelectionType, nsTextPaintStyle& aTextPaintStyle,
     const TextRangeStyle& aRangeStyle, const Point& aPt,
     gfxFloat aICoordInFrame, gfxFloat aWidth, gfxFloat aAscent,
     const gfxFont::Metrics& aFontMetrics, DrawPathCallbacks* aCallbacks,
-    bool aVertical, uint8_t aDecoration) {
+    bool aVertical, StyleTextDecorationLine aDecoration) {
   PaintDecorationLineParams params;
   params.context = aContext;
   params.dirtyRect = aDirtyRect;
   params.pt = aPt;
   params.lineSize.width = aWidth;
   params.ascent = aAscent;
-  params.offset = aDecoration == NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE
+  params.offset = aDecoration == StyleTextDecorationLine_UNDERLINE
                       ? aFontMetrics.underlineOffset
                       : aFontMetrics.maxAscent;
   params.decoration = aDecoration;
   params.decorationType = DecorationType::Selection;
   params.callbacks = aCallbacks;
   params.vertical = aVertical;
   params.sidewaysLeft = mTextRun->IsSidewaysLeft();
   params.descentLimit = ComputeDescentLimitForSelectionUnderline(
@@ -5889,17 +5890,17 @@ void nsTextFrame::DrawSelectionDecoratio
       const gfxFont::Metrics metrics =
           GetFirstFontMetrics(GetFontGroupForFrame(this, inflation), aVertical);
 
       relativeSize = 2.0f;
       aTextPaintStyle.GetURLSecondaryColor(&params.color);
       params.style = NS_STYLE_TEXT_DECORATION_STYLE_SOLID;
       params.lineSize.height = metrics.strikeoutSize;
       params.offset = metrics.strikeoutOffset + 0.5;
-      params.decoration = NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH;
+      params.decoration = StyleTextDecorationLine_LINE_THROUGH;
       break;
     }
     default:
       NS_WARNING("Requested selection decorations when there aren't any");
       return;
   }
   params.lineSize.height *= relativeSize;
   params.icoordInFrame =
@@ -6408,19 +6409,18 @@ void nsTextFrame::PaintTextSelectionDeco
         selectedChars[i] = sdptr;
       }
     }
   }
 
   gfxFont* firstFont = aParams.provider->GetFontGroup()->GetFirstValidFont();
   bool verticalRun = mTextRun->IsVertical();
   bool rightUnderline = verticalRun && IsUnderlineRight(this);
-  const uint8_t kDecoration = rightUnderline
-                                  ? NS_STYLE_TEXT_DECORATION_LINE_OVERLINE
-                                  : NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE;
+  const auto kDecoration = rightUnderline ? StyleTextDecorationLine_OVERLINE
+                                          : StyleTextDecorationLine_UNDERLINE;
   bool useVerticalMetrics = verticalRun && mTextRun->UseCenterBaseline();
   gfxFont::Metrics decorationMetrics(firstFont->GetMetrics(
       useVerticalMetrics ? gfxFont::eVertical : gfxFont::eHorizontal));
   if (!useVerticalMetrics) {
     // The potential adjustment from using gfxFontGroup::GetUnderlineOffset
     // is only valid for horizontal font metrics.
     decorationMetrics.underlineOffset =
         aParams.provider->GetFontGroup()->GetUnderlineOffset();
@@ -7121,24 +7121,24 @@ void nsTextFrame::DrawTextRunAndDecorati
     }
 
     clipRect.Scale(1 / app);
     clipRect.Round();
     params.context->Clip(clipRect);
   }
 
   // Underlines
-  params.decoration = NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE;
+  params.decoration = StyleTextDecorationLine_UNDERLINE;
   for (const LineDecoration& dec : Reversed(aDecorations.mUnderlines)) {
     paintDecorationLine(dec, &Metrics::underlineSize,
                         &Metrics::underlineOffset);
   }
 
   // Overlines
-  params.decoration = NS_STYLE_TEXT_DECORATION_LINE_OVERLINE;
+  params.decoration = StyleTextDecorationLine_OVERLINE;
   for (const LineDecoration& dec : Reversed(aDecorations.mOverlines)) {
     paintDecorationLine(dec, &Metrics::underlineSize, &Metrics::maxAscent);
   }
 
   // Some glyphs and emphasis marks may extend outside the region, so we reset
   // the clip region here. For an example, italic glyphs.
   if (!skipClipping) {
     params.context->PopClip();
@@ -7161,17 +7161,17 @@ void nsTextFrame::DrawTextRunAndDecorati
                     aRange, aParams.decorationOverrideColor, aParams.provider);
 
   // Re-apply the clip region when the line-through is being drawn.
   if (!skipClipping) {
     params.context->Clip(clipRect);
   }
 
   // Line-throughs
-  params.decoration = NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH;
+  params.decoration = StyleTextDecorationLine_LINE_THROUGH;
   for (const LineDecoration& dec : Reversed(aDecorations.mStrikes)) {
     paintDecorationLine(dec, &Metrics::strikeoutSize,
                         &Metrics::strikeoutOffset);
   }
 
   if (!skipClipping) {
     params.context->PopClip();
   }
--- a/layout/generic/nsTextFrame.h
+++ b/layout/generic/nsTextFrame.h
@@ -755,17 +755,17 @@ class nsTextFrame : public nsFrame {
    * Utility methods to paint selection.
    */
   void DrawSelectionDecorations(
       gfxContext* aContext, const LayoutDeviceRect& aDirtyRect,
       mozilla::SelectionType aSelectionType, nsTextPaintStyle& aTextPaintStyle,
       const TextRangeStyle& aRangeStyle, const Point& aPt,
       gfxFloat aICoordInFrame, gfxFloat aWidth, gfxFloat aAscent,
       const gfxFont::Metrics& aFontMetrics, DrawPathCallbacks* aCallbacks,
-      bool aVertical, uint8_t aDecoration);
+      bool aVertical, mozilla::StyleTextDecorationLine aDecoration);
 
   struct PaintDecorationLineParams;
   void PaintDecorationLine(const PaintDecorationLineParams& aParams);
   /**
    * ComputeDescentLimitForSelectionUnderline() computes the most far position
    * where we can put selection underline.
    *
    * @return The maximum underline offset from the baseline (positive value
--- a/layout/painting/nsCSSRendering.cpp
+++ b/layout/painting/nsCSSRendering.cpp
@@ -3677,20 +3677,20 @@ void nsCSSRendering::PaintDecorationLine
   NS_ASSERTION(aParams.style != NS_STYLE_TEXT_DECORATION_STYLE_NONE,
                "aStyle is none");
 
   Rect rect = ToRect(GetTextDecorationRectInternal(aParams.pt, aParams));
   if (rect.IsEmpty() || !rect.Intersects(aParams.dirtyRect)) {
     return;
   }
 
-  if (aParams.decoration != NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE &&
-      aParams.decoration != NS_STYLE_TEXT_DECORATION_LINE_OVERLINE &&
-      aParams.decoration != NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH) {
-    NS_ERROR("Invalid decoration value!");
+  if (aParams.decoration != StyleTextDecorationLine_UNDERLINE &&
+      aParams.decoration != StyleTextDecorationLine_OVERLINE &&
+      aParams.decoration != StyleTextDecorationLine_LINE_THROUGH) {
+    MOZ_ASSERT_UNREACHABLE("Invalid text decoration value");
     return;
   }
 
   Float lineThickness = std::max(NS_round(aParams.lineSize.height), 1.0);
 
   Color color = ToDeviceColor(aParams.color);
   ColorPattern colorPat(color);
   StrokeOptions strokeOptions(lineThickness);
@@ -3956,20 +3956,20 @@ Rect nsCSSRendering::DecorationLineToPat
 
   Rect path;  // To benefit from RVO, we return this from all return points
 
   Rect rect = ToRect(GetTextDecorationRectInternal(aParams.pt, aParams));
   if (rect.IsEmpty() || !rect.Intersects(aParams.dirtyRect)) {
     return path;
   }
 
-  if (aParams.decoration != NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE &&
-      aParams.decoration != NS_STYLE_TEXT_DECORATION_LINE_OVERLINE &&
-      aParams.decoration != NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH) {
-    NS_ERROR("Invalid decoration value!");
+  if (aParams.decoration != StyleTextDecorationLine_UNDERLINE &&
+      aParams.decoration != StyleTextDecorationLine_OVERLINE &&
+      aParams.decoration != StyleTextDecorationLine_LINE_THROUGH) {
+    MOZ_ASSERT_UNREACHABLE("Invalid text decoration value");
     return path;
   }
 
   if (aParams.style != NS_STYLE_TEXT_DECORATION_STYLE_SOLID) {
     // For the moment, we support only solid text decorations.
     return path;
   }
 
@@ -4104,52 +4104,44 @@ gfxRect nsCSSRendering::GetTextDecoratio
   // and double) "grow" in the appropriate direction compared to the basic
   // single line.
   //
   // Note that at this point, the decoration rect is being calculated in line-
   // relative coordinates, where 'x' is line-rightwards, and 'y' is line-
   // upwards. We'll swap them to be physical coords at the end.
   gfxFloat offset = 0.0;
 
-  switch (aParams.decoration) {
-    case NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE:
-      offset = aParams.offset;
-      if (canLiftUnderline) {
-        if (descentLimit < -offset + r.Height()) {
-          // If we can ignore the offset and the decoration line is overflowing,
-          // we should align the bottom edge of the decoration line rect if it's
-          // possible.  Otherwise, we should lift up the top edge of the rect as
-          // far as possible.
-          gfxFloat offsetBottomAligned = -descentLimit + r.Height();
-          gfxFloat offsetTopAligned = 0.0;
-          offset = std::min(offsetBottomAligned, offsetTopAligned);
-        }
+  if (aParams.decoration == StyleTextDecorationLine_UNDERLINE) {
+    offset = aParams.offset;
+    if (canLiftUnderline) {
+      if (descentLimit < -offset + r.Height()) {
+        // If we can ignore the offset and the decoration line is overflowing,
+        // we should align the bottom edge of the decoration line rect if it's
+        // possible.  Otherwise, we should lift up the top edge of the rect as
+        // far as possible.
+        gfxFloat offsetBottomAligned = -descentLimit + r.Height();
+        gfxFloat offsetTopAligned = 0.0;
+        offset = std::min(offsetBottomAligned, offsetTopAligned);
       }
-      break;
-
-    case NS_STYLE_TEXT_DECORATION_LINE_OVERLINE:
-      // For overline, we adjust the offset by lineThickness (the thickness of
-      // a single decoration line) because empirically it looks better to draw
-      // the overline just inside rather than outside the font's ascent, which
-      // is what nsTextFrame passes as aParams.offset (as fonts don't provide
-      // an explicit overline-offset).
-      offset = aParams.offset - lineThickness + r.Height();
-      break;
-
-    case NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH: {
-      // To maintain a consistent mid-point for line-through decorations,
-      // we adjust the offset by half of the decoration rect's height.
-      gfxFloat extra = floor(r.Height() / 2.0 + 0.5);
-      extra = std::max(extra, lineThickness);
-      offset = aParams.offset - lineThickness + extra;
-      break;
     }
-
-    default:
-      NS_ERROR("Invalid decoration value!");
+  } else if (aParams.decoration == StyleTextDecorationLine_OVERLINE) {
+    // For overline, we adjust the offset by lineThickness (the thickness of
+    // a single decoration line) because empirically it looks better to draw
+    // the overline just inside rather than outside the font's ascent, which
+    // is what nsTextFrame passes as aParams.offset (as fonts don't provide
+    // an explicit overline-offset).
+    offset = aParams.offset - lineThickness + r.Height();
+  } else if (aParams.decoration == StyleTextDecorationLine_LINE_THROUGH) {
+    // To maintain a consistent mid-point for line-through decorations,
+    // we adjust the offset by half of the decoration rect's height.
+    gfxFloat extra = floor(r.Height() / 2.0 + 0.5);
+    extra = std::max(extra, lineThickness);
+    offset = aParams.offset - lineThickness + extra;
+  } else {
+    MOZ_ASSERT_UNREACHABLE("Invalid text decoration value");
   }
 
   // Convert line-relative coordinate system (x = line-right, y = line-up)
   // to physical coords, and move the decoration rect to the calculated
   // offset from baseline.
   if (aParams.vertical) {
     Swap(r.x, r.y);
     Swap(r.width, r.height);
--- a/layout/painting/nsCSSRendering.h
+++ b/layout/painting/nsCSSRendering.h
@@ -567,20 +567,19 @@ struct nsCSSRendering {
     // as possible.  Note that this does not mean the underline never
     // overflows from this limitation, because if the underline is
     // positioned to the baseline or upper, it causes unreadability.
     // Note that if this is zero or larger, the underline rect may be
     // shrunken if it's possible.  Therefore, this value is used for
     // strikeout line and overline too.
     Float descentLimit = -1.0f;
     // Which line will be painted. The value can be
-    // NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE or
-    // NS_STYLE_TEXT_DECORATION_LINE_OVERLINE or
-    // NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH.
-    uint8_t decoration = NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE;
+    // UNDERLINE or OVERLINE or LINE_THROUGH.
+    mozilla::StyleTextDecorationLine decoration =
+        mozilla::StyleTextDecorationLine_UNDERLINE;
     // The style of the decoration line such as
     // NS_STYLE_TEXT_DECORATION_STYLE_*.
     uint8_t style = NS_STYLE_TEXT_DECORATION_STYLE_NONE;
     bool vertical = false;
     bool sidewaysLeft = false;
   };
 
   struct PaintDecorationLineParams : DecorationRectParams {
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -444,16 +444,17 @@ cbindgen-types = [
     { gecko = "StyleZIndex", servo = "values::computed::ZIndex" },
     { gecko = "StyleGenericZIndex", servo = "values::generics::position::ZIndex" },
     { gecko = "StyleTransformOrigin", servo = "values::computed::TransformOrigin" },
     { gecko = "StyleGenericBorderRadius", servo = "values::generics::border::BorderRadius" },
     { gecko = "StyleLetterSpacing", servo = "values::computed::text::LetterSpacing" },
     { gecko = "StyleGenericLineHeight", servo = "values::generics::text::LineHeight" },
     { gecko = "StyleContain", servo = "values::computed::Contain" },
     { gecko = "StyleRestyleHint", servo = "invalidation::element::restyle_hints::RestyleHint" },
+    { gecko = "StyleTextDecorationLine", servo = "values::computed::TextDecorationLine" },
 ]
 
 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
@@ -109,17 +109,16 @@ LONGHANDS_NOT_SERIALIZED_WITH_SERVO = [
     "-moz-osx-font-smoothing",
     "outline-width",
     "paint-order",
     "row-gap",
     "scrollbar-color",
     "scroll-snap-points-x",
     "scroll-snap-points-y",
     "stroke",
-    "text-decoration-line",
     "text-emphasis-position",
     "text-emphasis-style",
     "text-overflow",
     "text-shadow",
     "touch-action",
     "transition-delay",
     "transition-duration",
     "transition-property",
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -354,24 +354,16 @@ const KTableEntry nsCSSProps::kTextAlign
     {eCSSKeyword_justify, NS_STYLE_TEXT_ALIGN_JUSTIFY},
     {eCSSKeyword__moz_center, NS_STYLE_TEXT_ALIGN_MOZ_CENTER},
     {eCSSKeyword__moz_right, NS_STYLE_TEXT_ALIGN_MOZ_RIGHT},
     {eCSSKeyword__moz_left, NS_STYLE_TEXT_ALIGN_MOZ_LEFT},
     {eCSSKeyword_start, NS_STYLE_TEXT_ALIGN_START},
     {eCSSKeyword_end, NS_STYLE_TEXT_ALIGN_END},
     {eCSSKeyword_UNKNOWN, -1}};
 
-const KTableEntry nsCSSProps::kTextDecorationLineKTable[] = {
-    {eCSSKeyword_none, NS_STYLE_TEXT_DECORATION_LINE_NONE},
-    {eCSSKeyword_underline, NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE},
-    {eCSSKeyword_overline, NS_STYLE_TEXT_DECORATION_LINE_OVERLINE},
-    {eCSSKeyword_line_through, NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH},
-    {eCSSKeyword_blink, NS_STYLE_TEXT_DECORATION_LINE_BLINK},
-    {eCSSKeyword_UNKNOWN, -1}};
-
 const KTableEntry nsCSSProps::kTextDecorationStyleKTable[] = {
     {eCSSKeyword__moz_none, NS_STYLE_TEXT_DECORATION_STYLE_NONE},
     {eCSSKeyword_solid, NS_STYLE_TEXT_DECORATION_STYLE_SOLID},
     {eCSSKeyword_double, NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE},
     {eCSSKeyword_dotted, NS_STYLE_TEXT_DECORATION_STYLE_DOTTED},
     {eCSSKeyword_dashed, NS_STYLE_TEXT_DECORATION_STYLE_DASHED},
     {eCSSKeyword_wavy, NS_STYLE_TEXT_DECORATION_STYLE_WAVY},
     {eCSSKeyword_UNKNOWN, -1}};
--- a/layout/style/nsCSSProps.h
+++ b/layout/style/nsCSSProps.h
@@ -301,17 +301,16 @@ class nsCSSProps {
   static const KTableEntry kAutoCompletionAlignJustifyContent[];
   // ------------------------------------------------------------------
   // clang-format on
   static const KTableEntry kFontSmoothingKTable[];
   static const KTableEntry kGridAutoFlowKTable[];
   static const KTableEntry kGridTrackBreadthKTable[];
   static const KTableEntry kLineHeightKTable[];
   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[];
 };
 
 #endif /* nsCSSProps_h___ */
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -1985,61 +1985,48 @@ already_AddRefed<CSSValue> nsComputedDOM
 
 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetTextDecoration() {
   const nsStyleTextReset* textReset = StyleTextReset();
 
   bool isInitialStyle =
       textReset->mTextDecorationStyle == NS_STYLE_TEXT_DECORATION_STYLE_SOLID;
   StyleComplexColor color = textReset->mTextDecorationColor;
 
+  RefPtr<nsROCSSPrimitiveValue> textDecorationLine = new nsROCSSPrimitiveValue;
+
+  {
+    nsAutoString decorationLine;
+    Servo_GetPropertyValue(mComputedStyle, eCSSProperty_text_decoration_line,
+                           &decorationLine);
+    textDecorationLine->SetString(decorationLine);
+  }
+
   if (isInitialStyle && color.IsCurrentColor()) {
-    return DoGetTextDecorationLine();
+    return textDecorationLine.forget();
   }
 
   RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
 
-  valueList->AppendCSSValue(DoGetTextDecorationLine());
+  valueList->AppendCSSValue(textDecorationLine.forget());
   if (!isInitialStyle) {
     valueList->AppendCSSValue(DoGetTextDecorationStyle());
   }
   if (!color.IsCurrentColor()) {
     valueList->AppendCSSValue(DoGetTextDecorationColor());
   }
 
   return valueList.forget();
 }
 
 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetTextDecorationColor() {
   RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
   SetValueFromComplexColor(val, StyleTextReset()->mTextDecorationColor);
   return val.forget();
 }
 
-already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetTextDecorationLine() {
-  RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
-
-  int32_t intValue = StyleTextReset()->mTextDecorationLine;
-
-  if (NS_STYLE_TEXT_DECORATION_LINE_NONE == intValue) {
-    val->SetIdent(eCSSKeyword_none);
-  } else {
-    nsAutoString decorationLineString;
-    // Clear the OVERRIDE_ALL bits -- we don't want these to appear in
-    // the computed style.
-    intValue &= ~NS_STYLE_TEXT_DECORATION_LINE_OVERRIDE_ALL;
-    nsStyleUtil::AppendBitmaskCSSValue(
-        nsCSSProps::kTextDecorationLineKTable, intValue,
-        NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE,
-        NS_STYLE_TEXT_DECORATION_LINE_BLINK, decorationLineString);
-    val->SetString(decorationLineString);
-  }
-
-  return val.forget();
-}
-
 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetTextDecorationStyle() {
   RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
 
   val->SetIdent(
       nsCSSProps::ValueToKeywordEnum(StyleTextReset()->mTextDecorationStyle,
                                      nsCSSProps::kTextDecorationStyleKTable));
 
   return val.forget();
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -302,17 +302,16 @@ class nsComputedDOMStyle final : public 
   /* Outline Properties */
   already_AddRefed<CSSValue> DoGetOutlineWidth();
 
   /* Text Properties */
   already_AddRefed<CSSValue> DoGetInitialLetter();
   already_AddRefed<CSSValue> DoGetLineHeight();
   already_AddRefed<CSSValue> DoGetTextDecoration();
   already_AddRefed<CSSValue> DoGetTextDecorationColor();
-  already_AddRefed<CSSValue> DoGetTextDecorationLine();
   already_AddRefed<CSSValue> DoGetTextDecorationStyle();
   already_AddRefed<CSSValue> DoGetTextEmphasisPosition();
   already_AddRefed<CSSValue> DoGetTextEmphasisStyle();
   already_AddRefed<CSSValue> DoGetTextOverflow();
   already_AddRefed<CSSValue> DoGetTextShadow();
   already_AddRefed<CSSValue> DoGetWebkitTextStrokeWidth();
 
   /* Display properties */
--- a/layout/style/nsStyleConsts.h
+++ b/layout/style/nsStyleConsts.h
@@ -557,30 +557,16 @@ enum class StyleGridTrackBreadth : uint8
 #define NS_STYLE_TEXT_ALIGN_AUTO 7
 #define NS_STYLE_TEXT_ALIGN_MOZ_CENTER 8
 #define NS_STYLE_TEXT_ALIGN_MOZ_RIGHT 9
 #define NS_STYLE_TEXT_ALIGN_MOZ_LEFT 10
 
 // Note: make sure that the largest NS_STYLE_TEXT_ALIGN_* value is smaller than
 // the smallest NS_STYLE_VERTICAL_ALIGN_* value below!
 
-// See nsStyleText, nsStyleFont
-#define NS_STYLE_TEXT_DECORATION_LINE_NONE 0
-#define NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE 0x01
-#define NS_STYLE_TEXT_DECORATION_LINE_OVERLINE 0x02
-#define NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH 0x04
-#define NS_STYLE_TEXT_DECORATION_LINE_BLINK 0x08
-// OVERRIDE_ALL does not occur in stylesheets; it only comes from HTML
-// attribute mapping (and thus appears in computed data)
-#define NS_STYLE_TEXT_DECORATION_LINE_OVERRIDE_ALL 0x10
-#define NS_STYLE_TEXT_DECORATION_LINE_LINES_MASK \
-  (NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE |     \
-   NS_STYLE_TEXT_DECORATION_LINE_OVERLINE |      \
-   NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH)
-
 // See nsStyleText
 #define NS_STYLE_TEXT_DECORATION_STYLE_NONE \
   0  // not in CSS spec, mapped to -moz-none
 #define NS_STYLE_TEXT_DECORATION_STYLE_DOTTED 1
 #define NS_STYLE_TEXT_DECORATION_STYLE_DASHED 2
 #define NS_STYLE_TEXT_DECORATION_STYLE_SOLID 3
 #define NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE 4
 #define NS_STYLE_TEXT_DECORATION_STYLE_WAVY 5
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -3613,17 +3613,17 @@ nsChangeHint nsStyleContent::CalcDiffere
 }
 
 // --------------------
 // nsStyleTextReset
 //
 
 nsStyleTextReset::nsStyleTextReset(const Document& aDocument)
     : mTextOverflow(),
-      mTextDecorationLine(NS_STYLE_TEXT_DECORATION_LINE_NONE),
+      mTextDecorationLine(StyleTextDecorationLine_NONE),
       mTextDecorationStyle(NS_STYLE_TEXT_DECORATION_STYLE_SOLID),
       mUnicodeBidi(NS_STYLE_UNICODE_BIDI_NORMAL),
       mInitialLetterSink(0),
       mInitialLetterSize(0.0f),
       mTextDecorationColor(StyleComplexColor::CurrentColor()) {
   MOZ_COUNT_CTOR(nsStyleTextReset);
 }
 
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -1420,25 +1420,26 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
   nsStyleTextReset(const nsStyleTextReset& aOther);
   ~nsStyleTextReset();
   void TriggerImageLoads(mozilla::dom::Document&, const nsStyleTextReset*) {}
   const static bool kHasTriggerImageLoads = false;
 
   // Note the difference between this and
   // ComputedStyle::HasTextDecorationLines.
   bool HasTextDecorationLines() const {
-    return mTextDecorationLine != NS_STYLE_TEXT_DECORATION_LINE_NONE &&
-           mTextDecorationLine != NS_STYLE_TEXT_DECORATION_LINE_OVERRIDE_ALL;
+    return mTextDecorationLine != mozilla::StyleTextDecorationLine_NONE &&
+           mTextDecorationLine !=
+               mozilla::StyleTextDecorationLine_COLOR_OVERRIDE;
   }
 
   nsChangeHint CalcDifference(const nsStyleTextReset& aNewData) const;
 
   nsStyleTextOverflow mTextOverflow;  // enum, string
 
-  uint8_t mTextDecorationLine;   // NS_STYLE_TEXT_DECORATION_LINE_*
+  mozilla::StyleTextDecorationLine mTextDecorationLine;
   uint8_t mTextDecorationStyle;  // NS_STYLE_TEXT_DECORATION_STYLE_*
   uint8_t mUnicodeBidi;          // NS_STYLE_UNICODE_BIDI_*
   nscoord mInitialLetterSink;    // 0 means normal
   float mInitialLetterSize;      // 0.0f means normal
   mozilla::StyleComplexColor mTextDecorationColor;
 };
 
 struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleText {
--- a/layout/xul/nsTextBoxFrame.cpp
+++ b/layout/xul/nsTextBoxFrame.cpp
@@ -366,19 +366,21 @@ void nsTextBoxFrame::DrawText(gfxContext
   nscolor overColor = 0;
   nscolor underColor = 0;
   nscolor strikeColor = 0;
   uint8_t overStyle = 0;
   uint8_t underStyle = 0;
   uint8_t strikeStyle = 0;
 
   // Begin with no decorations
-  uint8_t decorations = NS_STYLE_TEXT_DECORATION_LINE_NONE;
-  // A mask of all possible decorations.
-  uint8_t decorMask = NS_STYLE_TEXT_DECORATION_LINE_LINES_MASK;
+  auto decorations = StyleTextDecorationLine_NONE;
+  // A mask of all possible line decorations.
+  auto decorMask = StyleTextDecorationLine_UNDERLINE |
+                   StyleTextDecorationLine_OVERLINE |
+                   StyleTextDecorationLine_LINE_THROUGH;
 
   WritingMode wm = GetWritingMode();
   bool vertical = wm.IsVertical();
 
   nsIFrame* f = this;
   do {  // find decoration colors
     ComputedStyle* context = f->Style();
     if (!context->HasTextDecorationLines()) {
@@ -391,39 +393,40 @@ void nsTextBoxFrame::DrawText(gfxContext
       nscolor color;
       if (aOverrideColor) {
         color = *aOverrideColor;
       } else {
         color = styleText->mTextDecorationColor.CalcColor(context);
       }
       uint8_t style = styleText->mTextDecorationStyle;
 
-      if (NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE & decorMask &
+      if (StyleTextDecorationLine_UNDERLINE & decorMask &
           styleText->mTextDecorationLine) {
         underColor = color;
         underStyle = style;
-        decorMask &= ~NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE;
-        decorations |= NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE;
+        // TODO(emilio): Could add `operator~` or `remove()` to cbindgen.
+        decorMask.bits &= ~StyleTextDecorationLine_UNDERLINE.bits;
+        decorations |= StyleTextDecorationLine_UNDERLINE;
       }
-      if (NS_STYLE_TEXT_DECORATION_LINE_OVERLINE & decorMask &
+      if (StyleTextDecorationLine_OVERLINE & decorMask &
           styleText->mTextDecorationLine) {
         overColor = color;
         overStyle = style;
-        decorMask &= ~NS_STYLE_TEXT_DECORATION_LINE_OVERLINE;
-        decorations |= NS_STYLE_TEXT_DECORATION_LINE_OVERLINE;
+        decorMask.bits &= ~StyleTextDecorationLine_OVERLINE.bits;
+        decorations |= StyleTextDecorationLine_OVERLINE;
       }
-      if (NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH & decorMask &
+      if (StyleTextDecorationLine_LINE_THROUGH & decorMask &
           styleText->mTextDecorationLine) {
         strikeColor = color;
         strikeStyle = style;
-        decorMask &= ~NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH;
-        decorations |= NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH;
+        decorMask.bits &= ~StyleTextDecorationLine_LINE_THROUGH.bits;
+        decorations |= StyleTextDecorationLine_LINE_THROUGH;
       }
     }
-  } while (0 != decorMask && (f = nsLayoutUtils::GetParentOrPlaceholderFor(f)));
+  } while (decorMask && (f = nsLayoutUtils::GetParentOrPlaceholderFor(f)));
 
   RefPtr<nsFontMetrics> fontMet =
       nsLayoutUtils::GetFontMetricsForFrame(this, 1.0f);
   fontMet->SetVertical(wm.IsVertical());
   fontMet->SetTextOrientation(StyleVisibility()->mTextOrientation);
 
   nscoord offset;
   nscoord size;
@@ -453,33 +456,33 @@ void nsTextBoxFrame::DrawText(gfxContext
   // XXX todo: vertical-mode support for decorations not tested yet,
   // probably won't be positioned correctly
 
   // Underlines are drawn before overlines, and both before the text
   // itself, per http://www.w3.org/TR/CSS21/zindex.html point 7.2.1.4.1.1.
   // (We don't apply this rule to the access-key underline because we only
   // find out where that is as a side effect of drawing the text, in the
   // general case -- see below.)
-  if (decorations & (NS_STYLE_TEXT_DECORATION_LINE_OVERLINE |
-                     NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE)) {
+  if (decorations &
+      (StyleTextDecorationLine_OVERLINE | StyleTextDecorationLine_UNDERLINE)) {
     fontMet->GetUnderline(offset, size);
     params.lineSize.height = presContext->AppUnitsToGfxUnits(size);
-    if ((decorations & NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE) &&
+    if ((decorations & StyleTextDecorationLine_UNDERLINE) &&
         underStyle != NS_STYLE_TEXT_DECORATION_STYLE_NONE) {
       params.color = underColor;
       params.offset = presContext->AppUnitsToGfxUnits(offset);
-      params.decoration = NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE;
+      params.decoration = StyleTextDecorationLine_UNDERLINE;
       params.style = underStyle;
       nsCSSRendering::PaintDecorationLine(this, *drawTarget, params);
     }
-    if ((decorations & NS_STYLE_TEXT_DECORATION_LINE_OVERLINE) &&
+    if ((decorations & StyleTextDecorationLine_OVERLINE) &&
         overStyle != NS_STYLE_TEXT_DECORATION_STYLE_NONE) {
       params.color = overColor;
       params.offset = params.ascent;
-      params.decoration = NS_STYLE_TEXT_DECORATION_LINE_OVERLINE;
+      params.decoration = StyleTextDecorationLine_OVERLINE;
       params.style = overStyle;
       nsCSSRendering::PaintDecorationLine(this, *drawTarget, params);
     }
   }
 
   RefPtr<gfxContext> refContext =
       PresShell()->CreateReferenceRenderingContext();
   DrawTarget* refDrawTarget = refContext->GetDrawTarget();
@@ -539,23 +542,23 @@ void nsTextBoxFrame::DrawText(gfxContext
              mAccessKeyInfo->mAccessWidth,
              mAccessKeyInfo->mAccessUnderlineSize);
     Rect devPxRect = NSRectToSnappedRect(r, appUnitsPerDevPixel, *drawTarget);
     drawTarget->FillRect(devPxRect, color);
   }
 
   // Strikeout is drawn on top of the text, per
   // http://www.w3.org/TR/CSS21/zindex.html point 7.2.1.4.1.1.
-  if ((decorations & NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH) &&
+  if ((decorations & StyleTextDecorationLine_LINE_THROUGH) &&
       strikeStyle != NS_STYLE_TEXT_DECORATION_STYLE_NONE) {
     fontMet->GetStrikeout(offset, size);
     params.color = strikeColor;
     params.lineSize.height = presContext->AppUnitsToGfxUnits(size);
     params.offset = presContext->AppUnitsToGfxUnits(offset);
-    params.decoration = NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH;
+    params.decoration = StyleTextDecorationLine_LINE_THROUGH;
     params.style = strikeStyle;
     nsCSSRendering::PaintDecorationLine(this, *drawTarget, params);
   }
 }
 
 void nsTextBoxFrame::CalculateUnderline(DrawTarget* aDrawTarget,
                                         nsFontMetrics& aFontMetrics) {
   if (mAccessKeyInfo && mAccessKeyInfo->mAccesskeyIndex != kNotFound) {
--- a/layout/xul/tree/nsTreeBodyFrame.cpp
+++ b/layout/xul/tree/nsTreeBodyFrame.cpp
@@ -3441,36 +3441,37 @@ ImgDrawResult nsTreeBodyFrame::PaintText
 
   // Time to paint our text.
   textRect.Deflate(bp);
 
   // Set our color.
   ColorPattern color(ToDeviceColor(textContext->StyleColor()->mColor));
 
   // Draw decorations.
-  uint8_t decorations = textContext->StyleTextReset()->mTextDecorationLine;
+  StyleTextDecorationLine decorations =
+      textContext->StyleTextReset()->mTextDecorationLine;
 
   nscoord offset;
   nscoord size;
-  if (decorations & (NS_STYLE_TEXT_DECORATION_LINE_OVERLINE |
-                     NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE)) {
+  if (decorations &
+      (StyleTextDecorationLine_OVERLINE | StyleTextDecorationLine_UNDERLINE)) {
     fontMet->GetUnderline(offset, size);
-    if (decorations & NS_STYLE_TEXT_DECORATION_LINE_OVERLINE) {
+    if (decorations & StyleTextDecorationLine_OVERLINE) {
       nsRect r(textRect.x, textRect.y, textRect.width, size);
       Rect devPxRect = NSRectToSnappedRect(r, appUnitsPerDevPixel, *drawTarget);
       drawTarget->FillRect(devPxRect, color);
     }
-    if (decorations & NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE) {
+    if (decorations & StyleTextDecorationLine_UNDERLINE) {
       nsRect r(textRect.x, textRect.y + baseline - offset, textRect.width,
                size);
       Rect devPxRect = NSRectToSnappedRect(r, appUnitsPerDevPixel, *drawTarget);
       drawTarget->FillRect(devPxRect, color);
     }
   }
-  if (decorations & NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH) {
+  if (decorations & StyleTextDecorationLine_LINE_THROUGH) {
     fontMet->GetStrikeout(offset, size);
     nsRect r(textRect.x, textRect.y + baseline - offset, textRect.width, size);
     Rect devPxRect = NSRectToSnappedRect(r, appUnitsPerDevPixel, *drawTarget);
     drawTarget->FillRect(devPxRect, color);
   }
   ComputedStyle* cellContext =
       GetPseudoComputedStyle(nsCSSAnonBoxes::mozTreeCell());
 
--- a/servo/components/style/cbindgen.toml
+++ b/servo/components/style/cbindgen.toml
@@ -100,16 +100,17 @@ include = [
   "BorderRadius",
   "NonNegativeLengthOrNumberRect",
   "Perspective",
   "ZIndex",
   "TransformOrigin",
   "WordBreak",
   "Contain",
   "RestyleHint",
+  "TextDecorationLine",
 ]
 item_types = ["enums", "structs", "typedefs"]
 
 [export.body]
 "CSSPixelLength" = """
   inline nscoord ToAppUnits() const;
   inline bool IsZero() const;
 """
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -3991,19 +3991,17 @@ fn static_assert() {
     }
 
     ${impl_non_negative_length('_webkit_text_stroke_width',
                                'mWebkitTextStrokeWidth')}
 
 </%self:impl_trait>
 
 <%self:impl_trait style_struct_name="Text"
-                  skip_longhands="text-decoration-line text-overflow initial-letter">
-
-    ${impl_simple_type_with_conversion("text_decoration_line")}
+                  skip_longhands="text-overflow initial-letter">
 
     fn clear_overflow_sides_if_string(&mut self) {
         use crate::gecko_bindings::structs::nsStyleTextOverflowSide;
         fn clear_if_string(side: &mut nsStyleTextOverflowSide) {
             if side.mType == structs::NS_STYLE_TEXT_OVERFLOW_STRING as u8 {
                 side.mString.truncate();
                 side.mType = structs::NS_STYLE_TEXT_OVERFLOW_CLIP as u8;
             }
@@ -4107,31 +4105,16 @@ fn static_assert() {
         if self.gecko.mInitialLetterSize == 0. && self.gecko.mInitialLetterSink == 0 {
             InitialLetter::Normal
         } else if self.gecko.mInitialLetterSize.floor() as i32 == self.gecko.mInitialLetterSink {
             InitialLetter::Specified(self.gecko.mInitialLetterSize, None)
         } else {
             InitialLetter::Specified(self.gecko.mInitialLetterSize, Some(self.gecko.mInitialLetterSink))
         }
     }
-
-    #[inline]
-    pub fn has_underline(&self) -> bool {
-        (self.gecko.mTextDecorationLine & (structs::NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE as u8)) != 0
-    }
-
-    #[inline]
-    pub fn has_overline(&self) -> bool {
-        (self.gecko.mTextDecorationLine & (structs::NS_STYLE_TEXT_DECORATION_LINE_OVERLINE as u8)) != 0
-    }
-
-    #[inline]
-    pub fn has_line_through(&self) -> bool {
-        (self.gecko.mTextDecorationLine & (structs::NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH as u8)) != 0
-    }
 </%self:impl_trait>
 
 // Set SVGPathData to StyleShapeSource.
 fn set_style_svg_path(
     shape_source: &mut structs::mozilla::StyleShapeSource,
     servo_path: &values::specified::svg_path::SVGPathData,
     fill: values::generics::basic_shape::FillRule,
 ) {
--- a/servo/components/style/properties/longhands/text.mako.rs
+++ b/servo/components/style/properties/longhands/text.mako.rs
@@ -1,25 +1,16 @@
 /* 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/. */
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
 <% from data import Method %>
 
-<% data.new_style_struct(
-    "Text",
-    inherited=False,
-    gecko_name="TextReset",
-    additional_methods=[
-        Method("has_underline", "bool"),
-        Method("has_overline", "bool"),
-        Method("has_line_through", "bool"),
-    ]
-) %>
+<% data.new_style_struct("Text", inherited=False, gecko_name="TextReset") %>
 
 ${helpers.predefined_type(
     "text-overflow",
     "TextOverflow",
     "computed::TextOverflow::get_initial_value()",
     animation_value_type="discrete",
     boxed=True,
     flags="APPLIES_TO_PLACEHOLDER",
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -2633,34 +2633,16 @@ pub mod style_structs {
 
             % elif style_struct.name == "Outline":
                 /// Whether the outline-width property is non-zero.
                 #[inline]
                 pub fn outline_has_nonzero_width(&self) -> bool {
                     use crate::Zero;
                     !self.outline_width.is_zero()
                 }
-            % elif style_struct.name == "Text":
-                /// Whether the text decoration has an underline.
-                #[inline]
-                pub fn has_underline(&self) -> bool {
-                    self.text_decoration_line.contains(longhands::text_decoration_line::SpecifiedValue::UNDERLINE)
-                }
-
-                /// Whether the text decoration has an overline.
-                #[inline]
-                pub fn has_overline(&self) -> bool {
-                    self.text_decoration_line.contains(longhands::text_decoration_line::SpecifiedValue::OVERLINE)
-                }
-
-                /// Whether the text decoration has a line through.
-                #[inline]
-                pub fn has_line_through(&self) -> bool {
-                    self.text_decoration_line.contains(longhands::text_decoration_line::SpecifiedValue::LINE_THROUGH)
-                }
             % elif style_struct.name == "Box":
                 /// Sets the display property, but without touching original_display,
                 /// except when the adjustment comes from root or item display fixups.
                 pub fn set_adjusted_display(
                     &mut self,
                     dpy: longhands::display::computed_value::T,
                     is_item_or_root: bool
                 ) {
--- a/servo/components/style/values/computed/text.rs
+++ b/servo/components/style/values/computed/text.rs
@@ -14,17 +14,17 @@ use crate::values::generics::text::Spaci
 use crate::values::specified::text::{self as specified, TextOverflowSide};
 use crate::values::specified::text::{TextEmphasisFillMode, TextEmphasisShapeKeyword};
 use crate::values::{CSSFloat, CSSInteger};
 use crate::Zero;
 use std::fmt::{self, Write};
 use style_traits::{CssWriter, ToCss};
 
 pub use crate::values::specified::TextAlignKeyword as TextAlign;
-pub use crate::values::specified::TextEmphasisPosition;
+pub use crate::values::specified::{TextEmphasisPosition, TextDecorationLine};
 pub use crate::values::specified::{OverflowWrap, WordBreak};
 
 /// A computed value for the `initial-letter` property.
 pub type InitialLetter = GenericInitialLetter<CSSFloat, CSSInteger>;
 
 /// A computed value for the `letter-spacing` property.
 #[repr(transparent)]
 #[derive(
@@ -177,21 +177,21 @@ impl TextDecorationsInEffect {
         let mut result = match style.get_box().clone_display() {
             Display::InlineBlock | Display::InlineTable => Self::default(),
             _ => style
                 .get_parent_inherited_text()
                 .text_decorations_in_effect
                 .clone(),
         };
 
-        let text_style = style.get_text();
+        let line = style.get_text().clone_text_decoration_line();
 
-        result.underline |= text_style.has_underline();
-        result.overline |= text_style.has_overline();
-        result.line_through |= text_style.has_line_through();
+        result.underline |= line.contains(TextDecorationLine::UNDERLINE);
+        result.overline |= line.contains(TextDecorationLine::OVERLINE);
+        result.line_through |= line.contains(TextDecorationLine::LINE_THROUGH);
 
         result
     }
 }
 
 /// computed value for the text-emphasis-style property
 #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
 pub enum TextEmphasisStyle {
--- a/servo/components/style/values/specified/text.rs
+++ b/servo/components/style/values/specified/text.rs
@@ -17,18 +17,17 @@ use crate::values::generics::text::Spaci
 use crate::values::specified::length::NonNegativeLengthPercentage;
 use crate::values::specified::length::{FontRelativeLength, Length};
 use crate::values::specified::length::{LengthPercentage, NoCalcLength};
 use crate::values::specified::{AllowQuirks, Integer, NonNegativeNumber, Number};
 use cssparser::{Parser, Token};
 use selectors::parser::SelectorParseErrorKind;
 use std::fmt::{self, Write};
 use style_traits::values::SequenceWriter;
-use style_traits::{CssWriter, KeywordsCollectFn, ParseError};
-use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss};
+use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
 use unicode_segmentation::UnicodeSegmentation;
 
 /// A specified type for the `initial-letter` property.
 pub type InitialLetter = GenericInitialLetter<Number, Integer>;
 
 /// A specified value for the `letter-spacing` property.
 pub type LetterSpacing = Spacing<Length>;
 
@@ -250,130 +249,126 @@ impl ToComputedValue for TextOverflow {
             TextOverflow {
                 first: computed.first.clone(),
                 second: Some(computed.second.clone()),
             }
         }
     }
 }
 
-macro_rules! impl_text_decoration_line {
-    {
-        $(
-            $(#[$($meta:tt)+])*
-            $ident:ident / $css:expr => $value:expr,
-        )+
-    } => {
-        bitflags! {
-            #[derive(MallocSizeOf, ToComputedValue)]
-            /// Specified keyword values for the text-decoration-line property.
-            pub struct TextDecorationLine: u8 {
-                /// No text decoration line is specified
-                const NONE = 0;
-                $(
-                    $(#[$($meta)+])*
-                    const $ident = $value;
-                )+
-                #[cfg(feature = "gecko")]
-                /// Only set by presentation attributes
-                ///
-                /// Setting this will mean that text-decorations use the color
-                /// specified by `color` in quirks mode.
-                ///
-                /// For example, this gives <a href=foo><font color="red">text</font></a>
-                /// a red text decoration
-                const COLOR_OVERRIDE = 0x10;
+bitflags! {
+    #[derive(MallocSizeOf, SpecifiedValueInfo, ToComputedValue)]
+    #[value_info(other_values = "none,underline,overline,line-through,blink")]
+    #[repr(C)]
+    /// Specified keyword values for the text-decoration-line property.
+    pub struct TextDecorationLine: u8 {
+        /// No text decoration line is specified.
+        const NONE = 0;
+        /// underline
+        const UNDERLINE = 1 << 0;
+        /// overline
+        const OVERLINE = 1 << 1;
+        /// line-through
+        const LINE_THROUGH = 1 << 2;
+        /// blink
+        const BLINK = 1 << 3;
+        /// Only set by presentation attributes
+        ///
+        /// Setting this will mean that text-decorations use the color
+        /// specified by `color` in quirks mode.
+        ///
+        /// For example, this gives <a href=foo><font color="red">text</font></a>
+        /// a red text decoration
+        #[cfg(feature = "gecko")]
+        const COLOR_OVERRIDE = 0x10;
+    }
+}
+
+
+impl Parse for TextDecorationLine {
+    /// none | [ underline || overline || line-through || blink ]
+    fn parse<'i, 't>(
+        _context: &ParserContext,
+        input: &mut Parser<'i, 't>,
+    ) -> Result<Self, ParseError<'i>> {
+        let mut result = TextDecorationLine::empty();
+
+        // NOTE(emilio): this loop has this weird structure because we run this
+        // code to parse the text-decoration shorthand as well, so we need to
+        // ensure we don't return an error if we don't consume the whole thing
+        // because we find an invalid identifier or other kind of token.
+        loop {
+            let flag: Result<_, ParseError<'i>> = input.try(|input| {
+                let flag = try_match_ident_ignore_ascii_case! { input,
+                    "none" if result.is_empty() => TextDecorationLine::NONE,
+                    "underline" => TextDecorationLine::UNDERLINE,
+                    "overline" => TextDecorationLine::OVERLINE,
+                    "line-through" => TextDecorationLine::LINE_THROUGH,
+                    "blink" => TextDecorationLine::BLINK,
+                };
+
+                Ok(flag)
+            });
+
+            let flag = match flag {
+                Ok(flag) => flag,
+                Err(..) => break,
+            };
+
+            if flag.is_empty() {
+                return Ok(TextDecorationLine::NONE);
             }
+
+            if result.contains(flag) {
+                return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
+            }
+
+            result.insert(flag)
         }
 
-        impl Parse for TextDecorationLine {
-            /// none | [ underline || overline || line-through || blink ]
-            fn parse<'i, 't>(
-                _context: &ParserContext,
-                input: &mut Parser<'i, 't>,
-            ) -> Result<TextDecorationLine, ParseError<'i>> {
-                let mut result = TextDecorationLine::NONE;
-                if input
-                    .try(|input| input.expect_ident_matching("none"))
-                    .is_ok()
-                {
-                    return Ok(result);
-                }
-
-                loop {
-                    let result = input.try(|input| {
-                        let ident = input.expect_ident().map_err(|_| ())?;
-                        match_ignore_ascii_case! { ident,
-                            $(
-                                $css => {
-                                    if result.contains(TextDecorationLine::$ident) {
-                                        Err(())
-                                    } else {
-                                        result.insert(TextDecorationLine::$ident);
-                                        Ok(())
-                                    }
-                                }
-                            )+
-                            _ => Err(()),
-                        }
-                    });
-                    if result.is_err() {
-                        break;
-                    }
-                }
-
-                if !result.is_empty() {
-                    Ok(result)
-                } else {
-                    Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
-                }
-            }
-        }
-
-        impl ToCss for TextDecorationLine {
-            fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
-            where
-                W: Write,
-            {
-                if self.is_empty() {
-                    return dest.write_str("none");
-                }
-
-                let mut writer = SequenceWriter::new(dest, " ");
-                $(
-                    if self.contains(TextDecorationLine::$ident) {
-                        writer.raw_item($css)?;
-                    }
-                )+
-                Ok(())
-            }
-        }
-
-        impl SpecifiedValueInfo for TextDecorationLine {
-            fn collect_completion_keywords(f: KeywordsCollectFn) {
-                f(&["none", $($css,)+]);
-            }
+        if !result.is_empty() {
+            Ok(result)
+        } else {
+            Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
         }
     }
 }
 
-impl_text_decoration_line! {
-    /// Underline
-    UNDERLINE / "underline" => 1 << 0,
-    /// Overline
-    OVERLINE / "overline" => 1 << 1,
-    /// Line through
-    LINE_THROUGH / "line-through" => 1 << 2,
-    /// Blink
-    BLINK / "blink" => 1 << 3,
+impl ToCss for TextDecorationLine {
+    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
+    where
+        W: Write,
+    {
+        if self.is_empty() {
+            return dest.write_str("none");
+        }
+
+        let mut writer = SequenceWriter::new(dest, " ");
+        let mut any = false;
+
+        macro_rules! maybe_write {
+            ($ident:ident => $str:expr) => {
+                if self.contains(TextDecorationLine::$ident) {
+                    any = true;
+                    writer.raw_item($str)?;
+                }
+            };
+        }
+
+        maybe_write!(UNDERLINE => "underline");
+        maybe_write!(OVERLINE => "overline");
+        maybe_write!(LINE_THROUGH => "line-through");
+        maybe_write!(BLINK => "blink");
+
+        debug_assert!(any || *self == TextDecorationLine::COLOR_OVERRIDE);
+
+        Ok(())
+    }
 }
 
-#[cfg(feature = "gecko")]
-impl_bitflags_conversions!(TextDecorationLine);
-
 impl TextDecorationLine {
     #[inline]
     /// Returns the initial value of text-decoration-line
     pub fn none() -> Self {
         TextDecorationLine::NONE
     }
 }
 
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -4737,18 +4737,17 @@ pub extern "C" fn Servo_DeclarationBlock
 
 #[no_mangle]
 pub extern "C" fn Servo_DeclarationBlock_SetTextDecorationColorOverride(
     declarations: RawServoDeclarationBlockBorrowed,
 ) {
     use style::properties::PropertyDeclaration;
     use style::values::specified::text::TextDecorationLine;
 
-    let mut decoration = TextDecorationLine::none();
-    decoration |= TextDecorationLine::COLOR_OVERRIDE;
+    let decoration = TextDecorationLine::COLOR_OVERRIDE;
     let decl = PropertyDeclaration::TextDecorationLine(decoration);
     write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
         decls.push(decl, Importance::Normal);
     })
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn Servo_CSSSupports2(