diff --git a/netwerk/base/nsICachingChannel.idl b/netwerk/base/nsICachingChannel.idl index 7c6d2b5dba8..c0e1abbbd38 100644 --- a/netwerk/base/nsICachingChannel.idl +++ b/netwerk/base/nsICachingChannel.idl @@ -17,7 +17,7 @@ interface nsIFile; * 3) Support for uniquely identifying cached data in cases when the URL * is insufficient (e.g., HTTP form submission). */ -[scriptable, uuid(436b939d-e391-48e5-ba64-ab0e496e3400)] +[scriptable, uuid(dd1d6122-5ecf-4fe4-8f0f-995e7ab3121a)] interface nsICachingChannel : nsICacheInfoChannel { /** @@ -53,6 +53,11 @@ interface nsICachingChannel : nsICacheInfoChannel */ attribute boolean cacheOnlyMetadata; + /** + * Tells the channel to use the pinning storage. + */ + attribute boolean pin; + /************************************************************************** * Caching channel specific load flags: */ diff --git a/netwerk/cache2/CacheEntry.cpp b/netwerk/cache2/CacheEntry.cpp index 46d0adb04d6..c508388f817 100644 --- a/netwerk/cache2/CacheEntry.cpp +++ b/netwerk/cache2/CacheEntry.cpp @@ -163,7 +163,8 @@ NS_IMPL_ISUPPORTS(CacheEntry, CacheEntry::CacheEntry(const nsACString& aStorageID, nsIURI* aURI, const nsACString& aEnhanceID, - bool aUseDisk) + bool aUseDisk, + uint32_t aPinningAppId) : mFrecency(0) , mSortingExpirationTime(uint32_t(-1)) , mLock("CacheEntry") @@ -172,6 +173,7 @@ CacheEntry::CacheEntry(const nsACString& aStorageID, , mEnhanceID(aEnhanceID) , mStorageID(aStorageID) , mUseDisk(aUseDisk) +, mPinningAppId(aPinningAppId) , mIsDoomed(false) , mSecurityInfoLoaded(false) , mPreventCallbacks(false) @@ -341,7 +343,8 @@ bool CacheEntry::Load(bool aTruncate, bool aPriority) // as a new one. // 2. When this is a memory-only entry, check there is a disk file. // If there is or could be, doom that file. - if ((!aTruncate || !mUseDisk) && NS_SUCCEEDED(rv)) { + if ((!aTruncate || !mUseDisk) && NS_SUCCEEDED(rv) && + !mPinningAppId) { // Check the index right now to know we have or have not the entry // as soon as possible. CacheIndex::EntryStatus status; @@ -391,6 +394,7 @@ bool CacheEntry::Load(bool aTruncate, bool aPriority) rv = mFile->Init(fileKey, aTruncate, !mUseDisk, + mPinningAppId, aPriority, directLoad ? nullptr : this); } @@ -486,6 +490,7 @@ already_AddRefed CacheEntry::ReopenTruncated(bool aMemoryOnly, nsresult rv = CacheStorageService::Self()->AddStorageEntry( GetStorageID(), GetURI(), GetEnhanceID(), mUseDisk && !aMemoryOnly, + mPinningAppId, true, // always create true, // truncate existing (this one) getter_AddRefs(handle)); diff --git a/netwerk/cache2/CacheEntry.h b/netwerk/cache2/CacheEntry.h index c43c054d5c7..cb68fff50b2 100644 --- a/netwerk/cache2/CacheEntry.h +++ b/netwerk/cache2/CacheEntry.h @@ -55,7 +55,7 @@ public: NS_DECL_NSIRUNNABLE CacheEntry(const nsACString& aStorageID, nsIURI* aURI, const nsACString& aEnhanceID, - bool aUseDisk); + bool aUseDisk, uint32_t aPinningAppId); void AsyncOpen(nsICacheEntryOpenCallback* aCallback, uint32_t aFlags); @@ -276,6 +276,9 @@ private: // Whether it's allowed to persist the data to disk bool const mUseDisk; + // AppId of an app that wants this entry be pinned + uint32_t const mPinningAppId; + // Set when entry is doomed with AsyncDoom() or DoomAlreadyRemoved(). // Left as a standalone flag to not bother with locking (there is no need). bool mIsDoomed; diff --git a/netwerk/cache2/CacheFile.cpp b/netwerk/cache2/CacheFile.cpp index 0af6b3da33c..d83f86121c5 100644 --- a/netwerk/cache2/CacheFile.cpp +++ b/netwerk/cache2/CacheFile.cpp @@ -212,6 +212,7 @@ nsresult CacheFile::Init(const nsACString &aKey, bool aCreateNew, bool aMemoryOnly, + uint32_t aPinningAppID, bool aPriority, CacheFileListener *aCallback) { @@ -266,7 +267,7 @@ CacheFile::Init(const nsACString &aKey, mOpeningFile = true; mListener = aCallback; - rv = CacheFileIOManager::OpenFile(mKey, flags, this); + rv = CacheFileIOManager::OpenFile(mKey, aPinningAppID, flags, this); if (NS_FAILED(rv)) { mListener = nullptr; mOpeningFile = false; diff --git a/netwerk/cache2/CacheFile.h b/netwerk/cache2/CacheFile.h index 21e8281dd57..5fc44ed91dc 100644 --- a/netwerk/cache2/CacheFile.h +++ b/netwerk/cache2/CacheFile.h @@ -56,6 +56,7 @@ public: nsresult Init(const nsACString &aKey, bool aCreateNew, bool aMemoryOnly, + uint32_t aPinningAppID, bool aPriority, CacheFileListener *aCallback); @@ -103,6 +104,10 @@ public: void Key(nsACString& aKey) { aKey = mKey; } bool IsDoomed(); bool IsWriteInProgress(); + CacheFileIOManager* Manager() + { + return mHandle ? mHandle->Manager() : nullptr; + } // Memory reporting size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; diff --git a/netwerk/cache2/CacheFileContextEvictor.cpp b/netwerk/cache2/CacheFileContextEvictor.cpp index 561c7fa76ae..72b1231b074 100644 --- a/netwerk/cache2/CacheFileContextEvictor.cpp +++ b/netwerk/cache2/CacheFileContextEvictor.cpp @@ -398,7 +398,7 @@ CacheFileContextEvictor::GetContextFile(nsILoadContextInfo *aLoadContextInfo, leafName.AssignLiteral(CONTEXT_EVICTION_PREFIX); nsAutoCString keyPrefix; - CacheFileUtils::AppendKeyPrefix(aLoadContextInfo, keyPrefix); + CacheFileUtils::AppendKeyPrefix(aLoadContextInfo, false, keyPrefix); nsAutoCString data64; rv = Base64Encode(keyPrefix, data64); diff --git a/netwerk/cache2/CacheFileIOManager.cpp b/netwerk/cache2/CacheFileIOManager.cpp index 7cfa07eaf8e..8d7f24bfed6 100644 --- a/netwerk/cache2/CacheFileIOManager.cpp +++ b/netwerk/cache2/CacheFileIOManager.cpp @@ -108,8 +108,9 @@ NS_INTERFACE_MAP_BEGIN(CacheFileHandle) NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_END_THREADSAFE -CacheFileHandle::CacheFileHandle(const SHA1Sum::Hash *aHash, bool aPriority) - : mHash(aHash) +CacheFileHandle::CacheFileHandle(CacheFileIOManager* aManager, const SHA1Sum::Hash *aHash, bool aPriority) + : mManager(aManager) + , mHash(aHash) , mIsDoomed(false) , mPriority(aPriority) , mClosed(false) @@ -123,8 +124,9 @@ CacheFileHandle::CacheFileHandle(const SHA1Sum::Hash *aHash, bool aPriority) , this, LOGSHA1(aHash))); } -CacheFileHandle::CacheFileHandle(const nsACString &aKey, bool aPriority) - : mHash(nullptr) +CacheFileHandle::CacheFileHandle(CacheFileIOManager* aManager, const nsACString &aKey, bool aPriority) + : mManager(aManager) + , mHash(nullptr) , mIsDoomed(false) , mPriority(aPriority) , mClosed(false) @@ -145,9 +147,8 @@ CacheFileHandle::~CacheFileHandle() MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased()); - nsRefPtr ioMan = CacheFileIOManager::gInstance; - if (!IsClosed() && ioMan) { - ioMan->CloseHandleInternal(this); + if (!IsClosed() && mManager) { + mManager->CloseHandleInternal(this); } } @@ -295,7 +296,8 @@ CacheFileHandles::HandleHashKey::SizeOfExcludingThis(mozilla::MallocSizeOf mallo * CacheFileHandles *****************************************************************************/ -CacheFileHandles::CacheFileHandles() +CacheFileHandles::CacheFileHandles(CacheFileIOManager* aManager) + : mManager(aManager) { LOG(("CacheFileHandles::CacheFileHandles() [this=%p]", this)); MOZ_COUNT_CTOR(CacheFileHandles); @@ -376,7 +378,7 @@ CacheFileHandles::NewHandle(const SHA1Sum::Hash *aHash, entry->AssertHandlesState(); #endif - nsRefPtr handle = new CacheFileHandle(entry->Hash(), aPriority); + nsRefPtr handle = new CacheFileHandle(mManager, entry->Hash(), aPriority); entry->AddHandle(handle); LOG(("CacheFileHandles::NewHandle() hash=%08x%08x%08x%08x%08x " @@ -544,14 +546,15 @@ protected: class OpenFileEvent : public nsRunnable { public: - OpenFileEvent(const nsACString &aKey, uint32_t aFlags, + OpenFileEvent(CacheFileIOManager *aIOMan, + const nsACString &aKey, uint32_t aFlags, CacheFileIOListener *aCallback) : mFlags(aFlags) , mCallback(aCallback) + , mIOMan(aIOMan) , mKey(aKey) { MOZ_COUNT_CTOR(OpenFileEvent); - mIOMan = CacheFileIOManager::gInstance; } protected: @@ -629,7 +632,7 @@ public: if (mHandle->IsClosed()) { rv = NS_ERROR_NOT_INITIALIZED; } else { - rv = CacheFileIOManager::gInstance->ReadInternal( + rv = mHandle->Manager()->ReadInternal( mHandle, mOffset, mBuf, mCount); } @@ -679,11 +682,11 @@ public: if (mHandle->IsClosed()) { rv = NS_ERROR_NOT_INITIALIZED; } else { - rv = CacheFileIOManager::gInstance->WriteInternal( + rv = mHandle->Manager()->WriteInternal( mHandle, mOffset, mBuf, mCount, mValidate, mTruncate); if (NS_FAILED(rv) && !mCallback) { // No listener is going to handle the error, doom the file - CacheFileIOManager::gInstance->DoomFileInternal(mHandle); + mHandle->Manager()->DoomFileInternal(mHandle); } } if (mCallback) { @@ -730,7 +733,7 @@ public: if (mHandle->IsClosed()) { rv = NS_ERROR_NOT_INITIALIZED; } else { - rv = CacheFileIOManager::gInstance->DoomFileInternal(mHandle); + rv = mHandle->Manager()->DoomFileInternal(mHandle); } if (mCallback) { @@ -749,8 +752,10 @@ protected: class DoomFileByKeyEvent : public nsRunnable { public: DoomFileByKeyEvent(const nsACString &aKey, + CacheFileIOManager *aManager, CacheFileIOListener *aCallback) : mCallback(aCallback) + , mIOMan(aManager) { MOZ_COUNT_CTOR(DoomFileByKeyEvent); @@ -758,7 +763,7 @@ public: sum.update(aKey.BeginReading(), aKey.Length()); sum.finish(mHash); - mIOMan = CacheFileIOManager::gInstance; + mIOMan = aManager; } protected: @@ -810,7 +815,7 @@ public: NS_IMETHOD Run() { if (mHandle->mFD && !mHandle->IsClosed()) { - CacheFileIOManager::gInstance->ReleaseNSPRHandleInternal(mHandle); + mHandle->Manager()->ReleaseNSPRHandleInternal(mHandle); } return NS_OK; @@ -846,7 +851,7 @@ public: if (mHandle->IsClosed()) { rv = NS_ERROR_NOT_INITIALIZED; } else { - rv = CacheFileIOManager::gInstance->TruncateSeekSetEOFInternal( + rv = mHandle->Manager()->TruncateSeekSetEOFInternal( mHandle, mTruncatePos, mEOFPos); } @@ -889,8 +894,7 @@ public: if (mHandle->IsClosed()) { rv = NS_ERROR_NOT_INITIALIZED; } else { - rv = CacheFileIOManager::gInstance->RenameFileInternal(mHandle, - mNewName); + rv = mHandle->Manager()->RenameFileInternal(mHandle, mNewName); } if (mCallback) { @@ -1021,8 +1025,7 @@ public: NS_IMETHOD Run() { - nsRefPtr ioMan = CacheFileIOManager::gInstance; - if (!ioMan) { + if (!mIOMan) { NS_WARNING("CacheFileIOManager already gone in MetadataWriteScheduleEvent::Run()"); return NS_OK; } @@ -1030,13 +1033,13 @@ public: switch (mMode) { case SCHEDULE: - ioMan->ScheduleMetadataWriteInternal(mFile); + mIOMan->ScheduleMetadataWriteInternal(mFile); break; case UNSCHEDULE: - ioMan->UnscheduleMetadataWriteInternal(mFile); + mIOMan->UnscheduleMetadataWriteInternal(mFile); break; case SHUTDOWN: - ioMan->ShutdownMetadataWriteSchedulingInternal(); + mIOMan->ShutdownMetadataWriteSchedulingInternal(); break; } return NS_OK; @@ -1048,20 +1051,19 @@ CacheFileIOManager * CacheFileIOManager::gInstance = nullptr; NS_IMPL_ISUPPORTS(CacheFileIOManager, nsITimerCallback) CacheFileIOManager::CacheFileIOManager() - : mShuttingDown(false) + : mKind(UNKNOWN) + , mShuttingDown(false) , mTreeCreated(false) + , mHandles(this) , mOverLimitEvicting(false) , mRemovingTrashDirs(false) { LOG(("CacheFileIOManager::CacheFileIOManager [this=%p]", this)); - MOZ_COUNT_CTOR(CacheFileIOManager); - MOZ_ASSERT(!gInstance, "multiple CacheFileIOManager instances!"); } CacheFileIOManager::~CacheFileIOManager() { LOG(("CacheFileIOManager::~CacheFileIOManager [this=%p]", this)); - MOZ_COUNT_DTOR(CacheFileIOManager); } // static @@ -1082,6 +1084,10 @@ CacheFileIOManager::Init() NS_ENSURE_SUCCESS(rv, rv); ioMan.swap(gInstance); + + // Ensure pinning managers static to avoid concurrent thread initiation. + Pinning::Self(); + return NS_OK; } @@ -1090,6 +1096,7 @@ CacheFileIOManager::InitInternal() { nsresult rv; + mKind = GENERAL; mIOThread = new CacheIOThread(); rv = mIOThread->Init(); @@ -1101,6 +1108,38 @@ CacheFileIOManager::InitInternal() return NS_OK; } +nsresult +CacheFileIOManager::InitAsPinning(uint32_t aAppId, nsIFile* aProfileDir) +{ + if (!aProfileDir || !gInstance) { + return NS_ERROR_NOT_INITIALIZED; + } + + mKind = PINNING; + + MOZ_ASSERT(!mCacheDirectory); + MOZ_ASSERT(!mIOThread); + + nsresult rv; + + rv = aProfileDir->Clone(getter_AddRefs(mCacheDirectory)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mCacheDirectory->Append(NS_LITERAL_STRING("cache2")); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoString appIdString; + appIdString.AppendInt(aAppId); + rv = mCacheDirectory->Append(appIdString); + NS_ENSURE_SUCCESS(rv, rv); + + mIOThread = gInstance->mIOThread; + + mStartTime = TimeStamp::NowLoRes(); + + return NS_OK; +} + // static nsresult CacheFileIOManager::Shutdown() @@ -1133,6 +1172,7 @@ CacheFileIOManager::Shutdown() MOZ_ASSERT(gInstance->mHandles.HandleCount() == 0); MOZ_ASSERT(gInstance->mHandlesByLastUsed.Length() == 0); + MOZ_ASSERT(Pinning::Self()->Length() == 0); if (gInstance->mIOThread) { gInstance->mIOThread->Shutdown(); @@ -1143,11 +1183,14 @@ CacheFileIOManager::Shutdown() if (CacheObserver::ClearCacheOnShutdown()) { Telemetry::AutoTimer totalTimer; gInstance->SyncRemoveAllCacheFiles(); + // mayhemer: should this apply to pinning apps as well? followup sufficient... } nsRefPtr ioMan; ioMan.swap(gInstance); + Pinning::Self()->Destroy(); + return NS_OK; } @@ -1184,7 +1227,8 @@ CacheFileIOManager::ShutdownInternal() } if (!h->IsSpecialFile() && !h->mIsDoomed && - (h->mInvalid || !h->mFileExists)) { + (h->mInvalid || !h->mFileExists) && + mKind != PINNING) { CacheIndex::RemoveEntry(h->Hash()); } @@ -1215,6 +1259,8 @@ CacheFileIOManager::ShutdownInternal() mTrashDirEnumerator = nullptr; } + Pinning::Self()->ShutdownInternal(); + return NS_OK; } @@ -1299,6 +1345,8 @@ CacheFileIOManager::OnProfile() CacheIndex::Init(ioMan->mCacheDirectory); } + Pinning::Self()->OnProfile(); + return NS_OK; } @@ -1365,9 +1413,11 @@ CacheFileIOManager::IsShutdown() nsresult CacheFileIOManager::ScheduleMetadataWrite(CacheFile * aFile) { + // This can freely go to the global IO manager, no need for distinction + // since the timer code calls on the file that delegates to its handle + // which is already correctly bound to the correct manager. nsRefPtr ioMan = gInstance; NS_ENSURE_TRUE(ioMan, NS_ERROR_NOT_INITIALIZED); - NS_ENSURE_TRUE(!ioMan->mShuttingDown, NS_ERROR_NOT_INITIALIZED); nsRefPtr event = new MetadataWriteScheduleEvent( @@ -1409,7 +1459,6 @@ CacheFileIOManager::UnscheduleMetadataWrite(CacheFile * aFile) { nsRefPtr ioMan = gInstance; NS_ENSURE_TRUE(ioMan, NS_ERROR_NOT_INITIALIZED); - NS_ENSURE_TRUE(!ioMan->mShuttingDown, NS_ERROR_NOT_INITIALIZED); nsRefPtr event = new MetadataWriteScheduleEvent( @@ -1472,38 +1521,63 @@ CacheFileIOManager::ShutdownMetadataWriteSchedulingInternal() NS_IMETHODIMP CacheFileIOManager::Notify(nsITimer * aTimer) { - MOZ_ASSERT(IsOnIOThreadOrCeased()); - MOZ_ASSERT(mMetadataWritesTimer == aTimer); + if (mTrashTimer == aTimer) { + LOG(("CacheFileIOManager, trash timer [this=%p]", this)); - mMetadataWritesTimer = nullptr; + mTrashTimer = nullptr; + StartRemovingTrash(); - nsTArray > files; - files.SwapElements(mScheduledMetadataWrites); - for (uint32_t i = 0; i < files.Length(); ++i) { - CacheFile * file = files[i]; - file->WriteMetadataIfNeeded(); + return NS_OK; } - return NS_OK; + if (mMetadataWritesTimer == aTimer) { + LOG(("CacheFileIOManager, metadata write timer [this=%p]", this)); + + MOZ_ASSERT(IsOnIOThreadOrCeased()); + mMetadataWritesTimer = nullptr; + + nsTArray > files; + files.SwapElements(mScheduledMetadataWrites); + for (uint32_t i = 0; i < files.Length(); ++i) { + CacheFile * file = files[i]; + file->WriteMetadataIfNeeded(); + } + + return NS_OK; + } + + MOZ_ASSERT(false, "Unexpected timer in CacheFileIOManager::Notify()!"); + return NS_ERROR_UNEXPECTED; } // static nsresult CacheFileIOManager::OpenFile(const nsACString &aKey, uint32_t aFlags, CacheFileIOListener *aCallback) +{ + return OpenFile(aKey, nsILoadContextInfo::NO_APP_ID, aFlags, aCallback); +} + +// static +nsresult +CacheFileIOManager::OpenFile(const nsACString &aKey, uint32_t aPinningAppId, + uint32_t aFlags, CacheFileIOListener *aCallback) { LOG(("CacheFileIOManager::OpenFile() [key=%s, flags=%d, listener=%p]", PromiseFlatCString(aKey).get(), aFlags, aCallback)); nsresult rv; nsRefPtr ioMan = gInstance; + if (aPinningAppId != nsILoadContextInfo::NO_APP_ID && ioMan) { + ioMan = Pinning::Self()->Get(aPinningAppId); + } if (!ioMan) { return NS_ERROR_NOT_INITIALIZED; } bool priority = aFlags & CacheFileIOManager::PRIORITY; - nsRefPtr ev = new OpenFileEvent(aKey, aFlags, aCallback); + nsRefPtr ev = new OpenFileEvent(ioMan, aKey, aFlags, aCallback); rv = ioMan->mIOThread->Dispatch(ev, priority ? CacheIOThread::OPEN_PRIORITY : CacheIOThread::OPEN); @@ -1557,7 +1631,9 @@ CacheFileIOManager::OpenFileInternal(const SHA1Sum::Hash *aHash, NS_ENSURE_SUCCESS(rv, rv); if (exists) { - CacheIndex::RemoveEntry(aHash); + if (mKind != PINNING) { + CacheIndex::RemoveEntry(aHash); + } LOG(("CacheFileIOManager::OpenFileInternal() - Removing old file from " "disk")); @@ -1569,7 +1645,10 @@ CacheFileIOManager::OpenFileInternal(const SHA1Sum::Hash *aHash, } } - CacheIndex::AddEntry(aHash); + if (mKind != PINNING) { + CacheIndex::AddEntry(aHash); + } + handle->mFile.swap(file); handle->mFileSize = 0; } @@ -1594,7 +1673,10 @@ CacheFileIOManager::OpenFileInternal(const SHA1Sum::Hash *aHash, "entry was evicted by EvictByContext()")); exists = false; file->Remove(false); - CacheIndex::RemoveEntry(aHash); + + if (mKind != PINNING) { + CacheIndex::RemoveEntry(aHash); + } } } } @@ -1612,11 +1694,15 @@ CacheFileIOManager::OpenFileInternal(const SHA1Sum::Hash *aHash, handle->mFileExists = true; - CacheIndex::EnsureEntryExists(aHash); + if (mKind != PINNING) { + CacheIndex::EnsureEntryExists(aHash); + } } else { handle->mFileSize = 0; - CacheIndex::AddEntry(aHash); + if (mKind != PINNING) { + CacheIndex::AddEntry(aHash); + } } handle->mFile.swap(file); @@ -1664,7 +1750,7 @@ CacheFileIOManager::OpenSpecialFileInternal(const nsACString &aKey, handle = nullptr; } - handle = new CacheFileHandle(aKey, aFlags & PRIORITY); + handle = new CacheFileHandle(this, aKey, aFlags & PRIORITY); mSpecialHandles.AppendElement(handle); bool exists; @@ -1699,7 +1785,7 @@ CacheFileIOManager::OpenSpecialFileInternal(const nsACString &aKey, return NS_ERROR_NOT_AVAILABLE; } - handle = new CacheFileHandle(aKey, aFlags & PRIORITY); + handle = new CacheFileHandle(this, aKey, aFlags & PRIORITY); mSpecialHandles.AppendElement(handle); if (exists) { @@ -1740,7 +1826,8 @@ CacheFileIOManager::CloseHandleInternal(CacheFileHandle *aHandle) } if (!aHandle->IsSpecialFile() && !aHandle->mIsDoomed && - (aHandle->mInvalid || !aHandle->mFileExists)) { + (aHandle->mInvalid || !aHandle->mFileExists) && + mKind != PINNING) { CacheIndex::RemoveEntry(aHandle->Hash()); } @@ -1766,7 +1853,7 @@ CacheFileIOManager::Read(CacheFileHandle *aHandle, int64_t aOffset, "listener=%p]", aHandle, aOffset, aCount, aCallback)); nsresult rv; - nsRefPtr ioMan = gInstance; + nsRefPtr ioMan = aHandle->Manager(); if (aHandle->IsClosed() || !ioMan) { return NS_ERROR_NOT_INITIALIZED; @@ -1833,7 +1920,7 @@ CacheFileIOManager::Write(CacheFileHandle *aHandle, int64_t aOffset, aValidate, aTruncate, aCallback)); nsresult rv; - nsRefPtr ioMan = gInstance; + nsRefPtr ioMan = aHandle->Manager(); if (aHandle->IsClosed() || !ioMan) { if (!aCallback) { @@ -1950,7 +2037,7 @@ CacheFileIOManager::WriteInternal(CacheFileHandle *aHandle, int64_t aOffset, uint32_t newSizeInK = aHandle->FileSizeInK(); if (oldSizeInK != newSizeInK && !aHandle->IsDoomed() && - !aHandle->IsSpecialFile()) { + !aHandle->IsSpecialFile() && mKind != PINNING) { CacheIndex::UpdateEntry(aHandle->Hash(), nullptr, nullptr, &newSizeInK); if (oldSizeInK < newSizeInK) { @@ -1980,7 +2067,7 @@ CacheFileIOManager::DoomFile(CacheFileHandle *aHandle, aHandle, aCallback)); nsresult rv; - nsRefPtr ioMan = gInstance; + nsRefPtr ioMan = aHandle->Manager(); if (aHandle->IsClosed() || !ioMan) { return NS_ERROR_NOT_INITIALIZED; @@ -2037,7 +2124,7 @@ CacheFileIOManager::DoomFileInternal(CacheFileHandle *aHandle) } } - if (!aHandle->IsSpecialFile()) { + if (!aHandle->IsSpecialFile() && mKind != PINNING) { CacheIndex::RemoveEntry(aHandle->Hash()); } @@ -2046,12 +2133,12 @@ CacheFileIOManager::DoomFileInternal(CacheFileHandle *aHandle) if (!aHandle->IsSpecialFile()) { nsRefPtr storageService = CacheStorageService::Self(); if (storageService) { - nsAutoCString idExtension, url; + CacheFileUtils::KeyInfo keyInfo; nsCOMPtr info = - CacheFileUtils::ParseKey(aHandle->Key(), &idExtension, &url); + CacheFileUtils::ParseKey(aHandle->Key(), &keyInfo); MOZ_ASSERT(info); if (info) { - storageService->CacheFileDoomed(info, idExtension, url); + storageService->CacheFileDoomed(info, &keyInfo); } } } @@ -2067,14 +2154,21 @@ CacheFileIOManager::DoomFileByKey(const nsACString &aKey, LOG(("CacheFileIOManager::DoomFileByKey() [key=%s, listener=%p]", PromiseFlatCString(aKey).get(), aCallback)); + CacheFileUtils::KeyInfo keyInfo; + nsCOMPtr info = CacheFileUtils::ParseKey(aKey, &keyInfo); + nsresult rv; nsRefPtr ioMan = gInstance; + if (keyInfo.mPinningStorage && ioMan) { + ioMan = Pinning::Self()->Get(info->AppId()); + } + if (!ioMan) { return NS_ERROR_NOT_INITIALIZED; } - nsRefPtr ev = new DoomFileByKeyEvent(aKey, aCallback); + nsRefPtr ev = new DoomFileByKeyEvent(aKey, ioMan, aCallback); rv = ioMan->mIOThread->DispatchAfterPendingOpens(ev); NS_ENSURE_SUCCESS(rv, rv); @@ -2131,7 +2225,9 @@ CacheFileIOManager::DoomFileByKeyInternal(const SHA1Sum::Hash *aHash) "[rv=0x%08x]", rv)); } - CacheIndex::RemoveEntry(aHash); + if (mKind != PINNING) { + CacheIndex::RemoveEntry(aHash); + } return NS_OK; } @@ -2143,7 +2239,7 @@ CacheFileIOManager::ReleaseNSPRHandle(CacheFileHandle *aHandle) LOG(("CacheFileIOManager::ReleaseNSPRHandle() [handle=%p]", aHandle)); nsresult rv; - nsRefPtr ioMan = gInstance; + nsRefPtr ioMan = aHandle->Manager(); if (aHandle->IsClosed() || !ioMan) { return NS_ERROR_NOT_INITIALIZED; @@ -2165,7 +2261,7 @@ CacheFileIOManager::ReleaseNSPRHandleInternal(CacheFileHandle *aHandle) MOZ_ASSERT(aHandle->mFD); DebugOnly found; - found = mHandlesByLastUsed.RemoveElement(aHandle); + found = gInstance->mHandlesByLastUsed.RemoveElement(aHandle); MOZ_ASSERT(found); PR_Close(aHandle->mFD); @@ -2184,7 +2280,7 @@ CacheFileIOManager::TruncateSeekSetEOF(CacheFileHandle *aHandle, "EOFPos=%lld, listener=%p]", aHandle, aTruncatePos, aEOFPos, aCallback)); nsresult rv; - nsRefPtr ioMan = gInstance; + nsRefPtr ioMan = aHandle->Manager(); if (aHandle->IsClosed() || !ioMan) { return NS_ERROR_NOT_INITIALIZED; @@ -2238,19 +2334,19 @@ CacheFileIOManager::GetEntryInfo(const SHA1Sum::Hash *aHash, nsresult rv; + // mayhemer: This method should take aPinningAppId argument. nsRefPtr ioMan = gInstance; if (!ioMan) { return NS_ERROR_NOT_INITIALIZED; } - nsAutoCString enhanceId; - nsAutoCString uriSpec; + CacheFileUtils::KeyInfo keyInfo; nsRefPtr handle; ioMan->mHandles.GetHandle(aHash, getter_AddRefs(handle)); if (handle) { nsRefPtr info = - CacheFileUtils::ParseKey(handle->Key(), &enhanceId, &uriSpec); + CacheFileUtils::ParseKey(handle->Key(), &keyInfo); MOZ_ASSERT(info); if (!info) { @@ -2263,7 +2359,7 @@ CacheFileIOManager::GetEntryInfo(const SHA1Sum::Hash *aHash, } // Invokes OnCacheEntryInfo when an existing entry is found - if (service->GetCacheEntryInfo(info, enhanceId, uriSpec, aCallback)) { + if (service->GetCacheEntryInfo(info, &keyInfo, aCallback)) { return NS_OK; } @@ -2287,7 +2383,7 @@ CacheFileIOManager::GetEntryInfo(const SHA1Sum::Hash *aHash, metadata->GetKey(key); nsRefPtr info = - CacheFileUtils::ParseKey(key, &enhanceId, &uriSpec); + CacheFileUtils::ParseKey(key, &keyInfo); MOZ_ASSERT(info); if (!info) { return NS_OK; @@ -2309,8 +2405,8 @@ CacheFileIOManager::GetEntryInfo(const SHA1Sum::Hash *aHash, } // Call directly on the callback. - aCallback->OnEntryInfo(uriSpec, enhanceId, dataSize, fetchCount, - lastModified, expirationTime); + aCallback->OnEntryInfo(keyInfo.mURISpec, keyInfo.mIdEnhance, + dataSize, fetchCount, lastModified, expirationTime); return NS_OK; } @@ -2397,7 +2493,7 @@ CacheFileIOManager::RenameFile(CacheFileHandle *aHandle, aHandle, PromiseFlatCString(aNewName).get(), aCallback)); nsresult rv; - nsRefPtr ioMan = gInstance; + nsRefPtr ioMan = aHandle->Manager(); if (aHandle->IsClosed() || !ioMan) { return NS_ERROR_NOT_INITIALIZED; @@ -2508,6 +2604,10 @@ CacheFileIOManager::EvictIfOverLimitInternal() MOZ_ASSERT(mIOThread->IsCurrentThread()); + if (mKind == PINNING) { + return NS_ERROR_NOT_IMPLEMENTED; + } + if (mShuttingDown) { return NS_ERROR_NOT_INITIALIZED; } @@ -2569,6 +2669,7 @@ CacheFileIOManager::OverLimitEvictionInternal() nsresult rv; MOZ_ASSERT(mIOThread->IsCurrentThread()); + MOZ_ASSERT(mKind != PINNING); // mOverLimitEvicting is accessed only on IO thread, so we can set it to false // here and set it to true again once we dispatch another event that will @@ -2701,6 +2802,31 @@ CacheFileIOManager::EvictAll() return NS_OK; } +// static +nsresult +CacheFileIOManager::EvictPinned(uint32_t aPinningAppId) +{ + LOG(("CacheFileIOManager::EvictPinned(appId=%d)", aPinningAppId)); + + nsRefPtr ioMan = gInstance; + if (ioMan) { + ioMan = Pinning::Self()->Get(aPinningAppId); + } + if (!ioMan) { + return NS_ERROR_NOT_INITIALIZED; + } + + nsCOMPtr ev; + ev = NS_NewRunnableMethod(ioMan, &CacheFileIOManager::EvictAllInternal); + + nsresult rv = ioMan->mIOThread->DispatchAfterPendingOpens(ev); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + namespace { class EvictionNotifierRunnable : public nsRunnable @@ -2724,7 +2850,7 @@ EvictionNotifierRunnable::Run() nsresult CacheFileIOManager::EvictAllInternal() { - LOG(("CacheFileIOManager::EvictAllInternal()")); + LOG(("CacheFileIOManager::EvictAllInternal(), this=%p", this)); nsresult rv; @@ -2789,18 +2915,29 @@ CacheFileIOManager::EvictAllInternal() return rv; } - CacheIndex::RemoveAll(); + if (mKind != PINNING) { + CacheIndex::RemoveAll(); + } return NS_OK; } // static nsresult -CacheFileIOManager::EvictByContext(nsILoadContextInfo *aLoadContextInfo) +CacheFileIOManager::EvictByContext(nsILoadContextInfo *aLoadContextInfo, + bool aIsPinning) { LOG(("CacheFileIOManager::EvictByContext() [loadContextInfo=%p]", aLoadContextInfo)); + if (aIsPinning) { + // This is a kinda hacky workaround, we simply delete everything that + // the app has pinned with disrespect to the load context separation. + // This API is dependent on the index, but index is global and only + // works for the general manager. + return CacheFileIOManager::EvictPinned(aLoadContextInfo->AppId()); + } + nsresult rv; nsRefPtr ioMan = gInstance; @@ -2937,7 +3074,7 @@ CacheFileIOManager::TrashDirectory(nsIFile *aFile) { nsAutoCString path; aFile->GetNativePath(path); - LOG(("CacheFileIOManager::TrashDirectory() [file=%s]", path.get())); + LOG(("CacheFileIOManager::TrashDirectory() [this=%p, file=%s]", this, path.get())); nsresult rv; @@ -3001,23 +3138,6 @@ CacheFileIOManager::TrashDirectory(nsIFile *aFile) return NS_OK; } -// static -void -CacheFileIOManager::OnTrashTimer(nsITimer *aTimer, void *aClosure) -{ - LOG(("CacheFileIOManager::OnTrashTimer() [timer=%p, closure=%p]", aTimer, - aClosure)); - - nsRefPtr ioMan = gInstance; - - if (!ioMan) { - return; - } - - ioMan->mTrashTimer = nullptr; - ioMan->StartRemovingTrash(); -} - nsresult CacheFileIOManager::StartRemovingTrash() { @@ -3057,9 +3177,8 @@ CacheFileIOManager::StartRemovingTrash() rv = timer->SetTarget(ioTarget); NS_ENSURE_SUCCESS(rv, rv); - rv = timer->InitWithFuncCallback(CacheFileIOManager::OnTrashTimer, nullptr, - kRemoveTrashStartDelay - elapsed, - nsITimer::TYPE_ONE_SHOT); + rv = timer->InitWithCallback(this, kRemoveTrashStartDelay - elapsed, + nsITimer::TYPE_ONE_SHOT); NS_ENSURE_SUCCESS(rv, rv); mTrashTimer.swap(timer); @@ -3260,12 +3379,16 @@ CacheFileIOManager::InitIndexEntry(CacheFileHandle *aHandle, ", inBrowser=%d]", aHandle, aAppId, aAnonymous, aInBrowser)); nsresult rv; - nsRefPtr ioMan = gInstance; + nsRefPtr ioMan = aHandle->Manager(); if (aHandle->IsClosed() || !ioMan) { return NS_ERROR_NOT_INITIALIZED; } + if (ioMan->Kind() == PINNING) { + return NS_ERROR_NOT_IMPLEMENTED; + } + if (aHandle->IsSpecialFile()) { return NS_ERROR_UNEXPECTED; } @@ -3290,12 +3413,16 @@ CacheFileIOManager::UpdateIndexEntry(CacheFileHandle *aHandle, aExpirationTime ? nsPrintfCString("%u", *aExpirationTime).get() : "")); nsresult rv; - nsRefPtr ioMan = gInstance; + nsRefPtr ioMan = aHandle->Manager(); if (aHandle->IsClosed() || !ioMan) { return NS_ERROR_NOT_INITIALIZED; } + if (ioMan->Kind() == PINNING) { + return NS_ERROR_NOT_IMPLEMENTED; + } + if (aHandle->IsSpecialFile()) { return NS_ERROR_UNEXPECTED; } @@ -3623,16 +3750,16 @@ CacheFileIOManager::OpenNSPRHandle(CacheFileHandle *aHandle, bool aCreate) { MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased()); MOZ_ASSERT(!aHandle->mFD); - MOZ_ASSERT(mHandlesByLastUsed.IndexOf(aHandle) == mHandlesByLastUsed.NoIndex); - MOZ_ASSERT(mHandlesByLastUsed.Length() <= kOpenHandlesLimit); + MOZ_ASSERT(gInstance->mHandlesByLastUsed.IndexOf(aHandle) == gInstance->mHandlesByLastUsed.NoIndex); + MOZ_ASSERT(gInstance->mHandlesByLastUsed.Length() <= kOpenHandlesLimit); MOZ_ASSERT((aCreate && !aHandle->mFileExists) || (!aCreate && aHandle->mFileExists)); nsresult rv; - if (mHandlesByLastUsed.Length() == kOpenHandlesLimit) { + if (gInstance->mHandlesByLastUsed.Length() == kOpenHandlesLimit) { // close handle that hasn't been used for the longest time - rv = ReleaseNSPRHandleInternal(mHandlesByLastUsed[0]); + rv = ReleaseNSPRHandleInternal(gInstance->mHandlesByLastUsed[0]); NS_ENSURE_SUCCESS(rv, rv); } @@ -3689,7 +3816,7 @@ CacheFileIOManager::OpenNSPRHandle(CacheFileHandle *aHandle, bool aCreate) NS_ENSURE_SUCCESS(rv, rv); } - mHandlesByLastUsed.AppendElement(aHandle); + gInstance->mHandlesByLastUsed.AppendElement(aHandle); return NS_OK; } @@ -3700,10 +3827,10 @@ CacheFileIOManager::NSPRHandleUsed(CacheFileHandle *aHandle) MOZ_ASSERT(aHandle->mFD); DebugOnly found; - found = mHandlesByLastUsed.RemoveElement(aHandle); + found = gInstance->mHandlesByLastUsed.RemoveElement(aHandle); MOZ_ASSERT(found); - mHandlesByLastUsed.AppendElement(aHandle); + gInstance->mHandlesByLastUsed.AppendElement(aHandle); } nsresult @@ -3843,6 +3970,10 @@ CacheFileIOManager::UpdateSmartCacheSize(int64_t aFreeSpace) return NS_ERROR_NOT_AVAILABLE; } + if (mKind == PINNING) { + return NS_ERROR_NOT_AVAILABLE; + } + // Wait at least kSmartSizeUpdateInterval before recomputing smart size. static const TimeDuration kUpdateLimit = TimeDuration::FromMilliseconds(kSmartSizeUpdateInterval); @@ -3984,9 +4115,19 @@ CacheFileIOManager::SizeOfExcludingThisInternal(mozilla::MallocSizeOf mallocSize n += mFailedTrashDirs[i].SizeOfExcludingThisIfUnshared(mallocSizeOf); } + if (mKind == GENERAL) { + Pinning::Self()->SizeOfIncludingThis(mallocSizeOf); + } + return n; } +size_t +CacheFileIOManager::SizeOfIncludingThisInternal(mozilla::MallocSizeOf mallocSizeOf) const +{ + return mallocSizeOf(this) + SizeOfExcludingThisInternal(mallocSizeOf); +} + // static size_t CacheFileIOManager::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) @@ -4004,5 +4145,128 @@ CacheFileIOManager::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) return mallocSizeOf(gInstance) + SizeOfExcludingThis(mallocSizeOf); } +// CacheFileIOManager::Pinning + +CacheFileIOManager::Pinning* +CacheFileIOManager::Pinning::sSelf = nullptr; + +bool +CacheFileIOManager::Pinning::sDestroyed = false; + +CacheFileIOManager::Pinning::Pinning() + : mLock("CacheFileIOManager::Pinning") +{ + MOZ_ASSERT(!sSelf); + sSelf = this; + MOZ_COUNT_CTOR(CacheFileIOManager::Pinning); +} + +CacheFileIOManager::Pinning::~Pinning() +{ + MOZ_ASSERT(sSelf); + sSelf = nullptr; + MOZ_COUNT_DTOR(CacheFileIOManager::Pinning); +} + +// static +CacheFileIOManager::Pinning* +CacheFileIOManager::Pinning::Self() +{ + MOZ_ASSERT(!sDestroyed); + if (!sSelf) { + sSelf = new Pinning(); + } + return sSelf; +} + +void +CacheFileIOManager::Pinning::Destroy() +{ + delete sSelf; + sDestroyed = true; +} + +nsresult +CacheFileIOManager::Pinning::OnProfile() +{ + nsresult rv; + + rv = NS_GetSpecialDirectory( + NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(mRoamingProfileDir)); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +already_AddRefed +CacheFileIOManager::Pinning::Get(uint32_t aAppId) +{ + mozilla::MutexAutoLock lock(mLock); + + nsRefPtr pinningIOMan; + if (!mTable.Get(aAppId, getter_AddRefs(pinningIOMan))) { + pinningIOMan = new CacheFileIOManager(); + LOG(("CacheFileIOManager::Pinning::Get, new manager %p for appid=%u", + pinningIOMan.get(), aAppId)); + + nsresult rv = pinningIOMan->InitAsPinning(aAppId, mRoamingProfileDir); + if (NS_FAILED(rv)) { + return nullptr; + } + mTable.Put(aAppId, pinningIOMan); + } + + return pinningIOMan.forget(); +} + +// static +PLDHashOperator +CacheFileIOManager::Pinning::Collect(uint32_t const& aAppId, + nsRefPtr& aIoMan, + void* aClosure) +{ + nsTArray >* managers = + static_cast >*>(aClosure); + + managers->AppendElement()->swap(aIoMan); + return PL_DHASH_REMOVE; +} + +void +CacheFileIOManager::Pinning::ShutdownInternal() +{ + nsTArray > managers; + { + mozilla::MutexAutoLock lock(mLock); + mTable.Enumerate(&Pinning::Collect, &managers); + } + + for (uint32_t i = 0; i < managers.Length(); ++i) { + CacheFileIOManager* ioMan = managers[i]; + ioMan->ShutdownInternal(); + } +} + +static +size_t CollectManagerMemory(uint32_t const & aAppId, + nsRefPtr const & aManager, + mozilla::MallocSizeOf mallocSizeOf, + void * aClosure) +{ + return aManager->SizeOfIncludingThisInternal(mallocSizeOf); +} + +size_t +CacheFileIOManager::Pinning::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const +{ + return mTable.SizeOfExcludingThis(&CollectManagerMemory, mallocSizeOf, nullptr); +} + +size_t +CacheFileIOManager::Pinning::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const +{ + return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf); +} + } // namespace net } // namespace mozilla diff --git a/netwerk/cache2/CacheFileIOManager.h b/netwerk/cache2/CacheFileIOManager.h index aaae0a790b6..1ad3e46b558 100644 --- a/netwerk/cache2/CacheFileIOManager.h +++ b/netwerk/cache2/CacheFileIOManager.h @@ -15,6 +15,7 @@ #include "nsTArray.h" #include "nsString.h" #include "nsTHashtable.h" +#include "nsRefPtrHashtable.h" #include "prio.h" //#define DEBUG_HANDLES 1 @@ -28,6 +29,7 @@ namespace mozilla { namespace net { class CacheFile; +class CacheFileIOManager; #ifdef DEBUG_HANDLES class CacheFileHandlesEntry; #endif @@ -43,8 +45,8 @@ public: NS_DECL_THREADSAFE_ISUPPORTS bool DispatchRelease(); - CacheFileHandle(const SHA1Sum::Hash *aHash, bool aPriority); - CacheFileHandle(const nsACString &aKey, bool aPriority); + CacheFileHandle(CacheFileIOManager* aManager, const SHA1Sum::Hash *aHash, bool aPriority); + CacheFileHandle(CacheFileIOManager* aManager, const nsACString &aKey, bool aPriority); CacheFileHandle(const CacheFileHandle &aOther); void Log(); bool IsDoomed() const { return mIsDoomed; } @@ -56,6 +58,7 @@ public: bool IsClosed() const { return mClosed; } bool IsSpecialFile() const { return mSpecialFile; } nsCString & Key() { return mKey; } + CacheFileIOManager* Manager() const { return mManager; } // Memory reporting size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; @@ -68,6 +71,8 @@ private: virtual ~CacheFileHandle(); + nsRefPtr mManager; + const SHA1Sum::Hash *mHash; bool mIsDoomed; bool mPriority; @@ -86,7 +91,7 @@ private: class CacheFileHandles { public: - CacheFileHandles(); + CacheFileHandles(CacheFileIOManager* aManager); ~CacheFileHandles(); nsresult GetHandle(const SHA1Sum::Hash *aHash, CacheFileHandle **_retval); @@ -168,6 +173,7 @@ public: private: nsTHashtable mTable; + CacheFileIOManager* mManager; }; //////////////////////////////////////////////////////////////////////////////// @@ -218,6 +224,12 @@ public: SPECIAL_FILE = 8U }; + enum EKind { + UNKNOWN, + GENERAL, + PINNING + }; + CacheFileIOManager(); static nsresult Init(); @@ -229,6 +241,8 @@ public: static bool IsOnIOThreadOrCeased(); static bool IsShutdown(); + EKind Kind() const { return mKind; } + // Make aFile's WriteMetadataIfNeeded be called automatically after // a short interval. static nsresult ScheduleMetadataWrite(CacheFile * aFile); @@ -240,6 +254,8 @@ public: static nsresult OpenFile(const nsACString &aKey, uint32_t aFlags, CacheFileIOListener *aCallback); + static nsresult OpenFile(const nsACString &aKey, uint32_t aPinningAppId, + uint32_t aFlags, CacheFileIOListener *aCallback); static nsresult Read(CacheFileHandle *aHandle, int64_t aOffset, char *aBuf, int32_t aCount, CacheFileIOListener *aCallback); @@ -259,7 +275,9 @@ public: CacheFileIOListener *aCallback); static nsresult EvictIfOverLimit(); static nsresult EvictAll(); - static nsresult EvictByContext(nsILoadContextInfo *aLoadContextInfo); + static nsresult EvictPinned(uint32_t aPinningAppId); + static nsresult EvictByContext(nsILoadContextInfo *aLoadContextInfo, + bool aIsPinning); static nsresult InitIndexEntry(CacheFileHandle *aHandle, uint32_t aAppId, @@ -292,6 +310,9 @@ public: static size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf); static size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf); + size_t SizeOfIncludingThisInternal(mozilla::MallocSizeOf mallocSizeOf) const; + size_t SizeOfExcludingThisInternal(mozilla::MallocSizeOf mallocSizeOf) const; + private: friend class CacheFileHandle; friend class CacheFileChunk; @@ -313,6 +334,8 @@ private: virtual ~CacheFileIOManager(); nsresult InitInternal(); + nsresult InitAsPinning(uint32_t aAppId, nsIFile* aProfileDir); + nsresult ShutdownInternal(); nsresult OpenFileInternal(const SHA1Sum::Hash *aHash, @@ -376,10 +399,44 @@ private: // before we start an eviction loop. nsresult UpdateSmartCacheSize(int64_t aFreeSpace); - // Memory reporting (private part) - size_t SizeOfExcludingThisInternal(mozilla::MallocSizeOf mallocSizeOf) const; - static CacheFileIOManager *gInstance; + // Each pinning app has its own intance of an IO manager setup to point to + // the roaming part of the profile plus identification by the appid. + class Pinning + { + nsRefPtrHashtable mTable; + mozilla::Mutex mLock; + nsCOMPtr mRoamingProfileDir; + + static Pinning* sSelf; + static bool sDestroyed; + + static PLDHashOperator Collect(uint32_t const& aAppId, + nsRefPtr& aIoMan, + void* aClosure); + static PLDHashOperator Copy(uint32_t const& aAppId, + nsRefPtr& aIoMan, + void* aClosure); + public: + Pinning(); + ~Pinning(); + + static Pinning* Self(); + static void Destroy(); + + nsresult OnProfile(); + already_AddRefed Get(uint32_t aPinningAppId); + void ShutdownInternal(); +#ifdef DEBUG + uint32_t Length() { return mTable.Count(); } +#endif + + size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; + size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const; + }; + + EKind mKind; + TimeStamp mStartTime; bool mShuttingDown; nsRefPtr mIOThread; @@ -393,7 +450,7 @@ private: #endif bool mTreeCreated; CacheFileHandles mHandles; - nsTArray mHandlesByLastUsed; + nsTArray mHandlesByLastUsed; nsTArray mSpecialHandles; nsTArray > mScheduledMetadataWrites; nsCOMPtr mMetadataWritesTimer; diff --git a/netwerk/cache2/CacheFileUtils.cpp b/netwerk/cache2/CacheFileUtils.cpp index af848520f35..8e7d5375f26 100644 --- a/netwerk/cache2/CacheFileUtils.cpp +++ b/netwerk/cache2/CacheFileUtils.cpp @@ -4,6 +4,7 @@ #include "CacheLog.h" #include "CacheFileUtils.h" +#include "CacheStorage.h" #include "LoadContextInfo.h" #include "nsCOMPtr.h" #include "nsAutoPtr.h" @@ -27,6 +28,7 @@ public: : caret(aCaret) , end(aEnd) // Initialize attributes to their default values + , pinningStorage(false) , appId(nsILoadContextInfo::NO_APP_ID) , isPrivate(false) , isInBrowser(false) @@ -44,6 +46,7 @@ private: nsACString::const_iterator const end; // Results + bool pinningStorage; uint32_t appId; bool isPrivate; bool isInBrowser; @@ -76,6 +79,9 @@ private: cacheKey = caret; caret = end; return true; + case 'P': + pinningStorage = true; + break; case 'p': isPrivate = true; break; @@ -194,14 +200,18 @@ public: { result.Assign(idEnhance); } + + void PinningStorage(bool &result) + { + result = pinningStorage; + } }; } // namespace already_AddRefed ParseKey(const nsCSubstring &aKey, - nsCSubstring *aIdEnhance, - nsCSubstring *aURISpec) + KeyInfo* aKeyInfo) { nsACString::const_iterator caret, end; aKey.BeginReading(caret); @@ -210,18 +220,25 @@ ParseKey(const nsCSubstring &aKey, KeyParser parser(caret, end); nsRefPtr info = parser.Parse(); - if (info) { - if (aIdEnhance) - parser.IdEnhance(*aIdEnhance); - if (aURISpec) - parser.URISpec(*aURISpec); + if (info && aKeyInfo) { + parser.IdEnhance(aKeyInfo->mIdEnhance); + parser.URISpec(aKeyInfo->mURISpec); + parser.PinningStorage(aKeyInfo->mPinningStorage); } return info.forget(); } void -AppendKeyPrefix(nsILoadContextInfo* aInfo, nsACString &_retval) +AppendKeyPrefix(CacheStorage const* aStorage, nsACString &_retval) +{ + AppendKeyPrefix(aStorage->LoadInfo(), aStorage->IsPinning(), _retval); +} + +void +AppendKeyPrefix(nsILoadContextInfo* aInfo, + bool aPin, + nsACString &_retval) { /** * This key is used to salt file hashes. When form of the key is changed @@ -231,6 +248,10 @@ AppendKeyPrefix(nsILoadContextInfo* aInfo, nsACString &_retval) * Keep the attributes list sorted according their ASCII code. */ + if (aPin) { + _retval.AppendLiteral("P,"); + } + if (aInfo->IsAnonymous()) { _retval.AppendLiteral("a,"); } diff --git a/netwerk/cache2/CacheFileUtils.h b/netwerk/cache2/CacheFileUtils.h index bc876d85235..b3331d51f09 100644 --- a/netwerk/cache2/CacheFileUtils.h +++ b/netwerk/cache2/CacheFileUtils.h @@ -11,21 +11,26 @@ #include "nsTArray.h" #include "mozilla/StaticMutex.h" #include "mozilla/TimeStamp.h" +#include "CacheStorageService.h" class nsILoadContextInfo; class nsACString; namespace mozilla { namespace net { + +class CacheStorage; + namespace CacheFileUtils { already_AddRefed ParseKey(const nsCSubstring &aKey, - nsCSubstring *aIdEnhance = nullptr, - nsCSubstring *aURISpec = nullptr); + KeyInfo* aKeyInfo = nullptr); void -AppendKeyPrefix(nsILoadContextInfo *aInfo, nsACString &_retval); +AppendKeyPrefix(CacheStorage const* aStorage, nsACString &_retval); +void +AppendKeyPrefix(nsILoadContextInfo *aInfo, bool aPin, nsACString &_retval); void AppendTagWithValue(nsACString & aTarget, char const aTag, nsCSubstring const & aValue); diff --git a/netwerk/cache2/CacheObserver.cpp b/netwerk/cache2/CacheObserver.cpp index 03c85d29756..47e0d392266 100644 --- a/netwerk/cache2/CacheObserver.cpp +++ b/netwerk/cache2/CacheObserver.cpp @@ -429,6 +429,10 @@ CacheStorageEvictHelper::Run(mozIApplicationClearPrivateDataParams* aParams) rv = ClearStorage(true, aBrowserOnly, true); NS_ENSURE_SUCCESS(rv, rv); + // And finally evict everything that this app has stored as pinned + rv = CacheFileIOManager::EvictPinned(mAppId); + NS_ENSURE_SUCCESS(rv, rv); + return NS_OK; } diff --git a/netwerk/cache2/CacheStorage.h b/netwerk/cache2/CacheStorage.h index be0f805e19a..3a996262954 100644 --- a/netwerk/cache2/CacheStorage.h +++ b/netwerk/cache2/CacheStorage.h @@ -54,6 +54,11 @@ public: bool aAllowDisk, bool aLookupAppCache); + virtual bool IsPinning() const + { + return false; + } + protected: virtual ~CacheStorage(); diff --git a/netwerk/cache2/CacheStorageService.cpp b/netwerk/cache2/CacheStorageService.cpp index b8d5a772655..8de5248c09d 100644 --- a/netwerk/cache2/CacheStorageService.cpp +++ b/netwerk/cache2/CacheStorageService.cpp @@ -9,6 +9,7 @@ #include "CacheIndex.h" #include "CacheIndexIterator.h" #include "CacheStorage.h" +#include "PinningCacheStorage.h" #include "AppCacheStorage.h" #include "CacheEntry.h" #include "CacheFileUtils.h" @@ -209,12 +210,12 @@ protected: class WalkMemoryCacheRunnable : public WalkCacheRunnable { public: - WalkMemoryCacheRunnable(nsILoadContextInfo *aLoadInfo, + WalkMemoryCacheRunnable(CacheStorage const *aStorage, bool aVisitEntries, nsICacheStorageVisitor* aVisitor) : WalkCacheRunnable(aVisitor, aVisitEntries) { - CacheFileUtils::AppendKeyPrefix(aLoadInfo, mContextKey); + CacheFileUtils::AppendKeyPrefix(aStorage, mContextKey); MOZ_ASSERT(NS_IsMainThread()); } @@ -535,8 +536,9 @@ void CacheStorageService::DropPrivateBrowsingEntries() nsTArray keys; sGlobalEntryTables->EnumerateRead(&CollectPrivateContexts, &keys); - for (uint32_t i = 0; i < keys.Length(); ++i) - DoomStorageEntries(keys[i], nullptr, true, nullptr); + for (uint32_t i = 0; i < keys.Length(); ++i) { + DoomStorageEntries(keys[i], nullptr, true, false, nullptr); + } } namespace { @@ -716,6 +718,23 @@ NS_IMETHODIMP CacheStorageService::DiskCacheStorage(nsILoadContextInfo *aLoadCon return NS_OK; } +NS_IMETHODIMP CacheStorageService::PinningCacheStorage(nsILoadContextInfo *aLoadContextInfo, + nsICacheStorage * *_retval) +{ + NS_ENSURE_ARG(aLoadContextInfo); + NS_ENSURE_ARG(_retval); + + if (!CacheObserver::UseNewCache()) { + return NS_ERROR_NOT_IMPLEMENTED; + } + + nsCOMPtr storage = + new mozilla::net::PinningCacheStorage(aLoadContextInfo); + + storage.forget(_retval); + return NS_OK; +} + NS_IMETHODIMP CacheStorageService::AppCacheStorage(nsILoadContextInfo *aLoadContextInfo, nsIApplicationCache *aApplicationCache, nsICacheStorage * *_retval) @@ -751,7 +770,7 @@ NS_IMETHODIMP CacheStorageService::Clear() sGlobalEntryTables->EnumerateRead(&CollectContexts, &keys); for (uint32_t i = 0; i < keys.Length(); ++i) - DoomStorageEntries(keys[i], nullptr, true, nullptr); + DoomStorageEntries(keys[i], nullptr, true, false, nullptr); } rv = CacheFileIOManager::EvictAll(); @@ -1336,10 +1355,15 @@ CacheStorageService::AddStorageEntry(CacheStorage const* aStorage, NS_ENSURE_ARG(aStorage); nsAutoCString contextKey; - CacheFileUtils::AppendKeyPrefix(aStorage->LoadInfo(), contextKey); + CacheFileUtils::AppendKeyPrefix(aStorage, contextKey); + + uint32_t pinningAppId = aStorage->IsPinning() + ? aStorage->LoadInfo()->AppId() + : nsILoadContextInfo::NO_APP_ID; return AddStorageEntry(contextKey, aURI, aIdExtension, - aStorage->WriteToDisk(), aCreateIfNotExist, aReplace, + aStorage->WriteToDisk(), pinningAppId, + aCreateIfNotExist, aReplace, aResult); } @@ -1348,6 +1372,7 @@ CacheStorageService::AddStorageEntry(nsCSubstring const& aContextKey, nsIURI* aURI, const nsACString & aIdExtension, bool aWriteToDisk, + uint32_t aPinningAppId, bool aCreateIfNotExist, bool aReplace, CacheEntryHandle** aResult) @@ -1410,7 +1435,7 @@ CacheStorageService::AddStorageEntry(nsCSubstring const& aContextKey, // Ensure entry for the particular URL, if not read/only if (!entryExists && (aCreateIfNotExist || aReplace)) { // Entry is not in the hashtable or has just been truncated... - entry = new CacheEntry(aContextKey, aURI, aIdExtension, aWriteToDisk); + entry = new CacheEntry(aContextKey, aURI, aIdExtension, aWriteToDisk, aPinningAppId); entries->Put(entryKey, entry); LOG((" new entry %p for %s", entry.get(), entryKey.get())); } @@ -1435,7 +1460,7 @@ CacheStorageService::CheckStorageEntry(CacheStorage const* aStorage, nsresult rv; nsAutoCString contextKey; - CacheFileUtils::AppendKeyPrefix(aStorage->LoadInfo(), contextKey); + CacheFileUtils::AppendKeyPrefix(aStorage, contextKey); if (!aStorage->WriteToDisk()) { AppendMemoryStorageID(contextKey); @@ -1471,6 +1496,11 @@ CacheStorageService::CheckStorageEntry(CacheStorage const* aStorage, return NS_OK; } + if (aStorage->IsPinning()) { + LOG((" not implemented for pinning entries")); + return NS_ERROR_NOT_AVAILABLE; + } + // Disk entry, not found in the hashtable, check the index. nsAutoCString fileKey; rv = CacheEntry::HashingKey(contextKey, aIdExtension, aURI, fileKey); @@ -1557,7 +1587,7 @@ CacheStorageService::DoomStorageEntry(CacheStorage const* aStorage, NS_ENSURE_ARG(aURI); nsAutoCString contextKey; - CacheFileUtils::AppendKeyPrefix(aStorage->LoadInfo(), contextKey); + CacheFileUtils::AppendKeyPrefix(aStorage, contextKey); nsAutoCString entryKey; nsresult rv = CacheEntry::HashingKey(EmptyCString(), aIdExtension, aURI, entryKey); @@ -1598,7 +1628,7 @@ CacheStorageService::DoomStorageEntry(CacheStorage const* aStorage, if (aStorage->WriteToDisk()) { nsAutoCString contextKey; - CacheFileUtils::AppendKeyPrefix(aStorage->LoadInfo(), contextKey); + CacheFileUtils::AppendKeyPrefix(aStorage, contextKey); rv = CacheEntry::HashingKey(contextKey, aIdExtension, aURI, entryKey); NS_ENSURE_SUCCESS(rv, rv); @@ -1643,18 +1673,20 @@ CacheStorageService::DoomStorageEntries(CacheStorage const* aStorage, NS_ENSURE_ARG(aStorage); nsAutoCString contextKey; - CacheFileUtils::AppendKeyPrefix(aStorage->LoadInfo(), contextKey); + CacheFileUtils::AppendKeyPrefix(aStorage, contextKey); mozilla::MutexAutoLock lock(mLock); return DoomStorageEntries(contextKey, aStorage->LoadInfo(), - aStorage->WriteToDisk(), aCallback); + aStorage->WriteToDisk(), + aStorage->IsPinning(), aCallback); } nsresult CacheStorageService::DoomStorageEntries(nsCSubstring const& aContextKey, nsILoadContextInfo* aContext, bool aDiskStorage, + bool aPinningStorage, nsICacheEntryDoomCallback* aCallback) { mLock.AssertCurrentThreadOwns(); @@ -1673,7 +1705,7 @@ CacheStorageService::DoomStorageEntries(nsCSubstring const& aContextKey, if (aContext && !aContext->IsPrivate()) { LOG((" dooming disk entries")); - CacheFileIOManager::EvictByContext(aContext); + CacheFileIOManager::EvictByContext(aContext, aPinningStorage); } } else { LOG((" dooming memory-only storage of %s", aContextKey.BeginReading())); @@ -1740,6 +1772,10 @@ CacheStorageService::WalkStorageEntries(CacheStorage const* aStorage, NS_ENSURE_ARG(aStorage); + if (aStorage->IsPinning()) { + return NS_ERROR_NOT_AVAILABLE; + } + if (aStorage->WriteToDisk()) { nsRefPtr event = new WalkDiskCacheRunnable(aStorage->LoadInfo(), aVisitEntries, aVisitor); @@ -1747,20 +1783,20 @@ CacheStorageService::WalkStorageEntries(CacheStorage const* aStorage, } nsRefPtr event = - new WalkMemoryCacheRunnable(aStorage->LoadInfo(), aVisitEntries, aVisitor); + new WalkMemoryCacheRunnable(aStorage, aVisitEntries, aVisitor); return event->Walk(); } void CacheStorageService::CacheFileDoomed(nsILoadContextInfo* aLoadContextInfo, - const nsACString & aIdExtension, - const nsACString & aURISpec) + CacheFileUtils::KeyInfo* aKeyInfo) { nsAutoCString contextKey; - CacheFileUtils::AppendKeyPrefix(aLoadContextInfo, contextKey); + CacheFileUtils::AppendKeyPrefix(aLoadContextInfo, aKeyInfo->mPinningStorage, contextKey); nsAutoCString entryKey; - CacheEntry::HashingKey(EmptyCString(), aIdExtension, aURISpec, entryKey); + CacheEntry::HashingKey(EmptyCString(), + aKeyInfo->mIdEnhance, aKeyInfo->mURISpec, entryKey); mozilla::MutexAutoLock lock(mLock); @@ -1789,15 +1825,15 @@ CacheStorageService::CacheFileDoomed(nsILoadContextInfo* aLoadContextInfo, bool CacheStorageService::GetCacheEntryInfo(nsILoadContextInfo* aLoadContextInfo, - const nsACString & aIdExtension, - const nsACString & aURISpec, + CacheFileUtils::KeyInfo* aKeyInfo, EntryInfoCallback *aCallback) { nsAutoCString contextKey; - CacheFileUtils::AppendKeyPrefix(aLoadContextInfo, contextKey); + CacheFileUtils::AppendKeyPrefix(aLoadContextInfo, aKeyInfo->mPinningStorage, contextKey); nsAutoCString entryKey; - CacheEntry::HashingKey(EmptyCString(), aIdExtension, aURISpec, entryKey); + CacheEntry::HashingKey(EmptyCString(), + aKeyInfo->mIdEnhance, aKeyInfo->mURISpec, entryKey); nsRefPtr entry; { diff --git a/netwerk/cache2/CacheStorageService.h b/netwerk/cache2/CacheStorageService.h index e79283805d9..ba692f64c8b 100644 --- a/netwerk/cache2/CacheStorageService.h +++ b/netwerk/cache2/CacheStorageService.h @@ -29,6 +29,17 @@ class nsIEventTarget; namespace mozilla { namespace net { +namespace CacheFileUtils { + +class KeyInfo { +public: + bool mPinningStorage; + nsCString mIdEnhance; + nsCString mURISpec; +}; + +} // CacheFileUtils + class CacheStorageService; class CacheStorage; class CacheEntry; @@ -229,20 +240,18 @@ private: * removal was originated by CacheStorageService. */ void CacheFileDoomed(nsILoadContextInfo* aLoadContextInfo, - const nsACString & aIdExtension, - const nsACString & aURISpec); + CacheFileUtils::KeyInfo* aKeyInfo); /** * Tries to find an existing entry in the hashtables and synchronously call * OnCacheEntryInfo of the aVisitor callback when found. * @retuns * true, when the entry has been found that also implies the callbacks has - * beem invoked + * been invoked * false, when an entry has not been found */ bool GetCacheEntryInfo(nsILoadContextInfo* aLoadContextInfo, - const nsACString & aIdExtension, - const nsACString & aURISpec, + CacheFileUtils::KeyInfo* aKeyInfo, EntryInfoCallback *aCallback); private: @@ -273,11 +282,13 @@ private: nsresult DoomStorageEntries(nsCSubstring const& aContextKey, nsILoadContextInfo* aContext, bool aDiskStorage, + bool aPinningStorage, nsICacheEntryDoomCallback* aCallback); nsresult AddStorageEntry(nsCSubstring const& aContextKey, nsIURI* aURI, const nsACString & aIdExtension, bool aWriteToDisk, + uint32_t aPinningAppId, bool aCreateIfNotExist, bool aReplace, CacheEntryHandle** aResult); diff --git a/netwerk/cache2/PinningCacheStorage.cpp b/netwerk/cache2/PinningCacheStorage.cpp new file mode 100644 index 00000000000..4295b6ff342 --- /dev/null +++ b/netwerk/cache2/PinningCacheStorage.cpp @@ -0,0 +1,25 @@ +/* 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 "PinningCacheStorage.h" + +namespace mozilla { +namespace net { + +bool PinningCacheStorage::IsPinning() const +{ + if (LoadInfo()->AppId() == nsILoadContextInfo::NO_APP_ID) { + return false; + } + + if (LoadInfo()->IsPrivate()) { + return false; + } + + // We are a non-private app load, pin! + return true; +} + +} // net +} // mozilla diff --git a/netwerk/cache2/PinningCacheStorage.h b/netwerk/cache2/PinningCacheStorage.h new file mode 100644 index 00000000000..67208f64fb5 --- /dev/null +++ b/netwerk/cache2/PinningCacheStorage.h @@ -0,0 +1,27 @@ +/* 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 PinninCacheStorage__h__ +#define PinninCacheStorage__h__ + +#include "CacheStorage.h" + +namespace mozilla { +namespace net { + +class PinningCacheStorage : public CacheStorage +{ +public: + PinningCacheStorage(nsILoadContextInfo* aInfo) + : CacheStorage(aInfo, true, false) + { + } + + virtual bool IsPinning() const override; +}; + +} // net +} // mozilla + +#endif diff --git a/netwerk/cache2/moz.build b/netwerk/cache2/moz.build index 859b4dc22cd..f3bf2c58bee 100644 --- a/netwerk/cache2/moz.build +++ b/netwerk/cache2/moz.build @@ -40,6 +40,7 @@ UNIFIED_SOURCES += [ 'CacheStorage.cpp', 'CacheStorageService.cpp', 'OldWrappers.cpp', + 'PinningCacheStorage.cpp', ] # AppCacheStorage.cpp cannot be built in unified mode because it uses plarena.h. diff --git a/netwerk/cache2/nsICacheStorageService.idl b/netwerk/cache2/nsICacheStorageService.idl index 5713e63c5a9..50f7781b494 100644 --- a/netwerk/cache2/nsICacheStorageService.idl +++ b/netwerk/cache2/nsICacheStorageService.idl @@ -13,7 +13,7 @@ interface nsICacheStorageConsumptionObserver; /** * Provides access to particual cache storages of the network URI cache. */ -[scriptable, uuid(44de2fa4-1b0e-4cd3-9e32-211e936f721e)] +[scriptable, uuid(6764d6a5-af42-4655-aef2-81d9efe72ae6)] interface nsICacheStorageService : nsISupports { /** @@ -42,6 +42,14 @@ interface nsICacheStorageService : nsISupports nsICacheStorage diskCacheStorage(in nsILoadContextInfo aLoadContextInfo, in bool aLookupAppCache); + /** + * Entries bound to an app will be persisted in a roaming part of the profile + * and won't unpersist until the app goes away. Common web content will use + * disk cache storage, when under the private browsing mode, entries will be + * held in memory and evicted on limit. + */ + nsICacheStorage pinningCacheStorage(in nsILoadContextInfo aLoadContextInfo); + /** * Get storage for a specified application cache obtained using some different * mechanism. diff --git a/netwerk/protocol/http/PackagedAppService.cpp b/netwerk/protocol/http/PackagedAppService.cpp index d3840754d05..cbad5655d15 100644 --- a/netwerk/protocol/http/PackagedAppService.cpp +++ b/netwerk/protocol/http/PackagedAppService.cpp @@ -42,7 +42,7 @@ LogURI(const char *aFunctionName, void *self, nsIURI *aURI, nsILoadContextInfo * nsAutoCString prefix; if (aInfo) { - CacheFileUtils::AppendKeyPrefix(aInfo, prefix); + CacheFileUtils::AppendKeyPrefix(aInfo, false, prefix); prefix += ":"; } @@ -627,7 +627,7 @@ PackagedAppService::RequestURI(nsIURI *aURI, } nsAutoCString key; - CacheFileUtils::AppendKeyPrefix(aInfo, key); + CacheFileUtils::AppendKeyPrefix(aInfo, false, key); { nsAutoCString spec; diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index a76fec3cf1b..787f63d873e 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -260,6 +260,7 @@ nsHttpChannel::nsHttpChannel() , mConcurentCacheAccess(0) , mIsPartialRequest(0) , mHasAutoRedirectVetoNotifier(0) + , mPinCacheContent(0) , mIsPackagedAppResource(0) , mPushedStream(nullptr) , mLocalBlocklist(false) @@ -2847,6 +2848,10 @@ nsHttpChannel::OpenCacheEntry(bool isHttps) rv = cacheStorageService->MemoryCacheStorage(info, // ? choose app cache as well... getter_AddRefs(cacheStorage)); } + else if (mPinCacheContent) { + rv = cacheStorageService->PinningCacheStorage(info, + getter_AddRefs(cacheStorage)); + } else { rv = cacheStorageService->DiskCacheStorage(info, !mPostID && (mChooseApplicationCache || (mLoadFlags & LOAD_CHECK_OFFLINE_CACHE)), @@ -6271,6 +6276,26 @@ nsHttpChannel::SetCacheOnlyMetadata(bool aOnlyMetadata) return NS_OK; } +NS_IMETHODIMP +nsHttpChannel::GetPin(bool *aPin) +{ + NS_ENSURE_ARG(aPin); + *aPin = mPinCacheContent; + return NS_OK; +} + +NS_IMETHODIMP +nsHttpChannel::SetPin(bool aPin) +{ + LOG(("nsHttpChannel::SetPin [this=%p pin=%d]\n", + this, aPin)); + + ENSURE_CALLED_BEFORE_ASYNC_OPEN(); + + mPinCacheContent = aPin; + return NS_OK; +} + //----------------------------------------------------------------------------- // nsHttpChannel::nsIResumableChannel //----------------------------------------------------------------------------- diff --git a/netwerk/protocol/http/nsHttpChannel.h b/netwerk/protocol/http/nsHttpChannel.h index 894a8e12e13..a7a1716c16a 100644 --- a/netwerk/protocol/http/nsHttpChannel.h +++ b/netwerk/protocol/http/nsHttpChannel.h @@ -479,6 +479,9 @@ private: uint32_t mIsPartialRequest : 1; // true iff there is AutoRedirectVetoNotifier on the stack uint32_t mHasAutoRedirectVetoNotifier : 1; + // consumers set this to true to use cache pinning, this has effect + // only when the channel is in an app context (load context has an appid) + uint32_t mPinCacheContent : 1; // Whether fetching the content is meant to be handled by the // packaged app service, which behaves like a caching layer. // Upon successfully fetching the package, the resource will be placed in diff --git a/netwerk/test/unit/head_cache.js b/netwerk/test/unit/head_cache.js index 617df1d9110..6c8cf0d4a8c 100644 --- a/netwerk/test/unit/head_cache.js +++ b/netwerk/test/unit/head_cache.js @@ -50,6 +50,7 @@ function getCacheStorage(where, lci, appcache) case "disk": return svc.diskCacheStorage(lci, false); case "memory": return svc.memoryCacheStorage(lci); case "appcache": return svc.appCacheStorage(lci, appcache); + case "pin": return svc.pinningCacheStorage(lci); } return null; } diff --git a/netwerk/test/unit/test_cache2-28-concurrent_read_resumable_entry_size_zero.js b/netwerk/test/unit/test_cache2-29a-concurrent_read_resumable_entry_size_zero.js similarity index 100% rename from netwerk/test/unit/test_cache2-28-concurrent_read_resumable_entry_size_zero.js rename to netwerk/test/unit/test_cache2-29a-concurrent_read_resumable_entry_size_zero.js diff --git a/netwerk/test/unit/test_cache2-29-concurrent_read_non-resumable_entry_size_zero.js b/netwerk/test/unit/test_cache2-29b-concurrent_read_non-resumable_entry_size_zero.js similarity index 100% rename from netwerk/test/unit/test_cache2-29-concurrent_read_non-resumable_entry_size_zero.js rename to netwerk/test/unit/test_cache2-29b-concurrent_read_non-resumable_entry_size_zero.js diff --git a/netwerk/test/unit/test_cache2-30-app-pinning.js b/netwerk/test/unit/test_cache2-30-app-pinning.js new file mode 100644 index 00000000000..17d114d527b --- /dev/null +++ b/netwerk/test/unit/test_cache2-30-app-pinning.js @@ -0,0 +1,30 @@ +function run_test() +{ + do_get_profile(); + + var applci = LoadContextInfo.custom(false, false, 1001, false); + + // Open for write, write + asyncOpenCacheEntry("http://a/", "pin", Ci.nsICacheStorage.OPEN_NORMALLY, applci, + new OpenCallback(NEW|WAITFORWRITE, "a1m", "a1d", function(entry) { + // Open for read and check + asyncOpenCacheEntry("http://a/", "pin", Ci.nsICacheStorage.OPEN_NORMALLY, applci, + new OpenCallback(NORMAL, "a1m", "a1d", function(entry) { + + // Now clear the whole cache + get_cache_service().clear(); + + // The pinned entry should be intact + asyncOpenCacheEntry("http://a/", "pin", Ci.nsICacheStorage.OPEN_NORMALLY, applci, + new OpenCallback(NORMAL, "a1m", "a1d", function(entry) { + finish_cache2_test(); + }) + ); + + }) + ); + }) + ); + + do_test_pending(); +} diff --git a/netwerk/test/unit/test_cache_jar.js b/netwerk/test/unit/test_cache_jar.js index 92a2f44fd7d..1a74d32beac 100644 --- a/netwerk/test/unit/test_cache_jar.js +++ b/netwerk/test/unit/test_cache_jar.js @@ -101,7 +101,8 @@ function run_test() { function doneFirstLoad(req, buffer, expected) { // Load it again, make sure it hits the cache - var chan = makeChan(URL, 0, false); + var nc = req.notificationCallbacks.getInterface(Ci.nsILoadContext); + var chan = makeChan(URL, nc.appId, nc.isInBrowserElement); chan.asyncOpen(new ChannelListener(doneSecondLoad, expected), null); } diff --git a/netwerk/test/unit/xpcshell.ini b/netwerk/test/unit/xpcshell.ini index 34e6c5c09aa..030b99f794c 100644 --- a/netwerk/test/unit/xpcshell.ini +++ b/netwerk/test/unit/xpcshell.ini @@ -68,8 +68,9 @@ skip-if = true [test_cache2-28a-OPEN_SECRETLY.js] # This test will be fixed in bug 1067931 skip-if = true -[test_cache2-28-concurrent_read_resumable_entry_size_zero.js] -[test_cache2-29-concurrent_read_non-resumable_entry_size_zero.js] +[test_cache2-29a-concurrent_read_resumable_entry_size_zero.js] +[test_cache2-29b-concurrent_read_non-resumable_entry_size_zero.js] +[test_cache2-30-app-pinning.js] [test_partial_response_entry_size_smart_shrink.js] [test_304_responses.js] [test_421.js]