From 8b6e3c3cc788e277fd187dfb56ba536866efc261 Mon Sep 17 00:00:00 2001 From: Brian Birtles Date: Sat, 31 Jul 2010 16:02:52 +0900 Subject: [PATCH] Bug 527270: Implement SMIL TimeEvents. r=dholbert,smaug; sr=roc; a=blocking-betaN --- content/base/public/nsContentUtils.h | 1 + content/base/src/nsContentUtils.cpp | 8 + content/base/src/nsGkAtomList.h | 6 + content/events/public/nsIPrivateDOMEvent.h | 4 + content/events/src/nsDOMEvent.cpp | 20 ++ content/events/src/nsDOMEvent.h | 5 + content/events/src/nsEventDispatcher.cpp | 9 + content/events/src/nsEventListenerManager.cpp | 8 + content/smil/Makefile.in | 2 + content/smil/nsDOMTimeEvent.cpp | 130 ++++++++ content/smil/nsDOMTimeEvent.h | 65 ++++ content/smil/nsSMILTimedElement.cpp | 146 +++++++-- content/smil/nsSMILTimedElement.h | 2 + content/smil/test/Makefile.in | 1 + content/smil/test/test_smilTimeEvents.xhtml | 292 ++++++++++++++++++ .../svg/content/src/nsSVGAnimationElement.cpp | 6 + .../svg/content/src/nsSVGAnimationElement.h | 3 + content/svg/content/src/nsSVGElement.cpp | 8 + dom/base/nsDOMClassInfo.cpp | 7 + dom/base/nsDOMClassInfoClasses.h | 1 + dom/interfaces/smil/Makefile.in | 1 + dom/interfaces/smil/nsIDOMTimeEvent.idl | 57 ++++ widget/public/nsGUIEvent.h | 10 + 23 files changed, 766 insertions(+), 26 deletions(-) create mode 100644 content/smil/nsDOMTimeEvent.cpp create mode 100644 content/smil/nsDOMTimeEvent.h create mode 100644 content/smil/test/test_smilTimeEvents.xhtml create mode 100644 dom/interfaces/smil/nsIDOMTimeEvent.idl diff --git a/content/base/public/nsContentUtils.h b/content/base/public/nsContentUtils.h index f5b549db2e26..b7df007a84ab 100644 --- a/content/base/public/nsContentUtils.h +++ b/content/base/public/nsContentUtils.h @@ -157,6 +157,7 @@ enum EventNameType { EventNameType_XUL = 0x0002, EventNameType_SVGGraphic = 0x0004, // svg graphic elements EventNameType_SVGSVG = 0x0008, // the svg element + EventNameType_SMIL = 0x0016, // smil elements EventNameType_HTMLXUL = 0x0003, EventNameType_All = 0xFFFF diff --git a/content/base/src/nsContentUtils.cpp b/content/base/src/nsContentUtils.cpp index b7b3c2aa0bdc..85ccf9c7e18a 100644 --- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -575,6 +575,14 @@ nsContentUtils::InitializeEventTable() { // This is a bit hackish, but SVG's event names are weird. { nsGkAtoms::onzoom, NS_SVG_ZOOM, EventNameType_SVGSVG, NS_EVENT_NULL }, #endif // MOZ_SVG +#ifdef MOZ_SMIL + { nsGkAtoms::onbegin, NS_SMIL_BEGIN, EventNameType_SMIL, NS_EVENT_NULL }, + { nsGkAtoms::onbeginEvent, NS_SMIL_BEGIN, EventNameType_None, NS_SMIL_TIME_EVENT }, + { nsGkAtoms::onend, NS_SMIL_END, EventNameType_SMIL, NS_EVENT_NULL }, + { nsGkAtoms::onendEvent, NS_SMIL_END, EventNameType_None, NS_SMIL_TIME_EVENT }, + { nsGkAtoms::onrepeat, NS_SMIL_REPEAT, EventNameType_SMIL, NS_EVENT_NULL }, + { nsGkAtoms::onrepeatEvent, NS_SMIL_REPEAT, EventNameType_None, NS_SMIL_TIME_EVENT }, +#endif // MOZ_SMIL #ifdef MOZ_MEDIA { nsGkAtoms::onloadstart, NS_LOADSTART, EventNameType_HTML, NS_EVENT_NULL }, { nsGkAtoms::onprogress, NS_PROGRESS, EventNameType_HTML, NS_EVENT_NULL }, diff --git a/content/base/src/nsGkAtomList.h b/content/base/src/nsGkAtomList.h index e893bea73f11..e07b8ff82385 100644 --- a/content/base/src/nsGkAtomList.h +++ b/content/base/src/nsGkAtomList.h @@ -1309,6 +1309,12 @@ GK_ATOM(keyPoints, "keyPoints") GK_ATOM(keySplines, "keySplines") GK_ATOM(keyTimes, "keyTimes") GK_ATOM(mozAnimateMotionDummyAttr, "_mozAnimateMotionDummyAttr") +GK_ATOM(onbegin, "onbegin") +GK_ATOM(onbeginEvent, "onbeginEvent") +GK_ATOM(onend, "onend") +GK_ATOM(onendEvent, "onendEvent") +GK_ATOM(onrepeat, "onrepeat") +GK_ATOM(onrepeatEvent, "onrepeatEvent") GK_ATOM(repeatCount, "repeatCount") GK_ATOM(repeatDur, "repeatDur") GK_ATOM(restart, "restart") diff --git a/content/events/public/nsIPrivateDOMEvent.h b/content/events/public/nsIPrivateDOMEvent.h index 327778ecc7b4..c149d68c3fb9 100644 --- a/content/events/public/nsIPrivateDOMEvent.h +++ b/content/events/public/nsIPrivateDOMEvent.h @@ -103,6 +103,10 @@ NS_NewDOMSVGEvent(nsIDOMEvent** aResult, nsPresContext* aPresContext, class nsEv nsresult NS_NewDOMSVGZoomEvent(nsIDOMEvent** aResult, nsPresContext* aPresContext, class nsGUIEvent* aEvent); #endif // MOZ_SVG +#ifdef MOZ_SMIL +nsresult +NS_NewDOMTimeEvent(nsIDOMEvent** aResult, nsPresContext* aPresContext, class nsEvent* aEvent); +#endif // MOZ_SMIL nsresult NS_NewDOMXULCommandEvent(nsIDOMEvent** aResult, nsPresContext* aPresContext, class nsInputEvent* aEvent); nsresult diff --git a/content/events/src/nsDOMEvent.cpp b/content/events/src/nsDOMEvent.cpp index fc25ae94d924..7b5befbd6d07 100644 --- a/content/events/src/nsDOMEvent.cpp +++ b/content/events/src/nsDOMEvent.cpp @@ -81,6 +81,9 @@ static const char* const sEventNames[] = { "SVGLoad", "SVGUnload", "SVGAbort", "SVGError", "SVGResize", "SVGScroll", "SVGZoom", #endif // MOZ_SVG +#ifdef MOZ_SMIL + "beginEvent", "endEvent", "repeatEvent", +#endif // MOZ_SMIL #ifdef MOZ_MEDIA "loadstart", "progress", "suspend", "emptied", "stalled", "play", "pause", "loadedmetadata", "loadeddata", "waiting", "playing", "canplay", @@ -773,6 +776,15 @@ NS_METHOD nsDOMEvent::DuplicatePrivateData() break; } #endif // MOZ_SVG +#ifdef MOZ_SMIL + case NS_SMIL_TIME_EVENT: + { + newEvent = new nsUIEvent(PR_FALSE, msg, 0); + NS_ENSURE_TRUE(newEvent, NS_ERROR_OUT_OF_MEMORY); + newEvent->eventStructType = NS_SMIL_TIME_EVENT; + break; + } +#endif // MOZ_SMIL case NS_SIMPLE_GESTURE_EVENT: { nsSimpleGestureEvent* oldSimpleGestureEvent = static_cast(mEvent); @@ -1225,6 +1237,14 @@ const char* nsDOMEvent::GetEventName(PRUint32 aEventType) case NS_SVG_ZOOM: return sEventNames[eDOMEvents_SVGZoom]; #endif // MOZ_SVG +#ifdef MOZ_SMIL + case NS_SMIL_BEGIN: + return sEventNames[eDOMEvents_beginEvent]; + case NS_SMIL_END: + return sEventNames[eDOMEvents_endEvent]; + case NS_SMIL_REPEAT: + return sEventNames[eDOMEvents_repeatEvent]; +#endif // MOZ_SMIL #ifdef MOZ_MEDIA case NS_LOADSTART: return sEventNames[eDOMEvents_loadstart]; diff --git a/content/events/src/nsDOMEvent.h b/content/events/src/nsDOMEvent.h index 8abe6f4acebc..49d0e55df8c7 100644 --- a/content/events/src/nsDOMEvent.h +++ b/content/events/src/nsDOMEvent.h @@ -142,6 +142,11 @@ public: eDOMEvents_SVGScroll, eDOMEvents_SVGZoom, #endif // MOZ_SVG +#ifdef MOZ_SMIL + eDOMEvents_beginEvent, + eDOMEvents_endEvent, + eDOMEvents_repeatEvent, +#endif // MOZ_SMIL #ifdef MOZ_MEDIA eDOMEvents_loadstart, eDOMEvents_progress, diff --git a/content/events/src/nsEventDispatcher.cpp b/content/events/src/nsEventDispatcher.cpp index 9f36470d1f9c..92f7406bd49e 100644 --- a/content/events/src/nsEventDispatcher.cpp +++ b/content/events/src/nsEventDispatcher.cpp @@ -742,6 +742,10 @@ nsEventDispatcher::CreateEvent(nsPresContext* aPresContext, return NS_NewDOMSVGZoomEvent(aDOMEvent, aPresContext, static_cast(aEvent)); #endif // MOZ_SVG +#ifdef MOZ_SMIL + case NS_SMIL_TIME_EVENT: + return NS_NewDOMTimeEvent(aDOMEvent, aPresContext, aEvent); +#endif // MOZ_SMIL case NS_COMMAND_EVENT: return NS_NewDOMCommandEvent(aDOMEvent, aPresContext, @@ -797,6 +801,11 @@ nsEventDispatcher::CreateEvent(nsPresContext* aPresContext, aEventType.LowerCaseEqualsLiteral("svgzoomevents")) return NS_NewDOMSVGZoomEvent(aDOMEvent, aPresContext, nsnull); #endif // MOZ_SVG +#ifdef MOZ_SMIL + if (aEventType.LowerCaseEqualsLiteral("timeevent") || + aEventType.LowerCaseEqualsLiteral("timeevents")) + return NS_NewDOMTimeEvent(aDOMEvent, aPresContext, nsnull); +#endif // MOZ_SMIL if (aEventType.LowerCaseEqualsLiteral("xulcommandevent") || aEventType.LowerCaseEqualsLiteral("xulcommandevents")) return NS_NewDOMXULCommandEvent(aDOMEvent, aPresContext, nsnull); diff --git a/content/events/src/nsEventListenerManager.cpp b/content/events/src/nsEventListenerManager.cpp index c6363a388a95..34784c0637fd 100644 --- a/content/events/src/nsEventListenerManager.cpp +++ b/content/events/src/nsEventListenerManager.cpp @@ -983,6 +983,14 @@ nsEventListenerManager::CompileEventHandlerInternal(nsIScriptContext *aContext, else if (aName == nsGkAtoms::onSVGZoom) attrName = nsGkAtoms::onzoom; #endif // MOZ_SVG +#ifdef MOZ_SMIL + else if (aName == nsGkAtoms::onbeginEvent) + attrName = nsGkAtoms::onbegin; + else if (aName == nsGkAtoms::onrepeatEvent) + attrName = nsGkAtoms::onrepeat; + else if (aName == nsGkAtoms::onendEvent) + attrName = nsGkAtoms::onend; +#endif // MOZ_SMIL content->GetAttr(kNameSpaceID_None, attrName, handlerBody); diff --git a/content/smil/Makefile.in b/content/smil/Makefile.in index 5b76a9dc9c31..34039f0d6355 100644 --- a/content/smil/Makefile.in +++ b/content/smil/Makefile.in @@ -54,6 +54,7 @@ EXPORTS = nsSMILKeySpline.h ifdef MOZ_SMIL CPPSRCS += \ + nsDOMTimeEvent.cpp \ nsSMILAnimationController.cpp \ nsSMILAnimationFunction.cpp \ nsSMILCompositor.cpp \ @@ -105,6 +106,7 @@ EXPORTS += \ INCLUDES += \ -I$(srcdir)/../base/src \ -I$(srcdir)/../../layout/style \ + -I$(srcdir)/../events/src \ $(NULL) endif # MOZ_SMIL diff --git a/content/smil/nsDOMTimeEvent.cpp b/content/smil/nsDOMTimeEvent.cpp new file mode 100644 index 000000000000..100a385b31c3 --- /dev/null +++ b/content/smil/nsDOMTimeEvent.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 Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Brian Birtles + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsDOMTimeEvent.h" +#include "nsGUIEvent.h" +#include "nsPresContext.h" +#include "nsIInterfaceRequestorUtils.h" +#include "nsIDOMAbstractView.h" + +nsDOMTimeEvent::nsDOMTimeEvent(nsPresContext* aPresContext, nsEvent* aEvent) + : nsDOMEvent(aPresContext, aEvent ? aEvent : new nsUIEvent(PR_FALSE, 0, 0)), + mDetail(0) +{ + if (aEvent) { + mEventIsInternal = PR_FALSE; + } else { + mEventIsInternal = PR_TRUE; + mEvent->eventStructType = NS_SMIL_TIME_EVENT; + } + + if (mEvent->eventStructType == NS_SMIL_TIME_EVENT) { + nsUIEvent* event = static_cast(mEvent); + mDetail = event->detail; + } + + mEvent->flags |= NS_EVENT_FLAG_CANT_BUBBLE | + NS_EVENT_FLAG_CANT_CANCEL; + + if (mPresContext) { + nsCOMPtr container = mPresContext->GetContainer(); + if (container) { + nsCOMPtr window = do_GetInterface(container); + if (window) { + mView = do_QueryInterface(window); + } + } + } +} + +NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMTimeEvent) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDOMTimeEvent, nsDOMEvent) + NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mView) +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDOMTimeEvent, nsDOMEvent) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mView) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_ADDREF_INHERITED(nsDOMTimeEvent, nsDOMEvent) +NS_IMPL_RELEASE_INHERITED(nsDOMTimeEvent, nsDOMEvent) + +DOMCI_DATA(TimeEvent, nsDOMTimeEvent) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMTimeEvent) + NS_INTERFACE_MAP_ENTRY(nsIDOMTimeEvent) + NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(TimeEvent) +NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent) + +NS_IMETHODIMP +nsDOMTimeEvent::GetView(nsIDOMAbstractView** aView) +{ + *aView = mView; + NS_IF_ADDREF(*aView); + return NS_OK; +} + +NS_IMETHODIMP +nsDOMTimeEvent::GetDetail(PRInt32* aDetail) +{ + *aDetail = mDetail; + return NS_OK; +} + +NS_IMETHODIMP +nsDOMTimeEvent::InitTimeEvent(const nsAString& aTypeArg, + nsIDOMAbstractView* aViewArg, + PRInt32 aDetailArg) +{ + nsresult rv = nsDOMEvent::InitEvent(aTypeArg, PR_FALSE /*doesn't bubble*/, + PR_FALSE /*can't cancel*/); + NS_ENSURE_SUCCESS(rv, rv); + + mDetail = aDetailArg; + mView = aViewArg; + + return NS_OK; +} + +nsresult NS_NewDOMTimeEvent(nsIDOMEvent** aInstancePtrResult, + nsPresContext* aPresContext, + nsEvent* aEvent) +{ + nsDOMTimeEvent* it = new nsDOMTimeEvent(aPresContext, aEvent); + return CallQueryInterface(it, aInstancePtrResult); +} diff --git a/content/smil/nsDOMTimeEvent.h b/content/smil/nsDOMTimeEvent.h new file mode 100644 index 000000000000..632b8ddc19a9 --- /dev/null +++ b/content/smil/nsDOMTimeEvent.h @@ -0,0 +1,65 @@ +/* -*- 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 Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Brian Birtles + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef NS_DOMTIMEEVENT_H_ +#define NS_DOMTIMEEVENT_H_ + +#include "nsIDOMTimeEvent.h" +#include "nsDOMEvent.h" + +class nsDOMTimeEvent : public nsDOMEvent, + public nsIDOMTimeEvent +{ +public: + nsDOMTimeEvent(nsPresContext* aPresContext, nsEvent* aEvent); + + // nsISupports interface: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsDOMTimeEvent, nsDOMEvent) + + // nsIDOMTimeEvent interface: + NS_DECL_NSIDOMTIMEEVENT + + // Forward to base class + NS_FORWARD_TO_NSDOMEVENT + +private: + nsCOMPtr mView; + PRInt32 mDetail; +}; + +#endif // NS_DOMTIMEEVENT_H_ diff --git a/content/smil/nsSMILTimedElement.cpp b/content/smil/nsSMILTimedElement.cpp index b620cd82350c..712af1000631 100644 --- a/content/smil/nsSMILTimedElement.cpp +++ b/content/smil/nsSMILTimedElement.cpp @@ -43,15 +43,19 @@ #include "nsSMILParserUtils.h" #include "nsSMILTimeContainer.h" #include "nsGkAtoms.h" +#include "nsGUIEvent.h" +#include "nsEventDispatcher.h" #include "nsReadableUtils.h" #include "nsMathUtils.h" +#include "nsThreadUtils.h" +#include "nsIPresShell.h" #include "prdtoa.h" #include "plstr.h" #include "prtime.h" #include "nsString.h" //---------------------------------------------------------------------- -// Helper classes -- InstanceTimeComparator +// Helper class: InstanceTimeComparator // Upon inserting an instance time into one of our instance time lists we assign // it a serial number. This allows us to sort the instance times in such a way @@ -90,6 +94,43 @@ nsSMILTimedElement::InstanceTimeComparator::LessThan( return cmp == 0 ? aElem1->Serial() < aElem2->Serial() : cmp < 0; } +//---------------------------------------------------------------------- +// Helper class: AsyncTimeEventRunner + +namespace +{ + class AsyncTimeEventRunner : public nsRunnable + { + protected: + nsRefPtr mTarget; + PRUint32 mMsg; + PRInt32 mDetail; + + public: + AsyncTimeEventRunner(nsIContent* aTarget, PRUint32 aMsg, PRInt32 aDetail) + : mTarget(aTarget), mMsg(aMsg), mDetail(aDetail) + { + } + + NS_IMETHOD Run() + { + nsUIEvent event(PR_TRUE, mMsg, mDetail); + event.eventStructType = NS_SMIL_TIME_EVENT; + + nsPresContext* context = nsnull; + nsIDocument* doc = mTarget->GetCurrentDoc(); + if (doc) { + nsCOMPtr shell = doc->GetShell(); + if (shell) { + context = shell->GetPresContext(); + } + } + + return nsEventDispatcher::Dispatch(mTarget, context, &event); + } + }; +} + //---------------------------------------------------------------------- // Templated helper functions @@ -150,6 +191,7 @@ nsSMILTimedElement::nsSMILTimedElement() mInstanceSerialIndex(0), mClient(nsnull), mCurrentInterval(nsnull), + mCurrentRepeatIteration(0), mPrevRegisteredMilestone(sMaxMilestone), mElementState(STATE_STARTUP), mSeekState(SEEK_NOT_SEEKING) @@ -506,20 +548,21 @@ nsSMILTimedElement::DoSampleAt(nsSMILTime aContainerTime, PRBool aEndOnly) if (mCurrentInterval->Begin()->Time() <= sampleTime) { mElementState = STATE_ACTIVE; mCurrentInterval->FixBegin(); - if (HasPlayed()) { - Reset(); // Apply restart behaviour - } if (mClient) { mClient->Activate(mCurrentInterval->Begin()->Time().GetMillis()); } + if (mSeekState == SEEK_NOT_SEEKING) { + FireTimeEventAsync(NS_SMIL_BEGIN, 0); + } if (HasPlayed()) { + Reset(); // Apply restart behaviour // The call to Reset() may mean that the end point of our current // interval should be changed and so we should update the interval // now. However, calling UpdateCurrentInterval could result in the // interval getting deleted (perhaps through some web of syncbase // dependencies) therefore we make updating the interval the last - // thing we do. There is no guarantee that mCurrentInterval.IsSet() - // is true after this. + // thing we do. There is no guarantee that mCurrentInterval is set + // after this. UpdateCurrentInterval(); } stateChanged = PR_TRUE; @@ -541,6 +584,10 @@ nsSMILTimedElement::DoSampleAt(nsSMILTime aContainerTime, PRBool aEndOnly) mClient->Inactivate(mFillMode == FILL_FREEZE); } mCurrentInterval->FixEnd(); + if (mSeekState == SEEK_NOT_SEEKING) { + FireTimeEventAsync(NS_SMIL_END, 0); + } + mCurrentRepeatIteration = 0; mOldIntervals.AppendElement(mCurrentInterval.forget()); // We must update mOldIntervals before calling SampleFillValue SampleFillValue(); @@ -556,6 +603,19 @@ nsSMILTimedElement::DoSampleAt(nsSMILTime aContainerTime, PRBool aEndOnly) "Sample time should not precede current interval"); nsSMILTime activeTime = aContainerTime - beginTime; SampleSimpleTime(activeTime); + // We register our repeat times as milestones (except when we're + // seeking) so we should get a sample at exactly the time we repeat. + // (And even when we are seeking we want to update + // mCurrentRepeatIteration so we do that first before testing the seek + // state.) + PRUint32 prevRepeatIteration = mCurrentRepeatIteration; + if (ActiveTimeToSimpleTime(activeTime, mCurrentRepeatIteration)==0 && + mCurrentRepeatIteration != prevRepeatIteration && + mCurrentRepeatIteration && + mSeekState == SEEK_NOT_SEEKING) { + FireTimeEventAsync(NS_SMIL_REPEAT, + static_cast(mCurrentRepeatIteration)); + } } } break; @@ -607,6 +667,7 @@ nsSMILTimedElement::Rewind() // Set the STARTUP state first so that if we get any callbacks we won't waste // time recalculating the current interval mElementState = STATE_STARTUP; + mCurrentRepeatIteration = 0; // Clear the intervals and instance times except those instance times we can't // regenerate (DOM calls etc.) @@ -1238,13 +1299,6 @@ nsSMILTimedElement::Reset() void nsSMILTimedElement::DoPostSeek() { - // XXX When implementing TimeEvents we'll need to compare mElementState with - // mSeekState and dispatch events as follows: - // ACTIVE->INACTIVE: End event - // INACTIVE->ACTIVE: Begin event - // ACTIVE->ACTIVE: Nothing (even if they're different intervals) - // INACTIVE->INACTIVE: Nothing (even if we've skipped intervals) - // Finish backwards seek if (mSeekState == SEEK_BACKWARD_FROM_INACTIVE || mSeekState == SEEK_BACKWARD_FROM_ACTIVE) { @@ -1266,6 +1320,34 @@ nsSMILTimedElement::DoPostSeek() UpdateCurrentInterval(); } + // XXX + // Note that SMIL gives the very cryptic description: + // The associated time for the event is the document time before the seek. + // This action does not resolve any times in the instance times list for end + // times. + // + // The second sentence was added as a clarification in a SMIL 2.0 erratum. + // Presumably the intention is that we fire the event as implemented below but + // don't act on it. This makes sense at least for dependencies within the same + // time container. So we'll probably need to set a flag here to ensure we + // don't actually act on it when we implement event-based timing. + switch (mSeekState) + { + case SEEK_FORWARD_FROM_ACTIVE: + case SEEK_BACKWARD_FROM_ACTIVE: + if (mElementState != STATE_ACTIVE) { + FireTimeEventAsync(NS_SMIL_END, 0); + } + break; + + case SEEK_FORWARD_FROM_INACTIVE: + case SEEK_BACKWARD_FROM_INACTIVE: + if (mElementState == STATE_ACTIVE) { + FireTimeEventAsync(NS_SMIL_BEGIN, 0); + } + break; + } + mSeekState = SEEK_NOT_SEEKING; } @@ -1861,10 +1943,6 @@ nsSMILTimedElement::GetNextMilestone(nsSMILMilestone& aNextMilestone) const { // Return the next key moment in our lifetime. // - // XXX Once we implement TimeEvents and event based timing we might need to - // include repeat times too, particularly if it's important to get them in - // order. - // // XXX It may be possible in future to optimise this so that we only register // for milestones if: // a) We have time dependents, or @@ -1896,22 +1974,27 @@ nsSMILTimedElement::GetNextMilestone(nsSMILMilestone& aNextMilestone) const case STATE_ACTIVE: { - // XXX When we implement TimeEvents, we may need to consider what comes - // next: the interval end or an interval repeat. + // Work out what comes next: the interval end or the next repeat iteration + nsSMILTimeValue nextRepeat; + if (mSeekState == SEEK_NOT_SEEKING && mSimpleDur.IsResolved()) { + nextRepeat.SetMillis(mCurrentInterval->Begin()->Time().GetMillis() + + (mCurrentRepeatIteration + 1) * mSimpleDur.GetMillis()); + } + nsSMILTimeValue nextMilestone = + NS_MIN(mCurrentInterval->End()->Time(), nextRepeat); - // Check for an early end - nsSMILInstanceTime* earlyEnd = - CheckForEarlyEnd(mCurrentInterval->End()->Time()); + // Check for an early end before that time + nsSMILInstanceTime* earlyEnd = CheckForEarlyEnd(nextMilestone); if (earlyEnd) { aNextMilestone.mIsEnd = PR_TRUE; aNextMilestone.mTime = earlyEnd->Time().GetMillis(); return PR_TRUE; } - // Otherwise it's just the next interval end - if (mCurrentInterval->End()->Time().IsResolved()) { - aNextMilestone.mIsEnd = PR_TRUE; - aNextMilestone.mTime = mCurrentInterval->End()->Time().GetMillis(); + // Apply the previously calculated milestone + if (nextMilestone.IsResolved()) { + aNextMilestone.mIsEnd = nextMilestone != nextRepeat; + aNextMilestone.mTime = nextMilestone.GetMillis(); return PR_TRUE; } @@ -1958,6 +2041,17 @@ nsSMILTimedElement::NotifyChangedInterval() mCurrentInterval->NotifyChanged(container); } +void +nsSMILTimedElement::FireTimeEventAsync(PRUint32 aMsg, PRInt32 aDetail) +{ + if (!mAnimationElement) + return; + + nsCOMPtr event = + new AsyncTimeEventRunner(&mAnimationElement->Content(), aMsg, aDetail); + NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); +} + const nsSMILInstanceTime* nsSMILTimedElement::GetEffectiveBeginInstance() const { diff --git a/content/smil/nsSMILTimedElement.h b/content/smil/nsSMILTimedElement.h index b3ca341a9000..c410215f61af 100644 --- a/content/smil/nsSMILTimedElement.h +++ b/content/smil/nsSMILTimedElement.h @@ -482,6 +482,7 @@ protected: void NotifyNewInterval(); void NotifyChangedInterval(); + void FireTimeEventAsync(PRUint32 aMsg, PRInt32 aDetail); const nsSMILInstanceTime* GetEffectiveBeginInstance() const; const nsSMILInterval* GetPreviousInterval() const; PRBool HasPlayed() const { return !mOldIntervals.IsEmpty(); } @@ -539,6 +540,7 @@ protected: nsSMILAnimationFunction* mClient; nsAutoPtr mCurrentInterval; IntervalList mOldIntervals; + PRUint32 mCurrentRepeatIteration; nsSMILMilestone mPrevRegisteredMilestone; static const nsSMILMilestone sMaxMilestone; static const PRUint8 sMaxNumIntervals; diff --git a/content/smil/test/Makefile.in b/content/smil/test/Makefile.in index 655a73834086..77e4640a4cf4 100644 --- a/content/smil/test/Makefile.in +++ b/content/smil/test/Makefile.in @@ -82,6 +82,7 @@ _TEST_FILES = \ test_smilSyncbaseTarget.xhtml \ test_smilSyncTransform.xhtml \ test_smilTextZoom.xhtml \ + test_smilTimeEvents.xhtml \ test_smilTiming.xhtml \ test_smilTimingZeroIntervals.xhtml \ test_smilUpdatedInterval.xhtml \ diff --git a/content/smil/test/test_smilTimeEvents.xhtml b/content/smil/test/test_smilTimeEvents.xhtml new file mode 100644 index 000000000000..fa426b4bba8c --- /dev/null +++ b/content/smil/test/test_smilTimeEvents.xhtml @@ -0,0 +1,292 @@ + + + + Test TimeEvents dispatching + + + + + +Mozilla Bug + 572270 +

+ +
+
+
+ + diff --git a/content/svg/content/src/nsSVGAnimationElement.cpp b/content/svg/content/src/nsSVGAnimationElement.cpp index 8fded880517f..e4e0fdd7899a 100644 --- a/content/svg/content/src/nsSVGAnimationElement.cpp +++ b/content/svg/content/src/nsSVGAnimationElement.cpp @@ -476,6 +476,12 @@ nsSVGAnimationElement::EndElementAt(float offset) return NS_OK; } +PRBool +nsSVGAnimationElement::IsEventName(nsIAtom* aName) +{ + return nsContentUtils::IsEventAttributeName(aName, EventNameType_SMIL); +} + void nsSVGAnimationElement::UpdateHrefTarget(nsIContent* aNodeForContext, const nsAString& aHrefStr) diff --git a/content/svg/content/src/nsSVGAnimationElement.h b/content/svg/content/src/nsSVGAnimationElement.h index afef2da35c59..8d8e0cf2a917 100644 --- a/content/svg/content/src/nsSVGAnimationElement.h +++ b/content/svg/content/src/nsSVGAnimationElement.h @@ -98,6 +98,9 @@ public: virtual nsSMILTimeContainer* GetTimeContainer(); protected: + // nsSVGElement overrides + PRBool IsEventName(nsIAtom* aName); + void UpdateHrefTarget(nsIContent* aNodeForContext, const nsAString& aHrefStr); diff --git a/content/svg/content/src/nsSVGElement.cpp b/content/svg/content/src/nsSVGElement.cpp index 57d891abe03d..b27c7f9c92a6 100644 --- a/content/svg/content/src/nsSVGElement.cpp +++ b/content/svg/content/src/nsSVGElement.cpp @@ -1364,6 +1364,14 @@ nsIAtom* nsSVGElement::GetEventNameForAttr(nsIAtom* aAttr) return nsGkAtoms::onSVGScroll; if (aAttr == nsGkAtoms::onzoom) return nsGkAtoms::onSVGZoom; +#ifdef MOZ_SMIL + if (aAttr == nsGkAtoms::onbegin) + return nsGkAtoms::onbeginEvent; + if (aAttr == nsGkAtoms::onrepeat) + return nsGkAtoms::onrepeatEvent; + if (aAttr == nsGkAtoms::onend) + return nsGkAtoms::onendEvent; +#endif // MOZ_SMIL return aAttr; } diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index 0523a4328bff..6ee87f1f42d0 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -377,6 +377,7 @@ #include "nsIDOMSVGSetElement.h" #include "nsIDOMSVGAnimationElement.h" #include "nsIDOMElementTimeControl.h" +#include "nsIDOMTimeEvent.h" #endif // MOZ_SMIL #include "nsIDOMSVGAnimTransformList.h" #include "nsIDOMSVGCircleElement.h" @@ -1003,6 +1004,8 @@ static nsDOMClassInfoData sClassInfoData[] = { ELEMENT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(SVGSetElement, nsElementSH, ELEMENT_SCRIPTABLE_FLAGS) + NS_DEFINE_CLASSINFO_DATA(TimeEvent, nsDOMGenericSH, + DOM_DEFAULT_SCRIPTABLE_FLAGS) #endif // MOZ_SMIL NS_DEFINE_CLASSINFO_DATA(SVGCircleElement, nsElementSH, ELEMENT_SCRIPTABLE_FLAGS) @@ -3041,6 +3044,10 @@ nsDOMClassInfo::Init() DOM_CLASSINFO_SVG_ELEMENT_MAP_ENTRIES DOM_CLASSINFO_MAP_END + DOM_CLASSINFO_MAP_BEGIN(TimeEvent, nsIDOMTimeEvent) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMTimeEvent) + DOM_CLASSINFO_EVENT_MAP_ENTRIES + DOM_CLASSINFO_MAP_END #endif // MOZ_SMIL DOM_CLASSINFO_MAP_BEGIN(SVGCircleElement, nsIDOMSVGCircleElement) diff --git a/dom/base/nsDOMClassInfoClasses.h b/dom/base/nsDOMClassInfoClasses.h index d7fcbc7aaae3..e49d0d89422c 100644 --- a/dom/base/nsDOMClassInfoClasses.h +++ b/dom/base/nsDOMClassInfoClasses.h @@ -240,6 +240,7 @@ DOMCI_CLASS(SVGAnimateTransformElement) DOMCI_CLASS(SVGAnimateMotionElement) DOMCI_CLASS(SVGMpathElement) DOMCI_CLASS(SVGSetElement) +DOMCI_CLASS(TimeEvent) #endif // MOZ_SMIL DOMCI_CLASS(SVGCircleElement) DOMCI_CLASS(SVGClipPathElement) diff --git a/dom/interfaces/smil/Makefile.in b/dom/interfaces/smil/Makefile.in index 75216d109d2f..0257ff3fade4 100644 --- a/dom/interfaces/smil/Makefile.in +++ b/dom/interfaces/smil/Makefile.in @@ -48,6 +48,7 @@ XPIDL_MODULE = dom_smil XPIDLSRCS = \ nsIDOMElementTimeControl.idl \ + nsIDOMTimeEvent.idl \ $(NULL) include $(topsrcdir)/config/rules.mk diff --git a/dom/interfaces/smil/nsIDOMTimeEvent.idl b/dom/interfaces/smil/nsIDOMTimeEvent.idl new file mode 100644 index 000000000000..af1e5bc5ea09 --- /dev/null +++ b/dom/interfaces/smil/nsIDOMTimeEvent.idl @@ -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 Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Brian Birtles + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsIDOMEvent.idl" + +/** + * The SMIL TimeEvent interface. + * + * For more information please refer to: + * http://www.w3.org/TR/SMIL/smil-timing.html#Events-TimeEvent + * http://www.w3.org/TR/SVG/animate.html#InterfaceTimeEvent + */ + +[scriptable, uuid(0d309c26-ddbb-44cb-9af1-3008972349e3)] +interface nsIDOMTimeEvent : nsIDOMEvent +{ + readonly attribute long detail; + readonly attribute nsIDOMAbstractView view; + + void initTimeEvent(in DOMString typeArg, + in nsIDOMAbstractView viewArg, + in long detailArg); +}; diff --git a/widget/public/nsGUIEvent.h b/widget/public/nsGUIEvent.h index c16b9e96bee1..b50f2a49e615 100644 --- a/widget/public/nsGUIEvent.h +++ b/widget/public/nsGUIEvent.h @@ -101,6 +101,9 @@ class nsHashKey; #define NS_SVG_EVENT 30 #define NS_SVGZOOM_EVENT 31 #endif // MOZ_SVG +#ifdef MOZ_SMIL +#define NS_SMIL_TIME_EVENT 32 +#endif // MOZ_SMIL #define NS_QUERY_CONTENT_EVENT 33 @@ -460,6 +463,13 @@ class nsHashKey; #define NS_TRANSITION_EVENT_START 4200 #define NS_TRANSITION_END (NS_TRANSITION_EVENT_START) +#ifdef MOZ_SMIL +#define NS_SMIL_TIME_EVENT_START 4300 +#define NS_SMIL_BEGIN (NS_SMIL_TIME_EVENT_START) +#define NS_SMIL_END (NS_SMIL_TIME_EVENT_START + 1) +#define NS_SMIL_REPEAT (NS_SMIL_TIME_EVENT_START + 2) +#endif // MOZ_SMIL + /** * Return status for event processors, nsEventStatus, is defined in * nsEvent.h.