|
1 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ |
|
2 /* vim: set ts=2 et sw=2 tw=80: */ |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
|
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #ifndef mozilla_dom_Promise_h |
|
8 #define mozilla_dom_Promise_h |
|
9 |
|
10 #include "mozilla/Attributes.h" |
|
11 #include "mozilla/ErrorResult.h" |
|
12 #include "mozilla/TypeTraits.h" |
|
13 #include "mozilla/dom/BindingDeclarations.h" |
|
14 #include "nsCycleCollectionParticipant.h" |
|
15 #include "mozilla/dom/PromiseBinding.h" |
|
16 #include "mozilla/dom/ToJSValue.h" |
|
17 #include "nsWrapperCache.h" |
|
18 #include "nsAutoPtr.h" |
|
19 #include "js/TypeDecls.h" |
|
20 |
|
21 #include "mozilla/dom/workers/bindings/WorkerFeature.h" |
|
22 |
|
23 class nsIGlobalObject; |
|
24 |
|
25 namespace mozilla { |
|
26 namespace dom { |
|
27 |
|
28 class AnyCallback; |
|
29 class PromiseCallback; |
|
30 class PromiseInit; |
|
31 class PromiseNativeHandler; |
|
32 |
|
33 class Promise; |
|
34 class PromiseReportRejectFeature : public workers::WorkerFeature |
|
35 { |
|
36 // The Promise that owns this feature. |
|
37 Promise* mPromise; |
|
38 |
|
39 public: |
|
40 PromiseReportRejectFeature(Promise* aPromise) |
|
41 : mPromise(aPromise) |
|
42 { |
|
43 MOZ_ASSERT(mPromise); |
|
44 } |
|
45 |
|
46 virtual bool |
|
47 Notify(JSContext* aCx, workers::Status aStatus) MOZ_OVERRIDE; |
|
48 }; |
|
49 |
|
50 class Promise MOZ_FINAL : public nsISupports, |
|
51 public nsWrapperCache |
|
52 { |
|
53 friend class NativePromiseCallback; |
|
54 friend class PromiseResolverMixin; |
|
55 friend class PromiseResolverTask; |
|
56 friend class PromiseTask; |
|
57 friend class PromiseReportRejectFeature; |
|
58 friend class RejectPromiseCallback; |
|
59 friend class ResolvePromiseCallback; |
|
60 friend class WorkerPromiseResolverTask; |
|
61 friend class WorkerPromiseTask; |
|
62 friend class WrapperPromiseCallback; |
|
63 |
|
64 ~Promise(); |
|
65 |
|
66 public: |
|
67 NS_DECL_CYCLE_COLLECTING_ISUPPORTS |
|
68 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Promise) |
|
69 |
|
70 Promise(nsIGlobalObject* aGlobal); |
|
71 |
|
72 typedef void (Promise::*MaybeFunc)(JSContext* aCx, |
|
73 JS::Handle<JS::Value> aValue); |
|
74 |
|
75 void MaybeResolve(JSContext* aCx, |
|
76 JS::Handle<JS::Value> aValue); |
|
77 void MaybeReject(JSContext* aCx, |
|
78 JS::Handle<JS::Value> aValue); |
|
79 |
|
80 // Helpers for using Promise from C++. |
|
81 // Most DOM objects are handled already. To add a new type T, add a |
|
82 // ToJSValue overload in ToJSValue.h. |
|
83 // aArg is a const reference so we can pass rvalues like integer constants |
|
84 template <typename T> |
|
85 void MaybeResolve(const T& aArg) { |
|
86 MaybeSomething(aArg, &Promise::MaybeResolve); |
|
87 } |
|
88 |
|
89 // aArg is a const reference so we can pass rvalues like NS_ERROR_* |
|
90 template <typename T> |
|
91 void MaybeReject(const T& aArg) { |
|
92 MaybeSomething(aArg, &Promise::MaybeReject); |
|
93 } |
|
94 |
|
95 // WebIDL |
|
96 |
|
97 nsIGlobalObject* GetParentObject() const |
|
98 { |
|
99 return mGlobal; |
|
100 } |
|
101 |
|
102 virtual JSObject* |
|
103 WrapObject(JSContext* aCx) MOZ_OVERRIDE; |
|
104 |
|
105 static already_AddRefed<Promise> |
|
106 Constructor(const GlobalObject& aGlobal, PromiseInit& aInit, |
|
107 ErrorResult& aRv); |
|
108 |
|
109 static already_AddRefed<Promise> |
|
110 Resolve(const GlobalObject& aGlobal, JSContext* aCx, |
|
111 JS::Handle<JS::Value> aValue, ErrorResult& aRv); |
|
112 |
|
113 static already_AddRefed<Promise> |
|
114 Resolve(nsIGlobalObject* aGlobal, JSContext* aCx, |
|
115 JS::Handle<JS::Value> aValue, ErrorResult& aRv); |
|
116 |
|
117 static already_AddRefed<Promise> |
|
118 Reject(const GlobalObject& aGlobal, JSContext* aCx, |
|
119 JS::Handle<JS::Value> aValue, ErrorResult& aRv); |
|
120 |
|
121 static already_AddRefed<Promise> |
|
122 Reject(nsIGlobalObject* aGlobal, JSContext* aCx, |
|
123 JS::Handle<JS::Value> aValue, ErrorResult& aRv); |
|
124 |
|
125 already_AddRefed<Promise> |
|
126 Then(JSContext* aCx, AnyCallback* aResolveCallback, |
|
127 AnyCallback* aRejectCallback); |
|
128 |
|
129 already_AddRefed<Promise> |
|
130 Catch(JSContext* aCx, AnyCallback* aRejectCallback); |
|
131 |
|
132 static already_AddRefed<Promise> |
|
133 All(const GlobalObject& aGlobal, JSContext* aCx, |
|
134 const Sequence<JS::Value>& aIterable, ErrorResult& aRv); |
|
135 |
|
136 static already_AddRefed<Promise> |
|
137 Race(const GlobalObject& aGlobal, JSContext* aCx, |
|
138 const Sequence<JS::Value>& aIterable, ErrorResult& aRv); |
|
139 |
|
140 void AppendNativeHandler(PromiseNativeHandler* aRunnable); |
|
141 |
|
142 private: |
|
143 enum PromiseState { |
|
144 Pending, |
|
145 Resolved, |
|
146 Rejected |
|
147 }; |
|
148 |
|
149 enum PromiseTaskSync { |
|
150 SyncTask, |
|
151 AsyncTask |
|
152 }; |
|
153 |
|
154 void SetState(PromiseState aState) |
|
155 { |
|
156 MOZ_ASSERT(mState == Pending); |
|
157 MOZ_ASSERT(aState != Pending); |
|
158 mState = aState; |
|
159 } |
|
160 |
|
161 void SetResult(JS::Handle<JS::Value> aValue) |
|
162 { |
|
163 mResult = aValue; |
|
164 } |
|
165 |
|
166 // This method processes promise's resolve/reject callbacks with promise's |
|
167 // result. It's executed when the resolver.resolve() or resolver.reject() is |
|
168 // called or when the promise already has a result and new callbacks are |
|
169 // appended by then(), catch() or done(). |
|
170 void RunTask(); |
|
171 |
|
172 void RunResolveTask(JS::Handle<JS::Value> aValue, |
|
173 Promise::PromiseState aState, |
|
174 PromiseTaskSync aAsynchronous); |
|
175 |
|
176 void AppendCallbacks(PromiseCallback* aResolveCallback, |
|
177 PromiseCallback* aRejectCallback); |
|
178 |
|
179 // If we have been rejected and our mResult is a JS exception, |
|
180 // report it to the error console. |
|
181 // Use MaybeReportRejectedOnce() for actual calls. |
|
182 void MaybeReportRejected(); |
|
183 |
|
184 void MaybeReportRejectedOnce() { |
|
185 MaybeReportRejected(); |
|
186 RemoveFeature(); |
|
187 mResult = JS::UndefinedValue(); |
|
188 } |
|
189 |
|
190 void MaybeResolveInternal(JSContext* aCx, |
|
191 JS::Handle<JS::Value> aValue, |
|
192 PromiseTaskSync aSync = AsyncTask); |
|
193 void MaybeRejectInternal(JSContext* aCx, |
|
194 JS::Handle<JS::Value> aValue, |
|
195 PromiseTaskSync aSync = AsyncTask); |
|
196 |
|
197 void ResolveInternal(JSContext* aCx, |
|
198 JS::Handle<JS::Value> aValue, |
|
199 PromiseTaskSync aSync = AsyncTask); |
|
200 |
|
201 void RejectInternal(JSContext* aCx, |
|
202 JS::Handle<JS::Value> aValue, |
|
203 PromiseTaskSync aSync = AsyncTask); |
|
204 |
|
205 // Helper methods for using Promises from C++ |
|
206 JSObject* GetOrCreateWrapper(JSContext* aCx); |
|
207 |
|
208 template <typename T> |
|
209 void MaybeSomething(T& aArgument, MaybeFunc aFunc) { |
|
210 ThreadsafeAutoJSContext cx; |
|
211 |
|
212 JSObject* wrapper = GetOrCreateWrapper(cx); |
|
213 if (!wrapper) { |
|
214 HandleException(cx); |
|
215 return; |
|
216 } |
|
217 |
|
218 JSAutoCompartment ac(cx, wrapper); |
|
219 JS::Rooted<JS::Value> val(cx); |
|
220 if (!ToJSValue(cx, aArgument, &val)) { |
|
221 HandleException(cx); |
|
222 return; |
|
223 } |
|
224 |
|
225 (this->*aFunc)(cx, val); |
|
226 } |
|
227 |
|
228 // Static methods for the PromiseInit functions. |
|
229 static bool |
|
230 JSCallback(JSContext *aCx, unsigned aArgc, JS::Value *aVp); |
|
231 |
|
232 static bool |
|
233 ThenableResolverCommon(JSContext* aCx, uint32_t /* PromiseCallback::Task */ aTask, |
|
234 unsigned aArgc, JS::Value* aVp); |
|
235 static bool |
|
236 JSCallbackThenableResolver(JSContext *aCx, unsigned aArgc, JS::Value *aVp); |
|
237 static bool |
|
238 JSCallbackThenableRejecter(JSContext *aCx, unsigned aArgc, JS::Value *aVp); |
|
239 |
|
240 static JSObject* |
|
241 CreateFunction(JSContext* aCx, JSObject* aParent, Promise* aPromise, |
|
242 int32_t aTask); |
|
243 |
|
244 static JSObject* |
|
245 CreateThenableFunction(JSContext* aCx, Promise* aPromise, uint32_t aTask); |
|
246 |
|
247 void HandleException(JSContext* aCx); |
|
248 |
|
249 void RemoveFeature(); |
|
250 |
|
251 nsRefPtr<nsIGlobalObject> mGlobal; |
|
252 |
|
253 nsTArray<nsRefPtr<PromiseCallback> > mResolveCallbacks; |
|
254 nsTArray<nsRefPtr<PromiseCallback> > mRejectCallbacks; |
|
255 |
|
256 JS::Heap<JS::Value> mResult; |
|
257 PromiseState mState; |
|
258 bool mTaskPending; |
|
259 bool mHadRejectCallback; |
|
260 |
|
261 bool mResolvePending; |
|
262 |
|
263 // If a rejected promise on a worker has no reject callbacks attached, it |
|
264 // needs to know when the worker is shutting down, to report the error on the |
|
265 // console before the worker's context is deleted. This feature is used for |
|
266 // that purpose. |
|
267 nsAutoPtr<PromiseReportRejectFeature> mFeature; |
|
268 }; |
|
269 |
|
270 } // namespace dom |
|
271 } // namespace mozilla |
|
272 |
|
273 #endif // mozilla_dom_Promise_h |