mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:18:48 +00:00
import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1166504 - Make nsMultiplexInputStream cloneable. r=bkelly,froydnj (e3410f3a9) - Bug 1161240 - Make sure that NS_CloneInputStream correctly deals with null input; r=froydnj (b8afd03ec) - Bug 1059081 - Fix non-unified bustage. a=bustage (135927526) - Bug 1059081 - More non-unified bustage fixage. (d8fd4ad65) - Bug 1059081 - Add a threadsafe wrapper for persistent nsMultiplexStream queues. r=nfroyd (1060af97f) - Bug 1163029 - Use NamedDecl::getName instead of expensive NamedDecl::getNameAsString. r=ehsan (97a8a32a6) - Bug 1156084 - Disallow AddRef() and Release() calls on the return value of methods returning XPCOM objects; r=jrmuizel (96f6567c8)
This commit is contained in:
@@ -487,8 +487,8 @@ bool classHasAddRefRelease(const CXXRecordDecl *D) {
|
||||
bool seenAddRef = false;
|
||||
bool seenRelease = false;
|
||||
for (CXXRecordDecl::method_iterator method = D->method_begin();
|
||||
method != D->method_end(); ++method) {
|
||||
std::string name = method->getNameAsString();
|
||||
method != D->method_end(); ++method) {
|
||||
const auto &name = method->getName();
|
||||
if (name == "AddRef") {
|
||||
seenAddRef = true;
|
||||
} else if (name == "Release") {
|
||||
@@ -743,7 +743,7 @@ AST_MATCHER(MemberExpr, isAddRefOrRelease) {
|
||||
ValueDecl *Member = Node.getMemberDecl();
|
||||
CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(Member);
|
||||
if (Method) {
|
||||
std::string Name = Method->getNameAsString();
|
||||
const auto &Name = Method->getName();
|
||||
return Name == "AddRef" || Name == "Release";
|
||||
}
|
||||
return false;
|
||||
@@ -890,11 +890,19 @@ DiagnosticsMatcher::DiagnosticsMatcher()
|
||||
)).bind("node"),
|
||||
&nanExprChecker);
|
||||
|
||||
// First, look for direct parents of the MemberExpr.
|
||||
astMatcher.addMatcher(callExpr(callee(functionDecl(hasNoAddRefReleaseOnReturnAttr()).bind("func")),
|
||||
hasParent(memberExpr(isAddRefOrRelease(),
|
||||
hasParent(callExpr())).bind("member")
|
||||
)).bind("node"),
|
||||
&noAddRefReleaseOnReturnChecker);
|
||||
// Then, look for MemberExpr that need to be casted to the right type using
|
||||
// an intermediary CastExpr before we get to the CallExpr.
|
||||
astMatcher.addMatcher(callExpr(callee(functionDecl(hasNoAddRefReleaseOnReturnAttr()).bind("func")),
|
||||
hasParent(castExpr(hasParent(memberExpr(isAddRefOrRelease(),
|
||||
hasParent(callExpr())).bind("member"))))
|
||||
).bind("node"),
|
||||
&noAddRefReleaseOnReturnChecker);
|
||||
|
||||
// Match declrefs with type "pointer to object of ref-counted type" inside a
|
||||
// lambda, where the declaration they reference is not inside the lambda.
|
||||
|
||||
@@ -6,12 +6,20 @@ struct Test {
|
||||
void foo();
|
||||
};
|
||||
|
||||
struct TestD : Test {};
|
||||
|
||||
struct S {
|
||||
Test* f() MOZ_NO_ADDREF_RELEASE_ON_RETURN;
|
||||
Test& g() MOZ_NO_ADDREF_RELEASE_ON_RETURN;
|
||||
Test h() MOZ_NO_ADDREF_RELEASE_ON_RETURN;
|
||||
};
|
||||
|
||||
struct SD {
|
||||
TestD* f() MOZ_NO_ADDREF_RELEASE_ON_RETURN;
|
||||
TestD& g() MOZ_NO_ADDREF_RELEASE_ON_RETURN;
|
||||
TestD h() MOZ_NO_ADDREF_RELEASE_ON_RETURN;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct X {
|
||||
T* f() MOZ_NO_ADDREF_RELEASE_ON_RETURN;
|
||||
@@ -28,6 +36,10 @@ Test* f() MOZ_NO_ADDREF_RELEASE_ON_RETURN;
|
||||
Test& g() MOZ_NO_ADDREF_RELEASE_ON_RETURN;
|
||||
Test h() MOZ_NO_ADDREF_RELEASE_ON_RETURN;
|
||||
|
||||
TestD* fd() MOZ_NO_ADDREF_RELEASE_ON_RETURN;
|
||||
TestD& gd() MOZ_NO_ADDREF_RELEASE_ON_RETURN;
|
||||
TestD hd() MOZ_NO_ADDREF_RELEASE_ON_RETURN;
|
||||
|
||||
void test() {
|
||||
S s;
|
||||
s.f()->AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'f'}}
|
||||
@@ -39,6 +51,16 @@ void test() {
|
||||
s.h().AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'h'}}
|
||||
s.h().Release(); // expected-error{{'Release' cannot be called on the return value of 'h'}}
|
||||
s.h().foo();
|
||||
SD sd;
|
||||
sd.f()->AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'f'}}
|
||||
sd.f()->Release(); // expected-error{{'Release' cannot be called on the return value of 'f'}}
|
||||
sd.f()->foo();
|
||||
sd.g().AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'g'}}
|
||||
sd.g().Release(); // expected-error{{'Release' cannot be called on the return value of 'g'}}
|
||||
sd.g().foo();
|
||||
sd.h().AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'h'}}
|
||||
sd.h().Release(); // expected-error{{'Release' cannot be called on the return value of 'h'}}
|
||||
sd.h().foo();
|
||||
X<Test> x;
|
||||
x.f()->AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'f'}}
|
||||
x.f()->Release(); // expected-error{{'Release' cannot be called on the return value of 'f'}}
|
||||
@@ -49,10 +71,24 @@ void test() {
|
||||
x.h().AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'h'}}
|
||||
x.h().Release(); // expected-error{{'Release' cannot be called on the return value of 'h'}}
|
||||
x.h().foo();
|
||||
X<TestD> xd;
|
||||
xd.f()->AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'f'}}
|
||||
xd.f()->Release(); // expected-error{{'Release' cannot be called on the return value of 'f'}}
|
||||
xd.f()->foo();
|
||||
xd.g().AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'g'}}
|
||||
xd.g().Release(); // expected-error{{'Release' cannot be called on the return value of 'g'}}
|
||||
xd.g().foo();
|
||||
xd.h().AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'h'}}
|
||||
xd.h().Release(); // expected-error{{'Release' cannot be called on the return value of 'h'}}
|
||||
xd.h().foo();
|
||||
SP<Test> sp;
|
||||
sp->AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'operator->'}}
|
||||
sp->Release(); // expected-error{{'Release' cannot be called on the return value of 'operator->'}}
|
||||
sp->foo();
|
||||
SP<TestD> spd;
|
||||
spd->AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'operator->'}}
|
||||
spd->Release(); // expected-error{{'Release' cannot be called on the return value of 'operator->'}}
|
||||
spd->foo();
|
||||
f()->AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'f'}}
|
||||
f()->Release(); // expected-error{{'Release' cannot be called on the return value of 'f'}}
|
||||
f()->foo();
|
||||
@@ -62,4 +98,13 @@ void test() {
|
||||
h().AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'h'}}
|
||||
h().Release(); // expected-error{{'Release' cannot be called on the return value of 'h'}}
|
||||
h().foo();
|
||||
fd()->AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'fd'}}
|
||||
fd()->Release(); // expected-error{{'Release' cannot be called on the return value of 'fd'}}
|
||||
fd()->foo();
|
||||
gd().AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'gd'}}
|
||||
gd().Release(); // expected-error{{'Release' cannot be called on the return value of 'gd'}}
|
||||
gd().foo();
|
||||
hd().AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'hd'}}
|
||||
hd().Release(); // expected-error{{'Release' cannot be called on the return value of 'hd'}}
|
||||
hd().foo();
|
||||
}
|
||||
|
||||
@@ -3039,15 +3039,16 @@ public:
|
||||
NS_IMETHOD SetOriginalURI(nsIURI*) NO_IMPL
|
||||
NS_IMETHOD GetURI(nsIURI** aUri) override
|
||||
{
|
||||
NS_IF_ADDREF(mUri);
|
||||
*aUri = mUri;
|
||||
nsCOMPtr<nsIURI> copy = mUri;
|
||||
copy.forget(aUri);
|
||||
return NS_OK;
|
||||
}
|
||||
NS_IMETHOD GetOwner(nsISupports**) NO_IMPL
|
||||
NS_IMETHOD SetOwner(nsISupports*) NO_IMPL
|
||||
NS_IMETHOD GetLoadInfo(nsILoadInfo** aLoadInfo) override
|
||||
{
|
||||
NS_IF_ADDREF(*aLoadInfo = mLoadInfo);
|
||||
nsCOMPtr<nsILoadInfo> copy = mLoadInfo;
|
||||
copy.forget(aLoadInfo);
|
||||
return NS_OK;
|
||||
}
|
||||
NS_IMETHOD SetLoadInfo(nsILoadInfo* aLoadInfo) override
|
||||
|
||||
@@ -277,8 +277,8 @@ GeckoMediaPluginService::GetThread(nsIThread** aThread)
|
||||
InitializePlugins();
|
||||
}
|
||||
|
||||
NS_ADDREF(mGMPThread);
|
||||
*aThread = mGMPThread;
|
||||
nsCOMPtr<nsIThread> copy = mGMPThread;
|
||||
copy.forget(aThread);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@@ -252,7 +252,7 @@ GetRetainedImageFromSourceSurface(SourceSurface *aSurface)
|
||||
if (!data) {
|
||||
MOZ_CRASH("unsupported source surface");
|
||||
}
|
||||
data->AddRef();
|
||||
data.get()->AddRef();
|
||||
return CreateCGImage(releaseDataSurface, data.get(),
|
||||
data->GetData(), data->GetSize(),
|
||||
data->Stride(), data->GetFormat());
|
||||
|
||||
@@ -465,8 +465,8 @@ FTPChannelParent::GetInterface(const nsIID& uuid, void** result)
|
||||
{
|
||||
// Only support nsILoadContext if child channel's callbacks did too
|
||||
if (uuid.Equals(NS_GET_IID(nsILoadContext)) && mLoadContext) {
|
||||
NS_ADDREF(mLoadContext);
|
||||
*result = static_cast<nsILoadContext*>(mLoadContext);
|
||||
nsCOMPtr<nsILoadContext> copy = mLoadContext;
|
||||
copy.forget(result);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -238,8 +238,8 @@ HttpChannelParent::GetInterface(const nsIID& aIID, void **result)
|
||||
|
||||
// Only support nsILoadContext if child channel's callbacks did too
|
||||
if (aIID.Equals(NS_GET_IID(nsILoadContext)) && mLoadContext) {
|
||||
NS_ADDREF(mLoadContext);
|
||||
*result = static_cast<nsILoadContext*>(mLoadContext);
|
||||
nsCOMPtr<nsILoadContext> copy = mLoadContext;
|
||||
copy.forget(result);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -304,8 +304,8 @@ WebSocketChannelParent::GetInterface(const nsIID & iid, void **result)
|
||||
|
||||
// Only support nsILoadContext if child channel's callbacks did too
|
||||
if (iid.Equals(NS_GET_IID(nsILoadContext)) && mLoadContext) {
|
||||
NS_ADDREF(mLoadContext);
|
||||
*result = static_cast<nsILoadContext*>(mLoadContext);
|
||||
nsCOMPtr<nsILoadContext> copy = mLoadContext;
|
||||
copy.forget(result);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -341,8 +341,8 @@ WyciwygChannelParent::GetInterface(const nsIID& uuid, void** result)
|
||||
{
|
||||
// Only support nsILoadContext if child channel's callbacks did too
|
||||
if (uuid.Equals(NS_GET_IID(nsILoadContext)) && mLoadContext) {
|
||||
NS_ADDREF(mLoadContext);
|
||||
*result = static_cast<nsILoadContext*>(mLoadContext);
|
||||
nsCOMPtr<nsILoadContext> copy = mLoadContext;
|
||||
copy.forget(result);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,10 +11,12 @@
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/MathAlgorithms.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
|
||||
#include "base/basictypes.h"
|
||||
|
||||
#include "nsMultiplexInputStream.h"
|
||||
#include "nsICloneableInputStream.h"
|
||||
#include "nsIMultiplexInputStream.h"
|
||||
#include "nsISeekableStream.h"
|
||||
#include "nsCOMPtr.h"
|
||||
@@ -23,6 +25,7 @@
|
||||
#include "nsIIPCSerializableInputStream.h"
|
||||
#include "mozilla/ipc/InputStreamUtils.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::ipc;
|
||||
|
||||
using mozilla::DeprecatedAbs;
|
||||
@@ -31,6 +34,7 @@ class nsMultiplexInputStream final
|
||||
: public nsIMultiplexInputStream
|
||||
, public nsISeekableStream
|
||||
, public nsIIPCSerializableInputStream
|
||||
, public nsICloneableInputStream
|
||||
{
|
||||
public:
|
||||
nsMultiplexInputStream();
|
||||
@@ -40,6 +44,7 @@ public:
|
||||
NS_DECL_NSIMULTIPLEXINPUTSTREAM
|
||||
NS_DECL_NSISEEKABLESTREAM
|
||||
NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
|
||||
NS_DECL_NSICLONEABLEINPUTSTREAM
|
||||
|
||||
private:
|
||||
~nsMultiplexInputStream()
|
||||
@@ -59,6 +64,7 @@ private:
|
||||
const char* aFromRawSegment, uint32_t aToOffset,
|
||||
uint32_t aCount, uint32_t* aWriteCount);
|
||||
|
||||
Mutex mLock; // Protects access to all data members.
|
||||
nsTArray<nsCOMPtr<nsIInputStream>> mStreams;
|
||||
uint32_t mCurrentStream;
|
||||
bool mStartedReadingCurrent;
|
||||
@@ -75,7 +81,8 @@ NS_IMPL_QUERY_INTERFACE_CI(nsMultiplexInputStream,
|
||||
nsIMultiplexInputStream,
|
||||
nsIInputStream,
|
||||
nsISeekableStream,
|
||||
nsIIPCSerializableInputStream)
|
||||
nsIIPCSerializableInputStream,
|
||||
nsICloneableInputStream)
|
||||
NS_IMPL_CI_INTERFACE_GETTER(nsMultiplexInputStream,
|
||||
nsIMultiplexInputStream,
|
||||
nsIInputStream,
|
||||
@@ -119,7 +126,8 @@ TellMaybeSeek(nsISeekableStream* aSeekable, int64_t* aResult)
|
||||
}
|
||||
|
||||
nsMultiplexInputStream::nsMultiplexInputStream()
|
||||
: mCurrentStream(0),
|
||||
: mLock("nsMultiplexInputStream lock"),
|
||||
mCurrentStream(0),
|
||||
mStartedReadingCurrent(false),
|
||||
mStatus(NS_OK)
|
||||
{
|
||||
@@ -129,6 +137,7 @@ nsMultiplexInputStream::nsMultiplexInputStream()
|
||||
NS_IMETHODIMP
|
||||
nsMultiplexInputStream::GetCount(uint32_t* aCount)
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
*aCount = mStreams.Length();
|
||||
return NS_OK;
|
||||
}
|
||||
@@ -150,6 +159,7 @@ SeekableStreamAtBeginning(nsIInputStream* aStream)
|
||||
NS_IMETHODIMP
|
||||
nsMultiplexInputStream::AppendStream(nsIInputStream* aStream)
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
NS_ASSERTION(SeekableStreamAtBeginning(aStream),
|
||||
"Appended stream not at beginning.");
|
||||
return mStreams.AppendElement(aStream) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
|
||||
@@ -159,6 +169,7 @@ nsMultiplexInputStream::AppendStream(nsIInputStream* aStream)
|
||||
NS_IMETHODIMP
|
||||
nsMultiplexInputStream::InsertStream(nsIInputStream* aStream, uint32_t aIndex)
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
NS_ASSERTION(SeekableStreamAtBeginning(aStream),
|
||||
"Inserted stream not at beginning.");
|
||||
mStreams.InsertElementAt(aIndex, aStream);
|
||||
@@ -173,6 +184,7 @@ nsMultiplexInputStream::InsertStream(nsIInputStream* aStream, uint32_t aIndex)
|
||||
NS_IMETHODIMP
|
||||
nsMultiplexInputStream::RemoveStream(uint32_t aIndex)
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
mStreams.RemoveElementAt(aIndex);
|
||||
if (mCurrentStream > aIndex) {
|
||||
--mCurrentStream;
|
||||
@@ -187,6 +199,7 @@ nsMultiplexInputStream::RemoveStream(uint32_t aIndex)
|
||||
NS_IMETHODIMP
|
||||
nsMultiplexInputStream::GetStream(uint32_t aIndex, nsIInputStream** aResult)
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
*aResult = mStreams.SafeElementAt(aIndex, nullptr);
|
||||
if (NS_WARN_IF(!*aResult)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
@@ -200,6 +213,7 @@ nsMultiplexInputStream::GetStream(uint32_t aIndex, nsIInputStream** aResult)
|
||||
NS_IMETHODIMP
|
||||
nsMultiplexInputStream::Close()
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
mStatus = NS_BASE_STREAM_CLOSED;
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
@@ -219,6 +233,7 @@ nsMultiplexInputStream::Close()
|
||||
NS_IMETHODIMP
|
||||
nsMultiplexInputStream::Available(uint64_t* aResult)
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
if (NS_FAILED(mStatus)) {
|
||||
return mStatus;
|
||||
}
|
||||
@@ -243,6 +258,7 @@ nsMultiplexInputStream::Available(uint64_t* aResult)
|
||||
NS_IMETHODIMP
|
||||
nsMultiplexInputStream::Read(char* aBuf, uint32_t aCount, uint32_t* aResult)
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
// It is tempting to implement this method in terms of ReadSegments, but
|
||||
// that would prevent this class from being used with streams that only
|
||||
// implement Read (e.g., file streams).
|
||||
@@ -294,6 +310,8 @@ NS_IMETHODIMP
|
||||
nsMultiplexInputStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
|
||||
uint32_t aCount, uint32_t* aResult)
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
|
||||
if (mStatus == NS_BASE_STREAM_CLOSED) {
|
||||
*aResult = 0;
|
||||
return NS_OK;
|
||||
@@ -371,6 +389,8 @@ nsMultiplexInputStream::ReadSegCb(nsIInputStream* aIn, void* aClosure,
|
||||
NS_IMETHODIMP
|
||||
nsMultiplexInputStream::IsNonBlocking(bool* aNonBlocking)
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
|
||||
uint32_t len = mStreams.Length();
|
||||
if (len == 0) {
|
||||
// Claim to be non-blocking, since we won't block the caller.
|
||||
@@ -399,6 +419,8 @@ nsMultiplexInputStream::IsNonBlocking(bool* aNonBlocking)
|
||||
NS_IMETHODIMP
|
||||
nsMultiplexInputStream::Seek(int32_t aWhence, int64_t aOffset)
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
|
||||
if (NS_FAILED(mStatus)) {
|
||||
return mStatus;
|
||||
}
|
||||
@@ -640,6 +662,8 @@ nsMultiplexInputStream::Seek(int32_t aWhence, int64_t aOffset)
|
||||
NS_IMETHODIMP
|
||||
nsMultiplexInputStream::Tell(int64_t* aResult)
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
|
||||
if (NS_FAILED(mStatus)) {
|
||||
return mStatus;
|
||||
}
|
||||
@@ -697,6 +721,8 @@ void
|
||||
nsMultiplexInputStream::Serialize(InputStreamParams& aParams,
|
||||
FileDescriptorArray& aFileDescriptors)
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
|
||||
MultiplexInputStreamParams params;
|
||||
|
||||
uint32_t streamCount = mStreams.Length();
|
||||
@@ -757,3 +783,65 @@ nsMultiplexInputStream::Deserialize(const InputStreamParams& aParams,
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMultiplexInputStream::GetCloneable(bool* aCloneable)
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
//XXXnsm Cloning a multiplex stream which has started reading is not permitted
|
||||
//right now.
|
||||
if (mCurrentStream > 0 || mStartedReadingCurrent) {
|
||||
*aCloneable = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
uint32_t len = mStreams.Length();
|
||||
for (uint32_t i = 0; i < len; ++i) {
|
||||
nsCOMPtr<nsICloneableInputStream> cis = do_QueryInterface(mStreams[i]);
|
||||
if (!cis || !cis->GetCloneable()) {
|
||||
*aCloneable = false;
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
*aCloneable = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMultiplexInputStream::Clone(nsIInputStream** aClone)
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
|
||||
//XXXnsm Cloning a multiplex stream which has started reading is not permitted
|
||||
//right now.
|
||||
if (mCurrentStream > 0 || mStartedReadingCurrent) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsRefPtr<nsMultiplexInputStream> clone = new nsMultiplexInputStream();
|
||||
|
||||
nsresult rv;
|
||||
uint32_t len = mStreams.Length();
|
||||
for (uint32_t i = 0; i < len; ++i) {
|
||||
nsCOMPtr<nsICloneableInputStream> substream = do_QueryInterface(mStreams[i]);
|
||||
if (NS_WARN_IF(!substream)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIInputStream> clonedSubstream;
|
||||
rv = substream->Clone(getter_AddRefs(clonedSubstream));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = clone->AppendStream(clonedSubstream);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
clone.forget(aClone);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -852,6 +852,10 @@ nsresult
|
||||
NS_CloneInputStream(nsIInputStream* aSource, nsIInputStream** aCloneOut,
|
||||
nsIInputStream** aReplacementOut)
|
||||
{
|
||||
if (NS_WARN_IF(!aSource)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Attempt to perform the clone directly on the source stream
|
||||
nsCOMPtr<nsICloneableInputStream> cloneable = do_QueryInterface(aSource);
|
||||
if (cloneable && cloneable->GetCloneable()) {
|
||||
|
||||
@@ -7,10 +7,19 @@
|
||||
#include "Helpers.h"
|
||||
#include "mozilla/unused.h"
|
||||
#include "nsICloneableInputStream.h"
|
||||
#include "nsIMultiplexInputStream.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsStreamUtils.h"
|
||||
#include "nsStringStream.h"
|
||||
|
||||
TEST(CloneInputStream, InvalidInput)
|
||||
{
|
||||
nsCOMPtr<nsIInputStream> clone;
|
||||
nsresult rv = NS_CloneInputStream(nullptr, getter_AddRefs(clone));
|
||||
ASSERT_TRUE(NS_FAILED(rv));
|
||||
ASSERT_FALSE(clone);
|
||||
}
|
||||
|
||||
TEST(CloneInputStream, CloneableInput)
|
||||
{
|
||||
nsTArray<char> inputData;
|
||||
@@ -102,3 +111,88 @@ TEST(CloneInputStream, NonCloneableInput_Fallback)
|
||||
testing::ConsumeAndValidateStream(stream, inputString);
|
||||
testing::ConsumeAndValidateStream(clone, inputString);
|
||||
}
|
||||
|
||||
TEST(CloneInputStream, CloneMultiplexStream)
|
||||
{
|
||||
nsCOMPtr<nsIMultiplexInputStream> stream =
|
||||
do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1");
|
||||
ASSERT_TRUE(stream);
|
||||
|
||||
nsTArray<char> inputData;
|
||||
testing::CreateData(1024, inputData);
|
||||
for (uint32_t i = 0; i < 2; ++i) {
|
||||
nsCString inputString(inputData.Elements(), inputData.Length());
|
||||
|
||||
nsCOMPtr<nsIInputStream> base;
|
||||
nsresult rv = NS_NewCStringInputStream(getter_AddRefs(base), inputString);
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
|
||||
rv = stream->AppendStream(base);
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
}
|
||||
|
||||
// Unread stream should clone successfully.
|
||||
nsTArray<char> doubled;
|
||||
doubled.AppendElements(inputData);
|
||||
doubled.AppendElements(inputData);
|
||||
|
||||
nsCOMPtr<nsIInputStream> clone;
|
||||
nsresult rv = NS_CloneInputStream(stream, getter_AddRefs(clone));
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
testing::ConsumeAndValidateStream(clone, doubled);
|
||||
|
||||
// Stream that has been read should fail.
|
||||
nsAutoPtr<char> buffer(new char[512]);
|
||||
uint32_t read;
|
||||
rv = stream->Read(buffer, 512, &read);
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
|
||||
nsCOMPtr<nsIInputStream> clone2;
|
||||
rv = NS_CloneInputStream(stream, getter_AddRefs(clone2));
|
||||
ASSERT_TRUE(NS_FAILED(rv));
|
||||
}
|
||||
|
||||
TEST(CloneInputStream, CloneMultiplexStreamPartial)
|
||||
{
|
||||
nsCOMPtr<nsIMultiplexInputStream> stream =
|
||||
do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1");
|
||||
ASSERT_TRUE(stream);
|
||||
|
||||
nsTArray<char> inputData;
|
||||
testing::CreateData(1024, inputData);
|
||||
for (uint32_t i = 0; i < 2; ++i) {
|
||||
nsCString inputString(inputData.Elements(), inputData.Length());
|
||||
|
||||
nsCOMPtr<nsIInputStream> base;
|
||||
nsresult rv = NS_NewCStringInputStream(getter_AddRefs(base), inputString);
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
|
||||
rv = stream->AppendStream(base);
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
}
|
||||
|
||||
// Fail when first stream read, but second hasn't been started.
|
||||
nsAutoPtr<char> buffer(new char[1024]);
|
||||
uint32_t read;
|
||||
nsresult rv = stream->Read(buffer, 1024, &read);
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
|
||||
nsCOMPtr<nsIInputStream> clone;
|
||||
rv = NS_CloneInputStream(stream, getter_AddRefs(clone));
|
||||
ASSERT_TRUE(NS_FAILED(rv));
|
||||
|
||||
// Fail after beginning read of second stream.
|
||||
rv = stream->Read(buffer, 512, &read);
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv) && read == 512);
|
||||
|
||||
rv = NS_CloneInputStream(stream, getter_AddRefs(clone));
|
||||
ASSERT_TRUE(NS_FAILED(rv));
|
||||
|
||||
// Fail at the end.
|
||||
nsAutoCString consumed;
|
||||
rv = NS_ConsumeStream(stream, UINT32_MAX, consumed);
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
|
||||
rv = NS_CloneInputStream(stream, getter_AddRefs(clone));
|
||||
ASSERT_TRUE(NS_FAILED(rv));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user