Bug 1413920 - nsMultiplexInputStream should call OnInputStreamReady on close, r=smaug

This commit is contained in:
Andrea Marchesini 2017-11-06 14:20:15 +01:00
parent afe8ac1278
commit cc7b359311
5 changed files with 225 additions and 107 deletions

View File

@ -1,112 +1,11 @@
#include "gtest/gtest.h"
#include "nsBufferedStreams.h"
#include "nsCOMPtr.h"
#include "nsStreamUtils.h"
#include "nsString.h"
#include "nsStringStream.h"
#include "nsThreadUtils.h"
#include "Helpers.h"
class AsyncStringStream final : public nsIAsyncInputStream
{
nsCOMPtr<nsIInputStream> mStream;
public:
NS_DECL_THREADSAFE_ISUPPORTS
explicit AsyncStringStream(const nsACString& aBuffer)
{
NS_NewCStringInputStream(getter_AddRefs(mStream), aBuffer);
}
NS_IMETHOD
Available(uint64_t* aLength) override
{
return mStream->Available(aLength);
}
NS_IMETHOD
Read(char* aBuffer, uint32_t aCount, uint32_t* aReadCount) override
{
return mStream->Read(aBuffer, aCount, aReadCount);
}
NS_IMETHOD
ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
uint32_t aCount, uint32_t *aResult) override
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHOD
Close() override
{
nsresult rv = mStream->Close();
if (NS_SUCCEEDED(rv)) {
MaybeExecCallback(mCallback, mCallbackEventTarget);
}
return rv;
}
NS_IMETHOD
IsNonBlocking(bool* aNonBlocking) override
{
return mStream->IsNonBlocking(aNonBlocking);
}
NS_IMETHOD
CloseWithStatus(nsresult aStatus) override
{
return Close();
}
NS_IMETHOD
AsyncWait(nsIInputStreamCallback* aCallback,
uint32_t aFlags, uint32_t aRequestedCount,
nsIEventTarget* aEventTarget) override
{
if (aFlags & nsIAsyncInputStream::WAIT_CLOSURE_ONLY) {
mCallback = aCallback;
mCallbackEventTarget = aEventTarget;
return NS_OK;
}
MaybeExecCallback(aCallback, aEventTarget);
return NS_OK;
}
void
MaybeExecCallback(nsIInputStreamCallback* aCallback,
nsIEventTarget* aEventTarget)
{
if (!aCallback) {
return;
}
nsCOMPtr<nsIInputStreamCallback> callback = aCallback;
nsCOMPtr<nsIAsyncInputStream> self = this;
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
"AsyncWait", [callback, self]() { callback->OnInputStreamReady(self); });
if (aEventTarget) {
aEventTarget->Dispatch(r.forget());
} else {
r->Run();
}
}
private:
~AsyncStringStream() = default;
nsCOMPtr<nsIInputStreamCallback> mCallback;
nsCOMPtr<nsIEventTarget> mCallbackEventTarget;
};
NS_IMPL_ISUPPORTS(AsyncStringStream, nsIAsyncInputStream, nsIInputStream)
// Helper function for creating a AsyncStringStream
// Helper function for creating a testing::AsyncStringStream
already_AddRefed<nsBufferedInputStream>
CreateStream(uint32_t aSize, nsCString& aBuffer)
{
@ -115,7 +14,7 @@ CreateStream(uint32_t aSize, nsCString& aBuffer)
aBuffer.BeginWriting()[i] = i % 10;
}
nsCOMPtr<nsIInputStream> stream = new AsyncStringStream(aBuffer);
nsCOMPtr<nsIInputStream> stream = new testing::AsyncStringStream(aBuffer);
RefPtr<nsBufferedInputStream> bis = new nsBufferedInputStream();
bis->Init(stream, aSize);

View File

@ -255,8 +255,6 @@ nsMultiplexInputStream::Close()
}
}
mAsyncWaitCallback = nullptr;
return rv;
}

View File

@ -13,6 +13,7 @@
#include "nsIOutputStream.h"
#include "nsStreamUtils.h"
#include "nsTArray.h"
#include "nsThreadUtils.h"
namespace testing {
@ -130,4 +131,88 @@ InputStreamCallback::OnInputStreamReady(nsIAsyncInputStream* aStream)
return NS_OK;
}
AsyncStringStream::AsyncStringStream(const nsACString& aBuffer)
{
NS_NewCStringInputStream(getter_AddRefs(mStream), aBuffer);
}
NS_IMETHODIMP
AsyncStringStream::Available(uint64_t* aLength)
{
return mStream->Available(aLength);
}
NS_IMETHODIMP
AsyncStringStream::Read(char* aBuffer, uint32_t aCount, uint32_t* aReadCount)
{
return mStream->Read(aBuffer, aCount, aReadCount);
}
NS_IMETHODIMP
AsyncStringStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
uint32_t aCount, uint32_t *aResult)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
AsyncStringStream::Close()
{
nsresult rv = mStream->Close();
if (NS_SUCCEEDED(rv)) {
MaybeExecCallback(mCallback, mCallbackEventTarget);
}
return rv;
}
NS_IMETHODIMP
AsyncStringStream::IsNonBlocking(bool* aNonBlocking)
{
return mStream->IsNonBlocking(aNonBlocking);
}
NS_IMETHODIMP
AsyncStringStream::CloseWithStatus(nsresult aStatus)
{
return Close();
}
NS_IMETHODIMP
AsyncStringStream::AsyncWait(nsIInputStreamCallback* aCallback,
uint32_t aFlags, uint32_t aRequestedCount,
nsIEventTarget* aEventTarget)
{
if (aFlags & nsIAsyncInputStream::WAIT_CLOSURE_ONLY) {
mCallback = aCallback;
mCallbackEventTarget = aEventTarget;
return NS_OK;
}
MaybeExecCallback(aCallback, aEventTarget);
return NS_OK;
}
void
AsyncStringStream::MaybeExecCallback(nsIInputStreamCallback* aCallback,
nsIEventTarget* aEventTarget)
{
if (!aCallback) {
return;
}
nsCOMPtr<nsIInputStreamCallback> callback = aCallback;
nsCOMPtr<nsIAsyncInputStream> self = this;
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
"AsyncWait", [callback, self]() { callback->OnInputStreamReady(self); });
if (aEventTarget) {
aEventTarget->Dispatch(r.forget());
} else {
r->Run();
}
}
NS_IMPL_ISUPPORTS(AsyncStringStream, nsIAsyncInputStream, nsIInputStream)
} // namespace testing

View File

@ -7,9 +7,11 @@
#ifndef __Helpers_h
#define __Helpers_h
#include "nsCOMPtr.h"
#include "nsIAsyncInputStream.h"
#include "nsIAsyncOutputStream.h"
#include "nsString.h"
#include "nsStringStream.h"
#include "nsTArrayForwardDeclare.h"
#include <stdint.h>
@ -68,6 +70,28 @@ public:
NS_DECL_NSIINPUTSTREAMCALLBACK
};
class AsyncStringStream final : public nsIAsyncInputStream
{
nsCOMPtr<nsIInputStream> mStream;
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIINPUTSTREAM
NS_DECL_NSIASYNCINPUTSTREAM
explicit AsyncStringStream(const nsACString& aBuffer);
private:
~AsyncStringStream() = default;
void
MaybeExecCallback(nsIInputStreamCallback* aCallback,
nsIEventTarget* aEventTarget);
nsCOMPtr<nsIInputStreamCallback> mCallback;
nsCOMPtr<nsIEventTarget> mCallbackEventTarget;
};
} // namespace testing
#endif // __Helpers_h

View File

@ -5,12 +5,13 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "gtest/gtest.h"
#include "nsIAsyncInputStream.h"
#include "nsComponentManagerUtils.h"
#include "nsCOMPtr.h"
#include "nsIInputStream.h"
#include "nsIMultiplexInputStream.h"
#include "nsISeekableStream.h"
#include "nsStringStream.h"
#include "nsThreadUtils.h"
#include "Helpers.h"
TEST(MultiplexInputStream, Seek_SET)
{
@ -101,3 +102,114 @@ TEST(MultiplexInputStream, Seek_SET)
ASSERT_EQ((uint64_t)buf2.Length() - 6 + buf3.Length(), length);
ASSERT_EQ(0, strncmp(readBuf, "The qu", count));
}
already_AddRefed<nsIInputStream>
CreateStreamHelper()
{
nsCOMPtr<nsIMultiplexInputStream> multiplexStream =
do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1");
nsCString buf1;
buf1.AssignLiteral("Hello");
nsCOMPtr<nsIInputStream> inputStream1 = new testing::AsyncStringStream(buf1);
multiplexStream->AppendStream(inputStream1);
nsCString buf2;
buf2.AssignLiteral(" ");
nsCOMPtr<nsIInputStream> inputStream2 = new testing::AsyncStringStream(buf2);
multiplexStream->AppendStream(inputStream2);
nsCString buf3;
buf3.AssignLiteral("World!");
nsCOMPtr<nsIInputStream> inputStream3 = new testing::AsyncStringStream(buf3);
multiplexStream->AppendStream(inputStream3);
nsCOMPtr<nsIInputStream> stream(do_QueryInterface(multiplexStream));
return stream.forget();
}
// AsyncWait - without EventTarget
TEST(TestMultiplexInputStream, AsyncWait_withoutEventTarget) {
nsCOMPtr<nsIInputStream> is = CreateStreamHelper();
nsCOMPtr<nsIAsyncInputStream> ais = do_QueryInterface(is);
ASSERT_TRUE(!!ais);
RefPtr<testing::InputStreamCallback> cb =
new testing::InputStreamCallback();
ASSERT_EQ(NS_OK, ais->AsyncWait(cb, 0, 0, nullptr));
ASSERT_FALSE(cb->Called());
// Eventually it is called.
MOZ_ALWAYS_TRUE(mozilla::SpinEventLoopUntil([&]() { return cb->Called(); }));
ASSERT_TRUE(cb->Called());
}
// AsyncWait - with EventTarget
TEST(TestMultiplexInputStream, AsyncWait_withEventTarget) {
nsCOMPtr<nsIInputStream> is = CreateStreamHelper();
nsCOMPtr<nsIAsyncInputStream> ais = do_QueryInterface(is);
ASSERT_TRUE(!!ais);
RefPtr<testing::InputStreamCallback> cb =
new testing::InputStreamCallback();
nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
ASSERT_EQ(NS_OK, ais->AsyncWait(cb, 0, 0, thread));
ASSERT_FALSE(cb->Called());
// Eventually it is called.
MOZ_ALWAYS_TRUE(mozilla::SpinEventLoopUntil([&]() { return cb->Called(); }));
ASSERT_TRUE(cb->Called());
}
// AsyncWait - without EventTarget - closureOnly
TEST(TestMultiplexInputStream, AsyncWait_withoutEventTarget_closureOnly) {
nsCOMPtr<nsIInputStream> is = CreateStreamHelper();
nsCOMPtr<nsIAsyncInputStream> ais = do_QueryInterface(is);
ASSERT_TRUE(!!ais);
RefPtr<testing::InputStreamCallback> cb =
new testing::InputStreamCallback();
ASSERT_EQ(NS_OK, ais->AsyncWait(cb, nsIAsyncInputStream::WAIT_CLOSURE_ONLY,
0, nullptr));
ASSERT_FALSE(cb->Called());
ais->CloseWithStatus(NS_ERROR_FAILURE);
ASSERT_FALSE(cb->Called());
// Eventually it is called.
MOZ_ALWAYS_TRUE(mozilla::SpinEventLoopUntil([&]() { return cb->Called(); }));
ASSERT_TRUE(cb->Called());
}
// AsyncWait - withEventTarget - closureOnly
TEST(TestMultiplexInputStream, AsyncWait_withEventTarget_closureOnly) {
nsCOMPtr<nsIInputStream> is = CreateStreamHelper();
nsCOMPtr<nsIAsyncInputStream> ais = do_QueryInterface(is);
ASSERT_TRUE(!!ais);
RefPtr<testing::InputStreamCallback> cb =
new testing::InputStreamCallback();
nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
ASSERT_EQ(NS_OK, ais->AsyncWait(cb, nsIAsyncInputStream::WAIT_CLOSURE_ONLY,
0, thread));
ASSERT_FALSE(cb->Called());
ais->CloseWithStatus(NS_ERROR_FAILURE);
ASSERT_FALSE(cb->Called());
// Eventually it is called.
MOZ_ALWAYS_TRUE(mozilla::SpinEventLoopUntil([&]() { return cb->Called(); }));
ASSERT_TRUE(cb->Called());
}