dom/indexedDB/IndexedDatabaseManager.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:269862f0985e
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=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 "nsIConsoleService.h"
10 #include "nsIDiskSpaceWatcher.h"
11 #include "nsIFile.h"
12 #include "nsIFileStorage.h"
13 #include "nsIObserverService.h"
14 #include "nsIScriptError.h"
15
16 #include "jsapi.h"
17 #include "mozilla/ClearOnShutdown.h"
18 #include "mozilla/CondVar.h"
19 #include "mozilla/ContentEvents.h"
20 #include "mozilla/dom/ErrorEventBinding.h"
21 #include "mozilla/dom/quota/OriginOrPatternString.h"
22 #include "mozilla/dom/quota/QuotaManager.h"
23 #include "mozilla/dom/quota/Utilities.h"
24 #include "mozilla/dom/TabContext.h"
25 #include "mozilla/EventDispatcher.h"
26 #include "mozilla/Services.h"
27 #include "mozilla/Preferences.h"
28 #include "mozilla/storage.h"
29 #include "nsContentUtils.h"
30 #include "nsThreadUtils.h"
31
32 #include "IDBEvents.h"
33 #include "IDBFactory.h"
34 #include "IDBKeyRange.h"
35 #include "IDBRequest.h"
36
37 // Bindings for ResolveConstructors
38 #include "mozilla/dom/IDBCursorBinding.h"
39 #include "mozilla/dom/IDBDatabaseBinding.h"
40 #include "mozilla/dom/IDBFactoryBinding.h"
41 #include "mozilla/dom/IDBFileHandleBinding.h"
42 #include "mozilla/dom/IDBKeyRangeBinding.h"
43 #include "mozilla/dom/IDBIndexBinding.h"
44 #include "mozilla/dom/IDBObjectStoreBinding.h"
45 #include "mozilla/dom/IDBOpenDBRequestBinding.h"
46 #include "mozilla/dom/IDBRequestBinding.h"
47 #include "mozilla/dom/IDBTransactionBinding.h"
48 #include "mozilla/dom/IDBVersionChangeEventBinding.h"
49
50 #define IDB_STR "indexedDB"
51
52 // The two possible values for the data argument when receiving the disk space
53 // observer notification.
54 #define LOW_DISK_SPACE_DATA_FULL "full"
55 #define LOW_DISK_SPACE_DATA_FREE "free"
56
57 USING_INDEXEDDB_NAMESPACE
58 using namespace mozilla;
59 using namespace mozilla::dom;
60 USING_QUOTA_NAMESPACE
61
62 BEGIN_INDEXEDDB_NAMESPACE
63
64 class FileManagerInfo
65 {
66 public:
67 already_AddRefed<FileManager>
68 GetFileManager(PersistenceType aPersistenceType,
69 const nsAString& aName) const;
70
71 void
72 AddFileManager(FileManager* aFileManager);
73
74 bool
75 HasFileManagers() const
76 {
77 AssertIsOnIOThread();
78
79 return !mPersistentStorageFileManagers.IsEmpty() ||
80 !mTemporaryStorageFileManagers.IsEmpty();
81 }
82
83 void
84 InvalidateAllFileManagers() const;
85
86 void
87 InvalidateAndRemoveFileManagers(PersistenceType aPersistenceType);
88
89 void
90 InvalidateAndRemoveFileManager(PersistenceType aPersistenceType,
91 const nsAString& aName);
92
93 private:
94 nsTArray<nsRefPtr<FileManager> >&
95 GetArray(PersistenceType aPersistenceType);
96
97 const nsTArray<nsRefPtr<FileManager> >&
98 GetImmutableArray(PersistenceType aPersistenceType) const
99 {
100 return const_cast<FileManagerInfo*>(this)->GetArray(aPersistenceType);
101 }
102
103 nsTArray<nsRefPtr<FileManager> > mPersistentStorageFileManagers;
104 nsTArray<nsRefPtr<FileManager> > mTemporaryStorageFileManagers;
105 };
106
107 END_INDEXEDDB_NAMESPACE
108
109 namespace {
110
111 mozilla::StaticRefPtr<IndexedDatabaseManager> gDBManager;
112
113 mozilla::Atomic<bool> gInitialized(false);
114 mozilla::Atomic<bool> gClosed(false);
115
116 class AsyncDeleteFileRunnable MOZ_FINAL : public nsIRunnable
117 {
118 public:
119 NS_DECL_THREADSAFE_ISUPPORTS
120 NS_DECL_NSIRUNNABLE
121
122 AsyncDeleteFileRunnable(FileManager* aFileManager, int64_t aFileId);
123
124 private:
125 nsRefPtr<FileManager> mFileManager;
126 int64_t mFileId;
127 };
128
129 class GetFileReferencesHelper MOZ_FINAL : public nsIRunnable
130 {
131 public:
132 NS_DECL_THREADSAFE_ISUPPORTS
133 NS_DECL_NSIRUNNABLE
134
135 GetFileReferencesHelper(PersistenceType aPersistenceType,
136 const nsACString& aOrigin,
137 const nsAString& aDatabaseName,
138 int64_t aFileId)
139 : mPersistenceType(aPersistenceType),
140 mOrigin(aOrigin),
141 mDatabaseName(aDatabaseName),
142 mFileId(aFileId),
143 mMutex(IndexedDatabaseManager::FileMutex()),
144 mCondVar(mMutex, "GetFileReferencesHelper::mCondVar"),
145 mMemRefCnt(-1),
146 mDBRefCnt(-1),
147 mSliceRefCnt(-1),
148 mResult(false),
149 mWaiting(true)
150 { }
151
152 nsresult
153 DispatchAndReturnFileReferences(int32_t* aMemRefCnt,
154 int32_t* aDBRefCnt,
155 int32_t* aSliceRefCnt,
156 bool* aResult);
157
158 private:
159 PersistenceType mPersistenceType;
160 nsCString mOrigin;
161 nsString mDatabaseName;
162 int64_t mFileId;
163
164 mozilla::Mutex& mMutex;
165 mozilla::CondVar mCondVar;
166 int32_t mMemRefCnt;
167 int32_t mDBRefCnt;
168 int32_t mSliceRefCnt;
169 bool mResult;
170 bool mWaiting;
171 };
172
173 struct MOZ_STACK_CLASS InvalidateInfo
174 {
175 InvalidateInfo(PersistenceType aPersistenceType, const nsACString& aPattern)
176 : persistenceType(aPersistenceType), pattern(aPattern)
177 { }
178
179 PersistenceType persistenceType;
180 const nsACString& pattern;
181 };
182
183 } // anonymous namespace
184
185 IndexedDatabaseManager::IndexedDatabaseManager()
186 : mFileMutex("IndexedDatabaseManager.mFileMutex")
187 {
188 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
189 }
190
191 IndexedDatabaseManager::~IndexedDatabaseManager()
192 {
193 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
194 }
195
196 bool IndexedDatabaseManager::sIsMainProcess = false;
197 mozilla::Atomic<bool> IndexedDatabaseManager::sLowDiskSpaceMode(false);
198
199 // static
200 IndexedDatabaseManager*
201 IndexedDatabaseManager::GetOrCreate()
202 {
203 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
204
205 if (IsClosed()) {
206 NS_ERROR("Calling GetOrCreate() after shutdown!");
207 return nullptr;
208 }
209
210 if (!gDBManager) {
211 sIsMainProcess = XRE_GetProcessType() == GeckoProcessType_Default;
212
213 if (sIsMainProcess && Preferences::GetBool("disk_space_watcher.enabled", false)) {
214 // See if we're starting up in low disk space conditions.
215 nsCOMPtr<nsIDiskSpaceWatcher> watcher =
216 do_GetService(DISKSPACEWATCHER_CONTRACTID);
217 if (watcher) {
218 bool isDiskFull;
219 if (NS_SUCCEEDED(watcher->GetIsDiskFull(&isDiskFull))) {
220 sLowDiskSpaceMode = isDiskFull;
221 }
222 else {
223 NS_WARNING("GetIsDiskFull failed!");
224 }
225 }
226 else {
227 NS_WARNING("No disk space watcher component available!");
228 }
229 }
230
231 nsRefPtr<IndexedDatabaseManager> instance(new IndexedDatabaseManager());
232
233 nsresult rv = instance->Init();
234 NS_ENSURE_SUCCESS(rv, nullptr);
235
236 if (gInitialized.exchange(true)) {
237 NS_ERROR("Initialized more than once?!");
238 }
239
240 gDBManager = instance;
241
242 ClearOnShutdown(&gDBManager);
243 }
244
245 return gDBManager;
246 }
247
248 // static
249 IndexedDatabaseManager*
250 IndexedDatabaseManager::Get()
251 {
252 // Does not return an owning reference.
253 return gDBManager;
254 }
255
256 // static
257 IndexedDatabaseManager*
258 IndexedDatabaseManager::FactoryCreate()
259 {
260 // Returns a raw pointer that carries an owning reference! Lame, but the
261 // singleton factory macros force this.
262 IndexedDatabaseManager* mgr = GetOrCreate();
263 NS_IF_ADDREF(mgr);
264 return mgr;
265 }
266
267 nsresult
268 IndexedDatabaseManager::Init()
269 {
270 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
271
272 // Make sure that the quota manager is up.
273 QuotaManager* qm = QuotaManager::GetOrCreate();
274 NS_ENSURE_STATE(qm);
275
276 // During Init() we can't yet call IsMainProcess(), just check sIsMainProcess
277 // directly.
278 if (sIsMainProcess) {
279 // Must initialize the storage service on the main thread.
280 nsCOMPtr<mozIStorageService> ss =
281 do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
282 NS_ENSURE_STATE(ss);
283
284 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
285 NS_ENSURE_STATE(obs);
286
287 nsresult rv =
288 obs->AddObserver(this, DISKSPACEWATCHER_OBSERVER_TOPIC, false);
289 NS_ENSURE_SUCCESS(rv, rv);
290 }
291
292 return NS_OK;
293 }
294
295 void
296 IndexedDatabaseManager::Destroy()
297 {
298 // Setting the closed flag prevents the service from being recreated.
299 // Don't set it though if there's no real instance created.
300 if (gInitialized && gClosed.exchange(true)) {
301 NS_ERROR("Shutdown more than once?!");
302 }
303
304 delete this;
305 }
306
307 // static
308 nsresult
309 IndexedDatabaseManager::FireWindowOnError(nsPIDOMWindow* aOwner,
310 EventChainPostVisitor& aVisitor)
311 {
312 NS_ENSURE_TRUE(aVisitor.mDOMEvent, NS_ERROR_UNEXPECTED);
313 if (!aOwner) {
314 return NS_OK;
315 }
316
317 if (aVisitor.mEventStatus == nsEventStatus_eConsumeNoDefault) {
318 return NS_OK;
319 }
320
321 nsString type;
322 nsresult rv = aVisitor.mDOMEvent->GetType(type);
323 NS_ENSURE_SUCCESS(rv, rv);
324
325 if (!type.EqualsLiteral(ERROR_EVT_STR)) {
326 return NS_OK;
327 }
328
329 nsCOMPtr<EventTarget> eventTarget =
330 aVisitor.mDOMEvent->InternalDOMEvent()->GetTarget();
331
332 IDBRequest* request = static_cast<IDBRequest*>(eventTarget.get());
333 NS_ENSURE_TRUE(request, NS_ERROR_UNEXPECTED);
334
335 ErrorResult ret;
336 nsRefPtr<DOMError> error = request->GetError(ret);
337 if (ret.Failed()) {
338 return ret.ErrorCode();
339 }
340
341 nsString errorName;
342 if (error) {
343 error->GetName(errorName);
344 }
345
346 ThreadsafeAutoJSContext cx;
347 RootedDictionary<ErrorEventInit> init(cx);
348 request->FillScriptErrorEvent(init);
349
350 init.mMessage = errorName;
351 init.mCancelable = true;
352 init.mBubbles = true;
353
354 nsCOMPtr<nsIScriptGlobalObject> sgo(do_QueryInterface(aOwner));
355 NS_ASSERTION(sgo, "How can this happen?!");
356
357 nsEventStatus status = nsEventStatus_eIgnore;
358 if (NS_FAILED(sgo->HandleScriptError(init, &status))) {
359 NS_WARNING("Failed to dispatch script error event");
360 status = nsEventStatus_eIgnore;
361 }
362
363 bool preventDefaultCalled = status == nsEventStatus_eConsumeNoDefault;
364 if (preventDefaultCalled) {
365 return NS_OK;
366 }
367
368 // Log an error to the error console.
369 nsCOMPtr<nsIScriptError> scriptError =
370 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
371 NS_ENSURE_SUCCESS(rv, rv);
372
373 if (NS_FAILED(scriptError->InitWithWindowID(errorName,
374 init.mFilename,
375 EmptyString(), init.mLineno,
376 0, 0,
377 "IndexedDB",
378 aOwner->WindowID()))) {
379 NS_WARNING("Failed to init script error!");
380 return NS_ERROR_FAILURE;
381 }
382
383 nsCOMPtr<nsIConsoleService> consoleService =
384 do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv);
385 NS_ENSURE_SUCCESS(rv, rv);
386
387 return consoleService->LogMessage(scriptError);
388 }
389
390 // static
391 bool
392 IndexedDatabaseManager::TabContextMayAccessOrigin(const TabContext& aContext,
393 const nsACString& aOrigin)
394 {
395 NS_ASSERTION(!aOrigin.IsEmpty(), "Empty origin!");
396
397 // If aContext is for a browser element, it's allowed only to access other
398 // browser elements. But if aContext is not for a browser element, it may
399 // access both browser and non-browser elements.
400 nsAutoCString pattern;
401 QuotaManager::GetOriginPatternStringMaybeIgnoreBrowser(
402 aContext.OwnOrContainingAppId(),
403 aContext.IsBrowserElement(),
404 pattern);
405
406 return PatternMatchesOrigin(pattern, aOrigin);
407 }
408
409 // static
410 bool
411 IndexedDatabaseManager::DefineIndexedDB(JSContext* aCx,
412 JS::Handle<JSObject*> aGlobal)
413 {
414 MOZ_ASSERT(NS_IsMainThread());
415 MOZ_ASSERT(nsContentUtils::IsCallerChrome(), "Only for chrome!");
416 MOZ_ASSERT(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL,
417 "Passed object is not a global object!");
418
419 if (!IDBCursorBinding::GetConstructorObject(aCx, aGlobal) ||
420 !IDBCursorWithValueBinding::GetConstructorObject(aCx, aGlobal) ||
421 !IDBDatabaseBinding::GetConstructorObject(aCx, aGlobal) ||
422 !IDBFactoryBinding::GetConstructorObject(aCx, aGlobal) ||
423 !IDBFileHandleBinding::GetConstructorObject(aCx, aGlobal) ||
424 !IDBIndexBinding::GetConstructorObject(aCx, aGlobal) ||
425 !IDBKeyRangeBinding::GetConstructorObject(aCx, aGlobal) ||
426 !IDBObjectStoreBinding::GetConstructorObject(aCx, aGlobal) ||
427 !IDBOpenDBRequestBinding::GetConstructorObject(aCx, aGlobal) ||
428 !IDBRequestBinding::GetConstructorObject(aCx, aGlobal) ||
429 !IDBTransactionBinding::GetConstructorObject(aCx, aGlobal) ||
430 !IDBVersionChangeEventBinding::GetConstructorObject(aCx, aGlobal))
431 {
432 return false;
433 }
434
435 nsRefPtr<IDBFactory> factory;
436 if (NS_FAILED(IDBFactory::Create(aCx, aGlobal, nullptr,
437 getter_AddRefs(factory)))) {
438 return false;
439 }
440
441 MOZ_ASSERT(factory, "This should never fail for chrome!");
442
443 JS::Rooted<JS::Value> indexedDB(aCx);
444 js::AssertSameCompartment(aCx, aGlobal);
445 if (!WrapNewBindingObject(aCx, factory, &indexedDB)) {
446 return false;
447 }
448
449 return JS_DefineProperty(aCx, aGlobal, IDB_STR, indexedDB, JSPROP_ENUMERATE);
450 }
451
452 // static
453 bool
454 IndexedDatabaseManager::IsClosed()
455 {
456 return gClosed;
457 }
458
459 #ifdef DEBUG
460 // static
461 bool
462 IndexedDatabaseManager::IsMainProcess()
463 {
464 NS_ASSERTION(gDBManager,
465 "IsMainProcess() called before indexedDB has been initialized!");
466 NS_ASSERTION((XRE_GetProcessType() == GeckoProcessType_Default) ==
467 sIsMainProcess, "XRE_GetProcessType changed its tune!");
468 return sIsMainProcess;
469 }
470
471 //static
472 bool
473 IndexedDatabaseManager::InLowDiskSpaceMode()
474 {
475 NS_ASSERTION(gDBManager,
476 "InLowDiskSpaceMode() called before indexedDB has been "
477 "initialized!");
478 return sLowDiskSpaceMode;
479 }
480 #endif
481
482 already_AddRefed<FileManager>
483 IndexedDatabaseManager::GetFileManager(PersistenceType aPersistenceType,
484 const nsACString& aOrigin,
485 const nsAString& aDatabaseName)
486 {
487 AssertIsOnIOThread();
488
489 FileManagerInfo* info;
490 if (!mFileManagerInfos.Get(aOrigin, &info)) {
491 return nullptr;
492 }
493
494 nsRefPtr<FileManager> fileManager =
495 info->GetFileManager(aPersistenceType, aDatabaseName);
496
497 return fileManager.forget();
498 }
499
500 void
501 IndexedDatabaseManager::AddFileManager(FileManager* aFileManager)
502 {
503 AssertIsOnIOThread();
504 NS_ASSERTION(aFileManager, "Null file manager!");
505
506 FileManagerInfo* info;
507 if (!mFileManagerInfos.Get(aFileManager->Origin(), &info)) {
508 info = new FileManagerInfo();
509 mFileManagerInfos.Put(aFileManager->Origin(), info);
510 }
511
512 info->AddFileManager(aFileManager);
513 }
514
515 // static
516 PLDHashOperator
517 IndexedDatabaseManager::InvalidateAndRemoveFileManagers(
518 const nsACString& aKey,
519 nsAutoPtr<FileManagerInfo>& aValue,
520 void* aUserArg)
521 {
522 AssertIsOnIOThread();
523 NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
524 NS_ASSERTION(aValue, "Null pointer!");
525
526 if (!aUserArg) {
527 aValue->InvalidateAllFileManagers();
528 return PL_DHASH_REMOVE;
529 }
530
531 InvalidateInfo* info = static_cast<InvalidateInfo*>(aUserArg);
532
533 if (PatternMatchesOrigin(info->pattern, aKey)) {
534 aValue->InvalidateAndRemoveFileManagers(info->persistenceType);
535
536 if (!aValue->HasFileManagers()) {
537 return PL_DHASH_REMOVE;
538 }
539 }
540
541 return PL_DHASH_NEXT;
542 }
543
544 void
545 IndexedDatabaseManager::InvalidateAllFileManagers()
546 {
547 AssertIsOnIOThread();
548
549 mFileManagerInfos.Enumerate(InvalidateAndRemoveFileManagers, nullptr);
550 }
551
552 void
553 IndexedDatabaseManager::InvalidateFileManagers(
554 PersistenceType aPersistenceType,
555 const OriginOrPatternString& aOriginOrPattern)
556 {
557 AssertIsOnIOThread();
558 NS_ASSERTION(!aOriginOrPattern.IsEmpty(), "Empty pattern!");
559
560 if (aOriginOrPattern.IsOrigin()) {
561 FileManagerInfo* info;
562 if (!mFileManagerInfos.Get(aOriginOrPattern, &info)) {
563 return;
564 }
565
566 info->InvalidateAndRemoveFileManagers(aPersistenceType);
567
568 if (!info->HasFileManagers()) {
569 mFileManagerInfos.Remove(aOriginOrPattern);
570 }
571 }
572 else {
573 InvalidateInfo info(aPersistenceType, aOriginOrPattern);
574 mFileManagerInfos.Enumerate(InvalidateAndRemoveFileManagers, &info);
575 }
576 }
577
578 void
579 IndexedDatabaseManager::InvalidateFileManager(PersistenceType aPersistenceType,
580 const nsACString& aOrigin,
581 const nsAString& aDatabaseName)
582 {
583 AssertIsOnIOThread();
584
585 FileManagerInfo* info;
586 if (!mFileManagerInfos.Get(aOrigin, &info)) {
587 return;
588 }
589
590 info->InvalidateAndRemoveFileManager(aPersistenceType, aDatabaseName);
591
592 if (!info->HasFileManagers()) {
593 mFileManagerInfos.Remove(aOrigin);
594 }
595 }
596
597 nsresult
598 IndexedDatabaseManager::AsyncDeleteFile(FileManager* aFileManager,
599 int64_t aFileId)
600 {
601 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
602
603 NS_ENSURE_ARG_POINTER(aFileManager);
604
605 QuotaManager* quotaManager = QuotaManager::Get();
606 NS_ASSERTION(quotaManager, "Shouldn't be null!");
607
608 // See if we're currently clearing the storages for this origin. If so then
609 // we pretend that we've already deleted everything.
610 if (quotaManager->IsClearOriginPending(
611 aFileManager->Origin(),
612 Nullable<PersistenceType>(aFileManager->Type()))) {
613 return NS_OK;
614 }
615
616 nsRefPtr<AsyncDeleteFileRunnable> runnable =
617 new AsyncDeleteFileRunnable(aFileManager, aFileId);
618
619 nsresult rv =
620 quotaManager->IOThread()->Dispatch(runnable, NS_DISPATCH_NORMAL);
621 NS_ENSURE_SUCCESS(rv, rv);
622
623 return NS_OK;
624 }
625
626 nsresult
627 IndexedDatabaseManager::BlockAndGetFileReferences(
628 PersistenceType aPersistenceType,
629 const nsACString& aOrigin,
630 const nsAString& aDatabaseName,
631 int64_t aFileId,
632 int32_t* aRefCnt,
633 int32_t* aDBRefCnt,
634 int32_t* aSliceRefCnt,
635 bool* aResult)
636 {
637 nsRefPtr<GetFileReferencesHelper> helper =
638 new GetFileReferencesHelper(aPersistenceType, aOrigin, aDatabaseName,
639 aFileId);
640
641 nsresult rv = helper->DispatchAndReturnFileReferences(aRefCnt, aDBRefCnt,
642 aSliceRefCnt, aResult);
643 NS_ENSURE_SUCCESS(rv, rv);
644
645 return NS_OK;
646 }
647
648 NS_IMPL_ADDREF(IndexedDatabaseManager)
649 NS_IMPL_RELEASE_WITH_DESTROY(IndexedDatabaseManager, Destroy())
650 NS_IMPL_QUERY_INTERFACE(IndexedDatabaseManager, nsIIndexedDatabaseManager,
651 nsIObserver)
652
653 NS_IMETHODIMP
654 IndexedDatabaseManager::InitWindowless(JS::Handle<JS::Value> aGlobal, JSContext* aCx)
655 {
656 NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
657
658 JS::Rooted<JSObject*> global(aCx, JSVAL_TO_OBJECT(aGlobal));
659 if (!(js::GetObjectClass(global)->flags & JSCLASS_DOM_GLOBAL)) {
660 NS_WARNING("Passed object is not a global object!");
661 return NS_ERROR_FAILURE;
662 }
663
664 bool hasIndexedDB;
665 if (!JS_HasProperty(aCx, global, IDB_STR, &hasIndexedDB)) {
666 return NS_ERROR_FAILURE;
667 }
668
669 if (hasIndexedDB) {
670 NS_WARNING("Passed object already has an 'indexedDB' property!");
671 return NS_ERROR_FAILURE;
672 }
673
674 if (!DefineIndexedDB(aCx, global)) {
675 return NS_ERROR_FAILURE;
676 }
677
678 return NS_OK;
679 }
680
681 NS_IMETHODIMP
682 IndexedDatabaseManager::Observe(nsISupports* aSubject, const char* aTopic,
683 const char16_t* aData)
684 {
685 NS_ASSERTION(IsMainProcess(), "Wrong process!");
686 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
687
688 if (!strcmp(aTopic, DISKSPACEWATCHER_OBSERVER_TOPIC)) {
689 NS_ASSERTION(aData, "No data?!");
690
691 const nsDependentString data(aData);
692
693 if (data.EqualsLiteral(LOW_DISK_SPACE_DATA_FULL)) {
694 sLowDiskSpaceMode = true;
695 }
696 else if (data.EqualsLiteral(LOW_DISK_SPACE_DATA_FREE)) {
697 sLowDiskSpaceMode = false;
698 }
699 else {
700 NS_NOTREACHED("Unknown data value!");
701 }
702
703 return NS_OK;
704 }
705
706 NS_NOTREACHED("Unknown topic!");
707 return NS_ERROR_UNEXPECTED;
708 }
709
710 already_AddRefed<FileManager>
711 FileManagerInfo::GetFileManager(PersistenceType aPersistenceType,
712 const nsAString& aName) const
713 {
714 AssertIsOnIOThread();
715
716 const nsTArray<nsRefPtr<FileManager> >& managers =
717 GetImmutableArray(aPersistenceType);
718
719 for (uint32_t i = 0; i < managers.Length(); i++) {
720 const nsRefPtr<FileManager>& fileManager = managers[i];
721
722 if (fileManager->DatabaseName() == aName) {
723 nsRefPtr<FileManager> result = fileManager;
724 return result.forget();
725 }
726 }
727
728 return nullptr;
729 }
730
731 void
732 FileManagerInfo::AddFileManager(FileManager* aFileManager)
733 {
734 AssertIsOnIOThread();
735
736 nsTArray<nsRefPtr<FileManager> >& managers = GetArray(aFileManager->Type());
737
738 NS_ASSERTION(!managers.Contains(aFileManager), "Adding more than once?!");
739
740 managers.AppendElement(aFileManager);
741 }
742
743 void
744 FileManagerInfo::InvalidateAllFileManagers() const
745 {
746 AssertIsOnIOThread();
747
748 uint32_t i;
749
750 for (i = 0; i < mPersistentStorageFileManagers.Length(); i++) {
751 mPersistentStorageFileManagers[i]->Invalidate();
752 }
753
754 for (i = 0; i < mTemporaryStorageFileManagers.Length(); i++) {
755 mTemporaryStorageFileManagers[i]->Invalidate();
756 }
757 }
758
759 void
760 FileManagerInfo::InvalidateAndRemoveFileManagers(
761 PersistenceType aPersistenceType)
762 {
763 AssertIsOnIOThread();
764
765 nsTArray<nsRefPtr<FileManager > >& managers = GetArray(aPersistenceType);
766
767 for (uint32_t i = 0; i < managers.Length(); i++) {
768 managers[i]->Invalidate();
769 }
770
771 managers.Clear();
772 }
773
774 void
775 FileManagerInfo::InvalidateAndRemoveFileManager(
776 PersistenceType aPersistenceType,
777 const nsAString& aName)
778 {
779 AssertIsOnIOThread();
780
781 nsTArray<nsRefPtr<FileManager > >& managers = GetArray(aPersistenceType);
782
783 for (uint32_t i = 0; i < managers.Length(); i++) {
784 nsRefPtr<FileManager>& fileManager = managers[i];
785 if (fileManager->DatabaseName() == aName) {
786 fileManager->Invalidate();
787 managers.RemoveElementAt(i);
788 return;
789 }
790 }
791 }
792
793 nsTArray<nsRefPtr<FileManager> >&
794 FileManagerInfo::GetArray(PersistenceType aPersistenceType)
795 {
796 switch (aPersistenceType) {
797 case PERSISTENCE_TYPE_PERSISTENT:
798 return mPersistentStorageFileManagers;
799 case PERSISTENCE_TYPE_TEMPORARY:
800 return mTemporaryStorageFileManagers;
801
802 case PERSISTENCE_TYPE_INVALID:
803 default:
804 MOZ_CRASH("Bad storage type value!");
805 return mPersistentStorageFileManagers;
806 }
807 }
808
809 AsyncDeleteFileRunnable::AsyncDeleteFileRunnable(FileManager* aFileManager,
810 int64_t aFileId)
811 : mFileManager(aFileManager), mFileId(aFileId)
812 {
813 }
814
815 NS_IMPL_ISUPPORTS(AsyncDeleteFileRunnable,
816 nsIRunnable)
817
818 NS_IMETHODIMP
819 AsyncDeleteFileRunnable::Run()
820 {
821 AssertIsOnIOThread();
822
823 nsCOMPtr<nsIFile> directory = mFileManager->GetDirectory();
824 NS_ENSURE_TRUE(directory, NS_ERROR_FAILURE);
825
826 nsCOMPtr<nsIFile> file = mFileManager->GetFileForId(directory, mFileId);
827 NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);
828
829 nsresult rv;
830 int64_t fileSize;
831
832 if (mFileManager->Privilege() != Chrome) {
833 rv = file->GetFileSize(&fileSize);
834 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
835 }
836
837 rv = file->Remove(false);
838 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
839
840 if (mFileManager->Privilege() != Chrome) {
841 QuotaManager* quotaManager = QuotaManager::Get();
842 NS_ASSERTION(quotaManager, "Shouldn't be null!");
843
844 quotaManager->DecreaseUsageForOrigin(mFileManager->Type(),
845 mFileManager->Group(),
846 mFileManager->Origin(), fileSize);
847 }
848
849 directory = mFileManager->GetJournalDirectory();
850 NS_ENSURE_TRUE(directory, NS_ERROR_FAILURE);
851
852 file = mFileManager->GetFileForId(directory, mFileId);
853 NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);
854
855 rv = file->Remove(false);
856 NS_ENSURE_SUCCESS(rv, rv);
857
858 return NS_OK;
859 }
860
861 nsresult
862 GetFileReferencesHelper::DispatchAndReturnFileReferences(int32_t* aMemRefCnt,
863 int32_t* aDBRefCnt,
864 int32_t* aSliceRefCnt,
865 bool* aResult)
866 {
867 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
868
869 QuotaManager* quotaManager = QuotaManager::Get();
870 NS_ASSERTION(quotaManager, "Shouldn't be null!");
871
872 nsresult rv =
873 quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
874 NS_ENSURE_SUCCESS(rv, rv);
875
876 mozilla::MutexAutoLock autolock(mMutex);
877 while (mWaiting) {
878 mCondVar.Wait();
879 }
880
881 *aMemRefCnt = mMemRefCnt;
882 *aDBRefCnt = mDBRefCnt;
883 *aSliceRefCnt = mSliceRefCnt;
884 *aResult = mResult;
885
886 return NS_OK;
887 }
888
889 NS_IMPL_ISUPPORTS(GetFileReferencesHelper,
890 nsIRunnable)
891
892 NS_IMETHODIMP
893 GetFileReferencesHelper::Run()
894 {
895 AssertIsOnIOThread();
896
897 IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
898 NS_ASSERTION(mgr, "This should never fail!");
899
900 nsRefPtr<FileManager> fileManager =
901 mgr->GetFileManager(mPersistenceType, mOrigin, mDatabaseName);
902
903 if (fileManager) {
904 nsRefPtr<FileInfo> fileInfo = fileManager->GetFileInfo(mFileId);
905
906 if (fileInfo) {
907 fileInfo->GetReferences(&mMemRefCnt, &mDBRefCnt, &mSliceRefCnt);
908
909 if (mMemRefCnt != -1) {
910 // We added an extra temp ref, so account for that accordingly.
911 mMemRefCnt--;
912 }
913
914 mResult = true;
915 }
916 }
917
918 mozilla::MutexAutoLock lock(mMutex);
919 NS_ASSERTION(mWaiting, "Huh?!");
920
921 mWaiting = false;
922 mCondVar.Notify();
923
924 return NS_OK;
925 }

mercurial