1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "IDBTransaction.h"
9 #include "BackgroundChildImpl.h"
10 #include "IDBDatabase.h"
11 #include "IDBEvents.h"
12 #include "IDBObjectStore.h"
13 #include "IDBRequest.h"
14 #include "mozilla/ErrorResult.h"
15 #include "mozilla/EventDispatcher.h"
16 #include "mozilla/HoldDropJSObjects.h"
17 #include "mozilla/dom/DOMException.h"
18 #include "mozilla/dom/DOMStringList.h"
19 #include "mozilla/dom/WorkerRef.h"
20 #include "mozilla/dom/WorkerPrivate.h"
21 #include "mozilla/ipc/BackgroundChild.h"
22 #include "mozilla/ScopeExit.h"
23 #include "nsPIDOMWindow.h"
24 #include "nsQueryObject.h"
25 #include "nsServiceManagerUtils.h"
26 #include "nsTHashtable.h"
27 #include "ProfilerHelpers.h"
28 #include "ReportInternalError.h"
29 #include "ThreadLocal.h"
31 // Include this last to avoid path problems on Windows.
32 #include "ActorsChild.h"
35 using namespace mozilla::dom::indexedDB
;
36 using namespace mozilla::ipc
;
38 // TODO: Move this to xpcom/ds.
39 template <typename T
, typename Range
, typename Transformation
>
40 nsTHashtable
<T
> TransformToHashtable(const Range
& aRange
,
41 const Transformation
& aTransformation
) {
42 // TODO: Determining the size of the range is not syntactically necessary (and
43 // requires random access iterators if expressed this way). It is a
44 // performance optimization. We could resort to std::distance to support any
45 // iterator category, but this would lead to a double iteration of the range
46 // in case of non-random-access iterators. It is hard to determine in general
47 // if double iteration or reallocation is worse.
48 auto res
= nsTHashtable
<T
>(aRange
.cend() - aRange
.cbegin());
49 // TOOD: std::transform could be used if nsTHashtable had an insert_iterator,
50 // and this would also allow a more generic version not depending on
51 // nsTHashtable at all.
52 for (const auto& item
: aRange
) {
53 res
.PutEntry(aTransformation(item
));
58 ThreadLocal
* GetIndexedDBThreadLocal() {
59 BackgroundChildImpl::ThreadLocal
* const threadLocal
=
60 BackgroundChildImpl::GetThreadLocalForCurrentThread();
61 MOZ_ASSERT(threadLocal
);
63 ThreadLocal
* idbThreadLocal
= threadLocal
->mIndexedDBThreadLocal
.get();
64 MOZ_ASSERT(idbThreadLocal
);
66 return idbThreadLocal
;
70 namespace mozilla::dom
{
72 using namespace mozilla::dom::indexedDB
;
73 using namespace mozilla::ipc
;
75 bool IDBTransaction::HasTransactionChild() const {
76 return (mMode
== Mode::VersionChange
78 mBackgroundActor
.mVersionChangeBackgroundActor
)
79 : mBackgroundActor
.mNormalBackgroundActor
) != nullptr;
82 template <typename Func
>
83 auto IDBTransaction::DoWithTransactionChild(const Func
& aFunc
) const {
84 MOZ_ASSERT(HasTransactionChild());
85 return mMode
== Mode::VersionChange
86 ? aFunc(*mBackgroundActor
.mVersionChangeBackgroundActor
)
87 : aFunc(*mBackgroundActor
.mNormalBackgroundActor
);
90 IDBTransaction::IDBTransaction(IDBDatabase
* const aDatabase
,
91 const nsTArray
<nsString
>& aObjectStoreNames
,
92 const Mode aMode
, nsString aFilename
,
93 const uint32_t aLineNo
, const uint32_t aColumn
,
94 CreatedFromFactoryFunction
/*aDummy*/)
95 : DOMEventTargetHelper(aDatabase
),
97 mObjectStoreNames(aObjectStoreNames
.Clone()),
98 mLoggingSerialNumber(GetIndexedDBThreadLocal()->NextTransactionSN(aMode
)),
99 mNextObjectStoreId(0),
102 mPendingRequestCount(0),
103 mFilename(std::move(aFilename
)),
108 mNotedActiveTransaction(false) {
109 MOZ_ASSERT(aDatabase
);
110 aDatabase
->AssertIsOnOwningThread();
112 // This also nulls mBackgroundActor.mVersionChangeBackgroundActor, so this is
113 // valid also for mMode == Mode::VersionChange.
114 mBackgroundActor
.mNormalBackgroundActor
= nullptr;
117 if (!aObjectStoreNames
.IsEmpty()) {
118 // Make sure the array is properly sorted.
120 std::is_sorted(aObjectStoreNames
.cbegin(), aObjectStoreNames
.cend()));
122 // Make sure there are no duplicates in our objectStore names.
123 MOZ_ASSERT(aObjectStoreNames
.cend() ==
124 std::adjacent_find(aObjectStoreNames
.cbegin(),
125 aObjectStoreNames
.cend()));
129 mozilla::HoldJSObjects(this);
132 IDBTransaction::~IDBTransaction() {
133 AssertIsOnOwningThread();
134 MOZ_ASSERT(!mPendingRequestCount
);
135 MOZ_ASSERT(mReadyState
== ReadyState::Finished
);
136 MOZ_ASSERT(!mNotedActiveTransaction
);
137 MOZ_ASSERT(mSentCommitOrAbort
);
138 MOZ_ASSERT_IF(HasTransactionChild(), mFiredCompleteOrAbort
);
141 mDatabase
->UnregisterTransaction(*this);
147 if (HasTransactionChild()) {
148 if (mMode
== Mode::VersionChange
) {
149 mBackgroundActor
.mVersionChangeBackgroundActor
->SendDeleteMeInternal(
150 /* aFailedConstructor */ false);
152 mBackgroundActor
.mNormalBackgroundActor
->SendDeleteMeInternal();
155 MOZ_ASSERT(!HasTransactionChild(),
156 "SendDeleteMeInternal should have cleared!");
158 mozilla::DropJSObjects(this);
162 SafeRefPtr
<IDBTransaction
> IDBTransaction::CreateVersionChange(
163 IDBDatabase
* const aDatabase
,
164 BackgroundVersionChangeTransactionChild
* const aActor
,
165 const NotNull
<IDBOpenDBRequest
*> aOpenRequest
,
166 const int64_t aNextObjectStoreId
, const int64_t aNextIndexId
) {
167 MOZ_ASSERT(aDatabase
);
168 aDatabase
->AssertIsOnOwningThread();
170 MOZ_ASSERT(aNextObjectStoreId
> 0);
171 MOZ_ASSERT(aNextIndexId
> 0);
173 const nsTArray
<nsString
> emptyObjectStoreNames
;
176 uint32_t lineNo
, column
;
177 aOpenRequest
->GetCallerLocation(filename
, &lineNo
, &column
);
178 auto transaction
= MakeSafeRefPtr
<IDBTransaction
>(
179 aDatabase
, emptyObjectStoreNames
, Mode::VersionChange
,
180 std::move(filename
), lineNo
, column
, CreatedFromFactoryFunction
{});
182 transaction
->NoteActiveTransaction();
184 transaction
->mBackgroundActor
.mVersionChangeBackgroundActor
= aActor
;
185 transaction
->mNextObjectStoreId
= aNextObjectStoreId
;
186 transaction
->mNextIndexId
= aNextIndexId
;
188 aDatabase
->RegisterTransaction(*transaction
);
189 transaction
->mRegistered
= true;
195 SafeRefPtr
<IDBTransaction
> IDBTransaction::Create(
196 JSContext
* const aCx
, IDBDatabase
* const aDatabase
,
197 const nsTArray
<nsString
>& aObjectStoreNames
, const Mode aMode
) {
198 MOZ_ASSERT(aDatabase
);
199 aDatabase
->AssertIsOnOwningThread();
200 MOZ_ASSERT(!aObjectStoreNames
.IsEmpty());
201 MOZ_ASSERT(aMode
== Mode::ReadOnly
|| aMode
== Mode::ReadWrite
||
202 aMode
== Mode::ReadWriteFlush
|| aMode
== Mode::Cleanup
);
205 uint32_t lineNo
, column
;
206 IDBRequest::CaptureCaller(aCx
, filename
, &lineNo
, &column
);
207 auto transaction
= MakeSafeRefPtr
<IDBTransaction
>(
208 aDatabase
, aObjectStoreNames
, aMode
, std::move(filename
), lineNo
, column
,
209 CreatedFromFactoryFunction
{});
211 if (!NS_IsMainThread()) {
212 WorkerPrivate
* const workerPrivate
= GetCurrentThreadWorkerPrivate();
213 MOZ_ASSERT(workerPrivate
);
215 workerPrivate
->AssertIsOnWorkerThread();
217 RefPtr
<StrongWorkerRef
> workerRef
= StrongWorkerRef::Create(
218 workerPrivate
, "IDBTransaction",
219 [transaction
= AsRefPtr(transaction
.clonePtr())]() {
220 transaction
->AssertIsOnOwningThread();
221 if (!transaction
->IsCommittingOrFinished()) {
222 IDB_REPORT_INTERNAL_ERR();
223 transaction
->AbortInternal(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
,
227 if (NS_WARN_IF(!workerRef
)) {
229 // Silence the destructor assertions if we never made this object live.
230 transaction
->mReadyState
= ReadyState::Finished
;
231 transaction
->mSentCommitOrAbort
.Flip();
236 transaction
->mWorkerRef
= std::move(workerRef
);
239 nsCOMPtr
<nsIRunnable
> runnable
=
240 do_QueryObject(transaction
.unsafeGetRawPtr());
241 nsContentUtils::AddPendingIDBTransaction(runnable
.forget());
243 aDatabase
->RegisterTransaction(*transaction
);
244 transaction
->mRegistered
= true;
250 Maybe
<IDBTransaction
&> IDBTransaction::MaybeCurrent() {
251 using namespace mozilla::ipc
;
253 MOZ_ASSERT(BackgroundChild::GetForCurrentThread());
255 return GetIndexedDBThreadLocal()->MaybeCurrentTransactionRef();
260 void IDBTransaction::AssertIsOnOwningThread() const {
261 MOZ_ASSERT(mDatabase
);
262 mDatabase
->AssertIsOnOwningThread();
267 void IDBTransaction::SetBackgroundActor(
268 indexedDB::BackgroundTransactionChild
* const aBackgroundActor
) {
269 AssertIsOnOwningThread();
270 MOZ_ASSERT(aBackgroundActor
);
271 MOZ_ASSERT(!mBackgroundActor
.mNormalBackgroundActor
);
272 MOZ_ASSERT(mMode
!= Mode::VersionChange
);
274 NoteActiveTransaction();
276 mBackgroundActor
.mNormalBackgroundActor
= aBackgroundActor
;
279 BackgroundRequestChild
* IDBTransaction::StartRequest(
280 MovingNotNull
<RefPtr
<mozilla::dom::IDBRequest
> > aRequest
,
281 const RequestParams
& aParams
) {
282 AssertIsOnOwningThread();
283 MOZ_ASSERT(aParams
.type() != RequestParams::T__None
);
285 BackgroundRequestChild
* const actor
=
286 new BackgroundRequestChild(std::move(aRequest
));
288 DoWithTransactionChild([actor
, &aParams
](auto& transactionChild
) {
289 transactionChild
.SendPBackgroundIDBRequestConstructor(actor
, aParams
);
292 // Balanced in BackgroundRequestChild::Recv__delete__().
298 void IDBTransaction::OpenCursor(PBackgroundIDBCursorChild
& aBackgroundActor
,
299 const OpenCursorParams
& aParams
) {
300 AssertIsOnOwningThread();
301 MOZ_ASSERT(aParams
.type() != OpenCursorParams::T__None
);
303 DoWithTransactionChild([&aBackgroundActor
, &aParams
](auto& actor
) {
304 actor
.SendPBackgroundIDBCursorConstructor(&aBackgroundActor
, aParams
);
307 // Balanced in BackgroundCursorChild::RecvResponse().
311 void IDBTransaction::RefreshSpec(const bool aMayDelete
) {
312 AssertIsOnOwningThread();
314 for (auto& objectStore
: mObjectStores
) {
315 objectStore
->RefreshSpec(aMayDelete
);
318 for (auto& objectStore
: mDeletedObjectStores
) {
319 objectStore
->RefreshSpec(false);
323 void IDBTransaction::OnNewRequest() {
324 AssertIsOnOwningThread();
326 if (!mPendingRequestCount
) {
327 MOZ_ASSERT(ReadyState::Active
== mReadyState
);
331 ++mPendingRequestCount
;
334 void IDBTransaction::OnRequestFinished(
335 const bool aRequestCompletedSuccessfully
) {
336 AssertIsOnOwningThread();
337 MOZ_ASSERT(mReadyState
!= ReadyState::Active
);
338 MOZ_ASSERT_IF(mReadyState
== ReadyState::Finished
, !NS_SUCCEEDED(mAbortCode
));
339 MOZ_ASSERT(mPendingRequestCount
);
341 --mPendingRequestCount
;
343 if (!mPendingRequestCount
) {
344 if (mSentCommitOrAbort
) {
348 if (aRequestCompletedSuccessfully
) {
349 if (mReadyState
== ReadyState::Inactive
) {
350 mReadyState
= ReadyState::Committing
;
353 if (NS_SUCCEEDED(mAbortCode
)) {
356 SendAbort(mAbortCode
);
359 // Don't try to send any more messages to the parent if the request actor
360 // was killed. Set our state accordingly to Finished.
361 mReadyState
= ReadyState::Finished
;
362 mSentCommitOrAbort
.Flip();
363 IDB_LOG_MARK_CHILD_TRANSACTION(
364 "Request actor was killed, transaction will be aborted",
365 "IDBTransaction abort", LoggingSerialNumber());
370 void IDBTransaction::SendCommit(const bool aAutoCommit
) {
371 AssertIsOnOwningThread();
372 MOZ_ASSERT(NS_SUCCEEDED(mAbortCode
));
373 MOZ_ASSERT(IsCommittingOrFinished());
375 // Don't do this in the macro because we always need to increment the serial
376 // number to keep in sync with the parent.
377 const uint64_t requestSerialNumber
= IDBRequest::NextSerialNumber();
379 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
380 "Committing transaction (%s)", "IDBTransaction commit (%s)",
381 LoggingSerialNumber(), requestSerialNumber
,
382 aAutoCommit
? "automatically" : "explicitly");
384 const auto lastRequestSerialNumber
=
386 requestSerialNumber
]() -> Maybe
<decltype(requestSerialNumber
)> {
391 // In case of an explicit commit, we need to note the serial number of the
392 // last request to check if a request submitted before the commit request
393 // failed. If we are currently in an event handler for a request on this
394 // transaction, ignore this request. This is used to synchronize the
395 // transaction's committing state with the parent side, to abort the
396 // transaction in case of a request resulting in an error (see
397 // https://w3c.github.io/IndexedDB/#async-execute-request, step 5.3.). With
398 // automatic commit, this is not necessary, as the transaction's state will
399 // only be set to committing after the last request completed.
400 const auto maybeCurrentTransaction
=
401 BackgroundChildImpl::GetThreadLocalForCurrentThread()
402 ->mIndexedDBThreadLocal
->MaybeCurrentTransactionRef();
403 const bool dispatchingEventForThisTransaction
=
404 maybeCurrentTransaction
&& &maybeCurrentTransaction
.ref() == this;
406 return Some(requestSerialNumber
407 ? (requestSerialNumber
-
408 (dispatchingEventForThisTransaction
? 0 : 1))
412 DoWithTransactionChild([lastRequestSerialNumber
](auto& actor
) {
413 actor
.SendCommit(lastRequestSerialNumber
);
416 mSentCommitOrAbort
.Flip();
419 void IDBTransaction::SendAbort(const nsresult aResultCode
) {
420 AssertIsOnOwningThread();
421 MOZ_ASSERT(NS_FAILED(aResultCode
));
422 MOZ_ASSERT(IsCommittingOrFinished());
424 // Don't do this in the macro because we always need to increment the serial
425 // number to keep in sync with the parent.
426 const uint64_t requestSerialNumber
= IDBRequest::NextSerialNumber();
428 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
429 "Aborting transaction with result 0x%" PRIx32
,
430 "IDBTransaction abort (0x%" PRIx32
")", LoggingSerialNumber(),
431 requestSerialNumber
, static_cast<uint32_t>(aResultCode
));
433 DoWithTransactionChild(
434 [aResultCode
](auto& actor
) { actor
.SendAbort(aResultCode
); });
436 mSentCommitOrAbort
.Flip();
439 void IDBTransaction::NoteActiveTransaction() {
440 AssertIsOnOwningThread();
441 MOZ_ASSERT(!mNotedActiveTransaction
);
443 mDatabase
->NoteActiveTransaction();
444 mNotedActiveTransaction
= true;
447 void IDBTransaction::MaybeNoteInactiveTransaction() {
448 AssertIsOnOwningThread();
450 if (mNotedActiveTransaction
) {
451 mDatabase
->NoteInactiveTransaction();
452 mNotedActiveTransaction
= false;
456 void IDBTransaction::GetCallerLocation(nsAString
& aFilename
,
457 uint32_t* const aLineNo
,
458 uint32_t* const aColumn
) const {
459 AssertIsOnOwningThread();
463 aFilename
= mFilename
;
468 RefPtr
<IDBObjectStore
> IDBTransaction::CreateObjectStore(
469 ObjectStoreSpec
& aSpec
) {
470 AssertIsOnOwningThread();
471 MOZ_ASSERT(aSpec
.metadata().id());
472 MOZ_ASSERT(Mode::VersionChange
== mMode
);
473 MOZ_ASSERT(mBackgroundActor
.mVersionChangeBackgroundActor
);
474 MOZ_ASSERT(IsActive());
478 // TODO: Bind name outside of lambda capture as a workaround for GCC 7 bug
479 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66735.
480 const auto& name
= aSpec
.metadata().name();
481 // TODO: Use #ifdef and local variable as a workaround for Bug 1583449.
482 const bool objectStoreNameDoesNotYetExist
=
483 std::all_of(mObjectStores
.cbegin(), mObjectStores
.cend(),
484 [&name
](const auto& objectStore
) {
485 return objectStore
->Name() != name
;
487 MOZ_ASSERT(objectStoreNameDoesNotYetExist
);
492 mBackgroundActor
.mVersionChangeBackgroundActor
->SendCreateObjectStore(
495 RefPtr
<IDBObjectStore
> objectStore
= IDBObjectStore::Create(
496 SafeRefPtr
{this, AcquireStrongRefFromRawPtr
{}}, aSpec
);
497 MOZ_ASSERT(objectStore
);
499 mObjectStores
.AppendElement(objectStore
);
504 void IDBTransaction::DeleteObjectStore(const int64_t aObjectStoreId
) {
505 AssertIsOnOwningThread();
506 MOZ_ASSERT(aObjectStoreId
);
507 MOZ_ASSERT(Mode::VersionChange
== mMode
);
508 MOZ_ASSERT(mBackgroundActor
.mVersionChangeBackgroundActor
);
509 MOZ_ASSERT(IsActive());
512 mBackgroundActor
.mVersionChangeBackgroundActor
->SendDeleteObjectStore(
516 std::find_if(mObjectStores
.begin(), mObjectStores
.end(),
517 [aObjectStoreId
](const auto& objectStore
) {
518 return objectStore
->Id() == aObjectStoreId
;
520 if (foundIt
!= mObjectStores
.end()) {
521 auto& objectStore
= *foundIt
;
522 objectStore
->NoteDeletion();
524 RefPtr
<IDBObjectStore
>* deletedObjectStore
=
525 mDeletedObjectStores
.AppendElement();
526 deletedObjectStore
->swap(objectStore
);
528 mObjectStores
.RemoveElementAt(foundIt
);
532 void IDBTransaction::RenameObjectStore(const int64_t aObjectStoreId
,
533 const nsAString
& aName
) {
534 AssertIsOnOwningThread();
535 MOZ_ASSERT(aObjectStoreId
);
536 MOZ_ASSERT(Mode::VersionChange
== mMode
);
537 MOZ_ASSERT(mBackgroundActor
.mVersionChangeBackgroundActor
);
538 MOZ_ASSERT(IsActive());
541 mBackgroundActor
.mVersionChangeBackgroundActor
->SendRenameObjectStore(
542 aObjectStoreId
, nsString(aName
)));
545 void IDBTransaction::CreateIndex(IDBObjectStore
* const aObjectStore
,
546 const indexedDB::IndexMetadata
& aMetadata
) {
547 AssertIsOnOwningThread();
548 MOZ_ASSERT(aObjectStore
);
549 MOZ_ASSERT(aMetadata
.id());
550 MOZ_ASSERT(Mode::VersionChange
== mMode
);
551 MOZ_ASSERT(mBackgroundActor
.mVersionChangeBackgroundActor
);
552 MOZ_ASSERT(IsActive());
555 mBackgroundActor
.mVersionChangeBackgroundActor
->SendCreateIndex(
556 aObjectStore
->Id(), aMetadata
));
559 void IDBTransaction::DeleteIndex(IDBObjectStore
* const aObjectStore
,
560 const int64_t aIndexId
) {
561 AssertIsOnOwningThread();
562 MOZ_ASSERT(aObjectStore
);
563 MOZ_ASSERT(aIndexId
);
564 MOZ_ASSERT(Mode::VersionChange
== mMode
);
565 MOZ_ASSERT(mBackgroundActor
.mVersionChangeBackgroundActor
);
566 MOZ_ASSERT(IsActive());
569 mBackgroundActor
.mVersionChangeBackgroundActor
->SendDeleteIndex(
570 aObjectStore
->Id(), aIndexId
));
573 void IDBTransaction::RenameIndex(IDBObjectStore
* const aObjectStore
,
574 const int64_t aIndexId
,
575 const nsAString
& aName
) {
576 AssertIsOnOwningThread();
577 MOZ_ASSERT(aObjectStore
);
578 MOZ_ASSERT(aIndexId
);
579 MOZ_ASSERT(Mode::VersionChange
== mMode
);
580 MOZ_ASSERT(mBackgroundActor
.mVersionChangeBackgroundActor
);
581 MOZ_ASSERT(IsActive());
584 mBackgroundActor
.mVersionChangeBackgroundActor
->SendRenameIndex(
585 aObjectStore
->Id(), aIndexId
, nsString(aName
)));
588 void IDBTransaction::AbortInternal(const nsresult aAbortCode
,
589 RefPtr
<DOMException
> aError
) {
590 AssertIsOnOwningThread();
591 MOZ_ASSERT(NS_FAILED(aAbortCode
));
592 MOZ_ASSERT(!IsCommittingOrFinished());
594 const bool isVersionChange
= mMode
== Mode::VersionChange
;
595 const bool needToSendAbort
= mReadyState
== ReadyState::Inactive
&& !mStarted
;
597 mAbortCode
= aAbortCode
;
598 mReadyState
= ReadyState::Finished
;
599 mError
= std::move(aError
);
601 if (isVersionChange
) {
602 // If a version change transaction is aborted, we must revert the world
603 // back to its previous state unless we're being invalidated after the
604 // transaction already completed.
605 if (!mDatabase
->IsInvalidated()) {
606 mDatabase
->RevertToPreviousState();
609 // We do the reversion only for the mObjectStores/mDeletedObjectStores but
610 // not for the mIndexes/mDeletedIndexes of each IDBObjectStore because it's
611 // time-consuming(O(m*n)) and mIndexes/mDeletedIndexes won't be used anymore
612 // in IDBObjectStore::(Create|Delete)Index() and IDBObjectStore::Index() in
613 // which all the executions are returned earlier by
614 // !transaction->IsActive().
616 const nsTArray
<ObjectStoreSpec
>& specArray
=
617 mDatabase
->Spec()->objectStores();
619 if (specArray
.IsEmpty()) {
620 // This case is specially handled as a performance optimization, it is
621 // equivalent to the else block.
622 mObjectStores
.Clear();
624 const auto validIds
= TransformToHashtable
<nsUint64HashKey
>(
625 specArray
, [](const auto& spec
) {
626 const int64_t objectStoreId
= spec
.metadata().id();
627 MOZ_ASSERT(objectStoreId
);
628 return static_cast<uint64_t>(objectStoreId
);
631 mObjectStores
.RemoveLastElements(
632 mObjectStores
.end() -
633 std::remove_if(mObjectStores
.begin(), mObjectStores
.end(),
634 [&validIds
](const auto& objectStore
) {
635 return !validIds
.Contains(
636 uint64_t(objectStore
->Id()));
639 std::copy_if(std::make_move_iterator(mDeletedObjectStores
.begin()),
640 std::make_move_iterator(mDeletedObjectStores
.end()),
641 MakeBackInserter(mObjectStores
),
642 [&validIds
](const auto& deletedObjectStore
) {
643 const int64_t objectStoreId
= deletedObjectStore
->Id();
644 MOZ_ASSERT(objectStoreId
);
645 return validIds
.Contains(uint64_t(objectStoreId
));
648 mDeletedObjectStores
.Clear();
651 // Fire the abort event if there are no outstanding requests. Otherwise the
652 // abort event will be fired when all outstanding requests finish.
653 if (needToSendAbort
) {
654 SendAbort(aAbortCode
);
657 if (isVersionChange
) {
662 void IDBTransaction::Abort(IDBRequest
* const aRequest
) {
663 AssertIsOnOwningThread();
664 MOZ_ASSERT(aRequest
);
666 if (IsCommittingOrFinished()) {
667 // Already started (and maybe finished) the commit or abort so there is
668 // nothing to do here.
673 RefPtr
<DOMException
> error
= aRequest
->GetError(rv
);
675 // TODO: Do we deliberately ignore rv here? Isn't there a static analysis that
678 AbortInternal(aRequest
->GetErrorCode(), std::move(error
));
681 void IDBTransaction::Abort(const nsresult aErrorCode
) {
682 AssertIsOnOwningThread();
684 if (IsCommittingOrFinished()) {
685 // Already started (and maybe finished) the commit or abort so there is
686 // nothing to do here.
690 AbortInternal(aErrorCode
, DOMException::Create(aErrorCode
));
693 // Specified by https://w3c.github.io/IndexedDB/#dom-idbtransaction-abort.
694 void IDBTransaction::Abort(ErrorResult
& aRv
) {
695 AssertIsOnOwningThread();
697 if (IsCommittingOrFinished()) {
698 aRv
= NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR
;
702 mReadyState
= ReadyState::Inactive
;
704 AbortInternal(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR
, nullptr);
706 mAbortedByScript
.Flip();
709 // Specified by https://w3c.github.io/IndexedDB/#dom-idbtransaction-commit.
710 void IDBTransaction::Commit(ErrorResult
& aRv
) {
711 AssertIsOnOwningThread();
713 if (mReadyState
!= ReadyState::Active
|| !mNotedActiveTransaction
) {
714 aRv
= NS_ERROR_DOM_INVALID_STATE_ERR
;
718 MOZ_ASSERT(!mSentCommitOrAbort
);
720 MOZ_ASSERT(mReadyState
== ReadyState::Active
);
721 mReadyState
= ReadyState::Committing
;
722 if (NS_WARN_IF(NS_FAILED(mAbortCode
))) {
723 SendAbort(mAbortCode
);
729 mWasExplicitlyCommitted
.Flip();
735 void IDBTransaction::FireCompleteOrAbortEvents(const nsresult aResult
) {
736 AssertIsOnOwningThread();
737 MOZ_ASSERT(!mFiredCompleteOrAbort
);
739 mReadyState
= ReadyState::Finished
;
742 mFiredCompleteOrAbort
.Flip();
745 // Make sure we drop the WorkerRef when this function completes.
746 const auto scopeExit
= MakeScopeExit([&] { mWorkerRef
= nullptr; });
749 if (NS_SUCCEEDED(aResult
)) {
750 event
= CreateGenericEvent(this, nsDependentString(kCompleteEventType
),
751 eDoesNotBubble
, eNotCancelable
);
754 // If we hit this assertion, it probably means transaction object on the
755 // parent process doesn't propagate error properly.
756 MOZ_ASSERT(NS_SUCCEEDED(mAbortCode
));
758 if (aResult
== NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR
) {
759 mDatabase
->SetQuotaExceeded();
762 if (!mError
&& !mAbortedByScript
) {
763 mError
= DOMException::Create(aResult
);
766 event
= CreateGenericEvent(this, nsDependentString(kAbortEventType
),
767 eDoesBubble
, eNotCancelable
);
770 if (NS_SUCCEEDED(mAbortCode
)) {
771 mAbortCode
= aResult
;
775 if (NS_SUCCEEDED(mAbortCode
)) {
776 IDB_LOG_MARK_CHILD_TRANSACTION("Firing 'complete' event",
777 "IDBTransaction 'complete' event",
778 mLoggingSerialNumber
);
780 IDB_LOG_MARK_CHILD_TRANSACTION(
781 "Firing 'abort' event with error 0x%" PRIx32
,
782 "IDBTransaction 'abort' event (0x%" PRIx32
")", mLoggingSerialNumber
,
783 static_cast<uint32_t>(mAbortCode
));
786 IgnoredErrorResult rv
;
787 DispatchEvent(*event
, rv
);
789 NS_WARNING("DispatchEvent failed!");
792 // Normally, we note inactive transaction here instead of
793 // IDBTransaction::ClearBackgroundActor() because here is the earliest place
794 // to know that it becomes non-blocking to allow the scheduler to start the
795 // preemption as soon as it can.
796 // Note: If the IDBTransaction object is held by the script,
797 // ClearBackgroundActor() will be done in ~IDBTransaction() until garbage
798 // collected after its window is closed which prevents us to preempt its
799 // window immediately after committed.
800 MaybeNoteInactiveTransaction();
803 int64_t IDBTransaction::NextObjectStoreId() {
804 AssertIsOnOwningThread();
805 MOZ_ASSERT(Mode::VersionChange
== mMode
);
807 return mNextObjectStoreId
++;
810 int64_t IDBTransaction::NextIndexId() {
811 AssertIsOnOwningThread();
812 MOZ_ASSERT(Mode::VersionChange
== mMode
);
814 return mNextIndexId
++;
817 void IDBTransaction::InvalidateCursorCaches() {
818 AssertIsOnOwningThread();
820 for (const auto& cursor
: mCursors
) {
821 cursor
->InvalidateCachedResponses();
825 void IDBTransaction::RegisterCursor(IDBCursor
& aCursor
) {
826 AssertIsOnOwningThread();
828 mCursors
.AppendElement(WrapNotNullUnchecked(&aCursor
));
831 void IDBTransaction::UnregisterCursor(IDBCursor
& aCursor
) {
832 AssertIsOnOwningThread();
834 DebugOnly
<bool> removed
= mCursors
.RemoveElement(&aCursor
);
838 nsIGlobalObject
* IDBTransaction::GetParentObject() const {
839 AssertIsOnOwningThread();
841 return mDatabase
->GetParentObject();
844 IDBTransactionMode
IDBTransaction::GetMode(ErrorResult
& aRv
) const {
845 AssertIsOnOwningThread();
849 return IDBTransactionMode::Readonly
;
851 case Mode::ReadWrite
:
852 return IDBTransactionMode::Readwrite
;
854 case Mode::ReadWriteFlush
:
855 return IDBTransactionMode::Readwriteflush
;
858 return IDBTransactionMode::Cleanup
;
860 case Mode::VersionChange
:
861 return IDBTransactionMode::Versionchange
;
865 MOZ_CRASH("Bad mode!");
869 DOMException
* IDBTransaction::GetError() const {
870 AssertIsOnOwningThread();
875 RefPtr
<DOMStringList
> IDBTransaction::ObjectStoreNames() const {
876 AssertIsOnOwningThread();
878 if (mMode
== Mode::VersionChange
) {
879 return mDatabase
->ObjectStoreNames();
882 auto list
= MakeRefPtr
<DOMStringList
>();
883 list
->StringArray() = mObjectStoreNames
.Clone();
887 RefPtr
<IDBObjectStore
> IDBTransaction::ObjectStore(const nsAString
& aName
,
889 AssertIsOnOwningThread();
891 if (IsCommittingOrFinished()) {
892 aRv
.ThrowInvalidStateError("Transaction is already committing or done.");
896 auto* const spec
= [this, &aName
]() -> ObjectStoreSpec
* {
897 if (IDBTransaction::Mode::VersionChange
== mMode
||
898 mObjectStoreNames
.Contains(aName
)) {
899 return mDatabase
->LookupModifiableObjectStoreSpec(
900 [&aName
](const auto& objectStore
) {
901 return objectStore
.metadata().name() == aName
;
908 aRv
.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR
);
912 RefPtr
<IDBObjectStore
> objectStore
;
914 const auto foundIt
= std::find_if(
915 mObjectStores
.cbegin(), mObjectStores
.cend(),
916 [desiredId
= spec
->metadata().id()](const auto& existingObjectStore
) {
917 return existingObjectStore
->Id() == desiredId
;
919 if (foundIt
!= mObjectStores
.cend()) {
920 objectStore
= *foundIt
;
922 objectStore
= IDBObjectStore::Create(
923 SafeRefPtr
{this, AcquireStrongRefFromRawPtr
{}}, *spec
);
924 MOZ_ASSERT(objectStore
);
926 mObjectStores
.AppendElement(objectStore
);
932 NS_IMPL_ADDREF_INHERITED(IDBTransaction
, DOMEventTargetHelper
)
933 NS_IMPL_RELEASE_INHERITED(IDBTransaction
, DOMEventTargetHelper
)
935 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBTransaction
)
936 NS_INTERFACE_MAP_ENTRY(nsIRunnable
)
937 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper
)
939 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBTransaction
)
941 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBTransaction
,
942 DOMEventTargetHelper
)
943 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDatabase
)
944 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError
)
945 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mObjectStores
)
946 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDeletedObjectStores
)
947 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
949 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBTransaction
,
950 DOMEventTargetHelper
)
951 // Don't unlink mDatabase!
952 NS_IMPL_CYCLE_COLLECTION_UNLINK(mError
)
953 NS_IMPL_CYCLE_COLLECTION_UNLINK(mObjectStores
)
954 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDeletedObjectStores
)
955 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
957 JSObject
* IDBTransaction::WrapObject(JSContext
* const aCx
,
958 JS::Handle
<JSObject
*> aGivenProto
) {
959 AssertIsOnOwningThread();
961 return IDBTransaction_Binding::Wrap(aCx
, this, std::move(aGivenProto
));
964 void IDBTransaction::GetEventTargetParent(EventChainPreVisitor
& aVisitor
) {
965 AssertIsOnOwningThread();
967 aVisitor
.mCanHandle
= true;
968 aVisitor
.SetParentTarget(mDatabase
, false);
972 IDBTransaction::Run() {
973 AssertIsOnOwningThread();
975 // TODO: Instead of checking for Finished and Committing states here, we could
976 // remove the transaction from the pending IDB transactions list on
979 if (ReadyState::Finished
== mReadyState
) {
980 // There are three cases where mReadyState is set to Finished: In
981 // FileCompleteOrAbortEvents, AbortInternal and in CommitIfNotStarted. We
982 // shouldn't get here after CommitIfNotStarted again.
983 MOZ_ASSERT(mFiredCompleteOrAbort
|| IsAborted());
987 if (ReadyState::Committing
== mReadyState
) {
988 MOZ_ASSERT(mSentCommitOrAbort
);
991 // We're back at the event loop, no longer newborn, so
992 // return to Inactive state:
993 // https://w3c.github.io/IndexedDB/#cleanup-indexed-database-transactions.
994 MOZ_ASSERT(ReadyState::Active
== mReadyState
);
995 mReadyState
= ReadyState::Inactive
;
997 CommitIfNotStarted();
1002 void IDBTransaction::CommitIfNotStarted() {
1003 AssertIsOnOwningThread();
1005 MOZ_ASSERT(ReadyState::Inactive
== mReadyState
);
1007 // Maybe commit if there were no requests generated.
1009 MOZ_ASSERT(!mPendingRequestCount
);
1010 mReadyState
= ReadyState::Finished
;
1016 } // namespace mozilla::dom