dom/quota/QuotaManager.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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 "QuotaManager.h"
michael@0 8
michael@0 9 #include "mozIApplicationClearPrivateDataParams.h"
michael@0 10 #include "nsIBinaryInputStream.h"
michael@0 11 #include "nsIBinaryOutputStream.h"
michael@0 12 #include "nsIFile.h"
michael@0 13 #include "nsIObserverService.h"
michael@0 14 #include "nsIOfflineStorage.h"
michael@0 15 #include "nsIPrincipal.h"
michael@0 16 #include "nsIQuotaRequest.h"
michael@0 17 #include "nsIRunnable.h"
michael@0 18 #include "nsISimpleEnumerator.h"
michael@0 19 #include "nsIScriptObjectPrincipal.h"
michael@0 20 #include "nsIScriptSecurityManager.h"
michael@0 21 #include "nsITimer.h"
michael@0 22 #include "nsIURI.h"
michael@0 23 #include "nsIUsageCallback.h"
michael@0 24
michael@0 25 #include <algorithm>
michael@0 26 #include "GeckoProfiler.h"
michael@0 27 #include "mozilla/Atomics.h"
michael@0 28 #include "mozilla/CondVar.h"
michael@0 29 #include "mozilla/dom/asmjscache/AsmJSCache.h"
michael@0 30 #include "mozilla/dom/file/FileService.h"
michael@0 31 #include "mozilla/dom/indexedDB/Client.h"
michael@0 32 #include "mozilla/Mutex.h"
michael@0 33 #include "mozilla/LazyIdleThread.h"
michael@0 34 #include "mozilla/Preferences.h"
michael@0 35 #include "mozilla/Services.h"
michael@0 36 #include "nsAppDirectoryServiceDefs.h"
michael@0 37 #include "nsComponentManagerUtils.h"
michael@0 38 #include "nsContentUtils.h"
michael@0 39 #include "nsCRTGlue.h"
michael@0 40 #include "nsDirectoryServiceUtils.h"
michael@0 41 #include "nsNetUtil.h"
michael@0 42 #include "nsScriptSecurityManager.h"
michael@0 43 #include "nsThreadUtils.h"
michael@0 44 #include "nsXULAppAPI.h"
michael@0 45 #include "xpcpublic.h"
michael@0 46
michael@0 47 #include "AcquireListener.h"
michael@0 48 #include "CheckQuotaHelper.h"
michael@0 49 #include "OriginCollection.h"
michael@0 50 #include "OriginOrPatternString.h"
michael@0 51 #include "QuotaObject.h"
michael@0 52 #include "StorageMatcher.h"
michael@0 53 #include "UsageInfo.h"
michael@0 54 #include "Utilities.h"
michael@0 55
michael@0 56 // The amount of time, in milliseconds, that our IO thread will stay alive
michael@0 57 // after the last event it processes.
michael@0 58 #define DEFAULT_THREAD_TIMEOUT_MS 30000
michael@0 59
michael@0 60 // The amount of time, in milliseconds, that we will wait for active storage
michael@0 61 // transactions on shutdown before aborting them.
michael@0 62 #define DEFAULT_SHUTDOWN_TIMER_MS 30000
michael@0 63
michael@0 64 // Preference that users can set to override DEFAULT_QUOTA_MB
michael@0 65 #define PREF_STORAGE_QUOTA "dom.indexedDB.warningQuota"
michael@0 66
michael@0 67 // Preference that users can set to override temporary storage smart limit
michael@0 68 // calculation.
michael@0 69 #define PREF_FIXED_LIMIT "dom.quotaManager.temporaryStorage.fixedLimit"
michael@0 70 #define PREF_CHUNK_SIZE "dom.quotaManager.temporaryStorage.chunkSize"
michael@0 71
michael@0 72 // Preference that is used to enable testing features
michael@0 73 #define PREF_TESTING_FEATURES "dom.quotaManager.testing"
michael@0 74
michael@0 75 // profile-before-change, when we need to shut down quota manager
michael@0 76 #define PROFILE_BEFORE_CHANGE_OBSERVER_ID "profile-before-change"
michael@0 77
michael@0 78 // The name of the file that we use to load/save the last access time of an
michael@0 79 // origin.
michael@0 80 #define METADATA_FILE_NAME ".metadata"
michael@0 81
michael@0 82 #define PERMISSION_DEFAUT_PERSISTENT_STORAGE "default-persistent-storage"
michael@0 83
michael@0 84 #define KB * 1024ULL
michael@0 85 #define MB * 1024ULL KB
michael@0 86 #define GB * 1024ULL MB
michael@0 87
michael@0 88 USING_QUOTA_NAMESPACE
michael@0 89 using namespace mozilla::dom;
michael@0 90 using mozilla::dom::file::FileService;
michael@0 91
michael@0 92 static_assert(
michael@0 93 static_cast<uint32_t>(StorageType::Persistent) ==
michael@0 94 static_cast<uint32_t>(PERSISTENCE_TYPE_PERSISTENT),
michael@0 95 "Enum values should match.");
michael@0 96
michael@0 97 static_assert(
michael@0 98 static_cast<uint32_t>(StorageType::Temporary) ==
michael@0 99 static_cast<uint32_t>(PERSISTENCE_TYPE_TEMPORARY),
michael@0 100 "Enum values should match.");
michael@0 101
michael@0 102 BEGIN_QUOTA_NAMESPACE
michael@0 103
michael@0 104 // A struct that contains the information corresponding to a pending or
michael@0 105 // running operation that requires synchronization (e.g. opening a db,
michael@0 106 // clearing dbs for an origin, etc).
michael@0 107 struct SynchronizedOp
michael@0 108 {
michael@0 109 SynchronizedOp(const OriginOrPatternString& aOriginOrPattern,
michael@0 110 Nullable<PersistenceType> aPersistenceType,
michael@0 111 const nsACString& aId);
michael@0 112
michael@0 113 ~SynchronizedOp();
michael@0 114
michael@0 115 // Test whether this SynchronizedOp needs to wait for the given op.
michael@0 116 bool
michael@0 117 MustWaitFor(const SynchronizedOp& aOp);
michael@0 118
michael@0 119 void
michael@0 120 DelayRunnable(nsIRunnable* aRunnable);
michael@0 121
michael@0 122 void
michael@0 123 DispatchDelayedRunnables();
michael@0 124
michael@0 125 const OriginOrPatternString mOriginOrPattern;
michael@0 126 Nullable<PersistenceType> mPersistenceType;
michael@0 127 nsCString mId;
michael@0 128 nsRefPtr<AcquireListener> mListener;
michael@0 129 nsTArray<nsCOMPtr<nsIRunnable> > mDelayedRunnables;
michael@0 130 ArrayCluster<nsIOfflineStorage*> mStorages;
michael@0 131 };
michael@0 132
michael@0 133 class CollectOriginsHelper MOZ_FINAL : public nsRunnable
michael@0 134 {
michael@0 135 public:
michael@0 136 CollectOriginsHelper(mozilla::Mutex& aMutex, uint64_t aMinSizeToBeFreed);
michael@0 137
michael@0 138 NS_IMETHOD
michael@0 139 Run();
michael@0 140
michael@0 141 // Blocks the current thread until origins are collected on the main thread.
michael@0 142 // The returned value contains an aggregate size of those origins.
michael@0 143 int64_t
michael@0 144 BlockAndReturnOriginsForEviction(nsTArray<OriginInfo*>& aOriginInfos);
michael@0 145
michael@0 146 private:
michael@0 147 ~CollectOriginsHelper()
michael@0 148 { }
michael@0 149
michael@0 150 uint64_t mMinSizeToBeFreed;
michael@0 151
michael@0 152 mozilla::Mutex& mMutex;
michael@0 153 mozilla::CondVar mCondVar;
michael@0 154
michael@0 155 // The members below are protected by mMutex.
michael@0 156 nsTArray<OriginInfo*> mOriginInfos;
michael@0 157 uint64_t mSizeToBeFreed;
michael@0 158 bool mWaiting;
michael@0 159 };
michael@0 160
michael@0 161 // Responsible for clearing the storage files for a particular origin on the
michael@0 162 // IO thread. Created when nsIQuotaManager::ClearStoragesForURI is called.
michael@0 163 // Runs three times, first on the main thread, next on the IO thread, and then
michael@0 164 // finally again on the main thread. While on the IO thread the runnable will
michael@0 165 // actually remove the origin's storage files and the directory that contains
michael@0 166 // them before dispatching itself back to the main thread. When back on the main
michael@0 167 // thread the runnable will notify the QuotaManager that the job has been
michael@0 168 // completed.
michael@0 169 class OriginClearRunnable MOZ_FINAL : public nsRunnable,
michael@0 170 public AcquireListener
michael@0 171 {
michael@0 172 enum CallbackState {
michael@0 173 // Not yet run.
michael@0 174 Pending = 0,
michael@0 175
michael@0 176 // Running on the main thread in the callback for OpenAllowed.
michael@0 177 OpenAllowed,
michael@0 178
michael@0 179 // Running on the IO thread.
michael@0 180 IO,
michael@0 181
michael@0 182 // Running on the main thread after all work is done.
michael@0 183 Complete
michael@0 184 };
michael@0 185
michael@0 186 public:
michael@0 187 NS_DECL_ISUPPORTS_INHERITED
michael@0 188
michael@0 189 OriginClearRunnable(const OriginOrPatternString& aOriginOrPattern,
michael@0 190 Nullable<PersistenceType> aPersistenceType)
michael@0 191 : mOriginOrPattern(aOriginOrPattern),
michael@0 192 mPersistenceType(aPersistenceType),
michael@0 193 mCallbackState(Pending)
michael@0 194 { }
michael@0 195
michael@0 196 NS_IMETHOD
michael@0 197 Run();
michael@0 198
michael@0 199 // AcquireListener override
michael@0 200 virtual nsresult
michael@0 201 OnExclusiveAccessAcquired() MOZ_OVERRIDE;
michael@0 202
michael@0 203 void
michael@0 204 AdvanceState()
michael@0 205 {
michael@0 206 switch (mCallbackState) {
michael@0 207 case Pending:
michael@0 208 mCallbackState = OpenAllowed;
michael@0 209 return;
michael@0 210 case OpenAllowed:
michael@0 211 mCallbackState = IO;
michael@0 212 return;
michael@0 213 case IO:
michael@0 214 mCallbackState = Complete;
michael@0 215 return;
michael@0 216 default:
michael@0 217 NS_NOTREACHED("Can't advance past Complete!");
michael@0 218 }
michael@0 219 }
michael@0 220
michael@0 221 static void
michael@0 222 InvalidateOpenedStorages(nsTArray<nsCOMPtr<nsIOfflineStorage> >& aStorages,
michael@0 223 void* aClosure);
michael@0 224
michael@0 225 void
michael@0 226 DeleteFiles(QuotaManager* aQuotaManager,
michael@0 227 PersistenceType aPersistenceType);
michael@0 228
michael@0 229 private:
michael@0 230 OriginOrPatternString mOriginOrPattern;
michael@0 231 Nullable<PersistenceType> mPersistenceType;
michael@0 232 CallbackState mCallbackState;
michael@0 233 };
michael@0 234
michael@0 235 // Responsible for calculating the amount of space taken up by storages of a
michael@0 236 // certain origin. Created when nsIQuotaManager::GetUsageForURI is called.
michael@0 237 // May be canceled with nsIQuotaRequest::Cancel. Runs three times, first
michael@0 238 // on the main thread, next on the IO thread, and then finally again on the main
michael@0 239 // thread. While on the IO thread the runnable will calculate the size of all
michael@0 240 // files in the origin's directory before dispatching itself back to the main
michael@0 241 // thread. When on the main thread the runnable will call the callback and then
michael@0 242 // notify the QuotaManager that the job has been completed.
michael@0 243 class AsyncUsageRunnable MOZ_FINAL : public UsageInfo,
michael@0 244 public nsRunnable,
michael@0 245 public nsIQuotaRequest
michael@0 246 {
michael@0 247 enum CallbackState {
michael@0 248 // Not yet run.
michael@0 249 Pending = 0,
michael@0 250
michael@0 251 // Running on the main thread in the callback for OpenAllowed.
michael@0 252 OpenAllowed,
michael@0 253
michael@0 254 // Running on the IO thread.
michael@0 255 IO,
michael@0 256
michael@0 257 // Running on the main thread after all work is done.
michael@0 258 Complete,
michael@0 259
michael@0 260 // Running on the main thread after skipping the work
michael@0 261 Shortcut
michael@0 262 };
michael@0 263
michael@0 264 public:
michael@0 265 NS_DECL_ISUPPORTS_INHERITED
michael@0 266 NS_DECL_NSIQUOTAREQUEST
michael@0 267
michael@0 268 AsyncUsageRunnable(uint32_t aAppId,
michael@0 269 bool aInMozBrowserOnly,
michael@0 270 const nsACString& aGroup,
michael@0 271 const OriginOrPatternString& aOrigin,
michael@0 272 nsIURI* aURI,
michael@0 273 nsIUsageCallback* aCallback);
michael@0 274
michael@0 275 NS_IMETHOD
michael@0 276 Run();
michael@0 277
michael@0 278 void
michael@0 279 AdvanceState()
michael@0 280 {
michael@0 281 switch (mCallbackState) {
michael@0 282 case Pending:
michael@0 283 mCallbackState = OpenAllowed;
michael@0 284 return;
michael@0 285 case OpenAllowed:
michael@0 286 mCallbackState = IO;
michael@0 287 return;
michael@0 288 case IO:
michael@0 289 mCallbackState = Complete;
michael@0 290 return;
michael@0 291 default:
michael@0 292 NS_NOTREACHED("Can't advance past Complete!");
michael@0 293 }
michael@0 294 }
michael@0 295
michael@0 296 nsresult
michael@0 297 TakeShortcut();
michael@0 298
michael@0 299 private:
michael@0 300 // Run calls the RunInternal method and makes sure that we always dispatch
michael@0 301 // to the main thread in case of an error.
michael@0 302 inline nsresult
michael@0 303 RunInternal();
michael@0 304
michael@0 305 nsresult
michael@0 306 AddToUsage(QuotaManager* aQuotaManager,
michael@0 307 PersistenceType aPersistenceType);
michael@0 308
michael@0 309 nsCOMPtr<nsIURI> mURI;
michael@0 310 nsCOMPtr<nsIUsageCallback> mCallback;
michael@0 311 uint32_t mAppId;
michael@0 312 nsCString mGroup;
michael@0 313 OriginOrPatternString mOrigin;
michael@0 314 CallbackState mCallbackState;
michael@0 315 bool mInMozBrowserOnly;
michael@0 316 };
michael@0 317
michael@0 318 class ResetOrClearRunnable MOZ_FINAL : public nsRunnable,
michael@0 319 public AcquireListener
michael@0 320 {
michael@0 321 enum CallbackState {
michael@0 322 // Not yet run.
michael@0 323 Pending = 0,
michael@0 324
michael@0 325 // Running on the main thread in the callback for OpenAllowed.
michael@0 326 OpenAllowed,
michael@0 327
michael@0 328 // Running on the IO thread.
michael@0 329 IO,
michael@0 330
michael@0 331 // Running on the main thread after all work is done.
michael@0 332 Complete
michael@0 333 };
michael@0 334
michael@0 335 public:
michael@0 336 NS_DECL_ISUPPORTS_INHERITED
michael@0 337
michael@0 338 ResetOrClearRunnable(bool aClear)
michael@0 339 : mCallbackState(Pending),
michael@0 340 mClear(aClear)
michael@0 341 { }
michael@0 342
michael@0 343 NS_IMETHOD
michael@0 344 Run();
michael@0 345
michael@0 346 // AcquireListener override
michael@0 347 virtual nsresult
michael@0 348 OnExclusiveAccessAcquired() MOZ_OVERRIDE;
michael@0 349
michael@0 350 void
michael@0 351 AdvanceState()
michael@0 352 {
michael@0 353 switch (mCallbackState) {
michael@0 354 case Pending:
michael@0 355 mCallbackState = OpenAllowed;
michael@0 356 return;
michael@0 357 case OpenAllowed:
michael@0 358 mCallbackState = IO;
michael@0 359 return;
michael@0 360 case IO:
michael@0 361 mCallbackState = Complete;
michael@0 362 return;
michael@0 363 default:
michael@0 364 NS_NOTREACHED("Can't advance past Complete!");
michael@0 365 }
michael@0 366 }
michael@0 367
michael@0 368 static void
michael@0 369 InvalidateOpenedStorages(nsTArray<nsCOMPtr<nsIOfflineStorage> >& aStorages,
michael@0 370 void* aClosure);
michael@0 371
michael@0 372 void
michael@0 373 DeleteFiles(QuotaManager* aQuotaManager,
michael@0 374 PersistenceType aPersistenceType);
michael@0 375
michael@0 376 private:
michael@0 377 CallbackState mCallbackState;
michael@0 378 bool mClear;
michael@0 379 };
michael@0 380
michael@0 381 // Responsible for finalizing eviction of certian origins (storage files have
michael@0 382 // been already cleared, we just need to release IO thread only objects and
michael@0 383 // allow next synchronized ops for evicted origins). Created when
michael@0 384 // QuotaManager::FinalizeOriginEviction is called. Runs three times, first
michael@0 385 // on the main thread, next on the IO thread, and then finally again on the main
michael@0 386 // thread. While on the IO thread the runnable will release IO thread only
michael@0 387 // objects before dispatching itself back to the main thread. When back on the
michael@0 388 // main thread the runnable will call QuotaManager::AllowNextSynchronizedOp.
michael@0 389 // The runnable can also run in a shortened mode (runs only twice).
michael@0 390 class FinalizeOriginEvictionRunnable MOZ_FINAL : public nsRunnable
michael@0 391 {
michael@0 392 enum CallbackState {
michael@0 393 // Not yet run.
michael@0 394 Pending = 0,
michael@0 395
michael@0 396 // Running on the main thread in the callback for OpenAllowed.
michael@0 397 OpenAllowed,
michael@0 398
michael@0 399 // Running on the IO thread.
michael@0 400 IO,
michael@0 401
michael@0 402 // Running on the main thread after IO work is done.
michael@0 403 Complete
michael@0 404 };
michael@0 405
michael@0 406 public:
michael@0 407 FinalizeOriginEvictionRunnable(nsTArray<nsCString>& aOrigins)
michael@0 408 : mCallbackState(Pending)
michael@0 409 {
michael@0 410 mOrigins.SwapElements(aOrigins);
michael@0 411 }
michael@0 412
michael@0 413 NS_IMETHOD
michael@0 414 Run();
michael@0 415
michael@0 416 void
michael@0 417 AdvanceState()
michael@0 418 {
michael@0 419 switch (mCallbackState) {
michael@0 420 case Pending:
michael@0 421 mCallbackState = OpenAllowed;
michael@0 422 return;
michael@0 423 case OpenAllowed:
michael@0 424 mCallbackState = IO;
michael@0 425 return;
michael@0 426 case IO:
michael@0 427 mCallbackState = Complete;
michael@0 428 return;
michael@0 429 default:
michael@0 430 MOZ_ASSUME_UNREACHABLE("Can't advance past Complete!");
michael@0 431 }
michael@0 432 }
michael@0 433
michael@0 434 nsresult
michael@0 435 Dispatch();
michael@0 436
michael@0 437 nsresult
michael@0 438 RunImmediately();
michael@0 439
michael@0 440 private:
michael@0 441 CallbackState mCallbackState;
michael@0 442 nsTArray<nsCString> mOrigins;
michael@0 443 };
michael@0 444
michael@0 445 bool
michael@0 446 IsOnIOThread()
michael@0 447 {
michael@0 448 QuotaManager* quotaManager = QuotaManager::Get();
michael@0 449 NS_ASSERTION(quotaManager, "Must have a manager here!");
michael@0 450
michael@0 451 bool currentThread;
michael@0 452 return NS_SUCCEEDED(quotaManager->IOThread()->
michael@0 453 IsOnCurrentThread(&currentThread)) && currentThread;
michael@0 454 }
michael@0 455
michael@0 456 void
michael@0 457 AssertIsOnIOThread()
michael@0 458 {
michael@0 459 NS_ASSERTION(IsOnIOThread(), "Running on the wrong thread!");
michael@0 460 }
michael@0 461
michael@0 462 void
michael@0 463 AssertCurrentThreadOwnsQuotaMutex()
michael@0 464 {
michael@0 465 #ifdef DEBUG
michael@0 466 QuotaManager* quotaManager = QuotaManager::Get();
michael@0 467 NS_ASSERTION(quotaManager, "Must have a manager here!");
michael@0 468
michael@0 469 quotaManager->AssertCurrentThreadOwnsQuotaMutex();
michael@0 470 #endif
michael@0 471 }
michael@0 472
michael@0 473 END_QUOTA_NAMESPACE
michael@0 474
michael@0 475 namespace {
michael@0 476
michael@0 477 // Amount of space that storages may use by default in megabytes.
michael@0 478 static const int32_t kDefaultQuotaMB = 50;
michael@0 479
michael@0 480
michael@0 481 QuotaManager* gInstance = nullptr;
michael@0 482 mozilla::Atomic<bool> gShutdown(false);
michael@0 483
michael@0 484 int32_t gStorageQuotaMB = kDefaultQuotaMB;
michael@0 485
michael@0 486 // Constants for temporary storage limit computing.
michael@0 487 static const int32_t kDefaultFixedLimitKB = -1;
michael@0 488 static const uint32_t kDefaultChunkSizeKB = 10 * 1024;
michael@0 489 int32_t gFixedLimitKB = kDefaultFixedLimitKB;
michael@0 490 uint32_t gChunkSizeKB = kDefaultChunkSizeKB;
michael@0 491
michael@0 492 bool gTestingEnabled = false;
michael@0 493
michael@0 494 // A callback runnable used by the TransactionPool when it's safe to proceed
michael@0 495 // with a SetVersion/DeleteDatabase/etc.
michael@0 496 class WaitForTransactionsToFinishRunnable MOZ_FINAL : public nsRunnable
michael@0 497 {
michael@0 498 public:
michael@0 499 WaitForTransactionsToFinishRunnable(SynchronizedOp* aOp)
michael@0 500 : mOp(aOp), mCountdown(1)
michael@0 501 {
michael@0 502 NS_ASSERTION(mOp, "Why don't we have a runnable?");
michael@0 503 NS_ASSERTION(mOp->mStorages.IsEmpty(), "We're here too early!");
michael@0 504 NS_ASSERTION(mOp->mListener,
michael@0 505 "What are we supposed to do when we're done?");
michael@0 506 NS_ASSERTION(mCountdown, "Wrong countdown!");
michael@0 507 }
michael@0 508
michael@0 509 NS_IMETHOD
michael@0 510 Run();
michael@0 511
michael@0 512 void
michael@0 513 AddRun()
michael@0 514 {
michael@0 515 mCountdown++;
michael@0 516 }
michael@0 517
michael@0 518 private:
michael@0 519 // The QuotaManager holds this alive.
michael@0 520 SynchronizedOp* mOp;
michael@0 521 uint32_t mCountdown;
michael@0 522 };
michael@0 523
michael@0 524 class WaitForLockedFilesToFinishRunnable MOZ_FINAL : public nsRunnable
michael@0 525 {
michael@0 526 public:
michael@0 527 WaitForLockedFilesToFinishRunnable()
michael@0 528 : mBusy(true)
michael@0 529 { }
michael@0 530
michael@0 531 NS_IMETHOD
michael@0 532 Run();
michael@0 533
michael@0 534 bool
michael@0 535 IsBusy() const
michael@0 536 {
michael@0 537 return mBusy;
michael@0 538 }
michael@0 539
michael@0 540 private:
michael@0 541 bool mBusy;
michael@0 542 };
michael@0 543
michael@0 544 class SaveOriginAccessTimeRunnable MOZ_FINAL : public nsRunnable
michael@0 545 {
michael@0 546 public:
michael@0 547 SaveOriginAccessTimeRunnable(const nsACString& aOrigin, int64_t aTimestamp)
michael@0 548 : mOrigin(aOrigin), mTimestamp(aTimestamp)
michael@0 549 { }
michael@0 550
michael@0 551 NS_IMETHOD
michael@0 552 Run();
michael@0 553
michael@0 554 private:
michael@0 555 nsCString mOrigin;
michael@0 556 int64_t mTimestamp;
michael@0 557 };
michael@0 558
michael@0 559 struct MOZ_STACK_CLASS RemoveQuotaInfo
michael@0 560 {
michael@0 561 RemoveQuotaInfo(PersistenceType aPersistenceType, const nsACString& aPattern)
michael@0 562 : persistenceType(aPersistenceType), pattern(aPattern)
michael@0 563 { }
michael@0 564
michael@0 565 PersistenceType persistenceType;
michael@0 566 nsCString pattern;
michael@0 567 };
michael@0 568
michael@0 569 struct MOZ_STACK_CLASS InactiveOriginsInfo
michael@0 570 {
michael@0 571 InactiveOriginsInfo(OriginCollection& aCollection,
michael@0 572 nsTArray<OriginInfo*>& aOrigins)
michael@0 573 : collection(aCollection), origins(aOrigins)
michael@0 574 { }
michael@0 575
michael@0 576 OriginCollection& collection;
michael@0 577 nsTArray<OriginInfo*>& origins;
michael@0 578 };
michael@0 579
michael@0 580 bool
michael@0 581 IsMainProcess()
michael@0 582 {
michael@0 583 return XRE_GetProcessType() == GeckoProcessType_Default;
michael@0 584 }
michael@0 585
michael@0 586 void
michael@0 587 SanitizeOriginString(nsCString& aOrigin)
michael@0 588 {
michael@0 589 // We want profiles to be platform-independent so we always need to replace
michael@0 590 // the same characters on every platform. Windows has the most extensive set
michael@0 591 // of illegal characters so we use its FILE_ILLEGAL_CHARACTERS and
michael@0 592 // FILE_PATH_SEPARATOR.
michael@0 593 static const char kReplaceChars[] = CONTROL_CHARACTERS "/:*?\"<>|\\";
michael@0 594
michael@0 595 #ifdef XP_WIN
michael@0 596 NS_ASSERTION(!strcmp(kReplaceChars,
michael@0 597 FILE_ILLEGAL_CHARACTERS FILE_PATH_SEPARATOR),
michael@0 598 "Illegal file characters have changed!");
michael@0 599 #endif
michael@0 600
michael@0 601 aOrigin.ReplaceChar(kReplaceChars, '+');
michael@0 602 }
michael@0 603
michael@0 604 nsresult
michael@0 605 EnsureDirectory(nsIFile* aDirectory, bool* aCreated)
michael@0 606 {
michael@0 607 AssertIsOnIOThread();
michael@0 608
michael@0 609 nsresult rv = aDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
michael@0 610 if (rv == NS_ERROR_FILE_ALREADY_EXISTS) {
michael@0 611 bool isDirectory;
michael@0 612 rv = aDirectory->IsDirectory(&isDirectory);
michael@0 613 NS_ENSURE_SUCCESS(rv, rv);
michael@0 614 NS_ENSURE_TRUE(isDirectory, NS_ERROR_UNEXPECTED);
michael@0 615
michael@0 616 *aCreated = false;
michael@0 617 }
michael@0 618 else {
michael@0 619 NS_ENSURE_SUCCESS(rv, rv);
michael@0 620
michael@0 621 *aCreated = true;
michael@0 622 }
michael@0 623
michael@0 624 return NS_OK;
michael@0 625 }
michael@0 626
michael@0 627 nsresult
michael@0 628 CreateDirectoryUpgradeStamp(nsIFile* aDirectory)
michael@0 629 {
michael@0 630 AssertIsOnIOThread();
michael@0 631
michael@0 632 nsCOMPtr<nsIFile> metadataFile;
michael@0 633 nsresult rv = aDirectory->Clone(getter_AddRefs(metadataFile));
michael@0 634 NS_ENSURE_SUCCESS(rv, rv);
michael@0 635
michael@0 636 rv = metadataFile->Append(NS_LITERAL_STRING(METADATA_FILE_NAME));
michael@0 637 NS_ENSURE_SUCCESS(rv, rv);
michael@0 638
michael@0 639 rv = metadataFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
michael@0 640 NS_ENSURE_SUCCESS(rv, rv);
michael@0 641
michael@0 642 return NS_OK;
michael@0 643 }
michael@0 644
michael@0 645 nsresult
michael@0 646 GetDirectoryMetadataStream(nsIFile* aDirectory, bool aUpdate,
michael@0 647 nsIBinaryOutputStream** aStream)
michael@0 648 {
michael@0 649 AssertIsOnIOThread();
michael@0 650
michael@0 651 nsCOMPtr<nsIFile> metadataFile;
michael@0 652 nsresult rv = aDirectory->Clone(getter_AddRefs(metadataFile));
michael@0 653 NS_ENSURE_SUCCESS(rv, rv);
michael@0 654
michael@0 655 rv = metadataFile->Append(NS_LITERAL_STRING(METADATA_FILE_NAME));
michael@0 656 NS_ENSURE_SUCCESS(rv, rv);
michael@0 657
michael@0 658 nsCOMPtr<nsIOutputStream> outputStream;
michael@0 659 if (aUpdate) {
michael@0 660 bool exists;
michael@0 661 rv = metadataFile->Exists(&exists);
michael@0 662 NS_ENSURE_SUCCESS(rv, rv);
michael@0 663
michael@0 664 if (!exists) {
michael@0 665 *aStream = nullptr;
michael@0 666 return NS_OK;
michael@0 667 }
michael@0 668
michael@0 669 nsCOMPtr<nsIFileStream> stream;
michael@0 670 rv = NS_NewLocalFileStream(getter_AddRefs(stream), metadataFile);
michael@0 671 NS_ENSURE_SUCCESS(rv, rv);
michael@0 672
michael@0 673 outputStream = do_QueryInterface(stream);
michael@0 674 NS_ENSURE_TRUE(outputStream, NS_ERROR_FAILURE);
michael@0 675 }
michael@0 676 else {
michael@0 677 rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream),
michael@0 678 metadataFile);
michael@0 679 NS_ENSURE_SUCCESS(rv, rv);
michael@0 680 }
michael@0 681
michael@0 682 nsCOMPtr<nsIBinaryOutputStream> binaryStream =
michael@0 683 do_CreateInstance("@mozilla.org/binaryoutputstream;1");
michael@0 684 NS_ENSURE_TRUE(binaryStream, NS_ERROR_FAILURE);
michael@0 685
michael@0 686 rv = binaryStream->SetOutputStream(outputStream);
michael@0 687 NS_ENSURE_SUCCESS(rv, rv);
michael@0 688
michael@0 689 binaryStream.forget(aStream);
michael@0 690 return NS_OK;
michael@0 691 }
michael@0 692
michael@0 693 nsresult
michael@0 694 CreateDirectoryMetadata(nsIFile* aDirectory, int64_t aTimestamp,
michael@0 695 const nsACString& aGroup, const nsACString& aOrigin)
michael@0 696 {
michael@0 697 AssertIsOnIOThread();
michael@0 698
michael@0 699 nsCOMPtr<nsIBinaryOutputStream> stream;
michael@0 700 nsresult rv =
michael@0 701 GetDirectoryMetadataStream(aDirectory, false, getter_AddRefs(stream));
michael@0 702 NS_ENSURE_SUCCESS(rv, rv);
michael@0 703
michael@0 704 NS_ASSERTION(stream, "This shouldn't be null!");
michael@0 705
michael@0 706 rv = stream->Write64(aTimestamp);
michael@0 707 NS_ENSURE_SUCCESS(rv, rv);
michael@0 708
michael@0 709 rv = stream->WriteStringZ(PromiseFlatCString(aGroup).get());
michael@0 710 NS_ENSURE_SUCCESS(rv, rv);
michael@0 711
michael@0 712 rv = stream->WriteStringZ(PromiseFlatCString(aOrigin).get());
michael@0 713 NS_ENSURE_SUCCESS(rv, rv);
michael@0 714
michael@0 715 return NS_OK;
michael@0 716 }
michael@0 717
michael@0 718 nsresult
michael@0 719 GetDirectoryMetadata(nsIFile* aDirectory, int64_t* aTimestamp,
michael@0 720 nsACString& aGroup, nsACString& aOrigin)
michael@0 721 {
michael@0 722 AssertIsOnIOThread();
michael@0 723
michael@0 724 nsCOMPtr<nsIFile> metadataFile;
michael@0 725 nsresult rv = aDirectory->Clone(getter_AddRefs(metadataFile));
michael@0 726 NS_ENSURE_SUCCESS(rv, rv);
michael@0 727
michael@0 728 rv = metadataFile->Append(NS_LITERAL_STRING(METADATA_FILE_NAME));
michael@0 729 NS_ENSURE_SUCCESS(rv, rv);
michael@0 730
michael@0 731 nsCOMPtr<nsIInputStream> stream;
michael@0 732 rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), metadataFile);
michael@0 733 NS_ENSURE_SUCCESS(rv, rv);
michael@0 734
michael@0 735 nsCOMPtr<nsIInputStream> bufferedStream;
michael@0 736 rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream), stream, 512);
michael@0 737 NS_ENSURE_SUCCESS(rv, rv);
michael@0 738
michael@0 739 nsCOMPtr<nsIBinaryInputStream> binaryStream =
michael@0 740 do_CreateInstance("@mozilla.org/binaryinputstream;1");
michael@0 741 NS_ENSURE_TRUE(binaryStream, NS_ERROR_FAILURE);
michael@0 742
michael@0 743 rv = binaryStream->SetInputStream(bufferedStream);
michael@0 744 NS_ENSURE_SUCCESS(rv, rv);
michael@0 745
michael@0 746 uint64_t timestamp;
michael@0 747 rv = binaryStream->Read64(&timestamp);
michael@0 748 NS_ENSURE_SUCCESS(rv, rv);
michael@0 749
michael@0 750 nsCString group;
michael@0 751 rv = binaryStream->ReadCString(group);
michael@0 752 NS_ENSURE_SUCCESS(rv, rv);
michael@0 753
michael@0 754 nsCString origin;
michael@0 755 rv = binaryStream->ReadCString(origin);
michael@0 756 NS_ENSURE_SUCCESS(rv, rv);
michael@0 757
michael@0 758 *aTimestamp = timestamp;
michael@0 759 aGroup = group;
michael@0 760 aOrigin = origin;
michael@0 761 return NS_OK;
michael@0 762 }
michael@0 763
michael@0 764 nsresult
michael@0 765 MaybeUpgradeOriginDirectory(nsIFile* aDirectory)
michael@0 766 {
michael@0 767 AssertIsOnIOThread();
michael@0 768 NS_ASSERTION(aDirectory, "Null pointer!");
michael@0 769
michael@0 770 nsCOMPtr<nsIFile> metadataFile;
michael@0 771 nsresult rv = aDirectory->Clone(getter_AddRefs(metadataFile));
michael@0 772 NS_ENSURE_SUCCESS(rv, rv);
michael@0 773
michael@0 774 rv = metadataFile->Append(NS_LITERAL_STRING(METADATA_FILE_NAME));
michael@0 775 NS_ENSURE_SUCCESS(rv, rv);
michael@0 776
michael@0 777 bool exists;
michael@0 778 rv = metadataFile->Exists(&exists);
michael@0 779 NS_ENSURE_SUCCESS(rv, rv);
michael@0 780
michael@0 781 if (!exists) {
michael@0 782 // Directory structure upgrade needed.
michael@0 783 // Move all files to IDB specific directory.
michael@0 784
michael@0 785 nsString idbDirectoryName;
michael@0 786 rv = Client::TypeToText(Client::IDB, idbDirectoryName);
michael@0 787 NS_ENSURE_SUCCESS(rv, rv);
michael@0 788
michael@0 789 nsCOMPtr<nsIFile> idbDirectory;
michael@0 790 rv = aDirectory->Clone(getter_AddRefs(idbDirectory));
michael@0 791 NS_ENSURE_SUCCESS(rv, rv);
michael@0 792
michael@0 793 rv = idbDirectory->Append(idbDirectoryName);
michael@0 794 NS_ENSURE_SUCCESS(rv, rv);
michael@0 795
michael@0 796 rv = idbDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
michael@0 797 if (rv == NS_ERROR_FILE_ALREADY_EXISTS) {
michael@0 798 NS_WARNING("IDB directory already exists!");
michael@0 799
michael@0 800 bool isDirectory;
michael@0 801 rv = idbDirectory->IsDirectory(&isDirectory);
michael@0 802 NS_ENSURE_SUCCESS(rv, rv);
michael@0 803 NS_ENSURE_TRUE(isDirectory, NS_ERROR_UNEXPECTED);
michael@0 804 }
michael@0 805 else {
michael@0 806 NS_ENSURE_SUCCESS(rv, rv);
michael@0 807 }
michael@0 808
michael@0 809 nsCOMPtr<nsISimpleEnumerator> entries;
michael@0 810 rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
michael@0 811 NS_ENSURE_SUCCESS(rv, rv);
michael@0 812
michael@0 813 bool hasMore;
michael@0 814 while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
michael@0 815 nsCOMPtr<nsISupports> entry;
michael@0 816 rv = entries->GetNext(getter_AddRefs(entry));
michael@0 817 NS_ENSURE_SUCCESS(rv, rv);
michael@0 818
michael@0 819 nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
michael@0 820 NS_ENSURE_TRUE(file, NS_NOINTERFACE);
michael@0 821
michael@0 822 nsString leafName;
michael@0 823 rv = file->GetLeafName(leafName);
michael@0 824 NS_ENSURE_SUCCESS(rv, rv);
michael@0 825
michael@0 826 if (!leafName.Equals(idbDirectoryName)) {
michael@0 827 rv = file->MoveTo(idbDirectory, EmptyString());
michael@0 828 NS_ENSURE_SUCCESS(rv, rv);
michael@0 829 }
michael@0 830 }
michael@0 831
michael@0 832 rv = metadataFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
michael@0 833 NS_ENSURE_SUCCESS(rv, rv);
michael@0 834 }
michael@0 835
michael@0 836 return NS_OK;
michael@0 837 }
michael@0 838
michael@0 839 // This method computes and returns our best guess for the temporary storage
michael@0 840 // limit (in bytes), based on the amount of space users have free on their hard
michael@0 841 // drive and on given temporary storage usage (also in bytes).
michael@0 842 nsresult
michael@0 843 GetTemporaryStorageLimit(nsIFile* aDirectory, uint64_t aCurrentUsage,
michael@0 844 uint64_t* aLimit)
michael@0 845 {
michael@0 846 // Check for free space on device where temporary storage directory lives.
michael@0 847 int64_t bytesAvailable;
michael@0 848 nsresult rv = aDirectory->GetDiskSpaceAvailable(&bytesAvailable);
michael@0 849 NS_ENSURE_SUCCESS(rv, rv);
michael@0 850
michael@0 851 NS_ASSERTION(bytesAvailable >= 0, "Negative bytes available?!");
michael@0 852
michael@0 853 uint64_t availableKB =
michael@0 854 static_cast<uint64_t>((bytesAvailable + aCurrentUsage) / 1024);
michael@0 855
michael@0 856 // Grow/shrink in gChunkSizeKB units, deliberately, so that in the common case
michael@0 857 // we don't shrink temporary storage and evict origin data every time we
michael@0 858 // initialize.
michael@0 859 availableKB = (availableKB / gChunkSizeKB) * gChunkSizeKB;
michael@0 860
michael@0 861 // Allow temporary storage to consume up to half the available space.
michael@0 862 uint64_t resultKB = availableKB * .50;
michael@0 863
michael@0 864 *aLimit = resultKB * 1024;
michael@0 865 return NS_OK;
michael@0 866 }
michael@0 867
michael@0 868 } // anonymous namespace
michael@0 869
michael@0 870 QuotaManager::QuotaManager()
michael@0 871 : mCurrentWindowIndex(BAD_TLS_INDEX),
michael@0 872 mQuotaMutex("QuotaManager.mQuotaMutex"),
michael@0 873 mTemporaryStorageLimit(0),
michael@0 874 mTemporaryStorageUsage(0),
michael@0 875 mTemporaryStorageInitialized(false),
michael@0 876 mStorageAreaInitialized(false)
michael@0 877 {
michael@0 878 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 879 NS_ASSERTION(!gInstance, "More than one instance!");
michael@0 880 }
michael@0 881
michael@0 882 QuotaManager::~QuotaManager()
michael@0 883 {
michael@0 884 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 885 NS_ASSERTION(!gInstance || gInstance == this, "Different instances!");
michael@0 886 gInstance = nullptr;
michael@0 887 }
michael@0 888
michael@0 889 // static
michael@0 890 QuotaManager*
michael@0 891 QuotaManager::GetOrCreate()
michael@0 892 {
michael@0 893 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 894
michael@0 895 if (IsShuttingDown()) {
michael@0 896 NS_ERROR("Calling GetOrCreate() after shutdown!");
michael@0 897 return nullptr;
michael@0 898 }
michael@0 899
michael@0 900 if (!gInstance) {
michael@0 901 nsRefPtr<QuotaManager> instance(new QuotaManager());
michael@0 902
michael@0 903 nsresult rv = instance->Init();
michael@0 904 NS_ENSURE_SUCCESS(rv, nullptr);
michael@0 905
michael@0 906 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
michael@0 907 NS_ENSURE_TRUE(obs, nullptr);
michael@0 908
michael@0 909 // We need this callback to know when to shut down all our threads.
michael@0 910 rv = obs->AddObserver(instance, PROFILE_BEFORE_CHANGE_OBSERVER_ID, false);
michael@0 911 NS_ENSURE_SUCCESS(rv, nullptr);
michael@0 912
michael@0 913 // The observer service will hold our last reference, don't AddRef here.
michael@0 914 gInstance = instance;
michael@0 915 }
michael@0 916
michael@0 917 return gInstance;
michael@0 918 }
michael@0 919
michael@0 920 // static
michael@0 921 QuotaManager*
michael@0 922 QuotaManager::Get()
michael@0 923 {
michael@0 924 // Does not return an owning reference.
michael@0 925 return gInstance;
michael@0 926 }
michael@0 927
michael@0 928 // static
michael@0 929 QuotaManager*
michael@0 930 QuotaManager::FactoryCreate()
michael@0 931 {
michael@0 932 // Returns a raw pointer that carries an owning reference! Lame, but the
michael@0 933 // singleton factory macros force this.
michael@0 934 QuotaManager* quotaManager = GetOrCreate();
michael@0 935 NS_IF_ADDREF(quotaManager);
michael@0 936 return quotaManager;
michael@0 937 }
michael@0 938
michael@0 939 // static
michael@0 940 bool
michael@0 941 QuotaManager::IsShuttingDown()
michael@0 942 {
michael@0 943 return gShutdown;
michael@0 944 }
michael@0 945
michael@0 946 nsresult
michael@0 947 QuotaManager::Init()
michael@0 948 {
michael@0 949 // We need a thread-local to hold the current window.
michael@0 950 NS_ASSERTION(mCurrentWindowIndex == BAD_TLS_INDEX, "Huh?");
michael@0 951
michael@0 952 if (PR_NewThreadPrivateIndex(&mCurrentWindowIndex, nullptr) != PR_SUCCESS) {
michael@0 953 NS_ERROR("PR_NewThreadPrivateIndex failed, QuotaManager disabled");
michael@0 954 mCurrentWindowIndex = BAD_TLS_INDEX;
michael@0 955 return NS_ERROR_FAILURE;
michael@0 956 }
michael@0 957
michael@0 958 nsresult rv;
michael@0 959 if (IsMainProcess()) {
michael@0 960 nsCOMPtr<nsIFile> baseDir;
michael@0 961 rv = NS_GetSpecialDirectory(NS_APP_INDEXEDDB_PARENT_DIR,
michael@0 962 getter_AddRefs(baseDir));
michael@0 963 if (NS_FAILED(rv)) {
michael@0 964 rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
michael@0 965 getter_AddRefs(baseDir));
michael@0 966 }
michael@0 967 NS_ENSURE_SUCCESS(rv, rv);
michael@0 968
michael@0 969 nsCOMPtr<nsIFile> indexedDBDir;
michael@0 970 rv = baseDir->Clone(getter_AddRefs(indexedDBDir));
michael@0 971 NS_ENSURE_SUCCESS(rv, rv);
michael@0 972
michael@0 973 rv = indexedDBDir->Append(NS_LITERAL_STRING("indexedDB"));
michael@0 974 NS_ENSURE_SUCCESS(rv, rv);
michael@0 975
michael@0 976 rv = indexedDBDir->GetPath(mIndexedDBPath);
michael@0 977 NS_ENSURE_SUCCESS(rv, rv);
michael@0 978
michael@0 979 rv = baseDir->Append(NS_LITERAL_STRING("storage"));
michael@0 980 NS_ENSURE_SUCCESS(rv, rv);
michael@0 981
michael@0 982 nsCOMPtr<nsIFile> persistentStorageDir;
michael@0 983 rv = baseDir->Clone(getter_AddRefs(persistentStorageDir));
michael@0 984 NS_ENSURE_SUCCESS(rv, rv);
michael@0 985
michael@0 986 rv = persistentStorageDir->Append(NS_LITERAL_STRING("persistent"));
michael@0 987 NS_ENSURE_SUCCESS(rv, rv);
michael@0 988
michael@0 989 rv = persistentStorageDir->GetPath(mPersistentStoragePath);
michael@0 990 NS_ENSURE_SUCCESS(rv, rv);
michael@0 991
michael@0 992 nsCOMPtr<nsIFile> temporaryStorageDir;
michael@0 993 rv = baseDir->Clone(getter_AddRefs(temporaryStorageDir));
michael@0 994 NS_ENSURE_SUCCESS(rv, rv);
michael@0 995
michael@0 996 rv = temporaryStorageDir->Append(NS_LITERAL_STRING("temporary"));
michael@0 997 NS_ENSURE_SUCCESS(rv, rv);
michael@0 998
michael@0 999 rv = temporaryStorageDir->GetPath(mTemporaryStoragePath);
michael@0 1000 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1001
michael@0 1002 // Make a lazy thread for any IO we need (like clearing or enumerating the
michael@0 1003 // contents of storage directories).
michael@0 1004 mIOThread = new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS,
michael@0 1005 NS_LITERAL_CSTRING("Storage I/O"),
michael@0 1006 LazyIdleThread::ManualShutdown);
michael@0 1007
michael@0 1008 // Make a timer here to avoid potential failures later. We don't actually
michael@0 1009 // initialize the timer until shutdown.
michael@0 1010 mShutdownTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
michael@0 1011 NS_ENSURE_TRUE(mShutdownTimer, NS_ERROR_FAILURE);
michael@0 1012 }
michael@0 1013
michael@0 1014 if (NS_FAILED(Preferences::AddIntVarCache(&gStorageQuotaMB,
michael@0 1015 PREF_STORAGE_QUOTA,
michael@0 1016 kDefaultQuotaMB))) {
michael@0 1017 NS_WARNING("Unable to respond to quota pref changes!");
michael@0 1018 }
michael@0 1019
michael@0 1020 if (NS_FAILED(Preferences::AddIntVarCache(&gFixedLimitKB, PREF_FIXED_LIMIT,
michael@0 1021 kDefaultFixedLimitKB)) ||
michael@0 1022 NS_FAILED(Preferences::AddUintVarCache(&gChunkSizeKB,
michael@0 1023 PREF_CHUNK_SIZE,
michael@0 1024 kDefaultChunkSizeKB))) {
michael@0 1025 NS_WARNING("Unable to respond to temp storage pref changes!");
michael@0 1026 }
michael@0 1027
michael@0 1028 if (NS_FAILED(Preferences::AddBoolVarCache(&gTestingEnabled,
michael@0 1029 PREF_TESTING_FEATURES, false))) {
michael@0 1030 NS_WARNING("Unable to respond to testing pref changes!");
michael@0 1031 }
michael@0 1032
michael@0 1033 static_assert(Client::IDB == 0 && Client::ASMJS == 1 && Client::TYPE_MAX == 2,
michael@0 1034 "Fix the registration!");
michael@0 1035
michael@0 1036 NS_ASSERTION(mClients.Capacity() == Client::TYPE_MAX,
michael@0 1037 "Should be using an auto array with correct capacity!");
michael@0 1038
michael@0 1039 // Register IndexedDB
michael@0 1040 mClients.AppendElement(new indexedDB::Client());
michael@0 1041 mClients.AppendElement(asmjscache::CreateClient());
michael@0 1042
michael@0 1043 return NS_OK;
michael@0 1044 }
michael@0 1045
michael@0 1046 void
michael@0 1047 QuotaManager::InitQuotaForOrigin(PersistenceType aPersistenceType,
michael@0 1048 const nsACString& aGroup,
michael@0 1049 const nsACString& aOrigin,
michael@0 1050 uint64_t aLimitBytes,
michael@0 1051 uint64_t aUsageBytes,
michael@0 1052 int64_t aAccessTime)
michael@0 1053 {
michael@0 1054 AssertIsOnIOThread();
michael@0 1055 MOZ_ASSERT(aLimitBytes > 0 ||
michael@0 1056 aPersistenceType == PERSISTENCE_TYPE_TEMPORARY);
michael@0 1057 MOZ_ASSERT(aUsageBytes <= aLimitBytes ||
michael@0 1058 aPersistenceType == PERSISTENCE_TYPE_TEMPORARY);
michael@0 1059
michael@0 1060 MutexAutoLock lock(mQuotaMutex);
michael@0 1061
michael@0 1062 GroupInfoPair* pair;
michael@0 1063 if (!mGroupInfoPairs.Get(aGroup, &pair)) {
michael@0 1064 pair = new GroupInfoPair();
michael@0 1065 mGroupInfoPairs.Put(aGroup, pair);
michael@0 1066 // The hashtable is now responsible to delete the GroupInfoPair.
michael@0 1067 }
michael@0 1068
michael@0 1069 nsRefPtr<GroupInfo> groupInfo = pair->LockedGetGroupInfo(aPersistenceType);
michael@0 1070 if (!groupInfo) {
michael@0 1071 groupInfo = new GroupInfo(aPersistenceType, aGroup);
michael@0 1072 pair->LockedSetGroupInfo(groupInfo);
michael@0 1073 }
michael@0 1074
michael@0 1075 nsRefPtr<OriginInfo> originInfo =
michael@0 1076 new OriginInfo(groupInfo, aOrigin, aLimitBytes, aUsageBytes, aAccessTime);
michael@0 1077 groupInfo->LockedAddOriginInfo(originInfo);
michael@0 1078 }
michael@0 1079
michael@0 1080 void
michael@0 1081 QuotaManager::DecreaseUsageForOrigin(PersistenceType aPersistenceType,
michael@0 1082 const nsACString& aGroup,
michael@0 1083 const nsACString& aOrigin,
michael@0 1084 int64_t aSize)
michael@0 1085 {
michael@0 1086 AssertIsOnIOThread();
michael@0 1087
michael@0 1088 MutexAutoLock lock(mQuotaMutex);
michael@0 1089
michael@0 1090 GroupInfoPair* pair;
michael@0 1091 if (!mGroupInfoPairs.Get(aGroup, &pair)) {
michael@0 1092 return;
michael@0 1093 }
michael@0 1094
michael@0 1095 nsRefPtr<GroupInfo> groupInfo = pair->LockedGetGroupInfo(aPersistenceType);
michael@0 1096 if (!groupInfo) {
michael@0 1097 return;
michael@0 1098 }
michael@0 1099
michael@0 1100 nsRefPtr<OriginInfo> originInfo = groupInfo->LockedGetOriginInfo(aOrigin);
michael@0 1101 if (originInfo) {
michael@0 1102 originInfo->LockedDecreaseUsage(aSize);
michael@0 1103 }
michael@0 1104 }
michael@0 1105
michael@0 1106 void
michael@0 1107 QuotaManager::UpdateOriginAccessTime(PersistenceType aPersistenceType,
michael@0 1108 const nsACString& aGroup,
michael@0 1109 const nsACString& aOrigin)
michael@0 1110 {
michael@0 1111 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 1112
michael@0 1113 MutexAutoLock lock(mQuotaMutex);
michael@0 1114
michael@0 1115 GroupInfoPair* pair;
michael@0 1116 if (!mGroupInfoPairs.Get(aGroup, &pair)) {
michael@0 1117 return;
michael@0 1118 }
michael@0 1119
michael@0 1120 nsRefPtr<GroupInfo> groupInfo =
michael@0 1121 pair->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY);
michael@0 1122 if (!groupInfo) {
michael@0 1123 return;
michael@0 1124 }
michael@0 1125
michael@0 1126 nsRefPtr<OriginInfo> originInfo = groupInfo->LockedGetOriginInfo(aOrigin);
michael@0 1127 if (originInfo) {
michael@0 1128 int64_t timestamp = PR_Now();
michael@0 1129 originInfo->LockedUpdateAccessTime(timestamp);
michael@0 1130
michael@0 1131 if (!groupInfo->IsForTemporaryStorage()) {
michael@0 1132 return;
michael@0 1133 }
michael@0 1134
michael@0 1135 MutexAutoUnlock autoUnlock(mQuotaMutex);
michael@0 1136
michael@0 1137 SaveOriginAccessTime(aOrigin, timestamp);
michael@0 1138 }
michael@0 1139 }
michael@0 1140
michael@0 1141 // static
michael@0 1142 PLDHashOperator
michael@0 1143 QuotaManager::RemoveQuotaCallback(const nsACString& aKey,
michael@0 1144 nsAutoPtr<GroupInfoPair>& aValue,
michael@0 1145 void* aUserArg)
michael@0 1146 {
michael@0 1147 NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
michael@0 1148 NS_ASSERTION(aValue, "Null pointer!");
michael@0 1149
michael@0 1150 nsRefPtr<GroupInfo> groupInfo =
michael@0 1151 aValue->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY);
michael@0 1152 if (groupInfo) {
michael@0 1153 groupInfo->LockedRemoveOriginInfos();
michael@0 1154 }
michael@0 1155
michael@0 1156 return PL_DHASH_REMOVE;
michael@0 1157 }
michael@0 1158
michael@0 1159 void
michael@0 1160 QuotaManager::RemoveQuota()
michael@0 1161 {
michael@0 1162 MutexAutoLock lock(mQuotaMutex);
michael@0 1163
michael@0 1164 mGroupInfoPairs.Enumerate(RemoveQuotaCallback, nullptr);
michael@0 1165
michael@0 1166 NS_ASSERTION(mTemporaryStorageUsage == 0, "Should be zero!");
michael@0 1167 }
michael@0 1168
michael@0 1169 // static
michael@0 1170 PLDHashOperator
michael@0 1171 QuotaManager::RemoveQuotaForPersistenceTypeCallback(
michael@0 1172 const nsACString& aKey,
michael@0 1173 nsAutoPtr<GroupInfoPair>& aValue,
michael@0 1174 void* aUserArg)
michael@0 1175 {
michael@0 1176 NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
michael@0 1177 NS_ASSERTION(aValue, "Null pointer!");
michael@0 1178 NS_ASSERTION(aUserArg, "Null pointer!");
michael@0 1179
michael@0 1180 PersistenceType& persistenceType = *static_cast<PersistenceType*>(aUserArg);
michael@0 1181
michael@0 1182 if (persistenceType == PERSISTENCE_TYPE_TEMPORARY) {
michael@0 1183 nsRefPtr<GroupInfo> groupInfo =
michael@0 1184 aValue->LockedGetGroupInfo(persistenceType);
michael@0 1185 if (groupInfo) {
michael@0 1186 groupInfo->LockedRemoveOriginInfos();
michael@0 1187 }
michael@0 1188 }
michael@0 1189
michael@0 1190 aValue->LockedClearGroupInfo(persistenceType);
michael@0 1191
michael@0 1192 return aValue->LockedHasGroupInfos() ? PL_DHASH_NEXT : PL_DHASH_REMOVE;
michael@0 1193 }
michael@0 1194
michael@0 1195 void
michael@0 1196 QuotaManager::RemoveQuotaForPersistenceType(PersistenceType aPersistenceType)
michael@0 1197 {
michael@0 1198 MutexAutoLock lock(mQuotaMutex);
michael@0 1199
michael@0 1200 mGroupInfoPairs.Enumerate(RemoveQuotaForPersistenceTypeCallback,
michael@0 1201 &aPersistenceType);
michael@0 1202
michael@0 1203 NS_ASSERTION(aPersistenceType == PERSISTENCE_TYPE_PERSISTENT ||
michael@0 1204 mTemporaryStorageUsage == 0, "Should be zero!");
michael@0 1205 }
michael@0 1206
michael@0 1207 // static
michael@0 1208 PLDHashOperator
michael@0 1209 QuotaManager::RemoveQuotaForPatternCallback(const nsACString& aKey,
michael@0 1210 nsAutoPtr<GroupInfoPair>& aValue,
michael@0 1211 void* aUserArg)
michael@0 1212 {
michael@0 1213 NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
michael@0 1214 NS_ASSERTION(aValue, "Null pointer!");
michael@0 1215 NS_ASSERTION(aUserArg, "Null pointer!");
michael@0 1216
michael@0 1217 RemoveQuotaInfo* info = static_cast<RemoveQuotaInfo*>(aUserArg);
michael@0 1218
michael@0 1219 nsRefPtr<GroupInfo> groupInfo =
michael@0 1220 aValue->LockedGetGroupInfo(info->persistenceType);
michael@0 1221 if (groupInfo) {
michael@0 1222 groupInfo->LockedRemoveOriginInfosForPattern(info->pattern);
michael@0 1223
michael@0 1224 if (!groupInfo->LockedHasOriginInfos()) {
michael@0 1225 aValue->LockedClearGroupInfo(info->persistenceType);
michael@0 1226
michael@0 1227 if (!aValue->LockedHasGroupInfos()) {
michael@0 1228 return PL_DHASH_REMOVE;
michael@0 1229 }
michael@0 1230 }
michael@0 1231 }
michael@0 1232
michael@0 1233 return PL_DHASH_NEXT;
michael@0 1234 }
michael@0 1235
michael@0 1236 void
michael@0 1237 QuotaManager::RemoveQuotaForPattern(PersistenceType aPersistenceType,
michael@0 1238 const nsACString& aPattern)
michael@0 1239 {
michael@0 1240 NS_ASSERTION(!aPattern.IsEmpty(), "Empty pattern!");
michael@0 1241
michael@0 1242 RemoveQuotaInfo info(aPersistenceType, aPattern);
michael@0 1243
michael@0 1244 MutexAutoLock lock(mQuotaMutex);
michael@0 1245
michael@0 1246 mGroupInfoPairs.Enumerate(RemoveQuotaForPatternCallback, &info);
michael@0 1247 }
michael@0 1248
michael@0 1249 already_AddRefed<QuotaObject>
michael@0 1250 QuotaManager::GetQuotaObject(PersistenceType aPersistenceType,
michael@0 1251 const nsACString& aGroup,
michael@0 1252 const nsACString& aOrigin,
michael@0 1253 nsIFile* aFile)
michael@0 1254 {
michael@0 1255 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
michael@0 1256
michael@0 1257 nsString path;
michael@0 1258 nsresult rv = aFile->GetPath(path);
michael@0 1259 NS_ENSURE_SUCCESS(rv, nullptr);
michael@0 1260
michael@0 1261 int64_t fileSize;
michael@0 1262
michael@0 1263 bool exists;
michael@0 1264 rv = aFile->Exists(&exists);
michael@0 1265 NS_ENSURE_SUCCESS(rv, nullptr);
michael@0 1266
michael@0 1267 if (exists) {
michael@0 1268 rv = aFile->GetFileSize(&fileSize);
michael@0 1269 NS_ENSURE_SUCCESS(rv, nullptr);
michael@0 1270 }
michael@0 1271 else {
michael@0 1272 fileSize = 0;
michael@0 1273 }
michael@0 1274
michael@0 1275 nsRefPtr<QuotaObject> result;
michael@0 1276 {
michael@0 1277 MutexAutoLock lock(mQuotaMutex);
michael@0 1278
michael@0 1279 GroupInfoPair* pair;
michael@0 1280 if (!mGroupInfoPairs.Get(aGroup, &pair)) {
michael@0 1281 return nullptr;
michael@0 1282 }
michael@0 1283
michael@0 1284 nsRefPtr<GroupInfo> groupInfo = pair->LockedGetGroupInfo(aPersistenceType);
michael@0 1285
michael@0 1286 if (!groupInfo) {
michael@0 1287 return nullptr;
michael@0 1288 }
michael@0 1289
michael@0 1290 nsRefPtr<OriginInfo> originInfo = groupInfo->LockedGetOriginInfo(aOrigin);
michael@0 1291
michael@0 1292 if (!originInfo) {
michael@0 1293 return nullptr;
michael@0 1294 }
michael@0 1295
michael@0 1296 // We need this extra raw pointer because we can't assign to the smart
michael@0 1297 // pointer directly since QuotaObject::AddRef would try to acquire the same
michael@0 1298 // mutex.
michael@0 1299 QuotaObject* quotaObject;
michael@0 1300 if (!originInfo->mQuotaObjects.Get(path, &quotaObject)) {
michael@0 1301 // Create a new QuotaObject.
michael@0 1302 quotaObject = new QuotaObject(originInfo, path, fileSize);
michael@0 1303
michael@0 1304 // Put it to the hashtable. The hashtable is not responsible to delete
michael@0 1305 // the QuotaObject.
michael@0 1306 originInfo->mQuotaObjects.Put(path, quotaObject);
michael@0 1307 }
michael@0 1308
michael@0 1309 // Addref the QuotaObject and move the ownership to the result. This must
michael@0 1310 // happen before we unlock!
michael@0 1311 result = quotaObject->LockedAddRef();
michael@0 1312 }
michael@0 1313
michael@0 1314 // The caller becomes the owner of the QuotaObject, that is, the caller is
michael@0 1315 // is responsible to delete it when the last reference is removed.
michael@0 1316 return result.forget();
michael@0 1317 }
michael@0 1318
michael@0 1319 already_AddRefed<QuotaObject>
michael@0 1320 QuotaManager::GetQuotaObject(PersistenceType aPersistenceType,
michael@0 1321 const nsACString& aGroup,
michael@0 1322 const nsACString& aOrigin,
michael@0 1323 const nsAString& aPath)
michael@0 1324 {
michael@0 1325 nsresult rv;
michael@0 1326 nsCOMPtr<nsIFile> file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
michael@0 1327 NS_ENSURE_SUCCESS(rv, nullptr);
michael@0 1328
michael@0 1329 rv = file->InitWithPath(aPath);
michael@0 1330 NS_ENSURE_SUCCESS(rv, nullptr);
michael@0 1331
michael@0 1332 return GetQuotaObject(aPersistenceType, aGroup, aOrigin, file);
michael@0 1333 }
michael@0 1334
michael@0 1335 bool
michael@0 1336 QuotaManager::RegisterStorage(nsIOfflineStorage* aStorage)
michael@0 1337 {
michael@0 1338 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 1339 NS_ASSERTION(aStorage, "Null pointer!");
michael@0 1340
michael@0 1341 // Don't allow any new storages to be created after shutdown.
michael@0 1342 if (IsShuttingDown()) {
michael@0 1343 return false;
michael@0 1344 }
michael@0 1345
michael@0 1346 // Add this storage to its origin info if it exists, create it otherwise.
michael@0 1347 const nsACString& origin = aStorage->Origin();
michael@0 1348 ArrayCluster<nsIOfflineStorage*>* cluster;
michael@0 1349 if (!mLiveStorages.Get(origin, &cluster)) {
michael@0 1350 cluster = new ArrayCluster<nsIOfflineStorage*>();
michael@0 1351 mLiveStorages.Put(origin, cluster);
michael@0 1352
michael@0 1353 UpdateOriginAccessTime(aStorage->Type(), aStorage->Group(), origin);
michael@0 1354 }
michael@0 1355 (*cluster)[aStorage->GetClient()->GetType()].AppendElement(aStorage);
michael@0 1356
michael@0 1357 return true;
michael@0 1358 }
michael@0 1359
michael@0 1360 void
michael@0 1361 QuotaManager::UnregisterStorage(nsIOfflineStorage* aStorage)
michael@0 1362 {
michael@0 1363 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 1364 NS_ASSERTION(aStorage, "Null pointer!");
michael@0 1365
michael@0 1366 // Remove this storage from its origin array, maybe remove the array if it
michael@0 1367 // is then empty.
michael@0 1368 const nsACString& origin = aStorage->Origin();
michael@0 1369 ArrayCluster<nsIOfflineStorage*>* cluster;
michael@0 1370 if (mLiveStorages.Get(origin, &cluster) &&
michael@0 1371 (*cluster)[aStorage->GetClient()->GetType()].RemoveElement(aStorage)) {
michael@0 1372 if (cluster->IsEmpty()) {
michael@0 1373 mLiveStorages.Remove(origin);
michael@0 1374
michael@0 1375 UpdateOriginAccessTime(aStorage->Type(), aStorage->Group(), origin);
michael@0 1376 }
michael@0 1377 return;
michael@0 1378 }
michael@0 1379 NS_ERROR("Didn't know anything about this storage!");
michael@0 1380 }
michael@0 1381
michael@0 1382 void
michael@0 1383 QuotaManager::OnStorageClosed(nsIOfflineStorage* aStorage)
michael@0 1384 {
michael@0 1385 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 1386 NS_ASSERTION(aStorage, "Null pointer!");
michael@0 1387
michael@0 1388 // Check through the list of SynchronizedOps to see if any are waiting for
michael@0 1389 // this storage to close before proceeding.
michael@0 1390 SynchronizedOp* op =
michael@0 1391 FindSynchronizedOp(aStorage->Origin(),
michael@0 1392 Nullable<PersistenceType>(aStorage->Type()),
michael@0 1393 aStorage->Id());
michael@0 1394 if (op) {
michael@0 1395 Client::Type clientType = aStorage->GetClient()->GetType();
michael@0 1396
michael@0 1397 // This storage is in the scope of this SynchronizedOp. Remove it
michael@0 1398 // from the list if necessary.
michael@0 1399 if (op->mStorages[clientType].RemoveElement(aStorage)) {
michael@0 1400 // Now set up the helper if there are no more live storages.
michael@0 1401 NS_ASSERTION(op->mListener,
michael@0 1402 "How did we get rid of the listener before removing the "
michael@0 1403 "last storage?");
michael@0 1404 if (op->mStorages[clientType].IsEmpty()) {
michael@0 1405 // At this point, all storages are closed, so no new transactions
michael@0 1406 // can be started. There may, however, still be outstanding
michael@0 1407 // transactions that have not completed. We need to wait for those
michael@0 1408 // before we dispatch the helper.
michael@0 1409 if (NS_FAILED(RunSynchronizedOp(aStorage, op))) {
michael@0 1410 NS_WARNING("Failed to run synchronized op!");
michael@0 1411 }
michael@0 1412 }
michael@0 1413 }
michael@0 1414 }
michael@0 1415 }
michael@0 1416
michael@0 1417 void
michael@0 1418 QuotaManager::AbortCloseStoragesForWindow(nsPIDOMWindow* aWindow)
michael@0 1419 {
michael@0 1420 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 1421 NS_ASSERTION(aWindow, "Null pointer!");
michael@0 1422
michael@0 1423 FileService* service = FileService::Get();
michael@0 1424
michael@0 1425 StorageMatcher<ArrayCluster<nsIOfflineStorage*> > liveStorages;
michael@0 1426 liveStorages.Find(mLiveStorages);
michael@0 1427
michael@0 1428 for (uint32_t i = 0; i < Client::TYPE_MAX; i++) {
michael@0 1429 nsRefPtr<Client>& client = mClients[i];
michael@0 1430 bool utilized = service && client->IsFileServiceUtilized();
michael@0 1431 bool activated = client->IsTransactionServiceActivated();
michael@0 1432
michael@0 1433 nsTArray<nsIOfflineStorage*>& array = liveStorages[i];
michael@0 1434 for (uint32_t j = 0; j < array.Length(); j++) {
michael@0 1435 nsIOfflineStorage*& storage = array[j];
michael@0 1436
michael@0 1437 if (storage->IsOwned(aWindow)) {
michael@0 1438 if (NS_FAILED(storage->Close())) {
michael@0 1439 NS_WARNING("Failed to close storage for dying window!");
michael@0 1440 }
michael@0 1441
michael@0 1442 if (utilized) {
michael@0 1443 service->AbortLockedFilesForStorage(storage);
michael@0 1444 }
michael@0 1445
michael@0 1446 if (activated) {
michael@0 1447 client->AbortTransactionsForStorage(storage);
michael@0 1448 }
michael@0 1449 }
michael@0 1450 }
michael@0 1451 }
michael@0 1452 }
michael@0 1453
michael@0 1454 bool
michael@0 1455 QuotaManager::HasOpenTransactions(nsPIDOMWindow* aWindow)
michael@0 1456 {
michael@0 1457 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 1458 NS_ASSERTION(aWindow, "Null pointer!");
michael@0 1459
michael@0 1460 FileService* service = FileService::Get();
michael@0 1461
michael@0 1462 nsAutoPtr<StorageMatcher<ArrayCluster<nsIOfflineStorage*> > > liveStorages;
michael@0 1463
michael@0 1464 for (uint32_t i = 0; i < Client::TYPE_MAX; i++) {
michael@0 1465 nsRefPtr<Client>& client = mClients[i];
michael@0 1466 bool utilized = service && client->IsFileServiceUtilized();
michael@0 1467 bool activated = client->IsTransactionServiceActivated();
michael@0 1468
michael@0 1469 if (utilized || activated) {
michael@0 1470 if (!liveStorages) {
michael@0 1471 liveStorages = new StorageMatcher<ArrayCluster<nsIOfflineStorage*> >();
michael@0 1472 liveStorages->Find(mLiveStorages);
michael@0 1473 }
michael@0 1474
michael@0 1475 nsTArray<nsIOfflineStorage*>& storages = liveStorages->ArrayAt(i);
michael@0 1476 for (uint32_t j = 0; j < storages.Length(); j++) {
michael@0 1477 nsIOfflineStorage*& storage = storages[j];
michael@0 1478
michael@0 1479 if (storage->IsOwned(aWindow) &&
michael@0 1480 ((utilized && service->HasLockedFilesForStorage(storage)) ||
michael@0 1481 (activated && client->HasTransactionsForStorage(storage)))) {
michael@0 1482 return true;
michael@0 1483 }
michael@0 1484 }
michael@0 1485 }
michael@0 1486 }
michael@0 1487
michael@0 1488 return false;
michael@0 1489 }
michael@0 1490
michael@0 1491 nsresult
michael@0 1492 QuotaManager::WaitForOpenAllowed(const OriginOrPatternString& aOriginOrPattern,
michael@0 1493 Nullable<PersistenceType> aPersistenceType,
michael@0 1494 const nsACString& aId, nsIRunnable* aRunnable)
michael@0 1495 {
michael@0 1496 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 1497 NS_ASSERTION(!aOriginOrPattern.IsEmpty() || aOriginOrPattern.IsNull(),
michael@0 1498 "Empty pattern!");
michael@0 1499 NS_ASSERTION(aRunnable, "Null pointer!");
michael@0 1500
michael@0 1501 nsAutoPtr<SynchronizedOp> op(new SynchronizedOp(aOriginOrPattern,
michael@0 1502 aPersistenceType, aId));
michael@0 1503
michael@0 1504 // See if this runnable needs to wait.
michael@0 1505 bool delayed = false;
michael@0 1506 for (uint32_t index = mSynchronizedOps.Length(); index > 0; index--) {
michael@0 1507 nsAutoPtr<SynchronizedOp>& existingOp = mSynchronizedOps[index - 1];
michael@0 1508 if (op->MustWaitFor(*existingOp)) {
michael@0 1509 existingOp->DelayRunnable(aRunnable);
michael@0 1510 delayed = true;
michael@0 1511 break;
michael@0 1512 }
michael@0 1513 }
michael@0 1514
michael@0 1515 // Otherwise, dispatch it immediately.
michael@0 1516 if (!delayed) {
michael@0 1517 nsresult rv = NS_DispatchToCurrentThread(aRunnable);
michael@0 1518 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1519 }
michael@0 1520
michael@0 1521 // Adding this to the synchronized ops list will block any additional
michael@0 1522 // ops from proceeding until this one is done.
michael@0 1523 mSynchronizedOps.AppendElement(op.forget());
michael@0 1524
michael@0 1525 return NS_OK;
michael@0 1526 }
michael@0 1527
michael@0 1528 void
michael@0 1529 QuotaManager::AddSynchronizedOp(const OriginOrPatternString& aOriginOrPattern,
michael@0 1530 Nullable<PersistenceType> aPersistenceType)
michael@0 1531 {
michael@0 1532 nsAutoPtr<SynchronizedOp> op(new SynchronizedOp(aOriginOrPattern,
michael@0 1533 aPersistenceType,
michael@0 1534 EmptyCString()));
michael@0 1535
michael@0 1536 #ifdef DEBUG
michael@0 1537 for (uint32_t index = mSynchronizedOps.Length(); index > 0; index--) {
michael@0 1538 nsAutoPtr<SynchronizedOp>& existingOp = mSynchronizedOps[index - 1];
michael@0 1539 NS_ASSERTION(!op->MustWaitFor(*existingOp), "What?");
michael@0 1540 }
michael@0 1541 #endif
michael@0 1542
michael@0 1543 mSynchronizedOps.AppendElement(op.forget());
michael@0 1544 }
michael@0 1545
michael@0 1546 void
michael@0 1547 QuotaManager::AllowNextSynchronizedOp(
michael@0 1548 const OriginOrPatternString& aOriginOrPattern,
michael@0 1549 Nullable<PersistenceType> aPersistenceType,
michael@0 1550 const nsACString& aId)
michael@0 1551 {
michael@0 1552 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 1553 NS_ASSERTION(!aOriginOrPattern.IsEmpty() || aOriginOrPattern.IsNull(),
michael@0 1554 "Empty origin/pattern!");
michael@0 1555
michael@0 1556 uint32_t count = mSynchronizedOps.Length();
michael@0 1557 for (uint32_t index = 0; index < count; index++) {
michael@0 1558 nsAutoPtr<SynchronizedOp>& op = mSynchronizedOps[index];
michael@0 1559 if (op->mOriginOrPattern.IsOrigin() == aOriginOrPattern.IsOrigin() &&
michael@0 1560 op->mOriginOrPattern == aOriginOrPattern &&
michael@0 1561 op->mPersistenceType == aPersistenceType) {
michael@0 1562 if (op->mId == aId) {
michael@0 1563 NS_ASSERTION(op->mStorages.IsEmpty(), "How did this happen?");
michael@0 1564
michael@0 1565 op->DispatchDelayedRunnables();
michael@0 1566
michael@0 1567 mSynchronizedOps.RemoveElementAt(index);
michael@0 1568 return;
michael@0 1569 }
michael@0 1570
michael@0 1571 // If one or the other is for an origin clear, we should have matched
michael@0 1572 // solely on origin.
michael@0 1573 NS_ASSERTION(!op->mId.IsEmpty() && !aId.IsEmpty(),
michael@0 1574 "Why didn't we match earlier?");
michael@0 1575 }
michael@0 1576 }
michael@0 1577
michael@0 1578 NS_NOTREACHED("Why didn't we find a SynchronizedOp?");
michael@0 1579 }
michael@0 1580
michael@0 1581 nsresult
michael@0 1582 QuotaManager::GetDirectoryForOrigin(PersistenceType aPersistenceType,
michael@0 1583 const nsACString& aASCIIOrigin,
michael@0 1584 nsIFile** aDirectory) const
michael@0 1585 {
michael@0 1586 nsresult rv;
michael@0 1587 nsCOMPtr<nsIFile> directory =
michael@0 1588 do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
michael@0 1589 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1590
michael@0 1591 rv = directory->InitWithPath(GetStoragePath(aPersistenceType));
michael@0 1592 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1593
michael@0 1594 nsAutoCString originSanitized(aASCIIOrigin);
michael@0 1595 SanitizeOriginString(originSanitized);
michael@0 1596
michael@0 1597 rv = directory->Append(NS_ConvertASCIItoUTF16(originSanitized));
michael@0 1598 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1599
michael@0 1600 directory.forget(aDirectory);
michael@0 1601 return NS_OK;
michael@0 1602 }
michael@0 1603
michael@0 1604 nsresult
michael@0 1605 QuotaManager::InitializeOrigin(PersistenceType aPersistenceType,
michael@0 1606 const nsACString& aGroup,
michael@0 1607 const nsACString& aOrigin,
michael@0 1608 bool aTrackQuota,
michael@0 1609 int64_t aAccessTime,
michael@0 1610 nsIFile* aDirectory)
michael@0 1611 {
michael@0 1612 AssertIsOnIOThread();
michael@0 1613
michael@0 1614 nsresult rv;
michael@0 1615
michael@0 1616 bool temporaryStorage = aPersistenceType == PERSISTENCE_TYPE_TEMPORARY;
michael@0 1617 if (!temporaryStorage) {
michael@0 1618 rv = MaybeUpgradeOriginDirectory(aDirectory);
michael@0 1619 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1620 }
michael@0 1621
michael@0 1622 // We need to initialize directories of all clients if they exists and also
michael@0 1623 // get the total usage to initialize the quota.
michael@0 1624 nsAutoPtr<UsageInfo> usageInfo;
michael@0 1625 if (aTrackQuota) {
michael@0 1626 usageInfo = new UsageInfo();
michael@0 1627 }
michael@0 1628
michael@0 1629 nsCOMPtr<nsISimpleEnumerator> entries;
michael@0 1630 rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
michael@0 1631 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1632
michael@0 1633 bool hasMore;
michael@0 1634 while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
michael@0 1635 nsCOMPtr<nsISupports> entry;
michael@0 1636 rv = entries->GetNext(getter_AddRefs(entry));
michael@0 1637 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1638
michael@0 1639 nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
michael@0 1640 NS_ENSURE_TRUE(file, NS_NOINTERFACE);
michael@0 1641
michael@0 1642 nsString leafName;
michael@0 1643 rv = file->GetLeafName(leafName);
michael@0 1644 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1645
michael@0 1646 if (leafName.EqualsLiteral(METADATA_FILE_NAME) ||
michael@0 1647 leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
michael@0 1648 continue;
michael@0 1649 }
michael@0 1650
michael@0 1651 bool isDirectory;
michael@0 1652 rv = file->IsDirectory(&isDirectory);
michael@0 1653 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1654
michael@0 1655 if (!isDirectory) {
michael@0 1656 NS_WARNING("Unknown file found!");
michael@0 1657 return NS_ERROR_UNEXPECTED;
michael@0 1658 }
michael@0 1659
michael@0 1660 Client::Type clientType;
michael@0 1661 rv = Client::TypeFromText(leafName, clientType);
michael@0 1662 if (NS_FAILED(rv)) {
michael@0 1663 NS_WARNING("Unknown directory found!");
michael@0 1664 return NS_ERROR_UNEXPECTED;
michael@0 1665 }
michael@0 1666
michael@0 1667 rv = mClients[clientType]->InitOrigin(aPersistenceType, aGroup, aOrigin,
michael@0 1668 usageInfo);
michael@0 1669 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1670 }
michael@0 1671
michael@0 1672 if (aTrackQuota) {
michael@0 1673 uint64_t quotaMaxBytes;
michael@0 1674 uint64_t totalUsageBytes = usageInfo->TotalUsage();
michael@0 1675
michael@0 1676 if (temporaryStorage) {
michael@0 1677 // Temporary storage has no limit for origin usage (there's a group and
michael@0 1678 // the global limit though).
michael@0 1679 quotaMaxBytes = 0;
michael@0 1680 }
michael@0 1681 else {
michael@0 1682 quotaMaxBytes = GetStorageQuotaMB() * 1024 * 1024;
michael@0 1683 if (totalUsageBytes > quotaMaxBytes) {
michael@0 1684 NS_WARNING("Origin is already using more storage than allowed!");
michael@0 1685 return NS_ERROR_UNEXPECTED;
michael@0 1686 }
michael@0 1687 }
michael@0 1688
michael@0 1689 InitQuotaForOrigin(aPersistenceType, aGroup, aOrigin, quotaMaxBytes,
michael@0 1690 totalUsageBytes, aAccessTime);
michael@0 1691 }
michael@0 1692
michael@0 1693 return NS_OK;
michael@0 1694 }
michael@0 1695
michael@0 1696 nsresult
michael@0 1697 QuotaManager::MaybeUpgradeIndexedDBDirectory()
michael@0 1698 {
michael@0 1699 AssertIsOnIOThread();
michael@0 1700
michael@0 1701 if (mStorageAreaInitialized) {
michael@0 1702 return NS_OK;
michael@0 1703 }
michael@0 1704
michael@0 1705 nsresult rv;
michael@0 1706
michael@0 1707 nsCOMPtr<nsIFile> indexedDBDir =
michael@0 1708 do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
michael@0 1709 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1710
michael@0 1711 rv = indexedDBDir->InitWithPath(mIndexedDBPath);
michael@0 1712 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1713
michael@0 1714 bool exists;
michael@0 1715 rv = indexedDBDir->Exists(&exists);
michael@0 1716 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1717
michael@0 1718 if (!exists) {
michael@0 1719 // Nothing to upgrade.
michael@0 1720 mStorageAreaInitialized = true;
michael@0 1721
michael@0 1722 return NS_OK;
michael@0 1723 }
michael@0 1724
michael@0 1725 bool isDirectory;
michael@0 1726 rv = indexedDBDir->IsDirectory(&isDirectory);
michael@0 1727 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1728
michael@0 1729 if (!isDirectory) {
michael@0 1730 NS_WARNING("indexedDB entry is not a directory!");
michael@0 1731 return NS_OK;
michael@0 1732 }
michael@0 1733
michael@0 1734 nsCOMPtr<nsIFile> persistentStorageDir =
michael@0 1735 do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
michael@0 1736 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1737
michael@0 1738 rv = persistentStorageDir->InitWithPath(mPersistentStoragePath);
michael@0 1739 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1740
michael@0 1741 rv = persistentStorageDir->Exists(&exists);
michael@0 1742 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1743
michael@0 1744 if (exists) {
michael@0 1745 NS_WARNING("indexedDB directory shouldn't exist after the upgrade!");
michael@0 1746 return NS_OK;
michael@0 1747 }
michael@0 1748
michael@0 1749 nsCOMPtr<nsIFile> storageDir;
michael@0 1750 rv = persistentStorageDir->GetParent(getter_AddRefs(storageDir));
michael@0 1751 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1752
michael@0 1753 nsString persistentStorageName;
michael@0 1754 rv = persistentStorageDir->GetLeafName(persistentStorageName);
michael@0 1755 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1756
michael@0 1757 // MoveTo() is atomic if the move happens on the same volume which should
michael@0 1758 // be our case, so even if we crash in the middle of the operation nothing
michael@0 1759 // breaks next time we try to initialize.
michael@0 1760 // However there's a theoretical possibility that the indexedDB directory
michael@0 1761 // is on different volume, but it should be rare enough that we don't have
michael@0 1762 // to worry about it.
michael@0 1763 rv = indexedDBDir->MoveTo(storageDir, persistentStorageName);
michael@0 1764 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1765
michael@0 1766 mStorageAreaInitialized = true;
michael@0 1767
michael@0 1768 return NS_OK;
michael@0 1769 }
michael@0 1770
michael@0 1771 nsresult
michael@0 1772 QuotaManager::EnsureOriginIsInitialized(PersistenceType aPersistenceType,
michael@0 1773 const nsACString& aGroup,
michael@0 1774 const nsACString& aOrigin,
michael@0 1775 bool aTrackQuota,
michael@0 1776 nsIFile** aDirectory)
michael@0 1777 {
michael@0 1778 AssertIsOnIOThread();
michael@0 1779
michael@0 1780 nsresult rv = MaybeUpgradeIndexedDBDirectory();
michael@0 1781 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1782
michael@0 1783 // Get directory for this origin and persistence type.
michael@0 1784 nsCOMPtr<nsIFile> directory;
michael@0 1785 rv = GetDirectoryForOrigin(aPersistenceType, aOrigin,
michael@0 1786 getter_AddRefs(directory));
michael@0 1787 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1788
michael@0 1789 if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) {
michael@0 1790 if (mInitializedOrigins.Contains(aOrigin)) {
michael@0 1791 NS_ADDREF(*aDirectory = directory);
michael@0 1792 return NS_OK;
michael@0 1793 }
michael@0 1794
michael@0 1795 bool created;
michael@0 1796 rv = EnsureDirectory(directory, &created);
michael@0 1797 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1798
michael@0 1799 if (created) {
michael@0 1800 rv = CreateDirectoryUpgradeStamp(directory);
michael@0 1801 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1802 }
michael@0 1803
michael@0 1804 rv = InitializeOrigin(aPersistenceType, aGroup, aOrigin, aTrackQuota, 0,
michael@0 1805 directory);
michael@0 1806 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1807
michael@0 1808 mInitializedOrigins.AppendElement(aOrigin);
michael@0 1809
michael@0 1810 directory.forget(aDirectory);
michael@0 1811 return NS_OK;
michael@0 1812 }
michael@0 1813
michael@0 1814 NS_ASSERTION(aPersistenceType == PERSISTENCE_TYPE_TEMPORARY, "Huh?");
michael@0 1815 NS_ASSERTION(aTrackQuota, "Huh?");
michael@0 1816
michael@0 1817 if (!mTemporaryStorageInitialized) {
michael@0 1818 nsCOMPtr<nsIFile> parentDirectory;
michael@0 1819 rv = directory->GetParent(getter_AddRefs(parentDirectory));
michael@0 1820 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1821
michael@0 1822 bool created;
michael@0 1823 rv = EnsureDirectory(parentDirectory, &created);
michael@0 1824 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1825
michael@0 1826 nsCOMPtr<nsISimpleEnumerator> entries;
michael@0 1827 rv = parentDirectory->GetDirectoryEntries(getter_AddRefs(entries));
michael@0 1828 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1829
michael@0 1830 bool hasMore;
michael@0 1831 while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
michael@0 1832 nsCOMPtr<nsISupports> entry;
michael@0 1833 rv = entries->GetNext(getter_AddRefs(entry));
michael@0 1834 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1835
michael@0 1836 nsCOMPtr<nsIFile> childDirectory = do_QueryInterface(entry);
michael@0 1837 NS_ENSURE_TRUE(childDirectory, NS_NOINTERFACE);
michael@0 1838
michael@0 1839 bool isDirectory;
michael@0 1840 rv = childDirectory->IsDirectory(&isDirectory);
michael@0 1841 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1842 NS_ENSURE_TRUE(isDirectory, NS_ERROR_UNEXPECTED);
michael@0 1843
michael@0 1844 int64_t timestamp;
michael@0 1845 nsCString group;
michael@0 1846 nsCString origin;
michael@0 1847 rv = GetDirectoryMetadata(childDirectory, &timestamp, group, origin);
michael@0 1848 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1849
michael@0 1850 rv = InitializeOrigin(aPersistenceType, group, origin, aTrackQuota,
michael@0 1851 timestamp, childDirectory);
michael@0 1852 if (NS_FAILED(rv)) {
michael@0 1853 NS_WARNING("Failed to initialize origin!");
michael@0 1854
michael@0 1855 // We have to cleanup partially initialized quota for temporary storage.
michael@0 1856 RemoveQuotaForPersistenceType(aPersistenceType);
michael@0 1857
michael@0 1858 return rv;
michael@0 1859 }
michael@0 1860 }
michael@0 1861
michael@0 1862 if (gFixedLimitKB >= 0) {
michael@0 1863 mTemporaryStorageLimit = gFixedLimitKB * 1024;
michael@0 1864 }
michael@0 1865 else {
michael@0 1866 rv = GetTemporaryStorageLimit(parentDirectory, mTemporaryStorageUsage,
michael@0 1867 &mTemporaryStorageLimit);
michael@0 1868 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1869 }
michael@0 1870
michael@0 1871 mTemporaryStorageInitialized = true;
michael@0 1872
michael@0 1873 CheckTemporaryStorageLimits();
michael@0 1874 }
michael@0 1875
michael@0 1876 bool created;
michael@0 1877 rv = EnsureDirectory(directory, &created);
michael@0 1878 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1879
michael@0 1880 if (created) {
michael@0 1881 int64_t timestamp = PR_Now();
michael@0 1882
michael@0 1883 rv = CreateDirectoryMetadata(directory, timestamp, aGroup, aOrigin);
michael@0 1884 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1885
michael@0 1886 rv = InitializeOrigin(aPersistenceType, aGroup, aOrigin, aTrackQuota,
michael@0 1887 timestamp, directory);
michael@0 1888 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1889 }
michael@0 1890
michael@0 1891 directory.forget(aDirectory);
michael@0 1892 return NS_OK;
michael@0 1893 }
michael@0 1894
michael@0 1895 void
michael@0 1896 QuotaManager::OriginClearCompleted(
michael@0 1897 PersistenceType aPersistenceType,
michael@0 1898 const OriginOrPatternString& aOriginOrPattern)
michael@0 1899 {
michael@0 1900 AssertIsOnIOThread();
michael@0 1901
michael@0 1902 if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) {
michael@0 1903 if (aOriginOrPattern.IsOrigin()) {
michael@0 1904 mInitializedOrigins.RemoveElement(aOriginOrPattern);
michael@0 1905 }
michael@0 1906 else {
michael@0 1907 for (uint32_t index = mInitializedOrigins.Length(); index > 0; index--) {
michael@0 1908 if (PatternMatchesOrigin(aOriginOrPattern,
michael@0 1909 mInitializedOrigins[index - 1])) {
michael@0 1910 mInitializedOrigins.RemoveElementAt(index - 1);
michael@0 1911 }
michael@0 1912 }
michael@0 1913 }
michael@0 1914 }
michael@0 1915
michael@0 1916 for (uint32_t index = 0; index < Client::TYPE_MAX; index++) {
michael@0 1917 mClients[index]->OnOriginClearCompleted(aPersistenceType, aOriginOrPattern);
michael@0 1918 }
michael@0 1919 }
michael@0 1920
michael@0 1921 void
michael@0 1922 QuotaManager::ResetOrClearCompleted()
michael@0 1923 {
michael@0 1924 AssertIsOnIOThread();
michael@0 1925
michael@0 1926 mInitializedOrigins.Clear();
michael@0 1927 mTemporaryStorageInitialized = false;
michael@0 1928
michael@0 1929 ReleaseIOThreadObjects();
michael@0 1930 }
michael@0 1931
michael@0 1932 already_AddRefed<mozilla::dom::quota::Client>
michael@0 1933 QuotaManager::GetClient(Client::Type aClientType)
michael@0 1934 {
michael@0 1935 nsRefPtr<Client> client = mClients.SafeElementAt(aClientType);
michael@0 1936 return client.forget();
michael@0 1937 }
michael@0 1938
michael@0 1939 uint64_t
michael@0 1940 QuotaManager::GetGroupLimit() const
michael@0 1941 {
michael@0 1942 MOZ_ASSERT(mTemporaryStorageInitialized);
michael@0 1943
michael@0 1944 // To avoid one group evicting all the rest, limit the amount any one group
michael@0 1945 // can use to 20%. To prevent individual sites from using exorbitant amounts
michael@0 1946 // of storage where there is a lot of free space, cap the group limit to 2GB.
michael@0 1947 uint64_t x = std::min<uint64_t>(mTemporaryStorageLimit * .20, 2 GB);
michael@0 1948
michael@0 1949 // In low-storage situations, make an exception (while not exceeding the total
michael@0 1950 // storage limit).
michael@0 1951 return std::min<uint64_t>(mTemporaryStorageLimit,
michael@0 1952 std::max<uint64_t>(x, 10 MB));
michael@0 1953 }
michael@0 1954
michael@0 1955 // static
michael@0 1956 uint32_t
michael@0 1957 QuotaManager::GetStorageQuotaMB()
michael@0 1958 {
michael@0 1959 return uint32_t(std::max(gStorageQuotaMB, 0));
michael@0 1960 }
michael@0 1961
michael@0 1962 // static
michael@0 1963 void
michael@0 1964 QuotaManager::GetStorageId(PersistenceType aPersistenceType,
michael@0 1965 const nsACString& aOrigin,
michael@0 1966 Client::Type aClientType,
michael@0 1967 const nsAString& aName,
michael@0 1968 nsACString& aDatabaseId)
michael@0 1969 {
michael@0 1970 nsAutoCString str;
michael@0 1971 str.AppendInt(aPersistenceType);
michael@0 1972 str.Append('*');
michael@0 1973 str.Append(aOrigin);
michael@0 1974 str.Append('*');
michael@0 1975 str.AppendInt(aClientType);
michael@0 1976 str.Append('*');
michael@0 1977 str.Append(NS_ConvertUTF16toUTF8(aName));
michael@0 1978
michael@0 1979 aDatabaseId = str;
michael@0 1980 }
michael@0 1981
michael@0 1982 // static
michael@0 1983 nsresult
michael@0 1984 QuotaManager::GetInfoFromURI(nsIURI* aURI,
michael@0 1985 uint32_t aAppId,
michael@0 1986 bool aInMozBrowser,
michael@0 1987 nsACString* aGroup,
michael@0 1988 nsACString* aASCIIOrigin,
michael@0 1989 StoragePrivilege* aPrivilege,
michael@0 1990 PersistenceType* aDefaultPersistenceType)
michael@0 1991 {
michael@0 1992 NS_ASSERTION(aURI, "Null uri!");
michael@0 1993
michael@0 1994 nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
michael@0 1995 NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE);
michael@0 1996
michael@0 1997 nsCOMPtr<nsIPrincipal> principal;
michael@0 1998 nsresult rv = secMan->GetAppCodebasePrincipal(aURI, aAppId, aInMozBrowser,
michael@0 1999 getter_AddRefs(principal));
michael@0 2000 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2001
michael@0 2002 rv = GetInfoFromPrincipal(principal, aGroup, aASCIIOrigin, aPrivilege,
michael@0 2003 aDefaultPersistenceType);
michael@0 2004 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2005
michael@0 2006 return NS_OK;
michael@0 2007 }
michael@0 2008
michael@0 2009 // static
michael@0 2010 nsresult
michael@0 2011 QuotaManager::GetInfoFromPrincipal(nsIPrincipal* aPrincipal,
michael@0 2012 nsACString* aGroup,
michael@0 2013 nsACString* aASCIIOrigin,
michael@0 2014 StoragePrivilege* aPrivilege,
michael@0 2015 PersistenceType* aDefaultPersistenceType)
michael@0 2016 {
michael@0 2017 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 2018 NS_ASSERTION(aPrincipal, "Don't hand me a null principal!");
michael@0 2019
michael@0 2020 if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
michael@0 2021 GetInfoForChrome(aGroup, aASCIIOrigin, aPrivilege, aDefaultPersistenceType);
michael@0 2022 return NS_OK;
michael@0 2023 }
michael@0 2024
michael@0 2025 bool isNullPrincipal;
michael@0 2026 nsresult rv = aPrincipal->GetIsNullPrincipal(&isNullPrincipal);
michael@0 2027 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2028
michael@0 2029 if (isNullPrincipal) {
michael@0 2030 NS_WARNING("IndexedDB not supported from this principal!");
michael@0 2031 return NS_ERROR_FAILURE;
michael@0 2032 }
michael@0 2033
michael@0 2034 nsCString origin;
michael@0 2035 rv = aPrincipal->GetOrigin(getter_Copies(origin));
michael@0 2036 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2037
michael@0 2038 if (origin.EqualsLiteral("chrome")) {
michael@0 2039 NS_WARNING("Non-chrome principal can't use chrome origin!");
michael@0 2040 return NS_ERROR_FAILURE;
michael@0 2041 }
michael@0 2042
michael@0 2043 nsCString jarPrefix;
michael@0 2044 if (aGroup || aASCIIOrigin) {
michael@0 2045 rv = aPrincipal->GetJarPrefix(jarPrefix);
michael@0 2046 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2047 }
michael@0 2048
michael@0 2049 if (aGroup) {
michael@0 2050 nsCString baseDomain;
michael@0 2051 rv = aPrincipal->GetBaseDomain(baseDomain);
michael@0 2052 if (NS_FAILED(rv)) {
michael@0 2053 // A hack for JetPack.
michael@0 2054
michael@0 2055 nsCOMPtr<nsIURI> uri;
michael@0 2056 rv = aPrincipal->GetURI(getter_AddRefs(uri));
michael@0 2057 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2058
michael@0 2059 bool isIndexedDBURI = false;
michael@0 2060 rv = uri->SchemeIs("indexedDB", &isIndexedDBURI);
michael@0 2061 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2062
michael@0 2063 if (isIndexedDBURI) {
michael@0 2064 rv = NS_OK;
michael@0 2065 }
michael@0 2066 }
michael@0 2067 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2068
michael@0 2069 if (baseDomain.IsEmpty()) {
michael@0 2070 aGroup->Assign(jarPrefix + origin);
michael@0 2071 }
michael@0 2072 else {
michael@0 2073 aGroup->Assign(jarPrefix + baseDomain);
michael@0 2074 }
michael@0 2075 }
michael@0 2076
michael@0 2077 if (aASCIIOrigin) {
michael@0 2078 aASCIIOrigin->Assign(jarPrefix + origin);
michael@0 2079 }
michael@0 2080
michael@0 2081 if (aPrivilege) {
michael@0 2082 *aPrivilege = Content;
michael@0 2083 }
michael@0 2084
michael@0 2085 if (aDefaultPersistenceType) {
michael@0 2086 *aDefaultPersistenceType = PERSISTENCE_TYPE_PERSISTENT;
michael@0 2087 }
michael@0 2088
michael@0 2089 return NS_OK;
michael@0 2090 }
michael@0 2091
michael@0 2092 // static
michael@0 2093 nsresult
michael@0 2094 QuotaManager::GetInfoFromWindow(nsPIDOMWindow* aWindow,
michael@0 2095 nsACString* aGroup,
michael@0 2096 nsACString* aASCIIOrigin,
michael@0 2097 StoragePrivilege* aPrivilege,
michael@0 2098 PersistenceType* aDefaultPersistenceType)
michael@0 2099 {
michael@0 2100 NS_ASSERTION(NS_IsMainThread(),
michael@0 2101 "We're about to touch a window off the main thread!");
michael@0 2102 NS_ASSERTION(aWindow, "Don't hand me a null window!");
michael@0 2103
michael@0 2104 nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aWindow);
michael@0 2105 NS_ENSURE_TRUE(sop, NS_ERROR_FAILURE);
michael@0 2106
michael@0 2107 nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
michael@0 2108 NS_ENSURE_TRUE(principal, NS_ERROR_FAILURE);
michael@0 2109
michael@0 2110 nsresult rv = GetInfoFromPrincipal(principal, aGroup, aASCIIOrigin,
michael@0 2111 aPrivilege, aDefaultPersistenceType);
michael@0 2112 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2113
michael@0 2114 return NS_OK;
michael@0 2115 }
michael@0 2116
michael@0 2117 // static
michael@0 2118 void
michael@0 2119 QuotaManager::GetInfoForChrome(nsACString* aGroup,
michael@0 2120 nsACString* aASCIIOrigin,
michael@0 2121 StoragePrivilege* aPrivilege,
michael@0 2122 PersistenceType* aDefaultPersistenceType)
michael@0 2123 {
michael@0 2124 NS_ASSERTION(nsContentUtils::IsCallerChrome(), "Only for chrome!");
michael@0 2125
michael@0 2126 static const char kChromeOrigin[] = "chrome";
michael@0 2127
michael@0 2128 if (aGroup) {
michael@0 2129 aGroup->AssignLiteral(kChromeOrigin);
michael@0 2130 }
michael@0 2131 if (aASCIIOrigin) {
michael@0 2132 aASCIIOrigin->AssignLiteral(kChromeOrigin);
michael@0 2133 }
michael@0 2134 if (aPrivilege) {
michael@0 2135 *aPrivilege = Chrome;
michael@0 2136 }
michael@0 2137 if (aDefaultPersistenceType) {
michael@0 2138 *aDefaultPersistenceType = PERSISTENCE_TYPE_PERSISTENT;
michael@0 2139 }
michael@0 2140 }
michael@0 2141
michael@0 2142 NS_IMPL_ISUPPORTS(QuotaManager, nsIQuotaManager, nsIObserver)
michael@0 2143
michael@0 2144 NS_IMETHODIMP
michael@0 2145 QuotaManager::GetUsageForURI(nsIURI* aURI,
michael@0 2146 nsIUsageCallback* aCallback,
michael@0 2147 uint32_t aAppId,
michael@0 2148 bool aInMozBrowserOnly,
michael@0 2149 uint8_t aOptionalArgCount,
michael@0 2150 nsIQuotaRequest** _retval)
michael@0 2151 {
michael@0 2152 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 2153
michael@0 2154 NS_ENSURE_ARG_POINTER(aURI);
michael@0 2155 NS_ENSURE_ARG_POINTER(aCallback);
michael@0 2156
michael@0 2157 // This only works from the main process.
michael@0 2158 NS_ENSURE_TRUE(IsMainProcess(), NS_ERROR_NOT_AVAILABLE);
michael@0 2159
michael@0 2160 if (!aOptionalArgCount) {
michael@0 2161 aAppId = nsIScriptSecurityManager::NO_APP_ID;
michael@0 2162 }
michael@0 2163
michael@0 2164 // Figure out which origin we're dealing with.
michael@0 2165 nsCString group;
michael@0 2166 nsCString origin;
michael@0 2167 nsresult rv = GetInfoFromURI(aURI, aAppId, aInMozBrowserOnly, &group, &origin,
michael@0 2168 nullptr, nullptr);
michael@0 2169 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2170
michael@0 2171 OriginOrPatternString oops = OriginOrPatternString::FromOrigin(origin);
michael@0 2172
michael@0 2173 nsRefPtr<AsyncUsageRunnable> runnable =
michael@0 2174 new AsyncUsageRunnable(aAppId, aInMozBrowserOnly, group, oops, aURI,
michael@0 2175 aCallback);
michael@0 2176
michael@0 2177 // Put the computation runnable in the queue.
michael@0 2178 rv = WaitForOpenAllowed(oops, Nullable<PersistenceType>(), EmptyCString(),
michael@0 2179 runnable);
michael@0 2180 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2181
michael@0 2182 runnable->AdvanceState();
michael@0 2183
michael@0 2184 runnable.forget(_retval);
michael@0 2185 return NS_OK;
michael@0 2186 }
michael@0 2187
michael@0 2188 NS_IMETHODIMP
michael@0 2189 QuotaManager::Clear()
michael@0 2190 {
michael@0 2191 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 2192
michael@0 2193 if (!gTestingEnabled) {
michael@0 2194 NS_WARNING("Testing features are not enabled!");
michael@0 2195 return NS_OK;
michael@0 2196 }
michael@0 2197
michael@0 2198 OriginOrPatternString oops = OriginOrPatternString::FromNull();
michael@0 2199
michael@0 2200 nsRefPtr<ResetOrClearRunnable> runnable = new ResetOrClearRunnable(true);
michael@0 2201
michael@0 2202 // Put the clear runnable in the queue.
michael@0 2203 nsresult rv =
michael@0 2204 WaitForOpenAllowed(oops, Nullable<PersistenceType>(), EmptyCString(),
michael@0 2205 runnable);
michael@0 2206 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2207
michael@0 2208 runnable->AdvanceState();
michael@0 2209
michael@0 2210 // Give the runnable some help by invalidating any storages in the way.
michael@0 2211 StorageMatcher<nsAutoTArray<nsIOfflineStorage*, 20> > matches;
michael@0 2212 matches.Find(mLiveStorages);
michael@0 2213
michael@0 2214 for (uint32_t index = 0; index < matches.Length(); index++) {
michael@0 2215 // We need to grab references to any live storages here to prevent them
michael@0 2216 // from dying while we invalidate them.
michael@0 2217 nsCOMPtr<nsIOfflineStorage> storage = matches[index];
michael@0 2218 storage->Invalidate();
michael@0 2219 }
michael@0 2220
michael@0 2221 // After everything has been invalidated the helper should be dispatched to
michael@0 2222 // the end of the event queue.
michael@0 2223 return NS_OK;
michael@0 2224 }
michael@0 2225
michael@0 2226 NS_IMETHODIMP
michael@0 2227 QuotaManager::ClearStoragesForURI(nsIURI* aURI,
michael@0 2228 uint32_t aAppId,
michael@0 2229 bool aInMozBrowserOnly,
michael@0 2230 const nsACString& aPersistenceType,
michael@0 2231 uint8_t aOptionalArgCount)
michael@0 2232 {
michael@0 2233 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 2234
michael@0 2235 NS_ENSURE_ARG_POINTER(aURI);
michael@0 2236
michael@0 2237 // This only works from the main process.
michael@0 2238 NS_ENSURE_TRUE(IsMainProcess(), NS_ERROR_NOT_AVAILABLE);
michael@0 2239
michael@0 2240 if (!aOptionalArgCount) {
michael@0 2241 aAppId = nsIScriptSecurityManager::NO_APP_ID;
michael@0 2242 }
michael@0 2243
michael@0 2244 // Figure out which origin we're dealing with.
michael@0 2245 nsCString origin;
michael@0 2246 nsresult rv = GetInfoFromURI(aURI, aAppId, aInMozBrowserOnly, nullptr, &origin,
michael@0 2247 nullptr, nullptr);
michael@0 2248 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2249
michael@0 2250 nsAutoCString pattern;
michael@0 2251 GetOriginPatternString(aAppId, aInMozBrowserOnly, origin, pattern);
michael@0 2252
michael@0 2253 Nullable<PersistenceType> persistenceType;
michael@0 2254 rv = NullablePersistenceTypeFromText(aPersistenceType, &persistenceType);
michael@0 2255 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2256
michael@0 2257 // If there is a pending or running clear operation for this origin, return
michael@0 2258 // immediately.
michael@0 2259 if (IsClearOriginPending(pattern, persistenceType)) {
michael@0 2260 return NS_OK;
michael@0 2261 }
michael@0 2262
michael@0 2263 OriginOrPatternString oops = OriginOrPatternString::FromPattern(pattern);
michael@0 2264
michael@0 2265 // Queue up the origin clear runnable.
michael@0 2266 nsRefPtr<OriginClearRunnable> runnable =
michael@0 2267 new OriginClearRunnable(oops, persistenceType);
michael@0 2268
michael@0 2269 rv = WaitForOpenAllowed(oops, persistenceType, EmptyCString(), runnable);
michael@0 2270 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2271
michael@0 2272 runnable->AdvanceState();
michael@0 2273
michael@0 2274 // Give the runnable some help by invalidating any storages in the way.
michael@0 2275 StorageMatcher<nsAutoTArray<nsIOfflineStorage*, 20> > matches;
michael@0 2276 matches.Find(mLiveStorages, pattern);
michael@0 2277
michael@0 2278 for (uint32_t index = 0; index < matches.Length(); index++) {
michael@0 2279 if (persistenceType.IsNull() ||
michael@0 2280 matches[index]->Type() == persistenceType.Value()) {
michael@0 2281 // We need to grab references to any live storages here to prevent them
michael@0 2282 // from dying while we invalidate them.
michael@0 2283 nsCOMPtr<nsIOfflineStorage> storage = matches[index];
michael@0 2284 storage->Invalidate();
michael@0 2285 }
michael@0 2286 }
michael@0 2287
michael@0 2288 // After everything has been invalidated the helper should be dispatched to
michael@0 2289 // the end of the event queue.
michael@0 2290 return NS_OK;
michael@0 2291 }
michael@0 2292
michael@0 2293 NS_IMETHODIMP
michael@0 2294 QuotaManager::Reset()
michael@0 2295 {
michael@0 2296 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 2297
michael@0 2298 if (!gTestingEnabled) {
michael@0 2299 NS_WARNING("Testing features are not enabled!");
michael@0 2300 return NS_OK;
michael@0 2301 }
michael@0 2302
michael@0 2303 OriginOrPatternString oops = OriginOrPatternString::FromNull();
michael@0 2304
michael@0 2305 nsRefPtr<ResetOrClearRunnable> runnable = new ResetOrClearRunnable(false);
michael@0 2306
michael@0 2307 // Put the reset runnable in the queue.
michael@0 2308 nsresult rv =
michael@0 2309 WaitForOpenAllowed(oops, Nullable<PersistenceType>(), EmptyCString(),
michael@0 2310 runnable);
michael@0 2311 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2312
michael@0 2313 runnable->AdvanceState();
michael@0 2314
michael@0 2315 // Give the runnable some help by invalidating any storages in the way.
michael@0 2316 StorageMatcher<nsAutoTArray<nsIOfflineStorage*, 20> > matches;
michael@0 2317 matches.Find(mLiveStorages);
michael@0 2318
michael@0 2319 for (uint32_t index = 0; index < matches.Length(); index++) {
michael@0 2320 // We need to grab references to any live storages here to prevent them
michael@0 2321 // from dying while we invalidate them.
michael@0 2322 nsCOMPtr<nsIOfflineStorage> storage = matches[index];
michael@0 2323 storage->Invalidate();
michael@0 2324 }
michael@0 2325
michael@0 2326 // After everything has been invalidated the helper should be dispatched to
michael@0 2327 // the end of the event queue.
michael@0 2328 return NS_OK;
michael@0 2329 }
michael@0 2330
michael@0 2331 NS_IMETHODIMP
michael@0 2332 QuotaManager::Observe(nsISupports* aSubject,
michael@0 2333 const char* aTopic,
michael@0 2334 const char16_t* aData)
michael@0 2335 {
michael@0 2336 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 2337
michael@0 2338 if (!strcmp(aTopic, PROFILE_BEFORE_CHANGE_OBSERVER_ID)) {
michael@0 2339 // Setting this flag prevents the service from being recreated and prevents
michael@0 2340 // further storagess from being created.
michael@0 2341 if (gShutdown.exchange(true)) {
michael@0 2342 NS_ERROR("Shutdown more than once?!");
michael@0 2343 }
michael@0 2344
michael@0 2345 if (IsMainProcess()) {
michael@0 2346 FileService* service = FileService::Get();
michael@0 2347 if (service) {
michael@0 2348 // This should only wait for storages registered in this manager
michael@0 2349 // to complete. Other storages may still have running locked files.
michael@0 2350 // If the necko service (thread pool) gets the shutdown notification
michael@0 2351 // first then the sync loop won't be processed at all, otherwise it will
michael@0 2352 // lock the main thread until all storages registered in this manager
michael@0 2353 // are finished.
michael@0 2354
michael@0 2355 nsTArray<uint32_t> indexes;
michael@0 2356 for (uint32_t index = 0; index < Client::TYPE_MAX; index++) {
michael@0 2357 if (mClients[index]->IsFileServiceUtilized()) {
michael@0 2358 indexes.AppendElement(index);
michael@0 2359 }
michael@0 2360 }
michael@0 2361
michael@0 2362 StorageMatcher<nsTArray<nsCOMPtr<nsIFileStorage> > > liveStorages;
michael@0 2363 liveStorages.Find(mLiveStorages, &indexes);
michael@0 2364
michael@0 2365 if (!liveStorages.IsEmpty()) {
michael@0 2366 nsRefPtr<WaitForLockedFilesToFinishRunnable> runnable =
michael@0 2367 new WaitForLockedFilesToFinishRunnable();
michael@0 2368
michael@0 2369 service->WaitForStoragesToComplete(liveStorages, runnable);
michael@0 2370
michael@0 2371 nsIThread* thread = NS_GetCurrentThread();
michael@0 2372 while (runnable->IsBusy()) {
michael@0 2373 if (!NS_ProcessNextEvent(thread)) {
michael@0 2374 NS_ERROR("Failed to process next event!");
michael@0 2375 break;
michael@0 2376 }
michael@0 2377 }
michael@0 2378 }
michael@0 2379 }
michael@0 2380
michael@0 2381 // Kick off the shutdown timer.
michael@0 2382 if (NS_FAILED(mShutdownTimer->Init(this, DEFAULT_SHUTDOWN_TIMER_MS,
michael@0 2383 nsITimer::TYPE_ONE_SHOT))) {
michael@0 2384 NS_WARNING("Failed to initialize shutdown timer!");
michael@0 2385 }
michael@0 2386
michael@0 2387 // Each client will spin the event loop while we wait on all the threads
michael@0 2388 // to close. Our timer may fire during that loop.
michael@0 2389 for (uint32_t index = 0; index < Client::TYPE_MAX; index++) {
michael@0 2390 mClients[index]->ShutdownTransactionService();
michael@0 2391 }
michael@0 2392
michael@0 2393 // Cancel the timer regardless of whether it actually fired.
michael@0 2394 if (NS_FAILED(mShutdownTimer->Cancel())) {
michael@0 2395 NS_WARNING("Failed to cancel shutdown timer!");
michael@0 2396 }
michael@0 2397
michael@0 2398 // Give clients a chance to cleanup IO thread only objects.
michael@0 2399 nsCOMPtr<nsIRunnable> runnable =
michael@0 2400 NS_NewRunnableMethod(this, &QuotaManager::ReleaseIOThreadObjects);
michael@0 2401 if (!runnable) {
michael@0 2402 NS_WARNING("Failed to create runnable!");
michael@0 2403 }
michael@0 2404
michael@0 2405 if (NS_FAILED(mIOThread->Dispatch(runnable, NS_DISPATCH_NORMAL))) {
michael@0 2406 NS_WARNING("Failed to dispatch runnable!");
michael@0 2407 }
michael@0 2408
michael@0 2409 // Make sure to join with our IO thread.
michael@0 2410 if (NS_FAILED(mIOThread->Shutdown())) {
michael@0 2411 NS_WARNING("Failed to shutdown IO thread!");
michael@0 2412 }
michael@0 2413 }
michael@0 2414
michael@0 2415 return NS_OK;
michael@0 2416 }
michael@0 2417
michael@0 2418 if (!strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC)) {
michael@0 2419 NS_ASSERTION(IsMainProcess(), "Should only happen in the main process!");
michael@0 2420
michael@0 2421 NS_WARNING("Some storage operations are taking longer than expected "
michael@0 2422 "during shutdown and will be aborted!");
michael@0 2423
michael@0 2424 // Grab all live storages, for all origins.
michael@0 2425 StorageMatcher<nsAutoTArray<nsIOfflineStorage*, 50> > liveStorages;
michael@0 2426 liveStorages.Find(mLiveStorages);
michael@0 2427
michael@0 2428 // Invalidate them all.
michael@0 2429 if (!liveStorages.IsEmpty()) {
michael@0 2430 uint32_t count = liveStorages.Length();
michael@0 2431 for (uint32_t index = 0; index < count; index++) {
michael@0 2432 liveStorages[index]->Invalidate();
michael@0 2433 }
michael@0 2434 }
michael@0 2435
michael@0 2436 return NS_OK;
michael@0 2437 }
michael@0 2438
michael@0 2439 if (!strcmp(aTopic, TOPIC_WEB_APP_CLEAR_DATA)) {
michael@0 2440 nsCOMPtr<mozIApplicationClearPrivateDataParams> params =
michael@0 2441 do_QueryInterface(aSubject);
michael@0 2442 NS_ENSURE_TRUE(params, NS_ERROR_UNEXPECTED);
michael@0 2443
michael@0 2444 uint32_t appId;
michael@0 2445 nsresult rv = params->GetAppId(&appId);
michael@0 2446 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2447
michael@0 2448 bool browserOnly;
michael@0 2449 rv = params->GetBrowserOnly(&browserOnly);
michael@0 2450 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2451
michael@0 2452 rv = ClearStoragesForApp(appId, browserOnly);
michael@0 2453 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2454
michael@0 2455 return NS_OK;
michael@0 2456 }
michael@0 2457
michael@0 2458 NS_NOTREACHED("Unknown topic!");
michael@0 2459 return NS_ERROR_UNEXPECTED;
michael@0 2460 }
michael@0 2461
michael@0 2462 void
michael@0 2463 QuotaManager::SetCurrentWindowInternal(nsPIDOMWindow* aWindow)
michael@0 2464 {
michael@0 2465 NS_ASSERTION(mCurrentWindowIndex != BAD_TLS_INDEX,
michael@0 2466 "Should have a valid TLS storage index!");
michael@0 2467
michael@0 2468 if (aWindow) {
michael@0 2469 NS_ASSERTION(!PR_GetThreadPrivate(mCurrentWindowIndex),
michael@0 2470 "Somebody forgot to clear the current window!");
michael@0 2471 PR_SetThreadPrivate(mCurrentWindowIndex, aWindow);
michael@0 2472 }
michael@0 2473 else {
michael@0 2474 // We cannot assert PR_GetThreadPrivate(mCurrentWindowIndex) here because
michael@0 2475 // there are some cases where we did not already have a window.
michael@0 2476 PR_SetThreadPrivate(mCurrentWindowIndex, nullptr);
michael@0 2477 }
michael@0 2478 }
michael@0 2479
michael@0 2480 void
michael@0 2481 QuotaManager::CancelPromptsForWindowInternal(nsPIDOMWindow* aWindow)
michael@0 2482 {
michael@0 2483 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 2484
michael@0 2485 nsRefPtr<CheckQuotaHelper> helper;
michael@0 2486
michael@0 2487 MutexAutoLock autoLock(mQuotaMutex);
michael@0 2488
michael@0 2489 if (mCheckQuotaHelpers.Get(aWindow, getter_AddRefs(helper))) {
michael@0 2490 helper->Cancel();
michael@0 2491 }
michael@0 2492 }
michael@0 2493
michael@0 2494 bool
michael@0 2495 QuotaManager::LockedQuotaIsLifted()
michael@0 2496 {
michael@0 2497 mQuotaMutex.AssertCurrentThreadOwns();
michael@0 2498
michael@0 2499 NS_ASSERTION(mCurrentWindowIndex != BAD_TLS_INDEX,
michael@0 2500 "Should have a valid TLS storage index!");
michael@0 2501
michael@0 2502 nsPIDOMWindow* window =
michael@0 2503 static_cast<nsPIDOMWindow*>(PR_GetThreadPrivate(mCurrentWindowIndex));
michael@0 2504
michael@0 2505 // Quota is not enforced in chrome contexts (e.g. for components and JSMs)
michael@0 2506 // so we must have a window here.
michael@0 2507 NS_ASSERTION(window, "Why don't we have a Window here?");
michael@0 2508
michael@0 2509 bool createdHelper = false;
michael@0 2510
michael@0 2511 nsRefPtr<CheckQuotaHelper> helper;
michael@0 2512 if (!mCheckQuotaHelpers.Get(window, getter_AddRefs(helper))) {
michael@0 2513 helper = new CheckQuotaHelper(window, mQuotaMutex);
michael@0 2514 createdHelper = true;
michael@0 2515
michael@0 2516 mCheckQuotaHelpers.Put(window, helper);
michael@0 2517
michael@0 2518 // Unlock while calling out to XPCOM (code behind the dispatch method needs
michael@0 2519 // to acquire its own lock which can potentially lead to a deadlock and it
michael@0 2520 // also calls an observer that can do various stuff like IO, so it's better
michael@0 2521 // to not hold our mutex while that happens).
michael@0 2522 {
michael@0 2523 MutexAutoUnlock autoUnlock(mQuotaMutex);
michael@0 2524
michael@0 2525 nsresult rv = NS_DispatchToMainThread(helper);
michael@0 2526 NS_ENSURE_SUCCESS(rv, false);
michael@0 2527 }
michael@0 2528
michael@0 2529 // Relocked. If any other threads hit the quota limit on the same Window,
michael@0 2530 // they are using the helper we created here and are now blocking in
michael@0 2531 // PromptAndReturnQuotaDisabled.
michael@0 2532 }
michael@0 2533
michael@0 2534 bool result = helper->PromptAndReturnQuotaIsDisabled();
michael@0 2535
michael@0 2536 // If this thread created the helper and added it to the hash, this thread
michael@0 2537 // must remove it.
michael@0 2538 if (createdHelper) {
michael@0 2539 mCheckQuotaHelpers.Remove(window);
michael@0 2540 }
michael@0 2541
michael@0 2542 return result;
michael@0 2543 }
michael@0 2544
michael@0 2545 uint64_t
michael@0 2546 QuotaManager::LockedCollectOriginsForEviction(
michael@0 2547 uint64_t aMinSizeToBeFreed,
michael@0 2548 nsTArray<OriginInfo*>& aOriginInfos)
michael@0 2549 {
michael@0 2550 mQuotaMutex.AssertCurrentThreadOwns();
michael@0 2551
michael@0 2552 nsRefPtr<CollectOriginsHelper> helper =
michael@0 2553 new CollectOriginsHelper(mQuotaMutex, aMinSizeToBeFreed);
michael@0 2554
michael@0 2555 // Unlock while calling out to XPCOM (see the detailed comment in
michael@0 2556 // LockedQuotaIsLifted)
michael@0 2557 {
michael@0 2558 MutexAutoUnlock autoUnlock(mQuotaMutex);
michael@0 2559
michael@0 2560 if (NS_FAILED(NS_DispatchToMainThread(helper))) {
michael@0 2561 NS_WARNING("Failed to dispatch to the main thread!");
michael@0 2562 }
michael@0 2563 }
michael@0 2564
michael@0 2565 return helper->BlockAndReturnOriginsForEviction(aOriginInfos);
michael@0 2566 }
michael@0 2567
michael@0 2568 void
michael@0 2569 QuotaManager::LockedRemoveQuotaForOrigin(PersistenceType aPersistenceType,
michael@0 2570 const nsACString& aGroup,
michael@0 2571 const nsACString& aOrigin)
michael@0 2572 {
michael@0 2573 mQuotaMutex.AssertCurrentThreadOwns();
michael@0 2574
michael@0 2575 GroupInfoPair* pair;
michael@0 2576 mGroupInfoPairs.Get(aGroup, &pair);
michael@0 2577
michael@0 2578 if (!pair) {
michael@0 2579 return;
michael@0 2580 }
michael@0 2581
michael@0 2582 nsRefPtr<GroupInfo> groupInfo = pair->LockedGetGroupInfo(aPersistenceType);
michael@0 2583 if (groupInfo) {
michael@0 2584 groupInfo->LockedRemoveOriginInfo(aOrigin);
michael@0 2585
michael@0 2586 if (!groupInfo->LockedHasOriginInfos()) {
michael@0 2587 pair->LockedClearGroupInfo(aPersistenceType);
michael@0 2588
michael@0 2589 if (!pair->LockedHasGroupInfos()) {
michael@0 2590 mGroupInfoPairs.Remove(aGroup);
michael@0 2591 }
michael@0 2592 }
michael@0 2593 }
michael@0 2594 }
michael@0 2595
michael@0 2596 nsresult
michael@0 2597 QuotaManager::AcquireExclusiveAccess(const nsACString& aPattern,
michael@0 2598 Nullable<PersistenceType> aPersistenceType,
michael@0 2599 nsIOfflineStorage* aStorage,
michael@0 2600 AcquireListener* aListener,
michael@0 2601 WaitingOnStoragesCallback aCallback,
michael@0 2602 void* aClosure)
michael@0 2603 {
michael@0 2604 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 2605 NS_ASSERTION(aListener, "Need a listener!");
michael@0 2606
michael@0 2607 // Find the right SynchronizedOp.
michael@0 2608 SynchronizedOp* op =
michael@0 2609 FindSynchronizedOp(aPattern, aPersistenceType,
michael@0 2610 aStorage ? aStorage->Id() : EmptyCString());
michael@0 2611
michael@0 2612 NS_ASSERTION(op, "We didn't find a SynchronizedOp?");
michael@0 2613 NS_ASSERTION(!op->mListener, "SynchronizedOp already has a listener?!?");
michael@0 2614
michael@0 2615 nsTArray<nsCOMPtr<nsIOfflineStorage> > liveStorages;
michael@0 2616
michael@0 2617 if (aStorage) {
michael@0 2618 // We need to wait for the storages to go away.
michael@0 2619 // Hold on to all storage objects that represent the same storage file
michael@0 2620 // (except the one that is requesting this version change).
michael@0 2621
michael@0 2622 Client::Type clientType = aStorage->GetClient()->GetType();
michael@0 2623
michael@0 2624 StorageMatcher<nsAutoTArray<nsIOfflineStorage*, 20> > matches;
michael@0 2625 matches.Find(mLiveStorages, aPattern, clientType);
michael@0 2626
michael@0 2627 if (!matches.IsEmpty()) {
michael@0 2628 // Grab all storages that are not yet closed but whose storage id match
michael@0 2629 // the one we're looking for.
michael@0 2630 for (uint32_t index = 0; index < matches.Length(); index++) {
michael@0 2631 nsIOfflineStorage*& storage = matches[index];
michael@0 2632 if (!storage->IsClosed() &&
michael@0 2633 storage != aStorage &&
michael@0 2634 storage->Id() == aStorage->Id()) {
michael@0 2635 liveStorages.AppendElement(storage);
michael@0 2636 }
michael@0 2637 }
michael@0 2638 }
michael@0 2639
michael@0 2640 if (!liveStorages.IsEmpty()) {
michael@0 2641 NS_ASSERTION(op->mStorages[clientType].IsEmpty(),
michael@0 2642 "How do we already have storages here?");
michael@0 2643 op->mStorages[clientType].AppendElements(liveStorages);
michael@0 2644 }
michael@0 2645 }
michael@0 2646 else {
michael@0 2647 StorageMatcher<ArrayCluster<nsIOfflineStorage*> > matches;
michael@0 2648 if (aPattern.IsVoid()) {
michael@0 2649 matches.Find(mLiveStorages);
michael@0 2650 }
michael@0 2651 else {
michael@0 2652 matches.Find(mLiveStorages, aPattern);
michael@0 2653 }
michael@0 2654
michael@0 2655 if (!matches.IsEmpty()) {
michael@0 2656 // We want *all* storages, even those that are closed, when we're going to
michael@0 2657 // clear the origin.
michael@0 2658 matches.AppendElementsTo(liveStorages);
michael@0 2659
michael@0 2660 NS_ASSERTION(op->mStorages.IsEmpty(),
michael@0 2661 "How do we already have storages here?");
michael@0 2662 matches.SwapElements(op->mStorages);
michael@0 2663 }
michael@0 2664 }
michael@0 2665
michael@0 2666 op->mListener = aListener;
michael@0 2667
michael@0 2668 if (!liveStorages.IsEmpty()) {
michael@0 2669 // Give our callback the storages so it can decide what to do with them.
michael@0 2670 aCallback(liveStorages, aClosure);
michael@0 2671
michael@0 2672 NS_ASSERTION(liveStorages.IsEmpty(),
michael@0 2673 "Should have done something with the array!");
michael@0 2674
michael@0 2675 if (aStorage) {
michael@0 2676 // Wait for those storages to close.
michael@0 2677 return NS_OK;
michael@0 2678 }
michael@0 2679 }
michael@0 2680
michael@0 2681 // If we're trying to open a storage and nothing blocks it, or if we're
michael@0 2682 // clearing an origin, then go ahead and schedule the op.
michael@0 2683 nsresult rv = RunSynchronizedOp(aStorage, op);
michael@0 2684 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2685
michael@0 2686 return NS_OK;
michael@0 2687 }
michael@0 2688
michael@0 2689 nsresult
michael@0 2690 QuotaManager::RunSynchronizedOp(nsIOfflineStorage* aStorage,
michael@0 2691 SynchronizedOp* aOp)
michael@0 2692 {
michael@0 2693 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 2694 NS_ASSERTION(aOp, "Null pointer!");
michael@0 2695 NS_ASSERTION(aOp->mListener, "No listener on this op!");
michael@0 2696 NS_ASSERTION(!aStorage ||
michael@0 2697 aOp->mStorages[aStorage->GetClient()->GetType()].IsEmpty(),
michael@0 2698 "This op isn't ready to run!");
michael@0 2699
michael@0 2700 ArrayCluster<nsIOfflineStorage*> storages;
michael@0 2701
michael@0 2702 uint32_t startIndex;
michael@0 2703 uint32_t endIndex;
michael@0 2704
michael@0 2705 if (aStorage) {
michael@0 2706 Client::Type clientType = aStorage->GetClient()->GetType();
michael@0 2707
michael@0 2708 storages[clientType].AppendElement(aStorage);
michael@0 2709
michael@0 2710 startIndex = clientType;
michael@0 2711 endIndex = clientType + 1;
michael@0 2712 }
michael@0 2713 else {
michael@0 2714 aOp->mStorages.SwapElements(storages);
michael@0 2715
michael@0 2716 startIndex = 0;
michael@0 2717 endIndex = Client::TYPE_MAX;
michael@0 2718 }
michael@0 2719
michael@0 2720 nsRefPtr<WaitForTransactionsToFinishRunnable> runnable =
michael@0 2721 new WaitForTransactionsToFinishRunnable(aOp);
michael@0 2722
michael@0 2723 // Ask the file service to call us back when it's done with this storage.
michael@0 2724 FileService* service = FileService::Get();
michael@0 2725
michael@0 2726 if (service) {
michael@0 2727 // Have to copy here in case a transaction service needs a list too.
michael@0 2728 nsTArray<nsCOMPtr<nsIFileStorage> > array;
michael@0 2729
michael@0 2730 for (uint32_t index = startIndex; index < endIndex; index++) {
michael@0 2731 if (!storages[index].IsEmpty() &&
michael@0 2732 mClients[index]->IsFileServiceUtilized()) {
michael@0 2733 array.AppendElements(storages[index]);
michael@0 2734 }
michael@0 2735 }
michael@0 2736
michael@0 2737 if (!array.IsEmpty()) {
michael@0 2738 runnable->AddRun();
michael@0 2739
michael@0 2740 service->WaitForStoragesToComplete(array, runnable);
michael@0 2741 }
michael@0 2742 }
michael@0 2743
michael@0 2744 // Ask each transaction service to call us back when they're done with this
michael@0 2745 // storage.
michael@0 2746 for (uint32_t index = startIndex; index < endIndex; index++) {
michael@0 2747 nsRefPtr<Client>& client = mClients[index];
michael@0 2748 if (!storages[index].IsEmpty() && client->IsTransactionServiceActivated()) {
michael@0 2749 runnable->AddRun();
michael@0 2750
michael@0 2751 client->WaitForStoragesToComplete(storages[index], runnable);
michael@0 2752 }
michael@0 2753 }
michael@0 2754
michael@0 2755 nsresult rv = runnable->Run();
michael@0 2756 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2757
michael@0 2758 return NS_OK;
michael@0 2759 }
michael@0 2760
michael@0 2761 SynchronizedOp*
michael@0 2762 QuotaManager::FindSynchronizedOp(const nsACString& aPattern,
michael@0 2763 Nullable<PersistenceType> aPersistenceType,
michael@0 2764 const nsACString& aId)
michael@0 2765 {
michael@0 2766 for (uint32_t index = 0; index < mSynchronizedOps.Length(); index++) {
michael@0 2767 const nsAutoPtr<SynchronizedOp>& currentOp = mSynchronizedOps[index];
michael@0 2768 if (PatternMatchesOrigin(aPattern, currentOp->mOriginOrPattern) &&
michael@0 2769 (currentOp->mPersistenceType.IsNull() ||
michael@0 2770 currentOp->mPersistenceType == aPersistenceType) &&
michael@0 2771 (currentOp->mId.IsEmpty() || currentOp->mId == aId)) {
michael@0 2772 return currentOp;
michael@0 2773 }
michael@0 2774 }
michael@0 2775
michael@0 2776 return nullptr;
michael@0 2777 }
michael@0 2778
michael@0 2779 nsresult
michael@0 2780 QuotaManager::ClearStoragesForApp(uint32_t aAppId, bool aBrowserOnly)
michael@0 2781 {
michael@0 2782 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 2783 NS_ASSERTION(aAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID,
michael@0 2784 "Bad appId!");
michael@0 2785
michael@0 2786 // This only works from the main process.
michael@0 2787 NS_ENSURE_TRUE(IsMainProcess(), NS_ERROR_NOT_AVAILABLE);
michael@0 2788
michael@0 2789 nsAutoCString pattern;
michael@0 2790 GetOriginPatternStringMaybeIgnoreBrowser(aAppId, aBrowserOnly, pattern);
michael@0 2791
michael@0 2792 // Clear both temporary and persistent storages.
michael@0 2793 Nullable<PersistenceType> persistenceType;
michael@0 2794
michael@0 2795 // If there is a pending or running clear operation for this app, return
michael@0 2796 // immediately.
michael@0 2797 if (IsClearOriginPending(pattern, persistenceType)) {
michael@0 2798 return NS_OK;
michael@0 2799 }
michael@0 2800
michael@0 2801 OriginOrPatternString oops = OriginOrPatternString::FromPattern(pattern);
michael@0 2802
michael@0 2803 // Queue up the origin clear runnable.
michael@0 2804 nsRefPtr<OriginClearRunnable> runnable =
michael@0 2805 new OriginClearRunnable(oops, persistenceType);
michael@0 2806
michael@0 2807 nsresult rv =
michael@0 2808 WaitForOpenAllowed(oops, persistenceType, EmptyCString(), runnable);
michael@0 2809 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2810
michael@0 2811 runnable->AdvanceState();
michael@0 2812
michael@0 2813 // Give the runnable some help by invalidating any storages in the way.
michael@0 2814 StorageMatcher<nsAutoTArray<nsIOfflineStorage*, 20> > matches;
michael@0 2815 matches.Find(mLiveStorages, pattern);
michael@0 2816
michael@0 2817 for (uint32_t index = 0; index < matches.Length(); index++) {
michael@0 2818 // We need to grab references here to prevent the storage from dying while
michael@0 2819 // we invalidate it.
michael@0 2820 nsCOMPtr<nsIOfflineStorage> storage = matches[index];
michael@0 2821 storage->Invalidate();
michael@0 2822 }
michael@0 2823
michael@0 2824 return NS_OK;
michael@0 2825 }
michael@0 2826
michael@0 2827 // static
michael@0 2828 PLDHashOperator
michael@0 2829 QuotaManager::GetOriginsExceedingGroupLimit(const nsACString& aKey,
michael@0 2830 GroupInfoPair* aValue,
michael@0 2831 void* aUserArg)
michael@0 2832 {
michael@0 2833 NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
michael@0 2834 NS_ASSERTION(aValue, "Null pointer!");
michael@0 2835
michael@0 2836 nsRefPtr<GroupInfo> groupInfo =
michael@0 2837 aValue->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY);
michael@0 2838 if (groupInfo) {
michael@0 2839 QuotaManager* quotaManager = QuotaManager::Get();
michael@0 2840 NS_ASSERTION(quotaManager, "Shouldn't be null!");
michael@0 2841
michael@0 2842 if (groupInfo->mUsage > quotaManager->GetGroupLimit()) {
michael@0 2843 nsTArray<OriginInfo*>* doomedOriginInfos =
michael@0 2844 static_cast<nsTArray<OriginInfo*>*>(aUserArg);
michael@0 2845
michael@0 2846 nsTArray<nsRefPtr<OriginInfo> >& originInfos = groupInfo->mOriginInfos;
michael@0 2847 originInfos.Sort(OriginInfoLRUComparator());
michael@0 2848
michael@0 2849 uint64_t usage = groupInfo->mUsage;
michael@0 2850 for (uint32_t i = 0; i < originInfos.Length(); i++) {
michael@0 2851 OriginInfo* originInfo = originInfos[i];
michael@0 2852
michael@0 2853 doomedOriginInfos->AppendElement(originInfo);
michael@0 2854 usage -= originInfo->mUsage;
michael@0 2855
michael@0 2856 if (usage <= quotaManager->GetGroupLimit()) {
michael@0 2857 break;
michael@0 2858 }
michael@0 2859 }
michael@0 2860 }
michael@0 2861 }
michael@0 2862
michael@0 2863 return PL_DHASH_NEXT;
michael@0 2864 }
michael@0 2865
michael@0 2866 // static
michael@0 2867 PLDHashOperator
michael@0 2868 QuotaManager::GetAllTemporaryStorageOrigins(const nsACString& aKey,
michael@0 2869 GroupInfoPair* aValue,
michael@0 2870 void* aUserArg)
michael@0 2871 {
michael@0 2872 NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
michael@0 2873 NS_ASSERTION(aValue, "Null pointer!");
michael@0 2874
michael@0 2875 nsRefPtr<GroupInfo> groupInfo =
michael@0 2876 aValue->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY);
michael@0 2877 if (groupInfo) {
michael@0 2878 nsTArray<OriginInfo*>* originInfos =
michael@0 2879 static_cast<nsTArray<OriginInfo*>*>(aUserArg);
michael@0 2880
michael@0 2881 originInfos->AppendElements(groupInfo->mOriginInfos);
michael@0 2882 }
michael@0 2883
michael@0 2884 return PL_DHASH_NEXT;
michael@0 2885 }
michael@0 2886
michael@0 2887 void
michael@0 2888 QuotaManager::CheckTemporaryStorageLimits()
michael@0 2889 {
michael@0 2890 AssertIsOnIOThread();
michael@0 2891
michael@0 2892 nsTArray<OriginInfo*> doomedOriginInfos;
michael@0 2893 {
michael@0 2894 MutexAutoLock lock(mQuotaMutex);
michael@0 2895
michael@0 2896 mGroupInfoPairs.EnumerateRead(GetOriginsExceedingGroupLimit,
michael@0 2897 &doomedOriginInfos);
michael@0 2898
michael@0 2899 uint64_t usage = 0;
michael@0 2900 for (uint32_t index = 0; index < doomedOriginInfos.Length(); index++) {
michael@0 2901 usage += doomedOriginInfos[index]->mUsage;
michael@0 2902 }
michael@0 2903
michael@0 2904 if (mTemporaryStorageUsage - usage > mTemporaryStorageLimit) {
michael@0 2905 nsTArray<OriginInfo*> originInfos;
michael@0 2906
michael@0 2907 mGroupInfoPairs.EnumerateRead(GetAllTemporaryStorageOrigins,
michael@0 2908 &originInfos);
michael@0 2909
michael@0 2910 for (uint32_t index = originInfos.Length(); index > 0; index--) {
michael@0 2911 if (doomedOriginInfos.Contains(originInfos[index - 1])) {
michael@0 2912 originInfos.RemoveElementAt(index - 1);
michael@0 2913 }
michael@0 2914 }
michael@0 2915
michael@0 2916 originInfos.Sort(OriginInfoLRUComparator());
michael@0 2917
michael@0 2918 for (uint32_t i = 0; i < originInfos.Length(); i++) {
michael@0 2919 if (mTemporaryStorageUsage - usage <= mTemporaryStorageLimit) {
michael@0 2920 originInfos.TruncateLength(i);
michael@0 2921 break;
michael@0 2922 }
michael@0 2923
michael@0 2924 usage += originInfos[i]->mUsage;
michael@0 2925 }
michael@0 2926
michael@0 2927 doomedOriginInfos.AppendElements(originInfos);
michael@0 2928 }
michael@0 2929 }
michael@0 2930
michael@0 2931 for (uint32_t index = 0; index < doomedOriginInfos.Length(); index++) {
michael@0 2932 DeleteTemporaryFilesForOrigin(doomedOriginInfos[index]->mOrigin);
michael@0 2933 }
michael@0 2934
michael@0 2935 nsTArray<nsCString> doomedOrigins;
michael@0 2936 {
michael@0 2937 MutexAutoLock lock(mQuotaMutex);
michael@0 2938
michael@0 2939 for (uint32_t index = 0; index < doomedOriginInfos.Length(); index++) {
michael@0 2940 OriginInfo* doomedOriginInfo = doomedOriginInfos[index];
michael@0 2941
michael@0 2942 nsCString group = doomedOriginInfo->mGroupInfo->mGroup;
michael@0 2943 nsCString origin = doomedOriginInfo->mOrigin;
michael@0 2944 LockedRemoveQuotaForOrigin(PERSISTENCE_TYPE_TEMPORARY, group, origin);
michael@0 2945
michael@0 2946 #ifdef DEBUG
michael@0 2947 doomedOriginInfos[index] = nullptr;
michael@0 2948 #endif
michael@0 2949
michael@0 2950 doomedOrigins.AppendElement(origin);
michael@0 2951 }
michael@0 2952 }
michael@0 2953
michael@0 2954 for (uint32_t index = 0; index < doomedOrigins.Length(); index++) {
michael@0 2955 OriginClearCompleted(
michael@0 2956 PERSISTENCE_TYPE_TEMPORARY,
michael@0 2957 OriginOrPatternString::FromOrigin(doomedOrigins[index]));
michael@0 2958 }
michael@0 2959 }
michael@0 2960
michael@0 2961 // static
michael@0 2962 PLDHashOperator
michael@0 2963 QuotaManager::AddTemporaryStorageOrigins(
michael@0 2964 const nsACString& aKey,
michael@0 2965 ArrayCluster<nsIOfflineStorage*>* aValue,
michael@0 2966 void* aUserArg)
michael@0 2967 {
michael@0 2968 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 2969 NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
michael@0 2970 NS_ASSERTION(aValue, "Null pointer!");
michael@0 2971 NS_ASSERTION(aUserArg, "Null pointer!");
michael@0 2972
michael@0 2973 OriginCollection& collection = *static_cast<OriginCollection*>(aUserArg);
michael@0 2974
michael@0 2975 if (collection.ContainsOrigin(aKey)) {
michael@0 2976 return PL_DHASH_NEXT;
michael@0 2977 }
michael@0 2978
michael@0 2979 for (uint32_t i = 0; i < Client::TYPE_MAX; i++) {
michael@0 2980 nsTArray<nsIOfflineStorage*>& array = (*aValue)[i];
michael@0 2981 for (uint32_t j = 0; j < array.Length(); j++) {
michael@0 2982 nsIOfflineStorage*& storage = array[j];
michael@0 2983 if (storage->Type() == PERSISTENCE_TYPE_TEMPORARY) {
michael@0 2984 collection.AddOrigin(aKey);
michael@0 2985 return PL_DHASH_NEXT;
michael@0 2986 }
michael@0 2987 }
michael@0 2988 }
michael@0 2989
michael@0 2990 return PL_DHASH_NEXT;
michael@0 2991 }
michael@0 2992
michael@0 2993 // static
michael@0 2994 PLDHashOperator
michael@0 2995 QuotaManager::GetInactiveTemporaryStorageOrigins(const nsACString& aKey,
michael@0 2996 GroupInfoPair* aValue,
michael@0 2997 void* aUserArg)
michael@0 2998 {
michael@0 2999 NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
michael@0 3000 NS_ASSERTION(aValue, "Null pointer!");
michael@0 3001 NS_ASSERTION(aUserArg, "Null pointer!");
michael@0 3002
michael@0 3003 nsRefPtr<GroupInfo> groupInfo =
michael@0 3004 aValue->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY);
michael@0 3005 if (groupInfo) {
michael@0 3006 InactiveOriginsInfo* info = static_cast<InactiveOriginsInfo*>(aUserArg);
michael@0 3007
michael@0 3008 nsTArray<nsRefPtr<OriginInfo> >& originInfos = groupInfo->mOriginInfos;
michael@0 3009
michael@0 3010 for (uint32_t i = 0; i < originInfos.Length(); i++) {
michael@0 3011 OriginInfo* originInfo = originInfos[i];
michael@0 3012
michael@0 3013 if (!info->collection.ContainsOrigin(originInfo->mOrigin)) {
michael@0 3014 NS_ASSERTION(!originInfo->mQuotaObjects.Count(),
michael@0 3015 "Inactive origin shouldn't have open files!");
michael@0 3016 info->origins.AppendElement(originInfo);
michael@0 3017 }
michael@0 3018 }
michael@0 3019 }
michael@0 3020
michael@0 3021 return PL_DHASH_NEXT;
michael@0 3022 }
michael@0 3023
michael@0 3024 uint64_t
michael@0 3025 QuotaManager::CollectOriginsForEviction(uint64_t aMinSizeToBeFreed,
michael@0 3026 nsTArray<OriginInfo*>& aOriginInfos)
michael@0 3027 {
michael@0 3028 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 3029
michael@0 3030 // Collect active origins first.
michael@0 3031 OriginCollection originCollection;
michael@0 3032
michael@0 3033 // Add patterns and origins that have running or pending synchronized ops.
michael@0 3034 // (add patterns first to reduce redundancy in the origin collection).
michael@0 3035 uint32_t index;
michael@0 3036 for (index = 0; index < mSynchronizedOps.Length(); index++) {
michael@0 3037 nsAutoPtr<SynchronizedOp>& op = mSynchronizedOps[index];
michael@0 3038 if (op->mPersistenceType.IsNull() ||
michael@0 3039 op->mPersistenceType.Value() == PERSISTENCE_TYPE_TEMPORARY) {
michael@0 3040 if (op->mOriginOrPattern.IsPattern() &&
michael@0 3041 !originCollection.ContainsPattern(op->mOriginOrPattern)) {
michael@0 3042 originCollection.AddPattern(op->mOriginOrPattern);
michael@0 3043 }
michael@0 3044 }
michael@0 3045 }
michael@0 3046
michael@0 3047 for (index = 0; index < mSynchronizedOps.Length(); index++) {
michael@0 3048 nsAutoPtr<SynchronizedOp>& op = mSynchronizedOps[index];
michael@0 3049 if (op->mPersistenceType.IsNull() ||
michael@0 3050 op->mPersistenceType.Value() == PERSISTENCE_TYPE_TEMPORARY) {
michael@0 3051 if (op->mOriginOrPattern.IsOrigin() &&
michael@0 3052 !originCollection.ContainsOrigin(op->mOriginOrPattern)) {
michael@0 3053 originCollection.AddOrigin(op->mOriginOrPattern);
michael@0 3054 }
michael@0 3055 }
michael@0 3056 }
michael@0 3057
michael@0 3058 // Add origins that have live temporary storages.
michael@0 3059 mLiveStorages.EnumerateRead(AddTemporaryStorageOrigins, &originCollection);
michael@0 3060
michael@0 3061 // Enumerate inactive origins. This must be protected by the mutex.
michael@0 3062 nsTArray<OriginInfo*> inactiveOrigins;
michael@0 3063 {
michael@0 3064 InactiveOriginsInfo info(originCollection, inactiveOrigins);
michael@0 3065 MutexAutoLock lock(mQuotaMutex);
michael@0 3066 mGroupInfoPairs.EnumerateRead(GetInactiveTemporaryStorageOrigins, &info);
michael@0 3067 }
michael@0 3068
michael@0 3069 // We now have a list of all inactive origins. So it's safe to sort the list
michael@0 3070 // and calculate available size without holding the lock.
michael@0 3071
michael@0 3072 // Sort by the origin access time.
michael@0 3073 inactiveOrigins.Sort(OriginInfoLRUComparator());
michael@0 3074
michael@0 3075 // Create a list of inactive and the least recently used origins
michael@0 3076 // whose aggregate size is greater or equals the minimal size to be freed.
michael@0 3077 uint64_t sizeToBeFreed = 0;
michael@0 3078 for(index = 0; index < inactiveOrigins.Length(); index++) {
michael@0 3079 if (sizeToBeFreed >= aMinSizeToBeFreed) {
michael@0 3080 inactiveOrigins.TruncateLength(index);
michael@0 3081 break;
michael@0 3082 }
michael@0 3083
michael@0 3084 sizeToBeFreed += inactiveOrigins[index]->mUsage;
michael@0 3085 }
michael@0 3086
michael@0 3087 if (sizeToBeFreed >= aMinSizeToBeFreed) {
michael@0 3088 // Success, add synchronized ops for these origins, so any other
michael@0 3089 // operations for them will be delayed (until origin eviction is finalized).
michael@0 3090
michael@0 3091 for(index = 0; index < inactiveOrigins.Length(); index++) {
michael@0 3092 OriginOrPatternString oops =
michael@0 3093 OriginOrPatternString::FromOrigin(inactiveOrigins[index]->mOrigin);
michael@0 3094
michael@0 3095 AddSynchronizedOp(oops,
michael@0 3096 Nullable<PersistenceType>(PERSISTENCE_TYPE_TEMPORARY));
michael@0 3097 }
michael@0 3098
michael@0 3099 inactiveOrigins.SwapElements(aOriginInfos);
michael@0 3100 return sizeToBeFreed;
michael@0 3101 }
michael@0 3102
michael@0 3103 return 0;
michael@0 3104 }
michael@0 3105
michael@0 3106 void
michael@0 3107 QuotaManager::DeleteTemporaryFilesForOrigin(const nsACString& aOrigin)
michael@0 3108 {
michael@0 3109 nsCOMPtr<nsIFile> directory;
michael@0 3110 nsresult rv = GetDirectoryForOrigin(PERSISTENCE_TYPE_TEMPORARY, aOrigin,
michael@0 3111 getter_AddRefs(directory));
michael@0 3112 NS_ENSURE_SUCCESS_VOID(rv);
michael@0 3113
michael@0 3114 rv = directory->Remove(true);
michael@0 3115 if (rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST &&
michael@0 3116 rv != NS_ERROR_FILE_NOT_FOUND && NS_FAILED(rv)) {
michael@0 3117 // This should never fail if we've closed all storage connections
michael@0 3118 // correctly...
michael@0 3119 NS_ERROR("Failed to remove directory!");
michael@0 3120 }
michael@0 3121 }
michael@0 3122
michael@0 3123 void
michael@0 3124 QuotaManager::FinalizeOriginEviction(nsTArray<nsCString>& aOrigins)
michael@0 3125 {
michael@0 3126 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
michael@0 3127
michael@0 3128 nsRefPtr<FinalizeOriginEvictionRunnable> runnable =
michael@0 3129 new FinalizeOriginEvictionRunnable(aOrigins);
michael@0 3130
michael@0 3131 nsresult rv = IsOnIOThread() ? runnable->RunImmediately()
michael@0 3132 : runnable->Dispatch();
michael@0 3133 NS_ENSURE_SUCCESS_VOID(rv);
michael@0 3134 }
michael@0 3135
michael@0 3136 void
michael@0 3137 QuotaManager::SaveOriginAccessTime(const nsACString& aOrigin,
michael@0 3138 int64_t aTimestamp)
michael@0 3139 {
michael@0 3140 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 3141
michael@0 3142 if (QuotaManager::IsShuttingDown()) {
michael@0 3143 return;
michael@0 3144 }
michael@0 3145
michael@0 3146 nsRefPtr<SaveOriginAccessTimeRunnable> runnable =
michael@0 3147 new SaveOriginAccessTimeRunnable(aOrigin, aTimestamp);
michael@0 3148
michael@0 3149 if (NS_FAILED(mIOThread->Dispatch(runnable, NS_DISPATCH_NORMAL))) {
michael@0 3150 NS_WARNING("Failed to dispatch runnable!");
michael@0 3151 }
michael@0 3152 }
michael@0 3153
michael@0 3154 void
michael@0 3155 QuotaManager::GetOriginPatternString(uint32_t aAppId,
michael@0 3156 MozBrowserPatternFlag aBrowserFlag,
michael@0 3157 const nsACString& aOrigin,
michael@0 3158 nsAutoCString& _retval)
michael@0 3159 {
michael@0 3160 NS_ASSERTION(aAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID,
michael@0 3161 "Bad appId!");
michael@0 3162 NS_ASSERTION(aOrigin.IsEmpty() || aBrowserFlag != IgnoreMozBrowser,
michael@0 3163 "Bad args!");
michael@0 3164
michael@0 3165 if (aOrigin.IsEmpty()) {
michael@0 3166 _retval.Truncate();
michael@0 3167
michael@0 3168 _retval.AppendInt(aAppId);
michael@0 3169 _retval.Append('+');
michael@0 3170
michael@0 3171 if (aBrowserFlag != IgnoreMozBrowser) {
michael@0 3172 if (aBrowserFlag == MozBrowser) {
michael@0 3173 _retval.Append('t');
michael@0 3174 }
michael@0 3175 else {
michael@0 3176 _retval.Append('f');
michael@0 3177 }
michael@0 3178 _retval.Append('+');
michael@0 3179 }
michael@0 3180
michael@0 3181 return;
michael@0 3182 }
michael@0 3183
michael@0 3184 #ifdef DEBUG
michael@0 3185 if (aAppId != nsIScriptSecurityManager::NO_APP_ID ||
michael@0 3186 aBrowserFlag == MozBrowser) {
michael@0 3187 nsAutoCString pattern;
michael@0 3188 GetOriginPatternString(aAppId, aBrowserFlag, EmptyCString(), pattern);
michael@0 3189 NS_ASSERTION(PatternMatchesOrigin(pattern, aOrigin),
michael@0 3190 "Origin doesn't match parameters!");
michael@0 3191 }
michael@0 3192 #endif
michael@0 3193
michael@0 3194 _retval = aOrigin;
michael@0 3195 }
michael@0 3196
michael@0 3197 SynchronizedOp::SynchronizedOp(const OriginOrPatternString& aOriginOrPattern,
michael@0 3198 Nullable<PersistenceType> aPersistenceType,
michael@0 3199 const nsACString& aId)
michael@0 3200 : mOriginOrPattern(aOriginOrPattern), mPersistenceType(aPersistenceType),
michael@0 3201 mId(aId)
michael@0 3202 {
michael@0 3203 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 3204 MOZ_COUNT_CTOR(SynchronizedOp);
michael@0 3205 }
michael@0 3206
michael@0 3207 SynchronizedOp::~SynchronizedOp()
michael@0 3208 {
michael@0 3209 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 3210 MOZ_COUNT_DTOR(SynchronizedOp);
michael@0 3211 }
michael@0 3212
michael@0 3213 bool
michael@0 3214 SynchronizedOp::MustWaitFor(const SynchronizedOp& aExistingOp)
michael@0 3215 {
michael@0 3216 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 3217
michael@0 3218 if (aExistingOp.mOriginOrPattern.IsNull() || mOriginOrPattern.IsNull()) {
michael@0 3219 return true;
michael@0 3220 }
michael@0 3221
michael@0 3222 bool match;
michael@0 3223
michael@0 3224 if (aExistingOp.mOriginOrPattern.IsOrigin()) {
michael@0 3225 if (mOriginOrPattern.IsOrigin()) {
michael@0 3226 match = aExistingOp.mOriginOrPattern.Equals(mOriginOrPattern);
michael@0 3227 }
michael@0 3228 else {
michael@0 3229 match = PatternMatchesOrigin(mOriginOrPattern, aExistingOp.mOriginOrPattern);
michael@0 3230 }
michael@0 3231 }
michael@0 3232 else if (mOriginOrPattern.IsOrigin()) {
michael@0 3233 match = PatternMatchesOrigin(aExistingOp.mOriginOrPattern, mOriginOrPattern);
michael@0 3234 }
michael@0 3235 else {
michael@0 3236 match = PatternMatchesOrigin(mOriginOrPattern, aExistingOp.mOriginOrPattern) ||
michael@0 3237 PatternMatchesOrigin(aExistingOp.mOriginOrPattern, mOriginOrPattern);
michael@0 3238 }
michael@0 3239
michael@0 3240 // If the origins don't match, the second can proceed.
michael@0 3241 if (!match) {
michael@0 3242 return false;
michael@0 3243 }
michael@0 3244
michael@0 3245 // If the origins match but the persistence types are different, the second
michael@0 3246 // can proceed.
michael@0 3247 if (!aExistingOp.mPersistenceType.IsNull() && !mPersistenceType.IsNull() &&
michael@0 3248 aExistingOp.mPersistenceType.Value() != mPersistenceType.Value()) {
michael@0 3249 return false;
michael@0 3250 }
michael@0 3251
michael@0 3252 // If the origins and the ids match, the second must wait.
michael@0 3253 if (aExistingOp.mId == mId) {
michael@0 3254 return true;
michael@0 3255 }
michael@0 3256
michael@0 3257 // Waiting is required if either one corresponds to an origin clearing
michael@0 3258 // (an empty Id).
michael@0 3259 if (aExistingOp.mId.IsEmpty() || mId.IsEmpty()) {
michael@0 3260 return true;
michael@0 3261 }
michael@0 3262
michael@0 3263 // Otherwise, things for the same origin but different storages can proceed
michael@0 3264 // independently.
michael@0 3265 return false;
michael@0 3266 }
michael@0 3267
michael@0 3268 void
michael@0 3269 SynchronizedOp::DelayRunnable(nsIRunnable* aRunnable)
michael@0 3270 {
michael@0 3271 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 3272 NS_ASSERTION(mDelayedRunnables.IsEmpty() || mId.IsEmpty(),
michael@0 3273 "Only ClearOrigin operations can delay multiple runnables!");
michael@0 3274
michael@0 3275 mDelayedRunnables.AppendElement(aRunnable);
michael@0 3276 }
michael@0 3277
michael@0 3278 void
michael@0 3279 SynchronizedOp::DispatchDelayedRunnables()
michael@0 3280 {
michael@0 3281 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 3282 NS_ASSERTION(!mListener, "Any listener should be gone by now!");
michael@0 3283
michael@0 3284 uint32_t count = mDelayedRunnables.Length();
michael@0 3285 for (uint32_t index = 0; index < count; index++) {
michael@0 3286 NS_DispatchToCurrentThread(mDelayedRunnables[index]);
michael@0 3287 }
michael@0 3288
michael@0 3289 mDelayedRunnables.Clear();
michael@0 3290 }
michael@0 3291
michael@0 3292 CollectOriginsHelper::CollectOriginsHelper(mozilla::Mutex& aMutex,
michael@0 3293 uint64_t aMinSizeToBeFreed)
michael@0 3294 : mMinSizeToBeFreed(aMinSizeToBeFreed),
michael@0 3295 mMutex(aMutex),
michael@0 3296 mCondVar(aMutex, "CollectOriginsHelper::mCondVar"),
michael@0 3297 mSizeToBeFreed(0),
michael@0 3298 mWaiting(true)
michael@0 3299 {
michael@0 3300 MOZ_ASSERT(!NS_IsMainThread(), "Wrong thread!");
michael@0 3301 mMutex.AssertCurrentThreadOwns();
michael@0 3302 }
michael@0 3303
michael@0 3304 int64_t
michael@0 3305 CollectOriginsHelper::BlockAndReturnOriginsForEviction(
michael@0 3306 nsTArray<OriginInfo*>& aOriginInfos)
michael@0 3307 {
michael@0 3308 MOZ_ASSERT(!NS_IsMainThread(), "Wrong thread!");
michael@0 3309 mMutex.AssertCurrentThreadOwns();
michael@0 3310
michael@0 3311 while (mWaiting) {
michael@0 3312 mCondVar.Wait();
michael@0 3313 }
michael@0 3314
michael@0 3315 mOriginInfos.SwapElements(aOriginInfos);
michael@0 3316 return mSizeToBeFreed;
michael@0 3317 }
michael@0 3318
michael@0 3319 NS_IMETHODIMP
michael@0 3320 CollectOriginsHelper::Run()
michael@0 3321 {
michael@0 3322 MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
michael@0 3323
michael@0 3324 QuotaManager* quotaManager = QuotaManager::Get();
michael@0 3325 NS_ASSERTION(quotaManager, "Shouldn't be null!");
michael@0 3326
michael@0 3327 // We use extra stack vars here to avoid race detector warnings (the same
michael@0 3328 // memory accessed with and without the lock held).
michael@0 3329 nsTArray<OriginInfo*> originInfos;
michael@0 3330 uint64_t sizeToBeFreed =
michael@0 3331 quotaManager->CollectOriginsForEviction(mMinSizeToBeFreed, originInfos);
michael@0 3332
michael@0 3333 MutexAutoLock lock(mMutex);
michael@0 3334
michael@0 3335 NS_ASSERTION(mWaiting, "Huh?!");
michael@0 3336
michael@0 3337 mOriginInfos.SwapElements(originInfos);
michael@0 3338 mSizeToBeFreed = sizeToBeFreed;
michael@0 3339 mWaiting = false;
michael@0 3340 mCondVar.Notify();
michael@0 3341
michael@0 3342 return NS_OK;
michael@0 3343 }
michael@0 3344
michael@0 3345 nsresult
michael@0 3346 OriginClearRunnable::OnExclusiveAccessAcquired()
michael@0 3347 {
michael@0 3348 QuotaManager* quotaManager = QuotaManager::Get();
michael@0 3349 NS_ASSERTION(quotaManager, "This should never fail!");
michael@0 3350
michael@0 3351 nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
michael@0 3352 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3353
michael@0 3354 return NS_OK;
michael@0 3355 }
michael@0 3356
michael@0 3357 // static
michael@0 3358 void
michael@0 3359 OriginClearRunnable::InvalidateOpenedStorages(
michael@0 3360 nsTArray<nsCOMPtr<nsIOfflineStorage> >& aStorages,
michael@0 3361 void* aClosure)
michael@0 3362 {
michael@0 3363 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 3364
michael@0 3365 nsTArray<nsCOMPtr<nsIOfflineStorage> > storages;
michael@0 3366 storages.SwapElements(aStorages);
michael@0 3367
michael@0 3368 for (uint32_t index = 0; index < storages.Length(); index++) {
michael@0 3369 storages[index]->Invalidate();
michael@0 3370 }
michael@0 3371 }
michael@0 3372
michael@0 3373 void
michael@0 3374 OriginClearRunnable::DeleteFiles(QuotaManager* aQuotaManager,
michael@0 3375 PersistenceType aPersistenceType)
michael@0 3376 {
michael@0 3377 AssertIsOnIOThread();
michael@0 3378 NS_ASSERTION(aQuotaManager, "Don't pass me null!");
michael@0 3379
michael@0 3380 nsresult rv;
michael@0 3381
michael@0 3382 nsCOMPtr<nsIFile> directory =
michael@0 3383 do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
michael@0 3384 NS_ENSURE_SUCCESS_VOID(rv);
michael@0 3385
michael@0 3386 rv = directory->InitWithPath(aQuotaManager->GetStoragePath(aPersistenceType));
michael@0 3387 NS_ENSURE_SUCCESS_VOID(rv);
michael@0 3388
michael@0 3389 nsCOMPtr<nsISimpleEnumerator> entries;
michael@0 3390 if (NS_FAILED(directory->GetDirectoryEntries(getter_AddRefs(entries))) ||
michael@0 3391 !entries) {
michael@0 3392 return;
michael@0 3393 }
michael@0 3394
michael@0 3395 nsCString originSanitized(mOriginOrPattern);
michael@0 3396 SanitizeOriginString(originSanitized);
michael@0 3397
michael@0 3398 bool hasMore;
michael@0 3399 while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
michael@0 3400 nsCOMPtr<nsISupports> entry;
michael@0 3401 rv = entries->GetNext(getter_AddRefs(entry));
michael@0 3402 NS_ENSURE_SUCCESS_VOID(rv);
michael@0 3403
michael@0 3404 nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
michael@0 3405 NS_ASSERTION(file, "Don't know what this is!");
michael@0 3406
michael@0 3407 bool isDirectory;
michael@0 3408 rv = file->IsDirectory(&isDirectory);
michael@0 3409 NS_ENSURE_SUCCESS_VOID(rv);
michael@0 3410
michael@0 3411 if (!isDirectory) {
michael@0 3412 NS_WARNING("Something in the IndexedDB directory that doesn't belong!");
michael@0 3413 continue;
michael@0 3414 }
michael@0 3415
michael@0 3416 nsString leafName;
michael@0 3417 rv = file->GetLeafName(leafName);
michael@0 3418 NS_ENSURE_SUCCESS_VOID(rv);
michael@0 3419
michael@0 3420 // Skip storages for other apps.
michael@0 3421 if (!PatternMatchesOrigin(originSanitized,
michael@0 3422 NS_ConvertUTF16toUTF8(leafName))) {
michael@0 3423 continue;
michael@0 3424 }
michael@0 3425
michael@0 3426 if (NS_FAILED(file->Remove(true))) {
michael@0 3427 // This should never fail if we've closed all storage connections
michael@0 3428 // correctly...
michael@0 3429 NS_ERROR("Failed to remove directory!");
michael@0 3430 }
michael@0 3431 }
michael@0 3432
michael@0 3433 aQuotaManager->RemoveQuotaForPattern(aPersistenceType, mOriginOrPattern);
michael@0 3434
michael@0 3435 aQuotaManager->OriginClearCompleted(aPersistenceType, mOriginOrPattern);
michael@0 3436 }
michael@0 3437
michael@0 3438 NS_IMPL_ISUPPORTS_INHERITED0(OriginClearRunnable, nsRunnable)
michael@0 3439
michael@0 3440 NS_IMETHODIMP
michael@0 3441 OriginClearRunnable::Run()
michael@0 3442 {
michael@0 3443 PROFILER_LABEL("Quota", "OriginClearRunnable::Run");
michael@0 3444
michael@0 3445 QuotaManager* quotaManager = QuotaManager::Get();
michael@0 3446 NS_ASSERTION(quotaManager, "This should never fail!");
michael@0 3447
michael@0 3448 switch (mCallbackState) {
michael@0 3449 case Pending: {
michael@0 3450 NS_NOTREACHED("Should never get here without being dispatched!");
michael@0 3451 return NS_ERROR_UNEXPECTED;
michael@0 3452 }
michael@0 3453
michael@0 3454 case OpenAllowed: {
michael@0 3455 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 3456
michael@0 3457 AdvanceState();
michael@0 3458
michael@0 3459 // Now we have to wait until the thread pool is done with all of the
michael@0 3460 // storages we care about.
michael@0 3461 nsresult rv =
michael@0 3462 quotaManager->AcquireExclusiveAccess(mOriginOrPattern, mPersistenceType,
michael@0 3463 this, InvalidateOpenedStorages,
michael@0 3464 nullptr);
michael@0 3465 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3466
michael@0 3467 return NS_OK;
michael@0 3468 }
michael@0 3469
michael@0 3470 case IO: {
michael@0 3471 AssertIsOnIOThread();
michael@0 3472
michael@0 3473 AdvanceState();
michael@0 3474
michael@0 3475 if (mPersistenceType.IsNull()) {
michael@0 3476 DeleteFiles(quotaManager, PERSISTENCE_TYPE_PERSISTENT);
michael@0 3477 DeleteFiles(quotaManager, PERSISTENCE_TYPE_TEMPORARY);
michael@0 3478 } else {
michael@0 3479 DeleteFiles(quotaManager, mPersistenceType.Value());
michael@0 3480 }
michael@0 3481
michael@0 3482 // Now dispatch back to the main thread.
michael@0 3483 if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) {
michael@0 3484 NS_WARNING("Failed to dispatch to main thread!");
michael@0 3485 return NS_ERROR_FAILURE;
michael@0 3486 }
michael@0 3487
michael@0 3488 return NS_OK;
michael@0 3489 }
michael@0 3490
michael@0 3491 case Complete: {
michael@0 3492 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 3493
michael@0 3494 // Tell the QuotaManager that we're done.
michael@0 3495 quotaManager->AllowNextSynchronizedOp(mOriginOrPattern, mPersistenceType,
michael@0 3496 EmptyCString());
michael@0 3497
michael@0 3498 return NS_OK;
michael@0 3499 }
michael@0 3500
michael@0 3501 default:
michael@0 3502 NS_ERROR("Unknown state value!");
michael@0 3503 return NS_ERROR_UNEXPECTED;
michael@0 3504 }
michael@0 3505
michael@0 3506 NS_NOTREACHED("Should never get here!");
michael@0 3507 return NS_ERROR_UNEXPECTED;
michael@0 3508 }
michael@0 3509
michael@0 3510 AsyncUsageRunnable::AsyncUsageRunnable(uint32_t aAppId,
michael@0 3511 bool aInMozBrowserOnly,
michael@0 3512 const nsACString& aGroup,
michael@0 3513 const OriginOrPatternString& aOrigin,
michael@0 3514 nsIURI* aURI,
michael@0 3515 nsIUsageCallback* aCallback)
michael@0 3516 : mURI(aURI),
michael@0 3517 mCallback(aCallback),
michael@0 3518 mAppId(aAppId),
michael@0 3519 mGroup(aGroup),
michael@0 3520 mOrigin(aOrigin),
michael@0 3521 mCallbackState(Pending),
michael@0 3522 mInMozBrowserOnly(aInMozBrowserOnly)
michael@0 3523 {
michael@0 3524 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 3525 NS_ASSERTION(aURI, "Null pointer!");
michael@0 3526 NS_ASSERTION(!aGroup.IsEmpty(), "Empty group!");
michael@0 3527 NS_ASSERTION(aOrigin.IsOrigin(), "Expect origin only here!");
michael@0 3528 NS_ASSERTION(!aOrigin.IsEmpty(), "Empty origin!");
michael@0 3529 NS_ASSERTION(aCallback, "Null pointer!");
michael@0 3530 }
michael@0 3531
michael@0 3532 nsresult
michael@0 3533 AsyncUsageRunnable::TakeShortcut()
michael@0 3534 {
michael@0 3535 NS_ASSERTION(mCallbackState == Pending, "Huh?");
michael@0 3536
michael@0 3537 nsresult rv = NS_DispatchToCurrentThread(this);
michael@0 3538 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3539
michael@0 3540 mCallbackState = Shortcut;
michael@0 3541 return NS_OK;
michael@0 3542 }
michael@0 3543
michael@0 3544 nsresult
michael@0 3545 AsyncUsageRunnable::RunInternal()
michael@0 3546 {
michael@0 3547 QuotaManager* quotaManager = QuotaManager::Get();
michael@0 3548 NS_ASSERTION(quotaManager, "This should never fail!");
michael@0 3549
michael@0 3550 nsresult rv;
michael@0 3551
michael@0 3552 switch (mCallbackState) {
michael@0 3553 case Pending: {
michael@0 3554 NS_NOTREACHED("Should never get here without being dispatched!");
michael@0 3555 return NS_ERROR_UNEXPECTED;
michael@0 3556 }
michael@0 3557
michael@0 3558 case OpenAllowed: {
michael@0 3559 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 3560
michael@0 3561 AdvanceState();
michael@0 3562
michael@0 3563 rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
michael@0 3564 if (NS_FAILED(rv)) {
michael@0 3565 NS_WARNING("Failed to dispatch to the IO thread!");
michael@0 3566 }
michael@0 3567
michael@0 3568 return NS_OK;
michael@0 3569 }
michael@0 3570
michael@0 3571 case IO: {
michael@0 3572 AssertIsOnIOThread();
michael@0 3573
michael@0 3574 AdvanceState();
michael@0 3575
michael@0 3576 // Add all the persistent storage files we care about.
michael@0 3577 rv = AddToUsage(quotaManager, PERSISTENCE_TYPE_PERSISTENT);
michael@0 3578 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3579
michael@0 3580 // Add all the temporary storage files we care about.
michael@0 3581 rv = AddToUsage(quotaManager, PERSISTENCE_TYPE_TEMPORARY);
michael@0 3582 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3583
michael@0 3584 // Run dispatches us back to the main thread.
michael@0 3585 return NS_OK;
michael@0 3586 }
michael@0 3587
michael@0 3588 case Complete: // Fall through
michael@0 3589 case Shortcut: {
michael@0 3590 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 3591
michael@0 3592 // Call the callback unless we were canceled.
michael@0 3593 if (!mCanceled) {
michael@0 3594 mCallback->OnUsageResult(mURI, TotalUsage(), FileUsage(), mAppId,
michael@0 3595 mInMozBrowserOnly);
michael@0 3596 }
michael@0 3597
michael@0 3598 // Clean up.
michael@0 3599 mURI = nullptr;
michael@0 3600 mCallback = nullptr;
michael@0 3601
michael@0 3602 // And tell the QuotaManager that we're done.
michael@0 3603 if (mCallbackState == Complete) {
michael@0 3604 quotaManager->AllowNextSynchronizedOp(mOrigin,
michael@0 3605 Nullable<PersistenceType>(),
michael@0 3606 EmptyCString());
michael@0 3607 }
michael@0 3608
michael@0 3609 return NS_OK;
michael@0 3610 }
michael@0 3611
michael@0 3612 default:
michael@0 3613 NS_ERROR("Unknown state value!");
michael@0 3614 return NS_ERROR_UNEXPECTED;
michael@0 3615 }
michael@0 3616
michael@0 3617 NS_NOTREACHED("Should never get here!");
michael@0 3618 return NS_ERROR_UNEXPECTED;
michael@0 3619 }
michael@0 3620
michael@0 3621 nsresult
michael@0 3622 AsyncUsageRunnable::AddToUsage(QuotaManager* aQuotaManager,
michael@0 3623 PersistenceType aPersistenceType)
michael@0 3624 {
michael@0 3625 AssertIsOnIOThread();
michael@0 3626
michael@0 3627 nsCOMPtr<nsIFile> directory;
michael@0 3628 nsresult rv = aQuotaManager->GetDirectoryForOrigin(aPersistenceType, mOrigin,
michael@0 3629 getter_AddRefs(directory));
michael@0 3630 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3631
michael@0 3632 bool exists;
michael@0 3633 rv = directory->Exists(&exists);
michael@0 3634 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3635
michael@0 3636 // If the directory exists then enumerate all the files inside, adding up
michael@0 3637 // the sizes to get the final usage statistic.
michael@0 3638 if (exists && !mCanceled) {
michael@0 3639 bool initialized;
michael@0 3640
michael@0 3641 if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) {
michael@0 3642 initialized = aQuotaManager->mInitializedOrigins.Contains(mOrigin);
michael@0 3643
michael@0 3644 if (!initialized) {
michael@0 3645 rv = MaybeUpgradeOriginDirectory(directory);
michael@0 3646 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3647 }
michael@0 3648 }
michael@0 3649 else {
michael@0 3650 NS_ASSERTION(aPersistenceType == PERSISTENCE_TYPE_TEMPORARY, "Huh?");
michael@0 3651 initialized = aQuotaManager->mTemporaryStorageInitialized;
michael@0 3652 }
michael@0 3653
michael@0 3654 nsCOMPtr<nsISimpleEnumerator> entries;
michael@0 3655 rv = directory->GetDirectoryEntries(getter_AddRefs(entries));
michael@0 3656 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3657
michael@0 3658 bool hasMore;
michael@0 3659 while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) &&
michael@0 3660 hasMore && !mCanceled) {
michael@0 3661 nsCOMPtr<nsISupports> entry;
michael@0 3662 rv = entries->GetNext(getter_AddRefs(entry));
michael@0 3663 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3664
michael@0 3665 nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
michael@0 3666 NS_ENSURE_TRUE(file, NS_NOINTERFACE);
michael@0 3667
michael@0 3668 nsString leafName;
michael@0 3669 rv = file->GetLeafName(leafName);
michael@0 3670 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3671
michael@0 3672 if (leafName.EqualsLiteral(METADATA_FILE_NAME) ||
michael@0 3673 leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
michael@0 3674 continue;
michael@0 3675 }
michael@0 3676
michael@0 3677 if (!initialized) {
michael@0 3678 bool isDirectory;
michael@0 3679 rv = file->IsDirectory(&isDirectory);
michael@0 3680 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3681
michael@0 3682 if (!isDirectory) {
michael@0 3683 NS_WARNING("Unknown file found!");
michael@0 3684 return NS_ERROR_UNEXPECTED;
michael@0 3685 }
michael@0 3686 }
michael@0 3687
michael@0 3688 Client::Type clientType;
michael@0 3689 rv = Client::TypeFromText(leafName, clientType);
michael@0 3690 if (NS_FAILED(rv)) {
michael@0 3691 NS_WARNING("Unknown directory found!");
michael@0 3692 if (!initialized) {
michael@0 3693 return NS_ERROR_UNEXPECTED;
michael@0 3694 }
michael@0 3695 continue;
michael@0 3696 }
michael@0 3697
michael@0 3698 nsRefPtr<Client>& client = aQuotaManager->mClients[clientType];
michael@0 3699
michael@0 3700 if (initialized) {
michael@0 3701 rv = client->GetUsageForOrigin(aPersistenceType, mGroup, mOrigin, this);
michael@0 3702 }
michael@0 3703 else {
michael@0 3704 rv = client->InitOrigin(aPersistenceType, mGroup, mOrigin, this);
michael@0 3705 }
michael@0 3706 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3707 }
michael@0 3708 }
michael@0 3709
michael@0 3710 return NS_OK;
michael@0 3711 }
michael@0 3712
michael@0 3713 NS_IMPL_ISUPPORTS_INHERITED(AsyncUsageRunnable, nsRunnable, nsIQuotaRequest)
michael@0 3714
michael@0 3715 NS_IMETHODIMP
michael@0 3716 AsyncUsageRunnable::Run()
michael@0 3717 {
michael@0 3718 PROFILER_LABEL("Quota", "AsyncUsageRunnable::Run");
michael@0 3719
michael@0 3720 nsresult rv = RunInternal();
michael@0 3721
michael@0 3722 if (!NS_IsMainThread()) {
michael@0 3723 if (NS_FAILED(rv)) {
michael@0 3724 ResetUsage();
michael@0 3725 }
michael@0 3726
michael@0 3727 if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) {
michael@0 3728 NS_WARNING("Failed to dispatch to main thread!");
michael@0 3729 }
michael@0 3730 }
michael@0 3731
michael@0 3732 return NS_OK;
michael@0 3733 }
michael@0 3734
michael@0 3735 NS_IMETHODIMP
michael@0 3736 AsyncUsageRunnable::Cancel()
michael@0 3737 {
michael@0 3738 if (mCanceled.exchange(true)) {
michael@0 3739 NS_WARNING("Canceled more than once?!");
michael@0 3740 return NS_ERROR_UNEXPECTED;
michael@0 3741 }
michael@0 3742
michael@0 3743 return NS_OK;
michael@0 3744 }
michael@0 3745
michael@0 3746 nsresult
michael@0 3747 ResetOrClearRunnable::OnExclusiveAccessAcquired()
michael@0 3748 {
michael@0 3749 QuotaManager* quotaManager = QuotaManager::Get();
michael@0 3750 NS_ASSERTION(quotaManager, "This should never fail!");
michael@0 3751
michael@0 3752 nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
michael@0 3753 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3754
michael@0 3755 return NS_OK;
michael@0 3756 }
michael@0 3757
michael@0 3758 // static
michael@0 3759 void
michael@0 3760 ResetOrClearRunnable::InvalidateOpenedStorages(
michael@0 3761 nsTArray<nsCOMPtr<nsIOfflineStorage> >& aStorages,
michael@0 3762 void* aClosure)
michael@0 3763 {
michael@0 3764 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 3765
michael@0 3766 nsTArray<nsCOMPtr<nsIOfflineStorage> > storages;
michael@0 3767 storages.SwapElements(aStorages);
michael@0 3768
michael@0 3769 for (uint32_t index = 0; index < storages.Length(); index++) {
michael@0 3770 storages[index]->Invalidate();
michael@0 3771 }
michael@0 3772 }
michael@0 3773
michael@0 3774 void
michael@0 3775 ResetOrClearRunnable::DeleteFiles(QuotaManager* aQuotaManager,
michael@0 3776 PersistenceType aPersistenceType)
michael@0 3777 {
michael@0 3778 AssertIsOnIOThread();
michael@0 3779 NS_ASSERTION(aQuotaManager, "Don't pass me null!");
michael@0 3780
michael@0 3781 nsresult rv;
michael@0 3782
michael@0 3783 nsCOMPtr<nsIFile> directory =
michael@0 3784 do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
michael@0 3785 NS_ENSURE_SUCCESS_VOID(rv);
michael@0 3786
michael@0 3787 rv = directory->InitWithPath(aQuotaManager->GetStoragePath(aPersistenceType));
michael@0 3788 NS_ENSURE_SUCCESS_VOID(rv);
michael@0 3789
michael@0 3790 rv = directory->Remove(true);
michael@0 3791 if (rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST &&
michael@0 3792 rv != NS_ERROR_FILE_NOT_FOUND && NS_FAILED(rv)) {
michael@0 3793 // This should never fail if we've closed all storage connections
michael@0 3794 // correctly...
michael@0 3795 NS_ERROR("Failed to remove directory!");
michael@0 3796 }
michael@0 3797 }
michael@0 3798
michael@0 3799 NS_IMPL_ISUPPORTS_INHERITED0(ResetOrClearRunnable, nsRunnable)
michael@0 3800
michael@0 3801 NS_IMETHODIMP
michael@0 3802 ResetOrClearRunnable::Run()
michael@0 3803 {
michael@0 3804 QuotaManager* quotaManager = QuotaManager::Get();
michael@0 3805 NS_ASSERTION(quotaManager, "This should never fail!");
michael@0 3806
michael@0 3807 switch (mCallbackState) {
michael@0 3808 case Pending: {
michael@0 3809 NS_NOTREACHED("Should never get here without being dispatched!");
michael@0 3810 return NS_ERROR_UNEXPECTED;
michael@0 3811 }
michael@0 3812
michael@0 3813 case OpenAllowed: {
michael@0 3814 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 3815
michael@0 3816 AdvanceState();
michael@0 3817
michael@0 3818 // Now we have to wait until the thread pool is done with all of the
michael@0 3819 // storages we care about.
michael@0 3820 nsresult rv =
michael@0 3821 quotaManager->AcquireExclusiveAccess(NullCString(),
michael@0 3822 Nullable<PersistenceType>(), this,
michael@0 3823 InvalidateOpenedStorages, nullptr);
michael@0 3824 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3825
michael@0 3826 return NS_OK;
michael@0 3827 }
michael@0 3828
michael@0 3829 case IO: {
michael@0 3830 AssertIsOnIOThread();
michael@0 3831
michael@0 3832 AdvanceState();
michael@0 3833
michael@0 3834 if (mClear) {
michael@0 3835 DeleteFiles(quotaManager, PERSISTENCE_TYPE_PERSISTENT);
michael@0 3836 DeleteFiles(quotaManager, PERSISTENCE_TYPE_TEMPORARY);
michael@0 3837 }
michael@0 3838
michael@0 3839 quotaManager->RemoveQuota();
michael@0 3840 quotaManager->ResetOrClearCompleted();
michael@0 3841
michael@0 3842 // Now dispatch back to the main thread.
michael@0 3843 if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) {
michael@0 3844 NS_WARNING("Failed to dispatch to main thread!");
michael@0 3845 return NS_ERROR_FAILURE;
michael@0 3846 }
michael@0 3847
michael@0 3848 return NS_OK;
michael@0 3849 }
michael@0 3850
michael@0 3851 case Complete: {
michael@0 3852 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 3853
michael@0 3854 // Tell the QuotaManager that we're done.
michael@0 3855 quotaManager->AllowNextSynchronizedOp(OriginOrPatternString::FromNull(),
michael@0 3856 Nullable<PersistenceType>(),
michael@0 3857 EmptyCString());
michael@0 3858
michael@0 3859 return NS_OK;
michael@0 3860 }
michael@0 3861
michael@0 3862 default:
michael@0 3863 NS_ERROR("Unknown state value!");
michael@0 3864 return NS_ERROR_UNEXPECTED;
michael@0 3865 }
michael@0 3866
michael@0 3867 NS_NOTREACHED("Should never get here!");
michael@0 3868 return NS_ERROR_UNEXPECTED;
michael@0 3869 }
michael@0 3870
michael@0 3871 NS_IMETHODIMP
michael@0 3872 FinalizeOriginEvictionRunnable::Run()
michael@0 3873 {
michael@0 3874 QuotaManager* quotaManager = QuotaManager::Get();
michael@0 3875 NS_ASSERTION(quotaManager, "This should never fail!");
michael@0 3876
michael@0 3877 nsresult rv;
michael@0 3878
michael@0 3879 switch (mCallbackState) {
michael@0 3880 case Pending: {
michael@0 3881 NS_NOTREACHED("Should never get here without being dispatched!");
michael@0 3882 return NS_ERROR_UNEXPECTED;
michael@0 3883 }
michael@0 3884
michael@0 3885 case OpenAllowed: {
michael@0 3886 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 3887
michael@0 3888 AdvanceState();
michael@0 3889
michael@0 3890 rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
michael@0 3891 if (NS_FAILED(rv)) {
michael@0 3892 NS_WARNING("Failed to dispatch to the IO thread!");
michael@0 3893 }
michael@0 3894
michael@0 3895 return NS_OK;
michael@0 3896 }
michael@0 3897
michael@0 3898 case IO: {
michael@0 3899 AssertIsOnIOThread();
michael@0 3900
michael@0 3901 AdvanceState();
michael@0 3902
michael@0 3903 for (uint32_t index = 0; index < mOrigins.Length(); index++) {
michael@0 3904 quotaManager->OriginClearCompleted(
michael@0 3905 PERSISTENCE_TYPE_TEMPORARY,
michael@0 3906 OriginOrPatternString::FromOrigin(mOrigins[index]));
michael@0 3907 }
michael@0 3908
michael@0 3909 if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) {
michael@0 3910 NS_WARNING("Failed to dispatch to main thread!");
michael@0 3911 return NS_ERROR_FAILURE;
michael@0 3912 }
michael@0 3913
michael@0 3914 return NS_OK;
michael@0 3915 }
michael@0 3916
michael@0 3917 case Complete: {
michael@0 3918 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 3919
michael@0 3920 for (uint32_t index = 0; index < mOrigins.Length(); index++) {
michael@0 3921 quotaManager->AllowNextSynchronizedOp(
michael@0 3922 OriginOrPatternString::FromOrigin(mOrigins[index]),
michael@0 3923 Nullable<PersistenceType>(PERSISTENCE_TYPE_TEMPORARY),
michael@0 3924 EmptyCString());
michael@0 3925 }
michael@0 3926
michael@0 3927 return NS_OK;
michael@0 3928 }
michael@0 3929
michael@0 3930 default:
michael@0 3931 NS_ERROR("Unknown state value!");
michael@0 3932 return NS_ERROR_UNEXPECTED;
michael@0 3933 }
michael@0 3934
michael@0 3935 NS_NOTREACHED("Should never get here!");
michael@0 3936 return NS_ERROR_UNEXPECTED;
michael@0 3937 }
michael@0 3938
michael@0 3939 nsresult
michael@0 3940 FinalizeOriginEvictionRunnable::Dispatch()
michael@0 3941 {
michael@0 3942 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
michael@0 3943 NS_ASSERTION(mCallbackState == Pending, "Huh?");
michael@0 3944
michael@0 3945 mCallbackState = OpenAllowed;
michael@0 3946 return NS_DispatchToMainThread(this);
michael@0 3947 }
michael@0 3948
michael@0 3949 nsresult
michael@0 3950 FinalizeOriginEvictionRunnable::RunImmediately()
michael@0 3951 {
michael@0 3952 AssertIsOnIOThread();
michael@0 3953 NS_ASSERTION(mCallbackState == Pending, "Huh?");
michael@0 3954
michael@0 3955 mCallbackState = IO;
michael@0 3956 return this->Run();
michael@0 3957 }
michael@0 3958
michael@0 3959 NS_IMETHODIMP
michael@0 3960 WaitForTransactionsToFinishRunnable::Run()
michael@0 3961 {
michael@0 3962 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 3963 NS_ASSERTION(mOp, "Null op!");
michael@0 3964 NS_ASSERTION(mOp->mListener, "Nothing to run!");
michael@0 3965 NS_ASSERTION(mCountdown, "Wrong countdown!");
michael@0 3966
michael@0 3967 if (--mCountdown) {
michael@0 3968 return NS_OK;
michael@0 3969 }
michael@0 3970
michael@0 3971 // Don't hold the listener alive longer than necessary.
michael@0 3972 nsRefPtr<AcquireListener> listener;
michael@0 3973 listener.swap(mOp->mListener);
michael@0 3974
michael@0 3975 mOp = nullptr;
michael@0 3976
michael@0 3977 nsresult rv = listener->OnExclusiveAccessAcquired();
michael@0 3978 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3979
michael@0 3980 // The listener is responsible for calling
michael@0 3981 // QuotaManager::AllowNextSynchronizedOp.
michael@0 3982 return NS_OK;
michael@0 3983 }
michael@0 3984
michael@0 3985 NS_IMETHODIMP
michael@0 3986 WaitForLockedFilesToFinishRunnable::Run()
michael@0 3987 {
michael@0 3988 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 3989
michael@0 3990 mBusy = false;
michael@0 3991
michael@0 3992 return NS_OK;
michael@0 3993 }
michael@0 3994
michael@0 3995 NS_IMETHODIMP
michael@0 3996 SaveOriginAccessTimeRunnable::Run()
michael@0 3997 {
michael@0 3998 AssertIsOnIOThread();
michael@0 3999
michael@0 4000 QuotaManager* quotaManager = QuotaManager::Get();
michael@0 4001 NS_ASSERTION(quotaManager, "This should never fail!");
michael@0 4002
michael@0 4003 nsCOMPtr<nsIFile> directory;
michael@0 4004 nsresult rv =
michael@0 4005 quotaManager->GetDirectoryForOrigin(PERSISTENCE_TYPE_TEMPORARY, mOrigin,
michael@0 4006 getter_AddRefs(directory));
michael@0 4007 NS_ENSURE_SUCCESS(rv, rv);
michael@0 4008
michael@0 4009 nsCOMPtr<nsIBinaryOutputStream> stream;
michael@0 4010 rv = GetDirectoryMetadataStream(directory, true, getter_AddRefs(stream));
michael@0 4011 NS_ENSURE_SUCCESS(rv, rv);
michael@0 4012
michael@0 4013 // The origin directory may not exist anymore.
michael@0 4014 if (stream) {
michael@0 4015 rv = stream->Write64(mTimestamp);
michael@0 4016 NS_ENSURE_SUCCESS(rv, rv);
michael@0 4017 }
michael@0 4018
michael@0 4019 return NS_OK;
michael@0 4020 }

mercurial