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