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/. */
7 #include "IDBRequest.h"
11 #include "BackgroundChildImpl.h"
12 #include "IDBCursor.h"
13 #include "IDBDatabase.h"
14 #include "IDBEvents.h"
15 #include "IDBFactory.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"
32 #include "nsContentUtils.h"
33 #include "nsIGlobalObject.h"
34 #include "nsIScriptContext.h"
35 #include "nsJSUtils.h"
37 #include "ThreadLocal.h"
39 namespace mozilla::dom
{
41 using namespace mozilla::dom::indexedDB
;
42 using namespace mozilla::ipc
;
44 IDBRequest::IDBRequest(IDBDatabase
* aDatabase
)
45 : DOMEventTargetHelper(aDatabase
),
46 mLoggingSerialNumber(0),
49 mHaveResultOrErrorCode(false) {
50 MOZ_ASSERT(aDatabase
);
51 aDatabase
->AssertIsOnOwningThread();
56 IDBRequest::IDBRequest(nsIGlobalObject
* aGlobal
)
57 : DOMEventTargetHelper(aGlobal
),
58 mLoggingSerialNumber(0),
61 mHaveResultOrErrorCode(false) {
65 IDBRequest::~IDBRequest() {
66 AssertIsOnOwningThread();
67 mozilla::DropJSObjects(this);
70 void IDBRequest::InitMembers() {
71 AssertIsOnOwningThread();
73 mResultVal
.setUndefined();
74 mLoggingSerialNumber
= NextSerialNumber();
78 mHaveResultOrErrorCode
= false;
82 MovingNotNull
<RefPtr
<IDBRequest
>> IDBRequest::Create(
83 JSContext
* aCx
, IDBDatabase
* aDatabase
,
84 SafeRefPtr
<IDBTransaction
> aTransaction
) {
86 MOZ_ASSERT(aDatabase
);
87 aDatabase
->AssertIsOnOwningThread();
89 RefPtr
<IDBRequest
> request
= new IDBRequest(aDatabase
);
90 CaptureCaller(aCx
, request
->mFilename
, &request
->mLineNo
, &request
->mColumn
);
92 request
->mTransaction
= std::move(aTransaction
);
94 return WrapMovingNotNullUnchecked(std::move(request
));
98 MovingNotNull
<RefPtr
<IDBRequest
>> IDBRequest::Create(
99 JSContext
* aCx
, IDBObjectStore
* aSourceAsObjectStore
,
100 IDBDatabase
* aDatabase
, SafeRefPtr
<IDBTransaction
> aTransaction
) {
101 MOZ_ASSERT(aSourceAsObjectStore
);
102 aSourceAsObjectStore
->AssertIsOnOwningThread();
105 Create(aCx
, aDatabase
, std::move(aTransaction
)).unwrapBasePtr();
107 request
->mSourceAsObjectStore
= aSourceAsObjectStore
;
109 return WrapMovingNotNullUnchecked(std::move(request
));
113 MovingNotNull
<RefPtr
<IDBRequest
>> IDBRequest::Create(
114 JSContext
* aCx
, IDBIndex
* aSourceAsIndex
, IDBDatabase
* aDatabase
,
115 SafeRefPtr
<IDBTransaction
> aTransaction
) {
116 MOZ_ASSERT(aSourceAsIndex
);
117 aSourceAsIndex
->AssertIsOnOwningThread();
120 Create(aCx
, aDatabase
, std::move(aTransaction
)).unwrapBasePtr();
122 request
->mSourceAsIndex
= aSourceAsIndex
;
124 return WrapMovingNotNullUnchecked(std::move(request
));
128 uint64_t IDBRequest::NextSerialNumber() {
129 BackgroundChildImpl::ThreadLocal
* threadLocal
=
130 BackgroundChildImpl::GetThreadLocalForCurrentThread();
131 MOZ_ASSERT(threadLocal
);
133 const auto& idbThreadLocal
= threadLocal
->mIndexedDBThreadLocal
;
134 MOZ_ASSERT(idbThreadLocal
);
136 return idbThreadLocal
->NextRequestSN();
139 void IDBRequest::SetLoggingSerialNumber(uint64_t aLoggingSerialNumber
) {
140 AssertIsOnOwningThread();
141 MOZ_ASSERT(aLoggingSerialNumber
> mLoggingSerialNumber
);
143 mLoggingSerialNumber
= aLoggingSerialNumber
;
146 void IDBRequest::CaptureCaller(JSContext
* aCx
, nsAString
& aFilename
,
147 uint32_t* aLineNo
, uint32_t* aColumn
) {
148 MOZ_ASSERT(aFilename
.IsEmpty());
152 nsJSUtils::GetCallingLocation(aCx
, aFilename
, aLineNo
, aColumn
);
155 void IDBRequest::GetSource(
156 Nullable
<OwningIDBObjectStoreOrIDBIndexOrIDBCursor
>& aSource
) const {
157 AssertIsOnOwningThread();
159 MOZ_ASSERT_IF(mSourceAsObjectStore
, !mSourceAsIndex
);
160 MOZ_ASSERT_IF(mSourceAsIndex
, !mSourceAsObjectStore
);
161 MOZ_ASSERT_IF(mSourceAsCursor
, mSourceAsObjectStore
|| mSourceAsIndex
);
163 // Always check cursor first since cursor requests hold both the cursor and
164 // the objectStore or index the cursor came from.
165 if (mSourceAsCursor
) {
166 aSource
.SetValue().SetAsIDBCursor() = mSourceAsCursor
;
167 } else if (mSourceAsObjectStore
) {
168 aSource
.SetValue().SetAsIDBObjectStore() = mSourceAsObjectStore
;
169 } else if (mSourceAsIndex
) {
170 aSource
.SetValue().SetAsIDBIndex() = mSourceAsIndex
;
176 void IDBRequest::Reset() {
177 AssertIsOnOwningThread();
179 mResultVal
.setUndefined();
181 mHaveResultOrErrorCode
= false;
185 void IDBRequest::SetError(nsresult aRv
) {
186 AssertIsOnOwningThread();
187 MOZ_ASSERT(NS_FAILED(aRv
));
188 MOZ_ASSERT(NS_ERROR_GET_MODULE(aRv
) == NS_ERROR_MODULE_DOM_INDEXEDDB
);
191 mHaveResultOrErrorCode
= true;
192 mError
= DOMException::Create(aRv
);
195 mResultVal
.setUndefined();
200 nsresult
IDBRequest::GetErrorCode() const {
201 AssertIsOnOwningThread();
202 MOZ_ASSERT(mHaveResultOrErrorCode
);
207 DOMException
* IDBRequest::GetErrorAfterResult() const {
208 AssertIsOnOwningThread();
209 MOZ_ASSERT(mHaveResultOrErrorCode
);
216 void IDBRequest::GetCallerLocation(nsAString
& aFilename
, uint32_t* aLineNo
,
217 uint32_t* aColumn
) const {
218 AssertIsOnOwningThread();
222 aFilename
= mFilename
;
227 IDBRequestReadyState
IDBRequest::ReadyState() const {
228 AssertIsOnOwningThread();
230 return IsPending() ? IDBRequestReadyState::Pending
231 : IDBRequestReadyState::Done
;
234 void IDBRequest::SetSource(IDBCursor
* aSource
) {
235 AssertIsOnOwningThread();
237 MOZ_ASSERT(mSourceAsObjectStore
|| mSourceAsIndex
);
238 MOZ_ASSERT(!mSourceAsCursor
);
240 mSourceAsCursor
= aSource
;
243 JSObject
* IDBRequest::WrapObject(JSContext
* aCx
,
244 JS::Handle
<JSObject
*> aGivenProto
) {
245 return IDBRequest_Binding::Wrap(aCx
, this, aGivenProto
);
248 void IDBRequest::GetResult(JS::MutableHandle
<JS::Value
> aResult
,
249 ErrorResult
& aRv
) const {
250 AssertIsOnOwningThread();
252 if (!mHaveResultOrErrorCode
) {
253 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
257 aResult
.set(mResultVal
);
260 DOMException
* IDBRequest::GetError(ErrorResult
& aRv
) {
261 AssertIsOnOwningThread();
263 if (!mHaveResultOrErrorCode
) {
264 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
271 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBRequest
)
273 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBRequest
,
274 DOMEventTargetHelper
)
275 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSourceAsObjectStore
)
276 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSourceAsIndex
)
277 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSourceAsCursor
)
278 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransaction
)
279 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError
)
280 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
282 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBRequest
,
283 DOMEventTargetHelper
)
284 mozilla::DropJSObjects(tmp
);
285 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSourceAsObjectStore
)
286 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSourceAsIndex
)
287 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSourceAsCursor
)
288 NS_IMPL_CYCLE_COLLECTION_UNLINK(mTransaction
)
289 NS_IMPL_CYCLE_COLLECTION_UNLINK(mError
)
290 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
292 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(IDBRequest
, DOMEventTargetHelper
)
293 // Don't need NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER because
294 // DOMEventTargetHelper does it for us.
295 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResultVal
)
296 NS_IMPL_CYCLE_COLLECTION_TRACE_END
298 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBRequest
)
299 if (aIID
.Equals(NS_GET_IID(mozilla::dom::detail::PrivateIDBRequest
))) {
300 foundInterface
= static_cast<EventTarget
*>(this);
302 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper
)
304 NS_IMPL_ADDREF_INHERITED(IDBRequest
, DOMEventTargetHelper
)
305 NS_IMPL_RELEASE_INHERITED(IDBRequest
, DOMEventTargetHelper
)
307 void IDBRequest::GetEventTargetParent(EventChainPreVisitor
& aVisitor
) {
308 AssertIsOnOwningThread();
310 aVisitor
.mCanHandle
= true;
311 aVisitor
.SetParentTarget(mTransaction
.unsafeGetRawPtr(), false);
314 IDBOpenDBRequest::IDBOpenDBRequest(SafeRefPtr
<IDBFactory
> aFactory
,
315 nsIGlobalObject
* aGlobal
)
316 : IDBRequest(aGlobal
),
317 mFactory(std::move(aFactory
)),
318 mIncreasedActiveDatabaseCount(false) {
319 AssertIsOnOwningThread();
320 MOZ_ASSERT(mFactory
);
324 IDBOpenDBRequest::~IDBOpenDBRequest() {
325 AssertIsOnOwningThread();
326 MOZ_ASSERT(!mIncreasedActiveDatabaseCount
);
330 RefPtr
<IDBOpenDBRequest
> IDBOpenDBRequest::Create(
331 JSContext
* aCx
, SafeRefPtr
<IDBFactory
> aFactory
, nsIGlobalObject
* aGlobal
) {
332 MOZ_ASSERT(aFactory
);
333 aFactory
->AssertIsOnOwningThread();
336 RefPtr
<IDBOpenDBRequest
> request
=
337 new IDBOpenDBRequest(std::move(aFactory
), aGlobal
);
338 CaptureCaller(aCx
, request
->mFilename
, &request
->mLineNo
, &request
->mColumn
);
340 if (!NS_IsMainThread()) {
341 WorkerPrivate
* workerPrivate
= GetCurrentThreadWorkerPrivate();
342 MOZ_ASSERT(workerPrivate
);
344 workerPrivate
->AssertIsOnWorkerThread();
346 request
->mWorkerRef
=
347 StrongWorkerRef::Create(workerPrivate
, "IDBOpenDBRequest");
348 if (NS_WARN_IF(!request
->mWorkerRef
)) {
353 request
->IncreaseActiveDatabaseCount();
358 void IDBOpenDBRequest::SetTransaction(SafeRefPtr
<IDBTransaction
> aTransaction
) {
359 AssertIsOnOwningThread();
361 MOZ_ASSERT(!aTransaction
|| !mTransaction
);
363 mTransaction
= std::move(aTransaction
);
366 void IDBOpenDBRequest::DispatchNonTransactionError(nsresult aErrorCode
) {
367 AssertIsOnOwningThread();
368 MOZ_ASSERT(NS_FAILED(aErrorCode
));
369 MOZ_ASSERT(NS_ERROR_GET_MODULE(aErrorCode
) == NS_ERROR_MODULE_DOM_INDEXEDDB
);
371 // The actor failed to initiate, decrease the number of active IDBOpenRequests
372 // here since NoteComplete won't be called.
373 MaybeDecreaseActiveDatabaseCount();
375 SetError(aErrorCode
);
377 // Make an error event and fire it at the target.
378 auto event
= CreateGenericEvent(this, nsDependentString(kErrorEventType
),
379 eDoesBubble
, eCancelable
);
381 IgnoredErrorResult rv
;
382 DispatchEvent(*event
, rv
);
384 NS_WARNING("Failed to dispatch event!");
388 void IDBOpenDBRequest::NoteComplete() {
389 AssertIsOnOwningThread();
390 MOZ_ASSERT_IF(!NS_IsMainThread(), mWorkerRef
);
392 // Normally, we decrease the number of active IDBOpenRequests here.
393 MaybeDecreaseActiveDatabaseCount();
395 // If we have a WorkerRef, then nulling this out will release the worker.
396 mWorkerRef
= nullptr;
399 void IDBOpenDBRequest::IncreaseActiveDatabaseCount() {
400 AssertIsOnOwningThread();
401 MOZ_ASSERT(!mIncreasedActiveDatabaseCount
);
403 // Increase the number of active IDBOpenRequests.
404 // Note: We count here instead of the actor's ctor because the preemption
405 // could happen at next JS interrupt but its BackgroundFactoryRequestChild
406 // could be created asynchronously from IDBFactory::BackgroundCreateCallback
407 // ::ActorCreated() if its PBackgroundChild is not created yet on this thread.
408 mFactory
->UpdateActiveDatabaseCount(1);
409 mIncreasedActiveDatabaseCount
= true;
412 void IDBOpenDBRequest::MaybeDecreaseActiveDatabaseCount() {
413 AssertIsOnOwningThread();
415 if (mIncreasedActiveDatabaseCount
) {
416 // Decrease the number of active IDBOpenRequests.
417 mFactory
->UpdateActiveDatabaseCount(-1);
418 mIncreasedActiveDatabaseCount
= false;
422 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBOpenDBRequest
)
424 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBOpenDBRequest
, IDBRequest
)
425 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFactory
)
426 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
428 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBOpenDBRequest
, IDBRequest
)
429 // Don't unlink mFactory!
430 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
432 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBOpenDBRequest
)
433 NS_INTERFACE_MAP_END_INHERITING(IDBRequest
)
435 NS_IMPL_ADDREF_INHERITED(IDBOpenDBRequest
, IDBRequest
)
436 NS_IMPL_RELEASE_INHERITED(IDBOpenDBRequest
, IDBRequest
)
438 JSObject
* IDBOpenDBRequest::WrapObject(JSContext
* aCx
,
439 JS::Handle
<JSObject
*> aGivenProto
) {
440 AssertIsOnOwningThread();
442 return IDBOpenDBRequest_Binding::Wrap(aCx
, this, aGivenProto
);
445 } // namespace mozilla::dom