Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=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 : #include "DOMRequest.h"
8 :
9 : #include "DOMError.h"
10 : #include "nsThreadUtils.h"
11 : #include "DOMCursor.h"
12 : #include "mozilla/ErrorResult.h"
13 : #include "mozilla/dom/Event.h"
14 : #include "mozilla/dom/Promise.h"
15 : #include "mozilla/dom/ScriptSettings.h"
16 : #include "jsfriendapi.h"
17 : #include "nsContentUtils.h"
18 :
19 : using mozilla::dom::AnyCallback;
20 : using mozilla::dom::DOMError;
21 : using mozilla::dom::DOMRequest;
22 : using mozilla::dom::DOMRequestService;
23 : using mozilla::dom::DOMCursor;
24 : using mozilla::dom::Promise;
25 : using mozilla::dom::AutoJSAPI;
26 : using mozilla::dom::RootingCx;
27 :
28 0 : DOMRequest::DOMRequest(nsPIDOMWindowInner* aWindow)
29 : : DOMEventTargetHelper(aWindow)
30 0 : , mResult(JS::UndefinedValue())
31 0 : , mDone(false)
32 : {
33 0 : }
34 :
35 0 : DOMRequest::DOMRequest(nsIGlobalObject* aGlobal)
36 : : DOMEventTargetHelper(aGlobal)
37 0 : , mResult(JS::UndefinedValue())
38 0 : , mDone(false)
39 : {
40 0 : }
41 :
42 0 : DOMRequest::~DOMRequest()
43 : {
44 0 : mResult.setUndefined();
45 0 : mozilla::DropJSObjects(this);
46 0 : }
47 :
48 : NS_IMPL_CYCLE_COLLECTION_CLASS(DOMRequest)
49 :
50 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DOMRequest,
51 : DOMEventTargetHelper)
52 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError)
53 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromise)
54 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
55 :
56 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DOMRequest,
57 : DOMEventTargetHelper)
58 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mError)
59 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromise)
60 0 : tmp->mResult.setUndefined();
61 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
62 :
63 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(DOMRequest,
64 : DOMEventTargetHelper)
65 : // Don't need NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER because
66 : // DOMEventTargetHelper does it for us.
67 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResult)
68 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_END
69 :
70 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DOMRequest)
71 0 : NS_INTERFACE_MAP_ENTRY(nsIDOMDOMRequest)
72 0 : NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
73 :
74 0 : NS_IMPL_ADDREF_INHERITED(DOMRequest, DOMEventTargetHelper)
75 0 : NS_IMPL_RELEASE_INHERITED(DOMRequest, DOMEventTargetHelper)
76 :
77 : /* virtual */ JSObject*
78 0 : DOMRequest::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
79 : {
80 0 : return DOMRequestBinding::Wrap(aCx, this, aGivenProto);
81 : }
82 :
83 0 : NS_IMPL_EVENT_HANDLER(DOMRequest, success)
84 0 : NS_IMPL_EVENT_HANDLER(DOMRequest, error)
85 :
86 : NS_IMETHODIMP
87 0 : DOMRequest::GetReadyState(nsAString& aReadyState)
88 : {
89 0 : DOMRequestReadyState readyState = ReadyState();
90 0 : switch (readyState) {
91 : case DOMRequestReadyState::Pending:
92 0 : aReadyState.AssignLiteral("pending");
93 0 : break;
94 : case DOMRequestReadyState::Done:
95 0 : aReadyState.AssignLiteral("done");
96 0 : break;
97 : default:
98 0 : MOZ_CRASH("Unrecognized readyState.");
99 : }
100 :
101 0 : return NS_OK;
102 : }
103 :
104 : NS_IMETHODIMP
105 0 : DOMRequest::GetResult(JS::MutableHandle<JS::Value> aResult)
106 : {
107 0 : GetResult(nullptr, aResult);
108 0 : return NS_OK;
109 : }
110 :
111 : NS_IMETHODIMP
112 0 : DOMRequest::GetError(nsISupports** aError)
113 : {
114 0 : NS_IF_ADDREF(*aError = GetError());
115 0 : return NS_OK;
116 : }
117 :
118 : void
119 0 : DOMRequest::FireSuccess(JS::Handle<JS::Value> aResult)
120 : {
121 0 : NS_ASSERTION(!mDone, "mDone shouldn't have been set to true already!");
122 0 : NS_ASSERTION(!mError, "mError shouldn't have been set!");
123 0 : NS_ASSERTION(mResult.isUndefined(), "mResult shouldn't have been set!");
124 :
125 0 : mDone = true;
126 0 : if (aResult.isGCThing()) {
127 0 : RootResultVal();
128 : }
129 0 : mResult = aResult;
130 :
131 0 : FireEvent(NS_LITERAL_STRING("success"), false, false);
132 :
133 0 : if (mPromise) {
134 0 : mPromise->MaybeResolve(mResult);
135 : }
136 0 : }
137 :
138 : void
139 0 : DOMRequest::FireError(const nsAString& aError)
140 : {
141 0 : NS_ASSERTION(!mDone, "mDone shouldn't have been set to true already!");
142 0 : NS_ASSERTION(!mError, "mError shouldn't have been set!");
143 0 : NS_ASSERTION(mResult.isUndefined(), "mResult shouldn't have been set!");
144 :
145 0 : mDone = true;
146 0 : mError = new DOMError(GetOwner(), aError);
147 :
148 0 : FireEvent(NS_LITERAL_STRING("error"), true, true);
149 :
150 0 : if (mPromise) {
151 0 : mPromise->MaybeRejectBrokenly(mError);
152 : }
153 0 : }
154 :
155 : void
156 0 : DOMRequest::FireError(nsresult aError)
157 : {
158 0 : NS_ASSERTION(!mDone, "mDone shouldn't have been set to true already!");
159 0 : NS_ASSERTION(!mError, "mError shouldn't have been set!");
160 0 : NS_ASSERTION(mResult.isUndefined(), "mResult shouldn't have been set!");
161 :
162 0 : mDone = true;
163 0 : mError = new DOMError(GetOwner(), aError);
164 :
165 0 : FireEvent(NS_LITERAL_STRING("error"), true, true);
166 :
167 0 : if (mPromise) {
168 0 : mPromise->MaybeRejectBrokenly(mError);
169 : }
170 0 : }
171 :
172 : void
173 0 : DOMRequest::FireDetailedError(DOMError* aError)
174 : {
175 0 : NS_ASSERTION(!mDone, "mDone shouldn't have been set to true already!");
176 0 : NS_ASSERTION(!mError, "mError shouldn't have been set!");
177 0 : NS_ASSERTION(mResult.isUndefined(), "mResult shouldn't have been set!");
178 0 : NS_ASSERTION(aError, "No detailed error provided");
179 :
180 0 : mDone = true;
181 0 : mError = aError;
182 :
183 0 : FireEvent(NS_LITERAL_STRING("error"), true, true);
184 :
185 0 : if (mPromise) {
186 0 : mPromise->MaybeRejectBrokenly(mError);
187 : }
188 0 : }
189 :
190 : void
191 0 : DOMRequest::FireEvent(const nsAString& aType, bool aBubble, bool aCancelable)
192 : {
193 0 : if (NS_FAILED(CheckInnerWindowCorrectness())) {
194 0 : return;
195 : }
196 :
197 0 : RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);
198 0 : event->InitEvent(aType, aBubble, aCancelable);
199 0 : event->SetTrusted(true);
200 :
201 : bool dummy;
202 0 : DispatchEvent(event, &dummy);
203 : }
204 :
205 : void
206 0 : DOMRequest::RootResultVal()
207 : {
208 0 : mozilla::HoldJSObjects(this);
209 0 : }
210 :
211 : void
212 0 : DOMRequest::Then(JSContext* aCx, AnyCallback* aResolveCallback,
213 : AnyCallback* aRejectCallback,
214 : JS::MutableHandle<JS::Value> aRetval,
215 : mozilla::ErrorResult& aRv)
216 : {
217 0 : if (!mPromise) {
218 0 : mPromise = Promise::Create(DOMEventTargetHelper::GetParentObject(), aRv);
219 0 : if (aRv.Failed()) {
220 0 : return;
221 : }
222 0 : if (mDone) {
223 : // Since we create mPromise lazily, it's possible that the DOMRequest object
224 : // has already fired its success/error event. In that case we should
225 : // manually resolve/reject mPromise here. mPromise will take care of
226 : // calling the callbacks on |promise| as needed.
227 0 : if (mError) {
228 0 : mPromise->MaybeRejectBrokenly(mError);
229 : } else {
230 0 : mPromise->MaybeResolve(mResult);
231 : }
232 : }
233 : }
234 :
235 : // Just use the global of the Promise itself as the callee global.
236 0 : JS::Rooted<JSObject*> global(aCx, mPromise->PromiseObj());
237 0 : global = js::GetGlobalForObjectCrossCompartment(global);
238 0 : mPromise->Then(aCx, global, aResolveCallback, aRejectCallback, aRetval, aRv);
239 : }
240 :
241 0 : NS_IMPL_ISUPPORTS(DOMRequestService, nsIDOMRequestService)
242 :
243 : NS_IMETHODIMP
244 0 : DOMRequestService::CreateRequest(mozIDOMWindow* aWindow,
245 : nsIDOMDOMRequest** aRequest)
246 : {
247 0 : MOZ_ASSERT(NS_IsMainThread());
248 0 : NS_ENSURE_STATE(aWindow);
249 0 : auto* win = nsPIDOMWindowInner::From(aWindow);
250 0 : NS_ADDREF(*aRequest = new DOMRequest(win));
251 :
252 0 : return NS_OK;
253 : }
254 :
255 : NS_IMETHODIMP
256 0 : DOMRequestService::CreateCursor(mozIDOMWindow* aWindow,
257 : nsICursorContinueCallback* aCallback,
258 : nsIDOMDOMCursor** aCursor)
259 : {
260 0 : NS_ENSURE_STATE(aWindow);
261 0 : auto* win = nsPIDOMWindowInner::From(aWindow);
262 0 : NS_ADDREF(*aCursor = new DOMCursor(win, aCallback));
263 :
264 0 : return NS_OK;
265 : }
266 :
267 : NS_IMETHODIMP
268 0 : DOMRequestService::FireSuccess(nsIDOMDOMRequest* aRequest,
269 : JS::Handle<JS::Value> aResult)
270 : {
271 0 : NS_ENSURE_STATE(aRequest);
272 0 : static_cast<DOMRequest*>(aRequest)->FireSuccess(aResult);
273 :
274 0 : return NS_OK;
275 : }
276 :
277 : NS_IMETHODIMP
278 0 : DOMRequestService::FireError(nsIDOMDOMRequest* aRequest,
279 : const nsAString& aError)
280 : {
281 0 : NS_ENSURE_STATE(aRequest);
282 0 : static_cast<DOMRequest*>(aRequest)->FireError(aError);
283 :
284 0 : return NS_OK;
285 : }
286 :
287 : NS_IMETHODIMP
288 0 : DOMRequestService::FireDetailedError(nsIDOMDOMRequest* aRequest,
289 : nsISupports* aError)
290 : {
291 0 : NS_ENSURE_STATE(aRequest);
292 0 : nsCOMPtr<DOMError> err = do_QueryInterface(aError);
293 0 : NS_ENSURE_STATE(err);
294 0 : static_cast<DOMRequest*>(aRequest)->FireDetailedError(err);
295 :
296 0 : return NS_OK;
297 : }
298 :
299 0 : class FireSuccessAsyncTask : public mozilla::Runnable
300 : {
301 :
302 0 : FireSuccessAsyncTask(DOMRequest* aRequest, const JS::Value& aResult)
303 0 : : mozilla::Runnable("FireSuccessAsyncTask")
304 : , mReq(aRequest)
305 0 : , mResult(RootingCx(), aResult)
306 : {
307 0 : }
308 :
309 : public:
310 :
311 : // Due to the fact that initialization can fail during shutdown (since we
312 : // can't fetch a js context), set up an initiatization function to make sure
313 : // we can return the failure appropriately
314 : static nsresult
315 0 : Dispatch(DOMRequest* aRequest,
316 : const JS::Value& aResult)
317 : {
318 : RefPtr<FireSuccessAsyncTask> asyncTask =
319 0 : new FireSuccessAsyncTask(aRequest, aResult);
320 0 : MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(asyncTask));
321 0 : return NS_OK;
322 : }
323 :
324 : NS_IMETHOD
325 0 : Run() override
326 : {
327 0 : mReq->FireSuccess(JS::Handle<JS::Value>::fromMarkedLocation(mResult.address()));
328 0 : return NS_OK;
329 : }
330 :
331 : private:
332 : RefPtr<DOMRequest> mReq;
333 : JS::PersistentRooted<JS::Value> mResult;
334 : };
335 :
336 0 : class FireErrorAsyncTask : public mozilla::Runnable
337 : {
338 : public:
339 0 : FireErrorAsyncTask(DOMRequest* aRequest, const nsAString& aError)
340 0 : : mozilla::Runnable("FireErrorAsyncTask")
341 : , mReq(aRequest)
342 0 : , mError(aError)
343 : {
344 0 : }
345 :
346 : NS_IMETHOD
347 0 : Run() override
348 : {
349 0 : mReq->FireError(mError);
350 0 : return NS_OK;
351 : }
352 : private:
353 : RefPtr<DOMRequest> mReq;
354 : nsString mError;
355 : };
356 :
357 : NS_IMETHODIMP
358 0 : DOMRequestService::FireSuccessAsync(nsIDOMDOMRequest* aRequest,
359 : JS::Handle<JS::Value> aResult)
360 : {
361 0 : NS_ENSURE_STATE(aRequest);
362 0 : return FireSuccessAsyncTask::Dispatch(static_cast<DOMRequest*>(aRequest), aResult);
363 : }
364 :
365 : NS_IMETHODIMP
366 0 : DOMRequestService::FireErrorAsync(nsIDOMDOMRequest* aRequest,
367 : const nsAString& aError)
368 : {
369 0 : NS_ENSURE_STATE(aRequest);
370 : nsCOMPtr<nsIRunnable> asyncTask =
371 0 : new FireErrorAsyncTask(static_cast<DOMRequest*>(aRequest), aError);
372 0 : MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(asyncTask));
373 0 : return NS_OK;
374 : }
375 :
376 : NS_IMETHODIMP
377 0 : DOMRequestService::FireDone(nsIDOMDOMCursor* aCursor) {
378 0 : NS_ENSURE_STATE(aCursor);
379 0 : static_cast<DOMCursor*>(aCursor)->FireDone();
380 :
381 0 : return NS_OK;
382 : }
|