Bug 1434710 - Removed mfbt/IndexSequence.h. r=botond draft
authorTom Bannister <tomabann@gmail.com>
Tue, 10 Apr 2018 20:17:23 +1000
changeset 779646 c36fe25dc37738f44bbfc6c0c1ddb16898af9df6
parent 779645 e9c10c8ed8dfe56bc6c32d5c8260006a2d394713
push id105830
push userbmo:tomabann@gmail.com
push dateTue, 10 Apr 2018 11:06:59 +0000
reviewersbotond
bugs1434710
milestone61.0a1
Bug 1434710 - Removed mfbt/IndexSequence.h. r=botond MozReview-Commit-ID: CzK20phNSKt
dom/media/MediaEventSource.h
mfbt/IndexSequence.h
mfbt/moz.build
obj-i686-pc-mingw32/dist/include/MediaEventSource.h
obj-i686-pc-mingw32/dist/include/mozilla/MozPromise.h
--- a/dom/media/MediaEventSource.h
+++ b/dom/media/MediaEventSource.h
@@ -4,17 +4,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MediaEventSource_h_
 #define MediaEventSource_h_
 
 #include "mozilla/AbstractThread.h"
 #include "mozilla/Atomics.h"
-#include "mozilla/IndexSequence.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/Tuple.h"
 #include "mozilla/TypeTraits.h"
 #include "mozilla/Unused.h"
 
 #include "nsISupportsImpl.h"
 #include "nsTArray.h"
 #include "nsThreadUtils.h"
deleted file mode 100644
--- a/mfbt/IndexSequence.h
+++ /dev/null
@@ -1,143 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-/* A utility for expanding a tuple into a variadic argument list.
- * Based on std::index_sequence. */
-
-/**
- * Example usage:
- *
- * Problem:
- *
- *   You have a variadic function Foo:
- *
- *     template <typename... Args> void Foo(Args...);
- *
- *   And a variadic function Bar, which contains a tuple:
- *
- *     template <typename... Args>
- *     void Bar() {
- *       // ...
- *       Tuple<Args...> t;
- *     }
- *
- *   And inside Bar, you want to call Foo with the elements of the tuple as
- *   arguments to Foo.
- *
- *   You want to write:
- *
- *     Foo(Get<0>(t), Get<1>(t), ..., Get<N>(t))
- *
- *   but you can't literally write that, because N is different for different
- *   instantiations of Bar.
- *
- * Solution:
- *
- *   Write a helper function which takes the tuple, and an index sequence
- *   containing indices corresponding to the tuple indices.
- *
- *     template <typename... Args, size_t... Indices>
- *     void Helper(const Tuple<Args...>& t, IndexSequence<Indices...>)
- *     {
- *       Foo(Get<Indices>(t)...);
- *     }
- *
- *   Assuming 'Indices...' are 0, 1, ..., N - 1, where N is the size of the
- *   tuple, pack expansion will expand the pack 'Get<Indices>(t)...' to
- *   'Get<0>(t), Get<1>(t), ..., Get<N>(t)'.
- *
- *   Finally, call the helper, creating the index sequence to pass in like so:
- *
- *     template <typename... Args>
- *     void Bar() {
- *       // ...
- *       Tuple<Args...> t;
- *       Helper(t, typename IndexSequenceFor<Args...>::Type());
- *     }
- */
-
-#ifndef mozilla_IndexSequence_h
-#define mozilla_IndexSequence_h
-
-#include "mozilla/Attributes.h"
-
-#include <stddef.h>
-
-namespace mozilla {
-
-/**
- * Represents a compile-time sequence of integer indices.
- */
-template<size_t... Indices>
-struct IndexSequence
-{
-  static constexpr size_t Size() { return sizeof...(Indices); }
-};
-
-namespace detail {
-
-// Helpers used by MakeIndexSequence.
-
-template<size_t... Indices>
-struct IndexTuple
-{
-  typedef IndexTuple<Indices..., sizeof...(Indices)> Next;
-};
-
-// Builds IndexTuple<0, 1, ..., N - 1>.
-template<size_t N>
-struct BuildIndexTuple
-{
-  typedef typename BuildIndexTuple<N - 1>::Type::Next Type;
-};
-
-template<>
-struct BuildIndexTuple<0>
-{
-  typedef IndexTuple<> Type;
-};
-
-template<size_t N, typename IndexTuple>
-struct MakeIndexSequenceImpl;
-
-template<size_t N, size_t... Indices>
-struct MakeIndexSequenceImpl<N, IndexTuple<Indices...>>
-{
-  typedef IndexSequence<Indices...> Type;
-};
-
-} // namespace detail
-
-/**
- * A utility for building an IndexSequence of consecutive indices.
- * MakeIndexSequence<N>::Type evaluates to IndexSequence<0, 1, .., N - 1>.
- * Note: unlike std::make_index_sequence, this is not an alias template
- * to work around bugs in MSVC 2013.
- */
-template<size_t N>
-struct MakeIndexSequence
-{
-  typedef typename detail::MakeIndexSequenceImpl<N,
-    typename detail::BuildIndexTuple<N>::Type>::Type Type;
-};
-
-/**
- * A utility for building an IndexSequence of consecutive indices
- * corresponding to a variadic argument list.
- * IndexSequenceFor<Types...> evaluates to IndexSequence<0, 1, ..., N - 1>
- * where N is the number of types in Types.
- * Note: unlike std::index_sequence_for, this is not an alias template
- * to work around bugs in MSVC 2013.
- */
-template<typename... Types>
-struct IndexSequenceFor
-{
-  typedef typename MakeIndexSequence<sizeof...(Types)>::Type Type;
-};
-
-} // namespace mozilla
-
-#endif /* mozilla_IndexSequence_h */
--- a/mfbt/moz.build
+++ b/mfbt/moz.build
@@ -39,17 +39,16 @@ EXPORTS.mozilla = [
     'EnumeratedRange.h',
     'EnumSet.h',
     'EnumTypeTraits.h',
     'FastBernoulliTrial.h',
     'FloatingPoint.h',
     'FStream.h',
     'GuardObjects.h',
     'HashFunctions.h',
-    'IndexSequence.h',
     'IntegerPrintfMacros.h',
     'IntegerRange.h',
     'IntegerTypeTraits.h',
     'JSONWriter.h',
     'Likely.h',
     'LinkedList.h',
     'MacroArgs.h',
     'MacroForEach.h',
new file mode 100644
--- /dev/null
+++ b/obj-i686-pc-mingw32/dist/include/MediaEventSource.h
@@ -0,0 +1,491 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MediaEventSource_h_
+#define MediaEventSource_h_
+
+#include "mozilla/AbstractThread.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/Tuple.h"
+#include "mozilla/TypeTraits.h"
+#include "mozilla/Unused.h"
+
+#include "nsISupportsImpl.h"
+#include "nsTArray.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+
+/**
+ * A thread-safe tool to communicate "revocation" across threads. It is used to
+ * disconnect a listener from the event source to prevent future notifications
+ * from coming. Revoke() can be called on any thread. However, it is recommended
+ * to be called on the target thread to avoid race condition.
+ *
+ * RevocableToken is not exposed to the client code directly.
+ * Use MediaEventListener below to do the job.
+ */
+class RevocableToken {
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RevocableToken);
+
+public:
+  RevocableToken() : mRevoked(false) {}
+
+  void Revoke() {
+    mRevoked = true;
+  }
+
+  bool IsRevoked() const {
+    return mRevoked;
+  }
+
+protected:
+  // Virtual destructor is required since we might delete a Listener object
+  // through its base type pointer.
+  virtual ~RevocableToken() { }
+
+private:
+  Atomic<bool> mRevoked;
+};
+
+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
+};
+
+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 {
+  typedef T ArgType;
+};
+
+template <>
+struct EventTypeTraits<void> {
+  typedef bool ArgType;
+};
+
+/**
+ * Test if a method function or lambda accepts one or more arguments.
+ */
+template <typename T>
+class TakeArgsHelper {
+  template <typename C> static FalseType test(void(C::*)(), int);
+  template <typename C> static FalseType test(void(C::*)() const, int);
+  template <typename C> static FalseType test(void(C::*)() volatile, int);
+  template <typename C> static FalseType test(void(C::*)() const volatile, int);
+  template <typename F> static FalseType test(F&&, decltype(DeclVal<F>()(), 0));
+  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 <>
+struct EventTarget<nsIEventTarget> {
+  static void
+  Dispatch(nsIEventTarget* aTarget, already_AddRefed<nsIRunnable> aTask) {
+    aTarget->Dispatch(Move(aTask), NS_DISPATCH_NORMAL);
+  }
+};
+
+template <>
+struct EventTarget<AbstractThread> {
+  static void
+  Dispatch(AbstractThread* aTarget, already_AddRefed<nsIRunnable> aTask) {
+    Unused << aTarget->Dispatch(Move(aTask));
+  }
+};
+
+/**
+ * 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) {}
+  T* get() const { return mPtr; }
+private:
+  T* const mPtr;
+};
+
+template <typename... As>
+class Listener : public RevocableToken
+{
+public:
+  template <typename... Ts>
+  void Dispatch(Ts&&... aEvents)
+  {
+    if (CanTakeArgs()) {
+      DispatchTask(NewRunnableMethod<typename Decay<Ts>::Type&&...>(
+        "detail::Listener::ApplyWithArgs",
+        this,
+        &Listener::ApplyWithArgs,
+        Forward<Ts>(aEvents)...));
+    } else {
+      DispatchTask(NewRunnableMethod(
+        "detail::Listener::ApplyWithNoArgs", this, &Listener::ApplyWithNoArgs));
+    }
+  }
+
+protected:
+  virtual ~Listener()
+  {
+    MOZ_ASSERT(IsRevoked(), "Must disconnect the listener.");
+  }
+
+private:
+  virtual void DispatchTask(already_AddRefed<nsIRunnable> aTask) = 0;
+
+  // True if the underlying listener function takes non-zero arguments.
+  virtual bool CanTakeArgs() const = 0;
+  // Pass the event data to the underlying listener function. Should be called
+  // only when CanTakeArgs() returns true.
+  virtual void ApplyWithArgs(As&&... aEvents) = 0;
+  // Invoke the underlying listener function. Should be called only when
+  // CanTakeArgs() returns false.
+  virtual void ApplyWithNoArgs() = 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, typename... As>
+class ListenerImpl : public Listener<As...>
+{
+  // Strip CV and reference from Function.
+  using FunctionStorage = typename Decay<Function>::Type;
+
+public:
+  template <typename F>
+  ListenerImpl(Target* aTarget, F&& aFunction)
+    : mTarget(aTarget)
+    , mFunction(Forward<F>(aFunction))
+  {
+  }
+
+private:
+  void DispatchTask(already_AddRefed<nsIRunnable> aTask) override
+  {
+    EventTarget<Target>::Dispatch(mTarget.get(), Move(aTask));
+  }
+
+  bool CanTakeArgs() const override
+  {
+    return TakeArgs<FunctionStorage>::value;
+  }
+
+  // |F| takes one or more arguments.
+  template <typename F>
+  typename EnableIf<TakeArgs<F>::value, void>::Type
+  ApplyWithArgsImpl(const F& aFunc, As&&... aEvents)
+  {
+    aFunc(Move(aEvents)...);
+  }
+
+  // |F| takes no arguments.
+  template <typename F>
+  typename EnableIf<!TakeArgs<F>::value, void>::Type
+  ApplyWithArgsImpl(const F& aFunc, As&&... aEvents)
+  {
+    MOZ_CRASH("Call ApplyWithNoArgs instead.");
+  }
+
+  void ApplyWithArgs(As&&... aEvents) override
+  {
+    MOZ_RELEASE_ASSERT(TakeArgs<Function>::value);
+    // Don't call the listener if it is disconnected.
+    if (!RevocableToken::IsRevoked()) {
+      ApplyWithArgsImpl(mFunction, Move(aEvents)...);
+    }
+  }
+
+  // |F| takes one or more arguments.
+  template <typename F>
+  typename EnableIf<TakeArgs<F>::value, void>::Type
+  ApplyWithNoArgsImpl(const F& aFunc)
+  {
+    MOZ_CRASH("Call ApplyWithArgs instead.");
+  }
+
+  // |F| takes no arguments.
+  template <typename F>
+  typename EnableIf<!TakeArgs<F>::value, void>::Type
+  ApplyWithNoArgsImpl(const F& aFunc)
+  {
+    aFunc();
+  }
+
+  virtual void ApplyWithNoArgs() override
+  {
+    MOZ_RELEASE_ASSERT(!TakeArgs<Function>::value);
+    // Don't call the listener if it is disconnected.
+    if (!RevocableToken::IsRevoked()) {
+      ApplyWithNoArgsImpl(mFunction);
+    }
+  }
+
+  const RefPtr<Target> mTarget;
+  FunctionStorage mFunction;
+};
+
+/**
+ * Return true if any type is a reference type.
+ */
+template <typename Head, typename... Tails>
+struct IsAnyReference {
+  static const bool value = IsReference<Head>::value ||
+                            IsAnyReference<Tails...>::value;
+};
+
+template <typename T>
+struct IsAnyReference<T> {
+  static const bool value = IsReference<T>::value;
+};
+
+} // namespace detail
+
+template <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>
+  friend class MediaEventSourceImpl;
+
+public:
+  MediaEventListener() {}
+
+  MediaEventListener(MediaEventListener&& aOther)
+    : mToken(Move(aOther.mToken)) {}
+
+  MediaEventListener& operator=(MediaEventListener&& aOther) {
+    MOZ_ASSERT(!mToken, "Must disconnect the listener.");
+    mToken = Move(aOther.mToken);
+    return *this;
+  }
+
+  ~MediaEventListener() {
+    MOZ_ASSERT(!mToken, "Must disconnect the listener.");
+  }
+
+  void Disconnect() {
+    mToken->Revoke();
+    mToken = nullptr;
+  }
+
+  void DisconnectIfExists() {
+    if (mToken) {
+      Disconnect();
+    }
+  }
+
+private:
+  // Avoid exposing RevocableToken directly to the client code so that
+  // 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>
+class MediaEventSourceImpl {
+  static_assert(!detail::IsAnyReference<Es...>::value,
+                "Ref-type not supported!");
+
+  template <typename T>
+  using ArgType = typename detail::EventTypeTraits<T>::ArgType;
+
+  typedef detail::Listener<ArgType<Es>...> Listener;
+
+  template<typename Target, typename Func>
+  using ListenerImpl = detail::ListenerImpl<Target, Func, ArgType<Es>...>;
+
+  template <typename Method>
+  using TakeArgs = detail::TakeArgs<Method>;
+
+  void PruneListeners() {
+    int32_t last = static_cast<int32_t>(mListeners.Length()) - 1;
+    for (int32_t i = last; i >= 0; --i) {
+      if (mListeners[i]->IsRevoked()) {
+        mListeners.RemoveElementAt(i);
+      }
+    }
+  }
+
+  template<typename Target, typename Function>
+  MediaEventListener
+  ConnectInternal(Target* aTarget, Function&& aFunction) {
+    MutexAutoLock lock(mMutex);
+    PruneListeners();
+    MOZ_ASSERT(Lp == ListenerPolicy::NonExclusive || mListeners.IsEmpty());
+    auto l = mListeners.AppendElement();
+    *l = new ListenerImpl<Target, Function>(
+      aTarget, Forward<Function>(aFunction));
+    return MediaEventListener(*l);
+  }
+
+  // |Method| takes one or more arguments.
+  template <typename Target, typename This, typename Method>
+  typename EnableIf<TakeArgs<Method>::value, MediaEventListener>::Type
+  ConnectInternal(Target* aTarget, This* aThis, Method aMethod) {
+    detail::RawPtr<This> thiz(aThis);
+    return ConnectInternal(aTarget,
+      [=](ArgType<Es>&&... aEvents) {
+        (thiz.get()->*aMethod)(Move(aEvents)...);
+      });
+  }
+
+  // |Method| takes no arguments. Don't bother passing the event data.
+  template <typename Target, typename This, typename Method>
+  typename EnableIf<!TakeArgs<Method>::value, MediaEventListener>::Type
+  ConnectInternal(Target* aTarget, This* aThis, Method aMethod) {
+    detail::RawPtr<This> thiz(aThis);
+    return ConnectInternal(aTarget,
+      [=]() {
+        (thiz.get()->*aMethod)();
+      });
+  }
+
+public:
+  /**
+   * Register a function to receive notifications from the event source.
+   *
+   * @param aTarget The target thread on which the function will run.
+   * @param aFunction A function to be called on the target thread. The function
+   *                  parameter must be convertible from |EventType|.
+   * @return An object used to disconnect from the event source.
+   */
+  template<typename Function>
+  MediaEventListener
+  Connect(AbstractThread* aTarget, Function&& aFunction) {
+    return ConnectInternal(aTarget, Forward<Function>(aFunction));
+  }
+
+  template<typename Function>
+  MediaEventListener
+  Connect(nsIEventTarget* aTarget, Function&& aFunction) {
+    return ConnectInternal(aTarget, Forward<Function>(aFunction));
+  }
+
+  /**
+   * As above.
+   *
+   * Note we deliberately keep a weak reference to |aThis| in order not to
+   * change its lifetime. This is because notifications are dispatched
+   * asynchronously and removing a listener doesn't always break the reference
+   * cycle for the pending event could still hold a reference to |aThis|.
+   *
+   * The caller must call MediaEventListener::Disconnect() to avoid dangling
+   * pointers.
+   */
+  template <typename This, typename Method>
+  MediaEventListener
+  Connect(AbstractThread* aTarget, This* aThis, Method aMethod) {
+    return ConnectInternal(aTarget, aThis, aMethod);
+  }
+
+  template <typename This, typename Method>
+  MediaEventListener
+  Connect(nsIEventTarget* aTarget, This* aThis, Method aMethod) {
+    return ConnectInternal(aTarget, aThis, aMethod);
+  }
+
+protected:
+  MediaEventSourceImpl() : mMutex("MediaEventSourceImpl::mMutex") {}
+
+  template <typename... Ts>
+  void NotifyInternal(Ts&&... aEvents) {
+    MutexAutoLock lock(mMutex);
+    int32_t last = static_cast<int32_t>(mListeners.Length()) - 1;
+    for (int32_t i = last; i >= 0; --i) {
+      auto&& l = mListeners[i];
+      // Remove disconnected listeners.
+      // It is not optimal but is simple and works well.
+      if (l->IsRevoked()) {
+        mListeners.RemoveElementAt(i);
+        continue;
+      }
+      l->Dispatch(Forward<Ts>(aEvents)...);
+    }
+  }
+
+private:
+  Mutex mMutex;
+  nsTArray<RefPtr<Listener>> mListeners;
+};
+
+template <typename... Es>
+using MediaEventSource =
+  MediaEventSourceImpl<ListenerPolicy::NonExclusive, Es...>;
+
+template <typename... Es>
+using MediaEventSourceExc =
+  MediaEventSourceImpl<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...> {
+public:
+  template <typename... Ts>
+  void Notify(Ts&&... aEvents) {
+    // Pass lvalues to prevent move in NonExclusive mode.
+    this->NotifyInternal(aEvents...);
+  }
+};
+
+/**
+ * Specialization for void type. A dummy bool is passed to NotifyInternal
+ * since there is no way to pass a void value.
+ */
+template <>
+class MediaEventProducer<void> : public MediaEventSource<void> {
+public:
+  void Notify() {
+    this->NotifyInternal(true /* dummy */);
+  }
+};
+
+/**
+ * A producer allowing at most one listener.
+ */
+template <typename... Es>
+class MediaEventProducerExc : public MediaEventSourceExc<Es...> {
+public:
+  template <typename... Ts>
+  void Notify(Ts&&... aEvents) {
+    this->NotifyInternal(Forward<Ts>(aEvents)...);
+  }
+};
+
+} // namespace mozilla
+
+#endif //MediaEventSource_h_
new file mode 100644
--- /dev/null
+++ b/obj-i686-pc-mingw32/dist/include/mozilla/MozPromise.h
@@ -0,0 +1,1580 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#if !defined(MozPromise_h_)
+#define MozPromise_h_
+
+#include "mozilla/Logging.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/Tuple.h"
+#include "mozilla/TypeTraits.h"
+#include "mozilla/Variant.h"
+
+#include "nsISerialEventTarget.h"
+#include "nsTArray.h"
+#include "nsThreadUtils.h"
+
+#if MOZ_DIAGNOSTIC_ASSERT_ENABLED
+#define PROMISE_DEBUG
+#endif
+
+#ifdef PROMISE_DEBUG
+#define PROMISE_ASSERT MOZ_RELEASE_ASSERT
+#else
+#define PROMISE_ASSERT(...) do { } while (0)
+#endif
+
+namespace mozilla {
+
+extern LazyLogModule gMozPromiseLog;
+
+#define PROMISE_LOG(x, ...) \
+  MOZ_LOG(gMozPromiseLog, mozilla::LogLevel::Debug, (x, ##__VA_ARGS__))
+
+namespace detail {
+template <typename F>
+struct MethodTraitsHelper : MethodTraitsHelper<decltype(&F::operator())>
+{
+};
+template <typename ThisType, typename Ret, typename... ArgTypes>
+struct MethodTraitsHelper<Ret(ThisType::*)(ArgTypes...)>
+{
+  using ReturnType = Ret;
+  static const size_t ArgSize = sizeof...(ArgTypes);
+};
+template <typename ThisType, typename Ret, typename... ArgTypes>
+struct MethodTraitsHelper<Ret(ThisType::*)(ArgTypes...) const>
+{
+  using ReturnType = Ret;
+  static const size_t ArgSize = sizeof...(ArgTypes);
+};
+template <typename ThisType, typename Ret, typename... ArgTypes>
+struct MethodTraitsHelper<Ret(ThisType::*)(ArgTypes...) volatile>
+{
+  using ReturnType = Ret;
+  static const size_t ArgSize = sizeof...(ArgTypes);
+};
+template <typename ThisType, typename Ret, typename... ArgTypes>
+struct MethodTraitsHelper<Ret(ThisType::*)(ArgTypes...) const volatile>
+{
+  using ReturnType = Ret;
+  static const size_t ArgSize = sizeof...(ArgTypes);
+};
+template <typename T>
+struct MethodTrait : MethodTraitsHelper<typename RemoveReference<T>::Type>
+{
+};
+
+} // namespace detail
+
+template<typename MethodType>
+using TakesArgument =
+  IntegralConstant<bool, detail::MethodTrait<MethodType>::ArgSize != 0>;
+
+template<typename MethodType, typename TargetType>
+using ReturnTypeIs =
+  IsConvertible<typename detail::MethodTrait<MethodType>::ReturnType, TargetType>;
+
+template<typename ResolveValueT, typename RejectValueT, bool IsExclusive>
+class MozPromise;
+
+template<typename Return>
+struct IsMozPromise : FalseType
+{
+};
+
+template<typename ResolveValueT, typename RejectValueT, bool IsExclusive>
+struct IsMozPromise<MozPromise<ResolveValueT, RejectValueT, IsExclusive>>
+  : TrueType
+{
+};
+
+/*
+ * A promise manages an asynchronous request that may or may not be able to be
+ * fulfilled immediately. When an API returns a promise, the consumer may attach
+ * callbacks to be invoked (asynchronously, on a specified thread) when the
+ * request is either completed (resolved) or cannot be completed (rejected).
+ * Whereas JS promise callbacks are dispatched from Microtask checkpoints,
+ * MozPromises resolution/rejection make a normal round-trip through the event
+ * loop, which simplifies their ordering semantics relative to other native code.
+ *
+ * MozPromises attempt to mirror the spirit of JS Promises to the extent that
+ * is possible (and desirable) in C++. While the intent is that MozPromises
+ * feel familiar to programmers who are accustomed to their JS-implemented cousin,
+ * we don't shy away from imposing restrictions and adding features that make
+ * sense for the use cases we encounter.
+ *
+ * A MozPromise is ThreadSafe, and may be ->Then()ed on any thread. The Then()
+ * call accepts resolve and reject callbacks, and returns a magic object which
+ * will be implicitly converted to a MozPromise::Request or a MozPromise object
+ * depending on how the return value is used. The magic object serves several
+ * purposes for the consumer.
+ *
+ *   (1) When converting to a MozPromise::Request, it allows the caller to
+ *       cancel the delivery of the resolve/reject value if it has not already
+ *       occurred, via Disconnect() (this must be done on the target thread to
+ *       avoid racing).
+ *
+ *   (2) When converting to a MozPromise (which is called a completion promise),
+ *       it allows promise chaining so ->Then() can be called again to attach
+ *       more resolve and reject callbacks. If the resolve/reject callback
+ *       returns a new MozPromise, that promise is chained to the completion
+ *       promise, such that its resolve/reject value will be forwarded along
+ *       when it arrives. If the resolve/reject callback returns void, the
+ *       completion promise is resolved/rejected with the same value that was
+ *       passed to the callback.
+ *
+ * The MozPromise APIs skirt traditional XPCOM convention by returning nsRefPtrs
+ * (rather than already_AddRefed) from various methods. This is done to allow elegant
+ * chaining of calls without cluttering up the code with intermediate variables, and
+ * without introducing separate API variants for callers that want a return value
+ * (from, say, ->Then()) from those that don't.
+ *
+ * When IsExclusive is true, the MozPromise does a release-mode assertion that
+ * there is at most one call to either Then(...) or ChainTo(...).
+ */
+
+class MozPromiseRefcountable
+{
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MozPromiseRefcountable)
+protected:
+  virtual ~MozPromiseRefcountable() {}
+};
+
+class MozPromiseBase : public MozPromiseRefcountable
+{
+public:
+  virtual void AssertIsDead() = 0;
+};
+
+template<typename T> class MozPromiseHolder;
+template<typename T> class MozPromiseRequestHolder;
+template<typename ResolveValueT, typename RejectValueT, bool IsExclusive>
+class MozPromise : public MozPromiseBase
+{
+  static const uint32_t sMagic = 0xcecace11;
+
+  // Return a |T&&| to enable move when IsExclusive is true or
+  // a |const T&| to enforce copy otherwise.
+  template <typename T,
+    typename R = typename Conditional<IsExclusive, T&&, const T&>::Type>
+  static R MaybeMove(T& aX)
+  {
+    return static_cast<R>(aX);
+  }
+
+public:
+  typedef ResolveValueT ResolveValueType;
+  typedef RejectValueT RejectValueType;
+  class ResolveOrRejectValue
+  {
+  public:
+    template<typename ResolveValueType_>
+    void SetResolve(ResolveValueType_&& aResolveValue)
+    {
+      MOZ_ASSERT(IsNothing());
+      mValue = Storage(VariantIndex<ResolveIndex>{},
+                       Forward<ResolveValueType_>(aResolveValue));
+    }
+
+    template<typename RejectValueType_>
+    void SetReject(RejectValueType_&& aRejectValue)
+    {
+      MOZ_ASSERT(IsNothing());
+      mValue = Storage(VariantIndex<RejectIndex>{},
+                       Forward<RejectValueType_>(aRejectValue));
+    }
+
+    template<typename ResolveValueType_>
+    static ResolveOrRejectValue MakeResolve(ResolveValueType_&& aResolveValue)
+    {
+      ResolveOrRejectValue val;
+      val.SetResolve(Forward<ResolveValueType_>(aResolveValue));
+      return val;
+    }
+
+    template<typename RejectValueType_>
+    static ResolveOrRejectValue MakeReject(RejectValueType_&& aRejectValue)
+    {
+      ResolveOrRejectValue val;
+      val.SetReject(Forward<RejectValueType_>(aRejectValue));
+      return val;
+    }
+
+    bool IsResolve() const { return mValue.template is<ResolveIndex>(); }
+    bool IsReject() const { return mValue.template is<RejectIndex>(); }
+    bool IsNothing() const { return mValue.template is<NothingIndex>(); }
+
+    const ResolveValueType& ResolveValue() const
+    {
+      return mValue.template as<ResolveIndex>();
+    }
+    ResolveValueType& ResolveValue()
+    {
+      return mValue.template as<ResolveIndex>();
+    }
+    const RejectValueType& RejectValue() const
+    {
+      return mValue.template as<RejectIndex>();
+    }
+    RejectValueType& RejectValue()
+    {
+      return mValue.template as<RejectIndex>();
+    }
+
+  private:
+    enum { NothingIndex, ResolveIndex, RejectIndex };
+    using Storage = Variant<Nothing, ResolveValueType, RejectValueType>;
+    Storage mValue = Storage(VariantIndex<NothingIndex>{});
+  };
+
+protected:
+  // MozPromise is the public type, and never constructed directly. Construct
+  // a MozPromise::Private, defined below.
+  MozPromise(const char* aCreationSite, bool aIsCompletionPromise)
+    : mCreationSite(aCreationSite)
+    , mMutex("MozPromise Mutex")
+    , mHaveRequest(false)
+    , mIsCompletionPromise(aIsCompletionPromise)
+#ifdef PROMISE_DEBUG
+    , mMagic4(&mMutex)
+#endif
+  {
+    PROMISE_LOG("%s creating MozPromise (%p)", mCreationSite, this);
+  }
+
+public:
+  // MozPromise::Private allows us to separate the public interface (upon which
+  // consumers of the promise may invoke methods like Then()) from the private
+  // interface (upon which the creator of the promise may invoke Resolve() or
+  // Reject()). APIs should create and store a MozPromise::Private (usually
+  // via a MozPromiseHolder), and return a MozPromise to consumers.
+  //
+  // NB: We can include the definition of this class inline once B2G ICS is gone.
+  class Private;
+
+  template<typename ResolveValueType_>
+  static RefPtr<MozPromise>
+  CreateAndResolve(ResolveValueType_&& aResolveValue, const char* aResolveSite)
+  {
+    RefPtr<typename MozPromise::Private> p = new MozPromise::Private(aResolveSite);
+    p->Resolve(Forward<ResolveValueType_>(aResolveValue), aResolveSite);
+    return p.forget();
+  }
+
+  template<typename RejectValueType_>
+  static RefPtr<MozPromise>
+  CreateAndReject(RejectValueType_&& aRejectValue, const char* aRejectSite)
+  {
+    RefPtr<typename MozPromise::Private> p = new MozPromise::Private(aRejectSite);
+    p->Reject(Forward<RejectValueType_>(aRejectValue), aRejectSite);
+    return p.forget();
+  }
+
+  typedef MozPromise<nsTArray<ResolveValueType>, RejectValueType, IsExclusive> AllPromiseType;
+private:
+  class AllPromiseHolder : public MozPromiseRefcountable
+  {
+  public:
+    explicit AllPromiseHolder(size_t aDependentPromises)
+      : mPromise(new typename AllPromiseType::Private(__func__))
+      , mOutstandingPromises(aDependentPromises)
+    {
+      MOZ_ASSERT(aDependentPromises > 0);
+      mResolveValues.SetLength(aDependentPromises);
+    }
+
+    void Resolve(size_t aIndex, ResolveValueType&& aResolveValue)
+    {
+      if (!mPromise) {
+        // Already rejected.
+        return;
+      }
+
+      mResolveValues[aIndex].emplace(Move(aResolveValue));
+      if (--mOutstandingPromises == 0) {
+        nsTArray<ResolveValueType> resolveValues;
+        resolveValues.SetCapacity(mResolveValues.Length());
+        for (auto&& resolveValue : mResolveValues) {
+          resolveValues.AppendElement(Move(resolveValue.ref()));
+        }
+
+        mPromise->Resolve(Move(resolveValues), __func__);
+        mPromise = nullptr;
+        mResolveValues.Clear();
+      }
+    }
+
+    void Reject(RejectValueType&& aRejectValue)
+    {
+      if (!mPromise) {
+        // Already rejected.
+        return;
+      }
+
+      mPromise->Reject(Move(aRejectValue), __func__);
+      mPromise = nullptr;
+      mResolveValues.Clear();
+    }
+
+    AllPromiseType* Promise() { return mPromise; }
+
+  private:
+    nsTArray<Maybe<ResolveValueType>> mResolveValues;
+    RefPtr<typename AllPromiseType::Private> mPromise;
+    size_t mOutstandingPromises;
+  };
+public:
+
+  static RefPtr<AllPromiseType> All(nsISerialEventTarget* aProcessingTarget, nsTArray<RefPtr<MozPromise>>& aPromises)
+  {
+    if (aPromises.Length() == 0) {
+      return AllPromiseType::CreateAndResolve(nsTArray<ResolveValueType>(), __func__);
+    }
+
+    RefPtr<AllPromiseHolder> holder = new AllPromiseHolder(aPromises.Length());
+    RefPtr<AllPromiseType> promise = holder->Promise();
+    for (size_t i = 0; i < aPromises.Length(); ++i) {
+      aPromises[i]->Then(aProcessingTarget, __func__,
+        [holder, i] (ResolveValueType aResolveValue) -> void { holder->Resolve(i, Move(aResolveValue)); },
+        [holder] (RejectValueType aRejectValue) -> void { holder->Reject(Move(aRejectValue)); }
+      );
+    }
+    return promise;
+  }
+
+  class Request : public MozPromiseRefcountable
+  {
+  public:
+    virtual void Disconnect() = 0;
+
+  protected:
+    Request() : mComplete(false), mDisconnected(false) {}
+    virtual ~Request() {}
+
+    bool mComplete;
+    bool mDisconnected;
+  };
+
+protected:
+
+  /*
+   * A ThenValue tracks a single consumer waiting on the promise. When a consumer
+   * invokes promise->Then(...), a ThenValue is created. Once the Promise is
+   * resolved or rejected, a {Resolve,Reject}Runnable is dispatched, which
+   * invokes the resolve/reject method and then deletes the ThenValue.
+   */
+  class ThenValueBase : public Request
+  {
+    friend class MozPromise;
+    static const uint32_t sMagic = 0xfadece11;
+
+  public:
+    class ResolveOrRejectRunnable : public CancelableRunnable
+    {
+    public:
+      ResolveOrRejectRunnable(ThenValueBase* aThenValue, MozPromise* aPromise)
+        : CancelableRunnable(
+            "MozPromise::ThenValueBase::ResolveOrRejectRunnable")
+        , mThenValue(aThenValue)
+        , mPromise(aPromise)
+      {
+        MOZ_DIAGNOSTIC_ASSERT(!mPromise->IsPending());
+      }
+
+      ~ResolveOrRejectRunnable()
+      {
+        if (mThenValue) {
+          mThenValue->AssertIsDead();
+        }
+      }
+
+      NS_IMETHOD Run() override
+      {
+        PROMISE_LOG("ResolveOrRejectRunnable::Run() [this=%p]", this);
+        mThenValue->DoResolveOrReject(mPromise->Value());
+        mThenValue = nullptr;
+        mPromise = nullptr;
+        return NS_OK;
+      }
+
+      nsresult Cancel() override
+      {
+        return Run();
+      }
+
+    private:
+      RefPtr<ThenValueBase> mThenValue;
+      RefPtr<MozPromise> mPromise;
+    };
+
+    ThenValueBase(nsISerialEventTarget* aResponseTarget,
+                  const char* aCallSite)
+      : mResponseTarget(aResponseTarget)
+      , mCallSite(aCallSite)
+    {
+      MOZ_ASSERT(aResponseTarget);
+    }
+
+#ifdef PROMISE_DEBUG
+    ~ThenValueBase()
+    {
+      mMagic1 = 0;
+      mMagic2 = 0;
+    }
+#endif
+
+    void AssertIsDead()
+    {
+      PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic);
+      // We want to assert that this ThenValues is dead - that is to say, that
+      // there are no consumers waiting for the result. In the case of a normal
+      // ThenValue, we check that it has been disconnected, which is the way
+      // that the consumer signals that it no longer wishes to hear about the
+      // result. If this ThenValue has a completion promise (which is mutually
+      // exclusive with being disconnectable), we recursively assert that every
+      // ThenValue associated with the completion promise is dead.
+      if (MozPromiseBase* p = CompletionPromise()) {
+        p->AssertIsDead();
+      } else {
+        MOZ_DIAGNOSTIC_ASSERT(Request::mDisconnected);
+      }
+    }
+
+    void Dispatch(MozPromise *aPromise)
+    {
+      PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic);
+      aPromise->mMutex.AssertCurrentThreadOwns();
+      MOZ_ASSERT(!aPromise->IsPending());
+
+      nsCOMPtr<nsIRunnable> r = new ResolveOrRejectRunnable(this, aPromise);
+      PROMISE_LOG("%s Then() call made from %s [Runnable=%p, Promise=%p, ThenValue=%p]",
+                  aPromise->mValue.IsResolve() ? "Resolving" : "Rejecting", mCallSite,
+                  r.get(), aPromise, this);
+
+      // Promise consumers are allowed to disconnect the Request object and
+      // then shut down the thread or task queue that the promise result would
+      // be dispatched on. So we unfortunately can't assert that promise
+      // dispatch succeeds. :-(
+      mResponseTarget->Dispatch(r.forget());
+    }
+
+    void Disconnect() override
+    {
+      MOZ_DIAGNOSTIC_ASSERT(mResponseTarget->IsOnCurrentThread());
+      MOZ_DIAGNOSTIC_ASSERT(!Request::mComplete);
+      Request::mDisconnected = true;
+
+      // We could support rejecting the completion promise on disconnection, but
+      // then we'd need to have some sort of default reject value. The use cases
+      // of disconnection and completion promise chaining seem pretty orthogonal,
+      // so let's use assert against it.
+      MOZ_DIAGNOSTIC_ASSERT(!CompletionPromise());
+    }
+
+  protected:
+    virtual MozPromiseBase* CompletionPromise() const = 0;
+    virtual void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) = 0;
+
+    void DoResolveOrReject(ResolveOrRejectValue& aValue)
+    {
+      PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic);
+      MOZ_DIAGNOSTIC_ASSERT(mResponseTarget->IsOnCurrentThread());
+      Request::mComplete = true;
+      if (Request::mDisconnected) {
+        PROMISE_LOG("ThenValue::DoResolveOrReject disconnected - bailing out [this=%p]", this);
+        return;
+      }
+
+      // Invoke the resolve or reject method.
+      DoResolveOrRejectInternal(aValue);
+    }
+
+    nsCOMPtr<nsISerialEventTarget> mResponseTarget; // May be released on any thread.
+#ifdef PROMISE_DEBUG
+    uint32_t mMagic1 = sMagic;
+#endif
+    const char* mCallSite;
+#ifdef PROMISE_DEBUG
+    uint32_t mMagic2 = sMagic;
+#endif
+  };
+
+  /*
+   * We create two overloads for invoking Resolve/Reject Methods so as to
+   * make the resolve/reject value argument "optional".
+   */
+  template<typename ThisType, typename MethodType, typename ValueType>
+  static typename EnableIf<
+    TakesArgument<MethodType>::value,
+    typename detail::MethodTrait<MethodType>::ReturnType>::Type
+  InvokeMethod(ThisType* aThisVal, MethodType aMethod, ValueType&& aValue)
+  {
+    return (aThisVal->*aMethod)(Forward<ValueType>(aValue));
+  }
+
+  template<typename ThisType, typename MethodType, typename ValueType>
+  static typename EnableIf<
+    !TakesArgument<MethodType>::value,
+    typename detail::MethodTrait<MethodType>::ReturnType>::Type
+  InvokeMethod(ThisType* aThisVal, MethodType aMethod, ValueType&& aValue)
+  {
+    return (aThisVal->*aMethod)();
+  }
+
+  // Called when promise chaining is supported.
+  template<bool SupportChaining,
+           typename ThisType,
+           typename MethodType,
+           typename ValueType,
+           typename CompletionPromiseType>
+  static typename EnableIf<SupportChaining, void>::Type InvokeCallbackMethod(
+    ThisType* aThisVal,
+    MethodType aMethod,
+    ValueType&& aValue,
+    CompletionPromiseType&& aCompletionPromise)
+  {
+    auto p = InvokeMethod(aThisVal, aMethod, Forward<ValueType>(aValue));
+    if (aCompletionPromise) {
+      p->ChainTo(aCompletionPromise.forget(), "<chained completion promise>");
+    }
+  }
+
+  // Called when promise chaining is not supported.
+  template<bool SupportChaining,
+           typename ThisType,
+           typename MethodType,
+           typename ValueType,
+           typename CompletionPromiseType>
+  static typename EnableIf<!SupportChaining, void>::Type InvokeCallbackMethod(
+    ThisType* aThisVal,
+    MethodType aMethod,
+    ValueType&& aValue,
+    CompletionPromiseType&& aCompletionPromise)
+  {
+    MOZ_DIAGNOSTIC_ASSERT(
+      !aCompletionPromise,
+      "Can't do promise chaining for a non-promise-returning method.");
+    InvokeMethod(aThisVal, aMethod, Forward<ValueType>(aValue));
+  }
+
+  template<typename>
+  class ThenCommand;
+
+  template<typename...>
+  class ThenValue;
+
+  template<typename ThisType,
+           typename ResolveMethodType,
+           typename RejectMethodType>
+  class ThenValue<ThisType*, ResolveMethodType, RejectMethodType>
+    : public ThenValueBase
+  {
+    friend class ThenCommand<ThenValue>;
+
+    using R1 = typename RemoveSmartPointer<
+      typename detail::MethodTrait<ResolveMethodType>::ReturnType>::Type;
+    using R2 = typename RemoveSmartPointer<
+      typename detail::MethodTrait<RejectMethodType>::ReturnType>::Type;
+    using SupportChaining =
+      IntegralConstant<bool, IsMozPromise<R1>::value && IsSame<R1, R2>::value>;
+
+    // Fall back to MozPromise when promise chaining is not supported to make code compile.
+    using PromiseType =
+      typename Conditional<SupportChaining::value, R1, MozPromise>::Type;
+
+  public:
+    ThenValue(nsISerialEventTarget* aResponseTarget,
+              ThisType* aThisVal,
+              ResolveMethodType aResolveMethod,
+              RejectMethodType aRejectMethod,
+              const char* aCallSite)
+      : ThenValueBase(aResponseTarget, aCallSite)
+      , mThisVal(aThisVal)
+      , mResolveMethod(aResolveMethod)
+      , mRejectMethod(aRejectMethod)
+    {
+    }
+
+    void Disconnect() override
+    {
+      ThenValueBase::Disconnect();
+
+      // If a Request has been disconnected, we don't guarantee that the
+      // resolve/reject runnable will be dispatched. Null out our refcounted
+      // this-value now so that it's released predictably on the dispatch thread.
+      mThisVal = nullptr;
+    }
+
+  protected:
+    MozPromiseBase* CompletionPromise() const override
+    {
+      return mCompletionPromise;
+    }
+
+    void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) override
+    {
+      if (aValue.IsResolve()) {
+        InvokeCallbackMethod<SupportChaining::value>(
+          mThisVal.get(),
+          mResolveMethod,
+          MaybeMove(aValue.ResolveValue()),
+          Move(mCompletionPromise));
+      } else {
+        InvokeCallbackMethod<SupportChaining::value>(
+          mThisVal.get(),
+          mRejectMethod,
+          MaybeMove(aValue.RejectValue()),
+          Move(mCompletionPromise));
+      }
+
+      // Null out mThisVal after invoking the callback so that any references are
+      // released predictably on the dispatch thread. Otherwise, it would be
+      // released on whatever thread last drops its reference to the ThenValue,
+      // which may or may not be ok.
+      mThisVal = nullptr;
+    }
+
+  private:
+    RefPtr<ThisType> mThisVal; // Only accessed and refcounted on dispatch thread.
+    ResolveMethodType mResolveMethod;
+    RejectMethodType mRejectMethod;
+    RefPtr<typename PromiseType::Private> mCompletionPromise;
+  };
+
+  template<typename ThisType, typename ResolveRejectMethodType>
+  class ThenValue<ThisType*, ResolveRejectMethodType> : public ThenValueBase
+  {
+    friend class ThenCommand<ThenValue>;
+
+    using R1 = typename RemoveSmartPointer<
+      typename detail::MethodTrait<ResolveRejectMethodType>::ReturnType>::Type;
+    using SupportChaining = IntegralConstant<bool, IsMozPromise<R1>::value>;
+
+    // Fall back to MozPromise when promise chaining is not supported to make code compile.
+    using PromiseType =
+      typename Conditional<SupportChaining::value, R1, MozPromise>::Type;
+
+  public:
+    ThenValue(nsISerialEventTarget* aResponseTarget,
+              ThisType* aThisVal,
+              ResolveRejectMethodType aResolveRejectMethod,
+              const char* aCallSite)
+      : ThenValueBase(aResponseTarget, aCallSite)
+      , mThisVal(aThisVal)
+      , mResolveRejectMethod(aResolveRejectMethod)
+    {}
+
+    void Disconnect() override
+    {
+      ThenValueBase::Disconnect();
+
+      // If a Request has been disconnected, we don't guarantee that the
+      // resolve/reject runnable will be dispatched. Null out our refcounted
+      // this-value now so that it's released predictably on the dispatch thread.
+      mThisVal = nullptr;
+    }
+
+  protected:
+    MozPromiseBase* CompletionPromise() const override
+    {
+      return mCompletionPromise;
+    }
+
+    void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) override
+    {
+      InvokeCallbackMethod<SupportChaining::value>(mThisVal.get(),
+                                                   mResolveRejectMethod,
+                                                   MaybeMove(aValue),
+                                                   Move(mCompletionPromise));
+
+      // Null out mThisVal after invoking the callback so that any references are
+      // released predictably on the dispatch thread. Otherwise, it would be
+      // released on whatever thread last drops its reference to the ThenValue,
+      // which may or may not be ok.
+      mThisVal = nullptr;
+    }
+
+  private:
+    RefPtr<ThisType> mThisVal; // Only accessed and refcounted on dispatch thread.
+    ResolveRejectMethodType mResolveRejectMethod;
+    RefPtr<typename PromiseType::Private> mCompletionPromise;
+  };
+
+  // NB: We could use std::function here instead of a template if it were supported. :-(
+  template<typename ResolveFunction, typename RejectFunction>
+  class ThenValue<ResolveFunction, RejectFunction> : public ThenValueBase
+  {
+    friend class ThenCommand<ThenValue>;
+
+    using R1 = typename RemoveSmartPointer<
+      typename detail::MethodTrait<ResolveFunction>::ReturnType>::Type;
+    using R2 = typename RemoveSmartPointer<
+      typename detail::MethodTrait<RejectFunction>::ReturnType>::Type;
+    using SupportChaining =
+      IntegralConstant<bool, IsMozPromise<R1>::value && IsSame<R1, R2>::value>;
+
+    // Fall back to MozPromise when promise chaining is not supported to make code compile.
+    using PromiseType =
+      typename Conditional<SupportChaining::value, R1, MozPromise>::Type;
+
+  public:
+    ThenValue(nsISerialEventTarget* aResponseTarget,
+              ResolveFunction&& aResolveFunction,
+              RejectFunction&& aRejectFunction,
+              const char* aCallSite)
+      : ThenValueBase(aResponseTarget, aCallSite)
+    {
+      mResolveFunction.emplace(Move(aResolveFunction));
+      mRejectFunction.emplace(Move(aRejectFunction));
+    }
+
+    void Disconnect() override
+    {
+      ThenValueBase::Disconnect();
+
+      // If a Request has been disconnected, we don't guarantee that the
+      // resolve/reject runnable will be dispatched. Destroy our callbacks
+      // now so that any references in closures are released predictable on
+      // the dispatch thread.
+      mResolveFunction.reset();
+      mRejectFunction.reset();
+    }
+
+  protected:
+    MozPromiseBase* CompletionPromise() const override
+    {
+      return mCompletionPromise;
+    }
+
+    void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) override
+    {
+      // Note: The usage of InvokeCallbackMethod here requires that
+      // ResolveFunction/RejectFunction are capture-lambdas (i.e. anonymous
+      // classes with ::operator()), since it allows us to share code more easily.
+      // We could fix this if need be, though it's quite easy to work around by
+      // just capturing something.
+      if (aValue.IsResolve()) {
+        InvokeCallbackMethod<SupportChaining::value>(
+          mResolveFunction.ptr(),
+          &ResolveFunction::operator(),
+          MaybeMove(aValue.ResolveValue()),
+          Move(mCompletionPromise));
+      } else {
+        InvokeCallbackMethod<SupportChaining::value>(
+          mRejectFunction.ptr(),
+          &RejectFunction::operator(),
+          MaybeMove(aValue.RejectValue()),
+          Move(mCompletionPromise));
+      }
+
+      // Destroy callbacks after invocation so that any references in closures are
+      // released predictably on the dispatch thread. Otherwise, they would be
+      // released on whatever thread last drops its reference to the ThenValue,
+      // which may or may not be ok.
+      mResolveFunction.reset();
+      mRejectFunction.reset();
+    }
+
+  private:
+    Maybe<ResolveFunction> mResolveFunction; // Only accessed and deleted on dispatch thread.
+    Maybe<RejectFunction> mRejectFunction; // Only accessed and deleted on dispatch thread.
+    RefPtr<typename PromiseType::Private> mCompletionPromise;
+  };
+
+  template<typename ResolveRejectFunction>
+  class ThenValue<ResolveRejectFunction> : public ThenValueBase
+  {
+    friend class ThenCommand<ThenValue>;
+
+    using R1 = typename RemoveSmartPointer<
+      typename detail::MethodTrait<ResolveRejectFunction>::ReturnType>::Type;
+    using SupportChaining = IntegralConstant<bool, IsMozPromise<R1>::value>;
+
+    // Fall back to MozPromise when promise chaining is not supported to make code compile.
+    using PromiseType =
+      typename Conditional<SupportChaining::value, R1, MozPromise>::Type;
+
+  public:
+    ThenValue(nsISerialEventTarget* aResponseTarget,
+              ResolveRejectFunction&& aResolveRejectFunction,
+              const char* aCallSite)
+      : ThenValueBase(aResponseTarget, aCallSite)
+    {
+      mResolveRejectFunction.emplace(Move(aResolveRejectFunction));
+    }
+
+    void Disconnect() override
+    {
+      ThenValueBase::Disconnect();
+
+      // If a Request has been disconnected, we don't guarantee that the
+      // resolve/reject runnable will be dispatched. Destroy our callbacks
+      // now so that any references in closures are released predictable on
+      // the dispatch thread.
+      mResolveRejectFunction.reset();
+    }
+
+  protected:
+    MozPromiseBase* CompletionPromise() const override
+    {
+      return mCompletionPromise;
+    }
+
+    void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) override
+    {
+      // Note: The usage of InvokeCallbackMethod here requires that
+      // ResolveRejectFunction is capture-lambdas (i.e. anonymous
+      // classes with ::operator()), since it allows us to share code more easily.
+      // We could fix this if need be, though it's quite easy to work around by
+      // just capturing something.
+      InvokeCallbackMethod<SupportChaining::value>(
+        mResolveRejectFunction.ptr(),
+        &ResolveRejectFunction::operator(),
+        MaybeMove(aValue),
+        Move(mCompletionPromise));
+
+      // Destroy callbacks after invocation so that any references in closures are
+      // released predictably on the dispatch thread. Otherwise, they would be
+      // released on whatever thread last drops its reference to the ThenValue,
+      // which may or may not be ok.
+      mResolveRejectFunction.reset();
+    }
+
+  private:
+    Maybe<ResolveRejectFunction> mResolveRejectFunction; // Only accessed and deleted on dispatch thread.
+    RefPtr<typename PromiseType::Private> mCompletionPromise;
+  };
+
+public:
+  void ThenInternal(already_AddRefed<ThenValueBase> aThenValue,
+                    const char* aCallSite)
+  {
+    PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic && mMagic3 == sMagic && mMagic4 == &mMutex);
+    RefPtr<ThenValueBase> thenValue = aThenValue;
+    MutexAutoLock lock(mMutex);
+    MOZ_DIAGNOSTIC_ASSERT(!IsExclusive || !mHaveRequest);
+    mHaveRequest = true;
+    PROMISE_LOG("%s invoking Then() [this=%p, aThenValue=%p, isPending=%d]",
+                aCallSite,
+                this,
+                thenValue.get(),
+                (int)IsPending());
+    if (!IsPending()) {
+      thenValue->Dispatch(this);
+    } else {
+      mThenValues.AppendElement(thenValue.forget());
+    }
+  }
+
+protected:
+  /*
+   * A command object to store all information needed to make a request to
+   * the promise. This allows us to delay the request until further use is
+   * known (whether it is ->Then() again for more promise chaining or ->Track()
+   * to terminate chaining and issue the request).
+   *
+   * This allows a unified syntax for promise chaining and disconnection
+   * and feels more like its JS counterpart.
+   */
+  template<typename ThenValueType>
+  class ThenCommand
+  {
+    // Allow Promise1::ThenCommand to access the private constructor,
+    // Promise2::ThenCommand(ThenCommand&&).
+    template<typename, typename, bool>
+    friend class MozPromise;
+
+    using PromiseType = typename ThenValueType::PromiseType;
+    using Private = typename PromiseType::Private;
+
+    ThenCommand(const char* aCallSite,
+                already_AddRefed<ThenValueType> aThenValue,
+                MozPromise* aReceiver)
+      : mCallSite(aCallSite)
+      , mThenValue(aThenValue)
+      , mReceiver(aReceiver)
+    {
+    }
+
+    ThenCommand(ThenCommand&& aOther) = default;
+
+  public:
+    ~ThenCommand()
+    {
+      // Issue the request now if the return value of Then() is not used.
+      if (mThenValue) {
+        mReceiver->ThenInternal(mThenValue.forget(), mCallSite);
+      }
+    }
+
+    // Allow RefPtr<MozPromise> p = somePromise->Then();
+    //       p->Then(thread1, ...);
+    //       p->Then(thread2, ...);
+    operator RefPtr<PromiseType>()
+    {
+      static_assert(
+        ThenValueType::SupportChaining::value,
+        "The resolve/reject callback needs to return a RefPtr<MozPromise> "
+        "in order to do promise chaining.");
+
+      // mCompletionPromise must be created before ThenInternal() to avoid race.
+      RefPtr<Private> p =
+        new Private("<completion promise>", true /* aIsCompletionPromise */);
+      mThenValue->mCompletionPromise = p;
+      // Note ThenInternal() might nullify mCompletionPromise before return.
+      // So we need to return p instead of mCompletionPromise.
+      mReceiver->ThenInternal(mThenValue.forget(), mCallSite);
+      return p;
+    }
+
+    template<typename... Ts>
+    auto Then(Ts&&... aArgs)
+      -> decltype(DeclVal<PromiseType>().Then(Forward<Ts>(aArgs)...))
+    {
+      return static_cast<RefPtr<PromiseType>>(*this)->Then(
+        Forward<Ts>(aArgs)...);
+    }
+
+    void Track(MozPromiseRequestHolder<MozPromise>& aRequestHolder)
+    {
+      aRequestHolder.Track(do_AddRef(mThenValue));
+      mReceiver->ThenInternal(mThenValue.forget(), mCallSite);
+    }
+
+    // Allow calling ->Then() again for more promise chaining or ->Track() to
+    // end chaining and track the request for future disconnection.
+    ThenCommand* operator->()
+    {
+      return this;
+    }
+
+  private:
+    const char* mCallSite;
+    RefPtr<ThenValueType> mThenValue;
+    RefPtr<MozPromise> mReceiver;
+  };
+
+public:
+  template<typename ThisType,
+           typename... Methods,
+           typename ThenValueType = ThenValue<ThisType*, Methods...>,
+           typename ReturnType = ThenCommand<ThenValueType>>
+  ReturnType Then(nsISerialEventTarget* aResponseTarget,
+                  const char* aCallSite,
+                  ThisType* aThisVal,
+                  Methods... aMethods)
+  {
+    RefPtr<ThenValueType> thenValue =
+      new ThenValueType(aResponseTarget, aThisVal, aMethods..., aCallSite);
+    return ReturnType(aCallSite, thenValue.forget(), this);
+  }
+
+  template<typename... Functions,
+           typename ThenValueType = ThenValue<Functions...>,
+           typename ReturnType = ThenCommand<ThenValueType>>
+  ReturnType Then(nsISerialEventTarget* aResponseTarget,
+                  const char* aCallSite,
+                  Functions&&... aFunctions)
+  {
+    RefPtr<ThenValueType> thenValue =
+      new ThenValueType(aResponseTarget, Move(aFunctions)..., aCallSite);
+    return ReturnType(aCallSite, thenValue.forget(), this);
+  }
+
+  void ChainTo(already_AddRefed<Private> aChainedPromise, const char* aCallSite)
+  {
+    MutexAutoLock lock(mMutex);
+    MOZ_DIAGNOSTIC_ASSERT(!IsExclusive || !mHaveRequest);
+    mHaveRequest = true;
+    RefPtr<Private> chainedPromise = aChainedPromise;
+    PROMISE_LOG("%s invoking Chain() [this=%p, chainedPromise=%p, isPending=%d]",
+                aCallSite, this, chainedPromise.get(), (int) IsPending());
+    if (!IsPending()) {
+      ForwardTo(chainedPromise);
+    } else {
+      mChainedPromises.AppendElement(chainedPromise);
+    }
+  }
+
+  // Note we expose the function AssertIsDead() instead of IsDead() since
+  // checking IsDead() is a data race in the situation where the request is not
+  // dead. Therefore we enforce the form |Assert(IsDead())| by exposing
+  // AssertIsDead() only.
+  void AssertIsDead() override
+  {
+    PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic && mMagic3 == sMagic && mMagic4 == &mMutex);
+    MutexAutoLock lock(mMutex);
+    for (auto&& then : mThenValues) {
+      then->AssertIsDead();
+    }
+    for (auto&& chained : mChainedPromises) {
+      chained->AssertIsDead();
+    }
+  }
+
+protected:
+  bool IsPending() const { return mValue.IsNothing(); }
+
+  ResolveOrRejectValue& Value()
+  {
+    // This method should only be called once the value has stabilized. As
+    // such, we don't need to acquire the lock here.
+    MOZ_DIAGNOSTIC_ASSERT(!IsPending());
+    return mValue;
+  }
+
+  void DispatchAll()
+  {
+    mMutex.AssertCurrentThreadOwns();
+    for (auto&& thenValue : mThenValues) {
+      thenValue->Dispatch(this);
+    }
+    mThenValues.Clear();
+
+    for (auto&& chainedPromise : mChainedPromises) {
+      ForwardTo(chainedPromise);
+    }
+    mChainedPromises.Clear();
+  }
+
+  void ForwardTo(Private* aOther)
+  {
+    MOZ_ASSERT(!IsPending());
+    if (mValue.IsResolve()) {
+      aOther->Resolve(MaybeMove(mValue.ResolveValue()), "<chained promise>");
+    } else {
+      aOther->Reject(MaybeMove(mValue.RejectValue()), "<chained promise>");
+    }
+  }
+
+  virtual ~MozPromise()
+  {
+    PROMISE_LOG("MozPromise::~MozPromise [this=%p]", this);
+    AssertIsDead();
+    // We can't guarantee a completion promise will always be revolved or
+    // rejected since ResolveOrRejectRunnable might not run when dispatch fails.
+    if (!mIsCompletionPromise) {
+      MOZ_ASSERT(!IsPending());
+      MOZ_ASSERT(mThenValues.IsEmpty());
+      MOZ_ASSERT(mChainedPromises.IsEmpty());
+    }
+#ifdef PROMISE_DEBUG
+    mMagic1 = 0;
+    mMagic2 = 0;
+    mMagic3 = 0;
+    mMagic4 = nullptr;
+#endif
+  };
+
+  const char* mCreationSite; // For logging
+  Mutex mMutex;
+  ResolveOrRejectValue mValue;
+#ifdef PROMISE_DEBUG
+  uint32_t mMagic1 = sMagic;
+#endif
+  // Try shows we never have more than 3 elements when IsExclusive is false.
+  // So '3' is a good value to avoid heap allocation in most cases.
+  AutoTArray<RefPtr<ThenValueBase>, IsExclusive ? 1 : 3> mThenValues;
+#ifdef PROMISE_DEBUG
+  uint32_t mMagic2 = sMagic;
+#endif
+  nsTArray<RefPtr<Private>> mChainedPromises;
+#ifdef PROMISE_DEBUG
+  uint32_t mMagic3 = sMagic;
+#endif
+  bool mHaveRequest;
+  const bool mIsCompletionPromise;
+#ifdef PROMISE_DEBUG
+  void* mMagic4;
+#endif
+};
+
+template<typename ResolveValueT, typename RejectValueT, bool IsExclusive>
+class MozPromise<ResolveValueT, RejectValueT, IsExclusive>::Private
+  : public MozPromise<ResolveValueT, RejectValueT, IsExclusive>
+{
+public:
+  explicit Private(const char* aCreationSite, bool aIsCompletionPromise = false)
+    : MozPromise(aCreationSite, aIsCompletionPromise) {}
+
+  template<typename ResolveValueT_>
+  void Resolve(ResolveValueT_&& aResolveValue, const char* aResolveSite)
+  {
+    PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic && mMagic3 == sMagic && mMagic4 == &mMutex);
+    MutexAutoLock lock(mMutex);
+    PROMISE_LOG("%s resolving MozPromise (%p created at %s)", aResolveSite, this, mCreationSite);
+    if (!IsPending()) {
+      PROMISE_LOG("%s ignored already resolved or rejected MozPromise (%p created at %s)", aResolveSite, this, mCreationSite);
+      return;
+    }
+    mValue.SetResolve(Forward<ResolveValueT_>(aResolveValue));
+    DispatchAll();
+  }
+
+  template<typename RejectValueT_>
+  void Reject(RejectValueT_&& aRejectValue, const char* aRejectSite)
+  {
+    PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic && mMagic3 == sMagic && mMagic4 == &mMutex);
+    MutexAutoLock lock(mMutex);
+    PROMISE_LOG("%s rejecting MozPromise (%p created at %s)", aRejectSite, this, mCreationSite);
+    if (!IsPending()) {
+      PROMISE_LOG("%s ignored already resolved or rejected MozPromise (%p created at %s)", aRejectSite, this, mCreationSite);
+      return;
+    }
+    mValue.SetReject(Forward<RejectValueT_>(aRejectValue));
+    DispatchAll();
+  }
+
+  template<typename ResolveOrRejectValue_>
+  void ResolveOrReject(ResolveOrRejectValue_&& aValue, const char* aSite)
+  {
+    PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic && mMagic3 == sMagic && mMagic4 == &mMutex);
+    MutexAutoLock lock(mMutex);
+    PROMISE_LOG("%s resolveOrRejecting MozPromise (%p created at %s)", aSite, this, mCreationSite);
+    if (!IsPending()) {
+      PROMISE_LOG("%s ignored already resolved or rejected MozPromise (%p created at %s)", aSite, this, mCreationSite);
+      return;
+    }
+    mValue = Forward<ResolveOrRejectValue_>(aValue);
+    DispatchAll();
+  }
+};
+
+// A generic promise type that does the trick for simple use cases.
+typedef MozPromise<bool, nsresult, /* IsExclusive = */ false> GenericPromise;
+
+/*
+ * Class to encapsulate a promise for a particular role. Use this as the member
+ * variable for a class whose method returns a promise.
+ */
+template<typename PromiseType>
+class MozPromiseHolder
+{
+public:
+  MozPromiseHolder()
+    : mMonitor(nullptr) {}
+
+  MozPromiseHolder(MozPromiseHolder&& aOther)
+    : mMonitor(nullptr), mPromise(aOther.mPromise.forget()) {}
+
+  // Move semantics.
+  MozPromiseHolder& operator=(MozPromiseHolder&& aOther)
+  {
+    MOZ_ASSERT(!mMonitor && !aOther.mMonitor);
+    MOZ_DIAGNOSTIC_ASSERT(!mPromise);
+    mPromise = aOther.mPromise;
+    aOther.mPromise = nullptr;
+    return *this;
+  }
+
+  ~MozPromiseHolder() { MOZ_ASSERT(!mPromise); }
+
+  already_AddRefed<PromiseType> Ensure(const char* aMethodName) {
+    if (mMonitor) {
+      mMonitor->AssertCurrentThreadOwns();
+    }
+    if (!mPromise) {
+      mPromise = new (typename PromiseType::Private)(aMethodName);
+    }
+    RefPtr<PromiseType> p = mPromise.get();
+    return p.forget();
+  }
+
+  // Provide a Monitor that should always be held when accessing this instance.
+  void SetMonitor(Monitor* aMonitor) { mMonitor = aMonitor; }
+
+  bool IsEmpty() const
+  {
+    if (mMonitor) {
+      mMonitor->AssertCurrentThreadOwns();
+    }
+    return !mPromise;
+  }
+
+  already_AddRefed<typename PromiseType::Private> Steal()
+  {
+    if (mMonitor) {
+      mMonitor->AssertCurrentThreadOwns();
+    }
+    return mPromise.forget();
+  }
+
+  void Resolve(const typename PromiseType::ResolveValueType& aResolveValue,
+               const char* aMethodName)
+  {
+    if (mMonitor) {
+      mMonitor->AssertCurrentThreadOwns();
+    }
+    MOZ_ASSERT(mPromise);
+    mPromise->Resolve(aResolveValue, aMethodName);
+    mPromise = nullptr;
+  }
+  void Resolve(typename PromiseType::ResolveValueType&& aResolveValue,
+               const char* aMethodName)
+  {
+    if (mMonitor) {
+      mMonitor->AssertCurrentThreadOwns();
+    }
+    MOZ_ASSERT(mPromise);
+    mPromise->Resolve(Move(aResolveValue), aMethodName);
+    mPromise = nullptr;
+  }
+
+  void ResolveIfExists(const typename PromiseType::ResolveValueType& aResolveValue,
+                       const char* aMethodName)
+  {
+    if (!IsEmpty()) {
+      Resolve(aResolveValue, aMethodName);
+    }
+  }
+  void ResolveIfExists(typename PromiseType::ResolveValueType&& aResolveValue,
+                       const char* aMethodName)
+  {
+    if (!IsEmpty()) {
+      Resolve(Move(aResolveValue), aMethodName);
+    }
+  }
+
+  void Reject(const typename PromiseType::RejectValueType& aRejectValue,
+              const char* aMethodName)
+  {
+    if (mMonitor) {
+      mMonitor->AssertCurrentThreadOwns();
+    }
+    MOZ_ASSERT(mPromise);
+    mPromise->Reject(aRejectValue, aMethodName);
+    mPromise = nullptr;
+  }
+  void Reject(typename PromiseType::RejectValueType&& aRejectValue,
+              const char* aMethodName)
+  {
+    if (mMonitor) {
+      mMonitor->AssertCurrentThreadOwns();
+    }
+    MOZ_ASSERT(mPromise);
+    mPromise->Reject(Move(aRejectValue), aMethodName);
+    mPromise = nullptr;
+  }
+
+  void RejectIfExists(const typename PromiseType::RejectValueType& aRejectValue,
+                      const char* aMethodName)
+  {
+    if (!IsEmpty()) {
+      Reject(aRejectValue, aMethodName);
+    }
+  }
+  void RejectIfExists(typename PromiseType::RejectValueType&& aRejectValue,
+                      const char* aMethodName)
+  {
+    if (!IsEmpty()) {
+      Reject(Move(aRejectValue), aMethodName);
+    }
+  }
+
+private:
+  Monitor* mMonitor;
+  RefPtr<typename PromiseType::Private> mPromise;
+};
+
+/*
+ * Class to encapsulate a MozPromise::Request reference. Use this as the member
+ * variable for a class waiting on a MozPromise.
+ */
+template<typename PromiseType>
+class MozPromiseRequestHolder
+{
+public:
+  MozPromiseRequestHolder() {}
+  ~MozPromiseRequestHolder() { MOZ_ASSERT(!mRequest); }
+
+  void Track(already_AddRefed<typename PromiseType::Request> aRequest)
+  {
+    MOZ_DIAGNOSTIC_ASSERT(!Exists());
+    mRequest = aRequest;
+  }
+
+  void Complete()
+  {
+    MOZ_DIAGNOSTIC_ASSERT(Exists());
+    mRequest = nullptr;
+  }
+
+  // Disconnects and forgets an outstanding promise. The resolve/reject methods
+  // will never be called.
+  void Disconnect() {
+    MOZ_ASSERT(Exists());
+    mRequest->Disconnect();
+    mRequest = nullptr;
+  }
+
+  void DisconnectIfExists() {
+    if (Exists()) {
+      Disconnect();
+    }
+  }
+
+  bool Exists() const { return !!mRequest; }
+
+private:
+  RefPtr<typename PromiseType::Request> mRequest;
+};
+
+// Asynchronous Potentially-Cross-Thread Method Calls.
+//
+// This machinery allows callers to schedule a promise-returning function
+// (a method and object, or a function object like a lambda) to be invoked
+// asynchronously on a given thread, while at the same time receiving a
+// promise upon which to invoke Then() immediately. InvokeAsync dispatches a
+// task to invoke the function on the proper thread and also chain the
+// resulting promise to the one that the caller received, so that resolve/
+// reject values are forwarded through.
+
+namespace detail {
+
+// Non-templated base class to allow us to use MOZ_COUNT_{C,D}TOR, which cause
+// assertions when used on templated types.
+class MethodCallBase
+{
+public:
+  MethodCallBase() { MOZ_COUNT_CTOR(MethodCallBase); }
+  virtual ~MethodCallBase() { MOZ_COUNT_DTOR(MethodCallBase); }
+};
+
+template<typename PromiseType, typename MethodType, typename ThisType,
+         typename... Storages>
+class MethodCall : public MethodCallBase
+{
+public:
+  template<typename... Args>
+  MethodCall(MethodType aMethod, ThisType* aThisVal, Args&&... aArgs)
+    : mMethod(aMethod)
+    , mThisVal(aThisVal)
+    , mArgs(Forward<Args>(aArgs)...)
+  {
+    static_assert(sizeof...(Storages) == sizeof...(Args), "Storages and Args should have equal sizes");
+  }
+
+  RefPtr<PromiseType> Invoke()
+  {
+    return mArgs.apply(mThisVal.get(), mMethod);
+  }
+
+private:
+  MethodType mMethod;
+  RefPtr<ThisType> mThisVal;
+  RunnableMethodArguments<Storages...> mArgs;
+};
+
+template<typename PromiseType, typename MethodType, typename ThisType,
+         typename... Storages>
+class ProxyRunnable : public CancelableRunnable
+{
+public:
+  ProxyRunnable(
+    typename PromiseType::Private* aProxyPromise,
+    MethodCall<PromiseType, MethodType, ThisType, Storages...>* aMethodCall)
+    : CancelableRunnable("detail::ProxyRunnable")
+    , mProxyPromise(aProxyPromise)
+    , mMethodCall(aMethodCall)
+  {
+  }
+
+  NS_IMETHOD Run() override
+  {
+    RefPtr<PromiseType> p = mMethodCall->Invoke();
+    mMethodCall = nullptr;
+    p->ChainTo(mProxyPromise.forget(), "<Proxy Promise>");
+    return NS_OK;
+  }
+
+  nsresult Cancel() override
+  {
+    return Run();
+  }
+
+private:
+  RefPtr<typename PromiseType::Private> mProxyPromise;
+  nsAutoPtr<MethodCall<PromiseType, MethodType, ThisType, Storages...>> mMethodCall;
+};
+
+template<typename... Storages,
+         typename PromiseType, typename ThisType, typename... ArgTypes,
+         typename... ActualArgTypes>
+static RefPtr<PromiseType>
+InvokeAsyncImpl(nsISerialEventTarget* aTarget, ThisType* aThisVal,
+                const char* aCallerName,
+                RefPtr<PromiseType>(ThisType::*aMethod)(ArgTypes...),
+                ActualArgTypes&&... aArgs)
+{
+  MOZ_ASSERT(aTarget);
+
+  typedef RefPtr<PromiseType>(ThisType::*MethodType)(ArgTypes...);
+  typedef detail::MethodCall<PromiseType, MethodType, ThisType, Storages...> MethodCallType;
+  typedef detail::ProxyRunnable<PromiseType, MethodType, ThisType, Storages...> ProxyRunnableType;
+
+  MethodCallType* methodCall =
+    new MethodCallType(aMethod, aThisVal, Forward<ActualArgTypes>(aArgs)...);
+  RefPtr<typename PromiseType::Private> p = new (typename PromiseType::Private)(aCallerName);
+  RefPtr<ProxyRunnableType> r = new ProxyRunnableType(p, methodCall);
+  aTarget->Dispatch(r.forget());
+  return p.forget();
+}
+
+constexpr bool Any()
+{
+  return false;
+}
+
+template <typename T1>
+constexpr bool Any(T1 a)
+{
+  return static_cast<bool>(a);
+}
+
+template <typename T1, typename... Ts>
+constexpr bool Any(T1 a, Ts... aOthers)
+{
+  return a || Any(aOthers...);
+}
+
+} // namespace detail
+
+// InvokeAsync with explicitly-specified storages.
+// See ParameterStorage in nsThreadUtils.h for help.
+template<typename... Storages,
+         typename PromiseType, typename ThisType, typename... ArgTypes,
+         typename... ActualArgTypes,
+         typename EnableIf<sizeof...(Storages) != 0, int>::Type = 0>
+static RefPtr<PromiseType>
+InvokeAsync(nsISerialEventTarget* aTarget, ThisType* aThisVal, const char* aCallerName,
+            RefPtr<PromiseType>(ThisType::*aMethod)(ArgTypes...),
+            ActualArgTypes&&... aArgs)
+{
+  static_assert(sizeof...(Storages) == sizeof...(ArgTypes),
+                "Provided Storages and method's ArgTypes should have equal sizes");
+  static_assert(sizeof...(Storages) == sizeof...(ActualArgTypes),
+                "Provided Storages and ActualArgTypes should have equal sizes");
+  return detail::InvokeAsyncImpl<Storages...>(
+           aTarget, aThisVal, aCallerName, aMethod,
+           Forward<ActualArgTypes>(aArgs)...);
+}
+
+// InvokeAsync with no explicitly-specified storages, will copy arguments and
+// then move them out of the runnable into the target method parameters.
+template<typename... Storages,
+         typename PromiseType, typename ThisType, typename... ArgTypes,
+         typename... ActualArgTypes,
+         typename EnableIf<sizeof...(Storages) == 0, int>::Type = 0>
+static RefPtr<PromiseType>
+InvokeAsync(nsISerialEventTarget* aTarget, ThisType* aThisVal, const char* aCallerName,
+            RefPtr<PromiseType>(ThisType::*aMethod)(ArgTypes...),
+            ActualArgTypes&&... aArgs)
+{
+  static_assert(!detail::Any(IsPointer<typename RemoveReference<ActualArgTypes>::Type>::value...),
+                "Cannot pass pointer types through InvokeAsync, Storages must be provided");
+  static_assert(sizeof...(ArgTypes) == sizeof...(ActualArgTypes),
+                "Method's ArgTypes and ActualArgTypes should have equal sizes");
+  return detail::InvokeAsyncImpl<StoreCopyPassByRRef<typename Decay<ActualArgTypes>::Type>...>(
+           aTarget, aThisVal, aCallerName, aMethod,
+           Forward<ActualArgTypes>(aArgs)...);
+}
+
+namespace detail {
+
+template<typename Function, typename PromiseType>
+class ProxyFunctionRunnable : public CancelableRunnable
+{
+  typedef typename Decay<Function>::Type FunctionStorage;
+public:
+  template<typename F>
+  ProxyFunctionRunnable(typename PromiseType::Private* aProxyPromise,
+                        F&& aFunction)
+    : CancelableRunnable("detail::ProxyFunctionRunnable")
+    , mProxyPromise(aProxyPromise)
+    , mFunction(new FunctionStorage(Forward<F>(aFunction)))
+  {
+  }
+
+  NS_IMETHOD Run() override
+  {
+    RefPtr<PromiseType> p = (*mFunction)();
+    mFunction = nullptr;
+    p->ChainTo(mProxyPromise.forget(), "<Proxy Promise>");
+    return NS_OK;
+  }
+
+  nsresult Cancel() override
+  {
+    return Run();
+  }
+
+private:
+  RefPtr<typename PromiseType::Private> mProxyPromise;
+  UniquePtr<FunctionStorage> mFunction;
+};
+
+// Note: The following struct and function are not for public consumption (yet?)
+// as we would prefer all calls to pass on-the-spot lambdas (or at least moved
+// function objects). They could be moved outside of detail if really needed.
+
+// We prefer getting function objects by non-lvalue-ref (to avoid copying them
+// and their captures). This struct is a tag that allows the use of objects
+// through lvalue-refs where necessary.
+struct AllowInvokeAsyncFunctionLVRef {};
+
+// Invoke a function object (e.g., lambda or std/mozilla::function)
+// asynchronously; note that the object will be copied if provided by lvalue-ref.
+// Return a promise that the function should eventually resolve or reject.
+template<typename Function>
+static auto
+InvokeAsync(nsISerialEventTarget* aTarget, const char* aCallerName,
+            AllowInvokeAsyncFunctionLVRef, Function&& aFunction)
+  -> decltype(aFunction())
+{
+  static_assert(IsRefcountedSmartPointer<decltype(aFunction())>::value
+                && IsMozPromise<typename RemoveSmartPointer<
+                                           decltype(aFunction())>::Type>::value,
+                "Function object must return RefPtr<MozPromise>");
+  MOZ_ASSERT(aTarget);
+  typedef typename RemoveSmartPointer<decltype(aFunction())>::Type PromiseType;
+  typedef detail::ProxyFunctionRunnable<Function, PromiseType> ProxyRunnableType;
+
+  auto p = MakeRefPtr<typename PromiseType::Private>(aCallerName);
+  auto r = MakeRefPtr<ProxyRunnableType>(p, Forward<Function>(aFunction));
+  aTarget->Dispatch(r.forget());
+  return p.forget();
+}
+
+} // namespace detail
+
+// Invoke a function object (e.g., lambda) asynchronously.
+// Return a promise that the function should eventually resolve or reject.
+template<typename Function>
+static auto
+InvokeAsync(nsISerialEventTarget* aTarget, const char* aCallerName,
+            Function&& aFunction)
+  -> decltype(aFunction())
+{
+  static_assert(!IsLvalueReference<Function>::value,
+                "Function object must not be passed by lvalue-ref (to avoid "
+                "unplanned copies); Consider move()ing the object.");
+  return detail::InvokeAsync(aTarget, aCallerName,
+                             detail::AllowInvokeAsyncFunctionLVRef(),
+                             Forward<Function>(aFunction));
+}
+
+#undef PROMISE_LOG
+#undef PROMISE_ASSERT
+#undef PROMISE_DEBUG
+
+} // namespace mozilla
+
+#endif