Bug 887029 - 'DeviceStorage: addNamed() crashes when invoked from an inline activity'. r=khuey.

This commit is contained in:
Ben Turner 2013-08-04 10:51:18 -07:00
parent b4bbf21bd8
commit 4c78215f62

View File

@ -14,6 +14,7 @@
#include "nsIDOMFile.h"
#include "nsIInputStream.h"
#include "nsIIPCSerializableInputStream.h"
#include "nsIMultiplexInputStream.h"
#include "nsIRemoteBlob.h"
#include "nsISeekableStream.h"
@ -21,6 +22,7 @@
#include "mozilla/unused.h"
#include "mozilla/ipc/InputStreamUtils.h"
#include "nsDOMFile.h"
#include "nsProxyRelease.h"
#include "nsThreadUtils.h"
#include "ContentChild.h"
@ -35,6 +37,24 @@ using namespace mozilla::ipc;
namespace {
/**
* Ensure that a nsCOMPtr/nsRefPtr is released on the main thread.
*/
template <template <class> class SmartPtr, class T>
void
ProxyReleaseToMainThread(SmartPtr<T>& aDoomed)
{
MOZ_ASSERT(!NS_IsMainThread());
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
NS_ENSURE_TRUE_VOID(mainThread);
if (NS_FAILED(NS_ProxyRelease(mainThread, aDoomed, true))) {
NS_WARNING("Failed to proxy release to main thread!");
}
}
class NS_NO_VTABLE IPrivateRemoteInputStream : public nsISupports
{
public:
@ -48,15 +68,89 @@ public:
NS_DEFINE_STATIC_IID_ACCESSOR(IPrivateRemoteInputStream,
PRIVATE_REMOTE_INPUT_STREAM_IID)
// This class exists to keep a blob alive at least as long as its internal
// stream.
class BlobInputStreamTether : public nsIMultiplexInputStream,
public nsISeekableStream,
public nsIIPCSerializableInputStream
{
nsCOMPtr<nsIInputStream> mStream;
nsCOMPtr<nsIDOMBlob> mSourceBlob;
nsIMultiplexInputStream* mWeakMultiplexStream;
nsISeekableStream* mWeakSeekableStream;
nsIIPCSerializableInputStream* mWeakSerializableStream;
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_FORWARD_NSIINPUTSTREAM(mStream->)
NS_FORWARD_SAFE_NSIMULTIPLEXINPUTSTREAM(mWeakMultiplexStream)
NS_FORWARD_SAFE_NSISEEKABLESTREAM(mWeakSeekableStream)
NS_FORWARD_SAFE_NSIIPCSERIALIZABLEINPUTSTREAM(mWeakSerializableStream)
BlobInputStreamTether(nsIInputStream* aStream, nsIDOMBlob* aSourceBlob)
: mStream(aStream), mSourceBlob(aSourceBlob), mWeakMultiplexStream(nullptr),
mWeakSeekableStream(nullptr), mWeakSerializableStream(nullptr)
{
MOZ_ASSERT(aStream);
MOZ_ASSERT(aSourceBlob);
nsCOMPtr<nsIMultiplexInputStream> multiplexStream =
do_QueryInterface(aStream);
if (multiplexStream) {
MOZ_ASSERT(SameCOMIdentity(aStream, multiplexStream));
mWeakMultiplexStream = multiplexStream;
}
nsCOMPtr<nsISeekableStream> seekableStream = do_QueryInterface(aStream);
if (seekableStream) {
MOZ_ASSERT(SameCOMIdentity(aStream, seekableStream));
mWeakSeekableStream = seekableStream;
}
nsCOMPtr<nsIIPCSerializableInputStream> serializableStream =
do_QueryInterface(aStream);
if (serializableStream) {
MOZ_ASSERT(SameCOMIdentity(aStream, serializableStream));
mWeakSerializableStream = serializableStream;
}
}
protected:
virtual ~BlobInputStreamTether()
{
MOZ_ASSERT(mStream);
MOZ_ASSERT(mSourceBlob);
if (!NS_IsMainThread()) {
mStream = nullptr;
ProxyReleaseToMainThread(mSourceBlob);
}
}
};
NS_IMPL_ADDREF(BlobInputStreamTether)
NS_IMPL_RELEASE(BlobInputStreamTether)
NS_INTERFACE_MAP_BEGIN(BlobInputStreamTether)
NS_INTERFACE_MAP_ENTRY(nsIInputStream)
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIMultiplexInputStream,
mWeakMultiplexStream)
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISeekableStream, mWeakSeekableStream)
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIPCSerializableInputStream,
mWeakSerializableStream)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
NS_INTERFACE_MAP_END
class RemoteInputStream : public nsIInputStream,
public nsISeekableStream,
public nsIIPCSerializableInputStream,
public IPrivateRemoteInputStream
{
mozilla::Monitor mMonitor;
nsCOMPtr<nsIDOMBlob> mSourceBlob;
nsCOMPtr<nsIInputStream> mStream;
nsCOMPtr<nsISeekableStream> mSeekableStream;
nsCOMPtr<nsIDOMBlob> mSourceBlob;
nsISeekableStream* mWeakSeekableStream;
ActorFlavorEnum mOrigin;
public:
@ -64,7 +158,7 @@ public:
RemoteInputStream(nsIDOMBlob* aSourceBlob, ActorFlavorEnum aOrigin)
: mMonitor("RemoteInputStream.mMonitor"), mSourceBlob(aSourceBlob),
mOrigin(aOrigin)
mWeakSeekableStream(nullptr), mOrigin(aOrigin)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aSourceBlob);
@ -102,14 +196,16 @@ public:
nsCOMPtr<nsIInputStream> stream = aStream;
nsCOMPtr<nsISeekableStream> seekableStream = do_QueryInterface(aStream);
MOZ_ASSERT_IF(seekableStream, SameCOMIdentity(aStream, seekableStream));
{
mozilla::MonitorAutoLock lock(mMonitor);
MOZ_ASSERT(!mStream);
MOZ_ASSERT(!mSeekableStream);
MOZ_ASSERT(!mWeakSeekableStream);
mStream.swap(stream);
mSeekableStream.swap(seekableStream);
mWeakSeekableStream = seekableStream;
mMonitor.Notify();
}
@ -187,12 +283,12 @@ public:
nsresult rv = BlockAndWaitForStream();
NS_ENSURE_SUCCESS(rv, rv);
if (!mSeekableStream) {
if (!mWeakSeekableStream) {
NS_WARNING("Underlying blob stream is not seekable!");
return NS_ERROR_NO_INTERFACE;
}
rv = mSeekableStream->Seek(aWhence, aOffset);
rv = mWeakSeekableStream->Seek(aWhence, aOffset);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
@ -212,12 +308,12 @@ public:
nsresult rv = BlockAndWaitForStream();
NS_ENSURE_SUCCESS(rv, rv);
if (!mSeekableStream) {
if (!mWeakSeekableStream) {
NS_WARNING("Underlying blob stream is not seekable!");
return NS_ERROR_NO_INTERFACE;
}
rv = mSeekableStream->Tell(aResult);
rv = mWeakSeekableStream->Tell(aResult);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
@ -229,12 +325,12 @@ public:
nsresult rv = BlockAndWaitForStream();
NS_ENSURE_SUCCESS(rv, rv);
if (!mSeekableStream) {
if (!mWeakSeekableStream) {
NS_WARNING("Underlying blob stream is not seekable!");
return NS_ERROR_NO_INTERFACE;
}
rv = mSeekableStream->SetEOF();
rv = mWeakSeekableStream->SetEOF();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
@ -253,7 +349,13 @@ public:
private:
virtual ~RemoteInputStream()
{ }
{
if (!NS_IsMainThread()) {
mStream = nullptr;
mWeakSeekableStream = nullptr;
ProxyReleaseToMainThread(mSourceBlob);
}
}
void
ReallyBlockAndWaitForStream()
@ -273,9 +375,9 @@ private:
MOZ_ASSERT(mStream);
#ifdef DEBUG
if (waited && mSeekableStream) {
if (waited && mWeakSeekableStream) {
int64_t position;
MOZ_ASSERT(NS_SUCCEEDED(mSeekableStream->Tell(&position)),
MOZ_ASSERT(NS_SUCCEEDED(mWeakSeekableStream->Tell(&position)),
"Failed to determine initial stream position!");
MOZ_ASSERT(!position, "Stream not starting at 0!");
}
@ -308,7 +410,7 @@ private:
ReallyBlockAndWaitForStream();
}
return !!mSeekableStream;
return !!mWeakSeekableStream;
}
};
@ -655,6 +757,24 @@ public:
private:
ActorType* mActor;
virtual ~RemoteBlob()
{
if (mActor) {
mActor->NoteDyingRemoteBlob();
}
}
nsresult
GetInternalStreamViaHelper(nsIInputStream** aStream)
{
if (!mActor) {
return NS_ERROR_UNEXPECTED;
}
nsRefPtr<StreamHelper> helper = new StreamHelper(mActor, this);
return helper->GetStream(aStream);
}
class StreamHelper : public nsRunnable
{
typedef Blob<ActorFlavor> ActorType;
@ -893,13 +1013,6 @@ public:
mImmutable = true;
}
virtual ~RemoteBlob()
{
if (mActor) {
mActor->NoteDyingRemoteBlob();
}
}
void
SetActor(ActorType* aActor)
{
@ -938,7 +1051,7 @@ public:
}
NS_IMETHOD
GetLastModifiedDate(JSContext* cx, JS::Value* aLastModifiedDate)
GetLastModifiedDate(JSContext* cx, JS::Value* aLastModifiedDate) MOZ_OVERRIDE
{
if (IsDateUnknown()) {
aLastModifiedDate->setNull();
@ -980,34 +1093,27 @@ NS_IMETHODIMP
RemoteBlob<Parent>::GetInternalStream(nsIInputStream** aStream)
{
if (mInputStreamParams.type() != InputStreamParams::T__None) {
nsCOMPtr<nsIInputStream> stream = DeserializeInputStream(mInputStreamParams);
if (!stream) {
nsCOMPtr<nsIInputStream> realStream =
DeserializeInputStream(mInputStreamParams);
if (!realStream) {
NS_WARNING("Failed to deserialize stream!");
return NS_ERROR_UNEXPECTED;
}
nsCOMPtr<nsIInputStream> stream =
new BlobInputStreamTether(realStream, this);
stream.forget(aStream);
return NS_OK;
}
if (!mActor) {
return NS_ERROR_UNEXPECTED;
}
nsRefPtr<StreamHelper> helper = new StreamHelper(mActor, this);
return helper->GetStream(aStream);
return GetInternalStreamViaHelper(aStream);
}
template <>
NS_IMETHODIMP
RemoteBlob<Child>::GetInternalStream(nsIInputStream** aStream)
{
if (!mActor) {
return NS_ERROR_UNEXPECTED;
}
nsRefPtr<StreamHelper> helper = new StreamHelper(mActor, this);
return helper->GetStream(aStream);
return GetInternalStreamViaHelper(aStream);
}
template <ActorFlavorEnum ActorFlavor>
@ -1226,6 +1332,7 @@ Blob<ActorFlavor>::NoteDyingRemoteBlob()
// Must do this before calling Send__delete__ or we'll crash there trying to
// access a dangling pointer.
mBlob = nullptr;
mRemoteBlob = nullptr;
mozilla::unused << ProtocolType::Send__delete__(this);
@ -1236,13 +1343,12 @@ void
Blob<ActorFlavor>::ActorDestroy(ActorDestroyReason aWhy)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mBlob);
if (mRemoteBlob) {
mRemoteBlob->SetActor(nullptr);
}
if (mOwnsBlob) {
if (mBlob && mOwnsBlob) {
mBlob->Release();
}
}