Bug 216462: Add support for basic SVG animation (SMIL). Disabled in builds by default. r+sr=roc
authorBrian Birtles <birtles@gmail.com>
Wed, 14 Jan 2009 20:38:07 -0800
changeset 23697 ed15cc897a16
parent 23696 69765da46ccf
child 23698 f38ff6f08594
push id4688
push userdholbert@mozilla.com
push date2009-01-15 04:40 +0000
treeherdermozilla-central@ed15cc897a16 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs216462
milestone1.9.2a1pre
Bug 216462: Add support for basic SVG animation (SMIL). Disabled in builds by default. r+sr=roc
browser/installer/unix/packages-static
browser/installer/windows/packages-static
config/autoconf.mk.in
configure.in
content/Makefile.in
content/base/public/nsContentCreatorFunctions.h
content/base/public/nsIContent.h
content/base/public/nsIDocument.h
content/base/src/nsDocument.cpp
content/base/src/nsDocument.h
content/base/src/nsGenericDOMDataNode.h
content/base/src/nsGenericElement.cpp
content/base/src/nsGenericElement.h
content/base/src/nsGkAtomList.h
content/base/src/nsNameSpaceManager.cpp
content/smil/Makefile.in
content/smil/nsISMILAnimationElement.h
content/smil/nsISMILAttr.h
content/smil/nsISMILType.h
content/smil/nsSMILAnimationController.cpp
content/smil/nsSMILAnimationController.h
content/smil/nsSMILAnimationFunction.cpp
content/smil/nsSMILAnimationFunction.h
content/smil/nsSMILCompositor.cpp
content/smil/nsSMILCompositor.h
content/smil/nsSMILCompositorTable.h
content/smil/nsSMILFloatType.cpp
content/smil/nsSMILFloatType.h
content/smil/nsSMILInstanceTime.cpp
content/smil/nsSMILInstanceTime.h
content/smil/nsSMILInterval.h
content/smil/nsSMILKeySpline.cpp
content/smil/nsSMILKeySpline.h
content/smil/nsSMILNullType.cpp
content/smil/nsSMILNullType.h
content/smil/nsSMILParserUtils.cpp
content/smil/nsSMILParserUtils.h
content/smil/nsSMILRepeatCount.cpp
content/smil/nsSMILRepeatCount.h
content/smil/nsSMILSetAnimationFunction.cpp
content/smil/nsSMILSetAnimationFunction.h
content/smil/nsSMILTimeContainer.cpp
content/smil/nsSMILTimeContainer.h
content/smil/nsSMILTimeValue.cpp
content/smil/nsSMILTimeValue.h
content/smil/nsSMILTimeValueSpec.cpp
content/smil/nsSMILTimeValueSpec.h
content/smil/nsSMILTimedElement.cpp
content/smil/nsSMILTimedElement.h
content/smil/nsSMILTypes.h
content/smil/nsSMILValue.cpp
content/smil/nsSMILValue.h
content/smil/test/Makefile.in
content/smil/test/test_smilRestart.xhtml
content/svg/content/src/Makefile.in
content/svg/content/src/nsSVGAnimateElement.cpp
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/nsSVGElementFactory.cpp
content/svg/content/src/nsSVGLength2.cpp
content/svg/content/src/nsSVGLength2.h
content/svg/content/src/nsSVGSVGElement.cpp
content/svg/content/src/nsSVGSVGElement.h
content/svg/content/src/nsSVGSetElement.cpp
content/svg/content/src/nsSVGUseElement.cpp
content/svg/content/src/nsSVGUseElement.h
content/xml/document/src/nsXMLContentSink.cpp
dom/public/idl/Makefile.in
dom/public/idl/smil/Makefile.in
dom/public/idl/smil/nsIDOMElementTimeControl.idl
dom/public/idl/svg/Makefile.in
dom/public/idl/svg/nsIDOMSVGAnimateElement.idl
dom/public/idl/svg/nsIDOMSVGAnimationElement.idl
dom/public/idl/svg/nsIDOMSVGSetElement.idl
dom/public/nsDOMClassInfoID.h
dom/src/base/nsDOMClassInfo.cpp
layout/base/nsPresContext.cpp
layout/base/nsPresContext.h
layout/build/Makefile.in
layout/reftests/svg/reftest.list
layout/reftests/svg/smil/anim-discrete-replace-sum-1.svg
layout/reftests/svg/smil/anim-discrete-sum-none-1.svg
layout/reftests/svg/smil/anim-discrete-sum-sum-1.svg
layout/reftests/svg/smil/anim-discrete-values-1.svg
layout/reftests/svg/smil/anim-fillcolor-1.svg
layout/reftests/svg/smil/anim-fillopacity-1.svg
layout/reftests/svg/smil/anim-height-done-1a.svg
layout/reftests/svg/smil/anim-height-done-1b.svg
layout/reftests/svg/smil/anim-height-interp-1-ref.svg
layout/reftests/svg/smil/anim-height-interp-1.svg
layout/reftests/svg/smil/anim-height-interp-2-ref.svg
layout/reftests/svg/smil/anim-height-interp-2.svg
layout/reftests/svg/smil/anim-height-interp-3-ref.svg
layout/reftests/svg/smil/anim-height-interp-3.svg
layout/reftests/svg/smil/anim-height-interp-4-ref.svg
layout/reftests/svg/smil/anim-height-interp-4.svg
layout/reftests/svg/smil/anim-height-interp-5-ref.svg
layout/reftests/svg/smil/anim-height-interp-5.svg
layout/reftests/svg/smil/anim-height-interp-6-ref.svg
layout/reftests/svg/smil/anim-height-interp-6.svg
layout/reftests/svg/smil/anim-ref-text.svg
layout/reftests/svg/smil/anim-standard-ref.svg
layout/reftests/svg/smil/anim-strokecolor-1.svg
layout/reftests/svg/smil/anim-strokewidth-1.svg
layout/reftests/svg/smil/anim-targethref-1.svg
layout/reftests/svg/smil/anim-width-done-1a.svg
layout/reftests/svg/smil/anim-width-done-1b.svg
layout/reftests/svg/smil/anim-x-done-1a.svg
layout/reftests/svg/smil/anim-x-done-1b.svg
layout/reftests/svg/smil/anim-x-interp-1-ref.svg
layout/reftests/svg/smil/anim-x-interp-1.svg
layout/reftests/svg/smil/anim-x-interp-2-ref.svg
layout/reftests/svg/smil/anim-x-interp-2.svg
layout/reftests/svg/smil/anim-x-interp-3-ref.svg
layout/reftests/svg/smil/anim-x-interp-3.svg
layout/reftests/svg/smil/anim-x-interp-4-ref.svg
layout/reftests/svg/smil/anim-x-interp-4.svg
layout/reftests/svg/smil/anim-x-interp-5-ref.svg
layout/reftests/svg/smil/anim-x-interp-5.svg
layout/reftests/svg/smil/anim-x-interp-6-ref.svg
layout/reftests/svg/smil/anim-x-interp-6.svg
layout/reftests/svg/smil/anim-y-done-1a.svg
layout/reftests/svg/smil/anim-y-done-1b.svg
layout/reftests/svg/smil/anim-y-interp-1-ref.svg
layout/reftests/svg/smil/anim-y-interp-1.svg
layout/reftests/svg/smil/anim-y-interp-2-ref.svg
layout/reftests/svg/smil/anim-y-interp-2.svg
layout/reftests/svg/smil/anim-y-interp-3-ref.svg
layout/reftests/svg/smil/anim-y-interp-3.svg
layout/reftests/svg/smil/anim-y-interp-4-ref.svg
layout/reftests/svg/smil/anim-y-interp-4.svg
layout/reftests/svg/smil/anim-y-interp-5-ref.svg
layout/reftests/svg/smil/anim-y-interp-5.svg
layout/reftests/svg/smil/anim-y-interp-6-ref.svg
layout/reftests/svg/smil/anim-y-interp-6.svg
layout/reftests/svg/smil/api-sanity-1-ref.svg
layout/reftests/svg/smil/api-sanity-1.svg
layout/reftests/svg/smil/container/deferred-anim-1-ref.xhtml
layout/reftests/svg/smil/container/deferred-anim-1.xhtml
layout/reftests/svg/smil/container/deferred-tree-1-ref.xhtml
layout/reftests/svg/smil/container/deferred-tree-1.xhtml
layout/reftests/svg/smil/container/enveloped-tree-1-ref.xhtml
layout/reftests/svg/smil/container/enveloped-tree-1.xhtml
layout/reftests/svg/smil/container/invalid-elem-1-ref.xhtml
layout/reftests/svg/smil/container/invalid-elem-1.xhtml
layout/reftests/svg/smil/container/moved-tree-1-ref.xhtml
layout/reftests/svg/smil/container/moved-tree-1.xhtml
layout/reftests/svg/smil/container/promoted-tree-1-ref.xhtml
layout/reftests/svg/smil/container/promoted-tree-1.xhtml
layout/reftests/svg/smil/container/reftest.list
layout/reftests/svg/smil/pause/init-pause-1-ref.svg
layout/reftests/svg/smil/pause/init-pause-1.svg
layout/reftests/svg/smil/pause/reftest.list
layout/reftests/svg/smil/reftest.list
layout/reftests/svg/smil/repeat/init-repeat-1-ref.svg
layout/reftests/svg/smil/repeat/init-repeat-1.svg
layout/reftests/svg/smil/repeat/reftest.list
layout/reftests/svg/smil/restart/reftest.list
layout/reftests/svg/smil/restart/reset-1-ref.svg
layout/reftests/svg/smil/restart/reset-1.svg
layout/reftests/svg/smil/smil-util.js
layout/reftests/svg/smil/sort/reftest.list
layout/reftests/svg/smil/sort/sort-additive-1-ref.svg
layout/reftests/svg/smil/sort/sort-additive-1.svg
layout/reftests/svg/smil/sort/sort-startAfter-1-ref.svg
layout/reftests/svg/smil/sort/sort-startAfter-1.svg
layout/reftests/svg/smil/sort/sort-startAfter-2-ref.svg
layout/reftests/svg/smil/sort/sort-startAfter-2.svg
layout/reftests/svg/smil/sort/sort-startAfter-3-ref.svg
layout/reftests/svg/smil/sort/sort-startAfter-3.svg
layout/reftests/svg/smil/sort/sort-startSame-1-ref.svg
layout/reftests/svg/smil/sort/sort-startSame-1a.svg
layout/reftests/svg/smil/sort/sort-startSame-1b.svg
layout/reftests/svg/smil/sort/sort-startSame-2-ref.svg
layout/reftests/svg/smil/sort/sort-startSame-2a.svg
layout/reftests/svg/smil/sort/sort-startSame-2b.svg
layout/reftests/svg/smil/style/anim-css-fontsize-pt-1-ref.svg
layout/reftests/svg/smil/style/anim-css-fontsize-pt-1.svg
layout/reftests/svg/smil/style/anim-css-fontsize-px-1-ref.svg
layout/reftests/svg/smil/style/anim-css-fontsize-px-1a.svg
layout/reftests/svg/smil/style/anim-css-fontsize-px-1b.svg
layout/reftests/svg/smil/style/anim-css-fontsize-px-1c.svg
layout/reftests/svg/smil/style/fill-1-ref.svg
layout/reftests/svg/smil/style/fill-1.svg
layout/reftests/svg/smil/style/font-size-1-ref.svg
layout/reftests/svg/smil/style/font-size-1.svg
layout/reftests/svg/smil/style/reftest.list
layout/reftests/svg/smil/timed/appendChild-1a-ref.svg
layout/reftests/svg/smil/timed/appendChild-1a.svg
layout/reftests/svg/smil/timed/appendChild-1b-ref.svg
layout/reftests/svg/smil/timed/appendChild-1b.svg
layout/reftests/svg/smil/timed/appendChild-2a-ref.svg
layout/reftests/svg/smil/timed/appendChild-2a.svg
layout/reftests/svg/smil/timed/appendChild-2b-ref.svg
layout/reftests/svg/smil/timed/appendChild-2b.svg
layout/reftests/svg/smil/timed/paced-1-ref.svg
layout/reftests/svg/smil/timed/paced-1.svg
layout/reftests/svg/smil/timed/pause-1-ref.svg
layout/reftests/svg/smil/timed/pause-1.svg
layout/reftests/svg/smil/timed/reftest.list
layout/reftests/svg/smil/timed/removeChild-1-ref.svg
layout/reftests/svg/smil/timed/removeChild-1.svg
layout/reftests/svg/smil/timed/removeChild-2-ref.svg
layout/reftests/svg/smil/timed/removeChild-2.svg
nsprpub/configure
toolkit/toolkit-makefiles.sh
--- a/browser/installer/unix/packages-static
+++ b/browser/installer/unix/packages-static
@@ -337,16 +337,19 @@ bin/res/charsetalias.properties
 bin/res/charsetData.properties
 bin/res/langGroups.properties
 bin/res/language.properties
 bin/res/entityTables/*
 
 ; svg
 bin/res/svg.css
 bin/components/dom_svg.xpt
+#ifdef MOZ_SMIL
+bin/components/dom_smil.xpt
+#endif
 
 ; [Personal Security Manager]
 ;
 bin/libnssckbi.so
 bin/components/pipboot.xpt
 bin/components/pipnss.xpt
 bin/components/pippki.xpt
 bin/libnss3.so
--- a/browser/installer/windows/packages-static
+++ b/browser/installer/windows/packages-static
@@ -332,16 +332,19 @@ bin\res\charsetalias.properties
 bin\res\charsetData.properties
 bin\res\langGroups.properties
 bin\res\language.properties
 bin\res\entityTables\*
 
 ; svg
 bin\res\svg.css
 bin\components\dom_svg.xpt
+#ifdef MOZ_SMIL
+bin\components\dom_smil.xpt
+#endif
 
 ; [Personal Security Manager]
 ;
 bin\nssckbi.dll
 bin\components\pipboot.xpt
 bin\components\pipnss.xpt
 bin\components\pippki.xpt
 bin\nssutil3.dll
--- a/config/autoconf.mk.in
+++ b/config/autoconf.mk.in
@@ -224,16 +224,17 @@ MOZ_UPDATE_XTERM = @MOZ_UPDATE_XTERM@
 MOZ_MATHML = @MOZ_MATHML@
 MOZ_PERMISSIONS = @MOZ_PERMISSIONS@
 MOZ_XTF = @MOZ_XTF@
 MOZ_NO_INSPECTOR_APIS = @MOZ_NO_INSPECTOR_APIS@
 MOZ_SVG = @MOZ_SVG@
 MOZ_LIBART_CFLAGS = @MOZ_LIBART_CFLAGS@
 MOZ_ENABLE_CANVAS = @MOZ_ENABLE_CANVAS@
 MOZ_CAIRO_CFLAGS = @MOZ_CAIRO_CFLAGS@
+MOZ_SMIL = @MOZ_SMIL@
 MOZ_XSLT_STANDALONE = @MOZ_XSLT_STANDALONE@
 
 MOZ_PREF_EXTENSIONS = @MOZ_PREF_EXTENSIONS@
 
 MOZ_CAIRO_LIBS = @MOZ_CAIRO_LIBS@
 
 MOZ_ENABLE_GNOMEUI = @MOZ_ENABLE_GNOMEUI@
 MOZ_GNOMEUI_CFLAGS = @MOZ_GNOMEUI_CFLAGS@
--- a/configure.in
+++ b/configure.in
@@ -5755,16 +5755,27 @@ MOZ_ARG_DISABLE_BOOL(svg,
 [  --disable-svg            Disable SVG support],
     MOZ_SVG=,
     MOZ_SVG=1 )
 if test -n "$MOZ_SVG"; then
   AC_DEFINE(MOZ_SVG)
 fi
 
 dnl ========================================================
+dnl SMIL
+dnl ========================================================
+MOZ_ARG_ENABLE_BOOL(smil,
+[  --enable-smil            Enable SMIL animation support],
+    MOZ_SMIL=1,
+    MOZ_SMIL= )
+if test -n "$MOZ_SMIL"; then
+  AC_DEFINE(MOZ_SMIL)
+fi
+
+dnl ========================================================
 dnl Installer
 dnl ========================================================
 case "$target_os" in
     aix*|solaris*|linux*|msvc*|mks*|cygwin*|mingw*|os2*|wince*)
         MOZ_INSTALLER=1
         ;;
 esac
 
@@ -7802,16 +7813,17 @@ AC_SUBST(NS_OSSO)
 AC_SUBST(NS_MAEMO_LOCATION)
 AC_SUBST(MOZ_AUTH_EXTENSION)
 AC_SUBST(MOZ_MATHML)
 AC_SUBST(MOZ_PERMISSIONS)
 AC_SUBST(MOZ_XTF)
 AC_SUBST(MOZ_NO_INSPECTOR_APIS)
 AC_SUBST(MOZ_PREF_EXTENSIONS)
 AC_SUBST(MOZ_SVG)
+AC_SUBST(MOZ_SMIL)
 AC_SUBST(MOZ_XSLT_STANDALONE)
 AC_SUBST(MOZ_JS_LIBS)
 AC_SUBST(MOZ_PSM)
 AC_SUBST(MOZ_DEBUG)
 AC_SUBST(MOZ_DEBUG_MODULES)
 AC_SUBST(MOZ_PROFILE_MODULES)
 AC_SUBST(MOZ_DEBUG_ENABLE_DEFS)
 AC_SUBST(MOZ_DEBUG_DISABLE_DEFS)
--- a/content/Makefile.in
+++ b/content/Makefile.in
@@ -54,16 +54,20 @@ endif
 ifdef MOZ_SVG
 PARALLEL_DIRS	+= svg
 endif
 
 ifdef MOZ_XTF
 PARALLEL_DIRS   += xtf
 endif
 
+ifdef MOZ_SMIL
+PARALLEL_DIRS	+= smil
+endif
+
 ifdef MOZ_MATHML
 PARALLEL_DIRS   += mathml
 endif
 
 PARALLEL_DIRS   += events
 
 ifdef ENABLE_TESTS
 TOOL_DIRS += test
--- a/content/base/public/nsContentCreatorFunctions.h
+++ b/content/base/public/nsContentCreatorFunctions.h
@@ -116,17 +116,18 @@ NS_NewMathMLElement(nsIContent** aResult
 
 #ifdef MOZ_XUL
 nsresult
 NS_NewXULElement(nsIContent** aResult, nsINodeInfo* aNodeInfo);
 #endif
 
 #ifdef MOZ_SVG
 nsresult
-NS_NewSVGElement(nsIContent** aResult, nsINodeInfo* aNodeInfo);
+NS_NewSVGElement(nsIContent** aResult, nsINodeInfo* aNodeInfo,
+                 PRBool aFromParser);
 #endif
 
 nsresult
 NS_NewGenConImageContent(nsIContent** aResult, nsINodeInfo* aNodeInfo,
                          imgIRequest* aImageRequest);
 
 nsresult
 NS_NewXMLEventsElement(nsIContent** aResult, nsINodeInfo* aNodeInfo);
--- a/content/base/public/nsIContent.h
+++ b/content/base/public/nsIContent.h
@@ -54,16 +54,19 @@ class nsIContent;
 class nsIEventListenerManager;
 class nsIURI;
 class nsICSSStyleRule;
 class nsRuleWalker;
 class nsAttrValue;
 class nsAttrName;
 class nsTextFragment;
 class nsIDocShell;
+#ifdef MOZ_SMIL
+class nsISMILAttr;
+#endif // MOZ_SMIL
 
 // IID for the nsIContent interface
 #define NS_ICONTENT_IID       \
 { 0x2813b1d9, 0x7fe1, 0x496f, \
  { 0x85, 0x52, 0xa2, 0xc1, 0xc5, 0x6b, 0x15, 0x40 } }
 
 /**
  * A node of content in a document's content model. This interface
@@ -854,16 +857,26 @@ public:
    */
   virtual void DestroyContent() = 0;
 
   /**
    * Saves the form state of this node and its children.
    */
   virtual void SaveSubtreeState() = 0;
 
+#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(const nsIAtom* aName) = 0;
+#endif // MOZ_SMIL
+
 private:
   /**
    * Hook for implementing GetClasses.  This is guaranteed to only be
    * called if the NODE_MAY_HAVE_CLASS flag is set.
    */
   virtual const nsAttrValue* DoGetClasses() const = 0;
 
 public:
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -49,16 +49,19 @@
 #include "nsCRT.h"
 #include "mozFlushType.h"
 #include "nsIAtom.h"
 #include "nsCompatibility.h"
 #include "nsTObserverArray.h"
 #include "nsNodeInfoManager.h"
 #include "nsIStreamListener.h"
 #include "nsIObserver.h"
+#ifdef MOZ_SMIL
+class nsSMILAnimationController;
+#endif // MOZ_SMIL
 
 class nsIContent;
 class nsPresContext;
 class nsIPresShell;
 class nsIDocShell;
 class nsStyleSet;
 class nsIStyleSheet;
 class nsIStyleRule;
@@ -1104,16 +1107,21 @@ public:
 
   /**
    * Return whether the document is currently showing (in the sense of
    * OnPageShow() having been called already and OnPageHide() not having been
    * called yet.
    */
   PRBool IsShowing() { return mIsShowing; }
 
+#ifdef MOZ_SMIL
+  // Getter for this document's SMIL Animation Controller
+  virtual nsSMILAnimationController* GetAnimationController() = 0;
+#endif // MOZ_SMIL
+
 protected:
   ~nsIDocument()
   {
     // XXX The cleanup of mNodeInfoManager (calling DropDocumentReference and
     //     releasing it) happens in the nsDocument destructor. We'd prefer to
     //     do it here but nsNodeInfoManager is a concrete class that we don't
     //     want to expose to users of the nsIDocument API outside of Gecko.
   }
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -166,16 +166,21 @@ static NS_DEFINE_CID(kDOMEventGroupCID, 
 #include "nsIXULDocument.h"
 #include "nsIPrompt.h"
 #include "nsIPropertyBag2.h"
 
 #include "nsFrameLoader.h"
 
 #include "mozAutoDocUpdate.h"
 
+#ifdef MOZ_SMIL
+#include "nsSMILAnimationController.h"
+#include "imgIContainer.h"
+#endif // MOZ_SMIL
+
 
 #ifdef MOZ_LOGGING
 // so we can get logging even in release builds
 #define FORCE_PR_LOG 1
 #endif
 #include "prlog.h"
 
 #ifdef PR_LOGGING
@@ -1772,16 +1777,23 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
     tmp->mLinkMap.EnumerateEntries(LinkMapTraverser, &cb);
   }
 
   // Traverse all our nsCOMArrays.
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mStyleSheets)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mCatalogSheets)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mVisitednessChangedURIs)
 
+#ifdef MOZ_SMIL
+  // Traverse animation components
+  if (tmp->mAnimationController) {
+    tmp->mAnimationController->Traverse(&cb);
+  }
+#endif // MOZ_SMIL
+
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_PRESERVED_WRAPPER
 
   if (tmp->mSubDocuments && tmp->mSubDocuments->ops) {
     PL_DHashTableEnumerate(tmp->mSubDocuments, SubDocTraverser, &cb);
   }
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 
@@ -5250,16 +5262,42 @@ nsDocument::RequestExternalResource(nsIU
 }
 
 void
 nsDocument::EnumerateExternalResources(nsSubDocEnumFunc aCallback, void* aData)
 {
   mExternalResourceMap.EnumerateResources(aCallback, aData);
 }
 
+#ifdef MOZ_SMIL
+nsSMILAnimationController*
+nsDocument::GetAnimationController()
+{
+  // We create the animation controller lazily because most documents won't want
+  // one and only SVG documents and the like will call this
+  if (mAnimationController)
+    return mAnimationController;
+
+  mAnimationController = NS_NewSMILAnimationController(this);
+  
+  // If there's a presContext then check the animation mode and pause if
+  // necessary.
+  nsIPresShell *shell = GetPrimaryShell();
+  if (mAnimationController && shell) {
+    nsPresContext *context = shell->GetPresContext();
+    if (context &&
+        context->ImageAnimationMode() == imgIContainer::kDontAnimMode) {
+      mAnimationController->Pause(nsSMILTimeContainer::PAUSE_USERPREF);
+    }
+  }
+
+  return mAnimationController;
+}
+#endif // MOZ_SMIL
+
 struct DirTable {
   const char* mName;
   PRUint8     mValue;
 };
 
 static const DirTable dirAttributes[] = {
   {"ltr", IBMBIDI_TEXTDIRECTION_LTR},
   {"rtl", IBMBIDI_TEXTDIRECTION_RTL},
@@ -7098,16 +7136,22 @@ nsDocument::OnPageShow(PRBool aPersisted
         }
       }
     }
   }
 
   // Set mIsShowing before firing events, in case those event handlers
   // move us around.
   mIsShowing = PR_TRUE;
+
+#ifdef MOZ_SMIL
+  if (mAnimationController) {
+    mAnimationController->OnPageShow();
+  }
+#endif
   
   nsPageTransitionEvent event(PR_TRUE, NS_PAGE_SHOW, aPersisted);
   DispatchEventToWindow(&event);
 }
 
 void
 nsDocument::OnPageHide(PRBool aPersisted)
 {
@@ -7128,16 +7172,22 @@ nsDocument::OnPageHide(PRBool aPersisted
         }
       }
     }
   }
 
   // Set mIsShowing before firing events, in case those event handlers
   // move us around.
   mIsShowing = PR_FALSE;
+
+#ifdef MOZ_SMIL
+  if (mAnimationController) {
+    mAnimationController->OnPageHide();
+  }
+#endif
   
   // Now send out a PageHide event.
   nsPageTransitionEvent event(PR_TRUE, NS_PAGE_HIDE, aPersisted);
   DispatchEventToWindow(&event);
 
   mVisible = PR_FALSE;
 }
 
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -129,16 +129,19 @@ class nsDocument;
 class nsIDTD;
 class nsIRadioVisitor;
 class nsIFormControl;
 struct nsRadioGroupStruct;
 class nsOnloadBlocker;
 class nsUnblockOnloadEvent;
 struct PLEvent;
 class nsChildContentList;
+#ifdef MOZ_SMIL
+class nsSMILAnimationController;
+#endif // MOZ_SMIL
 
 PR_BEGIN_EXTERN_C
 /* Note that these typedefs declare functions, not pointer to
    functions.  That's the only way in which they differ from
    PLHandleEventProc and PLDestroyEventProc. */
 typedef void*
 (EventHandlerFunc)(PLEvent* self);
 typedef void
@@ -974,16 +977,20 @@ public:
   virtual NS_HIDDEN_(PRBool) FrameLoaderScheduledToBeFinalized(nsIDocShell* aShell);
   virtual NS_HIDDEN_(nsIDocument*)
     RequestExternalResource(nsIURI* aURI,
                             nsINode* aRequestingNode,
                             ExternalResourceLoad** aPendingLoad);
   virtual NS_HIDDEN_(void)
     EnumerateExternalResources(nsSubDocEnumFunc aCallback, void* aData);
 
+#ifdef MOZ_SMIL
+  nsSMILAnimationController* GetAnimationController();
+#endif // MOZ_SMIL
+
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsDocument, nsIDocument)
 
   /**
    * Utility method for getElementsByClassName.  aRootNode is the node (either
    * document or element), which getElementsByClassName was called on.
    */
   static nsresult GetElementsByClassNameHelper(nsINode* aRootNode,
                                                const nsAString& aClasses,
@@ -1265,16 +1272,20 @@ private:
 
   nsTArray<nsRefPtr<nsFrameLoader> > mInitializableFrameLoaders;
   nsTArray<nsRefPtr<nsFrameLoader> > mFinalizableFrameLoaders;
   nsCOMPtr<nsIRunnable> mFrameLoaderRunner;
 
   nsRevocableEventPtr<nsRunnableMethod<nsDocument> > mPendingTitleChangeEvent;
 
   nsExternalResourceMap mExternalResourceMap;
+
+#ifdef MOZ_SMIL
+  nsAutoPtr<nsSMILAnimationController> mAnimationController;
+#endif // MOZ_SMIL
 };
 
 #define NS_DOCUMENT_INTERFACE_TABLE_BEGIN(_class)                             \
   NS_NODE_OFFSET_AND_INTERFACE_TABLE_BEGIN(_class)                            \
   NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(_class, nsIDOMDocument, nsDocument)      \
   NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(_class, nsIDOMNSDocument, nsDocument)    \
   NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(_class, nsIDOMDocumentEvent, nsDocument) \
   NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(_class, nsIDOMDocumentView, nsDocument)  \
--- a/content/base/src/nsGenericDOMDataNode.h
+++ b/content/base/src/nsGenericDOMDataNode.h
@@ -49,16 +49,20 @@
 #include "nsTextFragment.h"
 #include "nsVoidArray.h"
 #include "nsDOMError.h"
 #include "nsIEventListenerManager.h"
 #include "nsGenericElement.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsContentUtils.h"
 
+#ifdef MOZ_SMIL
+#include "nsISMILAttr.h"
+#endif // MOZ_SMIL
+
 class nsIDOMAttr;
 class nsIDOMEventListener;
 class nsIDOMNodeList;
 class nsIFrame;
 class nsIDOMText;
 class nsINodeInfo;
 class nsURI;
 
@@ -212,16 +216,24 @@ public:
     return SetText(aStr.BeginReading(), aStr.Length(), aNotify);
   }
   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(const nsIAtom* /*aName*/)
+  {
+    return nsnull;
+  }
+#endif // MOZ_SMIL
+
 #ifdef DEBUG
   virtual void List(FILE* out, PRInt32 aIndent) const;
   virtual void DumpContent(FILE* out, PRInt32 aIndent, PRBool aDumpAll) const;
 #endif
 
   virtual nsIContent *GetBindingParent() const;
   virtual PRBool IsNodeOfType(PRUint32 aFlags) const;
 
--- a/content/base/src/nsGenericElement.cpp
+++ b/content/base/src/nsGenericElement.cpp
@@ -1888,16 +1888,23 @@ nsGenericElement::InternalIsSupported(ns
            NS_SVG_HaveFeature(aFeature)) {
     if (aVersion.IsEmpty() ||
         PL_strcmp(v, "1.0") == 0 ||
         PL_strcmp(v, "1.1") == 0) {
       *aReturn = PR_TRUE;
     }
   }
 #endif /* MOZ_SVG */
+#ifdef MOZ_SMIL
+  else if (PL_strcasecmp(f, "TimeControl") == 0) {
+    if (aVersion.IsEmpty() || PL_strcmp(v, "1.0") == 0) {
+      *aReturn = PR_TRUE;
+    }
+  }
+#endif /* MOZ_SMIL */
   else {
     nsCOMPtr<nsIDOMNSFeatureFactory> factory =
       GetDOMFeatureFactory(aFeature, aVersion);
 
     if (factory) {
       factory->HasFeature(aObject, aFeature, aVersion, aReturn);
     }
   }
--- a/content/base/src/nsGenericElement.h
+++ b/content/base/src/nsGenericElement.h
@@ -60,16 +60,20 @@
 #include "nsAttrAndChildArray.h"
 #include "mozFlushType.h"
 #include "nsDOMAttributeMap.h"
 #include "nsIWeakReference.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIDocument.h"
 #include "nsIDOMNodeSelector.h"
 
+#ifdef MOZ_SMIL
+#include "nsISMILAttr.h"
+#endif // MOZ_SMIL
+
 class nsIDOMAttr;
 class nsIDOMEventListener;
 class nsIFrame;
 class nsIDOMNamedNodeMap;
 class nsDOMCSSDeclaration;
 class nsIDOMCSSStyleDeclaration;
 class nsIURI;
 class nsVoidArray;
@@ -422,16 +426,23 @@ public:
   virtual PRBool MayHaveFrame() const;
 
   virtual PRUint32 GetScriptTypeID() const;
   virtual nsresult SetScriptTypeID(PRUint32 aLang);
 
   virtual void DestroyContent();
   virtual void SaveSubtreeState();
 
+#ifdef MOZ_SMIL
+  virtual nsISMILAttr* GetAnimatedAttr(const nsIAtom* /*aName*/)
+  {
+    return nsnull;
+  }
+#endif // MOZ_SMIL
+
 #ifdef DEBUG
   virtual void List(FILE* out, PRInt32 aIndent) const
   {
     List(out, aIndent, EmptyCString());
   }
   virtual void DumpContent(FILE* out, PRInt32 aIndent, PRBool aDumpAll) const;
   void List(FILE* out, PRInt32 aIndent, const nsCString& aPrefix) const;
   void ListAttributes(FILE* out) const;
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -1252,16 +1252,35 @@ GK_ATOM(xChannelSelector, "xChannelSelec
 GK_ATOM(y, "y")
 GK_ATOM(y1, "y1")
 GK_ATOM(y2, "y2")
 GK_ATOM(yChannelSelector, "yChannelSelector")
 GK_ATOM(z, "z")
 GK_ATOM(zoomAndPan, "zoomAndPan")
 #endif
 
+#ifdef MOZ_SMIL
+GK_ATOM(accumulate, "accumulate")
+GK_ATOM(additive, "additive")
+GK_ATOM(attributeName, "attributeName")
+GK_ATOM(attributeType, "attributeType")
+GK_ATOM(begin, "begin")
+GK_ATOM(by, "by")
+GK_ATOM(calcMode, "calcMode")
+GK_ATOM(css, "CSS")
+GK_ATOM(dur, "dur")
+GK_ATOM(keySplines, "keySplines")
+GK_ATOM(keyTimes, "keyTimes")
+GK_ATOM(repeatCount, "repeatCount")
+GK_ATOM(repeatDur, "repeatDur")
+GK_ATOM(restart, "restart")
+GK_ATOM(to, "to")
+GK_ATOM(XML, "XML")
+#endif
+
 #ifdef MOZ_MATHML
 GK_ATOM(MOZcolumnalign, "-moz-math-columnalign")
 GK_ATOM(MOZcolumnline, "-moz-math-columnline") // different from columnlines_
 GK_ATOM(MOZfontsize, "-moz-math-font-size") // different from fontsize_
 GK_ATOM(MOZfontstyle, "-moz-math-font-style") // different from fontstyle_
 GK_ATOM(MOZrowalign, "-moz-math-rowalign")
 GK_ATOM(MOZrowline, "-moz-math-rowline") // different from rowlines_
 GK_ATOM(abs_, "abs")
--- a/content/base/src/nsNameSpaceManager.cpp
+++ b/content/base/src/nsNameSpaceManager.cpp
@@ -238,17 +238,17 @@ NS_NewElement(nsIContent** aResult, PRIn
 #endif
 #ifdef MOZ_MATHML
   if (aElementType == kNameSpaceID_MathML) {
     return NS_NewMathMLElement(aResult, aNodeInfo);
   }
 #endif
 #ifdef MOZ_SVG
   if (aElementType == kNameSpaceID_SVG && NS_SVGEnabled()) {
-    return NS_NewSVGElement(aResult, aNodeInfo);
+    return NS_NewSVGElement(aResult, aNodeInfo, aFromParser);
   }
 #endif
   if (aElementType == kNameSpaceID_XMLEvents) {
     return NS_NewXMLEventsElement(aResult, aNodeInfo);
   }
 #ifdef MOZ_XTF
   if (aElementType > kNameSpaceID_LastBuiltin) {
     nsIXTFService* xtfService = nsContentUtils::GetXTFService();
new file mode 100644
--- /dev/null
+++ b/content/smil/Makefile.in
@@ -0,0 +1,102 @@
+# 
+# ***** 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 SMIL module.
+#
+# 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>
+#
+# 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 *****
+
+DEPTH		= ../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE		= content
+LIBRARY_NAME	= gkconsmil_s
+LIBXUL_LIBRARY	= 1
+
+REQUIRES	= xpcom \
+		  string \
+		  layout \
+		  widget \
+		  gfx \
+		  unicharutil \
+		  dom \
+		  js \
+		  pref \
+		  $(NULL)
+
+CPPSRCS		= \
+	nsSMILAnimationController.cpp \
+	nsSMILAnimationFunction.cpp \
+	nsSMILCompositor.cpp \
+	nsSMILFloatType.cpp \
+	nsSMILInstanceTime.cpp \
+	nsSMILKeySpline.cpp \
+	nsSMILNullType.cpp \
+	nsSMILParserUtils.cpp \
+	nsSMILRepeatCount.cpp \
+	nsSMILSetAnimationFunction.cpp \
+	nsSMILTimeContainer.cpp \
+	nsSMILTimedElement.cpp \
+	nsSMILTimeValue.cpp \
+	nsSMILTimeValueSpec.cpp \
+	nsSMILValue.cpp \
+		$(NULL)
+
+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
+
+ifdef ENABLE_TESTS
+TOOL_DIRS		+= test
+endif
+
+EXPORTS		= \
+	  nsISMILAnimationElement.h \
+	  nsISMILAttr.h \
+	  nsSMILAnimationController.h \
+	  nsSMILCompositorTable.h \
+	  nsSMILTimeContainer.h \
+	  nsSMILTypes.h \
+	  $(NULL)
+
+include $(topsrcdir)/config/rules.mk
+
+INCLUDES += 	\
+		-I$(srcdir)/../base/src \
+		$(NULL)
+
+DEFINES += -D_IMPL_NS_LAYOUT
new file mode 100644
--- /dev/null
+++ b/content/smil/nsISMILAnimationElement.h
@@ -0,0 +1,153 @@
+/* -*- 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) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Daniel Holbert <dholbert@mozilla.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
+ * 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 NS_ISMILANIMATIONELEMENT_H_
+#define NS_ISMILANIMATIONELEMENT_H_
+
+#include "nsISupports.h"
+#include "nsIContent.h"
+
+class nsISMILAttr;
+
+//////////////////////////////////////////////////////////////////////////////
+// nsISMILAnimationElement: Interface for elements that control the animation of
+// some property of another element, e.g. <animate>, <set>.
+
+#define NS_ISMILANIMATIONELEMENT_IID \
+{ 0x70ac6eed, 0x0dba, 0x4c11, { 0xa6, 0xc5, 0x15, 0x73, 0xbc, 0x2f, 0x1a, 0xd8 } }
+
+class nsSMILAnimationFunction;
+class nsSMILTimeContainer;
+class nsSMILTimedElement;
+
+enum nsSMILTargetAttrType {
+  eSMILTargetAttrType_auto,
+  eSMILTargetAttrType_CSS,
+  eSMILTargetAttrType_XML
+};
+
+class nsISMILAnimationElement : public nsISupports
+{
+public:
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_ISMILANIMATIONELEMENT_IID)
+
+  /*
+   * Returns this element as nsIContent.
+   */
+  virtual const nsIContent& Content() const = 0;
+
+  /*
+   * Non-const version of Content()
+   */
+  virtual nsIContent& Content() = 0;
+
+  /*
+   * Returns the source attribute as an nsAttrValue. The global namespace will
+   * be used.
+   *
+   * (The 'Anim' here and below is largely to avoid conflicts for subclasses
+   * that derive from nsGenericElement)
+   *
+   * @param aName the name of the attr
+   * @returns PR_TRUE if the attribute was set (even when set to empty string)
+   *          PR_FALSE when not set.
+   */
+  virtual const nsAttrValue* GetAnimAttr(nsIAtom* aName) const = 0;
+
+  /*
+   * Get the current value of an attribute as a string. The global namespace
+   * will be used.
+   *
+   * @param aName the name of the attr
+   * @param aResult the value (may legitimately be the empty string) [OUT]
+   * @returns PR_TRUE if the attribute was set (even when set to empty string)
+   *          PR_FALSE when not set.
+   */
+  PRBool GetAnimAttr(nsIAtom* aAttName, nsAString &aResult) const
+  {
+    return Content().GetAttr(kNameSpaceID_None, aAttName, aResult);
+  }
+
+  /*
+   * Check for the presence of an attribute in the global namespace.
+   */
+  PRBool HasAnimAttr(nsIAtom* aAttName) const
+  {
+    return Content().HasAttr(kNameSpaceID_None, aAttName);
+  }
+
+  /*
+   * Returns the target (animated) element.
+   */
+  virtual nsIContent* GetTargetElementContent() = 0;
+
+  /*
+   * Returns the name of the target (animated) attribute or property.
+   */
+  virtual nsIAtom* GetTargetAttributeName() 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.
+   *
+   * The animation function is owned by the animation element.
+   */
+  virtual nsSMILAnimationFunction& AnimationFunction() = 0;
+
+  /*
+   * Returns the SMIL timed element associated with this animation element.
+   *
+   * The timed element is owned by the animation element.
+   */
+  virtual nsSMILTimedElement& TimedElement() = 0;
+
+  /*
+   * Returns the SMIL timed container root with which this animation element is
+   * associated (if any).
+   */
+  virtual nsSMILTimeContainer* GetTimeContainer() = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsISMILAnimationElement,
+                              NS_ISMILANIMATIONELEMENT_IID)
+
+#endif // NS_ISMILANIMATIONELEMENT_H_
new file mode 100644
--- /dev/null
+++ b/content/smil/nsISMILAttr.h
@@ -0,0 +1,99 @@
+/* -*- 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 SMIL module.
+ *
+ * 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>
+ *
+ * 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 NS_ISMILATTR_H_
+#define NS_ISMILATTR_H_
+
+#include "nsStringFwd.h"
+
+class nsSMILValue;
+class nsISMILType;
+class nsISMILAnimationElement;
+
+////////////////////////////////////////////////////////////////////////
+// nsISMILAttr: A variable targeted by SMIL for animation and can therefore have
+// an underlying (base) value and an animated value For example, an attribute of
+// a particular SVG element.
+//
+// These objects only exist during the compositing phase of SMIL animation
+// calculations. They have a single owner who is responsible for deleting the
+// object.
+
+class nsISMILAttr
+{
+public:
+  /**
+   * Creates a new nsSMILValue for this attribute from a string. The string is
+   * parsed in the context of this attribute so that context-dependent values
+   * such as em-based units can be resolved into a canonical form suitable for
+   * animation (including interpolation etc.).
+   *
+   * @param aStr        A string defining the new value to be created.
+   * @param aSrcElement The source animation element. This may be needed to
+   *                    provided additional context data such as for
+   *                    animateTransform where the 'type' attribute is needed to
+   *                    parse the value.
+   * @param aValue      Outparam for storing the parsed value.
+   * @return NS_OK on success or an error code if creation failed.
+   */
+  virtual nsresult ValueFromString(const nsAString& aStr,
+                                   const nsISMILAnimationElement* aSrcElement,
+                                   nsSMILValue& aValue) const = 0;
+
+  /**
+   * Gets the underlying value of this attribute.
+   *
+   * @return an nsSMILValue object. returned_object.IsNull() will be true if an
+   * error occurred.
+   */
+  virtual nsSMILValue GetBaseValue() const = 0;
+
+  /**
+   * Sets the presentation value of this attribute.
+   *
+   * @param aValue  The value to set.
+   * @return NS_OK on success or an error code if setting failed.
+   */
+  virtual nsresult SetAnimValue(const nsSMILValue& aValue) = 0;
+
+  /**
+   * Virtual destructor, to make sure subclasses can clean themselves up.
+   */
+  virtual ~nsISMILAttr() {};
+};
+
+#endif // NS_ISMILATTR_H_
new file mode 100644
--- /dev/null
+++ b/content/smil/nsISMILType.h
@@ -0,0 +1,196 @@
+/* -*- 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 SMIL module.
+ *
+ * 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):
+ *   Robert O'Callahan <roc+moz@cs.cmu.edu>
+ *   Brian Birtles <birtles@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
+ * 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 NS_ISMILTYPE_H_
+#define NS_ISMILTYPE_H_
+
+#include "nscore.h"
+
+class nsSMILValue;
+
+//////////////////////////////////////////////////////////////////////////////
+// nsISMILType: Interface for defining the basic operations needed for animating
+// a particular kind of data (e.g. lengths, colors, transformation matrices).
+//
+// This interface is never used directly but always through an nsSMILValue that
+// bundles together a pointer to a concrete implementation of this interface and
+// the data upon which it should operate.
+//
+// We keep the data and type separate rather than just providing different
+// subclasses of nsSMILValue as this allows nsSMILValues to be allocated on the
+// stack and directly assigned to one another provided performance benefits for
+// the animation code.
+//
+// Note that different types have different capabilities. Roughly speaking there
+// are probably three main types:
+//
+// +---------------------+---------------+-------------+------------------+
+// | CATEGORY:           | DISCRETE      | LINEAR      | ADDITIVE         |
+// +---------------------+---------------+-------------+------------------+
+// | Example:            | strings,      | path data?  | lengths,         |
+// |                     | color k/words?|             | RGB color values |
+// |                     |               |             |                  |
+// | -- Assign?          |     X         |    X        |    X             |
+// | -- Add?             |     -         |    X?       |    X             |
+// | -- ComputeDistance? |     -         |    -        |    X?            |
+// | -- Interpolate?     |     -         |    X        |    X             |
+// +---------------------+---------------+-------------+------------------+
+//
+
+class nsISMILType
+{
+public:
+  /**
+   * Initialises aValue and sets it to some identity value such that adding
+   * aValue to another value of the same type has no effect.
+   *
+   * @pre (aValue.mType == this && aValue.mU is valid)
+   *      || aValue.mType == null-type
+   * @post aValue.mType == this || NS_FAILED(rv)
+   */
+  virtual nsresult Init(nsSMILValue& aValue) const = 0;
+
+  /**
+   * Destroys any data associated with a value of this type.
+   *
+   * @pre  aValue.mType == this
+   * @post aValue.IsNull()
+   */
+  virtual void Destroy(nsSMILValue& aValue) const = 0;
+
+  /**
+   * Assign this object the value of another. Think of this as the assignment
+   * operator.
+   *
+   * @param   aDest       The left-hand side of the assignment.
+   * @param   aSrc        The right-hand side of the assignment.
+   * @return  NS_OK on success, an error code on failure such as when the
+   *          underlying type of the specified object differs.
+   *
+   * @pre aDest.mType == aSrc.mType == this
+   */
+  virtual nsresult Assign(nsSMILValue& aDest,
+                          const nsSMILValue& aSrc) const = 0;
+
+  /**
+   * Adds two values.
+   *
+   * The count parameter facilitates repetition.
+   *
+   * By equation,
+   *
+   *   aDest += aValueToAdd * aCount
+   *
+   * Therefore, if aCount == 0, aDest will be unaltered.
+   *
+   * This method will fail if this data type is not additive or the value was
+   * not specified using an additive syntax.
+   *
+   * See SVG 1.1, section 19.2.5. In particular,
+   *
+   *   "If a given attribute or property can take values of keywords (which are
+   *   not additive) or numeric values (which are additive), then additive
+   *   animations are possible if the subsequent animation uses a numeric value
+   *   even if the base animation uses a keyword value; however, if the
+   *   subsequent animation uses a keyword value, additive animation is not
+   *   possible."
+   *
+   * If this method fails (e.g. because the data type is not additive), aDest
+   * will be unaltered.
+   *
+   * @param   aDest       The value to add to.
+   * @param   aValueToAdd The value to add.
+   * @param   aCount      The number of times to add aValueToAdd.
+   * @return  NS_OK on success, an error code on failure.
+   *
+   * @pre aValueToAdd.mType == aDest.mType == this
+   */
+  virtual nsresult Add(nsSMILValue& aDest,
+                       const nsSMILValue& aValueToAdd,
+                       PRUint32 aCount) const = 0;
+
+  /**
+   * Calculates the 'distance' between two values. This is the distance used in
+   * paced interpolation.
+   *
+   * @param   aFrom     The start of the interval for which the distance should
+   *                    be calculated.
+   * @param   aTo       The end of the interval for which the distance should be
+   *                    calculated.
+   * @param   aDistance The result of the calculation.
+   * @return  NS_OK on success, or an appropriate error code if there is no
+   *          notion of distance for the underlying data type or the distance
+   *          could not be calculated.
+   *
+   * @pre aFrom.mType == aTo.mType == this
+   */
+  virtual nsresult ComputeDistance(const nsSMILValue& aFrom,
+                                   const nsSMILValue& aTo,
+                                   double& aDistance) const = 0;
+
+  /**
+   * Calculates an interpolated value between two values using the specified
+   * proportion.
+   *
+   * @param   aStartVal     The value defining the start of the interval of
+   *                        interpolation.
+   * @param   aEndVal       The value defining the end of the interval of
+   *                        interpolation.
+   * @param   aUnitDistance A number between 0.0 and 1.0 (inclusive) defining
+   *                        the distance of the interpolated value in the
+   *                        interval.
+   * @param   aResult       The interpolated value.
+   * @result  NS_OK on success, NS_ERROR_FAILURE if this data type cannot be
+   *          interpolated or NS_ERROR_OUT_OF_MEMORY if insufficient memory was
+   *          available for storing the result.
+   *
+   * @pre aStartVal.mType == aEndVal.mType == aResult.mType == this
+   */
+  virtual nsresult Interpolate(const nsSMILValue& aStartVal,
+                               const nsSMILValue& aEndVal,
+                               double aUnitDistance,
+                               nsSMILValue& aResult) const = 0;
+
+  /*
+   * Virtual destructor: Nothing to do here, but subclasses
+   * may need it.
+   */
+  virtual ~nsISMILType() {};
+};
+
+#endif // NS_ISMILTYPE_H_
new file mode 100644
--- /dev/null
+++ b/content/smil/nsSMILAnimationController.cpp
@@ -0,0 +1,511 @@
+/* -*- 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 SMIL module.
+ *
+ * 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>
+ *
+ * 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 "nsSMILAnimationController.h"
+#include "nsSMILCompositor.h"
+#include "nsComponentManagerUtils.h"
+#include "nsITimer.h"
+#include "nsIContent.h"
+#include "nsIDocument.h"
+#include "nsISMILAnimationElement.h"
+#include "nsIDOMSVGAnimationElement.h"
+#include "nsSMILTimedElement.h"
+
+//----------------------------------------------------------------------
+// nsSMILAnimationController implementation
+
+// In my testing the minimum needed for smooth animation is 36 frames per
+// second which seems like a lot (Flash traditionally uses 14fps).
+//
+// Redrawing is synchronous. This is deliberate so that later we can tune the
+// timer based on how long the callback takes. To achieve 36fps we'd need 28ms
+// between frames. For now we set the timer interval to be a little less than
+// this (to allow for the render itself) and then let performance decay as the
+// image gets more complicated and render times increase.
+//
+const PRUint32 nsSMILAnimationController::kTimerInterval = 22;
+
+//----------------------------------------------------------------------
+// ctors, dtors, factory methods
+
+nsSMILAnimationController::nsSMILAnimationController()
+  : mDocument(nsnull)
+{
+  mAnimationElementTable.Init();
+  mChildContainerTable.Init();
+}
+
+nsSMILAnimationController::~nsSMILAnimationController()
+{
+  if (mTimer) {
+    mTimer->Cancel();
+    mTimer = nsnull;
+  }
+
+  if (mForceSampleEvent) {
+    mForceSampleEvent->Expire();
+    mForceSampleEvent = nsnull;
+  }
+
+  NS_ASSERTION(mAnimationElementTable.Count() == 0,
+               "Animation controller shouldn't be tracking any animation"
+               " elements when it dies.");
+}
+
+nsSMILAnimationController* NS_NewSMILAnimationController(nsIDocument* aDoc)
+{
+  nsSMILAnimationController* animationController = 
+    new nsSMILAnimationController();
+  NS_ENSURE_TRUE(animationController, nsnull);
+
+  nsresult rv = animationController->Init(aDoc);
+  if (NS_FAILED(rv)) {
+    delete animationController;
+    animationController = nsnull;
+  }
+
+  return animationController;
+}
+
+nsresult
+nsSMILAnimationController::Init(nsIDocument* aDoc)
+{
+  NS_ENSURE_ARG_POINTER(aDoc);
+
+  mTimer = do_CreateInstance("@mozilla.org/timer;1");
+  NS_ENSURE_TRUE(mTimer, NS_ERROR_OUT_OF_MEMORY);
+
+  // Keep track of document, so we can traverse its set of animation elements
+  mDocument = aDoc;
+
+  Begin();
+
+  return NS_OK;
+}
+
+//----------------------------------------------------------------------
+// nsSMILTimeContainer methods:
+
+void
+nsSMILAnimationController::Pause(PRUint32 aType)
+{
+  nsSMILTimeContainer::Pause(aType);
+
+  if (mPauseState) {
+    StopTimer();
+  }
+}
+
+void
+nsSMILAnimationController::Resume(PRUint32 aType)
+{
+  PRBool wasPaused = (mPauseState != 0);
+
+  nsSMILTimeContainer::Resume(aType);
+
+  if (wasPaused && !mPauseState) {
+    StartTimer();
+  }
+}
+
+nsSMILTime
+nsSMILAnimationController::GetParentTime() const
+{
+  // Our parent time is wallclock time
+  return PR_Now() / PR_USEC_PER_MSEC;
+}
+
+//----------------------------------------------------------------------
+// Animation element registration methods:
+
+void
+nsSMILAnimationController::RegisterAnimationElement(
+                                  nsISMILAnimationElement* aAnimationElement)
+{
+  mAnimationElementTable.PutEntry(aAnimationElement);
+}
+
+void
+nsSMILAnimationController::UnregisterAnimationElement(
+                                  nsISMILAnimationElement* aAnimationElement)
+{
+  mAnimationElementTable.RemoveEntry(aAnimationElement);
+}
+
+//----------------------------------------------------------------------
+// nsSMILAnimationController methods:
+
+nsresult
+nsSMILAnimationController::OnForceSample()
+{
+  // Make sure this was a queued call
+  NS_ENSURE_TRUE(mForceSampleEvent, NS_ERROR_FAILURE);
+
+  nsresult rv = NS_OK;
+  if (!mPauseState) {
+    // Stop timer-controlled samples first, to avoid race conditions.
+    rv = StopTimer();
+    if (NS_SUCCEEDED(rv)) {
+      // StartTimer does a synchronous sample before it starts the timer.
+      // This is the sample that we're "forcing" here.
+      rv = StartTimer();
+    }
+  }
+  mForceSampleEvent = nsnull;
+  return rv;
+}
+
+void
+nsSMILAnimationController::FireForceSampleEvent()
+{
+  if (!mForceSampleEvent) {
+    mForceSampleEvent = new ForceSampleEvent(*this);
+    if (NS_FAILED(NS_DispatchToCurrentThread(mForceSampleEvent))) {
+      NS_WARNING("Failed to dispatch force sample event");
+      mForceSampleEvent = nsnull;
+    }
+  }
+}
+
+//----------------------------------------------------------------------
+// Page show/hide
+
+void
+nsSMILAnimationController::OnPageShow()
+{
+  Resume(nsSMILTimeContainer::PAUSE_PAGEHIDE);
+}
+
+void
+nsSMILAnimationController::OnPageHide()
+{
+  Pause(nsSMILTimeContainer::PAUSE_PAGEHIDE);
+}
+
+//----------------------------------------------------------------------
+// Cycle-collection support
+
+void
+nsSMILAnimationController::Traverse(
+    nsCycleCollectionTraversalCallback* aCallback)
+{
+  // Traverse last compositor table
+  if (mLastCompositorTable) {
+    mLastCompositorTable->EnumerateEntries(CompositorTableEntryTraverse,
+                                           aCallback);
+  }
+}
+
+/*static*/ PR_CALLBACK PLDHashOperator
+nsSMILAnimationController::CompositorTableEntryTraverse(
+                                      nsSMILCompositor* aCompositor,
+                                      void* aArg)
+{
+  nsCycleCollectionTraversalCallback* cb =
+    static_cast<nsCycleCollectionTraversalCallback*>(aArg);
+  aCompositor->Traverse(cb);
+  return PL_DHASH_NEXT;
+}
+
+
+void
+nsSMILAnimationController::Unlink()
+{
+  mLastCompositorTable = nsnull;
+}
+
+//----------------------------------------------------------------------
+// Timer-related implementation helpers
+
+/*static*/ void
+nsSMILAnimationController::Notify(nsITimer* timer, void* aClosure)
+{
+  nsSMILAnimationController* controller = (nsSMILAnimationController*)aClosure;
+
+  NS_ASSERTION(controller->mTimer == timer,
+               "nsSMILAnimationController::Notify called with incorrect timer");
+
+  controller->Sample();
+}
+
+nsresult
+nsSMILAnimationController::StartTimer()
+{
+  NS_ENSURE_TRUE(mTimer, NS_ERROR_FAILURE);
+  NS_ASSERTION(mPauseState == 0, "Starting timer but controller is paused.");
+
+  // Run the first sample manually
+  Sample();
+
+  // 
+  // XXX Make this self-tuning. Sounds like control theory to me and not
+  // something I'm familiar with.
+  //
+  return mTimer->InitWithFuncCallback(nsSMILAnimationController::Notify,
+                                      this,
+                                      kTimerInterval,
+                                      nsITimer::TYPE_REPEATING_SLACK);
+}
+
+nsresult
+nsSMILAnimationController::StopTimer()
+{
+  NS_ENSURE_TRUE(mTimer, NS_ERROR_FAILURE);
+
+  return mTimer->Cancel();
+}
+
+//----------------------------------------------------------------------
+// Sample-related methods and callbacks
+
+void
+nsSMILAnimationController::DoSample()
+{
+  // STEP 1: Sample the child time containers
+  //
+  // When we sample the child time containers they will simply record the sample
+  // time in document time.
+  TimeContainerHashtable activeContainers;
+  activeContainers.Init(mChildContainerTable.Count());
+  mChildContainerTable.EnumerateEntries(SampleTimeContainers,
+                                        &activeContainers);
+
+  // STEP 2: (i)  Sample the timed elements AND
+  //         (ii) Create a table of compositors
+  // 
+  // (i) Here we sample the timed elements (fetched from the
+  // nsISMILAnimationElements) which determine from the active time if the
+  // element is active and what it's simple time etc. is. This information is
+  // then passed to its time client (nsSMILAnimationFunction).
+  //
+  // (ii) During the same loop we also build up a table that contains one
+  // compositor for each animated attribute and which maps animated elements to
+  // the corresponding compositor for their target attribute.
+  //
+  // Note that this compositor table needs to be allocated on the heap so we can
+  // store it until the next sample. This lets us find out which elements were
+  // animated in sample 'n-1' but not in sample 'n' (and hence need to have
+  // their animation effects removed in sample 'n').
+  //
+  // Parts (i) and (ii) are not functionally related but we combine them here to
+  // save iterating over the animation elements twice.
+
+  // Create the compositor table
+  nsAutoPtr<nsSMILCompositorTable> 
+    currentCompositorTable(new nsSMILCompositorTable());
+  if (!currentCompositorTable)
+    return;
+  currentCompositorTable->Init(0);
+
+  SampleAnimationParams params = { &activeContainers, currentCompositorTable };
+  nsresult rv = mAnimationElementTable.EnumerateEntries(SampleAnimation,
+                                                        &params);
+  if (NS_FAILED(rv)) {
+    NS_WARNING("SampleAnimationParams failed");
+  }
+  activeContainers.Clear();
+
+  // STEP 3: Remove animation effects from any no-longer-animated elems/attrs
+  if (mLastCompositorTable) {
+    // XXX Remove animation effects from no-longer-animated elements
+    //  * For each compositor in current sample's hash table:
+    //    - Remove entry from *prev sample's* hash table
+    //  * For any entries still remaining in prev sample's hash table:
+    //    - Remove animation from that entry's attribute.
+    //      (For nsSVGLength2, set anim val = base val.  For CSS attribs,
+    //      just clear the relevant chunk of OverrideStyle)
+  }
+
+  // STEP 4: Compose currently-animated attributes.
+  nsSMILCompositor::ComposeAttributes(*currentCompositorTable);
+
+  // Update last compositor table
+  mLastCompositorTable = currentCompositorTable.forget();
+}
+
+/*static*/ PR_CALLBACK PLDHashOperator
+nsSMILAnimationController::SampleTimeContainers(TimeContainerPtrKey* aKey,
+                                                void* aData)
+{ 
+  NS_ENSURE_TRUE(aKey, PL_DHASH_NEXT);
+  NS_ENSURE_TRUE(aKey->GetKey(), PL_DHASH_NEXT);
+  NS_ENSURE_TRUE(aData, PL_DHASH_NEXT);
+
+  TimeContainerHashtable* activeContainers
+    = static_cast<TimeContainerHashtable*>(aData);
+
+  nsSMILTimeContainer* container = aKey->GetKey();
+  if (container->NeedsSample()) {
+    container->Sample();
+    activeContainers->PutEntry(container);
+  }
+
+  return PL_DHASH_NEXT;
+}
+
+/*static*/ PR_CALLBACK PLDHashOperator
+nsSMILAnimationController::SampleAnimation(AnimationElementPtrKey* aKey,
+                                           void* aData)
+{
+  NS_ENSURE_TRUE(aKey, PL_DHASH_NEXT);
+  NS_ENSURE_TRUE(aKey->GetKey(), PL_DHASH_NEXT);
+  NS_ENSURE_TRUE(aData, PL_DHASH_NEXT);
+
+  nsISMILAnimationElement* animElem = aKey->GetKey();
+  SampleAnimationParams* params = static_cast<SampleAnimationParams*>(aData);
+
+  SampleTimedElement(animElem, params->mActiveContainers);
+  AddAnimationToCompositorTable(animElem, params->mCompositorTable);
+
+  return PL_DHASH_NEXT;
+}
+
+/*static*/ void
+nsSMILAnimationController::SampleTimedElement(
+  nsISMILAnimationElement* aElement, TimeContainerHashtable* aActiveContainers)
+{
+  nsSMILTimeContainer* timeContainer = aElement->GetTimeContainer();
+  if (!timeContainer)
+    return;
+
+  // We'd like to call timeContainer->NeedsSample() here and skip all timed
+  // elements that belong to paused time containers that don't need a sample,
+  // but that doesn't work because we've already called Sample() on all the time
+  // containers so the paused ones don't need a sample any more and they'll
+  // return false.
+  //
+  // Instead we build up a hashmap of active time containers during the previous
+  // step (SampleTimeContainers) and then test here if the container for this
+  // timed element is in the list.
+  if (!aActiveContainers->GetEntry(timeContainer))
+    return;
+
+  nsSMILTime containerTime = timeContainer->GetCurrentTime();
+
+  aElement->TimedElement().SampleAt(containerTime);
+}
+
+/*static*/ void
+nsSMILAnimationController::AddAnimationToCompositorTable(
+  nsISMILAnimationElement* aElement, nsSMILCompositorTable* aCompositorTable)
+{
+  // Add a compositor to the hash table if there's not already one there
+  nsSMILCompositorKey key;
+  if (!GetCompositorKeyForAnimation(aElement, key))
+    // Something's wrong/missing about animation's target; skip this animation
+    return;
+
+  nsSMILCompositor* result = aCompositorTable->PutEntry(key);
+  
+  // Add this animationElement's animation function to the compositor's list of
+  // animation functions.
+  result->AddAnimationFunction(&aElement->AnimationFunction());
+}
+
+// Helper function that, given a nsISMILAnimationElement, looks up its target
+// element & target attribute and returns a newly-constructed nsSMILCompositor
+// for this target.
+/*static*/ PRBool
+nsSMILAnimationController::GetCompositorKeyForAnimation(
+    nsISMILAnimationElement* aAnimElem, nsSMILCompositorKey& aResult)
+{
+  // Look up target (animated) element
+  nsIContent* 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)
+    // 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)
+  //
+  // XXX This doesn't really work for CSS properties that aren't mapped
+  // attributes
+  if (attributeType == eSMILTargetAttrType_auto) {
+    attributeType = (targetElem->IsAttributeMapped(attributeName))
+                  ? eSMILTargetAttrType_CSS
+                  : eSMILTargetAttrType_XML;
+  }
+  PRBool isCSS = (attributeType == eSMILTargetAttrType_CSS);
+
+  // Construct the key
+  aResult.mElement = targetElem;
+  aResult.mAttributeName = attributeName;
+  aResult.mIsCSS = isCSS;
+
+  return PR_TRUE;
+}
+
+//----------------------------------------------------------------------
+// Add/remove child time containers
+
+nsresult
+nsSMILAnimationController::AddChild(nsSMILTimeContainer& aChild)
+{
+  TimeContainerPtrKey* key = mChildContainerTable.PutEntry(&aChild);
+  NS_ENSURE_TRUE(key,NS_ERROR_OUT_OF_MEMORY);
+
+  if (!mPauseState && mChildContainerTable.Count() == 1) {
+    StartTimer();
+  }
+    
+  return NS_OK;
+}
+
+void
+nsSMILAnimationController::RemoveChild(nsSMILTimeContainer& aChild)
+{
+  mChildContainerTable.RemoveEntry(&aChild);
+
+  if (!mPauseState && mChildContainerTable.Count() == 0) {
+    StopTimer();
+  }
+}
new file mode 100644
--- /dev/null
+++ b/content/smil/nsSMILAnimationController.h
@@ -0,0 +1,179 @@
+/* -*- 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 SMIL module.
+ *
+ * The Initial Developer of the Original Code is Brian Birtles.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian Birtles <birtles@gmail.com>
+ *   Daniel Holbert <dholbert@mozilla.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
+ * 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 NS_SMILANIMATIONCONTROLLER_H_
+#define NS_SMILANIMATIONCONTROLLER_H_
+
+#include "nsAutoPtr.h"
+#include "nsCOMPtr.h"
+#include "nsTArray.h"
+#include "nsITimer.h"
+#include "nsTHashtable.h"
+#include "nsHashKeys.h"
+#include "nsThreadUtils.h" // for nsRunnable
+#include "nsSMILTimeContainer.h"
+#include "nsSMILCompositorTable.h"
+
+class nsISMILAnimationElement;
+class nsIDocument;
+
+//----------------------------------------------------------------------
+// nsSMILAnimationController
+// 
+// The animation controller maintains the animation timer and determines the
+// sample times and sample rate for all SMIL animations in a document. There is
+// at most one animation controller per nsDocument so that frame-rate tuning can
+// be performed at a document-level.
+//
+// The animation controller can contain many child time containers (timed
+// document root objects) which may correspond to SVG document fragments within
+// a compound document. These time containers can be paused individually or
+// here, at the document level.
+//
+class nsSMILAnimationController : public nsSMILTimeContainer
+{
+public:
+  nsSMILAnimationController();
+  ~nsSMILAnimationController();
+
+  // nsSMILContainer
+  virtual void Pause(PRUint32 aType);
+  virtual void Resume(PRUint32 aType);
+  virtual nsSMILTime GetParentTime() const;
+  
+  // Methods for registering and enumerating animation elements
+  void RegisterAnimationElement(nsISMILAnimationElement* aAnimationElement);
+  void UnregisterAnimationElement(nsISMILAnimationElement* aAnimationElement);
+
+  // Methods for forcing synchronous samples
+  nsresult OnForceSample();
+  void     FireForceSampleEvent();
+
+  // Methods for handling page transitions
+  void     OnPageShow();
+  void     OnPageHide();
+
+  // Methods for supporting cycle-collection
+  void     Traverse(nsCycleCollectionTraversalCallback* aCallback);
+  void     Unlink();
+
+protected:
+  // Typedefs
+  typedef nsPtrHashKey<nsSMILTimeContainer> TimeContainerPtrKey;
+  typedef nsTHashtable<TimeContainerPtrKey> TimeContainerHashtable;
+  typedef nsPtrHashKey<nsISMILAnimationElement> AnimationElementPtrKey;
+  typedef nsTHashtable<AnimationElementPtrKey> AnimationElementHashtable;
+
+  // Types
+  class ForceSampleEvent : public nsRunnable {
+  public:
+    ForceSampleEvent(nsSMILAnimationController &aAnimationController)
+      : mAnimationController(&aAnimationController) { }
+
+    NS_IMETHOD Run() {
+      if (!mAnimationController)
+        return NS_OK;
+
+      return mAnimationController->OnForceSample();
+    }
+    void Expire() { mAnimationController = nsnull; }
+
+  private:
+    nsSMILAnimationController* mAnimationController;
+  };
+
+  struct SampleAnimationParams
+  {
+    TimeContainerHashtable* mActiveContainers;
+    nsSMILCompositorTable*  mCompositorTable;
+  };
+  
+  // Factory methods
+  friend nsSMILAnimationController*
+  NS_NewSMILAnimationController(nsIDocument* aDoc);
+  nsresult    Init(nsIDocument* aDoc);
+
+  // Cycle-collection implementation helpers
+  PR_STATIC_CALLBACK(PLDHashOperator) CompositorTableEntryTraverse(
+      nsSMILCompositor* aCompositor, void* aArg);
+
+  // Timer-related implementation helpers
+  static void Notify(nsITimer* aTimer, void* aClosure);
+  nsresult    StartTimer();
+  nsresult    StopTimer();
+
+  // Sample-related callbacks and implementation helpers
+  virtual void DoSample();
+  PR_STATIC_CALLBACK(PLDHashOperator) SampleTimeContainers(
+      TimeContainerPtrKey* aKey, void* aData);
+  PR_STATIC_CALLBACK(PLDHashOperator) SampleAnimation(
+      AnimationElementPtrKey* aKey, void* aData);
+  PR_STATIC_CALLBACK(PLDHashOperator) AddAnimationToCompositorTable(
+      AnimationElementPtrKey* aKey, void* aData);
+  static void SampleTimedElement(nsISMILAnimationElement* aElement,
+                                 TimeContainerHashtable* aActiveContainers);
+  static void AddAnimationToCompositorTable(
+    nsISMILAnimationElement* aElement, nsSMILCompositorTable* aCompositorTable);
+  static PRBool GetCompositorKeyForAnimation(nsISMILAnimationElement* aAnimElem,
+                                             nsSMILCompositorKey& aResult);
+
+  // Methods for adding/removing time containers
+  virtual nsresult AddChild(nsSMILTimeContainer& aChild);
+  virtual void     RemoveChild(nsSMILTimeContainer& aChild);
+
+  // Members
+  static const PRUint32      kTimerInterval;
+  nsCOMPtr<nsITimer>         mTimer;
+  AnimationElementHashtable  mAnimationElementTable;
+  TimeContainerHashtable     mChildContainerTable;
+  nsRefPtr<ForceSampleEvent> mForceSampleEvent;
+  
+  // Store raw ptr to mDocument.  It owns the controller, so controller
+  // shouldn't outlive it
+  nsIDocument* mDocument;
+
+  // Contains compositors used in our last sample.  We keep this around
+  // so we can detect when an element/attribute used to be animated,
+  // but isn't anymore for some reason. (e.g. if its <animate> element is 
+  // removed or retargeted)
+  nsAutoPtr<nsSMILCompositorTable> mLastCompositorTable;
+};
+
+nsSMILAnimationController* NS_NewSMILAnimationController(nsIDocument *doc);
+
+#endif // NS_SMILANIMATIONCONTROLLER_H_
new file mode 100644
--- /dev/null
+++ b/content/smil/nsSMILAnimationFunction.cpp
@@ -0,0 +1,1011 @@
+/* -*- 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 SMIL module.
+ *
+ * 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>
+ *   Chris Double  <chris.double@double.co.nz>
+ *   Daniel Holbert <dholbert@cs.stanford.edu>
+ *
+ * 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 "nsSMILAnimationFunction.h"
+#include "nsISMILAttr.h"
+#include "nsSMILParserUtils.h"
+#include "nsSMILNullType.h"
+#include "nsISMILAnimationElement.h"
+#include "nsGkAtoms.h"
+#include "nsCOMPtr.h"
+#include "nsCOMArray.h"
+#include "nsIContent.h"
+#include "nsAutoPtr.h"
+#include "nsContentUtils.h"
+#include "nsReadableUtils.h"
+#include "nsString.h"
+#include <math.h>
+
+//----------------------------------------------------------------------
+// Static members
+
+nsAttrValue::EnumTable nsSMILAnimationFunction::sAccumulateTable[] = {
+      {"none", PR_FALSE},
+      {"sum", PR_TRUE},
+      {nsnull, 0}
+};
+
+nsAttrValue::EnumTable nsSMILAnimationFunction::sAdditiveTable[] = {
+      {"replace", PR_FALSE},
+      {"sum", PR_TRUE},
+      {nsnull, 0}
+};
+
+nsAttrValue::EnumTable nsSMILAnimationFunction::sCalcModeTable[] = {
+      {"linear", CALC_LINEAR},
+      {"discrete", CALC_DISCRETE},
+      {"paced", CALC_PACED},
+      {"spline", CALC_SPLINE},
+      {nsnull, 0}
+};
+
+// Bits for attributes that are parsed the same regardless of animated type
+#define BF_ACCUMULATE  0
+#define BF_ADDITIVE    1
+#define BF_CALC_MODE   2
+#define BF_KEY_TIMES   3
+#define BF_KEY_SPLINES 4
+
+// Any negative number should be fine as a sentinel here,
+// because valid distances are non-negative.
+#define COMPUTE_DISTANCE_ERROR (-1)
+
+// Based on GET/SET_BOOLBIT in nsHTMLInputElement.cpp
+#define GET_FLAG(bitfield, field) (((bitfield) & (0x01 << (field))) \
+                                     ? PR_TRUE : PR_FALSE)
+#define SET_FLAG(bitfield, field, b) ((b) \
+                                     ? ((bitfield) |=  (0x01 << (field))) \
+                                     : ((bitfield) &= ~(0x01 << (field))))
+
+//----------------------------------------------------------------------
+// Constructors etc.
+
+nsSMILAnimationFunction::nsSMILAnimationFunction()
+  : mIsActive(PR_FALSE),
+    mIsFrozen(PR_FALSE),
+    mSampleTime(-1),
+    mRepeatIteration(0),
+    mLastValue(PR_FALSE),
+    mHasChanged(PR_TRUE),
+    mBeginTime(LL_MININT),
+    mAnimationElement(nsnull),
+    mErrorFlags(0)
+{
+}
+
+void
+nsSMILAnimationFunction::SetAnimationElement(
+    nsISMILAnimationElement* aAnimationElement)
+{
+  mAnimationElement = aAnimationElement;
+}
+
+PRBool
+nsSMILAnimationFunction::SetAttr(nsIAtom* aAttribute, const nsAString& aValue,
+                                 nsAttrValue& aResult, nsresult* aParseResult)
+{
+  PRBool foundMatch = PR_TRUE;
+  nsresult parseResult = NS_OK;
+
+  // The attributes 'by', 'from', 'to', and 'values' may be parsed differently
+  // depending on the element & attribute we're animating.  So instead of
+  // parsing them now we re-parse them at every sample.
+  if (aAttribute == nsGkAtoms::by ||
+      aAttribute == nsGkAtoms::from ||
+      aAttribute == nsGkAtoms::to ||
+      aAttribute == nsGkAtoms::values) {
+    // We parse to, from, by, values at sample time.
+    // XXX Need to flag which attribute has changed and then when we parse it at
+    // sample time, report any errors and reset the flag
+    mHasChanged = PR_TRUE;
+    aResult.SetTo(aValue);
+  } else if (aAttribute == nsGkAtoms::accumulate) {
+    parseResult = SetAccumulate(aValue, aResult);
+  } else if (aAttribute == nsGkAtoms::additive) {
+    parseResult = SetAdditive(aValue, aResult);
+  } else if (aAttribute == nsGkAtoms::calcMode) {
+    parseResult = SetCalcMode(aValue, aResult);
+  } else if (aAttribute == nsGkAtoms::keyTimes) {
+    parseResult = SetKeyTimes(aValue, aResult);
+  } else if (aAttribute == nsGkAtoms::keySplines) {
+    parseResult = SetKeySplines(aValue, aResult);
+  } else {
+    foundMatch = PR_FALSE;
+  }
+
+  if (foundMatch && aParseResult) {
+    *aParseResult = parseResult;
+  }
+
+  return foundMatch;
+}
+
+PRBool
+nsSMILAnimationFunction::UnsetAttr(nsIAtom* aAttribute)
+{
+  PRBool foundMatch = PR_TRUE;
+
+  if (aAttribute == nsGkAtoms::by ||
+      aAttribute == nsGkAtoms::from ||
+      aAttribute == nsGkAtoms::to ||
+      aAttribute == nsGkAtoms::values) {
+    mHasChanged = PR_TRUE;
+  } else if (aAttribute == nsGkAtoms::accumulate) {
+    UnsetAccumulate();
+  } else if (aAttribute == nsGkAtoms::additive) {
+    UnsetAdditive();
+  } else if (aAttribute == nsGkAtoms::calcMode) {
+    UnsetCalcMode();
+  } else if (aAttribute == nsGkAtoms::keyTimes) {
+    UnsetKeyTimes();
+  } else if (aAttribute == nsGkAtoms::keySplines) {
+    UnsetKeySplines();
+  } else {
+    foundMatch = PR_FALSE;
+  }
+
+  return foundMatch;
+}
+
+void
+nsSMILAnimationFunction::SampleAt(nsSMILTime aSampleTime,
+                                  const nsSMILTimeValue& aSimpleDuration,
+                                  PRUint32 aRepeatIteration)
+{
+  if (mHasChanged || mLastValue || mSampleTime != aSampleTime || 
+      mSimpleDuration.CompareTo(aSimpleDuration) || 
+      mRepeatIteration != aRepeatIteration) {
+    mHasChanged = PR_TRUE;
+  }
+
+  mSampleTime       = aSampleTime;
+  mSimpleDuration   = aSimpleDuration;
+  mRepeatIteration  = aRepeatIteration;
+  mLastValue        = PR_FALSE;
+}
+
+void
+nsSMILAnimationFunction::SampleLastValue(PRUint32 aRepeatIteration)
+{
+  if (mHasChanged || !mLastValue || mRepeatIteration != aRepeatIteration) {
+    mHasChanged = PR_TRUE;
+  }
+
+  mRepeatIteration  = aRepeatIteration;
+  mLastValue        = PR_TRUE;
+}
+
+void
+nsSMILAnimationFunction::Activate(nsSMILTime aBeginTime)
+{
+  mBeginTime = aBeginTime;
+  mIsActive = PR_TRUE;
+  mIsFrozen = PR_FALSE;
+  mFrozenValue = nsSMILValue();
+}
+
+void
+nsSMILAnimationFunction::Inactivate(PRBool aIsFrozen)
+{
+  mIsActive = PR_FALSE;
+  mIsFrozen = aIsFrozen;
+  mFrozenValue = nsSMILValue();
+  mHasChanged = PR_TRUE;
+}
+
+void
+nsSMILAnimationFunction::ComposeResult(const nsISMILAttr& aSMILAttr,
+                                       nsSMILValue& aResult)
+{
+  mHasChanged = PR_FALSE;
+
+  // Skip animations that are inactive or in error
+  if (!IsActive() || mErrorFlags != 0)
+    return;
+
+  // Get the animation values
+  nsSMILValueArray values;
+  nsresult rv = GetValues(aSMILAttr, values);
+  if (NS_FAILED(rv))
+    return;
+
+  // If this interval is active, we must have a non-negative
+  // mSampleTime and a resolved or indefinite mSimpleDuration.
+  // (Otherwise, we're probably just frozen.)
+  if (mIsActive) {
+    NS_ENSURE_TRUE(mSampleTime >= 0,);
+    NS_ENSURE_TRUE(mSimpleDuration.IsResolved() || 
+                   mSimpleDuration.IsIndefinite(),);
+  }
+
+  nsSMILValue result(aResult.mType);
+  
+  if (mSimpleDuration.IsIndefinite() ||
+      (HasAttr(nsGkAtoms::values) && values.Length() == 1)) {
+
+    // Indefinite duration or only one value set: Always set the first value
+    result = values[0];
+
+  } else if (mLastValue) {
+
+    // Sampling last value
+    nsSMILValue last(values[values.Length() - 1]);
+    result = last;
+
+    // See comment in AccumulateResult: to-animation does not accumulate
+    if (!IsToAnimation() && GetAccumulate() && mRepeatIteration) {
+      // If the target attribute type doesn't support addition Add will
+      // fail leaving result = last
+      result.Add(last, mRepeatIteration);
+    }
+
+  } else if (!mFrozenValue.IsNull() && !mHasChanged) {
+
+    // Frozen to animation
+    result = mFrozenValue;
+
+  } else {
+
+    // Interpolation
+    NS_ENSURE_SUCCESS(InterpolateResult(values, result, aResult),);
+    NS_ENSURE_SUCCESS(AccumulateResult(values, result),);
+
+    if (IsToAnimation() && mIsFrozen) {
+      mFrozenValue = result;
+    }
+  }
+
+  // If additive animation isn't required or isn't supported, set the value.
+  if (!IsAdditive() || NS_FAILED(aResult.Add(result)))
+    aResult = result;
+}
+
+PRInt8
+nsSMILAnimationFunction::CompareTo(const nsSMILAnimationFunction* aOther) const
+{
+  NS_ENSURE_TRUE(aOther, 0);
+
+  NS_ASSERTION(aOther != this, "Trying to compare to self.");
+
+  // Inactive animations sort first
+  if (!IsActive() && aOther->IsActive())
+    return -1;
+
+  if (IsActive() && !aOther->IsActive())
+    return 1;
+
+  // Sort based on begin time
+  if (mBeginTime != aOther->GetBeginTime())
+    return mBeginTime > aOther->GetBeginTime() ? 1 : -1;
+
+  // XXX When syncbase timing is implemented, we next need to sort based on
+  // dependencies
+
+  // Animations that appear later in the document sort after those earlier in
+  // the document
+  nsIContent &thisElement = mAnimationElement->Content();
+  nsIContent &otherElement = aOther->mAnimationElement->Content();
+
+  NS_ASSERTION(&thisElement != &otherElement,
+             "Two animations cannot have the same animation content element!");
+
+  return (nsContentUtils::PositionIsBefore(&thisElement, &otherElement))
+          ? -1 : 1;
+}
+
+PRBool
+nsSMILAnimationFunction::WillReplace() const
+{
+  /*
+   * In IsAdditive() we don't consider to-animation to be additive as it is
+   * a special case that is dealt with differently in the compositing method but
+   * here we return false for to animation as it builds on the underlying value
+   * unless its a frozen to animation.
+   */
+  return !(IsAdditive() || IsToAnimation()) || 
+    (IsToAnimation() && mIsFrozen && !mHasChanged);
+}
+
+PRBool
+nsSMILAnimationFunction::HasChanged() const
+{
+  return mHasChanged;
+}
+
+//----------------------------------------------------------------------
+// Implementation helpers
+
+nsresult
+nsSMILAnimationFunction::InterpolateResult(const nsSMILValueArray& aValues,
+                                           nsSMILValue& aResult,
+                                           nsSMILValue& aBaseValue)
+{
+  nsresult rv = NS_OK;
+  const nsSMILValue* from = nsnull;
+  const nsSMILValue* to = nsnull;
+  const nsSMILTime& dur = mSimpleDuration.GetMillis();
+
+  // Sanity Checks
+  NS_ASSERTION(mSampleTime >= 0.0f, "Sample time should not be negative...");
+  NS_ASSERTION(dur  >= 0.0f, "Simple duration should not be negative...");
+
+  if (mSampleTime >= dur || mSampleTime < 0) {
+    NS_ERROR("Animation sampled outside interval.");
+    return NS_ERROR_FAILURE;
+  }
+
+  if ((!IsToAnimation() && aValues.Length() < 2) || 
+      (IsToAnimation()  && aValues.Length() != 1)) {
+    NS_ERROR("Unexpected number of values.");
+    return NS_ERROR_FAILURE;
+  }
+  // End Sanity Checks
+
+  double fTime = double(mSampleTime);
+  double fDur = double(dur);
+
+  // Get the normalised progress through the simple duration
+  double simpleProgress = (fDur > 0.0) ? fTime / fDur : 0.0;
+
+  // Handle bad keytimes (where first != 0 and/or last != 1)
+  // See http://brian.sol1.net/svg/range-for-keytimes for more info.
+  if (HasAttr(nsGkAtoms::keyTimes)) {
+    double first = mKeyTimes[0];
+    if (first > 0.0 && simpleProgress < first) {
+      if (!IsToAnimation())
+        aResult = aValues[0];
+      return rv;
+    }
+    double last = mKeyTimes[mKeyTimes.Length() - 1];
+    if (last < 1.0 && simpleProgress >= last) {
+      if (IsToAnimation())
+        aResult = aValues[0];
+      else
+        aResult = aValues[aValues.Length() - 1];
+      return rv;
+    }
+  }
+
+  ScaleSimpleProgress(simpleProgress);
+
+  // Handle CALC_DISCRETE separately, because it's simple.
+  if (GetCalcMode() == CALC_DISCRETE) {
+    PRUint32 index = IsToAnimation() ? 0 :
+      (PRUint32) floor(simpleProgress * (aValues.Length()));
+    aResult = aValues[index];
+    return NS_OK;
+  } 
+
+  // Get the normalised progress between adjacent values
+  double intervalProgress;
+  if (IsToAnimation()) {
+    // Note: Don't need to do any special-casing for CALC_PACED here,
+    // because To-Animation doesn't use a values list, by definition.
+    from = &aBaseValue;
+    to = &aValues[0];
+    intervalProgress = simpleProgress;
+    ScaleIntervalProgress(intervalProgress, 0, 1);
+  } else {
+    if (GetCalcMode() == CALC_PACED) { 
+      rv = ComputePacedPosition(aValues, simpleProgress, intervalProgress,
+                                from, to);
+      NS_ENSURE_SUCCESS(rv,rv);
+    } else { // GetCalcMode() == CALC_LINEAR or GetCalcMode() == CALC_SPLINE
+      PRUint32 index = (PRUint32)floor(simpleProgress * (aValues.Length() - 1));
+      from = &aValues[index];
+      to = &aValues[index + 1];
+      intervalProgress = simpleProgress * (aValues.Length() - 1) - index;
+      ScaleIntervalProgress(intervalProgress, index, aValues.Length() - 1);
+    }
+  }
+  NS_ASSERTION(from, "NULL from-value during interpolation.");
+  NS_ASSERTION(to, "NULL to-value during interpolation.");
+
+  return from->Interpolate(*to, intervalProgress, aResult);
+}
+
+nsresult
+nsSMILAnimationFunction::AccumulateResult(const nsSMILValueArray& aValues,
+                                          nsSMILValue& aResult)
+{
+  if (!IsToAnimation() && GetAccumulate() && mRepeatIteration)
+  {
+    nsSMILValue lastValue = aValues[aValues.Length() - 1];
+
+    // If the target attribute type doesn't support addition, Add will
+    // fail and we leave aResult untouched.
+    aResult.Add(lastValue, mRepeatIteration);
+  }
+
+  return NS_OK;
+}
+
+/*
+ * Given the simple progress for a paced animation, this method:
+ *  - determines which two elements of the values array we're in between
+ *    (returned as aFrom and aTo)
+ *  - determines where we are between them
+ *    (returned as aIntervalProgress)
+ *
+ * Returns NS_OK, unless there's an error computing distances.
+ */
+nsresult
+nsSMILAnimationFunction::ComputePacedPosition(const nsSMILValueArray& aValues,
+                                              double aSimpleProgress,
+                                              double& aIntervalProgress,
+                                              const nsSMILValue*& aFrom,
+                                              const nsSMILValue*& aTo)
+{
+  NS_ASSERTION(0.0f <= aSimpleProgress && aSimpleProgress < 1.0f,
+               "aSimpleProgress is out of bounds.");
+  NS_ASSERTION(GetCalcMode() == CALC_PACED,
+               "Calling paced-specific function, but not in paced mode");
+
+  double totalDistance = ComputePacedTotalDistance(aValues);
+  if (totalDistance == COMPUTE_DISTANCE_ERROR)
+    return NS_ERROR_FAILURE;
+
+  // total distance we should have moved at this point in time.
+  // (called 'remainingDist' due to how it's used in loop below)
+  double remainingDist = aSimpleProgress * totalDistance;
+
+  // Must be satisfied, because totalDistance is a sum of (non-negative)
+  // distances, and aSimpleProgress is non-negative
+  NS_ASSERTION(remainingDist >= 0, "distance values must be non-negative");
+
+  // Find where remainingDist puts us in the list of values
+  // Note: We could optimize this next loop by caching the
+  // interval-distances in an array, but maybe that's excessive.
+  for (PRUint32 i = 0; i < aValues.Length() - 1; i++) {
+    // Note: The following assertion is valid because remainingDist should
+    // start out non-negative, and this loop never shaves off more than its
+    // current value.
+    NS_ASSERTION(remainingDist >= 0, "distance values must be non-negative");
+
+    double curIntervalDist;
+    nsresult tmpRv = aValues[i].ComputeDistance(aValues[i+1], curIntervalDist);
+    NS_ASSERTION(NS_SUCCEEDED(tmpRv), "ComputeDistance failed...?");
+    NS_ASSERTION(curIntervalDist >= 0, "distance values must be non-negative");
+    // Clamp distance value at 0, just in case ComputeDistance is evil.
+    curIntervalDist = PR_MAX(curIntervalDist, 0.0f);
+
+    if (remainingDist >= curIntervalDist) {
+      remainingDist -= curIntervalDist;
+    } else {
+      // NOTE: If we get here, then curIntervalDist necessarily is not 0. Why?
+      // Because this clause is only hit when remainingDist < curIntervalDist,
+      // and if curIntervalDist were 0, that would mean remainingDist would
+      // have to be < 0.  But that can't happen, because remainingDist (as
+      // a distance) is non-negative by definition.
+      NS_ASSERTION(curIntervalDist != 0,
+                   "We should never get here with this set to 0...");
+
+      // We found the right spot -- an interpolated position between
+      // values i and i+1.
+      aFrom = &aValues[i];
+      aTo = &aValues[i+1];
+      aIntervalProgress = remainingDist / curIntervalDist;
+      return NS_OK;
+    }
+  }
+
+  NS_NOTREACHED("shouldn't complete loop & get here -- if we do, "
+                "then aSimpleProgress was probably out of bounds.");
+  return NS_ERROR_FAILURE;
+}
+
+/*
+ * Computes & caches the total distance to be travelled by a paced animation.
+ *
+ * Returns NS_OK, unless there's an error computing distance.
+ */
+double
+nsSMILAnimationFunction::ComputePacedTotalDistance(
+    const nsSMILValueArray& aValues) const
+{
+  NS_ASSERTION(GetCalcMode() == CALC_PACED,
+               "Calling paced-specific function, but not in paced mode");
+
+  double totalDistance = 0.0;
+  for (PRUint32 i = 0; i < aValues.Length() - 1; i++) {
+    double tmpDist;
+    nsresult rv = aValues[i].ComputeDistance(aValues[i+1], tmpDist);
+    if (!NS_SUCCEEDED(rv)) {
+      NS_NOTREACHED("ComputeDistance failed...?");
+      return COMPUTE_DISTANCE_ERROR;
+    }
+
+    // Clamp distance value at 0, just in case ComputeDistance is evil.
+    NS_ASSERTION(tmpDist >= 0, "distance values must be non-negative");
+    tmpDist = PR_MAX(tmpDist, 0.0f);
+
+    totalDistance += tmpDist;
+  }
+
+  return totalDistance;
+}
+
+/*
+ * Scale the simple progress, taking into account any keyTimes.
+ */
+void
+nsSMILAnimationFunction::ScaleSimpleProgress(double& aProgress)
+{
+  if (!HasAttr(nsGkAtoms::keyTimes))
+    return;
+
+  PRUint32 numTimes = mKeyTimes.Length();
+
+  if (numTimes < 2)
+    return;
+
+  PRUint32 i = 0;
+  for (; i < numTimes - 2 && aProgress >= mKeyTimes[i+1]; ++i);
+
+  double& intervalStart = mKeyTimes[i];
+  double& intervalEnd   = mKeyTimes[i+1];
+
+  double intervalLength = intervalEnd - intervalStart;
+  if (intervalLength <= 0.0) {
+    aProgress = intervalStart;
+    return;
+  }
+
+  aProgress = (i + (aProgress - intervalStart) / intervalLength) * 
+         1.0 / double(numTimes - 1);
+}
+
+/*
+ * Scale the interval progress, taking into account any keySplines
+ * or discrete methods.
+ */
+void
+nsSMILAnimationFunction::ScaleIntervalProgress(double& aProgress,
+                                               PRUint32   aIntervalIndex,
+                                               PRUint32   aNumIntervals)
+{
+  if (GetCalcMode() != CALC_SPLINE)
+    return;
+
+  if (!HasAttr(nsGkAtoms::keySplines))
+    return;
+
+  NS_ASSERTION(aIntervalIndex >= 0 && 
+               aIntervalIndex < (PRUint32)mKeySplines.Length(),
+               "Invalid interval index.");
+  NS_ASSERTION(aNumIntervals >= 1, "Invalid number of intervals.");
+
+  if (aIntervalIndex < 0 ||
+      aIntervalIndex >= (PRUint32)mKeySplines.Length() ||
+      aNumIntervals < 1)
+    return;
+
+  nsSMILKeySpline const &spline = mKeySplines[aIntervalIndex];
+  aProgress = spline.GetSplineValue(aProgress);
+}
+
+PRBool
+nsSMILAnimationFunction::HasAttr(nsIAtom* aAttName) const
+{
+  return mAnimationElement->HasAnimAttr(aAttName);
+}
+
+const nsAttrValue*
+nsSMILAnimationFunction::GetAttr(nsIAtom* aAttName) const
+{
+  return mAnimationElement->GetAnimAttr(aAttName);
+}
+
+PRBool
+nsSMILAnimationFunction::GetAttr(nsIAtom* aAttName, nsAString& aResult) const
+{
+  return mAnimationElement->GetAnimAttr(aAttName, aResult);
+}
+
+/*
+ * A utility function to make querying an attribute that corresponds to an
+ * nsSMILValue a little neater.
+ *
+ * @param aAttName    The attribute name (in the global namespace)
+ * @param aSMILAttr   The SMIL attribute to perform the parsing
+ * @param aResult     The resulting nsSMILValue
+ *
+ * Returns PR_FALSE if a parse error occurred, otherwise returns PR_TRUE.
+ */
+PRBool
+nsSMILAnimationFunction::ParseAttr(nsIAtom* aAttName,
+                                   const nsISMILAttr& aSMILAttr,
+                                   nsSMILValue& aResult) const
+{
+  nsAutoString attValue;
+  if (GetAttr(aAttName, attValue)) {
+    nsresult rv = 
+      aSMILAttr.ValueFromString(attValue, mAnimationElement, aResult);
+    if (NS_FAILED(rv))
+      return PR_FALSE;
+  }
+  return PR_TRUE;
+}
+
+/*
+ * SMILANIM specifies the following rules for animation function values:
+ *
+ * (1) if values is set, it overrides everything
+ * (2) for from/to/by animation at least to or by must be specified, from on its
+ *     own (or nothing) is an error--which we will ignore
+ * (3) if both by and to are specified only to will be used, by will be ignored
+ * (4) if by is specified without from (by animation), forces additive behaviour
+ * (5) if to is specified without from (to animation), special care needs to be
+ *     taken when compositing animation as such animations are composited last.
+ *
+ * This helper method applies these rules to fill in the values list and to set
+ * some internal state.
+ */
+nsresult
+nsSMILAnimationFunction::GetValues(const nsISMILAttr& aSMILAttr,
+                                   nsSMILValueArray& aResult)
+{
+  if (!mAnimationElement)
+    return NS_ERROR_FAILURE;
+
+  nsSMILValueArray result;
+
+  // If "values" is set, use it
+  if (HasAttr(nsGkAtoms::values)) {
+    nsAutoString attValue;
+    GetAttr(nsGkAtoms::values, attValue);
+    nsresult rv = nsSMILParserUtils::ParseValues(attValue, mAnimationElement,
+                                                 aSMILAttr, result);
+    if (NS_FAILED(rv))
+      return rv;
+
+  // Else try to/from/by
+  } else {
+
+    PRBool parseOk = PR_TRUE;
+    nsSMILValue to, from, by;
+    parseOk &= ParseAttr(nsGkAtoms::to,   aSMILAttr, to);
+    parseOk &= ParseAttr(nsGkAtoms::from, aSMILAttr, from);
+    parseOk &= ParseAttr(nsGkAtoms::by,   aSMILAttr, by);
+
+    if (!parseOk)
+      return NS_ERROR_FAILURE;
+
+    result.SetCapacity(2);
+    if (!to.IsNull()) {
+      if (!from.IsNull()) {
+        result.AppendElement(from);
+        result.AppendElement(to);
+      } else {
+        result.AppendElement(to);
+      }
+    } else if (!by.IsNull()) {
+      nsSMILValue effectiveFrom(by.mType);
+      if (!from.IsNull())
+        effectiveFrom = from;
+      // Set values to 'from; from + by'
+      result.AppendElement(effectiveFrom);
+      nsSMILValue effectiveTo(effectiveFrom);
+      if (!effectiveTo.IsNull() && NS_SUCCEEDED(effectiveTo.Add(by))) {
+        result.AppendElement(effectiveTo);
+      } else {
+        // Using by-animation with non-additive type or bad base-value
+        return NS_ERROR_FAILURE;
+      }
+    } else {
+      // No values, no to, no by -- call it a day
+      return NS_ERROR_FAILURE;
+    }
+  }
+
+  // Check that we have the right number of keySplines and keyTimes
+  CheckKeyTimes(result.Length());
+  CheckKeySplines(result.Length());
+
+  result.SwapElements(aResult);
+  
+  return NS_OK;
+}
+
+inline PRBool
+nsSMILAnimationFunction::IsToAnimation() const
+{
+  return !HasAttr(nsGkAtoms::values) && 
+         HasAttr(nsGkAtoms::to) &&
+         !HasAttr(nsGkAtoms::from);
+}
+
+inline PRBool
+nsSMILAnimationFunction::IsAdditive() const
+{
+  /*
+   * Animation is additive if:
+   *
+   * (1) additive = "sum" (GetAdditive() == true), or
+   * (2) it is 'by animation' (by is set, from and values are not)
+   *
+   * Although animation is not additive if it is 'to animation'
+   */
+  PRBool isByAnimation = (!HasAttr(nsGkAtoms::values)
+                       &&  HasAttr(nsGkAtoms::by)
+                       && !HasAttr(nsGkAtoms::from));
+  return !IsToAnimation() && (GetAdditive() || isByAnimation);
+}
+
+/**
+ * Performs checks for the keyTimes attribute required by the SMIL spec but
+ * which depend on other attributes and therefore needs to be updated as
+ * dependent attributes are set.
+ */
+void
+nsSMILAnimationFunction::CheckKeyTimes(PRUint32 aNumValues)
+{
+  if (!HasAttr(nsGkAtoms::keyTimes))
+    return;
+
+  // attribute is ignored for calcMode = paced
+  if (GetCalcMode() == CALC_PACED) {
+    SET_FLAG(mErrorFlags, BF_KEY_TIMES, PR_FALSE);
+    return;
+  }
+
+  if (mKeyTimes.Length() < 1) {
+    // keyTimes isn't set or failed preliminary checks
+    SET_FLAG(mErrorFlags, BF_KEY_TIMES, PR_TRUE);
+    return;
+  }
+
+  // no. keyTimes == no. values
+  if ((mKeyTimes.Length() != aNumValues && !IsToAnimation()) ||
+      (IsToAnimation() && mKeyTimes.Length() != 2)) {
+    SET_FLAG(mErrorFlags, BF_KEY_TIMES, PR_TRUE);
+    return;
+  }
+
+  // special handling if there is only one keyTime. The spec doesn't say what to
+  // do in this case so we allow the keyTime to be either 0 or 1.
+  if (mKeyTimes.Length() == 1) {
+    double time = mKeyTimes[0];
+    SET_FLAG(mErrorFlags, BF_KEY_TIMES, !(time == 0.0 || time == 1.0));
+    return;
+  }
+
+  // According to the spec, the first value should be 0 and for linear or spline
+  // calcMode's the last value should be 1, but then an example is give with
+  // a spline calcMode and keyTimes "0.0; 0.7". So we don't bother checking
+  // the end-values here but just allow bad specs.
+
+  SET_FLAG(mErrorFlags, BF_KEY_TIMES, PR_FALSE);
+}
+
+void
+nsSMILAnimationFunction::CheckKeySplines(PRUint32 aNumValues)
+{
+  // attribute is ignored if calc mode is not spline
+  if (GetCalcMode() != CALC_SPLINE) {
+    SET_FLAG(mErrorFlags, BF_KEY_SPLINES, PR_FALSE);
+    return;
+  }
+
+  // calc mode is spline but the attribute is not set
+  if (!HasAttr(nsGkAtoms::keySplines)) {
+    SET_FLAG(mErrorFlags, BF_KEY_SPLINES, PR_FALSE);
+    return;
+  }
+
+  if (mKeySplines.Length() < 1) {
+    // keyTimes isn't set or failed preliminary checks
+    SET_FLAG(mErrorFlags, BF_KEY_SPLINES, PR_TRUE);
+    return;
+  }
+
+  // ignore splines if there's only one value
+  if (aNumValues == 1 && !IsToAnimation()) {
+    SET_FLAG(mErrorFlags, BF_KEY_SPLINES, PR_FALSE);
+    return;
+  }
+
+  // no. keySpline specs == no. values - 1
+  PRUint32 splineSpecs = mKeySplines.Length();
+  if ((splineSpecs != aNumValues - 1 && !IsToAnimation()) ||
+      (IsToAnimation() && splineSpecs != 1)) {
+    SET_FLAG(mErrorFlags, BF_KEY_SPLINES, PR_TRUE);
+    return;
+  }
+
+  SET_FLAG(mErrorFlags, BF_KEY_SPLINES, PR_FALSE);
+}
+
+//----------------------------------------------------------------------
+// Property getters
+
+PRBool
+nsSMILAnimationFunction::GetAccumulate() const
+{
+  const nsAttrValue* value = GetAttr(nsGkAtoms::accumulate);
+  if (!value)
+    return PR_FALSE;
+
+  return (value->GetEnumValue() == PR_TRUE);
+}
+
+PRBool
+nsSMILAnimationFunction::GetAdditive() const
+{
+  const nsAttrValue* value = GetAttr(nsGkAtoms::additive);
+  if (!value)
+    return PR_FALSE;
+
+  return (value->GetEnumValue() == PR_TRUE);
+}
+
+nsSMILAnimationFunction::nsSMILCalcMode
+nsSMILAnimationFunction::GetCalcMode() const
+{
+  const nsAttrValue* value = GetAttr(nsGkAtoms::calcMode);
+  if (!value)
+    return CALC_LINEAR;
+
+  return nsSMILCalcMode(value->GetEnumValue());
+}
+
+//----------------------------------------------------------------------
+// Property setters / un-setters:
+
+nsresult
+nsSMILAnimationFunction::SetAccumulate(const nsAString& aAccumulate,
+                                       nsAttrValue& aResult)
+{
+  mHasChanged = PR_TRUE;
+  PRBool parseResult = 
+    aResult.ParseEnumValue(aAccumulate, sAccumulateTable, PR_TRUE);
+  SET_FLAG(mErrorFlags, BF_ACCUMULATE, !parseResult);
+  return parseResult ? NS_OK : NS_ERROR_FAILURE;
+}
+
+void
+nsSMILAnimationFunction::UnsetAccumulate()
+{
+  SET_FLAG(mErrorFlags, BF_ACCUMULATE, PR_FALSE);
+  mHasChanged = PR_TRUE;
+}
+
+nsresult
+nsSMILAnimationFunction::SetAdditive(const nsAString& aAdditive,
+                                     nsAttrValue& aResult)
+{
+  mHasChanged = PR_TRUE;
+  PRBool parseResult 
+    = aResult.ParseEnumValue(aAdditive, sAdditiveTable, PR_TRUE);
+  SET_FLAG(mErrorFlags, BF_ADDITIVE, !parseResult);
+  return parseResult ? NS_OK : NS_ERROR_FAILURE;
+}
+
+void
+nsSMILAnimationFunction::UnsetAdditive()
+{
+  SET_FLAG(mErrorFlags, BF_ADDITIVE, PR_FALSE);
+  mHasChanged = PR_TRUE;
+}
+
+nsresult
+nsSMILAnimationFunction::SetCalcMode(const nsAString& aCalcMode,
+                                     nsAttrValue& aResult)
+{
+  mHasChanged = PR_TRUE;
+  PRBool parseResult 
+    = aResult.ParseEnumValue(aCalcMode, sCalcModeTable, PR_TRUE);
+  SET_FLAG(mErrorFlags, BF_CALC_MODE, !parseResult);
+  return parseResult ? NS_OK : NS_ERROR_FAILURE;
+}
+
+void
+nsSMILAnimationFunction::UnsetCalcMode()
+{
+  SET_FLAG(mErrorFlags, BF_CALC_MODE, PR_FALSE);
+  mHasChanged = PR_TRUE;
+}
+
+nsresult
+nsSMILAnimationFunction::SetKeySplines(const nsAString& aKeySplines,
+                                       nsAttrValue& aResult)
+{
+  mKeySplines.Clear();
+  aResult.SetTo(aKeySplines);
+
+  nsTArray<double> keySplines;
+  nsresult rv = nsSMILParserUtils::ParseKeySplines(aKeySplines, keySplines);
+
+  if (keySplines.Length() < 1 || keySplines.Length() % 4)
+    rv = NS_ERROR_FAILURE;
+
+  if (NS_SUCCEEDED(rv))
+  {
+    mKeySplines.SetCapacity(keySplines.Length() % 4);
+    for (PRUint32 i = 0; i < keySplines.Length() && NS_SUCCEEDED(rv); i += 4)
+    {
+      if (!mKeySplines.AppendElement(nsSMILKeySpline(keySplines[i],
+                                                     keySplines[i+1],
+                                                     keySplines[i+2],
+                                                     keySplines[i+3]))) {
+        rv = NS_ERROR_OUT_OF_MEMORY;
+      }
+    }
+  }
+
+  mHasChanged = PR_TRUE;
+
+  return rv;
+}
+
+void
+nsSMILAnimationFunction::UnsetKeySplines()
+{
+  mKeySplines.Clear();
+  SET_FLAG(mErrorFlags, BF_KEY_SPLINES, PR_FALSE);
+  mHasChanged = PR_TRUE;
+}
+
+nsresult
+nsSMILAnimationFunction::SetKeyTimes(const nsAString& aKeyTimes,
+                                     nsAttrValue& aResult)
+{
+  mKeyTimes.Clear();
+  aResult.SetTo(aKeyTimes);
+
+  nsresult rv = nsSMILParserUtils::ParseKeyTimes(aKeyTimes, mKeyTimes);
+
+  if (NS_SUCCEEDED(rv) && mKeyTimes.Length() < 1)
+    rv = NS_ERROR_FAILURE;
+
+  if (NS_FAILED(rv))
+    mKeyTimes.Clear();
+
+  mHasChanged = PR_TRUE;
+
+  return NS_OK;
+}
+
+void
+nsSMILAnimationFunction::UnsetKeyTimes()
+{
+  mKeyTimes.Clear();
+  SET_FLAG(mErrorFlags, BF_KEY_TIMES, PR_FALSE);
+  mHasChanged = PR_TRUE;
+}
new file mode 100644
--- /dev/null
+++ b/content/smil/nsSMILAnimationFunction.h
@@ -0,0 +1,364 @@
+/* -*- 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 SMIL module.
+ *
+ * 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>
+ *
+ * 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 NS_SMILANIMATIONFUNCTION_H_
+#define NS_SMILANIMATIONFUNCTION_H_
+
+#include "nsISMILAttr.h"
+#include "nsGkAtoms.h"
+#include "nsString.h"
+#include "nsSMILTimeValue.h"
+#include "nsSMILKeySpline.h"
+#include "nsSMILValue.h"
+#include "nsAutoPtr.h"
+#include "nsTArray.h"
+#include "nsAttrValue.h"
+#include "nsSMILTypes.h"
+
+class nsISMILAnimationElement;
+
+//----------------------------------------------------------------------
+// nsSMILAnimationFunction
+//
+// The animation function calculates animation values. It it is provided with
+// time parameters (sample time, repeat iteration etc.) and it uses this to
+// build an appropriate animation value by performing interpolation and
+// addition operations.
+//
+// It is responsible for implementing the animation parameters of an animation
+// element (e.g. from, by, to, values, calcMode, additive, accumulate, keyTimes,
+// keySplines)
+//
+class nsSMILAnimationFunction
+{
+public:
+  nsSMILAnimationFunction();
+
+  /*
+   * Sets the owning animation element which this class uses to query attribute
+   * values and compare document positions.
+   */
+  void SetAnimationElement(nsISMILAnimationElement* aAnimationElement);
+
+  /*
+   * Sets animation-specific attributes (or marks them dirty, in the case
+   * of from/to/by/values).
+   *
+   * @param aAttribute The attribute being set
+   * @param aValue     The updated value of the attribute.
+   * @param aResult    The nsAttrValue object that may be used for storing the
+   *                   parsed result.
+   * @param aParseResult  Outparam used for reporting parse errors. Will be set
+   *                      to NS_OK if everything succeeds.
+   * @returns PR_TRUE if aAttribute is a recognized animation-related
+   *          attribute; PR_FALSE otherwise.
+   */
+  virtual PRBool SetAttr(nsIAtom* aAttribute, const nsAString& aValue,
+                         nsAttrValue& aResult, nsresult* aParseResult = nsnull);
+
+  /*
+   * Unsets the given attribute.
+   *
+   * @returns PR_TRUE if aAttribute is a recognized animation-related
+   *          attribute; PR_FALSE otherwise.
+   */
+  virtual PRBool UnsetAttr(nsIAtom* aAttribute);
+
+  /**
+   * Indicate a new sample has occurred.
+   *
+   * @param aSampleTime The sample time for this timed element expressed in
+   *                    simple time.
+   * @param aSimpleDuration The simple duration for this timed element.
+   * @param aRepeatIteration  The repeat iteration for this sample. The first
+   *                          iteration has a value of 0.
+   */
+  void SampleAt(nsSMILTime aSampleTime,
+                const nsSMILTimeValue& aSimpleDuration,
+                PRUint32 aRepeatIteration);
+
+  /**
+   * Indicate to sample using the last value defined for the animation function.
+   * This value is not normally sampled due to the end-point exclusive timing
+   * model but only occurs when the fill mode is "freeze" and the active
+   * duration is an even multiple of the simple duration.
+   *
+   * @param aRepeatIteration  The repeat iteration for this sample. The first
+   *                          iteration has a value of 0.
+   */
+  void SampleLastValue(PRUint32 aRepeatIteration);
+
+  /**
+   * Indicate that this animation is now active. This is used to instruct the
+   * animation function that it should now add its result to the animation
+   * sandwich. The begin time is also provided for proper prioritization of
+   * animation functions, and for this reason, this method must be called
+   * before either of the Sample methods.
+   *
+   * @param aBeginTime The begin time for the newly active interval.
+   */
+  void Activate(nsSMILTime aBeginTime);
+
+  /**
+   * Indicate that this animation is no longer active. This is used to instruct
+   * the animation function that it should no longer add its result to the
+   * animation sandwich.
+   *
+   * @param aIsFrozen True if this animation should continue to contribute to
+   *                  the animation sandwich using the most recent sample
+   *                  parameters.
+   */
+  void Inactivate(PRBool aIsFrozen);
+
+  /**
+   * Combines the result of this animation function for the last sample with the
+   * specified value.
+   *
+   * @param aSMILAttr This animation's target attribute. Used here for
+   *                  doing attribute-specific parsing of from/to/by/values.
+   *
+   * @param aResult   The value to compose with.
+   */
+  void ComposeResult(const nsISMILAttr& aSMILAttr, nsSMILValue& aResult);
+
+  /**
+   * Returns the relative priority of this animation to another. The priority is
+   * used for determining the position of the animation in the animation
+   * sandwich -- higher priority animations are applied on top of lower
+   * priority animations.
+   *
+   * @return  -1 if this animation has lower priority or 1 if this animation has
+   *          higher priority
+   *
+   * This method should never return any other value, including 0.
+   */
+  PRInt8 CompareTo(const nsSMILAnimationFunction* aOther) const;
+
+  /*
+   * The following methods are provided so that the compositor can optimize its
+   * operations by only composing those animation that will affect the final
+   * result.
+   */
+ 
+  /**
+   * Indicates if the animation is currently active. Inactive animations will
+   * not contribute to the composed result.
+   *
+   * @return  True if the animation active, false otherwise.
+   */
+  PRBool IsActive() const
+  {
+    /* 
+     * - Frozen animations should be considered active for the purposes of
+     * compositing.
+     * - This function does not assume that our nsSMILValues (by/from/to/values)
+     * have already been parsed.
+     */
+    return (mIsActive || mIsFrozen);
+  }
+
+  /**
+   * Indicates if this animation will replace the passed in result rather than
+   * adding to it. Animations that replace the underlying value may be called
+   * without first calling lower priority animations.
+   *
+   * @return  True if the animation will replace, false if it will add or
+   *          otherwise build on the passed in value.
+   */
+  PRBool WillReplace() const;
+
+  /**
+   * Indicates if the parameters for this animation have changed since the last
+   * time it was composited. This allows rendering to be performed only when
+   * necessary, particularly when no animations are active.
+   *
+   * Note that the caller is responsible for determining if the animation target
+   * has changed.
+   *
+   * @return  True if the animation parameters have changed, false otherwise.
+   */
+  PRBool HasChanged() const;
+
+  // Comparator utility class, used for sorting nsSMILAnimationFunctions
+  class Comparator {
+    public:
+      PRBool Equals(const nsSMILAnimationFunction* aElem1,
+                    const nsSMILAnimationFunction* aElem2) const {
+        return (aElem1->CompareTo(aElem2) == 0);
+      }
+      PRBool LessThan(const nsSMILAnimationFunction* aElem1,
+                      const nsSMILAnimationFunction* aElem2) const {
+        return (aElem1->CompareTo(aElem2) < 0);
+      }
+  };
+
+protected:
+  // Typedefs
+  typedef nsTArray<nsSMILValue> nsSMILValueArray;
+
+  // Types
+  enum nsSMILCalcMode
+  {
+    CALC_LINEAR,
+    CALC_DISCRETE,
+    CALC_PACED,
+    CALC_SPLINE
+  };
+
+  // Used for sorting nsSMILAnimationFunctions
+  nsSMILTime GetBeginTime() const { return mBeginTime; }
+
+  // Property getters
+  PRBool                 GetAccumulate() const;
+  PRBool                 GetAdditive() const;
+  virtual nsSMILCalcMode GetCalcMode() const;
+
+  // Property setters
+  nsresult SetAccumulate(const nsAString& aAccumulate, nsAttrValue& aResult);
+  nsresult SetAdditive(const nsAString& aAdditive, nsAttrValue& aResult);
+  nsresult SetCalcMode(const nsAString& aCalcMode, nsAttrValue& aResult);
+  nsresult SetKeyTimes(const nsAString& aKeyTimes, nsAttrValue& aResult);
+  nsresult SetKeySplines(const nsAString& aKeySplines, nsAttrValue& aResult);
+
+  // Property un-setters
+  void     UnsetAccumulate();
+  void     UnsetAdditive();
+  void     UnsetCalcMode();
+  void     UnsetKeyTimes();
+  void     UnsetKeySplines();
+
+  // Helpers
+  nsresult InterpolateResult(const nsSMILValueArray& aValues,
+                             nsSMILValue& aResult,
+                             nsSMILValue& aBaseValue);
+  nsresult AccumulateResult(const nsSMILValueArray& aValues,
+                            nsSMILValue& aResult);
+
+  nsresult ComputePacedPosition(const nsSMILValueArray& aValues,
+                                double aSimpleProgress,
+                                double& aIntervalProgress,
+                                const nsSMILValue*& aFrom,
+                                const nsSMILValue*& aTo);
+  double   ComputePacedTotalDistance(const nsSMILValueArray& aValues) const;
+
+  void     ScaleSimpleProgress(double& aProgress);
+  void     ScaleIntervalProgress(double& aProgress, PRUint32 aIntervalIndex,
+                                 PRUint32 aNumIntervals);
+
+  // Convenience attribute getters -- use these instead of querying
+  // mAnimationElement as these may need to be overridden by subclasses
+  virtual PRBool             HasAttr(nsIAtom* aAttName) const;
+  virtual const nsAttrValue* GetAttr(nsIAtom* aAttName) const;
+  virtual PRBool             GetAttr(nsIAtom* aAttName,
+                                     nsAString& aResult) const;
+
+  PRBool   ParseAttr(nsIAtom* aAttName, const nsISMILAttr& aSMILAttr,
+                     nsSMILValue& aResult) const;
+  nsresult GetValues(const nsISMILAttr& aSMILAttr,
+                     nsSMILValueArray& aResult);
+  void     UpdateValuesArray();
+  PRBool   IsToAnimation() const;
+  PRBool   IsAdditive() const;
+  void     CheckKeyTimes(PRUint32 aNumValues);
+  void     CheckKeySplines(PRUint32 aNumValues);
+
+
+  // Members
+  // -------
+
+  static nsAttrValue::EnumTable sAdditiveTable[];
+  static nsAttrValue::EnumTable sCalcModeTable[];
+  static nsAttrValue::EnumTable sAccumulateTable[];
+
+  nsTArray<double>              mKeyTimes;
+  nsTArray<nsSMILKeySpline>     mKeySplines;
+
+  PRPackedBool                  mIsActive;
+  PRPackedBool                  mIsFrozen;
+
+  // These are the parameters provided by the previous sample. Currently we
+  // perform lazy calculation. That is, we only calculate the result if and when
+  // instructed by the compositor. This allows us to apply the result directly
+  // to the animation value and allows the compositor to filter out functions
+  // that it determines will not contribute to the final result.
+  nsSMILTime                    mSampleTime; // sample time within simple dur
+  nsSMILTimeValue               mSimpleDuration;
+  PRUint32                      mRepeatIteration;
+  PRPackedBool                  mLastValue;
+  PRPackedBool                  mHasChanged;
+
+  nsSMILTime                    mBeginTime; // document time
+  
+  // The owning animation element. This is used for sorting based on document
+  // position and for fetching attribute values stored in the element.
+  // Raw pointer is OK here, because this nsSMILAnimationFunction can't outlive
+  // its owning animation element.
+  nsISMILAnimationElement*      mAnimationElement;
+
+  // Which attributes have been set but have had errors. This is not used for
+  // all attributes but only those which have specified error behaviour
+  // associated with them.
+  PRUint16                      mErrorFlags;
+
+  // This is for the very specific case where we have a 'to' animation that is
+  // frozen part way through the simple duration and there are other active
+  // lower-priority animations targetting the same attribute. In this case
+  // SMILANIM 3.3.6 says:
+  //
+  //   The value for F(t) when a to-animation is frozen (at the end of the
+  //   simple duration) is just the to value. If a to-animation is frozen
+  //   anywhere within the simple duration (e.g., using a repeatCount of "2.5"),
+  //   the value for F(t) when the animation is frozen is the value computed for
+  //   the end of the active duration. Even if other, lower priority animations
+  //   are active while a to-animation is frozen, the value for F(t) does not
+  //   change.
+  //
+  // To implement this properly we'd need to force a resample of all the lower
+  // priority animations at the active end of this animation--something which
+  // would introduce unwanted coupling between the timing and animation model.
+  // Instead we just save the value calculated when this animation is frozen (in
+  // which case this animation will be sampled at the active end and the lower
+  // priority animations should be sampled at a time pretty close to this,
+  // provided we have a reasonable frame rate and we aren't seeking).
+  //
+  // @see
+  // http://www.w3.org/TR/2001/REC-smil-animation-20010904/#FromToByAndAdditive
+  nsSMILValue                   mFrozenValue;
+};
+
+#endif // NS_SMILANIMATIONFUNCTION_H_
new file mode 100644
--- /dev/null
+++ b/content/smil/nsSMILCompositor.cpp
@@ -0,0 +1,178 @@
+/* -*- 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 SMIL module.
+ *
+ * The Initial Developer of the Original Code is the Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Daniel Holbert <dholbert@mozilla.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
+ * 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 "nsSMILCompositor.h"
+#include "nsHashKeys.h"
+
+// nsSMILCompositorKey methods
+inline PRBool
+nsSMILCompositorKey::Equals(const nsSMILCompositorKey &aOther) const
+{
+  return (aOther.mElement       == mElement &&
+          aOther.mAttributeName == mAttributeName &&
+          aOther.mIsCSS         == mIsCSS);
+}
+
+// PLDHashEntryHdr methods
+PRBool
+nsSMILCompositor::KeyEquals(KeyTypePointer aKey) const
+{
+  return aKey && aKey->Equals(mKey);
+}
+
+/*static*/ PLDHashNumber
+nsSMILCompositor::HashKey(KeyTypePointer aKey)
+{
+  // Combine the 3 values into one numeric value, which will be hashed
+  const char *attrName = nsnull;
+  aKey->mAttributeName->GetUTF8String(&attrName);
+  return NS_PTR_TO_UINT32(aKey->mElement.get()) +
+    HashString(attrName) +
+    (aKey->mIsCSS ? 1 : 0);
+}
+
+// Cycle-collection support
+void
+nsSMILCompositor::Traverse(nsCycleCollectionTraversalCallback* aCallback)
+{
+  if (!mKey.mElement)
+    return;
+
+  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback, "Compositor mKey.mElement");
+  aCallback->NoteXPCOMChild(mKey.mElement);
+}
+
+// Other methods
+void
+nsSMILCompositor::AddAnimationFunction(nsSMILAnimationFunction* aFunc)
+{
+  if (aFunc) {
+    mAnimationFunctions.AppendElement(aFunc);
+  }
+}
+
+void
+nsSMILCompositor::ComposeAttribute()
+{
+  if (!mKey.mElement)
+    return;
+
+  // FIRST: Get the nsISMILAttr (to grab base value from, and to eventually
+  // give animated value to)
+  nsAutoPtr<nsISMILAttr> smilAttr;
+  if (mKey.mIsCSS) {
+    // XXX Look up style system for the CSS property. The set of CSS properties
+    // should be the same for all elements so we don't need to query the element
+    // itself.
+  } else {
+    smilAttr = mKey.mElement->GetAnimatedAttr(mKey.mAttributeName);
+  }
+
+  if (!smilAttr) {
+    // Target attribute not found
+    return;
+  }
+
+  // SECOND: Sort the animationFunctions, to prepare for compositing.
+  nsSMILAnimationFunction::Comparator comparator;
+  mAnimationFunctions.Sort(comparator);
+
+  // THIRD: Step backwards through animation functions to find out
+  // which ones we actually care about.
+  //  PRBool changed = PR_FALSE; // XXXdholbert removing until we have
+                                 //  HasChangedTarget
+  PRUint32 length = mAnimationFunctions.Length();
+  PRUint32 i;
+  for (i = length; i > 0; --i) {
+    nsSMILAnimationFunction* curAnimFunc = mAnimationFunctions[i-1];
+    // XXXdholbert we need to add another function
+    // nsSMILAnimationFunction::HasChangedTarget(elem, smilAttr, isCSS) that
+    // we call here (in addition to HasChanged(), because even if function
+    // value hasn't changed, its target might have.
+    // For this to work, the nsSMILAnimationFunction needs to cache its last
+    // elem/smilAttr/isCSS values, and then check them against the new values
+    // here.
+    /*
+    if (!changed && curAnimFunc->HasChanged()) {
+      changed = PR_TRUE;
+    }
+    */
+    
+    if (curAnimFunc->WillReplace()) {
+      --i;
+      break;
+    }
+  }
+  // NOTE: 'i' is now the index of the first animation function that we need
+  // to use in compositing.
+
+  // if (!changed) // XXXdholbert removing until we have HasChangedTarget
+  //  return;
+
+  // FOURTH: Compose animation functions (starting with base value)
+  nsSMILValue resultValue = smilAttr->GetBaseValue();
+  if (resultValue.IsNull()) {
+    NS_WARNING("nsISMILAttr::GetBaseValue failed");
+    return;
+  }
+  for (; i < length; ++i) {
+    nsSMILAnimationFunction* curAnimFunc = mAnimationFunctions[i];
+    if (curAnimFunc) {
+      curAnimFunc->ComposeResult(*smilAttr, resultValue);
+    }
+  }
+
+  // FIFTH: Set the animated value to the final composited result.
+  nsresult rv = smilAttr->SetAnimValue(resultValue);
+  if (NS_FAILED(rv)) {
+    NS_WARNING("nsISMILAttr::SetAnimValue failed");
+  } 
+}
+
+/*static*/ void
+nsSMILCompositor::ComposeAttributes(nsSMILCompositorTable& aCompositorTable)
+{
+  aCompositorTable.EnumerateEntries(DoComposeAttribute, nsnull);
+}
+
+/*static*/ PR_CALLBACK PLDHashOperator
+nsSMILCompositor::DoComposeAttribute(nsSMILCompositor* aCompositor,
+                                     void* /*aData*/)
+{ 
+  aCompositor->ComposeAttribute();
+  return PL_DHASH_NEXT;
+}
new file mode 100644
--- /dev/null
+++ b/content/smil/nsSMILCompositor.h
@@ -0,0 +1,122 @@
+/* -*- 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 SMIL module.
+ *
+ * The Initial Developer of the Original Code is the Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Daniel Holbert <dholbert@mozilla.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
+ * 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 NS_SMILCOMPOSITOR_H_
+#define NS_SMILCOMPOSITOR_H_
+
+#include "nsIContent.h"
+#include "nsTHashtable.h"
+#include "nsAutoPtr.h"
+#include "nsString.h"
+#include "nsSMILAnimationFunction.h"
+#include "nsSMILCompositorTable.h"
+#include "pldhash.h"
+
+//----------------------------------------------------------------------
+// nsSMILCompositorKey
+//
+// Hash Key: Animated Element, Attribute Name & Type (CSS vs. XML)
+//
+// NOTE: Need a nsRefPtr to the element, because nsSMILCompositors are kept
+// around for 1 sample after they're used, and they need to make sure their
+// target isn't deleted.
+
+struct nsSMILCompositorKey
+{
+  PRBool    Equals(const nsSMILCompositorKey &aOther) const;
+
+  nsRefPtr<nsIContent> mElement;
+  nsRefPtr<nsIAtom>    mAttributeName; // XXX need to consider namespaces here
+  PRPackedBool         mIsCSS;
+};
+
+//----------------------------------------------------------------------
+// nsSMILCompositor
+//
+// Performs the composition of the animation sandwich by combining the results
+// of a series animation functions according to the rules of SMIL composition
+// including prioritising animations.
+
+class nsSMILCompositor : public PLDHashEntryHdr
+{
+public:
+  typedef const nsSMILCompositorKey& KeyType;
+  typedef const nsSMILCompositorKey* KeyTypePointer;
+
+  nsSMILCompositor(KeyTypePointer aKey) : mKey(*aKey) { }
+  nsSMILCompositor(const nsSMILCompositor& toCopy)
+    : mKey(toCopy.mKey),
+      mAnimationFunctions(toCopy.mAnimationFunctions)
+  { }
+  ~nsSMILCompositor() { }
+
+  // PLDHashEntryHdr methods
+  KeyType GetKey() const { return mKey; }
+  PRBool KeyEquals(KeyTypePointer aKey) const;
+  static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
+  static PLDHashNumber HashKey(KeyTypePointer aKey);
+  enum { ALLOW_MEMMOVE = PR_FALSE };
+
+  // Adds the given animation function to this Compositor's list of functions
+  void AddAnimationFunction(nsSMILAnimationFunction* aFunc);
+  
+  // Calls ComposeAttribute() on each nsSMILCompositor in the given hashset
+  static void ComposeAttributes(nsSMILCompositorTable& aCompositorTable);
+
+  // Cycle-collection support
+  void Traverse(nsCycleCollectionTraversalCallback* aCallback);
+
+ private:
+  // Composes the attribute's current value with the list of animation
+  // functions, and assigns the resulting value to this compositor's target
+  // attribute.
+  void ComposeAttribute();
+
+  // Static callback methods
+  PR_STATIC_CALLBACK(PLDHashOperator) DoComposeAttribute(
+      nsSMILCompositor* aCompositor, void *aData);
+
+  // The hash key (element, attribute name, isCSS)
+  nsSMILCompositorKey  mKey;
+
+  // Hash Value: List of animation functions that animate the specified
+  // attribute
+  // ---------------------------------------------------------------
+  nsTArray<nsSMILAnimationFunction*> mAnimationFunctions;
+};
+
+#endif // NS_SMILCOMPOSITOR_H_
new file mode 100644
--- /dev/null
+++ b/content/smil/nsSMILCompositorTable.h
@@ -0,0 +1,55 @@
+/* -*- 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 SMIL module.
+ *
+ * The Initial Developer of the Original Code is the Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Daniel Holbert <dholbert@mozilla.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
+ * 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 NS_SMILCOMPOSITORTABLE_H_
+#define NS_SMILCOMPOSITORTABLE_H_
+
+#include "nsTHashtable.h"
+
+//----------------------------------------------------------------------
+// nsSMILCompositorTable : A hashmap of nsSMILCompositors
+//
+// This is just a forward-declaration because it is included in
+// nsSMILAnimationController which is used in nsDocument. We don't want to
+// expose all of nsSMILCompositor or otherwise any changes to it will mean the
+// whole world will need to be rebuilt.
+
+class nsSMILCompositor;
+typedef nsTHashtable<nsSMILCompositor> nsSMILCompositorTable;
+struct nsSMILCompositorKey;
+
+#endif // NS_SMILCOMPOSITORTABLE_H_
new file mode 100644
--- /dev/null
+++ b/content/smil/nsSMILFloatType.cpp
@@ -0,0 +1,118 @@
+/* -*- 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 SMIL module.
+ *
+ * The Initial Developer of the Original Code is Brian Birtles.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Robert O'Callahan <roc+moz@cs.cmu.edu>
+ *   Brian Birtles <birtles@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
+ * 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 "nsSMILFloatType.h"
+#include "nsSMILValue.h"
+#include "nsDebug.h"
+#include <math.h>
+
+/*static*/ nsSMILFloatType nsSMILFloatType::sSingleton;
+
+nsresult
+nsSMILFloatType::Init(nsSMILValue& aValue) const
+{
+  NS_PRECONDITION(aValue.mType == this || aValue.IsNull(),
+    "Unexpected value type");
+  aValue.mU.mDouble = 0.0;
+  aValue.mType = this;
+  return NS_OK;
+}
+
+void
+nsSMILFloatType::Destroy(nsSMILValue& aValue) const
+{
+  NS_PRECONDITION(aValue.mType == this, "Unexpected SMIL value.");
+  aValue.mU.mDouble = 0.0;
+  aValue.mType      = &nsSMILNullType::sSingleton;
+}
+
+nsresult
+nsSMILFloatType::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.mDouble = aSrc.mU.mDouble;
+  return NS_OK;
+}
+
+nsresult
+nsSMILFloatType::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");
+  aDest.mU.mDouble += aValueToAdd.mU.mDouble * aCount;
+  return NS_OK;
+}
+
+nsresult
+nsSMILFloatType::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");
+  
+  const double &from = aFrom.mU.mDouble;
+  const double &to   = aTo.mU.mDouble;
+
+  aDistance = fabs(to - from);
+
+  return NS_OK;
+}
+
+nsresult
+nsSMILFloatType::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.");
+
+  const double &startVal = aStartVal.mU.mDouble;
+  const double &endVal   = aEndVal.mU.mDouble;
+
+  aResult.mU.mDouble = (startVal + (endVal - startVal) * aUnitDistance);
+
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/content/smil/nsSMILFloatType.h
@@ -0,0 +1,66 @@
+/* -*- 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 SMIL module.
+ *
+ * The Initial Developer of the Original Code is Brian Birtles.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Robert O'Callahan <roc+moz@cs.cmu.edu>
+ *   Brian Birtles <birtles@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
+ * 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 NS_SMILFLOATTYPE_H_
+#define NS_SMILFLOATTYPE_H_
+
+#include "nsISMILType.h"
+
+class nsSMILFloatType : 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 nsSMILFloatType sSingleton;
+
+private:
+  nsSMILFloatType() {}
+};
+
+#endif // NS_SMILFLOATTYPE_H_
new file mode 100644
--- /dev/null
+++ b/content/smil/nsSMILInstanceTime.cpp
@@ -0,0 +1,59 @@
+/* -*- 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 SMIL module.
+ *
+ * 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>
+ *
+ * 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 "nsSMILInstanceTime.h"
+#include "nsSMILTimeValueSpec.h"
+#include "nsSMILTimeValue.h"
+
+//----------------------------------------------------------------------
+// Implementation
+
+nsSMILInstanceTime::nsSMILInstanceTime(const nsSMILTimeValue& aTime,
+                                       nsSMILTimeValueSpec* /*aCreator*/,
+                                       PRBool aClearOnReset /*=false*/)
+  : mTime(aTime), // Copy the time
+    mClearOnReset(aClearOnReset)
+{
+  // XXX
+}
+
+nsSMILInstanceTime::~nsSMILInstanceTime()
+{
+  // XXXdholbert When we add support for syncbase timing, we'll
+  // need to remove this nsSMILInstanceTime from its timebase
+  // here.
+}
new file mode 100644
--- /dev/null
+++ b/content/smil/nsSMILInstanceTime.h
@@ -0,0 +1,95 @@
+/* -*- 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 SMIL module.
+ *
+ * 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>
+ *
+ * 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 NS_SMILINSTANCETIME_H_
+#define NS_SMILINSTANCETIME_H_
+
+#include "nsISupports.h"
+#include "nsSMILTimeValue.h"
+#include "nsWeakReference.h"
+#include "nsAutoPtr.h"
+
+class nsSMILTimeValueSpec;
+
+//----------------------------------------------------------------------
+// nsSMILInstanceTime
+//
+// An instant in document simple time that may be used in creating a new
+// interval
+//
+// For an overview of how this class is related to other SMIL time classes see
+// the documentstation in nsSMILTimeValue.h
+
+class nsSMILInstanceTime
+{
+public:
+  nsSMILInstanceTime(const nsSMILTimeValue& aTime,
+                     nsSMILTimeValueSpec* aCreator,
+                     PRBool aClearOnReset = PR_FALSE);
+
+  ~nsSMILInstanceTime();
+
+  const nsSMILTimeValue& Time() const { return mTime; }
+
+  PRBool                 ClearOnReset() const { return mClearOnReset; }
+
+  // void DependentUpdate(const nsSMILTimeValue& aNewTime); -- NOT YET IMPL.
+
+  // Used by nsTArray::Sort
+  class Comparator {
+    public:
+      PRBool Equals(const nsSMILInstanceTime& aElem1,
+                    const nsSMILInstanceTime& aElem2) const {
+        return (aElem1.Time().CompareTo(aElem2.Time()) == 0);
+      }
+      PRBool LessThan(const nsSMILInstanceTime& aElem1,
+                      const nsSMILInstanceTime& aElem2) const {
+        return (aElem1.Time().CompareTo(aElem2.Time()) < 0);
+      }
+  };
+
+protected:
+  nsSMILTimeValue     mTime;
+
+  /**
+   * Indicates if this instance time should be removed when the owning timed
+   * element is reset. True for events and DOM calls.
+   */
+  PRPackedBool        mClearOnReset;
+};
+
+#endif // NS_SMILINSTANCETIME_H_
new file mode 100644
--- /dev/null
+++ b/content/smil/nsSMILInterval.h
@@ -0,0 +1,60 @@
+/* -*- 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 SMIL module.
+ *
+ * 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>
+ *
+ * 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 NS_SMILINTERVAL_H_
+#define NS_SMILINTERVAL_H_
+
+#include "nsSMILTimeValue.h"
+
+//----------------------------------------------------------------------
+// nsSMILInterval class
+//
+// This class is essentially a structure consisting of a begin and end time. It
+// is used for representing the current interval and also for storing past
+// intervals for the purpose of hyperlinking back in time. 
+//
+// For an overview of how this class is related to other SMIL time classes see
+// the documentstation in nsSMILTimeValue.h
+
+class nsSMILInterval
+{
+public:
+  nsSMILTimeValue mBegin;
+  nsSMILTimeValue mEnd;
+};
+
+#endif // NS_SMILINTERVAL_H_
new file mode 100644
--- /dev/null
+++ b/content/smil/nsSMILKeySpline.cpp
@@ -0,0 +1,120 @@
+/* -*- 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 SMIL module.
+ *
+ * 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>
+ *
+ * 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 "nsSMILKeySpline.h"
+#include <math.h>
+
+#define NEWTON_ITERATIONS   4
+
+const double nsSMILKeySpline::kSampleStepSize = 
+                                        1.0 / double(kSplineTableSize - 1);
+
+nsSMILKeySpline::nsSMILKeySpline(double aX1,
+                                 double aY1,
+                                 double aX2,
+                                 double aY2)
+: mX1(aX1),
+  mY1(aY1),
+  mX2(aX2),
+  mY2(aY2)
+{
+  if (mX1 != mY1 || mX2 != mY2)
+    CalcSampleValues();
+}
+
+double
+nsSMILKeySpline::GetSplineValue(double aX) const
+{
+  if (mX1 == mY1 && mX2 == mY2)
+    return aX;
+
+  return CalcBezier(GetTForX(aX), mY1, mY2);
+}
+
+void
+nsSMILKeySpline::CalcSampleValues()
+{
+  for (int i = 0; i < kSplineTableSize; ++i) {
+    mSampleValues[i] = CalcBezier(double(i) * kSampleStepSize, mX1, mX2);
+  }
+}
+
+/*static*/ double
+nsSMILKeySpline::CalcBezier(double aT,
+                            double aA1,
+                            double aA2)
+{
+  return A(aA1, aA2) * pow(aT,3) + B(aA1, aA2)*aT*aT + C(aA1) * aT;
+}
+
+/*static*/ double
+nsSMILKeySpline::GetSlope(double aT,
+                          double aA1,
+                          double aA2)
+{
+  double denom = (3.0 * A(aA1, aA2)*aT*aT + 2.0 * B(aA1, aA2) * aT + C(aA1)); 
+  return (denom == 0.0) ? 0.0 : 1.0 / denom;
+}
+
+double
+nsSMILKeySpline::GetTForX(double aX) const
+{
+  int i;
+
+  // Get an initial guess.
+  //
+  // Note: This is better than just taking x as our initial guess as cases such
+  // as where the control points are (1, 1), (0, 0) will take some 20 iterations
+  // to converge to a good accuracy. By taking an initial guess in this way we
+  // only need 3~4 iterations depending on the size of the table.
+  for (i = 0; i < kSplineTableSize - 2 && mSampleValues[i] < aX; ++i);
+  double currentT = 
+    double(i) * kSampleStepSize + (aX - mSampleValues[i]) * kSampleStepSize;
+
+  // Refine with Newton-Raphson iteration
+  for (i = 0; i < NEWTON_ITERATIONS; ++i) {
+    double currentX = CalcBezier(currentT, mX1, mX2);
+    double currentSlope = GetSlope(currentT, mX1, mX2);
+
+    if (currentSlope == 0.0)
+      return currentT;
+
+    currentT -= (currentX - aX) * currentSlope;
+  }
+
+  return currentT;
+}
new file mode 100644
--- /dev/null
+++ b/content/smil/nsSMILKeySpline.h
@@ -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 SMIL module.
+ *
+ * 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>
+ *
+ * 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 NS_SMILKEYSPLINE_H_
+#define NS_SMILKEYSPLINE_H_
+
+#include "prtypes.h"
+
+/**
+ * Utility class to provide scaling defined in a keySplines element.
+ */
+class nsSMILKeySpline
+{
+public:
+  /*
+   * Create a new key spline control point description.
+   *
+   * aX1, etc. are the x1, y1, x2, y2 cubic Bezier control points as defined by
+   * SMILANIM 3.2.3. They must each be in the range 0.0 <= x <= 1.0
+   */
+  nsSMILKeySpline(double aX1, double aY1,
+                  double aX2, double aY2);
+
+  /*
+   * Get the output (y) value for an input (x).
+   *
+   * x should be a floating-point number between 0 and 1 (inclusive).
+   */
+  double GetSplineValue(double aX) const;
+
+private:
+  void
+  CalcSampleValues();
+
+  static double
+  CalcBezier(double aT, double aA1, double aA2);
+
+  static double
+  GetSlope(double aT, double aA1, double aA2);
+
+  double
+  GetTForX(double aX) const;
+
+  static double
+  A(double aA1, double aA2)
+  {
+    return 1.0 - 3.0 * aA2 + 3.0 * aA1;
+  }
+  
+  static double
+  B(double aA1, double aA2)
+  {
+    return 3.0 * aA2 - 6.0 * aA1;
+  }
+
+  static double
+  C(double aA1)
+  {
+    return 3.0 * aA1;
+  }
+
+  const double         mX1;
+  const double         mY1;
+  const double         mX2;
+  const double         mY2;
+
+  enum { kSplineTableSize = 11 };
+  double               mSampleValues[kSplineTableSize];
+
+  static const double  kSampleStepSize;
+};
+
+#endif // NS_SMILKEYSPLINE_H_
new file mode 100644
--- /dev/null
+++ b/content/smil/nsSMILNullType.cpp
@@ -0,0 +1,80 @@
+/* -*- 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 SMIL module.
+ *
+ * The Initial Developer of the Original Code is Brian Birtles.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Robert O'Callahan <roc+moz@cs.cmu.edu>
+ *   Brian Birtles <birtles@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
+ * 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 "nsSMILNullType.h"
+#include "nsSMILValue.h"
+#include "nsDebug.h"
+
+/*static*/ nsSMILNullType nsSMILNullType::sSingleton;
+
+nsresult
+nsSMILNullType::Assign(nsSMILValue& aDest, const nsSMILValue& aSrc) const
+{
+  NS_PRECONDITION(aDest.mType == aSrc.mType, "Incompatible SMIL types.");
+  NS_PRECONDITION(aSrc.mType == this, "Unexpected source type");
+  aDest.mU    = aSrc.mU;
+  aDest.mType = &sSingleton;
+  return NS_OK;
+}
+
+nsresult
+nsSMILNullType::Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd,
+                    PRUint32 aCount) const
+{
+  NS_NOTREACHED("Adding NULL type.");
+  return NS_ERROR_FAILURE;
+}
+
+nsresult
+nsSMILNullType::ComputeDistance(const nsSMILValue& aFrom,
+                                const nsSMILValue& aTo,
+                                double& aDistance) const
+{
+  NS_NOTREACHED("Computing distance for NULL type.");
+  return NS_ERROR_FAILURE;
+}
+
+nsresult
+nsSMILNullType::Interpolate(const nsSMILValue& aStartVal,
+                            const nsSMILValue& aEndVal,
+                            double aUnitDistance,
+                            nsSMILValue& aResult) const
+{
+  NS_NOTREACHED("Interpolating NULL type.");
+  return NS_ERROR_FAILURE;
+}
new file mode 100644
--- /dev/null
+++ b/content/smil/nsSMILNullType.h
@@ -0,0 +1,66 @@
+/* -*- 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 SMIL module.
+ *
+ * The Initial Developer of the Original Code is Brian Birtles.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Robert O'Callahan <roc+moz@cs.cmu.edu>
+ *   Brian Birtles <birtles@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
+ * 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 NS_SMILNULLTYPE_H_
+#define NS_SMILNULLTYPE_H_
+
+#include "nsISMILType.h"
+
+class nsSMILNullType : public nsISMILType
+{
+public:
+  virtual nsresult Init(nsSMILValue& aValue) const { return NS_OK; }
+  virtual void Destroy(nsSMILValue& aValue) const {}
+  virtual nsresult Assign(nsSMILValue& aDest, const nsSMILValue& aSrc) const;
+
+  // The remaining methods should never be called, so although they're very
+  // simple they don't need to be inline.
+  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 nsSMILNullType sSingleton;
+};
+
+#endif // NS_SMILNULLTYPE_H_
new file mode 100644
--- /dev/null
+++ b/content/smil/nsSMILParserUtils.cpp
@@ -0,0 +1,603 @@
+/* -*- 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 SMIL module.
+ *
+ * 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>
+ *
+ * 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 "nsSMILParserUtils.h"
+#include "nsISMILAttr.h"
+#include "nsSMILValue.h"
+#include "nsSMILTimeValue.h"
+#include "nsSMILTypes.h"
+#include "nsSMILRepeatCount.h"
+#include "nsString.h"
+#include "prdtoa.h"
+#include "nsCRT.h"
+#include "nsCOMPtr.h"
+#include "prlong.h"
+
+const PRUint32 nsSMILParserUtils::MSEC_PER_SEC   = 1000;
+const PRUint32 nsSMILParserUtils::MSEC_PER_MIN   = 1000 * 60;
+const PRUint32 nsSMILParserUtils::MSEC_PER_HOUR  = 1000 * 60 * 60;
+
+//------------------------------------------------------------------------------
+// Inlines
+
+// NS_IS_SPACE relies on isspace which may return true for \xB and \xC but
+// SMILANIM does not consider these characters to be whitespace.
+inline PRBool
+nsSMILParserUtils::IsSpace(const PRUnichar c)
+{
+  return (c == 0x9 || c == 0xA || c == 0xD || c == 0x20);
+}
+
+inline void
+nsSMILParserUtils::SkipWsp(nsACString::const_iterator& aIter,
+                           const nsACString::const_iterator& aIterEnd)
+{
+  while (aIter != aIterEnd && IsSpace(*aIter)) {
+    ++aIter;
+  }
+}
+
+inline void
+nsSMILParserUtils::SkipWsp(nsAString::const_iterator& aIter,
+                           const nsAString::const_iterator& aIterEnd)
+{
+  while (aIter != aIterEnd && IsSpace(*aIter)) {
+    ++aIter;
+  }
+}
+
+inline double
+nsSMILParserUtils::GetFloat(nsACString::const_iterator& aIter,
+                            const nsACString::const_iterator& aIterEnd,
+                            nsresult *aErrorCode)
+{
+  char *end;
+  const char *start = aIter.get();
+  double value = PR_strtod(start, &end);
+
+  nsresult rv = NS_OK;
+
+  if (end == start || end > aIterEnd.get()) {
+    rv = NS_ERROR_FAILURE;
+  } else {
+    aIter.advance(end - start);
+  }
+
+  if (aErrorCode) {
+    *aErrorCode = rv;
+  }
+
+  return value;
+}
+
+inline PRBool
+nsSMILParserUtils::ConsumeSubstring(nsACString::const_iterator& aIter,
+                                    const nsACString::const_iterator& aIterEnd,
+                                    const char *aSubstring)
+{
+  size_t substrLen = PL_strlen(aSubstring);
+  typedef nsACString::const_iterator::difference_type diff_type;
+
+  if (aIterEnd.get() - aIter.get() < static_cast<diff_type>(substrLen))
+    return PR_FALSE;
+
+  PRBool result = PR_FALSE;
+
+  if (PL_strstr(aIter.get(), aSubstring) == aIter.get()) {
+    aIter.advance(substrLen);
+    result = PR_TRUE;
+  }
+
+  return result;
+}
+
+//------------------------------------------------------------------------------
+// Implementation
+
+nsresult
+nsSMILParserUtils::ParseKeySplines(const nsAString& aSpec,
+                                   nsTArray<double>& aSplineArray)
+{
+  nsresult rv = NS_OK;
+
+  NS_ConvertUTF16toUTF8 spec(aSpec);
+
+  nsACString::const_iterator start, end;
+  spec.BeginReading(start);
+  spec.EndReading(end);
+
+  SkipWsp(start, end);
+
+  int i = 0;
+
+  while (start != end)
+  {
+    double value = GetFloat(start, end, &rv);
+    if (NS_FAILED(rv))
+      break;
+
+    if (value > 1.0 || value < 0.0) {
+      rv = NS_ERROR_FAILURE;
+      break;
+    }
+
+    if (!aSplineArray.AppendElement(value)) {
+      rv = NS_ERROR_OUT_OF_MEMORY;
+      break;
+    }
+
+    ++i;
+
+    SkipWsp(start, end);
+    if (start == end)
+      break;
+
+    if (i % 4) {
+      if (*start == ',') {
+        ++start;
+      }
+    } else {
+      if (*start != ';') {
+        rv = NS_ERROR_FAILURE;
+        break;
+      }
+      ++start;
+    }
+
+    SkipWsp(start, end);
+  }
+
+  if (i % 4) {
+    rv = NS_ERROR_FAILURE; // wrong number of points
+  }
+
+  return rv;
+}
+
+nsresult
+nsSMILParserUtils::ParseKeyTimes(const nsAString& aSpec,
+                                 nsTArray<double>& aTimeArray)
+{
+  nsresult rv = NS_OK;
+
+  NS_ConvertUTF16toUTF8 spec(aSpec);
+
+  nsACString::const_iterator start, end;
+  spec.BeginReading(start);
+  spec.EndReading(end);
+
+  SkipWsp(start, end);
+
+  double previousValue = -1.0;
+
+  while (start != end) {
+    double value = GetFloat(start, end, &rv);
+    if (NS_FAILED(rv))
+      break;
+
+    if (value > 1.0 || value < 0.0 || value < previousValue) {
+      rv = NS_ERROR_FAILURE;
+      break;
+    }
+
+    if (!aTimeArray.AppendElement(value)) {
+      rv = NS_ERROR_OUT_OF_MEMORY;
+      break;
+    }
+    previousValue = value;
+
+    SkipWsp(start, end);
+    if (start == end)
+      break;
+    
+    if (*start++ != ';') {
+      rv = NS_ERROR_FAILURE;
+      break;
+    }
+
+    SkipWsp(start, end);
+  }
+
+  return rv;
+}
+
+nsresult
+nsSMILParserUtils::ParseValues(const nsAString& aSpec,
+                               const nsISMILAnimationElement* aSrcElement,
+                               const nsISMILAttr& aAttribute, 
+                               nsTArray<nsSMILValue>& aValuesArray)
+{
+  nsresult rv = NS_ERROR_FAILURE;
+  nsAString::const_iterator start;
+  nsAString::const_iterator end;
+  nsAString::const_iterator substr_end;
+  nsAString::const_iterator next;
+
+  aSpec.BeginReading(start);
+  aSpec.EndReading(end);
+
+  while (start != end) {
+    rv = NS_ERROR_FAILURE;
+
+    SkipWsp(start, end);
+
+    if (start == end || *start == ';')
+      break;
+
+    substr_end = start;
+
+    while (substr_end != end && *substr_end != ';') {
+      ++substr_end;
+    }
+
+    next = substr_end;
+    if (*substr_end == ';') {
+      ++next;
+      if (next == end)
+        break;
+    }
+
+    do --substr_end; while (start != substr_end && NS_IS_SPACE(*substr_end));
+    ++substr_end;
+
+    nsSMILValue newValue;
+    rv = aAttribute.ValueFromString(Substring(start, substr_end), 
+                                    aSrcElement, newValue);
+    if (NS_FAILED(rv))
+      break;
+
+    if (!aValuesArray.AppendElement(newValue)) {
+      rv = NS_ERROR_OUT_OF_MEMORY;
+      break;
+    }
+
+    rv = NS_OK;
+    start = next;
+  }
+
+  return rv;
+}
+
+nsresult
+nsSMILParserUtils::ParseRepeatCount(const nsAString& aSpec,
+                                    nsSMILRepeatCount& aResult)
+{
+  nsresult rv = NS_OK;
+
+  NS_ConvertUTF16toUTF8 spec(aSpec);
+
+  nsACString::const_iterator start, end;
+  spec.BeginReading(start);
+  spec.EndReading(end);
+
+  SkipWsp(start, end);
+  
+  if (start != end)
+  {
+    if (ConsumeSubstring(start, end, "indefinite")) {
+      aResult.SetIndefinite();
+    } else {
+      double value = GetFloat(start, end, &rv);
+
+      if (NS_SUCCEEDED(rv))
+      {
+        /* Repeat counts must be > 0 */
+        if (value <= 0.0) {
+          rv = NS_ERROR_FAILURE;
+        } else {
+          aResult = value;
+        }
+      }
+    }
+
+    /* Check for trailing junk */
+    SkipWsp(start, end);
+    if (start != end) {
+      rv = NS_ERROR_FAILURE;
+    }
+  } else {
+    /* Empty spec */
+    rv = NS_ERROR_FAILURE;
+  }
+
+  if (NS_FAILED(rv)) {
+    aResult.Unset();
+  }
+
+  return rv;
+}
+
+nsresult
+nsSMILParserUtils::ParseClockValue(const nsAString& aSpec,
+                                   nsSMILTimeValue* aResult, 
+                                   PRUint32 aFlags,   // = 0
+                                   PRBool* aIsMedia)  // = nsnull
+{
+  nsSMILTime offset = 0L;
+  double component = 0.0;
+
+  PRInt8 sign = 0;
+  PRUint8 colonCount = 0;
+
+  PRBool started = PR_FALSE;
+  PRBool isValid = PR_TRUE;
+
+  PRInt32 metricMultiplicand = MSEC_PER_SEC;
+
+  PRBool numIsReal = PR_FALSE;
+  PRBool prevNumCouldBeMin = PR_FALSE;
+  PRBool numCouldBeMin = PR_FALSE;
+  PRBool numCouldBeSec = PR_FALSE;
+  PRBool isIndefinite = PR_FALSE;
+
+  if (aIsMedia) {
+    *aIsMedia = PR_FALSE;
+  }
+
+  NS_ConvertUTF16toUTF8 spec(aSpec);
+
+  nsACString::const_iterator start, end;
+  spec.BeginReading(start);
+  spec.EndReading(end);
+
+  while (start != end) {
+    if (IsSpace(*start)) {
+      if (started) {
+        ++start;
+        break;
+      }
+      // else, we haven't started yet, ignore initial whitespace
+      ++start;
+
+    } else if ((aFlags & kClockValueAllowSign)
+               && (*start == '+' || *start == '-')) {
+      if (sign != 0) {
+        // sign has already been set
+        isValid = PR_FALSE;
+        break;
+      }
+
+      if (started) {
+        // sign appears in the middle of the string
+        isValid = PR_FALSE;
+        break;
+      }
+
+      sign = (*start == '+') ? 1 : -1;
+      ++start;
+    // The NS_IS_DIGIT etc. macros are not locale-specific
+    } else if (NS_IS_DIGIT(*start)) {
+      prevNumCouldBeMin = numCouldBeMin;
+
+      if (!ParseClockComponent(start, end, component, numIsReal, numCouldBeMin,
+                               numCouldBeSec)) {
+        isValid = PR_FALSE;
+        break;
+      }
+
+      started = PR_TRUE;
+    } else if (*start == ':') {
+      ++colonCount;
+
+      // Neither minutes nor hours can be reals
+      if (numIsReal) {
+        isValid = PR_FALSE;
+        break;
+      }
+
+      // Clock value can't start with a ':'
+      if (!started) {
+        isValid = PR_FALSE;
+        break;
+      }
+
+      // Can't have more than two colons
+      if (colonCount > 2) {
+        isValid = PR_FALSE;
+        break;
+      }
+
+      // Multiply the offset by 60 and add the last accumulated component
+      offset = offset * 60 + PRInt64(component);
+
+      component = 0.0;
+      ++start;
+    } else if (NS_IS_ALPHA(*start)) {
+      if (colonCount > 0) {
+        isValid = PR_FALSE;
+        break;
+      }
+
+      if ((aFlags & kClockValueAllowIndefinite)
+          && ConsumeSubstring(start, end, "indefinite")) {
+        // We set a separate flag because we don't know what the state of the
+        // passed in time value is and we shouldn't change it in the case of a
+        // bad input string (so we can't initialise it to 0ms for example).
+        isIndefinite = PR_TRUE;
+        if (aResult) {
+          aResult->SetIndefinite();
+        }
+      } else if (aIsMedia && ConsumeSubstring(start, end, "media")) {
+        *aIsMedia = PR_TRUE;
+      } else if (!ParseMetricMultiplicand(start, end, metricMultiplicand)) {
+        isValid = PR_FALSE;
+        break;
+      }
+
+      // Nothing must come after the string except whitespace
+      break;
+    } else {
+      isValid = PR_FALSE;
+      break;
+    }
+  }
+
+  if (!started) {
+    isValid = PR_FALSE;
+  }
+
+  // Process remainder of string (if any) to ensure it is only trailing
+  // whitespace (embedded whitespace is not allowed)
+  SkipWsp(start, end);
+  if (start != end) {
+    isValid = PR_FALSE;
+  }
+
+  // No more processing required if the value was "indefinite" or "media".
+  if (isIndefinite || (aIsMedia && *aIsMedia))
+    return NS_OK;
+
+  // If there is more than one colon then the previous component must be a
+  // correctly formatted minute (i.e. two digits between 00 and 59) and the
+  // latest component must be a correctly formatted second (i.e. two digits
+  // before the .)
+  if (colonCount > 0 && (!prevNumCouldBeMin || !numCouldBeSec)) {
+    isValid = PR_FALSE;
+  }
+
+  if (isValid) {
+    // Tack on the last component
+    if (colonCount > 0) {
+      offset = offset * 60 * 1000;
+      component *= 1000;
+      // rounding
+      component = (component >= 0) ? component + 0.5 : component - 0.5;
+      offset += PRInt64(component);
+    } else {
+      component *= metricMultiplicand;
+      // rounding
+      component = (component >= 0) ? component + 0.5 : component - 0.5;
+      offset = PRInt64(component);
+    }
+
+    if (aResult) {
+      nsSMILTime millis = offset;
+
+      if (sign == -1) {
+        millis = -offset;
+      }
+
+      aResult->SetMillis(millis);
+    }
+  }
+
+  return (isValid) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+PRBool
+nsSMILParserUtils::ParseClockComponent(nsACString::const_iterator& aSpec,
+                                       const nsACString::const_iterator& aEnd,
+                                       double& aResult,
+                                       PRBool& aIsReal,
+                                       PRBool& aCouldBeMin,
+                                       PRBool& aCouldBeSec)
+{
+  nsresult rv;
+  char const *begin = aSpec.get();
+  double value = GetFloat(aSpec, aEnd, &rv);
+
+  // Check a number was found
+  if (NS_FAILED(rv))
+    return PR_FALSE;
+
+  // Check it's not expressed in exponential form
+  size_t len = aSpec.get() - begin;
+  PRBool isExp = (PL_strnpbrk(begin, "eE", len) != nsnull);
+  if (isExp)
+    return PR_FALSE;
+
+  // Don't allow real numbers of the form "23."
+  if (*(aSpec.get() - 1) == '.')
+    return PR_FALSE;
+
+  // Number looks good
+  aResult = value;
+
+  // Set some flags so we can check this number is valid once we know
+  // whether it's an hour, minute string etc.
+  aIsReal = (PL_strnchr(begin, '.', len) != nsnull);
+  aCouldBeMin = (value < 60.0 && (len == 2));
+  aCouldBeSec = (value < 60.0 ||
+      (value == 60.0 && begin[0] == '5')); // Take care of rounding error
+  aCouldBeSec &= (len >= 2 &&
+      (begin[2] == '\0' || begin[2] == '.' || IsSpace(begin[2])));
+
+  return PR_TRUE;
+}
+
+inline PRBool
+nsSMILParserUtils::ParseMetricMultiplicand(nsACString::const_iterator& aSpec,
+                                         const nsACString::const_iterator& aEnd,
+                                         PRInt32& multiplicand)
+{
+  PRBool result = PR_FALSE;
+
+  size_t len = aEnd.get() - aSpec.get();
+  nsACString::const_iterator spec(aSpec);
+  
+  if (len) {
+    switch (*spec++)
+    {
+      case 'h':
+        multiplicand = MSEC_PER_HOUR;
+        result = PR_TRUE;
+        break;
+      case 'm':
+        if (len >= 2) {
+          if (*spec == 's') {
+            ++spec;
+            multiplicand = 1;
+            result = PR_TRUE;
+          } else if (len >= 3 && *spec++ == 'i' && *spec++ == 'n') {
+            multiplicand = MSEC_PER_MIN;
+            result = PR_TRUE;
+          }
+        }
+        break;
+      case 's':
+        multiplicand = MSEC_PER_SEC;
+        result = PR_TRUE;
+        break;
+    }
+  }
+
+  if (result) {
+    aSpec = spec;
+  }
+
+  return result;
+}
new file mode 100644
--- /dev/null
+++ b/content/smil/nsSMILParserUtils.h
@@ -0,0 +1,136 @@
+/* -*- 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 SMIL module.
+ *
+ * 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>
+ *
+ * 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 NS_SMILPARSERUTILS_H_
+#define NS_SMILPARSERUTILS_H_
+
+#include "nscore.h"
+#include "nsTArray.h"
+#include "nsString.h"
+
+class nsISMILAttr;
+class nsISMILAnimationElement;
+class nsSMILTimeValue;
+class nsSMILValue;
+class nsSMILRepeatCount;
+
+/**
+ * Common parsing utilities for the SMIL module. There is little re-use here; it
+ * simply serves to simplify other classes by moving parsing outside and to aid
+ * unit testing.
+ */
+class nsSMILParserUtils
+{
+public:
+  static nsresult ParseKeySplines(const nsAString& aSpec,
+                                  nsTArray<double>& aSplineArray);
+
+  static nsresult ParseKeyTimes(const nsAString& aSpec,
+                                nsTArray<double>& aTimesArray);
+
+  static nsresult ParseValues(const nsAString& aSpec,
+                              const nsISMILAnimationElement* aSrcElement,
+                              const nsISMILAttr& aAttribute,
+                              nsTArray<nsSMILValue>& aValuesArray);
+
+  static nsresult ParseRepeatCount(const nsAString& aSpec,
+                                   nsSMILRepeatCount& aResult);
+
+  // Used with ParseClockValue. Allow + or - before a clock value.
+  static const PRInt8 kClockValueAllowSign       = 1;
+  // Used with ParseClockValue. Allow "indefinite" in a clock value
+  static const PRInt8 kClockValueAllowIndefinite = 2;
+
+  /*
+   * This method can actually parse more than a clock value as defined in the
+   * SMIL Animation specification. It can also parse:
+   *  - the + or - before an offset
+   *  - the special value "indefinite"
+   *  - the special value "media"
+   *
+   * Because the value "media" cannot be represented as part of an
+   * nsSMILTimeValue and has different meanings depending on where it is used,
+   * it is passed out as a separate parameter (which can be set to nsnull if the
+   * media attribute is not allowed).
+   *
+   * @param aSpec    The string containing a clock value, e.g. "10s"
+   * @param aResult  The parsed result. May be NULL (e.g. if this method is
+   *                 being called just to test if aSpec is a valid clock value).
+   *                 [OUT]
+   * @param aFlags   A combination of the kClockValue* bit flags OR'ed together
+   *                 to define what additional syntax is allowed.
+   * @param aIsMedia Optional out parameter which, if not null, will be set to
+   *                 PR_TRUE if the value is the string "media", PR_FALSE
+   *                 otherwise. If it is null, the string "media" is not
+   *                 allowed.
+   *
+   * @return NS_OK if aSpec was successfully parsed as a valid clock value
+   * (according to aFlags), an error code otherwise.
+   */
+  static nsresult ParseClockValue(const nsAString& aSpec,
+                                  nsSMILTimeValue* aResult, 
+                                  PRUint32 aFlags = 0,
+                                  PRBool* aIsMedia = nsnull);
+
+private:
+  static void   SkipWsp(nsACString::const_iterator& aIter,
+                        const nsACString::const_iterator& aIterEnd);
+  static void   SkipWsp(nsAString::const_iterator& aIter,
+                        const nsAString::const_iterator& aIterEnd);
+  static double GetFloat(nsACString::const_iterator& aIter,
+                         const nsACString::const_iterator& aIterEnd,
+                         nsresult *aErrorCode = nsnull);
+  static PRBool IsSpace(const PRUnichar c);
+  static PRBool ConsumeSubstring(nsACString::const_iterator& aIter,
+                                 const nsACString::const_iterator& aIterEnd,
+                                 const char *aSubstring);
+  static PRBool ParseClockComponent(nsACString::const_iterator& aSpec,
+                                    const nsACString::const_iterator& aEnd,
+                                    double& aResult,
+                                    PRBool& aIsReal,
+                                    PRBool& aCouldBeMin,
+                                    PRBool& aCouldBeSec);
+  static PRBool ParseMetricMultiplicand(nsACString::const_iterator& aSpec,
+                                        const nsACString::const_iterator& aEnd,
+                                        PRInt32& multiplicand);
+
+  static const PRUint32 MSEC_PER_SEC;
+  static const PRUint32 MSEC_PER_MIN;
+  static const PRUint32 MSEC_PER_HOUR;
+};
+
+#endif // NS_SMILPARSERUTILS_H_
new file mode 100644
--- /dev/null
+++ b/content/smil/nsSMILRepeatCount.cpp
@@ -0,0 +1,41 @@
+/* -*- 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 SMIL module.
+ *
+ * The Initial Developer of the Original Code is Brian Birtles.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian Birtles <birtles@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
+ * 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 "nsSMILRepeatCount.h"
+
+/*static*/ const double nsSMILRepeatCount::kNotSet     = -1.0;
+/*static*/ const double nsSMILRepeatCount::kIndefinite = -2.0;
new file mode 100644
--- /dev/null
+++ b/content/smil/nsSMILRepeatCount.h
@@ -0,0 +1,84 @@
+/* -*- 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 SMIL module.
+ *
+ * The Initial Developer of the Original Code is Brian Birtles.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian Birtles <birtles@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
+ * 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 "prtypes.h"
+#include "nsDebug.h"
+#include <math.h>
+
+//----------------------------------------------------------------------
+// nsSMILRepeatCount
+//
+// A tri-state non-negative floating point number for representing the number of
+// times an animation repeat, i.e. the SMIL repeatCount attribute.
+//
+// The three states are:
+//  1. not-set
+//  2. set (with non-negative, non-zero count value)
+//  3. indefinite
+//
+class nsSMILRepeatCount
+{
+public:
+  nsSMILRepeatCount() : mCount(kNotSet) {}
+  nsSMILRepeatCount(double aCount) : mCount(kNotSet) { SetCount(aCount); }
+
+  operator double() const { return mCount; }
+  PRBool IsDefinite() const {
+    return mCount != kNotSet && mCount != kIndefinite;
+  }
+  PRBool IsIndefinite() const { return mCount == kIndefinite; }
+  PRBool IsSet() const { return mCount != kNotSet; }
+
+  nsSMILRepeatCount& operator=(double aCount)
+  {
+    SetCount(aCount);
+    return *this;
+  }
+  void SetCount(double aCount)
+  {
+    NS_ASSERTION(aCount > 0.0, "Negative or zero repeat count.");
+    mCount = aCount > 0.0 ? aCount : kNotSet;
+  }
+  void SetIndefinite() { mCount = kIndefinite; }
+  void Unset() { mCount = kNotSet; }
+
+private:
+  static const double kNotSet;
+  static const double kIndefinite;
+
+  double  mCount;
+};
new file mode 100644
--- /dev/null
+++ b/content/smil/nsSMILSetAnimationFunction.cpp
@@ -0,0 +1,132 @@
+/* -*- 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 SMIL module.
+ *
+ * The Initial Developer of the Original Code is Brian Birtles.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian Birtles <birtles@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
+ * 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 "nsSMILSetAnimationFunction.h"
+
+inline PRBool
+nsSMILSetAnimationFunction::IsDisallowedAttribute(
+    const nsIAtom* aAttribute) const
+{
+  //
+  // A <set> element is similar to <animate> but lacks:
+  //   AnimationValue.attrib(calcMode, values, keyTimes, keySplines, from, to,
+  //                         by) -- BUT has 'to'
+  //   AnimationAddition.attrib(additive, accumulate)
+  //
+  if (aAttribute == nsGkAtoms::calcMode ||
+      aAttribute == nsGkAtoms::values ||
+      aAttribute == nsGkAtoms::keyTimes ||
+      aAttribute == nsGkAtoms::keySplines ||
+      aAttribute == nsGkAtoms::from ||
+      aAttribute == nsGkAtoms::by ||
+      aAttribute == nsGkAtoms::additive ||
+      aAttribute == nsGkAtoms::accumulate) {
+    return PR_TRUE;
+  }
+
+  return PR_FALSE;
+}
+
+PRBool
+nsSMILSetAnimationFunction::SetAttr(nsIAtom* aAttribute,
+                                    const nsAString& aValue,
+                                    nsAttrValue& aResult,
+                                    nsresult* aParseResult)
+{
+  if (IsDisallowedAttribute(aAttribute)) {
+    aResult.SetTo(aValue);
+    if (aParseResult) {
+      // SMILANIM 4.2 says:
+      //
+      //   The additive and accumulate attributes are not allowed, and will be
+      //   ignored if specified.
+      //
+      // So at least for those two attributes we shouldn't report an error even
+      // if they're present. For now we'll also just silently ignore other
+      // attribute types too.
+      *aParseResult = NS_OK;
+    }
+    return PR_TRUE;
+  }
+
+  return nsSMILAnimationFunction::SetAttr(aAttribute, aValue,
+                                          aResult, aParseResult);
+}
+
+PRBool
+nsSMILSetAnimationFunction::UnsetAttr(nsIAtom* aAttribute)
+{
+  if (IsDisallowedAttribute(aAttribute)) {
+    return PR_TRUE;
+  }
+
+  return nsSMILAnimationFunction::UnsetAttr(aAttribute);
+}
+
+nsSMILAnimationFunction::nsSMILCalcMode
+nsSMILSetAnimationFunction::GetCalcMode() const
+{
+  return CALC_DISCRETE;
+}
+
+PRBool
+nsSMILSetAnimationFunction::HasAttr(nsIAtom* aAttName) const
+{
+  if (IsDisallowedAttribute(aAttName))
+    return PR_FALSE;
+
+  return nsSMILAnimationFunction::HasAttr(aAttName);
+}
+
+const nsAttrValue*
+nsSMILSetAnimationFunction::GetAttr(nsIAtom* aAttName) const
+{
+  if (IsDisallowedAttribute(aAttName))
+    return nsnull;
+
+  return nsSMILAnimationFunction::GetAttr(aAttName);
+}
+
+PRBool
+nsSMILSetAnimationFunction::GetAttr(nsIAtom* aAttName,
+                                    nsAString& aResult) const
+{
+  if (IsDisallowedAttribute(aAttName))
+    return nsnull;
+
+  return nsSMILAnimationFunction::GetAttr(aAttName, aResult);
+}
new file mode 100644
--- /dev/null
+++ b/content/smil/nsSMILSetAnimationFunction.h
@@ -0,0 +1,87 @@
+/* -*- 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 SMIL module.
+ *
+ * The Initial Developer of the Original Code is Brian Birtles.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian Birtles <birtles@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
+ * 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 NS_SMILSETANIMATIONFUNCTION_H_
+#define NS_SMILSETANIMATIONFUNCTION_H_
+
+#include "nsSMILAnimationFunction.h"
+
+//----------------------------------------------------------------------
+// nsSMILSetAnimationFunction
+//
+// Subclass of nsSMILAnimationFunction that limits the behaviour to that offered
+// by a <set> element.
+//
+class nsSMILSetAnimationFunction : public nsSMILAnimationFunction
+{
+public:
+  /*
+   * Sets animation-specific attributes (or marks them dirty, in the case
+   * of from/to/by/values).
+   *
+   * @param aAttribute The attribute being set
+   * @param aValue     The updated value of the attribute.
+   * @param aResult    The nsAttrValue object that may be used for storing the
+   *                   parsed result.
+   * @param aParseResult  Outparam used for reporting parse errors. Will be set
+   *                      to NS_OK if everything succeeds.
+   * @returns PR_TRUE if aAttribute is a recognized animation-related
+   *          attribute; PR_FALSE otherwise.
+   */
+  virtual PRBool SetAttr(nsIAtom* aAttribute, const nsAString& aValue,
+                         nsAttrValue& aResult, nsresult* aParseResult = nsnull);
+
+  /*
+   * Unsets the given attribute.
+   *
+   * @returns PR_TRUE if aAttribute is a recognized animation-related
+   *          attribute; PR_FALSE otherwise.
+   */
+  virtual PRBool UnsetAttr(nsIAtom* aAttribute);
+
+protected:
+  virtual nsSMILAnimationFunction::nsSMILCalcMode GetCalcMode() const;
+
+  virtual PRBool             HasAttr(nsIAtom* aAttName) const;
+  virtual const nsAttrValue* GetAttr(nsIAtom* aAttName) const;
+  virtual PRBool             GetAttr(nsIAtom* aAttName,
+                                     nsAString& aResult) const;
+
+  PRBool IsDisallowedAttribute(const nsIAtom* aAttribute) const;
+};
+
+#endif // NS_SMILSETANIMATIONFUNCTION_H_
new file mode 100644
--- /dev/null
+++ b/content/smil/nsSMILTimeContainer.cpp
@@ -0,0 +1,177 @@
+/* -*- 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 SMIL module.
+ *
+ * The Initial Developer of the Original Code is Brian Birtles.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian Birtles <birtles@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
+ * 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 "nsSMILTimeContainer.h"
+
+nsSMILTimeContainer::nsSMILTimeContainer()
+:
+  mParent(nsnull),
+  mCurrentTime(0L),
+  mParentOffset(0L),
+  mPauseStart(0L),
+  mNeedsPauseSample(PR_FALSE),
+  mPauseState(PAUSE_BEGIN)
+{
+}
+
+nsSMILTimeContainer::~nsSMILTimeContainer()
+{
+  if (mParent) {
+    mParent->RemoveChild(*this);
+  }
+}
+
+void
+nsSMILTimeContainer::Begin()
+{
+  Resume(PAUSE_BEGIN);
+  if (mPauseState) {
+    mNeedsPauseSample = PR_TRUE;
+  }
+
+  // This is a little bit complicated here. Ideally we'd just like to call
+  // Sample() and force an initial sample but this turns out to be a bad idea
+  // because this may mean that NeedsSample() no longer reports true and so when
+  // we come to the first real sample our parent will skip us over altogether.
+  // So we force the time to be updated and adopt the policy to never call
+  // Sample() ourselves but to always leave that to our parent or client.
+
+  UpdateCurrentTime();
+}
+
+void
+nsSMILTimeContainer::Pause(PRUint32 aType)
+{
+  if (!mPauseState && aType) {
+    mPauseStart = GetParentTime();
+    mNeedsPauseSample = PR_TRUE;
+  }
+
+  mPauseState |= aType;
+}
+
+void
+nsSMILTimeContainer::Resume(PRUint32 aType)
+{
+  if (!mPauseState)
+    return;
+
+  mPauseState &= ~aType;
+
+  if (!mPauseState) {
+    nsSMILTime extraOffset = GetParentTime() - mPauseStart;
+    mParentOffset += extraOffset;
+  }
+}
+
+nsSMILTime
+nsSMILTimeContainer::GetCurrentTime() const
+{
+  // The following behaviour is consistent with:
+  // http://www.w3.org/2003/01/REC-SVG11-20030114-errata
+  //  #getCurrentTime_setCurrentTime_undefined_before_document_timeline_begin
+  // which says that if GetCurrentTime is called before the document timeline
+  // has begun we should just return 0.
+  if (IsPausedByType(PAUSE_BEGIN))
+    return 0L;
+
+  return mCurrentTime;
+}
+
+void
+nsSMILTimeContainer::SetCurrentTime(nsSMILTime aSeekTo)
+{
+  // The following behaviour is consistent with:
+  // http://www.w3.org/2003/01/REC-SVG11-20030114-errata
+  //  #getCurrentTime_setCurrentTime_undefined_before_document_timeline_begin
+  // which says that if SetCurrentTime is called before the document timeline
+  // has begun we should still adjust the offset.
+  mParentOffset = GetParentTime() - aSeekTo;
+
+  if (mPauseState) {
+    mNeedsPauseSample = PR_TRUE;
+  }
+
+  // Force an update to the current time in case we get a call to GetCurrentTime
+  // before another call to Sample().
+  UpdateCurrentTime();
+}
+
+nsSMILTime
+nsSMILTimeContainer::GetParentTime() const
+{
+  if (mParent)
+    return mParent->GetCurrentTime();
+
+  return 0L;
+}
+
+void
+nsSMILTimeContainer::Sample()
+{
+  if (!NeedsSample())
+    return;
+
+  UpdateCurrentTime();
+  DoSample();
+
+  mNeedsPauseSample = PR_FALSE;
+}
+
+nsresult
+nsSMILTimeContainer::SetParent(nsSMILTimeContainer* aParent)
+{
+  if (mParent) {
+    mParent->RemoveChild(*this);
+  }
+
+  mParent = aParent;
+
+  nsresult rv = NS_OK;
+  if (mParent) {
+    rv = mParent->AddChild(*this);
+  }
+
+  return rv;
+}
+
+void
+nsSMILTimeContainer::UpdateCurrentTime()
+{
+  nsSMILTime now = mPauseState ? mPauseStart : GetParentTime();
+  mCurrentTime = now - mParentOffset;
+}
new file mode 100644
--- /dev/null
+++ b/content/smil/nsSMILTimeContainer.h
@@ -0,0 +1,194 @@
+/* -*- 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 SMIL module.
+ *
+ * The Initial Developer of the Original Code is Brian Birtles.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian Birtles <birtles@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
+ * 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 NS_SMILTIMECONTAINER_H_
+#define NS_SMILTIMECONTAINER_H_
+
+#include "nscore.h"
+#include "nsSMILTypes.h"
+
+//----------------------------------------------------------------------
+// nsSMILTimeContainer
+// 
+// Common base class for a time base that can be paused, resumed, and sampled.
+//
+class nsSMILTimeContainer
+{
+public:
+  nsSMILTimeContainer();
+  virtual ~nsSMILTimeContainer();
+
+  /*
+   * Pause request types.
+   */
+  enum {
+    PAUSE_BEGIN    = 1,
+    PAUSE_SCRIPT   = 2,
+    PAUSE_PAGEHIDE = 4,
+    PAUSE_USERPREF = 8
+  };
+
+  /*
+   * Cause the time container to records its begin time.
+   */
+  void Begin();
+
+  /*
+   * Pause this time container
+   *
+   * @param aType The source of the pause request. Successive calls to Pause
+   * with the same aType will be ignored. The container will remain paused until
+   * each call to Pause of a given aType has been matched by at least one call
+   * to Resume with the same aType.
+   */
+  virtual void Pause(PRUint32 aType);
+
+  /*
+   * Resume this time container
+   *
+   * param @aType The source of the resume request. Clears the pause flag for
+   * this particular type of pause request. When all pause flags have been
+   * cleared the time container will be resumed.
+   */
+  virtual void Resume(PRUint32 aType);
+
+  /**
+   * Returns true if this time container is paused by the specified type.
+   * Note that the time container may also be paused by other types; this method
+   * does not test if aType is the exclusive pause source.
+   *
+   * @param @aType The pause source to test for.
+   * @return PR_TRUE if this container is paused by aType.
+   */
+  PRBool IsPausedByType(PRUint32 aType) const { return mPauseState & aType; }
+
+  /*
+   * Return the time elapsed since this time container's begin time (expressed
+   * in parent time) minus any accumulated offset from pausing.
+   */
+  nsSMILTime GetCurrentTime() const;
+
+  /*
+   * Seek the document timeline to the specified time.
+   *
+   * @param aSeekTo The time to seek to, expressed in this time container's time
+   * base (i.e. the same units as GetCurrentTime).
+   */
+  void SetCurrentTime(nsSMILTime aSeekTo);
+
+  /*
+   * Return the current time for the parent time container if any.
+   */
+  virtual nsSMILTime GetParentTime() const;
+
+  /*
+   * Updates the current time of this time container and calls DoSample to
+   * perform any sample-operations.
+   */
+  void Sample();
+
+  /*
+   * Return if this time container should be sampled or can be skipped.
+   *
+   * This is most useful as an optimisation for skipping time containers that
+   * don't require a sample.
+   */
+  PRBool NeedsSample() const { return !mPauseState || mNeedsPauseSample; }
+
+  /*
+   * Sets the parent time container.
+   *
+   * The callee still retains ownership of the time container.
+   */
+  nsresult SetParent(nsSMILTimeContainer* aParent);
+
+protected:
+  /*
+   * Per-sample operations to be performed whenever Sample() is called and
+   * NeedsSample() is true. Called after updating mCurrentTime;
+   */
+  virtual void DoSample() { }
+
+  /*
+   * Adding and removing child containers is not implemented in the base class
+   * because not all subclasses need this.
+   */
+
+  /*
+   * Adds a child time container.
+   */
+  virtual nsresult AddChild(nsSMILTimeContainer& aChild)
+  {
+    return NS_ERROR_FAILURE;
+  }
+
+  /*
+   * Removes a child time container.
+   */
+  virtual void RemoveChild(nsSMILTimeContainer& aChild) { }
+
+  /*
+   * Implementation helper to update the current time.
+   */
+  void UpdateCurrentTime();
+
+  // The parent time container, if any
+  nsSMILTimeContainer* mParent;
+
+  // The current time established at the last call to Sample()
+  nsSMILTime mCurrentTime;
+
+  // The number of milliseconds for which the container has been paused
+  // (excluding the current pause interval if the container is currently
+  // paused).
+  //
+  //  Current time = parent time - mParentOffset
+  //
+  nsSMILTime mParentOffset;
+
+  // The timestamp in milliseconds since the epoch (i.e. wallclock time) when
+  // the document was paused
+  nsSMILTime mPauseStart;
+
+  // Whether or not a pause sample is required
+  PRPackedBool mNeedsPauseSample;
+
+  // A bitfield of the pause state for all pause requests
+  PRUint32 mPauseState;
+};
+
+#endif // NS_SMILTIMECONTAINER_H_
new file mode 100644
--- /dev/null
+++ b/content/smil/nsSMILTimeValue.cpp
@@ -0,0 +1,116 @@
+/* -*- 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 SMIL module.
+ *
+ * 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>
+ *
+ * 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 "nsSMILTimeValue.h"
+#include "nsDebug.h"
+
+nsSMILTime nsSMILTimeValue::kUnresolvedSeconds = LL_MAXINT;
+
+//----------------------------------------------------------------------
+// Implementation
+
+// Default constructor creates an unresolved time
+nsSMILTimeValue::nsSMILTimeValue()
+  : mMilliseconds(LL_MAXINT),
+    mState(STATE_UNRESOLVED)
+{
+}
+
+//----------------------------------------------------------------------
+// nsSMILTimeValue methods:
+
+inline PRInt8
+nsSMILTimeValue::Cmp(PRInt64 aA, PRInt64 aB) const
+{
+  return aA == aB ? 0 : (aA > aB ? 1 : -1);
+}
+
+void
+nsSMILTimeValue::SetIndefinite()
+{
+  mState = STATE_INDEFINITE;
+  mMilliseconds = LL_MAXINT;
+}
+
+void
+nsSMILTimeValue::SetUnresolved()
+{
+  mState = STATE_UNRESOLVED;
+  mMilliseconds = LL_MAXINT;
+}
+
+nsSMILTime
+nsSMILTimeValue::GetMillis() const
+{
+  NS_ASSERTION(mState == STATE_RESOLVED,
+               "GetMillis() called for unresolved time.");
+
+  if (mState != STATE_RESOLVED)
+      return kUnresolvedSeconds;
+
+  return mMilliseconds;
+}
+
+void
+nsSMILTimeValue::SetMillis(nsSMILTime aMillis)
+{
+  mState = STATE_RESOLVED;
+  mMilliseconds = aMillis;
+}
+
+PRInt8
+nsSMILTimeValue::CompareTo(const nsSMILTimeValue& aOther) const
+{
+  PRInt8 result;
+
+  if (mState == STATE_RESOLVED) {
+    result = (aOther.mState == STATE_RESOLVED)
+           ? Cmp(mMilliseconds, aOther.mMilliseconds)
+           : -1;
+  } else if (mState == STATE_INDEFINITE) {
+    if (aOther.mState == STATE_RESOLVED)
+      result = 1;
+    else if (aOther.mState == STATE_INDEFINITE)
+      result = 0;
+    else
+      result = -1;
+  } else {
+    result = (aOther.mState != STATE_UNRESOLVED) ? 1 : 0;
+  }
+
+  return result;
+}
new file mode 100644
--- /dev/null
+++ b/content/smil/nsSMILTimeValue.h
@@ -0,0 +1,131 @@
+/* -*- 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 SMIL module.
+ *
+ * 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>
+ *
+ * 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 NS_SMILTIMEVALUE_H_
+#define NS_SMILTIMEVALUE_H_
+
+#include "prtypes.h"
+#include "prlong.h"
+#include "nsSMILTypes.h"
+
+/*----------------------------------------------------------------------
+ * nsSMILTimeValue class
+ *
+ * A tri-state time value.
+ *
+ * First a quick overview of the SMIL time data types:
+ *
+ * nsSMILTime          -- a timestamp in milliseconds.
+ * nsSMILTimeValue     -- (this class) a timestamp that can take the additional
+ *                        states 'indefinite' and 'unresolved'
+ * nsSMILInterval      -- a pair of nsSMILTimeValues that defines a begin and an
+ *                        end point.
+ * nsSMILInstanceTime  -- an nsSMILTimeValue used for constructing intervals. It
+ *                        contains additional fields to govern reset behavior.
+ *                        It also forms an anchor for establishing syncbase
+ *                        relationships.
+ * nsSMILTimeValueSpec -- a component of a begin or end attribute, such as the
+ *                        '5s' or 'a.end+2m' in begin="5s; a.end+2m". These
+ *                        objects produce nsSMILInstanceTimes and are also used
+ *                        in managing dependent relationships between
+ *                        animations.
+ *
+ * Objects of this class may be in one of three states:
+ *
+ * 1) The time is resolved and has a millisecond value
+ * 2) The time is indefinite
+ * 3) The time in unresolved
+ *
+ * There is considerable chance for confusion with regards to the indefinite
+ * state. Is it resolved? We adopt the convention that it is NOT resolved (but
+ * nor is it unresolved). This simplifies implementation as you can then write:
+ *
+ * if (time.IsResolved())
+ *    x = time.GetMillis()
+ *
+ * instead of:
+ *
+ * if (time.IsResolved() && !time.IsIndefinite())
+ *    x = time.GetMillis()
+ *
+ * Testing if a time is unresolved becomes more complicated but this is tested
+ * much less often.
+ *
+ * In summary:
+ *
+ * State         |  GetMillis         |  IsResolved        |  IsIndefinite
+ * --------------+--------------------+--------------------+-------------------
+ * Resolved      |  The millisecond   |  PR_TRUE           |  PR_FALSE
+ *               |  time              |                    |
+ * --------------+--------------------+--------------------+-------------------
+ * Indefinite    |  LL_MAXINT         |  PR_FALSE          |  PR_TRUE
+ * --------------+--------------------+--------------------+-------------------
+ * Unresolved    |  LL_MAXINT         |  PR_FALSE          |  PR_FALSE
+ *
+ */
+
+class nsSMILTimeValue
+{
+public:
+  // Creates an unresolved time value
+  nsSMILTimeValue();
+
+  PRBool            IsIndefinite() const { return mState == STATE_INDEFINITE; }
+  void              SetIndefinite();
+
+  PRBool            IsResolved() const { return mState == STATE_RESOLVED; }
+  void              SetUnresolved();
+
+  nsSMILTime        GetMillis() const;
+  void              SetMillis(nsSMILTime aMillis);
+
+  PRInt8            CompareTo(const nsSMILTimeValue& aOther) const;
+
+private:
+  PRInt8            Cmp(PRInt64 aA, PRInt64 aB) const;
+
+  static nsSMILTime kUnresolvedSeconds;
+
+  nsSMILTime        mMilliseconds;
+  enum {
+    STATE_RESOLVED,
+    STATE_INDEFINITE,
+    STATE_UNRESOLVED
+  } mState;
+};
+
+#endif // NS_SMILTIMEVALUE_H_
new file mode 100644
--- /dev/null
+++ b/content/smil/nsSMILTimeValueSpec.cpp
@@ -0,0 +1,104 @@
+/* -*- 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 SMIL module.
+ *
+ * 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>
+ *
+ * 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 "nsSMILTimeValueSpec.h"
+#include "nsSMILTimeValue.h"
+#include "nsSMILTimedElement.h"
+#include "nsSMILInstanceTime.h"
+#include "nsSMILParserUtils.h"
+#include "nsString.h"
+#include "nsAutoPtr.h"
+
+//----------------------------------------------------------------------
+// Implementation
+
+already_AddRefed<nsSMILTimeValueSpec>
+NS_NewSMILTimeValueSpec(nsSMILTimedElement* aOwner,
+                        PRBool aIsBegin,
+                        const nsAString& aStringSpec)
+{
+  nsSMILTimeValueSpec* result = new nsSMILTimeValueSpec(aOwner, aIsBegin);
+  NS_ENSURE_TRUE(result, nsnull);
+
+  NS_ADDREF(result); // Need to addref as SetSpec calls getWeakReference
+  nsresult rv = result->SetSpec(aStringSpec);
+  if (NS_FAILED(rv)) {
+    NS_RELEASE(result);
+    return nsnull;
+  }
+  return result;
+}
+
+nsSMILTimeValueSpec::nsSMILTimeValueSpec(nsSMILTimedElement* aOwner,
+                                         PRBool aIsBegin)
+  : mOwner(aOwner),
+    mIsBegin(aIsBegin),
+    mOffset() // initalises to zero
+{
+}
+
+//----------------------------------------------------------------------
+// nsISupports
+
+NS_IMPL_ISUPPORTS1(nsSMILTimeValueSpec,
+                   nsISupports)
+
+//----------------------------------------------------------------------
+// nsSMILTimeValueSpec
+
+nsresult
+nsSMILTimeValueSpec::SetSpec(const nsAString& aStringSpec)
+{
+  // XXX Need to parse other specifiers, not just offset type
+  nsSMILTimeValue clockTime;
+  nsresult rv = nsSMILParserUtils::ParseClockValue(aStringSpec, &clockTime,
+                              nsSMILParserUtils::kClockValueAllowSign
+                              | nsSMILParserUtils::kClockValueAllowIndefinite);
+
+  if (NS_FAILED(rv) || (!clockTime.IsResolved() && !clockTime.IsIndefinite()))
+    return NS_ERROR_FAILURE;
+  
+  if (clockTime.IsResolved())
+    mOffset = clockTime.GetMillis();
+  
+  if (mOwner) {
+    nsSMILInstanceTime instance(clockTime, this);
+    mOwner->AddInstanceTime(instance, mIsBegin);
+  }
+
+  return rv;
+}
new file mode 100644
--- /dev/null
+++ b/content/smil/nsSMILTimeValueSpec.h
@@ -0,0 +1,95 @@
+/* -*- 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 SMIL module.
+ *
+ * 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>
+ *
+ * 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 NS_SMILTIMEVALUESPEC_H_
+#define NS_SMILTIMEVALUESPEC_H_
+
+#include "nsISupports.h"
+#include "nsCOMPtr.h"
+#include "nsSMILTypes.h"
+
+class nsAString;
+class nsSMILTimeValue;
+class nsSMILTimedElement;
+
+//----------------------------------------------------------------------
+// nsSMILTimeValueSpec class
+//
+// An individual element of a 'begin' or 'end' attribute, e.g. '5s', 'a.end'.
+// This class handles the parsing of such specifications and performs the
+// necessary event handling (for event, repeat, and accesskey specifications)
+// and synchronisation (for syncbase specifications).
+//
+// For an overview of how this class is related to other SMIL time classes see
+// the documentstation in nsSMILTimeValue.h
+
+// {39d2f376-6bda-42c0-8510-a93b24828a80}
+#define NS_SMILTIMEVALUESPEC_IID \
+{ 0x39d2f376, 0x6bda, 0x42c0, { 0x85, 0x10, 0xa9, 0x3b, 0x24, 0x82, 0x8a, 0x80 } }
+
+class nsSMILTimeValueSpec : public nsISupports
+{
+public:
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_SMILTIMEVALUESPEC_IID)
+  NS_DECL_ISUPPORTS
+
+protected:
+  nsSMILTimeValueSpec(nsSMILTimedElement* aOwner, PRBool aIsBegin);
+
+  friend already_AddRefed<nsSMILTimeValueSpec>
+  NS_NewSMILTimeValueSpec(nsSMILTimedElement* aOwner,
+                          PRBool aIsBegin,
+                          const nsAString& aStringSpec);
+
+  nsresult SetSpec(const nsAString& aStringSpec);
+
+  nsSMILTimedElement* mOwner;
+  PRPackedBool        mIsBegin;
+  nsSMILTime          mOffset;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsSMILTimeValueSpec, NS_SMILTIMEVALUESPEC_IID)
+
+////////////////////////////////////////////////////////////////////////
+// Factory methods
+
+already_AddRefed<nsSMILTimeValueSpec>
+NS_NewSMILTimeValueSpec(nsSMILTimedElement* aOwner,
+                        PRBool aIsBegin,
+                        const nsAString& aStringSpec);
+
+#endif // NS_SMILTIMEVALUESPEC_H_
new file mode 100644
--- /dev/null
+++ b/content/smil/nsSMILTimedElement.cpp
@@ -0,0 +1,1038 @@
+/* -*- 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 SMIL module.
+ *
+ * The Initial Developer of the Original Code is Brian Birtles.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian Birtles <birtles@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
+ * 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 "nsSMILTimedElement.h"
+#include "nsSMILAnimationFunction.h"
+#include "nsSMILTimeValue.h"
+#include "nsSMILTimeValueSpec.h"
+#include "nsSMILInstanceTime.h"
+#include "nsSMILParserUtils.h"
+#include "nsSMILTimeContainer.h"
+#include "nsGkAtoms.h"
+#include "nsReadableUtils.h"
+#include "nsMathUtils.h"
+#include "prdtoa.h"
+#include "plstr.h"
+#include "prtime.h"
+#include "nsString.h"
+
+//----------------------------------------------------------------------
+// Static members
+
+nsAttrValue::EnumTable nsSMILTimedElement::sFillModeTable[] = {
+      {"remove", FILL_REMOVE},
+      {"freeze", FILL_FREEZE},
+      {nsnull, 0}
+};
+
+nsAttrValue::EnumTable nsSMILTimedElement::sRestartModeTable[] = {
+      {"always", RESTART_ALWAYS},
+      {"whenNotActive", RESTART_WHENNOTACTIVE},
+      {"never", RESTART_NEVER},
+      {nsnull, 0}
+};
+
+//----------------------------------------------------------------------
+// Ctor, dtor
+
+nsSMILTimedElement::nsSMILTimedElement()
+:
+  mBeginSpecs(),
+  mEndSpecs(),
+  mFillMode(FILL_REMOVE),
+  mRestartMode(RESTART_ALWAYS),
+  mBeginSpecSet(PR_FALSE),
+  mEndHasEventConditions(PR_FALSE),
+  mClient(nsnull),
+  mCurrentInterval(),
+  mElementState(STATE_STARTUP)
+{
+  mSimpleDur.SetIndefinite();
+  mMin.SetMillis(0L);
+  mMax.SetIndefinite();
+}
+
+//----------------------------------------------------------------------
+// nsIDOMElementTimeControl methods
+//
+// The definition of the ElementTimeControl interface differs between SMIL
+// Animation and SVG 1.1. In SMIL Animation all methods have a void return
+// type and the new instance time is simply added to the list and restart
+// semantics are applied as with any other instance time. In the SVG definition
+// the methods return a bool depending on the restart mode. There are some
+// cases where this is problematic.
+//
+// For example, if a call is made to beginElementAt and the resolved time
+// after including the offset falls outside the current interval then using
+// the SMIL Animation definition an element with restart == whenNotActive
+// would restart with this new instance time. The SVG definition however seems
+// to imply that in this case the implementation should ignore the new
+// instance time if the restart mode == whenNotActive and the element is
+// currently active and return false.
+//
+// It is tempting to try and determine when a new instance time will actually
+// cause a restart but this is not possible as in the meantime a new event may
+// trump the new instance time. We take a compromise of returning true and
+// false according to the SVG definition but adding the instance time to the
+// list regardless. This may produce different results to an implementation that
+// follows strictly the behaviour implied by the SVG spec.
+//
+
+/* boolean beginElementAt (in float offset); */
+PRBool
+nsSMILTimedElement::BeginElementAt(double aOffsetSeconds,
+                                   const nsSMILTimeContainer* aContainer)
+{
+  // If restart == never or restart == whenNotActive, check whether we're
+  // in a state that allows us to restart.
+  if ((mRestartMode == RESTART_NEVER &&
+       (mElementState == STATE_ACTIVE || mElementState == STATE_POSTACTIVE)) ||
+      (mRestartMode == RESTART_WHENNOTACTIVE &&
+       mElementState == STATE_ACTIVE)) {
+    return PR_FALSE;
+  }
+
+  if (!AddInstanceTimeFromCurrentTime(aOffsetSeconds, PR_TRUE, aContainer)) {
+    // Probably we don't have a time container
+    NS_ERROR("Failed to begin element");
+    return PR_FALSE;
+  }
+
+  return PR_TRUE;
+}
+
+/* boolean endElementAt (in float offset); */
+PRBool
+nsSMILTimedElement::EndElementAt(double aOffsetSeconds,
+                                 const nsSMILTimeContainer* aContainer)
+{
+  if (mElementState != STATE_ACTIVE)
+    return PR_FALSE;
+
+  if (!AddInstanceTimeFromCurrentTime(aOffsetSeconds, PR_FALSE, aContainer)) {
+    // Probably we don't have a time container
+    NS_ERROR("Failed to end element");
+    return PR_FALSE;
+  }
+
+  return PR_TRUE;
+}
+
+//----------------------------------------------------------------------
+// nsSMILTimedElement
+
+void
+nsSMILTimedElement::AddInstanceTime(const nsSMILInstanceTime& aInstanceTime,
+                                    PRBool aIsBegin)
+{
+  if (aIsBegin)
+    mBeginInstances.AppendElement(aInstanceTime);
+  else
+    mEndInstances.AppendElement(aInstanceTime);
+
+  UpdateCurrentInterval();
+}
+
+void
+nsSMILTimedElement::SetTimeClient(nsSMILAnimationFunction* aClient)
+{
+  //
+  // No need to check for NULL. A NULL parameter simply means to remove the
+  // previous client which we do by setting to NULL anyway.
+  //
+
+  mClient = aClient;
+}
+
+void
+nsSMILTimedElement::SampleAt(nsSMILTime aDocumentTime)
+{
+  PRBool          stateChanged;
+  nsSMILTimeValue docTime;
+  docTime.SetMillis(aDocumentTime);
+
+  // XXX Need to cache previous sample time and if this time is less then
+  // perform backwards seeking behaviour (see SMILANIM 3.6.5 Hyperlinks and
+  // timing)
+
+  do {
+    stateChanged = PR_FALSE;
+
+    switch (mElementState)
+    {
+    case STATE_STARTUP:
+      {
+        nsSMILTimeValue beginAfter;
+        beginAfter.SetMillis(LL_MININT);
+
+        mElementState = 
+         (NS_SUCCEEDED(GetNextInterval(beginAfter, PR_TRUE, mCurrentInterval)))
+         ? STATE_WAITING
+         : STATE_POSTACTIVE;
+        stateChanged = PR_TRUE;
+      }
+      break;
+
+    case STATE_WAITING:
+      {
+        if (mCurrentInterval.mBegin.CompareTo(docTime) <= 0) {
+          mElementState = STATE_ACTIVE;
+          if (mClient) {
+            mClient->Activate(mCurrentInterval.mBegin.GetMillis());
+          }
+          stateChanged = PR_TRUE;
+        }
+      }
+      break;
+
+    case STATE_ACTIVE:
+      {
+        CheckForEarlyEnd(docTime);
+        if (mCurrentInterval.mEnd.CompareTo(docTime) <= 0) {
+          nsSMILInterval newInterval;
+          mElementState = 
+            (NS_SUCCEEDED(GetNextInterval(mCurrentInterval.mEnd,
+                                          PR_FALSE,
+                                          newInterval)))
+            ? STATE_WAITING
+            : STATE_POSTACTIVE;
+          if (mClient) {
+            mClient->Inactivate(mFillMode == FILL_FREEZE);
+          }
+          SampleFillValue();
+          mOldIntervals.AppendElement(mCurrentInterval);
+          mCurrentInterval = newInterval;
+          Reset();
+          stateChanged = PR_TRUE;
+        } else {
+          nsSMILTime beginTime = mCurrentInterval.mBegin.GetMillis();
+          nsSMILTime activeTime = aDocumentTime - beginTime;
+          SampleSimpleTime(activeTime);
+        }
+      }
+      break;
+
+    case STATE_POSTACTIVE:
+      break;
+    }
+  } while (stateChanged);
+}
+
+void
+nsSMILTimedElement::Reset()
+{
+  PRInt32 count = mBeginInstances.Length();
+
+  for (PRInt32 i = count - 1; i >= 0; --i) {
+    nsSMILInstanceTime &instance = mBeginInstances[i];
+    // XXX If the instance corresponds to the begin time of the current interval
+    // we shouldn't clear it (see SMILANIM 3.3.7). This probably only matters
+    // for syncbase timing where there may be dependents on that instance.
+    if (instance.ClearOnReset()) {
+      mBeginInstances.RemoveElementAt(i);
+    }
+  }
+
+  count = mEndInstances.Length();
+
+  for (PRInt32 j = count - 1; j >= 0; --j) {
+    nsSMILInstanceTime &instance = mEndInstances[j];
+    if (instance.ClearOnReset()) {
+      mEndInstances.RemoveElementAt(j);
+    }
+  }
+}
+
+void
+nsSMILTimedElement::HardReset()
+{
+  Reset();
+  mCurrentInterval = nsSMILInterval();
+  mElementState    = STATE_STARTUP;
+  mOldIntervals.Clear();
+
+  // Remove any fill
+  if (mClient) {
+    mClient->Inactivate(PR_FALSE);
+  }
+}
+
+PRBool
+nsSMILTimedElement::SetAttr(nsIAtom* aAttribute, const nsAString& aValue,
+                            nsAttrValue& aResult, nsresult* aParseResult)
+{
+  PRBool foundMatch = PR_TRUE;
+  nsresult parseResult = NS_OK;
+
+  if (aAttribute == nsGkAtoms::begin) {
+    parseResult = SetBeginSpec(aValue);
+  } else if (aAttribute == nsGkAtoms::dur) {
+    parseResult = SetSimpleDuration(aValue);
+  } else if (aAttribute == nsGkAtoms::end) {
+    parseResult = SetEndSpec(aValue);
+  } else if (aAttribute == nsGkAtoms::fill) {
+    parseResult = SetFillMode(aValue);
+  } else if (aAttribute == nsGkAtoms::max) {
+    parseResult = SetMax(aValue);
+  } else if (aAttribute == nsGkAtoms::min) {
+    parseResult = SetMin(aValue);
+  } else if (aAttribute == nsGkAtoms::repeatCount) {
+    parseResult = SetRepeatCount(aValue);
+  } else if (aAttribute == nsGkAtoms::repeatDur) {
+    parseResult = SetRepeatDur(aValue);
+  } else if (aAttribute == nsGkAtoms::restart) {
+    parseResult = SetRestart(aValue);
+  } else {
+    foundMatch = PR_FALSE;
+  }
+
+  if (foundMatch) {
+    aResult.SetTo(aValue);
+    if (aParseResult) {
+      *aParseResult = parseResult;
+    }
+  }
+
+  return foundMatch;
+}
+
+PRBool
+nsSMILTimedElement::UnsetAttr(nsIAtom* aAttribute)
+{
+  PRBool foundMatch = PR_TRUE;
+
+  if (aAttribute == nsGkAtoms::begin) {
+    UnsetBeginSpec();
+  } else if (aAttribute == nsGkAtoms::dur) {
+    UnsetSimpleDuration();
+  } else if (aAttribute == nsGkAtoms::end) {
+    UnsetEndSpec();
+  } else if (aAttribute == nsGkAtoms::fill) {
+    UnsetFillMode();
+  } else if (aAttribute == nsGkAtoms::max) {
+    UnsetMax();
+  } else if (aAttribute == nsGkAtoms::min) {
+    UnsetMin();
+  } else if (aAttribute == nsGkAtoms::repeatCount) {
+    UnsetRepeatCount();
+  } else if (aAttribute == nsGkAtoms::repeatDur) {
+    UnsetRepeatDur();
+  } else if (aAttribute == nsGkAtoms::restart) {
+    UnsetRestart();
+  } else {
+    foundMatch = PR_FALSE;
+  }
+
+  return foundMatch;
+}
+
+//----------------------------------------------------------------------
+// Setters and unsetters
+
+nsresult
+nsSMILTimedElement::SetBeginSpec(const nsAString& aBeginSpec)
+{
+  mBeginSpecSet = PR_TRUE;
+  return SetBeginOrEndSpec(aBeginSpec, PR_TRUE);
+}
+
+void
+nsSMILTimedElement::UnsetBeginSpec()
+{
+  mBeginSpecs.Clear();
+  mBeginInstances.Clear();
+  mBeginSpecSet = PR_FALSE;
+  UpdateCurrentInterval();
+}
+
+nsresult
+nsSMILTimedElement::SetEndSpec(const nsAString& aEndSpec)
+{
+  //
+  // When implementing events etc., don't forget to ensure
+  // mEndHasEventConditions is set if the specification contains conditions that
+  // describe event-values, repeat-values or accessKey-values.
+  //
+  return SetBeginOrEndSpec(aEndSpec, PR_FALSE);
+}
+
+void
+nsSMILTimedElement::UnsetEndSpec()
+{
+  mEndSpecs.Clear();
+  mEndInstances.Clear();
+  UpdateCurrentInterval();
+}
+
+nsresult
+nsSMILTimedElement::SetSimpleDuration(const nsAString& aDurSpec)
+{
+  nsSMILTimeValue duration;
+  PRBool isMedia;
+  nsresult rv;
+  
+  rv = nsSMILParserUtils::ParseClockValue(aDurSpec, &duration,
+          nsSMILParserUtils::kClockValueAllowIndefinite, &isMedia);
+
+  if (NS_FAILED(rv) || (!duration.IsResolved() && !duration.IsIndefinite()))
+    return NS_ERROR_FAILURE;
+  
+  if (duration.IsResolved() && duration.GetMillis() == 0L)
+    return NS_ERROR_FAILURE;
+
+  //
+  // SVG-specific: "For SVG's animation elements, if "media" is specified, the
+  // attribute will be ignored." (SVG 1.1, section 19.2.6)
+  //
+  if (isMedia)
+    duration.SetIndefinite();
+
+  mSimpleDur = duration;
+
+  return NS_OK;
+}
+
+void
+nsSMILTimedElement::UnsetSimpleDuration()
+{
+  mSimpleDur.SetIndefinite();
+  UpdateCurrentInterval();
+}
+
+nsresult
+nsSMILTimedElement::SetMin(const nsAString& aMinSpec)
+{
+  nsSMILTimeValue duration;
+  PRBool isMedia;
+  nsresult rv;
+  
+  rv = nsSMILParserUtils::ParseClockValue(aMinSpec, &duration, 0, &isMedia);
+
+  if (isMedia) {
+    duration.SetMillis(0L);
+  }
+
+  if (NS_FAILED(rv) || !duration.IsResolved()) {
+    mMin.SetMillis(0L);
+    return NS_ERROR_FAILURE;
+  }
+  
+  if (duration.GetMillis() < 0L) {
+    mMin.SetMillis(0L);
+    return NS_ERROR_FAILURE;
+  }
+
+  mMin = duration;
+  UpdateCurrentInterval();
+
+  return NS_OK;
+}
+
+void
+nsSMILTimedElement::UnsetMin()
+{
+  mMin.SetMillis(0L);
+  UpdateCurrentInterval();
+}
+
+nsresult
+nsSMILTimedElement::SetMax(const nsAString& aMaxSpec)
+{
+  nsSMILTimeValue duration;
+  PRBool isMedia;
+  nsresult rv;
+  
+  rv = nsSMILParserUtils::ParseClockValue(aMaxSpec, &duration,
+          nsSMILParserUtils::kClockValueAllowIndefinite, &isMedia);
+
+  if (isMedia)
+    duration.SetIndefinite();
+
+  if (NS_FAILED(rv) || (!duration.IsResolved() && !duration.IsIndefinite())) {
+    mMax.SetIndefinite();
+    return NS_ERROR_FAILURE;
+  }
+  
+  if (duration.IsResolved() && duration.GetMillis() <= 0L) {
+    mMax.SetIndefinite();
+    return NS_ERROR_FAILURE;
+  }
+
+  mMax = duration;
+  UpdateCurrentInterval();
+
+  return NS_OK;
+}
+
+void
+nsSMILTimedElement::UnsetMax()
+{
+  mMax.SetIndefinite();
+  UpdateCurrentInterval();
+}
+
+nsresult
+nsSMILTimedElement::SetRestart(const nsAString& aRestartSpec)
+{
+  nsAttrValue temp;
+  PRBool parseResult 
+    = temp.ParseEnumValue(aRestartSpec, sRestartModeTable, PR_TRUE);
+  mRestartMode = parseResult
+               ? nsSMILRestartMode(temp.GetEnumValue())
+               : RESTART_ALWAYS;
+  UpdateCurrentInterval();
+  return parseResult ? NS_OK : NS_ERROR_FAILURE;
+}
+
+void
+nsSMILTimedElement::UnsetRestart()
+{
+  mRestartMode = RESTART_ALWAYS;
+  UpdateCurrentInterval();
+}
+
+nsresult
+nsSMILTimedElement::SetRepeatCount(const nsAString& aRepeatCountSpec)
+{
+  nsSMILRepeatCount newRepeatCount;
+  nsresult rv = 
+    nsSMILParserUtils::ParseRepeatCount(aRepeatCountSpec, newRepeatCount);
+
+  UpdateCurrentInterval();
+
+  if (NS_SUCCEEDED(rv)) {
+    mRepeatCount = newRepeatCount;
+  } else {
+    mRepeatCount.Unset();
+  }
+    
+  return rv;
+}
+
+void
+nsSMILTimedElement::UnsetRepeatCount()
+{
+  mRepeatCount.Unset();
+  UpdateCurrentInterval();
+}
+
+nsresult
+nsSMILTimedElement::SetRepeatDur(const nsAString& aRepeatDurSpec)
+{
+  nsresult        rv;
+  nsSMILTimeValue duration;
+
+  rv = nsSMILParserUtils::ParseClockValue(aRepeatDurSpec, &duration,
+          nsSMILParserUtils::kClockValueAllowIndefinite);
+
+  if (NS_FAILED(rv) || (!duration.IsResolved() && !duration.IsIndefinite()))
+    return NS_ERROR_FAILURE;
+  
+  UpdateCurrentInterval();
+  
+  mRepeatDur = duration;
+
+  return NS_OK;
+}
+
+void
+nsSMILTimedElement::UnsetRepeatDur()
+{
+  mRepeatDur.SetUnresolved();
+  UpdateCurrentInterval();
+}
+
+nsresult
+nsSMILTimedElement::SetFillMode(const nsAString& aFillModeSpec)
+{
+  PRUint16 previousFillMode = mFillMode;
+
+  nsAttrValue temp;
+  PRBool parseResult = 
+    temp.ParseEnumValue(aFillModeSpec, sFillModeTable, PR_TRUE);
+  mFillMode = parseResult
+            ? nsSMILFillMode(temp.GetEnumValue())
+            : FILL_REMOVE;
+
+  if (mFillMode != previousFillMode &&
+      (mElementState == STATE_WAITING || mElementState == STATE_POSTACTIVE) &&
+      mClient)
+      mClient->Inactivate(mFillMode == FILL_FREEZE);
+
+  return parseResult ? NS_OK : NS_ERROR_FAILURE;
+}
+
+void
+nsSMILTimedElement::UnsetFillMode()
+{
+  PRUint16 previousFillMode = mFillMode;
+  mFillMode = FILL_REMOVE;
+  if ((mElementState == STATE_WAITING || mElementState == STATE_POSTACTIVE) &&
+      previousFillMode == FILL_FREEZE &&
+      mClient)
+    mClient->Inactivate(PR_FALSE);
+}
+
+//----------------------------------------------------------------------
+// Implementation helpers
+
+nsresult
+nsSMILTimedElement::SetBeginOrEndSpec(const nsAString& aSpec,
+                                      PRBool aIsBegin)
+{
+  nsRefPtr<nsSMILTimeValueSpec> spec;
+  SMILTimeValueSpecList& timeSpecsList = aIsBegin ? mBeginSpecs : mEndSpecs;
+  nsTArray<nsSMILInstanceTime>& instancesList 
+    = aIsBegin ? mBeginInstances : mEndInstances;
+
+  timeSpecsList.Clear();
+  instancesList.Clear();
+
+  PRInt32 start;
+  PRInt32 end = -1;
+  PRInt32 length;
+
+  do {
+    start = end + 1;
+    end = aSpec.FindChar(';', start);
+    length = (end == -1) ? -1 : end - start;
+    spec = NS_NewSMILTimeValueSpec(this, aIsBegin,
+                                   Substring(aSpec, start, length));
+
+    if (spec)
+      timeSpecsList.AppendElement(spec);
+  } while (end != -1 && spec);
+
+  if (!spec) {
+    timeSpecsList.Clear();
+    instancesList.Clear();
+    HardReset();
+    return NS_ERROR_FAILURE;
+  }
+
+  UpdateCurrentInterval();
+
+  return NS_OK;
+}
+
+//
+// This method is based on the pseudocode given in the SMILANIM spec.
+//
+// See:
+// http://www.w3.org/TR/2001/REC-smil-animation-20010904/#Timing-BeginEnd-LC-Start
+//
+nsresult
+nsSMILTimedElement::GetNextInterval(const nsSMILTimeValue& aBeginAfter,
+                                    PRBool aFirstInterval,
+                                    nsSMILInterval& aResult)
+{
+  static nsSMILTimeValue zeroTime;
+  zeroTime.SetMillis(0L);
+  
+  nsSMILTimeValue beginAfter = aBeginAfter;
+  nsSMILTimeValue tempBegin;
+  nsSMILTimeValue tempEnd;
+  PRInt32         beginPos = 0;
+  PRInt32         endPos = 0;
+
+  //
+  // This is to handle the special case when a we are calculating the first
+  // interval and we have a non-0-duration interval immediately after
+  // a 0-duration in which case but we have to be careful not to re-use an end
+  // that has already been used in another interval. See the pseudocode in
+  // SMILANIM 3.6.8 for getFirstInterval.
+  //
+  PRInt32         endMaxPos = 0;
+
+  if (mRestartMode == RESTART_NEVER && !aFirstInterval)
+    return NS_ERROR_FAILURE;
+
+  nsSMILInstanceTime::Comparator comparator;
+  mBeginInstances.Sort(comparator);
+  mEndInstances.Sort(comparator);
+
+  while (PR_TRUE) {
+    if (!mBeginSpecSet && beginAfter.CompareTo(zeroTime) <= 0) {
+      tempBegin.SetMillis(0);
+    } else {
+      PRBool beginFound = GetNextGreaterOrEqual(mBeginInstances, beginAfter,
+                                                beginPos, tempBegin);
+      if (!beginFound)
+        return NS_ERROR_FAILURE;
+    }
+
+    if (mEndInstances.Length() == 0) {
+      nsSMILTimeValue indefiniteEnd;
+      indefiniteEnd.SetIndefinite();
+
+      tempEnd = CalcActiveEnd(tempBegin, indefiniteEnd);
+    } else {
+      // 
+      // Start searching from the beginning again.
+      //
+      endPos = 0;
+
+      PRBool endFound = GetNextGreaterOrEqual(mEndInstances, tempBegin,
+                                              endPos, tempEnd);
+
+      if ((!aFirstInterval && tempEnd.CompareTo(aBeginAfter) == 0) ||
+          (aFirstInterval && tempEnd.CompareTo(tempBegin) == 0 && 
+           endPos <= endMaxPos)) {
+        endFound = 
+          GetNextGreaterOrEqual(mEndInstances, tempBegin, endPos, tempEnd);
+      }
+
+      endMaxPos = endPos;
+
+      if (!endFound) {
+        if (mEndHasEventConditions || mEndInstances.Length() == 0) {
+          tempEnd.SetUnresolved();
+        } else {
+          // 
+          // This is a little counter-intuitive but according to SMILANIM, if
+          // all the ends are before the begin, we _don't_ just assume an
+          // infinite end, it's actually a bad interval. ASV however will just
+          // use an infinite end.
+          // 
+          return NS_ERROR_FAILURE;
+        }
+      }
+
+      tempEnd = CalcActiveEnd(tempBegin, tempEnd);
+    }
+
+    if (tempEnd.CompareTo(zeroTime) > 0) {
+      aResult.mBegin = tempBegin;
+      aResult.mEnd = tempEnd;
+      return NS_OK;
+    } else if (mRestartMode == RESTART_NEVER) {
+      return NS_ERROR_FAILURE;
+    } else {
+      beginAfter = tempEnd;
+    }
+  }
+  NS_NOTREACHED("Hmm... we really shouldn't be here");
+
+  return NS_ERROR_FAILURE;
+}
+
+PRBool
+nsSMILTimedElement::GetNextGreaterOrEqual(
+    const nsTArray<nsSMILInstanceTime>& aList,
+    const nsSMILTimeValue& aBase,
+    PRInt32 &aPosition,
+    nsSMILTimeValue& aResult)
+{
+  PRBool found = PR_FALSE;
+  PRInt32 count = aList.Length();
+
+  for (; aPosition < count && !found; ++aPosition) {
+    const nsSMILInstanceTime &val = aList[aPosition];
+    if (val.Time().CompareTo(aBase) >= 0) {
+      aResult = val.Time();
+      found = PR_TRUE;
+    }
+  }
+
+  return found;
+}
+
+/**
+ * @see SMILANIM 3.3.4
+ */
+nsSMILTimeValue
+nsSMILTimedElement::CalcActiveEnd(const nsSMILTimeValue& aBegin,
+                                  const nsSMILTimeValue& aEnd)
+{
+  nsSMILTimeValue result;
+
+  if (!mSimpleDur.IsIndefinite() && !mSimpleDur.IsResolved()) {
+    NS_ERROR("Unresolved simple duration in CalcActiveEnd.");
+    result.SetIndefinite();
+    return result;
+  }
+
+  if (!aBegin.IsResolved() && !aBegin.IsIndefinite()) {
+    NS_ERROR("Unresolved begin time passed to CalcActiveEnd.");
+    result.SetIndefinite();
+    return result;
+  }
+
+  if (mRepeatDur.IsIndefinite() || aBegin.IsIndefinite()) {
+    result.SetIndefinite();
+  } else {
+    result = GetRepeatDuration();
+  }
+
+  if (aEnd.IsResolved() && aBegin.IsResolved()) {
+    nsSMILTime activeDur = aEnd.GetMillis() - aBegin.GetMillis();
+
+    if (result.IsResolved()) {
+      result.SetMillis(PR_MIN(result.GetMillis(), activeDur));
+    } else {
+      result.SetMillis(activeDur);
+    }
+  }
+
+  result = ApplyMinAndMax(result);
+
+  if (result.IsResolved()) {
+    nsSMILTime activeEnd = result.GetMillis() + aBegin.GetMillis();
+    result.SetMillis(activeEnd);
+  }
+
+  return result;
+}
+
+nsSMILTimeValue
+nsSMILTimedElement::GetRepeatDuration()
+{
+  nsSMILTimeValue result;
+
+  if (mRepeatCount.IsDefinite() && mRepeatDur.IsResolved()) {
+    if (mSimpleDur.IsResolved()) {
+      nsSMILTime activeDur = mRepeatCount * double(mSimpleDur.GetMillis());
+      result.SetMillis(PR_MIN(activeDur, mRepeatDur.GetMillis()));
+    } else {
+      result = mRepeatDur;
+    }
+  } else if (mRepeatCount.IsDefinite() && mSimpleDur.IsResolved()) {
+    nsSMILTime activeDur = mRepeatCount * double(mSimpleDur.GetMillis());
+    result.SetMillis(activeDur);
+  } else if (mRepeatDur.IsResolved()) {
+    result = mRepeatDur;
+  } else if (mRepeatCount.IsIndefinite()) {
+    result.SetIndefinite();
+  } else {
+    result = mSimpleDur;
+  }
+
+  return result;
+}
+
+nsSMILTimeValue
+nsSMILTimedElement::ApplyMinAndMax(const nsSMILTimeValue& aDuration)
+{
+  if (!aDuration.IsResolved() && !aDuration.IsIndefinite()) {
+    return aDuration;
+  }
+
+  if (mMax.CompareTo(mMin) < 0) {
+    return aDuration;
+  }
+
+  nsSMILTimeValue result;
+
+  if (aDuration.CompareTo(mMax) > 0) {
+    result = mMax;
+  } else if (aDuration.CompareTo(mMin) < 0) {
+    nsSMILTimeValue repeatDur = GetRepeatDuration();
+    result = (mMin.CompareTo(repeatDur) > 0) ? repeatDur : mMin;
+  } else {
+    result = aDuration;
+  }
+
+  return result;
+}
+
+nsSMILTime
+nsSMILTimedElement::ActiveTimeToSimpleTime(nsSMILTime aActiveTime,
+                                           PRUint32& aRepeatIteration)
+{
+  nsSMILTime result;
+
+  NS_ASSERTION(mSimpleDur.IsResolved() || mSimpleDur.IsIndefinite(),
+      "Trying to calculate active time with unresolved duration");
+
+  if (mSimpleDur.IsIndefinite() || mSimpleDur.GetMillis() == 0L) {
+    aRepeatIteration = 0;
+    result = aActiveTime;
+  } else {    
+    result = aActiveTime % mSimpleDur.GetMillis();
+    aRepeatIteration = (PRUint32)(aActiveTime / mSimpleDur.GetMillis());
+  }
+
+  return result;
+}
+
+//
+// Although in many cases it would be possible to check for an early end and
+// adjust the current interval well in advance the SMIL Animation spec seems to
+// indicate that we should only apply an early end at the latest possible
+// moment. In particular, this paragraph from section 3.6.8:
+//
+// 'If restart  is set to "always", then the current interval will end early if
+// there is an instance time in the begin list that is before (i.e. earlier
+// than) the defined end for the current interval. Ending in this manner will
+// also send a changed time notice to all time dependents for the current
+// interval end.'
+//
+void
+nsSMILTimedElement::CheckForEarlyEnd(const nsSMILTimeValue& aDocumentTime)
+{
+  if (mRestartMode != RESTART_ALWAYS)
+    return;
+
+  nsSMILTimeValue nextBegin;
+  PRInt32 position = 0;
+
+  while (GetNextGreaterOrEqual(mBeginInstances, mCurrentInterval.mBegin,
+                               position, nextBegin)
+         && nextBegin.CompareTo(mCurrentInterval.mBegin) == 0);
+        
+  if (nextBegin.IsResolved() && 
+      nextBegin.CompareTo(mCurrentInterval.mBegin) > 0 &&
+      nextBegin.CompareTo(mCurrentInterval.mEnd) < 0 &&
+      nextBegin.CompareTo(aDocumentTime) <= 0) {
+    mCurrentInterval.mEnd = nextBegin;
+  }
+}
+
+void
+nsSMILTimedElement::UpdateCurrentInterval()
+{
+  if (mElementState == STATE_STARTUP)
+    return;
+
+  nsSMILInterval updatedInterval;
+  PRBool isFirstInterval = mOldIntervals.IsEmpty();
+
+  nsSMILTimeValue beginAfter;
+  if (!isFirstInterval) {
+    beginAfter = mOldIntervals[mOldIntervals.Length() - 1].mEnd;
+  } else {
+    beginAfter.SetMillis(LL_MININT);
+  }
+
+  nsresult rv = GetNextInterval(beginAfter, isFirstInterval, updatedInterval);
+
+  if (NS_SUCCEEDED(rv)) {
+
+    if (mElementState != STATE_ACTIVE &&
+        updatedInterval.mBegin.CompareTo(mCurrentInterval.mBegin)) {
+      mCurrentInterval.mBegin = updatedInterval.mBegin;
+    }
+
+    if (updatedInterval.mEnd.CompareTo(mCurrentInterval.mEnd)) {
+      mCurrentInterval.mEnd = updatedInterval.mEnd;
+    }
+
+    if (mElementState == STATE_POSTACTIVE) {
+      // XXX notify dependents of new interval
+      mElementState = STATE_WAITING;
+    }
+  } else {
+
+    nsSMILTimeValue unresolvedTime;
+    mCurrentInterval.mEnd = unresolvedTime;
+    if (mElementState != STATE_ACTIVE) {
+      mCurrentInterval.mBegin = unresolvedTime;
+    }
+
+    if (mElementState == STATE_WAITING) {
+      // XXX notify dependents the current interval has been deleted
+      mElementState = STATE_POSTACTIVE;
+    }
+
+    if (mElementState == STATE_ACTIVE) {
+      // XXX notify dependents the current interval has been deleted
+      mElementState = STATE_POSTACTIVE;
+      if (mClient) {
+        mClient->Inactivate(PR_FALSE);
+      }
+    }
+  }
+}
+
+void
+nsSMILTimedElement::SampleSimpleTime(nsSMILTime aActiveTime)
+{
+  if (mClient) {
+    PRUint32 repeatIteration;
+    nsSMILTime simpleTime = 
+      ActiveTimeToSimpleTime(aActiveTime, repeatIteration);
+    mClient->SampleAt(simpleTime, mSimpleDur, repeatIteration);
+  }
+}
+
+void
+nsSMILTimedElement::SampleFillValue()
+{
+  if (mFillMode != FILL_FREEZE)
+    return;
+
+  if (!mClient)
+    return;
+
+  PRUint32 repeatIteration;
+  nsSMILTime activeTime = 
+    mCurrentInterval.mEnd.GetMillis() - mCurrentInterval.mBegin.GetMillis();
+
+  nsSMILTime simpleTime = 
+    ActiveTimeToSimpleTime(activeTime, repeatIteration);
+
+  if (simpleTime == 0L) {
+    mClient->SampleLastValue(--repeatIteration);
+  } else {
+    mClient->SampleAt(simpleTime, mSimpleDur, repeatIteration);
+  }
+}
+
+PRBool
+nsSMILTimedElement::AddInstanceTimeFromCurrentTime(double aOffsetSeconds,
+    PRBool aIsBegin, const nsSMILTimeContainer* aContainer)
+{
+  /*
+   * SMIL doesn't say what to do if someone calls BeginElement etc. before the
+   * document has started. For now we just fail.
+   */
+  if (!aContainer)
+    return PR_FALSE;
+
+  double offset = aOffsetSeconds * PR_MSEC_PER_SEC;
+
+  nsSMILTime timeWithOffset = 
+    aContainer->GetCurrentTime() + PRInt64(NS_round(offset));
+
+  nsSMILTimeValue timeVal;
+  timeVal.SetMillis(timeWithOffset);
+
+  nsSMILInstanceTime instanceTime(timeVal, nsnull, PR_TRUE);
+  AddInstanceTime(instanceTime, aIsBegin);
+
+  return PR_TRUE;
+}
new file mode 100644
--- /dev/null
+++ b/content/smil/nsSMILTimedElement.h
@@ -0,0 +1,298 @@
+/* -*- 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 SMIL module.
+ *
+ * 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>
+ *
+ * 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 NS_SMILTIMEDELEMENT_H_
+#define NS_SMILTIMEDELEMENT_H_
+
+#include "nsSMILInterval.h"
+#include "nsSMILInstanceTime.h"
+#include "nsSMILTimeValueSpec.h"
+#include "nsSMILRepeatCount.h"
+#include "nsSMILTypes.h"
+#include "nsTArray.h"
+#include "nsAutoPtr.h"
+#include "nsAttrValue.h"
+
+class nsSMILAnimationFunction;
+class nsSMILTimeContainer;
+class nsSMILTimeValue;
+class nsIAtom;
+
+//----------------------------------------------------------------------
+// nsSMILTimedElement
+
+class nsSMILTimedElement
+{
+public:
+  nsSMILTimedElement();
+
+  /** 
+   * Methods for supporting the nsIDOMElementTimeControl interface.
+   */
+
+  /*
+   * Adds a new begin instance time at the current document time (as defined by
+   * aContainer) plus or minus the specified offset.
+   *
+   * @param aOffsetSeconds A real number specifying the number of seconds to add
+   *                       to the current container time.
+   * @param aContainer The time container with which this timed element is
+   *                   associated and which should be used for determining the
+   *                   current time.
+   * @return NS_OK if the operation succeeeded, or an error code otherwise.
+   */
+  PRBool BeginElementAt(double aOffsetSeconds,
+                        const nsSMILTimeContainer* aContainer);
+
+  /*
+   * Adds a new end instance time at the current document time (as defined by
+   * aContainer) plus or minus the specified offset.
+   *
+   * @param aOffsetSeconds A real number specifying the number of seconds to add
+   *                       to the current container time.
+   * @param aContainer The time container with which this timed element is
+   *                   associated and which should be used for determining the
+   *                   current time.
+   * @return NS_OK if the operation succeeeded, or an error code otherwise.
+   */
+  PRBool EndElementAt(double aOffsetSeconds,
+                      const nsSMILTimeContainer* aContainer);
+
+  /**
+   * Adds an instance time object this element's list of instance times.
+   * These instance times are used when creating intervals.
+   *
+   * This method is typically called by an nsSMILTimeValueSpec.
+   *
+   * @param aInstanceTime   The time to add, expressed in document time.
+   *
+   * @param aIsBegin        True if the time to be added represents a begin time
+   *                        or false if it represents an end time.
+   */
+  void AddInstanceTime(const nsSMILInstanceTime& aInstanceTime,
+                       PRBool aIsBegin);
+
+  /**
+   * Sets the object that will be called by this timed element each time it is
+   * sampled.
+   *
+   * In Schmitz's model it is possible to associate several time clients with
+   * a timed element but for now we only allow one.
+   *
+   * @param aClient   The time client to associate. Any previous time client
+   *                  will be disassociated and no longer sampled. Setting this
+   *                  to nsnull will simply disassociate the previous client, if
+   *                  any.
+   */
+  void SetTimeClient(nsSMILAnimationFunction* aClient);
+
+  /**
+   * Samples the object at the given document time. Timing intervals are updated
+   * and if this element is active at the given time the associated time client
+   * will be sampled with the appropriate simple time.
+   *
+   * @param aDocumentTime The document time at which to sample.
+   */
+  void SampleAt(nsSMILTime aDocumentTime);
+
+  /**
+   * Reset the element's internal state. As described in SMILANIM 3.3.7, all
+   * instance times associated with DOM calls, events, etc. are cleared.
+   */
+  void Reset();
+
+  /**
+   * Restores the element to its initial state. As with Reset() this involves
+   * clearing certain instance times, however in addition to the Reset()
+   * behaviour this method also discards all previously constructed intervals,
+   * removes any previously applied fill effects, and resets the elements
+   * internal state. It is suitable for use, for example, when the begin or end
+   * attribute has been updated.
+   */
+  void HardReset();
+
+  /**
+   * Attempts to set an attribute on this timed element.
+   *
+   * @param aAttribute  The name of the attribute to set. The namespace of this
+   *                    attribute is not specified as it is checked by the host
+   *                    element. Only attributes in the namespace defined for
+   *                    SMIL attributes in the host language are passed to the
+   *                    timed element.
+   * @param aValue      The attribute value.
+   * @param aResult     The nsAttrValue object that may be used for storing the
+   *                    parsed result.
+   * @param[out] aParseResult The result of parsing the attribute. Will be set
+   *                          to NS_OK if parsing is successful.
+   *
+   * @return PR_TRUE if the given attribute is a timing attribute, PR_FALSE
+   * otherwise.
+   */
+  PRBool SetAttr(nsIAtom* aAttribute, const nsAString& aValue,
+                 nsAttrValue& aResult, nsresult* aParseResult = nsnull);
+
+  /**
+   * Attempts to unset an attribute on this timed element.
+   *
+   * @param aAttribute  The name of the attribute to set. As with SetAttr the
+   *                    namespace of the attribute is not specified (see
+   *                    SetAttr).
+   *
+   * @return PR_TRUE if the given attribute is a timing attribute, PR_FALSE
+   * otherwise.
+   */
+  PRBool UnsetAttr(nsIAtom* aAttribute);
+
+protected:
+  //
+  // Implementation helpers
+  //
+
+  nsresult          SetBeginSpec(const nsAString& aBeginSpec);
+  nsresult          SetEndSpec(const nsAString& aEndSpec);
+  nsresult          SetSimpleDuration(const nsAString& aDurSpec);
+  nsresult          SetMin(const nsAString& aMinSpec);
+  nsresult          SetMax(const nsAString& aMaxSpec);
+  nsresult          SetRestart(const nsAString& aRestartSpec);
+  nsresult          SetRepeatCount(const nsAString& aRepeatCountSpec);
+  nsresult          SetRepeatDur(const nsAString& aRepeatDurSpec);
+  nsresult          SetFillMode(const nsAString& aFillModeSpec);
+
+  void              UnsetBeginSpec();
+  void              UnsetEndSpec();
+  void              UnsetSimpleDuration();
+  void              UnsetMin();
+  void              UnsetMax();
+  void              UnsetRestart();
+  void              UnsetRepeatCount();
+  void              UnsetRepeatDur();
+  void              UnsetFillMode();
+
+  nsresult          SetBeginOrEndSpec(const nsAString& aSpec, PRBool aIsBegin);
+
+  /**
+   * Calculates the first acceptable interval for this element.
+   *
+   * @see SMILANIM 3.6.8
+   */
+  nsresult          GetNextInterval(const nsSMILTimeValue& aBeginAfter,
+                                    PRBool aFirstInstance,
+                                    nsSMILInterval& aResult);
+  PRBool            GetNextGreaterOrEqual(
+                                   const nsTArray<nsSMILInstanceTime>& aList,
+                                   const nsSMILTimeValue& aBase,
+                                   PRInt32& aPosition,
+                                   nsSMILTimeValue& aResult);
+  nsSMILTimeValue   CalcActiveEnd(const nsSMILTimeValue& aBegin,
+                                  const nsSMILTimeValue& aEnd);
+  nsSMILTimeValue   GetRepeatDuration();
+  nsSMILTimeValue   ApplyMinAndMax(const nsSMILTimeValue& aDuration);
+  nsSMILTime        ActiveTimeToSimpleTime(nsSMILTime aActiveTime,
+                                           PRUint32& aRepeatIteration);
+  void              CheckForEarlyEnd(const nsSMILTimeValue& aDocumentTime);
+  void              UpdateCurrentInterval();
+  void              SampleSimpleTime(nsSMILTime aActiveTime);
+  void              SampleFillValue();
+  PRBool            AddInstanceTimeFromCurrentTime(double aOffsetSeconds,
+                        PRBool aIsBegin, const nsSMILTimeContainer* aContainer);
+
+  // Typedefs
+  typedef nsTArray<nsRefPtr<nsSMILTimeValueSpec> >  SMILTimeValueSpecList;
+
+  //
+  // Members
+  //
+  SMILTimeValueSpecList mBeginSpecs;
+  SMILTimeValueSpecList mEndSpecs;
+
+  nsSMILTimeValue                 mSimpleDur;
+
+  nsSMILRepeatCount               mRepeatCount;
+  nsSMILTimeValue                 mRepeatDur;
+
+  nsSMILTimeValue                 mMin;
+  nsSMILTimeValue                 mMax;
+
+  enum nsSMILFillMode
+  {
+    FILL_REMOVE,
+    FILL_FREEZE
+  };
+  nsSMILFillMode                  mFillMode;
+  static nsAttrValue::EnumTable   sFillModeTable[];
+
+  enum nsSMILRestartMode
+  {
+    RESTART_ALWAYS,
+    RESTART_WHENNOTACTIVE,
+    RESTART_NEVER
+  };
+  nsSMILRestartMode               mRestartMode;
+  static nsAttrValue::EnumTable   sRestartModeTable[];
+
+  //
+  // We need to distinguish between attempting to set the begin spec and failing
+  // (in which case the mBeginSpecs array will be empty) and not attempting to
+  // set the begin spec at all. In the first case, we should act as if the begin
+  // was indefinite, and in the second, we should act as if begin was 0s.
+  //
+  PRPackedBool                    mBeginSpecSet;
+
+  PRPackedBool                    mEndHasEventConditions;
+
+  nsTArray<nsSMILInstanceTime>    mBeginInstances;
+  nsTArray<nsSMILInstanceTime>    mEndInstances;
+
+  nsSMILAnimationFunction*        mClient;
+  nsSMILInterval                  mCurrentInterval;
+  nsTArray<nsSMILInterval>        mOldIntervals;
+
+  /**
+   * The state of the element in its life-cycle. These states are based on the
+   * element life-cycle described in SMILANIM 3.6.8
+   */
+  enum nsSMILElementState
+  {
+    STATE_STARTUP,
+    STATE_WAITING,
+    STATE_ACTIVE,
+    STATE_POSTACTIVE
+  };
+  nsSMILElementState              mElementState;
+};
+
+#endif // NS_SMILTIMEDELEMENT_H_
new file mode 100644
--- /dev/null
+++ b/content/smil/nsSMILTypes.h
@@ -0,0 +1,57 @@
+/* -*- 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 SMIL module.
+ *
+ * The Initial Developer of the Original Code is Brian Birtles.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian Birtles <birtles@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
+ * 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 NS_SMILTYPES_H_
+#define NS_SMILTYPES_H_
+
+#include "prtypes.h"
+
+// A timestamp in milliseconds
+//
+// A time may represent:
+//
+//   simple time -- offset within the simple duration
+//   active time -- offset within the active duration
+//   document time -- offset since the document begin
+//   wallclock time -- "real" time -- offset since the epoch
+//
+// For an overview of how this class is related to other SMIL time classes see
+// the documentstation in nsSMILTimeValue.h
+//
+typedef PRInt64 nsSMILTime;
+
+#endif // NS_SMILTYPES_H_
new file mode 100644
--- /dev/null
+++ b/content/smil/nsSMILValue.cpp
@@ -0,0 +1,130 @@
+/* -*- 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 SMIL module.
+ *
+ * The Initial Developer of the Original Code is Brian Birtles.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Robert O'Callahan <roc+moz@cs.cmu.edu>
+ *   Brian Birtles <birtles@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
+ * 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 "nsSMILValue.h"
+#include "nsDebug.h"
+
+nsSMILValue::nsSMILValue(const nsISMILType* aType)
+: mU(),
+  mType(&nsSMILNullType::sSingleton)
+{
+  if (!aType) return;
+
+  nsresult rv = aType->Init(*this);
+  NS_POSTCONDITION(mType == aType || (NS_FAILED(rv) && IsNull()),
+    "Post-condition of Init failed. nsSMILValue is invalid.");
+}
+
+nsSMILValue::nsSMILValue(const nsSMILValue& aVal)
+:
+  mU(),
+  mType(&nsSMILNullType::sSingleton)
+{
+  nsresult rv = aVal.mType->Init(*this);
+  NS_POSTCONDITION(mType == aVal.mType || (NS_FAILED(rv) && IsNull()),
+    "Post-condition of Init failed. nsSMILValue is invalid.");
+  if (NS_FAILED(rv)) return;
+  mType->Assign(*this, aVal);
+}
+
+const nsSMILValue&
+nsSMILValue::operator=(const nsSMILValue& aVal)
+{
+  if (&aVal == this)
+    return *this;
+
+  if (mType != aVal.mType) {
+    mType->Destroy(*this);
+    NS_POSTCONDITION(IsNull(), "nsSMILValue not null after destroying");
+    nsresult rv = aVal.mType->Init(*this);
+    NS_POSTCONDITION(mType == aVal.mType || (NS_FAILED(rv) && IsNull()),
+      "Post-condition of Init failed. nsSMILValue is invalid.");
+    if (NS_FAILED(rv)) return *this;
+  }
+
+  mType->Assign(*this, aVal);
+
+  return *this;
+}
+
+nsresult
+nsSMILValue::Add(const nsSMILValue& aValueToAdd, PRUint32 aCount)
+{
+  if (aValueToAdd.IsNull()) return NS_OK;
+
+  if (aValueToAdd.mType != mType) {
+    NS_WARNING("Trying to add incompatible types.");
+    return NS_ERROR_FAILURE;
+  }
+
+  return mType->Add(*this, aValueToAdd, aCount);
+}
+
+nsresult
+nsSMILValue::ComputeDistance(const nsSMILValue& aTo, double& aDistance) const
+{
+  if (aTo.mType != mType) {
+    NS_WARNING("Trying to calculate distance between incompatible types.");
+    return NS_ERROR_FAILURE;
+  }
+
+  return mType->ComputeDistance(*this, aTo, aDistance);
+}
+
+nsresult
+nsSMILValue::Interpolate(const nsSMILValue& aEndVal,
+                         double aUnitDistance,
+                         nsSMILValue& aResult) const
+{
+  if (aEndVal.mType != mType) {
+    NS_WARNING("Trying to interpolate between incompatible types.");
+    return NS_ERROR_FAILURE;
+  }
+
+  if (aResult.mType != mType) {
+    aResult.mType->Destroy(aResult);
+    NS_POSTCONDITION(aResult.IsNull(), "nsSMILValue not null after destroying");
+    nsresult rv = mType->Init(aResult);
+    NS_POSTCONDITION(aResult.mType == mType
+      || (NS_FAILED(rv) && aResult.IsNull()),
+      "Post-condition of Init failed. nsSMILValue is invalid.");
+    if (NS_FAILED(rv)) return rv;
+  }
+
+  return mType->Interpolate(*this, aEndVal, aUnitDistance, aResult);
+}
new file mode 100644
--- /dev/null
+++ b/content/smil/nsSMILValue.h
@@ -0,0 +1,78 @@
+/* -*- 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 SMIL module.
+ *
+ * The Initial Developer of the Original Code is Brian Birtles.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Robert O'Callahan <roc+moz@cs.cmu.edu>
+ *   Brian Birtles <birtles@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
+ * 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 NS_SMILVALUE_H_
+#define NS_SMILVALUE_H_
+
+#include "nsISMILType.h"
+#include "nsSMILNullType.h"
+
+class nsSMILValue
+{
+public:
+  nsSMILValue() : mU(), mType(&nsSMILNullType::sSingleton) { }
+  nsSMILValue(const nsISMILType* aType);
+  nsSMILValue(const nsSMILValue& aVal);
+
+  ~nsSMILValue()
+  {
+    mType->Destroy(*this);
+  }
+
+  const nsSMILValue& operator=(const nsSMILValue& aVal);
+
+  PRBool IsNull() const
+  {
+    return (mType == &nsSMILNullType::sSingleton);
+  }
+
+  nsresult Add(const nsSMILValue& aValueToAdd, PRUint32 aCount = 1);
+  nsresult ComputeDistance(const nsSMILValue& aTo, double& aDistance) const;
+  nsresult Interpolate(const nsSMILValue& aEndVal,
+                       double aUnitDistance,
+                       nsSMILValue& aResult) const;
+
+  union {
+    PRInt64 mInt;
+    double mDouble;
+    void* mPtr;
+  } mU;
+  const nsISMILType* mType;
+};
+
+#endif  // NS_SMILVALUE_H_
new file mode 100644
--- /dev/null
+++ b/content/smil/test/Makefile.in
@@ -0,0 +1,50 @@
+# ***** 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 SMIL module.
+#
+# The Initial Developer of the Original Code is
+# Mozilla Corporation.
+# Portions created by the Initial Developer are Copyright (C) 2008
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Daniel Holbert <dholbert@mozilla.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
+# 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 *****
+DEPTH		= ../../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+relativesrcdir  = content/smil/test
+
+include $(DEPTH)/config/autoconf.mk
+include $(topsrcdir)/config/rules.mk
+
+_TEST_FILES = 	test_smilRestart.xhtml \
+		$(NULL)
+
+libs:: $(_TEST_FILES)
+	$(INSTALL) $^ $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/content/smil/test/test_smilRestart.xhtml
@@ -0,0 +1,105 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+  <title>Test for SMIL Restart Behavior </title>
+  <script type="text/javascript" src="/MochiKit/packed.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+<svg id="svg" xmlns="http://www.w3.org/2000/svg" width="120px" height="120px">
+  <!-- These 3 circles only differ in their animation's "restart" value -->
+  <circle cx="20" cy="20" r="15" fill="blue">
+    <animate attributeName="cx" from="20" to="100" begin="0s" dur="4s"
+             restart="always" id="always" attributeType="XML"/>
+  </circle>
+  <circle cx="20" cy="60" r="15" fill="blue">
+    <animate attributeName="cx" from="20" to="100" begin="0s" dur="4s"
+             restart="whenNotActive" id="whenNotActive" attributeType="XML"/>
+  </circle>
+  <circle cx="20" cy="100" r="15" fill="blue">
+    <animate attributeName="cx" from="20" to="100" begin="0s" dur="4s"
+             restart="never" id="never" attributeType="XML"/>
+  </circle>
+</svg>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+<![CDATA[
+/** Test for SMIL Restart Behavior  **/
+
+/* Global Variables */
+var dur = 4.0; // this must match the "dur" attribute on animations above.
+var svg = document.getElementById("svg");
+
+SimpleTest.waitForExplicitFinish();
+
+// main: just triggers the first link in the chain of function-calls
+function main() {
+  checkInitialState();
+}
+
+// Attempt a "beginElement" call on the given element, and
+// complain if we don't get the expected return value.
+// 'time' is only provided for better diagnostic output.
+function tryRestartElem(id, time, expectedRetVal) {
+  var elem = document.getElementById(id); 
+  var retVal = elem.beginElement();
+  is(retVal, expectedRetVal,
+     "Error restarting animation '" + id + 
+     "' at time = " + time + ": " + 
+     "expected return value of " + expectedRetVal + 
+     ", but got " + retVal + ".");
+}
+
+function checkInitialState() {
+  svg.setCurrentTime(0.0);
+  setTimeout('doCheckInitialState(0.0)', 0);
+}
+function doCheckInitialState(time) {
+  tryRestartElem('always', time, true);
+  tryRestartElem('whenNotActive', time, false);
+  tryRestartElem('never', time, false);
+  checkHalfwayState();
+}
+
+function checkHalfwayState() {
+  svg.setCurrentTime(0.5 * dur);
+  setTimeout('doCheckHalfwayState(0.5 * dur)', 0);
+}
+function doCheckHalfwayState(time) {
+  tryRestartElem('always', time, true);
+  tryRestartElem('whenNotActive', time, false);
+  tryRestartElem('never', time, false);
+  checkEndingState();
+}
+
+function checkEndingState() {
+  svg.setCurrentTime(dur);
+  setTimeout('doCheckEndingState(dur)', 0);
+}
+function doCheckEndingState(time) {
+  tryRestartElem('always', time, true);
+  tryRestartElem('whenNotActive', time, true);
+  tryRestartElem('never', time, false);
+  checkAfterEndingState();
+}
+
+function checkAfterEndingState() {
+  svg.setCurrentTime(dur * 3);
+  setTimeout('doCheckAfterEndingState(dur * 3)', 0);
+}
+function doCheckAfterEndingState(time) {
+  tryRestartElem('always', time, true);
+  tryRestartElem('whenNotActive', time, true);
+  tryRestartElem('never', time, false);
+  SimpleTest.finish();
+}
+
+window.addEventListener("load", main, false);
+]]>
+</script>
+</pre>
+</body>
+</html>
--- a/content/svg/content/src/Makefile.in
+++ b/content/svg/content/src/Makefile.in
@@ -16,16 +16,17 @@
 #
 # The Initial Developer of the Original Code is
 # Crocodile Clips Ltd.
 # Portions created by the Initial Developer are Copyright (C) 2001
 # the Initial Developer. All Rights Reserved.
 #
 # Contributor(s):
 #   Alex Fritze <alex.fritze@crocodile-clips.com>
+#   Chris Double  <chris.double@double.co.nz>
 #
 # 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
@@ -132,16 +133,23 @@ CPPSRCS		= \
 		nsSVGTitleElement.cpp \
 		nsSVGTransform.cpp \
 		nsSVGTransformList.cpp \
 		nsSVGTransformListParser.cpp \
 		nsSVGUseElement.cpp \
 		nsSVGValue.cpp \
 		$(NULL)
 
+ifdef MOZ_SMIL
+CPPSRCS += nsSVGAnimateElement.cpp \
+           nsSVGAnimationElement.cpp \
+           nsSVGSetElement.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
 
 EXPORTS =  			\
 	nsIDOMSVGListener.h \
 	nsIDOMSVGZoomListener.h \
@@ -150,25 +158,30 @@ EXPORTS =  			\
 	nsISVGValueObserver.h      \
 	nsISVGValueUtils.h         \
 	nsSVGNumber.h              \
 	nsSVGRect.h                \
 	nsSVGPoint.h               \
 	nsSVGMatrix.h              \
 	$(NULL)
 
-
 include $(topsrcdir)/config/rules.mk
 
 INCLUDES += 	\
 		-I$(srcdir)/../../../shared/public \
 		-I$(srcdir)/../../../html/base/src \
 		-I$(srcdir)/../../../xml/content/src \
 		-I$(srcdir)/../../../../dom \
 		-I$(srcdir)/../../../base/src \
 		-I$(srcdir)/../../../../layout/svg/base/src \
 		-I$(srcdir)/../../../../layout/style \
 		-I$(srcdir)/../../../events/src \
 		-I$(srcdir)/../../../html/content/src \
 		-I$(topsrcdir)/content/xbl/src \
 		$(NULL)
 
+ifdef MOZ_SMIL
+INCLUDES += 	\
+		-I$(srcdir)/../../../smil \
+		$(NULL)
+endif
+
 DEFINES += -D_IMPL_NS_LAYOUT
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/nsSVGAnimateElement.cpp
@@ -0,0 +1,106 @@
+/* -*- 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 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>
+ *
+ * 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 "nsSVGAnimationElement.h"
+#include "nsIDOMSVGAnimateElement.h"
+#include "nsSMILAnimationFunction.h"
+
+typedef nsSVGAnimationElement nsSVGAnimateElementBase;
+
+class nsSVGAnimateElement : public nsSVGAnimateElementBase,
+                            public nsIDOMSVGAnimateElement
+{
+protected:
+  friend nsresult NS_NewSVGAnimateElement(nsIContent **aResult,
+                                          nsINodeInfo *aNodeInfo);
+  nsSVGAnimateElement(nsINodeInfo* aNodeInfo);
+
+  nsSMILAnimationFunction mAnimationFunction;
+
+public:
+  // interfaces:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_NSIDOMSVGANIMATEELEMENT
+
+  NS_FORWARD_NSIDOMNODE(nsSVGAnimateElementBase::)
+  NS_FORWARD_NSIDOMELEMENT(nsSVGAnimateElementBase::)
+  NS_FORWARD_NSIDOMSVGELEMENT(nsSVGAnimateElementBase::)
+  NS_FORWARD_NSIDOMSVGANIMATIONELEMENT(nsSVGAnimateElementBase::)
+  
+  // nsIDOMNode
+  virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
+
+  // nsISMILAnimationElement
+  virtual nsSMILAnimationFunction& AnimationFunction();
+};
+
+NS_IMPL_NS_NEW_SVG_ELEMENT(Animate)
+
+//----------------------------------------------------------------------
+// nsISupports methods
+
+NS_IMPL_ADDREF_INHERITED(nsSVGAnimateElement,nsSVGAnimateElementBase)
+NS_IMPL_RELEASE_INHERITED(nsSVGAnimateElement,nsSVGAnimateElementBase)
+
+NS_INTERFACE_TABLE_HEAD(nsSVGAnimateElement)
+  NS_NODE_INTERFACE_TABLE5(nsSVGAnimateElement, nsIDOMNode, nsIDOMElement,
+                           nsIDOMSVGElement, nsIDOMSVGAnimationElement,
+                           nsIDOMSVGAnimateElement)
+  NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(SVGAnimateElement)
+NS_INTERFACE_MAP_END_INHERITING(nsSVGAnimateElementBase)
+
+//----------------------------------------------------------------------
+// Implementation
+
+nsSVGAnimateElement::nsSVGAnimateElement(nsINodeInfo *aNodeInfo)
+  : nsSVGAnimateElementBase(aNodeInfo)
+{
+}
+
+//----------------------------------------------------------------------
+// nsIDOMNode methods
+
+NS_IMPL_ELEMENT_CLONE_WITH_INIT(nsSVGAnimateElement)
+
+//----------------------------------------------------------------------
+// nsISMILAnimationElement methods
+
+nsSMILAnimationFunction&
+nsSVGAnimateElement::AnimationFunction()
+{
+  return mAnimationFunction;
+}
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/nsSVGAnimationElement.cpp
@@ -0,0 +1,404 @@
+/* -*- 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 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>
+ *   Chris Double  <chris.double@double.co.nz>
+ *
+ * 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 "nsSVGAnimationElement.h"
+#include "nsSVGSVGElement.h"
+#include "nsSMILTimeContainer.h"
+#include "nsSMILAnimationController.h"
+#include "nsSMILAnimationFunction.h"
+#include "nsISMILAttr.h"
+#include "nsBindingManager.h"
+
+//----------------------------------------------------------------------
+// nsISupports methods
+
+NS_IMPL_ADDREF_INHERITED(nsSVGAnimationElement, nsSVGAnimationElementBase)
+NS_IMPL_RELEASE_INHERITED(nsSVGAnimationElement, nsSVGAnimationElementBase)
+
+NS_INTERFACE_MAP_BEGIN(nsSVGAnimationElement)
+  NS_INTERFACE_MAP_ENTRY(nsISMILAnimationElement)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMElementTimeControl)
+NS_INTERFACE_MAP_END_INHERITING(nsSVGAnimationElementBase)
+
+//----------------------------------------------------------------------
+// Implementation
+
+nsSVGAnimationElement::nsSVGAnimationElement(nsINodeInfo *aNodeInfo)
+  : nsSVGAnimationElementBase(aNodeInfo),
+    mTimedDocumentRoot(nsnull)
+{
+}
+
+nsresult
+nsSVGAnimationElement::Init()
+{
+  nsresult rv = nsSVGAnimationElementBase::Init();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  AnimationFunction().SetAnimationElement(this);
+  mTimedElement.SetTimeClient(&AnimationFunction());
+
+  return NS_OK;
+}
+
+//----------------------------------------------------------------------
+// nsISMILAnimationElement methods
+
+const nsIContent&
+nsSVGAnimationElement::Content() const
+{
+  return *this;
+}
+
+nsIContent&
+nsSVGAnimationElement::Content()
+{
+  return *this;
+}
+
+const nsAttrValue*
+nsSVGAnimationElement::GetAnimAttr(nsIAtom* aName) const
+{
+  return mAttrsAndChildren.GetAttr(aName, kNameSpaceID_None);
+}
+
+nsIContent*
+nsSVGAnimationElement::GetTargetElementContent()
+{
+  if (HasAttr(kNameSpaceID_XLink, nsGkAtoms::href)) {
+    // XXXdholbert: Use xlink:href attr to look up target element here.
+
+    // Note: Need to check for updated target element each sample, because
+    // the existing target's ID might've changed, or another element
+    // with the same ID might've been inserted earlier in the DOM tree.
+    NS_NOTYETIMPLEMENTED("nsSVGAnimationElement::GetTargetElementContent for "
+                         "xlink:href-targeted animations");
+    return nsnull;
+  }
+
+  // No "xlink:href" attribute --> target is my parent.
+  return GetParentElement();
+}
+
+nsIAtom*
+nsSVGAnimationElement::GetTargetAttributeName() const
+{
+  const nsAttrValue* nameAttr
+    = mAttrsAndChildren.GetAttr(nsGkAtoms::attributeName);
+
+  if (!nameAttr)
+    return nsnull;
+
+  NS_ASSERTION(nameAttr->Type() == nsAttrValue::eAtom,
+    "attributeName should have been parsed as an atom");
+  return nameAttr->GetAtomValue();
+}
+
+nsSMILTargetAttrType
+nsSVGAnimationElement::GetTargetAttributeType() const
+{
+  nsIContent::AttrValuesArray typeValues[] = { &nsGkAtoms::css,
+                                               &nsGkAtoms::XML };
+  nsSMILTargetAttrType smilTypes[] = { eSMILTargetAttrType_CSS,
+                                       eSMILTargetAttrType_XML };
+  PRInt32 index = FindAttrValueIn(kNameSpaceID_None,
+                                  nsGkAtoms::attributeType,
+                                  typeValues,
+                                  eCaseMatters);
+  return (index >= 0) ? smilTypes[index] : eSMILTargetAttrType_auto;
+}
+
+nsSMILTimedElement&
+nsSVGAnimationElement::TimedElement()
+{
+  return mTimedElement;
+}
+
+//----------------------------------------------------------------------
+// nsIDOMSVGAnimationElement methods
+
+/* readonly attribute SVGElement targetElement; */
+NS_IMETHODIMP
+nsSVGAnimationElement::GetTargetElement(nsIDOMSVGElement** aTarget)
+{
+  // We'll just call the other GetTargetElement method, and QI to the right type
+  nsIContent* targetContent = GetTargetElementContent();
+
+  nsCOMPtr<nsIDOMSVGElement> targetSVG = do_QueryInterface(targetContent);
+  NS_IF_ADDREF(*aTarget = targetSVG);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSVGAnimationElement::GetStartTime(float* retval)
+{
+  // XXX
+  *retval = 0.f;
+  NS_NOTYETIMPLEMENTED("nsSVGAnimationElement::GetStartTime");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsSVGAnimationElement::GetCurrentTime(float* retval)
+{
+  // XXX
+  *retval = 0.f;
+  NS_NOTYETIMPLEMENTED("nsSVGAnimationElement::GetCurrentTime");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsSVGAnimationElement::GetSimpleDuration(float* retval)
+{
+  // XXX
+  *retval = 0.f;
+  NS_NOTYETIMPLEMENTED("nsSVGAnimationElement::GetSimpleDuration");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+//----------------------------------------------------------------------
+// nsIContent methods
+
+nsresult
+nsSVGAnimationElement::BindToTree(nsIDocument* aDocument,
+                                  nsIContent* aParent,
+                                  nsIContent* aBindingParent,
+                                  PRBool aCompileEventHandlers)
+{
+  nsresult rv = nsSVGAnimationElementBase::BindToTree(aDocument, aParent,
+                                                      aBindingParent,
+                                                      aCompileEventHandlers);
+  NS_ENSURE_SUCCESS(rv,rv);
+
+  // XXXdholbert is ownerDOMSVG (as a check for SVG parent) still needed here?
+  nsCOMPtr<nsIDOMSVGSVGElement> ownerDOMSVG;
+  rv = GetOwnerSVGElement(getter_AddRefs(ownerDOMSVG));
+
+  if (NS_FAILED(rv) || !ownerDOMSVG)
+    // No use proceeding. We don't have an SVG parent (yet) so we won't be able
+    // to register ourselves etc. Maybe next time we'll have more luck.
+    // (This sort of situation will arise a lot when trees are being constructed
+    // piece by piece via script)
+    return NS_OK;
+
+  mTimedDocumentRoot = GetTimeContainer();
+  if (!mTimedDocumentRoot)
+    // Timed document root hasn't been created yet. This will be created when
+    // the SVG parent is bound. This happens when we create SVG trees entirely
+    // by script.
+    return NS_OK;
+
+  // Add myself to the animation controller's master set of animation elements.
+  if (aDocument) {
+    nsSMILAnimationController *controller = aDocument->GetAnimationController();
+    if (controller) {
+      controller->RegisterAnimationElement(this);
+    }
+  }
+
+  return NS_OK;
+}
+
+void
+nsSVGAnimationElement::UnbindFromTree(PRBool aDeep, PRBool aNullParent)
+{
+  nsIDocument *doc = GetOwnerDoc();
+  if (doc) {
+    nsSMILAnimationController *controller = doc->GetAnimationController();
+    if (controller) {
+      controller->UnregisterAnimationElement(this);
+    }
+  }
+
+  if (mTimedDocumentRoot) {
+    mTimedDocumentRoot = nsnull;
+  }
+
+  nsSVGAnimationElementBase::UnbindFromTree(aDeep, aNullParent);
+}
+
+//----------------------------------------------------------------------
+// nsIContent methods
+
+PRBool
+nsSVGAnimationElement::ParseAttribute(PRInt32 aNamespaceID,
+                                      nsIAtom* aAttribute,
+                                      const nsAString& aValue,
+                                      nsAttrValue& aResult)
+{
+  if (aNamespaceID == kNameSpaceID_None) {
+    // Deal with target-related attributes here
+    if (aAttribute == nsGkAtoms::attributeName ||
+        aAttribute == nsGkAtoms::attributeType) {
+      aResult.ParseAtom(aValue);
+      return PR_TRUE;
+    }
+
<