Bug 1464090 - Implement InputStreamLengthWrapper to make any stream nsIInputStreamLength and nsIAsyncInputStreamLength, r=froydnj
authorAndrea Marchesini <amarchesini@mozilla.com>
Thu, 31 May 2018 18:12:25 +0200
changeset 420720 c36cbbfd103372b568727fd5083f0424722ca0ec
parent 420719 aebf9e698f76913a66d13ecc9841fc005caa82b8
child 420721 ab037c88ae18f7df8540517895fbf2ff6c8ff2ea
push id34077
push usernerli@mozilla.com
push dateThu, 31 May 2018 21:51:59 +0000
treeherdermozilla-central@42880a726964 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1464090
milestone62.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 1464090 - Implement InputStreamLengthWrapper to make any stream nsIInputStreamLength and nsIAsyncInputStreamLength, r=froydnj
ipc/glue/InputStreamParams.ipdlh
ipc/glue/InputStreamUtils.cpp
xpcom/io/InputStreamLengthWrapper.cpp
xpcom/io/InputStreamLengthWrapper.h
xpcom/io/moz.build
--- a/ipc/glue/InputStreamParams.ipdlh
+++ b/ipc/glue/InputStreamParams.ipdlh
@@ -56,16 +56,17 @@ union InputStreamParams
 {
   StringInputStreamParams;
   FileInputStreamParams;
   BufferedInputStreamParams;
   MIMEInputStreamParams;
   MultiplexInputStreamParams;
   SlicedInputStreamParams;
   IPCBlobInputStreamParams;
+  InputStreamLengthWrapperParams;
 };
 
 union OptionalInputStreamParams
 {
   void_t;
   InputStreamParams;
 };
 
@@ -77,10 +78,17 @@ struct BufferedInputStreamParams
 
 struct MIMEInputStreamParams
 {
   OptionalInputStreamParams optionalStream;
   HeaderEntry[] headers;
   bool startedReading;
 };
 
+struct InputStreamLengthWrapperParams
+{
+  InputStreamParams stream;
+  int64_t length;
+  bool consumed;
+};
+
 } // namespace ipc
 } // namespace mozilla
--- a/ipc/glue/InputStreamUtils.cpp
+++ b/ipc/glue/InputStreamUtils.cpp
@@ -8,16 +8,17 @@
 
 #include "nsIIPCSerializableInputStream.h"
 
 #include "mozilla/Assertions.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/ipc/IPCBlobInputStream.h"
 #include "mozilla/dom/ipc/IPCBlobInputStreamStorage.h"
 #include "mozilla/SlicedInputStream.h"
+#include "mozilla/InputStreamLengthWrapper.h"
 #include "nsComponentManagerUtils.h"
 #include "nsDebug.h"
 #include "nsID.h"
 #include "nsIXULRuntime.h"
 #include "nsMIMEInputStream.h"
 #include "nsMultiplexInputStream.h"
 #include "nsNetCID.h"
 #include "nsStringStream.h"
@@ -95,16 +96,20 @@ InputStreamHelper::DeserializeInputStrea
     case InputStreamParams::TMultiplexInputStreamParams:
       serializable = do_CreateInstance(kMultiplexInputStreamCID);
       break;
 
     case InputStreamParams::TSlicedInputStreamParams:
       serializable = new mozilla::SlicedInputStream();
       break;
 
+    case InputStreamParams::TInputStreamLengthWrapperParams:
+      serializable = new mozilla::InputStreamLengthWrapper();
+      break;
+
     default:
       MOZ_ASSERT(false, "Unknown params!");
       return nullptr;
   }
 
   MOZ_ASSERT(serializable);
 
   if (!serializable->Deserialize(aParams, aFileDescriptors)) {
new file mode 100644
--- /dev/null
+++ b/xpcom/io/InputStreamLengthWrapper.cpp
@@ -0,0 +1,336 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#include "InputStreamLengthWrapper.h"
+#include "mozilla/ipc/InputStreamUtils.h"
+#include "nsISeekableStream.h"
+#include "nsStreamUtils.h"
+
+namespace mozilla {
+
+using namespace ipc;
+
+NS_IMPL_ADDREF(InputStreamLengthWrapper);
+NS_IMPL_RELEASE(InputStreamLengthWrapper);
+
+NS_INTERFACE_MAP_BEGIN(InputStreamLengthWrapper)
+  NS_INTERFACE_MAP_ENTRY(nsIInputStream)
+  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsICloneableInputStream,
+                                     mWeakCloneableInputStream || !mInputStream)
+  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIPCSerializableInputStream,
+                                     mWeakIPCSerializableInputStream || !mInputStream)
+  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISeekableStream,
+                                     mWeakSeekableInputStream || !mInputStream)
+  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAsyncInputStream,
+                                     mWeakAsyncInputStream || !mInputStream)
+  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamCallback,
+                                     mWeakAsyncInputStream || !mInputStream)
+  NS_INTERFACE_MAP_ENTRY(nsIInputStreamLength)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
+NS_INTERFACE_MAP_END
+
+InputStreamLengthWrapper::InputStreamLengthWrapper(already_AddRefed<nsIInputStream> aInputStream,
+                                                   int64_t aLength)
+  : mWeakCloneableInputStream(nullptr)
+  , mWeakIPCSerializableInputStream(nullptr)
+  , mWeakSeekableInputStream(nullptr)
+  , mWeakAsyncInputStream(nullptr)
+  , mLength(aLength)
+  , mConsumed(false)
+  , mMutex("InputStreamLengthWrapper::mMutex")
+{
+  MOZ_ASSERT(mLength >= 0);
+
+  nsCOMPtr<nsIInputStream> inputStream = mozilla::Move(aInputStream);
+  SetSourceStream(inputStream.forget());
+}
+
+InputStreamLengthWrapper::InputStreamLengthWrapper()
+  : mWeakCloneableInputStream(nullptr)
+  , mWeakIPCSerializableInputStream(nullptr)
+  , mWeakSeekableInputStream(nullptr)
+  , mWeakAsyncInputStream(nullptr)
+  , mLength(-1)
+  , mConsumed(false)
+  , mMutex("InputStreamLengthWrapper::mMutex")
+{}
+
+InputStreamLengthWrapper::~InputStreamLengthWrapper() = default;
+
+void
+InputStreamLengthWrapper::SetSourceStream(already_AddRefed<nsIInputStream> aInputStream)
+{
+  MOZ_ASSERT(!mInputStream);
+
+  mInputStream = mozilla::Move(aInputStream);
+
+  nsCOMPtr<nsICloneableInputStream> cloneableStream =
+    do_QueryInterface(mInputStream);
+  if (cloneableStream && SameCOMIdentity(mInputStream, cloneableStream)) {
+    mWeakCloneableInputStream = cloneableStream;
+  }
+
+  nsCOMPtr<nsIIPCSerializableInputStream> serializableStream =
+    do_QueryInterface(mInputStream);
+  if (serializableStream &&
+      SameCOMIdentity(mInputStream, serializableStream)) {
+    mWeakIPCSerializableInputStream = serializableStream;
+  }
+
+  nsCOMPtr<nsISeekableStream> seekableStream =
+    do_QueryInterface(mInputStream);
+  if (seekableStream && SameCOMIdentity(mInputStream, seekableStream)) {
+    mWeakSeekableInputStream = seekableStream;
+  }
+
+  nsCOMPtr<nsIAsyncInputStream> asyncInputStream =
+    do_QueryInterface(mInputStream);
+  if (asyncInputStream && SameCOMIdentity(mInputStream, asyncInputStream)) {
+    mWeakAsyncInputStream = asyncInputStream;
+  }
+}
+
+// nsIInputStream interface
+
+NS_IMETHODIMP
+InputStreamLengthWrapper::Close()
+{
+  NS_ENSURE_STATE(mInputStream);
+  return mInputStream->Close();
+}
+
+NS_IMETHODIMP
+InputStreamLengthWrapper::Available(uint64_t* aLength)
+{
+  NS_ENSURE_STATE(mInputStream);
+  return mInputStream->Available(aLength);
+}
+
+NS_IMETHODIMP
+InputStreamLengthWrapper::Read(char* aBuffer, uint32_t aCount, uint32_t* aReadCount)
+{
+  NS_ENSURE_STATE(mInputStream);
+  mConsumed = true;
+  return mInputStream->Read(aBuffer, aCount, aReadCount);
+}
+
+NS_IMETHODIMP
+InputStreamLengthWrapper::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
+                                       uint32_t aCount, uint32_t *aResult)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+InputStreamLengthWrapper::IsNonBlocking(bool* aNonBlocking)
+{
+  NS_ENSURE_STATE(mInputStream);
+  return mInputStream->IsNonBlocking(aNonBlocking);
+}
+
+// nsICloneableInputStream interface
+
+NS_IMETHODIMP
+InputStreamLengthWrapper::GetCloneable(bool* aCloneable)
+{
+  NS_ENSURE_STATE(mInputStream);
+  NS_ENSURE_STATE(mWeakCloneableInputStream);
+  mWeakCloneableInputStream->GetCloneable(aCloneable);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+InputStreamLengthWrapper::Clone(nsIInputStream** aResult)
+{
+  NS_ENSURE_STATE(mInputStream);
+  NS_ENSURE_STATE(mWeakCloneableInputStream);
+
+  nsCOMPtr<nsIInputStream> clonedStream;
+  nsresult rv = mWeakCloneableInputStream->Clone(getter_AddRefs(clonedStream));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCOMPtr<nsIInputStream> stream =
+    new InputStreamLengthWrapper(clonedStream.forget(), mLength);
+
+  stream.forget(aResult);
+  return NS_OK;
+}
+
+// nsIAsyncInputStream interface
+
+NS_IMETHODIMP
+InputStreamLengthWrapper::CloseWithStatus(nsresult aStatus)
+{
+  NS_ENSURE_STATE(mInputStream);
+  NS_ENSURE_STATE(mWeakAsyncInputStream);
+
+  mConsumed = true;
+  return mWeakAsyncInputStream->CloseWithStatus(aStatus);
+}
+
+NS_IMETHODIMP
+InputStreamLengthWrapper::AsyncWait(nsIInputStreamCallback* aCallback,
+                                    uint32_t aFlags,
+                                    uint32_t aRequestedCount,
+                                    nsIEventTarget* aEventTarget)
+{
+  NS_ENSURE_STATE(mInputStream);
+  NS_ENSURE_STATE(mWeakAsyncInputStream);
+
+  nsCOMPtr<nsIInputStreamCallback> callback = this;
+  {
+    MutexAutoLock lock(mMutex);
+
+    if (mAsyncWaitCallback && aCallback) {
+      return NS_ERROR_FAILURE;
+    }
+
+    bool hadCallback = !!mAsyncWaitCallback;
+    mAsyncWaitCallback = aCallback;
+
+    if (!mAsyncWaitCallback) {
+      if (!hadCallback) {
+        // No pending operation.
+        return NS_OK;
+      }
+
+      // Abort current operation.
+      callback = nullptr;
+    }
+  }
+
+  return mWeakAsyncInputStream->AsyncWait(callback, aFlags, aRequestedCount,
+                                          aEventTarget);
+}
+
+// nsIInputStreamCallback
+
+NS_IMETHODIMP
+InputStreamLengthWrapper::OnInputStreamReady(nsIAsyncInputStream* aStream)
+{
+  MOZ_ASSERT(mInputStream);
+  MOZ_ASSERT(mWeakAsyncInputStream);
+  MOZ_ASSERT(mWeakAsyncInputStream == aStream);
+
+  nsCOMPtr<nsIInputStreamCallback> callback;
+  {
+    MutexAutoLock lock(mMutex);
+    // We have been canceled in the meanwhile.
+    if (!mAsyncWaitCallback) {
+      return NS_OK;
+    }
+
+    callback.swap(mAsyncWaitCallback);
+  }
+
+  MOZ_ASSERT(callback);
+  return callback->OnInputStreamReady(this);
+}
+
+// nsIIPCSerializableInputStream
+
+void
+InputStreamLengthWrapper::Serialize(mozilla::ipc::InputStreamParams& aParams,
+                                    FileDescriptorArray& aFileDescriptors)
+{
+  MOZ_ASSERT(mInputStream);
+  MOZ_ASSERT(mWeakIPCSerializableInputStream);
+
+  InputStreamLengthWrapperParams params;
+  InputStreamHelper::SerializeInputStream(mInputStream, params.stream(),
+                                          aFileDescriptors);
+  params.length() = mLength;
+  params.consumed() = mConsumed;
+
+  aParams = params;
+}
+
+bool
+InputStreamLengthWrapper::Deserialize(const mozilla::ipc::InputStreamParams& aParams,
+                                      const FileDescriptorArray& aFileDescriptors)
+{
+  MOZ_ASSERT(!mInputStream);
+  MOZ_ASSERT(!mWeakIPCSerializableInputStream);
+
+  if (aParams.type() !=
+      InputStreamParams::TInputStreamLengthWrapperParams) {
+    NS_ERROR("Received unknown parameters from the other process!");
+    return false;
+  }
+
+  const InputStreamLengthWrapperParams& params =
+    aParams.get_InputStreamLengthWrapperParams();
+
+  nsCOMPtr<nsIInputStream> stream =
+    InputStreamHelper::DeserializeInputStream(params.stream(),
+                                              aFileDescriptors);
+  if (!stream) {
+    NS_WARNING("Deserialize failed!");
+    return false;
+  }
+
+  SetSourceStream(stream.forget());
+
+  mLength = params.length();
+  mConsumed = params.consumed();
+
+  return true;
+}
+
+mozilla::Maybe<uint64_t>
+InputStreamLengthWrapper::ExpectedSerializedLength()
+{
+  if (!mInputStream || !mWeakIPCSerializableInputStream) {
+    return mozilla::Nothing();
+  }
+
+  return mWeakIPCSerializableInputStream->ExpectedSerializedLength();
+}
+
+// nsISeekableStream
+
+NS_IMETHODIMP
+InputStreamLengthWrapper::Seek(int32_t aWhence, int64_t aOffset)
+{
+  NS_ENSURE_STATE(mInputStream);
+  NS_ENSURE_STATE(mWeakSeekableInputStream);
+
+  mConsumed = true;
+  return mWeakSeekableInputStream->Seek(aWhence, aOffset);
+}
+
+NS_IMETHODIMP
+InputStreamLengthWrapper::Tell(int64_t *aResult)
+{
+  NS_ENSURE_STATE(mInputStream);
+  NS_ENSURE_STATE(mWeakSeekableInputStream);
+
+  return mWeakSeekableInputStream->Tell(aResult);
+}
+
+NS_IMETHODIMP
+InputStreamLengthWrapper::SetEOF()
+{
+  NS_ENSURE_STATE(mInputStream);
+  NS_ENSURE_STATE(mWeakSeekableInputStream);
+
+  mConsumed = true;
+  return mWeakSeekableInputStream->SetEOF();
+}
+
+// nsIInputStreamLength
+
+NS_IMETHODIMP
+InputStreamLengthWrapper::Length(int64_t* aLength)
+{
+  NS_ENSURE_STATE(mInputStream);
+  *aLength = mLength;
+  return NS_OK;
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/xpcom/io/InputStreamLengthWrapper.h
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 InputStreamLengthWrapper_h
+#define InputStreamLengthWrapper_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/Mutex.h"
+#include "nsCOMPtr.h"
+#include "nsIAsyncInputStream.h"
+#include "nsICloneableInputStream.h"
+#include "nsIIPCSerializableInputStream.h"
+#include "nsISeekableStream.h"
+#include "nsIInputStreamLength.h"
+
+namespace mozilla {
+
+// A wrapper keeps an inputStream together with its length.
+// This class can be used for nsIInputStreams that do not implement
+// nsIInputStreamLength.
+
+class InputStreamLengthWrapper final : public nsIAsyncInputStream
+                                     , public nsICloneableInputStream
+                                     , public nsIIPCSerializableInputStream
+                                     , public nsISeekableStream
+                                     , public nsIInputStreamCallback
+                                     , public nsIInputStreamLength
+{
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSIINPUTSTREAM
+  NS_DECL_NSIASYNCINPUTSTREAM
+  NS_DECL_NSICLONEABLEINPUTSTREAM
+  NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
+  NS_DECL_NSISEEKABLESTREAM
+  NS_DECL_NSIINPUTSTREAMCALLBACK
+  NS_DECL_NSIINPUTSTREAMLENGTH
+
+  // The length here will be used when nsIInputStreamLength::Length() is called.
+  InputStreamLengthWrapper(already_AddRefed<nsIInputStream> aInputStream,
+                           int64_t aLength);
+
+  // This CTOR is meant to be used just for IPC.
+  InputStreamLengthWrapper();
+
+private:
+  ~InputStreamLengthWrapper();
+
+  void
+  SetSourceStream(already_AddRefed<nsIInputStream> aInputStream);
+
+  nsCOMPtr<nsIInputStream> mInputStream;
+
+  // Raw pointers because these are just QI of mInputStream.
+  nsICloneableInputStream* mWeakCloneableInputStream;
+  nsIIPCSerializableInputStream* mWeakIPCSerializableInputStream;
+  nsISeekableStream* mWeakSeekableInputStream;
+  nsIAsyncInputStream* mWeakAsyncInputStream;
+
+  int64_t mLength;
+  bool mConsumed;
+
+  mozilla::Mutex mMutex;
+
+  // This is used for AsyncWait and it's protected by mutex.
+  nsCOMPtr<nsIInputStreamCallback> mAsyncWaitCallback;
+};
+
+} // mozilla namespace
+
+#endif // InputStreamLengthWrapper_h
--- a/xpcom/io/moz.build
+++ b/xpcom/io/moz.build
@@ -80,28 +80,30 @@ EXPORTS += [
     'nsUnicharInputStream.h',
     'nsWildCard.h',
     'SpecialSystemDirectory.h',
 ]
 
 EXPORTS.mozilla += [
     'Base64.h',
     'InputStreamLengthHelper.h',
+    'InputStreamLengthWrapper.h',
     'NonBlockingAsyncInputStream.h',
     'SlicedInputStream.h',
     'SnappyCompressOutputStream.h',
     'SnappyFrameUtils.h',
     'SnappyUncompressInputStream.h',
 ]
 
 UNIFIED_SOURCES += [
     'Base64.cpp',
     'crc32c.c',
     'FileDescriptorFile.cpp',
     'InputStreamLengthHelper.cpp',
+    'InputStreamLengthWrapper.cpp',
     'NonBlockingAsyncInputStream.cpp',
     'nsAnonymousTemporaryFile.cpp',
     'nsAppFileLocationProvider.cpp',
     'nsBinaryStream.cpp',
     'nsEscape.cpp',
     'nsInputStreamTee.cpp',
     'nsIOUtil.cpp',
     'nsLinebreakConverter.cpp',