gecko/dom/ipc/Blob.cpp

2976 lines
72 KiB
C++

/* 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 "BlobChild.h"
#include "BlobParent.h"
#include "BackgroundParent.h"
#include "ContentChild.h"
#include "ContentParent.h"
#include "FileDescriptorSetChild.h"
#include "jsapi.h"
#include "mozilla/Assertions.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/Monitor.h"
#include "mozilla/unused.h"
#include "mozilla/dom/nsIContentParent.h"
#include "mozilla/dom/nsIContentChild.h"
#include "mozilla/dom/PBlobStreamChild.h"
#include "mozilla/dom/PBlobStreamParent.h"
#include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
#include "mozilla/ipc/InputStreamUtils.h"
#include "mozilla/ipc/PBackgroundChild.h"
#include "mozilla/ipc/PBackgroundParent.h"
#include "mozilla/ipc/PFileDescriptorSetParent.h"
#include "nsCOMPtr.h"
#include "nsDOMFile.h"
#include "nsIDOMFile.h"
#include "nsIInputStream.h"
#include "nsIIPCSerializableInputStream.h"
#include "nsIMultiplexInputStream.h"
#include "nsIRemoteBlob.h"
#include "nsISeekableStream.h"
#include "nsNetCID.h"
#include "nsThreadUtils.h"
#include "nsXULAppAPI.h"
#ifdef DEBUG
#include "BackgroundChild.h" // BackgroundChild::GetForCurrentThread().
#endif
#define DISABLE_ASSERTS_FOR_FUZZING 0
#if DISABLE_ASSERTS_FOR_FUZZING
#define ASSERT_UNLESS_FUZZING(...) do { } while (0)
#else
#define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false, __VA_ARGS__)
#endif
#define PRIVATE_REMOTE_INPUT_STREAM_IID \
{0x30c7699f, 0x51d2, 0x48c8, {0xad, 0x56, 0xc0, 0x16, 0xd7, 0x6f, 0x71, 0x27}}
using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::ipc;
namespace {
enum ActorType
{
ChildActor,
ParentActor
};
template <class ManagerType>
struct ConcreteManagerTypeTraits;
template <>
struct ConcreteManagerTypeTraits<nsIContentChild>
{
typedef ContentChild Type;
};
template <>
struct ConcreteManagerTypeTraits<PBackgroundChild>
{
typedef PBackgroundChild Type;
};
template <>
struct ConcreteManagerTypeTraits<nsIContentParent>
{
typedef ContentParent Type;
};
template <>
struct ConcreteManagerTypeTraits<PBackgroundParent>
{
typedef PBackgroundParent Type;
};
template <class ManagerType>
void AssertCorrectThreadForManager(ManagerType* aManager);
template <>
void AssertCorrectThreadForManager<nsIContentChild>(nsIContentChild* aManager)
{
MOZ_ASSERT(NS_IsMainThread());
}
template <>
void AssertCorrectThreadForManager<nsIContentParent>(nsIContentParent* aManager)
{
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
MOZ_ASSERT(NS_IsMainThread());
}
template <>
void AssertCorrectThreadForManager<PBackgroundChild>(PBackgroundChild* aManager)
{
#ifdef DEBUG
if (aManager) {
PBackgroundChild* backgroundChild = BackgroundChild::GetForCurrentThread();
MOZ_ASSERT(backgroundChild);
MOZ_ASSERT(backgroundChild == aManager);
}
#endif
}
template <>
void AssertCorrectThreadForManager<PBackgroundParent>(
PBackgroundParent* aManager)
{
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
AssertIsOnBackgroundThread();
}
bool
EventTargetIsOnCurrentThread(nsIEventTarget* aEventTarget)
{
if (!aEventTarget) {
return NS_IsMainThread();
}
bool current;
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aEventTarget->IsOnCurrentThread(&current)));
return current;
}
// Ensure that a nsCOMPtr/nsRefPtr is released on the target thread.
template <template <class> class SmartPtr, class T>
void
ReleaseOnTarget(SmartPtr<T>& aDoomed, nsIEventTarget* aTarget)
{
MOZ_ASSERT(aDoomed);
MOZ_ASSERT(!EventTargetIsOnCurrentThread(aTarget));
T* doomedRaw;
aDoomed.forget(&doomedRaw);
auto* doomedSupports = static_cast<nsISupports*>(doomedRaw);
nsCOMPtr<nsIRunnable> releaseRunnable =
NS_NewNonOwningRunnableMethod(doomedSupports, &nsISupports::Release);
MOZ_ASSERT(releaseRunnable);
if (aTarget) {
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aTarget->Dispatch(releaseRunnable,
NS_DISPATCH_NORMAL)));
} else {
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(releaseRunnable)));
}
}
template <class ManagerType>
PFileDescriptorSetParent*
ConstructFileDescriptorSet(ManagerType* aManager,
const nsTArray<FileDescriptor>& aFDs)
{
typedef typename ConcreteManagerTypeTraits<ManagerType>::Type
ConcreteManagerType;
MOZ_ASSERT(aManager);
if (aFDs.IsEmpty()) {
return nullptr;
}
auto* concreteManager = static_cast<ConcreteManagerType*>(aManager);
PFileDescriptorSetParent* fdSet =
concreteManager->SendPFileDescriptorSetConstructor(aFDs[0]);
if (!fdSet) {
return nullptr;
}
for (uint32_t index = 1; index < aFDs.Length(); index++) {
if (!fdSet->SendAddFileDescriptor(aFDs[index])) {
return nullptr;
}
}
return fdSet;
}
class NS_NO_VTABLE IPrivateRemoteInputStream : public nsISupports
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(PRIVATE_REMOTE_INPUT_STREAM_IID)
// This will return the underlying stream.
virtual nsIInputStream*
BlockAndGetInternalStream() = 0;
};
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;
nsRefPtr<DOMFileImplBase> mSourceBlob;
nsCOMPtr<nsIEventTarget> mEventTarget;
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,
mozilla::dom::DOMFileImplBase* aSourceBlob)
: mStream(aStream)
, mSourceBlob(aSourceBlob)
, mWeakMultiplexStream(nullptr)
, mWeakSeekableStream(nullptr)
, mWeakSerializableStream(nullptr)
{
MOZ_ASSERT(aStream);
MOZ_ASSERT(aSourceBlob);
if (!NS_IsMainThread()) {
mEventTarget = do_GetCurrentThread();
MOZ_ASSERT(mEventTarget);
}
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 (!EventTargetIsOnCurrentThread(mEventTarget)) {
mStream = nullptr;
ReleaseOnTarget(mSourceBlob, mEventTarget);
}
}
};
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
{
Monitor mMonitor;
nsCOMPtr<nsIInputStream> mStream;
nsRefPtr<DOMFileImplBase> mSourceBlob;
nsCOMPtr<nsIEventTarget> mEventTarget;
nsISeekableStream* mWeakSeekableStream;
ActorType mOrigin;
public:
NS_DECL_THREADSAFE_ISUPPORTS
RemoteInputStream(mozilla::dom::DOMFileImplBase* aSourceBlob,
ActorType aOrigin)
: mMonitor("RemoteInputStream.mMonitor")
, mSourceBlob(aSourceBlob)
, mWeakSeekableStream(nullptr)
, mOrigin(aOrigin)
{
MOZ_ASSERT(IsOnOwningThread());
MOZ_ASSERT(aSourceBlob);
if (!NS_IsMainThread()) {
mEventTarget = do_GetCurrentThread();
MOZ_ASSERT(mEventTarget);
}
}
bool
IsOnOwningThread() const
{
return EventTargetIsOnCurrentThread(mEventTarget);
}
void
AssertIsOnOwningThread() const
{
MOZ_ASSERT(IsOnOwningThread());
}
void
Serialize(InputStreamParams& aParams,
FileDescriptorArray& /* aFileDescriptors */)
{
nsCOMPtr<nsIRemoteBlob> remote = do_QueryInterface(mSourceBlob);
MOZ_ASSERT(remote);
if (mOrigin == ParentActor) {
MOZ_ASSERT(remote->GetBlobParent());
aParams = RemoteInputStreamParams(
remote->GetBlobParent() /* sourceParent */,
nullptr /* sourceChild */);
} else {
MOZ_ASSERT(mOrigin == ChildActor);
MOZ_ASSERT(remote->GetBlobChild());
aParams = RemoteInputStreamParams(
nullptr /* sourceParent */,
remote->GetBlobChild() /* sourceChild */);
}
}
bool
Deserialize(const InputStreamParams& aParams,
const FileDescriptorArray& /* aFileDescriptors */)
{
// See InputStreamUtils.cpp to see how deserialization of a
// RemoteInputStream is special-cased.
MOZ_CRASH("RemoteInputStream should never be deserialized");
}
void
SetStream(nsIInputStream* aStream)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aStream);
nsCOMPtr<nsIInputStream> stream = aStream;
nsCOMPtr<nsISeekableStream> seekableStream = do_QueryInterface(aStream);
MOZ_ASSERT_IF(seekableStream, SameCOMIdentity(aStream, seekableStream));
{
MonitorAutoLock lock(mMonitor);
MOZ_ASSERT(!mStream);
MOZ_ASSERT(!mWeakSeekableStream);
mStream.swap(stream);
mWeakSeekableStream = seekableStream;
mMonitor.Notify();
}
}
NS_IMETHOD
Close() MOZ_OVERRIDE
{
nsresult rv = BlockAndWaitForStream();
NS_ENSURE_SUCCESS(rv, rv);
nsRefPtr<mozilla::dom::DOMFileImplBase> sourceBlob;
mSourceBlob.swap(sourceBlob);
rv = mStream->Close();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
NS_IMETHOD
Available(uint64_t* aAvailable) MOZ_OVERRIDE
{
// See large comment in FileInputStreamWrapper::Available.
if (IsOnOwningThread()) {
return NS_BASE_STREAM_CLOSED;
}
nsresult rv = BlockAndWaitForStream();
NS_ENSURE_SUCCESS(rv, rv);
rv = mStream->Available(aAvailable);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
NS_IMETHOD
Read(char* aBuffer, uint32_t aCount, uint32_t* aResult) MOZ_OVERRIDE
{
nsresult rv = BlockAndWaitForStream();
NS_ENSURE_SUCCESS(rv, rv);
rv = mStream->Read(aBuffer, aCount, aResult);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
NS_IMETHOD
ReadSegments(nsWriteSegmentFun aWriter, void* aClosure, uint32_t aCount,
uint32_t* aResult) MOZ_OVERRIDE
{
nsresult rv = BlockAndWaitForStream();
NS_ENSURE_SUCCESS(rv, rv);
rv = mStream->ReadSegments(aWriter, aClosure, aCount, aResult);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
NS_IMETHOD
IsNonBlocking(bool* aNonBlocking) MOZ_OVERRIDE
{
NS_ENSURE_ARG_POINTER(aNonBlocking);
*aNonBlocking = false;
return NS_OK;
}
NS_IMETHOD
Seek(int32_t aWhence, int64_t aOffset) MOZ_OVERRIDE
{
nsresult rv = BlockAndWaitForStream();
NS_ENSURE_SUCCESS(rv, rv);
if (!mWeakSeekableStream) {
NS_WARNING("Underlying blob stream is not seekable!");
return NS_ERROR_NO_INTERFACE;
}
rv = mWeakSeekableStream->Seek(aWhence, aOffset);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
NS_IMETHOD
Tell(int64_t* aResult) MOZ_OVERRIDE
{
// We can cheat here and assume that we're going to start at 0 if we don't
// yet have our stream. Though, really, this should abort since most input
// streams could block here.
if (IsOnOwningThread() && !mStream) {
*aResult = 0;
return NS_OK;
}
nsresult rv = BlockAndWaitForStream();
NS_ENSURE_SUCCESS(rv, rv);
if (!mWeakSeekableStream) {
NS_WARNING("Underlying blob stream is not seekable!");
return NS_ERROR_NO_INTERFACE;
}
rv = mWeakSeekableStream->Tell(aResult);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
NS_IMETHOD
SetEOF() MOZ_OVERRIDE
{
nsresult rv = BlockAndWaitForStream();
NS_ENSURE_SUCCESS(rv, rv);
if (!mWeakSeekableStream) {
NS_WARNING("Underlying blob stream is not seekable!");
return NS_ERROR_NO_INTERFACE;
}
rv = mWeakSeekableStream->SetEOF();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
virtual nsIInputStream*
BlockAndGetInternalStream()
{
AssertIsOnOwningThread();
nsresult rv = BlockAndWaitForStream();
NS_ENSURE_SUCCESS(rv, nullptr);
return mStream;
}
private:
virtual ~RemoteInputStream()
{
if (!IsOnOwningThread()) {
mStream = nullptr;
mWeakSeekableStream = nullptr;
if (mSourceBlob) {
ReleaseOnTarget(mSourceBlob, mEventTarget);
}
}
}
void
ReallyBlockAndWaitForStream()
{
MOZ_ASSERT(!IsOnOwningThread());
DebugOnly<bool> waited;
{
MonitorAutoLock lock(mMonitor);
waited = !mStream;
while (!mStream) {
mMonitor.Wait();
}
}
MOZ_ASSERT(mStream);
#ifdef DEBUG
if (waited && mWeakSeekableStream) {
int64_t position;
MOZ_ASSERT(NS_SUCCEEDED(mWeakSeekableStream->Tell(&position)),
"Failed to determine initial stream position!");
MOZ_ASSERT(!position, "Stream not starting at 0!");
}
#endif
}
nsresult
BlockAndWaitForStream()
{
if (IsOnOwningThread()) {
NS_WARNING("Blocking the owning thread is not supported!");
return NS_ERROR_FAILURE;
}
ReallyBlockAndWaitForStream();
return NS_OK;
}
bool
IsSeekableStream()
{
if (IsOnOwningThread()) {
if (!mStream) {
NS_WARNING("Don't know if this stream is seekable yet!");
return true;
}
} else {
ReallyBlockAndWaitForStream();
}
return !!mWeakSeekableStream;
}
};
NS_IMPL_ADDREF(RemoteInputStream)
NS_IMPL_RELEASE(RemoteInputStream)
NS_INTERFACE_MAP_BEGIN(RemoteInputStream)
NS_INTERFACE_MAP_ENTRY(nsIInputStream)
NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableInputStream)
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISeekableStream, IsSeekableStream())
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
NS_INTERFACE_MAP_ENTRY(IPrivateRemoteInputStream)
NS_INTERFACE_MAP_END
class InputStreamChild MOZ_FINAL
: public PBlobStreamChild
{
nsRefPtr<RemoteInputStream> mRemoteStream;
public:
explicit InputStreamChild(RemoteInputStream* aRemoteStream)
: mRemoteStream(aRemoteStream)
{
MOZ_ASSERT(aRemoteStream);
aRemoteStream->AssertIsOnOwningThread();
}
InputStreamChild()
{ }
private:
// This method is only called by the IPDL message machinery.
virtual bool
Recv__delete__(const InputStreamParams& aParams,
const OptionalFileDescriptorSet& aFDs) MOZ_OVERRIDE;
};
class InputStreamParent MOZ_FINAL
: public PBlobStreamParent
{
nsRefPtr<RemoteInputStream> mRemoteStream;
public:
explicit InputStreamParent(RemoteInputStream* aRemoteStream)
: mRemoteStream(aRemoteStream)
{
MOZ_ASSERT(aRemoteStream);
aRemoteStream->AssertIsOnOwningThread();
}
InputStreamParent()
{ }
protected:
virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
private:
// This method is only called by the IPDL message machinery.
virtual bool
Recv__delete__(const InputStreamParams& aParams,
const OptionalFileDescriptorSet& aFDs) MOZ_OVERRIDE;
};
} // anonymous namespace
// Each instance of this class will be dispatched to the network stream thread
// pool to run the first time where it will open the file input stream. It will
// then dispatch itself back to the owning thread to send the child process its
// response (assuming that the child has not crashed). The runnable will then
// dispatch itself to the thread pool again in order to close the file input
// stream.
class BlobParent::OpenStreamRunnable MOZ_FINAL
: public nsRunnable
{
friend class nsRevocableEventPtr<OpenStreamRunnable>;
// Only safe to access these pointers if mRevoked is false!
BlobParent* mBlobActor;
PBlobStreamParent* mStreamActor;
nsCOMPtr<nsIInputStream> mStream;
nsCOMPtr<nsIIPCSerializableInputStream> mSerializable;
nsCOMPtr<nsIEventTarget> mActorTarget;
nsCOMPtr<nsIThread> mIOTarget;
bool mRevoked;
bool mClosing;
public:
OpenStreamRunnable(BlobParent* aBlobActor,
PBlobStreamParent* aStreamActor,
nsIInputStream* aStream,
nsIIPCSerializableInputStream* aSerializable,
nsIThread* aIOTarget)
: mBlobActor(aBlobActor)
, mStreamActor(aStreamActor)
, mStream(aStream)
, mSerializable(aSerializable)
, mIOTarget(aIOTarget)
, mRevoked(false)
, mClosing(false)
{
MOZ_ASSERT(aBlobActor);
aBlobActor->AssertIsOnOwningThread();
MOZ_ASSERT(aStreamActor);
MOZ_ASSERT(aStream);
// aSerializable may be null.
MOZ_ASSERT(aIOTarget);
if (!NS_IsMainThread()) {
AssertIsOnBackgroundThread();
mActorTarget = do_GetCurrentThread();
MOZ_ASSERT(mActorTarget);
}
AssertIsOnOwningThread();
}
nsresult
Dispatch()
{
AssertIsOnOwningThread();
MOZ_ASSERT(mIOTarget);
nsresult rv = mIOTarget->Dispatch(this, NS_DISPATCH_NORMAL);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
NS_DECL_ISUPPORTS_INHERITED
private:
~OpenStreamRunnable()
{ }
bool
IsOnOwningThread() const
{
return EventTargetIsOnCurrentThread(mActorTarget);
}
void
AssertIsOnOwningThread() const
{
MOZ_ASSERT(IsOnOwningThread());
}
void
Revoke()
{
AssertIsOnOwningThread();
#ifdef DEBUG
mBlobActor = nullptr;
mStreamActor = nullptr;
#endif
mRevoked = true;
}
nsresult
OpenStream()
{
MOZ_ASSERT(!IsOnOwningThread());
MOZ_ASSERT(mStream);
if (!mSerializable) {
nsCOMPtr<IPrivateRemoteInputStream> remoteStream =
do_QueryInterface(mStream);
MOZ_ASSERT(remoteStream, "Must QI to IPrivateRemoteInputStream here!");
nsCOMPtr<nsIInputStream> realStream =
remoteStream->BlockAndGetInternalStream();
NS_ENSURE_TRUE(realStream, NS_ERROR_FAILURE);
mSerializable = do_QueryInterface(realStream);
if (!mSerializable) {
MOZ_ASSERT(false, "Must be serializable!");
return NS_ERROR_FAILURE;
}
mStream.swap(realStream);
}
// To force the stream open we call Available(). We don't actually care
// how much data is available.
uint64_t available;
if (NS_FAILED(mStream->Available(&available))) {
NS_WARNING("Available failed on this stream!");
}
if (mActorTarget) {
nsresult rv = mActorTarget->Dispatch(this, NS_DISPATCH_NORMAL);
NS_ENSURE_SUCCESS(rv, rv);
} else {
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(this)));
}
return NS_OK;
}
nsresult
CloseStream()
{
MOZ_ASSERT(!IsOnOwningThread());
MOZ_ASSERT(mStream);
// Going to always release here.
nsCOMPtr<nsIInputStream> stream;
mStream.swap(stream);
nsCOMPtr<nsIThread> ioTarget;
mIOTarget.swap(ioTarget);
NS_WARN_IF_FALSE(NS_SUCCEEDED(stream->Close()), "Failed to close stream!");
nsCOMPtr<nsIRunnable> shutdownRunnable =
NS_NewRunnableMethod(ioTarget, &nsIThread::Shutdown);
MOZ_ASSERT(shutdownRunnable);
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(shutdownRunnable)));
return NS_OK;
}
nsresult
SendResponse()
{
AssertIsOnOwningThread();
MOZ_ASSERT(mStream);
MOZ_ASSERT(mSerializable);
MOZ_ASSERT(mIOTarget);
MOZ_ASSERT(!mClosing);
nsCOMPtr<nsIIPCSerializableInputStream> serializable;
mSerializable.swap(serializable);
if (mRevoked) {
MOZ_ASSERT(!mBlobActor);
MOZ_ASSERT(!mStreamActor);
}
else {
MOZ_ASSERT(mBlobActor);
MOZ_ASSERT(mBlobActor->HasManager());
MOZ_ASSERT(mStreamActor);
InputStreamParams params;
nsAutoTArray<FileDescriptor, 10> fds;
serializable->Serialize(params, fds);
MOZ_ASSERT(params.type() != InputStreamParams::T__None);
PFileDescriptorSetParent* fdSet;
if (nsIContentParent* contentManager = mBlobActor->GetContentManager()) {
fdSet = ConstructFileDescriptorSet(contentManager, fds);
} else {
fdSet = ConstructFileDescriptorSet(mBlobActor->GetBackgroundManager(),
fds);
}
OptionalFileDescriptorSet optionalFDs;
if (fdSet) {
optionalFDs = fdSet;
} else {
optionalFDs = void_t();
}
unused <<
PBlobStreamParent::Send__delete__(mStreamActor, params, optionalFDs);
mBlobActor->NoteRunnableCompleted(this);
#ifdef DEBUG
mBlobActor = nullptr;
mStreamActor = nullptr;
#endif
}
// If our luck is *really* bad then it is possible for the CloseStream() and
// nsIThread::Shutdown() functions to run before the Dispatch() call here
// finishes... Keep the thread alive until this method returns.
nsCOMPtr<nsIThread> kungFuDeathGrip = mIOTarget;
mClosing = true;
nsresult rv = mIOTarget->Dispatch(this, NS_DISPATCH_NORMAL);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
NS_IMETHOD
Run() MOZ_OVERRIDE
{
MOZ_ASSERT(mIOTarget);
if (IsOnOwningThread()) {
return SendResponse();
}
if (!mClosing) {
return OpenStream();
}
return CloseStream();
}
};
NS_IMPL_ISUPPORTS_INHERITED0(BlobParent::OpenStreamRunnable, nsRunnable)
/*******************************************************************************
* BlobChild::RemoteBlob Declaration
******************************************************************************/
class BlobChild::RemoteBlob MOZ_FINAL : public DOMFileImplBase
, public nsIRemoteBlob
{
class StreamHelper;
class SliceHelper;
BlobChild* mActor;
nsCOMPtr<nsIEventTarget> mActorTarget;
public:
RemoteBlob(BlobChild* aActor,
const nsAString& aName,
const nsAString& aContentType,
uint64_t aLength,
uint64_t aModDate)
: DOMFileImplBase(aName, aContentType, aLength, aModDate)
{
CommonInit(aActor);
}
RemoteBlob(BlobChild* aActor, const nsAString& aContentType, uint64_t aLength)
: DOMFileImplBase(aContentType, aLength)
{
CommonInit(aActor);
}
explicit RemoteBlob(BlobChild* aActor)
: DOMFileImplBase(EmptyString(), EmptyString(), UINT64_MAX, UINT64_MAX)
{
CommonInit(aActor);
}
void
NoteDyingActor()
{
MOZ_ASSERT(mActor);
mActor->AssertIsOnOwningThread();
mActor = nullptr;
}
NS_DECL_ISUPPORTS_INHERITED
virtual nsresult
GetMozFullPathInternal(nsAString &aFilePath) MOZ_OVERRIDE;
virtual already_AddRefed<DOMFileImpl>
CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType)
MOZ_OVERRIDE;
virtual nsresult
GetInternalStream(nsIInputStream** aStream) MOZ_OVERRIDE;
virtual int64_t
GetFileId() MOZ_OVERRIDE;
virtual nsresult
GetLastModifiedDate(JSContext* cx,
JS::MutableHandle<JS::Value> aLastModifiedDate)
MOZ_OVERRIDE;
virtual BlobChild*
GetBlobChild() MOZ_OVERRIDE;
virtual BlobParent*
GetBlobParent() MOZ_OVERRIDE;
private:
~RemoteBlob()
{
MOZ_ASSERT_IF(mActorTarget,
EventTargetIsOnCurrentThread(mActorTarget));
}
void
CommonInit(BlobChild* aActor)
{
MOZ_ASSERT(aActor);
aActor->AssertIsOnOwningThread();
mActor = aActor;
mActorTarget = aActor->EventTarget();
mImmutable = true;
}
void
Destroy()
{
if (EventTargetIsOnCurrentThread(mActorTarget)) {
if (mActor) {
mActor->AssertIsOnOwningThread();
mActor->NoteDyingRemoteBlob();
}
delete this;
return;
}
nsCOMPtr<nsIRunnable> destroyRunnable =
NS_NewNonOwningRunnableMethod(this, &RemoteBlob::Destroy);
if (mActorTarget) {
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mActorTarget->Dispatch(destroyRunnable,
NS_DISPATCH_NORMAL)));
} else {
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(destroyRunnable)));
}
}
};
class BlobChild::RemoteBlob::StreamHelper MOZ_FINAL
: public nsRunnable
{
Monitor mMonitor;
BlobChild* mActor;
nsRefPtr<mozilla::dom::DOMFileImplBase> mSourceBlob;
nsRefPtr<RemoteInputStream> mInputStream;
bool mDone;
public:
StreamHelper(BlobChild* aActor, mozilla::dom::DOMFileImplBase* aSourceBlob)
: mMonitor("BlobChild::RemoteBlob::StreamHelper::mMonitor")
, mActor(aActor)
, mSourceBlob(aSourceBlob)
, mDone(false)
{
// This may be created on any thread.
MOZ_ASSERT(aActor);
MOZ_ASSERT(aSourceBlob);
}
nsresult
GetStream(nsIInputStream** aInputStream)
{
// This may be called on any thread.
MOZ_ASSERT(aInputStream);
MOZ_ASSERT(mActor);
MOZ_ASSERT(!mInputStream);
MOZ_ASSERT(!mDone);
if (mActor->IsOnOwningThread()) {
RunInternal(false);
} else {
nsCOMPtr<nsIEventTarget> target = mActor->EventTarget();
if (!target) {
target = do_GetMainThread();
}
MOZ_ASSERT(target);
nsresult rv = target->Dispatch(this, NS_DISPATCH_NORMAL);
NS_ENSURE_SUCCESS(rv, rv);
{
MonitorAutoLock lock(mMonitor);
while (!mDone) {
lock.Wait();
}
}
}
MOZ_ASSERT(!mActor);
MOZ_ASSERT(mDone);
if (!mInputStream) {
return NS_ERROR_UNEXPECTED;
}
mInputStream.forget(aInputStream);
return NS_OK;
}
NS_IMETHOD
Run() MOZ_OVERRIDE
{
MOZ_ASSERT(mActor);
mActor->AssertIsOnOwningThread();
RunInternal(true);
return NS_OK;
}
private:
void
RunInternal(bool aNotify)
{
MOZ_ASSERT(mActor);
mActor->AssertIsOnOwningThread();
MOZ_ASSERT(!mInputStream);
MOZ_ASSERT(!mDone);
nsRefPtr<RemoteInputStream> stream =
new RemoteInputStream(mSourceBlob, ChildActor);
InputStreamChild* streamActor = new InputStreamChild(stream);
if (mActor->SendPBlobStreamConstructor(streamActor)) {
stream.swap(mInputStream);
}
mActor = nullptr;
if (aNotify) {
MonitorAutoLock lock(mMonitor);
mDone = true;
lock.Notify();
}
else {
mDone = true;
}
}
};
class BlobChild::RemoteBlob::SliceHelper MOZ_FINAL
: public nsRunnable
{
Monitor mMonitor;
BlobChild* mActor;
nsRefPtr<DOMFileImpl> mSlice;
uint64_t mStart;
uint64_t mLength;
nsString mContentType;
bool mDone;
public:
explicit SliceHelper(BlobChild* aActor)
: mMonitor("BlobChild::RemoteBlob::SliceHelper::mMonitor")
, mActor(aActor)
, mStart(0)
, mLength(0)
, mDone(false)
{
// This may be created on any thread.
MOZ_ASSERT(aActor);
}
DOMFileImpl*
GetSlice(uint64_t aStart,
uint64_t aLength,
const nsAString& aContentType)
{
// This may be called on any thread.
MOZ_ASSERT(mActor);
MOZ_ASSERT(!mSlice);
MOZ_ASSERT(!mDone);
mStart = aStart;
mLength = aLength;
mContentType = aContentType;
if (mActor->IsOnOwningThread()) {
RunInternal(false);
} else {
nsCOMPtr<nsIEventTarget> target = mActor->EventTarget();
if (!target) {
target = do_GetMainThread();
}
MOZ_ASSERT(target);
nsresult rv = target->Dispatch(this, NS_DISPATCH_NORMAL);
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
{
MonitorAutoLock lock(mMonitor);
while (!mDone) {
lock.Wait();
}
}
}
MOZ_ASSERT(!mActor);
MOZ_ASSERT(mDone);
if (NS_WARN_IF(!mSlice)) {
return nullptr;
}
return mSlice;
}
NS_IMETHOD
Run() MOZ_OVERRIDE
{
MOZ_ASSERT(mActor);
mActor->AssertIsOnOwningThread();
RunInternal(true);
return NS_OK;
}
private:
void
RunInternal(bool aNotify)
{
MOZ_ASSERT(mActor);
mActor->AssertIsOnOwningThread();
MOZ_ASSERT(!mSlice);
MOZ_ASSERT(!mDone);
NS_ENSURE_TRUE_VOID(mActor->HasManager());
NormalBlobConstructorParams params(mContentType /* contentType */,
mLength /* length */);
ParentBlobConstructorParams otherSideParams(
SlicedBlobConstructorParams(nullptr /* sourceParent */,
mActor /* sourceChild */,
mStart /* begin */,
mStart + mLength /* end */,
mContentType /* contentType */),
void_t() /* optionalInputStream */);
BlobChild* newActor;
if (nsIContentChild* contentManager = mActor->GetContentManager()) {
newActor = SendSliceConstructor(contentManager, params, otherSideParams);
} else {
newActor = SendSliceConstructor(mActor->GetBackgroundManager(),
params,
otherSideParams);
}
if (newActor) {
nsCOMPtr<nsIDOMBlob> blob = newActor->GetBlob();
mSlice = static_cast<DOMFile*>(blob.get())->Impl();
}
mActor = nullptr;
if (aNotify) {
MonitorAutoLock lock(mMonitor);
mDone = true;
lock.Notify();
}
else {
mDone = true;
}
}
};
/*******************************************************************************
* BlobChild::RemoteBlob Implementation
******************************************************************************/
NS_IMPL_ADDREF(BlobChild::RemoteBlob)
NS_IMPL_RELEASE_WITH_DESTROY(BlobChild::RemoteBlob, Destroy())
NS_IMPL_QUERY_INTERFACE_INHERITED(BlobChild::RemoteBlob,
DOMFileImpl,
nsIRemoteBlob)
nsresult
BlobChild::
RemoteBlob::GetMozFullPathInternal(nsAString &aFilePath)
{
if (!mActor) {
return NS_ERROR_UNEXPECTED;
}
nsString filePath;
if (!mActor->SendGetFilePath(&filePath)) {
return NS_ERROR_FAILURE;
}
aFilePath = filePath;
return NS_OK;
}
already_AddRefed<DOMFileImpl>
BlobChild::
RemoteBlob::CreateSlice(uint64_t aStart,
uint64_t aLength,
const nsAString& aContentType)
{
if (!mActor) {
return nullptr;
}
nsRefPtr<SliceHelper> helper = new SliceHelper(mActor);
nsRefPtr<DOMFileImpl> impl = helper->GetSlice(aStart, aLength, aContentType);
if (NS_WARN_IF(!impl)) {
return nullptr;
}
return impl.forget();
}
nsresult
BlobChild::
RemoteBlob::GetInternalStream(nsIInputStream** aStream)
{
if (!mActor) {
return NS_ERROR_UNEXPECTED;
}
nsRefPtr<StreamHelper> helper = new StreamHelper(mActor, this);
return helper->GetStream(aStream);
}
int64_t
BlobChild::
RemoteBlob::GetFileId()
{
int64_t fileId;
if (mActor && mActor->SendGetFileId(&fileId)) {
return fileId;
}
return -1;
}
nsresult
BlobChild::
RemoteBlob::GetLastModifiedDate(JSContext* cx,
JS::MutableHandle<JS::Value> aLastModifiedDate)
{
if (IsDateUnknown()) {
aLastModifiedDate.setNull();
} else {
JSObject* date = JS_NewDateObjectMsec(cx, mLastModificationDate);
if (!date) {
return NS_ERROR_OUT_OF_MEMORY;
}
aLastModifiedDate.setObject(*date);
}
return NS_OK;
}
BlobChild*
BlobChild::
RemoteBlob::GetBlobChild()
{
return mActor;
}
BlobParent*
BlobChild::
RemoteBlob::GetBlobParent()
{
return nullptr;
}
/*******************************************************************************
* BlobChild
******************************************************************************/
BlobChild::BlobChild(nsIContentChild* aManager, nsIDOMBlob* aBlob)
: mBlob(aBlob)
, mRemoteBlob(nullptr)
, mBackgroundManager(nullptr)
, mContentManager(aManager)
, mOwnsBlob(true)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aManager);
MOZ_ASSERT(aBlob);
MOZ_COUNT_CTOR(BlobChild);
CommonInit(aBlob);
}
BlobChild::BlobChild(PBackgroundChild* aManager, nsIDOMBlob* aBlob)
: mBlob(aBlob)
, mRemoteBlob(nullptr)
, mBackgroundManager(aManager)
, mContentManager(nullptr)
, mOwnsBlob(true)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aManager);
MOZ_ASSERT(aBlob);
MOZ_COUNT_CTOR(BlobChild);
if (!NS_IsMainThread()) {
mEventTarget = do_GetCurrentThread();
MOZ_ASSERT(mEventTarget);
}
CommonInit(aBlob);
}
BlobChild::BlobChild(nsIContentChild* aManager,
const ChildBlobConstructorParams& aParams)
: mBlob(nullptr)
, mRemoteBlob(nullptr)
, mBackgroundManager(nullptr)
, mContentManager(aManager)
, mOwnsBlob(false)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aManager);
MOZ_ASSERT(aParams.type() != ChildBlobConstructorParams::T__None);
MOZ_COUNT_CTOR(BlobChild);
CommonInit(aParams);
}
BlobChild::BlobChild(PBackgroundChild* aManager,
const ChildBlobConstructorParams& aParams)
: mBlob(nullptr)
, mRemoteBlob(nullptr)
, mBackgroundManager(aManager)
, mContentManager(nullptr)
, mOwnsBlob(false)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aManager);
MOZ_ASSERT(aParams.type() != ChildBlobConstructorParams::T__None);
MOZ_COUNT_CTOR(BlobChild);
if (!NS_IsMainThread()) {
mEventTarget = do_GetCurrentThread();
MOZ_ASSERT(mEventTarget);
}
CommonInit(aParams);
}
BlobChild::~BlobChild()
{
AssertIsOnOwningThread();
MOZ_COUNT_DTOR(BlobChild);
}
void
BlobChild::CommonInit(nsIDOMBlob* aBlob)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aBlob);
MOZ_ASSERT(!mRemoteBlob);
aBlob->AddRef();
}
void
BlobChild::CommonInit(const ChildBlobConstructorParams& aParams)
{
AssertIsOnOwningThread();
MOZ_ASSERT(!mRemoteBlob);
ChildBlobConstructorParams::Type paramsType = aParams.type();
MOZ_ASSERT(paramsType != ChildBlobConstructorParams::T__None);
nsRefPtr<RemoteBlob> remoteBlob;
switch (paramsType) {
case ChildBlobConstructorParams::TNormalBlobConstructorParams: {
const NormalBlobConstructorParams& params =
aParams.get_NormalBlobConstructorParams();
remoteBlob = new RemoteBlob(this, params.contentType(), params.length());
break;
}
case ChildBlobConstructorParams::TFileBlobConstructorParams: {
const FileBlobConstructorParams& params =
aParams.get_FileBlobConstructorParams();
remoteBlob = new RemoteBlob(this,
params.name(),
params.contentType(),
params.length(),
params.modDate());
break;
}
case ChildBlobConstructorParams::TMysteryBlobConstructorParams: {
remoteBlob = new RemoteBlob(this);
break;
}
default:
MOZ_CRASH("Unknown params!");
}
MOZ_ASSERT(remoteBlob);
DebugOnly<bool> isMutable;
MOZ_ASSERT(NS_SUCCEEDED(remoteBlob->GetMutable(&isMutable)));
MOZ_ASSERT(!isMutable);
nsRefPtr<DOMFile> blob = new DOMFile(remoteBlob);
blob.forget(&mBlob);
mRemoteBlob = remoteBlob;
mOwnsBlob = true;
}
#ifdef DEBUG
void
BlobChild::AssertIsOnOwningThread() const
{
MOZ_ASSERT(IsOnOwningThread());
}
#endif // DEBUG
// static
BlobChild*
BlobChild::Create(nsIContentChild* aManager,
const ChildBlobConstructorParams& aParams)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aManager);
return CreateFromParams(aManager, aParams);
}
// static
BlobChild*
BlobChild::Create(PBackgroundChild* aManager,
const ChildBlobConstructorParams& aParams)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aManager);
return CreateFromParams(aManager, aParams);
}
// static
template <class ChildManagerType>
BlobChild*
BlobChild::CreateFromParams(ChildManagerType* aManager,
const ChildBlobConstructorParams& aParams)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aManager);
switch (aParams.type()) {
case ChildBlobConstructorParams::TNormalBlobConstructorParams:
case ChildBlobConstructorParams::TFileBlobConstructorParams:
case ChildBlobConstructorParams::TMysteryBlobConstructorParams:
return new BlobChild(aManager, aParams);
case ChildBlobConstructorParams::TSlicedBlobConstructorParams: {
const SlicedBlobConstructorParams& params =
aParams.get_SlicedBlobConstructorParams();
auto* actor =
const_cast<BlobChild*>(
static_cast<const BlobChild*>(params.sourceChild()));
MOZ_ASSERT(actor);
nsCOMPtr<nsIDOMBlob> source = actor->GetBlob();
MOZ_ASSERT(source);
nsCOMPtr<nsIDOMBlob> slice;
nsresult rv =
source->Slice(params.begin(), params.end(), params.contentType(), 3,
getter_AddRefs(slice));
NS_ENSURE_SUCCESS(rv, nullptr);
return new BlobChild(aManager, slice);
}
default:
MOZ_CRASH("Unknown params!");
}
return nullptr;
}
// static
template <class ChildManagerType>
BlobChild*
BlobChild::SendSliceConstructor(
ChildManagerType* aManager,
const NormalBlobConstructorParams& aParams,
const ParentBlobConstructorParams& aOtherSideParams)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aManager);
BlobChild* newActor = BlobChild::Create(aManager, aParams);
MOZ_ASSERT(newActor);
if (aManager->SendPBlobConstructor(newActor, aOtherSideParams)) {
return newActor;
}
return nullptr;
}
already_AddRefed<nsIDOMBlob>
BlobChild::GetBlob()
{
AssertIsOnOwningThread();
MOZ_ASSERT(mBlob);
nsCOMPtr<nsIDOMBlob> blob;
// Remote blobs are held alive until the first call to GetBlob. Thereafter we
// only hold a weak reference. Normal blobs are held alive until the actor is
// destroyed.
if (mRemoteBlob && mOwnsBlob) {
blob = dont_AddRef(mBlob);
mOwnsBlob = false;
}
else {
blob = mBlob;
}
MOZ_ASSERT(blob);
return blob.forget();
}
already_AddRefed<DOMFileImpl>
BlobChild::GetBlobImpl()
{
AssertIsOnOwningThread();
nsCOMPtr<nsIDOMBlob> blob = GetBlob();
MOZ_ASSERT(blob);
nsRefPtr<DOMFileImpl> impl = static_cast<DOMFile*>(blob.get())->Impl();
MOZ_ASSERT(impl);
return impl.forget();
}
bool
BlobChild::SetMysteryBlobInfo(const nsString& aName,
const nsString& aContentType,
uint64_t aLength,
uint64_t aLastModifiedDate)
{
AssertIsOnOwningThread();
MOZ_ASSERT(mBlob);
MOZ_ASSERT(mRemoteBlob);
MOZ_ASSERT(aLastModifiedDate != UINT64_MAX);
static_cast<DOMFile*>(mBlob)->SetLazyData(aName, aContentType, aLength,
aLastModifiedDate);
FileBlobConstructorParams params(aName, aContentType, aLength,
aLastModifiedDate);
return SendResolveMystery(params);
}
bool
BlobChild::SetMysteryBlobInfo(const nsString& aContentType, uint64_t aLength)
{
AssertIsOnOwningThread();
MOZ_ASSERT(mBlob);
MOZ_ASSERT(mRemoteBlob);
nsString voidString;
voidString.SetIsVoid(true);
static_cast<DOMFile*>(mBlob)->SetLazyData(voidString, aContentType, aLength,
UINT64_MAX);
NormalBlobConstructorParams params(aContentType, aLength);
return SendResolveMystery(params);
}
void
BlobChild::NoteDyingRemoteBlob()
{
MOZ_ASSERT(mBlob);
MOZ_ASSERT(mRemoteBlob);
MOZ_ASSERT(!mOwnsBlob);
// This may be called on any thread due to the fact that RemoteBlob is
// designed to be passed between threads. We must start the shutdown process
// on the owning thread, so we proxy here if necessary.
if (!IsOnOwningThread()) {
nsCOMPtr<nsIRunnable> runnable =
NS_NewNonOwningRunnableMethod(this, &BlobChild::NoteDyingRemoteBlob);
if (mEventTarget) {
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mEventTarget->Dispatch(runnable,
NS_DISPATCH_NORMAL)));
} else {
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable)));
}
return;
}
// Must do this before calling Send__delete__ or we'll crash there trying to
// access a dangling pointer.
mBlob = nullptr;
mRemoteBlob = nullptr;
PBlobChild::Send__delete__(this);
}
bool
BlobChild::IsOnOwningThread() const
{
return EventTargetIsOnCurrentThread(mEventTarget);
}
void
BlobChild::ActorDestroy(ActorDestroyReason aWhy)
{
AssertIsOnOwningThread();
if (mRemoteBlob) {
mRemoteBlob->NoteDyingActor();
}
if (mBlob && mOwnsBlob) {
mBlob->Release();
}
mBackgroundManager = nullptr;
mContentManager = nullptr;
}
PBlobStreamChild*
BlobChild::AllocPBlobStreamChild()
{
AssertIsOnOwningThread();
return new InputStreamChild();
}
bool
BlobChild::RecvPBlobStreamConstructor(PBlobStreamChild* aActor)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aActor);
MOZ_ASSERT(mBlob);
MOZ_ASSERT(!mRemoteBlob);
nsCOMPtr<nsIInputStream> stream;
nsresult rv = mBlob->GetInternalStream(getter_AddRefs(stream));
NS_ENSURE_SUCCESS(rv, false);
nsCOMPtr<nsIIPCSerializableInputStream> serializable =
do_QueryInterface(stream);
if (!serializable) {
MOZ_ASSERT(false, "Must be serializable!");
return false;
}
InputStreamParams params;
nsTArray<FileDescriptor> fds;
serializable->Serialize(params, fds);
MOZ_ASSERT(params.type() != InputStreamParams::T__None);
MOZ_ASSERT(fds.IsEmpty());
return aActor->Send__delete__(aActor, params, void_t());
}
bool
BlobChild::DeallocPBlobStreamChild(PBlobStreamChild* aActor)
{
AssertIsOnOwningThread();
delete static_cast<InputStreamChild*>(aActor);
return true;
}
/*******************************************************************************
* BlobParent::RemoteBlob Declaration
******************************************************************************/
class BlobParent::RemoteBlob MOZ_FINAL : public mozilla::dom::DOMFileImplBase
, public nsIRemoteBlob
{
class StreamHelper;
class SliceHelper;
BlobParent* mActor;
nsCOMPtr<nsIEventTarget> mActorTarget;
InputStreamParams mInputStreamParams;
public:
RemoteBlob(BlobParent* aActor,
const OptionalInputStreamParams& aOptionalInputStreamParams,
const nsAString& aName,
const nsAString& aContentType,
uint64_t aLength,
uint64_t aModDate)
: DOMFileImplBase(aName, aContentType, aLength, aModDate)
{
CommonInit(aActor, aOptionalInputStreamParams);
}
RemoteBlob(BlobParent* aActor,
const OptionalInputStreamParams& aOptionalInputStreamParams,
const nsAString& aContentType,
uint64_t aLength)
: DOMFileImplBase(aContentType, aLength)
{
CommonInit(aActor, aOptionalInputStreamParams);
}
RemoteBlob(BlobParent* aActor,
const OptionalInputStreamParams& aOptionalInputStreamParams)
: DOMFileImplBase(EmptyString(), EmptyString(), UINT64_MAX, UINT64_MAX)
{
CommonInit(aActor, aOptionalInputStreamParams);
}
void
NoteDyingActor()
{
MOZ_ASSERT(mActor);
mActor->AssertIsOnOwningThread();
mActor = nullptr;
}
NS_DECL_ISUPPORTS_INHERITED
virtual already_AddRefed<DOMFileImpl>
CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType)
MOZ_OVERRIDE;
virtual nsresult
GetInternalStream(nsIInputStream** aStream) MOZ_OVERRIDE;
virtual nsresult
GetLastModifiedDate(JSContext* cx,
JS::MutableHandle<JS::Value> aLastModifiedDate)
MOZ_OVERRIDE;
virtual BlobChild*
GetBlobChild() MOZ_OVERRIDE;
virtual BlobParent*
GetBlobParent() MOZ_OVERRIDE;
private:
~RemoteBlob()
{
MOZ_ASSERT_IF(mActorTarget,
EventTargetIsOnCurrentThread(mActorTarget));
}
void
CommonInit(BlobParent* aActor,
const OptionalInputStreamParams& aOptionalInputStreamParams)
{
MOZ_ASSERT(aActor);
aActor->AssertIsOnOwningThread();
MOZ_ASSERT(aOptionalInputStreamParams.type() !=
OptionalInputStreamParams::T__None);
mActor = aActor;
mActorTarget = aActor->EventTarget();
if (aOptionalInputStreamParams.type() ==
OptionalInputStreamParams::TInputStreamParams) {
mInputStreamParams = aOptionalInputStreamParams.get_InputStreamParams();
}
mImmutable = true;
}
void
Destroy()
{
if (EventTargetIsOnCurrentThread(mActorTarget)) {
if (mActor) {
mActor->AssertIsOnOwningThread();
mActor->NoteDyingRemoteBlob();
}
delete this;
return;
}
nsCOMPtr<nsIRunnable> destroyRunnable =
NS_NewNonOwningRunnableMethod(this, &RemoteBlob::Destroy);
if (mActorTarget) {
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mActorTarget->Dispatch(destroyRunnable,
NS_DISPATCH_NORMAL)));
} else {
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(destroyRunnable)));
}
}
};
class BlobParent::RemoteBlob::StreamHelper MOZ_FINAL
: public nsRunnable
{
Monitor mMonitor;
BlobParent* mActor;
nsRefPtr<mozilla::dom::DOMFileImplBase> mSourceBlob;
nsRefPtr<RemoteInputStream> mInputStream;
bool mDone;
public:
StreamHelper(BlobParent* aActor, mozilla::dom::DOMFileImplBase* aSourceBlob)
: mMonitor("BlobParent::RemoteBlob::StreamHelper::mMonitor")
, mActor(aActor)
, mSourceBlob(aSourceBlob)
, mDone(false)
{
// This may be created on any thread.
MOZ_ASSERT(aActor);
MOZ_ASSERT(aSourceBlob);
}
nsresult
GetStream(nsIInputStream** aInputStream)
{
// This may be called on any thread.
MOZ_ASSERT(aInputStream);
MOZ_ASSERT(mActor);
MOZ_ASSERT(!mInputStream);
MOZ_ASSERT(!mDone);
if (mActor->IsOnOwningThread()) {
RunInternal(false);
} else {
nsCOMPtr<nsIEventTarget> target = mActor->EventTarget();
if (!target) {
target = do_GetMainThread();
}
MOZ_ASSERT(target);
nsresult rv = target->Dispatch(this, NS_DISPATCH_NORMAL);
NS_ENSURE_SUCCESS(rv, rv);
{
MonitorAutoLock lock(mMonitor);
while (!mDone) {
lock.Wait();
}
}
}
MOZ_ASSERT(!mActor);
MOZ_ASSERT(mDone);
if (!mInputStream) {
return NS_ERROR_UNEXPECTED;
}
mInputStream.forget(aInputStream);
return NS_OK;
}
NS_IMETHOD
Run() MOZ_OVERRIDE
{
MOZ_ASSERT(mActor);
mActor->AssertIsOnOwningThread();
RunInternal(true);
return NS_OK;
}
private:
void
RunInternal(bool aNotify)
{
MOZ_ASSERT(mActor);
mActor->AssertIsOnOwningThread();
MOZ_ASSERT(!mInputStream);
MOZ_ASSERT(!mDone);
nsRefPtr<RemoteInputStream> stream =
new RemoteInputStream(mSourceBlob, ParentActor);
InputStreamParent* streamActor = new InputStreamParent(stream);
if (mActor->SendPBlobStreamConstructor(streamActor)) {
stream.swap(mInputStream);
}
mActor = nullptr;
if (aNotify) {
MonitorAutoLock lock(mMonitor);
mDone = true;
lock.Notify();
}
else {
mDone = true;
}
}
};
class BlobParent::RemoteBlob::SliceHelper MOZ_FINAL
: public nsRunnable
{
Monitor mMonitor;
BlobParent* mActor;
nsRefPtr<DOMFileImpl> mSlice;
uint64_t mStart;
uint64_t mLength;
nsString mContentType;
bool mDone;
public:
explicit SliceHelper(BlobParent* aActor)
: mMonitor("BlobParent::RemoteBlob::SliceHelper::mMonitor")
, mActor(aActor)
, mStart(0)
, mLength(0)
, mDone(false)
{
// This may be created on any thread.
MOZ_ASSERT(aActor);
}
DOMFileImpl*
GetSlice(uint64_t aStart,
uint64_t aLength,
const nsAString& aContentType)
{
// This may be called on any thread.
MOZ_ASSERT(mActor);
MOZ_ASSERT(!mSlice);
MOZ_ASSERT(!mDone);
mStart = aStart;
mLength = aLength;
mContentType = aContentType;
if (mActor->IsOnOwningThread()) {
RunInternal(false);
} else {
nsCOMPtr<nsIEventTarget> target = mActor->EventTarget();
if (!target) {
target = do_GetMainThread();
}
MOZ_ASSERT(target);
nsresult rv = target->Dispatch(this, NS_DISPATCH_NORMAL);
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
{
MonitorAutoLock lock(mMonitor);
while (!mDone) {
lock.Wait();
}
}
}
MOZ_ASSERT(!mActor);
MOZ_ASSERT(mDone);
if (NS_WARN_IF(!mSlice)) {
return nullptr;
}
return mSlice;
}
NS_IMETHOD
Run() MOZ_OVERRIDE
{
MOZ_ASSERT(mActor);
mActor->AssertIsOnOwningThread();
RunInternal(true);
return NS_OK;
}
private:
void
RunInternal(bool aNotify)
{
MOZ_ASSERT(mActor);
mActor->AssertIsOnOwningThread();
MOZ_ASSERT(!mSlice);
MOZ_ASSERT(!mDone);
NS_ENSURE_TRUE_VOID(mActor->HasManager());
ParentBlobConstructorParams params(
NormalBlobConstructorParams(mContentType /* contentType */,
mLength /* length */),
void_t() /* optionalInputStreamParams */);
ChildBlobConstructorParams otherSideParams(
SlicedBlobConstructorParams(mActor /* sourceParent*/,
nullptr /* sourceChild */,
mStart /* begin */,
mStart + mLength /* end */,
mContentType /* contentType */));
BlobParent* newActor;
if (nsIContentParent* contentManager = mActor->GetContentManager()) {
newActor = SendSliceConstructor(contentManager, params, otherSideParams);
} else {
newActor = SendSliceConstructor(mActor->GetBackgroundManager(),
params,
otherSideParams);
}
if (newActor) {
nsCOMPtr<nsIDOMBlob> blob =newActor->GetBlob();
mSlice = static_cast<DOMFile*>(blob.get())->Impl();
}
mActor = nullptr;
if (aNotify) {
MonitorAutoLock lock(mMonitor);
mDone = true;
lock.Notify();
}
else {
mDone = true;
}
}
};
/*******************************************************************************
* BlobChild::RemoteBlob Implementation
******************************************************************************/
NS_IMPL_ADDREF(BlobParent::RemoteBlob)
NS_IMPL_RELEASE_WITH_DESTROY(BlobParent::RemoteBlob, Destroy())
NS_IMPL_QUERY_INTERFACE_INHERITED(BlobParent::RemoteBlob,
DOMFileImpl,
nsIRemoteBlob)
already_AddRefed<DOMFileImpl>
BlobParent::
RemoteBlob::CreateSlice(uint64_t aStart,
uint64_t aLength,
const nsAString& aContentType)
{
if (!mActor) {
return nullptr;
}
nsRefPtr<SliceHelper> helper = new SliceHelper(mActor);
nsRefPtr<DOMFileImpl> impl = helper->GetSlice(aStart, aLength, aContentType);
if (NS_WARN_IF(!impl)) {
return nullptr;
}
return impl.forget();
}
nsresult
BlobParent::
RemoteBlob::GetInternalStream(nsIInputStream** aStream)
{
if (mInputStreamParams.type() != InputStreamParams::T__None) {
nsTArray<FileDescriptor> fds;
nsCOMPtr<nsIInputStream> realStream =
DeserializeInputStream(mInputStreamParams, fds);
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);
}
nsresult
BlobParent::
RemoteBlob::GetLastModifiedDate(JSContext* cx,
JS::MutableHandle<JS::Value> aLastModifiedDate)
{
if (IsDateUnknown()) {
aLastModifiedDate.setNull();
} else {
JSObject* date = JS_NewDateObjectMsec(cx, mLastModificationDate);
if (!date) {
return NS_ERROR_OUT_OF_MEMORY;
}
aLastModifiedDate.setObject(*date);
}
return NS_OK;
}
BlobChild*
BlobParent::
RemoteBlob::GetBlobChild()
{
return nullptr;
}
BlobParent*
BlobParent::
RemoteBlob::GetBlobParent()
{
return mActor;
}
/*******************************************************************************
* BlobParent
******************************************************************************/
BlobParent::BlobParent(nsIContentParent* aManager, nsIDOMBlob* aBlob)
: mBlob(aBlob)
, mRemoteBlob(nullptr)
, mBackgroundManager(nullptr)
, mContentManager(aManager)
, mOwnsBlob(true)
, mOwnsRemoteBlob(false)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aManager);
MOZ_ASSERT(aBlob);
MOZ_COUNT_CTOR(BlobParent);
CommonInit(aBlob);
}
BlobParent::BlobParent(PBackgroundParent* aManager, nsIDOMBlob* aBlob)
: mBlob(aBlob)
, mRemoteBlob(nullptr)
, mBackgroundManager(aManager)
, mContentManager(nullptr)
, mEventTarget(do_GetCurrentThread())
, mOwnsBlob(true)
, mOwnsRemoteBlob(false)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aManager);
MOZ_ASSERT(aBlob);
MOZ_ASSERT(mEventTarget);
MOZ_COUNT_CTOR(BlobParent);
CommonInit(aBlob);
}
BlobParent::BlobParent(PBackgroundParent* aManager, DOMFileImpl* aBlobImpl)
: mBlob(nullptr)
, mRemoteBlob(nullptr)
, mBackgroundManager(aManager)
, mContentManager(nullptr)
, mBlobImpl(aBlobImpl)
, mEventTarget(do_GetCurrentThread())
, mOwnsBlob(false)
, mOwnsRemoteBlob(false)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aManager);
MOZ_ASSERT(aBlobImpl);
MOZ_ASSERT(mEventTarget);
MOZ_COUNT_CTOR(BlobParent);
}
BlobParent::BlobParent(nsIContentParent* aManager,
const ParentBlobConstructorParams& aParams)
: mBlob(nullptr)
, mRemoteBlob(nullptr)
, mBackgroundManager(nullptr)
, mContentManager(aManager)
, mOwnsBlob(false)
, mOwnsRemoteBlob(false)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aManager);
MOZ_ASSERT(aParams.blobParams().type() !=
ChildBlobConstructorParams::T__None);
MOZ_COUNT_CTOR(BlobParent);
CommonInit(aParams);
}
BlobParent::BlobParent(PBackgroundParent* aManager,
const ParentBlobConstructorParams& aParams)
: mBlob(nullptr)
, mRemoteBlob(nullptr)
, mBackgroundManager(aManager)
, mContentManager(nullptr)
, mEventTarget(do_GetCurrentThread())
, mOwnsBlob(false)
, mOwnsRemoteBlob(false)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aManager);
MOZ_ASSERT(aParams.blobParams().type() !=
ChildBlobConstructorParams::T__None);
MOZ_ASSERT(mEventTarget);
MOZ_COUNT_CTOR(BlobParent);
CommonInit(aParams);
}
BlobParent::~BlobParent()
{
AssertIsOnOwningThread();
MOZ_COUNT_DTOR(BlobParent);
}
void
BlobParent::CommonInit(nsIDOMBlob* aBlob)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aBlob);
aBlob->AddRef();
}
void
BlobParent::CommonInit(const ParentBlobConstructorParams& aParams)
{
AssertIsOnOwningThread();
const ChildBlobConstructorParams& blobParams = aParams.blobParams();
ChildBlobConstructorParams::Type paramsType = blobParams.type();
MOZ_ASSERT(paramsType != ChildBlobConstructorParams::T__None);
nsRefPtr<RemoteBlob> remoteBlob;
switch (paramsType) {
case ChildBlobConstructorParams::TNormalBlobConstructorParams: {
const NormalBlobConstructorParams& params =
blobParams.get_NormalBlobConstructorParams();
remoteBlob = new RemoteBlob(this,
aParams.optionalInputStreamParams(),
params.contentType(),
params.length());
break;
}
case ChildBlobConstructorParams::TFileBlobConstructorParams: {
const FileBlobConstructorParams& params =
blobParams.get_FileBlobConstructorParams();
remoteBlob = new RemoteBlob(this,
aParams.optionalInputStreamParams(),
params.name(),
params.contentType(),
params.length(),
params.modDate());
break;
}
case ChildBlobConstructorParams::TMysteryBlobConstructorParams: {
remoteBlob = new RemoteBlob(this, aParams.optionalInputStreamParams());
break;
}
default:
MOZ_CRASH("Unknown params!");
}
MOZ_ASSERT(remoteBlob);
DebugOnly<bool> isMutable;
MOZ_ASSERT(NS_SUCCEEDED(remoteBlob->GetMutable(&isMutable)));
MOZ_ASSERT(!isMutable);
remoteBlob.forget(&mRemoteBlob);
mOwnsRemoteBlob = true;
}
#ifdef DEBUG
void
BlobParent::AssertIsOnOwningThread() const
{
MOZ_ASSERT(IsOnOwningThread());
}
#endif // DEBUG
// static
BlobParent*
BlobParent::Create(nsIContentParent* aManager,
const ParentBlobConstructorParams& aParams)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aManager);
return CreateFromParams(aManager, aParams);
}
// static
BlobParent*
BlobParent::Create(PBackgroundParent* aManager,
const ParentBlobConstructorParams& aParams)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aManager);
return CreateFromParams(aManager, aParams);
}
// static
BlobParent*
BlobParent::CreateFromParams(nsIContentParent* aManager,
const ParentBlobConstructorParams& aParams)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aManager);
const ChildBlobConstructorParams& blobParams = aParams.blobParams();
MOZ_ASSERT(blobParams.type() !=
ChildBlobConstructorParams::TMysteryBlobConstructorParams);
switch (blobParams.type()) {
case ChildBlobConstructorParams::TMysteryBlobConstructorParams:
ASSERT_UNLESS_FUZZING();
return nullptr;
case ChildBlobConstructorParams::TNormalBlobConstructorParams:
case ChildBlobConstructorParams::TFileBlobConstructorParams:
return new BlobParent(aManager, aParams);
case ChildBlobConstructorParams::TSlicedBlobConstructorParams: {
const SlicedBlobConstructorParams& params =
blobParams.get_SlicedBlobConstructorParams();
auto* actor =
const_cast<BlobParent*>(
static_cast<const BlobParent*>(params.sourceParent()));
MOZ_ASSERT(actor);
nsRefPtr<DOMFileImpl> source = actor->GetBlobImpl();
MOZ_ASSERT(source);
nsRefPtr<DOMFileImpl> slicedImpl;
nsresult rv =
source->Slice(params.begin(), params.end(), params.contentType(), 3,
getter_AddRefs(slicedImpl));
NS_ENSURE_SUCCESS(rv, nullptr);
nsRefPtr<DOMFile> slice = new DOMFile(slicedImpl);
return new BlobParent(aManager, slice);
}
default:
MOZ_CRASH("Unknown params!");
}
MOZ_CRASH("Should never get here!");
}
BlobParent*
BlobParent::CreateFromParams(PBackgroundParent* aManager,
const ParentBlobConstructorParams& aParams)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aManager);
const ChildBlobConstructorParams& blobParams = aParams.blobParams();
MOZ_ASSERT(blobParams.type() !=
ChildBlobConstructorParams::TMysteryBlobConstructorParams);
switch (blobParams.type()) {
case ChildBlobConstructorParams::TMysteryBlobConstructorParams:
ASSERT_UNLESS_FUZZING();
return nullptr;
case ChildBlobConstructorParams::TNormalBlobConstructorParams:
case ChildBlobConstructorParams::TFileBlobConstructorParams:
return new BlobParent(aManager, aParams);
case ChildBlobConstructorParams::TSlicedBlobConstructorParams: {
const SlicedBlobConstructorParams& params =
blobParams.get_SlicedBlobConstructorParams();
auto* actor =
const_cast<BlobParent*>(
static_cast<const BlobParent*>(params.sourceParent()));
MOZ_ASSERT(actor);
nsRefPtr<DOMFileImpl> source = actor->GetBlobImpl();
MOZ_ASSERT(source);
nsRefPtr<DOMFileImpl> slice;
nsresult rv =
source->Slice(params.begin(), params.end(), params.contentType(), 3,
getter_AddRefs(slice));
NS_ENSURE_SUCCESS(rv, nullptr);
return new BlobParent(aManager, slice);
}
default:
MOZ_CRASH("Unknown params!");
}
MOZ_CRASH("Should never get here!");
}
// static
template <class ParentManagerType>
BlobParent*
BlobParent::SendSliceConstructor(
ParentManagerType* aManager,
const ParentBlobConstructorParams& aParams,
const ChildBlobConstructorParams& aOtherSideParams)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aManager);
BlobParent* newActor = BlobParent::Create(aManager, aParams);
MOZ_ASSERT(newActor);
if (aManager->SendPBlobConstructor(newActor, aOtherSideParams)) {
return newActor;
}
return nullptr;
}
already_AddRefed<nsIDOMBlob>
BlobParent::GetBlob()
{
AssertIsOnOwningThread();
MOZ_ASSERT(mContentManager,
"Use GetBlobImpl() instead for background-managed blobs");
nsCOMPtr<nsIDOMBlob> blob = mBlob;
if (blob) {
return blob.forget();
}
MOZ_ASSERT(mRemoteBlob);
MOZ_ASSERT(!mOwnsBlob);
MOZ_ASSERT(mOwnsRemoteBlob);
nsRefPtr<DOMFileImpl> impl = GetBlobImpl();
MOZ_ASSERT(impl);
MOZ_ASSERT(impl == mRemoteBlob);
MOZ_ASSERT(!mOwnsRemoteBlob);
blob = new DOMFile(impl);
mBlob = blob;
return blob.forget();
}
already_AddRefed<DOMFileImpl>
BlobParent::GetBlobImpl()
{
AssertIsOnOwningThread();
nsRefPtr<DOMFileImpl> impl;
if (mBlobImpl) {
// If we own a DOMFileImpl then just hand it out.
impl = mBlobImpl;
} else if (mBlob) {
// If we already have a blob (either a normal blob that we always own or a
// remote blob that we created) then just return its impl.
nsCOMPtr<nsIDOMBlob> blob = mBlob;
impl = static_cast<DOMFile*>(blob.get())->Impl();
// Make sure the impl hasn't changed somehow if this is a remote blob that
// we created.
MOZ_ASSERT_IF(mRemoteBlob, impl == mRemoteBlob);
} else if (mOwnsRemoteBlob) {
// If this is a remote blob and this is the first time a caller is asking
// for the impl then we drop our strong reference and only hold a weak
// reference hereafter.
impl = dont_AddRef(mRemoteBlob);
mOwnsRemoteBlob = false;
} else {
// Hand out our weak reference.
impl = mRemoteBlob;
}
MOZ_ASSERT(impl);
return impl.forget();
}
void
BlobParent::NoteDyingRemoteBlob()
{
MOZ_ASSERT(mRemoteBlob);
MOZ_ASSERT(!mOwnsBlob);
MOZ_ASSERT(!mOwnsRemoteBlob);
// This may be called on any thread due to the fact that RemoteBlob is
// designed to be passed between threads. We must start the shutdown process
// on the main thread, so we proxy here if necessary.
if (!IsOnOwningThread()) {
nsCOMPtr<nsIRunnable> runnable =
NS_NewNonOwningRunnableMethod(this, &BlobParent::NoteDyingRemoteBlob);
if (mEventTarget) {
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mEventTarget->Dispatch(runnable,
NS_DISPATCH_NORMAL)));
} else {
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable)));
}
return;
}
// Must do this before calling Send__delete__ or we'll crash there trying to
// access a dangling pointer.
mBlob = nullptr;
mRemoteBlob = nullptr;
mBlobImpl = nullptr;
unused << PBlobParent::Send__delete__(this);
}
void
BlobParent::NoteRunnableCompleted(OpenStreamRunnable* aRunnable)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aRunnable);
for (uint32_t index = 0; index < mOpenStreamRunnables.Length(); index++) {
nsRevocableEventPtr<OpenStreamRunnable>& runnable =
mOpenStreamRunnables[index];
if (runnable.get() == aRunnable) {
runnable.Forget();
mOpenStreamRunnables.RemoveElementAt(index);
return;
}
}
MOZ_CRASH("Runnable not in our array!");
}
bool
BlobParent::IsOnOwningThread() const
{
return EventTargetIsOnCurrentThread(mEventTarget);
}
void
BlobParent::ActorDestroy(ActorDestroyReason aWhy)
{
AssertIsOnOwningThread();
if (mRemoteBlob) {
mRemoteBlob->NoteDyingActor();
}
if (mBlob && mOwnsBlob) {
mBlob->Release();
}
if (mRemoteBlob && mOwnsRemoteBlob) {
mRemoteBlob->Release();
}
mBlobImpl = nullptr;
mBackgroundManager = nullptr;
mContentManager = nullptr;
}
PBlobStreamParent*
BlobParent::AllocPBlobStreamParent()
{
AssertIsOnOwningThread();
return new InputStreamParent();
}
bool
BlobParent::RecvPBlobStreamConstructor(PBlobStreamParent* aActor)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aActor);
MOZ_ASSERT_IF(mBlob, !mBlobImpl);
MOZ_ASSERT_IF(!mBlob, mBlobImpl);
MOZ_ASSERT(!mRemoteBlob);
nsRefPtr<DOMFile> blob = static_cast<DOMFile*>(mBlob);
nsRefPtr<DOMFileImpl> impl;
if (blob) {
impl = blob->Impl();
} else {
impl = mBlobImpl;
}
MOZ_ASSERT(impl);
nsCOMPtr<nsIInputStream> stream;
nsresult rv = impl->GetInternalStream(getter_AddRefs(stream));
NS_ENSURE_SUCCESS(rv, false);
// If the stream is entirely backed by memory then we can serialize and send
// it immediately.
if (impl->IsMemoryFile()) {
InputStreamParams params;
nsTArray<FileDescriptor> fds;
SerializeInputStream(stream, params, fds);
MOZ_ASSERT(params.type() != InputStreamParams::T__None);
MOZ_ASSERT(fds.IsEmpty());
return PBlobStreamParent::Send__delete__(aActor, params, void_t());
}
nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryInterface(impl);
nsCOMPtr<IPrivateRemoteInputStream> remoteStream;
if (remoteBlob) {
remoteStream = do_QueryInterface(stream);
}
// There are three cases in which we can use the stream obtained from the blob
// directly as our serialized stream:
//
// 1. The blob is not a remote blob.
// 2. The blob is a remote blob that represents this actor.
// 3. The blob is a remote blob representing a different actor but we
// already have a non-remote, i.e. serialized, serialized stream.
//
// In all other cases we need to be on a background thread before we can get
// to the real stream.
nsCOMPtr<nsIIPCSerializableInputStream> serializableStream;
if (!remoteBlob ||
remoteBlob->GetBlobParent() == this ||
!remoteStream) {
serializableStream = do_QueryInterface(stream);
if (!serializableStream) {
MOZ_ASSERT(false, "Must be serializable!");
return false;
}
}
nsCOMPtr<nsIThread> target;
rv = NS_NewNamedThread("Blob Opener", getter_AddRefs(target));
if (NS_WARN_IF(NS_FAILED(rv))) {
return false;
}
nsRefPtr<OpenStreamRunnable> runnable =
new OpenStreamRunnable(this, aActor, stream, serializableStream, target);
rv = runnable->Dispatch();
NS_ENSURE_SUCCESS(rv, false);
// nsRevocableEventPtr lacks some of the operators needed for anything nicer.
*mOpenStreamRunnables.AppendElement() = runnable;
return true;
}
bool
BlobParent::DeallocPBlobStreamParent(PBlobStreamParent* aActor)
{
AssertIsOnOwningThread();
delete static_cast<InputStreamParent*>(aActor);
return true;
}
bool
BlobParent::RecvResolveMystery(const ResolveMysteryParams& aParams)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aParams.type() != ResolveMysteryParams::T__None);
MOZ_ASSERT_IF(mBlob, !mBlobImpl);
MOZ_ASSERT_IF(mBlob, mOwnsBlob);
MOZ_ASSERT_IF(mBlobImpl, !mBlob);
MOZ_ASSERT_IF(mBlobImpl, mBackgroundManager);
MOZ_ASSERT(!mRemoteBlob);
MOZ_ASSERT(!mOwnsRemoteBlob);
nsRefPtr<DOMFileImpl> blobImpl;
if (mBlob) {
blobImpl = static_cast<DOMFile*>(mBlob)->Impl();
} else {
blobImpl = mBlobImpl;
}
MOZ_ASSERT(blobImpl);
switch (aParams.type()) {
case ResolveMysteryParams::TNormalBlobConstructorParams: {
const NormalBlobConstructorParams& params =
aParams.get_NormalBlobConstructorParams();
if (NS_WARN_IF(params.length() == UINT64_MAX)) {
ASSERT_UNLESS_FUZZING();
return false;
}
nsString voidString;
voidString.SetIsVoid(true);
blobImpl->SetLazyData(voidString,
params.contentType(),
params.length(),
UINT64_MAX);
return true;
}
case ResolveMysteryParams::TFileBlobConstructorParams: {
const FileBlobConstructorParams& params =
aParams.get_FileBlobConstructorParams();
if (NS_WARN_IF(params.name().IsVoid())) {
ASSERT_UNLESS_FUZZING();
return false;
}
if (NS_WARN_IF(params.length() == UINT64_MAX)) {
ASSERT_UNLESS_FUZZING();
return false;
}
if (NS_WARN_IF(params.modDate() == UINT64_MAX)) {
ASSERT_UNLESS_FUZZING();
return false;
}
blobImpl->SetLazyData(params.name(),
params.contentType(),
params.length(),
params.modDate());
return true;
}
default:
MOZ_CRASH("Unknown params!");
}
MOZ_CRASH("Should never get here!");
}
bool
BlobParent::RecvGetFileId(int64_t* aFileId)
{
using namespace mozilla::dom::indexedDB;
AssertIsOnOwningThread();
MOZ_ASSERT_IF(mBlob, !mBlobImpl);
MOZ_ASSERT_IF(!mBlob, mBlobImpl);
MOZ_ASSERT(!mRemoteBlob);
if (NS_WARN_IF(!IndexedDatabaseManager::InTestingMode())) {
ASSERT_UNLESS_FUZZING();
return false;
}
nsRefPtr<DOMFileImpl> blobImpl;
if (mBlob) {
blobImpl = static_cast<DOMFile*>(mBlob)->Impl();
} else {
blobImpl = mBlobImpl;
}
MOZ_ASSERT(blobImpl);
*aFileId = blobImpl->GetFileId();
return true;
}
bool
BlobParent::RecvGetFilePath(nsString* aFilePath)
{
using namespace mozilla::dom::indexedDB;
AssertIsOnOwningThread();
MOZ_ASSERT_IF(mBlob, !mBlobImpl);
MOZ_ASSERT_IF(!mBlob, mBlobImpl);
MOZ_ASSERT(!mRemoteBlob);
// In desktop e10s the file picker code sends this message.
#ifdef MOZ_CHILD_PERMISSIONS
if (NS_WARN_IF(!IndexedDatabaseManager::InTestingMode())) {
ASSERT_UNLESS_FUZZING();
return false;
}
#endif
nsRefPtr<DOMFileImpl> blobImpl;
if (mBlob) {
blobImpl = static_cast<DOMFile*>(mBlob)->Impl();
} else {
blobImpl = mBlobImpl;
}
MOZ_ASSERT(blobImpl);
nsString filePath;
nsresult rv = blobImpl->GetMozFullPathInternal(filePath);
if (NS_WARN_IF(NS_FAILED(rv))) {
return false;
}
*aFilePath = filePath;
return true;
}
bool
InputStreamChild::Recv__delete__(const InputStreamParams& aParams,
const OptionalFileDescriptorSet& aFDs)
{
MOZ_ASSERT(mRemoteStream);
mRemoteStream->AssertIsOnOwningThread();
nsTArray<FileDescriptor> fds;
if (aFDs.type() == OptionalFileDescriptorSet::TPFileDescriptorSetChild) {
FileDescriptorSetChild* fdSetActor =
static_cast<FileDescriptorSetChild*>(aFDs.get_PFileDescriptorSetChild());
MOZ_ASSERT(fdSetActor);
fdSetActor->ForgetFileDescriptors(fds);
MOZ_ASSERT(!fds.IsEmpty());
fdSetActor->Send__delete__(fdSetActor);
}
nsCOMPtr<nsIInputStream> stream = DeserializeInputStream(aParams, fds);
MOZ_ASSERT(stream);
mRemoteStream->SetStream(stream);
return true;
}
void
InputStreamParent::ActorDestroy(ActorDestroyReason aWhy)
{
// Implement me! Bug 1005150
}
bool
InputStreamParent::Recv__delete__(const InputStreamParams& aParams,
const OptionalFileDescriptorSet& aFDs)
{
MOZ_ASSERT(mRemoteStream);
mRemoteStream->AssertIsOnOwningThread();
if (aFDs.type() != OptionalFileDescriptorSet::Tvoid_t) {
NS_WARNING("Child cannot send FileDescriptors to the parent!");
return false;
}
nsTArray<FileDescriptor> fds;
nsCOMPtr<nsIInputStream> stream = DeserializeInputStream(aParams, fds);
if (!stream) {
return false;
}
MOZ_ASSERT(fds.IsEmpty());
mRemoteStream->SetStream(stream);
return true;
}