dom/file/LockedFile.cpp

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

mercurial