gecko-dev/dom/file/FileReader.cpp
Gabriele Svelto ace6d1063f Bug 1600545 - Remove useless inclusions of header files generated from IDL files in dom/ r=Ehsan
The inclusions were removed with the following very crude script and the
resulting breakage was fixed up by hand. The manual fixups did either
revert the changes done by the script, replace a generic header with a more
specific one or replace a header with a forward declaration.

find . -name "*.idl" | grep -v web-platform | grep -v third_party | while read path; do
    interfaces=$(grep "^\(class\|interface\).*:.*" "$path" | cut -d' ' -f2)
    if [ -n "$interfaces" ]; then
        if [[ "$interfaces" == *$'\n'* ]]; then
          regexp="\("
          for i in $interfaces; do regexp="$regexp$i\|"; done
          regexp="${regexp%%\\\|}\)"
        else
          regexp="$interfaces"
        fi
        interface=$(basename "$path")
        rg -l "#include.*${interface%%.idl}.h" . | while read path2; do
            hits=$(grep -v "#include.*${interface%%.idl}.h" "$path2" | grep -c "$regexp" )
            if [ $hits -eq 0 ]; then
                echo "Removing ${interface} from ${path2}"
                grep -v "#include.*${interface%%.idl}.h" "$path2" > "$path2".tmp
                mv -f "$path2".tmp "$path2"
            fi
        done
    fi
done

Differential Revision: https://phabricator.services.mozilla.com/D55442

--HG--
extra : moz-landing-system : lando
2019-12-06 09:24:56 +00:00

767 lines
20 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "FileReader.h"
#include "nsIGlobalObject.h"
#include "nsITimer.h"
#include "js/ArrayBuffer.h" // JS::NewArrayBufferWithContents
#include "mozilla/Base64.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/dom/DOMException.h"
#include "mozilla/dom/DOMExceptionBinding.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/FileReaderBinding.h"
#include "mozilla/dom/ProgressEvent.h"
#include "mozilla/dom/WorkerCommon.h"
#include "mozilla/dom/WorkerRef.h"
#include "mozilla/dom/WorkerScope.h"
#include "mozilla/Encoding.h"
#include "nsAlgorithm.h"
#include "nsCycleCollectionParticipant.h"
#include "nsDOMJSUtils.h"
#include "nsError.h"
#include "nsNetUtil.h"
#include "xpcpublic.h"
#include "nsReadableUtils.h"
namespace mozilla {
namespace dom {
#define ABORT_STR "abort"
#define LOAD_STR "load"
#define LOADSTART_STR "loadstart"
#define LOADEND_STR "loadend"
#define ERROR_STR "error"
#define PROGRESS_STR "progress"
const uint64_t kUnknownSize = uint64_t(-1);
NS_IMPL_CYCLE_COLLECTION_CLASS(FileReader)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(FileReader,
DOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBlob)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mProgressNotifier)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(FileReader,
DOMEventTargetHelper)
tmp->Shutdown();
NS_IMPL_CYCLE_COLLECTION_UNLINK(mBlob)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mProgressNotifier)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mError)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(FileReader, DOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResultArrayBuffer)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FileReader)
NS_INTERFACE_MAP_ENTRY_CONCRETE(FileReader)
NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
NS_INTERFACE_MAP_ENTRY(nsIInputStreamCallback)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_ENTRY(nsINamed)
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
NS_IMPL_ADDREF_INHERITED(FileReader, DOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(FileReader, DOMEventTargetHelper)
class MOZ_RAII FileReaderDecreaseBusyCounter {
RefPtr<FileReader> mFileReader;
public:
explicit FileReaderDecreaseBusyCounter(FileReader* aFileReader)
: mFileReader(aFileReader) {}
~FileReaderDecreaseBusyCounter() { mFileReader->DecreaseBusyCounter(); }
};
void FileReader::RootResultArrayBuffer() { mozilla::HoldJSObjects(this); }
// FileReader constructors/initializers
FileReader::FileReader(nsIGlobalObject* aGlobal, WeakWorkerRef* aWorkerRef)
: DOMEventTargetHelper(aGlobal),
mFileData(nullptr),
mDataLen(0),
mDataFormat(FILE_AS_BINARY),
mResultArrayBuffer(nullptr),
mProgressEventWasDelayed(false),
mTimerIsActive(false),
mReadyState(EMPTY),
mTotal(0),
mTransferred(0),
mBusyCount(0),
mWeakWorkerRef(aWorkerRef) {
MOZ_ASSERT(aGlobal);
MOZ_ASSERT_IF(NS_IsMainThread(), !mWeakWorkerRef);
if (NS_IsMainThread()) {
mTarget = aGlobal->EventTargetFor(TaskCategory::Other);
} else {
mTarget = GetCurrentThreadSerialEventTarget();
}
SetDOMStringToNull(mResult);
}
FileReader::~FileReader() {
Shutdown();
DropJSObjects(this);
}
/* static */
already_AddRefed<FileReader> FileReader::Constructor(
const GlobalObject& aGlobal) {
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
RefPtr<WeakWorkerRef> workerRef;
if (!NS_IsMainThread()) {
JSContext* cx = aGlobal.Context();
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
workerRef = WeakWorkerRef::Create(workerPrivate);
}
RefPtr<FileReader> fileReader = new FileReader(global, workerRef);
return fileReader.forget();
}
// nsIInterfaceRequestor
NS_IMETHODIMP
FileReader::GetInterface(const nsIID& aIID, void** aResult) {
return QueryInterface(aIID, aResult);
}
void FileReader::GetResult(JSContext* aCx,
Nullable<OwningStringOrArrayBuffer>& aResult) {
JS::Rooted<JS::Value> result(aCx);
if (mDataFormat == FILE_AS_ARRAYBUFFER) {
if (mReadyState != DONE || !mResultArrayBuffer ||
!aResult.SetValue().SetAsArrayBuffer().Init(mResultArrayBuffer)) {
aResult.SetNull();
}
return;
}
if (mResult.IsVoid()) {
aResult.SetNull();
return;
}
aResult.SetValue().SetAsString() = mResult;
}
void FileReader::OnLoadEndArrayBuffer() {
AutoJSAPI jsapi;
if (!jsapi.Init(GetParentObject())) {
FreeDataAndDispatchError(NS_ERROR_FAILURE);
return;
}
RootResultArrayBuffer();
JSContext* cx = jsapi.cx();
mResultArrayBuffer = JS::NewArrayBufferWithContents(cx, mDataLen, mFileData);
if (mResultArrayBuffer) {
mFileData = nullptr; // Transfer ownership
FreeDataAndDispatchSuccess();
return;
}
// Let's handle the error status.
JS::Rooted<JS::Value> exceptionValue(cx);
if (!JS_GetPendingException(cx, &exceptionValue) ||
// This should not really happen, exception should always be an object.
!exceptionValue.isObject()) {
JS_ClearPendingException(jsapi.cx());
FreeDataAndDispatchError(NS_ERROR_OUT_OF_MEMORY);
return;
}
JS_ClearPendingException(jsapi.cx());
JS::Rooted<JSObject*> exceptionObject(cx, &exceptionValue.toObject());
JSErrorReport* er = JS_ErrorFromException(cx, exceptionObject);
if (!er || er->message()) {
FreeDataAndDispatchError(NS_ERROR_OUT_OF_MEMORY);
return;
}
nsAutoString errorName;
JSLinearString* name = js::GetErrorTypeName(cx, er->exnType);
if (name) {
AssignJSLinearString(errorName, name);
}
nsAutoCString errorMsg(er->message().c_str());
nsAutoCString errorNameC = NS_LossyConvertUTF16toASCII(errorName);
// XXX Code selected arbitrarily
mError =
new DOMException(NS_ERROR_DOM_INVALID_STATE_ERR, errorMsg, errorNameC,
DOMException_Binding::INVALID_STATE_ERR);
FreeDataAndDispatchError();
}
nsresult FileReader::DoAsyncWait() {
nsresult rv = IncreaseBusyCounter();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = mAsyncStream->AsyncWait(this,
/* aFlags*/ 0,
/* aRequestedCount */ 0, mTarget);
if (NS_WARN_IF(NS_FAILED(rv))) {
DecreaseBusyCounter();
return rv;
}
return NS_OK;
}
namespace {
void PopulateBufferForBinaryString(char16_t* aDest, const char* aSource,
uint32_t aCount) {
// Zero-extend each char to char16_t.
ConvertLatin1toUtf16(MakeSpan(aSource, aCount), MakeSpan(aDest, aCount));
}
nsresult ReadFuncBinaryString(nsIInputStream* aInputStream, void* aClosure,
const char* aFromRawSegment, uint32_t aToOffset,
uint32_t aCount, uint32_t* aWriteCount) {
char16_t* dest = static_cast<char16_t*>(aClosure) + aToOffset;
PopulateBufferForBinaryString(dest, aFromRawSegment, aCount);
*aWriteCount = aCount;
return NS_OK;
}
} // namespace
nsresult FileReader::DoReadData(uint64_t aCount) {
MOZ_ASSERT(mAsyncStream);
uint32_t bytesRead = 0;
if (mDataFormat == FILE_AS_BINARY) {
// Continuously update our binary string as data comes in
CheckedInt<uint64_t> size = mResult.Length();
size += aCount;
if (!size.isValid() || size.value() > UINT32_MAX || size.value() > mTotal) {
return NS_ERROR_OUT_OF_MEMORY;
}
uint32_t oldLen = mResult.Length();
MOZ_ASSERT(oldLen == mDataLen, "unexpected mResult length");
char16_t* dest = nullptr;
mResult.GetMutableData(&dest, size.value(), fallible);
NS_ENSURE_TRUE(dest, NS_ERROR_OUT_OF_MEMORY);
dest += oldLen;
if (NS_InputStreamIsBuffered(mAsyncStream)) {
nsresult rv = mAsyncStream->ReadSegments(ReadFuncBinaryString, dest,
aCount, &bytesRead);
NS_ENSURE_SUCCESS(rv, rv);
} else {
while (aCount > 0) {
char tmpBuffer[4096];
uint32_t minCount =
XPCOM_MIN(aCount, static_cast<uint64_t>(sizeof(tmpBuffer)));
uint32_t read;
nsresult rv = mAsyncStream->Read(tmpBuffer, minCount, &read);
if (rv == NS_BASE_STREAM_CLOSED) {
rv = NS_OK;
}
NS_ENSURE_SUCCESS(rv, rv);
if (read == 0) {
// The stream finished too early.
return NS_ERROR_OUT_OF_MEMORY;
}
PopulateBufferForBinaryString(dest, tmpBuffer, read);
dest += read;
aCount -= read;
bytesRead += read;
}
}
MOZ_ASSERT(size.value() == oldLen + bytesRead);
mResult.Truncate(size.value());
} else {
CheckedInt<uint64_t> size = mDataLen;
size += aCount;
// Update memory buffer to reflect the contents of the file
if (!size.isValid() ||
// PR_Realloc doesn't support over 4GB memory size even if 64-bit OS
// XXX: it's likely that this check is unnecessary and the comment is
// wrong because we no longer use PR_Realloc outside of NSPR and NSS.
size.value() > UINT32_MAX || size.value() > mTotal) {
return NS_ERROR_OUT_OF_MEMORY;
}
MOZ_DIAGNOSTIC_ASSERT(mFileData);
MOZ_RELEASE_ASSERT((mDataLen + aCount) <= mTotal);
nsresult rv = mAsyncStream->Read(mFileData + mDataLen, aCount, &bytesRead);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
mDataLen += bytesRead;
return NS_OK;
}
// Helper methods
void FileReader::ReadFileContent(Blob& aBlob, const nsAString& aCharset,
eDataFormat aDataFormat, ErrorResult& aRv) {
if (IsCurrentThreadRunningWorker() && !mWeakWorkerRef) {
// The worker is already shutting down.
return;
}
if (mReadyState == LOADING) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
mError = nullptr;
SetDOMStringToNull(mResult);
mResultArrayBuffer = nullptr;
mAsyncStream = nullptr;
mTransferred = 0;
mTotal = 0;
mReadyState = EMPTY;
FreeFileData();
mBlob = &aBlob;
mDataFormat = aDataFormat;
CopyUTF16toUTF8(aCharset, mCharset);
{
nsCOMPtr<nsIInputStream> stream;
mBlob->CreateInputStream(getter_AddRefs(stream), aRv);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
aRv = NS_MakeAsyncNonBlockingInputStream(stream.forget(),
getter_AddRefs(mAsyncStream));
if (NS_WARN_IF(aRv.Failed())) {
return;
}
}
MOZ_ASSERT(mAsyncStream);
mTotal = mBlob->GetSize(aRv);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
// Binary Format doesn't need a post-processing of the data. Everything is
// written directly into mResult.
if (mDataFormat != FILE_AS_BINARY) {
if (mDataFormat == FILE_AS_ARRAYBUFFER) {
mFileData = js_pod_malloc<char>(mTotal);
} else {
mFileData = (char*)malloc(mTotal);
}
if (!mFileData) {
NS_WARNING("Preallocation failed for ReadFileData");
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
}
}
aRv = DoAsyncWait();
if (NS_WARN_IF(aRv.Failed())) {
FreeFileData();
return;
}
// FileReader should be in loading state here
mReadyState = LOADING;
DispatchProgressEvent(NS_LITERAL_STRING(LOADSTART_STR));
}
nsresult FileReader::GetAsText(Blob* aBlob, const nsACString& aCharset,
const char* aFileData, uint32_t aDataLen,
nsAString& aResult) {
// Try the API argument.
const Encoding* encoding = Encoding::ForLabel(aCharset);
if (!encoding) {
// API argument failed. Try the type property of the blob.
nsAutoString type16;
aBlob->GetType(type16);
NS_ConvertUTF16toUTF8 type(type16);
nsAutoCString specifiedCharset;
bool haveCharset;
int32_t charsetStart, charsetEnd;
NS_ExtractCharsetFromContentType(type, specifiedCharset, &haveCharset,
&charsetStart, &charsetEnd);
encoding = Encoding::ForLabel(specifiedCharset);
if (!encoding) {
// Type property failed. Use UTF-8.
encoding = UTF_8_ENCODING;
}
}
auto data = MakeSpan(reinterpret_cast<const uint8_t*>(aFileData), aDataLen);
nsresult rv;
Tie(rv, encoding) = encoding->Decode(data, aResult);
return NS_FAILED(rv) ? rv : NS_OK;
}
nsresult FileReader::GetAsDataURL(Blob* aBlob, const char* aFileData,
uint32_t aDataLen, nsAString& aResult) {
aResult.AssignLiteral("data:");
nsAutoString contentType;
aBlob->GetType(contentType);
if (!contentType.IsEmpty()) {
aResult.Append(contentType);
} else {
aResult.AppendLiteral("application/octet-stream");
}
aResult.AppendLiteral(";base64,");
nsCString encodedData;
nsresult rv = Base64Encode(Substring(aFileData, aDataLen), encodedData);
NS_ENSURE_SUCCESS(rv, rv);
if (!AppendASCIItoUTF16(encodedData, aResult, fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}
/* virtual */
JSObject* FileReader::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) {
return FileReader_Binding::Wrap(aCx, this, aGivenProto);
}
void FileReader::StartProgressEventTimer() {
if (!mProgressNotifier) {
mProgressNotifier = NS_NewTimer(mTarget);
}
if (mProgressNotifier) {
mProgressEventWasDelayed = false;
mTimerIsActive = true;
mProgressNotifier->Cancel();
mProgressNotifier->InitWithCallback(this, NS_PROGRESS_EVENT_INTERVAL,
nsITimer::TYPE_ONE_SHOT);
}
}
void FileReader::ClearProgressEventTimer() {
mProgressEventWasDelayed = false;
mTimerIsActive = false;
if (mProgressNotifier) {
mProgressNotifier->Cancel();
}
}
void FileReader::FreeFileData() {
if (mFileData) {
if (mDataFormat == FILE_AS_ARRAYBUFFER) {
js_free(mFileData);
} else {
free(mFileData);
}
mFileData = nullptr;
}
mDataLen = 0;
}
void FileReader::FreeDataAndDispatchSuccess() {
FreeFileData();
mResult.SetIsVoid(false);
mAsyncStream = nullptr;
mBlob = nullptr;
// Dispatch event to signify end of a successful operation
DispatchProgressEvent(NS_LITERAL_STRING(LOAD_STR));
DispatchProgressEvent(NS_LITERAL_STRING(LOADEND_STR));
}
void FileReader::FreeDataAndDispatchError() {
MOZ_ASSERT(mError);
FreeFileData();
mResult.SetIsVoid(true);
mAsyncStream = nullptr;
mBlob = nullptr;
// Dispatch error event to signify load failure
DispatchProgressEvent(NS_LITERAL_STRING(ERROR_STR));
DispatchProgressEvent(NS_LITERAL_STRING(LOADEND_STR));
}
void FileReader::FreeDataAndDispatchError(nsresult aRv) {
// Set the status attribute, and dispatch the error event
switch (aRv) {
case NS_ERROR_FILE_NOT_FOUND:
mError = DOMException::Create(NS_ERROR_DOM_NOT_FOUND_ERR);
break;
case NS_ERROR_FILE_ACCESS_DENIED:
mError = DOMException::Create(NS_ERROR_DOM_SECURITY_ERR);
break;
default:
mError = DOMException::Create(NS_ERROR_DOM_FILE_NOT_READABLE_ERR);
break;
}
FreeDataAndDispatchError();
}
nsresult FileReader::DispatchProgressEvent(const nsAString& aType) {
ProgressEventInit init;
init.mBubbles = false;
init.mCancelable = false;
init.mLoaded = mTransferred;
if (mTotal != kUnknownSize) {
init.mLengthComputable = true;
init.mTotal = mTotal;
} else {
init.mLengthComputable = false;
init.mTotal = 0;
}
RefPtr<ProgressEvent> event = ProgressEvent::Constructor(this, aType, init);
event->SetTrusted(true);
ErrorResult rv;
DispatchEvent(*event, rv);
return rv.StealNSResult();
}
// nsITimerCallback
NS_IMETHODIMP
FileReader::Notify(nsITimer* aTimer) {
nsresult rv;
mTimerIsActive = false;
if (mProgressEventWasDelayed) {
rv = DispatchProgressEvent(NS_LITERAL_STRING("progress"));
NS_ENSURE_SUCCESS(rv, rv);
StartProgressEventTimer();
}
return NS_OK;
}
// InputStreamCallback
NS_IMETHODIMP
FileReader::OnInputStreamReady(nsIAsyncInputStream* aStream) {
if (mReadyState != LOADING || aStream != mAsyncStream) {
return NS_OK;
}
// We use this class to decrease the busy counter at the end of this method.
// In theory we can do it immediatelly but, for debugging reasons, we want to
// be 100% sure we have a workerRef when OnLoadEnd() is called.
FileReaderDecreaseBusyCounter RAII(this);
uint64_t count;
nsresult rv = aStream->Available(&count);
if (NS_SUCCEEDED(rv) && count) {
rv = DoReadData(count);
if (NS_SUCCEEDED(rv)) {
rv = DoAsyncWait();
}
}
if (NS_FAILED(rv) || !count) {
if (rv == NS_BASE_STREAM_CLOSED) {
rv = NS_OK;
}
OnLoadEnd(rv);
return NS_OK;
}
mTransferred += count;
// Notify the timer is the appropriate timeframe has passed
if (mTimerIsActive) {
mProgressEventWasDelayed = true;
} else {
rv = DispatchProgressEvent(NS_LITERAL_STRING(PROGRESS_STR));
NS_ENSURE_SUCCESS(rv, rv);
StartProgressEventTimer();
}
return NS_OK;
}
// nsINamed
NS_IMETHODIMP
FileReader::GetName(nsACString& aName) {
aName.AssignLiteral("FileReader");
return NS_OK;
}
void FileReader::OnLoadEnd(nsresult aStatus) {
// Cancel the progress event timer
ClearProgressEventTimer();
// FileReader must be in DONE stage after an operation
mReadyState = DONE;
// Quick return, if failed.
if (NS_FAILED(aStatus)) {
FreeDataAndDispatchError(aStatus);
return;
}
// In case we read a different number of bytes, we can assume that the
// underlying storage has changed. We should not continue.
if (mDataLen != mTotal) {
FreeDataAndDispatchError(NS_ERROR_FAILURE);
return;
}
// ArrayBuffer needs a custom handling.
if (mDataFormat == FILE_AS_ARRAYBUFFER) {
OnLoadEndArrayBuffer();
return;
}
nsresult rv = NS_OK;
// We don't do anything special for Binary format.
if (mDataFormat == FILE_AS_DATAURL) {
rv = GetAsDataURL(mBlob, mFileData, mDataLen, mResult);
} else if (mDataFormat == FILE_AS_TEXT) {
if (!mFileData && mDataLen) {
rv = NS_ERROR_OUT_OF_MEMORY;
} else if (!mFileData) {
rv = GetAsText(mBlob, mCharset, "", mDataLen, mResult);
} else {
rv = GetAsText(mBlob, mCharset, mFileData, mDataLen, mResult);
}
}
if (NS_WARN_IF(NS_FAILED(rv))) {
FreeDataAndDispatchError(rv);
return;
}
FreeDataAndDispatchSuccess();
}
void FileReader::Abort() {
if (mReadyState == EMPTY || mReadyState == DONE) {
return;
}
MOZ_ASSERT(mReadyState == LOADING);
ClearProgressEventTimer();
mReadyState = DONE;
// XXX The spec doesn't say this
mError = DOMException::Create(NS_ERROR_DOM_ABORT_ERR);
// Revert status and result attributes
SetDOMStringToNull(mResult);
mResultArrayBuffer = nullptr;
mAsyncStream = nullptr;
mBlob = nullptr;
// Clean up memory buffer
FreeFileData();
// Dispatch the events
DispatchProgressEvent(NS_LITERAL_STRING(ABORT_STR));
DispatchProgressEvent(NS_LITERAL_STRING(LOADEND_STR));
}
nsresult FileReader::IncreaseBusyCounter() {
if (mWeakWorkerRef && mBusyCount++ == 0) {
if (NS_WARN_IF(!mWeakWorkerRef->GetPrivate())) {
return NS_ERROR_FAILURE;
}
RefPtr<FileReader> self = this;
RefPtr<StrongWorkerRef> ref =
StrongWorkerRef::Create(mWeakWorkerRef->GetPrivate(), "FileReader",
[self]() { self->Shutdown(); });
if (NS_WARN_IF(!ref)) {
return NS_ERROR_FAILURE;
}
mStrongWorkerRef = ref;
}
return NS_OK;
}
void FileReader::DecreaseBusyCounter() {
MOZ_ASSERT_IF(mStrongWorkerRef, mBusyCount);
if (mStrongWorkerRef && --mBusyCount == 0) {
mStrongWorkerRef = nullptr;
}
}
void FileReader::Shutdown() {
mReadyState = DONE;
if (mAsyncStream) {
mAsyncStream->Close();
mAsyncStream = nullptr;
}
FreeFileData();
mResultArrayBuffer = nullptr;
if (mWeakWorkerRef && mBusyCount != 0) {
mStrongWorkerRef = nullptr;
mWeakWorkerRef = nullptr;
mBusyCount = 0;
}
}
} // namespace dom
} // namespace mozilla