dom/file/LockedFile.cpp

Thu, 15 Jan 2015 15:59:08 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:59:08 +0100
branch
TOR_BUG_9701
changeset 10
ac0c01689b40
permissions
-rw-r--r--

Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set ts=2 et sw=2 tw=80: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "LockedFile.h"
michael@0 8
michael@0 9 #include "AsyncHelper.h"
michael@0 10 #include "FileHandle.h"
michael@0 11 #include "FileHelper.h"
michael@0 12 #include "FileRequest.h"
michael@0 13 #include "FileService.h"
michael@0 14 #include "FileStreamWrappers.h"
michael@0 15 #include "MemoryStreams.h"
michael@0 16 #include "MetadataHelper.h"
michael@0 17 #include "mozilla/dom/DOMRequest.h"
michael@0 18 #include "mozilla/dom/EncodingUtils.h"
michael@0 19 #include "mozilla/dom/LockedFileBinding.h"
michael@0 20 #include "mozilla/dom/TypedArray.h"
michael@0 21 #include "mozilla/dom/UnionTypes.h"
michael@0 22 #include "mozilla/EventDispatcher.h"
michael@0 23 #include "nsContentUtils.h"
michael@0 24 #include "nsError.h"
michael@0 25 #include "nsIAppShell.h"
michael@0 26 #include "nsIDOMEvent.h"
michael@0 27 #include "nsIDOMFile.h"
michael@0 28 #include "nsIFileStorage.h"
michael@0 29 #include "nsISeekableStream.h"
michael@0 30 #include "nsNetUtil.h"
michael@0 31 #include "nsStringStream.h"
michael@0 32 #include "nsWidgetsCID.h"
michael@0 33
michael@0 34 #define STREAM_COPY_BLOCK_SIZE 32768
michael@0 35
michael@0 36 BEGIN_FILE_NAMESPACE
michael@0 37
michael@0 38 namespace {
michael@0 39
michael@0 40 NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
michael@0 41
michael@0 42 class ReadHelper : public FileHelper
michael@0 43 {
michael@0 44 public:
michael@0 45 ReadHelper(LockedFile* aLockedFile,
michael@0 46 FileRequest* aFileRequest,
michael@0 47 uint64_t aLocation,
michael@0 48 uint64_t aSize)
michael@0 49 : FileHelper(aLockedFile, aFileRequest),
michael@0 50 mLocation(aLocation), mSize(aSize)
michael@0 51 {
michael@0 52 NS_ASSERTION(mSize, "Passed zero size!");
michael@0 53 }
michael@0 54
michael@0 55 nsresult
michael@0 56 Init();
michael@0 57
michael@0 58 nsresult
michael@0 59 DoAsyncRun(nsISupports* aStream) MOZ_OVERRIDE;
michael@0 60
michael@0 61 nsresult
michael@0 62 GetSuccessResult(JSContext* aCx,
michael@0 63 JS::MutableHandle<JS::Value> aVal) MOZ_OVERRIDE;
michael@0 64
michael@0 65 protected:
michael@0 66 uint64_t mLocation;
michael@0 67 uint64_t mSize;
michael@0 68
michael@0 69 nsRefPtr<MemoryOutputStream> mStream;
michael@0 70 };
michael@0 71
michael@0 72 class ReadTextHelper : public ReadHelper
michael@0 73 {
michael@0 74 public:
michael@0 75 ReadTextHelper(LockedFile* aLockedFile,
michael@0 76 FileRequest* aFileRequest,
michael@0 77 uint64_t aLocation,
michael@0 78 uint64_t aSize,
michael@0 79 const nsAString& aEncoding)
michael@0 80 : ReadHelper(aLockedFile, aFileRequest, aLocation, aSize),
michael@0 81 mEncoding(aEncoding)
michael@0 82 { }
michael@0 83
michael@0 84 nsresult
michael@0 85 GetSuccessResult(JSContext* aCx,
michael@0 86 JS::MutableHandle<JS::Value> aVal) MOZ_OVERRIDE;
michael@0 87
michael@0 88 private:
michael@0 89 nsString mEncoding;
michael@0 90 };
michael@0 91
michael@0 92 class WriteHelper : public FileHelper
michael@0 93 {
michael@0 94 public:
michael@0 95 WriteHelper(LockedFile* aLockedFile,
michael@0 96 FileRequest* aFileRequest,
michael@0 97 uint64_t aLocation,
michael@0 98 nsIInputStream* aStream,
michael@0 99 uint64_t aLength)
michael@0 100 : FileHelper(aLockedFile, aFileRequest),
michael@0 101 mLocation(aLocation), mStream(aStream), mLength(aLength)
michael@0 102 {
michael@0 103 NS_ASSERTION(mLength, "Passed zero length!");
michael@0 104 }
michael@0 105
michael@0 106 nsresult
michael@0 107 DoAsyncRun(nsISupports* aStream);
michael@0 108
michael@0 109 private:
michael@0 110 uint64_t mLocation;
michael@0 111 nsCOMPtr<nsIInputStream> mStream;
michael@0 112 uint64_t mLength;
michael@0 113 };
michael@0 114
michael@0 115 class TruncateHelper : public FileHelper
michael@0 116 {
michael@0 117 public:
michael@0 118 TruncateHelper(LockedFile* aLockedFile,
michael@0 119 FileRequest* aFileRequest,
michael@0 120 uint64_t aOffset)
michael@0 121 : FileHelper(aLockedFile, aFileRequest),
michael@0 122 mOffset(aOffset)
michael@0 123 { }
michael@0 124
michael@0 125 nsresult
michael@0 126 DoAsyncRun(nsISupports* aStream);
michael@0 127
michael@0 128 private:
michael@0 129 class AsyncTruncator : public AsyncHelper
michael@0 130 {
michael@0 131 public:
michael@0 132 AsyncTruncator(nsISupports* aStream, int64_t aOffset)
michael@0 133 : AsyncHelper(aStream),
michael@0 134 mOffset(aOffset)
michael@0 135 { }
michael@0 136 protected:
michael@0 137 nsresult
michael@0 138 DoStreamWork(nsISupports* aStream);
michael@0 139
michael@0 140 uint64_t mOffset;
michael@0 141 };
michael@0 142
michael@0 143 uint64_t mOffset;
michael@0 144 };
michael@0 145
michael@0 146 class FlushHelper : public FileHelper
michael@0 147 {
michael@0 148 public:
michael@0 149 FlushHelper(LockedFile* aLockedFile,
michael@0 150 FileRequest* aFileRequest)
michael@0 151 : FileHelper(aLockedFile, aFileRequest)
michael@0 152 { }
michael@0 153
michael@0 154 nsresult
michael@0 155 DoAsyncRun(nsISupports* aStream);
michael@0 156
michael@0 157 private:
michael@0 158 class AsyncFlusher : public AsyncHelper
michael@0 159 {
michael@0 160 public:
michael@0 161 AsyncFlusher(nsISupports* aStream)
michael@0 162 : AsyncHelper(aStream)
michael@0 163 { }
michael@0 164 protected:
michael@0 165 nsresult
michael@0 166 DoStreamWork(nsISupports* aStream);
michael@0 167 };
michael@0 168 };
michael@0 169
michael@0 170 class OpenStreamHelper : public FileHelper
michael@0 171 {
michael@0 172 public:
michael@0 173 OpenStreamHelper(LockedFile* aLockedFile,
michael@0 174 bool aWholeFile,
michael@0 175 uint64_t aStart,
michael@0 176 uint64_t aLength)
michael@0 177 : FileHelper(aLockedFile, nullptr),
michael@0 178 mWholeFile(aWholeFile), mStart(aStart), mLength(aLength)
michael@0 179 { }
michael@0 180
michael@0 181 nsresult
michael@0 182 DoAsyncRun(nsISupports* aStream);
michael@0 183
michael@0 184 nsCOMPtr<nsIInputStream>&
michael@0 185 Result()
michael@0 186 {
michael@0 187 return mStream;
michael@0 188 }
michael@0 189
michael@0 190 private:
michael@0 191 bool mWholeFile;
michael@0 192 uint64_t mStart;
michael@0 193 uint64_t mLength;
michael@0 194
michael@0 195 nsCOMPtr<nsIInputStream> mStream;
michael@0 196 };
michael@0 197
michael@0 198 already_AddRefed<nsIDOMEvent>
michael@0 199 CreateGenericEvent(mozilla::dom::EventTarget* aEventOwner,
michael@0 200 const nsAString& aType, bool aBubbles, bool aCancelable)
michael@0 201 {
michael@0 202 nsCOMPtr<nsIDOMEvent> event;
michael@0 203 NS_NewDOMEvent(getter_AddRefs(event), aEventOwner, nullptr, nullptr);
michael@0 204 nsresult rv = event->InitEvent(aType, aBubbles, aCancelable);
michael@0 205 NS_ENSURE_SUCCESS(rv, nullptr);
michael@0 206
michael@0 207 event->SetTrusted(true);
michael@0 208
michael@0 209 return event.forget();
michael@0 210 }
michael@0 211
michael@0 212 } // anonymous namespace
michael@0 213
michael@0 214 // static
michael@0 215 already_AddRefed<LockedFile>
michael@0 216 LockedFile::Create(FileHandle* aFileHandle,
michael@0 217 FileMode aMode,
michael@0 218 RequestMode aRequestMode)
michael@0 219 {
michael@0 220 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 221
michael@0 222 nsRefPtr<LockedFile> lockedFile = new LockedFile();
michael@0 223
michael@0 224 lockedFile->BindToOwner(aFileHandle);
michael@0 225
michael@0 226 lockedFile->mFileHandle = aFileHandle;
michael@0 227 lockedFile->mMode = aMode;
michael@0 228 lockedFile->mRequestMode = aRequestMode;
michael@0 229
michael@0 230 nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
michael@0 231 NS_ENSURE_TRUE(appShell, nullptr);
michael@0 232
michael@0 233 nsresult rv = appShell->RunBeforeNextEvent(lockedFile);
michael@0 234 NS_ENSURE_SUCCESS(rv, nullptr);
michael@0 235
michael@0 236 lockedFile->mCreating = true;
michael@0 237
michael@0 238 FileService* service = FileService::GetOrCreate();
michael@0 239 NS_ENSURE_TRUE(service, nullptr);
michael@0 240
michael@0 241 rv = service->Enqueue(lockedFile, nullptr);
michael@0 242 NS_ENSURE_SUCCESS(rv, nullptr);
michael@0 243
michael@0 244 return lockedFile.forget();
michael@0 245 }
michael@0 246
michael@0 247 LockedFile::LockedFile()
michael@0 248 : mReadyState(INITIAL),
michael@0 249 mMode(FileMode::Readonly),
michael@0 250 mRequestMode(NORMAL),
michael@0 251 mLocation(0),
michael@0 252 mPendingRequests(0),
michael@0 253 mAborted(false),
michael@0 254 mCreating(false)
michael@0 255 {
michael@0 256 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 257 SetIsDOMBinding();
michael@0 258 }
michael@0 259
michael@0 260 LockedFile::~LockedFile()
michael@0 261 {
michael@0 262 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 263 }
michael@0 264
michael@0 265 NS_IMPL_CYCLE_COLLECTION_INHERITED(LockedFile, DOMEventTargetHelper,
michael@0 266 mFileHandle)
michael@0 267
michael@0 268 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(LockedFile)
michael@0 269 NS_INTERFACE_MAP_ENTRY(nsIRunnable)
michael@0 270 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
michael@0 271
michael@0 272 NS_IMPL_ADDREF_INHERITED(LockedFile, DOMEventTargetHelper)
michael@0 273 NS_IMPL_RELEASE_INHERITED(LockedFile, DOMEventTargetHelper)
michael@0 274
michael@0 275 nsresult
michael@0 276 LockedFile::PreHandleEvent(EventChainPreVisitor& aVisitor)
michael@0 277 {
michael@0 278 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 279
michael@0 280 aVisitor.mCanHandle = true;
michael@0 281 aVisitor.mParentTarget = mFileHandle;
michael@0 282 return NS_OK;
michael@0 283 }
michael@0 284
michael@0 285 void
michael@0 286 LockedFile::OnNewRequest()
michael@0 287 {
michael@0 288 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 289 if (!mPendingRequests) {
michael@0 290 NS_ASSERTION(mReadyState == INITIAL,
michael@0 291 "Reusing a locked file!");
michael@0 292 mReadyState = LOADING;
michael@0 293 }
michael@0 294 ++mPendingRequests;
michael@0 295 }
michael@0 296
michael@0 297 void
michael@0 298 LockedFile::OnRequestFinished()
michael@0 299 {
michael@0 300 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 301 NS_ASSERTION(mPendingRequests, "Mismatched calls!");
michael@0 302 --mPendingRequests;
michael@0 303 if (!mPendingRequests) {
michael@0 304 NS_ASSERTION(mAborted || mReadyState == LOADING,
michael@0 305 "Bad state!");
michael@0 306 mReadyState = LockedFile::FINISHING;
michael@0 307 Finish();
michael@0 308 }
michael@0 309 }
michael@0 310
michael@0 311 nsresult
michael@0 312 LockedFile::CreateParallelStream(nsISupports** aStream)
michael@0 313 {
michael@0 314 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 315
michael@0 316 nsIFileStorage* fileStorage = mFileHandle->mFileStorage;
michael@0 317 if (fileStorage->IsInvalidated()) {
michael@0 318 return NS_ERROR_NOT_AVAILABLE;
michael@0 319 }
michael@0 320
michael@0 321 nsCOMPtr<nsISupports> stream =
michael@0 322 mFileHandle->CreateStream(mFileHandle->mFile, mMode == FileMode::Readonly);
michael@0 323 NS_ENSURE_TRUE(stream, NS_ERROR_FAILURE);
michael@0 324
michael@0 325 mParallelStreams.AppendElement(stream);
michael@0 326
michael@0 327 stream.forget(aStream);
michael@0 328 return NS_OK;
michael@0 329 }
michael@0 330
michael@0 331 nsresult
michael@0 332 LockedFile::GetOrCreateStream(nsISupports** aStream)
michael@0 333 {
michael@0 334 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 335
michael@0 336 nsIFileStorage* fileStorage = mFileHandle->mFileStorage;
michael@0 337 if (fileStorage->IsInvalidated()) {
michael@0 338 return NS_ERROR_NOT_AVAILABLE;
michael@0 339 }
michael@0 340
michael@0 341 if (!mStream) {
michael@0 342 nsCOMPtr<nsISupports> stream =
michael@0 343 mFileHandle->CreateStream(mFileHandle->mFile, mMode == FileMode::Readonly);
michael@0 344 NS_ENSURE_TRUE(stream, NS_ERROR_FAILURE);
michael@0 345
michael@0 346 stream.swap(mStream);
michael@0 347 }
michael@0 348
michael@0 349 nsCOMPtr<nsISupports> stream(mStream);
michael@0 350 stream.forget(aStream);
michael@0 351
michael@0 352 return NS_OK;
michael@0 353 }
michael@0 354
michael@0 355 already_AddRefed<FileRequest>
michael@0 356 LockedFile::GenerateFileRequest()
michael@0 357 {
michael@0 358 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 359 return FileRequest::Create(GetOwner(), this, /* aWrapAsDOMRequest */ false);
michael@0 360 }
michael@0 361
michael@0 362 bool
michael@0 363 LockedFile::IsOpen() const
michael@0 364 {
michael@0 365 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 366
michael@0 367 // If we haven't started anything then we're open.
michael@0 368 if (mReadyState == INITIAL) {
michael@0 369 NS_ASSERTION(FileHelper::GetCurrentLockedFile() != this,
michael@0 370 "This should be some other locked file (or null)!");
michael@0 371 return true;
michael@0 372 }
michael@0 373
michael@0 374 // If we've already started then we need to check to see if we still have the
michael@0 375 // mCreating flag set. If we do (i.e. we haven't returned to the event loop
michael@0 376 // from the time we were created) then we are open. Otherwise check the
michael@0 377 // currently running locked files to see if it's the same. We only allow other
michael@0 378 // requests to be made if this locked file is currently running.
michael@0 379 if (mReadyState == LOADING) {
michael@0 380 if (mCreating) {
michael@0 381 return true;
michael@0 382 }
michael@0 383
michael@0 384 if (FileHelper::GetCurrentLockedFile() == this) {
michael@0 385 return true;
michael@0 386 }
michael@0 387 }
michael@0 388
michael@0 389 return false;
michael@0 390 }
michael@0 391
michael@0 392 // virtual
michael@0 393 JSObject*
michael@0 394 LockedFile::WrapObject(JSContext* aCx)
michael@0 395 {
michael@0 396 return LockedFileBinding::Wrap(aCx, this);
michael@0 397 }
michael@0 398
michael@0 399 already_AddRefed<FileRequest>
michael@0 400 LockedFile::GetMetadata(const DOMFileMetadataParameters& aParameters,
michael@0 401 ErrorResult& aRv)
michael@0 402 {
michael@0 403 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 404
michael@0 405 // Common state checking
michael@0 406 if (!CheckState(aRv)) {
michael@0 407 return nullptr;
michael@0 408 }
michael@0 409
michael@0 410 // Do nothing if the window is closed
michael@0 411 if (!GetOwner()) {
michael@0 412 return nullptr;
michael@0 413 }
michael@0 414
michael@0 415 nsRefPtr<MetadataParameters> params =
michael@0 416 new MetadataParameters(aParameters.mSize, aParameters.mLastModified);
michael@0 417 if (!params->IsConfigured()) {
michael@0 418 aRv.ThrowTypeError(MSG_METADATA_NOT_CONFIGURED);
michael@0 419 return nullptr;
michael@0 420 }
michael@0 421
michael@0 422 nsRefPtr<FileRequest> fileRequest = GenerateFileRequest();
michael@0 423
michael@0 424 nsRefPtr<MetadataHelper> helper =
michael@0 425 new MetadataHelper(this, fileRequest, params);
michael@0 426
michael@0 427 if (NS_WARN_IF(NS_FAILED(helper->Enqueue()))) {
michael@0 428 aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
michael@0 429 return nullptr;
michael@0 430 }
michael@0 431
michael@0 432 return fileRequest.forget();
michael@0 433 }
michael@0 434
michael@0 435 already_AddRefed<FileRequest>
michael@0 436 LockedFile::ReadAsArrayBuffer(uint64_t aSize, ErrorResult& aRv)
michael@0 437 {
michael@0 438 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 439
michael@0 440 // State and argument checking for read
michael@0 441 if (!CheckStateAndArgumentsForRead(aSize, aRv)) {
michael@0 442 return nullptr;
michael@0 443 }
michael@0 444
michael@0 445 // Do nothing if the window is closed
michael@0 446 if (!GetOwner()) {
michael@0 447 return nullptr;
michael@0 448 }
michael@0 449
michael@0 450 nsRefPtr<FileRequest> fileRequest = GenerateFileRequest();
michael@0 451
michael@0 452 nsRefPtr<ReadHelper> helper =
michael@0 453 new ReadHelper(this, fileRequest, mLocation, aSize);
michael@0 454
michael@0 455 if (NS_WARN_IF(NS_FAILED(helper->Init())) ||
michael@0 456 NS_WARN_IF(NS_FAILED(helper->Enqueue()))) {
michael@0 457 aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
michael@0 458 return nullptr;
michael@0 459 }
michael@0 460
michael@0 461 mLocation += aSize;
michael@0 462
michael@0 463 return fileRequest.forget();
michael@0 464 }
michael@0 465
michael@0 466 already_AddRefed<FileRequest>
michael@0 467 LockedFile::ReadAsText(uint64_t aSize, const nsAString& aEncoding,
michael@0 468 ErrorResult& aRv)
michael@0 469 {
michael@0 470 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 471
michael@0 472 // State and argument checking for read
michael@0 473 if (!CheckStateAndArgumentsForRead(aSize, aRv)) {
michael@0 474 return nullptr;
michael@0 475 }
michael@0 476
michael@0 477 // Do nothing if the window is closed
michael@0 478 if (!GetOwner()) {
michael@0 479 return nullptr;
michael@0 480 }
michael@0 481
michael@0 482 nsRefPtr<FileRequest> fileRequest = GenerateFileRequest();
michael@0 483
michael@0 484 nsRefPtr<ReadTextHelper> helper =
michael@0 485 new ReadTextHelper(this, fileRequest, mLocation, aSize, aEncoding);
michael@0 486
michael@0 487 if (NS_WARN_IF(NS_FAILED(helper->Init())) ||
michael@0 488 NS_WARN_IF(NS_FAILED(helper->Enqueue()))) {
michael@0 489 aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
michael@0 490 return nullptr;
michael@0 491 }
michael@0 492
michael@0 493 mLocation += aSize;
michael@0 494
michael@0 495 return fileRequest.forget();
michael@0 496 }
michael@0 497
michael@0 498 already_AddRefed<FileRequest>
michael@0 499 LockedFile::Truncate(const Optional<uint64_t>& aSize, ErrorResult& aRv)
michael@0 500 {
michael@0 501 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 502
michael@0 503 // State checking for write
michael@0 504 if (!CheckStateForWrite(aRv)) {
michael@0 505 return nullptr;
michael@0 506 }
michael@0 507
michael@0 508 // Getting location and additional state checking for truncate
michael@0 509 uint64_t location;
michael@0 510 if (aSize.WasPassed()) {
michael@0 511 // Just in case someone calls us from C++
michael@0 512 NS_ASSERTION(aSize.Value() != UINT64_MAX, "Passed wrong size!");
michael@0 513 location = aSize.Value();
michael@0 514 } else {
michael@0 515 if (mLocation == UINT64_MAX) {
michael@0 516 aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
michael@0 517 return nullptr;
michael@0 518 }
michael@0 519 location = mLocation;
michael@0 520 }
michael@0 521
michael@0 522 // Do nothing if the window is closed
michael@0 523 if (!GetOwner()) {
michael@0 524 return nullptr;
michael@0 525 }
michael@0 526
michael@0 527 nsRefPtr<FileRequest> fileRequest = GenerateFileRequest();
michael@0 528
michael@0 529 nsRefPtr<TruncateHelper> helper =
michael@0 530 new TruncateHelper(this, fileRequest, location);
michael@0 531
michael@0 532 if (NS_WARN_IF(NS_FAILED(helper->Enqueue()))) {
michael@0 533 aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
michael@0 534 return nullptr;
michael@0 535 }
michael@0 536
michael@0 537 if (aSize.WasPassed()) {
michael@0 538 mLocation = aSize.Value();
michael@0 539 }
michael@0 540
michael@0 541 return fileRequest.forget();
michael@0 542 }
michael@0 543
michael@0 544 already_AddRefed<FileRequest>
michael@0 545 LockedFile::Flush(ErrorResult& aRv)
michael@0 546 {
michael@0 547 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 548
michael@0 549 // State checking for write
michael@0 550 if (!CheckStateForWrite(aRv)) {
michael@0 551 return nullptr;
michael@0 552 }
michael@0 553
michael@0 554 // Do nothing if the window is closed
michael@0 555 if (!GetOwner()) {
michael@0 556 return nullptr;
michael@0 557 }
michael@0 558
michael@0 559 nsRefPtr<FileRequest> fileRequest = GenerateFileRequest();
michael@0 560
michael@0 561 nsRefPtr<FlushHelper> helper = new FlushHelper(this, fileRequest);
michael@0 562
michael@0 563 if (NS_WARN_IF(NS_FAILED(helper->Enqueue()))) {
michael@0 564 aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
michael@0 565 return nullptr;
michael@0 566 }
michael@0 567
michael@0 568 return fileRequest.forget();
michael@0 569 }
michael@0 570
michael@0 571 void
michael@0 572 LockedFile::Abort(ErrorResult& aRv)
michael@0 573 {
michael@0 574 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 575
michael@0 576 // This method is special enough for not using generic state checking methods.
michael@0 577
michael@0 578 // We can't use IsOpen here since we need it to be possible to call Abort()
michael@0 579 // even from outside of transaction callbacks.
michael@0 580 if (mReadyState != LockedFile::INITIAL &&
michael@0 581 mReadyState != LockedFile::LOADING) {
michael@0 582 aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
michael@0 583 return;
michael@0 584 }
michael@0 585
michael@0 586 bool needToFinish = mReadyState == INITIAL;
michael@0 587
michael@0 588 mAborted = true;
michael@0 589 mReadyState = DONE;
michael@0 590
michael@0 591 // Fire the abort event if there are no outstanding requests. Otherwise the
michael@0 592 // abort event will be fired when all outstanding requests finish.
michael@0 593 if (needToFinish) {
michael@0 594 aRv = Finish();
michael@0 595 }
michael@0 596 }
michael@0 597
michael@0 598 NS_IMETHODIMP
michael@0 599 LockedFile::Run()
michael@0 600 {
michael@0 601 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 602
michael@0 603 // We're back at the event loop, no longer newborn.
michael@0 604 mCreating = false;
michael@0 605
michael@0 606 // Maybe set the readyState to DONE if there were no requests generated.
michael@0 607 if (mReadyState == INITIAL) {
michael@0 608 mReadyState = DONE;
michael@0 609
michael@0 610 if (NS_FAILED(Finish())) {
michael@0 611 NS_WARNING("Failed to finish!");
michael@0 612 }
michael@0 613 }
michael@0 614
michael@0 615 return NS_OK;
michael@0 616 }
michael@0 617
michael@0 618 nsresult
michael@0 619 LockedFile::OpenInputStream(bool aWholeFile, uint64_t aStart, uint64_t aLength,
michael@0 620 nsIInputStream** aResult)
michael@0 621 {
michael@0 622 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 623 NS_ASSERTION(mRequestMode == PARALLEL,
michael@0 624 "Don't call me in other than parallel mode!");
michael@0 625
michael@0 626 // Common state checking
michael@0 627 ErrorResult error;
michael@0 628 if (!CheckState(error)) {
michael@0 629 return error.ErrorCode();
michael@0 630 }
michael@0 631
michael@0 632 // Do nothing if the window is closed
michael@0 633 if (!GetOwner()) {
michael@0 634 return NS_OK;
michael@0 635 }
michael@0 636
michael@0 637 nsRefPtr<OpenStreamHelper> helper =
michael@0 638 new OpenStreamHelper(this, aWholeFile, aStart, aLength);
michael@0 639
michael@0 640 nsresult rv = helper->Enqueue();
michael@0 641 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
michael@0 642
michael@0 643 nsCOMPtr<nsIInputStream>& result = helper->Result();
michael@0 644 NS_ENSURE_TRUE(result, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
michael@0 645
michael@0 646 result.forget(aResult);
michael@0 647 return NS_OK;
michael@0 648 }
michael@0 649
michael@0 650 bool
michael@0 651 LockedFile::CheckState(ErrorResult& aRv)
michael@0 652 {
michael@0 653 if (!IsOpen()) {
michael@0 654 aRv.Throw(NS_ERROR_DOM_FILEHANDLE_LOCKEDFILE_INACTIVE_ERR);
michael@0 655 return false;
michael@0 656 }
michael@0 657
michael@0 658 return true;
michael@0 659 }
michael@0 660
michael@0 661 bool
michael@0 662 LockedFile::CheckStateAndArgumentsForRead(uint64_t aSize, ErrorResult& aRv)
michael@0 663 {
michael@0 664 // Common state checking
michael@0 665 if (!CheckState(aRv)) {
michael@0 666 return false;
michael@0 667 }
michael@0 668
michael@0 669 // Additional state checking for read
michael@0 670 if (mLocation == UINT64_MAX) {
michael@0 671 aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
michael@0 672 return false;
michael@0 673 }
michael@0 674
michael@0 675 // Argument checking for read
michael@0 676 if (!aSize) {
michael@0 677 aRv.ThrowTypeError(MSG_INVALID_READ_SIZE);
michael@0 678 return false;
michael@0 679 }
michael@0 680
michael@0 681 return true;
michael@0 682 }
michael@0 683
michael@0 684 bool
michael@0 685 LockedFile::CheckStateForWrite(ErrorResult& aRv)
michael@0 686 {
michael@0 687 // Common state checking
michael@0 688 if (!CheckState(aRv)) {
michael@0 689 return false;
michael@0 690 }
michael@0 691
michael@0 692 // Additional state checking for write
michael@0 693 if (mMode != FileMode::Readwrite) {
michael@0 694 aRv.Throw(NS_ERROR_DOM_FILEHANDLE_READ_ONLY_ERR);
michael@0 695 return false;
michael@0 696 }
michael@0 697
michael@0 698 return true;
michael@0 699 }
michael@0 700
michael@0 701 already_AddRefed<FileRequest>
michael@0 702 LockedFile::WriteInternal(nsIInputStream* aInputStream, uint64_t aInputLength,
michael@0 703 bool aAppend, ErrorResult& aRv)
michael@0 704 {
michael@0 705 MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
michael@0 706
michael@0 707 DebugOnly<ErrorResult> error;
michael@0 708 MOZ_ASSERT(CheckStateForWrite(error));
michael@0 709 MOZ_ASSERT_IF(!aAppend, mLocation != UINT64_MAX);
michael@0 710 MOZ_ASSERT(aInputStream);
michael@0 711 MOZ_ASSERT(aInputLength);
michael@0 712 MOZ_ASSERT(GetOwner());
michael@0 713
michael@0 714 nsRefPtr<FileRequest> fileRequest = GenerateFileRequest();
michael@0 715
michael@0 716 uint64_t location = aAppend ? UINT64_MAX : mLocation;
michael@0 717
michael@0 718 nsRefPtr<WriteHelper> helper =
michael@0 719 new WriteHelper(this, fileRequest, location, aInputStream, aInputLength);
michael@0 720
michael@0 721 if (NS_WARN_IF(NS_FAILED(helper->Enqueue()))) {
michael@0 722 aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
michael@0 723 return nullptr;
michael@0 724 }
michael@0 725
michael@0 726 if (aAppend) {
michael@0 727 mLocation = UINT64_MAX;
michael@0 728 }
michael@0 729 else {
michael@0 730 mLocation += aInputLength;
michael@0 731 }
michael@0 732
michael@0 733 return fileRequest.forget();
michael@0 734 }
michael@0 735
michael@0 736 nsresult
michael@0 737 LockedFile::Finish()
michael@0 738 {
michael@0 739 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 740
michael@0 741 nsRefPtr<FinishHelper> helper(new FinishHelper(this));
michael@0 742
michael@0 743 FileService* service = FileService::Get();
michael@0 744 NS_ASSERTION(service, "This should never be null");
michael@0 745
michael@0 746 nsIEventTarget* target = service->StreamTransportTarget();
michael@0 747
michael@0 748 nsresult rv = target->Dispatch(helper, NS_DISPATCH_NORMAL);
michael@0 749 NS_ENSURE_SUCCESS(rv, rv);
michael@0 750
michael@0 751 return NS_OK;
michael@0 752 }
michael@0 753
michael@0 754 // static
michael@0 755 already_AddRefed<nsIInputStream>
michael@0 756 LockedFile::GetInputStream(const ArrayBuffer& aValue, uint64_t* aInputLength,
michael@0 757 ErrorResult& aRv)
michael@0 758 {
michael@0 759 aValue.ComputeLengthAndData();
michael@0 760
michael@0 761 const char* data = reinterpret_cast<const char*>(aValue.Data());
michael@0 762 uint32_t length = aValue.Length();
michael@0 763
michael@0 764 nsCOMPtr<nsIInputStream> stream;
michael@0 765 aRv = NS_NewByteInputStream(getter_AddRefs(stream), data, length,
michael@0 766 NS_ASSIGNMENT_COPY);
michael@0 767 if (aRv.Failed()) {
michael@0 768 return nullptr;
michael@0 769 }
michael@0 770
michael@0 771 *aInputLength = length;
michael@0 772 return stream.forget();
michael@0 773 }
michael@0 774
michael@0 775 // static
michael@0 776 already_AddRefed<nsIInputStream>
michael@0 777 LockedFile::GetInputStream(nsIDOMBlob* aValue, uint64_t* aInputLength,
michael@0 778 ErrorResult& aRv)
michael@0 779 {
michael@0 780 uint64_t length;
michael@0 781 aRv = aValue->GetSize(&length);
michael@0 782 if (aRv.Failed()) {
michael@0 783 return nullptr;
michael@0 784 }
michael@0 785
michael@0 786 nsCOMPtr<nsIInputStream> stream;
michael@0 787 aRv = aValue->GetInternalStream(getter_AddRefs(stream));
michael@0 788 if (aRv.Failed()) {
michael@0 789 return nullptr;
michael@0 790 }
michael@0 791
michael@0 792 *aInputLength = length;
michael@0 793 return stream.forget();
michael@0 794 }
michael@0 795
michael@0 796 // static
michael@0 797 already_AddRefed<nsIInputStream>
michael@0 798 LockedFile::GetInputStream(const nsAString& aValue, uint64_t* aInputLength,
michael@0 799 ErrorResult& aRv)
michael@0 800 {
michael@0 801 NS_ConvertUTF16toUTF8 cstr(aValue);
michael@0 802
michael@0 803 nsCOMPtr<nsIInputStream> stream;
michael@0 804 aRv = NS_NewCStringInputStream(getter_AddRefs(stream), cstr);
michael@0 805 if (aRv.Failed()) {
michael@0 806 return nullptr;
michael@0 807 }
michael@0 808
michael@0 809 *aInputLength = cstr.Length();
michael@0 810 return stream.forget();
michael@0 811 }
michael@0 812
michael@0 813 FinishHelper::FinishHelper(LockedFile* aLockedFile)
michael@0 814 : mLockedFile(aLockedFile),
michael@0 815 mAborted(aLockedFile->mAborted)
michael@0 816 {
michael@0 817 mParallelStreams.SwapElements(aLockedFile->mParallelStreams);
michael@0 818 mStream.swap(aLockedFile->mStream);
michael@0 819 }
michael@0 820
michael@0 821 NS_IMPL_ISUPPORTS(FinishHelper, nsIRunnable)
michael@0 822
michael@0 823 NS_IMETHODIMP
michael@0 824 FinishHelper::Run()
michael@0 825 {
michael@0 826 if (NS_IsMainThread()) {
michael@0 827 mLockedFile->mReadyState = LockedFile::DONE;
michael@0 828
michael@0 829 FileService* service = FileService::Get();
michael@0 830 if (service) {
michael@0 831 service->NotifyLockedFileCompleted(mLockedFile);
michael@0 832 }
michael@0 833
michael@0 834 nsCOMPtr<nsIDOMEvent> event;
michael@0 835 if (mAborted) {
michael@0 836 event = CreateGenericEvent(mLockedFile, NS_LITERAL_STRING("abort"),
michael@0 837 true, false);
michael@0 838 }
michael@0 839 else {
michael@0 840 event = CreateGenericEvent(mLockedFile, NS_LITERAL_STRING("complete"),
michael@0 841 false, false);
michael@0 842 }
michael@0 843 NS_ENSURE_TRUE(event, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
michael@0 844
michael@0 845 bool dummy;
michael@0 846 if (NS_FAILED(mLockedFile->DispatchEvent(event, &dummy))) {
michael@0 847 NS_WARNING("Dispatch failed!");
michael@0 848 }
michael@0 849
michael@0 850 mLockedFile = nullptr;
michael@0 851
michael@0 852 return NS_OK;
michael@0 853 }
michael@0 854
michael@0 855 nsIFileStorage* fileStorage = mLockedFile->mFileHandle->mFileStorage;
michael@0 856 if (fileStorage->IsInvalidated()) {
michael@0 857 mAborted = true;
michael@0 858 }
michael@0 859
michael@0 860 for (uint32_t index = 0; index < mParallelStreams.Length(); index++) {
michael@0 861 nsCOMPtr<nsIInputStream> stream =
michael@0 862 do_QueryInterface(mParallelStreams[index]);
michael@0 863
michael@0 864 if (NS_FAILED(stream->Close())) {
michael@0 865 NS_WARNING("Failed to close stream!");
michael@0 866 }
michael@0 867
michael@0 868 mParallelStreams[index] = nullptr;
michael@0 869 }
michael@0 870
michael@0 871 if (mStream) {
michael@0 872 nsCOMPtr<nsIInputStream> stream = do_QueryInterface(mStream);
michael@0 873
michael@0 874 if (NS_FAILED(stream->Close())) {
michael@0 875 NS_WARNING("Failed to close stream!");
michael@0 876 }
michael@0 877
michael@0 878 mStream = nullptr;
michael@0 879 }
michael@0 880
michael@0 881 return NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL);
michael@0 882 }
michael@0 883
michael@0 884 nsresult
michael@0 885 ReadHelper::Init()
michael@0 886 {
michael@0 887 mStream = MemoryOutputStream::Create(mSize);
michael@0 888 NS_ENSURE_TRUE(mStream, NS_ERROR_FAILURE);
michael@0 889
michael@0 890 return NS_OK;
michael@0 891 }
michael@0 892
michael@0 893 nsresult
michael@0 894 ReadHelper::DoAsyncRun(nsISupports* aStream)
michael@0 895 {
michael@0 896 NS_ASSERTION(aStream, "Passed a null stream!");
michael@0 897
michael@0 898 uint32_t flags = FileStreamWrapper::NOTIFY_PROGRESS;
michael@0 899
michael@0 900 nsCOMPtr<nsIInputStream> istream =
michael@0 901 new FileInputStreamWrapper(aStream, this, mLocation, mSize, flags);
michael@0 902
michael@0 903 FileService* service = FileService::Get();
michael@0 904 NS_ASSERTION(service, "This should never be null");
michael@0 905
michael@0 906 nsIEventTarget* target = service->StreamTransportTarget();
michael@0 907
michael@0 908 nsCOMPtr<nsIAsyncStreamCopier> copier;
michael@0 909 nsresult rv =
michael@0 910 NS_NewAsyncStreamCopier(getter_AddRefs(copier), istream, mStream, target,
michael@0 911 false, true, STREAM_COPY_BLOCK_SIZE);
michael@0 912 NS_ENSURE_SUCCESS(rv, rv);
michael@0 913
michael@0 914 rv = copier->AsyncCopy(this, nullptr);
michael@0 915 NS_ENSURE_SUCCESS(rv, rv);
michael@0 916
michael@0 917 mRequest = do_QueryInterface(copier);
michael@0 918
michael@0 919 return NS_OK;
michael@0 920 }
michael@0 921
michael@0 922 nsresult
michael@0 923 ReadHelper::GetSuccessResult(JSContext* aCx,
michael@0 924 JS::MutableHandle<JS::Value> aVal)
michael@0 925 {
michael@0 926 JS::Rooted<JSObject*> arrayBuffer(aCx);
michael@0 927 nsresult rv =
michael@0 928 nsContentUtils::CreateArrayBuffer(aCx, mStream->Data(), arrayBuffer.address());
michael@0 929 NS_ENSURE_SUCCESS(rv, rv);
michael@0 930
michael@0 931 aVal.setObject(*arrayBuffer);
michael@0 932 return NS_OK;
michael@0 933 }
michael@0 934
michael@0 935 nsresult
michael@0 936 ReadTextHelper::GetSuccessResult(JSContext* aCx,
michael@0 937 JS::MutableHandle<JS::Value> aVal)
michael@0 938 {
michael@0 939 nsAutoCString encoding;
michael@0 940 const nsCString& data = mStream->Data();
michael@0 941 // The BOM sniffing is baked into the "decode" part of the Encoding
michael@0 942 // Standard, which the File API references.
michael@0 943 if (!nsContentUtils::CheckForBOM(
michael@0 944 reinterpret_cast<const unsigned char *>(data.get()),
michael@0 945 data.Length(),
michael@0 946 encoding)) {
michael@0 947 // BOM sniffing failed. Try the API argument.
michael@0 948 if (!EncodingUtils::FindEncodingForLabel(mEncoding, encoding)) {
michael@0 949 // API argument failed. Since we are dealing with a file system file,
michael@0 950 // we don't have a meaningful type attribute for the blob available,
michael@0 951 // so proceeding to the next step, which is defaulting to UTF-8.
michael@0 952 encoding.AssignLiteral("UTF-8");
michael@0 953 }
michael@0 954 }
michael@0 955
michael@0 956 nsString tmpString;
michael@0 957 nsresult rv = nsContentUtils::ConvertStringFromEncoding(encoding, data,
michael@0 958 tmpString);
michael@0 959 NS_ENSURE_SUCCESS(rv, rv);
michael@0 960
michael@0 961 if (!xpc::StringToJsval(aCx, tmpString, aVal)) {
michael@0 962 NS_WARNING("Failed to convert string!");
michael@0 963 return NS_ERROR_FAILURE;
michael@0 964 }
michael@0 965 return NS_OK;
michael@0 966 }
michael@0 967
michael@0 968 nsresult
michael@0 969 WriteHelper::DoAsyncRun(nsISupports* aStream)
michael@0 970 {
michael@0 971 NS_ASSERTION(aStream, "Passed a null stream!");
michael@0 972
michael@0 973 uint32_t flags = FileStreamWrapper::NOTIFY_PROGRESS;
michael@0 974
michael@0 975 nsCOMPtr<nsIOutputStream> ostream =
michael@0 976 new FileOutputStreamWrapper(aStream, this, mLocation, mLength, flags);
michael@0 977
michael@0 978 FileService* service = FileService::Get();
michael@0 979 NS_ASSERTION(service, "This should never be null");
michael@0 980
michael@0 981 nsIEventTarget* target = service->StreamTransportTarget();
michael@0 982
michael@0 983 nsCOMPtr<nsIAsyncStreamCopier> copier;
michael@0 984 nsresult rv =
michael@0 985 NS_NewAsyncStreamCopier(getter_AddRefs(copier), mStream, ostream, target,
michael@0 986 true, false, STREAM_COPY_BLOCK_SIZE);
michael@0 987 NS_ENSURE_SUCCESS(rv, rv);
michael@0 988
michael@0 989 rv = copier->AsyncCopy(this, nullptr);
michael@0 990 NS_ENSURE_SUCCESS(rv, rv);
michael@0 991
michael@0 992 mRequest = do_QueryInterface(copier);
michael@0 993
michael@0 994 return NS_OK;
michael@0 995 }
michael@0 996
michael@0 997 nsresult
michael@0 998 TruncateHelper::DoAsyncRun(nsISupports* aStream)
michael@0 999 {
michael@0 1000 NS_ASSERTION(aStream, "Passed a null stream!");
michael@0 1001
michael@0 1002 nsRefPtr<AsyncTruncator> truncator = new AsyncTruncator(aStream, mOffset);
michael@0 1003
michael@0 1004 nsresult rv = truncator->AsyncWork(this, nullptr);
michael@0 1005 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1006
michael@0 1007 return NS_OK;
michael@0 1008 }
michael@0 1009
michael@0 1010 nsresult
michael@0 1011 TruncateHelper::AsyncTruncator::DoStreamWork(nsISupports* aStream)
michael@0 1012 {
michael@0 1013 nsCOMPtr<nsISeekableStream> sstream = do_QueryInterface(aStream);
michael@0 1014
michael@0 1015 nsresult rv = sstream->Seek(nsISeekableStream::NS_SEEK_SET, mOffset);
michael@0 1016 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1017
michael@0 1018 rv = sstream->SetEOF();
michael@0 1019 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1020
michael@0 1021 return NS_OK;
michael@0 1022 }
michael@0 1023
michael@0 1024 nsresult
michael@0 1025 FlushHelper::DoAsyncRun(nsISupports* aStream)
michael@0 1026 {
michael@0 1027 NS_ASSERTION(aStream, "Passed a null stream!");
michael@0 1028
michael@0 1029 nsRefPtr<AsyncFlusher> flusher = new AsyncFlusher(aStream);
michael@0 1030
michael@0 1031 nsresult rv = flusher->AsyncWork(this, nullptr);
michael@0 1032 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1033
michael@0 1034 return NS_OK;
michael@0 1035 }
michael@0 1036
michael@0 1037 nsresult
michael@0 1038 FlushHelper::AsyncFlusher::DoStreamWork(nsISupports* aStream)
michael@0 1039 {
michael@0 1040 nsCOMPtr<nsIOutputStream> ostream = do_QueryInterface(aStream);
michael@0 1041
michael@0 1042 nsresult rv = ostream->Flush();
michael@0 1043 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1044
michael@0 1045 return NS_OK;
michael@0 1046 }
michael@0 1047
michael@0 1048 nsresult
michael@0 1049 OpenStreamHelper::DoAsyncRun(nsISupports* aStream)
michael@0 1050 {
michael@0 1051 NS_ASSERTION(aStream, "Passed a null stream!");
michael@0 1052
michael@0 1053 uint32_t flags = FileStreamWrapper::NOTIFY_CLOSE |
michael@0 1054 FileStreamWrapper::NOTIFY_DESTROY;
michael@0 1055
michael@0 1056 mStream = mWholeFile ?
michael@0 1057 new FileInputStreamWrapper(aStream, this, 0, mLength, flags) :
michael@0 1058 new FileInputStreamWrapper(aStream, this, mStart, mLength, flags);
michael@0 1059
michael@0 1060 return NS_OK;
michael@0 1061 }
michael@0 1062
michael@0 1063 END_FILE_NAMESPACE

mercurial