Bug 1338596: Add support for agile references to mscom; r?jimm
MozReview-Commit-ID: 1NZoFZntO3g
new file mode 100644
--- /dev/null
+++ b/ipc/mscom/AgileReference.cpp
@@ -0,0 +1,152 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/mscom/AgileReference.h"
+
+#include "DynamicallyLinkedFunctionPtr.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Move.h"
+
+#if NTDDI_VERSION < NTDDI_WINBLUE
+
+// Declarations from Windows SDK specific to Windows 8.1
+
+enum AgileReferenceOptions
+{
+ AGILEREFERENCE_DEFAULT = 0,
+ AGILEREFERENCE_DELAYEDMARSHAL = 1,
+};
+
+HRESULT WINAPI RoGetAgileReference(AgileReferenceOptions options,
+ REFIID riid, IUnknown* pUnk,
+ IAgileReference** ppAgileReference);
+
+#endif // NTDDI_VERSION < NTDDI_WINBLUE
+
+namespace mozilla {
+namespace mscom {
+
+AgileReference::AgileReference(REFIID aIid, IUnknown* aObject)
+ : mIid(aIid)
+ , mGitCookie(0)
+{
+ /*
+ * There are two possible techniques for creating agile references. Starting
+ * with Windows 8.1, we may use the RoGetAgileReference API, which is faster.
+ * If that API is not available, we fall back to using the Global Interface
+ * Table.
+ */
+ static const DynamicallyLinkedFunctionPtr<decltype(&::RoGetAgileReference)>
+ pRoGetAgileReference(L"ole32.dll", "RoGetAgileReference");
+
+ MOZ_ASSERT(aObject);
+
+ if (pRoGetAgileReference &&
+ SUCCEEDED(pRoGetAgileReference(AGILEREFERENCE_DEFAULT, aIid, aObject,
+ getter_AddRefs(mAgileRef)))) {
+ return;
+ }
+
+ IGlobalInterfaceTable* git = ObtainGit();
+ MOZ_ASSERT(git);
+ if (!git) {
+ return;
+ }
+
+ DebugOnly<HRESULT> hr = git->RegisterInterfaceInGlobal(aObject, aIid,
+ &mGitCookie);
+ MOZ_ASSERT(SUCCEEDED(hr));
+}
+
+AgileReference::AgileReference(AgileReference&& aOther)
+ : mIid(aOther.mIid)
+ , mAgileRef(Move(aOther.mAgileRef))
+ , mGitCookie(aOther.mGitCookie)
+{
+ aOther.mGitCookie = 0;
+}
+
+AgileReference::~AgileReference()
+{
+ if (!mGitCookie) {
+ return;
+ }
+
+ IGlobalInterfaceTable* git = ObtainGit();
+ MOZ_ASSERT(git);
+ if (!git) {
+ return;
+ }
+
+ DebugOnly<HRESULT> hr = git->RevokeInterfaceFromGlobal(mGitCookie);
+ MOZ_ASSERT(SUCCEEDED(hr));
+}
+
+HRESULT
+AgileReference::Resolve(REFIID aIid, void** aOutInterface)
+{
+ MOZ_ASSERT(aOutInterface);
+ MOZ_ASSERT(mAgileRef || mGitCookie);
+
+ if (!aOutInterface) {
+ return E_INVALIDARG;
+ }
+
+ *aOutInterface = nullptr;
+
+ if (mAgileRef) {
+ // IAgileReference lets you directly resolve the interface you want...
+ return mAgileRef->Resolve(aIid, aOutInterface);
+ }
+
+ if (!mGitCookie) {
+ return E_UNEXPECTED;
+ }
+
+ IGlobalInterfaceTable* git = ObtainGit();
+ MOZ_ASSERT(git);
+ if (!git) {
+ return E_UNEXPECTED;
+ }
+
+ RefPtr<IUnknown> originalInterface;
+ HRESULT hr = git->GetInterfaceFromGlobal(mGitCookie, mIid,
+ getter_AddRefs(originalInterface));
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ if (aIid == mIid) {
+ originalInterface.forget(aOutInterface);
+ return S_OK;
+ }
+
+ // ...Whereas the GIT requires us to obtain the same interface that we
+ // requested and then QI for the desired interface afterward.
+ return originalInterface->QueryInterface(aIid, aOutInterface);
+}
+
+IGlobalInterfaceTable*
+AgileReference::ObtainGit()
+{
+ // Internally to COM, the Global Interface Table is a singleton, therefore we
+ // don't worry about holding onto this reference indefinitely.
+ static IGlobalInterfaceTable * const sGit = []() -> IGlobalInterfaceTable * const {
+ IGlobalInterfaceTable* result = nullptr;
+ DebugOnly<HRESULT> hr =
+ ::CoCreateInstance(CLSID_StdGlobalInterfaceTable, nullptr,
+ CLSCTX_INPROC_SERVER, IID_IGlobalInterfaceTable,
+ reinterpret_cast<void**>(&result));
+ MOZ_ASSERT(SUCCEEDED(hr));
+ return result;
+ }();
+
+ return sGit;
+}
+
+} // namespace mscom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/ipc/mscom/AgileReference.h
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_mscom_AgileReference_h
+#define mozilla_mscom_AgileReference_h
+
+#include "mozilla/RefPtr.h"
+
+#include <objidl.h>
+
+namespace mozilla {
+namespace mscom {
+
+/**
+ * This class encapsulates an "agile reference." These are references that
+ * allow you to pass COM interfaces between apartments. When you have an
+ * interface that you would like to pass between apartments, you wrap that
+ * interface in an AgileReference and pass the agile reference instead. Then
+ * you unwrap the interface by calling AgileReference::Resolve.
+ *
+ * Sample usage:
+ *
+ * // In the multithreaded apartment, foo is an IFoo*
+ * auto myAgileRef = MakeUnique<AgileReference>(IID_IFoo, foo);
+ *
+ * // myAgileRef is passed to our main thread, which runs in a single-threaded
+ * // apartment:
+ *
+ * RefPtr<IFoo> foo;
+ * HRESULT hr = myAgileRef->Resolve(IID_IFoo, getter_AddRefs(foo));
+ * // Now foo may be called from the main thread
+ */
+class AgileReference
+{
+public:
+ AgileReference(REFIID aIid, IUnknown* aObject);
+ AgileReference(AgileReference&& aOther);
+
+ ~AgileReference();
+
+ explicit operator bool() const
+ {
+ return mAgileRef || mGitCookie;
+ }
+
+ HRESULT Resolve(REFIID aIid, void** aOutInterface);
+
+ AgileReference(const AgileReference& aOther) = delete;
+ AgileReference& operator=(const AgileReference& aOther) = delete;
+ AgileReference& operator=(AgileReference&& aOther) = delete;
+
+private:
+ IGlobalInterfaceTable* ObtainGit();
+
+private:
+ REFIID mIid;
+ RefPtr<IAgileReference> mAgileRef;
+ DWORD mGitCookie;
+};
+
+} // namespace mscom
+} // namespace mozilla
+
+#endif // mozilla_mscom_AgileReference_h
--- a/ipc/mscom/DynamicallyLinkedFunctionPtr.h
+++ b/ipc/mscom/DynamicallyLinkedFunctionPtr.h
@@ -25,33 +25,40 @@ public:
DynamicallyLinkedFunctionPtr(const wchar_t* aLibName, const char* aFuncName)
: mModule(NULL)
, mFunction(nullptr)
{
mModule = ::LoadLibraryW(aLibName);
if (mModule) {
mFunction = reinterpret_cast<FunctionPtrT>(
::GetProcAddress(mModule, aFuncName));
+
+ if (!mFunction) {
+ // Since the function doesn't exist, there is no point in holding a
+ // reference to mModule anymore.
+ ::FreeLibrary(mModule);
+ mModule = NULL;
+ }
}
}
DynamicallyLinkedFunctionPtr(const DynamicallyLinkedFunctionPtr&) = delete;
DynamicallyLinkedFunctionPtr& operator=(const DynamicallyLinkedFunctionPtr&) = delete;
DynamicallyLinkedFunctionPtr(DynamicallyLinkedFunctionPtr&&) = delete;
DynamicallyLinkedFunctionPtr& operator=(DynamicallyLinkedFunctionPtr&&) = delete;
~DynamicallyLinkedFunctionPtr()
{
if (mModule) {
::FreeLibrary(mModule);
}
}
- R operator()(Args... args)
+ R operator()(Args... args) const
{
return mFunction(mozilla::Forward<Args>(args)...);
}
explicit operator bool() const
{
return !!mFunction;
}
--- a/ipc/mscom/moz.build
+++ b/ipc/mscom/moz.build
@@ -1,27 +1,29 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
EXPORTS.mozilla.mscom += [
'Aggregation.h',
+ 'AgileReference.h',
'AsyncInvoker.h',
'COMApartmentRegion.h',
'COMPtrHolder.h',
'EnsureMTA.h',
'MainThreadRuntime.h',
'ProxyStream.h',
'Ptr.h',
'Utils.h',
]
UNIFIED_SOURCES += [
+ 'AgileReference.cpp',
'EnsureMTA.cpp',
'MainThreadRuntime.cpp',
'ProxyStream.cpp',
'Utils.cpp',
]
if CONFIG['ACCESSIBILITY']:
EXPORTS.mozilla.mscom += [
@@ -53,10 +55,10 @@ LOCAL_INCLUDES += [
'/xpcom/base',
'/xpcom/build',
]
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'
-with Files("**"):
- BUG_COMPONENT = ("Core", "Disability Access APIs")
+with Files("**"):
+ BUG_COMPONENT = ("Core", "Disability Access APIs")