Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
* License, v. 2.0. If a copy of the MPL was not distributed with this
5
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "IndexedDatabaseManager.h"
8
9
#include "chrome/common/ipc_channel.h" // for IPC::Channel::kMaximumMessageSize
10
#include "nsIScriptError.h"
11
#include "nsIScriptGlobalObject.h"
12
13
#include "jsapi.h"
14
#include "mozilla/ClearOnShutdown.h"
15
#include "mozilla/ContentEvents.h"
16
#include "mozilla/EventDispatcher.h"
17
#include "mozilla/Preferences.h"
18
#include "mozilla/dom/DOMException.h"
19
#include "mozilla/dom/ErrorEvent.h"
20
#include "mozilla/dom/ErrorEventBinding.h"
21
#include "mozilla/dom/WorkerScope.h"
22
#include "mozilla/dom/WorkerPrivate.h"
23
#include "mozilla/ipc/BackgroundChild.h"
24
#include "mozilla/ipc/PBackgroundChild.h"
25
#include "nsContentUtils.h"
26
#include "nsGlobalWindow.h"
27
#include "mozilla/Logging.h"
28
29
#include "FileManager.h"
30
#include "IDBEvents.h"
31
#include "IDBFactory.h"
32
#include "IDBKeyRange.h"
33
#include "IDBRequest.h"
34
#include "ProfilerHelpers.h"
35
#include "ScriptErrorHelper.h"
36
#include "nsCharSeparatedTokenizer.h"
37
#include "unicode/locid.h"
38
39
// Bindings for ResolveConstructors
40
#include "mozilla/dom/IDBCursorBinding.h"
41
#include "mozilla/dom/IDBDatabaseBinding.h"
42
#include "mozilla/dom/IDBFactoryBinding.h"
43
#include "mozilla/dom/IDBIndexBinding.h"
44
#include "mozilla/dom/IDBKeyRangeBinding.h"
45
#include "mozilla/dom/IDBMutableFileBinding.h"
46
#include "mozilla/dom/IDBObjectStoreBinding.h"
47
#include "mozilla/dom/IDBOpenDBRequestBinding.h"
48
#include "mozilla/dom/IDBRequestBinding.h"
49
#include "mozilla/dom/IDBTransactionBinding.h"
50
#include "mozilla/dom/IDBVersionChangeEventBinding.h"
51
52
#define IDB_STR "indexedDB"
53
54
namespace mozilla {
55
namespace dom {
56
namespace indexedDB {
57
58
using namespace mozilla::dom::quota;
59
using namespace mozilla::ipc;
60
61
class FileManagerInfo {
62
public:
63
already_AddRefed<FileManager> GetFileManager(PersistenceType aPersistenceType,
64
const nsAString& aName) const;
65
66
void AddFileManager(FileManager* aFileManager);
67
68
bool HasFileManagers() const {
69
AssertIsOnIOThread();
70
71
return !mPersistentStorageFileManagers.IsEmpty() ||
72
!mTemporaryStorageFileManagers.IsEmpty() ||
73
!mDefaultStorageFileManagers.IsEmpty();
74
}
75
76
void InvalidateAllFileManagers() const;
77
78
void InvalidateAndRemoveFileManagers(PersistenceType aPersistenceType);
79
80
void InvalidateAndRemoveFileManager(PersistenceType aPersistenceType,
81
const nsAString& aName);
82
83
private:
84
nsTArray<RefPtr<FileManager> >& GetArray(PersistenceType aPersistenceType);
85
86
const nsTArray<RefPtr<FileManager> >& GetImmutableArray(
87
PersistenceType aPersistenceType) const {
88
return const_cast<FileManagerInfo*>(this)->GetArray(aPersistenceType);
89
}
90
91
nsTArray<RefPtr<FileManager> > mPersistentStorageFileManagers;
92
nsTArray<RefPtr<FileManager> > mTemporaryStorageFileManagers;
93
nsTArray<RefPtr<FileManager> > mDefaultStorageFileManagers;
94
};
95
96
} // namespace indexedDB
97
98
using namespace mozilla::dom::indexedDB;
99
100
namespace {
101
102
NS_DEFINE_IID(kIDBPrivateRequestIID, PRIVATE_IDBREQUEST_IID);
103
104
const uint32_t kDeleteTimeoutMs = 1000;
105
106
// The threshold we use for structured clone data storing.
107
// Anything smaller than the threshold is compressed and stored in the database.
108
// Anything larger is compressed and stored outside the database.
109
const int32_t kDefaultDataThresholdBytes = 1024 * 1024; // 1MB
110
111
// The maximal size of a serialized object to be transfered through IPC.
112
const int32_t kDefaultMaxSerializedMsgSize = IPC::Channel::kMaximumMessageSize;
113
114
// The maximum number of records to preload (in addition to the one requested by
115
// the child).
116
//
117
// TODO: The current number was chosen for no particular reason. Telemetry
118
// should be added to determine whether this is a reasonable number for an
119
// overwhelming majority of cases.
120
const int32_t kDefaultMaxPreloadExtraRecords = 64;
121
122
#define IDB_PREF_BRANCH_ROOT "dom.indexedDB."
123
124
const char kTestingPref[] = IDB_PREF_BRANCH_ROOT "testing";
125
const char kPrefExperimental[] = IDB_PREF_BRANCH_ROOT "experimental";
126
const char kPrefFileHandle[] = "dom.fileHandle.enabled";
127
const char kDataThresholdPref[] = IDB_PREF_BRANCH_ROOT "dataThreshold";
128
const char kPrefMaxSerilizedMsgSize[] =
129
IDB_PREF_BRANCH_ROOT "maxSerializedMsgSize";
130
const char kPrefErrorEventToSelfError[] =
131
IDB_PREF_BRANCH_ROOT "errorEventToSelfError";
132
const char kPreprocessingPref[] = IDB_PREF_BRANCH_ROOT "preprocessing";
133
const char kPrefMaxPreloadExtraRecords[] =
134
IDB_PREF_BRANCH_ROOT "maxPreloadExtraRecords";
135
136
#define IDB_PREF_LOGGING_BRANCH_ROOT IDB_PREF_BRANCH_ROOT "logging."
137
138
const char kPrefLoggingEnabled[] = IDB_PREF_LOGGING_BRANCH_ROOT "enabled";
139
const char kPrefLoggingDetails[] = IDB_PREF_LOGGING_BRANCH_ROOT "details";
140
141
#if defined(DEBUG) || defined(MOZ_GECKO_PROFILER)
142
const char kPrefLoggingProfiler[] =
143
IDB_PREF_LOGGING_BRANCH_ROOT "profiler-marks";
144
#endif
145
146
#undef IDB_PREF_LOGGING_BRANCH_ROOT
147
#undef IDB_PREF_BRANCH_ROOT
148
149
StaticRefPtr<IndexedDatabaseManager> gDBManager;
150
151
Atomic<bool> gInitialized(false);
152
Atomic<bool> gClosed(false);
153
Atomic<bool> gTestingMode(false);
154
Atomic<bool> gExperimentalFeaturesEnabled(false);
155
Atomic<bool> gFileHandleEnabled(false);
156
Atomic<bool> gPrefErrorEventToSelfError(false);
157
Atomic<int32_t> gDataThresholdBytes(0);
158
Atomic<int32_t> gMaxSerializedMsgSize(0);
159
Atomic<bool> gPreprocessingEnabled(false);
160
Atomic<int32_t> gMaxPreloadExtraRecords(0);
161
162
void AtomicBoolPrefChangedCallback(const char* aPrefName, void* aBool) {
163
MOZ_ASSERT(NS_IsMainThread());
164
MOZ_ASSERT(aBool);
165
166
*static_cast<Atomic<bool>*>(aBool) = Preferences::GetBool(aPrefName);
167
}
168
169
void DataThresholdPrefChangedCallback(const char* aPrefName, void* aClosure) {
170
MOZ_ASSERT(NS_IsMainThread());
171
MOZ_ASSERT(!strcmp(aPrefName, kDataThresholdPref));
172
MOZ_ASSERT(!aClosure);
173
174
int32_t dataThresholdBytes =
175
Preferences::GetInt(aPrefName, kDefaultDataThresholdBytes);
176
177
// The magic -1 is for use only by tests that depend on stable blob file id's.
178
if (dataThresholdBytes == -1) {
179
dataThresholdBytes = INT32_MAX;
180
}
181
182
gDataThresholdBytes = dataThresholdBytes;
183
}
184
185
void MaxSerializedMsgSizePrefChangeCallback(const char* aPrefName,
186
void* aClosure) {
187
MOZ_ASSERT(NS_IsMainThread());
188
MOZ_ASSERT(!strcmp(aPrefName, kPrefMaxSerilizedMsgSize));
189
MOZ_ASSERT(!aClosure);
190
191
gMaxSerializedMsgSize =
192
Preferences::GetInt(aPrefName, kDefaultMaxSerializedMsgSize);
193
MOZ_ASSERT(gMaxSerializedMsgSize > 0);
194
}
195
196
void MaxPreloadExtraRecordsPrefChangeCallback(const char* aPrefName,
197
void* aClosure) {
198
MOZ_ASSERT(NS_IsMainThread());
199
MOZ_ASSERT(!strcmp(aPrefName, kPrefMaxPreloadExtraRecords));
200
MOZ_ASSERT(!aClosure);
201
202
gMaxPreloadExtraRecords =
203
Preferences::GetInt(aPrefName, kDefaultMaxPreloadExtraRecords);
204
MOZ_ASSERT(gMaxPreloadExtraRecords >= 0);
205
206
// TODO: We could also allow setting a negative value to preload all available
207
// records, but this doesn't seem to be too useful in general, and it would
208
// require adaptations in ActorsParent.cpp
209
}
210
211
auto DatabaseNameMatchPredicate(const nsAString* const aName) {
212
MOZ_ASSERT(aName);
213
return [aName](const auto& fileManager) {
214
return fileManager->DatabaseName() == *aName;
215
};
216
}
217
218
} // namespace
219
220
IndexedDatabaseManager::IndexedDatabaseManager()
221
: mFileMutex("IndexedDatabaseManager.mFileMutex"),
222
mBackgroundActor(nullptr) {
223
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
224
}
225
226
IndexedDatabaseManager::~IndexedDatabaseManager() {
227
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
228
229
if (mBackgroundActor) {
230
mBackgroundActor->SendDeleteMeInternal();
231
MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!");
232
}
233
}
234
235
bool IndexedDatabaseManager::sIsMainProcess = false;
236
bool IndexedDatabaseManager::sFullSynchronousMode = false;
237
238
mozilla::LazyLogModule IndexedDatabaseManager::sLoggingModule("IndexedDB");
239
240
Atomic<IndexedDatabaseManager::LoggingMode>
241
IndexedDatabaseManager::sLoggingMode(
242
IndexedDatabaseManager::Logging_Disabled);
243
244
// static
245
IndexedDatabaseManager* IndexedDatabaseManager::GetOrCreate() {
246
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
247
248
if (IsClosed()) {
249
NS_ERROR("Calling GetOrCreate() after shutdown!");
250
return nullptr;
251
}
252
253
if (!gDBManager) {
254
sIsMainProcess = XRE_IsParentProcess();
255
256
RefPtr<IndexedDatabaseManager> instance(new IndexedDatabaseManager());
257
258
nsresult rv = instance->Init();
259
NS_ENSURE_SUCCESS(rv, nullptr);
260
261
if (gInitialized.exchange(true)) {
262
NS_ERROR("Initialized more than once?!");
263
}
264
265
gDBManager = instance;
266
267
ClearOnShutdown(&gDBManager);
268
}
269
270
return gDBManager;
271
}
272
273
// static
274
IndexedDatabaseManager* IndexedDatabaseManager::Get() {
275
// Does not return an owning reference.
276
return gDBManager;
277
}
278
279
nsresult IndexedDatabaseManager::Init() {
280
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
281
282
Preferences::RegisterCallbackAndCall(AtomicBoolPrefChangedCallback,
283
kTestingPref, &gTestingMode);
284
Preferences::RegisterCallbackAndCall(AtomicBoolPrefChangedCallback,
285
kPrefExperimental,
286
&gExperimentalFeaturesEnabled);
287
Preferences::RegisterCallbackAndCall(AtomicBoolPrefChangedCallback,
288
kPrefFileHandle, &gFileHandleEnabled);
289
Preferences::RegisterCallbackAndCall(AtomicBoolPrefChangedCallback,
290
kPrefErrorEventToSelfError,
291
&gPrefErrorEventToSelfError);
292
293
// By default IndexedDB uses SQLite with PRAGMA synchronous = NORMAL. This
294
// guarantees (unlike synchronous = OFF) atomicity and consistency, but not
295
// necessarily durability in situations such as power loss. This preference
296
// allows enabling PRAGMA synchronous = FULL on SQLite, which does guarantee
297
// durability, but with an extra fsync() and the corresponding performance
298
// hit.
299
sFullSynchronousMode = Preferences::GetBool("dom.indexedDB.fullSynchronous");
300
301
Preferences::RegisterCallback(LoggingModePrefChangedCallback,
302
kPrefLoggingDetails);
303
#ifdef MOZ_GECKO_PROFILER
304
Preferences::RegisterCallback(LoggingModePrefChangedCallback,
305
kPrefLoggingProfiler);
306
#endif
307
Preferences::RegisterCallbackAndCall(LoggingModePrefChangedCallback,
308
kPrefLoggingEnabled);
309
310
Preferences::RegisterCallbackAndCall(DataThresholdPrefChangedCallback,
311
kDataThresholdPref);
312
313
Preferences::RegisterCallbackAndCall(MaxSerializedMsgSizePrefChangeCallback,
314
kPrefMaxSerilizedMsgSize);
315
316
Preferences::RegisterCallbackAndCall(AtomicBoolPrefChangedCallback,
317
kPreprocessingPref,
318
&gPreprocessingEnabled);
319
320
Preferences::RegisterCallbackAndCall(MaxPreloadExtraRecordsPrefChangeCallback,
321
kPrefMaxPreloadExtraRecords);
322
323
nsAutoCString acceptLang;
324
Preferences::GetLocalizedCString("intl.accept_languages", acceptLang);
325
326
// Split values on commas.
327
nsCCharSeparatedTokenizer langTokenizer(acceptLang, ',');
328
while (langTokenizer.hasMoreTokens()) {
329
nsAutoCString lang(langTokenizer.nextToken());
330
icu::Locale locale = icu::Locale::createCanonical(lang.get());
331
if (!locale.isBogus()) {
332
// icu::Locale::getBaseName is always ASCII as per BCP 47
333
mLocale.AssignASCII(locale.getBaseName());
334
break;
335
}
336
}
337
338
if (mLocale.IsEmpty()) {
339
mLocale.AssignLiteral("en_US");
340
}
341
342
return NS_OK;
343
}
344
345
void IndexedDatabaseManager::Destroy() {
346
// Setting the closed flag prevents the service from being recreated.
347
// Don't set it though if there's no real instance created.
348
if (gInitialized && gClosed.exchange(true)) {
349
NS_ERROR("Shutdown more than once?!");
350
}
351
352
Preferences::UnregisterCallback(AtomicBoolPrefChangedCallback, kTestingPref,
353
&gTestingMode);
354
Preferences::UnregisterCallback(AtomicBoolPrefChangedCallback,
355
kPrefExperimental,
356
&gExperimentalFeaturesEnabled);
357
Preferences::UnregisterCallback(AtomicBoolPrefChangedCallback,
358
kPrefFileHandle, &gFileHandleEnabled);
359
Preferences::UnregisterCallback(AtomicBoolPrefChangedCallback,
360
kPrefErrorEventToSelfError,
361
&gPrefErrorEventToSelfError);
362
363
Preferences::UnregisterCallback(LoggingModePrefChangedCallback,
364
kPrefLoggingDetails);
365
#ifdef MOZ_GECKO_PROFILER
366
Preferences::UnregisterCallback(LoggingModePrefChangedCallback,
367
kPrefLoggingProfiler);
368
#endif
369
Preferences::UnregisterCallback(LoggingModePrefChangedCallback,
370
kPrefLoggingEnabled);
371
372
Preferences::UnregisterCallback(DataThresholdPrefChangedCallback,
373
kDataThresholdPref);
374
375
Preferences::UnregisterCallback(MaxSerializedMsgSizePrefChangeCallback,
376
kPrefMaxSerilizedMsgSize);
377
378
Preferences::UnregisterCallback(AtomicBoolPrefChangedCallback,
379
kPreprocessingPref, &gPreprocessingEnabled);
380
381
delete this;
382
}
383
384
// static
385
nsresult IndexedDatabaseManager::CommonPostHandleEvent(
386
EventChainPostVisitor& aVisitor, IDBFactory* aFactory) {
387
MOZ_ASSERT(aVisitor.mDOMEvent);
388
MOZ_ASSERT(aFactory);
389
390
if (!gPrefErrorEventToSelfError) {
391
return NS_OK;
392
}
393
394
if (aVisitor.mEventStatus == nsEventStatus_eConsumeNoDefault) {
395
return NS_OK;
396
}
397
398
if (!aVisitor.mDOMEvent->IsTrusted()) {
399
return NS_OK;
400
}
401
402
nsAutoString type;
403
aVisitor.mDOMEvent->GetType(type);
404
405
MOZ_ASSERT(nsDependentString(kErrorEventType).EqualsLiteral("error"));
406
if (!type.EqualsLiteral("error")) {
407
return NS_OK;
408
}
409
410
nsCOMPtr<EventTarget> eventTarget = aVisitor.mDOMEvent->GetTarget();
411
MOZ_ASSERT(eventTarget);
412
413
// Only mess with events that were originally targeted to an IDBRequest.
414
RefPtr<IDBRequest> request;
415
if (NS_FAILED(eventTarget->QueryInterface(kIDBPrivateRequestIID,
416
getter_AddRefs(request))) ||
417
!request) {
418
return NS_OK;
419
}
420
421
RefPtr<DOMException> error = request->GetErrorAfterResult();
422
423
nsString errorName;
424
if (error) {
425
error->GetName(errorName);
426
}
427
428
RootedDictionary<ErrorEventInit> init(RootingCx());
429
request->GetCallerLocation(init.mFilename, &init.mLineno, &init.mColno);
430
431
init.mMessage = errorName;
432
init.mCancelable = true;
433
init.mBubbles = true;
434
435
nsEventStatus status = nsEventStatus_eIgnore;
436
437
if (NS_IsMainThread()) {
438
nsCOMPtr<nsIDOMWindow> window =
439
do_QueryInterface(eventTarget->GetOwnerGlobal());
440
if (window) {
441
nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(window);
442
MOZ_ASSERT(sgo);
443
444
if (NS_WARN_IF(!sgo->HandleScriptError(init, &status))) {
445
status = nsEventStatus_eIgnore;
446
}
447
} else {
448
// We don't fire error events at any global for non-window JS on the main
449
// thread.
450
}
451
} else {
452
// Not on the main thread, must be in a worker.
453
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
454
MOZ_ASSERT(workerPrivate);
455
456
RefPtr<WorkerGlobalScope> globalScope = workerPrivate->GlobalScope();
457
MOZ_ASSERT(globalScope);
458
459
RefPtr<ErrorEvent> errorEvent = ErrorEvent::Constructor(
460
globalScope, nsDependentString(kErrorEventType), init);
461
MOZ_ASSERT(errorEvent);
462
463
errorEvent->SetTrusted(true);
464
465
auto* target = static_cast<EventTarget*>(globalScope.get());
466
467
if (NS_WARN_IF(NS_FAILED(EventDispatcher::DispatchDOMEvent(
468
target,
469
/* aWidgetEvent */ nullptr, errorEvent,
470
/* aPresContext */ nullptr, &status)))) {
471
status = nsEventStatus_eIgnore;
472
}
473
}
474
475
if (status == nsEventStatus_eConsumeNoDefault) {
476
return NS_OK;
477
}
478
479
// Log the error to the error console.
480
ScriptErrorHelper::Dump(errorName, init.mFilename, init.mLineno, init.mColno,
481
nsIScriptError::errorFlag, aFactory->IsChrome(),
482
aFactory->InnerWindowID());
483
484
return NS_OK;
485
}
486
487
// static
488
bool IndexedDatabaseManager::ResolveSandboxBinding(JSContext* aCx) {
489
MOZ_ASSERT(NS_IsMainThread());
490
MOZ_ASSERT(js::GetObjectClass(JS::CurrentGlobalOrNull(aCx))->flags &
491
JSCLASS_DOM_GLOBAL,
492
"Passed object is not a global object!");
493
494
// We need to ensure that the manager has been created already here so that we
495
// load preferences that may control which properties are exposed.
496
if (NS_WARN_IF(!GetOrCreate())) {
497
return false;
498
}
499
500
if (!IDBCursor_Binding::GetConstructorObject(aCx) ||
501
!IDBCursorWithValue_Binding::GetConstructorObject(aCx) ||
502
!IDBDatabase_Binding::GetConstructorObject(aCx) ||
503
!IDBFactory_Binding::GetConstructorObject(aCx) ||
504
!IDBIndex_Binding::GetConstructorObject(aCx) ||
505
!IDBKeyRange_Binding::GetConstructorObject(aCx) ||
506
!IDBLocaleAwareKeyRange_Binding::GetConstructorObject(aCx) ||
507
!IDBMutableFile_Binding::GetConstructorObject(aCx) ||
508
!IDBObjectStore_Binding::GetConstructorObject(aCx) ||
509
!IDBOpenDBRequest_Binding::GetConstructorObject(aCx) ||
510
!IDBRequest_Binding::GetConstructorObject(aCx) ||
511
!IDBTransaction_Binding::GetConstructorObject(aCx) ||
512
!IDBVersionChangeEvent_Binding::GetConstructorObject(aCx)) {
513
return false;
514
}
515
516
return true;
517
}
518
519
// static
520
bool IndexedDatabaseManager::DefineIndexedDB(JSContext* aCx,
521
JS::Handle<JSObject*> aGlobal) {
522
MOZ_ASSERT(NS_IsMainThread());
523
MOZ_ASSERT(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL,
524
"Passed object is not a global object!");
525
526
nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx);
527
if (NS_WARN_IF(!global)) {
528
return false;
529
}
530
531
RefPtr<IDBFactory> factory;
532
if (NS_FAILED(
533
IDBFactory::CreateForMainThreadJS(global, getter_AddRefs(factory)))) {
534
return false;
535
}
536
537
MOZ_ASSERT(factory, "This should never fail for chrome!");
538
539
JS::Rooted<JS::Value> indexedDB(aCx);
540
js::AssertSameCompartment(aCx, aGlobal);
541
if (!GetOrCreateDOMReflector(aCx, factory, &indexedDB)) {
542
return false;
543
}
544
545
return JS_DefineProperty(aCx, aGlobal, IDB_STR, indexedDB, JSPROP_ENUMERATE);
546
}
547
548
// static
549
bool IndexedDatabaseManager::IsClosed() { return gClosed; }
550
551
#ifdef DEBUG
552
// static
553
bool IndexedDatabaseManager::IsMainProcess() {
554
NS_ASSERTION(gDBManager,
555
"IsMainProcess() called before indexedDB has been initialized!");
556
NS_ASSERTION((XRE_IsParentProcess()) == sIsMainProcess,
557
"XRE_GetProcessType changed its tune!");
558
return sIsMainProcess;
559
}
560
561
// static
562
IndexedDatabaseManager::LoggingMode IndexedDatabaseManager::GetLoggingMode() {
563
MOZ_ASSERT(gDBManager,
564
"GetLoggingMode called before IndexedDatabaseManager has been "
565
"initialized!");
566
567
return sLoggingMode;
568
}
569
570
// static
571
mozilla::LogModule* IndexedDatabaseManager::GetLoggingModule() {
572
MOZ_ASSERT(gDBManager,
573
"GetLoggingModule called before IndexedDatabaseManager has been "
574
"initialized!");
575
576
return sLoggingModule;
577
}
578
579
#endif // DEBUG
580
581
// static
582
bool IndexedDatabaseManager::InTestingMode() {
583
MOZ_ASSERT(gDBManager,
584
"InTestingMode() called before indexedDB has been initialized!");
585
586
return gTestingMode;
587
}
588
589
// static
590
bool IndexedDatabaseManager::FullSynchronous() {
591
MOZ_ASSERT(gDBManager,
592
"FullSynchronous() called before indexedDB has been initialized!");
593
594
return sFullSynchronousMode;
595
}
596
597
// static
598
bool IndexedDatabaseManager::ExperimentalFeaturesEnabled() {
599
if (NS_IsMainThread()) {
600
if (NS_WARN_IF(!GetOrCreate())) {
601
return false;
602
}
603
} else {
604
MOZ_ASSERT(Get(),
605
"ExperimentalFeaturesEnabled() called off the main thread "
606
"before indexedDB has been initialized!");
607
}
608
609
return gExperimentalFeaturesEnabled;
610
}
611
612
// static
613
bool IndexedDatabaseManager::ExperimentalFeaturesEnabled(JSContext* aCx,
614
JSObject* aGlobal) {
615
// If, in the child process, properties of the global object are enumerated
616
// before the chrome registry (and thus the value of |intl.accept_languages|)
617
// is ready, calling IndexedDatabaseManager::Init will permanently break
618
// that preference. We can retrieve gExperimentalFeaturesEnabled without
619
// actually going through IndexedDatabaseManager.
620
// See Bug 1198093 comment 14 for detailed explanation.
621
MOZ_DIAGNOSTIC_ASSERT(JS_IsGlobalObject(aGlobal));
622
if (IsNonExposedGlobal(aCx, aGlobal, GlobalNames::BackstagePass)) {
623
MOZ_ASSERT(NS_IsMainThread());
624
static bool featureRetrieved = false;
625
if (!featureRetrieved) {
626
gExperimentalFeaturesEnabled = Preferences::GetBool(kPrefExperimental);
627
featureRetrieved = true;
628
}
629
return gExperimentalFeaturesEnabled;
630
}
631
632
return ExperimentalFeaturesEnabled();
633
}
634
635
// static
636
bool IndexedDatabaseManager::IsFileHandleEnabled() {
637
MOZ_ASSERT(gDBManager,
638
"IsFileHandleEnabled() called before indexedDB has been "
639
"initialized!");
640
641
return gFileHandleEnabled;
642
}
643
644
// static
645
uint32_t IndexedDatabaseManager::DataThreshold() {
646
MOZ_ASSERT(gDBManager,
647
"DataThreshold() called before indexedDB has been initialized!");
648
649
return gDataThresholdBytes;
650
}
651
652
// static
653
uint32_t IndexedDatabaseManager::MaxSerializedMsgSize() {
654
MOZ_ASSERT(
655
gDBManager,
656
"MaxSerializedMsgSize() called before indexedDB has been initialized!");
657
MOZ_ASSERT(gMaxSerializedMsgSize > 0);
658
659
return gMaxSerializedMsgSize;
660
}
661
662
// static
663
bool IndexedDatabaseManager::PreprocessingEnabled() {
664
MOZ_ASSERT(gDBManager,
665
"PreprocessingEnabled() called before indexedDB has been "
666
"initialized!");
667
668
return gPreprocessingEnabled;
669
}
670
671
// static
672
int32_t IndexedDatabaseManager::MaxPreloadExtraRecords() {
673
MOZ_ASSERT(gDBManager,
674
"MaxPreloadExtraRecords() called before indexedDB has been "
675
"initialized!");
676
677
return gMaxPreloadExtraRecords;
678
}
679
680
void IndexedDatabaseManager::ClearBackgroundActor() {
681
MOZ_ASSERT(NS_IsMainThread());
682
683
mBackgroundActor = nullptr;
684
}
685
686
already_AddRefed<FileManager> IndexedDatabaseManager::GetFileManager(
687
PersistenceType aPersistenceType, const nsACString& aOrigin,
688
const nsAString& aDatabaseName) {
689
AssertIsOnIOThread();
690
691
FileManagerInfo* info;
692
if (!mFileManagerInfos.Get(aOrigin, &info)) {
693
return nullptr;
694
}
695
696
RefPtr<FileManager> fileManager =
697
info->GetFileManager(aPersistenceType, aDatabaseName);
698
699
return fileManager.forget();
700
}
701
702
void IndexedDatabaseManager::AddFileManager(FileManager* aFileManager) {
703
AssertIsOnIOThread();
704
NS_ASSERTION(aFileManager, "Null file manager!");
705
706
FileManagerInfo* info;
707
if (!mFileManagerInfos.Get(aFileManager->Origin(), &info)) {
708
info = new FileManagerInfo();
709
mFileManagerInfos.Put(aFileManager->Origin(), info);
710
}
711
712
info->AddFileManager(aFileManager);
713
}
714
715
void IndexedDatabaseManager::InvalidateAllFileManagers() {
716
AssertIsOnIOThread();
717
718
for (auto iter = mFileManagerInfos.ConstIter(); !iter.Done(); iter.Next()) {
719
auto value = iter.Data();
720
MOZ_ASSERT(value);
721
722
value->InvalidateAllFileManagers();
723
}
724
725
mFileManagerInfos.Clear();
726
}
727
728
void IndexedDatabaseManager::InvalidateFileManagers(
729
PersistenceType aPersistenceType, const nsACString& aOrigin) {
730
AssertIsOnIOThread();
731
MOZ_ASSERT(!aOrigin.IsEmpty());
732
733
FileManagerInfo* info;
734
if (!mFileManagerInfos.Get(aOrigin, &info)) {
735
return;
736
}
737
738
info->InvalidateAndRemoveFileManagers(aPersistenceType);
739
740
if (!info->HasFileManagers()) {
741
mFileManagerInfos.Remove(aOrigin);
742
}
743
}
744
745
void IndexedDatabaseManager::InvalidateFileManager(
746
PersistenceType aPersistenceType, const nsACString& aOrigin,
747
const nsAString& aDatabaseName) {
748
AssertIsOnIOThread();
749
750
FileManagerInfo* info;
751
if (!mFileManagerInfos.Get(aOrigin, &info)) {
752
return;
753
}
754
755
info->InvalidateAndRemoveFileManager(aPersistenceType, aDatabaseName);
756
757
if (!info->HasFileManagers()) {
758
mFileManagerInfos.Remove(aOrigin);
759
}
760
}
761
762
nsresult IndexedDatabaseManager::BlockAndGetFileReferences(
763
PersistenceType aPersistenceType, const nsACString& aOrigin,
764
const nsAString& aDatabaseName, int64_t aFileId, int32_t* aRefCnt,
765
int32_t* aDBRefCnt, int32_t* aSliceRefCnt, bool* aResult) {
766
MOZ_ASSERT(NS_IsMainThread());
767
768
if (NS_WARN_IF(!InTestingMode())) {
769
return NS_ERROR_UNEXPECTED;
770
}
771
772
if (!mBackgroundActor) {
773
PBackgroundChild* bgActor = BackgroundChild::GetForCurrentThread();
774
if (NS_WARN_IF(!bgActor)) {
775
return NS_ERROR_FAILURE;
776
}
777
778
BackgroundUtilsChild* actor = new BackgroundUtilsChild(this);
779
780
// We don't set event target for BackgroundUtilsChild because:
781
// 1. BackgroundUtilsChild is a singleton.
782
// 2. SendGetFileReferences is a sync operation to be returned asap if
783
// unlabeled.
784
// 3. The rest operations like DeleteMe/__delete__ only happens at shutdown.
785
// Hence, we should keep it unlabeled.
786
mBackgroundActor = static_cast<BackgroundUtilsChild*>(
787
bgActor->SendPBackgroundIndexedDBUtilsConstructor(actor));
788
}
789
790
if (NS_WARN_IF(!mBackgroundActor)) {
791
return NS_ERROR_FAILURE;
792
}
793
794
if (!mBackgroundActor->SendGetFileReferences(
795
aPersistenceType, nsCString(aOrigin), nsString(aDatabaseName),
796
aFileId, aRefCnt, aDBRefCnt, aSliceRefCnt, aResult)) {
797
return NS_ERROR_FAILURE;
798
}
799
800
return NS_OK;
801
}
802
803
nsresult IndexedDatabaseManager::FlushPendingFileDeletions() {
804
MOZ_ASSERT(NS_IsMainThread());
805
806
if (NS_WARN_IF(!InTestingMode())) {
807
return NS_ERROR_UNEXPECTED;
808
}
809
810
PBackgroundChild* bgActor = BackgroundChild::GetForCurrentThread();
811
if (NS_WARN_IF(!bgActor)) {
812
return NS_ERROR_FAILURE;
813
}
814
815
if (!bgActor->SendFlushPendingFileDeletions()) {
816
return NS_ERROR_FAILURE;
817
}
818
819
return NS_OK;
820
}
821
822
// static
823
void IndexedDatabaseManager::LoggingModePrefChangedCallback(
824
const char* /* aPrefName */, void* /* aClosure */) {
825
MOZ_ASSERT(NS_IsMainThread());
826
827
if (!Preferences::GetBool(kPrefLoggingEnabled)) {
828
sLoggingMode = Logging_Disabled;
829
return;
830
}
831
832
bool useProfiler =
833
#if defined(DEBUG) || defined(MOZ_GECKO_PROFILER)
834
Preferences::GetBool(kPrefLoggingProfiler);
835
# if !defined(MOZ_GECKO_PROFILER)
836
if (useProfiler) {
837
NS_WARNING(
838
"IndexedDB cannot create profiler marks because this build does "
839
"not have profiler extensions enabled!");
840
useProfiler = false;
841
}
842
# endif
843
#else
844
false;
845
#endif
846
847
const bool logDetails = Preferences::GetBool(kPrefLoggingDetails);
848
849
if (useProfiler) {
850
sLoggingMode = logDetails ? Logging_DetailedProfilerMarks
851
: Logging_ConciseProfilerMarks;
852
} else {
853
sLoggingMode = logDetails ? Logging_Detailed : Logging_Concise;
854
}
855
}
856
857
// static
858
const nsCString& IndexedDatabaseManager::GetLocale() {
859
IndexedDatabaseManager* idbManager = Get();
860
MOZ_ASSERT(idbManager, "IDBManager is not ready!");
861
862
return idbManager->mLocale;
863
}
864
865
already_AddRefed<FileManager> FileManagerInfo::GetFileManager(
866
PersistenceType aPersistenceType, const nsAString& aName) const {
867
AssertIsOnIOThread();
868
869
const auto& managers = GetImmutableArray(aPersistenceType);
870
871
const auto end = managers.cend();
872
const auto foundIt =
873
std::find_if(managers.cbegin(), end, DatabaseNameMatchPredicate(&aName));
874
875
return foundIt != end ? RefPtr<FileManager>{*foundIt}.forget() : nullptr;
876
}
877
878
void FileManagerInfo::AddFileManager(FileManager* aFileManager) {
879
AssertIsOnIOThread();
880
881
nsTArray<RefPtr<FileManager> >& managers = GetArray(aFileManager->Type());
882
883
NS_ASSERTION(!managers.Contains(aFileManager), "Adding more than once?!");
884
885
managers.AppendElement(aFileManager);
886
}
887
888
void FileManagerInfo::InvalidateAllFileManagers() const {
889
AssertIsOnIOThread();
890
891
uint32_t i;
892
893
for (i = 0; i < mPersistentStorageFileManagers.Length(); i++) {
894
mPersistentStorageFileManagers[i]->Invalidate();
895
}
896
897
for (i = 0; i < mTemporaryStorageFileManagers.Length(); i++) {
898
mTemporaryStorageFileManagers[i]->Invalidate();
899
}
900
901
for (i = 0; i < mDefaultStorageFileManagers.Length(); i++) {
902
mDefaultStorageFileManagers[i]->Invalidate();
903
}
904
}
905
906
void FileManagerInfo::InvalidateAndRemoveFileManagers(
907
PersistenceType aPersistenceType) {
908
AssertIsOnIOThread();
909
910
nsTArray<RefPtr<FileManager> >& managers = GetArray(aPersistenceType);
911
912
for (uint32_t i = 0; i < managers.Length(); i++) {
913
managers[i]->Invalidate();
914
}
915
916
managers.Clear();
917
}
918
919
void FileManagerInfo::InvalidateAndRemoveFileManager(
920
PersistenceType aPersistenceType, const nsAString& aName) {
921
AssertIsOnIOThread();
922
923
auto& managers = GetArray(aPersistenceType);
924
const auto end = managers.cend();
925
const auto foundIt =
926
std::find_if(managers.cbegin(), end, DatabaseNameMatchPredicate(&aName));
927
928
if (foundIt != end) {
929
(*foundIt)->Invalidate();
930
managers.RemoveElementAt(foundIt.GetIndex());
931
}
932
}
933
934
nsTArray<RefPtr<FileManager> >& FileManagerInfo::GetArray(
935
PersistenceType aPersistenceType) {
936
switch (aPersistenceType) {
937
case PERSISTENCE_TYPE_PERSISTENT:
938
return mPersistentStorageFileManagers;
939
case PERSISTENCE_TYPE_TEMPORARY:
940
return mTemporaryStorageFileManagers;
941
case PERSISTENCE_TYPE_DEFAULT:
942
return mDefaultStorageFileManagers;
943
944
case PERSISTENCE_TYPE_INVALID:
945
default:
946
MOZ_CRASH("Bad storage type value!");
947
}
948
}
949
950
} // namespace dom
951
} // namespace mozilla