1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/file/FileStreamWrappers.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,387 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=2 et sw=2 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.8 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "FileStreamWrappers.h" 1.11 + 1.12 +#include "nsIFileStorage.h" 1.13 +#include "nsISeekableStream.h" 1.14 +#include "mozilla/Attributes.h" 1.15 + 1.16 +#include "FileHelper.h" 1.17 + 1.18 +USING_FILE_NAMESPACE 1.19 + 1.20 +namespace { 1.21 + 1.22 +class ProgressRunnable MOZ_FINAL : public nsIRunnable 1.23 +{ 1.24 +public: 1.25 + NS_DECL_THREADSAFE_ISUPPORTS 1.26 + NS_DECL_NSIRUNNABLE 1.27 + 1.28 + ProgressRunnable(FileHelper* aFileHelper, 1.29 + uint64_t aProgress, 1.30 + uint64_t aProgressMax) 1.31 + : mFileHelper(aFileHelper), 1.32 + mProgress(aProgress), 1.33 + mProgressMax(aProgressMax) 1.34 + { 1.35 + } 1.36 + 1.37 +private: 1.38 + nsRefPtr<FileHelper> mFileHelper; 1.39 + uint64_t mProgress; 1.40 + uint64_t mProgressMax; 1.41 +}; 1.42 + 1.43 +class CloseRunnable MOZ_FINAL : public nsIRunnable 1.44 +{ 1.45 +public: 1.46 + NS_DECL_THREADSAFE_ISUPPORTS 1.47 + NS_DECL_NSIRUNNABLE 1.48 + 1.49 + CloseRunnable(FileHelper* aFileHelper) 1.50 + : mFileHelper(aFileHelper) 1.51 + { } 1.52 + 1.53 +private: 1.54 + nsRefPtr<FileHelper> mFileHelper; 1.55 +}; 1.56 + 1.57 +class DestroyRunnable MOZ_FINAL : public nsIRunnable 1.58 +{ 1.59 +public: 1.60 + NS_DECL_THREADSAFE_ISUPPORTS 1.61 + NS_DECL_NSIRUNNABLE 1.62 + 1.63 + DestroyRunnable(FileHelper* aFileHelper) 1.64 + : mFileHelper(aFileHelper) 1.65 + { } 1.66 + 1.67 +private: 1.68 + nsRefPtr<FileHelper> mFileHelper; 1.69 +}; 1.70 + 1.71 +} // anonymous namespace 1.72 + 1.73 +FileStreamWrapper::FileStreamWrapper(nsISupports* aFileStream, 1.74 + FileHelper* aFileHelper, 1.75 + uint64_t aOffset, 1.76 + uint64_t aLimit, 1.77 + uint32_t aFlags) 1.78 +: mFileStream(aFileStream), 1.79 + mFileHelper(aFileHelper), 1.80 + mOffset(aOffset), 1.81 + mLimit(aLimit), 1.82 + mFlags(aFlags), 1.83 + mFirstTime(true) 1.84 +{ 1.85 + NS_ASSERTION(mFileStream, "Must have a file stream!"); 1.86 + NS_ASSERTION(mFileHelper, "Must have a file helper!"); 1.87 +} 1.88 + 1.89 +FileStreamWrapper::~FileStreamWrapper() 1.90 +{ 1.91 + if (mFlags & NOTIFY_DESTROY) { 1.92 + if (NS_IsMainThread()) { 1.93 + mFileHelper->OnStreamDestroy(); 1.94 + } 1.95 + else { 1.96 + nsCOMPtr<nsIRunnable> runnable = 1.97 + new DestroyRunnable(mFileHelper); 1.98 + 1.99 + nsresult rv = NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL); 1.100 + if (NS_FAILED(rv)) { 1.101 + NS_WARNING("Failed to dispatch to the main thread!"); 1.102 + } 1.103 + } 1.104 + } 1.105 +} 1.106 + 1.107 +NS_IMPL_ISUPPORTS0(FileStreamWrapper) 1.108 + 1.109 +FileInputStreamWrapper::FileInputStreamWrapper(nsISupports* aFileStream, 1.110 + FileHelper* aFileHelper, 1.111 + uint64_t aOffset, 1.112 + uint64_t aLimit, 1.113 + uint32_t aFlags) 1.114 +: FileStreamWrapper(aFileStream, aFileHelper, aOffset, aLimit, aFlags) 1.115 +{ 1.116 + mInputStream = do_QueryInterface(mFileStream); 1.117 + NS_ASSERTION(mInputStream, "This should always succeed!"); 1.118 +} 1.119 + 1.120 +NS_IMPL_ISUPPORTS_INHERITED(FileInputStreamWrapper, 1.121 + FileStreamWrapper, 1.122 + nsIInputStream) 1.123 + 1.124 +NS_IMETHODIMP 1.125 +FileInputStreamWrapper::Close() 1.126 +{ 1.127 + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); 1.128 + 1.129 + if (mFlags & NOTIFY_CLOSE) { 1.130 + nsCOMPtr<nsIRunnable> runnable = new CloseRunnable(mFileHelper); 1.131 + 1.132 + if (NS_FAILED(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))) { 1.133 + NS_WARNING("Failed to dispatch to the main thread!"); 1.134 + } 1.135 + } 1.136 + 1.137 + mOffset = 0; 1.138 + mLimit = 0; 1.139 + 1.140 + return NS_OK; 1.141 +} 1.142 + 1.143 +NS_IMETHODIMP 1.144 +FileInputStreamWrapper::Available(uint64_t* _retval) 1.145 +{ 1.146 + // Performing sync IO on the main thread is generally not allowed. 1.147 + // However, the input stream wrapper is also used to track reads performed by 1.148 + // other APIs like FileReader, XHR, etc. 1.149 + // In that case nsInputStreamChannel::OpenContentStream() calls Available() 1.150 + // before setting the content length property. This property is not important 1.151 + // to perform reads properly, so we can just return NS_BASE_STREAM_CLOSED 1.152 + // here. It causes OpenContentStream() to set the content length property to 1.153 + // zero. 1.154 + 1.155 + if (NS_IsMainThread()) { 1.156 + return NS_BASE_STREAM_CLOSED; 1.157 + } 1.158 + 1.159 + return mInputStream->Available(_retval); 1.160 +} 1.161 + 1.162 +NS_IMETHODIMP 1.163 +FileInputStreamWrapper::Read(char* aBuf, uint32_t aCount, uint32_t* _retval) 1.164 +{ 1.165 + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); 1.166 + 1.167 + nsresult rv; 1.168 + 1.169 + if (mFirstTime) { 1.170 + mFirstTime = false; 1.171 + 1.172 + if (mOffset != UINT64_MAX) { 1.173 + nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mInputStream); 1.174 + if (seekable) { 1.175 + rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, mOffset); 1.176 + NS_ENSURE_SUCCESS(rv, rv); 1.177 + } 1.178 + } 1.179 + 1.180 + mOffset = 0; 1.181 + } 1.182 + 1.183 + uint64_t max = mLimit - mOffset; 1.184 + if (max == 0) { 1.185 + *_retval = 0; 1.186 + return NS_OK; 1.187 + } 1.188 + 1.189 + if (aCount > max) { 1.190 + aCount = max; 1.191 + } 1.192 + 1.193 + rv = mInputStream->Read(aBuf, aCount, _retval); 1.194 + NS_ENSURE_SUCCESS(rv, rv); 1.195 + 1.196 + mOffset += *_retval; 1.197 + 1.198 + if (mFlags & NOTIFY_PROGRESS) { 1.199 + nsCOMPtr<nsIRunnable> runnable = 1.200 + new ProgressRunnable(mFileHelper, mOffset, mLimit); 1.201 + 1.202 + rv = NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL); 1.203 + if (NS_FAILED(rv)) { 1.204 + NS_WARNING("Failed to dispatch to the main thread!"); 1.205 + } 1.206 + } 1.207 + 1.208 + return NS_OK; 1.209 +} 1.210 + 1.211 +NS_IMETHODIMP 1.212 +FileInputStreamWrapper::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure, 1.213 + uint32_t aCount, uint32_t* _retval) 1.214 +{ 1.215 + return NS_ERROR_NOT_IMPLEMENTED; 1.216 +} 1.217 + 1.218 +NS_IMETHODIMP 1.219 +FileInputStreamWrapper::IsNonBlocking(bool* _retval) 1.220 +{ 1.221 + *_retval = false; 1.222 + return NS_OK; 1.223 +} 1.224 + 1.225 +FileOutputStreamWrapper::FileOutputStreamWrapper(nsISupports* aFileStream, 1.226 + FileHelper* aFileHelper, 1.227 + uint64_t aOffset, 1.228 + uint64_t aLimit, 1.229 + uint32_t aFlags) 1.230 +: FileStreamWrapper(aFileStream, aFileHelper, aOffset, aLimit, aFlags) 1.231 +#ifdef DEBUG 1.232 +, mWriteThread(nullptr) 1.233 +#endif 1.234 +{ 1.235 + mOutputStream = do_QueryInterface(mFileStream); 1.236 + NS_ASSERTION(mOutputStream, "This should always succeed!"); 1.237 +} 1.238 + 1.239 +NS_IMPL_ISUPPORTS_INHERITED(FileOutputStreamWrapper, 1.240 + FileStreamWrapper, 1.241 + nsIOutputStream) 1.242 + 1.243 +NS_IMETHODIMP 1.244 +FileOutputStreamWrapper::Close() 1.245 +{ 1.246 + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); 1.247 + 1.248 + nsresult rv = NS_OK; 1.249 + 1.250 + if (!mFirstTime) { 1.251 + NS_ASSERTION(PR_GetCurrentThread() == mWriteThread, 1.252 + "Unsetting thread locals on wrong thread!"); 1.253 + mFileHelper->mFileStorage->UnsetThreadLocals(); 1.254 + } 1.255 + 1.256 + if (mFlags & NOTIFY_CLOSE) { 1.257 + nsCOMPtr<nsIRunnable> runnable = new CloseRunnable(mFileHelper); 1.258 + 1.259 + if (NS_FAILED(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))) { 1.260 + NS_WARNING("Failed to dispatch to the main thread!"); 1.261 + } 1.262 + } 1.263 + 1.264 + mOffset = 0; 1.265 + mLimit = 0; 1.266 + 1.267 + return rv; 1.268 +} 1.269 + 1.270 +NS_IMETHODIMP 1.271 +FileOutputStreamWrapper::Write(const char* aBuf, uint32_t aCount, 1.272 + uint32_t* _retval) 1.273 +{ 1.274 + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); 1.275 + 1.276 + nsresult rv; 1.277 + 1.278 + if (mFirstTime) { 1.279 + mFirstTime = false; 1.280 + 1.281 +#ifdef DEBUG 1.282 + mWriteThread = PR_GetCurrentThread(); 1.283 +#endif 1.284 + mFileHelper->mFileStorage->SetThreadLocals(); 1.285 + 1.286 + nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mOutputStream); 1.287 + if (seekable) { 1.288 + if (mOffset == UINT64_MAX) { 1.289 + rv = seekable->Seek(nsISeekableStream::NS_SEEK_END, 0); 1.290 + } 1.291 + else { 1.292 + rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, mOffset); 1.293 + } 1.294 + NS_ENSURE_SUCCESS(rv, rv); 1.295 + } 1.296 + 1.297 + mOffset = 0; 1.298 + } 1.299 + 1.300 + uint64_t max = mLimit - mOffset; 1.301 + if (max == 0) { 1.302 + *_retval = 0; 1.303 + return NS_OK; 1.304 + } 1.305 + 1.306 + if (aCount > max) { 1.307 + aCount = max; 1.308 + } 1.309 + 1.310 + rv = mOutputStream->Write(aBuf, aCount, _retval); 1.311 + NS_ENSURE_SUCCESS(rv, rv); 1.312 + 1.313 + mOffset += *_retval; 1.314 + 1.315 + if (mFlags & NOTIFY_PROGRESS) { 1.316 + nsCOMPtr<nsIRunnable> runnable = 1.317 + new ProgressRunnable(mFileHelper, mOffset, mLimit); 1.318 + 1.319 + NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL); 1.320 + } 1.321 + 1.322 + return NS_OK; 1.323 +} 1.324 + 1.325 +NS_IMETHODIMP 1.326 +FileOutputStreamWrapper::Flush() 1.327 +{ 1.328 + return NS_OK; 1.329 +} 1.330 + 1.331 +NS_IMETHODIMP 1.332 +FileOutputStreamWrapper::WriteFrom(nsIInputStream* aFromStream, 1.333 + uint32_t aCount, uint32_t* _retval) 1.334 +{ 1.335 + return NS_ERROR_NOT_IMPLEMENTED; 1.336 +} 1.337 + 1.338 +NS_IMETHODIMP 1.339 +FileOutputStreamWrapper::WriteSegments(nsReadSegmentFun aReader, 1.340 + void* aClosure, uint32_t aCount, 1.341 + uint32_t* _retval) 1.342 +{ 1.343 + return NS_ERROR_NOT_IMPLEMENTED; 1.344 +} 1.345 + 1.346 +NS_IMETHODIMP 1.347 +FileOutputStreamWrapper::IsNonBlocking(bool* _retval) 1.348 +{ 1.349 + *_retval = false; 1.350 + return NS_OK; 1.351 +} 1.352 + 1.353 +NS_IMPL_ISUPPORTS(ProgressRunnable, nsIRunnable) 1.354 + 1.355 +NS_IMETHODIMP 1.356 +ProgressRunnable::Run() 1.357 +{ 1.358 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.359 + 1.360 + mFileHelper->OnStreamProgress(mProgress, mProgressMax); 1.361 + mFileHelper = nullptr; 1.362 + 1.363 + return NS_OK; 1.364 +} 1.365 + 1.366 +NS_IMPL_ISUPPORTS(CloseRunnable, nsIRunnable) 1.367 + 1.368 +NS_IMETHODIMP 1.369 +CloseRunnable::Run() 1.370 +{ 1.371 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.372 + 1.373 + mFileHelper->OnStreamClose(); 1.374 + mFileHelper = nullptr; 1.375 + 1.376 + return NS_OK; 1.377 +} 1.378 + 1.379 +NS_IMPL_ISUPPORTS(DestroyRunnable, nsIRunnable) 1.380 + 1.381 +NS_IMETHODIMP 1.382 +DestroyRunnable::Run() 1.383 +{ 1.384 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.385 + 1.386 + mFileHelper->OnStreamDestroy(); 1.387 + mFileHelper = nullptr; 1.388 + 1.389 + return NS_OK; 1.390 +}