michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "FileIOObject.h" michael@0: #include "mozilla/EventDispatcher.h" michael@0: #include "nsDOMFile.h" michael@0: #include "nsError.h" michael@0: #include "nsIDOMEvent.h" michael@0: #include "nsIDOMProgressEvent.h" michael@0: #include "nsComponentManagerUtils.h" michael@0: michael@0: #define ERROR_STR "error" michael@0: #define ABORT_STR "abort" michael@0: #define PROGRESS_STR "progress" michael@0: michael@0: namespace mozilla { michael@0: namespace dom { michael@0: michael@0: const uint64_t kUnknownSize = uint64_t(-1); michael@0: michael@0: NS_IMPL_ADDREF_INHERITED(FileIOObject, DOMEventTargetHelper) michael@0: NS_IMPL_RELEASE_INHERITED(FileIOObject, DOMEventTargetHelper) michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FileIOObject) michael@0: NS_INTERFACE_MAP_ENTRY(nsITimerCallback) michael@0: NS_INTERFACE_MAP_ENTRY(nsIStreamListener) michael@0: NS_INTERFACE_MAP_ENTRY(nsIRequestObserver) michael@0: NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CLASS(FileIOObject) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(FileIOObject, michael@0: DOMEventTargetHelper) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mProgressNotifier) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError) michael@0: // Can't traverse mChannel because it's a multithreaded object. michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(FileIOObject, michael@0: DOMEventTargetHelper) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mProgressNotifier) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mError) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mChannel) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_END michael@0: michael@0: NS_IMPL_EVENT_HANDLER(FileIOObject, abort) michael@0: NS_IMPL_EVENT_HANDLER(FileIOObject, error) michael@0: NS_IMPL_EVENT_HANDLER(FileIOObject, progress) michael@0: michael@0: FileIOObject::FileIOObject() michael@0: : mProgressEventWasDelayed(false), michael@0: mTimerIsActive(false), michael@0: mReadyState(0), michael@0: mTotal(0), mTransferred(0) michael@0: {} michael@0: michael@0: void michael@0: FileIOObject::StartProgressEventTimer() michael@0: { michael@0: if (!mProgressNotifier) { michael@0: mProgressNotifier = do_CreateInstance(NS_TIMER_CONTRACTID); michael@0: } michael@0: if (mProgressNotifier) { michael@0: mProgressEventWasDelayed = false; michael@0: mTimerIsActive = true; michael@0: mProgressNotifier->Cancel(); michael@0: mProgressNotifier->InitWithCallback(this, NS_PROGRESS_EVENT_INTERVAL, michael@0: nsITimer::TYPE_ONE_SHOT); michael@0: } michael@0: } michael@0: michael@0: void michael@0: FileIOObject::ClearProgressEventTimer() michael@0: { michael@0: mProgressEventWasDelayed = false; michael@0: mTimerIsActive = false; michael@0: if (mProgressNotifier) { michael@0: mProgressNotifier->Cancel(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: FileIOObject::DispatchError(nsresult rv, nsAString& finalEvent) michael@0: { michael@0: // Set the status attribute, and dispatch the error event michael@0: switch (rv) { michael@0: case NS_ERROR_FILE_NOT_FOUND: michael@0: mError = new DOMError(GetOwner(), NS_LITERAL_STRING("NotFoundError")); michael@0: break; michael@0: case NS_ERROR_FILE_ACCESS_DENIED: michael@0: mError = new DOMError(GetOwner(), NS_LITERAL_STRING("SecurityError")); michael@0: break; michael@0: default: michael@0: mError = new DOMError(GetOwner(), NS_LITERAL_STRING("NotReadableError")); michael@0: break; michael@0: } michael@0: michael@0: // Dispatch error event to signify load failure michael@0: DispatchProgressEvent(NS_LITERAL_STRING(ERROR_STR)); michael@0: DispatchProgressEvent(finalEvent); michael@0: } michael@0: michael@0: nsresult michael@0: FileIOObject::DispatchProgressEvent(const nsAString& aType) michael@0: { michael@0: nsCOMPtr event; michael@0: nsresult rv = NS_NewDOMProgressEvent(getter_AddRefs(event), this, michael@0: nullptr, nullptr); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: event->SetTrusted(true); michael@0: nsCOMPtr progress = do_QueryInterface(event); michael@0: NS_ENSURE_TRUE(progress, NS_ERROR_UNEXPECTED); michael@0: michael@0: bool known; michael@0: uint64_t size; michael@0: if (mTotal != kUnknownSize) { michael@0: known = true; michael@0: size = mTotal; michael@0: } else { michael@0: known = false; michael@0: size = 0; michael@0: } michael@0: rv = progress->InitProgressEvent(aType, false, false, known, michael@0: mTransferred, size); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return DispatchDOMEvent(nullptr, event, nullptr, nullptr); michael@0: } michael@0: michael@0: // nsITimerCallback michael@0: NS_IMETHODIMP michael@0: FileIOObject::Notify(nsITimer* aTimer) michael@0: { michael@0: nsresult rv; michael@0: mTimerIsActive = false; michael@0: michael@0: if (mProgressEventWasDelayed) { michael@0: rv = DispatchProgressEvent(NS_LITERAL_STRING("progress")); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: StartProgressEventTimer(); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // nsIStreamListener michael@0: NS_IMETHODIMP michael@0: FileIOObject::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext) michael@0: { michael@0: return DoOnStartRequest(aRequest, aContext); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: FileIOObject::DoOnStartRequest(nsIRequest *request, nsISupports *ctxt) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: FileIOObject::OnDataAvailable(nsIRequest *aRequest, michael@0: nsISupports *aContext, michael@0: nsIInputStream *aInputStream, michael@0: uint64_t aOffset, michael@0: uint32_t aCount) michael@0: { michael@0: nsresult rv; michael@0: rv = DoOnDataAvailable(aRequest, aContext, aInputStream, aOffset, aCount); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: mTransferred += aCount; michael@0: michael@0: //Notify the timer is the appropriate timeframe has passed michael@0: if (mTimerIsActive) { michael@0: mProgressEventWasDelayed = true; michael@0: } else { michael@0: rv = DispatchProgressEvent(NS_LITERAL_STRING(PROGRESS_STR)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: StartProgressEventTimer(); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: FileIOObject::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext, michael@0: nsresult aStatus) michael@0: { michael@0: // If we're here as a result of a call from Abort(), michael@0: // simply ignore the request. michael@0: if (aRequest != mChannel) michael@0: return NS_OK; michael@0: michael@0: // Cancel the progress event timer michael@0: ClearProgressEventTimer(); michael@0: michael@0: // FileIOObject must be in DONE stage after an operation michael@0: mReadyState = 2; michael@0: michael@0: nsString successEvent, termEvent; michael@0: nsresult rv = DoOnStopRequest(aRequest, aContext, aStatus, michael@0: successEvent, termEvent); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Set the status field as appropriate michael@0: if (NS_FAILED(aStatus)) { michael@0: DispatchError(aStatus, termEvent); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Dispatch event to signify end of a successful operation michael@0: DispatchProgressEvent(successEvent); michael@0: DispatchProgressEvent(termEvent); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: FileIOObject::Abort(ErrorResult& aRv) michael@0: { michael@0: if (mReadyState != 1) { michael@0: // XXX The spec doesn't say this michael@0: aRv.Throw(NS_ERROR_DOM_FILE_ABORT_ERR); michael@0: return; michael@0: } michael@0: michael@0: ClearProgressEventTimer(); michael@0: michael@0: mReadyState = 2; // There are DONE constants on multiple interfaces, michael@0: // but they all have value 2. michael@0: // XXX The spec doesn't say this michael@0: mError = new DOMError(GetOwner(), NS_LITERAL_STRING("AbortError")); michael@0: michael@0: nsString finalEvent; michael@0: DoAbort(finalEvent); michael@0: michael@0: // Dispatch the events michael@0: DispatchProgressEvent(NS_LITERAL_STRING(ABORT_STR)); michael@0: DispatchProgressEvent(finalEvent); michael@0: } michael@0: michael@0: } // namespace dom michael@0: } // namespace mozilla