Bug 540479. Add support for SMIL animation of boolean attributes in SVG. r=dholbert
authorJonathan Watt <jwatt@jwatt.org>
Sun, 24 Jan 2010 16:42:08 +0000
changeset 37456 b3b2e90f743d
parent 37455 5fca1946778b
child 37457 2c1b2b5f4d50
push id11313
push userjwatt@jwatt.org
push date2010-01-24 16:44 +0000
treeherdermozilla-central@b3b2e90f743d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdholbert
bugs540479
milestone1.9.3a1pre
Bug 540479. Add support for SMIL animation of boolean attributes in SVG. r=dholbert
content/smil/Makefile.in
content/smil/SMILBoolType.cpp
content/smil/SMILBoolType.h
content/smil/nsSMILValue.h
content/svg/content/src/nsSVGBoolean.cpp
content/svg/content/src/nsSVGBoolean.h
content/svg/content/src/nsSVGElement.cpp
content/svg/content/src/nsSVGElement.h
content/svg/content/src/nsSVGFilters.cpp
content/svg/content/src/nsSVGFilters.h
layout/reftests/svg/smil/anim-feConvolveMatrix-preserveAlpha-01.svg
layout/reftests/svg/smil/reftest.list
--- a/content/smil/Makefile.in
+++ b/content/smil/Makefile.in
@@ -65,16 +65,17 @@ CPPSRCS		+= \
 	nsSMILParserUtils.cpp \
 	nsSMILRepeatCount.cpp \
 	nsSMILSetAnimationFunction.cpp \
 	nsSMILTimeContainer.cpp \
 	nsSMILTimedElement.cpp \
 	nsSMILTimeValue.cpp \
 	nsSMILTimeValueSpec.cpp \
 	nsSMILValue.cpp \
+	SMILBoolType.cpp \
 	SMILEnumType.cpp \
 		$(NULL)
 endif
 
 include $(topsrcdir)/config/config.mk
 
 # we don't want the shared lib, but we want to force the creation of a static lib.
 FORCE_STATIC_LIB = 1
new file mode 100644
--- /dev/null
+++ b/content/smil/SMILBoolType.cpp
@@ -0,0 +1,107 @@
+/* -*- 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 the Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of 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 "SMILBoolType.h"
+#include "nsSMILValue.h"
+#include "nsDebug.h"
+#include <math.h>
+
+namespace mozilla {
+
+/*static*/ SMILBoolType SMILBoolType::sSingleton;
+
+nsresult
+SMILBoolType::Init(nsSMILValue& aValue) const
+{
+  NS_PRECONDITION(aValue.mType == this || aValue.IsNull(),
+                  "Unexpected value type");
+  aValue.mU.mBool = PR_FALSE;
+  aValue.mType = this;
+  return NS_OK;
+}
+
+void
+SMILBoolType::Destroy(nsSMILValue& aValue) const
+{
+  NS_PRECONDITION(aValue.mType == this, "Unexpected SMIL value.");
+  aValue.mU.mBool = PR_FALSE;
+  aValue.mType = &nsSMILNullType::sSingleton;
+}
+
+nsresult
+SMILBoolType::Assign(nsSMILValue& aDest, const nsSMILValue& aSrc) const
+{
+  NS_PRECONDITION(aDest.mType == aSrc.mType, "Incompatible SMIL types.");
+  NS_PRECONDITION(aDest.mType == this, "Unexpected SMIL value.");
+  aDest.mU.mBool = aSrc.mU.mBool;
+  return NS_OK;
+}
+
+nsresult
+SMILBoolType::Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd,
+                  PRUint32 aCount) const
+{
+  NS_PRECONDITION(aValueToAdd.mType == aDest.mType,
+                  "Trying to add invalid types");
+  NS_PRECONDITION(aValueToAdd.mType == this, "Unexpected source type");
+  return NS_ERROR_FAILURE; // bool values can't be added to each other
+}
+
+nsresult
+SMILBoolType::ComputeDistance(const nsSMILValue& aFrom,
+                              const nsSMILValue& aTo,
+                              double& aDistance) const
+{
+  NS_PRECONDITION(aFrom.mType == aTo.mType,"Trying to compare different types");
+  NS_PRECONDITION(aFrom.mType == this, "Unexpected source type");
+  return NS_ERROR_FAILURE; // there is no concept of distance between bool values
+}
+
+nsresult
+SMILBoolType::Interpolate(const nsSMILValue& aStartVal,
+                          const nsSMILValue& aEndVal,
+                          double aUnitDistance,
+                          nsSMILValue& aResult) const
+{
+  NS_PRECONDITION(aStartVal.mType == aEndVal.mType,
+      "Trying to interpolate different types");
+  NS_PRECONDITION(aStartVal.mType == this,
+      "Unexpected types for interpolation.");
+  NS_PRECONDITION(aResult.mType   == this, "Unexpected result type.");
+  return NS_ERROR_FAILURE; // bool values do not interpolate
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/smil/SMILBoolType.h
@@ -0,0 +1,68 @@
+/* -*- 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 the Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of 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 ***** */
+
+#ifndef MOZILLA_SMILBOOLTYPE_H_
+#define MOZILLA_SMILBOOLTYPE_H_
+
+#include "nsISMILType.h"
+
+namespace mozilla {
+
+class SMILBoolType : public nsISMILType
+{
+public:
+  virtual nsresult Init(nsSMILValue& aValue) const;
+  virtual void     Destroy(nsSMILValue&) const;
+  virtual nsresult Assign(nsSMILValue& aDest, const nsSMILValue& aSrc) const;
+  virtual nsresult Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd,
+                       PRUint32 aCount) const;
+  virtual nsresult ComputeDistance(const nsSMILValue& aFrom,
+                                   const nsSMILValue& aTo,
+                                   double& aDistance) const;
+  virtual nsresult Interpolate(const nsSMILValue& aStartVal,
+                               const nsSMILValue& aEndVal,
+                               double aUnitDistance,
+                               nsSMILValue& aResult) const;
+
+  static SMILBoolType sSingleton;
+
+private:
+  SMILBoolType() {}
+};
+
+} // namespace mozilla
+
+#endif // MOZILLA_SMILBOOLTYPE_H_
--- a/content/smil/nsSMILValue.h
+++ b/content/smil/nsSMILValue.h
@@ -64,16 +64,17 @@ public:
   nsresult Add(const nsSMILValue& aValueToAdd, PRUint32 aCount = 1);
   nsresult SandwichAdd(const nsSMILValue& aValueToAdd);
   nsresult ComputeDistance(const nsSMILValue& aTo, double& aDistance) const;
   nsresult Interpolate(const nsSMILValue& aEndVal,
                        double aUnitDistance,
                        nsSMILValue& aResult) const;
 
   union {
+    PRBool mBool;
     PRUint64 mUint;
     PRInt64 mInt;
     double mDouble;
     void* mPtr;
   } mU;
   const nsISMILType* mType;
 };
 
--- a/content/svg/content/src/nsSVGBoolean.cpp
+++ b/content/svg/content/src/nsSVGBoolean.cpp
@@ -30,16 +30,22 @@
  * 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 "nsSVGBoolean.h"
+#ifdef MOZ_SMIL
+#include "nsSMILValue.h"
+#include "SMILBoolType.h"
+#endif // MOZ_SMIL
+
+using namespace mozilla;
 
 NS_SVG_VAL_IMPL_CYCLE_COLLECTION(nsSVGBoolean::DOMAnimatedBoolean, mSVGElement)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsSVGBoolean::DOMAnimatedBoolean)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsSVGBoolean::DOMAnimatedBoolean)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsSVGBoolean::DOMAnimatedBoolean)
   NS_INTERFACE_MAP_ENTRY(nsIDOMSVGAnimatedBoolean)
@@ -59,16 +65,21 @@ nsSVGBoolean::SetBaseValueString(const n
   if (aValueAsString.EqualsLiteral("true"))
     val = PR_TRUE;
   else if (aValueAsString.EqualsLiteral("false"))
     val = PR_FALSE;
   else
     return NS_ERROR_DOM_SYNTAX_ERR;
 
   mBaseVal = mAnimVal = val;
+#ifdef MOZ_SMIL
+  if (mIsAnimated) {
+    aSVGElement->AnimationNeedsResample();
+  }
+#endif
   return NS_OK;
 }
 
 void
 nsSVGBoolean::GetBaseValueString(nsAString & aValueAsString)
 {
   aValueAsString.Assign(mBaseVal
                         ? NS_LITERAL_STRING("true")
@@ -76,23 +87,92 @@ nsSVGBoolean::GetBaseValueString(nsAStri
 }
 
 void
 nsSVGBoolean::SetBaseValue(PRBool aValue,
                            nsSVGElement *aSVGElement)
 {
   NS_PRECONDITION(aValue == PR_TRUE || aValue == PR_FALSE, "Boolean out of range");
 
-  mAnimVal = mBaseVal = aValue;
-  aSVGElement->DidChangeBoolean(mAttrEnum, PR_TRUE);
+  if (aValue != mBaseVal) {
+    mAnimVal = mBaseVal = aValue;
+    aSVGElement->DidChangeBoolean(mAttrEnum, PR_TRUE);
+#ifdef MOZ_SMIL
+    if (mIsAnimated) {
+      aSVGElement->AnimationNeedsResample();
+    }
+#endif
+  }
+}
+
+void
+nsSVGBoolean::SetAnimValue(PRBool aValue, nsSVGElement *aSVGElement)
+{
+  mAnimVal = aValue;
+  mIsAnimated = PR_TRUE;
+  aSVGElement->DidAnimateBoolean(mAttrEnum);
 }
 
 nsresult
 nsSVGBoolean::ToDOMAnimatedBoolean(nsIDOMSVGAnimatedBoolean **aResult,
                                    nsSVGElement *aSVGElement)
 {
   *aResult = new DOMAnimatedBoolean(this, aSVGElement);
   if (!*aResult)
     return NS_ERROR_OUT_OF_MEMORY;
 
   NS_ADDREF(*aResult);
   return NS_OK;
 }
+
+#ifdef MOZ_SMIL
+nsISMILAttr*
+nsSVGBoolean::ToSMILAttr(nsSVGElement *aSVGElement)
+{
+  return new SMILBool(this, aSVGElement);
+}
+
+nsresult
+nsSVGBoolean::SMILBool::ValueFromString(const nsAString& aStr,
+                                        const nsISMILAnimationElement* /*aSrcElement*/,
+                                        nsSMILValue& aValue) const
+{
+  nsSMILValue val(&SMILBoolType::sSingleton);
+
+  if (aStr.EqualsLiteral("true"))
+    val.mU.mBool = PR_TRUE;
+  else if (aStr.EqualsLiteral("false"))
+    val.mU.mBool = PR_FALSE;
+  else
+    return NS_ERROR_FAILURE;
+
+  aValue = val;
+  return NS_OK;
+}
+
+nsSMILValue
+nsSVGBoolean::SMILBool::GetBaseValue() const
+{
+  nsSMILValue val(&SMILBoolType::sSingleton);
+  val.mU.mBool = mVal->mBaseVal;
+  return val;
+}
+
+void
+nsSVGBoolean::SMILBool::ClearAnimValue()
+{
+  if (mVal->mIsAnimated) {
+    mVal->SetAnimValue(mVal->mBaseVal, mSVGElement);
+    mVal->mIsAnimated = PR_FALSE;
+  }
+}
+
+nsresult
+nsSVGBoolean::SMILBool::SetAnimValue(const nsSMILValue& aValue)
+{
+  NS_ASSERTION(aValue.mType == &SMILBoolType::sSingleton,
+               "Unexpected type to assign animated value");
+  if (aValue.mType == &SMILBoolType::sSingleton) {
+    mVal->SetAnimValue(PRUint16(aValue.mU.mBool), mSVGElement);
+  }
+  return NS_OK;
+}
+#endif // MOZ_SMIL
--- a/content/svg/content/src/nsSVGBoolean.h
+++ b/content/svg/content/src/nsSVGBoolean.h
@@ -43,36 +43,49 @@
 
 class nsSVGBoolean
 {
 
 public:
   void Init(PRUint8 aAttrEnum = 0xff, PRBool aValue = PR_FALSE) {
     mAnimVal = mBaseVal = aValue;
     mAttrEnum = aAttrEnum;
+    mIsAnimated = PR_FALSE;
   }
 
   nsresult SetBaseValueString(const nsAString& aValue,
                               nsSVGElement *aSVGElement,
                               PRBool aDoSetAttr);
   void GetBaseValueString(nsAString& aValue);
 
   void SetBaseValue(PRBool aValue, nsSVGElement *aSVGElement);
   PRBool GetBaseValue() const
     { return mBaseVal; }
-  PRBool GetAnimValue() const
-    { return mAnimVal; }
+
+  void SetAnimValue(PRBool aValue, nsSVGElement *aSVGElement);
+  PRBool GetAnimValue(nsSVGElement *aSVGElement) const
+  {
+  #ifdef MOZ_SMIL
+    aSVGElement->FlushAnimations();
+  #endif
+    return mAnimVal;
+  }
 
   nsresult ToDOMAnimatedBoolean(nsIDOMSVGAnimatedBoolean **aResult,
                                 nsSVGElement* aSVGElement);
+#ifdef MOZ_SMIL
+  // Returns a new nsISMILAttr object that the caller must delete
+  nsISMILAttr* ToSMILAttr(nsSVGElement* aSVGElement);
+#endif // MOZ_SMIL
 
 private:
 
   PRPackedBool mAnimVal;
   PRPackedBool mBaseVal;
+  PRPackedBool mIsAnimated;
   PRUint8 mAttrEnum; // element specified tracking for attribute
 
   struct DOMAnimatedBoolean : public nsIDOMSVGAnimatedBoolean
   {
     NS_DECL_CYCLE_COLLECTING_ISUPPORTS
     NS_DECL_CYCLE_COLLECTION_CLASS(DOMAnimatedBoolean)
 
     DOMAnimatedBoolean(nsSVGBoolean* aVal, nsSVGElement *aSVGElement)
@@ -81,14 +94,36 @@ private:
     nsSVGBoolean* mVal; // kept alive because it belongs to content
     nsRefPtr<nsSVGElement> mSVGElement;
 
     NS_IMETHOD GetBaseVal(PRBool* aResult)
       { *aResult = mVal->GetBaseValue(); return NS_OK; }
     NS_IMETHOD SetBaseVal(PRBool aValue)
       { mVal->SetBaseValue(aValue, mSVGElement); return NS_OK; }
     NS_IMETHOD GetAnimVal(PRBool* aResult)
-      { *aResult = mVal->GetAnimValue(); return NS_OK; }
+      { *aResult = mVal->GetAnimValue(mSVGElement); return NS_OK; }
 
   };
 
+#ifdef MOZ_SMIL
+  struct SMILBool : public nsISMILAttr
+  {
+  public:
+    SMILBool(nsSVGBoolean* aVal, nsSVGElement* aSVGElement)
+      : mVal(aVal), mSVGElement(aSVGElement) {}
+    
+    // These will stay alive because a nsISMILAttr only lives as long
+    // as the Compositing step, and DOM elements don't get a chance to
+    // die during that.
+    nsSVGBoolean* mVal;
+    nsSVGElement* mSVGElement;
+    
+    // nsISMILAttr methods
+    virtual nsresult ValueFromString(const nsAString& aStr,
+                                     const nsISMILAnimationElement* aSrcElement,
+                                     nsSMILValue& aValue) const;
+    virtual nsSMILValue GetBaseValue() const;
+    virtual void ClearAnimValue();
+    virtual nsresult SetAnimValue(const nsSMILValue& aValue);
+  };
+#endif // MOZ_SMIL
 };
 #endif //__NS_SVGBOOLEAN_H__
--- a/content/svg/content/src/nsSVGElement.cpp
+++ b/content/svg/content/src/nsSVGElement.cpp
@@ -1417,16 +1417,29 @@ nsSVGElement::DidChangeBoolean(PRUint8 a
 
   nsAutoString newStr;
   info.mBooleans[aAttrEnum].GetBaseValueString(newStr);
 
   SetAttr(kNameSpaceID_None, *info.mBooleanInfo[aAttrEnum].mName,
           newStr, PR_TRUE);
 }
 
+void
+nsSVGElement::DidAnimateBoolean(PRUint8 aAttrEnum)
+{
+  nsIFrame* frame = GetPrimaryFrame();
+  
+  if (frame) {
+    BooleanAttributesInfo info = GetBooleanInfo();
+    frame->AttributeChanged(kNameSpaceID_None,
+                            *info.mBooleanInfo[aAttrEnum].mName,
+                            nsIDOMMutationEvent::MODIFICATION);
+  }
+}
+
 nsSVGElement::EnumAttributesInfo
 nsSVGElement::GetEnumInfo()
 {
   return EnumAttributesInfo(nsnull, nsnull, 0);
 }
 
 void nsSVGElement::EnumAttributesInfo::Reset(PRUint8 aAttrEnum)
 {
@@ -1721,16 +1734,26 @@ nsSVGElement::GetAnimatedAttr(const nsIA
     EnumAttributesInfo info = GetEnumInfo();
     for (PRUint32 i = 0; i < info.mEnumCount; i++) {
       if (aName == *info.mEnumInfo[i].mName) {
         return info.mEnums[i].ToSMILAttr(this);
       }
     }
   }
 
+  // Booleans:
+  {
+    BooleanAttributesInfo info = GetBooleanInfo();
+    for (PRUint32 i = 0; i < info.mBooleanCount; i++) {
+      if (aName == *info.mBooleanInfo[i].mName) {
+        return info.mBooleans[i].ToSMILAttr(this);
+      }
+    }
+  }
+
   return nsnull;
 }
 
 void
 nsSVGElement::AnimationNeedsResample()
 {
   nsIDocument* doc = GetCurrentDoc();
   if (doc) {
--- a/content/svg/content/src/nsSVGElement.h
+++ b/content/svg/content/src/nsSVGElement.h
@@ -152,16 +152,17 @@ public:
   virtual void DidChangeBoolean(PRUint8 aAttrEnum, PRBool aDoSetAttr);
   virtual void DidChangeEnum(PRUint8 aAttrEnum, PRBool aDoSetAttr);
   virtual void DidChangeViewBox(PRBool aDoSetAttr);
   virtual void DidChangePreserveAspectRatio(PRBool aDoSetAttr);
   virtual void DidChangeString(PRUint8 aAttrEnum) {}
 
   virtual void DidAnimateLength(PRUint8 aAttrEnum);
   virtual void DidAnimateNumber(PRUint8 aAttrEnum);
+  virtual void DidAnimateBoolean(PRUint8 aAttrEnum);
   virtual void DidAnimateEnum(PRUint8 aAttrEnum);
 
   void GetAnimatedLengthValues(float *aFirst, ...);
   void GetAnimatedNumberValues(float *aFirst, ...);
   void GetAnimatedIntegerValues(PRInt32 *aFirst, ...);
 
 #ifdef MOZ_SMIL
   virtual nsISMILAttr* GetAnimatedAttr(const nsIAtom* aName);
--- a/content/svg/content/src/nsSVGFilters.cpp
+++ b/content/svg/content/src/nsSVGFilters.cpp
@@ -324,16 +324,22 @@ nsSVGFE::DidAnimateNumber(PRUint8 aAttrE
 }
 
 void
 nsSVGFE::DidAnimateEnum(PRUint8 aAttrEnum)
 {
   DidAnimateAttr(this);
 }
 
+void
+nsSVGFE::DidAnimateBoolean(PRUint8 aAttrEnum)
+{
+  DidAnimateAttr(this);
+}
+
 //---------------------Gaussian Blur------------------------
 
 typedef nsSVGFE nsSVGFEGaussianBlurElementBase;
 
 class nsSVGFEGaussianBlurElement : public nsSVGFEGaussianBlurElementBase,
                                    public nsIDOMSVGFEGaussianBlurElement
 {
   friend nsresult NS_NewSVGFEGaussianBlurElement(nsIContent **aResult,
@@ -3821,17 +3827,17 @@ public:
 
   NS_FORWARD_NSIDOMNODE(nsSVGFEConvolveMatrixElementBase::)
   NS_FORWARD_NSIDOMELEMENT(nsSVGFEConvolveMatrixElementBase::)
 
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
 
 protected:
   virtual PRBool OperatesOnPremultipledAlpha() {
-    return !mBooleanAttributes[PRESERVEALPHA].GetAnimValue();
+    return !mBooleanAttributes[PRESERVEALPHA].GetAnimValue(this);
   }
 
   virtual NumberAttributesInfo GetNumberInfo();
   virtual IntegerAttributesInfo GetIntegerInfo();
   virtual BooleanAttributesInfo GetBooleanInfo();
   virtual EnumAttributesInfo GetEnumInfo();
   virtual StringAttributesInfo GetStringInfo();
 
@@ -4185,17 +4191,17 @@ nsSVGFEConvolveMatrixElement::Filter(nsS
   ScaleInfo info = SetupScalingFilter(instance, aSources[0], aTarget, rect,
                                       &mNumberAttributes[KERNEL_UNIT_LENGTH_X],
                                       &mNumberAttributes[KERNEL_UNIT_LENGTH_Y],
                                       this);
   if (!info.mTarget)
     return NS_ERROR_FAILURE;
 
   PRUint16 edgeMode = mEnumAttributes[EDGEMODE].GetAnimValue(this);
-  PRBool preserveAlpha = mBooleanAttributes[PRESERVEALPHA].GetAnimValue();
+  PRBool preserveAlpha = mBooleanAttributes[PRESERVEALPHA].GetAnimValue(this);
 
   float bias = 0;
   if (HasAttr(kNameSpaceID_None, nsGkAtoms::bias)) {
     bias = mNumberAttributes[BIAS].GetAnimValue(this);
   }
 
   const nsIntRect& dataRect = info.mDataRect;
   PRInt32 stride = info.mSource->Stride();
--- a/content/svg/content/src/nsSVGFilters.h
+++ b/content/svg/content/src/nsSVGFilters.h
@@ -215,16 +215,17 @@ protected:
              NS_STYLE_COLOR_INTERPOLATION_SRGB;
   }
 
   // nsSVGElement specializations:
   virtual LengthAttributesInfo GetLengthInfo();
   virtual void DidAnimateLength(PRUint8 aAttrEnum);
   virtual void DidAnimateNumber(PRUint8 aAttrEnum);
   virtual void DidAnimateEnum(PRUint8 aAttrEnum);
+  virtual void DidAnimateBoolean(PRUint8 aAttrEnum);
 
   // nsIDOMSVGFitlerPrimitiveStandardAttributes values
   enum { X, Y, WIDTH, HEIGHT };
   nsSVGLength2 mLengthAttributes[4];
   static LengthInfo sLengthInfo[4];
 };
 
 #endif
new file mode 100755
--- /dev/null
+++ b/layout/reftests/svg/smil/anim-feConvolveMatrix-preserveAlpha-01.svg
@@ -0,0 +1,41 @@
+<svg xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink"
+     class="reftest-wait"
+     onload="setTimeAndSnapshot(1, true)">
+  <title>Test animation of the "preserveAlpha" &lt;boolean&gt; attribute on the "feConvolveMatrix" element</title>
+  <script xlink:href="smil-util.js" type="text/javascript"/>
+  <filter id="convolve_1">
+    <feConvolveMatrix order="2 2" kernelMatrix="0 0 0 0" preserveAlpha="true">
+      <!-- turn the referencing element from transparent to red at 1.25s -->
+      <animate attributeName="preserveAlpha"
+               calcMode="discrete"
+               begin="0s" dur="2.5s"
+               from="false" to="true"
+               fill="freeze"/>
+    </feConvolveMatrix>
+    <feColorMatrix type="matrix"
+                   values="0 0 0 1 0  0 0 0 0 0  0 0 0 0 0  0 0 0 1 0"/>
+  </filter>
+  <filter id="convolve_2">
+    <feConvolveMatrix order="2 2" kernelMatrix="0 0 0 0" preserveAlpha="true">
+      <!-- turn the referencing element from red to transparent at 1s -->
+      <animate attributeName="preserveAlpha"
+               calcMode="discrete"
+               begin="0s" dur="2s"
+               from="true" to="false"
+               fill="freeze"/>
+    </feConvolveMatrix>
+    <feColorMatrix type="matrix"
+                   values="0 0 0 1 0  0 0 0 0 0  0 0 0 0 0  0 0 0 1 0"/>
+  </filter>
+  <rect width="100%" height="100%" fill="lime"/>
+
+  <!-- 40% through discrete animation simple duration -
+       tests that the animation doesn't affect the element too early -->
+  <rect        width="100" height="50" fill="red" filter="url(#convolve_1)"/>
+
+  <!-- 50% through discrete animation simple duration -
+       tests that the animation affects the element now -->
+  <rect y="50" width="100" height="50" fill="red" filter="url(#convolve_2)"/>
+
+</svg>
--- a/layout/reftests/svg/smil/reftest.list
+++ b/layout/reftests/svg/smil/reftest.list
@@ -80,16 +80,19 @@ fails == anim-fillopacity-1xml.svg  anim
 == anim-feOffset-01.svg lime.svg
 == anim-offset-01.svg lime.svg
 == anim-pathLength-01.svg anim-pathLength-01-ref.svg
 
 # animate some enumeration attributes:
 == anim-feComposite-operator-01.svg lime.svg
 == anim-filter-filterUnits-01.svg lime.svg
 
+# animate some boolean attributes:
+== anim-feConvolveMatrix-preserveAlpha-01.svg lime.svg
+
 == anim-remove-1.svg anim-standard-ref.svg
 == anim-remove-2.svg anim-standard-ref.svg
 == anim-remove-3.svg anim-standard-ref.svg
 == anim-remove-4.svg anim-standard-ref.svg
 == anim-remove-5.svg anim-standard-ref.svg
 == anim-remove-6.svg anim-standard-ref.svg
 == anim-remove-7.svg anim-standard-ref.svg
 == anim-remove-8css.svg anim-standard-ref.svg