Backed out changeset f85447f6f56d (bug 1891145) for causing mochitest failures @...
[gecko.git] / ipc / mscom / AgileReference.h
blob384c391ac76d4e3c4483fdbdb393aca623837992
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/. */
7 #ifndef mozilla_mscom_AgileReference_h
8 #define mozilla_mscom_AgileReference_h
10 #include "mozilla/Attributes.h"
11 #include "mozilla/RefPtr.h"
12 #include "mozilla/Result.h"
13 #include "mozilla/Unused.h"
14 #include "nsDebug.h"
15 #include "nsISupportsImpl.h"
17 #include <objidl.h>
19 namespace mozilla::mscom {
21 namespace detail {
22 // Detemplatized implementation details of `AgileReference`.
23 HRESULT AgileReference_CreateImpl(RefPtr<IAgileReference>&, REFIID, IUnknown*);
24 HRESULT AgileReference_ResolveImpl(RefPtr<IAgileReference> const&, REFIID,
25 void**);
26 } // namespace detail
28 /**
29 * This class encapsulates an "agile reference". These are references that allow
30 * you to pass COM interfaces between apartments. When you have an interface
31 * that you would like to pass between apartments, you wrap that interface in an
32 * AgileReference and pass that instead. Then you can "unwrap" the interface by
33 * calling Resolve(), which will return a proxy object implementing the same
34 * interface.
36 * Sample usage:
38 * ```
39 * // From a non-main thread, where `foo` is an `IFoo*` or `RefPtr<IFoo>`:
40 * auto myAgileRef = AgileReference(foo);
41 * NS_DispatchToMainThread([mar = std::move(myAgileRef)] {
42 * RefPtr<IFoo> foo = mar.Resolve();
43 * // Now methods may be invoked on `foo`
44 * });
45 * ```
47 template <typename InterfaceT>
48 class AgileReference final {
49 static_assert(
50 std::is_base_of_v<IUnknown, InterfaceT>,
51 "template parameter of AgileReference must be a COM interface type");
53 public:
54 AgileReference() = default;
55 ~AgileReference() = default;
57 AgileReference(const AgileReference& aOther) = default;
58 AgileReference(AgileReference&& aOther) noexcept = default;
60 AgileReference& operator=(const AgileReference& aOther) = default;
61 AgileReference& operator=(AgileReference&& aOther) noexcept = default;
63 AgileReference& operator=(std::nullptr_t) {
64 mAgileRef = nullptr;
65 return *this;
68 // Create a new AgileReference from an existing COM object.
70 // These constructors do not provide the HRESULT on failure. If that's
71 // desired, use `AgileReference::Create()`, below.
72 explicit AgileReference(InterfaceT* aObject) {
73 HRESULT const hr = detail::AgileReference_CreateImpl(
74 mAgileRef, __uuidof(InterfaceT), aObject);
75 Unused << NS_WARN_IF(FAILED(hr));
77 explicit AgileReference(RefPtr<InterfaceT> const& aObject)
78 : AgileReference(aObject.get()) {}
80 // Create a new AgileReference from an existing COM object, or alternatively,
81 // return the HRESULT explaining why one couldn't be created.
83 // A convenience wrapper `MakeAgileReference()` which infers `InterfaceT` from
84 // the RefPtr's concrete type is provided below.
85 static Result<AgileReference<InterfaceT>, HRESULT> Create(
86 RefPtr<InterfaceT> const& aObject) {
87 AgileReference ret;
88 HRESULT const hr = detail::AgileReference_CreateImpl(
89 ret.mAgileRef, __uuidof(InterfaceT), aObject.get());
90 if (FAILED(hr)) {
91 return Err(hr);
93 return ret;
96 explicit operator bool() const { return !!mAgileRef; }
98 // Common case: resolve directly to the originally-specified interface-type.
99 RefPtr<InterfaceT> Resolve() const {
100 auto res = ResolveAs<InterfaceT>();
101 if (res.isErr()) return nullptr;
102 return res.unwrap();
105 // Uncommon cases: resolve directly to a different interface type, and/or
106 // provide IAgileReference::Resolve()'s HRESULT.
108 // When used in other COM apartments, `IAgileInterface::Resolve()` returns a
109 // proxy object which (at time of writing) is not documented to provide any
110 // interface other than the one for which it was instantiated. (Calling
111 // `QueryInterface` _might_ work, but isn't explicitly guaranteed.)
113 template <typename OtherInterface = InterfaceT>
114 Result<RefPtr<OtherInterface>, HRESULT> ResolveAs() const {
115 RefPtr<OtherInterface> p;
116 auto const hr = ResolveRaw(__uuidof(OtherInterface), getter_AddRefs(p));
117 if (FAILED(hr)) {
118 return Err(hr);
120 return p;
123 // Raw version of Resolve/ResolveAs. Rarely, if ever, preferable to the
124 // statically-typed versions.
125 HRESULT ResolveRaw(REFIID aIid, void** aOutInterface) const {
126 return detail::AgileReference_ResolveImpl(mAgileRef, aIid, aOutInterface);
129 private:
130 RefPtr<IAgileReference> mAgileRef;
133 // Attempt to create an AgileReference from a refcounted interface pointer,
134 // providing the HRESULT as a secondary return-value.
135 template <typename InterfaceT>
136 inline Result<AgileReference<InterfaceT>, HRESULT> MakeAgileReference(
137 RefPtr<InterfaceT> const& aObj) {
138 return AgileReference<InterfaceT>::Create(aObj);
141 } // namespace mozilla::mscom
143 #endif // mozilla_mscom_AgileReference_h