Coverage Report

Created: 2018-09-25 14:53

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