Bug 696078 - Move filter attribute processing to frame classes. r=jwatt
authorRobert Longson <longsonr@gmail.com>
Sat, 19 Nov 2011 17:53:52 +0000
changeset 80599 5ed397fc162a3ff37410b900e2854818e77eb28f
parent 80598 38e4456727805e36a044ad07bca9000437fb1fcd
child 80600 83feacf6ca6830ad256943e4e90aff88e11999aa
push id21511
push userbmo@edmorley.co.uk
push dateTue, 22 Nov 2011 02:38:00 +0000
treeherdermozilla-central@b8955ff00aae [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwatt
bugs696078
milestone11.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 696078 - Move filter attribute processing to frame classes. r=jwatt
content/base/src/nsGkAtomList.h
content/svg/content/src/nsSVGFilters.cpp
content/svg/content/src/nsSVGFilters.h
content/svg/content/src/nsSVGImageElement.cpp
layout/base/nsCSSFrameConstructor.cpp
layout/generic/nsQueryFrame.h
layout/svg/base/src/Makefile.in
layout/svg/base/src/SVGFEContainerFrame.cpp
layout/svg/base/src/SVGFEImageFrame.cpp
layout/svg/base/src/SVGFELeafFrame.cpp
layout/svg/base/src/SVGFEUnstyledLeafFrame.cpp
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -1771,16 +1771,20 @@ GK_ATOM(textInputFrame,"TextInputFrame")
 GK_ATOM(textFrame, "TextFrame")
 GK_ATOM(viewportFrame, "ViewportFrame")
 #ifdef MOZ_XUL
 GK_ATOM(XULLabelFrame, "XULLabelFrame")
 #endif
 GK_ATOM(svgAFrame, "SVGAFrame")
 GK_ATOM(svgClipPathFrame, "SVGClipPathFrame")
 GK_ATOM(svgDefsFrame, "SVGDefsFrame")
+GK_ATOM(svgFEContainerFrame, "SVGFEContainerFrame")
+GK_ATOM(svgFEImageFrame, "SVGFEImageFrame")
+GK_ATOM(svgFELeafFrame, "SVGFELeafFrame")
+GK_ATOM(svgFEUnstyledLeafFrame, "SVGFEUnstyledLeafFrame")
 GK_ATOM(svgFilterFrame, "SVGFilterFrame")
 GK_ATOM(svgForeignObjectFrame, "SVGForeignObjectFrame")
 GK_ATOM(svgGenericContainerFrame, "SVGGenericContainerFrame")
 GK_ATOM(svgGFrame, "SVGGFrame")
 GK_ATOM(svgGlyphFrame, "SVGGlyphFrame")
 GK_ATOM(svgGradientFrame, "SVGGradientFrame")
 GK_ATOM(svgImageFrame, "SVGImageFrame")
 GK_ATOM(svgInnerSVGFrame, "SVGInnerSVGFrame")
--- a/content/svg/content/src/nsSVGFilters.cpp
+++ b/content/svg/content/src/nsSVGFilters.cpp
@@ -54,21 +54,18 @@
 #include "nsSVGFilters.h"
 #include "nsLayoutUtils.h"
 #include "nsSVGUtils.h"
 #include "nsStyleContext.h"
 #include "nsIDocument.h"
 #include "nsIFrame.h"
 #include "gfxContext.h"
 #include "gfxMatrix.h"
-#include "nsIDOMSVGURIReference.h"
-#include "nsImageLoadingContent.h"
 #include "imgIContainer.h"
 #include "nsNetUtil.h"
-#include "SVGAnimatedPreserveAspectRatio.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsSVGFilterElement.h"
 #include "nsSVGString.h"
 #include "nsSVGEffects.h"
 #include "gfxUtils.h"
 
 #if defined(XP_WIN) 
 // Prevent Windows redefining LoadImage
@@ -246,16 +243,28 @@ nsSVGFE::ComputeChangeBBox(const nsTArra
   return r;
 }
 
 void
 nsSVGFE::GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources)
 {
 }
 
+bool
+nsSVGFE::AttributeAffectsRendering(PRInt32 aNameSpaceID,
+                                   nsIAtom* aAttribute) const
+{
+  return aNameSpaceID == kNameSpaceID_None &&
+         (aAttribute == nsGkAtoms::x ||
+          aAttribute == nsGkAtoms::y ||
+          aAttribute == nsGkAtoms::width ||
+          aAttribute == nsGkAtoms::height ||
+          aAttribute == nsGkAtoms::result);
+}
+
 //----------------------------------------------------------------------
 // nsIDOMSVGFilterPrimitiveStandardAttributes methods
 
 /* readonly attribute nsIDOMSVGAnimatedLength x; */
 NS_IMETHODIMP nsSVGFE::GetX(nsIDOMSVGAnimatedLength * *aX)
 {
   return mLengthAttributes[X].ToDOMAnimatedLength(aX, this);
 }
@@ -303,95 +312,16 @@ nsSVGFE::IsAttributeMapped(const nsIAtom
 
 nsSVGElement::LengthAttributesInfo
 nsSVGFE::GetLengthInfo()
 {
   return LengthAttributesInfo(mLengthAttributes, sLengthInfo,
                               ArrayLength(sLengthInfo));
 }
 
-inline static void DidAnimateAttr(Element *aFilterPrimitive)
-{
-  // nsSVGLeafFrame doesn't implement AttributeChanged.
-  nsIFrame* frame = aFilterPrimitive->GetPrimaryFrame();
-  if (frame) {
-    nsSVGEffects::InvalidateRenderingObservers(frame);
-  }
-}
-
-inline static void DidAnimateAttrViaParent(Element *aFilterPrimitive)
-{
-  // No frame, use parent's
-  NS_ASSERTION(!aFilterPrimitive->GetPrimaryFrame(), "Not expecting a frame");
-  nsIContent *parent = aFilterPrimitive->GetFlattenedTreeParent();
-  if (parent && parent->IsElement()) {
-    DidAnimateAttr(parent->AsElement());
-  }
-}
-
-void
-nsSVGFE::DidAnimateLength(PRUint8 aAttrEnum)
-{
-  DidAnimateAttr(this);
-}
-
-void
-nsSVGFE::DidAnimateNumber(PRUint8 aAttrEnum)
-{
-  DidAnimateAttr(this);
-}
-
-void
-nsSVGFE::DidAnimateNumberPair(PRUint8 aAttrEnum)
-{
-  DidAnimateAttr(this);
-}
-
-void
-nsSVGFE::DidAnimateNumberList(PRUint8 aAttrEnum)
-{
-  DidAnimateAttr(this);
-}
-
-void
-nsSVGFE::DidAnimateInteger(PRUint8 aAttrEnum)
-{
-  DidAnimateAttr(this);
-}
-
-void
-nsSVGFE::DidAnimateIntegerPair(PRUint8 aAttrEnum)
-{
-  DidAnimateAttr(this);
-}
-
-void
-nsSVGFE::DidAnimateEnum(PRUint8 aAttrEnum)
-{
-  DidAnimateAttr(this);
-}
-
-void
-nsSVGFE::DidAnimatePreserveAspectRatio()
-{
-  DidAnimateAttr(this);
-}
-
-void
-nsSVGFE::DidAnimateBoolean(PRUint8 aAttrEnum)
-{
-  DidAnimateAttr(this);
-}
-
-void
-nsSVGFE::DidAnimateString(PRUint8 aAttrEnum)
-{
-  DidAnimateAttr(this);
-}
-
 //---------------------Gaussian Blur------------------------
 
 typedef nsSVGFE nsSVGFEGaussianBlurElementBase;
 
 class nsSVGFEGaussianBlurElement : public nsSVGFEGaussianBlurElementBase,
                                    public nsIDOMSVGFEGaussianBlurElement
 {
   friend nsresult NS_NewSVGFEGaussianBlurElement(nsIContent **aResult,
@@ -406,16 +336,18 @@ public:
 
   // FE Base
   NS_FORWARD_NSIDOMSVGFILTERPRIMITIVESTANDARDATTRIBUTES(nsSVGFEGaussianBlurElementBase::)
 
   virtual nsresult Filter(nsSVGFilterInstance* aInstance,
                           const nsTArray<const Image*>& aSources,
                           const Image* aTarget,
                           const nsIntRect& aDataRect);
+  virtual bool AttributeAffectsRendering(
+          PRInt32 aNameSpaceID, nsIAtom* aAttribute) const;
   virtual nsSVGString& GetResultImageName() { return mStringAttributes[RESULT]; }
   virtual void GetSourceImageNames(nsTArray<nsSVGStringInfo >& aSources);
   virtual nsIntRect ComputeTargetBBox(const nsTArray<nsIntRect>& aSourceBBoxes,
           const nsSVGFilterInstance& aInstance);
   virtual void ComputeNeededSourceBBoxes(const nsIntRect& aTargetBBox,
           nsTArray<nsIntRect>& aSourceBBoxes, const nsSVGFilterInstance& aInstance);
   virtual nsIntRect ComputeChangeBBox(const nsTArray<nsIntRect>& aSourceChangeBoxes,
           const nsSVGFilterInstance& aInstance);
@@ -795,16 +727,26 @@ nsSVGFEGaussianBlurElement::Filter(nsSVG
   nsIntRect computationRect = rect;
   InflateRectForBlurDXY(&computationRect, dx, dy);
   ClipComputationRectToSurface(aInstance, &computationRect);
   GaussianBlur(aSources[0], aTarget, computationRect, dx, dy);
   ClipTarget(aInstance, aTarget, computationRect);
   return NS_OK;
 }
 
+bool
+nsSVGFEGaussianBlurElement::AttributeAffectsRendering(PRInt32 aNameSpaceID,
+                                                      nsIAtom* aAttribute) const
+{
+  return nsSVGFEGaussianBlurElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) ||
+         (aNameSpaceID == kNameSpaceID_None &&
+          (aAttribute == nsGkAtoms::in ||
+           aAttribute == nsGkAtoms::stdDeviation));
+}
+
 void
 nsSVGFEGaussianBlurElement::GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources)
 {
   aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN1], this));
 }
 
 nsIntRect
 nsSVGFEGaussianBlurElement::InflateRectForBlur(const nsIntRect& aRect,
@@ -876,16 +818,18 @@ public:
 
   // FE Base
   NS_FORWARD_NSIDOMSVGFILTERPRIMITIVESTANDARDATTRIBUTES(nsSVGFEBlendElementBase::)
 
   virtual nsresult Filter(nsSVGFilterInstance* aInstance,
                           const nsTArray<const Image*>& aSources,
                           const Image* aTarget,
                           const nsIntRect& aDataRect);
+  virtual bool AttributeAffectsRendering(
+          PRInt32 aNameSpaceID, nsIAtom* aAttribute) const;
   virtual nsSVGString& GetResultImageName() { return mStringAttributes[RESULT]; }
   virtual void GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources);
 
   // Blend
   NS_DECL_NSIDOMSVGFEBLENDELEMENT
 
   NS_FORWARD_NSIDOMSVGELEMENT(nsSVGFEBlendElementBase::)
 
@@ -1030,16 +974,27 @@ nsSVGFEBlendElement::Filter(nsSVGFilterI
       }
       PRUint32 alpha = 255 * 255 - (255 - qa) * (255 - qb);
       FAST_DIVIDE_BY_255(targetData[targIndex + GFX_ARGB32_OFFSET_A], alpha);
     }
   }
   return NS_OK;
 }
 
+bool
+nsSVGFEBlendElement::AttributeAffectsRendering(PRInt32 aNameSpaceID,
+                                               nsIAtom* aAttribute) const
+{
+  return nsSVGFEBlendElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) ||
+         (aNameSpaceID == kNameSpaceID_None &&
+          (aAttribute == nsGkAtoms::in ||
+           aAttribute == nsGkAtoms::in2 ||
+           aAttribute == nsGkAtoms::mode));
+}
+
 void
 nsSVGFEBlendElement::GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources)
 {
   aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN1], this));
   aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN2], this));
 }
 
 //----------------------------------------------------------------------
@@ -1078,16 +1033,18 @@ public:
 
   // FE Base
   NS_FORWARD_NSIDOMSVGFILTERPRIMITIVESTANDARDATTRIBUTES(nsSVGFEColorMatrixElementBase::)
 
   virtual nsresult Filter(nsSVGFilterInstance* aInstance,
                           const nsTArray<const Image*>& aSources,
                           const Image* aTarget,
                           const nsIntRect& aDataRect);
+  virtual bool AttributeAffectsRendering(
+          PRInt32 aNameSpaceID, nsIAtom* aAttribute) const;
   virtual nsSVGString& GetResultImageName() { return mStringAttributes[RESULT]; }
   virtual void GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources);
 
   // Color Matrix
   NS_DECL_NSIDOMSVGFECOLORMATRIXELEMENT
 
   NS_FORWARD_NSIDOMSVGELEMENT(nsSVGFEColorMatrixElementBase::)
 
@@ -1333,16 +1290,27 @@ nsSVGFEColorMatrixElement::Filter(nsSVGF
         static_cast<PRUint8>(col[2]);
       targetData[targIndex + GFX_ARGB32_OFFSET_A] =
         static_cast<PRUint8>(col[3]);
     }
   }
   return NS_OK;
 }
 
+bool
+nsSVGFEColorMatrixElement::AttributeAffectsRendering(PRInt32 aNameSpaceID,
+                                                     nsIAtom* aAttribute) const
+{
+  return nsSVGFEColorMatrixElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) ||
+         (aNameSpaceID == kNameSpaceID_None &&
+          (aAttribute == nsGkAtoms::in ||
+           aAttribute == nsGkAtoms::type ||
+           aAttribute == nsGkAtoms::values));
+}
+
 //----------------------------------------------------------------------
 // nsSVGElement methods
 
 nsSVGElement::EnumAttributesInfo
 nsSVGFEColorMatrixElement::GetEnumInfo()
 {
   return EnumAttributesInfo(mEnumAttributes, sEnumInfo,
                             ArrayLength(sEnumInfo));
@@ -1381,16 +1349,18 @@ public:
 
   // FE Base
   NS_FORWARD_NSIDOMSVGFILTERPRIMITIVESTANDARDATTRIBUTES(nsSVGFECompositeElementBase::)
 
   virtual nsresult Filter(nsSVGFilterInstance* aInstance,
                           const nsTArray<const Image*>& aSources,
                           const Image* aTarget,
                           const nsIntRect& aDataRect);
+  virtual bool AttributeAffectsRendering(
+          PRInt32 aNameSpaceID, nsIAtom* aAttribute) const;
   virtual nsSVGString& GetResultImageName() { return mStringAttributes[RESULT]; }
   virtual void GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources);
   virtual nsIntRect ComputeTargetBBox(const nsTArray<nsIntRect>& aSourceBBoxes,
           const nsSVGFilterInstance& aInstance);
 
   // Composite
   NS_DECL_NSIDOMSVGFECOMPOSITEELEMENT
 
@@ -1592,16 +1562,31 @@ nsSVGFECompositeElement::Filter(nsSVGFil
                                            gfxContext::OPERATOR_ATOP,
                                            gfxContext::OPERATOR_XOR };
   ctx.SetOperator(opMap[op]);
   ctx.SetSource(aSources[0]->mImage);
   ctx.Paint();
   return NS_OK;
 }
 
+bool
+nsSVGFECompositeElement::AttributeAffectsRendering(PRInt32 aNameSpaceID,
+                                                   nsIAtom* aAttribute) const
+{
+  return nsSVGFECompositeElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) ||
+         (aNameSpaceID == kNameSpaceID_None &&
+          (aAttribute == nsGkAtoms::in ||
+           aAttribute == nsGkAtoms::in2 ||
+           aAttribute == nsGkAtoms::k1 ||
+           aAttribute == nsGkAtoms::k2 ||
+           aAttribute == nsGkAtoms::k3 ||
+           aAttribute == nsGkAtoms::k4 ||
+           aAttribute == nsGkAtoms::_operator));
+}
+
 void
 nsSVGFECompositeElement::GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources)
 {
   aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN1], this));
   aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN2], this));
 }
 
 nsIntRect
@@ -1675,16 +1660,18 @@ public:
 
   // FE Base
   NS_FORWARD_NSIDOMSVGFILTERPRIMITIVESTANDARDATTRIBUTES(nsSVGFEComponentTransferElementBase::)
 
   virtual nsresult Filter(nsSVGFilterInstance* aInstance,
                           const nsTArray<const Image*>& aSources,
                           const Image* aTarget,
                           const nsIntRect& aDataRect);
+  virtual bool AttributeAffectsRendering(
+          PRInt32 aNameSpaceID, nsIAtom* aAttribute) const;
   virtual nsSVGString& GetResultImageName() { return mStringAttributes[RESULT]; }
   virtual void GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources);
 
   // Component Transfer
   NS_DECL_NSIDOMSVGFECOMPONENTTRANSFERELEMENT
 
   NS_FORWARD_NSIDOMSVGELEMENT(nsSVGFEComponentTransferElementBase::)
 
@@ -1755,49 +1742,43 @@ nsSVGFEComponentTransferElement::GetStri
 }
 
 //--------------------------------------------
 
 #define NS_SVG_FE_COMPONENT_TRANSFER_FUNCTION_ELEMENT_CID \
     { 0xafab106d, 0xbc18, 0x4f7f, \
   { 0x9e, 0x29, 0xfe, 0xb4, 0xb0, 0x16, 0x5f, 0xf4 } }
 
-typedef nsSVGElement nsSVGComponentTransferFunctionElementBase;
+typedef SVGFEUnstyledElement nsSVGComponentTransferFunctionElementBase;
 
 class nsSVGComponentTransferFunctionElement : public nsSVGComponentTransferFunctionElementBase
 {
   friend nsresult NS_NewSVGComponentTransferFunctionElement(nsIContent **aResult,
                                                             already_AddRefed<nsINodeInfo> aNodeInfo);
 protected:
   nsSVGComponentTransferFunctionElement(already_AddRefed<nsINodeInfo> aNodeInfo)
     : nsSVGComponentTransferFunctionElementBase(aNodeInfo) {}
 
 public:
   // interfaces:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_SVG_FE_COMPONENT_TRANSFER_FUNCTION_ELEMENT_CID)
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIDOMSVGCOMPONENTTRANSFERFUNCTIONELEMENT
 
+  virtual bool AttributeAffectsRendering(
+          PRInt32 aNameSpaceID, nsIAtom* aAttribute) const;
+
   virtual PRInt32 GetChannel() = 0;
   void GenerateLookupTable(PRUint8* aTable);
 
 protected:
   virtual NumberAttributesInfo GetNumberInfo();
-  virtual void DidAnimateNumber(PRUint8 aAttrEnum) {
-    DidAnimateAttrViaParent(this);
-  }
   virtual EnumAttributesInfo GetEnumInfo();
-  virtual void DidAnimateEnum(PRUint8 aAttrEnum) {
-    DidAnimateAttrViaParent(this);
-  }
   virtual NumberListAttributesInfo GetNumberListInfo();
-  virtual void DidAnimateNumberList(PRUint8 aAttrEnum) {
-    DidAnimateAttrViaParent(this);
-  }
 
   // nsIDOMSVGComponentTransferFunctionElement properties:
   enum { TABLEVALUES };
   SVGAnimatedNumberList mNumberListAttributes[1];
   static NumberListInfo sNumberListInfo[1];
 
   enum { SLOPE, INTERCEPT, AMPLITUDE, EXPONENT, OFFSET };
   nsSVGNumber2 mNumberAttributes[5];
@@ -1846,16 +1827,25 @@ nsSVGFEComponentTransferElement::Filter(
         tableR[sourceData[targIndex + GFX_ARGB32_OFFSET_R]];
       targetData[targIndex + GFX_ARGB32_OFFSET_A] =
         tableA[sourceData[targIndex + GFX_ARGB32_OFFSET_A]];
     }
   }
   return NS_OK;
 }
 
+bool
+nsSVGFEComponentTransferElement::AttributeAffectsRendering(PRInt32 aNameSpaceID,
+                                                           nsIAtom* aAttribute) const
+{
+  return nsSVGFEComponentTransferElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) ||
+         (aNameSpaceID == kNameSpaceID_None &&
+          aAttribute == nsGkAtoms::in);
+}
+
 void
 nsSVGFEComponentTransferElement::GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources)
 {
   aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN1], this));
 }
 
 nsSVGElement::NumberListInfo nsSVGComponentTransferFunctionElement::sNumberListInfo[1] =
 {
@@ -1906,16 +1896,33 @@ NS_INTERFACE_MAP_BEGIN(nsSVGComponentTra
    // around that
    if ( aIID.Equals(NS_GET_IID(nsSVGComponentTransferFunctionElement)) )
      foundInterface = static_cast<nsISupports*>(static_cast<void*>(this));
    else
 NS_INTERFACE_MAP_END_INHERITING(nsSVGComponentTransferFunctionElementBase)
 
 
 //----------------------------------------------------------------------
+// nsFEUnstyledElement methods
+
+bool
+nsSVGComponentTransferFunctionElement::AttributeAffectsRendering(PRInt32 aNameSpaceID,
+                                                                 nsIAtom* aAttribute) const
+{
+  return aNameSpaceID == kNameSpaceID_None &&
+         (aAttribute == nsGkAtoms::tableValues ||
+          aAttribute == nsGkAtoms::slope ||
+          aAttribute == nsGkAtoms::intercept ||
+          aAttribute == nsGkAtoms::amplitude ||
+          aAttribute == nsGkAtoms::exponent ||
+          aAttribute == nsGkAtoms::offset ||
+          aAttribute == nsGkAtoms::type);
+}
+
+//----------------------------------------------------------------------
 // nsIDOMSVGComponentTransferFunctionElement methods
 
 /* readonly attribute nsIDOMSVGAnimatedEnumeration type; */
 NS_IMETHODIMP nsSVGComponentTransferFunctionElement::GetType(nsIDOMSVGAnimatedEnumeration * *aType)
 {
   return mEnumAttributes[TYPE].ToDOMAnimatedEnum(aType, this);
 }
 
@@ -2281,17 +2288,17 @@ public:
 protected:
   virtual StringAttributesInfo GetStringInfo();
 
   enum { RESULT };
   nsSVGString mStringAttributes[1];
   static StringInfo sStringInfo[1];
 };
 
-typedef nsSVGStylableElement nsSVGFEMergeNodeElementBase;
+typedef SVGFEUnstyledElement nsSVGFEMergeNodeElementBase;
 
 #define NS_SVG_FE_MERGE_NODE_CID \
     { 0x413687ec, 0x77fd, 0x4077, \
   { 0x9d, 0x7a, 0x97, 0x51, 0xa8, 0x4b, 0x7b, 0x40 } }
 
 class nsSVGFEMergeNodeElement : public nsSVGFEMergeNodeElementBase,
                                 public nsIDOMSVGFEMergeNodeElement
 {
@@ -2311,16 +2318,19 @@ public:
 
   NS_FORWARD_NSIDOMSVGELEMENT(nsSVGFEMergeNodeElementBase::)
 
   NS_FORWARD_NSIDOMNODE(nsSVGFEMergeNodeElementBase::)
   NS_FORWARD_NSIDOMELEMENT(nsSVGFEMergeNodeElementBase::)
 
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
 
+  virtual bool AttributeAffectsRendering(
+          PRInt32 aNameSpaceID, nsIAtom* aAttribute) const;
+
   const nsSVGString* In1() { return &mStringAttributes[IN1]; }
   
   operator nsISupports*() { return static_cast<nsIContent*>(this); }
 
   virtual nsXPCClassInfo* GetClassInfo();
 protected:
   virtual StringAttributesInfo GetStringInfo();
 
@@ -2428,16 +2438,26 @@ NS_INTERFACE_TABLE_HEAD(nsSVGFEMergeNode
 NS_INTERFACE_MAP_END_INHERITING(nsSVGFEMergeNodeElementBase)
 
 //----------------------------------------------------------------------
 // nsIDOMNode methods
 
 NS_IMPL_ELEMENT_CLONE_WITH_INIT(nsSVGFEMergeNodeElement)
 
 //----------------------------------------------------------------------
+// nsFEUnstyledElement methods
+
+bool
+nsSVGFEMergeNodeElement::AttributeAffectsRendering(PRInt32 aNameSpaceID,
+                                                   nsIAtom* aAttribute) const
+{
+  return aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::in;
+}
+
+//----------------------------------------------------------------------
 // nsIDOMSVGFEMergeNodeElement methods
 
 /* readonly attribute nsIDOMSVGAnimatedString in1; */
 NS_IMETHODIMP nsSVGFEMergeNodeElement::GetIn1(nsIDOMSVGAnimatedString * *aIn)
 {
   return mStringAttributes[IN1].ToDOMAnimatedString(aIn, this);
 }
 
@@ -2470,16 +2490,18 @@ public:
 
   // FE Base
   NS_FORWARD_NSIDOMSVGFILTERPRIMITIVESTANDARDATTRIBUTES(nsSVGFEOffsetElementBase::)
 
   virtual nsresult Filter(nsSVGFilterInstance* aInstance,
                           const nsTArray<const Image*>& aSources,
                           const Image* aTarget,
                           const nsIntRect& aDataRect);
+  virtual bool AttributeAffectsRendering(
+          PRInt32 aNameSpaceID, nsIAtom* aAttribute) const;
   virtual nsSVGString& GetResultImageName() { return mStringAttributes[RESULT]; }
   virtual void GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources);
   virtual nsIntRect ComputeTargetBBox(const nsTArray<nsIntRect>& aSourceBBoxes,
           const nsSVGFilterInstance& aInstance);
   virtual void ComputeNeededSourceBBoxes(const nsIntRect& aTargetBBox,
           nsTArray<nsIntRect>& aSourceBBoxes, const nsSVGFilterInstance& aInstance);
   virtual nsIntRect ComputeChangeBBox(const nsTArray<nsIntRect>& aSourceChangeBoxes,
           const nsSVGFilterInstance& aInstance);
@@ -2591,16 +2613,27 @@ nsSVGFEOffsetElement::Filter(nsSVGFilter
   ctx.Clip(aTarget->mFilterPrimitiveSubregion);
   ctx.Translate(gfxPoint(offset.x, offset.y));
   ctx.SetSource(aSources[0]->mImage);
   ctx.Paint();
 
   return NS_OK;
 }
 
+bool
+nsSVGFEOffsetElement::AttributeAffectsRendering(PRInt32 aNameSpaceID,
+                                                nsIAtom* aAttribute) const
+{
+  return nsSVGFEOffsetElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) ||
+         (aNameSpaceID == kNameSpaceID_None &&
+          (aAttribute == nsGkAtoms::in ||
+           aAttribute == nsGkAtoms::dx ||
+           aAttribute == nsGkAtoms::dy));
+}
+
 void
 nsSVGFEOffsetElement::GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources)
 {
   aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN1], this));
 }
 
 nsIntRect
 nsSVGFEOffsetElement::ComputeTargetBBox(const nsTArray<nsIntRect>& aSourceBBoxes,
@@ -2802,16 +2835,18 @@ public:
 
   // FE Base
   NS_FORWARD_NSIDOMSVGFILTERPRIMITIVESTANDARDATTRIBUTES(nsSVGFETileElementBase::)
 
   virtual nsresult Filter(nsSVGFilterInstance* aInstance,
                           const nsTArray<const Image*>& aSources,
                           const Image* aTarget,
                           const nsIntRect& aDataRect);
+  virtual bool AttributeAffectsRendering(
+          PRInt32 aNameSpaceID, nsIAtom* aAttribute) const;
   virtual nsSVGString& GetResultImageName() { return mStringAttributes[RESULT]; }
   virtual void GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources);
   virtual nsIntRect ComputeTargetBBox(const nsTArray<nsIntRect>& aSourceBBoxes,
           const nsSVGFilterInstance& aInstance);
   virtual void ComputeNeededSourceBBoxes(const nsIntRect& aTargetBBox,
           nsTArray<nsIntRect>& aSourceBBoxes, const nsSVGFilterInstance& aInstance);
   virtual nsIntRect ComputeChangeBBox(const nsTArray<nsIntRect>& aSourceChangeBoxes,
           const nsSVGFilterInstance& aInstance);
@@ -2957,16 +2992,25 @@ nsSVGFETileElement::Filter(nsSVGFilterIn
         }
       }
     }
   }
 
   return NS_OK;
 }
 
+bool
+nsSVGFETileElement::AttributeAffectsRendering(PRInt32 aNameSpaceID,
+                                              nsIAtom* aAttribute) const
+{
+  return nsSVGFETileElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) ||
+         (aNameSpaceID == kNameSpaceID_None &&
+          aAttribute == nsGkAtoms::in);
+}
+
 //----------------------------------------------------------------------
 // nsSVGElement methods
 
 nsSVGElement::StringAttributesInfo
 nsSVGFETileElement::GetStringInfo()
 {
   return StringAttributesInfo(mStringAttributes, sStringInfo,
                               ArrayLength(sStringInfo));
@@ -2993,16 +3037,18 @@ public:
 
   // FE Base
   NS_FORWARD_NSIDOMSVGFILTERPRIMITIVESTANDARDATTRIBUTES(nsSVGFETurbulenceElementBase::)
 
   virtual nsresult Filter(nsSVGFilterInstance* aInstance,
                           const nsTArray<const Image*>& aSources,
                           const Image* aTarget,
                           const nsIntRect& aDataRect);
+  virtual bool AttributeAffectsRendering(
+          PRInt32 aNameSpaceID, nsIAtom* aAttribute) const;
   virtual nsSVGString& GetResultImageName() { return mStringAttributes[RESULT]; }
   virtual nsIntRect ComputeTargetBBox(const nsTArray<nsIntRect>& aSourceBBoxes,
           const nsSVGFilterInstance& aInstance);
 
   // Turbulence
   NS_DECL_NSIDOMSVGFETURBULENCEELEMENT
 
   NS_FORWARD_NSIDOMSVGELEMENT(nsSVGFETurbulenceElementBase::)
@@ -3296,16 +3342,29 @@ nsSVGFETurbulenceElement::Filter(nsSVGFi
       targetData[targIndex + GFX_ARGB32_OFFSET_R] = r;
       targetData[targIndex + GFX_ARGB32_OFFSET_A] = a;
     }
   }
 
   return NS_OK;
 }
 
+bool
+nsSVGFETurbulenceElement::AttributeAffectsRendering(PRInt32 aNameSpaceID,
+                                                    nsIAtom* aAttribute) const
+{
+  return nsSVGFETurbulenceElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) ||
+         (aNameSpaceID == kNameSpaceID_None &&
+          (aAttribute == nsGkAtoms::seed ||
+           aAttribute == nsGkAtoms::baseFrequency ||
+           aAttribute == nsGkAtoms::numOctaves ||
+           aAttribute == nsGkAtoms::type ||
+           aAttribute == nsGkAtoms::stitchTiles));
+}
+
 void
 nsSVGFETurbulenceElement::InitSeed(PRInt32 aSeed)
 {
   double s;
   int i, j, k;
   aSeed = SetupSeed(aSeed);
   for (k = 0; k < 4; k++) {
     for (i = 0; i < sBSize; i++) {
@@ -3520,16 +3579,18 @@ public:
 
   // FE Base
   NS_FORWARD_NSIDOMSVGFILTERPRIMITIVESTANDARDATTRIBUTES(nsSVGFEMorphologyElementBase::)
 
   virtual nsresult Filter(nsSVGFilterInstance* aInstance,
                           const nsTArray<const Image*>& aSources,
                           const Image* aTarget,
                           const nsIntRect& aDataRect);
+  virtual bool AttributeAffectsRendering(
+          PRInt32 aNameSpaceID, nsIAtom* aAttribute) const;
   virtual nsSVGString& GetResultImageName() { return mStringAttributes[RESULT]; }
   virtual void GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources);
   virtual nsIntRect ComputeTargetBBox(const nsTArray<nsIntRect>& aSourceBBoxes,
           const nsSVGFilterInstance& aInstance);
   virtual void ComputeNeededSourceBBoxes(const nsIntRect& aTargetBBox,
           nsTArray<nsIntRect>& aSourceBBoxes, const nsSVGFilterInstance& aInstance);
   virtual nsIntRect ComputeChangeBBox(const nsTArray<nsIntRect>& aSourceChangeBoxes,
           const nsSVGFilterInstance& aInstance);
@@ -3793,16 +3854,27 @@ nsSVGFEMorphologyElement::Filter(nsSVGFi
       targetData[targIndex+1] = extrema[1];
       targetData[targIndex+2] = extrema[2];
       targetData[targIndex+3] = extrema[3];
     }
   }
   return NS_OK;
 }
 
+bool
+nsSVGFEMorphologyElement::AttributeAffectsRendering(PRInt32 aNameSpaceID,
+                                                    nsIAtom* aAttribute) const
+{
+  return nsSVGFEMorphologyElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) ||
+         (aNameSpaceID == kNameSpaceID_None &&
+          (aAttribute == nsGkAtoms::in ||
+           aAttribute == nsGkAtoms::radius ||
+           aAttribute == nsGkAtoms::_operator));
+}
+
 //----------------------------------------------------------------------
 // nsSVGElement methods
 
 nsSVGElement::NumberPairAttributesInfo
 nsSVGFEMorphologyElement::GetNumberPairInfo()
 {
   return NumberPairAttributesInfo(mNumberPairAttributes, sNumberPairInfo,
                                   ArrayLength(sNumberPairInfo));
@@ -3841,16 +3913,18 @@ public:
 
   // FE Base
   NS_FORWARD_NSIDOMSVGFILTERPRIMITIVESTANDARDATTRIBUTES(nsSVGFEConvolveMatrixElementBase::)
 
   virtual nsresult Filter(nsSVGFilterInstance* aInstance,
                           const nsTArray<const Image*>& aSources,
                           const Image* aTarget,
                           const nsIntRect& aDataRect);
+  virtual bool AttributeAffectsRendering(
+          PRInt32 aNameSpaceID, nsIAtom* aAttribute) const;
   virtual nsSVGString& GetResultImageName() { return mStringAttributes[RESULT]; }
   virtual void GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources);
   virtual nsIntRect ComputeTargetBBox(const nsTArray<nsIntRect>& aSourceBBoxes,
           const nsSVGFilterInstance& aInstance);
   virtual void ComputeNeededSourceBBoxes(const nsIntRect& aTargetBBox,
           nsTArray<nsIntRect>& aSourceBBoxes, const nsSVGFilterInstance& aInstance);
   virtual nsIntRect ComputeChangeBBox(const nsTArray<nsIntRect>& aSourceChangeBoxes,
           const nsSVGFilterInstance& aInstance);
@@ -4251,16 +4325,34 @@ nsSVGFEConvolveMatrixElement::Filter(nsS
     }
   }
 
   FinishScalingFilter(&info);
 
   return NS_OK;
 }
 
+bool
+nsSVGFEConvolveMatrixElement::AttributeAffectsRendering(PRInt32 aNameSpaceID,
+                                                        nsIAtom* aAttribute) const
+{
+  return nsSVGFEConvolveMatrixElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) ||
+         (aNameSpaceID == kNameSpaceID_None &&
+          (aAttribute == nsGkAtoms::in ||
+           aAttribute == nsGkAtoms::divisor ||
+           aAttribute == nsGkAtoms::bias ||
+           aAttribute == nsGkAtoms::kernelUnitLength ||
+           aAttribute == nsGkAtoms::targetX ||
+           aAttribute == nsGkAtoms::targetY ||
+           aAttribute == nsGkAtoms::order ||
+           aAttribute == nsGkAtoms::preserveAlpha||
+           aAttribute == nsGkAtoms::edgeMode ||
+           aAttribute == nsGkAtoms::kernelMatrix));
+}
+
 //----------------------------------------------------------------------
 // nsSVGElement methods
 
 nsSVGElement::NumberAttributesInfo
 nsSVGFEConvolveMatrixElement::GetNumberInfo()
 {
   return NumberAttributesInfo(mNumberAttributes, sNumberInfo,
                               ArrayLength(sNumberInfo));
@@ -4312,17 +4404,17 @@ nsSVGElement::NumberListAttributesInfo
 nsSVGFEConvolveMatrixElement::GetNumberListInfo()
 {
   return NumberListAttributesInfo(mNumberListAttributes, sNumberListInfo,
                                   ArrayLength(sNumberListInfo));
 }
 
 //---------------------DistantLight------------------------
 
-typedef nsSVGElement nsSVGFEDistantLightElementBase;
+typedef SVGFEUnstyledElement nsSVGFEDistantLightElementBase;
 
 class nsSVGFEDistantLightElement : public nsSVGFEDistantLightElementBase,
                                    public nsIDOMSVGFEDistantLightElement
 {
   friend nsresult NS_NewSVGFEDistantLightElement(nsIContent **aResult,
                                                  already_AddRefed<nsINodeInfo> aNodeInfo);
 protected:
   nsSVGFEDistantLightElement(already_AddRefed<nsINodeInfo> aNodeInfo)
@@ -4332,24 +4424,24 @@ public:
   // interfaces:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIDOMSVGFEDISTANTLIGHTELEMENT
 
   NS_FORWARD_NSIDOMSVGELEMENT(nsSVGFEDistantLightElementBase::)
   NS_FORWARD_NSIDOMNODE(nsSVGFEDistantLightElementBase::)
   NS_FORWARD_NSIDOMELEMENT(nsSVGFEDistantLightElementBase::)
 
+  virtual bool AttributeAffectsRendering(
+          PRInt32 aNameSpaceID, nsIAtom* aAttribute) const;
+
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
 
   virtual nsXPCClassInfo* GetClassInfo();
 protected:
   virtual NumberAttributesInfo GetNumberInfo();
-  virtual void DidAnimateNumber(PRUint8 aAttrEnum) {
-    DidAnimateAttrViaParent(this);
-  }
 
   enum { AZIMUTH, ELEVATION };
   nsSVGNumber2 mNumberAttributes[2];
   static NumberInfo sNumberInfo[2];
 };
 
 NS_IMPL_NS_NEW_SVG_ELEMENT(FEDistantLight)
 
@@ -4375,16 +4467,28 @@ NS_INTERFACE_TABLE_HEAD(nsSVGFEDistantLi
 NS_INTERFACE_MAP_END_INHERITING(nsSVGFEDistantLightElementBase)
 
 //----------------------------------------------------------------------
 // nsIDOMNode methods
 
 NS_IMPL_ELEMENT_CLONE_WITH_INIT(nsSVGFEDistantLightElement)
 
 //----------------------------------------------------------------------
+// nsFEUnstyledElement methods
+
+bool
+nsSVGFEDistantLightElement::AttributeAffectsRendering(PRInt32 aNameSpaceID,
+                                                      nsIAtom* aAttribute) const
+{
+  return aNameSpaceID == kNameSpaceID_None &&
+         (aAttribute == nsGkAtoms::azimuth ||
+          aAttribute == nsGkAtoms::elevation);
+}
+
+//----------------------------------------------------------------------
 // nsIDOMSVGFEDistantLightElement methods
 
 NS_IMETHODIMP
 nsSVGFEDistantLightElement::GetAzimuth(nsIDOMSVGAnimatedNumber **aAzimuth)
 {
   return mNumberAttributes[AZIMUTH].ToDOMAnimatedNumber(aAzimuth,
                                                         this);
 }
@@ -4403,17 +4507,17 @@ nsSVGElement::NumberAttributesInfo
 nsSVGFEDistantLightElement::GetNumberInfo()
 {
   return NumberAttributesInfo(mNumberAttributes, sNumberInfo,
                               ArrayLength(sNumberInfo));
 }
 
 //---------------------PointLight------------------------
 
-typedef nsSVGElement nsSVGFEPointLightElementBase;
+typedef SVGFEUnstyledElement nsSVGFEPointLightElementBase;
 
 class nsSVGFEPointLightElement : public nsSVGFEPointLightElementBase,
                                  public nsIDOMSVGFEPointLightElement
 {
   friend nsresult NS_NewSVGFEPointLightElement(nsIContent **aResult,
                                                  already_AddRefed<nsINodeInfo> aNodeInfo);
 protected:
   nsSVGFEPointLightElement(already_AddRefed<nsINodeInfo> aNodeInfo)
@@ -4423,24 +4527,24 @@ public:
   // interfaces:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIDOMSVGFEPOINTLIGHTELEMENT
 
   NS_FORWARD_NSIDOMSVGELEMENT(nsSVGFEPointLightElementBase::)
   NS_FORWARD_NSIDOMNODE(nsSVGFEPointLightElementBase::)
   NS_FORWARD_NSIDOMELEMENT(nsSVGFEPointLightElementBase::)
 
+  virtual bool AttributeAffectsRendering(
+          PRInt32 aNameSpaceID, nsIAtom* aAttribute) const;
+
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
 
   virtual nsXPCClassInfo* GetClassInfo();
 protected:
   virtual NumberAttributesInfo GetNumberInfo();
-  virtual void DidAnimateNumber(PRUint8 aAttrEnum) {
-    DidAnimateAttrViaParent(this);
-  }
 
   enum { X, Y, Z };
   nsSVGNumber2 mNumberAttributes[3];
   static NumberInfo sNumberInfo[3];
 };
 
 NS_IMPL_NS_NEW_SVG_ELEMENT(FEPointLight)
 
@@ -4467,16 +4571,29 @@ NS_INTERFACE_TABLE_HEAD(nsSVGFEPointLigh
 NS_INTERFACE_MAP_END_INHERITING(nsSVGFEPointLightElementBase)
 
 //----------------------------------------------------------------------
 // nsIDOMNode methods
 
 NS_IMPL_ELEMENT_CLONE_WITH_INIT(nsSVGFEPointLightElement)
 
 //----------------------------------------------------------------------
+// nsFEUnstyledElement methods
+
+bool
+nsSVGFEPointLightElement::AttributeAffectsRendering(PRInt32 aNameSpaceID,
+                                                    nsIAtom* aAttribute) const
+{
+  return aNameSpaceID == kNameSpaceID_None &&
+         (aAttribute == nsGkAtoms::x ||
+          aAttribute == nsGkAtoms::y ||
+          aAttribute == nsGkAtoms::z);
+}
+
+//----------------------------------------------------------------------
 // nsIDOMSVGFEPointLightElement methods
 
 NS_IMETHODIMP
 nsSVGFEPointLightElement::GetX(nsIDOMSVGAnimatedNumber **aX)
 {
   return mNumberAttributes[X].ToDOMAnimatedNumber(aX, this);
 }
 
@@ -4499,17 +4616,17 @@ nsSVGElement::NumberAttributesInfo
 nsSVGFEPointLightElement::GetNumberInfo()
 {
   return NumberAttributesInfo(mNumberAttributes, sNumberInfo,
                               ArrayLength(sNumberInfo));
 }
 
 //---------------------SpotLight------------------------
 
-typedef nsSVGElement nsSVGFESpotLightElementBase;
+typedef SVGFEUnstyledElement nsSVGFESpotLightElementBase;
 
 class nsSVGFESpotLightElement : public nsSVGFESpotLightElementBase,
                                 public nsIDOMSVGFESpotLightElement
 {
   friend nsresult NS_NewSVGFESpotLightElement(nsIContent **aResult,
                                               already_AddRefed<nsINodeInfo> aNodeInfo);
   friend class nsSVGFELightingElement;
 protected:
@@ -4520,24 +4637,24 @@ public:
   // interfaces:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIDOMSVGFESPOTLIGHTELEMENT
 
   NS_FORWARD_NSIDOMSVGELEMENT(nsSVGFESpotLightElementBase::)
   NS_FORWARD_NSIDOMNODE(nsSVGFESpotLightElementBase::)
   NS_FORWARD_NSIDOMELEMENT(nsSVGFESpotLightElementBase::)
 
+  virtual bool AttributeAffectsRendering(
+          PRInt32 aNameSpaceID, nsIAtom* aAttribute) const;
+
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
 
   virtual nsXPCClassInfo* GetClassInfo();
 protected:
   virtual NumberAttributesInfo GetNumberInfo();
-  virtual void DidAnimateNumber(PRUint8 aAttrEnum) {
-    DidAnimateAttrViaParent(this);
-  }
 
   enum { X, Y, Z, POINTS_AT_X, POINTS_AT_Y, POINTS_AT_Z,
          SPECULAR_EXPONENT, LIMITING_CONE_ANGLE };
   nsSVGNumber2 mNumberAttributes[8];
   static NumberInfo sNumberInfo[8];
 };
 
 NS_IMPL_NS_NEW_SVG_ELEMENT(FESpotLight)
@@ -4570,16 +4687,34 @@ NS_INTERFACE_TABLE_HEAD(nsSVGFESpotLight
 NS_INTERFACE_MAP_END_INHERITING(nsSVGFESpotLightElementBase)
 
 //----------------------------------------------------------------------
 // nsIDOMNode methods
 
 NS_IMPL_ELEMENT_CLONE_WITH_INIT(nsSVGFESpotLightElement)
 
 //----------------------------------------------------------------------
+// nsFEUnstyledElement methods
+
+bool
+nsSVGFESpotLightElement::AttributeAffectsRendering(PRInt32 aNameSpaceID,
+                                                   nsIAtom* aAttribute) const
+{
+  return aNameSpaceID == kNameSpaceID_None &&
+         (aAttribute == nsGkAtoms::x ||
+          aAttribute == nsGkAtoms::y ||
+          aAttribute == nsGkAtoms::z ||
+          aAttribute == nsGkAtoms::pointsAtX ||
+          aAttribute == nsGkAtoms::pointsAtY ||
+          aAttribute == nsGkAtoms::pointsAtZ ||
+          aAttribute == nsGkAtoms::specularExponent ||
+          aAttribute == nsGkAtoms::limitingConeAngle);
+}
+
+//----------------------------------------------------------------------
 // nsIDOMSVGFESpotLightElement methods
 
 NS_IMETHODIMP
 nsSVGFESpotLightElement::GetX(nsIDOMSVGAnimatedNumber **aX)
 {
   return mNumberAttributes[X].ToDOMAnimatedNumber(aX, this);
 }
 
@@ -4653,16 +4788,18 @@ public:
 
   // FE Base
   NS_FORWARD_NSIDOMSVGFILTERPRIMITIVESTANDARDATTRIBUTES(nsSVGFELightingElementBase::)
 
   virtual nsresult Filter(nsSVGFilterInstance* aInstance,
                           const nsTArray<const Image*>& aSources,
                           const Image* aTarget,
                           const nsIntRect& aDataRect);
+  virtual bool AttributeAffectsRendering(
+          PRInt32 aNameSpaceID, nsIAtom* aAttribute) const;
   virtual nsSVGString& GetResultImageName() { return mStringAttributes[RESULT]; }
   virtual void GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources);
   // XXX shouldn't we have ComputeTargetBBox here, since the output can
   // extend beyond the bounds of the inputs thanks to the convolution kernel?
   virtual void ComputeNeededSourceBBoxes(const nsIntRect& aTargetBBox,
           nsTArray<nsIntRect>& aSourceBBoxes, const nsSVGFilterInstance& aInstance);
   virtual nsIntRect ComputeChangeBBox(const nsTArray<nsIntRect>& aSourceChangeBoxes,
           const nsSVGFilterInstance& aInstance);
@@ -4980,16 +5117,27 @@ nsSVGFELightingElement::Filter(nsSVGFilt
     }
   }
 
   FinishScalingFilter(&info);
 
   return NS_OK;
 }
 
+bool
+nsSVGFELightingElement::AttributeAffectsRendering(PRInt32 aNameSpaceID,
+                                                  nsIAtom* aAttribute) const
+{
+  return nsSVGFELightingElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) ||
+         (aNameSpaceID == kNameSpaceID_None &&
+          (aAttribute == nsGkAtoms::in ||
+           aAttribute == nsGkAtoms::surfaceScale ||
+           aAttribute == nsGkAtoms::kernelUnitLength));
+}
+
 //----------------------------------------------------------------------
 // nsSVGElement methods
 
 nsSVGElement::NumberAttributesInfo
 nsSVGFELightingElement::GetNumberInfo()
 {
   return NumberAttributesInfo(mNumberAttributes, sNumberInfo,
                               ArrayLength(sNumberInfo));
@@ -5029,16 +5177,19 @@ public:
   // DiffuseLighting
   NS_DECL_NSIDOMSVGFEDIFFUSELIGHTINGELEMENT
 
   NS_FORWARD_NSIDOMSVGFILTERPRIMITIVESTANDARDATTRIBUTES(nsSVGFEDiffuseLightingElementBase::)
   NS_FORWARD_NSIDOMSVGELEMENT(nsSVGFEDiffuseLightingElementBase::)
   NS_FORWARD_NSIDOMNODE(nsSVGFEDiffuseLightingElementBase::)
   NS_FORWARD_NSIDOMELEMENT(nsSVGFEDiffuseLightingElementBase::)
 
+  virtual bool AttributeAffectsRendering(
+          PRInt32 aNameSpaceID, nsIAtom* aAttribute) const;
+
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
 
   virtual nsXPCClassInfo* GetClassInfo();
 protected:
   virtual void LightPixel(const float *N, const float *L,
                           nscolor color, PRUint8 *targetData);
 
 };
@@ -5100,16 +5251,25 @@ nsSVGFEDiffuseLightingElement::GetKernel
 NS_IMETHODIMP
 nsSVGFEDiffuseLightingElement::GetKernelUnitLengthY(nsIDOMSVGAnimatedNumber **aKernelY)
 {
   return mNumberPairAttributes[KERNEL_UNIT_LENGTH].ToDOMAnimatedNumber(aKernelY,
                                                                        nsSVGNumberPair::eSecond,
                                                                        this);
 }
 
+bool
+nsSVGFEDiffuseLightingElement::AttributeAffectsRendering(PRInt32 aNameSpaceID,
+                                                         nsIAtom* aAttribute) const
+{
+  return nsSVGFEDiffuseLightingElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) ||
+         (aNameSpaceID == kNameSpaceID_None &&
+          aAttribute == nsGkAtoms::diffuseConstant);
+}
+
 //----------------------------------------------------------------------
 // nsSVGElement methods
 
 void
 nsSVGFEDiffuseLightingElement::LightPixel(const float *N, const float *L,
                                           nscolor color, PRUint8 *targetData)
 {
   float diffuseNL =
@@ -5157,16 +5317,18 @@ public:
   NS_FORWARD_NSIDOMELEMENT(nsSVGFESpecularLightingElementBase::)
 
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
 
   virtual nsresult Filter(nsSVGFilterInstance* aInstance,
                           const nsTArray<const Image*>& aSources,
                           const Image* aTarget,
                           const nsIntRect& aDataRect);
+  virtual bool AttributeAffectsRendering(
+          PRInt32 aNameSpaceID, nsIAtom* aAttribute) const;
 
   virtual nsXPCClassInfo* GetClassInfo();
 protected:
   virtual void LightPixel(const float *N, const float *L,
                           nscolor color, PRUint8 *targetData);
 
 };
 
@@ -5252,16 +5414,25 @@ nsSVGFESpecularLightingElement::Filter(n
 
   // specification defined range (15.22)
   if (specularExponent < 1 || specularExponent > 128)
     return NS_ERROR_FAILURE;
 
   return nsSVGFESpecularLightingElementBase::Filter(instance, aSources, aTarget, rect);
 }
 
+bool
+nsSVGFESpecularLightingElement::AttributeAffectsRendering(PRInt32 aNameSpaceID,
+                                                          nsIAtom* aAttribute) const
+{
+  return nsSVGFESpecularLightingElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) ||
+         (aNameSpaceID == kNameSpaceID_None &&
+          (aAttribute == nsGkAtoms::specularConstant ||
+           aAttribute == nsGkAtoms::specularExponent));
+}
 
 void
 nsSVGFESpecularLightingElement::LightPixel(const float *N, const float *L,
                                            nscolor color, PRUint8 *targetData)
 {
   float H[3];
   H[0] = L[0];
   H[1] = L[1];
@@ -5291,100 +5462,16 @@ nsSVGFESpecularLightingElement::LightPix
     targetData[GFX_ARGB32_OFFSET_G] = 0;
     targetData[GFX_ARGB32_OFFSET_R] = 0;
     targetData[GFX_ARGB32_OFFSET_A] = 255;
   }
 }
 
 //---------------------Image------------------------
 
-typedef nsSVGFE nsSVGFEImageElementBase;
-
-class nsSVGFEImageElement : public nsSVGFEImageElementBase,
-                            public nsIDOMSVGFEImageElement,
-                            public nsIDOMSVGURIReference,
-                            public nsImageLoadingContent
-{
-protected:
-  friend nsresult NS_NewSVGFEImageElement(nsIContent **aResult,
-                                          already_AddRefed<nsINodeInfo> aNodeInfo);
-  nsSVGFEImageElement(already_AddRefed<nsINodeInfo> aNodeInfo);
-  virtual ~nsSVGFEImageElement();
-
-public:
-  virtual bool SubregionIsUnionOfRegions() { return false; }
-
-  // interfaces:
-  NS_DECL_ISUPPORTS_INHERITED
-
-  // FE Base
-  NS_FORWARD_NSIDOMSVGFILTERPRIMITIVESTANDARDATTRIBUTES(nsSVGFEImageElementBase::)
-
-  virtual nsresult Filter(nsSVGFilterInstance* aInstance,
-                          const nsTArray<const Image*>& aSources,
-                          const Image* aTarget,
-                          const nsIntRect& aDataRect);
-  virtual nsSVGString& GetResultImageName() { return mStringAttributes[RESULT]; }
-  virtual nsIntRect ComputeTargetBBox(const nsTArray<nsIntRect>& aSourceBBoxes,
-          const nsSVGFilterInstance& aInstance);
-
-  NS_DECL_NSIDOMSVGFEIMAGEELEMENT
-  NS_DECL_NSIDOMSVGURIREFERENCE
-
-  NS_FORWARD_NSIDOMSVGELEMENT(nsSVGFEImageElementBase::)
-
-  NS_FORWARD_NSIDOMNODE(nsSVGFEImageElementBase::)
-  NS_FORWARD_NSIDOMELEMENT(nsSVGFEImageElementBase::)
-
-  // nsIContent
-  NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const;
-
-  virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
-
-  virtual nsresult AfterSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
-                                const nsAString* aValue, bool aNotify);
-  virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
-                              nsIContent* aBindingParent,
-                              bool aCompileEventHandlers);
-  virtual nsEventStates IntrinsicState() const;
-
-  // imgIDecoderObserver
-  NS_IMETHOD OnStopDecode(imgIRequest *aRequest, nsresult status,
-                          const PRUnichar *statusArg);
-  // imgIContainerObserver
-  NS_IMETHOD FrameChanged(imgIContainer *aContainer,
-                          const nsIntRect *aDirtyRect);
-  // imgIContainerObserver
-  NS_IMETHOD OnStartContainer(imgIRequest *aRequest,
-                              imgIContainer *aContainer);
-
-  void MaybeLoadSVGImage();
-
-  virtual nsXPCClassInfo* GetClassInfo();
-private:
-  // Invalidate users of the filter containing this element.
-  void Invalidate();
-
-  nsresult LoadSVGImage(bool aForce, bool aNotify);
-
-protected:
-  virtual bool OperatesOnSRGB(nsSVGFilterInstance*,
-                                PRInt32, Image*) { return true; }
-
-  virtual SVGAnimatedPreserveAspectRatio *GetPreserveAspectRatio();
-  virtual StringAttributesInfo GetStringInfo();
-  virtual void DidAnimateString(PRUint8 aAttrEnum);
-
-  enum { RESULT, HREF };
-  nsSVGString mStringAttributes[2];
-  static StringInfo sStringInfo[2];
-
-  SVGAnimatedPreserveAspectRatio mPreserveAspectRatio;
-};
-
 nsSVGElement::StringInfo nsSVGFEImageElement::sStringInfo[2] =
 {
   { &nsGkAtoms::result, kNameSpaceID_None, true },
   { &nsGkAtoms::href, kNameSpaceID_XLink, true }
 };
 
 NS_IMPL_NS_NEW_SVG_ELEMENT(FEImage)
 
@@ -5463,20 +5550,32 @@ nsSVGFEImageElement::IsAttributeMapped(c
     nsSVGFEImageElementBase::IsAttributeMapped(name);
 }
 
 nsresult
 nsSVGFEImageElement::AfterSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
                                   const nsAString* aValue, bool aNotify)
 {
   if (aNamespaceID == kNameSpaceID_XLink && aName == nsGkAtoms::href) {
-    if (aValue) {
-      LoadSVGImage(true, aNotify);
-    } else {
-      CancelImageRequests(aNotify);
+
+    // If there isn't a frame we still need to load the image in case
+    // the frame is created later e.g. by attaching to a document.
+    // If there is a frame then it should deal with loading as the image
+    // url may be animated.
+    if (!GetPrimaryFrame()) {
+
+      // Prevent setting image.src by exiting early
+      if (nsContentUtils::IsImageSrcSetDisabled()) {
+        return NS_OK;
+      }
+      if (aValue) {
+        LoadSVGImage(true, aNotify);
+      } else {
+        CancelImageRequests(aNotify);
+      }
     }
   }
 
   return nsSVGFEImageElementBase::AfterSetAttr(aNamespaceID, aName,
                                                aValue, aNotify);
 }
 
 void
@@ -5586,16 +5685,27 @@ nsSVGFEImageElement::Filter(nsSVGFilterI
     
     nsRefPtr<gfxContext> ctx = new gfxContext(aTarget->mImage);
     nsSVGUtils::CompositePatternMatrix(ctx, thebesPattern, TM, nativeWidth, nativeHeight, 1.0);
   }
 
   return NS_OK;
 }
 
+bool
+nsSVGFEImageElement::AttributeAffectsRendering(PRInt32 aNameSpaceID,
+                                               nsIAtom* aAttribute) const
+{
+  // nsGkAtoms::href is deliberately omitted as the frame has special
+  // handling to load the image
+  return nsSVGFEImageElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) ||
+         (aNameSpaceID == kNameSpaceID_None &&
+          aAttribute == nsGkAtoms::preserveAspectRatio);
+}
+
 nsIntRect
 nsSVGFEImageElement::ComputeTargetBBox(const nsTArray<nsIntRect>& aSourceBBoxes,
         const nsSVGFilterInstance& aInstance)
 {
   // XXX can do better here ... we could check what we know of the source
   // image bounds and compute an accurate bounding box for the filter
   // primitive result.
   return GetMaxRect();
@@ -5612,27 +5722,16 @@ nsSVGFEImageElement::GetPreserveAspectRa
 
 nsSVGElement::StringAttributesInfo
 nsSVGFEImageElement::GetStringInfo()
 {
   return StringAttributesInfo(mStringAttributes, sStringInfo,
                               ArrayLength(sStringInfo));
 }
 
-void
-nsSVGFEImageElement::DidAnimateString(PRUint8 aAttrEnum)
-{
-  if (aAttrEnum == HREF) {
-    LoadSVGImage(true, false);
-    return;
-  }
-
-  nsSVGFEImageElementBase::DidAnimateString(aAttrEnum);
-}
-
 //----------------------------------------------------------------------
 // imgIDecoderObserver methods
 
 NS_IMETHODIMP
 nsSVGFEImageElement::OnStopDecode(imgIRequest *aRequest,
                                   nsresult status,
                                   const PRUnichar *statusArg)
 {
@@ -5699,16 +5798,18 @@ public:
 
   // FE Base
   NS_FORWARD_NSIDOMSVGFILTERPRIMITIVESTANDARDATTRIBUTES(nsSVGFEDisplacementMapElementBase::)
 
   virtual nsresult Filter(nsSVGFilterInstance* aInstance,
                           const nsTArray<const Image*>& aSources,
                           const Image* aTarget,
                           const nsIntRect& aDataRect);
+  virtual bool AttributeAffectsRendering(
+          PRInt32 aNameSpaceID, nsIAtom* aAttribute) const;
   virtual nsSVGString& GetResultImageName() { return mStringAttributes[RESULT]; }
   virtual void GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources);
   virtual nsIntRect ComputeTargetBBox(const nsTArray<nsIntRect>& aSourceBBoxes,
           const nsSVGFilterInstance& aInstance);
   virtual void ComputeNeededSourceBBoxes(const nsIntRect& aTargetBBox,
           nsTArray<nsIntRect>& aSourceBBoxes, const nsSVGFilterInstance& aInstance);
   virtual nsIntRect ComputeChangeBBox(const nsTArray<nsIntRect>& aSourceChangeBoxes,
           const nsSVGFilterInstance& aInstance);
@@ -5898,16 +5999,29 @@ nsSVGFEDisplacementMapElement::Filter(ns
         *(PRUint32*)(targetData + targIndex) =
           *(PRUint32*)(sourceData + sourceY * stride + 4 * sourceX);
       }
     }
   }
   return NS_OK;
 }
 
+bool
+nsSVGFEDisplacementMapElement::AttributeAffectsRendering(PRInt32 aNameSpaceID,
+                                                         nsIAtom* aAttribute) const
+{
+  return nsSVGFEDisplacementMapElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) ||
+         (aNameSpaceID == kNameSpaceID_None &&
+          (aAttribute == nsGkAtoms::in ||
+           aAttribute == nsGkAtoms::in2 ||
+           aAttribute == nsGkAtoms::scale ||
+           aAttribute == nsGkAtoms::xChannelSelector ||
+           aAttribute == nsGkAtoms::yChannelSelector));
+}
+
 void
 nsSVGFEDisplacementMapElement::GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources)
 {
   aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN1], this));
   aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN2], this));
 }
 
 nsIntRect
--- a/content/svg/content/src/nsSVGFilters.h
+++ b/content/svg/content/src/nsSVGFilters.h
@@ -34,23 +34,26 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef __NS_SVGFILTERSELEMENT_H__
 #define __NS_SVGFILTERSELEMENT_H__
 
 #include "nsSVGStylableElement.h"
 #include "nsSVGLength2.h"
+#include "nsSVGString.h"
 #include "nsIFrame.h"
 #include "gfxRect.h"
 #include "gfxImageSurface.h"
 #include "nsIDOMSVGFilters.h"
+#include "nsImageLoadingContent.h"
+#include "nsIDOMSVGURIReference.h"
+#include "SVGAnimatedPreserveAspectRatio.h"
 
 class nsSVGFilterResource;
-class nsSVGString;
 class nsSVGNumberPair;
 class nsSVGFilterInstance;
 
 struct nsSVGStringInfo {
   nsSVGStringInfo(const nsSVGString* aString,
                   nsSVGElement *aElement) :
     mString(aString), mElement(aElement) {}
 
@@ -59,16 +62,21 @@ struct nsSVGStringInfo {
 };
 
 typedef nsSVGStylableElement nsSVGFEBase;
 
 #define NS_SVG_FE_CID \
 { 0x60483958, 0xd229, 0x4a77, \
   { 0x96, 0xb2, 0x62, 0x3e, 0x69, 0x95, 0x1e, 0x0e } }
 
+/**
+ * Base class for filter primitive elements
+ * Children of those elements e.g. feMergeNode
+ * derive from SVGFEUnstyledElement instead
+ */
 class nsSVGFE : public nsSVGFEBase
 //, public nsIDOMSVGFilterPrimitiveStandardAttributes
 {
   friend class nsSVGFilterInstance;
 
 public:
   class ColorModel {
   public:
@@ -189,16 +197,21 @@ public:
   // filter primitive subregion for this filter element and the
   // temporary surface area. Output need not be clipped to this rect but
   // it must be clipped to aTarget->mFilterPrimitiveSubregion.
   virtual nsresult Filter(nsSVGFilterInstance* aInstance,
                           const nsTArray<const Image*>& aSources,
                           const Image* aTarget,
                           const nsIntRect& aDataRect) = 0;
 
+  // returns true if changes to the attribute should cause us to
+  // repaint the filter
+  virtual bool AttributeAffectsRendering(
+          PRInt32 aNameSpaceID, nsIAtom* aAttribute) const;
+
   static nsIntRect GetMaxRect() {
     // Try to avoid overflow errors dealing with this rect. It will
     // be intersected with some other reasonable-sized rect eventually.
     return nsIntRect(PR_INT32_MIN/2, PR_INT32_MIN/2, PR_INT32_MAX, PR_INT32_MAX);
   }
 
   operator nsISupports*() { return static_cast<nsIContent*>(this); }
   
@@ -215,26 +228,118 @@ protected:
 
     nsStyleContext* style = frame->GetStyleContext();
     return style->GetStyleSVG()->mColorInterpolationFilters ==
              NS_STYLE_COLOR_INTERPOLATION_SRGB;
   }
 
   // nsSVGElement specializations:
   virtual LengthAttributesInfo GetLengthInfo();
-  virtual void DidAnimateLength(PRUint8 aAttrEnum);
-  virtual void DidAnimateNumber(PRUint8 aAttrEnum);
-  virtual void DidAnimateNumberPair(PRUint8 aAttrEnum);
-  virtual void DidAnimateNumberList(PRUint8 aAttrEnum);
-  virtual void DidAnimateInteger(PRUint8 aAttrEnum);
-  virtual void DidAnimateIntegerPair(PRUint8 aAttrEnum);
-  virtual void DidAnimateEnum(PRUint8 aAttrEnum);
-  virtual void DidAnimateBoolean(PRUint8 aAttrEnum);
-  virtual void DidAnimatePreserveAspectRatio();
-  virtual void DidAnimateString(PRUint8 aAttrEnum);
 
   // nsIDOMSVGFitlerPrimitiveStandardAttributes values
   enum { X, Y, WIDTH, HEIGHT };
   nsSVGLength2 mLengthAttributes[4];
   static LengthInfo sLengthInfo[4];
 };
 
+typedef nsSVGFE nsSVGFEImageElementBase;
+
+class nsSVGFEImageElement : public nsSVGFEImageElementBase,
+                            public nsIDOMSVGFEImageElement,
+                            public nsIDOMSVGURIReference,
+                            public nsImageLoadingContent
+{
+  friend class SVGFEImageFrame;
+
+protected:
+  friend nsresult NS_NewSVGFEImageElement(nsIContent **aResult,
+                                          already_AddRefed<nsINodeInfo> aNodeInfo);
+  nsSVGFEImageElement(already_AddRefed<nsINodeInfo> aNodeInfo);
+  virtual ~nsSVGFEImageElement();
+
+public:
+  virtual bool SubregionIsUnionOfRegions() { return false; }
+
+  // interfaces:
+  NS_DECL_ISUPPORTS_INHERITED
+
+  // FE Base
+  NS_FORWARD_NSIDOMSVGFILTERPRIMITIVESTANDARDATTRIBUTES(nsSVGFEImageElementBase::)
+
+  virtual nsresult Filter(nsSVGFilterInstance* aInstance,
+                          const nsTArray<const Image*>& aSources,
+                          const Image* aTarget,
+                          const nsIntRect& aDataRect);
+  virtual bool AttributeAffectsRendering(
+          PRInt32 aNameSpaceID, nsIAtom* aAttribute) const;
+  virtual nsSVGString& GetResultImageName() { return mStringAttributes[RESULT]; }
+  virtual nsIntRect ComputeTargetBBox(const nsTArray<nsIntRect>& aSourceBBoxes,
+          const nsSVGFilterInstance& aInstance);
+
+  NS_DECL_NSIDOMSVGFEIMAGEELEMENT
+  NS_DECL_NSIDOMSVGURIREFERENCE
+
+  NS_FORWARD_NSIDOMSVGELEMENT(nsSVGFEImageElementBase::)
+
+  NS_FORWARD_NSIDOMNODE(nsSVGFEImageElementBase::)
+  NS_FORWARD_NSIDOMELEMENT(nsSVGFEImageElementBase::)
+
+  // nsIContent
+  NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const;
+
+  virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
+
+  virtual nsresult AfterSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
+                                const nsAString* aValue, bool aNotify);
+  virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
+                              nsIContent* aBindingParent,
+                              bool aCompileEventHandlers);
+  virtual nsEventStates IntrinsicState() const;
+
+  // imgIDecoderObserver
+  NS_IMETHOD OnStopDecode(imgIRequest *aRequest, nsresult status,
+                          const PRUnichar *statusArg);
+  // imgIContainerObserver
+  NS_IMETHOD FrameChanged(imgIContainer *aContainer,
+                          const nsIntRect *aDirtyRect);
+  // imgIContainerObserver
+  NS_IMETHOD OnStartContainer(imgIRequest *aRequest,
+                              imgIContainer *aContainer);
+
+  void MaybeLoadSVGImage();
+
+  virtual nsXPCClassInfo* GetClassInfo();
+private:
+  // Invalidate users of the filter containing this element.
+  void Invalidate();
+
+  nsresult LoadSVGImage(bool aForce, bool aNotify);
+
+protected:
+  virtual bool OperatesOnSRGB(nsSVGFilterInstance*,
+                                PRInt32, Image*) { return true; }
+
+  virtual SVGAnimatedPreserveAspectRatio *GetPreserveAspectRatio();
+  virtual StringAttributesInfo GetStringInfo();
+
+  enum { RESULT, HREF };
+  nsSVGString mStringAttributes[2];
+  static StringInfo sStringInfo[2];
+
+  SVGAnimatedPreserveAspectRatio mPreserveAspectRatio;
+};
+
+typedef nsSVGElement SVGFEUnstyledElementBase;
+
+class SVGFEUnstyledElement : public SVGFEUnstyledElementBase
+{
+protected:
+  SVGFEUnstyledElement(already_AddRefed<nsINodeInfo> aNodeInfo)
+    : SVGFEUnstyledElementBase(aNodeInfo) {}
+
+public:
+  // returns true if changes to the attribute should cause us to
+  // repaint the filter
+  virtual bool AttributeAffectsRendering(
+          PRInt32 aNameSpaceID, nsIAtom* aAttribute) const = 0;
+};
+
 #endif
--- a/content/svg/content/src/nsSVGImageElement.cpp
+++ b/content/svg/content/src/nsSVGImageElement.cpp
@@ -168,17 +168,20 @@ nsSVGImageElement::LoadSVGImage(bool aFo
 // nsIContent methods:
 
 nsresult
 nsSVGImageElement::AfterSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
                                 const nsAString* aValue, bool aNotify)
 {
   if (aNamespaceID == kNameSpaceID_XLink && aName == nsGkAtoms::href) {
 
-    // If there's a frame it will deal
+    // If there isn't a frame we still need to load the image in case
+    // the frame is created later e.g. by attaching to a document.
+    // If there is a frame then it should deal with loading as the image
+    // url may be animated
     if (!GetPrimaryFrame()) {
 
       // Prevent setting image.src by exiting early
       if (nsContentUtils::IsImageSrcSetDisabled()) {
         return NS_OK;
       }
 
       if (aValue) {
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -137,16 +137,17 @@
 
 #include "nsIScrollableFrame.h"
 
 #include "nsIXBLService.h"
 
 #undef NOISY_FIRST_LETTER
 
 #include "nsMathMLParts.h"
+#include "nsIDOMSVGFilters.h"
 #include "nsSVGFeatures.h"
 #include "nsSVGEffects.h"
 #include "nsSVGUtils.h"
 #include "nsSVGOuterSVGFrame.h"
 
 #include "nsRefreshDriver.h"
 #include "nsRuleProcessorData.h"
 
@@ -208,17 +209,23 @@ nsIFrame*
 NS_NewSVGTextPathFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 nsIFrame*
 NS_NewSVGFilterFrame(nsIPresShell *aPresShell, nsStyleContext* aContext);
 nsIFrame*
 NS_NewSVGPatternFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 nsIFrame*
 NS_NewSVGMaskFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 nsIFrame*
-NS_NewSVGLeafFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+NS_NewSVGFEContainerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+nsIFrame*
+NS_NewSVGFELeafFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+nsIFrame*
+NS_NewSVGFEImageFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+nsIFrame*
+NS_NewSVGFEUnstyledLeafFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 
 #include "nsIDocument.h"
 #include "nsIScrollable.h"
 #include "nsINodeInfo.h"
 #include "prenv.h"
 #include "nsWidgetsCID.h"
 #include "nsNodeInfoManager.h"
 #include "nsContentCreatorFunctions.h"
@@ -4712,46 +4719,39 @@ nsCSSFrameConstructor::FindSVGData(Eleme
     aParentFrame ? aParentFrame->GetContent() : nsnull;
   // XXXbz should this really be based on the XBL-resolved tag of the parent
   // frame's content?  Should it not be based on the type of the parent frame
   // (e.g. whether it's an SVG frame)?
   if (parentContent) {
     PRInt32 parentNSID;
     nsIAtom* parentTag =
       parentContent->OwnerDoc()->BindingManager()->
-        ResolveTag(aParentFrame->GetContent(), &parentNSID);
+        ResolveTag(parentContent, &parentNSID);
 
     // It's not clear whether the SVG spec intends to allow any SVG
     // content within svg:foreignObject at all (SVG 1.1, section
     // 23.2), but if it does, it better be svg:svg.  So given that
     // we're allowing it, treat it as a non-SVG parent.
     parentIsSVG = parentNSID == kNameSpaceID_SVG &&
                   parentTag != nsGkAtoms::foreignObject;
   }
 
   if ((aTag != nsGkAtoms::svg && !parentIsSVG) ||
-      (aTag == nsGkAtoms::desc || aTag == nsGkAtoms::title ||
-       aTag == nsGkAtoms::feFuncR || aTag == nsGkAtoms::feFuncG ||
-       aTag == nsGkAtoms::feFuncB || aTag == nsGkAtoms::feFuncA ||
-       aTag == nsGkAtoms::feDistantLight || aTag == nsGkAtoms::fePointLight ||
-       aTag == nsGkAtoms::feSpotLight)) {
+      (aTag == nsGkAtoms::desc || aTag == nsGkAtoms::title)) {
     // Sections 5.1 and G.4 of SVG 1.1 say that SVG elements other than
     // svg:svg not contained within svg:svg are incorrect, although they
     // don't seem to specify error handling.  Ignore them, since many of
     // our frame classes can't deal.  It *may* be that the document
     // should at that point be considered in error according to F.2, but
     // it's hard to tell.
     //
     // Style mutation can't change this situation, so don't bother
     // adding to the undisplayed content map.
     //
     // We don't currently handle any UI for desc/title
-    //
-    // The filter types are children of filter elements that use their
-    // parent frames when necessary
     return &sSuppressData;
   }
 
   // We don't need frames for animation elements
   if (aElement->IsNodeOfType(nsINode::eANIMATION)) {
     return &sSuppressData;
   }
 
@@ -4770,16 +4770,34 @@ nsCSSFrameConstructor::FindSVGData(Eleme
   
   if (!nsSVGFeatures::PassesConditionalProcessingTests(aElement)) {
     // Elements with failing conditional processing attributes never get
     // rendered.  Note that this is not where we select which frame in a
     // <switch> to render!  That happens in nsSVGSwitchFrame::PaintSVG.
     return &sContainerData;
   }
 
+  // Special case for filter primitive elements.
+  // These elements must have a filter element as a parent
+  nsCOMPtr<nsIDOMSVGFilterPrimitiveStandardAttributes> filterPrimitive =
+    do_QueryInterface(aElement);
+  if (filterPrimitive && aParentFrame->GetType() != nsGkAtoms::svgFilterFrame) {
+    return &sSuppressData;
+  }
+
+  // Some elements must be children of filter primitive elements.
+  if ((aTag == nsGkAtoms::feDistantLight || aTag == nsGkAtoms::fePointLight ||
+       aTag == nsGkAtoms::feSpotLight ||
+       aTag == nsGkAtoms::feFuncR || aTag == nsGkAtoms::feFuncG ||
+       aTag == nsGkAtoms::feFuncB || aTag == nsGkAtoms::feFuncA ||
+       aTag == nsGkAtoms::feMergeNode) &&
+       aParentFrame->GetType() != nsGkAtoms::svgFEContainerFrame) {
+    return &sSuppressData;
+  }
+
   // Special cases for text/tspan/textPath, because the kind of frame
   // they get depends on the parent frame.  We ignore 'a' elements when
   // determining the parent, however.
   nsIFrame *ancestorFrame =
     nsSVGUtils::GetFirstNonAAncestorFrame(aParentFrame);
   if (ancestorFrame) {
     if (aTag == nsGkAtoms::tspan || aTag == nsGkAtoms::altGlyph) {
       // tspan and altGlyph must be children of another text content element.
@@ -4829,32 +4847,40 @@ nsCSSFrameConstructor::FindSVGData(Eleme
     SIMPLE_SVG_CREATE(use, NS_NewSVGUseFrame),
     SIMPLE_SVG_CREATE(marker, NS_NewSVGMarkerFrame),
     SIMPLE_SVG_CREATE(image, NS_NewSVGImageFrame),
     SIMPLE_SVG_CREATE(clipPath, NS_NewSVGClipPathFrame),
     SIMPLE_SVG_CREATE(textPath, NS_NewSVGTextPathFrame),
     SIMPLE_SVG_CREATE(filter, NS_NewSVGFilterFrame),
     SIMPLE_SVG_CREATE(pattern, NS_NewSVGPatternFrame),
     SIMPLE_SVG_CREATE(mask, NS_NewSVGMaskFrame),
-    SIMPLE_SVG_CREATE(feBlend, NS_NewSVGLeafFrame),
-    SIMPLE_SVG_CREATE(feColorMatrix, NS_NewSVGLeafFrame),
-    SIMPLE_SVG_CREATE(feComposite, NS_NewSVGLeafFrame),
-    SIMPLE_SVG_CREATE(feComponentTransfer, NS_NewSVGLeafFrame),
-    SIMPLE_SVG_CREATE(feConvolveMatrix, NS_NewSVGLeafFrame),
-    SIMPLE_SVG_CREATE(feDiffuseLighting, NS_NewSVGLeafFrame),
-    SIMPLE_SVG_CREATE(feDisplacementMap, NS_NewSVGLeafFrame),
-    SIMPLE_SVG_CREATE(feFlood, NS_NewSVGLeafFrame),
-    SIMPLE_SVG_CREATE(feGaussianBlur, NS_NewSVGLeafFrame),
-    SIMPLE_SVG_CREATE(feImage, NS_NewSVGLeafFrame),
-    SIMPLE_SVG_CREATE(feMergeNode, NS_NewSVGLeafFrame),
-    SIMPLE_SVG_CREATE(feMorphology, NS_NewSVGLeafFrame), 
-    SIMPLE_SVG_CREATE(feOffset, NS_NewSVGLeafFrame), 
-    SIMPLE_SVG_CREATE(feSpecularLighting, NS_NewSVGLeafFrame),
-    SIMPLE_SVG_CREATE(feTile, NS_NewSVGLeafFrame), 
-    SIMPLE_SVG_CREATE(feTurbulence, NS_NewSVGLeafFrame) 
+    SIMPLE_SVG_CREATE(feDistantLight, NS_NewSVGFEUnstyledLeafFrame),
+    SIMPLE_SVG_CREATE(fePointLight, NS_NewSVGFEUnstyledLeafFrame),
+    SIMPLE_SVG_CREATE(feSpotLight, NS_NewSVGFEUnstyledLeafFrame),
+    SIMPLE_SVG_CREATE(feBlend, NS_NewSVGFELeafFrame),
+    SIMPLE_SVG_CREATE(feColorMatrix, NS_NewSVGFELeafFrame),
+    SIMPLE_SVG_CREATE(feFuncR, NS_NewSVGFEUnstyledLeafFrame),
+    SIMPLE_SVG_CREATE(feFuncG, NS_NewSVGFEUnstyledLeafFrame),
+    SIMPLE_SVG_CREATE(feFuncB, NS_NewSVGFEUnstyledLeafFrame),
+    SIMPLE_SVG_CREATE(feFuncA, NS_NewSVGFEUnstyledLeafFrame),
+    SIMPLE_SVG_CREATE(feComposite, NS_NewSVGFELeafFrame),
+    SIMPLE_SVG_CREATE(feComponentTransfer, NS_NewSVGFEContainerFrame),
+    SIMPLE_SVG_CREATE(feConvolveMatrix, NS_NewSVGFELeafFrame),
+    SIMPLE_SVG_CREATE(feDiffuseLighting, NS_NewSVGFEContainerFrame),
+    SIMPLE_SVG_CREATE(feDisplacementMap, NS_NewSVGFELeafFrame),
+    SIMPLE_SVG_CREATE(feFlood, NS_NewSVGFELeafFrame),
+    SIMPLE_SVG_CREATE(feGaussianBlur, NS_NewSVGFELeafFrame),
+    SIMPLE_SVG_CREATE(feImage, NS_NewSVGFEImageFrame),
+    SIMPLE_SVG_CREATE(feMerge, NS_NewSVGFEContainerFrame),
+    SIMPLE_SVG_CREATE(feMergeNode, NS_NewSVGFEUnstyledLeafFrame),
+    SIMPLE_SVG_CREATE(feMorphology, NS_NewSVGFELeafFrame), 
+    SIMPLE_SVG_CREATE(feOffset, NS_NewSVGFELeafFrame), 
+    SIMPLE_SVG_CREATE(feSpecularLighting, NS_NewSVGFEContainerFrame),
+    SIMPLE_SVG_CREATE(feTile, NS_NewSVGFELeafFrame), 
+    SIMPLE_SVG_CREATE(feTurbulence, NS_NewSVGFELeafFrame) 
   };
 
   const FrameConstructionData* data =
     FindDataByTag(aTag, aElement, aStyleContext, sSVGData,
                   ArrayLength(sSVGData));
 
   if (!data) {
     data = &sContainerData;
--- a/layout/generic/nsQueryFrame.h
+++ b/layout/generic/nsQueryFrame.h
@@ -194,26 +194,29 @@ public:
     nsSplittableFrame_id,
     nsSplitterFrame_id,
     nsStackFrame_id,
     nsSubDocumentFrame_id,
     nsSVGAFrame_id,
     nsSVGClipPathFrame_id,
     nsSVGContainerFrame_id,
     nsSVGDisplayContainerFrame_id,
+    SVGFEContainerFrame_id,
+    SVGFEImageFrame_id,
+    SVGFELeafFrame_id,
+    SVGFEUnstyledLeafFrame_id,
     nsSVGFilterFrame_id,
     nsSVGForeignObjectFrame_id,
     nsSVGGenericContainerFrame_id,
     nsSVGGeometryFrame_id,
     nsSVGGFrame_id,
     nsSVGGlyphFrame_id,
     nsSVGGradientFrame_id,
     nsSVGImageFrame_id,
     nsSVGInnerSVGFrame_id,
-    nsSVGLeafFrame_id,
     nsSVGLinearGradientFrame_id,
     nsSVGMarkerFrame_id,
     nsSVGMaskFrame_id,
     nsSVGOuterSVGFrame_id,
     nsSVGPaintServerFrame_id,
     nsSVGPathGeometryFrame_id,
     nsSVGPatternFrame_id,
     nsSVGRadialGradientFrame_id,
--- a/layout/svg/base/src/Makefile.in
+++ b/layout/svg/base/src/Makefile.in
@@ -58,17 +58,20 @@ CPPSRCS		= \
 		nsSVGGFrame.cpp \
 		nsSVGGenericContainerFrame.cpp \
 		nsSVGGeometryFrame.cpp \
 		nsSVGGlyphFrame.cpp \
 		nsSVGGradientFrame.cpp \
 		nsSVGImageFrame.cpp \
 		nsSVGInnerSVGFrame.cpp \
 		nsSVGIntegrationUtils.cpp \
-		nsSVGLeafFrame.cpp \
+		SVGFEContainerFrame.cpp \
+		SVGFEImageFrame.cpp \
+		SVGFELeafFrame.cpp \
+		SVGFEUnstyledLeafFrame.cpp \
 		nsSVGMarkerFrame.cpp \
 		nsSVGMaskFrame.cpp \
 		nsSVGOuterSVGFrame.cpp \
 		nsSVGPaintServerFrame.cpp \
 		nsSVGPathGeometryFrame.cpp \
 		nsSVGPatternFrame.cpp \
 		nsSVGStopFrame.cpp \
 		nsSVGSwitchFrame.cpp \
new file mode 100644
--- /dev/null
+++ b/layout/svg/base/src/SVGFEContainerFrame.cpp
@@ -0,0 +1,137 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Mozilla SVG project.
+ *
+ * The Initial Developer of the Original Code is Robert Longson.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsContainerFrame.h"
+#include "nsSVGEffects.h"
+#include "nsSVGFilters.h"
+
+typedef nsContainerFrame SVGFEContainerFrameBase;
+
+/*
+ * This frame is used by filter primitive elements that
+ * have special child elements that provide parameters.
+ */
+class SVGFEContainerFrame : public SVGFEContainerFrameBase
+{
+  friend nsIFrame*
+  NS_NewSVGFEContainerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+protected:
+  SVGFEContainerFrame(nsStyleContext* aContext) : SVGFEContainerFrameBase(aContext) {}
+
+public:
+  NS_DECL_FRAMEARENA_HELPERS
+
+  virtual bool IsFrameOfType(PRUint32 aFlags) const
+  {
+    return SVGFEContainerFrameBase::IsFrameOfType(
+            aFlags & ~(nsIFrame::eSVG | nsIFrame::eSVGContainer));
+  }
+
+#ifdef DEBUG
+  NS_IMETHOD GetFrameName(nsAString& aResult) const
+  {
+    return MakeFrameName(NS_LITERAL_STRING("SVGFEContainer"), aResult);
+  }
+#endif
+
+  virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext);
+
+#ifdef DEBUG
+  NS_IMETHOD Init(nsIContent* aContent,
+                  nsIFrame*   aParent,
+                  nsIFrame*   aPrevInFlow);
+#endif
+  /**
+   * Get the "type" of the frame
+   *
+   * @see nsGkAtoms::svgFEContainerFrame
+   */
+  virtual nsIAtom* GetType() const;
+
+  NS_IMETHOD AttributeChanged(PRInt32  aNameSpaceID,
+                              nsIAtom* aAttribute,
+                              PRInt32  aModType);
+};
+
+nsIFrame*
+NS_NewSVGFEContainerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
+{
+  return new (aPresShell) SVGFEContainerFrame(aContext);
+}
+
+NS_IMPL_FRAMEARENA_HELPERS(SVGFEContainerFrame)
+
+/* virtual */ void
+SVGFEContainerFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
+{
+  SVGFEContainerFrameBase::DidSetStyleContext(aOldStyleContext);
+  nsSVGEffects::InvalidateRenderingObservers(this);
+}
+
+#ifdef DEBUG
+NS_IMETHODIMP
+SVGFEContainerFrame::Init(nsIContent* aContent,
+                          nsIFrame* aParent,
+                          nsIFrame* aPrevInFlow)
+{
+  nsCOMPtr<nsIDOMSVGFilterPrimitiveStandardAttributes> elem = do_QueryInterface(aContent);
+  NS_ASSERTION(elem,
+               "Trying to construct an SVGFEContainerFrame for a "
+               "content element that doesn't support the right interfaces");
+
+  return SVGFEContainerFrameBase::Init(aContent, aParent, aPrevInFlow);
+}
+#endif /* DEBUG */
+
+nsIAtom *
+SVGFEContainerFrame::GetType() const
+{
+  return nsGkAtoms::svgFEContainerFrame;
+}
+
+NS_IMETHODIMP
+SVGFEContainerFrame::AttributeChanged(PRInt32  aNameSpaceID,
+                                      nsIAtom* aAttribute,
+                                      PRInt32  aModType)
+{
+  nsSVGFE *element = static_cast<nsSVGFE*>(mContent);
+  if (element->AttributeAffectsRendering(aNameSpaceID, aAttribute)) {
+    nsSVGEffects::InvalidateRenderingObservers(this);
+  }
+
+  return SVGFEContainerFrameBase::AttributeChanged(aNameSpaceID,
+                                                     aAttribute, aModType);
+}
new file mode 100644
--- /dev/null
+++ b/layout/svg/base/src/SVGFEImageFrame.cpp
@@ -0,0 +1,171 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Mozilla SVG project.
+ *
+ * The Initial Developer of the Original Code is Robert Longson.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsFrame.h"
+#include "nsSVGEffects.h"
+#include "nsSVGFilters.h"
+#include "nsContentUtils.h"
+#include "nsImageLoadingContent.h"
+
+using namespace mozilla;
+
+typedef nsFrame SVGFEImageFrameBase;
+
+class SVGFEImageFrame : public SVGFEImageFrameBase
+{
+  friend nsIFrame*
+  NS_NewSVGFEImageFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+protected:
+  SVGFEImageFrame(nsStyleContext* aContext) : SVGFEImageFrameBase(aContext) {}
+
+public:
+  NS_DECL_FRAMEARENA_HELPERS
+
+  NS_IMETHOD Init(nsIContent* aContent,
+                  nsIFrame*   aParent,
+                  nsIFrame*   aPrevInFlow);
+  virtual void DestroyFrom(nsIFrame* aDestructRoot);
+
+  virtual bool IsFrameOfType(PRUint32 aFlags) const
+  {
+    return SVGFEImageFrameBase::IsFrameOfType(aFlags & ~(nsIFrame::eSVG));
+  }
+
+#ifdef DEBUG
+  NS_IMETHOD GetFrameName(nsAString& aResult) const
+  {
+    return MakeFrameName(NS_LITERAL_STRING("SVGFEImage"), aResult);
+  }
+#endif
+
+  virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext);
+
+  /**
+   * Get the "type" of the frame
+   *
+   * @see nsGkAtoms::svgFEImageFrame
+   */
+  virtual nsIAtom* GetType() const;
+
+  NS_IMETHOD AttributeChanged(PRInt32  aNameSpaceID,
+                              nsIAtom* aAttribute,
+                              PRInt32  aModType);
+};
+
+nsIFrame*
+NS_NewSVGFEImageFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
+{
+  return new (aPresShell) SVGFEImageFrame(aContext);
+}
+
+NS_IMPL_FRAMEARENA_HELPERS(SVGFEImageFrame)
+
+/* virtual */ void
+SVGFEImageFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
+{
+  SVGFEImageFrameBase::DidSetStyleContext(aOldStyleContext);
+  nsSVGEffects::InvalidateRenderingObservers(this);
+}
+
+/* virtual */ void
+SVGFEImageFrame::DestroyFrom(nsIFrame* aDestructRoot)
+{
+  nsCOMPtr<nsIImageLoadingContent> imageLoader =
+    do_QueryInterface(SVGFEImageFrameBase::mContent);
+
+  if (imageLoader) {
+    imageLoader->FrameDestroyed(this);
+  }
+
+  SVGFEImageFrameBase::DestroyFrom(aDestructRoot);
+}
+
+NS_IMETHODIMP
+SVGFEImageFrame::Init(nsIContent* aContent,
+                        nsIFrame* aParent,
+                        nsIFrame* aPrevInFlow)
+{
+#ifdef DEBUG
+  nsCOMPtr<nsIDOMSVGFEImageElement> elem = do_QueryInterface(aContent);
+  NS_ASSERTION(elem,
+               "Trying to construct an SVGFEImageFrame for a "
+               "content element that doesn't support the right interfaces");
+#endif /* DEBUG */
+
+  SVGFEImageFrameBase::Init(aContent, aParent, aPrevInFlow);
+  nsCOMPtr<nsIImageLoadingContent> imageLoader =
+    do_QueryInterface(SVGFEImageFrameBase::mContent);
+
+  if (imageLoader) {
+    imageLoader->FrameCreated(this);
+  }
+
+  return NS_OK;
+}
+
+nsIAtom *
+SVGFEImageFrame::GetType() const
+{
+  return nsGkAtoms::svgFEImageFrame;
+}
+
+NS_IMETHODIMP
+SVGFEImageFrame::AttributeChanged(PRInt32  aNameSpaceID,
+                                  nsIAtom* aAttribute,
+                                  PRInt32  aModType)
+{
+  nsSVGFEImageElement *element = static_cast<nsSVGFEImageElement*>(mContent);
+  if (element->AttributeAffectsRendering(aNameSpaceID, aAttribute)) {
+    nsSVGEffects::InvalidateRenderingObservers(this);
+  }
+  if (aNameSpaceID == kNameSpaceID_XLink &&
+      aAttribute == nsGkAtoms::href) {
+
+    // Prevent setting image.src by exiting early
+    if (nsContentUtils::IsImageSrcSetDisabled()) {
+      return NS_OK;
+    }
+
+    if (element->mStringAttributes[nsSVGFEImageElement::HREF].IsExplicitlySet()) {
+      element->LoadSVGImage(true, true);
+    } else {
+      element->CancelImageRequests(true);
+    }
+  }
+
+  return SVGFEImageFrameBase::AttributeChanged(aNameSpaceID,
+                                                 aAttribute, aModType);
+}
copy from layout/svg/base/src/nsSVGLeafFrame.cpp
copy to layout/svg/base/src/SVGFELeafFrame.cpp
--- a/layout/svg/base/src/nsSVGLeafFrame.cpp
+++ b/layout/svg/base/src/SVGFELeafFrame.cpp
@@ -30,83 +30,108 @@
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsFrame.h"
+#include "nsSVGFilters.h"
 #include "nsSVGEffects.h"
-#include "nsImageLoadingContent.h"
+
+typedef nsFrame SVGFELeafFrameBase;
 
-class nsSVGLeafFrame : public nsFrame
+/*
+ * This frame is used by filter primitive elements that don't
+ * have special child elements that provide parameters.
+ */
+class SVGFELeafFrame : public SVGFELeafFrameBase
 {
   friend nsIFrame*
-  NS_NewSVGLeafFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+  NS_NewSVGFELeafFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 protected:
-  nsSVGLeafFrame(nsStyleContext* aContext) : nsFrame(aContext) {}
+  SVGFELeafFrame(nsStyleContext* aContext) : SVGFELeafFrameBase(aContext) {}
 
 public:
   NS_DECL_FRAMEARENA_HELPERS
 
-  NS_IMETHOD Init(nsIContent*      aContent,
-                  nsIFrame*        aParent,
-                  nsIFrame*        asPrevInFlow);
-
-  virtual void DestroyFrom(nsIFrame* aDestructRoot);
+#ifdef DEBUG
+  NS_IMETHOD Init(nsIContent* aContent,
+                  nsIFrame*   aParent,
+                  nsIFrame*   aPrevInFlow);
+#endif
 
   virtual bool IsFrameOfType(PRUint32 aFlags) const
   {
-    return nsFrame::IsFrameOfType(aFlags & ~(nsIFrame::eSVG));
+    return SVGFELeafFrameBase::IsFrameOfType(aFlags & ~(nsIFrame::eSVG));
   }
 
 #ifdef DEBUG
   NS_IMETHOD GetFrameName(nsAString& aResult) const
   {
-    return MakeFrameName(NS_LITERAL_STRING("SVGLeaf"), aResult);
+    return MakeFrameName(NS_LITERAL_STRING("SVGFELeaf"), aResult);
   }
 #endif
 
   virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext);
+
+  /**
+   * Get the "type" of the frame
+   *
+   * @see nsGkAtoms::svgFELeafFrame
+   */
+  virtual nsIAtom* GetType() const;
+
+  NS_IMETHOD AttributeChanged(PRInt32  aNameSpaceID,
+                              nsIAtom* aAttribute,
+                              PRInt32  aModType);
 };
 
 nsIFrame*
-NS_NewSVGLeafFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
+NS_NewSVGFELeafFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
 {
-  return new (aPresShell) nsSVGLeafFrame(aContext);
+  return new (aPresShell) SVGFELeafFrame(aContext);
+}
+
+NS_IMPL_FRAMEARENA_HELPERS(SVGFELeafFrame)
+
+/* virtual */ void
+SVGFELeafFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
+{
+  SVGFELeafFrameBase::DidSetStyleContext(aOldStyleContext);
+  nsSVGEffects::InvalidateRenderingObservers(this);
 }
 
-NS_IMPL_FRAMEARENA_HELPERS(nsSVGLeafFrame)
+#ifdef DEBUG
+NS_IMETHODIMP
+SVGFELeafFrame::Init(nsIContent* aContent,
+                     nsIFrame* aParent,
+                     nsIFrame* aPrevInFlow)
+{
+  nsCOMPtr<nsIDOMSVGFilterPrimitiveStandardAttributes> elem = do_QueryInterface(aContent);
+  NS_ASSERTION(elem,
+               "Trying to construct an SVGFELeafFrame for a "
+               "content element that doesn't support the right interfaces");
+
+  return SVGFELeafFrameBase::Init(aContent, aParent, aPrevInFlow);
+}
+#endif /* DEBUG */
+
+nsIAtom *
+SVGFELeafFrame::GetType() const
+{
+  return nsGkAtoms::svgFELeafFrame;
+}
 
 NS_IMETHODIMP
-nsSVGLeafFrame::Init(nsIContent* aContent, nsIFrame* aParent, nsIFrame* asPrevInFlow)
+SVGFELeafFrame::AttributeChanged(PRInt32  aNameSpaceID,
+                                 nsIAtom* aAttribute,
+                                 PRInt32  aModType)
 {
-  nsFrame::Init(aContent, aParent, asPrevInFlow);
-  nsCOMPtr<nsIImageLoadingContent> imageLoader =
-    do_QueryInterface(nsFrame::mContent);
-
-  if (imageLoader) {
-    imageLoader->FrameCreated(this);
+  nsSVGFE *element = static_cast<nsSVGFE*>(mContent);
+  if (element->AttributeAffectsRendering(aNameSpaceID, aAttribute)) {
+    nsSVGEffects::InvalidateRenderingObservers(this);
   }
 
-  return NS_OK;
+  return SVGFELeafFrameBase::AttributeChanged(aNameSpaceID,
+                                                aAttribute, aModType);
 }
-
-/* virtual */ void
-nsSVGLeafFrame::DestroyFrom(nsIFrame* aDestructRoot)
-{
-  nsCOMPtr<nsIImageLoadingContent> imageLoader =
-    do_QueryInterface(nsFrame::mContent);
-
-  if (imageLoader) {
-    imageLoader->FrameDestroyed(this);
-  }
-
-  nsFrame::DestroyFrom(aDestructRoot);
-}
-
-/* virtual */ void
-nsSVGLeafFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
-{
-  nsFrame::DidSetStyleContext(aOldStyleContext);
-  nsSVGEffects::InvalidateRenderingObservers(this);
-}
new file mode 100644
--- /dev/null
+++ b/layout/svg/base/src/SVGFEUnstyledLeafFrame.cpp
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Mozilla SVG project.
+ *
+ * The Initial Developer of the Original Code is Robert Longson.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsFrame.h"
+#include "nsSVGFilters.h"
+#include "nsSVGEffects.h"
+
+typedef nsFrame SVGFEUnstyledLeafFrameBase;
+
+class SVGFEUnstyledLeafFrame : public SVGFEUnstyledLeafFrameBase
+{
+  friend nsIFrame*
+  NS_NewSVGFEUnstyledLeafFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+protected:
+  SVGFEUnstyledLeafFrame(nsStyleContext* aContext) : SVGFEUnstyledLeafFrameBase(aContext) {}
+
+public:
+  NS_DECL_FRAMEARENA_HELPERS
+
+  virtual bool IsFrameOfType(PRUint32 aFlags) const
+  {
+    return SVGFEUnstyledLeafFrameBase::IsFrameOfType(aFlags & ~(nsIFrame::eSVG));
+  }
+
+#ifdef DEBUG
+  NS_IMETHOD GetFrameName(nsAString& aResult) const
+  {
+    return MakeFrameName(NS_LITERAL_STRING("SVGFEUnstyledLeaf"), aResult);
+  }
+#endif
+
+  /**
+   * Get the "type" of the frame
+   *
+   * @see nsGkAtoms::svgFEUnstyledLeafFrame
+   */
+  virtual nsIAtom* GetType() const;
+
+  NS_IMETHOD AttributeChanged(PRInt32  aNameSpaceID,
+                              nsIAtom* aAttribute,
+                              PRInt32  aModType);
+};
+
+nsIFrame*
+NS_NewSVGFEUnstyledLeafFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
+{
+  return new (aPresShell) SVGFEUnstyledLeafFrame(aContext);
+}
+
+NS_IMPL_FRAMEARENA_HELPERS(SVGFEUnstyledLeafFrame)
+
+nsIAtom *
+SVGFEUnstyledLeafFrame::GetType() const
+{
+  return nsGkAtoms::svgFEUnstyledLeafFrame;
+}
+
+NS_IMETHODIMP
+SVGFEUnstyledLeafFrame::AttributeChanged(PRInt32  aNameSpaceID,
+                                         nsIAtom* aAttribute,
+                                         PRInt32  aModType)
+{
+  SVGFEUnstyledElement *element = static_cast<SVGFEUnstyledElement*>(mContent);
+  if (element->AttributeAffectsRendering(aNameSpaceID, aAttribute)) {
+    nsSVGEffects::InvalidateRenderingObservers(this);
+  }
+
+  return SVGFEUnstyledLeafFrameBase::AttributeChanged(aNameSpaceID,
+                                                        aAttribute, aModType);
+}