Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/indexedDB/IDBDatabase.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 "IDBDatabase.h"
8
9
#include "FileInfo.h"
10
#include "IDBEvents.h"
11
#include "IDBFactory.h"
12
#include "IDBIndex.h"
13
#include "IDBMutableFile.h"
14
#include "IDBObjectStore.h"
15
#include "IDBRequest.h"
16
#include "IDBTransaction.h"
17
#include "IDBFactory.h"
18
#include "IndexedDatabaseManager.h"
19
#include "mozilla/ErrorResult.h"
20
#include "mozilla/EventDispatcher.h"
21
#include "MainThreadUtils.h"
22
#include "mozilla/Services.h"
23
#include "mozilla/storage.h"
24
#include "mozilla/dom/BindingDeclarations.h"
25
#include "mozilla/dom/DOMStringList.h"
26
#include "mozilla/dom/DOMStringListBinding.h"
27
#include "mozilla/dom/Exceptions.h"
28
#include "mozilla/dom/File.h"
29
#include "mozilla/dom/IDBDatabaseBinding.h"
30
#include "mozilla/dom/IDBObjectStoreBinding.h"
31
#include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseFileChild.h"
32
#include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h"
33
#include "mozilla/dom/IPCBlobUtils.h"
34
#include "mozilla/dom/quota/QuotaManager.h"
35
#include "mozilla/ipc/BackgroundChild.h"
36
#include "mozilla/ipc/BackgroundUtils.h"
37
#include "mozilla/ipc/FileDescriptor.h"
38
#include "mozilla/ipc/InputStreamParams.h"
39
#include "mozilla/ipc/InputStreamUtils.h"
40
#include "nsAutoPtr.h"
41
#include "nsCOMPtr.h"
42
#include "nsIDocument.h"
43
#include "nsIObserver.h"
44
#include "nsIObserverService.h"
45
#include "nsIScriptError.h"
46
#include "nsISupportsPrimitives.h"
47
#include "nsThreadUtils.h"
48
#include "ProfilerHelpers.h"
49
#include "ReportInternalError.h"
50
#include "ScriptErrorHelper.h"
51
#include "nsQueryObject.h"
52
53
// Include this last to avoid path problems on Windows.
54
#include "ActorsChild.h"
55
56
namespace mozilla {
57
namespace dom {
58
59
using namespace mozilla::dom::indexedDB;
60
using namespace mozilla::dom::quota;
61
using namespace mozilla::ipc;
62
using namespace mozilla::services;
63
64
namespace {
65
66
const char kCycleCollectionObserverTopic[] = "cycle-collector-end";
67
const char kMemoryPressureObserverTopic[] = "memory-pressure";
68
const char kWindowObserverTopic[] = "inner-window-destroyed";
69
70
class CancelableRunnableWrapper final
71
  : public CancelableRunnable
72
{
73
  nsCOMPtr<nsIRunnable> mRunnable;
74
75
public:
76
  explicit CancelableRunnableWrapper(nsIRunnable* aRunnable)
77
    : CancelableRunnable("dom::CancelableRunnableWrapper")
78
    , mRunnable(aRunnable)
79
0
  {
80
0
    MOZ_ASSERT(aRunnable);
81
0
  }
82
83
private:
84
  ~CancelableRunnableWrapper()
85
0
  { }
86
87
  NS_DECL_NSIRUNNABLE
88
  nsresult Cancel() override;
89
};
90
91
class DatabaseFile final
92
  : public PBackgroundIDBDatabaseFileChild
93
{
94
  IDBDatabase* mDatabase;
95
96
public:
97
  explicit DatabaseFile(IDBDatabase* aDatabase)
98
    : mDatabase(aDatabase)
99
0
  {
100
0
    MOZ_ASSERT(aDatabase);
101
0
    aDatabase->AssertIsOnOwningThread();
102
0
103
0
    MOZ_COUNT_CTOR(DatabaseFile);
104
0
  }
105
106
private:
107
  ~DatabaseFile()
108
0
  {
109
0
    MOZ_ASSERT(!mDatabase);
110
0
111
0
    MOZ_COUNT_DTOR(DatabaseFile);
112
0
  }
113
114
  virtual void
115
  ActorDestroy(ActorDestroyReason aWhy) override
116
0
  {
117
0
    MOZ_ASSERT(mDatabase);
118
0
    mDatabase->AssertIsOnOwningThread();
119
0
120
0
    if (aWhy != Deletion) {
121
0
      RefPtr<IDBDatabase> database = mDatabase;
122
0
      database->NoteFinishedFileActor(this);
123
0
    }
124
0
125
#ifdef DEBUG
126
    mDatabase = nullptr;
127
#endif
128
  }
129
};
130
131
} // namespace
132
133
class IDBDatabase::Observer final
134
  : public nsIObserver
135
{
136
  IDBDatabase* mWeakDatabase;
137
  const uint64_t mWindowId;
138
139
public:
140
  Observer(IDBDatabase* aDatabase, uint64_t aWindowId)
141
    : mWeakDatabase(aDatabase)
142
    , mWindowId(aWindowId)
143
0
  {
144
0
    MOZ_ASSERT(NS_IsMainThread());
145
0
    MOZ_ASSERT(aDatabase);
146
0
  }
147
148
  void
149
  Revoke()
150
0
  {
151
0
    MOZ_ASSERT(NS_IsMainThread());
152
0
153
0
    mWeakDatabase = nullptr;
154
0
  }
155
156
  NS_DECL_ISUPPORTS
157
158
private:
159
  ~Observer()
160
0
  {
161
0
    MOZ_ASSERT(NS_IsMainThread());
162
0
    MOZ_ASSERT(!mWeakDatabase);
163
0
  }
164
165
  NS_DECL_NSIOBSERVER
166
};
167
168
IDBDatabase::IDBDatabase(IDBOpenDBRequest* aRequest,
169
                         IDBFactory* aFactory,
170
                         BackgroundDatabaseChild* aActor,
171
                         DatabaseSpec* aSpec)
172
  : IDBWrapperCache(aRequest)
173
  , mFactory(aFactory)
174
  , mSpec(aSpec)
175
  , mBackgroundActor(aActor)
176
  , mFileHandleDisabled(aRequest->IsFileHandleDisabled())
177
  , mClosed(false)
178
  , mInvalidated(false)
179
  , mQuotaExceeded(false)
180
  , mIncreasedActiveDatabaseCount(false)
181
0
{
182
0
  MOZ_ASSERT(aRequest);
183
0
  MOZ_ASSERT(aFactory);
184
0
  aFactory->AssertIsOnOwningThread();
185
0
  MOZ_ASSERT(aActor);
186
0
  MOZ_ASSERT(aSpec);
187
0
}
188
189
IDBDatabase::~IDBDatabase()
190
0
{
191
0
  AssertIsOnOwningThread();
192
0
  MOZ_ASSERT(!mBackgroundActor);
193
0
  MOZ_ASSERT(!mIncreasedActiveDatabaseCount);
194
0
}
195
196
// static
197
already_AddRefed<IDBDatabase>
198
IDBDatabase::Create(IDBOpenDBRequest* aRequest,
199
                    IDBFactory* aFactory,
200
                    BackgroundDatabaseChild* aActor,
201
                    DatabaseSpec* aSpec)
202
0
{
203
0
  MOZ_ASSERT(aRequest);
204
0
  MOZ_ASSERT(aFactory);
205
0
  aFactory->AssertIsOnOwningThread();
206
0
  MOZ_ASSERT(aActor);
207
0
  MOZ_ASSERT(aSpec);
208
0
209
0
  RefPtr<IDBDatabase> db =
210
0
    new IDBDatabase(aRequest, aFactory, aActor, aSpec);
211
0
212
0
  db->SetScriptOwner(aRequest->GetScriptOwner());
213
0
214
0
  if (NS_IsMainThread()) {
215
0
    if (nsPIDOMWindowInner* window = aFactory->GetParentObject()) {
216
0
      uint64_t windowId = window->WindowID();
217
0
218
0
      RefPtr<Observer> observer = new Observer(db, windowId);
219
0
220
0
      nsCOMPtr<nsIObserverService> obsSvc = GetObserverService();
221
0
      MOZ_ASSERT(obsSvc);
222
0
223
0
      // This topic must be successfully registered.
224
0
      MOZ_ALWAYS_SUCCEEDS(
225
0
        obsSvc->AddObserver(observer, kWindowObserverTopic, false));
226
0
227
0
      // These topics are not crucial.
228
0
      if (NS_FAILED(obsSvc->AddObserver(observer,
229
0
                                        kCycleCollectionObserverTopic,
230
0
                                        false)) ||
231
0
          NS_FAILED(obsSvc->AddObserver(observer,
232
0
                                        kMemoryPressureObserverTopic,
233
0
                                        false))) {
234
0
        NS_WARNING("Failed to add additional memory observers!");
235
0
      }
236
0
237
0
      db->mObserver.swap(observer);
238
0
    }
239
0
  }
240
0
241
0
  db->IncreaseActiveDatabaseCount();
242
0
243
0
  return db.forget();
244
0
}
245
246
#ifdef DEBUG
247
248
void
249
IDBDatabase::AssertIsOnOwningThread() const
250
{
251
  MOZ_ASSERT(mFactory);
252
  mFactory->AssertIsOnOwningThread();
253
}
254
255
#endif // DEBUG
256
257
nsIEventTarget*
258
IDBDatabase::EventTarget() const
259
0
{
260
0
  AssertIsOnOwningThread();
261
0
  return Factory()->EventTarget();
262
0
}
263
264
void
265
IDBDatabase::CloseInternal()
266
0
{
267
0
  AssertIsOnOwningThread();
268
0
269
0
  if (!mClosed) {
270
0
    mClosed = true;
271
0
272
0
    ExpireFileActors(/* aExpireAll */ true);
273
0
274
0
    if (mObserver) {
275
0
      mObserver->Revoke();
276
0
277
0
      nsCOMPtr<nsIObserverService> obsSvc = GetObserverService();
278
0
      if (obsSvc) {
279
0
        // These might not have been registered.
280
0
        obsSvc->RemoveObserver(mObserver, kCycleCollectionObserverTopic);
281
0
        obsSvc->RemoveObserver(mObserver, kMemoryPressureObserverTopic);
282
0
283
0
        MOZ_ALWAYS_SUCCEEDS(
284
0
          obsSvc->RemoveObserver(mObserver, kWindowObserverTopic));
285
0
      }
286
0
287
0
      mObserver = nullptr;
288
0
    }
289
0
290
0
    if (mBackgroundActor && !mInvalidated) {
291
0
      mBackgroundActor->SendClose();
292
0
    }
293
0
294
0
    // Decrease the number of active databases right after the database is
295
0
    // closed.
296
0
    MaybeDecreaseActiveDatabaseCount();
297
0
  }
298
0
}
299
300
void
301
IDBDatabase::InvalidateInternal()
302
0
{
303
0
  AssertIsOnOwningThread();
304
0
305
0
  InvalidateMutableFiles();
306
0
  AbortTransactions(/* aShouldWarn */ true);
307
0
308
0
  CloseInternal();
309
0
}
310
311
void
312
IDBDatabase::EnterSetVersionTransaction(uint64_t aNewVersion)
313
0
{
314
0
  AssertIsOnOwningThread();
315
0
  MOZ_ASSERT(aNewVersion);
316
0
  MOZ_ASSERT(!RunningVersionChangeTransaction());
317
0
  MOZ_ASSERT(mSpec);
318
0
  MOZ_ASSERT(!mPreviousSpec);
319
0
320
0
  mPreviousSpec = new DatabaseSpec(*mSpec);
321
0
322
0
  mSpec->metadata().version() = aNewVersion;
323
0
}
324
325
void
326
IDBDatabase::ExitSetVersionTransaction()
327
0
{
328
0
  AssertIsOnOwningThread();
329
0
330
0
  if (mPreviousSpec) {
331
0
    mPreviousSpec = nullptr;
332
0
  }
333
0
}
334
335
void
336
IDBDatabase::RevertToPreviousState()
337
0
{
338
0
  AssertIsOnOwningThread();
339
0
  MOZ_ASSERT(RunningVersionChangeTransaction());
340
0
  MOZ_ASSERT(mPreviousSpec);
341
0
342
0
  // Hold the current spec alive until RefreshTransactionsSpecEnumerator has
343
0
  // finished!
344
0
  nsAutoPtr<DatabaseSpec> currentSpec(mSpec.forget());
345
0
346
0
  mSpec = mPreviousSpec.forget();
347
0
348
0
  RefreshSpec(/* aMayDelete */ true);
349
0
}
350
351
void
352
IDBDatabase::RefreshSpec(bool aMayDelete)
353
0
{
354
0
  AssertIsOnOwningThread();
355
0
356
0
  for (auto iter = mTransactions.Iter(); !iter.Done(); iter.Next()) {
357
0
    RefPtr<IDBTransaction> transaction = iter.Get()->GetKey();
358
0
    MOZ_ASSERT(transaction);
359
0
    transaction->AssertIsOnOwningThread();
360
0
    transaction->RefreshSpec(aMayDelete);
361
0
  }
362
0
}
363
364
nsPIDOMWindowInner*
365
IDBDatabase::GetParentObject() const
366
0
{
367
0
  return mFactory->GetParentObject();
368
0
}
369
370
const nsString&
371
IDBDatabase::Name() const
372
0
{
373
0
  AssertIsOnOwningThread();
374
0
  MOZ_ASSERT(mSpec);
375
0
376
0
  return mSpec->metadata().name();
377
0
}
378
379
uint64_t
380
IDBDatabase::Version() const
381
0
{
382
0
  AssertIsOnOwningThread();
383
0
  MOZ_ASSERT(mSpec);
384
0
385
0
  return mSpec->metadata().version();
386
0
}
387
388
already_AddRefed<DOMStringList>
389
IDBDatabase::ObjectStoreNames() const
390
0
{
391
0
  AssertIsOnOwningThread();
392
0
  MOZ_ASSERT(mSpec);
393
0
394
0
  const nsTArray<ObjectStoreSpec>& objectStores = mSpec->objectStores();
395
0
396
0
  RefPtr<DOMStringList> list = new DOMStringList();
397
0
398
0
  if (!objectStores.IsEmpty()) {
399
0
    nsTArray<nsString>& listNames = list->StringArray();
400
0
    listNames.SetCapacity(objectStores.Length());
401
0
402
0
    for (uint32_t index = 0; index < objectStores.Length(); index++) {
403
0
      listNames.InsertElementSorted(objectStores[index].metadata().name());
404
0
    }
405
0
  }
406
0
407
0
  return list.forget();
408
0
}
409
410
already_AddRefed<nsIDocument>
411
IDBDatabase::GetOwnerDocument() const
412
0
{
413
0
  if (nsPIDOMWindowInner* window = GetOwner()) {
414
0
    nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
415
0
    return doc.forget();
416
0
  }
417
0
  return nullptr;
418
0
}
419
420
already_AddRefed<IDBObjectStore>
421
IDBDatabase::CreateObjectStore(
422
                            const nsAString& aName,
423
                            const IDBObjectStoreParameters& aOptionalParameters,
424
                            ErrorResult& aRv)
425
0
{
426
0
  AssertIsOnOwningThread();
427
0
428
0
  IDBTransaction* transaction = IDBTransaction::GetCurrent();
429
0
  if (!transaction ||
430
0
      transaction->Database() != this ||
431
0
      transaction->GetMode() != IDBTransaction::VERSION_CHANGE) {
432
0
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
433
0
    return nullptr;
434
0
  }
435
0
436
0
  if (!transaction->IsOpen()) {
437
0
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
438
0
    return nullptr;
439
0
  }
440
0
441
0
  KeyPath keyPath(0);
442
0
  if (NS_FAILED(KeyPath::Parse(aOptionalParameters.mKeyPath, &keyPath))) {
443
0
    aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
444
0
    return nullptr;
445
0
  }
446
0
447
0
  nsTArray<ObjectStoreSpec>& objectStores = mSpec->objectStores();
448
0
  for (uint32_t count = objectStores.Length(), index = 0;
449
0
       index < count;
450
0
       index++) {
451
0
    if (aName == objectStores[index].metadata().name()) {
452
0
      aRv.Throw(NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR);
453
0
      return nullptr;
454
0
    }
455
0
  }
456
0
457
0
  if (!keyPath.IsAllowedForObjectStore(aOptionalParameters.mAutoIncrement)) {
458
0
    aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
459
0
    return nullptr;
460
0
  }
461
0
462
0
  const ObjectStoreSpec* oldSpecElements =
463
0
    objectStores.IsEmpty() ? nullptr : objectStores.Elements();
464
0
465
0
  ObjectStoreSpec* newSpec = objectStores.AppendElement();
466
0
  newSpec->metadata() =
467
0
    ObjectStoreMetadata(transaction->NextObjectStoreId(), nsString(aName),
468
0
                        keyPath, aOptionalParameters.mAutoIncrement);
469
0
470
0
  if (oldSpecElements &&
471
0
      oldSpecElements != objectStores.Elements()) {
472
0
    MOZ_ASSERT(objectStores.Length() > 1);
473
0
474
0
    // Array got moved, update the spec pointers for all live objectStores and
475
0
    // indexes.
476
0
    RefreshSpec(/* aMayDelete */ false);
477
0
  }
478
0
479
0
  RefPtr<IDBObjectStore> objectStore =
480
0
    transaction->CreateObjectStore(*newSpec);
481
0
  MOZ_ASSERT(objectStore);
482
0
483
0
  // Don't do this in the macro because we always need to increment the serial
484
0
  // number to keep in sync with the parent.
485
0
  const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber();
486
0
487
0
  IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
488
0
                 "database(%s).transaction(%s).createObjectStore(%s)",
489
0
               "IndexedDB %s: C T[%lld] R[%llu]: "
490
0
                 "IDBDatabase.createObjectStore()",
491
0
               IDB_LOG_ID_STRING(),
492
0
               transaction->LoggingSerialNumber(),
493
0
               requestSerialNumber,
494
0
               IDB_LOG_STRINGIFY(this),
495
0
               IDB_LOG_STRINGIFY(transaction),
496
0
               IDB_LOG_STRINGIFY(objectStore));
497
0
498
0
  return objectStore.forget();
499
0
}
500
501
void
502
IDBDatabase::DeleteObjectStore(const nsAString& aName, ErrorResult& aRv)
503
0
{
504
0
  AssertIsOnOwningThread();
505
0
506
0
  IDBTransaction* transaction = IDBTransaction::GetCurrent();
507
0
  if (!transaction ||
508
0
      transaction->Database() != this ||
509
0
      transaction->GetMode() != IDBTransaction::VERSION_CHANGE) {
510
0
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
511
0
    return;
512
0
  }
513
0
514
0
  if (!transaction->IsOpen()) {
515
0
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
516
0
    return;
517
0
  }
518
0
519
0
  nsTArray<ObjectStoreSpec>& specArray = mSpec->objectStores();
520
0
521
0
  int64_t objectStoreId = 0;
522
0
523
0
  for (uint32_t specCount = specArray.Length(), specIndex = 0;
524
0
       specIndex < specCount;
525
0
       specIndex++) {
526
0
    const ObjectStoreMetadata& metadata = specArray[specIndex].metadata();
527
0
    MOZ_ASSERT(metadata.id());
528
0
529
0
    if (aName == metadata.name()) {
530
0
      objectStoreId = metadata.id();
531
0
532
0
      // Must do this before altering the metadata array!
533
0
      transaction->DeleteObjectStore(objectStoreId);
534
0
535
0
      specArray.RemoveElementAt(specIndex);
536
0
537
0
      RefreshSpec(/* aMayDelete */ false);
538
0
      break;
539
0
    }
540
0
  }
541
0
542
0
  if (!objectStoreId) {
543
0
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR);
544
0
    return;
545
0
  }
546
0
547
0
  // Don't do this in the macro because we always need to increment the serial
548
0
  // number to keep in sync with the parent.
549
0
  const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber();
550
0
551
0
  IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
552
0
                 "database(%s).transaction(%s).deleteObjectStore(\"%s\")",
553
0
               "IndexedDB %s: C T[%lld] R[%llu]: "
554
0
                 "IDBDatabase.deleteObjectStore()",
555
0
               IDB_LOG_ID_STRING(),
556
0
               transaction->LoggingSerialNumber(),
557
0
               requestSerialNumber,
558
0
               IDB_LOG_STRINGIFY(this),
559
0
               IDB_LOG_STRINGIFY(transaction),
560
0
               NS_ConvertUTF16toUTF8(aName).get());
561
0
}
562
563
already_AddRefed<IDBTransaction>
564
IDBDatabase::Transaction(JSContext* aCx,
565
                         const StringOrStringSequence& aStoreNames,
566
                         IDBTransactionMode aMode,
567
                         ErrorResult& aRv)
568
0
{
569
0
  AssertIsOnOwningThread();
570
0
571
0
  if ((aMode == IDBTransactionMode::Readwriteflush ||
572
0
       aMode == IDBTransactionMode::Cleanup) &&
573
0
      !IndexedDatabaseManager::ExperimentalFeaturesEnabled()) {
574
0
    // Pretend that this mode doesn't exist. We don't have a way to annotate
575
0
    // certain enum values as depending on preferences so we just duplicate the
576
0
    // normal exception generation here.
577
0
    aRv.ThrowTypeError<MSG_INVALID_ENUM_VALUE>(
578
0
      NS_LITERAL_STRING("Argument 2 of IDBDatabase.transaction"),
579
0
      NS_LITERAL_STRING("readwriteflush"),
580
0
      NS_LITERAL_STRING("IDBTransactionMode"));
581
0
    return nullptr;
582
0
  }
583
0
584
0
  RefPtr<IDBTransaction> transaction;
585
0
  aRv = Transaction(aCx, aStoreNames, aMode, getter_AddRefs(transaction));
586
0
  if (NS_WARN_IF(aRv.Failed())) {
587
0
    return nullptr;
588
0
  }
589
0
590
0
  return transaction.forget();
591
0
}
592
593
nsresult
594
IDBDatabase::Transaction(JSContext* aCx,
595
                         const StringOrStringSequence& aStoreNames,
596
                         IDBTransactionMode aMode,
597
                         IDBTransaction** aTransaction)
598
0
{
599
0
  AssertIsOnOwningThread();
600
0
601
0
  if (NS_WARN_IF((aMode == IDBTransactionMode::Readwriteflush ||
602
0
                  aMode == IDBTransactionMode::Cleanup) &&
603
0
                 !IndexedDatabaseManager::ExperimentalFeaturesEnabled())) {
604
0
    IDB_REPORT_INTERNAL_ERR();
605
0
    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
606
0
  }
607
0
608
0
  if (QuotaManager::IsShuttingDown()) {
609
0
    IDB_REPORT_INTERNAL_ERR();
610
0
    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
611
0
  }
612
0
613
0
  if (mClosed || RunningVersionChangeTransaction()) {
614
0
    return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
615
0
  }
616
0
617
0
  AutoTArray<nsString, 1> stackSequence;
618
0
619
0
  if (aStoreNames.IsString()) {
620
0
    stackSequence.AppendElement(aStoreNames.GetAsString());
621
0
  } else {
622
0
    MOZ_ASSERT(aStoreNames.IsStringSequence());
623
0
    if (aStoreNames.GetAsStringSequence().IsEmpty()) {
624
0
      return NS_ERROR_DOM_INVALID_ACCESS_ERR;
625
0
    }
626
0
  }
627
0
628
0
  const nsTArray<nsString>& storeNames =
629
0
    aStoreNames.IsString() ?
630
0
    stackSequence :
631
0
    static_cast<const nsTArray<nsString>&>(aStoreNames.GetAsStringSequence());
632
0
  MOZ_ASSERT(!storeNames.IsEmpty());
633
0
634
0
  const nsTArray<ObjectStoreSpec>& objectStores = mSpec->objectStores();
635
0
  const uint32_t nameCount = storeNames.Length();
636
0
637
0
  nsTArray<nsString> sortedStoreNames;
638
0
  sortedStoreNames.SetCapacity(nameCount);
639
0
640
0
  // Check to make sure the object store names we collected actually exist.
641
0
  for (uint32_t nameIndex = 0; nameIndex < nameCount; nameIndex++) {
642
0
    const nsString& name = storeNames[nameIndex];
643
0
644
0
    bool found = false;
645
0
646
0
    for (uint32_t objCount = objectStores.Length(), objIndex = 0;
647
0
         objIndex < objCount;
648
0
         objIndex++) {
649
0
      if (objectStores[objIndex].metadata().name() == name) {
650
0
        found = true;
651
0
        break;
652
0
      }
653
0
    }
654
0
655
0
    if (!found) {
656
0
      return NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR;
657
0
    }
658
0
659
0
    sortedStoreNames.InsertElementSorted(name);
660
0
  }
661
0
662
0
  // Remove any duplicates.
663
0
  for (uint32_t nameIndex = nameCount - 1; nameIndex > 0; nameIndex--) {
664
0
    if (sortedStoreNames[nameIndex] == sortedStoreNames[nameIndex - 1]) {
665
0
      sortedStoreNames.RemoveElementAt(nameIndex);
666
0
    }
667
0
  }
668
0
669
0
  IDBTransaction::Mode mode;
670
0
  switch (aMode) {
671
0
    case IDBTransactionMode::Readonly:
672
0
      mode = IDBTransaction::READ_ONLY;
673
0
      break;
674
0
    case IDBTransactionMode::Readwrite:
675
0
      if (mQuotaExceeded) {
676
0
        mode = IDBTransaction::CLEANUP;
677
0
        mQuotaExceeded = false;
678
0
      } else {
679
0
        mode = IDBTransaction::READ_WRITE;
680
0
      }
681
0
      break;
682
0
    case IDBTransactionMode::Readwriteflush:
683
0
      mode = IDBTransaction::READ_WRITE_FLUSH;
684
0
      break;
685
0
    case IDBTransactionMode::Cleanup:
686
0
      mode = IDBTransaction::CLEANUP;
687
0
      mQuotaExceeded = false;
688
0
      break;
689
0
    case IDBTransactionMode::Versionchange:
690
0
      return NS_ERROR_DOM_TYPE_ERR;
691
0
692
0
    default:
693
0
      MOZ_CRASH("Unknown mode!");
694
0
  }
695
0
696
0
  RefPtr<IDBTransaction> transaction =
697
0
    IDBTransaction::Create(aCx, this, sortedStoreNames, mode);
698
0
  if (NS_WARN_IF(!transaction)) {
699
0
    IDB_REPORT_INTERNAL_ERR();
700
0
    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
701
0
  }
702
0
703
0
  BackgroundTransactionChild* actor =
704
0
    new BackgroundTransactionChild(transaction);
705
0
706
0
  IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld]: "
707
0
                 "database(%s).transaction(%s)",
708
0
               "IndexedDB %s: C T[%lld]: IDBDatabase.transaction()",
709
0
               IDB_LOG_ID_STRING(),
710
0
               transaction->LoggingSerialNumber(),
711
0
               IDB_LOG_STRINGIFY(this),
712
0
               IDB_LOG_STRINGIFY(transaction));
713
0
714
0
  MOZ_ALWAYS_TRUE(
715
0
    mBackgroundActor->SendPBackgroundIDBTransactionConstructor(actor,
716
0
                                                               sortedStoreNames,
717
0
                                                               mode));
718
0
  MOZ_ASSERT(actor->GetActorEventTarget(),
719
0
    "The event target shall be inherited from it manager actor.");
720
0
721
0
  transaction->SetBackgroundActor(actor);
722
0
723
0
  if (mode == IDBTransaction::CLEANUP) {
724
0
    ExpireFileActors(/* aExpireAll */ true);
725
0
  }
726
0
727
0
  transaction.forget(aTransaction);
728
0
  return NS_OK;
729
0
}
730
731
StorageType
732
IDBDatabase::Storage() const
733
0
{
734
0
  AssertIsOnOwningThread();
735
0
  MOZ_ASSERT(mSpec);
736
0
737
0
  return PersistenceTypeToStorage(mSpec->metadata().persistenceType());
738
0
}
739
740
already_AddRefed<IDBRequest>
741
IDBDatabase::CreateMutableFile(JSContext* aCx,
742
                               const nsAString& aName,
743
                               const Optional<nsAString>& aType,
744
                               ErrorResult& aRv)
745
0
{
746
0
  AssertIsOnOwningThread();
747
0
748
0
  if (QuotaManager::IsShuttingDown()) {
749
0
    IDB_REPORT_INTERNAL_ERR();
750
0
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
751
0
    return nullptr;
752
0
  }
753
0
754
0
  if (mClosed || mFileHandleDisabled) {
755
0
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
756
0
    return nullptr;
757
0
  }
758
0
759
0
  nsString type;
760
0
  if (aType.WasPassed()) {
761
0
    type = aType.Value();
762
0
  }
763
0
764
0
  CreateFileParams params(nsString(aName), type);
765
0
766
0
  RefPtr<IDBRequest> request = IDBRequest::Create(aCx, this, nullptr);
767
0
  MOZ_ASSERT(request);
768
0
769
0
  BackgroundDatabaseRequestChild* actor =
770
0
    new BackgroundDatabaseRequestChild(this, request);
771
0
772
0
  IDB_LOG_MARK("IndexedDB %s: Child  Request[%llu]: "
773
0
                 "database(%s).createMutableFile(%s)",
774
0
               "IndexedDB %s: C R[%llu]: IDBDatabase.createMutableFile()",
775
0
               IDB_LOG_ID_STRING(),
776
0
               request->LoggingSerialNumber(),
777
0
               IDB_LOG_STRINGIFY(this),
778
0
               NS_ConvertUTF16toUTF8(aName).get());
779
0
780
0
  mBackgroundActor->SendPBackgroundIDBDatabaseRequestConstructor(actor, params);
781
0
782
0
  MOZ_ASSERT(actor->GetActorEventTarget(),
783
0
    "The event target shall be inherited from its manager actor.");
784
0
785
0
  return request.forget();
786
0
}
787
788
void
789
IDBDatabase::RegisterTransaction(IDBTransaction* aTransaction)
790
0
{
791
0
  AssertIsOnOwningThread();
792
0
  MOZ_ASSERT(aTransaction);
793
0
  aTransaction->AssertIsOnOwningThread();
794
0
  MOZ_ASSERT(!mTransactions.Contains(aTransaction));
795
0
796
0
  mTransactions.PutEntry(aTransaction);
797
0
}
798
799
void
800
IDBDatabase::UnregisterTransaction(IDBTransaction* aTransaction)
801
0
{
802
0
  AssertIsOnOwningThread();
803
0
  MOZ_ASSERT(aTransaction);
804
0
  aTransaction->AssertIsOnOwningThread();
805
0
  MOZ_ASSERT(mTransactions.Contains(aTransaction));
806
0
807
0
  mTransactions.RemoveEntry(aTransaction);
808
0
}
809
810
void
811
IDBDatabase::AbortTransactions(bool aShouldWarn)
812
0
{
813
0
  AssertIsOnOwningThread();
814
0
815
0
  class MOZ_STACK_CLASS Helper final
816
0
  {
817
0
    typedef AutoTArray<RefPtr<IDBTransaction>, 20> StrongTransactionArray;
818
0
    typedef AutoTArray<IDBTransaction*, 20> WeakTransactionArray;
819
0
820
0
  public:
821
0
    static void
822
0
    AbortTransactions(IDBDatabase* aDatabase, const bool aShouldWarn)
823
0
    {
824
0
      MOZ_ASSERT(aDatabase);
825
0
      aDatabase->AssertIsOnOwningThread();
826
0
827
0
      nsTHashtable<nsPtrHashKey<IDBTransaction>>& transactionTable =
828
0
        aDatabase->mTransactions;
829
0
830
0
      if (!transactionTable.Count()) {
831
0
        return;
832
0
      }
833
0
834
0
      StrongTransactionArray transactionsToAbort;
835
0
      transactionsToAbort.SetCapacity(transactionTable.Count());
836
0
837
0
      for (auto iter = transactionTable.Iter(); !iter.Done(); iter.Next()) {
838
0
        IDBTransaction* transaction = iter.Get()->GetKey();
839
0
        MOZ_ASSERT(transaction);
840
0
841
0
        transaction->AssertIsOnOwningThread();
842
0
843
0
        // Transactions that are already done can simply be ignored. Otherwise
844
0
        // there is a race here and it's possible that the transaction has not
845
0
        // been successfully committed yet so we will warn the user.
846
0
        if (!transaction->IsDone()) {
847
0
          transactionsToAbort.AppendElement(transaction);
848
0
        }
849
0
      }
850
0
      MOZ_ASSERT(transactionsToAbort.Length() <= transactionTable.Count());
851
0
852
0
      if (transactionsToAbort.IsEmpty()) {
853
0
        return;
854
0
      }
855
0
856
0
      // We want to abort transactions as soon as possible so we iterate the
857
0
      // transactions once and abort them all first, collecting the transactions
858
0
      // that need to have a warning issued along the way. Those that need a
859
0
      // warning will be a subset of those that are aborted, so we don't need
860
0
      // additional strong references here.
861
0
      WeakTransactionArray transactionsThatNeedWarning;
862
0
863
0
      for (RefPtr<IDBTransaction>& transaction : transactionsToAbort) {
864
0
        MOZ_ASSERT(transaction);
865
0
        MOZ_ASSERT(!transaction->IsDone());
866
0
867
0
        if (aShouldWarn) {
868
0
          switch (transaction->GetMode()) {
869
0
            // We ignore transactions that could not have written any data.
870
0
            case IDBTransaction::READ_ONLY:
871
0
              break;
872
0
873
0
            // We warn for any transactions that could have written data.
874
0
            case IDBTransaction::READ_WRITE:
875
0
            case IDBTransaction::READ_WRITE_FLUSH:
876
0
            case IDBTransaction::CLEANUP:
877
0
            case IDBTransaction::VERSION_CHANGE:
878
0
              transactionsThatNeedWarning.AppendElement(transaction);
879
0
              break;
880
0
881
0
            default:
882
0
              MOZ_CRASH("Unknown mode!");
883
0
          }
884
0
        }
885
0
886
0
        transaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
887
0
      }
888
0
889
0
      static const char kWarningMessage[] =
890
0
        "IndexedDBTransactionAbortNavigation";
891
0
892
0
      for (IDBTransaction* transaction : transactionsThatNeedWarning) {
893
0
        MOZ_ASSERT(transaction);
894
0
895
0
        nsString filename;
896
0
        uint32_t lineNo, column;
897
0
        transaction->GetCallerLocation(filename, &lineNo, &column);
898
0
899
0
        aDatabase->LogWarning(kWarningMessage, filename, lineNo, column);
900
0
      }
901
0
    }
902
0
  };
903
0
904
0
  Helper::AbortTransactions(this, aShouldWarn);
905
0
}
906
907
PBackgroundIDBDatabaseFileChild*
908
IDBDatabase::GetOrCreateFileActorForBlob(Blob* aBlob)
909
0
{
910
0
  AssertIsOnOwningThread();
911
0
  MOZ_ASSERT(aBlob);
912
0
  MOZ_ASSERT(mBackgroundActor);
913
0
914
0
  // We use the File's nsIWeakReference as the key to the table because
915
0
  // a) it is unique per blob, b) it is reference-counted so that we can
916
0
  // guarantee that it stays alive, and c) it doesn't hold the actual File
917
0
  // alive.
918
0
  nsCOMPtr<nsIWeakReference> weakRef = do_GetWeakReference(aBlob);
919
0
  MOZ_ASSERT(weakRef);
920
0
921
0
  PBackgroundIDBDatabaseFileChild* actor = nullptr;
922
0
923
0
  if (!mFileActors.Get(weakRef, &actor)) {
924
0
    BlobImpl* blobImpl = aBlob->Impl();
925
0
    MOZ_ASSERT(blobImpl);
926
0
927
0
    PBackgroundChild* backgroundManager =
928
0
      mBackgroundActor->Manager()->Manager();
929
0
    MOZ_ASSERT(backgroundManager);
930
0
931
0
    IPCBlob ipcBlob;
932
0
    nsresult rv = IPCBlobUtils::Serialize(blobImpl, backgroundManager, ipcBlob);
933
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
934
0
      return nullptr;
935
0
    }
936
0
937
0
    auto* dbFile = new DatabaseFile(this);
938
0
939
0
    actor =
940
0
      mBackgroundActor->SendPBackgroundIDBDatabaseFileConstructor(dbFile,
941
0
                                                                  ipcBlob);
942
0
    if (NS_WARN_IF(!actor)) {
943
0
      return nullptr;
944
0
    }
945
0
946
0
    MOZ_ASSERT(actor->GetActorEventTarget(),
947
0
     "The event target shall be inherited from its manager actor.");
948
0
    mFileActors.Put(weakRef, actor);
949
0
  }
950
0
951
0
  MOZ_ASSERT(actor);
952
0
953
0
  return actor;
954
0
}
955
956
void
957
IDBDatabase::NoteFinishedFileActor(PBackgroundIDBDatabaseFileChild* aFileActor)
958
0
{
959
0
  AssertIsOnOwningThread();
960
0
  MOZ_ASSERT(aFileActor);
961
0
962
0
  for (auto iter = mFileActors.Iter(); !iter.Done(); iter.Next()) {
963
0
    MOZ_ASSERT(iter.Key());
964
0
    PBackgroundIDBDatabaseFileChild* actor = iter.Data();
965
0
    MOZ_ASSERT(actor);
966
0
967
0
    if (actor == aFileActor) {
968
0
      iter.Remove();
969
0
    }
970
0
  }
971
0
}
972
973
void
974
IDBDatabase::NoteActiveTransaction()
975
0
{
976
0
  AssertIsOnOwningThread();
977
0
  MOZ_ASSERT(mFactory);
978
0
979
0
  // Increase the number of active transactions.
980
0
  mFactory->UpdateActiveTransactionCount(1);
981
0
}
982
983
void
984
IDBDatabase::NoteInactiveTransaction()
985
0
{
986
0
  AssertIsOnOwningThread();
987
0
988
0
  if (!mBackgroundActor || !mFileActors.Count()) {
989
0
    MOZ_ASSERT(mFactory);
990
0
    mFactory->UpdateActiveTransactionCount(-1);
991
0
    return;
992
0
  }
993
0
994
0
  RefPtr<Runnable> runnable =
995
0
    NewRunnableMethod("IDBDatabase::NoteInactiveTransactionDelayed",
996
0
                      this,
997
0
                      &IDBDatabase::NoteInactiveTransactionDelayed);
998
0
  MOZ_ASSERT(runnable);
999
0
1000
0
  if (!NS_IsMainThread()) {
1001
0
    // Wrap as a nsICancelableRunnable to make workers happy.
1002
0
    RefPtr<Runnable> cancelable = new CancelableRunnableWrapper(runnable);
1003
0
    cancelable.swap(runnable);
1004
0
  }
1005
0
1006
0
  MOZ_ALWAYS_SUCCEEDS(
1007
0
    EventTarget()->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL));
1008
0
}
1009
1010
nsresult
1011
IDBDatabase::GetQuotaInfo(nsACString& aOrigin,
1012
                          PersistenceType* aPersistenceType)
1013
0
{
1014
0
  using mozilla::dom::quota::QuotaManager;
1015
0
1016
0
  MOZ_ASSERT(NS_IsMainThread(), "This can't work off the main thread!");
1017
0
1018
0
  if (aPersistenceType) {
1019
0
    *aPersistenceType = mSpec->metadata().persistenceType();
1020
0
    MOZ_ASSERT(*aPersistenceType != PERSISTENCE_TYPE_INVALID);
1021
0
  }
1022
0
1023
0
  PrincipalInfo* principalInfo = mFactory->GetPrincipalInfo();
1024
0
  MOZ_ASSERT(principalInfo);
1025
0
1026
0
  switch (principalInfo->type()) {
1027
0
    case PrincipalInfo::TNullPrincipalInfo:
1028
0
      MOZ_CRASH("Is this needed?!");
1029
0
1030
0
    case PrincipalInfo::TSystemPrincipalInfo:
1031
0
      QuotaManager::GetInfoForChrome(nullptr, nullptr, &aOrigin);
1032
0
      return NS_OK;
1033
0
1034
0
    case PrincipalInfo::TContentPrincipalInfo: {
1035
0
      nsresult rv;
1036
0
      nsCOMPtr<nsIPrincipal> principal =
1037
0
        PrincipalInfoToPrincipal(*principalInfo, &rv);
1038
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
1039
0
        return rv;
1040
0
      }
1041
0
1042
0
      rv = QuotaManager::GetInfoFromPrincipal(principal,
1043
0
                                              nullptr,
1044
0
                                              nullptr,
1045
0
                                              &aOrigin);
1046
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
1047
0
        return rv;
1048
0
      }
1049
0
1050
0
      return NS_OK;
1051
0
    }
1052
0
1053
0
    default:
1054
0
      MOZ_CRASH("Unknown PrincipalInfo type!");
1055
0
  }
1056
0
1057
0
  MOZ_CRASH("Should never get here!");
1058
0
}
1059
1060
void
1061
IDBDatabase::ExpireFileActors(bool aExpireAll)
1062
0
{
1063
0
  AssertIsOnOwningThread();
1064
0
1065
0
  if (mBackgroundActor && mFileActors.Count()) {
1066
0
    for (auto iter = mFileActors.Iter(); !iter.Done(); iter.Next()) {
1067
0
      nsISupports* key = iter.Key();
1068
0
      PBackgroundIDBDatabaseFileChild* actor = iter.Data();
1069
0
      MOZ_ASSERT(key);
1070
0
      MOZ_ASSERT(actor);
1071
0
1072
0
      bool shouldExpire = aExpireAll;
1073
0
      if (!shouldExpire) {
1074
0
        nsCOMPtr<nsIWeakReference> weakRef = do_QueryInterface(key);
1075
0
        MOZ_ASSERT(weakRef);
1076
0
1077
0
        nsCOMPtr<nsISupports> referent = do_QueryReferent(weakRef);
1078
0
        shouldExpire = !referent;
1079
0
      }
1080
0
1081
0
      if (shouldExpire) {
1082
0
        PBackgroundIDBDatabaseFileChild::Send__delete__(actor);
1083
0
1084
0
        if (!aExpireAll) {
1085
0
          iter.Remove();
1086
0
        }
1087
0
      }
1088
0
    }
1089
0
    if (aExpireAll) {
1090
0
      mFileActors.Clear();
1091
0
    }
1092
0
  } else {
1093
0
    MOZ_ASSERT(!mFileActors.Count());
1094
0
  }
1095
0
}
1096
1097
void
1098
IDBDatabase::NoteLiveMutableFile(IDBMutableFile* aMutableFile)
1099
0
{
1100
0
  AssertIsOnOwningThread();
1101
0
  MOZ_ASSERT(aMutableFile);
1102
0
  aMutableFile->AssertIsOnOwningThread();
1103
0
  MOZ_ASSERT(!mLiveMutableFiles.Contains(aMutableFile));
1104
0
1105
0
  mLiveMutableFiles.AppendElement(aMutableFile);
1106
0
}
1107
1108
void
1109
IDBDatabase::NoteFinishedMutableFile(IDBMutableFile* aMutableFile)
1110
0
{
1111
0
  AssertIsOnOwningThread();
1112
0
  MOZ_ASSERT(aMutableFile);
1113
0
  aMutableFile->AssertIsOnOwningThread();
1114
0
1115
0
  // It's ok if this is called after we cleared the array, so don't assert that
1116
0
  // aMutableFile is in the list.
1117
0
1118
0
  mLiveMutableFiles.RemoveElement(aMutableFile);
1119
0
}
1120
1121
void
1122
IDBDatabase::InvalidateMutableFiles()
1123
0
{
1124
0
  AssertIsOnOwningThread();
1125
0
1126
0
  if (!mLiveMutableFiles.IsEmpty()) {
1127
0
    for (uint32_t count = mLiveMutableFiles.Length(), index = 0;
1128
0
         index < count;
1129
0
         index++) {
1130
0
      mLiveMutableFiles[index]->Invalidate();
1131
0
    }
1132
0
1133
0
    mLiveMutableFiles.Clear();
1134
0
  }
1135
0
}
1136
1137
void
1138
IDBDatabase::Invalidate()
1139
0
{
1140
0
  AssertIsOnOwningThread();
1141
0
1142
0
  if (!mInvalidated) {
1143
0
    mInvalidated = true;
1144
0
1145
0
    InvalidateInternal();
1146
0
  }
1147
0
}
1148
1149
void
1150
IDBDatabase::NoteInactiveTransactionDelayed()
1151
0
{
1152
0
  ExpireFileActors(/* aExpireAll */ false);
1153
0
1154
0
  MOZ_ASSERT(mFactory);
1155
0
  mFactory->UpdateActiveTransactionCount(-1);
1156
0
}
1157
1158
void
1159
IDBDatabase::LogWarning(const char* aMessageName,
1160
                        const nsAString& aFilename,
1161
                        uint32_t aLineNumber,
1162
                        uint32_t aColumnNumber)
1163
0
{
1164
0
  AssertIsOnOwningThread();
1165
0
  MOZ_ASSERT(aMessageName);
1166
0
1167
0
  ScriptErrorHelper::DumpLocalizedMessage(nsDependentCString(aMessageName),
1168
0
                                          aFilename,
1169
0
                                          aLineNumber,
1170
0
                                          aColumnNumber,
1171
0
                                          nsIScriptError::warningFlag,
1172
0
                                          mFactory->IsChrome(),
1173
0
                                          mFactory->InnerWindowID());
1174
0
}
1175
1176
NS_IMPL_ADDREF_INHERITED(IDBDatabase, IDBWrapperCache)
1177
NS_IMPL_RELEASE_INHERITED(IDBDatabase, IDBWrapperCache)
1178
1179
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBDatabase)
1180
0
NS_INTERFACE_MAP_END_INHERITING(IDBWrapperCache)
1181
1182
NS_IMPL_CYCLE_COLLECTION_CLASS(IDBDatabase)
1183
1184
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBDatabase, IDBWrapperCache)
1185
0
  tmp->AssertIsOnOwningThread();
1186
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFactory)
1187
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1188
1189
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBDatabase, IDBWrapperCache)
1190
0
  tmp->AssertIsOnOwningThread();
1191
0
1192
0
  // Don't unlink mFactory!
1193
0
1194
0
  // We've been unlinked, at the very least we should be able to prevent further
1195
0
  // transactions from starting and unblock any other SetVersion callers.
1196
0
  tmp->CloseInternal();
1197
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1198
1199
void
1200
IDBDatabase::DisconnectFromOwner()
1201
0
{
1202
0
  InvalidateInternal();
1203
0
  IDBWrapperCache::DisconnectFromOwner();
1204
0
}
1205
1206
void
1207
IDBDatabase::LastRelease()
1208
0
{
1209
0
  AssertIsOnOwningThread();
1210
0
1211
0
  CloseInternal();
1212
0
1213
0
  if (mBackgroundActor) {
1214
0
    mBackgroundActor->SendDeleteMeInternal();
1215
0
    MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!");
1216
0
  }
1217
0
}
1218
1219
nsresult
1220
IDBDatabase::PostHandleEvent(EventChainPostVisitor& aVisitor)
1221
0
{
1222
0
  nsresult rv =
1223
0
    IndexedDatabaseManager::CommonPostHandleEvent(aVisitor, mFactory);
1224
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1225
0
    return rv;
1226
0
  }
1227
0
1228
0
  return NS_OK;
1229
0
}
1230
1231
JSObject*
1232
IDBDatabase::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
1233
0
{
1234
0
  return IDBDatabase_Binding::Wrap(aCx, this, aGivenProto);
1235
0
}
1236
1237
NS_IMETHODIMP
1238
CancelableRunnableWrapper::Run()
1239
0
{
1240
0
  nsCOMPtr<nsIRunnable> runnable;
1241
0
  mRunnable.swap(runnable);
1242
0
1243
0
  if (runnable) {
1244
0
    return runnable->Run();
1245
0
  }
1246
0
1247
0
  return NS_OK;
1248
0
}
1249
1250
nsresult
1251
CancelableRunnableWrapper::Cancel()
1252
0
{
1253
0
  if (mRunnable) {
1254
0
    mRunnable = nullptr;
1255
0
    return NS_OK;
1256
0
  }
1257
0
1258
0
  return NS_ERROR_UNEXPECTED;
1259
0
}
1260
1261
NS_IMPL_ISUPPORTS(IDBDatabase::Observer, nsIObserver)
1262
1263
NS_IMETHODIMP
1264
IDBDatabase::
1265
Observer::Observe(nsISupports* aSubject,
1266
                  const char* aTopic,
1267
                  const char16_t* aData)
1268
0
{
1269
0
  MOZ_ASSERT(NS_IsMainThread());
1270
0
  MOZ_ASSERT(aTopic);
1271
0
1272
0
  if (!strcmp(aTopic, kWindowObserverTopic)) {
1273
0
    if (mWeakDatabase) {
1274
0
      nsCOMPtr<nsISupportsPRUint64> supportsInt = do_QueryInterface(aSubject);
1275
0
      MOZ_ASSERT(supportsInt);
1276
0
1277
0
      uint64_t windowId;
1278
0
      MOZ_ALWAYS_SUCCEEDS(supportsInt->GetData(&windowId));
1279
0
1280
0
      if (windowId == mWindowId) {
1281
0
        RefPtr<IDBDatabase> database = mWeakDatabase;
1282
0
        mWeakDatabase = nullptr;
1283
0
1284
0
        database->InvalidateInternal();
1285
0
      }
1286
0
    }
1287
0
1288
0
    return NS_OK;
1289
0
  }
1290
0
1291
0
  if (!strcmp(aTopic, kCycleCollectionObserverTopic) ||
1292
0
      !strcmp(aTopic, kMemoryPressureObserverTopic)) {
1293
0
    if (mWeakDatabase) {
1294
0
      RefPtr<IDBDatabase> database = mWeakDatabase;
1295
0
1296
0
      database->ExpireFileActors(/* aExpireAll */ false);
1297
0
    }
1298
0
1299
0
    return NS_OK;
1300
0
  }
1301
0
1302
0
  NS_WARNING("Unknown observer topic!");
1303
0
  return NS_OK;
1304
0
}
1305
1306
nsresult
1307
IDBDatabase::RenameObjectStore(int64_t aObjectStoreId, const nsAString& aName)
1308
0
{
1309
0
  MOZ_ASSERT(mSpec);
1310
0
1311
0
  nsTArray<ObjectStoreSpec>& objectStores = mSpec->objectStores();
1312
0
1313
0
  ObjectStoreSpec* foundObjectStoreSpec = nullptr;
1314
0
  // Find the matched object store spec and check if 'aName' is already used by
1315
0
  // another object store.
1316
0
  for (uint32_t objCount = objectStores.Length(), objIndex = 0;
1317
0
       objIndex < objCount;
1318
0
       objIndex++) {
1319
0
    const ObjectStoreSpec& objSpec = objectStores[objIndex];
1320
0
    if (objSpec.metadata().id() == aObjectStoreId) {
1321
0
      MOZ_ASSERT(!foundObjectStoreSpec);
1322
0
      foundObjectStoreSpec = &objectStores[objIndex];
1323
0
      continue;
1324
0
    }
1325
0
    if (aName == objSpec.metadata().name()) {
1326
0
      return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR;
1327
0
    }
1328
0
  }
1329
0
1330
0
  MOZ_ASSERT(foundObjectStoreSpec);
1331
0
1332
0
  // Update the name of the matched object store.
1333
0
  foundObjectStoreSpec->metadata().name() = nsString(aName);
1334
0
1335
0
  return NS_OK;
1336
0
}
1337
1338
nsresult
1339
IDBDatabase::RenameIndex(int64_t aObjectStoreId,
1340
                         int64_t aIndexId,
1341
                         const nsAString& aName)
1342
0
{
1343
0
  MOZ_ASSERT(mSpec);
1344
0
1345
0
  nsTArray<ObjectStoreSpec>& objectStores = mSpec->objectStores();
1346
0
1347
0
  ObjectStoreSpec* foundObjectStoreSpec = nullptr;
1348
0
  // Find the matched index metadata and check if 'aName' is already used by
1349
0
  // another index.
1350
0
  for (uint32_t objCount = objectStores.Length(), objIndex = 0;
1351
0
       objIndex < objCount;
1352
0
       objIndex++) {
1353
0
    const ObjectStoreSpec& objSpec = objectStores[objIndex];
1354
0
    if (objSpec.metadata().id() == aObjectStoreId) {
1355
0
      foundObjectStoreSpec = &objectStores[objIndex];
1356
0
      break;
1357
0
    }
1358
0
  }
1359
0
1360
0
  MOZ_ASSERT(foundObjectStoreSpec);
1361
0
1362
0
  nsTArray<IndexMetadata>& indexes = foundObjectStoreSpec->indexes();
1363
0
  IndexMetadata* foundIndexMetadata = nullptr;
1364
0
  for (uint32_t idxCount = indexes.Length(), idxIndex = 0;
1365
0
       idxIndex < idxCount;
1366
0
       idxIndex++) {
1367
0
    const IndexMetadata& metadata = indexes[idxIndex];
1368
0
    if (metadata.id() == aIndexId) {
1369
0
      MOZ_ASSERT(!foundIndexMetadata);
1370
0
      foundIndexMetadata = &indexes[idxIndex];
1371
0
      continue;
1372
0
    }
1373
0
    if (aName == metadata.name()) {
1374
0
      return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR;
1375
0
    }
1376
0
  }
1377
0
1378
0
  MOZ_ASSERT(foundIndexMetadata);
1379
0
1380
0
  // Update the name of the matched object store.
1381
0
  foundIndexMetadata->name() = nsString(aName);
1382
0
1383
0
  return NS_OK;
1384
0
}
1385
1386
void
1387
IDBDatabase::IncreaseActiveDatabaseCount()
1388
0
{
1389
0
  AssertIsOnOwningThread();
1390
0
  MOZ_ASSERT(mFactory);
1391
0
  MOZ_ASSERT(!mIncreasedActiveDatabaseCount);
1392
0
1393
0
  mFactory->UpdateActiveDatabaseCount(1);
1394
0
  mIncreasedActiveDatabaseCount = true;
1395
0
}
1396
1397
void
1398
IDBDatabase::MaybeDecreaseActiveDatabaseCount()
1399
0
{
1400
0
  AssertIsOnOwningThread();
1401
0
1402
0
  if (mIncreasedActiveDatabaseCount) {
1403
0
    // Decrease the number of active databases.
1404
0
    MOZ_ASSERT(mFactory);
1405
0
    mFactory->UpdateActiveDatabaseCount(-1);
1406
0
    mIncreasedActiveDatabaseCount = false;
1407
0
  }
1408
0
}
1409
1410
} // namespace dom
1411
} // namespace mozilla