author | Michael Wu <mwu@mozilla.com> |
Wed, 08 Sep 2010 20:38:34 -0700 | |
changeset 52251 | 3e6d29c4ab619e0806f581987f3afb1c631b705e |
parent 52250 | b32655a792347a55bd5831f8250805d3b936cb2f |
child 52252 | 04f642fa69a2165507461cdc71d896f190a11d8a |
push id | 15580 |
push user | mwu@mozilla.com |
push date | Thu, 09 Sep 2010 03:42:33 +0000 |
treeherder | mozilla-central@a26593abc145 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | taras, blocking-beta6 |
bugs | 533038 |
milestone | 2.0b6pre |
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/modules/libjar/nsIZipReader.idl +++ b/modules/libjar/nsIZipReader.idl @@ -86,27 +86,32 @@ interface nsIZipEntry : nsISupports * directory foo/ in a zip containing exactly one entry for foo/bar.txt * is synthetic. If the zip file contains an actual entry for a directory, * this attribute will be false for the nsIZipEntry for that directory. * It is impossible for a file to be synthetic. */ readonly attribute boolean isSynthetic; }; -[scriptable, uuid(27067432-cb21-437e-99d1-f85858522bb1)] +[scriptable, uuid(7bb925d6-833a-486c-8ef2-9bc15c670a60)] interface nsIZipReader : nsISupports { /** * Opens a zip file for reading. * It is allowed to open with another file, * but it needs to be closed first with close(). */ void open(in nsIFile zipFile); /** + * Opens a zip file inside a zip file for reading. + */ + void openInner(in nsIZipReader zipReader, in string zipEntry); + + /** * The file that represents the zip with which this zip reader was * initialized. */ readonly attribute nsIFile file; /** * Closes a zip reader. Subsequent attempts to extract files or read from * its input stream will result in an error. @@ -235,16 +240,23 @@ interface nsIZipReaderCache : nsISupport * is created, initialized, and opened (see nsIZipReader::init and * nsIZipReader::open). Otherwise the previously created zip reader is * returned. * * @note If someone called close() on the shared nsIZipReader, this method * will return the closed zip reader. */ nsIZipReader getZip(in nsIFile zipFile); + + /** + * Returns a (possibly shared) nsIZipReader for a zip inside another zip + * + * See getZip + */ + nsIZipReader getInnerZip(in nsIFile zipFile, in string zipEntry); }; //////////////////////////////////////////////////////////////////////////////// %{C++ #define NS_ZIPREADER_CID \ { /* 7526a738-9632-11d3-8cd9-0060b0fc14a3 */ \
--- a/modules/libjar/nsJAR.cpp +++ b/modules/libjar/nsJAR.cpp @@ -169,16 +169,40 @@ nsJAR::Open(nsIFile* zipFile) mLock = PR_NewLock(); NS_ENSURE_TRUE(mLock, NS_ERROR_OUT_OF_MEMORY); return mZip.OpenArchive(zipFile); } NS_IMETHODIMP +nsJAR::OpenInner(nsIZipReader *aZipReader, const char *aZipEntry) +{ + NS_ENSURE_ARG_POINTER(aZipReader); + NS_ENSURE_ARG_POINTER(aZipEntry); + if (mLock) return NS_ERROR_FAILURE; // Already open! + + nsresult rv = aZipReader->GetFile(getter_AddRefs(mZipFile)); + NS_ENSURE_SUCCESS(rv, rv); + + mLock = PR_NewLock(); + NS_ENSURE_TRUE(mLock, NS_ERROR_OUT_OF_MEMORY); + + mOuterZipEntry.Assign(aZipEntry); + + nsRefPtr<nsZipHandle> handle; + rv = nsZipHandle::Init(&static_cast<nsJAR*>(aZipReader)->mZip, aZipEntry, + getter_AddRefs(handle)); + if (NS_FAILED(rv)) + return rv; + + return mZip.OpenArchive(handle); +} + +NS_IMETHODIMP nsJAR::GetFile(nsIFile* *result) { *result = mZipFile; NS_IF_ADDREF(*result); return NS_OK; } NS_IMETHODIMP @@ -188,16 +212,17 @@ nsJAR::Close() PR_DestroyLock(mLock); mLock = nsnull; } mParsedManifest = PR_FALSE; mManifestData.Reset(); mGlobalStatus = JAR_MANIFEST_NOT_PARSED; mTotalItemsInManifest = 0; + mOuterZipEntry.Truncate(0); return mZip.CloseArchive(); } NS_IMETHODIMP nsJAR::Test(const char *aEntryName) { return mZip.Test(aEntryName); @@ -996,18 +1021,18 @@ nsJARItem::GetLastModifiedTime(PRTime* a } //////////////////////////////////////////////////////////////////////////////// // nsIZipReaderCache NS_IMPL_THREADSAFE_ISUPPORTS3(nsZipReaderCache, nsIZipReaderCache, nsIObserver, nsISupportsWeakReference) nsZipReaderCache::nsZipReaderCache() - : mLock(nsnull), - mZips(16) + : mLock(nsnull) + , mZips(16) #ifdef ZIP_CACHE_HIT_RATE , mZipCacheLookups(0), mZipCacheHits(0), mZipCacheFlushes(0), mZipSyncMisses(0) #endif { @@ -1061,21 +1086,23 @@ nsZipReaderCache::GetZip(nsIFile* zipFil nsresult rv; nsCOMPtr<nsIZipReader> antiLockZipGrip; nsAutoLock lock(mLock); #ifdef ZIP_CACHE_HIT_RATE mZipCacheLookups++; #endif - nsCAutoString path; - rv = zipFile->GetNativePath(path); + nsCAutoString uri; + rv = zipFile->GetNativePath(uri); if (NS_FAILED(rv)) return rv; - nsCStringKey key(path); + uri.Insert(NS_LITERAL_CSTRING("file:"), 0); + + nsCStringKey key(uri); nsJAR* zip = static_cast<nsJAR*>(static_cast<nsIZipReader*>(mZips.Get(&key))); // AddRefs if (zip) { #ifdef ZIP_CACHE_HIT_RATE mZipCacheHits++; #endif zip->ClearReleaseTime(); } else { @@ -1093,16 +1120,66 @@ nsZipReaderCache::GetZip(nsIFile* zipFil PRBool collision = mZips.Put(&key, static_cast<nsIZipReader*>(zip)); // AddRefs to 2 NS_ASSERTION(!collision, "horked"); } *result = zip; return rv; } +NS_IMETHODIMP +nsZipReaderCache::GetInnerZip(nsIFile* zipFile, const char *entry, + nsIZipReader* *result) +{ + NS_ENSURE_ARG_POINTER(zipFile); + + nsCOMPtr<nsIZipReader> outerZipReader; + nsresult rv = GetZip(zipFile, getter_AddRefs(outerZipReader)); + NS_ENSURE_SUCCESS(rv, rv); + +#ifdef ZIP_CACHE_HIT_RATE + mZipCacheLookups++; +#endif + + nsCAutoString uri; + rv = zipFile->GetNativePath(uri); + if (NS_FAILED(rv)) return rv; + + uri.Insert(NS_LITERAL_CSTRING("jar:"), 0); + uri.AppendLiteral("!/"); + uri.Append(entry); + + nsCStringKey key(uri); + nsJAR* zip = static_cast<nsJAR*>(static_cast<nsIZipReader*>(mZips.Get(&key))); // AddRefs + if (zip) { +#ifdef ZIP_CACHE_HIT_RATE + mZipCacheHits++; +#endif + zip->ClearReleaseTime(); + } + else { + zip = new nsJAR(); + if (zip == nsnull) + return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(zip); + zip->SetZipReaderCache(this); + + rv = zip->OpenInner(outerZipReader, entry); + if (NS_FAILED(rv)) { + NS_RELEASE(zip); + return rv; + } + + PRBool collision = mZips.Put(&key, static_cast<nsIZipReader*>(zip)); // AddRefs to 2 + NS_ASSERTION(!collision, "horked"); + } + *result = zip; + return rv; +} + static PRBool FindOldestZip(nsHashKey *aKey, void *aData, void* closure) { nsJAR** oldestPtr = (nsJAR**)closure; nsJAR* oldest = *oldestPtr; nsJAR* current = (nsJAR*)aData; PRIntervalTime currentReleaseTime = current->GetReleaseTime(); if (currentReleaseTime != PR_INTERVAL_NO_TIMEOUT) { @@ -1176,22 +1253,32 @@ nsZipReaderCache::ReleaseZip(nsJAR* zip) #endif // Clear the cache pointer in case we gave out this oldest guy while // his Release call was being made. Otherwise we could nest on ReleaseZip // when the second owner calls Release and we are still here in this lock. oldest->SetZipReaderCache(nsnull); // remove from hashtable - nsCAutoString path; - rv = oldest->GetJarPath(path); - if (NS_FAILED(rv)) return rv; + nsCAutoString uri; + rv = oldest->GetJarPath(uri); + if (NS_FAILED(rv)) + return rv; - nsCStringKey key(path); - PRBool removed = mZips.Remove(&key); // Releases + if (zip->mOuterZipEntry.IsEmpty()) { + uri.Insert(NS_LITERAL_CSTRING("file:"), 0); + } else { + uri.Insert(NS_LITERAL_CSTRING("jar:"), 0); + uri.AppendLiteral("!/"); + uri.Append(zip->mOuterZipEntry); + } + + nsCStringKey key(uri); + PRBool removed; + removed = mZips.Remove(&key); // Releases NS_ASSERTION(removed, "botched"); return NS_OK; } static PRBool FindFlushableZip(nsHashKey *aKey, void *aData, void* closure) {
--- a/modules/libjar/nsJAR.h +++ b/modules/libjar/nsJAR.h @@ -88,16 +88,18 @@ typedef enum * Class nsJAR declaration. * nsJAR serves as an XPCOM wrapper for nsZipArchive with the addition of * JAR manifest file parsing. *------------------------------------------------------------------------*/ class nsJAR : public nsIZipReader { // Allows nsJARInputStream to call the verification functions friend class nsJARInputStream; + // Allows nsZipReaderCache to access mOuterZipEntry + friend class nsZipReaderCache; public: nsJAR(); virtual ~nsJAR(); NS_DEFINE_STATIC_CID_ACCESSOR( NS_ZIPREADER_CID ) @@ -125,16 +127,17 @@ class nsJAR : public nsIZipReader void SetZipReaderCache(nsZipReaderCache* cache) { mCache = cache; } protected: //-- Private data members nsCOMPtr<nsIFile> mZipFile; // The zip/jar file on disk + nsCString mOuterZipEntry; // The entry in the zip this zip is reading from nsZipArchive mZip; // The underlying zip archive nsObjectHashtable mManifestData; // Stores metadata for each entry PRBool mParsedManifest; // True if manifest has been parsed nsCOMPtr<nsIPrincipal> mPrincipal; // The entity which signed this file PRInt16 mGlobalStatus; // Global signature verification status PRIntervalTime mReleaseTime; // used by nsZipReaderCache for flushing entries nsZipReaderCache* mCache; // if cached, this points to the cache it's contained in PRLock* mLock;
--- a/modules/libjar/nsJARChannel.cpp +++ b/modules/libjar/nsJARChannel.cpp @@ -79,27 +79,25 @@ static PRLogModuleInfo *gJarProtocolLog //----------------------------------------------------------------------------- class nsJARInputThunk : public nsIInputStream { public: NS_DECL_ISUPPORTS NS_DECL_NSIINPUTSTREAM - nsJARInputThunk(nsIFile *jarFile, + nsJARInputThunk(nsIZipReader *zipReader, nsIURI* fullJarURI, const nsACString &jarEntry, nsIZipReaderCache *jarCache) : mJarCache(jarCache) - , mJarFile(jarFile) + , mJarReader(zipReader) , mJarEntry(jarEntry) , mContentLength(-1) { - NS_ASSERTION(mJarFile, "no jar file"); - if (fullJarURI) { nsresult rv = fullJarURI->GetAsciiSpec(mJarDirSpec); NS_ASSERTION(NS_SUCCEEDED(rv), "this shouldn't fail"); } } virtual ~nsJARInputThunk() { @@ -118,43 +116,31 @@ public: } nsresult EnsureJarStream(); private: nsCOMPtr<nsIZipReaderCache> mJarCache; nsCOMPtr<nsIZipReader> mJarReader; - nsCOMPtr<nsIFile> mJarFile; nsCString mJarDirSpec; nsCOMPtr<nsIInputStream> mJarStream; nsCString mJarEntry; PRInt32 mContentLength; }; NS_IMPL_THREADSAFE_ISUPPORTS1(nsJARInputThunk, nsIInputStream) nsresult nsJARInputThunk::EnsureJarStream() { if (mJarStream) return NS_OK; nsresult rv; - if (mJarCache) - rv = mJarCache->GetZip(mJarFile, getter_AddRefs(mJarReader)); - else { - // create an uncached jar reader - mJarReader = do_CreateInstance(kZipReaderCID, &rv); - if (NS_FAILED(rv)) return rv; - - rv = mJarReader->Open(mJarFile); - } - if (NS_FAILED(rv)) return rv; - if (ENTRY_IS_DIRECTORY(mJarEntry)) { // A directory stream also needs the Spec of the FullJarURI // because is included in the stream data itself. NS_ENSURE_STATE(!mJarDirSpec.IsEmpty()); rv = mJarReader->GetInputStreamWithSpec(mJarDirSpec, mJarEntry.get(), @@ -298,19 +284,50 @@ nsJARChannel::Init(nsIURI *uri) nsresult nsJARChannel::CreateJarInput(nsIZipReaderCache *jarCache) { // important to pass a clone of the file since the nsIFile impl is not // necessarily MT-safe nsCOMPtr<nsIFile> clonedFile; nsresult rv = mJarFile->Clone(getter_AddRefs(clonedFile)); - if (NS_FAILED(rv)) return rv; + if (NS_FAILED(rv)) + return rv; + + nsCOMPtr<nsIZipReader> reader; + if (jarCache) { + if (mInnerJarEntry.IsEmpty()) + rv = jarCache->GetZip(mJarFile, getter_AddRefs(reader)); + else + rv = jarCache->GetInnerZip(mJarFile, mInnerJarEntry.get(), + getter_AddRefs(reader)); + } else { + // create an uncached jar reader + nsCOMPtr<nsIZipReader> outerReader = do_CreateInstance(kZipReaderCID, &rv); + if (NS_FAILED(rv)) + return rv; - mJarInput = new nsJARInputThunk(clonedFile, mJarURI, mJarEntry, jarCache); + rv = outerReader->Open(mJarFile); + if (NS_FAILED(rv)) + return rv; + + if (mInnerJarEntry.IsEmpty()) + reader = outerReader; + else { + reader = do_CreateInstance(kZipReaderCID, &rv); + if (NS_FAILED(rv)) + return rv; + + rv = reader->OpenInner(outerReader, mInnerJarEntry.get()); + } + } + if (NS_FAILED(rv)) + return rv; + + mJarInput = new nsJARInputThunk(reader, mJarURI, mJarEntry, jarCache); if (!mJarInput) return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(mJarInput); return NS_OK; } nsresult nsJARChannel::EnsureJarInput(PRBool blocking) @@ -333,16 +350,31 @@ nsJARChannel::EnsureJarInput(PRBool bloc NS_UnescapeURL(mJarEntry); // try to get a nsIFile directly from the url, which will often succeed. { nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(mJarBaseURI); if (fileURL) fileURL->GetFile(getter_AddRefs(mJarFile)); } + // try to handle a nested jar + if (!mJarFile) { + nsCOMPtr<nsIJARURI> jarURI = do_QueryInterface(mJarBaseURI); + if (jarURI) { + nsCOMPtr<nsIFileURL> fileURL; + nsCOMPtr<nsIURI> innerJarURI; + rv = jarURI->GetJARFile(getter_AddRefs(innerJarURI)); + if (NS_SUCCEEDED(rv)) + fileURL = do_QueryInterface(innerJarURI); + if (fileURL) { + fileURL->GetFile(getter_AddRefs(mJarFile)); + jarURI->GetJAREntry(mInnerJarEntry); + } + } + } if (mJarFile) { mIsUnsafe = PR_FALSE; // NOTE: we do not need to deal with mSecurityInfo here, // because we're loading from a local file rv = CreateJarInput(gJarHandler->JarCache()); }
--- a/modules/libjar/nsJARChannel.h +++ b/modules/libjar/nsJARChannel.h @@ -103,11 +103,12 @@ private: PRPackedBool mIsUnsafe; nsJARInputThunk *mJarInput; nsCOMPtr<nsIStreamListener> mDownloader; nsCOMPtr<nsIInputStreamPump> mPump; nsCOMPtr<nsIFile> mJarFile; nsCOMPtr<nsIURI> mJarBaseURI; nsCString mJarEntry; + nsCString mInnerJarEntry; }; #endif // nsJARChannel_h__