Source code

Revision control

Other Tools

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 "IDBTransaction.h"
8
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 "nsAutoPtr.h"
24
#include "nsPIDOMWindow.h"
25
#include "nsQueryObject.h"
26
#include "nsServiceManagerUtils.h"
27
#include "nsTHashtable.h"
28
#include "ProfilerHelpers.h"
29
#include "ReportInternalError.h"
30
31
// Include this last to avoid path problems on Windows.
32
#include "ActorsChild.h"
33
34
namespace {
35
using namespace mozilla::dom::indexedDB;
36
using namespace mozilla::ipc;
37
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));
54
}
55
return res;
56
}
57
58
ThreadLocal* GetIndexedDBThreadLocal() {
59
BackgroundChildImpl::ThreadLocal* const threadLocal =
60
BackgroundChildImpl::GetThreadLocalForCurrentThread();
61
MOZ_ASSERT(threadLocal);
62
63
ThreadLocal* idbThreadLocal = threadLocal->mIndexedDBThreadLocal;
64
MOZ_ASSERT(idbThreadLocal);
65
66
return idbThreadLocal;
67
}
68
} // namespace
69
70
namespace mozilla {
71
namespace dom {
72
73
using namespace mozilla::dom::indexedDB;
74
using namespace mozilla::ipc;
75
76
bool IDBTransaction::HasTransactionChild() const {
77
return (mMode == Mode::VersionChange
78
? static_cast<void*>(
79
mBackgroundActor.mVersionChangeBackgroundActor)
80
: mBackgroundActor.mNormalBackgroundActor) != nullptr;
81
}
82
83
template <typename Func>
84
auto IDBTransaction::DoWithTransactionChild(const Func& aFunc) const {
85
MOZ_ASSERT(HasTransactionChild());
86
return mMode == Mode::VersionChange
87
? aFunc(*mBackgroundActor.mVersionChangeBackgroundActor)
88
: aFunc(*mBackgroundActor.mNormalBackgroundActor);
89
}
90
91
IDBTransaction::IDBTransaction(IDBDatabase* const aDatabase,
92
const nsTArray<nsString>& aObjectStoreNames,
93
const Mode aMode, nsString aFilename,
94
const uint32_t aLineNo, const uint32_t aColumn,
95
CreatedFromFactoryFunction /*aDummy*/)
96
: DOMEventTargetHelper(aDatabase),
97
mDatabase(aDatabase),
98
mObjectStoreNames(aObjectStoreNames),
99
mLoggingSerialNumber(GetIndexedDBThreadLocal()->NextTransactionSN(aMode)),
100
mNextObjectStoreId(0),
101
mNextIndexId(0),
102
mAbortCode(NS_OK),
103
mPendingRequestCount(0),
104
mFilename(std::move(aFilename)),
105
mLineNo(aLineNo),
106
mColumn(aColumn),
107
mMode(aMode),
108
mCreating(false),
109
mRegistered(false),
110
mNotedActiveTransaction(false) {
111
MOZ_ASSERT(aDatabase);
112
aDatabase->AssertIsOnOwningThread();
113
114
// This also nulls mBackgroundActor.mVersionChangeBackgroundActor, so this is
115
// valid also for mMode == Mode::VersionChange.
116
mBackgroundActor.mNormalBackgroundActor = nullptr;
117
118
#ifdef DEBUG
119
if (!aObjectStoreNames.IsEmpty()) {
120
// Make sure the array is properly sorted.
121
MOZ_ASSERT(
122
std::is_sorted(aObjectStoreNames.cbegin(), aObjectStoreNames.cend()));
123
124
// Make sure there are no duplicates in our objectStore names.
125
MOZ_ASSERT(aObjectStoreNames.cend() ==
126
std::adjacent_find(aObjectStoreNames.cbegin(),
127
aObjectStoreNames.cend()));
128
}
129
#endif
130
131
mozilla::HoldJSObjects(this);
132
}
133
134
IDBTransaction::~IDBTransaction() {
135
AssertIsOnOwningThread();
136
MOZ_ASSERT(!mPendingRequestCount);
137
MOZ_ASSERT(!mCreating);
138
MOZ_ASSERT(!mNotedActiveTransaction);
139
MOZ_ASSERT(mSentCommitOrAbort);
140
MOZ_ASSERT_IF(HasTransactionChild(), mFiredCompleteOrAbort);
141
142
if (mRegistered) {
143
mDatabase->UnregisterTransaction(this);
144
#ifdef DEBUG
145
mRegistered = false;
146
#endif
147
}
148
149
if (HasTransactionChild()) {
150
if (mMode == Mode::VersionChange) {
151
mBackgroundActor.mVersionChangeBackgroundActor->SendDeleteMeInternal(
152
/* aFailedConstructor */ false);
153
} else {
154
mBackgroundActor.mNormalBackgroundActor->SendDeleteMeInternal();
155
}
156
}
157
MOZ_ASSERT(!HasTransactionChild(),
158
"SendDeleteMeInternal should have cleared!");
159
160
ReleaseWrapper(this);
161
mozilla::DropJSObjects(this);
162
}
163
164
// static
165
RefPtr<IDBTransaction> IDBTransaction::CreateVersionChange(
166
IDBDatabase* const aDatabase,
167
BackgroundVersionChangeTransactionChild* const aActor,
168
IDBOpenDBRequest* const aOpenRequest, const int64_t aNextObjectStoreId,
169
const int64_t aNextIndexId) {
170
MOZ_ASSERT(aDatabase);
171
aDatabase->AssertIsOnOwningThread();
172
MOZ_ASSERT(aActor);
173
MOZ_ASSERT(aOpenRequest);
174
MOZ_ASSERT(aNextObjectStoreId > 0);
175
MOZ_ASSERT(aNextIndexId > 0);
176
177
const nsTArray<nsString> emptyObjectStoreNames;
178
179
nsString filename;
180
uint32_t lineNo, column;
181
aOpenRequest->GetCallerLocation(filename, &lineNo, &column);
182
auto transaction = MakeRefPtr<IDBTransaction>(
183
aDatabase, emptyObjectStoreNames, Mode::VersionChange,
184
std::move(filename), lineNo, column, CreatedFromFactoryFunction{});
185
186
transaction->NoteActiveTransaction();
187
188
transaction->mBackgroundActor.mVersionChangeBackgroundActor = aActor;
189
transaction->mNextObjectStoreId = aNextObjectStoreId;
190
transaction->mNextIndexId = aNextIndexId;
191
192
aDatabase->RegisterTransaction(transaction);
193
transaction->mRegistered = true;
194
195
return transaction;
196
}
197
198
// static
199
RefPtr<IDBTransaction> IDBTransaction::Create(
200
JSContext* const aCx, IDBDatabase* const aDatabase,
201
const nsTArray<nsString>& aObjectStoreNames, const Mode aMode) {
202
MOZ_ASSERT(aDatabase);
203
aDatabase->AssertIsOnOwningThread();
204
MOZ_ASSERT(!aObjectStoreNames.IsEmpty());
205
MOZ_ASSERT(aMode == Mode::ReadOnly || aMode == Mode::ReadWrite ||
206
aMode == Mode::ReadWriteFlush || aMode == Mode::Cleanup);
207
208
nsString filename;
209
uint32_t lineNo, column;
210
IDBRequest::CaptureCaller(aCx, filename, &lineNo, &column);
211
auto transaction = MakeRefPtr<IDBTransaction>(
212
aDatabase, aObjectStoreNames, aMode, std::move(filename), lineNo, column,
213
CreatedFromFactoryFunction{});
214
215
if (!NS_IsMainThread()) {
216
WorkerPrivate* const workerPrivate = GetCurrentThreadWorkerPrivate();
217
MOZ_ASSERT(workerPrivate);
218
219
workerPrivate->AssertIsOnWorkerThread();
220
221
RefPtr<StrongWorkerRef> workerRef = StrongWorkerRef::Create(
222
workerPrivate, "IDBTransaction", [transaction]() {
223
transaction->AssertIsOnOwningThread();
224
if (!transaction->IsCommittingOrFinished()) {
225
IDB_REPORT_INTERNAL_ERR();
226
transaction->AbortInternal(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR,
227
nullptr);
228
}
229
});
230
if (NS_WARN_IF(!workerRef)) {
231
// Silence the destructor assertion if we never made this object live.
232
#ifdef DEBUG
233
transaction->mSentCommitOrAbort.Flip();
234
#endif
235
return nullptr;
236
}
237
238
transaction->mWorkerRef = std::move(workerRef);
239
}
240
241
nsCOMPtr<nsIRunnable> runnable = do_QueryObject(transaction);
242
nsContentUtils::AddPendingIDBTransaction(runnable.forget());
243
244
transaction->mCreating = true;
245
246
aDatabase->RegisterTransaction(transaction);
247
transaction->mRegistered = true;
248
249
return transaction;
250
}
251
252
// static
253
IDBTransaction* IDBTransaction::GetCurrent() {
254
using namespace mozilla::ipc;
255
256
MOZ_ASSERT(BackgroundChild::GetForCurrentThread());
257
258
return GetIndexedDBThreadLocal()->GetCurrentTransaction();
259
}
260
261
#ifdef DEBUG
262
263
void IDBTransaction::AssertIsOnOwningThread() const {
264
MOZ_ASSERT(mDatabase);
265
mDatabase->AssertIsOnOwningThread();
266
}
267
268
#endif // DEBUG
269
270
void IDBTransaction::SetBackgroundActor(
271
indexedDB::BackgroundTransactionChild* const aBackgroundActor) {
272
AssertIsOnOwningThread();
273
MOZ_ASSERT(aBackgroundActor);
274
MOZ_ASSERT(!mBackgroundActor.mNormalBackgroundActor);
275
MOZ_ASSERT(mMode != Mode::VersionChange);
276
277
NoteActiveTransaction();
278
279
mBackgroundActor.mNormalBackgroundActor = aBackgroundActor;
280
}
281
282
BackgroundRequestChild* IDBTransaction::StartRequest(
283
IDBRequest* const aRequest, const RequestParams& aParams) {
284
AssertIsOnOwningThread();
285
MOZ_ASSERT(aRequest);
286
MOZ_ASSERT(aParams.type() != RequestParams::T__None);
287
288
BackgroundRequestChild* const actor = new BackgroundRequestChild(aRequest);
289
290
DoWithTransactionChild([actor, &aParams](auto& transactionChild) {
291
transactionChild.SendPBackgroundIDBRequestConstructor(actor, aParams);
292
});
293
294
MOZ_ASSERT(actor->GetActorEventTarget(),
295
"The event target shall be inherited from its manager actor.");
296
297
// Balanced in BackgroundRequestChild::Recv__delete__().
298
OnNewRequest();
299
300
return actor;
301
}
302
303
void IDBTransaction::OpenCursor(BackgroundCursorChild* const aBackgroundActor,
304
const OpenCursorParams& aParams) {
305
AssertIsOnOwningThread();
306
MOZ_ASSERT(aBackgroundActor);
307
MOZ_ASSERT(aParams.type() != OpenCursorParams::T__None);
308
309
DoWithTransactionChild([aBackgroundActor, &aParams](auto& actor) {
310
actor.SendPBackgroundIDBCursorConstructor(aBackgroundActor, aParams);
311
});
312
313
MOZ_ASSERT(aBackgroundActor->GetActorEventTarget(),
314
"The event target shall be inherited from its manager actor.");
315
316
// Balanced in BackgroundCursorChild::RecvResponse().
317
OnNewRequest();
318
}
319
320
void IDBTransaction::RefreshSpec(const bool aMayDelete) {
321
AssertIsOnOwningThread();
322
323
for (auto& objectStore : mObjectStores) {
324
objectStore->RefreshSpec(aMayDelete);
325
}
326
327
for (auto& objectStore : mDeletedObjectStores) {
328
objectStore->RefreshSpec(false);
329
}
330
}
331
332
void IDBTransaction::OnNewRequest() {
333
AssertIsOnOwningThread();
334
335
if (!mPendingRequestCount) {
336
MOZ_ASSERT(ReadyState::Active == mReadyState);
337
mStarted.Flip();
338
}
339
340
++mPendingRequestCount;
341
}
342
343
void IDBTransaction::OnRequestFinished(
344
const bool aRequestCompletedSuccessfully) {
345
AssertIsOnOwningThread();
346
MOZ_ASSERT(mReadyState == ReadyState::Inactive ||
347
mReadyState == ReadyState::Finished);
348
MOZ_ASSERT_IF(mReadyState == ReadyState::Finished, !NS_SUCCEEDED(mAbortCode));
349
MOZ_ASSERT(mPendingRequestCount);
350
351
--mPendingRequestCount;
352
353
if (!mPendingRequestCount) {
354
if (mReadyState == ReadyState::Inactive) {
355
mReadyState = ReadyState::Committing;
356
}
357
358
if (aRequestCompletedSuccessfully) {
359
if (NS_SUCCEEDED(mAbortCode)) {
360
SendCommit();
361
} else {
362
SendAbort(mAbortCode);
363
}
364
} else {
365
// Don't try to send any more messages to the parent if the request actor
366
// was killed.
367
#ifdef DEBUG
368
mSentCommitOrAbort.Flip();
369
#endif
370
IDB_LOG_MARK_CHILD_TRANSACTION(
371
"Request actor was killed, transaction will be aborted",
372
"IDBTransaction abort", LoggingSerialNumber());
373
}
374
}
375
}
376
377
void IDBTransaction::SendCommit() {
378
AssertIsOnOwningThread();
379
MOZ_ASSERT(NS_SUCCEEDED(mAbortCode));
380
MOZ_ASSERT(IsCommittingOrFinished());
381
MOZ_ASSERT(!mPendingRequestCount);
382
383
// Don't do this in the macro because we always need to increment the serial
384
// number to keep in sync with the parent.
385
const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber();
386
387
IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
388
"All requests complete, committing transaction", "IDBTransaction commit",
389
LoggingSerialNumber(), requestSerialNumber);
390
391
DoWithTransactionChild([](auto& actor) { actor.SendCommit(); });
392
393
#ifdef DEBUG
394
mSentCommitOrAbort.Flip();
395
#endif
396
}
397
398
void IDBTransaction::SendAbort(const nsresult aResultCode) {
399
AssertIsOnOwningThread();
400
MOZ_ASSERT(NS_FAILED(aResultCode));
401
MOZ_ASSERT(IsCommittingOrFinished());
402
403
// Don't do this in the macro because we always need to increment the serial
404
// number to keep in sync with the parent.
405
const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber();
406
407
IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
408
"Aborting transaction with result 0x%x", "IDBTransaction abort (0x%x)",
409
LoggingSerialNumber(), requestSerialNumber, aResultCode);
410
411
DoWithTransactionChild(
412
[aResultCode](auto& actor) { actor.SendAbort(aResultCode); });
413
414
#ifdef DEBUG
415
mSentCommitOrAbort.Flip();
416
#endif
417
}
418
419
void IDBTransaction::NoteActiveTransaction() {
420
AssertIsOnOwningThread();
421
MOZ_ASSERT(!mNotedActiveTransaction);
422
423
mDatabase->NoteActiveTransaction();
424
mNotedActiveTransaction = true;
425
}
426
427
void IDBTransaction::MaybeNoteInactiveTransaction() {
428
AssertIsOnOwningThread();
429
430
if (mNotedActiveTransaction) {
431
mDatabase->NoteInactiveTransaction();
432
mNotedActiveTransaction = false;
433
}
434
}
435
436
bool IDBTransaction::CanAcceptRequests() const {
437
AssertIsOnOwningThread();
438
439
// If we haven't started anything then we can accept requests.
440
// If we've already started then we need to check to see if we still have the
441
// mCreating flag set. If we do (i.e. we haven't returned to the event loop
442
// from the time we were created) then we can accept requests. Otherwise check
443
// the currently running transaction to see if it's the same. We only allow
444
// other requests to be made if this transaction is currently running.
445
return mReadyState == ReadyState::Active &&
446
(!mStarted || mCreating || GetCurrent() == this);
447
}
448
449
IDBTransaction::AutoRestoreState<IDBTransaction::ReadyState::Inactive,
450
IDBTransaction::ReadyState::Active>
451
IDBTransaction::TemporarilyTransitionToActive() {
452
return AutoRestoreState<ReadyState::Inactive, ReadyState::Active>{*this};
453
}
454
455
IDBTransaction::AutoRestoreState<IDBTransaction::ReadyState::Active,
456
IDBTransaction::ReadyState::Inactive>
457
IDBTransaction::TemporarilyTransitionToInactive() {
458
return AutoRestoreState<ReadyState::Active, ReadyState::Inactive>{*this};
459
}
460
461
void IDBTransaction::GetCallerLocation(nsAString& aFilename,
462
uint32_t* const aLineNo,
463
uint32_t* const aColumn) const {
464
AssertIsOnOwningThread();
465
MOZ_ASSERT(aLineNo);
466
MOZ_ASSERT(aColumn);
467
468
aFilename = mFilename;
469
*aLineNo = mLineNo;
470
*aColumn = mColumn;
471
}
472
473
RefPtr<IDBObjectStore> IDBTransaction::CreateObjectStore(
474
const ObjectStoreSpec& aSpec) {
475
AssertIsOnOwningThread();
476
MOZ_ASSERT(aSpec.metadata().id());
477
MOZ_ASSERT(Mode::VersionChange == mMode);
478
MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor);
479
MOZ_ASSERT(CanAcceptRequests());
480
481
#ifdef DEBUG
482
{
483
// TODO: Bind name outside of lambda capture as a workaround for GCC 7 bug
485
const auto& name = aSpec.metadata().name();
486
// TODO: Use #ifdef and local variable as a workaround for Bug 1583449.
487
const bool objectStoreNameDoesNotYetExist =
488
std::all_of(mObjectStores.cbegin(), mObjectStores.cend(),
489
[&name](const auto& objectStore) {
490
return objectStore->Name() != name;
491
});
492
MOZ_ASSERT(objectStoreNameDoesNotYetExist);
493
}
494
#endif
495
496
MOZ_ALWAYS_TRUE(
497
mBackgroundActor.mVersionChangeBackgroundActor->SendCreateObjectStore(
498
aSpec.metadata()));
499
500
RefPtr<IDBObjectStore> objectStore = IDBObjectStore::Create(this, aSpec);
501
MOZ_ASSERT(objectStore);
502
503
mObjectStores.AppendElement(objectStore);
504
505
return objectStore;
506
}
507
508
void IDBTransaction::DeleteObjectStore(const int64_t aObjectStoreId) {
509
AssertIsOnOwningThread();
510
MOZ_ASSERT(aObjectStoreId);
511
MOZ_ASSERT(Mode::VersionChange == mMode);
512
MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor);
513
MOZ_ASSERT(CanAcceptRequests());
514
515
MOZ_ALWAYS_TRUE(
516
mBackgroundActor.mVersionChangeBackgroundActor->SendDeleteObjectStore(
517
aObjectStoreId));
518
519
const auto foundIt =
520
std::find_if(mObjectStores.begin(), mObjectStores.end(),
521
[aObjectStoreId](const auto& objectStore) {
522
return objectStore->Id() == aObjectStoreId;
523
});
524
if (foundIt != mObjectStores.end()) {
525
auto& objectStore = *foundIt;
526
objectStore->NoteDeletion();
527
528
RefPtr<IDBObjectStore>* deletedObjectStore =
529
mDeletedObjectStores.AppendElement();
530
deletedObjectStore->swap(objectStore);
531
532
mObjectStores.RemoveElementAt(foundIt);
533
}
534
}
535
536
void IDBTransaction::RenameObjectStore(const int64_t aObjectStoreId,
537
const nsAString& aName) {
538
AssertIsOnOwningThread();
539
MOZ_ASSERT(aObjectStoreId);
540
MOZ_ASSERT(Mode::VersionChange == mMode);
541
MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor);
542
MOZ_ASSERT(CanAcceptRequests());
543
544
MOZ_ALWAYS_TRUE(
545
mBackgroundActor.mVersionChangeBackgroundActor->SendRenameObjectStore(
546
aObjectStoreId, nsString(aName)));
547
}
548
549
void IDBTransaction::CreateIndex(IDBObjectStore* const aObjectStore,
550
const indexedDB::IndexMetadata& aMetadata) {
551
AssertIsOnOwningThread();
552
MOZ_ASSERT(aObjectStore);
553
MOZ_ASSERT(aMetadata.id());
554
MOZ_ASSERT(Mode::VersionChange == mMode);
555
MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor);
556
MOZ_ASSERT(CanAcceptRequests());
557
558
MOZ_ALWAYS_TRUE(
559
mBackgroundActor.mVersionChangeBackgroundActor->SendCreateIndex(
560
aObjectStore->Id(), aMetadata));
561
}
562
563
void IDBTransaction::DeleteIndex(IDBObjectStore* const aObjectStore,
564
const int64_t aIndexId) {
565
AssertIsOnOwningThread();
566
MOZ_ASSERT(aObjectStore);
567
MOZ_ASSERT(aIndexId);
568
MOZ_ASSERT(Mode::VersionChange == mMode);
569
MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor);
570
MOZ_ASSERT(CanAcceptRequests());
571
572
MOZ_ALWAYS_TRUE(
573
mBackgroundActor.mVersionChangeBackgroundActor->SendDeleteIndex(
574
aObjectStore->Id(), aIndexId));
575
}
576
577
void IDBTransaction::RenameIndex(IDBObjectStore* const aObjectStore,
578
const int64_t aIndexId,
579
const nsAString& aName) {
580
AssertIsOnOwningThread();
581
MOZ_ASSERT(aObjectStore);
582
MOZ_ASSERT(aIndexId);
583
MOZ_ASSERT(Mode::VersionChange == mMode);
584
MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor);
585
MOZ_ASSERT(CanAcceptRequests());
586
587
MOZ_ALWAYS_TRUE(
588
mBackgroundActor.mVersionChangeBackgroundActor->SendRenameIndex(
589
aObjectStore->Id(), aIndexId, nsString(aName)));
590
}
591
592
void IDBTransaction::AbortInternal(const nsresult aAbortCode,
593
RefPtr<DOMException> aError) {
594
AssertIsOnOwningThread();
595
MOZ_ASSERT(NS_FAILED(aAbortCode));
596
MOZ_ASSERT(!IsCommittingOrFinished());
597
598
const bool isVersionChange = mMode == Mode::VersionChange;
599
const bool needToSendAbort = mReadyState == ReadyState::Inactive && !mStarted;
600
601
mAbortCode = aAbortCode;
602
mReadyState = ReadyState::Finished;
603
mError = std::move(aError);
604
605
if (isVersionChange) {
606
// If a version change transaction is aborted, we must revert the world
607
// back to its previous state unless we're being invalidated after the
608
// transaction already completed.
609
if (!mDatabase->IsInvalidated()) {
610
mDatabase->RevertToPreviousState();
611
}
612
613
// We do the reversion only for the mObjectStores/mDeletedObjectStores but
614
// not for the mIndexes/mDeletedIndexes of each IDBObjectStore because it's
615
// time-consuming(O(m*n)) and mIndexes/mDeletedIndexes won't be used anymore
616
// in IDBObjectStore::(Create|Delete)Index() and IDBObjectStore::Index() in
617
// which all the executions are returned earlier by
618
// !transaction->CanAcceptRequests().
619
620
const nsTArray<ObjectStoreSpec>& specArray =
621
mDatabase->Spec()->objectStores();
622
623
if (specArray.IsEmpty()) {
624
// This case is specially handled as a performance optimization, it is
625
// equivalent to the else block.
626
mObjectStores.Clear();
627
} else {
628
const auto validIds = TransformToHashtable<nsUint64HashKey>(
629
specArray, [](const auto& spec) {
630
const int64_t objectStoreId = spec.metadata().id();
631
MOZ_ASSERT(objectStoreId);
632
return static_cast<uint64_t>(objectStoreId);
633
});
634
635
mObjectStores.RemoveElementsAt(
636
std::remove_if(mObjectStores.begin(), mObjectStores.end(),
637
[&validIds](const auto& objectStore) {
638
return !validIds.Contains(
639
uint64_t(objectStore->Id()));
640
}),
641
mObjectStores.end());
642
643
std::copy_if(std::make_move_iterator(mDeletedObjectStores.begin()),
644
std::make_move_iterator(mDeletedObjectStores.end()),
645
MakeBackInserter(mObjectStores),
646
[&validIds](const auto& deletedObjectStore) {
647
const int64_t objectStoreId = deletedObjectStore->Id();
648
MOZ_ASSERT(objectStoreId);
649
return validIds.Contains(uint64_t(objectStoreId));
650
});
651
}
652
mDeletedObjectStores.Clear();
653
}
654
655
// Fire the abort event if there are no outstanding requests. Otherwise the
656
// abort event will be fired when all outstanding requests finish.
657
if (needToSendAbort) {
658
SendAbort(aAbortCode);
659
}
660
661
if (isVersionChange) {
662
mDatabase->Close();
663
}
664
}
665
666
void IDBTransaction::Abort(IDBRequest* const aRequest) {
667
AssertIsOnOwningThread();
668
MOZ_ASSERT(aRequest);
669
670
if (IsCommittingOrFinished()) {
671
// Already started (and maybe finished) the commit or abort so there is
672
// nothing to do here.
673
return;
674
}
675
676
ErrorResult rv;
677
RefPtr<DOMException> error = aRequest->GetError(rv);
678
679
// TODO: Do we deliberately ignore rv here? Isn't there a static analysis that
680
// prevents that?
681
682
AbortInternal(aRequest->GetErrorCode(), std::move(error));
683
}
684
685
void IDBTransaction::Abort(const nsresult aErrorCode) {
686
AssertIsOnOwningThread();
687
688
if (IsCommittingOrFinished()) {
689
// Already started (and maybe finished) the commit or abort so there is
690
// nothing to do here.
691
return;
692
}
693
694
AbortInternal(aErrorCode, DOMException::Create(aErrorCode));
695
}
696
698
void IDBTransaction::Abort(ErrorResult& aRv) {
699
AssertIsOnOwningThread();
700
701
if (IsCommittingOrFinished()) {
702
aRv = NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
703
return;
704
}
705
706
mReadyState = ReadyState::Inactive;
707
708
AbortInternal(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR, nullptr);
709
710
mAbortedByScript.Flip();
711
}
712
713
void IDBTransaction::FireCompleteOrAbortEvents(const nsresult aResult) {
714
AssertIsOnOwningThread();
715
MOZ_ASSERT(!mFiredCompleteOrAbort);
716
717
mReadyState = ReadyState::Finished;
718
719
#ifdef DEBUG
720
mFiredCompleteOrAbort.Flip();
721
#endif
722
723
// Make sure we drop the WorkerRef when this function completes.
724
const auto scopeExit = MakeScopeExit([&] { mWorkerRef = nullptr; });
725
726
RefPtr<Event> event;
727
if (NS_SUCCEEDED(aResult)) {
728
event = CreateGenericEvent(this, nsDependentString(kCompleteEventType),
729
eDoesNotBubble, eNotCancelable);
730
MOZ_ASSERT(event);
731
732
// If we hit this assertion, it probably means transaction object on the
733
// parent process doesn't propagate error properly.
734
MOZ_ASSERT(NS_SUCCEEDED(mAbortCode));
735
} else {
736
if (aResult == NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR) {
737
mDatabase->SetQuotaExceeded();
738
}
739
740
if (!mError && !mAbortedByScript) {
741
mError = DOMException::Create(aResult);
742
}
743
744
event = CreateGenericEvent(this, nsDependentString(kAbortEventType),
745
eDoesBubble, eNotCancelable);
746
MOZ_ASSERT(event);
747
748
if (NS_SUCCEEDED(mAbortCode)) {
749
mAbortCode = aResult;
750
}
751
}
752
753
if (NS_SUCCEEDED(mAbortCode)) {
754
IDB_LOG_MARK_CHILD_TRANSACTION("Firing 'complete' event",
755
"IDBTransaction 'complete' event",
756
mLoggingSerialNumber);
757
} else {
758
IDB_LOG_MARK_CHILD_TRANSACTION("Firing 'abort' event with error 0x%x",
759
"IDBTransaction 'abort' event (0x%x)",
760
mLoggingSerialNumber, mAbortCode);
761
}
762
763
IgnoredErrorResult rv;
764
DispatchEvent(*event, rv);
765
if (rv.Failed()) {
766
NS_WARNING("DispatchEvent failed!");
767
}
768
769
// Normally, we note inactive transaction here instead of
770
// IDBTransaction::ClearBackgroundActor() because here is the earliest place
771
// to know that it becomes non-blocking to allow the scheduler to start the
772
// preemption as soon as it can.
773
// Note: If the IDBTransaction object is held by the script,
774
// ClearBackgroundActor() will be done in ~IDBTransaction() until garbage
775
// collected after its window is closed which prevents us to preempt its
776
// window immediately after committed.
777
MaybeNoteInactiveTransaction();
778
}
779
780
int64_t IDBTransaction::NextObjectStoreId() {
781
AssertIsOnOwningThread();
782
MOZ_ASSERT(Mode::VersionChange == mMode);
783
784
return mNextObjectStoreId++;
785
}
786
787
int64_t IDBTransaction::NextIndexId() {
788
AssertIsOnOwningThread();
789
MOZ_ASSERT(Mode::VersionChange == mMode);
790
791
return mNextIndexId++;
792
}
793
794
void IDBTransaction::InvalidateCursorCaches() {
795
AssertIsOnOwningThread();
796
797
for (auto* const cursor : mCursors) {
798
cursor->InvalidateCachedResponses();
799
}
800
}
801
802
void IDBTransaction::RegisterCursor(IDBCursor* const aCursor) {
803
AssertIsOnOwningThread();
804
805
mCursors.AppendElement(aCursor);
806
}
807
808
void IDBTransaction::UnregisterCursor(IDBCursor* const aCursor) {
809
AssertIsOnOwningThread();
810
811
DebugOnly<bool> removed = mCursors.RemoveElement(aCursor);
812
MOZ_ASSERT(removed);
813
}
814
815
nsIGlobalObject* IDBTransaction::GetParentObject() const {
816
AssertIsOnOwningThread();
817
818
return mDatabase->GetParentObject();
819
}
820
821
IDBTransactionMode IDBTransaction::GetMode(ErrorResult& aRv) const {
822
AssertIsOnOwningThread();
823
824
switch (mMode) {
825
case Mode::ReadOnly:
826
return IDBTransactionMode::Readonly;
827
828
case Mode::ReadWrite:
829
return IDBTransactionMode::Readwrite;
830
831
case Mode::ReadWriteFlush:
832
return IDBTransactionMode::Readwriteflush;
833
834
case Mode::Cleanup:
835
return IDBTransactionMode::Cleanup;
836
837
case Mode::VersionChange:
838
return IDBTransactionMode::Versionchange;
839
840
case Mode::Invalid:
841
default:
842
MOZ_CRASH("Bad mode!");
843
}
844
}
845
846
DOMException* IDBTransaction::GetError() const {
847
AssertIsOnOwningThread();
848
849
return mError;
850
}
851
852
RefPtr<DOMStringList> IDBTransaction::ObjectStoreNames() const {
853
AssertIsOnOwningThread();
854
855
if (mMode == Mode::VersionChange) {
856
return mDatabase->ObjectStoreNames();
857
}
858
859
auto list = MakeRefPtr<DOMStringList>();
860
list->StringArray() = mObjectStoreNames;
861
return list;
862
}
863
864
RefPtr<IDBObjectStore> IDBTransaction::ObjectStore(const nsAString& aName,
865
ErrorResult& aRv) {
866
AssertIsOnOwningThread();
867
868
if (IsCommittingOrFinished()) {
869
aRv.ThrowDOMException(
870
NS_ERROR_DOM_INVALID_STATE_ERR,
871
NS_LITERAL_CSTRING("Transaction is already committing or done."));
872
return nullptr;
873
}
874
875
const ObjectStoreSpec* spec = nullptr;
876
877
if (IDBTransaction::Mode::VersionChange == mMode ||
878
mObjectStoreNames.Contains(aName)) {
879
const nsTArray<ObjectStoreSpec>& objectStores =
880
mDatabase->Spec()->objectStores();
881
882
const auto foundIt =
883
std::find_if(objectStores.cbegin(), objectStores.cend(),
884
[&aName](const auto& objectStore) {
885
return objectStore.metadata().name() == aName;
886
});
887
if (foundIt != objectStores.cend()) {
888
spec = &*foundIt;
889
}
890
}
891
892
if (!spec) {
893
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR);
894
return nullptr;
895
}
896
897
RefPtr<IDBObjectStore> objectStore;
898
899
const auto foundIt = std::find_if(
900
mObjectStores.cbegin(), mObjectStores.cend(),
901
[desiredId = spec->metadata().id()](const auto& existingObjectStore) {
902
return existingObjectStore->Id() == desiredId;
903
});
904
if (foundIt != mObjectStores.cend()) {
905
objectStore = *foundIt;
906
} else {
907
objectStore = IDBObjectStore::Create(this, *spec);
908
MOZ_ASSERT(objectStore);
909
910
mObjectStores.AppendElement(objectStore);
911
}
912
913
return objectStore;
914
}
915
916
NS_IMPL_ADDREF_INHERITED(IDBTransaction, DOMEventTargetHelper)
917
NS_IMPL_RELEASE_INHERITED(IDBTransaction, DOMEventTargetHelper)
918
919
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBTransaction)
920
NS_INTERFACE_MAP_ENTRY(nsIRunnable)
921
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
922
923
NS_IMPL_CYCLE_COLLECTION_CLASS(IDBTransaction)
924
925
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBTransaction,
926
DOMEventTargetHelper)
927
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDatabase)
928
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError)
929
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mObjectStores)
930
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDeletedObjectStores)
931
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
932
933
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBTransaction,
934
DOMEventTargetHelper)
935
// Don't unlink mDatabase!
936
NS_IMPL_CYCLE_COLLECTION_UNLINK(mError)
937
NS_IMPL_CYCLE_COLLECTION_UNLINK(mObjectStores)
938
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDeletedObjectStores)
939
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
940
941
JSObject* IDBTransaction::WrapObject(JSContext* const aCx,
942
JS::Handle<JSObject*> aGivenProto) {
943
AssertIsOnOwningThread();
944
945
return IDBTransaction_Binding::Wrap(aCx, this, std::move(aGivenProto));
946
}
947
948
void IDBTransaction::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
949
AssertIsOnOwningThread();
950
951
aVisitor.mCanHandle = true;
952
aVisitor.SetParentTarget(mDatabase, false);
953
}
954
955
NS_IMETHODIMP
956
IDBTransaction::Run() {
957
AssertIsOnOwningThread();
958
959
// We're back at the event loop, no longer newborn.
960
mCreating = false;
961
962
MOZ_ASSERT_IF(mReadyState == ReadyState::Finished, IsAborted());
963
964
// Maybe commit if there were no requests generated.
965
if (!mStarted && mReadyState != ReadyState::Finished) {
966
MOZ_ASSERT(mReadyState == ReadyState::Inactive ||
967
mReadyState == ReadyState::Active);
968
mReadyState = ReadyState::Finished;
969
970
SendCommit();
971
}
972
973
return NS_OK;
974
}
975
976
} // namespace dom
977
} // namespace mozilla