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_INHERITED_WITH_JS_MEMBERS(DOMRequest
,
44 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMRequest
)
45 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper
)
47 NS_IMPL_ADDREF_INHERITED(DOMRequest
, DOMEventTargetHelper
)
48 NS_IMPL_RELEASE_INHERITED(DOMRequest
, DOMEventTargetHelper
)
51 JSObject
* DOMRequest::WrapObject(JSContext
* aCx
,
52 JS::Handle
<JSObject
*> aGivenProto
) {
53 return DOMRequest_Binding::Wrap(aCx
, this, aGivenProto
);
56 void DOMRequest::FireSuccess(JS::Handle
<JS::Value
> aResult
) {
57 NS_ASSERTION(!mDone
, "mDone shouldn't have been set to true already!");
58 NS_ASSERTION(!mError
, "mError shouldn't have been set!");
59 NS_ASSERTION(mResult
.isUndefined(), "mResult shouldn't have been set!");
62 if (aResult
.isGCThing()) {
67 FireEvent(u
"success"_ns
, false, false);
70 mPromise
->MaybeResolve(mResult
);
74 void DOMRequest::FireError(const nsAString
& aError
) {
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 // XXX Error code chosen arbitrarily
81 mError
= DOMException::Create(NS_ERROR_DOM_UNKNOWN_ERR
,
82 NS_ConvertUTF16toUTF8(aError
));
84 FireEvent(u
"error"_ns
, true, true);
87 mPromise
->MaybeRejectBrokenly(mError
);
91 void DOMRequest::FireError(nsresult aError
) {
92 NS_ASSERTION(!mDone
, "mDone shouldn't have been set to true already!");
93 NS_ASSERTION(!mError
, "mError shouldn't have been set!");
94 NS_ASSERTION(mResult
.isUndefined(), "mResult shouldn't have been set!");
97 mError
= DOMException::Create(aError
);
99 FireEvent(u
"error"_ns
, true, true);
102 mPromise
->MaybeRejectBrokenly(mError
);
106 void DOMRequest::FireDetailedError(DOMException
& aError
) {
107 NS_ASSERTION(!mDone
, "mDone shouldn't have been set to true already!");
108 NS_ASSERTION(!mError
, "mError shouldn't have been set!");
109 NS_ASSERTION(mResult
.isUndefined(), "mResult shouldn't have been set!");
114 FireEvent(u
"error"_ns
, true, true);
117 mPromise
->MaybeRejectBrokenly(mError
);
121 void DOMRequest::FireEvent(const nsAString
& aType
, bool aBubble
,
123 if (NS_FAILED(CheckCurrentGlobalCorrectness())) {
127 RefPtr
<Event
> event
= NS_NewDOMEvent(this, nullptr, nullptr);
128 event
->InitEvent(aType
, aBubble
, aCancelable
);
129 event
->SetTrusted(true);
131 DispatchEvent(*event
);
134 void DOMRequest::RootResultVal() { mozilla::HoldJSObjects(this); }
136 void DOMRequest::Then(JSContext
* aCx
, AnyCallback
* aResolveCallback
,
137 AnyCallback
* aRejectCallback
,
138 JS::MutableHandle
<JS::Value
> aRetval
,
139 mozilla::ErrorResult
& aRv
) {
141 mPromise
= Promise::Create(DOMEventTargetHelper::GetParentObject(), aRv
);
146 // Since we create mPromise lazily, it's possible that the DOMRequest
147 // object has already fired its success/error event. In that case we
148 // should manually resolve/reject mPromise here. mPromise will take care
149 // of calling the callbacks on |promise| as needed.
151 mPromise
->MaybeRejectBrokenly(mError
);
153 mPromise
->MaybeResolve(mResult
);
158 // Just use the global of the Promise itself as the callee global.
159 JS::Rooted
<JSObject
*> global(aCx
, mPromise
->PromiseObj());
160 global
= JS::GetNonCCWObjectGlobal(global
);
161 mPromise
->Then(aCx
, global
, aResolveCallback
, aRejectCallback
, aRetval
, aRv
);
164 NS_IMPL_ISUPPORTS(DOMRequestService
, nsIDOMRequestService
)
167 DOMRequestService::CreateRequest(mozIDOMWindow
* aWindow
,
168 DOMRequest
** aRequest
) {
169 MOZ_ASSERT(NS_IsMainThread());
170 NS_ENSURE_STATE(aWindow
);
171 auto* win
= nsPIDOMWindowInner::From(aWindow
);
172 RefPtr
<DOMRequest
> req
= new DOMRequest(win
);
173 req
.forget(aRequest
);
179 DOMRequestService::FireSuccess(DOMRequest
* aRequest
,
180 JS::Handle
<JS::Value
> aResult
) {
181 NS_ENSURE_STATE(aRequest
);
182 aRequest
->FireSuccess(aResult
);
188 DOMRequestService::FireError(DOMRequest
* aRequest
, const nsAString
& aError
) {
189 NS_ENSURE_STATE(aRequest
);
190 aRequest
->FireError(aError
);
195 class FireSuccessAsyncTask
: public mozilla::Runnable
{
196 FireSuccessAsyncTask(DOMRequest
* aRequest
, const JS::Value
& aResult
)
197 : mozilla::Runnable("FireSuccessAsyncTask"),
199 mResult(RootingCx(), aResult
) {}
202 // Due to the fact that initialization can fail during shutdown (since we
203 // can't fetch a js context), set up an initiatization function to make sure
204 // we can return the failure appropriately
205 static nsresult
Dispatch(DOMRequest
* aRequest
, const JS::Value
& aResult
) {
206 RefPtr
<FireSuccessAsyncTask
> asyncTask
=
207 new FireSuccessAsyncTask(aRequest
, aResult
);
208 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(asyncTask
));
215 JS::Handle
<JS::Value
>::fromMarkedLocation(mResult
.address()));
220 RefPtr
<DOMRequest
> mReq
;
221 JS::PersistentRooted
<JS::Value
> mResult
;
224 class FireErrorAsyncTask
: public mozilla::Runnable
{
226 FireErrorAsyncTask(DOMRequest
* aRequest
, const nsAString
& aError
)
227 : mozilla::Runnable("FireErrorAsyncTask"),
233 mReq
->FireError(mError
);
238 RefPtr
<DOMRequest
> mReq
;
243 DOMRequestService::FireSuccessAsync(DOMRequest
* aRequest
,
244 JS::Handle
<JS::Value
> aResult
) {
245 NS_ENSURE_STATE(aRequest
);
246 return FireSuccessAsyncTask::Dispatch(aRequest
, aResult
);
250 DOMRequestService::FireErrorAsync(DOMRequest
* aRequest
,
251 const nsAString
& aError
) {
252 NS_ENSURE_STATE(aRequest
);
253 nsCOMPtr
<nsIRunnable
> asyncTask
= new FireErrorAsyncTask(aRequest
, aError
);
254 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(asyncTask
));