dom/quota/QuotaManager.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/dom/quota/QuotaManager.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,4020 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim: set ts=2 et sw=2 tw=80: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     1.8 + * You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "QuotaManager.h"
    1.11 +
    1.12 +#include "mozIApplicationClearPrivateDataParams.h"
    1.13 +#include "nsIBinaryInputStream.h"
    1.14 +#include "nsIBinaryOutputStream.h"
    1.15 +#include "nsIFile.h"
    1.16 +#include "nsIObserverService.h"
    1.17 +#include "nsIOfflineStorage.h"
    1.18 +#include "nsIPrincipal.h"
    1.19 +#include "nsIQuotaRequest.h"
    1.20 +#include "nsIRunnable.h"
    1.21 +#include "nsISimpleEnumerator.h"
    1.22 +#include "nsIScriptObjectPrincipal.h"
    1.23 +#include "nsIScriptSecurityManager.h"
    1.24 +#include "nsITimer.h"
    1.25 +#include "nsIURI.h"
    1.26 +#include "nsIUsageCallback.h"
    1.27 +
    1.28 +#include <algorithm>
    1.29 +#include "GeckoProfiler.h"
    1.30 +#include "mozilla/Atomics.h"
    1.31 +#include "mozilla/CondVar.h"
    1.32 +#include "mozilla/dom/asmjscache/AsmJSCache.h"
    1.33 +#include "mozilla/dom/file/FileService.h"
    1.34 +#include "mozilla/dom/indexedDB/Client.h"
    1.35 +#include "mozilla/Mutex.h"
    1.36 +#include "mozilla/LazyIdleThread.h"
    1.37 +#include "mozilla/Preferences.h"
    1.38 +#include "mozilla/Services.h"
    1.39 +#include "nsAppDirectoryServiceDefs.h"
    1.40 +#include "nsComponentManagerUtils.h"
    1.41 +#include "nsContentUtils.h"
    1.42 +#include "nsCRTGlue.h"
    1.43 +#include "nsDirectoryServiceUtils.h"
    1.44 +#include "nsNetUtil.h"
    1.45 +#include "nsScriptSecurityManager.h"
    1.46 +#include "nsThreadUtils.h"
    1.47 +#include "nsXULAppAPI.h"
    1.48 +#include "xpcpublic.h"
    1.49 +
    1.50 +#include "AcquireListener.h"
    1.51 +#include "CheckQuotaHelper.h"
    1.52 +#include "OriginCollection.h"
    1.53 +#include "OriginOrPatternString.h"
    1.54 +#include "QuotaObject.h"
    1.55 +#include "StorageMatcher.h"
    1.56 +#include "UsageInfo.h"
    1.57 +#include "Utilities.h"
    1.58 +
    1.59 +// The amount of time, in milliseconds, that our IO thread will stay alive
    1.60 +// after the last event it processes.
    1.61 +#define DEFAULT_THREAD_TIMEOUT_MS 30000
    1.62 +
    1.63 +// The amount of time, in milliseconds, that we will wait for active storage
    1.64 +// transactions on shutdown before aborting them.
    1.65 +#define DEFAULT_SHUTDOWN_TIMER_MS 30000
    1.66 +
    1.67 +// Preference that users can set to override DEFAULT_QUOTA_MB
    1.68 +#define PREF_STORAGE_QUOTA "dom.indexedDB.warningQuota"
    1.69 +
    1.70 +// Preference that users can set to override temporary storage smart limit
    1.71 +// calculation.
    1.72 +#define PREF_FIXED_LIMIT "dom.quotaManager.temporaryStorage.fixedLimit"
    1.73 +#define PREF_CHUNK_SIZE "dom.quotaManager.temporaryStorage.chunkSize"
    1.74 +
    1.75 +// Preference that is used to enable testing features
    1.76 +#define PREF_TESTING_FEATURES "dom.quotaManager.testing"
    1.77 +
    1.78 +// profile-before-change, when we need to shut down quota manager
    1.79 +#define PROFILE_BEFORE_CHANGE_OBSERVER_ID "profile-before-change"
    1.80 +
    1.81 +// The name of the file that we use to load/save the last access time of an
    1.82 +// origin.
    1.83 +#define METADATA_FILE_NAME ".metadata"
    1.84 +
    1.85 +#define PERMISSION_DEFAUT_PERSISTENT_STORAGE "default-persistent-storage"
    1.86 +
    1.87 +#define KB * 1024ULL
    1.88 +#define MB * 1024ULL KB
    1.89 +#define GB * 1024ULL MB
    1.90 +
    1.91 +USING_QUOTA_NAMESPACE
    1.92 +using namespace mozilla::dom;
    1.93 +using mozilla::dom::file::FileService;
    1.94 +
    1.95 +static_assert(
    1.96 +  static_cast<uint32_t>(StorageType::Persistent) ==
    1.97 +  static_cast<uint32_t>(PERSISTENCE_TYPE_PERSISTENT),
    1.98 +  "Enum values should match.");
    1.99 +
   1.100 +static_assert(
   1.101 +  static_cast<uint32_t>(StorageType::Temporary) ==
   1.102 +  static_cast<uint32_t>(PERSISTENCE_TYPE_TEMPORARY),
   1.103 +  "Enum values should match.");
   1.104 +
   1.105 +BEGIN_QUOTA_NAMESPACE
   1.106 +
   1.107 +// A struct that contains the information corresponding to a pending or
   1.108 +// running operation that requires synchronization (e.g. opening a db,
   1.109 +// clearing dbs for an origin, etc).
   1.110 +struct SynchronizedOp
   1.111 +{
   1.112 +  SynchronizedOp(const OriginOrPatternString& aOriginOrPattern,
   1.113 +                 Nullable<PersistenceType> aPersistenceType,
   1.114 +                 const nsACString& aId);
   1.115 +
   1.116 +  ~SynchronizedOp();
   1.117 +
   1.118 +  // Test whether this SynchronizedOp needs to wait for the given op.
   1.119 +  bool
   1.120 +  MustWaitFor(const SynchronizedOp& aOp);
   1.121 +
   1.122 +  void
   1.123 +  DelayRunnable(nsIRunnable* aRunnable);
   1.124 +
   1.125 +  void
   1.126 +  DispatchDelayedRunnables();
   1.127 +
   1.128 +  const OriginOrPatternString mOriginOrPattern;
   1.129 +  Nullable<PersistenceType> mPersistenceType;
   1.130 +  nsCString mId;
   1.131 +  nsRefPtr<AcquireListener> mListener;
   1.132 +  nsTArray<nsCOMPtr<nsIRunnable> > mDelayedRunnables;
   1.133 +  ArrayCluster<nsIOfflineStorage*> mStorages;
   1.134 +};
   1.135 +
   1.136 +class CollectOriginsHelper MOZ_FINAL : public nsRunnable
   1.137 +{
   1.138 +public:
   1.139 +  CollectOriginsHelper(mozilla::Mutex& aMutex, uint64_t aMinSizeToBeFreed);
   1.140 +
   1.141 +  NS_IMETHOD
   1.142 +  Run();
   1.143 +
   1.144 +  // Blocks the current thread until origins are collected on the main thread.
   1.145 +  // The returned value contains an aggregate size of those origins.
   1.146 +  int64_t
   1.147 +  BlockAndReturnOriginsForEviction(nsTArray<OriginInfo*>& aOriginInfos);
   1.148 +
   1.149 +private:
   1.150 +  ~CollectOriginsHelper()
   1.151 +  { }
   1.152 +
   1.153 +  uint64_t mMinSizeToBeFreed;
   1.154 +
   1.155 +  mozilla::Mutex& mMutex;
   1.156 +  mozilla::CondVar mCondVar;
   1.157 +
   1.158 +  // The members below are protected by mMutex.
   1.159 +  nsTArray<OriginInfo*> mOriginInfos;
   1.160 +  uint64_t mSizeToBeFreed;
   1.161 +  bool mWaiting;
   1.162 +};
   1.163 +
   1.164 +// Responsible for clearing the storage files for a particular origin on the
   1.165 +// IO thread. Created when nsIQuotaManager::ClearStoragesForURI is called.
   1.166 +// Runs three times, first on the main thread, next on the IO thread, and then
   1.167 +// finally again on the main thread. While on the IO thread the runnable will
   1.168 +// actually remove the origin's storage files and the directory that contains
   1.169 +// them before dispatching itself back to the main thread. When back on the main
   1.170 +// thread the runnable will notify the QuotaManager that the job has been
   1.171 +// completed.
   1.172 +class OriginClearRunnable MOZ_FINAL : public nsRunnable,
   1.173 +                                      public AcquireListener
   1.174 +{
   1.175 +  enum CallbackState {
   1.176 +    // Not yet run.
   1.177 +    Pending = 0,
   1.178 +
   1.179 +    // Running on the main thread in the callback for OpenAllowed.
   1.180 +    OpenAllowed,
   1.181 +
   1.182 +    // Running on the IO thread.
   1.183 +    IO,
   1.184 +
   1.185 +    // Running on the main thread after all work is done.
   1.186 +    Complete
   1.187 +  };
   1.188 +
   1.189 +public:
   1.190 +  NS_DECL_ISUPPORTS_INHERITED
   1.191 +
   1.192 +  OriginClearRunnable(const OriginOrPatternString& aOriginOrPattern,
   1.193 +                      Nullable<PersistenceType> aPersistenceType)
   1.194 +  : mOriginOrPattern(aOriginOrPattern),
   1.195 +    mPersistenceType(aPersistenceType),
   1.196 +    mCallbackState(Pending)
   1.197 +  { }
   1.198 +
   1.199 +  NS_IMETHOD
   1.200 +  Run();
   1.201 +
   1.202 +  // AcquireListener override
   1.203 +  virtual nsresult
   1.204 +  OnExclusiveAccessAcquired() MOZ_OVERRIDE;
   1.205 +
   1.206 +  void
   1.207 +  AdvanceState()
   1.208 +  {
   1.209 +    switch (mCallbackState) {
   1.210 +      case Pending:
   1.211 +        mCallbackState = OpenAllowed;
   1.212 +        return;
   1.213 +      case OpenAllowed:
   1.214 +        mCallbackState = IO;
   1.215 +        return;
   1.216 +      case IO:
   1.217 +        mCallbackState = Complete;
   1.218 +        return;
   1.219 +      default:
   1.220 +        NS_NOTREACHED("Can't advance past Complete!");
   1.221 +    }
   1.222 +  }
   1.223 +
   1.224 +  static void
   1.225 +  InvalidateOpenedStorages(nsTArray<nsCOMPtr<nsIOfflineStorage> >& aStorages,
   1.226 +                           void* aClosure);
   1.227 +
   1.228 +  void
   1.229 +  DeleteFiles(QuotaManager* aQuotaManager,
   1.230 +              PersistenceType aPersistenceType);
   1.231 +
   1.232 +private:
   1.233 +  OriginOrPatternString mOriginOrPattern;
   1.234 +  Nullable<PersistenceType> mPersistenceType;
   1.235 +  CallbackState mCallbackState;
   1.236 +};
   1.237 +
   1.238 +// Responsible for calculating the amount of space taken up by storages of a
   1.239 +// certain origin. Created when nsIQuotaManager::GetUsageForURI is called.
   1.240 +// May be canceled with nsIQuotaRequest::Cancel. Runs three times, first
   1.241 +// on the main thread, next on the IO thread, and then finally again on the main
   1.242 +// thread. While on the IO thread the runnable will calculate the size of all
   1.243 +// files in the origin's directory before dispatching itself back to the main
   1.244 +// thread. When on the main thread the runnable will call the callback and then
   1.245 +// notify the QuotaManager that the job has been completed.
   1.246 +class AsyncUsageRunnable MOZ_FINAL : public UsageInfo,
   1.247 +                                     public nsRunnable,
   1.248 +                                     public nsIQuotaRequest
   1.249 +{
   1.250 +  enum CallbackState {
   1.251 +    // Not yet run.
   1.252 +    Pending = 0,
   1.253 +
   1.254 +    // Running on the main thread in the callback for OpenAllowed.
   1.255 +    OpenAllowed,
   1.256 +
   1.257 +    // Running on the IO thread.
   1.258 +    IO,
   1.259 +
   1.260 +    // Running on the main thread after all work is done.
   1.261 +    Complete,
   1.262 +
   1.263 +    // Running on the main thread after skipping the work
   1.264 +    Shortcut
   1.265 +  };
   1.266 +
   1.267 +public:
   1.268 +  NS_DECL_ISUPPORTS_INHERITED
   1.269 +  NS_DECL_NSIQUOTAREQUEST
   1.270 +
   1.271 +  AsyncUsageRunnable(uint32_t aAppId,
   1.272 +                     bool aInMozBrowserOnly,
   1.273 +                     const nsACString& aGroup,
   1.274 +                     const OriginOrPatternString& aOrigin,
   1.275 +                     nsIURI* aURI,
   1.276 +                     nsIUsageCallback* aCallback);
   1.277 +
   1.278 +  NS_IMETHOD
   1.279 +  Run();
   1.280 +
   1.281 +  void
   1.282 +  AdvanceState()
   1.283 +  {
   1.284 +    switch (mCallbackState) {
   1.285 +      case Pending:
   1.286 +        mCallbackState = OpenAllowed;
   1.287 +        return;
   1.288 +      case OpenAllowed:
   1.289 +        mCallbackState = IO;
   1.290 +        return;
   1.291 +      case IO:
   1.292 +        mCallbackState = Complete;
   1.293 +        return;
   1.294 +      default:
   1.295 +        NS_NOTREACHED("Can't advance past Complete!");
   1.296 +    }
   1.297 +  }
   1.298 +
   1.299 +  nsresult
   1.300 +  TakeShortcut();
   1.301 +
   1.302 +private:
   1.303 +  // Run calls the RunInternal method and makes sure that we always dispatch
   1.304 +  // to the main thread in case of an error.
   1.305 +  inline nsresult
   1.306 +  RunInternal();
   1.307 +
   1.308 +  nsresult
   1.309 +  AddToUsage(QuotaManager* aQuotaManager,
   1.310 +             PersistenceType aPersistenceType);
   1.311 +
   1.312 +  nsCOMPtr<nsIURI> mURI;
   1.313 +  nsCOMPtr<nsIUsageCallback> mCallback;
   1.314 +  uint32_t mAppId;
   1.315 +  nsCString mGroup;
   1.316 +  OriginOrPatternString mOrigin;
   1.317 +  CallbackState mCallbackState;
   1.318 +  bool mInMozBrowserOnly;
   1.319 +};
   1.320 +
   1.321 +class ResetOrClearRunnable MOZ_FINAL : public nsRunnable,
   1.322 +                                       public AcquireListener
   1.323 +{
   1.324 +  enum CallbackState {
   1.325 +    // Not yet run.
   1.326 +    Pending = 0,
   1.327 +
   1.328 +    // Running on the main thread in the callback for OpenAllowed.
   1.329 +    OpenAllowed,
   1.330 +
   1.331 +    // Running on the IO thread.
   1.332 +    IO,
   1.333 +
   1.334 +    // Running on the main thread after all work is done.
   1.335 +    Complete
   1.336 +  };
   1.337 +
   1.338 +public:
   1.339 +  NS_DECL_ISUPPORTS_INHERITED
   1.340 +
   1.341 +  ResetOrClearRunnable(bool aClear)
   1.342 +  : mCallbackState(Pending),
   1.343 +    mClear(aClear)
   1.344 +  { }
   1.345 +
   1.346 +  NS_IMETHOD
   1.347 +  Run();
   1.348 +
   1.349 +  // AcquireListener override
   1.350 +  virtual nsresult
   1.351 +  OnExclusiveAccessAcquired() MOZ_OVERRIDE;
   1.352 +
   1.353 +  void
   1.354 +  AdvanceState()
   1.355 +  {
   1.356 +    switch (mCallbackState) {
   1.357 +      case Pending:
   1.358 +        mCallbackState = OpenAllowed;
   1.359 +        return;
   1.360 +      case OpenAllowed:
   1.361 +        mCallbackState = IO;
   1.362 +        return;
   1.363 +      case IO:
   1.364 +        mCallbackState = Complete;
   1.365 +        return;
   1.366 +      default:
   1.367 +        NS_NOTREACHED("Can't advance past Complete!");
   1.368 +    }
   1.369 +  }
   1.370 +
   1.371 +  static void
   1.372 +  InvalidateOpenedStorages(nsTArray<nsCOMPtr<nsIOfflineStorage> >& aStorages,
   1.373 +                           void* aClosure);
   1.374 +
   1.375 +  void
   1.376 +  DeleteFiles(QuotaManager* aQuotaManager,
   1.377 +              PersistenceType aPersistenceType);
   1.378 +
   1.379 +private:
   1.380 +  CallbackState mCallbackState;
   1.381 +  bool mClear;
   1.382 +};
   1.383 +
   1.384 +// Responsible for finalizing eviction of certian origins (storage files have
   1.385 +// been already cleared, we just need to release IO thread only objects and
   1.386 +// allow next synchronized ops for evicted origins). Created when
   1.387 +// QuotaManager::FinalizeOriginEviction is called. Runs three times, first
   1.388 +// on the main thread, next on the IO thread, and then finally again on the main
   1.389 +// thread. While on the IO thread the runnable will release IO thread only
   1.390 +// objects before dispatching itself back to the main thread. When back on the
   1.391 +// main thread the runnable will call QuotaManager::AllowNextSynchronizedOp.
   1.392 +// The runnable can also run in a shortened mode (runs only twice).
   1.393 +class FinalizeOriginEvictionRunnable MOZ_FINAL : public nsRunnable
   1.394 +{
   1.395 +  enum CallbackState {
   1.396 +    // Not yet run.
   1.397 +    Pending = 0,
   1.398 +
   1.399 +    // Running on the main thread in the callback for OpenAllowed.
   1.400 +    OpenAllowed,
   1.401 +
   1.402 +    // Running on the IO thread.
   1.403 +    IO,
   1.404 +
   1.405 +    // Running on the main thread after IO work is done.
   1.406 +    Complete
   1.407 +  };
   1.408 +
   1.409 +public:
   1.410 +  FinalizeOriginEvictionRunnable(nsTArray<nsCString>& aOrigins)
   1.411 +  : mCallbackState(Pending)
   1.412 +  {
   1.413 +    mOrigins.SwapElements(aOrigins);
   1.414 +  }
   1.415 +
   1.416 +  NS_IMETHOD
   1.417 +  Run();
   1.418 +
   1.419 +  void
   1.420 +  AdvanceState()
   1.421 +  {
   1.422 +    switch (mCallbackState) {
   1.423 +      case Pending:
   1.424 +        mCallbackState = OpenAllowed;
   1.425 +        return;
   1.426 +      case OpenAllowed:
   1.427 +        mCallbackState = IO;
   1.428 +        return;
   1.429 +      case IO:
   1.430 +        mCallbackState = Complete;
   1.431 +        return;
   1.432 +      default:
   1.433 +        MOZ_ASSUME_UNREACHABLE("Can't advance past Complete!");
   1.434 +    }
   1.435 +  }
   1.436 +
   1.437 +  nsresult
   1.438 +  Dispatch();
   1.439 +
   1.440 +  nsresult
   1.441 +  RunImmediately();
   1.442 +
   1.443 +private:
   1.444 +  CallbackState mCallbackState;
   1.445 +  nsTArray<nsCString> mOrigins;
   1.446 +};
   1.447 +
   1.448 +bool
   1.449 +IsOnIOThread()
   1.450 +{
   1.451 +  QuotaManager* quotaManager = QuotaManager::Get();
   1.452 +  NS_ASSERTION(quotaManager, "Must have a manager here!");
   1.453 +
   1.454 +  bool currentThread;
   1.455 +  return NS_SUCCEEDED(quotaManager->IOThread()->
   1.456 +                      IsOnCurrentThread(&currentThread)) && currentThread;
   1.457 +}
   1.458 +
   1.459 +void
   1.460 +AssertIsOnIOThread()
   1.461 +{
   1.462 +  NS_ASSERTION(IsOnIOThread(), "Running on the wrong thread!");
   1.463 +}
   1.464 +
   1.465 +void
   1.466 +AssertCurrentThreadOwnsQuotaMutex()
   1.467 +{
   1.468 +#ifdef DEBUG
   1.469 +  QuotaManager* quotaManager = QuotaManager::Get();
   1.470 +  NS_ASSERTION(quotaManager, "Must have a manager here!");
   1.471 +
   1.472 +  quotaManager->AssertCurrentThreadOwnsQuotaMutex();
   1.473 +#endif
   1.474 +}
   1.475 +
   1.476 +END_QUOTA_NAMESPACE
   1.477 +
   1.478 +namespace {
   1.479 +
   1.480 +// Amount of space that storages may use by default in megabytes.
   1.481 +static const int32_t  kDefaultQuotaMB =             50;
   1.482 +
   1.483 +
   1.484 +QuotaManager* gInstance = nullptr;
   1.485 +mozilla::Atomic<bool> gShutdown(false);
   1.486 +
   1.487 +int32_t gStorageQuotaMB = kDefaultQuotaMB;
   1.488 +
   1.489 +// Constants for temporary storage limit computing.
   1.490 +static const int32_t kDefaultFixedLimitKB = -1;
   1.491 +static const uint32_t kDefaultChunkSizeKB = 10 * 1024;
   1.492 +int32_t gFixedLimitKB = kDefaultFixedLimitKB;
   1.493 +uint32_t gChunkSizeKB = kDefaultChunkSizeKB;
   1.494 +
   1.495 +bool gTestingEnabled = false;
   1.496 +
   1.497 +// A callback runnable used by the TransactionPool when it's safe to proceed
   1.498 +// with a SetVersion/DeleteDatabase/etc.
   1.499 +class WaitForTransactionsToFinishRunnable MOZ_FINAL : public nsRunnable
   1.500 +{
   1.501 +public:
   1.502 +  WaitForTransactionsToFinishRunnable(SynchronizedOp* aOp)
   1.503 +  : mOp(aOp), mCountdown(1)
   1.504 +  {
   1.505 +    NS_ASSERTION(mOp, "Why don't we have a runnable?");
   1.506 +    NS_ASSERTION(mOp->mStorages.IsEmpty(), "We're here too early!");
   1.507 +    NS_ASSERTION(mOp->mListener,
   1.508 +                 "What are we supposed to do when we're done?");
   1.509 +    NS_ASSERTION(mCountdown, "Wrong countdown!");
   1.510 +  }
   1.511 +
   1.512 +  NS_IMETHOD
   1.513 +  Run();
   1.514 +
   1.515 +  void
   1.516 +  AddRun()
   1.517 +  {
   1.518 +    mCountdown++;
   1.519 +  }
   1.520 +
   1.521 +private:
   1.522 +  // The QuotaManager holds this alive.
   1.523 +  SynchronizedOp* mOp;
   1.524 +  uint32_t mCountdown;
   1.525 +};
   1.526 +
   1.527 +class WaitForLockedFilesToFinishRunnable MOZ_FINAL : public nsRunnable
   1.528 +{
   1.529 +public:
   1.530 +  WaitForLockedFilesToFinishRunnable()
   1.531 +  : mBusy(true)
   1.532 +  { }
   1.533 +
   1.534 +  NS_IMETHOD
   1.535 +  Run();
   1.536 +
   1.537 +  bool
   1.538 +  IsBusy() const
   1.539 +  {
   1.540 +    return mBusy;
   1.541 +  }
   1.542 +
   1.543 +private:
   1.544 +  bool mBusy;
   1.545 +};
   1.546 +
   1.547 +class SaveOriginAccessTimeRunnable MOZ_FINAL : public nsRunnable
   1.548 +{
   1.549 +public:
   1.550 +  SaveOriginAccessTimeRunnable(const nsACString& aOrigin, int64_t aTimestamp)
   1.551 +  : mOrigin(aOrigin), mTimestamp(aTimestamp)
   1.552 +  { }
   1.553 +
   1.554 +  NS_IMETHOD
   1.555 +  Run();
   1.556 +
   1.557 +private:
   1.558 +  nsCString mOrigin;
   1.559 +  int64_t mTimestamp;
   1.560 +};
   1.561 +
   1.562 +struct MOZ_STACK_CLASS RemoveQuotaInfo
   1.563 +{
   1.564 +  RemoveQuotaInfo(PersistenceType aPersistenceType, const nsACString& aPattern)
   1.565 +  : persistenceType(aPersistenceType), pattern(aPattern)
   1.566 +  { }
   1.567 +
   1.568 +  PersistenceType persistenceType;
   1.569 +  nsCString pattern;
   1.570 +};
   1.571 +
   1.572 +struct MOZ_STACK_CLASS InactiveOriginsInfo
   1.573 +{
   1.574 +  InactiveOriginsInfo(OriginCollection& aCollection,
   1.575 +                      nsTArray<OriginInfo*>& aOrigins)
   1.576 +  : collection(aCollection), origins(aOrigins)
   1.577 +  { }
   1.578 +
   1.579 +  OriginCollection& collection;
   1.580 +  nsTArray<OriginInfo*>& origins;
   1.581 +};
   1.582 +
   1.583 +bool
   1.584 +IsMainProcess()
   1.585 +{
   1.586 +  return XRE_GetProcessType() == GeckoProcessType_Default;
   1.587 +}
   1.588 +
   1.589 +void
   1.590 +SanitizeOriginString(nsCString& aOrigin)
   1.591 +{
   1.592 +  // We want profiles to be platform-independent so we always need to replace
   1.593 +  // the same characters on every platform. Windows has the most extensive set
   1.594 +  // of illegal characters so we use its FILE_ILLEGAL_CHARACTERS and
   1.595 +  // FILE_PATH_SEPARATOR.
   1.596 +  static const char kReplaceChars[] = CONTROL_CHARACTERS "/:*?\"<>|\\";
   1.597 +
   1.598 +#ifdef XP_WIN
   1.599 +  NS_ASSERTION(!strcmp(kReplaceChars,
   1.600 +                       FILE_ILLEGAL_CHARACTERS FILE_PATH_SEPARATOR),
   1.601 +               "Illegal file characters have changed!");
   1.602 +#endif
   1.603 +
   1.604 +  aOrigin.ReplaceChar(kReplaceChars, '+');
   1.605 +}
   1.606 +
   1.607 +nsresult
   1.608 +EnsureDirectory(nsIFile* aDirectory, bool* aCreated)
   1.609 +{
   1.610 +  AssertIsOnIOThread();
   1.611 +
   1.612 +  nsresult rv = aDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
   1.613 +  if (rv == NS_ERROR_FILE_ALREADY_EXISTS) {
   1.614 +    bool isDirectory;
   1.615 +    rv = aDirectory->IsDirectory(&isDirectory);
   1.616 +    NS_ENSURE_SUCCESS(rv, rv);
   1.617 +    NS_ENSURE_TRUE(isDirectory, NS_ERROR_UNEXPECTED);
   1.618 +
   1.619 +    *aCreated = false;
   1.620 +  }
   1.621 +  else {
   1.622 +    NS_ENSURE_SUCCESS(rv, rv);
   1.623 +
   1.624 +    *aCreated = true;
   1.625 +  }
   1.626 +
   1.627 +  return NS_OK;
   1.628 +}
   1.629 +
   1.630 +nsresult
   1.631 +CreateDirectoryUpgradeStamp(nsIFile* aDirectory)
   1.632 +{
   1.633 +  AssertIsOnIOThread();
   1.634 +
   1.635 +  nsCOMPtr<nsIFile> metadataFile;
   1.636 +  nsresult rv = aDirectory->Clone(getter_AddRefs(metadataFile));
   1.637 +  NS_ENSURE_SUCCESS(rv, rv);
   1.638 +
   1.639 +  rv = metadataFile->Append(NS_LITERAL_STRING(METADATA_FILE_NAME));
   1.640 +  NS_ENSURE_SUCCESS(rv, rv);
   1.641 +
   1.642 +  rv = metadataFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
   1.643 +  NS_ENSURE_SUCCESS(rv, rv);
   1.644 +
   1.645 +  return NS_OK;
   1.646 +}
   1.647 +
   1.648 +nsresult
   1.649 +GetDirectoryMetadataStream(nsIFile* aDirectory, bool aUpdate,
   1.650 +                           nsIBinaryOutputStream** aStream)
   1.651 +{
   1.652 +  AssertIsOnIOThread();
   1.653 +
   1.654 +  nsCOMPtr<nsIFile> metadataFile;
   1.655 +  nsresult rv = aDirectory->Clone(getter_AddRefs(metadataFile));
   1.656 +  NS_ENSURE_SUCCESS(rv, rv);
   1.657 +
   1.658 +  rv = metadataFile->Append(NS_LITERAL_STRING(METADATA_FILE_NAME));
   1.659 +  NS_ENSURE_SUCCESS(rv, rv);
   1.660 +
   1.661 +  nsCOMPtr<nsIOutputStream> outputStream;
   1.662 +  if (aUpdate) {
   1.663 +    bool exists;
   1.664 +    rv = metadataFile->Exists(&exists);
   1.665 +    NS_ENSURE_SUCCESS(rv, rv);
   1.666 +
   1.667 +    if (!exists) {
   1.668 +      *aStream = nullptr;
   1.669 +      return NS_OK;
   1.670 +    }
   1.671 +
   1.672 +    nsCOMPtr<nsIFileStream> stream;
   1.673 +    rv = NS_NewLocalFileStream(getter_AddRefs(stream), metadataFile);
   1.674 +    NS_ENSURE_SUCCESS(rv, rv);
   1.675 +
   1.676 +    outputStream = do_QueryInterface(stream);
   1.677 +    NS_ENSURE_TRUE(outputStream, NS_ERROR_FAILURE);
   1.678 +  }
   1.679 +  else {
   1.680 +    rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream),
   1.681 +                                     metadataFile);
   1.682 +    NS_ENSURE_SUCCESS(rv, rv);
   1.683 +  }
   1.684 +
   1.685 +  nsCOMPtr<nsIBinaryOutputStream> binaryStream =
   1.686 +    do_CreateInstance("@mozilla.org/binaryoutputstream;1");
   1.687 +  NS_ENSURE_TRUE(binaryStream, NS_ERROR_FAILURE);
   1.688 +
   1.689 +  rv = binaryStream->SetOutputStream(outputStream);
   1.690 +  NS_ENSURE_SUCCESS(rv, rv);
   1.691 +
   1.692 +  binaryStream.forget(aStream);
   1.693 +  return NS_OK;
   1.694 +}
   1.695 +
   1.696 +nsresult
   1.697 +CreateDirectoryMetadata(nsIFile* aDirectory, int64_t aTimestamp,
   1.698 +                        const nsACString& aGroup, const nsACString& aOrigin)
   1.699 +{
   1.700 +  AssertIsOnIOThread();
   1.701 +
   1.702 +  nsCOMPtr<nsIBinaryOutputStream> stream;
   1.703 +  nsresult rv =
   1.704 +    GetDirectoryMetadataStream(aDirectory, false, getter_AddRefs(stream));
   1.705 +  NS_ENSURE_SUCCESS(rv, rv);
   1.706 +
   1.707 +  NS_ASSERTION(stream, "This shouldn't be null!");
   1.708 +
   1.709 +  rv = stream->Write64(aTimestamp);
   1.710 +  NS_ENSURE_SUCCESS(rv, rv);
   1.711 +
   1.712 +  rv = stream->WriteStringZ(PromiseFlatCString(aGroup).get());
   1.713 +  NS_ENSURE_SUCCESS(rv, rv);
   1.714 +
   1.715 +  rv = stream->WriteStringZ(PromiseFlatCString(aOrigin).get());
   1.716 +  NS_ENSURE_SUCCESS(rv, rv);
   1.717 +
   1.718 +  return NS_OK;
   1.719 +}
   1.720 +
   1.721 +nsresult
   1.722 +GetDirectoryMetadata(nsIFile* aDirectory, int64_t* aTimestamp,
   1.723 +                     nsACString& aGroup, nsACString& aOrigin)
   1.724 +{
   1.725 +  AssertIsOnIOThread();
   1.726 +
   1.727 +  nsCOMPtr<nsIFile> metadataFile;
   1.728 +  nsresult rv = aDirectory->Clone(getter_AddRefs(metadataFile));
   1.729 +  NS_ENSURE_SUCCESS(rv, rv);
   1.730 +
   1.731 +  rv = metadataFile->Append(NS_LITERAL_STRING(METADATA_FILE_NAME));
   1.732 +  NS_ENSURE_SUCCESS(rv, rv);
   1.733 +
   1.734 +  nsCOMPtr<nsIInputStream> stream;
   1.735 +  rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), metadataFile);
   1.736 +  NS_ENSURE_SUCCESS(rv, rv);
   1.737 +
   1.738 +  nsCOMPtr<nsIInputStream> bufferedStream;
   1.739 +  rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream), stream, 512);
   1.740 +  NS_ENSURE_SUCCESS(rv, rv);
   1.741 +
   1.742 +  nsCOMPtr<nsIBinaryInputStream> binaryStream =
   1.743 +    do_CreateInstance("@mozilla.org/binaryinputstream;1");
   1.744 +  NS_ENSURE_TRUE(binaryStream, NS_ERROR_FAILURE);
   1.745 +
   1.746 +  rv = binaryStream->SetInputStream(bufferedStream);
   1.747 +  NS_ENSURE_SUCCESS(rv, rv);
   1.748 +
   1.749 +  uint64_t timestamp;
   1.750 +  rv = binaryStream->Read64(&timestamp);
   1.751 +  NS_ENSURE_SUCCESS(rv, rv);
   1.752 +
   1.753 +  nsCString group;
   1.754 +  rv = binaryStream->ReadCString(group);
   1.755 +  NS_ENSURE_SUCCESS(rv, rv);
   1.756 +
   1.757 +  nsCString origin;
   1.758 +  rv = binaryStream->ReadCString(origin);
   1.759 +  NS_ENSURE_SUCCESS(rv, rv);
   1.760 +
   1.761 +  *aTimestamp = timestamp;
   1.762 +  aGroup = group;
   1.763 +  aOrigin = origin;
   1.764 +  return NS_OK;
   1.765 +}
   1.766 +
   1.767 +nsresult
   1.768 +MaybeUpgradeOriginDirectory(nsIFile* aDirectory)
   1.769 +{
   1.770 +  AssertIsOnIOThread();
   1.771 +  NS_ASSERTION(aDirectory, "Null pointer!");
   1.772 +
   1.773 +  nsCOMPtr<nsIFile> metadataFile;
   1.774 +  nsresult rv = aDirectory->Clone(getter_AddRefs(metadataFile));
   1.775 +  NS_ENSURE_SUCCESS(rv, rv);
   1.776 +
   1.777 +  rv = metadataFile->Append(NS_LITERAL_STRING(METADATA_FILE_NAME));
   1.778 +  NS_ENSURE_SUCCESS(rv, rv);
   1.779 +
   1.780 +  bool exists;
   1.781 +  rv = metadataFile->Exists(&exists);
   1.782 +  NS_ENSURE_SUCCESS(rv, rv);
   1.783 +
   1.784 +  if (!exists) {
   1.785 +    // Directory structure upgrade needed.
   1.786 +    // Move all files to IDB specific directory.
   1.787 +
   1.788 +    nsString idbDirectoryName;
   1.789 +    rv = Client::TypeToText(Client::IDB, idbDirectoryName);
   1.790 +    NS_ENSURE_SUCCESS(rv, rv);
   1.791 +
   1.792 +    nsCOMPtr<nsIFile> idbDirectory;
   1.793 +    rv = aDirectory->Clone(getter_AddRefs(idbDirectory));
   1.794 +    NS_ENSURE_SUCCESS(rv, rv);
   1.795 +
   1.796 +    rv = idbDirectory->Append(idbDirectoryName);
   1.797 +    NS_ENSURE_SUCCESS(rv, rv);
   1.798 +
   1.799 +    rv = idbDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
   1.800 +    if (rv == NS_ERROR_FILE_ALREADY_EXISTS) {
   1.801 +      NS_WARNING("IDB directory already exists!");
   1.802 +
   1.803 +      bool isDirectory;
   1.804 +      rv = idbDirectory->IsDirectory(&isDirectory);
   1.805 +      NS_ENSURE_SUCCESS(rv, rv);
   1.806 +      NS_ENSURE_TRUE(isDirectory, NS_ERROR_UNEXPECTED);
   1.807 +    }
   1.808 +    else {
   1.809 +      NS_ENSURE_SUCCESS(rv, rv);
   1.810 +    }
   1.811 +
   1.812 +    nsCOMPtr<nsISimpleEnumerator> entries;
   1.813 +    rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
   1.814 +    NS_ENSURE_SUCCESS(rv, rv);
   1.815 +
   1.816 +    bool hasMore;
   1.817 +    while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
   1.818 +      nsCOMPtr<nsISupports> entry;
   1.819 +      rv = entries->GetNext(getter_AddRefs(entry));
   1.820 +      NS_ENSURE_SUCCESS(rv, rv);
   1.821 +
   1.822 +      nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
   1.823 +      NS_ENSURE_TRUE(file, NS_NOINTERFACE);
   1.824 +
   1.825 +      nsString leafName;
   1.826 +      rv = file->GetLeafName(leafName);
   1.827 +      NS_ENSURE_SUCCESS(rv, rv);
   1.828 +
   1.829 +      if (!leafName.Equals(idbDirectoryName)) {
   1.830 +        rv = file->MoveTo(idbDirectory, EmptyString());
   1.831 +        NS_ENSURE_SUCCESS(rv, rv);
   1.832 +      }
   1.833 +    }
   1.834 +
   1.835 +    rv = metadataFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
   1.836 +    NS_ENSURE_SUCCESS(rv, rv);
   1.837 +  }
   1.838 +
   1.839 +  return NS_OK;
   1.840 +}
   1.841 +
   1.842 +// This method computes and returns our best guess for the temporary storage
   1.843 +// limit (in bytes), based on the amount of space users have free on their hard
   1.844 +// drive and on given temporary storage usage (also in bytes).
   1.845 +nsresult
   1.846 +GetTemporaryStorageLimit(nsIFile* aDirectory, uint64_t aCurrentUsage,
   1.847 +                         uint64_t* aLimit)
   1.848 +{
   1.849 +  // Check for free space on device where temporary storage directory lives.
   1.850 +  int64_t bytesAvailable;
   1.851 +  nsresult rv = aDirectory->GetDiskSpaceAvailable(&bytesAvailable);
   1.852 +  NS_ENSURE_SUCCESS(rv, rv);
   1.853 +
   1.854 +  NS_ASSERTION(bytesAvailable >= 0, "Negative bytes available?!");
   1.855 +
   1.856 +  uint64_t availableKB =
   1.857 +    static_cast<uint64_t>((bytesAvailable + aCurrentUsage) / 1024);
   1.858 +
   1.859 +  // Grow/shrink in gChunkSizeKB units, deliberately, so that in the common case
   1.860 +  // we don't shrink temporary storage and evict origin data every time we
   1.861 +  // initialize.
   1.862 +  availableKB = (availableKB / gChunkSizeKB) * gChunkSizeKB;
   1.863 +
   1.864 +  // Allow temporary storage to consume up to half the available space.
   1.865 +  uint64_t resultKB = availableKB * .50;
   1.866 +
   1.867 +  *aLimit = resultKB * 1024;
   1.868 +  return NS_OK;
   1.869 +}
   1.870 +
   1.871 +} // anonymous namespace
   1.872 +
   1.873 +QuotaManager::QuotaManager()
   1.874 +: mCurrentWindowIndex(BAD_TLS_INDEX),
   1.875 +  mQuotaMutex("QuotaManager.mQuotaMutex"),
   1.876 +  mTemporaryStorageLimit(0),
   1.877 +  mTemporaryStorageUsage(0),
   1.878 +  mTemporaryStorageInitialized(false),
   1.879 +  mStorageAreaInitialized(false)
   1.880 +{
   1.881 +  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   1.882 +  NS_ASSERTION(!gInstance, "More than one instance!");
   1.883 +}
   1.884 +
   1.885 +QuotaManager::~QuotaManager()
   1.886 +{
   1.887 +  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   1.888 +  NS_ASSERTION(!gInstance || gInstance == this, "Different instances!");
   1.889 +  gInstance = nullptr;
   1.890 +}
   1.891 +
   1.892 +// static
   1.893 +QuotaManager*
   1.894 +QuotaManager::GetOrCreate()
   1.895 +{
   1.896 +  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   1.897 +
   1.898 +  if (IsShuttingDown()) {
   1.899 +    NS_ERROR("Calling GetOrCreate() after shutdown!");
   1.900 +    return nullptr;
   1.901 +  }
   1.902 +
   1.903 +  if (!gInstance) {
   1.904 +    nsRefPtr<QuotaManager> instance(new QuotaManager());
   1.905 +
   1.906 +    nsresult rv = instance->Init();
   1.907 +    NS_ENSURE_SUCCESS(rv, nullptr);
   1.908 +
   1.909 +    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   1.910 +    NS_ENSURE_TRUE(obs, nullptr);
   1.911 +
   1.912 +    // We need this callback to know when to shut down all our threads.
   1.913 +    rv = obs->AddObserver(instance, PROFILE_BEFORE_CHANGE_OBSERVER_ID, false);
   1.914 +    NS_ENSURE_SUCCESS(rv, nullptr);
   1.915 +
   1.916 +    // The observer service will hold our last reference, don't AddRef here.
   1.917 +    gInstance = instance;
   1.918 +  }
   1.919 +
   1.920 +  return gInstance;
   1.921 +}
   1.922 +
   1.923 +// static
   1.924 +QuotaManager*
   1.925 +QuotaManager::Get()
   1.926 +{
   1.927 +  // Does not return an owning reference.
   1.928 +  return gInstance;
   1.929 +}
   1.930 +
   1.931 +// static
   1.932 +QuotaManager*
   1.933 +QuotaManager::FactoryCreate()
   1.934 +{
   1.935 +  // Returns a raw pointer that carries an owning reference! Lame, but the
   1.936 +  // singleton factory macros force this.
   1.937 +  QuotaManager* quotaManager = GetOrCreate();
   1.938 +  NS_IF_ADDREF(quotaManager);
   1.939 +  return quotaManager;
   1.940 +}
   1.941 +
   1.942 +// static
   1.943 +bool
   1.944 +QuotaManager::IsShuttingDown()
   1.945 +{
   1.946 +  return gShutdown;
   1.947 +}
   1.948 +
   1.949 +nsresult
   1.950 +QuotaManager::Init()
   1.951 +{
   1.952 +  // We need a thread-local to hold the current window.
   1.953 +  NS_ASSERTION(mCurrentWindowIndex == BAD_TLS_INDEX, "Huh?");
   1.954 +
   1.955 +  if (PR_NewThreadPrivateIndex(&mCurrentWindowIndex, nullptr) != PR_SUCCESS) {
   1.956 +    NS_ERROR("PR_NewThreadPrivateIndex failed, QuotaManager disabled");
   1.957 +    mCurrentWindowIndex = BAD_TLS_INDEX;
   1.958 +    return NS_ERROR_FAILURE;
   1.959 +  }
   1.960 +
   1.961 +  nsresult rv;
   1.962 +  if (IsMainProcess()) {
   1.963 +    nsCOMPtr<nsIFile> baseDir;
   1.964 +    rv = NS_GetSpecialDirectory(NS_APP_INDEXEDDB_PARENT_DIR,
   1.965 +                                getter_AddRefs(baseDir));
   1.966 +    if (NS_FAILED(rv)) {
   1.967 +      rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
   1.968 +                                  getter_AddRefs(baseDir));
   1.969 +    }
   1.970 +    NS_ENSURE_SUCCESS(rv, rv);
   1.971 +
   1.972 +    nsCOMPtr<nsIFile> indexedDBDir;
   1.973 +    rv = baseDir->Clone(getter_AddRefs(indexedDBDir));
   1.974 +    NS_ENSURE_SUCCESS(rv, rv);
   1.975 +
   1.976 +    rv = indexedDBDir->Append(NS_LITERAL_STRING("indexedDB"));
   1.977 +    NS_ENSURE_SUCCESS(rv, rv);
   1.978 +
   1.979 +    rv = indexedDBDir->GetPath(mIndexedDBPath);
   1.980 +    NS_ENSURE_SUCCESS(rv, rv);
   1.981 +
   1.982 +    rv = baseDir->Append(NS_LITERAL_STRING("storage"));
   1.983 +    NS_ENSURE_SUCCESS(rv, rv);
   1.984 +
   1.985 +    nsCOMPtr<nsIFile> persistentStorageDir;
   1.986 +    rv = baseDir->Clone(getter_AddRefs(persistentStorageDir));
   1.987 +    NS_ENSURE_SUCCESS(rv, rv);
   1.988 +
   1.989 +    rv = persistentStorageDir->Append(NS_LITERAL_STRING("persistent"));
   1.990 +    NS_ENSURE_SUCCESS(rv, rv);
   1.991 +
   1.992 +    rv = persistentStorageDir->GetPath(mPersistentStoragePath);
   1.993 +    NS_ENSURE_SUCCESS(rv, rv);
   1.994 +
   1.995 +    nsCOMPtr<nsIFile> temporaryStorageDir;
   1.996 +    rv = baseDir->Clone(getter_AddRefs(temporaryStorageDir));
   1.997 +    NS_ENSURE_SUCCESS(rv, rv);
   1.998 +
   1.999 +    rv = temporaryStorageDir->Append(NS_LITERAL_STRING("temporary"));
  1.1000 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1001 +
  1.1002 +    rv = temporaryStorageDir->GetPath(mTemporaryStoragePath);
  1.1003 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1004 +
  1.1005 +    // Make a lazy thread for any IO we need (like clearing or enumerating the
  1.1006 +    // contents of storage directories).
  1.1007 +    mIOThread = new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS,
  1.1008 +                                   NS_LITERAL_CSTRING("Storage I/O"),
  1.1009 +                                   LazyIdleThread::ManualShutdown);
  1.1010 +
  1.1011 +    // Make a timer here to avoid potential failures later. We don't actually
  1.1012 +    // initialize the timer until shutdown.
  1.1013 +    mShutdownTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
  1.1014 +    NS_ENSURE_TRUE(mShutdownTimer, NS_ERROR_FAILURE);
  1.1015 +  }
  1.1016 +
  1.1017 +  if (NS_FAILED(Preferences::AddIntVarCache(&gStorageQuotaMB,
  1.1018 +                                            PREF_STORAGE_QUOTA,
  1.1019 +                                            kDefaultQuotaMB))) {
  1.1020 +    NS_WARNING("Unable to respond to quota pref changes!");
  1.1021 +  }
  1.1022 +
  1.1023 +  if (NS_FAILED(Preferences::AddIntVarCache(&gFixedLimitKB, PREF_FIXED_LIMIT,
  1.1024 +                                            kDefaultFixedLimitKB)) ||
  1.1025 +      NS_FAILED(Preferences::AddUintVarCache(&gChunkSizeKB,
  1.1026 +                                             PREF_CHUNK_SIZE,
  1.1027 +                                             kDefaultChunkSizeKB))) {
  1.1028 +    NS_WARNING("Unable to respond to temp storage pref changes!");
  1.1029 +  }
  1.1030 +
  1.1031 +  if (NS_FAILED(Preferences::AddBoolVarCache(&gTestingEnabled,
  1.1032 +                                             PREF_TESTING_FEATURES, false))) {
  1.1033 +    NS_WARNING("Unable to respond to testing pref changes!");
  1.1034 +  }
  1.1035 +
  1.1036 +  static_assert(Client::IDB == 0 && Client::ASMJS == 1 && Client::TYPE_MAX == 2,
  1.1037 +                "Fix the registration!");
  1.1038 +
  1.1039 +  NS_ASSERTION(mClients.Capacity() == Client::TYPE_MAX,
  1.1040 +               "Should be using an auto array with correct capacity!");
  1.1041 +
  1.1042 +  // Register IndexedDB
  1.1043 +  mClients.AppendElement(new indexedDB::Client());
  1.1044 +  mClients.AppendElement(asmjscache::CreateClient());
  1.1045 +
  1.1046 +  return NS_OK;
  1.1047 +}
  1.1048 +
  1.1049 +void
  1.1050 +QuotaManager::InitQuotaForOrigin(PersistenceType aPersistenceType,
  1.1051 +                                 const nsACString& aGroup,
  1.1052 +                                 const nsACString& aOrigin,
  1.1053 +                                 uint64_t aLimitBytes,
  1.1054 +                                 uint64_t aUsageBytes,
  1.1055 +                                 int64_t aAccessTime)
  1.1056 +{
  1.1057 +  AssertIsOnIOThread();
  1.1058 +  MOZ_ASSERT(aLimitBytes > 0 ||
  1.1059 +             aPersistenceType == PERSISTENCE_TYPE_TEMPORARY);
  1.1060 +  MOZ_ASSERT(aUsageBytes <= aLimitBytes ||
  1.1061 +             aPersistenceType == PERSISTENCE_TYPE_TEMPORARY);
  1.1062 +
  1.1063 +  MutexAutoLock lock(mQuotaMutex);
  1.1064 +
  1.1065 +  GroupInfoPair* pair;
  1.1066 +  if (!mGroupInfoPairs.Get(aGroup, &pair)) {
  1.1067 +    pair = new GroupInfoPair();
  1.1068 +    mGroupInfoPairs.Put(aGroup, pair);
  1.1069 +    // The hashtable is now responsible to delete the GroupInfoPair.
  1.1070 +  }
  1.1071 +
  1.1072 +  nsRefPtr<GroupInfo> groupInfo = pair->LockedGetGroupInfo(aPersistenceType);
  1.1073 +  if (!groupInfo) {
  1.1074 +    groupInfo = new GroupInfo(aPersistenceType, aGroup);
  1.1075 +    pair->LockedSetGroupInfo(groupInfo);
  1.1076 +  }
  1.1077 +
  1.1078 +  nsRefPtr<OriginInfo> originInfo =
  1.1079 +    new OriginInfo(groupInfo, aOrigin, aLimitBytes, aUsageBytes, aAccessTime);
  1.1080 +  groupInfo->LockedAddOriginInfo(originInfo);
  1.1081 +}
  1.1082 +
  1.1083 +void
  1.1084 +QuotaManager::DecreaseUsageForOrigin(PersistenceType aPersistenceType,
  1.1085 +                                     const nsACString& aGroup,
  1.1086 +                                     const nsACString& aOrigin,
  1.1087 +                                     int64_t aSize)
  1.1088 +{
  1.1089 +  AssertIsOnIOThread();
  1.1090 +
  1.1091 +  MutexAutoLock lock(mQuotaMutex);
  1.1092 +
  1.1093 +  GroupInfoPair* pair;
  1.1094 +  if (!mGroupInfoPairs.Get(aGroup, &pair)) {
  1.1095 +    return;
  1.1096 +  }
  1.1097 +
  1.1098 +  nsRefPtr<GroupInfo> groupInfo = pair->LockedGetGroupInfo(aPersistenceType);
  1.1099 +  if (!groupInfo) {
  1.1100 +    return;
  1.1101 +  }
  1.1102 +
  1.1103 +  nsRefPtr<OriginInfo> originInfo = groupInfo->LockedGetOriginInfo(aOrigin);
  1.1104 +  if (originInfo) {
  1.1105 +    originInfo->LockedDecreaseUsage(aSize);
  1.1106 +  }
  1.1107 +}
  1.1108 +
  1.1109 +void
  1.1110 +QuotaManager::UpdateOriginAccessTime(PersistenceType aPersistenceType,
  1.1111 +                                     const nsACString& aGroup,
  1.1112 +                                     const nsACString& aOrigin)
  1.1113 +{
  1.1114 +  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1.1115 +
  1.1116 +  MutexAutoLock lock(mQuotaMutex);
  1.1117 +
  1.1118 +  GroupInfoPair* pair;
  1.1119 +  if (!mGroupInfoPairs.Get(aGroup, &pair)) {
  1.1120 +    return;
  1.1121 +  }
  1.1122 +
  1.1123 +  nsRefPtr<GroupInfo> groupInfo =
  1.1124 +    pair->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY);
  1.1125 +  if (!groupInfo) {
  1.1126 +    return;
  1.1127 +  }
  1.1128 +
  1.1129 +  nsRefPtr<OriginInfo> originInfo = groupInfo->LockedGetOriginInfo(aOrigin);
  1.1130 +  if (originInfo) {
  1.1131 +    int64_t timestamp = PR_Now();
  1.1132 +    originInfo->LockedUpdateAccessTime(timestamp);
  1.1133 +
  1.1134 +    if (!groupInfo->IsForTemporaryStorage()) {
  1.1135 +      return;
  1.1136 +    }
  1.1137 +
  1.1138 +    MutexAutoUnlock autoUnlock(mQuotaMutex);
  1.1139 +
  1.1140 +    SaveOriginAccessTime(aOrigin, timestamp);
  1.1141 +  }
  1.1142 +}
  1.1143 +
  1.1144 +// static
  1.1145 +PLDHashOperator
  1.1146 +QuotaManager::RemoveQuotaCallback(const nsACString& aKey,
  1.1147 +                                  nsAutoPtr<GroupInfoPair>& aValue,
  1.1148 +                                  void* aUserArg)
  1.1149 +{
  1.1150 +  NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
  1.1151 +  NS_ASSERTION(aValue, "Null pointer!");
  1.1152 +
  1.1153 +  nsRefPtr<GroupInfo> groupInfo =
  1.1154 +    aValue->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY);
  1.1155 +  if (groupInfo) {
  1.1156 +    groupInfo->LockedRemoveOriginInfos();
  1.1157 +  }
  1.1158 +
  1.1159 +  return PL_DHASH_REMOVE;
  1.1160 +}
  1.1161 +
  1.1162 +void
  1.1163 +QuotaManager::RemoveQuota()
  1.1164 +{
  1.1165 +  MutexAutoLock lock(mQuotaMutex);
  1.1166 +
  1.1167 +  mGroupInfoPairs.Enumerate(RemoveQuotaCallback, nullptr);
  1.1168 +
  1.1169 +  NS_ASSERTION(mTemporaryStorageUsage == 0, "Should be zero!");
  1.1170 +}
  1.1171 +
  1.1172 +// static
  1.1173 +PLDHashOperator
  1.1174 +QuotaManager::RemoveQuotaForPersistenceTypeCallback(
  1.1175 +                                               const nsACString& aKey,
  1.1176 +                                               nsAutoPtr<GroupInfoPair>& aValue,
  1.1177 +                                               void* aUserArg)
  1.1178 +{
  1.1179 +  NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
  1.1180 +  NS_ASSERTION(aValue, "Null pointer!");
  1.1181 +  NS_ASSERTION(aUserArg, "Null pointer!");
  1.1182 +
  1.1183 +  PersistenceType& persistenceType = *static_cast<PersistenceType*>(aUserArg);
  1.1184 +
  1.1185 +  if (persistenceType == PERSISTENCE_TYPE_TEMPORARY) {
  1.1186 +    nsRefPtr<GroupInfo> groupInfo =
  1.1187 +      aValue->LockedGetGroupInfo(persistenceType);
  1.1188 +    if (groupInfo) {
  1.1189 +      groupInfo->LockedRemoveOriginInfos();
  1.1190 +    }
  1.1191 +  }
  1.1192 +
  1.1193 +  aValue->LockedClearGroupInfo(persistenceType);
  1.1194 +
  1.1195 +  return aValue->LockedHasGroupInfos() ? PL_DHASH_NEXT : PL_DHASH_REMOVE;
  1.1196 +}
  1.1197 +
  1.1198 +void
  1.1199 +QuotaManager::RemoveQuotaForPersistenceType(PersistenceType aPersistenceType)
  1.1200 +{
  1.1201 +  MutexAutoLock lock(mQuotaMutex);
  1.1202 +
  1.1203 +  mGroupInfoPairs.Enumerate(RemoveQuotaForPersistenceTypeCallback,
  1.1204 +                            &aPersistenceType);
  1.1205 +
  1.1206 +  NS_ASSERTION(aPersistenceType == PERSISTENCE_TYPE_PERSISTENT ||
  1.1207 +               mTemporaryStorageUsage == 0, "Should be zero!");
  1.1208 +}
  1.1209 +
  1.1210 +// static
  1.1211 +PLDHashOperator
  1.1212 +QuotaManager::RemoveQuotaForPatternCallback(const nsACString& aKey,
  1.1213 +                                            nsAutoPtr<GroupInfoPair>& aValue,
  1.1214 +                                            void* aUserArg)
  1.1215 +{
  1.1216 +  NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
  1.1217 +  NS_ASSERTION(aValue, "Null pointer!");
  1.1218 +  NS_ASSERTION(aUserArg, "Null pointer!");
  1.1219 +
  1.1220 +  RemoveQuotaInfo* info = static_cast<RemoveQuotaInfo*>(aUserArg);
  1.1221 +
  1.1222 +  nsRefPtr<GroupInfo> groupInfo =
  1.1223 +    aValue->LockedGetGroupInfo(info->persistenceType);
  1.1224 +  if (groupInfo) {
  1.1225 +    groupInfo->LockedRemoveOriginInfosForPattern(info->pattern);
  1.1226 +
  1.1227 +    if (!groupInfo->LockedHasOriginInfos()) {
  1.1228 +      aValue->LockedClearGroupInfo(info->persistenceType);
  1.1229 +
  1.1230 +      if (!aValue->LockedHasGroupInfos()) {
  1.1231 +        return PL_DHASH_REMOVE;
  1.1232 +      }
  1.1233 +    }
  1.1234 +  }
  1.1235 +
  1.1236 +  return PL_DHASH_NEXT;
  1.1237 +}
  1.1238 +
  1.1239 +void
  1.1240 +QuotaManager::RemoveQuotaForPattern(PersistenceType aPersistenceType,
  1.1241 +                                    const nsACString& aPattern)
  1.1242 +{
  1.1243 +  NS_ASSERTION(!aPattern.IsEmpty(), "Empty pattern!");
  1.1244 +
  1.1245 +  RemoveQuotaInfo info(aPersistenceType, aPattern);
  1.1246 +
  1.1247 +  MutexAutoLock lock(mQuotaMutex);
  1.1248 +
  1.1249 +  mGroupInfoPairs.Enumerate(RemoveQuotaForPatternCallback, &info);
  1.1250 +}
  1.1251 +
  1.1252 +already_AddRefed<QuotaObject>
  1.1253 +QuotaManager::GetQuotaObject(PersistenceType aPersistenceType,
  1.1254 +                             const nsACString& aGroup,
  1.1255 +                             const nsACString& aOrigin,
  1.1256 +                             nsIFile* aFile)
  1.1257 +{
  1.1258 +  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
  1.1259 +
  1.1260 +  nsString path;
  1.1261 +  nsresult rv = aFile->GetPath(path);
  1.1262 +  NS_ENSURE_SUCCESS(rv, nullptr);
  1.1263 +
  1.1264 +  int64_t fileSize;
  1.1265 +
  1.1266 +  bool exists;
  1.1267 +  rv = aFile->Exists(&exists);
  1.1268 +  NS_ENSURE_SUCCESS(rv, nullptr);
  1.1269 +
  1.1270 +  if (exists) {
  1.1271 +    rv = aFile->GetFileSize(&fileSize);
  1.1272 +    NS_ENSURE_SUCCESS(rv, nullptr);
  1.1273 +  }
  1.1274 +  else {
  1.1275 +    fileSize = 0;
  1.1276 +  }
  1.1277 +
  1.1278 +  nsRefPtr<QuotaObject> result;
  1.1279 +  {
  1.1280 +    MutexAutoLock lock(mQuotaMutex);
  1.1281 +
  1.1282 +    GroupInfoPair* pair;
  1.1283 +    if (!mGroupInfoPairs.Get(aGroup, &pair)) {
  1.1284 +      return nullptr;
  1.1285 +    }
  1.1286 +
  1.1287 +    nsRefPtr<GroupInfo> groupInfo = pair->LockedGetGroupInfo(aPersistenceType);
  1.1288 +
  1.1289 +    if (!groupInfo) {
  1.1290 +      return nullptr;
  1.1291 +    }
  1.1292 +
  1.1293 +    nsRefPtr<OriginInfo> originInfo = groupInfo->LockedGetOriginInfo(aOrigin);
  1.1294 +
  1.1295 +    if (!originInfo) {
  1.1296 +      return nullptr;
  1.1297 +    }
  1.1298 +
  1.1299 +    // We need this extra raw pointer because we can't assign to the smart
  1.1300 +    // pointer directly since QuotaObject::AddRef would try to acquire the same
  1.1301 +    // mutex.
  1.1302 +    QuotaObject* quotaObject;
  1.1303 +    if (!originInfo->mQuotaObjects.Get(path, &quotaObject)) {
  1.1304 +      // Create a new QuotaObject.
  1.1305 +      quotaObject = new QuotaObject(originInfo, path, fileSize);
  1.1306 +
  1.1307 +      // Put it to the hashtable. The hashtable is not responsible to delete
  1.1308 +      // the QuotaObject.
  1.1309 +      originInfo->mQuotaObjects.Put(path, quotaObject);
  1.1310 +    }
  1.1311 +
  1.1312 +    // Addref the QuotaObject and move the ownership to the result. This must
  1.1313 +    // happen before we unlock!
  1.1314 +    result = quotaObject->LockedAddRef();
  1.1315 +  }
  1.1316 +
  1.1317 +  // The caller becomes the owner of the QuotaObject, that is, the caller is
  1.1318 +  // is responsible to delete it when the last reference is removed.
  1.1319 +  return result.forget();
  1.1320 +}
  1.1321 +
  1.1322 +already_AddRefed<QuotaObject>
  1.1323 +QuotaManager::GetQuotaObject(PersistenceType aPersistenceType,
  1.1324 +                             const nsACString& aGroup,
  1.1325 +                             const nsACString& aOrigin,
  1.1326 +                             const nsAString& aPath)
  1.1327 +{
  1.1328 +  nsresult rv;
  1.1329 +  nsCOMPtr<nsIFile> file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
  1.1330 +  NS_ENSURE_SUCCESS(rv, nullptr);
  1.1331 +
  1.1332 +  rv = file->InitWithPath(aPath);
  1.1333 +  NS_ENSURE_SUCCESS(rv, nullptr);
  1.1334 +
  1.1335 +  return GetQuotaObject(aPersistenceType, aGroup, aOrigin, file);
  1.1336 +}
  1.1337 +
  1.1338 +bool
  1.1339 +QuotaManager::RegisterStorage(nsIOfflineStorage* aStorage)
  1.1340 +{
  1.1341 +  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1.1342 +  NS_ASSERTION(aStorage, "Null pointer!");
  1.1343 +
  1.1344 +  // Don't allow any new storages to be created after shutdown.
  1.1345 +  if (IsShuttingDown()) {
  1.1346 +    return false;
  1.1347 +  }
  1.1348 +
  1.1349 +  // Add this storage to its origin info if it exists, create it otherwise.
  1.1350 +  const nsACString& origin = aStorage->Origin();
  1.1351 +  ArrayCluster<nsIOfflineStorage*>* cluster;
  1.1352 +  if (!mLiveStorages.Get(origin, &cluster)) {
  1.1353 +    cluster = new ArrayCluster<nsIOfflineStorage*>();
  1.1354 +    mLiveStorages.Put(origin, cluster);
  1.1355 +
  1.1356 +    UpdateOriginAccessTime(aStorage->Type(), aStorage->Group(), origin);
  1.1357 +  }
  1.1358 +  (*cluster)[aStorage->GetClient()->GetType()].AppendElement(aStorage);
  1.1359 +
  1.1360 +  return true;
  1.1361 +}
  1.1362 +
  1.1363 +void
  1.1364 +QuotaManager::UnregisterStorage(nsIOfflineStorage* aStorage)
  1.1365 +{
  1.1366 +  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1.1367 +  NS_ASSERTION(aStorage, "Null pointer!");
  1.1368 +
  1.1369 +  // Remove this storage from its origin array, maybe remove the array if it
  1.1370 +  // is then empty.
  1.1371 +  const nsACString& origin = aStorage->Origin();
  1.1372 +  ArrayCluster<nsIOfflineStorage*>* cluster;
  1.1373 +  if (mLiveStorages.Get(origin, &cluster) &&
  1.1374 +      (*cluster)[aStorage->GetClient()->GetType()].RemoveElement(aStorage)) {
  1.1375 +    if (cluster->IsEmpty()) {
  1.1376 +      mLiveStorages.Remove(origin);
  1.1377 +
  1.1378 +      UpdateOriginAccessTime(aStorage->Type(), aStorage->Group(), origin);
  1.1379 +    }
  1.1380 +    return;
  1.1381 +  }
  1.1382 +  NS_ERROR("Didn't know anything about this storage!");
  1.1383 +}
  1.1384 +
  1.1385 +void
  1.1386 +QuotaManager::OnStorageClosed(nsIOfflineStorage* aStorage)
  1.1387 +{
  1.1388 +  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1.1389 +  NS_ASSERTION(aStorage, "Null pointer!");
  1.1390 +
  1.1391 +  // Check through the list of SynchronizedOps to see if any are waiting for
  1.1392 +  // this storage to close before proceeding.
  1.1393 +  SynchronizedOp* op =
  1.1394 +    FindSynchronizedOp(aStorage->Origin(),
  1.1395 +                       Nullable<PersistenceType>(aStorage->Type()),
  1.1396 +                       aStorage->Id());
  1.1397 +  if (op) {
  1.1398 +    Client::Type clientType = aStorage->GetClient()->GetType();
  1.1399 +
  1.1400 +    // This storage is in the scope of this SynchronizedOp.  Remove it
  1.1401 +    // from the list if necessary.
  1.1402 +    if (op->mStorages[clientType].RemoveElement(aStorage)) {
  1.1403 +      // Now set up the helper if there are no more live storages.
  1.1404 +      NS_ASSERTION(op->mListener,
  1.1405 +                   "How did we get rid of the listener before removing the "
  1.1406 +                    "last storage?");
  1.1407 +      if (op->mStorages[clientType].IsEmpty()) {
  1.1408 +        // At this point, all storages are closed, so no new transactions
  1.1409 +        // can be started.  There may, however, still be outstanding
  1.1410 +        // transactions that have not completed.  We need to wait for those
  1.1411 +        // before we dispatch the helper.
  1.1412 +        if (NS_FAILED(RunSynchronizedOp(aStorage, op))) {
  1.1413 +          NS_WARNING("Failed to run synchronized op!");
  1.1414 +        }
  1.1415 +      }
  1.1416 +    }
  1.1417 +  }
  1.1418 +}
  1.1419 +
  1.1420 +void
  1.1421 +QuotaManager::AbortCloseStoragesForWindow(nsPIDOMWindow* aWindow)
  1.1422 +{
  1.1423 +  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1.1424 +  NS_ASSERTION(aWindow, "Null pointer!");
  1.1425 +
  1.1426 +  FileService* service = FileService::Get();
  1.1427 +
  1.1428 +  StorageMatcher<ArrayCluster<nsIOfflineStorage*> > liveStorages;
  1.1429 +  liveStorages.Find(mLiveStorages);
  1.1430 +
  1.1431 +  for (uint32_t i = 0; i < Client::TYPE_MAX; i++) {
  1.1432 +    nsRefPtr<Client>& client = mClients[i];
  1.1433 +    bool utilized = service && client->IsFileServiceUtilized();
  1.1434 +    bool activated = client->IsTransactionServiceActivated();
  1.1435 +
  1.1436 +    nsTArray<nsIOfflineStorage*>& array = liveStorages[i];
  1.1437 +    for (uint32_t j = 0; j < array.Length(); j++) {
  1.1438 +      nsIOfflineStorage*& storage = array[j];
  1.1439 +
  1.1440 +      if (storage->IsOwned(aWindow)) {
  1.1441 +        if (NS_FAILED(storage->Close())) {
  1.1442 +          NS_WARNING("Failed to close storage for dying window!");
  1.1443 +        }
  1.1444 +
  1.1445 +        if (utilized) {
  1.1446 +          service->AbortLockedFilesForStorage(storage);
  1.1447 +        }
  1.1448 +
  1.1449 +        if (activated) {
  1.1450 +          client->AbortTransactionsForStorage(storage);
  1.1451 +        }
  1.1452 +      }
  1.1453 +    }
  1.1454 +  }
  1.1455 +}
  1.1456 +
  1.1457 +bool
  1.1458 +QuotaManager::HasOpenTransactions(nsPIDOMWindow* aWindow)
  1.1459 +{
  1.1460 +  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1.1461 +  NS_ASSERTION(aWindow, "Null pointer!");
  1.1462 +
  1.1463 +  FileService* service = FileService::Get();
  1.1464 +
  1.1465 +  nsAutoPtr<StorageMatcher<ArrayCluster<nsIOfflineStorage*> > > liveStorages;
  1.1466 +
  1.1467 +  for (uint32_t i = 0; i < Client::TYPE_MAX; i++) {
  1.1468 +    nsRefPtr<Client>& client = mClients[i];
  1.1469 +    bool utilized = service && client->IsFileServiceUtilized();
  1.1470 +    bool activated = client->IsTransactionServiceActivated();
  1.1471 +
  1.1472 +    if (utilized || activated) {
  1.1473 +      if (!liveStorages) {
  1.1474 +        liveStorages = new StorageMatcher<ArrayCluster<nsIOfflineStorage*> >();
  1.1475 +        liveStorages->Find(mLiveStorages);
  1.1476 +      }
  1.1477 +
  1.1478 +      nsTArray<nsIOfflineStorage*>& storages = liveStorages->ArrayAt(i);
  1.1479 +      for (uint32_t j = 0; j < storages.Length(); j++) {
  1.1480 +        nsIOfflineStorage*& storage = storages[j];
  1.1481 +
  1.1482 +        if (storage->IsOwned(aWindow) &&
  1.1483 +            ((utilized && service->HasLockedFilesForStorage(storage)) ||
  1.1484 +             (activated && client->HasTransactionsForStorage(storage)))) {
  1.1485 +          return true;
  1.1486 +        }
  1.1487 +      }
  1.1488 +    }
  1.1489 +  }
  1.1490 +
  1.1491 +  return false;
  1.1492 +}
  1.1493 +
  1.1494 +nsresult
  1.1495 +QuotaManager::WaitForOpenAllowed(const OriginOrPatternString& aOriginOrPattern,
  1.1496 +                                 Nullable<PersistenceType> aPersistenceType,
  1.1497 +                                 const nsACString& aId, nsIRunnable* aRunnable)
  1.1498 +{
  1.1499 +  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1.1500 +  NS_ASSERTION(!aOriginOrPattern.IsEmpty() || aOriginOrPattern.IsNull(),
  1.1501 +               "Empty pattern!");
  1.1502 +  NS_ASSERTION(aRunnable, "Null pointer!");
  1.1503 +
  1.1504 +  nsAutoPtr<SynchronizedOp> op(new SynchronizedOp(aOriginOrPattern,
  1.1505 +                                                  aPersistenceType, aId));
  1.1506 +
  1.1507 +  // See if this runnable needs to wait.
  1.1508 +  bool delayed = false;
  1.1509 +  for (uint32_t index = mSynchronizedOps.Length(); index > 0; index--) {
  1.1510 +    nsAutoPtr<SynchronizedOp>& existingOp = mSynchronizedOps[index - 1];
  1.1511 +    if (op->MustWaitFor(*existingOp)) {
  1.1512 +      existingOp->DelayRunnable(aRunnable);
  1.1513 +      delayed = true;
  1.1514 +      break;
  1.1515 +    }
  1.1516 +  }
  1.1517 +
  1.1518 +  // Otherwise, dispatch it immediately.
  1.1519 +  if (!delayed) {
  1.1520 +    nsresult rv = NS_DispatchToCurrentThread(aRunnable);
  1.1521 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1522 +  }
  1.1523 +
  1.1524 +  // Adding this to the synchronized ops list will block any additional
  1.1525 +  // ops from proceeding until this one is done.
  1.1526 +  mSynchronizedOps.AppendElement(op.forget());
  1.1527 +
  1.1528 +  return NS_OK;
  1.1529 +}
  1.1530 +
  1.1531 +void
  1.1532 +QuotaManager::AddSynchronizedOp(const OriginOrPatternString& aOriginOrPattern,
  1.1533 +                                Nullable<PersistenceType> aPersistenceType)
  1.1534 +{
  1.1535 +  nsAutoPtr<SynchronizedOp> op(new SynchronizedOp(aOriginOrPattern,
  1.1536 +                                                  aPersistenceType,
  1.1537 +                                                  EmptyCString()));
  1.1538 +
  1.1539 +#ifdef DEBUG
  1.1540 +  for (uint32_t index = mSynchronizedOps.Length(); index > 0; index--) {
  1.1541 +    nsAutoPtr<SynchronizedOp>& existingOp = mSynchronizedOps[index - 1];
  1.1542 +    NS_ASSERTION(!op->MustWaitFor(*existingOp), "What?");
  1.1543 +  }
  1.1544 +#endif
  1.1545 +
  1.1546 +  mSynchronizedOps.AppendElement(op.forget());
  1.1547 +}
  1.1548 +
  1.1549 +void
  1.1550 +QuotaManager::AllowNextSynchronizedOp(
  1.1551 +                                  const OriginOrPatternString& aOriginOrPattern,
  1.1552 +                                  Nullable<PersistenceType> aPersistenceType,
  1.1553 +                                  const nsACString& aId)
  1.1554 +{
  1.1555 +  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1.1556 +  NS_ASSERTION(!aOriginOrPattern.IsEmpty() || aOriginOrPattern.IsNull(),
  1.1557 +               "Empty origin/pattern!");
  1.1558 +
  1.1559 +  uint32_t count = mSynchronizedOps.Length();
  1.1560 +  for (uint32_t index = 0; index < count; index++) {
  1.1561 +    nsAutoPtr<SynchronizedOp>& op = mSynchronizedOps[index];
  1.1562 +    if (op->mOriginOrPattern.IsOrigin() == aOriginOrPattern.IsOrigin() &&
  1.1563 +        op->mOriginOrPattern == aOriginOrPattern &&
  1.1564 +        op->mPersistenceType == aPersistenceType) {
  1.1565 +      if (op->mId == aId) {
  1.1566 +        NS_ASSERTION(op->mStorages.IsEmpty(), "How did this happen?");
  1.1567 +
  1.1568 +        op->DispatchDelayedRunnables();
  1.1569 +
  1.1570 +        mSynchronizedOps.RemoveElementAt(index);
  1.1571 +        return;
  1.1572 +      }
  1.1573 +
  1.1574 +      // If one or the other is for an origin clear, we should have matched
  1.1575 +      // solely on origin.
  1.1576 +      NS_ASSERTION(!op->mId.IsEmpty() && !aId.IsEmpty(),
  1.1577 +                   "Why didn't we match earlier?");
  1.1578 +    }
  1.1579 +  }
  1.1580 +
  1.1581 +  NS_NOTREACHED("Why didn't we find a SynchronizedOp?");
  1.1582 +}
  1.1583 +
  1.1584 +nsresult
  1.1585 +QuotaManager::GetDirectoryForOrigin(PersistenceType aPersistenceType,
  1.1586 +                                    const nsACString& aASCIIOrigin,
  1.1587 +                                    nsIFile** aDirectory) const
  1.1588 +{
  1.1589 +  nsresult rv;
  1.1590 +  nsCOMPtr<nsIFile> directory =
  1.1591 +    do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
  1.1592 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1593 +
  1.1594 +  rv = directory->InitWithPath(GetStoragePath(aPersistenceType));
  1.1595 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1596 +
  1.1597 +  nsAutoCString originSanitized(aASCIIOrigin);
  1.1598 +  SanitizeOriginString(originSanitized);
  1.1599 +
  1.1600 +  rv = directory->Append(NS_ConvertASCIItoUTF16(originSanitized));
  1.1601 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1602 +
  1.1603 +  directory.forget(aDirectory);
  1.1604 +  return NS_OK;
  1.1605 +}
  1.1606 +
  1.1607 +nsresult
  1.1608 +QuotaManager::InitializeOrigin(PersistenceType aPersistenceType,
  1.1609 +                               const nsACString& aGroup,
  1.1610 +                               const nsACString& aOrigin,
  1.1611 +                               bool aTrackQuota,
  1.1612 +                               int64_t aAccessTime,
  1.1613 +                               nsIFile* aDirectory)
  1.1614 +{
  1.1615 +  AssertIsOnIOThread();
  1.1616 +
  1.1617 +  nsresult rv;
  1.1618 +
  1.1619 +  bool temporaryStorage = aPersistenceType == PERSISTENCE_TYPE_TEMPORARY;
  1.1620 +  if (!temporaryStorage) {
  1.1621 +    rv = MaybeUpgradeOriginDirectory(aDirectory);
  1.1622 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1623 +  }
  1.1624 +
  1.1625 +  // We need to initialize directories of all clients if they exists and also
  1.1626 +  // get the total usage to initialize the quota.
  1.1627 +  nsAutoPtr<UsageInfo> usageInfo;
  1.1628 +  if (aTrackQuota) {
  1.1629 +    usageInfo = new UsageInfo();
  1.1630 +  }
  1.1631 +
  1.1632 +  nsCOMPtr<nsISimpleEnumerator> entries;
  1.1633 +  rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
  1.1634 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1635 +
  1.1636 +  bool hasMore;
  1.1637 +  while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
  1.1638 +    nsCOMPtr<nsISupports> entry;
  1.1639 +    rv = entries->GetNext(getter_AddRefs(entry));
  1.1640 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1641 +
  1.1642 +    nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
  1.1643 +    NS_ENSURE_TRUE(file, NS_NOINTERFACE);
  1.1644 +
  1.1645 +    nsString leafName;
  1.1646 +    rv = file->GetLeafName(leafName);
  1.1647 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1648 +
  1.1649 +    if (leafName.EqualsLiteral(METADATA_FILE_NAME) ||
  1.1650 +        leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
  1.1651 +      continue;
  1.1652 +    }
  1.1653 +
  1.1654 +    bool isDirectory;
  1.1655 +    rv = file->IsDirectory(&isDirectory);
  1.1656 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1657 +
  1.1658 +    if (!isDirectory) {
  1.1659 +      NS_WARNING("Unknown file found!");
  1.1660 +      return NS_ERROR_UNEXPECTED;
  1.1661 +    }
  1.1662 +
  1.1663 +    Client::Type clientType;
  1.1664 +    rv = Client::TypeFromText(leafName, clientType);
  1.1665 +    if (NS_FAILED(rv)) {
  1.1666 +      NS_WARNING("Unknown directory found!");
  1.1667 +      return NS_ERROR_UNEXPECTED;
  1.1668 +    }
  1.1669 +
  1.1670 +    rv = mClients[clientType]->InitOrigin(aPersistenceType, aGroup, aOrigin,
  1.1671 +                                          usageInfo);
  1.1672 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1673 +  }
  1.1674 +
  1.1675 +  if (aTrackQuota) {
  1.1676 +    uint64_t quotaMaxBytes;
  1.1677 +    uint64_t totalUsageBytes = usageInfo->TotalUsage();
  1.1678 +
  1.1679 +    if (temporaryStorage) {
  1.1680 +      // Temporary storage has no limit for origin usage (there's a group and
  1.1681 +      // the global limit though).
  1.1682 +      quotaMaxBytes = 0;
  1.1683 +    }
  1.1684 +    else {
  1.1685 +      quotaMaxBytes = GetStorageQuotaMB() * 1024 * 1024;
  1.1686 +      if (totalUsageBytes > quotaMaxBytes) {
  1.1687 +        NS_WARNING("Origin is already using more storage than allowed!");
  1.1688 +        return NS_ERROR_UNEXPECTED;
  1.1689 +      }
  1.1690 +    }
  1.1691 +
  1.1692 +    InitQuotaForOrigin(aPersistenceType, aGroup, aOrigin, quotaMaxBytes,
  1.1693 +                       totalUsageBytes, aAccessTime);
  1.1694 +  }
  1.1695 +
  1.1696 +  return NS_OK;
  1.1697 +}
  1.1698 +
  1.1699 +nsresult
  1.1700 +QuotaManager::MaybeUpgradeIndexedDBDirectory()
  1.1701 +{
  1.1702 +  AssertIsOnIOThread();
  1.1703 +
  1.1704 +  if (mStorageAreaInitialized) {
  1.1705 +    return NS_OK;
  1.1706 +  }
  1.1707 +
  1.1708 +  nsresult rv;
  1.1709 +
  1.1710 +  nsCOMPtr<nsIFile> indexedDBDir =
  1.1711 +    do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
  1.1712 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1713 +
  1.1714 +  rv = indexedDBDir->InitWithPath(mIndexedDBPath);
  1.1715 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1716 +
  1.1717 +  bool exists;
  1.1718 +  rv = indexedDBDir->Exists(&exists);
  1.1719 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1720 +
  1.1721 +  if (!exists) {
  1.1722 +    // Nothing to upgrade.
  1.1723 +    mStorageAreaInitialized = true;
  1.1724 +
  1.1725 +    return NS_OK;
  1.1726 +  }
  1.1727 +
  1.1728 +  bool isDirectory;
  1.1729 +  rv = indexedDBDir->IsDirectory(&isDirectory);
  1.1730 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1731 +
  1.1732 +  if (!isDirectory) {
  1.1733 +    NS_WARNING("indexedDB entry is not a directory!");
  1.1734 +    return NS_OK;
  1.1735 +  }
  1.1736 +
  1.1737 +  nsCOMPtr<nsIFile> persistentStorageDir =
  1.1738 +    do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
  1.1739 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1740 +
  1.1741 +  rv = persistentStorageDir->InitWithPath(mPersistentStoragePath);
  1.1742 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1743 +
  1.1744 +  rv = persistentStorageDir->Exists(&exists);
  1.1745 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1746 +
  1.1747 +  if (exists) {
  1.1748 +    NS_WARNING("indexedDB directory shouldn't exist after the upgrade!");
  1.1749 +    return NS_OK;
  1.1750 +  }
  1.1751 +
  1.1752 +  nsCOMPtr<nsIFile> storageDir;
  1.1753 +  rv = persistentStorageDir->GetParent(getter_AddRefs(storageDir));
  1.1754 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1755 +
  1.1756 +  nsString persistentStorageName;
  1.1757 +  rv = persistentStorageDir->GetLeafName(persistentStorageName);
  1.1758 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1759 +
  1.1760 +  // MoveTo() is atomic if the move happens on the same volume which should
  1.1761 +  // be our case, so even if we crash in the middle of the operation nothing
  1.1762 +  // breaks next time we try to initialize.
  1.1763 +  // However there's a theoretical possibility that the indexedDB directory
  1.1764 +  // is on different volume, but it should be rare enough that we don't have
  1.1765 +  // to worry about it.
  1.1766 +  rv = indexedDBDir->MoveTo(storageDir, persistentStorageName);
  1.1767 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1768 +
  1.1769 +  mStorageAreaInitialized = true;
  1.1770 +
  1.1771 +  return NS_OK;
  1.1772 +}
  1.1773 +
  1.1774 +nsresult
  1.1775 +QuotaManager::EnsureOriginIsInitialized(PersistenceType aPersistenceType,
  1.1776 +                                        const nsACString& aGroup,
  1.1777 +                                        const nsACString& aOrigin,
  1.1778 +                                        bool aTrackQuota,
  1.1779 +                                        nsIFile** aDirectory)
  1.1780 +{
  1.1781 +  AssertIsOnIOThread();
  1.1782 +
  1.1783 +  nsresult rv = MaybeUpgradeIndexedDBDirectory();
  1.1784 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1785 +
  1.1786 +  // Get directory for this origin and persistence type.
  1.1787 +  nsCOMPtr<nsIFile> directory;
  1.1788 +  rv = GetDirectoryForOrigin(aPersistenceType, aOrigin,
  1.1789 +                             getter_AddRefs(directory));
  1.1790 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1791 +
  1.1792 +  if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) {
  1.1793 +    if (mInitializedOrigins.Contains(aOrigin)) {
  1.1794 +      NS_ADDREF(*aDirectory = directory);
  1.1795 +      return NS_OK;
  1.1796 +    }
  1.1797 +
  1.1798 +    bool created;
  1.1799 +    rv = EnsureDirectory(directory, &created);
  1.1800 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1801 +
  1.1802 +    if (created) {
  1.1803 +      rv = CreateDirectoryUpgradeStamp(directory);
  1.1804 +      NS_ENSURE_SUCCESS(rv, rv);
  1.1805 +    }
  1.1806 +
  1.1807 +    rv = InitializeOrigin(aPersistenceType, aGroup, aOrigin, aTrackQuota, 0,
  1.1808 +                          directory);
  1.1809 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1810 +
  1.1811 +    mInitializedOrigins.AppendElement(aOrigin);
  1.1812 +
  1.1813 +    directory.forget(aDirectory);
  1.1814 +    return NS_OK;
  1.1815 +  }
  1.1816 +
  1.1817 +  NS_ASSERTION(aPersistenceType == PERSISTENCE_TYPE_TEMPORARY, "Huh?");
  1.1818 +  NS_ASSERTION(aTrackQuota, "Huh?");
  1.1819 +
  1.1820 +  if (!mTemporaryStorageInitialized) {
  1.1821 +    nsCOMPtr<nsIFile> parentDirectory;
  1.1822 +    rv = directory->GetParent(getter_AddRefs(parentDirectory));
  1.1823 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1824 +
  1.1825 +    bool created;
  1.1826 +    rv = EnsureDirectory(parentDirectory, &created);
  1.1827 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1828 +
  1.1829 +    nsCOMPtr<nsISimpleEnumerator> entries;
  1.1830 +    rv = parentDirectory->GetDirectoryEntries(getter_AddRefs(entries));
  1.1831 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1832 +
  1.1833 +    bool hasMore;
  1.1834 +    while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
  1.1835 +      nsCOMPtr<nsISupports> entry;
  1.1836 +      rv = entries->GetNext(getter_AddRefs(entry));
  1.1837 +      NS_ENSURE_SUCCESS(rv, rv);
  1.1838 +
  1.1839 +      nsCOMPtr<nsIFile> childDirectory = do_QueryInterface(entry);
  1.1840 +      NS_ENSURE_TRUE(childDirectory, NS_NOINTERFACE);
  1.1841 +
  1.1842 +      bool isDirectory;
  1.1843 +      rv = childDirectory->IsDirectory(&isDirectory);
  1.1844 +      NS_ENSURE_SUCCESS(rv, rv);
  1.1845 +      NS_ENSURE_TRUE(isDirectory, NS_ERROR_UNEXPECTED);
  1.1846 +
  1.1847 +      int64_t timestamp;
  1.1848 +      nsCString group;
  1.1849 +      nsCString origin;
  1.1850 +      rv = GetDirectoryMetadata(childDirectory, &timestamp, group, origin);
  1.1851 +      NS_ENSURE_SUCCESS(rv, rv);
  1.1852 +
  1.1853 +      rv = InitializeOrigin(aPersistenceType, group, origin, aTrackQuota,
  1.1854 +                            timestamp, childDirectory);
  1.1855 +      if (NS_FAILED(rv)) {
  1.1856 +        NS_WARNING("Failed to initialize origin!");
  1.1857 +
  1.1858 +        // We have to cleanup partially initialized quota for temporary storage.
  1.1859 +        RemoveQuotaForPersistenceType(aPersistenceType);
  1.1860 +
  1.1861 +        return rv;
  1.1862 +      }
  1.1863 +    }
  1.1864 +
  1.1865 +    if (gFixedLimitKB >= 0) {
  1.1866 +      mTemporaryStorageLimit = gFixedLimitKB * 1024;
  1.1867 +    }
  1.1868 +    else {
  1.1869 +      rv = GetTemporaryStorageLimit(parentDirectory, mTemporaryStorageUsage,
  1.1870 +                                    &mTemporaryStorageLimit);
  1.1871 +      NS_ENSURE_SUCCESS(rv, rv);
  1.1872 +    }
  1.1873 +
  1.1874 +    mTemporaryStorageInitialized = true;
  1.1875 +
  1.1876 +    CheckTemporaryStorageLimits();
  1.1877 +  }
  1.1878 +
  1.1879 +  bool created;
  1.1880 +  rv = EnsureDirectory(directory, &created);
  1.1881 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1882 +
  1.1883 +  if (created) {
  1.1884 +    int64_t timestamp = PR_Now();
  1.1885 +
  1.1886 +    rv = CreateDirectoryMetadata(directory, timestamp, aGroup, aOrigin);
  1.1887 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1888 +
  1.1889 +    rv = InitializeOrigin(aPersistenceType, aGroup, aOrigin, aTrackQuota,
  1.1890 +                          timestamp, directory);
  1.1891 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1892 +  }
  1.1893 +
  1.1894 +  directory.forget(aDirectory);
  1.1895 +  return NS_OK;
  1.1896 +}
  1.1897 +
  1.1898 +void
  1.1899 +QuotaManager::OriginClearCompleted(
  1.1900 +                                  PersistenceType aPersistenceType,
  1.1901 +                                  const OriginOrPatternString& aOriginOrPattern)
  1.1902 +{
  1.1903 +  AssertIsOnIOThread();
  1.1904 +
  1.1905 +  if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) {
  1.1906 +    if (aOriginOrPattern.IsOrigin()) {
  1.1907 +      mInitializedOrigins.RemoveElement(aOriginOrPattern);
  1.1908 +    }
  1.1909 +    else {
  1.1910 +      for (uint32_t index = mInitializedOrigins.Length(); index > 0; index--) {
  1.1911 +        if (PatternMatchesOrigin(aOriginOrPattern,
  1.1912 +                                 mInitializedOrigins[index - 1])) {
  1.1913 +          mInitializedOrigins.RemoveElementAt(index - 1);
  1.1914 +        }
  1.1915 +      }
  1.1916 +    }
  1.1917 +  }
  1.1918 +
  1.1919 +  for (uint32_t index = 0; index < Client::TYPE_MAX; index++) {
  1.1920 +    mClients[index]->OnOriginClearCompleted(aPersistenceType, aOriginOrPattern);
  1.1921 +  }
  1.1922 +}
  1.1923 +
  1.1924 +void
  1.1925 +QuotaManager::ResetOrClearCompleted()
  1.1926 +{
  1.1927 +  AssertIsOnIOThread();
  1.1928 +
  1.1929 +  mInitializedOrigins.Clear();
  1.1930 +  mTemporaryStorageInitialized = false;
  1.1931 +
  1.1932 +  ReleaseIOThreadObjects();
  1.1933 +}
  1.1934 +
  1.1935 +already_AddRefed<mozilla::dom::quota::Client>
  1.1936 +QuotaManager::GetClient(Client::Type aClientType)
  1.1937 +{
  1.1938 +  nsRefPtr<Client> client = mClients.SafeElementAt(aClientType);
  1.1939 +  return client.forget();
  1.1940 +}
  1.1941 +
  1.1942 +uint64_t
  1.1943 +QuotaManager::GetGroupLimit() const
  1.1944 +{
  1.1945 +  MOZ_ASSERT(mTemporaryStorageInitialized);
  1.1946 +
  1.1947 +  // To avoid one group evicting all the rest, limit the amount any one group
  1.1948 +  // can use to 20%. To prevent individual sites from using exorbitant amounts
  1.1949 +  // of storage where there is a lot of free space, cap the group limit to 2GB.
  1.1950 +  uint64_t x = std::min<uint64_t>(mTemporaryStorageLimit * .20, 2 GB);
  1.1951 +
  1.1952 +  // In low-storage situations, make an exception (while not exceeding the total
  1.1953 +  // storage limit).
  1.1954 +  return std::min<uint64_t>(mTemporaryStorageLimit,
  1.1955 +                            std::max<uint64_t>(x, 10 MB));
  1.1956 +}
  1.1957 +
  1.1958 +// static
  1.1959 +uint32_t
  1.1960 +QuotaManager::GetStorageQuotaMB()
  1.1961 +{
  1.1962 +  return uint32_t(std::max(gStorageQuotaMB, 0));
  1.1963 +}
  1.1964 +
  1.1965 +// static
  1.1966 +void
  1.1967 +QuotaManager::GetStorageId(PersistenceType aPersistenceType,
  1.1968 +                           const nsACString& aOrigin,
  1.1969 +                           Client::Type aClientType,
  1.1970 +                           const nsAString& aName,
  1.1971 +                           nsACString& aDatabaseId)
  1.1972 +{
  1.1973 +  nsAutoCString str;
  1.1974 +  str.AppendInt(aPersistenceType);
  1.1975 +  str.Append('*');
  1.1976 +  str.Append(aOrigin);
  1.1977 +  str.Append('*');
  1.1978 +  str.AppendInt(aClientType);
  1.1979 +  str.Append('*');
  1.1980 +  str.Append(NS_ConvertUTF16toUTF8(aName));
  1.1981 +
  1.1982 +  aDatabaseId = str;
  1.1983 +}
  1.1984 +
  1.1985 +// static
  1.1986 +nsresult
  1.1987 +QuotaManager::GetInfoFromURI(nsIURI* aURI,
  1.1988 +                             uint32_t aAppId,
  1.1989 +                             bool aInMozBrowser,
  1.1990 +                             nsACString* aGroup,
  1.1991 +                             nsACString* aASCIIOrigin,
  1.1992 +                             StoragePrivilege* aPrivilege,
  1.1993 +                             PersistenceType* aDefaultPersistenceType)
  1.1994 +{
  1.1995 +  NS_ASSERTION(aURI, "Null uri!");
  1.1996 +
  1.1997 +  nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
  1.1998 +  NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE);
  1.1999 +
  1.2000 +  nsCOMPtr<nsIPrincipal> principal;
  1.2001 +  nsresult rv = secMan->GetAppCodebasePrincipal(aURI, aAppId, aInMozBrowser,
  1.2002 +                                                getter_AddRefs(principal));
  1.2003 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2004 +
  1.2005 +  rv = GetInfoFromPrincipal(principal, aGroup, aASCIIOrigin, aPrivilege,
  1.2006 +                            aDefaultPersistenceType);
  1.2007 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2008 +
  1.2009 +  return NS_OK;
  1.2010 +}
  1.2011 +
  1.2012 +// static
  1.2013 +nsresult
  1.2014 +QuotaManager::GetInfoFromPrincipal(nsIPrincipal* aPrincipal,
  1.2015 +                                   nsACString* aGroup,
  1.2016 +                                   nsACString* aASCIIOrigin,
  1.2017 +                                   StoragePrivilege* aPrivilege,
  1.2018 +                                   PersistenceType* aDefaultPersistenceType)
  1.2019 +{
  1.2020 +  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1.2021 +  NS_ASSERTION(aPrincipal, "Don't hand me a null principal!");
  1.2022 +
  1.2023 +  if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
  1.2024 +    GetInfoForChrome(aGroup, aASCIIOrigin, aPrivilege, aDefaultPersistenceType);
  1.2025 +    return NS_OK;
  1.2026 +  }
  1.2027 +
  1.2028 +  bool isNullPrincipal;
  1.2029 +  nsresult rv = aPrincipal->GetIsNullPrincipal(&isNullPrincipal);
  1.2030 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2031 +
  1.2032 +  if (isNullPrincipal) {
  1.2033 +    NS_WARNING("IndexedDB not supported from this principal!");
  1.2034 +    return NS_ERROR_FAILURE;
  1.2035 +  }
  1.2036 +
  1.2037 +  nsCString origin;
  1.2038 +  rv = aPrincipal->GetOrigin(getter_Copies(origin));
  1.2039 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2040 +
  1.2041 +  if (origin.EqualsLiteral("chrome")) {
  1.2042 +    NS_WARNING("Non-chrome principal can't use chrome origin!");
  1.2043 +    return NS_ERROR_FAILURE;
  1.2044 +  }
  1.2045 +
  1.2046 +  nsCString jarPrefix;
  1.2047 +  if (aGroup || aASCIIOrigin) {
  1.2048 +    rv = aPrincipal->GetJarPrefix(jarPrefix);
  1.2049 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2050 +  }
  1.2051 +
  1.2052 +  if (aGroup) {
  1.2053 +    nsCString baseDomain;
  1.2054 +    rv = aPrincipal->GetBaseDomain(baseDomain);
  1.2055 +    if (NS_FAILED(rv)) {
  1.2056 +      // A hack for JetPack.
  1.2057 +
  1.2058 +      nsCOMPtr<nsIURI> uri;
  1.2059 +      rv = aPrincipal->GetURI(getter_AddRefs(uri));
  1.2060 +      NS_ENSURE_SUCCESS(rv, rv);
  1.2061 +
  1.2062 +      bool isIndexedDBURI = false;
  1.2063 +      rv = uri->SchemeIs("indexedDB", &isIndexedDBURI);
  1.2064 +      NS_ENSURE_SUCCESS(rv, rv);
  1.2065 +
  1.2066 +      if (isIndexedDBURI) {
  1.2067 +        rv = NS_OK;
  1.2068 +      }
  1.2069 +    }
  1.2070 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2071 +
  1.2072 +    if (baseDomain.IsEmpty()) {
  1.2073 +      aGroup->Assign(jarPrefix + origin);
  1.2074 +    }
  1.2075 +    else {
  1.2076 +      aGroup->Assign(jarPrefix + baseDomain);
  1.2077 +    }
  1.2078 +  }
  1.2079 +
  1.2080 +  if (aASCIIOrigin) {
  1.2081 +    aASCIIOrigin->Assign(jarPrefix + origin);
  1.2082 +  }
  1.2083 +
  1.2084 +  if (aPrivilege) {
  1.2085 +    *aPrivilege = Content;
  1.2086 +  }
  1.2087 +
  1.2088 +  if (aDefaultPersistenceType) {
  1.2089 +    *aDefaultPersistenceType = PERSISTENCE_TYPE_PERSISTENT;
  1.2090 +  }
  1.2091 +
  1.2092 +  return NS_OK;
  1.2093 +}
  1.2094 +
  1.2095 +// static
  1.2096 +nsresult
  1.2097 +QuotaManager::GetInfoFromWindow(nsPIDOMWindow* aWindow,
  1.2098 +                                nsACString* aGroup,
  1.2099 +                                nsACString* aASCIIOrigin,
  1.2100 +                                StoragePrivilege* aPrivilege,
  1.2101 +                                PersistenceType* aDefaultPersistenceType)
  1.2102 +{
  1.2103 +  NS_ASSERTION(NS_IsMainThread(),
  1.2104 +               "We're about to touch a window off the main thread!");
  1.2105 +  NS_ASSERTION(aWindow, "Don't hand me a null window!");
  1.2106 +
  1.2107 +  nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aWindow);
  1.2108 +  NS_ENSURE_TRUE(sop, NS_ERROR_FAILURE);
  1.2109 +
  1.2110 +  nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
  1.2111 +  NS_ENSURE_TRUE(principal, NS_ERROR_FAILURE);
  1.2112 +
  1.2113 +  nsresult rv = GetInfoFromPrincipal(principal, aGroup, aASCIIOrigin,
  1.2114 +                                     aPrivilege, aDefaultPersistenceType);
  1.2115 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2116 +
  1.2117 +  return NS_OK;
  1.2118 +}
  1.2119 +
  1.2120 +// static
  1.2121 +void
  1.2122 +QuotaManager::GetInfoForChrome(nsACString* aGroup,
  1.2123 +                               nsACString* aASCIIOrigin,
  1.2124 +                               StoragePrivilege* aPrivilege,
  1.2125 +                               PersistenceType* aDefaultPersistenceType)
  1.2126 +{
  1.2127 +  NS_ASSERTION(nsContentUtils::IsCallerChrome(), "Only for chrome!");
  1.2128 +
  1.2129 +  static const char kChromeOrigin[] = "chrome";
  1.2130 +
  1.2131 +  if (aGroup) {
  1.2132 +    aGroup->AssignLiteral(kChromeOrigin);
  1.2133 +  }
  1.2134 +  if (aASCIIOrigin) {
  1.2135 +    aASCIIOrigin->AssignLiteral(kChromeOrigin);
  1.2136 +  }
  1.2137 +  if (aPrivilege) {
  1.2138 +    *aPrivilege = Chrome;
  1.2139 +  }
  1.2140 +  if (aDefaultPersistenceType) {
  1.2141 +    *aDefaultPersistenceType = PERSISTENCE_TYPE_PERSISTENT;
  1.2142 +  }
  1.2143 +}
  1.2144 +
  1.2145 +NS_IMPL_ISUPPORTS(QuotaManager, nsIQuotaManager, nsIObserver)
  1.2146 +
  1.2147 +NS_IMETHODIMP
  1.2148 +QuotaManager::GetUsageForURI(nsIURI* aURI,
  1.2149 +                             nsIUsageCallback* aCallback,
  1.2150 +                             uint32_t aAppId,
  1.2151 +                             bool aInMozBrowserOnly,
  1.2152 +                             uint8_t aOptionalArgCount,
  1.2153 +                             nsIQuotaRequest** _retval)
  1.2154 +{
  1.2155 +  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1.2156 +
  1.2157 +  NS_ENSURE_ARG_POINTER(aURI);
  1.2158 +  NS_ENSURE_ARG_POINTER(aCallback);
  1.2159 +
  1.2160 +  // This only works from the main process.
  1.2161 +  NS_ENSURE_TRUE(IsMainProcess(), NS_ERROR_NOT_AVAILABLE);
  1.2162 +
  1.2163 +  if (!aOptionalArgCount) {
  1.2164 +    aAppId = nsIScriptSecurityManager::NO_APP_ID;
  1.2165 +  }
  1.2166 +
  1.2167 +  // Figure out which origin we're dealing with.
  1.2168 +  nsCString group;
  1.2169 +  nsCString origin;
  1.2170 +  nsresult rv = GetInfoFromURI(aURI, aAppId, aInMozBrowserOnly, &group, &origin,
  1.2171 +                               nullptr, nullptr);
  1.2172 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2173 +
  1.2174 +  OriginOrPatternString oops = OriginOrPatternString::FromOrigin(origin);
  1.2175 +
  1.2176 +  nsRefPtr<AsyncUsageRunnable> runnable =
  1.2177 +    new AsyncUsageRunnable(aAppId, aInMozBrowserOnly, group, oops, aURI,
  1.2178 +                           aCallback);
  1.2179 +
  1.2180 +  // Put the computation runnable in the queue.
  1.2181 +  rv = WaitForOpenAllowed(oops, Nullable<PersistenceType>(), EmptyCString(),
  1.2182 +                          runnable);
  1.2183 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2184 +
  1.2185 +  runnable->AdvanceState();
  1.2186 +
  1.2187 +  runnable.forget(_retval);
  1.2188 +  return NS_OK;
  1.2189 +}
  1.2190 +
  1.2191 +NS_IMETHODIMP
  1.2192 +QuotaManager::Clear()
  1.2193 +{
  1.2194 +  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1.2195 +
  1.2196 +  if (!gTestingEnabled) {
  1.2197 +    NS_WARNING("Testing features are not enabled!");
  1.2198 +    return NS_OK;
  1.2199 +  }
  1.2200 +
  1.2201 +  OriginOrPatternString oops = OriginOrPatternString::FromNull();
  1.2202 +
  1.2203 +  nsRefPtr<ResetOrClearRunnable> runnable = new ResetOrClearRunnable(true);
  1.2204 +
  1.2205 +  // Put the clear runnable in the queue.
  1.2206 +  nsresult rv =
  1.2207 +    WaitForOpenAllowed(oops, Nullable<PersistenceType>(), EmptyCString(),
  1.2208 +                       runnable);
  1.2209 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2210 +
  1.2211 +  runnable->AdvanceState();
  1.2212 +
  1.2213 +  // Give the runnable some help by invalidating any storages in the way.
  1.2214 +  StorageMatcher<nsAutoTArray<nsIOfflineStorage*, 20> > matches;
  1.2215 +  matches.Find(mLiveStorages);
  1.2216 +
  1.2217 +  for (uint32_t index = 0; index < matches.Length(); index++) {
  1.2218 +    // We need to grab references to any live storages here to prevent them
  1.2219 +    // from dying while we invalidate them.
  1.2220 +    nsCOMPtr<nsIOfflineStorage> storage = matches[index];
  1.2221 +    storage->Invalidate();
  1.2222 +  }
  1.2223 +
  1.2224 +  // After everything has been invalidated the helper should be dispatched to
  1.2225 +  // the end of the event queue.
  1.2226 +  return NS_OK;
  1.2227 +}
  1.2228 +
  1.2229 +NS_IMETHODIMP
  1.2230 +QuotaManager::ClearStoragesForURI(nsIURI* aURI,
  1.2231 +                                  uint32_t aAppId,
  1.2232 +                                  bool aInMozBrowserOnly,
  1.2233 +                                  const nsACString& aPersistenceType,
  1.2234 +                                  uint8_t aOptionalArgCount)
  1.2235 +{
  1.2236 +  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1.2237 +
  1.2238 +  NS_ENSURE_ARG_POINTER(aURI);
  1.2239 +
  1.2240 +  // This only works from the main process.
  1.2241 +  NS_ENSURE_TRUE(IsMainProcess(), NS_ERROR_NOT_AVAILABLE);
  1.2242 +
  1.2243 +  if (!aOptionalArgCount) {
  1.2244 +    aAppId = nsIScriptSecurityManager::NO_APP_ID;
  1.2245 +  }
  1.2246 +
  1.2247 +  // Figure out which origin we're dealing with.
  1.2248 +  nsCString origin;
  1.2249 +  nsresult rv = GetInfoFromURI(aURI, aAppId, aInMozBrowserOnly, nullptr, &origin,
  1.2250 +                               nullptr, nullptr);
  1.2251 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2252 +
  1.2253 +  nsAutoCString pattern;
  1.2254 +  GetOriginPatternString(aAppId, aInMozBrowserOnly, origin, pattern);
  1.2255 +
  1.2256 +  Nullable<PersistenceType> persistenceType;
  1.2257 +  rv = NullablePersistenceTypeFromText(aPersistenceType, &persistenceType);
  1.2258 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2259 +
  1.2260 +  // If there is a pending or running clear operation for this origin, return
  1.2261 +  // immediately.
  1.2262 +  if (IsClearOriginPending(pattern, persistenceType)) {
  1.2263 +    return NS_OK;
  1.2264 +  }
  1.2265 +
  1.2266 +  OriginOrPatternString oops = OriginOrPatternString::FromPattern(pattern);
  1.2267 +
  1.2268 +  // Queue up the origin clear runnable.
  1.2269 +  nsRefPtr<OriginClearRunnable> runnable =
  1.2270 +    new OriginClearRunnable(oops, persistenceType);
  1.2271 +
  1.2272 +  rv = WaitForOpenAllowed(oops, persistenceType, EmptyCString(), runnable);
  1.2273 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2274 +
  1.2275 +  runnable->AdvanceState();
  1.2276 +
  1.2277 +  // Give the runnable some help by invalidating any storages in the way.
  1.2278 +  StorageMatcher<nsAutoTArray<nsIOfflineStorage*, 20> > matches;
  1.2279 +  matches.Find(mLiveStorages, pattern);
  1.2280 +
  1.2281 +  for (uint32_t index = 0; index < matches.Length(); index++) {
  1.2282 +    if (persistenceType.IsNull() ||
  1.2283 +        matches[index]->Type() == persistenceType.Value()) {
  1.2284 +      // We need to grab references to any live storages here to prevent them
  1.2285 +      // from dying while we invalidate them.
  1.2286 +      nsCOMPtr<nsIOfflineStorage> storage = matches[index];
  1.2287 +      storage->Invalidate();
  1.2288 +    }
  1.2289 +  }
  1.2290 +
  1.2291 +  // After everything has been invalidated the helper should be dispatched to
  1.2292 +  // the end of the event queue.
  1.2293 +  return NS_OK;
  1.2294 +}
  1.2295 +
  1.2296 +NS_IMETHODIMP
  1.2297 +QuotaManager::Reset()
  1.2298 +{
  1.2299 +  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1.2300 +
  1.2301 +  if (!gTestingEnabled) {
  1.2302 +    NS_WARNING("Testing features are not enabled!");
  1.2303 +    return NS_OK;
  1.2304 +  }
  1.2305 +
  1.2306 +  OriginOrPatternString oops = OriginOrPatternString::FromNull();
  1.2307 +
  1.2308 +  nsRefPtr<ResetOrClearRunnable> runnable = new ResetOrClearRunnable(false);
  1.2309 +
  1.2310 +  // Put the reset runnable in the queue.
  1.2311 +  nsresult rv =
  1.2312 +    WaitForOpenAllowed(oops, Nullable<PersistenceType>(), EmptyCString(),
  1.2313 +                       runnable);
  1.2314 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2315 +
  1.2316 +  runnable->AdvanceState();
  1.2317 +
  1.2318 +  // Give the runnable some help by invalidating any storages in the way.
  1.2319 +  StorageMatcher<nsAutoTArray<nsIOfflineStorage*, 20> > matches;
  1.2320 +  matches.Find(mLiveStorages);
  1.2321 +
  1.2322 +  for (uint32_t index = 0; index < matches.Length(); index++) {
  1.2323 +    // We need to grab references to any live storages here to prevent them
  1.2324 +    // from dying while we invalidate them.
  1.2325 +    nsCOMPtr<nsIOfflineStorage> storage = matches[index];
  1.2326 +    storage->Invalidate();
  1.2327 +  }
  1.2328 +
  1.2329 +  // After everything has been invalidated the helper should be dispatched to
  1.2330 +  // the end of the event queue.
  1.2331 +  return NS_OK;
  1.2332 +}
  1.2333 +
  1.2334 +NS_IMETHODIMP
  1.2335 +QuotaManager::Observe(nsISupports* aSubject,
  1.2336 +                      const char* aTopic,
  1.2337 +                      const char16_t* aData)
  1.2338 +{
  1.2339 +  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1.2340 +
  1.2341 +  if (!strcmp(aTopic, PROFILE_BEFORE_CHANGE_OBSERVER_ID)) {
  1.2342 +    // Setting this flag prevents the service from being recreated and prevents
  1.2343 +    // further storagess from being created.
  1.2344 +    if (gShutdown.exchange(true)) {
  1.2345 +      NS_ERROR("Shutdown more than once?!");
  1.2346 +    }
  1.2347 +
  1.2348 +    if (IsMainProcess()) {
  1.2349 +      FileService* service = FileService::Get();
  1.2350 +      if (service) {
  1.2351 +        // This should only wait for storages registered in this manager
  1.2352 +        // to complete. Other storages may still have running locked files.
  1.2353 +        // If the necko service (thread pool) gets the shutdown notification
  1.2354 +        // first then the sync loop won't be processed at all, otherwise it will
  1.2355 +        // lock the main thread until all storages registered in this manager
  1.2356 +        // are finished.
  1.2357 +
  1.2358 +        nsTArray<uint32_t> indexes;
  1.2359 +        for (uint32_t index = 0; index < Client::TYPE_MAX; index++) {
  1.2360 +          if (mClients[index]->IsFileServiceUtilized()) {
  1.2361 +            indexes.AppendElement(index);
  1.2362 +          }
  1.2363 +        }
  1.2364 +
  1.2365 +        StorageMatcher<nsTArray<nsCOMPtr<nsIFileStorage> > > liveStorages;
  1.2366 +        liveStorages.Find(mLiveStorages, &indexes);
  1.2367 +
  1.2368 +        if (!liveStorages.IsEmpty()) {
  1.2369 +          nsRefPtr<WaitForLockedFilesToFinishRunnable> runnable =
  1.2370 +            new WaitForLockedFilesToFinishRunnable();
  1.2371 +
  1.2372 +          service->WaitForStoragesToComplete(liveStorages, runnable);
  1.2373 +
  1.2374 +          nsIThread* thread = NS_GetCurrentThread();
  1.2375 +          while (runnable->IsBusy()) {
  1.2376 +            if (!NS_ProcessNextEvent(thread)) {
  1.2377 +              NS_ERROR("Failed to process next event!");
  1.2378 +              break;
  1.2379 +            }
  1.2380 +          }
  1.2381 +        }
  1.2382 +      }
  1.2383 +
  1.2384 +      // Kick off the shutdown timer.
  1.2385 +      if (NS_FAILED(mShutdownTimer->Init(this, DEFAULT_SHUTDOWN_TIMER_MS,
  1.2386 +                                         nsITimer::TYPE_ONE_SHOT))) {
  1.2387 +        NS_WARNING("Failed to initialize shutdown timer!");
  1.2388 +      }
  1.2389 +
  1.2390 +      // Each client will spin the event loop while we wait on all the threads
  1.2391 +      // to close. Our timer may fire during that loop.
  1.2392 +      for (uint32_t index = 0; index < Client::TYPE_MAX; index++) {
  1.2393 +        mClients[index]->ShutdownTransactionService();
  1.2394 +      }
  1.2395 +
  1.2396 +      // Cancel the timer regardless of whether it actually fired.
  1.2397 +      if (NS_FAILED(mShutdownTimer->Cancel())) {
  1.2398 +        NS_WARNING("Failed to cancel shutdown timer!");
  1.2399 +      }
  1.2400 +
  1.2401 +      // Give clients a chance to cleanup IO thread only objects.
  1.2402 +      nsCOMPtr<nsIRunnable> runnable =
  1.2403 +        NS_NewRunnableMethod(this, &QuotaManager::ReleaseIOThreadObjects);
  1.2404 +      if (!runnable) {
  1.2405 +        NS_WARNING("Failed to create runnable!");
  1.2406 +      }
  1.2407 +
  1.2408 +      if (NS_FAILED(mIOThread->Dispatch(runnable, NS_DISPATCH_NORMAL))) {
  1.2409 +        NS_WARNING("Failed to dispatch runnable!");
  1.2410 +      }
  1.2411 +
  1.2412 +      // Make sure to join with our IO thread.
  1.2413 +      if (NS_FAILED(mIOThread->Shutdown())) {
  1.2414 +        NS_WARNING("Failed to shutdown IO thread!");
  1.2415 +      }
  1.2416 +    }
  1.2417 +
  1.2418 +    return NS_OK;
  1.2419 +  }
  1.2420 +
  1.2421 +  if (!strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC)) {
  1.2422 +    NS_ASSERTION(IsMainProcess(), "Should only happen in the main process!");
  1.2423 +
  1.2424 +    NS_WARNING("Some storage operations are taking longer than expected "
  1.2425 +               "during shutdown and will be aborted!");
  1.2426 +
  1.2427 +    // Grab all live storages, for all origins.
  1.2428 +    StorageMatcher<nsAutoTArray<nsIOfflineStorage*, 50> > liveStorages;
  1.2429 +    liveStorages.Find(mLiveStorages);
  1.2430 +
  1.2431 +    // Invalidate them all.
  1.2432 +    if (!liveStorages.IsEmpty()) {
  1.2433 +      uint32_t count = liveStorages.Length();
  1.2434 +      for (uint32_t index = 0; index < count; index++) {
  1.2435 +        liveStorages[index]->Invalidate();
  1.2436 +      }
  1.2437 +    }
  1.2438 +
  1.2439 +    return NS_OK;
  1.2440 +  }
  1.2441 +
  1.2442 +  if (!strcmp(aTopic, TOPIC_WEB_APP_CLEAR_DATA)) {
  1.2443 +    nsCOMPtr<mozIApplicationClearPrivateDataParams> params =
  1.2444 +      do_QueryInterface(aSubject);
  1.2445 +    NS_ENSURE_TRUE(params, NS_ERROR_UNEXPECTED);
  1.2446 +
  1.2447 +    uint32_t appId;
  1.2448 +    nsresult rv = params->GetAppId(&appId);
  1.2449 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2450 +
  1.2451 +    bool browserOnly;
  1.2452 +    rv = params->GetBrowserOnly(&browserOnly);
  1.2453 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2454 +
  1.2455 +    rv = ClearStoragesForApp(appId, browserOnly);
  1.2456 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2457 +
  1.2458 +    return NS_OK;
  1.2459 +  }
  1.2460 +
  1.2461 +  NS_NOTREACHED("Unknown topic!");
  1.2462 +  return NS_ERROR_UNEXPECTED;
  1.2463 +}
  1.2464 +
  1.2465 +void
  1.2466 +QuotaManager::SetCurrentWindowInternal(nsPIDOMWindow* aWindow)
  1.2467 +{
  1.2468 +  NS_ASSERTION(mCurrentWindowIndex != BAD_TLS_INDEX,
  1.2469 +               "Should have a valid TLS storage index!");
  1.2470 +
  1.2471 +  if (aWindow) {
  1.2472 +    NS_ASSERTION(!PR_GetThreadPrivate(mCurrentWindowIndex),
  1.2473 +                 "Somebody forgot to clear the current window!");
  1.2474 +    PR_SetThreadPrivate(mCurrentWindowIndex, aWindow);
  1.2475 +  }
  1.2476 +  else {
  1.2477 +    // We cannot assert PR_GetThreadPrivate(mCurrentWindowIndex) here because
  1.2478 +    // there are some cases where we did not already have a window.
  1.2479 +    PR_SetThreadPrivate(mCurrentWindowIndex, nullptr);
  1.2480 +  }
  1.2481 +}
  1.2482 +
  1.2483 +void
  1.2484 +QuotaManager::CancelPromptsForWindowInternal(nsPIDOMWindow* aWindow)
  1.2485 +{
  1.2486 +  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1.2487 +
  1.2488 +  nsRefPtr<CheckQuotaHelper> helper;
  1.2489 +
  1.2490 +  MutexAutoLock autoLock(mQuotaMutex);
  1.2491 +
  1.2492 +  if (mCheckQuotaHelpers.Get(aWindow, getter_AddRefs(helper))) {
  1.2493 +    helper->Cancel();
  1.2494 +  }
  1.2495 +}
  1.2496 +
  1.2497 +bool
  1.2498 +QuotaManager::LockedQuotaIsLifted()
  1.2499 +{
  1.2500 +  mQuotaMutex.AssertCurrentThreadOwns();
  1.2501 +
  1.2502 +  NS_ASSERTION(mCurrentWindowIndex != BAD_TLS_INDEX,
  1.2503 +               "Should have a valid TLS storage index!");
  1.2504 +
  1.2505 +  nsPIDOMWindow* window =
  1.2506 +    static_cast<nsPIDOMWindow*>(PR_GetThreadPrivate(mCurrentWindowIndex));
  1.2507 +
  1.2508 +  // Quota is not enforced in chrome contexts (e.g. for components and JSMs)
  1.2509 +  // so we must have a window here.
  1.2510 +  NS_ASSERTION(window, "Why don't we have a Window here?");
  1.2511 +
  1.2512 +  bool createdHelper = false;
  1.2513 +
  1.2514 +  nsRefPtr<CheckQuotaHelper> helper;
  1.2515 +  if (!mCheckQuotaHelpers.Get(window, getter_AddRefs(helper))) {
  1.2516 +    helper = new CheckQuotaHelper(window, mQuotaMutex);
  1.2517 +    createdHelper = true;
  1.2518 +
  1.2519 +    mCheckQuotaHelpers.Put(window, helper);
  1.2520 +
  1.2521 +    // Unlock while calling out to XPCOM (code behind the dispatch method needs
  1.2522 +    // to acquire its own lock which can potentially lead to a deadlock and it
  1.2523 +    // also calls an observer that can do various stuff like IO, so it's better
  1.2524 +    // to not hold our mutex while that happens).
  1.2525 +    {
  1.2526 +      MutexAutoUnlock autoUnlock(mQuotaMutex);
  1.2527 +
  1.2528 +      nsresult rv = NS_DispatchToMainThread(helper);
  1.2529 +      NS_ENSURE_SUCCESS(rv, false);
  1.2530 +    }
  1.2531 +
  1.2532 +    // Relocked.  If any other threads hit the quota limit on the same Window,
  1.2533 +    // they are using the helper we created here and are now blocking in
  1.2534 +    // PromptAndReturnQuotaDisabled.
  1.2535 +  }
  1.2536 +
  1.2537 +  bool result = helper->PromptAndReturnQuotaIsDisabled();
  1.2538 +
  1.2539 +  // If this thread created the helper and added it to the hash, this thread
  1.2540 +  // must remove it.
  1.2541 +  if (createdHelper) {
  1.2542 +    mCheckQuotaHelpers.Remove(window);
  1.2543 +  }
  1.2544 +
  1.2545 +  return result;
  1.2546 +}
  1.2547 +
  1.2548 +uint64_t
  1.2549 +QuotaManager::LockedCollectOriginsForEviction(
  1.2550 +                                            uint64_t aMinSizeToBeFreed,
  1.2551 +                                            nsTArray<OriginInfo*>& aOriginInfos)
  1.2552 +{
  1.2553 +  mQuotaMutex.AssertCurrentThreadOwns();
  1.2554 +
  1.2555 +  nsRefPtr<CollectOriginsHelper> helper =
  1.2556 +    new CollectOriginsHelper(mQuotaMutex, aMinSizeToBeFreed);
  1.2557 +
  1.2558 +  // Unlock while calling out to XPCOM (see the detailed comment in
  1.2559 +  // LockedQuotaIsLifted)
  1.2560 +  {
  1.2561 +    MutexAutoUnlock autoUnlock(mQuotaMutex);
  1.2562 +
  1.2563 +    if (NS_FAILED(NS_DispatchToMainThread(helper))) {
  1.2564 +      NS_WARNING("Failed to dispatch to the main thread!");
  1.2565 +    }
  1.2566 +  }
  1.2567 +
  1.2568 +  return helper->BlockAndReturnOriginsForEviction(aOriginInfos);
  1.2569 +}
  1.2570 +
  1.2571 +void
  1.2572 +QuotaManager::LockedRemoveQuotaForOrigin(PersistenceType aPersistenceType,
  1.2573 +                                         const nsACString& aGroup,
  1.2574 +                                         const nsACString& aOrigin)
  1.2575 +{
  1.2576 +  mQuotaMutex.AssertCurrentThreadOwns();
  1.2577 +
  1.2578 +  GroupInfoPair* pair;
  1.2579 +  mGroupInfoPairs.Get(aGroup, &pair);
  1.2580 +
  1.2581 +  if (!pair) {
  1.2582 +    return;
  1.2583 +  }
  1.2584 +
  1.2585 +  nsRefPtr<GroupInfo> groupInfo = pair->LockedGetGroupInfo(aPersistenceType);
  1.2586 +  if (groupInfo) {
  1.2587 +    groupInfo->LockedRemoveOriginInfo(aOrigin);
  1.2588 +
  1.2589 +    if (!groupInfo->LockedHasOriginInfos()) {
  1.2590 +      pair->LockedClearGroupInfo(aPersistenceType);
  1.2591 +
  1.2592 +      if (!pair->LockedHasGroupInfos()) {
  1.2593 +        mGroupInfoPairs.Remove(aGroup);
  1.2594 +      }
  1.2595 +    }
  1.2596 +  }
  1.2597 +}
  1.2598 +
  1.2599 +nsresult
  1.2600 +QuotaManager::AcquireExclusiveAccess(const nsACString& aPattern,
  1.2601 +                                     Nullable<PersistenceType> aPersistenceType,
  1.2602 +                                     nsIOfflineStorage* aStorage,
  1.2603 +                                     AcquireListener* aListener,
  1.2604 +                                     WaitingOnStoragesCallback aCallback,
  1.2605 +                                     void* aClosure)
  1.2606 +{
  1.2607 +  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1.2608 +  NS_ASSERTION(aListener, "Need a listener!");
  1.2609 +
  1.2610 +  // Find the right SynchronizedOp.
  1.2611 +  SynchronizedOp* op =
  1.2612 +    FindSynchronizedOp(aPattern, aPersistenceType,
  1.2613 +                       aStorage ? aStorage->Id() : EmptyCString());
  1.2614 +
  1.2615 +  NS_ASSERTION(op, "We didn't find a SynchronizedOp?");
  1.2616 +  NS_ASSERTION(!op->mListener, "SynchronizedOp already has a listener?!?");
  1.2617 +
  1.2618 +  nsTArray<nsCOMPtr<nsIOfflineStorage> > liveStorages;
  1.2619 +
  1.2620 +  if (aStorage) {
  1.2621 +    // We need to wait for the storages to go away.
  1.2622 +    // Hold on to all storage objects that represent the same storage file
  1.2623 +    // (except the one that is requesting this version change).
  1.2624 +
  1.2625 +    Client::Type clientType = aStorage->GetClient()->GetType();
  1.2626 +
  1.2627 +    StorageMatcher<nsAutoTArray<nsIOfflineStorage*, 20> > matches;
  1.2628 +    matches.Find(mLiveStorages, aPattern, clientType);
  1.2629 +
  1.2630 +    if (!matches.IsEmpty()) {
  1.2631 +      // Grab all storages that are not yet closed but whose storage id match
  1.2632 +      // the one we're looking for.
  1.2633 +      for (uint32_t index = 0; index < matches.Length(); index++) {
  1.2634 +        nsIOfflineStorage*& storage = matches[index];
  1.2635 +        if (!storage->IsClosed() &&
  1.2636 +            storage != aStorage &&
  1.2637 +            storage->Id() == aStorage->Id()) {
  1.2638 +          liveStorages.AppendElement(storage);
  1.2639 +        }
  1.2640 +      }
  1.2641 +    }
  1.2642 +
  1.2643 +    if (!liveStorages.IsEmpty()) {
  1.2644 +      NS_ASSERTION(op->mStorages[clientType].IsEmpty(),
  1.2645 +                   "How do we already have storages here?");
  1.2646 +      op->mStorages[clientType].AppendElements(liveStorages);
  1.2647 +    }
  1.2648 +  }
  1.2649 +  else {
  1.2650 +    StorageMatcher<ArrayCluster<nsIOfflineStorage*> > matches;
  1.2651 +    if (aPattern.IsVoid()) {
  1.2652 +      matches.Find(mLiveStorages);
  1.2653 +    }
  1.2654 +    else {
  1.2655 +      matches.Find(mLiveStorages, aPattern);
  1.2656 +    }
  1.2657 +
  1.2658 +    if (!matches.IsEmpty()) {
  1.2659 +      // We want *all* storages, even those that are closed, when we're going to
  1.2660 +      // clear the origin.
  1.2661 +      matches.AppendElementsTo(liveStorages);
  1.2662 +
  1.2663 +      NS_ASSERTION(op->mStorages.IsEmpty(),
  1.2664 +                   "How do we already have storages here?");
  1.2665 +      matches.SwapElements(op->mStorages);
  1.2666 +    }
  1.2667 +  }
  1.2668 +
  1.2669 +  op->mListener = aListener;
  1.2670 +
  1.2671 +  if (!liveStorages.IsEmpty()) {
  1.2672 +    // Give our callback the storages so it can decide what to do with them.
  1.2673 +    aCallback(liveStorages, aClosure);
  1.2674 +
  1.2675 +    NS_ASSERTION(liveStorages.IsEmpty(),
  1.2676 +                 "Should have done something with the array!");
  1.2677 +
  1.2678 +    if (aStorage) {
  1.2679 +      // Wait for those storages to close.
  1.2680 +      return NS_OK;
  1.2681 +    }
  1.2682 +  }
  1.2683 +
  1.2684 +  // If we're trying to open a storage and nothing blocks it, or if we're
  1.2685 +  // clearing an origin, then go ahead and schedule the op.
  1.2686 +  nsresult rv = RunSynchronizedOp(aStorage, op);
  1.2687 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2688 +
  1.2689 +  return NS_OK;
  1.2690 +}
  1.2691 +
  1.2692 +nsresult
  1.2693 +QuotaManager::RunSynchronizedOp(nsIOfflineStorage* aStorage,
  1.2694 +                                SynchronizedOp* aOp)
  1.2695 +{
  1.2696 +  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1.2697 +  NS_ASSERTION(aOp, "Null pointer!");
  1.2698 +  NS_ASSERTION(aOp->mListener, "No listener on this op!");
  1.2699 +  NS_ASSERTION(!aStorage ||
  1.2700 +               aOp->mStorages[aStorage->GetClient()->GetType()].IsEmpty(),
  1.2701 +               "This op isn't ready to run!");
  1.2702 +
  1.2703 +  ArrayCluster<nsIOfflineStorage*> storages;
  1.2704 +
  1.2705 +  uint32_t startIndex;
  1.2706 +  uint32_t endIndex;
  1.2707 +
  1.2708 +  if (aStorage) {
  1.2709 +    Client::Type clientType = aStorage->GetClient()->GetType();
  1.2710 +
  1.2711 +    storages[clientType].AppendElement(aStorage);
  1.2712 +
  1.2713 +    startIndex = clientType;
  1.2714 +    endIndex = clientType + 1;
  1.2715 +  }
  1.2716 +  else {
  1.2717 +    aOp->mStorages.SwapElements(storages);
  1.2718 +
  1.2719 +    startIndex = 0;
  1.2720 +    endIndex = Client::TYPE_MAX;
  1.2721 +  }
  1.2722 +
  1.2723 +  nsRefPtr<WaitForTransactionsToFinishRunnable> runnable =
  1.2724 +    new WaitForTransactionsToFinishRunnable(aOp);
  1.2725 +
  1.2726 +  // Ask the file service to call us back when it's done with this storage.
  1.2727 +  FileService* service = FileService::Get();
  1.2728 +
  1.2729 +  if (service) {
  1.2730 +    // Have to copy here in case a transaction service needs a list too.
  1.2731 +    nsTArray<nsCOMPtr<nsIFileStorage> > array;
  1.2732 +
  1.2733 +    for (uint32_t index = startIndex; index < endIndex; index++)  {
  1.2734 +      if (!storages[index].IsEmpty() &&
  1.2735 +          mClients[index]->IsFileServiceUtilized()) {
  1.2736 +        array.AppendElements(storages[index]);
  1.2737 +      }
  1.2738 +    }
  1.2739 +
  1.2740 +    if (!array.IsEmpty()) {
  1.2741 +      runnable->AddRun();
  1.2742 +
  1.2743 +      service->WaitForStoragesToComplete(array, runnable);
  1.2744 +    }
  1.2745 +  }
  1.2746 +
  1.2747 +  // Ask each transaction service to call us back when they're done with this
  1.2748 +  // storage.
  1.2749 +  for (uint32_t index = startIndex; index < endIndex; index++)  {
  1.2750 +    nsRefPtr<Client>& client = mClients[index];
  1.2751 +    if (!storages[index].IsEmpty() && client->IsTransactionServiceActivated()) {
  1.2752 +      runnable->AddRun();
  1.2753 +
  1.2754 +      client->WaitForStoragesToComplete(storages[index], runnable);
  1.2755 +    }
  1.2756 +  }
  1.2757 +
  1.2758 +  nsresult rv = runnable->Run();
  1.2759 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2760 +
  1.2761 +  return NS_OK;
  1.2762 +}
  1.2763 +
  1.2764 +SynchronizedOp*
  1.2765 +QuotaManager::FindSynchronizedOp(const nsACString& aPattern,
  1.2766 +                                 Nullable<PersistenceType> aPersistenceType,
  1.2767 +                                 const nsACString& aId)
  1.2768 +{
  1.2769 +  for (uint32_t index = 0; index < mSynchronizedOps.Length(); index++) {
  1.2770 +    const nsAutoPtr<SynchronizedOp>& currentOp = mSynchronizedOps[index];
  1.2771 +    if (PatternMatchesOrigin(aPattern, currentOp->mOriginOrPattern) &&
  1.2772 +        (currentOp->mPersistenceType.IsNull() ||
  1.2773 +         currentOp->mPersistenceType == aPersistenceType) &&
  1.2774 +        (currentOp->mId.IsEmpty() || currentOp->mId == aId)) {
  1.2775 +      return currentOp;
  1.2776 +    }
  1.2777 +  }
  1.2778 +
  1.2779 +  return nullptr;
  1.2780 +}
  1.2781 +
  1.2782 +nsresult
  1.2783 +QuotaManager::ClearStoragesForApp(uint32_t aAppId, bool aBrowserOnly)
  1.2784 +{
  1.2785 +  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1.2786 +  NS_ASSERTION(aAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID,
  1.2787 +               "Bad appId!");
  1.2788 +
  1.2789 +  // This only works from the main process.
  1.2790 +  NS_ENSURE_TRUE(IsMainProcess(), NS_ERROR_NOT_AVAILABLE);
  1.2791 +
  1.2792 +  nsAutoCString pattern;
  1.2793 +  GetOriginPatternStringMaybeIgnoreBrowser(aAppId, aBrowserOnly, pattern);
  1.2794 +
  1.2795 +  // Clear both temporary and persistent storages.
  1.2796 +  Nullable<PersistenceType> persistenceType;
  1.2797 +
  1.2798 +  // If there is a pending or running clear operation for this app, return
  1.2799 +  // immediately.
  1.2800 +  if (IsClearOriginPending(pattern, persistenceType)) {
  1.2801 +    return NS_OK;
  1.2802 +  }
  1.2803 +
  1.2804 +  OriginOrPatternString oops = OriginOrPatternString::FromPattern(pattern);
  1.2805 +
  1.2806 +  // Queue up the origin clear runnable.
  1.2807 +  nsRefPtr<OriginClearRunnable> runnable =
  1.2808 +    new OriginClearRunnable(oops, persistenceType);
  1.2809 +
  1.2810 +  nsresult rv =
  1.2811 +    WaitForOpenAllowed(oops, persistenceType, EmptyCString(), runnable);
  1.2812 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2813 +
  1.2814 +  runnable->AdvanceState();
  1.2815 +
  1.2816 +  // Give the runnable some help by invalidating any storages in the way.
  1.2817 +  StorageMatcher<nsAutoTArray<nsIOfflineStorage*, 20> > matches;
  1.2818 +  matches.Find(mLiveStorages, pattern);
  1.2819 +
  1.2820 +  for (uint32_t index = 0; index < matches.Length(); index++) {
  1.2821 +    // We need to grab references here to prevent the storage from dying while
  1.2822 +    // we invalidate it.
  1.2823 +    nsCOMPtr<nsIOfflineStorage> storage = matches[index];
  1.2824 +    storage->Invalidate();
  1.2825 +  }
  1.2826 +
  1.2827 +  return NS_OK;
  1.2828 +}
  1.2829 +
  1.2830 +// static
  1.2831 +PLDHashOperator
  1.2832 +QuotaManager::GetOriginsExceedingGroupLimit(const nsACString& aKey,
  1.2833 +                                            GroupInfoPair* aValue,
  1.2834 +                                            void* aUserArg)
  1.2835 +{
  1.2836 +  NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
  1.2837 +  NS_ASSERTION(aValue, "Null pointer!");
  1.2838 +
  1.2839 +  nsRefPtr<GroupInfo> groupInfo =
  1.2840 +    aValue->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY);
  1.2841 +  if (groupInfo) {
  1.2842 +    QuotaManager* quotaManager = QuotaManager::Get();
  1.2843 +    NS_ASSERTION(quotaManager, "Shouldn't be null!");
  1.2844 +
  1.2845 +    if (groupInfo->mUsage > quotaManager->GetGroupLimit()) {
  1.2846 +      nsTArray<OriginInfo*>* doomedOriginInfos =
  1.2847 +        static_cast<nsTArray<OriginInfo*>*>(aUserArg);
  1.2848 +
  1.2849 +      nsTArray<nsRefPtr<OriginInfo> >& originInfos = groupInfo->mOriginInfos;
  1.2850 +      originInfos.Sort(OriginInfoLRUComparator());
  1.2851 +
  1.2852 +      uint64_t usage = groupInfo->mUsage;
  1.2853 +      for (uint32_t i = 0; i < originInfos.Length(); i++) {
  1.2854 +        OriginInfo* originInfo = originInfos[i];
  1.2855 +
  1.2856 +        doomedOriginInfos->AppendElement(originInfo);
  1.2857 +        usage -= originInfo->mUsage;
  1.2858 +
  1.2859 +        if (usage <= quotaManager->GetGroupLimit()) {
  1.2860 +          break;
  1.2861 +        }
  1.2862 +      }
  1.2863 +    }
  1.2864 +  }
  1.2865 +
  1.2866 +  return PL_DHASH_NEXT;
  1.2867 +}
  1.2868 +
  1.2869 +// static
  1.2870 +PLDHashOperator
  1.2871 +QuotaManager::GetAllTemporaryStorageOrigins(const nsACString& aKey,
  1.2872 +                                            GroupInfoPair* aValue,
  1.2873 +                                            void* aUserArg)
  1.2874 +{
  1.2875 +  NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
  1.2876 +  NS_ASSERTION(aValue, "Null pointer!");
  1.2877 +
  1.2878 +  nsRefPtr<GroupInfo> groupInfo =
  1.2879 +    aValue->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY);
  1.2880 +  if (groupInfo) {
  1.2881 +    nsTArray<OriginInfo*>* originInfos =
  1.2882 +      static_cast<nsTArray<OriginInfo*>*>(aUserArg);
  1.2883 +
  1.2884 +    originInfos->AppendElements(groupInfo->mOriginInfos);
  1.2885 +  }
  1.2886 +
  1.2887 +  return PL_DHASH_NEXT;
  1.2888 +}
  1.2889 +
  1.2890 +void
  1.2891 +QuotaManager::CheckTemporaryStorageLimits()
  1.2892 +{
  1.2893 +  AssertIsOnIOThread();
  1.2894 +
  1.2895 +  nsTArray<OriginInfo*> doomedOriginInfos;
  1.2896 +  {
  1.2897 +    MutexAutoLock lock(mQuotaMutex);
  1.2898 +
  1.2899 +    mGroupInfoPairs.EnumerateRead(GetOriginsExceedingGroupLimit,
  1.2900 +                                  &doomedOriginInfos);
  1.2901 +
  1.2902 +    uint64_t usage = 0;
  1.2903 +    for (uint32_t index = 0; index < doomedOriginInfos.Length(); index++) {
  1.2904 +      usage += doomedOriginInfos[index]->mUsage;
  1.2905 +    }
  1.2906 +
  1.2907 +    if (mTemporaryStorageUsage - usage > mTemporaryStorageLimit) {
  1.2908 +      nsTArray<OriginInfo*> originInfos;
  1.2909 +
  1.2910 +      mGroupInfoPairs.EnumerateRead(GetAllTemporaryStorageOrigins,
  1.2911 +                                    &originInfos);
  1.2912 +
  1.2913 +      for (uint32_t index = originInfos.Length(); index > 0; index--) {
  1.2914 +        if (doomedOriginInfos.Contains(originInfos[index - 1])) {
  1.2915 +          originInfos.RemoveElementAt(index - 1);
  1.2916 +        }
  1.2917 +      }
  1.2918 +
  1.2919 +      originInfos.Sort(OriginInfoLRUComparator());
  1.2920 +
  1.2921 +      for (uint32_t i = 0; i < originInfos.Length(); i++) {
  1.2922 +        if (mTemporaryStorageUsage - usage <= mTemporaryStorageLimit) {
  1.2923 +          originInfos.TruncateLength(i);
  1.2924 +          break;
  1.2925 +        }
  1.2926 +
  1.2927 +        usage += originInfos[i]->mUsage;
  1.2928 +      }
  1.2929 +
  1.2930 +      doomedOriginInfos.AppendElements(originInfos);
  1.2931 +    }
  1.2932 +  }
  1.2933 +
  1.2934 +  for (uint32_t index = 0; index < doomedOriginInfos.Length(); index++) {
  1.2935 +    DeleteTemporaryFilesForOrigin(doomedOriginInfos[index]->mOrigin);
  1.2936 +  }
  1.2937 +
  1.2938 +  nsTArray<nsCString> doomedOrigins;
  1.2939 +  {
  1.2940 +    MutexAutoLock lock(mQuotaMutex);
  1.2941 +
  1.2942 +    for (uint32_t index = 0; index < doomedOriginInfos.Length(); index++) {
  1.2943 +      OriginInfo* doomedOriginInfo = doomedOriginInfos[index];
  1.2944 +
  1.2945 +      nsCString group = doomedOriginInfo->mGroupInfo->mGroup;
  1.2946 +      nsCString origin = doomedOriginInfo->mOrigin;
  1.2947 +      LockedRemoveQuotaForOrigin(PERSISTENCE_TYPE_TEMPORARY, group, origin);
  1.2948 +
  1.2949 +#ifdef DEBUG
  1.2950 +      doomedOriginInfos[index] = nullptr;
  1.2951 +#endif
  1.2952 +
  1.2953 +      doomedOrigins.AppendElement(origin);
  1.2954 +    }
  1.2955 +  }
  1.2956 +
  1.2957 +  for (uint32_t index = 0; index < doomedOrigins.Length(); index++) {
  1.2958 +    OriginClearCompleted(
  1.2959 +                       PERSISTENCE_TYPE_TEMPORARY,
  1.2960 +                       OriginOrPatternString::FromOrigin(doomedOrigins[index]));
  1.2961 +  }
  1.2962 +}
  1.2963 +
  1.2964 +// static
  1.2965 +PLDHashOperator
  1.2966 +QuotaManager::AddTemporaryStorageOrigins(
  1.2967 +                                       const nsACString& aKey,
  1.2968 +                                       ArrayCluster<nsIOfflineStorage*>* aValue,
  1.2969 +                                       void* aUserArg)
  1.2970 +{
  1.2971 +  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1.2972 +  NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
  1.2973 +  NS_ASSERTION(aValue, "Null pointer!");
  1.2974 +  NS_ASSERTION(aUserArg, "Null pointer!");
  1.2975 +
  1.2976 +  OriginCollection& collection = *static_cast<OriginCollection*>(aUserArg);
  1.2977 +
  1.2978 +  if (collection.ContainsOrigin(aKey)) {
  1.2979 +    return PL_DHASH_NEXT;
  1.2980 +  }
  1.2981 +
  1.2982 +  for (uint32_t i = 0; i < Client::TYPE_MAX; i++) {
  1.2983 +    nsTArray<nsIOfflineStorage*>& array = (*aValue)[i];
  1.2984 +    for (uint32_t j = 0; j < array.Length(); j++) {
  1.2985 +      nsIOfflineStorage*& storage = array[j];
  1.2986 +      if (storage->Type() == PERSISTENCE_TYPE_TEMPORARY) {
  1.2987 +        collection.AddOrigin(aKey);
  1.2988 +        return PL_DHASH_NEXT;
  1.2989 +      }
  1.2990 +    }
  1.2991 +  }
  1.2992 +
  1.2993 +  return PL_DHASH_NEXT;
  1.2994 +}
  1.2995 +
  1.2996 +// static
  1.2997 +PLDHashOperator
  1.2998 +QuotaManager::GetInactiveTemporaryStorageOrigins(const nsACString& aKey,
  1.2999 +                                                 GroupInfoPair* aValue,
  1.3000 +                                                 void* aUserArg)
  1.3001 +{
  1.3002 +  NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
  1.3003 +  NS_ASSERTION(aValue, "Null pointer!");
  1.3004 +  NS_ASSERTION(aUserArg, "Null pointer!");
  1.3005 +
  1.3006 +  nsRefPtr<GroupInfo> groupInfo =
  1.3007 +    aValue->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY);
  1.3008 +  if (groupInfo) {
  1.3009 +    InactiveOriginsInfo* info = static_cast<InactiveOriginsInfo*>(aUserArg);
  1.3010 +
  1.3011 +    nsTArray<nsRefPtr<OriginInfo> >& originInfos = groupInfo->mOriginInfos;
  1.3012 +
  1.3013 +    for (uint32_t i = 0; i < originInfos.Length(); i++) {
  1.3014 +      OriginInfo* originInfo = originInfos[i];
  1.3015 +
  1.3016 +      if (!info->collection.ContainsOrigin(originInfo->mOrigin)) {
  1.3017 +        NS_ASSERTION(!originInfo->mQuotaObjects.Count(),
  1.3018 +                     "Inactive origin shouldn't have open files!");
  1.3019 +        info->origins.AppendElement(originInfo);
  1.3020 +      }
  1.3021 +    }
  1.3022 +  }
  1.3023 +
  1.3024 +  return PL_DHASH_NEXT;
  1.3025 +}
  1.3026 +
  1.3027 +uint64_t
  1.3028 +QuotaManager::CollectOriginsForEviction(uint64_t aMinSizeToBeFreed,
  1.3029 +                                        nsTArray<OriginInfo*>& aOriginInfos)
  1.3030 +{
  1.3031 +  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1.3032 +
  1.3033 +  // Collect active origins first.
  1.3034 +  OriginCollection originCollection;
  1.3035 +
  1.3036 +  // Add patterns and origins that have running or pending synchronized ops.
  1.3037 +  // (add patterns first to reduce redundancy in the origin collection).
  1.3038 +  uint32_t index;
  1.3039 +  for (index = 0; index < mSynchronizedOps.Length(); index++) {
  1.3040 +    nsAutoPtr<SynchronizedOp>& op = mSynchronizedOps[index];
  1.3041 +    if (op->mPersistenceType.IsNull() ||
  1.3042 +        op->mPersistenceType.Value() == PERSISTENCE_TYPE_TEMPORARY) {
  1.3043 +      if (op->mOriginOrPattern.IsPattern() &&
  1.3044 +          !originCollection.ContainsPattern(op->mOriginOrPattern)) {
  1.3045 +        originCollection.AddPattern(op->mOriginOrPattern);
  1.3046 +      }
  1.3047 +    }
  1.3048 +  }
  1.3049 +
  1.3050 +  for (index = 0; index < mSynchronizedOps.Length(); index++) {
  1.3051 +    nsAutoPtr<SynchronizedOp>& op = mSynchronizedOps[index];
  1.3052 +    if (op->mPersistenceType.IsNull() ||
  1.3053 +        op->mPersistenceType.Value() == PERSISTENCE_TYPE_TEMPORARY) {
  1.3054 +      if (op->mOriginOrPattern.IsOrigin() &&
  1.3055 +          !originCollection.ContainsOrigin(op->mOriginOrPattern)) {
  1.3056 +        originCollection.AddOrigin(op->mOriginOrPattern);
  1.3057 +      }
  1.3058 +    }
  1.3059 +  }
  1.3060 +
  1.3061 +  // Add origins that have live temporary storages.
  1.3062 +  mLiveStorages.EnumerateRead(AddTemporaryStorageOrigins, &originCollection);
  1.3063 +
  1.3064 +  // Enumerate inactive origins. This must be protected by the mutex.
  1.3065 +  nsTArray<OriginInfo*> inactiveOrigins;
  1.3066 +  {
  1.3067 +    InactiveOriginsInfo info(originCollection, inactiveOrigins);
  1.3068 +    MutexAutoLock lock(mQuotaMutex);
  1.3069 +    mGroupInfoPairs.EnumerateRead(GetInactiveTemporaryStorageOrigins, &info);
  1.3070 +  }
  1.3071 +
  1.3072 +  // We now have a list of all inactive origins. So it's safe to sort the list
  1.3073 +  // and calculate available size without holding the lock.
  1.3074 +
  1.3075 +  // Sort by the origin access time.
  1.3076 +  inactiveOrigins.Sort(OriginInfoLRUComparator());
  1.3077 +
  1.3078 +  // Create a list of inactive and the least recently used origins
  1.3079 +  // whose aggregate size is greater or equals the minimal size to be freed.
  1.3080 +  uint64_t sizeToBeFreed = 0;
  1.3081 +  for(index = 0; index < inactiveOrigins.Length(); index++) {
  1.3082 +    if (sizeToBeFreed >= aMinSizeToBeFreed) {
  1.3083 +      inactiveOrigins.TruncateLength(index);
  1.3084 +      break;
  1.3085 +    }
  1.3086 +
  1.3087 +    sizeToBeFreed += inactiveOrigins[index]->mUsage;
  1.3088 +  }
  1.3089 +
  1.3090 +  if (sizeToBeFreed >= aMinSizeToBeFreed) {
  1.3091 +    // Success, add synchronized ops for these origins, so any other
  1.3092 +    // operations for them will be delayed (until origin eviction is finalized).
  1.3093 +
  1.3094 +    for(index = 0; index < inactiveOrigins.Length(); index++) {
  1.3095 +      OriginOrPatternString oops =
  1.3096 +        OriginOrPatternString::FromOrigin(inactiveOrigins[index]->mOrigin);
  1.3097 +
  1.3098 +      AddSynchronizedOp(oops,
  1.3099 +                        Nullable<PersistenceType>(PERSISTENCE_TYPE_TEMPORARY));
  1.3100 +    }
  1.3101 +
  1.3102 +    inactiveOrigins.SwapElements(aOriginInfos);
  1.3103 +    return sizeToBeFreed;
  1.3104 +  }
  1.3105 +
  1.3106 +  return 0;
  1.3107 +}
  1.3108 +
  1.3109 +void
  1.3110 +QuotaManager::DeleteTemporaryFilesForOrigin(const nsACString& aOrigin)
  1.3111 +{
  1.3112 +  nsCOMPtr<nsIFile> directory;
  1.3113 +  nsresult rv = GetDirectoryForOrigin(PERSISTENCE_TYPE_TEMPORARY, aOrigin,
  1.3114 +                                      getter_AddRefs(directory));
  1.3115 +  NS_ENSURE_SUCCESS_VOID(rv);
  1.3116 +
  1.3117 +  rv = directory->Remove(true);
  1.3118 +  if (rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST &&
  1.3119 +      rv != NS_ERROR_FILE_NOT_FOUND && NS_FAILED(rv)) {
  1.3120 +    // This should never fail if we've closed all storage connections
  1.3121 +    // correctly...
  1.3122 +    NS_ERROR("Failed to remove directory!");
  1.3123 +  }
  1.3124 +}
  1.3125 +
  1.3126 +void
  1.3127 +QuotaManager::FinalizeOriginEviction(nsTArray<nsCString>& aOrigins)
  1.3128 +{
  1.3129 +  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
  1.3130 +
  1.3131 +  nsRefPtr<FinalizeOriginEvictionRunnable> runnable =
  1.3132 +    new FinalizeOriginEvictionRunnable(aOrigins);
  1.3133 +
  1.3134 +  nsresult rv = IsOnIOThread() ? runnable->RunImmediately()
  1.3135 +                               : runnable->Dispatch();
  1.3136 +  NS_ENSURE_SUCCESS_VOID(rv);
  1.3137 +}
  1.3138 +
  1.3139 +void
  1.3140 +QuotaManager::SaveOriginAccessTime(const nsACString& aOrigin,
  1.3141 +                                   int64_t aTimestamp)
  1.3142 +{
  1.3143 +  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1.3144 +
  1.3145 +  if (QuotaManager::IsShuttingDown()) {
  1.3146 +    return;
  1.3147 +  }
  1.3148 +
  1.3149 +  nsRefPtr<SaveOriginAccessTimeRunnable> runnable =
  1.3150 +    new SaveOriginAccessTimeRunnable(aOrigin, aTimestamp);
  1.3151 +
  1.3152 +  if (NS_FAILED(mIOThread->Dispatch(runnable, NS_DISPATCH_NORMAL))) {
  1.3153 +    NS_WARNING("Failed to dispatch runnable!");
  1.3154 +  }
  1.3155 +}
  1.3156 +
  1.3157 +void
  1.3158 +QuotaManager::GetOriginPatternString(uint32_t aAppId,
  1.3159 +                                     MozBrowserPatternFlag aBrowserFlag,
  1.3160 +                                     const nsACString& aOrigin,
  1.3161 +                                     nsAutoCString& _retval)
  1.3162 +{
  1.3163 +  NS_ASSERTION(aAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID,
  1.3164 +               "Bad appId!");
  1.3165 +  NS_ASSERTION(aOrigin.IsEmpty() || aBrowserFlag != IgnoreMozBrowser,
  1.3166 +               "Bad args!");
  1.3167 +
  1.3168 +  if (aOrigin.IsEmpty()) {
  1.3169 +    _retval.Truncate();
  1.3170 +
  1.3171 +    _retval.AppendInt(aAppId);
  1.3172 +    _retval.Append('+');
  1.3173 +
  1.3174 +    if (aBrowserFlag != IgnoreMozBrowser) {
  1.3175 +      if (aBrowserFlag == MozBrowser) {
  1.3176 +        _retval.Append('t');
  1.3177 +      }
  1.3178 +      else {
  1.3179 +        _retval.Append('f');
  1.3180 +      }
  1.3181 +      _retval.Append('+');
  1.3182 +    }
  1.3183 +
  1.3184 +    return;
  1.3185 +  }
  1.3186 +
  1.3187 +#ifdef DEBUG
  1.3188 +  if (aAppId != nsIScriptSecurityManager::NO_APP_ID ||
  1.3189 +      aBrowserFlag == MozBrowser) {
  1.3190 +    nsAutoCString pattern;
  1.3191 +    GetOriginPatternString(aAppId, aBrowserFlag, EmptyCString(), pattern);
  1.3192 +    NS_ASSERTION(PatternMatchesOrigin(pattern, aOrigin),
  1.3193 +                 "Origin doesn't match parameters!");
  1.3194 +  }
  1.3195 +#endif
  1.3196 +
  1.3197 +  _retval = aOrigin;
  1.3198 +}
  1.3199 +
  1.3200 +SynchronizedOp::SynchronizedOp(const OriginOrPatternString& aOriginOrPattern,
  1.3201 +                               Nullable<PersistenceType> aPersistenceType,
  1.3202 +                               const nsACString& aId)
  1.3203 +: mOriginOrPattern(aOriginOrPattern), mPersistenceType(aPersistenceType),
  1.3204 +  mId(aId)
  1.3205 +{
  1.3206 +  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1.3207 +  MOZ_COUNT_CTOR(SynchronizedOp);
  1.3208 +}
  1.3209 +
  1.3210 +SynchronizedOp::~SynchronizedOp()
  1.3211 +{
  1.3212 +  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1.3213 +  MOZ_COUNT_DTOR(SynchronizedOp);
  1.3214 +}
  1.3215 +
  1.3216 +bool
  1.3217 +SynchronizedOp::MustWaitFor(const SynchronizedOp& aExistingOp)
  1.3218 +{
  1.3219 +  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1.3220 +
  1.3221 +  if (aExistingOp.mOriginOrPattern.IsNull() || mOriginOrPattern.IsNull()) {
  1.3222 +    return true;
  1.3223 +  }
  1.3224 +
  1.3225 +  bool match;
  1.3226 +
  1.3227 +  if (aExistingOp.mOriginOrPattern.IsOrigin()) {
  1.3228 +    if (mOriginOrPattern.IsOrigin()) {
  1.3229 +      match = aExistingOp.mOriginOrPattern.Equals(mOriginOrPattern);
  1.3230 +    }
  1.3231 +    else {
  1.3232 +      match = PatternMatchesOrigin(mOriginOrPattern, aExistingOp.mOriginOrPattern);
  1.3233 +    }
  1.3234 +  }
  1.3235 +  else if (mOriginOrPattern.IsOrigin()) {
  1.3236 +    match = PatternMatchesOrigin(aExistingOp.mOriginOrPattern, mOriginOrPattern);
  1.3237 +  }
  1.3238 +  else {
  1.3239 +    match = PatternMatchesOrigin(mOriginOrPattern, aExistingOp.mOriginOrPattern) ||
  1.3240 +            PatternMatchesOrigin(aExistingOp.mOriginOrPattern, mOriginOrPattern);
  1.3241 +  }
  1.3242 +
  1.3243 +  // If the origins don't match, the second can proceed.
  1.3244 +  if (!match) {
  1.3245 +    return false;
  1.3246 +  }
  1.3247 +
  1.3248 +  // If the origins match but the persistence types are different, the second
  1.3249 +  // can proceed.
  1.3250 +  if (!aExistingOp.mPersistenceType.IsNull() && !mPersistenceType.IsNull() &&
  1.3251 +      aExistingOp.mPersistenceType.Value() != mPersistenceType.Value()) {
  1.3252 +    return false;
  1.3253 +  }
  1.3254 +
  1.3255 +  // If the origins and the ids match, the second must wait.
  1.3256 +  if (aExistingOp.mId == mId) {
  1.3257 +    return true;
  1.3258 +  }
  1.3259 +
  1.3260 +  // Waiting is required if either one corresponds to an origin clearing
  1.3261 +  // (an empty Id).
  1.3262 +  if (aExistingOp.mId.IsEmpty() || mId.IsEmpty()) {
  1.3263 +    return true;
  1.3264 +  }
  1.3265 +
  1.3266 +  // Otherwise, things for the same origin but different storages can proceed
  1.3267 +  // independently.
  1.3268 +  return false;
  1.3269 +}
  1.3270 +
  1.3271 +void
  1.3272 +SynchronizedOp::DelayRunnable(nsIRunnable* aRunnable)
  1.3273 +{
  1.3274 +  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1.3275 +  NS_ASSERTION(mDelayedRunnables.IsEmpty() || mId.IsEmpty(),
  1.3276 +               "Only ClearOrigin operations can delay multiple runnables!");
  1.3277 +
  1.3278 +  mDelayedRunnables.AppendElement(aRunnable);
  1.3279 +}
  1.3280 +
  1.3281 +void
  1.3282 +SynchronizedOp::DispatchDelayedRunnables()
  1.3283 +{
  1.3284 +  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1.3285 +  NS_ASSERTION(!mListener, "Any listener should be gone by now!");
  1.3286 +
  1.3287 +  uint32_t count = mDelayedRunnables.Length();
  1.3288 +  for (uint32_t index = 0; index < count; index++) {
  1.3289 +    NS_DispatchToCurrentThread(mDelayedRunnables[index]);
  1.3290 +  }
  1.3291 +
  1.3292 +  mDelayedRunnables.Clear();
  1.3293 +}
  1.3294 +
  1.3295 +CollectOriginsHelper::CollectOriginsHelper(mozilla::Mutex& aMutex,
  1.3296 +                                           uint64_t aMinSizeToBeFreed)
  1.3297 +: mMinSizeToBeFreed(aMinSizeToBeFreed),
  1.3298 +  mMutex(aMutex),
  1.3299 +  mCondVar(aMutex, "CollectOriginsHelper::mCondVar"),
  1.3300 +  mSizeToBeFreed(0),
  1.3301 +  mWaiting(true)
  1.3302 +{
  1.3303 +  MOZ_ASSERT(!NS_IsMainThread(), "Wrong thread!");
  1.3304 +  mMutex.AssertCurrentThreadOwns();
  1.3305 +}
  1.3306 +
  1.3307 +int64_t
  1.3308 +CollectOriginsHelper::BlockAndReturnOriginsForEviction(
  1.3309 +                                            nsTArray<OriginInfo*>& aOriginInfos)
  1.3310 +{
  1.3311 +  MOZ_ASSERT(!NS_IsMainThread(), "Wrong thread!");
  1.3312 +  mMutex.AssertCurrentThreadOwns();
  1.3313 +
  1.3314 +  while (mWaiting) {
  1.3315 +    mCondVar.Wait();
  1.3316 +  }
  1.3317 +
  1.3318 +  mOriginInfos.SwapElements(aOriginInfos);
  1.3319 +  return mSizeToBeFreed;
  1.3320 +}
  1.3321 +
  1.3322 +NS_IMETHODIMP
  1.3323 +CollectOriginsHelper::Run()
  1.3324 +{
  1.3325 +  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
  1.3326 +
  1.3327 +  QuotaManager* quotaManager = QuotaManager::Get();
  1.3328 +  NS_ASSERTION(quotaManager, "Shouldn't be null!");
  1.3329 +
  1.3330 +  // We use extra stack vars here to avoid race detector warnings (the same
  1.3331 +  // memory accessed with and without the lock held).
  1.3332 +  nsTArray<OriginInfo*> originInfos;
  1.3333 +  uint64_t sizeToBeFreed =
  1.3334 +    quotaManager->CollectOriginsForEviction(mMinSizeToBeFreed, originInfos);
  1.3335 +
  1.3336 +  MutexAutoLock lock(mMutex);
  1.3337 +
  1.3338 +  NS_ASSERTION(mWaiting, "Huh?!");
  1.3339 +
  1.3340 +  mOriginInfos.SwapElements(originInfos);
  1.3341 +  mSizeToBeFreed = sizeToBeFreed;
  1.3342 +  mWaiting = false;
  1.3343 +  mCondVar.Notify();
  1.3344 +
  1.3345 +  return NS_OK;
  1.3346 +}
  1.3347 +
  1.3348 +nsresult
  1.3349 +OriginClearRunnable::OnExclusiveAccessAcquired()
  1.3350 +{
  1.3351 +  QuotaManager* quotaManager = QuotaManager::Get();
  1.3352 +  NS_ASSERTION(quotaManager, "This should never fail!");
  1.3353 +
  1.3354 +  nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
  1.3355 +  NS_ENSURE_SUCCESS(rv, rv);
  1.3356 +
  1.3357 +  return NS_OK;
  1.3358 +}
  1.3359 +
  1.3360 +// static
  1.3361 +void
  1.3362 +OriginClearRunnable::InvalidateOpenedStorages(
  1.3363 +                              nsTArray<nsCOMPtr<nsIOfflineStorage> >& aStorages,
  1.3364 +                              void* aClosure)
  1.3365 +{
  1.3366 +  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1.3367 +
  1.3368 +  nsTArray<nsCOMPtr<nsIOfflineStorage> > storages;
  1.3369 +  storages.SwapElements(aStorages);
  1.3370 +
  1.3371 +  for (uint32_t index = 0; index < storages.Length(); index++) {
  1.3372 +    storages[index]->Invalidate();
  1.3373 +  }
  1.3374 +}
  1.3375 +
  1.3376 +void
  1.3377 +OriginClearRunnable::DeleteFiles(QuotaManager* aQuotaManager,
  1.3378 +                                 PersistenceType aPersistenceType)
  1.3379 +{
  1.3380 +  AssertIsOnIOThread();
  1.3381 +  NS_ASSERTION(aQuotaManager, "Don't pass me null!");
  1.3382 +
  1.3383 +  nsresult rv;
  1.3384 +
  1.3385 +  nsCOMPtr<nsIFile> directory =
  1.3386 +    do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
  1.3387 +  NS_ENSURE_SUCCESS_VOID(rv);
  1.3388 +
  1.3389 +  rv = directory->InitWithPath(aQuotaManager->GetStoragePath(aPersistenceType));
  1.3390 +  NS_ENSURE_SUCCESS_VOID(rv);
  1.3391 +
  1.3392 +  nsCOMPtr<nsISimpleEnumerator> entries;
  1.3393 +  if (NS_FAILED(directory->GetDirectoryEntries(getter_AddRefs(entries))) ||
  1.3394 +      !entries) {
  1.3395 +    return;
  1.3396 +  }
  1.3397 +
  1.3398 +  nsCString originSanitized(mOriginOrPattern);
  1.3399 +  SanitizeOriginString(originSanitized);
  1.3400 +
  1.3401 +  bool hasMore;
  1.3402 +  while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
  1.3403 +    nsCOMPtr<nsISupports> entry;
  1.3404 +    rv = entries->GetNext(getter_AddRefs(entry));
  1.3405 +    NS_ENSURE_SUCCESS_VOID(rv);
  1.3406 +
  1.3407 +    nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
  1.3408 +    NS_ASSERTION(file, "Don't know what this is!");
  1.3409 +
  1.3410 +    bool isDirectory;
  1.3411 +    rv = file->IsDirectory(&isDirectory);
  1.3412 +    NS_ENSURE_SUCCESS_VOID(rv);
  1.3413 +
  1.3414 +    if (!isDirectory) {
  1.3415 +      NS_WARNING("Something in the IndexedDB directory that doesn't belong!");
  1.3416 +      continue;
  1.3417 +    }
  1.3418 +
  1.3419 +    nsString leafName;
  1.3420 +    rv = file->GetLeafName(leafName);
  1.3421 +    NS_ENSURE_SUCCESS_VOID(rv);
  1.3422 +
  1.3423 +    // Skip storages for other apps.
  1.3424 +    if (!PatternMatchesOrigin(originSanitized,
  1.3425 +                              NS_ConvertUTF16toUTF8(leafName))) {
  1.3426 +      continue;
  1.3427 +    }
  1.3428 +
  1.3429 +    if (NS_FAILED(file->Remove(true))) {
  1.3430 +      // This should never fail if we've closed all storage connections
  1.3431 +      // correctly...
  1.3432 +      NS_ERROR("Failed to remove directory!");
  1.3433 +    }
  1.3434 +  }
  1.3435 +
  1.3436 +  aQuotaManager->RemoveQuotaForPattern(aPersistenceType, mOriginOrPattern);
  1.3437 +
  1.3438 +  aQuotaManager->OriginClearCompleted(aPersistenceType, mOriginOrPattern);
  1.3439 +}
  1.3440 +
  1.3441 +NS_IMPL_ISUPPORTS_INHERITED0(OriginClearRunnable, nsRunnable)
  1.3442 +
  1.3443 +NS_IMETHODIMP
  1.3444 +OriginClearRunnable::Run()
  1.3445 +{
  1.3446 +  PROFILER_LABEL("Quota", "OriginClearRunnable::Run");
  1.3447 +
  1.3448 +  QuotaManager* quotaManager = QuotaManager::Get();
  1.3449 +  NS_ASSERTION(quotaManager, "This should never fail!");
  1.3450 +
  1.3451 +  switch (mCallbackState) {
  1.3452 +    case Pending: {
  1.3453 +      NS_NOTREACHED("Should never get here without being dispatched!");
  1.3454 +      return NS_ERROR_UNEXPECTED;
  1.3455 +    }
  1.3456 +
  1.3457 +    case OpenAllowed: {
  1.3458 +      NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1.3459 +
  1.3460 +      AdvanceState();
  1.3461 +
  1.3462 +      // Now we have to wait until the thread pool is done with all of the
  1.3463 +      // storages we care about.
  1.3464 +      nsresult rv =
  1.3465 +        quotaManager->AcquireExclusiveAccess(mOriginOrPattern, mPersistenceType,
  1.3466 +                                             this, InvalidateOpenedStorages,
  1.3467 +                                             nullptr);
  1.3468 +      NS_ENSURE_SUCCESS(rv, rv);
  1.3469 +
  1.3470 +      return NS_OK;
  1.3471 +    }
  1.3472 +
  1.3473 +    case IO: {
  1.3474 +      AssertIsOnIOThread();
  1.3475 +
  1.3476 +      AdvanceState();
  1.3477 +
  1.3478 +      if (mPersistenceType.IsNull()) {
  1.3479 +        DeleteFiles(quotaManager, PERSISTENCE_TYPE_PERSISTENT);
  1.3480 +        DeleteFiles(quotaManager, PERSISTENCE_TYPE_TEMPORARY);
  1.3481 +      } else {
  1.3482 +        DeleteFiles(quotaManager, mPersistenceType.Value());
  1.3483 +      }
  1.3484 +
  1.3485 +      // Now dispatch back to the main thread.
  1.3486 +      if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) {
  1.3487 +        NS_WARNING("Failed to dispatch to main thread!");
  1.3488 +        return NS_ERROR_FAILURE;
  1.3489 +      }
  1.3490 +
  1.3491 +      return NS_OK;
  1.3492 +    }
  1.3493 +
  1.3494 +    case Complete: {
  1.3495 +      NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1.3496 +
  1.3497 +      // Tell the QuotaManager that we're done.
  1.3498 +      quotaManager->AllowNextSynchronizedOp(mOriginOrPattern, mPersistenceType,
  1.3499 +                                            EmptyCString());
  1.3500 +
  1.3501 +      return NS_OK;
  1.3502 +    }
  1.3503 +
  1.3504 +    default:
  1.3505 +      NS_ERROR("Unknown state value!");
  1.3506 +      return NS_ERROR_UNEXPECTED;
  1.3507 +  }
  1.3508 +
  1.3509 +  NS_NOTREACHED("Should never get here!");
  1.3510 +  return NS_ERROR_UNEXPECTED;
  1.3511 +}
  1.3512 +
  1.3513 +AsyncUsageRunnable::AsyncUsageRunnable(uint32_t aAppId,
  1.3514 +                                       bool aInMozBrowserOnly,
  1.3515 +                                       const nsACString& aGroup,
  1.3516 +                                       const OriginOrPatternString& aOrigin,
  1.3517 +                                       nsIURI* aURI,
  1.3518 +                                       nsIUsageCallback* aCallback)
  1.3519 +: mURI(aURI),
  1.3520 +  mCallback(aCallback),
  1.3521 +  mAppId(aAppId),
  1.3522 +  mGroup(aGroup),
  1.3523 +  mOrigin(aOrigin),
  1.3524 +  mCallbackState(Pending),
  1.3525 +  mInMozBrowserOnly(aInMozBrowserOnly)
  1.3526 +{
  1.3527 +  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1.3528 +  NS_ASSERTION(aURI, "Null pointer!");
  1.3529 +  NS_ASSERTION(!aGroup.IsEmpty(), "Empty group!");
  1.3530 +  NS_ASSERTION(aOrigin.IsOrigin(), "Expect origin only here!");
  1.3531 +  NS_ASSERTION(!aOrigin.IsEmpty(), "Empty origin!");
  1.3532 +  NS_ASSERTION(aCallback, "Null pointer!");
  1.3533 +}
  1.3534 +
  1.3535 +nsresult
  1.3536 +AsyncUsageRunnable::TakeShortcut()
  1.3537 +{
  1.3538 +  NS_ASSERTION(mCallbackState == Pending, "Huh?");
  1.3539 +
  1.3540 +  nsresult rv = NS_DispatchToCurrentThread(this);
  1.3541 +  NS_ENSURE_SUCCESS(rv, rv);
  1.3542 +
  1.3543 +  mCallbackState = Shortcut;
  1.3544 +  return NS_OK;
  1.3545 +}
  1.3546 +
  1.3547 +nsresult
  1.3548 +AsyncUsageRunnable::RunInternal()
  1.3549 +{
  1.3550 +  QuotaManager* quotaManager = QuotaManager::Get();
  1.3551 +  NS_ASSERTION(quotaManager, "This should never fail!");
  1.3552 +
  1.3553 +  nsresult rv;
  1.3554 +
  1.3555 +  switch (mCallbackState) {
  1.3556 +    case Pending: {
  1.3557 +      NS_NOTREACHED("Should never get here without being dispatched!");
  1.3558 +      return NS_ERROR_UNEXPECTED;
  1.3559 +    }
  1.3560 +
  1.3561 +    case OpenAllowed: {
  1.3562 +      NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1.3563 +
  1.3564 +      AdvanceState();
  1.3565 +
  1.3566 +      rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
  1.3567 +      if (NS_FAILED(rv)) {
  1.3568 +        NS_WARNING("Failed to dispatch to the IO thread!");
  1.3569 +      }
  1.3570 +
  1.3571 +      return NS_OK;
  1.3572 +    }
  1.3573 +
  1.3574 +    case IO: {
  1.3575 +      AssertIsOnIOThread();
  1.3576 +
  1.3577 +      AdvanceState();
  1.3578 +
  1.3579 +      // Add all the persistent storage files we care about.
  1.3580 +      rv = AddToUsage(quotaManager, PERSISTENCE_TYPE_PERSISTENT);
  1.3581 +      NS_ENSURE_SUCCESS(rv, rv);
  1.3582 +
  1.3583 +      // Add all the temporary storage files we care about.
  1.3584 +      rv = AddToUsage(quotaManager, PERSISTENCE_TYPE_TEMPORARY);
  1.3585 +      NS_ENSURE_SUCCESS(rv, rv);
  1.3586 +
  1.3587 +      // Run dispatches us back to the main thread.
  1.3588 +      return NS_OK;
  1.3589 +    }
  1.3590 +
  1.3591 +    case Complete: // Fall through
  1.3592 +    case Shortcut: {
  1.3593 +      NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1.3594 +
  1.3595 +      // Call the callback unless we were canceled.
  1.3596 +      if (!mCanceled) {
  1.3597 +        mCallback->OnUsageResult(mURI, TotalUsage(), FileUsage(), mAppId,
  1.3598 +                                 mInMozBrowserOnly);
  1.3599 +      }
  1.3600 +
  1.3601 +      // Clean up.
  1.3602 +      mURI = nullptr;
  1.3603 +      mCallback = nullptr;
  1.3604 +
  1.3605 +      // And tell the QuotaManager that we're done.
  1.3606 +      if (mCallbackState == Complete) {
  1.3607 +        quotaManager->AllowNextSynchronizedOp(mOrigin,
  1.3608 +                                              Nullable<PersistenceType>(),
  1.3609 +                                              EmptyCString());
  1.3610 +      }
  1.3611 +
  1.3612 +      return NS_OK;
  1.3613 +    }
  1.3614 +
  1.3615 +    default:
  1.3616 +      NS_ERROR("Unknown state value!");
  1.3617 +      return NS_ERROR_UNEXPECTED;
  1.3618 +  }
  1.3619 +
  1.3620 +  NS_NOTREACHED("Should never get here!");
  1.3621 +  return NS_ERROR_UNEXPECTED;
  1.3622 +}
  1.3623 +
  1.3624 +nsresult
  1.3625 +AsyncUsageRunnable::AddToUsage(QuotaManager* aQuotaManager,
  1.3626 +                               PersistenceType aPersistenceType)
  1.3627 +{
  1.3628 +  AssertIsOnIOThread();
  1.3629 +
  1.3630 +  nsCOMPtr<nsIFile> directory;
  1.3631 +  nsresult rv = aQuotaManager->GetDirectoryForOrigin(aPersistenceType, mOrigin,
  1.3632 +                                                     getter_AddRefs(directory));
  1.3633 +  NS_ENSURE_SUCCESS(rv, rv);
  1.3634 +
  1.3635 +  bool exists;
  1.3636 +  rv = directory->Exists(&exists);
  1.3637 +  NS_ENSURE_SUCCESS(rv, rv);
  1.3638 +
  1.3639 +  // If the directory exists then enumerate all the files inside, adding up
  1.3640 +  // the sizes to get the final usage statistic.
  1.3641 +  if (exists && !mCanceled) {
  1.3642 +    bool initialized;
  1.3643 +
  1.3644 +    if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) {
  1.3645 +      initialized = aQuotaManager->mInitializedOrigins.Contains(mOrigin);
  1.3646 +
  1.3647 +      if (!initialized) {
  1.3648 +        rv = MaybeUpgradeOriginDirectory(directory);
  1.3649 +        NS_ENSURE_SUCCESS(rv, rv);
  1.3650 +      }
  1.3651 +    }
  1.3652 +    else {
  1.3653 +      NS_ASSERTION(aPersistenceType == PERSISTENCE_TYPE_TEMPORARY, "Huh?");
  1.3654 +      initialized = aQuotaManager->mTemporaryStorageInitialized;
  1.3655 +    }
  1.3656 +
  1.3657 +    nsCOMPtr<nsISimpleEnumerator> entries;
  1.3658 +    rv = directory->GetDirectoryEntries(getter_AddRefs(entries));
  1.3659 +    NS_ENSURE_SUCCESS(rv, rv);
  1.3660 +
  1.3661 +    bool hasMore;
  1.3662 +    while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) &&
  1.3663 +           hasMore && !mCanceled) {
  1.3664 +      nsCOMPtr<nsISupports> entry;
  1.3665 +      rv = entries->GetNext(getter_AddRefs(entry));
  1.3666 +      NS_ENSURE_SUCCESS(rv, rv);
  1.3667 +
  1.3668 +      nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
  1.3669 +      NS_ENSURE_TRUE(file, NS_NOINTERFACE);
  1.3670 +
  1.3671 +      nsString leafName;
  1.3672 +      rv = file->GetLeafName(leafName);
  1.3673 +      NS_ENSURE_SUCCESS(rv, rv);
  1.3674 +
  1.3675 +      if (leafName.EqualsLiteral(METADATA_FILE_NAME) ||
  1.3676 +          leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
  1.3677 +        continue;
  1.3678 +      }
  1.3679 +
  1.3680 +      if (!initialized) {
  1.3681 +        bool isDirectory;
  1.3682 +        rv = file->IsDirectory(&isDirectory);
  1.3683 +        NS_ENSURE_SUCCESS(rv, rv);
  1.3684 +
  1.3685 +        if (!isDirectory) {
  1.3686 +          NS_WARNING("Unknown file found!");
  1.3687 +          return NS_ERROR_UNEXPECTED;
  1.3688 +        }
  1.3689 +      }
  1.3690 +
  1.3691 +      Client::Type clientType;
  1.3692 +      rv = Client::TypeFromText(leafName, clientType);
  1.3693 +      if (NS_FAILED(rv)) {
  1.3694 +        NS_WARNING("Unknown directory found!");
  1.3695 +        if (!initialized) {
  1.3696 +          return NS_ERROR_UNEXPECTED;
  1.3697 +        }
  1.3698 +        continue;
  1.3699 +      }
  1.3700 +
  1.3701 +      nsRefPtr<Client>& client = aQuotaManager->mClients[clientType];
  1.3702 +
  1.3703 +      if (initialized) {
  1.3704 +        rv = client->GetUsageForOrigin(aPersistenceType, mGroup, mOrigin, this);
  1.3705 +      }
  1.3706 +      else {
  1.3707 +        rv = client->InitOrigin(aPersistenceType, mGroup, mOrigin, this);
  1.3708 +      }
  1.3709 +      NS_ENSURE_SUCCESS(rv, rv);
  1.3710 +    }
  1.3711 +  }
  1.3712 +
  1.3713 +  return NS_OK;
  1.3714 +}
  1.3715 +
  1.3716 +NS_IMPL_ISUPPORTS_INHERITED(AsyncUsageRunnable, nsRunnable, nsIQuotaRequest)
  1.3717 +
  1.3718 +NS_IMETHODIMP
  1.3719 +AsyncUsageRunnable::Run()
  1.3720 +{
  1.3721 +  PROFILER_LABEL("Quota", "AsyncUsageRunnable::Run");
  1.3722 +
  1.3723 +  nsresult rv = RunInternal();
  1.3724 +
  1.3725 +  if (!NS_IsMainThread()) {
  1.3726 +    if (NS_FAILED(rv)) {
  1.3727 +      ResetUsage();
  1.3728 +    }
  1.3729 +
  1.3730 +    if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) {
  1.3731 +      NS_WARNING("Failed to dispatch to main thread!");
  1.3732 +    }
  1.3733 +  }
  1.3734 +
  1.3735 +  return NS_OK;
  1.3736 +}
  1.3737 +
  1.3738 +NS_IMETHODIMP
  1.3739 +AsyncUsageRunnable::Cancel()
  1.3740 +{
  1.3741 +  if (mCanceled.exchange(true)) {
  1.3742 +    NS_WARNING("Canceled more than once?!");
  1.3743 +    return NS_ERROR_UNEXPECTED;
  1.3744 +  }
  1.3745 +
  1.3746 +  return NS_OK;
  1.3747 +}
  1.3748 +
  1.3749 +nsresult
  1.3750 +ResetOrClearRunnable::OnExclusiveAccessAcquired()
  1.3751 +{
  1.3752 +  QuotaManager* quotaManager = QuotaManager::Get();
  1.3753 +  NS_ASSERTION(quotaManager, "This should never fail!");
  1.3754 +
  1.3755 +  nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
  1.3756 +  NS_ENSURE_SUCCESS(rv, rv);
  1.3757 +
  1.3758 +  return NS_OK;
  1.3759 +}
  1.3760 +
  1.3761 +// static
  1.3762 +void
  1.3763 +ResetOrClearRunnable::InvalidateOpenedStorages(
  1.3764 +                              nsTArray<nsCOMPtr<nsIOfflineStorage> >& aStorages,
  1.3765 +                              void* aClosure)
  1.3766 +{
  1.3767 +  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1.3768 +
  1.3769 +  nsTArray<nsCOMPtr<nsIOfflineStorage> > storages;
  1.3770 +  storages.SwapElements(aStorages);
  1.3771 +
  1.3772 +  for (uint32_t index = 0; index < storages.Length(); index++) {
  1.3773 +    storages[index]->Invalidate();
  1.3774 +  }
  1.3775 +}
  1.3776 +
  1.3777 +void
  1.3778 +ResetOrClearRunnable::DeleteFiles(QuotaManager* aQuotaManager,
  1.3779 +                                  PersistenceType aPersistenceType)
  1.3780 +{
  1.3781 +  AssertIsOnIOThread();
  1.3782 +  NS_ASSERTION(aQuotaManager, "Don't pass me null!");
  1.3783 +
  1.3784 +  nsresult rv;
  1.3785 +
  1.3786 +  nsCOMPtr<nsIFile> directory =
  1.3787 +    do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
  1.3788 +  NS_ENSURE_SUCCESS_VOID(rv);
  1.3789 +
  1.3790 +  rv = directory->InitWithPath(aQuotaManager->GetStoragePath(aPersistenceType));
  1.3791 +  NS_ENSURE_SUCCESS_VOID(rv);
  1.3792 +
  1.3793 +  rv = directory->Remove(true);
  1.3794 +  if (rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST &&
  1.3795 +      rv != NS_ERROR_FILE_NOT_FOUND && NS_FAILED(rv)) {
  1.3796 +    // This should never fail if we've closed all storage connections
  1.3797 +    // correctly...
  1.3798 +    NS_ERROR("Failed to remove directory!");
  1.3799 +  }
  1.3800 +}
  1.3801 +
  1.3802 +NS_IMPL_ISUPPORTS_INHERITED0(ResetOrClearRunnable, nsRunnable)
  1.3803 +
  1.3804 +NS_IMETHODIMP
  1.3805 +ResetOrClearRunnable::Run()
  1.3806 +{
  1.3807 +  QuotaManager* quotaManager = QuotaManager::Get();
  1.3808 +  NS_ASSERTION(quotaManager, "This should never fail!");
  1.3809 +
  1.3810 +  switch (mCallbackState) {
  1.3811 +    case Pending: {
  1.3812 +      NS_NOTREACHED("Should never get here without being dispatched!");
  1.3813 +      return NS_ERROR_UNEXPECTED;
  1.3814 +    }
  1.3815 +
  1.3816 +    case OpenAllowed: {
  1.3817 +      NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1.3818 +
  1.3819 +      AdvanceState();
  1.3820 +
  1.3821 +      // Now we have to wait until the thread pool is done with all of the
  1.3822 +      // storages we care about.
  1.3823 +      nsresult rv =
  1.3824 +        quotaManager->AcquireExclusiveAccess(NullCString(),
  1.3825 +                                             Nullable<PersistenceType>(), this,
  1.3826 +                                             InvalidateOpenedStorages, nullptr);
  1.3827 +      NS_ENSURE_SUCCESS(rv, rv);
  1.3828 +
  1.3829 +      return NS_OK;
  1.3830 +    }
  1.3831 +
  1.3832 +    case IO: {
  1.3833 +      AssertIsOnIOThread();
  1.3834 +
  1.3835 +      AdvanceState();
  1.3836 +
  1.3837 +      if (mClear) {
  1.3838 +        DeleteFiles(quotaManager, PERSISTENCE_TYPE_PERSISTENT);
  1.3839 +        DeleteFiles(quotaManager, PERSISTENCE_TYPE_TEMPORARY);
  1.3840 +      }
  1.3841 +
  1.3842 +      quotaManager->RemoveQuota();
  1.3843 +      quotaManager->ResetOrClearCompleted();
  1.3844 +
  1.3845 +      // Now dispatch back to the main thread.
  1.3846 +      if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) {
  1.3847 +        NS_WARNING("Failed to dispatch to main thread!");
  1.3848 +        return NS_ERROR_FAILURE;
  1.3849 +      }
  1.3850 +
  1.3851 +      return NS_OK;
  1.3852 +    }
  1.3853 +
  1.3854 +    case Complete: {
  1.3855 +      NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1.3856 +
  1.3857 +      // Tell the QuotaManager that we're done.
  1.3858 +      quotaManager->AllowNextSynchronizedOp(OriginOrPatternString::FromNull(),
  1.3859 +                                            Nullable<PersistenceType>(),
  1.3860 +                                            EmptyCString());
  1.3861 +
  1.3862 +      return NS_OK;
  1.3863 +    }
  1.3864 +
  1.3865 +    default:
  1.3866 +      NS_ERROR("Unknown state value!");
  1.3867 +      return NS_ERROR_UNEXPECTED;
  1.3868 +  }
  1.3869 +
  1.3870 +  NS_NOTREACHED("Should never get here!");
  1.3871 +  return NS_ERROR_UNEXPECTED;
  1.3872 +}
  1.3873 +
  1.3874 +NS_IMETHODIMP
  1.3875 +FinalizeOriginEvictionRunnable::Run()
  1.3876 +{
  1.3877 +  QuotaManager* quotaManager = QuotaManager::Get();
  1.3878 +  NS_ASSERTION(quotaManager, "This should never fail!");
  1.3879 +
  1.3880 +  nsresult rv;
  1.3881 +
  1.3882 +  switch (mCallbackState) {
  1.3883 +    case Pending: {
  1.3884 +      NS_NOTREACHED("Should never get here without being dispatched!");
  1.3885 +      return NS_ERROR_UNEXPECTED;
  1.3886 +    }
  1.3887 +
  1.3888 +    case OpenAllowed: {
  1.3889 +      NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1.3890 +
  1.3891 +      AdvanceState();
  1.3892 +
  1.3893 +      rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
  1.3894 +      if (NS_FAILED(rv)) {
  1.3895 +        NS_WARNING("Failed to dispatch to the IO thread!");
  1.3896 +      }
  1.3897 +
  1.3898 +      return NS_OK;
  1.3899 +    }
  1.3900 +
  1.3901 +    case IO: {
  1.3902 +      AssertIsOnIOThread();
  1.3903 +
  1.3904 +      AdvanceState();
  1.3905 +
  1.3906 +      for (uint32_t index = 0; index < mOrigins.Length(); index++) {
  1.3907 +        quotaManager->OriginClearCompleted(
  1.3908 +                            PERSISTENCE_TYPE_TEMPORARY,
  1.3909 +                            OriginOrPatternString::FromOrigin(mOrigins[index]));
  1.3910 +      }
  1.3911 +
  1.3912 +      if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) {
  1.3913 +        NS_WARNING("Failed to dispatch to main thread!");
  1.3914 +        return NS_ERROR_FAILURE;
  1.3915 +      }
  1.3916 +
  1.3917 +      return NS_OK;
  1.3918 +    }
  1.3919 +
  1.3920 +    case Complete: {
  1.3921 +      NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1.3922 +
  1.3923 +      for (uint32_t index = 0; index < mOrigins.Length(); index++) {
  1.3924 +        quotaManager->AllowNextSynchronizedOp(
  1.3925 +                          OriginOrPatternString::FromOrigin(mOrigins[index]),
  1.3926 +                          Nullable<PersistenceType>(PERSISTENCE_TYPE_TEMPORARY),
  1.3927 +                          EmptyCString());
  1.3928 +      }
  1.3929 +
  1.3930 +      return NS_OK;
  1.3931 +    }
  1.3932 +
  1.3933 +    default:
  1.3934 +      NS_ERROR("Unknown state value!");
  1.3935 +      return NS_ERROR_UNEXPECTED;
  1.3936 +  }
  1.3937 +
  1.3938 +  NS_NOTREACHED("Should never get here!");
  1.3939 +  return NS_ERROR_UNEXPECTED;
  1.3940 +}
  1.3941 +
  1.3942 +nsresult
  1.3943 +FinalizeOriginEvictionRunnable::Dispatch()
  1.3944 +{
  1.3945 +  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
  1.3946 +  NS_ASSERTION(mCallbackState == Pending, "Huh?");
  1.3947 +
  1.3948 +  mCallbackState = OpenAllowed;
  1.3949 +  return NS_DispatchToMainThread(this);
  1.3950 +}
  1.3951 +
  1.3952 +nsresult
  1.3953 +FinalizeOriginEvictionRunnable::RunImmediately()
  1.3954 +{
  1.3955 +  AssertIsOnIOThread();
  1.3956 +  NS_ASSERTION(mCallbackState == Pending, "Huh?");
  1.3957 +
  1.3958 +  mCallbackState = IO;
  1.3959 +  return this->Run();
  1.3960 +}
  1.3961 +
  1.3962 +NS_IMETHODIMP
  1.3963 +WaitForTransactionsToFinishRunnable::Run()
  1.3964 +{
  1.3965 +  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1.3966 +  NS_ASSERTION(mOp, "Null op!");
  1.3967 +  NS_ASSERTION(mOp->mListener, "Nothing to run!");
  1.3968 +  NS_ASSERTION(mCountdown, "Wrong countdown!");
  1.3969 +
  1.3970 +  if (--mCountdown) {
  1.3971 +    return NS_OK;
  1.3972 +  }
  1.3973 +
  1.3974 +  // Don't hold the listener alive longer than necessary.
  1.3975 +  nsRefPtr<AcquireListener> listener;
  1.3976 +  listener.swap(mOp->mListener);
  1.3977 +
  1.3978 +  mOp = nullptr;
  1.3979 +
  1.3980 +  nsresult rv = listener->OnExclusiveAccessAcquired();
  1.3981 +  NS_ENSURE_SUCCESS(rv, rv);
  1.3982 +
  1.3983 +  // The listener is responsible for calling
  1.3984 +  // QuotaManager::AllowNextSynchronizedOp.
  1.3985 +  return NS_OK;
  1.3986 +}
  1.3987 +
  1.3988 +NS_IMETHODIMP
  1.3989 +WaitForLockedFilesToFinishRunnable::Run()
  1.3990 +{
  1.3991 +  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1.3992 +
  1.3993 +  mBusy = false;
  1.3994 +
  1.3995 +  return NS_OK;
  1.3996 +}
  1.3997 +
  1.3998 +NS_IMETHODIMP
  1.3999 +SaveOriginAccessTimeRunnable::Run()
  1.4000 +{
  1.4001 +  AssertIsOnIOThread();
  1.4002 +
  1.4003 +  QuotaManager* quotaManager = QuotaManager::Get();
  1.4004 +  NS_ASSERTION(quotaManager, "This should never fail!");
  1.4005 +
  1.4006 +  nsCOMPtr<nsIFile> directory;
  1.4007 +  nsresult rv =
  1.4008 +    quotaManager->GetDirectoryForOrigin(PERSISTENCE_TYPE_TEMPORARY, mOrigin,
  1.4009 +                                        getter_AddRefs(directory));
  1.4010 +  NS_ENSURE_SUCCESS(rv, rv);
  1.4011 +
  1.4012 +  nsCOMPtr<nsIBinaryOutputStream> stream;
  1.4013 +  rv = GetDirectoryMetadataStream(directory, true, getter_AddRefs(stream));
  1.4014 +  NS_ENSURE_SUCCESS(rv, rv);
  1.4015 +
  1.4016 +  // The origin directory may not exist anymore.
  1.4017 +  if (stream) {
  1.4018 +    rv = stream->Write64(mTimestamp);
  1.4019 +    NS_ENSURE_SUCCESS(rv, rv);
  1.4020 +  }
  1.4021 +
  1.4022 +  return NS_OK;
  1.4023 +}

mercurial