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/. */
7 #include "DOMRequest.h"
9 #include "DOMException.h"
10 #include "nsThreadUtils.h"
11 #include "mozilla/HoldDropJSObjects.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"
19 using mozilla::dom::AnyCallback
;
20 using mozilla::dom::AutoJSAPI
;
21 using mozilla::dom::DOMException
;
22 using mozilla::dom::DOMRequest
;
23 using mozilla::dom::DOMRequestService
;
24 using mozilla::dom::Promise
;
25 using mozilla::dom::RootingCx
;
27 DOMRequest::DOMRequest(nsPIDOMWindowInner
* aWindow
)
28 : DOMEventTargetHelper(aWindow
),
29 mResult(JS::UndefinedValue()),
32 DOMRequest::DOMRequest(nsIGlobalObject
* aGlobal
)
33 : DOMEventTargetHelper(aGlobal
),
34 mResult(JS::UndefinedValue()),
37 DOMRequest::~DOMRequest() {
38 mResult
.setUndefined();
39 mozilla::DropJSObjects(this);
42 NS_IMPL_CYCLE_COLLECTION_CLASS(DOMRequest
)
44 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DOMRequest
,
46 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError
)
47 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromise
)
48 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
50 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DOMRequest
,
52 NS_IMPL_CYCLE_COLLECTION_UNLINK(mError
)
53 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromise
)
54 tmp
->mResult
.setUndefined();
55 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
57 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(DOMRequest
, DOMEventTargetHelper
)
58 // Don't need NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER because
59 // DOMEventTargetHelper does it for us.
60 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResult
)
61 NS_IMPL_CYCLE_COLLECTION_TRACE_END
63 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMRequest
)
64 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper
)
66 NS_IMPL_ADDREF_INHERITED(DOMRequest
, DOMEventTargetHelper
)
67 NS_IMPL_RELEASE_INHERITED(DOMRequest
, DOMEventTargetHelper
)
70 JSObject
* DOMRequest::WrapObject(JSContext
* aCx
,
71 JS::Handle
<JSObject
*> aGivenProto
) {
72 return DOMRequest_Binding::Wrap(aCx
, this, aGivenProto
);
75 void DOMRequest::FireSuccess(JS::Handle
<JS::Value
> aResult
) {
76 NS_ASSERTION(!mDone
, "mDone shouldn't have been set to true already!");
77 NS_ASSERTION(!mError
, "mError shouldn't have been set!");
78 NS_ASSERTION(mResult
.isUndefined(), "mResult shouldn't have been set!");
81 if (aResult
.isGCThing()) {
86 FireEvent(u
"success"_ns
, false, false);
89 mPromise
->MaybeResolve(mResult
);
93 void DOMRequest::FireError(const nsAString
& aError
) {
94 NS_ASSERTION(!mDone
, "mDone shouldn't have been set to true already!");
95 NS_ASSERTION(!mError
, "mError shouldn't have been set!");
96 NS_ASSERTION(mResult
.isUndefined(), "mResult shouldn't have been set!");
99 // XXX Error code chosen arbitrarily
100 mError
= DOMException::Create(NS_ERROR_DOM_UNKNOWN_ERR
,
101 NS_ConvertUTF16toUTF8(aError
));
103 FireEvent(u
"error"_ns
, true, true);
106 mPromise
->MaybeRejectBrokenly(mError
);
110 void DOMRequest::FireError(nsresult aError
) {
111 NS_ASSERTION(!mDone
, "mDone shouldn't have been set to true already!");
112 NS_ASSERTION(!mError
, "mError shouldn't have been set!");
113 NS_ASSERTION(mResult
.isUndefined(), "mResult shouldn't have been set!");
116 mError
= DOMException::Create(aError
);
118 FireEvent(u
"error"_ns
, true, true);
121 mPromise
->MaybeRejectBrokenly(mError
);
125 void DOMRequest::FireDetailedError(DOMException
& aError
) {
126 NS_ASSERTION(!mDone
, "mDone shouldn't have been set to true already!");
127 NS_ASSERTION(!mError
, "mError shouldn't have been set!");
128 NS_ASSERTION(mResult
.isUndefined(), "mResult shouldn't have been set!");
133 FireEvent(u
"error"_ns
, true, true);
136 mPromise
->MaybeRejectBrokenly(mError
);
140 void DOMRequest::FireEvent(const nsAString
& aType
, bool aBubble
,
142 if (NS_FAILED(CheckCurrentGlobalCorrectness())) {
146 RefPtr
<Event
> event
= NS_NewDOMEvent(this, nullptr, nullptr);
147 event
->InitEvent(aType
, aBubble
, aCancelable
);
148 event
->SetTrusted(true);
150 DispatchEvent(*event
);
153 void DOMRequest::RootResultVal() { mozilla::HoldJSObjects(this); }
155 void DOMRequest::Then(JSContext
* aCx
, AnyCallback
* aResolveCallback
,
156 AnyCallback
* aRejectCallback
,
157 JS::MutableHandle
<JS::Value
> aRetval
,
158 mozilla::ErrorResult
& aRv
) {
160 mPromise
= Promise::Create(DOMEventTargetHelper::GetParentObject(), aRv
);
165 // Since we create mPromise lazily, it's possible that the DOMRequest
166 // object has already fired its success/error event. In that case we
167 // should manually resolve/reject mPromise here. mPromise will take care
168 // of calling the callbacks on |promise| as needed.
170 mPromise
->MaybeRejectBrokenly(mError
);
172 mPromise
->MaybeResolve(mResult
);
177 // Just use the global of the Promise itself as the callee global.
178 JS::Rooted
<JSObject
*> global(aCx
, mPromise
->PromiseObj());
179 global
= JS::GetNonCCWObjectGlobal(global
);
180 mPromise
->Then(aCx
, global
, aResolveCallback
, aRejectCallback
, aRetval
, aRv
);
183 NS_IMPL_ISUPPORTS(DOMRequestService
, nsIDOMRequestService
)
186 DOMRequestService::CreateRequest(mozIDOMWindow
* aWindow
,
187 DOMRequest
** aRequest
) {
188 MOZ_ASSERT(NS_IsMainThread());
189 NS_ENSURE_STATE(aWindow
);
190 auto* win
= nsPIDOMWindowInner::From(aWindow
);
191 RefPtr
<DOMRequest
> req
= new DOMRequest(win
);
192 req
.forget(aRequest
);
198 DOMRequestService::FireSuccess(DOMRequest
* aRequest
,
199 JS::Handle
<JS::Value
> aResult
) {
200 NS_ENSURE_STATE(aRequest
);
201 aRequest
->FireSuccess(aResult
);
207 DOMRequestService::FireError(DOMRequest
* aRequest
, const nsAString
& aError
) {
208 NS_ENSURE_STATE(aRequest
);
209 aRequest
->FireError(aError
);
214 class FireSuccessAsyncTask
: public mozilla::Runnable
{
215 FireSuccessAsyncTask(DOMRequest
* aRequest
, const JS::Value
& aResult
)
216 : mozilla::Runnable("FireSuccessAsyncTask"),
218 mResult(RootingCx(), aResult
) {}
221 // Due to the fact that initialization can fail during shutdown (since we
222 // can't fetch a js context), set up an initiatization function to make sure
223 // we can return the failure appropriately
224 static nsresult
Dispatch(DOMRequest
* aRequest
, const JS::Value
& aResult
) {
225 RefPtr
<FireSuccessAsyncTask
> asyncTask
=
226 new FireSuccessAsyncTask(aRequest
, aResult
);
227 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(asyncTask
));
234 JS::Handle
<JS::Value
>::fromMarkedLocation(mResult
.address()));
239 RefPtr
<DOMRequest
> mReq
;
240 JS::PersistentRooted
<JS::Value
> mResult
;
243 class FireErrorAsyncTask
: public mozilla::Runnable
{
245 FireErrorAsyncTask(DOMRequest
* aRequest
, const nsAString
& aError
)
246 : mozilla::Runnable("FireErrorAsyncTask"),
252 mReq
->FireError(mError
);
257 RefPtr
<DOMRequest
> mReq
;
262 DOMRequestService::FireSuccessAsync(DOMRequest
* aRequest
,
263 JS::Handle
<JS::Value
> aResult
) {
264 NS_ENSURE_STATE(aRequest
);
265 return FireSuccessAsyncTask::Dispatch(aRequest
, aResult
);
269 DOMRequestService::FireErrorAsync(DOMRequest
* aRequest
,
270 const nsAString
& aError
) {
271 NS_ENSURE_STATE(aRequest
);
272 nsCOMPtr
<nsIRunnable
> asyncTask
= new FireErrorAsyncTask(aRequest
, aError
);
273 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(asyncTask
));