Bug 964200 (Part 3a moz2d) - Implement Filter Effects Module feDropShadow filter. r=mstange
authorRobert Longson <longsonr@gmail.com>
Sat, 08 Feb 2014 16:49:46 +0000
changeset 167651 01617899e713
parent 167650 386a2183cc84
child 167652 d4f859c2f0e0
push id39518
push userlongsonr@gmail.com
push dateSat, 08 Feb 2014 16:49:48 +0000
treeherdermozilla-inbound@01617899e713 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmstange
bugs964200
milestone30.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 964200 (Part 3a moz2d) - Implement Filter Effects Module feDropShadow filter. r=mstange
content/svg/content/src/SVGFEDropShadowElement.cpp
content/svg/content/src/SVGFEDropShadowElement.h
content/svg/content/src/SVGFEGaussianBlurElement.cpp
content/svg/content/src/moz.build
dom/webidl/SVGFEDropShadowElement.webidl
gfx/src/FilterSupport.cpp
gfx/src/FilterSupport.h
layout/reftests/svg/filters/feDropShadow-01-ref.svg
layout/reftests/svg/filters/feDropShadow-01.svg
copy from content/svg/content/src/SVGFEGaussianBlurElement.cpp
copy to content/svg/content/src/SVGFEDropShadowElement.cpp
--- a/content/svg/content/src/SVGFEGaussianBlurElement.cpp
+++ b/content/svg/content/src/SVGFEDropShadowElement.cpp
@@ -1,125 +1,181 @@
 /* a*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "mozilla/dom/SVGFEGaussianBlurElement.h"
-#include "mozilla/dom/SVGFEGaussianBlurElementBinding.h"
+#include "mozilla/dom/SVGFEDropShadowElement.h"
+#include "mozilla/dom/SVGFEDropShadowElementBinding.h"
 #include "nsSVGFilterInstance.h"
-#include "nsSVGUtils.h"
 
-NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(FEGaussianBlur)
+NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(FEDropShadow)
 
 using namespace mozilla::gfx;
 
 namespace mozilla {
 namespace dom {
 
 JSObject*
-SVGFEGaussianBlurElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aScope)
+SVGFEDropShadowElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aScope)
 {
-  return SVGFEGaussianBlurElementBinding::Wrap(aCx, aScope, this);
+  return SVGFEDropShadowElementBinding::Wrap(aCx, aScope, this);
 }
 
-nsSVGElement::NumberPairInfo SVGFEGaussianBlurElement::sNumberPairInfo[1] =
+nsSVGElement::NumberInfo SVGFEDropShadowElement::sNumberInfo[2] =
 {
-  { &nsGkAtoms::stdDeviation, 0, 0 }
+  { &nsGkAtoms::dx, 2, false },
+  { &nsGkAtoms::dy, 2, false }
 };
 
-nsSVGElement::StringInfo SVGFEGaussianBlurElement::sStringInfo[2] =
+nsSVGElement::NumberPairInfo SVGFEDropShadowElement::sNumberPairInfo[1] =
+{
+  { &nsGkAtoms::stdDeviation, 2, 2 }
+};
+
+nsSVGElement::StringInfo SVGFEDropShadowElement::sStringInfo[2] =
 {
   { &nsGkAtoms::result, kNameSpaceID_None, true },
   { &nsGkAtoms::in, kNameSpaceID_None, true }
 };
 
 //----------------------------------------------------------------------
 // nsIDOMNode methods
 
-NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEGaussianBlurElement)
+NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEDropShadowElement)
 
 //----------------------------------------------------------------------
 
 already_AddRefed<SVGAnimatedString>
-SVGFEGaussianBlurElement::In1()
+SVGFEDropShadowElement::In1()
 {
   return mStringAttributes[IN1].ToDOMAnimatedString(this);
 }
 
 already_AddRefed<SVGAnimatedNumber>
-SVGFEGaussianBlurElement::StdDeviationX()
+SVGFEDropShadowElement::Dx()
+{
+  return mNumberAttributes[DX].ToDOMAnimatedNumber(this);
+}
+
+already_AddRefed<SVGAnimatedNumber>
+SVGFEDropShadowElement::Dy()
+{
+  return mNumberAttributes[DY].ToDOMAnimatedNumber(this);
+}
+
+already_AddRefed<SVGAnimatedNumber>
+SVGFEDropShadowElement::StdDeviationX()
 {
   return mNumberPairAttributes[STD_DEV].ToDOMAnimatedNumber(nsSVGNumberPair::eFirst, this);
 }
 
 already_AddRefed<SVGAnimatedNumber>
-SVGFEGaussianBlurElement::StdDeviationY()
+SVGFEDropShadowElement::StdDeviationY()
 {
   return mNumberPairAttributes[STD_DEV].ToDOMAnimatedNumber(nsSVGNumberPair::eSecond, this);
 }
 
 void
-SVGFEGaussianBlurElement::SetStdDeviation(float stdDeviationX, float stdDeviationY)
+SVGFEDropShadowElement::SetStdDeviation(float stdDeviationX, float stdDeviationY)
 {
   mNumberPairAttributes[STD_DEV].SetBaseValues(stdDeviationX, stdDeviationY, this);
 }
 
-static const float kMaxStdDeviation = 500;
-
 FilterPrimitiveDescription
-SVGFEGaussianBlurElement::GetPrimitiveDescription(nsSVGFilterInstance* aInstance,
-                                                  const IntRect& aFilterSubregion,
-                                                  const nsTArray<bool>& aInputsAreTainted,
-                                                  nsTArray<RefPtr<SourceSurface>>& aInputImages)
+SVGFEDropShadowElement::GetPrimitiveDescription(nsSVGFilterInstance* aInstance,
+                                                const IntRect& aFilterSubregion,
+                                                const nsTArray<bool>& aInputsAreTainted,
+                                                nsTArray<RefPtr<SourceSurface>>& aInputImages)
 {
   float stdX = aInstance->GetPrimitiveNumber(SVGContentUtils::X,
                                              &mNumberPairAttributes[STD_DEV],
                                              nsSVGNumberPair::eFirst);
   float stdY = aInstance->GetPrimitiveNumber(SVGContentUtils::Y,
                                              &mNumberPairAttributes[STD_DEV],
                                              nsSVGNumberPair::eSecond);
   if (stdX < 0 || stdY < 0) {
     return FilterPrimitiveDescription(FilterPrimitiveDescription::eNone);
   }
 
-  stdX = std::min(stdX, kMaxStdDeviation);
-  stdY = std::min(stdY, kMaxStdDeviation);
-  FilterPrimitiveDescription descr(FilterPrimitiveDescription::eGaussianBlur);
-  descr.Attributes().Set(eGaussianBlurStdDeviation, Size(stdX, stdY));
+  IntPoint offset(int32_t(aInstance->GetPrimitiveNumber(
+                            SVGContentUtils::X, &mNumberAttributes[DX])),
+                  int32_t(aInstance->GetPrimitiveNumber(
+                            SVGContentUtils::Y, &mNumberAttributes[DY])));
+
+  FilterPrimitiveDescription descr(FilterPrimitiveDescription::eDropShadow);
+  descr.Attributes().Set(eDropShadowStdDeviation, Size(stdX, stdY));
+  descr.Attributes().Set(eDropShadowOffset, offset);
+
+  nsIFrame* frame = GetPrimaryFrame();
+  if (frame) {
+    nsStyleContext* style = frame->StyleContext();
+    nscolor floodColor = style->StyleSVGReset()->mFloodColor;
+    float floodOpacity = style->StyleSVGReset()->mFloodOpacity;
+    Color color(NS_GET_R(floodColor) / 255.0,
+                NS_GET_G(floodColor) / 255.0,
+                NS_GET_B(floodColor) / 255.0,
+                NS_GET_A(floodColor) / 255.0 * floodOpacity);
+    descr.Attributes().Set(eDropShadowColor, color);
+  } else {
+    descr.Attributes().Set(eDropShadowColor, Color());
+  }
   return descr;
 }
 
 bool
-SVGFEGaussianBlurElement::AttributeAffectsRendering(int32_t aNameSpaceID,
-                                                    nsIAtom* aAttribute) const
+SVGFEDropShadowElement::AttributeAffectsRendering(int32_t aNameSpaceID,
+                                                  nsIAtom* aAttribute) const
 {
-  return SVGFEGaussianBlurElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) ||
+  return SVGFEDropShadowElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) ||
          (aNameSpaceID == kNameSpaceID_None &&
           (aAttribute == nsGkAtoms::in ||
-           aAttribute == nsGkAtoms::stdDeviation));
+           aAttribute == nsGkAtoms::stdDeviation ||
+           aAttribute == nsGkAtoms::dx ||
+           aAttribute == nsGkAtoms::dy));
 }
 
 void
-SVGFEGaussianBlurElement::GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources)
+SVGFEDropShadowElement::GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources)
 {
   aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN1], this));
 }
 
 //----------------------------------------------------------------------
+// nsIContent methods
+
+NS_IMETHODIMP_(bool)
+SVGFEDropShadowElement::IsAttributeMapped(const nsIAtom* name) const
+{
+  static const MappedAttributeEntry* const map[] = {
+    sFEFloodMap
+  };
+
+  return FindAttributeDependence(name, map) ||
+    SVGFEDropShadowElementBase::IsAttributeMapped(name);
+}
+
+//----------------------------------------------------------------------
 // nsSVGElement methods
 
+nsSVGElement::NumberAttributesInfo
+SVGFEDropShadowElement::GetNumberInfo()
+{
+  return NumberAttributesInfo(mNumberAttributes, sNumberInfo,
+                              ArrayLength(sNumberInfo));
+}
+
 nsSVGElement::NumberPairAttributesInfo
-SVGFEGaussianBlurElement::GetNumberPairInfo()
+SVGFEDropShadowElement::GetNumberPairInfo()
 {
   return NumberPairAttributesInfo(mNumberPairAttributes, sNumberPairInfo,
                                   ArrayLength(sNumberPairInfo));
 }
 
 nsSVGElement::StringAttributesInfo
-SVGFEGaussianBlurElement::GetStringInfo()
+SVGFEDropShadowElement::GetStringInfo()
 {
   return StringAttributesInfo(mStringAttributes, sStringInfo,
                               ArrayLength(sStringInfo));
 }
 
 } // namespace dom
 } // namespace mozilla
copy from content/svg/content/src/SVGFEGaussianBlurElement.h
copy to content/svg/content/src/SVGFEDropShadowElement.h
--- a/content/svg/content/src/SVGFEGaussianBlurElement.h
+++ b/content/svg/content/src/SVGFEDropShadowElement.h
@@ -1,68 +1,79 @@
 /* a*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef mozilla_dom_SVGFEGaussianBlurElement_h
-#define mozilla_dom_SVGFEGaussianBlurElement_h
+#ifndef mozilla_dom_SVGFEDropShadowElement_h
+#define mozilla_dom_SVGFEDropShadowElement_h
 
 #include "nsSVGFilters.h"
+#include "nsSVGNumber2.h"
 #include "nsSVGNumberPair.h"
 #include "nsSVGString.h"
 
-nsresult NS_NewSVGFEGaussianBlurElement(nsIContent **aResult,
-                                        already_AddRefed<nsINodeInfo> aNodeInfo);
+nsresult NS_NewSVGFEDropShadowElement(nsIContent **aResult,
+                                      already_AddRefed<nsINodeInfo> aNodeInfo);
 
 namespace mozilla {
 namespace dom {
 
-typedef nsSVGFE SVGFEGaussianBlurElementBase;
+typedef nsSVGFE SVGFEDropShadowElementBase;
 
-class SVGFEGaussianBlurElement : public SVGFEGaussianBlurElementBase
+class SVGFEDropShadowElement : public SVGFEDropShadowElementBase
 {
-  friend nsresult (::NS_NewSVGFEGaussianBlurElement(nsIContent **aResult,
-                                                    already_AddRefed<nsINodeInfo> aNodeInfo));
+  friend nsresult (::NS_NewSVGFEDropShadowElement(nsIContent **aResult,
+                                                  already_AddRefed<nsINodeInfo> aNodeInfo));
 protected:
-  SVGFEGaussianBlurElement(already_AddRefed<nsINodeInfo> aNodeInfo)
-    : SVGFEGaussianBlurElementBase(aNodeInfo)
+  SVGFEDropShadowElement(already_AddRefed<nsINodeInfo> aNodeInfo)
+    : SVGFEDropShadowElementBase(aNodeInfo)
   {
   }
   virtual JSObject* WrapNode(JSContext* aCx,
                              JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
 
 public:
   virtual FilterPrimitiveDescription
     GetPrimitiveDescription(nsSVGFilterInstance* aInstance,
                             const IntRect& aFilterSubregion,
                             const nsTArray<bool>& aInputsAreTainted,
                             nsTArray<mozilla::RefPtr<SourceSurface>>& aInputImages) MOZ_OVERRIDE;
   virtual bool AttributeAffectsRendering(
           int32_t aNameSpaceID, nsIAtom* aAttribute) const MOZ_OVERRIDE;
   virtual nsSVGString& GetResultImageName() MOZ_OVERRIDE { return mStringAttributes[RESULT]; }
   virtual void GetSourceImageNames(nsTArray<nsSVGStringInfo >& aSources) MOZ_OVERRIDE;
 
+  // nsIContent interface
+  NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const MOZ_OVERRIDE;
+
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE;
 
   // WebIDL
   already_AddRefed<SVGAnimatedString> In1();
+  already_AddRefed<SVGAnimatedNumber> Dx();
+  already_AddRefed<SVGAnimatedNumber> Dy();
   already_AddRefed<SVGAnimatedNumber> StdDeviationX();
   already_AddRefed<SVGAnimatedNumber> StdDeviationY();
   void SetStdDeviation(float stdDeviationX, float stdDeviationY);
 
 protected:
+  virtual NumberAttributesInfo GetNumberInfo() MOZ_OVERRIDE;
   virtual NumberPairAttributesInfo GetNumberPairInfo() MOZ_OVERRIDE;
   virtual StringAttributesInfo GetStringInfo() MOZ_OVERRIDE;
 
+  enum { DX, DY };
+  nsSVGNumber2 mNumberAttributes[2];
+  static NumberInfo sNumberInfo[2];
+
   enum { STD_DEV };
   nsSVGNumberPair mNumberPairAttributes[1];
   static NumberPairInfo sNumberPairInfo[1];
 
   enum { RESULT, IN1 };
   nsSVGString mStringAttributes[2];
   static StringInfo sStringInfo[2];
 };
 
 } // namespace dom
 } // namespace mozilla
 
-#endif // mozilla_dom_SVGFEGaussianBlurElement_h
+#endif // mozilla_dom_SVGFEDropShadowElement_h
--- a/content/svg/content/src/SVGFEGaussianBlurElement.cpp
+++ b/content/svg/content/src/SVGFEGaussianBlurElement.cpp
@@ -58,36 +58,32 @@ SVGFEGaussianBlurElement::StdDeviationY(
 }
 
 void
 SVGFEGaussianBlurElement::SetStdDeviation(float stdDeviationX, float stdDeviationY)
 {
   mNumberPairAttributes[STD_DEV].SetBaseValues(stdDeviationX, stdDeviationY, this);
 }
 
-static const float kMaxStdDeviation = 500;
-
 FilterPrimitiveDescription
 SVGFEGaussianBlurElement::GetPrimitiveDescription(nsSVGFilterInstance* aInstance,
                                                   const IntRect& aFilterSubregion,
                                                   const nsTArray<bool>& aInputsAreTainted,
                                                   nsTArray<RefPtr<SourceSurface>>& aInputImages)
 {
   float stdX = aInstance->GetPrimitiveNumber(SVGContentUtils::X,
                                              &mNumberPairAttributes[STD_DEV],
                                              nsSVGNumberPair::eFirst);
   float stdY = aInstance->GetPrimitiveNumber(SVGContentUtils::Y,
                                              &mNumberPairAttributes[STD_DEV],
                                              nsSVGNumberPair::eSecond);
   if (stdX < 0 || stdY < 0) {
     return FilterPrimitiveDescription(FilterPrimitiveDescription::eNone);
   }
 
-  stdX = std::min(stdX, kMaxStdDeviation);
-  stdY = std::min(stdY, kMaxStdDeviation);
   FilterPrimitiveDescription descr(FilterPrimitiveDescription::eGaussianBlur);
   descr.Attributes().Set(eGaussianBlurStdDeviation, Size(stdX, stdY));
   return descr;
 }
 
 bool
 SVGFEGaussianBlurElement::AttributeAffectsRendering(int32_t aNameSpaceID,
                                                     nsIAtom* aAttribute) const
--- a/content/svg/content/src/moz.build
+++ b/content/svg/content/src/moz.build
@@ -40,16 +40,17 @@ EXPORTS.mozilla.dom += [
     'SVGFEBlendElement.h',
     'SVGFEColorMatrixElement.h',
     'SVGFEComponentTransferElement.h',
     'SVGFECompositeElement.h',
     'SVGFEConvolveMatrixElement.h',
     'SVGFEDiffuseLightingElement.h',
     'SVGFEDisplacementMapElement.h',
     'SVGFEDistantLightElement.h',
+    'SVGFEDropShadowElement.h',
     'SVGFEFloodElement.h',
     'SVGFEGaussianBlurElement.h',
     'SVGFEImageElement.h',
     'SVGFEMergeElement.h',
     'SVGFEMergeNodeElement.h',
     'SVGFEMorphologyElement.h',
     'SVGFEOffsetElement.h',
     'SVGFEPointLightElement.h',
copy from dom/webidl/SVGFEGaussianBlurElement.webidl
copy to dom/webidl/SVGFEDropShadowElement.webidl
--- a/dom/webidl/SVGFEGaussianBlurElement.webidl
+++ b/dom/webidl/SVGFEDropShadowElement.webidl
@@ -5,17 +5,19 @@
  *
  * The origin of this IDL file is
  * http://www.w3.org/TR/SVG2/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
-interface SVGFEGaussianBlurElement : SVGElement {
+interface SVGFEDropShadowElement : SVGElement {
   readonly attribute SVGAnimatedString in1;
+  readonly attribute SVGAnimatedNumber dx;
+  readonly attribute SVGAnimatedNumber dy;
   readonly attribute SVGAnimatedNumber stdDeviationX;
   readonly attribute SVGAnimatedNumber stdDeviationY;
 
   void setStdDeviation(float stdDeviationX, float stdDeviationY);
 };
 
-SVGFEGaussianBlurElement implements SVGFilterPrimitiveStandardAttributes;
+SVGFEDropShadowElement implements SVGFilterPrimitiveStandardAttributes;
--- a/gfx/src/FilterSupport.cpp
+++ b/gfx/src/FilterSupport.cpp
@@ -90,16 +90,18 @@ static const float gsRGBToLinearRGBMap[2
   0.939f, 0.947f, 0.956f, 0.965f, 0.973f, 0.982f, 0.991f, 1.000f
 };
 
 namespace mozilla {
 namespace gfx {
 
 // Some convenience FilterNode creation functions.
 
+static const float kMaxStdDeviation = 500;
+
 namespace FilterWrappers {
 
   static TemporaryRef<FilterNode>
   Unpremultiply(DrawTarget* aDT, FilterNode* aInput)
   {
     RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::UNPREMULTIPLY);
     filter->SetInput(IN_UNPREMULTIPLY_IN, aInput);
     return filter;
@@ -157,16 +159,38 @@ namespace FilterWrappers {
   {
     RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::TRANSFORM);
     filter->SetAttribute(ATT_TRANSFORM_MATRIX, Matrix().Translate(aOffset.x, aOffset.y));
     filter->SetInput(IN_TRANSFORM_IN, aInputFilter);
     return filter;
   }
 
   static TemporaryRef<FilterNode>
+  GaussianBlur(DrawTarget* aDT, FilterNode* aInputFilter, const Size& aStdDeviation)
+  {
+    float stdX = float(std::min(aStdDeviation.width, kMaxStdDeviation));
+    float stdY = float(std::min(aStdDeviation.height, kMaxStdDeviation));
+    if (stdX == stdY) {
+      RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::GAUSSIAN_BLUR);
+      filter->SetAttribute(ATT_GAUSSIAN_BLUR_STD_DEVIATION, stdX);
+      filter->SetInput(IN_GAUSSIAN_BLUR_IN, aInputFilter);
+      return filter;
+    }
+    RefPtr<FilterNode> filterH = aDT->CreateFilter(FilterType::DIRECTIONAL_BLUR);
+    RefPtr<FilterNode> filterV = aDT->CreateFilter(FilterType::DIRECTIONAL_BLUR);
+    filterH->SetAttribute(ATT_DIRECTIONAL_BLUR_DIRECTION, (uint32_t)BLUR_DIRECTION_X);
+    filterH->SetAttribute(ATT_DIRECTIONAL_BLUR_STD_DEVIATION, stdX);
+    filterV->SetAttribute(ATT_DIRECTIONAL_BLUR_DIRECTION, (uint32_t)BLUR_DIRECTION_Y);
+    filterV->SetAttribute(ATT_DIRECTIONAL_BLUR_STD_DEVIATION, stdY);
+    filterH->SetInput(IN_DIRECTIONAL_BLUR_IN, aInputFilter);
+    filterV->SetInput(IN_DIRECTIONAL_BLUR_IN, filterH);
+    return filterV;
+  }
+
+  static TemporaryRef<FilterNode>
   Clear(DrawTarget* aDT)
   {
     RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::FLOOD);
     filter->SetAttribute(ATT_FLOOD_COLOR, Color(0,0,0,0));
     return filter;
   }
 
   static TemporaryRef<FilterNode>
@@ -810,32 +834,47 @@ FilterNodeFromPrimitiveDescription(const
       for (size_t i = 0; i < aSources.Length(); i++) {
         filter->SetInput(IN_COMPOSITE_IN_START + i, aSources[i]);
       }
       return filter;
     }
 
     case FilterPrimitiveDescription::eGaussianBlur:
     {
-      Size stdDeviation = atts.GetSize(eGaussianBlurStdDeviation);
-      if (stdDeviation.width == stdDeviation.height) {
-        RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::GAUSSIAN_BLUR);
-        filter->SetAttribute(ATT_GAUSSIAN_BLUR_STD_DEVIATION, float(stdDeviation.width));
-        filter->SetInput(IN_GAUSSIAN_BLUR_IN, aSources[0]);
-        return filter;
+      return FilterWrappers::GaussianBlur(aDT, aSources[0],
+                                          atts.GetSize(eGaussianBlurStdDeviation));
+    }
+
+    case FilterPrimitiveDescription::eDropShadow:
+    {
+      RefPtr<FilterNode> alpha = FilterWrappers::ToAlpha(aDT, aSources[0]);
+      RefPtr<FilterNode> blur = FilterWrappers::GaussianBlur(aDT, alpha,
+                                  atts.GetSize(eDropShadowStdDeviation));
+      RefPtr<FilterNode> offsetBlur = FilterWrappers::Offset(aDT, blur,
+                                        atts.GetIntPoint(eDropShadowOffset));
+      RefPtr<FilterNode> flood = aDT->CreateFilter(FilterType::FLOOD);
+      Color color = atts.GetColor(eDropShadowColor);
+      if (aDescription.InputColorSpace(0) == LINEAR_RGB) {
+        color = Color(gsRGBToLinearRGBMap[uint8_t(color.r * 255)],
+                      gsRGBToLinearRGBMap[uint8_t(color.g * 255)],
+                      gsRGBToLinearRGBMap[uint8_t(color.b * 255)],
+                      color.a);
       }
-      RefPtr<FilterNode> filterH = aDT->CreateFilter(FilterType::DIRECTIONAL_BLUR);
-      RefPtr<FilterNode> filterV = aDT->CreateFilter(FilterType::DIRECTIONAL_BLUR);
-      filterH->SetAttribute(ATT_DIRECTIONAL_BLUR_DIRECTION, (uint32_t)BLUR_DIRECTION_X);
-      filterH->SetAttribute(ATT_DIRECTIONAL_BLUR_STD_DEVIATION, float(stdDeviation.width));
-      filterV->SetAttribute(ATT_DIRECTIONAL_BLUR_DIRECTION, (uint32_t)BLUR_DIRECTION_Y);
-      filterV->SetAttribute(ATT_DIRECTIONAL_BLUR_STD_DEVIATION, float(stdDeviation.height));
-      filterH->SetInput(IN_DIRECTIONAL_BLUR_IN, aSources[0]);
-      filterV->SetInput(IN_DIRECTIONAL_BLUR_IN, filterH);
-      return filterV;
+      flood->SetAttribute(ATT_FLOOD_COLOR, color);
+
+      RefPtr<FilterNode> composite = aDT->CreateFilter(FilterType::COMPOSITE);
+      composite->SetAttribute(ATT_COMPOSITE_OPERATOR, (uint32_t)COMPOSITE_OPERATOR_IN);
+      composite->SetInput(IN_COMPOSITE_IN_START, offsetBlur);
+      composite->SetInput(IN_COMPOSITE_IN_START + 1, flood);
+
+      RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::COMPOSITE);
+      filter->SetAttribute(ATT_COMPOSITE_OPERATOR, (uint32_t)COMPOSITE_OPERATOR_OVER);
+      filter->SetInput(IN_COMPOSITE_IN_START, composite);
+      filter->SetInput(IN_COMPOSITE_IN_START + 1, aSources[0]);
+      return filter;
     }
 
     case FilterPrimitiveDescription::eDiffuseLighting:
     case FilterPrimitiveDescription::eSpecularLighting:
     {
       bool isSpecular =
         aDescription.Type() == FilterPrimitiveDescription::eSpecularLighting;
 
@@ -1122,21 +1161,19 @@ UnionOfRegions(const nsTArray<nsIntRegio
   nsIntRegion result;
   for (size_t i = 0; i < aRegions.Length(); i++) {
     result.Or(result, aRegions[i]);
   }
   return result;
 }
 
 static int32_t
-InflateSizeForBlurStdDev(double aStdDev)
+InflateSizeForBlurStdDev(float aStdDev)
 {
-  double size = aStdDev * (3 * sqrt(2 * M_PI) / 4) * 1.5;
-  static const double max = 1024;
-  size = std::min(size, max);
+  double size = std::min(aStdDev, kMaxStdDeviation) * (3 * sqrt(2 * M_PI) / 4) * 1.5;
   return uint32_t(floor(size + 0.5));
 }
 
 static nsIntRegion
 ResultChangeRegionForPrimitive(const FilterPrimitiveDescription& aDescription,
                                const nsTArray<nsIntRegion>& aInputChangeRegions)
 {
   const AttributeMap& atts = aDescription.Attributes();
@@ -1195,16 +1232,28 @@ ResultChangeRegionForPrimitive(const Fil
     case FilterPrimitiveDescription::eGaussianBlur:
     {
       Size stdDeviation = atts.GetSize(eGaussianBlurStdDeviation);
       int32_t dx = InflateSizeForBlurStdDev(stdDeviation.width);
       int32_t dy = InflateSizeForBlurStdDev(stdDeviation.height);
       return aInputChangeRegions[0].Inflated(nsIntMargin(dy, dx, dy, dx));
     }
 
+    case FilterPrimitiveDescription::eDropShadow:
+    {
+      IntPoint offset = atts.GetIntPoint(eDropShadowOffset);
+      nsIntRegion offsetRegion = aInputChangeRegions[0].MovedBy(offset.x, offset.y);
+      Size stdDeviation = atts.GetSize(eDropShadowStdDeviation);
+      int32_t dx = InflateSizeForBlurStdDev(stdDeviation.width);
+      int32_t dy = InflateSizeForBlurStdDev(stdDeviation.height);
+      nsIntRegion blurRegion = offsetRegion.Inflated(nsIntMargin(dy, dx, dy, dx));
+      blurRegion.Or(blurRegion, aInputChangeRegions[0]);
+      return blurRegion;
+    }
+
     case FilterPrimitiveDescription::eDiffuseLighting:
     case FilterPrimitiveDescription::eSpecularLighting:
     {
       Size kernelUnitLength = atts.GetSize(eLightingKernelUnitLength);
       int32_t dx = ceil(kernelUnitLength.width);
       int32_t dy = ceil(kernelUnitLength.height);
       return aInputChangeRegions[0].Inflated(nsIntMargin(dy, dx, dy, dx));
     }
@@ -1413,16 +1462,29 @@ SourceNeededRegionForPrimitive(const Fil
     case FilterPrimitiveDescription::eGaussianBlur:
     {
       Size stdDeviation = atts.GetSize(eGaussianBlurStdDeviation);
       int32_t dx = InflateSizeForBlurStdDev(stdDeviation.width);
       int32_t dy = InflateSizeForBlurStdDev(stdDeviation.height);
       return aResultNeededRegion.Inflated(nsIntMargin(dy, dx, dy, dx));
     }
 
+    case FilterPrimitiveDescription::eDropShadow:
+    {
+      IntPoint offset = atts.GetIntPoint(eDropShadowOffset);
+      nsIntRegion offsetRegion =
+        aResultNeededRegion.MovedBy(-nsIntPoint(offset.x, offset.y));
+      Size stdDeviation = atts.GetSize(eDropShadowStdDeviation);
+      int32_t dx = InflateSizeForBlurStdDev(stdDeviation.width);
+      int32_t dy = InflateSizeForBlurStdDev(stdDeviation.height);
+      nsIntRegion blurRegion = offsetRegion.Inflated(nsIntMargin(dy, dx, dy, dx));
+      blurRegion.Or(blurRegion, aResultNeededRegion);
+      return blurRegion;
+    }
+
     case FilterPrimitiveDescription::eDiffuseLighting:
     case FilterPrimitiveDescription::eSpecularLighting:
     {
       Size kernelUnitLength = atts.GetSize(eLightingKernelUnitLength);
       int32_t dx = ceil(kernelUnitLength.width);
       int32_t dy = ceil(kernelUnitLength.height);
       return aResultNeededRegion.Inflated(nsIntMargin(dy, dx, dy, dx));
     }
--- a/gfx/src/FilterSupport.h
+++ b/gfx/src/FilterSupport.h
@@ -95,16 +95,19 @@ enum AttributeName {
   eConvolveMatrixKernelMatrix,
   eConvolveMatrixDivisor,
   eConvolveMatrixBias,
   eConvolveMatrixTarget,
   eConvolveMatrixEdgeMode,
   eConvolveMatrixKernelUnitLength,
   eConvolveMatrixPreserveAlpha,
   eOffsetOffset,
+  eDropShadowStdDeviation,
+  eDropShadowOffset,
+  eDropShadowColor,
   eDisplacementMapScale,
   eDisplacementMapXChannel,
   eDisplacementMapYChannel,
   eTurbulenceOffset,
   eTurbulenceBaseFrequency,
   eTurbulenceNumOctaves,
   eTurbulenceSeed,
   eTurbulenceStitchable,
@@ -226,16 +229,17 @@ public:
     eConvolveMatrix,
     eOffset,
     eDisplacementMap,
     eTurbulence,
     eComposite,
     eMerge,
     eImage,
     eGaussianBlur,
+    eDropShadow,
     eDiffuseLighting,
     eSpecularLighting
   };
   enum {
     kPrimitiveIndexSourceGraphic = -1,
     kPrimitiveIndexSourceAlpha = -2,
     kPrimitiveIndexFillPaint = -3,
     kPrimitiveIndexStrokePaint = -4
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/filters/feDropShadow-01-ref.svg
@@ -0,0 +1,68 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<svg xmlns="http://www.w3.org/2000/svg">
+
+  <title>Reference for Filter Effects Module Level 1 feDropShadow</title>
+
+  <defs>
+    <filter id="dropShadow1">
+      <feGaussianBlur in="SourceAlpha" stdDeviation="2"/>
+      <feOffset dx="2" dy="2" result="offsetblur"/>
+      <feFlood flood-color="green"/>
+      <feComposite in2="offsetblur" operator="in"/>
+      <feMerge>
+        <feMergeNode/>
+        <feMergeNode in="SourceGraphic"/>
+      </feMerge>
+    </filter>
+    <filter id="dropShadow2">
+      <feGaussianBlur in="SourceAlpha" stdDeviation="5"/>
+      <feOffset dx="-2" dy="-5" result="offsetblur"/>
+      <feFlood flood-color="green"/>
+      <feComposite in2="offsetblur" operator="in"/>
+      <feMerge>
+        <feMergeNode/>
+        <feMergeNode in="SourceGraphic"/>
+      </feMerge>
+    </filter>
+    <filter id="dropShadow3">
+      <feGaussianBlur in="SourceAlpha" stdDeviation="2 5"/>
+      <feOffset dx="5" dy="5" result="offsetblur"/>
+      <feFlood flood-color="green" flood-opacity="0.5"/>
+      <feComposite in2="offsetblur" operator="in"/>
+      <feMerge>
+        <feMergeNode/>
+        <feMergeNode in="SourceGraphic"/>
+      </feMerge>
+    </filter>
+    <filter id="dropShadow4" color-interpolation-filters="sRGB">
+      <feGaussianBlur in="SourceAlpha" stdDeviation="5"/>
+      <feOffset dx="5" dy="5" result="offsetblur"/>
+      <feFlood flood-color="green"/>
+      <feComposite in2="offsetblur" operator="in"/>
+      <feMerge>
+        <feMergeNode/>
+        <feMergeNode in="SourceGraphic"/>
+      </feMerge>
+    </filter>
+    <filter id="dropShadow5">
+      <feGaussianBlur in="SourceAlpha" stdDeviation="2"/>
+      <feOffset dx="2" dy="2" result="offsetblur"/>
+      <feFlood flood-color="green"/>
+      <feComposite in2="offsetblur" operator="in"/>
+      <feMerge>
+        <feMergeNode/>
+        <feMergeNode in="SourceAlpha"/>
+      </feMerge>
+    </filter>
+  </defs>
+  <g fill="yellow" transform="translate(10,10)">
+    <circle cx="75" cy="75" r="70" filter="url(#dropShadow1)" />
+    <circle cx="250" cy="75" r="70" filter="url(#dropShadow2)" />
+    <circle cx="425" cy="75" r="70" filter="url(#dropShadow3)" />
+    <circle cx="600" cy="75" r="70" filter="url(#dropShadow4)" />
+    <circle cx="75" cy="250" r="70" filter="url(#dropShadow5)" />
+  </g>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/filters/feDropShadow-01.svg
@@ -0,0 +1,43 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<svg xmlns="http://www.w3.org/2000/svg">
+
+  <title>Testcase for Filter Effects Module Level 1 feDropShadow</title>
+
+  <defs>
+    <!-- check default values -->
+    <filter id="dropShadow1">
+      <feDropShadow flood-color="green"/>
+    </filter>
+    <!-- check negative dx, dy -->
+    <filter id="dropShadow2">
+      <feDropShadow stdDeviation="5" dx="-2" dy="-5" flood-color="green"/>
+    </filter>
+    <!-- check flood-opacity and non-default values -->
+    <filter id="dropShadow3">
+      <feDropShadow stdDeviation="2 5" dx="5" dy="5" flood-color="green" flood-opacity="0.5"/>
+    </filter>
+    <!-- check sRGB -->
+    <filter id="dropShadow4" color-interpolation-filters="sRGB">
+      <feDropShadow stdDeviation="5" dx="5" dy="5" flood-color="green"/>
+    </filter>
+    <!-- check sourceAlpha input -->
+    <filter id="dropShadow5">
+      <feDropShadow in="SourceAlpha" flood-color="green"/>
+    </filter>
+    <!-- check negative stdDeviation (should not display anything) -->
+    <filter id="dropShadow6">
+      <feDropShadow stdDeviation="-2" flood-color="green"/>
+    </filter>
+  </defs>
+  <g fill="yellow" transform="translate(10,10)">
+    <circle cx="75" cy="75" r="70" filter="url(#dropShadow1)" />
+    <circle cx="250" cy="75" r="70" filter="url(#dropShadow2)" />
+    <circle cx="425" cy="75" r="70" filter="url(#dropShadow3)" />
+    <circle cx="600" cy="75" r="70" filter="url(#dropShadow4)" />
+    <circle cx="75" cy="250" r="70" filter="url(#dropShadow5)" />
+    <circle cx="250" cy="250" r="70" filter="url(#dropShadow6)" />
+  </g>
+</svg>