Bug 59109 Part 1: Adding -moz-text-decoration-color and -moz-text-decoration-style r=dbaron, sr=bzbarsky
authorMasayuki Nakano <masayuki@d-toybox.com>
Thu, 31 Mar 2011 21:26:35 +0900
changeset 64456 003589ae679e4cf2a92c5e1ffd76225749c4b093
parent 64455 44056696abfcd79e78929893b5e220d3724e65cd
child 64457 a0a53ca7935ddb542b676f4d41821faca2c924bd
push id19348
push usermasayuki@d-toybox.com
push dateThu, 31 Mar 2011 12:31:22 +0000
treeherderautoland@24f25bf15372 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdbaron, bzbarsky
bugs59109
milestone2.2a1pre
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 59109 Part 1: Adding -moz-text-decoration-color and -moz-text-decoration-style r=dbaron, sr=bzbarsky
dom/interfaces/css/nsIDOMCSS2Properties.idl
layout/base/nsStyleConsts.h
layout/style/nsCSSKeywordList.h
layout/style/nsCSSPropList.h
layout/style/nsCSSProps.cpp
layout/style/nsCSSProps.h
layout/style/nsComputedDOMStyle.cpp
layout/style/nsComputedDOMStyle.h
layout/style/nsRuleNode.cpp
layout/style/nsStyleAnimation.cpp
layout/style/nsStyleContext.cpp
layout/style/nsStyleStruct.cpp
layout/style/nsStyleStruct.h
layout/style/test/property_database.js
layout/style/test/test_transitions_events.html
layout/style/test/test_transitions_per_property.html
--- a/dom/interfaces/css/nsIDOMCSS2Properties.idl
+++ b/dom/interfaces/css/nsIDOMCSS2Properties.idl
@@ -45,17 +45,17 @@
  * The nsIDOMCSS2Properties interface is a datatype for additional
  * reflection of data already provided in nsIDOMCSSStyleDeclaration in
  * the Document Object Model.
  *
  * For more information on this interface please see
  * http://www.w3.org/TR/DOM-Level-2-Style
  */
 
-[scriptable, uuid(abedfd52-9821-4311-b50c-7ef229b43abf)]
+[scriptable, uuid(6b8fda8f-94f6-4d5c-aa77-17c5270f36c7)]
 interface nsIDOMCSS2Properties : nsISupports
 {
            attribute DOMString        azimuth;
                                         // raises(DOMException) on setting
 
            attribute DOMString        background;
                                         // raises(DOMException) on setting
 
@@ -729,16 +729,22 @@ interface nsIDOMCSS2Properties : nsISupp
                                         // raises(DOMException) on setting 
 
            attribute DOMString        MozWindowShadow;
                                         // raises(DOMException) on setting
 
            attribute DOMString        backgroundSize;
                                         // raises(DOMException) on setting
 
+           attribute DOMString        MozTextDecorationColor;
+                                        // raises(DOMException) on setting
+
+           attribute DOMString        MozTextDecorationStyle;
+                                        // raises(DOMException) on setting
+
            attribute DOMString        MozTransitionProperty;
                                         // raises(DOMException) on setting
 
            attribute DOMString        MozTransitionDuration;
                                         // raises(DOMException) on setting
 
            attribute DOMString        MozTransitionDelay;
                                         // raises(DOMException) on setting
--- a/layout/base/nsStyleConsts.h
+++ b/layout/base/nsStyleConsts.h
@@ -622,16 +622,25 @@ static inline mozilla::css::Side operato
 #define NS_STYLE_TEXT_DECORATION_BLINK          0x08
 #define NS_STYLE_TEXT_DECORATION_PREF_ANCHORS   0x10
 // 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_OVERRIDE_ALL   0x20
 #define NS_STYLE_TEXT_DECORATION_LINES_MASK     (NS_STYLE_TEXT_DECORATION_UNDERLINE | NS_STYLE_TEXT_DECORATION_OVERLINE | NS_STYLE_TEXT_DECORATION_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
+#define NS_STYLE_TEXT_DECORATION_STYLE_MAX      NS_STYLE_TEXT_DECORATION_STYLE_WAVY
+
+// See nsStyleText
 #define NS_STYLE_TEXT_TRANSFORM_NONE            0
 #define NS_STYLE_TEXT_TRANSFORM_CAPITALIZE      1
 #define NS_STYLE_TEXT_TRANSFORM_LOWERCASE       2
 #define NS_STYLE_TEXT_TRANSFORM_UPPERCASE       3
 
 // See nsStyleDisplay
 #define NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE         0
 #define NS_STYLE_TRANSITION_TIMING_FUNCTION_LINEAR       1
--- a/layout/style/nsCSSKeywordList.h
+++ b/layout/style/nsCSSKeywordList.h
@@ -480,16 +480,17 @@ CSS_KEY(uppercase, uppercase)
 CSS_KEY(vertical, vertical)
 CSS_KEY(vertical-text, vertical_text)
 CSS_KEY(visible, visible)
 CSS_KEY(visiblefill, visiblefill)
 CSS_KEY(visiblepainted, visiblepainted)
 CSS_KEY(visiblestroke, visiblestroke)
 CSS_KEY(w-resize, w_resize)
 CSS_KEY(wait, wait)
+CSS_KEY(wavy, wavy)
 CSS_KEY(wider, wider)
 CSS_KEY(window, window)
 CSS_KEY(windowframe, windowframe)
 CSS_KEY(windowtext, windowtext)
 CSS_KEY(write-only, write_only)
 CSS_KEY(x-fast, x_fast)
 CSS_KEY(x-high, x_high)
 CSS_KEY(x-large, x_large)
--- a/layout/style/nsCSSPropList.h
+++ b/layout/style/nsCSSPropList.h
@@ -2160,16 +2160,37 @@ CSS_PROP_TEXTRESET(
     TextDecoration,
     CSS_PROPERTY_PARSE_VALUE |
         CSS_PROPERTY_VALUE_PARSER_FUNCTION |
         CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE,
     0,
     kTextDecorationKTable,
     offsetof(nsStyleTextReset, mTextDecoration),
     eStyleAnimType_EnumU8)
+CSS_PROP_TEXTRESET(
+    -moz-text-decoration-color,
+    text_decoration_color,
+    CSS_PROP_DOMPROP_PREFIXED(TextDecorationColor),
+    CSS_PROPERTY_PARSE_VALUE |
+        CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE |
+        CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED,
+    VARIANT_HCK,
+    kBorderColorKTable,
+    CSS_PROP_NO_OFFSET,
+    eStyleAnimType_Custom)
+CSS_PROP_TEXTRESET(
+    -moz-text-decoration-style,
+    text_decoration_style,
+    CSS_PROP_DOMPROP_PREFIXED(TextDecorationStyle),
+    CSS_PROPERTY_PARSE_VALUE |
+        CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE,
+    VARIANT_HK,
+    kTextDecorationStyleKTable,
+    CSS_PROP_NO_OFFSET,
+    eStyleAnimType_Custom)
 CSS_PROP_TEXT(
     text-indent,
     text_indent,
     TextIndent,
     CSS_PROPERTY_PARSE_VALUE |
         CSS_PROPERTY_STORES_CALC,
     VARIANT_HLP | VARIANT_CALC,
     nsnull,
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -1233,16 +1233,26 @@ const PRInt32 nsCSSProps::kTextDecoratio
   eCSSKeyword_underline, NS_STYLE_TEXT_DECORATION_UNDERLINE,
   eCSSKeyword_overline, NS_STYLE_TEXT_DECORATION_OVERLINE,
   eCSSKeyword_line_through, NS_STYLE_TEXT_DECORATION_LINE_THROUGH,
   eCSSKeyword_blink, NS_STYLE_TEXT_DECORATION_BLINK,
   eCSSKeyword__moz_anchor_decoration, NS_STYLE_TEXT_DECORATION_PREF_ANCHORS,
   eCSSKeyword_UNKNOWN,-1
 };
 
+const PRInt32 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
+};
+
 const PRInt32 nsCSSProps::kTextTransformKTable[] = {
   eCSSKeyword_none, NS_STYLE_TEXT_TRANSFORM_NONE,
   eCSSKeyword_capitalize, NS_STYLE_TEXT_TRANSFORM_CAPITALIZE,
   eCSSKeyword_lowercase, NS_STYLE_TEXT_TRANSFORM_LOWERCASE,
   eCSSKeyword_uppercase, NS_STYLE_TEXT_TRANSFORM_UPPERCASE,
   eCSSKeyword_UNKNOWN,-1
 };
 
--- a/layout/style/nsCSSProps.h
+++ b/layout/style/nsCSSProps.h
@@ -397,16 +397,17 @@ public:
   static const PRInt32 kSpeakHeaderKTable[];
   static const PRInt32 kSpeakNumeralKTable[];
   static const PRInt32 kSpeakPunctuationKTable[];
   static const PRInt32 kSpeechRateKTable[];
   static const PRInt32 kStackSizingKTable[];
   static const PRInt32 kTableLayoutKTable[];
   static const PRInt32 kTextAlignKTable[];
   static const PRInt32 kTextDecorationKTable[];
+  static const PRInt32 kTextDecorationStyleKTable[];
   static const PRInt32 kTextTransformKTable[];
   static const PRInt32 kTransitionTimingFunctionKTable[];
   static const PRInt32 kUnicodeBidiKTable[];
   static const PRInt32 kUserFocusKTable[];
   static const PRInt32 kUserInputKTable[];
   static const PRInt32 kUserModifyKTable[];
   static const PRInt32 kUserSelectKTable[];
   static const PRInt32 kVerticalAlignKTable[];
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -2280,16 +2280,45 @@ nsComputedDOMStyle::DoGetTextDecoration(
                                        decorationString);
     val->SetString(decorationString);
   }
 
   return val;
 }
 
 nsIDOMCSSValue*
+nsComputedDOMStyle::DoGetMozTextDecorationColor()
+{
+  nsROCSSPrimitiveValue* val = GetROCSSPrimitiveValue();
+
+  nscolor color;
+  PRBool isForeground;
+  GetStyleTextReset()->GetDecorationColor(color, isForeground);
+  if (isForeground) {
+    color = GetStyleColor()->mColor;
+  }
+
+  SetToRGBAColor(val, color);
+
+  return val;
+}
+
+nsIDOMCSSValue*
+nsComputedDOMStyle::DoGetMozTextDecorationStyle()
+{
+  nsROCSSPrimitiveValue* val = GetROCSSPrimitiveValue();
+
+  val->SetIdent(
+    nsCSSProps::ValueToKeywordEnum(GetStyleTextReset()->GetDecorationStyle(),
+                                   nsCSSProps::kTextDecorationStyleKTable));
+
+  return val;
+}
+
+nsIDOMCSSValue*
 nsComputedDOMStyle::DoGetTextIndent()
 {
   nsROCSSPrimitiveValue *val = GetROCSSPrimitiveValue();
   SetValueToCoord(val, GetStyleText()->mTextIndent, PR_FALSE,
                   &nsComputedDOMStyle::GetCBContentWidth);
   return val;
 }
 
@@ -4109,17 +4138,19 @@ nsComputedDOMStyle::GetQueryableProperty
     COMPUTED_STYLE_MAP_ENTRY(stroke_dasharray,              StrokeDasharray),
     COMPUTED_STYLE_MAP_ENTRY(stroke_dashoffset,             StrokeDashoffset),
     COMPUTED_STYLE_MAP_ENTRY(stroke_linecap,                StrokeLinecap),
     COMPUTED_STYLE_MAP_ENTRY(stroke_linejoin,               StrokeLinejoin),
     COMPUTED_STYLE_MAP_ENTRY(stroke_miterlimit,             StrokeMiterlimit),
     COMPUTED_STYLE_MAP_ENTRY(stroke_opacity,                StrokeOpacity),
     COMPUTED_STYLE_MAP_ENTRY(stroke_width,                  StrokeWidth),
     COMPUTED_STYLE_MAP_ENTRY(text_anchor,                   TextAnchor),
-    COMPUTED_STYLE_MAP_ENTRY(text_rendering,                TextRendering)
+    COMPUTED_STYLE_MAP_ENTRY(text_rendering,                TextRendering),
+    COMPUTED_STYLE_MAP_ENTRY(text_decoration_color,         MozTextDecorationColor),
+    COMPUTED_STYLE_MAP_ENTRY(text_decoration_style,         MozTextDecorationStyle)
 
   };
 
   *aLength = NS_ARRAY_LENGTH(map);
 
   return map;
 }
 
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -302,16 +302,18 @@ private:
   nsIDOMCSSValue* DoGetListStylePosition();
   nsIDOMCSSValue* DoGetListStyleType();
   nsIDOMCSSValue* DoGetImageRegion();
 
   /* Text Properties */
   nsIDOMCSSValue* DoGetLineHeight();
   nsIDOMCSSValue* DoGetTextAlign();
   nsIDOMCSSValue* DoGetTextDecoration();
+  nsIDOMCSSValue* DoGetMozTextDecorationColor();
+  nsIDOMCSSValue* DoGetMozTextDecorationStyle();
   nsIDOMCSSValue* DoGetTextIndent();
   nsIDOMCSSValue* DoGetTextTransform();
   nsIDOMCSSValue* DoGetTextShadow();
   nsIDOMCSSValue* DoGetLetterSpacing();
   nsIDOMCSSValue* DoGetWordSpacing();
   nsIDOMCSSValue* DoGetWhiteSpace();
   nsIDOMCSSValue* DoGetWordWrap();
   nsIDOMCSSValue* DoGetMozTabSize();
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -3392,16 +3392,62 @@ nsRuleNode::ComputeTextResetData(void* a
     }
   } else if (eCSSUnit_Inherit == decorationValue->GetUnit()) {
     canStoreInRuleTree = PR_FALSE;
     text->mTextDecoration = parentText->mTextDecoration;
   } else if (eCSSUnit_Initial == decorationValue->GetUnit()) {
     text->mTextDecoration = NS_STYLE_TEXT_DECORATION_NONE;
   }
 
+  // text-decoration-color: color, string, enum, inherit, initial
+  const nsCSSValue* decorationColorValue =
+    aRuleData->ValueForTextDecorationColor();
+  nscolor decorationColor;
+  if (eCSSUnit_Inherit == decorationColorValue->GetUnit()) {
+    canStoreInRuleTree = PR_FALSE;
+    if (parentContext) {
+      PRBool isForeground;
+      parentText->GetDecorationColor(decorationColor, isForeground);
+      if (isForeground) {
+        text->SetDecorationColor(parentContext->GetStyleColor()->mColor);
+      } else {
+        text->SetDecorationColor(decorationColor);
+      }
+    } else {
+      text->SetDecorationColorToForeground();
+    }
+  }
+  else if (SetColor(*decorationColorValue, 0, mPresContext, aContext,
+                    decorationColor, canStoreInRuleTree)) {
+    text->SetDecorationColor(decorationColor);
+  }
+  else if (eCSSUnit_Initial == decorationColorValue->GetUnit() ||
+           eCSSUnit_Enumerated == decorationColorValue->GetUnit()) {
+    NS_ABORT_IF_FALSE(eCSSUnit_Enumerated != decorationColorValue->GetUnit() ||
+                      decorationColorValue->GetIntValue() ==
+                        NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR,
+                      "unexpected enumerated value");
+    text->SetDecorationColorToForeground();
+  }
+  else if (eCSSUnit_Initial == decorationColorValue->GetUnit()) {
+    text->SetDecorationColorToForeground();
+  }
+
+  // text-decoration-style: enum, inherit, initial
+  const nsCSSValue* decorationStyleValue =
+    aRuleData->ValueForTextDecorationStyle();
+  if (eCSSUnit_Enumerated == decorationStyleValue->GetUnit()) {
+    text->SetDecorationStyle(decorationStyleValue->GetIntValue());
+  } else if (eCSSUnit_Inherit == decorationStyleValue->GetUnit()) {
+    text->SetDecorationStyle(parentText->GetDecorationStyle());
+    canStoreInRuleTree = PR_FALSE;
+  } else if (eCSSUnit_Initial == decorationStyleValue->GetUnit()) {
+    text->SetDecorationStyle(NS_STYLE_TEXT_DECORATION_STYLE_SOLID);
+  }
+
   // unicode-bidi: enum, inherit, initial
   SetDiscrete(*aRuleData->ValueForUnicodeBidi(), text->mUnicodeBidi, canStoreInRuleTree,
               SETDSC_ENUMERATED, parentText->mUnicodeBidi,
               NS_STYLE_UNICODE_BIDI_NORMAL, 0, 0, 0, 0);
 
   COMPUTE_END_RESET(TextReset, text)
 }
 
--- a/layout/style/nsStyleAnimation.cpp
+++ b/layout/style/nsStyleAnimation.cpp
@@ -2245,16 +2245,37 @@ nsStyleAnimation::ExtractComputedValue(n
             aComputedValue.SetAutoValue();
           } else {
             aComputedValue.SetIntValue(styleColumn->mColumnCount,
                                        eUnit_Integer);
           }
           break;
         }
 
+        case eCSSProperty_text_decoration_color: {
+          const nsStyleTextReset *styleTextReset =
+            static_cast<const nsStyleTextReset*>(styleStruct);
+          nscolor color;
+          PRBool isForeground;
+          styleTextReset->GetDecorationColor(color, isForeground);
+          if (isForeground) {
+            color = aStyleContext->GetStyleColor()->mColor;
+          }
+          aComputedValue.SetColorValue(color);
+          break;
+        }
+
+        case eCSSProperty_text_decoration_style: {
+          PRUint8 decorationStyle =
+            static_cast<const nsStyleTextReset*>(styleStruct)->
+              GetDecorationStyle();
+          aComputedValue.SetIntValue(decorationStyle, eUnit_Enumerated);
+          break;
+        }
+
         case eCSSProperty_border_spacing: {
           const nsStyleTableBorder *styleTableBorder =
             static_cast<const nsStyleTableBorder*>(styleStruct);
           nsAutoPtr<nsCSSValuePair> pair(new nsCSSValuePair);
           if (!pair) {
             return PR_FALSE;
           }
           nscoordToCSSValue(styleTableBorder->mBorderSpacingX, pair->mXValue);
--- a/layout/style/nsStyleContext.cpp
+++ b/layout/style/nsStyleContext.cpp
@@ -561,16 +561,32 @@ nsStyleContext::CalcStyleDifference(nsSt
       if (thisVisColumn->mColumnRuleColor != otherVisColumn->mColumnRuleColor ||
           thisVisColumn->mColumnRuleColorIsForeground !=
             otherVisColumn->mColumnRuleColorIsForeground) {
         change = PR_TRUE;
       }
     }
 
     // NB: Calling Peek on |this|, not |thisVis| (see above).
+    if (!change && PeekStyleTextReset()) {
+      const nsStyleTextReset *thisVisTextReset = thisVis->GetStyleTextReset();
+      const nsStyleTextReset *otherVisTextReset = otherVis->GetStyleTextReset();
+      nscolor thisVisDecColor, otherVisDecColor;
+      PRBool thisVisDecColorIsFG, otherVisDecColorIsFG;
+      thisVisTextReset->GetDecorationColor(thisVisDecColor,
+                                           thisVisDecColorIsFG);
+      otherVisTextReset->GetDecorationColor(otherVisDecColor,
+                                            otherVisDecColorIsFG);
+      if (thisVisDecColorIsFG != otherVisDecColorIsFG ||
+          (!thisVisDecColorIsFG && thisVisDecColor != otherVisDecColor)) {
+        change = PR_TRUE;
+      }
+    }
+
+    // NB: Calling Peek on |this|, not |thisVis| (see above).
     if (!change && PeekStyleSVG()) {
       const nsStyleSVG *thisVisSVG = thisVis->GetStyleSVG();
       const nsStyleSVG *otherVisSVG = otherVis->GetStyleSVG();
       if (thisVisSVG->mFill != otherVisSVG->mFill ||
           thisVisSVG->mStroke != otherVisSVG->mStroke) {
         change = PR_TRUE;
       }
     }
@@ -721,16 +737,17 @@ nsStyleContext::GetVisitedDependentColor
   NS_ASSERTION(aProperty == eCSSProperty_color ||
                aProperty == eCSSProperty_background_color ||
                aProperty == eCSSProperty_border_top_color ||
                aProperty == eCSSProperty_border_right_color_value ||
                aProperty == eCSSProperty_border_bottom_color ||
                aProperty == eCSSProperty_border_left_color_value ||
                aProperty == eCSSProperty_outline_color ||
                aProperty == eCSSProperty__moz_column_rule_color ||
+               aProperty == eCSSProperty_text_decoration_color ||
                aProperty == eCSSProperty_fill ||
                aProperty == eCSSProperty_stroke,
                "we need to add to nsStyleContext::CalcStyleDifference");
 
   nscolor colors[2];
   colors[0] = ExtractColor(aProperty, this);
 
   nsStyleContext *visitedStyle = this->GetStyleIfVisited();
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -2526,16 +2526,19 @@ nsChangeHint nsStyleQuotes::MaxDifferenc
 // nsStyleTextReset
 //
 
 nsStyleTextReset::nsStyleTextReset(void) 
 { 
   MOZ_COUNT_CTOR(nsStyleTextReset);
   mVerticalAlign.SetIntValue(NS_STYLE_VERTICAL_ALIGN_BASELINE, eStyleUnit_Enumerated);
   mTextDecoration = NS_STYLE_TEXT_DECORATION_NONE;
+  mTextDecorationColor = NS_RGB(0,0,0);
+  mTextDecorationStyle =
+    NS_STYLE_TEXT_DECORATION_STYLE_SOLID | BORDER_COLOR_FOREGROUND;
   mUnicodeBidi = NS_STYLE_UNICODE_BIDI_NORMAL;
 }
 
 nsStyleTextReset::nsStyleTextReset(const nsStyleTextReset& aSource) 
 { 
   MOZ_COUNT_CTOR(nsStyleTextReset);
   memcpy((nsStyleTextReset*)this, &aSource, sizeof(nsStyleTextReset));
 }
@@ -2544,22 +2547,45 @@ nsStyleTextReset::~nsStyleTextReset(void
 {
   MOZ_COUNT_DTOR(nsStyleTextReset);
 }
 
 nsChangeHint nsStyleTextReset::CalcDifference(const nsStyleTextReset& aOther) const
 {
   if (mVerticalAlign == aOther.mVerticalAlign
       && mUnicodeBidi == aOther.mUnicodeBidi) {
-    if (mTextDecoration != aOther.mTextDecoration) {
-      // Reflow for blink changes, repaint for others
-      return
-        (mTextDecoration & NS_STYLE_TEXT_DECORATION_BLINK) ==
-        (aOther.mTextDecoration & NS_STYLE_TEXT_DECORATION_BLINK) ?
-          NS_STYLE_HINT_VISUAL : NS_STYLE_HINT_REFLOW;
+    PRUint8 lineStyle = GetDecorationStyle();
+    PRUint8 otherLineStyle = aOther.GetDecorationStyle();
+    if (mTextDecoration != aOther.mTextDecoration ||
+        lineStyle != otherLineStyle) {
+      // Reflow for blink changes
+      if ((mTextDecoration & NS_STYLE_TEXT_DECORATION_BLINK) !=
+            (aOther.mTextDecoration & NS_STYLE_TEXT_DECORATION_BLINK)) {
+        return NS_STYLE_HINT_REFLOW;
+      }
+      // Reflow for decoration line style changes only to or from double or
+      // wave because that may cause overflow area changes
+      if (lineStyle == NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE ||
+          lineStyle == NS_STYLE_TEXT_DECORATION_STYLE_WAVY ||
+          otherLineStyle == NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE ||
+          otherLineStyle == NS_STYLE_TEXT_DECORATION_STYLE_WAVY) {
+        return NS_STYLE_HINT_REFLOW;
+      }
+      // Repaint for other style decoration lines because they must be in
+      // default overflow rect
+      return NS_STYLE_HINT_VISUAL;
+    }
+
+    // Repaint for decoration color changes
+    nscolor decColor, otherDecColor;
+    PRBool isFG, otherIsFG;
+    GetDecorationColor(decColor, isFG);
+    aOther.GetDecorationColor(otherDecColor, otherIsFG);
+    if (isFG != otherIsFG || (!isFG && decColor != otherDecColor)) {
+      return NS_STYLE_HINT_VISUAL;
     }
 
     return NS_STYLE_HINT_NONE;
   }
   return NS_STYLE_HINT_REFLOW;
 }
 
 #ifdef DEBUG
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -1160,26 +1160,67 @@ struct nsStyleTextReset {
   void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW {
     return aContext->AllocateFromShell(sz);
   }
   void Destroy(nsPresContext* aContext) {
     this->~nsStyleTextReset();
     aContext->FreeToShell(sizeof(nsStyleTextReset), this);
   }
 
+  PRUint8 GetDecorationStyle() const
+  {
+    return (mTextDecorationStyle & BORDER_STYLE_MASK);
+  }
+
+  void SetDecorationStyle(PRUint8 aStyle)
+  {
+    NS_ABORT_IF_FALSE((aStyle & BORDER_STYLE_MASK) == aStyle,
+                      "style doesn't fit");
+    mTextDecorationStyle &= ~BORDER_STYLE_MASK;
+    mTextDecorationStyle |= (aStyle & BORDER_STYLE_MASK);
+  }
+
+  void GetDecorationColor(nscolor& aColor, PRBool& aForeground) const
+  {
+    aForeground = PR_FALSE;
+    if ((mTextDecorationStyle & BORDER_COLOR_SPECIAL) == 0) {
+      aColor = mTextDecorationColor;
+    } else if (mTextDecorationStyle & BORDER_COLOR_FOREGROUND) {
+      aForeground = PR_TRUE;
+    } else {
+      NS_NOTREACHED("OUTLINE_COLOR_INITIAL should not be set here");
+    }
+  }
+
+  void SetDecorationColor(nscolor aColor)
+  {
+    mTextDecorationColor = aColor;
+    mTextDecorationStyle &= ~BORDER_COLOR_SPECIAL;
+  }
+
+  void SetDecorationColorToForeground()
+  {
+    mTextDecorationStyle &= ~BORDER_COLOR_SPECIAL;
+    mTextDecorationStyle |= BORDER_COLOR_FOREGROUND;
+  }
+
   nsChangeHint CalcDifference(const nsStyleTextReset& aOther) const;
 #ifdef DEBUG
   static nsChangeHint MaxDifference();
 #endif
   static PRBool ForceCompare() { return PR_FALSE; }
 
+  nsStyleCoord  mVerticalAlign;         // [reset] coord, percent, calc, enum (see nsStyleConsts.h)
+
   PRUint8 mTextDecoration;              // [reset] see nsStyleConsts.h
   PRUint8 mUnicodeBidi;                 // [reset] see nsStyleConsts.h
+protected:
+  PRUint8 mTextDecorationStyle;         // [reset] see nsStyleConsts.h
 
-  nsStyleCoord  mVerticalAlign;         // [reset] coord, percent, calc, enum (see nsStyleConsts.h)
+  nscolor mTextDecorationColor;         // [reset] the colors to use for a decoration lines, not used at currentColor
 };
 
 struct nsStyleText {
   nsStyleText(void);
   nsStyleText(const nsStyleText& aOther);
   ~nsStyleText(void);
 
   void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW {
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -2517,16 +2517,33 @@ var gCSSProperties = {
 	"text-decoration": {
 		domProp: "textDecoration",
 		inherited: false,
 		type: CSS_TYPE_LONGHAND,
 		initial_values: [ "none" ],
 		other_values: [ "underline", "overline", "line-through", "blink line-through underline", "underline overline line-through blink", "-moz-anchor-decoration", "blink -moz-anchor-decoration" ],
 		invalid_values: [ "underline none", "none underline", "line-through blink line-through" ]
 	},
+	"-moz-text-decoration-color": {
+		domProp: "MozTextDecorationColor",
+		inherited: false,
+		type: CSS_TYPE_LONGHAND,
+		prerequisites: { "color": "black" },
+		initial_values: [ "currentColor", "-moz-use-text-color" ],
+		other_values: [ "green", "rgba(255,128,0,0.5)", "transparent" ],
+		invalid_values: [ "#0", "#00", "#0000", "#00000", "#0000000", "#00000000", "#000000000" ]
+	},
+	"-moz-text-decoration-style": {
+		domProp: "MozTextDecorationStyle",
+		inherited: false,
+		type: CSS_TYPE_LONGHAND,
+		initial_values: [ "solid" ],
+		other_values: [ "double", "dotted", "dashed", "wavy", "-moz-none" ],
+		invalid_values: [ "none", "groove", "ridge", "inset", "outset", "solid dashed", "wave" ]
+	},
 	"text-indent": {
 		domProp: "textIndent",
 		inherited: true,
 		type: CSS_TYPE_LONGHAND,
 		initial_values: [ "0", "-moz-calc(3em - 5em + 2px + 2em - 2px)" ],
 		other_values: [ "2em", "5%", "-10px",
 			"-moz-calc(2px)",
 			"-moz-calc(-2px)",
--- a/layout/style/test/test_transitions_events.html
+++ b/layout/style/test/test_transitions_events.html
@@ -16,16 +16,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 #one { -moz-transition-duration: 500ms; -moz-transition-property: all; }
 #two { -moz-transition: margin-left 1s; }
 #three { -moz-transition: margin 0.5s 0.25s; }
 
 #four, #five, #six, #seven::before, #seven::after {
   -moz-transition: 500ms color;
   border-color: black; /* don't derive from color */
   -moz-column-rule-color: black; /* don't derive from color */
+  -moz-text-decoration-color: black; /* don't derive from color */
 }
 
 #four {
   /* give the reversing transition a long duration; the reversing will
      still be quick */
   -moz-transition-duration: 30s;
   -moz-transition-timing-function: cubic-bezier(0, 1, 1, 0);
 }
@@ -66,16 +67,17 @@ function cs(id) { return getComputedStyl
 
 var got_one_root = false;
 var got_one_target = false;
 var got_one_target_bordertop = false;
 var got_one_target_borderright = false;
 var got_one_target_borderbottom = false;
 var got_one_target_borderleft = false;
 var got_one_target_columnrule = false;
+var got_one_target_textdecorationcolor = false;
 var got_two_target = false;
 var got_three_top = false;
 var got_three_right = false;
 var got_three_bottom = false;
 var got_three_left = false;
 var got_four_root = false;
 var got_body = false;
 var did_stops = false;
@@ -162,16 +164,22 @@ document.documentElement.addEventListene
         event.stopPropagation();
         break;
       case "-moz-column-rule-color":
         ok(!got_one_target_columnrule,
            "transitionend on one on target (-moz-column-rule-color)");
         got_one_target_columnrule = true;
         event.stopPropagation();
         break;
+      case "-moz-text-decoration-color":
+        ok(!got_one_target_textdecorationcolor,
+           "transitionend on one on target (-moz-text-decoration-color)");
+        got_one_target_textdecorationcolor = true;
+        event.stopPropagation();
+        break;
       default:
         ok(false, "unexpected property name " + event.propertyName +
                   " for transitionend on one on target");
     }
     is(event.elapsedTime, 0.5,
        "elapsedTime for transitionend on one");
     is(cs("one").getPropertyValue(event.propertyName), "rgb(0, 255, 0)",
        "computed style of " + event.propertyName + " for transitionend on one");
--- a/layout/style/test/test_transitions_per_property.html
+++ b/layout/style/test/test_transitions_per_property.html
@@ -63,16 +63,18 @@ var supported_properties = {
                                 test_border_color_transition ],
     "-moz-column-rule-width": [ test_length_transition ],
     "-moz-column-width": [ test_length_transition ],
     "-moz-image-region": [ test_rect_transition ],
     "-moz-outline-radius-bottomleft": [ test_radius_transition ],
     "-moz-outline-radius-bottomright": [ test_radius_transition ],
     "-moz-outline-radius-topleft": [ test_radius_transition ],
     "-moz-outline-radius-topright": [ test_radius_transition ],
+    "-moz-text-decoration-color": [ test_color_transition,
+                                    test_border_color_transition ],
     "-moz-transform": [ test_transform_transition ],
     "-moz-transform-origin": [ test_length_pair_transition,
                                test_length_percent_pair_transition ],
     "background-color": [ test_color_transition ],
     "background-position": [ test_background_position_transition ],
     "background-size": [ test_background_size_transition ],
     "border-bottom-color": [ test_color_transition,
                              test_border_color_transition ],