mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 05:41:12 +00:00
Bug 1434553 - Implement nsIInputStreamLength and nsIAsyncInputStreamLength - part 7 - nsIMultiplexInputStream exposes nsIInputStreamLength, r=froydnj, r=mayhemer
This commit is contained in:
parent
f0fb8aa183
commit
767ed01fe0
@ -26,6 +26,7 @@
|
|||||||
#include "nsIIPCSerializableInputStream.h"
|
#include "nsIIPCSerializableInputStream.h"
|
||||||
#include "mozilla/ipc/InputStreamUtils.h"
|
#include "mozilla/ipc/InputStreamUtils.h"
|
||||||
#include "nsIAsyncInputStream.h"
|
#include "nsIAsyncInputStream.h"
|
||||||
|
#include "nsIInputStreamLength.h"
|
||||||
|
|
||||||
using namespace mozilla;
|
using namespace mozilla;
|
||||||
using namespace mozilla::ipc;
|
using namespace mozilla::ipc;
|
||||||
@ -42,6 +43,8 @@ class nsMultiplexInputStream final
|
|||||||
, public nsICloneableInputStream
|
, public nsICloneableInputStream
|
||||||
, public nsIAsyncInputStream
|
, public nsIAsyncInputStream
|
||||||
, public nsIInputStreamCallback
|
, public nsIInputStreamCallback
|
||||||
|
, public nsIInputStreamLength
|
||||||
|
, public nsIAsyncInputStreamLength
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
nsMultiplexInputStream();
|
nsMultiplexInputStream();
|
||||||
@ -54,9 +57,16 @@ public:
|
|||||||
NS_DECL_NSICLONEABLEINPUTSTREAM
|
NS_DECL_NSICLONEABLEINPUTSTREAM
|
||||||
NS_DECL_NSIASYNCINPUTSTREAM
|
NS_DECL_NSIASYNCINPUTSTREAM
|
||||||
NS_DECL_NSIINPUTSTREAMCALLBACK
|
NS_DECL_NSIINPUTSTREAMCALLBACK
|
||||||
|
NS_DECL_NSIINPUTSTREAMLENGTH
|
||||||
|
NS_DECL_NSIASYNCINPUTSTREAMLENGTH
|
||||||
|
|
||||||
|
// This is used for nsIAsyncInputStream::AsyncWait
|
||||||
void AsyncWaitCompleted();
|
void AsyncWaitCompleted();
|
||||||
|
|
||||||
|
// This is used for nsIAsyncInputStreamLength::AsyncLengthWait
|
||||||
|
void AsyncWaitCompleted(int64_t aLength,
|
||||||
|
const MutexAutoLock& aProofOfLock);
|
||||||
|
|
||||||
struct StreamData
|
struct StreamData
|
||||||
{
|
{
|
||||||
void Initialize(nsIInputStream* aStream)
|
void Initialize(nsIInputStream* aStream)
|
||||||
@ -74,6 +84,11 @@ public:
|
|||||||
nsCOMPtr<nsISeekableStream> mSeekableStream;
|
nsCOMPtr<nsISeekableStream> mSeekableStream;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Mutex& GetLock()
|
||||||
|
{
|
||||||
|
return mLock;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
~nsMultiplexInputStream()
|
~nsMultiplexInputStream()
|
||||||
{
|
{
|
||||||
@ -103,6 +118,8 @@ private:
|
|||||||
bool IsIPCSerializable() const;
|
bool IsIPCSerializable() const;
|
||||||
bool IsCloneable() const;
|
bool IsCloneable() const;
|
||||||
bool IsAsyncInputStream() const;
|
bool IsAsyncInputStream() const;
|
||||||
|
bool IsInputStreamLength() const;
|
||||||
|
bool IsAsyncInputStreamLength() const;
|
||||||
|
|
||||||
Mutex mLock; // Protects access to all data members.
|
Mutex mLock; // Protects access to all data members.
|
||||||
|
|
||||||
@ -115,11 +132,17 @@ private:
|
|||||||
uint32_t mAsyncWaitFlags;
|
uint32_t mAsyncWaitFlags;
|
||||||
uint32_t mAsyncWaitRequestedCount;
|
uint32_t mAsyncWaitRequestedCount;
|
||||||
nsCOMPtr<nsIEventTarget> mAsyncWaitEventTarget;
|
nsCOMPtr<nsIEventTarget> mAsyncWaitEventTarget;
|
||||||
|
nsCOMPtr<nsIInputStreamLengthCallback> mAsyncWaitLengthCallback;
|
||||||
|
|
||||||
|
class AsyncWaitLengthHelper;
|
||||||
|
RefPtr<AsyncWaitLengthHelper> mAsyncWaitLengthHelper;
|
||||||
|
|
||||||
uint32_t mSeekableStreams;
|
uint32_t mSeekableStreams;
|
||||||
uint32_t mIPCSerializableStreams;
|
uint32_t mIPCSerializableStreams;
|
||||||
uint32_t mCloneableStreams;
|
uint32_t mCloneableStreams;
|
||||||
uint32_t mAsyncInputStreams;
|
uint32_t mAsyncInputStreams;
|
||||||
|
uint32_t mInputStreamLengths;
|
||||||
|
uint32_t mAsyncInputStreamLengths;
|
||||||
};
|
};
|
||||||
|
|
||||||
NS_IMPL_ADDREF(nsMultiplexInputStream)
|
NS_IMPL_ADDREF(nsMultiplexInputStream)
|
||||||
@ -140,6 +163,10 @@ NS_INTERFACE_MAP_BEGIN(nsMultiplexInputStream)
|
|||||||
IsAsyncInputStream())
|
IsAsyncInputStream())
|
||||||
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamCallback,
|
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamCallback,
|
||||||
IsAsyncInputStream())
|
IsAsyncInputStream())
|
||||||
|
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamLength,
|
||||||
|
IsInputStreamLength())
|
||||||
|
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAsyncInputStreamLength,
|
||||||
|
IsAsyncInputStreamLength())
|
||||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIMultiplexInputStream)
|
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIMultiplexInputStream)
|
||||||
NS_IMPL_QUERY_CLASSINFO(nsMultiplexInputStream)
|
NS_IMPL_QUERY_CLASSINFO(nsMultiplexInputStream)
|
||||||
NS_INTERFACE_MAP_END
|
NS_INTERFACE_MAP_END
|
||||||
@ -198,6 +225,8 @@ nsMultiplexInputStream::nsMultiplexInputStream()
|
|||||||
, mIPCSerializableStreams(0)
|
, mIPCSerializableStreams(0)
|
||||||
, mCloneableStreams(0)
|
, mCloneableStreams(0)
|
||||||
, mAsyncInputStreams(0)
|
, mAsyncInputStreams(0)
|
||||||
|
, mInputStreamLengths(0)
|
||||||
|
, mAsyncInputStreamLengths(0)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
@ -1117,6 +1146,315 @@ nsMultiplexInputStream::Clone(nsIInputStream** aClone)
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
nsMultiplexInputStream::Length(int64_t* aLength)
|
||||||
|
{
|
||||||
|
MutexAutoLock lock(mLock);
|
||||||
|
|
||||||
|
if (mCurrentStream > 0 || mStartedReadingCurrent) {
|
||||||
|
return NS_ERROR_NOT_AVAILABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckedInt64 length = 0;
|
||||||
|
nsresult retval = NS_OK;
|
||||||
|
|
||||||
|
for (uint32_t i = 0, len = mStreams.Length(); i < len; ++i) {
|
||||||
|
nsCOMPtr<nsIInputStreamLength> substream =
|
||||||
|
do_QueryInterface(mStreams[i].mStream);
|
||||||
|
if (!substream) {
|
||||||
|
// Let's use available as fallback.
|
||||||
|
uint64_t streamAvail = 0;
|
||||||
|
nsresult rv = AvailableMaybeSeek(mStreams[i], &streamAvail);
|
||||||
|
if (rv == NS_BASE_STREAM_CLOSED) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
mStatus = rv;
|
||||||
|
return mStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
length += streamAvail;
|
||||||
|
if (!length.isValid()) {
|
||||||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t size = 0;
|
||||||
|
nsresult rv = substream->Length(&size);
|
||||||
|
if (rv == NS_BASE_STREAM_CLOSED) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rv == NS_ERROR_NOT_AVAILABLE) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If one stream blocks, we all block.
|
||||||
|
if (rv != NS_BASE_STREAM_WOULD_BLOCK &&
|
||||||
|
NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We want to return WOULD_BLOCK if there is 1 stream that blocks. But want
|
||||||
|
// to see if there are other streams with length = -1.
|
||||||
|
if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
|
||||||
|
retval = NS_BASE_STREAM_WOULD_BLOCK;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If one of the stream doesn't know the size, we all don't know the size.
|
||||||
|
if (size == -1) {
|
||||||
|
*aLength = -1;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
length += size;
|
||||||
|
if (!length.isValid()) {
|
||||||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*aLength = length.value();
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
class nsMultiplexInputStream::AsyncWaitLengthHelper final : public nsIInputStreamLengthCallback
|
||||||
|
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NS_DECL_ISUPPORTS
|
||||||
|
|
||||||
|
AsyncWaitLengthHelper()
|
||||||
|
: mStreamNotified(false)
|
||||||
|
, mLength(0)
|
||||||
|
, mNegativeSize(false)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void
|
||||||
|
AddStream(nsIAsyncInputStreamLength* aStream)
|
||||||
|
{
|
||||||
|
mPendingStreams.AppendElement(aStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
AddSize(int64_t aSize)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(!mNegativeSize);
|
||||||
|
|
||||||
|
mLength += aSize;
|
||||||
|
return mLength.isValid();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
NegativeSize()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(!mNegativeSize);
|
||||||
|
mNegativeSize = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
Proceed(nsMultiplexInputStream* aParentStream,
|
||||||
|
nsIEventTarget* aEventTarget,
|
||||||
|
const MutexAutoLock& aProofOfLock)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(!mStream);
|
||||||
|
|
||||||
|
// If we don't need to wait, let's inform the callback immediately.
|
||||||
|
if (mPendingStreams.IsEmpty() || mNegativeSize) {
|
||||||
|
RefPtr<nsMultiplexInputStream> parentStream = aParentStream;
|
||||||
|
int64_t length = -1;
|
||||||
|
if (!mNegativeSize && mLength.isValid()) {
|
||||||
|
length = mLength.value();
|
||||||
|
}
|
||||||
|
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
|
||||||
|
"AsyncWaitLengthHelper",
|
||||||
|
[parentStream, length]() {
|
||||||
|
MutexAutoLock lock(parentStream->GetLock());
|
||||||
|
parentStream->AsyncWaitCompleted(length, lock);
|
||||||
|
});
|
||||||
|
return aEventTarget->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let's store the callback and the parent stream until we have
|
||||||
|
// notifications from the async length streams.
|
||||||
|
|
||||||
|
mStream = aParentStream;
|
||||||
|
|
||||||
|
// Let's activate all the pending streams.
|
||||||
|
for (nsIAsyncInputStreamLength* stream : mPendingStreams) {
|
||||||
|
nsresult rv = stream->AsyncLengthWait(this, aEventTarget);
|
||||||
|
if (rv == NS_BASE_STREAM_CLOSED) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHOD
|
||||||
|
OnInputStreamLengthReady(nsIAsyncInputStreamLength* aStream,
|
||||||
|
int64_t aLength) override
|
||||||
|
{
|
||||||
|
MutexAutoLock lock(mStream->GetLock());
|
||||||
|
|
||||||
|
MOZ_ASSERT(mPendingStreams.Contains(aStream));
|
||||||
|
mPendingStreams.RemoveElement(aStream);
|
||||||
|
|
||||||
|
// Already notified.
|
||||||
|
if (mStreamNotified) {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aLength == -1) {
|
||||||
|
mNegativeSize = true;
|
||||||
|
} else {
|
||||||
|
mLength += aLength;
|
||||||
|
if (!mLength.isValid()) {
|
||||||
|
mNegativeSize = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to wait.
|
||||||
|
if (!mNegativeSize && !mPendingStreams.IsEmpty()) {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let's notify the parent stream.
|
||||||
|
mStreamNotified = true;
|
||||||
|
mStream->AsyncWaitCompleted(mNegativeSize ? -1 : mLength.value(), lock);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
~AsyncWaitLengthHelper() = default;
|
||||||
|
|
||||||
|
RefPtr<nsMultiplexInputStream> mStream;
|
||||||
|
bool mStreamNotified;
|
||||||
|
|
||||||
|
CheckedInt64 mLength;
|
||||||
|
bool mNegativeSize;
|
||||||
|
|
||||||
|
nsTArray<nsCOMPtr<nsIAsyncInputStreamLength>> mPendingStreams;
|
||||||
|
};
|
||||||
|
|
||||||
|
NS_IMPL_ISUPPORTS(nsMultiplexInputStream::AsyncWaitLengthHelper,
|
||||||
|
nsIInputStreamLengthCallback)
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
nsMultiplexInputStream::AsyncLengthWait(nsIInputStreamLengthCallback* aCallback,
|
||||||
|
nsIEventTarget* aEventTarget)
|
||||||
|
{
|
||||||
|
if (NS_WARN_IF(!aEventTarget)) {
|
||||||
|
return NS_ERROR_NULL_POINTER;
|
||||||
|
}
|
||||||
|
|
||||||
|
MutexAutoLock lock(mLock);
|
||||||
|
|
||||||
|
if (mCurrentStream > 0 || mStartedReadingCurrent) {
|
||||||
|
return NS_ERROR_NOT_AVAILABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!aCallback) {
|
||||||
|
mAsyncWaitLengthCallback = nullptr;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have a pending operation! Let's use this instead of creating a new one.
|
||||||
|
if (mAsyncWaitLengthHelper) {
|
||||||
|
mAsyncWaitLengthCallback = aCallback;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<AsyncWaitLengthHelper> helper = new AsyncWaitLengthHelper();
|
||||||
|
|
||||||
|
for (uint32_t i = 0, len = mStreams.Length(); i < len; ++i) {
|
||||||
|
nsCOMPtr<nsIAsyncInputStreamLength> asyncStream =
|
||||||
|
do_QueryInterface(mStreams[i].mStream);
|
||||||
|
if (asyncStream) {
|
||||||
|
helper->AddStream(asyncStream);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<nsIInputStreamLength> stream =
|
||||||
|
do_QueryInterface(mStreams[i].mStream);
|
||||||
|
if (!stream) {
|
||||||
|
// Let's use available as fallback.
|
||||||
|
uint64_t streamAvail = 0;
|
||||||
|
nsresult rv = AvailableMaybeSeek(mStreams[i], &streamAvail);
|
||||||
|
if (rv == NS_BASE_STREAM_CLOSED) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
mStatus = rv;
|
||||||
|
return mStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NS_WARN_IF(!helper->AddSize(streamAvail))) {
|
||||||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t size = 0;
|
||||||
|
nsresult rv = stream->Length(&size);
|
||||||
|
if (rv == NS_BASE_STREAM_CLOSED) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_ASSERT(rv != NS_BASE_STREAM_WOULD_BLOCK,
|
||||||
|
"A nsILengthInutStream returns NS_BASE_STREAM_WOULD_BLOCK but it doesn't implement nsIAsyncInputStreamLength.");
|
||||||
|
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size == -1) {
|
||||||
|
helper->NegativeSize();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NS_WARN_IF(!helper->AddSize(size))) {
|
||||||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult rv = helper->Proceed(this, aEventTarget, lock);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
mAsyncWaitLengthHelper = helper;
|
||||||
|
mAsyncWaitLengthCallback = aCallback;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsMultiplexInputStream::AsyncWaitCompleted(int64_t aLength,
|
||||||
|
const MutexAutoLock& aProofOfLock)
|
||||||
|
{
|
||||||
|
nsCOMPtr<nsIInputStreamLengthCallback> callback;
|
||||||
|
callback.swap(mAsyncWaitLengthCallback);
|
||||||
|
|
||||||
|
mAsyncWaitLengthHelper = nullptr;
|
||||||
|
|
||||||
|
// Already canceled.
|
||||||
|
if (!callback) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MutexAutoUnlock unlock(mLock);
|
||||||
|
callback->OnInputStreamLengthReady(this, aLength);
|
||||||
|
}
|
||||||
|
|
||||||
#define MAYBE_UPDATE_VALUE_REAL(x, y) \
|
#define MAYBE_UPDATE_VALUE_REAL(x, y) \
|
||||||
if (y) { \
|
if (y) { \
|
||||||
if (aCount == 1) { \
|
if (aCount == 1) { \
|
||||||
@ -1143,6 +1481,8 @@ nsMultiplexInputStream::UpdateQIMap(StreamData& aStream, int32_t aCount)
|
|||||||
MAYBE_UPDATE_VALUE(mIPCSerializableStreams, nsIIPCSerializableInputStream)
|
MAYBE_UPDATE_VALUE(mIPCSerializableStreams, nsIIPCSerializableInputStream)
|
||||||
MAYBE_UPDATE_VALUE(mCloneableStreams, nsICloneableInputStream)
|
MAYBE_UPDATE_VALUE(mCloneableStreams, nsICloneableInputStream)
|
||||||
MAYBE_UPDATE_VALUE_REAL(mAsyncInputStreams, aStream.mAsyncStream)
|
MAYBE_UPDATE_VALUE_REAL(mAsyncInputStreams, aStream.mAsyncStream)
|
||||||
|
MAYBE_UPDATE_VALUE(mInputStreamLengths, nsIInputStreamLength)
|
||||||
|
MAYBE_UPDATE_VALUE(mAsyncInputStreamLengths, nsIAsyncInputStreamLength)
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef MAYBE_UPDATE_VALUE
|
#undef MAYBE_UPDATE_VALUE
|
||||||
@ -1172,3 +1512,15 @@ nsMultiplexInputStream::IsAsyncInputStream() const
|
|||||||
// substream implements that interface.
|
// substream implements that interface.
|
||||||
return !!mAsyncInputStreams;
|
return !!mAsyncInputStreams;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
nsMultiplexInputStream::IsInputStreamLength() const
|
||||||
|
{
|
||||||
|
return !!mInputStreamLengths;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
nsMultiplexInputStream::IsAsyncInputStreamLength() const
|
||||||
|
{
|
||||||
|
return !!mAsyncInputStreamLengths;
|
||||||
|
}
|
||||||
|
@ -386,3 +386,148 @@ TEST(TestMultiplexInputStream, Available) {
|
|||||||
ASSERT_EQ(NS_OK, rv);
|
ASSERT_EQ(NS_OK, rv);
|
||||||
ASSERT_EQ(buffer.Length(), length);
|
ASSERT_EQ(buffer.Length(), length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(TestMultiplexInputStream, QILengthInputStream) {
|
||||||
|
nsCString buf;
|
||||||
|
buf.AssignLiteral("Hello world");
|
||||||
|
|
||||||
|
nsCOMPtr<nsIMultiplexInputStream> multiplexStream =
|
||||||
|
do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1");
|
||||||
|
|
||||||
|
// nsMultiplexInputStream doesn't expose nsIInputStreamLength if there are
|
||||||
|
// no nsIInputStreamLength sub streams.
|
||||||
|
{
|
||||||
|
nsCOMPtr<nsIInputStream> inputStream;
|
||||||
|
nsresult rv = NS_NewCStringInputStream(getter_AddRefs(inputStream), buf);
|
||||||
|
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||||
|
|
||||||
|
rv = multiplexStream->AppendStream(inputStream);
|
||||||
|
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||||
|
|
||||||
|
nsCOMPtr<nsIInputStreamLength> fsis = do_QueryInterface(multiplexStream);
|
||||||
|
ASSERT_TRUE(!fsis);
|
||||||
|
|
||||||
|
nsCOMPtr<nsIAsyncInputStreamLength> afsis = do_QueryInterface(multiplexStream);
|
||||||
|
ASSERT_TRUE(!afsis);
|
||||||
|
}
|
||||||
|
|
||||||
|
// nsMultiplexInputStream exposes nsIInputStreamLength if there is one or
|
||||||
|
// more nsIInputStreamLength sub streams.
|
||||||
|
{
|
||||||
|
RefPtr<testing::LengthInputStream> inputStream =
|
||||||
|
new testing::LengthInputStream(buf, true, false);
|
||||||
|
|
||||||
|
nsresult rv = multiplexStream->AppendStream(inputStream);
|
||||||
|
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||||
|
|
||||||
|
nsCOMPtr<nsIInputStreamLength> fsis = do_QueryInterface(multiplexStream);
|
||||||
|
ASSERT_TRUE(!!fsis);
|
||||||
|
|
||||||
|
nsCOMPtr<nsIAsyncInputStreamLength> afsis = do_QueryInterface(multiplexStream);
|
||||||
|
ASSERT_TRUE(!afsis);
|
||||||
|
}
|
||||||
|
|
||||||
|
// nsMultiplexInputStream exposes nsIAsyncInputStreamLength if there is one
|
||||||
|
// or more nsIAsyncInputStreamLength sub streams.
|
||||||
|
{
|
||||||
|
RefPtr<testing::LengthInputStream> inputStream =
|
||||||
|
new testing::LengthInputStream(buf, true, true);
|
||||||
|
|
||||||
|
nsresult rv = multiplexStream->AppendStream(inputStream);
|
||||||
|
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||||
|
|
||||||
|
nsCOMPtr<nsIInputStreamLength> fsis = do_QueryInterface(multiplexStream);
|
||||||
|
ASSERT_TRUE(!!fsis);
|
||||||
|
|
||||||
|
nsCOMPtr<nsIAsyncInputStreamLength> afsis = do_QueryInterface(multiplexStream);
|
||||||
|
ASSERT_TRUE(!!afsis);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TestMultiplexInputStream, LengthInputStream) {
|
||||||
|
nsCOMPtr<nsIMultiplexInputStream> multiplexStream =
|
||||||
|
do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1");
|
||||||
|
|
||||||
|
// First stream is a a simple one.
|
||||||
|
nsCString buf;
|
||||||
|
buf.AssignLiteral("Hello world");
|
||||||
|
|
||||||
|
nsCOMPtr<nsIInputStream> inputStream;
|
||||||
|
nsresult rv = NS_NewCStringInputStream(getter_AddRefs(inputStream), buf);
|
||||||
|
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||||
|
|
||||||
|
rv = multiplexStream->AppendStream(inputStream);
|
||||||
|
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||||
|
|
||||||
|
// A LengthInputStream, non-async.
|
||||||
|
RefPtr<testing::LengthInputStream> lengthStream =
|
||||||
|
new testing::LengthInputStream(buf, true, false);
|
||||||
|
|
||||||
|
rv = multiplexStream->AppendStream(lengthStream);
|
||||||
|
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||||
|
|
||||||
|
nsCOMPtr<nsIInputStreamLength> fsis = do_QueryInterface(multiplexStream);
|
||||||
|
ASSERT_TRUE(!!fsis);
|
||||||
|
|
||||||
|
// Size is the sum of the 2 streams.
|
||||||
|
int64_t length;
|
||||||
|
rv = fsis->Length(&length);
|
||||||
|
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||||
|
ASSERT_EQ(buf.Length() * 2, length);
|
||||||
|
|
||||||
|
// An async LengthInputStream.
|
||||||
|
RefPtr<testing::LengthInputStream> asyncLengthStream =
|
||||||
|
new testing::LengthInputStream(buf, true, true, NS_BASE_STREAM_WOULD_BLOCK);
|
||||||
|
|
||||||
|
rv = multiplexStream->AppendStream(asyncLengthStream);
|
||||||
|
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||||
|
|
||||||
|
nsCOMPtr<nsIAsyncInputStreamLength> afsis = do_QueryInterface(multiplexStream);
|
||||||
|
ASSERT_TRUE(!!afsis);
|
||||||
|
|
||||||
|
// Now it would block.
|
||||||
|
rv = fsis->Length(&length);
|
||||||
|
ASSERT_EQ(NS_BASE_STREAM_WOULD_BLOCK, rv);
|
||||||
|
|
||||||
|
// Let's read the size async.
|
||||||
|
RefPtr<testing::LengthCallback> callback = new testing::LengthCallback();
|
||||||
|
rv = afsis->AsyncLengthWait(callback, GetCurrentThreadSerialEventTarget());
|
||||||
|
ASSERT_EQ(NS_OK, rv);
|
||||||
|
|
||||||
|
MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return callback->Called(); }));
|
||||||
|
ASSERT_EQ(buf.Length() * 3, callback->Size());
|
||||||
|
|
||||||
|
// Now a negative stream
|
||||||
|
lengthStream =
|
||||||
|
new testing::LengthInputStream(buf, true, false, NS_OK, true);
|
||||||
|
|
||||||
|
rv = multiplexStream->AppendStream(lengthStream);
|
||||||
|
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||||
|
|
||||||
|
rv = fsis->Length(&length);
|
||||||
|
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||||
|
ASSERT_EQ(-1, length);
|
||||||
|
|
||||||
|
// Another async LengthInputStream.
|
||||||
|
asyncLengthStream =
|
||||||
|
new testing::LengthInputStream(buf, true, true, NS_BASE_STREAM_WOULD_BLOCK);
|
||||||
|
|
||||||
|
rv = multiplexStream->AppendStream(asyncLengthStream);
|
||||||
|
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||||
|
|
||||||
|
afsis = do_QueryInterface(multiplexStream);
|
||||||
|
ASSERT_TRUE(!!afsis);
|
||||||
|
|
||||||
|
// Let's read the size async.
|
||||||
|
RefPtr<testing::LengthCallback> callback1 = new testing::LengthCallback();
|
||||||
|
rv = afsis->AsyncLengthWait(callback1, GetCurrentThreadSerialEventTarget());
|
||||||
|
ASSERT_EQ(NS_OK, rv);
|
||||||
|
|
||||||
|
RefPtr<testing::LengthCallback> callback2 = new testing::LengthCallback();
|
||||||
|
rv = afsis->AsyncLengthWait(callback2, GetCurrentThreadSerialEventTarget());
|
||||||
|
ASSERT_EQ(NS_OK, rv);
|
||||||
|
|
||||||
|
MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return callback2->Called(); }));
|
||||||
|
ASSERT_FALSE(callback1->Called());
|
||||||
|
ASSERT_TRUE(callback2->Called());
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user