Bug 1264566 - Part 4: Close unused stream in IPC. r?valentin,baku draft
authorWei-Cheng Pan <wpan@mozilla.com>
Thu, 23 Jun 2016 18:49:42 +0800
changeset 387518 c47f2bc7ca91f5ad99379a0b40ffd9ba0305c643
parent 387517 1d6336993f5c81f6fd75fdc06cb82dcdafbb7801
child 525363 a182faeb7e07b5151731f34d681823a4343f5a2f
push id22974
push userbmo:wpan@mozilla.com
push dateThu, 14 Jul 2016 06:19:47 +0000
reviewersvalentin, baku
bugs1264566
milestone50.0a1
Bug 1264566 - Part 4: Close unused stream in IPC. r?valentin,baku MozReview-Commit-ID: 3U9JB6oGGta
dom/ipc/Blob.cpp
dom/ipc/BlobChild.h
netwerk/protocol/http/HttpChannelChild.cpp
--- a/dom/ipc/Blob.cpp
+++ b/dom/ipc/Blob.cpp
@@ -25,21 +25,23 @@
 #include "mozilla/dom/PBlobStreamParent.h"
 #include "mozilla/dom/indexedDB/FileSnapshot.h"
 #include "mozilla/dom/IndexedDatabaseManager.h"
 #include "mozilla/ipc/InputStreamUtils.h"
 #include "mozilla/ipc/PBackgroundChild.h"
 #include "mozilla/ipc/PBackgroundParent.h"
 #include "mozilla/ipc/PFileDescriptorSetParent.h"
 #include "MultipartBlobImpl.h"
+#include "nsBufferedStreams.h"
 #include "nsDataHashtable.h"
 #include "nsHashKeys.h"
 #include "nsID.h"
 #include "nsIInputStream.h"
 #include "nsIIPCSerializableInputStream.h"
+#include "nsIMIMEInputStream.h"
 #include "nsIMultiplexInputStream.h"
 #include "nsIRemoteBlob.h"
 #include "nsISeekableStream.h"
 #include "nsIUUIDGenerator.h"
 #include "nsNetCID.h"
 #include "nsServiceManagerUtils.h"
 #include "nsStringStream.h"
 #include "nsThreadUtils.h"
@@ -387,16 +389,20 @@ class NS_NO_VTABLE IPrivateRemoteInputSt
   : public nsISupports
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(PRIVATE_REMOTE_INPUT_STREAM_IID)
 
   // This will return the underlying stream.
   virtual nsIInputStream*
   BlockAndGetInternalStream() = 0;
+
+  // This will close the underlying stream.
+  virtual void
+  CloseInternalStream() = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(IPrivateRemoteInputStream,
                               PRIVATE_REMOTE_INPUT_STREAM_IID)
 
 // This class exists to keep a blob alive at least as long as its internal
 // stream.
 class BlobInputStreamTether final
@@ -476,16 +482,17 @@ class RemoteInputStream final
   Monitor mMonitor;
   BlobChild* mActor;
   nsCOMPtr<nsIInputStream> mStream;
   RefPtr<BlobImpl> mBlobImpl;
   nsCOMPtr<nsIEventTarget> mEventTarget;
   nsISeekableStream* mWeakSeekableStream;
   uint64_t mStart;
   uint64_t mLength;
+  bool mShouldCloseInternalStream;
 
 public:
   RemoteInputStream(BlobImpl* aBlobImpl,
                     uint64_t aStart,
                     uint64_t aLength);
 
   RemoteInputStream(BlobChild* aActor,
                     BlobImpl* aBlobImpl,
@@ -510,16 +517,24 @@ public:
     return !!mActor;
   }
 
   void
   SetStream(nsIInputStream* aStream);
 
   NS_DECL_THREADSAFE_ISUPPORTS
 
+  virtual void
+  CloseInternalStream() override {
+    mShouldCloseInternalStream = true;
+    if (mStream) {
+      mStream->Close();
+    }
+  }
+
 private:
   ~RemoteInputStream();
 
   nsresult
   BlockAndWaitForStream();
 
   void
   ReallyBlockAndWaitForStream();
@@ -982,16 +997,17 @@ RemoteInputStream::RemoteInputStream(Blo
                                      uint64_t aStart,
                                      uint64_t aLength)
   : mMonitor("RemoteInputStream.mMonitor")
   , mActor(nullptr)
   , mBlobImpl(aBlobImpl)
   , mWeakSeekableStream(nullptr)
   , mStart(aStart)
   , mLength(aLength)
+  , mShouldCloseInternalStream(false)
 {
   MOZ_ASSERT(aBlobImpl);
 
   if (!NS_IsMainThread()) {
     mEventTarget = do_GetCurrentThread();
     MOZ_ASSERT(mEventTarget);
   }
 
@@ -1004,16 +1020,17 @@ RemoteInputStream::RemoteInputStream(Blo
                                      uint64_t aLength)
   : mMonitor("RemoteInputStream.mMonitor")
   , mActor(aActor)
   , mBlobImpl(aBlobImpl)
   , mEventTarget(NS_GetCurrentThread())
   , mWeakSeekableStream(nullptr)
   , mStart(aStart)
   , mLength(aLength)
+  , mShouldCloseInternalStream(false)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aActor);
   MOZ_ASSERT(aBlobImpl);
 
   MOZ_ASSERT(IsOnOwningThread());
 }
 
@@ -1049,16 +1066,20 @@ RemoteInputStream::SetStream(nsIInputStr
       MOZ_ASSERT(!mWeakSeekableStream);
 
       mStream.swap(stream);
       mWeakSeekableStream = seekableStream;
 
       mMonitor.Notify();
     }
   }
+
+  if (mShouldCloseInternalStream) {
+    CloseInternalStream();
+  }
 }
 
 nsresult
 RemoteInputStream::BlockAndWaitForStream()
 {
   if (IsOnOwningThread()) {
     if (NS_IsMainThread()) {
       NS_WARNING("Blocking the main thread is not supported!");
@@ -4493,9 +4514,55 @@ InputStreamChild::Recv__delete__(const I
   nsCOMPtr<nsIInputStream> stream = DeserializeInputStream(aParams, fds);
   MOZ_ASSERT(stream);
 
   mRemoteStream->SetStream(stream);
   return true;
 }
 
 } // namespace dom
+
+namespace ipc {
+
+void
+CloseRemoteInputStreamsInternal(nsIInputStream* aStream, int aRecursionLevel)
+{
+  using namespace mozilla::dom;
+  if (nsCOMPtr<nsIMIMEInputStream> stream = do_QueryInterface(aStream)) {
+    nsCOMPtr<nsIInputStream> data;
+    stream->GetData(getter_AddRefs(data));
+    CloseRemoteInputStreamsInternal(data, aRecursionLevel + 1);
+  }
+  else if (nsCOMPtr<nsIMultiplexInputStream> stream = do_QueryInterface(aStream)) {
+    uint32_t count = 0;
+    stream->GetCount(&count);
+    for (uint32_t i = 0; i < count; ++i) {
+      nsCOMPtr<nsIInputStream> childStream;
+      stream->GetStream(i, getter_AddRefs(childStream));
+      CloseRemoteInputStreamsInternal(childStream, aRecursionLevel + 1);
+    }
+  }
+  else if (nsCOMPtr<nsIStreamBufferAccess> stream = do_QueryInterface(aStream)) {
+    nsCOMPtr<nsISupports> tmpStream;
+    stream->GetUnbufferedStream(getter_AddRefs(tmpStream));
+    nsCOMPtr<nsIInputStream> unbufferedStream = do_QueryInterface(tmpStream);
+    CloseRemoteInputStreamsInternal(unbufferedStream, aRecursionLevel + 1);
+  }
+  else if (nsCOMPtr<IPrivateRemoteInputStream> stream = do_QueryInterface(aStream)) {
+    // We only need to close the real file from form input. Such streams
+    // should be contained by nsMIMEInputStream or nsIMultiplexInputStream.
+    // If a RemoteInputStream is the root, then it is likely a script-generated
+    // Blob object.
+    if (aRecursionLevel > 0) {
+      stream->CloseInternalStream();
+    }
+  }
+}
+
+void
+CloseRemoteInputStreams(nsIInputStream* aStream)
+{
+  CloseRemoteInputStreamsInternal(aStream, 0);
+}
+
+} // namespace ipc
+
 } // namespace mozilla
--- a/dom/ipc/BlobChild.h
+++ b/dom/ipc/BlobChild.h
@@ -8,24 +8,28 @@
 #define mozilla_dom_ipc_BlobChild_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/PBlobChild.h"
 #include "nsCOMPtr.h"
 #include "nsID.h"
 
 class nsIEventTarget;
+class nsIInputStream;
 class nsIRemoteBlob;
 class nsString;
 
 namespace mozilla {
 namespace ipc {
 
 class PBackgroundChild;
 
+void
+CloseRemoteInputStreams(nsIInputStream* aStream);
+
 } // namespace ipc
 
 namespace dom {
 
 class Blob;
 class BlobImpl;
 class ContentChild;
 class nsIContentChild;
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -20,16 +20,17 @@
 #include "nsISupportsPrimitives.h"
 #include "nsChannelClassifier.h"
 #include "nsStringStream.h"
 #include "nsHttpHandler.h"
 #include "nsNetUtil.h"
 #include "nsSerializationHelper.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/Performance.h"
+#include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/ipc/InputStreamUtils.h"
 #include "mozilla/ipc/URIUtils.h"
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "mozilla/net/ChannelDiverterChild.h"
 #include "mozilla/net/DNS.h"
 #include "SerializedLoadContext.h"
 #include "nsInputStreamPump.h"
 #include "InterceptedChannel.h"
@@ -1737,16 +1738,23 @@ HttpChannelChild::AsyncOpen(nsIStreamLis
              mLoadInfo->GetSecurityMode() == 0 ||
              mLoadInfo->GetInitialSecurityCheckDone() ||
              (mLoadInfo->GetSecurityMode() == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL &&
               nsContentUtils::IsSystemPrincipal(mLoadInfo->LoadingPrincipal())),
              "security flags in loadInfo but asyncOpen2() not called");
 
   LOG(("HttpChannelChild::AsyncOpen [this=%p uri=%s]\n", this, mSpec.get()));
 
+  // XXX bug 1264566
+  // Upload stream is only read by parent process, so content process should
+  // not keep file descriptor opening in the stream.
+  if (mUploadStream) {
+    CloseRemoteInputStreams(mUploadStream);
+  }
+
   if (mCanceled)
     return mStatus;
 
   NS_ENSURE_TRUE(gNeckoChild != nullptr, NS_ERROR_FAILURE);
   NS_ENSURE_ARG_POINTER(listener);
   NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
   NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);