michael@0: /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ michael@0: /* vim: set ts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef mozilla_dom_Promise_h michael@0: #define mozilla_dom_Promise_h michael@0: michael@0: #include "mozilla/Attributes.h" michael@0: #include "mozilla/ErrorResult.h" michael@0: #include "mozilla/TypeTraits.h" michael@0: #include "mozilla/dom/BindingDeclarations.h" michael@0: #include "nsCycleCollectionParticipant.h" michael@0: #include "mozilla/dom/PromiseBinding.h" michael@0: #include "mozilla/dom/ToJSValue.h" michael@0: #include "nsWrapperCache.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "js/TypeDecls.h" michael@0: michael@0: #include "mozilla/dom/workers/bindings/WorkerFeature.h" michael@0: michael@0: class nsIGlobalObject; michael@0: michael@0: namespace mozilla { michael@0: namespace dom { michael@0: michael@0: class AnyCallback; michael@0: class PromiseCallback; michael@0: class PromiseInit; michael@0: class PromiseNativeHandler; michael@0: michael@0: class Promise; michael@0: class PromiseReportRejectFeature : public workers::WorkerFeature michael@0: { michael@0: // The Promise that owns this feature. michael@0: Promise* mPromise; michael@0: michael@0: public: michael@0: PromiseReportRejectFeature(Promise* aPromise) michael@0: : mPromise(aPromise) michael@0: { michael@0: MOZ_ASSERT(mPromise); michael@0: } michael@0: michael@0: virtual bool michael@0: Notify(JSContext* aCx, workers::Status aStatus) MOZ_OVERRIDE; michael@0: }; michael@0: michael@0: class Promise MOZ_FINAL : public nsISupports, michael@0: public nsWrapperCache michael@0: { michael@0: friend class NativePromiseCallback; michael@0: friend class PromiseResolverMixin; michael@0: friend class PromiseResolverTask; michael@0: friend class PromiseTask; michael@0: friend class PromiseReportRejectFeature; michael@0: friend class RejectPromiseCallback; michael@0: friend class ResolvePromiseCallback; michael@0: friend class WorkerPromiseResolverTask; michael@0: friend class WorkerPromiseTask; michael@0: friend class WrapperPromiseCallback; michael@0: michael@0: ~Promise(); michael@0: michael@0: public: michael@0: NS_DECL_CYCLE_COLLECTING_ISUPPORTS michael@0: NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Promise) michael@0: michael@0: Promise(nsIGlobalObject* aGlobal); michael@0: michael@0: typedef void (Promise::*MaybeFunc)(JSContext* aCx, michael@0: JS::Handle aValue); michael@0: michael@0: void MaybeResolve(JSContext* aCx, michael@0: JS::Handle aValue); michael@0: void MaybeReject(JSContext* aCx, michael@0: JS::Handle aValue); michael@0: michael@0: // Helpers for using Promise from C++. michael@0: // Most DOM objects are handled already. To add a new type T, add a michael@0: // ToJSValue overload in ToJSValue.h. michael@0: // aArg is a const reference so we can pass rvalues like integer constants michael@0: template michael@0: void MaybeResolve(const T& aArg) { michael@0: MaybeSomething(aArg, &Promise::MaybeResolve); michael@0: } michael@0: michael@0: // aArg is a const reference so we can pass rvalues like NS_ERROR_* michael@0: template michael@0: void MaybeReject(const T& aArg) { michael@0: MaybeSomething(aArg, &Promise::MaybeReject); michael@0: } michael@0: michael@0: // WebIDL michael@0: michael@0: nsIGlobalObject* GetParentObject() const michael@0: { michael@0: return mGlobal; michael@0: } michael@0: michael@0: virtual JSObject* michael@0: WrapObject(JSContext* aCx) MOZ_OVERRIDE; michael@0: michael@0: static already_AddRefed michael@0: Constructor(const GlobalObject& aGlobal, PromiseInit& aInit, michael@0: ErrorResult& aRv); michael@0: michael@0: static already_AddRefed michael@0: Resolve(const GlobalObject& aGlobal, JSContext* aCx, michael@0: JS::Handle aValue, ErrorResult& aRv); michael@0: michael@0: static already_AddRefed michael@0: Resolve(nsIGlobalObject* aGlobal, JSContext* aCx, michael@0: JS::Handle aValue, ErrorResult& aRv); michael@0: michael@0: static already_AddRefed michael@0: Reject(const GlobalObject& aGlobal, JSContext* aCx, michael@0: JS::Handle aValue, ErrorResult& aRv); michael@0: michael@0: static already_AddRefed michael@0: Reject(nsIGlobalObject* aGlobal, JSContext* aCx, michael@0: JS::Handle aValue, ErrorResult& aRv); michael@0: michael@0: already_AddRefed michael@0: Then(JSContext* aCx, AnyCallback* aResolveCallback, michael@0: AnyCallback* aRejectCallback); michael@0: michael@0: already_AddRefed michael@0: Catch(JSContext* aCx, AnyCallback* aRejectCallback); michael@0: michael@0: static already_AddRefed michael@0: All(const GlobalObject& aGlobal, JSContext* aCx, michael@0: const Sequence& aIterable, ErrorResult& aRv); michael@0: michael@0: static already_AddRefed michael@0: Race(const GlobalObject& aGlobal, JSContext* aCx, michael@0: const Sequence& aIterable, ErrorResult& aRv); michael@0: michael@0: void AppendNativeHandler(PromiseNativeHandler* aRunnable); michael@0: michael@0: private: michael@0: enum PromiseState { michael@0: Pending, michael@0: Resolved, michael@0: Rejected michael@0: }; michael@0: michael@0: enum PromiseTaskSync { michael@0: SyncTask, michael@0: AsyncTask michael@0: }; michael@0: michael@0: void SetState(PromiseState aState) michael@0: { michael@0: MOZ_ASSERT(mState == Pending); michael@0: MOZ_ASSERT(aState != Pending); michael@0: mState = aState; michael@0: } michael@0: michael@0: void SetResult(JS::Handle aValue) michael@0: { michael@0: mResult = aValue; michael@0: } michael@0: michael@0: // This method processes promise's resolve/reject callbacks with promise's michael@0: // result. It's executed when the resolver.resolve() or resolver.reject() is michael@0: // called or when the promise already has a result and new callbacks are michael@0: // appended by then(), catch() or done(). michael@0: void RunTask(); michael@0: michael@0: void RunResolveTask(JS::Handle aValue, michael@0: Promise::PromiseState aState, michael@0: PromiseTaskSync aAsynchronous); michael@0: michael@0: void AppendCallbacks(PromiseCallback* aResolveCallback, michael@0: PromiseCallback* aRejectCallback); michael@0: michael@0: // If we have been rejected and our mResult is a JS exception, michael@0: // report it to the error console. michael@0: // Use MaybeReportRejectedOnce() for actual calls. michael@0: void MaybeReportRejected(); michael@0: michael@0: void MaybeReportRejectedOnce() { michael@0: MaybeReportRejected(); michael@0: RemoveFeature(); michael@0: mResult = JS::UndefinedValue(); michael@0: } michael@0: michael@0: void MaybeResolveInternal(JSContext* aCx, michael@0: JS::Handle aValue, michael@0: PromiseTaskSync aSync = AsyncTask); michael@0: void MaybeRejectInternal(JSContext* aCx, michael@0: JS::Handle aValue, michael@0: PromiseTaskSync aSync = AsyncTask); michael@0: michael@0: void ResolveInternal(JSContext* aCx, michael@0: JS::Handle aValue, michael@0: PromiseTaskSync aSync = AsyncTask); michael@0: michael@0: void RejectInternal(JSContext* aCx, michael@0: JS::Handle aValue, michael@0: PromiseTaskSync aSync = AsyncTask); michael@0: michael@0: // Helper methods for using Promises from C++ michael@0: JSObject* GetOrCreateWrapper(JSContext* aCx); michael@0: michael@0: template michael@0: void MaybeSomething(T& aArgument, MaybeFunc aFunc) { michael@0: ThreadsafeAutoJSContext cx; michael@0: michael@0: JSObject* wrapper = GetOrCreateWrapper(cx); michael@0: if (!wrapper) { michael@0: HandleException(cx); michael@0: return; michael@0: } michael@0: michael@0: JSAutoCompartment ac(cx, wrapper); michael@0: JS::Rooted val(cx); michael@0: if (!ToJSValue(cx, aArgument, &val)) { michael@0: HandleException(cx); michael@0: return; michael@0: } michael@0: michael@0: (this->*aFunc)(cx, val); michael@0: } michael@0: michael@0: // Static methods for the PromiseInit functions. michael@0: static bool michael@0: JSCallback(JSContext *aCx, unsigned aArgc, JS::Value *aVp); michael@0: michael@0: static bool michael@0: ThenableResolverCommon(JSContext* aCx, uint32_t /* PromiseCallback::Task */ aTask, michael@0: unsigned aArgc, JS::Value* aVp); michael@0: static bool michael@0: JSCallbackThenableResolver(JSContext *aCx, unsigned aArgc, JS::Value *aVp); michael@0: static bool michael@0: JSCallbackThenableRejecter(JSContext *aCx, unsigned aArgc, JS::Value *aVp); michael@0: michael@0: static JSObject* michael@0: CreateFunction(JSContext* aCx, JSObject* aParent, Promise* aPromise, michael@0: int32_t aTask); michael@0: michael@0: static JSObject* michael@0: CreateThenableFunction(JSContext* aCx, Promise* aPromise, uint32_t aTask); michael@0: michael@0: void HandleException(JSContext* aCx); michael@0: michael@0: void RemoveFeature(); michael@0: michael@0: nsRefPtr mGlobal; michael@0: michael@0: nsTArray > mResolveCallbacks; michael@0: nsTArray > mRejectCallbacks; michael@0: michael@0: JS::Heap mResult; michael@0: PromiseState mState; michael@0: bool mTaskPending; michael@0: bool mHadRejectCallback; michael@0: michael@0: bool mResolvePending; michael@0: michael@0: // If a rejected promise on a worker has no reject callbacks attached, it michael@0: // needs to know when the worker is shutting down, to report the error on the michael@0: // console before the worker's context is deleted. This feature is used for michael@0: // that purpose. michael@0: nsAutoPtr mFeature; michael@0: }; michael@0: michael@0: } // namespace dom michael@0: } // namespace mozilla michael@0: michael@0: #endif // mozilla_dom_Promise_h