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/ErrorResult.h"
12 #include "mozilla/dom/Event.h"
13 #include "mozilla/dom/Promise.h"
14 #include "mozilla/dom/ScriptSettings.h"
15 #include "jsfriendapi.h"
16 #include "nsContentUtils.h"
18 using mozilla::dom::AnyCallback
;
19 using mozilla::dom::AutoJSAPI
;
20 using mozilla::dom::DOMException
;
21 using mozilla::dom::DOMRequest
;
22 using mozilla::dom::DOMRequestService
;
23 using mozilla::dom::Promise
;
24 using mozilla::dom::RootingCx
;
26 DOMRequest::DOMRequest(nsPIDOMWindowInner
* aWindow
)
27 : DOMEventTargetHelper(aWindow
),
28 mResult(JS::UndefinedValue()),
31 DOMRequest::DOMRequest(nsIGlobalObject
* aGlobal
)
32 : DOMEventTargetHelper(aGlobal
),
33 mResult(JS::UndefinedValue()),
36 DOMRequest::~DOMRequest() {
37 mResult
.setUndefined();
38 mozilla::DropJSObjects(this);
41 NS_IMPL_CYCLE_COLLECTION_CLASS(DOMRequest
)
43 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DOMRequest
,
45 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError
)
46 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromise
)
47 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
49 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DOMRequest
,
51 NS_IMPL_CYCLE_COLLECTION_UNLINK(mError
)
52 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromise
)
53 tmp
->mResult
.setUndefined();
54 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
56 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(DOMRequest
, DOMEventTargetHelper
)
57 // Don't need NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER because
58 // DOMEventTargetHelper does it for us.
59 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResult
)
60 NS_IMPL_CYCLE_COLLECTION_TRACE_END
62 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMRequest
)
63 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper
)
65 NS_IMPL_ADDREF_INHERITED(DOMRequest
, DOMEventTargetHelper
)
66 NS_IMPL_RELEASE_INHERITED(DOMRequest
, DOMEventTargetHelper
)
69 JSObject
* DOMRequest::WrapObject(JSContext
* aCx
,
70 JS::Handle
<JSObject
*> aGivenProto
) {
71 return DOMRequest_Binding::Wrap(aCx
, this, aGivenProto
);
74 void DOMRequest::FireSuccess(JS::Handle
<JS::Value
> aResult
) {
75 NS_ASSERTION(!mDone
, "mDone shouldn't have been set to true already!");
76 NS_ASSERTION(!mError
, "mError shouldn't have been set!");
77 NS_ASSERTION(mResult
.isUndefined(), "mResult shouldn't have been set!");
80 if (aResult
.isGCThing()) {
85 FireEvent(NS_LITERAL_STRING("success"), false, false);
88 mPromise
->MaybeResolve(mResult
);
92 void DOMRequest::FireError(const nsAString
& aError
) {
93 NS_ASSERTION(!mDone
, "mDone shouldn't have been set to true already!");
94 NS_ASSERTION(!mError
, "mError shouldn't have been set!");
95 NS_ASSERTION(mResult
.isUndefined(), "mResult shouldn't have been set!");
98 // XXX Error code chosen arbitrarily
99 mError
= DOMException::Create(NS_ERROR_DOM_UNKNOWN_ERR
,
100 NS_ConvertUTF16toUTF8(aError
));
102 FireEvent(NS_LITERAL_STRING("error"), true, true);
105 mPromise
->MaybeRejectBrokenly(mError
);
109 void DOMRequest::FireError(nsresult aError
) {
110 NS_ASSERTION(!mDone
, "mDone shouldn't have been set to true already!");
111 NS_ASSERTION(!mError
, "mError shouldn't have been set!");
112 NS_ASSERTION(mResult
.isUndefined(), "mResult shouldn't have been set!");
115 mError
= DOMException::Create(aError
);
117 FireEvent(NS_LITERAL_STRING("error"), true, true);
120 mPromise
->MaybeRejectBrokenly(mError
);
124 void DOMRequest::FireDetailedError(DOMException
& aError
) {
125 NS_ASSERTION(!mDone
, "mDone shouldn't have been set to true already!");
126 NS_ASSERTION(!mError
, "mError shouldn't have been set!");
127 NS_ASSERTION(mResult
.isUndefined(), "mResult shouldn't have been set!");
132 FireEvent(NS_LITERAL_STRING("error"), true, true);
135 mPromise
->MaybeRejectBrokenly(mError
);
139 void DOMRequest::FireEvent(const nsAString
& aType
, bool aBubble
,
141 if (NS_FAILED(CheckCurrentGlobalCorrectness())) {
145 RefPtr
<Event
> event
= NS_NewDOMEvent(this, nullptr, nullptr);
146 event
->InitEvent(aType
, aBubble
, aCancelable
);
147 event
->SetTrusted(true);
149 DispatchEvent(*event
);
152 void DOMRequest::RootResultVal() { mozilla::HoldJSObjects(this); }
154 void DOMRequest::Then(JSContext
* aCx
, AnyCallback
* aResolveCallback
,
155 AnyCallback
* aRejectCallback
,
156 JS::MutableHandle
<JS::Value
> aRetval
,
157 mozilla::ErrorResult
& aRv
) {
159 mPromise
= Promise::Create(DOMEventTargetHelper::GetParentObject(), aRv
);
164 // Since we create mPromise lazily, it's possible that the DOMRequest
165 // object has already fired its success/error event. In that case we
166 // should manually resolve/reject mPromise here. mPromise will take care
167 // of calling the callbacks on |promise| as needed.
169 mPromise
->MaybeRejectBrokenly(mError
);
171 mPromise
->MaybeResolve(mResult
);
176 // Just use the global of the Promise itself as the callee global.
177 JS::Rooted
<JSObject
*> global(aCx
, mPromise
->PromiseObj());
178 global
= JS::GetNonCCWObjectGlobal(global
);
179 mPromise
->Then(aCx
, global
, aResolveCallback
, aRejectCallback
, aRetval
, aRv
);
182 NS_IMPL_ISUPPORTS(DOMRequestService
, nsIDOMRequestService
)
185 DOMRequestService::CreateRequest(mozIDOMWindow
* aWindow
,
186 DOMRequest
** aRequest
) {
187 MOZ_ASSERT(NS_IsMainThread());
188 NS_ENSURE_STATE(aWindow
);
189 auto* win
= nsPIDOMWindowInner::From(aWindow
);
190 RefPtr
<DOMRequest
> req
= new DOMRequest(win
);
191 req
.forget(aRequest
);
197 DOMRequestService::FireSuccess(DOMRequest
* aRequest
,
198 JS::Handle
<JS::Value
> aResult
) {
199 NS_ENSURE_STATE(aRequest
);
200 aRequest
->FireSuccess(aResult
);
206 DOMRequestService::FireError(DOMRequest
* aRequest
, const nsAString
& aError
) {
207 NS_ENSURE_STATE(aRequest
);
208 aRequest
->FireError(aError
);
213 class FireSuccessAsyncTask
: public mozilla::Runnable
{
214 FireSuccessAsyncTask(DOMRequest
* aRequest
, const JS::Value
& aResult
)
215 : mozilla::Runnable("FireSuccessAsyncTask"),
217 mResult(RootingCx(), aResult
) {}
220 // Due to the fact that initialization can fail during shutdown (since we
221 // can't fetch a js context), set up an initiatization function to make sure
222 // we can return the failure appropriately
223 static nsresult
Dispatch(DOMRequest
* aRequest
, const JS::Value
& aResult
) {
224 RefPtr
<FireSuccessAsyncTask
> asyncTask
=
225 new FireSuccessAsyncTask(aRequest
, aResult
);
226 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(asyncTask
));
233 JS::Handle
<JS::Value
>::fromMarkedLocation(mResult
.address()));
238 RefPtr
<DOMRequest
> mReq
;
239 JS::PersistentRooted
<JS::Value
> mResult
;
242 class FireErrorAsyncTask
: public mozilla::Runnable
{
244 FireErrorAsyncTask(DOMRequest
* aRequest
, const nsAString
& aError
)
245 : mozilla::Runnable("FireErrorAsyncTask"),
251 mReq
->FireError(mError
);
256 RefPtr
<DOMRequest
> mReq
;
261 DOMRequestService::FireSuccessAsync(DOMRequest
* aRequest
,
262 JS::Handle
<JS::Value
> aResult
) {
263 NS_ENSURE_STATE(aRequest
);
264 return FireSuccessAsyncTask::Dispatch(aRequest
, aResult
);
268 DOMRequestService::FireErrorAsync(DOMRequest
* aRequest
,
269 const nsAString
& aError
) {
270 NS_ENSURE_STATE(aRequest
);
271 nsCOMPtr
<nsIRunnable
> asyncTask
= new FireErrorAsyncTask(aRequest
, aError
);
272 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(asyncTask
));