1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/file/LockedFile.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1063 @@ 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 "LockedFile.h" 1.11 + 1.12 +#include "AsyncHelper.h" 1.13 +#include "FileHandle.h" 1.14 +#include "FileHelper.h" 1.15 +#include "FileRequest.h" 1.16 +#include "FileService.h" 1.17 +#include "FileStreamWrappers.h" 1.18 +#include "MemoryStreams.h" 1.19 +#include "MetadataHelper.h" 1.20 +#include "mozilla/dom/DOMRequest.h" 1.21 +#include "mozilla/dom/EncodingUtils.h" 1.22 +#include "mozilla/dom/LockedFileBinding.h" 1.23 +#include "mozilla/dom/TypedArray.h" 1.24 +#include "mozilla/dom/UnionTypes.h" 1.25 +#include "mozilla/EventDispatcher.h" 1.26 +#include "nsContentUtils.h" 1.27 +#include "nsError.h" 1.28 +#include "nsIAppShell.h" 1.29 +#include "nsIDOMEvent.h" 1.30 +#include "nsIDOMFile.h" 1.31 +#include "nsIFileStorage.h" 1.32 +#include "nsISeekableStream.h" 1.33 +#include "nsNetUtil.h" 1.34 +#include "nsStringStream.h" 1.35 +#include "nsWidgetsCID.h" 1.36 + 1.37 +#define STREAM_COPY_BLOCK_SIZE 32768 1.38 + 1.39 +BEGIN_FILE_NAMESPACE 1.40 + 1.41 +namespace { 1.42 + 1.43 +NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID); 1.44 + 1.45 +class ReadHelper : public FileHelper 1.46 +{ 1.47 +public: 1.48 + ReadHelper(LockedFile* aLockedFile, 1.49 + FileRequest* aFileRequest, 1.50 + uint64_t aLocation, 1.51 + uint64_t aSize) 1.52 + : FileHelper(aLockedFile, aFileRequest), 1.53 + mLocation(aLocation), mSize(aSize) 1.54 + { 1.55 + NS_ASSERTION(mSize, "Passed zero size!"); 1.56 + } 1.57 + 1.58 + nsresult 1.59 + Init(); 1.60 + 1.61 + nsresult 1.62 + DoAsyncRun(nsISupports* aStream) MOZ_OVERRIDE; 1.63 + 1.64 + nsresult 1.65 + GetSuccessResult(JSContext* aCx, 1.66 + JS::MutableHandle<JS::Value> aVal) MOZ_OVERRIDE; 1.67 + 1.68 +protected: 1.69 + uint64_t mLocation; 1.70 + uint64_t mSize; 1.71 + 1.72 + nsRefPtr<MemoryOutputStream> mStream; 1.73 +}; 1.74 + 1.75 +class ReadTextHelper : public ReadHelper 1.76 +{ 1.77 +public: 1.78 + ReadTextHelper(LockedFile* aLockedFile, 1.79 + FileRequest* aFileRequest, 1.80 + uint64_t aLocation, 1.81 + uint64_t aSize, 1.82 + const nsAString& aEncoding) 1.83 + : ReadHelper(aLockedFile, aFileRequest, aLocation, aSize), 1.84 + mEncoding(aEncoding) 1.85 + { } 1.86 + 1.87 + nsresult 1.88 + GetSuccessResult(JSContext* aCx, 1.89 + JS::MutableHandle<JS::Value> aVal) MOZ_OVERRIDE; 1.90 + 1.91 +private: 1.92 + nsString mEncoding; 1.93 +}; 1.94 + 1.95 +class WriteHelper : public FileHelper 1.96 +{ 1.97 +public: 1.98 + WriteHelper(LockedFile* aLockedFile, 1.99 + FileRequest* aFileRequest, 1.100 + uint64_t aLocation, 1.101 + nsIInputStream* aStream, 1.102 + uint64_t aLength) 1.103 + : FileHelper(aLockedFile, aFileRequest), 1.104 + mLocation(aLocation), mStream(aStream), mLength(aLength) 1.105 + { 1.106 + NS_ASSERTION(mLength, "Passed zero length!"); 1.107 + } 1.108 + 1.109 + nsresult 1.110 + DoAsyncRun(nsISupports* aStream); 1.111 + 1.112 +private: 1.113 + uint64_t mLocation; 1.114 + nsCOMPtr<nsIInputStream> mStream; 1.115 + uint64_t mLength; 1.116 +}; 1.117 + 1.118 +class TruncateHelper : public FileHelper 1.119 +{ 1.120 +public: 1.121 + TruncateHelper(LockedFile* aLockedFile, 1.122 + FileRequest* aFileRequest, 1.123 + uint64_t aOffset) 1.124 + : FileHelper(aLockedFile, aFileRequest), 1.125 + mOffset(aOffset) 1.126 + { } 1.127 + 1.128 + nsresult 1.129 + DoAsyncRun(nsISupports* aStream); 1.130 + 1.131 +private: 1.132 + class AsyncTruncator : public AsyncHelper 1.133 + { 1.134 + public: 1.135 + AsyncTruncator(nsISupports* aStream, int64_t aOffset) 1.136 + : AsyncHelper(aStream), 1.137 + mOffset(aOffset) 1.138 + { } 1.139 + protected: 1.140 + nsresult 1.141 + DoStreamWork(nsISupports* aStream); 1.142 + 1.143 + uint64_t mOffset; 1.144 + }; 1.145 + 1.146 + uint64_t mOffset; 1.147 +}; 1.148 + 1.149 +class FlushHelper : public FileHelper 1.150 +{ 1.151 +public: 1.152 + FlushHelper(LockedFile* aLockedFile, 1.153 + FileRequest* aFileRequest) 1.154 + : FileHelper(aLockedFile, aFileRequest) 1.155 + { } 1.156 + 1.157 + nsresult 1.158 + DoAsyncRun(nsISupports* aStream); 1.159 + 1.160 +private: 1.161 + class AsyncFlusher : public AsyncHelper 1.162 + { 1.163 + public: 1.164 + AsyncFlusher(nsISupports* aStream) 1.165 + : AsyncHelper(aStream) 1.166 + { } 1.167 + protected: 1.168 + nsresult 1.169 + DoStreamWork(nsISupports* aStream); 1.170 + }; 1.171 +}; 1.172 + 1.173 +class OpenStreamHelper : public FileHelper 1.174 +{ 1.175 +public: 1.176 + OpenStreamHelper(LockedFile* aLockedFile, 1.177 + bool aWholeFile, 1.178 + uint64_t aStart, 1.179 + uint64_t aLength) 1.180 + : FileHelper(aLockedFile, nullptr), 1.181 + mWholeFile(aWholeFile), mStart(aStart), mLength(aLength) 1.182 + { } 1.183 + 1.184 + nsresult 1.185 + DoAsyncRun(nsISupports* aStream); 1.186 + 1.187 + nsCOMPtr<nsIInputStream>& 1.188 + Result() 1.189 + { 1.190 + return mStream; 1.191 + } 1.192 + 1.193 +private: 1.194 + bool mWholeFile; 1.195 + uint64_t mStart; 1.196 + uint64_t mLength; 1.197 + 1.198 + nsCOMPtr<nsIInputStream> mStream; 1.199 +}; 1.200 + 1.201 +already_AddRefed<nsIDOMEvent> 1.202 +CreateGenericEvent(mozilla::dom::EventTarget* aEventOwner, 1.203 + const nsAString& aType, bool aBubbles, bool aCancelable) 1.204 +{ 1.205 + nsCOMPtr<nsIDOMEvent> event; 1.206 + NS_NewDOMEvent(getter_AddRefs(event), aEventOwner, nullptr, nullptr); 1.207 + nsresult rv = event->InitEvent(aType, aBubbles, aCancelable); 1.208 + NS_ENSURE_SUCCESS(rv, nullptr); 1.209 + 1.210 + event->SetTrusted(true); 1.211 + 1.212 + return event.forget(); 1.213 +} 1.214 + 1.215 +} // anonymous namespace 1.216 + 1.217 +// static 1.218 +already_AddRefed<LockedFile> 1.219 +LockedFile::Create(FileHandle* aFileHandle, 1.220 + FileMode aMode, 1.221 + RequestMode aRequestMode) 1.222 +{ 1.223 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.224 + 1.225 + nsRefPtr<LockedFile> lockedFile = new LockedFile(); 1.226 + 1.227 + lockedFile->BindToOwner(aFileHandle); 1.228 + 1.229 + lockedFile->mFileHandle = aFileHandle; 1.230 + lockedFile->mMode = aMode; 1.231 + lockedFile->mRequestMode = aRequestMode; 1.232 + 1.233 + nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID); 1.234 + NS_ENSURE_TRUE(appShell, nullptr); 1.235 + 1.236 + nsresult rv = appShell->RunBeforeNextEvent(lockedFile); 1.237 + NS_ENSURE_SUCCESS(rv, nullptr); 1.238 + 1.239 + lockedFile->mCreating = true; 1.240 + 1.241 + FileService* service = FileService::GetOrCreate(); 1.242 + NS_ENSURE_TRUE(service, nullptr); 1.243 + 1.244 + rv = service->Enqueue(lockedFile, nullptr); 1.245 + NS_ENSURE_SUCCESS(rv, nullptr); 1.246 + 1.247 + return lockedFile.forget(); 1.248 +} 1.249 + 1.250 +LockedFile::LockedFile() 1.251 +: mReadyState(INITIAL), 1.252 + mMode(FileMode::Readonly), 1.253 + mRequestMode(NORMAL), 1.254 + mLocation(0), 1.255 + mPendingRequests(0), 1.256 + mAborted(false), 1.257 + mCreating(false) 1.258 +{ 1.259 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.260 + SetIsDOMBinding(); 1.261 +} 1.262 + 1.263 +LockedFile::~LockedFile() 1.264 +{ 1.265 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.266 +} 1.267 + 1.268 +NS_IMPL_CYCLE_COLLECTION_INHERITED(LockedFile, DOMEventTargetHelper, 1.269 + mFileHandle) 1.270 + 1.271 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(LockedFile) 1.272 + NS_INTERFACE_MAP_ENTRY(nsIRunnable) 1.273 +NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) 1.274 + 1.275 +NS_IMPL_ADDREF_INHERITED(LockedFile, DOMEventTargetHelper) 1.276 +NS_IMPL_RELEASE_INHERITED(LockedFile, DOMEventTargetHelper) 1.277 + 1.278 +nsresult 1.279 +LockedFile::PreHandleEvent(EventChainPreVisitor& aVisitor) 1.280 +{ 1.281 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.282 + 1.283 + aVisitor.mCanHandle = true; 1.284 + aVisitor.mParentTarget = mFileHandle; 1.285 + return NS_OK; 1.286 +} 1.287 + 1.288 +void 1.289 +LockedFile::OnNewRequest() 1.290 +{ 1.291 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.292 + if (!mPendingRequests) { 1.293 + NS_ASSERTION(mReadyState == INITIAL, 1.294 + "Reusing a locked file!"); 1.295 + mReadyState = LOADING; 1.296 + } 1.297 + ++mPendingRequests; 1.298 +} 1.299 + 1.300 +void 1.301 +LockedFile::OnRequestFinished() 1.302 +{ 1.303 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.304 + NS_ASSERTION(mPendingRequests, "Mismatched calls!"); 1.305 + --mPendingRequests; 1.306 + if (!mPendingRequests) { 1.307 + NS_ASSERTION(mAborted || mReadyState == LOADING, 1.308 + "Bad state!"); 1.309 + mReadyState = LockedFile::FINISHING; 1.310 + Finish(); 1.311 + } 1.312 +} 1.313 + 1.314 +nsresult 1.315 +LockedFile::CreateParallelStream(nsISupports** aStream) 1.316 +{ 1.317 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.318 + 1.319 + nsIFileStorage* fileStorage = mFileHandle->mFileStorage; 1.320 + if (fileStorage->IsInvalidated()) { 1.321 + return NS_ERROR_NOT_AVAILABLE; 1.322 + } 1.323 + 1.324 + nsCOMPtr<nsISupports> stream = 1.325 + mFileHandle->CreateStream(mFileHandle->mFile, mMode == FileMode::Readonly); 1.326 + NS_ENSURE_TRUE(stream, NS_ERROR_FAILURE); 1.327 + 1.328 + mParallelStreams.AppendElement(stream); 1.329 + 1.330 + stream.forget(aStream); 1.331 + return NS_OK; 1.332 +} 1.333 + 1.334 +nsresult 1.335 +LockedFile::GetOrCreateStream(nsISupports** aStream) 1.336 +{ 1.337 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.338 + 1.339 + nsIFileStorage* fileStorage = mFileHandle->mFileStorage; 1.340 + if (fileStorage->IsInvalidated()) { 1.341 + return NS_ERROR_NOT_AVAILABLE; 1.342 + } 1.343 + 1.344 + if (!mStream) { 1.345 + nsCOMPtr<nsISupports> stream = 1.346 + mFileHandle->CreateStream(mFileHandle->mFile, mMode == FileMode::Readonly); 1.347 + NS_ENSURE_TRUE(stream, NS_ERROR_FAILURE); 1.348 + 1.349 + stream.swap(mStream); 1.350 + } 1.351 + 1.352 + nsCOMPtr<nsISupports> stream(mStream); 1.353 + stream.forget(aStream); 1.354 + 1.355 + return NS_OK; 1.356 +} 1.357 + 1.358 +already_AddRefed<FileRequest> 1.359 +LockedFile::GenerateFileRequest() 1.360 +{ 1.361 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.362 + return FileRequest::Create(GetOwner(), this, /* aWrapAsDOMRequest */ false); 1.363 +} 1.364 + 1.365 +bool 1.366 +LockedFile::IsOpen() const 1.367 +{ 1.368 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.369 + 1.370 + // If we haven't started anything then we're open. 1.371 + if (mReadyState == INITIAL) { 1.372 + NS_ASSERTION(FileHelper::GetCurrentLockedFile() != this, 1.373 + "This should be some other locked file (or null)!"); 1.374 + return true; 1.375 + } 1.376 + 1.377 + // If we've already started then we need to check to see if we still have the 1.378 + // mCreating flag set. If we do (i.e. we haven't returned to the event loop 1.379 + // from the time we were created) then we are open. Otherwise check the 1.380 + // currently running locked files to see if it's the same. We only allow other 1.381 + // requests to be made if this locked file is currently running. 1.382 + if (mReadyState == LOADING) { 1.383 + if (mCreating) { 1.384 + return true; 1.385 + } 1.386 + 1.387 + if (FileHelper::GetCurrentLockedFile() == this) { 1.388 + return true; 1.389 + } 1.390 + } 1.391 + 1.392 + return false; 1.393 +} 1.394 + 1.395 +// virtual 1.396 +JSObject* 1.397 +LockedFile::WrapObject(JSContext* aCx) 1.398 +{ 1.399 + return LockedFileBinding::Wrap(aCx, this); 1.400 +} 1.401 + 1.402 +already_AddRefed<FileRequest> 1.403 +LockedFile::GetMetadata(const DOMFileMetadataParameters& aParameters, 1.404 + ErrorResult& aRv) 1.405 +{ 1.406 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.407 + 1.408 + // Common state checking 1.409 + if (!CheckState(aRv)) { 1.410 + return nullptr; 1.411 + } 1.412 + 1.413 + // Do nothing if the window is closed 1.414 + if (!GetOwner()) { 1.415 + return nullptr; 1.416 + } 1.417 + 1.418 + nsRefPtr<MetadataParameters> params = 1.419 + new MetadataParameters(aParameters.mSize, aParameters.mLastModified); 1.420 + if (!params->IsConfigured()) { 1.421 + aRv.ThrowTypeError(MSG_METADATA_NOT_CONFIGURED); 1.422 + return nullptr; 1.423 + } 1.424 + 1.425 + nsRefPtr<FileRequest> fileRequest = GenerateFileRequest(); 1.426 + 1.427 + nsRefPtr<MetadataHelper> helper = 1.428 + new MetadataHelper(this, fileRequest, params); 1.429 + 1.430 + if (NS_WARN_IF(NS_FAILED(helper->Enqueue()))) { 1.431 + aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR); 1.432 + return nullptr; 1.433 + } 1.434 + 1.435 + return fileRequest.forget(); 1.436 +} 1.437 + 1.438 +already_AddRefed<FileRequest> 1.439 +LockedFile::ReadAsArrayBuffer(uint64_t aSize, ErrorResult& aRv) 1.440 +{ 1.441 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.442 + 1.443 + // State and argument checking for read 1.444 + if (!CheckStateAndArgumentsForRead(aSize, aRv)) { 1.445 + return nullptr; 1.446 + } 1.447 + 1.448 + // Do nothing if the window is closed 1.449 + if (!GetOwner()) { 1.450 + return nullptr; 1.451 + } 1.452 + 1.453 + nsRefPtr<FileRequest> fileRequest = GenerateFileRequest(); 1.454 + 1.455 + nsRefPtr<ReadHelper> helper = 1.456 + new ReadHelper(this, fileRequest, mLocation, aSize); 1.457 + 1.458 + if (NS_WARN_IF(NS_FAILED(helper->Init())) || 1.459 + NS_WARN_IF(NS_FAILED(helper->Enqueue()))) { 1.460 + aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR); 1.461 + return nullptr; 1.462 + } 1.463 + 1.464 + mLocation += aSize; 1.465 + 1.466 + return fileRequest.forget(); 1.467 +} 1.468 + 1.469 +already_AddRefed<FileRequest> 1.470 +LockedFile::ReadAsText(uint64_t aSize, const nsAString& aEncoding, 1.471 + ErrorResult& aRv) 1.472 +{ 1.473 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.474 + 1.475 + // State and argument checking for read 1.476 + if (!CheckStateAndArgumentsForRead(aSize, aRv)) { 1.477 + return nullptr; 1.478 + } 1.479 + 1.480 + // Do nothing if the window is closed 1.481 + if (!GetOwner()) { 1.482 + return nullptr; 1.483 + } 1.484 + 1.485 + nsRefPtr<FileRequest> fileRequest = GenerateFileRequest(); 1.486 + 1.487 + nsRefPtr<ReadTextHelper> helper = 1.488 + new ReadTextHelper(this, fileRequest, mLocation, aSize, aEncoding); 1.489 + 1.490 + if (NS_WARN_IF(NS_FAILED(helper->Init())) || 1.491 + NS_WARN_IF(NS_FAILED(helper->Enqueue()))) { 1.492 + aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR); 1.493 + return nullptr; 1.494 + } 1.495 + 1.496 + mLocation += aSize; 1.497 + 1.498 + return fileRequest.forget(); 1.499 +} 1.500 + 1.501 +already_AddRefed<FileRequest> 1.502 +LockedFile::Truncate(const Optional<uint64_t>& aSize, ErrorResult& aRv) 1.503 +{ 1.504 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.505 + 1.506 + // State checking for write 1.507 + if (!CheckStateForWrite(aRv)) { 1.508 + return nullptr; 1.509 + } 1.510 + 1.511 + // Getting location and additional state checking for truncate 1.512 + uint64_t location; 1.513 + if (aSize.WasPassed()) { 1.514 + // Just in case someone calls us from C++ 1.515 + NS_ASSERTION(aSize.Value() != UINT64_MAX, "Passed wrong size!"); 1.516 + location = aSize.Value(); 1.517 + } else { 1.518 + if (mLocation == UINT64_MAX) { 1.519 + aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR); 1.520 + return nullptr; 1.521 + } 1.522 + location = mLocation; 1.523 + } 1.524 + 1.525 + // Do nothing if the window is closed 1.526 + if (!GetOwner()) { 1.527 + return nullptr; 1.528 + } 1.529 + 1.530 + nsRefPtr<FileRequest> fileRequest = GenerateFileRequest(); 1.531 + 1.532 + nsRefPtr<TruncateHelper> helper = 1.533 + new TruncateHelper(this, fileRequest, location); 1.534 + 1.535 + if (NS_WARN_IF(NS_FAILED(helper->Enqueue()))) { 1.536 + aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR); 1.537 + return nullptr; 1.538 + } 1.539 + 1.540 + if (aSize.WasPassed()) { 1.541 + mLocation = aSize.Value(); 1.542 + } 1.543 + 1.544 + return fileRequest.forget(); 1.545 +} 1.546 + 1.547 +already_AddRefed<FileRequest> 1.548 +LockedFile::Flush(ErrorResult& aRv) 1.549 +{ 1.550 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.551 + 1.552 + // State checking for write 1.553 + if (!CheckStateForWrite(aRv)) { 1.554 + return nullptr; 1.555 + } 1.556 + 1.557 + // Do nothing if the window is closed 1.558 + if (!GetOwner()) { 1.559 + return nullptr; 1.560 + } 1.561 + 1.562 + nsRefPtr<FileRequest> fileRequest = GenerateFileRequest(); 1.563 + 1.564 + nsRefPtr<FlushHelper> helper = new FlushHelper(this, fileRequest); 1.565 + 1.566 + if (NS_WARN_IF(NS_FAILED(helper->Enqueue()))) { 1.567 + aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR); 1.568 + return nullptr; 1.569 + } 1.570 + 1.571 + return fileRequest.forget(); 1.572 +} 1.573 + 1.574 +void 1.575 +LockedFile::Abort(ErrorResult& aRv) 1.576 +{ 1.577 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.578 + 1.579 + // This method is special enough for not using generic state checking methods. 1.580 + 1.581 + // We can't use IsOpen here since we need it to be possible to call Abort() 1.582 + // even from outside of transaction callbacks. 1.583 + if (mReadyState != LockedFile::INITIAL && 1.584 + mReadyState != LockedFile::LOADING) { 1.585 + aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR); 1.586 + return; 1.587 + } 1.588 + 1.589 + bool needToFinish = mReadyState == INITIAL; 1.590 + 1.591 + mAborted = true; 1.592 + mReadyState = DONE; 1.593 + 1.594 + // Fire the abort event if there are no outstanding requests. Otherwise the 1.595 + // abort event will be fired when all outstanding requests finish. 1.596 + if (needToFinish) { 1.597 + aRv = Finish(); 1.598 + } 1.599 +} 1.600 + 1.601 +NS_IMETHODIMP 1.602 +LockedFile::Run() 1.603 +{ 1.604 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.605 + 1.606 + // We're back at the event loop, no longer newborn. 1.607 + mCreating = false; 1.608 + 1.609 + // Maybe set the readyState to DONE if there were no requests generated. 1.610 + if (mReadyState == INITIAL) { 1.611 + mReadyState = DONE; 1.612 + 1.613 + if (NS_FAILED(Finish())) { 1.614 + NS_WARNING("Failed to finish!"); 1.615 + } 1.616 + } 1.617 + 1.618 + return NS_OK; 1.619 +} 1.620 + 1.621 +nsresult 1.622 +LockedFile::OpenInputStream(bool aWholeFile, uint64_t aStart, uint64_t aLength, 1.623 + nsIInputStream** aResult) 1.624 +{ 1.625 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.626 + NS_ASSERTION(mRequestMode == PARALLEL, 1.627 + "Don't call me in other than parallel mode!"); 1.628 + 1.629 + // Common state checking 1.630 + ErrorResult error; 1.631 + if (!CheckState(error)) { 1.632 + return error.ErrorCode(); 1.633 + } 1.634 + 1.635 + // Do nothing if the window is closed 1.636 + if (!GetOwner()) { 1.637 + return NS_OK; 1.638 + } 1.639 + 1.640 + nsRefPtr<OpenStreamHelper> helper = 1.641 + new OpenStreamHelper(this, aWholeFile, aStart, aLength); 1.642 + 1.643 + nsresult rv = helper->Enqueue(); 1.644 + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR); 1.645 + 1.646 + nsCOMPtr<nsIInputStream>& result = helper->Result(); 1.647 + NS_ENSURE_TRUE(result, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR); 1.648 + 1.649 + result.forget(aResult); 1.650 + return NS_OK; 1.651 +} 1.652 + 1.653 +bool 1.654 +LockedFile::CheckState(ErrorResult& aRv) 1.655 +{ 1.656 + if (!IsOpen()) { 1.657 + aRv.Throw(NS_ERROR_DOM_FILEHANDLE_LOCKEDFILE_INACTIVE_ERR); 1.658 + return false; 1.659 + } 1.660 + 1.661 + return true; 1.662 +} 1.663 + 1.664 +bool 1.665 +LockedFile::CheckStateAndArgumentsForRead(uint64_t aSize, ErrorResult& aRv) 1.666 +{ 1.667 + // Common state checking 1.668 + if (!CheckState(aRv)) { 1.669 + return false; 1.670 + } 1.671 + 1.672 + // Additional state checking for read 1.673 + if (mLocation == UINT64_MAX) { 1.674 + aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR); 1.675 + return false; 1.676 + } 1.677 + 1.678 + // Argument checking for read 1.679 + if (!aSize) { 1.680 + aRv.ThrowTypeError(MSG_INVALID_READ_SIZE); 1.681 + return false; 1.682 + } 1.683 + 1.684 + return true; 1.685 +} 1.686 + 1.687 +bool 1.688 +LockedFile::CheckStateForWrite(ErrorResult& aRv) 1.689 +{ 1.690 + // Common state checking 1.691 + if (!CheckState(aRv)) { 1.692 + return false; 1.693 + } 1.694 + 1.695 + // Additional state checking for write 1.696 + if (mMode != FileMode::Readwrite) { 1.697 + aRv.Throw(NS_ERROR_DOM_FILEHANDLE_READ_ONLY_ERR); 1.698 + return false; 1.699 + } 1.700 + 1.701 + return true; 1.702 +} 1.703 + 1.704 +already_AddRefed<FileRequest> 1.705 +LockedFile::WriteInternal(nsIInputStream* aInputStream, uint64_t aInputLength, 1.706 + bool aAppend, ErrorResult& aRv) 1.707 +{ 1.708 + MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); 1.709 + 1.710 + DebugOnly<ErrorResult> error; 1.711 + MOZ_ASSERT(CheckStateForWrite(error)); 1.712 + MOZ_ASSERT_IF(!aAppend, mLocation != UINT64_MAX); 1.713 + MOZ_ASSERT(aInputStream); 1.714 + MOZ_ASSERT(aInputLength); 1.715 + MOZ_ASSERT(GetOwner()); 1.716 + 1.717 + nsRefPtr<FileRequest> fileRequest = GenerateFileRequest(); 1.718 + 1.719 + uint64_t location = aAppend ? UINT64_MAX : mLocation; 1.720 + 1.721 + nsRefPtr<WriteHelper> helper = 1.722 + new WriteHelper(this, fileRequest, location, aInputStream, aInputLength); 1.723 + 1.724 + if (NS_WARN_IF(NS_FAILED(helper->Enqueue()))) { 1.725 + aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR); 1.726 + return nullptr; 1.727 + } 1.728 + 1.729 + if (aAppend) { 1.730 + mLocation = UINT64_MAX; 1.731 + } 1.732 + else { 1.733 + mLocation += aInputLength; 1.734 + } 1.735 + 1.736 + return fileRequest.forget(); 1.737 +} 1.738 + 1.739 +nsresult 1.740 +LockedFile::Finish() 1.741 +{ 1.742 + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 1.743 + 1.744 + nsRefPtr<FinishHelper> helper(new FinishHelper(this)); 1.745 + 1.746 + FileService* service = FileService::Get(); 1.747 + NS_ASSERTION(service, "This should never be null"); 1.748 + 1.749 + nsIEventTarget* target = service->StreamTransportTarget(); 1.750 + 1.751 + nsresult rv = target->Dispatch(helper, NS_DISPATCH_NORMAL); 1.752 + NS_ENSURE_SUCCESS(rv, rv); 1.753 + 1.754 + return NS_OK; 1.755 +} 1.756 + 1.757 +// static 1.758 +already_AddRefed<nsIInputStream> 1.759 +LockedFile::GetInputStream(const ArrayBuffer& aValue, uint64_t* aInputLength, 1.760 + ErrorResult& aRv) 1.761 +{ 1.762 + aValue.ComputeLengthAndData(); 1.763 + 1.764 + const char* data = reinterpret_cast<const char*>(aValue.Data()); 1.765 + uint32_t length = aValue.Length(); 1.766 + 1.767 + nsCOMPtr<nsIInputStream> stream; 1.768 + aRv = NS_NewByteInputStream(getter_AddRefs(stream), data, length, 1.769 + NS_ASSIGNMENT_COPY); 1.770 + if (aRv.Failed()) { 1.771 + return nullptr; 1.772 + } 1.773 + 1.774 + *aInputLength = length; 1.775 + return stream.forget(); 1.776 +} 1.777 + 1.778 +// static 1.779 +already_AddRefed<nsIInputStream> 1.780 +LockedFile::GetInputStream(nsIDOMBlob* aValue, uint64_t* aInputLength, 1.781 + ErrorResult& aRv) 1.782 +{ 1.783 + uint64_t length; 1.784 + aRv = aValue->GetSize(&length); 1.785 + if (aRv.Failed()) { 1.786 + return nullptr; 1.787 + } 1.788 + 1.789 + nsCOMPtr<nsIInputStream> stream; 1.790 + aRv = aValue->GetInternalStream(getter_AddRefs(stream)); 1.791 + if (aRv.Failed()) { 1.792 + return nullptr; 1.793 + } 1.794 + 1.795 + *aInputLength = length; 1.796 + return stream.forget(); 1.797 +} 1.798 + 1.799 +// static 1.800 +already_AddRefed<nsIInputStream> 1.801 +LockedFile::GetInputStream(const nsAString& aValue, uint64_t* aInputLength, 1.802 + ErrorResult& aRv) 1.803 +{ 1.804 + NS_ConvertUTF16toUTF8 cstr(aValue); 1.805 + 1.806 + nsCOMPtr<nsIInputStream> stream; 1.807 + aRv = NS_NewCStringInputStream(getter_AddRefs(stream), cstr); 1.808 + if (aRv.Failed()) { 1.809 + return nullptr; 1.810 + } 1.811 + 1.812 + *aInputLength = cstr.Length(); 1.813 + return stream.forget(); 1.814 +} 1.815 + 1.816 +FinishHelper::FinishHelper(LockedFile* aLockedFile) 1.817 +: mLockedFile(aLockedFile), 1.818 + mAborted(aLockedFile->mAborted) 1.819 +{ 1.820 + mParallelStreams.SwapElements(aLockedFile->mParallelStreams); 1.821 + mStream.swap(aLockedFile->mStream); 1.822 +} 1.823 + 1.824 +NS_IMPL_ISUPPORTS(FinishHelper, nsIRunnable) 1.825 + 1.826 +NS_IMETHODIMP 1.827 +FinishHelper::Run() 1.828 +{ 1.829 + if (NS_IsMainThread()) { 1.830 + mLockedFile->mReadyState = LockedFile::DONE; 1.831 + 1.832 + FileService* service = FileService::Get(); 1.833 + if (service) { 1.834 + service->NotifyLockedFileCompleted(mLockedFile); 1.835 + } 1.836 + 1.837 + nsCOMPtr<nsIDOMEvent> event; 1.838 + if (mAborted) { 1.839 + event = CreateGenericEvent(mLockedFile, NS_LITERAL_STRING("abort"), 1.840 + true, false); 1.841 + } 1.842 + else { 1.843 + event = CreateGenericEvent(mLockedFile, NS_LITERAL_STRING("complete"), 1.844 + false, false); 1.845 + } 1.846 + NS_ENSURE_TRUE(event, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR); 1.847 + 1.848 + bool dummy; 1.849 + if (NS_FAILED(mLockedFile->DispatchEvent(event, &dummy))) { 1.850 + NS_WARNING("Dispatch failed!"); 1.851 + } 1.852 + 1.853 + mLockedFile = nullptr; 1.854 + 1.855 + return NS_OK; 1.856 + } 1.857 + 1.858 + nsIFileStorage* fileStorage = mLockedFile->mFileHandle->mFileStorage; 1.859 + if (fileStorage->IsInvalidated()) { 1.860 + mAborted = true; 1.861 + } 1.862 + 1.863 + for (uint32_t index = 0; index < mParallelStreams.Length(); index++) { 1.864 + nsCOMPtr<nsIInputStream> stream = 1.865 + do_QueryInterface(mParallelStreams[index]); 1.866 + 1.867 + if (NS_FAILED(stream->Close())) { 1.868 + NS_WARNING("Failed to close stream!"); 1.869 + } 1.870 + 1.871 + mParallelStreams[index] = nullptr; 1.872 + } 1.873 + 1.874 + if (mStream) { 1.875 + nsCOMPtr<nsIInputStream> stream = do_QueryInterface(mStream); 1.876 + 1.877 + if (NS_FAILED(stream->Close())) { 1.878 + NS_WARNING("Failed to close stream!"); 1.879 + } 1.880 + 1.881 + mStream = nullptr; 1.882 + } 1.883 + 1.884 + return NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL); 1.885 +} 1.886 + 1.887 +nsresult 1.888 +ReadHelper::Init() 1.889 +{ 1.890 + mStream = MemoryOutputStream::Create(mSize); 1.891 + NS_ENSURE_TRUE(mStream, NS_ERROR_FAILURE); 1.892 + 1.893 + return NS_OK; 1.894 +} 1.895 + 1.896 +nsresult 1.897 +ReadHelper::DoAsyncRun(nsISupports* aStream) 1.898 +{ 1.899 + NS_ASSERTION(aStream, "Passed a null stream!"); 1.900 + 1.901 + uint32_t flags = FileStreamWrapper::NOTIFY_PROGRESS; 1.902 + 1.903 + nsCOMPtr<nsIInputStream> istream = 1.904 + new FileInputStreamWrapper(aStream, this, mLocation, mSize, flags); 1.905 + 1.906 + FileService* service = FileService::Get(); 1.907 + NS_ASSERTION(service, "This should never be null"); 1.908 + 1.909 + nsIEventTarget* target = service->StreamTransportTarget(); 1.910 + 1.911 + nsCOMPtr<nsIAsyncStreamCopier> copier; 1.912 + nsresult rv = 1.913 + NS_NewAsyncStreamCopier(getter_AddRefs(copier), istream, mStream, target, 1.914 + false, true, STREAM_COPY_BLOCK_SIZE); 1.915 + NS_ENSURE_SUCCESS(rv, rv); 1.916 + 1.917 + rv = copier->AsyncCopy(this, nullptr); 1.918 + NS_ENSURE_SUCCESS(rv, rv); 1.919 + 1.920 + mRequest = do_QueryInterface(copier); 1.921 + 1.922 + return NS_OK; 1.923 +} 1.924 + 1.925 +nsresult 1.926 +ReadHelper::GetSuccessResult(JSContext* aCx, 1.927 + JS::MutableHandle<JS::Value> aVal) 1.928 +{ 1.929 + JS::Rooted<JSObject*> arrayBuffer(aCx); 1.930 + nsresult rv = 1.931 + nsContentUtils::CreateArrayBuffer(aCx, mStream->Data(), arrayBuffer.address()); 1.932 + NS_ENSURE_SUCCESS(rv, rv); 1.933 + 1.934 + aVal.setObject(*arrayBuffer); 1.935 + return NS_OK; 1.936 +} 1.937 + 1.938 +nsresult 1.939 +ReadTextHelper::GetSuccessResult(JSContext* aCx, 1.940 + JS::MutableHandle<JS::Value> aVal) 1.941 +{ 1.942 + nsAutoCString encoding; 1.943 + const nsCString& data = mStream->Data(); 1.944 + // The BOM sniffing is baked into the "decode" part of the Encoding 1.945 + // Standard, which the File API references. 1.946 + if (!nsContentUtils::CheckForBOM( 1.947 + reinterpret_cast<const unsigned char *>(data.get()), 1.948 + data.Length(), 1.949 + encoding)) { 1.950 + // BOM sniffing failed. Try the API argument. 1.951 + if (!EncodingUtils::FindEncodingForLabel(mEncoding, encoding)) { 1.952 + // API argument failed. Since we are dealing with a file system file, 1.953 + // we don't have a meaningful type attribute for the blob available, 1.954 + // so proceeding to the next step, which is defaulting to UTF-8. 1.955 + encoding.AssignLiteral("UTF-8"); 1.956 + } 1.957 + } 1.958 + 1.959 + nsString tmpString; 1.960 + nsresult rv = nsContentUtils::ConvertStringFromEncoding(encoding, data, 1.961 + tmpString); 1.962 + NS_ENSURE_SUCCESS(rv, rv); 1.963 + 1.964 + if (!xpc::StringToJsval(aCx, tmpString, aVal)) { 1.965 + NS_WARNING("Failed to convert string!"); 1.966 + return NS_ERROR_FAILURE; 1.967 + } 1.968 + return NS_OK; 1.969 +} 1.970 + 1.971 +nsresult 1.972 +WriteHelper::DoAsyncRun(nsISupports* aStream) 1.973 +{ 1.974 + NS_ASSERTION(aStream, "Passed a null stream!"); 1.975 + 1.976 + uint32_t flags = FileStreamWrapper::NOTIFY_PROGRESS; 1.977 + 1.978 + nsCOMPtr<nsIOutputStream> ostream = 1.979 + new FileOutputStreamWrapper(aStream, this, mLocation, mLength, flags); 1.980 + 1.981 + FileService* service = FileService::Get(); 1.982 + NS_ASSERTION(service, "This should never be null"); 1.983 + 1.984 + nsIEventTarget* target = service->StreamTransportTarget(); 1.985 + 1.986 + nsCOMPtr<nsIAsyncStreamCopier> copier; 1.987 + nsresult rv = 1.988 + NS_NewAsyncStreamCopier(getter_AddRefs(copier), mStream, ostream, target, 1.989 + true, false, STREAM_COPY_BLOCK_SIZE); 1.990 + NS_ENSURE_SUCCESS(rv, rv); 1.991 + 1.992 + rv = copier->AsyncCopy(this, nullptr); 1.993 + NS_ENSURE_SUCCESS(rv, rv); 1.994 + 1.995 + mRequest = do_QueryInterface(copier); 1.996 + 1.997 + return NS_OK; 1.998 +} 1.999 + 1.1000 +nsresult 1.1001 +TruncateHelper::DoAsyncRun(nsISupports* aStream) 1.1002 +{ 1.1003 + NS_ASSERTION(aStream, "Passed a null stream!"); 1.1004 + 1.1005 + nsRefPtr<AsyncTruncator> truncator = new AsyncTruncator(aStream, mOffset); 1.1006 + 1.1007 + nsresult rv = truncator->AsyncWork(this, nullptr); 1.1008 + NS_ENSURE_SUCCESS(rv, rv); 1.1009 + 1.1010 + return NS_OK; 1.1011 +} 1.1012 + 1.1013 +nsresult 1.1014 +TruncateHelper::AsyncTruncator::DoStreamWork(nsISupports* aStream) 1.1015 +{ 1.1016 + nsCOMPtr<nsISeekableStream> sstream = do_QueryInterface(aStream); 1.1017 + 1.1018 + nsresult rv = sstream->Seek(nsISeekableStream::NS_SEEK_SET, mOffset); 1.1019 + NS_ENSURE_SUCCESS(rv, rv); 1.1020 + 1.1021 + rv = sstream->SetEOF(); 1.1022 + NS_ENSURE_SUCCESS(rv, rv); 1.1023 + 1.1024 + return NS_OK; 1.1025 +} 1.1026 + 1.1027 +nsresult 1.1028 +FlushHelper::DoAsyncRun(nsISupports* aStream) 1.1029 +{ 1.1030 + NS_ASSERTION(aStream, "Passed a null stream!"); 1.1031 + 1.1032 + nsRefPtr<AsyncFlusher> flusher = new AsyncFlusher(aStream); 1.1033 + 1.1034 + nsresult rv = flusher->AsyncWork(this, nullptr); 1.1035 + NS_ENSURE_SUCCESS(rv, rv); 1.1036 + 1.1037 + return NS_OK; 1.1038 +} 1.1039 + 1.1040 +nsresult 1.1041 +FlushHelper::AsyncFlusher::DoStreamWork(nsISupports* aStream) 1.1042 +{ 1.1043 + nsCOMPtr<nsIOutputStream> ostream = do_QueryInterface(aStream); 1.1044 + 1.1045 + nsresult rv = ostream->Flush(); 1.1046 + NS_ENSURE_SUCCESS(rv, rv); 1.1047 + 1.1048 + return NS_OK; 1.1049 +} 1.1050 + 1.1051 +nsresult 1.1052 +OpenStreamHelper::DoAsyncRun(nsISupports* aStream) 1.1053 +{ 1.1054 + NS_ASSERTION(aStream, "Passed a null stream!"); 1.1055 + 1.1056 + uint32_t flags = FileStreamWrapper::NOTIFY_CLOSE | 1.1057 + FileStreamWrapper::NOTIFY_DESTROY; 1.1058 + 1.1059 + mStream = mWholeFile ? 1.1060 + new FileInputStreamWrapper(aStream, this, 0, mLength, flags) : 1.1061 + new FileInputStreamWrapper(aStream, this, mStart, mLength, flags); 1.1062 + 1.1063 + return NS_OK; 1.1064 +} 1.1065 + 1.1066 +END_FILE_NAMESPACE