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

mercurial