gecko/dom/ipc/Blob.cpp
Trevor Saunders 0f8704de2c no bug - fix nullptr to bool conversions in Blob.cpp r=themaid
--HG--
extra : rebase_source : aa2c9dc11c88beb53b12ddd583121ca90a2f4969
2014-11-03 07:59:27 -05:00

4229 lines
104 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/ClearOnShutdown.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/Monitor.h"
#include "mozilla/Mutex.h"
#include "mozilla/unused.h"
#include "mozilla/dom/File.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/FileSnapshot.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 "MultipartFileImpl.h"
#include "nsDataHashtable.h"
#include "nsHashKeys.h"
#include "nsID.h"
#include "nsIInputStream.h"
#include "nsIIPCSerializableInputStream.h"
#include "nsIMultiplexInputStream.h"
#include "nsIRemoteBlob.h"
#include "nsISeekableStream.h"
#include "nsIUUIDGenerator.h"
#include "nsNetCID.h"
#include "nsServiceManagerUtils.h"
#include "nsStringStream.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}}
namespace mozilla {
namespace dom {
using namespace mozilla::ipc;
using namespace mozilla::dom::indexedDB;
namespace {
const char kUUIDGeneratorContractId[] = "@mozilla.org/uuid-generator;1";
StaticRefPtr<nsIUUIDGenerator> gUUIDGenerator;
GeckoProcessType gProcessType = GeckoProcessType_Invalid;
void
CommonStartup()
{
MOZ_ASSERT(NS_IsMainThread());
gProcessType = XRE_GetProcessType();
MOZ_ASSERT(gProcessType != GeckoProcessType_Invalid);
nsCOMPtr<nsIUUIDGenerator> uuidGen = do_GetService(kUUIDGeneratorContractId);
MOZ_RELEASE_ASSERT(uuidGen);
gUUIDGenerator = uuidGen;
ClearOnShutdown(&gUUIDGenerator);
}
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;
};
void
AssertCorrectThreadForManager(nsIContentChild* aManager)
{
MOZ_ASSERT(NS_IsMainThread());
}
void
AssertCorrectThreadForManager(nsIContentParent* aManager)
{
MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
MOZ_ASSERT(NS_IsMainThread());
}
void
AssertCorrectThreadForManager(PBackgroundChild* aManager)
{
#ifdef DEBUG
if (aManager) {
PBackgroundChild* backgroundChild = BackgroundChild::GetForCurrentThread();
MOZ_ASSERT(backgroundChild);
MOZ_ASSERT(backgroundChild == aManager);
}
#endif
}
void
AssertCorrectThreadForManager(PBackgroundParent* aManager)
{
MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
AssertIsOnBackgroundThread();
}
intptr_t
ActorManagerProcessID(nsIContentParent* aManager)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aManager);
return reinterpret_cast<intptr_t>(aManager);
}
intptr_t
ActorManagerProcessID(PBackgroundParent* aManager)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aManager);
return BackgroundParent::GetRawContentParentForComparison(aManager);
}
bool
ActorManagerIsSameProcess(nsIContentParent* aManager)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aManager);
return false;
}
bool
ActorManagerIsSameProcess(PBackgroundParent* aManager)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aManager);
return !BackgroundParent::IsOtherProcessActor(aManager);
}
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 MOZ_FINAL
: public nsIMultiplexInputStream
, public nsISeekableStream
, public nsIIPCSerializableInputStream
{
nsCOMPtr<nsIInputStream> mStream;
nsRefPtr<FileImpl> mBlobImpl;
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, FileImpl* aBlobImpl)
: mStream(aStream)
, mBlobImpl(aBlobImpl)
, mWeakMultiplexStream(nullptr)
, mWeakSeekableStream(nullptr)
, mWeakSerializableStream(nullptr)
{
MOZ_ASSERT(aStream);
MOZ_ASSERT(aBlobImpl);
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;
}
}
private:
~BlobInputStreamTether()
{ }
};
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 MOZ_FINAL
: public nsIInputStream
, public nsISeekableStream
, public nsIIPCSerializableInputStream
, public IPrivateRemoteInputStream
{
Monitor mMonitor;
nsCOMPtr<nsIInputStream> mStream;
nsRefPtr<FileImpl> mBlobImpl;
nsCOMPtr<nsIEventTarget> mEventTarget;
nsISeekableStream* mWeakSeekableStream;
public:
NS_DECL_THREADSAFE_ISUPPORTS
explicit
RemoteInputStream(FileImpl* aBlobImpl)
: mMonitor("RemoteInputStream.mMonitor")
, mBlobImpl(aBlobImpl)
, mWeakSeekableStream(nullptr)
{
MOZ_ASSERT(IsOnOwningThread());
MOZ_ASSERT(aBlobImpl);
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 */)
{
MOZ_RELEASE_ASSERT(mBlobImpl);
nsCOMPtr<nsIRemoteBlob> remote = do_QueryInterface(mBlobImpl);
MOZ_ASSERT(remote);
BlobChild* actor = remote->GetBlobChild();
MOZ_ASSERT(actor);
aParams = RemoteInputStreamParams(actor->ParentID());
}
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<FileImpl> blobImpl;
mBlobImpl.swap(blobImpl);
rv = mStream->Close();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
NS_IMETHOD
Available(uint64_t* aAvailable) MOZ_OVERRIDE
{
if (!IsOnOwningThread()) {
nsresult rv = BlockAndWaitForStream();
NS_ENSURE_SUCCESS(rv, rv);
rv = mStream->Available(aAvailable);
NS_ENSURE_SUCCESS(rv, rv);
}
#ifdef DEBUG
if (NS_IsMainThread()) {
NS_WARNING("Someone is trying to do main-thread I/O...");
}
#endif
nsresult rv;
// See if we already have our real stream.
nsCOMPtr<nsIInputStream> inputStream;
{
MonitorAutoLock lock(mMonitor);
inputStream = mStream;
}
// If we do then just call through.
if (inputStream) {
rv = inputStream->Available(aAvailable);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
// If the stream is already closed then we can't do anything.
if (!mBlobImpl) {
return NS_BASE_STREAM_CLOSED;
}
// Otherwise fake it...
NS_WARNING("Available() called before real stream has been delivered, "
"guessing the amount of data available!");
ErrorResult error;
*aAvailable = mBlobImpl->GetSize(error);
if (NS_WARN_IF(error.Failed())) {
return error.ErrorCode();
}
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() MOZ_OVERRIDE
{
MOZ_ASSERT(!IsOnOwningThread());
nsresult rv = BlockAndWaitForStream();
NS_ENSURE_SUCCESS(rv, nullptr);
return mStream;
}
private:
~RemoteInputStream()
{
if (!IsOnOwningThread()) {
mStream = nullptr;
mWeakSeekableStream = nullptr;
if (mBlobImpl) {
ReleaseOnTarget(mBlobImpl, 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()
{ }
~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
{
public:
InputStreamParent()
{ }
~InputStreamParent()
{ }
private:
// This method is only called by the IPDL message machinery.
virtual void
ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE
{
// Nothing needs to be done here.
}
};
class EmptyBlobImpl MOZ_FINAL
: public FileImplBase
{
public:
explicit EmptyBlobImpl(const nsAString& aContentType)
: FileImplBase(aContentType, 0)
{
mImmutable = true;
}
EmptyBlobImpl(const nsAString& aName,
const nsAString& aContentType,
uint64_t aLastModifiedDate)
: FileImplBase(aName, aContentType, 0, aLastModifiedDate)
{
mImmutable = true;
}
private:
virtual already_AddRefed<FileImpl>
CreateSlice(uint64_t /* aStart */,
uint64_t aLength,
const nsAString& aContentType,
ErrorResult& /* aRv */) MOZ_OVERRIDE
{
MOZ_ASSERT(!aLength);
nsRefPtr<FileImpl> sliceImpl = new EmptyBlobImpl(aContentType);
DebugOnly<bool> isMutable;
MOZ_ASSERT(NS_SUCCEEDED(sliceImpl->GetMutable(&isMutable)));
MOZ_ASSERT(!isMutable);
return sliceImpl.forget();
}
virtual nsresult
GetInternalStream(nsIInputStream** aStream) MOZ_OVERRIDE
{
NS_ENSURE_ARG_POINTER(aStream);
nsString emptyString;
nsresult rv = NS_NewStringInputStream(aStream, emptyString);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
};
// This is only needed for IndexedDB FileImplSnapshot.
class SameProcessInputStreamBlobImpl MOZ_FINAL
: public FileImplBase
{
nsCOMPtr<nsIInputStream> mInputStream;
public:
SameProcessInputStreamBlobImpl(const nsAString& aContentType,
uint64_t aLength,
nsIInputStream* aInputStream)
: FileImplBase(aContentType, aLength)
, mInputStream(aInputStream)
{
MOZ_ASSERT(aLength != UINT64_MAX);
MOZ_ASSERT(aInputStream);
mImmutable = true;
}
SameProcessInputStreamBlobImpl(const nsAString& aName,
const nsAString& aContentType,
uint64_t aLength,
uint64_t aLastModifiedDate,
nsIInputStream* aInputStream)
: FileImplBase(aName, aContentType, aLength, aLastModifiedDate)
, mInputStream(aInputStream)
{
MOZ_ASSERT(aLength != UINT64_MAX);
MOZ_ASSERT(aLastModifiedDate != UINT64_MAX);
MOZ_ASSERT(aInputStream);
mImmutable = true;
}
private:
virtual already_AddRefed<FileImpl>
CreateSlice(uint64_t /* aStart */,
uint64_t /* aLength */,
const nsAString& /* aContentType */,
ErrorResult& /* aRv */) MOZ_OVERRIDE
{
MOZ_CRASH("Not implemented");
}
virtual nsresult
GetInternalStream(nsIInputStream** aStream) MOZ_OVERRIDE
{
NS_ENSURE_ARG_POINTER(aStream);
nsCOMPtr<nsIInputStream> inputStream = mInputStream;
inputStream.forget(aStream);
return NS_OK;
}
};
struct MOZ_STACK_CLASS CreateBlobImplMetadata MOZ_FINAL
{
nsString mContentType;
nsString mName;
uint64_t mLength;
uint64_t mLastModifiedDate;
bool mHasRecursed;
const bool mIsSameProcessActor;
explicit CreateBlobImplMetadata(bool aIsSameProcessActor)
: mLength(0)
, mLastModifiedDate(0)
, mHasRecursed(false)
, mIsSameProcessActor(aIsSameProcessActor)
{
MOZ_COUNT_CTOR(CreateBlobImplMetadata);
mName.SetIsVoid(true);
}
~CreateBlobImplMetadata()
{
MOZ_COUNT_DTOR(CreateBlobImplMetadata);
}
bool
IsFile() const
{
return !mName.IsVoid();
}
};
already_AddRefed<FileImpl>
CreateBlobImpl(const nsID& aKnownBlobIDData,
const CreateBlobImplMetadata& aMetadata)
{
MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
MOZ_ASSERT(aMetadata.mHasRecursed);
nsRefPtr<FileImpl> blobImpl = BlobParent::GetBlobImplForID(aKnownBlobIDData);
if (NS_WARN_IF(!blobImpl)) {
ASSERT_UNLESS_FUZZING();
return nullptr;
}
DebugOnly<bool> isMutable;
MOZ_ASSERT(NS_SUCCEEDED(blobImpl->GetMutable(&isMutable)));
MOZ_ASSERT(!isMutable);
return blobImpl.forget();
}
already_AddRefed<FileImpl>
CreateBlobImpl(const nsTArray<uint8_t>& aMemoryData,
const CreateBlobImplMetadata& aMetadata)
{
static_assert(sizeof(aMemoryData.Length()) <= sizeof(size_t),
"String length won't fit in size_t!");
static_assert(sizeof(size_t) <= sizeof(uint64_t),
"size_t won't fit in uint64_t!");
MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
nsRefPtr<FileImpl> blobImpl;
if (auto length = static_cast<size_t>(aMemoryData.Length())) {
static MOZ_CONSTEXPR_VAR size_t elementSizeMultiplier =
sizeof(aMemoryData[0]) / sizeof(char);
if (!aMetadata.mHasRecursed &&
NS_WARN_IF(aMetadata.mLength != uint64_t(length))) {
ASSERT_UNLESS_FUZZING();
return nullptr;
}
void* buffer = moz_malloc(length * elementSizeMultiplier);
if (NS_WARN_IF(!buffer)) {
return nullptr;
}
memcpy(buffer, aMemoryData.Elements(), length * elementSizeMultiplier);
if (!aMetadata.mHasRecursed && aMetadata.IsFile()) {
blobImpl =
new FileImplMemory(buffer,
uint64_t(length),
aMetadata.mName,
aMetadata.mContentType,
aMetadata.mLastModifiedDate);
} else {
blobImpl =
new FileImplMemory(buffer, uint64_t(length), aMetadata.mContentType);
}
} else if (!aMetadata.mHasRecursed && aMetadata.IsFile()) {
blobImpl =
new EmptyBlobImpl(aMetadata.mName,
aMetadata.mContentType,
aMetadata.mLastModifiedDate);
} else {
blobImpl = new EmptyBlobImpl(aMetadata.mContentType);
}
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(blobImpl->SetMutable(false)));
return blobImpl.forget();
}
already_AddRefed<FileImpl>
CreateBlobImpl(intptr_t aAddRefedInputStream,
const CreateBlobImplMetadata& aMetadata)
{
MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
MOZ_ASSERT(aMetadata.mIsSameProcessActor);
MOZ_ASSERT(aAddRefedInputStream);
nsCOMPtr<nsIInputStream> inputStream =
dont_AddRef(
reinterpret_cast<nsIInputStream*>(aAddRefedInputStream));
nsRefPtr<FileImpl> blobImpl;
if (!aMetadata.mHasRecursed && aMetadata.IsFile()) {
blobImpl =
new SameProcessInputStreamBlobImpl(aMetadata.mName,
aMetadata.mContentType,
aMetadata.mLength,
aMetadata.mLastModifiedDate,
inputStream);
} else {
blobImpl =
new SameProcessInputStreamBlobImpl(aMetadata.mContentType,
aMetadata.mLength,
inputStream);
}
DebugOnly<bool> isMutable;
MOZ_ASSERT(NS_SUCCEEDED(blobImpl->GetMutable(&isMutable)));
MOZ_ASSERT(!isMutable);
return blobImpl.forget();
}
already_AddRefed<FileImpl>
CreateBlobImpl(const nsTArray<BlobData>& aBlobData,
CreateBlobImplMetadata& aMetadata);
already_AddRefed<FileImpl>
CreateBlobImplFromBlobData(const BlobData& aBlobData,
CreateBlobImplMetadata& aMetadata)
{
MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
nsRefPtr<FileImpl> blobImpl;
switch (aBlobData.type()) {
case BlobData::TnsID: {
blobImpl = CreateBlobImpl(aBlobData.get_nsID(), aMetadata);
break;
}
case BlobData::TArrayOfuint8_t: {
blobImpl = CreateBlobImpl(aBlobData.get_ArrayOfuint8_t(), aMetadata);
break;
}
case BlobData::Tintptr_t: {
blobImpl = CreateBlobImpl(aBlobData.get_intptr_t(), aMetadata);
break;
}
case BlobData::TArrayOfBlobData: {
blobImpl = CreateBlobImpl(aBlobData.get_ArrayOfBlobData(), aMetadata);
break;
}
default:
MOZ_CRASH("Unknown params!");
}
return blobImpl.forget();
}
already_AddRefed<FileImpl>
CreateBlobImpl(const nsTArray<BlobData>& aBlobDatas,
CreateBlobImplMetadata& aMetadata)
{
MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
// Special case for a multipart blob with only one part.
if (aBlobDatas.Length() == 1) {
const BlobData& blobData = aBlobDatas[0];
nsRefPtr<FileImpl> blobImpl =
CreateBlobImplFromBlobData(blobData, aMetadata);
if (NS_WARN_IF(!blobImpl)) {
return nullptr;
}
DebugOnly<bool> isMutable;
MOZ_ASSERT(NS_SUCCEEDED(blobImpl->GetMutable(&isMutable)));
MOZ_ASSERT(!isMutable);
return blobImpl.forget();
}
FallibleTArray<nsRefPtr<FileImpl>> fallibleBlobImpls;
if (NS_WARN_IF(!fallibleBlobImpls.SetLength(aBlobDatas.Length()))) {
return nullptr;
}
nsTArray<nsRefPtr<FileImpl>> blobImpls;
fallibleBlobImpls.SwapElements(blobImpls);
const bool hasRecursed = aMetadata.mHasRecursed;
aMetadata.mHasRecursed = true;
for (uint32_t count = aBlobDatas.Length(), index = 0;
index < count;
index++) {
const BlobData& blobData = aBlobDatas[index];
nsRefPtr<FileImpl>& blobImpl = blobImpls[index];
blobImpl = CreateBlobImplFromBlobData(blobData, aMetadata);
if (NS_WARN_IF(!blobImpl)) {
return nullptr;
}
DebugOnly<bool> isMutable;
MOZ_ASSERT(NS_SUCCEEDED(blobImpl->GetMutable(&isMutable)));
MOZ_ASSERT(!isMutable);
}
nsRefPtr<FileImpl> blobImpl;
if (!hasRecursed && aMetadata.IsFile()) {
blobImpl =
new MultipartFileImpl(blobImpls, aMetadata.mName, aMetadata.mContentType);
} else {
blobImpl =
new MultipartFileImpl(blobImpls, aMetadata.mContentType);
}
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(blobImpl->SetMutable(false)));
return blobImpl.forget();
}
already_AddRefed<FileImpl>
CreateBlobImpl(const ParentBlobConstructorParams& aParams,
const BlobData& aBlobData,
bool aIsSameProcessActor)
{
MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
MOZ_ASSERT(aParams.blobParams().type() ==
AnyBlobConstructorParams::TNormalBlobConstructorParams ||
aParams.blobParams().type() ==
AnyBlobConstructorParams::TFileBlobConstructorParams);
CreateBlobImplMetadata metadata(aIsSameProcessActor);
if (aParams.blobParams().type() ==
AnyBlobConstructorParams::TNormalBlobConstructorParams) {
const NormalBlobConstructorParams& params =
aParams.blobParams().get_NormalBlobConstructorParams();
if (NS_WARN_IF(params.length() == UINT64_MAX)) {
ASSERT_UNLESS_FUZZING();
return nullptr;
}
metadata.mContentType = params.contentType();
metadata.mLength = params.length();
} else {
const FileBlobConstructorParams& params =
aParams.blobParams().get_FileBlobConstructorParams();
if (NS_WARN_IF(params.length() == UINT64_MAX)) {
ASSERT_UNLESS_FUZZING();
return nullptr;
}
if (NS_WARN_IF(params.modDate() == UINT64_MAX)) {
ASSERT_UNLESS_FUZZING();
return nullptr;
}
metadata.mContentType = params.contentType();
metadata.mName = params.name();
metadata.mLength = params.length();
metadata.mLastModifiedDate = params.modDate();
}
nsRefPtr<FileImpl> blobImpl =
CreateBlobImplFromBlobData(aBlobData, metadata);
return blobImpl.forget();
}
void
BlobDataFromBlobImpl(FileImpl* aBlobImpl, BlobData& aBlobData)
{
MOZ_ASSERT(gProcessType != GeckoProcessType_Default);
MOZ_ASSERT(aBlobImpl);
const nsTArray<nsRefPtr<FileImpl>>* subBlobs = aBlobImpl->GetSubBlobImpls();
if (subBlobs) {
aBlobData = nsTArray<BlobData>();
nsTArray<BlobData>& subBlobDatas = aBlobData.get_ArrayOfBlobData();
subBlobDatas.SetLength(subBlobs->Length());
for (uint32_t count = subBlobs->Length(), index = 0;
index < count;
index++) {
BlobDataFromBlobImpl(subBlobs->ElementAt(index), subBlobDatas[index]);
}
return;
}
nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryInterface(aBlobImpl);
if (remoteBlob) {
BlobChild* actor = remoteBlob->GetBlobChild();
MOZ_ASSERT(actor);
aBlobData = actor->ParentID();
return;
}
MOZ_ASSERT(aBlobImpl->IsMemoryFile());
nsCOMPtr<nsIInputStream> inputStream;
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
aBlobImpl->GetInternalStream(getter_AddRefs(inputStream))));
DebugOnly<bool> isNonBlocking;
MOZ_ASSERT(NS_SUCCEEDED(inputStream->IsNonBlocking(&isNonBlocking)));
MOZ_ASSERT(isNonBlocking);
uint64_t available;
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(inputStream->Available(&available)));
MOZ_ASSERT(available <= uint64_t(UINT32_MAX));
aBlobData = nsTArray<uint8_t>();
nsTArray<uint8_t>& blobData = aBlobData.get_ArrayOfuint8_t();
blobData.SetLength(size_t(available));
uint32_t readCount;
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
inputStream->Read(reinterpret_cast<char*>(blobData.Elements()),
uint32_t(available),
&readCount)));
}
} // anonymous namespace
StaticAutoPtr<BlobParent::IDTable> BlobParent::sIDTable;
StaticAutoPtr<Mutex> BlobParent::sIDTableMutex;
/*******************************************************************************
* BlobParent::IDTableEntry Declaration
******************************************************************************/
class BlobParent::IDTableEntry MOZ_FINAL
{
const nsID mID;
const intptr_t mProcessID;
const nsRefPtr<FileImpl> mBlobImpl;
public:
static already_AddRefed<IDTableEntry>
Create(const nsID& aID, intptr_t aProcessID, FileImpl* aBlobImpl)
{
MOZ_ASSERT(aBlobImpl);
DebugOnly<bool> isMutable;
MOZ_ASSERT(NS_SUCCEEDED(aBlobImpl->GetMutable(&isMutable)));
MOZ_ASSERT(!isMutable);
return GetOrCreateInternal(aID,
aProcessID,
aBlobImpl,
/* aMayCreate */ true,
/* aMayGet */ false,
/* aIgnoreProcessID */ false);
}
static already_AddRefed<IDTableEntry>
Get(const nsID& aID, intptr_t aProcessID)
{
return GetOrCreateInternal(aID,
aProcessID,
nullptr,
/* aMayCreate */ false,
/* aMayGet */ true,
/* aIgnoreProcessID */ false);
}
static already_AddRefed<IDTableEntry>
Get(const nsID& aID)
{
return GetOrCreateInternal(aID,
0,
nullptr,
/* aMayCreate */ false,
/* aMayGet */ true,
/* aIgnoreProcessID */ true);
}
static already_AddRefed<IDTableEntry>
GetOrCreate(const nsID& aID, intptr_t aProcessID, FileImpl* aBlobImpl)
{
MOZ_ASSERT(aBlobImpl);
DebugOnly<bool> isMutable;
MOZ_ASSERT(NS_SUCCEEDED(aBlobImpl->GetMutable(&isMutable)));
MOZ_ASSERT(!isMutable);
return GetOrCreateInternal(aID,
aProcessID,
aBlobImpl,
/* aMayCreate */ true,
/* aMayGet */ true,
/* aIgnoreProcessID */ false);
}
const nsID&
ID() const
{
return mID;
}
intptr_t
ProcessID() const
{
return mProcessID;
}
FileImpl*
BlobImpl() const
{
return mBlobImpl;
}
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(IDTableEntry)
private:
IDTableEntry(const nsID& aID, intptr_t aProcessID, FileImpl* aBlobImpl);
~IDTableEntry();
static already_AddRefed<IDTableEntry>
GetOrCreateInternal(const nsID& aID,
intptr_t aProcessID,
FileImpl* aBlobImpl,
bool aMayCreate,
bool aMayGet,
bool aIgnoreProcessID);
};
/*******************************************************************************
* BlobParent::OpenStreamRunnable Declaration
******************************************************************************/
// 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::RemoteBlobImpl Declaration
******************************************************************************/
class BlobChild::RemoteBlobImpl
: public FileImplBase
, public nsIRemoteBlob
{
protected:
class CreateStreamHelper;
BlobChild* mActor;
nsCOMPtr<nsIEventTarget> mActorTarget;
nsRefPtr<FileImpl> mSameProcessFileImpl;
const bool mIsSlice;
public:
// For File.
RemoteBlobImpl(BlobChild* aActor,
const nsAString& aName,
const nsAString& aContentType,
uint64_t aLength,
uint64_t aModDate);
// For Blob.
RemoteBlobImpl(BlobChild* aActor,
const nsAString& aContentType,
uint64_t aLength);
// For same-process blobs.
RemoteBlobImpl(BlobChild* aActor,
FileImpl* aSameProcessBlobImpl,
const nsAString& aName,
const nsAString& aContentType,
uint64_t aLength,
uint64_t aModDate);
// For same-process blobs.
RemoteBlobImpl(BlobChild* aActor,
FileImpl* aSameProcessBlobImpl,
const nsAString& aContentType,
uint64_t aLength);
// For mystery blobs.
explicit
RemoteBlobImpl(BlobChild* aActor);
void
NoteDyingActor();
BlobChild*
GetActor() const
{
AssertActorEventTargetIsOnCurrentThread();
return mActor;
}
nsIEventTarget*
GetActorEventTarget() const
{
return mActorTarget;
}
void
AssertActorEventTargetIsOnCurrentThread() const
{
MOZ_ASSERT(
EventTargetIsOnCurrentThread(BaseRemoteBlobImpl()->mActorTarget));
}
bool
IsSlice() const
{
return mIsSlice;
}
RemoteBlobSliceImpl*
AsSlice() const;
RemoteBlobImpl*
BaseRemoteBlobImpl() const;
NS_DECL_ISUPPORTS_INHERITED
virtual void
GetMozFullPathInternal(nsAString& aFileName, ErrorResult& aRv) MOZ_OVERRIDE;
virtual already_AddRefed<FileImpl>
CreateSlice(uint64_t aStart,
uint64_t aLength,
const nsAString& aContentType,
ErrorResult& aRv) MOZ_OVERRIDE;
virtual nsresult
GetInternalStream(nsIInputStream** aStream) MOZ_OVERRIDE;
virtual int64_t
GetFileId() MOZ_OVERRIDE;
virtual int64_t GetLastModified(ErrorResult& aRv) MOZ_OVERRIDE;
virtual BlobChild*
GetBlobChild() MOZ_OVERRIDE;
virtual BlobParent*
GetBlobParent() MOZ_OVERRIDE;
protected:
// For SliceImpl.
RemoteBlobImpl(const nsAString& aContentType, uint64_t aLength);
~RemoteBlobImpl()
{
MOZ_ASSERT_IF(mActorTarget,
EventTargetIsOnCurrentThread(mActorTarget));
}
void
CommonInit(BlobChild* aActor);
void
Destroy();
};
class BlobChild::RemoteBlobImpl::CreateStreamHelper MOZ_FINAL
: public nsRunnable
{
Monitor mMonitor;
nsRefPtr<RemoteBlobImpl> mRemoteBlobImpl;
nsRefPtr<RemoteInputStream> mInputStream;
const uint64_t mStart;
const uint64_t mLength;
bool mDone;
public:
explicit CreateStreamHelper(RemoteBlobImpl* aRemoteBlobImpl);
nsresult
GetStream(nsIInputStream** aInputStream);
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIRUNNABLE
private:
~CreateStreamHelper()
{
MOZ_ASSERT(!mRemoteBlobImpl);
MOZ_ASSERT(!mInputStream);
MOZ_ASSERT(mDone);
}
void
RunInternal(RemoteBlobImpl* aBaseRemoteBlobImpl, bool aNotify);
};
class BlobChild::RemoteBlobSliceImpl MOZ_FINAL
: public RemoteBlobImpl
{
nsRefPtr<RemoteBlobImpl> mParent;
bool mActorWasCreated;
public:
RemoteBlobSliceImpl(RemoteBlobImpl* aParent,
uint64_t aStart,
uint64_t aLength,
const nsAString& aContentType);
RemoteBlobImpl*
Parent() const
{
MOZ_ASSERT(mParent);
return const_cast<RemoteBlobImpl*>(mParent.get());
}
uint64_t
Start() const
{
return mStart;
}
NS_DECL_ISUPPORTS_INHERITED
virtual BlobChild*
GetBlobChild() MOZ_OVERRIDE;
private:
~RemoteBlobSliceImpl()
{ }
};
/*******************************************************************************
* BlobParent::RemoteBlobImpl Declaration
******************************************************************************/
class BlobParent::RemoteBlobImpl MOZ_FINAL
: public FileImpl
, public nsIRemoteBlob
{
BlobParent* mActor;
nsCOMPtr<nsIEventTarget> mActorTarget;
nsRefPtr<FileImpl> mBlobImpl;
public:
RemoteBlobImpl(BlobParent* aActor, FileImpl* aBlobImpl);
void
NoteDyingActor();
NS_DECL_ISUPPORTS_INHERITED
virtual void
GetName(nsAString& aName) MOZ_OVERRIDE;
virtual nsresult
GetPath(nsAString& aPath) MOZ_OVERRIDE;
virtual int64_t
GetLastModified(ErrorResult& aRv) MOZ_OVERRIDE;
virtual void
GetMozFullPath(nsAString& aName, ErrorResult& aRv) MOZ_OVERRIDE;
virtual void
GetMozFullPathInternal(nsAString& aFileName, ErrorResult& aRv) MOZ_OVERRIDE;
virtual uint64_t
GetSize(ErrorResult& aRv) MOZ_OVERRIDE;
virtual void
GetType(nsAString& aType) MOZ_OVERRIDE;
virtual already_AddRefed<FileImpl>
CreateSlice(uint64_t aStart,
uint64_t aLength,
const nsAString& aContentType,
ErrorResult& aRv) MOZ_OVERRIDE;
virtual const nsTArray<nsRefPtr<FileImpl>>*
GetSubBlobImpls() const MOZ_OVERRIDE;
virtual nsresult
GetInternalStream(nsIInputStream** aStream) MOZ_OVERRIDE;
virtual int64_t
GetFileId() MOZ_OVERRIDE;
virtual void
AddFileInfo(FileInfo* aFileInfo) MOZ_OVERRIDE;
virtual FileInfo*
GetFileInfo(FileManager* aFileManager) MOZ_OVERRIDE;
virtual nsresult
GetSendInfo(nsIInputStream** aBody,
uint64_t* aContentLength,
nsACString& aContentType,
nsACString& aCharset) MOZ_OVERRIDE;
virtual nsresult
GetMutable(bool* aMutable) const MOZ_OVERRIDE;
virtual nsresult
SetMutable(bool aMutable) MOZ_OVERRIDE;
virtual void
SetLazyData(const nsAString& aName,
const nsAString& aContentType,
uint64_t aLength,
uint64_t aLastModifiedDate) MOZ_OVERRIDE;
virtual bool
IsMemoryFile() const MOZ_OVERRIDE;
virtual bool
IsSizeUnknown() const MOZ_OVERRIDE;
virtual bool
IsDateUnknown() const MOZ_OVERRIDE;
virtual bool
IsFile() const MOZ_OVERRIDE;
virtual void
Unlink() MOZ_OVERRIDE;
virtual void
Traverse(nsCycleCollectionTraversalCallback& aCallback) MOZ_OVERRIDE;
virtual BlobChild*
GetBlobChild() MOZ_OVERRIDE;
virtual BlobParent*
GetBlobParent() MOZ_OVERRIDE;
private:
~RemoteBlobImpl()
{
MOZ_ASSERT_IF(mActorTarget,
EventTargetIsOnCurrentThread(mActorTarget));
}
void
Destroy();
};
/*******************************************************************************
* BlobChild::RemoteBlobImpl
******************************************************************************/
BlobChild::
RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor,
const nsAString& aName,
const nsAString& aContentType,
uint64_t aLength,
uint64_t aModDate)
: FileImplBase(aName, aContentType, aLength, aModDate)
, mIsSlice(false)
{
CommonInit(aActor);
}
BlobChild::
RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor,
const nsAString& aContentType,
uint64_t aLength)
: FileImplBase(aContentType, aLength)
, mIsSlice(false)
{
CommonInit(aActor);
}
BlobChild::
RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor,
FileImpl* aSameProcessBlobImpl,
const nsAString& aName,
const nsAString& aContentType,
uint64_t aLength,
uint64_t aModDate)
: FileImplBase(aName, aContentType, aLength, aModDate)
, mSameProcessFileImpl(aSameProcessBlobImpl)
, mIsSlice(false)
{
MOZ_ASSERT(aSameProcessBlobImpl);
MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
CommonInit(aActor);
}
BlobChild::
RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor,
FileImpl* aSameProcessBlobImpl,
const nsAString& aContentType,
uint64_t aLength)
: FileImplBase(aContentType, aLength)
, mSameProcessFileImpl(aSameProcessBlobImpl)
, mIsSlice(false)
{
MOZ_ASSERT(aSameProcessBlobImpl);
MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
CommonInit(aActor);
}
BlobChild::
RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor)
: FileImplBase(EmptyString(), EmptyString(), UINT64_MAX, UINT64_MAX)
, mIsSlice(false)
{
CommonInit(aActor);
}
BlobChild::
RemoteBlobImpl::RemoteBlobImpl(const nsAString& aContentType, uint64_t aLength)
: FileImplBase(aContentType, aLength)
, mActor(nullptr)
, mIsSlice(true)
{
mImmutable = true;
}
void
BlobChild::
RemoteBlobImpl::CommonInit(BlobChild* aActor)
{
MOZ_ASSERT(aActor);
aActor->AssertIsOnOwningThread();
MOZ_ASSERT(!mIsSlice);
mActor = aActor;
mActorTarget = aActor->EventTarget();
mImmutable = true;
}
void
BlobChild::
RemoteBlobImpl::NoteDyingActor()
{
MOZ_ASSERT(mActor);
mActor->AssertIsOnOwningThread();
mActor = nullptr;
}
BlobChild::RemoteBlobSliceImpl*
BlobChild::
RemoteBlobImpl::AsSlice() const
{
MOZ_ASSERT(IsSlice());
return static_cast<RemoteBlobSliceImpl*>(const_cast<RemoteBlobImpl*>(this));
}
BlobChild::RemoteBlobImpl*
BlobChild::
RemoteBlobImpl::BaseRemoteBlobImpl() const
{
if (IsSlice()) {
return AsSlice()->Parent()->BaseRemoteBlobImpl();
}
return const_cast<RemoteBlobImpl*>(this);
}
void
BlobChild::
RemoteBlobImpl::Destroy()
{
if (EventTargetIsOnCurrentThread(mActorTarget)) {
if (mActor) {
mActor->AssertIsOnOwningThread();
mActor->NoteDyingRemoteBlobImpl();
}
delete this;
return;
}
nsCOMPtr<nsIRunnable> destroyRunnable =
NS_NewNonOwningRunnableMethod(this, &RemoteBlobImpl::Destroy);
if (mActorTarget) {
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mActorTarget->Dispatch(destroyRunnable,
NS_DISPATCH_NORMAL)));
} else {
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(destroyRunnable)));
}
}
NS_IMPL_ADDREF(BlobChild::RemoteBlobImpl)
NS_IMPL_RELEASE_WITH_DESTROY(BlobChild::RemoteBlobImpl, Destroy())
NS_IMPL_QUERY_INTERFACE_INHERITED(BlobChild::RemoteBlobImpl,
FileImpl,
nsIRemoteBlob)
void
BlobChild::
RemoteBlobImpl::GetMozFullPathInternal(nsAString& aFilePath,
ErrorResult& aRv)
{
if (!EventTargetIsOnCurrentThread(mActorTarget)) {
MOZ_CRASH("Not implemented!");
}
if (mSameProcessFileImpl) {
MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
mSameProcessFileImpl->GetMozFullPathInternal(aFilePath, aRv);
return;
}
if (!mActor) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return;
}
nsString filePath;
if (!mActor->SendGetFilePath(&filePath)) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
aFilePath = filePath;
}
already_AddRefed<FileImpl>
BlobChild::
RemoteBlobImpl::CreateSlice(uint64_t aStart,
uint64_t aLength,
const nsAString& aContentType,
ErrorResult& aRv)
{
// May be called on any thread.
if (mSameProcessFileImpl) {
MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
return mSameProcessFileImpl->CreateSlice(aStart,
aLength,
aContentType,
aRv);
}
nsRefPtr<RemoteBlobSliceImpl> slice =
new RemoteBlobSliceImpl(this, aStart, aLength, aContentType);
return slice.forget();
}
nsresult
BlobChild::
RemoteBlobImpl::GetInternalStream(nsIInputStream** aStream)
{
// May be called on any thread.
if (mSameProcessFileImpl) {
MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
nsCOMPtr<nsIInputStream> realStream;
nsresult rv =
mSameProcessFileImpl->GetInternalStream(getter_AddRefs(realStream));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsRefPtr<BlobInputStreamTether> tether =
new BlobInputStreamTether(realStream, mSameProcessFileImpl);
tether.forget(aStream);
return NS_OK;
}
nsRefPtr<CreateStreamHelper> helper = new CreateStreamHelper(this);
return helper->GetStream(aStream);
}
int64_t
BlobChild::
RemoteBlobImpl::GetFileId()
{
if (!EventTargetIsOnCurrentThread(mActorTarget)) {
MOZ_CRASH("Not implemented!");
}
if (mSameProcessFileImpl) {
MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
return mSameProcessFileImpl->GetFileId();
}
int64_t fileId;
if (mActor && mActor->SendGetFileId(&fileId)) {
return fileId;
}
return -1;
}
int64_t
BlobChild::
RemoteBlobImpl::GetLastModified(ErrorResult& aRv)
{
if (IsDateUnknown()) {
return 0;
}
return mLastModificationDate;
}
BlobChild*
BlobChild::
RemoteBlobImpl::GetBlobChild()
{
return mActor;
}
BlobParent*
BlobChild::
RemoteBlobImpl::GetBlobParent()
{
return nullptr;
}
/*******************************************************************************
* BlobChild::RemoteBlobImpl::CreateStreamHelper
******************************************************************************/
BlobChild::RemoteBlobImpl::
CreateStreamHelper::CreateStreamHelper(RemoteBlobImpl* aRemoteBlobImpl)
: mMonitor("BlobChild::RemoteBlobImpl::CreateStreamHelper::mMonitor")
, mRemoteBlobImpl(aRemoteBlobImpl)
, mStart(aRemoteBlobImpl->IsSlice() ? aRemoteBlobImpl->AsSlice()->Start() : 0)
, mLength(0)
, mDone(false)
{
// This may be created on any thread.
MOZ_ASSERT(aRemoteBlobImpl);
ErrorResult rv;
const_cast<uint64_t&>(mLength) = aRemoteBlobImpl->GetSize(rv);
MOZ_ASSERT(!rv.Failed());
}
nsresult
BlobChild::RemoteBlobImpl::
CreateStreamHelper::GetStream(nsIInputStream** aInputStream)
{
// This may be called on any thread.
MOZ_ASSERT(aInputStream);
MOZ_ASSERT(mRemoteBlobImpl);
MOZ_ASSERT(!mInputStream);
MOZ_ASSERT(!mDone);
nsRefPtr<RemoteBlobImpl> baseRemoteBlobImpl =
mRemoteBlobImpl->BaseRemoteBlobImpl();
MOZ_ASSERT(baseRemoteBlobImpl);
if (EventTargetIsOnCurrentThread(baseRemoteBlobImpl->GetActorEventTarget())) {
RunInternal(baseRemoteBlobImpl, false);
} else {
nsCOMPtr<nsIEventTarget> target = baseRemoteBlobImpl->GetActorEventTarget();
if (!target) {
target = do_GetMainThread();
}
MOZ_ASSERT(target);
nsresult rv = target->Dispatch(this, NS_DISPATCH_NORMAL);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
{
MonitorAutoLock lock(mMonitor);
while (!mDone) {
lock.Wait();
}
}
}
MOZ_ASSERT(!mRemoteBlobImpl);
MOZ_ASSERT(mDone);
if (!mInputStream) {
return NS_ERROR_UNEXPECTED;
}
mInputStream.forget(aInputStream);
return NS_OK;
}
void
BlobChild::RemoteBlobImpl::
CreateStreamHelper::RunInternal(RemoteBlobImpl* aBaseRemoteBlobImpl,
bool aNotify)
{
MOZ_ASSERT(aBaseRemoteBlobImpl);
aBaseRemoteBlobImpl->AssertActorEventTargetIsOnCurrentThread();
MOZ_ASSERT(!mInputStream);
MOZ_ASSERT(!mDone);
if (BlobChild* actor = aBaseRemoteBlobImpl->GetActor()) {
nsRefPtr<RemoteInputStream> stream =
new RemoteInputStream(aBaseRemoteBlobImpl);
InputStreamChild* streamActor = new InputStreamChild(stream);
if (actor->SendPBlobStreamConstructor(streamActor, mStart, mLength)) {
stream.swap(mInputStream);
}
}
mRemoteBlobImpl = nullptr;
if (aNotify) {
MonitorAutoLock lock(mMonitor);
mDone = true;
lock.Notify();
} else {
mDone = true;
}
}
NS_IMPL_ISUPPORTS_INHERITED0(BlobChild::RemoteBlobImpl::CreateStreamHelper,
nsRunnable)
NS_IMETHODIMP
BlobChild::RemoteBlobImpl::
CreateStreamHelper::Run()
{
MOZ_ASSERT(mRemoteBlobImpl);
mRemoteBlobImpl->AssertActorEventTargetIsOnCurrentThread();
nsRefPtr<RemoteBlobImpl> baseRemoteBlobImpl =
mRemoteBlobImpl->BaseRemoteBlobImpl();
MOZ_ASSERT(baseRemoteBlobImpl);
RunInternal(baseRemoteBlobImpl, true);
return NS_OK;
}
/*******************************************************************************
* BlobChild::RemoteBlobSliceImpl
******************************************************************************/
BlobChild::
RemoteBlobSliceImpl::RemoteBlobSliceImpl(RemoteBlobImpl* aParent,
uint64_t aStart,
uint64_t aLength,
const nsAString& aContentType)
: RemoteBlobImpl(aContentType, aLength)
, mParent(aParent->BaseRemoteBlobImpl())
, mActorWasCreated(false)
{
MOZ_ASSERT(mParent);
MOZ_ASSERT(mParent->BaseRemoteBlobImpl() == mParent);
DebugOnly<bool> isMutable;
MOZ_ASSERT(NS_SUCCEEDED(aParent->GetMutable(&isMutable)));
MOZ_ASSERT(!isMutable);
#ifdef DEBUG
{
ErrorResult rv;
uint64_t parentSize = aParent->GetSize(rv);
MOZ_ASSERT(!rv.Failed());
MOZ_ASSERT(parentSize >= aStart + aLength);
}
#endif
// Account for the offset of the parent slice, if any.
mStart = aParent->IsSlice() ? aParent->AsSlice()->mStart + aStart : aStart;
}
NS_IMPL_ISUPPORTS_INHERITED0(BlobChild::RemoteBlobSliceImpl,
BlobChild::RemoteBlobImpl)
BlobChild*
BlobChild::
RemoteBlobSliceImpl::GetBlobChild()
{
AssertActorEventTargetIsOnCurrentThread();
if (mActorWasCreated) {
return RemoteBlobImpl::GetBlobChild();
}
mActorWasCreated = true;
BlobChild* baseActor = mParent->GetActor();
MOZ_ASSERT(baseActor);
MOZ_ASSERT(baseActor->HasManager());
nsID id;
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(gUUIDGenerator->GenerateUUIDInPlace(&id)));
ParentBlobConstructorParams params(
SlicedBlobConstructorParams(nullptr /* sourceParent */,
baseActor /* sourceChild */,
id /* id */,
mStart /* begin */,
mStart + mLength /* end */,
mContentType /* contentType */));
if (nsIContentChild* contentManager = baseActor->GetContentManager()) {
mActor = SendSliceConstructor(contentManager, this, params);
} else {
mActor =
SendSliceConstructor(baseActor->GetBackgroundManager(), this, params);
}
return mActor;
}
/*******************************************************************************
* BlobParent::RemoteBlobImpl
******************************************************************************/
BlobParent::
RemoteBlobImpl::RemoteBlobImpl(BlobParent* aActor, FileImpl* aBlobImpl)
: mActor(aActor)
, mActorTarget(aActor->EventTarget())
, mBlobImpl(aBlobImpl)
{
MOZ_ASSERT(aActor);
aActor->AssertIsOnOwningThread();
MOZ_ASSERT(aBlobImpl);
DebugOnly<bool> isMutable;
MOZ_ASSERT(NS_SUCCEEDED(aBlobImpl->GetMutable(&isMutable)));
MOZ_ASSERT(!isMutable);
}
void
BlobParent::
RemoteBlobImpl::NoteDyingActor()
{
MOZ_ASSERT(mActor);
mActor->AssertIsOnOwningThread();
mActor = nullptr;
}
void
BlobParent::
RemoteBlobImpl::Destroy()
{
if (EventTargetIsOnCurrentThread(mActorTarget)) {
if (mActor) {
mActor->AssertIsOnOwningThread();
mActor->NoteDyingRemoteBlobImpl();
}
delete this;
return;
}
nsCOMPtr<nsIRunnable> destroyRunnable =
NS_NewNonOwningRunnableMethod(this, &RemoteBlobImpl::Destroy);
if (mActorTarget) {
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mActorTarget->Dispatch(destroyRunnable,
NS_DISPATCH_NORMAL)));
} else {
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(destroyRunnable)));
}
}
NS_IMPL_ADDREF(BlobParent::RemoteBlobImpl)
NS_IMPL_RELEASE_WITH_DESTROY(BlobParent::RemoteBlobImpl, Destroy())
NS_IMPL_QUERY_INTERFACE_INHERITED(BlobParent::RemoteBlobImpl,
FileImpl,
nsIRemoteBlob)
void
BlobParent::
RemoteBlobImpl::GetName(nsAString& aName)
{
mBlobImpl->GetName(aName);
}
nsresult
BlobParent::
RemoteBlobImpl::GetPath(nsAString& aPath)
{
return mBlobImpl->GetPath(aPath);
}
int64_t
BlobParent::
RemoteBlobImpl::GetLastModified(ErrorResult& aRv)
{
return mBlobImpl->GetLastModified(aRv);
}
void
BlobParent::
RemoteBlobImpl::GetMozFullPath(nsAString& aName, ErrorResult& aRv)
{
mBlobImpl->GetMozFullPath(aName, aRv);
}
void
BlobParent::
RemoteBlobImpl::GetMozFullPathInternal(nsAString& aFileName, ErrorResult& aRv)
{
mBlobImpl->GetMozFullPathInternal(aFileName, aRv);
}
uint64_t
BlobParent::
RemoteBlobImpl::GetSize(ErrorResult& aRv)
{
return mBlobImpl->GetSize(aRv);
}
void
BlobParent::
RemoteBlobImpl::GetType(nsAString& aType)
{
mBlobImpl->GetType(aType);
}
already_AddRefed<FileImpl>
BlobParent::
RemoteBlobImpl::CreateSlice(uint64_t aStart,
uint64_t aLength,
const nsAString& aContentType,
ErrorResult& aRv)
{
return mBlobImpl->CreateSlice(aStart, aLength, aContentType, aRv);
}
const nsTArray<nsRefPtr<FileImpl>>*
BlobParent::
RemoteBlobImpl::GetSubBlobImpls() const
{
return mBlobImpl->GetSubBlobImpls();
}
nsresult
BlobParent::
RemoteBlobImpl::GetInternalStream(nsIInputStream** aStream)
{
return mBlobImpl->GetInternalStream(aStream);
}
int64_t
BlobParent::
RemoteBlobImpl::GetFileId()
{
return mBlobImpl->GetFileId();
}
void
BlobParent::
RemoteBlobImpl::AddFileInfo(FileInfo* aFileInfo)
{
return mBlobImpl->AddFileInfo(aFileInfo);
}
FileInfo*
BlobParent::
RemoteBlobImpl::GetFileInfo(FileManager* aFileManager)
{
return mBlobImpl->GetFileInfo(aFileManager);
}
nsresult
BlobParent::
RemoteBlobImpl::GetSendInfo(nsIInputStream** aBody,
uint64_t* aContentLength,
nsACString& aContentType,
nsACString& aCharset)
{
return mBlobImpl->GetSendInfo(aBody,
aContentLength,
aContentType,
aCharset);
}
nsresult
BlobParent::
RemoteBlobImpl::GetMutable(bool* aMutable) const
{
return mBlobImpl->GetMutable(aMutable);
}
nsresult
BlobParent::
RemoteBlobImpl::SetMutable(bool aMutable)
{
return mBlobImpl->SetMutable(aMutable);
}
void
BlobParent::
RemoteBlobImpl::SetLazyData(const nsAString& aName,
const nsAString& aContentType,
uint64_t aLength,
uint64_t aLastModifiedDate)
{
MOZ_CRASH("This should never be called!");
}
bool
BlobParent::
RemoteBlobImpl::IsMemoryFile() const
{
return mBlobImpl->IsMemoryFile();
}
bool
BlobParent::
RemoteBlobImpl::IsSizeUnknown() const
{
return mBlobImpl->IsSizeUnknown();
}
bool
BlobParent::
RemoteBlobImpl::IsDateUnknown() const
{
return mBlobImpl->IsDateUnknown();
}
bool
BlobParent::
RemoteBlobImpl::IsFile() const
{
return mBlobImpl->IsFile();
}
void
BlobParent::
RemoteBlobImpl::Unlink()
{
return mBlobImpl->Unlink();
}
void
BlobParent::
RemoteBlobImpl::Traverse(nsCycleCollectionTraversalCallback& aCallback)
{
return mBlobImpl->Traverse(aCallback);
}
BlobChild*
BlobParent::
RemoteBlobImpl::GetBlobChild()
{
return nullptr;
}
BlobParent*
BlobParent::
RemoteBlobImpl::GetBlobParent()
{
return mActor;
}
/*******************************************************************************
* BlobChild
******************************************************************************/
BlobChild::BlobChild(nsIContentChild* aManager, FileImpl* aBlobImpl)
: mBackgroundManager(nullptr)
, mContentManager(aManager)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aManager);
CommonInit(aBlobImpl);
}
BlobChild::BlobChild(PBackgroundChild* aManager, FileImpl* aBlobImpl)
: mBackgroundManager(aManager)
, mContentManager(nullptr)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aManager);
CommonInit(aBlobImpl);
if (!NS_IsMainThread()) {
mEventTarget = do_GetCurrentThread();
MOZ_ASSERT(mEventTarget);
}
}
BlobChild::BlobChild(nsIContentChild* aManager, BlobChild* aOther)
: mBackgroundManager(nullptr)
, mContentManager(aManager)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aManager);
CommonInit(aOther);
}
BlobChild::BlobChild(PBackgroundChild* aManager, BlobChild* aOther)
: mBackgroundManager(aManager)
, mContentManager(nullptr)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aManager);
CommonInit(aOther);
if (!NS_IsMainThread()) {
mEventTarget = do_GetCurrentThread();
MOZ_ASSERT(mEventTarget);
}
}
BlobChild::BlobChild(nsIContentChild* aManager,
const ChildBlobConstructorParams& aParams)
: mBackgroundManager(nullptr)
, mContentManager(aManager)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aManager);
CommonInit(aParams);
}
BlobChild::BlobChild(PBackgroundChild* aManager,
const ChildBlobConstructorParams& aParams)
: mBackgroundManager(aManager)
, mContentManager(nullptr)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aManager);
CommonInit(aParams);
if (!NS_IsMainThread()) {
mEventTarget = do_GetCurrentThread();
MOZ_ASSERT(mEventTarget);
}
}
BlobChild::BlobChild(nsIContentChild* aManager,
const nsID& aParentID,
RemoteBlobSliceImpl* aRemoteBlobSliceImpl)
: mBackgroundManager(nullptr)
, mContentManager(aManager)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aManager);
CommonInit(aParentID, aRemoteBlobSliceImpl);
}
BlobChild::BlobChild(PBackgroundChild* aManager,
const nsID& aParentID,
RemoteBlobSliceImpl* aRemoteBlobSliceImpl)
: mBackgroundManager(aManager)
, mContentManager(nullptr)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aManager);
CommonInit(aParentID, aRemoteBlobSliceImpl);
if (!NS_IsMainThread()) {
mEventTarget = do_GetCurrentThread();
MOZ_ASSERT(mEventTarget);
}
}
BlobChild::~BlobChild()
{
AssertIsOnOwningThread();
MOZ_COUNT_DTOR(BlobChild);
}
void
BlobChild::CommonInit(FileImpl* aBlobImpl)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aBlobImpl);
MOZ_COUNT_CTOR(BlobChild);
mBlobImpl = aBlobImpl;
mRemoteBlobImpl = nullptr;
mBlobImpl->AddRef();
mOwnsBlobImpl = true;
memset(&mParentID, 0, sizeof(mParentID));
}
void
BlobChild::CommonInit(BlobChild* aOther)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aOther);
MOZ_ASSERT_IF(mContentManager, aOther->GetBackgroundManager());
MOZ_ASSERT_IF(mBackgroundManager, aOther->GetContentManager());
nsRefPtr<FileImpl> otherImpl = aOther->GetBlobImpl();
MOZ_ASSERT(otherImpl);
nsString contentType;
otherImpl->GetType(contentType);
ErrorResult rv;
uint64_t length = otherImpl->GetSize(rv);
MOZ_ASSERT(!rv.Failed());
nsRefPtr<RemoteBlobImpl> remoteBlob;
if (otherImpl->IsFile()) {
nsString name;
otherImpl->GetName(name);
uint64_t modDate = otherImpl->GetLastModified(rv);
MOZ_ASSERT(!rv.Failed());
remoteBlob = new RemoteBlobImpl(this, name, contentType, length, modDate);
} else {
remoteBlob = new RemoteBlobImpl(this, contentType, length);
}
CommonInit(aOther->ParentID(), remoteBlob);
}
void
BlobChild::CommonInit(const ChildBlobConstructorParams& aParams)
{
AssertIsOnOwningThread();
MOZ_COUNT_CTOR(BlobChild);
const AnyBlobConstructorParams& blobParams = aParams.blobParams();
AnyBlobConstructorParams::Type paramsType = blobParams.type();
MOZ_ASSERT(paramsType != AnyBlobConstructorParams::T__None &&
paramsType !=
AnyBlobConstructorParams::TSlicedBlobConstructorParams &&
paramsType !=
AnyBlobConstructorParams::TKnownBlobConstructorParams);
nsRefPtr<RemoteBlobImpl> remoteBlob;
switch (paramsType) {
case AnyBlobConstructorParams::TNormalBlobConstructorParams: {
const NormalBlobConstructorParams& params =
blobParams.get_NormalBlobConstructorParams();
remoteBlob =
new RemoteBlobImpl(this, params.contentType(), params.length());
break;
}
case AnyBlobConstructorParams::TFileBlobConstructorParams: {
const FileBlobConstructorParams& params =
blobParams.get_FileBlobConstructorParams();
remoteBlob = new RemoteBlobImpl(this,
params.name(),
params.contentType(),
params.length(),
params.modDate());
break;
}
case AnyBlobConstructorParams::TSameProcessBlobConstructorParams: {
MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
const SameProcessBlobConstructorParams& params =
blobParams.get_SameProcessBlobConstructorParams();
MOZ_ASSERT(params.addRefedFileImpl());
nsRefPtr<FileImpl> blobImpl =
dont_AddRef(reinterpret_cast<FileImpl*>(params.addRefedFileImpl()));
ErrorResult rv;
uint64_t size = blobImpl->GetSize(rv);
MOZ_ASSERT(!rv.Failed());
nsString contentType;
blobImpl->GetType(contentType);
if (blobImpl->IsFile()) {
nsString name;
blobImpl->GetName(name);
uint64_t lastModifiedDate = blobImpl->GetLastModified(rv);
MOZ_ASSERT(!rv.Failed());
remoteBlob =
new RemoteBlobImpl(this,
blobImpl,
name,
contentType,
size,
lastModifiedDate);
} else {
remoteBlob = new RemoteBlobImpl(this, blobImpl, contentType, size);
}
break;
}
case AnyBlobConstructorParams::TMysteryBlobConstructorParams: {
remoteBlob = new RemoteBlobImpl(this);
break;
}
default:
MOZ_CRASH("Unknown params!");
}
MOZ_ASSERT(remoteBlob);
DebugOnly<bool> isMutable;
MOZ_ASSERT(NS_SUCCEEDED(remoteBlob->GetMutable(&isMutable)));
MOZ_ASSERT(!isMutable);
mRemoteBlobImpl = remoteBlob;
remoteBlob.forget(&mBlobImpl);
mOwnsBlobImpl = true;
mParentID = aParams.id();
}
void
BlobChild::CommonInit(const nsID& aParentID, RemoteBlobImpl* aRemoteBlobImpl)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aRemoteBlobImpl);
DebugOnly<bool> isMutable;
MOZ_ASSERT(NS_SUCCEEDED(aRemoteBlobImpl->GetMutable(&isMutable)));
MOZ_ASSERT(!isMutable);
MOZ_COUNT_CTOR(BlobChild);
nsRefPtr<RemoteBlobImpl> remoteBlob = aRemoteBlobImpl;
mRemoteBlobImpl = remoteBlob;
remoteBlob.forget(&mBlobImpl);
mOwnsBlobImpl = true;
mParentID = aParentID;
}
#ifdef DEBUG
void
BlobChild::AssertIsOnOwningThread() const
{
MOZ_ASSERT(IsOnOwningThread());
}
#endif // DEBUG
// static
void
BlobChild::Startup(const FriendKey& /* aKey */)
{
MOZ_ASSERT(XRE_GetProcessType() != GeckoProcessType_Default);
CommonStartup();
}
// static
BlobChild*
BlobChild::GetOrCreate(nsIContentChild* aManager, FileImpl* aBlobImpl)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aManager);
return GetOrCreateFromImpl(aManager, aBlobImpl);
}
// static
BlobChild*
BlobChild::GetOrCreate(PBackgroundChild* aManager, FileImpl* aBlobImpl)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aManager);
return GetOrCreateFromImpl(aManager, aBlobImpl);
}
// 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::GetOrCreateFromImpl(ChildManagerType* aManager,
FileImpl* aBlobImpl)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aManager);
MOZ_ASSERT(aBlobImpl);
// If the blob represents a remote blob then we can simply pass its actor back
// here.
if (nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryInterface(aBlobImpl)) {
BlobChild* actor = MaybeGetActorFromRemoteBlob(remoteBlob, aManager);
if (actor) {
return actor;
}
}
// All blobs shared between threads or processes must be immutable.
if (NS_WARN_IF(NS_FAILED(aBlobImpl->SetMutable(false)))) {
return nullptr;
}
MOZ_ASSERT(!aBlobImpl->IsSizeUnknown());
MOZ_ASSERT(!aBlobImpl->IsDateUnknown());
AnyBlobConstructorParams blobParams;
nsCOMPtr<nsIInputStream> snapshotInputStream;
if (gProcessType == GeckoProcessType_Default) {
nsCOMPtr<PIFileImplSnapshot> snapshot = do_QueryInterface(aBlobImpl);
if (snapshot) {
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
aBlobImpl->GetInternalStream(getter_AddRefs(snapshotInputStream))));
}
}
if (gProcessType == GeckoProcessType_Default && !snapshotInputStream) {
nsRefPtr<FileImpl> sameProcessImpl = aBlobImpl;
auto addRefedFileImpl =
reinterpret_cast<intptr_t>(sameProcessImpl.forget().take());
blobParams = SameProcessBlobConstructorParams(addRefedFileImpl);
} else {
BlobData blobData;
if (snapshotInputStream) {
blobData =
reinterpret_cast<intptr_t>(snapshotInputStream.forget().take());
} else {
BlobDataFromBlobImpl(aBlobImpl, blobData);
}
nsString contentType;
aBlobImpl->GetType(contentType);
ErrorResult rv;
uint64_t length = aBlobImpl->GetSize(rv);
MOZ_ASSERT(!rv.Failed());
if (aBlobImpl->IsFile()) {
nsString name;
aBlobImpl->GetName(name);
uint64_t modDate = aBlobImpl->GetLastModified(rv);
MOZ_ASSERT(!rv.Failed());
blobParams =
FileBlobConstructorParams(name, contentType, length, modDate, blobData);
} else {
blobParams = NormalBlobConstructorParams(contentType, length, blobData);
}
}
BlobChild* actor = new BlobChild(aManager, aBlobImpl);
ParentBlobConstructorParams params(blobParams);
if (NS_WARN_IF(!aManager->SendPBlobConstructor(actor, params))) {
BlobChild::Destroy(actor);
return nullptr;
}
return actor;
}
// static
template <class ChildManagerType>
BlobChild*
BlobChild::CreateFromParams(ChildManagerType* aManager,
const ChildBlobConstructorParams& aParams)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aManager);
const AnyBlobConstructorParams& blobParams = aParams.blobParams();
switch (blobParams.type()) {
case AnyBlobConstructorParams::TNormalBlobConstructorParams:
case AnyBlobConstructorParams::TFileBlobConstructorParams:
case AnyBlobConstructorParams::TSameProcessBlobConstructorParams:
case AnyBlobConstructorParams::TMysteryBlobConstructorParams: {
return new BlobChild(aManager, aParams);
}
case AnyBlobConstructorParams::TSlicedBlobConstructorParams: {
MOZ_CRASH("Parent should never send SlicedBlobConstructorParams!");
}
case AnyBlobConstructorParams::TKnownBlobConstructorParams: {
MOZ_CRASH("Parent should never send KnownBlobConstructorParams!");
}
default:
MOZ_CRASH("Unknown params!");
}
MOZ_CRASH("Should never get here!");
}
// static
template <class ChildManagerType>
BlobChild*
BlobChild::SendSliceConstructor(ChildManagerType* aManager,
RemoteBlobSliceImpl* aRemoteBlobSliceImpl,
const ParentBlobConstructorParams& aParams)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aManager);
MOZ_ASSERT(aRemoteBlobSliceImpl);
MOZ_ASSERT(aParams.blobParams().type() ==
AnyBlobConstructorParams::TSlicedBlobConstructorParams);
const nsID& id = aParams.blobParams().get_SlicedBlobConstructorParams().id();
BlobChild* newActor = new BlobChild(aManager, id, aRemoteBlobSliceImpl);
if (aManager->SendPBlobConstructor(newActor, aParams)) {
if (gProcessType != GeckoProcessType_Default || !NS_IsMainThread()) {
newActor->SendWaitForSliceCreation();
}
return newActor;
}
BlobChild::Destroy(newActor);
return nullptr;
}
// static
BlobChild*
BlobChild::MaybeGetActorFromRemoteBlob(nsIRemoteBlob* aRemoteBlob,
nsIContentChild* aManager)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aRemoteBlob);
MOZ_ASSERT(aManager);
if (BlobChild* actor = aRemoteBlob->GetBlobChild()) {
if (actor->GetContentManager() == aManager) {
return actor;
}
MOZ_ASSERT(actor->GetBackgroundManager());
actor = new BlobChild(aManager, actor);
ParentBlobConstructorParams params(
KnownBlobConstructorParams(actor->ParentID()));
aManager->SendPBlobConstructor(actor, params);
return actor;
}
return nullptr;
}
// static
BlobChild*
BlobChild::MaybeGetActorFromRemoteBlob(nsIRemoteBlob* aRemoteBlob,
PBackgroundChild* aManager)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aRemoteBlob);
MOZ_ASSERT(aManager);
if (BlobChild* actor = aRemoteBlob->GetBlobChild()) {
if (actor->GetBackgroundManager() == aManager) {
return actor;
}
MOZ_ASSERT(actor->GetContentManager());
actor = new BlobChild(aManager, actor);
ParentBlobConstructorParams params(
KnownBlobConstructorParams(actor->ParentID()));
aManager->SendPBlobConstructor(actor, params);
return actor;
}
return nullptr;
}
const nsID&
BlobChild::ParentID() const
{
AssertIsOnOwningThread();
MOZ_ASSERT(mRemoteBlobImpl);
return mParentID;
}
already_AddRefed<FileImpl>
BlobChild::GetBlobImpl()
{
AssertIsOnOwningThread();
MOZ_ASSERT(mBlobImpl);
nsRefPtr<FileImpl> blobImpl;
// Remote blobs are held alive until the first call to GetBlobImpl. Thereafter
// we only hold a weak reference. Normal blobs are held alive until the actor
// is destroyed.
if (mRemoteBlobImpl && mOwnsBlobImpl) {
blobImpl = dont_AddRef(mBlobImpl);
mOwnsBlobImpl = false;
} else {
blobImpl = mBlobImpl;
}
MOZ_ASSERT(blobImpl);
return blobImpl.forget();
}
bool
BlobChild::SetMysteryBlobInfo(const nsString& aName,
const nsString& aContentType,
uint64_t aLength,
uint64_t aLastModifiedDate)
{
AssertIsOnOwningThread();
MOZ_ASSERT(mBlobImpl);
MOZ_ASSERT(mRemoteBlobImpl);
MOZ_ASSERT(aLastModifiedDate != UINT64_MAX);
mBlobImpl->SetLazyData(aName, aContentType, aLength, aLastModifiedDate);
FileBlobConstructorParams params(aName,
aContentType,
aLength,
aLastModifiedDate,
void_t() /* optionalBlobData */);
return SendResolveMystery(params);
}
bool
BlobChild::SetMysteryBlobInfo(const nsString& aContentType, uint64_t aLength)
{
AssertIsOnOwningThread();
MOZ_ASSERT(mBlobImpl);
MOZ_ASSERT(mRemoteBlobImpl);
nsString voidString;
voidString.SetIsVoid(true);
mBlobImpl->SetLazyData(voidString, aContentType, aLength, UINT64_MAX);
NormalBlobConstructorParams params(aContentType,
aLength,
void_t() /* optionalBlobData */);
return SendResolveMystery(params);
}
void
BlobChild::NoteDyingRemoteBlobImpl()
{
MOZ_ASSERT(mBlobImpl);
MOZ_ASSERT(mRemoteBlobImpl);
MOZ_ASSERT(!mOwnsBlobImpl);
// This may be called on any thread due to the fact that RemoteBlobImpl 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::NoteDyingRemoteBlobImpl);
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.
mBlobImpl = nullptr;
mRemoteBlobImpl = nullptr;
PBlobChild::Send__delete__(this);
}
bool
BlobChild::IsOnOwningThread() const
{
return EventTargetIsOnCurrentThread(mEventTarget);
}
void
BlobChild::ActorDestroy(ActorDestroyReason aWhy)
{
AssertIsOnOwningThread();
if (mRemoteBlobImpl) {
mRemoteBlobImpl->NoteDyingActor();
}
if (mBlobImpl && mOwnsBlobImpl) {
mBlobImpl->Release();
}
#ifdef DEBUG
mBlobImpl = nullptr;
mRemoteBlobImpl = nullptr;
mBackgroundManager = nullptr;
mContentManager = nullptr;
mOwnsBlobImpl = false;
#endif
}
PBlobStreamChild*
BlobChild::AllocPBlobStreamChild(const uint64_t& aStart,
const uint64_t& aLength)
{
AssertIsOnOwningThread();
return new InputStreamChild();
}
bool
BlobChild::DeallocPBlobStreamChild(PBlobStreamChild* aActor)
{
AssertIsOnOwningThread();
delete static_cast<InputStreamChild*>(aActor);
return true;
}
/*******************************************************************************
* BlobParent
******************************************************************************/
BlobParent::BlobParent(nsIContentParent* aManager, IDTableEntry* aIDTableEntry)
: mBackgroundManager(nullptr)
, mContentManager(aManager)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aManager);
CommonInit(aIDTableEntry);
}
BlobParent::BlobParent(PBackgroundParent* aManager, IDTableEntry* aIDTableEntry)
: mBackgroundManager(aManager)
, mContentManager(nullptr)
, mEventTarget(do_GetCurrentThread())
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aManager);
MOZ_ASSERT(mEventTarget);
CommonInit(aIDTableEntry);
}
BlobParent::BlobParent(nsIContentParent* aManager,
FileImpl* aBlobImpl,
IDTableEntry* aIDTableEntry)
: mBackgroundManager(nullptr)
, mContentManager(aManager)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aManager);
CommonInit(aBlobImpl, aIDTableEntry);
}
BlobParent::BlobParent(PBackgroundParent* aManager,
FileImpl* aBlobImpl,
IDTableEntry* aIDTableEntry)
: mBackgroundManager(aManager)
, mContentManager(nullptr)
, mEventTarget(do_GetCurrentThread())
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aManager);
MOZ_ASSERT(mEventTarget);
CommonInit(aBlobImpl, aIDTableEntry);
}
BlobParent::~BlobParent()
{
AssertIsOnOwningThread();
MOZ_COUNT_DTOR(BlobParent);
}
void
BlobParent::CommonInit(IDTableEntry* aIDTableEntry)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aIDTableEntry);
MOZ_ASSERT(aIDTableEntry->BlobImpl());
MOZ_COUNT_CTOR(BlobParent);
mBlobImpl = aIDTableEntry->BlobImpl();
mRemoteBlobImpl = nullptr;
mBlobImpl->AddRef();
mOwnsBlobImpl = true;
mIDTableEntry = aIDTableEntry;
}
void
BlobParent::CommonInit(FileImpl* aBlobImpl, IDTableEntry* aIDTableEntry)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aBlobImpl);
MOZ_ASSERT(aIDTableEntry);
MOZ_COUNT_CTOR(BlobParent);
DebugOnly<bool> isMutable;
MOZ_ASSERT(NS_SUCCEEDED(aBlobImpl->GetMutable(&isMutable)));
MOZ_ASSERT(!isMutable);
nsRefPtr<RemoteBlobImpl> remoteBlobImpl = new RemoteBlobImpl(this, aBlobImpl);
MOZ_ASSERT(NS_SUCCEEDED(remoteBlobImpl->GetMutable(&isMutable)));
MOZ_ASSERT(!isMutable);
mRemoteBlobImpl = remoteBlobImpl;
remoteBlobImpl.forget(&mBlobImpl);
mOwnsBlobImpl = true;
mIDTableEntry = aIDTableEntry;
}
#ifdef DEBUG
void
BlobParent::AssertIsOnOwningThread() const
{
MOZ_ASSERT(IsOnOwningThread());
}
#endif // DEBUG
// static
void
BlobParent::Startup(const FriendKey& /* aKey */)
{
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
CommonStartup();
ClearOnShutdown(&sIDTable);
sIDTableMutex = new Mutex("BlobParent::sIDTableMutex");
ClearOnShutdown(&sIDTableMutex);
}
// static
BlobParent*
BlobParent::GetOrCreate(nsIContentParent* aManager, FileImpl* aBlobImpl)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aManager);
return GetOrCreateFromImpl(aManager, aBlobImpl);
}
// static
BlobParent*
BlobParent::GetOrCreate(PBackgroundParent* aManager, FileImpl* aBlobImpl)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aManager);
return GetOrCreateFromImpl(aManager, aBlobImpl);
}
// 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
already_AddRefed<FileImpl>
BlobParent::GetBlobImplForID(const nsID& aID)
{
if (NS_WARN_IF(gProcessType != GeckoProcessType_Default)) {
ASSERT_UNLESS_FUZZING();
return nullptr;
}
nsRefPtr<IDTableEntry> idTableEntry = IDTableEntry::Get(aID);
if (NS_WARN_IF(!idTableEntry)) {
return nullptr;
}
nsRefPtr<FileImpl> blobImpl = idTableEntry->BlobImpl();
MOZ_ASSERT(blobImpl);
return blobImpl.forget();
}
// static
template <class ParentManagerType>
BlobParent*
BlobParent::GetOrCreateFromImpl(ParentManagerType* aManager,
FileImpl* aBlobImpl)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aManager);
MOZ_ASSERT(aBlobImpl);
// If the blob represents a remote blob for this manager then we can simply
// pass its actor back here.
if (nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryInterface(aBlobImpl)) {
BlobParent* actor = MaybeGetActorFromRemoteBlob(remoteBlob, aManager);
if (actor) {
return actor;
}
}
// All blobs shared between threads or processes must be immutable.
if (NS_WARN_IF(NS_FAILED(aBlobImpl->SetMutable(false)))) {
return nullptr;
}
const bool isSameProcessActor = ActorManagerIsSameProcess(aManager);
AnyBlobConstructorParams blobParams;
bool isSnapshot;
if (isSameProcessActor) {
nsCOMPtr<PIFileImplSnapshot> snapshot = do_QueryInterface(aBlobImpl);
isSnapshot = !!snapshot;
} else {
isSnapshot = false;
}
if (isSameProcessActor && !isSnapshot) {
nsRefPtr<FileImpl> sameProcessImpl = aBlobImpl;
auto addRefedFileImpl =
reinterpret_cast<intptr_t>(sameProcessImpl.forget().take());
blobParams = SameProcessBlobConstructorParams(addRefedFileImpl);
} else {
if (aBlobImpl->IsSizeUnknown() || aBlobImpl->IsDateUnknown()) {
// We don't want to call GetSize or GetLastModifiedDate yet since that may
// stat a file on the this thread. Instead we'll learn the size lazily
// from the other side.
blobParams = MysteryBlobConstructorParams();
} else {
nsString contentType;
aBlobImpl->GetType(contentType);
ErrorResult rv;
uint64_t length = aBlobImpl->GetSize(rv);
MOZ_ASSERT(!rv.Failed());
if (aBlobImpl->IsFile()) {
nsString name;
aBlobImpl->GetName(name);
uint64_t modDate = aBlobImpl->GetLastModified(rv);
MOZ_ASSERT(!rv.Failed());
blobParams =
FileBlobConstructorParams(name, contentType, length, modDate, void_t());
} else {
blobParams = NormalBlobConstructorParams(contentType, length, void_t());
}
}
}
nsID id;
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(gUUIDGenerator->GenerateUUIDInPlace(&id)));
nsRefPtr<IDTableEntry> idTableEntry =
IDTableEntry::GetOrCreate(id, ActorManagerProcessID(aManager), aBlobImpl);
MOZ_ASSERT(idTableEntry);
BlobParent* actor = new BlobParent(aManager, idTableEntry);
ChildBlobConstructorParams params(id, blobParams);
if (NS_WARN_IF(!aManager->SendPBlobConstructor(actor, params))) {
BlobParent::Destroy(actor);
return nullptr;
}
return actor;
}
// static
template <class ParentManagerType>
BlobParent*
BlobParent::CreateFromParams(ParentManagerType* aManager,
const ParentBlobConstructorParams& aParams)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aManager);
const AnyBlobConstructorParams& blobParams = aParams.blobParams();
switch (blobParams.type()) {
case AnyBlobConstructorParams::TMysteryBlobConstructorParams: {
ASSERT_UNLESS_FUZZING();
return nullptr;
}
case AnyBlobConstructorParams::TNormalBlobConstructorParams:
case AnyBlobConstructorParams::TFileBlobConstructorParams: {
const OptionalBlobData& optionalBlobData =
blobParams.type() ==
AnyBlobConstructorParams::TNormalBlobConstructorParams ?
blobParams.get_NormalBlobConstructorParams().optionalBlobData() :
blobParams.get_FileBlobConstructorParams().optionalBlobData();
if (NS_WARN_IF(optionalBlobData.type() != OptionalBlobData::TBlobData)) {
ASSERT_UNLESS_FUZZING();
return nullptr;
}
nsRefPtr<FileImpl> blobImpl =
CreateBlobImpl(aParams,
optionalBlobData.get_BlobData(),
ActorManagerIsSameProcess(aManager));
if (NS_WARN_IF(!blobImpl)) {
ASSERT_UNLESS_FUZZING();
return nullptr;
}
nsID id;
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(gUUIDGenerator->GenerateUUIDInPlace(&id)));
nsRefPtr<IDTableEntry> idTableEntry =
IDTableEntry::Create(id, ActorManagerProcessID(aManager), blobImpl);
if (NS_WARN_IF(!idTableEntry)) {
ASSERT_UNLESS_FUZZING();
return nullptr;
}
return new BlobParent(aManager, blobImpl, idTableEntry);
}
case AnyBlobConstructorParams::TSlicedBlobConstructorParams: {
const SlicedBlobConstructorParams& params =
blobParams.get_SlicedBlobConstructorParams();
if (NS_WARN_IF(params.end() < params.begin())) {
ASSERT_UNLESS_FUZZING();
return nullptr;
}
auto* actor =
const_cast<BlobParent*>(
static_cast<const BlobParent*>(params.sourceParent()));
MOZ_ASSERT(actor);
nsRefPtr<FileImpl> source = actor->GetBlobImpl();
MOZ_ASSERT(source);
ErrorResult rv;
nsRefPtr<FileImpl> slice =
source->CreateSlice(params.begin(),
params.end() - params.begin(),
params.contentType(),
rv);
if (NS_WARN_IF(rv.Failed())) {
ASSERT_UNLESS_FUZZING();
return nullptr;
}
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(slice->SetMutable(false)));
nsRefPtr<IDTableEntry> idTableEntry =
IDTableEntry::Create(params.id(),
ActorManagerProcessID(aManager),
slice);
if (NS_WARN_IF(!idTableEntry)) {
ASSERT_UNLESS_FUZZING();
return nullptr;
}
return new BlobParent(aManager, slice, idTableEntry);
}
case AnyBlobConstructorParams::TKnownBlobConstructorParams: {
const KnownBlobConstructorParams& params =
blobParams.get_KnownBlobConstructorParams();
nsRefPtr<IDTableEntry> idTableEntry =
IDTableEntry::Get(params.id(), ActorManagerProcessID(aManager));
if (NS_WARN_IF(!idTableEntry)) {
ASSERT_UNLESS_FUZZING();
return nullptr;
}
nsRefPtr<FileImpl> blobImpl = idTableEntry->BlobImpl();
MOZ_ASSERT(blobImpl);
return new BlobParent(aManager, blobImpl, idTableEntry);
}
case AnyBlobConstructorParams::TSameProcessBlobConstructorParams: {
if (NS_WARN_IF(!ActorManagerIsSameProcess(aManager))) {
ASSERT_UNLESS_FUZZING();
return nullptr;
}
const SameProcessBlobConstructorParams& params =
blobParams.get_SameProcessBlobConstructorParams();
nsRefPtr<FileImpl> blobImpl =
dont_AddRef(reinterpret_cast<FileImpl*>(params.addRefedFileImpl()));
MOZ_ASSERT(blobImpl);
nsID id;
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(gUUIDGenerator->GenerateUUIDInPlace(&id)));
nsRefPtr<IDTableEntry> idTableEntry =
IDTableEntry::Create(id, ActorManagerProcessID(aManager), blobImpl);
MOZ_ASSERT(idTableEntry);
return new BlobParent(aManager, blobImpl, idTableEntry);
}
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;
}
BlobParent::Destroy(newActor);
return nullptr;
}
// static
BlobParent*
BlobParent::MaybeGetActorFromRemoteBlob(nsIRemoteBlob* aRemoteBlob,
nsIContentParent* aManager)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aRemoteBlob);
MOZ_ASSERT(aManager);
BlobParent* actor = aRemoteBlob->GetBlobParent();
if (actor && actor->GetContentManager() == aManager) {
return actor;
}
return nullptr;
}
// static
BlobParent*
BlobParent::MaybeGetActorFromRemoteBlob(nsIRemoteBlob* aRemoteBlob,
PBackgroundParent* aManager)
{
AssertCorrectThreadForManager(aManager);
MOZ_ASSERT(aRemoteBlob);
MOZ_ASSERT(aManager);
BlobParent* actor = aRemoteBlob->GetBlobParent();
if (actor && actor->GetBackgroundManager() == aManager) {
return actor;
}
return nullptr;
}
already_AddRefed<FileImpl>
BlobParent::GetBlobImpl()
{
AssertIsOnOwningThread();
MOZ_ASSERT(mBlobImpl);
nsRefPtr<FileImpl> blobImpl;
// Remote blobs are held alive until the first call to GetBlobImpl. Thereafter
// we only hold a weak reference. Normal blobs are held alive until the actor
// is destroyed.
if (mRemoteBlobImpl && mOwnsBlobImpl) {
blobImpl = dont_AddRef(mBlobImpl);
mOwnsBlobImpl = false;
} else {
blobImpl = mBlobImpl;
}
MOZ_ASSERT(blobImpl);
return blobImpl.forget();
}
void
BlobParent::NoteDyingRemoteBlobImpl()
{
MOZ_ASSERT(mRemoteBlobImpl);
MOZ_ASSERT(!mOwnsBlobImpl);
// This may be called on any thread due to the fact that RemoteBlobImpl 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::NoteDyingRemoteBlobImpl);
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.
mBlobImpl = nullptr;
mRemoteBlobImpl = nullptr;
unused << PBlobParent::Send__delete__(this);
}
void
BlobParent::NoteRunnableCompleted(OpenStreamRunnable* aRunnable)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aRunnable);
for (uint32_t count = mOpenStreamRunnables.Length(), index = 0;
index < count;
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 (mRemoteBlobImpl) {
mRemoteBlobImpl->NoteDyingActor();
}
if (mBlobImpl && mOwnsBlobImpl) {
mBlobImpl->Release();
}
#ifdef DEBUG
mBlobImpl = nullptr;
mRemoteBlobImpl = nullptr;
mBackgroundManager = nullptr;
mContentManager = nullptr;
mOwnsBlobImpl = false;
#endif
}
PBlobStreamParent*
BlobParent::AllocPBlobStreamParent(const uint64_t& aStart,
const uint64_t& aLength)
{
AssertIsOnOwningThread();
if (NS_WARN_IF(mRemoteBlobImpl)) {
ASSERT_UNLESS_FUZZING();
return nullptr;
}
return new InputStreamParent();
}
bool
BlobParent::RecvPBlobStreamConstructor(PBlobStreamParent* aActor,
const uint64_t& aStart,
const uint64_t& aLength)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aActor);
MOZ_ASSERT(mBlobImpl);
MOZ_ASSERT(!mRemoteBlobImpl);
MOZ_ASSERT(mOwnsBlobImpl);
// Make sure we can't overflow.
if (NS_WARN_IF(UINT64_MAX - aLength < aStart)) {
ASSERT_UNLESS_FUZZING();
return false;
}
ErrorResult errorResult;
uint64_t blobLength = mBlobImpl->GetSize(errorResult);
MOZ_ASSERT(!errorResult.Failed());
if (NS_WARN_IF(aStart + aLength > blobLength)) {
ASSERT_UNLESS_FUZZING();
return false;
}
nsRefPtr<FileImpl> blobImpl;
if (!aStart && aLength == blobLength) {
blobImpl = mBlobImpl;
} else {
nsString type;
mBlobImpl->GetType(type);
blobImpl = mBlobImpl->CreateSlice(aStart, aLength, type, errorResult);
if (NS_WARN_IF(errorResult.Failed())) {
return false;
}
}
nsCOMPtr<nsIInputStream> stream;
errorResult = blobImpl->GetInternalStream(getter_AddRefs(stream));
if (NS_WARN_IF(errorResult.Failed())) {
return false;
}
// If the stream is entirely backed by memory then we can serialize and send
// it immediately.
if (mBlobImpl->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(mBlobImpl);
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;
errorResult = NS_NewNamedThread("Blob Opener", getter_AddRefs(target));
if (NS_WARN_IF(errorResult.Failed())) {
return false;
}
nsRefPtr<OpenStreamRunnable> runnable =
new OpenStreamRunnable(this, aActor, stream, serializableStream, target);
errorResult = runnable->Dispatch();
if (NS_WARN_IF(errorResult.Failed())) {
return 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(mBlobImpl);
MOZ_ASSERT(!mRemoteBlobImpl);
MOZ_ASSERT(mOwnsBlobImpl);
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);
mBlobImpl->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;
}
mBlobImpl->SetLazyData(params.name(),
params.contentType(),
params.length(),
params.modDate());
return true;
}
default:
MOZ_CRASH("Unknown params!");
}
MOZ_CRASH("Should never get here!");
}
bool
BlobParent::RecvWaitForSliceCreation()
{
AssertIsOnOwningThread();
MOZ_ASSERT(mBlobImpl);
MOZ_ASSERT(mOwnsBlobImpl);
// The whole point of this message is to ensure that the sliced blob created
// by the child has been inserted into our IDTable.
MOZ_ASSERT(mIDTableEntry);
#ifdef DEBUG
{
MOZ_ASSERT(sIDTableMutex);
MutexAutoLock lock(*sIDTableMutex);
MOZ_ASSERT(sIDTable);
MOZ_ASSERT(sIDTable->Contains(mIDTableEntry->ID()));
}
#endif
return true;
}
bool
BlobParent::RecvGetFileId(int64_t* aFileId)
{
AssertIsOnOwningThread();
MOZ_ASSERT(mBlobImpl);
MOZ_ASSERT(!mRemoteBlobImpl);
MOZ_ASSERT(mOwnsBlobImpl);
if (NS_WARN_IF(!IndexedDatabaseManager::InTestingMode())) {
ASSERT_UNLESS_FUZZING();
return false;
}
*aFileId = mBlobImpl->GetFileId();
return true;
}
bool
BlobParent::RecvGetFilePath(nsString* aFilePath)
{
AssertIsOnOwningThread();
MOZ_ASSERT(mBlobImpl);
MOZ_ASSERT(!mRemoteBlobImpl);
MOZ_ASSERT(mOwnsBlobImpl);
// 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
nsString filePath;
ErrorResult rv;
mBlobImpl->GetMozFullPathInternal(filePath, rv);
if (NS_WARN_IF(rv.Failed())) {
return false;
}
*aFilePath = filePath;
return true;
}
/*******************************************************************************
* BlobParent::IDTableEntry
******************************************************************************/
BlobParent::
IDTableEntry::IDTableEntry(const nsID& aID,
intptr_t aProcessID,
FileImpl* aBlobImpl)
: mID(aID)
, mProcessID(aProcessID)
, mBlobImpl(aBlobImpl)
{
MOZ_ASSERT(aBlobImpl);
}
BlobParent::
IDTableEntry::~IDTableEntry()
{
MOZ_ASSERT(sIDTableMutex);
sIDTableMutex->AssertNotCurrentThreadOwns();
MOZ_ASSERT(sIDTable);
{
MutexAutoLock lock(*sIDTableMutex);
MOZ_ASSERT(sIDTable->Get(mID) == this);
sIDTable->Remove(mID);
if (!sIDTable->Count()) {
sIDTable = nullptr;
}
}
}
// static
already_AddRefed<BlobParent::IDTableEntry>
BlobParent::
IDTableEntry::GetOrCreateInternal(const nsID& aID,
intptr_t aProcessID,
FileImpl* aBlobImpl,
bool aMayCreate,
bool aMayGet,
bool aIgnoreProcessID)
{
MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
MOZ_ASSERT(sIDTableMutex);
sIDTableMutex->AssertNotCurrentThreadOwns();
nsRefPtr<IDTableEntry> entry;
{
MutexAutoLock lock(*sIDTableMutex);
if (!sIDTable) {
if (NS_WARN_IF(!aMayCreate)) {
return nullptr;
}
sIDTable = new IDTable();
}
entry = sIDTable->Get(aID);
if (entry) {
MOZ_ASSERT_IF(aBlobImpl, entry->BlobImpl() == aBlobImpl);
if (NS_WARN_IF(!aMayGet)) {
return nullptr;
}
if (!aIgnoreProcessID && NS_WARN_IF(entry->mProcessID != aProcessID)) {
return nullptr;
}
} else {
if (NS_WARN_IF(!aMayCreate)) {
return nullptr;
}
MOZ_ASSERT(aBlobImpl);
entry = new IDTableEntry(aID, aProcessID, aBlobImpl);
sIDTable->Put(aID, entry);
}
}
MOZ_ASSERT(entry);
return entry.forget();
}
/*******************************************************************************
* Other stuff
******************************************************************************/
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;
}
} // namespace dom
} // namespace mozilla