1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef mozilla_dom_DOMMozPromiseRequestHolder_h 8 #define mozilla_dom_DOMMozPromiseRequestHolder_h 9 10 #include "mozilla/DOMEventTargetHelper.h" 11 #include "mozilla/MozPromise.h" 12 13 namespace mozilla { 14 namespace dom { 15 16 /** 17 * This is a helper class that can be used when MozPromises are 18 * being consumed by binding layer code. It effectively creates 19 * a MozPromiseRequestHolder that auto-disconnects when the binding's 20 * global is disconnected. 21 * 22 * It can be used like this: 23 * 24 * RefPtr<Promise> 25 * SomeAsyncAPI(Args& aArgs, ErrorResult& aRv) 26 * { 27 * nsIGlobalObject* global = GetParentObject(); 28 * if (!global) { 29 * aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 30 * return nullptr; 31 * } 32 * 33 * RefPtr<Promise> outer = Promise::Create(global, aRv); 34 * if (aRv.Failed()) { 35 * return nullptr; 36 * } 37 * 38 * RefPtr<DOMMozPromiseRequestHolder> holder = 39 * new DOMMozPromiseRequestHolder(global); 40 * 41 * DoAsyncStuff()->Then( 42 * global->EventTargetFor(TaskCategory::Other), __func__, 43 * [holder, outer] (const Result& aResult) { 44 * holder->Complete(); 45 * 46 * // Note, you can access the holder's bound global in 47 * // your reaction handler. Its mostly likely set if 48 * // the handler fires, but you still must check for 49 * // its existence since something could disconnect 50 * // the global between when the MozPromise reaction 51 * // runnable is queued and when it actually runs. 52 * nsIGlobalObject* global = holder->GetParentObject(); 53 * NS_ENSURE_TRUE_VOID(global); 54 * 55 * outer->MaybeResolve(aResult); 56 * }, [holder, outer] (nsresult aRv) { 57 * holder->Complete(); 58 * outer->MaybeReject(aRv); 59 * })->Track(*holder); 60 * 61 * return outer.forget(); 62 * } 63 * 64 * NOTE: Currently this helper class extends DETH. This is only 65 * so that it can bind to the global and receive the 66 * DisconnectFromOwner() method call. In this future the 67 * binding code should be factored out so DETH is not 68 * needed here. See bug 1456893. 69 */ 70 template <typename PromiseType> 71 class DOMMozPromiseRequestHolder final : public DOMEventTargetHelper { 72 MozPromiseRequestHolder<PromiseType> mHolder; 73 74 ~DOMMozPromiseRequestHolder() = default; 75 DisconnectFromOwner()76 void DisconnectFromOwner() override { 77 mHolder.DisconnectIfExists(); 78 DOMEventTargetHelper::DisconnectFromOwner(); 79 } 80 WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)81 JSObject* WrapObject(JSContext* aCx, 82 JS::Handle<JSObject*> aGivenProto) override { 83 // We are extending DETH to get notified when the global goes 84 // away, but this object should never actually be exposed to 85 // script. 86 MOZ_CRASH("illegal method"); 87 } 88 89 public: DOMMozPromiseRequestHolder(nsIGlobalObject * aGlobal)90 explicit DOMMozPromiseRequestHolder(nsIGlobalObject* aGlobal) 91 : DOMEventTargetHelper(aGlobal) { 92 MOZ_DIAGNOSTIC_ASSERT(aGlobal); 93 } 94 95 operator MozPromiseRequestHolder<PromiseType>&() { return mHolder; } 96 97 operator const MozPromiseRequestHolder<PromiseType>&() const { 98 return mHolder; 99 } 100 Complete()101 void Complete() { mHolder.Complete(); } 102 DisconnectIfExists()103 void DisconnectIfExists() { mHolder.DisconnectIfExists(); } 104 Exists()105 bool Exists() const { return mHolder.Exists(); } 106 107 NS_INLINE_DECL_REFCOUNTING_INHERITED(DOMMozPromiseRequestHolder, 108 DOMEventTargetHelper) 109 }; 110 111 } // namespace dom 112 } // namespace mozilla 113 114 #endif // mozilla_dom_DOMMozPromiseRequestHolder_h 115