Bug 589436 patch 2: Allow string-valued SVG attributes to be SMIL-animated. r=dholbert a=blocking-b8+
authorRobert Longson <longsonr@gmail.com>
Thu, 07 Oct 2010 12:19:32 -0700
changeset 55139 8efdbfd80d46dd50d64dea63f7fac23353c345bf
parent 55138 4aaba15aaecf1c2ca44b023109ea0864397c0a10
child 55140 dc5fc7c30ee335b35178556953692a3c06c3f304
push idunknown
push userunknown
push dateunknown
reviewersdholbert, blocking-b8
bugs589436
milestone2.0b8pre
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 589436 patch 2: Allow string-valued SVG attributes to be SMIL-animated. r=dholbert a=blocking-b8+
content/base/public/nsIContent.h
content/base/src/nsGenericDOMDataNode.h
content/base/src/nsGenericElement.h
content/smil/Makefile.in
content/smil/SMILStringType.cpp
content/smil/SMILStringType.h
content/smil/nsISMILAnimationElement.h
content/smil/nsSMILAnimationController.cpp
content/smil/nsSMILCompositor.cpp
content/smil/nsSMILTargetIdentifier.h
content/svg/content/src/nsSVGAElement.cpp
content/svg/content/src/nsSVGAltGlyphElement.cpp
content/svg/content/src/nsSVGAnimateMotionElement.cpp
content/svg/content/src/nsSVGAnimateMotionElement.h
content/svg/content/src/nsSVGAnimationElement.cpp
content/svg/content/src/nsSVGAnimationElement.h
content/svg/content/src/nsSVGElement.cpp
content/svg/content/src/nsSVGElement.h
content/svg/content/src/nsSVGFilterElement.cpp
content/svg/content/src/nsSVGFilters.cpp
content/svg/content/src/nsSVGFilters.h
content/svg/content/src/nsSVGGradientElement.cpp
content/svg/content/src/nsSVGImageElement.cpp
content/svg/content/src/nsSVGImageElement.h
content/svg/content/src/nsSVGMpathElement.cpp
content/svg/content/src/nsSVGPatternElement.cpp
content/svg/content/src/nsSVGScriptElement.cpp
content/svg/content/src/nsSVGString.cpp
content/svg/content/src/nsSVGString.h
content/svg/content/src/nsSVGTextPathElement.cpp
content/svg/content/src/nsSVGUseElement.cpp
content/svg/content/src/nsSVGUseElement.h
--- a/content/base/public/nsIContent.h
+++ b/content/base/public/nsIContent.h
@@ -66,18 +66,18 @@ enum nsLinkState {
   eLinkState_Unknown    = 0,
   eLinkState_Unvisited  = 1,
   eLinkState_Visited    = 2,
   eLinkState_NotLink    = 3
 };
 
 // IID for the nsIContent interface
 #define NS_ICONTENT_IID       \
-{ 0xdd254504, 0xe273, 0x4923, \
-  { 0x9e, 0xc1, 0xd8, 0x42, 0x1a, 0x66, 0x35, 0xf1 } }
+{ 0x64ef8589, 0xbd19, 0x40f4, \
+  { 0xa9, 0x61, 0x47, 0x89, 0xe0, 0x8d, 0xb0, 0x49 } }
 
 /**
  * A node of content in a document's content model. This interface
  * is supported by all content objects.
  */
 class nsIContent : public nsINode {
 public:
 #ifdef MOZILLA_INTERNAL_API
@@ -890,17 +890,17 @@ public:
 
 #ifdef MOZ_SMIL
   /*
    * Returns a new nsISMILAttr that allows the caller to animate the given
    * attribute on this element.
    *
    * The CALLER OWNS the result and is responsible for deleting it.
    */
-  virtual nsISMILAttr* GetAnimatedAttr(nsIAtom* aName) = 0;
+  virtual nsISMILAttr* GetAnimatedAttr(PRInt32 aNamespaceID, nsIAtom* aName) = 0;
 
    /**
     * Get the SMIL override style for this content node.  This is a style
     * declaration that is applied *after* the inline style, and it can be used
     * e.g. to store animated style values.
     *
     * Note: This method is analogous to the 'GetStyle' method in
     * nsGenericHTMLElement and nsStyledElement.
--- a/content/base/src/nsGenericDOMDataNode.h
+++ b/content/base/src/nsGenericDOMDataNode.h
@@ -225,17 +225,17 @@ public:
   virtual nsresult AppendText(const PRUnichar* aBuffer, PRUint32 aLength,
                               PRBool aNotify);
   virtual PRBool TextIsOnlyWhitespace();
   virtual void AppendTextTo(nsAString& aResult);
   virtual void DestroyContent();
   virtual void SaveSubtreeState();
 
 #ifdef MOZ_SMIL
-  virtual nsISMILAttr* GetAnimatedAttr(nsIAtom* /*aName*/)
+  virtual nsISMILAttr* GetAnimatedAttr(PRInt32 /*aNamespaceID*/, nsIAtom* /*aName*/)
   {
     return nsnull;
   }
   virtual nsresult GetSMILOverrideStyle(nsIDOMCSSStyleDeclaration** aStyle);
   virtual nsICSSStyleRule* GetSMILOverrideStyleRule();
   virtual nsresult SetSMILOverrideStyleRule(nsICSSStyleRule* aStyleRule,
                                             PRBool aNotify);
 #endif // MOZ_SMIL
--- a/content/base/src/nsGenericElement.h
+++ b/content/base/src/nsGenericElement.h
@@ -425,17 +425,17 @@ public:
 
   virtual PRUint32 GetScriptTypeID() const;
   NS_IMETHOD SetScriptTypeID(PRUint32 aLang);
 
   virtual void DestroyContent();
   virtual void SaveSubtreeState();
 
 #ifdef MOZ_SMIL
-  virtual nsISMILAttr* GetAnimatedAttr(nsIAtom* /*aName*/)
+  virtual nsISMILAttr* GetAnimatedAttr(PRInt32 /*aNamespaceID*/, nsIAtom* /*aName*/)
   {
     return nsnull;
   }
   virtual nsresult GetSMILOverrideStyle(nsIDOMCSSStyleDeclaration** aStyle);
   virtual nsICSSStyleRule* GetSMILOverrideStyleRule();
   virtual nsresult SetSMILOverrideStyleRule(nsICSSStyleRule* aStyleRule,
                                             PRBool aNotify);
 #endif // MOZ_SMIL
--- a/content/smil/Makefile.in
+++ b/content/smil/Makefile.in
@@ -71,16 +71,17 @@ CPPSRCS		+= \
 	nsSMILTimeContainer.cpp \
 	nsSMILTimedElement.cpp \
 	nsSMILTimeValue.cpp \
 	nsSMILTimeValueSpec.cpp \
 	nsSMILValue.cpp \
 	SMILBoolType.cpp \
 	SMILEnumType.cpp \
 	SMILIntegerType.cpp \
+	SMILStringType.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/SMILStringType.cpp
@@ -0,0 +1,123 @@
+/* -*- 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) 2010
+ * 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 "SMILStringType.h"
+#include "nsSMILValue.h"
+#include "nsDebug.h"
+#include "nsString.h"
+
+namespace mozilla {
+
+/*static*/ SMILStringType SMILStringType::sSingleton;
+
+void
+SMILStringType::Init(nsSMILValue& aValue) const
+{
+  NS_PRECONDITION(aValue.IsNull(), "Unexpected value type");
+  aValue.mU.mPtr = new nsString();
+  aValue.mType = this;
+}
+
+void
+SMILStringType::Destroy(nsSMILValue& aValue) const
+{
+  NS_PRECONDITION(aValue.mType == this, "Unexpected SMIL value");
+  delete static_cast<nsAString*>(aValue.mU.mPtr);
+  aValue.mU.mPtr = nsnull;
+  aValue.mType = &nsSMILNullType::sSingleton;
+}
+
+nsresult
+SMILStringType::Assign(nsSMILValue& aDest, const nsSMILValue& aSrc) const
+{
+  NS_PRECONDITION(aDest.mType == aSrc.mType, "Incompatible SMIL types");
+  NS_PRECONDITION(aDest.mType == this, "Unexpected SMIL value");
+
+  const nsAString* src = static_cast<const nsAString*>(aSrc.mU.mPtr);
+  nsAString* dst = static_cast<nsAString*>(aDest.mU.mPtr);
+  *dst = *src;
+  return NS_OK;
+}
+
+PRBool
+SMILStringType::IsEqual(const nsSMILValue& aLeft,
+                        const nsSMILValue& aRight) const
+{
+  NS_PRECONDITION(aLeft.mType == aRight.mType, "Incompatible SMIL types");
+  NS_PRECONDITION(aLeft.mType == this, "Unexpected type for SMIL value");
+
+  const nsAString* leftString =
+    static_cast<const nsAString*>(aLeft.mU.mPtr);
+  const nsAString* rightString =
+    static_cast<nsAString*>(aRight.mU.mPtr);
+  return *leftString == *rightString;
+}
+
+nsresult
+SMILStringType::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; // string values can't be added to each other
+}
+
+nsresult
+SMILStringType::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 string values
+}
+
+nsresult
+SMILStringType::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; // string values do not interpolate
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/smil/SMILStringType.h
@@ -0,0 +1,77 @@
+/* -*- 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) 2010
+ * 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_SMILSTRINGTYPE_H_
+#define MOZILLA_SMILSTRINGTYPE_H_
+
+#include "nsISMILType.h"
+
+namespace mozilla {
+
+class SMILStringType : public nsISMILType
+{
+public:
+  // Singleton for nsSMILValue objects to hold onto.
+  static SMILStringType sSingleton;
+
+protected:
+  // nsISMILType Methods
+  // -------------------
+  virtual void     Init(nsSMILValue& aValue) const;
+  virtual void     Destroy(nsSMILValue&) const;
+  virtual nsresult Assign(nsSMILValue& aDest, const nsSMILValue& aSrc) const;
+  virtual PRBool   IsEqual(const nsSMILValue& aLeft,
+                           const nsSMILValue& aRight) 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;
+
+private:
+  // Private constructor & destructor: prevent instances beyond my singleton,
+  // and prevent others from deleting my singleton.
+  SMILStringType()  {}
+  ~SMILStringType() {}
+};
+
+} // namespace mozilla
+
+#endif // MOZILLA_SMILSTRINGTYPE_H_
--- a/content/smil/nsISMILAnimationElement.h
+++ b/content/smil/nsISMILAnimationElement.h
@@ -41,18 +41,18 @@
 
 #include "nsISupports.h"
 
 //////////////////////////////////////////////////////////////////////////////
 // nsISMILAnimationElement: Interface for elements that control the animation of
 // some property of another element, e.g. <animate>, <set>.
 
 #define NS_ISMILANIMATIONELEMENT_IID \
-{ 0x5c891601, 0x47aa, 0x4230,        \
-  { 0xb8, 0xdc, 0xb9, 0x26, 0xd1, 0xe7, 0xd7, 0xf4 } }
+{ 0xaf92584b, 0x75b0, 0x4584,        \
+  { 0x87, 0xd2, 0xa8, 0x3, 0x34, 0xf0, 0x5, 0xaf } }
 
 class nsISMILAttr;
 class nsSMILAnimationFunction;
 class nsSMILTimeContainer;
 class nsSMILTimedElement;
 class nsIAtom;
 class nsAttrValue;
 
@@ -115,17 +115,18 @@ public:
   /*
    * Returns the target (animated) element.
    */
   virtual mozilla::dom::Element* GetTargetElementContent() = 0;
 
   /*
    * Returns the name of the target (animated) attribute or property.
    */
-  virtual nsIAtom* GetTargetAttributeName() const = 0;
+  virtual PRBool GetTargetAttributeName(PRInt32* aNamespaceID,
+                                        nsIAtom** aLocalName) const = 0;
 
   /*
    * Returns the type of the target (animated) attribute or property.
    */
   virtual nsSMILTargetAttrType GetTargetAttributeType() const = 0;
 
   /*
    * Returns the SMIL animation function associated with this animation element.
--- a/content/smil/nsSMILAnimationController.cpp
+++ b/content/smil/nsSMILAnimationController.cpp
@@ -16,16 +16,17 @@
  *
  * The Initial Developer of the Original Code is Brian Birtles.
  * Portions created by the Initial Developer are Copyright (C) 2005
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Brian Birtles <birtles@gmail.com>
  *   Daniel Holbert <dholbert@mozilla.com>
+ *   Robert Longson <longsonr@gmail.com>
  *
  * 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
@@ -715,43 +716,46 @@ nsSMILAnimationController::GetTargetIden
 {
   // Look up target (animated) element
   Element* targetElem = aAnimElem->GetTargetElementContent();
   if (!targetElem)
     // Animation has no target elem -- skip it.
     return PR_FALSE;
 
   // Look up target (animated) attribute
-  //
-  // XXXdholbert As mentioned in SMILANIM section 3.1, attributeName may
-  // have an XMLNS prefix to indicate the XML namespace. Need to parse
-  // that somewhere.
-  nsIAtom* attributeName = aAnimElem->GetTargetAttributeName();
-  if (!attributeName)
+  // SMILANIM section 3.1, attributeName may
+  // have an XMLNS prefix to indicate the XML namespace.
+  nsCOMPtr<nsIAtom> attributeName;
+  PRInt32 attributeNamespaceID;
+  if (!aAnimElem->GetTargetAttributeName(&attributeNamespaceID,
+                                         getter_AddRefs(attributeName)))
     // Animation has no target attr -- skip it.
     return PR_FALSE;
 
   // Look up target (animated) attribute-type
   nsSMILTargetAttrType attributeType = aAnimElem->GetTargetAttributeType();
 
   // Check if an 'auto' attributeType refers to a CSS property or XML attribute.
   // Note that SMIL requires we search for CSS properties first. So if they
   // overlap, 'auto' = 'CSS'. (SMILANIM 3.1)
-  PRBool isCSS;
+  PRBool isCSS = PR_FALSE;
   if (attributeType == eSMILTargetAttrType_auto) {
-    nsCSSProperty prop =
-      nsCSSProps::LookupProperty(nsDependentAtomString(attributeName));
-    isCSS = nsSMILCSSProperty::IsPropertyAnimatable(prop);
+    if (attributeNamespaceID == kNameSpaceID_None) {
+      nsCSSProperty prop =
+        nsCSSProps::LookupProperty(nsDependentAtomString(attributeName));
+      isCSS = nsSMILCSSProperty::IsPropertyAnimatable(prop);
+    }
   } else {
     isCSS = (attributeType == eSMILTargetAttrType_CSS);
   }
 
   // Construct the key
   aResult.mElement = targetElem;
   aResult.mAttributeName = attributeName;
+  aResult.mAttributeNamespaceID = attributeNamespaceID;
   aResult.mIsCSS = isCSS;
 
   return PR_TRUE;
 }
 
 //----------------------------------------------------------------------
 // Add/remove child time containers
 
--- a/content/smil/nsSMILCompositor.cpp
+++ b/content/smil/nsSMILCompositor.cpp
@@ -153,17 +153,18 @@ nsSMILCompositor::CreateSMILAttr()
 {
   if (mKey.mIsCSS) {
     nsCSSProperty propId =
       nsCSSProps::LookupProperty(nsDependentAtomString(mKey.mAttributeName));
     if (nsSMILCSSProperty::IsPropertyAnimatable(propId)) {
       return new nsSMILCSSProperty(propId, mKey.mElement.get());
     }
   } else {
-    return mKey.mElement->GetAnimatedAttr(mKey.mAttributeName);
+    return mKey.mElement->GetAnimatedAttr(mKey.mAttributeNamespaceID,
+                                          mKey.mAttributeName);
   }
   return nsnull;
 }
 
 PRUint32
 nsSMILCompositor::GetFirstFuncToAffectSandwich()
 {
   PRUint32 i;
--- a/content/smil/nsSMILTargetIdentifier.h
+++ b/content/smil/nsSMILTargetIdentifier.h
@@ -53,27 +53,30 @@
  * NOTE: Need a nsRefPtr for the element & attribute name, because
  * nsSMILAnimationController retain its hash table for one sample into the
  * future, and we need to make sure their target isn't deleted in that time.
  */
 
 struct nsSMILTargetIdentifier
 {
   nsSMILTargetIdentifier()
-    : mElement(nsnull), mAttributeName(nsnull), mIsCSS(PR_FALSE) {}
+    : mElement(nsnull), mAttributeName(nsnull),
+      mAttributeNamespaceID(kNameSpaceID_Unknown), mIsCSS(PR_FALSE) {}
 
   inline PRBool Equals(const nsSMILTargetIdentifier& aOther) const
   {
-    return (aOther.mElement       == mElement &&
-            aOther.mAttributeName == mAttributeName &&
-            aOther.mIsCSS         == mIsCSS);
+    return (aOther.mElement              == mElement &&
+            aOther.mAttributeName        == mAttributeName &&
+            aOther.mAttributeNamespaceID == mAttributeNamespaceID &&
+            aOther.mIsCSS                == mIsCSS);
   }
 
   nsRefPtr<mozilla::dom::Element> mElement;
-  nsRefPtr<nsIAtom>    mAttributeName; // XXX need to consider namespaces here
+  nsRefPtr<nsIAtom>    mAttributeName;
+  PRInt32              mAttributeNamespaceID;
   PRPackedBool         mIsCSS;
 };
 
 /**
  * Class: nsSMILWeakTargetIdentifier
  *
  * Version of the above struct that uses non-owning pointers.  These are kept
  * private, to ensure that they aren't ever dereferenced (or used at all,
--- a/content/svg/content/src/nsSVGAElement.cpp
+++ b/content/svg/content/src/nsSVGAElement.cpp
@@ -42,18 +42,18 @@
 #include "nsILink.h"
 #include "nsSVGString.h"
 #include "nsCOMPtr.h"
 #include "nsGkAtoms.h"
 
 
 nsSVGElement::StringInfo nsSVGAElement::sStringInfo[2] =
 {
-  { &nsGkAtoms::href, kNameSpaceID_XLink },
-  { &nsGkAtoms::target, kNameSpaceID_None }
+  { &nsGkAtoms::href, kNameSpaceID_XLink, PR_TRUE },
+  { &nsGkAtoms::target, kNameSpaceID_None, PR_TRUE }
 };
 
 NS_IMPL_NS_NEW_SVG_ELEMENT(A)
 
 
 //----------------------------------------------------------------------
 // nsISupports methods
 
--- a/content/svg/content/src/nsSVGAltGlyphElement.cpp
+++ b/content/svg/content/src/nsSVGAltGlyphElement.cpp
@@ -80,17 +80,17 @@ protected:
   enum { HREF };
   nsSVGString mStringAttributes[1];
   static StringInfo sStringInfo[1];
 
 };
 
 nsSVGElement::StringInfo nsSVGAltGlyphElement::sStringInfo[1] =
 {
-  { &nsGkAtoms::href, kNameSpaceID_XLink }
+  { &nsGkAtoms::href, kNameSpaceID_XLink, PR_FALSE }
 };
 
 NS_IMPL_NS_NEW_SVG_ELEMENT(AltGlyph)
 
 //----------------------------------------------------------------------
 // nsISupports methods
 
 NS_IMPL_ADDREF_INHERITED(nsSVGAltGlyphElement,nsSVGAltGlyphElementBase)
--- a/content/svg/content/src/nsSVGAnimateMotionElement.cpp
+++ b/content/svg/content/src/nsSVGAnimateMotionElement.cpp
@@ -75,23 +75,26 @@ NS_IMPL_ELEMENT_CLONE_WITH_INIT(nsSVGAni
 // nsISMILAnimationElement methods
 
 nsSMILAnimationFunction&
 nsSVGAnimateMotionElement::AnimationFunction()
 {
   return mAnimationFunction;
 }
 
-nsIAtom*
-nsSVGAnimateMotionElement::GetTargetAttributeName() const
+PRBool
+nsSVGAnimateMotionElement::GetTargetAttributeName(PRInt32 *aNamespaceID,
+                                                  nsIAtom **aLocalName) const
 {
   // <animateMotion> doesn't take an attributeName, since it doesn't target an
   // 'attribute' per se.  We'll use a unique dummy attribute-name so that our
   // nsSMILTargetIdentifier logic (which requires a attribute name) still works.
-  return nsGkAtoms::mozAnimateMotionDummyAttr;
+  *aNamespaceID = kNameSpaceID_None;
+  *aLocalName = nsGkAtoms::mozAnimateMotionDummyAttr;
+  return PR_TRUE;
 }
 
 nsSMILTargetAttrType
 nsSVGAnimateMotionElement::GetTargetAttributeType() const
 {
   // <animateMotion> doesn't take an attributeType, since it doesn't target an
   // 'attribute' per se.  We'll just return 'XML' for simplicity.  (This just
   // needs to match what we expect in nsSVGElement::GetAnimAttr.)
--- a/content/svg/content/src/nsSVGAnimateMotionElement.h
+++ b/content/svg/content/src/nsSVGAnimateMotionElement.h
@@ -65,17 +65,18 @@ public:
   NS_FORWARD_NSIDOMSVGELEMENT(nsSVGAnimateMotionElementBase::)
   NS_FORWARD_NSIDOMSVGANIMATIONELEMENT(nsSVGAnimateMotionElementBase::)
 
   // nsIDOMNode specializations
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
 
   // nsISMILAnimationElement
   virtual nsSMILAnimationFunction& AnimationFunction();
-  virtual nsIAtom* GetTargetAttributeName() const;
+  virtual PRBool GetTargetAttributeName(PRInt32 *aNamespaceID,
+                                        nsIAtom **aLocalName) const;
   virtual nsSMILTargetAttrType GetTargetAttributeType() const;
 
   // Utility method to let our <mpath> children tell us when they've changed,
   // so we can make sure our mAnimationFunction is marked as having changed.
   void MpathChanged() { mAnimationFunction.MpathChanged(); }
 
   virtual nsXPCClassInfo* GetClassInfo();
 };
--- a/content/svg/content/src/nsSVGAnimationElement.cpp
+++ b/content/svg/content/src/nsSVGAnimationElement.cpp
@@ -146,28 +146,32 @@ nsSVGAnimationElement::GetTargetElementC
   NS_ABORT_IF_FALSE(!mHrefTarget.get(),
                     "We shouldn't have an xlink:href target "
                     "if we don't have an xlink:href attribute");
 
   // No "xlink:href" attribute --> I should target my parent.
   return nsSVGUtils::GetParentElement(this);
 }
 
-nsIAtom*
-nsSVGAnimationElement::GetTargetAttributeName() const
+PRBool
+nsSVGAnimationElement::GetTargetAttributeName(PRInt32 *aNamespaceID,
+                                              nsIAtom **aLocalName) const
 {
   const nsAttrValue* nameAttr
     = mAttrsAndChildren.GetAttr(nsGkAtoms::attributeName);
 
   if (!nameAttr)
-    return nsnull;
+    return PR_FALSE;
 
   NS_ASSERTION(nameAttr->Type() == nsAttrValue::eAtom,
     "attributeName should have been parsed as an atom");
-  return nameAttr->GetAtomValue();
+
+  return NS_SUCCEEDED(nsContentUtils::SplitQName(
+                        this, nsDependentAtomString(nameAttr->GetAtomValue()),
+                        aNamespaceID, aLocalName));
 }
 
 nsSMILTargetAttrType
 nsSVGAnimationElement::GetTargetAttributeType() const
 {
   nsIContent::AttrValuesArray typeValues[] = { &nsGkAtoms::css,
                                                &nsGkAtoms::XML,
                                                nsnull};
--- a/content/svg/content/src/nsSVGAnimationElement.h
+++ b/content/svg/content/src/nsSVGAnimationElement.h
@@ -89,17 +89,18 @@ public:
 
   // nsISMILAnimationElement interface
   virtual const Element& AsElement() const;
   virtual Element& AsElement();
   virtual const nsAttrValue* GetAnimAttr(nsIAtom* aName) const;
   virtual PRBool GetAnimAttr(nsIAtom* aAttName, nsAString& aResult) const;
   virtual PRBool HasAnimAttr(nsIAtom* aAttName) const;
   virtual Element* GetTargetElementContent();
-  virtual nsIAtom* GetTargetAttributeName() const;
+  virtual PRBool GetTargetAttributeName(PRInt32* aNamespaceID,
+                                        nsIAtom** aLocalName) const;
   virtual nsSMILTargetAttrType GetTargetAttributeType() const;
   virtual nsSMILTimedElement& TimedElement();
   virtual nsSMILTimeContainer* GetTimeContainer();
 
 protected:
   // nsSVGElement overrides
   PRBool IsEventName(nsIAtom* aName);
 
--- a/content/svg/content/src/nsSVGElement.cpp
+++ b/content/svg/content/src/nsSVGElement.cpp
@@ -1928,16 +1928,29 @@ void nsSVGElement::SetStringBaseValue(PR
                "SetBaseValue on element with no string attribs");
 
   NS_ASSERTION(aAttrEnum < info.mStringCount, "aAttrEnum out of range");
 
   SetAttr(info.mStringInfo[aAttrEnum].mNamespaceID,
           *info.mStringInfo[aAttrEnum].mName, aValue, PR_TRUE);
 }
 
+void
+nsSVGElement::DidAnimateString(PRUint8 aAttrEnum)
+{
+  nsIFrame* frame = GetPrimaryFrame();
+
+  if (frame) {
+    StringAttributesInfo info = GetStringInfo();
+    frame->AttributeChanged(info.mStringInfo[aAttrEnum].mNamespaceID,
+                            *info.mStringInfo[aAttrEnum].mName,
+                            nsIDOMMutationEvent::MODIFICATION);
+  }
+}
+
 nsresult
 nsSVGElement::ParseNumberOptionalNumber(const nsAString& aValue,
                                         PRUint32 aIndex1, PRUint32 aIndex2)
 {
   NS_ConvertUTF16toUTF8 value(aValue);
   const char *str = value.get();
 
   if (NS_IsAsciiWhitespace(*str))
@@ -2049,147 +2062,160 @@ nsSVGElement::RecompileScriptEventListen
     nsAutoString value;
     GetAttr(kNameSpaceID_None, attr, value);
     AddScriptEventListener(GetEventNameForAttr(attr), value, PR_TRUE);
   }
 }
 
 #ifdef MOZ_SMIL
 nsISMILAttr*
-nsSVGElement::GetAnimatedAttr(nsIAtom* aName)
+nsSVGElement::GetAnimatedAttr(PRInt32 aNamespaceID, nsIAtom* aName)
 {
-  // Transforms:
-  nsCOMPtr<nsIDOMSVGAnimatedTransformList> transformList;
-  if (aName == nsGkAtoms::transform) {
-    nsCOMPtr<nsIDOMSVGTransformable> transformable(
-            do_QueryInterface(static_cast<nsIContent*>(this)));
-    if (!transformable)
-      return nsnull;
-    nsresult rv = transformable->GetTransform(getter_AddRefs(transformList));
-    NS_ENSURE_SUCCESS(rv, nsnull);
-  }
-  if (aName == nsGkAtoms::gradientTransform) {
-    nsCOMPtr<nsIDOMSVGGradientElement> gradientElement(
-            do_QueryInterface(static_cast<nsIContent*>(this)));
-    if (!gradientElement)
-      return nsnull;
+  if (aNamespaceID == kNameSpaceID_None) {
+    // Transforms:
+    nsCOMPtr<nsIDOMSVGAnimatedTransformList> transformList;
+    if (aName == nsGkAtoms::transform) {
+      nsCOMPtr<nsIDOMSVGTransformable> transformable(
+              do_QueryInterface(static_cast<nsIContent*>(this)));
+      if (!transformable)
+        return nsnull;
+      nsresult rv = transformable->GetTransform(getter_AddRefs(transformList));
+      NS_ENSURE_SUCCESS(rv, nsnull);
+    }
+    if (aName == nsGkAtoms::gradientTransform) {
+      nsCOMPtr<nsIDOMSVGGradientElement> gradientElement(
+              do_QueryInterface(static_cast<nsIContent*>(this)));
+      if (!gradientElement)
+        return nsnull;
 
-    nsresult rv = gradientElement->GetGradientTransform(getter_AddRefs(transformList));
-    NS_ENSURE_SUCCESS(rv, nsnull);
-  }
-  if (aName == nsGkAtoms::patternTransform) {
-    nsCOMPtr<nsIDOMSVGPatternElement> patternElement(
-            do_QueryInterface(static_cast<nsIContent*>(this)));
-    if (!patternElement)
-      return nsnull;
+      nsresult rv = gradientElement->GetGradientTransform(getter_AddRefs(transformList));
+      NS_ENSURE_SUCCESS(rv, nsnull);
+    }
+    if (aName == nsGkAtoms::patternTransform) {
+      nsCOMPtr<nsIDOMSVGPatternElement> patternElement(
+              do_QueryInterface(static_cast<nsIContent*>(this)));
+      if (!patternElement)
+        return nsnull;
+
+      nsresult rv = patternElement->GetPatternTransform(getter_AddRefs(transformList));
+      NS_ENSURE_SUCCESS(rv, nsnull);
+    }
+    if (transformList) {
+      nsSVGAnimatedTransformList* list
+        = static_cast<nsSVGAnimatedTransformList*>(transformList.get());
+      NS_ENSURE_TRUE(list, nsnull);
+
+      return new nsSVGTransformSMILAttr(list, this);
+    }
+
+    // Motion (fake 'attribute' for animateMotion)
+    if (aName == nsGkAtoms::mozAnimateMotionDummyAttr) {
+      return new SVGMotionSMILAttr(this);
+    }
+
+    // Lengths:
+    LengthAttributesInfo info = GetLengthInfo();
+    for (PRUint32 i = 0; i < info.mLengthCount; i++) {
+      if (aName == *info.mLengthInfo[i].mName) {
+        return info.mLengths[i].ToSMILAttr(this);
+      }
+    }
 
-    nsresult rv = patternElement->GetPatternTransform(getter_AddRefs(transformList));
-    NS_ENSURE_SUCCESS(rv, nsnull);
-  }
-  if (transformList) {
-    nsSVGAnimatedTransformList* list
-      = static_cast<nsSVGAnimatedTransformList*>(transformList.get());
-    NS_ENSURE_TRUE(list, nsnull);
+    // Numbers:
+    {
+      NumberAttributesInfo info = GetNumberInfo();
+      for (PRUint32 i = 0; i < info.mNumberCount; i++) {
+        // XXX this isn't valid for either of the two properties corresponding to
+        // attributes of type <number-optional-number> - see filter,
+        // feConvolveMatrix, feDiffuseLighting, feGaussianBlur, feMorphology and
+        // feTurbulence.
+        // The way to fix this is probably to handle them as 2-item number lists
+        // once we implement number list animation, and put the number list loop
+        // *above* this one at that time to catch those properties before we get
+        // here. The separate properties should then point into the list.
+        if (aName == *info.mNumberInfo[i].mName) {
+          return info.mNumbers[i].ToSMILAttr(this);
+        }
+      }
+    }
 
-    return new nsSVGTransformSMILAttr(list, this);
-  }
+    // Integers:
+    {
+      IntegerAttributesInfo info = GetIntegerInfo();
+      for (PRUint32 i = 0; i < info.mIntegerCount; i++) {
+        if (aName == *info.mIntegerInfo[i].mName) {
+          return info.mIntegers[i].ToSMILAttr(this);
+        }
+      }
+    }
 
-  // Motion (fake 'attribute' for animateMotion)
-  if (aName == nsGkAtoms::mozAnimateMotionDummyAttr) {
-    return new SVGMotionSMILAttr(this);
-  }
+    // Enumerations:
+    {
+      EnumAttributesInfo info = GetEnumInfo();
+      for (PRUint32 i = 0; i < info.mEnumCount; i++) {
+        if (aName == *info.mEnumInfo[i].mName) {
+          return info.mEnums[i].ToSMILAttr(this);
+        }
+      }
+    }
 
-  // Lengths:
-  LengthAttributesInfo info = GetLengthInfo();
-  for (PRUint32 i = 0; i < info.mLengthCount; i++) {
-    if (aName == *info.mLengthInfo[i].mName) {
-      return info.mLengths[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);
+        }
+      }
     }
-  }
+
+    // Angles:
+    {
+      AngleAttributesInfo info = GetAngleInfo();
+      for (PRUint32 i = 0; i < info.mAngleCount; i++) {
+        if (aName == *info.mAngleInfo[i].mName) {
+          return info.mAngles[i].ToSMILAttr(this);
+        }
+      }
+    }
 
-  // Numbers:
-  {
-    NumberAttributesInfo info = GetNumberInfo();
-    for (PRUint32 i = 0; i < info.mNumberCount; i++) {
-      // XXX this isn't valid for either of the two properties corresponding to
-      // attributes of type <number-optional-number> - see filter,
-      // feConvolveMatrix, feDiffuseLighting, feGaussianBlur, feMorphology and
-      // feTurbulence.
-      // The way to fix this is probably to handle them as 2-item number lists
-      // once we implement number list animation, and put the number list loop
-      // *above* this one at that time to catch those properties before we get
-      // here. The separate properties should then point into the list.
-      if (aName == *info.mNumberInfo[i].mName) {
-        return info.mNumbers[i].ToSMILAttr(this);
+    // viewBox:
+    if (aName == nsGkAtoms::viewBox) {
+      nsSVGViewBox *viewBox = GetViewBox();
+      return viewBox ? viewBox->ToSMILAttr(this) : nsnull;
+    }
+
+    // preserveAspectRatio:
+    if (aName == nsGkAtoms::preserveAspectRatio) {
+      nsSVGPreserveAspectRatio *preserveAspectRatio = GetPreserveAspectRatio();
+      return preserveAspectRatio ? preserveAspectRatio->ToSMILAttr(this) : nsnull;
+    }
+
+    // LengthLists:
+    {
+      LengthListAttributesInfo info = GetLengthListInfo();
+      for (PRUint32 i = 0; i < info.mLengthListCount; i++) {
+        if (aName == *info.mLengthListInfo[i].mName) {
+          NS_ABORT_IF_FALSE(i <= UCHAR_MAX, "Too many attributes");
+          return info.mLengthLists[i].ToSMILAttr(this,
+                                                 PRUint8(i),
+                                                 info.mLengthListInfo[i].mAxis,
+                                                 info.mLengthListInfo[i].mCouldZeroPadList);
+        }
       }
     }
   }
 
-  // Integers:
-  {
-    IntegerAttributesInfo info = GetIntegerInfo();
-    for (PRUint32 i = 0; i < info.mIntegerCount; i++) {
-      if (aName == *info.mIntegerInfo[i].mName) {
-        return info.mIntegers[i].ToSMILAttr(this);
-      }
-    }
-  }
-
-  // Enumerations:
-  {
-    EnumAttributesInfo info = GetEnumInfo();
-    for (PRUint32 i = 0; i < info.mEnumCount; i++) {
-      if (aName == *info.mEnumInfo[i].mName) {
-        return info.mEnums[i].ToSMILAttr(this);
-      }
-    }
-  }
-
-  // Booleans:
+  // Strings
   {
-    BooleanAttributesInfo info = GetBooleanInfo();
-    for (PRUint32 i = 0; i < info.mBooleanCount; i++) {
-      if (aName == *info.mBooleanInfo[i].mName) {
-        return info.mBooleans[i].ToSMILAttr(this);
-      }
-    }
-  }
-
-  // Angles:
-  {
-    AngleAttributesInfo info = GetAngleInfo();
-    for (PRUint32 i = 0; i < info.mAngleCount; i++) {
-      if (aName == *info.mAngleInfo[i].mName) {
-        return info.mAngles[i].ToSMILAttr(this);
-      }
-    }
-  }
-
-  // viewBox:
-  if (aName == nsGkAtoms::viewBox) {
-    nsSVGViewBox *viewBox = GetViewBox();
-    return viewBox ? viewBox->ToSMILAttr(this) : nsnull;
-  }
-
-  // preserveAspectRatio:
-  if (aName == nsGkAtoms::preserveAspectRatio) {
-    nsSVGPreserveAspectRatio *preserveAspectRatio = GetPreserveAspectRatio();
-    return preserveAspectRatio ? preserveAspectRatio->ToSMILAttr(this) : nsnull;
-  }
-
-  // LengthLists:
-  {
-    LengthListAttributesInfo info = GetLengthListInfo();
-    for (PRUint32 i = 0; i < info.mLengthListCount; i++) {
-      if (aName == *info.mLengthListInfo[i].mName) {
-        NS_ABORT_IF_FALSE(i <= UCHAR_MAX, "Too many attributes");
-        return info.mLengthLists[i].ToSMILAttr(this,
-                                               PRUint8(i),
-                                               info.mLengthListInfo[i].mAxis,
-                                               info.mLengthListInfo[i].mCouldZeroPadList);
+    StringAttributesInfo info = GetStringInfo();
+    for (PRUint32 i = 0; i < info.mStringCount; i++) {
+      if (aNamespaceID == info.mStringInfo[i].mNamespaceID &&
+          aName == *info.mStringInfo[i].mName) {
+        return info.mStrings[i].ToSMILAttr(this);
       }
     }
   }
 
   // Mapped attributes:
   if (IsAttributeMapped(aName)) {
     nsCSSProperty prop =
       nsCSSProps::LookupProperty(nsDependentAtomString(aName));
--- a/content/svg/content/src/nsSVGElement.h
+++ b/content/svg/content/src/nsSVGElement.h
@@ -150,16 +150,19 @@ public:
    */
   virtual gfxMatrix PrependLocalTransformTo(const gfxMatrix &aMatrix);
 
   // Setter for to set the current <animateMotion> transformation
   // Only visible for nsSVGGraphicElement, so it's a no-op here, and that
   // subclass has the useful implementation.
   virtual void SetAnimateMotionTransform(const gfxMatrix* aMatrix) {/*no-op*/}
 
+  PRBool IsStringAnimatable(PRUint8 aAttrEnum) {
+    return GetStringInfo().mStringInfo[aAttrEnum].mIsAnimatable;
+  }
   virtual void DidChangeLength(PRUint8 aAttrEnum, PRBool aDoSetAttr);
   virtual void DidChangeNumber(PRUint8 aAttrEnum, PRBool aDoSetAttr);
   virtual void DidChangeInteger(PRUint8 aAttrEnum, PRBool aDoSetAttr);
   virtual void DidChangeAngle(PRUint8 aAttrEnum, PRBool aDoSetAttr);
   virtual void DidChangeBoolean(PRUint8 aAttrEnum, PRBool aDoSetAttr);
   virtual void DidChangeEnum(PRUint8 aAttrEnum, PRBool aDoSetAttr);
   virtual void DidChangeViewBox(PRBool aDoSetAttr);
   virtual void DidChangePreserveAspectRatio(PRBool aDoSetAttr);
@@ -171,25 +174,26 @@ public:
   virtual void DidAnimateInteger(PRUint8 aAttrEnum);
   virtual void DidAnimateAngle(PRUint8 aAttrEnum);
   virtual void DidAnimateBoolean(PRUint8 aAttrEnum);
   virtual void DidAnimateEnum(PRUint8 aAttrEnum);
   virtual void DidAnimateViewBox();
   virtual void DidAnimatePreserveAspectRatio();
   virtual void DidAnimateLengthList(PRUint8 aAttrEnum);
   virtual void DidAnimateTransform();
+  virtual void DidAnimateString(PRUint8 aAttrEnum);
 
   void GetAnimatedLengthValues(float *aFirst, ...);
   void GetAnimatedNumberValues(float *aFirst, ...);
   void GetAnimatedIntegerValues(PRInt32 *aFirst, ...);
   void GetAnimatedLengthListValues(mozilla::SVGUserUnitList *aFirst, ...);
   mozilla::SVGAnimatedLengthList* GetAnimatedLengthList(PRUint8 aAttrEnum);
 
 #ifdef MOZ_SMIL
-  virtual nsISMILAttr* GetAnimatedAttr(nsIAtom* aName);
+  virtual nsISMILAttr* GetAnimatedAttr(PRInt32 aNamespaceID, nsIAtom* aName);
   void AnimationNeedsResample();
   void FlushAnimations();
 #endif
 
   virtual void RecompileScriptEventListeners();
 
   void GetStringBaseValue(PRUint8 aAttrEnum, nsAString& aResult) const;
   void SetStringBaseValue(PRUint8 aAttrEnum, const nsAString& aValue);
@@ -366,16 +370,17 @@ protected:
     {}
 
     void Reset(PRUint8 aAttrEnum);
   };
 
   struct StringInfo {
     nsIAtom**    mName;
     PRInt32      mNamespaceID;
+    PRPackedBool mIsAnimatable;
   };
 
   struct StringAttributesInfo {
     nsSVGString*  mStrings;
     StringInfo*   mStringInfo;
     PRUint32      mStringCount;
 
     StringAttributesInfo(nsSVGString *aStrings,
--- a/content/svg/content/src/nsSVGFilterElement.cpp
+++ b/content/svg/content/src/nsSVGFilterElement.cpp
@@ -62,17 +62,17 @@ nsSVGElement::EnumInfo nsSVGFilterElemen
   { &nsGkAtoms::primitiveUnits,
     sSVGUnitTypesMap,
     nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE
   }
 };
 
 nsSVGElement::StringInfo nsSVGFilterElement::sStringInfo[1] =
 {
-  { &nsGkAtoms::href, kNameSpaceID_XLink }
+  { &nsGkAtoms::href, kNameSpaceID_XLink, PR_TRUE }
 };
 
 NS_IMPL_NS_NEW_SVG_ELEMENT(Filter)
 
 //----------------------------------------------------------------------
 // nsISupports methods
 
 NS_IMPL_ADDREF_INHERITED(nsSVGFilterElement,nsSVGFilterElementBase)
--- a/content/svg/content/src/nsSVGFilters.cpp
+++ b/content/svg/content/src/nsSVGFilters.cpp
@@ -340,16 +340,22 @@ nsSVGFE::DidAnimatePreserveAspectRatio()
 }
 
 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,
@@ -413,18 +419,18 @@ private:
 nsSVGElement::NumberInfo nsSVGFEGaussianBlurElement::sNumberInfo[2] =
 {
   { &nsGkAtoms::stdDeviation, 0 },
   { &nsGkAtoms::stdDeviation, 0 }
 };
 
 nsSVGElement::StringInfo nsSVGFEGaussianBlurElement::sStringInfo[2] =
 {
-  { &nsGkAtoms::result, kNameSpaceID_None },
-  { &nsGkAtoms::in, kNameSpaceID_None }
+  { &nsGkAtoms::result, kNameSpaceID_None, PR_TRUE },
+  { &nsGkAtoms::in, kNameSpaceID_None, PR_TRUE }
 };
 
 NS_IMPL_NS_NEW_SVG_ELEMENT(FEGaussianBlur)
 
 //----------------------------------------------------------------------
 // nsISupports methods
 
 NS_IMPL_ADDREF_INHERITED(nsSVGFEGaussianBlurElement,nsSVGFEGaussianBlurElementBase)
@@ -893,19 +899,19 @@ nsSVGElement::EnumInfo nsSVGFEBlendEleme
   { &nsGkAtoms::mode,
     sModeMap,
     nsSVGFEBlendElement::SVG_MODE_NORMAL
   }
 };
 
 nsSVGElement::StringInfo nsSVGFEBlendElement::sStringInfo[3] =
 {
-  { &nsGkAtoms::result, kNameSpaceID_None },
-  { &nsGkAtoms::in, kNameSpaceID_None },
-  { &nsGkAtoms::in2, kNameSpaceID_None }
+  { &nsGkAtoms::result, kNameSpaceID_None, PR_TRUE },
+  { &nsGkAtoms::in, kNameSpaceID_None, PR_TRUE },
+  { &nsGkAtoms::in2, kNameSpaceID_None, PR_TRUE }
 };
 
 NS_IMPL_NS_NEW_SVG_ELEMENT(FEBlend)
 
 //----------------------------------------------------------------------
 // nsISupports methods
 
 NS_IMPL_ADDREF_INHERITED(nsSVGFEBlendElement,nsSVGFEBlendElementBase)
@@ -1098,18 +1104,18 @@ nsSVGElement::EnumInfo nsSVGFEColorMatri
   { &nsGkAtoms::type,
     sTypeMap,
     nsSVGFEColorMatrixElement::SVG_FECOLORMATRIX_TYPE_MATRIX
   }
 };
 
 nsSVGElement::StringInfo nsSVGFEColorMatrixElement::sStringInfo[2] =
 {
-  { &nsGkAtoms::result, kNameSpaceID_None },
-  { &nsGkAtoms::in, kNameSpaceID_None }
+  { &nsGkAtoms::result, kNameSpaceID_None, PR_TRUE },
+  { &nsGkAtoms::in, kNameSpaceID_None, PR_TRUE }
 };
 
 NS_IMPL_NS_NEW_SVG_ELEMENT(FEColorMatrix)
 
 //----------------------------------------------------------------------
 // nsISupports methods
 
 NS_IMPL_ADDREF_INHERITED(nsSVGFEColorMatrixElement,nsSVGFEColorMatrixElementBase)
@@ -1438,19 +1444,19 @@ nsSVGElement::EnumInfo nsSVGFECompositeE
   { &nsGkAtoms::_operator,
     sOperatorMap,
     nsIDOMSVGFECompositeElement::SVG_OPERATOR_OVER
   }
 };
 
 nsSVGElement::StringInfo nsSVGFECompositeElement::sStringInfo[3] =
 {
-  { &nsGkAtoms::result, kNameSpaceID_None },
-  { &nsGkAtoms::in, kNameSpaceID_None },
-  { &nsGkAtoms::in2, kNameSpaceID_None }
+  { &nsGkAtoms::result, kNameSpaceID_None, PR_TRUE },
+  { &nsGkAtoms::in, kNameSpaceID_None, PR_TRUE },
+  { &nsGkAtoms::in2, kNameSpaceID_None, PR_TRUE }
 };
 
 NS_IMPL_NS_NEW_SVG_ELEMENT(FEComposite)
 
 //----------------------------------------------------------------------
 // nsISupports methods
 
 NS_IMPL_ADDREF_INHERITED(nsSVGFECompositeElement,nsSVGFECompositeElementBase)
@@ -1701,18 +1707,18 @@ protected:
 
   enum { RESULT, IN1 };
   nsSVGString mStringAttributes[2];
   static StringInfo sStringInfo[2];
 };
 
 nsSVGElement::StringInfo nsSVGFEComponentTransferElement::sStringInfo[2] =
 {
-  { &nsGkAtoms::result, kNameSpaceID_None },
-  { &nsGkAtoms::in, kNameSpaceID_None }
+  { &nsGkAtoms::result, kNameSpaceID_None, PR_TRUE },
+  { &nsGkAtoms::in, kNameSpaceID_None, PR_TRUE }
 };
 
 NS_IMPL_NS_NEW_SVG_ELEMENT(FEComponentTransfer)
 
 //----------------------------------------------------------------------
 // nsISupports methods
 
 NS_IMPL_ADDREF_INHERITED(nsSVGFEComponentTransferElement,nsSVGFEComponentTransferElementBase)
@@ -2342,17 +2348,17 @@ protected:
 
   enum { IN1 };
   nsSVGString mStringAttributes[1];
   static StringInfo sStringInfo[1];
 };
 
 nsSVGElement::StringInfo nsSVGFEMergeElement::sStringInfo[1] =
 {
-  { &nsGkAtoms::result, kNameSpaceID_None }
+  { &nsGkAtoms::result, kNameSpaceID_None, PR_TRUE }
 };
 
 NS_IMPL_NS_NEW_SVG_ELEMENT(FEMerge)
 
 //----------------------------------------------------------------------
 // nsISupports methods
 
 NS_IMPL_ADDREF_INHERITED(nsSVGFEMergeElement,nsSVGFEMergeElementBase)
@@ -2414,17 +2420,17 @@ nsSVGFEMergeElement::GetStringInfo()
 }
 
 //---------------------Merge Node------------------------
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsSVGFEMergeNodeElement, NS_SVG_FE_MERGE_NODE_CID)
 
 nsSVGElement::StringInfo nsSVGFEMergeNodeElement::sStringInfo[1] =
 {
-  { &nsGkAtoms::in, kNameSpaceID_None }
+  { &nsGkAtoms::in, kNameSpaceID_None, PR_TRUE }
 };
 
 NS_IMPL_NS_NEW_SVG_ELEMENT(FEMergeNode)
 
 //----------------------------------------------------------------------
 // nsISupports methods
 
 NS_IMPL_ADDREF_INHERITED(nsSVGFEMergeNodeElement,nsSVGFEMergeNodeElementBase)
@@ -2529,18 +2535,18 @@ protected:
 nsSVGElement::NumberInfo nsSVGFEOffsetElement::sNumberInfo[2] =
 {
   { &nsGkAtoms::dx, 0 },
   { &nsGkAtoms::dy, 0 }
 };
 
 nsSVGElement::StringInfo nsSVGFEOffsetElement::sStringInfo[2] =
 {
-  { &nsGkAtoms::result, kNameSpaceID_None },
-  { &nsGkAtoms::in, kNameSpaceID_None }
+  { &nsGkAtoms::result, kNameSpaceID_None, PR_TRUE },
+  { &nsGkAtoms::in, kNameSpaceID_None, PR_TRUE }
 };
 
 NS_IMPL_NS_NEW_SVG_ELEMENT(FEOffset)
 
 //----------------------------------------------------------------------
 // nsISupports methods
 
 NS_IMPL_ADDREF_INHERITED(nsSVGFEOffsetElement,nsSVGFEOffsetElementBase)
@@ -2721,17 +2727,17 @@ protected:
 
   enum { RESULT };
   nsSVGString mStringAttributes[1];
   static StringInfo sStringInfo[1];
 };
  
 nsSVGElement::StringInfo nsSVGFEFloodElement::sStringInfo[1] =
 {
-  { &nsGkAtoms::result, kNameSpaceID_None }
+  { &nsGkAtoms::result, kNameSpaceID_None, PR_TRUE }
 };
 
 NS_IMPL_NS_NEW_SVG_ELEMENT(FEFlood)
 
 //----------------------------------------------------------------------
 // nsISupports methods
 
 NS_IMPL_ADDREF_INHERITED(nsSVGFEFloodElement,nsSVGFEFloodElementBase)
@@ -2862,18 +2868,18 @@ protected:
   
   enum { RESULT, IN1 };
   nsSVGString mStringAttributes[2];
   static StringInfo sStringInfo[2];
 };
 
 nsSVGElement::StringInfo nsSVGFETileElement::sStringInfo[2] =
 {
-  { &nsGkAtoms::result, kNameSpaceID_None },
-  { &nsGkAtoms::in, kNameSpaceID_None }
+  { &nsGkAtoms::result, kNameSpaceID_None, PR_TRUE },
+  { &nsGkAtoms::in, kNameSpaceID_None, PR_TRUE }
 };
 
 NS_IMPL_NS_NEW_SVG_ELEMENT(FETile)
 
 //----------------------------------------------------------------------
 // nsISupports methods
 
 NS_IMPL_ADDREF_INHERITED(nsSVGFETileElement,nsSVGFETileElementBase)
@@ -3170,17 +3176,17 @@ nsSVGElement::EnumInfo nsSVGFETurbulence
   { &nsGkAtoms::stitchTiles,
     sStitchTilesMap,
     nsIDOMSVGFETurbulenceElement::SVG_STITCHTYPE_NOSTITCH
   }
 };
 
 nsSVGElement::StringInfo nsSVGFETurbulenceElement::sStringInfo[1] =
 {
-  { &nsGkAtoms::result, kNameSpaceID_None }
+  { &nsGkAtoms::result, kNameSpaceID_None, PR_TRUE }
 };
 
 NS_IMPL_NS_NEW_SVG_ELEMENT(FETurbulence)
 
 //----------------------------------------------------------------------
 // nsISupports methods
 
 NS_IMPL_ADDREF_INHERITED(nsSVGFETurbulenceElement,nsSVGFETurbulenceElementBase)
@@ -3608,18 +3614,18 @@ nsSVGElement::EnumInfo nsSVGFEMorphology
   { &nsGkAtoms::_operator,
     sOperatorMap,
     nsSVGFEMorphologyElement::SVG_OPERATOR_ERODE
   }
 };
 
 nsSVGElement::StringInfo nsSVGFEMorphologyElement::sStringInfo[2] =
 {
-  { &nsGkAtoms::result, kNameSpaceID_None },
-  { &nsGkAtoms::in, kNameSpaceID_None }
+  { &nsGkAtoms::result, kNameSpaceID_None, PR_TRUE },
+  { &nsGkAtoms::in, kNameSpaceID_None, PR_TRUE }
 };
 
 NS_IMPL_NS_NEW_SVG_ELEMENT(FEMorphology)
 
 //----------------------------------------------------------------------
 // nsISupports methods
 
 NS_IMPL_ADDREF_INHERITED(nsSVGFEMorphologyElement,nsSVGFEMorphologyElementBase)
@@ -3965,18 +3971,18 @@ nsSVGElement::EnumInfo nsSVGFEConvolveMa
   { &nsGkAtoms::edgeMode,
     sEdgeModeMap,
     nsSVGFEConvolveMatrixElement::SVG_EDGEMODE_DUPLICATE
   }
 };
 
 nsSVGElement::StringInfo nsSVGFEConvolveMatrixElement::sStringInfo[2] =
 {
-  { &nsGkAtoms::result, kNameSpaceID_None },
-  { &nsGkAtoms::in, kNameSpaceID_None }
+  { &nsGkAtoms::result, kNameSpaceID_None, PR_TRUE },
+  { &nsGkAtoms::in, kNameSpaceID_None, PR_TRUE }
 };
 
 NS_IMPL_NS_NEW_SVG_ELEMENT(FEConvolveMatrix)
 
 //----------------------------------------------------------------------
 // nsISupports methods
 
 NS_IMPL_ADDREF_INHERITED(nsSVGFEConvolveMatrixElement,nsSVGFEConvolveMatrixElementBase)
@@ -4704,18 +4710,18 @@ nsSVGElement::NumberInfo nsSVGFELighting
   { &nsGkAtoms::specularConstant, 1 },
   { &nsGkAtoms::specularExponent, 1 },
   { &nsGkAtoms::kernelUnitLength, 0 },
   { &nsGkAtoms::kernelUnitLength, 0 }
 };
 
 nsSVGElement::StringInfo nsSVGFELightingElement::sStringInfo[2] =
 {
-  { &nsGkAtoms::result, kNameSpaceID_None },
-  { &nsGkAtoms::in, kNameSpaceID_None }
+  { &nsGkAtoms::result, kNameSpaceID_None, PR_TRUE },
+  { &nsGkAtoms::in, kNameSpaceID_None, PR_TRUE }
 };
 
 //----------------------------------------------------------------------
 // nsISupports methods
 
 NS_IMPL_ADDREF_INHERITED(nsSVGFELightingElement,nsSVGFELightingElementBase)
 NS_IMPL_RELEASE_INHERITED(nsSVGFELightingElement,nsSVGFELightingElementBase)
 
@@ -5353,28 +5359,29 @@ private:
   nsresult LoadSVGImage(PRBool aForce, PRBool aNotify);
 
 protected:
   virtual PRBool OperatesOnSRGB(nsSVGFilterInstance*,
                                 PRInt32, Image*) { return PR_TRUE; }
 
   virtual nsSVGPreserveAspectRatio *GetPreserveAspectRatio();
   virtual StringAttributesInfo GetStringInfo();
+  virtual void DidAnimateString(PRUint8 aAttrEnum);
 
   enum { RESULT, HREF };
   nsSVGString mStringAttributes[2];
   static StringInfo sStringInfo[2];
 
   nsSVGPreserveAspectRatio mPreserveAspectRatio;
 };
 
 nsSVGElement::StringInfo nsSVGFEImageElement::sStringInfo[2] =
 {
-  { &nsGkAtoms::result, kNameSpaceID_None },
-  { &nsGkAtoms::href, kNameSpaceID_XLink }
+  { &nsGkAtoms::result, kNameSpaceID_None, PR_TRUE },
+  { &nsGkAtoms::href, kNameSpaceID_XLink, PR_TRUE }
 };
 
 NS_IMPL_NS_NEW_SVG_ELEMENT(FEImage)
 
 //----------------------------------------------------------------------
 // nsISupports methods
 
 NS_IMPL_ADDREF_INHERITED(nsSVGFEImageElement,nsSVGFEImageElementBase)
@@ -5586,16 +5593,27 @@ nsSVGFEImageElement::GetPreserveAspectRa
 
 nsSVGElement::StringAttributesInfo
 nsSVGFEImageElement::GetStringInfo()
 {
   return StringAttributesInfo(mStringAttributes, sStringInfo,
                               NS_ARRAY_LENGTH(sStringInfo));
 }
 
+void
+nsSVGFEImageElement::DidAnimateString(PRUint8 aAttrEnum)
+{
+  if (aAttrEnum == HREF) {
+    LoadSVGImage(PR_TRUE, PR_FALSE);
+    return;
+  }
+
+  nsSVGFEImageElementBase::DidAnimateString(aAttrEnum);
+}
+
 //----------------------------------------------------------------------
 // imgIDecoderObserver methods
 
 NS_IMETHODIMP
 nsSVGFEImageElement::OnStopDecode(imgIRequest *aRequest,
                                   nsresult status,
                                   const PRUnichar *statusArg)
 {
@@ -5740,19 +5758,19 @@ nsSVGElement::EnumInfo nsSVGFEDisplaceme
   { &nsGkAtoms::yChannelSelector,
     sChannelMap,
     nsSVGFEDisplacementMapElement::SVG_CHANNEL_A
   }
 };
 
 nsSVGElement::StringInfo nsSVGFEDisplacementMapElement::sStringInfo[3] =
 {
-  { &nsGkAtoms::result, kNameSpaceID_None },
-  { &nsGkAtoms::in, kNameSpaceID_None },
-  { &nsGkAtoms::in2, kNameSpaceID_None }
+  { &nsGkAtoms::result, kNameSpaceID_None, PR_TRUE },
+  { &nsGkAtoms::in, kNameSpaceID_None, PR_TRUE },
+  { &nsGkAtoms::in2, kNameSpaceID_None, PR_TRUE }
 };
 
 NS_IMPL_NS_NEW_SVG_ELEMENT(FEDisplacementMap)
 
 //----------------------------------------------------------------------
 // nsISupports methods
 
 NS_IMPL_ADDREF_INHERITED(nsSVGFEDisplacementMapElement,nsSVGFEDisplacementMapElementBase)
--- a/content/svg/content/src/nsSVGFilters.h
+++ b/content/svg/content/src/nsSVGFilters.h
@@ -217,16 +217,17 @@ protected:
   // nsSVGElement specializations:
   virtual LengthAttributesInfo GetLengthInfo();
   virtual void DidAnimateLength(PRUint8 aAttrEnum);
   virtual void DidAnimateNumber(PRUint8 aAttrEnum);
   virtual void DidAnimateInteger(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];
 };
 
 #endif
--- a/content/svg/content/src/nsSVGGradientElement.cpp
+++ b/content/svg/content/src/nsSVGGradientElement.cpp
@@ -66,17 +66,17 @@ nsSVGElement::EnumInfo nsSVGGradientElem
   { &nsGkAtoms::spreadMethod,
     sSpreadMethodMap,
     nsIDOMSVGGradientElement::SVG_SPREADMETHOD_PAD
   }
 };
 
 nsSVGElement::StringInfo nsSVGGradientElement::sStringInfo[1] =
 {
-  { &nsGkAtoms::href, kNameSpaceID_XLink }
+  { &nsGkAtoms::href, kNameSpaceID_XLink, PR_TRUE }
 };
 
 //----------------------------------------------------------------------
 // nsISupports methods
 
 NS_IMPL_ADDREF_INHERITED(nsSVGGradientElement,nsSVGGradientElementBase)
 NS_IMPL_RELEASE_INHERITED(nsSVGGradientElement,nsSVGGradientElementBase)
 
--- a/content/svg/content/src/nsSVGImageElement.cpp
+++ b/content/svg/content/src/nsSVGImageElement.cpp
@@ -49,17 +49,17 @@ nsSVGElement::LengthInfo nsSVGImageEleme
   { &nsGkAtoms::x, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, nsSVGUtils::X },
   { &nsGkAtoms::y, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, nsSVGUtils::Y },
   { &nsGkAtoms::width, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, nsSVGUtils::X },
   { &nsGkAtoms::height, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, nsSVGUtils::Y },
 };
 
 nsSVGElement::StringInfo nsSVGImageElement::sStringInfo[1] =
 {
-  { &nsGkAtoms::href, kNameSpaceID_XLink }
+  { &nsGkAtoms::href, kNameSpaceID_XLink, PR_TRUE }
 };
 
 NS_IMPL_NS_NEW_SVG_ELEMENT(Image)
 
 //----------------------------------------------------------------------
 // nsISupports methods
 
 NS_IMPL_ADDREF_INHERITED(nsSVGImageElement,nsSVGImageElementBase)
@@ -266,16 +266,27 @@ nsSVGImageElement::GetPreserveAspectRati
 
 nsSVGElement::StringAttributesInfo
 nsSVGImageElement::GetStringInfo()
 {
   return StringAttributesInfo(mStringAttributes, sStringInfo,
                               NS_ARRAY_LENGTH(sStringInfo));
 }
 
+void
+nsSVGImageElement::DidAnimateString(PRUint8 aAttrEnum)
+{
+  if (aAttrEnum == HREF) {
+    LoadSVGImage(PR_TRUE, PR_FALSE);
+    return;
+  }
+
+  nsSVGImageElementBase::DidAnimateString(aAttrEnum);
+}
+
 nsresult
 nsSVGImageElement::CopyInnerTo(nsGenericElement* aDest) const
 {
   if (aDest->GetOwnerDoc()->IsStaticDocument()) {
     CreateStaticImageClone(static_cast<nsSVGImageElement*>(aDest));
   }
   return nsSVGImageElementBase::CopyInnerTo(aDest);
 }
--- a/content/svg/content/src/nsSVGImageElement.h
+++ b/content/svg/content/src/nsSVGImageElement.h
@@ -96,16 +96,17 @@ public:
 
   virtual nsXPCClassInfo* GetClassInfo();
 protected:
   nsresult LoadSVGImage(PRBool aForce, PRBool aNotify);
 
   virtual LengthAttributesInfo GetLengthInfo();
   virtual nsSVGPreserveAspectRatio *GetPreserveAspectRatio();
   virtual StringAttributesInfo GetStringInfo();
+  virtual void DidAnimateString(PRUint8 aAttrEnum);
 
   enum { X, Y, WIDTH, HEIGHT };
   nsSVGLength2 mLengthAttributes[4];
   static LengthInfo sLengthInfo[4];
 
   nsSVGPreserveAspectRatio mPreserveAspectRatio;
 
   enum { HREF };
--- a/content/svg/content/src/nsSVGMpathElement.cpp
+++ b/content/svg/content/src/nsSVGMpathElement.cpp
@@ -40,17 +40,17 @@
 #include "nsDebug.h"
 #include "nsSVGPathElement.h"
 #include "nsSVGAnimateMotionElement.h"
 
 using namespace mozilla::dom;
 
 nsSVGElement::StringInfo nsSVGMpathElement::sStringInfo[1] =
 {
-  { &nsGkAtoms::href, kNameSpaceID_XLink },
+  { &nsGkAtoms::href, kNameSpaceID_XLink, PR_FALSE }
 };
 
 NS_IMPL_NS_NEW_SVG_ELEMENT(Mpath)
 
 // Cycle collection magic -- based on nsSVGUseElement
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsSVGMpathElement)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsSVGMpathElement,
                                                 nsSVGMpathElementBase)
--- a/content/svg/content/src/nsSVGPatternElement.cpp
+++ b/content/svg/content/src/nsSVGPatternElement.cpp
@@ -63,17 +63,17 @@ nsSVGElement::EnumInfo nsSVGPatternEleme
   { &nsGkAtoms::patternContentUnits,
     sSVGUnitTypesMap,
     nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE
   }
 };
 
 nsSVGElement::StringInfo nsSVGPatternElement::sStringInfo[1] =
 {
-  { &nsGkAtoms::href, kNameSpaceID_XLink }
+  { &nsGkAtoms::href, kNameSpaceID_XLink, PR_TRUE }
 };
 
 NS_IMPL_NS_NEW_SVG_ELEMENT(Pattern)
 
 //----------------------------------------------------------------------
 // nsISupports methods
 
 NS_IMPL_ADDREF_INHERITED(nsSVGPatternElement,nsSVGPatternElementBase)
--- a/content/svg/content/src/nsSVGScriptElement.cpp
+++ b/content/svg/content/src/nsSVGScriptElement.cpp
@@ -103,17 +103,17 @@ protected:
 
   enum { HREF };
   nsSVGString mStringAttributes[1];
   static StringInfo sStringInfo[1];
 };
 
 nsSVGElement::StringInfo nsSVGScriptElement::sStringInfo[1] =
 {
-  { &nsGkAtoms::href, kNameSpaceID_XLink }
+  { &nsGkAtoms::href, kNameSpaceID_XLink, PR_FALSE }
 };
 
 NS_IMPL_NS_NEW_SVG_ELEMENT_CHECK_PARSER(Script)
 
 //----------------------------------------------------------------------
 // nsISupports methods
 
 NS_IMPL_ADDREF_INHERITED(nsSVGScriptElement,nsSVGScriptElementBase)
--- a/content/svg/content/src/nsSVGString.cpp
+++ b/content/svg/content/src/nsSVGString.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 "nsSVGString.h"
+#ifdef MOZ_SMIL
+#include "nsSMILValue.h"
+#include "SMILStringType.h"
+#endif // MOZ_SMIL
+
+using namespace mozilla;
 
 NS_SVG_VAL_IMPL_CYCLE_COLLECTION(nsSVGString::DOMAnimatedString, mSVGElement)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsSVGString::DOMAnimatedString)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsSVGString::DOMAnimatedString)
 
 DOMCI_DATA(SVGAnimatedString, nsSVGString::DOMAnimatedString)
 
@@ -53,37 +59,104 @@ NS_INTERFACE_MAP_END
 
 void
 nsSVGString::SetBaseValue(const nsAString& aValue,
                           nsSVGElement *aSVGElement,
                           PRBool aDoSetAttr)
 {
   NS_ASSERTION(aSVGElement, "Null element passed to SetBaseValue");
 
-  mAnimVal = nsnull;
-
   if (aDoSetAttr) {
     aSVGElement->SetStringBaseValue(mAttrEnum, aValue);
   }
+#ifdef MOZ_SMIL
+  if (mAnimVal) {
+    aSVGElement->AnimationNeedsResample();
+  }
+#endif
 
   aSVGElement->DidChangeString(mAttrEnum);
 }
 
 void
-nsSVGString::GetAnimValue(nsAString& aResult, const nsSVGElement* aSVGElement) const
+nsSVGString::GetAnimValue(nsAString& aResult, const nsSVGElement *aSVGElement) const
 {
-  if (mAnimVal)
+  if (mAnimVal) {
     aResult = *mAnimVal;
+    return;
+  }
 
   aSVGElement->GetStringBaseValue(mAttrEnum, aResult);
 }
 
+void
+nsSVGString::SetAnimValue(const nsAString& aValue, nsSVGElement *aSVGElement)
+{
+  if (aSVGElement->IsStringAnimatable(mAttrEnum)) {
+    if (!mAnimVal) {
+      mAnimVal = new nsString();
+    }
+    *mAnimVal = aValue;
+    aSVGElement->DidAnimateString(mAttrEnum);
+  }
+}
+
 nsresult
 nsSVGString::ToDOMAnimatedString(nsIDOMSVGAnimatedString **aResult,
                                  nsSVGElement *aSVGElement)
 {
   *aResult = new DOMAnimatedString(this, aSVGElement);
   if (!*aResult)
     return NS_ERROR_OUT_OF_MEMORY;
 
   NS_ADDREF(*aResult);
   return NS_OK;
 }
+
+#ifdef MOZ_SMIL
+nsISMILAttr*
+nsSVGString::ToSMILAttr(nsSVGElement *aSVGElement)
+{
+  return new SMILString(this, aSVGElement);
+}
+
+nsresult
+nsSVGString::SMILString::ValueFromString(const nsAString& aStr,
+                                         const nsISMILAnimationElement* /*aSrcElement*/,
+                                         nsSMILValue& aValue,
+                                         PRBool& aPreventCachingOfSandwich) const
+{
+  nsSMILValue val(&SMILStringType::sSingleton);
+
+  *static_cast<nsAString*>(val.mU.mPtr) = aStr;
+  aValue.Swap(val);
+  aPreventCachingOfSandwich = PR_FALSE;
+  return NS_OK;
+}
+
+nsSMILValue
+nsSVGString::SMILString::GetBaseValue() const
+{
+  nsSMILValue val(&SMILStringType::sSingleton);
+  mSVGElement->GetStringBaseValue(mVal->mAttrEnum, *static_cast<nsAString*>(val.mU.mPtr));
+  return val;
+}
+
+void
+nsSVGString::SMILString::ClearAnimValue()
+{
+  if (mVal->mAnimVal) {
+    mVal->mAnimVal = nsnull;
+    mSVGElement->DidAnimateString(mVal->mAttrEnum);
+  }
+}
+
+nsresult
+nsSVGString::SMILString::SetAnimValue(const nsSMILValue& aValue)
+{
+  NS_ASSERTION(aValue.mType == &SMILStringType::sSingleton,
+               "Unexpected type to assign animated value");
+  if (aValue.mType == &SMILStringType::sSingleton) {
+    mVal->SetAnimValue(*static_cast<nsAString*>(aValue.mU.mPtr), mSVGElement);
+  }
+  return NS_OK;
+}
+#endif // MOZ_SMIL
--- a/content/svg/content/src/nsSVGString.h
+++ b/content/svg/content/src/nsSVGString.h
@@ -48,44 +48,77 @@ public:
   void Init(PRUint8 aAttrEnum) {
     mAnimVal = nsnull;
     mAttrEnum = aAttrEnum;
   }
 
   void SetBaseValue(const nsAString& aValue,
                     nsSVGElement *aSVGElement,
                     PRBool aDoSetAttr);
-  void GetBaseValue(nsAString& aValue, nsSVGElement* aSVGElement) const
+  void GetBaseValue(nsAString& aValue, nsSVGElement *aSVGElement) const
     { aSVGElement->GetStringBaseValue(mAttrEnum, aValue); }
 
-  void GetAnimValue(nsAString& aValue, const nsSVGElement* aSVGElement) const;
+  void SetAnimValue(const nsAString& aValue, nsSVGElement *aSVGElement);
+  void GetAnimValue(nsAString& aValue, const nsSVGElement *aSVGElement) const;
 
   nsresult ToDOMAnimatedString(nsIDOMSVGAnimatedString **aResult,
-                               nsSVGElement* aSVGElement);
+                               nsSVGElement *aSVGElement);
+#ifdef MOZ_SMIL
+  // Returns a new nsISMILAttr object that the caller must delete
+  nsISMILAttr* ToSMILAttr(nsSVGElement *aSVGElement);
+#endif // MOZ_SMIL
 
 private:
 
   nsAutoPtr<nsString> mAnimVal;
   PRUint8 mAttrEnum; // element specified tracking for attribute
 
 public:
   struct DOMAnimatedString : public nsIDOMSVGAnimatedString
   {
     NS_DECL_CYCLE_COLLECTING_ISUPPORTS
     NS_DECL_CYCLE_COLLECTION_CLASS(DOMAnimatedString)
 
-    DOMAnimatedString(nsSVGString* aVal, nsSVGElement *aSVGElement)
+    DOMAnimatedString(nsSVGString *aVal, nsSVGElement *aSVGElement)
       : mVal(aVal), mSVGElement(aSVGElement) {}
 
     nsSVGString* mVal; // kept alive because it belongs to content
     nsRefPtr<nsSVGElement> mSVGElement;
 
     NS_IMETHOD GetBaseVal(nsAString & aResult)
       { mVal->GetBaseValue(aResult, mSVGElement); return NS_OK; }
     NS_IMETHOD SetBaseVal(const nsAString & aValue)
       { mVal->SetBaseValue(aValue, mSVGElement, PR_TRUE); return NS_OK; }
 
     NS_IMETHOD GetAnimVal(nsAString & aResult)
-      { mVal->GetAnimValue(aResult, mSVGElement); return NS_OK; }
+    { 
+#ifdef MOZ_SMIL
+      mSVGElement->FlushAnimations();
+#endif
+      mVal->GetAnimValue(aResult, mSVGElement); return NS_OK;
+    }
 
   };
+#ifdef MOZ_SMIL
+  struct SMILString : public nsISMILAttr
+  {
+  public:
+    SMILString(nsSVGString *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.
+    nsSVGString* mVal;
+    nsSVGElement* mSVGElement;
+
+    // nsISMILAttr methods
+    virtual nsresult ValueFromString(const nsAString& aStr,
+                                     const nsISMILAnimationElement *aSrcElement,
+                                     nsSMILValue& aValue,
+                                     PRBool& aPreventCachingOfSandwich) const;
+    virtual nsSMILValue GetBaseValue() const;
+    virtual void ClearAnimValue();
+    virtual nsresult SetAnimValue(const nsSMILValue& aValue);
+  };
+#endif // MOZ_SMIL
 };
 #endif //__NS_SVGSTRING_H__
--- a/content/svg/content/src/nsSVGTextPathElement.cpp
+++ b/content/svg/content/src/nsSVGTextPathElement.cpp
@@ -69,17 +69,17 @@ nsSVGElement::EnumInfo nsSVGTextPathElem
   { &nsGkAtoms::spacing,
     sSpacingMap,
     nsIDOMSVGTextPathElement::TEXTPATH_SPACINGTYPE_EXACT
   }
 };
 
 nsSVGElement::StringInfo nsSVGTextPathElement::sStringInfo[1] =
 {
-  { &nsGkAtoms::href, kNameSpaceID_XLink }
+  { &nsGkAtoms::href, kNameSpaceID_XLink, PR_TRUE }
 };
 
 NS_IMPL_NS_NEW_SVG_ELEMENT(TextPath)
 
 //----------------------------------------------------------------------
 // nsISupports methods
 
 NS_IMPL_ADDREF_INHERITED(nsSVGTextPathElement,nsSVGTextPathElementBase)
--- a/content/svg/content/src/nsSVGUseElement.cpp
+++ b/content/svg/content/src/nsSVGUseElement.cpp
@@ -54,17 +54,17 @@ nsSVGElement::LengthInfo nsSVGUseElement
   { &nsGkAtoms::x, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, nsSVGUtils::X },
   { &nsGkAtoms::y, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, nsSVGUtils::Y },
   { &nsGkAtoms::width, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, nsSVGUtils::X },
   { &nsGkAtoms::height, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, nsSVGUtils::Y },
 };
 
 nsSVGElement::StringInfo nsSVGUseElement::sStringInfo[1] =
 {
-  { &nsGkAtoms::href, kNameSpaceID_XLink }
+  { &nsGkAtoms::href, kNameSpaceID_XLink, PR_TRUE }
 };
 
 NS_IMPL_NS_NEW_SVG_ELEMENT(Use)
 
 //----------------------------------------------------------------------
 // nsISupports methods
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsSVGUseElement)
@@ -511,16 +511,30 @@ nsSVGUseElement::DidChangeString(PRUint8
   if (aAttrEnum == HREF) {
     // we're changing our nature, clear out the clone information
     mOriginal = nsnull;
     UnlinkSource();
     TriggerReclone();
   }
 }
 
+void
+nsSVGUseElement::DidAnimateString(PRUint8 aAttrEnum)
+{
+  if (aAttrEnum == HREF) {
+    // we're changing our nature, clear out the clone information
+    mOriginal = nsnull;
+    UnlinkSource();
+    TriggerReclone();
+    return;
+  }
+
+  nsSVGUseElementBase::DidAnimateString(aAttrEnum);
+}
+
 nsSVGElement::StringAttributesInfo
 nsSVGUseElement::GetStringInfo()
 {
   return StringAttributesInfo(mStringAttributes, sStringInfo,
                               NS_ARRAY_LENGTH(sStringInfo));
 }
 
 //----------------------------------------------------------------------
--- a/content/svg/content/src/nsSVGUseElement.h
+++ b/content/svg/content/src/nsSVGUseElement.h
@@ -99,16 +99,17 @@ public:
   nsIContent* CreateAnonymousContent();
   nsIContent* GetAnonymousContent() const { return mClone; }
   void DestroyAnonymousContent();
 
   // nsSVGElement specializations:
   virtual gfxMatrix PrependLocalTransformTo(const gfxMatrix &aMatrix);
   virtual void DidChangeLength(PRUint8 aAttrEnum, PRBool aDoSetAttr);
   virtual void DidChangeString(PRUint8 aAttrEnum);
+  virtual void DidAnimateString(PRUint8 aAttrEnum);
 
   // nsIContent interface
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
   NS_IMETHOD_(PRBool) IsAttributeMapped(const nsIAtom* aAttribute) const;
 
   virtual nsXPCClassInfo* GetClassInfo();
 protected:
   class SourceReference : public nsReferencedElement {