author | Brian Birtles <birtles@gmail.com> |
Sat, 31 Jul 2010 16:02:52 +0900 | |
changeset 48450 | f73e5032cfadb8b8d575b8d1d40814700e038530 |
parent 48449 | 99146dea1c85e0c73094025d5fbb3659fe9c3879 |
child 48451 | ee695e20ee8b4908f21dcf705904f9afa7df8116 |
push id | 14740 |
push user | bbirtles@mozilla.com |
push date | Sat, 31 Jul 2010 07:22:55 +0000 |
treeherder | mozilla-central@f73e5032cfad [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | dholbert, smaug, roc, blocking-betaN |
bugs | 527270 |
milestone | 2.0b3pre |
first release with | nightly linux32
f73e5032cfad
/
4.0b3pre
/
20100731030927
/
files
nightly linux64
f73e5032cfad
/
4.0b3pre
/
20100731030739
/
files
nightly mac
f73e5032cfad
/
4.0b3pre
/
20100731030843
/
files
nightly win32
f73e5032cfad
/
4.0b3pre
/
20100731040142
/
files
nightly win64
f73e5032cfad
/
4.0b3pre
/
20100731040539
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
4.0b3pre
/
20100731030927
/
pushlog to previous
nightly linux64
4.0b3pre
/
20100731030739
/
pushlog to previous
nightly mac
4.0b3pre
/
20100731030843
/
pushlog to previous
nightly win32
4.0b3pre
/
20100731040142
/
pushlog to previous
nightly win64
4.0b3pre
/
20100731040539
/
pushlog to previous
|
--- a/content/base/public/nsContentUtils.h +++ b/content/base/public/nsContentUtils.h @@ -152,16 +152,17 @@ class Element; extern const char kLoadAsData[]; enum EventNameType { EventNameType_None = 0x0000, EventNameType_HTML = 0x0001, 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 }; struct EventNameMapping { nsIAtom* mAtom;
--- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -570,16 +570,24 @@ nsContentUtils::InitializeEventTable() { { nsGkAtoms::onSVGResize, NS_SVG_RESIZE, EventNameType_None, NS_SVG_EVENT }, { nsGkAtoms::onSVGScroll, NS_SVG_SCROLL, EventNameType_None, NS_SVG_EVENT }, { nsGkAtoms::onSVGZoom, NS_SVG_ZOOM, EventNameType_None, NS_SVGZOOM_EVENT }, // 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 }, { nsGkAtoms::onsuspend, NS_SUSPEND, EventNameType_HTML, NS_EVENT_NULL }, { nsGkAtoms::onemptied, NS_EMPTIED, EventNameType_HTML, NS_EVENT_NULL }, { nsGkAtoms::onstalled, NS_STALLED, EventNameType_HTML, NS_EVENT_NULL }, { nsGkAtoms::onplay, NS_PLAY, EventNameType_HTML, NS_EVENT_NULL }, { nsGkAtoms::onpause, NS_PAUSE, EventNameType_HTML, NS_EVENT_NULL },
--- a/content/base/src/nsGkAtomList.h +++ b/content/base/src/nsGkAtomList.h @@ -1304,16 +1304,22 @@ GK_ATOM(begin, "begin") GK_ATOM(by, "by") GK_ATOM(calcMode, "calcMode") GK_ATOM(css, "CSS") GK_ATOM(dur, "dur") 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") GK_ATOM(to, "to") GK_ATOM(XML, "XML") #endif #ifdef MOZ_MATHML
--- a/content/events/public/nsIPrivateDOMEvent.h +++ b/content/events/public/nsIPrivateDOMEvent.h @@ -98,16 +98,20 @@ NS_NewDOMBeforeUnloadEvent(nsIDOMEvent** nsresult NS_NewDOMPageTransitionEvent(nsIDOMEvent** aResult, nsPresContext* aPresContext, nsEvent* aEvent); #ifdef MOZ_SVG nsresult NS_NewDOMSVGEvent(nsIDOMEvent** aResult, nsPresContext* aPresContext, class nsEvent* aEvent); 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 NS_NewDOMCommandEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, nsCommandEvent* aEvent); nsresult NS_NewDOMMessageEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, class nsEvent* aEvent); nsresult NS_NewDOMProgressEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, class nsEvent* aEvent);
--- a/content/events/src/nsDOMEvent.cpp +++ b/content/events/src/nsDOMEvent.cpp @@ -76,16 +76,19 @@ static const char* const sEventNames[] = "DOMAttrModified", "DOMCharacterDataModified", "DOMActivate", "DOMFocusIn", "DOMFocusOut", "pageshow", "pagehide", "DOMMouseScroll", "MozMousePixelScroll", "offline", "online", "copy", "cut", "paste", #ifdef MOZ_SVG "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", "canplaythrough", "seeking", "seeked", "timeupdate", "ended", "ratechange", "durationchange", "volumechange", #endif // MOZ_MEDIA "MozAfterPaint", "MozSwipeGesture", @@ -768,16 +771,25 @@ NS_METHOD nsDOMEvent::DuplicatePrivateDa case NS_SVGZOOM_EVENT: { newEvent = new nsGUIEvent(PR_FALSE, msg, nsnull); NS_ENSURE_TRUE(newEvent, NS_ERROR_OUT_OF_MEMORY); newEvent->eventStructType = NS_SVGZOOM_EVENT; 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<nsSimpleGestureEvent*>(mEvent); nsSimpleGestureEvent* simpleGestureEvent = new nsSimpleGestureEvent(PR_FALSE, msg, nsnull, 0, 0.0); NS_ENSURE_TRUE(simpleGestureEvent, NS_ERROR_OUT_OF_MEMORY); isInputEvent = PR_TRUE; simpleGestureEvent->direction = oldSimpleGestureEvent->direction; @@ -1220,16 +1232,24 @@ const char* nsDOMEvent::GetEventName(PRU return sEventNames[eDOMEvents_SVGError]; case NS_SVG_RESIZE: return sEventNames[eDOMEvents_SVGResize]; case NS_SVG_SCROLL: return sEventNames[eDOMEvents_SVGScroll]; 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]; case NS_PROGRESS: return sEventNames[eDOMEvents_progress]; case NS_SUSPEND: return sEventNames[eDOMEvents_suspend]; case NS_EMPTIED:
--- a/content/events/src/nsDOMEvent.h +++ b/content/events/src/nsDOMEvent.h @@ -137,16 +137,21 @@ public: eDOMEvents_SVGLoad, eDOMEvents_SVGUnload, eDOMEvents_SVGAbort, eDOMEvents_SVGError, eDOMEvents_SVGResize, 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, eDOMEvents_suspend, eDOMEvents_emptied, eDOMEvents_stalled, eDOMEvents_play, eDOMEvents_pause,
--- a/content/events/src/nsEventDispatcher.cpp +++ b/content/events/src/nsEventDispatcher.cpp @@ -737,16 +737,20 @@ nsEventDispatcher::CreateEvent(nsPresCon #ifdef MOZ_SVG case NS_SVG_EVENT: return NS_NewDOMSVGEvent(aDOMEvent, aPresContext, aEvent); case NS_SVGZOOM_EVENT: return NS_NewDOMSVGZoomEvent(aDOMEvent, aPresContext, static_cast<nsGUIEvent*>(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, static_cast<nsCommandEvent*>(aEvent)); case NS_SIMPLE_GESTURE_EVENT: return NS_NewDOMSimpleGestureEvent(aDOMEvent, aPresContext, static_cast<nsSimpleGestureEvent*>(aEvent)); case NS_TRANSITION_EVENT: @@ -792,16 +796,21 @@ nsEventDispatcher::CreateEvent(nsPresCon #ifdef MOZ_SVG if (aEventType.LowerCaseEqualsLiteral("svgevent") || aEventType.LowerCaseEqualsLiteral("svgevents")) return NS_NewDOMSVGEvent(aDOMEvent, aPresContext, nsnull); if (aEventType.LowerCaseEqualsLiteral("svgzoomevent") || 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); if (aEventType.LowerCaseEqualsLiteral("commandevent") || aEventType.LowerCaseEqualsLiteral("commandevents")) return NS_NewDOMCommandEvent(aDOMEvent, aPresContext, nsnull); if (aEventType.LowerCaseEqualsLiteral("datacontainerevent") || aEventType.LowerCaseEqualsLiteral("datacontainerevents"))
--- a/content/events/src/nsEventListenerManager.cpp +++ b/content/events/src/nsEventListenerManager.cpp @@ -978,16 +978,24 @@ nsEventListenerManager::CompileEventHand attrName = nsGkAtoms::onerror; else if (aName == nsGkAtoms::onSVGResize) attrName = nsGkAtoms::onresize; else if (aName == nsGkAtoms::onSVGScroll) attrName = nsGkAtoms::onscroll; 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); PRUint32 lineNo = 0; nsCAutoString url (NS_LITERAL_CSTRING("javascript:alert('TODO: FIXME')")); nsIDocument* doc = nsnull; nsCOMPtr<nsINode> node = do_QueryInterface(aCurrentTarget); if (node) {
--- a/content/smil/Makefile.in +++ b/content/smil/Makefile.in @@ -49,16 +49,17 @@ LIBXUL_LIBRARY = 1 # nsSMILKeySpline is used by CSS transitions -- need to build it regardless # of whether SMIL is enabled. CPPSRCS = nsSMILKeySpline.cpp EXPORTS = nsSMILKeySpline.h ifdef MOZ_SMIL CPPSRCS += \ + nsDOMTimeEvent.cpp \ nsSMILAnimationController.cpp \ nsSMILAnimationFunction.cpp \ nsSMILCompositor.cpp \ nsSMILCSSProperty.cpp \ nsSMILCSSValueType.cpp \ nsSMILFloatType.cpp \ nsSMILInstanceTime.cpp \ nsSMILInterval.cpp \ @@ -100,14 +101,15 @@ EXPORTS += \ nsSMILMilestone.h \ nsSMILTimeContainer.h \ nsSMILTypes.h \ $(NULL) INCLUDES += \ -I$(srcdir)/../base/src \ -I$(srcdir)/../../layout/style \ + -I$(srcdir)/../events/src \ $(NULL) endif # MOZ_SMIL include $(topsrcdir)/config/rules.mk DEFINES += -D_IMPL_NS_LAYOUT
new file mode 100644 --- /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 <birtles@gmail.com> + * + * 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<nsUIEvent*>(mEvent); + mDetail = event->detail; + } + + mEvent->flags |= NS_EVENT_FLAG_CANT_BUBBLE | + NS_EVENT_FLAG_CANT_CANCEL; + + if (mPresContext) { + nsCOMPtr<nsISupports> container = mPresContext->GetContainer(); + if (container) { + nsCOMPtr<nsIDOMWindowInternal> 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); +}
new file mode 100644 --- /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 <birtles@gmail.com> + * + * 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<nsIDOMAbstractView> mView; + PRInt32 mDetail; +}; + +#endif // NS_DOMTIMEEVENT_H_
--- a/content/smil/nsSMILTimedElement.cpp +++ b/content/smil/nsSMILTimedElement.cpp @@ -38,25 +38,29 @@ #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 "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 // that where we have several equal instance times, the ones added later will // sort later. This means that when we call UpdateCurrentInterval during the // waiting state we won't unnecessarily change the begin instance. // // The serial number also means that every instance time has an unambiguous @@ -86,16 +90,53 @@ nsSMILTimedElement::InstanceTimeComparat NS_ABORT_IF_FALSE(aElem1->Serial() && aElem2->Serial(), "Instance times have not been assigned serial numbers"); PRInt8 cmp = aElem1->Time().CompareTo(aElem2->Time()); return cmp == 0 ? aElem1->Serial() < aElem2->Serial() : cmp < 0; } //---------------------------------------------------------------------- +// Helper class: AsyncTimeEventRunner + +namespace +{ + class AsyncTimeEventRunner : public nsRunnable + { + protected: + nsRefPtr<nsIContent> 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<nsIPresShell> shell = doc->GetShell(); + if (shell) { + context = shell->GetPresContext(); + } + } + + return nsEventDispatcher::Dispatch(mTarget, context, &event); + } + }; +} + +//---------------------------------------------------------------------- // Templated helper functions // Selectively remove elements from an array of type // nsTArray<nsRefPtr<nsSMILInstanceTime> > with O(n) performance. template <class TestFunctor> void nsSMILTimedElement::RemoveInstanceTimes(InstanceTimeList& aArray, TestFunctor& aTest) @@ -145,16 +186,17 @@ nsSMILTimedElement::nsSMILTimedElement() mAnimationElement(nsnull), mFillMode(FILL_REMOVE), mRestartMode(RESTART_ALWAYS), mBeginSpecSet(PR_FALSE), mEndHasEventConditions(PR_FALSE), mInstanceSerialIndex(0), mClient(nsnull), mCurrentInterval(nsnull), + mCurrentRepeatIteration(0), mPrevRegisteredMilestone(sMaxMilestone), mElementState(STATE_STARTUP), mSeekState(SEEK_NOT_SEEKING) { mSimpleDur.SetIndefinite(); mMin.SetMillis(0L); mMax.SetIndefinite(); mTimeDependents.Init(); @@ -501,30 +543,31 @@ nsSMILTimedElement::DoSampleAt(nsSMILTim } break; case STATE_WAITING: { 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; } } break; case STATE_ACTIVE: @@ -536,31 +579,48 @@ nsSMILTimedElement::DoSampleAt(nsSMILTim mElementState = NS_SUCCEEDED(GetNextInterval(mCurrentInterval, nsnull, newInterval)) ? STATE_WAITING : STATE_POSTACTIVE; if (mClient) { 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(); if (mElementState == STATE_WAITING) { mCurrentInterval = new nsSMILInterval(newInterval); NotifyNewInterval(); } FilterHistory(); stateChanged = PR_TRUE; } else { nsSMILTime beginTime = mCurrentInterval->Begin()->Time().GetMillis(); NS_ASSERTION(aContainerTime >= beginTime, "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<PRInt32>(mCurrentRepeatIteration)); + } } } break; case STATE_POSTACTIVE: break; } @@ -602,16 +662,17 @@ nsSMILTimedElement::Rewind() mSeekState = mElementState == STATE_ACTIVE ? SEEK_BACKWARD_FROM_ACTIVE : SEEK_BACKWARD_FROM_INACTIVE; // 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.) RewindTiming(); UnsetBeginSpec(); UnsetEndSpec(); @@ -1233,23 +1294,16 @@ nsSMILTimedElement::Reset() RemoveReset resetEnd(nsnull); RemoveInstanceTimes(mEndInstances, resetEnd); } 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) { // Previously some dynamic instance times may have been marked to be // preserved because they were endpoints of an historic interval (which may // or may not have been filtered). Now that we've finished a seek we should // clear that flag for those instance times whose intervals are no longer // historic. @@ -1261,16 +1315,44 @@ nsSMILTimedElement::DoPostSeek() // SMIL seems to require this. SMIL 3.0, 'Hyperlinks and timing': // Resolved end times associated with events, Repeat-values, // Accesskey-values or added via DOM method calls are cleared when seeking // to time earlier than the resolved end time. Reset(); 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; } void nsSMILTimedElement::UnpreserveInstanceTimes(InstanceTimeList& aList) { const nsSMILInterval* prevInterval = GetPreviousInterval(); const nsSMILInstanceTime* cutoff = mCurrentInterval ? @@ -1856,20 +1938,16 @@ nsSMILTimedElement::RegisterMilestone() mPrevRegisteredMilestone = nextMilestone; } PRBool 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 // b) We are dependent on events or syncbase relationships, or // c) There are registered listeners for our events // // Then for the simple case where everything uses offset values we could // ignore milestones altogether. @@ -1891,32 +1969,37 @@ nsSMILTimedElement::GetNextMilestone(nsS NS_ABORT_IF_FALSE(mCurrentInterval, "In waiting state but the current interval has not been set"); aNextMilestone.mIsEnd = PR_FALSE; aNextMilestone.mTime = mCurrentInterval->Begin()->Time().GetMillis(); return PR_TRUE; 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; } return PR_FALSE; } case STATE_POSTACTIVE: return PR_FALSE; @@ -1953,16 +2036,27 @@ nsSMILTimedElement::NotifyChangedInterva nsSMILTimeContainer* container = GetTimeContainer(); if (container) { container->SyncPauseTime(); } mCurrentInterval->NotifyChanged(container); } +void +nsSMILTimedElement::FireTimeEventAsync(PRUint32 aMsg, PRInt32 aDetail) +{ + if (!mAnimationElement) + return; + + nsCOMPtr<nsIRunnable> event = + new AsyncTimeEventRunner(&mAnimationElement->Content(), aMsg, aDetail); + NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); +} + const nsSMILInstanceTime* nsSMILTimedElement::GetEffectiveBeginInstance() const { switch (mElementState) { case STATE_STARTUP: return nsnull;
--- a/content/smil/nsSMILTimedElement.h +++ b/content/smil/nsSMILTimedElement.h @@ -477,16 +477,17 @@ protected: void SampleFillValue(); void AddInstanceTimeFromCurrentTime(nsSMILTime aCurrentTime, double aOffsetSeconds, PRBool aIsBegin); void RegisterMilestone(); PRBool GetNextMilestone(nsSMILMilestone& aNextMilestone) const; void NotifyNewInterval(); void NotifyChangedInterval(); + void FireTimeEventAsync(PRUint32 aMsg, PRInt32 aDetail); const nsSMILInstanceTime* GetEffectiveBeginInstance() const; const nsSMILInterval* GetPreviousInterval() const; PRBool HasPlayed() const { return !mOldIntervals.IsEmpty(); } // Hashtable callback methods PR_STATIC_CALLBACK(PLDHashOperator) NotifyNewIntervalCallback( TimeValueSpecPtrKey* aKey, void* aData); @@ -534,16 +535,17 @@ protected: InstanceTimeList mBeginInstances; InstanceTimeList mEndInstances; PRUint32 mInstanceSerialIndex; nsSMILAnimationFunction* mClient; nsAutoPtr<nsSMILInterval> mCurrentInterval; IntervalList mOldIntervals; + PRUint32 mCurrentRepeatIteration; nsSMILMilestone mPrevRegisteredMilestone; static const nsSMILMilestone sMaxMilestone; static const PRUint8 sMaxNumIntervals; static const PRUint8 sMaxNumInstanceTimes; // Set of dependent time value specs to be notified when establishing a new // current interval. Change notifications and delete notifications are handled // by the interval.
--- a/content/smil/test/Makefile.in +++ b/content/smil/test/Makefile.in @@ -77,16 +77,17 @@ include $(topsrcdir)/config/rules.mk test_smilGetSimpleDuration.xhtml \ test_smilKeySplines.xhtml \ test_smilKeyTimesPacedMode.xhtml \ test_smilSetCurrentTime.xhtml \ test_smilSync.xhtml \ test_smilSyncbaseTarget.xhtml \ test_smilSyncTransform.xhtml \ test_smilTextZoom.xhtml \ + test_smilTimeEvents.xhtml \ test_smilTiming.xhtml \ test_smilTimingZeroIntervals.xhtml \ test_smilUpdatedInterval.xhtml \ test_smilXHR.xhtml \ $(NULL) libs:: $(_TEST_FILES) $(INSTALL) $^ $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
new file mode 100644 --- /dev/null +++ b/content/smil/test/test_smilTimeEvents.xhtml @@ -0,0 +1,292 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=572270 +--> +<head> + <title>Test TimeEvents dispatching</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> +<a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=572270">Mozilla Bug + 572270</a> +<p id="display"></p> +<div id="content" style="display: none"> +<svg id="svg" xmlns="http://www.w3.org/2000/svg" width="100px" height="100px"> + <g font-size="10px"> + <circle cx="0" cy="0" r="15" fill="blue" id="circle" + onbegin="parentHandler(evt)" onrepeat="parentHandler(evt)" + onend="parentHandler(evt)"> + <animate attributeName="cy" from="0" to="100" dur="60s" begin="2s" + id="anim" repeatCount="2" + onbegin="handleOnBegin(evt)" onrepeat="handleOnRepeat(evt)" + onend="handleOnEnd(evt)"/> + </circle> + </g> +</svg> +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> +<![CDATA[ +/** Test SMIL TimeEvents dispatching **/ + +/* Global Variables */ +const gTimeoutDur = 5000; // Time until we give up waiting for events in ms +var gSvg = document.getElementById("svg"); +var gAnim = document.getElementById('anim'); +var gCircle = document.getElementById('circle'); +var gExpectedEvents = new Array(); +var gTimeoutID; +var gTestStages = + [ testPlaybackBegin, + testPlaybackRepeat, + testPlaybackEnd, + testForwardsSeekToMid, + testForwardsSeekToNextInterval, + testForwardsSeekPastEnd, + testBackwardsSeekToMid, + testBackwardsSeekToStart, + testCreateEvent, + testRegistration + ]; + +SimpleTest.waitForExplicitFinish(); + +function continueTest() +{ + if (gTestStages.length == 0) { + SimpleTest.finish(); + return; + } + gTestStages.shift()(); +} + +function testPlaybackBegin() +{ + // Test events are dispatched through normal playback + gSvg.pauseAnimations(); + gSvg.setCurrentTime(1.99); + gExpectedEvents.push("beginEvent", "beginEvent"); // Two registered handlers + gTimeoutID = setTimeout(timeoutFail, gTimeoutDur); + gSvg.unpauseAnimations(); +} + +function testPlaybackRepeat() +{ + gSvg.pauseAnimations(); + gSvg.setCurrentTime(61.99); + gExpectedEvents.push(["repeatEvent", 1], ["repeatEvent", 1]); + gTimeoutID = setTimeout(timeoutFail, gTimeoutDur); + gSvg.unpauseAnimations(); +} + +function testPlaybackEnd() +{ + gSvg.pauseAnimations(); + gSvg.setCurrentTime(121.99); + gExpectedEvents.push("endEvent", "endEvent"); + gTimeoutID = setTimeout(timeoutFail, gTimeoutDur); + gSvg.unpauseAnimations(); +} + +function testForwardsSeekToMid() +{ + gSvg.pauseAnimations(); + // Set animation parameters to something that repeats a lot + gSvg.setCurrentTime(0); + gAnim.setAttribute('begin', '2s; 102s'); + gAnim.setAttribute('dur', '15s'); + gAnim.setAttribute('repeatCount', '6'); + gSvg.setCurrentTime(46.99); + gExpectedEvents.push("beginEvent", "beginEvent", + ["repeatEvent", 3], ["repeatEvent", 3]); + gTimeoutID = setTimeout(timeoutFail, gTimeoutDur); + gSvg.unpauseAnimations(); +} + +function testForwardsSeekToNextInterval() +{ + // Skip to next interval -- we shouldn't get any additional begin or end + // events in between + gSvg.pauseAnimations(); + gSvg.setCurrentTime(131.99); + gExpectedEvents.push(["repeatEvent", 2], ["repeatEvent", 2]); + gTimeoutID = setTimeout(timeoutFail, gTimeoutDur); + gSvg.unpauseAnimations(); +} + +function testForwardsSeekPastEnd() +{ + gSvg.pauseAnimations(); + gSvg.setCurrentTime(200); + gExpectedEvents.push("endEvent", "endEvent"); + gTimeoutID = setTimeout(timeoutFail, gTimeoutDur); + gSvg.unpauseAnimations(); +} + +function testBackwardsSeekToMid() +{ + gSvg.pauseAnimations(); + gSvg.setCurrentTime(31.99); + gExpectedEvents.push("beginEvent", "beginEvent", + ["repeatEvent", 2], ["repeatEvent", 2]); + gTimeoutID = setTimeout(timeoutFail, gTimeoutDur); + gSvg.unpauseAnimations(); +} + +function testBackwardsSeekToStart() +{ + gSvg.pauseAnimations(); + gExpectedEvents.push("endEvent", "endEvent"); + gTimeoutID = setTimeout(timeoutFail, gTimeoutDur); + gSvg.setCurrentTime(0); +} + +function testCreateEvent() +{ + var evt; + try { + evt = document.createEvent("TimeEvents"); + } catch (e) { + ok(false, "Failed to create TimeEvent via script: " + e); + return; + } + evt.initTimeEvent("repeatEvent", null, 3); + is(evt.type, "repeatEvent", "Unexpected type for user-generated event"); + is(evt.detail, 3, "Unexpected detail for user-generated event"); + is(evt.target, null, "Unexpected event target"); + is(evt.currentTarget, null, "Unexpected event current target"); + is(evt.eventPhase, evt.AT_TARGET); + is(evt.bubbles, false, "Event should not bubble"); + is(evt.cancelable, false, "Event should not be cancelable"); + is(evt.view, null, "Event view should be null"); + + // Prior to dispatch we should be able to change the event type + evt.initTimeEvent("beginEvent", document.defaultView, 0); + is(evt.type, "beginEvent", "Failed to update event type before dispatch"); + is(evt.detail, 0, "Failed to update event detail before dispatch"); + is(evt.view, document.defaultView, "Event view should be set"); + + // But not directly as it's readonly + try { + evt.type = "endEvent"; + } catch(e) { } + is(evt.type, "beginEvent", "Event type should be readonly"); + + // Likewise the detail field should be readonly + try { + evt.detail = "8"; + } catch(e) { } + is(evt.detail, 0, "Event detail should be readonly"); + + // Dispatch + gExpectedEvents.push("beginEvent", "beginEvent"); + gTimeoutID = setTimeout(timeoutFail, gTimeoutDur); + gAnim.dispatchEvent(evt); +} + +function testRegistration() +{ + gSvg.pauseAnimations(); + // Reset animation to something simple + gSvg.setCurrentTime(0); + gAnim.setAttribute('begin', '2s'); + gAnim.setAttribute('dur', '50s'); + + // Remove attribute handler + gAnim.removeAttribute('onbegin'); + + // Add bogus handlers + gAnim.setAttribute('onbeginElement', 'handleOnBegin(evt)'); + gAnim.addEventListener("begin", handleOnBegin, false); + gAnim.addEventListener("onbegin", handleOnBegin, false); + + // We should now have just one legitimate listener: the one registered to + // handle 'beginElement' + gSvg.setCurrentTime(1.99); + gExpectedEvents.push("beginEvent"); + gTimeoutID = setTimeout(timeoutFail, gTimeoutDur); + gSvg.unpauseAnimations(); +} + +function handleOnBegin(evt) +{ + is(evt.type, "beginEvent", "Expected begin event but got " + evt.type); + checkExpectedEvent(evt); +} + +function handleOnRepeat(evt) +{ + is(evt.type, "repeatEvent", "Expected repeat event but got " + evt.type); + checkExpectedEvent(evt); +} + +function handleOnEnd(evt) +{ + is(evt.type, "endEvent", "Expected end event but got " + evt.type); + checkExpectedEvent(evt); +} + +function sanityCheckEvent(evt) +{ + is(evt.target, gAnim, "Unexpected event target"); + is(evt.currentTarget, gAnim, "Unexpected event current target"); + is(evt.eventPhase, evt.AT_TARGET); + is(evt.bubbles, false, "Event should not bubble"); + is(evt.cancelable, false, "Event should not be cancelable"); + // Currently we set event timestamps to 0 which DOM 2 allows. This isn't + // correct since SMIL uses this field to avoid synchronisation slew but first + // we need to fix bug 323039 and bug 77992 which involve storing timestamps as + // 64-bit integers and deciding whether those timestamps should be related to + // the epoch or system start. + is(evt.timeStamp, 0, "Event timeStamp should be 0"); + ok(evt.view !== null, "Event view not set"); +} + +function checkExpectedEvent(evt) +{ + sanityCheckEvent(evt); + ok(gExpectedEvents.length > 0, "Unexpected event: " + evt.type); + if (gExpectedEvents.length == 0) return; + + var expected = gExpectedEvents.shift(); + if (typeof expected == 'string') { + is(evt.type, expected, "Unexpected event type"); + is(evt.detail, 0, "Unexpected event detail (repeat iteration)"); + } else { + is(evt.type, expected[0], "Unexpected event type"); + is(evt.detail, expected[1], "Unexpected event detail (repeat iteration)"); + } + if (gExpectedEvents.length == 0) { + clearTimeout(gTimeoutID); + continueTest(); + } +} + +function timeoutFail() +{ + ok(false, "Timed out waiting for events: " + gExpectedEvents.join(', ')); + SimpleTest.finish(); // No point continuing +} + +function parentHandler(evt) +{ + ok(false, "Handler on parent got called but event shouldn't bubble."); +} + +window.addEventListener("load", continueTest, false); + +// Register event handlers *in addition* to the handlers already added via the +// "onbegin", "onend", "onrepeat" attributes on the <animate> and <circle> +// elements. This is to test that both types of registration work. +gAnim.addEventListener("beginEvent", handleOnBegin, false); +gAnim.addEventListener("repeatEvent", handleOnRepeat, false); +gAnim.addEventListener("endEvent", handleOnEnd, false); +gCircle.addEventListener("beginEvent", parentHandler, false); +]]> +</script> +</pre> +</body> +</html>
--- a/content/svg/content/src/nsSVGAnimationElement.cpp +++ b/content/svg/content/src/nsSVGAnimationElement.cpp @@ -471,16 +471,22 @@ nsSVGAnimationElement::EndElementAt(floa if (NS_FAILED(rv)) return rv; AnimationNeedsResample(); return NS_OK; } +PRBool +nsSVGAnimationElement::IsEventName(nsIAtom* aName) +{ + return nsContentUtils::IsEventAttributeName(aName, EventNameType_SMIL); +} + void nsSVGAnimationElement::UpdateHrefTarget(nsIContent* aNodeForContext, const nsAString& aHrefStr) { nsCOMPtr<nsIURI> targetURI; nsCOMPtr<nsIURI> baseURI = GetBaseURI(); nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), aHrefStr, GetOwnerDoc(), baseURI);
--- a/content/svg/content/src/nsSVGAnimationElement.h +++ b/content/svg/content/src/nsSVGAnimationElement.h @@ -93,16 +93,19 @@ public: virtual PRBool HasAnimAttr(nsIAtom* aAttName) const; virtual mozilla::dom::Element* GetTargetElementContent(); virtual nsIAtom* GetTargetAttributeName() const; virtual nsSMILTargetAttrType GetTargetAttributeType() const; virtual nsSMILTimedElement& TimedElement(); virtual nsSMILTimeContainer* GetTimeContainer(); protected: + // nsSVGElement overrides + PRBool IsEventName(nsIAtom* aName); + void UpdateHrefTarget(nsIContent* aNodeForContext, const nsAString& aHrefStr); class TargetReference : public nsReferencedElement { public: TargetReference(nsSVGAnimationElement* aAnimationElement) : mAnimationElement(aAnimationElement) {} protected:
--- a/content/svg/content/src/nsSVGElement.cpp +++ b/content/svg/content/src/nsSVGElement.cpp @@ -1359,16 +1359,24 @@ nsIAtom* nsSVGElement::GetEventNameForAt if (aAttr == nsGkAtoms::onerror) return nsGkAtoms::onSVGError; if (aAttr == nsGkAtoms::onresize) return nsGkAtoms::onSVGResize; if (aAttr == nsGkAtoms::onscroll) 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; } nsSVGSVGElement * nsSVGElement::GetCtx() { nsCOMPtr<nsIDOMSVGSVGElement> svg;
--- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -372,16 +372,17 @@ #ifdef MOZ_SMIL #include "nsIDOMSVGAnimateElement.h" #include "nsIDOMSVGAnimateTransformElement.h" #include "nsIDOMSVGAnimateMotionElement.h" #include "nsIDOMSVGMpathElement.h" #include "nsIDOMSVGSetElement.h" #include "nsIDOMSVGAnimationElement.h" #include "nsIDOMElementTimeControl.h" +#include "nsIDOMTimeEvent.h" #endif // MOZ_SMIL #include "nsIDOMSVGAnimTransformList.h" #include "nsIDOMSVGCircleElement.h" #include "nsIDOMSVGClipPathElement.h" #include "nsIDOMSVGDefsElement.h" #include "nsIDOMSVGDescElement.h" #include "nsIDOMSVGDocument.h" #include "nsIDOMSVGElement.h" @@ -998,16 +999,18 @@ static nsDOMClassInfoData sClassInfoData NS_DEFINE_CLASSINFO_DATA(SVGAnimateTransformElement, nsElementSH, ELEMENT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(SVGAnimateMotionElement, nsElementSH, ELEMENT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(SVGMpathElement, nsElementSH, 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) NS_DEFINE_CLASSINFO_DATA(SVGClipPathElement, nsElementSH, ELEMENT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(SVGDefsElement, nsElementSH, ELEMENT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(SVGDescElement, nsElementSH, @@ -3036,16 +3039,20 @@ nsDOMClassInfo::Init() DOM_CLASSINFO_SVG_ELEMENT_MAP_ENTRIES DOM_CLASSINFO_MAP_END DOM_CLASSINFO_MAP_BEGIN(SVGMpathElement, nsIDOMSVGMpathElement) DOM_CLASSINFO_MAP_ENTRY(nsIDOMSVGURIReference) 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) DOM_CLASSINFO_MAP_ENTRY(nsIDOMSVGCircleElement) DOM_CLASSINFO_SVG_GRAPHIC_ELEMENT_MAP_ENTRIES DOM_CLASSINFO_MAP_END DOM_CLASSINFO_MAP_BEGIN(SVGClipPathElement, nsIDOMSVGClipPathElement)
--- a/dom/base/nsDOMClassInfoClasses.h +++ b/dom/base/nsDOMClassInfoClasses.h @@ -235,16 +235,17 @@ DOMCI_CLASS(SVGDocument) DOMCI_CLASS(SVGAElement) DOMCI_CLASS(SVGAltGlyphElement) #ifdef MOZ_SMIL DOMCI_CLASS(SVGAnimateElement) DOMCI_CLASS(SVGAnimateTransformElement) DOMCI_CLASS(SVGAnimateMotionElement) DOMCI_CLASS(SVGMpathElement) DOMCI_CLASS(SVGSetElement) +DOMCI_CLASS(TimeEvent) #endif // MOZ_SMIL DOMCI_CLASS(SVGCircleElement) DOMCI_CLASS(SVGClipPathElement) DOMCI_CLASS(SVGDefsElement) DOMCI_CLASS(SVGDescElement) DOMCI_CLASS(SVGEllipseElement) DOMCI_CLASS(SVGFEBlendElement) DOMCI_CLASS(SVGFEColorMatrixElement)
--- a/dom/interfaces/smil/Makefile.in +++ b/dom/interfaces/smil/Makefile.in @@ -43,11 +43,12 @@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk MODULE = dom XPIDL_MODULE = dom_smil XPIDLSRCS = \ nsIDOMElementTimeControl.idl \ + nsIDOMTimeEvent.idl \ $(NULL) include $(topsrcdir)/config/rules.mk
new file mode 100644 --- /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 <birtles@gmail.com> + * + * 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); +};
--- a/widget/public/nsGUIEvent.h +++ b/widget/public/nsGUIEvent.h @@ -96,16 +96,19 @@ class nsHashKey; #define NS_SCROLLAREA_EVENT 25 #define NS_TRANSITION_EVENT 26 #define NS_UI_EVENT 27 #ifdef MOZ_SVG #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 #define NS_DRAG_EVENT 35 #define NS_NOTIFYPAINT_EVENT 36 #define NS_SIMPLE_GESTURE_EVENT 37 #define NS_SELECTION_EVENT 38 #define NS_CONTENT_COMMAND_EVENT 39 @@ -455,16 +458,23 @@ class nsHashKey; #define NS_ORIENTATION_EVENT 4000 #define NS_SCROLLAREA_EVENT_START 4100 #define NS_SCROLLEDAREACHANGED (NS_SCROLLAREA_EVENT_START) #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. */ /** * different types of (top-level) window z-level positioning */