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