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 #include "mozilla/mscom/AgileReference.h"
8 
9 #include "DynamicallyLinkedFunctionPtr.h"
10 #include "mozilla/DebugOnly.h"
11 #include "mozilla/Assertions.h"
12 #include "mozilla/Move.h"
13 
14 #if NTDDI_VERSION < NTDDI_WINBLUE
15 
16 // Declarations from Windows SDK specific to Windows 8.1
17 
18 enum AgileReferenceOptions {
19   AGILEREFERENCE_DEFAULT = 0,
20   AGILEREFERENCE_DELAYEDMARSHAL = 1,
21 };
22 
23 HRESULT WINAPI RoGetAgileReference(AgileReferenceOptions options, REFIID riid,
24                                    IUnknown* pUnk,
25                                    IAgileReference** ppAgileReference);
26 
27 #endif  // NTDDI_VERSION < NTDDI_WINBLUE
28 
29 namespace mozilla {
30 namespace mscom {
31 
AgileReference(REFIID aIid,IUnknown * aObject)32 AgileReference::AgileReference(REFIID aIid, IUnknown* aObject)
33     : mIid(aIid), mGitCookie(0) {
34   /*
35    * There are two possible techniques for creating agile references. Starting
36    * with Windows 8.1, we may use the RoGetAgileReference API, which is faster.
37    * If that API is not available, we fall back to using the Global Interface
38    * Table.
39    */
40   static const DynamicallyLinkedFunctionPtr<decltype(&::RoGetAgileReference)>
41       pRoGetAgileReference(L"ole32.dll", "RoGetAgileReference");
42 
43   MOZ_ASSERT(aObject);
44 
45   if (pRoGetAgileReference &&
46       SUCCEEDED(pRoGetAgileReference(AGILEREFERENCE_DEFAULT, aIid, aObject,
47                                      getter_AddRefs(mAgileRef)))) {
48     return;
49   }
50 
51   IGlobalInterfaceTable* git = ObtainGit();
52   MOZ_ASSERT(git);
53   if (!git) {
54     return;
55   }
56 
57   DebugOnly<HRESULT> hr =
58       git->RegisterInterfaceInGlobal(aObject, aIid, &mGitCookie);
59   MOZ_ASSERT(SUCCEEDED(hr));
60 }
61 
AgileReference(AgileReference && aOther)62 AgileReference::AgileReference(AgileReference&& aOther)
63     : mIid(aOther.mIid),
64       mAgileRef(Move(aOther.mAgileRef)),
65       mGitCookie(aOther.mGitCookie) {
66   aOther.mGitCookie = 0;
67 }
68 
~AgileReference()69 AgileReference::~AgileReference() {
70   if (!mGitCookie) {
71     return;
72   }
73 
74   IGlobalInterfaceTable* git = ObtainGit();
75   MOZ_ASSERT(git);
76   if (!git) {
77     return;
78   }
79 
80   DebugOnly<HRESULT> hr = git->RevokeInterfaceFromGlobal(mGitCookie);
81   MOZ_ASSERT(SUCCEEDED(hr));
82 }
83 
84 HRESULT
Resolve(REFIID aIid,void ** aOutInterface)85 AgileReference::Resolve(REFIID aIid, void** aOutInterface) {
86   MOZ_ASSERT(aOutInterface);
87   MOZ_ASSERT(mAgileRef || mGitCookie);
88 
89   if (!aOutInterface) {
90     return E_INVALIDARG;
91   }
92 
93   *aOutInterface = nullptr;
94 
95   if (mAgileRef) {
96     // IAgileReference lets you directly resolve the interface you want...
97     return mAgileRef->Resolve(aIid, aOutInterface);
98   }
99 
100   if (!mGitCookie) {
101     return E_UNEXPECTED;
102   }
103 
104   IGlobalInterfaceTable* git = ObtainGit();
105   MOZ_ASSERT(git);
106   if (!git) {
107     return E_UNEXPECTED;
108   }
109 
110   RefPtr<IUnknown> originalInterface;
111   HRESULT hr = git->GetInterfaceFromGlobal(mGitCookie, mIid,
112                                            getter_AddRefs(originalInterface));
113   if (FAILED(hr)) {
114     return hr;
115   }
116 
117   if (aIid == mIid) {
118     originalInterface.forget(aOutInterface);
119     return S_OK;
120   }
121 
122   // ...Whereas the GIT requires us to obtain the same interface that we
123   // requested and then QI for the desired interface afterward.
124   return originalInterface->QueryInterface(aIid, aOutInterface);
125 }
126 
ObtainGit()127 IGlobalInterfaceTable* AgileReference::ObtainGit() {
128   // Internally to COM, the Global Interface Table is a singleton, therefore we
129   // don't worry about holding onto this reference indefinitely.
130   static IGlobalInterfaceTable* sGit = []() -> IGlobalInterfaceTable* {
131     IGlobalInterfaceTable* result = nullptr;
132     DebugOnly<HRESULT> hr = ::CoCreateInstance(
133         CLSID_StdGlobalInterfaceTable, nullptr, CLSCTX_INPROC_SERVER,
134         IID_IGlobalInterfaceTable, reinterpret_cast<void**>(&result));
135     MOZ_ASSERT(SUCCEEDED(hr));
136     return result;
137   }();
138 
139   return sGit;
140 }
141 
142 }  // namespace mscom
143 }  // namespace mozilla
144