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