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