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 "IDBCursor.h"
8 
9 #include "IDBDatabase.h"
10 #include "IDBIndex.h"
11 #include "IDBObjectStore.h"
12 #include "IDBRequest.h"
13 #include "IDBTransaction.h"
14 #include "IndexedDatabaseInlines.h"
15 #include "mozilla/ErrorResult.h"
16 #include "mozilla/HoldDropJSObjects.h"
17 #include "mozilla/dom/UnionTypes.h"
18 #include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h"
19 #include "nsString.h"
20 #include "ProfilerHelpers.h"
21 #include "ReportInternalError.h"
22 
23 // Include this last to avoid path problems on Windows.
24 #include "ActorsChild.h"
25 
26 namespace mozilla::dom {
27 
28 using namespace indexedDB;
29 
IDBCursor(BackgroundCursorChildBase * const aBackgroundActor)30 IDBCursor::IDBCursor(BackgroundCursorChildBase* const aBackgroundActor)
31     : mBackgroundActor(WrapNotNull(aBackgroundActor)),
32       mRequest(aBackgroundActor->GetRequest()),
33       mTransaction(&mRequest->MutableTransactionRef()),
34       mCachedKey(JS::UndefinedValue()),
35       mCachedPrimaryKey(JS::UndefinedValue()),
36       mCachedValue(JS::UndefinedValue()),
37       mDirection(aBackgroundActor->GetDirection()),
38       mHaveCachedKey(false),
39       mHaveCachedPrimaryKey(false),
40       mHaveCachedValue(false),
41       mRooted(false),
42       mContinueCalled(false),
43       mHaveValue(true) {
44   MOZ_ASSERT(aBackgroundActor);
45   aBackgroundActor->AssertIsOnOwningThread();
46   MOZ_ASSERT(mRequest);
47 
48   mTransaction->RegisterCursor(*this);
49 }
50 
51 template <IDBCursor::Type CursorType>
~IDBTypedCursor()52 IDBTypedCursor<CursorType>::~IDBTypedCursor() {
53   AssertIsOnOwningThread();
54 
55   mTransaction->UnregisterCursor(*this);
56 
57   DropJSObjects();
58 
59   if (mBackgroundActor) {
60     (*mBackgroundActor)->SendDeleteMeInternal();
61     MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!");
62   }
63 }
64 
65 // static
Create(BackgroundCursorChild<Type::ObjectStore> * const aBackgroundActor,Key aKey,StructuredCloneReadInfoChild && aCloneInfo)66 RefPtr<IDBObjectStoreCursor> IDBCursor::Create(
67     BackgroundCursorChild<Type::ObjectStore>* const aBackgroundActor, Key aKey,
68     StructuredCloneReadInfoChild&& aCloneInfo) {
69   MOZ_ASSERT(aBackgroundActor);
70   aBackgroundActor->AssertIsOnOwningThread();
71   MOZ_ASSERT(!aKey.IsUnset());
72 
73   return MakeRefPtr<IDBObjectStoreCursor>(aBackgroundActor, std::move(aKey),
74                                           std::move(aCloneInfo));
75 }
76 
77 // static
Create(BackgroundCursorChild<Type::ObjectStoreKey> * const aBackgroundActor,Key aKey)78 RefPtr<IDBObjectStoreKeyCursor> IDBCursor::Create(
79     BackgroundCursorChild<Type::ObjectStoreKey>* const aBackgroundActor,
80     Key aKey) {
81   MOZ_ASSERT(aBackgroundActor);
82   aBackgroundActor->AssertIsOnOwningThread();
83   MOZ_ASSERT(!aKey.IsUnset());
84 
85   return MakeRefPtr<IDBObjectStoreKeyCursor>(aBackgroundActor, std::move(aKey));
86 }
87 
88 // static
Create(BackgroundCursorChild<Type::Index> * const aBackgroundActor,Key aKey,Key aSortKey,Key aPrimaryKey,StructuredCloneReadInfoChild && aCloneInfo)89 RefPtr<IDBIndexCursor> IDBCursor::Create(
90     BackgroundCursorChild<Type::Index>* const aBackgroundActor, Key aKey,
91     Key aSortKey, Key aPrimaryKey, StructuredCloneReadInfoChild&& aCloneInfo) {
92   MOZ_ASSERT(aBackgroundActor);
93   aBackgroundActor->AssertIsOnOwningThread();
94   MOZ_ASSERT(!aKey.IsUnset());
95   MOZ_ASSERT(!aPrimaryKey.IsUnset());
96 
97   return MakeRefPtr<IDBIndexCursor>(aBackgroundActor, std::move(aKey),
98                                     std::move(aSortKey), std::move(aPrimaryKey),
99                                     std::move(aCloneInfo));
100 }
101 
102 // static
Create(BackgroundCursorChild<Type::IndexKey> * const aBackgroundActor,Key aKey,Key aSortKey,Key aPrimaryKey)103 RefPtr<IDBIndexKeyCursor> IDBCursor::Create(
104     BackgroundCursorChild<Type::IndexKey>* const aBackgroundActor, Key aKey,
105     Key aSortKey, Key aPrimaryKey) {
106   MOZ_ASSERT(aBackgroundActor);
107   aBackgroundActor->AssertIsOnOwningThread();
108   MOZ_ASSERT(!aKey.IsUnset());
109   MOZ_ASSERT(!aPrimaryKey.IsUnset());
110 
111   return MakeRefPtr<IDBIndexKeyCursor>(aBackgroundActor, std::move(aKey),
112                                        std::move(aSortKey),
113                                        std::move(aPrimaryKey));
114 }
115 
116 #ifdef DEBUG
117 
AssertIsOnOwningThread() const118 void IDBCursor::AssertIsOnOwningThread() const {
119   MOZ_ASSERT(mTransaction);
120   mTransaction->AssertIsOnOwningThread();
121 }
122 
123 #endif  // DEBUG
124 
125 template <IDBCursor::Type CursorType>
DropJSObjects()126 void IDBTypedCursor<CursorType>::DropJSObjects() {
127   AssertIsOnOwningThread();
128 
129   Reset();
130 
131   if (!mRooted) {
132     return;
133   }
134 
135   mRooted = false;
136 
137   mozilla::DropJSObjects(this);
138 }
139 
140 template <IDBCursor::Type CursorType>
IsSourceDeleted() const141 bool IDBTypedCursor<CursorType>::IsSourceDeleted() const {
142   AssertIsOnOwningThread();
143   MOZ_ASSERT(mTransaction);
144   MOZ_ASSERT(mTransaction->IsActive());
145 
146   const auto* const sourceObjectStore = [this]() -> const IDBObjectStore* {
147     if constexpr (IsObjectStoreCursor) {
148       return mSource;
149     } else {
150       if (GetSourceRef().IsDeleted()) {
151         return nullptr;
152       }
153 
154       const auto* const res = GetSourceRef().ObjectStore();
155       MOZ_ASSERT(res);
156       return res;
157     }
158   }();
159 
160   return !sourceObjectStore || sourceObjectStore->IsDeleted();
161 }
162 
ResetBase()163 void IDBCursor::ResetBase() {
164   AssertIsOnOwningThread();
165 
166   mCachedKey.setUndefined();
167   mCachedPrimaryKey.setUndefined();
168   mCachedValue.setUndefined();
169 
170   mHaveCachedKey = false;
171   mHaveCachedPrimaryKey = false;
172   mHaveCachedValue = false;
173   mHaveValue = false;
174   mContinueCalled = false;
175 }
176 
177 template <IDBCursor::Type CursorType>
Reset()178 void IDBTypedCursor<CursorType>::Reset() {
179   AssertIsOnOwningThread();
180 
181   if constexpr (!IsKeyOnlyCursor) {
182     IDBObjectStore::ClearCloneReadInfo(mData.mCloneInfo);
183   }
184 
185   ResetBase();
186 }
187 
GetParentObject() const188 nsIGlobalObject* IDBCursor::GetParentObject() const {
189   AssertIsOnOwningThread();
190   MOZ_ASSERT(mTransaction);
191 
192   return mTransaction->GetParentObject();
193 }
194 
GetDirection() const195 IDBCursorDirection IDBCursor::GetDirection() const {
196   AssertIsOnOwningThread();
197 
198   switch (mDirection) {
199     case Direction::Next:
200       return IDBCursorDirection::Next;
201 
202     case Direction::Nextunique:
203       return IDBCursorDirection::Nextunique;
204 
205     case Direction::Prev:
206       return IDBCursorDirection::Prev;
207 
208     case Direction::Prevunique:
209       return IDBCursorDirection::Prevunique;
210 
211     default:
212       MOZ_CRASH("Bad direction!");
213   }
214 }
215 
Request() const216 RefPtr<IDBRequest> IDBCursor::Request() const {
217   AssertIsOnOwningThread();
218   return mRequest;
219 }
220 
221 template <IDBCursor::Type CursorType>
GetSource(OwningIDBObjectStoreOrIDBIndex & aSource) const222 void IDBTypedCursor<CursorType>::GetSource(
223     OwningIDBObjectStoreOrIDBIndex& aSource) const {
224   AssertIsOnOwningThread();
225 
226   if constexpr (IsObjectStoreCursor) {
227     aSource.SetAsIDBObjectStore() = mSource;
228   } else {
229     aSource.SetAsIDBIndex() = mSource;
230   }
231 }
232 
233 template <IDBCursor::Type CursorType>
GetKey(JSContext * const aCx,JS::MutableHandle<JS::Value> aResult,ErrorResult & aRv)234 void IDBTypedCursor<CursorType>::GetKey(JSContext* const aCx,
235                                         JS::MutableHandle<JS::Value> aResult,
236                                         ErrorResult& aRv) {
237   AssertIsOnOwningThread();
238   MOZ_ASSERT(!mData.mKey.IsUnset() || !mHaveValue);
239 
240   if (!mHaveValue) {
241     aResult.setUndefined();
242     return;
243   }
244 
245   if (!mHaveCachedKey) {
246     if (!mRooted) {
247       mozilla::HoldJSObjects(this);
248       mRooted = true;
249     }
250 
251     aRv = mData.mKey.ToJSVal(aCx, mCachedKey);
252     if (NS_WARN_IF(aRv.Failed())) {
253       return;
254     }
255 
256     mHaveCachedKey = true;
257   }
258 
259   aResult.set(mCachedKey);
260 }
261 
262 template <IDBCursor::Type CursorType>
GetPrimaryKey(JSContext * const aCx,JS::MutableHandle<JS::Value> aResult,ErrorResult & aRv)263 void IDBTypedCursor<CursorType>::GetPrimaryKey(
264     JSContext* const aCx, JS::MutableHandle<JS::Value> aResult,
265     ErrorResult& aRv) {
266   AssertIsOnOwningThread();
267 
268   if (!mHaveValue) {
269     aResult.setUndefined();
270     return;
271   }
272 
273   if (!mHaveCachedPrimaryKey) {
274     if (!mRooted) {
275       mozilla::HoldJSObjects(this);
276       mRooted = true;
277     }
278 
279     const Key& key = mData.GetObjectStoreKey();
280 
281     MOZ_ASSERT(!key.IsUnset());
282 
283     aRv = key.ToJSVal(aCx, mCachedPrimaryKey);
284     if (NS_WARN_IF(aRv.Failed())) {
285       return;
286     }
287 
288     mHaveCachedPrimaryKey = true;
289   }
290 
291   aResult.set(mCachedPrimaryKey);
292 }
293 
294 template <IDBCursor::Type CursorType>
GetValue(JSContext * const aCx,JS::MutableHandle<JS::Value> aResult,ErrorResult & aRv)295 void IDBTypedCursor<CursorType>::GetValue(JSContext* const aCx,
296                                           JS::MutableHandle<JS::Value> aResult,
297                                           ErrorResult& aRv) {
298   AssertIsOnOwningThread();
299 
300   if constexpr (!IsKeyOnlyCursor) {
301     if (!mHaveValue) {
302       aResult.setUndefined();
303       return;
304     }
305 
306     if (!mHaveCachedValue) {
307       if (!mRooted) {
308         mozilla::HoldJSObjects(this);
309         mRooted = true;
310       }
311 
312       JS::Rooted<JS::Value> val(aCx);
313       if (NS_WARN_IF(!IDBObjectStore::DeserializeValue(
314               aCx, std::move(mData.mCloneInfo), &val))) {
315         aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
316         return;
317       }
318 
319       // XXX This seems redundant, sine mData.mCloneInfo is moved above.
320       IDBObjectStore::ClearCloneReadInfo(mData.mCloneInfo);
321 
322       mCachedValue = val;
323       mHaveCachedValue = true;
324     }
325 
326     aResult.set(mCachedValue);
327   } else {
328     MOZ_CRASH("This shouldn't be callable on a key-only cursor.");
329   }
330 }
331 
332 template <IDBCursor::Type CursorType>
Continue(JSContext * const aCx,JS::Handle<JS::Value> aKey,ErrorResult & aRv)333 void IDBTypedCursor<CursorType>::Continue(JSContext* const aCx,
334                                           JS::Handle<JS::Value> aKey,
335                                           ErrorResult& aRv) {
336   AssertIsOnOwningThread();
337 
338   if (!mTransaction->IsActive()) {
339     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
340     return;
341   }
342 
343   if (IsSourceDeleted() || !mHaveValue || mContinueCalled) {
344     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
345     return;
346   }
347 
348   Key key;
349   auto result = key.SetFromJSVal(aCx, aKey);
350   if (result.isErr()) {
351     aRv = result.unwrapErr().ExtractErrorResult(
352         InvalidMapsTo<NS_ERROR_DOM_INDEXEDDB_DATA_ERR>);
353     return;
354   }
355 
356   if constexpr (!IsObjectStoreCursor) {
357     if (IsLocaleAware() && !key.IsUnset()) {
358       auto result = key.ToLocaleAwareKey(GetSourceRef().Locale());
359       if (result.isErr()) {
360         aRv.Throw(result.inspectErr());
361         return;
362       }
363       key = result.unwrap();
364     }
365   }
366 
367   const Key& sortKey = mData.GetSortKey(IsLocaleAware());
368 
369   if (!key.IsUnset()) {
370     switch (mDirection) {
371       case Direction::Next:
372       case Direction::Nextunique:
373         if (key <= sortKey) {
374           aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
375           return;
376         }
377         break;
378 
379       case Direction::Prev:
380       case Direction::Prevunique:
381         if (key >= sortKey) {
382           aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
383           return;
384         }
385         break;
386 
387       default:
388         MOZ_CRASH("Unknown direction type!");
389     }
390   }
391 
392   const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber();
393   mRequest->SetLoggingSerialNumber(requestSerialNumber);
394 
395   if constexpr (IsObjectStoreCursor) {
396     IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
397         "database(%s).transaction(%s).objectStore(%s)."
398         "cursor(%s).continue(%s)",
399         "IDBCursor.continue(%.0s%.0s%.0s%.0s%.0s)",
400         mTransaction->LoggingSerialNumber(), requestSerialNumber,
401         IDB_LOG_STRINGIFY(mTransaction->Database()),
402         IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(mSource),
403         IDB_LOG_STRINGIFY(mDirection), IDB_LOG_STRINGIFY(key));
404   } else {
405     IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
406         "database(%s).transaction(%s).objectStore(%s)."
407         "index(%s).cursor(%s).continue(%s)",
408         "IDBCursor.continue(%.0s%.0s%.0s%.0s%.0s%.0s)",
409         mTransaction->LoggingSerialNumber(), requestSerialNumber,
410         IDB_LOG_STRINGIFY(mTransaction->Database()),
411         IDB_LOG_STRINGIFY(*mTransaction),
412         IDB_LOG_STRINGIFY(GetSourceRef().ObjectStore()),
413         IDB_LOG_STRINGIFY(mSource), IDB_LOG_STRINGIFY(mDirection),
414         IDB_LOG_STRINGIFY(key));
415   }
416 
417   GetTypedBackgroundActorRef().SendContinueInternal(ContinueParams(key), mData);
418 
419   mContinueCalled = true;
420 }
421 
422 template <IDBCursor::Type CursorType>
ContinuePrimaryKey(JSContext * const aCx,JS::Handle<JS::Value> aKey,JS::Handle<JS::Value> aPrimaryKey,ErrorResult & aRv)423 void IDBTypedCursor<CursorType>::ContinuePrimaryKey(
424     JSContext* const aCx, JS::Handle<JS::Value> aKey,
425     JS::Handle<JS::Value> aPrimaryKey, ErrorResult& aRv) {
426   AssertIsOnOwningThread();
427 
428   if (!mTransaction->IsActive()) {
429     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
430     return;
431   }
432 
433   if (IsSourceDeleted()) {
434     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
435     return;
436   }
437 
438   if (IsObjectStoreCursor ||
439       (mDirection != Direction::Next && mDirection != Direction::Prev)) {
440     aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
441     return;
442   }
443 
444   if constexpr (!IsObjectStoreCursor) {
445     if (!mHaveValue || mContinueCalled) {
446       aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
447       return;
448     }
449 
450     Key key;
451     auto result = key.SetFromJSVal(aCx, aKey);
452     if (result.isErr()) {
453       aRv = result.unwrapErr().ExtractErrorResult(
454           InvalidMapsTo<NS_ERROR_DOM_INDEXEDDB_DATA_ERR>);
455       return;
456     }
457 
458     if (IsLocaleAware() && !key.IsUnset()) {
459       auto result = key.ToLocaleAwareKey(GetSourceRef().Locale());
460       if (result.isErr()) {
461         aRv.Throw(result.inspectErr());
462         return;
463       }
464       key = result.unwrap();
465     }
466 
467     if (key.IsUnset()) {
468       aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
469       return;
470     }
471 
472     Key primaryKey;
473     result = primaryKey.SetFromJSVal(aCx, aPrimaryKey);
474     if (result.isErr()) {
475       aRv = result.unwrapErr().ExtractErrorResult(
476           InvalidMapsTo<NS_ERROR_DOM_INDEXEDDB_DATA_ERR>);
477       return;
478     }
479 
480     if (primaryKey.IsUnset()) {
481       aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
482       return;
483     }
484 
485     const Key& sortKey = mData.GetSortKey(IsLocaleAware());
486 
487     switch (mDirection) {
488       case Direction::Next:
489         if (key < sortKey ||
490             (key == sortKey && primaryKey <= mData.mObjectStoreKey)) {
491           aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
492           return;
493         }
494         break;
495 
496       case Direction::Prev:
497         if (key > sortKey ||
498             (key == sortKey && primaryKey >= mData.mObjectStoreKey)) {
499           aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
500           return;
501         }
502         break;
503 
504       default:
505         MOZ_CRASH("Unknown direction type!");
506     }
507 
508     const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber();
509     mRequest->SetLoggingSerialNumber(requestSerialNumber);
510 
511     IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
512         "database(%s).transaction(%s).objectStore(%s)."
513         "index(%s).cursor(%s).continuePrimaryKey(%s, %s)",
514         "IDBCursor.continuePrimaryKey(%.0s%.0s%.0s%.0s%.0s%.0s%.0s)",
515         mTransaction->LoggingSerialNumber(), requestSerialNumber,
516         IDB_LOG_STRINGIFY(mTransaction->Database()),
517         IDB_LOG_STRINGIFY(*mTransaction),
518         IDB_LOG_STRINGIFY(&GetSourceObjectStoreRef()),
519         IDB_LOG_STRINGIFY(mSource), IDB_LOG_STRINGIFY(mDirection),
520         IDB_LOG_STRINGIFY(key), IDB_LOG_STRINGIFY(primaryKey));
521 
522     GetTypedBackgroundActorRef().SendContinueInternal(
523         ContinuePrimaryKeyParams(key, primaryKey), mData);
524 
525     mContinueCalled = true;
526   }
527 }
528 
529 template <IDBCursor::Type CursorType>
Advance(const uint32_t aCount,ErrorResult & aRv)530 void IDBTypedCursor<CursorType>::Advance(const uint32_t aCount,
531                                          ErrorResult& aRv) {
532   AssertIsOnOwningThread();
533 
534   if (!aCount) {
535     aRv.ThrowTypeError("0 (Zero) is not a valid advance count.");
536     return;
537   }
538 
539   if (!mTransaction->IsActive()) {
540     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
541     return;
542   }
543 
544   if (IsSourceDeleted() || !mHaveValue || mContinueCalled) {
545     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
546     return;
547   }
548 
549   const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber();
550   mRequest->SetLoggingSerialNumber(requestSerialNumber);
551 
552   if constexpr (IsObjectStoreCursor) {
553     IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
554         "database(%s).transaction(%s).objectStore(%s)."
555         "cursor(%s).advance(%" PRIi32 ")",
556         "IDBCursor.advance(%.0s%.0s%.0s%.0s%" PRIi32 ")",
557         mTransaction->LoggingSerialNumber(), requestSerialNumber,
558         IDB_LOG_STRINGIFY(mTransaction->Database()),
559         IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(mSource),
560         IDB_LOG_STRINGIFY(mDirection), aCount);
561   } else {
562     IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
563         "database(%s).transaction(%s).objectStore(%s)."
564         "index(%s).cursor(%s).advance(%" PRIi32 ")",
565         "IDBCursor.advance(%.0s%.0s%.0s%.0s%.0s%" PRIi32 ")",
566         mTransaction->LoggingSerialNumber(), requestSerialNumber,
567         IDB_LOG_STRINGIFY(mTransaction->Database()),
568         IDB_LOG_STRINGIFY(*mTransaction),
569         IDB_LOG_STRINGIFY(GetSourceRef().ObjectStore()),
570         IDB_LOG_STRINGIFY(mSource), IDB_LOG_STRINGIFY(mDirection), aCount);
571   }
572 
573   GetTypedBackgroundActorRef().SendContinueInternal(AdvanceParams(aCount),
574                                                     mData);
575 
576   mContinueCalled = true;
577 }
578 
579 template <IDBCursor::Type CursorType>
Update(JSContext * const aCx,JS::Handle<JS::Value> aValue,ErrorResult & aRv)580 RefPtr<IDBRequest> IDBTypedCursor<CursorType>::Update(
581     JSContext* const aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv) {
582   AssertIsOnOwningThread();
583 
584   if (!mTransaction->IsActive()) {
585     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
586     return nullptr;
587   }
588 
589   if (!mTransaction->IsWriteAllowed()) {
590     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR);
591     return nullptr;
592   }
593 
594   if (mTransaction->GetMode() == IDBTransaction::Mode::Cleanup ||
595       IsSourceDeleted() || !mHaveValue || IsKeyOnlyCursor || mContinueCalled) {
596     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
597     return nullptr;
598   }
599 
600   if constexpr (!IsKeyOnlyCursor) {
601     MOZ_ASSERT(!mData.mKey.IsUnset());
602     if constexpr (!IsObjectStoreCursor) {
603       MOZ_ASSERT(!mData.mObjectStoreKey.IsUnset());
604     }
605 
606     mTransaction->InvalidateCursorCaches();
607 
608     IDBObjectStore::ValueWrapper valueWrapper(aCx, aValue);
609 
610     const Key& primaryKey = mData.GetObjectStoreKey();
611 
612     RefPtr<IDBRequest> request;
613 
614     IDBObjectStore& objectStore = GetSourceObjectStoreRef();
615     if (objectStore.HasValidKeyPath()) {
616       if (!valueWrapper.Clone(aCx)) {
617         aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
618         return nullptr;
619       }
620 
621       // Make sure the object given has the correct keyPath value set on it.
622       const KeyPath& keyPath = objectStore.GetKeyPath();
623       Key key;
624 
625       aRv = keyPath.ExtractKey(aCx, valueWrapper.Value(), key);
626       if (aRv.Failed()) {
627         return nullptr;
628       }
629 
630       if (key != primaryKey) {
631         aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
632         return nullptr;
633       }
634 
635       request = objectStore.AddOrPut(aCx, valueWrapper,
636                                      /* aKey */ JS::UndefinedHandleValue,
637                                      /* aOverwrite */ true,
638                                      /* aFromCursor */ true, aRv);
639       if (aRv.Failed()) {
640         return nullptr;
641       }
642     } else {
643       JS::Rooted<JS::Value> keyVal(aCx);
644       aRv = primaryKey.ToJSVal(aCx, &keyVal);
645       if (aRv.Failed()) {
646         return nullptr;
647       }
648 
649       request = objectStore.AddOrPut(aCx, valueWrapper, keyVal,
650                                      /* aOverwrite */ true,
651                                      /* aFromCursor */ true, aRv);
652       if (aRv.Failed()) {
653         return nullptr;
654       }
655     }
656 
657     request->SetSource(this);
658 
659     if (IsObjectStoreCursor) {
660       IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
661           "database(%s).transaction(%s).objectStore(%s)."
662           "cursor(%s).update(%s)",
663           "IDBCursor.update(%.0s%.0s%.0s%.0s%.0s)",
664           mTransaction->LoggingSerialNumber(), request->LoggingSerialNumber(),
665           IDB_LOG_STRINGIFY(mTransaction->Database()),
666           IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(&objectStore),
667           IDB_LOG_STRINGIFY(mDirection),
668           IDB_LOG_STRINGIFY(&objectStore, primaryKey));
669     } else {
670       IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
671           "database(%s).transaction(%s).objectStore(%s)."
672           "index(%s).cursor(%s).update(%s)",
673           "IDBCursor.update(%.0s%.0s%.0s%.0s%.0s%.0s)",
674           mTransaction->LoggingSerialNumber(), request->LoggingSerialNumber(),
675           IDB_LOG_STRINGIFY(mTransaction->Database()),
676           IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(&objectStore),
677           IDB_LOG_STRINGIFY(mSource), IDB_LOG_STRINGIFY(mDirection),
678           IDB_LOG_STRINGIFY(&objectStore, primaryKey));
679     }
680 
681     return request;
682   } else {
683     // XXX: Just to work around a bug in gcc, which otherwise claims 'control
684     // reaches end of non-void function', which is not true.
685     return nullptr;
686   }
687 }
688 
689 template <IDBCursor::Type CursorType>
Delete(JSContext * const aCx,ErrorResult & aRv)690 RefPtr<IDBRequest> IDBTypedCursor<CursorType>::Delete(JSContext* const aCx,
691                                                       ErrorResult& aRv) {
692   AssertIsOnOwningThread();
693 
694   if (!mTransaction->IsActive()) {
695     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
696     return nullptr;
697   }
698 
699   if (!mTransaction->IsWriteAllowed()) {
700     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR);
701     return nullptr;
702   }
703 
704   if (IsSourceDeleted() || !mHaveValue || IsKeyOnlyCursor || mContinueCalled) {
705     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
706     return nullptr;
707   }
708 
709   if constexpr (!IsKeyOnlyCursor) {
710     MOZ_ASSERT(!mData.mKey.IsUnset());
711 
712     mTransaction->InvalidateCursorCaches();
713 
714     const Key& primaryKey = mData.GetObjectStoreKey();
715 
716     JS::Rooted<JS::Value> key(aCx);
717     aRv = primaryKey.ToJSVal(aCx, &key);
718     if (NS_WARN_IF(aRv.Failed())) {
719       return nullptr;
720     }
721 
722     auto& objectStore = GetSourceObjectStoreRef();
723     RefPtr<IDBRequest> request =
724         objectStore.DeleteInternal(aCx, key, /* aFromCursor */ true, aRv);
725     if (aRv.Failed()) {
726       return nullptr;
727     }
728 
729     request->SetSource(this);
730 
731     if (IsObjectStoreCursor) {
732       IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
733           "database(%s).transaction(%s).objectStore(%s)."
734           "cursor(%s).delete(%s)",
735           "IDBCursor.delete(%.0s%.0s%.0s%.0s%.0s)",
736           mTransaction->LoggingSerialNumber(), request->LoggingSerialNumber(),
737           IDB_LOG_STRINGIFY(mTransaction->Database()),
738           IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(&objectStore),
739           IDB_LOG_STRINGIFY(mDirection),
740           IDB_LOG_STRINGIFY(&objectStore, primaryKey));
741     } else {
742       IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
743           "database(%s).transaction(%s).objectStore(%s)."
744           "index(%s).cursor(%s).delete(%s)",
745           "IDBCursor.delete(%.0s%.0s%.0s%.0s%.0s%.0s)",
746           mTransaction->LoggingSerialNumber(), request->LoggingSerialNumber(),
747           IDB_LOG_STRINGIFY(mTransaction->Database()),
748           IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(&objectStore),
749           IDB_LOG_STRINGIFY(mSource), IDB_LOG_STRINGIFY(mDirection),
750           IDB_LOG_STRINGIFY(&objectStore, primaryKey));
751     }
752 
753     return request;
754   } else {
755     // XXX: Just to work around a bug in gcc, which otherwise claims 'control
756     // reaches end of non-void function', which is not true.
757     return nullptr;
758   }
759 }
760 
761 template <IDBCursor::Type CursorType>
Reset(CursorData<CursorType> && aCursorData)762 void IDBTypedCursor<CursorType>::Reset(CursorData<CursorType>&& aCursorData) {
763   this->AssertIsOnOwningThread();
764 
765   Reset();
766 
767   mData = std::move(aCursorData);
768 
769   mHaveValue = !mData.mKey.IsUnset();
770 }
771 
772 template <IDBCursor::Type CursorType>
InvalidateCachedResponses()773 void IDBTypedCursor<CursorType>::InvalidateCachedResponses() {
774   AssertIsOnOwningThread();
775 
776   // TODO: Can mBackgroundActor actually be empty at this point?
777   if (mBackgroundActor) {
778     GetTypedBackgroundActorRef().InvalidateCachedResponses();
779   }
780 }
781 
782 NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBCursor)
783 NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBCursor)
784 
785 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBCursor)
786   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
787   NS_INTERFACE_MAP_ENTRY(nsISupports)
788 NS_INTERFACE_MAP_END
789 
790 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBCursor)
791 
792 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBCursor)
793   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRequest)
794 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
795 
796 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBCursor)
797   MOZ_ASSERT_IF(!tmp->mHaveCachedKey, tmp->mCachedKey.isUndefined());
798   MOZ_ASSERT_IF(!tmp->mHaveCachedPrimaryKey,
799                 tmp->mCachedPrimaryKey.isUndefined());
800   MOZ_ASSERT_IF(!tmp->mHaveCachedValue, tmp->mCachedValue.isUndefined());
801 
802   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCachedKey)803   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCachedKey)
804   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCachedPrimaryKey)
805   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCachedValue)
806 NS_IMPL_CYCLE_COLLECTION_TRACE_END
807 
808 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBCursor)
809 // Unlinking is done in the subclasses.
810 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
811 
812 // Don't unlink mRequest or mSource in
813 // NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED!
814 #define NS_IMPL_CYCLE_COLLECTION_IDBCURSOR_SUBCLASS_METHODS(_subclassName)    \
815   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(_subclassName, IDBCursor) \
816     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSource)                                \
817   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END                                       \
818                                                                               \
819   NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(_subclassName, IDBCursor)   \
820     NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER                         \
821     tmp->DropJSObjects();                                                     \
822   NS_IMPL_CYCLE_COLLECTION_UNLINK_END                                         \
823                                                                               \
824   NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(_subclassName)                      \
825   NS_INTERFACE_MAP_END_INHERITING(IDBCursor)                                  \
826                                                                               \
827   NS_IMPL_ADDREF_INHERITED(_subclassName, IDBCursor)                          \
828   NS_IMPL_RELEASE_INHERITED(_subclassName, IDBCursor)
829 
830 #define NS_IMPL_CYCLE_COLLECTION_IDBCURSOR_SUBCLASS(_subclassName)  \
831   NS_IMPL_CYCLE_COLLECTION_MULTI_ZONE_JSHOLDER_CLASS(_subclassName) \
832   NS_IMPL_CYCLE_COLLECTION_IDBCURSOR_SUBCLASS_METHODS(_subclassName)
833 
834 NS_IMPL_CYCLE_COLLECTION_IDBCURSOR_SUBCLASS(IDBObjectStoreCursor)
835 NS_IMPL_CYCLE_COLLECTION_IDBCURSOR_SUBCLASS(IDBObjectStoreKeyCursor)
836 NS_IMPL_CYCLE_COLLECTION_IDBCURSOR_SUBCLASS(IDBIndexCursor)
837 NS_IMPL_CYCLE_COLLECTION_IDBCURSOR_SUBCLASS(IDBIndexKeyCursor)
838 
839 template <IDBCursor::Type CursorType>
840 JSObject* IDBTypedCursor<CursorType>::WrapObject(
841     JSContext* const aCx, JS::Handle<JSObject*> aGivenProto) {
842   AssertIsOnOwningThread();
843 
844   return IsKeyOnlyCursor
845              ? IDBCursor_Binding::Wrap(aCx, this, aGivenProto)
846              : IDBCursorWithValue_Binding::Wrap(aCx, this, aGivenProto);
847 }
848 
849 template <IDBCursor::Type CursorType>
850 template <typename... DataArgs>
IDBTypedCursor(indexedDB::BackgroundCursorChild<CursorType> * const aBackgroundActor,DataArgs &&...aDataArgs)851 IDBTypedCursor<CursorType>::IDBTypedCursor(
852     indexedDB::BackgroundCursorChild<CursorType>* const aBackgroundActor,
853     DataArgs&&... aDataArgs)
854     : IDBCursor{aBackgroundActor},
855       mData{std::forward<DataArgs>(aDataArgs)...},
856       mSource(aBackgroundActor->GetSource()) {}
857 
858 template <IDBCursor::Type CursorType>
IsLocaleAware() const859 bool IDBTypedCursor<CursorType>::IsLocaleAware() const {
860   if constexpr (IsObjectStoreCursor) {
861     return false;
862   } else {
863     return !GetSourceRef().Locale().IsEmpty();
864   }
865 }
866 
867 template class IDBTypedCursor<IDBCursorType::ObjectStore>;
868 template class IDBTypedCursor<IDBCursorType::ObjectStoreKey>;
869 template class IDBTypedCursor<IDBCursorType::Index>;
870 template class IDBTypedCursor<IDBCursorType::IndexKey>;
871 
872 }  // namespace mozilla::dom
873