Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/indexedDB/IndexedDatabaseManager.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 "IndexedDatabaseManager.h"
8
9
#include "chrome/common/ipc_channel.h" // for IPC::Channel::kMaximumMessageSize
10
#include "nsIConsoleService.h"
11
#include "nsIDOMWindow.h"
12
#include "nsIEventTarget.h"
13
#include "nsIFile.h"
14
#include "nsIScriptError.h"
15
#include "nsIScriptGlobalObject.h"
16
17
#include "jsapi.h"
18
#include "mozilla/ClearOnShutdown.h"
19
#include "mozilla/CondVar.h"
20
#include "mozilla/ContentEvents.h"
21
#include "mozilla/EventDispatcher.h"
22
#include "mozilla/Preferences.h"
23
#include "mozilla/Services.h"
24
#include "mozilla/dom/DOMException.h"
25
#include "mozilla/dom/ErrorEvent.h"
26
#include "mozilla/dom/ErrorEventBinding.h"
27
#include "mozilla/dom/quota/QuotaManager.h"
28
#include "mozilla/dom/WorkerScope.h"
29
#include "mozilla/dom/WorkerPrivate.h"
30
#include "mozilla/ipc/BackgroundChild.h"
31
#include "mozilla/ipc/BackgroundParent.h"
32
#include "mozilla/ipc/PBackgroundChild.h"
33
#include "nsContentUtils.h"
34
#include "nsGlobalWindow.h"
35
#include "nsThreadUtils.h"
36
#include "mozilla/Logging.h"
37
38
#include "FileInfo.h"
39
#include "FileManager.h"
40
#include "IDBEvents.h"
41
#include "IDBFactory.h"
42
#include "IDBKeyRange.h"
43
#include "IDBRequest.h"
44
#include "ProfilerHelpers.h"
45
#include "ScriptErrorHelper.h"
46
#include "nsCharSeparatedTokenizer.h"
47
#include "unicode/locid.h"
48
49
// Bindings for ResolveConstructors
50
#include "mozilla/dom/IDBCursorBinding.h"
51
#include "mozilla/dom/IDBDatabaseBinding.h"
52
#include "mozilla/dom/IDBFactoryBinding.h"
53
#include "mozilla/dom/IDBIndexBinding.h"
54
#include "mozilla/dom/IDBKeyRangeBinding.h"
55
#include "mozilla/dom/IDBMutableFileBinding.h"
56
#include "mozilla/dom/IDBObjectStoreBinding.h"
57
#include "mozilla/dom/IDBOpenDBRequestBinding.h"
58
#include "mozilla/dom/IDBRequestBinding.h"
59
#include "mozilla/dom/IDBTransactionBinding.h"
60
#include "mozilla/dom/IDBVersionChangeEventBinding.h"
61
62
0
#define IDB_STR "indexedDB"
63
64
namespace mozilla {
65
namespace dom {
66
namespace indexedDB {
67
68
using namespace mozilla::dom::quota;
69
using namespace mozilla::ipc;
70
71
class FileManagerInfo
72
{
73
public:
74
  already_AddRefed<FileManager>
75
  GetFileManager(PersistenceType aPersistenceType,
76
                 const nsAString& aName) const;
77
78
  void
79
  AddFileManager(FileManager* aFileManager);
80
81
  bool
82
  HasFileManagers() const
83
0
  {
84
0
    AssertIsOnIOThread();
85
0
86
0
    return !mPersistentStorageFileManagers.IsEmpty() ||
87
0
           !mTemporaryStorageFileManagers.IsEmpty() ||
88
0
           !mDefaultStorageFileManagers.IsEmpty();
89
0
  }
90
91
  void
92
  InvalidateAllFileManagers() const;
93
94
  void
95
  InvalidateAndRemoveFileManagers(PersistenceType aPersistenceType);
96
97
  void
98
  InvalidateAndRemoveFileManager(PersistenceType aPersistenceType,
99
                                 const nsAString& aName);
100
101
private:
102
  nsTArray<RefPtr<FileManager> >&
103
  GetArray(PersistenceType aPersistenceType);
104
105
  const nsTArray<RefPtr<FileManager> >&
106
  GetImmutableArray(PersistenceType aPersistenceType) const
107
0
  {
108
0
    return const_cast<FileManagerInfo*>(this)->GetArray(aPersistenceType);
109
0
  }
110
111
  nsTArray<RefPtr<FileManager> > mPersistentStorageFileManagers;
112
  nsTArray<RefPtr<FileManager> > mTemporaryStorageFileManagers;
113
  nsTArray<RefPtr<FileManager> > mDefaultStorageFileManagers;
114
};
115
116
} // namespace indexedDB
117
118
using namespace mozilla::dom::indexedDB;
119
120
namespace {
121
122
NS_DEFINE_IID(kIDBRequestIID, PRIVATE_IDBREQUEST_IID);
123
124
const uint32_t kDeleteTimeoutMs = 1000;
125
126
// The threshold we use for structured clone data storing.
127
// Anything smaller than the threshold is compressed and stored in the database.
128
// Anything larger is compressed and stored outside the database.
129
const int32_t kDefaultDataThresholdBytes = 1024 * 1024; // 1MB
130
131
// The maximal size of a serialized object to be transfered through IPC.
132
const int32_t kDefaultMaxSerializedMsgSize = IPC::Channel::kMaximumMessageSize;
133
134
#define IDB_PREF_BRANCH_ROOT "dom.indexedDB."
135
136
const char kTestingPref[] = IDB_PREF_BRANCH_ROOT "testing";
137
const char kPrefExperimental[] = IDB_PREF_BRANCH_ROOT "experimental";
138
const char kPrefFileHandle[] = "dom.fileHandle.enabled";
139
const char kDataThresholdPref[] = IDB_PREF_BRANCH_ROOT "dataThreshold";
140
const char kPrefMaxSerilizedMsgSize[] = IDB_PREF_BRANCH_ROOT "maxSerializedMsgSize";
141
const char kPrefErrorEventToSelfError[] = IDB_PREF_BRANCH_ROOT "errorEventToSelfError";
142
143
#define IDB_PREF_LOGGING_BRANCH_ROOT IDB_PREF_BRANCH_ROOT "logging."
144
145
const char kPrefLoggingEnabled[] = IDB_PREF_LOGGING_BRANCH_ROOT "enabled";
146
const char kPrefLoggingDetails[] = IDB_PREF_LOGGING_BRANCH_ROOT "details";
147
148
#if defined(DEBUG) || defined(MOZ_GECKO_PROFILER)
149
const char kPrefLoggingProfiler[] =
150
  IDB_PREF_LOGGING_BRANCH_ROOT "profiler-marks";
151
#endif
152
153
#undef IDB_PREF_LOGGING_BRANCH_ROOT
154
#undef IDB_PREF_BRANCH_ROOT
155
156
StaticRefPtr<IndexedDatabaseManager> gDBManager;
157
158
Atomic<bool> gInitialized(false);
159
Atomic<bool> gClosed(false);
160
Atomic<bool> gTestingMode(false);
161
Atomic<bool> gExperimentalFeaturesEnabled(false);
162
Atomic<bool> gFileHandleEnabled(false);
163
Atomic<bool> gPrefErrorEventToSelfError(false);
164
Atomic<int32_t> gDataThresholdBytes(0);
165
Atomic<int32_t> gMaxSerializedMsgSize(0);
166
167
class DeleteFilesRunnable final
168
  : public nsIRunnable
169
  , public OpenDirectoryListener
170
{
171
  typedef mozilla::dom::quota::DirectoryLock DirectoryLock;
172
173
  enum State
174
  {
175
    // Just created on the main thread. Next step is State_DirectoryOpenPending.
176
    State_Initial,
177
178
    // Waiting for directory open allowed on the main thread. The next step is
179
    // State_DatabaseWorkOpen.
180
    State_DirectoryOpenPending,
181
182
    // Waiting to do/doing work on the QuotaManager IO thread. The next step is
183
    // State_UnblockingOpen.
184
    State_DatabaseWorkOpen,
185
186
    // Notifying the QuotaManager that it can proceed to the next operation on
187
    // the main thread. Next step is State_Completed.
188
    State_UnblockingOpen,
189
190
    // All done.
191
    State_Completed
192
  };
193
194
  nsCOMPtr<nsIEventTarget> mBackgroundThread;
195
196
  RefPtr<FileManager> mFileManager;
197
  nsTArray<int64_t> mFileIds;
198
199
  RefPtr<DirectoryLock> mDirectoryLock;
200
201
  nsCOMPtr<nsIFile> mDirectory;
202
  nsCOMPtr<nsIFile> mJournalDirectory;
203
204
  State mState;
205
206
public:
207
  DeleteFilesRunnable(nsIEventTarget* aBackgroundThread,
208
                      FileManager* aFileManager,
209
                      nsTArray<int64_t>& aFileIds);
210
211
  void
212
  Dispatch();
213
214
  NS_DECL_THREADSAFE_ISUPPORTS
215
  NS_DECL_NSIRUNNABLE
216
217
  virtual void
218
  DirectoryLockAcquired(DirectoryLock* aLock) override;
219
220
  virtual void
221
  DirectoryLockFailed() override;
222
223
private:
224
0
  ~DeleteFilesRunnable() {}
225
226
  nsresult
227
  Open();
228
229
  nsresult
230
  DeleteFile(int64_t aFileId);
231
232
  nsresult
233
  DoDatabaseWork();
234
235
  void
236
  Finish();
237
238
  void
239
  UnblockOpen();
240
};
241
242
void
243
AtomicBoolPrefChangedCallback(const char* aPrefName, Atomic<bool>* aClosure)
244
0
{
245
0
  MOZ_ASSERT(NS_IsMainThread());
246
0
  MOZ_ASSERT(aClosure);
247
0
248
0
  *aClosure = Preferences::GetBool(aPrefName);
249
0
}
250
251
void
252
DataThresholdPrefChangedCallback(const char* aPrefName, void* aClosure)
253
0
{
254
0
  MOZ_ASSERT(NS_IsMainThread());
255
0
  MOZ_ASSERT(!strcmp(aPrefName, kDataThresholdPref));
256
0
  MOZ_ASSERT(!aClosure);
257
0
258
0
  int32_t dataThresholdBytes =
259
0
    Preferences::GetInt(aPrefName, kDefaultDataThresholdBytes);
260
0
261
0
  // The magic -1 is for use only by tests that depend on stable blob file id's.
262
0
  if (dataThresholdBytes == -1) {
263
0
    dataThresholdBytes = INT32_MAX;
264
0
  }
265
0
266
0
  gDataThresholdBytes = dataThresholdBytes;
267
0
}
268
269
void
270
MaxSerializedMsgSizePrefChangeCallback(const char* aPrefName, void* aClosure)
271
0
{
272
0
  MOZ_ASSERT(NS_IsMainThread());
273
0
  MOZ_ASSERT(!strcmp(aPrefName, kPrefMaxSerilizedMsgSize));
274
0
  MOZ_ASSERT(!aClosure);
275
0
276
0
  gMaxSerializedMsgSize =
277
0
    Preferences::GetInt(aPrefName, kDefaultMaxSerializedMsgSize);
278
0
  MOZ_ASSERT(gMaxSerializedMsgSize > 0);
279
0
}
280
281
} // namespace
282
283
IndexedDatabaseManager::IndexedDatabaseManager()
284
  : mFileMutex("IndexedDatabaseManager.mFileMutex")
285
  , mBackgroundActor(nullptr)
286
0
{
287
0
  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
288
0
}
289
290
IndexedDatabaseManager::~IndexedDatabaseManager()
291
0
{
292
0
  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
293
0
294
0
  if (mBackgroundActor) {
295
0
    mBackgroundActor->SendDeleteMeInternal();
296
0
    MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!");
297
0
  }
298
0
}
299
300
bool IndexedDatabaseManager::sIsMainProcess = false;
301
bool IndexedDatabaseManager::sFullSynchronousMode = false;
302
303
mozilla::LazyLogModule IndexedDatabaseManager::sLoggingModule("IndexedDB");
304
305
Atomic<IndexedDatabaseManager::LoggingMode>
306
  IndexedDatabaseManager::sLoggingMode(
307
    IndexedDatabaseManager::Logging_Disabled);
308
309
// static
310
IndexedDatabaseManager*
311
IndexedDatabaseManager::GetOrCreate()
312
0
{
313
0
  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
314
0
315
0
  if (IsClosed()) {
316
0
    NS_ERROR("Calling GetOrCreate() after shutdown!");
317
0
    return nullptr;
318
0
  }
319
0
320
0
  if (!gDBManager) {
321
0
    sIsMainProcess = XRE_IsParentProcess();
322
0
323
0
    RefPtr<IndexedDatabaseManager> instance(new IndexedDatabaseManager());
324
0
325
0
    nsresult rv = instance->Init();
326
0
    NS_ENSURE_SUCCESS(rv, nullptr);
327
0
328
0
    if (gInitialized.exchange(true)) {
329
0
      NS_ERROR("Initialized more than once?!");
330
0
    }
331
0
332
0
    gDBManager = instance;
333
0
334
0
    ClearOnShutdown(&gDBManager);
335
0
  }
336
0
337
0
  return gDBManager;
338
0
}
339
340
// static
341
IndexedDatabaseManager*
342
IndexedDatabaseManager::Get()
343
0
{
344
0
  // Does not return an owning reference.
345
0
  return gDBManager;
346
0
}
347
348
nsresult
349
IndexedDatabaseManager::Init()
350
0
{
351
0
  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
352
0
353
0
  // During Init() we can't yet call IsMainProcess(), just check sIsMainProcess
354
0
  // directly.
355
0
  if (sIsMainProcess) {
356
0
    mDeleteTimer = NS_NewTimer();
357
0
    NS_ENSURE_STATE(mDeleteTimer);
358
0
359
0
    if (QuotaManager* quotaManager = QuotaManager::Get()) {
360
0
      NoteLiveQuotaManager(quotaManager);
361
0
    }
362
0
  }
363
0
364
0
  Preferences::RegisterCallbackAndCall(AtomicBoolPrefChangedCallback,
365
0
                                       kTestingPref,
366
0
                                       &gTestingMode);
367
0
  Preferences::RegisterCallbackAndCall(AtomicBoolPrefChangedCallback,
368
0
                                       kPrefExperimental,
369
0
                                       &gExperimentalFeaturesEnabled);
370
0
  Preferences::RegisterCallbackAndCall(AtomicBoolPrefChangedCallback,
371
0
                                       kPrefFileHandle,
372
0
                                       &gFileHandleEnabled);
373
0
  Preferences::RegisterCallbackAndCall(AtomicBoolPrefChangedCallback,
374
0
                                       kPrefErrorEventToSelfError,
375
0
                                       &gPrefErrorEventToSelfError);
376
0
377
0
  // By default IndexedDB uses SQLite with PRAGMA synchronous = NORMAL. This
378
0
  // guarantees (unlike synchronous = OFF) atomicity and consistency, but not
379
0
  // necessarily durability in situations such as power loss. This preference
380
0
  // allows enabling PRAGMA synchronous = FULL on SQLite, which does guarantee
381
0
  // durability, but with an extra fsync() and the corresponding performance
382
0
  // hit.
383
0
  sFullSynchronousMode = Preferences::GetBool("dom.indexedDB.fullSynchronous");
384
0
385
0
  Preferences::RegisterCallback(LoggingModePrefChangedCallback,
386
0
                                kPrefLoggingDetails);
387
0
#ifdef MOZ_GECKO_PROFILER
388
0
  Preferences::RegisterCallback(LoggingModePrefChangedCallback,
389
0
                                kPrefLoggingProfiler);
390
0
#endif
391
0
  Preferences::RegisterCallbackAndCall(LoggingModePrefChangedCallback,
392
0
                                       kPrefLoggingEnabled);
393
0
394
0
  Preferences::RegisterCallbackAndCall(DataThresholdPrefChangedCallback,
395
0
                                       kDataThresholdPref);
396
0
397
0
  Preferences::RegisterCallbackAndCall(MaxSerializedMsgSizePrefChangeCallback,
398
0
                                       kPrefMaxSerilizedMsgSize);
399
0
400
0
  nsAutoCString acceptLang;
401
0
  Preferences::GetLocalizedCString("intl.accept_languages", acceptLang);
402
0
403
0
  // Split values on commas.
404
0
  nsCCharSeparatedTokenizer langTokenizer(acceptLang, ',');
405
0
  while (langTokenizer.hasMoreTokens()) {
406
0
    nsAutoCString lang(langTokenizer.nextToken());
407
0
    icu::Locale locale = icu::Locale::createCanonical(lang.get());
408
0
    if (!locale.isBogus()) {
409
0
      // icu::Locale::getBaseName is always ASCII as per BCP 47
410
0
      mLocale.AssignASCII(locale.getBaseName());
411
0
      break;
412
0
    }
413
0
  }
414
0
415
0
  if (mLocale.IsEmpty()) {
416
0
    mLocale.AssignLiteral("en_US");
417
0
  }
418
0
419
0
  return NS_OK;
420
0
}
421
422
void
423
IndexedDatabaseManager::Destroy()
424
0
{
425
0
  // Setting the closed flag prevents the service from being recreated.
426
0
  // Don't set it though if there's no real instance created.
427
0
  if (gInitialized && gClosed.exchange(true)) {
428
0
    NS_ERROR("Shutdown more than once?!");
429
0
  }
430
0
431
0
  if (sIsMainProcess && mDeleteTimer) {
432
0
    if (NS_FAILED(mDeleteTimer->Cancel())) {
433
0
      NS_WARNING("Failed to cancel timer!");
434
0
    }
435
0
436
0
    mDeleteTimer = nullptr;
437
0
  }
438
0
439
0
  Preferences::UnregisterCallback(AtomicBoolPrefChangedCallback,
440
0
                                  kTestingPref,
441
0
                                  &gTestingMode);
442
0
  Preferences::UnregisterCallback(AtomicBoolPrefChangedCallback,
443
0
                                  kPrefExperimental,
444
0
                                  &gExperimentalFeaturesEnabled);
445
0
  Preferences::UnregisterCallback(AtomicBoolPrefChangedCallback,
446
0
                                  kPrefFileHandle,
447
0
                                  &gFileHandleEnabled);
448
0
  Preferences::UnregisterCallback(AtomicBoolPrefChangedCallback,
449
0
                                  kPrefErrorEventToSelfError,
450
0
                                  &gPrefErrorEventToSelfError);
451
0
452
0
  Preferences::UnregisterCallback(LoggingModePrefChangedCallback,
453
0
                                  kPrefLoggingDetails);
454
0
#ifdef MOZ_GECKO_PROFILER
455
0
  Preferences::UnregisterCallback(LoggingModePrefChangedCallback,
456
0
                                  kPrefLoggingProfiler);
457
0
#endif
458
0
  Preferences::UnregisterCallback(LoggingModePrefChangedCallback,
459
0
                                  kPrefLoggingEnabled);
460
0
461
0
  Preferences::UnregisterCallback(DataThresholdPrefChangedCallback,
462
0
                                  kDataThresholdPref);
463
0
464
0
  Preferences::UnregisterCallback(MaxSerializedMsgSizePrefChangeCallback,
465
0
                                  kPrefMaxSerilizedMsgSize);
466
0
467
0
  delete this;
468
0
}
469
470
// static
471
nsresult
472
IndexedDatabaseManager::CommonPostHandleEvent(EventChainPostVisitor& aVisitor,
473
                                              IDBFactory* aFactory)
474
0
{
475
0
  MOZ_ASSERT(aVisitor.mDOMEvent);
476
0
  MOZ_ASSERT(aFactory);
477
0
478
0
  if (!gPrefErrorEventToSelfError) {
479
0
    return NS_OK;
480
0
  }
481
0
482
0
  if (aVisitor.mEventStatus == nsEventStatus_eConsumeNoDefault) {
483
0
    return NS_OK;
484
0
  }
485
0
486
0
  if (!aVisitor.mDOMEvent->IsTrusted()) {
487
0
    return NS_OK;
488
0
  }
489
0
490
0
  nsAutoString type;
491
0
  aVisitor.mDOMEvent->GetType(type);
492
0
493
0
  MOZ_ASSERT(nsDependentString(kErrorEventType).EqualsLiteral("error"));
494
0
  if (!type.EqualsLiteral("error")) {
495
0
    return NS_OK;
496
0
  }
497
0
498
0
  nsCOMPtr<EventTarget> eventTarget = aVisitor.mDOMEvent->GetTarget();
499
0
  MOZ_ASSERT(eventTarget);
500
0
501
0
  // Only mess with events that were originally targeted to an IDBRequest.
502
0
  RefPtr<IDBRequest> request;
503
0
  if (NS_FAILED(eventTarget->QueryInterface(kIDBRequestIID,
504
0
                                            getter_AddRefs(request))) ||
505
0
      !request) {
506
0
    return NS_OK;
507
0
  }
508
0
509
0
  RefPtr<DOMException> error = request->GetErrorAfterResult();
510
0
511
0
  nsString errorName;
512
0
  if (error) {
513
0
    error->GetName(errorName);
514
0
  }
515
0
516
0
  RootedDictionary<ErrorEventInit> init(RootingCx());
517
0
  request->GetCallerLocation(init.mFilename, &init.mLineno, &init.mColno);
518
0
519
0
  init.mMessage = errorName;
520
0
  init.mCancelable = true;
521
0
  init.mBubbles = true;
522
0
523
0
  nsEventStatus status = nsEventStatus_eIgnore;
524
0
525
0
  if (NS_IsMainThread()) {
526
0
    nsCOMPtr<nsIDOMWindow> window = do_QueryInterface(eventTarget->GetOwnerGlobal());
527
0
    if (window) {
528
0
      nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(window);
529
0
      MOZ_ASSERT(sgo);
530
0
531
0
      if (NS_WARN_IF(!sgo->HandleScriptError(init, &status))) {
532
0
        status = nsEventStatus_eIgnore;
533
0
      }
534
0
    } else {
535
0
      // We don't fire error events at any global for non-window JS on the main
536
0
      // thread.
537
0
    }
538
0
  } else {
539
0
    // Not on the main thread, must be in a worker.
540
0
    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
541
0
    MOZ_ASSERT(workerPrivate);
542
0
543
0
    RefPtr<WorkerGlobalScope> globalScope = workerPrivate->GlobalScope();
544
0
    MOZ_ASSERT(globalScope);
545
0
546
0
    RefPtr<ErrorEvent> errorEvent =
547
0
      ErrorEvent::Constructor(globalScope,
548
0
                              nsDependentString(kErrorEventType),
549
0
                              init);
550
0
    MOZ_ASSERT(errorEvent);
551
0
552
0
    errorEvent->SetTrusted(true);
553
0
554
0
    auto* target = static_cast<EventTarget*>(globalScope.get());
555
0
556
0
    if (NS_WARN_IF(NS_FAILED(
557
0
      EventDispatcher::DispatchDOMEvent(target,
558
0
                                        /* aWidgetEvent */ nullptr,
559
0
                                        errorEvent,
560
0
                                        /* aPresContext */ nullptr,
561
0
                                        &status)))) {
562
0
      status = nsEventStatus_eIgnore;
563
0
    }
564
0
  }
565
0
566
0
  if (status == nsEventStatus_eConsumeNoDefault) {
567
0
    return NS_OK;
568
0
  }
569
0
570
0
  // Log the error to the error console.
571
0
  ScriptErrorHelper::Dump(errorName,
572
0
                          init.mFilename,
573
0
                          init.mLineno,
574
0
                          init.mColno,
575
0
                          nsIScriptError::errorFlag,
576
0
                          aFactory->IsChrome(),
577
0
                          aFactory->InnerWindowID());
578
0
579
0
  return NS_OK;
580
0
}
581
582
// static
583
bool
584
IndexedDatabaseManager::ResolveSandboxBinding(JSContext* aCx)
585
0
{
586
0
  MOZ_ASSERT(NS_IsMainThread());
587
0
  MOZ_ASSERT(js::GetObjectClass(JS::CurrentGlobalOrNull(aCx))->flags &
588
0
             JSCLASS_DOM_GLOBAL,
589
0
             "Passed object is not a global object!");
590
0
591
0
  // We need to ensure that the manager has been created already here so that we
592
0
  // load preferences that may control which properties are exposed.
593
0
  if (NS_WARN_IF(!GetOrCreate())) {
594
0
    return false;
595
0
  }
596
0
597
0
  if (!IDBCursor_Binding::GetConstructorObject(aCx) ||
598
0
      !IDBCursorWithValue_Binding::GetConstructorObject(aCx) ||
599
0
      !IDBDatabase_Binding::GetConstructorObject(aCx) ||
600
0
      !IDBFactory_Binding::GetConstructorObject(aCx) ||
601
0
      !IDBIndex_Binding::GetConstructorObject(aCx) ||
602
0
      !IDBKeyRange_Binding::GetConstructorObject(aCx) ||
603
0
      !IDBLocaleAwareKeyRange_Binding::GetConstructorObject(aCx) ||
604
0
      !IDBMutableFile_Binding::GetConstructorObject(aCx) ||
605
0
      !IDBObjectStore_Binding::GetConstructorObject(aCx) ||
606
0
      !IDBOpenDBRequest_Binding::GetConstructorObject(aCx) ||
607
0
      !IDBRequest_Binding::GetConstructorObject(aCx) ||
608
0
      !IDBTransaction_Binding::GetConstructorObject(aCx) ||
609
0
      !IDBVersionChangeEvent_Binding::GetConstructorObject(aCx))
610
0
  {
611
0
    return false;
612
0
  }
613
0
614
0
  return true;
615
0
}
616
617
// static
618
bool
619
IndexedDatabaseManager::DefineIndexedDB(JSContext* aCx,
620
                                        JS::Handle<JSObject*> aGlobal)
621
0
{
622
0
  MOZ_ASSERT(NS_IsMainThread());
623
0
  MOZ_ASSERT(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL,
624
0
             "Passed object is not a global object!");
625
0
626
0
  RefPtr<IDBFactory> factory;
627
0
  if (NS_FAILED(IDBFactory::CreateForMainThreadJS(aCx,
628
0
                                                  aGlobal,
629
0
                                                  getter_AddRefs(factory)))) {
630
0
    return false;
631
0
  }
632
0
633
0
  MOZ_ASSERT(factory, "This should never fail for chrome!");
634
0
635
0
  JS::Rooted<JS::Value> indexedDB(aCx);
636
0
  js::AssertSameCompartment(aCx, aGlobal);
637
0
  if (!GetOrCreateDOMReflector(aCx, factory, &indexedDB)) {
638
0
    return false;
639
0
  }
640
0
641
0
  return JS_DefineProperty(aCx, aGlobal, IDB_STR, indexedDB, JSPROP_ENUMERATE);
642
0
}
643
644
// static
645
bool
646
IndexedDatabaseManager::IsClosed()
647
0
{
648
0
  return gClosed;
649
0
}
650
651
#ifdef DEBUG
652
// static
653
bool
654
IndexedDatabaseManager::IsMainProcess()
655
{
656
  NS_ASSERTION(gDBManager,
657
               "IsMainProcess() called before indexedDB has been initialized!");
658
  NS_ASSERTION((XRE_IsParentProcess()) ==
659
               sIsMainProcess, "XRE_GetProcessType changed its tune!");
660
  return sIsMainProcess;
661
}
662
663
// static
664
IndexedDatabaseManager::LoggingMode
665
IndexedDatabaseManager::GetLoggingMode()
666
{
667
  MOZ_ASSERT(gDBManager,
668
             "GetLoggingMode called before IndexedDatabaseManager has been "
669
             "initialized!");
670
671
  return sLoggingMode;
672
}
673
674
// static
675
mozilla::LogModule*
676
IndexedDatabaseManager::GetLoggingModule()
677
{
678
  MOZ_ASSERT(gDBManager,
679
             "GetLoggingModule called before IndexedDatabaseManager has been "
680
             "initialized!");
681
682
  return sLoggingModule;
683
}
684
685
#endif // DEBUG
686
687
// static
688
bool
689
IndexedDatabaseManager::InTestingMode()
690
0
{
691
0
  MOZ_ASSERT(gDBManager,
692
0
             "InTestingMode() called before indexedDB has been initialized!");
693
0
694
0
  return gTestingMode;
695
0
}
696
697
// static
698
bool
699
IndexedDatabaseManager::FullSynchronous()
700
0
{
701
0
  MOZ_ASSERT(gDBManager,
702
0
             "FullSynchronous() called before indexedDB has been initialized!");
703
0
704
0
  return sFullSynchronousMode;
705
0
}
706
707
// static
708
bool
709
IndexedDatabaseManager::ExperimentalFeaturesEnabled()
710
0
{
711
0
  if (NS_IsMainThread()) {
712
0
    if (NS_WARN_IF(!GetOrCreate())) {
713
0
      return false;
714
0
    }
715
0
  } else {
716
0
    MOZ_ASSERT(Get(),
717
0
               "ExperimentalFeaturesEnabled() called off the main thread "
718
0
               "before indexedDB has been initialized!");
719
0
  }
720
0
721
0
  return gExperimentalFeaturesEnabled;
722
0
}
723
724
// static
725
bool
726
IndexedDatabaseManager::ExperimentalFeaturesEnabled(JSContext* aCx, JSObject* aGlobal)
727
0
{
728
0
  // If, in the child process, properties of the global object are enumerated
729
0
  // before the chrome registry (and thus the value of |intl.accept_languages|)
730
0
  // is ready, calling IndexedDatabaseManager::Init will permanently break
731
0
  // that preference. We can retrieve gExperimentalFeaturesEnabled without
732
0
  // actually going through IndexedDatabaseManager.
733
0
  // See Bug 1198093 comment 14 for detailed explanation.
734
0
  MOZ_DIAGNOSTIC_ASSERT(JS_IsGlobalObject(aGlobal));
735
0
  if (IsNonExposedGlobal(aCx, aGlobal, GlobalNames::BackstagePass)) {
736
0
    MOZ_ASSERT(NS_IsMainThread());
737
0
    static bool featureRetrieved = false;
738
0
    if (!featureRetrieved) {
739
0
      gExperimentalFeaturesEnabled = Preferences::GetBool(kPrefExperimental);
740
0
      featureRetrieved = true;
741
0
    }
742
0
    return gExperimentalFeaturesEnabled;
743
0
  }
744
0
745
0
  return ExperimentalFeaturesEnabled();
746
0
}
747
748
// static
749
bool
750
IndexedDatabaseManager::IsFileHandleEnabled()
751
0
{
752
0
  MOZ_ASSERT(gDBManager,
753
0
             "IsFileHandleEnabled() called before indexedDB has been "
754
0
             "initialized!");
755
0
756
0
  return gFileHandleEnabled;
757
0
}
758
759
// static
760
uint32_t
761
IndexedDatabaseManager::DataThreshold()
762
0
{
763
0
  MOZ_ASSERT(gDBManager,
764
0
             "DataThreshold() called before indexedDB has been initialized!");
765
0
766
0
  return gDataThresholdBytes;
767
0
}
768
769
// static
770
uint32_t
771
IndexedDatabaseManager::MaxSerializedMsgSize()
772
0
{
773
0
  MOZ_ASSERT(gDBManager,
774
0
             "MaxSerializedMsgSize() called before indexedDB has been initialized!");
775
0
  MOZ_ASSERT(gMaxSerializedMsgSize > 0);
776
0
777
0
  return gMaxSerializedMsgSize;
778
0
}
779
780
void
781
IndexedDatabaseManager::ClearBackgroundActor()
782
0
{
783
0
  MOZ_ASSERT(NS_IsMainThread());
784
0
785
0
  mBackgroundActor = nullptr;
786
0
}
787
788
void
789
IndexedDatabaseManager::NoteLiveQuotaManager(QuotaManager* aQuotaManager)
790
0
{
791
0
  // This can be called during Init, so we can't use IsMainProcess() yet.
792
0
  MOZ_ASSERT(sIsMainProcess);
793
0
  MOZ_ASSERT(NS_IsMainThread());
794
0
  MOZ_ASSERT(aQuotaManager);
795
0
796
0
  mBackgroundThread = aQuotaManager->OwningThread();
797
0
}
798
799
void
800
IndexedDatabaseManager::NoteShuttingDownQuotaManager()
801
0
{
802
0
  MOZ_ASSERT(IsMainProcess());
803
0
  MOZ_ASSERT(NS_IsMainThread());
804
0
805
0
  MOZ_ALWAYS_SUCCEEDS(mDeleteTimer->Cancel());
806
0
807
0
  mBackgroundThread = nullptr;
808
0
}
809
810
already_AddRefed<FileManager>
811
IndexedDatabaseManager::GetFileManager(PersistenceType aPersistenceType,
812
                                       const nsACString& aOrigin,
813
                                       const nsAString& aDatabaseName)
814
0
{
815
0
  AssertIsOnIOThread();
816
0
817
0
  FileManagerInfo* info;
818
0
  if (!mFileManagerInfos.Get(aOrigin, &info)) {
819
0
    return nullptr;
820
0
  }
821
0
822
0
  RefPtr<FileManager> fileManager =
823
0
    info->GetFileManager(aPersistenceType, aDatabaseName);
824
0
825
0
  return fileManager.forget();
826
0
}
827
828
void
829
IndexedDatabaseManager::AddFileManager(FileManager* aFileManager)
830
0
{
831
0
  AssertIsOnIOThread();
832
0
  NS_ASSERTION(aFileManager, "Null file manager!");
833
0
834
0
  FileManagerInfo* info;
835
0
  if (!mFileManagerInfos.Get(aFileManager->Origin(), &info)) {
836
0
    info = new FileManagerInfo();
837
0
    mFileManagerInfos.Put(aFileManager->Origin(), info);
838
0
  }
839
0
840
0
  info->AddFileManager(aFileManager);
841
0
}
842
843
void
844
IndexedDatabaseManager::InvalidateAllFileManagers()
845
0
{
846
0
  AssertIsOnIOThread();
847
0
848
0
  for (auto iter = mFileManagerInfos.ConstIter(); !iter.Done(); iter.Next()) {
849
0
    auto value = iter.Data();
850
0
    MOZ_ASSERT(value);
851
0
852
0
    value->InvalidateAllFileManagers();
853
0
  }
854
0
855
0
  mFileManagerInfos.Clear();
856
0
}
857
858
void
859
IndexedDatabaseManager::InvalidateFileManagers(PersistenceType aPersistenceType,
860
                                               const nsACString& aOrigin)
861
0
{
862
0
  AssertIsOnIOThread();
863
0
  MOZ_ASSERT(!aOrigin.IsEmpty());
864
0
865
0
  FileManagerInfo* info;
866
0
  if (!mFileManagerInfos.Get(aOrigin, &info)) {
867
0
    return;
868
0
  }
869
0
870
0
  info->InvalidateAndRemoveFileManagers(aPersistenceType);
871
0
872
0
  if (!info->HasFileManagers()) {
873
0
    mFileManagerInfos.Remove(aOrigin);
874
0
  }
875
0
}
876
877
void
878
IndexedDatabaseManager::InvalidateFileManager(PersistenceType aPersistenceType,
879
                                              const nsACString& aOrigin,
880
                                              const nsAString& aDatabaseName)
881
0
{
882
0
  AssertIsOnIOThread();
883
0
884
0
  FileManagerInfo* info;
885
0
  if (!mFileManagerInfos.Get(aOrigin, &info)) {
886
0
    return;
887
0
  }
888
0
889
0
  info->InvalidateAndRemoveFileManager(aPersistenceType, aDatabaseName);
890
0
891
0
  if (!info->HasFileManagers()) {
892
0
    mFileManagerInfos.Remove(aOrigin);
893
0
  }
894
0
}
895
896
nsresult
897
IndexedDatabaseManager::AsyncDeleteFile(FileManager* aFileManager,
898
                                        int64_t aFileId)
899
0
{
900
0
  MOZ_ASSERT(IsMainProcess());
901
0
  MOZ_ASSERT(NS_IsMainThread());
902
0
  MOZ_ASSERT(aFileManager);
903
0
  MOZ_ASSERT(aFileId > 0);
904
0
  MOZ_ASSERT(mDeleteTimer);
905
0
906
0
  if (!mBackgroundThread) {
907
0
    return NS_OK;
908
0
  }
909
0
910
0
  nsresult rv = mDeleteTimer->Cancel();
911
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
912
0
    return rv;
913
0
  }
914
0
915
0
  rv = mDeleteTimer->InitWithCallback(this, kDeleteTimeoutMs,
916
0
                                      nsITimer::TYPE_ONE_SHOT);
917
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
918
0
    return rv;
919
0
  }
920
0
921
0
  nsTArray<int64_t>* array;
922
0
  if (!mPendingDeleteInfos.Get(aFileManager, &array)) {
923
0
    array = new nsTArray<int64_t>();
924
0
    mPendingDeleteInfos.Put(aFileManager, array);
925
0
  }
926
0
927
0
  array->AppendElement(aFileId);
928
0
929
0
  return NS_OK;
930
0
}
931
932
nsresult
933
IndexedDatabaseManager::BlockAndGetFileReferences(
934
                                               PersistenceType aPersistenceType,
935
                                               const nsACString& aOrigin,
936
                                               const nsAString& aDatabaseName,
937
                                               int64_t aFileId,
938
                                               int32_t* aRefCnt,
939
                                               int32_t* aDBRefCnt,
940
                                               int32_t* aSliceRefCnt,
941
                                               bool* aResult)
942
0
{
943
0
  MOZ_ASSERT(NS_IsMainThread());
944
0
945
0
  if (NS_WARN_IF(!InTestingMode())) {
946
0
    return NS_ERROR_UNEXPECTED;
947
0
  }
948
0
949
0
  if (!mBackgroundActor) {
950
0
    PBackgroundChild* bgActor = BackgroundChild::GetForCurrentThread();
951
0
    if (NS_WARN_IF(!bgActor)) {
952
0
      return NS_ERROR_FAILURE;
953
0
    }
954
0
955
0
    BackgroundUtilsChild* actor = new BackgroundUtilsChild(this);
956
0
957
0
    // We don't set event target for BackgroundUtilsChild because:
958
0
    // 1. BackgroundUtilsChild is a singleton.
959
0
    // 2. SendGetFileReferences is a sync operation to be returned asap if unlabeled.
960
0
    // 3. The rest operations like DeleteMe/__delete__ only happens at shutdown.
961
0
    // Hence, we should keep it unlabeled.
962
0
    mBackgroundActor =
963
0
      static_cast<BackgroundUtilsChild*>(
964
0
        bgActor->SendPBackgroundIndexedDBUtilsConstructor(actor));
965
0
  }
966
0
967
0
  if (NS_WARN_IF(!mBackgroundActor)) {
968
0
    return NS_ERROR_FAILURE;
969
0
  }
970
0
971
0
  if (!mBackgroundActor->SendGetFileReferences(aPersistenceType,
972
0
                                               nsCString(aOrigin),
973
0
                                               nsString(aDatabaseName),
974
0
                                               aFileId,
975
0
                                               aRefCnt,
976
0
                                               aDBRefCnt,
977
0
                                               aSliceRefCnt,
978
0
                                               aResult)) {
979
0
    return NS_ERROR_FAILURE;
980
0
  }
981
0
982
0
  return NS_OK;
983
0
}
984
985
nsresult
986
IndexedDatabaseManager::FlushPendingFileDeletions()
987
0
{
988
0
  MOZ_ASSERT(NS_IsMainThread());
989
0
990
0
  if (NS_WARN_IF(!InTestingMode())) {
991
0
    return NS_ERROR_UNEXPECTED;
992
0
  }
993
0
994
0
  if (IsMainProcess()) {
995
0
    nsresult rv = mDeleteTimer->Cancel();
996
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
997
0
      return rv;
998
0
    }
999
0
1000
0
    rv = Notify(mDeleteTimer);
1001
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1002
0
      return rv;
1003
0
    }
1004
0
  } else {
1005
0
    PBackgroundChild* bgActor = BackgroundChild::GetForCurrentThread();
1006
0
    if (NS_WARN_IF(!bgActor)) {
1007
0
      return NS_ERROR_FAILURE;
1008
0
    }
1009
0
1010
0
    if (!bgActor->SendFlushPendingFileDeletions()) {
1011
0
      return NS_ERROR_FAILURE;
1012
0
    }
1013
0
  }
1014
0
1015
0
  return NS_OK;
1016
0
}
1017
1018
// static
1019
void
1020
IndexedDatabaseManager::LoggingModePrefChangedCallback(
1021
                                                    const char* /* aPrefName */,
1022
                                                    void* /* aClosure */)
1023
0
{
1024
0
  MOZ_ASSERT(NS_IsMainThread());
1025
0
1026
0
  if (!Preferences::GetBool(kPrefLoggingEnabled)) {
1027
0
    sLoggingMode = Logging_Disabled;
1028
0
    return;
1029
0
  }
1030
0
1031
0
  bool useProfiler =
1032
0
#if defined(DEBUG) || defined(MOZ_GECKO_PROFILER)
1033
0
    Preferences::GetBool(kPrefLoggingProfiler);
1034
#if !defined(MOZ_GECKO_PROFILER)
1035
  if (useProfiler) {
1036
    NS_WARNING("IndexedDB cannot create profiler marks because this build does "
1037
               "not have profiler extensions enabled!");
1038
    useProfiler = false;
1039
  }
1040
#endif
1041
#else
1042
    false;
1043
#endif
1044
1045
0
  const bool logDetails = Preferences::GetBool(kPrefLoggingDetails);
1046
0
1047
0
  if (useProfiler) {
1048
0
    sLoggingMode = logDetails ?
1049
0
                   Logging_DetailedProfilerMarks :
1050
0
                   Logging_ConciseProfilerMarks;
1051
0
  } else {
1052
0
    sLoggingMode = logDetails ? Logging_Detailed : Logging_Concise;
1053
0
  }
1054
0
}
1055
1056
// static
1057
const nsCString&
1058
IndexedDatabaseManager::GetLocale()
1059
0
{
1060
0
  IndexedDatabaseManager* idbManager = Get();
1061
0
  MOZ_ASSERT(idbManager, "IDBManager is not ready!");
1062
0
1063
0
  return idbManager->mLocale;
1064
0
}
1065
1066
NS_IMPL_ADDREF(IndexedDatabaseManager)
1067
NS_IMPL_RELEASE_WITH_DESTROY(IndexedDatabaseManager, Destroy())
1068
NS_IMPL_QUERY_INTERFACE(IndexedDatabaseManager, nsITimerCallback, nsINamed)
1069
1070
NS_IMETHODIMP
1071
IndexedDatabaseManager::Notify(nsITimer* aTimer)
1072
0
{
1073
0
  MOZ_ASSERT(IsMainProcess());
1074
0
  MOZ_ASSERT(NS_IsMainThread());
1075
0
  MOZ_ASSERT(mBackgroundThread);
1076
0
1077
0
  for (auto iter = mPendingDeleteInfos.ConstIter(); !iter.Done(); iter.Next()) {
1078
0
    auto key = iter.Key();
1079
0
    auto value = iter.Data();
1080
0
    MOZ_ASSERT(!value->IsEmpty());
1081
0
1082
0
    RefPtr<DeleteFilesRunnable> runnable =
1083
0
      new DeleteFilesRunnable(mBackgroundThread, key, *value);
1084
0
1085
0
    MOZ_ASSERT(value->IsEmpty());
1086
0
1087
0
    runnable->Dispatch();
1088
0
  }
1089
0
1090
0
  mPendingDeleteInfos.Clear();
1091
0
1092
0
  return NS_OK;
1093
0
}
1094
1095
NS_IMETHODIMP
1096
IndexedDatabaseManager::GetName(nsACString& aName)
1097
0
{
1098
0
  aName.AssignLiteral("IndexedDatabaseManager");
1099
0
  return NS_OK;
1100
0
}
1101
1102
already_AddRefed<FileManager>
1103
FileManagerInfo::GetFileManager(PersistenceType aPersistenceType,
1104
                                const nsAString& aName) const
1105
0
{
1106
0
  AssertIsOnIOThread();
1107
0
1108
0
  const nsTArray<RefPtr<FileManager> >& managers =
1109
0
    GetImmutableArray(aPersistenceType);
1110
0
1111
0
  for (uint32_t i = 0; i < managers.Length(); i++) {
1112
0
    const RefPtr<FileManager>& fileManager = managers[i];
1113
0
1114
0
    if (fileManager->DatabaseName() == aName) {
1115
0
      RefPtr<FileManager> result = fileManager;
1116
0
      return result.forget();
1117
0
    }
1118
0
  }
1119
0
1120
0
  return nullptr;
1121
0
}
1122
1123
void
1124
FileManagerInfo::AddFileManager(FileManager* aFileManager)
1125
0
{
1126
0
  AssertIsOnIOThread();
1127
0
1128
0
  nsTArray<RefPtr<FileManager> >& managers = GetArray(aFileManager->Type());
1129
0
1130
0
  NS_ASSERTION(!managers.Contains(aFileManager), "Adding more than once?!");
1131
0
1132
0
  managers.AppendElement(aFileManager);
1133
0
}
1134
1135
void
1136
FileManagerInfo::InvalidateAllFileManagers() const
1137
0
{
1138
0
  AssertIsOnIOThread();
1139
0
1140
0
  uint32_t i;
1141
0
1142
0
  for (i = 0; i < mPersistentStorageFileManagers.Length(); i++) {
1143
0
    mPersistentStorageFileManagers[i]->Invalidate();
1144
0
  }
1145
0
1146
0
  for (i = 0; i < mTemporaryStorageFileManagers.Length(); i++) {
1147
0
    mTemporaryStorageFileManagers[i]->Invalidate();
1148
0
  }
1149
0
1150
0
  for (i = 0; i < mDefaultStorageFileManagers.Length(); i++) {
1151
0
    mDefaultStorageFileManagers[i]->Invalidate();
1152
0
  }
1153
0
}
1154
1155
void
1156
FileManagerInfo::InvalidateAndRemoveFileManagers(
1157
                                               PersistenceType aPersistenceType)
1158
0
{
1159
0
  AssertIsOnIOThread();
1160
0
1161
0
  nsTArray<RefPtr<FileManager > >& managers = GetArray(aPersistenceType);
1162
0
1163
0
  for (uint32_t i = 0; i < managers.Length(); i++) {
1164
0
    managers[i]->Invalidate();
1165
0
  }
1166
0
1167
0
  managers.Clear();
1168
0
}
1169
1170
void
1171
FileManagerInfo::InvalidateAndRemoveFileManager(
1172
                                               PersistenceType aPersistenceType,
1173
                                               const nsAString& aName)
1174
0
{
1175
0
  AssertIsOnIOThread();
1176
0
1177
0
  nsTArray<RefPtr<FileManager > >& managers = GetArray(aPersistenceType);
1178
0
1179
0
  for (uint32_t i = 0; i < managers.Length(); i++) {
1180
0
    RefPtr<FileManager>& fileManager = managers[i];
1181
0
    if (fileManager->DatabaseName() == aName) {
1182
0
      fileManager->Invalidate();
1183
0
      managers.RemoveElementAt(i);
1184
0
      return;
1185
0
    }
1186
0
  }
1187
0
}
1188
1189
nsTArray<RefPtr<FileManager> >&
1190
FileManagerInfo::GetArray(PersistenceType aPersistenceType)
1191
0
{
1192
0
  switch (aPersistenceType) {
1193
0
    case PERSISTENCE_TYPE_PERSISTENT:
1194
0
      return mPersistentStorageFileManagers;
1195
0
    case PERSISTENCE_TYPE_TEMPORARY:
1196
0
      return mTemporaryStorageFileManagers;
1197
0
    case PERSISTENCE_TYPE_DEFAULT:
1198
0
      return mDefaultStorageFileManagers;
1199
0
1200
0
    case PERSISTENCE_TYPE_INVALID:
1201
0
    default:
1202
0
      MOZ_CRASH("Bad storage type value!");
1203
0
  }
1204
0
}
1205
1206
DeleteFilesRunnable::DeleteFilesRunnable(nsIEventTarget* aBackgroundThread,
1207
                                         FileManager* aFileManager,
1208
                                         nsTArray<int64_t>& aFileIds)
1209
  : mBackgroundThread(aBackgroundThread)
1210
  , mFileManager(aFileManager)
1211
  , mState(State_Initial)
1212
0
{
1213
0
  mFileIds.SwapElements(aFileIds);
1214
0
}
1215
1216
void
1217
DeleteFilesRunnable::Dispatch()
1218
0
{
1219
0
  MOZ_ASSERT(NS_IsMainThread());
1220
0
  MOZ_ASSERT(mState == State_Initial);
1221
0
1222
0
  MOZ_ALWAYS_SUCCEEDS(mBackgroundThread->Dispatch(this, NS_DISPATCH_NORMAL));
1223
0
}
1224
1225
NS_IMPL_ISUPPORTS(DeleteFilesRunnable, nsIRunnable)
1226
1227
NS_IMETHODIMP
1228
DeleteFilesRunnable::Run()
1229
0
{
1230
0
  nsresult rv;
1231
0
1232
0
  switch (mState) {
1233
0
    case State_Initial:
1234
0
      rv = Open();
1235
0
      break;
1236
0
1237
0
    case State_DatabaseWorkOpen:
1238
0
      rv = DoDatabaseWork();
1239
0
      break;
1240
0
1241
0
    case State_UnblockingOpen:
1242
0
      UnblockOpen();
1243
0
      return NS_OK;
1244
0
1245
0
    case State_DirectoryOpenPending:
1246
0
    default:
1247
0
      MOZ_CRASH("Should never get here!");
1248
0
  }
1249
0
1250
0
  if (NS_WARN_IF(NS_FAILED(rv)) && mState != State_UnblockingOpen) {
1251
0
    Finish();
1252
0
  }
1253
0
1254
0
  return NS_OK;
1255
0
}
1256
1257
void
1258
DeleteFilesRunnable::DirectoryLockAcquired(DirectoryLock* aLock)
1259
0
{
1260
0
  AssertIsOnBackgroundThread();
1261
0
  MOZ_ASSERT(mState == State_DirectoryOpenPending);
1262
0
  MOZ_ASSERT(!mDirectoryLock);
1263
0
1264
0
  mDirectoryLock = aLock;
1265
0
1266
0
  QuotaManager* quotaManager = QuotaManager::Get();
1267
0
  MOZ_ASSERT(quotaManager);
1268
0
1269
0
  // Must set this before dispatching otherwise we will race with the IO thread
1270
0
  mState = State_DatabaseWorkOpen;
1271
0
1272
0
  nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
1273
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1274
0
    Finish();
1275
0
    return;
1276
0
  }
1277
0
}
1278
1279
void
1280
DeleteFilesRunnable::DirectoryLockFailed()
1281
0
{
1282
0
  AssertIsOnBackgroundThread();
1283
0
  MOZ_ASSERT(mState == State_DirectoryOpenPending);
1284
0
  MOZ_ASSERT(!mDirectoryLock);
1285
0
1286
0
  Finish();
1287
0
}
1288
1289
nsresult
1290
DeleteFilesRunnable::Open()
1291
0
{
1292
0
  AssertIsOnBackgroundThread();
1293
0
  MOZ_ASSERT(mState == State_Initial);
1294
0
1295
0
  QuotaManager* quotaManager = QuotaManager::Get();
1296
0
  if (NS_WARN_IF(!quotaManager)) {
1297
0
    return NS_ERROR_FAILURE;
1298
0
  }
1299
0
1300
0
  mState = State_DirectoryOpenPending;
1301
0
1302
0
  quotaManager->OpenDirectory(mFileManager->Type(),
1303
0
                              mFileManager->Group(),
1304
0
                              mFileManager->Origin(),
1305
0
                              quota::Client::IDB,
1306
0
                              /* aExclusive */ false,
1307
0
                              this);
1308
0
1309
0
  return NS_OK;
1310
0
}
1311
1312
nsresult
1313
DeleteFilesRunnable::DeleteFile(int64_t aFileId)
1314
0
{
1315
0
  MOZ_ASSERT(mDirectory);
1316
0
  MOZ_ASSERT(mJournalDirectory);
1317
0
1318
0
  nsCOMPtr<nsIFile> file = mFileManager->GetFileForId(mDirectory, aFileId);
1319
0
  NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);
1320
0
1321
0
  nsresult rv;
1322
0
  int64_t fileSize;
1323
0
1324
0
  if (mFileManager->EnforcingQuota()) {
1325
0
    rv = file->GetFileSize(&fileSize);
1326
0
    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
1327
0
  }
1328
0
1329
0
  rv = file->Remove(false);
1330
0
  NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
1331
0
1332
0
  if (mFileManager->EnforcingQuota()) {
1333
0
    QuotaManager* quotaManager = QuotaManager::Get();
1334
0
    NS_ASSERTION(quotaManager, "Shouldn't be null!");
1335
0
1336
0
    quotaManager->DecreaseUsageForOrigin(mFileManager->Type(),
1337
0
                                         mFileManager->Group(),
1338
0
                                         mFileManager->Origin(), fileSize);
1339
0
  }
1340
0
1341
0
  file = mFileManager->GetFileForId(mJournalDirectory, aFileId);
1342
0
  NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);
1343
0
1344
0
  rv = file->Remove(false);
1345
0
  NS_ENSURE_SUCCESS(rv, rv);
1346
0
1347
0
  return NS_OK;
1348
0
}
1349
1350
nsresult
1351
DeleteFilesRunnable::DoDatabaseWork()
1352
0
{
1353
0
  AssertIsOnIOThread();
1354
0
  MOZ_ASSERT(mState == State_DatabaseWorkOpen);
1355
0
1356
0
  if (!mFileManager->Invalidated()) {
1357
0
    mDirectory = mFileManager->GetDirectory();
1358
0
    if (NS_WARN_IF(!mDirectory)) {
1359
0
      return NS_ERROR_FAILURE;
1360
0
    }
1361
0
1362
0
    mJournalDirectory = mFileManager->GetJournalDirectory();
1363
0
    if (NS_WARN_IF(!mJournalDirectory)) {
1364
0
      return NS_ERROR_FAILURE;
1365
0
    }
1366
0
1367
0
    for (int64_t fileId : mFileIds) {
1368
0
      if (NS_FAILED(DeleteFile(fileId))) {
1369
0
        NS_WARNING("Failed to delete file!");
1370
0
      }
1371
0
    }
1372
0
  }
1373
0
1374
0
  Finish();
1375
0
1376
0
  return NS_OK;
1377
0
}
1378
1379
void
1380
DeleteFilesRunnable::Finish()
1381
0
{
1382
0
  // Must set mState before dispatching otherwise we will race with the main
1383
0
  // thread.
1384
0
  mState = State_UnblockingOpen;
1385
0
1386
0
  MOZ_ALWAYS_SUCCEEDS(mBackgroundThread->Dispatch(this, NS_DISPATCH_NORMAL));
1387
0
}
1388
1389
void
1390
DeleteFilesRunnable::UnblockOpen()
1391
0
{
1392
0
  AssertIsOnBackgroundThread();
1393
0
  MOZ_ASSERT(mState == State_UnblockingOpen);
1394
0
1395
0
  mDirectoryLock = nullptr;
1396
0
1397
0
  mState = State_Completed;
1398
0
}
1399
1400
} // namespace dom
1401
} // namespace mozilla