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_mscom_AgileReference_h
8 #define mozilla_mscom_AgileReference_h
9 
10 #include "mozilla/Attributes.h"
11 #include "mozilla/RefPtr.h"
12 #include "nsISupportsImpl.h"
13 
14 #include <objidl.h>
15 
16 namespace mozilla {
17 namespace mscom {
18 namespace detail {
19 
20 class MOZ_HEAP_CLASS GlobalInterfaceTableCookie final {
21  public:
22   GlobalInterfaceTableCookie(IUnknown* aObject, REFIID aIid,
23                              HRESULT& aOutHResult);
24 
IsValid()25   bool IsValid() const { return !!mCookie; }
26   HRESULT GetInterface(REFIID aIid, void** aOutInterface) const;
27 
28   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GlobalInterfaceTableCookie)
29 
30   GlobalInterfaceTableCookie(const GlobalInterfaceTableCookie&) = delete;
31   GlobalInterfaceTableCookie(GlobalInterfaceTableCookie&&) = delete;
32 
33   GlobalInterfaceTableCookie& operator=(const GlobalInterfaceTableCookie&) =
34       delete;
35   GlobalInterfaceTableCookie& operator=(GlobalInterfaceTableCookie&&) = delete;
36 
37  private:
38   ~GlobalInterfaceTableCookie();
39 
40  private:
41   DWORD mCookie;
42 
43  private:
44   static IGlobalInterfaceTable* ObtainGit();
45 };
46 
47 }  // namespace detail
48 
49 /**
50  * This class encapsulates an "agile reference." These are references that
51  * allow you to pass COM interfaces between apartments. When you have an
52  * interface that you would like to pass between apartments, you wrap that
53  * interface in an AgileReference and pass the agile reference instead. Then
54  * you unwrap the interface by calling AgileReference::Resolve.
55  *
56  * Sample usage:
57  *
58  * // In the multithreaded apartment, foo is an IFoo*
59  * auto myAgileRef = MakeUnique<AgileReference>(IID_IFoo, foo);
60  *
61  * // myAgileRef is passed to our main thread, which runs in a single-threaded
62  * // apartment:
63  *
64  * RefPtr<IFoo> foo;
65  * HRESULT hr = myAgileRef->Resolve(IID_IFoo, getter_AddRefs(foo));
66  * // Now foo may be called from the main thread
67  */
68 class AgileReference final {
69  public:
70   AgileReference();
71 
72   template <typename InterfaceT>
AgileReference(RefPtr<InterfaceT> & aObject)73   explicit AgileReference(RefPtr<InterfaceT>& aObject)
74       : AgileReference(__uuidof(InterfaceT), aObject) {}
75 
76   AgileReference(REFIID aIid, IUnknown* aObject);
77 
78   AgileReference(const AgileReference& aOther) = default;
79   AgileReference(AgileReference&& aOther);
80 
81   ~AgileReference();
82 
83   explicit operator bool() const {
84     return mAgileRef || (mGitCookie && mGitCookie->IsValid());
85   }
86 
GetHResult()87   HRESULT GetHResult() const { return mHResult; }
88 
89   template <typename T>
Assign(const RefPtr<T> & aOther)90   void Assign(const RefPtr<T>& aOther) {
91     Assign(__uuidof(T), aOther);
92   }
93 
94   template <typename T>
95   AgileReference& operator=(const RefPtr<T>& aOther) {
96     Assign(aOther);
97     return *this;
98   }
99 
100   HRESULT Resolve(REFIID aIid, void** aOutInterface) const;
101 
102   AgileReference& operator=(const AgileReference& aOther);
103   AgileReference& operator=(AgileReference&& aOther);
104 
decltype(nullptr)105   AgileReference& operator=(decltype(nullptr)) {
106     Clear();
107     return *this;
108   }
109 
110   void Clear();
111 
112  private:
113   void Assign(REFIID aIid, IUnknown* aObject);
114   void AssignInternal(IUnknown* aObject);
115 
116  private:
117   IID mIid;
118   RefPtr<IAgileReference> mAgileRef;
119   RefPtr<detail::GlobalInterfaceTableCookie> mGitCookie;
120   HRESULT mHResult;
121 };
122 
123 }  // namespace mscom
124 }  // namespace mozilla
125 
126 template <typename T>
RefPtr(const mozilla::mscom::AgileReference & aAgileRef)127 RefPtr<T>::RefPtr(const mozilla::mscom::AgileReference& aAgileRef)
128     : mRawPtr(nullptr) {
129   (*this) = aAgileRef;
130 }
131 
132 template <typename T>
133 RefPtr<T>& RefPtr<T>::operator=(
134     const mozilla::mscom::AgileReference& aAgileRef) {
135   void* newRawPtr;
136   if (FAILED(aAgileRef.Resolve(__uuidof(T), &newRawPtr))) {
137     newRawPtr = nullptr;
138   }
139   assign_assuming_AddRef(static_cast<T*>(newRawPtr));
140   return *this;
141 }
142 
143 #endif  // mozilla_mscom_AgileReference_h
144