Bug 380813, improve scriptable io, and give file objects class info, meant to check this in a while ago, r=mfinkle,bsmedberg,sr=bsmedberg
authorenndeakin@sympatico.ca
Wed, 25 Jul 2007 09:53:37 -0700
changeset 3958 c92bcdf78520b884b11dfd0b89921ca1952b3fd9
parent 3957 fa513432297b4b654fcb07b1f1448b211e26cc7a
child 3959 e200b1563539ae8347f5166a58b04f1cbb466387
push idunknown
push userunknown
push dateunknown
reviewersmfinkle, bsmedberg, bsmedberg
bugs380813
milestone1.9a7pre
Bug 380813, improve scriptable io, and give file objects class info, meant to check this in a while ago, r=mfinkle,bsmedberg,sr=bsmedberg
netwerk/base/public/Makefile.in
netwerk/base/public/nsIScriptableIO.idl
netwerk/base/src/Makefile.in
netwerk/base/src/nsScriptableIO.js
netwerk/test/Makefile.in
netwerk/test/chrome/Makefile.in
netwerk/test/chrome/test_scriptableio.xul
xpcom/build/nsXPComInit.cpp
xpcom/io/Makefile.in
xpcom/io/nsIScriptableStreams.idl
xpcom/io/nsLocalFileOS2.cpp
xpcom/io/nsLocalFileOS2.h
xpcom/io/nsLocalFileOSX.cpp
xpcom/io/nsLocalFileOSX.h
xpcom/io/nsLocalFileUnix.cpp
xpcom/io/nsLocalFileUnix.h
xpcom/io/nsLocalFileWin.cpp
xpcom/io/nsLocalFileWin.h
xpcom/io/nsScriptableInputStream.cpp
xpcom/io/nsScriptableInputStream.h
xpcom/io/nsScriptableOutputStream.cpp
xpcom/io/nsScriptableOutputStream.h
--- a/netwerk/base/public/Makefile.in
+++ b/netwerk/base/public/Makefile.in
@@ -120,16 +120,17 @@ XPIDLSRCS	= \
 		nsIExternalProtocolHandler.idl \
 		nsIAuthModule.idl \
 		nsIContentSniffer.idl \
 		nsIAuthPromptProvider.idl \
 		nsPISocketTransportService.idl \
 		nsIChannelEventSink.idl \
 		nsINetUtil.idl \
 		nsIProxiedChannel.idl \
+		nsIScriptableIO.idl \
 		$(NULL)
 
 EXPORTS		= \
 		netCore.h \
 		nsNetError.h \
 		nsNetUtil.h \
 		nsNetStrings.h \
 		nsChannelProperties.h \
new file mode 100644
--- /dev/null
+++ b/netwerk/base/public/nsIScriptableIO.idl
@@ -0,0 +1,244 @@
+/* ***** 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 Scriptable IO.
+ *
+ * The Initial Developer of the Original Code is Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Neil Deakin <enndeakin@sympatico.ca> (Original Author)
+ *
+ * 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 nsIVariant;
+interface nsIFile;
+interface nsIInputStream;
+interface nsIOutputStream;
+interface nsIUnicharInputStream;
+interface nsIUnicharOutputStream;
+interface nsIURI;
+
+/**
+ * A singleton object which contains a number of methods for creating files,
+ * streams and uris.
+ *
+ * This interface is intended to be used only from script.
+ */
+[scriptable, uuid(E54798D5-7693-43F9-9BB6-F21E434DD3FC)]
+interface nsIScriptableIO : nsISupports
+{
+  /**
+   * Retrieves a reference to a file or directory on disk, which may or may
+   * not exist. If the file exists, it may be opened for reading by passing
+   * the file as the base to newInputStream. If it doesn't exist, it may be
+   * created by opening an output stream and writing to it.
+   *
+   * Files are identified by the filename argument. The file is found relative
+   * to a well known directory identified by the location argument. This
+   * location is a string key which identifies common directories typically
+   * found on a system. For instance, using the location key 'Desk' will
+   * retrieve files in the desktop folder, and the location key 'TmpD' will
+   * retrieve files in the system's temporary directory.
+   *
+   * A complete list of location keys may be found at
+   *   http://developer.mozilla.org/en/docs/IO_Guide/Directory_Keys
+   *
+   * The filename is always a file within the directory identified by the
+   * location key and this does not include a path. To retrieve
+   * subdirectories, retrieve a file and then use the file's append method
+   * to navigate into further subdirectories. This allows platform independent
+   * paths to be constructed.
+   *
+   * The filename may be a null string to retrieve a reference to the
+   * location directory itself.
+   *
+   * @param aLocation location key of well-known directory
+   * @param aFileName filename to locate within this directory, may be null
+   * @returns a file object
+   * @throws NS_ERROR_INVALID_ARG when aLocation is null
+   */
+  nsIFile getFile(in AString aLocation, in AString aFileName);
+
+  /**
+   * Retrieves a reference to a file given a absolute file path.
+   *
+   * Use this method only when absolutely necessary. In most cases, getFile
+   * should be used instead, as file paths are not portable across different
+   * platforms and systems so this method should be avoided if possible.
+   * Instead, the getFile method should be used and a path constructed from
+   * it.
+   *
+   * The filepath should be an absolute path or the value of the
+   * persistentDescriptor of a file.
+   *
+   * @param aFilePath path to the file
+   * @returns a file object
+   * @throws NS_ERROR_INVALID_ARG when aFilePath is null
+   */
+  nsIFile getFileWithPath(in AString aFilePath);
+
+  /**
+   * Creates a URI object which implements nsIURI. The url argument may either
+   * be a string or a file.
+   *
+   * @param aUri the url to create
+   * @returns a new nsIURI object
+   * @throws NS_ERROR_INVALID_ARG when aUri is null
+   */
+  nsIURI newURI(in nsIVariant aUri);
+
+  /**
+   * Retrieves a stream which may be read from.
+   *
+   * The base argument may be one of a number of different types of objects
+   * which may be read from:
+   *   nsIFile - an object returned from the getFile or getFileWithPath
+   *             methods, or any object which implements the nsIFile
+   *             interface.
+   *   nsITransport - a transport object such as a socket.
+   *   nsIInputStream - a stream returned by a previous call to this method or
+   *                    any other object which implements the nsIInputStream
+   *                    interface.
+   *   string - a string
+   *
+   * The mode may be any number of space separated strings which control
+   * the manner is which the stream is created. If no strings apply, use
+   * a null string. Possible values are:
+   *   text - read unicode converted text. The default character set is UTF-8.
+   *          To read text in a different character set, set the aCharSet
+   *          argument to the desired character set.
+   *   buffered - a stream which uses a buffer to hold a block of the next
+   *              part of the data to read. This mode would normally be used
+   *              as a wrapper for other streams. The size of the buffer
+   *              defaults to 1024 bytes, however the size may be changed by
+   *              specifying the aBufferSize argument. If the text mode is
+   *              used, the stream is always buffered.
+   *   block - when reading from a transport such as a socket, an attempt to
+   *           read from the stream while there is no data available will wait
+   *           until data is available before returning. Without this mode,
+   *           the stream will throw an exception if there is no data
+   *           available.
+   *   deleteonclose - the file is automatically deleted when the stream is
+   *                   closed. This might be used for temporary files.
+   *   closeoneof - the file is automatically closed when the end of the file
+   *                is reached.
+   *   reopenonrewind - used in conjuction with the seek mode, the file will
+   *                    be reopened when a seek to the beginning of the file
+   *                    is done.
+   *   multi - a stream which is used to concatenate the input from multiple
+   *           streams together as if it was one long continuous stream. The
+   *           returned stream implements the nsIMultiplexInputStream
+   *           interface. This mode may only be used if the text or buffered
+   *           modes are not used.
+   *
+   * If the mode is a null string, then no special type of reading is
+   * performed. In this case, the data from the stream is not interpreted in
+   * any way.
+   *
+   * @param aBase the base object to read from
+   * @param aMode flags controlling the reading
+   * @param aCharSet the character set to use when parsing text streams
+   * @param aReplaceChar the replacement character for unknown characters
+   * @param aBufferSize the size of buffer to use for buffered streams
+   * @returns a new input stream
+   * @throws NS_ERROR_INVALID_ARG when aBase is null
+   */
+  nsISupports newInputStream(in nsIVariant aBase,
+                             in AString aMode,
+                             [optional] in AString aCharSet,
+                             [optional] in AString aReplaceChar,
+                             [optional] in unsigned long aBufferSize);
+
+  /**
+   * Retrieves a stream which may be written to.
+   *
+   * The base argument may be one of a number of different types of objects
+   * which may be written to:
+   *   nsIFile - an object returned from the getFile or getFileWithPath
+   *             methods, or any object which implements the nsIFile
+   *             interface.
+   *   nsITransport - a transport object such as a socket.
+   *   nsIOutputStream - a stream returned by a previous call to this method
+   *                     or any other object which implements the
+   *                     nsIOutputStream interface.
+   *   string - a string
+   *
+   * The mode may be any number of space separated strings which control
+   * the manner is which the stream is created. If no strings apply, use
+   * a null string. Possible values are:
+   *   text - write unicode converted text. The default character set is
+   *          UTF-8. To write text in a different character set, set the
+   *          aCharSet argument to the desired character set.
+   *   buffered - a stream which buffers the data being written, which would
+   *              normally be used as a wrapper for other streams. The size of
+   *              the buffer defaults to 1024 bytes, however the size may be
+   *              changed by specifying the aBufferSize argument. If the text
+   *              mode is used, the stream is always buffered.
+   *   append - when writing to files, append to the end of the file instead
+   *            of overwriting. If used in conjuction with the create mode, an
+   *            existing file may be opened for appending, or a new file
+   *            created.
+   *   nocreate - when writing to files, and the file does not yet exist,
+   *              don't create a new file. If this mode is not used, a
+   *              new file will be created if it doesn't exist.
+   *   notruncate - when writing to an existing file, overwrite the existing
+   *                content. If this mode is not used, the file will be
+   *                truncated to 0 length.
+   *   syncsave - if used, then writing methods do not return until the
+   *              data is properly saved.
+   *   block - when writing to a transport such as a socket, an attempt
+   *           to write to the stream will not return until all of the
+   *           data has been written. This may cause a delay if the
+   *           socket's underlying buffer is full. If this mode is not used,
+   *           then an exception will be thrown if the buffer is full.
+   *
+   * If the mode is a null string, then no special type of writing is
+   * performed. In this case, the data being written to the stream is not
+   * interpreted in any way.
+   *
+   * The permissions may be set if a file is created. Typically, an octal
+   * value is used, for example: 0775. The default value when calling
+   * newOutputStream is 0664.
+   *
+   * @param aBase the base object to write to
+   * @param aMode flags controlling the writing
+   * @param aCharSet the character set to use when writing text streams
+   * @param aReplaceChar the replacement character for unknown characters
+   * @param aBufferSize the size of buffer to use for buffered streams
+   * @param aPermissions permissions of a file if one is created.
+   * @returns a new output stream
+   * @throws NS_ERROR_INVALID_ARG when aBase is null
+   */
+  nsISupports newOutputStream(in nsIVariant aBase,
+                              in AString aMode,
+                              [optional] in AString aCharSet,
+                              [optional] in AString aReplaceChar,
+                              [optional] in unsigned long aBufferSize,
+                              [optional] in unsigned long aPermissions);
+};
--- a/netwerk/base/src/Makefile.in
+++ b/netwerk/base/src/Makefile.in
@@ -107,16 +107,17 @@ ifneq (,$(filter mac cocoa,$(MOZ_WIDGET_
 else
 	CPPSRCS += nsURLHelperUnix.cpp
 endif
 endif
 endif
 
 EXTRA_COMPONENTS = \
 		$(srcdir)/nsProxyAutoConfig.js \
+		$(srcdir)/nsScriptableIO.js \
 		$(NULL)
 
 # we don't want the shared lib, but we want to force the creation of a
 # static lib.
 FORCE_STATIC_LIB = 1
 
 include $(topsrcdir)/config/rules.mk
 
new file mode 100644
--- /dev/null
+++ b/netwerk/base/src/nsScriptableIO.js
@@ -0,0 +1,377 @@
+/* ***** 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 Scriptable IO.
+ *
+ * The Initial Developer of the Original Code is Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Neil Deakin <enndeakin@sympatico.ca> (Original Author)
+ *
+ * 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 SCRIPTABLEIO_CLASS_ID = Components.ID("1EDB3B94-0E2E-419A-8C2B-D9E232D41086");
+const SCRIPTABLEIO_CLASS_NAME = "Scriptable IO";
+const SCRIPTABLEIO_CONTRACT_ID = "@mozilla.org/io/scriptable-io;1";
+
+const DEFAULT_BUFFER_SIZE = 1024;
+const DEFAULT_PERMISSIONS = 0644;
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+var gScriptableIO = null;
+
+// when using the directory service, map some extra keys that are
+// easier to understand for common directories
+var mappedDirKeys = {
+  Application: "resource:app",
+  Working: "CurWorkD",
+  Profile: "ProfD",
+  Desktop: "Desk",
+  Components: "ComsD",
+  Temp: "TmpD"
+};
+  
+function ScriptableIO() {
+}
+
+ScriptableIO.prototype =
+{
+  _wrapBaseWithInputStream : function ScriptableIO_wrapBaseWithInputStream
+                                      (base, modes, permissions)
+  {
+    var stream;
+    if (base instanceof Ci.nsIFile) {
+      var behaviour = 0;
+      if (modes["deleteonclose"])
+        behaviour |= Ci.nsIFileInputStream.DELETE_ON_CLOSE;
+      if (modes["closeoneof"])
+        behaviour |= Ci.nsIFileInputStream.CLOSE_ON_EOF;
+      if (modes["reopenonrewind"])
+        behaviour |= Ci.nsIFileInputStream.REOPEN_ON_REWIND;
+
+      stream = Cc["@mozilla.org/network/file-input-stream;1"].
+                 createInstance(Ci.nsIFileInputStream);
+      stream.init(base, 1, permissions, behaviour);
+    }
+    else if (base instanceof Ci.nsITransport) {
+      var flags = modes["block"] ? Ci.nsITransport.OPEN_BLOCKING : 0;
+      stream = base.openInputStream(flags, buffersize, 0);
+    }
+    else if (base instanceof Ci.nsIInputStream) {
+      stream = base;
+    }
+    else if (typeof base == "string") {
+      stream = Cc["@mozilla.org/io/string-input-stream;1"].
+                 createInstance(Ci.nsIStringInputStream);
+      stream.setData(base, base.length);
+    }
+    if (!stream)
+      throw "Cannot create input stream from base";
+
+    return stream;
+  },
+
+  _wrapBaseWithOutputStream : function ScriptableIO_wrapBaseWithOutputStream
+                              (base, modes, permissions)
+  {
+    var stream;
+    if (base instanceof Ci.nsIFile) {
+      stream = Cc["@mozilla.org/network/file-output-stream;1"].
+                 createInstance(Ci.nsIFileOutputStream);
+
+      // default for writing is 'write create truncate'
+      var modeFlags = 42;
+      if (modes["nocreate"])
+        modeFlags ^= 8;
+      if (modes["append"])
+        modeFlags |= 16;
+      if (modes["notruncate"])
+        modeFlags ^= 32;
+      if (modes["syncsave"])
+        modeFlags |= 64;
+
+      stream.init(base, modeFlags, permissions, 0);
+    }
+    else if (base instanceof Ci.nsITransport) {
+      var flags = modes["block"] ? Ci.nsITransport.OPEN_BLOCKING : 0;
+      stream = base.openOutputStream(flags, buffersize, 0);
+    }
+    else if (base instanceof Ci.nsIInputStream) {
+      stream = base;
+    }
+    if (!stream)
+      throw "Cannot create output stream from base";
+
+    return stream;
+  },
+
+  _openForReading : function ScriptableIO_openForReading
+                             (base, modes, buffersize, charset, replchar)
+  {
+    var stream = base;
+    var charstream = null;
+    if (modes["text"]) {
+      if (!charset)
+        charset = "UTF-8";
+      if (!replchar)
+        replchar = Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER;
+
+      charstream = Cc["@mozilla.org/intl/converter-input-stream;1"].
+                     createInstance(Ci.nsIConverterInputStream);
+      charstream.init(base, charset, buffersize, replchar);
+    }
+    else if (modes["buffered"]) {
+      stream = Cc["@mozilla.org/network/buffered-input-stream;1"].
+                 createInstance(Ci.nsIBufferedInputStream);
+      stream.init(base, buffersize);
+    }
+    else if (modes["multi"]) {
+      stream = Cc["@mozilla.org/io/multiplex-input-stream;1"].
+                 createInstance(Ci.nsIMultiplexInputStream);
+      stream.appendStream(base);
+    }
+
+    // wrap the stream in a scriptable stream
+    var sstream = Cc["@mozilla.org/scriptableinputstream;1"].createInstance();
+    sstream.initWithStreams(stream, charstream);
+    return sstream;
+  },
+
+  _openForWriting : function ScriptableIO_openForWriting
+                             (base, modes, buffersize, charset, replchar)
+  {
+    var stream = base;
+    var charstream = null;
+    if (modes["text"]) {
+      if (!charset)
+        charset = "UTF-8";
+      if (!replchar)
+        replchar = Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER;
+
+      charstream = Cc["@mozilla.org/intl/converter-output-stream;1"].
+                     createInstance(Ci.nsIConverterOutputStream);
+      charstream.init(base, charset, buffersize, replchar);
+    }
+    else if (modes["buffered"]) {
+      stream = Cc["@mozilla.org/network/buffered-output-stream;1"].
+                 createInstance(Ci.nsIBufferedOutputStream);
+      stream.init(base, buffersize);
+    }
+
+    // wrap the stream in a scriptable stream
+    var sstream = Cc["@mozilla.org/scriptableoutputstream;1"].createInstance();
+    sstream.initWithStreams(stream, charstream);
+    return sstream;
+  },
+
+  getFile : function ScriptableIO_getFile(location, file)
+  {
+    if (!location)
+      throw Components.results.NS_ERROR_INVALID_ARG;
+
+    if (location in mappedDirKeys)
+      location = mappedDirKeys[location];
+
+    var ds = Cc["@mozilla.org/file/directory_service;1"].
+               getService(Ci.nsIProperties);
+    var fl = ds.get(location, Ci.nsILocalFile);
+    if (file)
+      fl.append(file);
+    return fl;
+  },
+
+  getFileWithPath : function ScriptableIO_getFileWithPath(filepath)
+  {
+    if (!filepath)
+      throw Components.results.NS_ERROR_INVALID_ARG;
+
+    // XXXndeakin not sure if setting persistentDescriptor is best here, but
+    // it's more useful than initWithPath, for instance, for preferences
+    var fl = Cc["@mozilla.org/file/local;1"].createInstance();
+    fl.persistentDescriptor = filepath;
+    return fl;
+  },
+
+  newURI : function ScriptableIO_newURI(uri)
+  {
+    if (!uri)
+      throw Components.results.NS_ERROR_INVALID_ARG;
+
+    var ios = Cc["@mozilla.org/network/io-service;1"].
+                getService(Ci.nsIIOService);
+    if (uri instanceof Ci.nsIFile)
+      return ios.newFileURI(uri);
+    return ios.newURI(uri, "", null);
+  },
+
+  newInputStream : function ScriptableIO_newInputStream
+                            (base, mode, charset, replchar, buffersize)
+  {
+    return this._newStream(base, mode, charset, replchar, buffersize,
+                           DEFAULT_PERMISSIONS, false);
+  },
+
+  newOutputStream : function ScriptableIO_newOutputStream
+                             (base, mode, charset, replchar, buffersize, permissions)
+  {
+    return this._newStream(base, mode, charset, replchar, buffersize,
+                           permissions, true);
+  },
+
+  _newStream : function ScriptableIO_newStream
+                        (base, mode, charset, replchar, buffersize, permissions, iswrite)
+  {
+    if (!base)
+      throw Components.results.NS_ERROR_INVALID_ARG;
+
+    var modes = {};
+    var modeArr = mode.split(/\s+/);
+    for (var m = 0; m < modeArr.length; m++) {
+      modes[modeArr[m]] = true;
+    }
+
+    if (!buffersize)
+      buffersize = DEFAULT_BUFFER_SIZE;
+
+    var stream;
+    if (iswrite) {
+      base = this._wrapBaseWithOutputStream(base, modes, permissions);
+      stream = this._openForWriting(base, modes, buffersize, charset, replchar);
+    }
+    else {
+      base = this._wrapBaseWithInputStream(base, modes, permissions);
+      stream = this._openForReading(base, modes, buffersize, charset, replchar);
+    }
+
+    if (!stream)
+      throw "Cannot create stream from base";
+
+    return stream;
+  },
+
+  // nsIClassInfo
+  classDescription : "IO",
+  classID : SCRIPTABLEIO_CLASS_ID,
+  contractID : SCRIPTABLEIO_CONTRACT_ID,
+  flags : Ci.nsIClassInfo.SINGLETON,
+  implementationLanguage : Ci.nsIProgrammingLanguage.JAVASCRIPT,
+
+  getInterfaces : function ScriptableIO_getInterfaces(aCount) {
+    var interfaces = [Ci.nsIScriptableIO, Ci.nsIClassInfo];
+    aCount.value = interfaces.length;
+    return interfaces;
+  },
+
+  getHelperForLanguage : function ScriptableIO_getHelperForLanguage(aCount) {
+    return null;
+  },
+  
+  QueryInterface: function ScriptableIO_QueryInterface(aIID) {
+    if (aIID.equals(Ci.nsIScriptableIO) ||
+        aIID.equals(Ci.nsIClassInfo) ||
+        aIID.equals(Ci.nsISupports))
+      return this;
+    throw Components.results.NS_ERROR_NO_INTERFACE;
+  }
+}
+
+var ScriptableIOFactory = {
+  QueryInterface: function ScriptableIOFactory_QueryInterface(iid)
+  {
+    if (iid.equals(Ci.nsIFactory) ||
+        iid.equals(Ci.nsISupports))
+      return this;
+    throw Cr.NS_ERROR_NO_INTERFACE;
+  },
+   
+  createInstance: function ScriptableIOFactory_createInstance(aOuter, aIID)
+  {
+    if (aOuter != null)
+      throw Components.results.NS_ERROR_NO_AGGREGATION;
+
+    if (gScriptableIO == null)
+      gScriptableIO = new ScriptableIO();
+
+    return gScriptableIO.QueryInterface(aIID);
+  }
+};
+
+var ScriptableIOModule = {
+  QueryInterface: function ScriptableIOModule_QueryInterface(iid) {
+    if (iid.equals(Ci.nsIModule) ||
+        iid.equals(Ci.nsISupports))
+      return this;
+    throw Cr.NS_ERROR_NO_INTERFACE;
+  },
+
+  registerSelf: function ScriptableIOModule_registerSelf(aCompMgr, aFileSpec, aLocation, aType)
+  {
+    aCompMgr = aCompMgr.QueryInterface(Ci.nsIComponentRegistrar);
+    aCompMgr.registerFactoryLocation(SCRIPTABLEIO_CLASS_ID,
+                                     SCRIPTABLEIO_CLASS_NAME,
+                                     SCRIPTABLEIO_CONTRACT_ID,
+                                     aFileSpec, aLocation, aType);
+
+    var categoryManager = Cc["@mozilla.org/categorymanager;1"].
+                            getService(Ci.nsICategoryManager);
+
+    categoryManager.addCategoryEntry("JavaScript global privileged property", "IO",
+                                     SCRIPTABLEIO_CONTRACT_ID, true, true);
+  },
+
+  unregisterSelf: function ScriptableIOModule_unregisterSelf(aCompMgr, aLocation, aType)
+  {
+    aCompMgr = aCompMgr.QueryInterface(Ci.nsIComponentRegistrar);
+    aCompMgr.unregisterFactoryLocation(SCRIPTABLEIO_CLASS_ID, aLocation);        
+
+    var categoryManager = Cc["@mozilla.org/categorymanager;1"].
+                            getService(Ci.nsICategoryManager);
+    categoryManager.deleteCategoryEntry("JavaScript global privileged property",
+                                        SCRIPTABLEIO_CONTRACT_ID, true);
+  },
+
+  getClassObject: function ScriptableIOModule_getClassObject(aCompMgr, aCID, aIID)
+  {
+    if (!aIID.equals(Ci.nsIFactory))
+      throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
+
+    if (aCID.equals(SCRIPTABLEIO_CLASS_ID))
+      return ScriptableIOFactory;
+
+    throw Components.results.NS_ERROR_NO_INTERFACE;
+  },
+
+  canUnload: function ScriptableIOModule_canUnload(aCompMgr)
+  {
+    gScriptableIO = null;
+    return true;
+  }
+};
+
+function NSGetModule(aCompMgr, aFileSpec) { return ScriptableIOModule; }
--- a/netwerk/test/Makefile.in
+++ b/netwerk/test/Makefile.in
@@ -41,16 +41,17 @@ topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE		= test_necko
 
 DIRS            = \
+                  chrome \
                   httpserver \
                   $(NULL)
 
 REQUIRES	= xpcom \
 		  string \
 		  necko \
 		  pref \
 		  $(NULL)
new file mode 100644
--- /dev/null
+++ b/netwerk/test/chrome/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 mozilla.org code.
+#
+# The Initial Developer of the Original Code is
+# Mozilla Foundation.
+# 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@
+relativesrcdir  = network/test/chrome
+
+include $(DEPTH)/config/autoconf.mk
+include $(topsrcdir)/config/rules.mk
+
+_TEST_FILES = 	test_scriptableio.xul \
+		$(NULL)
+
+libs:: $(_TEST_FILES)
+	$(INSTALL) $^ $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/netwerk/test/chrome/test_scriptableio.xul
@@ -0,0 +1,470 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+<!--
+  Tests for scriptable IO
+  -->
+<window title="Scriptable IO" width="500" height="600"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript" 
+          src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>      
+
+<label value="Scriptable IO Tests"/>
+
+  <!-- test resuls are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/>
+
+  <!-- test code goes here -->
+  <script type="application/javascript"><![CDATA[
+
+function testIO()
+{
+
+// delete files used for this test first in case it failed last time
+var fl = IO.getFile("Desk", "test-sample.txt");
+if (fl.exists())
+  fl.remove(false);
+
+var iswin = navigator.userAgent.indexOf("Windows") >= 0;
+
+ok(IO, "IO is avaialble");
+ok(typeof IO.getFile == "function", "IO.getFile is a function");
+
+// -------- Creating a file object --------
+
+fl = IO.getFile("Desk", "test-sample.txt");
+is(fl.leafName, "test-sample.txt", "getFile creates file");
+ok(fl instanceof Components.interfaces.nsIFile, "getFile creates an nsIFile");
+is(fl.exists(), false, "file returned by getFile does not exist yet");
+
+var dir = IO.getFile("Desktop", "");
+is(dir.isDirectory(), true, "directory returned when filename is null");
+
+expectException(function() { var s = IO.newInputStream(dir, "", ""); s.readString(5); },
+                "open input stream on directory");
+expectException(function() { IO.newOutputStream(dir, ""); },
+                "open output stream on directory");
+
+expectException(function() { IO.getFile("", ""); }, "open null file");
+
+is(IO.getFileWithPath(fl.path + "2").leafName, "test-sample.txt2",
+   "getFileWithPath creates file");
+
+expectException(function() { IO.getFileWithPath(""); },
+                "getFileWithPath with null path");
+
+ok(IO.getFile("Application", "").path.length > 0, "Application directory");
+ok(IO.getFile("Working", "").path.length > 0, "Working directory");
+ok(IO.getFile("Profile", "").path.length > 0, "Profile directory");
+ok(IO.getFile("Desktop", "").path.length > 0, "Desktop directory");
+ok(IO.getFile("Components", "").path.length > 0, "Components directory");
+ok(IO.getFile("Temp", "").path.length > 0, "Temp directory");
+
+// -------- Writing to a standard stream --------
+
+// Mac is the only platform where the file size is updated after writing
+var checkFileSize = (navigator.platform.indexOf("Mac") >= 0);
+
+var stream;
+var types = ["", "buffered"];
+
+for (var t = 0; t < types.length; t++) {
+  var etype = types[t];
+  var ename = etype + " ";
+
+  stream = IO.newOutputStream(fl, etype);
+  ok(typeof stream.write == "function", ename + "IO.newOutputStream returned a stream");
+  is(fl.exists(), true, ename + "file after creating output stream does not exist yet");
+  is(stream.tell(), 0, ename + "file write position is 0");
+  stream.write("This is a string", 16);
+
+  if (etype == "buffered") {
+    if (checkFileSize)
+      is(fl.fileSize, 0, ename + "file size after writing before flush");
+    stream.flush();
+  }
+  if (checkFileSize)
+    is(fl.fileSize, 16, ename + "file size after writing");
+  is(stream.tell(), 16, ename + "file write position after writing is 16");
+  stream.flush(); // just call flush to make sure no error occurs
+  // disable this test for now because the default permissions is different
+  // on each machine
+  // if (!win)
+  //   is(fl.permissions & 511, 0644, ename + "permissions after creating file");
+
+  // check stream constants
+  is(stream.NS_SEEK_SET, 0, ename + "NS_SEEK_SET is 0");
+  is(stream.NS_SEEK_CUR, 1, ename + "NS_SEEK_CUR is 1");
+  is(stream.NS_SEEK_END, 2, ename + "NS_SEEK_END is 2");
+
+  // check file seeking with writing
+  stream.seek(stream.NS_SEEK_SET, 8);
+  is(stream.tell(), 8, ename + "file write position after seek is correct");
+  stream.write("another string", 14);
+  if (etype == "buffered")
+    stream.flush();
+  if (checkFileSize)
+    is(fl.fileSize, 22, ename + "file size after writing again is correct");
+  is(stream.tell(), 22, ename + "file write position after writing again is correct");
+
+  stream.seek(stream.NS_SEEK_SET, 2);
+  stream.seek(stream.NS_SEEK_CUR, 9);
+  is(stream.tell(), 11, ename + "file write position after current position seek");
+  stream.seek(stream.NS_SEEK_CUR, -3);
+  is(stream.tell(), 8, ename + "file write position after reverse current position seek");
+  stream.seek(stream.NS_SEEK_END, 0);
+  is(stream.tell(), 22, ename + "file write position after end position seek");
+  stream.seek(stream.NS_SEEK_END, 10);
+  is(stream.tell(), 32, ename + "file write position after end position with value seek");
+  // seeking past the end of the file and writing will fill the blank area with zeros
+  stream.write(".", 1);
+  if (etype == "buffered")
+    stream.flush();
+  if (checkFileSize)
+    is(fl.fileSize, 33, ename + "file size after writing beyond end is correct");
+  is(stream.tell(), 33, ename + "file write position after writing beyond end seek");
+  stream.seek(stream.NS_SEEK_END, -11);
+  is(stream.tell(), 22, ename + "file write position after reverse end position seek");
+  stream.write("   ", 3);
+  stream.writeString("*a*");
+  stream.write("\u03a9", 2); // unicode isn't processed so should output a9 and 00
+  stream.write("\u03a9", 1); // one character, so should output a9
+  stream.write("\u00a8", 2); // write single character as a8 and 00
+  stream.write("\u00a8", 1); // write single character as a8
+  stream.writeString(".");
+  stream.write("Ends Here", 3);
+  is(stream.tell(), 38, ename + "file write position extra writing is 38");
+
+  stream.writeString("More Text");
+  if (etype = "buffered")
+    stream.flush();
+
+  stream.seek(stream.NS_SEEK_SET, 42);
+  stream.setEOF();
+  if (checkFileSize)
+    is(fl.fileSize, 42, ename + "setEOF truncates output stream");
+
+  stream.close();
+  // buffered streams cause an infinite loop when writing to a closed stream
+  if (etype != "buffered")
+    expectException(function() { stream.write("write after close", 17); },
+                    ename + "writing after stream is closed");
+  stream.close(); // calling close on a closed stream should do nothing
+
+  // -------- Reading from a standard stream --------
+
+  stream = IO.newInputStream(fl, etype);
+  ok(typeof stream.available == "function", ename + "IO.newInputStream returned a stream");
+  ok(!stream.isNonBlocking(), ename + "file input stream is blocking");
+  is(stream.tell(), 0, ename + "file read position is 0");
+  is(stream.available(), 42, ename + "available count after input stream creation");
+  is(stream.read(9), "This is a", ename + "file read");
+  is(stream.available(), 33, ename + "available count after read");
+  is(stream.tell(), 9, ename + "file read position after reading is 9");
+
+  expectException(function() { stream.setEOF(); }, ename + "setEOF on input stream");
+
+  // check file seeking with reading
+  stream.seek(stream.NS_SEEK_SET, 5);
+  is(stream.tell(), 5, ename + "file write position after seek is correct");
+  is(stream.read(2), "is", ename + "file read after seek");
+  is(stream.tell(), 7, ename + "file read position after seek and read is 7");
+
+  stream.seek(stream.NS_SEEK_CUR, 3);
+  is(stream.read(5), "other", ename + "file read after current seek");
+  stream.seek(stream.NS_SEEK_CUR, -4);
+  is(stream.read(3), "the", ename + "file read after reverse current seek");
+
+  stream.seek(stream.NS_SEEK_END, 0);
+  is(stream.available(), 0, ename + "available count after end seek");
+  is(stream.read(5), "", ename + "file read after end seek");
+  stream.seek(stream.NS_SEEK_END, -26);
+  is(stream.read(6), "string", ename + "file read after reverse end seek");
+
+  stream.seek(stream.NS_SEEK_SET, 5);
+
+  // this will only read to the 0 added when the unicode character was written above
+  is(stream.read(1000), "is another string   *a*\u00a9", ename + "file read after large read");
+  is(stream.tell(), 42, ename + "file read position after large read is 7");
+  stream.seek(stream.NS_SEEK_END, -9)
+  is(stream.read(1000), "\u00a8.EndMore", ename + "file read after another large read");
+  is(stream.available(), 0, ename + "available count after end of file reached");
+
+  stream.close();
+  // buffered input streams don't fail when reading after closing
+  if (etype != "buffered")
+    expectException(function() { stream.read(5); },
+                    ename + "reading after stream is closed");
+  stream.close();
+
+  // -------- Check other modes --------
+
+  // notruncate should open file at position 0 but doesn't truncate the file
+  // set the sync flag just to make sure that it doesn't cause an error
+  stream = IO.newOutputStream(fl, ename + "notruncate syncsave");
+  is(stream.tell(), 0, ename + "file write position after notruncate opening");
+  if (checkFileSize)
+    is(fl.fileSize, 42, ename + "file size after notruncate opening");
+  stream.writeString("That");
+  stream.close();
+
+  checkFile(fl, "That is", ename + "writing after notruncate opening");
+
+  stream = IO.newOutputStream(fl, "");
+  stream.writeString("This is a string.");
+  stream.close();
+
+  stream = IO.newOutputStream(fl, ename + "notruncate append");
+  // for appending, the write position isn't updated until a write occurs so
+  // it will still be 0 at this point
+  is(stream.tell(), 0, ename + "file write position after append opening");
+  if (checkFileSize)
+    is(fl.fileSize, 17, ename + "file size after append opening");
+  stream.writeString("...");
+  stream.close();
+
+  checkFile(fl, "This is a string....",
+            ename + "Writing after append opening");
+
+  stream = IO.newOutputStream(fl, etype);
+  is(stream.tell(), 0, ename + "file write position after reopening");
+  if (checkFileSize)
+    is(fl.fileSize, 0, ename + "file size after reopening");
+  stream.writeString("Hello");
+  stream.close();
+
+  checkFile(fl, "Hello", ename + "Writing after reopening");
+
+  var stream = IO.newInputStream(fl, "closeoneof", "", "", 512);
+  stream.read(50);
+  // the closeoneof flag actually only closes the file when an attempt is made
+  // to read when the end of file is already reached, so another call to read
+  // is needed
+  stream.read(1);
+  expectException(function() { stream.read(1); },
+                  ename + "read after end from closeoneof opened stream");
+
+  stream = IO.newInputStream(fl, "deleteonclose");
+  stream.readString(5);
+  stream.close();
+  is(fl.exists(), false, ename + "file is deleted with deleteonclose flag");
+
+  expectException(function() { stream = IO.newOutputStream(fl, ename + "nocreate"); },
+                  ename + "opening with nocreate when file doesn't exist");
+  is(fl.exists(), false, ename + "file doesn't exist after opening output stream with nocreate flag");
+
+  if (fl.exists())
+    fl.remove(false);
+
+  // -------- Check string streams --------
+
+  stream = IO.newInputStream("This is a string to read from", etype);
+  is(stream.available(), 29, ename + "available count in string stream");
+  is(stream.tell(), 0, ename + "string read positon is initially 0");
+  is(stream.read(4), "This", ename + "read from string stream");
+  is(stream.tell(), 4, ename + "string read positon after read is 4");
+  stream.seek(stream.NS_SEEK_SET, 10);
+  is(stream.tell(), 10, ename + "string read positon after set seek is 10");
+  is(stream.read(6), "string", ename + "string read after set seek");
+
+  stream.seek(stream.NS_SEEK_CUR, -8);
+  is(stream.read(1), "a", ename + "string read after cur seek");
+  stream.seek(stream.NS_SEEK_END, -4);
+  is(stream.read(40), "from", ename + "string read after end seek");
+
+  stream.close();
+}
+
+expectException(function() { IO.newInputStream(null, ""); },
+                "newInputStream with null base");
+expectException(function() { IO.newOutputStream(null, ""); },
+                "newOutputStream with null base");
+
+// -------- Writing to a text stream --------
+
+stream = IO.newOutputStream(fl, "text");
+ok(!stream.isNonBlocking(), "file output stream is blocking");
+stream.write("This is cool", 12);
+is(fl.fileSize, 12, "file size after text write");
+stream.writeString(" \u03a9\u03a9End");
+if (checkFileSize)
+  is(fl.fileSize, 20, "file size after text writeString");
+stream.close();
+
+// -------- Reading from a text stream --------
+
+stream = IO.newInputStream(fl, "text");
+// for text streams, 1 means text can be read, 0 means no text available
+is(stream.available(), 20, "available count after text input stream creation");
+is(stream.readString(4), "This", "read text file");
+is(stream.available(), 1, "available count after read");
+is(stream.readString(13), " is cool \u03a9\u03a9En", "read string from text file");
+is(stream.available(), 1, "available count at end");
+is(stream.readString(1), "d", "read string at end from text file");
+is(stream.available(), 1, "available count at end");
+is(stream.readString(1), "", "read string after end from text file");
+is(stream.available(), 0, "available count after end");
+
+stream.close();
+
+fl.remove(false);
+
+// -------- Check other character sets --------
+
+stream = IO.newOutputStream(fl, "text", "UTF-16", "*", 2048, 0755);
+
+ok("writeString" in stream, "newOutputStream for UTF-16 returns output stream");
+stream.write("This is cool", 12);
+// file size is 12 characters times 2 bytes each plus two bytes for
+// the byte order mark
+is(fl.fileSize, 26, "file size after UTF-16 text write");
+stream.writeString(" \u03a9\u03a9End\u9999");
+if (checkFileSize)
+  is(fl.fileSize, 40, "file size after UTF-16 text writeString");
+// XXXndeakin
+// disable this test for now because the default permissions is different
+// on each machine
+//if (!iswin)
+//  is(fl.permissions & 511, 0755, "permissions after creating file with newOutputStream");
+stream.close();
+
+stream = IO.newInputStream(fl, "text", "UTF-16", "*", 1024);
+is(stream.available(), 40, "available count after UTF-16 input stream creation");
+is(stream.readString(16), "This is cool \u03a9\u03a9E", "read UTF-16 file");
+stream.close();
+
+// -------- Check linefeed handling --------
+
+stream = IO.newOutputStream(fl, "text");
+stream.writeString("Line One\rLine Two\nLine Three\r\nLine Four\n\rLine Five\n\nLine Six");
+stream.close();
+
+stream = IO.newInputStream(fl, "text");
+is(stream.readLine(), "Line One", "read line with carriage return");
+is(stream.readLine(), "Line Two", "read line with newline");
+is(stream.readLine(), "Line Three", "read line with carriage return and newline");
+is(stream.readLine(), "Line Four", "read line with newline and carriage return");
+is(stream.readLine(), "Line Five", "read line with newline and newline");
+is(stream.readLine(), "", "read blank line");
+is(stream.readLine(), "Line Six", "read last line");
+is(stream.readLine(), "", "read after end");
+
+// -------- Check multiplex streams --------
+
+expectException(function() { stream.count(); }, "multi functions on non-multi stream");
+
+stream = IO.newOutputStream(fl, "");
+stream.writeString("A middle string. ");
+stream.close();
+
+stream = IO.newInputStream(fl, "multi");
+is(stream.count, 1, "count on multi stream");
+
+var stream2 = IO.newInputStream("A final string.", "");
+stream.appendStream(stream2);
+is(stream.getStream(1), stream2, "stream appended");
+is(stream.count, 2, "count on multi stream after appending");
+
+var stream3 = IO.newInputStream("An initial string. ", "");
+stream.insertStream(stream3, 0);
+is(stream.getStream(0), stream3, "stream inserting");
+is(stream.count, 3, "count on multi stream after inserting");
+
+stream.insertStream(IO.newInputStream("A small string", ""), 1);
+stream.removeStream(1);
+is(stream.count, 3, "count on multi stream after removing");
+
+expectException(function() { stream.removeStream(5); },
+                "remove out of range index in multi stream");
+
+var expectedText = "An initial string. A middle string. A final string.";
+is(stream.read(expectedText.length), expectedText, "read from multi stream");
+
+stream.close();
+
+// -------- Check binary methods --------
+
+stream = IO.newOutputStream(fl, "");
+stream.writeBoolean(true);
+stream.writeBoolean(false);
+stream.write8(10);
+stream.write16(8209);
+stream.write16(-8209);
+stream.write16(300000);
+stream.write32(200000);
+is(stream.tell(), 13, "write position after binary write");
+stream.writeFloat(0.25);
+stream.writeDouble(56020.65);
+stream.writeByteArray([100, 0, 1000, 5], 4);
+stream.seek(stream.NS_SEEK_SET, 3);
+stream.write16(8211);
+stream.close();
+if (checkFileSize)
+  is(fl.fileSize, 29, "file size after binary write");
+
+stream = IO.newInputStream(fl, "");
+is(stream.readBoolean(), true, "read boolean true from binary");
+is(stream.readBoolean(), false, "read boolean false from binary");
+is(stream.read8(), 10, "read 8-bit integer from binary");
+is(stream.read16(), 8211, "read 16-bit integer from binary");
+is(stream.read16(), 57327, "read negative 16-bit integer from binary");
+is(stream.read16(), 37856, "read another 16-bit integer from binary");
+is(stream.tell(), 9, "read position from binary");
+is(stream.read32(), 200000, "read 32-bit integer from binary");
+is(stream.readFloat(), 0.25, "read float from binary");
+is(stream.readDouble(), 56020.65, "read double from binary");
+
+stream.seek(stream.NS_SEEK_SET, 2);
+var arr = stream.readByteArray(27);
+var expectedArr = [10, 32, 19, 223, 239, 147, 224, 0, 3, 13, 64, 62, 128,
+                   0, 0, 64, 235, 90, 148, 204, 204, 204, 205, 100, 0, 232, 5];
+var isok = true;
+for (var a = 0; a < expectedArr.length; a++) {
+  if (expectedArr[a] != arr[a]) {
+    isok = false;
+    break;
+  }
+}
+ok(isok && arr.length == expectedArr.length, "read byte array from binary");
+stream.close();
+
+// -------- Check newURI method --------
+
+var uri = IO.newURI("http://www.mozilla.org");
+is(uri.spec, "http://www.mozilla.org/", "newURI returns nsIURI");
+
+ok(IO.newURI(fl).spec.indexOf("file:/") == 0, "newURI returns nsIURI for file");
+
+expectException(function() { IO.newURI(""); }, "newURI with null uri");
+
+}
+
+function checkFile(fl, expectedText, testname)
+{
+  var stream = IO.newInputStream(fl, "");
+  is(stream.read(expectedText.length), expectedText, testname);
+  stream.close();
+}
+
+function expectException(fn, testname)
+{
+  var exh = false;
+  try {
+    fn();
+  }
+  catch(ex) {
+    exh = true;
+    ok(ex, testname + " failed properly");
+  }
+  if (!exh) ok(false, testname + " didn't fail"); 
+}
+
+testIO();
+
+]]>
+
+</script>
+
+</window>
--- a/xpcom/build/nsXPComInit.cpp
+++ b/xpcom/build/nsXPComInit.cpp
@@ -46,16 +46,17 @@
 #include "prlink.h"
 #include "nsCOMPtr.h"
 #include "nsObserverList.h"
 #include "nsObserverService.h"
 #include "nsProperties.h"
 #include "nsIProperties.h"
 #include "nsPersistentProperties.h"
 #include "nsScriptableInputStream.h"
+#include "nsScriptableOutputStream.h"
 #include "nsBinaryStream.h"
 #include "nsStorageStream.h"
 #include "nsPipe.h"
 
 #include "nsMemoryImpl.h"
 #include "nsDebugImpl.h"
 #include "nsTraceRefcntImpl.h"
 #include "nsErrorService.h"
@@ -239,16 +240,19 @@ nsThreadManagerGetSingleton(nsISupports*
 
     return nsThreadManager::get()->QueryInterface(aIID, aInstancePtr);
 }
 NS_DECL_CLASSINFO(nsThreadManager)
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsThreadPool)
 NS_DECL_CLASSINFO(nsThreadPool)
 
+NS_DECL_CLASSINFO(nsScriptableInputStream)
+NS_DECL_CLASSINFO(nsScriptableOutputStream)
+
 static NS_METHOD
 nsXPTIInterfaceInfoManagerGetSingleton(nsISupports* outer,
                                        const nsIID& aIID,
                                        void* *aInstancePtr)
 {
     NS_ASSERTION(aInstancePtr, "null outptr");
     NS_ENSURE_TRUE(!outer, NS_ERROR_NO_AGGREGATION);
 
@@ -346,17 +350,20 @@ PRBool gXPCOMShuttingDown = PR_FALSE;
 
 static const nsModuleComponentInfo components[] = {
     COMPONENT(MEMORY, nsMemoryImpl::Create),
     COMPONENT(DEBUG,  nsDebugImpl::Create),
 #define NS_ERRORSERVICE_CLASSNAME NS_ERRORSERVICE_NAME
     COMPONENT(ERRORSERVICE, nsErrorService::Create),
 
     COMPONENT(BYTEBUFFER, ByteBufferImpl::Create),
-    COMPONENT(SCRIPTABLEINPUTSTREAM, nsScriptableInputStream::Create),
+    COMPONENT_CI(SCRIPTABLEINPUTSTREAM, nsScriptableInputStream::Create,
+                 nsScriptableInputStream),
+    COMPONENT_CI(SCRIPTABLEOUTPUTSTREAM, nsScriptableOutputStream::Create,
+                 nsScriptableOutputStream),
     COMPONENT(BINARYINPUTSTREAM, nsBinaryInputStreamConstructor),
     COMPONENT(BINARYOUTPUTSTREAM, nsBinaryOutputStreamConstructor),
     COMPONENT(STORAGESTREAM, nsStorageStreamConstructor),
     COMPONENT(VERSIONCOMPARATOR, nsVersionComparatorImplConstructor),
     COMPONENT(PIPE, nsPipeConstructor),
 
 #define NS_PROPERTIES_CLASSNAME  "Properties"
     COMPONENT(PROPERTIES, nsPropertiesConstructor),
--- a/xpcom/io/Makefile.in
+++ b/xpcom/io/Makefile.in
@@ -61,16 +61,17 @@ CPPSRCS		= \
 		nsEscape.cpp \
 		nsInputStreamTee.cpp \
 		nsLinebreakConverter.cpp \
 		nsLocalFileCommon.cpp \
 		nsMultiplexInputStream.cpp \
 		nsPipe3.cpp \
 		nsStreamUtils.cpp \
 		nsScriptableInputStream.cpp \
+		nsScriptableOutputStream.cpp \
 		nsSegmentedBuffer.cpp \
 		SpecialSystemDirectory.cpp \
 		nsStorageStream.cpp \
 		nsStringStream.cpp \
 		nsUnicharInputStream.cpp \
 		nsNativeCharsetUtils.cpp \
 		$(NULL)
 
@@ -162,16 +163,17 @@ endif # os2
 
 SDK_XPIDLSRCS   = \
 		nsIDirectoryService.idl \
 		nsIFile.idl \
 		nsILocalFile.idl \
 		nsIInputStream.idl \
 		nsIOutputStream.idl \
 		nsIScriptableInputStream.idl \
+		nsIScriptableStreams.idl \
 		$(NULL)
 
 SDK_HEADERS =  \
 		nsDirectoryServiceDefs.h \
 		nsDirectoryServiceUtils.h \
 		$(NULL)
 
 EXPORTS		:= $(addprefix $(srcdir)/, $(EXPORTS))
new file mode 100644
--- /dev/null
+++ b/xpcom/io/nsIScriptableStreams.idl
@@ -0,0 +1,212 @@
+/* ***** 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 Scriptable IO.
+ *
+ * The Initial Developer of the Original Code is Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Neil Deakin <enndeakin@sympatico.ca> (Original Author)
+ *
+ * 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 nsIVariant;
+interface nsIFile;
+interface nsIInputStream;
+interface nsIOutputStream;
+interface nsIUnicharInputStream;
+interface nsIUnicharOutputStream;
+
+/**
+ * Streams used with scriptable IO, which is itself located within netwerk.
+ */
+
+[scriptable, uuid(9245740D-D22E-4065-A1A0-72F0AE45E6DF)]
+interface nsIScriptableIOInputStream : nsISupports
+{
+  /**
+   * Called to initialize the stream.
+   */
+  void initWithStreams(in nsIInputStream aStream,
+                       in nsIUnicharInputStream aCharStream);
+
+  /**
+   * Read string of aCount characters from the stream. If the stream is
+   * text, then the characters are read in the expected character set.
+   * If the stream is non-text aCount bytes are read and returned as a
+   * string. If the end of the stream, or the end of the available data
+   * is reached, the returned string may be shorter than the desired
+   * length.
+   *
+   * @param aCount the number of characters to read
+   * @returns the string read from the stream 
+   */
+  AString readString(in unsigned long aCount);
+
+  /**
+   * Read from the stream until an end of line is reached and return a string
+   * containing all characters up until that point. An end of line is
+   * indicated by a 0x0A, 0x0D, a sequence of 0x0A 0x0D or a sequence of
+   * 0x0D 0x0A. These characters are not returned as part of the string.
+   *
+   * @returns the next line from the stream
+   */
+  AString readLine();
+
+  /**
+   * Read a single byte from a stream and return false if the byte is zero and
+   * true if the byte is non-zero.
+   *
+   * @param a boolean value for the next byte in the stream
+   */
+  boolean readBoolean();
+
+  /**
+   * Read a single byte from a stream.
+   *
+   * @returns the next byte in the stream
+   */
+  octet read8();
+
+  /**
+   * Read and interpret the next two bytes in the stream as an unsigned
+   * big endian integer.
+   *
+   * @returns the next 16-bit integer in the stream
+   */
+  unsigned short read16();
+
+  /**
+   * Read and interpret the next four bytes in the stream as an unsigned
+   * big endian integer.
+   *
+   * @returns the next 32-bit integer in the stream
+   */
+  unsigned long read32();
+
+  /**
+   * Read and interpret the next four bytes in the stream as a floating point
+   * value.
+   *
+   * @returns the next float in the stream
+   */
+  float readFloat();
+
+  /**
+   * Read and interpret the next eight bytes in the stream as a double
+   * floating point value.
+   *
+   * @returns the next double in the stream
+   */
+  double readDouble();
+
+  /**
+   * Read aCount bytes from the stream and fill the aBytes array with
+   * the bytes.
+   *
+   * @param aCount the number of bytes to read
+   * @param aBytes [out] set to the array of read bytes
+   */
+  void readByteArray(in unsigned long aCount,
+                     [array, size_is(aCount), retval] out octet aBytes);
+
+};
+
+[scriptable, uuid(11FAE7E6-DF5B-4D80-B4C9-61849378364D)]
+interface nsIScriptableIOOutputStream : nsISupports
+{
+  /**
+   * Called to initialize the stream.
+   */
+  void initWithStreams(in nsIOutputStream aStream,
+                       in nsIUnicharOutputStream aCharStream);
+
+  /**
+   * Write the string aString to the stream. For text streams, the string is
+   * written in the expected character set. For other streams, the string is
+   * interpreted as bytes, which means that characters above 255 are only
+   * written using their low 8 bits.
+   *
+   * @param aString the string to write
+   * @returns true if the entire string was written, false otherwise
+   */
+  boolean writeString(in AString aString);
+
+  /**
+   * Write a boolean to the stream. If the boolean is false, 0 is written,
+   * and if the boolean is true, 1 is written.
+   *
+   * @param aBoolean the value to write
+   */
+  void writeBoolean(in boolean aBoolean);
+
+  /**
+   * Write a single byte to the stream.
+   *
+   * @param aByte the value to write
+   */
+  void write8(in octet aByte);
+
+  /**
+   * Write a 16-bit integer to the stream as an unsigned big endian value.
+   *
+   * @param a16 the value to write
+   */
+  void write16(in unsigned short a16);
+
+  /**
+   * Write a 32-bit integer to the stream as an unsigned big endian value.
+   *
+   * @param a32 the value to write
+   */
+  void write32(in unsigned long a32);
+
+  /**
+   * Write a floating point value to the stream in 4 bytes.
+   *
+   * @param aFloat the value to write
+   */
+  void writeFloat(in float aFloat);
+
+  /**
+   * Write a double floating point value to the stream in 8 bytes.
+   *
+   * @param aDouble the value to write
+   */
+  void writeDouble(in double aDouble);
+
+  /**
+   * Write aCount values from the array aBytes to the stream.
+   *
+   * @param aBytes the array of write
+   * @param aCount the number of bytes to write
+   */
+  void writeByteArray([array, size_is(aCount)] in octet aBytes,
+                      in unsigned long aCount);
+};
--- a/xpcom/io/nsLocalFileOS2.cpp
+++ b/xpcom/io/nsLocalFileOS2.cpp
@@ -46,16 +46,17 @@
 #include "nsMemory.h"
 
 #include "nsLocalFile.h"
 #include "nsNativeCharsetUtils.h"
 
 #include "nsISimpleEnumerator.h"
 #include "nsIDirectoryEnumerator.h"
 #include "nsIComponentManager.h"
+#include "nsIProgrammingLanguage.h"
 #include "prtypes.h"
 #include "prio.h"
 
 #include "nsReadableUtils.h"
 #include "nsISupportsPrimitives.h"
 #include "nsIMutableArray.h"
 #include "nsTraceRefcntImpl.h"
 
@@ -610,21 +611,32 @@ nsLocalFile::nsLocalFileConstructor(nsIS
     return NS_OK;
 }
 
 
 //-----------------------------------------------------------------------------
 // nsLocalFile::nsISupports
 //-----------------------------------------------------------------------------
 
-NS_IMPL_THREADSAFE_ISUPPORTS4(nsLocalFile,
-                              nsILocalFile,
-                              nsIFile,
-                              nsILocalFileOS2,
-                              nsIHashable)
+NS_IMPL_THREADSAFE_ADDREF(nsLocalFile)
+NS_IMPL_THREADSAFE_RELEASE(nsLocalFile)
+NS_IMPL_QUERY_INTERFACE5_CI(nsLocalFile,
+                            nsILocalFile,
+                            nsIFile,
+                            nsILocalFileOS2,
+                            nsIHashable,
+                            nsIClassInfo)
+NS_IMPL_CI_INTERFACE_GETTER4(nsLocalFile,
+                             nsILocalFile,
+                             nsIFile,
+                             nsILocalFileOS2,
+                             nsIHashable)
+
+NS_DECL_CLASSINFO(nsLocalFile)
+NS_IMPL_THREADSAFE_CI(nsLocalFile)
 
 
 //-----------------------------------------------------------------------------
 // nsLocalFile <private>
 //-----------------------------------------------------------------------------
 
 nsLocalFile::nsLocalFile(const nsLocalFile& other)
   : mDirty(PR_TRUE)
--- a/xpcom/io/nsLocalFileOS2.h
+++ b/xpcom/io/nsLocalFileOS2.h
@@ -51,33 +51,36 @@
 #include "nscore.h"
 #include "nsError.h"
 #include "nsString.h"
 #include "nsCRT.h"
 #include "nsIFile.h"
 #include "nsIFactory.h"
 #include "nsILocalFileOS2.h"
 #include "nsIHashable.h"
+#include "nsIClassInfoImpl.h"
 
 #define INCL_DOSFILEMGR
 #define INCL_DOSERRORS
 #define INCL_DOSPROCESS
 #define INCL_DOSSESMGR
 #define INCL_DOSMODULEMGR
 #define INCL_DOSNLS
 #define INCL_DOSMISC
 #define INCL_WINCOUNTRY
 #define INCL_WINWORKPLACE
 
 #include <os2.h>
 
 class TypeEaEnumerator;
 
 class NS_COM nsLocalFile : public nsILocalFileOS2,
-                                   public nsIHashable
+                           public nsIHashable,
+                           public nsIClassInfo
+
 {
 public:
     NS_DEFINE_STATIC_CID_ACCESSOR(NS_LOCAL_FILE_CID)
 
     nsLocalFile();
 
     static NS_METHOD nsLocalFileConstructor(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr);
 
@@ -91,16 +94,19 @@ public:
     NS_DECL_NSILOCALFILE
 
     // nsILocalFileOS2 interface
     NS_DECL_NSILOCALFILEOS2
 
     // nsIHashable interface
     NS_DECL_NSIHASHABLE
 
+    // nsIClassInfo interface
+    NS_DECL_NSICLASSINFO
+
 public:
     static void GlobalInit();
     static void GlobalShutdown();
 
 private:
     nsLocalFile(const nsLocalFile& other);
     ~nsLocalFile() {}
 
--- a/xpcom/io/nsLocalFileOSX.cpp
+++ b/xpcom/io/nsLocalFileOSX.cpp
@@ -43,16 +43,18 @@
 #include "nsDirectoryServiceDefs.h"
 
 #include "nsString.h"
 #include "nsReadableUtils.h"
 #include "nsIDirectoryEnumerator.h"
 #include "nsISimpleEnumerator.h"
 #include "nsITimelineService.h"
 #include "nsVoidArray.h"
+#include "nsIClassInfoImpl.h"
+#include "nsIProgrammingLanguage.h"
 
 #include "plbase64.h"
 #include "prmem.h"
 #include "nsCRT.h"
 #include "nsHashKeys.h"
 
 #include "MoreFilesX.h"
 #include "FSCopyObject.h"
@@ -340,22 +342,33 @@ nsLocalFile::~nsLocalFile()
 
 
 //*****************************************************************************
 //  nsLocalFile::nsISupports
 //*****************************************************************************
 #pragma mark -
 #pragma mark [nsISupports]
 
-NS_IMPL_THREADSAFE_ISUPPORTS4(nsLocalFile,
-                              nsILocalFileMac,
-                              nsILocalFile,
-                              nsIFile,
-                              nsIHashable)
-                              
+NS_IMPL_THREADSAFE_ADDREF(nsLocalFile)
+NS_IMPL_THREADSAFE_RELEASE(nsLocalFile)
+NS_IMPL_QUERY_INTERFACE5_CI(nsLocalFile,
+                            nsILocalFileMac,
+                            nsILocalFile,
+                            nsIFile,
+                            nsIHashable,
+                            nsIClassInfo)
+NS_IMPL_CI_INTERFACE_GETTER4(nsLocalFile,
+                             nsILocalFileMac,
+                             nsILocalFile,
+                             nsIFile,
+                             nsIHashable)
+
+NS_DECL_CLASSINFO(nsLocalFile)
+NS_IMPL_THREADSAFE_CI(nsLocalFile)
+
 NS_METHOD nsLocalFile::nsLocalFileConstructor(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr)
 {
   NS_ENSURE_ARG_POINTER(aInstancePtr);
   NS_ENSURE_NO_AGGREGATION(outer);
 
   nsLocalFile* inst = new nsLocalFile();
   if (inst == NULL)
     return NS_ERROR_OUT_OF_MEMORY;
--- a/xpcom/io/nsLocalFileOSX.h
+++ b/xpcom/io/nsLocalFileOSX.h
@@ -37,47 +37,50 @@
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsLocalFileMac_h__
 #define nsLocalFileMac_h__
 
 #include "nsILocalFileMac.h"
 #include "nsString.h"
 #include "nsIHashable.h"
+#include "nsIClassInfoImpl.h"
 
 class nsDirEnumerator;
 
 //*****************************************************************************
 //  nsLocalFile
 //
 // The native charset of this implementation is UTF-8. The Unicode used by the
 // Mac OS file system is decomposed, so "Native" versions of these routines will
 // always use decomposed Unicode (NFD). Their "non-Native" counterparts are 
 // intended to be simple wrappers which call the "Native" version and convert 
 // between UTF-8 and UTF-16. All the work is done on the "Native" side except
 // for the conversion to NFC (composed Unicode) done in "non-Native" getters.
 //*****************************************************************************
 
 class NS_COM nsLocalFile : public nsILocalFileMac,
-                           public nsIHashable
+                           public nsIHashable,
+                           public nsIClassInfo
 {
     friend class nsDirEnumerator;
     
 public:
     NS_DEFINE_STATIC_CID_ACCESSOR(NS_LOCAL_FILE_CID)
     
                         nsLocalFile();
 
     static NS_METHOD    nsLocalFileConstructor(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr);
 
     NS_DECL_ISUPPORTS
     NS_DECL_NSIFILE
     NS_DECL_NSILOCALFILE
     NS_DECL_NSILOCALFILEMAC
     NS_DECL_NSIHASHABLE
+    NS_DECL_NSICLASSINFO
 
 public:
 
     static void         GlobalInit();
     static void         GlobalShutdown();
     
 private:
                         ~nsLocalFile();
--- a/xpcom/io/nsLocalFileUnix.cpp
+++ b/xpcom/io/nsLocalFileUnix.cpp
@@ -76,16 +76,17 @@
 #include "nsReadableUtils.h"
 #include "nsLocalFile.h"
 #include "nsIComponentManager.h"
 #include "nsXPIDLString.h"
 #include "prproces.h"
 #include "nsIDirectoryEnumerator.h"
 #include "nsISimpleEnumerator.h"
 #include "nsITimelineService.h"
+#include "nsIProgrammingLanguage.h"
 
 #include "nsNativeCharsetUtils.h"
 #include "nsTraceRefcntImpl.h"
 
 // On some platforms file/directory name comparisons need to
 // be case-blind.
 #if defined(VMS)
     #define FILE_STRCMP strcasecmp
@@ -248,20 +249,30 @@ nsLocalFile::nsLocalFile() :
 }
 
 nsLocalFile::nsLocalFile(const nsLocalFile& other)
   : mPath(other.mPath)
   , mHaveCachedStat(PR_FALSE)
 {
 }
 
-NS_IMPL_THREADSAFE_ISUPPORTS3(nsLocalFile,
-                              nsIFile,
-                              nsILocalFile,
-                              nsIHashable)
+NS_IMPL_THREADSAFE_ADDREF(nsLocalFile)
+NS_IMPL_THREADSAFE_RELEASE(nsLocalFile)
+NS_IMPL_QUERY_INTERFACE4_CI(nsLocalFile,
+                            nsILocalFile,
+                            nsIFile,
+                            nsIHashable,
+                            nsIClassInfo)
+NS_IMPL_CI_INTERFACE_GETTER3(nsLocalFile,
+                             nsILocalFile,
+                             nsIFile,
+                             nsIHashable)
+
+NS_DECL_CLASSINFO(nsLocalFile)
+NS_IMPL_THREADSAFE_CI(nsLocalFile)
 
 nsresult
 nsLocalFile::nsLocalFileConstructor(nsISupports *outer, 
                                     const nsIID &aIID,
                                     void **aInstancePtr)
 {
     NS_ENSURE_ARG_POINTER(aInstancePtr);
     NS_ENSURE_NO_AGGREGATION(outer);
--- a/xpcom/io/nsLocalFileUnix.h
+++ b/xpcom/io/nsLocalFileUnix.h
@@ -47,16 +47,17 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
 
 #include "nscore.h"
 #include "nsString.h"
 #include "nsReadableUtils.h"
 #include "nsIHashable.h"
+#include "nsIClassInfoImpl.h"
 
 /** 
  *  we need these for statfs()
  */
 #ifdef HAVE_SYS_STATVFS_H
     #if defined(__osf__) && defined(__DECCXX)
         extern "C" int statvfs(const char *, struct statvfs *);
     #endif
@@ -77,17 +78,18 @@
 #if defined(__FreeBSD__)
     #define HAVE_SYS_STATFS_H
     #define STATFS statfs
     #include <sys/param.h>
     #include <sys/mount.h>
 #endif
 
 class NS_COM nsLocalFile : public nsILocalFile,
-                           public nsIHashable
+                           public nsIHashable,
+                           public nsIClassInfo
 {
 public:
     NS_DEFINE_STATIC_CID_ACCESSOR(NS_LOCAL_FILE_CID)
     
     nsLocalFile();
 
     static NS_METHOD nsLocalFileConstructor(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr);
 
@@ -98,16 +100,19 @@ public:
     NS_DECL_NSIFILE
 
     // nsILocalFile
     NS_DECL_NSILOCALFILE
 
     // nsIHashable
     NS_DECL_NSIHASHABLE
 
+    // nsIClassInfo
+    NS_DECL_NSICLASSINFO
+
 public:
     static void GlobalInit();
     static void GlobalShutdown();
 
 private:
     nsLocalFile(const nsLocalFile& other);
     ~nsLocalFile() {}
 
--- a/xpcom/io/nsLocalFileWin.cpp
+++ b/xpcom/io/nsLocalFileWin.cpp
@@ -42,16 +42,17 @@
 
 
 #include "nsCOMPtr.h"
 #include "nsMemory.h"
 
 #include "nsLocalFile.h"
 #include "nsIDirectoryEnumerator.h"
 #include "nsNativeCharsetUtils.h"
+#include "nsIProgrammingLanguage.h"
 
 #include "nsISimpleEnumerator.h"
 #include "nsIComponentManager.h"
 #include "prtypes.h"
 #include "prio.h"
 #include "private/pprio.h"  // To get PR_ImportFile
 #include "prprf.h"
 #include "prmem.h"
@@ -741,21 +742,32 @@ nsLocalFile::nsLocalFileConstructor(nsIS
     return NS_OK;
 }
 
 
 //-----------------------------------------------------------------------------
 // nsLocalFile::nsISupports
 //-----------------------------------------------------------------------------
 
-NS_IMPL_THREADSAFE_ISUPPORTS4(nsLocalFile,
-                              nsILocalFile,
-                              nsIFile,
-                              nsILocalFileWin,
-                              nsIHashable)
+NS_IMPL_THREADSAFE_ADDREF(nsLocalFile)
+NS_IMPL_THREADSAFE_RELEASE(nsLocalFile)
+NS_IMPL_QUERY_INTERFACE5_CI(nsLocalFile,
+                            nsILocalFile,
+                            nsIFile,
+                            nsILocalFileWin,
+                            nsIHashable,
+                            nsIClassInfo)
+NS_IMPL_CI_INTERFACE_GETTER4(nsLocalFile,
+                             nsILocalFile,
+                             nsIFile,
+                             nsILocalFileWin,
+                             nsIHashable)
+
+NS_DECL_CLASSINFO(nsLocalFile)
+NS_IMPL_THREADSAFE_CI(nsLocalFile)
 
 
 //-----------------------------------------------------------------------------
 // nsLocalFile <private>
 //-----------------------------------------------------------------------------
 
 nsLocalFile::nsLocalFile(const nsLocalFile& other)
   : mDirty(PR_TRUE)
--- a/xpcom/io/nsLocalFileWin.h
+++ b/xpcom/io/nsLocalFileWin.h
@@ -44,31 +44,33 @@
 #include "nscore.h"
 #include "nsError.h"
 #include "nsString.h"
 #include "nsCRT.h"
 #include "nsIFile.h"
 #include "nsIFactory.h"
 #include "nsILocalFileWin.h"
 #include "nsIHashable.h"
+#include "nsIClassInfoImpl.h"
 
 #include "windows.h"
 
 // For older version (<6.0) of the VC Compiler
 #if (_MSC_VER == 1100)
 #include <objbase.h>
 DEFINE_OLEGUID(IID_IPersistFile, 0x0000010BL, 0, 0);
 #endif
 
 #include "shlobj.h"
 
 #include <sys/stat.h>
 
 class nsLocalFile : public nsILocalFileWin,
-                    public nsIHashable
+                    public nsIHashable,
+                    public nsIClassInfo
 {
 public:
     NS_DEFINE_STATIC_CID_ACCESSOR(NS_LOCAL_FILE_CID)
     
     nsLocalFile();
 
     static NS_METHOD nsLocalFileConstructor(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr);
 
@@ -82,16 +84,19 @@ public:
     NS_DECL_NSILOCALFILE
 
     // nsILocalFileWin interface
     NS_DECL_NSILOCALFILEWIN
 
     // nsIHashable interface
     NS_DECL_NSIHASHABLE
 
+    // nsIClassInfo interface
+    NS_DECL_NSICLASSINFO
+
 public:
     static void GlobalInit();
     static void GlobalShutdown();
 
 private:
     nsLocalFile(const nsLocalFile& other);
     ~nsLocalFile() {}
 
--- a/xpcom/io/nsScriptableInputStream.cpp
+++ b/xpcom/io/nsScriptableInputStream.cpp
@@ -1,9 +1,9 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* ***** 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/
  *
@@ -31,72 +31,340 @@
  * 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 "nsScriptableInputStream.h"
+#include "nsIProgrammingLanguage.h"
+#include "nsAutoPtr.h"
 #include "nsMemory.h"
+#include "nsString.h"
+#include "nsIUnicharLineInputStream.h"
+#include "nsIClassInfoImpl.h"
+
+// needed for NS_SWAP macros
+#include "nsIStreamBufferAccess.h"
 
-NS_IMPL_ISUPPORTS1(nsScriptableInputStream, nsIScriptableInputStream)
+NS_IMPL_THREADSAFE_ADDREF(nsScriptableInputStream)
+NS_IMPL_THREADSAFE_RELEASE(nsScriptableInputStream)
+NS_IMPL_QUERY_INTERFACE5_CI(nsScriptableInputStream,
+                            nsIInputStream,
+                            nsIScriptableInputStream,
+                            nsIScriptableIOInputStream,
+                            nsISeekableStream,
+                            nsIMultiplexInputStream)
+NS_IMPL_CI_INTERFACE_GETTER5(nsScriptableInputStream,
+                             nsIInputStream,
+                             nsIScriptableInputStream,
+                             nsIScriptableIOInputStream,
+                             nsISeekableStream,
+                             nsIMultiplexInputStream)
 
 // nsIBaseStream methods
 NS_IMETHODIMP
-nsScriptableInputStream::Close(void) {
-    if (!mInputStream) return NS_ERROR_NOT_INITIALIZED;
+nsScriptableInputStream::Close(void)
+{
+  if (mUnicharInputStream)
+    return mUnicharInputStream->Close();
+
+  if (mInputStream)
     return mInputStream->Close();
+
+  return NS_ERROR_NOT_INITIALIZED;
+}
+
+NS_IMETHODIMP
+nsScriptableInputStream::InitWithStreams(nsIInputStream *aInputStream,
+                                         nsIUnicharInputStream *aCharStream)
+{
+  NS_ENSURE_ARG(aInputStream);
+  mInputStream = aInputStream;
+  mUnicharInputStream = aCharStream;
+  return NS_OK;
 }
 
 // nsIScriptableInputStream methods
 NS_IMETHODIMP
-nsScriptableInputStream::Init(nsIInputStream *aInputStream) {
-    if (!aInputStream) return NS_ERROR_NULL_POINTER;
-    mInputStream = aInputStream;
-    return NS_OK;
+nsScriptableInputStream::Init(nsIInputStream *aInputStream)
+{
+  NS_ENSURE_ARG(aInputStream);
+  mInputStream = aInputStream;
+  mUnicharInputStream = nsnull;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsScriptableInputStream::Available(PRUint32 *aIsAvailable)
+{
+  NS_ENSURE_TRUE(mInputStream, NS_ERROR_NOT_INITIALIZED);
+
+  nsresult rv = mInputStream->Available(aIsAvailable);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // if there is no data left in the stream, check if the character stream
+  // has more text available. This is needed because the unichar stream
+  // will read a buffer from the input stream, leaving it with no remaining
+  // data, yet there is still text to read
+  if (!*aIsAvailable && mUnicharInputStream)
+    *aIsAvailable = mUnicharInputStreamHasMore;
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsScriptableInputStream::Read(PRUint32 aCount, char **_retval)
+{
+  nsresult rv = NS_OK;
+  PRUint32 count = 0;
+  char *buffer = nsnull;
+
+  NS_ENSURE_TRUE(mInputStream, NS_ERROR_NOT_INITIALIZED);
+
+  rv = mInputStream->Available(&count);
+  if (NS_FAILED(rv))
+    return rv;
+
+  count = PR_MIN(count, aCount);
+  buffer = (char*)NS_Alloc(count+1); // make room for '\0'
+  NS_ENSURE_TRUE(buffer, NS_ERROR_OUT_OF_MEMORY);
+
+  PRUint32 amtRead = 0;
+  rv = mInputStream->Read(buffer, count, &amtRead);
+  if (NS_FAILED(rv)) {
+      NS_Free(buffer);
+      return rv;
+  }
+
+  buffer[amtRead] = '\0';
+  *_retval = buffer;
+  return NS_OK;
+}
+
+// nsIInputStream methods
+
+NS_IMETHODIMP
+nsScriptableInputStream::IsNonBlocking(PRBool *aIsNonBlocking)
+{
+  NS_ENSURE_TRUE(mInputStream, NS_ERROR_NOT_INITIALIZED);
+  return mInputStream->IsNonBlocking(aIsNonBlocking);
 }
 
 NS_IMETHODIMP
-nsScriptableInputStream::Available(PRUint32 *_retval) {
-    if (!mInputStream) return NS_ERROR_NOT_INITIALIZED;
-    return mInputStream->Available(_retval);
+nsScriptableInputStream::Read(char* aData,
+                              PRUint32 aCount,
+                              PRUint32 *aReadCount)
+{
+  if (mUnicharInputStream) {
+    // XXXndeakin implement this
+    return NS_OK;
+  }
+
+  if (mInputStream)
+    return mInputStream->Read(aData, aCount, aReadCount);
+
+  return NS_ERROR_NOT_INITIALIZED;
+}
+
+NS_IMETHODIMP
+nsScriptableInputStream::ReadSegments(nsWriteSegmentFun aFn,
+                                      void* aClosure,
+                                      PRUint32 aCount,
+                                      PRUint32 *aReadCount)
+{
+  NS_ENSURE_TRUE(mInputStream, NS_ERROR_NOT_INITIALIZED);
+  return mInputStream->ReadSegments(aFn, aClosure, aCount, aReadCount);
+}
+
+NS_IMETHODIMP
+nsScriptableInputStream::ReadString(PRUint32 aCount, nsAString& aString)
+{
+  if (mUnicharInputStream) {
+    PRUint32 readCount;
+    nsresult rv = mUnicharInputStream->ReadString(aCount, aString, &readCount);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // If less characters are read than requested, then assume that the end
+    // of the file has been reached.
+    // XXXndeakin or it could be because the buffer is full
+    mUnicharInputStreamHasMore = (aCount == readCount);
+    return NS_OK;
+  }
+
+  // just call Read and convert to UTF-16
+  nsXPIDLCString cstr;
+  nsresult rv = Read(aCount, getter_Copies(cstr));
+  NS_ENSURE_SUCCESS(rv, rv);
+  aString.Assign(NS_ConvertASCIItoUTF16(cstr));
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsScriptableInputStream::ReadLine(nsAString& aLine)
+{
+  nsCOMPtr<nsIUnicharLineInputStream> cstream = do_QueryInterface(mUnicharInputStream);
+  NS_ENSURE_TRUE(cstream, NS_ERROR_NOT_AVAILABLE);
+
+  return cstream->ReadLine(aLine, &mUnicharInputStreamHasMore);
+}
+
+NS_IMETHODIMP
+nsScriptableInputStream::ReadBoolean(PRBool* aBoolean)
+{
+  PRUint8 byteResult;
+  nsresult rv = Read8(&byteResult);
+  *aBoolean = byteResult;
+  return rv;
+}
+
+NS_IMETHODIMP
+nsScriptableInputStream::Read8(PRUint8* aVal)
+{
+  return ReadFully(sizeof *aVal, NS_REINTERPRET_CAST(char*, aVal));
 }
 
 NS_IMETHODIMP
-nsScriptableInputStream::Read(PRUint32 aCount, char **_retval) {
-    nsresult rv = NS_OK;
-    PRUint32 count = 0;
-    char *buffer = nsnull;
+nsScriptableInputStream::Read16(PRUint16* aVal)
+{
+  nsresult rv = ReadFully(sizeof *aVal, NS_REINTERPRET_CAST(char*, aVal));
+  NS_ENSURE_SUCCESS(rv, rv);
+  *aVal = NS_SWAP16(*aVal);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsScriptableInputStream::Read32(PRUint32* aVal)
+{
+  nsresult rv = ReadFully(sizeof *aVal, NS_REINTERPRET_CAST(char*, aVal));
+  NS_ENSURE_SUCCESS(rv, rv);
+  *aVal = NS_SWAP32(*aVal);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsScriptableInputStream::ReadFloat(float* aFloat)
+{
+  NS_ASSERTION(sizeof(float) == sizeof (PRUint32),
+               "False assumption about sizeof(float)");
+  return Read32(NS_REINTERPRET_CAST(PRUint32*, aFloat));
+}
 
-    if (!mInputStream) return NS_ERROR_NOT_INITIALIZED;
+NS_IMETHODIMP
+nsScriptableInputStream::ReadDouble(double* aDouble)
+{
+  NS_ASSERTION(sizeof(double) == sizeof(PRUint64),
+               "False assumption about sizeof(double)");
+
+  nsresult rv = ReadFully(sizeof(double), NS_REINTERPRET_CAST(char*, aDouble));
+
+  NS_ENSURE_SUCCESS(rv, rv);
+  PRUint64 i = NS_SWAP64(*NS_REINTERPRET_CAST(PRUint64*, aDouble));
+  *aDouble = *NS_REINTERPRET_CAST(double*, &i);
+  return NS_OK;
+}
 
-    rv = mInputStream->Available(&count);
-    if (NS_FAILED(rv)) return rv;
+NS_IMETHODIMP
+nsScriptableInputStream::ReadByteArray(PRUint32 aCount, PRUint8 **aBytes)
+{
+  char* s = NS_REINTERPRET_CAST(char*, NS_Alloc(aCount));
+  NS_ENSURE_TRUE(s, NS_ERROR_OUT_OF_MEMORY);
+
+  PRUint32 bytesRead;
+  nsresult rv = mInputStream->Read(s, aCount, &bytesRead);
+  if (NS_FAILED(rv)) {
+    NS_Free(s);
+    return rv;
+  }
+  if (bytesRead != aCount) {
+    NS_Free(s);
+    return NS_ERROR_FAILURE;
+  }
+
+  *aBytes = (PRUint8 *)s;
+  return NS_OK;
+}
 
-    count = PR_MIN(count, aCount);
-    buffer = (char*)nsMemory::Alloc(count+1); // make room for '\0'
-    if (!buffer) return NS_ERROR_OUT_OF_MEMORY;
+nsresult
+nsScriptableInputStream::ReadFully(PRUint32 aCount, char* aBuf)
+{
+  PRUint32 bytesRead;
+  nsresult rv = Read(aBuf, aCount, &bytesRead);
+  NS_ENSURE_SUCCESS(rv, rv);
+  return (bytesRead != aCount) ? NS_ERROR_FAILURE : NS_OK;
+}
+
+// nsISeekableStream
+NS_IMETHODIMP
+nsScriptableInputStream::Seek(PRInt32 aWhence, PRInt64 aOffset)
+{
+  nsCOMPtr<nsISeekableStream> cstream = do_QueryInterface(mInputStream);
+  NS_ENSURE_TRUE(cstream, NS_ERROR_NOT_AVAILABLE);
+  return cstream->Seek(aWhence, aOffset);
+}
+
+NS_IMETHODIMP
+nsScriptableInputStream::Tell(PRInt64* aOffset)
+{
+  nsCOMPtr<nsISeekableStream> cstream = do_QueryInterface(mInputStream);
+  NS_ENSURE_TRUE(cstream, NS_ERROR_NOT_AVAILABLE);
+  return cstream->Tell(aOffset);
+}
+
+NS_IMETHODIMP
+nsScriptableInputStream::SetEOF()
+{
+  nsCOMPtr<nsISeekableStream> cstream = do_QueryInterface(mInputStream);
+  NS_ENSURE_TRUE(cstream, NS_ERROR_NOT_AVAILABLE);
+  return cstream->SetEOF();
+}
 
-    PRUint32 amtRead = 0;
-    rv = mInputStream->Read(buffer, count, &amtRead);
-    if (NS_FAILED(rv)) {
-        nsMemory::Free(buffer);
-        return rv;
-    }
+// nsIMultiplexInputStream
+NS_IMETHODIMP
+nsScriptableInputStream::GetCount(PRUint32* aCount)
+{
+  nsCOMPtr<nsIMultiplexInputStream> cstream = do_QueryInterface(mInputStream);
+  NS_ENSURE_TRUE(cstream, NS_ERROR_NOT_AVAILABLE);
+  return cstream->GetCount(aCount);
+}
+
+NS_IMETHODIMP
+nsScriptableInputStream::GetStream(PRUint32 aIndex, nsIInputStream** aStream)
+{
+  nsCOMPtr<nsIMultiplexInputStream> cstream = do_QueryInterface(mInputStream);
+  NS_ENSURE_TRUE(cstream, NS_ERROR_NOT_AVAILABLE);
+  return cstream->GetStream(aIndex, aStream);
+}
 
-    buffer[amtRead] = '\0';
-    *_retval = buffer;
-    return NS_OK;
+NS_IMETHODIMP
+nsScriptableInputStream::AppendStream(nsIInputStream* aStream)
+{
+  nsCOMPtr<nsIMultiplexInputStream> cstream = do_QueryInterface(mInputStream);
+  NS_ENSURE_TRUE(cstream, NS_ERROR_NOT_AVAILABLE);
+  return cstream->AppendStream(aStream);
+}
+
+NS_IMETHODIMP
+nsScriptableInputStream::InsertStream(nsIInputStream* aStream, PRUint32 aIndex)
+{
+  nsCOMPtr<nsIMultiplexInputStream> cstream = do_QueryInterface(mInputStream);
+  NS_ENSURE_TRUE(cstream, NS_ERROR_NOT_AVAILABLE);
+  return cstream->InsertStream(aStream, aIndex);
+}
+
+NS_IMETHODIMP
+nsScriptableInputStream::RemoveStream(PRUint32 aIndex)
+{
+  nsCOMPtr<nsIMultiplexInputStream> cstream = do_QueryInterface(mInputStream);
+  NS_ENSURE_TRUE(cstream, NS_ERROR_NOT_AVAILABLE);
+  return cstream->RemoveStream(aIndex);
 }
 
 NS_METHOD
-nsScriptableInputStream::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) {
-    if (aOuter) return NS_ERROR_NO_AGGREGATION;
-
-    nsScriptableInputStream *sis = new nsScriptableInputStream();
-    if (!sis) return NS_ERROR_OUT_OF_MEMORY;
+nsScriptableInputStream::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
+{
+  if (aOuter) return NS_ERROR_NO_AGGREGATION;
 
-    NS_ADDREF(sis);
-    nsresult rv = sis->QueryInterface(aIID, aResult);
-    NS_RELEASE(sis);
-    return rv;
+  nsRefPtr<nsScriptableInputStream> sis = new nsScriptableInputStream();
+  NS_ENSURE_TRUE(sis, NS_ERROR_OUT_OF_MEMORY);
+  return sis->QueryInterface(aIID, aResult);
 }
--- a/xpcom/io/nsScriptableInputStream.h
+++ b/xpcom/io/nsScriptableInputStream.h
@@ -34,38 +34,71 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef ___nsscriptableinputstream___h_
 #define ___nsscriptableinputstream___h_
 
 #include "nsIScriptableInputStream.h"
+#include "nsIScriptableStreams.h"
 #include "nsIInputStream.h"
+#include "nsISeekableStream.h"
+#include "nsIMultiplexInputStream.h"
+#include "nsIUnicharInputStream.h"
 #include "nsCOMPtr.h"
 
 #define NS_SCRIPTABLEINPUTSTREAM_CID        \
 { 0x7225c040, 0xa9bf, 0x11d3, { 0xa1, 0x97, 0x0, 0x50, 0x4, 0x1c, 0xaf, 0x44 } }
 
 #define NS_SCRIPTABLEINPUTSTREAM_CONTRACTID "@mozilla.org/scriptableinputstream;1"
 #define NS_SCRIPTABLEINPUTSTREAM_CLASSNAME "Scriptable Input Stream"
 
-class nsScriptableInputStream : public nsIScriptableInputStream {
+class nsScriptableInputStream : public nsIScriptableInputStream,
+                                public nsIScriptableIOInputStream,
+                                public nsISeekableStream,
+                                public nsIMultiplexInputStream
+{
 public:
     // nsISupports methods
     NS_DECL_ISUPPORTS
 
-    // nsIScriptableInputStream methods
-    NS_DECL_NSISCRIPTABLEINPUTSTREAM
+    // nsIScriptableIOInputStream methods
+    NS_DECL_NSISCRIPTABLEIOINPUTSTREAM
+
+    // nsISeekableStream methods
+    NS_DECL_NSISEEKABLESTREAM
+
+    // nsIMultiplexInputStream methods
+    NS_DECL_NSIMULTIPLEXINPUTSTREAM
+
+    // nsIInputStream and nsIScriptableInputStream methods
+    NS_IMETHOD Available(PRUint32 *aAvailable);
+    NS_IMETHOD Close();
+    NS_IMETHOD IsNonBlocking(PRBool *aIsNonBlocking);
+    NS_IMETHOD Read(char* aData,
+                    PRUint32 aCount,
+                    PRUint32 *aReadCount);
+    NS_IMETHOD ReadSegments(nsWriteSegmentFun aFn,
+                            void* aClosure,
+                            PRUint32 aCount,
+                            PRUint32 *aReadCount);
+    NS_IMETHOD Init(nsIInputStream* aInputStream);
+    NS_IMETHOD Read(PRUint32 aCount, char** aData);
 
     // nsScriptableInputStream methods
-    nsScriptableInputStream() {}
+    nsScriptableInputStream() :
+      mUnicharInputStreamHasMore(PR_TRUE) {}
 
     static NS_METHOD
     Create(nsISupports *aOuter, REFNSIID aIID, void **aResult);
 
 private:
     ~nsScriptableInputStream() {}
 
+    nsresult ReadFully(PRUint32 aCount, char* aBuf);
+
+    PRBool mUnicharInputStreamHasMore;
     nsCOMPtr<nsIInputStream> mInputStream;
+    nsCOMPtr<nsIUnicharInputStream> mUnicharInputStream;
 };
 
 #endif // ___nsscriptableinputstream___h_
new file mode 100644
--- /dev/null
+++ b/xpcom/io/nsScriptableOutputStream.cpp
@@ -0,0 +1,261 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** 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 mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * 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 ***** */
+
+#include "nsScriptableOutputStream.h"
+#include "nsIProgrammingLanguage.h"
+#include "nsAutoPtr.h"
+#include "nsMemory.h"
+#include "nsString.h"
+#include "nsIClassInfoImpl.h"
+
+// needed for NS_SWAP macros
+#include "nsIStreamBufferAccess.h"
+
+NS_IMPL_THREADSAFE_ADDREF(nsScriptableOutputStream)
+NS_IMPL_THREADSAFE_RELEASE(nsScriptableOutputStream)
+NS_IMPL_QUERY_INTERFACE3_CI(nsScriptableOutputStream,
+                            nsIOutputStream,
+                            nsIScriptableIOOutputStream,
+                            nsISeekableStream)
+NS_IMPL_CI_INTERFACE_GETTER3(nsScriptableOutputStream,
+                             nsIOutputStream,
+                             nsIScriptableIOOutputStream,
+                             nsISeekableStream)
+
+// nsIBaseStream methods
+NS_IMETHODIMP
+nsScriptableOutputStream::Close(void)
+{
+  if (mUnicharOutputStream)
+    return mUnicharOutputStream->Close();
+
+  if (mOutputStream)
+    return mOutputStream->Close();
+
+  return NS_ERROR_NOT_INITIALIZED;
+}
+
+NS_IMETHODIMP
+nsScriptableOutputStream::Flush(void)
+{
+  if (mUnicharOutputStream)
+    return mUnicharOutputStream->Flush();
+
+  if (mOutputStream)
+    return mOutputStream->Flush();
+
+  return NS_ERROR_NOT_INITIALIZED;
+}
+
+// nsIScriptableIOOutputStream methods
+
+NS_IMETHODIMP
+nsScriptableOutputStream::InitWithStreams(nsIOutputStream* aOutputStream,
+                                          nsIUnicharOutputStream *aCharStream)
+{
+  NS_ENSURE_ARG(aOutputStream);
+
+  mOutputStream = aOutputStream;
+  mUnicharOutputStream = aCharStream;
+  return NS_OK;
+}
+
+// nsIOutputStream methods
+
+NS_IMETHODIMP
+nsScriptableOutputStream::IsNonBlocking(PRBool *aIsNonBlocking)
+{
+  if (mOutputStream)
+    return mOutputStream->IsNonBlocking(aIsNonBlocking);
+
+  if (mUnicharOutputStream) {
+    *aIsNonBlocking = PR_FALSE;
+    return NS_OK;
+  }
+
+  return NS_ERROR_NOT_INITIALIZED;
+}
+
+NS_IMETHODIMP
+nsScriptableOutputStream::Write(const char* aBuffer, PRUint32 aCount, PRUint32 *aWriteCount)
+{
+  if (mUnicharOutputStream) {
+    nsAutoString str(NS_ConvertASCIItoUTF16(aBuffer, aCount));
+    PRBool ok;
+    mUnicharOutputStream->WriteString(str, &ok);
+    *aWriteCount = ok ? aCount : 0;
+    return NS_OK;
+  }
+
+  if (mOutputStream)
+    return mOutputStream->Write(aBuffer, aCount, aWriteCount);
+
+  return NS_ERROR_NOT_INITIALIZED;
+}
+
+NS_IMETHODIMP
+nsScriptableOutputStream::WriteFrom(nsIInputStream *aStream, PRUint32 aCount, PRUint32 *aWriteCount)
+{
+  if (mOutputStream)
+    return mOutputStream->WriteFrom(aStream, aCount, aWriteCount);
+
+  return NS_ERROR_NOT_INITIALIZED;
+}
+
+NS_IMETHODIMP
+nsScriptableOutputStream::WriteSegments(nsReadSegmentFun aFn,
+                                        void* aClosure,
+                                        PRUint32 aCount,
+                                        PRUint32 *aReadCount)
+{
+  NS_ENSURE_TRUE(mOutputStream, NS_ERROR_NOT_INITIALIZED);
+  return mOutputStream->WriteSegments(aFn, aClosure, aCount, aReadCount);
+}
+
+NS_IMETHODIMP
+nsScriptableOutputStream::WriteString(const nsAString& aString, PRBool *aOK)
+{
+  if (mUnicharOutputStream)
+    return mUnicharOutputStream->WriteString(aString, aOK);
+
+  if (!mOutputStream)
+    return NS_ERROR_NOT_INITIALIZED;
+
+  // just convert to ASCII and call Write
+  nsCAutoString cstr = NS_LossyConvertUTF16toASCII(aString);
+  PRUint32 count;
+  nsresult rv = mOutputStream->Write(cstr.get(), (PRUint32)cstr.Length(), &count);
+  NS_ENSURE_SUCCESS(rv, rv);
+  *aOK = (count == cstr.Length());
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsScriptableOutputStream::WriteBoolean(PRBool aBoolean)
+{
+  return Write8(aBoolean);
+}
+
+NS_IMETHODIMP
+nsScriptableOutputStream::Write8(PRUint8 aByte)
+{
+  return WriteFully((const char *)&aByte, sizeof aByte);
+}
+
+NS_IMETHODIMP
+nsScriptableOutputStream::Write16(PRUint16 a16)
+{
+  a16 = NS_SWAP16(a16);
+  return WriteFully((const char *)&a16, sizeof a16);
+}
+
+NS_IMETHODIMP
+nsScriptableOutputStream::Write32(PRUint32 a32)
+{
+  a32 = NS_SWAP32(a32);
+  return WriteFully((const char *)&a32, sizeof a32);
+}
+
+NS_IMETHODIMP
+nsScriptableOutputStream::WriteFloat(float aFloat)
+{
+  NS_ASSERTION(sizeof(float) == sizeof (PRUint32),
+               "False assumption about sizeof(float)");
+  return Write32(*NS_REINTERPRET_CAST(PRUint32*, &aFloat));
+}
+
+NS_IMETHODIMP
+nsScriptableOutputStream::WriteDouble(double aDouble)
+{
+  NS_ASSERTION(sizeof(double) == sizeof(PRUint64),
+               "False assumption about sizeof(double)");
+
+  PRUint64 val = NS_SWAP64(*NS_REINTERPRET_CAST(PRUint64*, &aDouble));
+  return WriteFully(NS_REINTERPRET_CAST(char*, &val), sizeof val);
+}
+
+NS_IMETHODIMP
+nsScriptableOutputStream::WriteByteArray(PRUint8 *aBytes, PRUint32 aCount)
+{
+  return WriteFully((char *)aBytes, aCount);
+}
+
+nsresult
+nsScriptableOutputStream::WriteFully(const char *aBuf, PRUint32 aCount)
+{
+  NS_ENSURE_TRUE(mOutputStream, NS_ERROR_NOT_INITIALIZED);
+
+  PRUint32 bytesWritten;
+  nsresult rv = mOutputStream->Write(aBuf, aCount, &bytesWritten);
+  NS_ENSURE_SUCCESS(rv, rv);
+  return (bytesWritten != aCount) ? NS_ERROR_FAILURE : NS_OK;
+}
+
+// nsISeekableStream
+NS_IMETHODIMP
+nsScriptableOutputStream::Seek(PRInt32 aWhence, PRInt64 aOffset)
+{
+  nsCOMPtr<nsISeekableStream> cstream = do_QueryInterface(mOutputStream);
+  NS_ENSURE_TRUE(cstream, NS_ERROR_NOT_AVAILABLE);
+  return cstream->Seek(aWhence, aOffset);
+}
+
+NS_IMETHODIMP
+nsScriptableOutputStream::Tell(PRInt64* aOffset)
+{
+  nsCOMPtr<nsISeekableStream> cstream = do_QueryInterface(mOutputStream);
+  NS_ENSURE_TRUE(cstream, NS_ERROR_NOT_AVAILABLE);
+  return cstream->Tell(aOffset);
+}
+
+NS_IMETHODIMP
+nsScriptableOutputStream::SetEOF()
+{
+  nsCOMPtr<nsISeekableStream> cstream = do_QueryInterface(mOutputStream);
+  NS_ENSURE_TRUE(cstream, NS_ERROR_NOT_AVAILABLE);
+  return cstream->SetEOF();
+}
+
+NS_METHOD
+nsScriptableOutputStream::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
+{
+  if (aOuter) return NS_ERROR_NO_AGGREGATION;
+
+  nsRefPtr<nsScriptableOutputStream> sos = new nsScriptableOutputStream();
+  NS_ENSURE_TRUE(sos, NS_ERROR_OUT_OF_MEMORY);
+  return sos->QueryInterface(aIID, aResult);
+}
new file mode 100644
--- /dev/null
+++ b/xpcom/io/nsScriptableOutputStream.h
@@ -0,0 +1,85 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** 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 mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * 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 ***** */
+
+#ifndef ___nsscriptableoutputstream___h_
+#define ___nsscriptableoutputstream___h_
+
+#include "nsIScriptableStreams.h"
+#include "nsIOutputStream.h"
+#include "nsISeekableStream.h"
+#include "nsIUnicharOutputStream.h"
+#include "nsCOMPtr.h"
+
+#define NS_SCRIPTABLEOUTPUTSTREAM_CID        \
+{ 0xaea1cfe2, 0xf727, 0x4b94, { 0x93, 0xff, 0x41, 0x8d, 0x96, 0x87, 0x94, 0xd1 } }
+
+#define NS_SCRIPTABLEOUTPUTSTREAM_CONTRACTID "@mozilla.org/scriptableoutputstream;1"
+#define NS_SCRIPTABLEOUTPUTSTREAM_CLASSNAME "Scriptable Output Stream"
+
+class nsScriptableOutputStream : public nsIScriptableIOOutputStream,
+                                 public nsISeekableStream,
+                                 public nsIOutputStream
+{
+public:
+  // nsISupports methods
+  NS_DECL_ISUPPORTS
+
+  // nsIOutputStream methods
+  NS_DECL_NSIOUTPUTSTREAM
+
+  // nsIScriptableIOOutputStream methods
+  NS_DECL_NSISCRIPTABLEIOOUTPUTSTREAM
+
+  // nsISeekableStream methods
+  NS_DECL_NSISEEKABLESTREAM
+
+  // nsScriptableOutputStream methods
+  nsScriptableOutputStream() {}
+
+  static NS_METHOD
+  Create(nsISupports *aOuter, REFNSIID aIID, void **aResult);
+
+private:
+  ~nsScriptableOutputStream() {}
+
+  nsresult WriteFully(const char *aBuf, PRUint32 aCount);
+
+  nsCOMPtr<nsIOutputStream> mOutputStream;
+  nsCOMPtr<nsIUnicharOutputStream> mUnicharOutputStream;
+};
+
+#endif // ___nsscriptableoutputstream___h_