Bug 730765 - Media cache shouldn't be used when loading from blob: urls. r=biesi
authorPaul Adenot <paul@paul.cx>
Tue, 21 Aug 2012 16:00:23 -0700
changeset 108448 5583ccdfea183a42da2c6fe071ab8352f53f8d79
parent 108447 56cec1a9c52a6cb69e16730ae22bedd3a6c9a28f
child 108449 581cdaf67a25a491c3471ab8c72c983ac42865d8
child 108630 68fc1a8f97d0acbe604c68147c0a20cff1e4a68d
push id1490
push userakeybl@mozilla.com
push dateMon, 08 Oct 2012 18:29:50 +0000
treeherdermozilla-beta@f335e7dacdc1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbiesi
bugs730765
milestone17.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 730765 - Media cache shouldn't be used when loading from blob: urls. r=biesi
content/base/public/Makefile.in
content/base/public/nsBlobProtocolHandler.h
content/base/src/nsBlobProtocolHandler.cpp
content/base/src/nsBlobProtocolHandler.h
content/media/MediaResource.cpp
netwerk/base/public/nsIFileStreams.idl
netwerk/base/src/nsFileStreams.cpp
netwerk/base/src/nsFileStreams.h
xpcom/io/nsMultiplexInputStream.cpp
--- a/content/base/public/Makefile.in
+++ b/content/base/public/Makefile.in
@@ -37,16 +37,17 @@ nsContentCID.h \
 nsCopySupport.h \
 nsContentCreatorFunctions.h \
 nsDOMFile.h \
 nsLineBreaker.h \
 nsReferencedElement.h \
 nsTreeSanitizer.h \
 nsXMLNameSpaceMap.h \
 nsIXFormsUtilityService.h \
+nsBlobProtocolHandler.h \
 $(NULL)
 
 EXPORTS_NAMESPACES = mozilla/dom mozilla
 
 EXPORTS_mozilla/dom = \
 		DirectionalityUtils.h \
 		Element.h \
 		FragmentOrElement.h \
new file mode 100644
--- /dev/null
+++ b/content/base/public/nsBlobProtocolHandler.h
@@ -0,0 +1,51 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsBlobProtocolHandler_h
+#define nsBlobProtocolHandler_h
+
+#include "nsIProtocolHandler.h"
+#include "nsIURI.h"
+#include "nsCOMPtr.h"
+
+#define BLOBURI_SCHEME "blob"
+
+class nsIDOMBlob;
+class nsIPrincipal;
+class nsIInputStream;
+
+inline bool IsBlobURI(nsIURI* aUri)
+{
+  bool isBlob;
+  return NS_SUCCEEDED(aUri->SchemeIs(BLOBURI_SCHEME, &isBlob)) && isBlob;
+}
+
+extern nsresult
+NS_GetStreamForBlobURI(nsIURI* aURI, nsIInputStream** aStream);
+
+class nsBlobProtocolHandler : public nsIProtocolHandler
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  // nsIProtocolHandler methods:
+  NS_DECL_NSIPROTOCOLHANDLER
+
+  // nsBlobProtocolHandler methods:
+  nsBlobProtocolHandler() {}
+  virtual ~nsBlobProtocolHandler() {}
+
+  // Methods for managing uri->file mapping
+  static void AddFileDataEntry(nsACString& aUri,
+                               nsIDOMBlob* aFile,
+                               nsIPrincipal* aPrincipal);
+  static void RemoveFileDataEntry(nsACString& aUri);
+  static nsIPrincipal* GetFileDataEntryPrincipal(nsACString& aUri);
+};
+
+#define NS_BLOBPROTOCOLHANDLER_CID \
+{ 0xb43964aa, 0xa078, 0x44b2, \
+  { 0xb0, 0x6b, 0xfd, 0x4d, 0x1b, 0x17, 0x2e, 0x66 } }
+
+#endif /* nsBlobProtocolHandler_h */
--- a/content/base/src/nsBlobProtocolHandler.cpp
+++ b/content/base/src/nsBlobProtocolHandler.cpp
@@ -158,18 +158,18 @@ nsBlobProtocolHandler::NewChannel(nsIURI
 #endif
 
   nsCOMPtr<nsIInputStream> stream;
   nsresult rv = info->mFile->GetInternalStream(getter_AddRefs(stream));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIChannel> channel;
   rv = NS_NewInputStreamChannel(getter_AddRefs(channel),
-				uri,
-				stream);
+                                uri,
+                                stream);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsISupports> owner = do_QueryInterface(info->mPrincipal);
 
   nsAutoString type;
   rv = info->mFile->GetType(type);
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -184,8 +184,28 @@ nsBlobProtocolHandler::NewChannel(nsIURI
 NS_IMETHODIMP 
 nsBlobProtocolHandler::AllowPort(PRInt32 port, const char *scheme,
                                      bool *_retval)
 {
     // don't override anything.  
     *_retval = false;
     return NS_OK;
 }
+
+nsresult
+NS_GetStreamForBlobURI(nsIURI* aURI, nsIInputStream** aStream)
+{
+  NS_ASSERTION(IsBlobURI(aURI), "Only call this with blob URIs");
+
+  *aStream = nullptr;
+
+  nsCString spec;
+  aURI->GetSpec(spec);
+
+  FileDataInfo* info =
+    GetFileDataInfo(spec);
+
+  if (!info) {
+    return NS_ERROR_DOM_BAD_URI;
+  }
+
+  return info->mFile->GetInternalStream(aStream);
+}
--- a/content/base/src/nsBlobProtocolHandler.h
+++ b/content/base/src/nsBlobProtocolHandler.h
@@ -1,40 +1,51 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsBlobProtocolHandler_h
 #define nsBlobProtocolHandler_h
 
 #include "nsIProtocolHandler.h"
+#include "nsIURI.h"
+#include "nsCOMPtr.h"
 
 #define BLOBURI_SCHEME "blob"
 
 class nsIDOMBlob;
 class nsIPrincipal;
+class nsIInputStream;
+
+inline bool IsBlobURI(nsIURI* aUri)
+{
+  bool isBlob;
+  return NS_SUCCEEDED(aUri->SchemeIs(BLOBURI_SCHEME, &isBlob)) && isBlob;
+}
+
+extern nsresult
+NS_GetStreamForBlobURI(nsIURI* aURI, nsIInputStream** aStream);
 
 class nsBlobProtocolHandler : public nsIProtocolHandler
 {
 public:
   NS_DECL_ISUPPORTS
 
   // nsIProtocolHandler methods:
   NS_DECL_NSIPROTOCOLHANDLER
 
   // nsBlobProtocolHandler methods:
   nsBlobProtocolHandler() {}
   virtual ~nsBlobProtocolHandler() {}
 
   // Methods for managing uri->file mapping
   static void AddFileDataEntry(nsACString& aUri,
-			       nsIDOMBlob* aFile,
+                               nsIDOMBlob* aFile,
                                nsIPrincipal* aPrincipal);
   static void RemoveFileDataEntry(nsACString& aUri);
   static nsIPrincipal* GetFileDataEntryPrincipal(nsACString& aUri);
-  
 };
 
 #define NS_BLOBPROTOCOLHANDLER_CID \
 { 0xb43964aa, 0xa078, 0x44b2, \
   { 0xb0, 0x6b, 0xfd, 0x4d, 0x1b, 0x17, 0x2e, 0x66 } }
 
 #endif /* nsBlobProtocolHandler_h */
--- a/content/media/MediaResource.cpp
+++ b/content/media/MediaResource.cpp
@@ -23,16 +23,17 @@
 #include "nsCrossSiteListenerProxy.h"
 #include "nsHTMLMediaElement.h"
 #include "nsError.h"
 #include "nsICachingChannel.h"
 #include "nsURILoader.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 #include "mozilla/Util.h" // for DebugOnly
 #include "nsContentUtils.h"
+#include "nsBlobProtocolHandler.h"
 
 static const PRUint32 HTTP_OK_CODE = 200;
 static const PRUint32 HTTP_PARTIAL_RESPONSE_CODE = 206;
 
 using namespace mozilla;
 
 ChannelMediaResource::ChannelMediaResource(nsMediaDecoder* aDecoder,
     nsIChannel* aChannel, nsIURI* aURI)
@@ -1033,24 +1034,25 @@ nsresult FileMediaResource::Open(nsIStre
   }
 
   nsresult rv = NS_OK;
   if (aStreamListener) {
     // The channel is already open. We need a synchronous stream that
     // implements nsISeekableStream, so we have to find the underlying
     // file and reopen it
     nsCOMPtr<nsIFileChannel> fc(do_QueryInterface(mChannel));
-    if (!fc)
-      return NS_ERROR_UNEXPECTED;
+    if (fc) {
+      nsCOMPtr<nsIFile> file;
+      rv = fc->GetFile(getter_AddRefs(file));
+      NS_ENSURE_SUCCESS(rv, rv);
 
-    nsCOMPtr<nsIFile> file;
-    rv = fc->GetFile(getter_AddRefs(file));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = NS_NewLocalFileInputStream(getter_AddRefs(mInput), file);
+      rv = NS_NewLocalFileInputStream(getter_AddRefs(mInput), file);
+    } else if (IsBlobURI(mURI)) {
+      rv = NS_GetStreamForBlobURI(mURI, getter_AddRefs(mInput));
+    }
   } else {
     // Ensure that we never load a local file from some page on a
     // web server.
     nsHTMLMediaElement* element = mDecoder->GetMediaElement();
     NS_ENSURE_TRUE(element, NS_ERROR_FAILURE);
 
     rv = nsContentUtils::GetSecurityManager()->
            CheckLoadURIWithPrincipal(element->NodePrincipal(),
@@ -1196,27 +1198,27 @@ PRInt64 FileMediaResource::Tell()
   mSeekable->Tell(&offset);
   return offset;
 }
 
 MediaResource*
 MediaResource::Create(nsMediaDecoder* aDecoder, nsIChannel* aChannel)
 {
   NS_ASSERTION(NS_IsMainThread(),
-	             "MediaResource::Open called on non-main thread");
+               "MediaResource::Open called on non-main thread");
 
   // If the channel was redirected, we want the post-redirect URI;
   // but if the URI scheme was expanded, say from chrome: to jar:file:,
   // we want the original URI.
   nsCOMPtr<nsIURI> uri;
   nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
   NS_ENSURE_SUCCESS(rv, nullptr);
 
   nsCOMPtr<nsIFileChannel> fc = do_QueryInterface(aChannel);
-  if (fc) {
+  if (fc || IsBlobURI(uri)) {
     return new FileMediaResource(aDecoder, aChannel, uri);
   }
   return new ChannelMediaResource(aDecoder, aChannel, uri);
 }
 
 void MediaResource::MoveLoadsToBackground() {
   NS_ASSERTION(!mLoadInBackground, "Why are you calling this more than once?");
   mLoadInBackground = true;
--- a/netwerk/base/public/nsIFileStreams.idl
+++ b/netwerk/base/public/nsIFileStreams.idl
@@ -39,19 +39,21 @@ interface nsIFileInputStream : nsIInputS
 
     /**
      * If this is set, the file will close automatically when the end of the
      * file is reached.
      */
     const long CLOSE_ON_EOF = 1<<2;
 
     /**
-     * If this is set, the file will be reopened whenever Seek(0) occurs.  If
-     * the file is already open and the seek occurs, it will happen naturally.
-     * (The file will only be reopened if it is closed for some reason.)
+     * If this is set, the file will be reopened whenever we reach the start of
+     * the file, either by doing a Seek(0, NS_SEEK_CUR), or by doing a relative
+     * seek that happen to reach the beginning of the file. If the file is
+     * already open and the seek occurs, it will happen naturally.  (The file
+     * will only be reopened if it is closed for some reason.)
      */
     const long REOPEN_ON_REWIND = 1<<3;
 
     /**
      * If this is set, the file will be opened (i.e., a call to
      * PR_Open done) only when we do an actual operation on the stream,
      * or more specifically, when one of the following is called:
      *   - Seek
--- a/netwerk/base/src/nsFileStreams.cpp
+++ b/netwerk/base/src/nsFileStreams.cpp
@@ -405,16 +405,24 @@ nsFileInputStream::Init(nsIFile* aFile, 
     mPerm = aPerm;
 
     return Open(aFile, aIOFlags, aPerm);
 }
 
 NS_IMETHODIMP
 nsFileInputStream::Close()
 {
+    // Get the cache position at the time the file was close. This allows
+    // NS_SEEK_CUR on a closed file that has been opened with
+    // REOPEN_ON_REWIND.
+    if (mBehaviorFlags & REOPEN_ON_REWIND) {
+        // Get actual position. Not one modified by subclasses
+        nsFileStreamBase::Tell(&mCachedPosition);
+    }
+
     // null out mLineBuffer in case Close() is called again after failing
     PR_FREEIF(mLineBuffer);
     nsresult rv = nsFileStreamBase::Close();
     if (NS_FAILED(rv)) return rv;
     if (mFile && (mBehaviorFlags & DELETE_ON_CLOSE)) {
         rv = mFile->Remove(false);
         NS_ASSERTION(NS_SUCCEEDED(rv), "failed to delete file");
         // If we don't need to save the file for reopening, free it up
@@ -456,28 +464,51 @@ NS_IMETHODIMP
 nsFileInputStream::Seek(PRInt32 aWhence, PRInt64 aOffset)
 {
     nsresult rv = DoPendingOpen();
     NS_ENSURE_SUCCESS(rv, rv);
 
     PR_FREEIF(mLineBuffer); // this invalidates the line buffer
     if (!mFD) {
         if (mBehaviorFlags & REOPEN_ON_REWIND) {
-            nsresult rv = Reopen();
-            if (NS_FAILED(rv)) {
-                return rv;
+            rv = Open(mFile, mIOFlags, mPerm);
+            NS_ENSURE_SUCCESS(rv, rv);
+
+            // If the file was closed, and we do a relative seek, use the
+            // position we cached when we closed the file to seek to the right
+            // location.
+            if (aWhence == NS_SEEK_CUR) {
+                aWhence = NS_SEEK_SET;
+                aOffset += mCachedPosition;
             }
         } else {
             return NS_BASE_STREAM_CLOSED;
         }
     }
 
     return nsFileStreamBase::Seek(aWhence, aOffset);
 }
 
+NS_IMETHODIMP
+nsFileInputStream::Tell(PRInt64 *aResult)
+{
+    return nsFileStreamBase::Tell(aResult);
+}
+
+NS_IMETHODIMP
+nsFileInputStream::Available(PRUint64 *aResult)
+{
+    //if (mFD == nullptr && mBehaviorFlags & REOPEN_ON_REWIND) {
+        //*aResult = 0;
+        //return NS_OK;
+    //}
+
+    return nsFileStreamBase::Available(aResult);
+}
+
 bool
 nsFileInputStream::Read(const IPC::Message *aMsg, void **aIter)
 {
     using IPC::ReadParam;
 
     nsCString path;
     bool followLinks;
     PRInt32 flags;
--- a/netwerk/base/src/nsFileStreams.h
+++ b/netwerk/base/src/nsFileStreams.h
@@ -109,42 +109,38 @@ class nsFileInputStream : public nsFileS
 public:
     NS_DECL_ISUPPORTS_INHERITED
     NS_DECL_NSIFILEINPUTSTREAM
     NS_DECL_NSILINEINPUTSTREAM
     NS_DECL_NSIIPCSERIALIZABLEOBSOLETE
     NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
 
     NS_IMETHOD Close();
-    NS_IMETHOD Available(PRUint64* _retval)
-    {
-        return nsFileStreamBase::Available(_retval);
-    }
+    NS_IMETHOD Available(PRUint64* _retval);
     NS_IMETHOD Read(char* aBuf, PRUint32 aCount, PRUint32* _retval);
     NS_IMETHOD ReadSegments(nsWriteSegmentFun aWriter, void *aClosure,
                             PRUint32 aCount, PRUint32* _retval)
     {
         return nsFileStreamBase::ReadSegments(aWriter, aClosure, aCount,
                                               _retval);
     }
     NS_IMETHOD IsNonBlocking(bool* _retval)
     {
         return nsFileStreamBase::IsNonBlocking(_retval);
     } 
     
     // Overrided from nsFileStreamBase
     NS_IMETHOD Seek(PRInt32 aWhence, PRInt64 aOffset);
+    NS_IMETHOD Tell(PRInt64 *aResult);
 
     nsFileInputStream()
-      : mIOFlags(0), mPerm(0)
-    {
-        mLineBuffer = nullptr;
-    }
+      : mIOFlags(0), mPerm(0), mCachedPosition(0), mLineBuffer(nullptr)
+    {}
 
-    virtual ~nsFileInputStream() 
+    virtual ~nsFileInputStream()
     {
         Close();
     }
 
     static nsresult
     Create(nsISupports *aOuter, REFNSIID aIID, void **aResult);
 
 protected:
@@ -158,26 +154,27 @@ protected:
      * The IO flags passed to Init() for the file open.
      */
     PRInt32 mIOFlags;
     /**
      * The permissions passed to Init() for the file open.
      */
     PRInt32 mPerm;
 
+    /**
+     * Cached position for Tell for automatically reopening streams.
+     */
+    PRInt64 mCachedPosition;
+
 protected:
     /**
      * Internal, called to open a file.  Parameters are the same as their
      * Init() analogues.
      */
     nsresult Open(nsIFile* file, PRInt32 ioFlags, PRInt32 perm);
-    /**
-     * Reopen the file (for OPEN_ON_READ only!)
-     */
-    nsresult Reopen() { return Open(mFile, mIOFlags, mPerm); }
 };
 
 ////////////////////////////////////////////////////////////////////////////////
 
 class nsPartialFileInputStream : public nsFileInputStream,
                                  public nsIPartialFileInputStream
 {
 public:
--- a/xpcom/io/nsMultiplexInputStream.cpp
+++ b/xpcom/io/nsMultiplexInputStream.cpp
@@ -50,17 +50,17 @@ private:
         void* mClosure;
         bool mDone;
     };
 
     static NS_METHOD ReadSegCb(nsIInputStream* aIn, void* aClosure,
                                const char* aFromRawSegment, PRUint32 aToOffset,
                                PRUint32 aCount, PRUint32 *aWriteCount);
     
-    nsCOMArray<nsIInputStream> mStreams;
+    nsTArray<nsCOMPtr<nsIInputStream> > mStreams;
     PRUint32 mCurrentStream;
     bool mStartedReadingCurrent;
     nsresult mStatus;
 };
 
 NS_IMPL_THREADSAFE_ADDREF(nsMultiplexInputStream)
 NS_IMPL_THREADSAFE_RELEASE(nsMultiplexInputStream)
 
@@ -84,73 +84,72 @@ nsMultiplexInputStream::nsMultiplexInput
       mStatus(NS_OK)
 {
 }
 
 /* readonly attribute unsigned long count; */
 NS_IMETHODIMP
 nsMultiplexInputStream::GetCount(PRUint32 *aCount)
 {
-    *aCount = mStreams.Count();
+    *aCount = mStreams.Length();
     return NS_OK;
 }
 
 /* void appendStream (in nsIInputStream stream); */
 NS_IMETHODIMP
 nsMultiplexInputStream::AppendStream(nsIInputStream *aStream)
 {
-    return mStreams.AppendObject(aStream) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+    return mStreams.AppendElement(aStream) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
 }
 
 /* void insertStream (in nsIInputStream stream, in unsigned long index); */
 NS_IMETHODIMP
 nsMultiplexInputStream::InsertStream(nsIInputStream *aStream, PRUint32 aIndex)
 {
-    bool result = mStreams.InsertObjectAt(aStream, aIndex);
+    bool result = mStreams.InsertElementAt(aIndex, aStream);
     NS_ENSURE_TRUE(result, NS_ERROR_OUT_OF_MEMORY);
     if (mCurrentStream > aIndex ||
         (mCurrentStream == aIndex && mStartedReadingCurrent))
         ++mCurrentStream;
     return NS_OK;
 }
 
 /* void removeStream (in unsigned long index); */
 NS_IMETHODIMP
 nsMultiplexInputStream::RemoveStream(PRUint32 aIndex)
 {
-    bool result = mStreams.RemoveObjectAt(aIndex);
-    NS_ENSURE_TRUE(result, NS_ERROR_NOT_AVAILABLE);
+    mStreams.RemoveElementAt(aIndex);
     if (mCurrentStream > aIndex)
         --mCurrentStream;
     else if (mCurrentStream == aIndex)
         mStartedReadingCurrent = false;
 
     return NS_OK;
 }
 
 /* nsIInputStream getStream (in unsigned long index); */
 NS_IMETHODIMP
 nsMultiplexInputStream::GetStream(PRUint32 aIndex, nsIInputStream **_retval)
 {
-    *_retval = mStreams.SafeObjectAt(aIndex);
+    *_retval = mStreams.SafeElementAt(aIndex, nullptr);
     NS_ENSURE_TRUE(*_retval, NS_ERROR_NOT_AVAILABLE);
 
     NS_ADDREF(*_retval);
     return NS_OK;
 }
 
 /* void close (); */
 NS_IMETHODIMP
 nsMultiplexInputStream::Close()
 {
     mStatus = NS_BASE_STREAM_CLOSED;
 
     nsresult rv = NS_OK;
 
-    PRUint32 len = mStreams.Count();
+    PRUint32 len = mStreams.Length();
     for (PRUint32 i = 0; i < len; ++i) {
         nsresult rv2 = mStreams[i]->Close();
         // We still want to close all streams, but we should return an error
         if (NS_FAILED(rv2))
             rv = rv2;
     }
     return rv;
 }
@@ -160,17 +159,17 @@ NS_IMETHODIMP
 nsMultiplexInputStream::Available(PRUint64 *_retval)
 {
     if (NS_FAILED(mStatus))
         return mStatus;
 
     nsresult rv;
     PRUint64 avail = 0;
 
-    PRUint32 len = mStreams.Count();
+    PRUint32 len = mStreams.Length();
     for (PRUint32 i = mCurrentStream; i < len; i++) {
         PRUint64 streamAvail;
         rv = mStreams[i]->Available(&streamAvail);
         NS_ENSURE_SUCCESS(rv, rv);
         avail += streamAvail;
     }
     *_retval = avail;
     return NS_OK;
@@ -188,17 +187,17 @@ nsMultiplexInputStream::Read(char * aBuf
 
     if (mStatus == NS_BASE_STREAM_CLOSED)
         return NS_OK;
     if (NS_FAILED(mStatus))
         return mStatus;
  
     nsresult rv = NS_OK;
 
-    PRUint32 len = mStreams.Count();
+    PRUint32 len = mStreams.Length();
     while (mCurrentStream < len && aCount) {
         PRUint32 read;
         rv = mStreams[mCurrentStream]->Read(aBuf, aCount, &read);
 
         // XXX some streams return NS_BASE_STREAM_CLOSED to indicate EOF.
         // (This is a bug in those stream implementations)
         if (rv == NS_BASE_STREAM_CLOSED) {
             NS_NOTREACHED("Input stream's Read method returned NS_BASE_STREAM_CLOSED");
@@ -242,17 +241,17 @@ nsMultiplexInputStream::ReadSegments(nsW
     nsresult rv = NS_OK;
     ReadSegmentsState state;
     state.mThisStream = this;
     state.mOffset = 0;
     state.mWriter = aWriter;
     state.mClosure = aClosure;
     state.mDone = false;
     
-    PRUint32 len = mStreams.Count();
+    PRUint32 len = mStreams.Length();
     while (mCurrentStream < len && aCount) {
         PRUint32 read;
         rv = mStreams[mCurrentStream]->ReadSegments(ReadSegCb, &state, aCount, &read);
 
         // XXX some streams return NS_BASE_STREAM_CLOSED to indicate EOF.
         // (This is a bug in those stream implementations)
         if (rv == NS_BASE_STREAM_CLOSED) {
             NS_NOTREACHED("Input stream's Read method returned NS_BASE_STREAM_CLOSED");
@@ -300,17 +299,17 @@ nsMultiplexInputStream::ReadSegCb(nsIInp
         state->mDone = true;
     return rv;
 }
 
 /* readonly attribute boolean nonBlocking; */
 NS_IMETHODIMP
 nsMultiplexInputStream::IsNonBlocking(bool *aNonBlocking)
 {
-    PRUint32 len = mStreams.Count();
+    PRUint32 len = mStreams.Length();
     if (len == 0) {
         // Claim to be non-blocking, since we won't block the caller.
         // On the other hand we'll never return NS_BASE_STREAM_WOULD_BLOCK,
         // so maybe we should claim to be blocking?  It probably doesn't
         // matter in practice.
         *aNonBlocking = true;
         return NS_OK;
     }
@@ -330,30 +329,218 @@ nsMultiplexInputStream::IsNonBlocking(bo
 NS_IMETHODIMP
 nsMultiplexInputStream::Seek(PRInt32 aWhence, PRInt64 aOffset)
 {
     if (NS_FAILED(mStatus))
         return mStatus;
 
     nsresult rv;
 
-    // rewinding to start is easy, and should be the most common case
-    if (aWhence == NS_SEEK_SET && aOffset == 0)
-    {
-        PRUint32 i, last;
-        last = mStartedReadingCurrent ? mCurrentStream+1 : mCurrentStream;
-        for (i = 0; i < last; ++i) {
-            nsCOMPtr<nsISeekableStream> stream = do_QueryInterface(mStreams[i]);
-            NS_ENSURE_TRUE(stream, NS_ERROR_NO_INTERFACE);
+    PRUint32 oldCurrentStream = mCurrentStream;
+    bool oldStartedReadingCurrent = mStartedReadingCurrent;
+
+    if (aWhence == NS_SEEK_SET) {
+        PRInt64 remaining = aOffset;
+        if (aOffset == 0) {
+            mCurrentStream = 0;
+        }
+        for (PRUint32 i = 0; i < mStreams.Length(); ++i) {
+            nsCOMPtr<nsISeekableStream> stream =
+                do_QueryInterface(mStreams[i]);
+            if (!stream) {
+              return NS_ERROR_FAILURE;
+            }
+
+            // See if all remaining streams should be rewound
+            if (remaining == 0) {
+                if (i < oldCurrentStream ||
+                    (i == oldCurrentStream && oldStartedReadingCurrent)) {
+                    rv = stream->Seek(NS_SEEK_SET, 0);
+                    NS_ENSURE_SUCCESS(rv, rv);
+                    continue;
+                }
+                else {
+                    break;
+                }
+            }
+
+            // Get position in current stream
+            PRInt64 streamPos;
+            if (i > oldCurrentStream ||
+                (i == oldCurrentStream && !oldStartedReadingCurrent)) {
+                streamPos = 0;
+            }
+            else {
+                rv = stream->Tell(&streamPos);
+                NS_ENSURE_SUCCESS(rv, rv);
+            }
+
+            // See if we need to seek current stream forward or backward
+            if (remaining < streamPos) {
+                rv = stream->Seek(NS_SEEK_SET, remaining);
+                NS_ENSURE_SUCCESS(rv, rv);
+
+                mCurrentStream = i;
+                mStartedReadingCurrent = remaining != 0;
+
+                remaining = 0;
+            }
+            else if (remaining > streamPos) {
+                if (i < oldCurrentStream) {
+                    // We're already at end so no need to seek this stream
+                    remaining -= streamPos;
+                }
+                else {
+                    PRUint64 avail;
+                    rv = mStreams[i]->Available(&avail);
+                    NS_ENSURE_SUCCESS(rv, rv);
+
+                    PRInt64 newPos = streamPos +
+                                     NS_MIN((PRInt64)avail, remaining);
+
+                    rv = stream->Seek(NS_SEEK_SET, newPos);
+                    NS_ENSURE_SUCCESS(rv, rv);
+
+                    mCurrentStream = i;
+                    mStartedReadingCurrent = true;
+
+                    remaining -= newPos;
+                }
+            }
+            else {
+                NS_ASSERTION(remaining == streamPos, "Huh?");
+                remaining = 0;
+            }
+        }
+
+        return NS_OK;
+    }
+
+    if (aWhence == NS_SEEK_CUR && aOffset > 0) {
+        PRInt64 remaining = aOffset;
+        for (PRUint32 i = mCurrentStream; remaining && i < mStreams.Length(); ++i) {
+            nsCOMPtr<nsISeekableStream> stream =
+                do_QueryInterface(mStreams[i]);
+
+            PRUint64 avail;
+            rv = mStreams[i]->Available(&avail);
+            NS_ENSURE_SUCCESS(rv, rv);
+
+            PRInt64 seek = NS_MIN((PRInt64)avail, remaining);
+
+            rv = stream->Seek(NS_SEEK_CUR, seek);
+            NS_ENSURE_SUCCESS(rv, rv);
+
+            mCurrentStream = i;
+            mStartedReadingCurrent = true;
+
+            remaining -= seek;
+        }
 
-            rv = stream->Seek(NS_SEEK_SET, 0);
+        return NS_OK;
+    }
+
+    if (aWhence == NS_SEEK_CUR && aOffset < 0) {
+        PRInt64 remaining = -aOffset;
+        for (PRUint32 i = mCurrentStream; remaining && i != (PRUint32)-1; --i) {
+            nsCOMPtr<nsISeekableStream> stream =
+                do_QueryInterface(mStreams[i]);
+
+            PRInt64 pos;
+            rv = stream->Tell(&pos);
+            NS_ENSURE_SUCCESS(rv, rv);
+
+            PRInt64 seek = NS_MIN(pos, remaining);
+
+            rv = stream->Seek(NS_SEEK_CUR, -seek);
             NS_ENSURE_SUCCESS(rv, rv);
+
+            mCurrentStream = i;
+            mStartedReadingCurrent = seek != -pos;
+
+            remaining -= seek;
+        }
+
+        return NS_OK;
+    }
+
+    if (aWhence == NS_SEEK_CUR) {
+        NS_ASSERTION(aOffset == 0, "Should have handled all non-zero values");
+
+        return NS_OK;
+    }
+
+    if (aWhence == NS_SEEK_END) {
+        if (aOffset > 0) {
+          return NS_ERROR_INVALID_ARG;
         }
-        mCurrentStream = 0;
-        mStartedReadingCurrent = false;
+        PRInt64 remaining = aOffset;
+        for (PRUint32 i = mStreams.Length() - 1; i != (PRUint32)-1; --i) {
+            nsCOMPtr<nsISeekableStream> stream =
+                do_QueryInterface(mStreams[i]);
+
+            // See if all remaining streams should be seeked to end
+            if (remaining == 0) {
+                if (i >= oldCurrentStream) {
+                    rv = stream->Seek(NS_SEEK_END, 0);
+                    NS_ENSURE_SUCCESS(rv, rv);
+                }
+                else {
+                    break;
+                }
+            }
+
+            // Get position in current stream
+            PRInt64 streamPos;
+            if (i < oldCurrentStream) {
+                streamPos = 0;
+            } else {
+                PRUint64 avail;
+                rv = mStreams[i]->Available(&avail);
+                NS_ENSURE_SUCCESS(rv, rv);
+
+                streamPos = avail;
+            }
+
+            // See if we have enough data in the current stream.
+            if (NS_ABS(remaining) < streamPos) {
+                rv = stream->Seek(NS_SEEK_END, remaining);
+                NS_ENSURE_SUCCESS(rv, rv);
+
+                mCurrentStream = i;
+                mStartedReadingCurrent = true;
+
+                remaining = 0;
+            } else if (NS_ABS(remaining) > streamPos) {
+                if (i > oldCurrentStream ||
+                    (i == oldCurrentStream && !oldStartedReadingCurrent)) {
+                    // We're already at start so no need to seek this stream
+                    remaining += streamPos;
+                } else {
+                    PRInt64 avail;
+                    rv = stream->Tell(&avail);
+                    NS_ENSURE_SUCCESS(rv, rv);
+
+                    PRInt64 newPos = streamPos + NS_MIN(avail, NS_ABS(remaining));
+
+                    rv = stream->Seek(NS_SEEK_END, -newPos);
+                    NS_ENSURE_SUCCESS(rv, rv);
+
+                    mCurrentStream = i;
+                    mStartedReadingCurrent = true;
+
+                    remaining += newPos;
+                }
+            }
+            else {
+                NS_ASSERTION(remaining == streamPos, "Huh?");
+                remaining = 0;
+            }
+        }
+
         return NS_OK;
     }
 
     // other Seeks not implemented yet
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 /* PRUint32 tell (); */
@@ -437,43 +624,43 @@ nsMultiplexInputStream::Read(const IPC::
     return true;
 }
 
 void
 nsMultiplexInputStream::Write(IPC::Message *aMsg)
 {
     using IPC::WriteParam;
 
-    PRUint32 count = mStreams.Count();
+    PRUint32 count = mStreams.Length();
     WriteParam(aMsg, count);
 
     for (PRUint32 i = 0; i < count; i++) {
-        IPC::InputStream inputStream(mStreams.ObjectAt(i));
+        IPC::InputStream inputStream(mStreams[i]);
         WriteParam(aMsg, inputStream);
     }
 
     WriteParam(aMsg, mCurrentStream);
     WriteParam(aMsg, mStartedReadingCurrent);
     WriteParam(aMsg, mStatus);
 }
 
 void
 nsMultiplexInputStream::Serialize(InputStreamParams& aParams)
 {
     MultiplexInputStreamParams params;
 
-    PRUint32 streamCount = mStreams.Count();
+    PRUint32 streamCount = mStreams.Length();
 
     if (streamCount) {
         InfallibleTArray<InputStreamParams>& streams = params.streams();
 
         streams.SetCapacity(streamCount);
         for (PRUint32 index = 0; index < streamCount; index++) {
             nsCOMPtr<nsIIPCSerializableInputStream> serializable =
-                do_QueryInterface(mStreams.ObjectAt(index));
+                do_QueryInterface(mStreams[index]);
             NS_ASSERTION(serializable, "Child stream isn't serializable!");
 
             if (serializable) {
                 InputStreamParams childStreamParams;
                 serializable->Serialize(childStreamParams);
 
                 NS_ASSERTION(childStreamParams.type() !=
                                  InputStreamParams::T__None,