/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ /* 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 "mozilla/dom/Future.h" #include "mozilla/dom/FutureBinding.h" #include "mozilla/dom/FutureResolver.h" #include "nsContentUtils.h" #include "nsPIDOMWindow.h" namespace mozilla { namespace dom { // FutureTask // This class processes the future's callbacks with future's result. class FutureTask MOZ_FINAL : public nsRunnable { public: FutureTask(Future* aFuture) : mFuture(aFuture) { MOZ_ASSERT(aFuture); MOZ_COUNT_CTOR(FutureTask); } ~FutureTask() { MOZ_COUNT_DTOR(FutureTask); } NS_IMETHOD Run() { mFuture->mTaskPending = false; mFuture->RunTask(); return NS_OK; } private: nsRefPtr mFuture; }; // Future NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Future) NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow) NS_IMPL_CYCLE_COLLECTION_UNLINK(mResolver) NS_IMPL_CYCLE_COLLECTION_UNLINK(mResolveCallbacks); NS_IMPL_CYCLE_COLLECTION_UNLINK(mRejectCallbacks); tmp->mResult = JSVAL_VOID; NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Future) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResolver) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResolveCallbacks); NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRejectCallbacks); NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Future) NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mResult) NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER NS_IMPL_CYCLE_COLLECTION_TRACE_END NS_IMPL_CYCLE_COLLECTING_ADDREF(Future) NS_IMPL_CYCLE_COLLECTING_RELEASE(Future) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Future) NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_END Future::Future(nsPIDOMWindow* aWindow) : mWindow(aWindow) , mResult(JS::UndefinedValue()) , mState(Pending) , mTaskPending(false) { MOZ_COUNT_CTOR(Future); NS_HOLD_JS_OBJECTS(this, Future); SetIsDOMBinding(); } Future::~Future() { mResult = JSVAL_VOID; NS_DROP_JS_OBJECTS(this, Future); MOZ_COUNT_DTOR(Future); } JSObject* Future::WrapObject(JSContext* aCx, JS::Handle aScope) { return FutureBinding::Wrap(aCx, aScope, this); } /* static */ already_AddRefed Future::Constructor(const GlobalObject& aGlobal, JSContext* aCx, FutureInit& aInit, ErrorResult& aRv) { nsCOMPtr window = do_QueryInterface(aGlobal.Get()); if (!window) { aRv.Throw(NS_ERROR_UNEXPECTED); return nullptr; } nsRefPtr future = new Future(window); future->mResolver = new FutureResolver(future); aInit.Call(future, *future->mResolver, aRv, CallbackObject::eRethrowExceptions); aRv.WouldReportJSException(); if (aRv.IsJSException()) { Optional > value(aCx); aRv.StealJSException(aCx, &value.Value()); future->mResolver->Reject(aCx, value); } return future.forget(); } void Future::Done(AnyCallback* aResolveCallback, AnyCallback* aRejectCallback) { AppendCallbacks(aResolveCallback, aRejectCallback); } void Future::AppendCallbacks(AnyCallback* aResolveCallback, AnyCallback* aRejectCallback) { mResolveCallbacks.AppendElement(aResolveCallback); mRejectCallbacks.AppendElement(aRejectCallback); // If future's state is resolved, queue a task to process future's resolve // callbacks with future's result. If future's state is rejected, queue a task // to process future's reject callbacks with future's result. if (mState != Pending && !mTaskPending) { nsRefPtr task = new FutureTask(this); NS_DispatchToCurrentThread(task); mTaskPending = true; } } void Future::RunTask() { MOZ_ASSERT(mState != Pending); nsTArray > callbacks; callbacks.SwapElements(mState == Resolved ? mResolveCallbacks : mRejectCallbacks); mResolveCallbacks.Clear(); mRejectCallbacks.Clear(); Optional > value(nsContentUtils::GetSafeJSContext(), mResult); for (uint32_t i = 0; i < callbacks.Length(); ++i) { ErrorResult rv; callbacks[i]->Call(value, rv); } } } // namespace dom } // namespace mozilla