Bug 1166504 - Make nsMultiplexInputStream cloneable. r=bkelly,froydnj
☠☠ backed out by 3ce3e50dae0f ☠ ☠
authorNikhil Marathe <nsm.nikhil@gmail.com>
Tue, 19 May 2015 14:28:32 -0700
changeset 277394 47a1034141772d1b6e26e700d753b9c6c08c5143
parent 277393 3796a4c827e4644d7f49637e4c5441fa664aa4e8
child 277395 c0e7bdb0da7bd0243772e7a4677be4df24a63bf9
push id4932
push userjlund@mozilla.com
push dateMon, 10 Aug 2015 18:23:06 +0000
treeherdermozilla-beta@6dd5a4f5f745 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbkelly, froydnj
bugs1166504
milestone41.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 1166504 - Make nsMultiplexInputStream cloneable. r=bkelly,froydnj
xpcom/io/nsMultiplexInputStream.cpp
xpcom/tests/gtest/TestCloneInputStream.cpp
--- a/xpcom/io/nsMultiplexInputStream.cpp
+++ b/xpcom/io/nsMultiplexInputStream.cpp
@@ -11,16 +11,17 @@
 
 #include "mozilla/Attributes.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/Mutex.h"
 
 #include "base/basictypes.h"
 
 #include "nsMultiplexInputStream.h"
+#include "nsICloneableInputStream.h"
 #include "nsIMultiplexInputStream.h"
 #include "nsISeekableStream.h"
 #include "nsCOMPtr.h"
 #include "nsCOMArray.h"
 #include "nsIClassInfoImpl.h"
 #include "nsIIPCSerializableInputStream.h"
 #include "mozilla/ipc/InputStreamUtils.h"
 
@@ -28,25 +29,27 @@ using namespace mozilla;
 using namespace mozilla::ipc;
 
 using mozilla::DeprecatedAbs;
 
 class nsMultiplexInputStream final
   : public nsIMultiplexInputStream
   , public nsISeekableStream
   , public nsIIPCSerializableInputStream
+  , public nsICloneableInputStream
 {
 public:
   nsMultiplexInputStream();
 
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIINPUTSTREAM
   NS_DECL_NSIMULTIPLEXINPUTSTREAM
   NS_DECL_NSISEEKABLESTREAM
   NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
+  NS_DECL_NSICLONEABLEINPUTSTREAM
 
 private:
   ~nsMultiplexInputStream()
   {
   }
 
   struct MOZ_STACK_CLASS ReadSegmentsState
   {
@@ -73,17 +76,18 @@ NS_IMPL_RELEASE(nsMultiplexInputStream)
 
 NS_IMPL_CLASSINFO(nsMultiplexInputStream, nullptr, nsIClassInfo::THREADSAFE,
                   NS_MULTIPLEXINPUTSTREAM_CID)
 
 NS_IMPL_QUERY_INTERFACE_CI(nsMultiplexInputStream,
                            nsIMultiplexInputStream,
                            nsIInputStream,
                            nsISeekableStream,
-                           nsIIPCSerializableInputStream)
+                           nsIIPCSerializableInputStream,
+                           nsICloneableInputStream)
 NS_IMPL_CI_INTERFACE_GETTER(nsMultiplexInputStream,
                             nsIMultiplexInputStream,
                             nsIInputStream,
                             nsISeekableStream)
 
 static nsresult
 AvailableMaybeSeek(nsIInputStream* aStream, uint64_t* aResult)
 {
@@ -774,8 +778,70 @@ nsMultiplexInputStream::Deserialize(cons
   }
 
   mCurrentStream = params.currentStream();
   mStatus = params.status();
   mStartedReadingCurrent = params.startedReadingCurrent();
 
   return true;
 }
+
+NS_IMETHODIMP
+nsMultiplexInputStream::GetCloneable(bool* aCloneable)
+{
+  MutexAutoLock lock(mLock);
+  //XXXnsm Cloning a multiplex stream which has started reading is not permitted
+  //right now.
+  if (mCurrentStream > 0 || mStartedReadingCurrent) {
+    *aCloneable = false;
+    return NS_OK;
+  }
+
+  uint32_t len = mStreams.Length();
+  for (uint32_t i = 0; i < len; ++i) {
+    nsCOMPtr<nsICloneableInputStream> cis = do_QueryInterface(mStreams[i]);
+    if (!cis || !cis->GetCloneable()) {
+      *aCloneable = false;
+      return NS_OK;
+    }
+  }
+
+  *aCloneable = true;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMultiplexInputStream::Clone(nsIInputStream** aClone)
+{
+  MutexAutoLock lock(mLock);
+
+  //XXXnsm Cloning a multiplex stream which has started reading is not permitted
+  //right now.
+  if (mCurrentStream > 0 || mStartedReadingCurrent) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsRefPtr<nsMultiplexInputStream> clone = new nsMultiplexInputStream();
+
+  nsresult rv;
+  uint32_t len = mStreams.Length();
+  for (uint32_t i = 0; i < len; ++i) {
+    nsCOMPtr<nsICloneableInputStream> substream = do_QueryInterface(mStreams[i]);
+    if (NS_WARN_IF(!substream)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    nsCOMPtr<nsIInputStream> clonedSubstream;
+    rv = substream->Clone(getter_AddRefs(clonedSubstream));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    rv = clone->AppendStream(clonedSubstream);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  clone.forget(aClone);
+  return NS_OK;
+}
+
--- a/xpcom/tests/gtest/TestCloneInputStream.cpp
+++ b/xpcom/tests/gtest/TestCloneInputStream.cpp
@@ -3,16 +3,17 @@
 /* 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 "gtest/gtest.h"
 #include "Helpers.h"
 #include "mozilla/unused.h"
 #include "nsICloneableInputStream.h"
+#include "nsIMultiplexInputStream.h"
 #include "nsNetUtil.h"
 #include "nsStreamUtils.h"
 #include "nsStringStream.h"
 
 TEST(CloneInputStream, InvalidInput)
 {
   nsCOMPtr<nsIInputStream> clone;
   nsresult rv = NS_CloneInputStream(nullptr, getter_AddRefs(clone));
@@ -106,8 +107,93 @@ TEST(CloneInputStream, NonCloneableInput
     mozilla::unused << PR_Sleep(PR_INTERVAL_NO_WAIT);
     rv = stream->Available(&available);
     ASSERT_TRUE(NS_SUCCEEDED(rv));
   } while(available < inputString.Length());
 
   testing::ConsumeAndValidateStream(stream, inputString);
   testing::ConsumeAndValidateStream(clone, inputString);
 }
+
+TEST(CloneInputStream, CloneMultiplexStream)
+{
+  nsCOMPtr<nsIMultiplexInputStream> stream =
+    do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1");
+  ASSERT_TRUE(stream);
+
+  nsTArray<char> inputData;
+  testing::CreateData(1024, inputData);
+  for (uint32_t i = 0; i < 2; ++i) {
+    nsCString inputString(inputData.Elements(), inputData.Length());
+
+    nsCOMPtr<nsIInputStream> base;
+    nsresult rv = NS_NewCStringInputStream(getter_AddRefs(base), inputString);
+    ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+    rv = stream->AppendStream(base);
+    ASSERT_TRUE(NS_SUCCEEDED(rv));
+  }
+
+  // Unread stream should clone successfully.
+  nsTArray<char> doubled;
+  doubled.AppendElements(inputData);
+  doubled.AppendElements(inputData);
+
+  nsCOMPtr<nsIInputStream> clone;
+  nsresult rv = NS_CloneInputStream(stream, getter_AddRefs(clone));
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+  testing::ConsumeAndValidateStream(clone, doubled);
+
+  // Stream that has been read should fail.
+  nsAutoPtr<char> buffer(new char[512]);
+  uint32_t read;
+  rv = stream->Read(buffer, 512, &read);
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+  nsCOMPtr<nsIInputStream> clone2;
+  rv = NS_CloneInputStream(stream, getter_AddRefs(clone2));
+  ASSERT_TRUE(NS_FAILED(rv));
+}
+
+TEST(CloneInputStream, CloneMultiplexStreamPartial)
+{
+  nsCOMPtr<nsIMultiplexInputStream> stream =
+    do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1");
+  ASSERT_TRUE(stream);
+
+  nsTArray<char> inputData;
+  testing::CreateData(1024, inputData);
+  for (uint32_t i = 0; i < 2; ++i) {
+    nsCString inputString(inputData.Elements(), inputData.Length());
+
+    nsCOMPtr<nsIInputStream> base;
+    nsresult rv = NS_NewCStringInputStream(getter_AddRefs(base), inputString);
+    ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+    rv = stream->AppendStream(base);
+    ASSERT_TRUE(NS_SUCCEEDED(rv));
+  }
+
+  // Fail when first stream read, but second hasn't been started.
+  nsAutoPtr<char> buffer(new char[1024]);
+  uint32_t read;
+  nsresult rv = stream->Read(buffer, 1024, &read);
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+  nsCOMPtr<nsIInputStream> clone;
+  rv = NS_CloneInputStream(stream, getter_AddRefs(clone));
+  ASSERT_TRUE(NS_FAILED(rv));
+
+  // Fail after beginning read of second stream.
+  rv = stream->Read(buffer, 512, &read);
+  ASSERT_TRUE(NS_SUCCEEDED(rv) && read == 512);
+
+  rv = NS_CloneInputStream(stream, getter_AddRefs(clone));
+  ASSERT_TRUE(NS_FAILED(rv));
+
+  // Fail at the end.
+  nsAutoCString consumed;
+  rv = NS_ConsumeStream(stream, UINT32_MAX, consumed);
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+  rv = NS_CloneInputStream(stream, getter_AddRefs(clone));
+  ASSERT_TRUE(NS_FAILED(rv));
+}