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() { mozilla::DropJSObjects(this); }
39 NS_IMPL_CYCLE_COLLECTION_CLASS(DOMRequest
)
41 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DOMRequest
,
43 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError
)
44 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromise
)
45 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
47 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DOMRequest
,
49 NS_IMPL_CYCLE_COLLECTION_UNLINK(mError
)
50 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromise
)
51 tmp
->mResult
.setUndefined();
52 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
54 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(DOMRequest
, DOMEventTargetHelper
)
55 // Don't need NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER because
56 // DOMEventTargetHelper does it for us.
57 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResult
)
58 NS_IMPL_CYCLE_COLLECTION_TRACE_END
60 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMRequest
)
61 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper
)
63 NS_IMPL_ADDREF_INHERITED(DOMRequest
, DOMEventTargetHelper
)
64 NS_IMPL_RELEASE_INHERITED(DOMRequest
, DOMEventTargetHelper
)
67 JSObject
* DOMRequest::WrapObject(JSContext
* aCx
,
68 JS::Handle
<JSObject
*> aGivenProto
) {
69 return DOMRequest_Binding::Wrap(aCx
, this, aGivenProto
);
72 void DOMRequest::FireSuccess(JS::Handle
<JS::Value
> aResult
) {
73 NS_ASSERTION(!mDone
, "mDone shouldn't have been set to true already!");
74 NS_ASSERTION(!mError
, "mError shouldn't have been set!");
75 NS_ASSERTION(mResult
.isUndefined(), "mResult shouldn't have been set!");
78 if (aResult
.isGCThing()) {
83 FireEvent(u
"success"_ns
, false, false);
86 mPromise
->MaybeResolve(mResult
);
90 void DOMRequest::FireError(const nsAString
& aError
) {
91 NS_ASSERTION(!mDone
, "mDone shouldn't have been set to true already!");
92 NS_ASSERTION(!mError
, "mError shouldn't have been set!");
93 NS_ASSERTION(mResult
.isUndefined(), "mResult shouldn't have been set!");
96 // XXX Error code chosen arbitrarily
97 mError
= DOMException::Create(NS_ERROR_DOM_UNKNOWN_ERR
,
98 NS_ConvertUTF16toUTF8(aError
));
100 FireEvent(u
"error"_ns
, true, true);
103 mPromise
->MaybeRejectBrokenly(mError
);
107 void DOMRequest::FireError(nsresult aError
) {
108 NS_ASSERTION(!mDone
, "mDone shouldn't have been set to true already!");
109 NS_ASSERTION(!mError
, "mError shouldn't have been set!");
110 NS_ASSERTION(mResult
.isUndefined(), "mResult shouldn't have been set!");
113 mError
= DOMException::Create(aError
);
115 FireEvent(u
"error"_ns
, true, true);
118 mPromise
->MaybeRejectBrokenly(mError
);
122 void DOMRequest::FireDetailedError(DOMException
& aError
) {
123 NS_ASSERTION(!mDone
, "mDone shouldn't have been set to true already!");
124 NS_ASSERTION(!mError
, "mError shouldn't have been set!");
125 NS_ASSERTION(mResult
.isUndefined(), "mResult shouldn't have been set!");
130 FireEvent(u
"error"_ns
, true, true);
133 mPromise
->MaybeRejectBrokenly(mError
);
137 void DOMRequest::FireEvent(const nsAString
& aType
, bool aBubble
,
139 if (NS_FAILED(CheckCurrentGlobalCorrectness())) {
143 RefPtr
<Event
> event
= NS_NewDOMEvent(this, nullptr, nullptr);
144 event
->InitEvent(aType
, aBubble
, aCancelable
);
145 event
->SetTrusted(true);
147 DispatchEvent(*event
);
150 void DOMRequest::RootResultVal() { mozilla::HoldJSObjects(this); }
152 void DOMRequest::Then(JSContext
* aCx
, AnyCallback
* aResolveCallback
,
153 AnyCallback
* aRejectCallback
,
154 JS::MutableHandle
<JS::Value
> aRetval
,
155 mozilla::ErrorResult
& aRv
) {
157 mPromise
= Promise::Create(DOMEventTargetHelper::GetParentObject(), aRv
);
162 // Since we create mPromise lazily, it's possible that the DOMRequest
163 // object has already fired its success/error event. In that case we
164 // should manually resolve/reject mPromise here. mPromise will take care
165 // of calling the callbacks on |promise| as needed.
167 mPromise
->MaybeRejectBrokenly(mError
);
169 mPromise
->MaybeResolve(mResult
);
174 // Just use the global of the Promise itself as the callee global.
175 JS::Rooted
<JSObject
*> global(aCx
, mPromise
->PromiseObj());
176 global
= JS::GetNonCCWObjectGlobal(global
);
177 mPromise
->Then(aCx
, global
, aResolveCallback
, aRejectCallback
, aRetval
, aRv
);
180 NS_IMPL_ISUPPORTS(DOMRequestService
, nsIDOMRequestService
)
183 DOMRequestService::CreateRequest(mozIDOMWindow
* aWindow
,
184 DOMRequest
** aRequest
) {
185 MOZ_ASSERT(NS_IsMainThread());
186 NS_ENSURE_STATE(aWindow
);
187 auto* win
= nsPIDOMWindowInner::From(aWindow
);
188 RefPtr
<DOMRequest
> req
= new DOMRequest(win
);
189 req
.forget(aRequest
);
195 DOMRequestService::FireSuccess(DOMRequest
* aRequest
,
196 JS::Handle
<JS::Value
> aResult
) {
197 NS_ENSURE_STATE(aRequest
);
198 aRequest
->FireSuccess(aResult
);
204 DOMRequestService::FireError(DOMRequest
* aRequest
, const nsAString
& aError
) {
205 NS_ENSURE_STATE(aRequest
);
206 aRequest
->FireError(aError
);
211 class FireSuccessAsyncTask
: public mozilla::Runnable
{
212 FireSuccessAsyncTask(DOMRequest
* aRequest
, const JS::Value
& aResult
)
213 : mozilla::Runnable("FireSuccessAsyncTask"),
215 mResult(RootingCx(), aResult
) {}
218 // Due to the fact that initialization can fail during shutdown (since we
219 // can't fetch a js context), set up an initiatization function to make sure
220 // we can return the failure appropriately
221 static nsresult
Dispatch(DOMRequest
* aRequest
, const JS::Value
& aResult
) {
222 RefPtr
<FireSuccessAsyncTask
> asyncTask
=
223 new FireSuccessAsyncTask(aRequest
, aResult
);
224 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(asyncTask
));
231 JS::Handle
<JS::Value
>::fromMarkedLocation(mResult
.address()));
236 RefPtr
<DOMRequest
> mReq
;
237 JS::PersistentRooted
<JS::Value
> mResult
;
240 class FireErrorAsyncTask
: public mozilla::Runnable
{
242 FireErrorAsyncTask(DOMRequest
* aRequest
, const nsAString
& aError
)
243 : mozilla::Runnable("FireErrorAsyncTask"),
249 mReq
->FireError(mError
);
254 RefPtr
<DOMRequest
> mReq
;
259 DOMRequestService::FireSuccessAsync(DOMRequest
* aRequest
,
260 JS::Handle
<JS::Value
> aResult
) {
261 NS_ENSURE_STATE(aRequest
);
262 return FireSuccessAsyncTask::Dispatch(aRequest
, aResult
);
266 DOMRequestService::FireErrorAsync(DOMRequest
* aRequest
,
267 const nsAString
& aError
) {
268 NS_ENSURE_STATE(aRequest
);
269 nsCOMPtr
<nsIRunnable
> asyncTask
= new FireErrorAsyncTask(aRequest
, aError
);
270 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(asyncTask
));