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
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "IDBRequest.h"
8
9 #include <utility>
10
11 #include "BackgroundChildImpl.h"
12 #include "IDBCursor.h"
13 #include "IDBDatabase.h"
14 #include "IDBEvents.h"
15 #include "IDBFactory.h"
16 #include "IDBIndex.h"
17 #include "IDBObjectStore.h"
18 #include "IDBTransaction.h"
19 #include "IndexedDatabaseManager.h"
20 #include "ReportInternalError.h"
21 #include "mozilla/ContentEvents.h"
22 #include "mozilla/ErrorResult.h"
23 #include "mozilla/EventDispatcher.h"
24 #include "mozilla/HoldDropJSObjects.h"
25 #include "mozilla/dom/DOMException.h"
26 #include "mozilla/dom/ErrorEventBinding.h"
27 #include "mozilla/dom/IDBOpenDBRequestBinding.h"
28 #include "mozilla/dom/ScriptSettings.h"
29 #include "mozilla/dom/WorkerPrivate.h"
30 #include "mozilla/dom/WorkerRef.h"
31 #include "nsCOMPtr.h"
32 #include "nsContentUtils.h"
33 #include "nsIGlobalObject.h"
34 #include "nsIScriptContext.h"
35 #include "nsJSUtils.h"
36 #include "nsString.h"
37 #include "ThreadLocal.h"
38
39 namespace mozilla::dom {
40
41 using namespace mozilla::dom::indexedDB;
42 using namespace mozilla::ipc;
43
44 namespace {
45
46 NS_DEFINE_IID(kIDBRequestIID, PRIVATE_IDBREQUEST_IID);
47
48 } // namespace
49
IDBRequest(IDBDatabase * aDatabase)50 IDBRequest::IDBRequest(IDBDatabase* aDatabase)
51 : DOMEventTargetHelper(aDatabase),
52 mLoggingSerialNumber(0),
53 mLineNo(0),
54 mColumn(0),
55 mHaveResultOrErrorCode(false) {
56 MOZ_ASSERT(aDatabase);
57 aDatabase->AssertIsOnOwningThread();
58
59 InitMembers();
60 }
61
IDBRequest(nsIGlobalObject * aGlobal)62 IDBRequest::IDBRequest(nsIGlobalObject* aGlobal)
63 : DOMEventTargetHelper(aGlobal),
64 mLoggingSerialNumber(0),
65 mLineNo(0),
66 mColumn(0),
67 mHaveResultOrErrorCode(false) {
68 InitMembers();
69 }
70
~IDBRequest()71 IDBRequest::~IDBRequest() {
72 AssertIsOnOwningThread();
73 mozilla::DropJSObjects(this);
74 }
75
InitMembers()76 void IDBRequest::InitMembers() {
77 AssertIsOnOwningThread();
78
79 mResultVal.setUndefined();
80 mLoggingSerialNumber = NextSerialNumber();
81 mErrorCode = NS_OK;
82 mLineNo = 0;
83 mColumn = 0;
84 mHaveResultOrErrorCode = false;
85 }
86
87 // static
Create(JSContext * aCx,IDBDatabase * aDatabase,SafeRefPtr<IDBTransaction> aTransaction)88 MovingNotNull<RefPtr<IDBRequest>> IDBRequest::Create(
89 JSContext* aCx, IDBDatabase* aDatabase,
90 SafeRefPtr<IDBTransaction> aTransaction) {
91 MOZ_ASSERT(aCx);
92 MOZ_ASSERT(aDatabase);
93 aDatabase->AssertIsOnOwningThread();
94
95 RefPtr<IDBRequest> request = new IDBRequest(aDatabase);
96 CaptureCaller(aCx, request->mFilename, &request->mLineNo, &request->mColumn);
97
98 request->mTransaction = std::move(aTransaction);
99
100 return WrapMovingNotNullUnchecked(std::move(request));
101 }
102
103 // static
Create(JSContext * aCx,IDBObjectStore * aSourceAsObjectStore,IDBDatabase * aDatabase,SafeRefPtr<IDBTransaction> aTransaction)104 MovingNotNull<RefPtr<IDBRequest>> IDBRequest::Create(
105 JSContext* aCx, IDBObjectStore* aSourceAsObjectStore,
106 IDBDatabase* aDatabase, SafeRefPtr<IDBTransaction> aTransaction) {
107 MOZ_ASSERT(aSourceAsObjectStore);
108 aSourceAsObjectStore->AssertIsOnOwningThread();
109
110 auto request =
111 Create(aCx, aDatabase, std::move(aTransaction)).unwrapBasePtr();
112
113 request->mSourceAsObjectStore = aSourceAsObjectStore;
114
115 return WrapMovingNotNullUnchecked(std::move(request));
116 }
117
118 // static
Create(JSContext * aCx,IDBIndex * aSourceAsIndex,IDBDatabase * aDatabase,SafeRefPtr<IDBTransaction> aTransaction)119 MovingNotNull<RefPtr<IDBRequest>> IDBRequest::Create(
120 JSContext* aCx, IDBIndex* aSourceAsIndex, IDBDatabase* aDatabase,
121 SafeRefPtr<IDBTransaction> aTransaction) {
122 MOZ_ASSERT(aSourceAsIndex);
123 aSourceAsIndex->AssertIsOnOwningThread();
124
125 auto request =
126 Create(aCx, aDatabase, std::move(aTransaction)).unwrapBasePtr();
127
128 request->mSourceAsIndex = aSourceAsIndex;
129
130 return WrapMovingNotNullUnchecked(std::move(request));
131 }
132
133 // static
NextSerialNumber()134 uint64_t IDBRequest::NextSerialNumber() {
135 BackgroundChildImpl::ThreadLocal* threadLocal =
136 BackgroundChildImpl::GetThreadLocalForCurrentThread();
137 MOZ_ASSERT(threadLocal);
138
139 const auto& idbThreadLocal = threadLocal->mIndexedDBThreadLocal;
140 MOZ_ASSERT(idbThreadLocal);
141
142 return idbThreadLocal->NextRequestSN();
143 }
144
SetLoggingSerialNumber(uint64_t aLoggingSerialNumber)145 void IDBRequest::SetLoggingSerialNumber(uint64_t aLoggingSerialNumber) {
146 AssertIsOnOwningThread();
147 MOZ_ASSERT(aLoggingSerialNumber > mLoggingSerialNumber);
148
149 mLoggingSerialNumber = aLoggingSerialNumber;
150 }
151
CaptureCaller(JSContext * aCx,nsAString & aFilename,uint32_t * aLineNo,uint32_t * aColumn)152 void IDBRequest::CaptureCaller(JSContext* aCx, nsAString& aFilename,
153 uint32_t* aLineNo, uint32_t* aColumn) {
154 MOZ_ASSERT(aFilename.IsEmpty());
155 MOZ_ASSERT(aLineNo);
156 MOZ_ASSERT(aColumn);
157
158 nsJSUtils::GetCallingLocation(aCx, aFilename, aLineNo, aColumn);
159 }
160
GetSource(Nullable<OwningIDBObjectStoreOrIDBIndexOrIDBCursor> & aSource) const161 void IDBRequest::GetSource(
162 Nullable<OwningIDBObjectStoreOrIDBIndexOrIDBCursor>& aSource) const {
163 AssertIsOnOwningThread();
164
165 MOZ_ASSERT_IF(mSourceAsObjectStore, !mSourceAsIndex);
166 MOZ_ASSERT_IF(mSourceAsIndex, !mSourceAsObjectStore);
167 MOZ_ASSERT_IF(mSourceAsCursor, mSourceAsObjectStore || mSourceAsIndex);
168
169 // Always check cursor first since cursor requests hold both the cursor and
170 // the objectStore or index the cursor came from.
171 if (mSourceAsCursor) {
172 aSource.SetValue().SetAsIDBCursor() = mSourceAsCursor;
173 } else if (mSourceAsObjectStore) {
174 aSource.SetValue().SetAsIDBObjectStore() = mSourceAsObjectStore;
175 } else if (mSourceAsIndex) {
176 aSource.SetValue().SetAsIDBIndex() = mSourceAsIndex;
177 } else {
178 aSource.SetNull();
179 }
180 }
181
Reset()182 void IDBRequest::Reset() {
183 AssertIsOnOwningThread();
184
185 mResultVal.setUndefined();
186
187 mHaveResultOrErrorCode = false;
188 mError = nullptr;
189 }
190
SetError(nsresult aRv)191 void IDBRequest::SetError(nsresult aRv) {
192 AssertIsOnOwningThread();
193 MOZ_ASSERT(NS_FAILED(aRv));
194 MOZ_ASSERT(NS_ERROR_GET_MODULE(aRv) == NS_ERROR_MODULE_DOM_INDEXEDDB);
195 MOZ_ASSERT(!mError);
196
197 mHaveResultOrErrorCode = true;
198 mError = DOMException::Create(aRv);
199 mErrorCode = aRv;
200
201 mResultVal.setUndefined();
202 }
203
204 #ifdef DEBUG
205
GetErrorCode() const206 nsresult IDBRequest::GetErrorCode() const {
207 AssertIsOnOwningThread();
208 MOZ_ASSERT(mHaveResultOrErrorCode);
209
210 return mErrorCode;
211 }
212
GetErrorAfterResult() const213 DOMException* IDBRequest::GetErrorAfterResult() const {
214 AssertIsOnOwningThread();
215 MOZ_ASSERT(mHaveResultOrErrorCode);
216
217 return mError;
218 }
219
220 #endif // DEBUG
221
GetCallerLocation(nsAString & aFilename,uint32_t * aLineNo,uint32_t * aColumn) const222 void IDBRequest::GetCallerLocation(nsAString& aFilename, uint32_t* aLineNo,
223 uint32_t* aColumn) const {
224 AssertIsOnOwningThread();
225 MOZ_ASSERT(aLineNo);
226 MOZ_ASSERT(aColumn);
227
228 aFilename = mFilename;
229 *aLineNo = mLineNo;
230 *aColumn = mColumn;
231 }
232
ReadyState() const233 IDBRequestReadyState IDBRequest::ReadyState() const {
234 AssertIsOnOwningThread();
235
236 return IsPending() ? IDBRequestReadyState::Pending
237 : IDBRequestReadyState::Done;
238 }
239
SetSource(IDBCursor * aSource)240 void IDBRequest::SetSource(IDBCursor* aSource) {
241 AssertIsOnOwningThread();
242 MOZ_ASSERT(aSource);
243 MOZ_ASSERT(mSourceAsObjectStore || mSourceAsIndex);
244 MOZ_ASSERT(!mSourceAsCursor);
245
246 mSourceAsCursor = aSource;
247 }
248
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)249 JSObject* IDBRequest::WrapObject(JSContext* aCx,
250 JS::Handle<JSObject*> aGivenProto) {
251 return IDBRequest_Binding::Wrap(aCx, this, aGivenProto);
252 }
253
GetResult(JS::MutableHandle<JS::Value> aResult,ErrorResult & aRv) const254 void IDBRequest::GetResult(JS::MutableHandle<JS::Value> aResult,
255 ErrorResult& aRv) const {
256 AssertIsOnOwningThread();
257
258 if (!mHaveResultOrErrorCode) {
259 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
260 return;
261 }
262
263 aResult.set(mResultVal);
264 }
265
GetError(ErrorResult & aRv)266 DOMException* IDBRequest::GetError(ErrorResult& aRv) {
267 AssertIsOnOwningThread();
268
269 if (!mHaveResultOrErrorCode) {
270 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
271 return nullptr;
272 }
273
274 return mError;
275 }
276
277 NS_IMPL_CYCLE_COLLECTION_MULTI_ZONE_JSHOLDER_CLASS(IDBRequest)
278
279 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBRequest,
280 DOMEventTargetHelper)
281 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSourceAsObjectStore)
282 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSourceAsIndex)
283 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSourceAsCursor)
284 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransaction)
285 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError)
286 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
287
288 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBRequest,
289 DOMEventTargetHelper)
290 mozilla::DropJSObjects(tmp);
291 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSourceAsObjectStore)
292 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSourceAsIndex)
293 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSourceAsCursor)
294 NS_IMPL_CYCLE_COLLECTION_UNLINK(mTransaction)
295 NS_IMPL_CYCLE_COLLECTION_UNLINK(mError)
296 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
297
298 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(IDBRequest, DOMEventTargetHelper)
299 // Don't need NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER because
300 // DOMEventTargetHelper does it for us.
301 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResultVal)
302 NS_IMPL_CYCLE_COLLECTION_TRACE_END
303
304 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBRequest)
305 if (aIID.Equals(kIDBRequestIID)) {
306 foundInterface = this;
307 } else
308 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
309
NS_IMPL_ADDREF_INHERITED(IDBRequest,DOMEventTargetHelper)310 NS_IMPL_ADDREF_INHERITED(IDBRequest, DOMEventTargetHelper)
311 NS_IMPL_RELEASE_INHERITED(IDBRequest, DOMEventTargetHelper)
312
313 void IDBRequest::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
314 AssertIsOnOwningThread();
315
316 aVisitor.mCanHandle = true;
317 aVisitor.SetParentTarget(mTransaction.unsafeGetRawPtr(), false);
318 }
319
IDBOpenDBRequest(SafeRefPtr<IDBFactory> aFactory,nsIGlobalObject * aGlobal,bool aFileHandleDisabled)320 IDBOpenDBRequest::IDBOpenDBRequest(SafeRefPtr<IDBFactory> aFactory,
321 nsIGlobalObject* aGlobal,
322 bool aFileHandleDisabled)
323 : IDBRequest(aGlobal),
324 mFactory(std::move(aFactory)),
325 mFileHandleDisabled(aFileHandleDisabled),
326 mIncreasedActiveDatabaseCount(false) {
327 AssertIsOnOwningThread();
328 MOZ_ASSERT(mFactory);
329 MOZ_ASSERT(aGlobal);
330 }
331
~IDBOpenDBRequest()332 IDBOpenDBRequest::~IDBOpenDBRequest() {
333 AssertIsOnOwningThread();
334 MOZ_ASSERT(!mIncreasedActiveDatabaseCount);
335 }
336
337 // static
Create(JSContext * aCx,SafeRefPtr<IDBFactory> aFactory,nsIGlobalObject * aGlobal)338 RefPtr<IDBOpenDBRequest> IDBOpenDBRequest::Create(
339 JSContext* aCx, SafeRefPtr<IDBFactory> aFactory, nsIGlobalObject* aGlobal) {
340 MOZ_ASSERT(aFactory);
341 aFactory->AssertIsOnOwningThread();
342 MOZ_ASSERT(aGlobal);
343
344 bool fileHandleDisabled = !IndexedDatabaseManager::IsFileHandleEnabled();
345
346 RefPtr<IDBOpenDBRequest> request =
347 new IDBOpenDBRequest(std::move(aFactory), aGlobal, fileHandleDisabled);
348 CaptureCaller(aCx, request->mFilename, &request->mLineNo, &request->mColumn);
349
350 if (!NS_IsMainThread()) {
351 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
352 MOZ_ASSERT(workerPrivate);
353
354 workerPrivate->AssertIsOnWorkerThread();
355
356 request->mWorkerRef =
357 StrongWorkerRef::Create(workerPrivate, "IDBOpenDBRequest");
358 if (NS_WARN_IF(!request->mWorkerRef)) {
359 return nullptr;
360 }
361 }
362
363 request->IncreaseActiveDatabaseCount();
364
365 return request;
366 }
367
SetTransaction(SafeRefPtr<IDBTransaction> aTransaction)368 void IDBOpenDBRequest::SetTransaction(SafeRefPtr<IDBTransaction> aTransaction) {
369 AssertIsOnOwningThread();
370
371 MOZ_ASSERT(!aTransaction || !mTransaction);
372
373 mTransaction = std::move(aTransaction);
374 }
375
DispatchNonTransactionError(nsresult aErrorCode)376 void IDBOpenDBRequest::DispatchNonTransactionError(nsresult aErrorCode) {
377 AssertIsOnOwningThread();
378 MOZ_ASSERT(NS_FAILED(aErrorCode));
379 MOZ_ASSERT(NS_ERROR_GET_MODULE(aErrorCode) == NS_ERROR_MODULE_DOM_INDEXEDDB);
380
381 // The actor failed to initiate, decrease the number of active IDBOpenRequests
382 // here since NoteComplete won't be called.
383 MaybeDecreaseActiveDatabaseCount();
384
385 SetError(aErrorCode);
386
387 // Make an error event and fire it at the target.
388 auto event = CreateGenericEvent(this, nsDependentString(kErrorEventType),
389 eDoesBubble, eCancelable);
390
391 IgnoredErrorResult rv;
392 DispatchEvent(*event, rv);
393 if (rv.Failed()) {
394 NS_WARNING("Failed to dispatch event!");
395 }
396 }
397
NoteComplete()398 void IDBOpenDBRequest::NoteComplete() {
399 AssertIsOnOwningThread();
400 MOZ_ASSERT_IF(!NS_IsMainThread(), mWorkerRef);
401
402 // Normally, we decrease the number of active IDBOpenRequests here.
403 MaybeDecreaseActiveDatabaseCount();
404
405 // If we have a WorkerRef, then nulling this out will release the worker.
406 mWorkerRef = nullptr;
407 }
408
IncreaseActiveDatabaseCount()409 void IDBOpenDBRequest::IncreaseActiveDatabaseCount() {
410 AssertIsOnOwningThread();
411 MOZ_ASSERT(!mIncreasedActiveDatabaseCount);
412
413 // Increase the number of active IDBOpenRequests.
414 // Note: We count here instead of the actor's ctor because the preemption
415 // could happen at next JS interrupt but its BackgroundFactoryRequestChild
416 // could be created asynchronously from IDBFactory::BackgroundCreateCallback
417 // ::ActorCreated() if its PBackgroundChild is not created yet on this thread.
418 mFactory->UpdateActiveDatabaseCount(1);
419 mIncreasedActiveDatabaseCount = true;
420 }
421
MaybeDecreaseActiveDatabaseCount()422 void IDBOpenDBRequest::MaybeDecreaseActiveDatabaseCount() {
423 AssertIsOnOwningThread();
424
425 if (mIncreasedActiveDatabaseCount) {
426 // Decrease the number of active IDBOpenRequests.
427 mFactory->UpdateActiveDatabaseCount(-1);
428 mIncreasedActiveDatabaseCount = false;
429 }
430 }
431
432 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBOpenDBRequest)
433
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBOpenDBRequest,IDBRequest)434 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBOpenDBRequest, IDBRequest)
435 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFactory)
436 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
437
438 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBOpenDBRequest, IDBRequest)
439 // Don't unlink mFactory!
440 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
441
442 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBOpenDBRequest)
443 NS_INTERFACE_MAP_END_INHERITING(IDBRequest)
444
445 NS_IMPL_ADDREF_INHERITED(IDBOpenDBRequest, IDBRequest)
446 NS_IMPL_RELEASE_INHERITED(IDBOpenDBRequest, IDBRequest)
447
448 nsresult IDBOpenDBRequest::PostHandleEvent(EventChainPostVisitor& aVisitor) {
449 nsresult rv =
450 IndexedDatabaseManager::CommonPostHandleEvent(aVisitor, *mFactory);
451 if (NS_WARN_IF(NS_FAILED(rv))) {
452 return rv;
453 }
454
455 return NS_OK;
456 }
457
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)458 JSObject* IDBOpenDBRequest::WrapObject(JSContext* aCx,
459 JS::Handle<JSObject*> aGivenProto) {
460 AssertIsOnOwningThread();
461
462 return IDBOpenDBRequest_Binding::Wrap(aCx, this, aGivenProto);
463 }
464
465 } // namespace mozilla::dom
466