/* -*- 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/. */ #ifndef mozilla_dom_DOMMozPromiseRequestHolder_h #define mozilla_dom_DOMMozPromiseRequestHolder_h #include "mozilla/DOMEventTargetHelper.h" #include "mozilla/MozPromise.h" namespace mozilla::dom { /** * This is a helper class that can be used when MozPromises are * being consumed by binding layer code. It effectively creates * a MozPromiseRequestHolder that auto-disconnects when the binding's * global is disconnected. * * It can be used like this: * * RefPtr * SomeAsyncAPI(Args& aArgs, ErrorResult& aRv) * { * nsIGlobalObject* global = GetParentObject(); * if (!global) { * aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); * return nullptr; * } * * RefPtr outer = Promise::Create(global, aRv); * if (aRv.Failed()) { * return nullptr; * } * * RefPtr holder = * new DOMMozPromiseRequestHolder(global); * * DoAsyncStuff()->Then( * global->EventTargetFor(TaskCategory::Other), __func__, * [holder, outer] (const Result& aResult) { * holder->Complete(); * * // Note, you can access the holder's bound global in * // your reaction handler. Its mostly likely set if * // the handler fires, but you still must check for * // its existence since something could disconnect * // the global between when the MozPromise reaction * // runnable is queued and when it actually runs. * nsIGlobalObject* global = holder->GetParentObject(); * NS_ENSURE_TRUE_VOID(global); * * outer->MaybeResolve(aResult); * }, [holder, outer] (nsresult aRv) { * holder->Complete(); * outer->MaybeReject(aRv); * })->Track(*holder); * * return outer.forget(); * } * * NOTE: Currently this helper class extends DETH. This is only * so that it can bind to the global and receive the * DisconnectFromOwner() method call. In this future the * binding code should be factored out so DETH is not * needed here. See bug 1456893. */ template class DOMMozPromiseRequestHolder final : public DOMEventTargetHelper { MozPromiseRequestHolder mHolder; ~DOMMozPromiseRequestHolder() = default; void DisconnectFromOwner() override { mHolder.DisconnectIfExists(); DOMEventTargetHelper::DisconnectFromOwner(); } JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override { // We are extending DETH to get notified when the global goes // away, but this object should never actually be exposed to // script. MOZ_CRASH("illegal method"); } public: explicit DOMMozPromiseRequestHolder(nsIGlobalObject* aGlobal) : DOMEventTargetHelper(aGlobal) { MOZ_DIAGNOSTIC_ASSERT(aGlobal); } operator MozPromiseRequestHolder&() { return mHolder; } operator const MozPromiseRequestHolder&() const { return mHolder; } void Complete() { mHolder.Complete(); } void DisconnectIfExists() { mHolder.DisconnectIfExists(); } bool Exists() const { return mHolder.Exists(); } NS_INLINE_DECL_REFCOUNTING_INHERITED(DOMMozPromiseRequestHolder, DOMEventTargetHelper) }; } // namespace mozilla::dom #endif // mozilla_dom_DOMMozPromiseRequestHolder_h