1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/indexedDB/Client.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,369 @@ 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 file, 1.8 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "Client.h" 1.11 + 1.12 +#include "mozilla/dom/quota/QuotaManager.h" 1.13 +#include "mozilla/dom/quota/UsageInfo.h" 1.14 +#include "mozilla/dom/quota/Utilities.h" 1.15 + 1.16 +#include "IDBDatabase.h" 1.17 +#include "IndexedDatabaseManager.h" 1.18 +#include "TransactionThreadPool.h" 1.19 +#include "nsISimpleEnumerator.h" 1.20 + 1.21 +USING_INDEXEDDB_NAMESPACE 1.22 +using mozilla::dom::quota::AssertIsOnIOThread; 1.23 +using mozilla::dom::quota::QuotaManager; 1.24 + 1.25 +namespace { 1.26 + 1.27 +bool 1.28 +GetDatabaseBaseFilename(const nsAString& aFilename, 1.29 + nsAString& aDatabaseBaseFilename) 1.30 +{ 1.31 + NS_ASSERTION(!aFilename.IsEmpty(), "Bad argument!"); 1.32 + 1.33 + NS_NAMED_LITERAL_STRING(sqlite, ".sqlite"); 1.34 + 1.35 + if (!StringEndsWith(aFilename, sqlite)) { 1.36 + return false; 1.37 + } 1.38 + 1.39 + aDatabaseBaseFilename = 1.40 + Substring(aFilename, 0, aFilename.Length() - sqlite.Length()); 1.41 + 1.42 + return true; 1.43 +} 1.44 + 1.45 +} // anonymous namespace 1.46 + 1.47 +// This needs to be fully qualified to not confuse trace refcnt assertions. 1.48 +NS_IMPL_ADDREF(mozilla::dom::indexedDB::Client) 1.49 +NS_IMPL_RELEASE(mozilla::dom::indexedDB::Client) 1.50 + 1.51 +nsresult 1.52 +Client::InitOrigin(PersistenceType aPersistenceType, const nsACString& aGroup, 1.53 + const nsACString& aOrigin, UsageInfo* aUsageInfo) 1.54 +{ 1.55 + AssertIsOnIOThread(); 1.56 + 1.57 + nsCOMPtr<nsIFile> directory; 1.58 + nsresult rv = 1.59 + GetDirectory(aPersistenceType, aOrigin, getter_AddRefs(directory)); 1.60 + NS_ENSURE_SUCCESS(rv, rv); 1.61 + 1.62 + // We need to see if there are any files in the directory already. If they 1.63 + // are database files then we need to cleanup stored files (if it's needed) 1.64 + // and also get the usage. 1.65 + 1.66 + nsAutoTArray<nsString, 20> subdirsToProcess; 1.67 + nsAutoTArray<nsCOMPtr<nsIFile> , 20> unknownFiles; 1.68 + nsTHashtable<nsStringHashKey> validSubdirs(20); 1.69 + 1.70 + nsCOMPtr<nsISimpleEnumerator> entries; 1.71 + rv = directory->GetDirectoryEntries(getter_AddRefs(entries)); 1.72 + NS_ENSURE_SUCCESS(rv, rv); 1.73 + 1.74 + bool hasMore; 1.75 + while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && 1.76 + hasMore && (!aUsageInfo || !aUsageInfo->Canceled())) { 1.77 + nsCOMPtr<nsISupports> entry; 1.78 + rv = entries->GetNext(getter_AddRefs(entry)); 1.79 + NS_ENSURE_SUCCESS(rv, rv); 1.80 + 1.81 + nsCOMPtr<nsIFile> file = do_QueryInterface(entry); 1.82 + NS_ENSURE_TRUE(file, NS_NOINTERFACE); 1.83 + 1.84 + nsString leafName; 1.85 + rv = file->GetLeafName(leafName); 1.86 + NS_ENSURE_SUCCESS(rv, rv); 1.87 + 1.88 + if (StringEndsWith(leafName, NS_LITERAL_STRING(".sqlite-journal"))) { 1.89 + continue; 1.90 + } 1.91 + 1.92 + if (leafName.EqualsLiteral(DSSTORE_FILE_NAME)) { 1.93 + continue; 1.94 + } 1.95 + 1.96 + bool isDirectory; 1.97 + rv = file->IsDirectory(&isDirectory); 1.98 + NS_ENSURE_SUCCESS(rv, rv); 1.99 + 1.100 + if (isDirectory) { 1.101 + if (!validSubdirs.GetEntry(leafName)) { 1.102 + subdirsToProcess.AppendElement(leafName); 1.103 + } 1.104 + continue; 1.105 + } 1.106 + 1.107 + nsString dbBaseFilename; 1.108 + if (!GetDatabaseBaseFilename(leafName, dbBaseFilename)) { 1.109 + unknownFiles.AppendElement(file); 1.110 + continue; 1.111 + } 1.112 + 1.113 + nsCOMPtr<nsIFile> fmDirectory; 1.114 + rv = directory->Clone(getter_AddRefs(fmDirectory)); 1.115 + NS_ENSURE_SUCCESS(rv, rv); 1.116 + 1.117 + rv = fmDirectory->Append(dbBaseFilename); 1.118 + NS_ENSURE_SUCCESS(rv, rv); 1.119 + 1.120 + rv = FileManager::InitDirectory(fmDirectory, file, aPersistenceType, aGroup, 1.121 + aOrigin); 1.122 + NS_ENSURE_SUCCESS(rv, rv); 1.123 + 1.124 + if (aUsageInfo) { 1.125 + int64_t fileSize; 1.126 + rv = file->GetFileSize(&fileSize); 1.127 + NS_ENSURE_SUCCESS(rv, rv); 1.128 + 1.129 + NS_ASSERTION(fileSize >= 0, "Negative size?!"); 1.130 + 1.131 + aUsageInfo->AppendToDatabaseUsage(uint64_t(fileSize)); 1.132 + 1.133 + uint64_t usage; 1.134 + rv = FileManager::GetUsage(fmDirectory, &usage); 1.135 + NS_ENSURE_SUCCESS(rv, rv); 1.136 + 1.137 + aUsageInfo->AppendToFileUsage(usage); 1.138 + } 1.139 + 1.140 + validSubdirs.PutEntry(dbBaseFilename); 1.141 + } 1.142 + NS_ENSURE_SUCCESS(rv, rv); 1.143 + 1.144 + for (uint32_t i = 0; i < subdirsToProcess.Length(); i++) { 1.145 + const nsString& subdir = subdirsToProcess[i]; 1.146 + if (!validSubdirs.GetEntry(subdir)) { 1.147 + NS_WARNING("Unknown subdirectory found!"); 1.148 + return NS_ERROR_UNEXPECTED; 1.149 + } 1.150 + } 1.151 + 1.152 + for (uint32_t i = 0; i < unknownFiles.Length(); i++) { 1.153 + nsCOMPtr<nsIFile>& unknownFile = unknownFiles[i]; 1.154 + 1.155 + // Some temporary SQLite files could disappear, so we have to check if the 1.156 + // unknown file still exists. 1.157 + bool exists; 1.158 + rv = unknownFile->Exists(&exists); 1.159 + NS_ENSURE_SUCCESS(rv, rv); 1.160 + 1.161 + if (exists) { 1.162 + nsString leafName; 1.163 + unknownFile->GetLeafName(leafName); 1.164 + 1.165 + // The journal file may exists even after db has been correctly opened. 1.166 + if (!StringEndsWith(leafName, NS_LITERAL_STRING(".sqlite-journal"))) { 1.167 + NS_WARNING("Unknown file found!"); 1.168 + return NS_ERROR_UNEXPECTED; 1.169 + } 1.170 + } 1.171 + } 1.172 + 1.173 + return NS_OK; 1.174 +} 1.175 + 1.176 +nsresult 1.177 +Client::GetUsageForOrigin(PersistenceType aPersistenceType, 1.178 + const nsACString& aGroup, const nsACString& aOrigin, 1.179 + UsageInfo* aUsageInfo) 1.180 +{ 1.181 + AssertIsOnIOThread(); 1.182 + NS_ASSERTION(aUsageInfo, "Null pointer!"); 1.183 + 1.184 + nsCOMPtr<nsIFile> directory; 1.185 + nsresult rv = 1.186 + GetDirectory(aPersistenceType, aOrigin, getter_AddRefs(directory)); 1.187 + NS_ENSURE_SUCCESS(rv, rv); 1.188 + 1.189 + rv = GetUsageForDirectoryInternal(directory, aUsageInfo, true); 1.190 + NS_ENSURE_SUCCESS(rv, rv); 1.191 + 1.192 + return NS_OK; 1.193 +} 1.194 + 1.195 +void 1.196 +Client::OnOriginClearCompleted(PersistenceType aPersistenceType, 1.197 + const OriginOrPatternString& aOriginOrPattern) 1.198 +{ 1.199 + AssertIsOnIOThread(); 1.200 + 1.201 + IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get(); 1.202 + if (mgr) { 1.203 + mgr->InvalidateFileManagers(aPersistenceType, aOriginOrPattern); 1.204 + } 1.205 +} 1.206 + 1.207 +void 1.208 +Client::ReleaseIOThreadObjects() 1.209 +{ 1.210 + AssertIsOnIOThread(); 1.211 + 1.212 + IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get(); 1.213 + if (mgr) { 1.214 + mgr->InvalidateAllFileManagers(); 1.215 + } 1.216 +} 1.217 + 1.218 +bool 1.219 +Client::IsTransactionServiceActivated() 1.220 +{ 1.221 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.222 + 1.223 + return !!TransactionThreadPool::Get(); 1.224 +} 1.225 + 1.226 +void 1.227 +Client::WaitForStoragesToComplete(nsTArray<nsIOfflineStorage*>& aStorages, 1.228 + nsIRunnable* aCallback) 1.229 +{ 1.230 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.231 + NS_ASSERTION(!aStorages.IsEmpty(), "No storages to wait on!"); 1.232 + NS_ASSERTION(aCallback, "Passed null callback!"); 1.233 + 1.234 + TransactionThreadPool* pool = TransactionThreadPool::Get(); 1.235 + NS_ASSERTION(pool, "Should have checked if transaction service is active!"); 1.236 + 1.237 + nsTArray<nsRefPtr<IDBDatabase> > databases(aStorages.Length()); 1.238 + for (uint32_t index = 0; index < aStorages.Length(); index++) { 1.239 + IDBDatabase* database = IDBDatabase::FromStorage(aStorages[index]); 1.240 + if (!database) { 1.241 + MOZ_CRASH(); 1.242 + } 1.243 + 1.244 + databases.AppendElement(database); 1.245 + } 1.246 + 1.247 + pool->WaitForDatabasesToComplete(databases, aCallback); 1.248 +} 1.249 + 1.250 +void 1.251 +Client::AbortTransactionsForStorage(nsIOfflineStorage* aStorage) 1.252 +{ 1.253 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.254 + NS_ASSERTION(aStorage, "Passed null storage!"); 1.255 + 1.256 + TransactionThreadPool* pool = TransactionThreadPool::Get(); 1.257 + NS_ASSERTION(pool, "Should have checked if transaction service is active!"); 1.258 + 1.259 + IDBDatabase* database = IDBDatabase::FromStorage(aStorage); 1.260 + NS_ASSERTION(database, "This shouldn't be null!"); 1.261 + 1.262 + pool->AbortTransactionsForDatabase(database); 1.263 +} 1.264 + 1.265 +bool 1.266 +Client::HasTransactionsForStorage(nsIOfflineStorage* aStorage) 1.267 +{ 1.268 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.269 + 1.270 + TransactionThreadPool* pool = TransactionThreadPool::Get(); 1.271 + NS_ASSERTION(pool, "Should have checked if transaction service is active!"); 1.272 + 1.273 + IDBDatabase* database = IDBDatabase::FromStorage(aStorage); 1.274 + NS_ASSERTION(database, "This shouldn't be null!"); 1.275 + 1.276 + return pool->HasTransactionsForDatabase(database); 1.277 +} 1.278 + 1.279 +void 1.280 +Client::ShutdownTransactionService() 1.281 +{ 1.282 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.283 + 1.284 + TransactionThreadPool::Shutdown(); 1.285 +} 1.286 + 1.287 +nsresult 1.288 +Client::GetDirectory(PersistenceType aPersistenceType, 1.289 + const nsACString& aOrigin, nsIFile** aDirectory) 1.290 +{ 1.291 + QuotaManager* quotaManager = QuotaManager::Get(); 1.292 + NS_ASSERTION(quotaManager, "This should never fail!"); 1.293 + 1.294 + nsCOMPtr<nsIFile> directory; 1.295 + nsresult rv = quotaManager->GetDirectoryForOrigin(aPersistenceType, aOrigin, 1.296 + getter_AddRefs(directory)); 1.297 + NS_ENSURE_SUCCESS(rv, rv); 1.298 + 1.299 + NS_ASSERTION(directory, "What?"); 1.300 + 1.301 + rv = directory->Append(NS_LITERAL_STRING(IDB_DIRECTORY_NAME)); 1.302 + NS_ENSURE_SUCCESS(rv, rv); 1.303 + 1.304 + directory.forget(aDirectory); 1.305 + return NS_OK; 1.306 +} 1.307 + 1.308 +nsresult 1.309 +Client::GetUsageForDirectoryInternal(nsIFile* aDirectory, 1.310 + UsageInfo* aUsageInfo, 1.311 + bool aDatabaseFiles) 1.312 +{ 1.313 + NS_ASSERTION(aDirectory, "Null pointer!"); 1.314 + NS_ASSERTION(aUsageInfo, "Null pointer!"); 1.315 + 1.316 + nsCOMPtr<nsISimpleEnumerator> entries; 1.317 + nsresult rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries)); 1.318 + NS_ENSURE_SUCCESS(rv, rv); 1.319 + 1.320 + if (!entries) { 1.321 + return NS_OK; 1.322 + } 1.323 + 1.324 + bool hasMore; 1.325 + while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && 1.326 + hasMore && !aUsageInfo->Canceled()) { 1.327 + nsCOMPtr<nsISupports> entry; 1.328 + rv = entries->GetNext(getter_AddRefs(entry)); 1.329 + NS_ENSURE_SUCCESS(rv, rv); 1.330 + 1.331 + nsCOMPtr<nsIFile> file(do_QueryInterface(entry)); 1.332 + NS_ASSERTION(file, "Don't know what this is!"); 1.333 + 1.334 + bool isDirectory; 1.335 + rv = file->IsDirectory(&isDirectory); 1.336 + NS_ENSURE_SUCCESS(rv, rv); 1.337 + 1.338 + if (isDirectory) { 1.339 + if (aDatabaseFiles) { 1.340 + rv = GetUsageForDirectoryInternal(file, aUsageInfo, false); 1.341 + NS_ENSURE_SUCCESS(rv, rv); 1.342 + } 1.343 + else { 1.344 + nsString leafName; 1.345 + rv = file->GetLeafName(leafName); 1.346 + NS_ENSURE_SUCCESS(rv, rv); 1.347 + 1.348 + if (!leafName.EqualsLiteral(JOURNAL_DIRECTORY_NAME)) { 1.349 + NS_WARNING("Unknown directory found!"); 1.350 + } 1.351 + } 1.352 + 1.353 + continue; 1.354 + } 1.355 + 1.356 + int64_t fileSize; 1.357 + rv = file->GetFileSize(&fileSize); 1.358 + NS_ENSURE_SUCCESS(rv, rv); 1.359 + 1.360 + NS_ASSERTION(fileSize >= 0, "Negative size?!"); 1.361 + 1.362 + if (aDatabaseFiles) { 1.363 + aUsageInfo->AppendToDatabaseUsage(uint64_t(fileSize)); 1.364 + } 1.365 + else { 1.366 + aUsageInfo->AppendToFileUsage(uint64_t(fileSize)); 1.367 + } 1.368 + } 1.369 + NS_ENSURE_SUCCESS(rv, rv); 1.370 + 1.371 + return NS_OK; 1.372 +}