michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "FileStreamWrappers.h" michael@0: michael@0: #include "nsIFileStorage.h" michael@0: #include "nsISeekableStream.h" michael@0: #include "mozilla/Attributes.h" michael@0: michael@0: #include "FileHelper.h" michael@0: michael@0: USING_FILE_NAMESPACE michael@0: michael@0: namespace { michael@0: michael@0: class ProgressRunnable MOZ_FINAL : public nsIRunnable michael@0: { michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: NS_DECL_NSIRUNNABLE michael@0: michael@0: ProgressRunnable(FileHelper* aFileHelper, michael@0: uint64_t aProgress, michael@0: uint64_t aProgressMax) michael@0: : mFileHelper(aFileHelper), michael@0: mProgress(aProgress), michael@0: mProgressMax(aProgressMax) michael@0: { michael@0: } michael@0: michael@0: private: michael@0: nsRefPtr mFileHelper; michael@0: uint64_t mProgress; michael@0: uint64_t mProgressMax; michael@0: }; michael@0: michael@0: class CloseRunnable MOZ_FINAL : public nsIRunnable michael@0: { michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: NS_DECL_NSIRUNNABLE michael@0: michael@0: CloseRunnable(FileHelper* aFileHelper) michael@0: : mFileHelper(aFileHelper) michael@0: { } michael@0: michael@0: private: michael@0: nsRefPtr mFileHelper; michael@0: }; michael@0: michael@0: class DestroyRunnable MOZ_FINAL : public nsIRunnable michael@0: { michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: NS_DECL_NSIRUNNABLE michael@0: michael@0: DestroyRunnable(FileHelper* aFileHelper) michael@0: : mFileHelper(aFileHelper) michael@0: { } michael@0: michael@0: private: michael@0: nsRefPtr mFileHelper; michael@0: }; michael@0: michael@0: } // anonymous namespace michael@0: michael@0: FileStreamWrapper::FileStreamWrapper(nsISupports* aFileStream, michael@0: FileHelper* aFileHelper, michael@0: uint64_t aOffset, michael@0: uint64_t aLimit, michael@0: uint32_t aFlags) michael@0: : mFileStream(aFileStream), michael@0: mFileHelper(aFileHelper), michael@0: mOffset(aOffset), michael@0: mLimit(aLimit), michael@0: mFlags(aFlags), michael@0: mFirstTime(true) michael@0: { michael@0: NS_ASSERTION(mFileStream, "Must have a file stream!"); michael@0: NS_ASSERTION(mFileHelper, "Must have a file helper!"); michael@0: } michael@0: michael@0: FileStreamWrapper::~FileStreamWrapper() michael@0: { michael@0: if (mFlags & NOTIFY_DESTROY) { michael@0: if (NS_IsMainThread()) { michael@0: mFileHelper->OnStreamDestroy(); michael@0: } michael@0: else { michael@0: nsCOMPtr runnable = michael@0: new DestroyRunnable(mFileHelper); michael@0: michael@0: nsresult rv = NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL); michael@0: if (NS_FAILED(rv)) { michael@0: NS_WARNING("Failed to dispatch to the main thread!"); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS0(FileStreamWrapper) michael@0: michael@0: FileInputStreamWrapper::FileInputStreamWrapper(nsISupports* aFileStream, michael@0: FileHelper* aFileHelper, michael@0: uint64_t aOffset, michael@0: uint64_t aLimit, michael@0: uint32_t aFlags) michael@0: : FileStreamWrapper(aFileStream, aFileHelper, aOffset, aLimit, aFlags) michael@0: { michael@0: mInputStream = do_QueryInterface(mFileStream); michael@0: NS_ASSERTION(mInputStream, "This should always succeed!"); michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS_INHERITED(FileInputStreamWrapper, michael@0: FileStreamWrapper, michael@0: nsIInputStream) michael@0: michael@0: NS_IMETHODIMP michael@0: FileInputStreamWrapper::Close() michael@0: { michael@0: NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: if (mFlags & NOTIFY_CLOSE) { michael@0: nsCOMPtr runnable = new CloseRunnable(mFileHelper); michael@0: michael@0: if (NS_FAILED(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))) { michael@0: NS_WARNING("Failed to dispatch to the main thread!"); michael@0: } michael@0: } michael@0: michael@0: mOffset = 0; michael@0: mLimit = 0; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: FileInputStreamWrapper::Available(uint64_t* _retval) michael@0: { michael@0: // Performing sync IO on the main thread is generally not allowed. michael@0: // However, the input stream wrapper is also used to track reads performed by michael@0: // other APIs like FileReader, XHR, etc. michael@0: // In that case nsInputStreamChannel::OpenContentStream() calls Available() michael@0: // before setting the content length property. This property is not important michael@0: // to perform reads properly, so we can just return NS_BASE_STREAM_CLOSED michael@0: // here. It causes OpenContentStream() to set the content length property to michael@0: // zero. michael@0: michael@0: if (NS_IsMainThread()) { michael@0: return NS_BASE_STREAM_CLOSED; michael@0: } michael@0: michael@0: return mInputStream->Available(_retval); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: FileInputStreamWrapper::Read(char* aBuf, uint32_t aCount, uint32_t* _retval) michael@0: { michael@0: NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: nsresult rv; michael@0: michael@0: if (mFirstTime) { michael@0: mFirstTime = false; michael@0: michael@0: if (mOffset != UINT64_MAX) { michael@0: nsCOMPtr seekable = do_QueryInterface(mInputStream); michael@0: if (seekable) { michael@0: rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, mOffset); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: } michael@0: michael@0: mOffset = 0; michael@0: } michael@0: michael@0: uint64_t max = mLimit - mOffset; michael@0: if (max == 0) { michael@0: *_retval = 0; michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (aCount > max) { michael@0: aCount = max; michael@0: } michael@0: michael@0: rv = mInputStream->Read(aBuf, aCount, _retval); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: mOffset += *_retval; michael@0: michael@0: if (mFlags & NOTIFY_PROGRESS) { michael@0: nsCOMPtr runnable = michael@0: new ProgressRunnable(mFileHelper, mOffset, mLimit); michael@0: michael@0: rv = NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL); michael@0: if (NS_FAILED(rv)) { michael@0: NS_WARNING("Failed to dispatch to the main thread!"); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: FileInputStreamWrapper::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure, michael@0: uint32_t aCount, uint32_t* _retval) michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: FileInputStreamWrapper::IsNonBlocking(bool* _retval) michael@0: { michael@0: *_retval = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: FileOutputStreamWrapper::FileOutputStreamWrapper(nsISupports* aFileStream, michael@0: FileHelper* aFileHelper, michael@0: uint64_t aOffset, michael@0: uint64_t aLimit, michael@0: uint32_t aFlags) michael@0: : FileStreamWrapper(aFileStream, aFileHelper, aOffset, aLimit, aFlags) michael@0: #ifdef DEBUG michael@0: , mWriteThread(nullptr) michael@0: #endif michael@0: { michael@0: mOutputStream = do_QueryInterface(mFileStream); michael@0: NS_ASSERTION(mOutputStream, "This should always succeed!"); michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS_INHERITED(FileOutputStreamWrapper, michael@0: FileStreamWrapper, michael@0: nsIOutputStream) michael@0: michael@0: NS_IMETHODIMP michael@0: FileOutputStreamWrapper::Close() michael@0: { michael@0: NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: nsresult rv = NS_OK; michael@0: michael@0: if (!mFirstTime) { michael@0: NS_ASSERTION(PR_GetCurrentThread() == mWriteThread, michael@0: "Unsetting thread locals on wrong thread!"); michael@0: mFileHelper->mFileStorage->UnsetThreadLocals(); michael@0: } michael@0: michael@0: if (mFlags & NOTIFY_CLOSE) { michael@0: nsCOMPtr runnable = new CloseRunnable(mFileHelper); michael@0: michael@0: if (NS_FAILED(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))) { michael@0: NS_WARNING("Failed to dispatch to the main thread!"); michael@0: } michael@0: } michael@0: michael@0: mOffset = 0; michael@0: mLimit = 0; michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: FileOutputStreamWrapper::Write(const char* aBuf, uint32_t aCount, michael@0: uint32_t* _retval) michael@0: { michael@0: NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: nsresult rv; michael@0: michael@0: if (mFirstTime) { michael@0: mFirstTime = false; michael@0: michael@0: #ifdef DEBUG michael@0: mWriteThread = PR_GetCurrentThread(); michael@0: #endif michael@0: mFileHelper->mFileStorage->SetThreadLocals(); michael@0: michael@0: nsCOMPtr seekable = do_QueryInterface(mOutputStream); michael@0: if (seekable) { michael@0: if (mOffset == UINT64_MAX) { michael@0: rv = seekable->Seek(nsISeekableStream::NS_SEEK_END, 0); michael@0: } michael@0: else { michael@0: rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, mOffset); michael@0: } michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: mOffset = 0; michael@0: } michael@0: michael@0: uint64_t max = mLimit - mOffset; michael@0: if (max == 0) { michael@0: *_retval = 0; michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (aCount > max) { michael@0: aCount = max; michael@0: } michael@0: michael@0: rv = mOutputStream->Write(aBuf, aCount, _retval); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: mOffset += *_retval; michael@0: michael@0: if (mFlags & NOTIFY_PROGRESS) { michael@0: nsCOMPtr runnable = michael@0: new ProgressRunnable(mFileHelper, mOffset, mLimit); michael@0: michael@0: NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: FileOutputStreamWrapper::Flush() michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: FileOutputStreamWrapper::WriteFrom(nsIInputStream* aFromStream, michael@0: uint32_t aCount, uint32_t* _retval) michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: FileOutputStreamWrapper::WriteSegments(nsReadSegmentFun aReader, michael@0: void* aClosure, uint32_t aCount, michael@0: uint32_t* _retval) michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: FileOutputStreamWrapper::IsNonBlocking(bool* _retval) michael@0: { michael@0: *_retval = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(ProgressRunnable, nsIRunnable) michael@0: michael@0: NS_IMETHODIMP michael@0: ProgressRunnable::Run() michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: mFileHelper->OnStreamProgress(mProgress, mProgressMax); michael@0: mFileHelper = nullptr; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(CloseRunnable, nsIRunnable) michael@0: michael@0: NS_IMETHODIMP michael@0: CloseRunnable::Run() michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: mFileHelper->OnStreamClose(); michael@0: mFileHelper = nullptr; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(DestroyRunnable, nsIRunnable) michael@0: michael@0: NS_IMETHODIMP michael@0: DestroyRunnable::Run() michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: mFileHelper->OnStreamDestroy(); michael@0: mFileHelper = nullptr; michael@0: michael@0: return NS_OK; michael@0: }