author | Jan Varga <jan.varga@gmail.com> |
Sat, 01 Nov 2014 02:21:25 +0100 | |
changeset 213470 | 45dad11b6580464401881765f575e00f81fb136e |
parent 213469 | 5eebce6c884517d56f9337b365073cd9db53d742 |
child 213471 | b327975e972b1d428f59af39aebf7f08e6460d9b |
push id | 27753 |
push user | philringnalda@gmail.com |
push date | Sun, 02 Nov 2014 16:27:30 +0000 |
treeherder | mozilla-central@443853a35898 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | bent |
bugs | 1089764 |
milestone | 36.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/dom/asmjscache/AsmJSCache.cpp +++ b/dom/asmjscache/AsmJSCache.cpp @@ -487,17 +487,20 @@ public: MainProcessRunnable(nsIPrincipal* aPrincipal, OpenMode aOpenMode, WriteParams aWriteParams) : mPrincipal(aPrincipal), mOpenMode(aOpenMode), mWriteParams(aWriteParams), mNeedAllowNextSynchronizedOp(false), mPersistence(quota::PERSISTENCE_TYPE_INVALID), - mState(eInitial) + mState(eInitial), + mIsApp(false), + mHasUnlimStoragePerm(false), + mEnforcingQuota(true) { MOZ_ASSERT(IsMainProcess()); } virtual ~MainProcessRunnable() { MOZ_ASSERT(mState == eFinished); MOZ_ASSERT(!mNeedAllowNextSynchronizedOp); @@ -588,16 +591,19 @@ protected: // Called by MainProcessRunnable on the main thread after a call to Close(): virtual void OnClose() { FinishOnMainThread(); } private: + void + InitPersistenceType(); + nsresult InitOnMainThread(); nsresult ReadMetadata(); nsresult OpenCacheFileForWrite(); @@ -654,101 +660,119 @@ private: eReadyToOpenCacheFileForRead, // Waiting to open cache file for read eSendingCacheFile, // Waiting to send OnOpenCacheFile on the main thread eOpened, // Finished calling OnOpen, waiting to be closed eClosing, // Waiting to be dispatched to main thread again eFailing, // Just failed, waiting to be dispatched to the main thread eFinished, // Terminal state }; State mState; + + bool mIsApp; + bool mHasUnlimStoragePerm; + bool mEnforcingQuota; }; +void +MainProcessRunnable::InitPersistenceType() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mState == eInitial); + + if (mOpenMode == eOpenForWrite) { + MOZ_ASSERT(mPersistence == quota::PERSISTENCE_TYPE_INVALID); + + // If we are performing install-time caching of an app, we'd like to store + // the cache entry in persistent storage so the entry is never evicted, + // but we need to check that quota is not enforced for the app. + // That justifies us in skipping all quota checks when storing the cache + // entry and avoids all the issues around the persistent quota prompt. + // If quota is enforced for the app, then we can still cache in temporary + // for a likely good first-run experience. + + MOZ_ASSERT_IF(mWriteParams.mInstalled, mIsApp); + + if (mWriteParams.mInstalled && + !QuotaManager::IsQuotaEnforced(quota::PERSISTENCE_TYPE_PERSISTENT, + mOrigin, mHasUnlimStoragePerm)) { + mPersistence = quota::PERSISTENCE_TYPE_PERSISTENT; + } else { + mPersistence = quota::PERSISTENCE_TYPE_TEMPORARY; + } + + return; + } + + // For the reasons described above, apps may have cache entries in both + // persistent and temporary storage. At lookup time we don't know how and + // where the given script was cached, so start the search in persistent + // storage and, if that fails, search in temporary storage. (Non-apps can + // only be stored in temporary storage.) + + MOZ_ASSERT_IF(mPersistence != quota::PERSISTENCE_TYPE_INVALID, + mIsApp && mPersistence == quota::PERSISTENCE_TYPE_PERSISTENT); + + if (mPersistence == quota::PERSISTENCE_TYPE_INVALID && mIsApp) { + mPersistence = quota::PERSISTENCE_TYPE_PERSISTENT; + } else { + mPersistence = quota::PERSISTENCE_TYPE_TEMPORARY; + } +} + nsresult MainProcessRunnable::InitOnMainThread() { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(mState == eInitial); QuotaManager* qm = QuotaManager::GetOrCreate(); NS_ENSURE_STATE(qm); - nsresult rv = QuotaManager::GetInfoFromPrincipal(mPrincipal, &mGroup, - &mOrigin, nullptr, nullptr); + nsresult rv = + QuotaManager::GetInfoFromPrincipal(mPrincipal, + quota::PERSISTENCE_TYPE_INVALID, + &mGroup, &mOrigin, &mIsApp, + &mHasUnlimStoragePerm); NS_ENSURE_SUCCESS(rv, rv); - bool isApp = mPrincipal->GetAppStatus() != - nsIPrincipal::APP_STATUS_NOT_INSTALLED; + // XXX Don't use mGroup yet! We might need to update it right after we + // initialize persistence type. - if (mOpenMode == eOpenForWrite) { - MOZ_ASSERT(mPersistence == quota::PERSISTENCE_TYPE_INVALID); - if (mWriteParams.mInstalled) { - // If we are performing install-time caching of an app, we'd like to store - // the cache entry in persistent storage so the entry is never evicted, - // but we need to verify that the app has unlimited storage permissions - // first. Unlimited storage permissions justify us in skipping all quota - // checks when storing the cache entry and avoids all the issues around - // the persistent quota prompt. - MOZ_ASSERT(isApp); - - nsCOMPtr<nsIPermissionManager> pm = - services::GetPermissionManager(); - NS_ENSURE_TRUE(pm, NS_ERROR_UNEXPECTED); - - uint32_t permission; - rv = pm->TestPermissionFromPrincipal(mPrincipal, - PERMISSION_STORAGE_UNLIMITED, - &permission); - NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED); + InitPersistenceType(); - // If app doens't have the unlimited storage permission, we can still - // cache in temporary for a likely good first-run experience. - mPersistence = permission == nsIPermissionManager::ALLOW_ACTION - ? quota::PERSISTENCE_TYPE_PERSISTENT - : quota::PERSISTENCE_TYPE_TEMPORARY; - } else { - mPersistence = quota::PERSISTENCE_TYPE_TEMPORARY; - } - } else { - // For the reasons described above, apps may have cache entries in both - // persistent and temporary storage. At lookup time we don't know how and - // where the given script was cached, so start the search in persistent - // storage and, if that fails, search in temporary storage. (Non-apps can - // only be stored in temporary storage.) - if (mPersistence == quota::PERSISTENCE_TYPE_INVALID) { - mPersistence = isApp ? quota::PERSISTENCE_TYPE_PERSISTENT - : quota::PERSISTENCE_TYPE_TEMPORARY; - } else { - MOZ_ASSERT(isApp); - MOZ_ASSERT(mPersistence == quota::PERSISTENCE_TYPE_PERSISTENT); - mPersistence = quota::PERSISTENCE_TYPE_TEMPORARY; - } + // XXX Since we couldn't pass persistence type to GetInfoFromPrincipal(), + // we need to do this manually. + // This hack is only temporary, it will go away once we have regular + // metadata files for persistent storge. + if (mPersistence == quota::PERSISTENCE_TYPE_PERSISTENT) { + mGroup = mOrigin; } + mEnforcingQuota = + QuotaManager::IsQuotaEnforced(mPersistence, mOrigin, mHasUnlimStoragePerm); + QuotaManager::GetStorageId(mPersistence, mOrigin, quota::Client::ASMJS, NS_LITERAL_STRING("asmjs"), mStorageId); return NS_OK; } nsresult MainProcessRunnable::ReadMetadata() { AssertIsOnIOThread(); MOZ_ASSERT(mState == eReadyToReadMetadata); QuotaManager* qm = QuotaManager::Get(); MOZ_ASSERT(qm, "We are on the QuotaManager's IO thread"); - // Only track quota for temporary storage. For persistent storage, we've - // already checked that we have unlimited-storage permissions. - bool trackQuota = mPersistence == quota::PERSISTENCE_TYPE_TEMPORARY; - - nsresult rv = qm->EnsureOriginIsInitialized(mPersistence, mGroup, mOrigin, - trackQuota, - getter_AddRefs(mDirectory)); + nsresult rv = + qm->EnsureOriginIsInitialized(mPersistence, mGroup, mOrigin, + mHasUnlimStoragePerm, + getter_AddRefs(mDirectory)); NS_ENSURE_SUCCESS(rv, rv); rv = mDirectory->Append(NS_LITERAL_STRING(ASMJSCACHE_DIRECTORY_NAME)); NS_ENSURE_SUCCESS(rv, rv); bool exists; rv = mDirectory->Exists(&exists); NS_ENSURE_SUCCESS(rv, rv); @@ -806,21 +830,17 @@ MainProcessRunnable::OpenCacheFileForWri nsCOMPtr<nsIFile> file; nsresult rv = GetCacheFile(mDirectory, mModuleIndex, getter_AddRefs(file)); NS_ENSURE_SUCCESS(rv, rv); QuotaManager* qm = QuotaManager::Get(); MOZ_ASSERT(qm, "We are on the QuotaManager's IO thread"); - // If we are allocating in temporary storage, ask the QuotaManager if we're - // within the quota. If we are allocating in persistent storage, we've already - // checked that we have the unlimited-storage permission, so there is nothing - // to check. - if (mPersistence == quota::PERSISTENCE_TYPE_TEMPORARY) { + if (mEnforcingQuota) { // Create the QuotaObject before all file IO and keep it alive until caching // completes to get maximum assertion coverage in QuotaManager against // concurrent removal, etc. mQuotaObject = qm->GetQuotaObject(mPersistence, mGroup, mOrigin, file); NS_ENSURE_STATE(mQuotaObject); if (!mQuotaObject->MaybeAllocateMoreSpace(0, mWriteParams.mSize)) { // If the request fails, it might be because mOrigin is using too much @@ -861,17 +881,17 @@ MainProcessRunnable::OpenCacheFileForRea nsCOMPtr<nsIFile> file; nsresult rv = GetCacheFile(mDirectory, mModuleIndex, getter_AddRefs(file)); NS_ENSURE_SUCCESS(rv, rv); QuotaManager* qm = QuotaManager::Get(); MOZ_ASSERT(qm, "We are on the QuotaManager's IO thread"); - if (mPersistence == quota::PERSISTENCE_TYPE_TEMPORARY) { + if (mEnforcingQuota) { // Even though it's not strictly necessary, create the QuotaObject before // all file IO and keep it alive until caching completes to get maximum // assertion coverage in QuotaManager against concurrent removal, etc. mQuotaObject = qm->GetQuotaObject(mPersistence, mGroup, mOrigin, file); NS_ENSURE_STATE(mQuotaObject); } rv = file->GetFileSize(&mFileSize);
--- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -3088,31 +3088,30 @@ nsDOMWindowUtils::GetFileReferences(cons int32_t* aSliceRefCnt, JSContext* aCx, bool* aResult) { MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow); NS_ENSURE_TRUE(window, NS_ERROR_FAILURE); - nsCString origin; - quota::PersistenceType defaultPersistenceType; - nsresult rv = - quota::QuotaManager::GetInfoFromWindow(window, nullptr, &origin, nullptr, - &defaultPersistenceType); - NS_ENSURE_SUCCESS(rv, rv); - IDBOpenDBOptions options; JS::Rooted<JS::Value> optionsVal(aCx, aOptions); if (!options.Init(aCx, optionsVal)) { return NS_ERROR_TYPE_ERR; } quota::PersistenceType persistenceType = - quota::PersistenceTypeFromStorage(options.mStorage, defaultPersistenceType); + quota::PersistenceTypeFromStorage(options.mStorage); + + nsCString origin; + nsresult rv = + quota::QuotaManager::GetInfoFromWindow(window, persistenceType, nullptr, + &origin, nullptr, nullptr); + NS_ENSURE_SUCCESS(rv, rv); nsRefPtr<indexedDB::IndexedDatabaseManager> mgr = indexedDB::IndexedDatabaseManager::Get(); if (mgr) { rv = mgr->BlockAndGetFileReferences(persistenceType, origin, aDatabaseName, aId, aRefCnt, aDBRefCnt, aSliceRefCnt, aResult);
--- a/dom/indexedDB/ActorsParent.cpp +++ b/dom/indexedDB/ActorsParent.cpp @@ -1,16 +1,15 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "ActorsParent.h" #include <algorithm> -#include "CheckQuotaHelper.h" #include "FileInfo.h" #include "FileManager.h" #include "IDBObjectStore.h" #include "IDBTransaction.h" #include "IndexedDatabase.h" #include "IndexedDatabaseInlines.h" #include "IndexedDatabaseManager.h" #include "js/StructuredClone.h" @@ -3887,16 +3886,17 @@ protected: nsTArray<MaybeBlockedDatabaseInfo> mMaybeBlockedDatabases; const CommonFactoryRequestParams mCommonParams; nsCString mGroup; nsCString mOrigin; nsCString mDatabaseId; State mState; + bool mHasUnlimStoragePerm; bool mEnforcingQuota; const bool mDeleting; bool mBlockedQuotaManager; bool mChromeWriteAccessAllowed; public: void NoteDatabaseBlocked(Database* aDatabase); @@ -5933,16 +5933,22 @@ Factory::AllocPBackgroundIDBFactoryReque } const PrincipalInfo& principalInfo = commonParams->principalInfo(); if (NS_WARN_IF(principalInfo.type() == PrincipalInfo::TNullPrincipalInfo)) { ASSERT_UNLESS_FUZZING(); return nullptr; } + if (NS_WARN_IF(principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo && + metadata.persistenceType() != PERSISTENCE_TYPE_PERSISTENT)) { + ASSERT_UNLESS_FUZZING(); + return nullptr; + } + nsRefPtr<ContentParent> contentParent = BackgroundParent::GetContentParent(Manager()); nsRefPtr<FactoryOp> actor; if (aParams.type() == FactoryRequestParams::TOpenDatabaseRequestParams) { actor = new OpenDatabaseOp(this, contentParent.forget(), mOptionalWindowId, @@ -10437,16 +10443,17 @@ AutoSetProgressHandler::Register( FactoryOp::FactoryOp(Factory* aFactory, already_AddRefed<ContentParent> aContentParent, const CommonFactoryRequestParams& aCommonParams, bool aDeleting) : mFactory(aFactory) , mContentParent(Move(aContentParent)) , mCommonParams(aCommonParams) , mState(State_Initial) + , mHasUnlimStoragePerm(false) , mEnforcingQuota(true) , mDeleting(aDeleting) , mBlockedQuotaManager(false) , mChromeWriteAccessAllowed(false) { AssertIsOnBackgroundThread(); MOZ_ASSERT(aFactory); MOZ_ASSERT(!QuotaClient::IsShuttingDownOnNonMainThread()); @@ -10682,21 +10689,24 @@ FactoryOp::CheckPermission(ContentParent return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR; } if (NS_WARN_IF(mCommonParams.privateBrowsingMode())) { // XXX This is only temporary. return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR; } + PersistenceType persistenceType = mCommonParams.metadata().persistenceType(); + const PrincipalInfo& principalInfo = mCommonParams.principalInfo(); MOZ_ASSERT(principalInfo.type() != PrincipalInfo::TNullPrincipalInfo); if (principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) { MOZ_ASSERT(mState == State_Initial); + MOZ_ASSERT(persistenceType == PERSISTENCE_TYPE_PERSISTENT); if (aContentParent) { // Check to make sure that the child process has access to the database it // is accessing. NS_NAMED_LITERAL_CSTRING(permissionStringBase, kPermissionStringChromeBase); NS_ConvertUTF16toUTF8 databaseName(mCommonParams.metadata().name()); NS_NAMED_LITERAL_CSTRING(readSuffix, kPermissionStringChromeReadSuffix); @@ -10735,18 +10745,22 @@ FactoryOp::CheckPermission(ContentParent } mChromeWriteAccessAllowed = canWrite; } else { mChromeWriteAccessAllowed = true; } if (State_Initial == mState) { - QuotaManager::GetInfoForChrome(&mGroup, &mOrigin, nullptr, nullptr); - mEnforcingQuota = false; + QuotaManager::GetInfoForChrome(&mGroup, &mOrigin, nullptr, + &mHasUnlimStoragePerm); + + mEnforcingQuota = + QuotaManager::IsQuotaEnforced(persistenceType, mOrigin, + mHasUnlimStoragePerm); } *aPermission = PermissionRequestBase::kPermissionAllowed; return NS_OK; } MOZ_ASSERT(principalInfo.type() == PrincipalInfo::TContentPrincipalInfo); @@ -10754,23 +10768,21 @@ FactoryOp::CheckPermission(ContentParent nsCOMPtr<nsIPrincipal> principal = PrincipalInfoToPrincipal(principalInfo, &rv); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } PermissionRequestBase::PermissionValue permission; - if (mCommonParams.metadata().persistenceType() == - PERSISTENCE_TYPE_TEMPORARY) { + if (persistenceType == PERSISTENCE_TYPE_TEMPORARY) { // Temporary storage doesn't need to check the permission. permission = PermissionRequestBase::kPermissionAllowed; } else { - MOZ_ASSERT(mCommonParams.metadata().persistenceType() == - PERSISTENCE_TYPE_PERSISTENT); + MOZ_ASSERT(persistenceType == PERSISTENCE_TYPE_PERSISTENT); #ifdef MOZ_CHILD_PERMISSIONS if (aContentParent) { if (NS_WARN_IF(!AssertAppPrincipal(aContentParent, principal))) { IDB_REPORT_INTERNAL_ERR(); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } @@ -10786,32 +10798,25 @@ FactoryOp::CheckPermission(ContentParent if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } } } if (permission != PermissionRequestBase::kPermissionDenied && State_Initial == mState) { - rv = QuotaManager::GetInfoFromPrincipal(principal, &mGroup, &mOrigin, - nullptr, nullptr); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - - if (permission == PermissionRequestBase::kPermissionAllowed && - mEnforcingQuota) - { - // If we're running from a window then we should check the quota permission - // as well. - uint32_t quotaPermission = CheckQuotaHelper::GetQuotaPermission(principal); - if (quotaPermission == nsIPermissionManager::ALLOW_ACTION) { - mEnforcingQuota = false; - } + rv = QuotaManager::GetInfoFromPrincipal(principal, persistenceType, &mGroup, + &mOrigin, nullptr, + &mHasUnlimStoragePerm); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + mEnforcingQuota = QuotaManager::IsQuotaEnforced(persistenceType, mOrigin, + mHasUnlimStoragePerm); } *aPermission = permission; return NS_OK; } nsresult FactoryOp::SendVersionChangeMessages(DatabaseActorInfo* aDatabaseActorInfo, @@ -11206,17 +11211,17 @@ OpenDatabaseOp::DoDatabaseWork() MOZ_ASSERT(quotaManager); nsCOMPtr<nsIFile> dbDirectory; nsresult rv = quotaManager->EnsureOriginIsInitialized(persistenceType, mGroup, mOrigin, - mEnforcingQuota, + mHasUnlimStoragePerm, getter_AddRefs(dbDirectory)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } rv = dbDirectory->Append(NS_LITERAL_STRING(IDB_DIRECTORY_NAME)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv;
--- a/dom/indexedDB/IDBDatabase.cpp +++ b/dom/indexedDB/IDBDatabase.cpp @@ -968,18 +968,20 @@ nsresult IDBDatabase::GetQuotaInfo(nsACString& aOrigin, PersistenceType* aPersistenceType) { using mozilla::dom::quota::QuotaManager; MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); MOZ_ASSERT(NS_IsMainThread()); + PersistenceType persistenceType = mSpec->metadata().persistenceType(); + if (aPersistenceType) { - *aPersistenceType = mSpec->metadata().persistenceType(); + *aPersistenceType = persistenceType; MOZ_ASSERT(*aPersistenceType != PERSISTENCE_TYPE_INVALID); } PrincipalInfo* principalInfo = mFactory->GetPrincipalInfo(); MOZ_ASSERT(principalInfo); switch (principalInfo->type()) { case PrincipalInfo::TNullPrincipalInfo: @@ -993,16 +995,17 @@ IDBDatabase::GetQuotaInfo(nsACString& aO nsresult rv; nsCOMPtr<nsIPrincipal> principal = PrincipalInfoToPrincipal(*principalInfo, &rv); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } rv = QuotaManager::GetInfoFromPrincipal(principal, + persistenceType, nullptr, &aOrigin, nullptr, nullptr); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
--- a/dom/indexedDB/IDBFactory.cpp +++ b/dom/indexedDB/IDBFactory.cpp @@ -478,18 +478,17 @@ IDBFactory::OpenInternal(nsIPrincipal* a PersistenceType persistenceType; bool persistenceTypeIsExplicit; if (principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) { // Chrome privilege always gets persistent storage. persistenceType = PERSISTENCE_TYPE_PERSISTENT; persistenceTypeIsExplicit = false; } else { - persistenceType = - PersistenceTypeFromStorage(aStorageType, PERSISTENCE_TYPE_PERSISTENT); + persistenceType = PersistenceTypeFromStorage(aStorageType); persistenceTypeIsExplicit = aStorageType.WasPassed(); } DatabaseMetadata& metadata = commonParams.metadata(); metadata.name() = aName; metadata.persistenceType() = persistenceType; metadata.persistenceTypeIsExplicit() = persistenceTypeIsExplicit;
--- a/dom/indexedDB/IDBMutableFile.cpp +++ b/dom/indexedDB/IDBMutableFile.cpp @@ -152,31 +152,32 @@ IDBMutableFile::Create(IDBDatabase* aDat PrincipalInfo* principalInfo = aDatabase->Factory()->GetPrincipalInfo(); MOZ_ASSERT(principalInfo); nsCOMPtr<nsIPrincipal> principal = PrincipalInfoToPrincipal(*principalInfo); if (NS_WARN_IF(!principal)) { return nullptr; } + const DatabaseSpec* spec = aDatabase->Spec(); + MOZ_ASSERT(spec); + + PersistenceType persistenceType = spec->metadata().persistenceType(); + nsCString group; nsCString origin; if (NS_WARN_IF(NS_FAILED(QuotaManager::GetInfoFromPrincipal(principal, + persistenceType, &group, &origin, nullptr, nullptr)))) { return nullptr; } - const DatabaseSpec* spec = aDatabase->Spec(); - MOZ_ASSERT(spec); - - PersistenceType persistenceType = spec->metadata().persistenceType(); - nsCString storageId; QuotaManager::GetStorageId(persistenceType, origin, Client::IDB, aDatabase->Name(), storageId); nsCOMPtr<nsIFile> file = GetFileFor(fileInfo);
--- a/dom/indexedDB/moz.build +++ b/dom/indexedDB/moz.build @@ -88,13 +88,12 @@ include('/ipc/chromium/chromium-config.m FINAL_LIBRARY = 'xul' FAIL_ON_WARNINGS = True LOCAL_INCLUDES += [ '/db/sqlite3/src', '/dom/base', - '/dom/quota', '/dom/storage', '/ipc/glue', '/xpcom/build', ]
--- a/dom/indexedDB/test/mochitest.ini +++ b/dom/indexedDB/test/mochitest.ini @@ -143,17 +143,19 @@ skip-if = (buildapp == 'b2g' && toolkit skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 [test_cursors.html] skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 [test_deleteDatabase.html] skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 [test_deleteDatabase_interactions.html] skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 [test_disabled_quota_prompt.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +# Test temporarily disabled. +skip-if = true +# skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 [test_error_events_abort_transactions.html] skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 [test_event_propagation.html] skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 [test_event_source.html] skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 [test_exceptions_in_events.html] skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
--- a/dom/indexedDB/test/unit/test_temporary_storage.js +++ b/dom/indexedDB/test/unit/test_temporary_storage.js @@ -1,15 +1,13 @@ /** * Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -Components.utils.importGlobalProperties(['Blob']); - var testGenerator = testSteps(); function testSteps() { const name = this.window ? window.location.pathname : "Splendid Test"; const urls = [ { url: "http://www.alpha.com", flags: [true, true, true, true] }, @@ -32,27 +30,30 @@ function testSteps() { url: "http://www.marie.org", flags: [true, true, true, true] }, { url: "http://www.john.org", flags: [true, true, true, true] }, { url: "http://www.ema.org", flags: [true, true, true, true] }, { url: "http://www.trigger.com", flags: [false, true, true, true] } ]; const lastIndex = urls.length - 1; const lastUrl = urls[lastIndex].url; + const openDBOptions = [ + { version: 1, storage: "temporary" }, + { version: 1 } + ]; + let quotaManager = Components.classes["@mozilla.org/dom/quota/manager;1"] .getService(Components.interfaces.nsIQuotaManager); let ioService = Components.classes["@mozilla.org/network/io-service;1"] .getService(Components.interfaces.nsIIOService); let dbSize = 0; - let databases = []; - function setLimit(limit) { if (limit) { SpecialPowers.setIntPref("dom.quotaManager.temporaryStorage.fixedLimit", limit); return; } SpecialPowers.clearUserPref("dom.quotaManager.temporaryStorage.fixedLimit"); } @@ -107,127 +108,127 @@ function testSteps() { storage: "temporary" }); request.onerror = errorHandler; request.onsuccess = grabEventAndContinueHandler; let event = yield undefined; getUsageForUrl(lastUrl, grabUsageAndContinueHandler); dbSize = yield undefined; - setLimit(lastIndex * dbSize / 1024); - quotaManager.clear(); + for (let options of openDBOptions) { + setLimit(lastIndex * dbSize / 1024); + quotaManager.clear(); + + info("Stage 1"); + + let databases = []; + for (let i = 0; i < lastIndex; i++) { + let data = urls[i]; + + info("Opening database for " + data.url); - info("Stage 1"); + request = indexedDB.openForPrincipal(getPrincipal(data.url), name, + options); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.type, "upgradeneeded", "Got correct event type"); + + let db = event.target.result; + db.createObjectStore("foo", { }); + + event = yield undefined; + + is(event.type, "success", "Got correct event type"); - for (let i = 0; i < lastIndex; i++) { - let data = urls[i]; + databases.push(event.target.result); + } + + request = indexedDB.openForPrincipal(getPrincipal(lastUrl), name, options); + request.addEventListener("error", new ExpectError("QuotaExceededError")); + request.onsuccess = unexpectedSuccessHandler; + event = yield undefined; + + checkUsage(1); + yield undefined; + + info("Stage 2"); + + for (let i = 1; i < urls.length; i++) { + databases[i] = null; - info("Opening database for " + data.url); + scheduleGC(); + yield undefined; - request = indexedDB.openForPrincipal(getPrincipal(data.url), name, - { storage: "temporary" }); + // The origin access time is set to the current system time when the first + // database for an origin is registered or the last one is unregistered. + // The registration happens when the database object is being created and + // the unregistration when it is unlinked/garbage collected. + // Some older windows systems have the system time limited to a maximum + // resolution of 10 or 15 milliseconds, so without a pause here we would + // end up with origins with the same access time which would cause random + // failures. + setTimeout(function() { testGenerator.next(); }, 20); + yield undefined; + } + + request = indexedDB.openForPrincipal(getPrincipal(lastUrl), name, options); request.onerror = errorHandler; request.onupgradeneeded = grabEventAndContinueHandler; request.onsuccess = grabEventAndContinueHandler; event = yield undefined; is(event.type, "upgradeneeded", "Got correct event type"); let db = event.target.result; db.createObjectStore("foo", { }); event = yield undefined; is(event.type, "success", "Got correct event type"); - databases.push(event.target.result); - } - - request = indexedDB.openForPrincipal(getPrincipal(lastUrl), name, - { storage: "temporary" }); - request.addEventListener("error", new ExpectError("QuotaExceededError")); - request.onsuccess = unexpectedSuccessHandler; - event = yield undefined; - - checkUsage(1); - yield undefined; - - info("Stage 2"); - - for (let i = 1; i < urls.length; i++) { - databases[i] = null; - - scheduleGC(); + checkUsage(2); yield undefined; - // The origin access time is set to the current system time when the first - // database for an origin is registered or the last one is unregistered. - // The registration happens when the database object is being created and - // the unregistration when it is unlinked/garbage collected. - // Some older windows systems have the system time limited to a maximum - // resolution of 10 or 15 milliseconds, so without a pause here we would - // end up with origins with the same access time which would cause random - // failures. - setTimeout(function() { testGenerator.next(); }, 20); + info("Stage 3"); + + setLimit(14 * dbSize / 1024); + quotaManager.reset(); + + request = indexedDB.openForPrincipal(getPrincipal(lastUrl), name, options); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + is(event.type, "success", "Got correct event type"); + + db = event.target.result; + + checkUsage(3); + yield undefined; + + info("Stage 4"); + + let trans = db.transaction(["foo"], "readwrite"); + + let blob = new Blob(["bar"]); + request = trans.objectStore("foo").add(blob, 42); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + event = yield undefined; + + trans.oncomplete = grabEventAndContinueHandler; + event = yield undefined; + + checkUsage(4); yield undefined; } - request = indexedDB.openForPrincipal(getPrincipal(lastUrl), name, - { storage: "temporary" }); - request.onerror = errorHandler; - request.onupgradeneeded = grabEventAndContinueHandler; - request.onsuccess = grabEventAndContinueHandler; - event = yield undefined; - - is(event.type, "upgradeneeded", "Got correct event type"); - - let db = event.target.result; - db.createObjectStore("foo", { }); - - event = yield undefined; - - is(event.type, "success", "Got correct event type"); - - checkUsage(2); - yield undefined; - - info("Stage 3"); - - setLimit(14 * dbSize / 1024); - quotaManager.reset(); - - request = indexedDB.openForPrincipal(getPrincipal(lastUrl), name, - { storage: "temporary" }); - request.onerror = errorHandler; - request.onsuccess = grabEventAndContinueHandler; - event = yield undefined; - - is(event.type, "success", "Got correct event type"); - - db = event.target.result; - - checkUsage(3); - yield undefined; - - info("Stage 4"); - - let trans = db.transaction(["foo"], "readwrite"); - - let blob = new Blob(["bar"]); - request = trans.objectStore("foo").add(blob, 42); - request.onerror = errorHandler; - request.onsuccess = grabEventAndContinueHandler; - event = yield undefined; - - trans.oncomplete = grabEventAndContinueHandler; - event = yield undefined; - - checkUsage(4); - yield undefined; - info("Cleanup"); setLimit(); quotaManager.reset(); SpecialPowers.setBoolPref("dom.quotaManager.testing", testingEnabled); finishTest();
--- a/dom/quota/OriginCollection.h +++ b/dom/quota/OriginCollection.h @@ -24,18 +24,19 @@ public: { return mPatterns.Contains(aPattern); } void AddPattern(const nsACString& aPattern) { MOZ_ASSERT(!mOrigins.Count()); - MOZ_ASSERT(!ContainsPattern(aPattern)); - mPatterns.AppendElement(aPattern); + if (!ContainsPattern(aPattern)) { + mPatterns.AppendElement(aPattern); + } } bool ContainsOrigin(const nsACString& aOrigin) { for (uint32_t index = 0; index < mPatterns.Length(); index++) { if (PatternMatchesOrigin(mPatterns[index], aOrigin)) { return true; @@ -43,18 +44,19 @@ public: } return mOrigins.GetEntry(aOrigin); } void AddOrigin(const nsACString& aOrigin) { - MOZ_ASSERT(!ContainsOrigin(aOrigin)); - mOrigins.PutEntry(aOrigin); + if (!ContainsOrigin(aOrigin)) { + mOrigins.PutEntry(aOrigin); + } } private: nsTArray<nsCString> mPatterns; nsTHashtable<nsCStringHashKey> mOrigins; }; END_QUOTA_NAMESPACE
--- a/dom/quota/PersistenceType.h +++ b/dom/quota/PersistenceType.h @@ -77,21 +77,31 @@ NullablePersistenceTypeFromText(const ns inline mozilla::dom::StorageType PersistenceTypeToStorage(PersistenceType aPersistenceType) { return mozilla::dom::StorageType(static_cast<int>(aPersistenceType)); } inline PersistenceType -PersistenceTypeFromStorage(const Optional<mozilla::dom::StorageType>& aStorage, - PersistenceType aDefaultPersistenceType) +PersistenceTypeFromStorage(const Optional<mozilla::dom::StorageType>& aStorage) { if (aStorage.WasPassed()) { return PersistenceType(static_cast<int>(aStorage.Value())); } - return aDefaultPersistenceType; + return PERSISTENCE_TYPE_PERSISTENT; +} + +inline PersistenceType +ComplementaryPersistenceType(PersistenceType aPersistenceType) +{ + if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) { + return PERSISTENCE_TYPE_TEMPORARY; + } + + MOZ_ASSERT(aPersistenceType == PERSISTENCE_TYPE_TEMPORARY); + return PERSISTENCE_TYPE_PERSISTENT; } END_QUOTA_NAMESPACE #endif // mozilla_dom_quota_persistencetype_h__
--- a/dom/quota/QuotaManager.cpp +++ b/dom/quota/QuotaManager.cpp @@ -7,16 +7,17 @@ #include "QuotaManager.h" #include "mozIApplicationClearPrivateDataParams.h" #include "nsIBinaryInputStream.h" #include "nsIBinaryOutputStream.h" #include "nsIFile.h" #include "nsIObserverService.h" #include "nsIOfflineStorage.h" +#include "nsIPermissionManager.h" #include "nsIPrincipal.h" #include "nsIQuotaRequest.h" #include "nsIRunnable.h" #include "nsISimpleEnumerator.h" #include "nsIScriptObjectPrincipal.h" #include "nsIScriptSecurityManager.h" #include "nsITimer.h" #include "nsIURI.h" @@ -36,16 +37,17 @@ #include "mozilla/Services.h" #include "nsAppDirectoryServiceDefs.h" #include "nsComponentManagerUtils.h" #include "nsAboutProtocolUtils.h" #include "nsContentUtils.h" #include "nsCRTGlue.h" #include "nsDirectoryServiceUtils.h" #include "nsNetUtil.h" +#include "nsPrintfCString.h" #include "nsScriptSecurityManager.h" #include "nsThreadUtils.h" #include "nsXULAppAPI.h" #include "xpcpublic.h" #include "AcquireListener.h" #include "CheckQuotaHelper.h" #include "OriginCollection.h" @@ -83,16 +85,17 @@ #define PERMISSION_DEFAUT_PERSISTENT_STORAGE "default-persistent-storage" #define KB * 1024ULL #define MB * 1024ULL KB #define GB * 1024ULL MB USING_QUOTA_NAMESPACE +using namespace mozilla; using namespace mozilla::dom; using mozilla::dom::FileService; static_assert( static_cast<uint32_t>(StorageType::Persistent) == static_cast<uint32_t>(PERSISTENCE_TYPE_PERSISTENT), "Enum values should match."); @@ -272,17 +275,18 @@ class AsyncUsageRunnable MOZ_FINAL : pub }; public: NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIQUOTAREQUEST AsyncUsageRunnable(uint32_t aAppId, bool aInMozBrowserOnly, - const nsACString& aGroup, + const nsACString& aPersistentGroup, + const nsACString& aTemporaryGroup, const OriginOrPatternString& aOrigin, nsIURI* aURI, nsIUsageCallback* aCallback); NS_IMETHOD Run(); void @@ -316,17 +320,18 @@ private: nsresult AddToUsage(QuotaManager* aQuotaManager, PersistenceType aPersistenceType); nsCOMPtr<nsIURI> mURI; nsCOMPtr<nsIUsageCallback> mCallback; uint32_t mAppId; - nsCString mGroup; + nsCString mPersistentGroup; + nsCString mTemporaryGroup; OriginOrPatternString mOrigin; CallbackState mCallbackState; bool mInMozBrowserOnly; }; class ResetOrClearRunnable MOZ_FINAL : public nsRunnable, public AcquireListener { @@ -412,17 +417,17 @@ class FinalizeOriginEvictionRunnable MOZ // Running on the IO thread. IO, // Running on the main thread after IO work is done. Complete }; public: - explicit FinalizeOriginEvictionRunnable(nsTArray<nsCString>& aOrigins) + explicit FinalizeOriginEvictionRunnable(nsTArray<OriginParams>& aOrigins) : mCallbackState(Pending) { mOrigins.SwapElements(aOrigins); } NS_IMETHOD Run(); @@ -447,17 +452,17 @@ public: nsresult Dispatch(); nsresult RunImmediately(); private: CallbackState mCallbackState; - nsTArray<nsCString> mOrigins; + nsTArray<OriginParams> mOrigins; }; bool IsOnIOThread() { QuotaManager* quotaManager = QuotaManager::Get(); NS_ASSERTION(quotaManager, "Must have a manager here!"); @@ -552,46 +557,53 @@ public: private: bool mBusy; }; class SaveOriginAccessTimeRunnable MOZ_FINAL : public nsRunnable { public: - SaveOriginAccessTimeRunnable(const nsACString& aOrigin, int64_t aTimestamp) - : mOrigin(aOrigin), mTimestamp(aTimestamp) + SaveOriginAccessTimeRunnable(PersistenceType aPersistenceType, + const nsACString& aOrigin, + int64_t aTimestamp) + : mPersistenceType(aPersistenceType), mOrigin(aOrigin), mTimestamp(aTimestamp) { } NS_IMETHOD Run(); private: + PersistenceType mPersistenceType; nsCString mOrigin; int64_t mTimestamp; }; struct MOZ_STACK_CLASS RemoveQuotaInfo { RemoveQuotaInfo(PersistenceType aPersistenceType, const nsACString& aPattern) : persistenceType(aPersistenceType), pattern(aPattern) { } PersistenceType persistenceType; nsCString pattern; }; struct MOZ_STACK_CLASS InactiveOriginsInfo { - InactiveOriginsInfo(OriginCollection& aCollection, + InactiveOriginsInfo(OriginCollection& aPersistentCollection, + OriginCollection& aTemporaryCollection, nsTArray<OriginInfo*>& aOrigins) - : collection(aCollection), origins(aOrigins) + : persistentCollection(aPersistentCollection), + temporaryCollection(aTemporaryCollection), + origins(aOrigins) { } - OriginCollection& collection; + OriginCollection& persistentCollection; + OriginCollection& temporaryCollection; nsTArray<OriginInfo*>& origins; }; bool IsMainProcess() { return XRE_GetProcessType() == GeckoProcessType_Default; } @@ -633,36 +645,18 @@ EnsureDirectory(nsIFile* aDirectory, boo *aCreated = true; } return NS_OK; } nsresult -CreateDirectoryUpgradeStamp(nsIFile* aDirectory) -{ - AssertIsOnIOThread(); - - nsCOMPtr<nsIFile> metadataFile; - nsresult rv = aDirectory->Clone(getter_AddRefs(metadataFile)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = metadataFile->Append(NS_LITERAL_STRING(METADATA_FILE_NAME)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = metadataFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -GetDirectoryMetadataStream(nsIFile* aDirectory, bool aUpdate, - nsIBinaryOutputStream** aStream) +GetDirectoryMetadataOutputStream(nsIFile* aDirectory, bool aUpdate, + nsIBinaryOutputStream** aStream) { AssertIsOnIOThread(); nsCOMPtr<nsIFile> metadataFile; nsresult rv = aDirectory->Clone(getter_AddRefs(metadataFile)); NS_ENSURE_SUCCESS(rv, rv); rv = metadataFile->Append(NS_LITERAL_STRING(METADATA_FILE_NAME)); @@ -706,17 +700,17 @@ GetDirectoryMetadataStream(nsIFile* aDir nsresult CreateDirectoryMetadata(nsIFile* aDirectory, int64_t aTimestamp, const nsACString& aGroup, const nsACString& aOrigin) { AssertIsOnIOThread(); nsCOMPtr<nsIBinaryOutputStream> stream; nsresult rv = - GetDirectoryMetadataStream(aDirectory, false, getter_AddRefs(stream)); + GetDirectoryMetadataOutputStream(aDirectory, false, getter_AddRefs(stream)); NS_ENSURE_SUCCESS(rv, rv); NS_ASSERTION(stream, "This shouldn't be null!"); rv = stream->Write64(aTimestamp); NS_ENSURE_SUCCESS(rv, rv); rv = stream->WriteStringZ(PromiseFlatCString(aGroup).get()); @@ -724,43 +718,94 @@ CreateDirectoryMetadata(nsIFile* aDirect rv = stream->WriteStringZ(PromiseFlatCString(aOrigin).get()); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } nsresult -GetDirectoryMetadata(nsIFile* aDirectory, int64_t* aTimestamp, - nsACString& aGroup, nsACString& aOrigin) +CreateDirectoryMetadataLastModifiedTime(nsIFile* aDirectory, int64_t aTimestamp) { AssertIsOnIOThread(); + nsCOMPtr<nsIBinaryOutputStream> stream; + nsresult rv = + GetDirectoryMetadataOutputStream(aDirectory, false, getter_AddRefs(stream)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT(stream); + + rv = stream->Write64(aTimestamp); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +GetDirectoryMetadataInputStream(nsIFile* aDirectory, + bool aClone, + nsIBinaryInputStream** aStream) +{ + AssertIsOnIOThread(); + MOZ_ASSERT(aDirectory); + MOZ_ASSERT(aStream); + + nsresult rv; + nsCOMPtr<nsIFile> metadataFile; - nsresult rv = aDirectory->Clone(getter_AddRefs(metadataFile)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = metadataFile->Append(NS_LITERAL_STRING(METADATA_FILE_NAME)); - NS_ENSURE_SUCCESS(rv, rv); + + if (aClone) { + rv = aDirectory->Clone(getter_AddRefs(metadataFile)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = metadataFile->Append(NS_LITERAL_STRING(METADATA_FILE_NAME)); + NS_ENSURE_SUCCESS(rv, rv); + } else { + metadataFile = aDirectory; + } nsCOMPtr<nsIInputStream> stream; rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), metadataFile); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsIInputStream> bufferedStream; rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream), stream, 512); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsIBinaryInputStream> binaryStream = do_CreateInstance("@mozilla.org/binaryinputstream;1"); NS_ENSURE_TRUE(binaryStream, NS_ERROR_FAILURE); rv = binaryStream->SetInputStream(bufferedStream); NS_ENSURE_SUCCESS(rv, rv); + binaryStream.forget(aStream); + return NS_OK; +} + +nsresult +GetDirectoryMetadata(nsIFile* aDirectory, + int64_t* aTimestamp, + nsACString& aGroup, + nsACString& aOrigin) +{ + AssertIsOnIOThread(); + MOZ_ASSERT(aDirectory); + MOZ_ASSERT(aTimestamp); + + nsCOMPtr<nsIBinaryInputStream> binaryStream; + nsresult rv = GetDirectoryMetadataInputStream(aDirectory, true, + getter_AddRefs(binaryStream)); + NS_ENSURE_SUCCESS(rv, rv); + uint64_t timestamp; rv = binaryStream->Read64(×tamp); NS_ENSURE_SUCCESS(rv, rv); nsCString group; rv = binaryStream->ReadCString(group); NS_ENSURE_SUCCESS(rv, rv); @@ -770,16 +815,113 @@ GetDirectoryMetadata(nsIFile* aDirectory *aTimestamp = timestamp; aGroup = group; aOrigin = origin; return NS_OK; } nsresult +GetDirectoryMetadataLastModifiedTime(nsIFile* aDirectory, int64_t* aTimestamp) +{ + AssertIsOnIOThread(); + MOZ_ASSERT(aDirectory); + MOZ_ASSERT(aTimestamp); + + nsresult rv; + +#ifdef DEBUG + nsCOMPtr<nsIFile> parentDirectory; + rv = aDirectory->GetParent(getter_AddRefs(parentDirectory)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsString path; + rv = parentDirectory->GetPath(path); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + QuotaManager* quotaManager = QuotaManager::Get(); + MOZ_ASSERT(quotaManager); + + MOZ_ASSERT(path == quotaManager->GetStoragePath(PERSISTENCE_TYPE_PERSISTENT)); +#endif + + nsCOMPtr<nsIFile> metadataFile; + rv = aDirectory->Clone(getter_AddRefs(metadataFile)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = metadataFile->Append(NS_LITERAL_STRING(METADATA_FILE_NAME)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + bool exists; + rv = metadataFile->Exists(&exists); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + if (NS_WARN_IF(!exists)) { + return NS_ERROR_UNEXPECTED; + } + + bool isDirectory; + rv = metadataFile->IsDirectory(&isDirectory); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + if (NS_WARN_IF(isDirectory)) { + return NS_ERROR_UNEXPECTED; + } + + int64_t fileSize; + rv = metadataFile->GetFileSize(&fileSize); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // XXX Since we don't have regular metadata files for persistent storage yet, + // we have to use a temporary hack. + // Load origin access time from the metadata file, if we alredy saved it + // to it. If the file size doesn't match a metadata file with saved origin + // access time, get the last modiefied time of the metadata file and use + // it as origin access time. + if (fileSize == sizeof(uint64_t)) { + nsCOMPtr<nsIBinaryInputStream> binaryStream; + rv = GetDirectoryMetadataInputStream(metadataFile, false, + getter_AddRefs(binaryStream)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + uint64_t timestamp; + rv = binaryStream->Read64(×tamp); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + *aTimestamp = timestamp; + return NS_OK; + } + + int64_t timestamp; + rv = metadataFile->GetLastModifiedTime(×tamp); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + *aTimestamp = timestamp; + return NS_OK; +} + +nsresult MaybeUpgradeOriginDirectory(nsIFile* aDirectory) { AssertIsOnIOThread(); NS_ASSERTION(aDirectory, "Null pointer!"); nsCOMPtr<nsIFile> metadataFile; nsresult rv = aDirectory->Clone(getter_AddRefs(metadataFile)); NS_ENSURE_SUCCESS(rv, rv); @@ -1065,33 +1207,33 @@ void QuotaManager::InitQuotaForOrigin(PersistenceType aPersistenceType, const nsACString& aGroup, const nsACString& aOrigin, uint64_t aLimitBytes, uint64_t aUsageBytes, int64_t aAccessTime) { AssertIsOnIOThread(); - MOZ_ASSERT(aLimitBytes > 0 || - aPersistenceType == PERSISTENCE_TYPE_TEMPORARY); - MOZ_ASSERT(aUsageBytes <= aLimitBytes || - aPersistenceType == PERSISTENCE_TYPE_TEMPORARY); + MOZ_ASSERT_IF(IsTreatedAsPersistent(aPersistenceType, aOrigin), + aLimitBytes > 0); + MOZ_ASSERT_IF(IsTreatedAsPersistent(aPersistenceType, aOrigin), + aUsageBytes <= aLimitBytes); MutexAutoLock lock(mQuotaMutex); GroupInfoPair* pair; if (!mGroupInfoPairs.Get(aGroup, &pair)) { pair = new GroupInfoPair(); mGroupInfoPairs.Put(aGroup, pair); // The hashtable is now responsible to delete the GroupInfoPair. } nsRefPtr<GroupInfo> groupInfo = pair->LockedGetGroupInfo(aPersistenceType); if (!groupInfo) { - groupInfo = new GroupInfo(aPersistenceType, aGroup); + groupInfo = new GroupInfo(pair, aPersistenceType, aGroup); pair->LockedSetGroupInfo(groupInfo); } nsRefPtr<OriginInfo> originInfo = new OriginInfo(groupInfo, aOrigin, aLimitBytes, aUsageBytes, aAccessTime); groupInfo->LockedAddOriginInfo(originInfo); } @@ -1130,48 +1272,48 @@ QuotaManager::UpdateOriginAccessTime(Per MutexAutoLock lock(mQuotaMutex); GroupInfoPair* pair; if (!mGroupInfoPairs.Get(aGroup, &pair)) { return; } - nsRefPtr<GroupInfo> groupInfo = - pair->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY); + nsRefPtr<GroupInfo> groupInfo = pair->LockedGetGroupInfo(aPersistenceType); if (!groupInfo) { return; } nsRefPtr<OriginInfo> originInfo = groupInfo->LockedGetOriginInfo(aOrigin); if (originInfo) { int64_t timestamp = PR_Now(); originInfo->LockedUpdateAccessTime(timestamp); - if (!groupInfo->IsForTemporaryStorage()) { - return; - } - MutexAutoUnlock autoUnlock(mQuotaMutex); - SaveOriginAccessTime(aOrigin, timestamp); + SaveOriginAccessTime(aPersistenceType, aOrigin, timestamp); } } // static PLDHashOperator QuotaManager::RemoveQuotaCallback(const nsACString& aKey, nsAutoPtr<GroupInfoPair>& aValue, void* aUserArg) { NS_ASSERTION(!aKey.IsEmpty(), "Empty key!"); NS_ASSERTION(aValue, "Null pointer!"); nsRefPtr<GroupInfo> groupInfo = - aValue->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY); + aValue->LockedGetGroupInfo(PERSISTENCE_TYPE_PERSISTENT); + if (groupInfo) { + groupInfo->LockedRemoveOriginInfos(); + } + + groupInfo = aValue->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY); if (groupInfo) { groupInfo->LockedRemoveOriginInfos(); } return PL_DHASH_REMOVE; } void @@ -1181,50 +1323,54 @@ QuotaManager::RemoveQuota() mGroupInfoPairs.Enumerate(RemoveQuotaCallback, nullptr); NS_ASSERTION(mTemporaryStorageUsage == 0, "Should be zero!"); } // static PLDHashOperator -QuotaManager::RemoveQuotaForPersistenceTypeCallback( +QuotaManager::RemoveQuotaForTemporaryStorageCallback( const nsACString& aKey, nsAutoPtr<GroupInfoPair>& aValue, void* aUserArg) { NS_ASSERTION(!aKey.IsEmpty(), "Empty key!"); NS_ASSERTION(aValue, "Null pointer!"); - NS_ASSERTION(aUserArg, "Null pointer!"); - - PersistenceType& persistenceType = *static_cast<PersistenceType*>(aUserArg); - - if (persistenceType == PERSISTENCE_TYPE_TEMPORARY) { - nsRefPtr<GroupInfo> groupInfo = - aValue->LockedGetGroupInfo(persistenceType); - if (groupInfo) { - groupInfo->LockedRemoveOriginInfos(); + + nsRefPtr<GroupInfo> groupInfo = + aValue->LockedGetGroupInfo(PERSISTENCE_TYPE_PERSISTENT); + if (groupInfo) { + groupInfo->LockedRemoveTemporaryOriginInfos(); + + if (!groupInfo->LockedHasOriginInfos()) { + aValue->LockedClearGroupInfo(PERSISTENCE_TYPE_PERSISTENT); } } - aValue->LockedClearGroupInfo(persistenceType); + groupInfo = aValue->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY); + if (groupInfo) { + groupInfo->LockedRemoveTemporaryOriginInfos(); + + if (!groupInfo->LockedHasOriginInfos()) { + aValue->LockedClearGroupInfo(PERSISTENCE_TYPE_TEMPORARY); + } + } return aValue->LockedHasGroupInfos() ? PL_DHASH_NEXT : PL_DHASH_REMOVE; } void -QuotaManager::RemoveQuotaForPersistenceType(PersistenceType aPersistenceType) +QuotaManager::RemoveQuotaForTemporaryStorage() { MutexAutoLock lock(mQuotaMutex); - mGroupInfoPairs.Enumerate(RemoveQuotaForPersistenceTypeCallback, - &aPersistenceType); - - NS_ASSERTION(aPersistenceType == PERSISTENCE_TYPE_PERSISTENT || - mTemporaryStorageUsage == 0, "Should be zero!"); + mGroupInfoPairs.Enumerate(RemoveQuotaForTemporaryStorageCallback, nullptr); + + NS_ASSERTION(mTemporaryStorageUsage == 0, "Should be zero!"); } // static PLDHashOperator QuotaManager::RemoveQuotaForPatternCallback(const nsACString& aKey, nsAutoPtr<GroupInfoPair>& aValue, void* aUserArg) { @@ -1362,44 +1508,63 @@ QuotaManager::RegisterStorage(nsIOffline } // Add this storage to its origin info if it exists, create it otherwise. const nsACString& origin = aStorage->Origin(); ArrayCluster<nsIOfflineStorage*>* cluster; if (!mLiveStorages.Get(origin, &cluster)) { cluster = new ArrayCluster<nsIOfflineStorage*>(); mLiveStorages.Put(origin, cluster); + } + (*cluster)[aStorage->GetClient()->GetType()].AppendElement(aStorage); + + LiveStorageTable& liveStorageTable = GetLiveStorageTable(aStorage->Type()); + + nsTArray<nsIOfflineStorage*>* array; + if (!liveStorageTable.Get(origin, &array)) { + array = new nsTArray<nsIOfflineStorage*>(); + liveStorageTable.Put(origin, array); UpdateOriginAccessTime(aStorage->Type(), aStorage->Group(), origin); } - (*cluster)[aStorage->GetClient()->GetType()].AppendElement(aStorage); + array->AppendElement(aStorage); return true; } void QuotaManager::UnregisterStorage(nsIOfflineStorage* aStorage) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); NS_ASSERTION(aStorage, "Null pointer!"); // Remove this storage from its origin array, maybe remove the array if it // is then empty. const nsACString& origin = aStorage->Origin(); + ArrayCluster<nsIOfflineStorage*>* cluster; - if (mLiveStorages.Get(origin, &cluster) && - (*cluster)[aStorage->GetClient()->GetType()].RemoveElement(aStorage)) { - if (cluster->IsEmpty()) { - mLiveStorages.Remove(origin); - - UpdateOriginAccessTime(aStorage->Type(), aStorage->Group(), origin); - } - return; + MOZ_ALWAYS_TRUE(mLiveStorages.Get(origin, &cluster)); + + MOZ_ALWAYS_TRUE( + (*cluster)[aStorage->GetClient()->GetType()].RemoveElement(aStorage)); + if (cluster->IsEmpty()) { + mLiveStorages.Remove(origin); } - NS_ERROR("Didn't know anything about this storage!"); + + LiveStorageTable& liveStorageTable = GetLiveStorageTable(aStorage->Type()); + + nsTArray<nsIOfflineStorage*>* array; + MOZ_ALWAYS_TRUE(liveStorageTable.Get(origin, &array)); + + MOZ_ALWAYS_TRUE(array->RemoveElement(aStorage)); + if (array->IsEmpty()) { + liveStorageTable.Remove(origin); + + UpdateOriginAccessTime(aStorage->Type(), aStorage->Group(), origin); + } } void QuotaManager::OnStorageClosed(nsIOfflineStorage* aStorage) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); NS_ASSERTION(aStorage, "Null pointer!"); @@ -1651,37 +1816,141 @@ QuotaManager::GetDirectoryForOrigin(Pers rv = directory->Append(NS_ConvertASCIItoUTF16(originSanitized)); NS_ENSURE_SUCCESS(rv, rv); directory.forget(aDirectory); return NS_OK; } nsresult +QuotaManager::InitializeRepository(PersistenceType aPersistenceType) +{ + nsresult rv; + + nsCOMPtr<nsIFile> directory = + do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = directory->InitWithPath(GetStoragePath(aPersistenceType)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + bool created; + rv = EnsureDirectory(directory, &created); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsCOMPtr<nsISimpleEnumerator> entries; + rv = directory->GetDirectoryEntries(getter_AddRefs(entries)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + bool hasMore; + while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) { + nsCOMPtr<nsISupports> entry; + rv = entries->GetNext(getter_AddRefs(entry)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsCOMPtr<nsIFile> childDirectory = do_QueryInterface(entry); + MOZ_ASSERT(childDirectory); + + nsString leafName; + rv = childDirectory->GetLeafName(leafName); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + bool isDirectory; + rv = childDirectory->IsDirectory(&isDirectory); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (!isDirectory) { + if (leafName.EqualsLiteral(DSSTORE_FILE_NAME)) { + continue; + } + + nsPrintfCString message("Something (%s) in the repository that doesn't " + "belong!", NS_ConvertUTF16toUTF8(leafName).get()); + NS_WARNING(message.get()); + return NS_ERROR_UNEXPECTED; + } + + int64_t timestamp; + nsCString group; + nsCString origin; + + // XXX This is temporary, we don't have real .metadata files for persistent + // storage yet. + if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) { + rv = GetDirectoryMetadataLastModifiedTime(childDirectory, ×tamp); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + origin = NS_ConvertUTF16toUTF8(leafName); + group = origin; + } else { + rv = GetDirectoryMetadata(childDirectory, ×tamp, group, origin); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + SanitizeOriginString(origin); + } + + if (IsTreatedAsPersistent(aPersistenceType, origin)) { + continue; + } + + rv = InitializeOrigin(aPersistenceType, group, origin, false, timestamp, + childDirectory); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult QuotaManager::InitializeOrigin(PersistenceType aPersistenceType, const nsACString& aGroup, const nsACString& aOrigin, - bool aTrackQuota, + bool aHasUnlimStoragePerm, int64_t aAccessTime, nsIFile* aDirectory) { AssertIsOnIOThread(); nsresult rv; - bool temporaryStorage = aPersistenceType == PERSISTENCE_TYPE_TEMPORARY; - if (!temporaryStorage) { + if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) { rv = MaybeUpgradeOriginDirectory(aDirectory); NS_ENSURE_SUCCESS(rv, rv); } + bool trackQuota = + IsQuotaEnforced(aPersistenceType, aOrigin, aHasUnlimStoragePerm); + // We need to initialize directories of all clients if they exists and also // get the total usage to initialize the quota. nsAutoPtr<UsageInfo> usageInfo; - if (aTrackQuota) { + if (trackQuota) { usageInfo = new UsageInfo(); } nsCOMPtr<nsISimpleEnumerator> entries; rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries)); NS_ENSURE_SUCCESS(rv, rv); bool hasMore; @@ -1718,21 +1987,21 @@ QuotaManager::InitializeOrigin(Persisten return NS_ERROR_UNEXPECTED; } rv = mClients[clientType]->InitOrigin(aPersistenceType, aGroup, aOrigin, usageInfo); NS_ENSURE_SUCCESS(rv, rv); } - if (aTrackQuota) { + if (trackQuota) { uint64_t quotaMaxBytes; uint64_t totalUsageBytes = usageInfo->TotalUsage(); - if (temporaryStorage) { + if (IsTreatedAsTemporary(aPersistenceType, aOrigin)) { // Temporary storage has no limit for origin usage (there's a group and // the global limit though). quotaMaxBytes = 0; } else { quotaMaxBytes = GetStorageQuotaMB() * 1024 * 1024; if (totalUsageBytes > quotaMaxBytes) { NS_WARNING("Origin is already using more storage than allowed!"); @@ -1821,153 +2090,160 @@ QuotaManager::MaybeUpgradeIndexedDBDirec return NS_OK; } nsresult QuotaManager::EnsureOriginIsInitialized(PersistenceType aPersistenceType, const nsACString& aGroup, const nsACString& aOrigin, - bool aTrackQuota, + bool aHasUnlimStoragePerm, nsIFile** aDirectory) { AssertIsOnIOThread(); nsresult rv = MaybeUpgradeIndexedDBDirectory(); NS_ENSURE_SUCCESS(rv, rv); // Get directory for this origin and persistence type. nsCOMPtr<nsIFile> directory; rv = GetDirectoryForOrigin(aPersistenceType, aOrigin, getter_AddRefs(directory)); NS_ENSURE_SUCCESS(rv, rv); - if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) { + if (IsTreatedAsPersistent(aPersistenceType, aOrigin)) { if (mInitializedOrigins.Contains(aOrigin)) { NS_ADDREF(*aDirectory = directory); return NS_OK; } - - bool created; - rv = EnsureDirectory(directory, &created); - NS_ENSURE_SUCCESS(rv, rv); - - if (created) { - rv = CreateDirectoryUpgradeStamp(directory); - NS_ENSURE_SUCCESS(rv, rv); + } else if (!mTemporaryStorageInitialized) { + rv = InitializeRepository(aPersistenceType); + if (NS_WARN_IF(NS_FAILED(rv))) { + // We have to cleanup partially initialized quota for temporary storage. + RemoveQuotaForTemporaryStorage(); + + return rv; } - rv = InitializeOrigin(aPersistenceType, aGroup, aOrigin, aTrackQuota, 0, - directory); - NS_ENSURE_SUCCESS(rv, rv); - - mInitializedOrigins.AppendElement(aOrigin); - - directory.forget(aDirectory); - return NS_OK; - } - - NS_ASSERTION(aPersistenceType == PERSISTENCE_TYPE_TEMPORARY, "Huh?"); - NS_ASSERTION(aTrackQuota, "Huh?"); - - if (!mTemporaryStorageInitialized) { - nsCOMPtr<nsIFile> parentDirectory; - rv = directory->GetParent(getter_AddRefs(parentDirectory)); - NS_ENSURE_SUCCESS(rv, rv); - - bool created; - rv = EnsureDirectory(parentDirectory, &created); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr<nsISimpleEnumerator> entries; - rv = parentDirectory->GetDirectoryEntries(getter_AddRefs(entries)); - NS_ENSURE_SUCCESS(rv, rv); - - bool hasMore; - while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) { - nsCOMPtr<nsISupports> entry; - rv = entries->GetNext(getter_AddRefs(entry)); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr<nsIFile> childDirectory = do_QueryInterface(entry); - NS_ENSURE_TRUE(childDirectory, NS_NOINTERFACE); - - bool isDirectory; - rv = childDirectory->IsDirectory(&isDirectory); - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_TRUE(isDirectory, NS_ERROR_UNEXPECTED); - - int64_t timestamp; - nsCString group; - nsCString origin; - rv = GetDirectoryMetadata(childDirectory, ×tamp, group, origin); - NS_ENSURE_SUCCESS(rv, rv); - - rv = InitializeOrigin(aPersistenceType, group, origin, aTrackQuota, - timestamp, childDirectory); - if (NS_FAILED(rv)) { - NS_WARNING("Failed to initialize origin!"); - - // We have to cleanup partially initialized quota for temporary storage. - RemoveQuotaForPersistenceType(aPersistenceType); - - return rv; - } + rv = InitializeRepository(ComplementaryPersistenceType(aPersistenceType)); + if (NS_WARN_IF(NS_FAILED(rv))) { + // We have to cleanup partially initialized quota for temporary storage. + RemoveQuotaForTemporaryStorage(); + + return rv; } if (gFixedLimitKB >= 0) { mTemporaryStorageLimit = gFixedLimitKB * 1024; } else { - rv = GetTemporaryStorageLimit(parentDirectory, mTemporaryStorageUsage, + nsCOMPtr<nsIFile> storageDir = + do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = storageDir->InitWithPath(GetStoragePath()); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = GetTemporaryStorageLimit(storageDir, mTemporaryStorageUsage, &mTemporaryStorageLimit); NS_ENSURE_SUCCESS(rv, rv); } mTemporaryStorageInitialized = true; CheckTemporaryStorageLimits(); } + nsCOMPtr<nsIFile> parentDirectory; + rv = directory->GetParent(getter_AddRefs(parentDirectory)); + NS_ENSURE_SUCCESS(rv, rv); + bool created; + rv = EnsureDirectory(parentDirectory, &created); + NS_ENSURE_SUCCESS(rv, rv); + + int64_t timestamp; + rv = EnsureDirectory(directory, &created); NS_ENSURE_SUCCESS(rv, rv); - if (created) { - int64_t timestamp = PR_Now(); - - rv = CreateDirectoryMetadata(directory, timestamp, aGroup, aOrigin); + if (IsTreatedAsPersistent(aPersistenceType, aOrigin)) { + if (created) { + timestamp = PR_Now(); + + if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) { + rv = CreateDirectoryMetadataLastModifiedTime(directory, timestamp); + } else { + rv = CreateDirectoryMetadata(directory, timestamp, aGroup, aOrigin); + } + NS_ENSURE_SUCCESS(rv, rv); + } else { + if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) { + rv = GetDirectoryMetadataLastModifiedTime(directory, ×tamp); + NS_ENSURE_SUCCESS(rv, rv); + } else { + nsCOMPtr<nsIBinaryInputStream> stream; + rv = GetDirectoryMetadataInputStream(directory, true, + getter_AddRefs(stream)); + NS_ENSURE_SUCCESS(rv, rv); + + uint64_t ts; + rv = stream->Read64(&ts); + NS_ENSURE_SUCCESS(rv, rv); + + timestamp = ts; + } + } + + rv = InitializeOrigin(aPersistenceType, aGroup, aOrigin, + aHasUnlimStoragePerm, timestamp, directory); NS_ENSURE_SUCCESS(rv, rv); - rv = InitializeOrigin(aPersistenceType, aGroup, aOrigin, aTrackQuota, - timestamp, directory); + mInitializedOrigins.AppendElement(aOrigin); + } else if (created) { + timestamp = PR_Now(); + + if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) { + rv = CreateDirectoryMetadataLastModifiedTime(directory, timestamp); + } else { + rv = CreateDirectoryMetadata(directory, timestamp, aGroup, aOrigin); + } + NS_ENSURE_SUCCESS(rv, rv); + + rv = InitializeOrigin(aPersistenceType, aGroup, aOrigin, + aHasUnlimStoragePerm, timestamp, directory); NS_ENSURE_SUCCESS(rv, rv); } directory.forget(aDirectory); return NS_OK; } void QuotaManager::OriginClearCompleted( PersistenceType aPersistenceType, const OriginOrPatternString& aOriginOrPattern) { AssertIsOnIOThread(); - if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) { - if (aOriginOrPattern.IsOrigin()) { + if (aOriginOrPattern.IsOrigin()) { + if (IsTreatedAsPersistent(aPersistenceType, aOriginOrPattern)) { mInitializedOrigins.RemoveElement(aOriginOrPattern); } - else { - for (uint32_t index = mInitializedOrigins.Length(); index > 0; index--) { - if (PatternMatchesOrigin(aOriginOrPattern, - mInitializedOrigins[index - 1])) { - mInitializedOrigins.RemoveElementAt(index - 1); - } + } + else { + for (uint32_t index = mInitializedOrigins.Length(); index > 0; index--) { + nsCString& initializedOrigin = mInitializedOrigins[index - 1]; + if (PatternMatchesOrigin(aOriginOrPattern, initializedOrigin) && + IsTreatedAsPersistent(aPersistenceType, aOriginOrPattern)) { + mInitializedOrigins.RemoveElementAt(index - 1); } } } for (uint32_t index = 0; index < Client::TYPE_MAX; index++) { mClients[index]->OnOriginClearCompleted(aPersistenceType, aOriginOrPattern); } } @@ -2034,45 +2310,46 @@ QuotaManager::GetStorageId(PersistenceTy aDatabaseId = str; } // static nsresult QuotaManager::GetInfoFromURI(nsIURI* aURI, uint32_t aAppId, bool aInMozBrowser, + PersistenceType aPersistenceType, nsACString* aGroup, nsACString* aOrigin, - StoragePrivilege* aPrivilege, - PersistenceType* aDefaultPersistenceType) + bool* aIsApp, + bool* aHasUnlimStoragePerm) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(aURI); nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager(); NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE); nsCOMPtr<nsIPrincipal> principal; nsresult rv = secMan->GetAppCodebasePrincipal(aURI, aAppId, aInMozBrowser, getter_AddRefs(principal)); NS_ENSURE_SUCCESS(rv, rv); - rv = GetInfoFromPrincipal(principal, aGroup, aOrigin, aPrivilege, - aDefaultPersistenceType); + rv = GetInfoFromPrincipal(principal, aPersistenceType, aGroup, aOrigin, + aIsApp, aHasUnlimStoragePerm); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } static nsresult TryGetInfoForAboutURI(nsIPrincipal* aPrincipal, nsACString& aGroup, nsACString& aASCIIOrigin, - StoragePrivilege* aPrivilege, - PersistenceType* aDefaultPersistenceType) + bool* aIsApp, + bool* aHasUnlimStoragePerm) { NS_ASSERTION(aPrincipal, "Don't hand me a null principal!"); nsCOMPtr<nsIURI> uri; nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri)); NS_ENSURE_SUCCESS(rv, rv); if (!uri) { return NS_ERROR_NOT_AVAILABLE; @@ -2108,48 +2385,58 @@ TryGetInfoForAboutURI(nsIPrincipal* aPri origin = scheme + NS_LITERAL_CSTRING(":") + NS_ConvertUTF16toUTF8(postfix); } ToLowerCase(origin); aGroup.Assign(origin); aASCIIOrigin.Assign(origin); - if (aPrivilege) { - *aPrivilege = Content; + if (aIsApp) { + *aIsApp = false; + } + + if (aHasUnlimStoragePerm) { + *aHasUnlimStoragePerm = false; } - if (aDefaultPersistenceType) { - *aDefaultPersistenceType = PERSISTENCE_TYPE_PERSISTENT; - } + // XXX This is temporary, we don't have regular .metadata files for persistent + // storage yet, so we have to use sanitized origin strings and create + // separate group for each origin. + nsCString originSanitized(aASCIIOrigin); + SanitizeOriginString(originSanitized); + aASCIIOrigin = originSanitized; + + // XXX The group is already the same as the origin. return NS_OK; } // static nsresult QuotaManager::GetInfoFromPrincipal(nsIPrincipal* aPrincipal, + PersistenceType aPersistenceType, nsACString* aGroup, nsACString* aOrigin, - StoragePrivilege* aPrivilege, - PersistenceType* aDefaultPersistenceType) + bool* aIsApp, + bool* aHasUnlimStoragePerm) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(aPrincipal); if (aGroup && aOrigin) { nsresult rv = TryGetInfoForAboutURI(aPrincipal, *aGroup, *aOrigin, - aPrivilege, aDefaultPersistenceType); + aIsApp, aHasUnlimStoragePerm); if (NS_SUCCEEDED(rv)) { return NS_OK; } } if (nsContentUtils::IsSystemPrincipal(aPrincipal)) { - GetInfoForChrome(aGroup, aOrigin, aPrivilege, aDefaultPersistenceType); + GetInfoForChrome(aGroup, aOrigin, aIsApp, aHasUnlimStoragePerm); return NS_OK; } bool isNullPrincipal; nsresult rv = aPrincipal->GetIsNullPrincipal(&isNullPrincipal); NS_ENSURE_SUCCESS(rv, rv); if (isNullPrincipal) { @@ -2199,73 +2486,125 @@ QuotaManager::GetInfoFromPrincipal(nsIPr aGroup->Assign(jarPrefix + baseDomain); } } if (aOrigin) { aOrigin->Assign(jarPrefix + origin); } - if (aPrivilege) { - *aPrivilege = Content; + if (aIsApp) { + *aIsApp = aPrincipal->GetAppStatus() != + nsIPrincipal::APP_STATUS_NOT_INSTALLED; + } + + if (aHasUnlimStoragePerm) { + *aHasUnlimStoragePerm = CheckQuotaHelper::GetQuotaPermission(aPrincipal) == + nsIPermissionManager::ALLOW_ACTION; } - if (aDefaultPersistenceType) { - *aDefaultPersistenceType = PERSISTENCE_TYPE_PERSISTENT; + // XXX This is temporary, we don't have regular .metadata files for persistent + // storage yet, so we have to use sanitized origin strings and create + // separate group for each origin. + if (aOrigin) { + nsCString originSanitized(*aOrigin); + SanitizeOriginString(originSanitized); + *aOrigin = originSanitized; + } + if (aGroup && aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) { + *aGroup = *aOrigin; } return NS_OK; } // static nsresult QuotaManager::GetInfoFromWindow(nsPIDOMWindow* aWindow, + PersistenceType aPersistenceType, nsACString* aGroup, nsACString* aOrigin, - StoragePrivilege* aPrivilege, - PersistenceType* aDefaultPersistenceType) + bool* aIsApp, + bool* aHasUnlimStoragePerm) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(aWindow); nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aWindow); NS_ENSURE_TRUE(sop, NS_ERROR_FAILURE); nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal(); NS_ENSURE_TRUE(principal, NS_ERROR_FAILURE); - nsresult rv = GetInfoFromPrincipal(principal, aGroup, aOrigin, aPrivilege, - aDefaultPersistenceType); + nsresult rv = GetInfoFromPrincipal(principal, aPersistenceType, aGroup, + aOrigin, aIsApp, aHasUnlimStoragePerm); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } // static void QuotaManager::GetInfoForChrome(nsACString* aGroup, nsACString* aOrigin, - StoragePrivilege* aPrivilege, - PersistenceType* aDefaultPersistenceType) + bool* aIsApp, + bool* aHasUnlimStoragePerm) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(nsContentUtils::IsCallerChrome()); if (aGroup) { ChromeOrigin(*aGroup); } if (aOrigin) { ChromeOrigin(*aOrigin); } - if (aPrivilege) { - *aPrivilege = Chrome; + if (aIsApp) { + *aIsApp = false; + } + if (aHasUnlimStoragePerm) { + *aHasUnlimStoragePerm = false; + } + + // XXX The chrome origin doesn't have to be sanitized and the group is already + // the same as the origin. +} + +bool +QuotaManager::IsTreatedAsPersistent(PersistenceType aPersistenceType, + const nsACString& aOrigin) +{ + if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT && + (aOrigin.Equals("chrome") || + aOrigin.Equals("moz-safe-about+home"))) { + return true; } - if (aDefaultPersistenceType) { - *aDefaultPersistenceType = PERSISTENCE_TYPE_PERSISTENT; + + return false; +} + +// static +bool +QuotaManager::IsQuotaEnforced(PersistenceType aPersistenceType, + const nsACString& aOrigin, + bool aHasUnlimStoragePerm) +{ + bool result; + + if (IsTreatedAsTemporary(aPersistenceType, aOrigin)) { + result = true; + } else { + if (aOrigin.Equals("chrome")) { + result = false; + } else { + result = !aHasUnlimStoragePerm; + } } + + return result; } // static void QuotaManager::ChromeOrigin(nsACString& aOrigin) { aOrigin.AssignLiteral(kChromeOrigin); } @@ -2288,27 +2627,34 @@ QuotaManager::GetUsageForURI(nsIURI* aUR // This only works from the main process. NS_ENSURE_TRUE(IsMainProcess(), NS_ERROR_NOT_AVAILABLE); if (!aOptionalArgCount) { aAppId = nsIScriptSecurityManager::NO_APP_ID; } // Figure out which origin we're dealing with. - nsCString group; + nsCString persistentGroup; nsCString origin; - nsresult rv = GetInfoFromURI(aURI, aAppId, aInMozBrowserOnly, &group, &origin, - nullptr, nullptr); + nsresult rv = GetInfoFromURI(aURI, aAppId, aInMozBrowserOnly, + PERSISTENCE_TYPE_PERSISTENT, &persistentGroup, + &origin, nullptr, nullptr); + NS_ENSURE_SUCCESS(rv, rv); + + nsCString temporaryGroup; + rv = GetInfoFromURI(aURI, aAppId, aInMozBrowserOnly, + PERSISTENCE_TYPE_TEMPORARY, &temporaryGroup, + nullptr, nullptr, nullptr); NS_ENSURE_SUCCESS(rv, rv); OriginOrPatternString oops = OriginOrPatternString::FromOrigin(origin); nsRefPtr<AsyncUsageRunnable> runnable = - new AsyncUsageRunnable(aAppId, aInMozBrowserOnly, group, oops, aURI, - aCallback); + new AsyncUsageRunnable(aAppId, aInMozBrowserOnly, persistentGroup, + temporaryGroup, oops, aURI, aCallback); // Put the computation runnable in the queue. rv = WaitForOpenAllowed(oops, Nullable<PersistenceType>(), EmptyCString(), runnable); NS_ENSURE_SUCCESS(rv, rv); runnable->AdvanceState(); @@ -2376,20 +2722,23 @@ QuotaManager::ClearStoragesForURI(nsIURI NS_ENSURE_TRUE(IsMainProcess(), NS_ERROR_NOT_AVAILABLE); if (!aOptionalArgCount) { aAppId = nsIScriptSecurityManager::NO_APP_ID; } // Figure out which origin we're dealing with. nsCString origin; - rv = GetInfoFromURI(aURI, aAppId, aInMozBrowserOnly, nullptr, &origin, - nullptr, nullptr); + rv = GetInfoFromURI(aURI, aAppId, aInMozBrowserOnly, PERSISTENCE_TYPE_INVALID, + nullptr, &origin, nullptr, nullptr); NS_ENSURE_SUCCESS(rv, rv); + // XXX We don't care about the group, no need to update it for persistence + // storage. + nsAutoCString pattern; GetOriginPatternString(aAppId, aInMozBrowserOnly, origin, pattern); // If there is a pending or running clear operation for this origin, return // immediately. if (IsClearOriginPending(pattern, persistenceType)) { return NS_OK; } @@ -2976,37 +3325,54 @@ QuotaManager::ClearStoragesForApp(uint32 PLDHashOperator QuotaManager::GetOriginsExceedingGroupLimit(const nsACString& aKey, GroupInfoPair* aValue, void* aUserArg) { NS_ASSERTION(!aKey.IsEmpty(), "Empty key!"); NS_ASSERTION(aValue, "Null pointer!"); - nsRefPtr<GroupInfo> groupInfo = + uint64_t groupUsage = 0; + + nsRefPtr<GroupInfo> persistentGroupInfo = + aValue->LockedGetGroupInfo(PERSISTENCE_TYPE_PERSISTENT); + if (persistentGroupInfo) { + groupUsage += persistentGroupInfo->LockedGetTemporaryUsage(); + } + + nsRefPtr<GroupInfo> temporaryGroupInfo = aValue->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY); - if (groupInfo) { + if (temporaryGroupInfo) { + groupUsage += temporaryGroupInfo->LockedGetTemporaryUsage(); + } + + if (groupUsage > 0) { QuotaManager* quotaManager = QuotaManager::Get(); NS_ASSERTION(quotaManager, "Shouldn't be null!"); - if (groupInfo->mUsage > quotaManager->GetGroupLimit()) { + if (groupUsage > quotaManager->GetGroupLimit()) { nsTArray<OriginInfo*>* doomedOriginInfos = static_cast<nsTArray<OriginInfo*>*>(aUserArg); - nsTArray<nsRefPtr<OriginInfo> >& originInfos = groupInfo->mOriginInfos; + nsTArray<OriginInfo*> originInfos; + if (persistentGroupInfo) { + persistentGroupInfo->LockedGetTemporaryOriginInfos(&originInfos); + } + if (temporaryGroupInfo) { + temporaryGroupInfo->LockedGetTemporaryOriginInfos(&originInfos); + } originInfos.Sort(OriginInfoLRUComparator()); - uint64_t usage = groupInfo->mUsage; for (uint32_t i = 0; i < originInfos.Length(); i++) { OriginInfo* originInfo = originInfos[i]; doomedOriginInfos->AppendElement(originInfo); - usage -= originInfo->mUsage; - - if (usage <= quotaManager->GetGroupLimit()) { + groupUsage -= originInfo->mUsage; + + if (groupUsage <= quotaManager->GetGroupLimit()) { break; } } } } return PL_DHASH_NEXT; } @@ -3015,23 +3381,28 @@ QuotaManager::GetOriginsExceedingGroupLi PLDHashOperator QuotaManager::GetAllTemporaryStorageOrigins(const nsACString& aKey, GroupInfoPair* aValue, void* aUserArg) { NS_ASSERTION(!aKey.IsEmpty(), "Empty key!"); NS_ASSERTION(aValue, "Null pointer!"); + nsTArray<OriginInfo*>* originInfos = + static_cast<nsTArray<OriginInfo*>*>(aUserArg); + nsRefPtr<GroupInfo> groupInfo = - aValue->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY); + aValue->LockedGetGroupInfo(PERSISTENCE_TYPE_PERSISTENT); if (groupInfo) { - nsTArray<OriginInfo*>* originInfos = - static_cast<nsTArray<OriginInfo*>*>(aUserArg); - - originInfos->AppendElements(groupInfo->mOriginInfos); + groupInfo->LockedGetTemporaryOriginInfos(originInfos); + } + + groupInfo = aValue->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY); + if (groupInfo) { + groupInfo->LockedGetTemporaryOriginInfos(originInfos); } return PL_DHASH_NEXT; } void QuotaManager::CheckTemporaryStorageLimits() { @@ -3072,98 +3443,116 @@ QuotaManager::CheckTemporaryStorageLimit usage += originInfos[i]->mUsage; } doomedOriginInfos.AppendElements(originInfos); } } for (uint32_t index = 0; index < doomedOriginInfos.Length(); index++) { - DeleteTemporaryFilesForOrigin(doomedOriginInfos[index]->mOrigin); + OriginInfo* doomedOriginInfo = doomedOriginInfos[index]; + + DeleteFilesForOrigin(doomedOriginInfo->mGroupInfo->mPersistenceType, + doomedOriginInfo->mOrigin); } - nsTArray<nsCString> doomedOrigins; + nsTArray<OriginParams> doomedOrigins; { MutexAutoLock lock(mQuotaMutex); for (uint32_t index = 0; index < doomedOriginInfos.Length(); index++) { OriginInfo* doomedOriginInfo = doomedOriginInfos[index]; + PersistenceType persistenceType = + doomedOriginInfo->mGroupInfo->mPersistenceType; nsCString group = doomedOriginInfo->mGroupInfo->mGroup; nsCString origin = doomedOriginInfo->mOrigin; - LockedRemoveQuotaForOrigin(PERSISTENCE_TYPE_TEMPORARY, group, origin); + LockedRemoveQuotaForOrigin(persistenceType, group, origin); #ifdef DEBUG doomedOriginInfos[index] = nullptr; #endif - doomedOrigins.AppendElement(origin); + doomedOrigins.AppendElement(OriginParams(persistenceType, origin)); } } for (uint32_t index = 0; index < doomedOrigins.Length(); index++) { + const OriginParams& doomedOrigin = doomedOrigins[index]; + OriginClearCompleted( - PERSISTENCE_TYPE_TEMPORARY, - OriginOrPatternString::FromOrigin(doomedOrigins[index])); + doomedOrigin.mPersistenceType, + OriginOrPatternString::FromOrigin(doomedOrigin.mOrigin)); } } // static PLDHashOperator -QuotaManager::AddTemporaryStorageOrigins( - const nsACString& aKey, - ArrayCluster<nsIOfflineStorage*>* aValue, - void* aUserArg) +QuotaManager::AddLiveStorageOrigins(const nsACString& aKey, + nsTArray<nsIOfflineStorage*>* aValue, + void* aUserArg) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); NS_ASSERTION(!aKey.IsEmpty(), "Empty key!"); NS_ASSERTION(aValue, "Null pointer!"); NS_ASSERTION(aUserArg, "Null pointer!"); OriginCollection& collection = *static_cast<OriginCollection*>(aUserArg); - if (collection.ContainsOrigin(aKey)) { - return PL_DHASH_NEXT; - } - - for (uint32_t i = 0; i < Client::TYPE_MAX; i++) { - nsTArray<nsIOfflineStorage*>& array = (*aValue)[i]; - for (uint32_t j = 0; j < array.Length(); j++) { - nsIOfflineStorage*& storage = array[j]; - if (storage->Type() == PERSISTENCE_TYPE_TEMPORARY) { - collection.AddOrigin(aKey); - return PL_DHASH_NEXT; - } - } + if (!collection.ContainsOrigin(aKey)) { + collection.AddOrigin(aKey); } return PL_DHASH_NEXT; } // static PLDHashOperator QuotaManager::GetInactiveTemporaryStorageOrigins(const nsACString& aKey, GroupInfoPair* aValue, void* aUserArg) { NS_ASSERTION(!aKey.IsEmpty(), "Empty key!"); NS_ASSERTION(aValue, "Null pointer!"); NS_ASSERTION(aUserArg, "Null pointer!"); + InactiveOriginsInfo* info = static_cast<InactiveOriginsInfo*>(aUserArg); + nsRefPtr<GroupInfo> groupInfo = - aValue->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY); + aValue->LockedGetGroupInfo(PERSISTENCE_TYPE_PERSISTENT); if (groupInfo) { - InactiveOriginsInfo* info = static_cast<InactiveOriginsInfo*>(aUserArg); - nsTArray<nsRefPtr<OriginInfo> >& originInfos = groupInfo->mOriginInfos; for (uint32_t i = 0; i < originInfos.Length(); i++) { OriginInfo* originInfo = originInfos[i]; - if (!info->collection.ContainsOrigin(originInfo->mOrigin)) { + if (originInfo->IsTreatedAsPersistent()) { + continue; + } + + if (!info->persistentCollection.ContainsOrigin(originInfo->mOrigin)) { + NS_ASSERTION(!originInfo->mQuotaObjects.Count(), + "Inactive origin shouldn't have open files!"); + info->origins.AppendElement(originInfo); + } + } + } + + groupInfo = aValue->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY); + if (groupInfo) { + nsTArray<nsRefPtr<OriginInfo> >& originInfos = groupInfo->mOriginInfos; + + for (uint32_t i = 0; i < originInfos.Length(); i++) { + OriginInfo* originInfo = originInfos[i]; + + if (originInfo->IsTreatedAsPersistent()) { + continue; + } + + if (!info->temporaryCollection.ContainsOrigin(originInfo->mOrigin)) { NS_ASSERTION(!originInfo->mQuotaObjects.Count(), "Inactive origin shouldn't have open files!"); info->origins.AppendElement(originInfo); } } } return PL_DHASH_NEXT; @@ -3171,50 +3560,80 @@ QuotaManager::GetInactiveTemporaryStorag uint64_t QuotaManager::CollectOriginsForEviction(uint64_t aMinSizeToBeFreed, nsTArray<OriginInfo*>& aOriginInfos) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); // Collect active origins first. - OriginCollection originCollection; + OriginCollection persistentOriginCollection; + OriginCollection temporaryOriginCollection; // Add patterns and origins that have running or pending synchronized ops. // (add patterns first to reduce redundancy in the origin collection). uint32_t index; for (index = 0; index < mSynchronizedOps.Length(); index++) { nsAutoPtr<SynchronizedOp>& op = mSynchronizedOps[index]; - if (op->mPersistenceType.IsNull() || - op->mPersistenceType.Value() == PERSISTENCE_TYPE_TEMPORARY) { - if (op->mOriginOrPattern.IsPattern() && - !originCollection.ContainsPattern(op->mOriginOrPattern)) { - originCollection.AddPattern(op->mOriginOrPattern); - } + + const OriginOrPatternString& originOrPattern = op->mOriginOrPattern; + + if (!originOrPattern.IsPattern()) { + continue; + } + + Nullable<PersistenceType>& persistenceType = op->mPersistenceType; + + if (persistenceType.IsNull()) { + persistentOriginCollection.AddPattern(originOrPattern); + temporaryOriginCollection.AddPattern(originOrPattern); + } else if (persistenceType.Value() == PERSISTENCE_TYPE_PERSISTENT) { + persistentOriginCollection.AddPattern(originOrPattern); + } else { + MOZ_ASSERT(persistenceType.Value() == PERSISTENCE_TYPE_TEMPORARY); + temporaryOriginCollection.AddPattern(originOrPattern); } } for (index = 0; index < mSynchronizedOps.Length(); index++) { nsAutoPtr<SynchronizedOp>& op = mSynchronizedOps[index]; - if (op->mPersistenceType.IsNull() || - op->mPersistenceType.Value() == PERSISTENCE_TYPE_TEMPORARY) { - if (op->mOriginOrPattern.IsOrigin() && - !originCollection.ContainsOrigin(op->mOriginOrPattern)) { - originCollection.AddOrigin(op->mOriginOrPattern); - } + + const OriginOrPatternString& originOrPattern = op->mOriginOrPattern; + + if (!originOrPattern.IsOrigin()) { + continue; + } + + Nullable<PersistenceType>& persistenceType = op->mPersistenceType; + + if (persistenceType.IsNull()) { + persistentOriginCollection.AddOrigin(originOrPattern); + temporaryOriginCollection.AddOrigin(originOrPattern); + } else if (persistenceType.Value() == PERSISTENCE_TYPE_PERSISTENT) { + persistentOriginCollection.AddOrigin(originOrPattern); + } else { + MOZ_ASSERT(persistenceType.Value() == PERSISTENCE_TYPE_TEMPORARY); + temporaryOriginCollection.AddOrigin(originOrPattern); } } + // Add origins that have live persistent storages. + mPersistentLiveStorageTable.EnumerateRead(AddLiveStorageOrigins, + &persistentOriginCollection); + // Add origins that have live temporary storages. - mLiveStorages.EnumerateRead(AddTemporaryStorageOrigins, &originCollection); + mTemporaryLiveStorageTable.EnumerateRead(AddLiveStorageOrigins, + &temporaryOriginCollection); // Enumerate inactive origins. This must be protected by the mutex. nsTArray<OriginInfo*> inactiveOrigins; { - InactiveOriginsInfo info(originCollection, inactiveOrigins); + InactiveOriginsInfo info(persistentOriginCollection, + temporaryOriginCollection, + inactiveOrigins); MutexAutoLock lock(mQuotaMutex); mGroupInfoPairs.EnumerateRead(GetInactiveTemporaryStorageOrigins, &info); } // We now have a list of all inactive origins. So it's safe to sort the list // and calculate available size without holding the lock. // Sort by the origin access time. @@ -3232,72 +3651,78 @@ QuotaManager::CollectOriginsForEviction( sizeToBeFreed += inactiveOrigins[index]->mUsage; } if (sizeToBeFreed >= aMinSizeToBeFreed) { // Success, add synchronized ops for these origins, so any other // operations for them will be delayed (until origin eviction is finalized). for(index = 0; index < inactiveOrigins.Length(); index++) { + OriginInfo* inactiveOrigin = inactiveOrigins[index]; + OriginOrPatternString oops = - OriginOrPatternString::FromOrigin(inactiveOrigins[index]->mOrigin); - - AddSynchronizedOp(oops, - Nullable<PersistenceType>(PERSISTENCE_TYPE_TEMPORARY)); + OriginOrPatternString::FromOrigin(inactiveOrigin->mOrigin); + + Nullable<PersistenceType> persistenceType = + Nullable<PersistenceType>(inactiveOrigin->mGroupInfo->mPersistenceType); + + AddSynchronizedOp(oops, persistenceType); } inactiveOrigins.SwapElements(aOriginInfos); return sizeToBeFreed; } return 0; } void -QuotaManager::DeleteTemporaryFilesForOrigin(const nsACString& aOrigin) +QuotaManager::DeleteFilesForOrigin(PersistenceType aPersistenceType, + const nsACString& aOrigin) { nsCOMPtr<nsIFile> directory; - nsresult rv = GetDirectoryForOrigin(PERSISTENCE_TYPE_TEMPORARY, aOrigin, + nsresult rv = GetDirectoryForOrigin(aPersistenceType, aOrigin, getter_AddRefs(directory)); NS_ENSURE_SUCCESS_VOID(rv); rv = directory->Remove(true); if (rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST && rv != NS_ERROR_FILE_NOT_FOUND && NS_FAILED(rv)) { // This should never fail if we've closed all storage connections // correctly... NS_ERROR("Failed to remove directory!"); } } void -QuotaManager::FinalizeOriginEviction(nsTArray<nsCString>& aOrigins) +QuotaManager::FinalizeOriginEviction(nsTArray<OriginParams>& aOrigins) { NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); nsRefPtr<FinalizeOriginEvictionRunnable> runnable = new FinalizeOriginEvictionRunnable(aOrigins); nsresult rv = IsOnIOThread() ? runnable->RunImmediately() : runnable->Dispatch(); NS_ENSURE_SUCCESS_VOID(rv); } void -QuotaManager::SaveOriginAccessTime(const nsACString& aOrigin, +QuotaManager::SaveOriginAccessTime(PersistenceType aPersistenceType, + const nsACString& aOrigin, int64_t aTimestamp) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); if (QuotaManager::IsShuttingDown()) { return; } nsRefPtr<SaveOriginAccessTimeRunnable> runnable = - new SaveOriginAccessTimeRunnable(aOrigin, aTimestamp); + new SaveOriginAccessTimeRunnable(aPersistenceType, aOrigin, aTimestamp); if (NS_FAILED(mIOThread->Dispatch(runnable, NS_DISPATCH_NORMAL))) { NS_WARNING("Failed to dispatch runnable!"); } } void QuotaManager::GetOriginPatternString(uint32_t aAppId, @@ -3337,16 +3762,32 @@ QuotaManager::GetOriginPatternString(uin NS_ASSERTION(PatternMatchesOrigin(pattern, aOrigin), "Origin doesn't match parameters!"); } #endif _retval = aOrigin; } +auto +QuotaManager::GetLiveStorageTable(PersistenceType aPersistenceType) + -> LiveStorageTable& +{ + switch (aPersistenceType) { + case PERSISTENCE_TYPE_PERSISTENT: + return mPersistentLiveStorageTable; + case PERSISTENCE_TYPE_TEMPORARY: + return mTemporaryLiveStorageTable; + + case PERSISTENCE_TYPE_INVALID: + default: + MOZ_CRASH("Bad persistence type value!"); + } +} + SynchronizedOp::SynchronizedOp(const OriginOrPatternString& aOriginOrPattern, Nullable<PersistenceType> aPersistenceType, const nsACString& aId) : mOriginOrPattern(aOriginOrPattern), mPersistenceType(aPersistenceType), mId(aId) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); MOZ_COUNT_CTOR(SynchronizedOp); @@ -3547,29 +3988,31 @@ OriginClearRunnable::DeleteFiles(QuotaMa while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) { nsCOMPtr<nsISupports> entry; rv = entries->GetNext(getter_AddRefs(entry)); NS_ENSURE_SUCCESS_VOID(rv); nsCOMPtr<nsIFile> file = do_QueryInterface(entry); NS_ASSERTION(file, "Don't know what this is!"); + nsString leafName; + rv = file->GetLeafName(leafName); + NS_ENSURE_SUCCESS_VOID(rv); + bool isDirectory; rv = file->IsDirectory(&isDirectory); NS_ENSURE_SUCCESS_VOID(rv); if (!isDirectory) { - NS_WARNING("Something in the IndexedDB directory that doesn't belong!"); + if (!leafName.EqualsLiteral(DSSTORE_FILE_NAME)) { + NS_WARNING("Something in the IndexedDB directory that doesn't belong!"); + } continue; } - nsString leafName; - rv = file->GetLeafName(leafName); - NS_ENSURE_SUCCESS_VOID(rv); - // Skip storages for other apps. if (!PatternMatchesOrigin(originSanitized, NS_ConvertUTF16toUTF8(leafName))) { continue; } for (uint32_t index = 0; index < 10; index++) { // We can't guarantee that this will always succeed on Windows... @@ -3662,31 +4105,34 @@ OriginClearRunnable::Run() } NS_NOTREACHED("Should never get here!"); return NS_ERROR_UNEXPECTED; } AsyncUsageRunnable::AsyncUsageRunnable(uint32_t aAppId, bool aInMozBrowserOnly, - const nsACString& aGroup, + const nsACString& aPersistentGroup, + const nsACString& aTemporaryGroup, const OriginOrPatternString& aOrigin, nsIURI* aURI, nsIUsageCallback* aCallback) : mURI(aURI), mCallback(aCallback), mAppId(aAppId), - mGroup(aGroup), + mPersistentGroup(aPersistentGroup), + mTemporaryGroup(aTemporaryGroup), mOrigin(aOrigin), mCallbackState(Pending), mInMozBrowserOnly(aInMozBrowserOnly) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); NS_ASSERTION(aURI, "Null pointer!"); - NS_ASSERTION(!aGroup.IsEmpty(), "Empty group!"); + NS_ASSERTION(!aPersistentGroup.IsEmpty(), "Empty group!"); + NS_ASSERTION(!aTemporaryGroup.IsEmpty(), "Empty group!"); NS_ASSERTION(aOrigin.IsOrigin(), "Expect origin only here!"); NS_ASSERTION(!aOrigin.IsEmpty(), "Empty origin!"); NS_ASSERTION(aCallback, "Null pointer!"); } nsresult AsyncUsageRunnable::TakeShortcut() { @@ -3791,27 +4237,32 @@ AsyncUsageRunnable::AddToUsage(QuotaMana rv = directory->Exists(&exists); NS_ENSURE_SUCCESS(rv, rv); // If the directory exists then enumerate all the files inside, adding up // the sizes to get the final usage statistic. if (exists && !mCanceled) { bool initialized; - if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) { + if (QuotaManager::IsTreatedAsPersistent(aPersistenceType, mOrigin)) { initialized = aQuotaManager->mInitializedOrigins.Contains(mOrigin); - + } else { + initialized = aQuotaManager->mTemporaryStorageInitialized; + } + + nsCString* group; + if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) { if (!initialized) { rv = MaybeUpgradeOriginDirectory(directory); NS_ENSURE_SUCCESS(rv, rv); } - } - else { - NS_ASSERTION(aPersistenceType == PERSISTENCE_TYPE_TEMPORARY, "Huh?"); - initialized = aQuotaManager->mTemporaryStorageInitialized; + + group = &mPersistentGroup; + } else { + group = &mTemporaryGroup; } nsCOMPtr<nsISimpleEnumerator> entries; rv = directory->GetDirectoryEntries(getter_AddRefs(entries)); NS_ENSURE_SUCCESS(rv, rv); bool hasMore; while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && @@ -3851,20 +4302,20 @@ AsyncUsageRunnable::AddToUsage(QuotaMana return NS_ERROR_UNEXPECTED; } continue; } nsRefPtr<Client>& client = aQuotaManager->mClients[clientType]; if (initialized) { - rv = client->GetUsageForOrigin(aPersistenceType, mGroup, mOrigin, this); + rv = client->GetUsageForOrigin(aPersistenceType, *group, mOrigin, this); } else { - rv = client->InitOrigin(aPersistenceType, mGroup, mOrigin, this); + rv = client->InitOrigin(aPersistenceType, *group, mOrigin, this); } NS_ENSURE_SUCCESS(rv, rv); } } return NS_OK; } @@ -4053,37 +4504,41 @@ FinalizeOriginEvictionRunnable::Run() } case IO: { AssertIsOnIOThread(); AdvanceState(); for (uint32_t index = 0; index < mOrigins.Length(); index++) { + const OriginParams& origin = mOrigins[index]; + quotaManager->OriginClearCompleted( - PERSISTENCE_TYPE_TEMPORARY, - OriginOrPatternString::FromOrigin(mOrigins[index])); + origin.mPersistenceType, + OriginOrPatternString::FromOrigin(origin.mOrigin)); } if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) { NS_WARNING("Failed to dispatch to main thread!"); return NS_ERROR_FAILURE; } return NS_OK; } case Complete: { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); for (uint32_t index = 0; index < mOrigins.Length(); index++) { + const OriginParams& origin = mOrigins[index]; + quotaManager->AllowNextSynchronizedOp( - OriginOrPatternString::FromOrigin(mOrigins[index]), - Nullable<PersistenceType>(PERSISTENCE_TYPE_TEMPORARY), - EmptyCString()); + OriginOrPatternString::FromOrigin(origin.mOrigin), + Nullable<PersistenceType>(origin.mPersistenceType), + EmptyCString()); } return NS_OK; } default: NS_ERROR("Unknown state value!"); return NS_ERROR_UNEXPECTED; @@ -4154,22 +4609,23 @@ SaveOriginAccessTimeRunnable::Run() { AssertIsOnIOThread(); QuotaManager* quotaManager = QuotaManager::Get(); NS_ASSERTION(quotaManager, "This should never fail!"); nsCOMPtr<nsIFile> directory; nsresult rv = - quotaManager->GetDirectoryForOrigin(PERSISTENCE_TYPE_TEMPORARY, mOrigin, + quotaManager->GetDirectoryForOrigin(mPersistenceType, mOrigin, getter_AddRefs(directory)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsIBinaryOutputStream> stream; - rv = GetDirectoryMetadataStream(directory, true, getter_AddRefs(stream)); + rv = GetDirectoryMetadataOutputStream(directory, true, + getter_AddRefs(stream)); NS_ENSURE_SUCCESS(rv, rv); // The origin directory may not exist anymore. if (stream) { rv = stream->Write64(mTimestamp); NS_ENSURE_SUCCESS(rv, rv); }
--- a/dom/quota/QuotaManager.h +++ b/dom/quota/QuotaManager.h @@ -16,17 +16,16 @@ #include "mozilla/Mutex.h" #include "nsClassHashtable.h" #include "nsRefPtrHashtable.h" #include "ArrayCluster.h" #include "Client.h" #include "PersistenceType.h" -#include "StoragePrivilege.h" #define QUOTA_MANAGER_CONTRACTID "@mozilla.org/dom/quota/manager;1" class nsIOfflineStorage; class nsIPrincipal; class nsIThread; class nsITimer; class nsIURI; @@ -50,16 +49,28 @@ class GroupInfo; class GroupInfoPair; class OriginClearRunnable; class OriginInfo; class OriginOrPatternString; class QuotaObject; class ResetOrClearRunnable; struct SynchronizedOp; +struct OriginParams +{ + OriginParams(PersistenceType aPersistenceType, + const nsACString& aOrigin) + : mOrigin(aOrigin) + , mPersistenceType(aPersistenceType) + { } + + nsCString mOrigin; + PersistenceType mPersistenceType; +}; + class QuotaManager MOZ_FINAL : public nsIQuotaManager, public nsIObserver { friend class AsyncUsageRunnable; friend class CollectOriginsHelper; friend class FinalizeOriginEvictionRunnable; friend class GroupInfo; friend class OriginClearRunnable; @@ -74,16 +85,19 @@ class QuotaManager MOZ_FINAL : public ns MozBrowser = 0, NotMozBrowser, IgnoreMozBrowser }; typedef void (*WaitingOnStoragesCallback)(nsTArray<nsCOMPtr<nsIOfflineStorage> >&, void*); + typedef nsClassHashtable<nsCStringHashKey, + nsTArray<nsIOfflineStorage*>> LiveStorageTable; + public: NS_DECL_ISUPPORTS NS_DECL_NSIQUOTAMANAGER NS_DECL_NSIOBSERVER // Returns a non-owning reference. static QuotaManager* GetOrCreate(); @@ -117,17 +131,17 @@ public: UpdateOriginAccessTime(PersistenceType aPersistenceType, const nsACString& aGroup, const nsACString& aOrigin); void RemoveQuota(); void - RemoveQuotaForPersistenceType(PersistenceType); + RemoveQuotaForTemporaryStorage(); void RemoveQuotaForOrigin(PersistenceType aPersistenceType, const nsACString& aGroup, const nsACString& aOrigin) { MutexAutoLock lock(mQuotaMutex); LockedRemoveQuotaForOrigin(aPersistenceType, aGroup, aOrigin); @@ -248,17 +262,17 @@ public: GetDirectoryForOrigin(PersistenceType aPersistenceType, const nsACString& aASCIIOrigin, nsIFile** aDirectory) const; nsresult EnsureOriginIsInitialized(PersistenceType aPersistenceType, const nsACString& aGroup, const nsACString& aOrigin, - bool aTrackQuota, + bool aHasUnlimStoragePerm, nsIFile** aDirectory); void OriginClearCompleted(PersistenceType aPersistenceType, const OriginOrPatternString& aOriginOrPattern); void ResetOrClearCompleted(); @@ -309,40 +323,59 @@ public: Client::Type aClientType, const nsAString& aName, nsACString& aDatabaseId); static nsresult GetInfoFromURI(nsIURI* aURI, uint32_t aAppId, bool aInMozBrowser, + PersistenceType aPersistenceType, nsACString* aGroup, nsACString* aOrigin, - StoragePrivilege* aPrivilege, - PersistenceType* aDefaultPersistenceType); + bool* aIsApp, + bool* aHasUnlimStoragePerm); static nsresult GetInfoFromPrincipal(nsIPrincipal* aPrincipal, + PersistenceType aPersistenceType, nsACString* aGroup, nsACString* aOrigin, - StoragePrivilege* aPrivilege, - PersistenceType* aDefaultPersistenceType); + bool* aIsApp, + bool* aHasUnlimStoragePerm); static nsresult GetInfoFromWindow(nsPIDOMWindow* aWindow, + PersistenceType aPersistenceType, nsACString* aGroup, nsACString* aOrigin, - StoragePrivilege* aPrivilege, - PersistenceType* aDefaultPersistenceType); + bool* aIsApp, + bool* aHasUnlimStoragePerm); static void GetInfoForChrome(nsACString* aGroup, nsACString* aOrigin, - StoragePrivilege* aPrivilege, - PersistenceType* aDefaultPersistenceType); + bool* aIsApp, + bool* aHasUnlimStoragePerm); + + static bool + IsTreatedAsPersistent(PersistenceType aPersistenceType, + const nsACString& aOrigin); + + static bool + IsTreatedAsTemporary(PersistenceType aPersistenceType, + const nsACString& aOrigin) + { + return !IsTreatedAsPersistent(aPersistenceType, aOrigin); + } + + static bool + IsQuotaEnforced(PersistenceType aPersistenceType, + const nsACString& aOrigin, + bool aHasUnlimStoragePerm); static void ChromeOrigin(nsACString& aOrigin); static void GetOriginPatternString(uint32_t aAppId, bool aBrowserOnly, const nsACString& aOrigin, nsAutoCString& _retval) { @@ -408,67 +441,76 @@ private: FindSynchronizedOp(const nsACString& aPattern, Nullable<PersistenceType> aPersistenceType, const nsACString& aId); nsresult MaybeUpgradeIndexedDBDirectory(); nsresult + InitializeRepository(PersistenceType aPersistenceType); + + nsresult InitializeOrigin(PersistenceType aPersistenceType, const nsACString& aGroup, const nsACString& aOrigin, - bool aTrackQuota, + bool aHasUnlimStoragePerm, int64_t aAccessTime, nsIFile* aDirectory); nsresult ClearStoragesForApp(uint32_t aAppId, bool aBrowserOnly); void CheckTemporaryStorageLimits(); // Collect inactive and the least recently used origins. uint64_t CollectOriginsForEviction(uint64_t aMinSizeToBeFreed, nsTArray<OriginInfo*>& aOriginInfos); void - DeleteTemporaryFilesForOrigin(const nsACString& aOrigin); + DeleteFilesForOrigin(PersistenceType aPersistenceType, + const nsACString& aOrigin); void - FinalizeOriginEviction(nsTArray<nsCString>& aOrigins); + FinalizeOriginEviction(nsTArray<OriginParams>& aOrigins); void - SaveOriginAccessTime(const nsACString& aOrigin, int64_t aTimestamp); + SaveOriginAccessTime(PersistenceType aPersistenceType, + const nsACString& aOrigin, + int64_t aTimestamp); void ReleaseIOThreadObjects() { AssertIsOnIOThread(); for (uint32_t index = 0; index < Client::TYPE_MAX; index++) { mClients[index]->ReleaseIOThreadObjects(); } } template <class OwnerClass> void AbortCloseStoragesFor(OwnerClass* aOwnerClass); + LiveStorageTable& + GetLiveStorageTable(PersistenceType aPersistenceType); + static void GetOriginPatternString(uint32_t aAppId, MozBrowserPatternFlag aBrowserFlag, const nsACString& aOrigin, nsAutoCString& _retval); static PLDHashOperator - RemoveQuotaForPersistenceTypeCallback(const nsACString& aKey, - nsAutoPtr<GroupInfoPair>& aValue, - void* aUserArg); + RemoveQuotaForTemporaryStorageCallback(const nsACString& aKey, + nsAutoPtr<GroupInfoPair>& aValue, + void* aUserArg); static PLDHashOperator RemoveQuotaCallback(const nsACString& aKey, nsAutoPtr<GroupInfoPair>& aValue, void* aUserArg); static PLDHashOperator RemoveQuotaForPatternCallback(const nsACString& aKey, @@ -481,19 +523,19 @@ private: void* aUserArg); static PLDHashOperator GetAllTemporaryStorageOrigins(const nsACString& aKey, GroupInfoPair* aValue, void* aUserArg); static PLDHashOperator - AddTemporaryStorageOrigins(const nsACString& aKey, - ArrayCluster<nsIOfflineStorage*>* aValue, - void* aUserArg); + AddLiveStorageOrigins(const nsACString& aKey, + nsTArray<nsIOfflineStorage*>* aValue, + void* aUserArg); static PLDHashOperator GetInactiveTemporaryStorageOrigins(const nsACString& aKey, GroupInfoPair* aValue, void* aUserArg); // TLS storage index for the current thread's window. unsigned int mCurrentWindowIndex; @@ -505,16 +547,19 @@ private: // A map of Windows to the corresponding quota helper. nsRefPtrHashtable<nsPtrHashKey<nsPIDOMWindow>, CheckQuotaHelper> mCheckQuotaHelpers; // Maintains a list of live storages per origin. nsClassHashtable<nsCStringHashKey, ArrayCluster<nsIOfflineStorage*> > mLiveStorages; + LiveStorageTable mPersistentLiveStorageTable; + LiveStorageTable mTemporaryLiveStorageTable; + // Maintains a list of synchronized operatons that are in progress or queued. nsAutoTArray<nsAutoPtr<SynchronizedOp>, 5> mSynchronizedOps; // Thread on which IO is performed. nsCOMPtr<nsIThread> mIOThread; // A timer that gets activated at shutdown to ensure we close all storages. nsCOMPtr<nsITimer> mShutdownTimer;
--- a/dom/quota/QuotaObject.cpp +++ b/dom/quota/QuotaObject.cpp @@ -70,27 +70,27 @@ QuotaObject::UpdateSize(int64_t aSize) MutexAutoLock lock(quotaManager->mQuotaMutex); if (!mOriginInfo) { return; } GroupInfo* groupInfo = mOriginInfo->mGroupInfo; - if (groupInfo->IsForTemporaryStorage()) { + if (mOriginInfo->IsTreatedAsTemporary()) { quotaManager->mTemporaryStorageUsage -= mSize; } groupInfo->mUsage -= mSize; mOriginInfo->mUsage -= mSize; mSize = aSize; mOriginInfo->mUsage += mSize; groupInfo->mUsage += mSize; - if (groupInfo->IsForTemporaryStorage()) { + if (mOriginInfo->IsTreatedAsTemporary()) { quotaManager->mTemporaryStorageUsage += mSize; } } bool QuotaObject::MaybeAllocateMoreSpace(int64_t aOffset, int32_t aCount) { int64_t end = aOffset + aCount; @@ -101,17 +101,17 @@ QuotaObject::MaybeAllocateMoreSpace(int6 MutexAutoLock lock(quotaManager->mQuotaMutex); if (mSize >= end || !mOriginInfo) { return true; } GroupInfo* groupInfo = mOriginInfo->mGroupInfo; - if (groupInfo->IsForPersistentStorage()) { + if (mOriginInfo->IsTreatedAsPersistent()) { uint64_t newUsage = mOriginInfo->mUsage - mSize + end; if (newUsage > mOriginInfo->mLimit) { // This will block the thread, but it will also drop the mutex while // waiting. The mutex will be reacquired again when the waiting is // finished. if (!quotaManager->LockedQuotaIsLifted()) { return false; @@ -130,17 +130,17 @@ QuotaObject::MaybeAllocateMoreSpace(int6 nsCString group = mOriginInfo->mGroupInfo->mGroup; nsCString origin = mOriginInfo->mOrigin; mOriginInfo->LockedClearOriginInfos(); NS_ASSERTION(!mOriginInfo, "Should have cleared in LockedClearOriginInfos!"); - quotaManager->LockedRemoveQuotaForOrigin(PERSISTENCE_TYPE_PERSISTENT, + quotaManager->LockedRemoveQuotaForOrigin(groupInfo->mPersistenceType, group, origin); // Some other thread could increase the size without blocking (increasing // the origin usage without hitting the limit), but no more than this one. NS_ASSERTION(mSize < end, "This shouldn't happen!"); mSize = end; @@ -151,31 +151,37 @@ QuotaObject::MaybeAllocateMoreSpace(int6 groupInfo->mUsage = groupInfo->mUsage - mSize + end; mSize = end; return true; } - NS_ASSERTION(groupInfo->mPersistenceType == PERSISTENCE_TYPE_TEMPORARY, - "Huh?"); + nsRefPtr<GroupInfo> complementaryGroupInfo = + groupInfo->mGroupInfoPair->LockedGetGroupInfo( + ComplementaryPersistenceType(groupInfo->mPersistenceType)); uint64_t delta = end - mSize; uint64_t newUsage = mOriginInfo->mUsage + delta; // Temporary storage has no limit for origin usage (there's a group and the // global limit though). uint64_t newGroupUsage = groupInfo->mUsage + delta; + uint64_t groupUsage = groupInfo->LockedGetTemporaryUsage(); + if (complementaryGroupInfo) { + groupUsage += complementaryGroupInfo->LockedGetTemporaryUsage(); + } + // Temporary storage has a hard limit for group usage (20 % of the global // limit). - if (newGroupUsage > quotaManager->GetGroupLimit()) { + if (groupUsage + delta > quotaManager->GetGroupLimit()) { return false; } uint64_t newTemporaryStorageUsage = quotaManager->mTemporaryStorageUsage + delta; if (newTemporaryStorageUsage > quotaManager->mTemporaryStorageLimit) { // This will block the thread without holding the lock while waitting. @@ -189,52 +195,62 @@ QuotaObject::MaybeAllocateMoreSpace(int6 } NS_ASSERTION(sizeToBeFreed >= delta, "Huh?"); { MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex); for (uint32_t i = 0; i < originInfos.Length(); i++) { - quotaManager->DeleteTemporaryFilesForOrigin(originInfos[i]->mOrigin); + OriginInfo* originInfo = originInfos[i]; + + quotaManager->DeleteFilesForOrigin( + originInfo->mGroupInfo->mPersistenceType, + originInfo->mOrigin); } } // Relocked. NS_ASSERTION(mOriginInfo, "How come?!"); - nsTArray<nsCString> origins; + nsTArray<OriginParams> origins; for (uint32_t i = 0; i < originInfos.Length(); i++) { OriginInfo* originInfo = originInfos[i]; NS_ASSERTION(originInfo != mOriginInfo, "Deleted itself!"); + PersistenceType persistenceType = + originInfo->mGroupInfo->mPersistenceType; nsCString group = originInfo->mGroupInfo->mGroup; nsCString origin = originInfo->mOrigin; - quotaManager->LockedRemoveQuotaForOrigin(PERSISTENCE_TYPE_TEMPORARY, - group, origin); + quotaManager->LockedRemoveQuotaForOrigin(persistenceType, group, origin); #ifdef DEBUG originInfos[i] = nullptr; #endif - origins.AppendElement(origin); + origins.AppendElement(OriginParams(persistenceType, origin)); } // We unlocked and relocked several times so we need to recompute all the // essential variables and recheck the group limit. delta = end - mSize; newUsage = mOriginInfo->mUsage + delta; newGroupUsage = groupInfo->mUsage + delta; - if (newGroupUsage > quotaManager->GetGroupLimit()) { + groupUsage = groupInfo->LockedGetTemporaryUsage(); + if (complementaryGroupInfo) { + groupUsage += complementaryGroupInfo->LockedGetTemporaryUsage(); + } + + if (groupUsage + delta > quotaManager->GetGroupLimit()) { // Unfortunately some other thread increased the group usage in the // meantime and we are not below the group limit anymore. // However, the origin eviction must be finalized in this case too. MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex); quotaManager->FinalizeOriginEviction(origins); @@ -271,26 +287,40 @@ QuotaObject::MaybeAllocateMoreSpace(int6 groupInfo->mUsage = newGroupUsage; quotaManager->mTemporaryStorageUsage = newTemporaryStorageUsage; mSize = end; return true; } +bool +OriginInfo::IsTreatedAsPersistent() const +{ + return QuotaManager::IsTreatedAsPersistent(mGroupInfo->mPersistenceType, + mOrigin); +} + +bool +OriginInfo::IsTreatedAsTemporary() const +{ + return QuotaManager::IsTreatedAsTemporary(mGroupInfo->mPersistenceType, + mOrigin); +} + void OriginInfo::LockedDecreaseUsage(int64_t aSize) { AssertCurrentThreadOwnsQuotaMutex(); mUsage -= aSize; mGroupInfo->mUsage -= aSize; - if (mGroupInfo->IsForTemporaryStorage()) { + if (IsTreatedAsTemporary()) { QuotaManager* quotaManager = QuotaManager::Get(); NS_ASSERTION(quotaManager, "Shouldn't be null!"); quotaManager->mTemporaryStorageUsage -= aSize; } } // static @@ -330,34 +360,34 @@ GroupInfo::LockedAddOriginInfo(OriginInf AssertCurrentThreadOwnsQuotaMutex(); NS_ASSERTION(!mOriginInfos.Contains(aOriginInfo), "Replacing an existing entry!"); mOriginInfos.AppendElement(aOriginInfo); mUsage += aOriginInfo->mUsage; - if (IsForTemporaryStorage()) { + if (aOriginInfo->IsTreatedAsTemporary()) { QuotaManager* quotaManager = QuotaManager::Get(); NS_ASSERTION(quotaManager, "Shouldn't be null!"); quotaManager->mTemporaryStorageUsage += aOriginInfo->mUsage; } } void GroupInfo::LockedRemoveOriginInfo(const nsACString& aOrigin) { AssertCurrentThreadOwnsQuotaMutex(); for (uint32_t index = 0; index < mOriginInfos.Length(); index++) { if (mOriginInfos[index]->mOrigin == aOrigin) { mUsage -= mOriginInfos[index]->mUsage; - if (IsForTemporaryStorage()) { + if (mOriginInfos[index]->IsTreatedAsTemporary()) { QuotaManager* quotaManager = QuotaManager::Get(); NS_ASSERTION(quotaManager, "Shouldn't be null!"); quotaManager->mTemporaryStorageUsage -= mOriginInfos[index]->mUsage; } mOriginInfos.RemoveElementAt(index); @@ -367,50 +397,108 @@ GroupInfo::LockedRemoveOriginInfo(const } void GroupInfo::LockedRemoveOriginInfos() { AssertCurrentThreadOwnsQuotaMutex(); for (uint32_t index = mOriginInfos.Length(); index > 0; index--) { - mUsage -= mOriginInfos[index - 1]->mUsage; + OriginInfo* originInfo = mOriginInfos[index - 1]; - if (IsForTemporaryStorage()) { + mUsage -= originInfo->mUsage; + + if (originInfo->IsTreatedAsTemporary()) { QuotaManager* quotaManager = QuotaManager::Get(); NS_ASSERTION(quotaManager, "Shouldn't be null!"); - quotaManager->mTemporaryStorageUsage -= mOriginInfos[index - 1]->mUsage; + quotaManager->mTemporaryStorageUsage -= originInfo->mUsage; } mOriginInfos.RemoveElementAt(index - 1); } } void GroupInfo::LockedRemoveOriginInfosForPattern(const nsACString& aPattern) { AssertCurrentThreadOwnsQuotaMutex(); for (uint32_t index = mOriginInfos.Length(); index > 0; index--) { if (PatternMatchesOrigin(aPattern, mOriginInfos[index - 1]->mOrigin)) { mUsage -= mOriginInfos[index - 1]->mUsage; - if (IsForTemporaryStorage()) { + if (mOriginInfos[index - 1]->IsTreatedAsTemporary()) { QuotaManager* quotaManager = QuotaManager::Get(); NS_ASSERTION(quotaManager, "Shouldn't be null!"); quotaManager->mTemporaryStorageUsage -= mOriginInfos[index - 1]->mUsage; } mOriginInfos.RemoveElementAt(index - 1); } } } +uint64_t +GroupInfo::LockedGetTemporaryUsage() +{ + uint64_t usage = 0; + + for (uint32_t count = mOriginInfos.Length(), index = 0; + index < count; + index++) { + nsRefPtr<OriginInfo>& originInfo = mOriginInfos[index]; + + if (originInfo->IsTreatedAsTemporary()) { + usage += originInfo->mUsage; + } + } + + return usage; +} + +void +GroupInfo::LockedGetTemporaryOriginInfos(nsTArray<OriginInfo*>* aOriginInfos) +{ + AssertCurrentThreadOwnsQuotaMutex(); + + for (uint32_t count = mOriginInfos.Length(), index = 0; + index < count; + index++) { + nsRefPtr<OriginInfo>& originInfo = mOriginInfos[index]; + + if (originInfo->IsTreatedAsTemporary()) { + aOriginInfos->AppendElement(originInfo); + } + } +} + +void +GroupInfo::LockedRemoveTemporaryOriginInfos() +{ + AssertCurrentThreadOwnsQuotaMutex(); + + QuotaManager* quotaManager = QuotaManager::Get(); + MOZ_ASSERT(quotaManager); + + for (uint32_t index = mOriginInfos.Length(); index > 0; index--) { + OriginInfo* originInfo = mOriginInfos[index - 1]; + if (originInfo->IsTreatedAsTemporary()) { + MOZ_ASSERT(mUsage >= originInfo->mUsage); + mUsage -= originInfo->mUsage; + + MOZ_ASSERT(quotaManager->mTemporaryStorageUsage >= originInfo->mUsage); + quotaManager->mTemporaryStorageUsage -= originInfo->mUsage; + + mOriginInfos.RemoveElementAt(index - 1); + } + } +} + nsRefPtr<GroupInfo>& GroupInfoPair::GetGroupInfoForPersistenceType(PersistenceType aPersistenceType) { switch (aPersistenceType) { case PERSISTENCE_TYPE_PERSISTENT: return mPersistentStorageGroupInfo; case PERSISTENCE_TYPE_TEMPORARY: return mTemporaryStorageGroupInfo;
--- a/dom/quota/QuotaObject.h +++ b/dom/quota/QuotaObject.h @@ -86,16 +86,22 @@ public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(OriginInfo) int64_t AccessTime() const { return mAccessTime; } + bool + IsTreatedAsPersistent() const; + + bool + IsTreatedAsTemporary() const; + private: // Private destructor, to discourage deletion outside of Release(): ~OriginInfo() { MOZ_COUNT_DTOR(OriginInfo); } void @@ -119,18 +125,18 @@ private: static PLDHashOperator ClearOriginInfoCallback(const nsAString& aKey, QuotaObject* aValue, void* aUserArg); nsDataHashtable<nsStringHashKey, QuotaObject*> mQuotaObjects; GroupInfo* mGroupInfo; - nsCString mOrigin; - uint64_t mLimit; + const nsCString mOrigin; + const uint64_t mLimit; uint64_t mUsage; int64_t mAccessTime; }; class OriginInfoLRUComparator { public: bool @@ -150,36 +156,26 @@ public: class GroupInfo MOZ_FINAL { friend class GroupInfoPair; friend class OriginInfo; friend class QuotaManager; friend class QuotaObject; public: - GroupInfo(PersistenceType aPersistenceType, const nsACString& aGroup) - : mPersistenceType(aPersistenceType), mGroup(aGroup), mUsage(0) + GroupInfo(GroupInfoPair* aGroupInfoPair, PersistenceType aPersistenceType, + const nsACString& aGroup) + : mGroupInfoPair(aGroupInfoPair), mPersistenceType(aPersistenceType), + mGroup(aGroup), mUsage(0) { MOZ_COUNT_CTOR(GroupInfo); } NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GroupInfo) - bool - IsForPersistentStorage() const - { - return mPersistenceType == PERSISTENCE_TYPE_PERSISTENT; - } - - bool - IsForTemporaryStorage() const - { - return mPersistenceType == PERSISTENCE_TYPE_TEMPORARY; - } - private: // Private destructor, to discourage deletion outside of Release(): ~GroupInfo() { MOZ_COUNT_DTOR(GroupInfo); } already_AddRefed<OriginInfo> @@ -200,26 +196,37 @@ private: bool LockedHasOriginInfos() { AssertCurrentThreadOwnsQuotaMutex(); return !mOriginInfos.IsEmpty(); } + uint64_t + LockedGetTemporaryUsage(); + + void + LockedGetTemporaryOriginInfos(nsTArray<OriginInfo*>* aOriginInfos); + + void + LockedRemoveTemporaryOriginInfos(); + nsTArray<nsRefPtr<OriginInfo> > mOriginInfos; + GroupInfoPair* mGroupInfoPair; PersistenceType mPersistenceType; nsCString mGroup; uint64_t mUsage; }; class GroupInfoPair { friend class QuotaManager; + friend class QuotaObject; public: GroupInfoPair() { MOZ_COUNT_CTOR(GroupInfoPair); } ~GroupInfoPair()
deleted file mode 100644 --- a/dom/quota/StoragePrivilege.h +++ /dev/null @@ -1,29 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_dom_quota_storageprivilege_h__ -#define mozilla_dom_quota_storageprivilege_h__ - -#include "mozilla/dom/quota/QuotaCommon.h" - -BEGIN_QUOTA_NAMESPACE - -enum StoragePrivilege { - // Quota not tracked, persistence type is always "persistent". - Chrome, - - // Quota tracked, persistence type can be either "persistent" or "temporary". - // The permission "defaul-persistent-storage" is used to determine the - // default persistence type. - Content, - - // Only needed for IPC serialization helper, should never be used in code. - Invalid -}; - -END_QUOTA_NAMESPACE - -#endif // mozilla_dom_quota_storageprivilege_h__
--- a/dom/quota/moz.build +++ b/dom/quota/moz.build @@ -21,17 +21,16 @@ EXPORTS.mozilla.dom.quota += [ 'ArrayCluster.h', 'Client.h', 'FileStreams.h', 'OriginOrPatternString.h', 'PersistenceType.h', 'QuotaCommon.h', 'QuotaManager.h', 'QuotaObject.h', - 'StoragePrivilege.h', 'UsageInfo.h', 'Utilities.h', ] UNIFIED_SOURCES += [ 'CheckQuotaHelper.cpp', 'FileStreams.cpp', 'QuotaManager.cpp',