forked from mirrors/gecko-dev
		
	This combines the multiple fields or variants which were previously used to track sided types like protocol types into a single field wrapped with a SideVariant. This will be used in the next part to avoid the need for default constructors for actor types allowing the proper types to be used. Differential Revision: https://phabricator.services.mozilla.com/D168879
		
			
				
	
	
		
			620 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			620 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* -*- 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 "mozilla/dom/cache/ReadStream.h"
 | 
						|
 | 
						|
#include "mozilla/Unused.h"
 | 
						|
#include "mozilla/dom/cache/CacheStreamControlChild.h"
 | 
						|
#include "mozilla/dom/cache/CacheStreamControlParent.h"
 | 
						|
#include "mozilla/dom/cache/CacheTypes.h"
 | 
						|
#include "mozilla/ipc/IPCStreamUtils.h"
 | 
						|
#include "mozilla/SnappyUncompressInputStream.h"
 | 
						|
#include "nsIAsyncInputStream.h"
 | 
						|
#include "nsIThread.h"
 | 
						|
#include "nsStringStream.h"
 | 
						|
#include "nsTArray.h"
 | 
						|
 | 
						|
namespace mozilla::dom::cache {
 | 
						|
 | 
						|
using mozilla::Unused;
 | 
						|
 | 
						|
// ----------------------------------------------------------------------------
 | 
						|
 | 
						|
// The inner stream class.  This is where all of the real work is done.  As
 | 
						|
// an invariant Inner::Close() must be called before ~Inner().  This is
 | 
						|
// guaranteed by our outer ReadStream class.
 | 
						|
class ReadStream::Inner final : public ReadStream::Controllable {
 | 
						|
 public:
 | 
						|
  Inner(StreamControl* aControl, const nsID& aId, nsIInputStream* aStream);
 | 
						|
 | 
						|
  void Serialize(Maybe<CacheReadStream>* aReadStreamOut, ErrorResult& aRv);
 | 
						|
 | 
						|
  void Serialize(CacheReadStream* aReadStreamOut, ErrorResult& aRv);
 | 
						|
 | 
						|
  // ReadStream::Controllable methods
 | 
						|
  virtual void CloseStream() override;
 | 
						|
 | 
						|
  virtual void CloseStreamWithoutReporting() override;
 | 
						|
 | 
						|
  virtual bool HasEverBeenRead() const override;
 | 
						|
 | 
						|
  // Simulate nsIInputStream methods, but we don't actually inherit from it
 | 
						|
  nsresult Close();
 | 
						|
 | 
						|
  nsresult Available(uint64_t* aNumAvailableOut);
 | 
						|
 | 
						|
  nsresult StreamStatus();
 | 
						|
 | 
						|
  nsresult Read(char* aBuf, uint32_t aCount, uint32_t* aNumReadOut);
 | 
						|
 | 
						|
  nsresult ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
 | 
						|
                        uint32_t aCount, uint32_t* aNumReadOut);
 | 
						|
 | 
						|
  nsresult IsNonBlocking(bool* aNonBlockingOut);
 | 
						|
 | 
						|
  NS_DECL_OWNINGTHREAD;
 | 
						|
 | 
						|
  ~Inner();
 | 
						|
 | 
						|
 private:
 | 
						|
  class NoteClosedRunnable;
 | 
						|
  class ForgetRunnable;
 | 
						|
 | 
						|
  void NoteClosed();
 | 
						|
 | 
						|
  void Forget();
 | 
						|
 | 
						|
  void NoteClosedOnOwningThread();
 | 
						|
 | 
						|
  void ForgetOnOwningThread();
 | 
						|
 | 
						|
  nsIInputStream* EnsureStream();
 | 
						|
 | 
						|
  void AsyncOpenStreamOnOwningThread();
 | 
						|
 | 
						|
  void MaybeAbortAsyncOpenStream();
 | 
						|
 | 
						|
  void OpenStreamFailed();
 | 
						|
 | 
						|
  inline SafeRefPtr<Inner> SafeRefPtrFromThis() {
 | 
						|
    return Controllable::SafeRefPtrFromThis().downcast<Inner>();
 | 
						|
  }
 | 
						|
 | 
						|
  // Weak ref to the stream control actor.  The actor will always call either
 | 
						|
  // CloseStream() or CloseStreamWithoutReporting() before it's destroyed.  The
 | 
						|
  // weak ref is cleared in the resulting NoteClosedOnOwningThread() or
 | 
						|
  // ForgetOnOwningThread() method call.
 | 
						|
  StreamControl* mControl;
 | 
						|
 | 
						|
  const nsID mId;
 | 
						|
  nsCOMPtr<nsISerialEventTarget> mOwningEventTarget;
 | 
						|
 | 
						|
  enum State { Open, Closed, NumStates };
 | 
						|
  Atomic<State> mState;
 | 
						|
  Atomic<bool> mHasEverBeenRead;
 | 
						|
  bool mAsyncOpenStarted;
 | 
						|
 | 
						|
  // The wrapped stream objects may not be threadsafe.  We need to be able
 | 
						|
  // to close a stream on our owning thread while an IO thread is simultaneously
 | 
						|
  // reading the same stream.  Therefore, protect all access to these stream
 | 
						|
  // objects with a mutex.
 | 
						|
  Mutex mMutex MOZ_UNANNOTATED;
 | 
						|
  CondVar mCondVar;
 | 
						|
  nsCOMPtr<nsIInputStream> mStream;
 | 
						|
  nsCOMPtr<nsIInputStream> mSnappyStream;
 | 
						|
};
 | 
						|
 | 
						|
// ----------------------------------------------------------------------------
 | 
						|
 | 
						|
// Runnable to notify actors that the ReadStream has closed.  This must
 | 
						|
// be done on the thread associated with the PBackground actor.  Must be
 | 
						|
// cancelable to execute on Worker threads (which can occur when the
 | 
						|
// ReadStream is constructed on a child process Worker thread).
 | 
						|
class ReadStream::Inner::NoteClosedRunnable final : public CancelableRunnable {
 | 
						|
 public:
 | 
						|
  explicit NoteClosedRunnable(SafeRefPtr<ReadStream::Inner> aStream)
 | 
						|
      : CancelableRunnable("dom::cache::ReadStream::Inner::NoteClosedRunnable"),
 | 
						|
        mStream(std::move(aStream)) {}
 | 
						|
 | 
						|
  NS_IMETHOD Run() override {
 | 
						|
    mStream->NoteClosedOnOwningThread();
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
 | 
						|
  // Note, we must proceed with the Run() method since our actor will not
 | 
						|
  // clean itself up until we note that the stream is closed.
 | 
						|
  nsresult Cancel() override {
 | 
						|
    Run();
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
 | 
						|
 private:
 | 
						|
  ~NoteClosedRunnable() = default;
 | 
						|
 | 
						|
  const SafeRefPtr<ReadStream::Inner> mStream;
 | 
						|
};
 | 
						|
 | 
						|
// ----------------------------------------------------------------------------
 | 
						|
 | 
						|
// Runnable to clear actors without reporting that the ReadStream has
 | 
						|
// closed.  Since this can trigger actor destruction, we need to do
 | 
						|
// it on the thread associated with the PBackground actor.  Must be
 | 
						|
// cancelable to execute on Worker threads (which can occur when the
 | 
						|
// ReadStream is constructed on a child process Worker thread).
 | 
						|
class ReadStream::Inner::ForgetRunnable final : public CancelableRunnable {
 | 
						|
 public:
 | 
						|
  explicit ForgetRunnable(SafeRefPtr<ReadStream::Inner> aStream)
 | 
						|
      : CancelableRunnable("dom::cache::ReadStream::Inner::ForgetRunnable"),
 | 
						|
        mStream(std::move(aStream)) {}
 | 
						|
 | 
						|
  NS_IMETHOD Run() override {
 | 
						|
    mStream->ForgetOnOwningThread();
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
 | 
						|
  // Note, we must proceed with the Run() method so that we properly
 | 
						|
  // call RemoveListener on the actor.
 | 
						|
  nsresult Cancel() override {
 | 
						|
    Run();
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
 | 
						|
 private:
 | 
						|
  ~ForgetRunnable() = default;
 | 
						|
 | 
						|
  const SafeRefPtr<ReadStream::Inner> mStream;
 | 
						|
};
 | 
						|
 | 
						|
// ----------------------------------------------------------------------------
 | 
						|
 | 
						|
ReadStream::Inner::Inner(StreamControl* aControl, const nsID& aId,
 | 
						|
                         nsIInputStream* aStream)
 | 
						|
    : mControl(aControl),
 | 
						|
      mId(aId),
 | 
						|
      mOwningEventTarget(GetCurrentSerialEventTarget()),
 | 
						|
      mState(Open),
 | 
						|
      mHasEverBeenRead(false),
 | 
						|
      mAsyncOpenStarted(false),
 | 
						|
      mMutex("dom::cache::ReadStream"),
 | 
						|
      mCondVar(mMutex, "dom::cache::ReadStream"),
 | 
						|
      mStream(aStream),
 | 
						|
      mSnappyStream(aStream ? new SnappyUncompressInputStream(aStream)
 | 
						|
                            : nullptr) {
 | 
						|
  MOZ_DIAGNOSTIC_ASSERT(mControl);
 | 
						|
  mControl->AddReadStream(SafeRefPtrFromThis());
 | 
						|
}
 | 
						|
 | 
						|
void ReadStream::Inner::Serialize(Maybe<CacheReadStream>* aReadStreamOut,
 | 
						|
                                  ErrorResult& aRv) {
 | 
						|
  MOZ_ASSERT(mOwningEventTarget->IsOnCurrentThread());
 | 
						|
  MOZ_DIAGNOSTIC_ASSERT(aReadStreamOut);
 | 
						|
  aReadStreamOut->emplace(CacheReadStream());
 | 
						|
  Serialize(&aReadStreamOut->ref(), aRv);
 | 
						|
}
 | 
						|
 | 
						|
void ReadStream::Inner::Serialize(CacheReadStream* aReadStreamOut,
 | 
						|
                                  ErrorResult& aRv) {
 | 
						|
  MOZ_ASSERT(mOwningEventTarget->IsOnCurrentThread());
 | 
						|
  MOZ_DIAGNOSTIC_ASSERT(aReadStreamOut);
 | 
						|
 | 
						|
  if (mState != Open) {
 | 
						|
    aRv.ThrowTypeError(
 | 
						|
        "Response body is a cache file stream that has already been closed.");
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  MOZ_DIAGNOSTIC_ASSERT(mControl);
 | 
						|
 | 
						|
  aReadStreamOut->id() = mId;
 | 
						|
  mControl->SerializeControl(aReadStreamOut);
 | 
						|
 | 
						|
  {
 | 
						|
    MutexAutoLock lock(mMutex);
 | 
						|
    mControl->SerializeStream(aReadStreamOut, mStream);
 | 
						|
  }
 | 
						|
 | 
						|
  MOZ_DIAGNOSTIC_ASSERT(aReadStreamOut->stream().isNothing() ||
 | 
						|
                        aReadStreamOut->stream().ref().stream().type() !=
 | 
						|
                            mozilla::ipc::InputStreamParams::T__None);
 | 
						|
 | 
						|
  // We're passing ownership across the IPC barrier with the control, so
 | 
						|
  // do not signal that the stream is closed here.
 | 
						|
  Forget();
 | 
						|
}
 | 
						|
 | 
						|
void ReadStream::Inner::CloseStream() {
 | 
						|
  MOZ_ASSERT(mOwningEventTarget->IsOnCurrentThread());
 | 
						|
  MOZ_ALWAYS_SUCCEEDS(Close());
 | 
						|
}
 | 
						|
 | 
						|
void ReadStream::Inner::CloseStreamWithoutReporting() {
 | 
						|
  MOZ_ASSERT(mOwningEventTarget->IsOnCurrentThread());
 | 
						|
  Forget();
 | 
						|
}
 | 
						|
 | 
						|
bool ReadStream::Inner::HasEverBeenRead() const {
 | 
						|
  MOZ_ASSERT(mOwningEventTarget->IsOnCurrentThread());
 | 
						|
  return mHasEverBeenRead;
 | 
						|
}
 | 
						|
 | 
						|
nsresult ReadStream::Inner::Close() {
 | 
						|
  // stream ops can happen on any thread
 | 
						|
  nsresult rv = NS_OK;
 | 
						|
  {
 | 
						|
    MutexAutoLock lock(mMutex);
 | 
						|
    if (mSnappyStream) {
 | 
						|
      rv = mSnappyStream->Close();
 | 
						|
    }
 | 
						|
  }
 | 
						|
  NoteClosed();
 | 
						|
  return rv;
 | 
						|
}
 | 
						|
 | 
						|
nsresult ReadStream::Inner::Available(uint64_t* aNumAvailableOut) {
 | 
						|
  // stream ops can happen on any thread
 | 
						|
  nsresult rv = NS_OK;
 | 
						|
  {
 | 
						|
    MutexAutoLock lock(mMutex);
 | 
						|
    rv = EnsureStream()->Available(aNumAvailableOut);
 | 
						|
  }
 | 
						|
 | 
						|
  if (NS_FAILED(rv)) {
 | 
						|
    Close();
 | 
						|
  }
 | 
						|
 | 
						|
  return rv;
 | 
						|
}
 | 
						|
 | 
						|
nsresult ReadStream::Inner::StreamStatus() {
 | 
						|
  // stream ops can happen on any thread
 | 
						|
  nsresult rv = NS_OK;
 | 
						|
  {
 | 
						|
    MutexAutoLock lock(mMutex);
 | 
						|
    rv = EnsureStream()->StreamStatus();
 | 
						|
  }
 | 
						|
 | 
						|
  if (NS_FAILED(rv)) {
 | 
						|
    Close();
 | 
						|
  }
 | 
						|
 | 
						|
  return rv;
 | 
						|
}
 | 
						|
 | 
						|
nsresult ReadStream::Inner::Read(char* aBuf, uint32_t aCount,
 | 
						|
                                 uint32_t* aNumReadOut) {
 | 
						|
  // stream ops can happen on any thread
 | 
						|
  MOZ_DIAGNOSTIC_ASSERT(aNumReadOut);
 | 
						|
 | 
						|
  nsresult rv = NS_OK;
 | 
						|
  {
 | 
						|
    MutexAutoLock lock(mMutex);
 | 
						|
    rv = EnsureStream()->Read(aBuf, aCount, aNumReadOut);
 | 
						|
  }
 | 
						|
 | 
						|
  if ((NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK) ||
 | 
						|
      *aNumReadOut == 0) {
 | 
						|
    Close();
 | 
						|
  }
 | 
						|
 | 
						|
  mHasEverBeenRead = true;
 | 
						|
 | 
						|
  return rv;
 | 
						|
}
 | 
						|
 | 
						|
nsresult ReadStream::Inner::ReadSegments(nsWriteSegmentFun aWriter,
 | 
						|
                                         void* aClosure, uint32_t aCount,
 | 
						|
                                         uint32_t* aNumReadOut) {
 | 
						|
  // stream ops can happen on any thread
 | 
						|
  MOZ_DIAGNOSTIC_ASSERT(aNumReadOut);
 | 
						|
 | 
						|
  if (aCount) {
 | 
						|
    mHasEverBeenRead = true;
 | 
						|
  }
 | 
						|
 | 
						|
  nsresult rv = NS_OK;
 | 
						|
  {
 | 
						|
    MutexAutoLock lock(mMutex);
 | 
						|
    rv = EnsureStream()->ReadSegments(aWriter, aClosure, aCount, aNumReadOut);
 | 
						|
  }
 | 
						|
 | 
						|
  if ((NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK &&
 | 
						|
       rv != NS_ERROR_NOT_IMPLEMENTED) ||
 | 
						|
      *aNumReadOut == 0) {
 | 
						|
    Close();
 | 
						|
  }
 | 
						|
 | 
						|
  // Verify bytes were actually read before marking as being ever read.  For
 | 
						|
  // example, code can test if the stream supports ReadSegments() by calling
 | 
						|
  // this method with a dummy callback which doesn't read anything.  We don't
 | 
						|
  // want to trigger on that.
 | 
						|
  if (*aNumReadOut) {
 | 
						|
    mHasEverBeenRead = true;
 | 
						|
  }
 | 
						|
 | 
						|
  return rv;
 | 
						|
}
 | 
						|
 | 
						|
nsresult ReadStream::Inner::IsNonBlocking(bool* aNonBlockingOut) {
 | 
						|
  // stream ops can happen on any thread
 | 
						|
  MutexAutoLock lock(mMutex);
 | 
						|
  if (mSnappyStream) {
 | 
						|
    return mSnappyStream->IsNonBlocking(aNonBlockingOut);
 | 
						|
  }
 | 
						|
  *aNonBlockingOut = false;
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
ReadStream::Inner::~Inner() {
 | 
						|
  // Any thread
 | 
						|
  MOZ_DIAGNOSTIC_ASSERT(mState == Closed);
 | 
						|
  MOZ_DIAGNOSTIC_ASSERT(!mControl);
 | 
						|
}
 | 
						|
 | 
						|
void ReadStream::Inner::NoteClosed() {
 | 
						|
  // Any thread
 | 
						|
  if (mState == Closed) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (mOwningEventTarget->IsOnCurrentThread()) {
 | 
						|
    NoteClosedOnOwningThread();
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  nsCOMPtr<nsIRunnable> runnable = new NoteClosedRunnable(SafeRefPtrFromThis());
 | 
						|
  MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(runnable.forget(),
 | 
						|
                                                   nsIThread::DISPATCH_NORMAL));
 | 
						|
}
 | 
						|
 | 
						|
void ReadStream::Inner::Forget() {
 | 
						|
  // Any thread
 | 
						|
  if (mState == Closed) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (mOwningEventTarget->IsOnCurrentThread()) {
 | 
						|
    ForgetOnOwningThread();
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  nsCOMPtr<nsIRunnable> runnable = new ForgetRunnable(SafeRefPtrFromThis());
 | 
						|
  MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(runnable.forget(),
 | 
						|
                                                   nsIThread::DISPATCH_NORMAL));
 | 
						|
}
 | 
						|
 | 
						|
void ReadStream::Inner::NoteClosedOnOwningThread() {
 | 
						|
  MOZ_ASSERT(mOwningEventTarget->IsOnCurrentThread());
 | 
						|
 | 
						|
  // Mark closed and do nothing if we were already closed
 | 
						|
  if (!mState.compareExchange(Open, Closed)) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  MaybeAbortAsyncOpenStream();
 | 
						|
 | 
						|
  MOZ_DIAGNOSTIC_ASSERT(mControl);
 | 
						|
  mControl->NoteClosed(SafeRefPtrFromThis(), mId);
 | 
						|
  mControl = nullptr;
 | 
						|
}
 | 
						|
 | 
						|
void ReadStream::Inner::ForgetOnOwningThread() {
 | 
						|
  MOZ_ASSERT(mOwningEventTarget->IsOnCurrentThread());
 | 
						|
 | 
						|
  // Mark closed and do nothing if we were already closed
 | 
						|
  if (!mState.compareExchange(Open, Closed)) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  MaybeAbortAsyncOpenStream();
 | 
						|
 | 
						|
  MOZ_DIAGNOSTIC_ASSERT(mControl);
 | 
						|
  mControl->ForgetReadStream(SafeRefPtrFromThis());
 | 
						|
  mControl = nullptr;
 | 
						|
}
 | 
						|
 | 
						|
nsIInputStream* ReadStream::Inner::EnsureStream() {
 | 
						|
  mMutex.AssertCurrentThreadOwns();
 | 
						|
 | 
						|
  // We need to block the current thread while we open the stream.  We
 | 
						|
  // cannot do this safely from the main owning thread since it would
 | 
						|
  // trigger deadlock.  This should be ok, though, since a blocking
 | 
						|
  // stream like this should never be read on the owning thread anyway.
 | 
						|
  if (mOwningEventTarget->IsOnCurrentThread()) {
 | 
						|
    MOZ_CRASH("Blocking read on the js/ipc owning thread!");
 | 
						|
  }
 | 
						|
 | 
						|
  if (mSnappyStream) {
 | 
						|
    return mSnappyStream;
 | 
						|
  }
 | 
						|
 | 
						|
  nsCOMPtr<nsIRunnable> r = NewCancelableRunnableMethod(
 | 
						|
      "ReadStream::Inner::AsyncOpenStreamOnOwningThread", this,
 | 
						|
      &ReadStream::Inner::AsyncOpenStreamOnOwningThread);
 | 
						|
  nsresult rv =
 | 
						|
      mOwningEventTarget->Dispatch(r.forget(), nsIThread::DISPATCH_NORMAL);
 | 
						|
  if (NS_WARN_IF(NS_FAILED(rv))) {
 | 
						|
    OpenStreamFailed();
 | 
						|
    return mSnappyStream;
 | 
						|
  }
 | 
						|
 | 
						|
  mCondVar.Wait();
 | 
						|
  MOZ_DIAGNOSTIC_ASSERT(mSnappyStream);
 | 
						|
 | 
						|
  return mSnappyStream;
 | 
						|
}
 | 
						|
 | 
						|
void ReadStream::Inner::AsyncOpenStreamOnOwningThread() {
 | 
						|
  MOZ_ASSERT(mOwningEventTarget->IsOnCurrentThread());
 | 
						|
 | 
						|
  if (mSnappyStream) {
 | 
						|
    // Different threads might request opening the stream at the same time. If
 | 
						|
    // the earlier request succeeded, then use the result.
 | 
						|
    mCondVar.NotifyAll();
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!mControl || mState == Closed) {
 | 
						|
    MutexAutoLock lock(mMutex);
 | 
						|
    OpenStreamFailed();
 | 
						|
    mCondVar.NotifyAll();
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (mAsyncOpenStarted) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  mAsyncOpenStarted = true;
 | 
						|
 | 
						|
  RefPtr<ReadStream::Inner> self = this;
 | 
						|
  mControl->OpenStream(mId, [self](nsCOMPtr<nsIInputStream>&& aStream) {
 | 
						|
    MutexAutoLock lock(self->mMutex);
 | 
						|
    self->mAsyncOpenStarted = false;
 | 
						|
    if (!self->mStream) {
 | 
						|
      if (!aStream) {
 | 
						|
        self->OpenStreamFailed();
 | 
						|
      } else {
 | 
						|
        self->mStream = std::move(aStream);
 | 
						|
        self->mSnappyStream = new SnappyUncompressInputStream(self->mStream);
 | 
						|
      }
 | 
						|
    }
 | 
						|
    self->mCondVar.NotifyAll();
 | 
						|
  });
 | 
						|
}
 | 
						|
 | 
						|
void ReadStream::Inner::MaybeAbortAsyncOpenStream() {
 | 
						|
  if (!mAsyncOpenStarted) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  MutexAutoLock lock(mMutex);
 | 
						|
  OpenStreamFailed();
 | 
						|
  mCondVar.NotifyAll();
 | 
						|
}
 | 
						|
 | 
						|
void ReadStream::Inner::OpenStreamFailed() {
 | 
						|
  MOZ_DIAGNOSTIC_ASSERT(!mStream);
 | 
						|
  MOZ_DIAGNOSTIC_ASSERT(!mSnappyStream);
 | 
						|
  mMutex.AssertCurrentThreadOwns();
 | 
						|
  Unused << NS_NewCStringInputStream(getter_AddRefs(mStream), ""_ns);
 | 
						|
  mSnappyStream = mStream;
 | 
						|
  mStream->Close();
 | 
						|
  NoteClosed();
 | 
						|
}
 | 
						|
 | 
						|
// ----------------------------------------------------------------------------
 | 
						|
 | 
						|
NS_IMPL_ISUPPORTS(cache::ReadStream, nsIInputStream, ReadStream);
 | 
						|
 | 
						|
// static
 | 
						|
already_AddRefed<ReadStream> ReadStream::Create(
 | 
						|
    const Maybe<CacheReadStream>& aMaybeReadStream) {
 | 
						|
  if (aMaybeReadStream.isNothing()) {
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  return Create(aMaybeReadStream.ref());
 | 
						|
}
 | 
						|
 | 
						|
// static
 | 
						|
already_AddRefed<ReadStream> ReadStream::Create(
 | 
						|
    const CacheReadStream& aReadStream) {
 | 
						|
  // The parameter may or may not be for a Cache created stream.  The way we
 | 
						|
  // tell is by looking at the stream control actor.  If the actor exists,
 | 
						|
  // then we know the Cache created it.
 | 
						|
  if (!aReadStream.control()) {
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  MOZ_DIAGNOSTIC_ASSERT(aReadStream.stream().isNothing() ||
 | 
						|
                        aReadStream.stream().ref().stream().type() !=
 | 
						|
                            mozilla::ipc::InputStreamParams::T__None);
 | 
						|
 | 
						|
  // Control is guaranteed to survive this method as ActorDestroy() cannot
 | 
						|
  // run on this thread until we complete.
 | 
						|
  StreamControl* control;
 | 
						|
  if (aReadStream.control().IsChild()) {
 | 
						|
    auto actor =
 | 
						|
        static_cast<CacheStreamControlChild*>(aReadStream.control().AsChild());
 | 
						|
    control = actor;
 | 
						|
  } else {
 | 
						|
    auto actor = static_cast<CacheStreamControlParent*>(
 | 
						|
        aReadStream.control().AsParent());
 | 
						|
    control = actor;
 | 
						|
  }
 | 
						|
  MOZ_DIAGNOSTIC_ASSERT(control);
 | 
						|
 | 
						|
  nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(aReadStream.stream());
 | 
						|
 | 
						|
  // Currently we expect all cache read streams to be blocking file streams.
 | 
						|
#if defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
 | 
						|
  if (stream) {
 | 
						|
    nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(stream);
 | 
						|
    MOZ_DIAGNOSTIC_ASSERT(!asyncStream);
 | 
						|
  }
 | 
						|
#endif
 | 
						|
 | 
						|
  return MakeAndAddRef<ReadStream>(MakeSafeRefPtr<ReadStream::Inner>(
 | 
						|
      std::move(control), aReadStream.id(), stream));
 | 
						|
}
 | 
						|
 | 
						|
// static
 | 
						|
already_AddRefed<ReadStream> ReadStream::Create(
 | 
						|
    PCacheStreamControlParent* aControl, const nsID& aId,
 | 
						|
    nsIInputStream* aStream) {
 | 
						|
  MOZ_DIAGNOSTIC_ASSERT(aControl);
 | 
						|
 | 
						|
  return MakeAndAddRef<ReadStream>(MakeSafeRefPtr<ReadStream::Inner>(
 | 
						|
      static_cast<CacheStreamControlParent*>(aControl), aId, aStream));
 | 
						|
}
 | 
						|
 | 
						|
void ReadStream::Serialize(Maybe<CacheReadStream>* aReadStreamOut,
 | 
						|
                           ErrorResult& aRv) {
 | 
						|
  mInner->Serialize(aReadStreamOut, aRv);
 | 
						|
}
 | 
						|
 | 
						|
void ReadStream::Serialize(CacheReadStream* aReadStreamOut, ErrorResult& aRv) {
 | 
						|
  mInner->Serialize(aReadStreamOut, aRv);
 | 
						|
}
 | 
						|
 | 
						|
ReadStream::ReadStream(SafeRefPtr<ReadStream::Inner> aInner)
 | 
						|
    : mInner(std::move(aInner)) {
 | 
						|
  MOZ_DIAGNOSTIC_ASSERT(mInner);
 | 
						|
}
 | 
						|
 | 
						|
ReadStream::~ReadStream() {
 | 
						|
  // Explicitly close the inner stream so that it does not have to
 | 
						|
  // deal with implicitly closing at destruction time.
 | 
						|
  mInner->Close();
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
ReadStream::Close() { return mInner->Close(); }
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
ReadStream::Available(uint64_t* aNumAvailableOut) {
 | 
						|
  return mInner->Available(aNumAvailableOut);
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
ReadStream::StreamStatus() { return mInner->StreamStatus(); }
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
ReadStream::Read(char* aBuf, uint32_t aCount, uint32_t* aNumReadOut) {
 | 
						|
  return mInner->Read(aBuf, aCount, aNumReadOut);
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
ReadStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
 | 
						|
                         uint32_t aCount, uint32_t* aNumReadOut) {
 | 
						|
  return mInner->ReadSegments(aWriter, aClosure, aCount, aNumReadOut);
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
ReadStream::IsNonBlocking(bool* aNonBlockingOut) {
 | 
						|
  return mInner->IsNonBlocking(aNonBlockingOut);
 | 
						|
}
 | 
						|
 | 
						|
}  // namespace mozilla::dom::cache
 |