Backing out 510844 to fix unittest failure on CLOSED TREE.
authorReed Loden <reed@reedloden.com>
Tue, 01 Sep 2009 16:20:47 -0500
changeset 32143 ac90123f11e9a9a6dedf1721f5b3e40424d4d865
parent 32139 3b1daf281dedf262a0e970202bd3d8f2ee719fb7
child 32144 35f87e14ce99acb1ebcad2433d0f50570ed01aa3
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs510844
milestone1.9.3a1pre
Backing out 510844 to fix unittest failure on CLOSED TREE.
modules/libjar/nsJAR.cpp
modules/libjar/nsJARInputStream.cpp
modules/libjar/nsJARInputStream.h
modules/libjar/nsZipArchive.cpp
modules/libjar/nsZipArchive.h
--- a/modules/libjar/nsJAR.cpp
+++ b/modules/libjar/nsJAR.cpp
@@ -334,17 +334,17 @@ nsJAR::GetInputStreamWithSpec(const nsAC
   // addref now so we can call InitFile/InitDirectory()
   NS_ENSURE_TRUE(jis, NS_ERROR_OUT_OF_MEMORY);
   NS_ADDREF(*result = jis);
 
   nsresult rv = NS_OK;
   if (!item || item->isDirectory) {
     rv = jis->InitDirectory(this, aJarDirSpec, aEntryName);
   } else {
-    rv = jis->InitFile(this, item);
+    rv = jis->InitFile(mZip.GetFD(item), item);
   }
   if (NS_FAILED(rv)) {
     NS_RELEASE(*result);
   }
   return rv;
 }
 
 //----------------------------------------------
--- a/modules/libjar/nsJARInputStream.cpp
+++ b/modules/libjar/nsJARInputStream.cpp
@@ -54,68 +54,68 @@
 
 NS_IMPL_THREADSAFE_ISUPPORTS1(nsJARInputStream, nsIInputStream)
 
 /*----------------------------------------------------------
  * nsJARInputStream implementation
  *--------------------------------------------------------*/
 
 nsresult
-nsJARInputStream::InitFile(nsJAR *aJar, nsZipItem *item)
+nsJARInputStream::InitFile(nsZipHandle *aFd, nsZipItem *item)
 {
-    nsresult rv = NS_OK;
-    NS_ABORT_IF_FALSE(aJar, "Argument may not be null");
+    nsresult rv;
+    NS_ABORT_IF_FALSE(aFd, "Argument may not be null");
     NS_ABORT_IF_FALSE(item, "Argument may not be null");
 
     // Mark it as closed, in case something fails in initialisation
-    mClosed = true;
+    mClosed = PR_TRUE;
+    // Keep the important bits of nsZipItem only
+    mInSize = item->size;
+
     //-- prepare for the compression type
     switch (item->compression) {
        case STORED: 
-           mCompressed = false;
            break;
 
        case DEFLATED:
-           mCompressed = true;
-           rv = gZlibInit(&mZs);
+           mInflate = (InflateStruct *) PR_Malloc(sizeof(InflateStruct));
+           NS_ENSURE_TRUE(mInflate, NS_ERROR_OUT_OF_MEMORY);
+    
+           rv = gZlibInit(&(mInflate->mZs));
            NS_ENSURE_SUCCESS(rv, NS_ERROR_OUT_OF_MEMORY);
     
-           mOutSize = item->realsize;
-           mInCrc = item->crc32;
-           mOutCrc = crc32(0L, Z_NULL, 0);
+           mInflate->mOutSize = item->realsize;
+           mInflate->mInCrc = item->crc32;
+           mInflate->mOutCrc = crc32(0L, Z_NULL, 0);
            break;
 
        default:
            return NS_ERROR_NOT_IMPLEMENTED;
     }
    
-    // Must keep handle to filepointer and mmap structure as long as we need access to the mmapped data
-    mFd = aJar->mZip.GetFD();
-    mZs.next_in = aJar->mZip.GetData(item);
-    mZs.avail_in = item->size;
-    mOutSize = item->realsize;
-    mDirectory = false;
+    //-- Set filepointer to start of item
+    mFd.Open(aFd, item->dataOffset, item->size);
+        
     // Open for reading
-    mClosed = false;
+    mClosed = PR_FALSE;
     mCurPos = 0;
     return NS_OK;
 }
 
 nsresult
 nsJARInputStream::InitDirectory(nsJAR* aJar,
                                 const nsACString& aJarDirSpec,
                                 const char* aDir)
 {
     NS_ABORT_IF_FALSE(aJar, "Argument may not be null");
     NS_ABORT_IF_FALSE(aDir, "Argument may not be null");
 
     // Mark it as closed, in case something fails in initialisation
-    mClosed = true;
-    mDirectory = true;
-    mCompressed = false;
+    mClosed = PR_TRUE;
+    mDirectory = PR_TRUE;
     
     // Keep the zipReader for getting the actual zipItems
     mJar = aJar;
     nsZipFind *find;
     nsresult rv;
     // We can get aDir's contents as strings via FindEntries
     // with the following pattern (see nsIZipReader.findEntries docs)
     // assuming dirName is properly escaped:
@@ -168,69 +168,80 @@ nsJARInputStream::InitDirectory(nsJAR* a
     // Sort it
     mArray.Sort();
 
     mBuffer.AssignLiteral("300: ");
     mBuffer.Append(aJarDirSpec);
     mBuffer.AppendLiteral("\n200: filename content-length last-modified file-type\n");
 
     // Open for reading
-    mClosed = false;
+    mClosed = PR_FALSE;
     mCurPos = 0;
     mArrPos = 0;
     return NS_OK;
 }
 
 NS_IMETHODIMP 
 nsJARInputStream::Available(PRUint32 *_retval)
 {
     if (mClosed)
         return NS_BASE_STREAM_CLOSED;
 
     if (mDirectory)
         *_retval = mBuffer.Length();
-    else if (mCompressed) 
-        *_retval = mOutSize - mZs.total_out;
+    else if (mInflate) 
+        *_retval = mInflate->mOutSize - mInflate->mZs.total_out;
     else 
-        *_retval = mOutSize - mCurPos;
+        *_retval = mInSize - mCurPos;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsJARInputStream::Read(char* aBuffer, PRUint32 aCount, PRUint32 *aBytesRead)
 {
     NS_ENSURE_ARG_POINTER(aBuffer);
     NS_ENSURE_ARG_POINTER(aBytesRead);
 
     *aBytesRead = 0;
 
+    nsresult rv = NS_OK;
     if (mClosed)
-        return NS_OK;
+        return rv;
 
     if (mDirectory) {
-        return ReadDirectory(aBuffer, aCount, aBytesRead);
-    } 
-    if (mCompressed) {
-        return ContinueInflate(aBuffer, aCount, aBytesRead);
-    } 
-    if (mFd) {
-        PRUint32 count = PR_MIN(aCount, mOutSize - mCurPos);
-        memcpy(aBuffer, mZs.next_in + mCurPos, count);
-        mCurPos += count;
-        *aBytesRead = count;
+        rv = ReadDirectory(aBuffer, aCount, aBytesRead);
+    } else {
+        if (mInflate) {
+            rv = ContinueInflate(aBuffer, aCount, aBytesRead);
+        } else {
+            PRInt32 bytesRead = 0;
+            aCount = PR_MIN(aCount, mInSize - mCurPos);
+            if (aCount) {
+                bytesRead = mFd.Read(aBuffer, aCount);
+                if (bytesRead < 0)
+                    return NS_ERROR_FILE_CORRUPTED;
+                mCurPos += bytesRead;
+                if ((PRUint32)bytesRead != aCount) {
+                    // file is truncated or was lying about size, we're done
+                    Close();
+                    return NS_ERROR_FILE_CORRUPTED;
+                }
+            }
+            *aBytesRead = bytesRead;
+        }
         // be aggressive about closing!
         // note that sometimes, we will close mFd before we've finished
         // deflating - this is because zlib buffers the input
         // So, don't free the ReadBuf/InflateStruct yet.
         // It is ok to close the fd multiple times (also in nsJARInputStream::Close())
-        if (mCurPos >= mZs.avail_in) {
-            mFd = nsnull;
+        if (mCurPos >= mInSize) {
+            mFd.Close();
         }
     }
-    return NS_OK;
+    return rv;
 }
 
 NS_IMETHODIMP
 nsJARInputStream::ReadSegments(nsWriteSegmentFun writer, void * closure, PRUint32 count, PRUint32 *_retval)
 {
     // don't have a buffer to read from, so this better not be called!
     NS_NOTREACHED("Consumers should be using Read()!");
     return NS_ERROR_NOT_IMPLEMENTED;
@@ -241,54 +252,77 @@ nsJARInputStream::IsNonBlocking(PRBool *
 {
     *aNonBlocking = PR_FALSE;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsJARInputStream::Close()
 {
-    mClosed = true;
-    mFd = nsnull;
-    mJar = nsnull;
+    PR_FREEIF(mInflate);
+    mClosed = PR_TRUE;
+    mFd.Close();
     return NS_OK;
 }
 
 nsresult 
 nsJARInputStream::ContinueInflate(char* aBuffer, PRUint32 aCount,
                                   PRUint32* aBytesRead)
 {
     // No need to check the args, ::Read did that, but assert them at least
+    NS_ASSERTION(mInflate,"inflate data structure missing");
     NS_ASSERTION(aBuffer,"aBuffer parameter must not be null");
     NS_ASSERTION(aBytesRead,"aBytesRead parameter must not be null");
 
     // Keep old total_out count
-    const PRUint32 oldTotalOut = mZs.total_out;
+    const PRUint32 oldTotalOut = mInflate->mZs.total_out;
     
     // make sure we aren't reading too much
-    mZs.avail_out = PR_MIN(aCount, (mOutSize-oldTotalOut));
-    mZs.next_out = (unsigned char*)aBuffer;
+    mInflate->mZs.avail_out = (mInflate->mOutSize-oldTotalOut > aCount) ? aCount : mInflate->mOutSize-oldTotalOut;
+    mInflate->mZs.next_out = (unsigned char*)aBuffer;
+
+    int zerr = Z_OK;
+    //-- inflate loop
+    while (mInflate->mZs.avail_out > 0 && zerr == Z_OK) {
+
+        if (mInflate->mZs.avail_in == 0 && mCurPos < mInSize) {
+            // time to fill the buffer!
+            PRUint32 bytesToRead = PR_MIN(mInSize - mCurPos, ZIP_BUFLEN);
 
-    // now inflate
-    int zerr = inflate(&mZs, Z_SYNC_FLUSH);
+            PRInt32 bytesRead = mFd.Read(mInflate->mReadBuf, bytesToRead);
+            if (bytesRead < 0) {
+                zerr = Z_ERRNO;
+                break;
+            }
+            mCurPos += bytesRead;
+
+            // now set the state for 'inflate'
+            mInflate->mZs.next_in = mInflate->mReadBuf;
+            mInflate->mZs.avail_in = bytesRead;
+        }
+
+        // now inflate
+        zerr = inflate(&(mInflate->mZs), Z_SYNC_FLUSH);
+    }
+
     if ((zerr != Z_OK) && (zerr != Z_STREAM_END))
         return NS_ERROR_FILE_CORRUPTED;
 
-    *aBytesRead = (mZs.total_out - oldTotalOut);
+    *aBytesRead = (mInflate->mZs.total_out - oldTotalOut);
 
     // Calculate the CRC on the output
-    mOutCrc = crc32(mOutCrc, (unsigned char*)aBuffer, *aBytesRead);
+    mInflate->mOutCrc = crc32(mInflate->mOutCrc, (unsigned char*)aBuffer, *aBytesRead);
 
     // be aggressive about ending the inflation
     // for some reason we don't always get Z_STREAM_END
-    if (zerr == Z_STREAM_END || mZs.total_out == mOutSize) {
-        inflateEnd(&mZs);
+    if (zerr == Z_STREAM_END || mInflate->mZs.total_out == mInflate->mOutSize) {
+        inflateEnd(&(mInflate->mZs));
 
         // stop returning valid data as soon as we know we have a bad CRC
-        if (mOutCrc != mInCrc) {
+        if (mInflate->mOutCrc != mInflate->mInCrc) {
             // asserting because while this rarely happens, you definitely
             // want to catch it in debug builds!
             NS_NOTREACHED(0);
             return NS_ERROR_FILE_CORRUPTED;
         }
     }
 
     return NS_OK;
--- a/modules/libjar/nsJARInputStream.h
+++ b/modules/libjar/nsJARInputStream.h
@@ -48,49 +48,55 @@
  * Class nsJARInputStream declaration. This class defines the type of the
  * object returned by calls to nsJAR::GetInputStream(filename) for the
  * purpose of reading a file item out of a JAR file. 
  *------------------------------------------------------------------------*/
 class nsJARInputStream : public nsIInputStream
 {
   public:
     nsJARInputStream() : 
-        mCurPos(0), mCompressed(false), mDirectory(false), mClosed(true)
-    { }
+        mInSize(0), mCurPos(0), mInflate(nsnull), mDirectory(0), mClosed(PR_FALSE)
+  { }
 
-    ~nsJARInputStream() { Close(); }
+  ~nsJARInputStream() {
+    Close();
+  }
 
     NS_DECL_ISUPPORTS
     NS_DECL_NSIINPUTSTREAM
    
     // takes ownership of |fd|, even on failure
-    nsresult InitFile(nsJAR *aJar, nsZipItem *item);
+    nsresult InitFile(nsZipHandle *aFd, nsZipItem *item);
 
     nsresult InitDirectory(nsJAR *aJar,
                            const nsACString& aJarDirSpec,
                            const char* aDir);
   
   private:
-    nsRefPtr<nsZipHandle>  mFd;         // handle for reading
-    PRUint32               mCurPos;     // Current position in input 
-    PRUint32               mOutSize;    // inflated size 
-    PRUint32               mInCrc;      // CRC as provided by the zipentry
-    PRUint32               mOutCrc;     // CRC as calculated by me
-    z_stream               mZs;         // zip data structure
+    PRUint32      mInSize;          // Size in original file 
+    PRUint32      mCurPos;          // Current position in input 
+
+    struct InflateStruct {
+        PRUint32      mOutSize;     // inflated size 
+        PRUint32      mInCrc;       // CRC as provided by the zipentry
+        PRUint32      mOutCrc;      // CRC as calculated by me
+        z_stream      mZs;          // zip data structure
+        unsigned char mReadBuf[ZIP_BUFLEN]; // Readbuffer to inflate from
+    };
+    struct InflateStruct *   mInflate;
 
     /* For directory reading */
-    nsRefPtr<nsJAR>        mJar;        // string reference to zipreader
-    PRUint32               mNameLen;    // length of dirname
-    nsCString              mBuffer;     // storage for generated text of stream
-    PRUint32               mArrPos;     // current position within mArray
-    nsTArray<nsCString>    mArray;      // array of names in (zip) directory
-
-    bool                   mCompressed; // is this compressed?
-    bool                   mDirectory;  // is this a directory?
-    bool                   mClosed;     // Whether the stream is closed
+    nsRefPtr<nsJAR>         mJar;     // string reference to zipreader
+    PRUint32                mNameLen; // length of dirname
+    nsCAutoString           mBuffer;  // storage for generated text of stream
+    PRUint32                mArrPos;  // current position within mArray
+    nsTArray<nsCString>     mArray;   // array of names in (zip) directory
+  PRPackedBool            mDirectory; // is this a directory?
+    PRPackedBool            mClosed;  // Whether the stream is closed
+    nsSeekableZipHandle     mFd;      // handle for reading
 
     nsresult ContinueInflate(char* aBuf, PRUint32 aCount, PRUint32* aBytesRead);
     nsresult ReadDirectory(char* aBuf, PRUint32 aCount, PRUint32* aBytesRead);
     PRUint32 CopyDataToBuffer(char* &aBuffer, PRUint32 &aCount);
 };
 
 #endif /* nsJARINPUTSTREAM_h__ */
 
--- a/modules/libjar/nsZipArchive.cpp
+++ b/modules/libjar/nsZipArchive.cpp
@@ -45,16 +45,17 @@
 /*
  * This module implements a simple archive extractor for the PKZIP format.
  *
  * The underlying nsZipArchive is NOT thread-safe. Do not pass references
  * or pointers to it across thread boundaries.
  */
 
 
+#define ZFILE_CREATE    PR_WRONLY | PR_CREATE_FILE
 #define READTYPE  PRInt32
 #include "zlib.h"
 #include "nsISupportsUtils.h"
 #include "nsRecyclingAllocator.h"
 #include "prio.h"
 #include "plstr.h"
 #include "prlog.h"
 #include "stdlib.h"
@@ -167,17 +168,17 @@ static void *
 zlibAlloc(void *opaque, uInt items, uInt size)
 {
   nsRecyclingAllocator *zallocator = (nsRecyclingAllocator *)opaque;
   if (zallocator) {
     // Bump up x4 allocations
     PRUint32 realitems = items;
     if (size == 4 && items < BY4ALLOC_ITEMS)
       realitems = BY4ALLOC_ITEMS;
-    return zallocator->Calloc(realitems, size);
+     return zallocator->Calloc(realitems, size);
   }
   else
     return calloc(items, size);
 }
 
 static void
 zlibFree(void *opaque, void *ptr)
 {
@@ -222,45 +223,49 @@ NS_IMPL_THREADSAFE_RELEASE(nsZipHandle)
 
 nsresult nsZipHandle::Init(PRFileDesc *fd, nsZipHandle **ret)
 {
   PRInt64 size = PR_Available64(fd);
   if (size >= PR_INT32_MAX)
     return NS_ERROR_FILE_TOO_BIG;
 
   PRFileMap *map = PR_CreateFileMap(fd, size, PR_PROT_READONLY);
+
   if (!map)
     return NS_ERROR_FAILURE;
 
   nsZipHandle *handle = new nsZipHandle();
-  if (!handle) {
-    PR_CloseFileMap(map);
+  if (!handle)
     return NS_ERROR_OUT_OF_MEMORY;
-  }
 
   handle->mFd = fd;
   handle->mMap = map;
   handle->mLen = (PRUint32) size;
   handle->mFileData = (PRUint8*) PR_MemMap(map, 0, handle->mLen);
   handle->AddRef();
   *ret = handle;
   return NS_OK;
 }
 
+PRInt32 nsZipHandle::Read(PRUint32 aPosition, void *aBuffer, PRUint32 aCount)
+{
+  if (mLen < 0 || aPosition+aCount > (PRUint32) mLen)
+    return -1;
+  PRInt32 count = PR_MIN(aCount, mLen - aPosition);
+  memcpy(aBuffer, mFileData + aPosition, count);
+  return count;
+}
+
 nsZipHandle::~nsZipHandle()
 {
-  if (mFileData) {
+  if (mFd) {
     PR_MemUnmap(mFileData, mLen);
     PR_CloseFileMap(mMap);
-    mFileData = nsnull;
-    mMap = nsnull;
-  }
-  if (mFd) {
     PR_Close(mFd);
-    mFd = nsnull;
+    mFd = 0;
   }
   MOZ_COUNT_DTOR(nsZipHandle);
 }
 
 //***********************************************************
 //      nsZipArchive  --  public methods
 //***********************************************************
 
@@ -316,28 +321,30 @@ nsresult nsZipArchive::Test(const char *
 
 //---------------------------------------------
 //  nsZipArchive::CloseArchive
 //---------------------------------------------
 nsresult nsZipArchive::CloseArchive()
 {
   if (mFd) {
     PL_FinishArenaPool(&mArena);
-    mFd = NULL;
   }
 
   // CAUTION:
   // We don't need to delete each of the nsZipItem as the memory for
   // the zip item and the filename it holds are both allocated from the Arena.
   // Hence, destroying the Arena is like destroying all the memory
   // for all the nsZipItem in one shot. But if the ~nsZipItem is doing
   // anything more than cleaning up memory, we should start calling it.
   // Let us also cleanup the mFiles table for re-use on the next 'open' call
-  memset(mFiles, 0, sizeof(mFiles));
-  mBuiltSynthetics = false;
+  for (int i = 0; i < ZIP_TABSIZE; i++) {
+    mFiles[i] = 0;
+  }
+  mFd = NULL;
+  mBuiltSynthetics = PR_FALSE;
   return ZIP_OK;
 }
 
 //---------------------------------------------
 // nsZipArchive::GetItem
 //---------------------------------------------
 nsZipItem*  nsZipArchive::GetItem(const char * aEntryName)
 {
@@ -376,27 +383,35 @@ nsresult nsZipArchive::ExtractFile(nsZip
     return ZIP_ERR_PARAM;
   if (!mFd)
     return ZIP_ERR_GENERAL;
 
   // Directory extraction is handled in nsJAR::Extract,
   // so the item to be extracted should never be a directory
   PR_ASSERT(!item->isDirectory);
 
+  //-- move to the start of file's data
+  if (!MaybeReadItem(item))
+    return ZIP_ERR_CORRUPT;
+
+  nsSeekableZipHandle fd;
+  if (!fd.Open(mFd.get(), item->dataOffset, item->size))
+    return ZIP_ERR_CORRUPT;
+
   nsresult rv;
 
   //-- extract the file using the appropriate method
   switch(item->compression)
   {
     case STORED:
-      rv = CopyItemToDisk(item, aFd);
+      rv = CopyItemToDisk(item->size, item->crc32, fd, aFd);
       break;
 
     case DEFLATED:
-      rv = InflateItem(item, aFd);
+      rv = InflateItem(item, fd, aFd);
       break;
 
     default:
       //-- unsupported compression type
       rv = ZIP_ERR_UNSUPPORTED;
   }
 
   //-- delete the file on errors, or resolve symlink if needed
@@ -587,22 +602,24 @@ nsresult nsZipArchive::BuildFileList()
     if (namelen > BR_BUF_SIZE || extralen > BR_BUF_SIZE || commentlen > 2*BR_BUF_SIZE)
       return ZIP_ERR_CORRUPT;
 
     nsZipItem* item = CreateZipItem(namelen);
     if (!item)
       return ZIP_ERR_MEMORY;
 
     item->headerOffset  = xtolong(central->localhdr_offset);
+    item->dataOffset    = 0;
     item->size          = xtolong(central->size);
     item->realsize      = xtolong(central->orglen);
     item->crc32         = xtolong(central->crc32);
     item->time          = xtoint(central->time);
     item->date          = xtoint(central->date);
     item->isSynthetic   = PR_FALSE;
+    item->hasDataOffset = PR_FALSE;
     item->compression   = PR_MIN(xtoint(central->method), UNSUPPORTED);
     item->mode          = ExtractMode(central->external_attributes);
 #if defined(XP_UNIX) || defined(XP_BEOS)
     // Check if item is a symlink
     item->isSymlink = IsSymlink(central->external_attributes);
 #endif
 
     buf += ZIPCENTRAL_SIZE;
@@ -630,17 +647,17 @@ nsresult nsZipArchive::BuildFileList()
 
 //---------------------------------------------
 //  nsZipArchive::BuildSynthetics
 //---------------------------------------------
 nsresult nsZipArchive::BuildSynthetics()
 {
   if (mBuiltSynthetics)
     return ZIP_OK;
-  mBuiltSynthetics = true;
+  mBuiltSynthetics = PR_TRUE;
 
   // Create synthetic entries for any missing directories.
   // Do this when all ziptable has scanned to prevent double entries.
   for (int i = 0; i < ZIP_TABSIZE; ++i)
   {
     for (nsZipItem* item = mFiles[i]; item != 0; item = item->next)
     {
       if (item->isSynthetic)
@@ -715,157 +732,233 @@ nsresult nsZipArchive::BuildSynthetics()
         diritem->next = mFiles[hash];
         mFiles[hash] = diritem;
       } /* end processing of dirs in item's name */
     }
   }
   return ZIP_OK;
 }
 
-nsZipHandle* nsZipArchive::GetFD()
+nsZipHandle* nsZipArchive::GetFD(nsZipItem* aItem)
 {
-  if (!mFd)
+  if (!mFd || !MaybeReadItem(aItem))
     return NULL;
   return mFd.get();
 }
 
 //---------------------------------------------
-// nsZipArchive::GetData
+// nsZipArchive::MaybeReadItem
 //---------------------------------------------
-PRUint8* nsZipArchive::GetData(nsZipItem* aItem)
+bool nsZipArchive::MaybeReadItem(nsZipItem* aItem)
 {
   PR_ASSERT (aItem);
 
-  //-- read local header to get variable length values and calculate
-  //-- the real data offset
-  if (aItem->headerOffset + ZIPLOCAL_SIZE > mFd->mLen)
-    return nsnull;
+  //-- the first time an item is used we need to calculate its offset
+  if (!aItem->hasDataOffset)
+  {
+    //-- read local header to get variable length values and calculate
+    //-- the real data offset
+    //--
+    //-- NOTE: extralen is different in central header and local header
+    //--       for archives created using the Unix "zip" utility. To set
+    //--       the offset accurately we need the _local_ extralen.
+    if (!mFd || !mFd->mLen > aItem->headerOffset + ZIPLOCAL_SIZE)
+      return false;
 
-  // -- check signature before using the structure, in case the zip file is corrupt
-  ZipLocal* Local = (ZipLocal*)(mFd->mFileData + aItem->headerOffset);
-  if ((xtolong(Local->signature) != LOCALSIG))
-    return nsnull;
+    ZipLocal   *Local =     (ZipLocal*)(mFd->mFileData + aItem->headerOffset);
+    //check limits here
+    if ((xtolong(Local->signature) != LOCALSIG))
+    {
+      //-- read error or local header not found
+      return false;
+    }
 
-  //-- NOTE: extralen is different in central header and local header
-  //--       for archives created using the Unix "zip" utility. To set
-  //--       the offset accurately we need the _local_ extralen.
-  PRUint32 dataOffset = aItem->headerOffset +
+    aItem->dataOffset = aItem->headerOffset +
                         ZIPLOCAL_SIZE +
                         xtoint(Local->filename_len) +
                         xtoint(Local->extrafield_len);
+    aItem->hasDataOffset = PR_TRUE;
+  }
 
-  // -- check if there is enough source data in the file
-  if (dataOffset + aItem->size > mFd->mLen)
-    return nsnull;
-
-  return mFd->mFileData + dataOffset;
+  return true;
 }
 
 //---------------------------------------------
 // nsZipArchive::CopyItemToDisk
 //---------------------------------------------
 nsresult
-nsZipArchive::CopyItemToDisk(nsZipItem *item, PRFileDesc* outFD)
+nsZipArchive::CopyItemToDisk(PRUint32 itemSize, PRUint32 itemCrc, 
+                             nsSeekableZipHandle &fd, PRFileDesc* outFD)
+/*
+ * This function copies an archive item to disk, to the
+ * file specified by outFD. If outFD is zero, the extracted data is
+ * not written, only checked for CRC, so this is in effect same as 'Test'.
+ */
 {
-  PR_ASSERT(item);
+  PRUint32    chunk, pos, crc;
+  char buf[ZIP_BUFLEN];
+
+  //-- initialize crc
+  crc = crc32(0L, Z_NULL, 0);
 
-  //-- get to the start of file's data
-  const PRUint8* itemData = GetData(item);
-  if (!itemData)
-    return ZIP_ERR_CORRUPT;
+  //-- copy chunks until file is done
+  for (pos = 0; pos < itemSize; pos += chunk)
+  {
+    chunk = (itemSize - pos < ZIP_BUFLEN) ? (itemSize - pos) : ZIP_BUFLEN;
+    
+    if (fd.Read(buf, chunk) != (READTYPE)chunk)
+    {
+      //-- unexpected end of data in archive
+      return ZIP_ERR_CORRUPT;
+    }
 
-  if (outFD && PR_Write(outFD, itemData, item->size) < (READTYPE)item->size)
-  {
-    //-- Couldn't write all the data (disk full?)
-    return ZIP_ERR_DISK;
+    //-- incrementally update crc32
+    crc = crc32(crc, (const unsigned char*)buf, chunk);
+
+    if (outFD && PR_Write(outFD, buf, chunk) < (READTYPE)chunk)
+    {
+      //-- Couldn't write all the data (disk full?)
+      return ZIP_ERR_DISK;
+    }
   }
 
-  //-- Calculate crc
-  PRUint32 crc = crc32(0L, (const unsigned char*)itemData, item->size);
   //-- verify crc32
-  if (crc != item->crc32)
+  if (crc != itemCrc)
       return ZIP_ERR_CORRUPT;
 
   return ZIP_OK;
 }
 
 
 //---------------------------------------------
 // nsZipArchive::InflateItem
 //---------------------------------------------
-nsresult nsZipArchive::InflateItem(nsZipItem * item, PRFileDesc* outFD)
+nsresult nsZipArchive::InflateItem(const nsZipItem* aItem, nsSeekableZipHandle &fd, PRFileDesc* outFD)
 /*
  * This function inflates an archive item to disk, to the
  * file specified by outFD. If outFD is zero, the extracted data is
  * not written, only checked for CRC, so this is in effect same as 'Test'.
  */
 {
-  PR_ASSERT(item);
+  PR_ASSERT(aItem);
+
   //-- allocate deflation buffers
+  Bytef inbuf[ZIP_BUFLEN];
   Bytef outbuf[ZIP_BUFLEN];
 
   //-- set up the inflate
   z_stream    zs;
   nsresult status = gZlibInit(&zs);
   if (status != ZIP_OK)
     return ZIP_ERR_GENERAL;
 
   //-- inflate loop
-  zs.avail_in = item->size;
-  zs.next_in = (Bytef*)GetData(item);
-  if (!zs.next_in)
-    return ZIP_ERR_CORRUPT;
+  zs.next_out = outbuf;
+  zs.avail_out = ZIP_BUFLEN;
 
+  PRUint32  size = aItem->size;
+  PRUint32  outpos = 0;
   PRUint32  crc = crc32(0L, Z_NULL, 0);
-  int zerr = Z_OK;
+  int       zerr = Z_OK;
   while (zerr == Z_OK)
   {
-    zs.next_out = outbuf;
-    zs.avail_out = ZIP_BUFLEN;
+    PRBool      bRead = PR_FALSE;
+    PRBool      bWrote= PR_FALSE;
+
+    if (zs.avail_in == 0 && zs.total_in < size)
+    {
+      //-- no data to inflate yet still more in file:
+      //-- read another chunk of compressed data
+      PRUint32 chunk = (size-zs.total_in < ZIP_BUFLEN) ? size-zs.total_in : ZIP_BUFLEN;
 
-    zerr = inflate(&zs, Z_PARTIAL_FLUSH);
-    if (zerr != Z_OK && zerr != Z_STREAM_END)
-    {
-      status = (zerr == Z_MEM_ERROR) ? ZIP_ERR_MEMORY : ZIP_ERR_CORRUPT;
-      break;
+      if (fd.Read(inbuf, chunk) != (READTYPE)chunk)
+      {
+        //-- unexpected end of data
+        status = ZIP_ERR_CORRUPT;
+        break;
+      }
+
+      zs.next_in  = inbuf;
+      zs.avail_in = chunk;
+      bRead       = PR_TRUE;
     }
-    PRUint32 count = zs.next_out - outbuf;
 
-    //-- incrementally update crc32
-    crc = crc32(crc, (const unsigned char*)outbuf, count);
+    if (zs.avail_out == 0)
+    {
+      //-- write inflated buffer to disk and make space
+      if (outFD && PR_Write(outFD, outbuf, ZIP_BUFLEN) < ZIP_BUFLEN)
+      {
+        //-- Couldn't write all the data (disk full?)
+        status = ZIP_ERR_DISK;
+        break;
+      }
 
-    if (outFD && PR_Write(outFD, outbuf, count) < (READTYPE)count)
+      outpos = zs.total_out;
+      zs.next_out  = outbuf;
+      zs.avail_out = ZIP_BUFLEN;
+      bWrote       = PR_TRUE;
+    }
+
+    if(bRead || bWrote)
     {
-      status = ZIP_ERR_DISK;
-      break;
+      Bytef* old_next_out = zs.next_out;
+
+      zerr = inflate(&zs, Z_PARTIAL_FLUSH);
+
+      //-- incrementally update crc32
+      crc = crc32(crc, (const unsigned char*)old_next_out, zs.next_out - old_next_out);
     }
+    else
+      zerr = Z_STREAM_END;
+
   } // while
 
+  //-- verify crc32
+  if ((status == ZIP_OK) && (crc != aItem->crc32))
+  {
+      status = ZIP_ERR_CORRUPT;
+      goto cleanup;
+  }
+
+  //-- write last inflated bit to disk
+  if (zerr == Z_STREAM_END && outpos < zs.total_out)
+  {
+    PRUint32 chunk = zs.total_out - outpos;
+    if (outFD && PR_Write(outFD, outbuf, chunk) < (READTYPE)chunk)
+      status = ZIP_ERR_DISK;
+  }
+
+  //-- convert zlib error to return value
+  if (status == ZIP_OK && zerr != Z_OK && zerr != Z_STREAM_END)
+  {
+    status = (zerr == Z_MEM_ERROR) ? ZIP_ERR_MEMORY : ZIP_ERR_CORRUPT;
+  }
+
+  //-- if found no errors make sure we've converted the whole thing
+  PR_ASSERT(status != ZIP_OK || zs.total_in == aItem->size);
+  PR_ASSERT(status != ZIP_OK || zs.total_out == aItem->realsize);
+
+cleanup:
   //-- free zlib internal state
   inflateEnd(&zs);
 
-  //-- verify crc32
-  if ((status == ZIP_OK) && (crc != item->crc32))
-  {
-    status = ZIP_ERR_CORRUPT;
-  }
   return status;
 }
 
 //------------------------------------------
 // nsZipArchive constructor and destructor
 //------------------------------------------
 
 nsZipArchive::nsZipArchive() :
-  mBuiltSynthetics(false)
+  mBuiltSynthetics(PR_FALSE)
 {
   MOZ_COUNT_CTOR(nsZipArchive);
 
   // initialize the table to NULL
-  memset(mFiles, 0, sizeof(mFiles));
+  memset(mFiles, 0, sizeof mFiles);
 }
 
 nsZipArchive::~nsZipArchive()
 {
   CloseArchive();
 
   MOZ_COUNT_DTOR(nsZipArchive);
 }
--- a/modules/libjar/nsZipArchive.h
+++ b/modules/libjar/nsZipArchive.h
@@ -41,26 +41,29 @@
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsZipArchive_h_
 #define nsZipArchive_h_
 
 #define ZIP_MAGIC     0x5A49505FL   /* "ZIP_" */
 #define ZIPFIND_MAGIC 0x5A495046L   /* "ZIPF" */
 #define ZIP_TABSIZE   256
-/* We really want to be a (multiple of) 4K for optimal file IO */
-#define ZIP_BUFLEN    (4*1024)      /* Used as output buffer when deflating items to a file */
+// Keep this odd. The -1 is significant.
+#define ZIP_BUFLEN    (4 * 1024 - 1)
 
 #define PL_ARENA_CONST_ALIGN_MASK 7
 #include "plarena.h"
+#define ZIP_Seek(fd,p,m) (PR_Seek((fd),((PROffset32)p),(m))==((PROffset32)p))
 
 #include "zlib.h"
 #include "nsAutoPtr.h"
 
 class nsZipFind;
+class nsZipReadState;
+class nsZipItemMetadata;
 
 struct PRFileDesc;
 
 /**
  * This file defines some of the basic structures used by libjar to
  * read Zip files. It makes use of zlib in order to do the decompression.
  *
  * A few notes on the classes/structs:
@@ -80,39 +83,43 @@ struct PRFileDesc;
  * each nsZipItem represents one file in the archive and all the
  * information needed to manipulate it.
  */
 struct nsZipItem
 {
   nsZipItem*  next;
 
   PRUint32    headerOffset;
+  PRUint32    dataOffset;
   PRUint32    size;             /* size in original file */
   PRUint32    realsize;         /* inflated size */
   PRUint32    crc32;
 
   /*
    * Keep small items together, to avoid overhead.
    */
   PRUint16     time;
   PRUint16     date;
   PRUint16     mode;
   PRUint8      compression;
-  bool         isDirectory;
-  bool         isSynthetic;     /* whether item is an actual zip entry or was
-                                   generated as part of a real entry's path */
+  PRPackedBool hasDataOffset : 1;
+  PRPackedBool isDirectory : 1; 
+  PRPackedBool isSynthetic : 1;  /* whether item is an actual zip entry or was
+                                    generated as part of a real entry's path,
+                                    e.g. foo/ in a zip containing only foo/a.txt
+                                    and no foo/ entry is synthetic */
 #if defined(XP_UNIX) || defined(XP_BEOS)
-  bool         isSymlink;
+  PRPackedBool isSymlink : 1;
 #endif
 
-  char         name[1];         /* actually, bigger than 1 */
+  char        name[1]; // actually, bigger than 1
 };
 
 class nsZipHandle;
-
+class nsSeekableZipHandle;
 /** 
  * nsZipArchive -- a class for reading the PKZIP file format.
  *
  */
 class nsZipArchive 
 {
   friend class nsZipFind;
 
@@ -178,82 +185,146 @@ public:
    *                      (may be NULL to find all files in archive)
    * @param   aFind       a pointer to a pointer to a structure used
    *                      in FindNext.  In the case of an error this
    *                      will be set to NULL.
    * @return  status code
    */
   PRInt32 FindInit(const char * aPattern, nsZipFind** aFind);
 
-  /*
-   * Gets an undependent handle to the mapped file.
+  /* Gets an undependent handle to the jar
+   * Also ensures that aItem is fully filled
    */
-  nsZipHandle* GetFD();
-
-  /**
-   * Get pointer to the data of the item.
-   * @param   aItem       Pointer to nsZipItem
-   * reutrns null when zip file is corrupt.
-   */
-  PRUint8* GetData(nsZipItem* aItem);
+  nsZipHandle* GetFD(nsZipItem* aItem);
 
 private:
   //--- private members ---
 
   nsZipItem*    mFiles[ZIP_TABSIZE];
   PLArenaPool   mArena;
 
+  /**
+   * Fills in nsZipItem fields that were not filled in by BuildFileList
+   * @param   aItem       Pointer to nsZipItem
+   * returns true if the item was filled in successfully
+   */
+  bool MaybeReadItem(nsZipItem* aItem);
+
   // Whether we synthesized the directory entries
-  bool          mBuiltSynthetics;
+  PRPackedBool  mBuiltSynthetics;
 
   // file handle
   nsRefPtr<nsZipHandle> mFd;
   //--- private methods ---
   
   nsZipArchive& operator=(const nsZipArchive& rhs); // prevent assignments
   nsZipArchive(const nsZipArchive& rhs);            // prevent copies
 
   nsZipItem*        CreateZipItem(PRUint16 namelen);
   nsresult          BuildFileList();
   nsresult          BuildSynthetics();
 
-  nsresult  CopyItemToDisk(nsZipItem* item, PRFileDesc* outFD);
-  nsresult  InflateItem(nsZipItem* item, PRFileDesc* outFD);
+  nsresult  CopyItemToDisk(PRUint32 size, PRUint32 crc, nsSeekableZipHandle &fd, PRFileDesc* outFD);
+  nsresult  InflateItem(const nsZipItem* aItem, nsSeekableZipHandle &fd, PRFileDesc* outFD);
 };
 
 class nsZipHandle {
 friend class nsZipArchive;
+friend class nsSeekableZipHandle;
 public:
   static nsresult Init(PRFileDesc *fd, nsZipHandle **ret NS_OUTPARAM);
 
+  /**
+   * Reads data at a certain point
+   * @param aPosition seek ofset
+   * @param aBuffer buffer
+   * @param aCount number of bytes to read */
+  PRInt32 Read(PRUint32 aPosition, void *aBuffer, PRUint32 aCount);
+
   NS_METHOD_(nsrefcnt) AddRef(void);
   NS_METHOD_(nsrefcnt) Release(void);
 
 protected:
-  PRFileDesc * mFd;       /* OS file-descriptor */
-  PRUint8 *    mFileData; /* pointer to mmaped file */
-  PRUint32     mLen;      /* length of file and memory mapped area */
+  PRFileDesc *mFd; // OS file-descriptor
+  PRUint8 *mFileData; // pointer to mmaped file
+  PRUint32 mLen; // length of file and memory mapped area
 
 private:
   nsZipHandle();
   ~nsZipHandle();
 
-  PRFileMap *  mMap;      /* nspr datastructure for mmap */
-  nsrefcnt     mRefCnt;   /* ref count */
+  PRFileMap *mMap; // nspr datastructure for mmap
+  nsrefcnt mRefCnt; // ref count
+};
+
+
+/** nsSeekableZipHandle acts as a container for nsZipHandle,
+    emulates sequential file io */
+class nsSeekableZipHandle {
+  //   stick nsZipItem in here
+public:
+  nsSeekableZipHandle()
+    : mOffset(0)
+    , mRemaining(0)
+  {
+  }
+
+  /** Initializes nsSeekableZipHandle with
+   * @param aOffset byte offset of the file to start reading at
+   * @param length of this descriptor
+   */
+  bool Open(nsZipHandle *aHandle, PRUint32 aOffset, PRUint32 aLength) {
+    NS_ABORT_IF_FALSE (aHandle, "Argument must not be NULL");
+    if (aOffset > aHandle->mLen)
+      return false;
+    mFd = aHandle;
+    mOffset = aOffset;
+    mRemaining = aLength;
+    return true;
+  }
+
+  /** Releases the file handle. It is safe to call multiple times. */
+  void Close()
+  {
+    mFd = NULL;
+  }
+
+  /**
+   * Reads data at a certain point
+   * @param aBuffer input buffer
+   * @param aCount number of bytes to read */
+  PRInt32 Read(void *aBuffer, PRUint32 aCount)
+  {
+    if (!mFd.get())
+      return -1;
+    aCount = PR_MIN(mRemaining, aCount);
+    PRInt32 ret = mFd->Read(mOffset, aBuffer, aCount);
+    if (ret > 0) {
+      mOffset += ret;
+      mRemaining -= ret;
+    }
+    return ret;
+  }
+
+private:
+  nsRefPtr<nsZipHandle> mFd; // file handle
+  PRUint32 mOffset; // current reading offset
+  PRUint32 mRemaining; // bytes remaining
 };
 
 
 /** 
  * nsZipFind 
  *
  * a helper class for nsZipArchive, representing a search
  */
 class nsZipFind
 {
 public:
+
   nsZipFind(nsZipArchive* aZip, char* aPattern, PRBool regExp);
   ~nsZipFind();
 
   nsresult      FindNext(const char ** aResult);
 
 private:
   nsZipArchive* mArchive;
   char*         mPattern;