summaryrefslogtreecommitdiffstats
path: root/ipc/mscom/AgileReference.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ipc/mscom/AgileReference.cpp')
-rw-r--r--ipc/mscom/AgileReference.cpp223
1 files changed, 223 insertions, 0 deletions
diff --git a/ipc/mscom/AgileReference.cpp b/ipc/mscom/AgileReference.cpp
new file mode 100644
index 0000000000..4bdb0e683e
--- /dev/null
+++ b/ipc/mscom/AgileReference.cpp
@@ -0,0 +1,223 @@
+/* -*- 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 <utility>
+
+#include "mozilla/Assertions.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/DynamicallyLinkedFunctionPtr.h"
+#include "mozilla/mscom/Utils.h"
+
+#if defined(MOZILLA_INTERNAL_API)
+# include "nsDebug.h"
+# include "nsPrintfCString.h"
+#endif // defined(MOZILLA_INTERNAL_API)
+
+#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 {
+namespace detail {
+
+GlobalInterfaceTableCookie::GlobalInterfaceTableCookie(IUnknown* aObject,
+ REFIID aIid,
+ HRESULT& aOutHResult)
+ : mCookie(0) {
+ IGlobalInterfaceTable* git = ObtainGit();
+ MOZ_ASSERT(git);
+ if (!git) {
+ aOutHResult = E_POINTER;
+ return;
+ }
+
+ aOutHResult = git->RegisterInterfaceInGlobal(aObject, aIid, &mCookie);
+ MOZ_ASSERT(SUCCEEDED(aOutHResult));
+}
+
+GlobalInterfaceTableCookie::~GlobalInterfaceTableCookie() {
+ IGlobalInterfaceTable* git = ObtainGit();
+ MOZ_ASSERT(git);
+ if (!git) {
+ return;
+ }
+
+ DebugOnly<HRESULT> hr = git->RevokeInterfaceFromGlobal(mCookie);
+#if defined(MOZILLA_INTERNAL_API)
+ NS_WARNING_ASSERTION(
+ SUCCEEDED(hr),
+ nsPrintfCString("IGlobalInterfaceTable::RevokeInterfaceFromGlobal failed "
+ "with HRESULT 0x%08lX",
+ ((HRESULT)hr))
+ .get());
+#else
+ MOZ_ASSERT(SUCCEEDED(hr));
+#endif // defined(MOZILLA_INTERNAL_API)
+ mCookie = 0;
+}
+
+HRESULT GlobalInterfaceTableCookie::GetInterface(REFIID aIid,
+ void** aOutInterface) const {
+ IGlobalInterfaceTable* git = ObtainGit();
+ MOZ_ASSERT(git);
+ if (!git) {
+ return E_UNEXPECTED;
+ }
+
+ MOZ_ASSERT(IsValid());
+ return git->GetInterfaceFromGlobal(mCookie, aIid, aOutInterface);
+}
+
+/* static */
+IGlobalInterfaceTable* GlobalInterfaceTableCookie::ObtainGit() {
+ // Internally to COM, the Global Interface Table is a singleton, therefore we
+ // don't worry about holding onto this reference indefinitely.
+ static IGlobalInterfaceTable* sGit = []() -> IGlobalInterfaceTable* {
+ 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 detail
+
+AgileReference::AgileReference() : mIid(), mHResult(E_NOINTERFACE) {}
+
+AgileReference::AgileReference(REFIID aIid, IUnknown* aObject)
+ : mIid(aIid), mHResult(E_UNEXPECTED) {
+ AssignInternal(aObject);
+}
+
+AgileReference::AgileReference(AgileReference&& aOther)
+ : mIid(aOther.mIid),
+ mAgileRef(std::move(aOther.mAgileRef)),
+ mGitCookie(std::move(aOther.mGitCookie)),
+ mHResult(aOther.mHResult) {
+ aOther.mHResult = CO_E_RELEASED;
+}
+
+void AgileReference::Assign(REFIID aIid, IUnknown* aObject) {
+ Clear();
+ mIid = aIid;
+ AssignInternal(aObject);
+}
+
+void AgileReference::AssignInternal(IUnknown* aObject) {
+ // We expect mIid to already be set
+ DebugOnly<IID> zeroIid = {};
+ MOZ_ASSERT(mIid != zeroIid);
+
+ /*
+ * 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 StaticDynamicallyLinkedFunctionPtr<decltype(
+ &::RoGetAgileReference)>
+ pRoGetAgileReference(L"ole32.dll", "RoGetAgileReference");
+
+ MOZ_ASSERT(aObject);
+
+ if (pRoGetAgileReference &&
+ SUCCEEDED(mHResult =
+ pRoGetAgileReference(AGILEREFERENCE_DEFAULT, mIid, aObject,
+ getter_AddRefs(mAgileRef)))) {
+ return;
+ }
+
+ mGitCookie = new detail::GlobalInterfaceTableCookie(aObject, mIid, mHResult);
+ MOZ_ASSERT(mGitCookie->IsValid());
+}
+
+AgileReference::~AgileReference() { Clear(); }
+
+void AgileReference::Clear() {
+ mIid = {};
+ mAgileRef = nullptr;
+ mGitCookie = nullptr;
+ mHResult = E_NOINTERFACE;
+}
+
+AgileReference& AgileReference::operator=(const AgileReference& aOther) {
+ Clear();
+ mIid = aOther.mIid;
+ mAgileRef = aOther.mAgileRef;
+ mGitCookie = aOther.mGitCookie;
+ mHResult = aOther.mHResult;
+ return *this;
+}
+
+AgileReference& AgileReference::operator=(AgileReference&& aOther) {
+ Clear();
+ mIid = aOther.mIid;
+ mAgileRef = std::move(aOther.mAgileRef);
+ mGitCookie = std::move(aOther.mGitCookie);
+ mHResult = aOther.mHResult;
+ aOther.mHResult = CO_E_RELEASED;
+ return *this;
+}
+
+HRESULT
+AgileReference::Resolve(REFIID aIid, void** aOutInterface) const {
+ MOZ_ASSERT(aOutInterface);
+ // This check is exclusive-OR; we should have one or the other, but not both
+ MOZ_ASSERT((mAgileRef || mGitCookie) && !(mAgileRef && mGitCookie));
+ MOZ_ASSERT(IsCOMInitializedOnCurrentThread());
+
+ 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;
+ }
+
+ RefPtr<IUnknown> originalInterface;
+ HRESULT hr =
+ mGitCookie->GetInterface(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);
+}
+
+} // namespace mscom
+} // namespace mozilla