Bug 379633: Scriptable zipwriter component. r=bsmedberg, sr=cbiesinger, a=bzbarsky
authordtownsend@oxymoronical.com
Sun, 16 Sep 2007 16:48:03 -0700
changeset 5928 ad83c6457f840b012d317e11f68eb600a24d828b
parent 5927 853fd76e5c0ac1880074fb72891b0ebb7017b7a4
child 5929 d67324aad3c5a298bf4237c82b83fe72b383f2c6
push idunknown
push userunknown
push dateunknown
reviewersbsmedberg, cbiesinger, bzbarsky
bugs379633
milestone1.9a8pre
Bug 379633: Scriptable zipwriter component. r=bsmedberg, sr=cbiesinger, a=bzbarsky
config/autoconf.mk.in
configure.in
modules/libjar/Makefile.in
modules/libjar/zipwriter/Makefile.in
modules/libjar/zipwriter/public/Makefile.in
modules/libjar/zipwriter/public/nsIZipWriter.idl
modules/libjar/zipwriter/src/Makefile.in
modules/libjar/zipwriter/src/StreamFunctions.cpp
modules/libjar/zipwriter/src/StreamFunctions.h
modules/libjar/zipwriter/src/ZipWriterModule.cpp
modules/libjar/zipwriter/src/nsDeflateConverter.cpp
modules/libjar/zipwriter/src/nsDeflateConverter.h
modules/libjar/zipwriter/src/nsZipDataStream.cpp
modules/libjar/zipwriter/src/nsZipDataStream.h
modules/libjar/zipwriter/src/nsZipHeader.cpp
modules/libjar/zipwriter/src/nsZipHeader.h
modules/libjar/zipwriter/src/nsZipWriter.cpp
modules/libjar/zipwriter/src/nsZipWriter.h
modules/libjar/zipwriter/test/Makefile.in
modules/libjar/zipwriter/test/unit/data/test.png
modules/libjar/zipwriter/test/unit/data/test.txt
modules/libjar/zipwriter/test/unit/data/test.zip
modules/libjar/zipwriter/test/unit/head_zipwriter.js
modules/libjar/zipwriter/test/unit/tail_zipwriter.js
modules/libjar/zipwriter/test/unit/test_asyncadd.js
modules/libjar/zipwriter/test/unit/test_asyncbadadd.js
modules/libjar/zipwriter/test/unit/test_asyncbadremove.js
modules/libjar/zipwriter/test/unit/test_asyncremove.js
modules/libjar/zipwriter/test/unit/test_createempty.js
modules/libjar/zipwriter/test/unit/test_deflatedata.js
modules/libjar/zipwriter/test/unit/test_directory.js
modules/libjar/zipwriter/test/unit/test_editexisting.js
modules/libjar/zipwriter/test/unit/test_storedata.js
modules/libjar/zipwriter/test/unit/test_sync.js
modules/libjar/zipwriter/test/unit/test_undochange.js
modules/libjar/zipwriter/test/unit/test_zipcomment.js
toolkit/library/libxul-config.mk
toolkit/library/nsStaticXULComponents.cpp
toolkit/toolkit-makefiles.sh
--- a/config/autoconf.mk.in
+++ b/config/autoconf.mk.in
@@ -143,16 +143,17 @@ MOZ_IPCD = @MOZ_IPCD@
 MOZ_PROFILESHARING = @MOZ_PROFILESHARING@
 MOZ_PROFILELOCKING = @MOZ_PROFILELOCKING@
 MOZ_FEEDS = @MOZ_FEEDS@
 MOZ_PLACES = @MOZ_PLACES@
 MOZ_PLACES_BOOKMARKS = @MOZ_PLACES_BOOKMARKS@
 MOZ_STORAGE = @MOZ_STORAGE@
 MOZ_SAFE_BROWSING = @MOZ_SAFE_BROWSING@
 MOZ_URL_CLASSIFIER = @MOZ_URL_CLASSIFIER@
+MOZ_ZIPWRITER = @MOZ_ZIPWRITER@
 MOZ_MORK = @MOZ_MORK@
 MOZ_MORKREADER = @MOZ_MORKREADER@
 MOZ_NO_XPCOM_OBSOLETE = @MOZ_NO_XPCOM_OBSOLETE@
 MOZ_NO_FAST_LOAD = @MOZ_NO_FAST_LOAD@
 NS_PRINTING = @NS_PRINTING@
 MOZ_CRASHREPORTER = @MOZ_CRASHREPORTER@
 MOZ_MOCHITEST = @MOZ_MOCHITEST@ 
 
--- a/configure.in
+++ b/configure.in
@@ -4159,16 +4159,17 @@ MOZ_VIEW_SOURCE=1
 MOZ_WEBSERVICES=
 MOZ_XMLEXTRAS=1
 MOZ_XPFE_COMPONENTS=1
 MOZ_XPINSTALL=1
 MOZ_XSLT_STANDALONE=
 MOZ_XTF=1
 MOZ_XUL=1
 MOZ_XUL_APP=1
+MOZ_ZIPWRITER=1
 NS_PRINTING=1
 NECKO_COOKIES=1
 NECKO_DISK_CACHE=1
 NECKO_PROTOCOLS_DEFAULT="about data file ftp gopher http res viewsource"
 NECKO_SMALL_BUFFERS=
 SUNCTL=
 JS_STATIC_BUILD=
 XPC_IDISPATCH_SUPPORT=
@@ -4276,16 +4277,17 @@ basic)
   MOZ_SVG=
   MOZ_UNIVERSALCHARDET=
   MOZ_UPDATER=
   MOZ_USE_NATIVE_UCONV=1
   MOZ_VIEW_SOURCE=
   MOZ_XPFE_COMPONENTS=
   MOZ_XPINSTALL=
   MOZ_XTF=
+  MOZ_ZIPWRITER=
   NECKO_DISK_CACHE=
   NECKO_PROTOCOLS_DEFAULT="about data http file res"
   NECKO_SMALL_BUFFERS=1
   NS_DISABLE_LOGGING=1
   NS_PRINTING=
   JS_STATIC_BUILD=1
   ;;
 
@@ -4326,16 +4328,17 @@ minimal)
   MOZ_UNIVERSALCHARDET=
   MOZ_UPDATER=
   MOZ_USE_NATIVE_UCONV=1
   MOZ_VIEW_SOURCE=
   MOZ_XPFE_COMPONENTS=
   MOZ_XPINSTALL=
   MOZ_XTF=
   MOZ_XUL=
+  MOZ_ZIPWRITER=
   MOZ_RDF=
   NECKO_DISK_CACHE=
   NECKO_PROTOCOLS_DEFAULT="about data http file res"
   NECKO_SMALL_BUFFERS=1
   NS_DISABLE_LOGGING=1
   NS_PRINTING=
   JS_STATIC_BUILD=1
   ;;
@@ -5899,16 +5902,25 @@ MOZ_ARG_ENABLE_BOOL(url-classifier,
     MOZ_URL_CLASSIFIER=1,
     MOZ_URL_CLASSIFIER= )
 if test -n "$MOZ_URL_CLASSIFIER"; then
     AC_DEFINE(MOZ_URL_CLASSIFIER)
 fi
 AC_SUBST(MOZ_URL_CLASSIFIER)
 
 dnl ========================================================
+dnl = Disable zipwriter
+dnl ========================================================
+MOZ_ARG_DISABLE_BOOL(zipwriter,
+[  --disable-zipwriter             Disable zipwriter component],
+    MOZ_ZIPWRITER=,
+    MOZ_ZIPWRITER=1 )
+AC_SUBST(MOZ_ZIPWRITER)
+
+dnl ========================================================
 dnl = Enable Ultrasparc specific optimizations for JS
 dnl ========================================================
 MOZ_ARG_ENABLE_BOOL(js-ultrasparc,
 [  --enable-js-ultrasparc  Use UltraSPARC optimizations in JS],
     JS_ULTRASPARC_OPTS=1,
     JS_ULTRASPARC_OPTS= )
 
 dnl only enable option for ultrasparcs
--- a/modules/libjar/Makefile.in
+++ b/modules/libjar/Makefile.in
@@ -39,19 +39,25 @@
 DEPTH		= ../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 include $(srcdir)/objs.mk
 
+DIRS   =
+
+ifdef MOZ_ZIPWRITER
+DIRS  += zipwriter
+endif
+
 ifdef MOZ_INSTALLER
 # Linux and Solaris installer needs standalone libjar
-DIRS		= standalone
+DIRS  += standalone
 
 # Make this a true dynamic component even in static builds because
 # this component is shared by installer
 ifndef MINIMO
 # we don't care about the installer for MINIMO
 ifndef MOZ_ENABLE_LIBXUL
 BUILD_STATIC_LIBS=
 endif
new file mode 100644
--- /dev/null
+++ b/modules/libjar/zipwriter/Makefile.in
@@ -0,0 +1,53 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is Zip Writer Component.
+#
+# The Initial Developer of the Original Code is
+# Dave Townsend <dtownsend@oxymoronical.com>.
+#
+# Portions created by the Initial Developer are Copyright (C) 2007
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH                 = ../../..
+topsrcdir             = @top_srcdir@
+srcdir                = @srcdir@
+VPATH                 = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE                = zipwriter
+
+DIRS                  = public src
+
+ifdef ENABLE_TESTS
+DIRS                 += test
+endif
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/modules/libjar/zipwriter/public/Makefile.in
@@ -0,0 +1,51 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is Zip Writer Component.
+#
+# The Initial Developer of the Original Code is
+# Dave Townsend <dtownsend@oxymoronical.com>.
+#
+# Portions created by the Initial Developer are Copyright (C) 2007
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH                 = ../../../..
+topsrcdir             = @top_srcdir@
+srcdir                = @srcdir@
+VPATH                 = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE                = zipwriter
+
+XPIDLSRCS = \
+  nsIZipWriter.idl \
+  $(NULL)
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/modules/libjar/zipwriter/public/nsIZipWriter.idl
@@ -0,0 +1,242 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Zip Writer Component.
+ *
+ * The Initial Developer of the Original Code is
+ * Dave Townsend <dtownsend@oxymoronical.com>.
+ *
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+#include "nsISupports.idl"
+interface nsIChannel;
+interface nsIInputStream;
+interface nsIRequestObserver;
+interface nsIFile;
+interface nsIZipEntry;
+
+/**
+ * nsIZipWriter
+ *
+ * An interface for a zip archiver that can be used from script.
+ *
+ * The interface supports both a synchronous method of archiving data and a
+ * queueing system to allow operations to be prepared then run in sequence
+ * with notification after completion.
+ *
+ * Operations added to the queue do not get performed until performQueue is
+ * called at which point they will be performed in the order that they were
+ * added to the queue.
+ *
+ * Operations performed on the queue will throw any errors out to the
+ * observer.
+ *
+ * An attempt to perform a synchronous operation while the background queue
+ * is in progress will throw NS_ERROR_IN_PROGRESS.
+ *
+ * Entry names should use /'s as path separators and should not start with
+ * a /.
+ *
+ * It is not generally necessary to add directory entries in order to add file
+ * entries within them, however it is possible that some zip programs may
+ * experience problems what that.
+ */
+[scriptable, uuid(6d4ef074-206c-4649-9884-57bc355864d6)]
+interface nsIZipWriter : nsISupports
+{
+  /**
+   * Some predefined compression levels
+   */
+  const PRUint32 COMPRESSION_NONE    = 0;
+  const PRUint32 COMPRESSION_FASTEST = 1;
+  const PRUint32 COMPRESSION_DEFAULT = 6;
+  const PRUint32 COMPRESSION_BEST    = 9;
+
+  /**
+   * Gets or sets the comment associated with the open zip file.
+   *
+   * @throws NS_ERROR_NOT_INITIALIZED if no zip file has been opened
+   */
+  attribute ACString comment;
+
+  /**
+   * Indicates that operations on the background queue are being performed.
+   */
+  readonly attribute boolean inQueue;
+
+  /**
+   * The file that the zipwriter is writing to.
+   */
+  readonly attribute nsIFile file;
+
+  /**
+   * Opens a zip file.
+   *
+   * @param aFile the zip file to open
+   * @param aIoFlags the open flags for the zip file from prio.h
+   *
+   * @throws NS_ERROR_ALREADY_INITIALIZED if a zip file is already open
+   * @throws NS_ERROR_INVALID_ARG if aFile is null
+   * @throws NS_ERROR_FILE_NOT_FOUND if aFile does not exist and flags did
+   *  not allow for creation
+   * @throws NS_ERROR_FILE_CORRUPTED if the file does not contain zip markers
+   * @throws <other-error> on failure to open zip file (most likely corrupt
+   *  or unsupported form)
+   */
+  void open(in nsIFile aFile, in PRInt32 aIoFlags);
+
+  /**
+   * Returns a nsIZipEntry describing a specified zip entry or null if there
+   * is no such entry in the zip file
+   *
+   * @param aZipEntry the path of the entry
+   */
+  nsIZipEntry getEntry(in AUTF8String aZipEntry);
+
+  /**
+   * Checks whether the zipfile contains an entry specified by zipEntry.
+   *
+   * @param aZipEntry the path of the entry
+   */
+  boolean hasEntry(in AUTF8String aZipEntry);
+
+  /**
+   * Adds a new directory entry to the zip file. If aZipEntry does not end with
+   * "/" then it will be added.
+   *
+   * @param aZipEntry the path of the directory entry
+   * @param aModTime the modification time of the entry in microseconds
+   * @param aQueue adds the operation to the background queue. Will be
+   *        performed when processQueue is called.
+   *
+   * @throws NS_ERROR_NOT_INITIALIZED if no zip file has been opened
+   * @throws NS_ERROR_FILE_ALREADY_EXISTS if the path already exists in the
+   *  file
+   * @throws NS_ERROR_IN_PROGRESS if another operation is currently in progress
+   */
+  void addEntryDirectory(in AUTF8String aZipEntry, in PRTime aModTime,
+                         in boolean aQueue);
+
+  /**
+   * Adds a new file or directory to the zip file. If the specified file is
+   * a directory then this will be equivalent to a call to
+   * addEntryDirectory(aZipEntry, aFile.lastModifiedTime, aQueue)
+   *
+   * @param aZipEntry the path of the file entry
+   * @param aCompression the compression level, 0 is no compression, 9 is best
+   * @param aFile the file to get the data and modification time from
+   * @param aQueue adds the operation to the background queue. Will be
+   *        performed when processQueue is called.
+   *
+   * @throws NS_ERROR_NOT_INITIALIZED if no zip file has been opened
+   * @throws NS_ERROR_FILE_ALREADY_EXISTS if the path already exists in the zip
+   * @throws NS_ERROR_IN_PROGRESS if another operation is currently in progress
+   * @throws NS_ERROR_FILE_NOT_FOUND if file does not exist
+   */
+  void addEntryFile(in AUTF8String aZipEntry,
+                    in PRInt32 aCompression, in nsIFile aFile,
+                    in boolean aQueue);
+
+  /**
+   * Adds data from a channel to the zip file. If the operation is performed
+   * on the queue then the channel will be opened asynchronously, otherwise
+   * the channel must support being opened synchronously.
+   *
+   * @param aZipEntry the path of the file entry
+   * @param aModTime the modification time of the entry in microseconds
+   * @param aCompression the compression level, 0 is no compression, 9 is best
+   * @param aChannel the channel to get the data from
+   * @param aQueue adds the operation to the background queue. Will be
+   *        performed when processQueue is called.
+   *
+   * @throws NS_ERROR_NOT_INITIALIZED if no zip file has been opened
+   * @throws NS_ERROR_FILE_ALREADY_EXISTS if the path already exists in the zip
+   * @throws NS_ERROR_IN_PROGRESS if another operation is currently in progress
+   */
+  void addEntryChannel(in AUTF8String aZipEntry, in PRTime aModTime,
+                       in PRInt32 aCompression, in nsIChannel aChannel,
+                       in boolean aQueue);
+
+  /**
+   * Adds data from an input stream to the zip file.
+   *
+   * @param aZipEntry the path of the file entry
+   * @param aModTime the modification time of the entry in microseconds
+   * @param aCompression the compression level, 0 is no compression, 9 is best
+   * @param aStream the input stream to get the data from
+   * @param aQueue adds the operation to the background queue. Will be
+   *        performed when processQueue is called.
+   *
+   * @throws NS_ERROR_NOT_INITIALIZED if no zip file has been opened
+   * @throws NS_ERROR_FILE_ALREADY_EXISTS if the path already exists in the zip
+   * @throws NS_ERROR_IN_PROGRESS if another operation is currently in progress
+   */
+  void addEntryStream(in AUTF8String aZipEntry, in PRTime aModTime,
+                      in PRInt32 aCompression, in nsIInputStream aStream,
+                      in boolean aQueue);
+
+  /**
+   * Removes an existing entry from the zip file.
+   *
+   * @param aZipEntry the path of the entry to be removed
+   * @param aQueue adds the operation to the background queue. Will be
+   *        performed when processQueue is called.
+   *
+   * @throws NS_ERROR_NOT_INITIALIZED if no zip file has been opened
+   * @throws NS_ERROR_IN_PROGRESS if another operation is currently in progress
+   * @throws NS_ERROR_FILE_NOT_FOUND if no entry with the given path exists
+   * @throws <other-error> on failure to update the zip file
+   */
+  void removeEntry(in AUTF8String aZipEntry, in boolean aQueue);
+
+  /**
+   * Processes all queued items until complete or some error occurs. The
+   * observer will be notified when the first operation starts and when the
+   * last operation completes. Any failures will be passed to the observer.
+   * The zip writer will be busy until the queue is complete or some error
+   * halted processing of the queue early. In the event of an early failure,
+   * remaining items will stay in the queue and calling processQueue will
+   * continue.
+   *
+   * @throws NS_ERROR_NOT_INITIALIZED if no zip file has been opened
+   * @throws NS_ERROR_IN_PROGRESS if the queue is already in progress
+   */
+  void processQueue(in nsIRequestObserver aObserver, in nsISupports aContext);
+
+  /**
+   * Closes the zip file.
+   *
+   * @throws NS_ERROR_NOT_INITIALIZED if no zip file has been opened
+   * @throws NS_ERROR_IN_PROGRESS if another operation is currently in progress
+   * @throws <other-error> on failure to complete the zip file
+   */
+  void close();
+};
new file mode 100644
--- /dev/null
+++ b/modules/libjar/zipwriter/src/Makefile.in
@@ -0,0 +1,74 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is Zip Writer Component.
+#
+# The Initial Developer of the Original Code is
+# Dave Townsend <dtownsend@oxymoronical.com>.
+#
+# Portions created by the Initial Developer are Copyright (C) 2007
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH                 = ../../../..
+topsrcdir             = @top_srcdir@
+srcdir                = @srcdir@
+VPATH                 = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE                = zipwriter
+LIBRARY_NAME          = zipwriter
+MODULE_NAME           = ZipWriterModule
+EXPORT_LIBRARY        = 1
+LIBXUL_LIBRARY        = 1
+IS_COMPONENT          = 1
+
+REQUIRES = \
+  xpcom \
+  string \
+  necko \
+  jar \
+  $(ZLIB_REQUIRES) \
+  $(NULL)
+
+CPPSRCS = \
+  StreamFunctions.cpp \
+  nsDeflateConverter.cpp \
+  nsZipHeader.cpp \
+  nsZipDataStream.cpp \
+  nsZipWriter.cpp \
+  ZipWriterModule.cpp \
+  $(NULL)
+
+include $(topsrcdir)/config/rules.mk
+
+EXTRA_DSO_LDOPTS += \
+  $(MOZ_COMPONENT_LIBS) \
+  $(ZLIB_LIBS) \
+  $(NULL)
new file mode 100644
--- /dev/null
+++ b/modules/libjar/zipwriter/src/StreamFunctions.cpp
@@ -0,0 +1,82 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Zip Writer Component.
+ *
+ * The Initial Developer of the Original Code is
+ * Dave Townsend <dtownsend@oxymoronical.com>.
+ *
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Mook <mook.moz+random.code@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+#include "nscore.h"
+#include "nsIInputStream.h"
+#include "nsIOutputStream.h"
+
+/*
+ * Fully reads the required amount of data. Keeps reading until all the
+ * data is retrieved or an error is hit.
+ */
+NS_HIDDEN_(nsresult) ZW_ReadData(nsIInputStream *aStream, char *aBuffer, PRUint32 aCount)
+{
+    while (aCount > 0) {
+        PRUint32 read;
+        nsresult rv = aStream->Read(aBuffer, aCount, &read);
+        NS_ENSURE_SUCCESS(rv, rv);
+        aCount -= read;
+        aBuffer += read;
+        // If we hit EOF before reading the data we need then throw.
+        if (read == 0 && aCount > 0)
+            return NS_ERROR_FAILURE;
+    }
+
+    return NS_OK;
+}
+
+/*
+ * Fully writes the required amount of data. Keeps writing untill all the
+ * data is written or an error is hit.
+ */
+NS_HIDDEN_(nsresult) ZW_WriteData(nsIOutputStream *aStream, const char *aBuffer,
+                                  PRUint32 aCount)
+{
+    while (aCount > 0) {
+        PRUint32 written;
+        nsresult rv = aStream->Write(aBuffer, aCount, &written);
+        NS_ENSURE_SUCCESS(rv, rv);
+        if (written <= 0)
+            return NS_ERROR_FAILURE;
+        aCount -= written;
+        aBuffer += written;
+    }
+
+    return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/modules/libjar/zipwriter/src/StreamFunctions.h
@@ -0,0 +1,98 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Zip Writer Component.
+ *
+ * The Initial Developer of the Original Code is
+ * Dave Townsend <dtownsend@oxymoronical.com>.
+ *
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Mook <mook.moz+random.code@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+#ifndef _nsStreamFunctions_h_
+#define _nsStreamFunctions_h_
+
+#include "nscore.h"
+#include "nsIInputStream.h"
+#include "nsIOutputStream.h"
+
+/*
+ * ZIP file data is stored little-endian. These are helper functions to read and
+ * write little endian data to/from a char buffer.
+ * The off argument is incremented according to the number of bytes consumed
+ * from the buffer.
+ */
+inline NS_HIDDEN_(void) WRITE8(char* buf, PRUint32* off, PRUint8 val)
+{
+  buf[(*off)++] = val & 0xff;
+}
+
+inline NS_HIDDEN_(void) WRITE16(char* buf, PRUint32* off, PRUint16 val)
+{
+  buf[(*off)++] = val & 0xff;
+  buf[(*off)++] = (val >> 8) & 0xff;
+}
+
+inline NS_HIDDEN_(void) WRITE32(char* buf, PRUint32* off, PRUint32 val)
+{
+  buf[(*off)++] = val & 0xff;
+  buf[(*off)++] = (val >> 8) & 0xff;
+  buf[(*off)++] = (val >> 16) & 0xff;
+  buf[(*off)++] = (val >> 24) & 0xff;
+}
+
+inline NS_HIDDEN_(PRUint8) READ8(char* buf, PRUint32* off)
+{
+  return (PRUint8)buf[(*off)++];
+}
+
+inline NS_HIDDEN_(PRUint16) READ16(char* buf, PRUint32* off)
+{
+  PRUint16 val = (PRUint16)buf[(*off)++] & 0xff;
+  val |= ((PRUint16)buf[(*off)++] & 0xff) << 8;
+  return val;
+}
+
+inline NS_HIDDEN_(PRUint32) READ32(char* buf, PRUint32* off)
+{
+  PRUint32 val = (PRUint32)buf[(*off)++] & 0xff;
+  val |= ((PRUint32)buf[(*off)++] & 0xff) << 8;
+  val |= ((PRUint32)buf[(*off)++] & 0xff) << 16;
+  val |= ((PRUint32)buf[(*off)++] & 0xff) << 24;
+  return val;
+}
+
+NS_HIDDEN_(nsresult) ZW_ReadData(nsIInputStream *aStream, char *aBuffer, PRUint32 aCount);
+
+NS_HIDDEN_(nsresult) ZW_WriteData(nsIOutputStream *aStream, const char *aBuffer,
+                      PRUint32 aCount);
+
+#endif
new file mode 100644
--- /dev/null
+++ b/modules/libjar/zipwriter/src/ZipWriterModule.cpp
@@ -0,0 +1,62 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Zip Writer Component.
+ *
+ * The Initial Developer of the Original Code is
+ * Dave Townsend <dtownsend@oxymoronical.com>.
+ *
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+#include "nsIGenericFactory.h"
+#include "nsDeflateConverter.h"
+#include "nsZipWriter.h"
+
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsDeflateConverter)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsZipWriter)
+
+static nsModuleComponentInfo components[] =
+{
+  {
+    DEFLATECONVERTER_CLASSNAME,
+    DEFLATECONVERTER_CID,
+    DEFLATECONVERTER_CONTRACTID,
+    nsDeflateConverterConstructor,
+  },
+  {
+    ZIPWRITER_CLASSNAME,
+    ZIPWRITER_CID,
+    ZIPWRITER_CONTRACTID,
+    nsZipWriterConstructor,
+  }
+};
+
+NS_IMPL_NSGETMODULE(ZipWriterModule, components)
new file mode 100644
--- /dev/null
+++ b/modules/libjar/zipwriter/src/nsDeflateConverter.cpp
@@ -0,0 +1,198 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Zip Writer Component.
+ *
+ * The Initial Developer of the Original Code is
+ * Dave Townsend <dtownsend@oxymoronical.com>.
+ *
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Lan Qiang <jameslan@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+#include "StreamFunctions.h"
+#include "nsDeflateConverter.h"
+#include "nsIStringStream.h"
+#include "nsIInputStreamPump.h"
+#include "nsComponentManagerUtils.h"
+#include "nsMemory.h"
+#include "nsAutoPtr.h"
+
+/**
+ * nsDeflateConverter is a stream converter applies the deflate compression
+ * method to the data.
+ */
+NS_IMPL_ISUPPORTS3(nsDeflateConverter, nsIStreamConverter,
+                                       nsIStreamListener,
+                                       nsIRequestObserver)
+
+nsresult nsDeflateConverter::Init()
+{
+    int zerr;
+
+    mOffset = 0;
+
+    mZstream.zalloc = Z_NULL;
+    mZstream.zfree = Z_NULL;
+    mZstream.opaque = Z_NULL;
+
+    zerr = deflateInit2(&mZstream, mLevel, Z_DEFLATED, -MAX_WBITS, 8,
+                        Z_DEFAULT_STRATEGY);
+    if (zerr != Z_OK) return NS_ERROR_OUT_OF_MEMORY;
+
+    mZstream.next_out = mWriteBuffer;
+    mZstream.avail_out = sizeof(mWriteBuffer);
+
+    return NS_OK;
+}
+
+/* nsIInputStream convert (in nsIInputStream aFromStream, in string aFromType
+ *                         in string aToType, in nsISupports aCtxt); */
+NS_IMETHODIMP nsDeflateConverter::Convert(nsIInputStream *aFromStream,
+                                          const char *aFromType,
+                                          const char *aToType,
+                                          nsISupports *aCtxt,
+                                          nsIInputStream **_retval)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* void asyncConvertData (in string aFromType, in string aToType,
+ *                        in nsIStreamListener aListener,
+ *                        in nsISupports aCtxt); */
+NS_IMETHODIMP nsDeflateConverter::AsyncConvertData(const char *aFromType,
+                                                   const char *aToType,
+                                                   nsIStreamListener *aListener,
+                                                   nsISupports *aCtxt)
+{
+    if (mListener)
+        return NS_ERROR_ALREADY_INITIALIZED;
+
+    NS_ENSURE_ARG_POINTER(aListener);
+
+    nsresult rv = Init();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    mListener = aListener;
+    mContext = aCtxt;
+    return rv;
+}
+
+/* void onDataAvailable (in nsIRequest aRequest, in nsISupports aContext,
+ *                       in nsIInputStream aInputStream,
+ *                       in unsigned long aOffset, in unsigned long aCount); */
+NS_IMETHODIMP nsDeflateConverter::OnDataAvailable(nsIRequest *aRequest,
+                                                  nsISupports *aContext,
+                                                  nsIInputStream *aInputStream,
+                                                  PRUint32 aOffset,
+                                                  PRUint32 aCount)
+{
+    if (!mListener)
+        return NS_ERROR_NOT_INITIALIZED;
+
+    nsAutoArrayPtr<char> buffer(new char[aCount]);
+    NS_ENSURE_TRUE(buffer, NS_ERROR_OUT_OF_MEMORY);
+
+    nsresult rv = ZW_ReadData(aInputStream, buffer.get(), aCount);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // make sure we aren't reading too much
+    mZstream.avail_in = aCount;
+    mZstream.next_in = (unsigned char*)buffer.get();
+
+    int zerr = Z_OK;
+    // deflate loop
+    while (mZstream.avail_in > 0 && zerr == Z_OK) {
+        zerr = deflate(&mZstream, Z_NO_FLUSH);
+
+        while (mZstream.avail_out == 0) {
+            // buffer is full, push the data out to the listener
+            rv = PushAvailableData(aRequest, aContext);
+            NS_ENSURE_SUCCESS(rv, rv);
+            zerr = deflate(&mZstream, Z_NO_FLUSH);
+        }
+    }
+
+    return NS_OK;
+}
+
+/* void onStartRequest (in nsIRequest aRequest, in nsISupports aContext); */
+NS_IMETHODIMP nsDeflateConverter::OnStartRequest(nsIRequest *aRequest,
+                                                 nsISupports *aContext)
+{
+    if (!mListener)
+        return NS_ERROR_NOT_INITIALIZED;
+
+    return mListener->OnStartRequest(aRequest, mContext);
+}
+
+/* void onStopRequest (in nsIRequest aRequest, in nsISupports aContext,
+ *                     in nsresult aStatusCode); */
+NS_IMETHODIMP nsDeflateConverter::OnStopRequest(nsIRequest *aRequest,
+                                                nsISupports *aContext,
+                                                nsresult aStatusCode)
+{
+    if (!mListener)
+        return NS_ERROR_NOT_INITIALIZED;
+
+    nsresult rv;
+
+    int zerr;
+    do {
+        zerr = deflate(&mZstream, Z_FINISH);
+        rv = PushAvailableData(aRequest, aContext);
+        NS_ENSURE_SUCCESS(rv, rv);
+    } while (zerr == Z_OK);
+
+    deflateEnd(&mZstream);
+
+    return mListener->OnStopRequest(aRequest, mContext, aStatusCode);
+}
+
+nsresult nsDeflateConverter::PushAvailableData(nsIRequest *aRequest,
+                                               nsISupports *aContext)
+{
+    nsresult rv;
+    nsCOMPtr<nsIStringInputStream> stream =
+             do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    PRUint32 bytesToWrite = ZIP_BUFLEN - mZstream.avail_out;
+    stream->ShareData((char*)mWriteBuffer, bytesToWrite);
+    rv = mListener->OnDataAvailable(aRequest, mContext, stream, mOffset,
+                                    bytesToWrite);
+
+    // now set the state for 'deflate'
+    mZstream.next_out = mWriteBuffer;
+    mZstream.avail_out = sizeof(mWriteBuffer);
+
+    mOffset += bytesToWrite;
+    return rv;
+}
new file mode 100644
--- /dev/null
+++ b/modules/libjar/zipwriter/src/nsDeflateConverter.h
@@ -0,0 +1,91 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Zip Writer Component.
+ *
+ * The Initial Developer of the Original Code is
+ * Dave Townsend <dtownsend@oxymoronical.com>.
+ *
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Lan Qiang <jameslan@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+#ifndef _nsDeflateConverter_h_
+#define _nsDeflateConverter_h_
+
+#include "nsIStreamConverter.h"
+#include "nsCOMPtr.h"
+#include "nsIPipe.h"
+#include "zlib.h"
+
+#define DEFLATECONVERTER_CONTRACTID \
+           "@mozilla.org/streamconv;1?from=uncompressed&to=deflate"
+#define DEFLATECONVERTER_CLASSNAME "Deflate converter"
+#define DEFLATECONVERTER_CID { 0x461cd5dd, 0x73c6, 0x47a4, \
+           { 0x8c, 0xc3, 0x60, 0x3b, 0x37, 0xd8, 0x4a, 0x61 } }
+
+#define ZIP_BUFLEN (4 * 1024 - 1)
+
+class nsDeflateConverter : public nsIStreamConverter
+{
+public:
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSIREQUESTOBSERVER
+    NS_DECL_NSISTREAMLISTENER
+    NS_DECL_NSISTREAMCONVERTER
+
+    nsDeflateConverter()
+    {
+        mLevel = Z_DEFAULT_COMPRESSION;
+    }
+
+    nsDeflateConverter(PRInt32 level)
+    {
+        mLevel = level;
+    }
+
+private:
+
+    ~nsDeflateConverter()
+    {
+    }
+
+    PRUint32 mOffset;
+    PRInt32 mLevel;
+    nsCOMPtr<nsIStreamListener> mListener;
+    nsCOMPtr<nsISupports> mContext;
+    z_stream mZstream;
+    unsigned char mWriteBuffer[ZIP_BUFLEN];
+
+    nsresult Init();
+    nsresult PushAvailableData(nsIRequest *aRequest, nsISupports *aContext);
+};
+
+#endif
new file mode 100644
--- /dev/null
+++ b/modules/libjar/zipwriter/src/nsZipDataStream.cpp
@@ -0,0 +1,230 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Zip Writer Component.
+ *
+ * The Initial Developer of the Original Code is
+ * Dave Townsend <dtownsend@oxymoronical.com>.
+ *
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Mook <mook.moz+random.code@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+#include "StreamFunctions.h"
+#include "nsZipDataStream.h"
+#include "nsIStringStream.h"
+#include "nsISeekableStream.h"
+#include "nsDeflateConverter.h"
+#include "nsNetUtil.h"
+#include "nsComponentManagerUtils.h"
+#include "nsMemory.h"
+
+#define ZIP_METHOD_STORE 0
+#define ZIP_METHOD_DEFLATE 8
+
+/**
+ * nsZipDataStream handles the writing an entry's into the zip file.
+ * It is set up to wither write the data as is, or in the event that compression
+ * has been requested to pass it through a stream converter.
+ * Currently only the deflate compression method is supported.
+ * The CRC checksum for the entry's data is also generated here.
+ */
+NS_IMPL_THREADSAFE_ISUPPORTS2(nsZipDataStream, nsIStreamListener,
+                                               nsIRequestObserver)
+
+nsresult nsZipDataStream::Init(nsZipWriter *aWriter,
+                               nsIOutputStream *aStream,
+                               nsZipHeader *aHeader,
+                               PRInt32 aCompression)
+{
+    mWriter = aWriter;
+    mHeader = aHeader;
+    mStream = aStream;
+    mHeader->mCRC = crc32(0L, Z_NULL, 0);
+
+    nsresult rv = NS_NewSimpleStreamListener(getter_AddRefs(mOutput), aStream,
+                                             nsnull);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (aCompression > 0) {
+        mHeader->mMethod = ZIP_METHOD_DEFLATE;
+        nsCOMPtr<nsIStreamConverter> converter =
+                              new nsDeflateConverter(aCompression);
+        NS_ENSURE_TRUE(converter, NS_ERROR_OUT_OF_MEMORY);
+
+        rv = converter->AsyncConvertData("uncompressed", "deflate", mOutput,
+                                         nsnull);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        mOutput = do_QueryInterface(converter, &rv);
+        NS_ENSURE_SUCCESS(rv, rv);
+    }
+    else {
+        mHeader->mMethod = ZIP_METHOD_STORE;
+    }
+
+    return NS_OK;
+}
+
+/* void onDataAvailable (in nsIRequest aRequest, in nsISupports aContext,
+ *                       in nsIInputStream aInputStream,
+ *                       in unsigned long aOffset, in unsigned long aCount); */
+NS_IMETHODIMP nsZipDataStream::OnDataAvailable(nsIRequest *aRequest,
+                                               nsISupports *aContext,
+                                               nsIInputStream *aInputStream,
+                                               PRUint32 aOffset,
+                                               PRUint32 aCount)
+{
+    if (!mOutput)
+        return NS_ERROR_NOT_INITIALIZED;
+
+    nsAutoArrayPtr<char> buffer(new char[aCount]);
+    NS_ENSURE_TRUE(buffer, NS_ERROR_OUT_OF_MEMORY);
+
+    nsresult rv = ZW_ReadData(aInputStream, buffer.get(), aCount);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return ProcessData(aRequest, aContext, buffer.get(), aOffset, aCount);
+}
+
+/* void onStartRequest (in nsIRequest aRequest, in nsISupports aContext); */
+NS_IMETHODIMP nsZipDataStream::OnStartRequest(nsIRequest *aRequest,
+                                              nsISupports *aContext)
+{
+    if (!mOutput)
+        return NS_ERROR_NOT_INITIALIZED;
+
+    return mOutput->OnStartRequest(aRequest, aContext);
+}
+
+/* void onStopRequest (in nsIRequest aRequest, in nsISupports aContext,
+ *                     in nsresult aStatusCode); */
+NS_IMETHODIMP nsZipDataStream::OnStopRequest(nsIRequest *aRequest,
+                                             nsISupports *aContext,
+                                             nsresult aStatusCode)
+{
+    if (!mOutput)
+        return NS_ERROR_NOT_INITIALIZED;
+
+    nsresult rv = mOutput->OnStopRequest(aRequest, aContext, aStatusCode);
+    mOutput = nsnull;
+    if (NS_FAILED(rv)) {
+        mWriter->EntryCompleteCallback(mHeader, rv);
+    }
+    else {
+        rv = CompleteEntry();
+        rv = mWriter->EntryCompleteCallback(mHeader, rv);
+    }
+
+    mStream = nsnull;
+    mWriter = nsnull;
+    mHeader = nsnull;
+
+    return rv;
+}
+
+inline nsresult nsZipDataStream::CompleteEntry()
+{
+    nsresult rv = mStream->Flush();
+    NS_ENSURE_SUCCESS(rv, rv);
+    nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mStream, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+    PRInt64 pos;
+    rv = seekable->Tell(&pos);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    mHeader->mCSize = pos - mHeader->mOffset - mHeader->GetFileHeaderLength();
+
+    // Go back and rewrite the file header
+    rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, mHeader->mOffset);
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = mHeader->WriteFileHeader(mStream);
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = mStream->Flush();
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, pos);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return NS_OK;
+}
+
+nsresult nsZipDataStream::ProcessData(nsIRequest *aRequest,
+                                      nsISupports *aContext, char *aBuffer,
+                                      PRUint32 aOffset, PRUint32 aCount)
+{
+    mHeader->mCRC = crc32(mHeader->mCRC,
+                          reinterpret_cast<const unsigned char*>(aBuffer),
+                          aCount);
+
+    nsresult rv;
+    nsCOMPtr<nsIStringInputStream> stream =
+             do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    stream->ShareData(aBuffer, aCount);
+    rv = mOutput->OnDataAvailable(aRequest, aContext, stream, aOffset, aCount);
+    mHeader->mUSize += aCount;
+
+    return rv;
+}
+
+nsresult nsZipDataStream::ReadStream(nsIInputStream *aStream)
+{
+    if (!mOutput)
+        return NS_ERROR_NOT_INITIALIZED;
+
+    nsresult rv = OnStartRequest(nsnull, nsnull);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsAutoArrayPtr<char> buffer(new char[4096]);
+    NS_ENSURE_TRUE(buffer, NS_ERROR_OUT_OF_MEMORY);
+
+    PRUint32 read = 0;
+    PRUint32 offset = 0;
+    do
+    {
+        rv = aStream->Read(buffer.get(), 4096, &read);
+        if (NS_FAILED(rv)) {
+            OnStopRequest(nsnull, nsnull, rv);
+            return rv;
+        }
+
+        if (read > 0) {
+            rv = ProcessData(nsnull, nsnull, buffer.get(), offset, read);
+            if (NS_FAILED(rv)) {
+                OnStopRequest(nsnull, nsnull, rv);
+                return rv;
+            }
+            offset += read;
+        }
+    } while (read > 0);
+
+    return OnStopRequest(nsnull, nsnull, NS_OK);
+}
new file mode 100644
--- /dev/null
+++ b/modules/libjar/zipwriter/src/nsZipDataStream.h
@@ -0,0 +1,76 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Zip Writer Component.
+ *
+ * The Initial Developer of the Original Code is
+ * Dave Townsend <dtownsend@oxymoronical.com>.
+ *
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Mook <mook.moz+random.code@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+#ifndef _nsZipDataStream_h_
+#define _nsZipDataStream_h_
+
+#include "nsZipWriter.h"
+#include "nsIOutputStream.h"
+#include "nsIStreamListener.h"
+#include "nsAutoPtr.h"
+
+class nsZipDataStream : public nsIStreamListener
+{
+public:
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSIREQUESTOBSERVER
+    NS_DECL_NSISTREAMLISTENER
+
+    nsZipDataStream()
+    {
+    }
+
+    nsresult Init(nsZipWriter *aWriter, nsIOutputStream *aStream,
+                  nsZipHeader *aHeader, PRInt32 aCompression);
+
+    nsresult ReadStream(nsIInputStream *aStream);
+
+private:
+
+    nsCOMPtr<nsIStreamListener> mOutput;
+    nsCOMPtr<nsIOutputStream> mStream;
+    nsRefPtr<nsZipWriter> mWriter;
+    nsRefPtr<nsZipHeader> mHeader;
+
+    nsresult CompleteEntry();
+    nsresult ProcessData(nsIRequest *aRequest, nsISupports *aContext,
+                         char *aBuffer, PRUint32 aOffset, PRUint32 aCount);
+};
+
+#endif
new file mode 100644
--- /dev/null
+++ b/modules/libjar/zipwriter/src/nsZipHeader.cpp
@@ -0,0 +1,292 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Zip Writer Component.
+ *
+ * The Initial Developer of the Original Code is
+ * Dave Townsend <dtownsend@oxymoronical.com>.
+ *
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+#include "StreamFunctions.h"
+#include "nsZipHeader.h"
+#include "nsMemory.h"
+
+#define ZIP_FILE_HEADER_SIGNATURE 0x04034b50
+#define ZIP_FILE_HEADER_SIZE 30
+#define ZIP_CDS_HEADER_SIGNATURE 0x02014b50
+#define ZIP_CDS_HEADER_SIZE 46
+
+#define FLAGS_IS_UTF8 0x800
+
+/**
+ * nsZipHeader represents an entry from a zip file.
+ */
+NS_IMPL_ISUPPORTS1(nsZipHeader, nsIZipEntry)
+
+/* readonly attribute unsigned short compression; */
+NS_IMETHODIMP nsZipHeader::GetCompression(PRUint16 *aCompression)
+{
+    NS_ASSERTION(mInited, "Not initalised");
+
+    *aCompression = mMethod;
+    return NS_OK;
+}
+
+/* readonly attribute unsigned long size; */
+NS_IMETHODIMP nsZipHeader::GetSize(PRUint32 *aSize)
+{
+    NS_ASSERTION(mInited, "Not initalised");
+
+    *aSize = mCSize;
+    return NS_OK;
+}
+
+/* readonly attribute unsigned long realSize; */
+NS_IMETHODIMP nsZipHeader::GetRealSize(PRUint32 *aRealSize)
+{
+    NS_ASSERTION(mInited, "Not initalised");
+
+    *aRealSize = mUSize;
+    return NS_OK;
+}
+
+/* readonly attribute unsigned long CRC32; */
+NS_IMETHODIMP nsZipHeader::GetCRC32(PRUint32 *aCRC32)
+{
+    NS_ASSERTION(mInited, "Not initalised");
+
+    *aCRC32 = mCRC;
+    return NS_OK;
+}
+
+/* readonly attribute boolean isDirectory; */
+NS_IMETHODIMP nsZipHeader::GetIsDirectory(PRBool *aIsDirectory)
+{
+    NS_ASSERTION(mInited, "Not initalised");
+
+    if (mName.Last() == '/')
+        *aIsDirectory = PR_TRUE;
+    else
+        *aIsDirectory = PR_FALSE;
+    return NS_OK;
+}
+
+/* readonly attribute PRTime lastModifiedTime; */
+NS_IMETHODIMP nsZipHeader::GetLastModifiedTime(PRTime *aLastModifiedTime)
+{
+    NS_ASSERTION(mInited, "Not initalised");
+
+    PRExplodedTime time;
+
+    time.tm_usec = 0;
+
+    time.tm_hour = mTime >> 11;
+    time.tm_min = (mTime >> 5) & 0x3F;
+    time.tm_sec = (mTime & 0x1F) * 2;
+
+    time.tm_year = (mDate >> 9) + 1980;
+    time.tm_month = ((mDate >> 5) & 0x0F) - 1;
+    time.tm_mday = mDate & 0x1F;
+
+    time.tm_params.tp_gmt_offset = 0;
+    time.tm_params.tp_dst_offset = 0;
+
+    PR_NormalizeTime(&time, PR_GMTParameters);
+    time.tm_params = PR_LocalTimeParameters(&time);
+
+    *aLastModifiedTime = PR_ImplodeTime(&time);
+
+    return NS_OK;
+}
+
+/* readonly attribute boolean isSynthetic; */
+NS_IMETHODIMP nsZipHeader::GetIsSynthetic(PRBool *aIsSynthetic)
+{
+    NS_ASSERTION(mInited, "Not initalised");
+
+    *aIsSynthetic = PR_FALSE;
+    return NS_OK;
+}
+
+void nsZipHeader::Init(const nsACString & aPath, PRTime aDate, PRUint32 aAttr,
+                       PRUint32 aOffset)
+{
+    NS_ASSERTION(!mInited, "Already initalised");
+
+    PRExplodedTime time;
+    PR_ExplodeTime(aDate, PR_LocalTimeParameters, &time);
+
+    mTime = time.tm_sec / 2 + (time.tm_min << 5) + (time.tm_hour << 11);
+    mDate = time.tm_mday + ((time.tm_month + 1) << 5) +
+            ((time.tm_year - 1980) << 9);
+
+    mEAttr = aAttr;
+    mOffset = aOffset;
+    mName = aPath;
+    mComment = NS_LITERAL_CSTRING("");
+    // Claim a UTF-8 path in case it needs it.
+    mFlags |= FLAGS_IS_UTF8;
+    mInited = PR_TRUE;
+}
+
+PRUint32 nsZipHeader::GetFileHeaderLength()
+{
+    return ZIP_FILE_HEADER_SIZE + mName.Length();
+}
+
+nsresult nsZipHeader::WriteFileHeader(nsIOutputStream *aStream)
+{
+    NS_ASSERTION(mInited, "Not initalised");
+
+    char buf[ZIP_FILE_HEADER_SIZE];
+    PRUint32 pos = 0;
+    WRITE32(buf, &pos, ZIP_FILE_HEADER_SIGNATURE);
+    WRITE16(buf, &pos, mVersionNeeded);
+    WRITE16(buf, &pos, mFlags);
+    WRITE16(buf, &pos, mMethod);
+    WRITE16(buf, &pos, mTime);
+    WRITE16(buf, &pos, mDate);
+    WRITE32(buf, &pos, mCRC);
+    WRITE32(buf, &pos, mCSize);
+    WRITE32(buf, &pos, mUSize);
+    WRITE16(buf, &pos, mName.Length());
+    WRITE16(buf, &pos, 0);
+
+    nsresult rv = ZW_WriteData(aStream, buf, pos);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return ZW_WriteData(aStream, mName.get(), mName.Length());
+}
+
+PRUint32 nsZipHeader::GetCDSHeaderLength()
+{
+    return ZIP_CDS_HEADER_SIZE + mName.Length() + mComment.Length() +
+           mFieldLength;
+}
+
+nsresult nsZipHeader::WriteCDSHeader(nsIOutputStream *aStream)
+{
+    NS_ASSERTION(mInited, "Not initalised");
+
+    char buf[ZIP_CDS_HEADER_SIZE];
+    PRUint32 pos = 0;
+    WRITE32(buf, &pos, ZIP_CDS_HEADER_SIGNATURE);
+    WRITE16(buf, &pos, mVersionMade);
+    WRITE16(buf, &pos, mVersionNeeded);
+    WRITE16(buf, &pos, mFlags);
+    WRITE16(buf, &pos, mMethod);
+    WRITE16(buf, &pos, mTime);
+    WRITE16(buf, &pos, mDate);
+    WRITE32(buf, &pos, mCRC);
+    WRITE32(buf, &pos, mCSize);
+    WRITE32(buf, &pos, mUSize);
+    WRITE16(buf, &pos, mName.Length());
+    WRITE16(buf, &pos, mFieldLength);
+    WRITE16(buf, &pos, mComment.Length());
+    WRITE16(buf, &pos, mDisk);
+    WRITE16(buf, &pos, mIAttr);
+    WRITE32(buf, &pos, mEAttr);
+    WRITE32(buf, &pos, mOffset);
+
+    nsresult rv = ZW_WriteData(aStream, buf, pos);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = ZW_WriteData(aStream, mName.get(), mName.Length());
+    NS_ENSURE_SUCCESS(rv, rv);
+    if (mExtraField) {
+        rv = ZW_WriteData(aStream, mExtraField, sizeof(mExtraField));
+        NS_ENSURE_SUCCESS(rv, rv);
+    }
+    return ZW_WriteData(aStream, mComment.get(), mComment.Length());
+}
+
+nsresult nsZipHeader::ReadCDSHeader(nsIInputStream *stream)
+{
+    NS_ASSERTION(!mInited, "Already initalised");
+
+    char buf[ZIP_CDS_HEADER_SIZE];
+
+    nsresult rv = ZW_ReadData(stream, buf, ZIP_CDS_HEADER_SIZE);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    PRUint32 pos = 0;
+    PRUint32 signature = READ32(buf, &pos);
+    if (signature != ZIP_CDS_HEADER_SIGNATURE)
+        return NS_ERROR_FILE_CORRUPTED;
+
+    mVersionMade = READ16(buf, &pos);
+    mVersionNeeded = READ16(buf, &pos);
+    mFlags = READ16(buf, &pos);
+    mMethod = READ16(buf, &pos);
+    mTime = READ16(buf, &pos);
+    mDate = READ16(buf, &pos);
+    mCRC = READ32(buf, &pos);
+    mCSize = READ32(buf, &pos);
+    mUSize = READ32(buf, &pos);
+    PRUint16 namelength = READ16(buf, &pos);
+    PRUint16 fieldlength = READ16(buf, &pos);
+    PRUint16 commentlength = READ16(buf, &pos);
+    mDisk = READ16(buf, &pos);
+    mIAttr = READ16(buf, &pos);
+    mEAttr = READ32(buf, &pos);
+    mOffset = READ32(buf, &pos);
+
+    if (namelength > 0) {
+        nsAutoArrayPtr<char> field(new char[namelength]);
+        NS_ENSURE_TRUE(field, NS_ERROR_OUT_OF_MEMORY);
+        rv = ZW_ReadData(stream, field.get(), namelength);
+        NS_ENSURE_SUCCESS(rv, rv);
+        mName.Assign(field, namelength);
+    }
+    else
+        mName = NS_LITERAL_CSTRING("");
+
+    if (fieldlength > 0) {
+        mExtraField = new char[fieldlength];
+        NS_ENSURE_TRUE(mExtraField, NS_ERROR_OUT_OF_MEMORY);
+        rv = ZW_ReadData(stream, mExtraField.get(), fieldlength);
+        NS_ENSURE_SUCCESS(rv, rv);
+    }
+
+    if (commentlength > 0) {
+        nsAutoArrayPtr<char> field(new char[commentlength]);
+        NS_ENSURE_TRUE(field, NS_ERROR_OUT_OF_MEMORY);
+        rv = ZW_ReadData(stream, field.get(), commentlength);
+        NS_ENSURE_SUCCESS(rv, rv);
+        mComment.Assign(field, commentlength);
+    }
+    else
+        mComment = NS_LITERAL_CSTRING("");
+
+    mInited = PR_TRUE;
+    return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/modules/libjar/zipwriter/src/nsZipHeader.h
@@ -0,0 +1,110 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Zip Writer Component.
+ *
+ * The Initial Developer of the Original Code is
+ * Dave Townsend <dtownsend@oxymoronical.com>.
+ *
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+#ifndef _nsZipHeader_h_
+#define _nsZipHeader_h_
+
+#include "nsString.h"
+#include "nsIOutputStream.h"
+#include "nsIInputStream.h"
+#include "nsIZipReader.h"
+#include "nsAutoPtr.h"
+
+#define ZIP_ATTRS_FILE 0
+#define ZIP_ATTRS_DIRECTORY 16
+
+class nsZipHeader : public nsIZipEntry
+{
+public:
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSIZIPENTRY
+
+    nsZipHeader() :
+        mCRC(0),
+        mCSize(0),
+        mUSize(0),
+        mEAttr(0),
+        mOffset(0),
+        mFieldLength(0),
+        mVersionMade(20),
+        mVersionNeeded(20),
+        mFlags(0),
+        mMethod(0),
+        mTime(0),
+        mDate(0),
+        mDisk(0),
+        mIAttr(0),
+        mInited(PR_FALSE),
+        mExtraField(NULL)
+    {
+    }
+
+    ~nsZipHeader()
+    {
+        mExtraField = NULL;
+    }
+
+    PRUint32 mCRC;
+    PRUint32 mCSize;
+    PRUint32 mUSize;
+    PRUint32 mEAttr;
+    PRUint32 mOffset;
+    PRUint32 mFieldLength;
+    PRUint16 mVersionMade;
+    PRUint16 mVersionNeeded;
+    PRUint16 mFlags;
+    PRUint16 mMethod;
+    PRUint16 mTime;
+    PRUint16 mDate;
+    PRUint16 mDisk;
+    PRUint16 mIAttr;
+    PRPackedBool mInited;
+    nsCString mName;
+    nsCString mComment;
+    nsAutoArrayPtr<char> mExtraField;
+
+    void Init(const nsACString & aPath, PRTime aDate, PRUint32 aAttr,
+              PRUint32 aOffset);
+    PRUint32 GetFileHeaderLength();
+    nsresult WriteFileHeader(nsIOutputStream *aStream);
+    PRUint32 GetCDSHeaderLength();
+    nsresult WriteCDSHeader(nsIOutputStream *aStream);
+    nsresult ReadCDSHeader(nsIInputStream *aStream);
+};
+
+#endif
new file mode 100644
--- /dev/null
+++ b/modules/libjar/zipwriter/src/nsZipWriter.cpp
@@ -0,0 +1,1033 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Zip Writer Component.
+ *
+ * The Initial Developer of the Original Code is
+ * Dave Townsend <dtownsend@oxymoronical.com>.
+ *
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Mook <mook.moz+random.code@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+#include "StreamFunctions.h"
+#include "nsZipWriter.h"
+#include "nsZipDataStream.h"
+#include "nsISeekableStream.h"
+#include "nsIAsyncStreamCopier.h"
+#include "nsIStreamListener.h"
+#include "nsIInputStreamPump.h"
+#include "nsComponentManagerUtils.h"
+#include "nsMemory.h"
+#include "nsNetError.h"
+#include "nsStreamUtils.h"
+#include "nsThreadUtils.h"
+#include "nsNetUtil.h"
+#include "prio.h"
+
+#define ZIP_EOCDR_HEADER_SIZE 22
+#define ZIP_EOCDR_HEADER_SIGNATURE 0x06054b50
+
+/**
+ * nsZipWriter is used to create and add to zip files.
+ * It is based on the spec available at
+ * http://www.pkware.com/documents/casestudies/APPNOTE.TXT.
+ * 
+ * The basic structure of a zip file created is slightly simpler than that
+ * illustrated in the spec because certain features of the zip format are
+ * unsupported:
+ * 
+ * [local file header 1]
+ * [file data 1]
+ * . 
+ * .
+ * .
+ * [local file header n]
+ * [file data n]
+ * [central directory]
+ * [end of central directory record]
+ */
+NS_IMPL_ISUPPORTS2(nsZipWriter, nsIZipWriter,
+                                nsIRequestObserver)
+
+nsZipWriter::nsZipWriter()
+{
+    mEntryHash.Init();
+    mInQueue = PR_FALSE;
+}
+
+nsZipWriter::~nsZipWriter()
+{
+    if (mStream && !mInQueue)
+        Close();
+}
+
+/* attribute AString comment; */
+NS_IMETHODIMP nsZipWriter::GetComment(nsACString & aComment)
+{
+    if (!mStream)
+        return NS_ERROR_NOT_INITIALIZED;
+
+    aComment = mComment;
+    return NS_OK;
+}
+
+NS_IMETHODIMP nsZipWriter::SetComment(const nsACString & aComment)
+{
+    if (!mStream)
+        return NS_ERROR_NOT_INITIALIZED;
+
+    mComment = aComment;
+    mCDSDirty = PR_TRUE;
+    return NS_OK;
+}
+
+/* readonly attribute boolean inQueue; */
+NS_IMETHODIMP nsZipWriter::GetInQueue(PRBool *aInQueue)
+{
+    *aInQueue = mInQueue;
+    return NS_OK;
+}
+
+/* readonly attribute nsIFile file; */
+NS_IMETHODIMP nsZipWriter::GetFile(nsIFile **aFile)
+{
+    nsCOMPtr<nsIFile> file;
+    nsresult rv = mFile->Clone(getter_AddRefs(file));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    NS_ADDREF(*aFile = file);
+    return NS_OK;
+}
+
+/*
+ * Reads file entries out of an existing zip file.
+ */
+nsresult nsZipWriter::ReadFile(nsIFile *aFile)
+{
+    PRInt64 size;
+    nsresult rv = aFile->GetFileSize(&size);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsIInputStream> inputStream;
+    rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), aFile);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    char buf[1024];
+    PRInt64 seek = size - 1024;
+    PRUint32 length = 1024;
+
+    if (seek < 0) {
+        length += seek;
+        seek = 0;
+    }
+
+    PRUint32 pos;
+    PRUint32 sig = 0;
+    nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(inputStream);
+
+    while (true) {
+        rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, seek);
+        if (NS_FAILED(rv)) {
+            inputStream->Close();
+            return rv;
+        }
+        rv = ZW_ReadData(inputStream, buf, length);
+        if (NS_FAILED(rv)) {
+            inputStream->Close();
+            return rv;
+        }
+
+        /*
+         * We have to backtrack from the end of the file until we find the
+         * CDS signature
+         */
+        // We know it's at least this far from the end
+        pos = length - ZIP_EOCDR_HEADER_SIZE;
+        sig = READ32(buf, &pos);
+        pos -= 4;
+        while (pos >=0) {
+            if (sig == ZIP_EOCDR_HEADER_SIGNATURE) {
+                // Skip down to entry count
+                pos += 10;
+                PRUint32 entries = READ16(buf, &pos);
+                // Skip past CDS size
+                pos += 4;
+                mCDSOffset = READ32(buf, &pos);
+                PRUint32 commentlen = READ16(buf, &pos);
+
+                if (commentlen == 0)
+                    mComment.Truncate();
+                else if (pos + commentlen <= length)
+                    mComment.Assign(buf + pos, commentlen);
+                else {
+                    if ((seek + pos + commentlen) > size) {
+                        inputStream->Close();
+                        return NS_ERROR_FILE_CORRUPTED;
+                    }
+                    nsAutoArrayPtr<char> field(new char[commentlen]);
+                    NS_ENSURE_TRUE(field, NS_ERROR_OUT_OF_MEMORY);
+                    rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
+                                        seek + pos);
+                    if (NS_FAILED(rv)) {
+                        inputStream->Close();
+                        return rv;
+                    }
+                    rv = ZW_ReadData(inputStream, field.get(), length);
+                    if (NS_FAILED(rv)) {
+                        inputStream->Close();
+                        return rv;
+                    }
+                    mComment.Assign(field.get(), commentlen);
+                }
+
+                rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
+                                    mCDSOffset);
+                if (NS_FAILED(rv)) {
+                    inputStream->Close();
+                    return rv;
+                }
+
+                for (PRUint32 entry = 0; entry < entries; entry++) {
+                    nsZipHeader* header = new nsZipHeader();
+                    if (!header) {
+                        inputStream->Close();
+                        mEntryHash.Clear();
+                        mHeaders.Clear();
+                        return NS_ERROR_OUT_OF_MEMORY;
+                    }
+                    rv = header->ReadCDSHeader(inputStream);
+                    if (NS_FAILED(rv)) {
+                        inputStream->Close();
+                        mEntryHash.Clear();
+                        mHeaders.Clear();
+                        return rv;
+                    }
+                    if (!mEntryHash.Put(header->mName, mHeaders.Count()))
+                        return NS_ERROR_OUT_OF_MEMORY;
+                    if (!mHeaders.AppendObject(header))
+                        return NS_ERROR_OUT_OF_MEMORY;
+                }
+
+                return inputStream->Close();
+            }
+            sig = sig << 8;
+            sig += buf[--pos];
+        }
+
+        if (seek == 0) {
+            // We've reached the start with no signature found. Corrupt.
+            inputStream->Close();
+            return NS_ERROR_FILE_CORRUPTED;
+        }
+
+        // Overlap by the size of the end of cdr
+        seek -= (1024 - ZIP_EOCDR_HEADER_SIZE);
+        if (seek < 0) {
+            length += seek;
+            seek = 0;
+        }
+    }
+    // Will never reach here in reality
+    NS_NOTREACHED("Loop should never complete");
+    return NS_ERROR_UNEXPECTED;
+}
+
+/* void open (in nsIFile aFile, in PRInt32 aIoFlags); */
+NS_IMETHODIMP nsZipWriter::Open(nsIFile *aFile, PRInt32 aIoFlags)
+{
+    if (mStream)
+        return NS_ERROR_ALREADY_INITIALIZED;
+
+    NS_ENSURE_ARG_POINTER(aFile);
+
+    // Need to be able to write to the file
+    if (aIoFlags & PR_RDONLY)
+        return NS_ERROR_FAILURE;
+
+    PRBool exists;
+    nsresult rv = aFile->Exists(&exists);
+    NS_ENSURE_SUCCESS(rv, rv);
+    if (!exists && !(aIoFlags & PR_CREATE_FILE))
+        return NS_ERROR_FILE_NOT_FOUND;
+
+    if (exists && !(aIoFlags & (PR_TRUNCATE | PR_WRONLY))) {
+        rv = ReadFile(aFile);
+        NS_ENSURE_SUCCESS(rv, rv);
+        mCDSDirty = PR_FALSE;
+    }
+    else {
+        mCDSOffset = 0;
+        mCDSDirty = PR_TRUE;
+        mComment.Truncate();
+    }
+
+    // Silently drop PR_APPEND
+    aIoFlags &= 0xef;
+
+    nsCOMPtr<nsIOutputStream> stream;
+    rv = NS_NewLocalFileOutputStream(getter_AddRefs(stream), aFile, aIoFlags);
+    if (NS_FAILED(rv)) {
+        mHeaders.Clear();
+        mEntryHash.Clear();
+        return rv;
+    }
+
+    rv = NS_NewBufferedOutputStream(getter_AddRefs(mStream), stream, 0x800);
+    if (NS_FAILED(rv)) {
+        stream->Close();
+        mHeaders.Clear();
+        mEntryHash.Clear();
+        return rv;
+    }
+
+    if (mCDSOffset > 0) {
+        rv = SeekCDS();
+        NS_ENSURE_SUCCESS(rv, rv);
+    }
+
+    mFile = aFile;
+
+    return NS_OK;
+}
+
+/* nsIZipEntry getEntry (in AString aZipEntry); */
+NS_IMETHODIMP nsZipWriter::GetEntry(const nsACString & aZipEntry,
+                                    nsIZipEntry **_retval)
+{
+    PRInt32 pos;
+    if (mEntryHash.Get(aZipEntry, &pos))
+        NS_ADDREF(*_retval = mHeaders[pos]);
+    else
+        *_retval = nsnull;
+
+    return NS_OK;
+}
+
+/* boolean hasEntry (in AString aZipEntry); */
+NS_IMETHODIMP nsZipWriter::HasEntry(const nsACString & aZipEntry,
+                                    PRBool *_retval)
+{
+    *_retval = mEntryHash.Get(aZipEntry, nsnull);
+
+    return NS_OK;
+}
+
+/* void addEntryDirectory (in AUTF8String aZipEntry, in PRTime aModTime,
+ *                         in boolean aQueue); */
+NS_IMETHODIMP nsZipWriter::AddEntryDirectory(const nsACString & aZipEntry,
+                                             PRTime aModTime, PRBool aQueue)
+{
+    if (!mStream)
+        return NS_ERROR_NOT_INITIALIZED;
+
+    if (aQueue) {
+        nsZipQueueItem item;
+        item.mOperation = OPERATION_ADD;
+        item.mZipEntry = aZipEntry;
+        item.mModTime = aModTime;
+        if (!mQueue.AppendElement(item))
+            return NS_ERROR_OUT_OF_MEMORY;
+        return NS_OK;
+    }
+
+    if (mInQueue)
+        return NS_ERROR_IN_PROGRESS;
+    return InternalAddEntryDirectory(aZipEntry, aModTime);
+}
+
+/* void addEntryFile (in AUTF8String aZipEntry, in PRInt32 aCompression,
+ *                    in nsIFile aFile, in boolean aQueue); */
+NS_IMETHODIMP nsZipWriter::AddEntryFile(const nsACString & aZipEntry,
+                                        PRInt32 aCompression, nsIFile *aFile,
+                                        PRBool aQueue)
+{
+    NS_ENSURE_ARG_POINTER(aFile);
+    if (!mStream)
+        return NS_ERROR_NOT_INITIALIZED;
+
+    if (aQueue) {
+        nsZipQueueItem item;
+        item.mOperation = OPERATION_ADD;
+        item.mZipEntry = aZipEntry;
+        item.mCompression = aCompression;
+        item.mFile = aFile;
+        if (!mQueue.AppendElement(item))
+            return NS_ERROR_OUT_OF_MEMORY;
+        return NS_OK;
+    }
+
+    if (mInQueue)
+        return NS_ERROR_IN_PROGRESS;
+
+    PRBool exists;
+    nsresult rv = aFile->Exists(&exists);
+    NS_ENSURE_SUCCESS(rv, rv);
+    if (!exists)
+        return NS_ERROR_FILE_NOT_FOUND;
+
+    PRBool isdir;
+    rv = aFile->IsDirectory(&isdir);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    PRInt64 modtime;
+    rv = aFile->GetLastModifiedTime(&modtime);
+    NS_ENSURE_SUCCESS(rv, rv);
+    modtime *= PR_USEC_PER_MSEC;
+
+    if (isdir)
+        return InternalAddEntryDirectory(aZipEntry, modtime);
+
+    if (mEntryHash.Get(aZipEntry, nsnull))
+        return NS_ERROR_FILE_ALREADY_EXISTS;
+
+    nsCOMPtr<nsIInputStream> inputStream;
+    rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream),
+                                    aFile);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = AddEntryStream(aZipEntry, modtime, aCompression, inputStream,
+                        PR_FALSE);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return inputStream->Close();
+}
+
+/* void addEntryChannel (in AUTF8String aZipEntry, in PRTime aModTime,
+ *                       in PRInt32 aCompression, in nsIChannel aChannel,
+ *                       in boolean aQueue); */
+NS_IMETHODIMP nsZipWriter::AddEntryChannel(const nsACString & aZipEntry,
+                                           PRTime aModTime,
+                                           PRInt32 aCompression,
+                                           nsIChannel *aChannel,
+                                           PRBool aQueue)
+{
+    NS_ENSURE_ARG_POINTER(aChannel);
+    if (!mStream)
+        return NS_ERROR_NOT_INITIALIZED;
+
+    if (aQueue) {
+        nsZipQueueItem item;
+        item.mOperation = OPERATION_ADD;
+        item.mZipEntry = aZipEntry;
+        item.mModTime = aModTime;
+        item.mCompression = aCompression;
+        item.mChannel = aChannel;
+        if (!mQueue.AppendElement(item))
+            return NS_ERROR_OUT_OF_MEMORY;
+        return NS_OK;
+    }
+
+    if (mInQueue)
+        return NS_ERROR_IN_PROGRESS;
+    if (mEntryHash.Get(aZipEntry, nsnull))
+        return NS_ERROR_FILE_ALREADY_EXISTS;
+
+    nsCOMPtr<nsIInputStream> inputStream;
+    nsresult rv = aChannel->Open(getter_AddRefs(inputStream));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = AddEntryStream(aZipEntry, aModTime, aCompression, inputStream,
+                        PR_FALSE);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return inputStream->Close();
+}
+
+/* void addEntryStream (in AUTF8String aZipEntry, in PRTime aModTime,
+ *                      in PRInt32 aCompression, in nsIInputStream aStream,
+ *                      in boolean aQueue); */
+NS_IMETHODIMP nsZipWriter::AddEntryStream(const nsACString & aZipEntry,
+                                          PRTime aModTime,
+                                          PRInt32 aCompression,
+                                          nsIInputStream *aStream,
+                                          PRBool aQueue)
+{
+    NS_ENSURE_ARG_POINTER(aStream);
+    if (!mStream)
+        return NS_ERROR_NOT_INITIALIZED;
+
+    if (aQueue) {
+        nsZipQueueItem item;
+        item.mOperation = OPERATION_ADD;
+        item.mZipEntry = aZipEntry;
+        item.mModTime = aModTime;
+        item.mCompression = aCompression;
+        item.mStream = aStream;
+        if (!mQueue.AppendElement(item))
+            return NS_ERROR_OUT_OF_MEMORY;
+        return NS_OK;
+    }
+
+    if (mInQueue)
+        return NS_ERROR_IN_PROGRESS;
+    if (mEntryHash.Get(aZipEntry, nsnull))
+        return NS_ERROR_FILE_ALREADY_EXISTS;
+
+    nsRefPtr<nsZipHeader> header = new nsZipHeader();
+    NS_ENSURE_TRUE(header, NS_ERROR_OUT_OF_MEMORY);
+    header->Init(aZipEntry, aModTime, ZIP_ATTRS_FILE, mCDSOffset);
+    nsresult rv = header->WriteFileHeader(mStream);
+    if (NS_FAILED(rv)) {
+        SeekCDS();
+        return rv;
+    }
+
+    nsRefPtr<nsZipDataStream> stream = new nsZipDataStream();
+    if (!stream) {
+        SeekCDS();
+        return NS_ERROR_OUT_OF_MEMORY;
+    }
+    rv = stream->Init(this, mStream, header, aCompression);
+    if (NS_FAILED(rv)) {
+        SeekCDS();
+        return rv;
+    }
+
+    rv = stream->ReadStream(aStream);
+    if (NS_FAILED(rv))
+        SeekCDS();
+    return rv;
+}
+
+/* void removeEntry (in AUTF8String aZipEntry, in boolean aQueue); */
+NS_IMETHODIMP nsZipWriter::RemoveEntry(const nsACString & aZipEntry,
+                                       PRBool aQueue)
+{
+    if (!mStream)
+        return NS_ERROR_NOT_INITIALIZED;
+
+    if (aQueue) {
+        nsZipQueueItem item;
+        item.mOperation = OPERATION_REMOVE;
+        item.mZipEntry = aZipEntry;
+        if (!mQueue.AppendElement(item))
+            return NS_ERROR_OUT_OF_MEMORY;
+        return NS_OK;
+    }
+
+    if (mInQueue)
+        return NS_ERROR_IN_PROGRESS;
+
+    PRInt32 pos;
+    if (mEntryHash.Get(aZipEntry, &pos)) {
+        // Flush any remaining data before we seek.
+        nsresult rv = mStream->Flush();
+        NS_ENSURE_SUCCESS(rv, rv);
+        if (pos < mHeaders.Count() - 1) {
+            // This is not the last entry, pull back the data.
+            nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mStream);
+            rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
+                                mHeaders[pos]->mOffset);
+            NS_ENSURE_SUCCESS(rv, rv);
+
+            nsCOMPtr<nsIInputStream> inputStream;
+            rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream),
+                                            mFile);
+            NS_ENSURE_SUCCESS(rv, rv);
+            seekable = do_QueryInterface(inputStream);
+            rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
+                                mHeaders[pos + 1]->mOffset);
+            if (NS_FAILED(rv)) {
+                inputStream->Close();
+                return rv;
+            }
+
+            PRUint32 count = mCDSOffset - mHeaders[pos + 1]->mOffset;
+            PRUint32 read = 0;
+            char buf[4096];
+            while (count > 0) {
+                if (count < sizeof(buf))
+                    read = count;
+                else
+                    read = sizeof(buf);
+
+                rv = inputStream->Read(buf, read, &read);
+                if (NS_FAILED(rv)) {
+                    inputStream->Close();
+                    Cleanup();
+                    return rv;
+                }
+
+                rv = ZW_WriteData(mStream, buf, read);
+                if (NS_FAILED(rv)) {
+                    inputStream->Close();
+                    Cleanup();
+                    return rv;
+                }
+
+                count -= read;
+            }
+            inputStream->Close();
+
+            // Rewrite header offsets and update hash
+            PRUint32 shift = (mHeaders[pos + 1]->mOffset -
+                              mHeaders[pos]->mOffset);
+            mCDSOffset -= shift;
+            PRInt32 pos2 = pos + 1;
+            while (pos2 < mHeaders.Count()) {
+                if (!mEntryHash.Put(mHeaders[pos2]->mName, pos2-1)) {
+                    Cleanup();
+                    return NS_ERROR_OUT_OF_MEMORY;
+                }
+                mHeaders[pos2]->mOffset -= shift;
+                pos2++;
+            }
+        }
+        else {
+            // Remove the last entry is just a case of moving the CDS
+            mCDSOffset = mHeaders[pos]->mOffset;
+            rv = SeekCDS();
+            NS_ENSURE_SUCCESS(rv, rv);
+        }
+
+        mEntryHash.Remove(mHeaders[pos]->mName);
+        mHeaders.RemoveObjectAt(pos);
+        mCDSDirty = PR_TRUE;
+
+        return NS_OK;
+    }
+
+    return NS_ERROR_FILE_NOT_FOUND;
+}
+
+/* void processQueue (in nsIRequestObserver aObserver,
+ *                    in nsISupports aContext); */
+NS_IMETHODIMP nsZipWriter::ProcessQueue(nsIRequestObserver *aObserver,
+                                        nsISupports *aContext)
+{
+    if (!mStream)
+        return NS_ERROR_NOT_INITIALIZED;
+    if (mInQueue)
+        return NS_ERROR_IN_PROGRESS;
+
+    mProcessObserver = aObserver;
+    mProcessContext = aContext;
+    mInQueue = PR_TRUE;
+
+    if (mProcessObserver)
+        mProcessObserver->OnStartRequest(nsnull, mProcessContext);
+
+    BeginProcessingNextItem();
+
+    return NS_OK;
+}
+
+/* void close (); */
+NS_IMETHODIMP nsZipWriter::Close()
+{
+    if (!mStream)
+        return NS_ERROR_NOT_INITIALIZED;
+    if (mInQueue)
+        return NS_ERROR_IN_PROGRESS;
+
+    if (mCDSDirty) {
+        PRUint32 size = 0;
+        for (PRInt32 i = 0; i < mHeaders.Count(); i++) {
+            nsresult rv = mHeaders[i]->WriteCDSHeader(mStream);
+            if (NS_FAILED(rv)) {
+                Cleanup();
+                return rv;
+            }
+            size += mHeaders[i]->GetCDSHeaderLength();
+        }
+
+        char buf[ZIP_EOCDR_HEADER_SIZE];
+        PRUint32 pos = 0;
+        WRITE32(buf, &pos, ZIP_EOCDR_HEADER_SIGNATURE);
+        WRITE16(buf, &pos, 0);
+        WRITE16(buf, &pos, 0);
+        WRITE16(buf, &pos, mHeaders.Count());
+        WRITE16(buf, &pos, mHeaders.Count());
+        WRITE32(buf, &pos, size);
+        WRITE32(buf, &pos, mCDSOffset);
+        WRITE16(buf, &pos, mComment.Length());
+
+        nsresult rv = ZW_WriteData(mStream, buf, pos);
+        if (NS_FAILED(rv)) {
+            Cleanup();
+            return rv;
+        }
+
+        rv = ZW_WriteData(mStream, mComment.get(), mComment.Length());
+        if (NS_FAILED(rv)) {
+            Cleanup();
+            return rv;
+        }
+
+        nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mStream);
+        rv = seekable->SetEOF();
+        if (NS_FAILED(rv)) {
+            Cleanup();
+            return rv;
+        }
+    }
+
+    nsresult rv = mStream->Close();
+    mStream = nsnull;
+    mHeaders.Clear();
+    mEntryHash.Clear();
+    mQueue.Clear();
+
+    return rv;
+}
+
+// Our nsIRequestObserver monitors removal operations performed on the queue
+/* void onStartRequest (in nsIRequest aRequest, in nsISupports aContext); */
+NS_IMETHODIMP nsZipWriter::OnStartRequest(nsIRequest *aRequest,
+                                          nsISupports *aContext)
+{
+    return NS_OK;
+}
+
+/* void onStopRequest (in nsIRequest aRequest, in nsISupports aContext,
+ *                                             in nsresult aStatusCode); */
+NS_IMETHODIMP nsZipWriter::OnStopRequest(nsIRequest *aRequest,
+                                         nsISupports *aContext,
+                                         nsresult aStatusCode)
+{
+    if (NS_FAILED(aStatusCode)) {
+        FinishQueue(aStatusCode);
+        Cleanup();
+    }
+
+    nsresult rv = mStream->Flush();
+    if (NS_FAILED(rv)) {
+        FinishQueue(rv);
+        Cleanup();
+        return rv;
+    }
+    rv = SeekCDS();
+    if (NS_FAILED(rv)) {
+        FinishQueue(rv);
+        return rv;
+    }
+
+    BeginProcessingNextItem();
+
+    return NS_OK;
+}
+
+nsresult nsZipWriter::InternalAddEntryDirectory(const nsACString & aZipEntry,
+                                                PRTime aModTime)
+{
+    nsRefPtr<nsZipHeader> header = new nsZipHeader();
+    NS_ENSURE_TRUE(header, NS_ERROR_OUT_OF_MEMORY);
+
+    if (aZipEntry.Last() != '/') {
+        nsCString dirPath;
+        dirPath.Assign(aZipEntry + NS_LITERAL_CSTRING("/"));
+        header->Init(dirPath, aModTime, ZIP_ATTRS_DIRECTORY, mCDSOffset);
+    }
+    else
+        header->Init(aZipEntry, aModTime, ZIP_ATTRS_DIRECTORY, mCDSOffset);
+
+    if (mEntryHash.Get(header->mName, nsnull))
+        return NS_ERROR_FILE_ALREADY_EXISTS;
+
+    nsresult rv = header->WriteFileHeader(mStream);
+    if (NS_FAILED(rv)) {
+        Cleanup();
+        return rv;
+    }
+
+    mCDSDirty = PR_TRUE;
+    mCDSOffset += header->GetFileHeaderLength();
+    if (!mEntryHash.Put(header->mName, mHeaders.Count())) {
+        Cleanup();
+        return NS_ERROR_OUT_OF_MEMORY;
+    }
+    if (!mHeaders.AppendObject(header)) {
+        Cleanup();
+        return NS_ERROR_OUT_OF_MEMORY;
+    }
+
+    return NS_OK;
+}
+
+/*
+ * Recovering from an error while adding a new entry is simply a case of
+ * seeking back to the CDS. If we fail trying to do that though then cleanup
+ * and bail out.
+ */
+nsresult nsZipWriter::SeekCDS()
+{
+    nsresult rv;
+    nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mStream, &rv);
+    if (NS_FAILED(rv)) {
+        Cleanup();
+        return rv;
+    }
+    rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, mCDSOffset);
+    if (NS_FAILED(rv))
+        Cleanup();
+    return rv;
+}
+
+/*
+ * In a bad error condition this essentially closes down the component as best
+ * it can.
+ */
+void nsZipWriter::Cleanup()
+{
+    mHeaders.Clear();
+    mEntryHash.Clear();
+    if (mStream)
+        mStream->Close();
+    mStream = nsnull;
+    mFile = nsnull;
+}
+
+/*
+ * Called when writing a file to the zip is complete.
+ */
+nsresult nsZipWriter::EntryCompleteCallback(nsZipHeader* aHeader,
+                                            nsresult aStatus)
+{
+    if (NS_SUCCEEDED(aStatus)) {
+        if (!mEntryHash.Put(aHeader->mName, mHeaders.Count())) {
+            SeekCDS();
+            return NS_ERROR_OUT_OF_MEMORY;
+        }
+        if (!mHeaders.AppendObject(aHeader)) {
+            mEntryHash.Remove(aHeader->mName);
+            SeekCDS();
+            return NS_ERROR_OUT_OF_MEMORY;
+        }
+        mCDSDirty = PR_TRUE;
+        mCDSOffset += aHeader->mCSize + aHeader->GetFileHeaderLength();
+
+        if (mInQueue)
+            BeginProcessingNextItem();
+
+        return NS_OK;
+    }
+
+    nsresult rv = SeekCDS();
+    if (mInQueue)
+        FinishQueue(aStatus);
+    return rv;
+}
+
+inline nsresult nsZipWriter::BeginProcessingAddition(nsZipQueueItem* aItem,
+                                                     PRBool* complete)
+{
+    if (aItem->mFile) {
+        PRBool exists;
+        nsresult rv = aItem->mFile->Exists(&exists);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        if (!exists) return NS_ERROR_FILE_NOT_FOUND;
+
+        PRBool isdir;
+        rv = aItem->mFile->IsDirectory(&isdir);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        rv = aItem->mFile->GetLastModifiedTime(&aItem->mModTime);
+        NS_ENSURE_SUCCESS(rv, rv);
+        aItem->mModTime *= PR_USEC_PER_MSEC;
+
+        if (!isdir) {
+            // Set up for fall through to stream reader
+            rv = NS_NewLocalFileInputStream(getter_AddRefs(aItem->mStream),
+                                            aItem->mFile);
+            NS_ENSURE_SUCCESS(rv, rv);
+        }
+        // If a dir then this will fall through to the plain dir addition
+    }
+
+    if (aItem->mStream) {
+        nsRefPtr<nsZipHeader> header = new nsZipHeader();
+        NS_ENSURE_TRUE(header, NS_ERROR_OUT_OF_MEMORY);
+
+        header->Init(aItem->mZipEntry, aItem->mModTime, ZIP_ATTRS_FILE,
+                     mCDSOffset);
+        nsresult rv = header->WriteFileHeader(mStream);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        nsRefPtr<nsZipDataStream> stream = new nsZipDataStream();
+        rv = stream->Init(this, mStream, header, aItem->mCompression);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        nsCOMPtr<nsIInputStreamPump> pump;
+        rv = NS_NewInputStreamPump(getter_AddRefs(pump), aItem->mStream, -1,
+                                   -1, 0, 0, PR_TRUE);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        rv = pump->AsyncRead(stream, nsnull);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        return NS_OK;
+    }
+
+    if (aItem->mChannel) {
+        nsRefPtr<nsZipHeader> header = new nsZipHeader();
+        NS_ENSURE_TRUE(header, NS_ERROR_OUT_OF_MEMORY);
+
+        header->Init(aItem->mZipEntry, aItem->mModTime, ZIP_ATTRS_FILE,
+                     mCDSOffset);
+
+        nsRefPtr<nsZipDataStream> stream = new nsZipDataStream();
+        NS_ENSURE_TRUE(stream, NS_ERROR_OUT_OF_MEMORY);
+        nsresult rv = stream->Init(this, mStream, header, aItem->mCompression);
+        NS_ENSURE_SUCCESS(rv, rv);
+        rv = aItem->mChannel->AsyncOpen(stream, nsnull);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        return NS_OK;
+    }
+
+    // Must be plain directory addition
+    *complete = PR_TRUE;
+    return InternalAddEntryDirectory(aItem->mZipEntry, aItem->mModTime);
+}
+
+inline nsresult nsZipWriter::BeginProcessingRemoval(PRInt32 aPos)
+{
+    // Open the zip file for reading
+    nsCOMPtr<nsIInputStream> inputStream;
+    nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream),
+                                             mFile);
+    NS_ENSURE_SUCCESS(rv, rv);
+    nsCOMPtr<nsIInputStreamPump> pump;
+    rv = NS_NewInputStreamPump(getter_AddRefs(pump), inputStream, -1, -1, 0,
+                               0, PR_TRUE);
+    if (NS_FAILED(rv)) {
+        inputStream->Close();
+        return rv;
+    }
+    nsCOMPtr<nsIStreamListener> listener;
+    rv = NS_NewSimpleStreamListener(getter_AddRefs(listener), mStream, this);
+    if (NS_FAILED(rv)) {
+        inputStream->Close();
+        return rv;
+    }
+
+    nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mStream);
+    rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
+                        mHeaders[aPos]->mOffset);
+    if (NS_FAILED(rv)) {
+        inputStream->Close();
+        return rv;
+    }
+
+    PRUint32 shift = (mHeaders[aPos + 1]->mOffset -
+                      mHeaders[aPos]->mOffset);
+    mCDSOffset -= shift;
+    PRInt32 pos2 = aPos + 1;
+    while (pos2 < mHeaders.Count()) {
+        mEntryHash.Put(mHeaders[pos2]->mName, pos2 - 1);
+        mHeaders[pos2]->mOffset -= shift;
+        pos2++;
+    }
+
+    mEntryHash.Remove(mHeaders[aPos]->mName);
+    mHeaders.RemoveObjectAt(aPos);
+    mCDSDirty = PR_TRUE;
+
+    rv = pump->AsyncRead(listener, nsnull);
+    if (NS_FAILED(rv)) {
+        inputStream->Close();
+        Cleanup();
+        return rv;
+    }
+    return NS_OK;
+}
+
+/*
+ * Starts processing on the next item in the queue.
+ */
+void nsZipWriter::BeginProcessingNextItem()
+{
+    while (!mQueue.IsEmpty()) {
+
+        nsZipQueueItem next = mQueue[0];
+        mQueue.RemoveElementAt(0);
+
+        if (next.mOperation == OPERATION_REMOVE) {
+            PRInt32 pos = -1;
+            if (mEntryHash.Get(next.mZipEntry, &pos)) {
+                if (pos < mHeaders.Count() - 1) {
+                    nsresult rv = BeginProcessingRemoval(pos);
+                    if (NS_FAILED(rv)) FinishQueue(rv);
+                    return;
+                }
+
+                mCDSOffset = mHeaders[pos]->mOffset;
+                nsresult rv = SeekCDS();
+                if (NS_FAILED(rv)) {
+                    FinishQueue(rv);
+                    return;
+                }
+                mEntryHash.Remove(mHeaders[pos]->mName);
+                mHeaders.RemoveObjectAt(pos);
+            }
+            else {
+                FinishQueue(NS_ERROR_FILE_NOT_FOUND);
+                return;
+            }
+        }
+        else if (next.mOperation == OPERATION_ADD) {
+            if (mEntryHash.Get(next.mZipEntry, nsnull)) {
+                FinishQueue(NS_ERROR_FILE_ALREADY_EXISTS);
+                return;
+            }
+
+            PRBool complete = PR_FALSE;
+            nsresult rv = BeginProcessingAddition(&next, &complete);
+            if (NS_FAILED(rv)) {
+                SeekCDS();
+                FinishQueue(rv);
+                return;
+            }
+            if (!complete)
+                return;
+        }
+    }
+
+    FinishQueue(NS_OK);
+}
+
+/*
+ * Ends processing with the given status.
+ */
+void nsZipWriter::FinishQueue(nsresult aStatus)
+{
+    nsCOMPtr<nsIRequestObserver> observer = mProcessObserver;
+    nsCOMPtr<nsISupports> context = mProcessContext;
+    // Clean up everything first in case the observer decides to queue more
+    // things
+    mProcessObserver = nsnull;
+    mProcessContext = nsnull;
+    mInQueue = PR_FALSE;
+
+    if (observer)
+        observer->OnStopRequest(nsnull, context, aStatus);
+}
new file mode 100644
--- /dev/null
+++ b/modules/libjar/zipwriter/src/nsZipWriter.h
@@ -0,0 +1,110 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Zip Writer Component.
+ *
+ * The Initial Developer of the Original Code is
+ * Dave Townsend <dtownsend@oxymoronical.com>.
+ *
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Mook <mook.moz+random.code@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+#ifndef _nsZipWriter_h_
+#define _nsZipWriter_h_
+
+#include "nsIZipWriter.h"
+#include "nsIFileStreams.h"
+#include "nsIBufferedStreams.h"
+#include "nsIRequestObserver.h"
+#include "nsZipHeader.h"
+#include "nsCOMPtr.h"
+#include "nsCOMArray.h"
+#include "nsTArray.h"
+#include "nsDataHashtable.h"
+
+#define ZIPWRITER_CONTRACTID "@mozilla.org/zipwriter;1"
+#define ZIPWRITER_CLASSNAME "Zip Writer"
+#define ZIPWRITER_CID { 0x430d416c, 0xa722, 0x4ad1, \
+           { 0xbe, 0x98, 0xd9, 0xa4, 0x45, 0xf8, 0x5e, 0x3f } }
+
+#define OPERATION_ADD 0
+#define OPERATION_REMOVE 1
+struct nsZipQueueItem
+{
+public:
+    PRUint32 mOperation;
+    nsCString mZipEntry;
+    nsCOMPtr<nsIFile> mFile;
+    nsCOMPtr<nsIChannel> mChannel;
+    nsCOMPtr<nsIInputStream> mStream;
+    PRTime mModTime;
+    PRInt32 mCompression;
+};
+
+class nsZipWriter : public nsIZipWriter,
+                    public nsIRequestObserver
+{
+public:
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSIZIPWRITER
+    NS_DECL_NSIREQUESTOBSERVER
+
+    nsZipWriter();
+    nsresult EntryCompleteCallback(nsZipHeader *aHeader, nsresult aStatus);
+
+private:
+    ~nsZipWriter();
+
+    PRUint32 mCDSOffset;
+    PRPackedBool mCDSDirty;
+    PRPackedBool mInQueue;
+
+    nsCOMPtr<nsIFile> mFile;
+    nsCOMPtr<nsIRequestObserver> mProcessObserver;
+    nsCOMPtr<nsISupports> mProcessContext;
+    nsCOMPtr<nsIOutputStream> mStream;
+    nsCOMArray<nsZipHeader> mHeaders;
+    nsTArray<nsZipQueueItem> mQueue;
+    nsDataHashtable<nsCStringHashKey, PRInt32> mEntryHash;
+    nsCString mComment;
+
+    nsresult SeekCDS();
+    void Cleanup();
+    nsresult ReadFile(nsIFile *aFile);
+    nsresult InternalAddEntryDirectory(const nsACString & aZipEntry,
+                                       PRTime aModTime);
+    nsresult BeginProcessingAddition(nsZipQueueItem* aItem, PRBool* complete);
+    nsresult BeginProcessingRemoval(PRInt32 aPos);
+    void BeginProcessingNextItem();
+    void FinishQueue(nsresult aStatus);
+};
+
+#endif
new file mode 100644
--- /dev/null
+++ b/modules/libjar/zipwriter/test/Makefile.in
@@ -0,0 +1,51 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is Zip Writer Component.
+#
+# The Initial Developer of the Original Code is
+# Dave Townsend <dtownsend@oxymoronical.com>.
+#
+# Portions created by the Initial Developer are Copyright (C) 2007
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either of the GNU General Public License Version 2 or later (the "GPL"),
+# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH          = ../../../..
+topsrcdir      = @top_srcdir@
+srcdir         = @srcdir@
+VPATH          = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE         = test_zipwriter
+
+XPCSHELL_TESTS = \
+  unit \
+  $(NULL)
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..c648f7299ddb65485444c8e5da7ab61331f2358d
GIT binary patch
literal 3402
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
new file mode 100644
--- /dev/null
+++ b/modules/libjar/zipwriter/test/unit/data/test.txt
@@ -0,0 +1,5 @@
+This is a test text file for the zipwriter component.
+It will be made available in the unit test directory.
+It will also be compressed into a testcase zip file
+made by a 3rd party zip tool to test the opening of
+existing zip files.
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..96581fe8b598b432acd893886b97802fad1839eb
GIT binary patch
literal 3824
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
Y^nbnXFM|G$xt$<DfY_7Sl>N`{KRQ1H*#H0l
new file mode 100644
--- /dev/null
+++ b/modules/libjar/zipwriter/test/unit/head_zipwriter.js
@@ -0,0 +1,79 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Zip Writer Component.
+ *
+ * The Initial Developer of the Original Code is
+ * Dave Townsend <dtownsend@oxymoronical.com>.
+ *
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+const NS_OS_TEMP_DIR = "TmpD";
+const Ci = Components.interfaces;
+const Cc = Components.classes;
+const NS_ERROR_IN_PROGRESS = 2152398863;
+
+const PR_RDONLY      = 0x01
+const PR_WRONLY      = 0x02
+const PR_RDWR        = 0x04
+const PR_CREATE_FILE = 0x08
+const PR_APPEND      = 0x10
+const PR_TRUNCATE    = 0x20
+const PR_SYNC        = 0x40
+const PR_EXCL        = 0x80
+
+const ZIP_EOCDR_HEADER_SIZE = 22;
+const ZIP_FILE_HEADER_SIZE = 30;
+const ZIP_CDS_HEADER_SIZE = 46;
+const ZIP_METHOD_STORE = 0
+const ZIP_METHOD_DEFLATE = 8
+
+const PR_USEC_PER_MSEC = 1000;
+
+// ZIP times are stored at a 2 second resolution.
+const TIME_RESOLUTION = 2000;
+
+const DATA_DIR = "modules/libjar/zipwriter/test/unit/data/";
+
+var ZipWriter = Components.Constructor("@mozilla.org/zipwriter;1",
+                                       "nsIZipWriter");
+var ZipReader = Components.Constructor("@mozilla.org/libjar/zip-reader;1",
+                                       "nsIZipReader", "open");
+
+var dirSvc = Cc["@mozilla.org/file/directory_service;1"]
+              .getService(Ci.nsIProperties);
+var tmpDir = dirSvc.get(NS_OS_TEMP_DIR, Ci.nsIFile);
+var tmpFile = tmpDir.clone();
+tmpFile.append("zipwriter-test.zip");
+if (tmpFile.exists())
+  tmpFile.remove(true);
+
+var zipW = new ZipWriter();
new file mode 100644
--- /dev/null
+++ b/modules/libjar/zipwriter/test/unit/tail_zipwriter.js
@@ -0,0 +1,47 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Zip Writer Component.
+ *
+ * The Initial Developer of the Original Code is
+ * Dave Townsend <dtownsend@oxymoronical.com>.
+ *
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+try {
+  zipW.close();
+}
+catch (e) {
+  // Just ignore a failure here and attempt to delete the file anyway.
+}
+
+if (tmpFile.exists())
+  tmpFile.remove(true);
new file mode 100644
--- /dev/null
+++ b/modules/libjar/zipwriter/test/unit/test_asyncadd.js
@@ -0,0 +1,108 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Zip Writer Component.
+ *
+ * The Initial Developer of the Original Code is
+ * Dave Townsend <dtownsend@oxymoronical.com>.
+ *
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+// Values taken from using zipinfo to list the test.zip contents
+var TESTS = [
+  {
+    name: "test.txt",
+    size: 232,
+    crc: 0x0373ac26
+  },
+  {
+    name: "test.png",
+    size: 3402,
+    crc: 0x504a5c30
+  }
+];
+
+var size = 0;
+
+var observer = {
+  onStartRequest: function(request, context)
+  {
+  },
+
+  onStopRequest: function(request, context, status)
+  {
+    do_check_eq(status, Components.results.NS_OK);
+
+    zipW.close();
+    size += ZIP_EOCDR_HEADER_SIZE;
+
+    do_check_eq(size, tmpFile.fileSize);
+
+    // Test the stored data with the zipreader
+    var zipR = new ZipReader(tmpFile);
+
+    for (var i = 0; i < TESTS.length; i++) {
+      do_check_true(zipR.hasEntry(TESTS[i].name));
+
+      var source = do_get_file(DATA_DIR + TESTS[i].name);
+      var entry = zipR.getEntry(TESTS[i].name);
+      do_check_eq(entry.realSize, TESTS[i].size);
+      do_check_eq(entry.size, TESTS[i].size);
+      do_check_eq(entry.CRC32, TESTS[i].crc);
+
+      var diff = Math.abs((entry.lastModifiedTime/PR_USEC_PER_MSEC) -
+                          source.lastModifiedTime);
+      if (diff > TIME_RESOLUTION)
+        do_throw(diff);
+
+      zipR.test(TESTS[i].name);
+    }
+
+    zipR.close();
+    do_test_finished();
+  }
+};
+
+function run_test()
+{
+  zipW.open(tmpFile, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE);
+
+  for (var i = 0; i < TESTS.length; i++) {
+    var source = do_get_file(DATA_DIR+TESTS[i].name);
+    zipW.addEntryFile(TESTS[i].name, Ci.nsIZipWriter.COMPRESSION_NONE, source,
+                      true);
+    size += ZIP_FILE_HEADER_SIZE + ZIP_CDS_HEADER_SIZE +
+            (TESTS[i].name.length*2) + TESTS[i].size;
+  }
+  do_test_pending();
+  zipW.processQueue(observer, null);
+  do_check_true(zipW.inQueue);
+}
new file mode 100644
--- /dev/null
+++ b/modules/libjar/zipwriter/test/unit/test_asyncbadadd.js
@@ -0,0 +1,68 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Zip Writer Component.
+ *
+ * The Initial Developer of the Original Code is
+ * Dave Townsend <dtownsend@oxymoronical.com>.
+ *
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+const FILENAME = "missing.txt";
+
+var observer = {
+  onStartRequest: function(request, context)
+  {
+  },
+
+  onStopRequest: function(request, context, status)
+  {
+    do_check_eq(status, Components.results.NS_ERROR_FILE_NOT_FOUND);
+    zipW.close();
+    do_check_eq(ZIP_EOCDR_HEADER_SIZE, tmpFile.fileSize);
+    do_test_finished();
+  }
+};
+
+function run_test()
+{
+  zipW.open(tmpFile, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE);
+
+  var source = tmpDir.clone();
+  source.append(FILENAME);
+  zipW.addEntryFile(FILENAME, Ci.nsIZipWriter.COMPRESSION_NONE, source, true);
+
+  do_test_pending();
+  zipW.processQueue(observer, null);
+
+  // With nothing to actually do the queue would have completed immediately
+  do_check_false(zipW.inQueue);
+}
new file mode 100644
--- /dev/null
+++ b/modules/libjar/zipwriter/test/unit/test_asyncbadremove.js
@@ -0,0 +1,64 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Zip Writer Component.
+ *
+ * The Initial Developer of the Original Code is
+ * Dave Townsend <dtownsend@oxymoronical.com>.
+ *
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+const FILENAME = "missing.txt";
+
+var observer = {
+  onStartRequest: function(request, context)
+  {
+  },
+
+  onStopRequest: function(request, context, status)
+  {
+    do_check_eq(status, Components.results.NS_ERROR_FILE_NOT_FOUND);
+    zipW.close();
+    do_check_eq(ZIP_EOCDR_HEADER_SIZE, tmpFile.fileSize);
+    do_test_finished();
+  }
+};
+
+function run_test()
+{
+  zipW.open(tmpFile, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE);
+  zipW.removeEntry(FILENAME, true);
+  do_test_pending();
+  zipW.processQueue(observer, null);
+
+  // With nothing to actually do the queue would have completed immediately
+  do_check_false(zipW.inQueue);
+}
new file mode 100644
--- /dev/null
+++ b/modules/libjar/zipwriter/test/unit/test_asyncremove.js
@@ -0,0 +1,79 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Zip Writer Component.
+ *
+ * The Initial Developer of the Original Code is
+ * Dave Townsend <dtownsend@oxymoronical.com>.
+ *
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+var TESTS = [
+  "test.txt",
+  "test.png"
+];
+
+var observer = {
+  onStartRequest: function(request, context)
+  {
+  },
+
+  onStopRequest: function(request, context, status)
+  {
+    do_check_eq(status, Components.results.NS_OK);
+
+    zipW.close();
+
+    // Empty zip file should just be the end of central directory marker
+    do_check_eq(tmpFile.fileSize, ZIP_EOCDR_HEADER_SIZE);
+    do_test_finished();
+  }
+};
+
+function run_test()
+{
+  // Copy our test zip to the tmp dir so we can modify it
+  var testzip = do_get_file(DATA_DIR + "test.zip");
+  testzip.copyTo(tmpDir, tmpFile.leafName);
+
+  do_check_true(tmpFile.exists());
+
+  zipW.open(tmpFile, PR_RDWR);
+
+  for (var i = 0; i < TESTS.length; i++) {
+    do_check_true(zipW.hasEntry(TESTS[i]));
+    zipW.removeEntry(TESTS[i], true);
+  }
+
+  do_test_pending();
+  zipW.processQueue(observer, null);
+  do_check_true(zipW.inQueue);
+}
new file mode 100644
--- /dev/null
+++ b/modules/libjar/zipwriter/test/unit/test_createempty.js
@@ -0,0 +1,49 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Zip Writer Component.
+ *
+ * The Initial Developer of the Original Code is
+ * Dave Townsend <dtownsend@oxymoronical.com>.
+ *
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+function run_test()
+{
+  zipW.open(tmpFile, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE);
+  zipW.close();
+
+  // Should have created a zip file
+  do_check_true(tmpFile.exists());
+
+  // Empty zip file should just be the end of central directory marker
+  do_check_eq(tmpFile.fileSize, ZIP_EOCDR_HEADER_SIZE);
+}
new file mode 100644
--- /dev/null
+++ b/modules/libjar/zipwriter/test/unit/test_deflatedata.js
@@ -0,0 +1,87 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Zip Writer Component.
+ *
+ * The Initial Developer of the Original Code is
+ * Dave Townsend <dtownsend@oxymoronical.com>.
+ *
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+const DATA = "ZIP WRITER TEST DATA";
+const FILENAME = "test.txt";
+const CRC = 0xe6164331;
+const time = Date.now();
+
+function run_test()
+{
+  zipW.open(tmpFile, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE);
+
+  // Shouldn't be there to start with.
+  do_check_false(zipW.hasEntry(FILENAME));
+
+  do_check_false(zipW.inQueue);
+
+  var stream = Cc["@mozilla.org/io/string-input-stream;1"]
+                .createInstance(Ci.nsIStringInputStream);
+  stream.setData(DATA, DATA.length);
+  zipW.addEntryStream(FILENAME, time * PR_USEC_PER_MSEC,
+                      Ci.nsIZipWriter.COMPRESSION_BEST, stream, false);
+
+  var entry = zipW.getEntry(FILENAME);
+
+  do_check_true(entry != null);
+
+  // Check entry seems right.
+  do_check_eq(entry.compression, ZIP_METHOD_DEFLATE);
+  do_check_eq(entry.CRC32, CRC);
+  do_check_eq(entry.realSize, DATA.length);
+  var diff = Math.abs((entry.lastModifiedTime/PR_USEC_PER_MSEC) - time);
+  if (diff > TIME_RESOLUTION)
+    do_throw(diff);
+
+  zipW.close();
+
+  // Test the stored data with the zipreader
+  var zipR = new ZipReader(tmpFile);
+  do_check_true(zipR.hasEntry(FILENAME));
+
+  zipR.test(FILENAME);
+
+  var stream = Cc["@mozilla.org/scriptableinputstream;1"]
+                .createInstance(Ci.nsIScriptableInputStream);
+  stream.init(zipR.getInputStream(FILENAME));
+  var result = stream.read(DATA.length);
+  stream.close();
+  zipR.close();
+
+  do_check_eq(result, DATA);
+}
new file mode 100644
--- /dev/null
+++ b/modules/libjar/zipwriter/test/unit/test_directory.js
@@ -0,0 +1,60 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Zip Writer Component.
+ *
+ * The Initial Developer of the Original Code is
+ * Dave Townsend <dtownsend@oxymoronical.com>.
+ *
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+const DIRNAME1 = "test";
+const DIRNAME1_CORRECT = "test/";
+const DIRNAME2 = "test2/";
+const time = Date.now();
+
+function run_test()
+{
+  zipW.open(tmpFile, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE);
+
+  zipW.addEntryDirectory(DIRNAME1, time * PR_USEC_PER_MSEC, false);
+  do_check_false(zipW.hasEntry(DIRNAME1));
+  do_check_true(zipW.hasEntry(DIRNAME1_CORRECT));
+  var entry = zipW.getEntry(DIRNAME1_CORRECT);
+  do_check_true(entry.isDirectory);
+
+  zipW.addEntryDirectory(DIRNAME2, time * PR_USEC_PER_MSEC, false);
+  do_check_true(zipW.hasEntry(DIRNAME2));
+  entry = zipW.getEntry(DIRNAME2);
+  do_check_true(entry.isDirectory);
+
+  zipW.close();
+}
new file mode 100644
--- /dev/null
+++ b/modules/libjar/zipwriter/test/unit/test_editexisting.js
@@ -0,0 +1,98 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Zip Writer Component.
+ *
+ * The Initial Developer of the Original Code is
+ * Dave Townsend <dtownsend@oxymoronical.com>.
+ *
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+// Values taken from using zipinfo to list the test.zip contents
+var TESTS = [
+  {
+    name: "test.txt",
+    size: 232,
+    crc: 0x0373ac26,
+    time: new Date(2007, 4, 1, 21, 44, 56)
+  },
+  {
+    name: "test.png",
+    size: 3402,
+    crc: 0x504a5c30,
+    time: new Date(2007, 4, 1, 21, 49, 40)
+  }
+];
+var BADENTRY = "unknown.txt";
+
+function run_test()
+{
+  // Copy our test zip to the tmp dir so we can modify it
+  var testzip = do_get_file(DATA_DIR + "test.zip");
+  testzip.copyTo(tmpDir, tmpFile.leafName);
+
+  do_check_true(tmpFile.exists());
+
+  zipW.open(tmpFile, PR_RDWR);
+
+  for (var i = 0; i < TESTS.length; i++) {
+    do_check_true(zipW.hasEntry(TESTS[i].name));
+    var entry = zipW.getEntry(TESTS[i].name);
+    do_check_true(entry != null);
+
+    do_check_eq(entry.realSize, TESTS[i].size);
+    do_check_eq(entry.CRC32, TESTS[i].crc);
+    var diff = Math.abs(TESTS[i].time -
+               (entry.lastModifiedTime / PR_USEC_PER_MSEC));
+    if (diff > TIME_RESOLUTION)
+      do_throw(diff);
+  }
+
+  try {
+    zipW.removeEntry(BADENTRY, false);
+    do_throw("shouldn't be able to remove an entry that doesn't exist");
+  }
+  catch (e) {
+    do_check_eq(e.result, Components.results.NS_ERROR_FILE_NOT_FOUND);
+  }
+
+  for (var i = 0; i < TESTS.length; i++) {
+    zipW.removeEntry(TESTS[i].name, false);
+  }
+
+  zipW.close();
+
+  // Certain platforms cache the file size so get a fresh file to check.
+  tmpFile = tmpFile.clone();
+
+  // Empty zip file should just be the end of central directory marker
+  do_check_eq(tmpFile.fileSize, ZIP_EOCDR_HEADER_SIZE);
+}
new file mode 100644
--- /dev/null
+++ b/modules/libjar/zipwriter/test/unit/test_storedata.js
@@ -0,0 +1,111 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Zip Writer Component.
+ *
+ * The Initial Developer of the Original Code is
+ * Dave Townsend <dtownsend@oxymoronical.com>.
+ *
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+const DATA = "ZIP WRITER TEST DATA";
+const FILENAME = "test.txt";
+const FILENAME2 = "test2.txt";
+const CRC = 0xe6164331;
+const time = Date.now();
+
+function testpass(source)
+{
+  // Should exist.
+  do_check_true(source.hasEntry(FILENAME));
+
+  var entry = source.getEntry(FILENAME);
+  do_check_neq(entry, null);
+
+  do_check_false(entry.isDirectory);
+
+  // Should be stored
+  do_check_eq(entry.compression, ZIP_METHOD_STORE);
+
+  var diff = Math.abs((entry.lastModifiedTime / PR_USEC_PER_MSEC) - time);
+  if (diff > TIME_RESOLUTION)
+    do_throw(diff);
+
+  // File size should match our data size.
+  do_check_eq(entry.realSize, DATA.length);
+  // When stored sizes should match.
+  do_check_eq(entry.size, entry.realSize);
+
+  // Check that the CRC is accurate
+  do_check_eq(entry.CRC32, CRC);
+}
+
+function run_test()
+{
+  zipW.open(tmpFile, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE);
+
+  // Shouldn't be there to start with.
+  do_check_false(zipW.hasEntry(FILENAME));
+
+  do_check_false(zipW.inQueue);
+
+  var stream = Cc["@mozilla.org/io/string-input-stream;1"]
+                .createInstance(Ci.nsIStringInputStream);
+  stream.setData(DATA, DATA.length);
+  zipW.addEntryStream(FILENAME, time * PR_USEC_PER_MSEC,
+                      Ci.nsIZipWriter.COMPRESSION_NONE, stream, false);
+
+  // Check that zip state is right at this stage.
+  testpass(zipW);
+  zipW.close();
+
+  do_check_eq(tmpFile.fileSize,
+              DATA.length + ZIP_FILE_HEADER_SIZE + ZIP_CDS_HEADER_SIZE +
+              (FILENAME.length * 2) + ZIP_EOCDR_HEADER_SIZE);
+
+  // Check to see if we get the same results loading afresh.
+  zipW.open(tmpFile, PR_RDWR);
+  testpass(zipW);
+  zipW.close();
+
+  // Test the stored data with the zipreader
+  var zipR = new ZipReader(tmpFile);
+  testpass(zipR);
+  zipR.test(FILENAME);
+  var stream = Cc["@mozilla.org/scriptableinputstream;1"]
+                .createInstance(Ci.nsIScriptableInputStream);
+  stream.init(zipR.getInputStream(FILENAME));
+  var result = stream.read(DATA.length);
+  stream.close();
+  zipR.close();
+
+  do_check_eq(result, DATA);
+}
new file mode 100644
--- /dev/null
+++ b/modules/libjar/zipwriter/test/unit/test_sync.js
@@ -0,0 +1,92 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Zip Writer Component.
+ *
+ * The Initial Developer of the Original Code is
+ * Dave Townsend <dtownsend@oxymoronical.com>.
+ *
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+// Values taken from using zipinfo to list the test.zip contents
+var TESTS = [
+  {
+    name: "test.txt",
+    size: 232,
+    crc: 0x0373ac26
+  },
+  {
+    name: "test.png",
+    size: 3402,
+    crc: 0x504a5c30
+  }
+];
+
+function run_test()
+{
+  zipW.open(tmpFile, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE);
+
+  var size = 0;
+  for (var i = 0; i < TESTS.length; i++) {
+    var source = do_get_file(DATA_DIR + TESTS[i].name);
+    zipW.addEntryFile(TESTS[i].name, Ci.nsIZipWriter.COMPRESSION_NONE, source,
+                      false);
+    size += ZIP_FILE_HEADER_SIZE + ZIP_CDS_HEADER_SIZE +
+            (TESTS[i].name.length*2) + TESTS[i].size;
+  }
+
+  zipW.close();
+  size += ZIP_EOCDR_HEADER_SIZE;
+
+  do_check_eq(size, tmpFile.fileSize);
+
+  // Test the stored data with the zipreader
+  var zipR = new ZipReader(tmpFile);
+
+  for (var i = 0; i < TESTS.length; i++) {
+    var source = do_get_file(DATA_DIR + TESTS[i].name);
+    do_check_true(zipR.hasEntry(TESTS[i].name));
+
+    var entry = zipR.getEntry(TESTS[i].name);
+    do_check_eq(entry.realSize, TESTS[i].size);
+    do_check_eq(entry.size, TESTS[i].size);
+    do_check_eq(entry.CRC32, TESTS[i].crc);
+
+    var diff = Math.abs((entry.lastModifiedTime/PR_USEC_PER_MSEC) -
+                        source.lastModifiedTime);
+    if (diff > TIME_RESOLUTION)
+      do_throw(diff);
+
+    zipR.test(TESTS[i].name);
+  }
+
+  zipR.close();
+}
new file mode 100644
--- /dev/null
+++ b/modules/libjar/zipwriter/test/unit/test_undochange.js
@@ -0,0 +1,74 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Zip Writer Component.
+ *
+ * The Initial Developer of the Original Code is
+ * Dave Townsend <dtownsend@oxymoronical.com>.
+ *
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+// Values taken from using zipinfo to list the test.zip contents
+var TESTS = [
+  "test.txt",
+  "test.png"
+];
+
+function run_test()
+{
+  zipW.open(tmpFile, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE);
+
+  for (var i = 0; i < TESTS.length; i++) {
+    var source = do_get_file(DATA_DIR + TESTS[i]);
+    zipW.addEntryFile(TESTS[i], Ci.nsIZipWriter.COMPRESSION_NONE, source,
+                      false);
+  }
+
+  try {
+    var source = do_get_file(DATA_DIR + TESTS[0]);
+    zipW.addEntryFile(TESTS[0], Ci.nsIZipWriter.COMPRESSION_NONE, source,
+                      false);
+    do_throw("Should not be able to add the same file twice");
+  }
+  catch (e) {
+    do_check_eq(e.result, Components.results.NS_ERROR_FILE_ALREADY_EXISTS);
+  }
+
+  // Remove all the tests and see if we are left with an empty zip
+  for (var i = 0; i < TESTS.length; i++) {
+    zipW.removeEntry(TESTS[i], false);
+  }
+
+  zipW.close();
+
+  // Empty zip file should just be the end of central directory marker
+  do_check_eq(tmpFile.fileSize, ZIP_EOCDR_HEADER_SIZE);
+}
new file mode 100644
--- /dev/null
+++ b/modules/libjar/zipwriter/test/unit/test_zipcomment.js
@@ -0,0 +1,67 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Zip Writer Component.
+ *
+ * The Initial Developer of the Original Code is
+ * Dave Townsend <dtownsend@oxymoronical.com>.
+ *
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+const DATA = "ZIP WRITER TEST COMMENT";
+const DATA2 = "ANOTHER ONE";
+
+function run_test()
+{
+  zipW.open(tmpFile, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE);
+  zipW.comment = DATA;
+  zipW.close();
+
+  // Should have created a zip file
+  do_check_true(tmpFile.exists());
+
+  // Empty zip file should just be the end of central directory marker
+  // and comment
+  do_check_eq(tmpFile.fileSize, ZIP_EOCDR_HEADER_SIZE + DATA.length);
+
+  zipW.open(tmpFile, PR_RDWR);
+  // Should have the set comment
+  do_check_eq(zipW.comment, DATA);
+  zipW.comment = DATA2;
+  zipW.close();
+
+  // Certain platforms cache the file size so get a fresh file to check.
+  tmpFile = tmpFile.clone();
+
+  // Empty zip file should just be the end of central directory marker
+  // and comment. This should now be shorter
+  do_check_eq(tmpFile.fileSize, ZIP_EOCDR_HEADER_SIZE + DATA2.length);
+}
--- a/toolkit/library/libxul-config.mk
+++ b/toolkit/library/libxul-config.mk
@@ -334,16 +334,21 @@ ifdef MOZ_ENABLE_XREMOTE
 COMPONENT_LIBS += remoteservice
 endif
 
 ifdef MOZ_SPELLCHECK
 DEFINES += -DMOZ_SPELLCHECK
 COMPONENT_LIBS += spellchecker
 endif
 
+ifdef MOZ_ZIPWRITER
+DEFINES += -DMOZ_ZIPWRITER
+COMPONENT_LIBS += zipwriter
+endif
+
 ifneq (,$(filter layout-debug,$(MOZ_EXTENSIONS)))
 COMPONENT_LIBS += gkdebug
 endif
 
 ifdef GC_LEAK_DETECTOR
 EXTRA_DSO_LIBS += boehm
 endif
 
--- a/toolkit/library/nsStaticXULComponents.cpp
+++ b/toolkit/library/nsStaticXULComponents.cpp
@@ -217,16 +217,22 @@
 #endif
 
 #ifdef MOZ_STORAGE
 #define STORAGE_MODULE MODULE(mozStorageModule)
 #else
 #define STORAGE_MODULE
 #endif
 
+#ifdef MOZ_ZIPWRITER
+#define ZIPWRITER_MODULE MODULE(ZipWriterModule)
+#else
+#define ZIPWRITER_MODULE
+#endif
+
 #ifdef MOZ_PLACES
 #define PLACES_MODULES \
     MODULE(nsPlacesModule)
 #else
 #if (defined(MOZ_MORK) && defined(MOZ_XUL))
 #define PLACES_MODULES \
     MODULE(nsMorkModule)                     \
     MODULE(nsToolkitHistory)
@@ -263,16 +269,17 @@
     MODULE(nsI18nModule)                     \
     MODULE(nsChardetModule)                  \
     UNIVERSALCHARDET_MODULE                  \
     MODULE(necko)                            \
     PERMISSIONS_MODULES                      \
     AUTH_MODULE                              \
     IPC_MODULE                               \
     MODULE(nsJarModule)                      \
+    ZIPWRITER_MODULE                         \
     MODULE(nsPrefModule)                     \
     MODULE(nsSecurityManagerModule)          \
     RDF_MODULE                               \
     RDFAPP_MODULES                           \
     MODULE(nsParserModule)                   \
     GFX_MODULES                              \
     WIDGET_MODULES                           \
     MODULE(nsImageLib2Module)                \
--- a/toolkit/toolkit-makefiles.sh
+++ b/toolkit/toolkit-makefiles.sh
@@ -835,16 +835,25 @@ add_makefiles "
 if [ "$MOZ_COMPOSER" ]; then
   add_makefiles "
     editor/composer/Makefile
     editor/ui/Makefile
     editor/ui/locales/Makefile
   "
 fi
 
+if [ "$MOZ_ZIPWRITER" ]; then
+  add_makefiles "
+    modules/libjar/zipwriter/Makefile
+    modules/libjar/zipwriter/public/Makefile
+    modules/libjar/zipwriter/src/Makefile
+    modules/libjar/zipwriter/test/Makefile
+  "
+fi
+
 if [ "$MOZ_STORAGE" ]; then
   add_makefiles "
     db/sqlite3/src/Makefile
     db/morkreader/Makefile
     storage/Makefile
     storage/public/Makefile
     storage/src/Makefile
     storage/build/Makefile