/* -*- 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(¤t)))) { 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, ¶ms, &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