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