Bug 1403771 - SlicedInputStream takes ownership of the underlying stream and it propagates the Close() call, r=smaug
authorAndrea Marchesini <amarchesini@mozilla.com>
Thu, 05 Oct 2017 07:38:48 +0200
changeset 384657 ad8bee818a39874e12c865386cf4ea1941e1b17f
parent 384656 950c069a0192372cae24392f87ab24bdc2ddf2d4
child 384658 6040c98bc11497443e2c2d95e38f18699c048da7
push id32634
push userkwierso@gmail.com
push dateFri, 06 Oct 2017 19:55:44 +0000
treeherdermozilla-central@2d7b8b5dd174 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1403771
milestone58.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 1403771 - SlicedInputStream takes ownership of the underlying stream and it propagates the Close() call, r=smaug
dom/file/FileBlobImpl.cpp
dom/file/StreamBlobImpl.cpp
dom/file/StreamBlobImpl.h
dom/file/ipc/IPCBlobInputStream.cpp
dom/file/ipc/IPCBlobInputStream.h
dom/file/ipc/IPCBlobInputStreamChild.cpp
dom/file/ipc/IPCBlobInputStreamStorage.cpp
xpcom/io/SlicedInputStream.cpp
xpcom/io/SlicedInputStream.h
xpcom/tests/gtest/TestSlicedInputStream.cpp
--- a/dom/file/FileBlobImpl.cpp
+++ b/dom/file/FileBlobImpl.cpp
@@ -249,17 +249,17 @@ FileBlobImpl::CreateInputStream(nsIInput
   }
 
   if (mWholeFile) {
     stream.forget(aStream);
     return;
   }
 
   RefPtr<SlicedInputStream> slicedInputStream =
-    new SlicedInputStream(stream, mStart, mLength);
+    new SlicedInputStream(stream.forget(), mStart, mLength);
   slicedInputStream.forget(aStream);
 }
 
 bool
 FileBlobImpl::IsDirectory() const
 {
   bool isDirectory = false;
   if (mFile) {
--- a/dom/file/StreamBlobImpl.cpp
+++ b/dom/file/StreamBlobImpl.cpp
@@ -44,27 +44,16 @@ StreamBlobImpl::StreamBlobImpl(nsIInputS
   : BaseBlobImpl(aContentType, aLength)
   , mInputStream(aInputStream)
   , mIsDirectory(false)
   , mFileId(-1)
 {
   mImmutable = true;
 }
 
-StreamBlobImpl::StreamBlobImpl(StreamBlobImpl* aOther,
-                               const nsAString& aContentType,
-                               uint64_t aStart, uint64_t aLength)
-  : BaseBlobImpl(aContentType, aOther->mStart + aStart, aLength)
-  , mInputStream(new SlicedInputStream(aOther->mInputStream, aStart, aLength))
-  , mIsDirectory(false)
-  , mFileId(-1)
-{
-  mImmutable = true;
-}
-
 StreamBlobImpl::StreamBlobImpl(nsIInputStream* aInputStream,
                                const nsAString& aName,
                                const nsAString& aContentType,
                                int64_t aLastModifiedDate,
                                uint64_t aLength)
   : BaseBlobImpl(aName, aContentType, aLength, aLastModifiedDate)
   , mInputStream(aInputStream)
   , mIsDirectory(false)
@@ -101,32 +90,39 @@ already_AddRefed<BlobImpl>
 StreamBlobImpl::CreateSlice(uint64_t aStart, uint64_t aLength,
                             const nsAString& aContentType, ErrorResult& aRv)
 {
   if (!aLength) {
     RefPtr<BlobImpl> impl = new EmptyBlobImpl(aContentType);
     return impl.forget();
   }
 
+  nsCOMPtr<nsIInputStream> clonedStream;
+
   nsCOMPtr<nsICloneableInputStreamWithRange> stream =
     do_QueryInterface(mInputStream);
   if (stream) {
-    nsCOMPtr<nsIInputStream> clonedStream;
     aRv = stream->CloneWithRange(aStart, aLength, getter_AddRefs(clonedStream));
     if (NS_WARN_IF(aRv.Failed())) {
       return nullptr;
     }
+  } else {
+    CreateInputStream(getter_AddRefs(clonedStream), aRv);
+    if (NS_WARN_IF(aRv.Failed())) {
+      return nullptr;
+    }
 
-    RefPtr<BlobImpl> impl =
-      new StreamBlobImpl(clonedStream, aContentType, aLength);
-    return impl.forget();
+    clonedStream =
+      new SlicedInputStream(clonedStream.forget(), aStart, aLength);
   }
 
-  RefPtr<BlobImpl> impl;
-    impl = new StreamBlobImpl(this, aContentType, aStart, aLength);
+  MOZ_ASSERT(clonedStream);
+
+  RefPtr<BlobImpl> impl =
+    new StreamBlobImpl(clonedStream, aContentType, aLength);
   return impl.forget();
 }
 
 void
 StreamBlobImpl::MaybeRegisterMemoryReporter()
 {
   // We report only stringInputStream.
   nsCOMPtr<nsIStringInputStream> stringInputStream =
--- a/dom/file/StreamBlobImpl.h
+++ b/dom/file/StreamBlobImpl.h
@@ -85,21 +85,16 @@ private:
                  uint64_t aLength);
 
   StreamBlobImpl(nsIInputStream* aInputStream,
                  const nsAString& aName,
                  const nsAString& aContentType,
                  int64_t aLastModifiedDate,
                  uint64_t aLength);
 
-  StreamBlobImpl(StreamBlobImpl* aOther,
-                 const nsAString& aContentType,
-                 uint64_t aStart,
-                 uint64_t aLength);
-
   ~StreamBlobImpl();
 
   void MaybeRegisterMemoryReporter();
 
   nsCOMPtr<nsIInputStream> mInputStream;
 
   nsString mFullPath;
   bool mIsDirectory;
--- a/dom/file/ipc/IPCBlobInputStream.cpp
+++ b/dom/file/ipc/IPCBlobInputStream.cpp
@@ -377,40 +377,43 @@ IPCBlobInputStream::AsyncWait(nsIInputSt
   // Stream is closed.
   default:
     MOZ_ASSERT(mState == eClosed);
     return NS_BASE_STREAM_CLOSED;
   }
 }
 
 void
-IPCBlobInputStream::StreamReady(nsIInputStream* aInputStream)
+IPCBlobInputStream::StreamReady(already_AddRefed<nsIInputStream> aInputStream)
 {
+  nsCOMPtr<nsIInputStream> inputStream = Move(aInputStream);
+
   // We have been closed in the meantime.
   if (mState == eClosed) {
-    if (aInputStream) {
-      aInputStream->Close();
+    if (inputStream) {
+      inputStream->Close();
     }
     return;
   }
 
-  // If aInputStream is null, it means that the serialization went wrong or the
+  // If inputStream 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) {
+  if (!inputStream) {
     return;
   }
 
   // Now it's the right time to apply a slice if needed.
   if (mStart > 0 || mLength < mActor->Size()) {
-    aInputStream = new SlicedInputStream(aInputStream, mStart, mLength);
+    inputStream =
+      new SlicedInputStream(inputStream.forget(), mStart, mLength);
   }
 
-  mRemoteStream = aInputStream;
+  mRemoteStream = inputStream;
 
   MOZ_ASSERT(mState == ePending);
   mState = eRunning;
 
   nsCOMPtr<nsIFileMetadataCallback> fileMetadataCallback;
   fileMetadataCallback.swap(mFileMetadataCallback);
 
   nsCOMPtr<nsIEventTarget> fileMetadataCallbackEventTarget;
@@ -471,17 +474,18 @@ IPCBlobInputStream::InitWithExistingRang
   mLength = aLength;
 
   // In the child, we slice in StreamReady() when we set mState to eRunning.
   // But in the parent, we start out eRunning, so it's necessary to slice the
   // stream as soon as we have the information during the initialization phase
   // because the stream is immediately consumable.
   if (mState == eRunning && mRemoteStream && XRE_IsParentProcess() &&
       (mStart > 0 || mLength < mActor->Size())) {
-    mRemoteStream = new SlicedInputStream(mRemoteStream, mStart, mLength);
+    mRemoteStream =
+      new SlicedInputStream(mRemoteStream.forget(), mStart, mLength);
   }
 }
 
 // nsIInputStreamCallback
 
 NS_IMETHODIMP
 IPCBlobInputStream::OnInputStreamReady(nsIAsyncInputStream* aStream)
 {
--- a/dom/file/ipc/IPCBlobInputStream.h
+++ b/dom/file/ipc/IPCBlobInputStream.h
@@ -33,17 +33,17 @@ public:
   NS_DECL_NSICLONEABLEINPUTSTREAMWITHRANGE
   NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
   NS_DECL_NSIFILEMETADATA
   NS_DECL_NSIASYNCFILEMETADATA
 
   explicit IPCBlobInputStream(IPCBlobInputStreamChild* aActor);
 
   void
-  StreamReady(nsIInputStream* aInputStream);
+  StreamReady(already_AddRefed<nsIInputStream> aInputStream);
 
 private:
   ~IPCBlobInputStream();
 
   nsresult
   MaybeExecuteInputStreamCallback(nsIInputStreamCallback* aCallback,
                                   nsIEventTarget* aEventTarget);
 
--- a/dom/file/ipc/IPCBlobInputStreamChild.cpp
+++ b/dom/file/ipc/IPCBlobInputStreamChild.cpp
@@ -66,29 +66,29 @@ private:
 };
 
 // When the stream has been received from the parent, we inform the
 // IPCBlobInputStream.
 class StreamReadyRunnable final : public CancelableRunnable
 {
 public:
   StreamReadyRunnable(IPCBlobInputStream* aDestinationStream,
-                      nsIInputStream* aCreatedStream)
+                      already_AddRefed<nsIInputStream> aCreatedStream)
     : CancelableRunnable("dom::StreamReadyRunnable")
     , mDestinationStream(aDestinationStream)
-    , mCreatedStream(aCreatedStream)
+    , mCreatedStream(Move(aCreatedStream))
   {
     MOZ_ASSERT(mDestinationStream);
     // mCreatedStream can be null.
   }
 
   NS_IMETHOD
   Run() override
   {
-    mDestinationStream->StreamReady(mCreatedStream);
+    mDestinationStream->StreamReady(mCreatedStream.forget());
     return NS_OK;
   }
 
 private:
   RefPtr<IPCBlobInputStream> mDestinationStream;
   nsCOMPtr<nsIInputStream> mCreatedStream;
 };
 
@@ -312,17 +312,17 @@ IPCBlobInputStreamChild::RecvStreamReady
 
     pendingStream = mPendingOperations[0].mStream;
     eventTarget = mPendingOperations[0].mEventTarget;
 
     mPendingOperations.RemoveElementAt(0);
   }
 
   RefPtr<StreamReadyRunnable> runnable =
-    new StreamReadyRunnable(pendingStream, stream);
+    new StreamReadyRunnable(pendingStream, stream.forget());
 
   // If IPCBlobInputStream::AsyncWait() has been executed without passing an
   // event target, we run the callback synchronous because any thread could be
   // result to be the wrong one. See more in nsIAsyncInputStream::asyncWait
   // documentation.
   if (eventTarget) {
     eventTarget->Dispatch(runnable, NS_DISPATCH_NORMAL);
   } else {
--- a/dom/file/ipc/IPCBlobInputStreamStorage.cpp
+++ b/dom/file/ipc/IPCBlobInputStreamStorage.cpp
@@ -167,17 +167,18 @@ IPCBlobInputStreamStorage::GetStream(con
       return;
     }
 
     data->mInputStream = replacementStream;
   }
 
   // Now it's the right time to apply a slice if needed.
   if (aStart > 0 || aLength < size) {
-    clonedStream = new SlicedInputStream(clonedStream, aStart, aLength);
+    clonedStream =
+      new SlicedInputStream(clonedStream.forget(), aStart, aLength);
   }
 
   clonedStream.forget(aInputStream);
 }
 
 void
 IPCBlobInputStreamStorage::StoreCallback(const nsID& aID,
                                          IPCBlobInputStreamParentCallback* aCallback)
--- a/xpcom/io/SlicedInputStream.cpp
+++ b/xpcom/io/SlicedInputStream.cpp
@@ -23,31 +23,31 @@ NS_INTERFACE_MAP_BEGIN(SlicedInputStream
                                      mWeakSeekableInputStream || !mInputStream)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAsyncInputStream,
                                      mWeakAsyncInputStream || !mInputStream)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamCallback,
                                      mWeakAsyncInputStream || !mInputStream)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
 NS_INTERFACE_MAP_END
 
-SlicedInputStream::SlicedInputStream(nsIInputStream* aInputStream,
+SlicedInputStream::SlicedInputStream(already_AddRefed<nsIInputStream> aInputStream,
                                      uint64_t aStart, uint64_t aLength)
   : mWeakCloneableInputStream(nullptr)
   , mWeakIPCSerializableInputStream(nullptr)
   , mWeakSeekableInputStream(nullptr)
   , mWeakAsyncInputStream(nullptr)
   , mStart(aStart)
   , mLength(aLength)
   , mCurPos(0)
   , mClosed(false)
   , mAsyncWaitFlags(0)
   , mAsyncWaitRequestedCount(0)
 {
-  MOZ_ASSERT(aInputStream);
-  SetSourceStream(aInputStream);
+  nsCOMPtr<nsIInputStream> inputStream = mozilla::Move(aInputStream);
+  SetSourceStream(inputStream.forget());
 }
 
 SlicedInputStream::SlicedInputStream()
   : mWeakCloneableInputStream(nullptr)
   , mWeakIPCSerializableInputStream(nullptr)
   , mWeakSeekableInputStream(nullptr)
   , mWeakAsyncInputStream(nullptr)
   , mStart(0)
@@ -57,56 +57,55 @@ SlicedInputStream::SlicedInputStream()
   , mAsyncWaitFlags(0)
   , mAsyncWaitRequestedCount(0)
 {}
 
 SlicedInputStream::~SlicedInputStream()
 {}
 
 void
-SlicedInputStream::SetSourceStream(nsIInputStream* aInputStream)
+SlicedInputStream::SetSourceStream(already_AddRefed<nsIInputStream> aInputStream)
 {
   MOZ_ASSERT(!mInputStream);
-  MOZ_ASSERT(aInputStream);
 
-  mInputStream = aInputStream;
+  mInputStream = mozilla::Move(aInputStream);
 
   nsCOMPtr<nsICloneableInputStream> cloneableStream =
-    do_QueryInterface(aInputStream);
-  if (cloneableStream && SameCOMIdentity(aInputStream, cloneableStream)) {
+    do_QueryInterface(mInputStream);
+  if (cloneableStream && SameCOMIdentity(mInputStream, cloneableStream)) {
     mWeakCloneableInputStream = cloneableStream;
   }
 
   nsCOMPtr<nsIIPCSerializableInputStream> serializableStream =
-    do_QueryInterface(aInputStream);
+    do_QueryInterface(mInputStream);
   if (serializableStream &&
-      SameCOMIdentity(aInputStream, serializableStream)) {
+      SameCOMIdentity(mInputStream, serializableStream)) {
     mWeakIPCSerializableInputStream = serializableStream;
   }
 
   nsCOMPtr<nsISeekableStream> seekableStream =
-    do_QueryInterface(aInputStream);
-  if (seekableStream && SameCOMIdentity(aInputStream, seekableStream)) {
+    do_QueryInterface(mInputStream);
+  if (seekableStream && SameCOMIdentity(mInputStream, seekableStream)) {
     mWeakSeekableInputStream = seekableStream;
   }
 
   nsCOMPtr<nsIAsyncInputStream> asyncInputStream =
-    do_QueryInterface(aInputStream);
-  if (asyncInputStream && SameCOMIdentity(aInputStream, asyncInputStream)) {
+    do_QueryInterface(mInputStream);
+  if (asyncInputStream && SameCOMIdentity(mInputStream, asyncInputStream)) {
     mWeakAsyncInputStream = asyncInputStream;
   }
 }
 
 NS_IMETHODIMP
 SlicedInputStream::Close()
 {
   NS_ENSURE_STATE(mInputStream);
 
   mClosed = true;
-  return NS_OK;
+  return mInputStream->Close();
 }
 
 // nsIInputStream interface
 
 NS_IMETHODIMP
 SlicedInputStream::Available(uint64_t* aLength)
 {
   NS_ENSURE_STATE(mInputStream);
@@ -221,30 +220,31 @@ SlicedInputStream::Clone(nsIInputStream*
 
   nsCOMPtr<nsIInputStream> clonedStream;
   nsresult rv = mWeakCloneableInputStream->Clone(getter_AddRefs(clonedStream));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   nsCOMPtr<nsIInputStream> sis =
-    new SlicedInputStream(clonedStream, mStart, mLength);
+    new SlicedInputStream(clonedStream.forget(), mStart, mLength);
 
   sis.forget(aResult);
   return NS_OK;
 }
 
 // nsIAsyncInputStream interface
 
 NS_IMETHODIMP
 SlicedInputStream::CloseWithStatus(nsresult aStatus)
 {
   NS_ENSURE_STATE(mInputStream);
   NS_ENSURE_STATE(mWeakAsyncInputStream);
 
+  mClosed = true;
   return mWeakAsyncInputStream->CloseWithStatus(aStatus);
 }
 
 NS_IMETHODIMP
 SlicedInputStream::AsyncWait(nsIInputStreamCallback* aCallback,
                              uint32_t aFlags,
                              uint32_t aRequestedCount,
                              nsIEventTarget* aEventTarget)
@@ -379,17 +379,17 @@ SlicedInputStream::Deserialize(const moz
   nsCOMPtr<nsIInputStream> stream =
     InputStreamHelper::DeserializeInputStream(params.stream(),
                                               aFileDescriptors);
   if (!stream) {
     NS_WARNING("Deserialize failed!");
     return false;
   }
 
-  SetSourceStream(stream);
+  SetSourceStream(stream.forget());
 
   mStart = params.start();
   mLength = params.length();
   mCurPos = params.curPos();
   mClosed = params.closed();
 
   return true;
 }
--- a/xpcom/io/SlicedInputStream.h
+++ b/xpcom/io/SlicedInputStream.h
@@ -36,27 +36,27 @@ public:
   // than aStart bytes, reading from SlicedInputStream returns no data.  If
   // aInputStream contains more than aStart bytes, but fewer than aStart +
   // aLength bytes, reading from SlicedInputStream returns as many bytes as can
   // be consumed from aInputStream after reading aLength bytes.
   //
   // aInputStream should not be read from after constructing a
   // SlicedInputStream wrapper around it.
 
-  SlicedInputStream(nsIInputStream* aInputStream,
+  SlicedInputStream(already_AddRefed<nsIInputStream> aInputStream,
                     uint64_t aStart, uint64_t aLength);
 
   // This CTOR is meant to be used just for IPC.
   SlicedInputStream();
 
 private:
   ~SlicedInputStream();
 
   void
-  SetSourceStream(nsIInputStream* aInputStream);
+  SetSourceStream(already_AddRefed<nsIInputStream> aInputStream);
 
   nsresult
   RunAsyncWaitCallback();
 
   nsCOMPtr<nsIInputStream> mInputStream;
 
   // Raw pointers because these are just QI of mInputStream.
   nsICloneableInputStream* mWeakCloneableInputStream;
--- a/xpcom/tests/gtest/TestSlicedInputStream.cpp
+++ b/xpcom/tests/gtest/TestSlicedInputStream.cpp
@@ -132,32 +132,32 @@ CreateSeekableStreams(uint32_t aSize, ui
 {
   aBuffer.SetLength(aSize);
   for (uint32_t i = 0; i < aSize; ++i) {
     aBuffer.BeginWriting()[i] = i % 10;
   }
 
   nsCOMPtr<nsIInputStream> stream;
   NS_NewCStringInputStream(getter_AddRefs(stream), aBuffer);
-  return new SlicedInputStream(stream, aStart, aLength);
+  return new SlicedInputStream(stream.forget(), aStart, aLength);
 }
 
 // Helper function for creating a non-seekable nsIInputStream + a
 // SlicedInputStream.
 SlicedInputStream*
 CreateNonSeekableStreams(uint32_t aSize, uint64_t aStart, uint64_t aLength,
                          nsCString& aBuffer)
 {
   aBuffer.SetLength(aSize);
   for (uint32_t i = 0; i < aSize; ++i) {
     aBuffer.BeginWriting()[i] = i % 10;
   }
 
   RefPtr<NonSeekableStringStream> stream = new NonSeekableStringStream(aBuffer);
-  return new SlicedInputStream(stream, aStart, aLength);
+  return new SlicedInputStream(stream.forget(), aStart, aLength);
 }
 
 // Same start, same length.
 TEST(TestSlicedInputStream, Simple) {
   const size_t kBufSize = 4096;
 
   nsCString buf;
   RefPtr<SlicedInputStream> sis =
@@ -329,20 +329,22 @@ TEST(TestSlicedInputStream, Length0) {
   ASSERT_EQ((uint64_t)0, count);
 }
 
 // Seek test NS_SEEK_SET
 TEST(TestSlicedInputStream, Seek_SET) {
   nsCString buf;
   buf.AssignLiteral("Hello world");
 
-  nsCOMPtr<nsIInputStream> stream;
-  NS_NewCStringInputStream(getter_AddRefs(stream), buf);
-
-  RefPtr<SlicedInputStream> sis = new SlicedInputStream(stream, 1, buf.Length());
+  RefPtr<SlicedInputStream> sis;
+  {
+    nsCOMPtr<nsIInputStream> stream;
+    NS_NewCStringInputStream(getter_AddRefs(stream), buf);
+    sis = new SlicedInputStream(stream.forget(), 1, buf.Length());
+  }
 
   ASSERT_EQ(NS_OK, sis->Seek(nsISeekableStream::NS_SEEK_SET, 1));
 
   uint64_t length;
   ASSERT_EQ(NS_OK, sis->Available(&length));
   ASSERT_EQ((uint64_t)buf.Length() - 2, length);
 
   char buf2[4096];
@@ -352,20 +354,23 @@ TEST(TestSlicedInputStream, Seek_SET) {
   ASSERT_EQ(0, strncmp(buf2, "llo world", count));
 }
 
 // Seek test NS_SEEK_CUR
 TEST(TestSlicedInputStream, Seek_CUR) {
   nsCString buf;
   buf.AssignLiteral("Hello world");
 
-  nsCOMPtr<nsIInputStream> stream;
-  NS_NewCStringInputStream(getter_AddRefs(stream), buf);
+  RefPtr<SlicedInputStream> sis;
+  {
+    nsCOMPtr<nsIInputStream> stream;
+    NS_NewCStringInputStream(getter_AddRefs(stream), buf);
 
-  RefPtr<SlicedInputStream> sis = new SlicedInputStream(stream, 1, buf.Length());
+    sis = new SlicedInputStream(stream.forget(), 1, buf.Length());
+  }
 
   ASSERT_EQ(NS_OK, sis->Seek(nsISeekableStream::NS_SEEK_CUR, 1));
 
   uint64_t length;
   ASSERT_EQ(NS_OK, sis->Available(&length));
   ASSERT_EQ((uint64_t)buf.Length() - 2, length);
 
   char buf2[3];
@@ -381,23 +386,27 @@ TEST(TestSlicedInputStream, Seek_CUR) {
   ASSERT_EQ(0, strncmp(buf2, "wor", count));
 }
 
 // Seek test NS_SEEK_END - length > real one
 TEST(TestSlicedInputStream, Seek_END_Bigger) {
   nsCString buf;
   buf.AssignLiteral("Hello world");
 
-  nsCOMPtr<nsIInputStream> stream;
-  NS_NewCStringInputStream(getter_AddRefs(stream), buf);
+  RefPtr<SlicedInputStream> sis;
+  {
+    nsCOMPtr<nsIInputStream> stream;
+    NS_NewCStringInputStream(getter_AddRefs(stream), buf);
 
-  RefPtr<SlicedInputStream> sis = new SlicedInputStream(stream, 2, buf.Length());
+    sis = new SlicedInputStream(stream.forget(), 2, buf.Length());
+  }
 
   ASSERT_EQ(NS_OK, sis->Seek(nsISeekableStream::NS_SEEK_END, -5));
 
+  nsCOMPtr<nsIInputStream> stream;
   NS_NewCStringInputStream(getter_AddRefs(stream), buf);
   nsCOMPtr<nsISeekableStream> seekStream = do_QueryInterface(stream);
   ASSERT_EQ(NS_OK, seekStream->Seek(nsISeekableStream::NS_SEEK_END, -5));
 
   uint64_t length;
   ASSERT_EQ(NS_OK, sis->Available(&length));
   ASSERT_EQ((uint64_t)5, length);
 
@@ -415,20 +424,23 @@ TEST(TestSlicedInputStream, Seek_END_Big
   ASSERT_EQ(0, strncmp(buf2, "world", count));
 }
 
 // Seek test NS_SEEK_END - length < real one
 TEST(TestSlicedInputStream, Seek_END_Lower) {
   nsCString buf;
   buf.AssignLiteral("Hello world");
 
-  nsCOMPtr<nsIInputStream> stream;
-  NS_NewCStringInputStream(getter_AddRefs(stream), buf);
+  RefPtr<SlicedInputStream> sis;
+  {
+    nsCOMPtr<nsIInputStream> stream;
+    NS_NewCStringInputStream(getter_AddRefs(stream), buf);
 
-  RefPtr<SlicedInputStream> sis = new SlicedInputStream(stream, 2, 6);
+    sis = new SlicedInputStream(stream.forget(), 2, 6);
+  }
 
   ASSERT_EQ(NS_OK, sis->Seek(nsISeekableStream::NS_SEEK_END, -3));
 
   uint64_t length;
   ASSERT_EQ(NS_OK, sis->Available(&length));
   ASSERT_EQ((uint64_t)3, length);
 
   char buf2[5];
@@ -463,20 +475,24 @@ TEST(TestSlicedInputStream, AsyncInputSt
                             segmentSize, numSegments);
   ASSERT_TRUE(NS_SUCCEEDED(rv));
 
   nsTArray<char> inputData;
   testing::CreateData(segmentSize, inputData);
 
   // We have to wrap the reader because it implements only a partial
   // nsISeekableStream interface. When ::Seek() is called, it does a MOZ_CRASH.
-  RefPtr<NonSeekableStringStream> wrapper =
-    new NonSeekableStringStream(reader);
+  nsCOMPtr<nsIInputStream> sis;
+  {
+    RefPtr<NonSeekableStringStream> wrapper =
+      new NonSeekableStringStream(reader);
 
-  nsCOMPtr<nsIInputStream> sis = new SlicedInputStream(wrapper, 500, 500);
+    sis = new SlicedInputStream(wrapper.forget(), 500, 500);
+  }
+
   nsCOMPtr<nsIAsyncInputStream> async = do_QueryInterface(sis);
   ASSERT_TRUE(!!async);
 
   RefPtr<testing::InputStreamCallback> cb =
     new testing::InputStreamCallback();
 
   rv = async->AsyncWait(cb, 0, 0, nullptr);
   ASSERT_TRUE(NS_SUCCEEDED(rv));