Skip to content

Commit 74cac72

Browse files
committed
Bug 1338596: Add support for agile references to mscom; r=jimm
MozReview-Commit-ID: 1NZoFZntO3g --HG-- extra : rebase_source : 6d3ed1da4893747d38c1bd2c31671d8ecf535cfd
1 parent 2d64aff commit 74cac72

File tree

4 files changed

+231
-3
lines changed

4 files changed

+231
-3
lines changed

ipc/mscom/AgileReference.cpp

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

ipc/mscom/AgileReference.h

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
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/RefPtr.h"
11+
12+
#include <objidl.h>
13+
14+
namespace mozilla {
15+
namespace mscom {
16+
17+
/**
18+
* This class encapsulates an "agile reference." These are references that
19+
* allow you to pass COM interfaces between apartments. When you have an
20+
* interface that you would like to pass between apartments, you wrap that
21+
* interface in an AgileReference and pass the agile reference instead. Then
22+
* you unwrap the interface by calling AgileReference::Resolve.
23+
*
24+
* Sample usage:
25+
*
26+
* // In the multithreaded apartment, foo is an IFoo*
27+
* auto myAgileRef = MakeUnique<AgileReference>(IID_IFoo, foo);
28+
*
29+
* // myAgileRef is passed to our main thread, which runs in a single-threaded
30+
* // apartment:
31+
*
32+
* RefPtr<IFoo> foo;
33+
* HRESULT hr = myAgileRef->Resolve(IID_IFoo, getter_AddRefs(foo));
34+
* // Now foo may be called from the main thread
35+
*/
36+
class AgileReference
37+
{
38+
public:
39+
AgileReference(REFIID aIid, IUnknown* aObject);
40+
AgileReference(AgileReference&& aOther);
41+
42+
~AgileReference();
43+
44+
explicit operator bool() const
45+
{
46+
return mAgileRef || mGitCookie;
47+
}
48+
49+
HRESULT Resolve(REFIID aIid, void** aOutInterface);
50+
51+
AgileReference(const AgileReference& aOther) = delete;
52+
AgileReference& operator=(const AgileReference& aOther) = delete;
53+
AgileReference& operator=(AgileReference&& aOther) = delete;
54+
55+
private:
56+
IGlobalInterfaceTable* ObtainGit();
57+
58+
private:
59+
REFIID mIid;
60+
RefPtr<IAgileReference> mAgileRef;
61+
DWORD mGitCookie;
62+
};
63+
64+
} // namespace mscom
65+
} // namespace mozilla
66+
67+
#endif // mozilla_mscom_AgileReference_h

ipc/mscom/DynamicallyLinkedFunctionPtr.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,13 @@ class DynamicallyLinkedFunctionPtr<R (__stdcall*)(Args...)>
3030
if (mModule) {
3131
mFunction = reinterpret_cast<FunctionPtrT>(
3232
::GetProcAddress(mModule, aFuncName));
33+
34+
if (!mFunction) {
35+
// Since the function doesn't exist, there is no point in holding a
36+
// reference to mModule anymore.
37+
::FreeLibrary(mModule);
38+
mModule = NULL;
39+
}
3340
}
3441
}
3542

@@ -46,7 +53,7 @@ class DynamicallyLinkedFunctionPtr<R (__stdcall*)(Args...)>
4653
}
4754
}
4855

49-
R operator()(Args... args)
56+
R operator()(Args... args) const
5057
{
5158
return mFunction(mozilla::Forward<Args>(args)...);
5259
}

ipc/mscom/moz.build

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
EXPORTS.mozilla.mscom += [
88
'Aggregation.h',
9+
'AgileReference.h',
910
'AsyncInvoker.h',
1011
'COMApartmentRegion.h',
1112
'COMPtrHolder.h',
@@ -17,6 +18,7 @@ EXPORTS.mozilla.mscom += [
1718
]
1819

1920
UNIFIED_SOURCES += [
21+
'AgileReference.cpp',
2022
'EnsureMTA.cpp',
2123
'MainThreadRuntime.cpp',
2224
'ProxyStream.cpp',
@@ -58,5 +60,5 @@ include('/ipc/chromium/chromium-config.mozbuild')
5860

5961
FINAL_LIBRARY = 'xul'
6062

61-
with Files("**"):
62-
BUG_COMPONENT = ("Core", "Disability Access APIs")
63+
with Files("**"):
64+
BUG_COMPONENT = ("Core", "Disability Access APIs")

0 commit comments

Comments
 (0)