зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1754004 - Part 8: Remove SeekableStreamWrapper, r=asuth,necko-reviewers,kershaw
This type is no longer necessary, and has various issues due to behaving incorrectly when used with async streams or streams which are not nsIPipe. Differential Revision: https://phabricator.services.mozilla.com/D141045
This commit is contained in:
Родитель
e18a717abc
Коммит
000305abbf
|
@ -14,7 +14,6 @@
|
|||
|
||||
#include "ipc/IPCMessageUtils.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/SeekableStreamWrapper.h"
|
||||
#include "mozilla/ipc/InputStreamUtils.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
|
|
|
@ -1,503 +0,0 @@
|
|||
/* -*- 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/SeekableStreamWrapper.h"
|
||||
|
||||
#include "mozilla/InputStreamLengthHelper.h"
|
||||
#include "mozilla/ipc/InputStreamUtils.h"
|
||||
#include "nsStreamUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/* static */
|
||||
nsresult SeekableStreamWrapper::MaybeWrap(
|
||||
already_AddRefed<nsIInputStream> aOriginal, nsIInputStream** aResult) {
|
||||
// First, check if the original stream is already seekable.
|
||||
nsCOMPtr<nsIInputStream> original(aOriginal);
|
||||
if (nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(original)) {
|
||||
original.forget(aResult);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// If the original stream was not seekable, clone it. This may create a
|
||||
// replacement stream using `nsPipe` if the original stream was not cloneable.
|
||||
nsCOMPtr<nsIInputStream> clone;
|
||||
nsCOMPtr<nsIInputStream> replacement;
|
||||
nsresult rv = NS_CloneInputStream(original, getter_AddRefs(clone),
|
||||
getter_AddRefs(replacement));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
if (replacement) {
|
||||
original = replacement.forget();
|
||||
}
|
||||
|
||||
// Our replacement or original must be cloneable if `NS_CloneInputStream`
|
||||
// succeeded.
|
||||
nsCOMPtr<nsICloneableInputStream> cloneable = do_QueryInterface(original);
|
||||
MOZ_DIAGNOSTIC_ASSERT(cloneable && cloneable->GetCloneable(),
|
||||
"Result of NS_CloneInputStream must be cloneable");
|
||||
|
||||
nsCOMPtr<nsIAsyncInputStream> stream =
|
||||
new SeekableStreamWrapper(cloneable, clone);
|
||||
stream.forget(aResult);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
nsCOMPtr<T> SeekableStreamWrapper::StreamAs() {
|
||||
MutexAutoLock lock(mMutex);
|
||||
if constexpr (std::is_same_v<T, nsIInputStream>) {
|
||||
return nsCOMPtr<T>(mCurrent);
|
||||
} else {
|
||||
return nsCOMPtr<T>(do_QueryInterface(mCurrent));
|
||||
}
|
||||
}
|
||||
|
||||
// nsIInputStream
|
||||
|
||||
NS_IMETHODIMP
|
||||
SeekableStreamWrapper::Close() {
|
||||
auto stream = StreamAs<nsIInputStream>();
|
||||
NS_ENSURE_STATE(stream);
|
||||
nsresult rv1 = stream->Close();
|
||||
|
||||
nsresult rv2 = NS_ERROR_NOT_IMPLEMENTED;
|
||||
if (nsCOMPtr<nsIInputStream> original = do_QueryInterface(mOriginal)) {
|
||||
rv2 = original->Close();
|
||||
}
|
||||
|
||||
return NS_FAILED(rv1) ? rv1 : rv2;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SeekableStreamWrapper::Available(uint64_t* aLength) {
|
||||
auto stream = StreamAs<nsIInputStream>();
|
||||
NS_ENSURE_STATE(stream);
|
||||
return stream->Available(aLength);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SeekableStreamWrapper::Read(char* aBuffer, uint32_t aCount,
|
||||
uint32_t* aReadCount) {
|
||||
auto stream = StreamAs<nsIInputStream>();
|
||||
NS_ENSURE_STATE(stream);
|
||||
return stream->Read(aBuffer, aCount, aReadCount);
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct WriterClosure {
|
||||
nsIInputStream* mStream;
|
||||
nsWriteSegmentFun mInnerFun;
|
||||
void* mInnerClosure;
|
||||
};
|
||||
|
||||
static nsresult WriterCb(nsIInputStream* aInStream, void* aClosure,
|
||||
const char* aFromSegment, uint32_t aToOffset,
|
||||
uint32_t aCount, uint32_t* aWriteCount) {
|
||||
auto* closure = static_cast<WriterClosure*>(aClosure);
|
||||
return (closure->mInnerFun)(closure->mStream, closure->mInnerClosure,
|
||||
aFromSegment, aToOffset, aCount, aWriteCount);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
NS_IMETHODIMP
|
||||
SeekableStreamWrapper::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
|
||||
uint32_t aCount, uint32_t* aResult) {
|
||||
auto stream = StreamAs<nsIInputStream>();
|
||||
NS_ENSURE_STATE(stream);
|
||||
WriterClosure innerClosure{static_cast<nsIAsyncInputStream*>(this), aWriter,
|
||||
aClosure};
|
||||
return stream->ReadSegments(WriterCb, &innerClosure, aCount, aResult);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SeekableStreamWrapper::IsNonBlocking(bool* aNonBlocking) {
|
||||
auto stream = StreamAs<nsIInputStream>();
|
||||
NS_ENSURE_STATE(stream);
|
||||
return stream->IsNonBlocking(aNonBlocking);
|
||||
}
|
||||
|
||||
// nsICloneableInputStream
|
||||
|
||||
NS_IMETHODIMP
|
||||
SeekableStreamWrapper::GetCloneable(bool* aCloneable) {
|
||||
auto stream = StreamAs<nsICloneableInputStream>();
|
||||
NS_ENSURE_STATE(stream);
|
||||
return stream->GetCloneable(aCloneable);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SeekableStreamWrapper::Clone(nsIInputStream** aResult) {
|
||||
NS_ENSURE_STATE(mOriginal);
|
||||
NS_ENSURE_STATE(mOriginal->GetCloneable());
|
||||
|
||||
nsCOMPtr<nsIInputStream> originalClone;
|
||||
nsresult rv = mOriginal->Clone(getter_AddRefs(originalClone));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsICloneableInputStream> newOriginal =
|
||||
do_QueryInterface(originalClone);
|
||||
if (!newOriginal || !newOriginal->GetCloneable()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
auto cloneable = StreamAs<nsICloneableInputStream>();
|
||||
if (!cloneable || !cloneable->GetCloneable()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIInputStream> newClone;
|
||||
rv = cloneable->Clone(getter_AddRefs(newClone));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIAsyncInputStream> result =
|
||||
new SeekableStreamWrapper(newOriginal, newClone);
|
||||
result.forget(aResult);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsIAsyncInputStream
|
||||
|
||||
NS_IMETHODIMP
|
||||
SeekableStreamWrapper::CloseWithStatus(nsresult aStatus) {
|
||||
auto stream = StreamAs<nsIAsyncInputStream>();
|
||||
NS_ENSURE_STATE(stream);
|
||||
|
||||
nsresult rv1 = stream->CloseWithStatus(aStatus);
|
||||
nsresult rv2 = NS_ERROR_NOT_IMPLEMENTED;
|
||||
if (nsCOMPtr<nsIAsyncInputStream> original = do_QueryInterface(mOriginal)) {
|
||||
rv2 = original->CloseWithStatus(aStatus);
|
||||
}
|
||||
return NS_FAILED(rv1) ? rv1 : rv2;
|
||||
}
|
||||
|
||||
class AsyncWaitCallback final : public nsIInputStreamCallback {
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIINPUTSTREAMCALLBACK
|
||||
|
||||
public:
|
||||
AsyncWaitCallback(nsIInputStreamCallback* aCallback,
|
||||
nsIAsyncInputStream* aStream)
|
||||
: mCallback(aCallback), mStream(aStream) {}
|
||||
|
||||
private:
|
||||
~AsyncWaitCallback() = default;
|
||||
|
||||
nsCOMPtr<nsIInputStreamCallback> mCallback;
|
||||
nsCOMPtr<nsIAsyncInputStream> mStream;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(AsyncWaitCallback, nsIInputStreamCallback)
|
||||
|
||||
NS_IMETHODIMP
|
||||
AsyncWaitCallback::OnInputStreamReady(nsIAsyncInputStream*) {
|
||||
return mCallback->OnInputStreamReady(mStream);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SeekableStreamWrapper::AsyncWait(nsIInputStreamCallback* aCallback,
|
||||
uint32_t aFlags, uint32_t aRequestedCount,
|
||||
nsIEventTarget* aEventTarget) {
|
||||
auto stream = StreamAs<nsIAsyncInputStream>();
|
||||
NS_ENSURE_TRUE(stream, NS_ERROR_NOT_IMPLEMENTED);
|
||||
|
||||
nsCOMPtr<nsIInputStreamCallback> callback;
|
||||
if (aCallback) {
|
||||
callback = new AsyncWaitCallback(aCallback, this);
|
||||
}
|
||||
return stream->AsyncWait(callback, aFlags, aRequestedCount, aEventTarget);
|
||||
}
|
||||
|
||||
// nsITellableStream
|
||||
|
||||
NS_IMETHODIMP
|
||||
SeekableStreamWrapper::Tell(int64_t* aResult) {
|
||||
auto stream = StreamAs<nsITellableStream>();
|
||||
NS_ENSURE_STATE(stream);
|
||||
return stream->Tell(aResult);
|
||||
}
|
||||
|
||||
// nsISeekableStream
|
||||
|
||||
NS_IMETHODIMP
|
||||
SeekableStreamWrapper::Seek(int32_t aWhence, int64_t aOffset) {
|
||||
// Check if our original stream has already been closed, in which case we
|
||||
// can't seek anymore.
|
||||
nsCOMPtr<nsIInputStream> original = do_QueryInterface(mOriginal);
|
||||
uint64_t ignored = 0;
|
||||
nsresult rv = original->Available(&ignored);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
int64_t offset = aOffset;
|
||||
nsCOMPtr<nsIInputStream> origin;
|
||||
switch (aWhence) {
|
||||
// With NS_SEEK_SET, offset relative to our original stream.
|
||||
case NS_SEEK_SET:
|
||||
break;
|
||||
|
||||
// With NS_SEEK_CUR, if the offset is positive, perform it relative to our
|
||||
// current stream, otherwise perform it relative to the original stream,
|
||||
// offset by the result of `Tell`-ing our current stream.
|
||||
case NS_SEEK_CUR: {
|
||||
auto current = StreamAs<nsIInputStream>();
|
||||
if (offset >= 0) {
|
||||
origin = current.forget();
|
||||
break;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsITellableStream> tellable = do_QueryInterface(current);
|
||||
if (!tellable) {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
int64_t cur = 0;
|
||||
rv = tellable->Tell(&cur);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
offset += cur;
|
||||
break;
|
||||
}
|
||||
|
||||
// We have no way of knowing what the end of the stream is, so we can't
|
||||
// implement this right now.
|
||||
case NS_SEEK_END:
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
if (offset < 0) {
|
||||
NS_WARNING("cannot seek before beginning of stream");
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
// If we aren't starting from our existing stream, clone a new stream from
|
||||
// `mOriginal`.
|
||||
if (!origin) {
|
||||
rv = mOriginal->Clone(getter_AddRefs(origin));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
MOZ_ASSERT(origin);
|
||||
|
||||
// If we have an offset from our origin to read to, use `ReadSegments` with
|
||||
// `NS_DiscardSegment` to read to that point in the stream.
|
||||
if (offset > 0) {
|
||||
uint64_t available = 0;
|
||||
rv = origin->Available(&available);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
if (available < uint64_t(offset)) {
|
||||
// The current implementation of Seek cannot handle seeking into
|
||||
// unavailable data, whether that's because it's past the end of the input
|
||||
// stream, or the data isn't available yet.
|
||||
NS_WARNING("cannot seek past the end of the currently avaliable data");
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
// Discard segments from the input stream.
|
||||
uint32_t written = 0;
|
||||
rv = origin->ReadSegments(NS_DiscardSegment, nullptr, offset, &written);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
if (written != offset) {
|
||||
return NS_BASE_STREAM_WOULD_BLOCK;
|
||||
}
|
||||
}
|
||||
|
||||
// Actually swap out our `nsIInputStream`.
|
||||
nsCOMPtr<nsIInputStream> toClose;
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
if (mCurrent != origin) {
|
||||
toClose = mCurrent.forget();
|
||||
mCurrent = origin.forget();
|
||||
}
|
||||
MOZ_ASSERT(mCurrent);
|
||||
}
|
||||
if (toClose) {
|
||||
toClose->Close();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SeekableStreamWrapper::SetEOF() { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
|
||||
// nsIInputStreamLength
|
||||
|
||||
NS_IMETHODIMP
|
||||
SeekableStreamWrapper::Length(int64_t* aLength) {
|
||||
auto stream = StreamAs<nsIInputStreamLength>();
|
||||
NS_ENSURE_STATE(stream);
|
||||
return stream->Length(aLength);
|
||||
}
|
||||
|
||||
// nsIAsyncInputStreamLength
|
||||
|
||||
class AsyncLengthCallback final : public nsIInputStreamLengthCallback {
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIINPUTSTREAMLENGTHCALLBACK
|
||||
|
||||
public:
|
||||
AsyncLengthCallback(nsIInputStreamLengthCallback* aCallback,
|
||||
nsIAsyncInputStreamLength* aStream)
|
||||
: mCallback(aCallback), mStream(aStream) {}
|
||||
|
||||
private:
|
||||
~AsyncLengthCallback() = default;
|
||||
|
||||
nsCOMPtr<nsIInputStreamLengthCallback> mCallback;
|
||||
nsCOMPtr<nsIAsyncInputStreamLength> mStream;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(AsyncLengthCallback, nsIInputStreamLengthCallback)
|
||||
|
||||
NS_IMETHODIMP
|
||||
AsyncLengthCallback::OnInputStreamLengthReady(nsIAsyncInputStreamLength*,
|
||||
int64_t aLength) {
|
||||
return mCallback->OnInputStreamLengthReady(mStream, aLength);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SeekableStreamWrapper::AsyncLengthWait(nsIInputStreamLengthCallback* aCallback,
|
||||
nsIEventTarget* aEventTarget) {
|
||||
auto stream = StreamAs<nsIAsyncInputStreamLength>();
|
||||
NS_ENSURE_TRUE(stream, NS_ERROR_NOT_IMPLEMENTED);
|
||||
|
||||
nsCOMPtr<nsIInputStreamLengthCallback> callback;
|
||||
if (aCallback) {
|
||||
callback = new AsyncLengthCallback(aCallback, this);
|
||||
}
|
||||
return stream->AsyncLengthWait(callback, aEventTarget);
|
||||
}
|
||||
|
||||
// nsIIPCSerializableInputStream
|
||||
|
||||
void SeekableStreamWrapper::SerializedComplexity(uint32_t aMaxSize,
|
||||
uint32_t* aSizeUsed,
|
||||
uint32_t* aPipes,
|
||||
uint32_t* aTransferables) {
|
||||
MOZ_ASSERT(IsIPCSerializableInputStream());
|
||||
nsCOMPtr<nsIInputStream> original = do_QueryInterface(mOriginal);
|
||||
mozilla::ipc::InputStreamHelper::SerializedComplexity(
|
||||
original, aMaxSize, aSizeUsed, aPipes, aTransferables);
|
||||
}
|
||||
|
||||
void SeekableStreamWrapper::Serialize(
|
||||
mozilla::ipc::InputStreamParams& aParams,
|
||||
FileDescriptorArray& aFileDescriptors, bool aDelayedStart,
|
||||
uint32_t aMaxSize, uint32_t* aSizeUsed,
|
||||
mozilla::ipc::ParentToChildStreamActorManager* aManager) {
|
||||
SerializeInternal(aParams, aFileDescriptors, aDelayedStart, aMaxSize,
|
||||
aSizeUsed, aManager);
|
||||
}
|
||||
|
||||
void SeekableStreamWrapper::Serialize(
|
||||
mozilla::ipc::InputStreamParams& aParams,
|
||||
FileDescriptorArray& aFileDescriptors, bool aDelayedStart,
|
||||
uint32_t aMaxSize, uint32_t* aSizeUsed,
|
||||
mozilla::ipc::ChildToParentStreamActorManager* aManager) {
|
||||
SerializeInternal(aParams, aFileDescriptors, aDelayedStart, aMaxSize,
|
||||
aSizeUsed, aManager);
|
||||
}
|
||||
|
||||
template <typename M>
|
||||
void SeekableStreamWrapper::SerializeInternal(
|
||||
mozilla::ipc::InputStreamParams& aParams,
|
||||
FileDescriptorArray& aFileDescriptors, bool aDelayedStart,
|
||||
uint32_t aMaxSize, uint32_t* aSizeUsed, M* aManager) {
|
||||
// Stream serialization methods generally do not respect the current seek
|
||||
// offset when serializing, as is seen in nsStringStream::Serialize. As
|
||||
// `StreamAs` may be nullptr and requires acquiring the mutex, instead
|
||||
// serialize `mOriginal` into our pipe.
|
||||
//
|
||||
// Don't serialize context about this being a SeekableStreamWrapper, as the
|
||||
// specific interfaces a stream implements, such as nsISeekableStream, are not
|
||||
// preserved over IPC.
|
||||
MOZ_ASSERT(IsIPCSerializableInputStream());
|
||||
nsCOMPtr<nsIInputStream> original = do_QueryInterface(mOriginal);
|
||||
mozilla::ipc::InputStreamHelper::SerializeInputStream(
|
||||
original, aParams, aFileDescriptors, aDelayedStart, aMaxSize, aSizeUsed,
|
||||
aManager);
|
||||
}
|
||||
|
||||
bool SeekableStreamWrapper::Deserialize(
|
||||
const mozilla::ipc::InputStreamParams& aParams,
|
||||
const FileDescriptorArray& aFileDescriptors) {
|
||||
MOZ_CRASH("This method should never be called!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// nsIBufferedInputStream
|
||||
|
||||
NS_IMETHODIMP
|
||||
SeekableStreamWrapper::Init(nsIInputStream*, uint32_t) {
|
||||
MOZ_CRASH(
|
||||
"SeekableStreamWrapper should never be initialized with "
|
||||
"nsIBufferedInputStream::Init!\n");
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SeekableStreamWrapper::GetData(nsIInputStream** aResult) {
|
||||
auto stream = StreamAs<nsIBufferedInputStream>();
|
||||
NS_ENSURE_STATE(stream);
|
||||
return stream->GetData(aResult);
|
||||
}
|
||||
|
||||
// nsISupports
|
||||
|
||||
bool SeekableStreamWrapper::IsAsyncInputStream() {
|
||||
nsCOMPtr<nsIAsyncInputStream> stream(do_QueryInterface(mOriginal));
|
||||
return stream;
|
||||
}
|
||||
bool SeekableStreamWrapper::IsInputStreamLength() {
|
||||
nsCOMPtr<nsIInputStreamLength> stream(do_QueryInterface(mOriginal));
|
||||
return stream;
|
||||
}
|
||||
bool SeekableStreamWrapper::IsAsyncInputStreamLength() {
|
||||
nsCOMPtr<nsIAsyncInputStreamLength> stream(do_QueryInterface(mOriginal));
|
||||
return stream;
|
||||
}
|
||||
bool SeekableStreamWrapper::IsIPCSerializableInputStream() {
|
||||
nsCOMPtr<nsIIPCSerializableInputStream> stream(do_QueryInterface(mOriginal));
|
||||
return stream;
|
||||
}
|
||||
bool SeekableStreamWrapper::IsBufferedInputStream() {
|
||||
nsCOMPtr<nsIBufferedInputStream> stream(do_QueryInterface(mOriginal));
|
||||
return stream;
|
||||
}
|
||||
|
||||
NS_IMPL_ADDREF(SeekableStreamWrapper)
|
||||
NS_IMPL_RELEASE(SeekableStreamWrapper)
|
||||
NS_INTERFACE_MAP_BEGIN(SeekableStreamWrapper)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAsyncInputStream)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIInputStream, nsIAsyncInputStream)
|
||||
NS_INTERFACE_MAP_ENTRY(nsITellableStream)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISeekableStream)
|
||||
NS_INTERFACE_MAP_ENTRY(nsICloneableInputStream)
|
||||
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAsyncInputStream, IsAsyncInputStream())
|
||||
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamLength,
|
||||
IsInputStreamLength())
|
||||
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAsyncInputStreamLength,
|
||||
IsAsyncInputStreamLength())
|
||||
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIPCSerializableInputStream,
|
||||
IsIPCSerializableInputStream())
|
||||
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIBufferedInputStream,
|
||||
IsBufferedInputStream())
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
} // namespace mozilla
|
|
@ -1,91 +0,0 @@
|
|||
/* -*- 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 mozilla_SeekableStreamWrapper_h
|
||||
#define mozilla_SeekableStreamWrapper_h
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIAsyncInputStream.h"
|
||||
#include "nsISeekableStream.h"
|
||||
#include "nsICloneableInputStream.h"
|
||||
#include "nsIIPCSerializableInputStream.h"
|
||||
#include "nsIInputStreamLength.h"
|
||||
#include "nsIBufferedStreams.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// Adapter class which converts an input stream implementing
|
||||
// `nsICloneableInputStream`, such as `nsPipe`, into a stream implementing
|
||||
// `nsISeekableStream`. This is done by keeping a clone of the original unwound
|
||||
// input stream to allow rewinding the stream back to its original position.
|
||||
class SeekableStreamWrapper final : public nsIAsyncInputStream,
|
||||
public nsISeekableStream,
|
||||
public nsICloneableInputStream,
|
||||
public nsIInputStreamLength,
|
||||
public nsIAsyncInputStreamLength,
|
||||
public nsIIPCSerializableInputStream,
|
||||
public nsIBufferedInputStream {
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIINPUTSTREAM
|
||||
NS_DECL_NSIASYNCINPUTSTREAM
|
||||
NS_DECL_NSITELLABLESTREAM
|
||||
NS_DECL_NSISEEKABLESTREAM
|
||||
NS_DECL_NSICLONEABLEINPUTSTREAM
|
||||
NS_DECL_NSIINPUTSTREAMLENGTH
|
||||
NS_DECL_NSIASYNCINPUTSTREAMLENGTH
|
||||
NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
|
||||
NS_DECL_NSIBUFFEREDINPUTSTREAM
|
||||
|
||||
public:
|
||||
/**
|
||||
* This function returns a nsIInputStream which is known to implement
|
||||
* nsISeekableStream. This will return |aSource| if it already implements
|
||||
* nsISeekableStream, and will wrap it if it does not.
|
||||
*
|
||||
* If the original stream implements `nsICloneableInputStream`, this will be
|
||||
* used to implement the rewinding functionality necessary for
|
||||
* `nsISeekableStream`, otherwise it will be copied into a `nsPipe`.
|
||||
*/
|
||||
static nsresult MaybeWrap(already_AddRefed<nsIInputStream> aOriginal,
|
||||
nsIInputStream** aResult);
|
||||
|
||||
private:
|
||||
SeekableStreamWrapper(nsICloneableInputStream* aOriginal,
|
||||
nsIInputStream* aCurrent)
|
||||
: mOriginal(aOriginal), mCurrent(aCurrent) {
|
||||
MOZ_ASSERT(mOriginal->GetCloneable());
|
||||
}
|
||||
|
||||
~SeekableStreamWrapper() = default;
|
||||
|
||||
template <typename T>
|
||||
nsCOMPtr<T> StreamAs();
|
||||
|
||||
template <typename M>
|
||||
void SerializeInternal(mozilla::ipc::InputStreamParams& aParams,
|
||||
FileDescriptorArray& aFileDescriptors,
|
||||
bool aDelayedStart, uint32_t aMaxSize,
|
||||
uint32_t* aSizeUsed, M* aManager);
|
||||
|
||||
bool IsAsyncInputStream();
|
||||
bool IsInputStreamLength();
|
||||
bool IsAsyncInputStreamLength();
|
||||
bool IsIPCSerializableInputStream();
|
||||
bool IsBufferedInputStream();
|
||||
|
||||
// mOriginal is immutable after creation, and is not guarded by `mMutex`.
|
||||
nsCOMPtr<nsICloneableInputStream> mOriginal;
|
||||
|
||||
// This mutex guards `mCurrent`.
|
||||
Mutex mMutex MOZ_UNANNOTATED{"SeekableStreamWrapper"};
|
||||
nsCOMPtr<nsIInputStream> mCurrent;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_SeekableStreamWrapper_h
|
|
@ -94,7 +94,6 @@ EXPORTS.mozilla += [
|
|||
"InputStreamLengthHelper.h",
|
||||
"InputStreamLengthWrapper.h",
|
||||
"NonBlockingAsyncInputStream.h",
|
||||
"SeekableStreamWrapper.h",
|
||||
"SlicedInputStream.h",
|
||||
"SnappyCompressOutputStream.h",
|
||||
"SnappyFrameUtils.h",
|
||||
|
@ -130,7 +129,6 @@ UNIFIED_SOURCES += [
|
|||
"nsStringStream.cpp",
|
||||
"nsUnicharInputStream.cpp",
|
||||
"nsWildCard.cpp",
|
||||
"SeekableStreamWrapper.cpp",
|
||||
"SlicedInputStream.cpp",
|
||||
"SnappyCompressOutputStream.cpp",
|
||||
"SnappyFrameUtils.cpp",
|
||||
|
|
|
@ -1,200 +0,0 @@
|
|||
/* -*- 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 <stdlib.h>
|
||||
#include "gtest/gtest.h"
|
||||
#include "Helpers.h"
|
||||
#include "mozilla/SeekableStreamWrapper.h"
|
||||
#include "nsIPipe.h"
|
||||
|
||||
static void NewSeekablePipe(nsIInputStream** aReader,
|
||||
nsIOutputStream** aWriter) {
|
||||
nsCOMPtr<nsIAsyncInputStream> reader;
|
||||
nsCOMPtr<nsIAsyncOutputStream> writer;
|
||||
|
||||
nsresult rv =
|
||||
NS_NewPipe2(getter_AddRefs(reader), getter_AddRefs(writer), true, true);
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
|
||||
nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(reader);
|
||||
EXPECT_FALSE(seekable);
|
||||
|
||||
nsCOMPtr<nsIInputStream> wrapped;
|
||||
rv = mozilla::SeekableStreamWrapper::MaybeWrap(do_AddRef(reader),
|
||||
getter_AddRefs(wrapped));
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
|
||||
wrapped.forget(aReader);
|
||||
writer.forget(aWriter);
|
||||
}
|
||||
|
||||
TEST(SeekableStreamWrapper, NoWrap)
|
||||
{
|
||||
nsTArray<char> inputData;
|
||||
testing::CreateData(4096, inputData);
|
||||
nsDependentCSubstring inputString(inputData.Elements(), inputData.Length());
|
||||
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
nsresult rv = NS_NewCStringInputStream(getter_AddRefs(stream), inputString);
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
|
||||
nsCOMPtr<nsIInputStream> wrapped;
|
||||
rv = mozilla::SeekableStreamWrapper::MaybeWrap(do_AddRef(stream),
|
||||
getter_AddRefs(wrapped));
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
|
||||
EXPECT_EQ(stream, wrapped);
|
||||
|
||||
testing::ConsumeAndValidateStream(wrapped, inputString);
|
||||
}
|
||||
|
||||
TEST(SeekableStreamWrapper, WrapPipe)
|
||||
{
|
||||
nsCOMPtr<nsIInputStream> reader;
|
||||
nsCOMPtr<nsIOutputStream> writer;
|
||||
|
||||
NewSeekablePipe(getter_AddRefs(reader), getter_AddRefs(writer));
|
||||
|
||||
nsTArray<char> inputData;
|
||||
testing::CreateData(1024, inputData);
|
||||
|
||||
uint32_t numWritten = 0;
|
||||
nsresult rv =
|
||||
writer->Write(inputData.Elements(), inputData.Length(), &numWritten);
|
||||
EXPECT_TRUE(NS_SUCCEEDED(rv));
|
||||
EXPECT_EQ(numWritten, 1024u);
|
||||
|
||||
nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(reader);
|
||||
ASSERT_TRUE(seekable);
|
||||
|
||||
nsCOMPtr<nsICloneableInputStream> cloneable = do_QueryInterface(reader);
|
||||
ASSERT_TRUE(cloneable);
|
||||
ASSERT_TRUE(cloneable->GetCloneable());
|
||||
|
||||
nsCOMPtr<nsIInputStream> clone1;
|
||||
rv = cloneable->Clone(getter_AddRefs(clone1));
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
ASSERT_TRUE(clone1);
|
||||
|
||||
// Check that we can read the first 512 bytes.
|
||||
{
|
||||
char buf[512];
|
||||
uint32_t numRead = 0;
|
||||
rv = reader->Read(buf, 512, &numRead);
|
||||
EXPECT_TRUE(NS_SUCCEEDED(rv));
|
||||
EXPECT_EQ(numRead, 512u);
|
||||
EXPECT_EQ(mozilla::Span(inputData).First(512), mozilla::Span(buf));
|
||||
}
|
||||
|
||||
// Check that we can read the second 512 bytes.
|
||||
{
|
||||
char buf[512];
|
||||
uint32_t numRead = 0;
|
||||
rv = reader->Read(buf, 512, &numRead);
|
||||
EXPECT_TRUE(NS_SUCCEEDED(rv));
|
||||
EXPECT_EQ(numRead, 512u);
|
||||
EXPECT_EQ(mozilla::Span(inputData).Last(512), mozilla::Span(buf));
|
||||
}
|
||||
|
||||
// Should be at the end of the pipe
|
||||
{
|
||||
char buf[1];
|
||||
uint32_t numRead = 0;
|
||||
rv = reader->Read(buf, 1, &numRead);
|
||||
EXPECT_EQ(rv, NS_BASE_STREAM_WOULD_BLOCK);
|
||||
}
|
||||
|
||||
// Re-read the second 512 bytes by seeking back.
|
||||
{
|
||||
rv = seekable->Seek(nsISeekableStream::NS_SEEK_CUR, -512);
|
||||
EXPECT_TRUE(NS_SUCCEEDED(rv));
|
||||
|
||||
char buf[512];
|
||||
uint32_t numRead = 0;
|
||||
rv = reader->Read(buf, 512, &numRead);
|
||||
EXPECT_TRUE(NS_SUCCEEDED(rv));
|
||||
EXPECT_EQ(numRead, 512u);
|
||||
EXPECT_EQ(mozilla::Span(inputData).Last(512), mozilla::Span(buf));
|
||||
}
|
||||
|
||||
// Re-read the last 256 bytes by seeking absolutely
|
||||
{
|
||||
rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 768);
|
||||
EXPECT_TRUE(NS_SUCCEEDED(rv));
|
||||
|
||||
char buf[256];
|
||||
uint32_t numRead = 0;
|
||||
rv = reader->Read(buf, 256, &numRead);
|
||||
EXPECT_TRUE(NS_SUCCEEDED(rv));
|
||||
EXPECT_EQ(numRead, 256u);
|
||||
EXPECT_EQ(mozilla::Span(inputData).Last(256), mozilla::Span(buf));
|
||||
}
|
||||
|
||||
// Double-check that we haven't impacted our clone
|
||||
{
|
||||
char buf[1024];
|
||||
uint32_t numRead = 0;
|
||||
rv = clone1->Read(buf, 1024, &numRead);
|
||||
EXPECT_TRUE(NS_SUCCEEDED(rv));
|
||||
EXPECT_EQ(numRead, 1024u);
|
||||
EXPECT_EQ(mozilla::Span(inputData), mozilla::Span(buf));
|
||||
}
|
||||
|
||||
// The clone should no longer be seekable once closed.
|
||||
rv = clone1->Close();
|
||||
EXPECT_TRUE(NS_SUCCEEDED(rv));
|
||||
nsCOMPtr<nsISeekableStream> clone1Seekable(do_QueryInterface(clone1));
|
||||
rv = clone1Seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
|
||||
EXPECT_TRUE(NS_FAILED(rv));
|
||||
|
||||
// Should still be open
|
||||
{
|
||||
uint64_t available = 0;
|
||||
rv = reader->Available(&available);
|
||||
EXPECT_TRUE(NS_SUCCEEDED(rv));
|
||||
EXPECT_EQ(available, uint64_t(0));
|
||||
}
|
||||
|
||||
// Close the original output stream
|
||||
rv = writer->Close();
|
||||
EXPECT_TRUE(NS_SUCCEEDED(rv));
|
||||
|
||||
// Should be closed
|
||||
{
|
||||
uint64_t available = 0;
|
||||
rv = reader->Available(&available);
|
||||
EXPECT_EQ(rv, NS_BASE_STREAM_CLOSED);
|
||||
}
|
||||
|
||||
// Seeing back to the beginning should re-open the stream which had reached
|
||||
// the end.
|
||||
rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
|
||||
EXPECT_TRUE(NS_SUCCEEDED(rv));
|
||||
testing::ConsumeAndValidateStream(reader, inputData);
|
||||
}
|
||||
|
||||
TEST(SeekableStreamWrapper, Interfaces)
|
||||
{
|
||||
nsCOMPtr<nsIInputStream> reader;
|
||||
nsCOMPtr<nsIOutputStream> writer;
|
||||
|
||||
NewSeekablePipe(getter_AddRefs(reader), getter_AddRefs(writer));
|
||||
|
||||
nsCOMPtr<nsIAsyncInputStream> readerType1 = do_QueryInterface(reader);
|
||||
EXPECT_TRUE(readerType1);
|
||||
|
||||
nsCOMPtr<nsITellableStream> readerType2 = do_QueryInterface(reader);
|
||||
EXPECT_TRUE(readerType2);
|
||||
|
||||
nsCOMPtr<nsISeekableStream> readerType3 = do_QueryInterface(reader);
|
||||
EXPECT_TRUE(readerType3);
|
||||
|
||||
nsCOMPtr<nsICloneableInputStream> readerType4 = do_QueryInterface(reader);
|
||||
EXPECT_TRUE(readerType4);
|
||||
|
||||
nsCOMPtr<nsIBufferedInputStream> readerType5 = do_QueryInterface(reader);
|
||||
EXPECT_TRUE(readerType5);
|
||||
}
|
|
@ -46,7 +46,6 @@ UNIFIED_SOURCES += [
|
|||
"TestRacingServiceManager.cpp",
|
||||
"TestRecursiveMutex.cpp",
|
||||
"TestRWLock.cpp",
|
||||
"TestSeekableStreamWrapper.cpp",
|
||||
"TestSegmentedBuffer.cpp",
|
||||
"TestSlicedInputStream.cpp",
|
||||
"TestSmallArrayLRUCache.cpp",
|
||||
|
|
Загрузка…
Ссылка в новой задаче