Bug 1750871 - run mochitest-remote on fission everywhere. r=releng-reviewers,aki
[gecko.git] / dom / base / DOMRequest.cpp
blob91ec9b65721ffcd4482d05367c0ac196f614a151
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()),
30 mDone(false) {}
32 DOMRequest::DOMRequest(nsIGlobalObject* aGlobal)
33 : DOMEventTargetHelper(aGlobal),
34 mResult(JS::UndefinedValue()),
35 mDone(false) {}
37 DOMRequest::~DOMRequest() { mozilla::DropJSObjects(this); }
39 NS_IMPL_CYCLE_COLLECTION_CLASS(DOMRequest)
41 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DOMRequest,
42 DOMEventTargetHelper)
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,
48 DOMEventTargetHelper)
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)
66 /* virtual */
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!");
77 mDone = true;
78 if (aResult.isGCThing()) {
79 RootResultVal();
81 mResult = aResult;
83 FireEvent(u"success"_ns, false, false);
85 if (mPromise) {
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!");
95 mDone = true;
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);
102 if (mPromise) {
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!");
112 mDone = true;
113 mError = DOMException::Create(aError);
115 FireEvent(u"error"_ns, true, true);
117 if (mPromise) {
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!");
127 mDone = true;
128 mError = &aError;
130 FireEvent(u"error"_ns, true, true);
132 if (mPromise) {
133 mPromise->MaybeRejectBrokenly(mError);
137 void DOMRequest::FireEvent(const nsAString& aType, bool aBubble,
138 bool aCancelable) {
139 if (NS_FAILED(CheckCurrentGlobalCorrectness())) {
140 return;
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) {
156 if (!mPromise) {
157 mPromise = Promise::Create(DOMEventTargetHelper::GetParentObject(), aRv);
158 if (aRv.Failed()) {
159 return;
161 if (mDone) {
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.
166 if (mError) {
167 mPromise->MaybeRejectBrokenly(mError);
168 } else {
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)
182 NS_IMETHODIMP
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);
191 return NS_OK;
194 NS_IMETHODIMP
195 DOMRequestService::FireSuccess(DOMRequest* aRequest,
196 JS::Handle<JS::Value> aResult) {
197 NS_ENSURE_STATE(aRequest);
198 aRequest->FireSuccess(aResult);
200 return NS_OK;
203 NS_IMETHODIMP
204 DOMRequestService::FireError(DOMRequest* aRequest, const nsAString& aError) {
205 NS_ENSURE_STATE(aRequest);
206 aRequest->FireError(aError);
208 return NS_OK;
211 class FireSuccessAsyncTask : public mozilla::Runnable {
212 FireSuccessAsyncTask(DOMRequest* aRequest, const JS::Value& aResult)
213 : mozilla::Runnable("FireSuccessAsyncTask"),
214 mReq(aRequest),
215 mResult(RootingCx(), aResult) {}
217 public:
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));
225 return NS_OK;
228 NS_IMETHOD
229 Run() override {
230 mReq->FireSuccess(
231 JS::Handle<JS::Value>::fromMarkedLocation(mResult.address()));
232 return NS_OK;
235 private:
236 RefPtr<DOMRequest> mReq;
237 JS::PersistentRooted<JS::Value> mResult;
240 class FireErrorAsyncTask : public mozilla::Runnable {
241 public:
242 FireErrorAsyncTask(DOMRequest* aRequest, const nsAString& aError)
243 : mozilla::Runnable("FireErrorAsyncTask"),
244 mReq(aRequest),
245 mError(aError) {}
247 NS_IMETHOD
248 Run() override {
249 mReq->FireError(mError);
250 return NS_OK;
253 private:
254 RefPtr<DOMRequest> mReq;
255 nsString mError;
258 NS_IMETHODIMP
259 DOMRequestService::FireSuccessAsync(DOMRequest* aRequest,
260 JS::Handle<JS::Value> aResult) {
261 NS_ENSURE_STATE(aRequest);
262 return FireSuccessAsyncTask::Dispatch(aRequest, aResult);
265 NS_IMETHODIMP
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));
271 return NS_OK;