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