1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/indexedDB/IndexedDatabaseManager.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,925 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=2 et sw=2 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "IndexedDatabaseManager.h" 1.11 + 1.12 +#include "nsIConsoleService.h" 1.13 +#include "nsIDiskSpaceWatcher.h" 1.14 +#include "nsIFile.h" 1.15 +#include "nsIFileStorage.h" 1.16 +#include "nsIObserverService.h" 1.17 +#include "nsIScriptError.h" 1.18 + 1.19 +#include "jsapi.h" 1.20 +#include "mozilla/ClearOnShutdown.h" 1.21 +#include "mozilla/CondVar.h" 1.22 +#include "mozilla/ContentEvents.h" 1.23 +#include "mozilla/dom/ErrorEventBinding.h" 1.24 +#include "mozilla/dom/quota/OriginOrPatternString.h" 1.25 +#include "mozilla/dom/quota/QuotaManager.h" 1.26 +#include "mozilla/dom/quota/Utilities.h" 1.27 +#include "mozilla/dom/TabContext.h" 1.28 +#include "mozilla/EventDispatcher.h" 1.29 +#include "mozilla/Services.h" 1.30 +#include "mozilla/Preferences.h" 1.31 +#include "mozilla/storage.h" 1.32 +#include "nsContentUtils.h" 1.33 +#include "nsThreadUtils.h" 1.34 + 1.35 +#include "IDBEvents.h" 1.36 +#include "IDBFactory.h" 1.37 +#include "IDBKeyRange.h" 1.38 +#include "IDBRequest.h" 1.39 + 1.40 +// Bindings for ResolveConstructors 1.41 +#include "mozilla/dom/IDBCursorBinding.h" 1.42 +#include "mozilla/dom/IDBDatabaseBinding.h" 1.43 +#include "mozilla/dom/IDBFactoryBinding.h" 1.44 +#include "mozilla/dom/IDBFileHandleBinding.h" 1.45 +#include "mozilla/dom/IDBKeyRangeBinding.h" 1.46 +#include "mozilla/dom/IDBIndexBinding.h" 1.47 +#include "mozilla/dom/IDBObjectStoreBinding.h" 1.48 +#include "mozilla/dom/IDBOpenDBRequestBinding.h" 1.49 +#include "mozilla/dom/IDBRequestBinding.h" 1.50 +#include "mozilla/dom/IDBTransactionBinding.h" 1.51 +#include "mozilla/dom/IDBVersionChangeEventBinding.h" 1.52 + 1.53 +#define IDB_STR "indexedDB" 1.54 + 1.55 +// The two possible values for the data argument when receiving the disk space 1.56 +// observer notification. 1.57 +#define LOW_DISK_SPACE_DATA_FULL "full" 1.58 +#define LOW_DISK_SPACE_DATA_FREE "free" 1.59 + 1.60 +USING_INDEXEDDB_NAMESPACE 1.61 +using namespace mozilla; 1.62 +using namespace mozilla::dom; 1.63 +USING_QUOTA_NAMESPACE 1.64 + 1.65 +BEGIN_INDEXEDDB_NAMESPACE 1.66 + 1.67 +class FileManagerInfo 1.68 +{ 1.69 +public: 1.70 + already_AddRefed<FileManager> 1.71 + GetFileManager(PersistenceType aPersistenceType, 1.72 + const nsAString& aName) const; 1.73 + 1.74 + void 1.75 + AddFileManager(FileManager* aFileManager); 1.76 + 1.77 + bool 1.78 + HasFileManagers() const 1.79 + { 1.80 + AssertIsOnIOThread(); 1.81 + 1.82 + return !mPersistentStorageFileManagers.IsEmpty() || 1.83 + !mTemporaryStorageFileManagers.IsEmpty(); 1.84 + } 1.85 + 1.86 + void 1.87 + InvalidateAllFileManagers() const; 1.88 + 1.89 + void 1.90 + InvalidateAndRemoveFileManagers(PersistenceType aPersistenceType); 1.91 + 1.92 + void 1.93 + InvalidateAndRemoveFileManager(PersistenceType aPersistenceType, 1.94 + const nsAString& aName); 1.95 + 1.96 +private: 1.97 + nsTArray<nsRefPtr<FileManager> >& 1.98 + GetArray(PersistenceType aPersistenceType); 1.99 + 1.100 + const nsTArray<nsRefPtr<FileManager> >& 1.101 + GetImmutableArray(PersistenceType aPersistenceType) const 1.102 + { 1.103 + return const_cast<FileManagerInfo*>(this)->GetArray(aPersistenceType); 1.104 + } 1.105 + 1.106 + nsTArray<nsRefPtr<FileManager> > mPersistentStorageFileManagers; 1.107 + nsTArray<nsRefPtr<FileManager> > mTemporaryStorageFileManagers; 1.108 +}; 1.109 + 1.110 +END_INDEXEDDB_NAMESPACE 1.111 + 1.112 +namespace { 1.113 + 1.114 +mozilla::StaticRefPtr<IndexedDatabaseManager> gDBManager; 1.115 + 1.116 +mozilla::Atomic<bool> gInitialized(false); 1.117 +mozilla::Atomic<bool> gClosed(false); 1.118 + 1.119 +class AsyncDeleteFileRunnable MOZ_FINAL : public nsIRunnable 1.120 +{ 1.121 +public: 1.122 + NS_DECL_THREADSAFE_ISUPPORTS 1.123 + NS_DECL_NSIRUNNABLE 1.124 + 1.125 + AsyncDeleteFileRunnable(FileManager* aFileManager, int64_t aFileId); 1.126 + 1.127 +private: 1.128 + nsRefPtr<FileManager> mFileManager; 1.129 + int64_t mFileId; 1.130 +}; 1.131 + 1.132 +class GetFileReferencesHelper MOZ_FINAL : public nsIRunnable 1.133 +{ 1.134 +public: 1.135 + NS_DECL_THREADSAFE_ISUPPORTS 1.136 + NS_DECL_NSIRUNNABLE 1.137 + 1.138 + GetFileReferencesHelper(PersistenceType aPersistenceType, 1.139 + const nsACString& aOrigin, 1.140 + const nsAString& aDatabaseName, 1.141 + int64_t aFileId) 1.142 + : mPersistenceType(aPersistenceType), 1.143 + mOrigin(aOrigin), 1.144 + mDatabaseName(aDatabaseName), 1.145 + mFileId(aFileId), 1.146 + mMutex(IndexedDatabaseManager::FileMutex()), 1.147 + mCondVar(mMutex, "GetFileReferencesHelper::mCondVar"), 1.148 + mMemRefCnt(-1), 1.149 + mDBRefCnt(-1), 1.150 + mSliceRefCnt(-1), 1.151 + mResult(false), 1.152 + mWaiting(true) 1.153 + { } 1.154 + 1.155 + nsresult 1.156 + DispatchAndReturnFileReferences(int32_t* aMemRefCnt, 1.157 + int32_t* aDBRefCnt, 1.158 + int32_t* aSliceRefCnt, 1.159 + bool* aResult); 1.160 + 1.161 +private: 1.162 + PersistenceType mPersistenceType; 1.163 + nsCString mOrigin; 1.164 + nsString mDatabaseName; 1.165 + int64_t mFileId; 1.166 + 1.167 + mozilla::Mutex& mMutex; 1.168 + mozilla::CondVar mCondVar; 1.169 + int32_t mMemRefCnt; 1.170 + int32_t mDBRefCnt; 1.171 + int32_t mSliceRefCnt; 1.172 + bool mResult; 1.173 + bool mWaiting; 1.174 +}; 1.175 + 1.176 +struct MOZ_STACK_CLASS InvalidateInfo 1.177 +{ 1.178 + InvalidateInfo(PersistenceType aPersistenceType, const nsACString& aPattern) 1.179 + : persistenceType(aPersistenceType), pattern(aPattern) 1.180 + { } 1.181 + 1.182 + PersistenceType persistenceType; 1.183 + const nsACString& pattern; 1.184 +}; 1.185 + 1.186 +} // anonymous namespace 1.187 + 1.188 +IndexedDatabaseManager::IndexedDatabaseManager() 1.189 +: mFileMutex("IndexedDatabaseManager.mFileMutex") 1.190 +{ 1.191 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.192 +} 1.193 + 1.194 +IndexedDatabaseManager::~IndexedDatabaseManager() 1.195 +{ 1.196 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.197 +} 1.198 + 1.199 +bool IndexedDatabaseManager::sIsMainProcess = false; 1.200 +mozilla::Atomic<bool> IndexedDatabaseManager::sLowDiskSpaceMode(false); 1.201 + 1.202 +// static 1.203 +IndexedDatabaseManager* 1.204 +IndexedDatabaseManager::GetOrCreate() 1.205 +{ 1.206 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.207 + 1.208 + if (IsClosed()) { 1.209 + NS_ERROR("Calling GetOrCreate() after shutdown!"); 1.210 + return nullptr; 1.211 + } 1.212 + 1.213 + if (!gDBManager) { 1.214 + sIsMainProcess = XRE_GetProcessType() == GeckoProcessType_Default; 1.215 + 1.216 + if (sIsMainProcess && Preferences::GetBool("disk_space_watcher.enabled", false)) { 1.217 + // See if we're starting up in low disk space conditions. 1.218 + nsCOMPtr<nsIDiskSpaceWatcher> watcher = 1.219 + do_GetService(DISKSPACEWATCHER_CONTRACTID); 1.220 + if (watcher) { 1.221 + bool isDiskFull; 1.222 + if (NS_SUCCEEDED(watcher->GetIsDiskFull(&isDiskFull))) { 1.223 + sLowDiskSpaceMode = isDiskFull; 1.224 + } 1.225 + else { 1.226 + NS_WARNING("GetIsDiskFull failed!"); 1.227 + } 1.228 + } 1.229 + else { 1.230 + NS_WARNING("No disk space watcher component available!"); 1.231 + } 1.232 + } 1.233 + 1.234 + nsRefPtr<IndexedDatabaseManager> instance(new IndexedDatabaseManager()); 1.235 + 1.236 + nsresult rv = instance->Init(); 1.237 + NS_ENSURE_SUCCESS(rv, nullptr); 1.238 + 1.239 + if (gInitialized.exchange(true)) { 1.240 + NS_ERROR("Initialized more than once?!"); 1.241 + } 1.242 + 1.243 + gDBManager = instance; 1.244 + 1.245 + ClearOnShutdown(&gDBManager); 1.246 + } 1.247 + 1.248 + return gDBManager; 1.249 +} 1.250 + 1.251 +// static 1.252 +IndexedDatabaseManager* 1.253 +IndexedDatabaseManager::Get() 1.254 +{ 1.255 + // Does not return an owning reference. 1.256 + return gDBManager; 1.257 +} 1.258 + 1.259 +// static 1.260 +IndexedDatabaseManager* 1.261 +IndexedDatabaseManager::FactoryCreate() 1.262 +{ 1.263 + // Returns a raw pointer that carries an owning reference! Lame, but the 1.264 + // singleton factory macros force this. 1.265 + IndexedDatabaseManager* mgr = GetOrCreate(); 1.266 + NS_IF_ADDREF(mgr); 1.267 + return mgr; 1.268 +} 1.269 + 1.270 +nsresult 1.271 +IndexedDatabaseManager::Init() 1.272 +{ 1.273 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.274 + 1.275 + // Make sure that the quota manager is up. 1.276 + QuotaManager* qm = QuotaManager::GetOrCreate(); 1.277 + NS_ENSURE_STATE(qm); 1.278 + 1.279 + // During Init() we can't yet call IsMainProcess(), just check sIsMainProcess 1.280 + // directly. 1.281 + if (sIsMainProcess) { 1.282 + // Must initialize the storage service on the main thread. 1.283 + nsCOMPtr<mozIStorageService> ss = 1.284 + do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID); 1.285 + NS_ENSURE_STATE(ss); 1.286 + 1.287 + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); 1.288 + NS_ENSURE_STATE(obs); 1.289 + 1.290 + nsresult rv = 1.291 + obs->AddObserver(this, DISKSPACEWATCHER_OBSERVER_TOPIC, false); 1.292 + NS_ENSURE_SUCCESS(rv, rv); 1.293 + } 1.294 + 1.295 + return NS_OK; 1.296 +} 1.297 + 1.298 +void 1.299 +IndexedDatabaseManager::Destroy() 1.300 +{ 1.301 + // Setting the closed flag prevents the service from being recreated. 1.302 + // Don't set it though if there's no real instance created. 1.303 + if (gInitialized && gClosed.exchange(true)) { 1.304 + NS_ERROR("Shutdown more than once?!"); 1.305 + } 1.306 + 1.307 + delete this; 1.308 +} 1.309 + 1.310 +// static 1.311 +nsresult 1.312 +IndexedDatabaseManager::FireWindowOnError(nsPIDOMWindow* aOwner, 1.313 + EventChainPostVisitor& aVisitor) 1.314 +{ 1.315 + NS_ENSURE_TRUE(aVisitor.mDOMEvent, NS_ERROR_UNEXPECTED); 1.316 + if (!aOwner) { 1.317 + return NS_OK; 1.318 + } 1.319 + 1.320 + if (aVisitor.mEventStatus == nsEventStatus_eConsumeNoDefault) { 1.321 + return NS_OK; 1.322 + } 1.323 + 1.324 + nsString type; 1.325 + nsresult rv = aVisitor.mDOMEvent->GetType(type); 1.326 + NS_ENSURE_SUCCESS(rv, rv); 1.327 + 1.328 + if (!type.EqualsLiteral(ERROR_EVT_STR)) { 1.329 + return NS_OK; 1.330 + } 1.331 + 1.332 + nsCOMPtr<EventTarget> eventTarget = 1.333 + aVisitor.mDOMEvent->InternalDOMEvent()->GetTarget(); 1.334 + 1.335 + IDBRequest* request = static_cast<IDBRequest*>(eventTarget.get()); 1.336 + NS_ENSURE_TRUE(request, NS_ERROR_UNEXPECTED); 1.337 + 1.338 + ErrorResult ret; 1.339 + nsRefPtr<DOMError> error = request->GetError(ret); 1.340 + if (ret.Failed()) { 1.341 + return ret.ErrorCode(); 1.342 + } 1.343 + 1.344 + nsString errorName; 1.345 + if (error) { 1.346 + error->GetName(errorName); 1.347 + } 1.348 + 1.349 + ThreadsafeAutoJSContext cx; 1.350 + RootedDictionary<ErrorEventInit> init(cx); 1.351 + request->FillScriptErrorEvent(init); 1.352 + 1.353 + init.mMessage = errorName; 1.354 + init.mCancelable = true; 1.355 + init.mBubbles = true; 1.356 + 1.357 + nsCOMPtr<nsIScriptGlobalObject> sgo(do_QueryInterface(aOwner)); 1.358 + NS_ASSERTION(sgo, "How can this happen?!"); 1.359 + 1.360 + nsEventStatus status = nsEventStatus_eIgnore; 1.361 + if (NS_FAILED(sgo->HandleScriptError(init, &status))) { 1.362 + NS_WARNING("Failed to dispatch script error event"); 1.363 + status = nsEventStatus_eIgnore; 1.364 + } 1.365 + 1.366 + bool preventDefaultCalled = status == nsEventStatus_eConsumeNoDefault; 1.367 + if (preventDefaultCalled) { 1.368 + return NS_OK; 1.369 + } 1.370 + 1.371 + // Log an error to the error console. 1.372 + nsCOMPtr<nsIScriptError> scriptError = 1.373 + do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv); 1.374 + NS_ENSURE_SUCCESS(rv, rv); 1.375 + 1.376 + if (NS_FAILED(scriptError->InitWithWindowID(errorName, 1.377 + init.mFilename, 1.378 + EmptyString(), init.mLineno, 1.379 + 0, 0, 1.380 + "IndexedDB", 1.381 + aOwner->WindowID()))) { 1.382 + NS_WARNING("Failed to init script error!"); 1.383 + return NS_ERROR_FAILURE; 1.384 + } 1.385 + 1.386 + nsCOMPtr<nsIConsoleService> consoleService = 1.387 + do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv); 1.388 + NS_ENSURE_SUCCESS(rv, rv); 1.389 + 1.390 + return consoleService->LogMessage(scriptError); 1.391 +} 1.392 + 1.393 +// static 1.394 +bool 1.395 +IndexedDatabaseManager::TabContextMayAccessOrigin(const TabContext& aContext, 1.396 + const nsACString& aOrigin) 1.397 +{ 1.398 + NS_ASSERTION(!aOrigin.IsEmpty(), "Empty origin!"); 1.399 + 1.400 + // If aContext is for a browser element, it's allowed only to access other 1.401 + // browser elements. But if aContext is not for a browser element, it may 1.402 + // access both browser and non-browser elements. 1.403 + nsAutoCString pattern; 1.404 + QuotaManager::GetOriginPatternStringMaybeIgnoreBrowser( 1.405 + aContext.OwnOrContainingAppId(), 1.406 + aContext.IsBrowserElement(), 1.407 + pattern); 1.408 + 1.409 + return PatternMatchesOrigin(pattern, aOrigin); 1.410 +} 1.411 + 1.412 +// static 1.413 +bool 1.414 +IndexedDatabaseManager::DefineIndexedDB(JSContext* aCx, 1.415 + JS::Handle<JSObject*> aGlobal) 1.416 +{ 1.417 + MOZ_ASSERT(NS_IsMainThread()); 1.418 + MOZ_ASSERT(nsContentUtils::IsCallerChrome(), "Only for chrome!"); 1.419 + MOZ_ASSERT(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL, 1.420 + "Passed object is not a global object!"); 1.421 + 1.422 + if (!IDBCursorBinding::GetConstructorObject(aCx, aGlobal) || 1.423 + !IDBCursorWithValueBinding::GetConstructorObject(aCx, aGlobal) || 1.424 + !IDBDatabaseBinding::GetConstructorObject(aCx, aGlobal) || 1.425 + !IDBFactoryBinding::GetConstructorObject(aCx, aGlobal) || 1.426 + !IDBFileHandleBinding::GetConstructorObject(aCx, aGlobal) || 1.427 + !IDBIndexBinding::GetConstructorObject(aCx, aGlobal) || 1.428 + !IDBKeyRangeBinding::GetConstructorObject(aCx, aGlobal) || 1.429 + !IDBObjectStoreBinding::GetConstructorObject(aCx, aGlobal) || 1.430 + !IDBOpenDBRequestBinding::GetConstructorObject(aCx, aGlobal) || 1.431 + !IDBRequestBinding::GetConstructorObject(aCx, aGlobal) || 1.432 + !IDBTransactionBinding::GetConstructorObject(aCx, aGlobal) || 1.433 + !IDBVersionChangeEventBinding::GetConstructorObject(aCx, aGlobal)) 1.434 + { 1.435 + return false; 1.436 + } 1.437 + 1.438 + nsRefPtr<IDBFactory> factory; 1.439 + if (NS_FAILED(IDBFactory::Create(aCx, aGlobal, nullptr, 1.440 + getter_AddRefs(factory)))) { 1.441 + return false; 1.442 + } 1.443 + 1.444 + MOZ_ASSERT(factory, "This should never fail for chrome!"); 1.445 + 1.446 + JS::Rooted<JS::Value> indexedDB(aCx); 1.447 + js::AssertSameCompartment(aCx, aGlobal); 1.448 + if (!WrapNewBindingObject(aCx, factory, &indexedDB)) { 1.449 + return false; 1.450 + } 1.451 + 1.452 + return JS_DefineProperty(aCx, aGlobal, IDB_STR, indexedDB, JSPROP_ENUMERATE); 1.453 +} 1.454 + 1.455 +// static 1.456 +bool 1.457 +IndexedDatabaseManager::IsClosed() 1.458 +{ 1.459 + return gClosed; 1.460 +} 1.461 + 1.462 +#ifdef DEBUG 1.463 +// static 1.464 +bool 1.465 +IndexedDatabaseManager::IsMainProcess() 1.466 +{ 1.467 + NS_ASSERTION(gDBManager, 1.468 + "IsMainProcess() called before indexedDB has been initialized!"); 1.469 + NS_ASSERTION((XRE_GetProcessType() == GeckoProcessType_Default) == 1.470 + sIsMainProcess, "XRE_GetProcessType changed its tune!"); 1.471 + return sIsMainProcess; 1.472 +} 1.473 + 1.474 +//static 1.475 +bool 1.476 +IndexedDatabaseManager::InLowDiskSpaceMode() 1.477 +{ 1.478 + NS_ASSERTION(gDBManager, 1.479 + "InLowDiskSpaceMode() called before indexedDB has been " 1.480 + "initialized!"); 1.481 + return sLowDiskSpaceMode; 1.482 +} 1.483 +#endif 1.484 + 1.485 +already_AddRefed<FileManager> 1.486 +IndexedDatabaseManager::GetFileManager(PersistenceType aPersistenceType, 1.487 + const nsACString& aOrigin, 1.488 + const nsAString& aDatabaseName) 1.489 +{ 1.490 + AssertIsOnIOThread(); 1.491 + 1.492 + FileManagerInfo* info; 1.493 + if (!mFileManagerInfos.Get(aOrigin, &info)) { 1.494 + return nullptr; 1.495 + } 1.496 + 1.497 + nsRefPtr<FileManager> fileManager = 1.498 + info->GetFileManager(aPersistenceType, aDatabaseName); 1.499 + 1.500 + return fileManager.forget(); 1.501 +} 1.502 + 1.503 +void 1.504 +IndexedDatabaseManager::AddFileManager(FileManager* aFileManager) 1.505 +{ 1.506 + AssertIsOnIOThread(); 1.507 + NS_ASSERTION(aFileManager, "Null file manager!"); 1.508 + 1.509 + FileManagerInfo* info; 1.510 + if (!mFileManagerInfos.Get(aFileManager->Origin(), &info)) { 1.511 + info = new FileManagerInfo(); 1.512 + mFileManagerInfos.Put(aFileManager->Origin(), info); 1.513 + } 1.514 + 1.515 + info->AddFileManager(aFileManager); 1.516 +} 1.517 + 1.518 +// static 1.519 +PLDHashOperator 1.520 +IndexedDatabaseManager::InvalidateAndRemoveFileManagers( 1.521 + const nsACString& aKey, 1.522 + nsAutoPtr<FileManagerInfo>& aValue, 1.523 + void* aUserArg) 1.524 +{ 1.525 + AssertIsOnIOThread(); 1.526 + NS_ASSERTION(!aKey.IsEmpty(), "Empty key!"); 1.527 + NS_ASSERTION(aValue, "Null pointer!"); 1.528 + 1.529 + if (!aUserArg) { 1.530 + aValue->InvalidateAllFileManagers(); 1.531 + return PL_DHASH_REMOVE; 1.532 + } 1.533 + 1.534 + InvalidateInfo* info = static_cast<InvalidateInfo*>(aUserArg); 1.535 + 1.536 + if (PatternMatchesOrigin(info->pattern, aKey)) { 1.537 + aValue->InvalidateAndRemoveFileManagers(info->persistenceType); 1.538 + 1.539 + if (!aValue->HasFileManagers()) { 1.540 + return PL_DHASH_REMOVE; 1.541 + } 1.542 + } 1.543 + 1.544 + return PL_DHASH_NEXT; 1.545 +} 1.546 + 1.547 +void 1.548 +IndexedDatabaseManager::InvalidateAllFileManagers() 1.549 +{ 1.550 + AssertIsOnIOThread(); 1.551 + 1.552 + mFileManagerInfos.Enumerate(InvalidateAndRemoveFileManagers, nullptr); 1.553 +} 1.554 + 1.555 +void 1.556 +IndexedDatabaseManager::InvalidateFileManagers( 1.557 + PersistenceType aPersistenceType, 1.558 + const OriginOrPatternString& aOriginOrPattern) 1.559 +{ 1.560 + AssertIsOnIOThread(); 1.561 + NS_ASSERTION(!aOriginOrPattern.IsEmpty(), "Empty pattern!"); 1.562 + 1.563 + if (aOriginOrPattern.IsOrigin()) { 1.564 + FileManagerInfo* info; 1.565 + if (!mFileManagerInfos.Get(aOriginOrPattern, &info)) { 1.566 + return; 1.567 + } 1.568 + 1.569 + info->InvalidateAndRemoveFileManagers(aPersistenceType); 1.570 + 1.571 + if (!info->HasFileManagers()) { 1.572 + mFileManagerInfos.Remove(aOriginOrPattern); 1.573 + } 1.574 + } 1.575 + else { 1.576 + InvalidateInfo info(aPersistenceType, aOriginOrPattern); 1.577 + mFileManagerInfos.Enumerate(InvalidateAndRemoveFileManagers, &info); 1.578 + } 1.579 +} 1.580 + 1.581 +void 1.582 +IndexedDatabaseManager::InvalidateFileManager(PersistenceType aPersistenceType, 1.583 + const nsACString& aOrigin, 1.584 + const nsAString& aDatabaseName) 1.585 +{ 1.586 + AssertIsOnIOThread(); 1.587 + 1.588 + FileManagerInfo* info; 1.589 + if (!mFileManagerInfos.Get(aOrigin, &info)) { 1.590 + return; 1.591 + } 1.592 + 1.593 + info->InvalidateAndRemoveFileManager(aPersistenceType, aDatabaseName); 1.594 + 1.595 + if (!info->HasFileManagers()) { 1.596 + mFileManagerInfos.Remove(aOrigin); 1.597 + } 1.598 +} 1.599 + 1.600 +nsresult 1.601 +IndexedDatabaseManager::AsyncDeleteFile(FileManager* aFileManager, 1.602 + int64_t aFileId) 1.603 +{ 1.604 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.605 + 1.606 + NS_ENSURE_ARG_POINTER(aFileManager); 1.607 + 1.608 + QuotaManager* quotaManager = QuotaManager::Get(); 1.609 + NS_ASSERTION(quotaManager, "Shouldn't be null!"); 1.610 + 1.611 + // See if we're currently clearing the storages for this origin. If so then 1.612 + // we pretend that we've already deleted everything. 1.613 + if (quotaManager->IsClearOriginPending( 1.614 + aFileManager->Origin(), 1.615 + Nullable<PersistenceType>(aFileManager->Type()))) { 1.616 + return NS_OK; 1.617 + } 1.618 + 1.619 + nsRefPtr<AsyncDeleteFileRunnable> runnable = 1.620 + new AsyncDeleteFileRunnable(aFileManager, aFileId); 1.621 + 1.622 + nsresult rv = 1.623 + quotaManager->IOThread()->Dispatch(runnable, NS_DISPATCH_NORMAL); 1.624 + NS_ENSURE_SUCCESS(rv, rv); 1.625 + 1.626 + return NS_OK; 1.627 +} 1.628 + 1.629 +nsresult 1.630 +IndexedDatabaseManager::BlockAndGetFileReferences( 1.631 + PersistenceType aPersistenceType, 1.632 + const nsACString& aOrigin, 1.633 + const nsAString& aDatabaseName, 1.634 + int64_t aFileId, 1.635 + int32_t* aRefCnt, 1.636 + int32_t* aDBRefCnt, 1.637 + int32_t* aSliceRefCnt, 1.638 + bool* aResult) 1.639 +{ 1.640 + nsRefPtr<GetFileReferencesHelper> helper = 1.641 + new GetFileReferencesHelper(aPersistenceType, aOrigin, aDatabaseName, 1.642 + aFileId); 1.643 + 1.644 + nsresult rv = helper->DispatchAndReturnFileReferences(aRefCnt, aDBRefCnt, 1.645 + aSliceRefCnt, aResult); 1.646 + NS_ENSURE_SUCCESS(rv, rv); 1.647 + 1.648 + return NS_OK; 1.649 +} 1.650 + 1.651 +NS_IMPL_ADDREF(IndexedDatabaseManager) 1.652 +NS_IMPL_RELEASE_WITH_DESTROY(IndexedDatabaseManager, Destroy()) 1.653 +NS_IMPL_QUERY_INTERFACE(IndexedDatabaseManager, nsIIndexedDatabaseManager, 1.654 + nsIObserver) 1.655 + 1.656 +NS_IMETHODIMP 1.657 +IndexedDatabaseManager::InitWindowless(JS::Handle<JS::Value> aGlobal, JSContext* aCx) 1.658 +{ 1.659 + NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE); 1.660 + 1.661 + JS::Rooted<JSObject*> global(aCx, JSVAL_TO_OBJECT(aGlobal)); 1.662 + if (!(js::GetObjectClass(global)->flags & JSCLASS_DOM_GLOBAL)) { 1.663 + NS_WARNING("Passed object is not a global object!"); 1.664 + return NS_ERROR_FAILURE; 1.665 + } 1.666 + 1.667 + bool hasIndexedDB; 1.668 + if (!JS_HasProperty(aCx, global, IDB_STR, &hasIndexedDB)) { 1.669 + return NS_ERROR_FAILURE; 1.670 + } 1.671 + 1.672 + if (hasIndexedDB) { 1.673 + NS_WARNING("Passed object already has an 'indexedDB' property!"); 1.674 + return NS_ERROR_FAILURE; 1.675 + } 1.676 + 1.677 + if (!DefineIndexedDB(aCx, global)) { 1.678 + return NS_ERROR_FAILURE; 1.679 + } 1.680 + 1.681 + return NS_OK; 1.682 +} 1.683 + 1.684 +NS_IMETHODIMP 1.685 +IndexedDatabaseManager::Observe(nsISupports* aSubject, const char* aTopic, 1.686 + const char16_t* aData) 1.687 +{ 1.688 + NS_ASSERTION(IsMainProcess(), "Wrong process!"); 1.689 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.690 + 1.691 + if (!strcmp(aTopic, DISKSPACEWATCHER_OBSERVER_TOPIC)) { 1.692 + NS_ASSERTION(aData, "No data?!"); 1.693 + 1.694 + const nsDependentString data(aData); 1.695 + 1.696 + if (data.EqualsLiteral(LOW_DISK_SPACE_DATA_FULL)) { 1.697 + sLowDiskSpaceMode = true; 1.698 + } 1.699 + else if (data.EqualsLiteral(LOW_DISK_SPACE_DATA_FREE)) { 1.700 + sLowDiskSpaceMode = false; 1.701 + } 1.702 + else { 1.703 + NS_NOTREACHED("Unknown data value!"); 1.704 + } 1.705 + 1.706 + return NS_OK; 1.707 + } 1.708 + 1.709 + NS_NOTREACHED("Unknown topic!"); 1.710 + return NS_ERROR_UNEXPECTED; 1.711 + } 1.712 + 1.713 +already_AddRefed<FileManager> 1.714 +FileManagerInfo::GetFileManager(PersistenceType aPersistenceType, 1.715 + const nsAString& aName) const 1.716 +{ 1.717 + AssertIsOnIOThread(); 1.718 + 1.719 + const nsTArray<nsRefPtr<FileManager> >& managers = 1.720 + GetImmutableArray(aPersistenceType); 1.721 + 1.722 + for (uint32_t i = 0; i < managers.Length(); i++) { 1.723 + const nsRefPtr<FileManager>& fileManager = managers[i]; 1.724 + 1.725 + if (fileManager->DatabaseName() == aName) { 1.726 + nsRefPtr<FileManager> result = fileManager; 1.727 + return result.forget(); 1.728 + } 1.729 + } 1.730 + 1.731 + return nullptr; 1.732 +} 1.733 + 1.734 +void 1.735 +FileManagerInfo::AddFileManager(FileManager* aFileManager) 1.736 +{ 1.737 + AssertIsOnIOThread(); 1.738 + 1.739 + nsTArray<nsRefPtr<FileManager> >& managers = GetArray(aFileManager->Type()); 1.740 + 1.741 + NS_ASSERTION(!managers.Contains(aFileManager), "Adding more than once?!"); 1.742 + 1.743 + managers.AppendElement(aFileManager); 1.744 +} 1.745 + 1.746 +void 1.747 +FileManagerInfo::InvalidateAllFileManagers() const 1.748 +{ 1.749 + AssertIsOnIOThread(); 1.750 + 1.751 + uint32_t i; 1.752 + 1.753 + for (i = 0; i < mPersistentStorageFileManagers.Length(); i++) { 1.754 + mPersistentStorageFileManagers[i]->Invalidate(); 1.755 + } 1.756 + 1.757 + for (i = 0; i < mTemporaryStorageFileManagers.Length(); i++) { 1.758 + mTemporaryStorageFileManagers[i]->Invalidate(); 1.759 + } 1.760 +} 1.761 + 1.762 +void 1.763 +FileManagerInfo::InvalidateAndRemoveFileManagers( 1.764 + PersistenceType aPersistenceType) 1.765 +{ 1.766 + AssertIsOnIOThread(); 1.767 + 1.768 + nsTArray<nsRefPtr<FileManager > >& managers = GetArray(aPersistenceType); 1.769 + 1.770 + for (uint32_t i = 0; i < managers.Length(); i++) { 1.771 + managers[i]->Invalidate(); 1.772 + } 1.773 + 1.774 + managers.Clear(); 1.775 +} 1.776 + 1.777 +void 1.778 +FileManagerInfo::InvalidateAndRemoveFileManager( 1.779 + PersistenceType aPersistenceType, 1.780 + const nsAString& aName) 1.781 +{ 1.782 + AssertIsOnIOThread(); 1.783 + 1.784 + nsTArray<nsRefPtr<FileManager > >& managers = GetArray(aPersistenceType); 1.785 + 1.786 + for (uint32_t i = 0; i < managers.Length(); i++) { 1.787 + nsRefPtr<FileManager>& fileManager = managers[i]; 1.788 + if (fileManager->DatabaseName() == aName) { 1.789 + fileManager->Invalidate(); 1.790 + managers.RemoveElementAt(i); 1.791 + return; 1.792 + } 1.793 + } 1.794 +} 1.795 + 1.796 +nsTArray<nsRefPtr<FileManager> >& 1.797 +FileManagerInfo::GetArray(PersistenceType aPersistenceType) 1.798 +{ 1.799 + switch (aPersistenceType) { 1.800 + case PERSISTENCE_TYPE_PERSISTENT: 1.801 + return mPersistentStorageFileManagers; 1.802 + case PERSISTENCE_TYPE_TEMPORARY: 1.803 + return mTemporaryStorageFileManagers; 1.804 + 1.805 + case PERSISTENCE_TYPE_INVALID: 1.806 + default: 1.807 + MOZ_CRASH("Bad storage type value!"); 1.808 + return mPersistentStorageFileManagers; 1.809 + } 1.810 +} 1.811 + 1.812 +AsyncDeleteFileRunnable::AsyncDeleteFileRunnable(FileManager* aFileManager, 1.813 + int64_t aFileId) 1.814 +: mFileManager(aFileManager), mFileId(aFileId) 1.815 +{ 1.816 +} 1.817 + 1.818 +NS_IMPL_ISUPPORTS(AsyncDeleteFileRunnable, 1.819 + nsIRunnable) 1.820 + 1.821 +NS_IMETHODIMP 1.822 +AsyncDeleteFileRunnable::Run() 1.823 +{ 1.824 + AssertIsOnIOThread(); 1.825 + 1.826 + nsCOMPtr<nsIFile> directory = mFileManager->GetDirectory(); 1.827 + NS_ENSURE_TRUE(directory, NS_ERROR_FAILURE); 1.828 + 1.829 + nsCOMPtr<nsIFile> file = mFileManager->GetFileForId(directory, mFileId); 1.830 + NS_ENSURE_TRUE(file, NS_ERROR_FAILURE); 1.831 + 1.832 + nsresult rv; 1.833 + int64_t fileSize; 1.834 + 1.835 + if (mFileManager->Privilege() != Chrome) { 1.836 + rv = file->GetFileSize(&fileSize); 1.837 + NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); 1.838 + } 1.839 + 1.840 + rv = file->Remove(false); 1.841 + NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); 1.842 + 1.843 + if (mFileManager->Privilege() != Chrome) { 1.844 + QuotaManager* quotaManager = QuotaManager::Get(); 1.845 + NS_ASSERTION(quotaManager, "Shouldn't be null!"); 1.846 + 1.847 + quotaManager->DecreaseUsageForOrigin(mFileManager->Type(), 1.848 + mFileManager->Group(), 1.849 + mFileManager->Origin(), fileSize); 1.850 + } 1.851 + 1.852 + directory = mFileManager->GetJournalDirectory(); 1.853 + NS_ENSURE_TRUE(directory, NS_ERROR_FAILURE); 1.854 + 1.855 + file = mFileManager->GetFileForId(directory, mFileId); 1.856 + NS_ENSURE_TRUE(file, NS_ERROR_FAILURE); 1.857 + 1.858 + rv = file->Remove(false); 1.859 + NS_ENSURE_SUCCESS(rv, rv); 1.860 + 1.861 + return NS_OK; 1.862 +} 1.863 + 1.864 +nsresult 1.865 +GetFileReferencesHelper::DispatchAndReturnFileReferences(int32_t* aMemRefCnt, 1.866 + int32_t* aDBRefCnt, 1.867 + int32_t* aSliceRefCnt, 1.868 + bool* aResult) 1.869 +{ 1.870 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.871 + 1.872 + QuotaManager* quotaManager = QuotaManager::Get(); 1.873 + NS_ASSERTION(quotaManager, "Shouldn't be null!"); 1.874 + 1.875 + nsresult rv = 1.876 + quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL); 1.877 + NS_ENSURE_SUCCESS(rv, rv); 1.878 + 1.879 + mozilla::MutexAutoLock autolock(mMutex); 1.880 + while (mWaiting) { 1.881 + mCondVar.Wait(); 1.882 + } 1.883 + 1.884 + *aMemRefCnt = mMemRefCnt; 1.885 + *aDBRefCnt = mDBRefCnt; 1.886 + *aSliceRefCnt = mSliceRefCnt; 1.887 + *aResult = mResult; 1.888 + 1.889 + return NS_OK; 1.890 +} 1.891 + 1.892 +NS_IMPL_ISUPPORTS(GetFileReferencesHelper, 1.893 + nsIRunnable) 1.894 + 1.895 +NS_IMETHODIMP 1.896 +GetFileReferencesHelper::Run() 1.897 +{ 1.898 + AssertIsOnIOThread(); 1.899 + 1.900 + IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get(); 1.901 + NS_ASSERTION(mgr, "This should never fail!"); 1.902 + 1.903 + nsRefPtr<FileManager> fileManager = 1.904 + mgr->GetFileManager(mPersistenceType, mOrigin, mDatabaseName); 1.905 + 1.906 + if (fileManager) { 1.907 + nsRefPtr<FileInfo> fileInfo = fileManager->GetFileInfo(mFileId); 1.908 + 1.909 + if (fileInfo) { 1.910 + fileInfo->GetReferences(&mMemRefCnt, &mDBRefCnt, &mSliceRefCnt); 1.911 + 1.912 + if (mMemRefCnt != -1) { 1.913 + // We added an extra temp ref, so account for that accordingly. 1.914 + mMemRefCnt--; 1.915 + } 1.916 + 1.917 + mResult = true; 1.918 + } 1.919 + } 1.920 + 1.921 + mozilla::MutexAutoLock lock(mMutex); 1.922 + NS_ASSERTION(mWaiting, "Huh?!"); 1.923 + 1.924 + mWaiting = false; 1.925 + mCondVar.Notify(); 1.926 + 1.927 + return NS_OK; 1.928 +}