Bug 898361 - [CSS Filters] Implement parsing for drop-shadow. r=heycam
☠☠ backed out by e926c98528ff ☠ ☠
authorDirk Schulze <krit@webkit.org>
Wed, 31 Jul 2013 09:26:45 -0400
changeset 153061 e27bd873b413e11e3c197b3b07f79d79899d4d22
parent 153060 9c6dc3897c17585bb1b73fefd986302f66c94443
child 153062 2e7ae68148a6c5fa6a5bc40be4f47479419e0ae2
push id2859
push userakeybl@mozilla.com
push dateMon, 16 Sep 2013 19:14:59 +0000
treeherdermozilla-beta@87d3c51cd2bf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersheycam
bugs898361
milestone25.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 898361 - [CSS Filters] Implement parsing for drop-shadow. r=heycam
layout/style/nsCSSKeywordList.h
layout/style/nsCSSParser.cpp
layout/style/nsComputedDOMStyle.cpp
layout/style/nsComputedDOMStyle.h
layout/style/nsRuleNode.cpp
layout/style/nsRuleNode.h
layout/style/nsStyleStruct.cpp
layout/style/nsStyleStruct.h
layout/style/test/property_database.js
--- a/layout/style/nsCSSKeywordList.h
+++ b/layout/style/nsCSSKeywordList.h
@@ -237,16 +237,17 @@ CSS_KEY(default, default)
 CSS_KEY(deg, deg)
 CSS_KEY(diagonal-fractions, diagonal_fractions)
 CSS_KEY(dialog, dialog)
 CSS_KEY(disabled, disabled)
 CSS_KEY(disc, disc)
 CSS_KEY(discretionary-ligatures, discretionary_ligatures)
 CSS_KEY(dotted, dotted)
 CSS_KEY(double, double)
+CSS_KEY(drop-shadow, drop_shadow)
 CSS_KEY(e-resize, e_resize)
 CSS_KEY(each-box, each_box)
 CSS_KEY(ease, ease)
 CSS_KEY(ease-in, ease_in)
 CSS_KEY(ease-in-out, ease_in_out)
 CSS_KEY(ease-out, ease_out)
 CSS_KEY(element, element)
 CSS_KEY(elements, elements)
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -602,16 +602,17 @@ protected:
                               InfallibleTArray<nsCSSValue>& aOutput);
 
   /* Functions for transform-origin/perspective-origin Parsing */
   bool ParseTransformOrigin(bool aPerspective);
 
   /* Functions for filter parsing */
   bool ParseFilter();
   bool ParseSingleFilter(nsCSSValue* aValue);
+  bool ParseDropShadow(nsCSSValue* aValue);
 
   /* Find and return the namespace ID associated with aPrefix.
      If aPrefix has not been declared in an @namespace rule, returns
      kNameSpaceID_Unknown. */
   int32_t GetNamespaceIdForPrefix(const nsString& aPrefix);
 
   /* Find the correct default namespace, and set it on aSelector. */
   void SetDefaultNamespaceOnSelector(nsCSSSelector& aSelector);
@@ -10047,16 +10048,42 @@ bool CSSParserImpl::ParseTransformOrigin
     }
 
     AppendValue(prop, value);
   }
   return true;
 }
 
 /**
+ * Reads a drop-shadow value. At the moment the Filter Effects specification
+ * just expects one shadow item. Should this ever change to a list of shadow
+ * items, use ParseShadowList instead.
+ */
+bool
+CSSParserImpl::ParseDropShadow(nsCSSValue* aValue)
+{
+  // Use nsCSSValueList to reuse the shadow resolving code in
+  // nsRuleNode and nsComputedDOMStyle.
+  nsCSSValue shadow;
+  nsCSSValueList* cur = shadow.SetListValue();
+  if (!ParseShadowItem(cur->mValue, false))
+    return false;
+
+  if (!ExpectSymbol(')', true))
+    return false;
+
+  nsCSSValue::Array* dropShadow = aValue->InitFunction(eCSSKeyword_drop_shadow, 1);
+
+  // Copy things over.
+  dropShadow->Item(1) = shadow;
+
+  return true;
+}
+
+/**
  * Reads a single url or filter function from the tokenizer stream, reporting an
  * error if something goes wrong.
  */
 bool
 CSSParserImpl::ParseSingleFilter(nsCSSValue* aValue)
 {
   if (ParseVariant(*aValue, VARIANT_URL, nullptr)) {
     return true;
@@ -10073,21 +10100,34 @@ CSSParserImpl::ParseSingleFilter(nsCSSVa
     return false;
   }
 
   if (mToken.mType != eCSSToken_Function) {
     REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURLOrFilterFunction);
     return false;
   }
 
+  nsCSSKeyword functionName = nsCSSKeywords::LookupKeyword(mToken.mIdent);
+  // Parse drop-shadow independently of the other filter functions
+  // because of its more complex characteristics.
+  if (functionName == eCSSKeyword_drop_shadow) {
+    if (ParseDropShadow(aValue)) {
+      return true;
+    } else {
+      // Unrecognized filter function.
+      REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURLOrFilterFunction);
+      SkipUntil(')');
+      return false;
+    }
+  }
+
   // Set up the parsing rules based on the filter function.
   int32_t variantMask = VARIANT_PN;
   bool rejectNegativeArgument = true;
   bool clampArgumentToOne = false;
-  nsCSSKeyword functionName = nsCSSKeywords::LookupKeyword(mToken.mIdent);
   switch (functionName) {
     case eCSSKeyword_blur:
       variantMask = VARIANT_LCALC | VARIANT_NONNEGATIVE_DIMENSION;
       // VARIANT_NONNEGATIVE_DIMENSION will already reject negative lengths.
       rejectNegativeArgument = false;
       break;
     case eCSSKeyword_brightness:
     case eCSSKeyword_contrast:
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -4493,28 +4493,31 @@ nsComputedDOMStyle::SetCssTextToCoord(ns
   nsROCSSPrimitiveValue* value = new nsROCSSPrimitiveValue;
   bool clampNegativeCalc = true;
   SetValueToCoord(value, aCoord, clampNegativeCalc);
   value->GetCssText(aCssText);
   delete value;
 }
 
 static void
-GetFilterFunctionName(nsAString& aString, nsStyleFilter::Type mType)
-{
-  switch (mType) {
+GetFilterFunctionName(nsAString& aString, nsStyleFilter::Type aType)
+{
+  switch (aType) {
     case nsStyleFilter::Type::eBlur:
       aString.AssignLiteral("blur(");
       break;
     case nsStyleFilter::Type::eBrightness:
       aString.AssignLiteral("brightness(");
       break;
     case nsStyleFilter::Type::eContrast:
       aString.AssignLiteral("contrast(");
       break;
+    case nsStyleFilter::Type::eDropShadow:
+      aString.AssignLiteral("drop-shadow(");
+      break;
     case nsStyleFilter::Type::eGrayscale:
       aString.AssignLiteral("grayscale(");
       break;
     case nsStyleFilter::Type::eHueRotate:
       aString.AssignLiteral("hue-rotate(");
       break;
     case nsStyleFilter::Type::eInvert:
       aString.AssignLiteral("invert(");
@@ -4528,35 +4531,44 @@ GetFilterFunctionName(nsAString& aString
     case nsStyleFilter::Type::eSepia:
       aString.AssignLiteral("sepia(");
       break;
     default:
       NS_NOTREACHED("unrecognized filter type");
   }
 }
 
-nsROCSSPrimitiveValue*
+CSSValue*
 nsComputedDOMStyle::CreatePrimitiveValueForStyleFilter(
   const nsStyleFilter& aStyleFilter)
 {
   nsROCSSPrimitiveValue* value = new nsROCSSPrimitiveValue;
-
   // Handle url().
-  if (nsStyleFilter::Type::eURL == aStyleFilter.mType) {
-    value->SetURI(aStyleFilter.mURL);
+  if (nsStyleFilter::Type::eURL == aStyleFilter.GetType()) {
+    value->SetURI(aStyleFilter.GetURL());
     return value;
   }
 
   // Filter function name and opening parenthesis.
   nsAutoString filterFunctionString;
-  GetFilterFunctionName(filterFunctionString, aStyleFilter.mType);
-
-  // Filter function argument.
+  GetFilterFunctionName(filterFunctionString, aStyleFilter.GetType());
+
   nsAutoString argumentString;
-  SetCssTextToCoord(argumentString, aStyleFilter.mFilterParameter);
+  if (nsStyleFilter::Type::eDropShadow == aStyleFilter.GetType()) {
+    // Handle drop-shadow()
+    nsRefPtr<CSSValue> shadowValue =
+      GetCSSShadowArray(aStyleFilter.GetDropShadow(),
+                        StyleColor()->mColor,
+                        false);
+    ErrorResult dummy;
+    shadowValue->GetCssText(argumentString, dummy);
+  } else {
+    // Filter function argument.
+    SetCssTextToCoord(argumentString, aStyleFilter.GetFilterParameter());
+  }
   filterFunctionString.Append(argumentString);
 
   // Filter function closing parenthesis.
   filterFunctionString.AppendLiteral(")");
 
   value->SetString(filterFunctionString);
   return value;
 }
@@ -4569,18 +4581,17 @@ nsComputedDOMStyle::DoGetFilter()
   if (filters.IsEmpty()) {
     nsROCSSPrimitiveValue* value = new nsROCSSPrimitiveValue;
     value->SetIdent(eCSSKeyword_none);
     return value;
   }
 
   nsDOMCSSValueList* valueList = GetROCSSValueList(false);
   for(uint32_t i = 0; i < filters.Length(); i++) {
-    nsROCSSPrimitiveValue* value =
-      CreatePrimitiveValueForStyleFilter(filters[i]);
+    CSSValue* value = CreatePrimitiveValueForStyleFilter(filters[i]);
     valueList->AppendCSSValue(value);
   }
   return valueList;
 }
 
 CSSValue*
 nsComputedDOMStyle::DoGetMask()
 {
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -489,17 +489,17 @@ private:
   bool GetCBContentHeight(nscoord& aWidth);
   bool GetFrameBoundsWidthForTransform(nscoord &aWidth);
   bool GetFrameBoundsHeightForTransform(nscoord &aHeight);
   bool GetFrameBorderRectWidth(nscoord& aWidth);
   bool GetFrameBorderRectHeight(nscoord& aHeight);
 
   /* Helper functions for computing the filter property style. */
   void SetCssTextToCoord(nsAString& aCssText, const nsStyleCoord& aCoord);
-  nsROCSSPrimitiveValue* CreatePrimitiveValueForStyleFilter(
+  mozilla::dom::CSSValue* CreatePrimitiveValueForStyleFilter(
     const nsStyleFilter& aStyleFilter);
 
   struct ComputedStyleMapEntry
   {
     // Create a pointer-to-member-function type.
     typedef mozilla::dom::CSSValue* (nsComputedDOMStyle::*ComputeMethod)();
 
     nsCSSProperty mProperty;
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -3747,17 +3747,17 @@ inline uint32_t ListLength(const T* aLis
 }
 
 
 
 already_AddRefed<nsCSSShadowArray>
 nsRuleNode::GetShadowData(const nsCSSValueList* aList,
                           nsStyleContext* aContext,
                           bool aIsBoxShadow,
-                          bool& canStoreInRuleTree)
+                          bool& aCanStoreInRuleTree)
 {
   uint32_t arrayLength = ListLength(aList);
 
   NS_ABORT_IF_FALSE(arrayLength > 0,
                     "Non-null text-shadow list, yet we counted 0 items.");
   nsRefPtr<nsCSSShadowArray> shadowList =
     new(arrayLength) nsCSSShadowArray(arrayLength);
 
@@ -3770,54 +3770,54 @@ nsRuleNode::GetShadowData(const nsCSSVal
        aList;
        aList = aList->mNext, ++item) {
     NS_ABORT_IF_FALSE(aList->mValue.GetUnit() == eCSSUnit_Array,
                       "expecting a plain array value");
     nsCSSValue::Array *arr = aList->mValue.GetArrayValue();
     // OK to pass bad aParentCoord since we're not passing SETCOORD_INHERIT
     unitOK = SetCoord(arr->Item(0), tempCoord, nsStyleCoord(),
                       SETCOORD_LENGTH | SETCOORD_CALC_LENGTH_ONLY,
-                      aContext, mPresContext, canStoreInRuleTree);
+                      aContext, mPresContext, aCanStoreInRuleTree);
     NS_ASSERTION(unitOK, "unexpected unit");
     item->mXOffset = tempCoord.GetCoordValue();
 
     unitOK = SetCoord(arr->Item(1), tempCoord, nsStyleCoord(),
                       SETCOORD_LENGTH | SETCOORD_CALC_LENGTH_ONLY,
-                      aContext, mPresContext, canStoreInRuleTree);
+                      aContext, mPresContext, aCanStoreInRuleTree);
     NS_ASSERTION(unitOK, "unexpected unit");
     item->mYOffset = tempCoord.GetCoordValue();
 
     // Blur radius is optional in the current box-shadow spec
     if (arr->Item(2).GetUnit() != eCSSUnit_Null) {
       unitOK = SetCoord(arr->Item(2), tempCoord, nsStyleCoord(),
                         SETCOORD_LENGTH | SETCOORD_CALC_LENGTH_ONLY |
                           SETCOORD_CALC_CLAMP_NONNEGATIVE,
-                        aContext, mPresContext, canStoreInRuleTree);
+                        aContext, mPresContext, aCanStoreInRuleTree);
       NS_ASSERTION(unitOK, "unexpected unit");
       item->mRadius = tempCoord.GetCoordValue();
     } else {
       item->mRadius = 0;
     }
 
     // Find the spread radius
     if (aIsBoxShadow && arr->Item(3).GetUnit() != eCSSUnit_Null) {
       unitOK = SetCoord(arr->Item(3), tempCoord, nsStyleCoord(),
                         SETCOORD_LENGTH | SETCOORD_CALC_LENGTH_ONLY,
-                        aContext, mPresContext, canStoreInRuleTree);
+                        aContext, mPresContext, aCanStoreInRuleTree);
       NS_ASSERTION(unitOK, "unexpected unit");
       item->mSpread = tempCoord.GetCoordValue();
     } else {
       item->mSpread = 0;
     }
 
     if (arr->Item(4).GetUnit() != eCSSUnit_Null) {
       item->mHasColor = true;
       // 2nd argument can be bogus since inherit is not a valid color
       unitOK = SetColor(arr->Item(4), 0, mPresContext, aContext, item->mColor,
-                        canStoreInRuleTree);
+                        aCanStoreInRuleTree);
       NS_ASSERTION(unitOK, "unexpected unit");
     }
 
     if (aIsBoxShadow && arr->Item(5).GetUnit() == eCSSUnit_Enumerated) {
       NS_ASSERTION(arr->Item(5).GetIntValue() == NS_STYLE_BOX_SHADOW_INSET,
                    "invalid keyword type for box shadow");
       item->mInset = true;
     } else {
@@ -7717,25 +7717,27 @@ nsRuleNode::ComputeSVGData(void* aStartS
               svg->mTextRendering, canStoreInRuleTree,
               SETDSC_ENUMERATED, parentSVG->mTextRendering,
               NS_STYLE_TEXT_RENDERING_AUTO, 0, 0, 0, 0);
 
   COMPUTE_END_INHERITED(SVG, svg)
 }
 
 static nsStyleFilter::Type
-StyleFilterTypeForFunctionName(nsCSSKeyword functionName)
+StyleFilterTypeForFunctionName(nsCSSKeyword aFunctionName)
 {
-  switch (functionName) {
+  switch (aFunctionName) {
     case eCSSKeyword_blur:
       return nsStyleFilter::Type::eBlur;
     case eCSSKeyword_brightness:
       return nsStyleFilter::Type::eBrightness;
     case eCSSKeyword_contrast:
       return nsStyleFilter::Type::eContrast;
+    case eCSSKeyword_drop_shadow:
+      return nsStyleFilter::Type::eDropShadow;
     case eCSSKeyword_grayscale:
       return nsStyleFilter::Type::eGrayscale;
     case eCSSKeyword_hue_rotate:
       return nsStyleFilter::Type::eHueRotate;
     case eCSSKeyword_invert:
       return nsStyleFilter::Type::eInvert;
     case eCSSKeyword_opacity:
       return nsStyleFilter::Type::eOpacity;
@@ -7744,53 +7746,64 @@ StyleFilterTypeForFunctionName(nsCSSKeyw
     case eCSSKeyword_sepia:
       return nsStyleFilter::Type::eSepia;
     default:
       NS_NOTREACHED("Unknown filter type.");
       return nsStyleFilter::Type::eNull;
   }
 }
 
-static void
-SetStyleFilterToCSSValue(nsStyleFilter* aStyleFilter,
-                         const nsCSSValue& aValue,
-                         nsStyleContext* aStyleContext,
-                         nsPresContext* aPresContext,
-                         bool& aCanStoreInRuleTree)
+void
+nsRuleNode::SetStyleFilterToCSSValue(nsStyleFilter* aStyleFilter,
+                                     const nsCSSValue& aValue,
+                                     nsStyleContext* aStyleContext,
+                                     nsPresContext* aPresContext,
+                                     bool& aCanStoreInRuleTree)
 {
   nsCSSUnit unit = aValue.GetUnit();
   if (unit == eCSSUnit_URL) {
-    aStyleFilter->mType = nsStyleFilter::Type::eURL;
-    aStyleFilter->mURL = aValue.GetURLValue();
+    aStyleFilter->SetURL(aValue.GetURLValue());
     return;
   }
 
   NS_ABORT_IF_FALSE(unit == eCSSUnit_Function, "expected a filter function");
 
   nsCSSValue::Array* filterFunction = aValue.GetArrayValue();
   nsCSSKeyword functionName =
     (nsCSSKeyword)filterFunction->Item(0).GetIntValue();
-  aStyleFilter->mType = StyleFilterTypeForFunctionName(functionName);
+
+  nsStyleFilter::Type type = StyleFilterTypeForFunctionName(functionName);
+  if (type == nsStyleFilter::Type::eDropShadow) {
+    nsRefPtr<nsCSSShadowArray> shadowArray = GetShadowData(
+      filterFunction->Item(1).GetListValue(),
+      aStyleContext,
+      false,
+      aCanStoreInRuleTree);
+    aStyleFilter->SetDropShadow(shadowArray);
+    return;
+  }
 
   int32_t mask = SETCOORD_PERCENT | SETCOORD_FACTOR;
-  if (aStyleFilter->mType == nsStyleFilter::Type::eBlur) {
+  if (type == nsStyleFilter::Type::eBlur) {
     mask = SETCOORD_LENGTH | SETCOORD_STORE_CALC;
-  } else if (aStyleFilter->mType == nsStyleFilter::Type::eHueRotate) {
+  } else if (type == nsStyleFilter::Type::eHueRotate) {
     mask = SETCOORD_ANGLE;
   }
 
   NS_ABORT_IF_FALSE(filterFunction->Count() == 2,
-                    "all filter functions except drop-shadow should have "
+                    "all filter functions should have "
                     "exactly one argument");
 
   nsCSSValue& arg = filterFunction->Item(1);
-  DebugOnly<bool> success = SetCoord(arg, aStyleFilter->mFilterParameter,
+  nsStyleCoord filterParameter;
+  DebugOnly<bool> success = SetCoord(arg, filterParameter,
                                      nsStyleCoord(), mask,
                                      aStyleContext, aPresContext,
                                      aCanStoreInRuleTree);
+  aStyleFilter->SetFilterParameter(filterParameter, type);
   NS_ABORT_IF_FALSE(success, "unexpected unit");
 }
 
 const void*
 nsRuleNode::ComputeSVGResetData(void* aStartStruct,
                                 const nsRuleData* aRuleData,
                                 nsStyleContext* aContext,
                                 nsRuleNode* aHighestNode,
@@ -7879,17 +7892,17 @@ nsRuleNode::ComputeSVGResetData(void* aS
     case eCSSUnit_List:
     case eCSSUnit_ListDep: {
       svgReset->mFilters.Clear();
       const nsCSSValueList* cur = filterValue->GetListValue();
       while(cur) {
         nsStyleFilter styleFilter;
         SetStyleFilterToCSSValue(&styleFilter, cur->mValue, aContext,
                                  mPresContext, canStoreInRuleTree);
-        NS_ABORT_IF_FALSE(styleFilter.mType != nsStyleFilter::Type::eNull,
+        NS_ABORT_IF_FALSE(styleFilter.GetType() != nsStyleFilter::Type::eNull,
                           "filter should be set");
         svgReset->mFilters.AppendElement(styleFilter);
         cur = cur->mNext;
       }
       break;
     }
     default:
       NS_NOTREACHED("unexpected unit");
--- a/layout/style/nsRuleNode.h
+++ b/layout/style/nsRuleNode.h
@@ -622,17 +622,22 @@ protected:
 
   inline RuleDetail CheckSpecifiedProperties(const nsStyleStructID aSID,
                                              const nsRuleData* aRuleData);
 
   already_AddRefed<nsCSSShadowArray>
               GetShadowData(const nsCSSValueList* aList,
                             nsStyleContext* aContext,
                             bool aIsBoxShadow,
-                            bool& inherited);
+                            bool& aCanStoreInRuleTree);
+  void SetStyleFilterToCSSValue(nsStyleFilter* aStyleFilter,
+                                const nsCSSValue& aValue,
+                                nsStyleContext* aStyleContext,
+                                nsPresContext* aPresContext,
+                                bool& aCanStoreInRuleTree);
 
 private:
   nsRuleNode(nsPresContext* aPresContext, nsRuleNode* aParent,
              nsIStyleRule* aRule, uint8_t aLevel, bool aIsImportant);
   ~nsRuleNode();
 
 public:
   static nsRuleNode* CreateRootNode(nsPresContext* aPresContext);
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -1006,48 +1006,92 @@ nsChangeHint nsStyleSVG::CalcDifference(
 //
 nsStyleFilter::nsStyleFilter()
   : mType(eNull)
 {
   MOZ_COUNT_CTOR(nsStyleFilter);
 }
 
 nsStyleFilter::nsStyleFilter(const nsStyleFilter& aSource)
-  : mType(aSource.mType)
 {
   MOZ_COUNT_CTOR(nsStyleFilter);
-
-  if (mType == eURL) {
-    mURL = aSource.mURL;
-  } else if (mType != eNull) {
-    mFilterParameter = aSource.mFilterParameter;
+  if (aSource.mType == eURL) {
+    SetURL(aSource.mURL);
+  } else if (aSource.mType == eDropShadow) {
+    SetDropShadow(aSource.mDropShadow);
+  } else if (aSource.mType != eNull) {
+    SetFilterParameter(aSource.mFilterParameter, aSource.mType);
   }
 }
 
 nsStyleFilter::~nsStyleFilter()
 {
+  ReleaseRef();
   MOZ_COUNT_DTOR(nsStyleFilter);
 }
 
 bool
 nsStyleFilter::operator==(const nsStyleFilter& aOther) const
 {
   if (mType != aOther.mType) {
       return false;
   }
 
   if (mType == eURL) {
     return EqualURIs(mURL, aOther.mURL);
+  } else if (mType == eDropShadow) {
+    return *mDropShadow == *aOther.mDropShadow;
   } else if (mType != eNull) {
     return mFilterParameter == aOther.mFilterParameter;
   }
 
   return true;
 }
 
+void
+nsStyleFilter::ReleaseRef()
+{
+  if (mType == eDropShadow) {
+    NS_ASSERTION(mDropShadow, "expected pointer");
+    mDropShadow->Release();
+  } else if (mType == eURL) {
+    NS_ASSERTION(mURL, "expected pointer");
+    mURL->Release();
+  }
+}
+
+void
+nsStyleFilter::SetFilterParameter(const nsStyleCoord& aFilterParameter,
+                                  Type aType)
+{
+  ReleaseRef();
+  mFilterParameter = aFilterParameter;
+  mType = aType;
+}
+
+void
+nsStyleFilter::SetURL(nsIURI* aURL)
+{
+  NS_ASSERTION(aURL, "expected pointer");
+  ReleaseRef();
+  mURL = aURL;
+  mURL->AddRef();
+  mType = eURL;
+}
+
+void
+nsStyleFilter::SetDropShadow(nsCSSShadowArray* aDropShadow)
+{
+  NS_ASSERTION(aDropShadow, "expected pointer");
+  ReleaseRef();
+  mDropShadow = aDropShadow;
+  mDropShadow->AddRef();
+  mType = eDropShadow;
+}
+
 // --------------------
 // nsStyleSVGReset
 //
 nsStyleSVGReset::nsStyleSVGReset() 
 {
     MOZ_COUNT_CTOR(nsStyleSVGReset);
     mStopColor               = NS_RGB(0,0,0);
     mFloodColor              = NS_RGB(0,0,0);
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -650,26 +650,26 @@ struct nsCSSShadowItem {
 
   nsCSSShadowItem() : mHasColor(false) {
     MOZ_COUNT_CTOR(nsCSSShadowItem);
   }
   ~nsCSSShadowItem() {
     MOZ_COUNT_DTOR(nsCSSShadowItem);
   }
 
-  bool operator==(const nsCSSShadowItem& aOther) {
+  bool operator==(const nsCSSShadowItem& aOther) const {
     return (mXOffset == aOther.mXOffset &&
             mYOffset == aOther.mYOffset &&
             mRadius == aOther.mRadius &&
             mHasColor == aOther.mHasColor &&
             mSpread == aOther.mSpread &&
             mInset == aOther.mInset &&
             (!mHasColor || mColor == aOther.mColor));
   }
-  bool operator!=(const nsCSSShadowItem& aOther) {
+  bool operator!=(const nsCSSShadowItem& aOther) const {
     return !(*this == aOther);
   }
 };
 
 class nsCSSShadowArray {
   public:
     void* operator new(size_t aBaseSize, uint32_t aArrayLen) {
       // We can allocate both this nsCSSShadowArray and the
@@ -711,16 +711,28 @@ class nsCSSShadowArray {
     bool HasShadowWithInset(bool aInset) {
       for (uint32_t i = 0; i < mLength; ++i) {
         if (mArray[i].mInset == aInset)
           return true;
       }
       return false;
     }
 
+    bool operator==(const nsCSSShadowArray& aOther) const {
+      if (mLength != aOther.Length())
+        return false;
+
+      for (uint32_t i = 0; i < mLength; ++i) {
+        if (ShadowAt(i) != aOther.ShadowAt(i))
+          return false;
+      }
+
+      return true;
+    }
+
     NS_INLINE_DECL_REFCOUNTING(nsCSSShadowArray)
 
   private:
     uint32_t mLength;
     nsCSSShadowItem mArray[1]; // This MUST be the last item
 };
 
 // Border widths are rounded to the nearest-below integer number of pixels,
@@ -2276,28 +2288,59 @@ struct nsStyleFilter {
   bool operator==(const nsStyleFilter& aOther) const;
 
   enum Type {
     eNull,
     eURL,
     eBlur,
     eBrightness,
     eContrast,
+    eDropShadow,
+    eGrayscale,
     eHueRotate,
     eInvert,
     eOpacity,
-    eGrayscale,
     eSaturate,
     eSepia,
   };
 
+  Type GetType() const {
+    return mType;
+  }
+
+  const nsStyleCoord& GetFilterParameter() const {
+    NS_ASSERTION(mType != eDropShadow &&
+                 mType != eURL &&
+                 mType != eNull, "wrong filter type");
+    return mFilterParameter;
+  }
+  void SetFilterParameter(const nsStyleCoord& aFilterParameter,
+                          Type aType);
+
+  nsIURI* GetURL() const {
+    NS_ASSERTION(mType == eURL, "wrong filter type");
+    return mURL;
+  }
+  void SetURL(nsIURI* aURL);
+
+  nsCSSShadowArray* GetDropShadow() const {
+    NS_ASSERTION(mType == eDropShadow, "wrong filter type");
+    return mDropShadow;
+  }
+  void SetDropShadow(nsCSSShadowArray* aDropShadow);
+
+private:
+  void ReleaseRef();
+
   Type mType;
-  nsIURI* mURL;
   nsStyleCoord mFilterParameter; // coord, percent, factor, angle
-  // FIXME: Add a nsCSSShadowItem when we implement drop shadow.
+  union {
+    nsIURI* mURL;
+    nsCSSShadowArray* mDropShadow;
+  };
 };
 
 struct nsStyleSVGReset {
   nsStyleSVGReset();
   nsStyleSVGReset(const nsStyleSVGReset& aSource);
   ~nsStyleSVGReset();
 
   void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW {
@@ -2313,18 +2356,18 @@ struct nsStyleSVGReset {
     return NS_CombineHint(nsChangeHint_UpdateEffects, NS_STYLE_HINT_REFLOW);
   }
 
   // The backend only supports one SVG reference right now.
   // Eventually, it will support multiple chained SVG reference filters and CSS
   // filter functions.
   nsIURI* SingleFilter() const {
     return (mFilters.Length() == 1 &&
-            mFilters[0].mType == nsStyleFilter::Type::eURL) ?
-            mFilters[0].mURL : nullptr;
+            mFilters[0].GetType() == nsStyleFilter::Type::eURL) ?
+            mFilters[0].GetURL() : nullptr;
   }
 
   nsCOMPtr<nsIURI> mClipPath;         // [reset]
   nsTArray<nsStyleFilter> mFilters;   // [reset]
   nsCOMPtr<nsIURI> mMask;             // [reset]
   nscolor          mStopColor;        // [reset]
   nscolor          mFloodColor;       // [reset]
   nscolor          mLightingColor;    // [reset]
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -4453,16 +4453,34 @@ if (SpecialPowers.getBoolPref("layout.cs
 			"contrast(0)",
 			"contrast(50%)",
 			"contrast(1)",
 			"contrast(1.0)",
 			"contrast(2)",
 			"contrast(350%)",
 			"contrast(4.567)",
 
+			"drop-shadow(2px 2px)",
+			"drop-shadow(2px 2px 1px)",
+			"drop-shadow(2px 2px green))",
+			"drop-shadow(2px 2px 1px green)",
+			"drop-shadow(green 2px 2px)",
+			"drop-shadow(green 2px 2px 1px)",
+			"drop-shadow(currentColor 3px 3px)",
+			"drop-shadow(2px 2px calc(-5px))", /* clamped */
+			"drop-shadow(calc(3em - 2px) 2px green)",
+			"drop-shadow(green calc(3em - 2px) 2px)",
+			"drop-shadow(2px calc(2px + 0.2em))",
+			"drop-shadow(blue 2px calc(2px + 0.2em))",
+			"drop-shadow(2px calc(2px + 0.2em) blue)",
+			"drop-shadow(calc(-2px) calc(-2px))",
+			"drop-shadow(-2px -2px)",
+			"drop-shadow(calc(2px) calc(2px))",
+			"drop-shadow(calc(2px) calc(2px) calc(2px))",
+
 			"grayscale(0)",
 			"grayscale(50%)",
 			"grayscale(1)",
 			"grayscale(1.0)",
 			"grayscale(2)",
 			"grayscale(350%)",
 			"grayscale(4.567)",
 
@@ -4551,16 +4569,32 @@ if (SpecialPowers.getBoolPref("layout.cs
 			"contrast()",
 			"contrast(0.5 0.5)",
 			"contrast(0.5,)",
 			"contrast(0.5, 0.5)",
 			"contrast(#my-filter)",
 			"contrast(10px)",
 			"contrast(-1)",
 
+			"drop-shadow()",
+			"drop-shadow(3% 3%)",
+			"drop-shadow(2px 2px -5px)",
+			"drop-shadow(2px 2px 2px 2px)",
+			"drop-shadow(2px 2px, none)",
+			"drop-shadow(none, 2px 2px)",
+			"drop-shadow(inherit, 2px 2px)",
+			"drop-shadow(2px 2px, inherit)",
+			"drop-shadow(2 2px)",
+			"drop-shadow(2px 2)",
+			"drop-shadow(2px 2px 2)",
+			"drop-shadow(2px 2px 2px 2)",
+			"drop-shadow(calc(2px) calc(2px) calc(2px) calc(2px))",
+			"drop-shadow(green 2px 2px, blue 1px 3px 4px)",
+			"drop-shadow(blue 2px 2px, currentColor 1px 2px)",
+
 			"grayscale()",
 			"grayscale(0.5 0.5)",
 			"grayscale(0.5,)",
 			"grayscale(0.5, 0.5)",
 			"grayscale(#my-filter)",
 			"grayscale(10px)",
 			"grayscale(-1)",