1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=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
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "FileReader.h"
9 #include "nsIGlobalObject.h"
12 #include "js/ArrayBuffer.h" // JS::NewArrayBufferWithContents
13 #include "mozilla/Base64.h"
14 #include "mozilla/CheckedInt.h"
15 #include "mozilla/dom/DOMException.h"
16 #include "mozilla/dom/DOMExceptionBinding.h"
17 #include "mozilla/dom/File.h"
18 #include "mozilla/dom/FileReaderBinding.h"
19 #include "mozilla/dom/ProgressEvent.h"
20 #include "mozilla/dom/UnionTypes.h"
21 #include "mozilla/dom/ScriptSettings.h"
22 #include "mozilla/dom/WorkerCommon.h"
23 #include "mozilla/dom/WorkerRef.h"
24 #include "mozilla/dom/WorkerScope.h"
25 #include "mozilla/Encoding.h"
26 #include "mozilla/HoldDropJSObjects.h"
27 #include "nsAlgorithm.h"
28 #include "nsCycleCollectionParticipant.h"
29 #include "nsDOMJSUtils.h"
31 #include "nsNetUtil.h"
32 #include "nsStreamUtils.h"
33 #include "nsThreadUtils.h"
34 #include "xpcpublic.h"
35 #include "nsReadableUtils.h"
37 namespace mozilla::dom
{
39 #define ABORT_STR u"abort"
40 #define LOAD_STR u"load"
41 #define LOADSTART_STR u"loadstart"
42 #define LOADEND_STR u"loadend"
43 #define ERROR_STR u"error"
44 #define PROGRESS_STR u"progress"
46 const uint64_t kUnknownSize
= uint64_t(-1);
48 NS_IMPL_CYCLE_COLLECTION_CLASS(FileReader
)
50 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(FileReader
,
52 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBlob
)
53 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mProgressNotifier
)
54 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError
)
55 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
57 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(FileReader
,
60 NS_IMPL_CYCLE_COLLECTION_UNLINK(mBlob
)
61 NS_IMPL_CYCLE_COLLECTION_UNLINK(mProgressNotifier
)
62 NS_IMPL_CYCLE_COLLECTION_UNLINK(mError
)
63 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
64 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
66 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(FileReader
, DOMEventTargetHelper
)
67 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResultArrayBuffer
)
68 NS_IMPL_CYCLE_COLLECTION_TRACE_END
70 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FileReader
)
71 NS_INTERFACE_MAP_ENTRY_CONCRETE(FileReader
)
72 NS_INTERFACE_MAP_ENTRY(nsITimerCallback
)
73 NS_INTERFACE_MAP_ENTRY(nsIInputStreamCallback
)
74 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference
)
75 NS_INTERFACE_MAP_ENTRY(nsINamed
)
76 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper
)
78 NS_IMPL_ADDREF_INHERITED(FileReader
, DOMEventTargetHelper
)
79 NS_IMPL_RELEASE_INHERITED(FileReader
, DOMEventTargetHelper
)
81 class MOZ_RAII FileReaderDecreaseBusyCounter
{
82 RefPtr
<FileReader
> mFileReader
;
85 explicit FileReaderDecreaseBusyCounter(FileReader
* aFileReader
)
86 : mFileReader(aFileReader
) {}
88 ~FileReaderDecreaseBusyCounter() { mFileReader
->DecreaseBusyCounter(); }
91 class FileReader::AsyncWaitRunnable final
: public CancelableRunnable
{
93 explicit AsyncWaitRunnable(FileReader
* aReader
)
94 : CancelableRunnable("FileReader::AsyncWaitRunnable"), mReader(aReader
) {}
99 mReader
->InitialAsyncWait();
104 nsresult
Cancel() override
{
110 RefPtr
<FileReader
> mReader
;
113 void FileReader::RootResultArrayBuffer() { mozilla::HoldJSObjects(this); }
115 // FileReader constructors/initializers
117 FileReader::FileReader(nsIGlobalObject
* aGlobal
, WeakWorkerRef
* aWorkerRef
)
118 : DOMEventTargetHelper(aGlobal
),
121 mDataFormat(FILE_AS_BINARY
),
122 mResultArrayBuffer(nullptr),
123 mProgressEventWasDelayed(false),
124 mTimerIsActive(false),
129 mWeakWorkerRef(aWorkerRef
) {
131 MOZ_ASSERT_IF(NS_IsMainThread(), !mWeakWorkerRef
);
133 if (NS_IsMainThread()) {
134 mTarget
= aGlobal
->SerialEventTarget();
136 mTarget
= GetCurrentSerialEventTarget();
139 SetDOMStringToNull(mResult
);
142 FileReader::~FileReader() {
148 already_AddRefed
<FileReader
> FileReader::Constructor(
149 const GlobalObject
& aGlobal
) {
150 nsCOMPtr
<nsIGlobalObject
> global
= do_QueryInterface(aGlobal
.GetAsSupports());
151 RefPtr
<WeakWorkerRef
> workerRef
;
153 if (!NS_IsMainThread()) {
154 JSContext
* cx
= aGlobal
.Context();
155 WorkerPrivate
* workerPrivate
= GetWorkerPrivateFromContext(cx
);
157 workerRef
= WeakWorkerRef::Create(workerPrivate
);
160 RefPtr
<FileReader
> fileReader
= new FileReader(global
, workerRef
);
162 return fileReader
.forget();
165 // nsIInterfaceRequestor
168 FileReader::GetInterface(const nsIID
& aIID
, void** aResult
) {
169 return QueryInterface(aIID
, aResult
);
172 void FileReader::GetResult(JSContext
* aCx
,
173 Nullable
<OwningStringOrArrayBuffer
>& aResult
) {
174 JS::Rooted
<JS::Value
> result(aCx
);
176 if (mDataFormat
== FILE_AS_ARRAYBUFFER
) {
177 if (mReadyState
!= DONE
|| !mResultArrayBuffer
||
178 !aResult
.SetValue().SetAsArrayBuffer().Init(mResultArrayBuffer
)) {
185 if (mReadyState
!= DONE
|| mResult
.IsVoid()) {
190 aResult
.SetValue().SetAsString() = mResult
;
193 void FileReader::OnLoadEndArrayBuffer() {
195 if (!jsapi
.Init(GetParentObject())) {
196 FreeDataAndDispatchError(NS_ERROR_FAILURE
);
200 RootResultArrayBuffer();
202 JSContext
* cx
= jsapi
.cx();
204 // |mFileData| will be deallocated in FileReader's destructor when this
205 // ArrayBuffer allocation failed.
206 mResultArrayBuffer
= JS::NewArrayBufferWithContents(
207 cx
, mDataLen
, mFileData
,
208 JS::NewArrayBufferOutOfMemory::CallerMustFreeMemory
);
209 if (mResultArrayBuffer
) {
210 mFileData
= nullptr; // Transfer ownership
211 FreeDataAndDispatchSuccess();
215 // Let's handle the error status.
217 JS::Rooted
<JS::Value
> exceptionValue(cx
);
218 if (!JS_GetPendingException(cx
, &exceptionValue
) ||
219 // This should not really happen, exception should always be an object.
220 !exceptionValue
.isObject()) {
221 JS_ClearPendingException(jsapi
.cx());
222 FreeDataAndDispatchError(NS_ERROR_OUT_OF_MEMORY
);
226 JS_ClearPendingException(jsapi
.cx());
228 JS::Rooted
<JSObject
*> exceptionObject(cx
, &exceptionValue
.toObject());
229 JSErrorReport
* er
= JS_ErrorFromException(cx
, exceptionObject
);
230 if (!er
|| er
->message()) {
231 FreeDataAndDispatchError(NS_ERROR_OUT_OF_MEMORY
);
235 nsAutoString errorName
;
236 JSLinearString
* name
= js::GetErrorTypeName(cx
, er
->exnType
);
238 AssignJSLinearString(errorName
, name
);
241 nsAutoCString
errorMsg(er
->message().c_str());
242 nsAutoCString errorNameC
= NS_LossyConvertUTF16toASCII(errorName
);
243 // XXX Code selected arbitrarily
245 new DOMException(NS_ERROR_DOM_INVALID_STATE_ERR
, errorMsg
, errorNameC
,
246 DOMException_Binding::INVALID_STATE_ERR
);
248 FreeDataAndDispatchError();
251 nsresult
FileReader::DoAsyncWait() {
252 nsresult rv
= IncreaseBusyCounter();
253 if (NS_WARN_IF(NS_FAILED(rv
))) {
257 rv
= mAsyncStream
->AsyncWait(this,
259 /* aRequestedCount */ 0, mTarget
);
260 if (NS_WARN_IF(NS_FAILED(rv
))) {
261 DecreaseBusyCounter();
270 void PopulateBufferForBinaryString(char16_t
* aDest
, const char* aSource
,
272 // Zero-extend each char to char16_t.
273 ConvertLatin1toUtf16(Span(aSource
, aCount
), Span(aDest
, aCount
));
276 nsresult
ReadFuncBinaryString(nsIInputStream
* aInputStream
, void* aClosure
,
277 const char* aFromRawSegment
, uint32_t aToOffset
,
278 uint32_t aCount
, uint32_t* aWriteCount
) {
279 char16_t
* dest
= static_cast<char16_t
*>(aClosure
) + aToOffset
;
280 PopulateBufferForBinaryString(dest
, aFromRawSegment
, aCount
);
281 *aWriteCount
= aCount
;
287 nsresult
FileReader::DoReadData(uint64_t aCount
) {
288 MOZ_ASSERT(mAsyncStream
);
290 uint32_t bytesRead
= 0;
292 if (mDataFormat
== FILE_AS_BINARY
) {
293 // Continuously update our binary string as data comes in
294 CheckedInt
<uint64_t> size
{mResult
.Length()};
297 if (!size
.isValid() || size
.value() > UINT32_MAX
|| size
.value() > mTotal
) {
298 return NS_ERROR_OUT_OF_MEMORY
;
301 uint32_t lenBeforeRead
= mResult
.Length();
302 MOZ_ASSERT(lenBeforeRead
== mDataLen
, "unexpected mResult length");
304 mResult
.SetLength(lenBeforeRead
+ aCount
);
305 char16_t
* currentPos
= mResult
.BeginWriting() + lenBeforeRead
;
307 if (NS_InputStreamIsBuffered(mAsyncStream
)) {
308 nsresult rv
= mAsyncStream
->ReadSegments(ReadFuncBinaryString
, currentPos
,
310 NS_ENSURE_SUCCESS(rv
, NS_OK
);
313 char tmpBuffer
[4096];
315 XPCOM_MIN(aCount
, static_cast<uint64_t>(sizeof(tmpBuffer
)));
318 nsresult rv
= mAsyncStream
->Read(tmpBuffer
, minCount
, &read
);
319 if (rv
== NS_BASE_STREAM_CLOSED
) {
323 NS_ENSURE_SUCCESS(rv
, NS_OK
);
326 // The stream finished too early.
327 return NS_ERROR_OUT_OF_MEMORY
;
330 PopulateBufferForBinaryString(currentPos
, tmpBuffer
, read
);
338 MOZ_ASSERT(size
.value() == lenBeforeRead
+ bytesRead
);
339 mResult
.Truncate(size
.value());
341 CheckedInt
<uint64_t> size
= mDataLen
;
344 // Update memory buffer to reflect the contents of the file
345 if (!size
.isValid() ||
346 // PR_Realloc doesn't support over 4GB memory size even if 64-bit OS
347 // XXX: it's likely that this check is unnecessary and the comment is
348 // wrong because we no longer use PR_Realloc outside of NSPR and NSS.
349 size
.value() > UINT32_MAX
|| size
.value() > mTotal
) {
350 return NS_ERROR_OUT_OF_MEMORY
;
353 MOZ_DIAGNOSTIC_ASSERT(mFileData
);
354 MOZ_RELEASE_ASSERT((mDataLen
+ aCount
) <= mTotal
);
356 nsresult rv
= mAsyncStream
->Read(mFileData
+ mDataLen
, aCount
, &bytesRead
);
357 if (NS_WARN_IF(NS_FAILED(rv
))) {
362 mDataLen
+= bytesRead
;
368 void FileReader::ReadFileContent(Blob
& aBlob
, const nsAString
& aCharset
,
369 eDataFormat aDataFormat
, ErrorResult
& aRv
) {
370 if (IsCurrentThreadRunningWorker() && !mWeakWorkerRef
) {
371 // The worker is already shutting down.
375 if (mReadyState
== LOADING
) {
376 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
382 SetDOMStringToNull(mResult
);
383 mResultArrayBuffer
= nullptr;
385 mAsyncStream
= nullptr;
393 mDataFormat
= aDataFormat
;
394 CopyUTF16toUTF8(aCharset
, mCharset
);
397 nsCOMPtr
<nsIInputStream
> stream
;
398 mBlob
->CreateInputStream(getter_AddRefs(stream
), aRv
);
399 if (NS_WARN_IF(aRv
.Failed())) {
403 aRv
= NS_MakeAsyncNonBlockingInputStream(stream
.forget(),
404 getter_AddRefs(mAsyncStream
));
405 if (NS_WARN_IF(aRv
.Failed())) {
410 MOZ_ASSERT(mAsyncStream
);
412 mTotal
= mBlob
->GetSize(aRv
);
413 if (NS_WARN_IF(aRv
.Failed())) {
417 // Binary Format doesn't need a post-processing of the data. Everything is
418 // written directly into mResult.
419 if (mDataFormat
!= FILE_AS_BINARY
) {
420 if (mDataFormat
== FILE_AS_ARRAYBUFFER
) {
421 mFileData
= js_pod_malloc
<char>(mTotal
);
423 mFileData
= (char*)malloc(mTotal
);
427 NS_WARNING("Preallocation failed for ReadFileData");
428 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
433 mAsyncWaitRunnable
= new AsyncWaitRunnable(this);
434 aRv
= NS_DispatchToCurrentThread(mAsyncWaitRunnable
);
435 if (NS_WARN_IF(aRv
.Failed())) {
440 // FileReader should be in loading state here
441 mReadyState
= LOADING
;
444 void FileReader::InitialAsyncWait() {
445 mAsyncWaitRunnable
= nullptr;
447 nsresult rv
= DoAsyncWait();
448 if (NS_WARN_IF(NS_FAILED(rv
))) {
454 DispatchProgressEvent(nsLiteralString(LOADSTART_STR
));
457 nsresult
FileReader::GetAsText(Blob
* aBlob
, const nsACString
& aCharset
,
458 const char* aFileData
, uint32_t aDataLen
,
459 nsAString
& aResult
) {
460 // Try the API argument.
461 const Encoding
* encoding
= Encoding::ForLabel(aCharset
);
463 // API argument failed. Try the type property of the blob.
465 aBlob
->GetType(type16
);
466 NS_ConvertUTF16toUTF8
type(type16
);
467 nsAutoCString specifiedCharset
;
469 int32_t charsetStart
, charsetEnd
;
470 NS_ExtractCharsetFromContentType(type
, specifiedCharset
, &haveCharset
,
471 &charsetStart
, &charsetEnd
);
472 encoding
= Encoding::ForLabel(specifiedCharset
);
474 // Type property failed. Use UTF-8.
475 encoding
= UTF_8_ENCODING
;
479 auto data
= Span(reinterpret_cast<const uint8_t*>(aFileData
), aDataLen
);
481 std::tie(rv
, std::ignore
) = encoding
->Decode(data
, aResult
);
482 return NS_FAILED(rv
) ? rv
: NS_OK
;
485 nsresult
FileReader::GetAsDataURL(Blob
* aBlob
, const char* aFileData
,
486 uint32_t aDataLen
, nsAString
& aResult
) {
487 aResult
.AssignLiteral("data:");
489 nsAutoString contentType
;
490 aBlob
->GetType(contentType
);
491 if (!contentType
.IsEmpty()) {
492 aResult
.Append(contentType
);
494 aResult
.AppendLiteral("application/octet-stream");
496 aResult
.AppendLiteral(";base64,");
498 return Base64EncodeAppend(aFileData
, aDataLen
, aResult
);
502 JSObject
* FileReader::WrapObject(JSContext
* aCx
,
503 JS::Handle
<JSObject
*> aGivenProto
) {
504 return FileReader_Binding::Wrap(aCx
, this, aGivenProto
);
507 void FileReader::StartProgressEventTimer() {
508 if (!NS_IsMainThread() && !mWeakWorkerRef
) {
509 // The worker is possibly shutting down if dispatching a DOM event right
510 // before this call triggered an InterruptCallback call.
511 // XXX Note, the check is limited to workers for now, since it is unclear
512 // in the spec how FileReader should behave in this case on the main thread.
516 if (!mProgressNotifier
) {
517 mProgressNotifier
= NS_NewTimer(mTarget
);
520 if (mProgressNotifier
) {
521 mProgressEventWasDelayed
= false;
522 mTimerIsActive
= true;
523 mProgressNotifier
->Cancel();
524 mProgressNotifier
->InitWithCallback(this, NS_PROGRESS_EVENT_INTERVAL
,
525 nsITimer::TYPE_ONE_SHOT
);
529 void FileReader::ClearProgressEventTimer() {
530 mProgressEventWasDelayed
= false;
531 mTimerIsActive
= false;
532 if (mProgressNotifier
) {
533 mProgressNotifier
->Cancel();
537 void FileReader::FreeFileData() {
539 if (mDataFormat
== FILE_AS_ARRAYBUFFER
) {
550 void FileReader::FreeDataAndDispatchSuccess() {
552 mResult
.SetIsVoid(false);
553 mAsyncStream
= nullptr;
556 // Dispatch event to signify end of a successful operation
557 DispatchProgressEvent(nsLiteralString(LOAD_STR
));
558 DispatchProgressEvent(nsLiteralString(LOADEND_STR
));
561 void FileReader::FreeDataAndDispatchError() {
565 mResult
.SetIsVoid(true);
566 mAsyncStream
= nullptr;
569 // Dispatch error event to signify load failure
570 DispatchProgressEvent(nsLiteralString(ERROR_STR
));
571 DispatchProgressEvent(nsLiteralString(LOADEND_STR
));
574 void FileReader::FreeDataAndDispatchError(nsresult aRv
) {
575 // Set the status attribute, and dispatch the error event
577 case NS_ERROR_FILE_NOT_FOUND
:
578 mError
= DOMException::Create(NS_ERROR_DOM_NOT_FOUND_ERR
);
580 case NS_ERROR_FILE_ACCESS_DENIED
:
581 mError
= DOMException::Create(NS_ERROR_DOM_SECURITY_ERR
);
584 mError
= DOMException::Create(NS_ERROR_DOM_FILE_NOT_READABLE_ERR
);
588 FreeDataAndDispatchError();
591 nsresult
FileReader::DispatchProgressEvent(const nsAString
& aType
) {
592 ProgressEventInit init
;
593 init
.mBubbles
= false;
594 init
.mCancelable
= false;
595 init
.mLoaded
= mTransferred
;
597 if (mTotal
!= kUnknownSize
) {
598 init
.mLengthComputable
= true;
599 init
.mTotal
= mTotal
;
601 init
.mLengthComputable
= false;
604 RefPtr
<ProgressEvent
> event
= ProgressEvent::Constructor(this, aType
, init
);
605 event
->SetTrusted(true);
608 DispatchEvent(*event
, rv
);
609 return rv
.StealNSResult();
614 FileReader::Notify(nsITimer
* aTimer
) {
616 mTimerIsActive
= false;
618 if (mProgressEventWasDelayed
) {
619 rv
= DispatchProgressEvent(u
"progress"_ns
);
620 NS_ENSURE_SUCCESS(rv
, rv
);
622 StartProgressEventTimer();
628 // InputStreamCallback
630 FileReader::OnInputStreamReady(nsIAsyncInputStream
* aStream
) {
631 // We use this class to decrease the busy counter at the end of this method.
632 // In theory we can do it immediatelly but, for debugging reasons, we want to
633 // be 100% sure we have a workerRef when OnLoadEnd() is called.
634 FileReaderDecreaseBusyCounter
RAII(this);
636 if (mReadyState
!= LOADING
|| aStream
!= mAsyncStream
) {
641 nsresult rv
= aStream
->Available(&count
);
643 if (NS_SUCCEEDED(rv
) && count
) {
644 rv
= DoReadData(count
);
646 if (NS_SUCCEEDED(rv
)) {
651 if (NS_FAILED(rv
) || !count
) {
652 if (rv
== NS_BASE_STREAM_CLOSED
) {
659 mTransferred
+= count
;
661 // Notify the timer is the appropriate timeframe has passed
662 if (mTimerIsActive
) {
663 mProgressEventWasDelayed
= true;
665 rv
= DispatchProgressEvent(nsLiteralString(PROGRESS_STR
));
666 NS_ENSURE_SUCCESS(rv
, rv
);
668 StartProgressEventTimer();
676 FileReader::GetName(nsACString
& aName
) {
677 aName
.AssignLiteral("FileReader");
681 void FileReader::OnLoadEnd(nsresult aStatus
) {
682 // Cancel the progress event timer
683 ClearProgressEventTimer();
685 // FileReader must be in DONE stage after an operation
688 // Quick return, if failed.
689 if (NS_FAILED(aStatus
)) {
690 FreeDataAndDispatchError(aStatus
);
694 // In case we read a different number of bytes, we can assume that the
695 // underlying storage has changed. We should not continue.
696 if (mDataLen
!= mTotal
) {
697 FreeDataAndDispatchError(NS_ERROR_FAILURE
);
701 // ArrayBuffer needs a custom handling.
702 if (mDataFormat
== FILE_AS_ARRAYBUFFER
) {
703 OnLoadEndArrayBuffer();
709 // We don't do anything special for Binary format.
711 if (mDataFormat
== FILE_AS_DATAURL
) {
712 rv
= GetAsDataURL(mBlob
, mFileData
, mDataLen
, mResult
);
713 } else if (mDataFormat
== FILE_AS_TEXT
) {
714 if (!mFileData
&& mDataLen
) {
715 rv
= NS_ERROR_OUT_OF_MEMORY
;
716 } else if (!mFileData
) {
717 rv
= GetAsText(mBlob
, mCharset
, "", mDataLen
, mResult
);
719 rv
= GetAsText(mBlob
, mCharset
, mFileData
, mDataLen
, mResult
);
723 if (NS_WARN_IF(NS_FAILED(rv
))) {
724 FreeDataAndDispatchError(rv
);
728 FreeDataAndDispatchSuccess();
731 void FileReader::Abort() {
732 if (mReadyState
== EMPTY
|| mReadyState
== DONE
) {
736 MOZ_ASSERT(mReadyState
== LOADING
);
740 // XXX The spec doesn't say this
741 mError
= DOMException::Create(NS_ERROR_DOM_ABORT_ERR
);
743 // Revert status and result attributes
744 SetDOMStringToNull(mResult
);
745 mResultArrayBuffer
= nullptr;
749 // Dispatch the events
750 DispatchProgressEvent(nsLiteralString(ABORT_STR
));
751 DispatchProgressEvent(nsLiteralString(LOADEND_STR
));
754 nsresult
FileReader::IncreaseBusyCounter() {
755 if (mWeakWorkerRef
&& mBusyCount
++ == 0) {
756 if (NS_WARN_IF(!mWeakWorkerRef
->GetPrivate())) {
757 return NS_ERROR_FAILURE
;
760 RefPtr
<FileReader
> self
= this;
762 RefPtr
<StrongWorkerRef
> ref
=
763 StrongWorkerRef::Create(mWeakWorkerRef
->GetPrivate(), "FileReader",
764 [self
]() { self
->Shutdown(); });
765 if (NS_WARN_IF(!ref
)) {
766 return NS_ERROR_FAILURE
;
769 mStrongWorkerRef
= ref
;
775 void FileReader::DecreaseBusyCounter() {
776 MOZ_ASSERT_IF(mStrongWorkerRef
, mBusyCount
);
777 if (mStrongWorkerRef
&& --mBusyCount
== 0) {
778 mStrongWorkerRef
= nullptr;
782 void FileReader::Cleanup() {
785 if (mAsyncWaitRunnable
) {
786 mAsyncWaitRunnable
->Cancel();
787 mAsyncWaitRunnable
= nullptr;
791 mAsyncStream
->Close();
792 mAsyncStream
= nullptr;
795 ClearProgressEventTimer();
797 mResultArrayBuffer
= nullptr;
800 void FileReader::Shutdown() {
802 if (mWeakWorkerRef
) {
803 mWeakWorkerRef
= nullptr;
807 } // namespace mozilla::dom