dom/indexedDB/Client.cpp

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set ts=2 et sw=2 tw=80: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "Client.h"
michael@0 8
michael@0 9 #include "mozilla/dom/quota/QuotaManager.h"
michael@0 10 #include "mozilla/dom/quota/UsageInfo.h"
michael@0 11 #include "mozilla/dom/quota/Utilities.h"
michael@0 12
michael@0 13 #include "IDBDatabase.h"
michael@0 14 #include "IndexedDatabaseManager.h"
michael@0 15 #include "TransactionThreadPool.h"
michael@0 16 #include "nsISimpleEnumerator.h"
michael@0 17
michael@0 18 USING_INDEXEDDB_NAMESPACE
michael@0 19 using mozilla::dom::quota::AssertIsOnIOThread;
michael@0 20 using mozilla::dom::quota::QuotaManager;
michael@0 21
michael@0 22 namespace {
michael@0 23
michael@0 24 bool
michael@0 25 GetDatabaseBaseFilename(const nsAString& aFilename,
michael@0 26 nsAString& aDatabaseBaseFilename)
michael@0 27 {
michael@0 28 NS_ASSERTION(!aFilename.IsEmpty(), "Bad argument!");
michael@0 29
michael@0 30 NS_NAMED_LITERAL_STRING(sqlite, ".sqlite");
michael@0 31
michael@0 32 if (!StringEndsWith(aFilename, sqlite)) {
michael@0 33 return false;
michael@0 34 }
michael@0 35
michael@0 36 aDatabaseBaseFilename =
michael@0 37 Substring(aFilename, 0, aFilename.Length() - sqlite.Length());
michael@0 38
michael@0 39 return true;
michael@0 40 }
michael@0 41
michael@0 42 } // anonymous namespace
michael@0 43
michael@0 44 // This needs to be fully qualified to not confuse trace refcnt assertions.
michael@0 45 NS_IMPL_ADDREF(mozilla::dom::indexedDB::Client)
michael@0 46 NS_IMPL_RELEASE(mozilla::dom::indexedDB::Client)
michael@0 47
michael@0 48 nsresult
michael@0 49 Client::InitOrigin(PersistenceType aPersistenceType, const nsACString& aGroup,
michael@0 50 const nsACString& aOrigin, UsageInfo* aUsageInfo)
michael@0 51 {
michael@0 52 AssertIsOnIOThread();
michael@0 53
michael@0 54 nsCOMPtr<nsIFile> directory;
michael@0 55 nsresult rv =
michael@0 56 GetDirectory(aPersistenceType, aOrigin, getter_AddRefs(directory));
michael@0 57 NS_ENSURE_SUCCESS(rv, rv);
michael@0 58
michael@0 59 // We need to see if there are any files in the directory already. If they
michael@0 60 // are database files then we need to cleanup stored files (if it's needed)
michael@0 61 // and also get the usage.
michael@0 62
michael@0 63 nsAutoTArray<nsString, 20> subdirsToProcess;
michael@0 64 nsAutoTArray<nsCOMPtr<nsIFile> , 20> unknownFiles;
michael@0 65 nsTHashtable<nsStringHashKey> validSubdirs(20);
michael@0 66
michael@0 67 nsCOMPtr<nsISimpleEnumerator> entries;
michael@0 68 rv = directory->GetDirectoryEntries(getter_AddRefs(entries));
michael@0 69 NS_ENSURE_SUCCESS(rv, rv);
michael@0 70
michael@0 71 bool hasMore;
michael@0 72 while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) &&
michael@0 73 hasMore && (!aUsageInfo || !aUsageInfo->Canceled())) {
michael@0 74 nsCOMPtr<nsISupports> entry;
michael@0 75 rv = entries->GetNext(getter_AddRefs(entry));
michael@0 76 NS_ENSURE_SUCCESS(rv, rv);
michael@0 77
michael@0 78 nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
michael@0 79 NS_ENSURE_TRUE(file, NS_NOINTERFACE);
michael@0 80
michael@0 81 nsString leafName;
michael@0 82 rv = file->GetLeafName(leafName);
michael@0 83 NS_ENSURE_SUCCESS(rv, rv);
michael@0 84
michael@0 85 if (StringEndsWith(leafName, NS_LITERAL_STRING(".sqlite-journal"))) {
michael@0 86 continue;
michael@0 87 }
michael@0 88
michael@0 89 if (leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
michael@0 90 continue;
michael@0 91 }
michael@0 92
michael@0 93 bool isDirectory;
michael@0 94 rv = file->IsDirectory(&isDirectory);
michael@0 95 NS_ENSURE_SUCCESS(rv, rv);
michael@0 96
michael@0 97 if (isDirectory) {
michael@0 98 if (!validSubdirs.GetEntry(leafName)) {
michael@0 99 subdirsToProcess.AppendElement(leafName);
michael@0 100 }
michael@0 101 continue;
michael@0 102 }
michael@0 103
michael@0 104 nsString dbBaseFilename;
michael@0 105 if (!GetDatabaseBaseFilename(leafName, dbBaseFilename)) {
michael@0 106 unknownFiles.AppendElement(file);
michael@0 107 continue;
michael@0 108 }
michael@0 109
michael@0 110 nsCOMPtr<nsIFile> fmDirectory;
michael@0 111 rv = directory->Clone(getter_AddRefs(fmDirectory));
michael@0 112 NS_ENSURE_SUCCESS(rv, rv);
michael@0 113
michael@0 114 rv = fmDirectory->Append(dbBaseFilename);
michael@0 115 NS_ENSURE_SUCCESS(rv, rv);
michael@0 116
michael@0 117 rv = FileManager::InitDirectory(fmDirectory, file, aPersistenceType, aGroup,
michael@0 118 aOrigin);
michael@0 119 NS_ENSURE_SUCCESS(rv, rv);
michael@0 120
michael@0 121 if (aUsageInfo) {
michael@0 122 int64_t fileSize;
michael@0 123 rv = file->GetFileSize(&fileSize);
michael@0 124 NS_ENSURE_SUCCESS(rv, rv);
michael@0 125
michael@0 126 NS_ASSERTION(fileSize >= 0, "Negative size?!");
michael@0 127
michael@0 128 aUsageInfo->AppendToDatabaseUsage(uint64_t(fileSize));
michael@0 129
michael@0 130 uint64_t usage;
michael@0 131 rv = FileManager::GetUsage(fmDirectory, &usage);
michael@0 132 NS_ENSURE_SUCCESS(rv, rv);
michael@0 133
michael@0 134 aUsageInfo->AppendToFileUsage(usage);
michael@0 135 }
michael@0 136
michael@0 137 validSubdirs.PutEntry(dbBaseFilename);
michael@0 138 }
michael@0 139 NS_ENSURE_SUCCESS(rv, rv);
michael@0 140
michael@0 141 for (uint32_t i = 0; i < subdirsToProcess.Length(); i++) {
michael@0 142 const nsString& subdir = subdirsToProcess[i];
michael@0 143 if (!validSubdirs.GetEntry(subdir)) {
michael@0 144 NS_WARNING("Unknown subdirectory found!");
michael@0 145 return NS_ERROR_UNEXPECTED;
michael@0 146 }
michael@0 147 }
michael@0 148
michael@0 149 for (uint32_t i = 0; i < unknownFiles.Length(); i++) {
michael@0 150 nsCOMPtr<nsIFile>& unknownFile = unknownFiles[i];
michael@0 151
michael@0 152 // Some temporary SQLite files could disappear, so we have to check if the
michael@0 153 // unknown file still exists.
michael@0 154 bool exists;
michael@0 155 rv = unknownFile->Exists(&exists);
michael@0 156 NS_ENSURE_SUCCESS(rv, rv);
michael@0 157
michael@0 158 if (exists) {
michael@0 159 nsString leafName;
michael@0 160 unknownFile->GetLeafName(leafName);
michael@0 161
michael@0 162 // The journal file may exists even after db has been correctly opened.
michael@0 163 if (!StringEndsWith(leafName, NS_LITERAL_STRING(".sqlite-journal"))) {
michael@0 164 NS_WARNING("Unknown file found!");
michael@0 165 return NS_ERROR_UNEXPECTED;
michael@0 166 }
michael@0 167 }
michael@0 168 }
michael@0 169
michael@0 170 return NS_OK;
michael@0 171 }
michael@0 172
michael@0 173 nsresult
michael@0 174 Client::GetUsageForOrigin(PersistenceType aPersistenceType,
michael@0 175 const nsACString& aGroup, const nsACString& aOrigin,
michael@0 176 UsageInfo* aUsageInfo)
michael@0 177 {
michael@0 178 AssertIsOnIOThread();
michael@0 179 NS_ASSERTION(aUsageInfo, "Null pointer!");
michael@0 180
michael@0 181 nsCOMPtr<nsIFile> directory;
michael@0 182 nsresult rv =
michael@0 183 GetDirectory(aPersistenceType, aOrigin, getter_AddRefs(directory));
michael@0 184 NS_ENSURE_SUCCESS(rv, rv);
michael@0 185
michael@0 186 rv = GetUsageForDirectoryInternal(directory, aUsageInfo, true);
michael@0 187 NS_ENSURE_SUCCESS(rv, rv);
michael@0 188
michael@0 189 return NS_OK;
michael@0 190 }
michael@0 191
michael@0 192 void
michael@0 193 Client::OnOriginClearCompleted(PersistenceType aPersistenceType,
michael@0 194 const OriginOrPatternString& aOriginOrPattern)
michael@0 195 {
michael@0 196 AssertIsOnIOThread();
michael@0 197
michael@0 198 IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
michael@0 199 if (mgr) {
michael@0 200 mgr->InvalidateFileManagers(aPersistenceType, aOriginOrPattern);
michael@0 201 }
michael@0 202 }
michael@0 203
michael@0 204 void
michael@0 205 Client::ReleaseIOThreadObjects()
michael@0 206 {
michael@0 207 AssertIsOnIOThread();
michael@0 208
michael@0 209 IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
michael@0 210 if (mgr) {
michael@0 211 mgr->InvalidateAllFileManagers();
michael@0 212 }
michael@0 213 }
michael@0 214
michael@0 215 bool
michael@0 216 Client::IsTransactionServiceActivated()
michael@0 217 {
michael@0 218 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 219
michael@0 220 return !!TransactionThreadPool::Get();
michael@0 221 }
michael@0 222
michael@0 223 void
michael@0 224 Client::WaitForStoragesToComplete(nsTArray<nsIOfflineStorage*>& aStorages,
michael@0 225 nsIRunnable* aCallback)
michael@0 226 {
michael@0 227 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 228 NS_ASSERTION(!aStorages.IsEmpty(), "No storages to wait on!");
michael@0 229 NS_ASSERTION(aCallback, "Passed null callback!");
michael@0 230
michael@0 231 TransactionThreadPool* pool = TransactionThreadPool::Get();
michael@0 232 NS_ASSERTION(pool, "Should have checked if transaction service is active!");
michael@0 233
michael@0 234 nsTArray<nsRefPtr<IDBDatabase> > databases(aStorages.Length());
michael@0 235 for (uint32_t index = 0; index < aStorages.Length(); index++) {
michael@0 236 IDBDatabase* database = IDBDatabase::FromStorage(aStorages[index]);
michael@0 237 if (!database) {
michael@0 238 MOZ_CRASH();
michael@0 239 }
michael@0 240
michael@0 241 databases.AppendElement(database);
michael@0 242 }
michael@0 243
michael@0 244 pool->WaitForDatabasesToComplete(databases, aCallback);
michael@0 245 }
michael@0 246
michael@0 247 void
michael@0 248 Client::AbortTransactionsForStorage(nsIOfflineStorage* aStorage)
michael@0 249 {
michael@0 250 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 251 NS_ASSERTION(aStorage, "Passed null storage!");
michael@0 252
michael@0 253 TransactionThreadPool* pool = TransactionThreadPool::Get();
michael@0 254 NS_ASSERTION(pool, "Should have checked if transaction service is active!");
michael@0 255
michael@0 256 IDBDatabase* database = IDBDatabase::FromStorage(aStorage);
michael@0 257 NS_ASSERTION(database, "This shouldn't be null!");
michael@0 258
michael@0 259 pool->AbortTransactionsForDatabase(database);
michael@0 260 }
michael@0 261
michael@0 262 bool
michael@0 263 Client::HasTransactionsForStorage(nsIOfflineStorage* aStorage)
michael@0 264 {
michael@0 265 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 266
michael@0 267 TransactionThreadPool* pool = TransactionThreadPool::Get();
michael@0 268 NS_ASSERTION(pool, "Should have checked if transaction service is active!");
michael@0 269
michael@0 270 IDBDatabase* database = IDBDatabase::FromStorage(aStorage);
michael@0 271 NS_ASSERTION(database, "This shouldn't be null!");
michael@0 272
michael@0 273 return pool->HasTransactionsForDatabase(database);
michael@0 274 }
michael@0 275
michael@0 276 void
michael@0 277 Client::ShutdownTransactionService()
michael@0 278 {
michael@0 279 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 280
michael@0 281 TransactionThreadPool::Shutdown();
michael@0 282 }
michael@0 283
michael@0 284 nsresult
michael@0 285 Client::GetDirectory(PersistenceType aPersistenceType,
michael@0 286 const nsACString& aOrigin, nsIFile** aDirectory)
michael@0 287 {
michael@0 288 QuotaManager* quotaManager = QuotaManager::Get();
michael@0 289 NS_ASSERTION(quotaManager, "This should never fail!");
michael@0 290
michael@0 291 nsCOMPtr<nsIFile> directory;
michael@0 292 nsresult rv = quotaManager->GetDirectoryForOrigin(aPersistenceType, aOrigin,
michael@0 293 getter_AddRefs(directory));
michael@0 294 NS_ENSURE_SUCCESS(rv, rv);
michael@0 295
michael@0 296 NS_ASSERTION(directory, "What?");
michael@0 297
michael@0 298 rv = directory->Append(NS_LITERAL_STRING(IDB_DIRECTORY_NAME));
michael@0 299 NS_ENSURE_SUCCESS(rv, rv);
michael@0 300
michael@0 301 directory.forget(aDirectory);
michael@0 302 return NS_OK;
michael@0 303 }
michael@0 304
michael@0 305 nsresult
michael@0 306 Client::GetUsageForDirectoryInternal(nsIFile* aDirectory,
michael@0 307 UsageInfo* aUsageInfo,
michael@0 308 bool aDatabaseFiles)
michael@0 309 {
michael@0 310 NS_ASSERTION(aDirectory, "Null pointer!");
michael@0 311 NS_ASSERTION(aUsageInfo, "Null pointer!");
michael@0 312
michael@0 313 nsCOMPtr<nsISimpleEnumerator> entries;
michael@0 314 nsresult rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
michael@0 315 NS_ENSURE_SUCCESS(rv, rv);
michael@0 316
michael@0 317 if (!entries) {
michael@0 318 return NS_OK;
michael@0 319 }
michael@0 320
michael@0 321 bool hasMore;
michael@0 322 while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) &&
michael@0 323 hasMore && !aUsageInfo->Canceled()) {
michael@0 324 nsCOMPtr<nsISupports> entry;
michael@0 325 rv = entries->GetNext(getter_AddRefs(entry));
michael@0 326 NS_ENSURE_SUCCESS(rv, rv);
michael@0 327
michael@0 328 nsCOMPtr<nsIFile> file(do_QueryInterface(entry));
michael@0 329 NS_ASSERTION(file, "Don't know what this is!");
michael@0 330
michael@0 331 bool isDirectory;
michael@0 332 rv = file->IsDirectory(&isDirectory);
michael@0 333 NS_ENSURE_SUCCESS(rv, rv);
michael@0 334
michael@0 335 if (isDirectory) {
michael@0 336 if (aDatabaseFiles) {
michael@0 337 rv = GetUsageForDirectoryInternal(file, aUsageInfo, false);
michael@0 338 NS_ENSURE_SUCCESS(rv, rv);
michael@0 339 }
michael@0 340 else {
michael@0 341 nsString leafName;
michael@0 342 rv = file->GetLeafName(leafName);
michael@0 343 NS_ENSURE_SUCCESS(rv, rv);
michael@0 344
michael@0 345 if (!leafName.EqualsLiteral(JOURNAL_DIRECTORY_NAME)) {
michael@0 346 NS_WARNING("Unknown directory found!");
michael@0 347 }
michael@0 348 }
michael@0 349
michael@0 350 continue;
michael@0 351 }
michael@0 352
michael@0 353 int64_t fileSize;
michael@0 354 rv = file->GetFileSize(&fileSize);
michael@0 355 NS_ENSURE_SUCCESS(rv, rv);
michael@0 356
michael@0 357 NS_ASSERTION(fileSize >= 0, "Negative size?!");
michael@0 358
michael@0 359 if (aDatabaseFiles) {
michael@0 360 aUsageInfo->AppendToDatabaseUsage(uint64_t(fileSize));
michael@0 361 }
michael@0 362 else {
michael@0 363 aUsageInfo->AppendToFileUsage(uint64_t(fileSize));
michael@0 364 }
michael@0 365 }
michael@0 366 NS_ENSURE_SUCCESS(rv, rv);
michael@0 367
michael@0 368 return NS_OK;
michael@0 369 }

mercurial