Bug 1361259. P2 - use NewRunnableMethod() to pass event data to the listener function. draft
authorJW Wang <jwwang@mozilla.com>
Fri, 28 Apr 2017 16:28:47 +0800
changeset 571200 05c792557e9c01740f6c95b2313ad4336e5b7d5f
parent 571199 f87215a2c7fc27140dad879561ec18a735639229
child 571201 12b6a213d49b1d8405a058030aad520c4cbde726
push id56725
push userjwwang@mozilla.com
push dateTue, 02 May 2017 08:46:33 +0000
bugs1361259
milestone55.0a1
Bug 1361259. P2 - use NewRunnableMethod() to pass event data to the listener function. Note this breaks the MediaEventSource::CopyEvent2 gtest since there is always one copy or move when storing the event data in the runnable created by NewRunnableMethod() even when the listener function takes no arguments at all. We will fix it later. MozReview-Commit-ID: J9T63yxXko2
dom/media/MediaEventSource.h
--- a/dom/media/MediaEventSource.h
+++ b/dom/media/MediaEventSource.h
@@ -229,53 +229,74 @@ protected:
  * Stored by MediaEventSource to send notifications to the listener.
  * Since virtual methods can not be templated, this class is specialized
  * to provide different Dispatch() overloads depending on EventPassMode.
  */
 template <EventPassMode Mode, typename... As>
 class Listener : public ListenerBase
 {
 public:
-  virtual void Dispatch(const As&... aEvents) = 0;
-};
+  template <typename... Ts>
+  void Dispatch(Ts&&... aEvents)
+  {
+    DispatchTask(NewRunnableMethod<typename Decay<Ts>::Type&&...>(
+      this, &Listener::Apply, Forward<Ts>(aEvents)...));
+  }
 
-template <typename... As>
-class Listener<EventPassMode::Move, As...> : public ListenerBase
-{
-public:
-  virtual void Dispatch(As... aEvents) = 0;
+private:
+  virtual void DispatchTask(already_AddRefed<nsIRunnable> aTask) = 0;
+  virtual void Apply(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>
-class ListenerImpl : public Listener<EventPassMode::Copy, As...> {
+template <typename Target, typename Function, EventPassMode Mode, typename... As>
+class ListenerImpl : public Listener<Mode, As...>
+{
 public:
   ListenerImpl(Target* aTarget, const Function& aFunction)
-    : mHelper(this, aTarget, aFunction) {}
-  void Dispatch(const As&... aEvents) override {
-    mHelper.Dispatch(aEvents...);
+    : mTarget(aTarget)
+    , mFunction(aFunction)
+  {
   }
+
 private:
-  ListenerHelper<Target, Function> mHelper;
-};
+  void DispatchTask(already_AddRefed<nsIRunnable> aTask) override
+  {
+    EventTarget<Target>::Dispatch(mTarget.get(), Move(aTask));
+  }
+
+  // |F| takes one or more arguments.
+  template <typename F>
+  typename EnableIf<TakeArgs<F>::value, void>::Type
+  ApplyImpl(const F& aFunc, As&&... aEvents)
+  {
+    aFunc(Move(aEvents)...);
+  }
 
-template <typename Target, typename Function, typename... As>
-class ListenerImpl<Target, Function, EventPassMode::Move, As...>
-  : public Listener<EventPassMode::Move, As...> {
-public:
-  ListenerImpl(Target* aTarget, const Function& aFunction)
-    : mHelper(this, aTarget, aFunction) {}
-  void Dispatch(As... aEvents) override {
-    mHelper.Dispatch(Move(aEvents)...);
+  // |F| takes no arguments. Don't bother passing aEvent.
+  template <typename F>
+  typename EnableIf<!TakeArgs<F>::value, void>::Type
+  ApplyImpl(const F& aFunc, As&&... aEvents)
+  {
+    aFunc();
   }
-private:
-  ListenerHelper<Target, Function> mHelper;
+
+  void Apply(As&&... aEvents) override
+  {
+    // Don't call the listener if it is disconnected.
+    if (!RevocableToken::IsRevoked()) {
+      ApplyImpl(mFunction, Move(aEvents)...);
+    }
+  }
+
+  const RefPtr<Target> mTarget;
+  Function mFunction;
 };
 
 /**
  * Select EventPassMode based on ListenerPolicy.
  *
  * @Copy Selected when ListenerPolicy is NonExclusive because each listener
  * must get a copy.
  *