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/. */
9 #include "IDBCursorType.h"
10 #include "IDBDatabase.h"
11 #include "IDBEvents.h"
12 #include "IDBKeyRange.h"
13 #include "IDBObjectStore.h"
14 #include "IDBRequest.h"
15 #include "IDBTransaction.h"
16 #include "IndexedDatabase.h"
17 #include "IndexedDatabaseInlines.h"
18 #include "mozilla/ErrorResult.h"
19 #include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h"
20 #include "ProfilerHelpers.h"
21 #include "ReportInternalError.h"
23 // Include this last to avoid path problems on Windows.
24 #include "ActorsChild.h"
26 namespace mozilla::dom
{
28 using namespace mozilla::dom::indexedDB
;
32 MovingNotNull
<RefPtr
<IDBRequest
>> GenerateRequest(JSContext
* aCx
,
35 aIndex
->AssertIsOnOwningThread();
37 auto transaction
= aIndex
->ObjectStore()->AcquireTransaction();
38 auto* const database
= transaction
->Database();
40 return IDBRequest::Create(aCx
, aIndex
, database
, std::move(transaction
));
45 IDBIndex::IDBIndex(IDBObjectStore
* aObjectStore
, const IndexMetadata
* aMetadata
)
46 : mObjectStore(aObjectStore
),
47 mCachedKeyPath(JS::UndefinedValue()),
51 MOZ_ASSERT(aObjectStore
);
52 aObjectStore
->AssertIsOnOwningThread();
53 MOZ_ASSERT(aMetadata
);
56 IDBIndex::~IDBIndex() {
57 AssertIsOnOwningThread();
60 mozilla::DropJSObjects(this);
64 RefPtr
<IDBIndex
> IDBIndex::Create(IDBObjectStore
* aObjectStore
,
65 const IndexMetadata
& aMetadata
) {
66 MOZ_ASSERT(aObjectStore
);
67 aObjectStore
->AssertIsOnOwningThread();
69 return new IDBIndex(aObjectStore
, &aMetadata
);
74 void IDBIndex::AssertIsOnOwningThread() const {
75 MOZ_ASSERT(mObjectStore
);
76 mObjectStore
->AssertIsOnOwningThread();
81 RefPtr
<IDBRequest
> IDBIndex::OpenCursor(JSContext
* aCx
,
82 JS::Handle
<JS::Value
> aRange
,
83 IDBCursorDirection aDirection
,
85 AssertIsOnOwningThread();
87 return OpenCursorInternal(/* aKeysOnly */ false, aCx
, aRange
, aDirection
,
91 RefPtr
<IDBRequest
> IDBIndex::OpenKeyCursor(JSContext
* aCx
,
92 JS::Handle
<JS::Value
> aRange
,
93 IDBCursorDirection aDirection
,
95 AssertIsOnOwningThread();
97 return OpenCursorInternal(/* aKeysOnly */ true, aCx
, aRange
, aDirection
, aRv
);
100 RefPtr
<IDBRequest
> IDBIndex::Get(JSContext
* aCx
, JS::Handle
<JS::Value
> aKey
,
102 AssertIsOnOwningThread();
104 return GetInternal(/* aKeyOnly */ false, aCx
, aKey
, aRv
);
107 RefPtr
<IDBRequest
> IDBIndex::GetKey(JSContext
* aCx
, JS::Handle
<JS::Value
> aKey
,
109 AssertIsOnOwningThread();
111 return GetInternal(/* aKeyOnly */ true, aCx
, aKey
, aRv
);
114 RefPtr
<IDBRequest
> IDBIndex::GetAll(JSContext
* aCx
, JS::Handle
<JS::Value
> aKey
,
115 const Optional
<uint32_t>& aLimit
,
117 AssertIsOnOwningThread();
119 return GetAllInternal(/* aKeysOnly */ false, aCx
, aKey
, aLimit
, aRv
);
122 RefPtr
<IDBRequest
> IDBIndex::GetAllKeys(JSContext
* aCx
,
123 JS::Handle
<JS::Value
> aKey
,
124 const Optional
<uint32_t>& aLimit
,
126 AssertIsOnOwningThread();
128 return GetAllInternal(/* aKeysOnly */ true, aCx
, aKey
, aLimit
, aRv
);
131 void IDBIndex::RefreshMetadata(bool aMayDelete
) {
132 AssertIsOnOwningThread();
133 MOZ_ASSERT_IF(mDeletedMetadata
, mMetadata
== mDeletedMetadata
.get());
135 const auto& indexes
= mObjectStore
->Spec().indexes();
136 const auto foundIt
= std::find_if(
137 indexes
.cbegin(), indexes
.cend(),
138 [id
= Id()](const auto& metadata
) { return metadata
.id() == id
; });
139 const bool found
= foundIt
!= indexes
.cend();
141 MOZ_ASSERT_IF(!aMayDelete
&& !mDeletedMetadata
, found
);
144 mMetadata
= &*foundIt
;
145 MOZ_ASSERT(mMetadata
!= mDeletedMetadata
.get());
146 mDeletedMetadata
= nullptr;
152 void IDBIndex::NoteDeletion() {
153 AssertIsOnOwningThread();
154 MOZ_ASSERT(mMetadata
);
155 MOZ_ASSERT(Id() == mMetadata
->id());
157 if (mDeletedMetadata
) {
158 MOZ_ASSERT(mMetadata
== mDeletedMetadata
.get());
162 mDeletedMetadata
= MakeUnique
<IndexMetadata
>(*mMetadata
);
164 mMetadata
= mDeletedMetadata
.get();
167 const nsString
& IDBIndex::Name() const {
168 AssertIsOnOwningThread();
169 MOZ_ASSERT(mMetadata
);
171 return mMetadata
->name();
174 void IDBIndex::SetName(const nsAString
& aName
, ErrorResult
& aRv
) {
175 AssertIsOnOwningThread();
177 const auto& transaction
= mObjectStore
->TransactionRef();
179 if (transaction
.GetMode() != IDBTransaction::Mode::VersionChange
||
181 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
185 if (!transaction
.IsActive()) {
186 aRv
.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR
);
190 if (aName
== mMetadata
->name()) {
194 // Cache logging string of this index before renaming.
195 const LoggingString
loggingOldIndex(this);
197 const int64_t indexId
= Id();
200 transaction
.Database()->RenameIndex(mObjectStore
->Id(), indexId
, aName
);
207 // Don't do this in the macro because we always need to increment the serial
208 // number to keep in sync with the parent.
209 const uint64_t requestSerialNumber
= IDBRequest::NextSerialNumber();
211 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
212 "database(%s).transaction(%s).objectStore(%s).index(%s)."
214 "IDBIndex.rename(%.0s%.0s%.0s%.0s%.0s)",
215 transaction
.LoggingSerialNumber(), requestSerialNumber
,
216 IDB_LOG_STRINGIFY(transaction
.Database()), IDB_LOG_STRINGIFY(transaction
),
217 IDB_LOG_STRINGIFY(mObjectStore
), loggingOldIndex
.get(),
218 IDB_LOG_STRINGIFY(this));
220 mObjectStore
->MutableTransactionRef().RenameIndex(mObjectStore
, indexId
,
224 bool IDBIndex::Unique() const {
225 AssertIsOnOwningThread();
226 MOZ_ASSERT(mMetadata
);
228 return mMetadata
->unique();
231 bool IDBIndex::MultiEntry() const {
232 AssertIsOnOwningThread();
233 MOZ_ASSERT(mMetadata
);
235 return mMetadata
->multiEntry();
238 bool IDBIndex::LocaleAware() const {
239 AssertIsOnOwningThread();
240 MOZ_ASSERT(mMetadata
);
242 return mMetadata
->locale().IsEmpty();
245 const indexedDB::KeyPath
& IDBIndex::GetKeyPath() const {
246 AssertIsOnOwningThread();
247 MOZ_ASSERT(mMetadata
);
249 return mMetadata
->keyPath();
252 void IDBIndex::GetLocale(nsString
& aLocale
) const {
253 AssertIsOnOwningThread();
254 MOZ_ASSERT(mMetadata
);
256 if (mMetadata
->locale().IsEmpty()) {
257 SetDOMStringToNull(aLocale
);
259 CopyASCIItoUTF16(mMetadata
->locale(), aLocale
);
263 const nsCString
& IDBIndex::Locale() const {
264 AssertIsOnOwningThread();
265 MOZ_ASSERT(mMetadata
);
267 return mMetadata
->locale();
270 bool IDBIndex::IsAutoLocale() const {
271 AssertIsOnOwningThread();
272 MOZ_ASSERT(mMetadata
);
274 return mMetadata
->autoLocale();
277 nsIGlobalObject
* IDBIndex::GetParentObject() const {
278 AssertIsOnOwningThread();
280 return mObjectStore
->GetParentObject();
283 void IDBIndex::GetKeyPath(JSContext
* aCx
, JS::MutableHandle
<JS::Value
> aResult
,
285 AssertIsOnOwningThread();
287 if (!mCachedKeyPath
.isUndefined()) {
289 aResult
.set(mCachedKeyPath
);
293 MOZ_ASSERT(!mRooted
);
295 aRv
= GetKeyPath().ToJSVal(aCx
, mCachedKeyPath
);
296 if (NS_WARN_IF(aRv
.Failed())) {
300 if (mCachedKeyPath
.isGCThing()) {
301 mozilla::HoldJSObjects(this);
305 aResult
.set(mCachedKeyPath
);
308 RefPtr
<IDBRequest
> IDBIndex::GetInternal(bool aKeyOnly
, JSContext
* aCx
,
309 JS::Handle
<JS::Value
> aKey
,
311 AssertIsOnOwningThread();
313 if (mDeletedMetadata
) {
314 aRv
.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR
);
318 const auto& transaction
= mObjectStore
->TransactionRef();
319 if (!transaction
.IsActive()) {
320 aRv
.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR
);
324 RefPtr
<IDBKeyRange
> keyRange
;
325 IDBKeyRange::FromJSVal(aCx
, aKey
, &keyRange
, aRv
);
326 if (NS_WARN_IF(aRv
.Failed())) {
331 // Must specify a key or keyRange for get() and getKey().
332 aRv
.Throw(NS_ERROR_DOM_INDEXEDDB_KEY_ERR
);
336 const int64_t objectStoreId
= mObjectStore
->Id();
337 const int64_t indexId
= Id();
339 SerializedKeyRange serializedKeyRange
;
340 keyRange
->ToSerialized(serializedKeyRange
);
342 RequestParams params
;
345 params
= IndexGetKeyParams(objectStoreId
, indexId
, serializedKeyRange
);
347 params
= IndexGetParams(objectStoreId
, indexId
, serializedKeyRange
);
350 auto request
= GenerateRequest(aCx
, this).unwrap();
353 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
354 "database(%s).transaction(%s).objectStore(%s).index(%s)."
356 "IDBIndex.getKey(%.0s%.0s%.0s%.0s%.0s)",
357 transaction
.LoggingSerialNumber(), request
->LoggingSerialNumber(),
358 IDB_LOG_STRINGIFY(transaction
.Database()),
359 IDB_LOG_STRINGIFY(transaction
), IDB_LOG_STRINGIFY(mObjectStore
),
360 IDB_LOG_STRINGIFY(this), IDB_LOG_STRINGIFY(keyRange
));
362 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
363 "database(%s).transaction(%s).objectStore(%s).index(%s)."
365 "IDBIndex.get(%.0s%.0s%.0s%.0s%.0s)", transaction
.LoggingSerialNumber(),
366 request
->LoggingSerialNumber(),
367 IDB_LOG_STRINGIFY(transaction
.Database()),
368 IDB_LOG_STRINGIFY(transaction
), IDB_LOG_STRINGIFY(mObjectStore
),
369 IDB_LOG_STRINGIFY(this), IDB_LOG_STRINGIFY(keyRange
));
372 auto& mutableTransaction
= mObjectStore
->MutableTransactionRef();
374 // TODO: This is necessary to preserve request ordering only. Proper
375 // sequencing of requests should be done in a more sophisticated manner that
376 // doesn't require invalidating cursor caches (Bug 1580499).
377 mutableTransaction
.InvalidateCursorCaches();
379 mutableTransaction
.StartRequest(request
, params
);
384 RefPtr
<IDBRequest
> IDBIndex::GetAllInternal(bool aKeysOnly
, JSContext
* aCx
,
385 JS::Handle
<JS::Value
> aKey
,
386 const Optional
<uint32_t>& aLimit
,
388 AssertIsOnOwningThread();
390 if (mDeletedMetadata
) {
391 aRv
.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR
);
395 const auto& transaction
= mObjectStore
->TransactionRef();
396 if (!transaction
.IsActive()) {
397 aRv
.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR
);
401 RefPtr
<IDBKeyRange
> keyRange
;
402 IDBKeyRange::FromJSVal(aCx
, aKey
, &keyRange
, aRv
);
403 if (NS_WARN_IF(aRv
.Failed())) {
407 const int64_t objectStoreId
= mObjectStore
->Id();
408 const int64_t indexId
= Id();
410 Maybe
<SerializedKeyRange
> optionalKeyRange
;
412 SerializedKeyRange serializedKeyRange
;
413 keyRange
->ToSerialized(serializedKeyRange
);
414 optionalKeyRange
.emplace(serializedKeyRange
);
417 const uint32_t limit
= aLimit
.WasPassed() ? aLimit
.Value() : 0;
420 aKeysOnly
? RequestParams
{IndexGetAllKeysParams(objectStoreId
, indexId
,
421 optionalKeyRange
, limit
)}
422 : RequestParams
{IndexGetAllParams(objectStoreId
, indexId
,
423 optionalKeyRange
, limit
)};
425 auto request
= GenerateRequest(aCx
, this).unwrap();
428 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
429 "database(%s).transaction(%s).objectStore(%s).index(%s)."
430 "getAllKeys(%s, %s)",
431 "IDBIndex.getAllKeys(%.0s%.0s%.0s%.0s%.0s%.0s)",
432 transaction
.LoggingSerialNumber(), request
->LoggingSerialNumber(),
433 IDB_LOG_STRINGIFY(transaction
.Database()),
434 IDB_LOG_STRINGIFY(transaction
), IDB_LOG_STRINGIFY(mObjectStore
),
435 IDB_LOG_STRINGIFY(this), IDB_LOG_STRINGIFY(keyRange
),
436 IDB_LOG_STRINGIFY(aLimit
));
438 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
439 "database(%s).transaction(%s).objectStore(%s).index(%s)."
441 "IDBIndex.getAll(%.0s%.0s%.0s%.0s%.0s%.0s)",
442 transaction
.LoggingSerialNumber(), request
->LoggingSerialNumber(),
443 IDB_LOG_STRINGIFY(transaction
.Database()),
444 IDB_LOG_STRINGIFY(transaction
), IDB_LOG_STRINGIFY(mObjectStore
),
445 IDB_LOG_STRINGIFY(this), IDB_LOG_STRINGIFY(keyRange
),
446 IDB_LOG_STRINGIFY(aLimit
));
449 auto& mutableTransaction
= mObjectStore
->MutableTransactionRef();
451 // TODO: This is necessary to preserve request ordering only. Proper
452 // sequencing of requests should be done in a more sophisticated manner that
453 // doesn't require invalidating cursor caches (Bug 1580499).
454 mutableTransaction
.InvalidateCursorCaches();
456 mutableTransaction
.StartRequest(request
, params
);
461 RefPtr
<IDBRequest
> IDBIndex::OpenCursorInternal(bool aKeysOnly
, JSContext
* aCx
,
462 JS::Handle
<JS::Value
> aRange
,
463 IDBCursorDirection aDirection
,
465 AssertIsOnOwningThread();
467 if (mDeletedMetadata
) {
468 aRv
.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR
);
472 const auto& transaction
= mObjectStore
->TransactionRef();
473 if (!transaction
.IsActive()) {
474 aRv
.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR
);
478 RefPtr
<IDBKeyRange
> keyRange
;
479 IDBKeyRange::FromJSVal(aCx
, aRange
, &keyRange
, aRv
);
480 if (NS_WARN_IF(aRv
.Failed())) {
484 const int64_t objectStoreId
= mObjectStore
->Id();
485 const int64_t indexId
= Id();
487 Maybe
<SerializedKeyRange
> optionalKeyRange
;
490 SerializedKeyRange serializedKeyRange
;
491 keyRange
->ToSerialized(serializedKeyRange
);
493 optionalKeyRange
.emplace(std::move(serializedKeyRange
));
496 const CommonIndexOpenCursorParams commonIndexParams
= {
497 {objectStoreId
, std::move(optionalKeyRange
), aDirection
}, indexId
};
500 aKeysOnly
? OpenCursorParams
{IndexOpenKeyCursorParams
{commonIndexParams
}}
501 : OpenCursorParams
{IndexOpenCursorParams
{commonIndexParams
}};
503 auto request
= GenerateRequest(aCx
, this).unwrap();
506 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
507 "database(%s).transaction(%s).objectStore(%s).index(%s)."
508 "openKeyCursor(%s, %s)",
509 "IDBIndex.openKeyCursor(%.0s%.0s%.0s%.0s%.0s%.0s)",
510 transaction
.LoggingSerialNumber(), request
->LoggingSerialNumber(),
511 IDB_LOG_STRINGIFY(transaction
.Database()),
512 IDB_LOG_STRINGIFY(transaction
), IDB_LOG_STRINGIFY(mObjectStore
),
513 IDB_LOG_STRINGIFY(this), IDB_LOG_STRINGIFY(keyRange
),
514 IDB_LOG_STRINGIFY(aDirection
));
516 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
517 "database(%s).transaction(%s).objectStore(%s).index(%s)."
518 "openCursor(%s, %s)",
519 "IDBIndex.openCursor(%.0s%.0s%.0s%.0s%.0s%.0s)",
520 transaction
.LoggingSerialNumber(), request
->LoggingSerialNumber(),
521 IDB_LOG_STRINGIFY(transaction
.Database()),
522 IDB_LOG_STRINGIFY(transaction
), IDB_LOG_STRINGIFY(mObjectStore
),
523 IDB_LOG_STRINGIFY(this), IDB_LOG_STRINGIFY(keyRange
),
524 IDB_LOG_STRINGIFY(aDirection
));
529 ? static_cast<SafeRefPtr
<BackgroundCursorChildBase
>>(
530 MakeSafeRefPtr
<BackgroundCursorChild
<IDBCursorType::IndexKey
>>(
531 request
, this, aDirection
))
532 : MakeSafeRefPtr
<BackgroundCursorChild
<IDBCursorType::Index
>>(
533 request
, this, aDirection
);
535 auto& mutableTransaction
= mObjectStore
->MutableTransactionRef();
537 // TODO: This is necessary to preserve request ordering only. Proper
538 // sequencing of requests should be done in a more sophisticated manner that
539 // doesn't require invalidating cursor caches (Bug 1580499).
540 mutableTransaction
.InvalidateCursorCaches();
542 mutableTransaction
.OpenCursor(*actor
, params
);
547 RefPtr
<IDBRequest
> IDBIndex::Count(JSContext
* aCx
, JS::Handle
<JS::Value
> aKey
,
549 AssertIsOnOwningThread();
551 if (mDeletedMetadata
) {
552 aRv
.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR
);
556 const auto& transaction
= mObjectStore
->TransactionRef();
557 if (!transaction
.IsActive()) {
558 aRv
.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR
);
562 RefPtr
<IDBKeyRange
> keyRange
;
563 IDBKeyRange::FromJSVal(aCx
, aKey
, &keyRange
, aRv
);
568 IndexCountParams params
;
569 params
.objectStoreId() = mObjectStore
->Id();
570 params
.indexId() = Id();
573 SerializedKeyRange serializedKeyRange
;
574 keyRange
->ToSerialized(serializedKeyRange
);
575 params
.optionalKeyRange().emplace(serializedKeyRange
);
578 auto request
= GenerateRequest(aCx
, this).unwrap();
580 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
581 "database(%s).transaction(%s).objectStore(%s).index(%s)."
583 "IDBIndex.count(%.0s%.0s%.0s%.0s%.0s)", transaction
.LoggingSerialNumber(),
584 request
->LoggingSerialNumber(), IDB_LOG_STRINGIFY(transaction
.Database()),
585 IDB_LOG_STRINGIFY(transaction
), IDB_LOG_STRINGIFY(mObjectStore
),
586 IDB_LOG_STRINGIFY(this), IDB_LOG_STRINGIFY(keyRange
));
588 auto& mutableTransaction
= mObjectStore
->MutableTransactionRef();
590 // TODO: This is necessary to preserve request ordering only. Proper
591 // sequencing of requests should be done in a more sophisticated manner that
592 // doesn't require invalidating cursor caches (Bug 1580499).
593 mutableTransaction
.InvalidateCursorCaches();
595 mutableTransaction
.StartRequest(request
, params
);
600 NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBIndex
)
601 NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBIndex
)
603 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBIndex
)
604 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
605 NS_INTERFACE_MAP_ENTRY(nsISupports
)
608 NS_IMPL_CYCLE_COLLECTION_MULTI_ZONE_JSHOLDER_CLASS(IDBIndex
)
610 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBIndex
)
611 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
612 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCachedKeyPath
)
613 NS_IMPL_CYCLE_COLLECTION_TRACE_END
615 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBIndex
)
616 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mObjectStore
)
617 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
619 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBIndex
)
620 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
622 // Don't unlink mObjectStore!
624 tmp
->mCachedKeyPath
.setUndefined();
627 mozilla::DropJSObjects(tmp
);
628 tmp
->mRooted
= false;
630 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
632 JSObject
* IDBIndex::WrapObject(JSContext
* aCx
,
633 JS::Handle
<JSObject
*> aGivenProto
) {
634 return IDBIndex_Binding::Wrap(aCx
, this, aGivenProto
);
637 } // namespace mozilla::dom