Bug 1264566 - Part 4: Close unused stream in IPC. r?valentin,baku
MozReview-Commit-ID: 3U9JB6oGGta
--- 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);