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 "IDBObjectStore.h"
12 #include "IDBCursorType.h"
13 #include "IDBDatabase.h"
14 #include "IDBEvents.h"
15 #include "IDBFactory.h"
17 #include "IDBKeyRange.h"
18 #include "IDBRequest.h"
19 #include "IDBTransaction.h"
20 #include "IndexedDatabase.h"
21 #include "IndexedDatabaseInlines.h"
22 #include "IndexedDatabaseManager.h"
23 #include "IndexedDBCommon.h"
25 #include "ProfilerHelpers.h"
26 #include "ReportInternalError.h"
27 #include "js/Array.h" // JS::GetArrayLength, JS::IsArrayObject
30 #include "js/Object.h" // JS::GetClass
31 #include "js/PropertyAndElement.h" // JS_GetProperty, JS_GetPropertyById, JS_HasOwnProperty, JS_HasOwnPropertyById
32 #include "js/StructuredClone.h"
33 #include "mozilla/EndianUtils.h"
34 #include "mozilla/ErrorResult.h"
35 #include "mozilla/ResultExtensions.h"
36 #include "mozilla/dom/BindingUtils.h"
37 #include "mozilla/dom/BlobBinding.h"
38 #include "mozilla/dom/Document.h"
39 #include "mozilla/dom/File.h"
40 #include "mozilla/dom/IDBObjectStoreBinding.h"
41 #include "mozilla/dom/MemoryBlobImpl.h"
42 #include "mozilla/dom/StreamBlobImpl.h"
43 #include "mozilla/dom/StructuredCloneHolder.h"
44 #include "mozilla/dom/StructuredCloneTags.h"
45 #include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h"
46 #include "mozilla/ipc/BackgroundChild.h"
47 #include "mozilla/ipc/PBackgroundSharedTypes.h"
49 #include "nsStreamUtils.h"
50 #include "nsStringStream.h"
52 // Include this last to avoid path problems on Windows.
53 #include "ActorsChild.h"
55 namespace mozilla::dom
{
57 using namespace mozilla::dom::indexedDB
;
58 using namespace mozilla::dom::quota
;
59 using namespace mozilla::ipc
;
63 Result
<IndexUpdateInfo
, nsresult
> MakeIndexUpdateInfo(
64 const int64_t aIndexID
, const Key
& aKey
, const nsCString
& aLocale
) {
65 IndexUpdateInfo indexUpdateInfo
;
66 indexUpdateInfo
.indexId() = aIndexID
;
67 indexUpdateInfo
.value() = aKey
;
68 if (!aLocale
.IsEmpty()) {
69 QM_TRY_UNWRAP(indexUpdateInfo
.localizedValue(),
70 aKey
.ToLocaleAwareKey(aLocale
));
72 return indexUpdateInfo
;
77 struct IDBObjectStore::StructuredCloneWriteInfo
{
78 JSAutoStructuredCloneBuffer mCloneBuffer
;
79 nsTArray
<StructuredCloneFileChild
> mFiles
;
80 IDBDatabase
* mDatabase
;
81 uint64_t mOffsetToKeyProp
;
83 explicit StructuredCloneWriteInfo(IDBDatabase
* aDatabase
)
84 : mCloneBuffer(JS::StructuredCloneScope::DifferentProcessForIndexedDB
,
88 MOZ_ASSERT(aDatabase
);
90 MOZ_COUNT_CTOR(StructuredCloneWriteInfo
);
93 StructuredCloneWriteInfo(StructuredCloneWriteInfo
&& aCloneWriteInfo
) noexcept
94 : mCloneBuffer(std::move(aCloneWriteInfo
.mCloneBuffer
)),
95 mFiles(std::move(aCloneWriteInfo
.mFiles
)),
96 mDatabase(aCloneWriteInfo
.mDatabase
),
97 mOffsetToKeyProp(aCloneWriteInfo
.mOffsetToKeyProp
) {
98 MOZ_ASSERT(mDatabase
);
100 MOZ_COUNT_CTOR(StructuredCloneWriteInfo
);
102 aCloneWriteInfo
.mOffsetToKeyProp
= 0;
105 MOZ_COUNTED_DTOR(StructuredCloneWriteInfo
)
108 // Used by ValueWrapper::Clone to hold strong references to any blob-like
109 // objects through the clone process. This is necessary because:
110 // - The structured clone process may trigger content code via getters/other
111 // which can potentially cause existing strong references to be dropped,
112 // necessitating the clone to hold its own strong references.
113 // - The structured clone can abort partway through, so it's necessary to track
114 // what strong references have been acquired so that they can be freed even
115 // if a de-serialization does not occur.
116 struct IDBObjectStore::StructuredCloneInfo
{
117 nsTArray
<StructuredCloneFileChild
> mFiles
;
122 struct MOZ_STACK_CLASS GetAddInfoClosure final
{
123 IDBObjectStore::StructuredCloneWriteInfo
& mCloneWriteInfo
;
124 JS::Handle
<JS::Value
> mValue
;
126 GetAddInfoClosure(IDBObjectStore::StructuredCloneWriteInfo
& aCloneWriteInfo
,
127 JS::Handle
<JS::Value
> aValue
)
128 : mCloneWriteInfo(aCloneWriteInfo
), mValue(aValue
) {
129 MOZ_COUNT_CTOR(GetAddInfoClosure
);
132 MOZ_COUNTED_DTOR(GetAddInfoClosure
)
135 MovingNotNull
<RefPtr
<IDBRequest
>> GenerateRequest(
136 JSContext
* aCx
, IDBObjectStore
* aObjectStore
) {
137 MOZ_ASSERT(aObjectStore
);
138 aObjectStore
->AssertIsOnOwningThread();
140 auto transaction
= aObjectStore
->AcquireTransaction();
141 auto* const database
= transaction
->Database();
143 return IDBRequest::Create(aCx
, aObjectStore
, database
,
144 std::move(transaction
));
147 bool StructuredCloneWriteCallback(JSContext
* aCx
,
148 JSStructuredCloneWriter
* aWriter
,
149 JS::Handle
<JSObject
*> aObj
,
150 bool* aSameProcessRequired
, void* aClosure
) {
153 MOZ_ASSERT(aClosure
);
155 auto* const cloneWriteInfo
=
156 static_cast<IDBObjectStore::StructuredCloneWriteInfo
*>(aClosure
);
158 if (JS::GetClass(aObj
) == IDBObjectStore::DummyPropClass()) {
159 MOZ_ASSERT(!cloneWriteInfo
->mOffsetToKeyProp
);
160 cloneWriteInfo
->mOffsetToKeyProp
= js::GetSCOffset(aWriter
);
164 return JS_WriteBytes(aWriter
, &value
, sizeof(value
));
167 // UNWRAP_OBJECT calls might mutate this.
168 JS::Rooted
<JSObject
*> obj(aCx
, aObj
);
171 Blob
* blob
= nullptr;
172 if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob
, &obj
, blob
))) {
174 const uint64_t nativeEndianSize
= blob
->GetSize(rv
);
175 MOZ_ASSERT(!rv
.Failed());
177 const uint64_t size
= NativeEndian::swapToLittleEndian(nativeEndianSize
);
182 const NS_ConvertUTF16toUTF8
convType(type
);
183 const uint32_t convTypeLength
=
184 NativeEndian::swapToLittleEndian(convType
.Length());
186 if (cloneWriteInfo
->mFiles
.Length() > size_t(UINT32_MAX
)) {
188 "Fix the structured clone data to use a bigger type!");
192 const uint32_t index
= cloneWriteInfo
->mFiles
.Length();
194 if (!JS_WriteUint32Pair(aWriter
,
195 blob
->IsFile() ? SCTAG_DOM_FILE
: SCTAG_DOM_BLOB
,
197 !JS_WriteBytes(aWriter
, &size
, sizeof(size
)) ||
198 !JS_WriteBytes(aWriter
, &convTypeLength
, sizeof(convTypeLength
)) ||
199 !JS_WriteBytes(aWriter
, convType
.get(), convType
.Length())) {
203 const RefPtr
<File
> file
= blob
->ToFile();
206 const int64_t nativeEndianLastModifiedDate
= file
->GetLastModified(rv
);
207 MOZ_ALWAYS_TRUE(!rv
.Failed());
209 const int64_t lastModifiedDate
=
210 NativeEndian::swapToLittleEndian(nativeEndianLastModifiedDate
);
215 const NS_ConvertUTF16toUTF8
convName(name
);
216 const uint32_t convNameLength
=
217 NativeEndian::swapToLittleEndian(convName
.Length());
219 if (!JS_WriteBytes(aWriter
, &lastModifiedDate
,
220 sizeof(lastModifiedDate
)) ||
221 !JS_WriteBytes(aWriter
, &convNameLength
, sizeof(convNameLength
)) ||
222 !JS_WriteBytes(aWriter
, convName
.get(), convName
.Length())) {
227 cloneWriteInfo
->mFiles
.EmplaceBack(StructuredCloneFileBase::eBlob
, blob
);
233 return StructuredCloneHolder::WriteFullySerializableObjects(aCx
, aWriter
,
237 bool CopyingStructuredCloneWriteCallback(JSContext
* aCx
,
238 JSStructuredCloneWriter
* aWriter
,
239 JS::Handle
<JSObject
*> aObj
,
240 bool* aSameProcessRequired
,
244 MOZ_ASSERT(aClosure
);
246 auto* const cloneInfo
=
247 static_cast<IDBObjectStore::StructuredCloneInfo
*>(aClosure
);
249 // UNWRAP_OBJECT calls might mutate this.
250 JS::Rooted
<JSObject
*> obj(aCx
, aObj
);
253 Blob
* blob
= nullptr;
254 if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob
, &obj
, blob
))) {
255 if (cloneInfo
->mFiles
.Length() > size_t(UINT32_MAX
)) {
257 "Fix the structured clone data to use a bigger type!");
261 const uint32_t index
= cloneInfo
->mFiles
.Length();
263 if (!JS_WriteUint32Pair(aWriter
,
264 blob
->IsFile() ? SCTAG_DOM_FILE
: SCTAG_DOM_BLOB
,
269 cloneInfo
->mFiles
.EmplaceBack(StructuredCloneFileBase::eBlob
, blob
);
275 return StructuredCloneHolder::WriteFullySerializableObjects(aCx
, aWriter
,
279 nsresult
GetAddInfoCallback(JSContext
* aCx
, void* aClosure
) {
280 static const JSStructuredCloneCallbacks kStructuredCloneCallbacks
= {
281 nullptr /* read */, StructuredCloneWriteCallback
/* write */,
282 nullptr /* reportError */, nullptr /* readTransfer */,
283 nullptr /* writeTransfer */, nullptr /* freeTransfer */,
284 nullptr /* canTransfer */, nullptr /* sabCloned */
289 auto* const data
= static_cast<GetAddInfoClosure
*>(aClosure
);
292 data
->mCloneWriteInfo
.mOffsetToKeyProp
= 0;
294 if (!data
->mCloneWriteInfo
.mCloneBuffer
.write(aCx
, data
->mValue
,
295 &kStructuredCloneCallbacks
,
296 &data
->mCloneWriteInfo
)) {
297 return NS_ERROR_DOM_DATA_CLONE_ERR
;
303 using indexedDB::WrapAsJSObject
;
305 template <typename T
>
306 JSObject
* WrapAsJSObject(JSContext
* const aCx
, T
& aBaseObject
) {
307 JS::Rooted
<JSObject
*> result(aCx
);
308 const bool res
= WrapAsJSObject(aCx
, aBaseObject
, &result
);
309 return res
? static_cast<JSObject
*>(result
) : nullptr;
312 JSObject
* CopyingStructuredCloneReadCallback(
313 JSContext
* aCx
, JSStructuredCloneReader
* aReader
,
314 const JS::CloneDataPolicy
& aCloneDataPolicy
, uint32_t aTag
, uint32_t aData
,
316 MOZ_ASSERT(aTag
!= SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE
);
318 if (aTag
== SCTAG_DOM_BLOB
|| aTag
== SCTAG_DOM_FILE
||
319 aTag
== SCTAG_DOM_MUTABLEFILE
) {
320 auto* const cloneInfo
=
321 static_cast<IDBObjectStore::StructuredCloneInfo
*>(aClosure
);
323 if (aData
>= cloneInfo
->mFiles
.Length()) {
324 MOZ_ASSERT(false, "Bad index value!");
328 StructuredCloneFileChild
& file
= cloneInfo
->mFiles
[aData
];
330 switch (static_cast<StructuredCloneTags
>(aTag
)) {
332 MOZ_ASSERT(file
.Type() == StructuredCloneFileBase::eBlob
);
333 MOZ_ASSERT(!file
.Blob().IsFile());
335 return WrapAsJSObject(aCx
, file
.MutableBlob());
337 case SCTAG_DOM_FILE
: {
338 MOZ_ASSERT(file
.Type() == StructuredCloneFileBase::eBlob
);
340 JS::Rooted
<JSObject
*> result(aCx
);
343 // Create a scope so ~RefPtr fires before returning an unwrapped
345 const RefPtr
<Blob
> blob
= file
.BlobPtr();
346 MOZ_ASSERT(blob
->IsFile());
348 const RefPtr
<File
> file
= blob
->ToFile();
351 if (!WrapAsJSObject(aCx
, file
, &result
)) {
359 case SCTAG_DOM_MUTABLEFILE
:
360 MOZ_ASSERT(file
.Type() == StructuredCloneFileBase::eMutableFile
);
365 // This cannot be reached due to the if condition before.
370 return StructuredCloneHolder::ReadFullySerializableObjects(aCx
, aReader
, aTag
,
376 const JSClass
IDBObjectStore::sDummyPropJSClass
= {
377 "IDBObjectStore Dummy", 0 /* flags */
380 IDBObjectStore::IDBObjectStore(SafeRefPtr
<IDBTransaction
> aTransaction
,
381 ObjectStoreSpec
* aSpec
)
382 : mTransaction(std::move(aTransaction
)),
383 mCachedKeyPath(JS::UndefinedValue()),
385 mId(aSpec
->metadata().id()),
387 MOZ_ASSERT(mTransaction
);
388 mTransaction
->AssertIsOnOwningThread();
392 IDBObjectStore::~IDBObjectStore() {
393 AssertIsOnOwningThread();
396 mozilla::DropJSObjects(this);
401 RefPtr
<IDBObjectStore
> IDBObjectStore::Create(
402 SafeRefPtr
<IDBTransaction
> aTransaction
, ObjectStoreSpec
& aSpec
) {
403 MOZ_ASSERT(aTransaction
);
404 aTransaction
->AssertIsOnOwningThread();
406 return new IDBObjectStore(std::move(aTransaction
), &aSpec
);
410 void IDBObjectStore::AppendIndexUpdateInfo(
411 const int64_t aIndexID
, const KeyPath
& aKeyPath
, const bool aMultiEntry
,
412 const nsCString
& aLocale
, JSContext
* const aCx
, JS::Handle
<JS::Value
> aVal
,
413 nsTArray
<IndexUpdateInfo
>* const aUpdateInfoArray
, ErrorResult
* const aRv
) {
414 // This precondition holds when `aVal` is the result of a structured clone.
415 js::AutoAssertNoContentJS
noContentJS(aCx
);
419 *aRv
= aKeyPath
.ExtractKey(aCx
, aVal
, key
);
421 // If an index's keyPath doesn't match an object, we ignore that object.
422 if (aRv
->ErrorCodeIs(NS_ERROR_DOM_INDEXEDDB_DATA_ERR
) || key
.IsUnset()) {
423 aRv
->SuppressException();
431 QM_TRY_UNWRAP(auto item
, MakeIndexUpdateInfo(aIndexID
, key
, aLocale
),
433 [aRv
](const nsresult tryResult
) { aRv
->Throw(tryResult
); });
435 aUpdateInfoArray
->AppendElement(std::move(item
));
439 JS::Rooted
<JS::Value
> val(aCx
);
440 if (NS_FAILED(aKeyPath
.ExtractKeyAsJSVal(aCx
, aVal
, val
.address()))) {
445 if (NS_WARN_IF(!JS::IsArrayObject(aCx
, val
, &isArray
))) {
446 IDB_REPORT_INTERNAL_ERR();
447 aRv
->Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
451 JS::Rooted
<JSObject
*> array(aCx
, &val
.toObject());
452 uint32_t arrayLength
;
453 if (NS_WARN_IF(!JS::GetArrayLength(aCx
, array
, &arrayLength
))) {
454 IDB_REPORT_INTERNAL_ERR();
455 aRv
->Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
459 for (uint32_t arrayIndex
= 0; arrayIndex
< arrayLength
; arrayIndex
++) {
460 JS::Rooted
<JS::PropertyKey
> indexId(aCx
);
461 if (NS_WARN_IF(!JS_IndexToId(aCx
, arrayIndex
, &indexId
))) {
462 IDB_REPORT_INTERNAL_ERR();
463 aRv
->Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
469 !JS_HasOwnPropertyById(aCx
, array
, indexId
, &hasOwnProperty
))) {
470 IDB_REPORT_INTERNAL_ERR();
471 aRv
->Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
475 if (!hasOwnProperty
) {
479 JS::Rooted
<JS::Value
> arrayItem(aCx
);
480 if (NS_WARN_IF(!JS_GetPropertyById(aCx
, array
, indexId
, &arrayItem
))) {
481 IDB_REPORT_INTERNAL_ERR();
482 aRv
->Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
487 auto result
= value
.SetFromJSVal(aCx
, arrayItem
);
488 if (result
.isErr() || value
.IsUnset()) {
489 // Not a value we can do anything with, ignore it.
490 if (result
.isErr() &&
491 result
.inspectErr().Is(SpecialValues::Exception
)) {
492 result
.unwrapErr().AsException().SuppressException();
497 QM_TRY_UNWRAP(auto item
, MakeIndexUpdateInfo(aIndexID
, value
, aLocale
),
499 [aRv
](const nsresult tryResult
) { aRv
->Throw(tryResult
); });
501 aUpdateInfoArray
->AppendElement(std::move(item
));
505 auto result
= value
.SetFromJSVal(aCx
, val
);
506 if (result
.isErr() || value
.IsUnset()) {
507 // Not a value we can do anything with, ignore it.
508 if (result
.isErr() && result
.inspectErr().Is(SpecialValues::Exception
)) {
509 result
.unwrapErr().AsException().SuppressException();
514 QM_TRY_UNWRAP(auto item
, MakeIndexUpdateInfo(aIndexID
, value
, aLocale
),
516 [aRv
](const nsresult tryResult
) { aRv
->Throw(tryResult
); });
518 aUpdateInfoArray
->AppendElement(std::move(item
));
523 void IDBObjectStore::ClearCloneReadInfo(
524 StructuredCloneReadInfoChild
& aReadInfo
) {
525 // This is kind of tricky, we only want to release stuff on the main thread,
526 // but we can end up being called on other threads if we have already been
527 // cleared on the main thread.
528 if (!aReadInfo
.HasFiles()) {
532 aReadInfo
.ReleaseFiles();
536 bool IDBObjectStore::DeserializeValue(
537 JSContext
* aCx
, StructuredCloneReadInfoChild
&& aCloneReadInfo
,
538 JS::MutableHandle
<JS::Value
> aValue
) {
541 if (!aCloneReadInfo
.Data().Size()) {
542 aValue
.setUndefined();
546 MOZ_ASSERT(!(aCloneReadInfo
.Data().Size() % sizeof(uint64_t)));
548 static const JSStructuredCloneCallbacks callbacks
= {
549 StructuredCloneReadCallback
<StructuredCloneReadInfoChild
>,
558 // FIXME: Consider to use StructuredCloneHolder here and in other
559 // deserializing methods.
560 return JS_ReadStructuredClone(
561 aCx
, aCloneReadInfo
.Data(), JS_STRUCTURED_CLONE_VERSION
,
562 JS::StructuredCloneScope::DifferentProcessForIndexedDB
, aValue
,
563 JS::CloneDataPolicy(), &callbacks
, &aCloneReadInfo
);
568 void IDBObjectStore::AssertIsOnOwningThread() const {
569 MOZ_ASSERT(mTransaction
);
570 mTransaction
->AssertIsOnOwningThread();
575 void IDBObjectStore::GetAddInfo(JSContext
* aCx
, ValueWrapper
& aValueWrapper
,
576 JS::Handle
<JS::Value
> aKeyVal
,
577 StructuredCloneWriteInfo
& aCloneWriteInfo
,
579 nsTArray
<IndexUpdateInfo
>& aUpdateInfoArray
,
581 // Return DATA_ERR if a key was passed in and this objectStore uses inline
583 if (!aKeyVal
.isUndefined() && HasValidKeyPath()) {
584 aRv
.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR
);
588 const bool isAutoIncrement
= AutoIncrement();
590 if (!HasValidKeyPath()) {
591 // Out-of-line keys must be passed in.
592 auto result
= aKey
.SetFromJSVal(aCx
, aKeyVal
);
593 if (result
.isErr()) {
594 aRv
= result
.unwrapErr().ExtractErrorResult(
595 InvalidMapsTo
<NS_ERROR_DOM_INDEXEDDB_DATA_ERR
>);
598 } else if (!isAutoIncrement
) {
599 if (!aValueWrapper
.Clone(aCx
)) {
600 aRv
.Throw(NS_ERROR_DOM_DATA_CLONE_ERR
);
604 aRv
= GetKeyPath().ExtractKey(aCx
, aValueWrapper
.Value(), aKey
);
610 // Return DATA_ERR if no key was specified this isn't an autoIncrement
612 if (aKey
.IsUnset() && !isAutoIncrement
) {
613 aRv
.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR
);
617 // Figure out indexes and the index values to update here.
619 if (mSpec
->indexes().Length() && !aValueWrapper
.Clone(aCx
)) {
620 aRv
.Throw(NS_ERROR_DOM_DATA_CLONE_ERR
);
625 const nsTArray
<IndexMetadata
>& indexes
= mSpec
->indexes();
626 const uint32_t idxCount
= indexes
.Length();
628 aUpdateInfoArray
.SetCapacity(idxCount
); // Pretty good estimate
630 for (uint32_t idxIndex
= 0; idxIndex
< idxCount
; idxIndex
++) {
631 const IndexMetadata
& metadata
= indexes
[idxIndex
];
633 AppendIndexUpdateInfo(metadata
.id(), metadata
.keyPath(),
634 metadata
.multiEntry(), metadata
.locale(), aCx
,
635 aValueWrapper
.Value(), &aUpdateInfoArray
, &aRv
);
636 if (NS_WARN_IF(aRv
.Failed())) {
642 if (isAutoIncrement
&& HasValidKeyPath()) {
643 if (!aValueWrapper
.Clone(aCx
)) {
644 aRv
.Throw(NS_ERROR_DOM_DATA_CLONE_ERR
);
648 GetAddInfoClosure
data(aCloneWriteInfo
, aValueWrapper
.Value());
650 MOZ_ASSERT(aKey
.IsUnset());
652 aRv
= GetKeyPath().ExtractOrCreateKey(aCx
, aValueWrapper
.Value(), aKey
,
653 &GetAddInfoCallback
, &data
);
655 GetAddInfoClosure
data(aCloneWriteInfo
, aValueWrapper
.Value());
657 aRv
= GetAddInfoCallback(aCx
, &data
);
661 RefPtr
<IDBRequest
> IDBObjectStore::AddOrPut(JSContext
* aCx
,
662 ValueWrapper
& aValueWrapper
,
663 JS::Handle
<JS::Value
> aKey
,
664 bool aOverwrite
, bool aFromCursor
,
666 AssertIsOnOwningThread();
668 MOZ_ASSERT_IF(aFromCursor
, aOverwrite
);
670 if (mTransaction
->GetMode() == IDBTransaction::Mode::Cleanup
||
672 aRv
.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR
);
676 if (!mTransaction
->IsActive()) {
677 aRv
.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR
);
681 if (!mTransaction
->IsWriteAllowed()) {
682 aRv
.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR
);
687 StructuredCloneWriteInfo
cloneWriteInfo(mTransaction
->Database());
688 nsTArray
<IndexUpdateInfo
> updateInfos
;
690 // According to spec https://w3c.github.io/IndexedDB/#clone-value,
691 // the transaction must be in inactive state during clone
692 mTransaction
->TransitionToInactive();
695 const uint32_t previousPendingRequestCount
{
696 mTransaction
->GetPendingRequestCount()};
698 GetAddInfo(aCx
, aValueWrapper
, aKey
, cloneWriteInfo
, key
, updateInfos
, aRv
);
699 // Check that new requests were rejected in the Inactive state
700 // and possibly in the Finished state, if the transaction has been aborted,
701 // during the structured cloning.
702 MOZ_ASSERT(mTransaction
->GetPendingRequestCount() ==
703 previousPendingRequestCount
);
705 if (!mTransaction
->IsAborted()) {
706 mTransaction
->TransitionToActive();
707 } else if (!aRv
.Failed()) {
708 aRv
.Throw(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR
);
709 return nullptr; // It is mandatory to return right after throw
716 // Check the size limit of the serialized message which mainly consists of
717 // a StructuredCloneBuffer, an encoded object key, and the encoded index keys.
718 // kMaxIDBMsgOverhead covers the minor stuff not included in this calculation
719 // because the precise calculation would slow down this AddOrPut operation.
720 static const size_t kMaxIDBMsgOverhead
= 1024 * 1024; // 1MB
721 const uint32_t maximalSizeFromPref
=
722 IndexedDatabaseManager::MaxSerializedMsgSize();
723 MOZ_ASSERT(maximalSizeFromPref
> kMaxIDBMsgOverhead
);
724 const size_t kMaxMessageSize
= maximalSizeFromPref
- kMaxIDBMsgOverhead
;
726 const size_t indexUpdateInfoSize
=
727 std::accumulate(updateInfos
.cbegin(), updateInfos
.cend(), 0u,
728 [](size_t old
, const IndexUpdateInfo
& updateInfo
) {
729 return old
+ updateInfo
.value().GetBuffer().Length() +
730 updateInfo
.localizedValue().GetBuffer().Length();
733 const size_t messageSize
= cloneWriteInfo
.mCloneBuffer
.data().Size() +
734 key
.GetBuffer().Length() + indexUpdateInfoSize
;
736 if (messageSize
> kMaxMessageSize
) {
737 IDB_REPORT_INTERNAL_ERR();
738 aRv
.ThrowUnknownError(
739 nsPrintfCString("The serialized value is too large"
740 " (size=%zu bytes, max=%zu bytes).",
741 messageSize
, kMaxMessageSize
));
745 ObjectStoreAddPutParams commonParams
;
746 commonParams
.objectStoreId() = Id();
747 commonParams
.cloneInfo().data().data
=
748 std::move(cloneWriteInfo
.mCloneBuffer
.data());
749 commonParams
.cloneInfo().offsetToKeyProp() = cloneWriteInfo
.mOffsetToKeyProp
;
750 commonParams
.key() = key
;
751 commonParams
.indexUpdateInfos() = std::move(updateInfos
);
753 // Convert any blobs or mutable files into FileAddInfo.
755 commonParams
.fileAddInfos(),
756 TransformIntoNewArrayAbortOnErr(
757 cloneWriteInfo
.mFiles
,
758 [&database
= *mTransaction
->Database()](
759 auto& file
) -> Result
<FileAddInfo
, nsresult
> {
760 switch (file
.Type()) {
761 case StructuredCloneFileBase::eBlob
: {
762 MOZ_ASSERT(file
.HasBlob());
764 PBackgroundIDBDatabaseFileChild
* const fileActor
=
765 database
.GetOrCreateFileActorForBlob(file
.MutableBlob());
766 if (NS_WARN_IF(!fileActor
)) {
767 IDB_REPORT_INTERNAL_ERR();
768 return Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
771 return FileAddInfo
{WrapNotNull(fileActor
),
772 StructuredCloneFileBase::eBlob
};
775 case StructuredCloneFileBase::eWasmBytecode
:
776 case StructuredCloneFileBase::eWasmCompiled
: {
777 MOZ_ASSERT(file
.HasBlob());
779 PBackgroundIDBDatabaseFileChild
* const fileActor
=
780 database
.GetOrCreateFileActorForBlob(file
.MutableBlob());
781 if (NS_WARN_IF(!fileActor
)) {
782 IDB_REPORT_INTERNAL_ERR();
783 return Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
786 return FileAddInfo
{WrapNotNull(fileActor
), file
.Type()};
790 MOZ_CRASH("Should never get here!");
794 nullptr, [&aRv
](const nsresult result
) { aRv
= result
; });
797 aOverwrite
? RequestParams
{ObjectStorePutParams(std::move(commonParams
))}
798 : RequestParams
{ObjectStoreAddParams(std::move(commonParams
))};
800 auto request
= GenerateRequest(aCx
, this).unwrap();
804 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
805 "database(%s).transaction(%s).objectStore(%s).put(%s)",
806 "IDBObjectStore.put(%.0s%.0s%.0s%.0s)",
807 mTransaction
->LoggingSerialNumber(), request
->LoggingSerialNumber(),
808 IDB_LOG_STRINGIFY(mTransaction
->Database()),
809 IDB_LOG_STRINGIFY(*mTransaction
), IDB_LOG_STRINGIFY(this),
810 IDB_LOG_STRINGIFY(key
));
812 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
813 "database(%s).transaction(%s).objectStore(%s).add(%s)",
814 "IDBObjectStore.add(%.0s%.0s%.0s%.0s)",
815 mTransaction
->LoggingSerialNumber(), request
->LoggingSerialNumber(),
816 IDB_LOG_STRINGIFY(mTransaction
->Database()),
817 IDB_LOG_STRINGIFY(*mTransaction
), IDB_LOG_STRINGIFY(this),
818 IDB_LOG_STRINGIFY(key
));
822 mTransaction
->StartRequest(request
, params
);
824 mTransaction
->InvalidateCursorCaches();
829 RefPtr
<IDBRequest
> IDBObjectStore::GetAllInternal(
830 bool aKeysOnly
, JSContext
* aCx
, JS::Handle
<JS::Value
> aKey
,
831 const Optional
<uint32_t>& aLimit
, ErrorResult
& aRv
) {
832 AssertIsOnOwningThread();
835 aRv
.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR
);
839 if (!mTransaction
->IsActive()) {
840 aRv
.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR
);
844 RefPtr
<IDBKeyRange
> keyRange
;
845 IDBKeyRange::FromJSVal(aCx
, aKey
, &keyRange
, aRv
);
846 if (NS_WARN_IF(aRv
.Failed())) {
850 const int64_t id
= Id();
852 Maybe
<SerializedKeyRange
> optionalKeyRange
;
854 SerializedKeyRange serializedKeyRange
;
855 keyRange
->ToSerialized(serializedKeyRange
);
856 optionalKeyRange
.emplace(serializedKeyRange
);
859 const uint32_t limit
= aLimit
.WasPassed() ? aLimit
.Value() : 0;
861 RequestParams params
;
863 params
= ObjectStoreGetAllKeysParams(id
, optionalKeyRange
, limit
);
865 params
= ObjectStoreGetAllParams(id
, optionalKeyRange
, limit
);
868 auto request
= GenerateRequest(aCx
, this).unwrap();
871 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
872 "database(%s).transaction(%s).objectStore(%s)."
873 "getAllKeys(%s, %s)",
874 "IDBObjectStore.getAllKeys(%.0s%.0s%.0s%.0s%.0s)",
875 mTransaction
->LoggingSerialNumber(), request
->LoggingSerialNumber(),
876 IDB_LOG_STRINGIFY(mTransaction
->Database()),
877 IDB_LOG_STRINGIFY(*mTransaction
), IDB_LOG_STRINGIFY(this),
878 IDB_LOG_STRINGIFY(keyRange
), IDB_LOG_STRINGIFY(aLimit
));
880 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
881 "database(%s).transaction(%s).objectStore(%s)."
883 "IDBObjectStore.getAll(%.0s%.0s%.0s%.0s%.0s)",
884 mTransaction
->LoggingSerialNumber(), request
->LoggingSerialNumber(),
885 IDB_LOG_STRINGIFY(mTransaction
->Database()),
886 IDB_LOG_STRINGIFY(*mTransaction
), IDB_LOG_STRINGIFY(this),
887 IDB_LOG_STRINGIFY(keyRange
), IDB_LOG_STRINGIFY(aLimit
));
890 // TODO: This is necessary to preserve request ordering only. Proper
891 // sequencing of requests should be done in a more sophisticated manner that
892 // doesn't require invalidating cursor caches (Bug 1580499).
893 mTransaction
->InvalidateCursorCaches();
895 mTransaction
->StartRequest(request
, params
);
900 RefPtr
<IDBRequest
> IDBObjectStore::Add(JSContext
* aCx
,
901 JS::Handle
<JS::Value
> aValue
,
902 JS::Handle
<JS::Value
> aKey
,
904 AssertIsOnOwningThread();
906 ValueWrapper
valueWrapper(aCx
, aValue
);
908 return AddOrPut(aCx
, valueWrapper
, aKey
, false, /* aFromCursor */ false, aRv
);
911 RefPtr
<IDBRequest
> IDBObjectStore::Put(JSContext
* aCx
,
912 JS::Handle
<JS::Value
> aValue
,
913 JS::Handle
<JS::Value
> aKey
,
915 AssertIsOnOwningThread();
917 ValueWrapper
valueWrapper(aCx
, aValue
);
919 return AddOrPut(aCx
, valueWrapper
, aKey
, true, /* aFromCursor */ false, aRv
);
922 RefPtr
<IDBRequest
> IDBObjectStore::Delete(JSContext
* aCx
,
923 JS::Handle
<JS::Value
> aKey
,
925 AssertIsOnOwningThread();
927 return DeleteInternal(aCx
, aKey
, /* aFromCursor */ false, aRv
);
930 RefPtr
<IDBRequest
> IDBObjectStore::Get(JSContext
* aCx
,
931 JS::Handle
<JS::Value
> aKey
,
933 AssertIsOnOwningThread();
935 return GetInternal(/* aKeyOnly */ false, aCx
, aKey
, aRv
);
938 RefPtr
<IDBRequest
> IDBObjectStore::GetKey(JSContext
* aCx
,
939 JS::Handle
<JS::Value
> aKey
,
941 AssertIsOnOwningThread();
943 return GetInternal(/* aKeyOnly */ true, aCx
, aKey
, aRv
);
946 RefPtr
<IDBRequest
> IDBObjectStore::Clear(JSContext
* aCx
, ErrorResult
& aRv
) {
947 AssertIsOnOwningThread();
950 aRv
.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR
);
954 if (!mTransaction
->IsActive()) {
955 aRv
.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR
);
959 if (!mTransaction
->IsWriteAllowed()) {
960 aRv
.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR
);
964 const ObjectStoreClearParams params
= {Id()};
966 auto request
= GenerateRequest(aCx
, this).unwrap();
968 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
969 "database(%s).transaction(%s).objectStore(%s).clear()",
970 "IDBObjectStore.clear(%.0s%.0s%.0s)", mTransaction
->LoggingSerialNumber(),
971 request
->LoggingSerialNumber(),
972 IDB_LOG_STRINGIFY(mTransaction
->Database()),
973 IDB_LOG_STRINGIFY(*mTransaction
), IDB_LOG_STRINGIFY(this));
975 mTransaction
->InvalidateCursorCaches();
977 mTransaction
->StartRequest(request
, params
);
982 RefPtr
<IDBRequest
> IDBObjectStore::GetAll(JSContext
* aCx
,
983 JS::Handle
<JS::Value
> aKey
,
984 const Optional
<uint32_t>& aLimit
,
986 AssertIsOnOwningThread();
988 return GetAllInternal(/* aKeysOnly */ false, aCx
, aKey
, aLimit
, aRv
);
991 RefPtr
<IDBRequest
> IDBObjectStore::GetAllKeys(JSContext
* aCx
,
992 JS::Handle
<JS::Value
> aKey
,
993 const Optional
<uint32_t>& aLimit
,
995 AssertIsOnOwningThread();
997 return GetAllInternal(/* aKeysOnly */ true, aCx
, aKey
, aLimit
, aRv
);
1000 RefPtr
<IDBRequest
> IDBObjectStore::OpenCursor(JSContext
* aCx
,
1001 JS::Handle
<JS::Value
> aRange
,
1002 IDBCursorDirection aDirection
,
1004 AssertIsOnOwningThread();
1006 return OpenCursorInternal(/* aKeysOnly */ false, aCx
, aRange
, aDirection
,
1010 RefPtr
<IDBRequest
> IDBObjectStore::OpenCursor(JSContext
* aCx
,
1011 IDBCursorDirection aDirection
,
1013 AssertIsOnOwningThread();
1015 return OpenCursorInternal(/* aKeysOnly */ false, aCx
,
1016 JS::UndefinedHandleValue
, aDirection
, aRv
);
1019 RefPtr
<IDBRequest
> IDBObjectStore::OpenKeyCursor(JSContext
* aCx
,
1020 JS::Handle
<JS::Value
> aRange
,
1021 IDBCursorDirection aDirection
,
1023 AssertIsOnOwningThread();
1025 return OpenCursorInternal(/* aKeysOnly */ true, aCx
, aRange
, aDirection
, aRv
);
1028 RefPtr
<IDBIndex
> IDBObjectStore::Index(const nsAString
& aName
,
1030 AssertIsOnOwningThread();
1032 if (mTransaction
->IsCommittingOrFinished() || mDeletedSpec
) {
1033 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1037 const nsTArray
<IndexMetadata
>& indexMetadatas
= mSpec
->indexes();
1039 const auto endIndexMetadatas
= indexMetadatas
.cend();
1040 const auto foundMetadata
=
1041 std::find_if(indexMetadatas
.cbegin(), endIndexMetadatas
,
1042 [&aName
](const auto& indexMetadata
) {
1043 return indexMetadata
.name() == aName
;
1046 if (foundMetadata
== endIndexMetadatas
) {
1047 aRv
.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR
);
1051 const IndexMetadata
& metadata
= *foundMetadata
;
1053 const auto endIndexes
= mIndexes
.cend();
1054 const auto foundIndex
=
1055 std::find_if(mIndexes
.cbegin(), endIndexes
,
1056 [desiredId
= metadata
.id()](const auto& index
) {
1057 return index
->Id() == desiredId
;
1060 RefPtr
<IDBIndex
> index
;
1062 if (foundIndex
== endIndexes
) {
1063 index
= IDBIndex::Create(this, metadata
);
1066 mIndexes
.AppendElement(index
);
1068 index
= *foundIndex
;
1074 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBObjectStore
)
1076 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBObjectStore
)
1077 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
1078 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCachedKeyPath
)
1079 NS_IMPL_CYCLE_COLLECTION_TRACE_END
1081 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBObjectStore
)
1082 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransaction
)
1083 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIndexes
)
1084 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDeletedIndexes
)
1085 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1087 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBObjectStore
)
1088 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
1090 // Don't unlink mTransaction!
1092 NS_IMPL_CYCLE_COLLECTION_UNLINK(mIndexes
)
1093 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDeletedIndexes
)
1095 tmp
->mCachedKeyPath
.setUndefined();
1098 mozilla::DropJSObjects(tmp
);
1099 tmp
->mRooted
= false;
1101 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1103 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBObjectStore
)
1104 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
1105 NS_INTERFACE_MAP_ENTRY(nsISupports
)
1106 NS_INTERFACE_MAP_END
1108 NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBObjectStore
)
1109 NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBObjectStore
)
1111 JSObject
* IDBObjectStore::WrapObject(JSContext
* aCx
,
1112 JS::Handle
<JSObject
*> aGivenProto
) {
1113 return IDBObjectStore_Binding::Wrap(aCx
, this, aGivenProto
);
1116 nsIGlobalObject
* IDBObjectStore::GetParentObject() const {
1117 return mTransaction
->GetParentObject();
1120 void IDBObjectStore::GetKeyPath(JSContext
* aCx
,
1121 JS::MutableHandle
<JS::Value
> aResult
,
1123 if (!mCachedKeyPath
.isUndefined()) {
1124 aResult
.set(mCachedKeyPath
);
1128 aRv
= GetKeyPath().ToJSVal(aCx
, mCachedKeyPath
);
1129 if (NS_WARN_IF(aRv
.Failed())) {
1133 if (mCachedKeyPath
.isGCThing()) {
1134 mozilla::HoldJSObjects(this);
1138 aResult
.set(mCachedKeyPath
);
1141 RefPtr
<DOMStringList
> IDBObjectStore::IndexNames() {
1142 AssertIsOnOwningThread();
1144 return CreateSortedDOMStringList(
1145 mSpec
->indexes(), [](const auto& index
) { return index
.name(); });
1148 RefPtr
<IDBRequest
> IDBObjectStore::GetInternal(bool aKeyOnly
, JSContext
* aCx
,
1149 JS::Handle
<JS::Value
> aKey
,
1151 AssertIsOnOwningThread();
1154 aRv
.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR
);
1158 if (!mTransaction
->IsActive()) {
1159 aRv
.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR
);
1163 RefPtr
<IDBKeyRange
> keyRange
;
1164 IDBKeyRange::FromJSVal(aCx
, aKey
, &keyRange
, aRv
);
1170 // Must specify a key or keyRange for get().
1171 aRv
.Throw(NS_ERROR_DOM_INDEXEDDB_KEY_ERR
);
1175 const int64_t id
= Id();
1177 SerializedKeyRange serializedKeyRange
;
1178 keyRange
->ToSerialized(serializedKeyRange
);
1180 const auto& params
=
1181 aKeyOnly
? RequestParams
{ObjectStoreGetKeyParams(id
, serializedKeyRange
)}
1182 : RequestParams
{ObjectStoreGetParams(id
, serializedKeyRange
)};
1184 auto request
= GenerateRequest(aCx
, this).unwrap();
1186 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
1187 "database(%s).transaction(%s).objectStore(%s).get(%s)",
1188 "IDBObjectStore.get(%.0s%.0s%.0s%.0s)",
1189 mTransaction
->LoggingSerialNumber(), request
->LoggingSerialNumber(),
1190 IDB_LOG_STRINGIFY(mTransaction
->Database()),
1191 IDB_LOG_STRINGIFY(*mTransaction
), IDB_LOG_STRINGIFY(this),
1192 IDB_LOG_STRINGIFY(keyRange
));
1194 // TODO: This is necessary to preserve request ordering only. Proper
1195 // sequencing of requests should be done in a more sophisticated manner that
1196 // doesn't require invalidating cursor caches (Bug 1580499).
1197 mTransaction
->InvalidateCursorCaches();
1199 mTransaction
->StartRequest(request
, params
);
1204 RefPtr
<IDBRequest
> IDBObjectStore::DeleteInternal(JSContext
* aCx
,
1205 JS::Handle
<JS::Value
> aKey
,
1208 AssertIsOnOwningThread();
1211 aRv
.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR
);
1215 if (!mTransaction
->IsActive()) {
1216 aRv
.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR
);
1220 if (!mTransaction
->IsWriteAllowed()) {
1221 aRv
.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR
);
1225 RefPtr
<IDBKeyRange
> keyRange
;
1226 IDBKeyRange::FromJSVal(aCx
, aKey
, &keyRange
, aRv
);
1227 if (NS_WARN_IF((aRv
.Failed()))) {
1232 // Must specify a key or keyRange for delete().
1233 aRv
.Throw(NS_ERROR_DOM_INDEXEDDB_KEY_ERR
);
1237 ObjectStoreDeleteParams params
;
1238 params
.objectStoreId() = Id();
1239 keyRange
->ToSerialized(params
.keyRange());
1241 auto request
= GenerateRequest(aCx
, this).unwrap();
1244 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
1245 "database(%s).transaction(%s).objectStore(%s).delete(%s)",
1246 "IDBObjectStore.delete(%.0s%.0s%.0s%.0s)",
1247 mTransaction
->LoggingSerialNumber(), request
->LoggingSerialNumber(),
1248 IDB_LOG_STRINGIFY(mTransaction
->Database()),
1249 IDB_LOG_STRINGIFY(*mTransaction
), IDB_LOG_STRINGIFY(this),
1250 IDB_LOG_STRINGIFY(keyRange
));
1253 mTransaction
->StartRequest(request
, params
);
1255 mTransaction
->InvalidateCursorCaches();
1260 RefPtr
<IDBIndex
> IDBObjectStore::CreateIndex(
1261 const nsAString
& aName
, const StringOrStringSequence
& aKeyPath
,
1262 const IDBIndexParameters
& aOptionalParameters
, ErrorResult
& aRv
) {
1263 AssertIsOnOwningThread();
1265 if (mTransaction
->GetMode() != IDBTransaction::Mode::VersionChange
||
1267 aRv
.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR
);
1271 const auto transaction
= IDBTransaction::MaybeCurrent();
1272 if (!transaction
|| transaction
!= mTransaction
|| !transaction
->IsActive()) {
1273 aRv
.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR
);
1277 const auto& indexes
= mSpec
->indexes();
1278 const auto end
= indexes
.cend();
1279 const auto foundIt
= std::find_if(
1280 indexes
.cbegin(), end
,
1281 [&aName
](const auto& index
) { return aName
== index
.name(); });
1282 if (foundIt
!= end
) {
1283 aRv
.ThrowConstraintError(nsPrintfCString(
1284 "Index named '%s' already exists at index '%zu'",
1285 NS_ConvertUTF16toUTF8(aName
).get(), foundIt
.GetIndex()));
1289 const auto checkValid
= [](const auto& keyPath
) -> Result
<KeyPath
, nsresult
> {
1290 if (!keyPath
.IsValid()) {
1291 return Err(NS_ERROR_DOM_SYNTAX_ERR
);
1297 QM_INFOONLY_TRY_UNWRAP(
1298 const auto maybeKeyPath
,
1299 ([&aKeyPath
, checkValid
]() -> Result
<KeyPath
, nsresult
> {
1300 if (aKeyPath
.IsString()) {
1302 KeyPath::Parse(aKeyPath
.GetAsString()).andThen(checkValid
));
1305 MOZ_ASSERT(aKeyPath
.IsStringSequence());
1306 if (aKeyPath
.GetAsStringSequence().IsEmpty()) {
1307 return Err(NS_ERROR_DOM_SYNTAX_ERR
);
1311 KeyPath::Parse(aKeyPath
.GetAsStringSequence()).andThen(checkValid
));
1313 if (!maybeKeyPath
) {
1314 aRv
.Throw(NS_ERROR_DOM_SYNTAX_ERR
);
1318 const auto& keyPath
= maybeKeyPath
.ref();
1320 if (aOptionalParameters
.mMultiEntry
&& keyPath
.IsArray()) {
1321 aRv
.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR
);
1327 const auto duplicateIndexName
= std::any_of(
1328 mIndexes
.cbegin(), mIndexes
.cend(),
1329 [&aName
](const auto& index
) { return index
->Name() == aName
; });
1330 MOZ_ASSERT(!duplicateIndexName
);
1334 const IndexMetadata
* const oldMetadataElements
=
1335 indexes
.IsEmpty() ? nullptr : indexes
.Elements();
1337 // With this setup we only validate the passed in locale name by the time we
1338 // get to encoding Keys. Maybe we should do it here right away and error out.
1340 // Valid locale names are always ASCII as per BCP-47.
1341 nsCString locale
= NS_LossyConvertUTF16toASCII(aOptionalParameters
.mLocale
);
1342 bool autoLocale
= locale
.EqualsASCII("auto");
1344 locale
= IndexedDatabaseManager::GetLocale();
1347 if (!locale
.IsEmpty()) {
1348 // Set use counter and log deprecation warning for locale in parent doc.
1349 nsIGlobalObject
* global
= GetParentObject();
1351 // This isn't critical so don't error out if init fails.
1352 if (jsapi
.Init(global
)) {
1354 jsapi
.cx(), global
->GetGlobalJSObject(),
1355 DeprecatedOperations::eIDBObjectStoreCreateIndexLocale
);
1359 IndexMetadata
* const metadata
= mSpec
->indexes().EmplaceBack(
1360 transaction
->NextIndexId(), nsString(aName
), keyPath
, locale
,
1361 aOptionalParameters
.mUnique
, aOptionalParameters
.mMultiEntry
, autoLocale
);
1363 if (oldMetadataElements
&& oldMetadataElements
!= indexes
.Elements()) {
1364 MOZ_ASSERT(indexes
.Length() > 1);
1366 // Array got moved, update the spec pointers for all live indexes.
1367 RefreshSpec(/* aMayDelete */ false);
1370 transaction
->CreateIndex(this, *metadata
);
1372 auto index
= IDBIndex::Create(this, *metadata
);
1374 mIndexes
.AppendElement(index
);
1376 // Don't do this in the macro because we always need to increment the serial
1377 // number to keep in sync with the parent.
1378 const uint64_t requestSerialNumber
= IDBRequest::NextSerialNumber();
1380 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
1381 "database(%s).transaction(%s).objectStore(%s).createIndex(%s)",
1382 "IDBObjectStore.createIndex(%.0s%.0s%.0s%.0s)",
1383 mTransaction
->LoggingSerialNumber(), requestSerialNumber
,
1384 IDB_LOG_STRINGIFY(mTransaction
->Database()),
1385 IDB_LOG_STRINGIFY(*mTransaction
), IDB_LOG_STRINGIFY(this),
1386 IDB_LOG_STRINGIFY(index
));
1391 void IDBObjectStore::DeleteIndex(const nsAString
& aName
, ErrorResult
& aRv
) {
1392 AssertIsOnOwningThread();
1394 if (mTransaction
->GetMode() != IDBTransaction::Mode::VersionChange
||
1396 aRv
.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR
);
1400 const auto transaction
= IDBTransaction::MaybeCurrent();
1401 if (!transaction
|| transaction
!= mTransaction
|| !transaction
->IsActive()) {
1402 aRv
.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR
);
1406 const auto& metadataArray
= mSpec
->indexes();
1408 const auto endMetadata
= metadataArray
.cend();
1409 const auto foundMetadataIt
= std::find_if(
1410 metadataArray
.cbegin(), endMetadata
,
1411 [&aName
](const auto& metadata
) { return aName
== metadata
.name(); });
1413 if (foundMetadataIt
== endMetadata
) {
1414 aRv
.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR
);
1418 const auto foundId
= foundMetadataIt
->id();
1419 MOZ_ASSERT(foundId
);
1421 // Must remove index from mIndexes before altering the metadata array!
1423 const auto end
= mIndexes
.end();
1424 const auto foundIt
= std::find_if(
1425 mIndexes
.begin(), end
,
1426 [foundId
](const auto& index
) { return index
->Id() == foundId
; });
1427 // TODO: Or should we assert foundIt != end?
1428 if (foundIt
!= end
) {
1429 auto& index
= *foundIt
;
1431 index
->NoteDeletion();
1433 mDeletedIndexes
.EmplaceBack(std::move(index
));
1434 mIndexes
.RemoveElementAt(foundIt
.GetIndex());
1438 mSpec
->indexes().RemoveElementAt(foundMetadataIt
.GetIndex());
1440 RefreshSpec(/* aMayDelete */ false);
1442 // Don't do this in the macro because we always need to increment the serial
1443 // number to keep in sync with the parent.
1444 const uint64_t requestSerialNumber
= IDBRequest::NextSerialNumber();
1446 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
1447 "database(%s).transaction(%s).objectStore(%s)."
1448 "deleteIndex(\"%s\")",
1449 "IDBObjectStore.deleteIndex(%.0s%.0s%.0s%.0s)",
1450 mTransaction
->LoggingSerialNumber(), requestSerialNumber
,
1451 IDB_LOG_STRINGIFY(mTransaction
->Database()),
1452 IDB_LOG_STRINGIFY(*mTransaction
), IDB_LOG_STRINGIFY(this),
1453 NS_ConvertUTF16toUTF8(aName
).get());
1455 transaction
->DeleteIndex(this, foundId
);
1458 RefPtr
<IDBRequest
> IDBObjectStore::Count(JSContext
* aCx
,
1459 JS::Handle
<JS::Value
> aKey
,
1461 AssertIsOnOwningThread();
1464 aRv
.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR
);
1468 if (!mTransaction
->IsActive()) {
1469 aRv
.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR
);
1473 RefPtr
<IDBKeyRange
> keyRange
;
1474 IDBKeyRange::FromJSVal(aCx
, aKey
, &keyRange
, aRv
);
1479 ObjectStoreCountParams params
;
1480 params
.objectStoreId() = Id();
1483 SerializedKeyRange serializedKeyRange
;
1484 keyRange
->ToSerialized(serializedKeyRange
);
1485 params
.optionalKeyRange().emplace(serializedKeyRange
);
1488 auto request
= GenerateRequest(aCx
, this).unwrap();
1490 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
1491 "database(%s).transaction(%s).objectStore(%s).count(%s)",
1492 "IDBObjectStore.count(%.0s%.0s%.0s%.0s)",
1493 mTransaction
->LoggingSerialNumber(), request
->LoggingSerialNumber(),
1494 IDB_LOG_STRINGIFY(mTransaction
->Database()),
1495 IDB_LOG_STRINGIFY(*mTransaction
), IDB_LOG_STRINGIFY(this),
1496 IDB_LOG_STRINGIFY(keyRange
));
1498 // TODO: This is necessary to preserve request ordering only. Proper
1499 // sequencing of requests should be done in a more sophisticated manner that
1500 // doesn't require invalidating cursor caches (Bug 1580499).
1501 mTransaction
->InvalidateCursorCaches();
1503 mTransaction
->StartRequest(request
, params
);
1508 RefPtr
<IDBRequest
> IDBObjectStore::OpenCursorInternal(
1509 bool aKeysOnly
, JSContext
* aCx
, JS::Handle
<JS::Value
> aRange
,
1510 IDBCursorDirection aDirection
, ErrorResult
& aRv
) {
1511 AssertIsOnOwningThread();
1515 aRv
.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR
);
1519 if (!mTransaction
->IsActive()) {
1520 aRv
.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR
);
1524 RefPtr
<IDBKeyRange
> keyRange
;
1525 IDBKeyRange::FromJSVal(aCx
, aRange
, &keyRange
, aRv
);
1526 if (NS_WARN_IF(aRv
.Failed())) {
1530 const int64_t objectStoreId
= Id();
1532 Maybe
<SerializedKeyRange
> optionalKeyRange
;
1535 SerializedKeyRange serializedKeyRange
;
1536 keyRange
->ToSerialized(serializedKeyRange
);
1538 optionalKeyRange
.emplace(std::move(serializedKeyRange
));
1541 const CommonOpenCursorParams commonParams
= {
1542 objectStoreId
, std::move(optionalKeyRange
), aDirection
};
1544 // TODO: It would be great if the IPDL generator created a constructor
1545 // accepting a CommonOpenCursorParams by value or rvalue reference.
1547 aKeysOnly
? OpenCursorParams
{ObjectStoreOpenKeyCursorParams
{commonParams
}}
1548 : OpenCursorParams
{ObjectStoreOpenCursorParams
{commonParams
}};
1550 auto request
= GenerateRequest(aCx
, this).unwrap();
1553 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
1554 "database(%s).transaction(%s).objectStore(%s)."
1555 "openKeyCursor(%s, %s)",
1556 "IDBObjectStore.openKeyCursor(%.0s%.0s%.0s%.0s%.0s)",
1557 mTransaction
->LoggingSerialNumber(), request
->LoggingSerialNumber(),
1558 IDB_LOG_STRINGIFY(mTransaction
->Database()),
1559 IDB_LOG_STRINGIFY(*mTransaction
), IDB_LOG_STRINGIFY(this),
1560 IDB_LOG_STRINGIFY(keyRange
), IDB_LOG_STRINGIFY(aDirection
));
1562 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
1563 "database(%s).transaction(%s).objectStore(%s)."
1564 "openCursor(%s, %s)",
1565 "IDBObjectStore.openCursor(%.0s%.0s%.0s%.0s%.0s)",
1566 mTransaction
->LoggingSerialNumber(), request
->LoggingSerialNumber(),
1567 IDB_LOG_STRINGIFY(mTransaction
->Database()),
1568 IDB_LOG_STRINGIFY(*mTransaction
), IDB_LOG_STRINGIFY(this),
1569 IDB_LOG_STRINGIFY(keyRange
), IDB_LOG_STRINGIFY(aDirection
));
1574 ? static_cast<SafeRefPtr
<BackgroundCursorChildBase
>>(
1576 BackgroundCursorChild
<IDBCursorType::ObjectStoreKey
>>(
1577 request
, this, aDirection
))
1578 : MakeSafeRefPtr
<BackgroundCursorChild
<IDBCursorType::ObjectStore
>>(
1579 request
, this, aDirection
);
1581 // TODO: This is necessary to preserve request ordering only. Proper
1582 // sequencing of requests should be done in a more sophisticated manner that
1583 // doesn't require invalidating cursor caches (Bug 1580499).
1584 mTransaction
->InvalidateCursorCaches();
1586 mTransaction
->OpenCursor(*actor
, params
);
1591 void IDBObjectStore::RefreshSpec(bool aMayDelete
) {
1592 AssertIsOnOwningThread();
1593 MOZ_ASSERT_IF(mDeletedSpec
, mSpec
== mDeletedSpec
.get());
1595 auto* const foundObjectStoreSpec
=
1596 mTransaction
->Database()->LookupModifiableObjectStoreSpec(
1597 [id
= Id()](const auto& objSpec
) {
1598 return objSpec
.metadata().id() == id
;
1600 if (foundObjectStoreSpec
) {
1601 mSpec
= foundObjectStoreSpec
;
1603 for (auto& index
: mIndexes
) {
1604 index
->RefreshMetadata(aMayDelete
);
1607 for (auto& index
: mDeletedIndexes
) {
1608 index
->RefreshMetadata(false);
1612 MOZ_ASSERT_IF(!aMayDelete
&& !mDeletedSpec
, foundObjectStoreSpec
);
1614 if (foundObjectStoreSpec
) {
1615 MOZ_ASSERT(mSpec
!= mDeletedSpec
.get());
1616 mDeletedSpec
= nullptr;
1622 const ObjectStoreSpec
& IDBObjectStore::Spec() const {
1623 AssertIsOnOwningThread();
1629 void IDBObjectStore::NoteDeletion() {
1630 AssertIsOnOwningThread();
1632 MOZ_ASSERT(Id() == mSpec
->metadata().id());
1635 MOZ_ASSERT(mDeletedSpec
.get() == mSpec
);
1639 // Copy the spec here.
1640 mDeletedSpec
= MakeUnique
<ObjectStoreSpec
>(*mSpec
);
1641 mDeletedSpec
->indexes().Clear();
1643 mSpec
= mDeletedSpec
.get();
1645 for (const auto& index
: mIndexes
) {
1646 index
->NoteDeletion();
1650 const nsString
& IDBObjectStore::Name() const {
1651 AssertIsOnOwningThread();
1654 return mSpec
->metadata().name();
1657 void IDBObjectStore::SetName(const nsAString
& aName
, ErrorResult
& aRv
) {
1658 AssertIsOnOwningThread();
1660 if (mTransaction
->GetMode() != IDBTransaction::Mode::VersionChange
||
1662 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1666 const auto transaction
= IDBTransaction::MaybeCurrent();
1667 if (!transaction
|| transaction
!= mTransaction
|| !transaction
->IsActive()) {
1668 aRv
.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR
);
1672 if (aName
== mSpec
->metadata().name()) {
1676 // Cache logging string of this object store before renaming.
1677 const LoggingString
loggingOldObjectStore(this);
1680 transaction
->Database()->RenameObjectStore(mSpec
->metadata().id(), aName
);
1682 if (NS_FAILED(rv
)) {
1687 // Don't do this in the macro because we always need to increment the serial
1688 // number to keep in sync with the parent.
1689 const uint64_t requestSerialNumber
= IDBRequest::NextSerialNumber();
1691 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
1692 "database(%s).transaction(%s).objectStore(%s).rename(%s)",
1693 "IDBObjectStore.rename(%.0s%.0s%.0s%.0s)",
1694 mTransaction
->LoggingSerialNumber(), requestSerialNumber
,
1695 IDB_LOG_STRINGIFY(mTransaction
->Database()),
1696 IDB_LOG_STRINGIFY(*mTransaction
), loggingOldObjectStore
.get(),
1697 IDB_LOG_STRINGIFY(this));
1699 transaction
->RenameObjectStore(mSpec
->metadata().id(), aName
);
1702 bool IDBObjectStore::AutoIncrement() const {
1703 AssertIsOnOwningThread();
1706 return mSpec
->metadata().autoIncrement();
1709 const indexedDB::KeyPath
& IDBObjectStore::GetKeyPath() const {
1710 AssertIsOnOwningThread();
1713 return mSpec
->metadata().keyPath();
1716 bool IDBObjectStore::HasValidKeyPath() const {
1717 AssertIsOnOwningThread();
1720 return GetKeyPath().IsValid();
1723 bool IDBObjectStore::ValueWrapper::Clone(JSContext
* aCx
) {
1728 static const JSStructuredCloneCallbacks callbacks
= {
1729 CopyingStructuredCloneReadCallback
/* read */,
1730 CopyingStructuredCloneWriteCallback
/* write */,
1731 nullptr /* reportError */,
1732 nullptr /* readTransfer */,
1733 nullptr /* writeTransfer */,
1734 nullptr /* freeTransfer */,
1735 nullptr /* canTransfer */,
1736 nullptr /* sabCloned */
1739 StructuredCloneInfo cloneInfo
;
1741 JS::Rooted
<JS::Value
> clonedValue(aCx
);
1742 if (!JS_StructuredClone(aCx
, mValue
, &clonedValue
, &callbacks
, &cloneInfo
)) {
1746 mValue
= clonedValue
;
1753 } // namespace mozilla::dom