IndexedDatabaseManager.cpp 29.4 KB
Newer Older
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
4
5
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
8

#include "IndexedDatabaseManager.h"

9
#include "chrome/common/ipc_channel.h"  // for IPC::Channel::kMaximumMessageSize
10
#include "nsIConsoleService.h"
11
#include "nsIScriptError.h"
Ben Turner's avatar
Ben Turner committed
12
#include "nsIScriptGlobalObject.h"
13

14
#include "jsapi.h"
15
#include "mozilla/ClearOnShutdown.h"
16
#include "mozilla/ContentEvents.h"
17
18
#include "mozilla/EventDispatcher.h"
#include "mozilla/Preferences.h"
19
#include "mozilla/dom/DOMException.h"
20
#include "mozilla/dom/ErrorEvent.h"
21
#include "mozilla/dom/ErrorEventBinding.h"
22
23
#include "mozilla/dom/WorkerScope.h"
#include "mozilla/dom/WorkerPrivate.h"
24
25
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/PBackgroundChild.h"
26
#include "nsContentUtils.h"
27
#include "nsGlobalWindow.h"
28
#include "mozilla/Logging.h"
29

30
#include "FileManager.h"
31
#include "IDBEvents.h"
32
#include "IDBFactory.h"
33
#include "IDBKeyRange.h"
34
#include "IDBRequest.h"
35
#include "ProfilerHelpers.h"
36
#include "ScriptErrorHelper.h"
37
38
#include "nsCharSeparatedTokenizer.h"
#include "unicode/locid.h"
39

40
41
42
43
44
// Bindings for ResolveConstructors
#include "mozilla/dom/IDBCursorBinding.h"
#include "mozilla/dom/IDBDatabaseBinding.h"
#include "mozilla/dom/IDBFactoryBinding.h"
#include "mozilla/dom/IDBIndexBinding.h"
45
46
#include "mozilla/dom/IDBKeyRangeBinding.h"
#include "mozilla/dom/IDBMutableFileBinding.h"
47
48
49
50
51
52
#include "mozilla/dom/IDBObjectStoreBinding.h"
#include "mozilla/dom/IDBOpenDBRequestBinding.h"
#include "mozilla/dom/IDBRequestBinding.h"
#include "mozilla/dom/IDBTransactionBinding.h"
#include "mozilla/dom/IDBVersionChangeEventBinding.h"

53
54
#define IDB_STR "indexedDB"

55
56
57
namespace mozilla {
namespace dom {
namespace indexedDB {
58

59
using namespace mozilla::dom::quota;
60
using namespace mozilla::ipc;
61

62
63
64
65
class FileManagerInfo {
 public:
  already_AddRefed<FileManager> GetFileManager(PersistenceType aPersistenceType,
                                               const nsAString& aName) const;
66

67
  void AddFileManager(FileManager* aFileManager);
68

69
  bool HasFileManagers() const {
70
71
72
    AssertIsOnIOThread();

    return !mPersistentStorageFileManagers.IsEmpty() ||
73
74
           !mTemporaryStorageFileManagers.IsEmpty() ||
           !mDefaultStorageFileManagers.IsEmpty();
75
76
  }

77
  void InvalidateAllFileManagers() const;
78

79
  void InvalidateAndRemoveFileManagers(PersistenceType aPersistenceType);
80

81
82
  void InvalidateAndRemoveFileManager(PersistenceType aPersistenceType,
                                      const nsAString& aName);
83

84
85
 private:
  nsTArray<RefPtr<FileManager> >& GetArray(PersistenceType aPersistenceType);
86

87
88
  const nsTArray<RefPtr<FileManager> >& GetImmutableArray(
      PersistenceType aPersistenceType) const {
89
90
91
    return const_cast<FileManagerInfo*>(this)->GetArray(aPersistenceType);
  }

92
93
94
  nsTArray<RefPtr<FileManager> > mPersistentStorageFileManagers;
  nsTArray<RefPtr<FileManager> > mTemporaryStorageFileManagers;
  nsTArray<RefPtr<FileManager> > mDefaultStorageFileManagers;
95
96
};

97
}  // namespace indexedDB
98
99
100

using namespace mozilla::dom::indexedDB;

101
namespace {
102

103
NS_DEFINE_IID(kIDBPrivateRequestIID, PRIVATE_IDBREQUEST_IID);
Ben Turner's avatar
Ben Turner committed
104

105
106
const uint32_t kDeleteTimeoutMs = 1000;

107
108
109
// The threshold we use for structured clone data storing.
// Anything smaller than the threshold is compressed and stored in the database.
// Anything larger is compressed and stored outside the database.
110
const int32_t kDefaultDataThresholdBytes = 1024 * 1024;  // 1MB
111

112
113
114
// The maximal size of a serialized object to be transfered through IPC.
const int32_t kDefaultMaxSerializedMsgSize = IPC::Channel::kMaximumMessageSize;

115
116
117
118
119
120
121
// The maximum number of records to preload (in addition to the one requested by
// the child).
//
// TODO: The current number was chosen for no particular reason. Telemetry
// should be added to determine whether this is a reasonable number for an
// overwhelming majority of cases.
const int32_t kDefaultMaxPreloadExtraRecords = 64;
122

123
124
125
#define IDB_PREF_BRANCH_ROOT "dom.indexedDB."

const char kTestingPref[] = IDB_PREF_BRANCH_ROOT "testing";
126
const char kPrefExperimental[] = IDB_PREF_BRANCH_ROOT "experimental";
127
const char kPrefFileHandle[] = "dom.fileHandle.enabled";
128
const char kDataThresholdPref[] = IDB_PREF_BRANCH_ROOT "dataThreshold";
129
130
131
132
const char kPrefMaxSerilizedMsgSize[] =
    IDB_PREF_BRANCH_ROOT "maxSerializedMsgSize";
const char kPrefErrorEventToSelfError[] =
    IDB_PREF_BRANCH_ROOT "errorEventToSelfError";
133
const char kPreprocessingPref[] = IDB_PREF_BRANCH_ROOT "preprocessing";
134
135
const char kPrefMaxPreloadExtraRecords[] =
    IDB_PREF_BRANCH_ROOT "maxPreloadExtraRecords";
136
137
138
139
140
141

#define IDB_PREF_LOGGING_BRANCH_ROOT IDB_PREF_BRANCH_ROOT "logging."

const char kPrefLoggingEnabled[] = IDB_PREF_LOGGING_BRANCH_ROOT "enabled";
const char kPrefLoggingDetails[] = IDB_PREF_LOGGING_BRANCH_ROOT "details";

142
#if defined(DEBUG) || defined(MOZ_GECKO_PROFILER)
143
const char kPrefLoggingProfiler[] =
144
    IDB_PREF_LOGGING_BRANCH_ROOT "profiler-marks";
145
146
147
148
#endif

#undef IDB_PREF_LOGGING_BRANCH_ROOT
#undef IDB_PREF_BRANCH_ROOT
149

150
StaticRefPtr<IndexedDatabaseManager> gDBManager;
151

152
153
154
155
Atomic<bool> gInitialized(false);
Atomic<bool> gClosed(false);
Atomic<bool> gTestingMode(false);
Atomic<bool> gExperimentalFeaturesEnabled(false);
156
Atomic<bool> gFileHandleEnabled(false);
157
Atomic<bool> gPrefErrorEventToSelfError(false);
158
Atomic<int32_t> gDataThresholdBytes(0);
159
Atomic<int32_t> gMaxSerializedMsgSize(0);
160
Atomic<bool> gPreprocessingEnabled(false);
161
Atomic<int32_t> gMaxPreloadExtraRecords(0);
162

163
void AtomicBoolPrefChangedCallback(const char* aPrefName, void* aBool) {
164
  MOZ_ASSERT(NS_IsMainThread());
165
  MOZ_ASSERT(aBool);
166

167
  *static_cast<Atomic<bool>*>(aBool) = Preferences::GetBool(aPrefName);
168
169
}

170
void DataThresholdPrefChangedCallback(const char* aPrefName, void* aClosure) {
171
172
173
174
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(!strcmp(aPrefName, kDataThresholdPref));
  MOZ_ASSERT(!aClosure);

175
  int32_t dataThresholdBytes =
176
      Preferences::GetInt(aPrefName, kDefaultDataThresholdBytes);
177
178
179
180
181
182
183

  // The magic -1 is for use only by tests that depend on stable blob file id's.
  if (dataThresholdBytes == -1) {
    dataThresholdBytes = INT32_MAX;
  }

  gDataThresholdBytes = dataThresholdBytes;
184
185
}

186
187
void MaxSerializedMsgSizePrefChangeCallback(const char* aPrefName,
                                            void* aClosure) {
188
189
190
191
192
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(!strcmp(aPrefName, kPrefMaxSerilizedMsgSize));
  MOZ_ASSERT(!aClosure);

  gMaxSerializedMsgSize =
193
      Preferences::GetInt(aPrefName, kDefaultMaxSerializedMsgSize);
194
195
196
  MOZ_ASSERT(gMaxSerializedMsgSize > 0);
}

197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
void MaxPreloadExtraRecordsPrefChangeCallback(const char* aPrefName,
                                              void* aClosure) {
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(!strcmp(aPrefName, kPrefMaxPreloadExtraRecords));
  MOZ_ASSERT(!aClosure);

  gMaxPreloadExtraRecords =
      Preferences::GetInt(aPrefName, kDefaultMaxPreloadExtraRecords);
  MOZ_ASSERT(gMaxPreloadExtraRecords >= 0);

  // TODO: We could also allow setting a negative value to preload all available
  // records, but this doesn't seem to be too useful in general, and it would
  // require adaptations in ActorsParent.cpp
}

212
213
214
215
216
217
218
auto DatabaseNameMatchPredicate(const nsAString* const aName) {
  MOZ_ASSERT(aName);
  return [aName](const auto& fileManager) {
    return fileManager->DatabaseName() == *aName;
  };
}

219
}  // namespace
220
221

IndexedDatabaseManager::IndexedDatabaseManager()
222
223
    : mFileMutex("IndexedDatabaseManager.mFileMutex"),
      mBackgroundActor(nullptr) {
224
225
226
  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
}

227
IndexedDatabaseManager::~IndexedDatabaseManager() {
228
  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
229
230
231
232
233

  if (mBackgroundActor) {
    mBackgroundActor->SendDeleteMeInternal();
    MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!");
  }
234
235
}

236
bool IndexedDatabaseManager::sIsMainProcess = false;
237
bool IndexedDatabaseManager::sFullSynchronousMode = false;
238

239
mozilla::LazyLogModule IndexedDatabaseManager::sLoggingModule("IndexedDB");
240
241

Atomic<IndexedDatabaseManager::LoggingMode>
242
243
    IndexedDatabaseManager::sLoggingMode(
        IndexedDatabaseManager::Logging_Disabled);
244

245
// static
246
IndexedDatabaseManager* IndexedDatabaseManager::GetOrCreate() {
247
248
  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");

249
250
  if (IsClosed()) {
    NS_ERROR("Calling GetOrCreate() after shutdown!");
251
    return nullptr;
252
253
  }

254
  if (!gDBManager) {
255
    sIsMainProcess = XRE_IsParentProcess();
Ben Turner's avatar
Ben Turner committed
256

257
    RefPtr<IndexedDatabaseManager> instance(new IndexedDatabaseManager());
258

259
    nsresult rv = instance->Init();
260
    NS_ENSURE_SUCCESS(rv, nullptr);
261

262
    if (gInitialized.exchange(true)) {
263
      NS_ERROR("Initialized more than once?!");
264
    }
Ben Turner's avatar
Ben Turner committed
265

266
    gDBManager = instance;
267

268
    ClearOnShutdown(&gDBManager);
269
270
  }

271
  return gDBManager;
272
273
}

274
// static
275
IndexedDatabaseManager* IndexedDatabaseManager::Get() {
276
  // Does not return an owning reference.
277
  return gDBManager;
278
279
}

280
nsresult IndexedDatabaseManager::Init() {
281
  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
Ben Turner's avatar
Ben Turner committed
282

283
  Preferences::RegisterCallbackAndCall(AtomicBoolPrefChangedCallback,
284
                                       kTestingPref, &gTestingMode);
285
286
287
  Preferences::RegisterCallbackAndCall(AtomicBoolPrefChangedCallback,
                                       kPrefExperimental,
                                       &gExperimentalFeaturesEnabled);
288
  Preferences::RegisterCallbackAndCall(AtomicBoolPrefChangedCallback,
289
                                       kPrefFileHandle, &gFileHandleEnabled);
290
291
292
  Preferences::RegisterCallbackAndCall(AtomicBoolPrefChangedCallback,
                                       kPrefErrorEventToSelfError,
                                       &gPrefErrorEventToSelfError);
293

294
295
296
297
298
299
300
301
  // By default IndexedDB uses SQLite with PRAGMA synchronous = NORMAL. This
  // guarantees (unlike synchronous = OFF) atomicity and consistency, but not
  // necessarily durability in situations such as power loss. This preference
  // allows enabling PRAGMA synchronous = FULL on SQLite, which does guarantee
  // durability, but with an extra fsync() and the corresponding performance
  // hit.
  sFullSynchronousMode = Preferences::GetBool("dom.indexedDB.fullSynchronous");

302
303
  Preferences::RegisterCallback(LoggingModePrefChangedCallback,
                                kPrefLoggingDetails);
304
#ifdef MOZ_GECKO_PROFILER
305
306
307
308
309
310
  Preferences::RegisterCallback(LoggingModePrefChangedCallback,
                                kPrefLoggingProfiler);
#endif
  Preferences::RegisterCallbackAndCall(LoggingModePrefChangedCallback,
                                       kPrefLoggingEnabled);

311
312
313
  Preferences::RegisterCallbackAndCall(DataThresholdPrefChangedCallback,
                                       kDataThresholdPref);

314
315
316
  Preferences::RegisterCallbackAndCall(MaxSerializedMsgSizePrefChangeCallback,
                                       kPrefMaxSerilizedMsgSize);

317
318
319
320
  Preferences::RegisterCallbackAndCall(AtomicBoolPrefChangedCallback,
                                       kPreprocessingPref,
                                       &gPreprocessingEnabled);

321
322
323
  Preferences::RegisterCallbackAndCall(MaxPreloadExtraRecordsPrefChangeCallback,
                                       kPrefMaxPreloadExtraRecords);

324
325
  nsAutoCString acceptLang;
  Preferences::GetLocalizedCString("intl.accept_languages", acceptLang);
326
327
328
329
330
331
332
333
334
335
336
337
338
339

  // Split values on commas.
  nsCCharSeparatedTokenizer langTokenizer(acceptLang, ',');
  while (langTokenizer.hasMoreTokens()) {
    nsAutoCString lang(langTokenizer.nextToken());
    icu::Locale locale = icu::Locale::createCanonical(lang.get());
    if (!locale.isBogus()) {
      // icu::Locale::getBaseName is always ASCII as per BCP 47
      mLocale.AssignASCII(locale.getBaseName());
      break;
    }
  }

  if (mLocale.IsEmpty()) {
340
    mLocale.AssignLiteral("en_US");
341
342
  }

Ben Turner's avatar
Ben Turner committed
343
344
345
  return NS_OK;
}

346
void IndexedDatabaseManager::Destroy() {
347
348
  // Setting the closed flag prevents the service from being recreated.
  // Don't set it though if there's no real instance created.
349
  if (gInitialized && gClosed.exchange(true)) {
350
351
    NS_ERROR("Shutdown more than once?!");
  }
352

353
  Preferences::UnregisterCallback(AtomicBoolPrefChangedCallback, kTestingPref,
354
355
356
357
                                  &gTestingMode);
  Preferences::UnregisterCallback(AtomicBoolPrefChangedCallback,
                                  kPrefExperimental,
                                  &gExperimentalFeaturesEnabled);
358
  Preferences::UnregisterCallback(AtomicBoolPrefChangedCallback,
359
                                  kPrefFileHandle, &gFileHandleEnabled);
360
361
362
  Preferences::UnregisterCallback(AtomicBoolPrefChangedCallback,
                                  kPrefErrorEventToSelfError,
                                  &gPrefErrorEventToSelfError);
363

364
365
  Preferences::UnregisterCallback(LoggingModePrefChangedCallback,
                                  kPrefLoggingDetails);
366
#ifdef MOZ_GECKO_PROFILER
367
368
369
370
371
372
  Preferences::UnregisterCallback(LoggingModePrefChangedCallback,
                                  kPrefLoggingProfiler);
#endif
  Preferences::UnregisterCallback(LoggingModePrefChangedCallback,
                                  kPrefLoggingEnabled);

373
374
375
  Preferences::UnregisterCallback(DataThresholdPrefChangedCallback,
                                  kDataThresholdPref);

376
377
378
  Preferences::UnregisterCallback(MaxSerializedMsgSizePrefChangeCallback,
                                  kPrefMaxSerilizedMsgSize);

379
380
381
  Preferences::UnregisterCallback(AtomicBoolPrefChangedCallback,
                                  kPreprocessingPref, &gPreprocessingEnabled);

382
  delete this;
383
384
}

385
// static
386
387
nsresult IndexedDatabaseManager::CommonPostHandleEvent(
    EventChainPostVisitor& aVisitor, IDBFactory* aFactory) {
388
  MOZ_ASSERT(aVisitor.mDOMEvent);
Ben Turner's avatar
Ben Turner committed
389
  MOZ_ASSERT(aFactory);
390

391
392
393
394
  if (!gPrefErrorEventToSelfError) {
    return NS_OK;
  }

395
396
397
398
  if (aVisitor.mEventStatus == nsEventStatus_eConsumeNoDefault) {
    return NS_OK;
  }

399
  if (!aVisitor.mDOMEvent->IsTrusted()) {
Ben Turner's avatar
Ben Turner committed
400
401
    return NS_OK;
  }
402

403
404
  nsAutoString type;
  aVisitor.mDOMEvent->GetType(type);
405

Ben Turner's avatar
Ben Turner committed
406
407
  MOZ_ASSERT(nsDependentString(kErrorEventType).EqualsLiteral("error"));
  if (!type.EqualsLiteral("error")) {
408
409
410
    return NS_OK;
  }

411
  nsCOMPtr<EventTarget> eventTarget = aVisitor.mDOMEvent->GetTarget();
412
  MOZ_ASSERT(eventTarget);
413

Ben Turner's avatar
Ben Turner committed
414
  // Only mess with events that were originally targeted to an IDBRequest.
415
  RefPtr<IDBRequest> request;
416
  if (NS_FAILED(eventTarget->QueryInterface(kIDBPrivateRequestIID,
Ben Turner's avatar
Ben Turner committed
417
418
419
420
                                            getter_AddRefs(request))) ||
      !request) {
    return NS_OK;
  }
421

422
  RefPtr<DOMException> error = request->GetErrorAfterResult();
423
424
425

  nsString errorName;
  if (error) {
426
    error->GetName(errorName);
427
428
  }

429
  RootedDictionary<ErrorEventInit> init(RootingCx());
430
  request->GetCallerLocation(init.mFilename, &init.mLineno, &init.mColno);
431

432
433
434
  init.mMessage = errorName;
  init.mCancelable = true;
  init.mBubbles = true;
435
436

  nsEventStatus status = nsEventStatus_eIgnore;
437
438

  if (NS_IsMainThread()) {
439
440
    nsCOMPtr<nsIDOMWindow> window =
        do_QueryInterface(eventTarget->GetOwnerGlobal());
441
    if (window) {
442
443
444
      nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(window);
      MOZ_ASSERT(sgo);

445
      if (NS_WARN_IF(!sgo->HandleScriptError(init, &status))) {
446
447
448
449
450
451
452
453
454
455
456
        status = nsEventStatus_eIgnore;
      }
    } else {
      // We don't fire error events at any global for non-window JS on the main
      // thread.
    }
  } else {
    // Not on the main thread, must be in a worker.
    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
    MOZ_ASSERT(workerPrivate);

457
    RefPtr<WorkerGlobalScope> globalScope = workerPrivate->GlobalScope();
458
459
    MOZ_ASSERT(globalScope);

460
461
    RefPtr<ErrorEvent> errorEvent = ErrorEvent::Constructor(
        globalScope, nsDependentString(kErrorEventType), init);
462
463
464
465
466
467
    MOZ_ASSERT(errorEvent);

    errorEvent->SetTrusted(true);

    auto* target = static_cast<EventTarget*>(globalScope.get());

468
469
470
471
    if (NS_WARN_IF(NS_FAILED(EventDispatcher::DispatchDOMEvent(
            target,
            /* aWidgetEvent */ nullptr, errorEvent,
            /* aPresContext */ nullptr, &status)))) {
472
473
      status = nsEventStatus_eIgnore;
    }
474
475
  }

476
  if (status == nsEventStatus_eConsumeNoDefault) {
477
478
479
    return NS_OK;
  }

480
  // Log the error to the error console.
481
482
  ScriptErrorHelper::Dump(errorName, init.mFilename, init.mLineno, init.mColno,
                          nsIScriptError::errorFlag, aFactory->IsChrome(),
483
                          aFactory->InnerWindowID());
484
485

  return NS_OK;
486
487
}

488
// static
489
bool IndexedDatabaseManager::ResolveSandboxBinding(JSContext* aCx) {
490
  MOZ_ASSERT(NS_IsMainThread());
491
  MOZ_ASSERT(js::GetObjectClass(JS::CurrentGlobalOrNull(aCx))->flags &
492
                 JSCLASS_DOM_GLOBAL,
493
             "Passed object is not a global object!");
494

495
496
497
498
499
500
  // We need to ensure that the manager has been created already here so that we
  // load preferences that may control which properties are exposed.
  if (NS_WARN_IF(!GetOrCreate())) {
    return false;
  }

501
502
503
504
505
506
507
508
509
510
511
512
  if (!IDBCursor_Binding::GetConstructorObject(aCx) ||
      !IDBCursorWithValue_Binding::GetConstructorObject(aCx) ||
      !IDBDatabase_Binding::GetConstructorObject(aCx) ||
      !IDBFactory_Binding::GetConstructorObject(aCx) ||
      !IDBIndex_Binding::GetConstructorObject(aCx) ||
      !IDBKeyRange_Binding::GetConstructorObject(aCx) ||
      !IDBLocaleAwareKeyRange_Binding::GetConstructorObject(aCx) ||
      !IDBMutableFile_Binding::GetConstructorObject(aCx) ||
      !IDBObjectStore_Binding::GetConstructorObject(aCx) ||
      !IDBOpenDBRequest_Binding::GetConstructorObject(aCx) ||
      !IDBRequest_Binding::GetConstructorObject(aCx) ||
      !IDBTransaction_Binding::GetConstructorObject(aCx) ||
513
      !IDBVersionChangeEvent_Binding::GetConstructorObject(aCx)) {
514
    return false;
515
516
  }

517
518
519
520
  return true;
}

// static
521
522
bool IndexedDatabaseManager::DefineIndexedDB(JSContext* aCx,
                                             JS::Handle<JSObject*> aGlobal) {
523
524
525
526
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL,
             "Passed object is not a global object!");

527
528
529
530
531
  nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx);
  if (NS_WARN_IF(!global)) {
    return false;
  }

532
  RefPtr<IDBFactory> factory;
533
534
  if (NS_FAILED(
          IDBFactory::CreateForMainThreadJS(global, getter_AddRefs(factory)))) {
535
536
    return false;
  }
537

538
  MOZ_ASSERT(factory, "This should never fail for chrome!");
539

540
  JS::Rooted<JS::Value> indexedDB(aCx);
541
  js::AssertSameCompartment(aCx, aGlobal);
542
  if (!GetOrCreateDOMReflector(aCx, factory, &indexedDB)) {
543
544
545
    return false;
  }

546
  return JS_DefineProperty(aCx, aGlobal, IDB_STR, indexedDB, JSPROP_ENUMERATE);
547
548
}

549
// static
550
bool IndexedDatabaseManager::IsClosed() { return gClosed; }
551

552
#ifdef DEBUG
553
// static
554
bool IndexedDatabaseManager::IsMainProcess() {
555
  NS_ASSERTION(gDBManager,
556
               "IsMainProcess() called before indexedDB has been initialized!");
557
558
  NS_ASSERTION((XRE_IsParentProcess()) == sIsMainProcess,
               "XRE_GetProcessType changed its tune!");
559
560
  return sIsMainProcess;
}
561

562
// static
563
IndexedDatabaseManager::LoggingMode IndexedDatabaseManager::GetLoggingMode() {
564
565
566
567
568
569
570
571
  MOZ_ASSERT(gDBManager,
             "GetLoggingMode called before IndexedDatabaseManager has been "
             "initialized!");

  return sLoggingMode;
}

// static
572
mozilla::LogModule* IndexedDatabaseManager::GetLoggingModule() {
573
574
575
576
577
578
579
  MOZ_ASSERT(gDBManager,
             "GetLoggingModule called before IndexedDatabaseManager has been "
             "initialized!");

  return sLoggingModule;
}

580
#endif  // DEBUG
581

582
// static
583
bool IndexedDatabaseManager::InTestingMode() {
584
585
586
587
588
589
  MOZ_ASSERT(gDBManager,
             "InTestingMode() called before indexedDB has been initialized!");

  return gTestingMode;
}

590
// static
591
bool IndexedDatabaseManager::FullSynchronous() {
592
593
594
595
596
597
  MOZ_ASSERT(gDBManager,
             "FullSynchronous() called before indexedDB has been initialized!");

  return sFullSynchronousMode;
}

598
// static
599
bool IndexedDatabaseManager::ExperimentalFeaturesEnabled() {
600
601
602
603
604
605
606
607
608
609
610
611
612
  if (NS_IsMainThread()) {
    if (NS_WARN_IF(!GetOrCreate())) {
      return false;
    }
  } else {
    MOZ_ASSERT(Get(),
               "ExperimentalFeaturesEnabled() called off the main thread "
               "before indexedDB has been initialized!");
  }

  return gExperimentalFeaturesEnabled;
}

613
// static
614
615
bool IndexedDatabaseManager::ExperimentalFeaturesEnabled(JSContext* aCx,
                                                         JSObject* aGlobal) {
616
617
618
619
620
621
  // If, in the child process, properties of the global object are enumerated
  // before the chrome registry (and thus the value of |intl.accept_languages|)
  // is ready, calling IndexedDatabaseManager::Init will permanently break
  // that preference. We can retrieve gExperimentalFeaturesEnabled without
  // actually going through IndexedDatabaseManager.
  // See Bug 1198093 comment 14 for detailed explanation.
622
623
  MOZ_DIAGNOSTIC_ASSERT(JS_IsGlobalObject(aGlobal));
  if (IsNonExposedGlobal(aCx, aGlobal, GlobalNames::BackstagePass)) {
624
625
626
627
628
629
630
631
632
633
634
635
    MOZ_ASSERT(NS_IsMainThread());
    static bool featureRetrieved = false;
    if (!featureRetrieved) {
      gExperimentalFeaturesEnabled = Preferences::GetBool(kPrefExperimental);
      featureRetrieved = true;
    }
    return gExperimentalFeaturesEnabled;
  }

  return ExperimentalFeaturesEnabled();
}

636
// static
637
bool IndexedDatabaseManager::IsFileHandleEnabled() {
638
639
640
641
642
643
644
  MOZ_ASSERT(gDBManager,
             "IsFileHandleEnabled() called before indexedDB has been "
             "initialized!");

  return gFileHandleEnabled;
}

645
// static
646
uint32_t IndexedDatabaseManager::DataThreshold() {
647
648
649
650
651
652
  MOZ_ASSERT(gDBManager,
             "DataThreshold() called before indexedDB has been initialized!");

  return gDataThresholdBytes;
}

653
// static
654
655
656
657
uint32_t IndexedDatabaseManager::MaxSerializedMsgSize() {
  MOZ_ASSERT(
      gDBManager,
      "MaxSerializedMsgSize() called before indexedDB has been initialized!");
658
659
660
661
662
  MOZ_ASSERT(gMaxSerializedMsgSize > 0);

  return gMaxSerializedMsgSize;
}

663
664
665
666
667
668
669
670
671
// static
bool IndexedDatabaseManager::PreprocessingEnabled() {
  MOZ_ASSERT(gDBManager,
             "PreprocessingEnabled() called before indexedDB has been "
             "initialized!");

  return gPreprocessingEnabled;
}

672
673
674
675
676
677
678
679
680
// static
int32_t IndexedDatabaseManager::MaxPreloadExtraRecords() {
  MOZ_ASSERT(gDBManager,
             "MaxPreloadExtraRecords() called before indexedDB has been "
             "initialized!");

  return gMaxPreloadExtraRecords;
}

681
void IndexedDatabaseManager::ClearBackgroundActor() {
682
683
684
685
686
  MOZ_ASSERT(NS_IsMainThread());

  mBackgroundActor = nullptr;
}

687
688
689
already_AddRefed<FileManager> IndexedDatabaseManager::GetFileManager(
    PersistenceType aPersistenceType, const nsACString& aOrigin,
    const nsAString& aDatabaseName) {
690
691
  AssertIsOnIOThread();

692
693
  FileManagerInfo* info;
  if (!mFileManagerInfos.Get(aOrigin, &info)) {
694
    return nullptr;
695
  }
696

697
  RefPtr<FileManager> fileManager =
698
      info->GetFileManager(aPersistenceType, aDatabaseName);
699

700
  return fileManager.forget();
701
702
}

703
void IndexedDatabaseManager::AddFileManager(FileManager* aFileManager) {
704
  AssertIsOnIOThread();
705
  NS_ASSERTION(aFileManager, "Null file manager!");
706

707
708
709
710
711
712
713
714
715
  FileManagerInfo* info;
  if (!mFileManagerInfos.Get(aFileManager->Origin(), &info)) {
    info = new FileManagerInfo();
    mFileManagerInfos.Put(aFileManager->Origin(), info);
  }

  info->AddFileManager(aFileManager);
}

716
void IndexedDatabaseManager::InvalidateAllFileManagers() {
717
718
  AssertIsOnIOThread();

719
720
721
722
723
724
  for (auto iter = mFileManagerInfos.ConstIter(); !iter.Done(); iter.Next()) {
    auto value = iter.Data();
    MOZ_ASSERT(value);

    value->InvalidateAllFileManagers();
  }
725
726

  mFileManagerInfos.Clear();
727
728
}

729
730
void IndexedDatabaseManager::InvalidateFileManagers(
    PersistenceType aPersistenceType, const nsACString& aOrigin) {
731
  AssertIsOnIOThread();
732
  MOZ_ASSERT(!aOrigin.IsEmpty());
733

734
735
736
737
  FileManagerInfo* info;
  if (!mFileManagerInfos.Get(aOrigin, &info)) {
    return;
  }
738

739
  info->InvalidateAndRemoveFileManagers(aPersistenceType);
740

741
742
  if (!info->HasFileManagers()) {
    mFileManagerInfos.Remove(aOrigin);
743
  }
744
}
745

746
747
748
void IndexedDatabaseManager::InvalidateFileManager(
    PersistenceType aPersistenceType, const nsACString& aOrigin,
    const nsAString& aDatabaseName) {
749
750
  AssertIsOnIOThread();

751
752
  FileManagerInfo* info;
  if (!mFileManagerInfos.Get(aOrigin, &info)) {
753
754
    return;
  }
755

756
  info->InvalidateAndRemoveFileManager(aPersistenceType, aDatabaseName);
757

758
759
  if (!info->HasFileManagers()) {
    mFileManagerInfos.Remove(aOrigin);
760
  }
761
762
}

763
764
765
766
nsresult IndexedDatabaseManager::BlockAndGetFileReferences(
    PersistenceType aPersistenceType, const nsACString& aOrigin,
    const nsAString& aDatabaseName, int64_t aFileId, int32_t* aRefCnt,
    int32_t* aDBRefCnt, int32_t* aSliceRefCnt, bool* aResult) {
767
768
  MOZ_ASSERT(NS_IsMainThread());

769
770
771
  if (NS_WARN_IF(!InTestingMode())) {
    return NS_ERROR_UNEXPECTED;
  }
772

773
774
775
  if (!mBackgroundActor) {
    PBackgroundChild* bgActor = BackgroundChild::GetForCurrentThread();
    if (NS_WARN_IF(!bgActor)) {
776
777
778
      return NS_ERROR_FAILURE;
    }

779
780
    BackgroundUtilsChild* actor = new BackgroundUtilsChild(this);

781
782
    // We don't set event target for BackgroundUtilsChild because:
    // 1. BackgroundUtilsChild is a singleton.
783
784
    // 2. SendGetFileReferences is a sync operation to be returned asap if
    // unlabeled.
785
786
    // 3. The rest operations like DeleteMe/__delete__ only happens at shutdown.
    // Hence, we should keep it unlabeled.
787
    mBackgroundActor = static_cast<BackgroundUtilsChild*>(
788
789
790
791
792
793
794
        bgActor->SendPBackgroundIndexedDBUtilsConstructor(actor));
  }

  if (NS_WARN_IF(!mBackgroundActor)) {
    return NS_ERROR_FAILURE;
  }

795
796
797
  if (!mBackgroundActor->SendGetFileReferences(
          aPersistenceType, nsCString(aOrigin), nsString(aDatabaseName),
          aFileId, aRefCnt, aDBRefCnt, aSliceRefCnt, aResult)) {
798
    return NS_ERROR_FAILURE;
799
  }
800
801
802
803

  return NS_OK;
}

804
nsresult IndexedDatabaseManager::FlushPendingFileDeletions() {
805
806
807
808
809
810
  MOZ_ASSERT(NS_IsMainThread());

  if (NS_WARN_IF(!InTestingMode())) {
    return NS_ERROR_UNEXPECTED;
  }

811
812
813
814
  PBackgroundChild* bgActor = BackgroundChild::GetForCurrentThread();
  if (NS_WARN_IF(!bgActor)) {
    return NS_ERROR_FAILURE;
  }
815

816
817
  if (!bgActor->SendFlushPendingFileDeletions()) {
    return NS_ERROR_FAILURE;
818
819
820
821
822
  }

  return NS_OK;
}

823
// static
824
825
void IndexedDatabaseManager::LoggingModePrefChangedCallback(
    const char* /* aPrefName */, void* /* aClosure */) {
826
827
828
829
830
831
832
  MOZ_ASSERT(NS_IsMainThread());

  if (!Preferences::GetBool(kPrefLoggingEnabled)) {
    sLoggingMode = Logging_Disabled;
    return;
  }

833
  bool useProfiler =
834
#if defined(DEBUG) || defined(MOZ_GECKO_PROFILER)
835
      Preferences::GetBool(kPrefLoggingProfiler);
836
#  if !defined(MOZ_GECKO_PROFILER)
837
  if (useProfiler) {
838
839
840
    NS_WARNING(
        "IndexedDB cannot create profiler marks because this build does "
        "not have profiler extensions enabled!");
841
842
    useProfiler = false;
  }
843
#  endif
844
#else
845
      false;
846
847
848
849
850
#endif

  const bool logDetails = Preferences::GetBool(kPrefLoggingDetails);

  if (useProfiler) {
851
852
    sLoggingMode = logDetails ? Logging_DetailedProfilerMarks
                              : Logging_ConciseProfilerMarks;
853
854
855
856
857
  } else {
    sLoggingMode = logDetails ? Logging_Detailed : Logging_Concise;
  }
}

858
// static
859
const nsCString& IndexedDatabaseManager::GetLocale() {
860
861
862
863
864
865
  IndexedDatabaseManager* idbManager = Get();
  MOZ_ASSERT(idbManager, "IDBManager is not ready!");

  return idbManager->mLocale;
}

866
867
already_AddRefed<FileManager> FileManagerInfo::GetFileManager(
    PersistenceType aPersistenceType, const nsAString& aName) const {
868
869
  AssertIsOnIOThread();

870
  const auto& managers = GetImmutableArray(aPersistenceType);
871

872
873
  const auto end = managers.cend();
  const auto foundIt =
874
      std::find_if(managers.cbegin(), end, DatabaseNameMatchPredicate(&aName));
875

876
  return foundIt != end ? RefPtr<FileManager>{*foundIt}.forget() : nullptr;
877
878
}

879
void FileManagerInfo::AddFileManager(FileManager* aFileManager) {
880
881
  AssertIsOnIOThread();

882
  nsTArray<RefPtr<FileManager> >& managers = GetArray(aFileManager->Type());
883
884
885
886
887
888

  NS_ASSERTION(!managers.Contains(aFileManager), "Adding more than once?!");

  managers.AppendElement(aFileManager);
}

889
void FileManagerInfo::InvalidateAllFileManagers() const {
890
891
892
893
894
895
896
897
898
899
900
  AssertIsOnIOThread();

  uint32_t i;

  for (i = 0; i < mPersistentStorageFileManagers.Length(); i++) {
    mPersistentStorageFileManagers[i]->Invalidate();
  }

  for (i = 0; i < mTemporaryStorageFileManagers.Length(); i++) {
    mTemporaryStorageFileManagers[i]->Invalidate();
  }
901
902
903
904

  for (i = 0; i < mDefaultStorageFileManagers.Length(); i++) {
    mDefaultStorageFileManagers[i]->Invalidate();
  }
905
906
}

907
908
void FileManagerInfo::InvalidateAndRemoveFileManagers(
    PersistenceType aPersistenceType) {
909
910
  AssertIsOnIOThread();

911
  nsTArray<RefPtr<FileManager> >& managers = GetArray(aPersistenceType);
912
913
914
915
916
917
918
919

  for (uint32_t i = 0; i < managers.Length(); i++) {
    managers[i]->Invalidate();
  }

  managers.Clear();
}

920
921
void FileManagerInfo::InvalidateAndRemoveFileManager(
    PersistenceType aPersistenceType, const nsAString& aName) {
922
923
  AssertIsOnIOThread();