--- a/dom/media/MediaEventSource.h
+++ b/dom/media/MediaEventSource.h
@@ -53,16 +53,21 @@ enum class ListenerPolicy : int8_t {
// Allow at most one listener. Move will be used when possible
// to pass the event data to save copy.
Exclusive,
// Allow multiple listeners. Event data will always be copied when passed
// to the listeners.
NonExclusive
};
+enum class DispatchPolicy : int8_t {
+ Sync, // Events are passed synchronously to the listeners.
+ Async // Events are passed asynchronously to the listeners.
+};
+
namespace detail {
/**
* Define how an event type is passed internally in MediaEventSource and to the
* listeners. Specialized for the void type to pass a dummy bool instead.
*/
template <typename T>
struct EventTypeTraits {
@@ -87,34 +92,59 @@ class TakeArgsHelper {
static TrueType test(...);
public:
typedef decltype(test(DeclVal<T>(), 0)) Type;
};
template <typename T>
struct TakeArgs : public TakeArgsHelper<T>::Type {};
-template <typename T> struct EventTarget;
+template <DispatchPolicy Dp, typename T> struct EventTarget;
template <>
-struct EventTarget<nsIEventTarget> {
+struct EventTarget<DispatchPolicy::Async, nsIEventTarget> {
static void
Dispatch(nsIEventTarget* aTarget, already_AddRefed<nsIRunnable> aTask) {
aTarget->Dispatch(Move(aTask), NS_DISPATCH_NORMAL);
}
};
template <>
-struct EventTarget<AbstractThread> {
+struct EventTarget<DispatchPolicy::Async, AbstractThread> {
static void
Dispatch(AbstractThread* aTarget, already_AddRefed<nsIRunnable> aTask) {
aTarget->Dispatch(Move(aTask));
}
};
+template <>
+struct EventTarget<DispatchPolicy::Sync, nsIEventTarget> {
+ static bool IsOnCurrentThread(nsIEventTarget* aTarget) {
+ bool current = false;
+ auto rv = aTarget->IsOnCurrentThread(¤t);
+ return NS_SUCCEEDED(rv) && current;
+ }
+ static void
+ Dispatch(nsIEventTarget* aTarget, already_AddRefed<nsIRunnable> aTask) {
+ MOZ_ASSERT(IsOnCurrentThread(aTarget));
+ nsCOMPtr<nsIRunnable> r = aTask;
+ r->Run();
+ }
+};
+
+template <>
+struct EventTarget<DispatchPolicy::Sync, AbstractThread> {
+ static void
+ Dispatch(AbstractThread* aTarget, already_AddRefed<nsIRunnable> aTask) {
+ MOZ_ASSERT(aTarget->IsCurrentThreadIn());
+ nsCOMPtr<nsIRunnable> r = aTask;
+ r->Run();
+ }
+};
+
/**
* Encapsulate a raw pointer to be captured by a lambda without causing
* static-analysis errors.
*/
template <typename T>
class RawPtr {
public:
explicit RawPtr(T* aPtr) : mPtr(aPtr) {}
@@ -122,17 +152,17 @@ public:
private:
T* const mPtr;
};
/**
* A helper class to pass event data to the listeners. Optimized to save
* copy when Move is possible or |Function| takes no arguments.
*/
-template<typename Target, typename Function>
+template<DispatchPolicy Dp, typename Target, typename Function>
class ListenerHelper {
// Define our custom runnable to minimize copy of the event data.
// NS_NewRunnableFunction will result in 2 copies of the event data.
// One is captured by the lambda and the other is the copy of the lambda.
template <typename... Ts>
class R : public Runnable {
public:
template <typename... Us>
@@ -169,31 +199,31 @@ public:
: mToken(aToken), mTarget(aTarget), mFunction(aFunc) {}
// |F| takes one or more arguments.
template <typename F, typename... Ts>
typename EnableIf<TakeArgs<F>::value, void>::Type
DispatchHelper(const F& aFunc, Ts&&... aEvents) {
nsCOMPtr<nsIRunnable> r =
new R<Ts...>(mToken, aFunc, Forward<Ts>(aEvents)...);
- EventTarget<Target>::Dispatch(mTarget.get(), r.forget());
+ EventTarget<Dp, Target>::Dispatch(mTarget.get(), r.forget());
}
// |F| takes no arguments. Don't bother passing aEvent.
template <typename F, typename... Ts>
typename EnableIf<!TakeArgs<F>::value, void>::Type
DispatchHelper(const F& aFunc, Ts&&...) {
const RefPtr<RevocableToken>& token = mToken;
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
// Don't call the listener if it is disconnected.
if (!token->IsRevoked()) {
aFunc();
}
});
- EventTarget<Target>::Dispatch(mTarget.get(), r.forget());
+ EventTarget<Dp, Target>::Dispatch(mTarget.get(), r.forget());
}
template <typename... Ts>
void Dispatch(Ts&&... aEvents) {
DispatchHelper(mFunction, Forward<Ts>(aEvents)...);
}
private:
@@ -244,39 +274,40 @@ public:
virtual ~Listener() {}
virtual void Dispatch(As... aEvents) = 0;
};
/**
* Store the registered target thread and function so it knows where and to
* whom to send the event data.
*/
-template <typename Target, typename Function, EventPassMode, typename... As>
+template <DispatchPolicy Dp, typename Target,
+ typename Function, EventPassMode, typename... As>
class ListenerImpl : public Listener<EventPassMode::Copy, As...> {
public:
ListenerImpl(Target* aTarget, const Function& aFunction)
: mHelper(ListenerBase::Token(), aTarget, aFunction) {}
void Dispatch(const As&... aEvents) override {
mHelper.Dispatch(aEvents...);
}
private:
- ListenerHelper<Target, Function> mHelper;
+ ListenerHelper<Dp, Target, Function> mHelper;
};
-template <typename Target, typename Function, typename... As>
-class ListenerImpl<Target, Function, EventPassMode::Move, As...>
+template <DispatchPolicy Dp, typename Target, typename Function, typename... As>
+class ListenerImpl<Dp, Target, Function, EventPassMode::Move, As...>
: public Listener<EventPassMode::Move, As...> {
public:
ListenerImpl(Target* aTarget, const Function& aFunction)
: mHelper(ListenerBase::Token(), aTarget, aFunction) {}
void Dispatch(As... aEvents) override {
mHelper.Dispatch(Move(aEvents)...);
}
private:
- ListenerHelper<Target, Function> mHelper;
+ ListenerHelper<Dp, Target, Function> mHelper;
};
/**
* Select EventPassMode based on ListenerPolicy.
*
* @Copy Selected when ListenerPolicy is NonExclusive because each listener
* must get a copy.
*
@@ -301,26 +332,27 @@ struct IsAnyReference {
template <typename T>
struct IsAnyReference<T> {
static const bool value = IsReference<T>::value;
};
} // namespace detail
-template <ListenerPolicy, typename... Ts> class MediaEventSourceImpl;
+template <DispatchPolicy, ListenerPolicy, typename... Ts>
+class MediaEventSourceImpl;
/**
* Not thread-safe since this is not meant to be shared and therefore only
* move constructor is provided. Used to hold the result of
* MediaEventSource<T>::Connect() and call Disconnect() to disconnect the
* listener from an event source.
*/
class MediaEventListener {
- template <ListenerPolicy, typename... Ts>
+ template <DispatchPolicy, ListenerPolicy, typename... Ts>
friend class MediaEventSourceImpl;
public:
MediaEventListener() {}
MediaEventListener(MediaEventListener&& aOther)
: mToken(Move(aOther.mToken)) {}
@@ -350,32 +382,32 @@ private:
// listeners can be disconnected in a controlled manner.
explicit MediaEventListener(RevocableToken* aToken) : mToken(aToken) {}
RefPtr<RevocableToken> mToken;
};
/**
* A generic and thread-safe class to implement the observer pattern.
*/
-template <ListenerPolicy Lp, typename... Es>
+template <DispatchPolicy Dp, ListenerPolicy Lp, typename... Es>
class MediaEventSourceImpl {
static_assert(!detail::IsAnyReference<Es...>::value,
"Ref-type not supported!");
template <typename T>
using ArgType = typename detail::EventTypeTraits<T>::ArgType;
static const detail::EventPassMode PassMode =
detail::PassModePicker<Lp>::Value;
typedef detail::Listener<PassMode, ArgType<Es>...> Listener;
template<typename Target, typename Func>
using ListenerImpl =
- detail::ListenerImpl<Target, Func, PassMode, ArgType<Es>...>;
+ detail::ListenerImpl<Dp, Target, Func, PassMode, ArgType<Es>...>;
template <typename Method>
using TakeArgs = detail::TakeArgs<Method>;
template<typename Target, typename Function>
MediaEventListener
ConnectInternal(Target* aTarget, const Function& aFunction) {
MutexAutoLock lock(mMutex);
@@ -471,21 +503,22 @@ protected:
private:
Mutex mMutex;
nsTArray<UniquePtr<Listener>> mListeners;
};
template <typename... Es>
using MediaEventSource =
- MediaEventSourceImpl<ListenerPolicy::NonExclusive, Es...>;
+ MediaEventSourceImpl<DispatchPolicy::Async,
+ ListenerPolicy::NonExclusive, Es...>;
template <typename... Es>
using MediaEventSourceExc =
- MediaEventSourceImpl<ListenerPolicy::Exclusive, Es...>;
+ MediaEventSourceImpl<DispatchPolicy::Async, ListenerPolicy::Exclusive, Es...>;
/**
* A class to separate the interface of event subject (MediaEventSource)
* and event publisher. Mostly used as a member variable to publish events
* to the listeners.
*/
template <typename... Es>
class MediaEventProducer : public MediaEventSource<Es...> {
@@ -515,11 +548,41 @@ template <typename... Es>
class MediaEventProducerExc : public MediaEventSourceExc<Es...> {
public:
template <typename... Ts>
void Notify(Ts&&... aEvents) {
this->NotifyInternal(Forward<Ts>(aEvents)...);
}
};
+/**
+ * Events are passed directly to the callback function of the listeners without
+ * dispatching. Note this class is not thread-safe. Both Connect() and Notify()
+ * must be called on the same thread.
+ */
+template <typename... Es>
+class MediaCallback
+ : public MediaEventSourceImpl<DispatchPolicy::Sync,
+ ListenerPolicy::NonExclusive, Es...> {
+public:
+ template <typename... Ts>
+ void Notify(Ts&&... aEvents) {
+ this->NotifyInternal(Forward<Ts>(aEvents)...);
+ }
+};
+
+/**
+ * A special version of MediaCallback which allows at most one listener.
+ */
+template <typename... Es>
+class MediaCallbackExc
+ : public MediaEventSourceImpl<DispatchPolicy::Sync,
+ ListenerPolicy::Exclusive, Es...> {
+public:
+ template <typename... Ts>
+ void Notify(Ts&&... aEvents) {
+ this->NotifyInternal(Forward<Ts>(aEvents)...);
+ }
+};
+
} // namespace mozilla
#endif //MediaEventSource_h_