/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=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/. */ #include #include "nsJARInputStream.h" #include "nsJAR.h" #include "nsIFile.h" #include "nsIObserverService.h" #include "mozilla/DebugOnly.h" #include "mozilla/Logging.h" #include "mozilla/Omnijar.h" #include "mozilla/Unused.h" #ifdef XP_UNIX # include #elif defined(XP_WIN) # include #endif using namespace mozilla; static LazyLogModule gJarLog("nsJAR"); #ifdef LOG # undef LOG #endif #ifdef LOG_ENABLED # undef LOG_ENABLED #endif #define LOG(args) MOZ_LOG(gJarLog, mozilla::LogLevel::Debug, args) #define LOG_ENABLED() MOZ_LOG_TEST(gJarLog, mozilla::LogLevel::Debug) //---------------------------------------------- // nsJAR constructor/destructor //---------------------------------------------- // The following initialization makes a guess of 10 entries per jarfile. nsJAR::nsJAR() : mReleaseTime(PR_INTERVAL_NO_TIMEOUT), mLock("nsJAR::mLock"), mCache(nullptr) {} nsJAR::~nsJAR() { Close(); } NS_IMPL_QUERY_INTERFACE(nsJAR, nsIZipReader) NS_IMPL_ADDREF(nsJAR) // Custom Release method works with nsZipReaderCache... // Release might be called from multi-thread, we have to // take this function carefully to avoid delete-after-use. MozExternalRefCountType nsJAR::Release(void) { nsrefcnt count; MOZ_ASSERT(0 != mRefCnt, "dup release"); RefPtr cache; if (mRefCnt == 2) { // don't use a lock too frequently // Use a mutex here to guarantee mCache is not racing and the target // instance is still valid to increase ref-count. RecursiveMutexAutoLock lock(mLock); cache = mCache; mCache = nullptr; } if (cache) { DebugOnly rv = cache->ReleaseZip(this); MOZ_ASSERT(NS_SUCCEEDED(rv), "failed to release zip file"); } count = --mRefCnt; // don't access any member variable after this line NS_LOG_RELEASE(this, count, "nsJAR"); if (0 == count) { mRefCnt = 1; /* stabilize */ /* enable this to find non-threadsafe destructors: */ /* NS_ASSERT_OWNINGTHREAD(nsJAR); */ delete this; return 0; } return count; } //---------------------------------------------- // nsIZipReader implementation //---------------------------------------------- NS_IMETHODIMP nsJAR::Open(nsIFile* zipFile) { NS_ENSURE_ARG_POINTER(zipFile); RecursiveMutexAutoLock lock(mLock); LOG(("Open[%p] %s", this, zipFile->HumanReadablePath().get())); if (mZip) return NS_ERROR_FAILURE; // Already open! mZipFile = zipFile; mOuterZipEntry.Truncate(); // The omnijar is special, it is opened early on and closed late RefPtr zip = mozilla::Omnijar::GetReader(zipFile); if (!zip) { zip = nsZipArchive::OpenArchive(zipFile); } mZip = zip; return mZip ? NS_OK : NS_ERROR_FAILURE; } NS_IMETHODIMP nsJAR::OpenInner(nsIZipReader* aZipReader, const nsACString& aZipEntry) { nsresult rv; LOG(("OpenInner[%p] %s", this, PromiseFlatCString(aZipEntry).get())); NS_ENSURE_ARG_POINTER(aZipReader); nsCOMPtr zipFile; rv = aZipReader->GetFile(getter_AddRefs(zipFile)); NS_ENSURE_SUCCESS(rv, rv); RefPtr innerZip = mozilla::Omnijar::GetInnerReader(zipFile, aZipEntry); if (innerZip) { RecursiveMutexAutoLock lock(mLock); if (mZip) { return NS_ERROR_FAILURE; } mZip = innerZip; return NS_OK; } bool exist; rv = aZipReader->HasEntry(aZipEntry, &exist); NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_TRUE(exist, NS_ERROR_FILE_NOT_FOUND); RefPtr handle; { nsJAR* outerJAR = static_cast(aZipReader); RecursiveMutexAutoLock outerLock(outerJAR->mLock); rv = nsZipHandle::Init(outerJAR->mZip.get(), PromiseFlatCString(aZipEntry).get(), getter_AddRefs(handle)); NS_ENSURE_SUCCESS(rv, rv); } RecursiveMutexAutoLock lock(mLock); MOZ_ASSERT(!mZip, "Another thread tried to open this nsJAR racily!"); mZipFile = zipFile.forget(); mOuterZipEntry.Assign(aZipEntry); mZip = nsZipArchive::OpenArchive(handle); return mZip ? NS_OK : NS_ERROR_FAILURE; } NS_IMETHODIMP nsJAR::OpenMemory(void* aData, uint32_t aLength) { NS_ENSURE_ARG_POINTER(aData); RecursiveMutexAutoLock lock(mLock); if (mZip) return NS_ERROR_FAILURE; // Already open! RefPtr handle; nsresult rv = nsZipHandle::Init(static_cast(aData), aLength, getter_AddRefs(handle)); if (NS_FAILED(rv)) return rv; mZip = nsZipArchive::OpenArchive(handle); return mZip ? NS_OK : NS_ERROR_FAILURE; } NS_IMETHODIMP nsJAR::GetFile(nsIFile** result) { RecursiveMutexAutoLock lock(mLock); LOG(("GetFile[%p]", this)); *result = mZipFile; NS_IF_ADDREF(*result); return NS_OK; } NS_IMETHODIMP nsJAR::Close() { RecursiveMutexAutoLock lock(mLock); LOG(("Close[%p]", this)); if (!mZip) { return NS_ERROR_FAILURE; // Never opened or already closed. } mZip = nullptr; return NS_OK; } NS_IMETHODIMP nsJAR::Test(const nsACString& aEntryName) { RecursiveMutexAutoLock lock(mLock); if (!mZip) { return NS_ERROR_FAILURE; } return mZip->Test( aEntryName.IsEmpty() ? nullptr : PromiseFlatCString(aEntryName).get()); } NS_IMETHODIMP nsJAR::Extract(const nsACString& aEntryName, nsIFile* outFile) { // nsZipArchive and zlib are not thread safe // we need to use a lock to prevent bug #51267 RecursiveMutexAutoLock lock(mLock); if (!mZip) { return NS_ERROR_FAILURE; } LOG(("Extract[%p] %s", this, PromiseFlatCString(aEntryName).get())); nsZipItem* item = mZip->GetItem(PromiseFlatCString(aEntryName).get()); NS_ENSURE_TRUE(item, NS_ERROR_FILE_NOT_FOUND); // Remove existing file or directory so we set permissions correctly. // If it's a directory that already exists and contains files, throw // an exception and return. nsresult rv = outFile->Remove(false); if (rv == NS_ERROR_FILE_DIR_NOT_EMPTY || rv == NS_ERROR_FAILURE) return rv; if (item->IsDirectory()) { rv = outFile->Create(nsIFile::DIRECTORY_TYPE, item->Mode()); // XXX Do this in nsZipArchive? It would be nice to keep extraction // XXX code completely there, but that would require a way to get a // XXX PRDir from outFile. } else { PRFileDesc* fd; rv = outFile->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE, item->Mode(), &fd); if (NS_FAILED(rv)) return rv; // ExtractFile also closes the fd handle and resolves the symlink if needed rv = mZip->ExtractFile(item, outFile, fd); } if (NS_FAILED(rv)) return rv; // nsIFile needs milliseconds, while prtime is in microseconds. // non-fatal if this fails, ignore errors outFile->SetLastModifiedTime(item->LastModTime() / PR_USEC_PER_MSEC); return NS_OK; } NS_IMETHODIMP nsJAR::GetEntry(const nsACString& aEntryName, nsIZipEntry** result) { RecursiveMutexAutoLock lock(mLock); LOG(("GetEntry[%p] %s", this, PromiseFlatCString(aEntryName).get())); if (!mZip) { return NS_ERROR_FAILURE; } nsZipItem* zipItem = mZip->GetItem(PromiseFlatCString(aEntryName).get()); NS_ENSURE_TRUE(zipItem, NS_ERROR_FILE_NOT_FOUND); nsJARItem* jarItem = new nsJARItem(zipItem); NS_ADDREF(*result = jarItem); return NS_OK; } NS_IMETHODIMP nsJAR::HasEntry(const nsACString& aEntryName, bool* result) { RecursiveMutexAutoLock lock(mLock); LOG(("HasEntry[%p] %s", this, PromiseFlatCString(aEntryName).get())); if (!mZip) { return NS_ERROR_FAILURE; } *result = mZip->GetItem(PromiseFlatCString(aEntryName).get()) != nullptr; return NS_OK; } NS_IMETHODIMP nsJAR::FindEntries(const nsACString& aPattern, nsIUTF8StringEnumerator** result) { NS_ENSURE_ARG_POINTER(result); RecursiveMutexAutoLock lock(mLock); LOG(("FindEntries[%p] %s", this, PromiseFlatCString(aPattern).get())); if (!mZip) { return NS_ERROR_FAILURE; } nsZipFind* find; nsresult rv = mZip->FindInit( aPattern.IsEmpty() ? nullptr : PromiseFlatCString(aPattern).get(), &find); NS_ENSURE_SUCCESS(rv, rv); nsIUTF8StringEnumerator* zipEnum = new nsJAREnumerator(find); NS_ADDREF(*result = zipEnum); return NS_OK; } NS_IMETHODIMP nsJAR::GetInputStream(const nsACString& aEntryName, nsIInputStream** result) { NS_ENSURE_ARG_POINTER(result); RecursiveMutexAutoLock lock(mLock); if (!mZip) { return NS_ERROR_FAILURE; } LOG(("GetInputStream[%p] %s", this, PromiseFlatCString(aEntryName).get())); // Watch out for the jar:foo.zip!/ (aDir is empty) top-level special case! nsZipItem* item = nullptr; const nsCString& entry = PromiseFlatCString(aEntryName); if (*entry.get()) { // First check if item exists in jar item = mZip->GetItem(entry.get()); if (!item) return NS_ERROR_FILE_NOT_FOUND; } nsJARInputStream* jis = new nsJARInputStream(); // addref now so we can call InitFile/InitDirectory() NS_ADDREF(*result = jis); nsresult rv = NS_OK; if (!item || item->IsDirectory()) { rv = jis->InitDirectory(this, entry.get()); } else { RefPtr fd = mZip->GetFD(); rv = jis->InitFile(fd, mZip->GetData(item), item); } if (NS_FAILED(rv)) { NS_RELEASE(*result); } return rv; } nsresult nsJAR::GetFullJarPath(nsACString& aResult) { RecursiveMutexAutoLock lock(mLock); NS_ENSURE_ARG_POINTER(mZipFile); nsresult rv = mZipFile->GetPersistentDescriptor(aResult); if (NS_FAILED(rv)) { return rv; } if (mOuterZipEntry.IsEmpty()) { aResult.InsertLiteral("file:", 0); } else { aResult.InsertLiteral("jar:", 0); aResult.AppendLiteral("!/"); aResult.Append(mOuterZipEntry); } return NS_OK; } nsresult nsJAR::GetNSPRFileDesc(PRFileDesc** aNSPRFileDesc) { RecursiveMutexAutoLock lock(mLock); if (!aNSPRFileDesc) { return NS_ERROR_ILLEGAL_VALUE; } *aNSPRFileDesc = nullptr; if (!mZip) { return NS_ERROR_FAILURE; } RefPtr handle = mZip->GetFD(); if (!handle) { return NS_ERROR_FAILURE; } return handle->GetNSPRFileDesc(aNSPRFileDesc); } //---------------------------------------------- // nsJAR private implementation //---------------------------------------------- nsresult nsJAR::LoadEntry(const nsACString& aFilename, nsCString& aBuf) { //-- Get a stream for reading the file nsresult rv; nsCOMPtr manifestStream; rv = GetInputStream(aFilename, getter_AddRefs(manifestStream)); if (NS_FAILED(rv)) return NS_ERROR_FILE_NOT_FOUND; //-- Read the manifest file into memory char* buf; uint64_t len64; rv = manifestStream->Available(&len64); if (NS_FAILED(rv)) return rv; NS_ENSURE_TRUE(len64 < UINT32_MAX, NS_ERROR_FILE_CORRUPTED); // bug 164695 uint32_t len = (uint32_t)len64; buf = (char*)malloc(len + 1); if (!buf) return NS_ERROR_OUT_OF_MEMORY; uint32_t bytesRead; rv = manifestStream->Read(buf, len, &bytesRead); if (bytesRead != len) { rv = NS_ERROR_FILE_CORRUPTED; } if (NS_FAILED(rv)) { free(buf); return rv; } buf[len] = '\0'; // Null-terminate the buffer aBuf.Adopt(buf, len); return NS_OK; } int32_t nsJAR::ReadLine(const char** src) { if (!*src) { return 0; } //--Moves pointer to beginning of next line and returns line length // not including CR/LF. int32_t length; const char* eol = strpbrk(*src, "\r\n"); if (eol == nullptr) // Probably reached end of file before newline { length = strlen(*src); if (length == 0) // immediate end-of-file *src = nullptr; else // some data left on this line *src += length; } else { length = eol - *src; if (eol[0] == '\r' && eol[1] == '\n') // CR LF, so skip 2 *src = eol + 2; else // Either CR or LF, so skip 1 *src = eol + 1; } return length; } NS_IMPL_ISUPPORTS(nsJAREnumerator, nsIUTF8StringEnumerator, nsIStringEnumerator) //---------------------------------------------- // nsJAREnumerator::HasMore //---------------------------------------------- NS_IMETHODIMP nsJAREnumerator::HasMore(bool* aResult) { // try to get the next element if (!mName) { NS_ASSERTION(mFind, "nsJAREnumerator: Missing zipFind."); nsresult rv = mFind->FindNext(&mName, &mNameLen); if (rv == NS_ERROR_FILE_NOT_FOUND) { *aResult = false; // No more matches available return NS_OK; } NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); // no error translation } *aResult = true; return NS_OK; } //---------------------------------------------- // nsJAREnumerator::GetNext //---------------------------------------------- NS_IMETHODIMP nsJAREnumerator::GetNext(nsACString& aResult) { // check if the current item is "stale" if (!mName) { bool bMore; nsresult rv = HasMore(&bMore); if (NS_FAILED(rv) || !bMore) return NS_ERROR_FAILURE; // no error translation } aResult.Assign(mName, mNameLen); mName = 0; // we just gave this one away return NS_OK; } NS_IMPL_ISUPPORTS(nsJARItem, nsIZipEntry) nsJARItem::nsJARItem(nsZipItem* aZipItem) : mSize(aZipItem->Size()), mRealsize(aZipItem->RealSize()), mCrc32(aZipItem->CRC32()), mLastModTime(aZipItem->LastModTime()), mCompression(aZipItem->Compression()), mPermissions(aZipItem->Mode()), mIsDirectory(aZipItem->IsDirectory()), mIsSynthetic(aZipItem->isSynthetic) {} //------------------------------------------ // nsJARItem::GetCompression //------------------------------------------ NS_IMETHODIMP nsJARItem::GetCompression(uint16_t* aCompression) { NS_ENSURE_ARG_POINTER(aCompression); *aCompression = mCompression; return NS_OK; } //------------------------------------------ // nsJARItem::GetSize //------------------------------------------ NS_IMETHODIMP nsJARItem::GetSize(uint32_t* aSize) { NS_ENSURE_ARG_POINTER(aSize); *aSize = mSize; return NS_OK; } //------------------------------------------ // nsJARItem::GetRealSize //------------------------------------------ NS_IMETHODIMP nsJARItem::GetRealSize(uint32_t* aRealsize) { NS_ENSURE_ARG_POINTER(aRealsize); *aRealsize = mRealsize; return NS_OK; } //------------------------------------------ // nsJARItem::GetCrc32 //------------------------------------------ NS_IMETHODIMP nsJARItem::GetCRC32(uint32_t* aCrc32) { NS_ENSURE_ARG_POINTER(aCrc32); *aCrc32 = mCrc32; return NS_OK; } //------------------------------------------ // nsJARItem::GetIsDirectory //------------------------------------------ NS_IMETHODIMP nsJARItem::GetIsDirectory(bool* aIsDirectory) { NS_ENSURE_ARG_POINTER(aIsDirectory); *aIsDirectory = mIsDirectory; return NS_OK; } //------------------------------------------ // nsJARItem::GetIsSynthetic //------------------------------------------ NS_IMETHODIMP nsJARItem::GetIsSynthetic(bool* aIsSynthetic) { NS_ENSURE_ARG_POINTER(aIsSynthetic); *aIsSynthetic = mIsSynthetic; return NS_OK; } //------------------------------------------ // nsJARItem::GetLastModifiedTime //------------------------------------------ NS_IMETHODIMP nsJARItem::GetLastModifiedTime(PRTime* aLastModTime) { NS_ENSURE_ARG_POINTER(aLastModTime); *aLastModTime = mLastModTime; return NS_OK; } //------------------------------------------ // nsJARItem::GetPermissions //------------------------------------------ NS_IMETHODIMP nsJARItem::GetPermissions(uint32_t* aPermissions) { NS_ENSURE_ARG_POINTER(aPermissions); *aPermissions = mPermissions; return NS_OK; } //////////////////////////////////////////////////////////////////////////////// // nsIZipReaderCache NS_IMPL_ISUPPORTS(nsZipReaderCache, nsIZipReaderCache, nsIObserver, nsISupportsWeakReference) nsZipReaderCache::nsZipReaderCache() : mLock("nsZipReaderCache.mLock"), mCacheSize(0), mZips() #ifdef ZIP_CACHE_HIT_RATE , mZipCacheLookups(0), mZipCacheHits(0), mZipCacheFlushes(0), mZipSyncMisses(0) #endif { } NS_IMETHODIMP nsZipReaderCache::Init(uint32_t cacheSize) { MutexAutoLock lock(mLock); mCacheSize = cacheSize; // Register as a memory pressure observer nsCOMPtr os = do_GetService("@mozilla.org/observer-service;1"); if (os) { os->AddObserver(this, "memory-pressure", true); os->AddObserver(this, "chrome-flush-caches", true); os->AddObserver(this, "flush-cache-entry", true); } // ignore failure of the observer registration. return NS_OK; } nsZipReaderCache::~nsZipReaderCache() { for (const auto& zip : mZips.Values()) { zip->SetZipReaderCache(nullptr); } #ifdef ZIP_CACHE_HIT_RATE printf( "nsZipReaderCache size=%d hits=%d lookups=%d rate=%f%% flushes=%d missed " "%d\n", mCacheSize, mZipCacheHits, mZipCacheLookups, (float)mZipCacheHits / mZipCacheLookups, mZipCacheFlushes, mZipSyncMisses); #endif } NS_IMETHODIMP nsZipReaderCache::IsCached(nsIFile* zipFile, bool* aResult) { NS_ENSURE_ARG_POINTER(zipFile); nsresult rv; MutexAutoLock lock(mLock); nsAutoCString uri; rv = zipFile->GetPersistentDescriptor(uri); if (NS_FAILED(rv)) return rv; uri.InsertLiteral("file:", 0); *aResult = mZips.Contains(uri); return NS_OK; } nsresult nsZipReaderCache::GetZip(nsIFile* zipFile, nsIZipReader** result, bool failOnMiss) { NS_ENSURE_ARG_POINTER(zipFile); nsresult rv; MutexAutoLock lock(mLock); #ifdef ZIP_CACHE_HIT_RATE mZipCacheLookups++; #endif nsAutoCString uri; rv = zipFile->GetPersistentDescriptor(uri); if (NS_FAILED(rv)) return rv; uri.InsertLiteral("file:", 0); RefPtr zip; mZips.Get(uri, getter_AddRefs(zip)); if (zip) { #ifdef ZIP_CACHE_HIT_RATE mZipCacheHits++; #endif zip->ClearReleaseTime(); } else { if (failOnMiss) { return NS_ERROR_CACHE_KEY_NOT_FOUND; } zip = new nsJAR(); zip->SetZipReaderCache(this); rv = zip->Open(zipFile); if (NS_FAILED(rv)) { return rv; } MOZ_ASSERT(!mZips.Contains(uri)); mZips.InsertOrUpdate(uri, RefPtr{zip}); } zip.forget(result); return rv; } NS_IMETHODIMP nsZipReaderCache::GetZipIfCached(nsIFile* zipFile, nsIZipReader** result) { return GetZip(zipFile, result, true); } NS_IMETHODIMP nsZipReaderCache::GetZip(nsIFile* zipFile, nsIZipReader** result) { return GetZip(zipFile, result, false); } NS_IMETHODIMP nsZipReaderCache::GetInnerZip(nsIFile* zipFile, const nsACString& entry, nsIZipReader** result) { NS_ENSURE_ARG_POINTER(zipFile); nsCOMPtr outerZipReader; nsresult rv = GetZip(zipFile, getter_AddRefs(outerZipReader)); NS_ENSURE_SUCCESS(rv, rv); MutexAutoLock lock(mLock); #ifdef ZIP_CACHE_HIT_RATE mZipCacheLookups++; #endif nsAutoCString uri; rv = zipFile->GetPersistentDescriptor(uri); if (NS_FAILED(rv)) return rv; uri.InsertLiteral("jar:", 0); uri.AppendLiteral("!/"); uri.Append(entry); RefPtr zip; mZips.Get(uri, getter_AddRefs(zip)); if (zip) { #ifdef ZIP_CACHE_HIT_RATE mZipCacheHits++; #endif zip->ClearReleaseTime(); } else { zip = new nsJAR(); zip->SetZipReaderCache(this); rv = zip->OpenInner(outerZipReader, entry); if (NS_FAILED(rv)) { return rv; } MOZ_ASSERT(!mZips.Contains(uri)); mZips.InsertOrUpdate(uri, RefPtr{zip}); } zip.forget(result); return rv; } NS_IMETHODIMP nsZipReaderCache::GetFd(nsIFile* zipFile, PRFileDesc** aRetVal) { #if defined(XP_WIN) MOZ_CRASH("Not implemented"); return NS_ERROR_NOT_IMPLEMENTED; #else if (!zipFile) { return NS_ERROR_FAILURE; } nsresult rv; nsAutoCString uri; rv = zipFile->GetPersistentDescriptor(uri); if (NS_FAILED(rv)) { return rv; } uri.InsertLiteral("file:", 0); MutexAutoLock lock(mLock); RefPtr zip; mZips.Get(uri, getter_AddRefs(zip)); if (!zip) { return NS_ERROR_FAILURE; } zip->ClearReleaseTime(); rv = zip->GetNSPRFileDesc(aRetVal); // Do this to avoid possible deadlock on mLock with ReleaseZip(). { MutexAutoUnlock unlock(mLock); zip = nullptr; } return rv; #endif /* XP_WIN */ } nsresult nsZipReaderCache::ReleaseZip(nsJAR* zip) { nsresult rv; MutexAutoLock lock(mLock); // It is possible that two thread compete for this zip. The dangerous // case is where one thread Releases the zip and discovers that the ref // count has gone to one. Before it can call this ReleaseZip method // another thread calls our GetZip method. The ref count goes to two. That // second thread then Releases the zip and the ref count goes to one. It // then tries to enter this ReleaseZip method and blocks while the first // thread is still here. The first thread continues and remove the zip from // the cache and calls its Release method sending the ref count to 0 and // deleting the zip. However, the second thread is still blocked at the // start of ReleaseZip, but the 'zip' param now hold a reference to a // deleted zip! // // So, we are going to try safeguarding here by searching our hashtable while // locked here for the zip. We return fast if it is not found. bool found = false; for (const auto& current : mZips.Values()) { if (zip == current) { found = true; break; } } if (!found) { #ifdef ZIP_CACHE_HIT_RATE mZipSyncMisses++; #endif return NS_OK; } zip->SetReleaseTime(); if (mZips.Count() <= mCacheSize) return NS_OK; // Find the oldest zip. nsJAR* oldest = nullptr; for (const auto& current : mZips.Values()) { PRIntervalTime currentReleaseTime = current->GetReleaseTime(); if (currentReleaseTime != PR_INTERVAL_NO_TIMEOUT) { if (oldest == nullptr || currentReleaseTime < oldest->GetReleaseTime()) { oldest = current; } } } // Because of the craziness above it is possible that there is no zip that // needs removing. if (!oldest) return NS_OK; #ifdef ZIP_CACHE_HIT_RATE mZipCacheFlushes++; #endif // remove from hashtable nsAutoCString uri; rv = oldest->GetFullJarPath(uri); if (NS_FAILED(rv)) { return rv; } // Retrieving and removing the JAR should be done without an extra AddRef // and Release, or we'll trigger nsJAR::Release's magic refcount 1 case // an extra time. RefPtr removed; mZips.Remove(uri, getter_AddRefs(removed)); NS_ASSERTION(removed, "botched"); NS_ASSERTION(oldest == removed, "removed wrong entry"); if (removed) removed->SetZipReaderCache(nullptr); return NS_OK; } NS_IMETHODIMP nsZipReaderCache::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aSomeData) { if (strcmp(aTopic, "memory-pressure") == 0) { MutexAutoLock lock(mLock); for (auto iter = mZips.Iter(); !iter.Done(); iter.Next()) { RefPtr& current = iter.Data(); if (current->GetReleaseTime() != PR_INTERVAL_NO_TIMEOUT) { current->SetZipReaderCache(nullptr); iter.Remove(); } } } else if (strcmp(aTopic, "chrome-flush-caches") == 0) { MutexAutoLock lock(mLock); for (const auto& current : mZips.Values()) { current->SetZipReaderCache(nullptr); } mZips.Clear(); } else if (strcmp(aTopic, "flush-cache-entry") == 0) { nsCOMPtr file; if (aSubject) { file = do_QueryInterface(aSubject); } else if (aSomeData) { nsDependentString fileName(aSomeData); Unused << NS_NewLocalFile(fileName, false, getter_AddRefs(file)); } if (!file) return NS_OK; nsAutoCString uri; if (NS_FAILED(file->GetPersistentDescriptor(uri))) return NS_OK; uri.InsertLiteral("file:", 0); MutexAutoLock lock(mLock); RefPtr zip; mZips.Get(uri, getter_AddRefs(zip)); if (!zip) return NS_OK; #ifdef ZIP_CACHE_HIT_RATE mZipCacheFlushes++; #endif zip->SetZipReaderCache(nullptr); mZips.Remove(uri); } return NS_OK; } ////////////////////////////////////////////////////////////////////////////////