Bug 1383518 - Part 2: nsMIMEInputStream should conditionally implement nsIAsyncInputStream. r=bkelly, a=jcristau
authorAndrew Sutherland <asutherland@asutherland.org>
Wed, 23 Aug 2017 06:39:36 -0400
changeset 666710 3feb092c607a8203ab1506af26415ca42bfb491f
parent 666709 c429763f95c373ffebb233b51c6d1ab9ad1d89cc
child 666711 2c5b968aba14f53facca0eb4acc43b3d45260a78
push id80488
push userbmo:mkelly@mozilla.com
push dateTue, 19 Sep 2017 04:42:30 +0000
reviewersbkelly, jcristau
bugs1383518, 1361443
milestone55.0.3
Bug 1383518 - Part 2: nsMIMEInputStream should conditionally implement nsIAsyncInputStream. r=bkelly, a=jcristau NS_AsyncCopy aborts if it receives an NS_BASE_STREAM_WOULD_BLOCK error result during copying and it is unable to QI the source stream to an nsIAsyncInputStream. IPCBlobInputStream can return this, especially if it's: - A freshly created aggregate stream as part of form submission of a type=file where the Blob will come from the parent because of the file picker but the stream is being uploaded from the child. - A ServiceWorker is involved, causing HttpBaseChannel::EnsureUploadStreamIsCloneable to trigger an NS_AsyncCopy very early in the process. IPCBlobInputStream implements nsIAsyncInputStream, and nsMultiplexInputStream does too (conditionally based on its child streams; if any are async, it takes step to uniformly expose async behavior). However, due to lack of sufficient test coverage, nsMIMEInputStream did not get fixed as part of bug 1361443 when nsMultiplexInputStream gained its nsIAsyncInputStream powers. We address that here in the same fashion. Part 1 of this series addresses the test coverage issue.
netwerk/base/nsMIMEInputStream.cpp
--- a/netwerk/base/nsMIMEInputStream.cpp
+++ b/netwerk/base/nsMIMEInputStream.cpp
@@ -7,84 +7,91 @@
  * The MIME stream separates headers and a datastream. It also allows
  * automatic creation of the content-length header.
  */
 
 #include "ipc/IPCMessageUtils.h"
 
 #include "nsCOMPtr.h"
 #include "nsComponentManagerUtils.h"
+#include "nsIAsyncInputStream.h"
 #include "nsIHttpHeaderVisitor.h"
 #include "nsIMIMEInputStream.h"
 #include "nsISeekableStream.h"
 #include "nsString.h"
 #include "nsMIMEInputStream.h"
 #include "nsIClassInfoImpl.h"
 #include "nsIIPCSerializableInputStream.h"
 #include "mozilla/Move.h"
 #include "mozilla/ipc/InputStreamUtils.h"
 
 using namespace mozilla::ipc;
 using mozilla::Maybe;
 using mozilla::Move;
 
 class nsMIMEInputStream : public nsIMIMEInputStream,
                           public nsISeekableStream,
-                          public nsIIPCSerializableInputStream
+                          public nsIIPCSerializableInputStream,
+                          public nsIAsyncInputStream
 {
     virtual ~nsMIMEInputStream();
 
 public:
     nsMIMEInputStream();
 
     NS_DECL_THREADSAFE_ISUPPORTS
     NS_DECL_NSIINPUTSTREAM
     NS_DECL_NSIMIMEINPUTSTREAM
     NS_DECL_NSISEEKABLESTREAM
     NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
+    NS_DECL_NSIASYNCINPUTSTREAM
 
 private:
 
     void InitStreams();
 
     struct MOZ_STACK_CLASS ReadSegmentsState {
         nsCOMPtr<nsIInputStream> mThisStream;
         nsWriteSegmentFun mWriter;
         void* mClosure;
     };
     static nsresult ReadSegCb(nsIInputStream* aIn, void* aClosure,
                               const char* aFromRawSegment, uint32_t aToOffset,
                               uint32_t aCount, uint32_t *aWriteCount);
 
+    bool IsAsyncInputStream() const;
     bool IsIPCSerializable() const;
 
     nsTArray<HeaderEntry> mHeaders;
 
     nsCOMPtr<nsIInputStream> mStream;
     bool mStartedReading;
 };
 
 NS_IMPL_ADDREF(nsMIMEInputStream)
 NS_IMPL_RELEASE(nsMIMEInputStream)
 
 NS_IMPL_CLASSINFO(nsMIMEInputStream, nullptr, nsIClassInfo::THREADSAFE,
                   NS_MIMEINPUTSTREAM_CID)
 
 NS_INTERFACE_MAP_BEGIN(nsMIMEInputStream)
   NS_INTERFACE_MAP_ENTRY(nsIMIMEInputStream)
-  NS_INTERFACE_MAP_ENTRY(nsIInputStream)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIInputStream, nsIMIMEInputStream)
   NS_INTERFACE_MAP_ENTRY(nsISeekableStream)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIPCSerializableInputStream,
                                      IsIPCSerializable())
+  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAsyncInputStream,
+                                     IsAsyncInputStream())
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIMIMEInputStream)
   NS_IMPL_QUERY_CLASSINFO(nsMIMEInputStream)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CI_INTERFACE_GETTER(nsMIMEInputStream,
                             nsIMIMEInputStream,
+                            nsIAsyncInputStream,
                             nsIInputStream,
                             nsISeekableStream)
 
 nsMIMEInputStream::nsMIMEInputStream() : mStartedReading(false)
 {
 }
 
 nsMIMEInputStream::~nsMIMEInputStream()
@@ -199,17 +206,19 @@ nsMIMEInputStream::Seek(int32_t whence, 
 
 // Proxy ReadSegments since we need to be a good little nsIInputStream
 NS_IMETHODIMP nsMIMEInputStream::ReadSegments(nsWriteSegmentFun aWriter,
                                               void *aClosure, uint32_t aCount,
                                               uint32_t *_retval)
 {
     INITSTREAMS;
     ReadSegmentsState state;
-    state.mThisStream = this;
+    // Disambiguate ambiguous nsIInputStream.
+    state.mThisStream = static_cast<nsIInputStream*>(
+                          static_cast<nsIMIMEInputStream*>(this));
     state.mWriter = aWriter;
     state.mClosure = aClosure;
     return mStream->ReadSegments(ReadSegCb, &state, aCount, _retval);
 }
 
 nsresult
 nsMIMEInputStream::ReadSegCb(nsIInputStream* aIn, void* aClosure,
                              const char* aFromRawSegment,
@@ -230,16 +239,36 @@ nsMIMEInputStream::ReadSegCb(nsIInputStr
  */
 
 // nsIInputStream
 NS_IMETHODIMP nsMIMEInputStream::Close(void) { INITSTREAMS; return mStream->Close(); }
 NS_IMETHODIMP nsMIMEInputStream::Available(uint64_t *_retval) { INITSTREAMS; return mStream->Available(_retval); }
 NS_IMETHODIMP nsMIMEInputStream::Read(char * buf, uint32_t count, uint32_t *_retval) { INITSTREAMS; return mStream->Read(buf, count, _retval); }
 NS_IMETHODIMP nsMIMEInputStream::IsNonBlocking(bool *aNonBlocking) { INITSTREAMS; return mStream->IsNonBlocking(aNonBlocking); }
 
+// nsIAsyncInputStream
+NS_IMETHODIMP
+nsMIMEInputStream::CloseWithStatus(nsresult aStatus)
+{
+    INITSTREAMS;
+    nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(mStream);
+    return asyncStream->CloseWithStatus(aStatus);
+}
+
+NS_IMETHODIMP
+nsMIMEInputStream::AsyncWait(nsIInputStreamCallback* aCallback,
+                             uint32_t aFlags, uint32_t aRequestedCount,
+                             nsIEventTarget* aEventTarget)
+{
+    INITSTREAMS;
+    nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(mStream);
+    return asyncStream->AsyncWait(
+        aCallback, aFlags, aRequestedCount, aEventTarget);
+}
+
 // nsISeekableStream
 NS_IMETHODIMP nsMIMEInputStream::Tell(int64_t *_retval)
 {
     INITSTREAMS;
     nsCOMPtr<nsISeekableStream> stream = do_QueryInterface(mStream);
     return stream->Tell(_retval);
 }
 NS_IMETHODIMP nsMIMEInputStream::SetEOF(void) {
@@ -333,16 +362,23 @@ nsMIMEInputStream::Deserialize(const Inp
 Maybe<uint64_t>
 nsMIMEInputStream::ExpectedSerializedLength()
 {
     nsCOMPtr<nsIIPCSerializableInputStream> serializable = do_QueryInterface(mStream);
     return serializable ? serializable->ExpectedSerializedLength() : Nothing();
 }
 
 bool
+nsMIMEInputStream::IsAsyncInputStream() const
+{
+    nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(mStream);
+    return !!asyncStream;
+}
+
+bool
 nsMIMEInputStream::IsIPCSerializable() const
 {
     // If SetData() or Deserialize() has not be called yet, mStream is null.
     if (!mStream) {
       return true;
     }
 
     nsCOMPtr<nsIIPCSerializableInputStream> serializable = do_QueryInterface(mStream);