Bug 1353629 - PBlob refactoring - part 13 - IPCBlobInputStream should support remote nsIAsyncInputStream, r=smaug
authorAndrea Marchesini <amarchesini@mozilla.com>
Mon, 24 Apr 2017 12:09:41 +0200
changeset 354628 6586412c7f6494e5d9df0c8a24e31ec55f9ab2d4
parent 354627 3a2c881ccf419304a7b728a9433fb3065f76f40a
child 354629 b93b33542bcf7baa6029df8e0bc7a822412fe0cc
push id31707
push userkwierso@gmail.com
push dateMon, 24 Apr 2017 22:53:41 +0000
treeherdermozilla-central@abdcc8dfc283 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1353629
milestone55.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 1353629 - PBlob refactoring - part 13 - IPCBlobInputStream should support remote nsIAsyncInputStream, r=smaug If a child-to-parent IPCBlob is more than 1mb, we end up using a pipe stream. If that ipcBlob is sent to a different process, we need to implement asyncWait correctly: we need to call the remoteStream->AsyncWait().
dom/file/ipc/IPCBlobInputStream.cpp
dom/file/ipc/IPCBlobInputStream.h
--- a/dom/file/ipc/IPCBlobInputStream.cpp
+++ b/dom/file/ipc/IPCBlobInputStream.cpp
@@ -59,16 +59,17 @@ private:
 } // anonymous
 
 NS_IMPL_ADDREF(IPCBlobInputStream);
 NS_IMPL_RELEASE(IPCBlobInputStream);
 
 NS_INTERFACE_MAP_BEGIN(IPCBlobInputStream)
   NS_INTERFACE_MAP_ENTRY(nsIInputStream)
   NS_INTERFACE_MAP_ENTRY(nsIAsyncInputStream)
+  NS_INTERFACE_MAP_ENTRY(nsIInputStreamCallback)
   NS_INTERFACE_MAP_ENTRY(nsICloneableInputStream)
   NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableInputStream)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
 NS_INTERFACE_MAP_END
 
 IPCBlobInputStream::IPCBlobInputStream(IPCBlobInputStreamChild* aActor)
   : mActor(aActor)
   , mState(eInit)
@@ -215,22 +216,19 @@ IPCBlobInputStream::AsyncWait(nsIInputSt
     if (mCallback && aCallback) {
       return NS_ERROR_FAILURE;
     }
 
     mCallback = aCallback;
     mCallbackEventTarget = aEventTarget;
     return NS_OK;
 
-  // We have the remote inputStream, let's execute the callback immediately.
+  // We have the remote inputStream, let's check if we can execute the callback.
   case eRunning:
-    if (aCallback) {
-      CallbackRunnable::Execute(aCallback, aEventTarget, this);
-    }
-    return NS_OK;
+    return MaybeExecuteCallback(aCallback, aEventTarget);
 
   // Stream is closed.
   default:
     MOZ_ASSERT(mState == eClosed);
     return NS_BASE_STREAM_CLOSED;
   }
 }
 
@@ -244,28 +242,94 @@ IPCBlobInputStream::StreamReady(nsIInput
     }
     return;
   }
 
   // If aInputStream is null, it means that the serialization went wrong or the
   // stream is not available anymore. We keep the state as pending just to block
   // any additional operation.
 
-  if (aInputStream && mCallback) {
+  nsCOMPtr<nsIInputStreamCallback> callback;
+  callback.swap(mCallback);
+
+  nsCOMPtr<nsIEventTarget> callbackEventTarget;
+  callbackEventTarget.swap(mCallbackEventTarget);
+
+  if (aInputStream && callback) {
     MOZ_ASSERT(mState == ePending);
-    MOZ_ASSERT(mCallback);
 
     mRemoteStream = aInputStream;
     mState = eRunning;
 
-    CallbackRunnable::Execute(mCallback, mCallbackEventTarget, this);
+    MaybeExecuteCallback(callback, callbackEventTarget);
+  }
+}
+
+nsresult
+IPCBlobInputStream::MaybeExecuteCallback(nsIInputStreamCallback* aCallback,
+                                         nsIEventTarget* aCallbackEventTarget)
+{
+  MOZ_ASSERT(mState == eRunning);
+  MOZ_ASSERT(mRemoteStream);
+
+  // If the stream supports nsIAsyncInputStream, we need to call its AsyncWait
+  // and wait for OnInputStreamReady.
+  nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(mRemoteStream);
+  if (asyncStream) {
+    // If the callback has been already set, we return an error.
+    if (mCallback && aCallback) {
+      return NS_ERROR_FAILURE;
+    }
+
+    mCallback = aCallback;
+    mCallbackEventTarget = aCallbackEventTarget;
+
+    if (!mCallback) {
+      return NS_OK;
+    }
+
+    nsCOMPtr<nsIEventTarget> target = NS_GetCurrentThread();
+    return asyncStream->AsyncWait(this, 0, 0, target);
   }
 
+  MOZ_ASSERT(!mCallback);
+  MOZ_ASSERT(!mCallbackEventTarget);
+
+  if (!aCallback) {
+    return NS_OK;
+  }
+
+  CallbackRunnable::Execute(aCallback, aCallbackEventTarget, this);
+  return NS_OK;
+}
+
+// nsIInputStreamCallback
+
+NS_IMETHODIMP
+IPCBlobInputStream::OnInputStreamReady(nsIAsyncInputStream* aStream)
+{
+  // We have been closed in the meantime.
+  if (mState == eClosed) {
+    return NS_OK;
+  }
+
+  MOZ_ASSERT(mState == eRunning);
+  MOZ_ASSERT(mRemoteStream == aStream);
+
+  // The callback has been canceled in the meantime.
+  if (!mCallback) {
+    return NS_OK;
+  }
+
+  CallbackRunnable::Execute(mCallback, mCallbackEventTarget, this);
+
   mCallback = nullptr;
   mCallbackEventTarget = nullptr;
+
+  return NS_OK;
 }
 
 // nsIIPCSerializableInputStream
 
 void
 IPCBlobInputStream::Serialize(mozilla::ipc::InputStreamParams& aParams,
                               FileDescriptorArray& aFileDescriptors)
 {
--- a/dom/file/ipc/IPCBlobInputStream.h
+++ b/dom/file/ipc/IPCBlobInputStream.h
@@ -12,34 +12,40 @@
 #include "nsIIPCSerializableInputStream.h"
 
 namespace mozilla {
 namespace dom {
 
 class IPCBlobInputStreamChild;
 
 class IPCBlobInputStream final : public nsIAsyncInputStream
+                               , public nsIInputStreamCallback
                                , public nsICloneableInputStream
                                , public nsIIPCSerializableInputStream
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIINPUTSTREAM
   NS_DECL_NSIASYNCINPUTSTREAM
+  NS_DECL_NSIINPUTSTREAMCALLBACK
   NS_DECL_NSICLONEABLEINPUTSTREAM
   NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
 
   explicit IPCBlobInputStream(IPCBlobInputStreamChild* aActor);
 
   void
   StreamReady(nsIInputStream* aInputStream);
 
 private:
   ~IPCBlobInputStream();
 
+  nsresult
+  MaybeExecuteCallback(nsIInputStreamCallback* aCallback,
+                       nsIEventTarget* aEventTarget);
+
   RefPtr<IPCBlobInputStreamChild> mActor;
 
   // This is the list of possible states.
   enum {
     // The initial state. Only ::Available() can be used without receiving an
     // error. The available size is known by the actor.
     eInit,