--- 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,
+ ¶ms);
+ 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.
+