Bug 1303241 part 2 - Make GetVisitedDependentColor use style structs directly. r=dbaron
authorXidorn Quan <me@upsuper.org>
Sat, 31 Dec 2016 00:57:37 +1100
changeset 328010 b144475237d3968025fd4b865013a00fdef2c257
parent 328009 ab9310993e1d290b7de4c4aa8e88373e20c7c2f0
child 328011 6b1a87d941fa94bda048d48276ec2025eaf0f7c4
push id31162
push userkwierso@gmail.com
push dateFri, 06 Jan 2017 01:24:21 +0000
treeherdermozilla-central@53193729a50b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdbaron
bugs1303241
milestone53.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 1303241 part 2 - Make GetVisitedDependentColor use style structs directly. r=dbaron I think there are three advantages of this change: 1. removes some dependencies from layout / painting code to pre-computed value stuff in the style system; 2. makes it easier to audit usage of specific fields in style structs (which is probably a side effect of the first one); 3. potentially improves performance since it doesn't go through the unnecessary general logic in ExtractComputedValue. Also, combined with the part before, we get a unified list for visited- dependent properties so that we can ensure the assertion here and the style difference calc code are consistent. MozReview-Commit-ID: 5B9aN7CfRgI
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
layout/generic/TextOverflow.cpp
layout/generic/nsBulletFrame.cpp
layout/generic/nsColumnSetFrame.cpp
layout/generic/nsIFrame.h
layout/generic/nsPluginFrame.cpp
layout/generic/nsTextFrame.cpp
layout/mathml/nsMathMLChar.cpp
layout/mathml/nsMathMLFrame.cpp
layout/mathml/nsMathMLmencloseFrame.cpp
layout/mathml/nsMathMLmfracFrame.cpp
layout/painting/nsCSSRendering.cpp
layout/style/nsStyleContext.cpp
layout/style/nsStyleContext.h
layout/style/nsStyleStruct.h
layout/tables/nsTableFrame.cpp
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -5573,23 +5573,22 @@ DarkenColor(nscolor aColor)
 static bool
 ShouldDarkenColors(nsPresContext* aPresContext)
 {
   return !aPresContext->GetBackgroundColorDraw() &&
          !aPresContext->GetBackgroundImageDraw();
 }
 
 nscolor
-nsLayoutUtils::GetColor(nsIFrame* aFrame, nsCSSPropertyID aProperty)
-{
-  nscolor color = aFrame->GetVisitedDependentColor(aProperty);
+nsLayoutUtils::DarkenColorIfNeeded(nsIFrame* aFrame, nscolor aColor)
+{
   if (ShouldDarkenColors(aFrame->PresContext())) {
-    color = DarkenColor(color);
-  }
-  return color;
+    return DarkenColor(aColor);
+  }
+  return aColor;
 }
 
 gfxFloat
 nsLayoutUtils::GetSnappedBaselineY(nsIFrame* aFrame, gfxContext* aContext,
                                    nscoord aY, nscoord aAscent)
 {
   gfxFloat appUnitsPerDevUnit = aFrame->PresContext()->AppUnitsPerDevPixel();
   gfxFloat baseline = gfxFloat(aY) + aAscent;
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -1541,18 +1541,28 @@ public:
   // Implement nsIFrame::GetPrefISize in terms of nsIFrame::AddInlinePrefISize
   static nscoord PrefISizeFromInline(nsIFrame* aFrame,
                                      nsRenderingContext* aRenderingContext);
 
   // Implement nsIFrame::GetMinISize in terms of nsIFrame::AddInlineMinISize
   static nscoord MinISizeFromInline(nsIFrame* aFrame,
                                     nsRenderingContext* aRenderingContext);
 
-  // Get a suitable foreground color for painting aProperty for aFrame.
-  static nscolor GetColor(nsIFrame* aFrame, nsCSSPropertyID aProperty);
+  // Get a suitable foreground color for painting aColor for aFrame.
+  static nscolor DarkenColorIfNeeded(nsIFrame* aFrame, nscolor aColor);
+
+  // Get a suitable foreground color for painting aField for aFrame.
+  // Type of aFrame is made a template parameter because nsIFrame is not
+  // a complete type in the header. Type-safety is not harmed given that
+  // DarkenColorIfNeeded requires an nsIFrame pointer.
+  template<typename Frame, typename T, typename S>
+  static nscolor GetColor(Frame* aFrame, T S::* aField) {
+    nscolor color = aFrame->GetVisitedDependentColor(aField);
+    return DarkenColorIfNeeded(aFrame, color);
+  }
 
   // Get a baseline y position in app units that is snapped to device pixels.
   static gfxFloat GetSnappedBaselineY(nsIFrame* aFrame, gfxContext* aContext,
                                       nscoord aY, nscoord aAscent);
   // Ditto for an x position (for vertical text). Note that for vertical-rl
   // writing mode, the ascent value should be negated by the caller.
   static gfxFloat GetSnappedBaselineX(nsIFrame* aFrame, gfxContext* aContext,
                                       nscoord aX, nscoord aAscent);
--- a/layout/generic/TextOverflow.cpp
+++ b/layout/generic/TextOverflow.cpp
@@ -210,17 +210,17 @@ PaintTextShadowCallback(nsRenderingConte
            PaintTextToContext(aCtx, aShadowOffset);
 }
 
 void
 nsDisplayTextOverflowMarker::Paint(nsDisplayListBuilder* aBuilder,
                                    nsRenderingContext*   aCtx)
 {
   nscolor foregroundColor = nsLayoutUtils::
-    GetColor(mFrame, eCSSProperty__webkit_text_fill_color);
+    GetColor(mFrame, &nsStyleText::mWebkitTextFillColor);
 
   // Paint the text-shadows for the overflow marker
   nsLayoutUtils::PaintTextShadow(mFrame, aCtx, mRect, mVisibleRect,
                                  foregroundColor, PaintTextShadowCallback,
                                  (void*)this);
   aCtx->ThebesContext()->SetColor(gfx::Color::FromABGR(foregroundColor));
   PaintTextToContext(aCtx, nsPoint(0, 0));
 }
--- a/layout/generic/nsBulletFrame.cpp
+++ b/layout/generic/nsBulletFrame.cpp
@@ -311,17 +311,17 @@ nsBulletFrame::PaintBullet(nsRenderingCo
              PresContext(),
              imageCon, nsLayoutUtils::GetSamplingFilterForFrame(this),
              dest + aPt, aDirtyRect, nullptr, aFlags);
       }
     }
   }
 
   ColorPattern color(ToDeviceColor(
-                       nsLayoutUtils::GetColor(this, eCSSProperty_color)));
+    nsLayoutUtils::GetColor(this, &nsStyleColor::mColor)));
 
   DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
   int32_t appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel();
 
   switch (listStyleType->GetStyle()) {
   case NS_STYLE_LIST_STYLE_NONE:
     break;
 
@@ -418,17 +418,17 @@ nsBulletFrame::PaintBullet(nsRenderingCo
     break;
 
   default:
     {
       DrawTargetAutoDisableSubpixelAntialiasing
         disable(aRenderingContext.GetDrawTarget(), aDisableSubpixelAA);
 
       aRenderingContext.ThebesContext()->SetColor(
-        Color::FromABGR(nsLayoutUtils::GetColor(this, eCSSProperty_color)));
+        Color::FromABGR(nsLayoutUtils::GetColor(this, &nsStyleColor::mColor)));
 
       RefPtr<nsFontMetrics> fm =
         nsLayoutUtils::GetFontMetricsForFrame(this, GetFontSizeInflation());
       nsAutoString text;
       GetListItemText(text);
       WritingMode wm = GetWritingMode();
       nscoord ascent = wm.IsLineInverted()
                          ? fm->MaxDescent() : fm->MaxAscent();
--- a/layout/generic/nsColumnSetFrame.cpp
+++ b/layout/generic/nsColumnSetFrame.cpp
@@ -78,17 +78,17 @@ nsColumnSetFrame::PaintColumnRule(nsRend
     ruleStyle = colStyle->mColumnRuleStyle;
 
   nsPresContext* presContext = PresContext();
   nscoord ruleWidth = colStyle->GetComputedColumnRuleWidth();
   if (!ruleWidth)
     return;
 
   nscolor ruleColor =
-    GetVisitedDependentColor(eCSSProperty_column_rule_color);
+    GetVisitedDependentColor(&nsStyleColumn::mColumnRuleColor);
 
   // In order to re-use a large amount of code, we treat the column rule as a border.
   // We create a new border style object and fill in all the details of the column rule as
   // the left border. PaintBorder() does all the rendering for us, so we not
   // only save an enormous amount of code but we'll support all the line styles that
   // we support on borders!
   nsStyleBorder border(presContext);
   Sides skipSides;
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -722,18 +722,19 @@ public:
     const nsStyle##name_ * Style##name_ () const {                            \
       NS_ASSERTION(mStyleContext, "No style context found!");                 \
       return mStyleContext->Style##name_ ();                                  \
     }
   #include "nsStyleStructList.h"
   #undef STYLE_STRUCT
 
   /** Also forward GetVisitedDependentColor to the style context */
-  nscolor GetVisitedDependentColor(nsCSSPropertyID aProperty)
-    { return mStyleContext->GetVisitedDependentColor(aProperty); }
+  template<typename T, typename S>
+  nscolor GetVisitedDependentColor(T S::* aField)
+    { return mStyleContext->GetVisitedDependentColor(aField); }
 
   /**
    * These methods are to access any additional style contexts that
    * the frame may be holding. These are contexts that are children
    * of the frame's primary context and are NOT used as style contexts
    * for any child frames. These contexts also MUST NOT have any child 
    * contexts whatsoever. If you need to insert style contexts into the
    * style tree, then you should create pseudo element frames to own them
--- a/layout/generic/nsPluginFrame.cpp
+++ b/layout/generic/nsPluginFrame.cpp
@@ -336,18 +336,18 @@ nsPluginFrame::PrepForDrawing(nsIWidget 
 
     RegisterPluginForGeometryUpdates();
 
     // Here we set the background color for this widget because some plugins will use 
     // the child window background color when painting. If it's not set, it may default to gray
     // Sometimes, a frame doesn't have a background color or is transparent. In this
     // case, walk up the frame tree until we do find a frame with a background color
     for (nsIFrame* frame = this; frame; frame = frame->GetParent()) {
-      nscolor bgcolor =
-        frame->GetVisitedDependentColor(eCSSProperty_background_color);
+      nscolor bgcolor = frame->
+        GetVisitedDependentColor(&nsStyleBackground::mBackgroundColor);
       if (NS_GET_A(bgcolor) > 0) {  // make sure we got an actual color
         mWidget->SetBackgroundColor(bgcolor);
         break;
       }
     }
   } else {
     // Changing to windowless mode changes the NPWindow geometry.
     FixupWindow(GetContentRectRelativeToSelf().Size());
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -3747,24 +3747,24 @@ nsTextPaintStyle::GetTextColor()
     if (!mResolveColors)
       return NS_SAME_AS_FOREGROUND_COLOR;
 
     const nsStyleSVG* style = mFrame->StyleSVG();
     switch (style->mFill.Type()) {
       case eStyleSVGPaintType_None:
         return NS_RGBA(0, 0, 0, 0);
       case eStyleSVGPaintType_Color:
-        return nsLayoutUtils::GetColor(mFrame, eCSSProperty_fill);
+        return nsLayoutUtils::GetColor(mFrame, &nsStyleSVG::mFill);
       default:
         NS_ERROR("cannot resolve SVG paint to nscolor");
         return NS_RGBA(0, 0, 0, 255);
     }
   }
 
-  return nsLayoutUtils::GetColor(mFrame, eCSSProperty__webkit_text_fill_color);
+  return nsLayoutUtils::GetColor(mFrame, &nsStyleText::mWebkitTextFillColor);
 }
 
 bool
 nsTextPaintStyle::GetSelectionColors(nscolor* aForeColor,
                                      nscolor* aBackColor)
 {
   NS_ASSERTION(aForeColor, "aForeColor is null");
   NS_ASSERTION(aBackColor, "aBackColor is null");
@@ -3848,18 +3848,18 @@ void
 nsTextPaintStyle::InitCommonColors()
 {
   if (mInitCommonColors)
     return;
 
   nsIFrame* bgFrame =
     nsCSSRendering::FindNonTransparentBackgroundFrame(mFrame);
   NS_ASSERTION(bgFrame, "Cannot find NonTransparentBackgroundFrame.");
-  nscolor bgColor =
-    bgFrame->GetVisitedDependentColor(eCSSProperty_background_color);
+  nscolor bgColor = bgFrame->
+    GetVisitedDependentColor(&nsStyleBackground::mBackgroundColor);
 
   nscolor defaultBgColor = mPresContext->DefaultBackgroundColor();
   mFrameBackgroundColor = NS_ComposeColors(defaultBgColor, bgColor);
 
   mSystemFieldForegroundColor =
     LookAndFeel::GetColor(LookAndFeel::eColorID__moz_fieldtext);
   mSystemFieldBackgroundColor =
     LookAndFeel::GetColor(LookAndFeel::eColorID__moz_field);
@@ -3946,19 +3946,19 @@ nsTextPaintStyle::InitSelectionColorsAnd
     RefPtr<nsStyleContext> sc = nullptr;
     sc = mPresContext->StyleSet()->
       ProbePseudoElementStyle(selectionElement,
                               CSSPseudoElementType::mozSelection,
                               mFrame->StyleContext());
     // Use -moz-selection pseudo class.
     if (sc) {
       mSelectionBGColor =
-        sc->GetVisitedDependentColor(eCSSProperty_background_color);
+        sc->GetVisitedDependentColor(&nsStyleBackground::mBackgroundColor);
       mSelectionTextColor =
-        sc->GetVisitedDependentColor(eCSSProperty__webkit_text_fill_color);
+        sc->GetVisitedDependentColor(&nsStyleText::mWebkitTextFillColor);
       mHasSelectionShadow =
         nsRuleNode::HasAuthorSpecifiedRules(sc,
                                             NS_AUTHOR_SPECIFIED_TEXT_SHADOW,
                                             true);
       if (mHasSelectionShadow) {
         mSelectionShadow = sc->StyleText()->mTextShadow;
       }
       return true;
@@ -3984,26 +3984,24 @@ nsTextPaintStyle::InitSelectionColorsAnd
   }
 
   mSelectionTextColor =
     LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectForeground);
 
   if (mResolveColors) {
     // On MacOS X, we don't exchange text color and BG color.
     if (mSelectionTextColor == NS_DONT_CHANGE_COLOR) {
-      nsCSSPropertyID property = mFrame->IsSVGText()
-                               ? eCSSProperty_fill
-                               : eCSSProperty__webkit_text_fill_color;
-      nscoord frameColor = mFrame->GetVisitedDependentColor(property);
+      nscolor frameColor = mFrame->IsSVGText()
+        ? mFrame->GetVisitedDependentColor(&nsStyleSVG::mFill)
+        : mFrame->GetVisitedDependentColor(&nsStyleText::mWebkitTextFillColor);
       mSelectionTextColor = EnsureDifferentColors(frameColor, mSelectionBGColor);
     } else if (mSelectionTextColor == NS_CHANGE_COLOR_IF_SAME_AS_BG) {
-      nsCSSPropertyID property = mFrame->IsSVGText()
-                               ? eCSSProperty_fill
-                               : eCSSProperty__webkit_text_fill_color;
-      nscolor frameColor = mFrame->GetVisitedDependentColor(property);
+      nscolor frameColor = mFrame->IsSVGText()
+        ? mFrame->GetVisitedDependentColor(&nsStyleSVG::mFill)
+        : mFrame->GetVisitedDependentColor(&nsStyleText::mWebkitTextFillColor);
       if (frameColor == mSelectionBGColor) {
         mSelectionTextColor =
           LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectForegroundCustom);
       }
     } else {
       EnsureSufficientContrast(&mSelectionTextColor, &mSelectionBGColor);
     }
   } else {
@@ -5308,17 +5306,17 @@ nsTextFrame::GetTextDecorations(
     const uint8_t textDecorations = styleText->mTextDecorationLine;
 
     if (!useOverride &&
         (NS_STYLE_TEXT_DECORATION_LINE_OVERRIDE_ALL & 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, eCSSProperty_text_decoration_color);
+        nsLayoutUtils::GetColor(f, &nsStyleTextReset::mTextDecorationColor);
     }
 
     nsBlockFrame* fBlock = nsLayoutUtils::GetAsBlock(f);
     const bool firstBlock = !nearestBlockFound && fBlock;
 
     // Not updating positions once we hit a parent block is equivalent to
     // the CSS 2.1 spec that blocks should propagate decorations down to their
     // children (albeit the style should be preserved)
@@ -5362,20 +5360,21 @@ nsTextFrame::GetTextDecorations(
         // XXX We might want to do something with text-decoration-color when
         //     painting SVG text, but it's not clear what we should do.  We
         //     at least need SVG text decorations to paint with 'fill' if
         //     text-decoration-color has its initial value currentColor.
         //     We could choose to interpret currentColor as "currentFill"
         //     for SVG text, and have e.g. text-decoration-color:red to
         //     override the fill paint of the decoration.
         color = aColorResolution == eResolvedColors ?
-                  nsLayoutUtils::GetColor(f, eCSSProperty_fill) :
+                  nsLayoutUtils::GetColor(f, &nsStyleSVG::mFill) :
                   NS_SAME_AS_FOREGROUND_COLOR;
       } else {
-        color = nsLayoutUtils::GetColor(f, eCSSProperty_text_decoration_color);
+        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 :
@@ -6556,17 +6555,17 @@ nsTextFrame::DrawEmphasisMarks(gfxContex
 {
   const EmphasisMarkInfo* info = Properties().Get(EmphasisMarkProperty());
   if (!info) {
     return;
   }
 
   bool isTextCombined = StyleContext()->IsTextCombined();
   nscolor color = aDecorationOverrideColor ? *aDecorationOverrideColor :
-    nsLayoutUtils::GetColor(this, eCSSProperty_text_emphasis_color);
+    nsLayoutUtils::GetColor(this, &nsStyleText::mTextEmphasisColor);
   aContext->SetColor(Color::FromABGR(color));
   gfxPoint pt;
   if (!isTextCombined) {
     pt = aTextBaselinePt;
   } else {
     MOZ_ASSERT(aWM.IsVertical());
     pt = aFramePt;
     if (aWM.IsVerticalRL()) {
--- a/layout/mathml/nsMathMLChar.cpp
+++ b/layout/mathml/nsMathMLChar.cpp
@@ -2051,17 +2051,17 @@ nsMathMLChar::PaintForeground(nsPresCont
     // Set default context to the parent context
     styleContext = parentContext;
   }
 
   RefPtr<gfxContext> thebesContext = aRenderingContext.ThebesContext();
 
   // Set color ...
   nscolor fgColor = styleContext->
-    GetVisitedDependentColor(eCSSProperty__webkit_text_fill_color);
+    GetVisitedDependentColor(&nsStyleText::mWebkitTextFillColor);
   if (aIsSelected) {
     // get color to use for selection from the look&feel object
     fgColor = LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectForeground,
                                     fgColor);
   }
   thebesContext->SetColor(Color::FromABGR(fgColor));
   thebesContext->Save();
   nsRect r = mRect + aPt;
--- a/layout/mathml/nsMathMLFrame.cpp
+++ b/layout/mathml/nsMathMLFrame.cpp
@@ -362,17 +362,17 @@ void nsDisplayMathMLBar::Paint(nsDisplay
 {
   // paint the bar with the current text color
   DrawTarget* drawTarget = aCtx->GetDrawTarget();
   Rect rect =
     NSRectToNonEmptySnappedRect(mRect + ToReferenceFrame(),
                                 mFrame->PresContext()->AppUnitsPerDevPixel(),
                                 *drawTarget);
   ColorPattern color(ToDeviceColor(
-    mFrame->GetVisitedDependentColor(eCSSProperty__webkit_text_fill_color)));
+    mFrame->GetVisitedDependentColor(&nsStyleText::mWebkitTextFillColor)));
   drawTarget->FillRect(rect, color);
 }
 
 void
 nsMathMLFrame::DisplayBar(nsDisplayListBuilder* aBuilder,
                           nsIFrame* aFrame, const nsRect& aRect,
                           const nsDisplayListSet& aLists) {
   if (!aFrame->StyleVisibility()->IsVisible() || aRect.IsEmpty())
--- a/layout/mathml/nsMathMLmencloseFrame.cpp
+++ b/layout/mathml/nsMathMLmencloseFrame.cpp
@@ -775,17 +775,17 @@ void nsDisplayNotation::Paint(nsDisplayL
 
   Float strokeWidth = presContext->AppUnitsToGfxUnits(mThickness);
 
   Rect rect = NSRectToRect(mRect + ToReferenceFrame(),
                            presContext->AppUnitsPerDevPixel());
   rect.Deflate(strokeWidth / 2.f);
 
   ColorPattern color(ToDeviceColor(
-    mFrame->GetVisitedDependentColor(eCSSProperty__webkit_text_fill_color)));
+    mFrame->GetVisitedDependentColor(&nsStyleText::mWebkitTextFillColor)));
 
   StrokeOptions strokeOptions(strokeWidth);
 
   switch(mType)
   {
     case NOTATION_CIRCLE: {
       RefPtr<Path> ellipse =
         MakePathForEllipse(aDrawTarget, rect.Center(), rect.Size());
--- a/layout/mathml/nsMathMLmfracFrame.cpp
+++ b/layout/mathml/nsMathMLmfracFrame.cpp
@@ -624,17 +624,17 @@ void nsDisplayMathMLSlash::Paint(nsDispl
   DrawTarget& aDrawTarget = *aCtx->GetDrawTarget();
 
   // get the gfxRect
   nsPresContext* presContext = mFrame->PresContext();
   Rect rect = NSRectToRect(mRect + ToReferenceFrame(),
                            presContext->AppUnitsPerDevPixel());
 
   ColorPattern color(ToDeviceColor(
-    mFrame->GetVisitedDependentColor(eCSSProperty__webkit_text_fill_color)));
+    mFrame->GetVisitedDependentColor(&nsStyleText::mWebkitTextFillColor)));
 
   // draw the slash as a parallelogram
   Point delta = Point(presContext->AppUnitsToGfxUnits(mThickness), 0);
   RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder();
   if (mRTL) {
     builder->MoveTo(rect.TopLeft());
     builder->LineTo(rect.TopLeft() + delta);
     builder->LineTo(rect.BottomRight());
--- a/layout/painting/nsCSSRendering.cpp
+++ b/layout/painting/nsCSSRendering.cpp
@@ -665,18 +665,18 @@ nsCSSRendering::PaintBorder(nsPresContex
     return PaintBorderWithStyleBorder(aPresContext, aRenderingContext, aForFrame,
                                       aDirtyRect, aBorderArea, *styleBorder,
                                       aStyleContext, aFlags, aSkipSides);
   }
 
   nsStyleBorder newStyleBorder(*styleBorder);
 
   NS_FOR_CSS_SIDES(side) {
-    nscolor color = aStyleContext->GetVisitedDependentColor(
-      nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_color)[side]);
+    nscolor color = aStyleContext->
+      GetVisitedDependentColor(nsStyleBorder::BorderColorFieldFor(side));
     newStyleBorder.mBorderColor[side] = StyleComplexColor::FromColor(color);
   }
   return PaintBorderWithStyleBorder(aPresContext, aRenderingContext, aForFrame,
                                     aDirtyRect, aBorderArea, newStyleBorder,
                                     aStyleContext, aFlags, aSkipSides);
 }
 
 Maybe<nsCSSBorderRenderer>
@@ -697,18 +697,18 @@ nsCSSRendering::CreateBorderRenderer(nsP
                                                aForFrame, aDirtyRect,
                                                aBorderArea, *styleBorder,
                                                aStyleContext, aSkipSides);
   }
 
   nsStyleBorder newStyleBorder(*styleBorder);
 
   NS_FOR_CSS_SIDES(side) {
-    nscolor color = aStyleContext->GetVisitedDependentColor(
-      nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_color)[side]);
+    nscolor color = aStyleContext->
+      GetVisitedDependentColor(nsStyleBorder::BorderColorFieldFor(side));
     newStyleBorder.mBorderColor[side] = StyleComplexColor::FromColor(color);
   }
   return CreateBorderRendererWithStyleBorder(aPresContext, aDrawTarget,
                                              aForFrame, aDirtyRect, aBorderArea,
                                              newStyleBorder, aStyleContext,
                                              aSkipSides);
 }
 
@@ -728,18 +728,18 @@ ConstructBorderRenderer(nsPresContext* a
   // Get our style context's color struct.
   const nsStyleColor* ourColor = aStyleContext->StyleColor();
 
   // In NavQuirks mode we want to use the parent's context as a starting point
   // for determining the background color.
   bool quirks = aPresContext->CompatibilityMode() == eCompatibility_NavQuirks;
   nsIFrame* bgFrame = nsCSSRendering::FindNonTransparentBackgroundFrame(aForFrame, quirks);
   nsStyleContext* bgContext = bgFrame->StyleContext();
-  nscolor bgColor =
-    bgContext->GetVisitedDependentColor(eCSSProperty_background_color);
+  nscolor bgColor = bgContext->
+    GetVisitedDependentColor(&nsStyleBackground::mBackgroundColor);
 
   // Compute the outermost boundary of the area that might be painted.
   // Same coordinate space as aBorderArea & aBGClipRect.
   nsRect joinedBorderArea =
     ::BoxDecorationRectForBorder(aForFrame, aBorderArea, aSkipSides, &aStyleBorder);
   RectCornerRadii bgRadii;
   ::GetRadii(aForFrame, aStyleBorder, aBorderArea, joinedBorderArea, &bgRadii);
 
@@ -968,18 +968,18 @@ nsCSSRendering::PaintOutline(nsPresConte
   if (width == 0 && outlineStyle != NS_STYLE_BORDER_STYLE_AUTO) {
     // Empty outline
     return;
   }
 
   nsIFrame* bgFrame = nsCSSRendering::FindNonTransparentBackgroundFrame
     (aForFrame, false);
   nsStyleContext* bgContext = bgFrame->StyleContext();
-  nscolor bgColor =
-    bgContext->GetVisitedDependentColor(eCSSProperty_background_color);
+  nscolor bgColor = bgContext->
+    GetVisitedDependentColor(&nsStyleBackground::mBackgroundColor);
 
   nsRect innerRect;
   if (
 #ifdef MOZ_XUL
       aStyleContext->GetPseudoType() == CSSPseudoElementType::XULTree
 #else
       false
 #endif
@@ -1036,17 +1036,17 @@ nsCSSRendering::PaintOutline(nsPresConte
   }
 
   uint8_t 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.
   nscolor outlineColor =
-    aStyleContext->GetVisitedDependentColor(eCSSProperty_outline_color);
+    aStyleContext->GetVisitedDependentColor(&nsStyleOutline::mOutlineColor);
   nscolor outlineColors[4] = { outlineColor,
                                outlineColor,
                                outlineColor,
                                outlineColor };
 
   // convert the border widths
   Float outlineWidths[4] = { Float(width / twipsPerPixel),
                              Float(width / twipsPerPixel),
@@ -2235,18 +2235,18 @@ nsCSSRendering::DetermineBackgroundColor
       aFrame->HonorPrintBackgroundSettings()) {
     aDrawBackgroundImage = aPresContext->GetBackgroundImageDraw();
     aDrawBackgroundColor = aPresContext->GetBackgroundColorDraw();
   }
 
   const nsStyleBackground *bg = aStyleContext->StyleBackground();
   nscolor bgColor;
   if (aDrawBackgroundColor) {
-    bgColor =
-      aStyleContext->GetVisitedDependentColor(eCSSProperty_background_color);
+    bgColor = aStyleContext->
+      GetVisitedDependentColor(&nsStyleBackground::mBackgroundColor);
     if (NS_GET_A(bgColor) == 0) {
       aDrawBackgroundColor = false;
     }
   } else {
     // If GetBackgroundColorDraw() is false, we are still expected to
     // draw color in the background of any frame that's not completely
     // transparent, but we are expected to use white instead of whatever
     // color was specified.
--- a/layout/style/nsStyleContext.cpp
+++ b/layout/style/nsStyleContext.cpp
@@ -20,17 +20,16 @@
 #include "nsIStyleRule.h"
 
 #include "nsCOMPtr.h"
 #include "nsStyleSet.h"
 #include "nsIPresShell.h"
 
 #include "nsRuleNode.h"
 #include "nsStyleContext.h"
-#include "mozilla/StyleAnimationValue.h"
 #include "GeckoProfiler.h"
 #include "nsIDocument.h"
 #include "nsPrintfCString.h"
 #include "RubyUtils.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ArenaObjectID.h"
 #include "mozilla/StyleSetHandle.h"
 #include "mozilla/StyleSetHandleInlines.h"
@@ -1383,98 +1382,73 @@ NS_NewStyleContext(nsStyleContext* aPare
 }
 
 nsIPresShell*
 nsStyleContext::Arena()
 {
   return PresContext()->PresShell();
 }
 
-static inline void
-ExtractAnimationValue(nsCSSPropertyID aProperty,
-                      nsStyleContext* aStyleContext,
-                      StyleAnimationValue& aResult)
+template<typename Func>
+static nscolor
+GetVisitedDependentColorInternal(nsStyleContext* aSc, Func aColorFunc)
 {
-  DebugOnly<bool> success =
-    StyleAnimationValue::ExtractComputedValue(aProperty, aStyleContext,
-                                              aResult);
-  MOZ_ASSERT(success,
-             "aProperty must be extractable by StyleAnimationValue");
+  nscolor colors[2];
+  colors[0] = aColorFunc(aSc);
+  if (nsStyleContext* visitedStyle = aSc->GetStyleIfVisited()) {
+    colors[1] = aColorFunc(visitedStyle);
+    return nsStyleContext::
+      CombineVisitedColors(colors, aSc->RelevantLinkVisited());
+  }
+  return colors[0];
 }
 
-static Maybe<nscolor>
-ExtractColor(nsCSSPropertyID aProperty,
-             nsStyleContext *aStyleContext)
+static nscolor
+ExtractColor(nsStyleContext* aContext, const nscolor& aColor)
 {
-  StyleAnimationValue val;
-  ExtractAnimationValue(aProperty, aStyleContext, val);
-  switch (val.GetUnit()) {
-    case StyleAnimationValue::eUnit_Color:
-      return Some(val.GetCSSValueValue()->GetColorValue());
-    case StyleAnimationValue::eUnit_CurrentColor:
-      return Some(aStyleContext->StyleColor()->mColor);
-    case StyleAnimationValue::eUnit_ComplexColor:
-      return Some(aStyleContext->StyleColor()->
-                  CalcComplexColor(val.GetStyleComplexColorValue()));
-    default:
-      return Nothing();
-  }
+  return aColor;
 }
 
 static nscolor
-ExtractColorLenient(nsCSSPropertyID aProperty,
-                    nsStyleContext *aStyleContext)
+ExtractColor(nsStyleContext* aContext, const StyleComplexColor& aColor)
+{
+  return aContext->StyleColor()->CalcComplexColor(aColor);
+}
+
+static nscolor
+ExtractColor(nsStyleContext* aContext, const nsStyleSVGPaint& aPaintServer)
 {
-  return ExtractColor(aProperty, aStyleContext).valueOr(NS_RGBA(0, 0, 0, 0));
+  return aPaintServer.Type() == eStyleSVGPaintType_Color
+    ? aPaintServer.GetColor() : NS_RGBA(0, 0, 0, 0);
 }
 
+#define STYLE_FIELD(struct_, field_) aField == &struct_::field_ ||
+#define STYLE_STRUCT(name_, fields_)                                          \
+  template<> nscolor                                                          \
+  nsStyleContext::GetVisitedDependentColor(                                   \
+    decltype(nsStyle##name_::MOZ_ARG_1 fields_) nsStyle##name_::* aField)     \
+  {                                                                           \
+    MOZ_ASSERT(MOZ_FOR_EACH(STYLE_FIELD, (nsStyle##name_,), fields_) false,   \
+               "Getting visited-dependent color for a field in nsStyle"#name_ \
+               " which is not listed in nsCSSVisitedDependentPropList.h");    \
+    return GetVisitedDependentColorInternal(this,                             \
+                                            [aField](nsStyleContext* sc) {    \
+      return ExtractColor(sc, sc->Style##name_()->*aField);                   \
+    });                                                                       \
+  }
+#include "nsCSSVisitedDependentPropList.h"
+#undef STYLE_STRUCT
+#undef STYLE_FIELD
+
 struct ColorIndexSet {
   uint8_t colorIndex, alphaIndex;
 };
 
 static const ColorIndexSet gVisitedIndices[2] = { { 0, 0 }, { 1, 0 } };
 
-nscolor
-nsStyleContext::GetVisitedDependentColor(nsCSSPropertyID aProperty)
-{
-  NS_ASSERTION(aProperty == eCSSProperty_color ||
-               aProperty == eCSSProperty_background_color ||
-               aProperty == eCSSProperty_border_top_color ||
-               aProperty == eCSSProperty_border_right_color ||
-               aProperty == eCSSProperty_border_bottom_color ||
-               aProperty == eCSSProperty_border_left_color ||
-               aProperty == eCSSProperty_outline_color ||
-               aProperty == eCSSProperty_column_rule_color ||
-               aProperty == eCSSProperty_text_decoration_color ||
-               aProperty == eCSSProperty_text_emphasis_color ||
-               aProperty == eCSSProperty__webkit_text_fill_color ||
-               aProperty == eCSSProperty__webkit_text_stroke_color ||
-               aProperty == eCSSProperty_fill ||
-               aProperty == eCSSProperty_stroke,
-               "we need to add to nsStyleContext::CalcStyleDifference");
-
-  bool isPaintProperty = aProperty == eCSSProperty_fill ||
-                         aProperty == eCSSProperty_stroke;
-
-  nscolor colors[2];
-  colors[0] = isPaintProperty ? ExtractColorLenient(aProperty, this)
-                              : ExtractColor(aProperty, this).value();
-
-  nsStyleContext *visitedStyle = this->GetStyleIfVisited();
-  if (!visitedStyle) {
-    return colors[0];
-  }
-
-  colors[1] = isPaintProperty ? ExtractColorLenient(aProperty, visitedStyle)
-                              : ExtractColor(aProperty, visitedStyle).value();
-
-  return nsStyleContext::CombineVisitedColors(colors,
-                                              this->RelevantLinkVisited());
-}
-
 /* static */ nscolor
 nsStyleContext::CombineVisitedColors(nscolor *aColors, bool aLinkIsVisited)
 {
   if (NS_GET_A(aColors[1]) == 0) {
     // If the style-if-visited is transparent, then just use the
     // unvisited style rather than using the (meaningless) color
     // components of the visited style along with a potentially
     // non-transparent alpha value.
--- a/layout/style/nsStyleContext.h
+++ b/layout/style/nsStyleContext.h
@@ -6,16 +6,17 @@
 /* the interface (to internal code) for retrieving computed style data */
 
 #ifndef _nsStyleContext_h_
 #define _nsStyleContext_h_
 
 #include "mozilla/Assertions.h"
 #include "mozilla/RestyleLogging.h"
 #include "mozilla/StyleContextSource.h"
+#include "mozilla/StyleComplexColor.h"
 #include "nsCSSAnonBoxes.h"
 #include "nsStyleSet.h"
 
 class nsIAtom;
 class nsPresContext;
 
 namespace mozilla {
 enum class CSSPseudoElementType : uint8_t;
@@ -404,21 +405,22 @@ private:
                                            uint32_t* aEqualStructs,
                                            uint32_t* aSamePointerStructs);
 
 public:
   /**
    * Get a color that depends on link-visitedness using this and
    * this->GetStyleIfVisited().
    *
-   * aProperty must be a color-valued property that StyleAnimationValue
-   * knows how to extract.  It must also be a property that we know to
-   * do change handling for in nsStyleContext::CalcDifference.
+   * @param aField A pointer to a member variable in a style struct.
+   *               The member variable and its style struct must have
+   *               been listed in nsCSSVisitedDependentPropList.h.
    */
-  nscolor GetVisitedDependentColor(nsCSSPropertyID aProperty);
+  template<typename T, typename S>
+  nscolor GetVisitedDependentColor(T S::* aField);
 
   /**
    * aColors should be a two element array of nscolor in which the first
    * color is the unvisited color and the second is the visited color.
    *
    * Combine the R, G, and B components of whichever of aColors should
    * be used based on aLinkIsVisited with the A component of aColors[0].
    */
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -1406,16 +1406,32 @@ public:
       mozilla::StyleComplexColor mBorderTopColor;
       mozilla::StyleComplexColor mBorderRightColor;
       mozilla::StyleComplexColor mBorderBottomColor;
       mozilla::StyleComplexColor mBorderLeftColor;
     };
     mozilla::StyleComplexColor mBorderColor[4];
   };
 
+  static mozilla::StyleComplexColor nsStyleBorder::*
+  BorderColorFieldFor(mozilla::Side aSide) {
+    switch (aSide) {
+      case mozilla::eSideTop:
+        return &nsStyleBorder::mBorderTopColor;
+      case mozilla::eSideRight:
+        return &nsStyleBorder::mBorderRightColor;
+      case mozilla::eSideBottom:
+        return &nsStyleBorder::mBorderBottomColor;
+      case mozilla::eSideLeft:
+        return &nsStyleBorder::mBorderLeftColor;
+    }
+    MOZ_ASSERT_UNREACHABLE("Unknown side");
+    return nullptr;
+  }
+
 protected:
   // mComputedBorder holds the CSS2.1 computed border-width values.
   // In particular, these widths take into account the border-style
   // for the relevant side, and the values are rounded to the nearest
   // device pixel (which is not part of the definition of computed
   // values). The presence or absence of a border-image does not
   // affect border-width values.
   nsMargin      mComputedBorder;
--- a/layout/tables/nsTableFrame.cpp
+++ b/layout/tables/nsTableFrame.cpp
@@ -4807,18 +4807,18 @@ GetColorAndStyle(const nsIFrame* aFrame,
   const nsStyleBorder* styleData = aFrame->StyleBorder();
   mozilla::Side physicalSide = aTableWM.PhysicalSide(aSide);
   *aStyle = styleData->GetBorderStyle(physicalSide);
 
   if ((NS_STYLE_BORDER_STYLE_NONE == *aStyle) ||
       (NS_STYLE_BORDER_STYLE_HIDDEN == *aStyle)) {
     return;
   }
-  *aColor = aFrame->StyleContext()->GetVisitedDependentColor(
-    nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_color)[physicalSide]);
+  *aColor = aFrame->StyleContext()->
+    GetVisitedDependentColor(nsStyleBorder::BorderColorFieldFor(physicalSide));
 
   if (aWidth) {
     nscoord width = styleData->GetComputedBorderWidth(physicalSide);
     *aWidth = nsPresContext::AppUnitsToIntCSSPixels(width);
   }
 }
 
 /** coerce the paint style as required by CSS2.1