tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

AgileReference.h (5019B)


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