summaryrefslogtreecommitdiffstats
path: root/dom/ipc/Blob.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/ipc/Blob.cpp')
-rw-r--r--dom/ipc/Blob.cpp4793
1 files changed, 4793 insertions, 0 deletions
diff --git a/dom/ipc/Blob.cpp b/dom/ipc/Blob.cpp
new file mode 100644
index 000000000..95d2e0513
--- /dev/null
+++ b/dom/ipc/Blob.cpp
@@ -0,0 +1,4793 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/IndexedDatabaseManager.h"
+#include "mozilla/ipc/InputStreamUtils.h"
+#include "mozilla/ipc/IPCStreamUtils.h"
+#include "mozilla/ipc/PBackgroundChild.h"
+#include "mozilla/ipc/PBackgroundParent.h"
+#include "mozilla/ipc/PFileDescriptorSetParent.h"
+#include "MultipartBlobImpl.h"
+#include "nsDataHashtable.h"
+#include "nsHashKeys.h"
+#include "nsID.h"
+#include "nsIFileStreams.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"
+#include "WorkerPrivate.h"
+#include "WorkerRunnable.h"
+
+#ifdef DEBUG
+#include "BackgroundChild.h" // BackgroundChild::GetForCurrentThread().
+#endif
+
+#ifdef OS_POSIX
+#include "chrome/common/file_descriptor_set_posix.h"
+#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;
+using namespace mozilla::dom::workers;
+
+namespace {
+
+const char kUUIDGeneratorContractId[] = "@mozilla.org/uuid-generator;1";
+
+const uint32_t kMaxFileDescriptorsPerMessage = 250;
+
+#ifdef OS_POSIX
+// Keep this in sync with other platforms.
+static_assert(FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE == 250,
+ "MAX_DESCRIPTORS_PER_MESSAGE mismatch!");
+#endif
+
+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;
+
+ // If this fails, we are probably shutting down.
+ if (NS_WARN_IF(NS_FAILED(aEventTarget->IsOnCurrentThread(&current)))) {
+ return true;
+ }
+
+ return current;
+}
+
+class CancelableRunnableWrapper final
+ : public CancelableRunnable
+{
+ nsCOMPtr<nsIRunnable> mRunnable;
+#ifdef DEBUG
+ nsCOMPtr<nsIEventTarget> mDEBUGEventTarget;
+#endif
+
+public:
+ CancelableRunnableWrapper(nsIRunnable* aRunnable,
+ nsIEventTarget* aEventTarget)
+ : mRunnable(aRunnable)
+#ifdef DEBUG
+ , mDEBUGEventTarget(aEventTarget)
+#endif
+ {
+ MOZ_ASSERT(aRunnable);
+ MOZ_ASSERT(aEventTarget);
+ }
+
+ NS_DECL_ISUPPORTS_INHERITED
+
+private:
+ ~CancelableRunnableWrapper()
+ { }
+
+ NS_DECL_NSIRUNNABLE
+ nsresult Cancel() override;
+};
+
+NS_IMPL_ISUPPORTS_INHERITED0(CancelableRunnableWrapper, CancelableRunnable)
+
+NS_IMETHODIMP
+CancelableRunnableWrapper::Run()
+{
+ DebugOnly<bool> onTarget;
+ MOZ_ASSERT(mDEBUGEventTarget);
+ MOZ_ASSERT(NS_SUCCEEDED(mDEBUGEventTarget->IsOnCurrentThread(&onTarget)));
+ MOZ_ASSERT(onTarget);
+
+ nsCOMPtr<nsIRunnable> runnable;
+ mRunnable.swap(runnable);
+
+ if (runnable) {
+ return runnable->Run();
+ }
+
+ return NS_OK;
+}
+
+nsresult
+CancelableRunnableWrapper::Cancel()
+{
+ DebugOnly<bool> onTarget;
+ MOZ_ASSERT(mDEBUGEventTarget);
+ MOZ_ASSERT(NS_SUCCEEDED(mDEBUGEventTarget->IsOnCurrentThread(&onTarget)));
+ MOZ_ASSERT(onTarget);
+
+ if (NS_WARN_IF(!mRunnable)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ Unused << Run();
+ MOZ_ASSERT(!mRunnable);
+
+ return NS_OK;
+}
+
+// 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 =
+ NewNonOwningRunnableMethod(doomedSupports, &nsISupports::Release);
+ MOZ_ASSERT(releaseRunnable);
+
+ if (aTarget) {
+ // If we're targeting a non-main thread then make sure the runnable is
+ // cancelable.
+ releaseRunnable = new CancelableRunnableWrapper(releaseRunnable, aTarget);
+
+ MOZ_ALWAYS_SUCCEEDS(aTarget->Dispatch(releaseRunnable,
+ NS_DISPATCH_NORMAL));
+ } else {
+ MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(releaseRunnable));
+ }
+}
+
+template <class ManagerType>
+void
+ConstructFileDescriptorSet(ManagerType* aManager,
+ nsTArray<FileDescriptor>& aFDs,
+ OptionalFileDescriptorSet& aOptionalFDSet)
+{
+ typedef typename ConcreteManagerTypeTraits<ManagerType>::Type
+ ConcreteManagerType;
+
+ MOZ_ASSERT(aManager);
+
+ if (aFDs.IsEmpty()) {
+ aOptionalFDSet = void_t();
+ return;
+ }
+
+ if (aFDs.Length() <= kMaxFileDescriptorsPerMessage) {
+ aOptionalFDSet = nsTArray<FileDescriptor>();
+ aOptionalFDSet.get_ArrayOfFileDescriptor().SwapElements(aFDs);
+ return;
+ }
+
+ auto* concreteManager = static_cast<ConcreteManagerType*>(aManager);
+
+ PFileDescriptorSetParent* fdSet =
+ concreteManager->SendPFileDescriptorSetConstructor(aFDs[0]);
+ if (!fdSet) {
+ aOptionalFDSet = void_t();
+ return;
+ }
+
+ for (uint32_t index = 1; index < aFDs.Length(); index++) {
+ if (!fdSet->SendAddFileDescriptor(aFDs[index])) {
+ aOptionalFDSet = void_t();
+ return;
+ }
+ }
+
+ aOptionalFDSet = fdSet;
+}
+
+void
+OptionalFileDescriptorSetToFDs(OptionalFileDescriptorSet& aOptionalSet,
+ nsTArray<FileDescriptor>& aFDs)
+{
+ MOZ_ASSERT(aFDs.IsEmpty());
+
+ switch (aOptionalSet.type()) {
+ case OptionalFileDescriptorSet::Tvoid_t:
+ return;
+
+ case OptionalFileDescriptorSet::TArrayOfFileDescriptor:
+ aOptionalSet.get_ArrayOfFileDescriptor().SwapElements(aFDs);
+ return;
+
+ case OptionalFileDescriptorSet::TPFileDescriptorSetChild: {
+ FileDescriptorSetChild* fdSetActor =
+ static_cast<FileDescriptorSetChild*>(
+ aOptionalSet.get_PFileDescriptorSetChild());
+ MOZ_ASSERT(fdSetActor);
+
+ fdSetActor->ForgetFileDescriptors(aFDs);
+ MOZ_ASSERT(!aFDs.IsEmpty());
+
+ PFileDescriptorSetChild::Send__delete__(fdSetActor);
+ return;
+ }
+
+ default:
+ MOZ_CRASH("Unknown type!");
+ }
+
+ MOZ_CRASH("Should never get here!");
+}
+
+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 final
+ : public nsIMultiplexInputStream
+ , public nsISeekableStream
+ , public nsIIPCSerializableInputStream
+ , public nsIFileMetadata
+{
+ nsCOMPtr<nsIInputStream> mStream;
+ RefPtr<BlobImpl> mBlobImpl;
+
+ nsIMultiplexInputStream* mWeakMultiplexStream;
+ nsISeekableStream* mWeakSeekableStream;
+ nsIIPCSerializableInputStream* mWeakSerializableStream;
+ nsIFileMetadata* mWeakFileMetadata;
+
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_FORWARD_NSIINPUTSTREAM(mStream->)
+ NS_FORWARD_SAFE_NSIMULTIPLEXINPUTSTREAM(mWeakMultiplexStream)
+ NS_FORWARD_SAFE_NSISEEKABLESTREAM(mWeakSeekableStream)
+ NS_FORWARD_SAFE_NSIIPCSERIALIZABLEINPUTSTREAM(mWeakSerializableStream)
+ NS_FORWARD_SAFE_NSIFILEMETADATA(mWeakFileMetadata)
+
+ BlobInputStreamTether(nsIInputStream* aStream, BlobImpl* aBlobImpl)
+ : mStream(aStream)
+ , mBlobImpl(aBlobImpl)
+ , mWeakMultiplexStream(nullptr)
+ , mWeakSeekableStream(nullptr)
+ , mWeakSerializableStream(nullptr)
+ , mWeakFileMetadata(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;
+ }
+
+ nsCOMPtr<nsIFileMetadata> fileMetadata = do_QueryInterface(aStream);
+ if (fileMetadata) {
+ MOZ_ASSERT(SameCOMIdentity(aStream, fileMetadata));
+ mWeakFileMetadata = fileMetadata;
+ }
+ }
+
+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_CONDITIONAL(nsIFileMetadata,
+ mWeakFileMetadata)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
+NS_INTERFACE_MAP_END
+
+class RemoteInputStream final
+ : public nsIInputStream
+ , public nsISeekableStream
+ , public nsIIPCSerializableInputStream
+ , public nsIFileMetadata
+ , public IPrivateRemoteInputStream
+{
+ Monitor mMonitor;
+ BlobChild* mActor;
+ nsCOMPtr<nsIInputStream> mStream;
+ RefPtr<BlobImpl> mBlobImpl;
+ nsCOMPtr<nsIEventTarget> mEventTarget;
+ nsISeekableStream* mWeakSeekableStream;
+ nsIFileMetadata* mWeakFileMetadata;
+ uint64_t mStart;
+ uint64_t mLength;
+
+public:
+ RemoteInputStream(BlobImpl* aBlobImpl,
+ uint64_t aStart,
+ uint64_t aLength);
+
+ RemoteInputStream(BlobChild* aActor,
+ BlobImpl* aBlobImpl,
+ uint64_t aStart,
+ uint64_t aLength);
+
+ bool
+ IsOnOwningThread() const
+ {
+ return EventTargetIsOnCurrentThread(mEventTarget);
+ }
+
+ void
+ AssertIsOnOwningThread() const
+ {
+ MOZ_ASSERT(IsOnOwningThread());
+ }
+
+ bool
+ IsWorkerStream() const
+ {
+ return !!mActor;
+ }
+
+ void
+ SetStream(nsIInputStream* aStream);
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+private:
+ ~RemoteInputStream();
+
+ nsresult
+ BlockAndWaitForStream();
+
+ void
+ ReallyBlockAndWaitForStream();
+
+ bool
+ IsSeekableStream();
+
+ bool
+ IsFileMetadata();
+
+ NS_DECL_NSIINPUTSTREAM
+ NS_DECL_NSISEEKABLESTREAM
+ NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
+ NS_DECL_NSIFILEMETADATA
+
+ virtual nsIInputStream*
+ BlockAndGetInternalStream() override;
+};
+
+class InputStreamChild final
+ : public PBlobStreamChild
+{
+ RefPtr<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) override;
+};
+
+class InputStreamParent final
+ : public PBlobStreamParent
+{
+ typedef mozilla::ipc::InputStreamParams InputStreamParams;
+ typedef mozilla::dom::OptionalFileDescriptorSet OptionalFileDescriptorSet;
+
+ bool* mSyncLoopGuard;
+ InputStreamParams* mParams;
+ OptionalFileDescriptorSet* mFDs;
+
+#ifdef DEBUG
+ PRThread* mOwningThread;
+#endif
+
+public:
+ InputStreamParent()
+ : mSyncLoopGuard(nullptr)
+ , mParams(nullptr)
+ , mFDs(nullptr)
+ {
+#ifdef DEBUG
+ mOwningThread = PR_GetCurrentThread();
+#endif
+
+ AssertIsOnOwningThread();
+
+ MOZ_COUNT_CTOR(InputStreamParent);
+ }
+
+ InputStreamParent(bool* aSyncLoopGuard,
+ InputStreamParams* aParams,
+ OptionalFileDescriptorSet* aFDs)
+ : mSyncLoopGuard(aSyncLoopGuard)
+ , mParams(aParams)
+ , mFDs(aFDs)
+ {
+#ifdef DEBUG
+ mOwningThread = PR_GetCurrentThread();
+#endif
+
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(aSyncLoopGuard);
+ MOZ_ASSERT(!*aSyncLoopGuard);
+ MOZ_ASSERT(aParams);
+ MOZ_ASSERT(aFDs);
+
+ MOZ_COUNT_CTOR(InputStreamParent);
+ }
+
+ ~InputStreamParent()
+ {
+ AssertIsOnOwningThread();
+
+ MOZ_COUNT_DTOR(InputStreamParent);
+ }
+
+ void
+ AssertIsOnOwningThread() const
+ {
+#ifdef DEBUG
+ MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread);
+#endif
+ }
+
+ bool
+ Destroy(const InputStreamParams& aParams,
+ const OptionalFileDescriptorSet& aFDs)
+ {
+ AssertIsOnOwningThread();
+
+ if (mSyncLoopGuard) {
+ MOZ_ASSERT(!*mSyncLoopGuard);
+
+ *mSyncLoopGuard = true;
+ *mParams = aParams;
+ *mFDs = aFDs;
+
+ // We're not a live actor so manage the memory ourselves.
+ delete this;
+ return true;
+ }
+
+ // This will be destroyed by BlobParent::DeallocPBlobStreamParent.
+ return PBlobStreamParent::Send__delete__(this, aParams, aFDs);
+ }
+
+private:
+ // This method is only called by the IPDL message machinery.
+ virtual void
+ ActorDestroy(ActorDestroyReason aWhy) override
+ {
+ // Nothing needs to be done here.
+ }
+};
+
+struct MOZ_STACK_CLASS CreateBlobImplMetadata final
+{
+ nsString mContentType;
+ nsString mName;
+ uint64_t mLength;
+ int64_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<BlobImpl>
+CreateBlobImpl(const nsID& aKnownBlobIDData,
+ const CreateBlobImplMetadata& aMetadata)
+{
+ MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
+ MOZ_ASSERT(aMetadata.mHasRecursed);
+
+ RefPtr<BlobImpl> 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<BlobImpl>
+CreateBlobImpl(const BlobDataStream& aStream,
+ const CreateBlobImplMetadata& aMetadata)
+{
+ MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
+
+ nsCOMPtr<nsIInputStream> inputStream = DeserializeIPCStream(aStream.stream());
+ if (!inputStream) {
+ ASSERT_UNLESS_FUZZING();
+ return nullptr;
+ }
+
+ uint64_t length = aStream.length();
+
+ RefPtr<BlobImpl> blobImpl;
+ if (!aMetadata.mHasRecursed && aMetadata.IsFile()) {
+ if (length) {
+ blobImpl =
+ BlobImplStream::Create(inputStream,
+ aMetadata.mName,
+ aMetadata.mContentType,
+ aMetadata.mLastModifiedDate,
+ length);
+ } else {
+ blobImpl =
+ new EmptyBlobImpl(aMetadata.mName,
+ aMetadata.mContentType,
+ aMetadata.mLastModifiedDate);
+ }
+ } else if (length) {
+ blobImpl =
+ BlobImplStream::Create(inputStream, aMetadata.mContentType,
+ length);
+ } else {
+ blobImpl = new EmptyBlobImpl(aMetadata.mContentType);
+ }
+
+ MOZ_ALWAYS_SUCCEEDS(blobImpl->SetMutable(false));
+
+ return blobImpl.forget();
+}
+
+already_AddRefed<BlobImpl>
+CreateBlobImpl(const nsTArray<BlobData>& aBlobData,
+ CreateBlobImplMetadata& aMetadata);
+
+already_AddRefed<BlobImpl>
+CreateBlobImplFromBlobData(const BlobData& aBlobData,
+ CreateBlobImplMetadata& aMetadata)
+{
+ MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
+
+ RefPtr<BlobImpl> blobImpl;
+
+ switch (aBlobData.type()) {
+ case BlobData::TnsID: {
+ blobImpl = CreateBlobImpl(aBlobData.get_nsID(), aMetadata);
+ break;
+ }
+
+ case BlobData::TBlobDataStream: {
+ blobImpl = CreateBlobImpl(aBlobData.get_BlobDataStream(), aMetadata);
+ break;
+ }
+
+ case BlobData::TArrayOfBlobData: {
+ blobImpl = CreateBlobImpl(aBlobData.get_ArrayOfBlobData(), aMetadata);
+ break;
+ }
+
+ default:
+ MOZ_CRASH("Unknown params!");
+ }
+
+ return blobImpl.forget();
+}
+
+already_AddRefed<BlobImpl>
+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];
+
+ RefPtr<BlobImpl> 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();
+ }
+
+ nsTArray<RefPtr<BlobImpl>> blobImpls;
+ if (NS_WARN_IF(!blobImpls.SetLength(aBlobDatas.Length(), fallible))) {
+ return nullptr;
+ }
+
+ const bool hasRecursed = aMetadata.mHasRecursed;
+ aMetadata.mHasRecursed = true;
+
+ for (uint32_t count = aBlobDatas.Length(), index = 0;
+ index < count;
+ index++) {
+ const BlobData& blobData = aBlobDatas[index];
+ RefPtr<BlobImpl>& 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);
+ }
+
+ ErrorResult rv;
+ RefPtr<BlobImpl> blobImpl;
+ if (!hasRecursed && aMetadata.IsFile()) {
+ blobImpl = MultipartBlobImpl::Create(Move(blobImpls), aMetadata.mName,
+ aMetadata.mContentType, rv);
+ } else {
+ blobImpl = MultipartBlobImpl::Create(Move(blobImpls), aMetadata.mContentType, rv);
+ }
+
+ if (NS_WARN_IF(rv.Failed())) {
+ rv.SuppressException();
+ return nullptr;
+ }
+
+ MOZ_ALWAYS_SUCCEEDS(blobImpl->SetMutable(false));
+
+ return blobImpl.forget();
+}
+
+already_AddRefed<BlobImpl>
+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() == INT64_MAX)) {
+ ASSERT_UNLESS_FUZZING();
+ return nullptr;
+ }
+
+ if (NS_WARN_IF(!params.path().IsEmpty())) {
+ ASSERT_UNLESS_FUZZING();
+ return nullptr;
+ }
+
+ metadata.mContentType = params.contentType();
+ metadata.mName = params.name();
+ metadata.mLength = params.length();
+ metadata.mLastModifiedDate = params.modDate();
+ }
+
+ RefPtr<BlobImpl> blobImpl =
+ CreateBlobImplFromBlobData(aBlobData, metadata);
+ return blobImpl.forget();
+}
+
+template <class ChildManagerType>
+void
+BlobDataFromBlobImpl(ChildManagerType* aManager, BlobImpl* aBlobImpl,
+ BlobData& aBlobData,
+ nsTArray<UniquePtr<AutoIPCStream>>& aIPCStreams)
+{
+ MOZ_ASSERT(gProcessType != GeckoProcessType_Default);
+ MOZ_ASSERT(aBlobImpl);
+
+ const nsTArray<RefPtr<BlobImpl>>* subBlobs = aBlobImpl->GetSubBlobImpls();
+
+ if (subBlobs) {
+ MOZ_ASSERT(subBlobs->Length());
+
+ 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(aManager, subBlobs->ElementAt(index),
+ subBlobDatas[index], aIPCStreams);
+ }
+
+ return;
+ }
+
+ nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryInterface(aBlobImpl);
+ if (remoteBlob) {
+ BlobChild* actor = remoteBlob->GetBlobChild();
+ MOZ_ASSERT(actor);
+
+ aBlobData = actor->ParentID();
+ return;
+ }
+
+ ErrorResult rv;
+ uint64_t length = aBlobImpl->GetSize(rv);
+ MOZ_ALWAYS_TRUE(!rv.Failed());
+
+ nsCOMPtr<nsIInputStream> inputStream;
+ aBlobImpl->GetInternalStream(getter_AddRefs(inputStream), rv);
+ MOZ_ALWAYS_TRUE(!rv.Failed());
+
+ UniquePtr<AutoIPCStream> autoStream(new AutoIPCStream());
+ autoStream->Serialize(inputStream, aManager);
+ aBlobData = BlobDataStream(autoStream->TakeValue(), length);
+
+ aIPCStreams.AppendElement(Move(autoStream));
+}
+
+RemoteInputStream::RemoteInputStream(BlobImpl* aBlobImpl,
+ uint64_t aStart,
+ uint64_t aLength)
+ : mMonitor("RemoteInputStream.mMonitor")
+ , mActor(nullptr)
+ , mBlobImpl(aBlobImpl)
+ , mWeakSeekableStream(nullptr)
+ , mWeakFileMetadata(nullptr)
+ , mStart(aStart)
+ , mLength(aLength)
+{
+ MOZ_ASSERT(aBlobImpl);
+
+ if (!NS_IsMainThread()) {
+ mEventTarget = do_GetCurrentThread();
+ MOZ_ASSERT(mEventTarget);
+ }
+
+ MOZ_ASSERT(IsOnOwningThread());
+}
+
+RemoteInputStream::RemoteInputStream(BlobChild* aActor,
+ BlobImpl* aBlobImpl,
+ uint64_t aStart,
+ uint64_t aLength)
+ : mMonitor("RemoteInputStream.mMonitor")
+ , mActor(aActor)
+ , mBlobImpl(aBlobImpl)
+ , mEventTarget(NS_GetCurrentThread())
+ , mWeakSeekableStream(nullptr)
+ , mWeakFileMetadata(nullptr)
+ , mStart(aStart)
+ , mLength(aLength)
+{
+ MOZ_ASSERT(!NS_IsMainThread());
+ MOZ_ASSERT(aActor);
+ MOZ_ASSERT(aBlobImpl);
+
+ MOZ_ASSERT(IsOnOwningThread());
+}
+
+RemoteInputStream::~RemoteInputStream()
+{
+ if (!IsOnOwningThread()) {
+ mStream = nullptr;
+ mWeakSeekableStream = nullptr;
+ mWeakFileMetadata = nullptr;
+
+ if (mBlobImpl) {
+ ReleaseOnTarget(mBlobImpl, mEventTarget);
+ }
+ }
+}
+
+void
+RemoteInputStream::SetStream(nsIInputStream* aStream)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(aStream);
+
+ nsCOMPtr<nsIInputStream> stream = aStream;
+ nsCOMPtr<nsISeekableStream> seekableStream = do_QueryInterface(aStream);
+ nsCOMPtr<nsIFileMetadata> fileMetadata = do_QueryInterface(aStream);
+
+ MOZ_ASSERT_IF(seekableStream, SameCOMIdentity(aStream, seekableStream));
+ MOZ_ASSERT_IF(fileMetadata, SameCOMIdentity(aStream, fileMetadata));
+
+ {
+ MonitorAutoLock lock(mMonitor);
+
+ MOZ_ASSERT_IF(mStream, IsWorkerStream());
+
+ if (!mStream) {
+ MOZ_ASSERT(!mWeakSeekableStream);
+ MOZ_ASSERT(!mWeakFileMetadata);
+
+ mStream.swap(stream);
+ mWeakSeekableStream = seekableStream;
+ mWeakFileMetadata = fileMetadata;
+
+ mMonitor.Notify();
+ }
+ }
+}
+
+nsresult
+RemoteInputStream::BlockAndWaitForStream()
+{
+ if (mStream) {
+ return NS_OK;
+ }
+
+ if (IsOnOwningThread()) {
+ if (NS_IsMainThread()) {
+ NS_WARNING("Blocking the main thread is not supported!");
+ return NS_ERROR_FAILURE;
+ }
+
+ MOZ_ASSERT(IsWorkerStream());
+
+ InputStreamParams params;
+ OptionalFileDescriptorSet optionalFDs;
+
+ mActor->SendBlobStreamSync(mStart, mLength, &params, &optionalFDs);
+
+ nsTArray<FileDescriptor> fds;
+ OptionalFileDescriptorSetToFDs(optionalFDs, fds);
+
+ nsCOMPtr<nsIInputStream> stream = DeserializeInputStream(params, fds);
+ MOZ_ASSERT(stream);
+
+ SetStream(stream);
+ return NS_OK;
+ }
+
+ ReallyBlockAndWaitForStream();
+
+ return NS_OK;
+}
+
+void
+RemoteInputStream::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;
+ if (NS_SUCCEEDED(mWeakSeekableStream->Tell(&position))) {
+ MOZ_ASSERT(!position, "Stream not starting at 0!");
+ }
+ }
+#endif
+}
+
+bool
+RemoteInputStream::IsSeekableStream()
+{
+ if (IsOnOwningThread()) {
+ if (!mStream) {
+ NS_WARNING("Don't know if this stream is seekable yet!");
+ return true;
+ }
+ } else {
+ ReallyBlockAndWaitForStream();
+ }
+
+ return !!mWeakSeekableStream;
+}
+
+bool
+RemoteInputStream::IsFileMetadata()
+{
+ if (IsOnOwningThread()) {
+ if (!mStream) {
+ NS_WARNING("Don't know if this stream supports file metadata yet!");
+ return true;
+ }
+ } else {
+ ReallyBlockAndWaitForStream();
+ }
+
+ return !!mWeakFileMetadata;
+}
+
+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_CONDITIONAL(nsIFileMetadata, IsFileMetadata())
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
+ NS_INTERFACE_MAP_ENTRY(IPrivateRemoteInputStream)
+NS_INTERFACE_MAP_END
+
+NS_IMETHODIMP
+RemoteInputStream::Close()
+{
+ nsresult rv = BlockAndWaitForStream();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ RefPtr<BlobImpl> blobImpl;
+ mBlobImpl.swap(blobImpl);
+
+ rv = mStream->Close();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RemoteInputStream::Available(uint64_t* aAvailable)
+{
+ if (!IsOnOwningThread()) {
+ nsresult rv = BlockAndWaitForStream();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mStream->Available(aAvailable);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+ }
+
+#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.StealNSResult();
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RemoteInputStream::Read(char* aBuffer, uint32_t aCount, uint32_t* aResult)
+{
+ nsresult rv = BlockAndWaitForStream();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mStream->Read(aBuffer, aCount, aResult);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RemoteInputStream::ReadSegments(nsWriteSegmentFun aWriter,
+ void* aClosure,
+ uint32_t aCount,
+ uint32_t* aResult)
+{
+ nsresult rv = BlockAndWaitForStream();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mStream->ReadSegments(aWriter, aClosure, aCount, aResult);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RemoteInputStream::IsNonBlocking(bool* aNonBlocking)
+{
+ NS_ENSURE_ARG_POINTER(aNonBlocking);
+
+ *aNonBlocking = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RemoteInputStream::Seek(int32_t aWhence, int64_t aOffset)
+{
+ 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_IMETHODIMP
+RemoteInputStream::Tell(int64_t* aResult)
+{
+ // 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_IMETHODIMP
+RemoteInputStream::SetEOF()
+{
+ 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;
+}
+
+void
+RemoteInputStream::Serialize(InputStreamParams& aParams,
+ FileDescriptorArray& /* aFDs */)
+{
+ 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
+RemoteInputStream::Deserialize(const InputStreamParams& /* aParams */,
+ const FileDescriptorArray& /* aFDs */)
+{
+ // See InputStreamUtils.cpp to see how deserialization of a
+ // RemoteInputStream is special-cased.
+ MOZ_CRASH("RemoteInputStream should never be deserialized");
+}
+
+NS_IMETHODIMP
+RemoteInputStream::GetSize(int64_t* aSize)
+{
+ nsresult rv = BlockAndWaitForStream();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (!mWeakFileMetadata) {
+ NS_WARNING("Underlying blob stream doesn't support file metadata!");
+ return NS_ERROR_NO_INTERFACE;
+ }
+
+ rv = mWeakFileMetadata->GetSize(aSize);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RemoteInputStream::GetLastModified(int64_t* aLastModified)
+{
+ nsresult rv = BlockAndWaitForStream();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (!mWeakFileMetadata) {
+ NS_WARNING("Underlying blob stream doesn't support file metadata!");
+ return NS_ERROR_NO_INTERFACE;
+ }
+
+ rv = mWeakFileMetadata->GetLastModified(aLastModified);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RemoteInputStream::GetFileDescriptor(PRFileDesc** aFileDescriptor)
+{
+ nsresult rv = BlockAndWaitForStream();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (!mWeakFileMetadata) {
+ NS_WARNING("Underlying blob stream doesn't support file metadata!");
+ return NS_ERROR_NO_INTERFACE;
+ }
+
+ rv = mWeakFileMetadata->GetFileDescriptor(aFileDescriptor);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+Maybe<uint64_t>
+RemoteInputStream::ExpectedSerializedLength()
+{
+ return Nothing();
+}
+
+nsIInputStream*
+RemoteInputStream::BlockAndGetInternalStream()
+{
+ MOZ_ASSERT(!IsOnOwningThread());
+
+ nsresult rv = BlockAndWaitForStream();
+ NS_ENSURE_SUCCESS(rv, nullptr);
+
+ return mStream;
+}
+
+} // namespace
+
+StaticAutoPtr<BlobParent::IDTable> BlobParent::sIDTable;
+StaticAutoPtr<Mutex> BlobParent::sIDTableMutex;
+
+/*******************************************************************************
+ * BlobParent::IDTableEntry Declaration
+ ******************************************************************************/
+
+class BlobParent::IDTableEntry final
+{
+ const nsID mID;
+ const intptr_t mProcessID;
+ const RefPtr<BlobImpl> mBlobImpl;
+
+public:
+ static already_AddRefed<IDTableEntry>
+ Create(const nsID& aID, intptr_t aProcessID, BlobImpl* 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, BlobImpl* 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;
+ }
+
+ BlobImpl*
+ GetBlobImpl() const
+ {
+ return mBlobImpl;
+ }
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(IDTableEntry)
+
+private:
+ IDTableEntry(const nsID& aID, intptr_t aProcessID, BlobImpl* aBlobImpl);
+ ~IDTableEntry();
+
+ static already_AddRefed<IDTableEntry>
+ GetOrCreateInternal(const nsID& aID,
+ intptr_t aProcessID,
+ BlobImpl* 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 final
+ : public Runnable
+{
+ friend class nsRevocableEventPtr<OpenStreamRunnable>;
+
+ // Only safe to access these pointers if mRevoked is false!
+ BlobParent* mBlobActor;
+ InputStreamParent* mStreamActor;
+
+ nsCOMPtr<nsIInputStream> mStream;
+ nsCOMPtr<nsIIPCSerializableInputStream> mSerializable;
+ nsCOMPtr<nsIEventTarget> mActorTarget;
+ nsCOMPtr<nsIThread> mIOTarget;
+
+ bool mRevoked;
+ bool mClosing;
+
+public:
+ OpenStreamRunnable(BlobParent* aBlobActor,
+ InputStreamParent* 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_SUCCEEDS(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);
+
+ DebugOnly<nsresult> rv = stream->Close();
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to close stream!");
+
+ MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(NewRunnableMethod(ioTarget, &nsIThread::Shutdown)));
+
+ 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;
+ nsTArray<FileDescriptor> fds;
+ serializable->Serialize(params, fds);
+
+ MOZ_ASSERT(params.type() != InputStreamParams::T__None);
+
+ OptionalFileDescriptorSet optionalFDSet;
+ if (nsIContentParent* contentManager = mBlobActor->GetContentManager()) {
+ ConstructFileDescriptorSet(contentManager, fds, optionalFDSet);
+ } else {
+ ConstructFileDescriptorSet(mBlobActor->GetBackgroundManager(),
+ fds,
+ optionalFDSet);
+ }
+
+ mStreamActor->Destroy(params, optionalFDSet);
+
+ 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 = kungFuDeathGrip->Dispatch(this, NS_DISPATCH_NORMAL);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+ }
+
+ NS_IMETHOD
+ Run() override
+ {
+ MOZ_ASSERT(mIOTarget);
+
+ if (IsOnOwningThread()) {
+ return SendResponse();
+ }
+
+ if (!mClosing) {
+ return OpenStream();
+ }
+
+ return CloseStream();
+ }
+};
+
+NS_IMPL_ISUPPORTS_INHERITED0(BlobParent::OpenStreamRunnable, Runnable)
+
+/*******************************************************************************
+ * BlobChild::RemoteBlobImpl Declaration
+ ******************************************************************************/
+
+class BlobChild::RemoteBlobImpl
+ : public BlobImplBase
+ , public nsIRemoteBlob
+{
+protected:
+ class CreateStreamHelper;
+ class WorkerHolder;
+
+ BlobChild* mActor;
+ nsCOMPtr<nsIEventTarget> mActorTarget;
+
+ // These member variables are protected by mutex and it's set to null when the
+ // worker goes away.
+ WorkerPrivate* mWorkerPrivate;
+ nsAutoPtr<WorkerHolder> mWorkerHolder;
+ Mutex mMutex;
+
+ // We use this pointer to keep a live a blobImpl coming from a different
+ // process until this one is fully created. We set it to null when
+ // SendCreatedFromKnownBlob() is received. This is used only with KnownBlob
+ // params in the CTOR of a IPC BlobImpl.
+ RefPtr<BlobImpl> mDifferentProcessBlobImpl;
+
+ RefPtr<BlobImpl> mSameProcessBlobImpl;
+
+ const bool mIsSlice;
+
+ const bool mIsDirectory;
+
+public:
+
+ enum BlobImplIsDirectory
+ {
+ eNotDirectory,
+ eDirectory
+ };
+
+ // For File.
+ RemoteBlobImpl(BlobChild* aActor,
+ BlobImpl* aRemoteBlobImpl,
+ const nsAString& aName,
+ const nsAString& aContentType,
+ const nsAString& aPath,
+ uint64_t aLength,
+ int64_t aModDate,
+ BlobImplIsDirectory aIsDirectory,
+ bool aIsSameProcessBlob);
+
+ // For Blob.
+ RemoteBlobImpl(BlobChild* aActor,
+ BlobImpl* aRemoteBlobImpl,
+ const nsAString& aContentType,
+ uint64_t aLength,
+ bool aIsSameProcessBlob);
+
+ // For mystery blobs.
+ explicit
+ RemoteBlobImpl(BlobChild* aActor);
+
+ void
+ NoteDyingActor();
+
+ BlobChild*
+ GetActor() const
+ {
+ MOZ_ASSERT(ActorEventTargetIsOnCurrentThread());
+
+ return mActor;
+ }
+
+ nsIEventTarget*
+ GetActorEventTarget() const
+ {
+ return mActorTarget;
+ }
+
+ bool
+ ActorEventTargetIsOnCurrentThread() const
+ {
+ return 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) const override;
+
+ virtual bool
+ IsDirectory() const override;
+
+ virtual already_AddRefed<BlobImpl>
+ CreateSlice(uint64_t aStart,
+ uint64_t aLength,
+ const nsAString& aContentType,
+ ErrorResult& aRv) override;
+
+ virtual void
+ GetInternalStream(nsIInputStream** aStream, ErrorResult& aRv) override;
+
+ virtual int64_t
+ GetFileId() override;
+
+ virtual int64_t
+ GetLastModified(ErrorResult& aRv) override;
+
+ virtual void
+ SetLastModified(int64_t aLastModified) override;
+
+ virtual nsresult
+ SetMutable(bool aMutable) override;
+
+ virtual BlobChild*
+ GetBlobChild() override;
+
+ virtual BlobParent*
+ GetBlobParent() override;
+
+ void
+ NullifyDifferentProcessBlobImpl()
+ {
+ MOZ_ASSERT(mDifferentProcessBlobImpl);
+ mDifferentProcessBlobImpl = nullptr;
+ }
+
+ // Used only by CreateStreamHelper, it dispatches a runnable to the target
+ // thread. This thread can be the main-thread, the background thread or a
+ // worker. If the thread is a worker, the aRunnable is wrapper into a
+ // ControlRunnable in order to avoid to be blocked into a sync event loop.
+ nsresult
+ DispatchToTarget(nsIRunnable* aRunnable);
+
+ void
+ WorkerHasNotified();
+
+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::WorkerHolder final
+ : public workers::WorkerHolder
+{
+ // Raw pointer because this class is kept alive by the mRemoteBlobImpl.
+ RemoteBlobImpl* mRemoteBlobImpl;
+
+public:
+ explicit WorkerHolder(RemoteBlobImpl* aRemoteBlobImpl)
+ : mRemoteBlobImpl(aRemoteBlobImpl)
+ {
+ MOZ_ASSERT(aRemoteBlobImpl);
+ }
+
+ bool Notify(Status aStatus) override
+ {
+ mRemoteBlobImpl->WorkerHasNotified();
+ return true;
+ }
+};
+
+class BlobChild::RemoteBlobImpl::CreateStreamHelper final
+ : public CancelableRunnable
+{
+ Monitor mMonitor;
+ RefPtr<RemoteBlobImpl> mRemoteBlobImpl;
+ RefPtr<RemoteInputStream> mInputStream;
+ const uint64_t mStart;
+ const uint64_t mLength;
+ bool mDone;
+
+public:
+ explicit CreateStreamHelper(RemoteBlobImpl* aRemoteBlobImpl);
+
+ nsresult
+ GetStream(nsIInputStream** aInputStream);
+
+ NS_IMETHOD Run() override;
+
+private:
+ ~CreateStreamHelper()
+ {
+ MOZ_ASSERT(!mRemoteBlobImpl);
+ MOZ_ASSERT(!mInputStream);
+ MOZ_ASSERT(mDone);
+ }
+
+ void
+ RunInternal(RemoteBlobImpl* aBaseRemoteBlobImpl, bool aNotify);
+};
+
+class BlobChild::RemoteBlobSliceImpl final
+ : public RemoteBlobImpl
+{
+ RefPtr<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;
+ }
+
+ void
+ EnsureActorWasCreated()
+ {
+ MOZ_ASSERT_IF(!ActorEventTargetIsOnCurrentThread(),
+ mActorWasCreated);
+
+ if (!mActorWasCreated) {
+ EnsureActorWasCreatedInternal();
+ }
+ }
+
+ NS_DECL_ISUPPORTS_INHERITED
+
+ virtual BlobChild*
+ GetBlobChild() override;
+
+private:
+ ~RemoteBlobSliceImpl()
+ { }
+
+ void
+ EnsureActorWasCreatedInternal();
+};
+
+/*******************************************************************************
+ * BlobParent::RemoteBlobImpl Declaration
+ ******************************************************************************/
+
+class BlobParent::RemoteBlobImpl final
+ : public BlobImpl
+ , public nsIRemoteBlob
+{
+ BlobParent* mActor;
+ nsCOMPtr<nsIEventTarget> mActorTarget;
+ RefPtr<BlobImpl> mBlobImpl;
+
+public:
+ RemoteBlobImpl(BlobParent* aActor, BlobImpl* aBlobImpl);
+
+ void
+ NoteDyingActor();
+
+ NS_DECL_ISUPPORTS_INHERITED
+
+ virtual void
+ GetName(nsAString& aName) const override;
+
+ virtual void
+ GetDOMPath(nsAString& aPath) const override;
+
+ virtual void
+ SetDOMPath(const nsAString& aPath) override;
+
+ virtual int64_t
+ GetLastModified(ErrorResult& aRv) override;
+
+ virtual void
+ SetLastModified(int64_t aLastModified) override;
+
+ virtual void
+ GetMozFullPath(nsAString& aName, ErrorResult& aRv) const override;
+
+ virtual void
+ GetMozFullPathInternal(nsAString& aFileName, ErrorResult& aRv) const override;
+
+ virtual bool
+ IsDirectory() const override;
+
+ virtual uint64_t
+ GetSize(ErrorResult& aRv) override;
+
+ virtual void
+ GetType(nsAString& aType) override;
+
+ virtual uint64_t
+ GetSerialNumber() const override;
+
+ virtual already_AddRefed<BlobImpl>
+ CreateSlice(uint64_t aStart,
+ uint64_t aLength,
+ const nsAString& aContentType,
+ ErrorResult& aRv) override;
+
+ virtual const nsTArray<RefPtr<BlobImpl>>*
+ GetSubBlobImpls() const override;
+
+ virtual void
+ GetInternalStream(nsIInputStream** aStream, ErrorResult& aRv) override;
+
+ virtual int64_t
+ GetFileId() override;
+
+ virtual nsresult
+ GetSendInfo(nsIInputStream** aBody,
+ uint64_t* aContentLength,
+ nsACString& aContentType,
+ nsACString& aCharset) override;
+
+ virtual nsresult
+ GetMutable(bool* aMutable) const override;
+
+ virtual nsresult
+ SetMutable(bool aMutable) override;
+
+ virtual void
+ SetLazyData(const nsAString& aName,
+ const nsAString& aContentType,
+ uint64_t aLength,
+ int64_t aLastModifiedDate) override;
+
+ virtual bool
+ IsMemoryFile() const override;
+
+ virtual bool
+ IsSizeUnknown() const override;
+
+ virtual bool
+ IsDateUnknown() const override;
+
+ virtual bool
+ IsFile() const override;
+
+ virtual bool
+ MayBeClonedToOtherThreads() const override;
+
+ virtual BlobChild*
+ GetBlobChild() override;
+
+ virtual BlobParent*
+ GetBlobParent() override;
+
+private:
+ ~RemoteBlobImpl()
+ {
+ MOZ_ASSERT_IF(mActorTarget,
+ EventTargetIsOnCurrentThread(mActorTarget));
+ }
+
+ void
+ Destroy();
+};
+
+/*******************************************************************************
+ * BlobChild::RemoteBlobImpl
+ ******************************************************************************/
+
+BlobChild::
+RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor,
+ BlobImpl* aRemoteBlobImpl,
+ const nsAString& aName,
+ const nsAString& aContentType,
+ const nsAString& aDOMPath,
+ uint64_t aLength,
+ int64_t aModDate,
+ BlobImplIsDirectory aIsDirectory,
+ bool aIsSameProcessBlob)
+ : BlobImplBase(aName, aContentType, aLength, aModDate)
+ , mWorkerPrivate(nullptr)
+ , mMutex("BlobChild::RemoteBlobImpl::mMutex")
+ , mIsSlice(false), mIsDirectory(aIsDirectory == eDirectory)
+{
+ SetDOMPath(aDOMPath);
+
+ if (aIsSameProcessBlob) {
+ MOZ_ASSERT(aRemoteBlobImpl);
+ mSameProcessBlobImpl = aRemoteBlobImpl;
+ MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
+ } else {
+ mDifferentProcessBlobImpl = aRemoteBlobImpl;
+ }
+
+ CommonInit(aActor);
+}
+
+BlobChild::
+RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor,
+ BlobImpl* aRemoteBlobImpl,
+ const nsAString& aContentType,
+ uint64_t aLength,
+ bool aIsSameProcessBlob)
+ : BlobImplBase(aContentType, aLength)
+ , mWorkerPrivate(nullptr)
+ , mMutex("BlobChild::RemoteBlobImpl::mMutex")
+ , mIsSlice(false), mIsDirectory(false)
+{
+ if (aIsSameProcessBlob) {
+ MOZ_ASSERT(aRemoteBlobImpl);
+ mSameProcessBlobImpl = aRemoteBlobImpl;
+ MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
+ } else {
+ mDifferentProcessBlobImpl = aRemoteBlobImpl;
+ }
+
+ CommonInit(aActor);
+}
+
+BlobChild::
+RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor)
+ : BlobImplBase(EmptyString(), EmptyString(), UINT64_MAX, INT64_MAX)
+ , mWorkerPrivate(nullptr)
+ , mMutex("BlobChild::RemoteBlobImpl::mMutex")
+ , mIsSlice(false), mIsDirectory(false)
+{
+ CommonInit(aActor);
+}
+
+BlobChild::
+RemoteBlobImpl::RemoteBlobImpl(const nsAString& aContentType, uint64_t aLength)
+ : BlobImplBase(aContentType, aLength)
+ , mActor(nullptr)
+ , mWorkerPrivate(nullptr)
+ , mMutex("BlobChild::RemoteBlobImpl::mMutex")
+ , mIsSlice(true)
+ , mIsDirectory(false)
+{
+ mImmutable = true;
+}
+
+void
+BlobChild::
+RemoteBlobImpl::CommonInit(BlobChild* aActor)
+{
+ MOZ_ASSERT(aActor);
+ aActor->AssertIsOnOwningThread();
+
+ mActor = aActor;
+ mActorTarget = aActor->EventTarget();
+
+ if (!NS_IsMainThread()) {
+ mWorkerPrivate = GetCurrentThreadWorkerPrivate();
+ // We must comunicate via IPC in the owning thread, so, if this BlobImpl has
+ // been created on a Workerr and then it's sent to a different thread (for
+ // instance the main-thread), we still need to keep alive that Worker.
+ if (mWorkerPrivate) {
+ mWorkerHolder = new RemoteBlobImpl::WorkerHolder(this);
+ if (NS_WARN_IF(!mWorkerHolder->HoldWorker(mWorkerPrivate, Closing))) {
+ // We don't care too much if the worker is already going away because no
+ // sync-event-loop can be created at this point.
+ mWorkerPrivate = nullptr;
+ mWorkerHolder = nullptr;
+ }
+ }
+ }
+
+ 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();
+ }
+
+ if (mWorkerHolder) {
+ // We are in the worker thread.
+ MutexAutoLock lock(mMutex);
+ mWorkerPrivate = nullptr;
+ mWorkerHolder = nullptr;
+ }
+
+ delete this;
+ return;
+ }
+
+ nsCOMPtr<nsIRunnable> destroyRunnable =
+ NewNonOwningRunnableMethod(this, &RemoteBlobImpl::Destroy);
+
+ if (mActorTarget) {
+ destroyRunnable =
+ new CancelableRunnableWrapper(destroyRunnable, mActorTarget);
+
+ MOZ_ALWAYS_SUCCEEDS(mActorTarget->Dispatch(destroyRunnable,
+ NS_DISPATCH_NORMAL));
+ } else {
+ MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(destroyRunnable));
+ }
+}
+
+NS_IMPL_ADDREF(BlobChild::RemoteBlobImpl)
+NS_IMPL_RELEASE_WITH_DESTROY(BlobChild::RemoteBlobImpl, Destroy())
+NS_IMPL_QUERY_INTERFACE_INHERITED(BlobChild::RemoteBlobImpl,
+ BlobImpl,
+ nsIRemoteBlob)
+
+void
+BlobChild::
+RemoteBlobImpl::GetMozFullPathInternal(nsAString& aFilePath,
+ ErrorResult& aRv) const
+{
+ if (!EventTargetIsOnCurrentThread(mActorTarget)) {
+ MOZ_CRASH("Not implemented!");
+ }
+
+ if (mSameProcessBlobImpl) {
+ MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
+
+ mSameProcessBlobImpl->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;
+}
+
+bool
+BlobChild::
+RemoteBlobImpl::IsDirectory() const
+{
+ return mIsDirectory;
+}
+
+already_AddRefed<BlobImpl>
+BlobChild::
+RemoteBlobImpl::CreateSlice(uint64_t aStart,
+ uint64_t aLength,
+ const nsAString& aContentType,
+ ErrorResult& aRv)
+{
+ // May be called on any thread.
+ if (mSameProcessBlobImpl) {
+ MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
+
+ return mSameProcessBlobImpl->CreateSlice(aStart,
+ aLength,
+ aContentType,
+ aRv);
+ }
+
+ RefPtr<RemoteBlobSliceImpl> slice =
+ new RemoteBlobSliceImpl(this, aStart, aLength, aContentType);
+ return slice.forget();
+}
+
+void
+BlobChild::
+RemoteBlobImpl::GetInternalStream(nsIInputStream** aStream, ErrorResult& aRv)
+{
+ // May be called on any thread.
+ if (mSameProcessBlobImpl) {
+ MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
+
+ nsCOMPtr<nsIInputStream> realStream;
+ mSameProcessBlobImpl->GetInternalStream(getter_AddRefs(realStream), aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
+ }
+
+ RefPtr<BlobInputStreamTether> tether =
+ new BlobInputStreamTether(realStream, mSameProcessBlobImpl);
+ tether.forget(aStream);
+ return;
+ }
+
+ RefPtr<CreateStreamHelper> helper = new CreateStreamHelper(this);
+ aRv = helper->GetStream(aStream);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
+ }
+}
+
+int64_t
+BlobChild::
+RemoteBlobImpl::GetFileId()
+{
+ if (!EventTargetIsOnCurrentThread(mActorTarget)) {
+ MOZ_CRASH("Not implemented!");
+ }
+
+ if (mSameProcessBlobImpl) {
+ MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
+
+ return mSameProcessBlobImpl->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;
+}
+
+void
+BlobChild::
+RemoteBlobImpl::SetLastModified(int64_t aLastModified)
+{
+ MOZ_CRASH("SetLastModified of a remote blob is not allowed!");
+}
+
+nsresult
+BlobChild::
+RemoteBlobImpl::SetMutable(bool aMutable)
+{
+ if (!aMutable && IsSlice()) {
+ // Make sure that slices are backed by a real actor now while we are still
+ // on the correct thread.
+ AsSlice()->EnsureActorWasCreated();
+ }
+
+ nsresult rv = BlobImplBase::SetMutable(aMutable);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ MOZ_ASSERT_IF(!aMutable, mImmutable);
+
+ return NS_OK;
+}
+
+BlobChild*
+BlobChild::
+RemoteBlobImpl::GetBlobChild()
+{
+ return mActor;
+}
+
+BlobParent*
+BlobChild::
+RemoteBlobImpl::GetBlobParent()
+{
+ return nullptr;
+}
+
+class RemoteBlobControlRunnable : public WorkerControlRunnable
+{
+ nsCOMPtr<nsIRunnable> mRunnable;
+
+public:
+ RemoteBlobControlRunnable(WorkerPrivate* aWorkerPrivate,
+ nsIRunnable* aRunnable)
+ : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
+ , mRunnable(aRunnable)
+ {
+ MOZ_ASSERT(aRunnable);
+ }
+
+ bool
+ WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
+ {
+ mRunnable->Run();
+ return true;
+ }
+};
+
+nsresult
+BlobChild::
+RemoteBlobImpl::DispatchToTarget(nsIRunnable* aRunnable)
+{
+ MOZ_ASSERT(aRunnable);
+
+ // We have to protected mWorkerPrivate because this method can be called by
+ // any thread (sort of).
+ MutexAutoLock lock(mMutex);
+
+ if (mWorkerPrivate) {
+ MOZ_ASSERT(mWorkerHolder);
+
+ RefPtr<RemoteBlobControlRunnable> controlRunnable =
+ new RemoteBlobControlRunnable(mWorkerPrivate, aRunnable);
+ if (!controlRunnable->Dispatch()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIEventTarget> target = BaseRemoteBlobImpl()->GetActorEventTarget();
+ if (!target) {
+ target = do_GetMainThread();
+ }
+
+ MOZ_ASSERT(target);
+
+ return target->Dispatch(aRunnable, NS_DISPATCH_NORMAL);
+}
+
+void
+BlobChild::
+RemoteBlobImpl::WorkerHasNotified()
+{
+ MutexAutoLock lock(mMutex);
+
+ mWorkerHolder->ReleaseWorker();
+
+ mWorkerHolder = nullptr;
+ mWorkerPrivate = 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);
+
+ RefPtr<RemoteBlobImpl> baseRemoteBlobImpl =
+ mRemoteBlobImpl->BaseRemoteBlobImpl();
+ MOZ_ASSERT(baseRemoteBlobImpl);
+
+ if (EventTargetIsOnCurrentThread(baseRemoteBlobImpl->GetActorEventTarget())) {
+ RunInternal(baseRemoteBlobImpl, false);
+ } else {
+ nsresult rv = baseRemoteBlobImpl->DispatchToTarget(this);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ DebugOnly<bool> warned = false;
+
+ {
+ MonitorAutoLock lock(mMonitor);
+
+ while (!mDone) {
+#ifdef DEBUG
+ if (!warned) {
+ NS_WARNING("RemoteBlobImpl::GetInternalStream() called on thread "
+ "that can't send messages, blocking here to wait for the "
+ "actor's thread to send the message!");
+ }
+#endif
+ 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);
+ MOZ_ASSERT(aBaseRemoteBlobImpl->ActorEventTargetIsOnCurrentThread());
+ MOZ_ASSERT(!mInputStream);
+ MOZ_ASSERT(!mDone);
+
+ if (BlobChild* actor = aBaseRemoteBlobImpl->GetActor()) {
+ RefPtr<RemoteInputStream> stream;
+
+ if (!NS_IsMainThread() && GetCurrentThreadWorkerPrivate()) {
+ stream =
+ new RemoteInputStream(actor, mRemoteBlobImpl, mStart, mLength);
+ } else {
+ stream = new RemoteInputStream(mRemoteBlobImpl, mStart, mLength);
+ }
+
+ 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_IMETHODIMP
+BlobChild::RemoteBlobImpl::
+CreateStreamHelper::Run()
+{
+ MOZ_ASSERT(mRemoteBlobImpl);
+ MOZ_ASSERT(mRemoteBlobImpl->ActorEventTargetIsOnCurrentThread());
+
+ RefPtr<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;
+}
+
+void
+BlobChild::
+RemoteBlobSliceImpl::EnsureActorWasCreatedInternal()
+{
+ MOZ_ASSERT(ActorEventTargetIsOnCurrentThread());
+ MOZ_ASSERT(!mActorWasCreated);
+
+ mActorWasCreated = true;
+
+ BlobChild* baseActor = mParent->GetActor();
+ MOZ_ASSERT(baseActor);
+ MOZ_ASSERT(baseActor->HasManager());
+
+ nsID id;
+ MOZ_ALWAYS_SUCCEEDS(gUUIDGenerator->GenerateUUIDInPlace(&id));
+
+ ParentBlobConstructorParams params(
+ SlicedBlobConstructorParams(nullptr /* sourceParent */,
+ baseActor /* sourceChild */,
+ id /* id */,
+ mStart /* begin */,
+ mStart + mLength /* end */,
+ mContentType /* contentType */));
+
+ BlobChild* actor;
+
+ if (nsIContentChild* contentManager = baseActor->GetContentManager()) {
+ actor = SendSliceConstructor(contentManager, this, params);
+ } else {
+ actor =
+ SendSliceConstructor(baseActor->GetBackgroundManager(), this, params);
+ }
+
+ CommonInit(actor);
+}
+
+NS_IMPL_ISUPPORTS_INHERITED0(BlobChild::RemoteBlobSliceImpl,
+ BlobChild::RemoteBlobImpl)
+
+BlobChild*
+BlobChild::
+RemoteBlobSliceImpl::GetBlobChild()
+{
+ EnsureActorWasCreated();
+
+ return RemoteBlobImpl::GetBlobChild();
+}
+
+/*******************************************************************************
+ * BlobParent::RemoteBlobImpl
+ ******************************************************************************/
+
+BlobParent::
+RemoteBlobImpl::RemoteBlobImpl(BlobParent* aActor, BlobImpl* 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 =
+ NewNonOwningRunnableMethod(this, &RemoteBlobImpl::Destroy);
+
+ if (mActorTarget) {
+ destroyRunnable =
+ new CancelableRunnableWrapper(destroyRunnable, mActorTarget);
+
+ MOZ_ALWAYS_SUCCEEDS(mActorTarget->Dispatch(destroyRunnable,
+ NS_DISPATCH_NORMAL));
+ } else {
+ MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(destroyRunnable));
+ }
+}
+
+NS_IMPL_ADDREF(BlobParent::RemoteBlobImpl)
+NS_IMPL_RELEASE_WITH_DESTROY(BlobParent::RemoteBlobImpl, Destroy())
+NS_IMPL_QUERY_INTERFACE_INHERITED(BlobParent::RemoteBlobImpl,
+ BlobImpl,
+ nsIRemoteBlob)
+
+void
+BlobParent::
+RemoteBlobImpl::GetName(nsAString& aName) const
+{
+ mBlobImpl->GetName(aName);
+}
+
+void
+BlobParent::
+RemoteBlobImpl::GetDOMPath(nsAString& aPath) const
+{
+ mBlobImpl->GetDOMPath(aPath);
+}
+
+void
+BlobParent::
+RemoteBlobImpl::SetDOMPath(const nsAString& aPath)
+{
+ mBlobImpl->SetDOMPath(aPath);
+}
+
+int64_t
+BlobParent::
+RemoteBlobImpl::GetLastModified(ErrorResult& aRv)
+{
+ return mBlobImpl->GetLastModified(aRv);
+}
+
+void
+BlobParent::
+RemoteBlobImpl::SetLastModified(int64_t aLastModified)
+{
+ MOZ_CRASH("SetLastModified of a remote blob is not allowed!");
+}
+
+void
+BlobParent::
+RemoteBlobImpl::GetMozFullPath(nsAString& aName, ErrorResult& aRv) const
+{
+ mBlobImpl->GetMozFullPath(aName, aRv);
+}
+
+void
+BlobParent::
+RemoteBlobImpl::GetMozFullPathInternal(nsAString& aFileName, ErrorResult& aRv) const
+{
+ mBlobImpl->GetMozFullPathInternal(aFileName, aRv);
+}
+
+bool
+BlobParent::
+RemoteBlobImpl::IsDirectory() const
+{
+ return mBlobImpl->IsDirectory();
+}
+
+uint64_t
+BlobParent::
+RemoteBlobImpl::GetSize(ErrorResult& aRv)
+{
+ return mBlobImpl->GetSize(aRv);
+}
+
+void
+BlobParent::
+RemoteBlobImpl::GetType(nsAString& aType)
+{
+ mBlobImpl->GetType(aType);
+}
+
+uint64_t
+BlobParent::
+RemoteBlobImpl::GetSerialNumber() const
+{
+ return mBlobImpl->GetSerialNumber();
+}
+
+already_AddRefed<BlobImpl>
+BlobParent::
+RemoteBlobImpl::CreateSlice(uint64_t aStart,
+ uint64_t aLength,
+ const nsAString& aContentType,
+ ErrorResult& aRv)
+{
+ return mBlobImpl->CreateSlice(aStart, aLength, aContentType, aRv);
+}
+
+const nsTArray<RefPtr<BlobImpl>>*
+BlobParent::
+RemoteBlobImpl::GetSubBlobImpls() const
+{
+ return mBlobImpl->GetSubBlobImpls();
+}
+
+void
+BlobParent::
+RemoteBlobImpl::GetInternalStream(nsIInputStream** aStream, ErrorResult& aRv)
+{
+ mBlobImpl->GetInternalStream(aStream, aRv);
+}
+
+int64_t
+BlobParent::
+RemoteBlobImpl::GetFileId()
+{
+ return mBlobImpl->GetFileId();
+}
+
+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,
+ int64_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();
+}
+
+bool
+BlobParent::
+RemoteBlobImpl::MayBeClonedToOtherThreads() const
+{
+ return mBlobImpl->MayBeClonedToOtherThreads();
+}
+
+BlobChild*
+BlobParent::
+RemoteBlobImpl::GetBlobChild()
+{
+ return nullptr;
+}
+
+BlobParent*
+BlobParent::
+RemoteBlobImpl::GetBlobParent()
+{
+ return mActor;
+}
+
+/*******************************************************************************
+ * BlobChild
+ ******************************************************************************/
+
+BlobChild::BlobChild(nsIContentChild* aManager, BlobImpl* aBlobImpl)
+ : mBackgroundManager(nullptr)
+ , mContentManager(aManager)
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aManager);
+
+ CommonInit(aBlobImpl);
+}
+
+BlobChild::BlobChild(PBackgroundChild* aManager, BlobImpl* aBlobImpl)
+ : mBackgroundManager(aManager)
+ , mContentManager(nullptr)
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aManager);
+
+ if (!NS_IsMainThread()) {
+ mEventTarget = do_GetCurrentThread();
+ MOZ_ASSERT(mEventTarget);
+ }
+
+ CommonInit(aBlobImpl);
+}
+
+BlobChild::BlobChild(nsIContentChild* aManager, BlobChild* aOther)
+ : mBackgroundManager(nullptr)
+ , mContentManager(aManager)
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aManager);
+
+ CommonInit(aOther, /* aBlobImpl */ nullptr);
+}
+
+BlobChild::BlobChild(PBackgroundChild* aManager,
+ BlobChild* aOther,
+ BlobImpl* aBlobImpl)
+ : mBackgroundManager(aManager)
+ , mContentManager(nullptr)
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aManager);
+ MOZ_ASSERT(aBlobImpl);
+
+ if (!NS_IsMainThread()) {
+ mEventTarget = do_GetCurrentThread();
+ MOZ_ASSERT(mEventTarget);
+ }
+
+ CommonInit(aOther, aBlobImpl);
+}
+
+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);
+
+ if (!NS_IsMainThread()) {
+ mEventTarget = do_GetCurrentThread();
+ MOZ_ASSERT(mEventTarget);
+ }
+
+ CommonInit(aParams);
+}
+
+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);
+
+ if (!NS_IsMainThread()) {
+ mEventTarget = do_GetCurrentThread();
+ MOZ_ASSERT(mEventTarget);
+ }
+
+ CommonInit(aParentID, aRemoteBlobSliceImpl);
+}
+
+BlobChild::~BlobChild()
+{
+ AssertIsOnOwningThread();
+
+ MOZ_COUNT_DTOR(BlobChild);
+}
+
+void
+BlobChild::CommonInit(BlobImpl* 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, BlobImpl* aBlobImpl)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(aOther);
+ MOZ_ASSERT_IF(mContentManager, aOther->GetBackgroundManager());
+ MOZ_ASSERT_IF(mContentManager, !aBlobImpl);
+ MOZ_ASSERT_IF(mBackgroundManager, aBlobImpl);
+
+ RefPtr<BlobImpl> otherImpl;
+ if (mBackgroundManager && aOther->GetBackgroundManager()) {
+ otherImpl = aBlobImpl;
+ } else {
+ otherImpl = aOther->GetBlobImpl();
+ }
+ MOZ_ASSERT(otherImpl);
+
+ nsString contentType;
+ otherImpl->GetType(contentType);
+
+ ErrorResult rv;
+ uint64_t length = otherImpl->GetSize(rv);
+ MOZ_ASSERT(!rv.Failed());
+
+ RemoteBlobImpl* remoteBlob = nullptr;
+ if (otherImpl->IsFile()) {
+ nsAutoString name;
+ otherImpl->GetName(name);
+
+ nsAutoString domPath;
+ otherImpl->GetDOMPath(domPath);
+
+ int64_t modDate = otherImpl->GetLastModified(rv);
+ MOZ_ASSERT(!rv.Failed());
+
+ RemoteBlobImpl::BlobImplIsDirectory directory = otherImpl->IsDirectory() ?
+ RemoteBlobImpl::BlobImplIsDirectory::eDirectory :
+ RemoteBlobImpl::BlobImplIsDirectory::eNotDirectory;
+
+ remoteBlob =
+ new RemoteBlobImpl(this, otherImpl, name, contentType, domPath,
+ length, modDate, directory,
+ false /* SameProcessBlobImpl */);
+ } else {
+ remoteBlob = new RemoteBlobImpl(this, otherImpl, contentType, length,
+ false /* SameProcessBlobImpl */);
+ }
+
+ // This RemoteBlob must be kept alive untill RecvCreatedFromKnownBlob is
+ // called because the parent will send this notification and we must be able
+ // to manage it.
+ MOZ_ASSERT(remoteBlob);
+ remoteBlob->AddRef();
+
+ 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);
+
+ RefPtr<RemoteBlobImpl> remoteBlob;
+
+ switch (paramsType) {
+ case AnyBlobConstructorParams::TNormalBlobConstructorParams: {
+ const NormalBlobConstructorParams& params =
+ blobParams.get_NormalBlobConstructorParams();
+ remoteBlob =
+ new RemoteBlobImpl(this, nullptr, params.contentType(), params.length(),
+ false /* SameProcessBlobImpl */);
+ break;
+ }
+
+ case AnyBlobConstructorParams::TFileBlobConstructorParams: {
+ const FileBlobConstructorParams& params =
+ blobParams.get_FileBlobConstructorParams();
+ RemoteBlobImpl::BlobImplIsDirectory directory = params.isDirectory() ?
+ RemoteBlobImpl::BlobImplIsDirectory::eDirectory :
+ RemoteBlobImpl::BlobImplIsDirectory::eNotDirectory;
+ remoteBlob = new RemoteBlobImpl(this,
+ nullptr,
+ params.name(),
+ params.contentType(),
+ params.path(),
+ params.length(),
+ params.modDate(),
+ directory,
+ false /* SameProcessBlobImpl */);
+ break;
+ }
+
+ case AnyBlobConstructorParams::TSameProcessBlobConstructorParams: {
+ MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
+
+ const SameProcessBlobConstructorParams& params =
+ blobParams.get_SameProcessBlobConstructorParams();
+ MOZ_ASSERT(params.addRefedBlobImpl());
+
+ RefPtr<BlobImpl> blobImpl =
+ dont_AddRef(reinterpret_cast<BlobImpl*>(params.addRefedBlobImpl()));
+
+ ErrorResult rv;
+ uint64_t size = blobImpl->GetSize(rv);
+ MOZ_ASSERT(!rv.Failed());
+
+ nsString contentType;
+ blobImpl->GetType(contentType);
+
+ if (blobImpl->IsFile()) {
+ nsAutoString name;
+ blobImpl->GetName(name);
+
+ nsAutoString domPath;
+ blobImpl->GetDOMPath(domPath);
+
+ int64_t lastModifiedDate = blobImpl->GetLastModified(rv);
+ MOZ_ASSERT(!rv.Failed());
+
+ RemoteBlobImpl::BlobImplIsDirectory directory =
+ blobImpl->IsDirectory() ?
+ RemoteBlobImpl::BlobImplIsDirectory::eDirectory :
+ RemoteBlobImpl::BlobImplIsDirectory::eNotDirectory;
+
+ remoteBlob =
+ new RemoteBlobImpl(this,
+ blobImpl,
+ name,
+ contentType,
+ domPath,
+ size,
+ lastModifiedDate,
+ directory,
+ true /* SameProcessBlobImpl */);
+ } else {
+ remoteBlob = new RemoteBlobImpl(this, blobImpl, contentType, size,
+ true /* SameProcessBlobImpl */);
+ }
+
+ 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);
+
+ RefPtr<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_IsParentProcess());
+
+ CommonStartup();
+}
+
+// static
+BlobChild*
+BlobChild::GetOrCreate(nsIContentChild* aManager, BlobImpl* aBlobImpl)
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aManager);
+
+ return GetOrCreateFromImpl(aManager, aBlobImpl);
+}
+
+// static
+BlobChild*
+BlobChild::GetOrCreate(PBackgroundChild* aManager, BlobImpl* 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,
+ BlobImpl* aBlobImpl)
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aManager);
+ MOZ_ASSERT(aBlobImpl);
+
+ // If the blob represents a wrapper around real blob implementation (so called
+ // snapshot) then we need to get the real one.
+ if (nsCOMPtr<PIBlobImplSnapshot> snapshot = do_QueryInterface(aBlobImpl)) {
+ aBlobImpl = snapshot->GetBlobImpl();
+ if (!aBlobImpl) {
+ // The snapshot is not valid anymore.
+ return nullptr;
+ }
+ }
+
+ // 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, aBlobImpl);
+ 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;
+ nsTArray<UniquePtr<AutoIPCStream>> autoIPCStreams;
+
+ if (gProcessType == GeckoProcessType_Default) {
+ RefPtr<BlobImpl> sameProcessImpl = aBlobImpl;
+ auto addRefedBlobImpl =
+ reinterpret_cast<intptr_t>(sameProcessImpl.forget().take());
+
+ blobParams = SameProcessBlobConstructorParams(addRefedBlobImpl);
+ } else {
+ // BlobData is going to be populate here and it _must_ be send via IPC in
+ // order to avoid leaks.
+ BlobData blobData;
+ BlobDataFromBlobImpl(aManager, aBlobImpl, blobData, autoIPCStreams);
+
+ nsString contentType;
+ aBlobImpl->GetType(contentType);
+
+ ErrorResult rv;
+ uint64_t length = aBlobImpl->GetSize(rv);
+ MOZ_ASSERT(!rv.Failed());
+
+ if (aBlobImpl->IsFile()) {
+ nsAutoString name;
+ aBlobImpl->GetName(name);
+
+ nsAutoString domPath;
+ aBlobImpl->GetDOMPath(domPath);
+
+ int64_t modDate = aBlobImpl->GetLastModified(rv);
+ MOZ_ASSERT(!rv.Failed());
+
+ blobParams =
+ FileBlobConstructorParams(name, contentType, domPath, length, modDate,
+ aBlobImpl->IsDirectory(), blobData);
+ } else {
+ blobParams = NormalBlobConstructorParams(contentType, length, blobData);
+ }
+ }
+
+ BlobChild* actor = new BlobChild(aManager, aBlobImpl);
+
+ ParentBlobConstructorParams params(blobParams);
+
+ if (NS_WARN_IF(!aManager->SendPBlobConstructor(actor, params))) {
+ return nullptr;
+ }
+
+ autoIPCStreams.Clear();
+ 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;
+ }
+
+ return nullptr;
+}
+
+// static
+BlobChild*
+BlobChild::MaybeGetActorFromRemoteBlob(nsIRemoteBlob* aRemoteBlob,
+ nsIContentChild* aManager,
+ BlobImpl* aBlobImpl)
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aRemoteBlob);
+ MOZ_ASSERT(aManager);
+ MOZ_ASSERT(aBlobImpl);
+
+ 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,
+ BlobImpl* aBlobImpl)
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aRemoteBlob);
+ MOZ_ASSERT(aManager);
+ MOZ_ASSERT(aBlobImpl);
+
+ if (BlobChild* actor = aRemoteBlob->GetBlobChild()) {
+ if (actor->GetBackgroundManager() == aManager) {
+ return actor;
+ }
+
+ actor = new BlobChild(aManager, actor, aBlobImpl);
+
+ ParentBlobConstructorParams params(
+ KnownBlobConstructorParams(actor->ParentID()));
+
+ aManager->SendPBlobConstructor(actor, params);
+
+ return actor;
+ }
+
+ return nullptr;
+}
+
+const nsID&
+BlobChild::ParentID() const
+{
+ MOZ_ASSERT(mRemoteBlobImpl);
+
+ return mParentID;
+}
+
+already_AddRefed<BlobImpl>
+BlobChild::GetBlobImpl()
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mBlobImpl);
+
+ RefPtr<BlobImpl> 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,
+ int64_t aLastModifiedDate)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mBlobImpl);
+ MOZ_ASSERT(!mBlobImpl->IsDirectory());
+ MOZ_ASSERT(mRemoteBlobImpl);
+ MOZ_ASSERT(!mRemoteBlobImpl->IsDirectory());
+ MOZ_ASSERT(aLastModifiedDate != INT64_MAX);
+
+ mBlobImpl->SetLazyData(aName, aContentType, aLength, aLastModifiedDate);
+
+ FileBlobConstructorParams params(aName,
+ aContentType,
+ EmptyString(),
+ aLength,
+ aLastModifiedDate,
+ mBlobImpl->IsDirectory(),
+ void_t() /* optionalBlobData */);
+ return SendResolveMystery(params);
+}
+
+bool
+BlobChild::SetMysteryBlobInfo(const nsString& aContentType, uint64_t aLength)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mBlobImpl);
+ MOZ_ASSERT(mRemoteBlobImpl);
+
+ mBlobImpl->SetLazyData(NullString(), aContentType, aLength, INT64_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 =
+ NewNonOwningRunnableMethod(this, &BlobChild::NoteDyingRemoteBlobImpl);
+
+ if (mEventTarget) {
+ runnable = new CancelableRunnableWrapper(runnable, mEventTarget);
+
+ MOZ_ALWAYS_SUCCEEDS(mEventTarget->Dispatch(runnable,
+ NS_DISPATCH_NORMAL));
+ } else {
+ MOZ_ALWAYS_SUCCEEDS(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;
+}
+
+bool
+BlobChild::RecvCreatedFromKnownBlob()
+{
+ MOZ_ASSERT(mRemoteBlobImpl);
+
+ // Releasing the other blob now that this blob is fully created.
+ mRemoteBlobImpl->NullifyDifferentProcessBlobImpl();
+
+ // Release the additional reference to ourself that was added in order to
+ // receive this RecvCreatedFromKnownBlob.
+ mRemoteBlobImpl->Release();
+ 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,
+ BlobImpl* aBlobImpl,
+ IDTableEntry* aIDTableEntry)
+ : mBackgroundManager(nullptr)
+ , mContentManager(aManager)
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aManager);
+
+ CommonInit(aBlobImpl, aIDTableEntry);
+}
+
+BlobParent::BlobParent(PBackgroundParent* aManager,
+ BlobImpl* 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->GetBlobImpl());
+
+ MOZ_COUNT_CTOR(BlobParent);
+
+ mBlobImpl = aIDTableEntry->GetBlobImpl();
+ mRemoteBlobImpl = nullptr;
+
+ mBlobImpl->AddRef();
+ mOwnsBlobImpl = true;
+
+ mIDTableEntry = aIDTableEntry;
+}
+
+void
+BlobParent::CommonInit(BlobImpl* 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);
+
+ RefPtr<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_IsParentProcess());
+
+ CommonStartup();
+
+ ClearOnShutdown(&sIDTable);
+
+ sIDTableMutex = new Mutex("BlobParent::sIDTableMutex");
+ ClearOnShutdown(&sIDTableMutex);
+}
+
+// static
+BlobParent*
+BlobParent::GetOrCreate(nsIContentParent* aManager, BlobImpl* aBlobImpl)
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aManager);
+
+ return GetOrCreateFromImpl(aManager, aBlobImpl);
+}
+
+// static
+BlobParent*
+BlobParent::GetOrCreate(PBackgroundParent* aManager, BlobImpl* 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<BlobImpl>
+BlobParent::GetBlobImplForID(const nsID& aID)
+{
+ if (NS_WARN_IF(gProcessType != GeckoProcessType_Default)) {
+ ASSERT_UNLESS_FUZZING();
+ return nullptr;
+ }
+
+ RefPtr<IDTableEntry> idTableEntry = IDTableEntry::Get(aID);
+ if (NS_WARN_IF(!idTableEntry)) {
+ return nullptr;
+ }
+
+ RefPtr<BlobImpl> blobImpl = idTableEntry->GetBlobImpl();
+ MOZ_ASSERT(blobImpl);
+
+ return blobImpl.forget();
+}
+
+// static
+template <class ParentManagerType>
+BlobParent*
+BlobParent::GetOrCreateFromImpl(ParentManagerType* aManager,
+ BlobImpl* aBlobImpl)
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aManager);
+ MOZ_ASSERT(aBlobImpl);
+
+ MOZ_ASSERT(!nsCOMPtr<PIBlobImplSnapshot>(do_QueryInterface(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;
+ }
+
+ AnyBlobConstructorParams blobParams;
+
+ if (ActorManagerIsSameProcess(aManager)) {
+ RefPtr<BlobImpl> sameProcessImpl = aBlobImpl;
+ auto addRefedBlobImpl =
+ reinterpret_cast<intptr_t>(sameProcessImpl.forget().take());
+
+ blobParams = SameProcessBlobConstructorParams(addRefedBlobImpl);
+ } 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()) {
+ nsAutoString name;
+ aBlobImpl->GetName(name);
+
+ nsAutoString domPath;
+ aBlobImpl->GetDOMPath(domPath);
+
+ int64_t modDate = aBlobImpl->GetLastModified(rv);
+ MOZ_ASSERT(!rv.Failed());
+
+ blobParams =
+ FileBlobConstructorParams(name, contentType, domPath, length, modDate,
+ aBlobImpl->IsDirectory(), void_t());
+ } else {
+ blobParams = NormalBlobConstructorParams(contentType, length, void_t());
+ }
+ }
+ }
+
+ nsID id;
+ MOZ_ALWAYS_SUCCEEDS(gUUIDGenerator->GenerateUUIDInPlace(&id));
+
+ RefPtr<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))) {
+ 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;
+ }
+
+ RefPtr<BlobImpl> blobImpl =
+ CreateBlobImpl(aParams,
+ optionalBlobData.get_BlobData(),
+ ActorManagerIsSameProcess(aManager));
+ if (NS_WARN_IF(!blobImpl)) {
+ ASSERT_UNLESS_FUZZING();
+ return nullptr;
+ }
+
+ nsID id;
+ MOZ_ALWAYS_SUCCEEDS(gUUIDGenerator->GenerateUUIDInPlace(&id));
+
+ RefPtr<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);
+
+ RefPtr<BlobImpl> source = actor->GetBlobImpl();
+ MOZ_ASSERT(source);
+
+ ErrorResult rv;
+ RefPtr<BlobImpl> 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_SUCCEEDS(slice->SetMutable(false));
+
+ RefPtr<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();
+
+ RefPtr<IDTableEntry> idTableEntry =
+ IDTableEntry::Get(params.id(), ActorManagerProcessID(aManager));
+ if (NS_WARN_IF(!idTableEntry)) {
+ ASSERT_UNLESS_FUZZING();
+ return nullptr;
+ }
+
+ return new BlobParent(aManager, idTableEntry);
+ }
+
+ case AnyBlobConstructorParams::TSameProcessBlobConstructorParams: {
+ if (NS_WARN_IF(!ActorManagerIsSameProcess(aManager))) {
+ ASSERT_UNLESS_FUZZING();
+ return nullptr;
+ }
+
+ const SameProcessBlobConstructorParams& params =
+ blobParams.get_SameProcessBlobConstructorParams();
+
+ RefPtr<BlobImpl> blobImpl =
+ dont_AddRef(reinterpret_cast<BlobImpl*>(params.addRefedBlobImpl()));
+ MOZ_ASSERT(blobImpl);
+
+ nsID id;
+ MOZ_ALWAYS_SUCCEEDS(gUUIDGenerator->GenerateUUIDInPlace(&id));
+
+ RefPtr<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;
+ }
+
+ 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<BlobImpl>
+BlobParent::GetBlobImpl()
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mBlobImpl);
+
+ RefPtr<BlobImpl> 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 =
+ NewNonOwningRunnableMethod(this, &BlobParent::NoteDyingRemoteBlobImpl);
+
+ if (mEventTarget) {
+ runnable = new CancelableRunnableWrapper(runnable, mEventTarget);
+
+ MOZ_ALWAYS_SUCCEEDS(mEventTarget->Dispatch(runnable,
+ NS_DISPATCH_NORMAL));
+ } else {
+ MOZ_ALWAYS_SUCCEEDS(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);
+
+ auto* actor = static_cast<InputStreamParent*>(aActor);
+
+ // 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;
+ }
+
+ RefPtr<BlobImpl> 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;
+ blobImpl->GetInternalStream(getter_AddRefs(stream), errorResult);
+ 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 actor->Destroy(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;
+ }
+
+ RefPtr<OpenStreamRunnable> runnable =
+ new OpenStreamRunnable(this, actor, 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;
+ }
+
+ mBlobImpl->SetLazyData(NullString(),
+ params.contentType(),
+ params.length(),
+ INT64_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() == INT64_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::RecvBlobStreamSync(const uint64_t& aStart,
+ const uint64_t& aLength,
+ InputStreamParams* aParams,
+ OptionalFileDescriptorSet* aFDs)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mBlobImpl);
+ MOZ_ASSERT(!mRemoteBlobImpl);
+ MOZ_ASSERT(mOwnsBlobImpl);
+
+ bool finished = false;
+
+ {
+ // Calling RecvPBlobStreamConstructor() may synchronously delete the actor
+ // we pass in so don't touch it outside this block.
+ auto* streamActor = new InputStreamParent(&finished, aParams, aFDs);
+
+ if (NS_WARN_IF(!RecvPBlobStreamConstructor(streamActor, aStart, aLength))) {
+ // If RecvPBlobStreamConstructor() returns false then it is our
+ // responsibility to destroy the actor.
+ delete streamActor;
+ return false;
+ }
+ }
+
+ if (finished) {
+ // The actor is already dead and we have already set our out params.
+ return true;
+ }
+
+ // The actor is alive and will be doing asynchronous work to load the stream.
+ // Spin a nested loop here while we wait for it.
+ nsIThread* currentThread = NS_GetCurrentThread();
+ MOZ_ASSERT(currentThread);
+
+ while (!finished) {
+ MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(currentThread));
+ }
+
+ return true;
+}
+
+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.
+
+ nsString filePath;
+ ErrorResult rv;
+ mBlobImpl->GetMozFullPathInternal(filePath, rv);
+ if (NS_WARN_IF(rv.Failed())) {
+ rv.SuppressException();
+ return false;
+ }
+
+ *aFilePath = filePath;
+ return true;
+}
+
+/*******************************************************************************
+ * BlobParent::IDTableEntry
+ ******************************************************************************/
+
+BlobParent::
+IDTableEntry::IDTableEntry(const nsID& aID,
+ intptr_t aProcessID,
+ BlobImpl* 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,
+ BlobImpl* aBlobImpl,
+ bool aMayCreate,
+ bool aMayGet,
+ bool aIgnoreProcessID)
+{
+ MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
+ MOZ_ASSERT(sIDTableMutex);
+ sIDTableMutex->AssertNotCurrentThreadOwns();
+
+ RefPtr<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->GetBlobImpl() == 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& aOptionalSet)
+{
+ MOZ_ASSERT(mRemoteStream);
+ mRemoteStream->AssertIsOnOwningThread();
+
+ nsTArray<FileDescriptor> fds;
+ OptionalFileDescriptorSetToFDs(
+ // XXX Fix this somehow...
+ const_cast<OptionalFileDescriptorSet&>(aOptionalSet),
+ fds);
+
+ nsCOMPtr<nsIInputStream> stream = DeserializeInputStream(aParams, fds);
+ MOZ_ASSERT(stream);
+
+ mRemoteStream->SetStream(stream);
+ return true;
+}
+
+} // namespace dom
+} // namespace mozilla