michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=2 et sw=2 tw=80: */ 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 file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "DOMRequest.h" michael@0: michael@0: #include "DOMError.h" michael@0: #include "nsCxPusher.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "DOMCursor.h" michael@0: #include "nsIDOMEvent.h" michael@0: michael@0: using mozilla::dom::DOMRequest; michael@0: using mozilla::dom::DOMRequestService; michael@0: using mozilla::dom::DOMCursor; michael@0: using mozilla::AutoSafeJSContext; michael@0: michael@0: DOMRequest::DOMRequest(nsPIDOMWindow* aWindow) michael@0: : DOMEventTargetHelper(aWindow->IsInnerWindow() ? michael@0: aWindow : aWindow->GetCurrentInnerWindow()) michael@0: , mResult(JSVAL_VOID) michael@0: , mDone(false) michael@0: { michael@0: } michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CLASS(DOMRequest) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DOMRequest, michael@0: DOMEventTargetHelper) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DOMRequest, michael@0: DOMEventTargetHelper) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mError) michael@0: tmp->mResult = JSVAL_VOID; michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(DOMRequest, michael@0: DOMEventTargetHelper) michael@0: // Don't need NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER because michael@0: // DOMEventTargetHelper does it for us. michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mResult) michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_END michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DOMRequest) michael@0: NS_INTERFACE_MAP_ENTRY(nsIDOMDOMRequest) michael@0: NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) michael@0: michael@0: NS_IMPL_ADDREF_INHERITED(DOMRequest, DOMEventTargetHelper) michael@0: NS_IMPL_RELEASE_INHERITED(DOMRequest, DOMEventTargetHelper) michael@0: michael@0: /* virtual */ JSObject* michael@0: DOMRequest::WrapObject(JSContext* aCx) michael@0: { michael@0: return DOMRequestBinding::Wrap(aCx, this); michael@0: } michael@0: michael@0: NS_IMPL_EVENT_HANDLER(DOMRequest, success) michael@0: NS_IMPL_EVENT_HANDLER(DOMRequest, error) michael@0: michael@0: NS_IMETHODIMP michael@0: DOMRequest::GetReadyState(nsAString& aReadyState) michael@0: { michael@0: DOMRequestReadyState readyState = ReadyState(); michael@0: switch (readyState) { michael@0: case DOMRequestReadyState::Pending: michael@0: aReadyState.AssignLiteral("pending"); michael@0: break; michael@0: case DOMRequestReadyState::Done: michael@0: aReadyState.AssignLiteral("done"); michael@0: break; michael@0: default: michael@0: MOZ_CRASH("Unrecognized readyState."); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DOMRequest::GetResult(JS::MutableHandle aResult) michael@0: { michael@0: GetResult(nullptr, aResult); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DOMRequest::GetError(nsISupports** aError) michael@0: { michael@0: NS_IF_ADDREF(*aError = GetError()); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: DOMRequest::FireSuccess(JS::Handle aResult) michael@0: { michael@0: NS_ASSERTION(!mDone, "mDone shouldn't have been set to true already!"); michael@0: NS_ASSERTION(!mError, "mError shouldn't have been set!"); michael@0: NS_ASSERTION(mResult == JSVAL_VOID, "mResult shouldn't have been set!"); michael@0: michael@0: mDone = true; michael@0: if (JSVAL_IS_GCTHING(aResult)) { michael@0: RootResultVal(); michael@0: } michael@0: mResult = aResult; michael@0: michael@0: FireEvent(NS_LITERAL_STRING("success"), false, false); michael@0: } michael@0: michael@0: void michael@0: DOMRequest::FireError(const nsAString& aError) michael@0: { michael@0: NS_ASSERTION(!mDone, "mDone shouldn't have been set to true already!"); michael@0: NS_ASSERTION(!mError, "mError shouldn't have been set!"); michael@0: NS_ASSERTION(mResult == JSVAL_VOID, "mResult shouldn't have been set!"); michael@0: michael@0: mDone = true; michael@0: mError = new DOMError(GetOwner(), aError); michael@0: michael@0: FireEvent(NS_LITERAL_STRING("error"), true, true); michael@0: } michael@0: michael@0: void michael@0: DOMRequest::FireError(nsresult aError) michael@0: { michael@0: NS_ASSERTION(!mDone, "mDone shouldn't have been set to true already!"); michael@0: NS_ASSERTION(!mError, "mError shouldn't have been set!"); michael@0: NS_ASSERTION(mResult == JSVAL_VOID, "mResult shouldn't have been set!"); michael@0: michael@0: mDone = true; michael@0: mError = new DOMError(GetOwner(), aError); michael@0: michael@0: FireEvent(NS_LITERAL_STRING("error"), true, true); michael@0: } michael@0: michael@0: void michael@0: DOMRequest::FireDetailedError(nsISupports* aError) michael@0: { michael@0: NS_ASSERTION(!mDone, "mDone shouldn't have been set to true already!"); michael@0: NS_ASSERTION(!mError, "mError shouldn't have been set!"); michael@0: NS_ASSERTION(mResult == JSVAL_VOID, "mResult shouldn't have been set!"); michael@0: NS_ASSERTION(aError, "No detailed error provided"); michael@0: michael@0: mDone = true; michael@0: mError = aError; michael@0: michael@0: FireEvent(NS_LITERAL_STRING("error"), true, true); michael@0: } michael@0: michael@0: void michael@0: DOMRequest::FireEvent(const nsAString& aType, bool aBubble, bool aCancelable) michael@0: { michael@0: if (NS_FAILED(CheckInnerWindowCorrectness())) { michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr event; michael@0: NS_NewDOMEvent(getter_AddRefs(event), this, nullptr, nullptr); michael@0: nsresult rv = event->InitEvent(aType, aBubble, aCancelable); michael@0: if (NS_FAILED(rv)) { michael@0: return; michael@0: } michael@0: michael@0: event->SetTrusted(true); michael@0: michael@0: bool dummy; michael@0: DispatchEvent(event, &dummy); michael@0: } michael@0: michael@0: void michael@0: DOMRequest::RootResultVal() michael@0: { michael@0: mozilla::HoldJSObjects(this); michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(DOMRequestService, nsIDOMRequestService) michael@0: michael@0: NS_IMETHODIMP michael@0: DOMRequestService::CreateRequest(nsIDOMWindow* aWindow, michael@0: nsIDOMDOMRequest** aRequest) michael@0: { michael@0: nsCOMPtr win(do_QueryInterface(aWindow)); michael@0: NS_ENSURE_STATE(win); michael@0: NS_ADDREF(*aRequest = new DOMRequest(win)); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DOMRequestService::CreateCursor(nsIDOMWindow* aWindow, michael@0: nsICursorContinueCallback* aCallback, michael@0: nsIDOMDOMCursor** aCursor) michael@0: { michael@0: nsCOMPtr win(do_QueryInterface(aWindow)); michael@0: NS_ENSURE_STATE(win); michael@0: NS_ADDREF(*aCursor = new DOMCursor(win, aCallback)); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DOMRequestService::FireSuccess(nsIDOMDOMRequest* aRequest, michael@0: JS::Handle aResult) michael@0: { michael@0: NS_ENSURE_STATE(aRequest); michael@0: static_cast(aRequest)->FireSuccess(aResult); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DOMRequestService::FireError(nsIDOMDOMRequest* aRequest, michael@0: const nsAString& aError) michael@0: { michael@0: NS_ENSURE_STATE(aRequest); michael@0: static_cast(aRequest)->FireError(aError); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DOMRequestService::FireDetailedError(nsIDOMDOMRequest* aRequest, michael@0: nsISupports* aError) michael@0: { michael@0: NS_ENSURE_STATE(aRequest); michael@0: static_cast(aRequest)->FireDetailedError(aError); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: class FireSuccessAsyncTask : public nsRunnable michael@0: { michael@0: michael@0: FireSuccessAsyncTask(JSContext* aCx, michael@0: DOMRequest* aRequest, michael@0: const JS::Value& aResult) : michael@0: mReq(aRequest), michael@0: mResult(aCx, aResult) michael@0: { michael@0: } michael@0: michael@0: public: michael@0: michael@0: // Due to the fact that initialization can fail during shutdown (since we michael@0: // can't fetch a js context), set up an initiatization function to make sure michael@0: // we can return the failure appropriately michael@0: static nsresult michael@0: Dispatch(DOMRequest* aRequest, michael@0: const JS::Value& aResult) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: AutoSafeJSContext cx; michael@0: nsRefPtr asyncTask = new FireSuccessAsyncTask(cx, aRequest, aResult); michael@0: if (NS_FAILED(NS_DispatchToMainThread(asyncTask))) { michael@0: NS_WARNING("Failed to dispatch to main thread!"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Run() michael@0: { michael@0: mReq->FireSuccess(JS::Handle::fromMarkedLocation(mResult.address())); michael@0: return NS_OK; michael@0: } michael@0: michael@0: ~FireSuccessAsyncTask() michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: } michael@0: michael@0: private: michael@0: nsRefPtr mReq; michael@0: JS::PersistentRooted mResult; michael@0: }; michael@0: michael@0: class FireErrorAsyncTask : public nsRunnable michael@0: { michael@0: public: michael@0: FireErrorAsyncTask(DOMRequest* aRequest, michael@0: const nsAString& aError) : michael@0: mReq(aRequest), michael@0: mError(aError) michael@0: { michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Run() michael@0: { michael@0: mReq->FireError(mError); michael@0: return NS_OK; michael@0: } michael@0: private: michael@0: nsRefPtr mReq; michael@0: nsString mError; michael@0: }; michael@0: michael@0: NS_IMETHODIMP michael@0: DOMRequestService::FireSuccessAsync(nsIDOMDOMRequest* aRequest, michael@0: JS::Handle aResult) michael@0: { michael@0: NS_ENSURE_STATE(aRequest); michael@0: return FireSuccessAsyncTask::Dispatch(static_cast(aRequest), aResult); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DOMRequestService::FireErrorAsync(nsIDOMDOMRequest* aRequest, michael@0: const nsAString& aError) michael@0: { michael@0: NS_ENSURE_STATE(aRequest); michael@0: nsCOMPtr asyncTask = michael@0: new FireErrorAsyncTask(static_cast(aRequest), aError); michael@0: if (NS_FAILED(NS_DispatchToMainThread(asyncTask))) { michael@0: NS_WARNING("Failed to dispatch to main thread!"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DOMRequestService::FireDone(nsIDOMDOMCursor* aCursor) { michael@0: NS_ENSURE_STATE(aCursor); michael@0: static_cast(aCursor)->FireDone(); michael@0: michael@0: return NS_OK; michael@0: }