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 "IDBObjectStore.h"
8 
9 #include <numeric>
10 #include <utility>
11 
12 #include "IDBCursorType.h"
13 #include "IDBDatabase.h"
14 #include "IDBEvents.h"
15 #include "IDBFactory.h"
16 #include "IDBIndex.h"
17 #include "IDBKeyRange.h"
18 #include "IDBMutableFile.h"
19 #include "IDBRequest.h"
20 #include "IDBTransaction.h"
21 #include "IndexedDatabase.h"
22 #include "IndexedDatabaseInlines.h"
23 #include "IndexedDatabaseManager.h"
24 #include "IndexedDBCommon.h"
25 #include "KeyPath.h"
26 #include "ProfilerHelpers.h"
27 #include "ReportInternalError.h"
28 #include "js/Array.h"  // JS::GetArrayLength, JS::IsArrayObject
29 #include "js/Class.h"
30 #include "js/Date.h"
31 #include "js/Object.h"  // JS::GetClass
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/File.h"
39 #include "mozilla/dom/IDBMutableFileBinding.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"
48 #include "nsCOMPtr.h"
49 #include "nsStreamUtils.h"
50 #include "nsStringStream.h"
51 
52 // Include this last to avoid path problems on Windows.
53 #include "ActorsChild.h"
54 
55 namespace mozilla::dom {
56 
57 using namespace mozilla::dom::indexedDB;
58 using namespace mozilla::dom::quota;
59 using namespace mozilla::ipc;
60 
61 namespace {
62 
MakeIndexUpdateInfo(const int64_t aIndexID,const Key & aKey,const nsCString & aLocale)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));
71   }
72   return indexUpdateInfo;
73 }
74 
75 }  // namespace
76 
77 struct IDBObjectStore::StructuredCloneWriteInfo {
78   JSAutoStructuredCloneBuffer mCloneBuffer;
79   nsTArray<StructuredCloneFileChild> mFiles;
80   IDBDatabase* mDatabase;
81   uint64_t mOffsetToKeyProp;
82 
StructuredCloneWriteInfomozilla::dom::IDBObjectStore::StructuredCloneWriteInfo83   explicit StructuredCloneWriteInfo(IDBDatabase* aDatabase)
84       : mCloneBuffer(JS::StructuredCloneScope::DifferentProcessForIndexedDB,
85                      nullptr, nullptr),
86         mDatabase(aDatabase),
87         mOffsetToKeyProp(0) {
88     MOZ_ASSERT(aDatabase);
89 
90     MOZ_COUNT_CTOR(StructuredCloneWriteInfo);
91   }
92 
StructuredCloneWriteInfomozilla::dom::IDBObjectStore::StructuredCloneWriteInfo93   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);
99 
100     MOZ_COUNT_CTOR(StructuredCloneWriteInfo);
101 
102     aCloneWriteInfo.mOffsetToKeyProp = 0;
103   }
104 
105   MOZ_COUNTED_DTOR(StructuredCloneWriteInfo)
106 };
107 
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;
118 };
119 
120 namespace {
121 
122 struct MOZ_STACK_CLASS GetAddInfoClosure final {
123   IDBObjectStore::StructuredCloneWriteInfo& mCloneWriteInfo;
124   JS::Handle<JS::Value> mValue;
125 
GetAddInfoClosuremozilla::dom::__anonf0fd2b3d0211::GetAddInfoClosure126   GetAddInfoClosure(IDBObjectStore::StructuredCloneWriteInfo& aCloneWriteInfo,
127                     JS::Handle<JS::Value> aValue)
128       : mCloneWriteInfo(aCloneWriteInfo), mValue(aValue) {
129     MOZ_COUNT_CTOR(GetAddInfoClosure);
130   }
131 
132   MOZ_COUNTED_DTOR(GetAddInfoClosure)
133 };
134 
GenerateRequest(JSContext * aCx,IDBObjectStore * aObjectStore)135 MovingNotNull<RefPtr<IDBRequest>> GenerateRequest(
136     JSContext* aCx, IDBObjectStore* aObjectStore) {
137   MOZ_ASSERT(aObjectStore);
138   aObjectStore->AssertIsOnOwningThread();
139 
140   auto transaction = aObjectStore->AcquireTransaction();
141   auto* const database = transaction->Database();
142 
143   return IDBRequest::Create(aCx, aObjectStore, database,
144                             std::move(transaction));
145 }
146 
StructuredCloneWriteCallback(JSContext * aCx,JSStructuredCloneWriter * aWriter,JS::Handle<JSObject * > aObj,bool * aSameProcessRequired,void * aClosure)147 bool StructuredCloneWriteCallback(JSContext* aCx,
148                                   JSStructuredCloneWriter* aWriter,
149                                   JS::Handle<JSObject*> aObj,
150                                   bool* aSameProcessRequired, void* aClosure) {
151   MOZ_ASSERT(aCx);
152   MOZ_ASSERT(aWriter);
153   MOZ_ASSERT(aClosure);
154 
155   auto* const cloneWriteInfo =
156       static_cast<IDBObjectStore::StructuredCloneWriteInfo*>(aClosure);
157 
158   if (JS::GetClass(aObj) == IDBObjectStore::DummyPropClass()) {
159     MOZ_ASSERT(!cloneWriteInfo->mOffsetToKeyProp);
160     cloneWriteInfo->mOffsetToKeyProp = js::GetSCOffset(aWriter);
161 
162     uint64_t value = 0;
163     // Omit endian swap
164     return JS_WriteBytes(aWriter, &value, sizeof(value));
165   }
166 
167   // UNWRAP_OBJECT calls might mutate this.
168   JS::Rooted<JSObject*> obj(aCx, aObj);
169 
170   IDBMutableFile* mutableFile;
171   if (NS_SUCCEEDED(UNWRAP_OBJECT(IDBMutableFile, &obj, mutableFile))) {
172     if (cloneWriteInfo->mDatabase->IsFileHandleDisabled()) {
173       return false;
174     }
175 
176     IDBDatabase* const database = mutableFile->Database();
177     MOZ_ASSERT(database);
178 
179     // Throw when trying to store IDBMutableFile objects that live in a
180     // different database.
181     if (database != cloneWriteInfo->mDatabase) {
182       MOZ_ASSERT(!SameCOMIdentity(database, cloneWriteInfo->mDatabase));
183 
184       if (database->Name() != cloneWriteInfo->mDatabase->Name()) {
185         return false;
186       }
187 
188       nsCString fileOrigin, databaseOrigin;
189       PersistenceType filePersistenceType, databasePersistenceType;
190 
191       if (NS_WARN_IF(NS_FAILED(
192               database->GetQuotaInfo(fileOrigin, &filePersistenceType)))) {
193         return false;
194       }
195 
196       if (NS_WARN_IF(NS_FAILED(cloneWriteInfo->mDatabase->GetQuotaInfo(
197               databaseOrigin, &databasePersistenceType)))) {
198         return false;
199       }
200 
201       if (filePersistenceType != databasePersistenceType ||
202           fileOrigin != databaseOrigin) {
203         return false;
204       }
205     }
206 
207     if (cloneWriteInfo->mFiles.Length() > size_t(UINT32_MAX)) {
208       MOZ_ASSERT(false, "Fix the structured clone data to use a bigger type!");
209       return false;
210     }
211 
212     const uint32_t index = cloneWriteInfo->mFiles.Length();
213 
214     const NS_ConvertUTF16toUTF8 convType(mutableFile->Type());
215     const uint32_t convTypeLength =
216         NativeEndian::swapToLittleEndian(convType.Length());
217 
218     const NS_ConvertUTF16toUTF8 convName(mutableFile->Name());
219     const uint32_t convNameLength =
220         NativeEndian::swapToLittleEndian(convName.Length());
221 
222     if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_MUTABLEFILE, uint32_t(index)) ||
223         !JS_WriteBytes(aWriter, &convTypeLength, sizeof(uint32_t)) ||
224         !JS_WriteBytes(aWriter, convType.get(), convType.Length()) ||
225         !JS_WriteBytes(aWriter, &convNameLength, sizeof(uint32_t)) ||
226         !JS_WriteBytes(aWriter, convName.get(), convName.Length())) {
227       return false;
228     }
229 
230     cloneWriteInfo->mFiles.EmplaceBack(mutableFile);
231 
232     return true;
233   }
234 
235   {
236     Blob* blob = nullptr;
237     if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, &obj, blob))) {
238       ErrorResult rv;
239       const uint64_t nativeEndianSize = blob->GetSize(rv);
240       MOZ_ASSERT(!rv.Failed());
241 
242       const uint64_t size = NativeEndian::swapToLittleEndian(nativeEndianSize);
243 
244       nsString type;
245       blob->GetType(type);
246 
247       const NS_ConvertUTF16toUTF8 convType(type);
248       const uint32_t convTypeLength =
249           NativeEndian::swapToLittleEndian(convType.Length());
250 
251       if (cloneWriteInfo->mFiles.Length() > size_t(UINT32_MAX)) {
252         MOZ_ASSERT(false,
253                    "Fix the structured clone data to use a bigger type!");
254         return false;
255       }
256 
257       const uint32_t index = cloneWriteInfo->mFiles.Length();
258 
259       if (!JS_WriteUint32Pair(aWriter,
260                               blob->IsFile() ? SCTAG_DOM_FILE : SCTAG_DOM_BLOB,
261                               index) ||
262           !JS_WriteBytes(aWriter, &size, sizeof(size)) ||
263           !JS_WriteBytes(aWriter, &convTypeLength, sizeof(convTypeLength)) ||
264           !JS_WriteBytes(aWriter, convType.get(), convType.Length())) {
265         return false;
266       }
267 
268       const RefPtr<File> file = blob->ToFile();
269       if (file) {
270         ErrorResult rv;
271         const int64_t nativeEndianLastModifiedDate = file->GetLastModified(rv);
272         MOZ_ALWAYS_TRUE(!rv.Failed());
273 
274         const int64_t lastModifiedDate =
275             NativeEndian::swapToLittleEndian(nativeEndianLastModifiedDate);
276 
277         nsString name;
278         file->GetName(name);
279 
280         const NS_ConvertUTF16toUTF8 convName(name);
281         const uint32_t convNameLength =
282             NativeEndian::swapToLittleEndian(convName.Length());
283 
284         if (!JS_WriteBytes(aWriter, &lastModifiedDate,
285                            sizeof(lastModifiedDate)) ||
286             !JS_WriteBytes(aWriter, &convNameLength, sizeof(convNameLength)) ||
287             !JS_WriteBytes(aWriter, convName.get(), convName.Length())) {
288           return false;
289         }
290       }
291 
292       cloneWriteInfo->mFiles.EmplaceBack(StructuredCloneFileBase::eBlob, blob);
293 
294       return true;
295     }
296   }
297 
298   return StructuredCloneHolder::WriteFullySerializableObjects(aCx, aWriter,
299                                                               aObj);
300 }
301 
CopyingStructuredCloneWriteCallback(JSContext * aCx,JSStructuredCloneWriter * aWriter,JS::Handle<JSObject * > aObj,bool * aSameProcessRequired,void * aClosure)302 bool CopyingStructuredCloneWriteCallback(JSContext* aCx,
303                                          JSStructuredCloneWriter* aWriter,
304                                          JS::Handle<JSObject*> aObj,
305                                          bool* aSameProcessRequired,
306                                          void* aClosure) {
307   MOZ_ASSERT(aCx);
308   MOZ_ASSERT(aWriter);
309   MOZ_ASSERT(aClosure);
310 
311   auto* const cloneInfo =
312       static_cast<IDBObjectStore::StructuredCloneInfo*>(aClosure);
313 
314   // UNWRAP_OBJECT calls might mutate this.
315   JS::Rooted<JSObject*> obj(aCx, aObj);
316 
317   {
318     Blob* blob = nullptr;
319     if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, &obj, blob))) {
320       if (cloneInfo->mFiles.Length() > size_t(UINT32_MAX)) {
321         MOZ_ASSERT(false,
322                    "Fix the structured clone data to use a bigger type!");
323         return false;
324       }
325 
326       const uint32_t index = cloneInfo->mFiles.Length();
327 
328       if (!JS_WriteUint32Pair(aWriter,
329                               blob->IsFile() ? SCTAG_DOM_FILE : SCTAG_DOM_BLOB,
330                               index)) {
331         return false;
332       }
333 
334       cloneInfo->mFiles.EmplaceBack(StructuredCloneFileBase::eBlob, blob);
335 
336       return true;
337     }
338   }
339 
340   {
341     IDBMutableFile* mutableFile;
342     if (NS_SUCCEEDED(UNWRAP_OBJECT(IDBMutableFile, &obj, mutableFile))) {
343       if (cloneInfo->mFiles.Length() > size_t(UINT32_MAX)) {
344         MOZ_ASSERT(false,
345                    "Fix the structured clone data to use a bigger type!");
346         return false;
347       }
348 
349       const uint32_t index = cloneInfo->mFiles.Length();
350 
351       if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_MUTABLEFILE, index)) {
352         return false;
353       }
354 
355       cloneInfo->mFiles.EmplaceBack(mutableFile);
356 
357       return true;
358     }
359   }
360 
361   return StructuredCloneHolder::WriteFullySerializableObjects(aCx, aWriter,
362                                                               aObj);
363 }
364 
GetAddInfoCallback(JSContext * aCx,void * aClosure)365 nsresult GetAddInfoCallback(JSContext* aCx, void* aClosure) {
366   static const JSStructuredCloneCallbacks kStructuredCloneCallbacks = {
367       nullptr /* read */,          StructuredCloneWriteCallback /* write */,
368       nullptr /* reportError */,   nullptr /* readTransfer */,
369       nullptr /* writeTransfer */, nullptr /* freeTransfer */,
370       nullptr /* canTransfer */,   nullptr /* sabCloned */
371   };
372 
373   MOZ_ASSERT(aCx);
374 
375   auto* const data = static_cast<GetAddInfoClosure*>(aClosure);
376   MOZ_ASSERT(data);
377 
378   data->mCloneWriteInfo.mOffsetToKeyProp = 0;
379 
380   if (!data->mCloneWriteInfo.mCloneBuffer.write(aCx, data->mValue,
381                                                 &kStructuredCloneCallbacks,
382                                                 &data->mCloneWriteInfo)) {
383     return NS_ERROR_DOM_DATA_CLONE_ERR;
384   }
385 
386   return NS_OK;
387 }
388 
389 using indexedDB::WrapAsJSObject;
390 
391 template <typename T>
WrapAsJSObject(JSContext * const aCx,T & aBaseObject)392 JSObject* WrapAsJSObject(JSContext* const aCx, T& aBaseObject) {
393   JS::Rooted<JSObject*> result(aCx);
394   const bool res = WrapAsJSObject(aCx, aBaseObject, &result);
395   return res ? static_cast<JSObject*>(result) : nullptr;
396 }
397 
CopyingStructuredCloneReadCallback(JSContext * aCx,JSStructuredCloneReader * aReader,const JS::CloneDataPolicy & aCloneDataPolicy,uint32_t aTag,uint32_t aData,void * aClosure)398 JSObject* CopyingStructuredCloneReadCallback(
399     JSContext* aCx, JSStructuredCloneReader* aReader,
400     const JS::CloneDataPolicy& aCloneDataPolicy, uint32_t aTag, uint32_t aData,
401     void* aClosure) {
402   MOZ_ASSERT(aTag != SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE);
403 
404   if (aTag == SCTAG_DOM_BLOB || aTag == SCTAG_DOM_FILE ||
405       aTag == SCTAG_DOM_MUTABLEFILE) {
406     auto* const cloneInfo =
407         static_cast<IDBObjectStore::StructuredCloneInfo*>(aClosure);
408 
409     if (aData >= cloneInfo->mFiles.Length()) {
410       MOZ_ASSERT(false, "Bad index value!");
411       return nullptr;
412     }
413 
414     StructuredCloneFileChild& file = cloneInfo->mFiles[aData];
415 
416     switch (static_cast<StructuredCloneTags>(aTag)) {
417       case SCTAG_DOM_BLOB:
418         MOZ_ASSERT(file.Type() == StructuredCloneFileBase::eBlob);
419         MOZ_ASSERT(!file.Blob().IsFile());
420 
421         return WrapAsJSObject(aCx, file.MutableBlob());
422 
423       case SCTAG_DOM_FILE: {
424         MOZ_ASSERT(file.Type() == StructuredCloneFileBase::eBlob);
425 
426         JS::Rooted<JSObject*> result(aCx);
427 
428         {
429           // Create a scope so ~RefPtr fires before returning an unwrapped
430           // JS::Value.
431           const RefPtr<Blob> blob = file.BlobPtr();
432           MOZ_ASSERT(blob->IsFile());
433 
434           const RefPtr<File> file = blob->ToFile();
435           MOZ_ASSERT(file);
436 
437           if (!WrapAsJSObject(aCx, file, &result)) {
438             return nullptr;
439           }
440         }
441 
442         return result;
443       }
444 
445       case SCTAG_DOM_MUTABLEFILE:
446         MOZ_ASSERT(file.Type() == StructuredCloneFileBase::eMutableFile);
447 
448         return WrapAsJSObject(aCx, file.MutableMutableFile());
449 
450       default:
451         // This cannot be reached due to the if condition before.
452         break;
453     }
454   }
455 
456   return StructuredCloneHolder::ReadFullySerializableObjects(aCx, aReader,
457                                                              aTag);
458 }
459 
460 }  // namespace
461 
462 const JSClass IDBObjectStore::sDummyPropJSClass = {
463     "IDBObjectStore Dummy", 0 /* flags */
464 };
465 
IDBObjectStore(SafeRefPtr<IDBTransaction> aTransaction,ObjectStoreSpec * aSpec)466 IDBObjectStore::IDBObjectStore(SafeRefPtr<IDBTransaction> aTransaction,
467                                ObjectStoreSpec* aSpec)
468     : mTransaction(std::move(aTransaction)),
469       mCachedKeyPath(JS::UndefinedValue()),
470       mSpec(aSpec),
471       mId(aSpec->metadata().id()),
472       mRooted(false) {
473   MOZ_ASSERT(mTransaction);
474   mTransaction->AssertIsOnOwningThread();
475   MOZ_ASSERT(aSpec);
476 }
477 
~IDBObjectStore()478 IDBObjectStore::~IDBObjectStore() {
479   AssertIsOnOwningThread();
480 
481   if (mRooted) {
482     mozilla::DropJSObjects(this);
483   }
484 }
485 
486 // static
Create(SafeRefPtr<IDBTransaction> aTransaction,ObjectStoreSpec & aSpec)487 RefPtr<IDBObjectStore> IDBObjectStore::Create(
488     SafeRefPtr<IDBTransaction> aTransaction, ObjectStoreSpec& aSpec) {
489   MOZ_ASSERT(aTransaction);
490   aTransaction->AssertIsOnOwningThread();
491 
492   return new IDBObjectStore(std::move(aTransaction), &aSpec);
493 }
494 
495 // static
AppendIndexUpdateInfo(const int64_t aIndexID,const KeyPath & aKeyPath,const bool aMultiEntry,const nsCString & aLocale,JSContext * const aCx,JS::Handle<JS::Value> aVal,nsTArray<IndexUpdateInfo> * const aUpdateInfoArray,ErrorResult * const aRv)496 void IDBObjectStore::AppendIndexUpdateInfo(
497     const int64_t aIndexID, const KeyPath& aKeyPath, const bool aMultiEntry,
498     const nsCString& aLocale, JSContext* const aCx, JS::Handle<JS::Value> aVal,
499     nsTArray<IndexUpdateInfo>* const aUpdateInfoArray, ErrorResult* const aRv) {
500   // This precondition holds when `aVal` is the result of a structured clone.
501   js::AutoAssertNoContentJS noContentJS(aCx);
502 
503   if (!aMultiEntry) {
504     Key key;
505     *aRv = aKeyPath.ExtractKey(aCx, aVal, key);
506 
507     // If an index's keyPath doesn't match an object, we ignore that object.
508     if (aRv->ErrorCodeIs(NS_ERROR_DOM_INDEXEDDB_DATA_ERR) || key.IsUnset()) {
509       aRv->SuppressException();
510       return;
511     }
512 
513     if (aRv->Failed()) {
514       return;
515     }
516 
517     QM_TRY_UNWRAP(auto item, MakeIndexUpdateInfo(aIndexID, key, aLocale),
518                   QM_VOID,
519                   [aRv](const nsresult tryResult) { aRv->Throw(tryResult); });
520 
521     aUpdateInfoArray->AppendElement(std::move(item));
522     return;
523   }
524 
525   JS::Rooted<JS::Value> val(aCx);
526   if (NS_FAILED(aKeyPath.ExtractKeyAsJSVal(aCx, aVal, val.address()))) {
527     return;
528   }
529 
530   bool isArray;
531   if (NS_WARN_IF(!JS::IsArrayObject(aCx, val, &isArray))) {
532     IDB_REPORT_INTERNAL_ERR();
533     aRv->Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
534     return;
535   }
536   if (isArray) {
537     JS::Rooted<JSObject*> array(aCx, &val.toObject());
538     uint32_t arrayLength;
539     if (NS_WARN_IF(!JS::GetArrayLength(aCx, array, &arrayLength))) {
540       IDB_REPORT_INTERNAL_ERR();
541       aRv->Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
542       return;
543     }
544 
545     for (uint32_t arrayIndex = 0; arrayIndex < arrayLength; arrayIndex++) {
546       JS::RootedId indexId(aCx);
547       if (NS_WARN_IF(!JS_IndexToId(aCx, arrayIndex, &indexId))) {
548         IDB_REPORT_INTERNAL_ERR();
549         aRv->Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
550         return;
551       }
552 
553       bool hasOwnProperty;
554       if (NS_WARN_IF(
555               !JS_HasOwnPropertyById(aCx, array, indexId, &hasOwnProperty))) {
556         IDB_REPORT_INTERNAL_ERR();
557         aRv->Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
558         return;
559       }
560 
561       if (!hasOwnProperty) {
562         continue;
563       }
564 
565       JS::RootedValue arrayItem(aCx);
566       if (NS_WARN_IF(!JS_GetPropertyById(aCx, array, indexId, &arrayItem))) {
567         IDB_REPORT_INTERNAL_ERR();
568         aRv->Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
569         return;
570       }
571 
572       Key value;
573       auto result = value.SetFromJSVal(aCx, arrayItem);
574       if (result.isErr() || value.IsUnset()) {
575         // Not a value we can do anything with, ignore it.
576         if (result.isErr() &&
577             result.inspectErr().Is(SpecialValues::Exception)) {
578           result.unwrapErr().AsException().SuppressException();
579         }
580         continue;
581       }
582 
583       QM_TRY_UNWRAP(auto item, MakeIndexUpdateInfo(aIndexID, value, aLocale),
584                     QM_VOID,
585                     [aRv](const nsresult tryResult) { aRv->Throw(tryResult); });
586 
587       aUpdateInfoArray->AppendElement(std::move(item));
588     }
589   } else {
590     Key value;
591     auto result = value.SetFromJSVal(aCx, val);
592     if (result.isErr() || value.IsUnset()) {
593       // Not a value we can do anything with, ignore it.
594       if (result.isErr() && result.inspectErr().Is(SpecialValues::Exception)) {
595         result.unwrapErr().AsException().SuppressException();
596       }
597       return;
598     }
599 
600     QM_TRY_UNWRAP(auto item, MakeIndexUpdateInfo(aIndexID, value, aLocale),
601                   QM_VOID,
602                   [aRv](const nsresult tryResult) { aRv->Throw(tryResult); });
603 
604     aUpdateInfoArray->AppendElement(std::move(item));
605   }
606 }
607 
608 // static
ClearCloneReadInfo(StructuredCloneReadInfoChild & aReadInfo)609 void IDBObjectStore::ClearCloneReadInfo(
610     StructuredCloneReadInfoChild& aReadInfo) {
611   // This is kind of tricky, we only want to release stuff on the main thread,
612   // but we can end up being called on other threads if we have already been
613   // cleared on the main thread.
614   if (!aReadInfo.HasFiles()) {
615     return;
616   }
617 
618   aReadInfo.ReleaseFiles();
619 }
620 
621 // static
DeserializeValue(JSContext * aCx,StructuredCloneReadInfoChild && aCloneReadInfo,JS::MutableHandle<JS::Value> aValue)622 bool IDBObjectStore::DeserializeValue(
623     JSContext* aCx, StructuredCloneReadInfoChild&& aCloneReadInfo,
624     JS::MutableHandle<JS::Value> aValue) {
625   MOZ_ASSERT(aCx);
626 
627   if (!aCloneReadInfo.Data().Size()) {
628     aValue.setUndefined();
629     return true;
630   }
631 
632   MOZ_ASSERT(!(aCloneReadInfo.Data().Size() % sizeof(uint64_t)));
633 
634   static const JSStructuredCloneCallbacks callbacks = {
635       StructuredCloneReadCallback<StructuredCloneReadInfoChild>,
636       nullptr,
637       nullptr,
638       nullptr,
639       nullptr,
640       nullptr,
641       nullptr,
642       nullptr};
643 
644   // FIXME: Consider to use StructuredCloneHolder here and in other
645   //        deserializing methods.
646   return JS_ReadStructuredClone(
647       aCx, aCloneReadInfo.Data(), JS_STRUCTURED_CLONE_VERSION,
648       JS::StructuredCloneScope::DifferentProcessForIndexedDB, aValue,
649       JS::CloneDataPolicy(), &callbacks, &aCloneReadInfo);
650 }
651 
652 #ifdef DEBUG
653 
AssertIsOnOwningThread() const654 void IDBObjectStore::AssertIsOnOwningThread() const {
655   MOZ_ASSERT(mTransaction);
656   mTransaction->AssertIsOnOwningThread();
657 }
658 
659 #endif  // DEBUG
660 
GetAddInfo(JSContext * aCx,ValueWrapper & aValueWrapper,JS::Handle<JS::Value> aKeyVal,StructuredCloneWriteInfo & aCloneWriteInfo,Key & aKey,nsTArray<IndexUpdateInfo> & aUpdateInfoArray,ErrorResult & aRv)661 void IDBObjectStore::GetAddInfo(JSContext* aCx, ValueWrapper& aValueWrapper,
662                                 JS::Handle<JS::Value> aKeyVal,
663                                 StructuredCloneWriteInfo& aCloneWriteInfo,
664                                 Key& aKey,
665                                 nsTArray<IndexUpdateInfo>& aUpdateInfoArray,
666                                 ErrorResult& aRv) {
667   // Return DATA_ERR if a key was passed in and this objectStore uses inline
668   // keys.
669   if (!aKeyVal.isUndefined() && HasValidKeyPath()) {
670     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
671     return;
672   }
673 
674   const bool isAutoIncrement = AutoIncrement();
675 
676   if (!HasValidKeyPath()) {
677     // Out-of-line keys must be passed in.
678     auto result = aKey.SetFromJSVal(aCx, aKeyVal);
679     if (result.isErr()) {
680       aRv = result.unwrapErr().ExtractErrorResult(
681           InvalidMapsTo<NS_ERROR_DOM_INDEXEDDB_DATA_ERR>);
682       return;
683     }
684   } else if (!isAutoIncrement) {
685     if (!aValueWrapper.Clone(aCx)) {
686       aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
687       return;
688     }
689 
690     aRv = GetKeyPath().ExtractKey(aCx, aValueWrapper.Value(), aKey);
691     if (aRv.Failed()) {
692       return;
693     }
694   }
695 
696   // Return DATA_ERR if no key was specified this isn't an autoIncrement
697   // objectStore.
698   if (aKey.IsUnset() && !isAutoIncrement) {
699     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
700     return;
701   }
702 
703   // Figure out indexes and the index values to update here.
704 
705   if (mSpec->indexes().Length() && !aValueWrapper.Clone(aCx)) {
706     aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
707     return;
708   }
709 
710   {
711     const nsTArray<IndexMetadata>& indexes = mSpec->indexes();
712     const uint32_t idxCount = indexes.Length();
713 
714     aUpdateInfoArray.SetCapacity(idxCount);  // Pretty good estimate
715 
716     for (uint32_t idxIndex = 0; idxIndex < idxCount; idxIndex++) {
717       const IndexMetadata& metadata = indexes[idxIndex];
718 
719       AppendIndexUpdateInfo(metadata.id(), metadata.keyPath(),
720                             metadata.multiEntry(), metadata.locale(), aCx,
721                             aValueWrapper.Value(), &aUpdateInfoArray, &aRv);
722       if (NS_WARN_IF(aRv.Failed())) {
723         return;
724       }
725     }
726   }
727 
728   if (isAutoIncrement && HasValidKeyPath()) {
729     if (!aValueWrapper.Clone(aCx)) {
730       aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
731       return;
732     }
733 
734     GetAddInfoClosure data(aCloneWriteInfo, aValueWrapper.Value());
735 
736     MOZ_ASSERT(aKey.IsUnset());
737 
738     aRv = GetKeyPath().ExtractOrCreateKey(aCx, aValueWrapper.Value(), aKey,
739                                           &GetAddInfoCallback, &data);
740   } else {
741     GetAddInfoClosure data(aCloneWriteInfo, aValueWrapper.Value());
742 
743     aRv = GetAddInfoCallback(aCx, &data);
744   }
745 }
746 
AddOrPut(JSContext * aCx,ValueWrapper & aValueWrapper,JS::Handle<JS::Value> aKey,bool aOverwrite,bool aFromCursor,ErrorResult & aRv)747 RefPtr<IDBRequest> IDBObjectStore::AddOrPut(JSContext* aCx,
748                                             ValueWrapper& aValueWrapper,
749                                             JS::Handle<JS::Value> aKey,
750                                             bool aOverwrite, bool aFromCursor,
751                                             ErrorResult& aRv) {
752   AssertIsOnOwningThread();
753   MOZ_ASSERT(aCx);
754   MOZ_ASSERT_IF(aFromCursor, aOverwrite);
755 
756   if (mTransaction->GetMode() == IDBTransaction::Mode::Cleanup ||
757       mDeletedSpec) {
758     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
759     return nullptr;
760   }
761 
762   if (!mTransaction->IsActive()) {
763     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
764     return nullptr;
765   }
766 
767   if (!mTransaction->IsWriteAllowed()) {
768     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR);
769     return nullptr;
770   }
771 
772   Key key;
773   StructuredCloneWriteInfo cloneWriteInfo(mTransaction->Database());
774   nsTArray<IndexUpdateInfo> updateInfos;
775 
776   {
777     const auto autoStateRestore =
778         mTransaction->TemporarilyTransitionToInactive();
779     GetAddInfo(aCx, aValueWrapper, aKey, cloneWriteInfo, key, updateInfos, aRv);
780   }
781 
782   if (aRv.Failed()) {
783     return nullptr;
784   }
785 
786   if (!mTransaction->IsActive()) {
787     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
788     return nullptr;
789   }
790 
791   // Check the size limit of the serialized message which mainly consists of
792   // a StructuredCloneBuffer, an encoded object key, and the encoded index keys.
793   // kMaxIDBMsgOverhead covers the minor stuff not included in this calculation
794   // because the precise calculation would slow down this AddOrPut operation.
795   static const size_t kMaxIDBMsgOverhead = 1024 * 1024;  // 1MB
796   const uint32_t maximalSizeFromPref =
797       IndexedDatabaseManager::MaxSerializedMsgSize();
798   MOZ_ASSERT(maximalSizeFromPref > kMaxIDBMsgOverhead);
799   const size_t kMaxMessageSize = maximalSizeFromPref - kMaxIDBMsgOverhead;
800 
801   const size_t indexUpdateInfoSize =
802       std::accumulate(updateInfos.cbegin(), updateInfos.cend(), 0u,
803                       [](size_t old, const IndexUpdateInfo& updateInfo) {
804                         return old + updateInfo.value().GetBuffer().Length() +
805                                updateInfo.localizedValue().GetBuffer().Length();
806                       });
807 
808   const size_t messageSize = cloneWriteInfo.mCloneBuffer.data().Size() +
809                              key.GetBuffer().Length() + indexUpdateInfoSize;
810 
811   if (messageSize > kMaxMessageSize) {
812     IDB_REPORT_INTERNAL_ERR();
813     aRv.ThrowUnknownError(
814         nsPrintfCString("The serialized value is too large"
815                         " (size=%zu bytes, max=%zu bytes).",
816                         messageSize, kMaxMessageSize));
817     return nullptr;
818   }
819 
820   ObjectStoreAddPutParams commonParams;
821   commonParams.objectStoreId() = Id();
822   commonParams.cloneInfo().data().data =
823       std::move(cloneWriteInfo.mCloneBuffer.data());
824   commonParams.cloneInfo().offsetToKeyProp() = cloneWriteInfo.mOffsetToKeyProp;
825   commonParams.key() = key;
826   commonParams.indexUpdateInfos() = std::move(updateInfos);
827 
828   // Convert any blobs or mutable files into FileAddInfo.
829   QM_TRY_UNWRAP(
830       commonParams.fileAddInfos(),
831       TransformIntoNewArrayAbortOnErr(
832           cloneWriteInfo.mFiles,
833           [&database = *mTransaction->Database()](
834               auto& file) -> Result<FileAddInfo, nsresult> {
835             switch (file.Type()) {
836               case StructuredCloneFileBase::eBlob: {
837                 MOZ_ASSERT(file.HasBlob());
838                 MOZ_ASSERT(!file.HasMutableFile());
839 
840                 PBackgroundIDBDatabaseFileChild* const fileActor =
841                     database.GetOrCreateFileActorForBlob(file.MutableBlob());
842                 if (NS_WARN_IF(!fileActor)) {
843                   IDB_REPORT_INTERNAL_ERR();
844                   return Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
845                 }
846 
847                 return FileAddInfo{fileActor, StructuredCloneFileBase::eBlob};
848               }
849 
850               case StructuredCloneFileBase::eMutableFile: {
851                 MOZ_ASSERT(file.HasMutableFile());
852                 MOZ_ASSERT(!file.HasBlob());
853 
854                 PBackgroundMutableFileChild* const mutableFileActor =
855                     file.MutableFile().GetBackgroundActor();
856                 if (NS_WARN_IF(!mutableFileActor)) {
857                   IDB_REPORT_INTERNAL_ERR();
858                   return Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
859                 }
860 
861                 return FileAddInfo{mutableFileActor,
862                                    StructuredCloneFileBase::eMutableFile};
863               }
864 
865               case StructuredCloneFileBase::eWasmBytecode:
866               case StructuredCloneFileBase::eWasmCompiled: {
867                 MOZ_ASSERT(file.HasBlob());
868                 MOZ_ASSERT(!file.HasMutableFile());
869 
870                 PBackgroundIDBDatabaseFileChild* const fileActor =
871                     database.GetOrCreateFileActorForBlob(file.MutableBlob());
872                 if (NS_WARN_IF(!fileActor)) {
873                   IDB_REPORT_INTERNAL_ERR();
874                   return Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
875                 }
876 
877                 return FileAddInfo{fileActor, file.Type()};
878               }
879 
880               default:
881                 MOZ_CRASH("Should never get here!");
882             }
883           },
884           fallible),
885       nullptr, [&aRv](const nsresult result) { aRv = result; });
886 
887   const auto& params =
888       aOverwrite ? RequestParams{ObjectStorePutParams(std::move(commonParams))}
889                  : RequestParams{ObjectStoreAddParams(std::move(commonParams))};
890 
891   auto request = GenerateRequest(aCx, this).unwrap();
892 
893   if (!aFromCursor) {
894     if (aOverwrite) {
895       IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
896           "database(%s).transaction(%s).objectStore(%s).put(%s)",
897           "IDBObjectStore.put(%.0s%.0s%.0s%.0s)",
898           mTransaction->LoggingSerialNumber(), request->LoggingSerialNumber(),
899           IDB_LOG_STRINGIFY(mTransaction->Database()),
900           IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(this),
901           IDB_LOG_STRINGIFY(key));
902     } else {
903       IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
904           "database(%s).transaction(%s).objectStore(%s).add(%s)",
905           "IDBObjectStore.add(%.0s%.0s%.0s%.0s)",
906           mTransaction->LoggingSerialNumber(), request->LoggingSerialNumber(),
907           IDB_LOG_STRINGIFY(mTransaction->Database()),
908           IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(this),
909           IDB_LOG_STRINGIFY(key));
910     }
911   }
912 
913   mTransaction->StartRequest(request, params);
914 
915   mTransaction->InvalidateCursorCaches();
916 
917   return request;
918 }
919 
GetAllInternal(bool aKeysOnly,JSContext * aCx,JS::Handle<JS::Value> aKey,const Optional<uint32_t> & aLimit,ErrorResult & aRv)920 RefPtr<IDBRequest> IDBObjectStore::GetAllInternal(
921     bool aKeysOnly, JSContext* aCx, JS::Handle<JS::Value> aKey,
922     const Optional<uint32_t>& aLimit, ErrorResult& aRv) {
923   AssertIsOnOwningThread();
924 
925   if (mDeletedSpec) {
926     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
927     return nullptr;
928   }
929 
930   if (!mTransaction->IsActive()) {
931     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
932     return nullptr;
933   }
934 
935   RefPtr<IDBKeyRange> keyRange;
936   IDBKeyRange::FromJSVal(aCx, aKey, &keyRange, aRv);
937   if (NS_WARN_IF(aRv.Failed())) {
938     return nullptr;
939   }
940 
941   const int64_t id = Id();
942 
943   Maybe<SerializedKeyRange> optionalKeyRange;
944   if (keyRange) {
945     SerializedKeyRange serializedKeyRange;
946     keyRange->ToSerialized(serializedKeyRange);
947     optionalKeyRange.emplace(serializedKeyRange);
948   }
949 
950   const uint32_t limit = aLimit.WasPassed() ? aLimit.Value() : 0;
951 
952   RequestParams params;
953   if (aKeysOnly) {
954     params = ObjectStoreGetAllKeysParams(id, optionalKeyRange, limit);
955   } else {
956     params = ObjectStoreGetAllParams(id, optionalKeyRange, limit);
957   }
958 
959   auto request = GenerateRequest(aCx, this).unwrap();
960 
961   if (aKeysOnly) {
962     IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
963         "database(%s).transaction(%s).objectStore(%s)."
964         "getAllKeys(%s, %s)",
965         "IDBObjectStore.getAllKeys(%.0s%.0s%.0s%.0s%.0s)",
966         mTransaction->LoggingSerialNumber(), request->LoggingSerialNumber(),
967         IDB_LOG_STRINGIFY(mTransaction->Database()),
968         IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(this),
969         IDB_LOG_STRINGIFY(keyRange), IDB_LOG_STRINGIFY(aLimit));
970   } else {
971     IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
972         "database(%s).transaction(%s).objectStore(%s)."
973         "getAll(%s, %s)",
974         "IDBObjectStore.getAll(%.0s%.0s%.0s%.0s%.0s)",
975         mTransaction->LoggingSerialNumber(), request->LoggingSerialNumber(),
976         IDB_LOG_STRINGIFY(mTransaction->Database()),
977         IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(this),
978         IDB_LOG_STRINGIFY(keyRange), IDB_LOG_STRINGIFY(aLimit));
979   }
980 
981   // TODO: This is necessary to preserve request ordering only. Proper
982   // sequencing of requests should be done in a more sophisticated manner that
983   // doesn't require invalidating cursor caches (Bug 1580499).
984   mTransaction->InvalidateCursorCaches();
985 
986   mTransaction->StartRequest(request, params);
987 
988   return request;
989 }
990 
Add(JSContext * aCx,JS::Handle<JS::Value> aValue,JS::Handle<JS::Value> aKey,ErrorResult & aRv)991 RefPtr<IDBRequest> IDBObjectStore::Add(JSContext* aCx,
992                                        JS::Handle<JS::Value> aValue,
993                                        JS::Handle<JS::Value> aKey,
994                                        ErrorResult& aRv) {
995   AssertIsOnOwningThread();
996 
997   ValueWrapper valueWrapper(aCx, aValue);
998 
999   return AddOrPut(aCx, valueWrapper, aKey, false, /* aFromCursor */ false, aRv);
1000 }
1001 
Put(JSContext * aCx,JS::Handle<JS::Value> aValue,JS::Handle<JS::Value> aKey,ErrorResult & aRv)1002 RefPtr<IDBRequest> IDBObjectStore::Put(JSContext* aCx,
1003                                        JS::Handle<JS::Value> aValue,
1004                                        JS::Handle<JS::Value> aKey,
1005                                        ErrorResult& aRv) {
1006   AssertIsOnOwningThread();
1007 
1008   ValueWrapper valueWrapper(aCx, aValue);
1009 
1010   return AddOrPut(aCx, valueWrapper, aKey, true, /* aFromCursor */ false, aRv);
1011 }
1012 
Delete(JSContext * aCx,JS::Handle<JS::Value> aKey,ErrorResult & aRv)1013 RefPtr<IDBRequest> IDBObjectStore::Delete(JSContext* aCx,
1014                                           JS::Handle<JS::Value> aKey,
1015                                           ErrorResult& aRv) {
1016   AssertIsOnOwningThread();
1017 
1018   return DeleteInternal(aCx, aKey, /* aFromCursor */ false, aRv);
1019 }
1020 
Get(JSContext * aCx,JS::Handle<JS::Value> aKey,ErrorResult & aRv)1021 RefPtr<IDBRequest> IDBObjectStore::Get(JSContext* aCx,
1022                                        JS::Handle<JS::Value> aKey,
1023                                        ErrorResult& aRv) {
1024   AssertIsOnOwningThread();
1025 
1026   return GetInternal(/* aKeyOnly */ false, aCx, aKey, aRv);
1027 }
1028 
GetKey(JSContext * aCx,JS::Handle<JS::Value> aKey,ErrorResult & aRv)1029 RefPtr<IDBRequest> IDBObjectStore::GetKey(JSContext* aCx,
1030                                           JS::Handle<JS::Value> aKey,
1031                                           ErrorResult& aRv) {
1032   AssertIsOnOwningThread();
1033 
1034   return GetInternal(/* aKeyOnly */ true, aCx, aKey, aRv);
1035 }
1036 
Clear(JSContext * aCx,ErrorResult & aRv)1037 RefPtr<IDBRequest> IDBObjectStore::Clear(JSContext* aCx, ErrorResult& aRv) {
1038   AssertIsOnOwningThread();
1039 
1040   if (mDeletedSpec) {
1041     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
1042     return nullptr;
1043   }
1044 
1045   if (!mTransaction->IsActive()) {
1046     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
1047     return nullptr;
1048   }
1049 
1050   if (!mTransaction->IsWriteAllowed()) {
1051     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR);
1052     return nullptr;
1053   }
1054 
1055   const ObjectStoreClearParams params = {Id()};
1056 
1057   auto request = GenerateRequest(aCx, this).unwrap();
1058 
1059   IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
1060       "database(%s).transaction(%s).objectStore(%s).clear()",
1061       "IDBObjectStore.clear(%.0s%.0s%.0s)", mTransaction->LoggingSerialNumber(),
1062       request->LoggingSerialNumber(),
1063       IDB_LOG_STRINGIFY(mTransaction->Database()),
1064       IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(this));
1065 
1066   mTransaction->InvalidateCursorCaches();
1067 
1068   mTransaction->StartRequest(request, params);
1069 
1070   return request;
1071 }
1072 
GetAll(JSContext * aCx,JS::Handle<JS::Value> aKey,const Optional<uint32_t> & aLimit,ErrorResult & aRv)1073 RefPtr<IDBRequest> IDBObjectStore::GetAll(JSContext* aCx,
1074                                           JS::Handle<JS::Value> aKey,
1075                                           const Optional<uint32_t>& aLimit,
1076                                           ErrorResult& aRv) {
1077   AssertIsOnOwningThread();
1078 
1079   return GetAllInternal(/* aKeysOnly */ false, aCx, aKey, aLimit, aRv);
1080 }
1081 
GetAllKeys(JSContext * aCx,JS::Handle<JS::Value> aKey,const Optional<uint32_t> & aLimit,ErrorResult & aRv)1082 RefPtr<IDBRequest> IDBObjectStore::GetAllKeys(JSContext* aCx,
1083                                               JS::Handle<JS::Value> aKey,
1084                                               const Optional<uint32_t>& aLimit,
1085                                               ErrorResult& aRv) {
1086   AssertIsOnOwningThread();
1087 
1088   return GetAllInternal(/* aKeysOnly */ true, aCx, aKey, aLimit, aRv);
1089 }
1090 
OpenCursor(JSContext * aCx,JS::Handle<JS::Value> aRange,IDBCursorDirection aDirection,ErrorResult & aRv)1091 RefPtr<IDBRequest> IDBObjectStore::OpenCursor(JSContext* aCx,
1092                                               JS::Handle<JS::Value> aRange,
1093                                               IDBCursorDirection aDirection,
1094                                               ErrorResult& aRv) {
1095   AssertIsOnOwningThread();
1096 
1097   return OpenCursorInternal(/* aKeysOnly */ false, aCx, aRange, aDirection,
1098                             aRv);
1099 }
1100 
OpenCursor(JSContext * aCx,IDBCursorDirection aDirection,ErrorResult & aRv)1101 RefPtr<IDBRequest> IDBObjectStore::OpenCursor(JSContext* aCx,
1102                                               IDBCursorDirection aDirection,
1103                                               ErrorResult& aRv) {
1104   AssertIsOnOwningThread();
1105 
1106   return OpenCursorInternal(/* aKeysOnly */ false, aCx,
1107                             JS::UndefinedHandleValue, aDirection, aRv);
1108 }
1109 
OpenKeyCursor(JSContext * aCx,JS::Handle<JS::Value> aRange,IDBCursorDirection aDirection,ErrorResult & aRv)1110 RefPtr<IDBRequest> IDBObjectStore::OpenKeyCursor(JSContext* aCx,
1111                                                  JS::Handle<JS::Value> aRange,
1112                                                  IDBCursorDirection aDirection,
1113                                                  ErrorResult& aRv) {
1114   AssertIsOnOwningThread();
1115 
1116   return OpenCursorInternal(/* aKeysOnly */ true, aCx, aRange, aDirection, aRv);
1117 }
1118 
Index(const nsAString & aName,ErrorResult & aRv)1119 RefPtr<IDBIndex> IDBObjectStore::Index(const nsAString& aName,
1120                                        ErrorResult& aRv) {
1121   AssertIsOnOwningThread();
1122 
1123   if (mTransaction->IsCommittingOrFinished() || mDeletedSpec) {
1124     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1125     return nullptr;
1126   }
1127 
1128   const nsTArray<IndexMetadata>& indexMetadatas = mSpec->indexes();
1129 
1130   const auto endIndexMetadatas = indexMetadatas.cend();
1131   const auto foundMetadata =
1132       std::find_if(indexMetadatas.cbegin(), endIndexMetadatas,
1133                    [&aName](const auto& indexMetadata) {
1134                      return indexMetadata.name() == aName;
1135                    });
1136 
1137   if (foundMetadata == endIndexMetadatas) {
1138     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR);
1139     return nullptr;
1140   }
1141 
1142   const IndexMetadata& metadata = *foundMetadata;
1143 
1144   const auto endIndexes = mIndexes.cend();
1145   const auto foundIndex =
1146       std::find_if(mIndexes.cbegin(), endIndexes,
1147                    [desiredId = metadata.id()](const auto& index) {
1148                      return index->Id() == desiredId;
1149                    });
1150 
1151   RefPtr<IDBIndex> index;
1152 
1153   if (foundIndex == endIndexes) {
1154     index = IDBIndex::Create(this, metadata);
1155     MOZ_ASSERT(index);
1156 
1157     mIndexes.AppendElement(index);
1158   } else {
1159     index = *foundIndex;
1160   }
1161 
1162   return index;
1163 }
1164 
1165 NS_IMPL_CYCLE_COLLECTION_MULTI_ZONE_JSHOLDER_CLASS(IDBObjectStore)
1166 
1167 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBObjectStore)
1168   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
1169   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCachedKeyPath)
1170 NS_IMPL_CYCLE_COLLECTION_TRACE_END
1171 
1172 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBObjectStore)
1173   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransaction)
1174   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIndexes)
1175   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDeletedIndexes)
1176 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1177 
1178 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBObjectStore)
1179   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
1180 
1181   // Don't unlink mTransaction!
1182 
1183   NS_IMPL_CYCLE_COLLECTION_UNLINK(mIndexes)
1184   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDeletedIndexes)
1185 
1186   tmp->mCachedKeyPath.setUndefined();
1187 
1188   if (tmp->mRooted) {
1189     mozilla::DropJSObjects(tmp);
1190     tmp->mRooted = false;
1191   }
1192 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1193 
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBObjectStore)1194 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBObjectStore)
1195   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
1196   NS_INTERFACE_MAP_ENTRY(nsISupports)
1197 NS_INTERFACE_MAP_END
1198 
1199 NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBObjectStore)
1200 NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBObjectStore)
1201 
1202 JSObject* IDBObjectStore::WrapObject(JSContext* aCx,
1203                                      JS::Handle<JSObject*> aGivenProto) {
1204   return IDBObjectStore_Binding::Wrap(aCx, this, aGivenProto);
1205 }
1206 
GetParentObject() const1207 nsIGlobalObject* IDBObjectStore::GetParentObject() const {
1208   return mTransaction->GetParentObject();
1209 }
1210 
GetKeyPath(JSContext * aCx,JS::MutableHandle<JS::Value> aResult,ErrorResult & aRv)1211 void IDBObjectStore::GetKeyPath(JSContext* aCx,
1212                                 JS::MutableHandle<JS::Value> aResult,
1213                                 ErrorResult& aRv) {
1214   if (!mCachedKeyPath.isUndefined()) {
1215     aResult.set(mCachedKeyPath);
1216     return;
1217   }
1218 
1219   aRv = GetKeyPath().ToJSVal(aCx, mCachedKeyPath);
1220   if (NS_WARN_IF(aRv.Failed())) {
1221     return;
1222   }
1223 
1224   if (mCachedKeyPath.isGCThing()) {
1225     mozilla::HoldJSObjects(this);
1226     mRooted = true;
1227   }
1228 
1229   aResult.set(mCachedKeyPath);
1230 }
1231 
IndexNames()1232 RefPtr<DOMStringList> IDBObjectStore::IndexNames() {
1233   AssertIsOnOwningThread();
1234 
1235   return CreateSortedDOMStringList(
1236       mSpec->indexes(), [](const auto& index) { return index.name(); });
1237 }
1238 
GetInternal(bool aKeyOnly,JSContext * aCx,JS::Handle<JS::Value> aKey,ErrorResult & aRv)1239 RefPtr<IDBRequest> IDBObjectStore::GetInternal(bool aKeyOnly, JSContext* aCx,
1240                                                JS::Handle<JS::Value> aKey,
1241                                                ErrorResult& aRv) {
1242   AssertIsOnOwningThread();
1243 
1244   if (mDeletedSpec) {
1245     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
1246     return nullptr;
1247   }
1248 
1249   if (!mTransaction->IsActive()) {
1250     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
1251     return nullptr;
1252   }
1253 
1254   RefPtr<IDBKeyRange> keyRange;
1255   IDBKeyRange::FromJSVal(aCx, aKey, &keyRange, aRv);
1256   if (aRv.Failed()) {
1257     return nullptr;
1258   }
1259 
1260   if (!keyRange) {
1261     // Must specify a key or keyRange for get().
1262     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_KEY_ERR);
1263     return nullptr;
1264   }
1265 
1266   const int64_t id = Id();
1267 
1268   SerializedKeyRange serializedKeyRange;
1269   keyRange->ToSerialized(serializedKeyRange);
1270 
1271   const auto& params =
1272       aKeyOnly ? RequestParams{ObjectStoreGetKeyParams(id, serializedKeyRange)}
1273                : RequestParams{ObjectStoreGetParams(id, serializedKeyRange)};
1274 
1275   auto request = GenerateRequest(aCx, this).unwrap();
1276 
1277   IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
1278       "database(%s).transaction(%s).objectStore(%s).get(%s)",
1279       "IDBObjectStore.get(%.0s%.0s%.0s%.0s)",
1280       mTransaction->LoggingSerialNumber(), request->LoggingSerialNumber(),
1281       IDB_LOG_STRINGIFY(mTransaction->Database()),
1282       IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(this),
1283       IDB_LOG_STRINGIFY(keyRange));
1284 
1285   // TODO: This is necessary to preserve request ordering only. Proper
1286   // sequencing of requests should be done in a more sophisticated manner that
1287   // doesn't require invalidating cursor caches (Bug 1580499).
1288   mTransaction->InvalidateCursorCaches();
1289 
1290   mTransaction->StartRequest(request, params);
1291 
1292   return request;
1293 }
1294 
DeleteInternal(JSContext * aCx,JS::Handle<JS::Value> aKey,bool aFromCursor,ErrorResult & aRv)1295 RefPtr<IDBRequest> IDBObjectStore::DeleteInternal(JSContext* aCx,
1296                                                   JS::Handle<JS::Value> aKey,
1297                                                   bool aFromCursor,
1298                                                   ErrorResult& aRv) {
1299   AssertIsOnOwningThread();
1300 
1301   if (mDeletedSpec) {
1302     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
1303     return nullptr;
1304   }
1305 
1306   if (!mTransaction->IsActive()) {
1307     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
1308     return nullptr;
1309   }
1310 
1311   if (!mTransaction->IsWriteAllowed()) {
1312     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR);
1313     return nullptr;
1314   }
1315 
1316   RefPtr<IDBKeyRange> keyRange;
1317   IDBKeyRange::FromJSVal(aCx, aKey, &keyRange, aRv);
1318   if (NS_WARN_IF((aRv.Failed()))) {
1319     return nullptr;
1320   }
1321 
1322   if (!keyRange) {
1323     // Must specify a key or keyRange for delete().
1324     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_KEY_ERR);
1325     return nullptr;
1326   }
1327 
1328   ObjectStoreDeleteParams params;
1329   params.objectStoreId() = Id();
1330   keyRange->ToSerialized(params.keyRange());
1331 
1332   auto request = GenerateRequest(aCx, this).unwrap();
1333 
1334   if (!aFromCursor) {
1335     IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
1336         "database(%s).transaction(%s).objectStore(%s).delete(%s)",
1337         "IDBObjectStore.delete(%.0s%.0s%.0s%.0s)",
1338         mTransaction->LoggingSerialNumber(), request->LoggingSerialNumber(),
1339         IDB_LOG_STRINGIFY(mTransaction->Database()),
1340         IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(this),
1341         IDB_LOG_STRINGIFY(keyRange));
1342   }
1343 
1344   mTransaction->StartRequest(request, params);
1345 
1346   mTransaction->InvalidateCursorCaches();
1347 
1348   return request;
1349 }
1350 
CreateIndex(const nsAString & aName,const StringOrStringSequence & aKeyPath,const IDBIndexParameters & aOptionalParameters,ErrorResult & aRv)1351 RefPtr<IDBIndex> IDBObjectStore::CreateIndex(
1352     const nsAString& aName, const StringOrStringSequence& aKeyPath,
1353     const IDBIndexParameters& aOptionalParameters, ErrorResult& aRv) {
1354   AssertIsOnOwningThread();
1355 
1356   if (mTransaction->GetMode() != IDBTransaction::Mode::VersionChange ||
1357       mDeletedSpec) {
1358     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
1359     return nullptr;
1360   }
1361 
1362   const auto transaction = IDBTransaction::MaybeCurrent();
1363   if (!transaction || transaction != mTransaction || !transaction->IsActive()) {
1364     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
1365     return nullptr;
1366   }
1367 
1368   const auto& indexes = mSpec->indexes();
1369   const auto end = indexes.cend();
1370   const auto foundIt = std::find_if(
1371       indexes.cbegin(), end,
1372       [&aName](const auto& index) { return aName == index.name(); });
1373   if (foundIt != end) {
1374     aRv.ThrowConstraintError(nsPrintfCString(
1375         "Index named '%s' already exists at index '%zu'",
1376         NS_ConvertUTF16toUTF8(aName).get(), foundIt.GetIndex()));
1377     return nullptr;
1378   }
1379 
1380   const auto checkValid = [](const auto& keyPath) -> Result<KeyPath, nsresult> {
1381     if (!keyPath.IsValid()) {
1382       return Err(NS_ERROR_DOM_SYNTAX_ERR);
1383     }
1384 
1385     return keyPath;
1386   };
1387 
1388   QM_NOTEONLY_TRY_UNWRAP(
1389       const auto maybeKeyPath,
1390       ([&aKeyPath, checkValid]() -> Result<KeyPath, nsresult> {
1391         if (aKeyPath.IsString()) {
1392           QM_TRY_RETURN(
1393               KeyPath::Parse(aKeyPath.GetAsString()).andThen(checkValid));
1394         }
1395 
1396         MOZ_ASSERT(aKeyPath.IsStringSequence());
1397         if (aKeyPath.GetAsStringSequence().IsEmpty()) {
1398           return Err(NS_ERROR_DOM_SYNTAX_ERR);
1399         }
1400 
1401         QM_TRY_RETURN(
1402             KeyPath::Parse(aKeyPath.GetAsStringSequence()).andThen(checkValid));
1403       })());
1404   if (!maybeKeyPath) {
1405     aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
1406     return nullptr;
1407   }
1408 
1409   const auto& keyPath = maybeKeyPath.ref();
1410 
1411   if (aOptionalParameters.mMultiEntry && keyPath.IsArray()) {
1412     aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
1413     return nullptr;
1414   }
1415 
1416 #ifdef DEBUG
1417   {
1418     const auto duplicateIndexName = std::any_of(
1419         mIndexes.cbegin(), mIndexes.cend(),
1420         [&aName](const auto& index) { return index->Name() == aName; });
1421     MOZ_ASSERT(!duplicateIndexName);
1422   }
1423 #endif
1424 
1425   const IndexMetadata* const oldMetadataElements =
1426       indexes.IsEmpty() ? nullptr : indexes.Elements();
1427 
1428   // With this setup we only validate the passed in locale name by the time we
1429   // get to encoding Keys. Maybe we should do it here right away and error out.
1430 
1431   // Valid locale names are always ASCII as per BCP-47.
1432   nsCString locale = NS_LossyConvertUTF16toASCII(aOptionalParameters.mLocale);
1433   bool autoLocale = locale.EqualsASCII("auto");
1434   if (autoLocale) {
1435     locale = IndexedDatabaseManager::GetLocale();
1436   }
1437 
1438   IndexMetadata* const metadata = mSpec->indexes().EmplaceBack(
1439       transaction->NextIndexId(), nsString(aName), keyPath, locale,
1440       aOptionalParameters.mUnique, aOptionalParameters.mMultiEntry, autoLocale);
1441 
1442   if (oldMetadataElements && oldMetadataElements != indexes.Elements()) {
1443     MOZ_ASSERT(indexes.Length() > 1);
1444 
1445     // Array got moved, update the spec pointers for all live indexes.
1446     RefreshSpec(/* aMayDelete */ false);
1447   }
1448 
1449   transaction->CreateIndex(this, *metadata);
1450 
1451   auto index = IDBIndex::Create(this, *metadata);
1452 
1453   mIndexes.AppendElement(index);
1454 
1455   // Don't do this in the macro because we always need to increment the serial
1456   // number to keep in sync with the parent.
1457   const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber();
1458 
1459   IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
1460       "database(%s).transaction(%s).objectStore(%s).createIndex(%s)",
1461       "IDBObjectStore.createIndex(%.0s%.0s%.0s%.0s)",
1462       mTransaction->LoggingSerialNumber(), requestSerialNumber,
1463       IDB_LOG_STRINGIFY(mTransaction->Database()),
1464       IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(this),
1465       IDB_LOG_STRINGIFY(index));
1466 
1467   return index;
1468 }
1469 
DeleteIndex(const nsAString & aName,ErrorResult & aRv)1470 void IDBObjectStore::DeleteIndex(const nsAString& aName, ErrorResult& aRv) {
1471   AssertIsOnOwningThread();
1472 
1473   if (mTransaction->GetMode() != IDBTransaction::Mode::VersionChange ||
1474       mDeletedSpec) {
1475     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
1476     return;
1477   }
1478 
1479   const auto transaction = IDBTransaction::MaybeCurrent();
1480   if (!transaction || transaction != mTransaction || !transaction->IsActive()) {
1481     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
1482     return;
1483   }
1484 
1485   const auto& metadataArray = mSpec->indexes();
1486 
1487   const auto endMetadata = metadataArray.cend();
1488   const auto foundMetadataIt = std::find_if(
1489       metadataArray.cbegin(), endMetadata,
1490       [&aName](const auto& metadata) { return aName == metadata.name(); });
1491 
1492   if (foundMetadataIt == endMetadata) {
1493     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR);
1494     return;
1495   }
1496 
1497   const auto foundId = foundMetadataIt->id();
1498   MOZ_ASSERT(foundId);
1499 
1500   // Must remove index from mIndexes before altering the metadata array!
1501   {
1502     const auto end = mIndexes.end();
1503     const auto foundIt = std::find_if(
1504         mIndexes.begin(), end,
1505         [foundId](const auto& index) { return index->Id() == foundId; });
1506     // TODO: Or should we assert foundIt != end?
1507     if (foundIt != end) {
1508       auto& index = *foundIt;
1509 
1510       index->NoteDeletion();
1511 
1512       mDeletedIndexes.EmplaceBack(std::move(index));
1513       mIndexes.RemoveElementAt(foundIt.GetIndex());
1514     }
1515   }
1516 
1517   mSpec->indexes().RemoveElementAt(foundMetadataIt.GetIndex());
1518 
1519   RefreshSpec(/* aMayDelete */ false);
1520 
1521   // Don't do this in the macro because we always need to increment the serial
1522   // number to keep in sync with the parent.
1523   const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber();
1524 
1525   IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
1526       "database(%s).transaction(%s).objectStore(%s)."
1527       "deleteIndex(\"%s\")",
1528       "IDBObjectStore.deleteIndex(%.0s%.0s%.0s%.0s)",
1529       mTransaction->LoggingSerialNumber(), requestSerialNumber,
1530       IDB_LOG_STRINGIFY(mTransaction->Database()),
1531       IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(this),
1532       NS_ConvertUTF16toUTF8(aName).get());
1533 
1534   transaction->DeleteIndex(this, foundId);
1535 }
1536 
Count(JSContext * aCx,JS::Handle<JS::Value> aKey,ErrorResult & aRv)1537 RefPtr<IDBRequest> IDBObjectStore::Count(JSContext* aCx,
1538                                          JS::Handle<JS::Value> aKey,
1539                                          ErrorResult& aRv) {
1540   AssertIsOnOwningThread();
1541 
1542   if (mDeletedSpec) {
1543     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
1544     return nullptr;
1545   }
1546 
1547   if (!mTransaction->IsActive()) {
1548     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
1549     return nullptr;
1550   }
1551 
1552   RefPtr<IDBKeyRange> keyRange;
1553   IDBKeyRange::FromJSVal(aCx, aKey, &keyRange, aRv);
1554   if (aRv.Failed()) {
1555     return nullptr;
1556   }
1557 
1558   ObjectStoreCountParams params;
1559   params.objectStoreId() = Id();
1560 
1561   if (keyRange) {
1562     SerializedKeyRange serializedKeyRange;
1563     keyRange->ToSerialized(serializedKeyRange);
1564     params.optionalKeyRange().emplace(serializedKeyRange);
1565   }
1566 
1567   auto request = GenerateRequest(aCx, this).unwrap();
1568 
1569   IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
1570       "database(%s).transaction(%s).objectStore(%s).count(%s)",
1571       "IDBObjectStore.count(%.0s%.0s%.0s%.0s)",
1572       mTransaction->LoggingSerialNumber(), request->LoggingSerialNumber(),
1573       IDB_LOG_STRINGIFY(mTransaction->Database()),
1574       IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(this),
1575       IDB_LOG_STRINGIFY(keyRange));
1576 
1577   // TODO: This is necessary to preserve request ordering only. Proper
1578   // sequencing of requests should be done in a more sophisticated manner that
1579   // doesn't require invalidating cursor caches (Bug 1580499).
1580   mTransaction->InvalidateCursorCaches();
1581 
1582   mTransaction->StartRequest(request, params);
1583 
1584   return request;
1585 }
1586 
OpenCursorInternal(bool aKeysOnly,JSContext * aCx,JS::Handle<JS::Value> aRange,IDBCursorDirection aDirection,ErrorResult & aRv)1587 RefPtr<IDBRequest> IDBObjectStore::OpenCursorInternal(
1588     bool aKeysOnly, JSContext* aCx, JS::Handle<JS::Value> aRange,
1589     IDBCursorDirection aDirection, ErrorResult& aRv) {
1590   AssertIsOnOwningThread();
1591   MOZ_ASSERT(aCx);
1592 
1593   if (mDeletedSpec) {
1594     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
1595     return nullptr;
1596   }
1597 
1598   if (!mTransaction->IsActive()) {
1599     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
1600     return nullptr;
1601   }
1602 
1603   RefPtr<IDBKeyRange> keyRange;
1604   IDBKeyRange::FromJSVal(aCx, aRange, &keyRange, aRv);
1605   if (NS_WARN_IF(aRv.Failed())) {
1606     return nullptr;
1607   }
1608 
1609   const int64_t objectStoreId = Id();
1610 
1611   Maybe<SerializedKeyRange> optionalKeyRange;
1612 
1613   if (keyRange) {
1614     SerializedKeyRange serializedKeyRange;
1615     keyRange->ToSerialized(serializedKeyRange);
1616 
1617     optionalKeyRange.emplace(std::move(serializedKeyRange));
1618   }
1619 
1620   const CommonOpenCursorParams commonParams = {
1621       objectStoreId, std::move(optionalKeyRange), aDirection};
1622 
1623   // TODO: It would be great if the IPDL generator created a constructor
1624   // accepting a CommonOpenCursorParams by value or rvalue reference.
1625   const auto params =
1626       aKeysOnly ? OpenCursorParams{ObjectStoreOpenKeyCursorParams{commonParams}}
1627                 : OpenCursorParams{ObjectStoreOpenCursorParams{commonParams}};
1628 
1629   auto request = GenerateRequest(aCx, this).unwrap();
1630 
1631   if (aKeysOnly) {
1632     IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
1633         "database(%s).transaction(%s).objectStore(%s)."
1634         "openKeyCursor(%s, %s)",
1635         "IDBObjectStore.openKeyCursor(%.0s%.0s%.0s%.0s%.0s)",
1636         mTransaction->LoggingSerialNumber(), request->LoggingSerialNumber(),
1637         IDB_LOG_STRINGIFY(mTransaction->Database()),
1638         IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(this),
1639         IDB_LOG_STRINGIFY(keyRange), IDB_LOG_STRINGIFY(aDirection));
1640   } else {
1641     IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
1642         "database(%s).transaction(%s).objectStore(%s)."
1643         "openCursor(%s, %s)",
1644         "IDBObjectStore.openCursor(%.0s%.0s%.0s%.0s%.0s)",
1645         mTransaction->LoggingSerialNumber(), request->LoggingSerialNumber(),
1646         IDB_LOG_STRINGIFY(mTransaction->Database()),
1647         IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(this),
1648         IDB_LOG_STRINGIFY(keyRange), IDB_LOG_STRINGIFY(aDirection));
1649   }
1650 
1651   const auto actor =
1652       aKeysOnly
1653           ? static_cast<SafeRefPtr<BackgroundCursorChildBase>>(
1654                 MakeSafeRefPtr<
1655                     BackgroundCursorChild<IDBCursorType::ObjectStoreKey>>(
1656                     request, this, aDirection))
1657           : MakeSafeRefPtr<BackgroundCursorChild<IDBCursorType::ObjectStore>>(
1658                 request, this, aDirection);
1659 
1660   // TODO: This is necessary to preserve request ordering only. Proper
1661   // sequencing of requests should be done in a more sophisticated manner that
1662   // doesn't require invalidating cursor caches (Bug 1580499).
1663   mTransaction->InvalidateCursorCaches();
1664 
1665   mTransaction->OpenCursor(*actor, params);
1666 
1667   return request;
1668 }
1669 
RefreshSpec(bool aMayDelete)1670 void IDBObjectStore::RefreshSpec(bool aMayDelete) {
1671   AssertIsOnOwningThread();
1672   MOZ_ASSERT_IF(mDeletedSpec, mSpec == mDeletedSpec.get());
1673 
1674   auto* const foundObjectStoreSpec =
1675       mTransaction->Database()->LookupModifiableObjectStoreSpec(
1676           [id = Id()](const auto& objSpec) {
1677             return objSpec.metadata().id() == id;
1678           });
1679   if (foundObjectStoreSpec) {
1680     mSpec = foundObjectStoreSpec;
1681 
1682     for (auto& index : mIndexes) {
1683       index->RefreshMetadata(aMayDelete);
1684     }
1685 
1686     for (auto& index : mDeletedIndexes) {
1687       index->RefreshMetadata(false);
1688     }
1689   }
1690 
1691   MOZ_ASSERT_IF(!aMayDelete && !mDeletedSpec, foundObjectStoreSpec);
1692 
1693   if (foundObjectStoreSpec) {
1694     MOZ_ASSERT(mSpec != mDeletedSpec.get());
1695     mDeletedSpec = nullptr;
1696   } else {
1697     NoteDeletion();
1698   }
1699 }
1700 
Spec() const1701 const ObjectStoreSpec& IDBObjectStore::Spec() const {
1702   AssertIsOnOwningThread();
1703   MOZ_ASSERT(mSpec);
1704 
1705   return *mSpec;
1706 }
1707 
NoteDeletion()1708 void IDBObjectStore::NoteDeletion() {
1709   AssertIsOnOwningThread();
1710   MOZ_ASSERT(mSpec);
1711   MOZ_ASSERT(Id() == mSpec->metadata().id());
1712 
1713   if (mDeletedSpec) {
1714     MOZ_ASSERT(mDeletedSpec.get() == mSpec);
1715     return;
1716   }
1717 
1718   // Copy the spec here.
1719   mDeletedSpec = MakeUnique<ObjectStoreSpec>(*mSpec);
1720   mDeletedSpec->indexes().Clear();
1721 
1722   mSpec = mDeletedSpec.get();
1723 
1724   for (const auto& index : mIndexes) {
1725     index->NoteDeletion();
1726   }
1727 }
1728 
Name() const1729 const nsString& IDBObjectStore::Name() const {
1730   AssertIsOnOwningThread();
1731   MOZ_ASSERT(mSpec);
1732 
1733   return mSpec->metadata().name();
1734 }
1735 
SetName(const nsAString & aName,ErrorResult & aRv)1736 void IDBObjectStore::SetName(const nsAString& aName, ErrorResult& aRv) {
1737   AssertIsOnOwningThread();
1738 
1739   if (mTransaction->GetMode() != IDBTransaction::Mode::VersionChange ||
1740       mDeletedSpec) {
1741     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1742     return;
1743   }
1744 
1745   const auto transaction = IDBTransaction::MaybeCurrent();
1746   if (!transaction || transaction != mTransaction || !transaction->IsActive()) {
1747     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
1748     return;
1749   }
1750 
1751   if (aName == mSpec->metadata().name()) {
1752     return;
1753   }
1754 
1755   // Cache logging string of this object store before renaming.
1756   const LoggingString loggingOldObjectStore(this);
1757 
1758   const nsresult rv =
1759       transaction->Database()->RenameObjectStore(mSpec->metadata().id(), aName);
1760 
1761   if (NS_FAILED(rv)) {
1762     aRv.Throw(rv);
1763     return;
1764   }
1765 
1766   // Don't do this in the macro because we always need to increment the serial
1767   // number to keep in sync with the parent.
1768   const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber();
1769 
1770   IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
1771       "database(%s).transaction(%s).objectStore(%s).rename(%s)",
1772       "IDBObjectStore.rename(%.0s%.0s%.0s%.0s)",
1773       mTransaction->LoggingSerialNumber(), requestSerialNumber,
1774       IDB_LOG_STRINGIFY(mTransaction->Database()),
1775       IDB_LOG_STRINGIFY(*mTransaction), loggingOldObjectStore.get(),
1776       IDB_LOG_STRINGIFY(this));
1777 
1778   transaction->RenameObjectStore(mSpec->metadata().id(), aName);
1779 }
1780 
AutoIncrement() const1781 bool IDBObjectStore::AutoIncrement() const {
1782   AssertIsOnOwningThread();
1783   MOZ_ASSERT(mSpec);
1784 
1785   return mSpec->metadata().autoIncrement();
1786 }
1787 
GetKeyPath() const1788 const indexedDB::KeyPath& IDBObjectStore::GetKeyPath() const {
1789   AssertIsOnOwningThread();
1790   MOZ_ASSERT(mSpec);
1791 
1792   return mSpec->metadata().keyPath();
1793 }
1794 
HasValidKeyPath() const1795 bool IDBObjectStore::HasValidKeyPath() const {
1796   AssertIsOnOwningThread();
1797   MOZ_ASSERT(mSpec);
1798 
1799   return GetKeyPath().IsValid();
1800 }
1801 
Clone(JSContext * aCx)1802 bool IDBObjectStore::ValueWrapper::Clone(JSContext* aCx) {
1803   if (mCloned) {
1804     return true;
1805   }
1806 
1807   static const JSStructuredCloneCallbacks callbacks = {
1808       CopyingStructuredCloneReadCallback /* read */,
1809       CopyingStructuredCloneWriteCallback /* write */,
1810       nullptr /* reportError */,
1811       nullptr /* readTransfer */,
1812       nullptr /* writeTransfer */,
1813       nullptr /* freeTransfer */,
1814       nullptr /* canTransfer */,
1815       nullptr /* sabCloned */
1816   };
1817 
1818   StructuredCloneInfo cloneInfo;
1819 
1820   JS::Rooted<JS::Value> clonedValue(aCx);
1821   if (!JS_StructuredClone(aCx, mValue, &clonedValue, &callbacks, &cloneInfo)) {
1822     return false;
1823   }
1824 
1825   mValue = clonedValue;
1826 
1827   mCloned = true;
1828 
1829   return true;
1830 }
1831 
1832 }  // namespace mozilla::dom
1833