/* -*- 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 "FileSnapshot.h" #include "IDBDatabase.h" #include "IDBFileHandle.h" #include "IDBMutableFile.h" #include "mozilla/Assertions.h" #include "mozilla/Mutex.h" #include "nsIAsyncInputStream.h" #include "nsICloneableInputStream.h" #include "nsIIPCSerializableInputStream.h" namespace mozilla { namespace dom { namespace indexedDB { using namespace mozilla::ipc; namespace { class StreamWrapper final : public nsIAsyncInputStream, public nsIInputStreamCallback, public nsICloneableInputStream, public nsIIPCSerializableInputStream { class CloseRunnable; nsCOMPtr mOwningThread; nsCOMPtr mInputStream; RefPtr mFileHandle; bool mFinished; // This is needed to call OnInputStreamReady() with the correct inputStream. // It is protected by mutex. nsCOMPtr mAsyncWaitCallback; Mutex mMutex; public: StreamWrapper(nsIInputStream* aInputStream, IDBFileHandle* aFileHandle) : mOwningThread(aFileHandle->GetMutableFile()->Database()->EventTarget()), mInputStream(aInputStream), mFileHandle(aFileHandle), mFinished(false), mMutex("StreamWrapper::mMutex") { AssertIsOnOwningThread(); MOZ_ASSERT(aInputStream); MOZ_ASSERT(aFileHandle); aFileHandle->AssertIsOnOwningThread(); mFileHandle->OnNewRequest(); } private: virtual ~StreamWrapper(); template void SerializeInternal(InputStreamParams& aParams, FileDescriptorArray& aFileDescriptors, bool aDelayedStart, uint32_t aMaxSize, uint32_t* aSizeUsed, M* aManager); bool IsOnOwningThread() const { MOZ_ASSERT(mOwningThread); bool current; return NS_SUCCEEDED(mOwningThread->IsOnCurrentThread(¤t)) && current; } void AssertIsOnOwningThread() const { MOZ_ASSERT(IsOnOwningThread()); } void Finish() { AssertIsOnOwningThread(); if (mFinished) { return; } mFinished = true; mFileHandle->OnRequestFinished(/* aActorDestroyedNormally */ true); } void Destroy() { if (IsOnOwningThread()) { delete this; return; } RefPtr destroyRunnable = NewNonOwningRunnableMethod( "StreamWrapper::Destroy", this, &StreamWrapper::Destroy); MOZ_ALWAYS_SUCCEEDS( mOwningThread->Dispatch(destroyRunnable, NS_DISPATCH_NORMAL)); } bool IsCloneableInputStream() const { nsCOMPtr stream = do_QueryInterface(mInputStream); return !!stream; } bool IsIPCSerializableInputStream() const { nsCOMPtr stream = do_QueryInterface(mInputStream); return !!stream; } bool IsAsyncInputStream() const { nsCOMPtr stream = do_QueryInterface(mInputStream); return !!stream; } NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSIINPUTSTREAM NS_DECL_NSIASYNCINPUTSTREAM NS_DECL_NSIINPUTSTREAMCALLBACK NS_DECL_NSICLONEABLEINPUTSTREAM NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM }; class StreamWrapper::CloseRunnable final : public Runnable { friend class StreamWrapper; RefPtr mStreamWrapper; public: NS_INLINE_DECL_REFCOUNTING_INHERITED(CloseRunnable, Runnable) private: explicit CloseRunnable(StreamWrapper* aStreamWrapper) : Runnable("StreamWrapper::CloseRunnable"), mStreamWrapper(aStreamWrapper) {} ~CloseRunnable() {} NS_IMETHOD Run() override; }; } // anonymous namespace BlobImplSnapshot::BlobImplSnapshot(BlobImpl* aFileImpl, IDBFileHandle* aFileHandle) : mBlobImpl(aFileImpl) { MOZ_ASSERT(aFileImpl); MOZ_ASSERT(aFileHandle); mFileHandle = do_GetWeakReference(NS_ISUPPORTS_CAST(EventTarget*, aFileHandle)); } BlobImplSnapshot::BlobImplSnapshot(BlobImpl* aFileImpl, nsIWeakReference* aFileHandle) : mBlobImpl(aFileImpl), mFileHandle(aFileHandle) { MOZ_ASSERT(aFileImpl); MOZ_ASSERT(aFileHandle); } BlobImplSnapshot::~BlobImplSnapshot() {} NS_IMPL_ISUPPORTS_INHERITED(BlobImplSnapshot, BlobImpl, PIBlobImplSnapshot) already_AddRefed BlobImplSnapshot::CreateSlice( uint64_t aStart, uint64_t aLength, const nsAString& aContentType, ErrorResult& aRv) { RefPtr blobImpl = mBlobImpl->CreateSlice(aStart, aLength, aContentType, aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } blobImpl = new BlobImplSnapshot(blobImpl, mFileHandle); return blobImpl.forget(); } void BlobImplSnapshot::CreateInputStream(nsIInputStream** aStream, ErrorResult& aRv) { nsCOMPtr et = do_QueryReferent(mFileHandle); RefPtr fileHandle = static_cast(et.get()); if (!fileHandle || !fileHandle->IsOpen()) { aRv.Throw(NS_ERROR_DOM_FILEHANDLE_INACTIVE_ERR); return; } nsCOMPtr stream; mBlobImpl->CreateInputStream(getter_AddRefs(stream), aRv); if (NS_WARN_IF(aRv.Failed())) { return; } RefPtr wrapper = new StreamWrapper(stream, fileHandle); wrapper.forget(aStream); } BlobImpl* BlobImplSnapshot::GetBlobImpl() const { nsCOMPtr et = do_QueryReferent(mFileHandle); RefPtr fileHandle = static_cast(et.get()); if (!fileHandle || !fileHandle->IsOpen()) { return nullptr; } return mBlobImpl; } void BlobImplSnapshot::GetBlobImplType(nsAString& aBlobImplType) const { aBlobImplType.AssignLiteral("BlobImplSnapshot["); nsAutoString blobImplType; mBlobImpl->GetBlobImplType(blobImplType); aBlobImplType.Append(blobImplType); aBlobImplType.AppendLiteral("]"); } StreamWrapper::~StreamWrapper() { AssertIsOnOwningThread(); Finish(); } NS_IMPL_ADDREF(StreamWrapper) NS_IMPL_RELEASE_WITH_DESTROY(StreamWrapper, Destroy()) NS_INTERFACE_MAP_BEGIN(StreamWrapper) NS_INTERFACE_MAP_ENTRY(nsIInputStream) NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAsyncInputStream, IsAsyncInputStream()) NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamCallback, IsAsyncInputStream()) NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsICloneableInputStream, IsCloneableInputStream()) NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIPCSerializableInputStream, IsIPCSerializableInputStream()) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream) NS_INTERFACE_MAP_END NS_IMETHODIMP StreamWrapper::Close() { RefPtr closeRunnable = new CloseRunnable(this); MOZ_ALWAYS_SUCCEEDS( mOwningThread->Dispatch(closeRunnable, NS_DISPATCH_NORMAL)); return NS_OK; } NS_IMETHODIMP StreamWrapper::Available(uint64_t* _retval) { return mInputStream->Available(_retval); } NS_IMETHODIMP StreamWrapper::Read(char* aBuf, uint32_t aCount, uint32_t* _retval) { return mInputStream->Read(aBuf, aCount, _retval); } NS_IMETHODIMP StreamWrapper::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure, uint32_t aCount, uint32_t* _retval) { return mInputStream->ReadSegments(aWriter, aClosure, aCount, _retval); } NS_IMETHODIMP StreamWrapper::IsNonBlocking(bool* _retval) { return mInputStream->IsNonBlocking(_retval); } void StreamWrapper::Serialize(InputStreamParams& aParams, FileDescriptorArray& aFileDescriptors, bool aDelayedStart, uint32_t aMaxSize, uint32_t* aSizeUsed, nsIContentChild* aManager) { SerializeInternal(aParams, aFileDescriptors, aDelayedStart, aMaxSize, aSizeUsed, aManager); } void StreamWrapper::Serialize(InputStreamParams& aParams, FileDescriptorArray& aFileDescriptors, bool aDelayedStart, uint32_t aMaxSize, uint32_t* aSizeUsed, PBackgroundChild* aManager) { SerializeInternal(aParams, aFileDescriptors, aDelayedStart, aMaxSize, aSizeUsed, aManager); } void StreamWrapper::Serialize(InputStreamParams& aParams, FileDescriptorArray& aFileDescriptors, bool aDelayedStart, uint32_t aMaxSize, uint32_t* aSizeUsed, nsIContentParent* aManager) { SerializeInternal(aParams, aFileDescriptors, aDelayedStart, aMaxSize, aSizeUsed, aManager); } void StreamWrapper::Serialize(InputStreamParams& aParams, FileDescriptorArray& aFileDescriptors, bool aDelayedStart, uint32_t aMaxSize, uint32_t* aSizeUsed, PBackgroundParent* aManager) { SerializeInternal(aParams, aFileDescriptors, aDelayedStart, aMaxSize, aSizeUsed, aManager); } template void StreamWrapper::SerializeInternal(InputStreamParams& aParams, FileDescriptorArray& aFileDescriptors, bool aDelayedStart, uint32_t aMaxSize, uint32_t* aSizeUsed, M* aManager) { MOZ_ASSERT(aSizeUsed); *aSizeUsed = 0; nsCOMPtr stream = do_QueryInterface(mInputStream); if (stream) { stream->Serialize(aParams, aFileDescriptors, aDelayedStart, aMaxSize, aSizeUsed, aManager); } } bool StreamWrapper::Deserialize(const InputStreamParams& aParams, const FileDescriptorArray& aFileDescriptors) { MOZ_CRASH("This method should never be called"); return false; } NS_IMETHODIMP StreamWrapper::CloseWithStatus(nsresult aStatus) { nsCOMPtr stream = do_QueryInterface(mInputStream); if (!stream) { return NS_ERROR_NO_INTERFACE; } nsresult rv = stream->CloseWithStatus(aStatus); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } return Close(); } NS_IMETHODIMP StreamWrapper::AsyncWait(nsIInputStreamCallback* aCallback, uint32_t aFlags, uint32_t aRequestedCount, nsIEventTarget* aEventTarget) { nsCOMPtr stream = do_QueryInterface(mInputStream); if (!stream) { return NS_ERROR_NO_INTERFACE; } nsCOMPtr callback = aCallback ? this : nullptr; { MutexAutoLock lock(mMutex); if (mAsyncWaitCallback && aCallback) { return NS_ERROR_FAILURE; } mAsyncWaitCallback = aCallback; } return stream->AsyncWait(callback, aFlags, aRequestedCount, aEventTarget); } // nsIInputStreamCallback NS_IMETHODIMP StreamWrapper::OnInputStreamReady(nsIAsyncInputStream* aStream) { nsCOMPtr stream = do_QueryInterface(mInputStream); if (!stream) { return NS_ERROR_NO_INTERFACE; } nsCOMPtr callback; { MutexAutoLock lock(mMutex); // We have been canceled in the meanwhile. if (!mAsyncWaitCallback) { return NS_OK; } callback.swap(mAsyncWaitCallback); } MOZ_ASSERT(callback); return callback->OnInputStreamReady(this); } // nsICloneableInputStream NS_IMETHODIMP StreamWrapper::GetCloneable(bool* aCloneable) { nsCOMPtr stream = do_QueryInterface(mInputStream); if (!stream) { *aCloneable = false; return NS_ERROR_NO_INTERFACE; } return stream->GetCloneable(aCloneable); } NS_IMETHODIMP StreamWrapper::Clone(nsIInputStream** aResult) { nsCOMPtr stream = do_QueryInterface(mInputStream); if (!stream) { return NS_ERROR_NO_INTERFACE; } return stream->Clone(aResult); } NS_IMETHODIMP StreamWrapper::CloseRunnable::Run() { mStreamWrapper->Finish(); return NS_OK; } } // namespace indexedDB } // namespace dom } // namespace mozilla