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